mirror of
https://github.com/kuhyx/signal-bot.git
synced 2026-11-11 18:43:49 +01:00
Adds !papież / !papiez / !pope: a random pope as a photo, captioned with his Latin name, reign and pl.wikipedia link. popes.json already carried a Commons portrait filename for all 267 and nothing had ever consumed it. The Latin name comes from the Latin column of Poczet papieży, not from the la.wikipedia sitelink. The sitelink resolved for all 267 and was still wrong: la.wikipedia numbers several popes differently, so #154 came out as "Stefan IX — Stephanus X" in a single caption, which reads as an off-by-one. The table's own column says "Stephanus Nonus" and cannot disagree with the name beside it. LATIN_OVERRIDES ships empty; it is a tripwire, not a backlog. Portraits are cached on disk under CacheDirectory=signal-bot, named from the ordinal so no upstream string ever reaches a path, and bounded at 267 entries so nothing needs evicting. Verified that CacheDirectory wins against ProtectHome=read-only before relying on it. Also adds POST /_selftest and scripts/selftest.sh. Signal never delivers an account its own sends, so no command has ever been exercised end-to-end without asking a human to type it. This injects a real frame into the live dispatcher; it is loopback-only and refuses any group not in SELFTEST_GROUPS, which is empty by default. Verified against the real endpoints, not just the fake: 267/267 portraits resolve on Commons, regeneration is byte-identical on every previously shipped field, and !papież was driven 7 times through the live dispatcher in testbot, returning distinct popes. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_015d48t4qsQLBJnDwDeo2Nom
48 lines
1.8 KiB
Markdown
48 lines
1.8 KiB
Markdown
# Testing a command without a second Signal account
|
|
|
|
Signal never delivers an account its own sends. The bot cannot type `!kot` at
|
|
itself, so until now every command was verified one of two weak ways: by asking
|
|
a human to type it, or by rebuilding the object graph in a scratch script. The
|
|
first proves nothing about the path between the websocket and the handler; the
|
|
second is not the wiring that runs in production.
|
|
|
|
`POST /_selftest` injects a real envelope frame into the **live** dispatcher.
|
|
Everything downstream of the socket read is the code that actually serves the
|
|
group: envelope parsing, the scoping gate, the tally, trigger resolution, the
|
|
cooldown decorator and the real REST transport. Only the socket read is skipped.
|
|
|
|
## Using it
|
|
|
|
Name the groups it may post into -- in `.env`, because the service reads it:
|
|
|
|
```
|
|
SELFTEST_GROUPS=testbot,catbot,finaltestbot
|
|
```
|
|
|
|
Then:
|
|
|
|
```sh
|
|
scripts/selftest.sh testbot '!papież' 3
|
|
```
|
|
|
|
`SELFTEST_INTERVAL` (default 11s) spaces the calls past the per-user cooldown;
|
|
the self-test is always the same author, so a tighter loop just exercises the
|
|
cooldown instead of the command.
|
|
|
|
## What it will not do
|
|
|
|
It **posts for real**, into a real group. Two things keep that from becoming a
|
|
way to make the bot say anything anywhere:
|
|
|
|
- The endpoint lives on the same loopback bind as `/healthz`, never a public one.
|
|
- `SELFTEST_GROUPS` is **empty by default**, and any group not named in it is
|
|
refused with 403. So a fresh checkout cannot post into the production group
|
|
even by accident.
|
|
|
|
## Why the author matters
|
|
|
|
The reply quotes whoever asked, and `quote_author` has to be somebody Signal can
|
|
resolve -- a made-up uuid comes back as `The user ... is not registered` and the
|
|
whole send fails. `resolve_group` therefore attributes the message to a real
|
|
member of the target group who is not the bot.
|