【discord.js】v13でbotを作ってみる


初書 : 2021/08/09
node : 16.6.0
discord.js: v13.0.1

前書き

先日、discord.jsがv13をリリースしたので、早速使ってみる。
なお、初書時点ではスレッドやボタンなど、v13で新しく登場した機能の一部は触れていないが、興味はあるので近々追記もしくは別記事で投稿する予定。

前提

・botのトークンの取得やサーバーへの導入の準備が終わっていること
・nodejsの基本的な扱いは出来ること。

インストール

まずはnodeのバージョン確認。v13は最低要件で16.6.0を要求するので、それ以下の場合はアップデートする必要がある。

% node --version
v16.6.1

もし更新していない場合はnodebrewなどでアップデートする必要がある。

出来れば、次はプロジェクトの作成。今回は単純に% npm init -yで作成した。

あとはdiscord.jsをインストールする。

% npm install discord.js

また、discordのapiを使う時にenvファイルを使うので、dotenvもインストールしておく。

% npm install dotenv

あとはtypescriptでやる場合など、必要なものを各自インストールする。
(以降、コードはTypescriptで記述する。)

% npm install -save-dev typescript

botを起動してみる

まずはbotの起動から。

今回はsrc/index.tsを作成し、この中で実行していく。

src/index.ts
import { Client, Intents } from 'discord.js';
import dotenv from 'dotenv';

dotenv.config(); // envファイルを読み込む

const client = new Client({ intents: [Intents.FLAGS.GUILDS, Intents.FLAGS.GUILD_MESSAGES] }); // 1

client.once('ready', () => { // 2
  console.log('Ready!');
});

client.login(process.env.DISCORD_TOKEN); // 3

軽く説明
dotenv.config();はenvファイルを読み込むためのおまじない。

(1)ではdiscord botを操作するクラスを生成する。
なお、v12までとは違い、引数ClientOptionsが必須となった。
その中でもintentsのみ必須で、これはdiscord apiからどのイベントを受信するかを指定する。

どのフラグがどのイベントに対応しているかはこちら:
公式:Discord Developer Portal — Documentation — Gateway
Discord.js Japan User Group:Gateway Intents の利用に関するガイド

今回はとりあえずメッセージのやりとりを目標にするため、GUILDSGUILD_MESSAGESを設定した。1

(2)ではbotを稼働する準備ができたら呼ばれるイベント。client.onに関しては他にもメッセージの受信やコマンドの受信などで使う。

(3)ではbotにログインする。引数にはbotのトークンを渡す。今回は.envDISCORD_TOKEN=xxxxxxxと書き込んでいるのでそこから読み込む。

これが出来れば一旦起動してみる。

% node .
Ready!

・Ready!と出てそのまま実行が続いていること
・discord側でオンライン表示になっていること

これが確認できれば起動はOK!

メッセージを受信してみる

次はメッセージを受信してみる。ついでにおうむ返ししてみる。

readyイベントの次に以下を追加する

src/index.ts
client.on("messageCreate", (message) => {
  if (message.author.bot) { // 1
    return;
  }
  console.log(message.content); // 2
  message.channel.send({ // 3
    content: `受信したメッセージ:${message.content}`,
    reply: {messageReference: message.id},
    // allowedMentions: { repliedUser: false }, // 4
  });
});

これも軽く説明

(1)では、botからの送信を無視する。これを飛ばすと、botのメッセージも認識する。
ちなみに他のbotのメッセージを受信したい場合は、message.author.id === client.user?.idをif文に入れる必要がある。
これを忘れた場合は、自身のメッセージを認識し、それを返すという無限ループが起きてしまう。

(2)では、受け取ったメッセージ文をログに出力する。

(3)では、受け取ったメッセージをそのチャンネルに送信する。
contentが本文で、replyが返信先のメッセージID。
もしメンション通知を飛ばしたくない場合は、(4)のallowMentionsを追記する。

これで実行してみて、受信したメッセージ:xxxが返ってきたら成功。

スラッシュコマンドを使ってみる

最近のbotによくあるスラッシュコマンドが、discord.jsのv13でようやく対応したので、それを使ってみる。

ちなみにグローバルコマンドとギルドコマンドの2種類あるのだが、テストする時はギルドコマンドを使用する。
と言うのも、グローバルコマンドはリアルタイムで反映されず、最長1時間待たないと変化しないため、何回もテストするには向いていないから。

権限を追加する

ギルドコマンド2を追加する場合は、別でサーバーに権限が必要なので追加する。

Discord Developer Portalから、該当のbotのOauth2から、botapplications.commandsの2つをオンにした状態でurlを生成し、botをディスコードサーバーに追加する(既にディスコードサーバーに入っている場合、一度脱退させる必要はない)

コマンドを追加する

次はスラッシュコマンドをディスコードサーバーに追加するコードを追記する。
今回はボットの起動時に追加するので、readyイベントの中に記述する。

src/index.ts
client.once("ready", async () => {
  const data: ApplicationCommandData[] = [{ // 1
    name: "run_the_slash_command",
    description: "テストとしてコマンドを実行します。",
  }];
  const command = await client.application?.commands.set(data,'server id'); // 2
  console.log("Ready!");
});

コード解説

(1)は登録するコマンドを配列で登録する。
nameはコマンド名、必ず英語の小文字である必要がある。descriptionはコマンドの説明。下の方に表示される。
他にもオプションなどがあるが、詳しくは:Registering slash commands | Discord.js Guide

(2)は登録するコマンドを追加する。
setの第1引数に(1)を、第二引数に登録するディスコードサーバーidを記述する。なお、第二引数を省略した場合はグローバルコマンドになる。
また、setの代わりにcreateを使うこともできる。この場合、第一引数は配列ではなく、単体のコマンド情報で、かつ既存のコマンドは削除されない。主に途中で追加するときに使う用だと思われる。

これが出来れば一度実行し、ディスコードサーバー内チャットでスラッシュコマンドが打てるか確認する。(なお、この時押してもインタラクションに失敗しましたと出る)

コマンドを受け取る

コマンドは受け取って初めて動作するので、その部分を作成する。

src/index.ts
client.on("interactionCreate", async (interaction) => { // 1
  if (!interaction.isCommand()) { // 2
    return;
  }
  if (interaction.commandName === 'run_the_slash_command') { // 3
    await interaction.reply('コマンドを実行しました!'); // 4
  }
  console.log(interaction);
});

コード解説
(1)で新規イベントを作成。

(2)で受信したinteractionがコマンドかどうかチェックし、コマンド以外を拒否する。

(3)では登録したイベントを判断し、(4)で該当コマンドに返信している。

終わりに

簡単にbotを作成できる部分をまとめてみた。
他にもいろんな機能があるので、一度ガイドやドキュメントをみるといいかもしれない。

参考サイト:

Introduction | Discord.js Guide
discord.js

node.js - Missing Access for Slash Commands - Stack Overflow


  1. メッセージの送受信だけだと一見GUILD_MESSAGESだけでいいように見えるが、それだと何故か動かなかったので2つ設定している。 

  2. グローバルコマンドの場合は不要だと思う。確証はない