Anybody may ask; ten quiet minutes is the whole downside, so there is no trust gate on the way in and no cooldown. The confirmation is the last thing said: below it the dispatcher drops every reply in that group -- commands, the near-miss suggestion, and the nightly report, which is held back (never marked) rather than lost. Messages are still counted. !odcisz is the one command that runs through a mute, and only for TRUSTED_INVITERS; a stranger is refused without a reply, because a refusal is a reply. Unset, anybody may -- the same reading InviteTrust gives an unset variable, now shared as trust.is_trusted. There is deliberately no !unmute alias: it is one edit from !mute, and tests/test_nearest.py forbids two commands within reach of each other. The state is a deadline in group_meta, so it survives a restart and a reload and is cleared with the group. tests/test_help_command.py hit the 250-line cap and its album half moved to tests/test_help_album.py. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
6.0 KiB
Commands
The copy that matters is not this file: !pomoc prints the same list in the
group, in Polish, one line of description per command. That version is built
from the command registry at run time and cannot drift, because
CommandRegistry.register takes the description as a required argument and
help_command.render_help prints any command its GROUPS table forgot to
file anyway, under no heading.
This file is the trigger reference — every spelling that resolves, which
!pomoc deliberately does not print.
| Command | Aliases |
|---|---|
| cat | !kot !koty !kots !cat !cats !meow !miau + styled variants |
| dog | !pies !psy !dog !dogs !woof !szczek + styled variants |
| gecko | !gekon !gekony !geko !gecko !geckon !geckos |
| art | !sztuka !sztukę !sztuki !art |
| poem | !wiersz !wiersze !wiersza !wierszyk !poem !poems !poezja !poezje !poetry |
| trap | !pułapka !pulapka !pułapki !pulapki !pułapkę !pulapke !trap !traps !trapy !trapper !sidła !sidla !sidło !sidlo !wnyk !wnyki !potrzask !zasadzka !klatka !samołówka !samolowka !beartrap !bear_trap !snare !snares !mousetrap |
| pope | !papież !papiez !papieze !papieża !papa !pope !popes !pontifex + styled variants |
| stats_today | !staty !statystyki !stats !dziś !dzis |
| stats_yesterday | !wczoraj !yesterday |
| stats_week | !tydzień !tydzien !week |
| stats_month | !miesiąc !miesiac !month |
| stats_year | !rok !year |
| stats_records | !rekordy !rekord !records |
| stats_crown | !korona !crown |
| stats_weekdays | !dni !days |
| stats_me | !ja !moje !mnie |
| command_stats | !analityka !command_stats !commandstats !analytics !uzycie !użycie !ranking |
| help | !pomoc !pomocy !help !komendy !commands |
| random | !losowe !losuj !random !rand |
| suggest | !zaproponuj !propozycja !sugestia !suggest !pomysl |
| mute | !cisza !cicho !mute |
| unmute | !odcisz !gadaj !wracaj |
Aliases live in signal_bot/aliases.json, not in source, because they are
data: adding one should never mean touching Python. Two constraints on a new
one. It is subtracted from the personal-data gate's word list by
_repo_vocabulary(), so it must not be name-shaped; and
tests/test_nearest.py fails if it lands within edit distance of a
different command's trigger, because the near-miss suggestion below would
then be a coin toss. !poems is the one trigger in the table that is only
just legal: it sits two edits from !pies, and passes solely because !poem
is one edit away and wins the tie. Deleting !poem would fail the suite at
!poems, nowhere near where the change was made.
A word nothing claims
An unrecognised !word used to be dropped in silence, which made a typo
indistinguishable from the bot being down. Now it is answered, and kept:
- within one edit (two, from six characters up) of a real trigger, the bot
asks whether that is what you meant —
!cta→ chodziło o !kot? The distance is optimal string alignment rather thandifflib's ratio, because a transposition costs one edit there and shreds the matching blocks a ratio counts. - otherwise it points at
!zaproponuj, which files the idea. - either way the word and the message go into
unknown_commands, which the management page shows. That is the list of commands worth writing next.
Three things stay silent, and each for its own reason. !kot ładny is a
real trigger with words after it, and always has been silent. !! and !?
are punctuation, not a mistyped command. And a command scoped to some other
group — the album's, whose triggers are real people's names — is neither
answered nor recorded, because a refusal would announce that the album
exists.
random exists only in the photo album's own groups, and only on an
installation that has an album at all — everywhere else it is not a command,
not refused and never logged. It takes arguments, and those
arguments are resolved to pseudonym keys by the registry before any handler,
log line or command_uses row sees them. See DOCS-photo-pack.md.
mute (!cisza [1-10]) silences the bot in that group only, for the
minutes asked or ten by default, and ten at most. It confirms once and then
answers nothing: no commands, no near-miss suggestion, and the nightly report
is held back (not marked, so the next run still says that day). Messages are
still counted. Anybody may ask -- ten quiet minutes is the whole downside.
unmute (!odcisz) is the one command that runs through a mute, and only
for TRUSTED_INVITERS; anyone else is refused without a reply, because a
refusal is a reply. With TRUSTED_INVITERS unset, anybody may. There is no
!unmute alias: it sits one edit from !mute, inside the near-miss budget.
add_photo (!dodaj) is unwired since 2026-09-04: a photo posted to a
group must not reach the album without somebody approving it, and no approval
queue exists yet. It comes back when one does; DOCS-add-photo.md describes
the path as it was.
Why !pomoc shows the first alias and not a trigger
Command.triggers is a frozenset of folded skeletons — folding.py
runs NFKC, strips diacritics and maps Cyrillic/Greek/small-caps/leet
lookalikes onto ASCII, so that !gecko typed on a Russian keyboard still
resolves. Everything that makes those triggers robust also makes them
unprintable: the set has no order, !papież survives only as !papiez, and
the styled variants come out as zalgo.
So Command.primary holds the first raw trigger instead, which
aliases.json lists as the canonical Polish spelling for every command.
Two consequences worth remembering, both from folding.py:
- A command name can never contain a digit —
foldmaps0134578onto letters, so!7dniwould resolve as!tdni. - The styled Unicode aliases in the table above are redundant now; folding covers them. They are kept because removing an alias can only take something away.