
はじめに
アドベントカレンダー1日目担当のすけと申します。
このあいだ、jackで
- SlackからDiscordへの履歴移行
- DiscordとSlackのメッセージの同期
をしたので、やったことを書こうかなと思います。
なぜDiscordへ?
今までjackではチャットツールとしてSlackを使っていました。 一番大きな理由は、メッセージ履歴の90日制限です。
https://slack.com/intl/ja-jp/help/articles/27204752526611-Slack-のフリープランの機能制限
息の長いプロジェクト、そしてサークル運営(幹部)側からすると、意外に古いやり取りを参照することがありまして…
なかなか90日の制限がつらいということで、移行を検討し始めました。
ということで、現在はDiscordお試し期間としてDiscordに移行してもやっていけるか試しています。
Slackの履歴をDiscordへ移行
Slackメッセージのエクスポート
管理者設定→データのインポート/エクスポートから履歴をエクスポートします。
ただし、無料プランでは2024年8月26日から直近一年までのデータしか保管してくれなくなってしまったので、最大一年分のみエクスポートできると思います。
(jackでは、規制の前にすべてのデータをエクスポートしておいたので全てのデータを移行できました)
Discord Developer Portalでアプリケーションを作成
Discord Developer Portal の画面右上にある「New Application」ボタンをクリックします。
アプリの名前(何でも構いません)を入力し、利用規約に同意するチェックを入れて「Create」をクリックします。
ボットを作成
左側のメニューからBot→Add Botボタンをクリックし、確認画面で「Yes, do it!」をクリックします。
ボットの権限(Intents)を設定
Botタブを少し下にスクロールし、Privileged Gateway Intentsのセクションを見つけます。
以下の項目を有効化してください。
- SERVER MEMBERS INTENT
- MESSAGE CONTENT INTENT
トークンを取得
Botタブの上部に戻り、「Reset Token」ボタンをからトークンを発行します。後で使うので控えておいてください。
ボットをサーバーに招待
最後に、作成したボットをあなたのDiscordサーバーに追加します。
- 左側のメニューから「OAuth2」タブを選び、その中の「URL Generator」を選択します。
- 「SCOPES」の項目で「bot」にチェックを入れます。
- 下に表示される「BOT PERMISSIONS」で、以下の権限を選択します。
- Read Messages/View Channels
- Send Messages
- Manage Channels
- Manage Webhooks
- Attach File
- 権限を選択すると、一番下に招待用のURLが生成されるので、移行先のサーバを選んでボットをサーバに追加します。
移行の実行
次に、slack-to-discordというpythonライブラリを使って移行していきます。 (Python3が実行できる環境を前提にさせていただきます)
とりあえずslack-to-discordをインストールします。
pip install slack-to-discord
slackのエクスポートファイルと同じディレクトリに移動して、次のコマンドを実行します。
slack-to-discord --zipfile <エクスポートしたZIPファイル名> --guild <Discordサーバー名> --token <Discordのボットトークン>
レート制限で時々止まりますが、そのまま放っておけば勝手に再開します。 大量にメッセージがあるとそこそこ時間がかかります。 jackの場合、丸二日くらいかかって歴史を感じました。
エクスポートにチャンネルが含まれないというエラーが出た場合
もしエクスポートしたzipファイルがMacとWindowsを行き来している場合、濁点・半濁点が悪さをしているかもしてません。 詳しくは以下の記事が参考になります。(偶然にも、著者はjackのOBの方です!) https://zenn.dev/hacobell\\_dev/articles/68ccc92bffd6cc 僕はこのせいで3時間は溶かしました😃
移行後のチャンネルはこんな感じです。Slackのスレッドもそのまま移行できています。

DiscordのメッセージをSlackと同期
MatterBridgeとFly.ioを使ってDiscordとSlackのメッセージの内容を同期します。
Slackアプリの準備
MatterBridgeを使ってSlackと同期するためには、Slack APIでアプリを作成する必要があります。
- Slack API にアクセスし、「Create New App」をクリックします。
- 「From scratch」を選択し、アプリ名(例: DiscordSync)と対象のワークスペースを選択して「Create App」をクリックします。
- Socket Modeの有効化:
- 左メニューの「Socket Mode」をクリックし、「Enable Socket Mode」をオンにします。
- Tokenの生成画面が出るので、名前を適当に付けて「Generate」をクリック。
- ここで表示される xapp- から始まるトークン(App-Level Token) を控えておきます。(後で MB_SLACK_APP_TOKEN として使います)
- 権限(Scopes)の設定:
- 左メニューの「OAuth & Permissions」をクリックします。
- 「Scopes」セクションの「Bot Token Scopes」に以下の権限を追加します。
- chat:write(メッセージ送信)
- channels:read, channels:history(パブリックチャンネルの読み取り)
- users:read(ユーザー名の取得に必要)
- files:write(画像のアップロード等)
- イベントの購読:
- 左メニューの「Event Subscriptions」をクリックし、「Enable Events」をオンにします。
- 「Subscribe to bot events」を展開し、以下を追加して「Save Changes」します。
- message.channels
- アプリのインストール:
- 「OAuth & Permissions」に戻り、「Install to Workspace」をクリックして許可します。
- ここで表示される xoxb- から始まるトークン(Bot User OAuth Token) を控えておきます。(後で MB_SLACK_TOKEN として使います)
Discordボットの準備(同期用)
移行ツールで使ったボットとは別に、同期用の新しいボットを作成します。
- アプリケーションの作成:
- Discord Developer Portal を開き、移行時と同様に「New Application」から新しいアプリを作成します。
- ボットの追加とIntent設定:
- 左メニュー「Bot」から「Add Bot」を実行します。
- 「Message Content Intent」 を必ずオンにして保存してください。
- 画面上部の「Reset Token」から 新しいトークン を発行して控えておきます。(MB_DISCORD_TOKEN)
- サーバーへの招待と権限設定:
- 左メニュー「OAuth2」→「URL Generator」を開きます。
- SCOPESで bot にチェックを入れます。
- BOT PERMISSIONSではシンプルに以下の2つを選択します。
- Read Messages/View Channels
- Send Messages
- 生成されたURLにアクセスし、同期したいサーバーにボットを招待します。
同期ツールのビルド
ただし、公式版はSlackのApp Token(後述)に対応していないので、対応してくれているフォーク版をビルドして使います。
ビルドにはGOの実行環境が必要です。 以下のコマンドでビルドします。
git clone https://github.com/PoshCode/matterbridge.git
cd matterbridge
go buildすると、matterbridge.goという実行ファイルができます。
環境の作成
matterbridge.goと同じディレクトリに、あるDockerfileを、以下の内容で上書きします。
# --- ステージ1: ビルダーステージ ---
# Goの公式イメージをビルド環境として使用
FROM golang:1.22-alpine AS builder
# ビルドに必要なツールをインストール
RUN apk add --no-cache git
# 作業ディレクトリを設定
WORKDIR /src
# Goモジュールの依存関係をキャッシュするために、先にダウンロード
COPY go.mod go.sum ./
RUN go mod download
# アプリケーションのソースコードをコピー
COPY . .
# アプリケーションをビルド
# CGO_ENABLED=0 は、Cのライブラリに依存しない静的なバイナリを生成するために重要
# -ldflags "-s -w" は、デバッグ情報を削除し、バイナリサイズを削減
RUN CGO_ENABLED=0 go build -ldflags "-s -w" -o /matterbridge
# --- ステージ2: 最終ステージ ---
# scratchイメージは最もミニマルなベースイメージで、セキュリティ上非常に優れている
FROM alpine
# ビルダーステージからコンパイル済みのバイナリとエントリーポイントスクリプトをコピー
COPY /matterbridge /matterbridge
COPY entrypoint.sh /entrypoint.sh
# エントリーポイントスクリプトに実行権限を付与
RUN chmod +x /entrypoint.sh
# コンテナ起動時にエントリーポイントスクリプトを実行
ENTRYPOINT ["/entrypoint.sh"]
同じディレクトリに entrypoint.sh を作成して、以下の内容を貼り付けます。
#!/bin/sh
set -e
# --- matterbridge.tomlの基本部分を生成 ---
# この部分は一度だけ書き込まれます。
cat << EOF > /etc/matterbridge.toml
# --- Slack settings ---
[slack.myteam]
Token="${MB_SLACK_TOKEN}"
AppToken="${MB_SLACK_APP_TOKEN}"
PreserveThreading=true
PrefixMessagesWithNick=true
# --- Discord settings ---
[discord.myserver]
Token="${MB_DISCORD_TOKEN}"
Server="${MB_DISCORD_SERVER_NAME}"
UseUserName=true
[general]
RemoteNickFormat="{NICK}: "
EOF
# --- MB_CHANNEL_LIST環境変数内の各チャンネルに対して、gateway設定を追記 ---
# forループを使い、チャンネルリストから一つずつ設定を生成します。
for channel_name in $MB_CHANNEL_LIST
do
cat << EOF >> /etc/matterbridge.toml
# --- Bridge for channel: ${channel_name} ---
[[gateway]]
name="bridge-${channel_name}"
enable=true
# (Slack)
[[gateway.inout]]
account="slack.myteam"
channel="${channel_name}"
# (Discord)
[[gateway.inout]]
account="discord.myserver"
channel="${channel_name}"
EOF
done
echo "✅ Configuration generated for the following channels: $MB_CHANNEL_LIST"
# execコマンドでmatterbridgeプロセスを起動
exec /matterbridge -conf /etc/matterbridge.toml
fly.ioの設定
Fly.ioをコマンドラインで操作できるflyctlをインストールします。 https://fly.io/docs/flyctl/install/
ログインして、
flyctl auth login
アプリを作成します。
flyctl launch --no-deploy
全く重いアプリではないので、なるべくマシンの性能は抑えましょう。 設定するとこんな感じのfly.tomlができるはずです。
# fly.toml app configuration file generated for matterbridge on 2025-09-24T00:40:18+09:00
#
# See [<https://fly.io/docs/reference/configuration/>](<https://fly.io/docs/reference/configuration/>) for information about how to use this file.
#
app = 'matterbridge'
primary_region = 'nrt'
[build]
dockerfile = "Dockerfile"
# [http_service]
# internal_port = 8080
# force_https = true
# auto_stop_machines = 'stop'
# auto_start_machines = true
# min_machines_running = 0
# processes = ['app']
[[vm]]
size = 'shared-cpu-1x'
同じディレクトリで、以下のシェルスクリプトを作成して実行します。
#!/bin/sh
# このスクリプトは、Fly.io上のmatterbridgeアプリケーションに必要なすべてのシークレット(機密情報)を一度に設定するのに役立ちます。
# 必要な各値の入力を求め、それらを一括で設定します。
echo "--- Matterbridge Fly.io シークレット設定スクリプト ---"
echo "SlackとDiscordのアプリ設定から、以下の値を入力してください。"
echo ""
# Slackボットトークンを要求
echo "1. Slackボットトークンを入力してください ('xoxb-'で始まります):"
read -r MB_SLACK_TOKEN
# Slackアプリトークンを要求
echo "2. Slackアプリトークンを入力してください ('xapp-'で始まります):"
read -r MB_SLACK_APP_TOKEN
# Discordボットトークンを要求
echo "3. Discordボットトークンを入力してください:"
read -r MB_DISCORD_TOKEN
# Discordサーバー名を要求
echo "4. Discordサーバーの正確な名前を入力してください (例: Test)"
echo "(全角文字、空白を含む場合はentrypoint.shに直接記述してください):"
read -r MB_DISCORD_SERVER_NAME
# 同期するチャンネル名を要求
echo "5. 同期するチャンネル名を入力してください (例: general random)"
read -r MB_CHANNEL_LIST
echo ""
echo "--- 入力値の確認 ---"
echo "Slackボットトークン: [設定済み]"
echo "Slackアプリトークン: [設定済み]"
echo "Discordボットトークン: [設定済み]"
echo "Discordサーバー名: ${MB_DISCORD_SERVER_NAME}"
echo "同期するチャンネル: ${MB_CHANNEL_LIST}"
echo "--------------------------"
echo ""
echo "Fly.ioにシークレットを設定しています... これによりアプリケーションが再起動されます。"
# 複数の再起動を避けるため、すべてのシークレットを一度に設定します
fly secrets set \\
MB_SLACK_TOKEN="$MB_SLACK_TOKEN" \\
MB_SLACK_APP_TOKEN="$MB_SLACK_APP_TOKEN" \\
MB_DISCORD_TOKEN="$MB_DISCORD_TOKEN" \\
MB_CHANNEL_LIST="$MB_CHANNEL_LIST" \\
# MB_SLACK_CHANNEL_ID="$MB_SLACK_CHANNEL_ID" \\
# MB_DISCORD_CHANNEL_ID="$MB_DISCORD_CHANNEL_ID" \\
MB_DISCORD_SERVER_NAME="$MB_DISCORD_SERVER_NAME"
echo ""
echo "✅ すべてのシークレットが正常に設定されました!"
echo "アプリケーションが新しい設定で再起動します。"
echo "'fly logs' コマンドで再起動の様子を監視できます。"
対話的にトークンだとかを聞かれるので、入力すると自動で反映させてくれます。
Botをチャンネルに招待
同期したい全てのSlackチャンネルで、/invite @<Botの名前> を実行します。
かなりめんどくさいです…
おわりに
お疲れさまでした!これでSlack⇔Discordのメッセージの同期ができます。
明日以降もアドベントカレンダーの記事を更新していきます!
お楽しみに!
おすすめ記事



