😊

Goで作るDiscord, SlackのMessage連携ツール

2023/10/16に公開

この記事は株式会社Gincoのテックブログとして書いています。

1. コンセプト

チャットツールとしてSlackとDiscordの両方を使っている方はいるのではないでしょうか?かくいう私もその1人です。

弊社ではコミュニケーションツールとしてSlackを使用してます。また弊社が注力しているWeb3業界においては、オープンソースにて開発が行われていることが多く、主な開発者のコミュニケーションツールとしてDiscordが使われています。

ツールの必要性として、エラーや仕様などの質問や公式のアナウンスなどを、逐一チームで内容を共有するのにリンクやスクリーンショットを添付する手間を省くため、SlackにDiscordのそれらのコメントを転送するツールを作成しました。

具体的には、Discordの複数のチャネルにて我々がコメントした、または我々への返信 、公式アナウンス、我々へのメンションのメッセージをSlackに転送することを実装しています。

開発したツールの主な機能

開発したツールの主な機能は下記の3つになります。

  • Discordのチャンネルを監視する
  • DiscordのメッセージをSlackに転送するか判別する
  • DiscordのメッセージをSlackに転送する

2. 技術的なアプローチ

今回作成したツールの機能のコード部分について書いています。

ツールの詳細

コードの解説

  • チャンネルを監視する部分のコード

    指定したトークンでDiscordのセッションを作成します。作成したセッションでチャンネルに新規メッセージが投稿されたら内部の処理を行います。
    isTargetChannel関数は取得したいチャンネルのIDをfor文で回し、各チャンネルのメッセージを取得します。
    次に1番最新のメッセージを取得し、Slackに転送すべきメッセージなのかCheckDiscordMessageで判別します。

session, err := discordgo.New(userToken)

session.AddHandler(func(s *discordgo.Session, m *discordgo.MessageCreate) {
		ok, cfgChannel := isTargetChannel(m.ChannelID, cfg.Channels)
		if !ok {
			return
		}
		
		messages, err := s.ChannelMessages(cfgChannel.Id, 1, "", "", "")
		if err != nil {
			return
		}

		err = CheckDiscordMessage(*messages[0], cfg.Users, cfgChannel.Name)
		if err != nil {
			return
		}
})

err = session.Open()
if err != nil {
	logger.Error("", zap.Error(err))
	return
}
defer session.Close()
func isTargetChannel(channelID string, channels []Channel) (bool, Channel) {
	for _, v := range channels {
		if v.Id == channelID {
			return true, v
		}
	}
	return false, Channel{}
}
  • Slackに転送するか判別する部分のコード

    最初にmap型のグローバル変数でチャネルのIDをKeyでメッセージIDをvalueにし、同じメッセージを再送するのを防ぎます。
    今回の判別は取得したいユーザーの発言かメンションであれば、Slackに転送するようにしています。

func CheckDiscordMessage(m discordgo.Message, users []User, serverName string) error {

	if lastMessage[serverName] == m.ID || lastMessage[serverName] == "" {
		lastMessage[serverName] = m.ID
		return nil
	}

	for _, user := range users {
		if m.Author.ID == user.Id {
			err := SendToSlack(serverName, user.Name, m.Content, m.ID)
			if err != nil {
				return err
			}
			lastMessage[serverName] = m.ID
			return nil
		}
		for _, mention := range m.Mentions {
			if mention.ID == user.Id {
				err := SendToSlack(serverName, "Mention to "+user.Name, m.Content, m.ID)
				if err != nil {
					return err
				}
				lastMessage[serverName] = m.ID
				return nil
			}
		}
	}

	return nil
}
  • Slackに転送する部分のコード
    ここでは転送するメッセージのタイトルを整え、Discordで取得したメッセージをTextにしSlackに転送します。
使用しているライブラリ: "github.com/slack-go/slack"
var webhook string = "https://xxxxxx"

func SendToSlack(serverName string, user string, message string, id string) (err error) {
	attachment := slack.Attachment{
		Fallback: id,
	}
	title := serverName + " | " + user

	msg := slack.WebhookMessage{
		Username:    title,
		Text:        message,
		Attachments: []slack.Attachment{attachment},
	}

	err = slack.PostWebhook(webhook, &msg)
	if err != nil {
		return err
	}

	return nil
}

Discord のメッセージを Slack へ転送した例
※ 下図では Slack に追加したアプリのアイコン画像を Discord のロゴマークにしています。

3. 検証

このツールによりDiscordのメッセージをSlackで確認することができ、コミュニティのアナウンスやDiscordでの会話を見逃したり共有する手間を省くことができるようになりました。
Discordのチャネルにメッセージが投稿されると、それがアクションとなるため若干のラグはあるものの準リアルタイムに機能します。
まだ改善の余地があると思いますが、気になった点などはコメントしていただけると幸いです。
この記事を通して、今回使用するdiscordgoの入門やSlackとDiscordの連携についてお力になれれば幸いです。

最後に

  • 株式会社 Gincoではブロックチェーンを学びたい方、ウォレットについて詳しくなりたい方を募集していますので下記リンクから是非ご応募ください。
  • 株式会社Ginco の求人一覧

Discussion