signal-bot/.env.sample
Krzysztof kuhy Rudnicki 558dd297a4 release: 0.0.7 «Telesfor» — !papież, and the bot can test itself
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
2026-08-30 17:06:25 +02:00

79 lines
3.3 KiB
Plaintext

# Copy to .env and fill in. Every value here is read by signal_bot/config.py.
# The bot refuses to start if any REQUIRED value is missing or blank.
# REQUIRED. The account signal-cli is linked to, in +E.164 form.
PHONE_NUMBER=
# OPTIONAL, and normally left empty. Comma-separated group ids.
#
# Empty means "every group this account is a member of", discovered at
# runtime: adding the bot to a group is then the whole procedure, with no
# edit here and no restart. That is safe when the bot has its OWN number,
# because such an account has no other chats.
#
# Set it when the bot runs as a LINKED DEVICE on a personal account, where
# it would otherwise see every group on that account. scripts/live_smoke.sh
# writes it for you in that case; scripts/register_bot.sh deliberately does
# not. Startup fails if a listed group is not one the bot has joined.
#
# Find the ids with: curl -s localhost:9922/v1/groups/$PHONE_NUMBER | jq
#
# Replies, rate limits and the daily tally are per group either way: a
# command is answered in the group that asked and nowhere else, so there is
# no send target to configure. GROUP_ID is still accepted as the one-group
# spelling; GROUP_ID_SEND was removed and now fails startup if set.
GROUP_IDS=
# Optional. The signal-cli-rest-api container. Point this at the fake server
# to run entirely offline. Default: http://localhost:9922
API_BASE_URL=
# Optional. Tally database. Survives restarts. Default: signal_bot.sqlite3
DATABASE_PATH=
# Optional. When the daily tally is posted. Default: 21:37
REPORT_HOUR=
REPORT_MINUTE=
# Optional. Per-user, per-command cooldown in seconds. Default: 10
COOLDOWN_SECONDS=
# Optional but recommended. Comma-separated +E.164 numbers -- or member
# UUIDs, which is how /v1/groups spells anybody the account has never
# messaged -- allowed to add this bot to a group.
#
# Empty means anyone who knows the number can pull the bot into a group and
# have it answer there. With it set, "add the bot" is still the whole setup
# for you, and a stranger's group is refused with one WARNING naming that
# group's members, so you can see exactly which string to add here.
TRUSTED_INVITERS=
# Optional. How long the bot may hear nothing at all -- no message, no
# receipt, no typing indicator -- before it probes /v1/devices to find out
# whether it has gone deaf. Three failed probes and /healthz turns 503.
# Default: 120. scripts/e2e.sh sets it to 0 to prove the gate really fires.
SILENCE_SECONDS=
# Optional. Image APIs. Overridden by scripts/e2e.sh to serve local fixtures.
CAT_API_URL=
DOG_API_URL=
GECKO_API_URL=
COMMONS_API_URL=
# Optional. Where !papież keeps the 267 papal portraits it has downloaded.
# Default: $CACHE_DIRECTORY/portraits under systemd (the unit sets
# CacheDirectory=signal-bot), otherwise $XDG_CACHE_HOME/signal-bot/portraits.
PORTRAIT_CACHE_DIR=
# Optional. Groups scripts/selftest.sh may make the bot post into. Empty by
# default, which makes POST /_selftest refuse everything -- Signal never
# delivers an account its own sends, so this is the only way to exercise a
# command without a second account, and it posts for real.
SELFTEST_GROUPS=
# Optional. Where the /healthz readiness probe binds.
# Default: 127.0.0.1:8137 -- deliberately not 8000, which unrelated dev
# servers grab, and below the ephemeral range so the kernel cannot take it.
BIND_HOST=
BIND_PORT=