Repository navigation
feat(bot): Discord bot workspace with /meet link and /meet create - #188
Merged
Merged
Conversation
Add a 4th monorepo workspace, bot/, a thin discord.js adapter over the /api/integrations/discord API (ticket #185). Slash commands only (no Message Content intent): - /meet link -> mints an account-link token, replies ephemerally with the confirmation URL. - /meet create -> creates an instant room hosted by the linked user and posts a public embed with the join link; not-linked users get an ephemeral prompt to run /meet link; other failures reply with a friendly ephemeral error. Includes a typed HTTP client for the two bot-facing endpoints (X-Bot-Api-Key auth, not_linked detection), a slash-command registration script, strict TS + Vitest + ESLint mirroring the other workspaces, per-workspace lockfile, CI lint/test jobs, aggregate typecheck coverage, a production Dockerfile, and a bot service wired into docker-compose.prod.yml (restart: always). Closes #186
|
The latest updates on your projects. Learn more about Vercel for GitHub.
|
Address review findings: - Handlers defer the reply immediately (deferReply ephemeral) before the HTTP call, then editReply/followUp — Discord invalidates the interaction token after ~3s, so a slow API call no longer leaves the user with a dead interaction. Success posts the public embed as a non-ephemeral followUp while errors/not-linked stay ephemeral, preserving the required reply routing. - docker-compose.prod.yml: the bot service is now profile-gated (profiles: [bot]) so the automated server deploy's bare `up -d` never touches it, and its env uses non-failing `:-` defaults instead of `:?` guards (which also fixes invalid YAML from a colon-space in the guard message). The bot validates its own required config at startup and exits non-zero if misconfigured. - Root .dockerignore now allowlists bot/package.json + bot/src so the bot image (built from the repo-root context) resolves its COPYs; removed the dead bot/.dockerignore (a per-Dockerfile ignore is not applied for a root context). - bot/Dockerfile drops the unused tsconfig copies (tsx strips types without them).
…ublic-boundary test - handleCreate now wraps the public followUp in try/catch: if posting the announcement fails (e.g. the bot can't send in that channel), it resolves the ephemeral deferred reply with the meeting link instead of letting the error bubble to the InteractionCreate handler, which skips already-deferred interactions and would leave the invoker hanging. - handlers test asserts the public followUp sets neither the ephemeral flag bit nor the deprecated `ephemeral: true` shorthand, and adds a rejecting-followUp case.
- Extract dispatchInteraction: a top-level safety net around the router that recovers ALREADY-deferred/replied interactions via editReply (the previous guard only handled the not-yet-deferred case, so a mid-handler editReply/ followUp rejection left the invoker with an unresolved spinner). bot.ts now delegates to it. Added tests for the deferred, not-deferred, failing-recovery, and non-repliable paths. - Harden the public-boundary test helper to reject the Ephemeral marker in every form discord.js accepts (numeric bit, 'Ephemeral' string, array) plus the deprecated ephemeral:true shorthand. - Root coverage + audit aggregates and the CI audit job now include the bot workspace, so its tests and dependency tree are inside the pre-merge gates.
A server that accepts the connection but never completes the response would otherwise leave a deferred /meet interaction hanging with no exception for dispatchInteraction to recover from. Each request now runs under an AbortController deadline (default 10s, configurable); on timeout the fetch rejects and the handler answers with a friendly ephemeral error. Tested with a never-resolving fetch under fake timers plus a signal-passing assertion.
Advance fake timers past the deadline after a successful request and assert the signal stays un-aborted, so the test would fail if clearTimeout were removed.
This branch was successfully deployed
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Ticket 2 of 2 of the Discord bot feature (spec: `docs/specs/2026-07-23-discord-bot-design.md`). Ticket #185 (the server integration API) is already merged; this adds the thin Discord-facing adapter that calls it.
What's here
Testing (strict TDD, behavior not implementation)
28 unit tests, no real Discord: command handlers with mocked interactions + mocked HTTP client (linked / not_linked / server-error / network-error, ephemeral vs public reply routing asserted); HTTP client against a mocked fetch (headers, body, base-URL, status→result mapping); env loader; interaction router. Coverage 100% stmts/funcs/lines, 96% branches.
Bot holds no meeting logic — thin adapter only. Runs independently of the API server.
Closes #186