Discordボットの構築とデプロイの手順

この記事では、Node.js、公式discord.jsライブラリ、Back4appコンテナを使用してDiscordボットをビルドし、デプロイします。

Discordボットは、Discord上で自動タスクを実行するプログラムです。これらのボットはユーザーと対話し、コミュニティの行動を管理し、音楽の再生、コンテンツのモデレート、投票の整理、外部サービスやAPIとの統合など、デフォルトではDiscordで利用できない追加機能を提供することができます。

開発環境のセットアップ

この記事では、Discordサーバー上で、ユーザーの好みの難易度や質問の種類に応じたトリビア問題を出題するDiscordボットを紹介します。

このチュートリアルに従うには、以下のものが必要です:

以下のコマンドを実行して、プロジェクト・ディレクトリを設定する:

mkdir discord-bot && cd discord-bot && npm init -y

上のコマンドは、discord-botディレクトリを作成し、その中でnpmを初期化する。

次に、package.jsonファイルに移動し、以下の開始スクリプトを追加する:

"start": "node server.js"

次に、以下のコマンドを実行して、プロジェクトに必要な依存関係をインストールする:

npm install discord.js axios dotenv 

上記でインストールした依存関係には以下が含まれる:

  • discord.js:このライブラリを使用すると、Node.jsアプリケーションからDiscord APIと対話することができます。
  • dotenv: このパッケージは、ボットのトークンのような機密データの管理に 役立ちます。
  • axios:このパッケージは Trivia API をリクエストできるようにします。

これで、開発環境のセットアップと必要な依存関係のインストールが完了しました。次に、Discordボットアカウントを作成します。

Discordボットアカウントの作成

Discordボットアカウントは、ボットを実行するために作成されたDiscordアカウントです。人間が管理する通常のユーザーアカウントとは異なり、ボットアカウントはソフトウェアが管理します。

このアカウントはゲートウェイとして機能し、サーバーリソースへのアクセス、イベントへの応答、Discord コミュニティ内でのアクションの実行に必要な権限をボットに付与します。

Discord botアカウントを作成するには、まずDiscordアプリケーションを作成する必要があります。作成するには、Discord の開発者ポータルに移動し、下の画像のように “Create App” ボタンをクリックします。

Discordボットアプリの作成

ボタンをクリックすると、Discordアカウントにログインするよう促されます。ログイン後、下の画像のようにアプリの名前を付け、”Create” ボタンをクリックします。

アプリ名 Discord

次に、公開ボットスイッチをオフに切り替えて、自分だけがサーバーにボットを追加できるようにします。

次に、ボットのパーミッションを設定し、”Privileged Gateway Intents “をすべてトグルして、メンバーのプレゼンスなどサーバーのアクティビティに関するリアルタイムのアップデートにアクセスできるようにします。

ゲートウェイ・インテントの設定

次に、左サイドバーの “OAuth2 “タブに移動します。SCOPES “セクションの下にある “bot“と “application.commands “にチェックを入れます。

ボットスコープ

botスコープを選択すると、Discordがあなたのアプリケーションをbotとして認識するようになり、application.commandsスコープを選択すると、botが聞くスラッシュコマンドを作成できるようになります。

ボットチェックボックスをチェックすると、”BOT PERMISSIONS “セクションが開きます。BOT PERMISSIONS “セクションで、下図のようにAdministratorチェックボックスにチェックを入れます。

ボットの許可

このチェックボックスをオンにすると、ボットにサーバー内で可能なすべてのパーミッションが付与されます。これらの権限には、メッセージやチャンネルを管理する権限が含まれます。

“BOT Permissions” セクションの下で、Discord はこのボットをサーバーに追加するための URL を生成します。 URL を Web ブラウザに貼り付け、ボットを追加するサーバーを選択します。

Discordボットが生成したURL

次に、下図に示すように、「ボット」タブで「トークンのリセット」ボタンをクリックし、ボットトークンを取得します。

ボタンを初めてクリックすると、ボットのデフォルトトークンがリセットされ、新しいトークンが提供されます。トークンはコピーして安全に保管してください。トークンを紛失した場合、トークンは一度しか表示されないため、再度リセットする必要があります。

.envファイルを作成し、ボットトークンなどの環境変数と、ボットを追加したギルドIDや ボットのクライアントIDなどのデータを追加します:

TOKEN = "<BOT TOKEN>"
GUILD_ID = "<GUILD ID>"
CLIENT_ID = "<BOT'S CLIENT ID>"

これでDiscordボットアカウントの設定が完了しました。次にDiscordボットを作成します。

Node.jsでDiscordボットプロジェクトを構築する

このセクションでは、Discordクイズボットを作成し、ボットスラッシュコマンドをDiscordに登録するためにインストールしたパッケージを統合し、ボットとギルドメンバーとのやり取りを処理する方法を説明します。

このチュートリアルに沿ってプロジェクトのディレクトリを以下のように構成してください。

quiz-bot/
│
├── src/
│   ├── register-commands.js   # Script to register slash commands with Discord
│   └── quiz-bot.js                # Script for message handling and fetching questions
│
├── server.js                  # Entry point for your bot and script for running a server
├── node_modules/              # Node.js modules
│
├── .env                       
├── .gitignore                   
├── package.json                         
└── package-lock.json

ボットコマンドの登録

ボットはユーザーにコマンドを入力させる必要があります。Discord ボットでは、ボットに聞かせたいコマンドをスラッシュ (/) 接頭辞で定義します。

ボットの/quizコマンドをDiscordサーバーに登録するには、まず、register-commands.jsファイルにdiscord.jsから以下のインポートを追加します:

// register-commands.js
const { REST, Routes, ApplicationCommandOptionType } = require('discord.js');

上記でインポートしたクラスには以下のものが含まれる:

  • REST:このクラスは Discord API への REST API 呼び出しを可能にします。
  • ルート:このクラスは Discord API エンドポイントを生成するユーティリティ関数を提供します。
  • ApplicationCommandOptionType:このクラスでは、コマンドが受け付けるオプションのデータ型を定義できます。

次に、ボットのすべてのコマンドを含むコマンドオブジェクトの配列を定義する必要があります。コマンドオブジェクトには、名前説明オプションの配列が含まれます。

これらのオプションにより、ユーザはボットのコマンドをカスタマイズすることができます。この場合、オプション配列はクイズボットが出題する問題の難易度とタイプを指定することができます。

以下のコードブロックをregister-commands.jsファイルに追加して、コマンド配列を作成する:

// register-commands.js
const commands = [
  {
    name: 'quiz',
    description:
      'Select difficulty and question type to tailor the quiz experience.',
    options: [
      {
        type: ApplicationCommandOptionType.String,
        name: 'difficulty',
        description: "Options include 'easy', 'medium', or 'hard'.",
        required: true,
      },
      {
        type: ApplicationCommandOptionType.String,
        name: 'type',
        description:
          " Choose 'multiple' for multiple choice or 'boolean' for true/false",
        required: true,
      },
    ],
  },
];

上のコードブロックでは、commands配列でquizコマンドを定義しています。quizオブジェクトには、ボットが出題する問題の難易度や問題のタイプなど、ユーザーが/quizコマンドと一緒に入力する必要がある2つのオプションが含まれています。

次に、/quizコマンドをボットからアクセスできるように登録する必要があります。コマンドを登録するには、まずあなたの discord bot トークンとお好みのRESTバージョンでRESTクラスのインスタンスを作成する必要があります。

quizコマンドを登録し、ボットからアクセスできるようにするには、Discordボットトークンを使用してRESTクラスのインスタンスを作成し、希望のRESTバージョンを指定します。

こんな感じだ:

// register-commands.js
const rest = new REST({ version: '10' }).setToken(process.env.TOKEN);

Discordは定期的にAPIを更新しており、エンドポイント、レスポンス構造、動作に変更が生じている。

Discordプロジェクトに特定のRESTバージョンを定義することで、プロジェクトで利用可能な機能を効率的に管理することができます。上のコードブロックは Discord API の v10 を使用しています。

次に、RoutesクラスのapplicationGuildCommandsメソッドを使用して、ギルドのサーバーにボットコマンドを登録する非同期関数を定義する必要があります。

こんな感じだ:

// register-commands.js
(async () => {
  try {
    console.log('Started refreshing SLASH (/) commands.');
    await rest.put(
      Routes.applicationGuildCommands(
        process.env.CLIENT_ID,
        process.env.GUILD_ID
      ),
      { body: commands }
    );
    console.log('Successfully reloaded SLASH (/) commands.');
  } catch (error) {
    console.error(error);
  }
})();

上のコード・ブロックは、Discord APIエンドポイントに、コマンド配列をリクエスト・ボディとしてPUTリクエストを実行する。

applicationGuildCommandsメソッドを使用してAPIエンドポイントを構築し、ボットのクライアントIDと対象のギルドIDを受け取ります。

コマンドを登録した後は、ボットとギルドメンバーの間のインタラクションフローを管理する必要があります。

Discordボットとのユーザーインタラクションを処理する

ボットとギルドメンバーの間のインタラクションフローを処理するには、まず、quiz-bot.jsファイルに以下のインポートを追加します:

// quiz-bot.js
const { Client, IntentsBitField } = require('discord.js');
const axios = require('axios');

Clientクラスを使って新しいDiscordボットのインスタンスを作成し、IntentsBitFieldクラスを使ってボットがDiscord APIから受け取るインテント(イベント)を指定します。

次に、quiz-bot.jsファイルに以下のコードブロックを追加して、ボットの新しいクライアントインスタンスを作成し、ボットアプリケーションの特定のインテントを指定します:

// quiz-bot.js
const client = new Client({
  intents: [
    IntentsBitField.Flags.Guilds,
    IntentsBitField.Flags.GuildMembers,
    IntentsBitField.Flags.GuildMessages,
    IntentsBitField.Flags.MessageContent,
  ],
});

上のコードブロックは、ボットが機能するために必要な特定のインテントを定義している:

  • IntentsBitField.Flags.Guilds:このインテントにより、ボットは自分が所属しているギルド(サーバー)の名前、アイコン、ロールなどの情報を受け取ることができます。
  • IntentsBitField.Flags.GuildMembers:このインテントにより、ボットはユーザー名、ステータス、ロールなどのギルドメンバーに関する情報を受け取ることができます。
  • IntentsBitField.Flags.GuildMessages:このインテントは、ボットが参加しているギルドで送信されたメッセージへのアクセスを許可し、メッセージによってトリガーされたコマンドやイベントに応答できるようにします。
  • IntentsBitField.Flags.MessageContent:このインテントを使用すると、ボットがギルドで送信されたメッセージの実際のコンテンツにアクセスできるようになります。このインテントがないと、メッセージの内容は空になります。

readyイベントのイベントリスナーを作成します。このイベントは、Discord ボットが Discord サーバーに正常に接続し、機能する準備ができたときにトリガーされます。

以下のコードブロックをquiz-bot.jsファイルに追加して、readyイベントリスナーを作成します:

// quiz-bot.js
client.on('ready', () => {
  console.log(`✅ Bot is online and ready`);
});

次に、interactionCreateイベントのイベントリスナーを作成する必要があります。このイベントは、スラッシュコマンドの使用など、ボットが処理できるインタラクションをユーザーが実行したときに発生します。

イベントが発生すると、イベントリスナーはインタラクションオブジェクトとともにinteractionCreateイベントを受け取ります。

このオブジェクトには、インタラクションのタイプやユーザーから提供されたデータなど、インタラクションに関するすべての詳細が含まれます。

以下のコードブロックをquiz-bot.jsファイルに追加して、interactionCreateイベントリスナーを作成します:

// quiz-bot.js
client.on('interactionCreate', async (interaction) => {
 if (!interaction.isChatInputCommand()) return;
 
 if (interaction.commandName === 'quiz') {
  // Rest of the code goes here...
 }
});

上のコードブロックは、interactionCreateイベントリスナーをセットアップする。

イベントリスナーは、ユーザーとボット間のインタラクションがチャット入力コマンド(スラッシュコマンド)であるかどうかをチェックし、他のタイプのインタラクションをフィルタリングします。

そして、commandNameが quizかどうかをチェックし、ボットとユーザー間のクイズセッションを処理します。

commandNameが quizの場合、AxiosでTrivia APIのURLにGETリクエストを行い、問題とその答え(正解・不正解)を取得する。

答えをシャッフルして選択肢をランダムにし、ユーザーに表示する。

こんな感じだ:

// quiz-bot.js

await interaction.deferReply();

const difficulty = interaction.options.getString("difficulty");
const type = interaction.options.getString("type");

try {
  const url = `https://opentdb.com/api.php?amount=1&difficulty=${difficulty}&type=${type}`;
  const response = await axios.get(url);
  const data = response.data.results[0];
  const question = data.question;
  const correctAnswer = data.correct_answer;
  const options = [...data.incorrect_answers, correctAnswer];

  // Shuffle the options
  for (let i = options.length - 1; i > 0; i--) {
    const j = Math.floor(Math.random() * (i + 1));
    [options[i], options[j]] = [options[j], options[i]];
  }

  await interaction.editReply({
    content: `Question: ${question}\\nOptions: ${options.join(", ")}`,
  });
} catch (error) {
  console.error(error);
  // Notify the user in case of an error after deferring
  await interaction.followUp("Failed to fetch the question. Please try again.");
}

上のコードブロックは、まずdeferReplyメソッドでボットの返信を遅延させ、ボットがGETリクエストを処理するための時間的間隔を与えている。

次に、このコードブロックは、interaction.options.getString()を使用して、インタラクションオブジェクトからユーザーが選択した難易度と質問タイプを取得します。

その後、ボットは取得したデータから質問、正解、不正解を抽出する。

選択肢の順番をランダムにするために正解と不正解をシャッフルした後、クイズの質問と選択肢で最初の延期された返信を編集します。

次に、正解と照合し、正解か不正解かをユーザーに通知することで、ユーザーの回答を処理する必要があります。

ユーザーの反応を収集する

質問に対するユーザーの回答を収集するには、インタラクションのチャンネル(interaction.channel)でcreateMessageCollectorメソッドを使用します。

このメソッドでは、/quizコマンドを開始したユーザーが送信していないメッセージをフィルタリングし、ユーザーの回答に時間制限を設定し、収集するメッセージの最大数を指定することができます。

ユーザーがレスポンスを送信すると、createMessageCollectorメソッドはcollectイベントをトリガーする。

このイベントにリスナーを追加する必要があります。リスナーはユーザーの回答を質問の正解と照合し、適切なメッセージを送信します。

一方、ユーザーが指定された制限時間内に適切な応答を送信しない場合、createMessageCollectorメソッドは終了イベントをトリガーする。

リスナーを追加し、ユーザーに応答して、このイベントのタイムアップを通知する必要がある。

こんな感じだ:

// quiz-bot.js
const filter = (m) => m.author.id === interaction.user.id;

const collector = interaction.channel.createMessageCollector({
  filter,
  time: 15000,
  max: 1,
});

collector.on("collect", async (m) => {
  if (m.content.toLowerCase() === correctAnswer.toLowerCase()) {
    await m.reply("Correct answer! 🎉");
  } else {
    await m.reply(`Wrong answer! The correct answer was: ${correctAnswer}`);
  }
});

collector.on("end", (collected) => {
  if (collected.size === 0) {
    interaction.followUp("Time is up! No answer was provided.");
  }
});

上のコードブロックは、インタラクションが行われたチャンネル(collector) のメッセージコレクターを作成します。collectorは作成者の ID に基づいてメッセージをフィルタリングし、クイズを開始したユーザーのみが 15 秒の制限時間内に回答を提供できるようにします。

次に、このコードブロックはcollectorのために2つのイベントリスナーを作成します。最初のイベントリスナーであるcollectは、コールバックをトリガーしてユーザーの回答をチェックし、それに応じてユーザーに返信します。

2番目のイベントリスナーであるendは、制限時間内に回答がなかったことをユーザーに通知するコールバックをトリガーします。

次に、quiz-bot.jsファイルの最後にコード行を追加して、ボットとDiscordの接続を確立します:

// quiz-bot.js
client.login(process.env.TOKEN);

エントリーファイルの作成

ボットアプリケーション全体を初期化する1つのファイルであるエントリーファイルを作成します。このファイルには2つの機能があります:

  • ボットロジックのインポートエントリーファイルは、ボットコマンドを Discord サーバーに登録し、ユーザーメッセージに応答する JavaScript ファイルを必要とすることで、ボットのロジックを実行します。
  • サーバーの起動エントリーファイルは、Back4appコンテナ上でボットを動作させるためのサーバーをセットアップします。Back4appコンテナは、アプリケーションのデプロイを成功させるために公開ポートを必要とします。

以下のコード・ブロックをserver.jsファイルに追加して、サーバーを作成する:

// server.js
require('dotenv').config();
const http = require('http');
require('./src/register-commands.js');
require('./src/quiz-bot.js');

http
  .createServer((req, res) => {
    res.writeHead(200, { 'Content-Type': 'text/plain' });
    res.end('Bot is running!');
  })
  .listen(3000, () => {
    console.log('Server is ready.');
  });

上記のコードブロックは、シンプルなHTTPサーバーを作成することで、Discordボットを実行するように設定します。このサーバーはエントリー・ポイントとして機能し、コンテナの環境内でボットが機能するようにします。

上記のコードブロックは、ボットのコアロジックをインポートし、Back4appのポートを指定することで、ボットをBack4appコンテナ上にデプロイできるようにします。

エントリーファイルを作成したら、Back4appコンテナにデプロイする前にローカルでDiscordボットをテストすることができます。

Discordボットのテスト

アプリケーションのターミナルで以下のコードブロックを実行して、Discordボットを起動します:

node server.js

このコマンドを実行すると、ボットの登録に成功し、ボットの準備が整ってオンラインになったことを示すメッセージがログに記録されます。

地元でボットを始める

ボットを追加したサーバーにアクセスしてください。オンラインになっていることに気づくはずです。

テストクイズアプリ

ボットと対話するには、/quizコマンドを送信してください。ボットはあなたのクイズ問題をどのように表示させたいかをオプションで応答するはずです。

問題の難易度とタイプを選ぶ

希望のオプション(クイズのカテゴリーや難易度の選択など)を選択し、Enterキーを押します。ボットが質問を返信し、あなたの回答に15秒の制限時間を設定します。

制限時間内に答えを提出すると、ボットがあなたの答えと正解を比較し、答えの正誤を通知します。

ディスコードでのボット対応

次に、DiscordボットをBack4appコンテナにデプロイします。

AIエージェントでBack4appコンテナにDiscordボットをデプロイする

このセクションでは、DiscordボットをBack4appコンテナにデプロイし、Back4app AIエージェントを使用します。

Back4appコンテナにアプリをデプロイするには、以下のものが必要です:

  • プロジェクト内のDockerfile
  • GitHubリポジトリ:プロジェクトはGitHubリポジトリにアップロードする必要があります。
  • Back4app GitHubアプリ:リポジトリ用のBack4app GitHubアプリをインストールし、必要なパーミッションを付与します。

まず、プロジェクトのルート・ディレクトリにDockerfileを作成し、以下のコード・ブロックを追加する:

# Dockerfile
FROM node:18-alpine

WORKDIR /app

COPY package*.json ./

RUN npm install

COPY . .

EXPOSE 3000

CMD ["npm", "start"]

次に、コードをGitHubリポジトリにアップロードして、デプロイ用にアクセスできるようにする必要がある。

次のステップに進む前に、GitHubアカウントにBack4App Containers GitHub Appをインストールし、デプロイするアプリにアクセスするために必要なパーミッションが与えられていることを確認してください。

Back4appのホームページに移動し、画面右上の「New App」ボタンをクリックします。

初期化画面が表示されるので、作成したいアプリのタイプを選ぶ。

以下の画像のように、Back4app Agentオプションを選択します。

Back4app Agentオプションを選択すると、Back4app AIエージェントのページにリダイレクトされます。

AIエージェントに以下のプロンプトを与え、アプリをデプロイする:

Deploy my "YOUR_REPOSITORY_URL" repository on GitHub to a Back4App Container.
Here are the required environmental variables:
TOKEN = "YOUR_DISCORD_BOT_TOKEN"
GUILD_ID = "YOUR_GUILD_ID"
CLIENT_ID = "YOUR_CLIENT_ID"

上のプロンプトのプレースホルダーを実際の値に置き換えてください。

以下のような返事が返ってくるはずだ:

AIエージェントの反応

上記の回答は、デプロイメントプロセスが進行中であり、あなたのアプリケーションがオンラインになり、提供されたURLで利用可能になることを示しています。

Back4appコンテナのダッシュボードでデプロイプロセスを監視するか、しばらくしてからAIエージェントに以下のプロンプトを表示して現在のデプロイステータスを確認することができます。

What is the current deployment status of my web app?

以下のような返事が返ってくるはずだ:

AIエージェントの反応

または、手動でBack4Appにアプリをデプロイすることもできます。

結論

この記事では、Node.jsを使ってDiscordボットを構築する方法を学びました。Discord ボットのアカウントを作成し、ボットコマンドを登録し、ユーザーの応答を処理する方法を探りました。

さらに、あなたはDiscordボットをテストし、AIエージェントの助けを借りてBack4appコンテナ上にデプロイしました。

Discordボットは、タスクの自動化、エンターテイメントの提供などに非常に便利で、ユースケースに基づいて、ニーズに合った特定の機能を実行するようにボットを微調整することができます。

このチュートリアルで使用した完全なコードは、このGitHubリポジトリで入手できる。


Leave a reply

Your email address will not be published.