mirror of
https://github.com/tiennm99/tiennm99bot.git
synced 2026-10-15 08:17:21 +00:00
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.
This commit is contained in:
1 parent
574d855806
commit
bd270a6835
15 files changed
+211
-58
No files matched your search
@@ -30,7 +30,8 @@ src/
|
||||
│ ├── validate-command.js ── shared validators
|
||||
│ ├── util/ ── fully implemented: /info + /help
|
||||
│ ├── trading/ ── paper trading: VN stocks (dynamic symbol resolution)
|
||||
│ ├── wordle/ loldle/ ── stub modules proving the plugin system
|
||||
│ ├── wordle/ ── 5-letter guessing game (KV storage)
|
||||
│ ├── loldle/ ── classic-mode LoL champion guesser (KV storage)
|
||||
│ └── misc/ ── stub that exercises the DB (ping/mstats)
|
||||
└── util/
|
||||
└── escape-html.js
|
||||
@@ -359,7 +360,7 @@ A previous design sketched a `POST /admin/setup` route inside the Worker, gated
|
||||
|
||||
## 12. Testing philosophy
|
||||
|
||||
Pure-logic unit tests only. No `workerd` pool, no Telegram fixtures, no integration-level tooling. 105 tests run in ~500ms.
|
||||
Pure-logic unit tests only. No `workerd` pool, no Telegram fixtures, no integration-level tooling. 200 tests run in ~2s.
|
||||
|
||||
Test seams:
|
||||
|
||||
@@ -381,7 +382,7 @@ Each module maintains its own `README.md` with commands, data model, and impleme
|
||||
|
||||
## 14. Non-goals (for now)
|
||||
|
||||
- Real game logic in `wordle` / `loldle` / `misc` — they're stubs that exercise the framework. Real implementations can land later.
|
||||
- Real game logic in `misc` — it's a stub that exercises the DB. Real game modules (`wordle`, `loldle`, `trading`) are live; `misc` stays a framework sanity check.
|
||||
- A sandbox between modules. Same-origin trust model: all modules are first-party code.
|
||||
- Per-user rate limiting. Cloudflare's own rate limiting is available as a higher layer if needed.
|
||||
- `nodejs_compat` flag. Not needed — grammY + this codebase use only Web APIs.
|
||||
|
||||
@@ -20,10 +20,10 @@ Telegram bot on Cloudflare Workers with a plug-n-play module system. grammY hand
|
||||
| Module | Status | Commands | Storage | Crons | Description |
|
||||
|--------|--------|----------|---------|-------|-------------|
|
||||
| `util` | Complete | `/info`, `/help` | — | — | Bot info and command help renderer |
|
||||
| `trading` | Complete | `/trade_topup`, `/trade_buy`, `/trade_sell`, `/trade_convert`, `/trade_stats`, `/history` | D1 (trades) | Daily 5PM trim | Paper trading — VN stocks with dynamic symbol resolution. Crypto/gold/forex coming soon. |
|
||||
| `trading` | Complete | `/trade_topup`, `/trade_buy`, `/trade_sell`, `/trade_convert`, `/trade_stats`, `/history` | D1 (trades) + KV (portfolio, symbol cache) | Daily 5PM trim | Paper trading — VN stocks with dynamic symbol resolution. Crypto/gold/forex coming soon. |
|
||||
| `wordle` | Complete | `/wordle`, `/wordle_new`, `/wordle_giveup`, `/wordle_stats` | KV (game, stats) | — | Classic 5-letter word game. 14,855-word dict sourced from [dracos's gist](https://gist.github.com/dracos/dd0668f281e685bad51479e5acaadb93). |
|
||||
| `loldle` | Complete | `/loldle`, `/loldle_new`, `/loldle_giveup`, `/loldle_stats` | KV (game, stats) | — | Classic-mode LoL champion guesser. Champion data synced from `tiennm99/loldle-data`. |
|
||||
| `misc` | Stub | `/ping`, `/mstats`, `/fortytwo` | KV | — | Health check + DB demo |
|
||||
| `wordle` | Stub | `/wordle`, `/wstats`, `/konami` | — | — | Placeholder for word game |
|
||||
| `loldle` | Stub | `/loldle`, `/ggwp` | — | — | Placeholder for LoL game |
|
||||
|
||||
## Key Data Flows
|
||||
|
||||
@@ -70,12 +70,14 @@ Each module maintains its own `README.md` with commands, data model, and impleme
|
||||
|
||||
## Test Coverage
|
||||
|
||||
105+ tests across 11+ test files:
|
||||
200 tests across 21 test files (run via `npm test` — ~2s):
|
||||
|
||||
| Area | Tests | What's Covered |
|
||||
|------|-------|---------------|
|
||||
| DB layer (KV) | 19 | KV store, prefixing, JSON helpers, pagination |
|
||||
| DB layer (D1) | — | Fake D1 in-memory implementation (fake-d1.js) |
|
||||
| DB layer (D1) | — | Fake D1 in-memory implementation (fake-d1.js) backs trading tests |
|
||||
| Module framework | 33 | Registry, dispatcher, validators, help renderer, cron validation |
|
||||
| Utilities | 4 | HTML escaping |
|
||||
| Trading module | 49 | Dynamic symbol resolution, formatters, flat portfolio CRUD, command handlers, history/retention |
|
||||
| Trading module | 79 | Symbol resolution, formatters, flat portfolio CRUD, command handlers, history/retention |
|
||||
| Loldle module | 18 | Classic-mode champion comparison, champion lookup, daily picker |
|
||||
| Wordle module | 13 | Duplicate-letter two-pass comparison, guess validation |
|
||||
@@ -0,0 +1,62 @@
|
||||
# Development Roadmap
|
||||
|
||||
Forward-looking plan for upcoming features and milestones. This document
|
||||
tracks what's **next**, not what's done — for completed work, see git log and
|
||||
`plans/*/` directories.
|
||||
|
||||
## Guiding Principles
|
||||
|
||||
- Each item lists the *future* value, not the past state.
|
||||
- Cross-module infra lands before per-module features that need it.
|
||||
- Keep items small enough to ship in one PR when possible.
|
||||
|
||||
## Modules
|
||||
|
||||
### Wordle
|
||||
|
||||
- **Daily mode.** Wire up `pickDaily(words, todayUtc())` so `/wordle` defaults
|
||||
to the shared puzzle of the day (one target per UTC date for every player).
|
||||
Give per-day stats a dedicated key so historical streaks aren't lost when
|
||||
random mode and daily mode are mixed. `pickDaily` already exists in
|
||||
`src/modules/wordle/daily.js` and has tests; the wiring in `handlers.js` is
|
||||
the missing piece.
|
||||
- Decide: should random-mode `/wordle_new` still work alongside daily, or
|
||||
should the flow be "one puzzle per day, no reroll"?
|
||||
|
||||
### Loldle
|
||||
|
||||
- **Daily mode.** Same shape as wordle's daily-mode plan — use `pickDaily`
|
||||
from `src/modules/loldle/daily.js` instead of `pickRandom`.
|
||||
|
||||
### Trading
|
||||
|
||||
- **Crypto support.** Add BTC / ETH price feed + asset category. Mirror the
|
||||
stock flow: dynamic symbol resolution, cache in KV, reuse `portfolio.assets`.
|
||||
- **Gold support.** SJC / PNJ spot price feed, treated as a single asset.
|
||||
- **Currency exchange.** `/trade_convert VND USD 1000000` — already scaffolded
|
||||
as "coming soon". Needs a forex buy/sell step that debits one currency and
|
||||
credits another.
|
||||
- **Leaderboard.** Cross-user ranking by realized P&L over a time window.
|
||||
D1 query on `trading_trades` plus portfolio snapshots.
|
||||
|
||||
## Infrastructure
|
||||
|
||||
- **Shared `picker` util.** Extract the duplicated `todayUtc` / `pickDaily` /
|
||||
`pickRandom` / djb2 hash from `loldle/daily.js` and `wordle/daily.js` into a
|
||||
single `src/util/picker.js`. Consolidate tests.
|
||||
- **Handler-level tests** for wordle + loldle mirroring
|
||||
`tests/modules/trading/handlers.test.js` (subject resolution, giveup flow,
|
||||
stats rendering, finished-round branch).
|
||||
- **Coverage reporting.** Add vitest `--coverage` config and wire a threshold
|
||||
gate into `npm test`.
|
||||
- **Staging environment.** Separate D1 database + KV namespace for a staging
|
||||
Worker, so migrations can be validated before prod.
|
||||
|
||||
## Unresolved Questions
|
||||
|
||||
- Daily-mode scope: global puzzle (everyone gets the same word / champion) vs
|
||||
per-chat daily (each group has its own seed)?
|
||||
- Does daily mode count toward the existing `stats:<subject>` record, or live
|
||||
in a separate `daily-stats:<subject>:<date>` namespace?
|
||||
- Do we keep `/wordle_new` and `/loldle_new` in daily mode, or hide them until
|
||||
the next UTC rollover?
|
||||
Reference in new issue
Block a user