こんにちは。ATOM 事業部エンジニアのyuinaです。
新年一発目のブログということで、あけましておめでとうございます。本年もよろしくお願いいたします。
今回は、年末の自由工作的な感じで Discord の Bot を作ってみたのですが、
思ったよりも簡単に作れたのでその紹介をしていこうと思います。
なぜ作ろうと思ったのか?
Slack の Bot は以前個人的に作っていたものがあったのですが、
元データの提供が終了してしまい、メンテすることもなくなってしまいました。
Slack の Bot はさくっと作ることができる状態ですが、他のツールで Bot 作ったことがないなと思い、一番最初に思いついた Discord で作ってみる方向に。
作ってみた
/dice
と入力すると、1~6 のランダムな数字を返す Bot を試しに作ってみました。
使用したのは以下の通りです。
node.js v20.2.0 typescript v5.1.3 discord.js v14.14.1
discord の Bot を作る場合は Python または JavaScript という選択肢がライブラリに対する日本語サポートもあるため書きやすいと思います。
今回は TypeScript の勉強も兼ねて TypeScript で書いてみました。 そのため、discord.js のライブラリを利用しています。
Bot の作成
Discord の Bot を作成するには、Discord の開発者サイトにアクセスして App を作成し、適当なサーバーに Bot を招待する必要があります。
この部分は公式ドキュメント通りにやるのが一番早いです。
https://discord.com/developers/docs/intro
Bot の実装
ざっくりこんな感じになりました。一部継承していたりする部分は割愛しています。
main.ts
import { Client, Events, GatewayIntentBits, Interaction } from "discord.js"; import dotenv from "dotenv"; dotenv.config(); import slash from "@/commands/slash"; import { Dice } from "@/commands/slash/dice"; const client = new Client({ intents: [GatewayIntentBits.Guilds, GatewayIntentBits.GuildMembers], }); client.once(Events.ClientReady, () => { console.log(`準備OKです! ${client.user?.tag}がログインします。`); }); client.on(Events.InteractionCreate, async (interaction: Interaction) => { // スラッシュコマンドの処理 if (interaction.isChatInputCommand()) { let action = null; switch (interaction.commandName) { case Dice.commandName: action = new Dice(); break; default: console.error( `${interaction.commandName}というコマンドには対応していません。` ); } if (action != null) { try { await action.execute(interaction); } catch (error) { await interaction.followUp({ content: "コマンド実行時にエラーになりました。[" + error + "]", ephemeral: true, }); } } else { await interaction.reply({ content: "コマンドが登録されていません。", ephemeral: true, }); } } }); // ログインします client.login(process.env.DISCORDAPPBOTTOKEN);
dice.ts
import { SlashCommandBuilder, CommandInteraction } from "discord.js"; import { Slash } from "@/commands/slash/slash"; export class Dice extends Slash { static readonly commandName: string = "dice"; slashCommand: SlashCommandBuilder; constructor() { super(); this.slashCommand = new SlashCommandBuilder() .setName(Dice.commandName) .setDescription("運命のダイスロール"); } async execute(interaction: CommandInteraction) { const result = Math.floor(Math.random() * (6 + 1 - 1)) + 1; const res = result.toString(); await interaction.reply("サイコロの出目:" + res); } }
これでざっくり動く部分はできたのですが、スラッシュコマンドを使っているため、事前に以下コードを実行してコマンドの登録はお忘れなく。。。。
deploy-command.ts
import dotenv from "dotenv"; import { REST } from "@discordjs/rest"; import { Routes } from "discord-api-types/v10"; dotenv.config(); import { Dice } from "@/commands/slash/dice"; // 登録コマンドを呼び出してリスト形式で登録 const dice = new Dice(); const commands = [dice.slashCommand.toJSON()]; // DiscordのAPIには現在最新のversion10を指定 const rest = new REST({ version: "10" }).setToken( process.env.DISCORDAPPBOTTOKEN ?? "" ); // Discordサーバーにコマンドを登録 (async () => { try { await rest.put( Routes.applicationGuildCommands( process.env.DISCORDAPPLICATIONID ?? "", process.env.DISCORDGUILDID ?? "" ), { body: commands } ); console.log("サーバー固有のコマンドが登録されました!"); } catch (error) { console.error("コマンドの登録中にエラーが発生しました:", error); } })();
Discord の Bot は常駐させる必要があるため、今回はローカル環境で試しています。
サーバーに置きたい場合は SlackBot と同様に一番小さい EC2 や GCP のインスタンスを立ててそこで動かすのが簡単かなと。
まとめ
たったこれだけでダイスをふる Bot ができるというだけで結構お手軽でした。
この状態から更に Bot の用途に合わせて色々機能を追加していけば、割となんでもできそうな気がします。
実際この Bot をどんどん拡張して他にも機能を作っています。
今回はスラッシュコマンドを使ってみましたが、モーダルを出して入力した内容を基に何かしらの処理をしたり、ボタンクリックに反応させたりできるので、
あまりコマンド等よく分からなくても、操作しやすいものは作れそうだなと思いました。
TypeScript の感想ですが、ちゃんと TypeScript で書いたのは初めてだったので、型周りで最初で苦戦しました。
ですが、慣れてきたら Promise 周りが逆にわかりやすくて普通に JavaScript で書くよりも楽に書けた気がします。
(多分普段遣いのエディタが VSCode だから説)
そして、今回は GitHub Copilot をフル活用し、生成されたコードでよくわからないなと思った部分は ChatGPT に聞きまくりました。
普段 Golang ばっかり書いているので、慣れてない言語で使ってみると、結構勉強になります。
普段の業務でも GitHub Copilot と ChatGPT は活用していますが、今回は特に使いまくったので、AI とちょっとは仲良くなれたんじゃないかなと勝手に思ってます。
ということで簡単にはなりますが、私の冬休みの自由工作でした。