Contents
Discord アプリと Bot の作成手順
- Discord Developer Portal(https://discord.com/developers/applications)にサインインし、「New Application」 をクリック。
- アプリ名を入力して Create。左メニューの Bot → Add Bot → Yes, do it! で Bot ユーザーが生成されます。
- 表示された Token と Client ID は、後ほどコードやデプロイ時に環境変数として使用します。トークンは一度しか表示されないので必ず安全な場所に保存してください。
重要:Bot の Token は「機密情報」です。漏洩すると第三者が Bot を乗っ取る危険があります。
インテントの有効化
- Message Content Intent と Server Members Intent を必ずオンにし、Save Changes を忘れないでください。
- これらは「Privileged Gateway Intents」セクションにあります。インテントが無効だと
message.contentが空になるなど、期待したデータが取得できません。
参考:Discord 開発者向け公式ドキュメント(https://discord.com/developers/docs/topics/gateway#gateway-intents)
OpenAI API キー取得とモデル選択のポイント
取得手順
- https://platform.openai.com/account/api-keys にアクセスし Create new secret key をクリック。
- 表示されたキーをコピーし、ローカルの
.envファイルにOPENAI_API_KEY=YOUR_KEYと記載します。
2026 年時点で注目されるモデル(※予測情報)
| モデル | 主な特徴 | 参考料金* |
|---|---|---|
| GPT‑4o | 音声・画像入力対応、リアルタイム応答に最適化 | 約 $0.005 / 1k トークン |
| GPT‑4 Turbo | 高速・低コスト、最大 32k トークンの長文コンテキスト | 約 $0.003 / 1k トークン |
| GPT‑3.5 Turbo | コスト最優先、短い対話向き | 約 $0.0015 / 1k トークン |
*料金は公式プライシングページ(https://openai.com/pricing) の情報を元にした概算です。実際の価格は利用状況・地域によって変動しますので、必ず最新のプラン表をご確認ください。
実装上のヒント
- 環境変数
OPENAI_MODELでモデル名を切り替えられるようにしておくと、将来のアップデートが楽になります。
|
1 2 |
OPENAI_MODEL=gpt-4o # デフォルトは gpt‑4o(必要に応じて変更可) |
ローカル環境構築とサンプルコード
1. Node.js (v20 以上) 環境
|
1 2 3 4 5 6 7 8 9 |
# macOS / Linux (Homebrew) brew install node@20 # Windows (Chocolatey) choco install nodejs --version=20 node -v # >= v20.x npm -v |
必要パッケージ
|
1 2 |
npm i discord.js@14 dotenv node-fetch |
2. Python (3.11) 環境
|
1 2 3 4 5 6 |
# macOS / Linux (Homebrew) brew install python@3.11 python --version # 3.11.x pip install py-cord python-dotenv httpx aiosqlite |
サンプルコード(Node.js)
ポイント
-.envの読み込みはrequire('dotenv').config()で行う。
- エラーハンドリングとレートリミット対策を組み込んでいる。
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 |
// src/index.js require('dotenv').config(); const { Client, GatewayIntentBits } = require('discord.js'); const fetch = (...args) => import('node-fetch') .then(({ default: fetch }) => fetch(...args)); const client = new Client({ intents: [ GatewayIntentBits.Guilds, GatewayIntentBits.MessageContent, GatewayIntentBits.GuildMessages ] }); client.once('ready', () => console.log(`🤖 Logged in as ${client.user.tag}`)); /* ---------- レートリミット対策用ラッパー ---------- */ let lastReply = 0; async function safeReply(channel, content) { const now = Date.now(); if (now - lastReply < 200) await new Promise(r => setTimeout(r, 200 - (now - lastReply))); await channel.send(content); lastReply = Date.now(); } /* ---------- メッセージハンドラ ---------- */ client.on('messageCreate', async msg => { if (msg.author.bot) return; if (!msg.content.startsWith('!ai')) return; const prompt = msg.content.replace(/^!ai\s*/, ''); try { const res = await fetch('https://api.openai.com/v1/chat/completions', { method: 'POST', headers: { Authorization: `Bearer ${process.env.OPENAI_API_KEY}`, 'Content-Type': 'application/json' }, body: JSON.stringify({ model: process.env.OPENAI_MODEL || 'gpt-4o', messages: [{ role: 'user', content: prompt }], max_tokens: 500, temperature: 0.6 }) }); const data = await res.json(); const reply = data?.choices?.[0]?.message?.content ?? '⚠️ 応答が取得できませんでした。'; await safeReply(msg.channel, reply); } catch (e) { console.error(e); await safeReply(msg.channel, '❗ エラーが発生しました。コンソールを確認してください。'); } }); client.login(process.env.BOT_TOKEN); |
サンプルコード(Python)
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 |
# main.py import os import discord from discord.ext import commands import httpx from dotenv import load_dotenv load_dotenv() intents = discord.Intents.default() intents.message_content = True # 必須インテント bot = commands.Bot(command_prefix='!', intents=intents) @bot.event async def on_ready(): print(f'🤖 Logged in as {bot.user}') @bot.command(name='ai') async def ai(ctx, *, prompt: str): headers = { "Authorization": f"Bearer {os.getenv('OPENAI_API_KEY')}", "Content-Type": "application/json" } payload = { "model": os.getenv('OPENAI_MODEL', 'gpt-4o'), "messages": [{"role": "user", "content": prompt}], "max_tokens": 500, "temperature": 0.6 } async with httpx.AsyncClient() as client: resp = await client.post("https://api.openai.com/v1/chat/completions", headers=headers, json=payload) data = resp.json() reply = data.get('choices', [{}])[0].get('message', {}).get('content', '⚠️ 応答が取得できませんでした。') await ctx.reply(reply) bot.run(os.getenv('BOT_TOKEN')) |
共通 .env 例
|
1 2 3 4 5 |
# .env(ローカル専用、必ず .gitignore に追加) BOT_TOKEN=YOUR_DISCORD_BOT_TOKEN OPENAI_API_KEY=YOUR_OPENAI_API_KEY OPENAI_MODEL=gpt-4o # 任意で変更可 |
会話履歴の管理例
1. インメモリキャッシュ(Node.js)
|
1 2 3 4 5 6 7 8 9 |
const cache = new Map(); // key: channelId, value: [{role, content}, ...] function addToCache(channelId, role, content) { const hist = cache.get(channelId) || []; if (hist.length >= 10) hist.shift(); // 最新 10 件だけ保持 hist.push({ role, content }); cache.set(channelId, hist); } |
2. 永続化(SQLite、Python)
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 |
import aiosqlite DB_PATH = 'conversations.db' async def init_db(): async with aiosqlite.connect(DB_PATH) as db: await db.execute(''' CREATE TABLE IF NOT EXISTS history ( channel_id TEXT, role TEXT, content TEXT, ts INTEGER ) ''') await db.commit() async def save_message(channel_id, role, content): async with aiosqlite.connect(DB_PATH) as db: await db.execute( "INSERT INTO history (channel_id, role, content, ts) VALUES (?, ?, ?, strftime('%s','now'))", (channel_id, role, content) ) await db.commit() |
実装上のベストプラクティス
- 「最新 20 件まで」「1 日以上前は自動削除」など、保持件数・保存期間をコードで明示するとメモリ使用量とコストが抑えられます。
- 会話履歴の総トークン数が 2k を超える場合は古いメッセージから順に削除し、OpenAI のコンテキスト上限を超えないようにします。
Bot の招待・テスト手順
- OAuth2 → URL Generator
- Scopes:
bot,applications.commands - Bot Permissions:
Send Messages,Read Message History,Add Reactions(必要に応じて) -
生成された URL をブラウザで開き、テストサーバーへ Bot を招待します。
-
ローカルでコードを起動
bash
# Node
npm run start # package.json の scripts に "start": "node src/index.js" を設定
# Python
python main.py
- Discord 上でテストコマンドを実行
!ai 今日の東京の天気は?
返答が表示されれば成功です。
ヒント:コンソールに
Gateway関連のログが出ない場合、インテントが無効化されている可能性があります。ポータルで再度確認してください。
Docker 化とホスティング比較
Dockerfile(Node.js)
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 |
# ビルドステージ FROM node:20-alpine AS builder WORKDIR /app COPY package*.json ./ RUN npm ci --omit=dev COPY . . # 実行ステージ FROM node:20-alpine WORKDIR /app COPY --from=builder /app . ENV NODE_ENV=production CMD ["node", "src/index.js"] |
Dockerfile(Python)
|
1 2 3 4 5 6 7 8 9 10 11 |
FROM python:3.11-slim AS builder WORKDIR /app COPY requirements.txt . RUN pip install --no-cache-dir -r requirements.txt FROM python:3.11-slim WORKDIR /app COPY --from=builder /usr/local/lib/python3.11/site-packages /usr/local/lib/python3.11/site-packages COPY . . CMD ["python", "main.py"] |
主要ホスティングサービス比較
| 項目 | Render | Railway | Fly.io |
|---|---|---|---|
| 無料枠 | 750 h/月(Docker) | $5 クレジット相当/月 | 3 GB RAM + 160 GB データ転送/月 |
| デプロイ方法 | GitHub 連携 → 自動ビルド | CLI railway up または GitHub Actions |
flyctl deploy |
| リージョン数 | US, EU, APAC(数拠点) | US / Europe(2 拠点) | 30+ ロケーション(エッジデプロイ可) |
| 水平スケール | 手動・自動 | プラン単位で水平 | 秒単位の CPU/RAM 調整が可能 |
| ログ/モニタ | ビルトイン UI | Datadog 連携(有料) | Dashboard + Metrics API |
選び方サマリー
- 低コスト・シンプル → Render の無料枠が最も手軽。
- CI/CD 重視 → Railway はプレビュー環境生成が速く、GitHub Actions との相性抜群。
- グローバルレイテンシ削減 → ユーザー分散が大きい場合は Fly.io のエッジ配置が有利。
.env ファイルとシークレット管理のベストプラクティス
| 項目 | 推奨対策 |
|---|---|
| .gitignore | プロジェクトルートに必ず /.env を追加し、リポジトリへのコミットを防止。 |
| ファイル権限 | Linux/macOS では chmod 600 .env、Windows でも「読み取り専用」かつ暗号化されたフォルダに格納。 |
| Git の履歴削除 | 誤ってコミットした場合は git filter-branch --force --index-filter 'git rm --cached --ignore-unmatch .env' --prune-empty --tag-name-filter cat -- --all で履歴から除去し、GitHub にも Force‑push。 |
| CI/CD のシークレット | GitHub Actions → Settings > Secrets and variables > Actions、Render/Railway/Fly.io の「Secret Store」に同様に登録し、.env を生成せず環境変数だけで動作させる。 |
| ローカルと本番の分離 | 本番用は ENV=production などフラグを立て、コード内で条件分岐してデバッグ情報や余計なログ出力を抑制する。 |
GitHub Actions の例(Docker ビルド+シークレット注入)
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 |
name: Deploy to Render on: push: branches: [ main ] jobs: build-deploy: runs-on: ubuntu-latest steps: - uses: actions/checkout@v3 - name: Set up Node uses: actions/setup-node@v3 with: node-version: '20' - name: Build Docker image env: BOT_TOKEN: ${{ secrets.BOT_TOKEN }} OPENAI_API_KEY: ${{ secrets.OPENAI_API_KEY }} run: | echo "BOT_TOKEN=${BOT_TOKEN}" > .env echo "OPENAI_API_KEY=${OPENAI_API_KEY}" >> .env docker build -t ghcr.io/${{ github.repository }}:${{ github.sha }} . - name: Push image to GitHub Container Registry run: | echo "${{ secrets.GITHUB_TOKEN }}" | docker login ghcr.io -u ${{ github.actor }} --password-stdin docker push ghcr.io/${{ github.repository }}:${{ github.sha }} |
注意:上記例はあくまで雛形です。実際の運用では
.envファイル自体をリポジトリに残さず、プラットフォーム側で直接シークレットとして設定することが安全です。
トラブルシューティング・FAQ
| 質問 | 回答 |
|---|---|
| Bot が起動しない | 1. .env が正しく読み込めているか console.log(process.env.BOT_TOKEN) で確認2. Node のバージョンが v20 以上か、Python が 3.11 かチェック |
| メッセージが送信されない(429 エラー) | Discord のレートリミットは 1 秒あたり最大 5 件程度です。setTimeout や先述の safeReply ラッパーで間隔を調整してください |
| OpenAI から「Invalid request」エラーが返る | - モデル名に誤字がないか - max_tokens がモデル上限(例:GPT‑4o は 8k)を超えていないか確認 |
| 環境変数が本番で反映されない | デプロイ先の「Secrets」設定が正しいか、Dockerfile の ENV 行と実行時コマンドで上書きしていないか点検 |
| 会話履歴が増えすぎてトークン制限に達する | 履歴を 2k トークン以内に切り詰め、古いメッセージは削除するロジック(例:history = history.slice(-10))を実装 |
| .env が外部に漏れたかも | 直ちにトークンを再生成し、GitHub の Security > Secret scanning で検出された場合はリポジトリから機密情報を削除し、履歴を書き換えて Force‑push |
最後に
- 本ガイドは 2026 年 4 月時点の情報 に基づいていますが、Discord・OpenAI の仕様は頻繁に更新されます。公式ドキュメント(Discord Developer Docs、OpenAI API Reference)を必ず定期的に確認してください。
- セキュリティは 「機密情報の管理」+「レートリミットとコスト意識」 が鍵です。安全な運用を心がけて楽しい Discord Bot 開発を! 🚀