Skip to content

Releases

知弦 0.54.0

Choose a tag to compare

@araea araea released this 28 Oct 10:57

知弦 0.54.0:群资源带上人数

  • guild.get、guild.list 返回的群资源多带两个扩展字段:member_count(现有人数)、max_member_count(人数上限)。人数原本就在内核的群列表缓存里,只是转成 Satori 形状时丢掉了,这次不多发任何请求;读不出时不带
  • 配合 acumen 1.77.0 起的群目录:连上时翻一遍群列表,就把每个群的名字和人数都记下了。欢迎词里的 {member_count} 原先从知弦这儿拿不到数字、总是空的,现在有了,而且不用为每个新人现问
  • 需要重启手机后生效

知弦 0.53.3

Choose a tag to compare

@araea araea released this 28 Oct 09:10

知弦 0.53.3:入群事件复查补两处

  • 新人的号码一时换不出、要重拉成员表时,原先在共用的定时线程上同步等(最多 4 秒);同时进来好几个人就会把线程占满,连回声过期这类别的定时任务都跟着慢。现在改到后台拉(同一个群同时只拉一份),之后按 0.5、1、2、4、8、12、20、60 秒查缓存,通常一秒内就能补发
  • 「A 邀请 B 加入了群聊」这种灰条,原先取第一个人,会把邀请人 A 当成新人去欢迎。现在取「邀请」后面的那个人

知弦 0.53.2

Choose a tag to compare

@araea araea released this 28 Oct 09:00

知弦 0.53.2:入群事件少走两步

接着 0.53.1,把入群事件发出之前的每一步再削一遍:

  • 入群灰条里本来就带着号码(uin 字段或名片链接里的 uin=)时直接用号码,不再先拿 uid 去资料服务换——新人刚进群时那一换常常换不出来,0.53.1 之前就是这一步把整条事件拖到成员列表回调
  • 灰条带着新人的昵称时,发事件前不再查成员表与资料服务补名字
  • 0.53.1 的挂起补发照旧兜底:号码真要换的时候立刻重拉成员表,新人先开口就在他的消息之前补发

知弦 0.53.1

Choose a tag to compare

@araea araea released this 28 Oct 08:56

知弦 0.53.1:新人进群就有入群事件,不用等他说完好几句

问题

入群欢迎偶尔要等新人在群里说了好几句之后才出来。原因在实现端:QQ 推来的入群灰条只带新人的 uid,刚进群的人资料服务里常常还换不出 QQ 号;换不出时这条入群通知被整条丢掉,只能等成员列表变化回调补发——那条回调来得很慢,新人往往已经说了五六句。

客户端这边看到的顺序也就反了:先收到新人的消息,过很久才收到「他进群了」。

改法

  • 认不出号码的入群通知不再丢掉,先挂起,带着灰条里的昵称:
    • 立刻、1 秒、3 秒、8 秒、20 秒、60 秒各再查一次号码,第一次与第四次顺带向服务器重拉一份成员表(新人通常要重拉后才在表里)
    • 这个人先在群里开口的话,消息里本来就有他的号码:在这条消息之前补发入群事件,客户端总是先看到「进群」、再看到他说的话
    • 挂着的时候他退群了,这条入群直接作废,不会退群之后才冒出一条「进群」
  • 成员列表回调仍然兜底;两路谁先到都只投递一次(沿用 0.49.2 的统一去重)
  • 挂起超过 10 分钟还认不出人就放弃,不发 user_id=0 的假事件

知弦 0.53.0

Choose a tag to compare

@araea araea released this 27 Oct 21:01

知弦 0.53.0:群管理补齐,资料卡点赞回来了

群管理

  • 群详情 group_detail:群主、建群时间、人数与上限、群介绍、全员禁言到几时,以及本号在群里的身份、入群时间、接收方式、是否置顶,整理成固定字段
  • 群设置 group_settings(群主或管理员):加群方式、入群问题与答案、成员能否邀请、能否被搜到、发言频率、群介绍。只改带了的那几项,其余不动;什么都不带就读当前设置
  • 批量踢人 / 批量禁言 group_kick / group_mute:一次处理多人,踢人可带理由、可拒绝再次申请
  • 转让群 group_transfer、解散群 group_destroy(要再写一遍群号确认)
  • 群消息接收方式 group_msg_mask:接收并提醒、接收不提醒、收进群助手、屏蔽
  • 会话置顶 session_top、拉黑 friend_block
  • 一次改本号在多个群的群名片:card 带 guild_ids[] 或 all: true,每个群单独限频,回执逐群列出

账号资料

  • 一次改几项资料 profile_set:昵称、签名、性别、生日
  • 在线状态 online_status:在线、离开、隐身、忙碌、Q我吧、请勿打扰
  • 资料详情 user_info:昵称、备注、签名、性别、年龄、生日、地区、学校、注册时间、会员与等级,整理成固定字段
  • 资料卡点赞 like:0.23 去掉钩子时一起下线的点赞回来了。改由本端编码手机 QQ 的 VisitorSvc.ReqFavorite(JCE),经已有的裸 SSO 通道收发,不再需要钩子;失败时带服务端原文(比如对方设置了不许赞)

以上都在 internal/capabilities 的目录里,踢、禁言、转让、解散、群设置归为 manage。

验证

JVM 单元测试 37 项通过(新增 JCE 编解码与能力目录的用例)。新动作的内核调用签名按 QQ 9.3.80 安装包核对,群设置的写法照 QQ 自己的 TroopOperationRepo;要重启手机让新 .so 生效后才能在真机上跑。加群方式的名字与数字的对应、点赞的 WUP 包在真机上还没跑过。

知弦 0.52.0

Choose a tag to compare

@araea araea released this 27 Oct 20:05

知弦 0.52.0:转发、语音转文字、群荣誉、WebHook,以及 QQ 升级后的自检

新能力

  • 原生转发 internal/message_forward:把已有消息原样转到别的群或私聊(可多个目标),走 QQ「转发」菜单那条内核调用,图片、文件、语音不重新上传
  • 语音转文字 internal/voice_to_text:借 QQ 自带的「转文字」听写一条语音,结果写回那条消息,再问直接读缓存
  • 待处理申请 internal/request_list:还没处理的好友申请、入群申请与群邀请,形状与推送事件相同,客户端重启后能补上错过的
  • 群荣誉榜 group_honor:龙王、群聊之火、群聊炽焰、冒尖小春笋、快乐源泉。群荣誉页已改成前端渲染,改读它背后的 qunapp/honor_* 接口
  • 名片卡 contact_card 与消息元素 <satori-qq:contact type="group|user" id="…"/>:发「推荐群 / 推荐好友」卡片
  • 收藏表情 fav_emoji_list
  • 以上都在 internal/capabilities 的目录里,acumen 这类按目录办事的客户端不用改代码就能用

协议对齐

  • 实现了元信息 API 的 meta/webhook.create / webhook.delete:事件原样 POST 到登记的地址,带 Satori-Opcode 与 Authorization: Bearer,与官方服务端同形;登记写盘,QQ 重启后接着推

修正

  • 网页接口的 bkn 先按 p_skey 前 10 个字符算(qun.qq.com 前端自己的写法),整串算出来的会被荣誉接口判 csrf error;群精华、群公告同样受益
  • 群文件列表:去掉按 UTF-8 硬解出来的二进制 md5 / sha;上传时间从内核误放的 uploadedSize 归位到 uploadTime;limit 真的按条数截断;走了内核路径时说明 0x6D8_1 为什么没用上(oidb_error)

QQ 升级适配

  • 模块记住上次运行的 QQ 版本,升级后第一次自检就在日志里写清「从哪版到哪版、接口面过了多少、缺什么」,/healthz 在升级后 7 天内带 qq_upgrade
  • 反射找不到方法时,错误里直接写着新版同名方法的真实签名,也汇总在 internal/compat 的 observed.signature_mismatches
  • scripts/qq-upgrade.sh:一条命令存新安装包、与上一版做 dex-diff、强制自检、清旧安装包;scripts/dex-sig.py 按类名或方法名查安装包里的签名

验证

JVM 单元测试 36 项与只读协议探针 157 项通过(探针跑的是线上 0.51.0);群荣誉与 bkn 口径用真账号的网页凭据实测过。新动作的内核调用(forwardMsg、translatePtt2Text、名片卡、收藏表情)签名按 9.3.80 安装包核对,要重启手机让新 .so 生效后才能在真机上跑。

知弦 0.51.0

Choose a tag to compare

@araea araea released this 27 Oct 16:15

知弦 0.51.0:群文件、群精华、群公告读得出来了

现象

  • group_file_list 对任何群都回空列表(all_file_count: -1),像是群里没有文件
  • group_essence_list、group_bulletin_get 一律报 pskey unavailable: code=-1 no valid local guest ticket

原因

  • p_skey 一直取错了地方:0.41 起退到登录服务的 guestA2ToPskey,它只给访客模式发票,正常登录的账号永远拿不到。依赖它的群精华、群公告(读与发)、发说说全部失效
  • 群文件列表的内核请求漏填了 sortType(枚举字段,null 交给了 native),内核不回包;回包又按「第一条目的群号」分发,空文件夹、出错的回包没有条目,永远送不到——调用方等满 15 秒拿回空列表
  • 精华读的是一个不存在的网页接口;公告接口的群号参数名写错(group_code,应为 qid),条目字段(feeds / inst)也认错了

改法

  • p_skey 依次取:票据管理器缓存 TicketManager.getPskey(uin, domain) → QQ 网页业务自己用的 IPskeyManager.getPskey(String[], cb)(回调是混淆接口,按方法签名取类型、按参数形状认成败)→ 访客票。网页凭据另带 TicketManager.getSkey,bkn 先按 skey 算、被拒再按 p_skey 算
  • 群精华改走 qun.qq.com/cgi-bin/group_digest/digest_list,按页翻(page、page_limit);群公告改走带 qid 的 announce/get_t_list,置顶的在前
  • 两者回包规整成固定字段(谁发的、什么时候、谁加的精、正文、图片),公告正文解开 HTML 转义;失败带 error 与原回包 raw
  • 群文件列表先发 OidbSvcTrpcTcp.0x6D8_1(一问一答,不靠监听器),回不来或解出零条再走内核;内核请求补上 sortType、sortOrder,回包按请求号收进信箱再取。条目统一成内核 GroupFileInfo / GroupFolderInfo 的字段名,外加 type 与回执里的 source
  • internal/compat 的 pskey 一项改为检查 IPskeyManager.getPskey

验证

真机验收要重启手机让新 .so 生效后做:internal/call 依次调 pskey(domains=["qun.qq.com"])、group_essence_list、group_bulletin_get、group_file_list(guild_id 取一个有文件的群)。

知弦 0.50.0

Choose a tag to compare

@araea araea released this 27 Oct 14:13

知弦 0.50.0:身份查询不再报旧的、不再冒充「普通成员」

现象

账号已经是群管理员,acumen 的入群验证却判定「不是管理员」、整个跳过。

原因

  • 成员表拉下来之后就不再刷新:账号后来被设成管理员,guild.member.get 问自己,答的仍是几小时前那一行里的「普通成员」
  • 成员表一时拉不下来(内核 4 秒没回调)时直接报错,调用方只能当自己不是管理员
  • 消息事件里,发言者的身份说不准时(成员表还没拉)写成 member,接收方以为是查过的结果
  • 内核推来的成员资料变更(onMemberInfoChange)没有接,身份变了没人知道

改法

  • 账号本人的身份改读群资料里的 memberRole:群资料由内核推送维护,被设成管理员时立刻跟着变;guild.member.get 问自己、消息事件里自己的发言都以它为准
  • 成员表一时拉不下来、问的又是账号自己时,先答身份(名字留空),不再报错
  • 成员表拉了超过 10 分钟,guild.member.get 照常用缓存回答,同时后台向服务器要一份新的
  • 接上 onMemberInfoChange:变更并进成员表;任何人身份变了(含账号自己),推 guild-member-updated,带 _type=satori-qq/role、_data.role 与 member.roles
  • 发言者身份说不准时消息事件不带 member.roles,不再写成 member

知弦 0.49.2

Choose a tag to compare

@araea araea released this 27 Oct 13:07

知弦 0.49.2:新人进群就欢迎,不再等慢回调

新版 JSON 入群灰条被丢掉了

QQ 9.3.80 会用 subElementType=17 / busiId=1022(RELATION_GROUP_MEMBER_ADD)通知成员入群。实现端原来只从 JSON 灰条里识别戳一戳,入群则依赖 XML、groupElement 或 onMemberListChange:

  • 即时到达的 JSON 入群通知被记为 unparsed grayTip 后丢弃
  • 只能等较慢的成员列表变化回调补发 guild-member-added
  • 下游欢迎逻辑本身没有合并等待,因此看到的就是「QQ 已显示新人进群,欢迎却很久才到」

改法

  • 识别 1022,同时覆盖同语义的 1015 / 1016 入群变体,从 items[] 的 uid(必要时从 uin 或名片链接)定位新成员
  • 灰条里的显示昵称直接放进 member_name,不必为了欢迎再补一次资料查询;uid → QQ 号暂时解析失败时不制造 user_id=0 的假事件,仍由成员列表回调兜底
  • 成员事件去重移到统一事件出口:即时灰条与慢成员列表回调无论谁先到,同一次入群只投递一次;相反方向会解除旧状态,退群后重新入群不会被吞
  • 不增加轮询,也不在欢迎链路新增等待或网络 API 调用

知弦 0.49.1

Choose a tag to compare

@araea araea released this 27 Oct 07:46

知弦 0.49.1:发出去的 @ 是真 @

atNtUid 一直是空的

发消息时 <at id="123456"/> 一直带着 atType=2 与 atUid,但 atNtUid(QQ 的 uid)只从消息缓存里取。缓存里只记着在本进程里发过言的人,于是:

  • 新入群的成员还没说过话,缓存里没有他的 uid,欢迎里的 @ 落下去是一段普通文字:客户端里点不开,对方也收不到提醒
  • 消息发出去以后,这一段就定型了,不会再被追认成真 @

改法

  • 缺 uid 时按「成员表缓存 → 资料服务」补齐,两步都是本地调用,不联网、不增加往返;补到就记进缓存,同一个人后续的 @ 直接命中
  • 灰条里的成员变动(入群、退群、禁言、踢人)本来就带着当事人的 uid,转事件时顺手把 uin ↔ uid 记下来。入群欢迎紧接着发出的 @ 因此不必再查一次
  • @ 后面的文字取成员的群名片或昵称(元素里带了 name 就用它),取不到才退回号码
  • dumpSent 的诊断行带上 atType / atUid / atNtUid,能直接看出这一段是不是真 @

配合 0.48.2 的入群事件补全,客户端收到 guild-member-added 就能立刻发出真 @,不必先等 guild.member.get。