mirror of
https://github.com/tiennm99/miti99bot.git
synced 2026-05-02 18:21:18 +00:00
08ff72985a
Telegram commands /semantle, /semantle_new, /semantle_giveup, /semantle_stats. Round starts with /random pick from hosted word2sim; each guess scored via /similarity. Unlimited guesses; solve on case-insensitive exact match. New env var WORD2SIM_API_URL (wrangler.toml, .env.deploy). Includes module README and 90 unit tests covering api-client, state, format, render, and handlers.
4.4 KiB
4.4 KiB
Phase 1 — Foundation
Module scaffold, KV state, word2sim HTTP client, env wiring. Nothing playable at the end of this phase, but all glue is in place.
Context links
- Overview:
./plan.md - Existing parallel module:
src/modules/loldle/(follow shape) - External API contract:
tiennm99/word2simrepoREADME.mdGET /random?min_rank&max_rank&alpha_only&min_len&max_len→{word, rank}GET /similarity?a&b→{a, b, canonical_a, canonical_b, in_vocab_a, in_vocab_b, similarity}
Files to create
src/modules/semantle/index.js (~45 LOC)
Module export. Mirrors src/modules/loldle/index.js:
- Captures
{db, env}ininit({db, env})—envis new vs loldle (needWORD2SIM_API_URL). - Exposes 5 commands (see
plan.md). Each handler closure gets(ctx, { db, apiBase }).
src/modules/semantle/api-client.js (~80 LOC)
Thin wrapper over word2sim HTTP endpoints. Example shape:
export function createClient(apiBase) {
return {
randomWord: (opts) => fetchJson(`${apiBase}/random`, opts),
similarity: (a, b) => fetchJson(`${apiBase}/similarity`, { a, b }),
};
}
- Normalize
apiBaseto strip trailing slash. - Build query strings with
URLSearchParams. - Timeout via
AbortController(5s). - Throw
Word2SimErrorwith{status, body}on non-2xx; caller decides user-facing message. User-Agent: miti99bot/semantleheader for traceability.
src/modules/semantle/state.js (~100 LOC)
KV persistence. Key layout under semantle: prefix:
game:<subject>→{target, startedAt, solved, guesses:[{word, canonical, similarity}]}—targetstored lowercased; solve =canonical.toLowerCase() === target.stats:<subject>→{played, solved, totalGuesses, bestGuessCount, lastResultAt}
Exports:
loadGame(db, subject) → GameState | nullsaveGame(db, subject, state)— TTL60*60*24*7(7d)clearGame(db, subject)loadStats(db, subject) → Stats(returns defaults if missing)recordResult(db, subject, {solved, guessCount})- Increments
played,solved(if solved),totalGuesses += guessCount. bestGuessCount = min(bestGuessCount ?? ∞, guessCount)on solved.- Writes
lastResultAt = Date.now().
- Increments
Files to edit
src/modules/index.js
Add one line to the static import map, alphabetically after misc:
semantle: () => import("./semantle/index.js"),
wrangler.toml
- In
[vars]appendsemantletoMODULES. - Add
WORD2SIM_API_URL = "https://word2sim.sg.miti99.com"to[vars].
.dev.vars.example
Add optional override:
# Optional: override for local/self-hosted word2sim instance
# WORD2SIM_API_URL=http://localhost:8000
Implementation steps
- Create folder + empty stubs for all files.
- Wire
src/modules/index.jsentry. - Wire
wrangler.tomlvars; confirmnpm run devboots without error. - Implement
api-client.js; ad-hoc test withwrangler dev+ curl to ensure the hosted instance responds. - Implement
state.js. index.jswith placeholder handlers that return "not implemented yet".
Todo
src/modules/semantle/folder + empty files- Register in
src/modules/index.js - Update
wrangler.tomlMODULES+WORD2SIM_API_URL - Update
.dev.vars.examplewith optional override comment - Implement
api-client.js(2 methods +Word2SimError+ timeout) - Implement
state.js(load/save/clear + stats + recordResult) - Placeholder
index.jsexport + noop handlers npm run devboots without errors;/semantlereply shows "not implemented"
Success criteria
- Dev server starts with
semantlelisted in modules. - Placeholder
/semantlecommand responds in Telegram (polling via dev webhook or logs). api-client.jscallable from a node REPL or test file against the live service.- No biome/eslint warnings.
Risk
- Cloudflare Worker egress to word2sim — ensure
fetch()to the SG subdomain is not blocked by any Worker networking policy. Expected fine; same pattern astrading/prices.jsandlolschedule/api-client.js. - KV size per game — just target + guess history (a few KB even after hundreds of guesses). Well under KV value limit.
Security
- No secrets added;
WORD2SIM_API_URLis a public endpoint. - All user input goes into URL query params — rely on
URLSearchParamsencoding to avoid injection; never concatenate user strings into URLs directly.
Next
→ Phase 2 phase-02-gameplay.md — real handlers and rendering.