あまり前置きが長くなっても飽きてしまうので、そろそろ手を動かし始めたいと思います。ここでようやく最初のコードが出てきますが、まずはデプロイ先の環境で動作確認をしてみましょう。なにごともまずは動作確認からです。足元を踏み固めて基礎を作ることはとても大切です。 このHow-Toではボットを作成するためのプラットフォームとして[Glitch](https://glitch.com/)と[Replit](https://replit.com)を使い、そこでJavaScriptのコードを書いていくので、まずはユーザー登録をしましょう。英語のサイトですが、それほど苦労せずユーザー登録ができるはずです。 # JavaScriptって何? Discordのボットが作れるという話だけを聞いて、勢いだけでここに来てしまった人もいるかもしれません。ここでは逐一JavaScriptのコードの内容について解説はしないので、最低限のJavaScriptのコードを読み書きできる必要はあります。 お金をかけられるのなら書籍で体系的に学ぶこともできますし、ウェブ上にも文献があります。無料で講習を受けられるプログラミング講座などもあるので自分に合ったものを選ぶとよいでしょう。私のおすすめは以下のふたつです。 - [JavaScript 第7版](https://www.oreilly.co.jp/books/9784873119700/) (O'Reilly) - [現代のJavaScriptチュートリアル](https://ja.javascript.info) 「そうは言ってもなんかいい方法あるでしょ?」とか「自分は手軽にボットを作りたいんですけど」と思うかもしれません。ですが、自分の人生を思い返してみてください。「学問に王道なし」と言うように、多少なりとも泥臭く勉強や努力をしなければなし得ないのです。 ## JavaScriptのコーディングスタイル JavaScriptにはさまざまなコーディングスタイルがありますが、その中でも興味深いもののひとつに文末セミコロンの有無があります。C言語の影響を受けているプログラミング言語では文の終わりに `;` を配置することが必要とされていますが、JavaScriptでは[自動セミコロン挿入](https://developer.mozilla.org/ja/docs/Web/JavaScript/Reference/Lexical_grammar#自動セミコロン挿入)と呼ばれる機能が存在し、文法的に省略できる文脈であれば省略できます。 この連載でもJavaScriptのコードにはセミコロンを付けていませんが、それはミスではなく意図的なものです。 ## 私はPythonでやりたいんですが? そういう人もいるでしょう。[discord.py](https://discordpy.readthedocs.io/ja/stable/)を使う上ではPythonでもあまり変わりません。ただし、GlitchはPythonをサポートしていないので、Replitを使うことになります。 私がおすすめする書籍やドキュメントは以下です。 - [Python言語リファレンス (公式)](https://docs.python.org/ja/3.13/reference/index.html) - [入門Python 3](https://www.oreilly.co.jp/books/9784873117386/) (O'Reilly) - [動かしながら学ぶ Python Discord Bot を作ってプログラミングの基礎を学ぼう](https://ebook.sbcr.jp/product/4815625825) (SBクリエイティブ) 通常Pythonではあまり使われない[asyncio](https://docs.python.org/ja/3.13/library/asyncio.html)の知識も必要になるので抑えておくといいでしょう。 # Discordでボットを登録する ## 開発者ページでボットを作成する Discordのボットを作るには、Discordの開発者ページでボット情報を登録する必要があります。[開発者向け](https://discord.com/developers)ページにアクセスし、はじめようのボタンをクリックすると、アプリケーション一覧の画面が表示されます。 New Applicationを選択するとダイアログが表示されるので、ボットの名前と利用規約への同意をしてCreateをクリックします。すぐにボット情報が登録されるので、General Informationの項目でApplication IDとPublic Keyの項目を控えておきます。次にBotの項目でReset Tokenボタンをクリックしてボットトークンを払い出します。すると新しいボット用のトークンが生成されるので、これも控えておきます。 > ⚠️ ボットトークンの取扱いには注意してください > > これらの情報が万が一流出すると、他人がボットを操作することができてしまいます。不特定多数がアクセスできる場所には絶対に書き込まないでください。 ## ボットをサーバーに招待する Installationの項目にInstall Linkがあり以下のようなURLが表示されています。これがあなたのボットのインストールURLです(以下のURLはサンプルです)。 ``` https://discord.com/oauth2/authorize?client_id=0123456789 ``` このURLにブラウザからアクセスすると、ボットを導入するための画面が表示されるので、サーバーに追加からインストール先のサーバーを選択します。 自分の管理下にあるサーバーならどこにでもインストールできますが、開発でテストメッセージが飛び交うことを想定すると、自分のプライベートなサーバーで試した方が無難です。 ボットにインストールしてもサーバー上ではなにも起きていないように見えます。それもそのはず、まだボットを動かすためのコードをひとつも書いていないからです。これからそれを書いていきましょう。 ## ボットの名前を変える ボットの名前は最初「bot1373638205999681638」のような無骨なものになっていますが、Discord側で変更できます。Discordのアプリケーションの設定画面のBotの項目にUsernameの欄があるので、好きな名前をつけてあげましょう。 # Glitch向けの最初のコード ユーザー登録が済むとProjectのページにアクセスできるようになるので、Node.jsの項目でBlank Nodeを選択します。ちょっと時間はかかりますが、そのうち空のプロジェクトが作成されます。 ## プロジェクトファイルの修正 空のプロジェクトとはいえど、最低限のサンプルファイルがあります。いろいろ書かれていますが、プロジェクトがごちゃごちゃする上にありがた迷惑なので、以下のファイルは削除してしまいましょう。 - public/ - src/ - README.md - TODO.md さらに最初からインストールされているライブラリを削除するためにpackage.jsonのdependenciesの中身を空にして、Node.jsバージョンを`16.x`にします。2025年5月現在でもGlitchのNode.jsのバージョンは16が最新です。 ```json { ...省略... "dependencies": {}, "engines": { "node": "16.x" }, ...省略... } ``` そしてserver.jsの中身はこれだけにします。 ```js console.log('Hello, world!') ``` 画面下部のTERMINALから端末を表示して、 ```bash node server.js ``` と打ち込みます。すると `Hello, world!`と表示されます。無事に基本のプログラムが動作することの確認ができました。 > ⚠️ SafariではTERMINALがうまく動作しません > > macOSユーザーはGoogle Chromeを使うとよいでしょう。 ## いろんなコードを試してみる さてserver.jsにJavaScriptのコードが書けばそれを実行できることがわかりました。Web向けのJavaScriptとは異なり、Node.jsという環境上で動かすことになるので、ウェブ向けの機能は利用できませんが、それ以外は同じように動作します。 ## セットアップコードを書く 再びGlitchに戻って、ボット用のセットアップをします。package.jsonを選択すると+ADD PACKAGEの項目が表示されるのでクリックすると、パッケージ名を入力する欄が表示されます。そこに「discord.js」と入力して最初に出てきたパッケージをインストールします。 するとdependenciesにdiscord.jsの項目が追加されますが、最新バージョンがインストールされるので、Node.js 16をサポートしているバージョンに変更します。 ```json "dependencies": { "discord.js": "13.17.1" }, ``` .envは環境変数を記載するためのファイルで、ここに先ほどのボットトークンを書き込みます。`TOKEN`という名前に対して、トークンの値を設定します。 環境変数内のデータは実行時にパラメータとしてプログラムに渡され、プログラム中で参照することができます。 > 🤔 環境変数を使わなくてもいいのでは? > > ボットトークンを直接コード中に記述することもできますが、そうすると書いたコードをどこかにアップロードしたり共有したりといったことがしにくくなるので、重要な情報はなるべく環境変数で与えたほうが無難です。 > > 自分はそんなことはしないと思うかもしれませんが、人間は往々にして過ちを犯す生き物なので、面倒くさがらず環境変数を使いましょう。 次にserver.jsを書き換えていきます。discord.jsの[Creating Your Bot](https://github.com/discordjs/guide/tree/v13/guide/creating-your-bot)を参考に以下のようにします。うまくいけば端末に「Ready!」と表示されるはずです。 ```js const { Client, Intents } = require('discord.js') const client = new Client({ intents: [Intents.FLAGS.GUILDS] }) // ボットが起動したときに1回だけ実行されるコード // この部分はログインしたあとで中身が実行される client.once('ready', () => { console.log('Ready!') }) // ボットトークンを使ってログインをしてボットを起動する client.login(process.env.TOKEN) ``` > ⚠️ 古いドキュメントを参照する必要があります! > > GlitchでサポートしているNode.jsのバージョンが16止まりなので、discord.jsのドキュメントも最新のドキュメントではなくv13のものを参照する必要があります。現状GitHub上でしかホスティングされていない点に注意してください。 ## コマンドに反応させる ボットにスラッシュコマンドを送ると返事を返すようにしてみましょう。まずは追加のライブラリをインストールします。インストール時に依存関係の警告が出ますが問題ないので無視します。 ```json "dependencies": { "discord.js": "13.17.1", "@discordjs/builders": "1.7.0", "@discordjs/rest": "2.2.0", "discord-api-types": "0.38.8" }, ``` スラッシュコマンドの登録にはApplication IDが必要です。これも.envファイルに追加した上で、以下のページを参考にserver.jsのコードを次のように書き換えます。 - [Creating Commands](https://github.com/discordjs/guide/blob/v13/guide/creating-your-bot/creating-commands.md) (v13のマニュアル) - [Advanced Creation](https://discordjs.guide/slash-commands/advanced-creation.html) (最新のマニュアル) ```js const { Client, Intents } = require('discord.js') const { REST } = require('@discordjs/rest') const { Routes } = require('discord-api-types/v9') const { SlashCommandBuilder } = require('@discordjs/builders') const client = new Client({ intents: [Intents.FLAGS.GUILDS] }) const commands = [ // /pingコマンドを定義する。パラメータはない new SlashCommandBuilder() .setName('ping') .setDescription('ボットが応答します'), // /echoコマンドを定義する。入力した文字列をそのまま返す new SlashCommandBuilder() .setName('echo') .setDescription('ボットがオウム返しします') .addStringOption(option => option .setName('text') .setDescription('内容') .setRequired(true)) ].map(command => command.toJSON()) const rest = new REST({ version: '10' }).setToken(process.env.TOKEN) // コマンドを登録する rest.put(Routes.applicationCommands(process.env.APPLICATION_ID), { body: commands }) .then(() => console.log('Successfully registered application commands.')) .catch(console.error); // ボットが起動したときに1回だけ実行されるコード // この部分はログインしたあとで中身が実行される client.once('ready', () => { console.log('Ready!') }) // コマンドが送信されたときの挙動を定義する client.on('interactionCreate', async interaction => { if (interaction.isCommand()) { if (interaction.commandName == 'ping') { // pingコマンドのときは"pong"と返す interaction.reply('pong') } else if (interaction.commandName == 'echo') { // echoコマンドのときはtextに入力された内容をそのまま返す interaction.reply(interaction.options.getString('text')) } else { // それ以外のコマンドは想定していないのでエラーにする throw new Error('未定義のコマンドです') } } }) // ボットトークンを使ってログインをしてボットを起動する client.login(process.env.TOKEN) ``` コードが結構長くなってしまいましたが、ボットを使った基本的なリクエストとレスポンスの制御はこれですべてです。スラッシュコマンドが反映されるまで少し時間がかかります。反映されないときには再起動することで即時反映されるので試してみてください。 Discordの画面から `/` を打ち込んでスラッシュコマンドの候補を見ると `/ping` と `/echo` コマンドが追加されているので、それぞれ実行するとボットから応答が返ってきます。 ## Glitchの注意点 Glitchで作ったアプリケーションはなにも要求がないと、5分後に自動的にスリープに入ります。5分以内になにかしらの要求を送信すればアプリケーションが稼働し続けますが、あまり現実的ではないでしょう。また、アプリケーションの名前がわかっていれば誰でもコードにはアクセスできてしまいます(環境変数の.envの中身は閲覧できません)。 毎月$8支払ってProプランに加入することで最大5つのアプリケーションを常時起動にできますが、そうなったらGlitchを使うよりもVPSを契約したほうが安上がりかもしれません。 # Replit向けの最初のコード [Replit](https://replit.com)でユーザー登録してダッシュボードにアクセスすると、+ Create Appが見えるので、選択すると新しいアプリケーションを作るウィザードが表示されます。Choose a TemplateからNode.jsを選択、名前を入力して新しいアプリケーションを作成します。 ## プロジェクトファイルの修正 Replitでは最新のNode.js環境が利用できるので、discord.jsも最新バージョンを指定できます。package.jsonの記述は次のようになるでしょう。 ```json "dependencies": { "discord.js": "14", "@discordjs/builders": "1.11.2", "@discordjs/rest": "2.5.0", "discord-api-types": "0.38.8", "@types/node": "^22.13.11" } ``` ## セットアップコードを書く ReplitにはGlitchのように環境変数を書くファイルがありませんが、代わりにSecretと呼ばれる同等の機能があります。IDEの左側にツールの項目があるので、その中からSecretを選択します(分かりにくいですがスクロールできます)。ここで名前と値を設定できるので、それぞれ`TOKEN`と`APPLICATION_ID`を登録します。 次に最新バージョンのdiscord.jsの[ガイド](https://discordjs.guide/creating-your-bot/main-file.html)を参照して最初のコードを書きます。 ```js const { Client, Events, GatewayIntentBits } = require('discord.js') const client = new Client({ intents: [GatewayIntentBits.Guilds] }) // ボットが起動したときに1回だけ実行されるコード // この部分はログインしたあとで中身が実行される client.once(Events.ClientReady, readyClient => { console.log(`Ready! Logged in as ${readyClient.user.tag}`) }) // ボットトークンを使ってログインをしてボットを起動する client.login(process.env.TOKEN) ``` ## コマンドに反応させる ボットにスラッシュコマンドを送ると返事を返すようにしてみましょう。これも基本的にはGlitchと同じコードを書けば動くようになるはずです。違いは最新のdiscord.jsのコードに変わっていることだけです。 ```js const { Client, Events, GatewayIntentBits } = require('discord.js') const { REST } = require('@discordjs/rest') const { Routes } = require('discord-api-types/v10') const { SlashCommandBuilder } = require('@discordjs/builders') const client = new Client({ intents: [GatewayIntentBits.Guilds] }) const commands = [ // /pingコマンドを定義する new SlashCommandBuilder() .setName('ping') .setDescription('ボットが応答します'), // /echoコマンドを定義する。入力した文字列をそのまま返す new SlashCommandBuilder() .setName('echo') .setDescription('ボットがオウム返しします') .addStringOption(option => option .setName('text') .setDescription('内容') .setRequired(true)) ].map(command => command.toJSON()) const rest = new REST({ version: '10' }).setToken(process.env.TOKEN); // コマンドを登録する rest.put(Routes.applicationCommands(process.env.APPLICATION_ID), { body: commands }) .then(() => console.log('Successfully registered application commands.')) .catch(console.error); // ボットが起動したときに1回だけ実行されるコード // この部分はログインしたあとで中身が実行される client.once(Events.ClientReady, readyClient => { console.log(`Ready! Logged in as ${readyClient.user.tag}`) }) client.on(Events.InteractionCreate, async interaction => { if (interaction.isCommand()) { if (interaction.commandName == 'ping') { interaction.reply('pong') } else if (interaction.commandName == 'echo') { interaction.reply(interaction.options.getString('text')) } else { throw new Error('未定義のコマンドです') } } }) // ボットトークンを使ってログインをしてボットを起動する client.login(process.env.TOKEN) ``` ## おまけ:Pythonでのコード [discord.py](https://discordpy.readthedocs.io/ja/stable/)を使ったPythonのコードは次のようになります。discord.jsと違うのは、[デコレータ](https://docs.python.org/ja/3.13/glossary.html#term-decorator)によるコマンドごとの関数定義になっているところです。 ```python import os import discord from discord.ext import commands intents = discord.Intents.default() bot = commands.Bot( application_id=int(os.environ['APPLICATION_ID']), command_prefix=None, intents=intents ) # ボット起動後に一度だけ実行される @bot.event async def on_ready(): # スラッシュコマンドを登録する await bot.tree.sync() # /ping コマンドに対する応答 @bot.tree.command(name="ping", description="ボットが応答します") async def money_check(interaction: discord.Interaction): await interaction.response.send_message('pong') bot.run(os.environ['TOKEN']) ``` ## Replitの注意点 Starterプランでは開発環境で月間1GB、本番環境で月間10GBのインターネット側への転送上限があります。また、[常時起動](https://docs.replit.com/cloud-services/deployments/reserved-vm-deployments)させ続けることはできず、ユーザーからの報告によれば、数分から30分程度で停止するようです。 > 💬 無料プランでも動かし続ける裏技とかありませんか? > > GlitchとReplitの双方ともアクティビティがないことを検知して稼働を停止するので、定期的になんらかの通信を発生させることによって、無料プランでも稼働させ続けることは可能と考えられますが、プラットフォーム側が意図しているサービスの利用方法であるとは考えにくいので、ここではあえて紹介しません。 # それで次は? いやいや、次はありません。これで全部です。Discordを介してユーザーとボットがやりとりする方法はこれまで説明してきた通りですし、ユーザーからの入力を受け付けて返答をすることができるようになったので、入力に応じて何かをするだけです。 とは言っても、いきなりオープンワールドに放り出されても困るとは思いますし、せめて草原フィールドくらいは渡り合えるくらいを目標に、次回からは設計やエラー処理、その他の雑多な事項について取り上げて、Discordボットが長期的な運用に耐えうるような仕組み作りや方法について学んでいきます。 [[第2回 ドメインモデル]]