fix: 死锁修复、管理员权限集中管控、群聊频率控制、表情映射修复

thinker.go: 移除所有 defer t.muUnlock() 持锁跨阻塞调用的模式,消除8处死锁点
- performThink: defer→立即解锁,LLM调用不再持锁
- lightThinkLoop: defer在for循环内→第2次迭代自死锁
- resetSilenceTimer: defer持锁调performThink
- UpdatePresence: defer持锁调time.Sleep+performThink
- storeThought: defer+panic→锁泄露; 移除extractProactiveMessage嵌套锁

is_admin三层防御:
- synthesizer: 系统提示词注入管理员/非管理员身份标签
- iot_provider: 非管理员直接拒绝IoT操作
- plugin-manager: ToolDefinition.AdminOnly自动拦截,集中管控

群聊优化:
- group_ambient: 强化审查指令,【不发送】自审查标签
- 群聊间隔4s→3s,最多2条/轮
- 工具失败也推跟进消息,避免沉默

平台桥接:
- 日志文件名使用适配器唯一标识符(ConfigName)
- QQ表情映射替换为官方116条目数据
- CQ表情保留名称/ID
This commit is contained in:
2026-06-28 20:22:09 +08:00
parent 236dbfcc92
commit 44048b333a
14 changed files with 491 additions and 72 deletions
+207
View File
@@ -0,0 +1,207 @@
#!/usr/bin/env python3
"""Add qqFaceName map and resolveFaceName function to adapter.go using official QQ Bot API data."""
# Official QQ Bot API Emoji List
OFFICIAL_DATA = """1 4 得意
1 5 流泪
1 8 睡
1 9 大哭
1 10 尴尬
1 12 调皮
1 14 微笑
1 16 酷
1 21 可爱
1 23 傲慢
1 24 饥饿
1 25 困
1 26 惊恐
1 27 流汗
1 28 憨笑
1 29 悠闲
1 30 奋斗
1 32 疑问
1 33 嘘
1 34 晕
1 38 敲打
1 39 再见
1 41 发抖
1 42 爱情
1 43 跳跳
1 49 拥抱
1 53 蛋糕
1 60 咖啡
1 63 玫瑰
1 66 爱心
1 74 太阳
1 75 月亮
1 76 赞
1 78 握手
1 79 胜利
1 85 飞吻
1 89 西瓜
1 96 冷汗
1 97 擦汗
1 98 抠鼻
1 99 鼓掌
1 100 糗大了
1 101 坏笑
1 102 左哼哼
1 103 右哼哼
1 104 哈欠
1 106 委屈
1 109 左亲亲
1 111 可怜
1 116 示爱
1 118 抱拳
1 120 拳头
1 122 爱你
1 123 NO
1 124 OK
1 125 转圈
1 129 挥手
1 144 喝彩
1 147 棒棒糖
1 171 茶
1 173 泪奔
1 174 无奈
1 175 卖萌
1 176 小纠结
1 179 doge
1 180 惊喜
1 181 骚扰
1 182 笑哭
1 183 我最美
1 201 点赞
1 203 托脸
1 212 托腮
1 214 啵啵
1 219 蹭一蹭
1 222 抱抱
1 227 拍手
1 232 佛系
1 240 喷脸
1 243 甩头
1 246 加油抱抱
1 262 脑阔疼
1 264 捂脸
1 265 辣眼睛
1 266 哦哟
1 267 头秃
1 268 问号脸
1 269 暗中观察
1 270 emm
1 271 吃瓜
1 272 呵呵哒
1 273 我酸了
1 277 汪汪
1 278 汗
1 281 无眼笑
1 282 敬礼
1 284 面无表情
1 285 摸鱼
1 287 哦
1 289 睁眼
1 290 敲开心
1 293 摸锦鲤
1 294 期待
1 297 拜谢
1 298 元宝
1 299 牛啊
1 305 右亲亲
1 306 牛气冲天
1 307 喵喵
1 314 仔细分析
1 315 加油
1 318 崇拜
1 319 比心
1 320 庆祝
1 322 拒绝
1 324 吃糖
1 326 生气"""
ADAPTER_PATH = "d:/Project/Code/Uni/Cyrene/backend/platform-bridge/internal/adapter/qq/adapter.go"
with open(ADAPTER_PATH, "r", encoding="utf-8") as f:
content = f.read()
# Build face map
entries = []
for line in OFFICIAL_DATA.strip().split("\n"):
parts = line.split("\t")
if len(parts) == 3 and parts[0] == "1":
entries.append((int(parts[1]), parts[2]))
entries.sort(key=lambda x: x[0])
map_code = []
map_code.append("")
map_code.append("// qqFaceName maps QQ face/emoji IDs to their Chinese names.")
map_code.append("// Source: Official QQ Bot API Emoji List (https://bot.q.qq.com/wiki/develop/api-v2/openapi/emoji/model.html)")
map_code.append("var qqFaceName = map[string]string{")
for i, (eid, name) in enumerate(entries):
comma = "," if i < len(entries) - 1 else ","
map_code.append(f'\t"{eid}": "{name}"{comma}')
map_code.append("}")
map_code.append("")
map_code.append("// resolveFaceName returns the display name for a face CQ code.")
map_code.append("// Uses name field if present (NapCat array format), otherwise looks up the face ID in qqFaceName map.")
map_code.append("func resolveFaceName(match string) string {")
map_code.append('\tif name := extractCQParam(match, "name"); name != "" {')
map_code.append('\t\treturn name')
map_code.append('\t}')
map_code.append('\tif id := extractCQParam(match, "id"); id != "" {')
map_code.append('\t\tif name, ok := qqFaceName[id]; ok {')
map_code.append('\t\t\treturn name')
map_code.append('\t\t}')
map_code.append('\t\treturn id')
map_code.append('\t}')
map_code.append('\treturn ""')
map_code.append("}")
new_block = "\n".join(map_code)
# 1. Update simplifyCQCodes to handle face with name
# Old: if label, ok := cqSimplifyMap[typ]; ok { return label }
# New: add face handling before the generic lookup
old_simplify = '''\t\tif label, ok := cqSimplifyMap[typ]; ok {
\t\t\treturn label
\t\t}'''
new_simplify = '''\t\tif typ == "face" {
\t\t\tfaceName := resolveFaceName(match)
\t\t\tif faceName != "" {
\t\t\t\treturn "[表情 " + faceName + "]"
\t\t\t}
\t\t\treturn "[表情]"
\t\t}
\t\tif label, ok := cqSimplifyMap[typ]; ok {
\t\t\treturn label
\t\t}'''
content = content.replace(old_simplify, new_simplify)
# 2. Update array-format face handler to use qqFaceName
old_face_array = '''\t\t\tcase "face":
\t\t\t\ttext += "[表情]"'''
new_face_array = '''\t\t\tcase "face":
\t\t\t\tfaceName := ""
\t\t\t\tif data, ok := s["data"].(map[string]interface{}); ok {
\t\t\t\t\tif name, ok := data["name"].(string); ok && name != "" {
\t\t\t\t\t\tfaceName = " " + name
\t\t\t\t\t} else if id, ok := data["id"].(string); ok && id != "" {
\t\t\t\t\t\tif mapped, ok2 := qqFaceName[id]; ok2 {
\t\t\t\t\t\t\tfaceName = " " + mapped
\t\t\t\t\t\t} else {
\t\t\t\t\t\t\tfaceName = " " + id
\t\t\t\t\t\t}
\t\t\t\t\t}
\t\t\t\t}
\t\t\t\ttext += "[表情" + faceName + "]"'''
content = content.replace(old_face_array, new_face_array)
# 3. Insert the face map and resolveFaceName function after cqSimplifyMap
insert_after = "}\n\n// simplifyCQCodes"
insert_idx = content.index(insert_after) + 1 # after the closing }
content = content[:insert_idx] + new_block + content[insert_idx:]
with open(ADAPTER_PATH, "w", encoding="utf-8") as f:
f.write(content)
print(f"Updated with {len(entries)} official QQ emoji entries")