Commit Graph
33 Commits
Author SHA1 Message Date
tiennm99 412d25b19e fix(stock): drop yahoo price fallback 2026-06-25 17:16:54 +07:00
tiennm99 ae92539043 docs(gold): document VNAppMob SJC pricing and env vars 2026-06-15 10:49:19 +07:00
tiennm99 32417e4196 refactor(stock): rename trading module to stock
Rename the `trading` module to `stock` across code, config, and docs for
naming consistency with the sibling coin/gold modules.

- Module: internal/modules/trading -> internal/modules/stock (package,
  factory, registry key in cmd/server)
- Commands: trade_* -> stock_* (telegram-commands.json)
- Env/SAM params: TRADING_INCOME_EVENTS_* -> STOCK_INCOME_EVENTS_*
- ModulesCSV updated in template.yaml, samconfig.toml, deploy.yml
- Add cmd/migrate-stock-key: idempotent, non-destructive DynamoDB
  partition copy (pk=trading -> pk=stock) with dry-run default

Production DynamoDB data already migrated (8 keys: 2 portfolios + 6
symbol-cache rows); old trading partition retained as rollback snapshot.
2026-06-12 14:52:12 +07:00
tiennm99 62555dee23 fix(deploy): include coin module in ModulesCSV overrides
coin was registered in the catalog and template default but absent from
the ModulesCSV override in both deploy.yml and samconfig.toml, so the
deployed Lambda never loaded it and all coin_* commands went unregistered.

Add a registration checklist to docs/deploy-aws.md to prevent recurrence.
2026-06-12 11:22:16 +07:00
tiennm99 d1a01830ae feat(gold): add gold paper trading module
Opt-in module for gold paper trading with VND currency.
Commands: /gold_topup, /gold_buy, /gold_sell, /gold_stats.
Pricing: spot XAU USD converted to VND per luong via GoldPrice.org + ExchangeRate-API.
Features: FX caching, dust normalization (1e-9), HTTPS-only URL validation,
per-user key locking, namespace isolation from trading module.
2026-06-11 17:13:02 +07:00
tiennm99 537bfc85e9 feat: add trade income events check 2026-06-05 14:21:38 +07:00
tiennm99 ec77a24db7 chore: drop CF→AWS migration tooling and stale JS-port references
The CF→AWS data migration (closed 2026-05-16) is long done and the
tooling isn't wired into any production path. Remove the one-shot binary,
its support package, and the migration runbook.

In live code, replace 'JS-parity' / 'same shape as JS' / 'cross-runtime
KV migration' comments with the real, stable reason for each behavior
(wire-format invariant, null-vs-zero distinction, CloudWatch alarm field
name, etc.). 24 files touched across lolschedule, loldle, wordle, twentyq,
trading, misc, util, server, metrics, ai, keylock.

- delete cmd/migrate_cf_data/
- delete internal/migration/
- delete docs/cf-to-aws-migration-runbook.md
2026-05-25 09:39:17 +07:00
tiennm99 3b74aa3abb docs(security): audit reports + IAM least-privilege plan + project policy
Captures the 2026-05-18 security review session output:

- plans/reports/code-reviewer-260518-1019-security-aws-infra.md
- plans/reports/code-reviewer-260518-1019-security-go-app.md
- plans/reports/researcher-260518-1019-security-dependencies.md
- docs/deploy-aws-free-tier-guide.md (adds free-tier hard rule +
  accepted security trade-offs as project standards)

Plan for the two HIGH-severity findings (F1, F2) targeting
github-deploy-miti99bot OIDC role: plans/260518-1019-iam-least-privilege/.
Plan was red-team-reviewed (15 findings applied) and validate-interviewed
(4 decisions recorded). Zero unresolved contradictions. Implementation
not yet started; phase 1 is standalone and lowest risk.

Other audit findings (F3 CORS, F4 root handler, F5-F16) deferred to
future commits; rationale in audit report.
2026-05-18 16:50:38 +07:00
tiennm99 dc51978f29 ci(deploy): auto-register Telegram webhook + commands after SAM deploy
Append two steps to .github/workflows/deploy.yml that POST setWebhook
and setMyCommands against the freshly-deployed Function URL, reading
credentials from SSM. Mirrors `make telegram-setup` but inlined to
avoid the Makefile's --profile admin assumption.

Token and webhook-secret are masked via ::add-mask:: before any echo.
Jobs fail loudly on Telegram API errors via `jq -e .ok`.

Mark the manual setWebhook snippets in docs/deploy-aws.md and
docs/deploy-aws-free-tier-guide.md as break-glass.
2026-05-16 10:55:43 +07:00
tiennm99 aff4bf4211 docs(plans): close cf→aws phases 01-02 and amend locked decisions
- phase-01: mark complete; record live D1+KV inventory outcome (9 durable
  KV keys, trading source is KV not D1, retired modules skipped)
- phase-02: mark complete; toolchain verified end-to-end against prod CF
- phase-03: rewrite to flat KV copy (D1-transform branch dropped)
- plan.md: amend staging-only decision to permit pre-cutover bulk import
  against the live table while it has no AWS-served writes
- runbook: lock matrix and trading source, document Phase 02 commands
2026-05-16 10:33:46 +07:00
tiennm99 7cc8bdfa8b fix(plans): align cf-to-aws migration and cutover docs
Lock the Cloudflare-to-AWS migration matrix and runbook against the live DynamoDB runtime shape, and gate AWS cutover on verified migration parity instead of assuming a symmetric rollback.
2026-05-15 23:54:06 +07:00
tiennm99 f2e0dd5156 fix(ci): pin deploy role ARN for OIDC auth
The deploy workflow no longer relies on a missing AWS_ACCOUNT_ID secret,
preventing invalid assume-role ARNs and matching the repo's documented AWS setup.
2026-05-15 22:36:15 +07:00
tiennm99 c2968e3de7 Fix AWS Lambda deploy setup 2026-05-15 22:18:27 +07:00
tiennm99 642fccb7b7 refactor: rename module to miti99bot, canonicalize AWS deploy path
Rename:
- Go module github.com/tiennm99/miti99bot-go → github.com/tiennm99/miti99bot
- CloudFormation stack miti99bot-aws-port → miti99bot
- Drop "port", "Cloud Run", "GCP", "cutover", "Phase NN" framing from
  active code and docs — project reads as canonical AWS-Lambda from now on.

AWS deploy guide + flow fix:
- New docs/deploy-aws-free-tier-guide.md — Ubuntu 24.04 ARM64 onboarding
  with project-local venv (pip awscli + sam-cli), SSM secrets via read -s,
  idempotent OIDC provider + role creation, $1 budget alarm.
- Drop sam build from the pipeline — provided.al2023 + makefile builder
  expects a Makefile in CodeUri (build/lambda/, the output dir), so the
  step always fails. sam deploy --template-file template.yaml now reads
  the raw template and zips build/lambda/ directly.
- Rollback section rewritten — use continue-update-rollback /
  cancel-update-stack / git-SHA redeploy. Drop the broken
  --use-previous-template recipe.
- DynamoDB free-tier row corrected (on-demand is 2.5M read / 1M write
  request units, not 25 RCU/WCU).

Updated:
- README.md fully rewritten (drops port/legacy framing, lists modules,
  points new users at the free-tier guide).
- aws/README.md retitled "AWS account setup", phase numbers stripped.
- Makefile / .github/workflows/deploy.yml — sam deploy flow.
- samconfig.toml — stack_name = "miti99bot".
- Go comments — Cloud Run → Lambda, Cloud Scheduler → EventBridge
  Scheduler, Cloud Logging → CloudWatch Logs.
- Struct field GCPProject → FirestoreProject (env GOOGLE_CLOUD_PROJECT
  unchanged).

Plus advisory reports under plans/reports/ from the code-reviewer +
researcher passes that informed the fixes.

Verified: go vet ./..., go build ./..., go test ./... all green.
2026-05-13 22:05:38 +07:00
tiennm99 03c2476616 docs(aws): bootstrap + ops cheatsheets
- aws/README.md: one-time AWS account setup (account ID, IAM, OIDC)
- aws/iam-github-oidc-trust.json: GitHub OIDC trust policy template
- docs/deploy-aws.md: steady-state deployment operations guide
2026-05-10 02:29:52 +07:00
tiennm99 9eb1c527af Clear and rewrite
Retire the JavaScript/Cloudflare implementation; history continues from the Go rewrite (miti99bot).
2026-08-04 23:21:19 +07:00
tiennm99 b52fa7094e feat(loldle): tunable max guesses per subject + retuned defaults
Drop classic loldle from 8 → 6 (7-axis grid leaks too much per guess for 8
to feel earned) and emoji from 5 → 4 (3 emojis are usually unmistakable).
Add a hidden /<module>_setmax <n> command per loldle module so a chat can
override its own round length (1-10). Override stored at config:<subject>
in each module's KV; getMaxGuesses() falls back to the default when unset.
2026-05-07 14:10:01 +07:00
tiennm99 b660692eb2 feat(db): finalize MongoDB Atlas migration documentation
Phase 08: Complete documentation pass for MongoDB Atlas migration.

- Create docs/cost-tracking.md: Cost monitoring, upgrade triggers, monthly checklist
- Create docs/project-changelog.md: Full migration summary with phase breakdown
- Update docs/architecture.md section 8: Describe dual-write era, MongoDB store layers
- Update docs/code-standards.md: Add Persistence section for storage factory patterns
- Update docs/codebase-summary.md: Reflect MongoDB as primary, update test count (733)
- Update README.md: Storage section now describes MongoDB + dual-write during migration
- Update CLAUDE.md: Architecture section references MongoDB instead of KV/D1
- Update tests/fakes/fake-mongo.js: Document frozen surface (Phase 02-08 API)

Verified:
- All 733 tests passing
- Lint + secret-leak check pass
- npm run register:dry succeeds
- Auto-pause concern satisfied: trading (17:00), lolschedule (01:00), drift-verifier (hourly) all write to Mongo
- Roadmap verified migration NOT listed (future-only per user feedback)

Post-Phase-07 cutover: dual-write collapses, KV/D1 deleted, MongoDB becomes sole backend.
2026-04-26 09:34:25 +07:00
tiennm99 3ef8540665 feat(db): phase 01 — atlas wrangler config + secret-leak lint + mongodb dep
Code/config slice of plan phase 01 (operator-only steps for cluster
provisioning, secrets, and runtime smoke tests deferred to user).

- wrangler.toml: add `compatibility_flags = ["nodejs_compat_v2"]`
  (compatibility_date `2025-10-01` already satisfies ≥ 2025-03-20)
- .env.deploy.example: add `MONGODB_URI` placeholder with mirror-protocol note
- scripts/check-secret-leaks.js: lint that fails build on `console.log(env.<SECRET>)`
  for MONGODB_URI / TELEGRAM_BOT_TOKEN / TELEGRAM_WEBHOOK_SECRET / ADMIN_TOKEN
- package.json: install mongodb@^6.7.0 (resolved 6.21.0); wire secret-leak
  check into `npm run lint`
- docs/using-mongodb.md: operational runbook (cluster spec, free-tier ceiling,
  auto-pause behavior, network access permanence, rollback, rotation)

Bundle-size HARD GATE: PASS. Probe with `import { MongoClient }` measures
226 KiB gzipped (3 MiB Free cap, 92% headroom) — nodejs_compat_v2 provides
node:net/tls/crypto from runtime so transitive deps stay unbundled.

CPU-time gate and auto-pause behavior gate require real Atlas access;
deferred to operator (see docs/using-mongodb.md for procedure).

503/503 vitest tests still pass.
2026-04-26 08:32:19 +07:00
tiennm99 eb00d0c409 docs(architecture): add missing types.js to file tree
Self-review of the prior cleanup commit caught one omission — src/types.js
(central JSDoc typedefs file: Env, Module, Command, Cron, …) was listed in
the top-level README but absent from docs/architecture.md's src/ tree.
2026-04-24 18:54:23 +07:00
tiennm99 0be7cc9a95 chore: project cleanup — purge stale function-calling refs + sync docs
Followed code-reviewer audit. Findings applied:
- twentyq/README.md, twentyq/index.js header — claimed "function calling"
  + ANSWER_FUNCTION_SCHEMA / submit_answer; rewrite to JSON-in-content
  matching what the code actually does. Added generateRoundStart line.
- wrangler.toml [ai] comment — list both bge-m3 (semantle/doantu) AND
  gemma-4 (twentyq) consumers; drop neuron math that no longer matched.
- scripts/stub-kv.js — drop reference to nonexistent REGISTER_DRYRUN flag.
- twentyq/ai-client.redactSecret — strip dead "if (out.length > 0)" branch
  (String.replace cannot produce empty string from the inputs we pass).
- handlers.test.js — drop noise saveGame() before "no games" stats assert;
  add ai.run call-count guards on two-AI-call flows.
- docs/codebase-summary.md — full rewrite of Active Modules table
  (semantle/doantu/lolschedule/twentyq were missing); fix vitest 2→4 +
  wrangler 3→4 versions; replace stale 200-test count with current ~450.
- docs/architecture.md — file tree includes lolschedule/semantle/doantu/
  twentyq + cron-dispatcher + sql-store* + scripts/migrate.js;
  moduleRegistry snippet matches src/modules/index.js.
- docs/todo.md — entire file obsolete (D1 UUID populated, cron live).
  Deleted.

Tests: 449 pass, lint clean.
2026-04-24 18:30:25 +07:00
tiennm99 85a266b7f7 feat(twentyq): add reverse-Akinator yes/no game module powered by Workers AI
- seeded 54 objects across 6 categories (instrument, animal, food, vehicle, sport, household)
- @cf/google/gemma-4-26b-a4b-it judges via function calling; returns {is_guess, answer, hint}
- pre-AI validator rejects open-ended questions; handler dedups exact repeats
- secret-redacting hint filter as defense-in-depth
- 86 new vitest tests (seeds, state, validator, ai-client, handlers, render)
2026-04-24 14:37:23 +07:00
tiennm99 06a93c6c60 feat(util): add private /stickerid to capture bot-scoped sticker file_ids
Reply to any sticker with /stickerid to get its file_id and
file_unique_id. Collects IDs for hard-coded sticker pools in other
modules (upcoming loldle win/lose/giveup reactions).

Private visibility keeps the command out of /help and the Telegram
slash menu.
2026-04-21 23:48:27 +07:00
tiennm99 7997c8eb5b feat(loldle): monospace aligned board, auto-restart round
- Remove /loldle_new; finished rounds (solve/giveup/out-of-guesses)
  immediately roll into a fresh round.
- Render guesses as an HTML <pre> monospace table with auto-widthed
  label column and a 🎯 Name row (uppercase champion name).
- Year direction uses ⬆️ / ⬇️.
2026-04-21 23:03:49 +07:00
tiennm99 bd270a6835 fix: project-wide review — trading safety, loldle drift guard, doc refresh
Code fixes:
- trading/handlers + stats-handler: guard ctx.from?.id to prevent
  cross-user state corruption when channel posts or inline queries lack
  a sender
- trading/prices + trading/symbols: encodeURIComponent on ticker before
  interpolating into TCBS API URLs
- trading/stats-handler: parallelize per-stock price fetches with
  Promise.allSettled so N-stock portfolios don't stack serial latency
- loldle/handlers: guard target champion lookup against champions.json
  refresh drift — start a fresh round or fall back to the stored id
- wordle + loldle: explicitly initialize giveup:false in startFreshGame
  for stable state shape
- wordle/lookup: fix stale JSDoc that claimed null return
- biome: ignore auto-generated champions.json / champions-data.js /
  words-data.js
- Apply formatter to src/index.js, loldle/handlers.js imports, and
  loldle/compare.test.js (previously red)

Docs refresh:
- README: 105+ tests -> 200+; wordle/loldle described as real modules
- architecture: module tree updated, test count 105 -> 200, runtime
  ~500ms -> ~2s, stub list narrowed to misc only
- codebase-summary: module table rewritten (wordle/loldle now Complete
  with real command lists and KV schema); test coverage table updated
- loldle/README: full rewrite matching the current implementation
  (was describing the original stub)
- New docs/development-roadmap.md tracking upcoming features
  (daily-mode for wordle + loldle, crypto/gold/forex trading, shared
  picker util, handler-level tests, coverage reporting, staging env)

Tests: 200/200 passing. Lint: clean.
2026-04-20 22:08:58 +07:00
tiennm99 dc4432aef3 docs: add D1 and Cron guides, update module contract across docs
- docs/using-d1.md and docs/using-cron.md for module authors
- architecture, codebase-summary, adding-a-module, code-standards, deployment-guide refreshed
- CLAUDE.md module contract shows optional crons[] and sql in init
- docs/todo.md tracks manual follow-ups (D1 UUID, first deploy, smoke tests)
2026-04-15 13:29:31 +07:00
tiennm99 dac6cc9778 docs: update all docs to reflect current trading module state
Trading module now VN stocks only with dynamic symbol resolution.
Update test counts (105), remove crypto/gold/forex references from
project-level docs, update architecture file tree descriptions.
2026-04-14 17:38:31 +07:00
tiennm99 3c0709b03b docs: move module-specific docs to module READMEs
Extract trading module details (commands, data model, price APIs, file
layout) from architecture.md and codebase-summary.md into
src/modules/trading/README.md. Project-level docs now only contain
global framework info with pointers to module-local READMEs.
2026-04-14 15:37:33 +07:00
tiennm99 dfe6686553 docs: remove development roadmap — stale-prone, not useful 2026-04-14 15:33:42 +07:00
tiennm99 59e9469e42 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
tiennm99 2d16c50d4d docs: update architecture and README for trading module
Add trading module section (§13) to architecture.md covering commands,
data model, price sources, and file layout. Update file trees, test
counts (56→110), and module registry snippet in both docs.
2026-04-14 15:24:01 +07:00
tiennm99 07337c00fb docs: add architecture guide and polish README intro
- README: add "Why" value prop, request-flow ASCII diagram,
  and "Further reading" links to the docs directory
- docs/architecture.md: new 14-section guide covering cold-start
  flow, module contract, static loader rationale, unified-namespace
  conflict detection, dispatcher minimalism, KVStore prefixing
  mechanics, deploy flow, security posture, testing philosophy,
  and non-goals
2026-04-11 10:25:32 +07:00
tiennm99 7e3d0ccc4d feat: scaffold plug-n-play telegram bot on cloudflare workers
grammY-based bot with a module plugin system loaded from the MODULES env
var. Three command visibility levels (public/protected/private) share a
unified command namespace with conflict detection at registry build.

- 4 initial modules (util, wordle, loldle, misc); util fully implemented,
  others are stubs proving the plugin system end-to-end
- util: /info (chat/thread/sender ids) + /help (pure renderer over the
  registry, HTML parse mode, escapes user-influenced strings)
- KVStore interface with CFKVStore and a per-module prefixing factory;
  getJSON/putJSON convenience helpers; other backends drop in via one file
- Webhook at POST /webhook with secret-token validation via grammY's
  webhookCallback; no admin HTTP surface
- Post-deploy register script (npm run deploy = wrangler deploy && node
  --env-file=.env.deploy scripts/register.js) for setWebhook and
  setMyCommands; --dry-run flag for preview
- 56 vitest unit tests across 7 suites covering registry, db wrapper,
  dispatcher, help renderer, validators, and HTML escaper
- biome for lint + format; phased implementation plan under plans/
2026-04-11 09:49:06 +07:00