Skip to content

本版新增功能 ​

本文记录本 Fork 相对上游 Gensokyo 的主要代码行为。

范围说明:

标记含义
-通用能力,具体可用性取决于调用的 Action 和 QQ API 限制
私聊 (C2C)QQ C2C 单聊
q群 (Group Chat)QQ 群

事件 ​

Handler范围行为
GroupMessageEventHandlerq群 (Group Chat)接收 GROUP_MESSAGE_CREATE 全量群消息(含 @Bot 的,其中 @Bot 始终剥离)。
GroupMemberAddEventHandlerq群 (Group Chat)GROUP_MEMBER_ADD 转 notice.group_increase。
GroupMemberRemoveEventHandlerq群 (Group Chat)GROUP_MEMBER_REMOVE 转 notice.group_decrease。
GroupJoinRequestEventHandlerq群 (Group Chat)GROUP_JOIN_REQUEST 转 request.group(sub_type=add,flag 为 join_request_id);需群管理员身份。
C2CMsgRejectHandler私聊 (C2C)用户关闭 C2C 主动消息推送。
C2CMsgReceiveHandler私聊 (C2C)用户开启 C2C 主动消息推送。
FriendAddEventHandler / FriendDelEventHandler私聊 (C2C)好友关系事件(request.friend + notice.friend_add / notice.friend_decrease)。

text_intent 只注册写入的 handler。遇到 4014: disallowed intents 时可开启:

yaml
suppress_disallowed_intents: true

该开关在 Identify 阶段自动屏蔽 GroupMemberAdd/Remove、GroupMessageCreate 等容易被拒绝的 intent。GroupMessageEventHandler 仅当无其他群组 handler 注册时才会被屏蔽;若同时注册了 GroupATMessageEventHandler 等 handler,则保留 intent 并复用。不会自动启用对应 handler。

API ​

Action范围行为
get_avatar-获取 QQ 头像直链。
get_robot_share_link-获取机器人分享链接(以 notice 事件推送)。
send_private_msg_wakeup私聊 (C2C)发送带 is_wakeup=true 的 C2C 召回消息。
put_interaction-回复按钮交互结果。
delete_group_msgq群 (Group Chat)撤回指定用户在群内的消息。
set_group_card-QQ 官方 API 未提供设置群名片接口,明确失败回执(retcode 100),不做假成功。
set_group_add_requestq群 (Group Chat)入群申请审批:真实调用 QQ v2 审批接口(approve 映射 op,flag 为 join_request_id)。与 [CQ:set_group,action=add_request] 共享实现底层。
get_group_infoq群 (Group Chat)获取群信息:真实调用 QQ v2 群信息接口,返回群名/简介/成员数。
set_group_ban / set_group_whole_banq群 (Group Chat)群禁言:set_group_ban 按官方增量语义提交(members[{op:add,member_openid,mute_expire_at(RFC3339)}];解禁 op:del + 空 mute_expire_at);set_group_whole_ban 固定失败——官方 POST /restrict_chat_setting 请求体只有 members,无全员禁言写入字段,API 路径返回 retcode=100 + 「官方未提供全员禁言写入字段,仅支持按成员禁言」,不再提交任何请求。与 [CQ:set_group] 共享实现底层。
get_group_join_request_listq群 (Group Chat)入群申请列表:请求 cursor(string,优先)/ limit(默认 20、上限 50),next_index(int)为兼容回退;响应 list + next_cursor,保留 join_requests / next_index 兼容别名。返回的 group_id/user_id/flag 可直接回传审批。
get_group_bot_stateq群 (Group Chat)机器人群内状态:新字段 joined_at(RFC3339 原样)/allow_proactive_msg/recv_msg_setting/member_role 与官方一致;旧字段 join_time(由 joined_at 解析的秒级时间戳)/can_push/push_msg_setting/role 保留为别名。
join_approval_strategy_create / _list / _update / _execute / _whitelist / _delete-入群自动审批策略全生命周期管理(对应 QQ v2 策略接口)。
get_group_member_listq群 (Group Chat)群成员列表:真实调用 QQ v2 接口,游标分页全量拉取(单页≤30、安全上限 100 页),返回 join_time/role/nickname 真实数据;官方接口不可用时回退 idmap-pro 本地缓存。
get_group_member_infoq群 (Group Chat)群成员信息:真实调用 QQ v2 接口,失败时回退中性值。
set_group_kickq群 (Group Chat)批量移出群成员(单批≤20,user_id/user_ids 二选一、可合并去重),可同步拉黑。与 [CQ:set_group,action=kick] 共享实现底层。
get_group_member_blacklist / set_group_member_blacklistq群 (Group Chat)群黑名单查询 / 增删(单批≤20)。
get_custom_menu / set_custom_menu私聊 (C2C)C2C 自定义菜单读取 / 设置(覆盖式,仅 C2C 全局生效)。
get_panel_list / create_panel / get_panel / set_panel / delete_panel / set_panel_target-指令面板管理(列表/创建/详情/更新/删除/关联对象增删),支持 c2c/group/channel/dm 场景。
/metrics-Prometheus 文本指标。

假数据诚实化(2026-09) ​

QQ 官方 API 不提供的字段/能力不再伪造假值:

  • 发送失败 / 审核中回执的 message_id 由固定假值改为 0(message_parser.go)。
  • set_group_card:官方无此接口,由假成功改为明确失败(retcode 100)。
  • get_group_list:group_create_time 置 0;逐群拉取真实群名/简介/成员数,get_g_list_delay(毫秒)控制逐群节流。
  • get_status:包收发/断线/丢包统计置 0(官方无对应接口),online/good 保持 true,消息收发计数仍为 botstats 真实数据。
  • mark_msg_as_read:官方无已读上报接口,保持空实现仅回执 ok。
  • get_group_member_list:官方路径弱字段 level/area/title 返回空字符串(本地缓存回退路径仍为 "0")。

消息与 CQ 行为 ​

q群 to_me ​

范围:q群 (Group Chat)

q群消息事件新增 to_me 字段:

  • GROUP_AT_MESSAGE_CREATE 固定为 true。
  • GROUP_MESSAGE_CREATE 根据 Mentions[].IsYou 判断。

非自身 @ ​

范围:q群 (Group Chat)

GROUP_MESSAGE_CREATE 中:

  • @bot 始终从文本中剥离(通过 Mentions[].OpenID 匹配 <@OpenID> / <@!OpenID>),并设置 to_me=true。不依赖 remove_at 配置,因全量群消息中的 @ 只是原文一部分,而非 GROUP_AT_MESSAGE_CREATE 的专门 @ 事件。
  • @其他人 转成 [CQ:at,qq=<虚拟ID>],同时建立 idmap 映射。

出站 @ ​

范围:q群 (Group Chat)

出站 [CQ:at,qq=<虚拟ID>] 根据消息类型做不同处理:

  • 纯文本消息:通过 resolvePlainTextAtMentions 将 [CQ:at,qq=数字] 替换为 @用户名 (带空格),用户名来自 idmap.GetUserName 缓存(入站时自动缓存,10 分钟 TTL)。
  • Markdown 消息:通过 ResolveMarkdownAtMentions 反查 OpenID,转换为 <qqbot-at-user id="OpenID" />,整个 messageText(含 <qqbot-at-user> 标签和文本内容)合并到 Markdown 内容头部,确保文本不丢失。
  • 图文混合消息(msg_type=7):同样通过 resolvePlainTextAtMentions 转换文本段中的 [CQ:at]。覆盖 send_group_msg、send_group_msg_raw、send_private_msg 的图文混合路径(2026-08 修复,此前漏处理导致 CQ 码原文显示)。
  • 图文混合消息走 Markdown 路径(auto_md,msg_type=2):当图文混合触发 auto_md(transmd=true)时,messageText 在塞进 Markdown 参数前经过 ResolveMarkdownAtMentions 转换为 <qqbot-at-user> 标签。覆盖共用 auto_md 的 send_group_msg、send_group_msg_raw(2026-08 修复,此前 auto_md 把含 [CQ:at] 的文本直接塞进 Markdown 参数,导致 QQ 官方 Markdown 渲染把 [CQ:at,qq=数字] 当纯文本显示,变形为 [CO:at,qq=数字])。
text
纯文本出站: [CQ:at,qq=123213]你好 → @张三 你好
Markdown 出站: [CQ:at,qq=123213]你好 → <qqbot-at-user id="OpenID"/>你好(合并到 markdown 内容头部)
图文混合 Markdown 出站: 图片[CQ:at,qq=123213]你好 → 图片<qqbot-at-user id="OpenID"/>你好

详见 Markdown 中的 CQ at。

is_full_group_message 字段 ​

范围:q群 (Group Chat)

群消息事件新增 is_full_group_message 布尔字段,标识消息是否来自全量(非@)群聊通道:

来源is_full_group_messageto_me
GROUP_MESSAGE_CREATE(全量群聊)true取决于是否提及机器人
GROUP_AT_MESSAGE_CREATE(@机器人)不出现(false)true

该字段用于区分消息的底层事件来源,与 to_me 字段互补。to_me 表示消息是否 @ 了机器人,is_full_group_message 表示消息是否来自全量监听通道。

Markdown 图片 ​

范围:q群 (Group Chat)

Markdown 内容中的 ![](path):

  • HTTP(S) URL 保留。
  • 本地路径或 file:// 路径会上传到 QQ CDN 后替换为直链。

[CQ:reply] ​

范围:-

send_group_msg / send_private_msg 会从文本中提取 [CQ:reply,id=<虚拟消息ID>],尝试通过 idmap 缓存反查真实 QQ message_id,再写入 message_reference 和 msg_id。两个 Handler 均已支持,确保 v2 API 正确识别为回复消息。QQ q群 API 可能接受但不显示引用样式。

REFIDX 引用(v013 新增):引用非机器人发的消息时,message_reference.message_id 必须使用事件 message_scene.ext[] 中的 REFIDX_* 索引(官方 Go SDK 未暴露,Gensokyo 自扩展)。入站时 Processor 从 message_scene.ext[].msg_idx= 提取并存储 消息ID→REFIDX 映射(echo 内存缓存,30min 清理);出站反查时若目标消息关联了 REFIDX 则优先使用,无 REFIDX(机器人自己消息 / 老消息)回退普通消息 ID,MsgID 始终为普通消息 ID。

覆盖路径(按优先级):

  1. 纯文本路径 — 所有 handler 均支持。
  2. 图文混合(combo)路径 — send_private_msg 支持;send_group_msg 在 2026-07 修复中补齐(含 transmd→markdown 路径)。
  3. 纯媒体路径(无文本) — 群聊和私聊的 foundItems 循环中,富媒体消息(msg_type=7)在 2026-07 修复中新增 reply 支持。

注意:msg_id 与 event_id 二选一(API 规范),设置 reply 的 MsgID 时会自动清空 EventID。

[CQ:member] ​

范围:q群 (Group Chat)

GROUP_MEMBER_ADD / GROUP_MEMBER_REMOVE 转 OneBot notice,message 字段附带 [CQ:member]。后端用普通 send_group_msg 回复时带回该 CQ 码,Gensokyo 会重新路由到真实 q群。详见 CQ member。

[CQ:active] ​

范围:-

[CQ:active] 用于标记出站消息为主动推送模式。支持两种格式:

  • [CQ:active] — 裸标记,识别为主动推送。
  • [CQ:active,type=xxx,sub_type=yyy] — 带参数,type 写入 foundItems["active_type"],sub_type 写入 foundItems["active_sub_type"]。

入站解析时,ProcessCQActive 从文本中移除该 CQ 码并在 foundItems 中记录。出站发送时,send_group_msg 和 send_private_msg 检测到 active 标记后清空缓存的 msg_id 和 event_id,强制走主动推送通道。send_private_msg_wakeup 中也会跳过 active 相关 key 避免误发媒体。

需要 C2C 召回消息时使用 send_private_msg_wakeup。详见 CQ active。

[CQ:wakeup] ​

范围:私聊 (C2C)

[CQ:wakeup,userid=xxx] 用于在 send_private_msg 出站消息中标记 C2C 互动召回(唤醒)消息:

  • 解析:ProcessCQWakeup 从文本中移除 CQ 码,userid 写入 foundItems["wakeup"];数组消息段 "type":"wakeup"(data.userid)与 TRSS 格式同样支持。
  • 发送:send_private_msg 检测到 wakeup 标记后,将目标用户覆盖为 CQ 码指定的用户(虚拟 ID 自动转 OpenID),按召回通道发送(IsWakeup=true、MsgID=""、EventID="" 互斥),支持纯文本/图文混合/Markdown/富媒体。
  • 发送结果以 notice(notice_type=c2c_wakeup_resp)推送给应用端。
  • 群聊消息(send_group_msg)中的 [CQ:wakeup] 会被跳过,不发送(召回仅支持 C2C)。

详见 CQ wakeup。

[CQ:keyboard] ​

范围:q群 (Group Chat) / C2C (私聊)

[CQ:keyboard] 用于在文本消息上附加 QQ 官方内嵌键盘(按钮消息),按钮支持跳转/回调/指令三种行为。语法与 [CQ:markdown] 一致:data 支持 base64 编码或原始 JSON。

  • 解析:ProcessCQKeyboard 从文本中提取并移除 CQ 码,解码后的键盘 JSON 存入 foundItems["keyboard"];消息段路径(数组/TRSS)同样支持 keyboard 段(v013 补齐)。
  • 发送:send_group_msg / send_private_msg / send_group_msg_raw 文本路径在无 markdown 时解析键盘并附加到 MessageToCreate.Keyboard,以 msg_type=0 文本 + 键盘发送;specify_user_ids 虚拟 ID 自动转 OpenID,私聊 __USER_ID__ 占位符替换为实际用户 OpenID。
  • 与 [CQ:markdown] 同存时:markdown JSON 内嵌的 keyboard 优先;markdown 未内嵌 keyboard 时独立 [CQ:keyboard] 仍会附加(v013 起支持共存)。

详见 CQ keyboard。

[CQ:remove] ​

范围:q群 (Group Chat)

出站 CQ 码 [CQ:remove,user_id=虚拟用户ID,msg_id=虚拟消息ID],后端通过 send_group_msg 携带此 CQ 码即可撤回指定的群消息。user_id 和 msg_id 均为虚拟 ID,Gensokyo 内部自动转换为 QQ OpenID / 真实 message_id。纯 CQ 码的消息不会实际发送。详见 CQ remove。

[CQ:card] ​

范围:q群 (Group Chat)

新增扩展 CQ 码 [CQ:card,title=xxx,desc=xxx,pic=xxx,url=xxx],在群聊中发送图文卡片消息(msg_type=8)。参数顺序无关,title 必填,其余可选。详见 CQ card。

[CQ:input_notify] ​

范围:私聊 (C2C)

新增扩展 CQ 码 [CQ:input_notify,type=1,second=60],在单聊正文发送前先发送"正在输入"状态通知(msg_type=6)。详见 CQ input_notify。

[CQ:stream] ​

范围:私聊 (C2C)

新增扩展 CQ 码 [CQ:stream,type:start,qq:<虚拟ID>],支持单聊流式消息。分 start→mid→finish 三阶段逐片发送,实现打字机效果。阶段间通过 stream_msg_id 缓存关联。详见 CQ stream。

按钮权限 ID ​

范围:-

发送 keyboard 时,会遍历按钮 permission.specify_user_ids,把虚拟数字 ID 转成 QQ OpenID。C2C 场景中,如果 permission.type=0 且指定了用户,会改为 type=2(所有人),避免 QQ API 拒绝。

零值字段 ​

范围:-

action.type=0、permission.type=0、render_data.style=0 会被保留,不再因为 JSON omitempty 丢失。

语音 ​

范围:-

[CQ:record] 支持自动转 SILK v3。WAV/MP3 可走 Go 内部处理;其他格式仍可能需要 ffmpeg。目标音频为 16-bit、单声道、采样率由 record_sampleRate 控制,默认 24000。

入站事件新增字段(2026-09-16) ​

范围:q群 (Group Chat) / 私聊 (C2C)

botgo/dto/message.go 补齐官方 2026-09-16 新增的消息事件字段,handlers/message_parser.go 将其降级为 CQ 码 / 消息段上报:

字段说明
message_type消息类型:0 文本 / 3 结构化卡片 / 101 并行消息 / 102 聊天记录 / 103 引用消息。
ark_data结构化卡片数据(prompt/ark_type/ark_name/fields);ark_type 含官方 2026-09-16 新增的 picture。
msg_elements嵌套消息元素(递归结构,含 msg_idx/content/attachments/ark_data/msg_elements)。
voice_wav_url / asr_refer_text语音附件 WAV 直链与语音转写文本。
MediaResponse.id / raw_url富媒体上传响应新增字段。

降级形态(字符串路径默认 array: false,追加在图片 CQ 码之后):

  • [CQ:record,file=<voice_wav_url 或附件 url>,asr=<转义文本>](asr 为空时省略)
  • [CQ:ark,ark_type=..,ark_name=..,prompt=..];无 ark_data 时输出裸 [CQ:ark]
  • [CQ:reply,msg_idx=..],以及嵌套元素正文(101 并行消息 / 102 聊天记录按正文保留,不静默丢弃)

数组路径(array: true)追加 record / ark / reply / text 段,排在图片段之后。

富媒体上传:5MB 直传上限与自动分片 ​

范围:q群 (Group Chat) / 私聊 (C2C)

官方 file_data 二进制直传上限为 5MB(handlers/upload_helper.go 的 maxDirectFileDataBytes):

  • 超过 5MB 的 base64 / 二进制自动改走分片上传(upload_prepare → 预签名 PUT → upload_part_finish → 合并),群聊 / 单聊一致。
  • 按类型软限制(图片 20MB / 视频 30MB / 语音 20MB / 文件 200MB)降级为“平台侧降级提示日志”,不再作为直传判据;硬限制 200MB 不变。

错误码提示 ​

范围:-

当 QQ OpenAPI 调用返回错误时,Gensokyo 会在控制台向开发者输出该错误码对应的中文描述和排查建议,方便定位问题。

实现: handlers/qq_error_codes.go

  • qqErrorCodes:错误码 → 中文描述/排查建议映射表(数据来源:QQ 官方文档)
  • ExtractQQErrorCode(err):从 err.Error() 中正则提取 "code":xxxxx
  • FormatQQError(err):格式化为开发者可读的提示

覆盖 Handler: send_group_msg / send_group_msg_raw / send_private_msg / send_private_msg_wakeup

2026-10-18 补码: 本轮新增 16 个官方错误码——富媒体上传(10000/850018/850019/850026/850027/850031/40093001/40093002)、群聊 v2 撤回(40061001/40062003/40064004/50065001)、取 token(100001/100007/100016);11253 描述更新为官方措辞;10004 双语义(频道 vs 取 token 的"机器人不存在")以注释说明。完整映射见 handlers/qq_error_codes.go。

示例输出:

text
发送文本群组信息失败: {"code":22009,"message":"频控"}
[QQ API 错误码 22009] 主动消息频控超限。排查建议:降低发送频率或等待配额恢复

未识别的错误码会提示查阅官方文档;无错误码的原始错误也会打印,方便调试。

idmap ​

旧版单库 idmap.db 拆为:

文件用途
idmap-identity.dbOpenID/rUIN/raw 与内部 vUIN 的身份关系(实际运行)。
idmap-msg.dbQQ message_id 与虚拟 message_id 的临时映射。
idmap.db旧库,仅作为一次性迁移源。

可通过 go build -tags map_idmap 启用 openid-map.db / msgid-map.db 多桶架构(v2 重构方案),两者的对外行为完全一致。详见 idmap 文档。

迁移阻塞式(2026-08-01 修复) ​

StartMigration 已改为阻塞式迁移(此前为 go backgroundMigration() 非阻塞)。迁移完成才返回,确保 main.go 调用点之后才连 WS / 启动 HTTP,避免迁移期间 storeIdentity 双写与 backgroundMigration 迁入并发产生 2 row → 1 OpenID 重复映射。同时 writeBatchToNewDB 追加按 value 委重(逆向条目 uin:row-* 迁入前查正向 uin:<OpenID> 是否已存在,若已有则跳过)。

强制解绑工具(2026-08-01 新增) ​

迁移异常或历史数据残留导致 2 个虚拟 ID 指向同一 OpenID 时,可用强制解绑工具按 OpenID 直接清理全部映射:

  • ForceUnbindID(id):Go 内部函数,入参支持OpenID 字符串或虚拟 ID两种形式。OpenID 时直接删正向 + 扫删所有指向同一 OpenID 的逆向条目;虚拟 ID 时先通过逆向条目 uin:row-<N> 反查 OpenID(新库+旧库回退),再按 OpenID 清理。返回清理条数
  • getid type=18:HTTP API GET /getid?type=18&id=<OpenID或虚拟ID>,返回 {"status":"success","unbound_count":N}
  • UpdateVirtualValue(old, 0) 解绑行为修复:newRowValue=0 解绑时已同步彻底清理正向 + 重复逆向条目(此前只删单条逆向不删正向,导致解绑失效)

2026-08-01 修复: ForceUnbindID 原仅接受 OpenID 入参,用户传虚拟 ID 时查正向 uin:<虚拟ID> 找不到直接返回 unboundCount=0(表现为 getid type=18 返回 false)。现已支持双形式入参:纯数字视为虚拟 ID 先反查 OpenID,非纯数字视为 OpenID 直接清理。

解绑后该 OpenID 彻底无映射,下次 storeIdentity 会重新分配唯一虚拟 ID。详见 idmap 文档。

规则:

  • 下游默认继续使用内部 vUIN。
  • QQ OpenID 身份键为 openid:QQ:<appid>:<openid>。
  • q群、私聊 (C2C)、好友事件、群成员事件共用同一 QQ OpenID 身份键。
  • message_id 默认保留 1 小时,由 msgid_ttl_seconds 控制。

相关配置:

yaml
op_userid_type: vuin
msgid_ttl_seconds: 3600

lazy_message_id 多段回复 msgseq 去重修复(2026-08-02) ​

文件: echo/messageidmap.go

lazy_message_id: true 下一条命令触发多段独立回复时,偶发某一段发送失败,QQ API 返回 {"message":"消息被去重,请检查请求msgseq","code":40054005}。

根因: GetLazyMessagesId / GetLazyMessagesIdv2 选中一条 record 后执行 usageCount++,导致同一条命令的多段回复每次调用都选中不同的 record(下次该条 usageCount!=0,选中另一条),两段拿到不同 msg_id。但 QQ API 视两 msg_id 为同一回复链(都回复同一 event),要求 msg_seq 在同一回复链内连续递增。GetMappingSeq 按 msg_id 字符串做 key 存 seq,两段拿到不同 msg_id 时第二段查不到第一段的 seq,seq 与第一段冲突,判去重 40054005。

修复: 移除 GetLazyMessagesId / GetLazyMessagesIdv2 选中后的 usageCount++,让同一回复链的多段回复都拿到同一 msg_id,配合 GetMappingSeq / AddMappingSeq 在该 msg_id 上连续递增 msg_seq。

关联 Issue: Te-River/Gensokyo-NewQQ#19

事件字段 ​

sender.nickname ​

event.sender.nickname 优先级:

  1. card_nick 配置。
  2. QQ API 的 Author.Username。
  3. 空字符串。

q群 sender.role ​

范围:q群 (Group Chat)

q群消息优先使用 QQ API 的 member_role:

member_rolesender.role
ownerowner
adminadmin
membermember

没有 member_role 时,再回退到 master_id 判断。

event_id ​

已存储 event_id 的入口:

事件处理入口
INTERACTION_CREATEProcessInlineSearch
C2C_MSG_RECEIVEprocess_c2c_switch
FRIEND_ADDprocess_friend
GROUP_ADD_ROBOTProcessGroupAddBot
GROUP_MSG_RECEIVEProcessGroupMsgReceive
GROUP_MSG_REJECTProcessGroupMsgReject
GROUP_MEMBER_ADDProcessGroupMember

GROUP_JOIN_REQUEST ​

范围:q群 (Group Chat)

GROUP_JOIN_REQUEST 转 request.group 的字段对齐官方现文档:

  • apply_at:官方为 RFC3339 字符串,按 RFC3339 解析为秒级时间戳(保留历史数字时间戳兼容)。
  • auto_approved:官方为对象({strategy_id},仅下行事件携带),改为对象透传;新增布尔伴随字段 auto_approved_bool(对象是否存在)。

日志与监控 ​

mylog ​

当前内置日志支持:

  • 控制台按级别着色:log_color_enabled。
  • 控制台日志格式:2006/JAN/02 PM 03:04:05 [INFO ] [module] message。
  • 本地文件日志默认关闭,save_logs: true 或启动参数 run --local-logger=enable 均可启用。
  • 文件日志写入 log/ 下的纯文本文件,文件名使用 UTC 时间。
  • log_suffix_per_mins 控制时间切分,log_max_size_mb 控制单文件大小轮转。
  • log_max_age_days 控制最大保留天数,log_keep_files 控制本地旧日志文件最大保留个数。

慢事件与 /metrics ​

runWithTimer 会按 log_slow_event_threshold_ms 判定慢事件。/metrics 输出:

  • gensokyo_uptime_seconds
  • gensokyo_messages_received_total
  • gensokyo_messages_sent_total
  • gensokyo_errors_total
  • gensokyo_slow_events_total
  • gensokyo_memory_allocated_bytes

Master 指令 ​

范围:进入 HandleFrameworkCommand 的消息事件。

权限:

  • master_id 为空数组时不限制。
  • 否则要求真实用户 ID 或虚拟用户 ID 命中 master_id。

指令:

配置项默认值行为
status_prefix/gskstatus返回版本、收发计数、慢事件/错误计数、内存、协程数、运行时间。
broadcast_prefix/gskbroadcast向 idmap 中的 q群提交广播。
me_prefix/me查询当前用户的真实 ID / 虚拟 ID 映射关系(idmaps-pro 模式下显示对应关系)。

指令前缀设为 ""、/disabled 或 /disabled... 时视为禁用。

架构 ​

arch_mode 分层架构接入 ​

生产主链已接入 internal/(application/、domain/、infrastructure/)+ adapter/(qq/、onebot/、media/、state/、identity/)分层架构,与 legacy 两路同时装配,按 arch_mode 逐请求切换:

配置项默认值可选值行为
arch_modenewlegacy / shadow / newnew 走新链;shadow 新旧并行、差异仅日志上报、行为仍走 legacy;legacy 全走旧链,一键回退。
  • 逐请求读取,热重载即时生效;空值/非法值回退 new。
  • 六个接缝(出站/入站/action/state/config/media)均保留 legacy 回退:新链未注入或失败时自动落回旧链。

构建 ​

CI 使用静态编译配置 CGO_ENABLED=0,降低 GLIBC 版本依赖。

构建版本由 -ldflags 注入 buildinfo.BuildType 和 buildinfo.BuildSpec,显示为 <buildtype>-<build_spec>,例如 git-2d643f4。未注入时回退为 dev-<UTC 时间戳>.<毫秒>。启动命令 version、OneBot get_version_info 和 /gskstatus 使用同一版本值。

build.ps1 同时支持单平台构建和 -All / -LinuxOnly 多平台构建;build_all.ps1 仅保留为兼容入口。

修复 ​

  • GetRemoveBotAtGroup 误删用户主动发送的 @。
  • 入站 @bot 剥离逻辑改用 Mentions[i].OpenID。
  • ParseAndHandle 增加错误日志和 panic 恢复。
  • 按钮零值字段序列化丢失。
  • C2C 按钮指定用户权限导致发送失败。
  • send_private_msg_wakeup 遍历 foundItems 时未跳过 "active" key,导致 [CQ:active] 标记被误当作媒体类型发送。
  • Markdown 消息中 [CQ:at] 写在文本段(messageText)时未合并到 Markdown 内容,导致 @ 标签丢失。已修复:HandleSendGroupMsg 中对 messageText 也调用 ResolveMarkdownAtMentions,将 [CQ:at,qq=数字] 转换为 <qqbot-at-user> 后合并到 Markdown 内容头部。
  • [CQ:video] string 格式缺少 base64:// 和 file:// 正则,视频被当作普通文本发送。已新增 base64VideoPattern 和 localVideoPattern。
  • 无前缀的 CQ 码(如 [CQ:image,file=filename.png])被收集到 unknown_* 后静默丢弃。已添加 fallback 处理,作为 URL 媒体尝试发送。
  • keyMap 中 "embed" 无任何写入路径,移除消除潜在空消息风险。
  • TRSS 格式(map[string]interface{})缺少 case "reply" 和 case "avatar" 处理,已补充。
  • HTTP API 端口 (http_address) 绑定失败时不再终止进程。Windows 上端口被 Hyper-V/系统保留时,log.Fatalf 会杀死整个进程(尽管主 Gin 服务器和 WebSocket 已正常运行),已改为 mylog.Printf 仅记录错误。
  • /me 命令中不必要的 err != nil 检查导致 ID 映射失败时误报错误并退出。/me 是状态查询命令,映射失败不应阻断执行,已移除多余检查。
  • 私聊图文混合消息(imageCount == 1 && messageText != "")对 groupReply 只做 *dto.RichMediaMessage 断言:local_image / base64_image 等在 generatePrivateMessage 内已完成上传、返回的是 *dto.MessageToCreate,断言失败后直接 return "", nil,既未发送也未回执,插件侧表现为 WebSocket call api send_msg timeout。已补齐 *dto.MessageToCreate 兜底断言(与群聊路径一致),并在发送失败时分类处理(22009 / 40034025 / 超时重试)后统一回执,避免客户端超时。
  • 入群/退群通知回复被误判为群私聊:ProcessGroupMember.go 生成虚拟 group_id 时未登记消息类型(常规群消息会写 echo.AddMsgType(..., "group") 与 idmap.WriteConfigv2(id, "type", "group")),导致针对 GROUP_MEMBER_ADD/REMOVE 通知回包的 send_group_msg 被 send_group_msg.go 兜底误判为 group_private,把群 OpenID 当用户走 C2C 接口报 11255 请求的资源不存在(用户/群已注销)。已补上与常规群消息一致的 group 类型登记。
  • get_group_bot_state 字段名与官方不符(join_time/can_push/push_msg_setting/role),按官方字段取值恒空。已改为官方字段 joined_at/allow_proactive_msg/recv_msg_setting/member_role,旧字段保留为别名。
  • get_group_join_request_list 请求/响应字段与官方不符(next_index→cursor、join_requests→list),且 apply_at 类型不符(int→RFC3339 字符串)。已对齐官方,旧字段保留为兼容别名。
  • GROUP_JOIN_REQUEST 的 auto_approved 官方为对象而 DTO 为 bool,导致事件解析失败。已改为对象透传,并新增布尔伴随字段 auto_approved_bool。

Keyboard 按钮本地图片 ​

范围:q群 (Group Chat), 私聊 (C2C)

按钮 render_data.label 和 render_data.visited_label 中的本地图片路径(如 ![text #20px #20px](D:\path\to\cover.png))现在也会被自动上传到图床并替换为 CDN URL,与 markdown 内容中的图片处理保持一致。

Gensokyo-NewQQ 配套 Web 文档站