wechat-group-bot 是我做的微信群自动回复。现在仓库里是免费版,11 月 11 日那次提交 8bae7f1 之后,业务指令和 AI 都不在这个仓库里了。免费版能用的是关键词规则,最多生效 5 条,回复里可以用 {sender} 和 {group}。文档和 settings.example.json 都是这么写的。
我没有连微信。Windows 专用的 uiautomation 用 mock 顶掉,直接调 match_rules、build_reply、handle_message 这些纯逻辑函数。默认配置里的演习模式也开着,不会真的往外发。
默认那条「在吗」是正常的。发送者是「小王」时,匹配结果是「在的 小王,有什么事你说~」。{sender} 被换掉了,看起来一切顺手。
然后我想加一条查单提示。免费版查不了真实订单,只能先告诉对方怎么发。我把回复写成「把 {订单号} 发给我,{sender}」。按理说,群里有人发「查单」,应该回这句话,{sender} 换成「小王」,{订单号} 当普通文字留下------它本来就不是程序提供的变量。实际不是这样。match_rules 直接抛了:
text
KeyError: '订单号'
原因就这一行:
python
return rule["reply"].format(sender=sender, group=group)
Python 的 str.format 会把模板里所有花括号都当成占位符。这里只提供了 sender 和 group,多出来的 {订单号} 就会 KeyError。写成「价格是 {100} 元」或「联系 QQ:{12345}」更糟,花括号里是数字,变成 IndexError: Replacement index 100 out of range。如果你刚好知道 Python 的转义规则,把字面量写成 {{100}},倒是能出来 {100},可配置文件的说明里并没有提这件事,只说了可以用 {sender} 和 {group}。
这条异常会一直冒到 handle_message。那里有一层总的 try/except,日志是「处理消息出错(已兜住,轮询继续):KeyError: '订单号'」。轮询不会断,机器人还在听,但这一条不会回复,也不会走到后面的兜底话术。从群里看,就是有人问了,机器人没反应。你要是只看群、不看日志,会以为规则没配上。
兜底话术 FALLBACK_REPLY 则是另一个方向:它根本不做替换。我把规则清空,把兜底设成「没听懂呢 {sender},再说一次」,没命中时 build_reply 返回的就是带花括号的原文,「小王」这两个字不会被填进去。规则里用 format,兜底里不用,两边行为不一致。同一份配置里写的相同占位符,有的地方生效,有的地方当普通字发出去。
顺手还测了两件跟规则有关、但不是 format 的事。
「帮助 / help / 菜单」这条是 exact 模式,也就是整句完全相等才回。单独发「帮助」或「HELP」能中,前后空格也能中,因为代码先 strip 了,比较时也不区分大小写。可是带上微信那种 @tao 再加一个 U+2005(微信 @ 后面常用这种空格)再写「帮助」,或者末尾加个「帮助!」,就都不中了。免费版已经没有业务指令那一层了,关键词规则就是入口,差这一个前缀或标点,在群里非常常见。
判断有没有人 @ 我,用的是 "@" + 昵称 in 内容。注释里写「只匹配前缀」,实现却是子串包含。昵称是 tao 时,@taotao、@taohaowu,以及句子里的 info@tao.com,都会被当成 @我。昵称只叫「小」的时候,@小明、@小李 也会中。大小写是区分的,@Tao 不会中。如果打开了「只有被 @ 才回」,这些误伤会变成不该回的也回了。
另外,免费版只取 RULES 的前 5 条,第 6、第 7 条我配了也不会命中。启动日志会警告一声,这是有意的限制,不是漏写。
这几件事里,最容易在现场让人摸不着头脑的还是第一条。规则明明配上了,关键词也对得上,群里却没有任何回复;日志里又不是「无命中」,而是一条被兜住的异常。把 {sender} 用 format 来填,本身很常见,只是回复文本一旦出现其它花括号,这条路就会把自己写炸。
本地补丁还没合进仓库。回复改成只替换 {sender} 和 {group},其它花括号原样保留,{订单号} 和 {100} 都不会再抛错,句式里需要写数字或「订单号」这种占位说明时,直接写就行;兜底话术改成同一套替换,{sender} 在兜底里也会被填上。exact 模式会先去掉开头的 @我,再去掉末尾的感叹号、问号,「@tao 帮助」和「帮助!」都能中,「请帮助」「帮助一下」仍然不中,避免 contains 那种误伤。_is_at_me 改成看边界:@昵称前面不能是字母数字,后面必须是结尾、空白(含 U+2005)或常见标点。补丁上重新跑过,@taotao、@小明 和邮箱里的 @tao 都不再误伤,真正的 @tao 在吗 和 @tao + U+2005 仍然能识别。
没有在真实的微信客户端上复测发送和 @ 落点,也没有改 contains 模式------「存在吗」仍可能命中「在吗」,这一点以前写过,这次没有再动。仓库还是免费版,业务指令和 AI 不在这里。