All projects

internal ยท Built 2026-10-26 (all files carry that date)

Discord Server Builder and Ticket Bot

A small Node command-line tool and a Cloudflare Worker bot that set up a Discord community and run role buttons, applications and support tickets.

  • Node.js 18+ (built-in fetch, no packages)
  • Discord REST API v10
  • Cloudflare Workers
  • Cloudflare KV storage
  • Ed25519 request signing
  • Python (banner images)

My role

I directed AI (Claude Code) to build it and ran the deployment. I own the Discord server and the bot application. AI wrote the code; no other person worked on it.

What it is

This project has two parts. The first is a set of dependency-free Node scripts that call the Discord API to create categories, channels and roles, and post styled messages from a layout file. That is how the Stratum Tech Group community server was set up.

The second is a serverless bot on Cloudflare Workers. It receives signed webhooks from Discord and handles buttons and forms: self-serve role toggles, a creator application that opens a private ticket channel, support tickets, and team-only Accept, Decline and Close buttons.

The bot token and other credentials are kept out of the code and out of the repo. Nothing here is public code; the server itself is a community space.

How it was built

  1. Write a tiny Discord REST clientdiscord.mjs (71 lines) wraps the Discord v10 API with plain fetch so there is nothing to install, and retries when Discord rate-limits a call.
  2. Describe the server as databuild-stratum.mjs (150 lines) and native-setup.mjs (67 lines) create categories, channels and roles from a layout, so the server can be rebuilt the same way.
  3. Post styled contentstyled-posts.mjs (119 lines), stratum-posts.mjs (71), games-posts.mjs (68), forum-and-announce.mjs (41) and upload-kit.mjs (21) publish the welcome, rules, games and announcement posts.
  4. Generate brand artmake-banners.py (33 lines) renders the server icon (1024 px), profile banner (1360x480) and server banner (1920x1080) in the Stratum gold-on-dark style.
  5. Pull game stats into a filegames.json snapshots the studio's Roblox games (players, visits, favourites) so posts can show current numbers.
  6. Write the interaction botworker/stratum-bot.js (157 lines) is the Worker. It answers Discord's ping, then routes button clicks (type 3) and form submissions (type 5).
  7. Verify every requestBefore doing anything the Worker checks Discord's Ed25519 signature on the raw request body using Web Crypto, and returns 401 on a bad signature.
  8. Run tickets as private channelsA creator application or support form creates a private channel under a tickets category, visible only to the member and the team roles, and stores the ticket in KV so each member gets one open ticket per kind.
  9. Gate team actionsAccept, Decline and Close buttons only work for members with a team, founder or moderator role, or admin permission; replies use deferred responses (ctx.waitUntil) so slow API work never times out.
  10. Deploy by APIworker/deploy.mjs (71 lines) uploads the Worker and its KV binding through the Cloudflare API, with the Cloudflare token taken from the environment and the bot token from a local secrets file.

Decisions and why

No npm dependencies
The scripts run on any machine with Node 18 and there is nothing to keep updated or audit.
Serverless Worker instead of an always-on bot
Button clicks arrive as webhooks, so a Worker with KV storage is cheap and needs no server to keep running.
Private ticket channel per application
Applicants get a place to talk with the team without exposing their details to the rest of the server.
Keep tokens and ids out of anything published
Credentials live in a local secrets file and environment variables; the deploy script's ids stay private.

Hardest problems

Discord rate limits break bulk setup scripts
Every API call retries up to four times, waiting the retry_after time Discord returns.
Discord needs a reply within seconds but ticket creation takes many API calls
The Worker answers instantly with a deferred message and finishes the work in the background, then edits the reply.
Stopping anyone but the team from using staff buttons
A role and permission check on every Accept, Decline and Close click.

Numbers

Source files11 scripts (.mjs, .js, .py)2026-11-06
Lines of code869 across the 11 files2026-11-06
Worker size157 lines (stratum-bot.js)2026-11-06
Brand images3 (icon, profile banner, server banner)2026-11-06

Timeline

  • 2026-10-26Admin scripts, brand banners and the Cloudflare Worker bot all built and deployed the same day.

What I took from it

  • Handling rate limits and deferred replies up front avoids most flaky bot behaviour.
  • Verifying signatures and gating staff buttons is the minimum for any public-facing bot.
  • Keeping secrets out of the scripts makes the code safe to show and move around.

Links