Skip to content

Rewrite the bot in TypeScript - #40

Merged
gaato merged 15 commits into
mainfrom
ts-rewrite
Jul 18, 2026
Merged

Rewrite the bot in TypeScript#40
gaato merged 15 commits into
mainfrom
ts-rewrite

Conversation

@gaato

@gaato gaato commented Jul 18, 2026

Copy link
Copy Markdown
Owner

Summary

Full rewrite of the Discord bot from Python (py-cord) to TypeScript on Node 24, discord.js v14, and pnpm.

  • In-process TeX rendering with MathJax v4 (@mathjax/src + newcm font) and sharp — retires the coderunbot-tex web service and TEX_BASE_URL
  • State storage via the S3-compatible API (@aws-sdk/client-s3, BOT_STATE_BACKEND=s3 + S3_* env) instead of the OCI SDK; deployment will target Cloudflare R2 (S3_REGION=auto). Object keys are unchanged ({prefix}/opt-out-users.txt), so migration is a plain object copy
  • Components V2 for all replies, user-installable commands, en/ja i18n (i18next), edit-following via reply edits with latest-wins generations, delete buttons authorized per invoker
  • Anonymous usage statistics (monthly counters per command × outcome, /run languages, guild counts) flushed to the state backend; documented in the privacy policy
  • Relicensed AGPL-3.0-or-later with a /source command for the §13 source offer
  • Architecture: index.ts as the only composition root, one-way features → platform|shared dependencies enforced by dependency-cruiser and madge in CI
  • CI builds ghcr.io/gaato/coderunbot-discord from discord/Containerfile (amd64 + arm64)

Testing

  • 78 unit tests, typecheck, Biome lint, dependency rules — all green in CI
  • Live-verified in the support server with a separate test app (CodeRunBot Nightly): slash/prefix/modal/button/context-menu dispatch, autocomplete, opt-out gating and re-opt-in, edit-following, i18n locale fallbacks, usage-stats flush on SIGTERM. Two reply bugs found there are fixed in 79b7fab

Deployment notes (handled separately in iac)

  • Quadlet env switches to BOT_STATE_BACKEND=s3 + S3_* (Cloudflare R2); the coderunbot-tex unit and the OCI API key mount go away
  • Token env var names are CODERUNBOT_TOKEN / GAATO_BOT_TOKEN (selected by GAATO_BOT)
  • Opt-out lists must be copied from the OCI bucket to R2 before cutover

gaato added 15 commits July 18, 2026 13:06
Replace mathjax-full v3 with @mathjax/src and the newcm font package.
TeX extensions are now enumerated explicitly (AllPackages is gone in v4),
compile errors throw via formatError instead of being scraped out of
data-mjx-error markup, and conversion goes through convertPromise so the
dynamically split font data can load on demand.
Add file-header comments describing each module's architectural role and
inline comments for the non-obvious constraints: Discord API limits (ack
window, autocomplete cap, Components V2 text budget), the latest-wins
generation scheme in the reply coordinator, snowflake/string and S3
compatibility details in the state layer, and the history behind the
pinned Wandbox compiler map.
Production runs on Podman quadlets and local runs need nothing more than
podman build/run, so drop compose.yaml and the duplicate Dockerfile
(Containerfile stays for the image workflow).
The TypeScript rewrite removed the only externally contributed code, and
Apache-2.0 history is one-way compatible with AGPLv3 anyway, so this is
the clean point to switch. The privacy policy now links the source code
to satisfy the section 13 network-source offer.
Both bots now answer /source with the repository URL, making the
AGPL-3.0 section 13 source offer available directly in Discord in
addition to the privacy policy link.
Aggregate monthly counters (command x outcome, /run language, guild
count) are persisted per bot through the existing state backend to
inform development decisions. No user IDs, guild IDs, message content,
or command arguments are recorded, and the privacy policy documents the
counters. Counters flush every five minutes and on SIGTERM/SIGINT.
The privacy policy outgrew the 2,000-character plain-content limit, so
/privacy-policy now replies with a Components V2 text display, with a
regression test pinning the document under the 4,000-character budget.

The escape context menu failed on messages without text content (e.g.
the bot's own Components V2 replies) and would also fail once escaping
pushed a long message past the content limit; it now explains the empty
case and falls back to an attached file for oversized results.
@gaato
gaato merged commit 140d174 into main Jul 18, 2026
2 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant