mirror of
https://github.com/tiennm99/tiennm99bot.git
synced 2026-10-15 08:17:21 +00:00
/alias <name> saves a replied message under a name and /insert <name> sends it back; /aliases lists every name and /unalias deletes one. Every Telegram format is supported — sticker, photo, GIF, video, video note, audio, voice, document, plain text — and each is kept as the file_id Telegram already issued, so nothing is downloaded and an alias survives redeploys. The namespace is global and the last assignment wins, matching the shared sticker pack; /unalias is open to anyone for the same reason. A saved name also works as its own command: /cheer rather than /insert cheer. This needs two new seams in the module contract. Module.Fallback handles a /command no module registered, and the dispatcher installs it after every Command — the bot library returns the first matching handler, so code always beats a name resolved at runtime, including an alias that shares a command added in a later build. /alias refuses a name already in the registry for the same reason, since such an alias would only reach /insert. An unknown command stays silent: the fallback sees every unrecognised /foo in every chat, so replying would make typos noisy and would confirm which names exist. Module.Inline answers inline-mode queries — "@botname <prefix>" from any chat, filtered by prefix and capped at Telegram's 50 results. Each result is a cached inline type carrying the stored file_id, so the picker renders real previews without an upload. Video notes are omitted because Telegram defines no InlineQueryResultCachedVideoNote and substituting a plain video would change what was saved. Inline mode must be enabled in BotFather before Telegram delivers these updates. Both slots are single-occupancy with conflict detection at Build. Auth.Permits learns the inline sender so a gated inline handler would not deny everyone. Build's command indexing and slot claiming move into addCommands/addSingletons, keeping it under the project's cyclomatic cap. Also restores the sticker module: /addsticker moves back out of util, which has no store, into internal/modules/sticker as its only command.
157 lines
6.7 KiB
Markdown
157 lines
6.7 KiB
Markdown
# Aliases
|
|
|
|
The `alias` module lets anyone give a short name to a message and send it back
|
|
later by that name.
|
|
|
|
| Command | Parameters | Reply to | What it does |
|
|
|---|---|---|---|
|
|
| `/alias` | `<name>` | any supported message | Saves it under that name |
|
|
| `/insert` | `<name>` | — | Sends back whatever is saved under it |
|
|
| `/aliases` | — | — | Lists every saved name |
|
|
| `/unalias` | `<name>` | — | Deletes a saved name |
|
|
| `/<name>` | — | — | Same as `/insert <name>` |
|
|
| `@botname <prefix>` | — | — | Inline picker, in any chat |
|
|
|
|
All are public and single-shot.
|
|
|
|
## The namespace is global
|
|
|
|
A name assigned in any chat works in **every** chat, for **everyone** — the same
|
|
way the sticker pack `/addsticker` writes to is shared. The store is a plain map
|
|
from name to content, with no chat or user in the key.
|
|
|
|
The consequence is worth stating plainly: anyone can reassign anyone's name.
|
|
`/alias` overwrites rather than refusing, and its reply says what it replaced —
|
|
|
|
> Replaced /insert cheer — it was a sticker, now it is a GIF.
|
|
|
|
Overwriting is deliberate: refusing would make a mistyped alias awkward to
|
|
correct. `/unalias <name>` deletes one, and is open to anyone for the same
|
|
reason overwriting is — the namespace is shared, so the permission model is too.
|
|
A per-owner restriction would leave an alias whose assigner has left the chat
|
|
permanently unremovable.
|
|
|
|
## Invoking an alias
|
|
|
|
Three ways, same binding:
|
|
|
|
1. **`/<name>`** — a saved name works as its own command. `/cheer` is `/insert cheer`.
|
|
2. **`/insert <name>`** — always works, including when inline mode is off.
|
|
3. **`@botname <prefix>`** — an inline picker with previews, usable in any chat,
|
|
even ones the bot is not a member of.
|
|
|
|
**Code always beats an alias.** The dispatcher registers every real command
|
|
before the alias fallback, and the bot library returns the *first* handler whose
|
|
matcher accepts an update — so a command defined in code can never be shadowed
|
|
by a name resolved at runtime. `/alias` refuses a name that is already a
|
|
command for the same reason, since such an alias would only ever be reachable
|
|
through `/insert`.
|
|
|
|
If a future build adds a command whose name an alias already uses, the command
|
|
silently wins and the alias stays reachable via `/insert`. That is the intended
|
|
precedence, not a bug to fix.
|
|
|
|
**An unknown `/command` is answered with silence.** The fallback sees every
|
|
unrecognised command in every chat the bot is in, so replying would turn a typo
|
|
like `/pign` into noise, and would confirm to anyone probing which names exist.
|
|
|
|
## Inline mode
|
|
|
|
`@botname` with no query lists everything; with a query it filters by name
|
|
prefix, case-insensitively, sorted, capped at Telegram's 50 results per answer.
|
|
|
|
Each result is a **cached** inline type — it carries the `file_id` Telegram
|
|
already holds, so nothing is uploaded and the picker shows real previews. This
|
|
is the payoff for storing a `file_id` rather than bytes.
|
|
|
|
**Video-note aliases do not appear inline.** Telegram defines no
|
|
`InlineQueryResultCachedVideoNote`, and substituting a plain video would change
|
|
what was saved. They stay reachable through `/insert` and `/<name>`.
|
|
|
|
**Inline mode must be enabled in BotFather** (`/setinline`) or Telegram never
|
|
delivers these updates and the handler is never called. This is a one-time
|
|
operational step that cannot be done from code.
|
|
|
|
## Names
|
|
|
|
One word, username-shaped: starts with a letter, then letters, digits and
|
|
underscores, up to 32 characters. A leading `@` is stripped rather than
|
|
rejected, since these names imitate usernames and typing the sigil is a natural
|
|
slip.
|
|
|
|
Lookups fold case — `/insert LOUD` and `/insert loud` find the same entry — and
|
|
the spelling the assigner used is what gets echoed back.
|
|
|
|
Telegram's own username minimum is 5 characters; this allows 1 on purpose. The
|
|
point of an alias is to be shorter than what it replaces, and `gg` is a good
|
|
name for a sticker.
|
|
|
|
## What can be saved
|
|
|
|
| Replied message | Sent back with |
|
|
|---|---|
|
|
| Sticker | `sendSticker` |
|
|
| Photo | `sendPhoto` |
|
|
| GIF / animation | `sendAnimation` |
|
|
| Video | `sendVideo` |
|
|
| Video note | `sendVideoNote` |
|
|
| Audio | `sendAudio` |
|
|
| Voice message | `sendVoice` |
|
|
| File / document | `sendDocument` |
|
|
| Plain text | `sendMessage` |
|
|
|
|
Anything else — a location, a poll, a contact — is refused with the list above.
|
|
|
|
**Nothing is downloaded.** Every media kind is kept as the `file_id` Telegram
|
|
already issued, and `/insert` hands that same id straight back to a send call.
|
|
The module stores bytes for nothing but the name and a caption. A `file_id`
|
|
refers to a file on Telegram's servers, so an alias survives restarts and
|
|
redeploys.
|
|
|
|
The kind is stored alongside the id because a bare `file_id` does not say which
|
|
send method will accept it.
|
|
|
|
**Order matters when Telegram fills more than one field.** A GIF arrives as an
|
|
`Animation` *and* a `Document`, and the more specific kind is claimed first —
|
|
otherwise `/insert` would hand back a plain file instead of a looping GIF.
|
|
|
|
**Captions come back too**, for the kinds that can carry one. Stickers and video
|
|
notes cannot, and Telegram's send methods for them have no caption field at all.
|
|
|
|
## Listing
|
|
|
|
`/aliases` prints the count and every name, sorted, in one message.
|
|
|
|
**Names only, not what each holds.** The store answers "which keys exist" in a
|
|
single call, while naming each kind would cost one read per alias — a round trip
|
|
each against MongoDB, on a dispatcher that serves one update at a time. To find
|
|
out what a name holds, `/insert` it.
|
|
|
|
Names list in their folded (lowercase) form, which is exactly what `/insert`
|
|
takes, and each is wrapped in a `<code>` span so tapping one copies just that
|
|
name.
|
|
|
|
The list is trimmed to fit Telegram's 4096-character message limit and ends
|
|
with `…and N more.` when it does not fit; the count at the top is always the
|
|
true total. The trim budget counts the markup, not only the names — at 13 bytes
|
|
a pair the tags outweigh a short name.
|
|
|
|
## Behaviour worth knowing
|
|
|
|
**Text loses its formatting.** Bold, links and mentions are stored as plain
|
|
text: re-sending entities means carrying offsets that no longer line up once the
|
|
text is repeated in a different message. A bare URL still auto-links.
|
|
|
|
**A `file_id` can stop working** — the original file was deleted, or Telegram
|
|
rejects it. `/insert` answers with something actionable rather than a generic
|
|
failure:
|
|
|
|
> "gone" can no longer be sent. Save it again with /alias gone.
|
|
|
|
**Replies keep their forum topic.** Every send forwards `MessageThreadID`, for
|
|
the reason `chathelper.Reply` documents: without it Telegram routes the message
|
|
to a supergroup's General topic instead of the topic the command was typed in.
|
|
|
|
Both handlers run under a 10-second deadline. The bot processes updates one at a
|
|
time, so that bound is what keeps a slow store from stalling other users.
|