mirror of
https://github.com/tiennm99/miti99bot.git
synced 2026-10-03 07:13:24 +00:00
docs: correct README, deploy guide and module docs against current code
This commit is contained in:
1 parent
97aaba83ec
commit
d51bcbd224
8 files changed
+124
-95
No files matched your search
+1
-1
@@ -15,7 +15,7 @@ MONGO_DATABASE=miti99bot
|
||||
# Comma-separated module list. Empty = load every module, including any module
|
||||
# added later — so list them explicitly when a deployment should only gain a new
|
||||
# module deliberately.
|
||||
MODULES=util,misc,amlich,wordle,loldle,lol,stock,gold,coin,stats,monkeyd,sticker,alias
|
||||
MODULES=util,misc,amlich,wordle,loldle,lol,stock,gold,coin,stats,monkeyd,sticker,alias,blacklist
|
||||
# Telegram user id for owner-only commands (renamed from BOT_OWNER_ID).
|
||||
OWNER_ID=
|
||||
# Comma-separated admin Telegram user ids (renamed from ADMIN_USER_IDS).
|
||||
|
||||
@@ -4,7 +4,8 @@
|
||||
|
||||
`miti99bot` is a Go Telegram bot with pluggable modules under
|
||||
`internal/modules`. Runtime storage is MongoDB when `MONGO_URL` is set and
|
||||
in-memory storage in tests. Read `README.md` before implementation work.
|
||||
in-memory otherwise, which is what tests and local no-database runs use. Read
|
||||
`README.md` before implementation work.
|
||||
|
||||
`third_party/monkeyd-crawler` is a git submodule resolved through a `go.mod`
|
||||
`replace` directive, not a versioned dependency. Go commands fail until it is
|
||||
@@ -33,7 +34,8 @@ deleting commands, update all related surfaces:
|
||||
- command parameter metadata used by Telegram and `/help`
|
||||
- handler usage text and user-facing error text
|
||||
- tests for registration, handlers, and command menu behavior
|
||||
- README/docs when behavior changes are user-visible
|
||||
- the README module table, plus the feature doc under `docs/` when one exists,
|
||||
when behavior changes are user-visible
|
||||
|
||||
Follow `docs/command-parameter-conventions.md` for all command parameter
|
||||
metadata and usage text. Keep metadata, usage errors, examples, and tests exact.
|
||||
|
||||
@@ -7,22 +7,27 @@ Atlas via long polling and an in-process cron scheduler.
|
||||
|
||||
| Module | What it does |
|
||||
|---|---|
|
||||
| `util` | `/help`, `/info`, `/stickerid` |
|
||||
| `misc` | `/ping`, `/ping_stats`, `/random`, `/wheelofnames`, `/ff`, `/xlt1`, `/giaxang`, `/the_answer`, `/trongtruonghop` + `/tth`, `/trongtruonghopvng` + `/tthvng` disclaimers |
|
||||
| `util` | `/help`, `/info` (admin), `/stickerid` (owner) |
|
||||
| `misc` | `/ping`, `/ping_stats` (admin), `/random`, `/wheelofnames`, `/ff` (admin), `/xlt1`, `/giaxang` (Petrolimex retail fuel prices), `/the_answer` (owner), `/trongtruonghop` + `/tth`, `/trongtruonghopvng` + `/tthvng` disclaimers |
|
||||
| `amlich` | Vietnamese lunar calendar: `/amlich` (dương lịch → âm lịch, defaults to today), `/duonglich` (âm lịch → dương lịch, `nhuan` flag for leap months); dates accept `d`, `d/m`, or `d/m/yyyy` — missing parts fill from today in the input's calendar. Years 1800–2199 only |
|
||||
| `wordle` | Daily Wordle game |
|
||||
| `loldle` | League-of-Legends "guess the champion" |
|
||||
| `lol` | Pro-match schedule (`/lol`, `/lol_tomorrow`, `/lol_this_week`, `/lol_next_week`), per-chat digest opt-in (`/lol_subscribe`, `/lol_unsubscribe`) + daily push at 08:00 ICT |
|
||||
| `stock` | VN-stocks paper trading |
|
||||
| `gold` | Gold paper trading (VNAppMob SJC buy/sell VND/luong) |
|
||||
| `coin` | Crypto paper trading in USD (Binance -> Coinbase -> CoinGecko price fallback) |
|
||||
| `wordle` | Daily Wordle game: `/wordle [word]`, `/wordle_new`, `/wordle_giveup`, `/wordle_stats` |
|
||||
| `loldle` | League-of-Legends "guess the champion": `/loldle [champion]`, `/loldle_giveup`, `/loldle_stats`, `/loldle_setmax` (owner) |
|
||||
| `lol` | Pro-match schedule (`/lol [date]`, `/lol_tomorrow`, `/lol_this_week`, `/lol_next_week`), per-chat digest opt-in (`/lol_subscribe`, `/lol_unsubscribe`) + daily push at 08:00 ICT |
|
||||
| `stock` | VN-stocks paper trading: `/stock_price`, `/stock_info`, `/stock_events`, `/stock_topup`, `/stock_buy`, `/stock_sell`, `/stock_cash_dividend`, `/stock_share_dividend`, `/stock_portfolio` |
|
||||
| `gold` | Gold paper trading (VNAppMob SJC buy/sell VND/luong): `/gold_price`, `/gold_topup`, `/gold_buy`, `/gold_sell`, `/gold_portfolio` |
|
||||
| `coin` | Crypto paper trading in USD (Binance -> Coinbase -> CoinGecko price fallback): `/coin_price`, `/coin_topup`, `/coin_buy`, `/coin_sell`, `/coin_portfolio` |
|
||||
| `stats` | `/stats` (top commands), `/stats users`, `/stats user <username>`, `/stats cmd <command_name>` |
|
||||
| `sticker` | `/addsticker` — append a replied sticker, image, video or GIF to one shared pack. See [docs/sticker-packs.md](docs/sticker-packs.md) |
|
||||
| `alias` | `/alias <name>` save a replied message under a name, then send it back with `/insert <name>`, bare `/<name>`, or inline `@botname <prefix>`; `/aliases` lists, `/unalias` deletes. See [docs/aliases.md](docs/aliases.md) |
|
||||
| `blacklist` | Per-topic text deny-list with whitelist exceptions: `/blacklist_add`, `/blacklist_del`, `/whitelist_add`, `/whitelist_del`, `/blacklist_rules` lists both, `/blacklist_check` judges a text. Passive — the bot never scans chat. See [docs/blacklist.md](docs/blacklist.md) |
|
||||
| `blacklist` | Per-topic text deny-list with whitelist exceptions: `/blacklist_add`, `/blacklist_del`, `/whitelist_add`, `/whitelist_del`, `/blacklist_rules` lists both, `/blacklist_check` judges a text, `/blacklist` does either, `/whitelist_rnd` picks a random exception. Passive — the bot never scans chat. See [docs/blacklist.md](docs/blacklist.md) |
|
||||
| `monkeyd` | `/monkeyd_crawl <url> [font_size]` export a monkeydd.com novel as a PDF, `/monkeyd_tags <url>` list its tags as hashtags |
|
||||
|
||||
Disable modules with the `MODULES` environment variable.
|
||||
Commands marked (admin) require a user ID in `ADMIN_IDS` or the owner; commands
|
||||
marked (owner) require `OWNER_ID`. Both kinds are hidden from `/help` and the
|
||||
Telegram command menu. Every other command is public.
|
||||
|
||||
Choose modules with the `MODULES` environment variable, a comma-separated list;
|
||||
empty loads every module.
|
||||
|
||||
## Command discovery
|
||||
|
||||
@@ -42,6 +47,8 @@ Future commands must follow the
|
||||
[command parameter conventions](docs/command-parameter-conventions.md). Keep
|
||||
command metadata, handler usage text, tests, and documentation aligned.
|
||||
|
||||
## Feature notes
|
||||
|
||||
### Lunar calendar accuracy
|
||||
|
||||
`/amlich` and `/duonglich` port Hồ Ngọc Đức's truncated-Meeus algorithm, the
|
||||
@@ -220,17 +227,26 @@ all.
|
||||
cmd/server/ entrypoint (long polling + in-process cron + HTTP health)
|
||||
internal/server/ HTTP route (/ health only; cron has no HTTP route)
|
||||
internal/telegram/ Telegram long-polling bot wrapper
|
||||
internal/cron/ in-process cron scheduler
|
||||
internal/cron/ in-process cron scheduler (UTC)
|
||||
internal/deploynotify/ startup DM to the owner with the deployed commit
|
||||
internal/modules/ Module framework, registry, dispatchers, modules
|
||||
internal/storage/ typed DocStore[T] (Provider + Typed); mongodb runtime + memory (tests). Values persist as flattened native BSON root documents
|
||||
internal/systemstate/ shared `system` collection helper for startup migration records
|
||||
internal/log/, metrics/ JSON logging (LOG_LEVEL) and periodic metrics flush
|
||||
third_party/monkeyd-crawler/ git submodule; resolved by a go.mod replace directive
|
||||
compose.yml Coolify self-host stack (single bot service)
|
||||
docs/deploy-coolify-selfhosted.md Self-host deploy and operations guide
|
||||
docs/command-parameter-conventions.md Command parameter syntax rules
|
||||
docs/amlich-known-issues.md Lunar algorithm decision and known edge cases
|
||||
```
|
||||
|
||||
## Documentation
|
||||
|
||||
- [Self-host deploy and operations](docs/deploy-coolify-selfhosted.md), including the full environment variable list
|
||||
- [Command parameter conventions](docs/command-parameter-conventions.md)
|
||||
- [Aliases](docs/aliases.md)
|
||||
- [Blacklist](docs/blacklist.md)
|
||||
- [Sticker pack](docs/sticker-packs.md)
|
||||
- [Lunar calendar algorithm and known issues](docs/amlich-known-issues.md)
|
||||
- [Agent and contributor rules](AGENTS.md)
|
||||
|
||||
## Run locally
|
||||
|
||||
Clone with submodules — the `monkeyd` module builds against
|
||||
@@ -270,8 +286,9 @@ dev bot is created manually; its token is injected through the environment.
|
||||
|
||||
The `lol` module reads its schedule from the PandaScore REST API and needs
|
||||
`LOL_PANDASCORE_TOKEN` (free tier). Without it every `/lol*` fetch fails while
|
||||
the rest of the bot runs normally. See [`.env.example`](.env.example) for the
|
||||
full variable list.
|
||||
the rest of the bot runs normally. See the
|
||||
[environment table](docs/deploy-coolify-selfhosted.md#environment) for every
|
||||
variable, and [`.env.example`](.env.example) for a template.
|
||||
|
||||
Persistent MongoDB locally (auto-selected when `MONGO_URL` is set):
|
||||
|
||||
@@ -295,18 +312,21 @@ use `go test -v ./...` to see individual skip reasons.
|
||||
|
||||
```sh
|
||||
go vet ./...
|
||||
golangci-lint run
|
||||
go test -count=1 ./...
|
||||
go build ./...
|
||||
```
|
||||
|
||||
CI additionally runs the test suite with Go's race detector.
|
||||
CI runs the same steps, with the test suite under Go's race detector
|
||||
(`go test -race`), plus an informational `govulncheck` and a `docker build`.
|
||||
Lint settings live in [`.golangci.yml`](.golangci.yml).
|
||||
|
||||
## Deploy
|
||||
|
||||
[`docs/deploy-coolify-selfhosted.md`](docs/deploy-coolify-selfhosted.md) covers
|
||||
Coolify + MongoDB Atlas (free M0), long polling (no public ingress), and
|
||||
in-process cron. Storage auto-selects `mongodb` when `MONGO_URL` is set; the
|
||||
cron scheduler runs by default.
|
||||
in-process cron. Coolify redeploys on every push to `main`. Storage
|
||||
auto-selects `mongodb` when `MONGO_URL` is set; the cron scheduler always runs.
|
||||
|
||||
## License
|
||||
|
||||
|
||||
+2
-2
@@ -33,7 +33,7 @@ services:
|
||||
# Coolify network for the container health monitor against GET / only.
|
||||
expose:
|
||||
- "8080"
|
||||
# No compose healthcheck: distroless has no shell/curl and cmd/server has no
|
||||
# -healthcheck flag. Configure Coolify's HTTP monitor against GET / instead
|
||||
# No compose healthcheck: Coolify's own HTTP monitor covers it, so none is
|
||||
# defined here. Configure that monitor against GET / instead
|
||||
# (returns text/plain "miti99bot ok"). Note: a plain / check does not verify
|
||||
# Mongo connectivity — see docs/deploy-coolify-selfhosted.md.
|
||||
+3
-9
@@ -149,15 +149,9 @@ notes cannot, and Telegram's send methods for them have no caption field at all.
|
||||
|
||||
## Listing
|
||||
|
||||
`/aliases` prints the count and every name, sorted, in one message.
|
||||
|
||||
**Names only, not what each holds.** The store answers "which keys exist" in a
|
||||
single call, while naming each kind would cost one read per alias — a round trip
|
||||
each against MongoDB, on a dispatcher that serves one update at a time. To find
|
||||
out what a name holds, `/insert` it.
|
||||
|
||||
One line per alias, showing what the name holds, with the invocation in a
|
||||
`<code>` span so tapping it copies a command ready to send:
|
||||
`/aliases` prints the count and every name, sorted, in one message — one line
|
||||
per alias, showing what the name holds, with the invocation in a `<code>` span
|
||||
so tapping it copies a command ready to send:
|
||||
|
||||
```
|
||||
3 aliases:
|
||||
|
||||
@@ -15,7 +15,7 @@ remain responsible for parsing and validation.
|
||||
|---|---|---|
|
||||
| Required value | `<name>` | `<ticker>` |
|
||||
| Required comma-separated values | `<name,...>` | `<option,...>` |
|
||||
| Required remaining text | `<name...>` | `<title...>` |
|
||||
| Required remaining text | `<name...>` | `<text...>` |
|
||||
| Optional value | `[name]` | `[date]` |
|
||||
| Optional remaining text | `[name...]` | `[target...]` |
|
||||
| Alternatives in an optional group | `[literal | literal <name>]` | `[users | user <username>]` |
|
||||
@@ -41,7 +41,7 @@ language or extra punctuation without a user-facing need.
|
||||
|
||||
```text
|
||||
/stock_buy <quantity> <ticker>
|
||||
/renamepack <title...>
|
||||
/blacklist_del <text...>
|
||||
/lol [date]
|
||||
/trongtruonghop [target...]
|
||||
/stats [users | user <username> | cmd <command_name>]
|
||||
|
||||
@@ -15,31 +15,38 @@ Run `miti99bot` as a long-lived container on [Coolify](https://coolify.io) with
|
||||
|
||||
- **Storage** — `mongodb` auto-selected when `MONGO_URL` is set (no `KV_PROVIDER`).
|
||||
- **Cron** — an in-process scheduler (`internal/cron`) runs unconditionally and
|
||||
fires each module cron on its `Schedule` (UTC).
|
||||
fires each module cron on its `Schedule`, evaluated in UTC. The only cron
|
||||
today is the `lol` daily digest at `0 1 * * *` (08:00 ICT).
|
||||
- **Transport** — long polling (`b.Start`) is the **only** transport. The bot
|
||||
opens an outbound connection to Telegram and pulls updates, so there is no
|
||||
public domain, no `/webhook`, and no webhook secret. The container clears any
|
||||
leftover webhook on startup (`deleteWebhook`) before polling.
|
||||
|
||||
## Required environment
|
||||
## Environment
|
||||
|
||||
Copy [`.env.example`](../.env.example) → `.env` (gitignored) and fill in.
|
||||
|
||||
| Var | Required | Notes |
|
||||
|---|---|---|
|
||||
| `TELEGRAM_BOT_TOKEN` | ✅ | from @BotFather |
|
||||
| `TELEGRAM_BOT_TOKEN` | ✅ | from @BotFather; startup fails without it |
|
||||
| `MONGO_URL` | ✅ | Atlas SRV string **incl. credentials** — secret, never logged |
|
||||
| `MONGO_DATABASE` | ✅ | e.g. `miti99bot` |
|
||||
| `MODULES` | optional | CSV; empty = all modules |
|
||||
| `OWNER_ID` | optional | owner-only commands (renamed from `BOT_OWNER_ID`) |
|
||||
| `ADMIN_IDS` | optional | CSV of admin ids (renamed from `ADMIN_USER_IDS`) |
|
||||
| `MODULES` | optional | CSV; empty = all modules, including any added later |
|
||||
| `OWNER_ID` | optional | Telegram user id for owner-only commands, the deploy DM, and the `/addsticker` pack owner. Unset = owner-only commands are denied and `/addsticker` refuses |
|
||||
| `ADMIN_IDS` | optional | CSV of Telegram user ids for admin-only commands |
|
||||
| `STICKER_PACK_NAME` | optional | set `/addsticker` writes to; default `miti99_by_miti99bot`. See [sticker packs](sticker-packs.md) |
|
||||
| `LOL_PANDASCORE_TOKEN` | ✅ for lol module | PandaScore API token (free tier) — secret, never logged; without it every `/lol*` fetch fails (stale cache may still serve briefly) |
|
||||
| `WHEELOFNAMES_API_URL` | optional | full `/api/gif` endpoint for remote `/wheelofnames` GIF rendering |
|
||||
| `WHEELOFNAMES_API_TOKEN` | optional | bearer token matching the wheelofnames service `API_TOKEN` |
|
||||
| `LOL_PANDASCORE_TOKEN` | ✅ for lol module | PandaScore API token (free tier) — secret, never logged; without it every `/lol*` fetch fails (stale cache may still serve briefly) |
|
||||
| `LOG_LEVEL` | optional | `debug`, `info` (default), `warn`, or `error`; logs are JSON on stdout |
|
||||
| `GOLD_VNAPP_API_KEY` | leave unset | VNAppMob key; unset = the gold module fetches one and caches it in MongoDB |
|
||||
| `KV_PROVIDER` | leave unset | `memory` or `mongodb`; unset = `mongodb` when `MONGO_URL` is set, otherwise `memory` |
|
||||
| `PORT` | leave unset | health server port; default `8080` |
|
||||
| `SOURCE_COMMIT` | never set | provided by Coolify at runtime for the deploy DM (see step 6 below) |
|
||||
|
||||
**Leave UNSET on self-host:** `KV_PROVIDER`, `PORT`,
|
||||
`TELEGRAM_WEBHOOK_SECRET`, and `GOLD_VNAPP_API_KEY`. Stock, coin, and gold URL
|
||||
overrides are not supported in runtime env; modules use coded defaults.
|
||||
Stock, coin, and gold provider URL overrides are not supported in runtime env;
|
||||
modules use coded defaults. There is no `TELEGRAM_WEBHOOK_SECRET`: long polling
|
||||
has no webhook.
|
||||
|
||||
> Cron runs in-process (`internal/cron`) — there is no `/cron` HTTP route and no
|
||||
> `CRON_SHARED_SECRET`. The scheduler is the sole trigger; nothing inbound.
|
||||
@@ -86,7 +93,7 @@ reply is immediate.
|
||||
Atlas admin or cluster-wide. Use a **strong unique password**.
|
||||
3. **Network access:** add `0.0.0.0/0`.
|
||||
|
||||
> **Accepted trade-off (validated decision).** The Coolify host has no stable
|
||||
> **Accepted trade-off.** The Coolify host has no stable
|
||||
> egress IP, so the Atlas IP allow-list is open to the internet. This widens
|
||||
> the database surface. The mandatory compensating controls are: (1) strong
|
||||
> unique password, (2) least-privilege `readWrite`-on-one-db user, (3) the
|
||||
@@ -96,31 +103,33 @@ reply is immediate.
|
||||
4. Copy the `mongodb+srv://…` connection string into `MONGO_URL` and put the
|
||||
db name in `MONGO_DATABASE`.
|
||||
|
||||
> Storage layout: one collection per module. Each document is a flattened native
|
||||
> document — `{ _id: <user key>, ...payload fields, version, updatedAt }` with no
|
||||
> `value` envelope. Payload fields are hoisted to the document root so they
|
||||
> expand and are queryable in Compass. The two non-object values are wrapped in a
|
||||
> named field: `lol` schedule subscribers under `subscribers` (array) and the
|
||||
> daily push date under `date`. Concurrency uses the `version` field (optimistic lock);
|
||||
> `updatedAt` is a BSON Date.
|
||||
>
|
||||
> The `stats` collection uses queryable aggregate documents for command/user
|
||||
> counts and creates indexes on startup. Deleted legacy command rows are
|
||||
> retained with `deleted: true`; `/stats` queries filter those rows from visible
|
||||
> results. Stats startup uses the idempotent
|
||||
> `migration:stats-delete-stock-dividend-v1` migration to retire historical
|
||||
> `/stock_dividend` rows without erasing them. A historical `system` collection may remain in MongoDB with completed
|
||||
> migration records; keep those records as audit history. Stock stores cash as
|
||||
> `vnd`, embeds positions as `assets.<symbol>.{quantity,base,openedAt}`, and
|
||||
> retains normalized per-user SSI history under
|
||||
> `dividends.<symbol>.<ssi_event_id>`. Unprocessed retained dividend events are
|
||||
> replayed on every `/stock_portfolio` until they are processed or expire after
|
||||
> 90 days; events with no Record date stay informational while SSI is
|
||||
> rechecked, and later SSI responses that omit an event do not delete the
|
||||
> retained record. Coin stores cash as `usd` and embeds positions as
|
||||
> `assets.<symbol>.{quantity,base}`. Stock startup maintenance runs the
|
||||
> idempotent `migration:stock-dividend-history-v1` migration to remove the
|
||||
> retired dividend cursor and hashed applied-event ledger.
|
||||
### Storage layout
|
||||
|
||||
- **One collection per module.** Each document is a flattened native document
|
||||
— `{ _id: <user key>, ...payload fields, version, updatedAt }` with no `value`
|
||||
envelope. Payload fields are hoisted to the document root so they expand and
|
||||
are queryable in Compass. The two non-object values are wrapped in a named
|
||||
field: `lol` schedule subscribers under `subscribers` (array) and the daily
|
||||
push date under `date`. Concurrency uses the `version` field (optimistic
|
||||
lock); `updatedAt` is a BSON Date.
|
||||
- **`stats`** uses queryable aggregate documents for command/user counts and
|
||||
creates its indexes on startup. Deleted legacy command rows are retained with
|
||||
`deleted: true`, and `/stats` filters them from visible results.
|
||||
- **`stock`** stores cash as `vnd`, embeds positions as
|
||||
`assets.<symbol>.{quantity,base,openedAt}`, and retains normalized per-user
|
||||
SSI dividend history under `dividends.<symbol>.<ssi_event_id>`. The
|
||||
[README](../README.md#stock-dividend-commands) describes how those records
|
||||
are replayed and expired.
|
||||
- **`coin`** stores cash as `usd` and embeds positions as
|
||||
`assets.<symbol>.{quantity,base}`.
|
||||
- **`system`** holds one marker per completed one-time startup migration. Keep
|
||||
those records as audit history. The current markers are
|
||||
`migration:stats-delete-stock-dividend-v1` (retires historical
|
||||
`/stock_dividend` stats rows without erasing them),
|
||||
`migration:stock-dividend-history-v1` (removes the retired dividend cursor
|
||||
and hashed applied-event ledger), and
|
||||
`migration:sticker-drop-legacy-packs-v1` (removes records left by the retired
|
||||
per-user sticker pack commands).
|
||||
|
||||
## 2. Coolify
|
||||
|
||||
@@ -132,9 +141,8 @@ reply is immediate.
|
||||
`replace` directive. Coolify must clone submodules, or the Docker build
|
||||
fails at `go mod download` with an unresolved
|
||||
`github.com/tiennm99/monkeyd-crawler`. Turn on Coolify's recursive-clone /
|
||||
submodule option for the resource. If submodules cannot be enabled, drop the
|
||||
module instead by setting `MODULES` to the list without `monkeyd` — the build
|
||||
still needs the submodule, so this is only a runtime opt-out.
|
||||
submodule option for the resource. There is no build without it: leaving
|
||||
`monkeyd` out of `MODULES` only disables the commands at runtime.
|
||||
3. Set the env vars above in Coolify.
|
||||
4. **No public domain / port** is needed — polling is outbound-only. Do not
|
||||
publish a port or attach a domain. `expose: 8080` keeps the health endpoint
|
||||
@@ -152,21 +160,19 @@ reply is immediate.
|
||||
`compose.yml`; an interpolated empty value can override Coolify's runtime
|
||||
env-file value.
|
||||
7. **Health check:** use Coolify's HTTP monitor against `GET /` (returns
|
||||
`text/plain` `miti99bot ok`). Do **not** use a compose `healthcheck` — the
|
||||
distroless image has no shell/curl and `cmd/server` has no `-healthcheck`
|
||||
flag. Note: `/` reports healthy even if Mongo is unreachable (the driver
|
||||
auto-reconnects on the next op); a DB outage will not auto-restart the
|
||||
container — accepted trade-off.
|
||||
`text/plain` `miti99bot ok`). The committed `compose.yml` defines no
|
||||
`healthcheck`, and `cmd/server` has no `-healthcheck` flag. Note: `/`
|
||||
reports healthy even if Mongo is unreachable (the driver auto-reconnects on
|
||||
the next op); a DB outage will not auto-restart the container — accepted
|
||||
trade-off.
|
||||
|
||||
## 3. Command menu
|
||||
|
||||
The bot registers its Telegram command menu from loaded public modules on
|
||||
every startup. The Go module registry is the single source of truth; no separate
|
||||
command-menu file or manual registration step is required. A command's
|
||||
description plus optional `Parameters` metadata feed both surfaces. Telegram
|
||||
renders the command name separately and accepts only a single-line plain-text
|
||||
description. Both the native menu and `/help` show syntax plus the summary and
|
||||
omit example invocations.
|
||||
The bot registers its Telegram command menu from the loaded modules' public
|
||||
commands on every startup. The Go module registry is the single source of
|
||||
truth, so no separate command-menu file or manual registration step is
|
||||
required. See [Command discovery](../README.md#command-discovery) for how the
|
||||
menu text is built.
|
||||
|
||||
## Operations
|
||||
|
||||
@@ -210,8 +216,15 @@ cp .env.example .env # fill TELEGRAM_BOT_TOKEN, MONGO_URL, MONGO_DATABASE
|
||||
docker compose up --build
|
||||
```
|
||||
|
||||
Boot logs should show `storage backend backend=mongodb database=…` (no
|
||||
connection string), `cron scheduler started`, and `telegram long polling
|
||||
started`. A request to `http://localhost:8080/` returns `miti99bot ok` (use
|
||||
`Invoke-WebRequest` in PowerShell or `curl` in a POSIX shell). The bot's webhook
|
||||
must be unset (the container clears it on startup) or `getUpdates` 409s.
|
||||
Boot logs are JSON lines. Look for `"msg":"storage backend"` with
|
||||
`"backend":"mongodb"` and the database name (never the connection string),
|
||||
`"msg":"cron scheduler started"`, and `"msg":"telegram long polling started"`.
|
||||
`compose.yml` does not publish port 8080 to the host, so check the health
|
||||
endpoint from inside the container:
|
||||
|
||||
```sh
|
||||
docker compose exec bot wget -qO- http://127.0.0.1:8080/
|
||||
```
|
||||
|
||||
It returns `miti99bot ok`. The bot's webhook must be unset (the container
|
||||
clears it on startup) or `getUpdates` 409s.
|
||||
@@ -15,10 +15,10 @@ written afterwards.
|
||||
|
||||
| Command | Parameters | Reply to | What it does |
|
||||
|---|---|---|---|
|
||||
| `/addsticker` | `[emoji...]` | sticker, photo, or image document | Adds it to the shared pack and replies with the link |
|
||||
| `/addsticker` | `[emoji...]` | sticker, photo, image document, video, GIF, or video note | Adds it to the shared pack and replies with the link |
|
||||
|
||||
Single-shot: one message, optionally replying to a sticker or image. No
|
||||
conversation state.
|
||||
Single-shot: one message replying to the media to add. No conversation state.
|
||||
[What it accepts](#what-it-accepts) lists every supported kind.
|
||||
|
||||
## Configuration
|
||||
|
||||
@@ -34,7 +34,7 @@ the configured pack belongs to someone else.
|
||||
|
||||
The caller's identity is used nowhere. That is what makes the command stateless:
|
||||
no records, no keys, no per-user locks, and no ownership checks. It is also why
|
||||
`/addsticker` needs no storage and fits in `util`.
|
||||
`/addsticker` needs no per-user storage.
|
||||
|
||||
`STICKER_PACK_NAME` **must end in `_by_<this bot's username>`** — Telegram
|
||||
requires that suffix on every set a bot creates, and refuses to let a bot edit
|
||||
|
||||
Reference in new issue
Block a user