前置き
作りながら得た知識の備忘録なので、一部不正確な情報があるかもしれません。本記事の内容をうのみにするのではなく、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によってコマンドを登録、取得するために使用されます。
これらをコードにすると以下のようになります。
private DiscordSocketClient client;
private CommandService commands;
private IServiceProvider serviceProvider;
public async Task MainAsync(){
serviceProvider = new ServiceCollection().BuildServiceProvider();
commands = new CommandService();
await commands.AddModulesAsync(Assembly.GetEntryAssembly(), serviceProvider);
client = new DiscordSocketClient();
}
3. 処理の登録
クライアントにはメッセージ受信時をはじめ、様々なタイミングで呼ばれるイベントが実装されています。ここに処理を登録することでBotに固有の処理をさせることができます。
public async MainAsync(){
client.Log += Log;
client.MessageReceived += CommandReceived;
}
private async Task Log(LogMessage message){
Console.WriteLine(message.ToString());
}
private async Task CommandReceived(SocketMessage message){
var msg = msg as SocketUserMessage;
if(message == null) return;
if(message.Author.IsBot) return;
int argPos = 0;
var command = new CommandContext(client, msg);
await commands.ExecuteAsync(command, argPos, serviceProvider);
}
4. コマンドの作成
Discord.Commands.ModuleBase
を継承したクラスにメソッドを追加することで、独自のコマンドを実装することができます。
public class CommandModule : ModuleBase{
/// <summary>
/// Helloと返す
/// </summary>
[Commands("hello")] //コマンドとして使用する文字列をアトリビュートで設定
public async Task SayHelloAsync(){
await ReplyAsync("Hello!");
}
}
引数付きのコマンドを作成する場合は以下のようにします。
public class CommandModule : ModuleBase{
/// <summary>
/// 入力したロールが付与されたユーザー名を列挙する。
/// </summary>
[Commands("show")]
public async Task EchoAsync([Summary("対象のロール名")] string rollName){
var role = Context.Guild.Roles.FirstOrDefault(rl => rl.ToString() == rollName);
var users = Context.Guild.GetUsersAsync();
foreach(var _user in users){
var user = _user as SocketGuildUser;
if(user.Roles.Contains(role)){
await ReplyAsync(user.Username);
}
}
}
}
詳細設定
一部API機能の使用にはDiscord Gateway Intentを有効にする必要があります。例えば上記のコマンド例で使用した、ユーザーリストを取得するメソッドを有効化するには、これらのインテントを有効化が必要です。
Discord Developer Portalの個別アプリケーションのページから設定を操作できます。
その他
コマンドの登録のために使用しているDependency Injection(依存性注入)は、クラス間の依存関係を疎結合にし、単体テストをしやすくするためにしばしば使われます。詳細はDependency Injectionについてまとめている記事をご確認ください。