前置き
作りながら得た知識の備忘録なので、一部不正確な情報があるかもしれません。本記事の内容をうのみにするのではなく、API開発者のドキュメントを確認するようお願いします。なお本記事は1から作ってみよう!といった内容ではなく、APIの解説寄りになっています。
感想、指摘等コメントお待ちしております。
Discord Botとは
Discordを使う上でロールの変更など、手動でやるのは面倒な操作がありますよね。そんな時はプログラムに働いてもらいましょう!
こちらのDeveloper PortalからBotを簡単に作成できます。詳細の手順が気になる場合は、他の方の記事を確認してみてください。
Discord.Netについて
Discord Botを作るためのAPIは様々なものが公開されています。Discord.pyやDiscord.jsがとくに有名なようです。
今回はMicrosoftの.NETで動くDiscord.Netを採用することにします。理由は私が.NET好きだというのと、Linqなど扱いやすいライブラリが多い(個人差あり)からです。
ちなみに同じく.NET用APIでDSharpPlusというものも公開されています。
0. サンプル
1. 初期設定
Visual Studio 2019で.NET Frameworkもしくは.NET Coreのコンソールアプリケーション用プロジェクトを作成します。本記事執筆時点でフレームワークのバージョンは.NET Core 3.1を採用しています。
Discord.NETのライブラリはNuGetから落とせます。NuGetパッケージマネージャーを開き、Discord.Net
とMicrosoft.Extensions.DependencyInjection
をプロジェクトにインポートします。
2. 初期化
Botを動かすために必要なものは大きく分けて3種類あります。順を追って説明していきます。
1. Bot本体
Discord.WebSocket.DiscordSocketClient
クラスはユーザーとしての基本機能を提供します。基本的にこのクラスのインスタンスを通してBot自身への操作をしていくことになります。
2. コマンド
Discord.Commands.CommandService
クラスは後述するコマンド群を管理するために使用されます。コマンドの登録はDependency Injection(依存性注入)を利用して自動で行われます。
3. サービスプロバイダー
Dependency Injectionによってコマンドを登録、取得するために使用されます。
これらをコードにすると以下のようになります。
privateDiscordSocketClientclient;privateCommandServicecommands;privateIServiceProviderserviceProvider;publicasyncTaskMainAsync(){serviceProvider=newServiceCollection().BuildServiceProvider();commands=newCommandService();awaitcommands.AddModulesAsync(Assembly.GetEntryAssembly(),serviceProvider);client=newDiscordSocketClient();}
3. 処理の登録
クライアントにはメッセージ受信時をはじめ、様々なタイミングで呼ばれるイベントが実装されています。ここに処理を登録することでBotに固有の処理をさせることができます。
publicasyncMainAsync(){client.Log+=Log;client.MessageReceived+=CommandReceived;}privateasyncTaskLog(LogMessagemessage){Console.WriteLine(message.ToString());}privateasyncTaskCommandReceived(SocketMessagemessage){varmsg=msgasSocketUserMessage;if(message==null)return;if(message.Author.IsBot)return;intargPos=0;varcommand=newCommandContext(client,msg);awaitcommands.ExecuteAsync(command,argPos,serviceProvider);}
4. コマンドの作成
Discord.Commands.ModuleBase
を継承したクラスにメソッドを追加することで、独自のコマンドを実装することができます。
publicclassCommandModule:ModuleBase{/// <summary>/// Helloと返す/// </summary>[Commands("hello")]//コマンドとして使用する文字列をアトリビュートで設定publicasyncTaskSayHelloAsync(){awaitReplyAsync("Hello!");}}
引数付きのコマンドを作成する場合は以下のようにします。
publicclassCommandModule:ModuleBase{/// <summary>/// 入力したロールが付与されたユーザー名を列挙する。/// </summary>[Commands("show")]publicasyncTaskEchoAsync([Summary("対象のロール名")]stringrollName){varrole=Context.Guild.Roles.FirstOrDefault(rl=>rl.ToString()==rollName);varusers=Context.Guild.GetUsersAsync();foreach(var_userinusers){varuser=_userasSocketGuildUser;if(user.Roles.Contains(role)){awaitReplyAsync(user.Username);}}}}
詳細設定
一部API機能の使用にはDiscord Gateway Intentを有効にする必要があります。例えば上記のコマンド例で使用した、ユーザーリストを取得するメソッドを有効化するには、これらのインテントを有効化が必要です。
Discord Developer Portalの個別アプリケーションのページから設定を操作できます。
その他
コマンドの登録のために使用しているDependency Injection(依存性注入)は、クラス間の依存関係を疎結合にし、単体テストをしやすくするためにしばしば使われます。詳細はDependency Injectionについてまとめている記事をご確認ください。