mirror of
https://github.com/kuhyx/signal-bot.git
synced 2026-11-10 19:27:20 +01:00
A photo sent to the chat no longer enters the album by itself. pack_add strips it, checks it against the album and the queue, and writes it to pending/ with a sidecar naming the people and labels the sender proposed; the reply is "czeka na zatwierdzenie". The management page lists the queue above the tagger, with the proposal pre-ticked, and Approve moves the file in, mints the labels, records approved_at and asks the bot to rebuild its table through /_reload -- the same publish path every other data change takes, so PackHolder.reload is gone. A reload the bot refuses is reported on the page, not rolled back: the photo is in the album either way. The personal-data gate now reads pending/*.json too, so a label that is still waiting for approval is forbidden in the tree already. The e2e run boots the management page beside the bot with the same environment, proves the queue is stripped and never reaches media/, approves it through the real routes, and has !losowe answer with the photo afterwards without a restart; exiftool is found through media_strip.locate() there rather than PATH, which lacks vendor_perl on Arch. !gadaj is dropped from !odcisz: it was within a typo of !dodaj, and the nearest-command suggestion must never be a coin toss. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
154 lines
9.0 KiB
Markdown
154 lines
9.0 KiB
Markdown
# 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` |
|
|
| add_photo | `!dodaj` `!dodaj_zdjecie` `!dodajzdjecie` `!add_photo` `!addphoto` |
|
|
| suggest | `!zaproponuj` `!propozycja` `!sugestia` `!suggest` `!pomysl` |
|
|
| good_bot | `!good bot` `!dobry bot` `!goodbot` `!dobrybot` `!good_bot` `!dobry_bot` |
|
|
| food_today | `!co_jadł` `!co_jadl` `!cojadł` `!cojadl` `!jedzenie` `!dieta` + the operator's own in `aliases_custom.json` |
|
|
| workout_today | `!co_ćwiczył` `!co_cwiczyl` `!coćwiczył` `!cocwiczyl` `!trening` `!workout` + the operator's own in `aliases_custom.json` |
|
|
| mute | `!cisza` `!cicho` `!mute` |
|
|
| unmute | `!odcisz` `!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.
|
|
|
|
The operator can add words of their own without a commit: the Commands tab of
|
|
the management page writes `aliases_custom.json` in the bot's state directory
|
|
(`CUSTOM_ALIASES_FILE` names another path), keyed by command name, and the
|
|
bot appends them to the shipped list when it builds the table. They are
|
|
extra spellings only -- `!pomoc` keeps showing the shipped one -- and they go
|
|
through the same clash check as `aliases.json`, so a word another command
|
|
already answers to is refused, as is a name no command has. That is what
|
|
makes the page's save a reload: the bot proves the file before it is kept.
|
|
The tagger refuses to give a person a trigger that is already somebody's
|
|
custom alias, for the same reason it refuses the shipped ones.
|
|
|
|
`good_bot` is the one two-word trigger. It works because `fold()` keeps inner
|
|
spaces and the registry matches the whole text before it looks at the first
|
|
word, so `!good bot` resolves and `!good` alone does not. Two spaces between
|
|
the words resolve to nothing, which is what the one-word spellings are for.
|
|
It is also the one command that answers with no message: it puts an emoji
|
|
on the compliment itself (`POST /v1/reactions/<number>`), drawn from a small
|
|
set, and says nothing in the group.
|
|
|
|
`food_today` and `workout_today` read two logs other programs keep --
|
|
diet_guard's `food_log.json` and screen_locker's `log.json`, at those
|
|
programs' default paths unless `FOOD_LOG_FILE` / `WORKOUT_LOG_FILE` say
|
|
otherwise -- and print the current Warsaw day out of them, fresh on every
|
|
call. The spelling the group actually uses for each has a surname in it,
|
|
so it is a custom alias in the state directory and never appears in this
|
|
repository. Food prints every entry and the day's total, never the daily
|
|
budget, which diet_guard keeps on the machine on purpose. Workouts print
|
|
every field an entry carries except a reservation phone number, a
|
|
screenshot path and the sync record id.
|
|
|
|
## 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 than `difflib`'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 alfa wakacje` + an attachment) exists in the same groups
|
|
as `random` and under the same silence elsewhere. It does **not** put the
|
|
photo in the album: it strips it and queues it for approval on the management
|
|
page (`./web.sh`), and answers with a count — `czeka na zatwierdzenie: 1`.
|
|
Only an approved photo is retrievable, and it is from the moment it is
|
|
approved. No cooldown, no size cap, no daily limit. `DOCS-add-photo.md`.
|
|
|
|
Any command can be **switched off** from the management page (Commands tab),
|
|
outright or until a moment, and given its own cooldown. The bot reads the
|
|
switch on every attempt, so it is live without a reload; a command that is
|
|
off answers `BEEP BOOP !kot WYŁĄCZONE.` (or `… WYŁĄCZONE DO 14:01.`, Warsaw
|
|
time) rather than going quiet, and the refusal neither starts the cooldown nor
|
|
counts as a use. `!pomoc` still lists it. `DOCS-web-ui.md` has the page, and
|
|
the same row is where a custom alias is typed.
|
|
|
|
## 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 — `fold` maps `0134578` onto
|
|
letters, so `!7dni` would 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.
|