🤖

Glitchを使ってDiscordのBotを作ってみる

2022/12/17に公開1

この記事は ウィルゲート Advent Calendar 2022 - Adventar の17日目の記事です。
昨日はikさんの外部リンクがDRに換算されるまでの期間の調査と効果の考察という記事でした。

はじめまして、ふわりです。
先日DiscordのBotを作る機会があったので備忘録代わりに残しておこうと思います。

この記事で話すこと

  • Glitchの簡単な使い方
  • DiscordのBotを作る流れ

この記事で話さないこと

  • JavaScriptやNode.jsについて
  • 実装内容の解説
  • 複雑なBotの作り方や運用の仕方

Glitchについて

Glitch(グリッチ)とは、ブラウザ上でNode.jsを用いたWebアプリの開発、そして公開までできてしまう無料のWebサービスです。オンラインエディターやターミナルまで用意されており、至れり尽くせりなサービスとなっています。
一応、実行時間やリクエスト数などの制限があるのですが、お試し的なプロジェクトには十分かなと思います。
細かい制限については以下を参照。
https://help.glitch.com/kb/article/17-technical-restrictions/

開発環境

  • Glitch (エディター/ビルド環境/実行環境)
  • Node.js v16.14.2
  • Discord.js v14.7.1 (2022/12/12時点で最新)

Glitchで使えるNode.jsは少し古い

残念なことにGlitchで利用できるNode.jsは少し古いものとなります。今回はGlitchで利用できる最新のバージョンかつDiscord.js v14.7.1がサポートしているNode.js v16.14.2を利用していきます。
Glitchで利用できるNode.jsは以下を参照。
https://support.glitch.com/t/what-is-the-latest-supported-node-js-version/51728/1

作ってみる

今回はDiscord上で /modal と送信したら文字を入力できるモーダルを出すアプリを作ってみます。

前提

GlitchやDiscordのアカウントは既に作ってあるものとします。さらにDiscordアプリ上から二要素認証の設定を済ませておいてください。
最終的なディレクトリ構造は以下の通り(あまり関係のないファイルは載せていません)。

root
├─ commands/
│    └─ modal.js
├─ .env
├─ deploy.js
├─ server.js
└─ package.json

1. 環境構築

1-1. 新規プロジェクトの作成

GlitchのトップページのNew projectからglitch-hello-nodeを選択して新しいプロジェクトを作成する。

1-2. 必要のないファイルやディレクトリを削除する

public/ src/を削除する。

1-3. Node.js のバージョンを変更し、Discord.js をインストールする

package.jsonの中身を以下のようにして保存する。
※ package.jsonを保存すると勝手に npm install が走るようになっています。

package.json
~ 略 ~
"dependencies": {
    "discord.js": "^14.7.0"
},
"engines": {
    "node": "^16.14.0"
},
~ 略 ~

1-4. DiscordでトークンやIDを発行する

Discord Developer Portalで各Botの管理やトークンの発行などすることが出来ます。ページを開いたら右上のNew Applicationを押して好きにBotの名前を入力しCreateを押すとアプリが作成されます。

次に左側のメニューからBotを開き、Add Botから新しいBotを作成します。作成すると以下のような画面になるので、Reset Tokenから新しいトークンを発行してください。トークンが発行できないときは二要素認証の設定が済んでいない可能性があります。Discordアプリから二要素認証の設定をしてから再度発行してみてください。

トークンの発行ができたらプロジェクトに.envを作成し、トークンを貼り付けます。
Glitchでは以下のような画面で各環境変数を設定することができます。今回はVariable NameをTOKENとし、Variable Valueに先程コピーしたトークンを貼り付けてください。

今回作るBotでは更にCLIENT IDGUILD IDという2つのIDが必要になります。
CLIENT IDDiscord Developer Portalの左側のメニューのOAuth2にあります。
GUILD IDはDiscordアプリからコピーする必要があります。Botを実際に運用したいサーバーのアイコンを右クリックするとメニューが表示されます。そうしたらメニューの中からIDをコピーを選択してください。
それぞれのIDは先程作成した.envの中にCLIENT_IDGUILD_IDとして保存してください。

2. メイン処理の実装

実際の処理を実装していきます。なお、当記事においてコードの解説は行わないのであしからず。
まずはメインに相当する処理を実装していきます。主にDiscordへのログインや各コマンドの読み込み、Discordへの入出力を担当します。

server.js
const fs = require("node:fs");
const path = require("node:path");
const { Client, Collection, Events, GatewayIntentBits } = require("discord.js");
const TOKEN = process.env.TOKEN;

const client = new Client({ intents: [GatewayIntentBits.Guilds] });

// 各コマンドの読み込み
client.commands = new Collection();
const commandsPath = path.join(__dirname, "commands");
const commandFiles = fs
  .readdirSync(commandsPath)
  .filter((file) => file.endsWith(".js"));

for (const file of commandFiles) {
  const filePath = path.join(commandsPath, file);
  const command = require(filePath);
  client.commands.set(command.data.name, command);
}

// BOTが稼働できる状態にあるかの確認
client.once(Events.ClientReady, () => {
  console.log("Ready!");
});

// Discordからコマンドを受け取り、それに応じた処理を行う
client.on(Events.InteractionCreate, async (interaction) => {
  if (!interaction.isChatInputCommand()) return;

  const command = client.commands.get(interaction.commandName);

  if (!command) return;

  try {
    await command.execute(interaction);
  } catch (error) {
    console.error(error);
    await interaction.reply({
      content: "このコマンドの実行中にエラーになりました。ごめんね。",
      ephemeral: true,
    });
  }
});

// モーダルで受け取った入力値をDiscordに送信する
client.on(Events.InteractionCreate, async (interaction) => {
  if (!interaction.isModalSubmit()) return;

  const userName = interaction.fields.getTextInputValue("userNameInput");
  const selfIntroduction = interaction.fields.getTextInputValue(
    "selfIntroductionInput"
  );
  console.log({ userName, selfIntroduction });
  await interaction.reply({
    content: "名前:\n" + userName + "\n" + "自己紹介:\n" + selfIntroduction,
  });
});

client.login(TOKEN);

3. コマンドの実装

メインの実装が完了したので、次にコマンドの実装をしていきます。
今回はDiscord上で/modalと送信したら自己紹介を入力できるモーダルを表示するようにします。
もしも新しいコマンドを増やしたい場合はcommands/の中に追加していくとよいです。

commands/modal.js
const {
  ActionRowBuilder,
  Events,
  ModalBuilder,
  TextInputBuilder,
  TextInputStyle,
  SlashCommandBuilder,
} = require("discord.js");

module.exports = {
  // スラッシュコマンドの登録
  data: new SlashCommandBuilder()
    .setName("modal")
    .setDescription("モーダルを表示するよ!"),
  // スラッシュコマンドを受け取ると以下が実行される
  async execute(interaction) {
    if (!interaction.isChatInputCommand()) return;

    if (interaction.commandName === "modal") {
      const modal = new ModalBuilder()
        .setCustomId("myModal")
        .setTitle("My Modal");

      // モーダルを構成するコンポーネントを定義
      const userNameInput = new TextInputBuilder()
        .setCustomId("userNameInput")
        .setLabel("あなたの名前は?")
        .setStyle(TextInputStyle.Short);

      const selfIntroductionInput = new TextInputBuilder()
        .setCustomId("selfIntroductionInput")
        .setLabel("自由に自己紹介してね!")
        .setStyle(TextInputStyle.Paragraph);

      // コンポーネントの登録
      const firstActionRow = new ActionRowBuilder().addComponents(
        userNameInput
      );
      const secondActionRow = new ActionRowBuilder().addComponents(
        selfIntroductionInput
      );
      modal.addComponents(firstActionRow, secondActionRow);

      // モーダルの表示
      await interaction.showModal(modal);
    }
  },
};

4. コマンドをデプロイする

ここまでの手順で各機能の実装は完了です。しかし、まだBotを使用することはできません。実装したコマンドをDiscordへ登録する必要があります。以下の実装が完了したら、Glitchのターミナルからnode deploy.jsを実行しましょう。

deploy.js
const { REST, Routes } = require('discord.js');
const { CLIENT_ID, GUILD_ID, TOKEN } = process.env;
const fs = require('node:fs');

// 各スラッシュコマンドのJSON形式で格納する
const commands = [];
const commandFiles = fs.readdirSync('./commands').filter(file => file.endsWith('.js'));

for (const file of commandFiles) {
	const command = require(`./commands/${file}`);
	commands.push(command.data.toJSON());
}

// スラッシュコマンドを登録するためのAPI
const rest = new REST({ version: '10' }).setToken(TOKEN);

// デプロイ
(async () => {
	try {
		console.log(`Started refreshing ${commands.length} application (/) commands.`);

		const data = await rest.put(
			Routes.applicationGuildCommands(CLIENT_ID, GUILD_ID),
			{ body: commands },
		);

		console.log(`Successfully reloaded ${data.length} application (/) commands.`);
	} catch (error) {
		console.error(error);
	}
})();

5. BotをDiscordのサーバーへ招待する

BotをDiscordのサーバーへ招待して動作確認してみましょう。
Discord Developer Portalの左側のメニューからOAuth2 > URL Generatorを開き、以下の項目にチェックを入れます。

  • SCOPES
    • bot
    • applications.commands
  • BOT PERMISSIONS
    • Read Messages/View Channels
    • Send Messages
    • Use Slash Commands

下の方に表示されるGENERATED URLのリンクを開けば追加できます。
※ Botの追加先は必ず自分のサーバーにしましょう。公開サーバーに追加して迷惑をかけないように

Botを追加したDiscordサーバーを開いて/modalと送信して以下の画面が表示されるので、適当に入力し送信してください。入力した内容が返ってきたら動作確認は完了です。

最後に

今回はじめてDiscordのBotを作ったのですが、思っていたよりも簡単に作れるというのが最初の感想でした。例ではDiscord.jsというライブラリを使用しましたが、他にもDiscord.pyDiscordGoなど、コミュニティによってメジャー言語ごとのライブラリが用意されているので自身の使い慣れた言語で作ってみるのが良いかと思います。※ Discordの各ライブラリ

今回使用したもの以外にもDiscordでは様々なAPIが公開されており、各ライブラリでも大体のものには対応しています。他にもDBやGoogle スプレッドシートなどを活用してデータを永続化したり、Webページを用意して使いやすくしたりなど工夫次第で大抵のことは実現できるかと思います。

ぜひこの記事がDiscordのBotを作る一歩目の道標になれたら幸いです。

明日の記事はりばすとさんのCollaを使ってメンバーのことをふんわり知っていますです!

参考リンク

Discussion

あまなつあまなつ

コメント失礼します。
commands/直下に新しくコマンドを作りたいのですがどのようにすればよいでしょうか?
よろしくお願いします。