From 8c3ecb89df7479ee9f4d00b3d8aafe765752ed17 Mon Sep 17 00:00:00 2001 From: oliveryhwu Date: Sat, 7 Mar 2026 05:14:50 +0800 Subject: [PATCH 1/2] =?UTF-8?q?fix:=20=E4=BF=AE=E5=A4=8D=20msgid=20?= =?UTF-8?q?=E5=8F=AF=E8=83=BD=E7=A2=B0=E6=92=9E=E7=9A=84=E9=97=AE=E9=A2=98?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/api.ts | 33 +++++++++++---------------------- 1 file changed, 11 insertions(+), 22 deletions(-) diff --git a/src/api.ts b/src/api.ts index 041fe72..ab11dd2 100644 --- a/src/api.ts +++ b/src/api.ts @@ -143,33 +143,22 @@ export function getTokenStatus(): { status: "valid" | "expired" | "refreshing" | } /** - * msg_seq 追踪器 - 用于对同一条消息的多次回复 - * key: msg_id, value: 当前 seq 值 - * 使用时间戳作为基础值,确保进程重启后不会重复 + * msg_seq 全局递增计数器 + * QQ 服务端要求同一个发送目标(用户/群)内 msg_seq 唯一 + * 使用时间戳作为基础值 + 全局递增,确保: + * 1. 进程重启后不会与之前的 seq 重复 + * 2. 不同 msg_id 的回复也不会产生相同的 seq */ -const msgSeqTracker = new Map(); const seqBaseTime = Math.floor(Date.now() / 1000) % 100000000; // 取秒级时间戳的后8位作为基础 +let globalSeqCounter = 0; /** - * 获取并递增消息序号 - * 返回的 seq 会基于时间戳,避免进程重启后重复 + * 获取并递增消息序号(全局唯一) + * @param _msgId - 保留参数,不再用于分桶计数 */ -export function getNextMsgSeq(msgId: string): number { - const current = msgSeqTracker.get(msgId) ?? 0; - const next = current + 1; - msgSeqTracker.set(msgId, next); - - // 清理过期的序号 - // 简单策略:保留最近 1000 条 - if (msgSeqTracker.size > 1000) { - const keys = Array.from(msgSeqTracker.keys()); - for (let i = 0; i < 500; i++) { - msgSeqTracker.delete(keys[i]); - } - } - - // 结合时间戳基础值,确保唯一性 - return seqBaseTime + next; +export function getNextMsgSeq(_msgId: string): number { + globalSeqCounter++; + return seqBaseTime + globalSeqCounter; } // API 请求超时配置(毫秒) From c16158597aa78b775917839557553a1c42024bd4 Mon Sep 17 00:00:00 2001 From: oliveryhwu Date: Sat, 7 Mar 2026 05:31:49 +0800 Subject: [PATCH 2/2] =?UTF-8?q?feat:=20seq=E6=94=B9=E4=B8=BA=E6=AF=AB?= =?UTF-8?q?=E7=A7=92=E9=80=92=E5=A2=9E=E5=B9=B6=E5=A2=9E=E5=8A=A0=E9=9A=8F?= =?UTF-8?q?=E6=9C=BA=E6=95=B0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/api.ts | 18 +++++------------- 1 file changed, 5 insertions(+), 13 deletions(-) diff --git a/src/api.ts b/src/api.ts index 80841dd..626b290 100644 --- a/src/api.ts +++ b/src/api.ts @@ -144,22 +144,14 @@ export function getTokenStatus(): { status: "valid" | "expired" | "refreshing" | } /** - * msg_seq 全局递增计数器 - * QQ 服务端要求同一个发送目标(用户/群)内 msg_seq 唯一 - * 使用时间戳作为基础值 + 全局递增,确保: - * 1. 进程重启后不会与之前的 seq 重复 - * 2. 不同 msg_id 的回复也不会产生相同的 seq - */ -const seqBaseTime = Math.floor(Date.now() / 1000) % 100000000; // 取秒级时间戳的后8位作为基础 -let globalSeqCounter = 0; - -/** - * 获取并递增消息序号(全局唯一) + * 获取全局唯一的消息序号(范围 0 ~ 65535) + * 使用毫秒级时间戳低位 + 随机数混合,确保唯一性 * @param _msgId - 保留参数,不再用于分桶计数 */ export function getNextMsgSeq(_msgId: string): number { - globalSeqCounter++; - return seqBaseTime + globalSeqCounter; + const timePart = Date.now() % 100000000; // 毫秒时间戳取模 60000(0~59999) + const random = Math.floor(Math.random() * 65536); // 0~65535 + return (timePart ^ random) % 65536; // 异或混合后限制在 0~65535 } // API 请求超时配置(毫秒)