Sakura Guard 智能群管
Sakura Guard 使用主 Bot 的 Telethon 客户端、MySQL 和现有 WebUI,独立于 QA Bot、UserBot。所有群组默认关闭,历史配置不需要迁移设置。启动会幂等创建 core/moderation/schema.sql 中的 InnoDB 表,不修改既有业务表。
启用
- 更新 Python 依赖:
python -m pip install -r requirements.txt。 - 将主 Bot 加入超级群组并授予 删除消息、限制/封禁成员 权限。自动批准入群申请还需要 邀请用户/批准入群 权限。普通基本群必须先升级成超级群组。
- 在
REPORT_ADMIN_IDS配置数字管理员 ID,并由管理员先启动与主 Bot 的私聊,以便接收通知。可另设通知收件人和审核备选群;收件人身份本身不会赋予审核权限。 - 在 WebUI → Sakura Guard 选择或输入负数 Telegram 群组 ID 添加,点击开关立即保存并生效;也可由授权管理员在群内执行
/guard_on,私聊执行/guard_on -1001234567890。
开启时默认检查昵称、验证新成员,消息过滤和全局黑名单共享默认关闭。所有成员处理默认通知管理员。停用群管或入群验证后,未执行的自动处理会取消并恢复原权限;已确认的封禁仍需单独复核解除。仅在明确配置“踢出”时执行 ban/unban 补偿,默认验证失败采用保留封禁。
规则与例外
“添加群组”会立即保存,默认关闭群管,无需额外保存。重复添加不会覆盖已配置的规则或开关。群名在保存后异步解析,刷新页面也会保留群组。
群组设置全部自动保存:开关、下拉选项和关键词标签变更后立即保存;规则、文字及数字输入暂停约 700ms 或离开输入框后保存。审计留存天数也自动保存,无需点击保存按钮。页面显示等待、保存中、已保存或错误状态。
每次只提交修改的字段,保存期间继续编辑不会被旧响应覆盖。未填写完整的规则、无效正则及超出范围的数字保留为本地草稿,不影响其他有效字段保存;修正后自动继续。网络或权限失败会保留表单草稿并提供重试,开关失败则恢复上次值。同一字段被其他入口修改时会显示冲突并保留草稿,点击重试才提交当前草稿。切换群组会提交原群待保存内容,页面内导航先等待保存;未完成或失败的输入会阻止静默离开,刷新按钮也不会丢弃草稿。
可从“已加入的群组”选择群名和 ID 后添加。自动发现只使用主 Bot 自己的会话实体缓存、已配置群组及收到的群消息/成员事件,随后向 Telegram 核实成员身份与最新群名,不依赖 UserBot。频道和已退出的群不会显示为已加入。Telegram 的 完整会话列表接口只向用户账户开放,因此未缓存、未产生事件的历史群不能保证一次性枚举;可发送一条主 Bot 可收到的群消息后刷新,或输入群 ID。群名解析失败不会撤销已保存的群组设置。
查看和退出机器人所在会话
在侧边栏的 机器人群组与频道 独立页面(/bot-chats)查看普通群、超级群和频道的名称、用户名、Telegram ID、成员与管理员身份。即使未启用该群的群管,也会显示已发现的会话。支持搜索和按类型、身份筛选;成员身份或权限无法核实时标为待核实。会话列表、退出预览和历史记录清理统一位于此页;Sakura Guard 页面用于群管设置、案件与名单管理。
默认表格只显示已核实机器人当前加入的会话。历史实体缓存还可能包含机器人仅见过、已退出或已无法访问的会话,不能当作当前加入名单;这些记录单独放在默认折叠的“历史记录”中,不计入已加入数量,也不参与批量退出。ChannelPrivateError 表示机器人无法访问,并不表示操作者不是群主;可能是未加入、已被移除或会话已不可用。
展开“历史记录”后可点击 预览清理无法访问的历史记录,查看完整名称、Telegram ID 和访问失败原因。只有明确拒绝访问的记录进入清理名单;已加入的会话、频道附属私信、已配置群管,以及超时、限流或实体 ID 不一致的记录均保留。取消不会清理,预览有效期 5 分钟;点击 确认清理这 N 条历史记录 后,后台逐条重新核实,只处理这次预览中的记录。预览后已加入、恢复访问或出现活动的会话自动保留。
此操作只清理管理页历史展示,通过独立的 MySQL 表按机器人 ID 保存结果,刷新和重启后仍生效;不会退出群组或频道,不删除 Telegram 消息、Session 实体缓存、群管设置、频道或定时任务配置。刷新时仍会核实这些会话,确认已加入或重新收到消息/成员活动后自动恢复显示。页面提供进度及每条清理、保留、失败原因,系统操作审计保留操作者与结果。清理任务与退出任务不会同时运行,服务重启后不会自动继续尚未完成的清理。
频道附带的私信会话由所属频道统一管理,不作为独立群组加入退出名单,也不会出现在受管群组选择器中。它们可能在 Telegram 底层表现为特殊超级群,机器人在其中显示为普通成员,不能据此推断机器人不是原频道的管理员。页面单独显示这类附属会话的数量;群组、频道及权限核实时还会检查实体 ID,ID 不一致时保留并提示待核实。参见 Telegram 频道私信说明。
点击 预览退出非管理员会话,先查看完整退出名单及排除数量;搜索和筛选不缩小批量退出范围。取消预览不会退出任何会话。预览有效期为 5 分钟,点击 确认退出这 N 个会话 后才开始后台执行。管理员、创建者和权限待核实的会话不进入名单;执行前逐项重新检查,已升为管理员、已离开或无法核实的会话自动跳过,预览后新增的会话不追加执行。
页面显示处理进度和每个会话的退出、跳过或失败原因,刷新页面可继续查看本次任务。遇到 Telegram 限流或操作者权限变更时停止剩余操作,按提示等待后重新刷新、预览。结果写入系统操作审计。退出不删除已保存的群管、频道、定时任务等配置;机器人重启后需重新预览,不会自动续跑退出任务。
列表沿用上面的主 Bot 发现范围:可以覆盖已记录的所有群组与频道,无法保证恢复 Telegram 未向机器人提供的历史会话。
过滤规则
- 内置昵称词:
主页、看我、头像、小学生,支持主頁、頭像、小學生,以及插入空白、零宽字符的变体。可通过标签表单或命令增删。 - 使用 Unicode NFKC、大小写、隐形字符和空白清洗,再通过 OpenCC 简繁归一化。独立的“拦截任何繁体昵称”开关默认关闭;正常繁体姓名不会仅因繁体而被封禁。
- 每次入群、申请、发言和编辑消息重新检查组合显示名称与用户名;消息检查同时覆盖文本、媒体说明、文本 URL 和隐藏超链接。Telegram 的实体偏移按 UTF-16 处理。
- “拦截机器人发言”默认关闭,按群独立保存。启用后按“机器人发言命中动作”处理未豁免机器人的新消息、媒体及编辑消息,默认删除,也可警告、禁言、踢出或封禁,无需打开消息关键词过滤。按 Telegram 的机器人账户标记识别;普通用户通过内联机器人生成的消息不因此被处理。主 Bot、管理员、受信任 Bot 和本群白名单继续豁免,昵称及共享黑名单规则仍优先生效。也可通过
/guard_bots [群ID] on|off切换。 - Telegram 的消息投递是前提:要接收其他机器人的普通群消息,需在 BotFather 为主 Bot 开启 Bot-to-Bot Communication Mode,并确保主 Bot 是群管理员。未开启时不能保证收到其他 Bot 的发言,Guard 只能处理实际收到的消息,不会声称已拦截未收到的内容。参见 Telegram 官方说明。
- 自定义规则可选昵称、用户名、消息或链接,关键词或正则;可增加、删除、上下移动。正则作用于归一化后的文本,保留正则表达式本身的语法。单次正则限时 5ms,正则总耗时预算 25ms,超预算正则跳过并记录告警;关键词仍继续检查。规则应尽量具体,避免昂贵表达式。
- 昵称和用户名关键词默认封禁,可改为警告、禁言或踢出;自定义昵称/用户名规则可单独选择动作或继承群默认。消息、链接和机器人发言可选择删除、警告、禁言、踢出或封禁。非移除型昵称处理不会绕过入群验证。群主、管理员、受信任 Bot 和本群白名单跳过自动处罚;显式共享的永久黑名单优先于本群白名单。无法确认成员权限时不会自动处罚。
- 首版只运行规则,不调用 AI。Telegram 不保证实时推送所有沉默成员的昵称变化,不能把事件检测当作全员实时改名监控。
入群验证与申请
普通新成员先检查黑名单和昵称,然后临时限制发言,发送四选一随机算术题。消息显示昵称、用户名与用户 ID;未设置用户名会明确显示。只有目标用户能作答,默认 120 秒超时、最多三次错误,可分别配置超时和答错上限后的动作:封禁、踢出、保持禁言、警告并保持禁言或等待管理员处理,默认封禁。其他用户、伪造或旧消息的回调不会解除限制;重放同一个错误回调不会重复消耗答题次数。
挑战、答案、原有成员限制、期限与执行阶段均保存在 MySQL。并发加入事件复用当前挑战且不延长超时,退群后再次加入会创建新的挑战。验证消息被删除后自动补发,旧按钮失效。通过后恢复原限制;默认超时或答错达到上限后封禁并删除验证消息,不自动加入全局黑名单。即使之前卡在恢复权限阶段,截止时间仍生效。Telegram 操作与消息清理分别可重试,已结束验证的清理失败也会在重启后继续,不会因删消息失败重复封禁。
验证消息底部默认提供“一键封禁”“封禁并加入黑名单”“跳过验证”。仅群主、具有封禁成员权限的群管理员和 REPORT_ADMIN_IDS 可操作,每次点击重新检查权限,不要求打开案件审核的群管理员授权开关。可按群隐藏任意管理按钮,停用的按钮即使被伪造回调也不能触发新操作。封禁只作用本群;拉黑同时写入现有永久黑名单;跳过只恢复本次验证前权限,不增加白名单,也不会绕过其他昵称规则、已有封禁或人工权限修改。首次有效选择会持久化,与作答、超时及其他管理员选择共用目标用户锁,失败后继续同一意图。
管理员可以处理仍在等待、恢复权限或取消恢复中的验证;passing 仅表示权限恢复尚未完成,不代表验证已经完成。已通过、已超时处理等终态的旧按钮只显示中文结果,不重复执行操作。禁言预期权限在发起操作前持久化,不再被一次空的权限读取覆盖;恢复时归一化 Telegram 的聚合禁言位和永久时限表示,同时保留对人工权限修改的检查。永久时限规则参见 Telegram 权限定义。
已经开始的验证仅由本人正确作答或有管理权限的管理员点击“跳过验证”恢复原权限。答题回调使用绑定本次题目和消息的随机令牌,按实际答案值重新计算题目,不能只用按钮序号或缓存的正确序号放行。作答者、答案值、消息、回调 ID 与时间先落盘,重启恢复也必须重新核验凭据;旧版本没有凭据的 passing 回到等待作答,保留原题、次数和截止时间,旧数字按钮会提示更新后重新作答。
超时或答错后选择警告、禁言、等待管理员处理(兼容原配置值 allow)时,继续保持验证禁言,状态为“未通过验证 / 保持禁言待管理员处理”,管理员仍可封禁、拉黑或跳过。关闭群管/入群验证、加入白名单或获得管理权限不会自动解禁已在验证中的成员;这些变化会阻止自动封禁,但仍需管理员明确跳过。已经完成封禁/踢出的旧验证不会被按钮重新放行。
“验证结束后的消息处理”默认删除,也可保留结果并移除全部按钮。升级后,仍在等待的旧验证消息会原地补充昵称与管理按钮,保留题目、截止时间和答错次数;旧版已经结束的验证只补做消息清理,不追溯批量封禁。
如果验证期间有其他管理员改变成员权限,Guard 会记录 guard.permissions_changed 并停止覆盖;管理员应检查“入群验证 / 错误”记录并人工调整权限,恢复操作随后重试,避免验证按钮覆盖人工封禁。
入群申请通过 MTProto UpdateBotChatInviteRequester 与 messages.hideChatJoinRequest 处理:
- 命中 ID 或昵称规则时拒绝申请并保存审计;拒绝申请不会显示成“已封禁”。
- 可按群将“申请命中规则后的动作”改为保留待审,或批准后继续执行本群入群规则。
- 默认 保持待审,向管理员报告,由管理员在 Telegram 处理。
- 可配置 规则通过后批准,若开启入群验证,批准后在群内继续限制发言并验证。此模式的验证发生在批准入群之后。
- 不假设申请人的私聊始终可用,不声称已完成无法支持的入群前私聊验证。API 或授权失败会显示执行失败。
管理员通知操作
只有 Telegram 确认成功封禁后,案件才进入“已封禁,待审核”,通知显示三个审核按钮。通知包含群名/ID、用户昵称和用户名快照、稳定用户 ID、触发对象、规则、原文和归一化结果、UTC 时间与可用的消息证据链接。
仅解除封禁:只撤销此案件的本群封禁,不新增白名单,不更改已有名单,不自动重新邀请用户。若当前已经进入新的验证或禁言,不会用旧通知清除这些限制;永久黑名单冲突及其他封禁仍会阻止解封。
解除并加入白名单:解除对应群组封禁,将
(chat_id, user_id)加入本群白名单,保存审核人和时间;不会自动重新邀请用户。如果存在全局永久拉黑记录,操作会明确报冲突且不解封。全局管理员需在 WebUI 显式撤销永久拉黑后再审核解封。永久拉黑并入库标记:确认本群禁令,以用户 ID 永久入库,保留首次/最近命中、来源、规则证据和审核人。昵称或用户名变更不影响识别。只在显式启用“共享全局永久黑名单”的群组后续入群/发言时拦截,不执行跨群批量封禁。
验证开始通知提供封禁、封禁并拉黑、跳过验证;验证通过或已跳过后,通知提供封禁和封禁并拉黑。按钮复用本群的“可用管理员验证按钮”设置,仅群主、有封禁成员权限的群管理员及 REPORT_ADMIN_IDS 可操作。回调绑定原始管理员通知和当前验证会话;旧验证通知不能操作后续重新入群的验证。群内已结束的挑战按钮仍不会重新生效。
普通禁言通知提供“仅解除本次禁言”“解除禁言并加入白名单”“封禁”“封禁并加入黑名单”;警告通知提供“忽略本次警告”“加入本群白名单”“封禁”“封禁并加入黑名单”。操作沿用案件审核权限。解除只撤销对应案件的限制,保留其他人工限制;若成员仍在入群验证中,只更新验证结束时应恢复的权限,继续保持验证禁言。警告/禁言升级为封禁后,原按钮失效,通知改为封禁审核按钮。操作及审核人持久化,失败和重启均可继续恢复。
Telegram 回调复核原始通知、数据库案件和当前审核权限。默认授权 REPORT_ADMIN_IDS,可设置独立审核用户列表,或显式授权本群管理员审核。全局命令管理员可以配置群组;通知收件人与审核名单相互独立。
WebUI 沿用既有认证。Telegram OAuth 用户按当前 REPORT_ADMIN_IDS 复核;管理 Token 登录记录为 webui:admin,reviewer_id 留空,不冒用某个 Telegram 用户。开发模式身份不能管理真实群管数据。
MySQL 按用户的连接级锁串行化跨群操作;首次有效审核先持久化选择,之后不能被相反按钮覆盖。Telegram 副作用与 MySQL 无法组成单一事务,因此先保存意图,确认 API 后再原子提交名单与案件终态;中断后继续同一选择。失败不会显示成功。数据库不可用时不继续发起未记录的处罚。
通知支持多个管理员,私聊失败记录在“通知投递 / 错误”,并尝试配置的审核群;后台按退避时间重试。审核完成后尽可能同步更新所有原始通知并移除按钮。进程重启后旧通知仍以数据库状态校验。原始通知或权限不符、案件已过期和已处理的回调均不会重复执行处罚。
“通知管理员”默认开启,事件默认全选:过滤及处罚、验证开始、验证通过/取消、验证失败处理、管理员验证操作、入群申请、白名单与黑名单操作。通知收件人留空时复用 REPORT_ADMIN_IDS,可按群设置独立收件人及事件选择;关闭某类通知不关闭审计记录,历史通知仍会更新最终状态。验证开始和通过也会创建审计事件,投递失败复用现有持久化重试。
验证开始、结束、失败及管理员处理通知均携带对应验证消息 ID 和“验证消息链接”。公开群使用群用户名直达链接,私有超级群使用 t.me/c/ 链接;验证消息被删除后补发时,原管理员通知原地更新为新消息链接。验证消息按配置删除后,其历史链接不能再打开已删除内容。
通知中的用户姓名可点击打开 Telegram 资料,引用按 User ID 绑定,没有 @用户名 或更改用户名后仍可使用;缺少姓名时以用户 ID 显示。昵称中的表情及 HTML/Markdown 字符保持原样,不影响资料引用和现有管理按钮。已注销或当前机器人无法解析的账号仍会保留 ID 并正常投递通知;资料能否访问受 Telegram 账号状态和隐私设置限制。
默认开启“结果通知送达后删除验证开始通知”:同一轮验证通过、超时/答错处理或管理员处理后,逐个收件人确认结果通知已送达,再删除该收件人的开始通知,只保留结果通知及其管理员按钮。结果发送失败时保留原通知;旧通知删除失败会退避重试,重启后继续,已删除的原通知不会重新发送,审计记录继续保留。关闭结果通知时不会删除管理员唯一收到的开始通知。
此开关按群自动保存,用于后续结果通知;已进入清理流程的任务会按原设置继续完成。群内挑战消息仍由“验证结束后的消息处理”独立控制。
管理入口
群内可省略群 ID,私聊必须填写:
/guard [群ID]
/guard_on [群ID]
/guard_off [群ID]
/guard_verify [群ID] on|off
/guard_bots [群ID] on|off
/guard_addword [群ID] 关键词
/guard_delword [群ID] 关键词
/guard_whitelist [群ID] add|remove 用户ID
/guard_logs [群ID]
/guard_unban [群ID] 案件IDWebUI 提供按群设置、关键词和规则表单、黑白名单、待审案件、处罚日志、入群验证与通知错误。支持群 ID、用户 ID、案件状态筛选与分页。执行失败的案件可重试;审核恢复中的案件由后台继续同一选择。撤销永久黑名单只删除库记录,各群现有封禁仍需分别解除。添加白名单本身不会解除已有封禁。
所有配置写入复用现有快照、文件锁、备份、并发差异合并与热重载,只修改 sakura_guard 分区;其他设置继续保留。WebUI 无需手填 JSON。后台审计留存默认 90 天,可设置 7–3650 天;过期待审案件、恢复中的操作、永久名单和白名单不随清理删除。案件仅保留最小证据摘要(原文/归一化各最多 2048 字符),不复制媒体或下载网页。
开发验证
python -m pytest tests/moderation/ -m "not integration" --no-cov
python -m ruff check .
python -m ruff format --check .
npm --prefix web run build
npm --prefix web run test:guard # 已在 Node.js 24 验证真实 MySQL 集成测试需显式配置专用 GUARD_MYSQL_TEST_DSN=mysql://user:password@127.0.0.1:3306,再运行 python -m pytest tests/moderation/test_mysql.py --no-cov。测试每次创建并删除随机命名的独立 schema,需要 CREATE/DROP DATABASE 权限,不读取应用的 MYSQL_* 凭据或操作已有业务表。常规 CI 未提供 DSN 时会跳过这些集成测试。
Telegram 权限、真实私聊投递与审批事件仍应在专用测试群内验收;单元测试使用模拟 Telegram,不会封禁真实用户。