@napuketto/adapter 0.1.0 → 0.2.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -636,7 +636,13 @@ declare function escapeCqParam(value: string): string;
636
636
  declare function unescapeCqText(text: string): string;
637
637
  /** 编码单个 CQ 码(undefined 参数自动省略)。 */
638
638
  declare function encodeCqCode(type: string, params: Record<string, string | number | undefined>): string;
639
- /** 解析一条消息文本 → 文本片段与 CQ 码片段的混合数组(文本已反转义)。 */
639
+ /**
640
+ * 解析一条消息文本 → 文本片段与 CQ 码片段的混合数组(文本已反转义)。
641
+ *
642
+ * 手工 indexOf 扫描而非全局正则(/\[CQ:[^\]]*\]/g):消息文本不可信,正则在
643
+ * 「大量 [CQ: 前缀且无闭合 ]」的输入下每个起点都全量回扫,复杂度 O(n²)
644
+ * (CodeQL polynomial-redos)。indexOf 语义等价:从 [CQ: 起到其后第一个 ]。
645
+ */
640
646
  declare function parseCqMessage(text: string): Array<string | CqCode>;
641
647
  /** 将混合片段数组序列化回消息文本(parseCqMessage 的逆操作)。 */
642
648
  declare function serializeCqParts(parts: Array<string | CqCode>): string;
@@ -766,6 +772,11 @@ declare class NapukettoOneBot11Adapter extends BaseProtocolAdapter<OB11Config> {
766
772
  subscribeOnly(): Promise<void>;
767
773
  /** 仅退订(与 subscribeOnly 配对的进程级清理;传输关闭仍走 stop())。 */
768
774
  unsubscribeOnly(): void;
775
+ /**
776
+ * 单条 sysmsg 字节块:解码 → 识别 → 命中才广播,未识别打结构化校准日志
777
+ * (识别表为空时全部走日志路径,design.md §7.5)。
778
+ */
779
+ private handleSysMsgBlob;
769
780
  /**
770
781
  * 广播消息事件(P2-19:接收方向 ID 转换)。
771
782
  * 收集 at uid → 一次批量 uidToUin → 构造上下文 → 翻译广播。
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.mts","names":[],"sources":["../../src/onebot11/helper/message-unique.ts","../../src/onebot11/api/one-bot-api.ts","../../src/onebot11/action/error-map.ts","../../src/onebot11/action/group/get-group-at-all-remain.ts","../../src/onebot11/types/index.ts","../../src/onebot11/event/base.ts","../../src/onebot11/event/message.ts","../../src/onebot11/event/meta.ts","../../src/onebot11/event/notice.ts","../../src/onebot11/event/request.ts","../../src/onebot11/event/index.ts","../../src/onebot11/action/index.ts","../../src/onebot11/helper/codec/cqcode.ts","../../src/onebot11/helper/codec/segment.ts","../../src/onebot11/helper/config.ts","../../src/onebot11/helper/data.ts","../../src/onebot11/adapter.ts","../../src/onebot11/transport.ts"],"mappings":";;;;;;cAkBa;;mBAEQ;;mBAEA;;mBAEA;;UAET;;mBAES;;;;;EAMjB,MAAM,eAAe,OAAO;;EAmB5B,SAAS;;EAKT,aAAa;;EAKb,QAAQ,gBAAgB;;EAKxB,QAAQ;;UAYA;;;;;UCvDK;;EAEb;;EAEA,mBAAmB;;EAEnB;;EAEA;;EAEA,aAAa;;EAEb,gBAAgB;;;UAIH;EACb,QAAQ;EACR,UAAU;EACV,gBAAgB;EAChB,WAAW;EACX,WAAW;EACX,cAAc;EACd,YAAY;EACZ,gBAAgB;EAChB,QAAQ;;EAER;IAAQ;IAAa;;;EAErB,QAAQ;;EAER,aAAa;;;;;;cAOJ;;WAEA,QAAQ;;WAER,UAAU;;WAEV,gBAAgB;;WAEhB,WAAW;;WAEX,WAAW;;WAEX,cAAc;;WAEd,YAAY;;WAEZ,gBAAgB;;WAEhB,QAAQ;;WAER,YAAY;;WAEZ,eAAe;;WAEf;IAAQ;IAAa;;;WAErB;;WAEA;;WAEA,mBAAmB;;WAEnB;;WAEA;;WAEA,aAAa;;WAEb,gBAAgB;EAEzB,YAAY,MAAM;;EAuBlB,SAAS,iBAAiB,QAAQ;;EAKlC,SAAS,iBAAiB,QAAQ;;;;;cCzHzB,kBAAkB;;;;UCMd;EACb;EACA;EACA;;;;;;;;;UCba,WAAW;EACxB;EACA;EACA,MAAM;EACN;EACA;;;KAIQ;EACJ;EAAc;IAAQ;;;EACtB;EAAY;IAAQ;IAAY;;;EAChC;EAAc;IAAQ;;;EACtB;EAAe;IAAQ;IAAc;;;EACrC;EAAgB;IAAQ;IAAc;;;EACtC;EAAe;IAAQ;IAAc;;;EACrC;EAAc;IAAQ;;;EACtB;EAAe;IAAQ;;;EACvB;EAAiB;IAAQ;;;EACzB;EAAc;IAAQ;;;EACtB;EAAa;IAAQ;;;EACrB;EAAgB;IAAQ;;;;KAGpB,uBAAuB;;UAGlB;EACb;EACA;EACA;EACA;;;UAIa;EACb;EACA;EACA;EACA;;;UAIa;EACb;EACA;;;UAIa;EACb;EACA;EACA;EACA;IACI;IACA;IACA;IACA;;EAEJ;EACA,SAAS;EACT;EACA;EACA;;;KAIQ;;UAGK;EACb;EACA;EACA;EACA;EACA,MAAM;EACN;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;;EAEA;EACA;EACA;EACA;EACA;EACA;;;UAIa;EACb;EACA;EACA;;;UAIa;EACb;EACA;EACA,MAAM;EACN;;EAEA;EACA;EACA;EACA;;;UAIa;EACb;EACA;EACA;EACA;EACA;;;UAIa;EACb;EACA;EACA;;;UAIa;EACb;EACA,oBAAoB;EACpB,gBAAgB;EAChB,gBAAgB;EAChB,aAAa;EACb,oBAAoB;EACpB,cAAc;;;UAID;EACb;EACA;EACA;;EAEA;EACA;EACA;EACA,eAAe;EACf,gBAAgB;EAChB;;;;;;;;;;;;KCtJQ;;UAGK;;EAEb;;EAEA;;EAEA,WAAW;;;;;UCPE;EACb;EACA;EACA;;;UAIa,oBAAoB;EACjC;EACA;;;UAIa,8BAA8B;EAC3C;EACA;;EAEA;EACA;EACA;EACA;EACA,SAAS;EACT;EACA;EACA,QAAQ;EACR,YAAY;;;UAIC,gCAAgC;EAC7C;EACA;;EAEA;EACA;EACA;EACA,SAAS;EACT;EACA;EACA,QAAQ;;EAER;;;KAIQ,mBAAmB,wBAAwB;;;;UC9CtC;EACb;EACA;GACC;;;UAIY,+BAA+B;EAC5C;EACA;EACA;;;UAIa,+BAA+B;EAC5C;EACA;;EAEA;EACA,QAAQ;;;KAIA,gBAAgB,yBAAyB;;;;UCvBpC;EACb;EACA;EACA;EACA;;;UAIa;EACb;EACA;EACA;;;UAIa;EACb;EACA;EACA;;;UAIa,mCAAmC;EAChD;EACA;EACA;EACA;EACA,MAAM;;;UAIO,kCAAkC;EAC/C;EACA;EACA;EACA;EACA;;;UAIa,qCAAqC;EAClD;EACA;EACA;EACA;EACA;EACA;;;UAIa,qCAAqC;EAClD;EACA;EACA;EACA;EACA;EACA;;;UAIa,gCAAgC;EAC7C;EACA;EACA;EACA;EACA;EACA;;EAEA;;;UAIa,iCAAiC;EAC9C;EACA;EACA;;;UAIa,mCAAmC;EAChD;EACA;EACA;EACA;EACA;EACA;;;UAIa,oCAAoC;EACjD;EACA;EACA;EACA;;;UAIa,iCAAiC;EAC9C;EACA;EACA;EACA;EACA;EACA;;;UAIa,oCAAoC;EACjD;EACA;EACA;EACA;EACA;EACA;EACA;;;UAIa,8BAA8B;EAC3C;EACA;EACA;EACA;EACA;EACA;;EAEA;;;UAIa,mCAAmC;EAChD;EACA;EACA;EACA,MAAM;;;UAIO,iCAAiC;EAC9C;EACA;EACA;EACA;;;UAIa,oCAAoC;EACjD;EACA;EACA;EACA;EACA;EACA,OAAO;;;UAIM,kCAAkC;EAC/C;EACA;EACA;EACA;EACA;;;KAIQ,kBACN,6BACA,4BACA,+BACA,+BACA,0BACA,2BACA,6BACA,8BACA,2BACA,8BACA,wBACA,6BACA,2BACA,8BACA;;;;UCpLW,+BAA+B;EAC5C;EACA;EACA;;EAEA;;EAEA;;;UAIa,8BAA8B;EAC3C;EACA;;EAEA;EACA;EACA;EACA;EACA;;;KAIQ,mBAAmB,yBAAyB;;;;KCiB5C,YAAY,mBAAoB,kBAAmB,mBAAoB;;;;UCyDlE;;EAEb,KAAK;;;iBAIO,yBAAyB,MAAM,iBAAiB;;;;;;;;;;;;;;UC9E/C;EACb;EACA,QAAQ;;;iBAII,aAAa;;iBAKb,cAAc;;iBAKd,eAAe;;iBASf,aACZ,cACA,QAAQ;;iBAeI,eAAe,eAAe,eAAe;;iBAkB7C,iBAAiB,OAAO,eAAe;;;;iBCsHvC,oBAAoB,UAAU;;;;cCxJjC,kBAAgB,EAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAuB3B,EAAA,KAAA;;KAGU,aAAa,EAAE,aAAa;;;;iBC/DxB,oBAAoB,UAAU,qBAAqB;;iBAYnD,oBAAoB,UAAU,uBAAuB;;iBAoIrD,oBAAoB,eAAe;;iBAkBnC,qBAAqB,UAAU;;iBAK/B,qBAAqB,eAAe;;;;UCvInC;EACb,KAAK,cAAc;EACnB,KAAK,cAAc;;;UAIN,+BAA+B;;EAE5C,QAAQ,eAAe;;EAEvB,aAAa;;EAEb,YAAY;;EAEZ,eAAe;;EAEf,gBAAgB;;EAEhB,mBAAmB;;EAEnB,SAAS;;;cAIA,iCAAiC,oBAAoB;WACrD;mBAEQ;mBACA;mBACA;mBACA;mBACA;mBACA;mBACA;;WAER,UAAU;UACX;UACA;UACA;UACA;UACA;;UAEA;;UAEA;EAER,YAAY,MAAM;;;;;UAwBJ;;UAYA;;UAyCN;;UAiBM;;UAaN;;;;;;EAoIF,iBAAiB;;EAUvB;;;;;;UAUc;;UA2BA;;UAiBA;;UA4BA;;UAiBN;;;;;EAWF,cAAc,cAAc,UAAU,wBAAwB;;;;;UC9YvD;;EAEb,SAAS;;EAET,YAAY;;EAEZ,aAAa;;;UAIA;;EAEb,QAAQ;;EAER,aAAa;;EAEb,gBAAgB,cAAc,UAAU;;EAExC;;;iBAiBY,uBAAuB,MAAM,gCAAgC"}
1
+ {"version":3,"file":"index.d.mts","names":[],"sources":["../../src/onebot11/helper/message-unique.ts","../../src/onebot11/api/one-bot-api.ts","../../src/onebot11/action/error-map.ts","../../src/onebot11/action/group/get-group-at-all-remain.ts","../../src/onebot11/types/index.ts","../../src/onebot11/event/base.ts","../../src/onebot11/event/message.ts","../../src/onebot11/event/meta.ts","../../src/onebot11/event/notice.ts","../../src/onebot11/event/request.ts","../../src/onebot11/event/index.ts","../../src/onebot11/action/index.ts","../../src/onebot11/helper/codec/cqcode.ts","../../src/onebot11/helper/codec/segment.ts","../../src/onebot11/helper/config.ts","../../src/onebot11/helper/data.ts","../../src/onebot11/adapter.ts","../../src/onebot11/transport.ts"],"mappings":";;;;;;cAkBa;;mBAEQ;;mBAEA;;mBAEA;;UAET;;mBAES;;;;;EAMjB,MAAM,eAAe,OAAO;;EAmB5B,SAAS;;EAKT,aAAa;;EAKb,QAAQ,gBAAgB;;EAKxB,QAAQ;;UAYA;;;;;UCvDK;;EAEb;;EAEA,mBAAmB;;EAEnB;;EAEA;;EAEA,aAAa;;EAEb,gBAAgB;;;UAIH;EACb,QAAQ;EACR,UAAU;EACV,gBAAgB;EAChB,WAAW;EACX,WAAW;EACX,cAAc;EACd,YAAY;EACZ,gBAAgB;EAChB,QAAQ;;EAER;IAAQ;IAAa;;;EAErB,QAAQ;;EAER,aAAa;;;;;;cAOJ;;WAEA,QAAQ;;WAER,UAAU;;WAEV,gBAAgB;;WAEhB,WAAW;;WAEX,WAAW;;WAEX,cAAc;;WAEd,YAAY;;WAEZ,gBAAgB;;WAEhB,QAAQ;;WAER,YAAY;;WAEZ,eAAe;;WAEf;IAAQ;IAAa;;;WAErB;;WAEA;;WAEA,mBAAmB;;WAEnB;;WAEA;;WAEA,aAAa;;WAEb,gBAAgB;EAEzB,YAAY,MAAM;;EAuBlB,SAAS,iBAAiB,QAAQ;;EAKlC,SAAS,iBAAiB,QAAQ;;;;;cCzHzB,kBAAkB;;;;UCMd;EACb;EACA;EACA;;;;;;;;;UCba,WAAW;EACxB;EACA;EACA,MAAM;EACN;EACA;;;KAIQ;EACJ;EAAc;IAAQ;;;EACtB;EAAY;IAAQ;IAAY;;;EAChC;EAAc;IAAQ;;;EACtB;EAAe;IAAQ;IAAc;;;EACrC;EAAgB;IAAQ;IAAc;;;EACtC;EAAe;IAAQ;IAAc;;;EACrC;EAAc;IAAQ;;;EACtB;EAAe;IAAQ;;;EACvB;EAAiB;IAAQ;;;EACzB;EAAc;IAAQ;;;EACtB;EAAa;IAAQ;;;EACrB;EAAgB;IAAQ;;;;KAGpB,uBAAuB;;UAGlB;EACb;EACA;EACA;EACA;;;UAIa;EACb;EACA;EACA;EACA;;;UAIa;EACb;EACA;;;UAIa;EACb;EACA;EACA;EACA;IACI;IACA;IACA;IACA;;EAEJ;EACA,SAAS;EACT;EACA;EACA;;;KAIQ;;UAGK;EACb;EACA;EACA;EACA;EACA,MAAM;EACN;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;;EAEA;EACA;EACA;EACA;EACA;EACA;;;UAIa;EACb;EACA;EACA;;;UAIa;EACb;EACA;EACA,MAAM;EACN;;EAEA;EACA;EACA;EACA;;;UAIa;EACb;EACA;EACA;EACA;EACA;;;UAIa;EACb;EACA;EACA;;;UAIa;EACb;EACA,oBAAoB;EACpB,gBAAgB;EAChB,gBAAgB;EAChB,aAAa;EACb,oBAAoB;EACpB,cAAc;;;UAID;EACb;EACA;EACA;;EAEA;EACA;EACA;EACA,eAAe;EACf,gBAAgB;EAChB;;;;;;;;;;;;KCtJQ;;UAGK;;EAEb;;EAEA;;EAEA,WAAW;;;;;UCPE;EACb;EACA;EACA;;;UAIa,oBAAoB;EACjC;EACA;;;UAIa,8BAA8B;EAC3C;EACA;;EAEA;EACA;EACA;EACA;EACA,SAAS;EACT;EACA;EACA,QAAQ;EACR,YAAY;;;UAIC,gCAAgC;EAC7C;EACA;;EAEA;EACA;EACA;EACA,SAAS;EACT;EACA;EACA,QAAQ;;EAER;;;KAIQ,mBAAmB,wBAAwB;;;;UC9CtC;EACb;EACA;GACC;;;UAIY,+BAA+B;EAC5C;EACA;EACA;;;UAIa,+BAA+B;EAC5C;EACA;;EAEA;EACA,QAAQ;;;KAIA,gBAAgB,yBAAyB;;;;UCvBpC;EACb;EACA;EACA;EACA;;;UAIa;EACb;EACA;EACA;;;UAIa;EACb;EACA;EACA;;;UAIa,mCAAmC;EAChD;EACA;EACA;EACA;EACA,MAAM;;;UAIO,kCAAkC;EAC/C;EACA;EACA;EACA;EACA;;;UAIa,qCAAqC;EAClD;EACA;EACA;EACA;EACA;EACA;;;UAIa,qCAAqC;EAClD;EACA;EACA;EACA;EACA;EACA;;;UAIa,gCAAgC;EAC7C;EACA;EACA;EACA;EACA;EACA;;EAEA;;;UAIa,iCAAiC;EAC9C;EACA;EACA;;;UAIa,mCAAmC;EAChD;EACA;EACA;EACA;EACA;EACA;;;UAIa,oCAAoC;EACjD;EACA;EACA;EACA;;;UAIa,iCAAiC;EAC9C;EACA;EACA;EACA;EACA;EACA;;;UAIa,oCAAoC;EACjD;EACA;EACA;EACA;EACA;EACA;EACA;;;UAIa,8BAA8B;EAC3C;EACA;EACA;EACA;EACA;EACA;;EAEA;;;UAIa,mCAAmC;EAChD;EACA;EACA;EACA,MAAM;;;UAIO,iCAAiC;EAC9C;EACA;EACA;EACA;;;UAIa,oCAAoC;EACjD;EACA;EACA;EACA;EACA;EACA,OAAO;;;UAIM,kCAAkC;EAC/C;EACA;EACA;EACA;EACA;;;KAIQ,kBACN,6BACA,4BACA,+BACA,+BACA,0BACA,2BACA,6BACA,8BACA,2BACA,8BACA,wBACA,6BACA,2BACA,8BACA;;;;UCpLW,+BAA+B;EAC5C;EACA;EACA;;EAEA;;EAEA;;;UAIa,8BAA8B;EAC3C;EACA;;EAEA;EACA;EACA;EACA;EACA;;;KAIQ,mBAAmB,yBAAyB;;;;KCiB5C,YAAY,mBAAoB,kBAAmB,mBAAoB;;;;UCyDlE;;EAEb,KAAK;;;iBAIO,yBAAyB,MAAM,iBAAiB;;;;;;;;;;;;;;UCjF/C;EACb;EACA,QAAQ;;;iBAII,aAAa;;iBAKb,cAAc;;iBAKd,eAAe;;iBASf,aACZ,cACA,QAAQ;;;;;;;;iBAqBI,eAAe,eAAe,eAAe;;iBAuB7C,iBAAiB,OAAO,eAAe;;;;iBC8GvC,oBAAoB,UAAU;;;;cCxJjC,kBAAgB,EAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAuB3B,EAAA,KAAA;;KAGU,aAAa,EAAE,aAAa;;;;iBC/DxB,oBAAoB,UAAU,qBAAqB;;iBAYnD,oBAAoB,UAAU,uBAAuB;;iBAoIrD,oBAAoB,eAAe;;iBAkBnC,qBAAqB,UAAU;;iBAK/B,qBAAqB,eAAe;;;;UChInC;EACb,KAAK,cAAc;EACnB,KAAK,cAAc;;;UAIN,+BAA+B;;EAE5C,QAAQ,eAAe;;EAEvB,aAAa;;EAEb,YAAY;;EAEZ,eAAe;;EAEf,gBAAgB;;EAEhB,mBAAmB;;EAEnB,SAAS;;;cAIA,iCAAiC,oBAAoB;WACrD;mBAEQ;mBACA;mBACA;mBACA;mBACA;mBACA;mBACA;;WAER,UAAU;UACX;UACA;UACA;UACA;UACA;;UAEA;;UAEA;EAER,YAAY,MAAM;;;;;UAwBJ;;UAYA;;UAyCN;;UAiBM;;UAaN;;;;;;EAmJF,iBAAiB;;EAUvB;;;;;UASQ;;;;;;UAyCM;;UA2BA;;UAiBA;;UA4BA;;UAiBN;;;;;EAWF,cAAc,cAAc,UAAU,wBAAwB;;;;;UC5cvD;;EAEb,SAAS;;EAET,YAAY;;EAEZ,aAAa;;;UAIA;;EAEb,QAAQ;;EAER,aAAa;;EAEb,gBAAgB,cAAc,UAAU;;EAExC;;;iBAiBY,uBAAuB,MAAM,gCAAgC"}
@@ -1416,8 +1416,6 @@ var ForwardFriendSingleMsgAction = class extends ForwardSingleMsgBase {
1416
1416
  * - 纯文本:& → &amp;,[ → &#91;,] → &#93;
1417
1417
  * - 参数值:额外 , → &#44;
1418
1418
  */
1419
- /** CQ 码片段正则([CQ:xxx] 或 [CQ:xxx,k=v,...])。 */
1420
- const CQ_CODE_REGEX = /\[CQ:[^\]]*\]/g;
1421
1419
  /** 解析单个 CQ 码文本 → 结构化 CqCode。 */
1422
1420
  function parseCqCode(raw) {
1423
1421
  const body = raw.slice(4, -1);
@@ -1453,15 +1451,24 @@ function encodeCqCode(type, params) {
1453
1451
  if (pairs.length === 0) return `[CQ:${type}]`;
1454
1452
  return `[CQ:${type},${pairs.join(",")}]`;
1455
1453
  }
1456
- /** 解析一条消息文本 → 文本片段与 CQ 码片段的混合数组(文本已反转义)。 */
1454
+ /**
1455
+ * 解析一条消息文本 → 文本片段与 CQ 码片段的混合数组(文本已反转义)。
1456
+ *
1457
+ * 手工 indexOf 扫描而非全局正则(/\[CQ:[^\]]*\]/g):消息文本不可信,正则在
1458
+ * 「大量 [CQ: 前缀且无闭合 ]」的输入下每个起点都全量回扫,复杂度 O(n²)
1459
+ * (CodeQL polynomial-redos)。indexOf 语义等价:从 [CQ: 起到其后第一个 ]。
1460
+ */
1457
1461
  function parseCqMessage(text) {
1458
1462
  const parts = [];
1459
1463
  let lastIndex = 0;
1460
- for (const match of text.matchAll(CQ_CODE_REGEX)) {
1461
- const index = match.index ?? 0;
1462
- if (index > lastIndex) parts.push(unescapeCqText(text.slice(lastIndex, index)));
1463
- parts.push(parseCqCode(match[0] ?? ""));
1464
- lastIndex = index + match[0].length;
1464
+ let start = text.indexOf("[CQ:");
1465
+ while (start !== -1) {
1466
+ const end = text.indexOf("]", start);
1467
+ if (end === -1) break;
1468
+ if (start > lastIndex) parts.push(unescapeCqText(text.slice(lastIndex, start)));
1469
+ parts.push(parseCqCode(text.slice(start, end + 1)));
1470
+ lastIndex = end + 1;
1471
+ start = text.indexOf("[CQ:", lastIndex);
1465
1472
  }
1466
1473
  if (lastIndex < text.length) parts.push(unescapeCqText(text.slice(lastIndex)));
1467
1474
  return parts;
@@ -3694,6 +3701,251 @@ function toOfflineFileNotice(item, selfUin) {
3694
3701
  };
3695
3702
  }
3696
3703
  //#endregion
3704
+ //#region src/onebot11/helper/sysmsg.ts
3705
+ /** 递归深度上限(防御:恶意/畸形字节防栈溢出)。 */
3706
+ const MAX_DEPTH = 16;
3707
+ /** 单层字段数上限。 */
3708
+ const MAX_FIELDS = 256;
3709
+ /** 单个 varint 最大字节数(64 位 + 1 冗余)。 */
3710
+ const MAX_VARINT_BYTES = 10;
3711
+ /** 读 varint:成功返回 [值, 下一偏移],越界/超长返回 null。 */
3712
+ function readVarint(data, start) {
3713
+ let value = 0n;
3714
+ let shift = 0n;
3715
+ let i = start;
3716
+ while (true) {
3717
+ if (i >= data.length || i - start >= MAX_VARINT_BYTES) return null;
3718
+ const byte = data[i];
3719
+ if (byte === void 0) return null;
3720
+ i += 1;
3721
+ value |= BigInt(byte & 127) << shift;
3722
+ if ((byte & 128) === 0) return [value, i];
3723
+ shift += 7n;
3724
+ }
3725
+ }
3726
+ /** wire 2:length-delimited 字段(递归 try-parse 子树,失败 → 不透明字节)。 */
3727
+ function decodeLengthDelimited(data, afterTag, no, depth) {
3728
+ const len = readVarint(data, afterTag);
3729
+ if (len === null) return null;
3730
+ const payloadStart = len[1];
3731
+ const end = payloadStart + Number(len[0]);
3732
+ if (end > data.length) return null;
3733
+ const slice = data.slice(payloadStart, end);
3734
+ return {
3735
+ field: {
3736
+ no,
3737
+ kind: "bytes",
3738
+ value: slice,
3739
+ children: decodeProtoTree(slice, depth + 1)
3740
+ },
3741
+ next: end
3742
+ };
3743
+ }
3744
+ /** 解码单个带 tag 字段;畸形(field 0 / group / 截断 / 越界)返回 null。 */
3745
+ function decodeTaggedField(data, start, depth) {
3746
+ const tag = readVarint(data, start);
3747
+ if (tag === null) return null;
3748
+ const [tagValue, afterTag] = tag;
3749
+ const no = Number(tagValue >> 3n);
3750
+ const wireType = Number(tagValue & 7n);
3751
+ if (no === 0) return null;
3752
+ if (wireType === 0) {
3753
+ const v = readVarint(data, afterTag);
3754
+ if (v === null) return null;
3755
+ return {
3756
+ field: {
3757
+ no,
3758
+ kind: "varint",
3759
+ value: v[0]
3760
+ },
3761
+ next: v[1]
3762
+ };
3763
+ }
3764
+ if (wireType === 2) return decodeLengthDelimited(data, afterTag, no, depth);
3765
+ if (wireType === 1 || wireType === 5) {
3766
+ const next = afterTag + (wireType === 1 ? 8 : 4);
3767
+ return next > data.length ? null : {
3768
+ field: void 0,
3769
+ next
3770
+ };
3771
+ }
3772
+ return null;
3773
+ }
3774
+ /**
3775
+ * 解码 protobuf wire-format 为递归字段树(不维护字段重复/顺序语义以外的信息)。
3776
+ *
3777
+ * - wire 0 varint / wire 2 length-delimited:支持;wire 2 递归 try-parse,
3778
+ * 子解析失败按不透明字节挂树(ASCII 串偶发误判嵌套无实害——树仅用于观测)。
3779
+ * - wire 1/5(fixed64/32):跳过(QQ NT blob 未观测到,防御性兼容)。
3780
+ * - wire 3/4(group)、截断、field 0、超限:判失败返回 null。
3781
+ */
3782
+ function decodeProtoTree(data, depth = 0) {
3783
+ if (depth > MAX_DEPTH) return null;
3784
+ const fields = [];
3785
+ let i = 0;
3786
+ while (i < data.length) {
3787
+ if (fields.length >= MAX_FIELDS) return null;
3788
+ const decoded = decodeTaggedField(data, i, depth);
3789
+ if (decoded === null) return null;
3790
+ if (decoded.field !== void 0) fields.push(decoded.field);
3791
+ i = decoded.next;
3792
+ }
3793
+ return fields;
3794
+ }
3795
+ /** 取字段号首个命中的节点。 */
3796
+ function firstField(tree, no) {
3797
+ return tree.find((f) => f.no === no);
3798
+ }
3799
+ /** varint → number(超安全整数返回 null,调用方按缺字段处理)。 */
3800
+ function varintToNumber(value) {
3801
+ return value <= BigInt(Number.MAX_SAFE_INTEGER) ? Number(value) : null;
3802
+ }
3803
+ /** UTF-8 严格解码 + 可打印检查(含控制字符判不可解码,返回 null)。 */
3804
+ function decodeText(bytes) {
3805
+ try {
3806
+ const text = new TextDecoder("utf-8", { fatal: true }).decode(bytes);
3807
+ for (const ch of text) {
3808
+ const code = ch.codePointAt(0) ?? 0;
3809
+ if (code < 32 && code !== 9) return null;
3810
+ }
3811
+ return text;
3812
+ } catch {
3813
+ return null;
3814
+ }
3815
+ }
3816
+ /** 递归收集树内全部可打印字符串(校准观测用;先序遍历,空字节段跳过)。 */
3817
+ function collectStrings(tree) {
3818
+ const out = [];
3819
+ for (const f of tree) {
3820
+ if (f.kind === "varint") continue;
3821
+ const text = decodeText(f.value);
3822
+ if (text !== null && text.length > 0) out.push(text);
3823
+ if (f.children !== null) out.push(...collectStrings(f.children));
3824
+ }
3825
+ return out;
3826
+ }
3827
+ /** 单个候选块 → Uint8Array(number[] -128..255 / TypedArray;其余 null)。 */
3828
+ function toBlob(item) {
3829
+ if (item instanceof Uint8Array) return new Uint8Array(item);
3830
+ if (item instanceof Int8Array) return Uint8Array.from(item, (b) => b & 255);
3831
+ if (Array.isArray(item) && item.length > 0) {
3832
+ const out = new Uint8Array(item.length);
3833
+ for (let i = 0; i < item.length; i += 1) {
3834
+ const b = item[i];
3835
+ if (typeof b !== "number" || !Number.isInteger(b) || b < -128 || b > 255) return null;
3836
+ out[i] = b & 255;
3837
+ }
3838
+ return out;
3839
+ }
3840
+ return null;
3841
+ }
3842
+ /**
3843
+ * onRecvSysMsg 参数 → sysmsg 字节块列表(防御性收窄)。
3844
+ * 接受批数组(实测形状)/ 单块裸数组 / TypedArray;零有效块返回 null
3845
+ * (调用方打 raw 日志,与 narrowOfflineFiles 同模式)。
3846
+ */
3847
+ function narrowSysMsgBlobs(arg) {
3848
+ if (Array.isArray(arg)) {
3849
+ const blobs = arg.map(toBlob).filter((b) => b !== null);
3850
+ if (blobs.length > 0) return blobs;
3851
+ const single = toBlob(arg);
3852
+ return single === null ? null : [single];
3853
+ }
3854
+ const single = toBlob(arg);
3855
+ return single === null ? null : [single];
3856
+ }
3857
+ /** 顶层 f1 块的字段(uin/uid 对)。 */
3858
+ function extractActorBlock(block) {
3859
+ const empty = {
3860
+ uin: null,
3861
+ uid: null,
3862
+ uin2: null,
3863
+ uid2: null
3864
+ };
3865
+ if (block === null) return empty;
3866
+ const f1 = firstField(block, 1);
3867
+ const f2 = firstField(block, 2);
3868
+ const f5 = firstField(block, 5);
3869
+ const f6 = firstField(block, 6);
3870
+ return {
3871
+ uin: f1?.kind === "varint" ? varintToNumber(f1.value) : null,
3872
+ uid: f2?.kind === "bytes" ? decodeText(f2.value) : null,
3873
+ uin2: f5?.kind === "varint" ? varintToNumber(f5.value) : null,
3874
+ uid2: f6?.kind === "bytes" ? decodeText(f6.value) : null
3875
+ };
3876
+ }
3877
+ /** 顶层 f2 块 → 信封数值字段。 */
3878
+ function extractEnvelopeBlock(block) {
3879
+ const empty = {
3880
+ msgType: null,
3881
+ subType: null,
3882
+ subTypeAlt: null,
3883
+ groupCodes: null,
3884
+ time: null
3885
+ };
3886
+ if (block === null) return empty;
3887
+ const num = (no) => {
3888
+ const f = firstField(block, no);
3889
+ return f?.kind === "varint" ? varintToNumber(f.value) : null;
3890
+ };
3891
+ const code4 = num(4);
3892
+ const code5 = num(5);
3893
+ return {
3894
+ msgType: num(1),
3895
+ subType: num(2),
3896
+ subTypeAlt: num(3),
3897
+ groupCodes: code4 !== null && code5 !== null ? [code4, code5] : null,
3898
+ time: num(6)
3899
+ };
3900
+ }
3901
+ /** 字段树 → 信封(design.md §7.3;任何字段缺失/非法 = null,不抛错)。 */
3902
+ function extractSysMsgEnvelope(tree) {
3903
+ const actor = extractActorBlock(getChildren(firstField(tree, 1)));
3904
+ return {
3905
+ actorUin: actor.uin,
3906
+ actorUid: actor.uid,
3907
+ targetUin: actor.uin2,
3908
+ targetUid: actor.uid2,
3909
+ ...extractEnvelopeBlock(getChildren(firstField(tree, 2))),
3910
+ strings: collectStrings(tree)
3911
+ };
3912
+ }
3913
+ /** bytes 节点的子树(非 bytes/不透明 → null)。 */
3914
+ function getChildren(field) {
3915
+ return field?.kind === "bytes" ? field.children : null;
3916
+ }
3917
+ /**
3918
+ * (msgType, subType) → 规则表,主键 `"${msgType}:${subType}"`(原生分发键,
3919
+ * design.md §7.1)。
3920
+ *
3921
+ * ⚠️ 当前为空:c3 以来的全部真实样本(2 条)均为 528/382 未命名类型,
3922
+ * card/title/sign 的判别值没有样本支撑——在此登记规则前不会广播任何 sysmsg
3923
+ * notice。拿到真实样本后登记 extract 规则即可(规则实现参考
3924
+ * notice-extra.ts 防御性收窄风格)。
3925
+ */
3926
+ const KIND_TABLE = /* @__PURE__ */ new Map();
3927
+ /** 已观测但未命名的 (msgType, subType):c3 双样本 528/382(语义未知)。 */
3928
+ const OBSERVED_UNNAMED = /* @__PURE__ */ new Set(["528:382"]);
3929
+ /**
3930
+ * (msgType, subType) → 识别结果。table 参数供单测注入合成规则(生产走
3931
+ * KIND_TABLE)。
3932
+ */
3933
+ function recognizeSysMsg(msgType, subType, table = KIND_TABLE) {
3934
+ if (msgType === null || subType === null) return { kind: "unknown" };
3935
+ const rule = table.get(`${msgType}:${subType}`);
3936
+ if (rule !== void 0) return {
3937
+ kind: "notice",
3938
+ rule
3939
+ };
3940
+ return OBSERVED_UNNAMED.has(`${msgType}:${subType}`) ? { kind: "observed_unnamed" } : { kind: "unknown" };
3941
+ }
3942
+ /** 字节块 → 小写 hex(校准日志用;超长由调用方截断)。 */
3943
+ function toHex(bytes) {
3944
+ let out = "";
3945
+ for (const b of bytes) out += b.toString(16).padStart(2, "0");
3946
+ return out;
3947
+ }
3948
+ //#endregion
3697
3949
  //#region src/onebot11/transport.ts
3698
3950
  /**
3699
3951
  * OB11 传输装配(P2-5,2026-08-05)
@@ -3991,8 +4243,16 @@ var NapukettoOneBot11Adapter = class extends BaseProtocolAdapter {
3991
4243
  }
3992
4244
  for (const item of items) this.broadcastEvent(toOfflineFileNotice(item, this.selfUin));
3993
4245
  }));
3994
- for (const evt of ["Msg/onRecvSysMsg", "Msg/onRecvOnlineFileMsg"]) this.unsubscribes.push(this.msgChannel.on(evt, (arg) => {
3995
- this.calibLogger?.info({ raw: JSON.stringify(arg)?.slice(0, 2e3) }, `ob11: ${evt} raw(sys msg 校准数据)`);
4246
+ this.unsubscribes.push(this.msgChannel.on("Msg/onRecvSysMsg", (arg) => {
4247
+ const blobs = narrowSysMsgBlobs(arg);
4248
+ if (blobs === null) {
4249
+ this.calibLogger?.warn({ raw: JSON.stringify(arg)?.slice(0, 2e3) }, "ob11: onRecvSysMsg 未知参数形状");
4250
+ return;
4251
+ }
4252
+ for (const blob of blobs) this.handleSysMsgBlob(blob);
4253
+ }));
4254
+ this.unsubscribes.push(this.msgChannel.on("Msg/onRecvOnlineFileMsg", (arg) => {
4255
+ this.calibLogger?.info({ raw: JSON.stringify(arg)?.slice(0, 2e3) }, "ob11: onRecvOnlineFileMsg raw(校准数据)");
3996
4256
  }));
3997
4257
  if (this.groupChannel !== void 0) {
3998
4258
  this.unsubscribes.push(this.groupChannel.on("Group/onGroupNotifiesUpdated", (doubt, notifies) => {
@@ -4039,6 +4299,38 @@ var NapukettoOneBot11Adapter = class extends BaseProtocolAdapter {
4039
4299
  this.unsubscribeAll();
4040
4300
  }
4041
4301
  /**
4302
+ * 单条 sysmsg 字节块:解码 → 识别 → 命中才广播,未识别打结构化校准日志
4303
+ * (识别表为空时全部走日志路径,design.md §7.5)。
4304
+ */
4305
+ handleSysMsgBlob(blob) {
4306
+ const tree = decodeProtoTree(blob);
4307
+ if (tree === null) {
4308
+ this.calibLogger?.warn({ raw: toHex(blob).slice(0, 1024) }, "ob11: onRecvSysMsg protobuf 解码失败");
4309
+ return;
4310
+ }
4311
+ const env = extractSysMsgEnvelope(tree);
4312
+ const rec = recognizeSysMsg(env.msgType, env.subType);
4313
+ if (rec.kind === "notice") {
4314
+ const event = rec.rule.extract(tree, env);
4315
+ if (event !== null) {
4316
+ this.broadcastEvent(event);
4317
+ return;
4318
+ }
4319
+ }
4320
+ this.calibLogger?.info({
4321
+ verdict: rec.kind,
4322
+ msgType: env.msgType,
4323
+ subType: env.subType,
4324
+ subTypeAlt: env.subTypeAlt,
4325
+ groupCodes: env.groupCodes,
4326
+ time: env.time,
4327
+ actorUin: env.actorUin,
4328
+ actorUid: env.actorUid,
4329
+ strings: env.strings,
4330
+ raw: toHex(blob).slice(0, 1024)
4331
+ }, "ob11: onRecvSysMsg 校准数据(未识别,不广播)");
4332
+ }
4333
+ /**
4042
4334
  * 广播消息事件(P2-19:接收方向 ID 转换)。
4043
4335
  * 收集 at uid → 一次批量 uidToUin → 构造上下文 → 翻译广播。
4044
4336
  * 翻译失败退化为原样(不阻塞上报)。