Files
tiennm99bot/internal/modules/blacklist/blacklist.go
T
tiennm99 53e569ca12 feat(blacklist): add /blacklist shorthand and /whitelist_rnd
/blacklist is the short form of the two read commands: bare it lists both
lists like /blacklist_rules, and given text it judges that text like
/blacklist_check. Both long names stay for when the intent should be
explicit. An argument of only whitespace is not an argument, so it lists.

/whitelist_rnd returns one whitelist entry chosen at random, and says the
list is empty rather than answering with nothing. It reads only the
whitelist, and only for the calling topic.

The six existing command descriptions are shortened alongside. /help
renders as one un-chunked message pinned at 4096 runes, and two more
commands pushed it to 4123; the shorter wording brings it to 4049. That
ceiling is a shared limit this module did not create, and it will bind again
on the next command added anywhere in the repo.
2026-09-15 13:58:45 +07:00

107 lines
3.5 KiB
Go

// Package blacklist implements a per-thread dictionary of forbidden text and a
// companion list of exceptions that rescue false positives.
//
// The module is passive on purpose. It never reads ordinary chat messages and
// never deletes, warns or restricts anyone: the lists are inert until
// /blacklist_check asks about a specific text. Enforcement would need a
// message-level hook the dispatcher does not have, privacy mode disabled in
// BotFather, and group-admin delete rights — all deliberately out of scope.
// Check is a pure function, so a future hook could call it unchanged.
//
// Scope is one thread: (Chat.ID, MessageThreadID), the same pair the lol module
// keys subscriptions by. Entries added in one forum topic are invisible in the
// next, and a DM is simply the thread (user's chat ID, 0). Within a thread the
// lists are world-writable, the same trust model the alias module uses: the
// list belongs to the conversation, so the permission to edit it does too.
package blacklist
import (
"github.com/tiennm99/miti99bot/internal/modules"
"github.com/tiennm99/miti99bot/internal/storage"
)
// Entry is one stored rule.
//
// The storage key holds the normalized form, so Text carries what the adder
// actually typed — the only place the original casing and spacing survive, and
// what /blacklist_rules shows.
type Entry struct {
Text string `bson:"text"` // as typed, for echoing back
OwnerID int64 `bson:"ownerId"` // who added it
CreatedAt int64 `bson:"createdAt"` // unix millis
}
// Store is the module's typed view over its collection. Both lists live in it,
// separated by the key prefixes in scope.go.
type Store = storage.DocStore[Entry]
// state holds what the handlers share.
type state struct {
store Store
}
// New is the module Factory.
func New(deps modules.Deps) modules.Module {
s := &state{store: storage.Typed[Entry](deps.Store)}
return modules.Module{
Commands: []modules.Command{
{
// The whole module behind one short name: bare it lists, with
// an argument it checks.
Name: "blacklist",
Visibility: modules.VisibilityPublic,
Description: "Check a text, or list both",
Parameters: "[text...]",
Handler: s.handleShort,
},
{
Name: "blacklist_add",
Visibility: modules.VisibilityPublic,
Description: "Blacklist a text or a reply",
Parameters: "[text...]",
Handler: s.handleAdd(listBlack),
},
{
Name: "blacklist_del",
Visibility: modules.VisibilityPublic,
Description: "Remove from the blacklist",
Parameters: "<text...>",
Handler: s.handleDel(listBlack),
},
{
Name: "whitelist_add",
Visibility: modules.VisibilityPublic,
Description: "Whitelist a text or a reply",
Parameters: "[text...]",
Handler: s.handleAdd(listWhite),
},
{
Name: "whitelist_del",
Visibility: modules.VisibilityPublic,
Description: "Remove from the whitelist",
Parameters: "<text...>",
Handler: s.handleDel(listWhite),
},
{
Name: "blacklist_rules",
Visibility: modules.VisibilityPublic,
Description: "List both lists here",
Handler: s.handleRules,
},
{
Name: "blacklist_check",
Visibility: modules.VisibilityPublic,
Description: "Check if a text is blacklisted",
Parameters: "<text...>",
Handler: s.handleCheck,
},
{
Name: "whitelist_rnd",
Visibility: modules.VisibilityPublic,
Description: "Random whitelist entry",
Handler: s.handleWhitelistRandom,
},
},
}
}