🫑tech.miyabitti
Markdownmdで開く

無料でDiscordBotを運用できる「Discord Hono」を使ってみた 【初学者向け】

無料でDiscordBotを運用できる「Discord Hono」を使ってみた 【初学者向け】

常時稼働は出来ませんが、Cloudflare WorkersでDiscordBotを運用できるライブラリ「Discord Hono」を紹介します。

目次

#サーバーレスでDiscord Botを常時稼働させるメリット

Discord Botを個人で運用する場合、VPSやクラウドサーバーの常時起動コストが最初の障壁になります。無料枠を利用する方法もありますが、スリープ対策の定期アクセスなどトリッキーな設定が必要になり、維持管理も容易ではありません。

そこで活躍するのが、Cloudflare Workers上で動作する軽量Botライブラリ Discord Hono です。

#従来のサーバー型BotとWebSocketの仕組み

まず、一般的なDiscord Botがどのように動作しているかを簡単に説明します。

従来のBotはWebSocket1と呼ばれる仕組みを使って、Discordのサーバーと常時接続しています。ユーザーがメッセージを送ったり、コマンドを使ったりすると、その情報がリアルタイムでBotに届き、Botが反応します。

この方式だと、Botは24時間常にプログラムを起動し続けるサーバー2が必要です。これがコストや管理の手間がかかる原因です。

#サーバーレスとCloudflare Workers

#サーバーレスとは

サーバーレス3とは、「サーバーを自分で管理しない」仕組みのことです。サーバーが存在しないわけではなく、クラウドサービス側がサーバーを管理してくれます。プログラムは「リクエストが来たときだけ起動」し、処理が終わると停止します。そのため待機中のコストがかからず、スケーリングも自動で行われます。

#Cloudflare Workersとは

Cloudflare Workers4は、Cloudflareが提供するサーバーレス実行環境です。世界中に分散したエッジサーバー5上でコードが実行されるため、ユーザーの近くで高速に動作します。無料プランでは1日10万リクエストまで使用できます。

Discord HonoはこのCloudflare Workersで動作するDiscord Botライブラリです。WebSocketによる常時接続の代わりに、HTTP6を使ってDiscordからのイベントを受け取ります。

#できること・できないこと

サーバーレス型BotであるDiscord Honoと、従来のサーバー型Botではできることに違いがあります。

サーバー型BotDiscord Hono(サーバーレス)
スラッシュコマンドへの返答✅✅
ボタン・モーダル等のインタラクション✅✅
REST API7の利用✅✅
Cron8による定期実行✅✅
VCへの接続(音楽Botなど)✅❌
メッセージ監視・自動返答✅❌
無料でダウンタイムなし❌(対策が必要)✅
大規模スケーリングコストがかかりやすい✅

最大のデメリットは、WebSocketを使った常時接続が必要な機能が使えないことです。具体的には以下ができません。

  • ボイスチャンネルへの接続(音楽Bot、会話Botなど)
  • メッセージの監視・自動返答(特定ワードへの反応など)
  • Botのステータス表示のリアルタイム更新

逆に、スラッシュコマンドへの応答・ボタンやモーダルなどのインタラクション・定期実行が中心のBotであれば、Discord Honoで十分実用的に運用できます。

💡 どんなBotに向いている?
「ユーザーがコマンドを打ったら何かしてくれる」という用途がメインのBotに最適です。情報検索・翻訳・ランダム抽選・ゲームサポートなどが挙げられます。

上記のことがしたい場合は素直にサーバーを借りるのが正解です。

#前提

本記事を進めるに当たって、以下のことが必要です。

  1. Discordのアカウントがあること
  2. Cloudflareのアカウントがあること
  3. bun9がインストールされていること - bunでなくても、node(npm/pnpm) や deno等でも問題ありません。
  4. VSCodeがインストールされていること - エディタであれば何でも良いです。

#事前準備:Developer Portalでの設定

Discord Botを作るには、まずDiscord Developer Portalでアプリケーションを作成し、トークン10などを取得する必要があります。

#1. アプリケーションの作成

  1. Discord Developer Portal にアクセスする
  2. 右上の「New Application」をクリックする
  3. Botの名前を入力して「Create」をクリックする

新しいアプリケーションの作成

#2. 各種キーの取得

アプリケーション作成後、左メニューの「General Information」を開き、以下の2つを控えておきます。

  • APPLICATION ID:BotのアプリケーションID
  • PUBLIC KEY:リクエストの署名検証11に使用するキー

#3. Botトークンの発行

  1. 左メニューの「Bot」タブを開く
  2. 「Reset Token」をクリックしてトークンを発行する
  3. 表示されたトークンをコピーして控えておく

⚠️ 注意
トークンは一度しか表示されません。ページを閉じた後は再確認できないので、必ずコピーして安全な場所に保存してください。

#テスト用Discordサーバーの準備

事前にBotをテストするサーバーを用意してください。そして、そのサーバーのIDをコピーします。

サーバーIDは、ブラウザであればdiscord.com/channels/[server-id]/[channel-id] の形式なので、server-idの部分をコピーしてください。

デスクトップアプリであれば、設定画面から、開発者モードをONにしたうえで、サーバー設定などを確認する場所の一番下にサーバーIDをコピーがあります。

サーバーIDのコピー方法

#プロジェクトのセットアップ

#Cloudflare Workersプロジェクトの作成

Terminal window
bunx create-cloudflare@latest discord-hono-bot
cd discord-hono-bot

いくつか質問されます。以下のように選択してください。

  • What would you like to start with? → Hello World example
  • Which template would you like to use? → Worker only
  • Which language do you want to use? → TypeScript
  • Do you want to use git for version control? → Yes(任意)
  • Do you want to deploy your application? → No(後で手動でデプロイします)

続いて、discord-hono と、DiscordのAPIの型を提供してくれるライブラリをインストールします。

Terminal window
bun add discord-hono
bun add -D discord-api-types

#環境変数の設定

プロジェクトルートに .env ファイルを作成し、Developer Portalで控えた値を設定します。このファイルはローカルでのコマンド登録スクリプト実行時に使用します。

DISCORD_APPLICATION_ID=your_application_id
DISCORD_PUBLIC_KEY=your_public_key
DISCORD_TOKEN=your_bot_token
DISCORD_TEST_GUILD_ID=your_test_server_id

.gitignore に .env が含まれているか確認し、含まれていない場合は追記してください。

#wrangler.jsonc確認・編集

wrangler.jsonc12を開き、以下のようになっているか確認します。

{
"$schema": "node_modules/wrangler/config-schema.json",
"name": "discord-hono-bot",
"main": "src/index.ts",
"compatibility_date": "2026-04-15", // 今日の日付でおk!
"observability": {
"enabled": true
},
"upload_source_maps": true,
"compatibility_flags": [
"nodejs_compat"
]
}

#package.jsonにスクリプトを追加する

package.json の scripts セクションを以下のように編集します。

{
"scripts": {
"dev": "wrangler dev",
"deploy": "wrangler deploy",
"register": "bun --env-file=.env src/register.ts"
}
}

#Botを実装する

#src/index.ts を作成する

src/index.ts がBotのメインハンドラーです。Cloudflare Workersへのリクエストをここで受け取り、コマンドに応じて処理します。まずは /ping に「Pong! 🏓」と返すだけのシンプルな実装から始めます。

src/index.ts
import { DiscordHono } from 'discord-hono'
// Cloudflare Workersの環境変数の型定義
type Env = {
Bindings: {
DISCORD_TOKEN: string
DISCORD_PUBLIC_KEY: string
DISCORD_APPLICATION_ID: string
}
}
const app = new DiscordHono<Env>()
.command('ping', (c) => c.res('Pong! 🏓'))
export default app

c.res() はコマンドへの返答メソッドです。文字列を渡すと、その内容がDiscordにメッセージとして送信されます。

名前の通り、かなりhonoに近い書き方なので、honoを知っていれば学習コストも低いでしょう。

#src/register.ts を作成する

Discordのスラッシュコマンドは、Discord APIに事前に登録しておく必要があります13。src/register.ts として登録用スクリプトを作成します。

import { Command, register } from 'discord-hono'
const commands = [
new Command('ping', 'Pongと返答します'),
]
register(
commands,
process.env.DISCORD_APPLICATION_ID,
process.env.DISCORD_TOKEN,
)

#コマンドの登録とデプロイ

#コマンドをDiscordに登録する

Terminal window
bun run register

コマンドの登録はDiscord APIへのリクエストです。登録自体はすぐ完了しますが、Discordのクライアントに反映されるまで最大1時間かかる場合があります。

#本番環境の環境変数を設定する

Cloudflare Workersに .env ファイルはそのまま使えません。本番用のシークレット14は、wrangler secret put コマンドで設定します。

Terminal window
bunx wrangler secret put DISCORD_APPLICATION_ID
bunx wrangler secret put DISCORD_PUBLIC_KEY
bunx wrangler secret put DISCORD_TOKEN

それぞれのコマンドを実行すると、対話的に値の入力を求められます。.env に記載した対応する値を入力してください。

#Cloudflare Workersへデプロイする

Terminal window
bun run deploy

デプロイ15が完了すると、以下のようなURLが発行されます。

https://discord-hono-bot.your-subdomain.workers.dev

#INTERACTIONS ENDPOINT URLを設定する

Discordはコマンドが実行されると、事前に登録したURL(エンドポイント16)にHTTPリクエストを送信します。このURLをDeveloper Portalに設定します。

  1. Discord Developer Portalの「General Information」タブを開く
  2. 「INTERACTIONS ENDPOINT URL」にデプロイで発行されたWorkerのURLを入力する
  3. 「Save Changes」をクリックする

Discordがエンドポイントの疎通確認を行うため、この時点でWorkerが正常に動いていることが重要です。保存に成功すれば、BotがDiscordからのインタラクションを受け取れる状態になります。

#BotをサーバーへInviteする

  1. Discord Developer Portalの「OAuth2」タブを開く
  2. 「OAuth2 URL Generator」を選択する
  3. SCOPES で bot と applications.commands にチェックを入れる
  4. 生成されたURLをブラウザで開き、招待するサーバーを選択する

招待後、Discordのチャットで /ping と入力すると「Pong! 🏓」と返ってくるはずです 🎉

#実践:ボタンインタラクション付きのダイスコマンド

Discord Honoはボタン等のコンポーネントインタラクション17にも対応しています。ここでは、「もう一度振る」ボタン付きのダイスロールコマンドを実装してみます。ゲームサーバーなどで実用的に活用できるコマンドです。

#register.ts にコマンドを追加する

import { Command, register } from 'discord-hono'
const commands = [
new Command('ping', 'Pongと返答します'),
new Command('dice', 'サイコロを振ります'), // 追加
]
register(
commands,
process.env.DISCORD_APPLICATION_ID,
process.env.DISCORD_TOKEN,
)

#index.ts を更新する

src/index.ts をボタンインタラクションに対応するよう更新します。

import { DiscordHono, Components, Button } from 'discord-hono'
type Env = {
Bindings: {
DISCORD_TOKEN: string
DISCORD_PUBLIC_KEY: string
DISCORD_APPLICATION_ID: string
}
}
// 1〜6のランダムな整数を返す
const rollDice = () => Math.floor(Math.random() * 6) + 1
// ダイス結果のメッセージ(ボタン付き)を生成する関数
const diceResponse = (result: number) => ({
content: `🎲 **${result}** が出ました!`,
components: new Components().row(
new Button('reroll', 'もう一度振る 🎲'),
),
})
const app = new DiscordHono<Env>()
// /ping コマンド
.command('ping', (c) => c.res('Pong! 🏓'))
// /dice コマンド:初回はコマンドとして返答
.command('dice', (c) => c.res(diceResponse(rollDice())))
// 「もう一度振る」ボタンが押されたとき:メッセージを更新
.component('reroll', (c) => c.resUpdate(diceResponse(rollDice())))
export default app

コードの要点は次の2点です。

  1. .component() の第1引数('reroll')を new Button() の第1引数(カスタムID)と一致させ、ボタン押下時のハンドラーを紐付けています
  2. c.resUpdate() を使うことで、ボタンを押した際に元のメッセージを新しい結果で上書きできます。c.res() では新規メッセージとして連投されてしまうため、resUpdate を活用すると結果がその場で切り替わる自然な操作感になります

#再登録してデプロイする

Terminal window
bun run register
bun run deploy

/dice を実行するとサイコロが振られ、「もう一度振る 🎲」ボタンを押すたびに結果が更新されます。

#まとめ:常時接続が不要ならDiscord Hono一択

Discord Honoを採用することで、インフラの常時稼働コストをかけずにDiscord Botを運用できました。WebSocketによる常時接続を必要としないBotであれば、きわめて合理的な選択肢です。

今回実装したボタンに加え、モーダルやセレクトメニュー、Cronトリガーによる定期実行にも対応しています。さらにCloudflareのD1(SQLデータベース)やKV(Key-Valueストア)と組み合わせれば、データを保持する本格的なWebアプリケーションBotへと容易に拡張できます。ぜひ公式ドキュメントを参照しながら、オリジナルの機能を実装してみてください。

間違っている箇所があればXのDMまでお知らせください!即座に修正いたします。

#Footnotes

  1. WebSocketとは、サーバーとクライアントが一度接続を確立したあと、双方向から自由なタイミングでデータを送受信できる通信規格です。一般的なHTTP通信が一往復のやり取りで完結するのに対し、WebSocketは常時接続を維持します。チャットツールやオンライン対戦ゲームなどで活用されています。 ↩

  2. ここでのサーバーとは、プログラムを常時起動し続けるコンピューター環境を指します。VPSやクラウドVM(AWS EC2など)が代表的です。無料枠のあるホスティングでも、スリープ制限や稼働時間の上限が設けられているケースが目立ちます。 ↩

  3. サーバーレスとは、開発者がOSやサーバーの調達・保守を意識せず、コード実行のみに集中できるクラウドの運用形態です。リクエストの到着時にのみ関数が起動するため、アイドル時のコストが発生しません。 ↩

  4. Cloudflare Workersは、Cloudflareが提供するエッジサーバーレス環境です。無料プランでも1日10万リクエストまで利用でき、個人開発のBot運用であれば無料枠内で十分に賄えます。 ↩

  5. エッジサーバーとは、利用者の地理的な近傍に配置されたサーバー群です。遠隔地のデータセンターへ通信するよりも遅延が大幅に短縮されます。 ↩

  6. HTTP(HyperText Transfer Protocol) は、Webブラウザとサーバー間で通信を行うための標準規約です。クライアントのリクエストに対してサーバーがレスポンスを返す一問一答型で動作します。 ↩

  7. REST APIとは、HTTPプロトコルに則って設計されたWeb APIの形式です。Discord APIもREST形式で公開されており、HTTPリクエストを通じてメッセージ送信やアカウント情報の取得が行えます。 ↩

  8. Cronとは、決められた日時や周期で処理を定期実行するスケジューラーです。「毎朝定時に情報を配信するBot」などの用途に適しています。 ↩

  9. Bunは、Node.jsと互換性を持つ高速なJavaScript/TypeScriptランタイムです。TypeScriptをトランスパイル不要で直接実行できるほか、パッケージ管理も高速に行えます。 ↩

  10. トークンとは、Botのアカウント認証に用いる秘密鍵です。流出すると不正操作の恐れがあるため、ソースコードに直書きせず厳重に秘匿管理します。 ↩

  11. 署名検証とは、届いたHTTPリクエストが正規のDiscordサーバーから送信されたものかを検証する暗号処理です。Discord Honoが内部で自動的に処理するため、利用側で複雑な暗号コードを書く必要はありません。 ↩

  12. wrangler.jsoncは、Cloudflare Workersの設定ファイルです。Workers名やエントリーポイントのパスをJSON形式(コメント付き)で指定します。 ↩

  13. スラッシュコマンドは、Discord APIに事前登録することで、ユーザーが「/」を入力した際の補完一覧に現れます。コード側のハンドラーを記述しただけでは反映されないため、本登録スクリプトの実行が必要です。 ↩

  14. シークレットとは、Cloudflare Workersの本番環境で利用する暗号化された環境変数です。コード上には現れず、安全に機密情報を保護します。 ↩

  15. デプロイとは、作成したプログラム資産を実行環境(ここではCloudflare Workers)へ転送し、サービスとして利用可能な状態に配置することです。 ↩

  16. エンドポイントとは、APIやWebサービスの通信先URLです。ここではDiscord側からBotへ通知イベントを送信する宛先URLを指します。 ↩

  17. コンポーネントインタラクションとは、ボタンやセレクトメニューなど、メッセージ内のUIパーツをユーザーが操作した際に生じるイベントです。画面の部分書き換えや後続アクションのトリガーとして機能します。 ↩