docs: describe the bundled renderer service

This commit is contained in:
tiennm99 committed 2026-10-03 11:16:54 +07:00
1 parent 2bb5108151
commit 3130f5ec36
5 files changed
+37 -31

No files matched your search

+6 -5
View File
@@ -38,12 +38,13 @@ STICKER_PACK_NAME=miti99_by_miti99bot
# Without it every /lol* fetch fails; the stale cache covers ≤60 min.
LOL_PANDASCORE_TOKEN=
# Optional /wheelofnames GIF and /gacha MP4 renderer. Standard deployment:
# https://github.com/tiennm99/wheelofnames. Set the full /api/gif endpoint;
# /gacha calls /api/gacha on the same service. Leave blank to fall back to text
# selection.
# Optional /wheelofnames GIF and /gacha MP4 renderer: the renderer/ service in
# this repository. compose.yml fixes this to http://renderer:3000/api/gif, so
# set it only when running the bot outside compose. /gacha calls /api/gacha on
# the same service. Leave blank to fall back to text selection.
WHEELOFNAMES_API_URL=
# Bearer token matching the wheelofnames service API_TOKEN when URL is set.
# Bearer token shared with the renderer; compose passes it to the renderer as
# API_TOKEN. Required for the renderer to start.
WHEELOFNAMES_API_TOKEN=
# ====================== Leave UNSET on self-host ==================
+5
View File
@@ -12,6 +12,11 @@ port of `tiennm99/mttools/monkeyd-crawler` under
`internal/modules/monkeyd/{crawler,pdf,export}`. Change them here; they no
longer track the mttools copy.
`renderer/` is a separate Node 24 service (JavaScript + JSDoc, Remotion) that
draws the `random` module's animations. It has its own `package.json`, tests,
and `renderer/AGENTS.md`; run its npm commands from that folder. `compose.yml`
deploys it next to the bot.
## Development Rules
- Keep changes scoped to the requested module or shared contract.
+3 -2
View File
@@ -9,7 +9,7 @@ Atlas via long polling and an in-process cron scheduler.
|---|---|
| `util` | `/help`, `/info` (admin), `/stickerid` (owner) |
| `misc` | `/ping`, `/ping_stats` (admin), `/ff` (admin), `/xlt1`, `/giaxang` (Petrolimex retail fuel prices), `/the_answer` (owner), `/trongtruonghop` + `/tth`, `/trongtruonghopvng` + `/tthvng` disclaimers |
| `random` | Pick one comma-separated option at random: `/random` (text), `/wheelofnames` (wheel GIF), `/gacha` (card-pack wish MP4; options are 5★ by default, prefix `4*` or `3*`), `/genshin` (unlisted; the same wish as a Genshin-style meteor). The animations use the optional wheelofnames renderer |
| `random` | Pick one comma-separated option at random: `/random` (text), `/wheelofnames` (wheel GIF), `/gacha` (card-pack wish MP4; options are 5★ by default, prefix `4*` or `3*`), `/genshin` (unlisted; the same wish as a Genshin-style meteor). The animations use the bundled [renderer](renderer/README.md) service |
| `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: `/wordle [word]`, `/wordle_new`, `/wordle_giveup`, `/wordle_stats` |
| `loldle` | League-of-Legends "guess the champion": `/loldle [champion]`, `/loldle_giveup`, `/loldle_stats`, `/loldle_setmax` (owner) |
@@ -240,7 +240,8 @@ 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
compose.yml Coolify self-host stack (single bot service)
renderer/ Node animation renderer for /wheelofnames, /gacha, /genshin
compose.yml Coolify self-host stack (bot + renderer services)
```
## Documentation
+20 -21
View File
@@ -36,8 +36,8 @@ Copy [`.env.example`](../.env.example) → `.env` (gitignored) and fill in.
| `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` |
| `WHEELOFNAMES_API_URL` | leave unset | fixed by `compose.yml` to the bundled renderer (`http://renderer:3000/api/gif`); a Coolify value is ignored |
| `WHEELOFNAMES_API_TOKEN` | ✅ for animations | bearer token shared by the bot and the bundled renderer (its `API_TOKEN`) — secret; unset = the renderer refuses to start and the animated commands reply with text |
| `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` |
@@ -51,31 +51,30 @@ 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.
### Optional wheelofnames renderer
### Animation renderer
`/wheelofnames` uses a remote GIF renderer when `WHEELOFNAMES_API_URL` is set.
The standard renderer is a deployment of
[`tiennm99/wheelofnames`](https://github.com/tiennm99/wheelofnames), but any
service that implements the same `/api/gif` contract can be used. Set the URL
to the full GIF endpoint and set the token to the same value as the service
`API_TOKEN`:
`/wheelofnames`, `/gacha`, and `/genshin` are drawn by the Node renderer in
[`renderer/`](../renderer/README.md) (Remotion and headless Chrome).
`compose.yml` deploys it as a second service, `renderer`, next to the bot. It
is internal only: the bot reaches it at `http://renderer:3000/api/gif` over the
compose network, so it needs no domain and publishes no port. The only setting
is the shared token:
```env
WHEELOFNAMES_API_URL=http://wheelofnames:3000/api/gif
WHEELOFNAMES_API_TOKEN=<same value as wheelofnames API_TOKEN>
WHEELOFNAMES_API_TOKEN=<random secret>
```
Use a public HTTPS URL instead when the bot cannot reach the service on a
private Coolify/Docker network:
Renderer tuning (`MAX_CONCURRENT_RENDERS`, `RENDER_TIMEOUT_MS`, `MAX_OPTIONS`,
`MAX_OPTION_CHARS`) can be set in Coolify too; the defaults are in
[`renderer/docs/deployment.md`](../renderer/docs/deployment.md). Give the host
1-2 GB of headroom for the renderer's Chrome.
```env
WHEELOFNAMES_API_URL=https://wheelofnames.example.com/api/gif
WHEELOFNAMES_API_TOKEN=<same value as wheelofnames API_TOKEN>
```
Outside compose, `WHEELOFNAMES_API_URL` can point the bot at any service that
implements the same `/api/gif` contract.
The bot sends outbound HTTP only; no public bot ingress is required. Remote
renders use `512px`, `20fps`, and `7` seconds total by default. If the remote
service is unset, unavailable, unauthorized, or returns a non-GIF response,
renders use `512px`, `20fps`, and `7` seconds total by default. If the renderer
is unset, unavailable, unauthorized, or returns a non-GIF response,
`/wheelofnames` falls back to the same plain text winner reply as `/random`.
Successful GIF replies include the result behind Telegram spoiler formatting.
@@ -146,8 +145,8 @@ MP4, with the same text fallback.
## 2. Coolify
1. New resource → from this Git repo (Docker Compose), or a prebuilt image.
The committed [`compose.yml`](../compose.yml) defines the single
`bot` service.
The committed [`compose.yml`](../compose.yml) defines the `bot` service
and the internal `renderer` service.
2. Set the env vars above in Coolify.
3. **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
@@ -16,9 +16,9 @@ import (
)
const (
// The standard renderer is a deployment of
// https://github.com/tiennm99/wheelofnames. Operators may point this to any
// service that implements the same /api/gif contract.
// The standard renderer is the renderer/ service in this repository, wired
// in by compose.yml. Operators may point this to any service that
// implements the same /api/gif contract.
wheelOfNamesAPIURLEnv = "WHEELOFNAMES_API_URL"
wheelOfNamesAPITokenEnv = "WHEELOFNAMES_API_TOKEN"