SlackからDiscordにメッセージ移行&同期

2025-12-01

はじめに

アドベントカレンダー1日目担当のすけと申します。

このあいだ、jackで

  1. SlackからDiscordへの履歴移行
  2. 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サーバーに追加します。

  1. 左側のメニューから「OAuth2」タブを選び、その中の「URL Generator」を選択します。
  2. 「SCOPES」の項目で「bot」にチェックを入れます。
  3. 下に表示される「BOT PERMISSIONS」で、以下の権限を選択します。
      • Read Messages/View Channels
      • Send Messages
      • Manage Channels
      • Manage Webhooks
      • Attach File
  4. 権限を選択すると、一番下に招待用の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でアプリを作成する必要があります。

  1. Slack API にアクセスし、「Create New App」をクリックします。
  2. 「From scratch」を選択し、アプリ名(例: DiscordSync)と対象のワークスペースを選択して「Create App」をクリックします。
  3. Socket Modeの有効化:
      • 左メニューの「Socket Mode」をクリックし、「Enable Socket Mode」をオンにします。
      • Tokenの生成画面が出るので、名前を適当に付けて「Generate」をクリック。
      • ここで表示される xapp- から始まるトークン(App-Level Token) を控えておきます。(後で MB_SLACK_APP_TOKEN として使います)
  4. 権限(Scopes)の設定:
      • 左メニューの「OAuth & Permissions」をクリックします。
      • 「Scopes」セクションの「Bot Token Scopes」に以下の権限を追加します。
          • chat:write(メッセージ送信)
          • channels:read, channels:history(パブリックチャンネルの読み取り)
          • users:read(ユーザー名の取得に必要)
          • files:write(画像のアップロード等)
  5. イベントの購読:
      • 左メニューの「Event Subscriptions」をクリックし、「Enable Events」をオンにします。
      • 「Subscribe to bot events」を展開し、以下を追加して「Save Changes」します。
          • message.channels
  6. アプリのインストール:
      • 「OAuth & Permissions」に戻り、「Install to Workspace」をクリックして許可します。
      • ここで表示される xoxb- から始まるトークン(Bot User OAuth Token) を控えておきます。(後で MB_SLACK_TOKEN として使います)

Discordボットの準備(同期用)

移行ツールで使ったボットとは別に、同期用の新しいボットを作成します。

  1. アプリケーションの作成:
      • Discord Developer Portal を開き、移行時と同様に「New Application」から新しいアプリを作成します。
  2. ボットの追加とIntent設定:
      • 左メニュー「Bot」から「Add Bot」を実行します。
      • 「Message Content Intent」 を必ずオンにして保存してください。
      • 画面上部の「Reset Token」から 新しいトークン を発行して控えておきます。(MB_DISCORD_TOKEN
  3. サーバーへの招待と権限設定:
      • 左メニュー「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 --from=builder /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のメッセージの同期ができます。

明日以降もアドベントカレンダーの記事を更新していきます!

お楽しみに!