Commit Graph
80 Commits
Author SHA1 Message Date
tiennm99 85579e56ef fix(deploy): pass BotOwnerID and AdminUserIDs from samconfig.toml in CI
CI's --parameter-overrides replaces (not merges with) samconfig.toml's
parameter_overrides, so BOT_OWNER_ID was empty in the deployed Lambda.
deploynotify silently skipped sending the post-deploy DM, and all
Protected/Private commands were denied.

Extract the values from samconfig.toml at deploy time so the file
remains the single source of truth for non-secret deploy params.

Also add 'stats' to samconfig.toml's ModulesCSV so local `sam deploy`
doesn't regress the stats module.
2026-05-22 15:39:42 +07:00
tiennm99 db8ee9c49b fix(stats): unblock deploy and register /stats in Telegram command menu
- Remove ReservedConcurrentExecutions: 1 (account lacks unreserved
  concurrency headroom; deploy rolled back). Accept best-effort
  counter semantics as documented.
- Add /stats to aws/telegram-commands.json so it appears in the
  Telegram client's / autocomplete after the next deploy.
2026-05-22 15:30:28 +07:00
tiennm99 a422913972 fix(dispatcher): place nolint:gosec on go statement, not inside goroutine 2026-05-22 15:14:07 +07:00
tiennm99 a1ed521c0e fix(stats): resolve golangci-lint G118 and QF1001 violations 2026-05-22 15:10:26 +07:00
tiennm99 3f1f264e4a feat(stats): add command usage statistics module with persistence
Implement a new stats module for the Telegram bot that tracks per-command usage with persistent KV storage. The module provides a /stats command displaying usage sorted by popularity with a 4096-byte Telegram message cap. Includes CommandHook integration for post-dispatch tracking via background goroutine (2s bounded context), proper test coverage, and registry initialization. Updated server config with stats factory and reserved concurrent execution control to prevent TOCTOU issues.
2026-05-22 15:06:46 +07:00
tiennm99 0475a69b89 chore(plans): add deploy-notify-owner plan (completed)
Plan + 2 phase files for the deploynotify feature. Both phases
marked completed.
2026-05-22 11:40:34 +07:00
tiennm99 a34320bc77 feat(deploynotify): DM owner once per new deployed git SHA
New internal/deploynotify package fires a single Telegram DM to
BOT_OWNER_ID on the first cold start that observes a new gitSHA.
Dedup via a DynamoDB KV record so repeat cold starts of the same
version stay silent. Send-then-write order means a failed Telegram
call doesn't permanently silence retries.

gitSHA is baked into the binary via `-ldflags "-X main.gitSHA=..."`
from Makefile; empty SHA (non-make builds) silently disables the
feature. No new env vars or IAM permissions.
2026-05-22 11:40:25 +07:00
tiennm99 33d88b97cc fix(lolschedule): paginate older pages to cover past days in week range
The default lolesports schedule page is anchored near "now", so a
calendar-aligned /lolschedule_week issued midweek was silently dropping
events from Mon–Wed: we only walked pages.newer. fetchSchedulePage now
also returns pages.older, and fetchEventsInRange walks older until the
earliest collected event is ≤ from, then walks newer until ≥ to. Page
budget bumped 3 → 8 to accommodate dense regular-season weeks.
2026-05-22 11:03:28 +07:00
tiennm99 3b05dc9fa8 fix(lolschedule): align /lolschedule_week to ICT calendar week (Mon–Sun)
Previously a rolling 7-day window from today; now snaps to the Monday
of the current ICT week so the reply matches what users mean by "this
week". Adds ictWeekStartOf helper plus week-boundary tests.
2026-05-22 10:36:53 +07:00
tiennm99 bc4e9ad4a9 chore(plans): remove completed/superseded plans and orphaned reports
Delete 7 plan dirs (6 implemented, 1 superseded) and 19 reports tied to
removed or non-existent plans. Active plans (aws-port, pre-deploy-wrapup,
cf-data-migration, trongtruonghop, iam-least-privilege, go-port-cloud-run)
and their referenced reports are preserved.
2026-05-18 17:56:59 +07:00
tiennm99 5721a1f1b0 fix(deploy): quote FlexibleTimeWindow.Mode as string (EarlyValidation)
YAML 1.1 implicit conversion: bare 'OFF' parses as boolean false.
AWS::Scheduler::Schedule.FlexibleTimeWindow.Mode requires the string
'OFF' or 'FLEXIBLE'. CFN's EarlyValidation hook rejects the boolean,
causing 'Failed to create changeset' on every deploy attempt.

Verified by manual create-change-set against the SAM-packaged
template: Mode: false -> FAILED; Mode: "OFF" -> CREATE_COMPLETE
with 4 changes (BotFunction modify, LolscheduleDailyPushSchedule add,
SchedulerExecutionRole policy modify, MonthlyBudget remove).
2026-05-18 17:26:19 +07:00
tiennm99 70b7dfc6a0 docs(security): bootstrap README reflects scoped deploy policy (F1, F2)
Step 4 now uses put-role-policy with the committed JSON instead of
attaching 10x FullAccess managed policies.

New subsections:
- Updating the deploy policy: edit JSON, apply out-of-band, commit,
  with jq -S structural-diff drift check (AWS normalizes JSON
  server-side, so byte-diff false-positives).
- Trust policy invariants: procedure to add a sub claim safely, and
  documented rationale for why pull_request is excluded.

Section 7 bullet 3 (replace broad policies) marked done.

Plan: plans/260518-1019-iam-least-privilege/phase-05-update-bootstrap-docs.md
2026-05-18 16:57:25 +07:00
tiennm99 dc8d77b787 feat(security): scoped IAM inline policy + rollback script for github-deploy-miti99bot (F1)
Replaces the 10x *FullAccess managed policies with a single
stack-scoped inline policy (miti99bot-deploy) on the role.

Policy boundaries:
- All resource ARNs scoped to miti99bot* (covers future miti99bot-dev)
- iam:PassRole conditioned on iam:PassedToService = lambda + scheduler
- iam:UpdateAssumeRolePolicy excluded (no trust-rewrite escalation)
- iam:AttachRolePolicy excluded (SAM uses inline PutRolePolicy)
- Wildcards limited to actions with no resource-level support
  (sts:GetCallerIdentity, s3:ListAllMyBuckets,
  cloudformation:ListStacks, cloudformation:ValidateTemplate)

Rollback: aws/iam-rollback-fullaccess.sh re-attaches all 10
FullAccess policies with retry-on-throttle + final verification.

Apply via Phase 4 two-stage cutover (dual-attach trial then detach).
Policy is committed but NOT yet attached -- Phase 4 applies it.

Plan: plans/260518-1019-iam-least-privilege/phase-03-draft-custom-policy.md
Audit: plans/reports/code-reviewer-260518-1019-security-aws-infra.md
2026-05-18 16:57:15 +07:00
tiennm99 148388e5a2 fix(security): narrow OIDC trust to main only (F2)
Drop refs/heads/dev and pull_request from the sub allowlist on
github-deploy-miti99bot trust policy. Only pushes to main can now
assume the deploy role.

dev was dormant (ci.yml has only contents:read; deploy.yml triggers
on push:main only). pull_request was dormant too (no workflow has
id-token:write under PR trigger). This closes the latent F2 path.

Plan: plans/260518-1019-iam-least-privilege/phase-01-narrow-oidc-trust-f2.md
Audit: plans/reports/code-reviewer-260518-1019-security-aws-infra.md
2026-05-18 16:57:04 +07:00
tiennm99 efe79be9ac 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 2d1864f1e0 refactor(deploy): swap cron to Scheduler + synthetic HTTP event (free tier only)
Supersedes c726c0a (never deployed). EventBridge ApiDestination has no
free tier — $0.20/M, rounds to $0 at our 30 invokes/month but still a
billable line item, which violates the project's hard free-tier rule.

Replace 4 resources with 2:
  - drop: CronConnection, LolscheduleDailyPushApiDestination,
          EventBridgeInvokeRole, LolscheduleDailyPushRule
  - add:  SchedulerExecutionRole, LolscheduleDailyPushSchedule

Schedule fires cron(0 1 * * ? *) at Lambda directly (free tier:
14M invocations/mo). Target.Input carries a synthetic Function URL v2
event so Lambda Web Adapter proxies it to /cron/lolschedule_daily_push
unchanged. The X-Cron-Token header is interpolated from the NoEcho
CronSharedSecret parameter.

Accepted trade-off: token value is visible to anyone with
scheduler:GetSchedule on the schedule. IAM gates that; the project's
security policy treats secret-in-Scheduler-Input as acceptable.
2026-05-18 15:36:59 +07:00
tiennm99 c726c0aca5 feat(deploy): wire lolschedule cron via EventBridge Rule + ApiDestination
CloudFormation's AWS::Scheduler::Schedule Target schema has no property
for HTTPS universal invocation (URL, method, headers) — confirmed
against AWS docs. Switch to the legacy EventBridge Rule path which
supports HTTP targets natively via ApiDestination:

- AWS::Events::Connection: API_KEY auth, presents X-Cron-Token header.
  ApiKeyValue stored in EventBridge service-linked secret on stack
  update (no per-invoke SSM fetch, AWS-managed secret fees).
- AWS::Events::ApiDestination: POST to ${FunctionUrl}cron/lolschedule_daily_push.
- AWS::Events::Rule: cron(0 1 * * ? *) — daily 01:00 UTC / 08:00 ICT.
  Targets ApiDestination with retry x2, 600s max age, DLQ to CronDLQ.
- EventBridgeInvokeRole replaces SchedulerExecutionRole (events.amazonaws.com
  principal, events:InvokeApiDestination scoped to this destination only).

NoEcho CronSharedSecret CFN parameter restored; GHA fetches the SSM
SecureString and passes via --parameter-overrides so the value never
appears in template source or stack events.

Free-tier preserved: 1 invocation/day, well under EventBridge Rules +
ApiDestinations free quotas.
2026-05-18 15:14:06 +07:00
tiennm99 fa6f22987e revert(deploy): roll back EventBridge HTTPS schedule attempts
Reverts 585d996 + c70b9d0. Both CFN deploys failed at changeset
validation; prod stack was never mutated. Approach A
(AWS::Scheduler::Schedule with arn:aws:scheduler:::http-invoke target)
is unimplementable in pure CloudFormation — the Target schema has no
property for HTTP endpoint/method/headers, regardless of name.
Replacement landing in a follow-up commit.

Restored: plans/reports/brainstorm-260517-1411-eventbridge-schedule-fix.md
(useful design context even though Approach A invalidated).
2026-05-18 15:11:18 +07:00
tiennm99 585d99633f fix(deploy): pass cron secret via CFN parameter, not ssm-secure resolve
CloudFormation rejects {{resolve:ssm-secure}} inside
AWS::Scheduler::Schedule HttpInvokeArgs.HeaderParameters (documented
property-allowlist limitation). Add a NoEcho CronSharedSecret parameter
and fetch the SSM SecureString in CI, then pass via --parameter-overrides.
Lambda runtime SSM-fetch flow unchanged.
2026-05-18 13:04:30 +07:00
tiennm99 c70b9d0c24 feat(deploy): wire EventBridge schedule for lolschedule daily push
Adds AWS::Scheduler::Schedule resource invoking the Function URL via
HTTPS universal target (arn:aws:scheduler:::http-invoke) at 01:00 UTC
daily. Token sourced from SSM via {{resolve:ssm-secure}}; failures
retried twice and DLQ'd to existing CronDLQ. Closes the deferred
phase-05 of 260510-0234-pre-deploy-wrapup.
2026-05-18 11:46:50 +07:00
tiennm99 8e7fdce06c feat(misc): add /trongtruonghop disclaimer command
Vietnamese disclaimer one-liner. Interpolates a target name (default "VNG")
and the sender — @username when set, else tg://user?id link with the display
name. User-supplied text HTML-escaped; reply routed through ReplyHTML to keep
forum-topic threading.
2026-05-16 14:51:26 +07:00
tiennm99 3402ca9285 refactor(plans): code-review audit reports — modules + infra
Captures the two parallel code-reviewer reports that drove the
audit-driven hygiene pass in the previous commit. Kept for future
reviewers and as the trail of which findings were applied vs.
deferred as cosmetic.
2026-05-16 13:35:11 +07:00
tiennm99 a8ed67a0a3 refactor: audit-driven hygiene pass across modules and infra
Concurrency
- lolschedule: serialize subscriber Get→mutate→Put via state.subscribersMu;
  the single-slot list was previously losing writes under concurrent
  /lolschedule_subscribe.
- trading: PriceClient memoises its default *http.Client so /trade_stats
  reuses TLS connections across held tickers.

Observability
- server/log_middleware: defer the req log line and recover panics so a
  panicking cron handler still emits the structured req entry CloudWatch
  filters on for 5xx alerting.
- server/router (cron): inner recover with cron-name context captures the
  panicking job before the middleware's safety net does.
- telegram/webhook: rune-safe truncation in dispatch logs — Vietnamese,
  Korean, and emoji previews no longer ship as garbled bytes.
- lolschedule/api_client: same rune-safe fix for error-body log truncation.
- telegram/webhook: gate the post-recover WriteHeader(200) so a panicking
  handler that already touched w doesn't trigger superfluous-WriteHeader.

Correctness
- twentyq: clearGame error during solved-relaunch is logged instead of
  silently swallowed (was a permanent deadlock vector on KV failure).
- misc /mstats: KV read failure replies "Could not load stats. Try again
  later." to the user instead of returning into the dispatcher; matches the
  pattern other modules use.
- migrate_cf_data trading-audit-dump: surface f.Close error so a truncated
  JSONL never passes silently as a complete audit dump.

Operator ergonomics
- migrate_cf_data (all 4 subcommands): signal.NotifyContext for SIGINT /
  SIGTERM. Ctrl-C mid-Scan now propagates cleanly instead of leaving a
  half-converted DynamoDB table.
- ai/ratelimit: doc the Lambda-recycle memory bound to match keylock.Map
  so a future reviewer doesn't re-flag the unbounded map.

I/O-changing (user-approved)
- lolschedule daily push auto-prunes subscribers whose Telegram error
  matches a terminal marker (blocked / deactivated / chat gone). Transient
  errors keep the chat on the list. Subscribe message updated to mention
  the auto-cleanup.
- twentyq seed pool grown 50 → 178; repeat-collision threshold moves from
  ~9 plays to ~17 (birthday paradox).
- util /info flipped Public → Protected — chat/thread/sender IDs are no
  longer enumerable by every group member.
- cmd/server WriteTimeout 6min → 75s (cron 60s + 15s slack). No-op on
  Lambda; matters only for local non-Lambda runs.
- webhook + cron rejection paths drop response bodies (no fingerprintable
  text for internet scanners hitting the public Function URL). Status
  codes preserved for CloudWatch metrics; structured log lines carry the
  rejection reason for operator triage.

Tests added: TestTruncateRunes, TestRunDailyPush_PrunesDeadSubscribers,
TestIsTerminalSendError, TestInfo_DeniedToNonOwner,
TestInfo_DeniedToChannelMessageNoFrom, plus owner-allowed counterparts.
2026-05-16 13:35:00 +07:00
tiennm99 3a12615ef5 fix(reply): forward message_thread_id so replies stay in the same forum topic
Telegram routes outgoing messages with no message_thread_id to a forum
supergroup's General topic. Commands sent in a topic were being answered
in General. chathelper.Reply / ReplyHTML now take *models.Message and
forward both ChatID and MessageThreadID; help.go SendMessage and
loldle trySendSticker do the same. Adds regression tests asserting the
field is forwarded for forum topics and omitted for private/regular groups.
2026-05-16 12:10:56 +07:00
tiennm99 3235ee879f ci: bump golangci-lint-action to v9 (last Node 20 straggler)
v9 is the first Node 24-native release and still accepts golangci-lint
>= v2.1.0, so the pinned v2.12.2 binary keeps working. Removes the
final Node 20 deprecation annotation from the CI run.
2026-05-16 11:56:54 +07:00
tiennm99 1e794d12fa ci: bump actions to Node 24-native versions
GitHub forces Node 20 actions to run on Node 24 starting 2026-06-02
and removes Node 20 entirely on 2026-09-16. Bump every action that
emitted the deprecation warning to the latest major that natively
declares runs.using: node24:

  actions/checkout                 v4 -> v6
  actions/setup-go                 v5 -> v6
  aws-actions/setup-sam            v2 -> v3
  aws-actions/configure-aws-credentials v4 -> v6

Verified action.yml runtime declarations (node24) for checkout v6
and setup-go v6. configure-aws-credentials v5 changed only invalid-
boolean-input handling, which we don't pass. setup-sam v3 is solely
the Node bump. golangci-lint-action stays at v7 (already Node 24,
never appeared in the deprecation warning).
2026-05-16 11:53:01 +07:00
tiennm99 4c81dd61ae fix(dispatcher): match /cmd@botname so commands work in groups
The go-telegram/bot v1.20.0 MatchTypeCommand does byte-exact equality
on the full bot_command entity, which in groups is "cmd@botname"
(length 15 for /help@miti99bot) — never equal to the registered name
"cmd". All 27 commands silently missed when used in group chats.

Swap RegisterHandler(..., MatchTypeCommand, ...) for a local matchFunc
that strips the @suffix before comparing. Telegram only routes
/cmd@otherbot to otherbot, so the suffix is safe to drop
unconditionally.

Add a structured `dispatch` log line in the webhook (update_id,
chat_id, chat_type, text preview) so the next silent-drop symptom
shows up in CloudWatch without code archaeology — the existing
[TGBOT] [UPDATE] line only prints struct pointers.

12 new TestMatchCommand sub-tests cover DM, group, mid-text, wrong
command, non-command entity, and bounds edge cases.
2026-05-16 11:36:11 +07:00
tiennm99 df8943155c fix(migration): satisfy errcheck on Close + Fprint* in migration toolchain
Pre-existing 9 errcheck violations introduced by d67517e blocked
golangci-lint on every CI run after the migration toolchain landed.

- Wrap defer Close() in anonymous func to match house style
  (cf. internal/modules/lolschedule/api_client.go:162).
- Mark Fprint*/Fprintln to io.Writer with _, _ = (best-effort writes
  to os.Stdout / bytes.Buffer; errors not actionable for callers).

No behavior change. go vet, golangci-lint, go test ./... all clean
locally.
2026-05-16 11:02:15 +07:00
tiennm99 1f5f304041 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 39491d134e 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 d67517e1fb feat(migration): cf→aws migration toolchain + DynamoDB value as String
Operator-run tooling that moves durable Cloudflare KV data into the live
AWS DynamoDB table, plus a runtime swap of the `value` attribute from
Binary to String so payloads are human-readable in the AWS console.

- cmd/migrate_cf_data: subcommands inventory, kv-import (idempotent via
  attribute_not_exists), trading-audit-dump, convert-value-to-string
- internal/migration: policy allowlist, CF KV+D1 REST clients, DynamoDB
  writer, report formatter with per-prefix counts + tests
- internal/storage/dynamodb_kv.go: Put writes MemberS, Get reads MemberS;
  dropped empty-bytes sentinel (DynamoDB allows empty strings)
2026-05-16 10:33:35 +07:00
tiennm99 75e936096d 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 0502ea28d2 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 dbfee38232 Fix AWS Lambda deploy setup 2026-05-15 22:18:27 +07:00
tiennm99 9c9bc65db8 fix(ci): clear sam lint + golangci-lint findings
- template.yaml: drop redundant DependsOn (W3005) — Ref in LoggingConfig
  already implies BotFunctionLogGroup ordering.
- dynamodb_kv.go: remove unused errIsTableMissing and orphaned errors import.
- trading/prices.go: wrap deferred resp.Body.Close to satisfy errcheck.
2026-05-14 09:57:12 +07:00
tiennm99 f3b9891a54 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 f632c68956 refactor(modules): drop loldle variants, semantle, doantu and dead framework surface
Removes six modules (loldle-ability/emoji/quote/splash, semantle, doantu)
and prunes the framework deps that were only there to serve them:

  - ai.Embedder + Client.Embed + embeddingModel const (semantle only)
  - Deps.Embedder + BuildOptions.Embedder
  - Deps.Env + Build(env) param + ModuleEnv config field + PHOW2SIM allowlist (doantu only)
  - internal/champname package (loldle now owns its lookup helpers directly)
  - template.yaml: Phow2simAPIURL parameter + PHOW2SIM_API_URL Lambda env

Active catalog: util, misc, wordle, loldle, lolschedule, twentyq, trading.

go build / vet / test all pass.
2026-05-11 16:02:06 +07:00
tiennm99 cd8c0df84b chore: gofmt dynamodb_kv.go 2026-05-10 03:06:27 +07:00
tiennm99 396e3d6808 feat(deploy): cold-start metric filter + trading module in SAM template
- Add AWS::Logs::MetricFilter for Lambda cold-start detection
- Wire trading module into template.yaml MODULES default list
2026-05-10 03:06:16 +07:00
tiennm99 0660960d35 feat(trading): VN stocks paper trading module — 5 commands + price engine
- Portfolio, holdings, prices, symbols packages with Telegram command handlers
- Handlers: trade_topup (add credits), trade_buy, trade_sell, trade_convert (coming-soon), trade_stats
- Per-user keylock for concurrency safety
- KBS (VN) as price source; ticker validation regex ^[A-Z0-9]{1,16}$
- Scope-trimmed: no SQL history, no retention cron, no daily refresh, no leaderboard, no forex
  (matches upstream miti99bot's current VN-stocks-only state)
- Full unit test coverage for format, portfolio, prices, symbols, handlers
2026-05-10 03:06:13 +07:00
tiennm99 af0641ba93 feat(lolschedule): daily-push cron handler + comprehensive tests
- Add cron.go: DailyPushCron handler (1 AM UTC, throttle >30 subs, partial-failure tolerance)
- messageSender interface for testability
- Add cron_test.go: 5 tests covering happy path, empty subs, throttle, net errors, partial failures
- Register cron in lolschedule.Crons() via module interface
2026-05-10 03:06:07 +07:00
tiennm99 5697dd8293 feat(modules): add Bot to Deps + BuildOptions for cron handlers
- Extend module.Deps struct with optional Bot field
- Add Bot to registry.BuildOptions and thread through builders
- Pass bot instance from main.go into module factory options
- Enables cron handlers to send messages and access bot state
2026-05-10 03:06:01 +07:00
tiennm99 cb8c8840a8 docs(plans): pre-deploy wrap-up status + AWS-port phase sync + code-review report
- Rewrite README.md for AWS-default deploy architecture
- Sync AWS-port plan.md phases 01-07 statuses to reflect shipped code (trading, lolschedule, gemini modules)
- Include code-reviewer report: cron-and-trading feedback addressed (gofmt, price-fetch reordering, senderInfo validation, ticker regex, typo fixes)
2026-05-10 03:05:56 +07:00
tiennm99 f525627d7c feat(modules): add Gemini AI integration module 2026-05-10 02:30:04 +07:00
tiennm99 ce99633e25 feat(modules): port new command modules + update registry
- Add new modules: doantu, semantle, twentyq, ai (Gemini integration)
- Update module registry with new command registration
- Update tests and documentation for module system
- Update README with new module references
2026-05-10 02:29:58 +07:00
tiennm99 fb553cfe93 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 c07d764aa2 feat(deploy): AWS SAM template + Makefile + GitHub Actions
- AWS SAM CloudFormation template for Lambda + DynamoDB + EventBridge
- SAM config for us-east-1 deployment with guided parameters
- Unified Makefile: build-lambda, dynamodb-local, sam-* targets
- GitHub Actions: OIDC trust + SAM deploy on push to main
- CI job: add iac stage (sam validate)
- .gitignore: build/, bin/, .aws-sam/, samconfig.local.toml
2026-05-10 02:29:49 +07:00
tiennm99 070894444e feat(server): wire KV_PROVIDER env selection (memory|firestore|dynamodb)
- Add environment-based provider selection in main.go
- Support memory (test), Firestore (GCP), and DynamoDB (AWS) backends
2026-05-10 02:29:43 +07:00
tiennm99 91b27783cc feat(storage): add DynamoDB KV provider sibling to Firestore
- Implement DynamoDB client wrapper and KV provider
- Full CRUD test coverage for both local and AWS modes
- AWS SDK v2 dependency added to go.mod/go.sum
2026-05-10 02:29:39 +07:00
tiennm99 29a1bface0 docs(plans): research AWS vs GCP free tier + create AWS port plan
- Research reports comparing AWS/GCP free tiers and greenfield trade-offs
- New AWS port plan with 7 phases (infrastructure, deployment, integration)
- Mark GCP plan deploy phases as superseded by new AWS plan
2026-05-10 02:29:35 +07:00