mirror of
https://github.com/tiennm99/tiennm99bot.git
synced 2026-10-11 03:13:46 +00:00
feat(sticker): add sticker pack module
Nine commands mirroring the names @Stickers uses: /newpack, /mypack, /addsticker, /delsticker, /editsticker, /ordersticker, /setpackicon, /renamepack and /delpack, plus a confirm callback for the destructive one. Sources are replied stickers, photos or image documents; photos are downloaded, resampled to 512px and re-uploaded. One pack per user, keyed by owner id. Creating a pack is the only operation here that makes a durable, publicly linkable object on a user's behalf, so it is built around proving ownership rather than assuming it: - A name is claimed globally and create-only before Telegram is called. A pending record alone proves only that a caller *asked* for a name, which is exactly what someone naming a victim's public slug also does. - Adopting an existing set additionally requires that the claim predates this invocation. The claim lives in our store and the pack lives at Telegram, so a wiped store would otherwise make every pack adoptable. - Names are released only on positive evidence that no pack stands behind them, never on a generic failure, so a transient error cannot hand a live name to the next caller. - Ownership refusals are byte-identical across failure modes, so they cannot be used to probe which sets exist. Error classification is positive-only throughout: "the set is gone" and "nothing was created" are each proven from a specific Telegram response, never inferred from an error. Post-action commits run on a context detached from the request so a shutdown mid-handler cannot lose the record of something Telegram already did. Enabled explicitly via MODULES rather than by default.
This commit is contained in:
1 parent
e1bf7f1ddb
commit
3751010b8e
35 files changed
+5178
-5
No files matched your search
@@ -72,6 +72,15 @@ func TestCommandDiscovery_AllPublicCommandsHaveSafeMetadata(t *testing.T) {
|
||||
"monkeyd_crawl": "<url> [font_size]",
|
||||
"monkeyd_tags": "<url>",
|
||||
"random": "<option,...>",
|
||||
"newpack": "<pack> <title...>",
|
||||
"mypack": "",
|
||||
"addsticker": "[emoji...]",
|
||||
"delsticker": "",
|
||||
"editsticker": "<emoji...>",
|
||||
"ordersticker": "<position>",
|
||||
"setpackicon": "",
|
||||
"renamepack": "<title...>",
|
||||
"delpack": "",
|
||||
"stats": "[users | user <username> | cmd <command_name>]",
|
||||
"stock_events": "<ticker> [days]",
|
||||
"stock_info": "<ticker>",
|
||||
@@ -91,7 +100,11 @@ func TestCommandDiscovery_AllPublicCommandsHaveSafeMetadata(t *testing.T) {
|
||||
if len(menu) != len(reg.PublicCommands()) {
|
||||
t.Fatalf("menu commands = %d, public commands = %d", len(menu), len(reg.PublicCommands()))
|
||||
}
|
||||
// This map lists the commands whose parameter strings are pinned; commands
|
||||
// absent from it are not asserted (the lookup yields "" for them).
|
||||
seen := map[string]bool{}
|
||||
for _, command := range reg.PublicCommands() {
|
||||
seen[command.Name] = true
|
||||
if got := command.Parameters; got != expectedParameters[command.Name] {
|
||||
t.Errorf("/%s parameters = %q, want %q", command.Name, got, expectedParameters[command.Name])
|
||||
}
|
||||
@@ -107,6 +120,20 @@ func TestCommandDiscovery_AllPublicCommandsHaveSafeMetadata(t *testing.T) {
|
||||
}
|
||||
}
|
||||
|
||||
// Every expectation must correspond to a registered command.
|
||||
//
|
||||
// Without this, an entry whose command stopped being registered simply
|
||||
// stopped being checked: the forward loop only visits commands that exist,
|
||||
// so removing a whole module from factories() left the suite green. That is
|
||||
// also what makes the parameterless entries above load-bearing rather than
|
||||
// decorative — "" == "" asserts nothing on its own, but the command having
|
||||
// to exist at all does.
|
||||
for name := range expectedParameters {
|
||||
if !seen[name] {
|
||||
t.Errorf("/%s is expected but not registered — a module dropped out of factories()", name)
|
||||
}
|
||||
}
|
||||
|
||||
help := moduleutil.RenderHelp(reg)
|
||||
if utf8.RuneCountInString(help) > telegramMessageMaxRunesForTest {
|
||||
t.Fatalf("/help source is %d characters, exceeds conservative Telegram limit %d", utf8.RuneCountInString(help), telegramMessageMaxRunesForTest)
|
||||
|
||||
@@ -26,6 +26,7 @@ import (
|
||||
"github.com/tiennm99/miti99bot/internal/modules/misc"
|
||||
"github.com/tiennm99/miti99bot/internal/modules/monkeyd"
|
||||
"github.com/tiennm99/miti99bot/internal/modules/stats"
|
||||
"github.com/tiennm99/miti99bot/internal/modules/sticker"
|
||||
"github.com/tiennm99/miti99bot/internal/modules/stock"
|
||||
"github.com/tiennm99/miti99bot/internal/modules/util"
|
||||
"github.com/tiennm99/miti99bot/internal/modules/wordle"
|
||||
@@ -91,6 +92,7 @@ func factories() map[string]modules.Factory {
|
||||
"gold": gold.New,
|
||||
stock.CollectionName: stock.New,
|
||||
"stats": stats.New,
|
||||
"sticker": sticker.New,
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
Reference in new issue
Block a user