?

Overview

The Discord transport (internal/transport/discord/) speaks the Discord Gateway v10 protocol directly — no third-party SDK. WebSocket for inbound (Identify → Ready → Heartbeat/Ack → Dispatch(MESSAGE_CREATE)); REST for outbound (POST /channels/{id}/messages).

Prerequisites

  1. A Discord Application with a Bot user. Create at https://discord.com/developers/applicationsNew ApplicationBot tab → Add Bot.
  2. A bot token (Bot tab → Reset Token → copy the token — you only see it once).
  3. Message Content intent enabled (Bot tab → Privileged Gateway Intents). Without this, the Gateway strips message text from every event and rousseau will see empty bodies.
  4. The bot invited to at least one server (or DMs enabled). Generate the invite URL under OAuth2 → URL Generator with the bot scope and Send Messages + Read Message History permissions.

Configuration

discord:
  token: "Bot MTIz..."
  reply_header: ""
  allowlist:
    - "123456789012345678"
Field Default Effect
token required Bot token from the Developer Portal.
reply_header empty Prepended to every outbound reply.
allowlist [] Discord user IDs whose messages are handled.

Command-line

rousseau discord --token 'MTIz...' --allow 123456789012345678

Gateway intents

rousseau requests three intents (internal/transport/discord/client.go):

Intent Bit Purpose
GUILD_MESSAGES 1 << 9 Messages in server channels.
DIRECT_MESSAGES 1 << 12 DMs to the bot.
MESSAGE_CONTENT 1 << 15 Populates the content field. Must be enabled in the portal.

Without the Message Content intent, MESSAGE_CREATE events arrive with empty content and rousseau will log discord.empty_body.

Heartbeat

The transport honours the Gateway's heartbeat_interval from the Hello opcode, sending Heartbeat + tracking heartbeat_ack. Missed acks close the socket and let systemd restart the process.

Reply header

Discord renders **text** as bold and does not require any specific header shape. Override as needed:

discord:
  reply_header: "**Rousseau Agent**\n"

Permissions bit calculator

Discord uses a bitmask to encode a bot's channel permissions. Each permission is a power of 2. Common ones for rousseau:

Permission Bit
Read Messages / View Channels 1 << 10 = 1024
Send Messages 1 << 11 = 2048
Send Messages in Threads 1 << 38 = 274877906944
Read Message History 1 << 16 = 65536
Add Reactions 1 << 6 = 64

To grant multiple permissions, OR the bits together and paste the resulting integer into the OAuth2 URL Generator's permissions= parameter:

Read Messages (1024) OR Send Messages (2048) OR Read Message History (65536) = 68608

Gateway lifecycle

The Gateway is stateful:

Client                        Discord Gateway
  │
  │   ────  Connect  ────▶
  │   ◀── HELLO (heartbeat_interval)
  │
  │   ───── IDENTIFY (token, intents) ────▶
  │   ◀── READY (session_id, user)
  │
  │   ─── Heartbeat every N ms ─▶
  │   ◀── HEARTBEAT_ACK
  │
  │   ◀── MESSAGE_CREATE (a user typed)
  │   ─── (rousseau handles + POSTs reply)
  │
  │   ◀── Disconnect (code 4009: session timed out)
  │   ─── RESUME (session_id) or re-IDENTIFY

The client tracks heartbeat_ack. If an ack is missed, the socket closes and the process exits — systemd or the container runtime restarts.

Failure modes

Symptom Fix
Bot sees empty messages Enable Message Content intent in the developer portal.
Gateway closes with code 4004 Invalid token. Regenerate.
Bot cannot see any channels Confirm the OAuth2 invite included the bot scope.
403 on send Bot lacks Send Messages permission in that channel.
Code 4014 on Identify Requested an intent your app is not approved for (usually Message Content on a 100+ server bot). Verify your bot.
Code 4009 (session timed out) Normal after long idle. Rousseau reconnects transparently.

Troubleshooting

Gateway 4013 (Invalid Intents)

You are requesting an intent bit that does not exist. This usually means a mismatch between the client library's intent constants and Discord's current intent map. Rousseau builds the intent bitmask in internal/transport/discord/client.go; upgrade to the latest release if you see 4013 after a Discord API change.

Bot receives events but does not respond

Allowlist mismatch. The --allow value must be the numeric Discord user ID (not username, not display name). Retrieve it in Discord: enable Developer Mode in User Settings > Advanced, then right-click a user > Copy User ID.

DMs work but guild channels do not

Missing GUILD_MESSAGES intent, or the bot has not been invited to the guild. Guild permissions are separate from DM permissions — the bot must have the Read Messages permission for the channel.

429 Too Many Requests on outbound

Discord enforces a global 50 req/s rate limit per bot, plus per-channel limits. Under sustained load, rousseau does not currently retry — the caller must back off. See Guides: Rate limits.

Bot online status flaps

Discord considers a bot offline after ~40s without a heartbeat. Log line discord.heartbeat_missed indicates a network problem or CPU-starved daemon. Verify the container has enough CPU allocated.

Related pages

Further reading

Type to search 150+ pages. Ranking: BM25 with title/description boost.