Discord.jsを触ったから書く

📅 February 01, 2022

⏱️8 min read

Discord.jsって何?

主にゲームで利用されるコミュニティアプリは様々あります。
一昔前はMSNメッセンジャーやSkypeとかでした。(ん?同じ?w

んで、今の主流はというと「Discord」です。

メッセージを飛ばしたり、ボイスチャット(通話)したり、配信なんかもできます。
個々でやり取りをするのはもちろん、グループでも可能となっていて、使い勝手はすごくいいです。

そんなDiscordをNodeで扱えるようにした便利なライブラリが「Discord.js」です。
DiscordAPIと簡単に接続できます。

https://discord.js.org/#/

DiscordでBOTを作成したかったので、その過程を本記事にまとめていきます。

前提

Discord用のBOTは事前に登録しておきます。

https://discordjs.guide/preparations/setting-up-a-bot-application.html#creating-your-bot

こちらを参考に、各種トークンがCopyできるようにしておきましょう。

チュートリアルに沿ってPingする

開発ディレクトリ作成 〜 npm init

この辺はDiscord.jsとはあまり関係ありません。
BOT開発用のディレクトリを作成します。

mkdir test-bot
cd test-bot
npm init

まーテンプレ通り。。。

Discord.jsのインストール

まずは、本題でもあるDiscord.jsをインストールします。

npm install discord.js

--save は不要です。npm v5 以降はインストール時にデフォルトで package.jsondependencies に追加してくれるようになりました。

Pingへの道のり

  1. 設定ファイルを書きます。

本来、環境変数や .envファイル 等から読み込むほうが良い気がします。
今回はGithubでも管理しないような個人のBOTなので設定ファイルを作ります。

config.json

{
  "clientId": "${Developer Potakで取得したclientId}",
  "guildId": "${BOTを動かしたいサーバのID(Discordを開発者モードにして、サーバ名を右クリックしたら取得できます)}",
  "token": "${Developer Potakで取得したtoken}"
}
  1. コマンド登録用のスクリプトを作成します。

以前までは「チャットの内容をリッスンして、とある文言だったらBOTが反応する〜」みたいなBOTの作りだったような気がするのですが、現在は「予め決められたコマンドを作成しておいて、それに対応するBOTを作る」みたいな作りが仕様になっていました。

v13の新機能なのかな?

https://scrapbox.io/discordjs-japan/Discord.js_v13%E3%81%AE%E6%96%B0%E6%A9%9F%E8%83%BD

ということで、まずは /ping に対応するBOTを作成します。

deploy-commands.js

const { SlashCommandBuilder } = require('@discordjs/builders');
const { REST } = require('@discordjs/rest');
const { Routes } = require('discord-api-types/v9');
const { clientId, guildId, token } = require('./config.json');

const commands = [
    new SlashCommandBuilder().setName('ping').setDescription('Replies with pong!')
]
    .map(command => command.toJSON());

const rest = new REST({ version: '9' }).setToken(token);

rest.put(Routes.applicationGuildCommands(clientId, guildId), { body: commands })
    .then(() => console.log('Successfully registered application commands.'))
    .catch(console.error);

こちらを実行すると、Discordの指定サーバ内でコマンドが有効になります。

【画像】

今回は、ギルドコマンドと呼ばれる特定のサーバで有効になるコマンドを作成しました。
どのサーバでも使えるようなグローバルコマンドも作れるようですが、めんどくさそうなので。。。

  1. コマンドに対応するスクリプトの作成

2でBotを呼び出すための準備が整いました。実際にレスポンス用のコードを書いていきます。

index.js

// Require the necessary discord.js classes
const { Client, Intents } = require('discord.js');
const { token } = require('./config.json');

// Create a new client instance
const client = new Client({ intents: [Intents.FLAGS.GUILDS] });

// When the client is ready, run this code (only once)
client.once('ready', () => {
    console.log('Ready!');
});

// ココがメイン
client.on('interactionCreate', async interaction => {
    if (!interaction.isCommand()) return;

    const { commandName } = interaction;

    if (commandName === 'ping') {
        await interaction.reply('Pong!');
    }
});

// Login to Discord with your client's token
client.login(token);

interactionCreate イベントに対応するコードを書きます。
interactionのcommandNameがpingだった場合、replyを利用して「Pong!」と返します。
(そのままw)

  1. 実行
node deploy-commands.js
node index.js

このように順に実行してもOKです。
私は、毎回2つ呼ぶのが面倒くさいので、npmのプロジェクトとして実行しました。

package.json

・・・,
"scripts": {
  "prestart": "node deploy-commands.js",
  "start": "node index.js"
},
・・・

上記のように package.json を書いた上で、ターミナルで

npm run start

すると、startコマンドに対応する prestart が直前に自動実行されます。

https://docs.npmjs.com/cli/v8/using-npm/scripts

 Ping Pong

準備完了です。
サーバ上のBOTはオンラインになりましたか?
オフラインの方は、コンソールのエラーログを見て解決してあげてください。

オンラインになった方はPingしてみましょう。

/ping

【画像】

Pong

【画像】

完璧です。

後は、想像力を働かせてBOTのコマンドを増やしていくだけです。

SlashCommandBuilderが使いやすい。

コマンドに引数がほしいときは、SlashCommandBuilderを用いてサブコマンドを登録していきます。

const commands = [
    new SlashCommandBuilder().setName('ping').setDescription('Replies with pong!').
  new SlashCommandBuilder().setName('echo').addStringOption(option =>
    option.setName('input').setDescription('The input to echo back').setRequired(true))
]
    .map(command => command.toJSON());

このように登録すると、echoコマンドのサブコマンドとして、inputの入力を求めるようになります。

応答する側は、

const string = interaction.options.getString('input');

とすることで、inputとして入力された値を取得することができます。

タイムアウト

私が作成したBOTは、入力された値を元に他の外部APIを呼び出して処理を行います。
よって、処理に少し時間がかかります。
何も対策せずに実行すると、、、

【画像】

このようにエラーとなってしまいます。たしか3秒。
そんなときは、Deferred responsesを使いましょう。

https://discordjs.guide/interactions/slash-commands.html#deferred-responses

if (interaction.commandName === 'ping') {
        await interaction.deferReply();
        await wait(4000);
        await interaction.editReply('Pong!');
}

deferReplyを呼び出すことで、タイムアウト時間を伸ばす事ができます。(15分?
editReplyで結果を返してあげましょう。

以上。
便利じゃの!!