Discord
Configure the Discord adapter with HTTP Interactions and Gateway WebSocket support.
Installation
pnpm add @chat-adapter/discordUsage
The adapter auto-detects DISCORD_BOT_TOKEN, DISCORD_PUBLIC_KEY, DISCORD_APPLICATION_ID, and DISCORD_MENTION_ROLE_IDS from environment variables:
import { Chat } from "chat";
import { createDiscordAdapter } from "@chat-adapter/discord";
const bot = new Chat({
userName: "mybot",
adapters: {
discord: createDiscordAdapter(),
},
});
bot.onNewMention(async (thread, message) => {
await thread.post("Hello from Discord!");
});Discord application setup
1. Create application
- Go to the Discord Developer Portal
- Click New Application and give it a name
- Note the Application ID from the General Information page
- Copy the Public Key from the General Information page
2. Create bot
- Go to the Bot section in the left sidebar
- Click Reset Token to generate a new bot token
- Copy and save the token (you won't see it again)
- Enable these Privileged Gateway Intents:
- Message Content Intent
- Server Members Intent (if needed)
3. Configure interactions endpoint
- Go to General Information
- Set Interactions Endpoint URL to
https://your-domain.com/api/webhooks/discord - Discord sends a PING to verify the endpoint
4. Add bot to server
- Go to OAuth2 then URL Generator
- Select scopes:
bot,applications.commands - Select bot permissions: Send Messages, Send Messages in Threads, Create Public Threads, Read Message History, Add Reactions, Attach Files
- Copy the generated URL and open it to invite the bot to your server
Architecture: HTTP Interactions vs Gateway
Discord has two ways to receive events:
HTTP Interactions (default):
- Receives button clicks, slash commands, and verification pings
- Works out of the box with serverless
- Does not receive regular messages
Gateway WebSocket (required for messages):
- Receives regular messages and reactions
- Requires a persistent connection
- In serverless environments, use a cron job to maintain the connection
Gateway setup for serverless
1. Create Gateway route
import { after } from "next/server";
import { bot } from "@/lib/bot";
export const maxDuration = 800;
export async function GET(request: Request): Promise<Response> {
const cronSecret = process.env.CRON_SECRET;
if (!cronSecret) {
return new Response("CRON_SECRET not configured", { status: 500 });
}
const authHeader = request.headers.get("authorization");
if (authHeader !== `Bearer ${cronSecret}`) {
return new Response("Unauthorized", { status: 401 });
}
const durationMs = 600 * 1000;
const webhookUrl = `https://${process.env.VERCEL_URL}/api/webhooks/discord`;
return bot.adapters.discord.startGatewayListener(
{ waitUntil: (task) => after(() => task) },
durationMs,
undefined,
webhookUrl
);
}2. Configure Vercel Cron
{
"crons": [
{
"path": "/api/discord/gateway",
"schedule": "*/9 * * * *"
}
]
}This runs every 9 minutes, ensuring overlap with the 10-minute listener duration.
3. Add environment variables
Add CRON_SECRET to your Vercel project settings.
Role mentions
By default, only direct user mentions (@BotName) trigger onNewMention handlers. To also trigger on role mentions (e.g., @AI):
- Create a role in your Discord server (e.g., "AI")
- Assign the role to your bot
- Copy the role ID (right-click role in server settings with Developer Mode enabled)
- Add to
mentionRoleIds:
createDiscordAdapter({
mentionRoleIds: ["1457473602180878604"],
});Or set DISCORD_MENTION_ROLE_IDS as a comma-separated string in your environment variables.
Configuration
All options are auto-detected from environment variables when not provided.
| Option | Required | Description |
|---|---|---|
botToken | No* | Discord bot token. Auto-detected from DISCORD_BOT_TOKEN |
publicKey | No* | Application public key. Auto-detected from DISCORD_PUBLIC_KEY |
applicationId | No* | Discord application ID. Auto-detected from DISCORD_APPLICATION_ID |
mentionRoleIds | No | Array of role IDs that trigger mention handlers. Auto-detected from DISCORD_MENTION_ROLE_IDS (comma-separated) |
logger | No | Logger instance (defaults to ConsoleLogger("info")) |
*botToken, publicKey, and applicationId are required — either via config or env vars.
Environment variables
DISCORD_BOT_TOKEN=your-bot-token
DISCORD_PUBLIC_KEY=your-application-public-key
DISCORD_APPLICATION_ID=your-application-id
DISCORD_MENTION_ROLE_IDS=1234567890,0987654321 # Optional
CRON_SECRET=your-random-secret # For Gateway cronFeatures
| Feature | Supported |
|---|---|
| Mentions | Yes |
| Reactions (add/remove) | Yes |
| Cards (Embeds) | Yes |
| Modals | No |
| Streaming | Post+Edit fallback |
| DMs | Yes |
| Ephemeral messages | No (DM fallback) |
| File uploads | Yes |
| Typing indicator | Yes |
| Message history | Yes |
Testing
Run a local tunnel (e.g., ngrok) to test webhooks locally:
ngrok http 3000Update the Interactions Endpoint URL in the Discord Developer Portal to your ngrok URL.
Troubleshooting
Bot not responding to messages
- Check Gateway connection: Messages require the Gateway WebSocket, not just HTTP interactions
- Verify Message Content Intent: Enable this in the Bot settings
- Check bot permissions: Ensure the bot can read messages in the channel
Role mentions not triggering
- Verify role ID: Enable Developer Mode in Discord settings, then right-click the role
- Check
mentionRoleIdsconfig: Ensure the role ID is in the array - Confirm bot has the role: The bot must have the role assigned
Signature verification failing
- Check public key format: Should be a 64-character hex string (lowercase)
- Verify endpoint URL: Must exactly match what's configured in Discord Developer Portal
- Check for body parsing: Don't parse the request body before verification