Files
miti99bot/CLAUDE.md
tiennm99 4277f11c48 docs: add CLAUDE.md and project documentation
Add CLAUDE.md for AI assistant context. Create four new docs:
deployment-guide.md (full deploy flow + secret rotation + rollback),
code-standards.md (formatting, naming, module conventions, testing),
codebase-summary.md (tech stack, modules, data flows, external APIs),
development-roadmap.md (completed phases + planned work).
2026-04-14 15:28:53 +07:00

3.7 KiB

CLAUDE.md

This file provides guidance to Claude Code (claude.ai/code) when working with code in this repository.

Commands

npm run dev          # local dev server (wrangler dev) at http://localhost:8787
npm run lint         # biome check src tests scripts
npm run format       # biome format --write
npm test             # vitest run (all tests)
npx vitest run tests/modules/trading/format.test.js   # single test file
npx vitest run -t "formats with dot"                  # single test by name
npm run deploy       # wrangler deploy + register webhook/commands with Telegram
npm run register:dry # preview setWebhook + setMyCommands payloads without calling Telegram

Architecture

grammY Telegram bot on Cloudflare Workers. Modules are plug-n-play: each module is a folder under src/modules/ that exports { name, commands[], init? }. A single MODULES env var controls which modules are loaded.

Request flow: POST /webhook → grammY validates secret header → getBot(env) (memoized per warm instance) → installDispatcher builds registry on first call → bot.command(name, handler) for every command → handler runs.

Key abstractions:

  • src/modules/registry.js — loads modules from static import map (src/modules/index.js), validates commands, detects name conflicts across all visibility levels, builds four maps (public/protected/private/all). Memoized via getCurrentRegistry().
  • src/db/create-store.js — wraps Cloudflare KV with auto-prefixed keys per module (moduleName:key). Modules never touch env.KV directly.
  • scripts/register.js — post-deploy script that imports the same registry to derive public commands, then calls Telegram setWebhook + setMyCommands. Uses stub-kv.js to satisfy KV binding without real IO.

Three command visibilities: public (in Telegram / menu + /help), protected (in /help only), private (hidden easter eggs). All three are registered via bot.command() — visibility controls discoverability, not access.

Adding a Module

  1. Create src/modules/<name>/index.js with default export { name, commands, init? }
  2. Add one line to src/modules/index.js static import map
  3. Add <name> to MODULES in wrangler.toml [vars]
  4. Full guide: docs/adding-a-module.md

Module Contract

{
  name: "mymod",                          // must match folder + import map key
  init: async ({ db, env }) => { ... },   // optional — db is prefixed KVStore
  commands: [{
    name: "mycmd",                        // ^[a-z0-9_]{1,32}$, no leading slash
    visibility: "public",                 // "public" | "protected" | "private"
    description: "Does a thing",          // required for all visibilities
    handler: async (ctx) => { ... },      // grammY context
  }],
}

Command names must be globally unique across ALL modules and visibilities. Conflicts throw at load time.

Testing

Pure-logic unit tests only — no workerd, no Telegram fixtures. Tests use fakes from tests/fakes/ (fake-kv-namespace, fake-bot, fake-modules) injected via parameters, not vi.mock.

For modules that call fetch (like trading/prices), stub global.fetch with vi.fn() in tests.

Code Style

Biome enforces: 2-space indent, double quotes, semicolons, trailing commas, 100-char line width, sorted imports. Run npm run format before committing. Keep files under 200 lines — split into focused submodules when approaching the limit.

Environment

  • Secrets (TELEGRAM_BOT_TOKEN, TELEGRAM_WEBHOOK_SECRET): set via wrangler secret put, mirrored in .env.deploy (gitignored) for register script
  • .dev.vars: local dev secrets (gitignored), copy from .dev.vars.example
  • Node >=20.6 required (for --env-file flag)