💨

Discord Bot作成を完全に理解する

2023/09/18に公開

1. はじめに

Discord Bot は SDK を使用することで簡単に作成することが可能です。
テキストでのメッセージの送信はもちろん、埋め込み、音声メッセージなど Discord Bot で表現できることの幅は広くなっています。
今回は新たに Discord Bot を作成する上で Discord Bot で表現できること、および運用する上での注意点を紹介します。

本記事の内容はGo(v1.20)とdiscordgo(v0.27.1)を使用し作成しています。
また記事内の内容は章ごとに独立しているので、必要に応じて目次から参照してください。

対象読者

  • Discord Bot で何ができるかよくわからない人
  • Discord Bot を作成したい人
  • Discord Bot の運用について知りたい人

2. Discord Bot で表現できるメッセージの種類

2.1. テキストメッセージ

  • 基本的なメッセージ形式です。
  • 2000 文字までのテキストを送信できます。

テキストメッセージ

実装例
dg, err := discordgo.New("Bot " + TOKEN)
...
_, err = dg.ChannelMessageSend(channelID, "Hello, Test Channel!")

https://github.com/sugar-cat7/example-discord-bot/tree/main/simple-message

2.2. ファイル

画像や動画、ドキュメントなどのファイルを送信・共有することができます。
ファイルを添付したメッセージ

基本的な形式はサポートされており、一例ですが以下のようなものが添付できます。

画像: .jpg, .jpeg, .png, .gif, .webp
動画: .mp4, .mov
オーディオ: .mp3, .wav
テキスト: .txt, .md, .log
ドキュメント: .pdf, .doc, .docx, .ppt, .pptx, .xls, .xlsx
圧縮ファイル: .zip, .rar, .tar.gz
実装例
dg, err := discordgo.New("Bot " + TOKEN)
...
resp, err := http.Get("")
if err != nil {
    log.Fatal(err)
}
defer resp.Body.Close()

// ファイルをメッセージとして送信
message := &discordgo.MessageSend{
    Content: "画像:",
    Files: []*discordgo.File{
        {
            Name:   "sugar.png",
            Reader: resp.Body,
        },
    },
}

_, err = dg.ChannelMessageSendComplex(channelID, message)

https://github.com/sugar-cat7/example-discord-bot/tree/main/file-message

2.3. リアクション

  • メッセージに絵文字での反応を追加できます。

  • リアクションとしてつけられる絵文字は 2 種類あります。

  • Unicode絵文字

リアクション1

  • カスタム絵文字

リアクション2

実装例
// Unicode絵文字
err = dg.MessageReactionAdd(channelID, message.ID, "😀")

// カスタム絵文字 name:id形式でエンコードする必要があります。
err = dg.MessageReactionAdd(channelID, message.ID, "emoji_name:emoji_id")
  • emoji_id はスタンプのリンクから得られるXXXの部分です。
    • https://cdn.discordapp.com/emojis/?XXX.webp

emoji_id

https://github.com/sugar-cat7/example-discord-bot/tree/main/reaction-message

2.4. リプライ

  • 特定のメッセージに返信することができます。

リプライ

実装例
dg, err := discordgo.New("Bot " + TOKEN)
...
m, _ := dg.ChannelMessageSend(channelID, "Hello, Test Channel!")
message := &discordgo.MessageSend{
    Content:   "This is a reply!",
    // 該当のメッセージIDを指定し、Contentの内容を送信する
    Reference: &discordgo.MessageReference{MessageID: m.ID},
}
_, err = dg.ChannelMessageSendComplex(channelID, message)

https://github.com/sugar-cat7/example-discord-bot/tree/main/inline-reply

2.5. スレッド

  • トピックごとに分けられたメッセージのスレッドを作成できます。

  • スレッド名は 1~100 文字

  • スレッドの非表示までの時間が調整できます

スレッド

実装例
thread, err := s.MessageThreadStartComplex(m.ChannelID, m.ID, &discordgo.ThreadStart{
   // see: https://discord.com/developers/docs/resources/channel#start-thread-from-message
    Name:                "Pong game with " + m.Author.Username,
    AutoArchiveDuration: 60,
    Invitable:           false,
    RateLimitPerUser:    10,
})
if err != nil {
    log.Println("Error starting thread:", err)
    return
}
_, err = s.ChannelMessageSend(thread.ID, "pong")

https://github.com/sugar-cat7/example-discord-bot/tree/main/thread-message

2.6. コンテキストメニュー

メッセージに関連する追加アクションを提供するメニューです。

コンテキストメニュー

実装例
func registerContextMenu(s *discordgo.Session, guildID string) {
	// Define the context menu
   // see: https://discord.com/developers/docs/interactions/application-commands#create-global-application-command
	cmd := &discordgo.ApplicationCommand{
		Name: "share-with-me",
		Type: discordgo.MessageApplicationCommand,
	}

	_, err := s.ApplicationCommandCreate(s.State.User.ID, guildID, cmd)
	if err != nil {
		log.Println("Failed to create context menu:", err)
	}
}

https://github.com/sugar-cat7/example-discord-bot/tree/main/contex-menu

2.7. Embed メッセージ

リッチなメッセージ形式です。

Embedメッセージ

実装例
dg, err := discordgo.New("Bot " + TOKEN)
...
embed := &discordgo.MessageEmbed{
    Title:       "Embed Title",
    Description: "This is an embed message",
    Color:       0x00ff00, // Green
}
_, err = dg.ChannelMessageSendEmbed(channelID, embed)

https://github.com/sugar-cat7/example-discord-bot/tree/main/embed-message

  • 埋め込み対象はカスタマイズできます。

カスタム

2.8. TTS (Text-to-Speech) メッセージ

テキストを音声に変換して読み上げるメッセージ形式です。

※音量注意

https://youtu.be/Pazc7r_mJdo

  • ユーザーが TTS を無効にしている場合、メッセージは通常のテキストとして表示されます。
  • TTS の音声のピッチや速度などの細かい設定をカスタマイズすることはできません。
  • TTS メッセージはテキストチャンネルでのみ再生されます。
実装例
dg, err := discordgo.New("Bot " + TOKEN)
...
_, err = dg.ChannelMessageSendTTS(channelID, "Hello in Text-to-Speech!")

https://github.com/sugar-cat7/example-discord-bot/tree/main/tts-message

2.9. コンポーネント (Button や Select Menu)

メッセージ内で使用できる UI コンポーネントです。

https://discord.com/developers/docs/interactions/message-components#component-object

1.ButtonComponent

  • メッセージ内で表示されるインタラクティブなボタンコンポーネントです。
    • ユーザーがクリックするとアプリにインタラクションを送信できます。
    • アクション行の中で送信の必要があります。
    • アクション行は最大 5 つのボタンを含むことができ、セレクトメニューコンポーネントと同時に含むことはできません。

ボタン

ボタンの種類は下記を参照して下さい。
https://discord.com/developers/docs/interactions/message-components#button-object-button-styles

実装例
&discordgo.MessageComponent{
    Type: discordgo.ButtonComponent,
    Label: "Click Me!",
    Style: discordgo.PrimaryButton, // 例: プライマリーボタン
    CustomID: "button_click",
}

https://github.com/sugar-cat7/example-discord-bot/tree/main/component-message

2.SelectMenuComponent

  • メッセージ内のドロップダウンリストからユーザーがオプションを選択するインタラクティブなコンポーネントです。
    • シングルセレクトとマルチセレクトをサポートしています。
    • アクション行内で送信する必要があり、1 つのアクション行には 1 つのセレクトメニューのみ含めることができます。セレクトメニューとボタンを同時に含むことはできません。

セレクト

実装例
&discordgo.MessageComponent{
    &discordgo.SelectMenu{
        CustomID: "select_option",
        Options: []discordgo.SelectMenuOption{
            {
                Label: "Option 1",
                Value: "opt1",
            },
            {
                Label: "Option 2",
                Value: "opt2",
            },
        },
    },
}

3.TextInputComponent

  • テキスト入力はモーダルで表示されるインタラクティブなコンポーネントです。
    • 短いフォームまたは長いフォームのテキストの収集に適しています。

Modal
※このコンポーネントは Interaction のみで利用可能です。

実装例
discordgo.TextInput{
    CustomID:    "text_input",
    Placeholder: "Enter some text here",
    Style:       discordgo.TextInputParagraph,
}
  • ボタン、セレクトメニュー、スラッシュコマンドなどのコンポーネントを活用してユーザのアクションに応答することができる機能です。

https://discord.com/developers/docs/interactions/receiving-and-responding

Interaction の種類:

  1. スラッシュコマンド (Slash Commands):

    • / を使用してコマンドを起動する機能。
    • カスタムコマンドを作成して、特定のアクションや応答をトリガーすることができる。
    • グローバルまたは特定のサーバーでのみ使用するために登録できる。
  2. メッセージコンポーネント (Message Components):

    • 前述したコンポーネント(TextInput など)を埋め込むことができる。
  3. コンテキストメニュー (Context Menus):

    • ユーザやメッセージに対してカスタムアクションを追加するための右クリックメニュー。
    • スラッシュコマンドと同様の登録と処理フローを持つ。

重い処理を行う場合は、時間制限を考慮し非同期で処理を行う必要があります。

下記の記事では、リクエストを受け付けるエンドポイントを用意し、PubSub キューにリダイレクトするような構成にすることで、Discord Bot への応答を返しつつ、非同期での処理を実現しています。

https://zenn.dev/suzuesa/articles/2bf80f33013a14

2.10. スケジュール

特定の時間や日付にメッセージを送信するスケジューリング機能です。

  • イベントは最大 100 件登録できます。

スケジュール

https://discord.com/developers/docs/resources/guild-scheduled-event#guild-scheduled-event

実装例
dg, err := discordgo.New("Bot " + TOKEN)
...
params := &discordgo.GuildScheduledEventParams{
    Name:               "イベント名",
    Description:        "イベントの説明",
    ScheduledStartTime: &startTime,
    ScheduledEndTime:   &endTime,
    PrivacyLevel:       discordgo.GuildScheduledEventPrivacyLevelGuildOnly,
    EntityType:         discordgo.GuildScheduledEventEntityTypeExternal,
    EntityMetadata:     &discordgo.GuildScheduledEventEntityMetadata{Location: "場所の情報"},
}

// スケジュールイベントを作成
_, err = dg.GuildScheduledEventCreate(guildID, params)

https://github.com/sugar-cat7/example-discord-bot/tree/main/schedule-event

https://discord.com/developers/docs/resources/guild-scheduled-event#create-guild-scheduled-event

2.11. ボイスチャンネルでの音声メッセージ

ユーザーとの音声コミュニケーションを実現できます。

※音量注意
https://youtu.be/bMsyCeMzY7w

https://discord.com/developers/docs/topics/voice-connections#voice

機能 実装例 説明
ボイスチャンネルへの参加・退出 dg.ChannelVoiceJoin(...) / v.Disconnect() Bot がボイスチャンネルに参加・退出するための基本的なメソッド
音声の受信 v.OpusRecv Bot が他のユーザーからの音声を受け取るためのメソッド。具体的には PCM データを取得することが可能
音声の送信 v.OpusSend Bot が他のユーザーからの音声を受け取るためのメソッド。具体的には Opus 形式の音声データを受け取る
ユーザーのミュート制御 dg.GuildMemberMute(...) 特定のユーザーをミュートにする、またはミュートを解除するためのメソッド
ユーザーの音量調整 - (Discord API に直接的なメソッドは存在しない) ユーザーの音量を調整するための機能。具体的な実装は、音声データを操作して実現することが必要
オーディオファイルの再生 カスタム実装が必要 オーディオファイルを読み込んで、Opus 形式に変換し、v.OpusSendを利用してボイスチャンネルに送信する
ユーザーとのインタラクティブなコマンド処理 カスタム実装が必要 テキストチャンネルでユーザーからのコマンドを受け取り、それに応じて Voice Bot の動作を変更するなどのインタラクティブな処理を実現
実装例
dg, err := discordgo.New("Bot " + TOKEN)
...
// ボットが参加するギルドとチャンネルの ID、ミュートやスピーカーのステータスなどのオプションを引数として受け取る。
v, err := dg.ChannelVoiceJoin(guildID, channelID, true, false)
  • ボイスチャンネルで効果音を送信する
    • dcaフォーマットのオーディオファイルを読み込む
func playSound(s *discordgo.Session, guildID, channelID string) (err error) {
	vc, err := s.ChannelVoiceJoin(guildID, channelID, false, true)
	if err != nil {
		return err
	}
	defer vc.Disconnect()

	time.Sleep(250 * time.Millisecond)
	vc.Speaking(true)
	for _, buff := range buffer {
		vc.OpusSend <- buff
	}
	vc.Speaking(false)
	time.Sleep(250 * time.Millisecond)

	return nil
}
  • 録音

https://github.com/sugar-cat7/example-discord-bot/tree/main/voice-bot

  • 効果音

https://github.com/sugar-cat7/example-discord-bot/tree/main/airhorn-message

https://discord.com/developers/docs/topics/voice-connections#encrypting-and-sending-voice

3. Discord Bot の運用

3.1. 開発言語

Bot を開発する際の主要な言語、それぞれの SDK の特性は以下の通りです。

https://discord.com/developers/docs/topics/community-resources

Discord Bot を開発するための言語は多岐にわたります。以下は主要な言語と、それぞれの SDK の特性、情報源をまとめたものです。

言語 SDK の特性 OSS
Python 豊富なライブラリと組み合わせることで、高度な機能を持つ Bot の開発が可能。主に非同期処理に対応している discord.py, hikari
JavaScript Node.js 上で動作する Bot 向けのライブラリ。非同期処理をサポートし、効率的な Bot 開発が可能 discord.js, Eris
Java 型安全性を活かした Bot 開発が可能。大規模な Bot の開発・運用が可能 JDA, Discord4J
C# .NET フレームワークをベースにした Bot 開発が行える。非同期処理やイベント駆動をサポート Discord.Net, DSharpPlus
Go Go 言語の特性を活かした、高性能・並行処理が得意な Bot の開発が可能 discordgo
Clojure JVM 上で動作する関数型言語での Bot 開発が可能 discljord
C++ 性能を追求した Bot 開発が可能 D++
Lua 軽量なスクリプト言語での Bot 開発が行える Discordia
PHP Web アプリケーションとの連携を重視した Bot 開発が可能 DiscordPHP
Rust 高性能かつ安全性を重視した Bot の開発が可能 Serenity
Ruby オブジェクト指向の特性を活かした Bot 開発が可能 discordrb

3.2. Interactions

Interactions をサポートするためのライブラリやツールは多数あります。
Webhook 経由で Interactions を受信する場合に必須となるセキュリティと認証チェックのヘルパーやデータモデルの型を提供しています。
以下はその代表的なものを一部紹介します。

言語 特性 情報源
C# Interactions via outgoing webhook のサポート Discord.Net.Rest
Dart Slash commands サポート、Commands framework を含み、クロスプラットフォームで動作、全ての送信 HTTP リクエストや WebSocket メッセージのコントロールが可能。 nyxx
Go Minimal caching を持つ高速な API ラッパー Tempest
Javascript Interactions webhook を実装する際に役立つタイプとヘルパー関数を提供 discord-interactions-js
Python Python 向けの Interactions サポートライブラリ discord-interactions-python
PHP Interactions webhook を実装する際に役立つタイプとヘルパー関数を提供 discord-interactions-php

3.3. API Types

Discord API 用のシンプルな型定義です。

名前 言語
dasgo Go
discord-api-types JavaScript

3.4. Discord Bot の開発を支えるツール

Game SDK Tools

Discord Game SDK と他のゲームプラットフォーム(例: Valve の Steamworks SDK)のネットワーク層は類似しています。
以下の OSS を使用することで、共有機能のための統一されたインターフェースを開発者に提供し、複数のプラットフォームでの開発を簡略化することができます。

ツール名 詳細 対応プラットフォーム
HouraiNetworking Unity3D 向けのライブラリ Unity3D

Dispatch Tools

Discord の Dispatch ツールを使用してゲームを Discord で公開する際に使用するツールです。

ツール名 詳細
JohnyTheCarrot's Dispatch CLI ゲームのアップデートを Push するときの Webhook サポート

Permission Calculators

Discord の権限は複雑ですが、以下のツールを使えば簡単に計算できます。

ツール名 詳細
FiniteReality's Permissions Calculator 権限計算ツール
abalabahaha's Permissions Calculator 権限計算ツール

Intent Calculators

インテントの計算に使用できます。(インテントは、特定のイベント群を Bot にサブスクライブさせるためのものです。)

ツール名 詳細
ziad87's Intent Calculator Intent 計算ツール
Larko's Intent Calculator Intent 計算ツール

Embed Visualizers

Embeds の視覚化に役立つツールです。

ツール名 詳細
Autocode Embed Builder Embed のビジュアライザ
JohnyTheCarrot's Embed Previewer Discord 内での Embed 表示をテストするブラウザ拡張

3.5. (無料枠での)ホスティング先

Bot のホスティング先には様々な選択肢があります。
無料枠で常時起動可能なマネージドなサービスで代表的なものは以下のとおりです。

サービス名 コスト (月) RAM CPU ストレージ ネットワーク転送量 備考
Render $0 (制限あり) 512 MB 0.1 vCPU - - 制限詳細
fly.io 無料プランあり 256 MB (3 VMs まで) 共有 CPU (3 VMs まで) 3 GB (合計) 160 GB 3 VMs (V2 apps, V1 apps, Machines 合計)
replit 無料プランあり 0.5 GiB 0.5 vCPUs 10 GiB 10 GiB (開発用) Unlimited Public Repls
Railway 初回 $5 まで無料 $10/GB/月 $20/vCPU/月 $0.25/GB/月 (オプション) $0.10/GB 2023 年 8 月 1 日からの転送料金

上記以外にも、Interaction 経由のみの動作、またはユーザーの入力を受け付けない...など、Bot の機能によってホスティング先の選択肢は異なります。
常駐の必要がなく HTTP Interaction のみの利用で十分な場合は、Lambda や Cloudflare Workers などの Faas によるホスティングも可能なため検討してください。

3.6. 一般に公開する場合

Discord Bot を一般に公開する際は、セキュリティや利用者の利便性、API の制限などさまざまな面での注意が必要です。

基本情報

https://discord.com/developers/applications

  • Bot は一般に公開する場合、Discord Developer Portal での公開設定が必要です。
  • Bot の公開設定は、Discord Developer Portal の「OAuth2」タブから設定できます。
  • 100 サーバー以上に Bot を導入する場合は別途申請が必要です。(75 サーバ以上に導入されると Dev Portal 上で本人確認が行われます)

権限と機能の制限

不正利用を防ぐため、ボットには必要最低限の権限のみを与えるのが基本です。
ボットの権限管理は、ホワイトリスト形式の許可制を採用しています。(ボットに明示的に権限が付与されない限りアクションは実行できません)

権限

またサーバー側でコマンド権限を設定することで、ユーザーごとにコマンド(/hoge) Interaction の実行を制限することができます。

https://zenn.dev/voiscord/articles/e0e3595a44ecf4

https://support.discord.com/hc/ja/articles/4644915651095-コマンド権限

API リミットについて

Discord API は一定の制限があります。これを超えると Bot が一時的に動作を停止する可能性があります。

https://discord.com/developers/docs/topics/rate-limits

1. レートリミットの種類と詳細

タイプ 説明 上限値
Per-route 個別のエンドポイントごとの制限。HTTP メソッドやエンドポイントの種類による。ヘッダーX-RateLimit-Bucketを参照。 ヘッダーX-RateLimit-Limitに示される値
Global ボットやユーザーが全体として行うリクエストの制限。 1 秒あたり 50 リクエスト
Emoji-specific 絵文字に関するエンドポイントは通常の制限とは異なる。 具体的な上限値は提示されていない
Invalid Request 無効な HTTP リクエストの制限。主に 401、403、429 ステータスに関連。 10 分間に 10,000 リクエスト

2. 主なレートリミット関連ヘッダー

ヘッダー名 説明
X-RateLimit-Limit そのエンドポイントのリクエスト上限値
X-RateLimit-Remaining 残りのリクエスト回数
X-RateLimit-Reset レートリミットがリセットされるエポックタイム(1970 年 1 月 1 日 00:00:00 UTC からの秒数)
X-RateLimit-Reset-After レートリミットがリセットされるまでの時間(秒)
X-RateLimit-Bucket レートリミットの一意な識別子
X-RateLimit-Global 429 レスポンス時にのみ返され、グローバルレートリミットにヒットしたことを示すフラグ
X-RateLimit-Scope 429 レスポンス時にのみ返され、ユーザー、グローバル、またはリソースのレートリミットの種類を示す

API からの応答は HTTP 429 として返される。応答にはRetry-Afterヘッダーまたはretry_afterフィールドが含まれ、再試行するまでの待機時間が示されます。

注意点: アプリケーションはレートリミットにヒットするのを避けるため、応答ヘッダーを適切に解析し、制限に従って動作する必要があります。特に、401 や 403 などのエラーレスポンスを適切に処理することで、無効なリクエスト制限にヒットするリスクを減らすことができます。

4. 一般公開されている Bot の紹介

  • 公開されている Discord Bot の一覧がランキング形式で閲覧できます。

https://top.gg/

  • VOICEVOX 読み上げ Bot

https://zenn.dev/kuronekoserver/articles/6080dd36129a15

  • (著者が作成した)Vtuber グループ の配信を通知する Bot
    Tweet

5. 参考文献

https://discord.com/developers/docs/intro

https://scrapbox.io/discordpy-japan/

https://pkg.go.dev/github.com/bwmarrin/discordgo

https://zenn.dev/voiscord/articles/e0e3595a44ecf4

https://zenn.dev/suzuesa/articles/2bf80f33013a14

Discussion