Skip to content

Commit fa6ca2f

Browse files
committed
feat(trogon-source-discord): add gateway mode and autocomplete request-reply
Signed-off-by: Yordis Prieto <yordis.prieto@gmail.com>
1 parent 2c80435 commit fa6ca2f

File tree

16 files changed

+2907
-1
lines changed

16 files changed

+2907
-1
lines changed

devops/docker/compose/.env.example

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,19 @@
2828
# LINEAR_WEBHOOK_TIMESTAMP_TOLERANCE_SECS=60
2929
# LINEAR_NATS_ACK_TIMEOUT_MS=10000
3030

31+
# --- Discord Source ---
32+
# DISCORD_MODE=gateway # "gateway" (WebSocket, all events) or "webhook" (HTTP, interactions only)
33+
# DISCORD_BOT_TOKEN= # required when DISCORD_MODE=gateway
34+
# DISCORD_GATEWAY_INTENTS=guilds,guild_members,guild_messages,guild_message_reactions,direct_messages,message_content,guild_voice_states
35+
# DISCORD_PUBLIC_KEY= # required when DISCORD_MODE=webhook
36+
# DISCORD_WEBHOOK_PORT=8080
37+
# DISCORD_SUBJECT_PREFIX=discord
38+
# DISCORD_STREAM_NAME=DISCORD
39+
# DISCORD_STREAM_MAX_AGE_SECS=604800
40+
# DISCORD_NATS_ACK_TIMEOUT_SECS=10
41+
# DISCORD_NATS_REQUEST_TIMEOUT_SECS=2
42+
# DISCORD_MAX_BODY_SIZE=4194304
43+
3144
# --- Telegram Source ---
3245
# TELEGRAM_WEBHOOK_SECRET=
3346
# TELEGRAM_SOURCE_PORT=8080

devops/docker/compose/compose.yml

Lines changed: 38 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -67,6 +67,37 @@ services:
6767
start_period: 10s
6868
retries: 3
6969

70+
trogon-source-discord:
71+
build:
72+
context: ../../../rsworkspace
73+
dockerfile: ../devops/docker/compose/services/trogon-source-discord/Dockerfile
74+
environment:
75+
NATS_URL: "nats:4222"
76+
DISCORD_MODE: "${DISCORD_MODE:-gateway}"
77+
DISCORD_BOT_TOKEN: "${DISCORD_BOT_TOKEN:-}"
78+
DISCORD_GATEWAY_INTENTS: "${DISCORD_GATEWAY_INTENTS:-}"
79+
DISCORD_PUBLIC_KEY: "${DISCORD_PUBLIC_KEY:-}"
80+
DISCORD_WEBHOOK_PORT: "${DISCORD_WEBHOOK_PORT:-8080}"
81+
DISCORD_SUBJECT_PREFIX: "${DISCORD_SUBJECT_PREFIX:-discord}"
82+
DISCORD_STREAM_NAME: "${DISCORD_STREAM_NAME:-DISCORD}"
83+
DISCORD_STREAM_MAX_AGE_SECS: "${DISCORD_STREAM_MAX_AGE_SECS:-604800}"
84+
DISCORD_NATS_ACK_TIMEOUT_SECS: "${DISCORD_NATS_ACK_TIMEOUT_SECS:-10}"
85+
DISCORD_NATS_REQUEST_TIMEOUT_SECS: "${DISCORD_NATS_REQUEST_TIMEOUT_SECS:-2}"
86+
DISCORD_MAX_BODY_SIZE: "${DISCORD_MAX_BODY_SIZE:-4194304}"
87+
RUST_LOG: "${RUST_LOG:-info}"
88+
depends_on:
89+
nats:
90+
condition: service_healthy
91+
restart: unless-stopped
92+
healthcheck:
93+
test: ["CMD", "curl", "-sf", "http://localhost:${DISCORD_WEBHOOK_PORT:-8080}/health"]
94+
interval: 10s
95+
timeout: 3s
96+
start_period: 10s
97+
retries: 3
98+
profiles:
99+
- discord
100+
70101
trogon-source-gitlab:
71102
build:
72103
context: ../../../rsworkspace
@@ -147,6 +178,7 @@ services:
147178
image: ngrok/ngrok:alpine
148179
environment:
149180
NGROK_AUTHTOKEN: "${NGROK_AUTHTOKEN:-}"
181+
DISCORD_ADDR: "trogon-source-discord:${DISCORD_WEBHOOK_PORT:-8080}"
150182
GITHUB_ADDR: "trogon-source-github:${GITHUB_WEBHOOK_PORT:-8080}"
151183
GITLAB_ADDR: "trogon-source-gitlab:${GITLAB_WEBHOOK_PORT:-8080}"
152184
LINEAR_ADDR: "trogon-source-linear:${LINEAR_WEBHOOK_PORT:-8080}"
@@ -159,6 +191,9 @@ services:
159191
cat > /tmp/ngrok.yml <<EOF
160192
version: 3
161193
tunnels:
194+
discord:
195+
addr: $${DISCORD_ADDR}
196+
proto: http
162197
github:
163198
addr: $${GITHUB_ADDR}
164199
proto: http
@@ -177,6 +212,9 @@ services:
177212
EOF
178213
exec ngrok start --all --config /tmp/ngrok.yml
179214
depends_on:
215+
trogon-source-discord:
216+
condition: service_healthy
217+
required: false
180218
trogon-source-github:
181219
condition: service_healthy
182220
trogon-source-gitlab:
Lines changed: 45 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,45 @@
1+
# ── Stage 1: chef — generate dependency recipe ──────────────────────────────
2+
FROM rust:1.93-slim AS chef
3+
4+
RUN cargo install cargo-chef --locked
5+
6+
WORKDIR /build
7+
8+
# ── Stage 2: planner — capture dependency graph ─────────────────────────────
9+
FROM chef AS planner
10+
11+
COPY Cargo.toml Cargo.lock ./
12+
COPY crates/ crates/
13+
14+
RUN cargo chef prepare --recipe-path recipe.json
15+
16+
# ── Stage 3: builder — cached dependency build + final compile ──────────────
17+
FROM chef AS builder
18+
19+
COPY --from=planner /build/recipe.json recipe.json
20+
RUN cargo chef cook --release --recipe-path recipe.json -p trogon-source-discord
21+
22+
COPY Cargo.toml Cargo.lock ./
23+
COPY crates/ crates/
24+
25+
RUN cargo build --release -p trogon-source-discord && \
26+
strip target/release/trogon-source-discord
27+
28+
# ── Stage 4: runtime ────────────────────────────────────────────────────────
29+
FROM debian:bookworm-20250317-slim AS runtime
30+
31+
RUN apt-get update && apt-get install -y --no-install-recommends \
32+
ca-certificates curl \
33+
&& rm -rf /var/lib/apt/lists/*
34+
35+
RUN useradd --no-create-home --shell /usr/sbin/nologin trogon
36+
37+
COPY --from=builder /build/target/release/trogon-source-discord /usr/local/bin/trogon-source-discord
38+
39+
USER trogon
40+
41+
EXPOSE 8080
42+
43+
STOPSIGNAL SIGTERM
44+
45+
ENTRYPOINT ["/usr/local/bin/trogon-source-discord"]
Lines changed: 92 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,92 @@
1+
# Receiving Discord Events Locally
2+
3+
`trogon-source-discord` is the inbound pipe for Discord events into NATS
4+
JetStream. It runs in one of two mutually exclusive modes, controlled by
5+
`DISCORD_MODE`:
6+
7+
| Mode | Transport | Events | Requires |
8+
|---|---|---|---|
9+
| `gateway` | WebSocket (you connect to Discord) | Everything — messages, reactions, members, voice, interactions, etc. | `DISCORD_BOT_TOKEN` |
10+
| `webhook` | HTTP POST (Discord connects to you) | Interactions only — slash commands, buttons, modals, autocomplete | `DISCORD_PUBLIC_KEY` |
11+
12+
## Prerequisites
13+
14+
- Docker Compose
15+
- A [Discord Application](https://discord.com/developers/applications)
16+
- An [ngrok](https://ngrok.com) account (free tier, only needed for `webhook` mode)
17+
18+
## Gateway mode
19+
20+
### 1. Get your bot token
21+
22+
1. Go to **Discord Developer Portal → Applications → your app → Bot**
23+
2. Copy the **Token**
24+
25+
### 2. Start the stack
26+
27+
```bash
28+
DISCORD_MODE=gateway \
29+
DISCORD_BOT_TOKEN=<your-bot-token> \
30+
docker compose up
31+
```
32+
33+
This connects to the Discord Gateway and publishes every event to NATS on
34+
`discord.{event_name}` subjects (e.g. `discord.message_create`,
35+
`discord.guild_member_add`).
36+
37+
## Webhook mode
38+
39+
### 1. Get your public key
40+
41+
1. Go to **Discord Developer Portal → Applications → your app → General Information**
42+
2. Copy the **Public Key** (hex string)
43+
44+
### 2. Start the stack
45+
46+
```bash
47+
DISCORD_MODE=webhook \
48+
DISCORD_PUBLIC_KEY=<your-hex-public-key> \
49+
docker compose --profile dev up
50+
```
51+
52+
This starts NATS, the webhook receiver, and ngrok. Find the public tunnel URL
53+
in the ngrok container logs:
54+
55+
```bash
56+
docker compose logs ngrok
57+
```
58+
59+
### 3. Configure your Discord Application
60+
61+
1. Go to **Discord Developer Portal → Applications → your app → General Information**
62+
2. Set **Interactions Endpoint URL** to `https://<ngrok-url>/webhook`
63+
3. Discord will send a PING to verify the endpoint — the receiver handles this
64+
automatically
65+
66+
## Verify
67+
68+
Subscribe to NATS to see events flowing:
69+
70+
```bash
71+
nats sub -s nats://nats.trogonai.orb.local:4222 "discord.>"
72+
```
73+
74+
Without `--profile dev`, ngrok is excluded and only the core services start.
75+
76+
## Environment variables
77+
78+
| Variable | Required | Default | Description |
79+
|---|---|---|---|
80+
| `DISCORD_MODE` | yes || `gateway` or `webhook` |
81+
| `DISCORD_BOT_TOKEN` | gateway mode || Bot token for Gateway WebSocket |
82+
| `DISCORD_PUBLIC_KEY` | webhook mode || Ed25519 public key (hex) for Interactions |
83+
| `DISCORD_GATEWAY_INTENTS` | no | see code | Comma-separated gateway intents |
84+
| `NGROK_AUTHTOKEN` | webhook + dev || ngrok auth token |
85+
| `DISCORD_WEBHOOK_PORT` | no | `8080` | HTTP port for the webhook receiver |
86+
| `DISCORD_SUBJECT_PREFIX` | no | `discord` | NATS subject prefix |
87+
| `DISCORD_STREAM_NAME` | no | `DISCORD` | JetStream stream name |
88+
| `DISCORD_STREAM_MAX_AGE_SECS` | no | `604800` | Max age in seconds for JetStream messages (7 days) |
89+
| `DISCORD_NATS_ACK_TIMEOUT_SECS` | no | `10` | NATS publish ack timeout in seconds |
90+
| `DISCORD_NATS_REQUEST_TIMEOUT_SECS` | no | `2` | NATS request-reply timeout for autocomplete in seconds |
91+
| `DISCORD_MAX_BODY_SIZE` | no | `4194304` | Max webhook body size in bytes (4 MB) |
92+
| `RUST_LOG` | no | `info` | Log level |

0 commit comments

Comments
 (0)