@napuketto/adapter 0.0.1 → 0.0.2

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.
@@ -1,4 +1,5 @@
1
1
  import { a as ActionRegistry, n as BaseProtocolAdapter, r as BaseAction } from "../core-Bj5L8WeZ.mjs";
2
+ import { i as createWsServerSchema, n as collectGrayTipUids, r as createHttpServerSchema, t as forEachRawMessage } from "../raw-message-Du7tvR1D.mjs";
2
3
  import { ChatType, GrayTipSubType, GroupApi, GroupNotifyMsgType, NTGroupMemberRole, NTGroupRequestOperateTypes, TicketApi, TipGroupElementType, WebHonorType, kernelError, toCanonicalElements } from "@napuketto/kernel";
3
4
  import { z } from "zod";
4
5
  import { existsSync, mkdirSync, writeFileSync } from "node:fs";
@@ -17,6 +18,17 @@ const ob11ErrorCodeMap = {
17
18
  UNKNOWN: 999
18
19
  };
19
20
  //#endregion
21
+ //#region src/onebot11/action/resolve-uid.ts
22
+ /**
23
+ * OB11 用户 ID 解析(uin → uid,多个动作共用,2026-08-08 克隆合并)
24
+ */
25
+ /** 解析单个 uin → uid(失败抛 KernelError INVALID_PARAM)。 */
26
+ async function resolveUid(uin, uinToUid) {
27
+ const uid = (await uinToUid([uin])).get(uin);
28
+ if (uid === void 0) throw kernelError(`用户 ${uin} 的 uid 解析失败`, "INVALID_PARAM");
29
+ return uid;
30
+ }
31
+ //#endregion
20
32
  //#region src/onebot11/action/friend/delete-friend.ts
21
33
  /**
22
34
  * delete_friend 动作:删除好友(P2-11 接 kernel FriendApi.deleteFriend)
@@ -33,8 +45,7 @@ var DeleteFriendAction = class extends BaseAction {
33
45
  this.deps = deps;
34
46
  }
35
47
  async _handle(payload) {
36
- const uid = (await this.deps.uinToUid([String(payload.user_id)])).get(String(payload.user_id));
37
- if (uid === void 0) throw kernelError(`用户 ${payload.user_id} 的 uid 解析失败`, "INVALID_PARAM");
48
+ const uid = await resolveUid(String(payload.user_id), this.deps.uinToUid);
38
49
  await this.deps.friendApi.deleteFriend(uid);
39
50
  return null;
40
51
  }
@@ -144,8 +155,7 @@ var SendLikeAction = class extends BaseAction {
144
155
  this.deps = deps;
145
156
  }
146
157
  async _handle(payload) {
147
- const uid = (await this.deps.uinToUid([String(payload.user_id)])).get(String(payload.user_id));
148
- if (uid === void 0) throw kernelError(`用户 ${payload.user_id} 的 uid 解析失败`, "INVALID_PARAM");
158
+ const uid = await resolveUid(String(payload.user_id), this.deps.uinToUid);
149
159
  await this.deps.profileLikeApi.sendLike(uid, payload.times ?? 1);
150
160
  return null;
151
161
  }
@@ -222,16 +232,10 @@ var SetFriendRemarkAction = class extends BaseAction {
222
232
  this.deps = deps;
223
233
  }
224
234
  async _handle(payload) {
225
- const uid = await this.resolveUid(payload.user_id);
235
+ const uid = await resolveUid(String(payload.user_id), this.deps.uinToUid);
226
236
  this.deps.friendApi.setFriendRemark(uid, payload.remark);
227
237
  return null;
228
238
  }
229
- /** user_id(uin)→ uid。 */
230
- async resolveUid(userId) {
231
- const uid = (await this.deps.uinToUid([String(userId)])).get(String(userId));
232
- if (uid === void 0) throw kernelError(`用户 ${userId} 的 uid 解析失败`, "INVALID_PARAM");
233
- return uid;
234
- }
235
239
  };
236
240
  //#endregion
237
241
  //#region src/onebot11/helper/message-unique.ts
@@ -322,15 +326,15 @@ function resolveMsgIdAndPeer(messageId, unique) {
322
326
  };
323
327
  }
324
328
  //#endregion
325
- //#region src/onebot11/action/group/delete-essence-msg.ts
329
+ //#region src/onebot11/action/group/set-essence-msg.ts
326
330
  /**
327
- * delete_essence_msg 动作:取消精华消息(P2-10 接 kernel GroupApi.removeGroupEssence)
331
+ * set_essence_msg 动作:设置精华消息(P2-10 接 kernel GroupApi.addGroupEssence)
332
+ *
333
+ * message_id → msgId + peer(MessageUnique 反查,需群消息)→ addGroupEssence。
328
334
  */
329
- const deleteEssenceMsgSchema = z.object({ message_id: z.union([z.number(), z.string()]) });
330
- /** 取消精华消息(P2-10 接 kernel removeGroupEssence)。 */
331
- var DeleteEssenceMsgAction = class extends BaseAction {
332
- name = "delete_essence_msg";
333
- schema = deleteEssenceMsgSchema;
335
+ const setEssenceMsgSchema = z.object({ message_id: z.union([z.number(), z.string()]) });
336
+ /** 精华消息基类(set/delete 共用实现,2026-08-08 克隆合并)。 */
337
+ var EssenceMsgBase = class extends BaseAction {
334
338
  errorCodeMap = ob11ErrorCodeMap;
335
339
  deps;
336
340
  constructor(deps) {
@@ -340,10 +344,32 @@ var DeleteEssenceMsgAction = class extends BaseAction {
340
344
  async _handle(payload) {
341
345
  const { msgId, peer } = resolveMsgIdAndPeer(payload.message_id, this.deps.messageUnique);
342
346
  if (peer.chatType !== ChatType.GROUP) throw kernelError("精华消息仅支持群聊消息", "INVALID_PARAM");
343
- await this.deps.groupApi.removeGroupEssence(peer.peerUid, msgId);
347
+ await this.operate(peer.peerUid, msgId);
344
348
  return null;
345
349
  }
346
350
  };
351
+ /** 设置精华消息(P2-10 接 kernel addGroupEssence)。 */
352
+ var SetEssenceMsgAction = class extends EssenceMsgBase {
353
+ name = "set_essence_msg";
354
+ schema = setEssenceMsgSchema;
355
+ async operate(groupCode, msgId) {
356
+ await this.deps.groupApi.addGroupEssence(groupCode, msgId);
357
+ }
358
+ };
359
+ //#endregion
360
+ //#region src/onebot11/action/group/delete-essence-msg.ts
361
+ /**
362
+ * delete_essence_msg 动作:取消精华消息(P2-10 接 kernel GroupApi.removeGroupEssence)
363
+ */
364
+ const deleteEssenceMsgSchema = z.object({ message_id: z.union([z.number(), z.string()]) });
365
+ /** 取消精华消息(P2-10 接 kernel removeGroupEssence)。 */
366
+ var DeleteEssenceMsgAction = class extends EssenceMsgBase {
367
+ name = "delete_essence_msg";
368
+ schema = deleteEssenceMsgSchema;
369
+ async operate(groupCode, msgId) {
370
+ await this.deps.groupApi.removeGroupEssence(groupCode, msgId);
371
+ }
372
+ };
347
373
  //#endregion
348
374
  //#region src/onebot11/action/group/get-essence-msg-list.ts
349
375
  const getEssenceMsgListSchema = z.object({ group_id: z.number() });
@@ -1020,31 +1046,6 @@ function toGroupFolderInfo(folder) {
1020
1046
  };
1021
1047
  }
1022
1048
  //#endregion
1023
- //#region src/onebot11/action/group/set-essence-msg.ts
1024
- /**
1025
- * set_essence_msg 动作:设置精华消息(P2-10 接 kernel GroupApi.addGroupEssence)
1026
- *
1027
- * message_id → msgId + peer(MessageUnique 反查,需群消息)→ addGroupEssence。
1028
- */
1029
- const setEssenceMsgSchema = z.object({ message_id: z.union([z.number(), z.string()]) });
1030
- /** 设置精华消息(P2-10 接 kernel addGroupEssence)。 */
1031
- var SetEssenceMsgAction = class extends BaseAction {
1032
- name = "set_essence_msg";
1033
- schema = setEssenceMsgSchema;
1034
- errorCodeMap = ob11ErrorCodeMap;
1035
- deps;
1036
- constructor(deps) {
1037
- super();
1038
- this.deps = deps;
1039
- }
1040
- async _handle(payload) {
1041
- const { msgId, peer } = resolveMsgIdAndPeer(payload.message_id, this.deps.messageUnique);
1042
- if (peer.chatType !== ChatType.GROUP) throw kernelError("精华消息仅支持群聊消息", "INVALID_PARAM");
1043
- await this.deps.groupApi.addGroupEssence(peer.peerUid, msgId);
1044
- return null;
1045
- }
1046
- };
1047
- //#endregion
1048
1049
  //#region src/onebot11/action/group/set-group-add-request.ts
1049
1050
  /**
1050
1051
  * set_group_add_request 动作:处理加群请求(P2-13 接 kernel GroupNotifyApi)
@@ -1110,8 +1111,7 @@ var SetGroupAdminAction = class extends BaseAction {
1110
1111
  this.groupApi = groupApi;
1111
1112
  }
1112
1113
  async _handle(payload) {
1113
- const uid = (await this.groupApi.uinToUid([String(payload.user_id)])).get(String(payload.user_id));
1114
- if (uid === void 0) throw kernelError(`用户 ${payload.user_id} 的 uid 解析失败`, "INVALID_PARAM");
1114
+ const uid = await resolveUid(String(payload.user_id), (uins) => this.groupApi.uinToUid(uins));
1115
1115
  let role = NTGroupMemberRole.MEMBER;
1116
1116
  if (payload.enable) role = NTGroupMemberRole.ADMIN;
1117
1117
  this.groupApi.setMemberRole(String(payload.group_id), uid, role);
@@ -1120,11 +1120,6 @@ var SetGroupAdminAction = class extends BaseAction {
1120
1120
  };
1121
1121
  //#endregion
1122
1122
  //#region src/onebot11/action/group/set-group-ban.ts
1123
- /**
1124
- * set_group_ban 动作:群组单人禁言(P2-10 接 kernel GroupApi.setMemberShutUp)
1125
- *
1126
- * duration 秒(0 解除禁言);user_id → uinToUid。
1127
- */
1128
1123
  const setGroupBanSchema = z.object({
1129
1124
  group_id: z.number(),
1130
1125
  user_id: z.number(),
@@ -1142,8 +1137,7 @@ var SetGroupBanAction = class extends BaseAction {
1142
1137
  this.groupApi = groupApi;
1143
1138
  }
1144
1139
  async _handle(payload) {
1145
- const uid = (await this.groupApi.uinToUid([String(payload.user_id)])).get(String(payload.user_id));
1146
- if (uid === void 0) throw kernelError(`用户 ${payload.user_id} 的 uid 解析失败`, "INVALID_PARAM");
1140
+ const uid = await resolveUid(String(payload.user_id), (uins) => this.groupApi.uinToUid(uins));
1147
1141
  await this.groupApi.setMemberShutUp(String(payload.group_id), [{
1148
1142
  uid,
1149
1143
  duration: payload.duration
@@ -1153,9 +1147,6 @@ var SetGroupBanAction = class extends BaseAction {
1153
1147
  };
1154
1148
  //#endregion
1155
1149
  //#region src/onebot11/action/group/set-group-card.ts
1156
- /**
1157
- * set_group_card 动作:设置群名片(P2-10 接 kernel GroupApi.setMemberCardName)
1158
- */
1159
1150
  const setGroupCardSchema = z.object({
1160
1151
  group_id: z.number(),
1161
1152
  user_id: z.number(),
@@ -1173,8 +1164,7 @@ var SetGroupCardAction = class extends BaseAction {
1173
1164
  this.groupApi = groupApi;
1174
1165
  }
1175
1166
  async _handle(payload) {
1176
- const uid = (await this.groupApi.uinToUid([String(payload.user_id)])).get(String(payload.user_id));
1177
- if (uid === void 0) throw kernelError(`用户 ${payload.user_id} 的 uid 解析失败`, "INVALID_PARAM");
1167
+ const uid = await resolveUid(String(payload.user_id), (uins) => this.groupApi.uinToUid(uins));
1178
1168
  this.groupApi.setMemberCardName(String(payload.group_id), uid, payload.card);
1179
1169
  return null;
1180
1170
  }
@@ -1331,20 +1321,14 @@ var FetchPttTextAction = class extends BaseAction {
1331
1321
  }
1332
1322
  };
1333
1323
  //#endregion
1334
- //#region src/onebot11/action/message/forward-single-msg.ts
1324
+ //#region src/onebot11/action/message/resolve-peer.ts
1335
1325
  /**
1336
- * forward_group_single_msg / forward_friend_single_msg 动作:单条转发(P2-12)
1326
+ * OB11 目标 Peer 解析 / 单条消息拉取(多个动作共用,2026-08-08 克隆合并)
1337
1327
  *
1338
- * message_id 反查源 msgId + peer;group_id/user_id 为目标 →
1339
- * MsgApi.forwardSingleMessage(srcPeer, [msgId], dstPeer)。
1328
+ * group_id 直通(群消息 peerUid = 群号);user_id 经 uin→uid 解析。
1340
1329
  */
1341
- const forwardSingleMsgSchema = z.object({
1342
- message_id: z.union([z.number(), z.string()]),
1343
- group_id: z.number().optional(),
1344
- user_id: z.number().optional()
1345
- });
1346
1330
  /** 解析目标 Peer(group_id 直通;user_id 经 uin→uid)。 */
1347
- async function resolveTargetPeer(payload, deps) {
1331
+ async function resolveTargetPeer(payload, deps, missingHint) {
1348
1332
  if (payload.group_id !== void 0) return {
1349
1333
  chatType: ChatType.GROUP,
1350
1334
  peerUid: String(payload.group_id)
@@ -1357,8 +1341,35 @@ async function resolveTargetPeer(payload, deps) {
1357
1341
  peerUid: uid
1358
1342
  };
1359
1343
  }
1360
- throw kernelError("单条转发需要 group_id 或 user_id", "INVALID_PARAM");
1344
+ throw kernelError(missingHint, "INVALID_PARAM");
1361
1345
  }
1346
+ /**
1347
+ * 按 message_id(OB11 int32 或 NT msgId)拉取单条消息。
1348
+ * 消息不存在/已被撤回 → 抛 KernelError NOT_FOUND。
1349
+ */
1350
+ async function fetchMsgById(messageId, messageUnique, msgApi) {
1351
+ const { msgId, peer } = resolveMsgIdAndPeer(messageId, messageUnique);
1352
+ const [msg] = await msgApi.fetchMsgsByMsgId(peer, [msgId]);
1353
+ if (msg === void 0) throw kernelError(`消息 ${messageId} 不存在或已被撤回`, "NOT_FOUND");
1354
+ return {
1355
+ msg,
1356
+ peer,
1357
+ msgId
1358
+ };
1359
+ }
1360
+ //#endregion
1361
+ //#region src/onebot11/action/message/forward-single-msg.ts
1362
+ /**
1363
+ * forward_group_single_msg / forward_friend_single_msg 动作:单条转发(P2-12)
1364
+ *
1365
+ * message_id 反查源 msgId + peer;group_id/user_id 为目标 →
1366
+ * MsgApi.forwardSingleMessage(srcPeer, [msgId], dstPeer)。
1367
+ */
1368
+ const forwardSingleMsgSchema = z.object({
1369
+ message_id: z.union([z.number(), z.string()]),
1370
+ group_id: z.number().optional(),
1371
+ user_id: z.number().optional()
1372
+ });
1362
1373
  /** 单条转发基类(两个动作共用实现)。 */
1363
1374
  var ForwardSingleMsgBase = class extends BaseAction {
1364
1375
  errorCodeMap = ob11ErrorCodeMap;
@@ -1369,7 +1380,7 @@ var ForwardSingleMsgBase = class extends BaseAction {
1369
1380
  }
1370
1381
  async _handle(payload) {
1371
1382
  const { msgId, peer: sourcePeer } = resolveMsgIdAndPeer(payload.message_id, this.deps.messageUnique);
1372
- const dstPeer = await resolveTargetPeer(payload, this.deps);
1383
+ const dstPeer = await resolveTargetPeer(payload, this.deps, "单条转发需要 group_id 或 user_id");
1373
1384
  await this.deps.msgApi.forwardSingleMessage(sourcePeer, [msgId], dstPeer);
1374
1385
  return null;
1375
1386
  }
@@ -1407,13 +1418,7 @@ const DEFAULT_RECONNECT_DELAY_MS = 5e3;
1407
1418
  /** 消息内容格式(NapCat messagePostFormat)。 */
1408
1419
  const MESSAGE_FORMATS = ["array", "string"];
1409
1420
  /** HTTP 反向服务器实例(第三方 POST 指令入口)。 */
1410
- const httpServerSchema = z.object({
1411
- enabled: z.boolean().default(false),
1412
- host: z.string().default("127.0.0.1"),
1413
- port: z.number().int().default(DEFAULT_HTTP_PORT),
1414
- /** 实例级 token,覆盖全局 token(缺省继承全局)。 */
1415
- token: z.string().optional()
1416
- });
1421
+ const httpServerSchema = createHttpServerSchema(DEFAULT_HTTP_PORT);
1417
1422
  /** HTTP 正向上报实例(Webhook 事件推送)。 */
1418
1423
  const httpPostSchema = z.object({
1419
1424
  enabled: z.boolean().default(false),
@@ -1423,16 +1428,10 @@ const httpPostSchema = z.object({
1423
1428
  /** 上报超时(毫秒),缺省不超时。 */
1424
1429
  timeoutMs: z.number().int().optional()
1425
1430
  });
1426
- /** 反向 WS server 实例(第三方 WS 连入)。 */
1427
- const wsServerSchema = z.object({
1428
- enabled: z.boolean().default(false),
1429
- host: z.string().default("127.0.0.1"),
1430
- port: z.number().int().default(DEFAULT_WS_PORT),
1431
- /** 实例级 token,覆盖全局 token(缺省继承全局)。 */
1432
- token: z.string().optional(),
1433
- /** WS ping 间隔(毫秒)。 */
1434
- heartbeatInterval: z.number().int().default(DEFAULT_WS_HEARTBEAT_MS)
1435
- });
1431
+ /** 反向 WS server 实例(第三方 WS 连入,带 WS ping 间隔扩展字段)。 */
1432
+ const wsServerSchema = createWsServerSchema(DEFAULT_WS_PORT).extend({
1433
+ /** WS ping 间隔(毫秒)。 */
1434
+ heartbeatInterval: z.number().int().default(DEFAULT_WS_HEARTBEAT_MS) });
1436
1435
  /** 正向 WS client 实例(主动连接第三方,双向)。 */
1437
1436
  const wsReverseSchema = z.object({
1438
1437
  enabled: z.boolean().default(false),
@@ -1537,7 +1536,7 @@ function serializeCqParts(parts) {
1537
1536
  return out;
1538
1537
  }
1539
1538
  //#endregion
1540
- //#region src/onebot11/helper/data.ts
1539
+ //#region src/onebot11/helper/segment.ts
1541
1540
  /** OB11 媒体段 data(image/record/video 共用,url 可选)。 */
1542
1541
  function mediaData(file, url) {
1543
1542
  const data = { file };
@@ -1700,6 +1699,15 @@ function cqCodeToSegment(code) {
1700
1699
  };
1701
1700
  return builder(code.params);
1702
1701
  }
1702
+ /** OB11 segment 数组 → CQ 码字符串(text 段转义拼接,特殊段编码)。 */
1703
+ function segmentsToCqMessage(segments) {
1704
+ let out = "";
1705
+ for (const seg of segments) if (seg.type === "text" || seg.type === "string") out += escapeCqText(seg.data.text);
1706
+ else out += encodeCqCode(seg.type, seg.data);
1707
+ return out;
1708
+ }
1709
+ //#endregion
1710
+ //#region src/onebot11/helper/data.ts
1703
1711
  /** canonical 元素数组 → OB11 segment 数组(无法表达的元素静默跳过)。 */
1704
1712
  function canonicalToSegments(elements) {
1705
1713
  const segments = [];
@@ -1718,13 +1726,6 @@ function segmentsToCanonical(segments) {
1718
1726
  }
1719
1727
  return elements;
1720
1728
  }
1721
- /** OB11 segment 数组 → CQ 码字符串(text 段转义拼接,特殊段编码)。 */
1722
- function segmentsToCqMessage(segments) {
1723
- let out = "";
1724
- for (const seg of segments) if (seg.type === "text" || seg.type === "string") out += escapeCqText(seg.data.text);
1725
- else out += encodeCqCode(seg.type, seg.data);
1726
- return out;
1727
- }
1728
1729
  /**
1729
1730
  * 收集 canonical 元素里需要转换的 at uid / reply NT msgId(接收方向)。
1730
1731
  * 调用方据此批量 uidToUin(一次异步调用),避免逐元素查询。
@@ -1878,23 +1879,6 @@ const MS_TO_SEC$2 = 1e3;
1878
1879
  function hasGrayTip(msg) {
1879
1880
  return msg.elements.some((el) => el.grayTipElement !== void 0);
1880
1881
  }
1881
- /** 收集 grayTip 涉及的 uid(批量 uidToUin 用)。 */
1882
- function collectGrayTipUids(msg) {
1883
- const uids = /* @__PURE__ */ new Set();
1884
- for (const el of msg.elements) {
1885
- const g = el.grayTipElement;
1886
- if (g === void 0) continue;
1887
- const revoke = g.revokeElement;
1888
- if (revoke?.operatorUid !== void 0 && revoke.operatorUid !== "") uids.add(revoke.operatorUid);
1889
- const grp = g.groupElement;
1890
- if (grp === void 0) continue;
1891
- if (grp.memberUid !== void 0 && grp.memberUid !== "") uids.add(grp.memberUid);
1892
- if (grp.adminUid !== void 0 && grp.adminUid !== "") uids.add(grp.adminUid);
1893
- if (grp.shutUp?.admin?.uid !== void 0 && grp.shutUp.admin.uid !== "") uids.add(grp.shutUp.admin.uid);
1894
- if (grp.shutUp?.member?.uid !== void 0 && grp.shutUp.member.uid !== "") uids.add(grp.shutUp.member.uid);
1895
- }
1896
- return [...uids];
1897
- }
1898
1882
  /** uid → uin(上下文映射缺省退化为 uid)。 */
1899
1883
  function toUin(uid, ctx) {
1900
1884
  const uin = ctx.uidToUin.get(uid);
@@ -1925,59 +1909,79 @@ function toRecall(msg, g, ctx) {
1925
1909
  function toGroupChange(msg, g, ctx) {
1926
1910
  const grp = g?.groupElement;
1927
1911
  if (grp === void 0 || grp.type === void 0) return null;
1928
- const groupId = Number(msg.peerUid);
1929
- const time = Math.floor(Number(msg.msgTime) / MS_TO_SEC$2);
1930
- const selfId = Number(ctx.selfUin);
1931
- const memberUid = grp.memberUid ?? "";
1932
- const adminUid = grp.adminUid ?? "";
1912
+ const common = {
1913
+ time: Math.floor(Number(msg.msgTime) / MS_TO_SEC$2),
1914
+ selfId: Number(ctx.selfUin),
1915
+ groupId: Number(msg.peerUid),
1916
+ memberUid: grp.memberUid ?? "",
1917
+ adminUid: grp.adminUid ?? ""
1918
+ };
1933
1919
  switch (grp.type) {
1934
- case TipGroupElementType.MEMBER_ADD: return {
1935
- time,
1936
- self_id: selfId,
1937
- post_type: "notice",
1938
- notice_type: "group_increase",
1939
- sub_type: adminUid === memberUid ? "approve" : "invite",
1940
- group_id: groupId,
1941
- operator_id: toUin(adminUid, ctx),
1942
- user_id: toUin(memberUid, ctx)
1943
- };
1944
- case TipGroupElementType.QUIT: return {
1945
- time,
1946
- self_id: selfId,
1947
- post_type: "notice",
1948
- notice_type: "group_decrease",
1949
- sub_type: "leave",
1950
- group_id: groupId,
1951
- operator_id: toUin(adminUid, ctx),
1952
- user_id: toUin(memberUid, ctx)
1953
- };
1920
+ case TipGroupElementType.MEMBER_ADD: return toMemberAdd(common, ctx);
1921
+ case TipGroupElementType.QUIT: return toQuit(common, ctx);
1954
1922
  case TipGroupElementType.BLOCK:
1955
- case TipGroupElementType.UNBLOCK: return {
1956
- time,
1957
- self_id: selfId,
1958
- post_type: "notice",
1959
- notice_type: "group_admin",
1960
- sub_type: grp.type === TipGroupElementType.BLOCK ? "set" : "unset",
1961
- group_id: groupId,
1962
- user_id: toUin(memberUid === "" ? adminUid : memberUid, ctx)
1963
- };
1964
- case TipGroupElementType.SHUT_UP: {
1965
- const shutUp = grp.shutUp;
1966
- return {
1967
- time,
1968
- self_id: selfId,
1969
- post_type: "notice",
1970
- notice_type: "group_ban",
1971
- sub_type: Number(shutUp?.duration ?? 0) > 0 ? "ban" : "lift_ban",
1972
- group_id: groupId,
1973
- operator_id: toUin(shutUp?.admin?.uid ?? adminUid, ctx),
1974
- user_id: toUin(shutUp?.member?.uid ?? memberUid, ctx),
1975
- duration: Number(shutUp?.duration ?? 0)
1976
- };
1977
- }
1923
+ case TipGroupElementType.UNBLOCK: return toAdminChange(common, grp, ctx);
1924
+ case TipGroupElementType.SHUT_UP: return toBan(common, grp, ctx);
1978
1925
  default: return null;
1979
1926
  }
1980
1927
  }
1928
+ /** 群成员增加(approve=管理员操作 / invite=普通邀请)。 */
1929
+ function toMemberAdd(common, ctx) {
1930
+ const { time, selfId, groupId, memberUid, adminUid } = common;
1931
+ return {
1932
+ time,
1933
+ self_id: selfId,
1934
+ post_type: "notice",
1935
+ notice_type: "group_increase",
1936
+ sub_type: adminUid === memberUid ? "approve" : "invite",
1937
+ group_id: groupId,
1938
+ operator_id: toUin(adminUid, ctx),
1939
+ user_id: toUin(memberUid, ctx)
1940
+ };
1941
+ }
1942
+ /** 群成员退出(leave)。 */
1943
+ function toQuit(common, ctx) {
1944
+ const { time, selfId, groupId, memberUid, adminUid } = common;
1945
+ return {
1946
+ time,
1947
+ self_id: selfId,
1948
+ post_type: "notice",
1949
+ notice_type: "group_decrease",
1950
+ sub_type: "leave",
1951
+ group_id: groupId,
1952
+ operator_id: toUin(adminUid, ctx),
1953
+ user_id: toUin(memberUid, ctx)
1954
+ };
1955
+ }
1956
+ /** 管理员变更(BLOCK=set / UNBLOCK=unset)。 */
1957
+ function toAdminChange(common, grp, ctx) {
1958
+ const { time, selfId, groupId, memberUid, adminUid } = common;
1959
+ return {
1960
+ time,
1961
+ self_id: selfId,
1962
+ post_type: "notice",
1963
+ notice_type: "group_admin",
1964
+ sub_type: grp.type === TipGroupElementType.BLOCK ? "set" : "unset",
1965
+ group_id: groupId,
1966
+ user_id: toUin(memberUid === "" ? adminUid : memberUid, ctx)
1967
+ };
1968
+ }
1969
+ /** 禁言(duration>0=ban / 否则 lift_ban)。 */
1970
+ function toBan(common, grp, ctx) {
1971
+ const { time, selfId, groupId, adminUid, memberUid } = common;
1972
+ const shutUp = grp.shutUp;
1973
+ return {
1974
+ time,
1975
+ self_id: selfId,
1976
+ post_type: "notice",
1977
+ notice_type: "group_ban",
1978
+ sub_type: Number(shutUp?.duration ?? 0) > 0 ? "ban" : "lift_ban",
1979
+ group_id: groupId,
1980
+ operator_id: toUin(shutUp?.admin?.uid ?? adminUid, ctx),
1981
+ user_id: toUin(shutUp?.member?.uid ?? memberUid, ctx),
1982
+ duration: Number(shutUp?.duration ?? 0)
1983
+ };
1984
+ }
1981
1985
  /** RawMessage → OB11 notice 事件(无 grayTip 返回 null)。 */
1982
1986
  function toOb11NoticeEvent(msg, ctx) {
1983
1987
  for (const el of msg.elements) {
@@ -2024,6 +2028,48 @@ var GetForwardMsgAction = class extends BaseAction {
2024
2028
  return { messages: (await this.deps.msgApi.fetchForwardMessage(peer, msgId)).map((msg) => toOb11MessageInfo(msg, this.deps.messageUnique)) };
2025
2029
  }
2026
2030
  };
2031
+ /** 历史消息公共参数(message_seq / count / 反向排序)。 */
2032
+ const msgHistoryParamsSchema = {
2033
+ /** 起始消息序号(message_seq,缺省从最新拉)。 */
2034
+ message_seq: z.union([z.number(), z.string()]).optional(),
2035
+ count: z.number().default(20),
2036
+ /** 是否反向排序(NapCat 扩展)。 */
2037
+ reverse_order: z.boolean().optional(),
2038
+ reverseOrder: z.boolean().optional()
2039
+ };
2040
+ /** 解析 message_seq → 起始 msgId(int32 经 MessageUnique 反查,否则原样透传)。 */
2041
+ function resolveStartMsgId(messageSeq, messageUnique) {
2042
+ if (messageSeq === void 0) return;
2043
+ const raw = String(messageSeq);
2044
+ const shortId = Number(raw);
2045
+ if (Number.isFinite(shortId)) return messageUnique.getMsgId(shortId) ?? raw;
2046
+ return raw;
2047
+ }
2048
+ /**
2049
+ * 拉取历史消息并翻译为 OB11 消息信息数组。
2050
+ * 收集全部消息 at uid → 一次批量 uidToUin → 上下文注入(P2-19)。
2051
+ */
2052
+ async function fetchAndTranslateHistory(peer, messageSeq, count, deps) {
2053
+ const startMsgId = resolveStartMsgId(messageSeq, deps.messageUnique);
2054
+ const opts = { count };
2055
+ if (startMsgId !== void 0) opts.msgId = startMsgId;
2056
+ const msgs = await deps.msgApi.fetchMessages(peer, opts);
2057
+ if (msgs.length === 0) throw kernelError(`消息 ${messageSeq ?? "0"} 不存在`, "NOT_FOUND");
2058
+ const atUids = /* @__PURE__ */ new Set();
2059
+ for (const m of msgs) {
2060
+ const { atUids: uids } = collectReceiveNeeds(toCanonicalElements(m));
2061
+ for (const u of uids) atUids.add(u);
2062
+ }
2063
+ let uidToUin;
2064
+ if (atUids.size > 0) try {
2065
+ uidToUin = await deps.uidToUin([...atUids]);
2066
+ } catch {}
2067
+ const ctx = {
2068
+ ...uidToUin !== void 0 ? { uidToUin } : {},
2069
+ msgIdToOb11Id: (m) => deps.messageUnique.getMessageId(m)
2070
+ };
2071
+ return { messages: msgs.map((msg) => toOb11MessageInfo(msg, deps.messageUnique, ctx)) };
2072
+ }
2027
2073
  //#endregion
2028
2074
  //#region src/onebot11/action/message/get-friend-msg-history.ts
2029
2075
  /**
@@ -2033,10 +2079,7 @@ var GetForwardMsgAction = class extends BaseAction {
2033
2079
  */
2034
2080
  const getFriendMsgHistorySchema = z.object({
2035
2081
  user_id: z.number(),
2036
- message_seq: z.union([z.number(), z.string()]).optional(),
2037
- count: z.number().default(20),
2038
- reverse_order: z.boolean().optional(),
2039
- reverseOrder: z.boolean().optional()
2082
+ ...msgHistoryParamsSchema
2040
2083
  });
2041
2084
  /** 获取私聊历史消息(P2-10 接 kernel fetchMessages)。 */
2042
2085
  var GetFriendMsgHistoryAction = class extends BaseAction {
@@ -2049,37 +2092,11 @@ var GetFriendMsgHistoryAction = class extends BaseAction {
2049
2092
  this.deps = deps;
2050
2093
  }
2051
2094
  async _handle(payload) {
2052
- const uid = (await this.deps.uinToUid([String(payload.user_id)])).get(String(payload.user_id));
2053
- if (uid === void 0) throw kernelError(`用户 ${payload.user_id} 的 uid 解析失败`, "INVALID_PARAM");
2054
- const peer = {
2095
+ const uid = await resolveUid(String(payload.user_id), this.deps.uinToUid);
2096
+ return await fetchAndTranslateHistory({
2055
2097
  chatType: ChatType.C2C,
2056
2098
  peerUid: uid
2057
- };
2058
- let startMsgId;
2059
- if (payload.message_seq !== void 0) {
2060
- const raw = String(payload.message_seq);
2061
- const shortId = Number(raw);
2062
- if (Number.isFinite(shortId)) startMsgId = this.deps.messageUnique.getMsgId(shortId);
2063
- startMsgId ??= raw;
2064
- }
2065
- const opts = { count: payload.count };
2066
- if (startMsgId !== void 0) opts.msgId = startMsgId;
2067
- const msgs = await this.deps.msgApi.fetchMessages(peer, opts);
2068
- if (msgs.length === 0) throw kernelError(`消息 ${payload.message_seq ?? "0"} 不存在`, "NOT_FOUND");
2069
- const atUids = /* @__PURE__ */ new Set();
2070
- for (const m of msgs) {
2071
- const { atUids: uids } = collectReceiveNeeds(toCanonicalElements(m));
2072
- for (const u of uids) atUids.add(u);
2073
- }
2074
- let uidToUin;
2075
- if (atUids.size > 0) try {
2076
- uidToUin = await this.deps.uidToUin([...atUids]);
2077
- } catch {}
2078
- const ctx = {
2079
- ...uidToUin !== void 0 ? { uidToUin } : {},
2080
- msgIdToOb11Id: (m) => this.deps.messageUnique.getMessageId(m)
2081
- };
2082
- return { messages: msgs.map((msg) => toOb11MessageInfo(msg, this.deps.messageUnique, ctx)) };
2099
+ }, payload.message_seq, payload.count, this.deps);
2083
2100
  }
2084
2101
  };
2085
2102
  //#endregion
@@ -2092,12 +2109,7 @@ var GetFriendMsgHistoryAction = class extends BaseAction {
2092
2109
  */
2093
2110
  const getGroupMsgHistorySchema = z.object({
2094
2111
  group_id: z.number(),
2095
- /** 起始消息序号(message_seq,缺省从最新拉)。 */
2096
- message_seq: z.union([z.number(), z.string()]).optional(),
2097
- count: z.number().default(20),
2098
- /** 是否反向排序(NapCat 扩展)。 */
2099
- reverse_order: z.boolean().optional(),
2100
- reverseOrder: z.boolean().optional()
2112
+ ...msgHistoryParamsSchema
2101
2113
  });
2102
2114
  /** 获取群历史消息(P2-10 接 kernel fetchMessages)。 */
2103
2115
  var GetGroupMsgHistoryAction = class extends BaseAction {
@@ -2110,35 +2122,10 @@ var GetGroupMsgHistoryAction = class extends BaseAction {
2110
2122
  this.deps = deps;
2111
2123
  }
2112
2124
  async _handle(payload) {
2113
- const peer = {
2125
+ return await fetchAndTranslateHistory({
2114
2126
  chatType: ChatType.GROUP,
2115
2127
  peerUid: String(payload.group_id)
2116
- };
2117
- let startMsgId;
2118
- if (payload.message_seq !== void 0) {
2119
- const raw = String(payload.message_seq);
2120
- const shortId = Number(raw);
2121
- if (Number.isFinite(shortId)) startMsgId = this.deps.messageUnique.getMsgId(shortId);
2122
- startMsgId ??= raw;
2123
- }
2124
- const opts = { count: payload.count };
2125
- if (startMsgId !== void 0) opts.msgId = startMsgId;
2126
- const msgs = await this.deps.msgApi.fetchMessages(peer, opts);
2127
- if (msgs.length === 0) throw kernelError(`消息 ${payload.message_seq ?? "0"} 不存在`, "NOT_FOUND");
2128
- const atUids = /* @__PURE__ */ new Set();
2129
- for (const m of msgs) {
2130
- const { atUids: uids } = collectReceiveNeeds(toCanonicalElements(m));
2131
- for (const u of uids) atUids.add(u);
2132
- }
2133
- let uidToUin;
2134
- if (atUids.size > 0) try {
2135
- uidToUin = await this.deps.uidToUin([...atUids]);
2136
- } catch {}
2137
- const ctx = {
2138
- ...uidToUin !== void 0 ? { uidToUin } : {},
2139
- msgIdToOb11Id: (m) => this.deps.messageUnique.getMessageId(m)
2140
- };
2141
- return { messages: msgs.map((msg) => toOb11MessageInfo(msg, this.deps.messageUnique, ctx)) };
2128
+ }, payload.message_seq, payload.count, this.deps);
2142
2129
  }
2143
2130
  };
2144
2131
  //#endregion
@@ -2154,19 +2141,38 @@ const getMediaSchema = z.object({
2154
2141
  file: z.string().optional(),
2155
2142
  message_id: z.union([z.number(), z.string()]).optional()
2156
2143
  });
2144
+ /**
2145
+ * 反查消息并提取媒体元素路径。
2146
+ * 返回 null = 消息不存在;extract 返回 undefined = 无匹配元素。
2147
+ */
2148
+ async function resolveMedia(id, label, deps, extract) {
2149
+ const { msgId, peer } = resolveMsgIdAndPeer(id, deps.messageUnique);
2150
+ const [first] = await deps.msgApi.fetchMsgsByMsgId(peer, [msgId]);
2151
+ if (first === void 0) throw kernelError(`消息 ${id} 不存在或已被撤回`, "NOT_FOUND");
2152
+ for (const el of first.elements) {
2153
+ const found = extract(el);
2154
+ if (found !== void 0) return found;
2155
+ }
2156
+ throw kernelError(`消息 ${id} 不包含${label}`, "NOT_FOUND");
2157
+ }
2157
2158
  /** 从消息元素提取媒体路径与 URL(PIC/PTT,纯函数)。 */
2158
2159
  function extractMediaPath(element) {
2159
2160
  const pic = element.picElement;
2160
2161
  if (pic !== void 0) {
2161
2162
  const out = {};
2162
2163
  const path = pic.sourcePath ?? pic.picPath;
2163
- if (path !== void 0) out.path = path;
2164
+ if (path !== void 0) out.file = path;
2164
2165
  if (pic.picUrl !== void 0) out.url = pic.picUrl;
2166
+ if (Object.keys(out).length === 0) return;
2165
2167
  return out;
2166
2168
  }
2167
2169
  const ptt = element.pttElement;
2168
- if (ptt !== void 0 && ptt.filePath !== void 0) return { path: ptt.filePath };
2169
- return {};
2170
+ if (ptt !== void 0 && ptt.filePath !== void 0) return { file: ptt.filePath };
2171
+ }
2172
+ /** 提取语音元素(get_record 用)。 */
2173
+ function extractPttPath(element) {
2174
+ const ptt = element.pttElement;
2175
+ if (ptt !== void 0 && ptt.filePath !== void 0) return { file: ptt.filePath };
2170
2176
  }
2171
2177
  /** 获取图片信息(P2-14)。 */
2172
2178
  var GetImageAction = class extends BaseAction {
@@ -2179,28 +2185,12 @@ var GetImageAction = class extends BaseAction {
2179
2185
  this.deps = deps;
2180
2186
  }
2181
2187
  _handle(payload) {
2182
- return this.resolveMedia(payload, "图片", "get_image");
2188
+ return this.resolve(payload, "图片", "get_image");
2183
2189
  }
2184
- /** 反查消息并提取媒体元素路径。 */
2185
- async resolveMedia(payload, label, actionName) {
2190
+ resolve(payload, label, actionName) {
2186
2191
  const id = payload.message_id ?? payload.file;
2187
2192
  if (id === void 0) throw kernelError(`${actionName} 需要 message_id 或 file`, "INVALID_PARAM");
2188
- const { msgId, peer } = resolveMsgIdAndPeer(id, this.deps.messageUnique);
2189
- const [first] = await this.deps.msgApi.fetchMsgsByMsgId(peer, [msgId]);
2190
- if (first === void 0) throw kernelError(`消息 ${id} 不存在或已被撤回`, "NOT_FOUND");
2191
- let found;
2192
- for (const el of first.elements) {
2193
- const media = extractMediaPath(el);
2194
- if (media.path !== void 0 || media.url !== void 0) {
2195
- found = media;
2196
- break;
2197
- }
2198
- }
2199
- if (found === void 0) throw kernelError(`消息 ${id} 不包含${label}`, "NOT_FOUND");
2200
- const result = {};
2201
- if (found.path !== void 0) result.file = found.path;
2202
- if (found.url !== void 0) result.url = found.url;
2203
- return result;
2193
+ return resolveMedia(id, label, this.deps, extractMediaPath);
2204
2194
  }
2205
2195
  };
2206
2196
  /** 获取语音信息(P2-14)。 */
@@ -2216,14 +2206,7 @@ var GetRecordAction = class extends BaseAction {
2216
2206
  async _handle(payload) {
2217
2207
  const id = payload.message_id ?? payload.file;
2218
2208
  if (id === void 0) throw kernelError("get_record 需要 message_id 或 file", "INVALID_PARAM");
2219
- const { msgId, peer } = resolveMsgIdAndPeer(id, this.deps.messageUnique);
2220
- const [first] = await this.deps.msgApi.fetchMsgsByMsgId(peer, [msgId]);
2221
- if (first === void 0) throw kernelError(`消息 ${id} 不存在或已被撤回`, "NOT_FOUND");
2222
- for (const el of first.elements) {
2223
- const ptt = el.pttElement;
2224
- if (ptt !== void 0 && ptt.filePath !== void 0) return { file: ptt.filePath };
2225
- }
2226
- throw kernelError(`消息 ${id} 不包含语音`, "NOT_FOUND");
2209
+ return await resolveMedia(id, "语音", this.deps, extractPttPath);
2227
2210
  }
2228
2211
  };
2229
2212
  //#endregion
@@ -2245,9 +2228,7 @@ var GetMsgAction = class extends BaseAction {
2245
2228
  this.deps = deps;
2246
2229
  }
2247
2230
  async _handle(payload) {
2248
- const { msgId, peer } = resolveMsgIdAndPeer(payload.message_id, this.deps.messageUnique);
2249
- const [msg] = await this.deps.msgApi.fetchMsgsByMsgId(peer, [msgId]);
2250
- if (msg === void 0) throw kernelError(`消息 ${payload.message_id} 不存在或已被撤回`, "NOT_FOUND");
2231
+ const { msg } = await fetchMsgById(payload.message_id, this.deps.messageUnique, this.deps.msgApi);
2251
2232
  const { atUids } = collectReceiveNeeds(toCanonicalElements(msg));
2252
2233
  let uidToUin;
2253
2234
  if (atUids.length > 0) try {
@@ -2292,8 +2273,7 @@ var MarkMsgAsReadAction = class extends BaseAction {
2292
2273
  return null;
2293
2274
  }
2294
2275
  if (payload.user_id !== void 0) {
2295
- const uid = (await this.deps.uinToUid([String(payload.user_id)])).get(String(payload.user_id));
2296
- if (uid === void 0) throw kernelError(`用户 ${payload.user_id} 的 uid 解析失败`, "INVALID_PARAM");
2276
+ const uid = await resolveUid(String(payload.user_id), this.deps.uinToUid);
2297
2277
  await this.deps.msgApi.markRead({
2298
2278
  chatType: ChatType.C2C,
2299
2279
  peerUid: uid
@@ -2324,8 +2304,7 @@ var MarkPrivateMsgAsReadAction = class extends BaseAction {
2324
2304
  this.deps = deps;
2325
2305
  }
2326
2306
  async _handle(payload) {
2327
- const uid = (await this.deps.uinToUid([String(payload.user_id)])).get(String(payload.user_id));
2328
- if (uid === void 0) throw kernelError(`用户 ${payload.user_id} 的 uid 解析失败`, "INVALID_PARAM");
2307
+ const uid = await resolveUid(String(payload.user_id), this.deps.uinToUid);
2329
2308
  await this.deps.msgApi.markRead({
2330
2309
  chatType: ChatType.C2C,
2331
2310
  peerUid: uid
@@ -2393,6 +2372,14 @@ function resolveSourceMessages(messages, unique) {
2393
2372
  }
2394
2373
  return out;
2395
2374
  }
2375
+ /** 合并转发发送(群/私聊共用:解析源消息 → 发送 → 映射 message_id)。 */
2376
+ async function sendForward(messages, target, deps) {
2377
+ const sources = resolveSourceMessages(messages, deps.messageUnique);
2378
+ const [firstSource] = sources;
2379
+ if (firstSource === void 0) throw kernelError("合并转发需要至少一条可解析的源消息(node.id)", "INVALID_PARAM");
2380
+ const { msgId } = await deps.msgApi.sendForwardMessage(target, firstSource.peer, sources.map((s) => s.msgId));
2381
+ return { message_id: deps.messageUnique.alloc(msgId, target) };
2382
+ }
2396
2383
  /** 发送群合并转发(P2-12 接 kernel sendForwardMessage)。 */
2397
2384
  var SendGroupForwardMsgAction = class extends BaseAction {
2398
2385
  name = "send_group_forward_msg";
@@ -2404,15 +2391,11 @@ var SendGroupForwardMsgAction = class extends BaseAction {
2404
2391
  this.deps = deps;
2405
2392
  }
2406
2393
  async _handle(payload) {
2407
- const sources = resolveSourceMessages(payload.messages, this.deps.messageUnique);
2408
- const [firstSource] = sources;
2409
- if (firstSource === void 0) throw kernelError("合并转发需要至少一条可解析的源消息(node.id)", "INVALID_PARAM");
2410
2394
  const target = {
2411
2395
  chatType: ChatType.GROUP,
2412
2396
  peerUid: String(payload.group_id)
2413
2397
  };
2414
- const { msgId } = await this.deps.msgApi.sendForwardMessage(target, firstSource.peer, sources.map((s) => s.msgId));
2415
- return { message_id: this.deps.messageUnique.alloc(msgId, target) };
2398
+ return await sendForward(payload.messages, target, this.deps);
2416
2399
  }
2417
2400
  };
2418
2401
  /** 发送私聊合并转发(P2-12)。 */
@@ -2426,31 +2409,16 @@ var SendPrivateForwardMsgAction = class extends BaseAction {
2426
2409
  this.deps = deps;
2427
2410
  }
2428
2411
  async _handle(payload) {
2429
- const sources = resolveSourceMessages(payload.messages, this.deps.messageUnique);
2430
- const [firstSource] = sources;
2431
- if (firstSource === void 0) throw kernelError("合并转发需要至少一条可解析的源消息(node.id)", "INVALID_PARAM");
2432
- const uid = (await this.deps.uinToUid([String(payload.user_id)])).get(String(payload.user_id));
2433
- if (uid === void 0) throw kernelError(`用户 ${payload.user_id} 的 uid 解析失败`, "INVALID_PARAM");
2412
+ const uid = await resolveUid(String(payload.user_id), this.deps.uinToUid);
2434
2413
  const target = {
2435
2414
  chatType: ChatType.C2C,
2436
2415
  peerUid: uid
2437
2416
  };
2438
- const { msgId } = await this.deps.msgApi.sendForwardMessage(target, firstSource.peer, sources.map((s) => s.msgId));
2439
- return { message_id: this.deps.messageUnique.alloc(msgId, target) };
2417
+ return await sendForward(payload.messages, target, this.deps);
2440
2418
  }
2441
2419
  };
2442
2420
  //#endregion
2443
2421
  //#region src/onebot11/action/message/send-msg.ts
2444
- /**
2445
- * send_msg 动作:发送消息(群聊/私聊)+ 共享发送核心 sendOb11Message
2446
- *
2447
- * P2-3 真实化:注入 kernel MsgApi,message 参数(CQ 码字符串或 segment 数组)
2448
- * 翻译为 canonical 元素 → MsgApi.sendMessage → 返回真实 message_id。
2449
- * send_private_msg / send_group_msg 复用 sendOb11Message(P2-10)。
2450
- *
2451
- * 群聊:group_id → Peer{ chatType: GROUP, peerUid: String(group_id) }(群消息 peerUid=群号)。
2452
- * 私聊:user_id 经 uin→uid → Peer{ chatType: C2C, peerUid: uid }。
2453
- */
2454
2422
  const sendMsgSchema = z.object({
2455
2423
  message_type: z.enum(["group", "private"]).optional(),
2456
2424
  user_id: z.number().optional(),
@@ -2458,25 +2426,9 @@ const sendMsgSchema = z.object({
2458
2426
  message: z.union([z.string(), z.array(z.unknown())]),
2459
2427
  auto_escape: z.boolean().optional()
2460
2428
  });
2461
- /** 解析目标 Peer(group_id 直通;user_id 经 uin→uid)。 */
2462
- async function resolvePeer(payload, deps) {
2463
- if (payload.group_id !== void 0) return {
2464
- chatType: ChatType.GROUP,
2465
- peerUid: String(payload.group_id)
2466
- };
2467
- if (payload.user_id !== void 0) {
2468
- const uid = (await deps.uinToUid([String(payload.user_id)])).get(String(payload.user_id));
2469
- if (uid === void 0) throw kernelError(`用户 ${payload.user_id} 的 uid 解析失败`, "INVALID_PARAM");
2470
- return {
2471
- chatType: ChatType.C2C,
2472
- peerUid: uid
2473
- };
2474
- }
2475
- throw kernelError("send_msg 需要 group_id 或 user_id", "INVALID_PARAM");
2476
- }
2477
2429
  /** 共享发送核心:解析 Peer → canonical 翻译(含 auto_escape)→ sendMessage → 映射 message_id。 */
2478
2430
  async function sendOb11Message(payload, deps) {
2479
- const peer = await resolvePeer(payload, deps);
2431
+ const peer = await resolveTargetPeer(payload, deps, "send_msg 需要 group_id 或 user_id");
2480
2432
  let canonical;
2481
2433
  if (Array.isArray(payload.message)) canonical = segmentsToCanonical(payload.message);
2482
2434
  else canonical = cqMessageToCanonical(payload.message);
@@ -2592,8 +2544,7 @@ var SetInputStatusAction = class extends BaseAction {
2592
2544
  this.deps = deps;
2593
2545
  }
2594
2546
  async _handle(payload) {
2595
- const uid = (await this.deps.uinToUid([String(payload.user_id)])).get(String(payload.user_id));
2596
- if (uid === void 0) throw kernelError(`用户 ${payload.user_id} 的 uid 解析失败`, "INVALID_PARAM");
2547
+ const uid = await resolveUid(String(payload.user_id), this.deps.uinToUid);
2597
2548
  await this.deps.msgApi.setInputStatus({
2598
2549
  chatType: ChatType.C2C,
2599
2550
  peerUid: uid
@@ -2628,9 +2579,7 @@ var SetMsgEmojiLikeAction = class extends BaseAction {
2628
2579
  this.deps = deps;
2629
2580
  }
2630
2581
  async _handle(payload) {
2631
- const { msgId, peer } = resolveMsgIdAndPeer(payload.message_id, this.deps.messageUnique);
2632
- const [msg] = await this.deps.msgApi.fetchMsgsByMsgId(peer, [msgId]);
2633
- if (msg === void 0) throw kernelError(`消息 ${payload.message_id} 不存在或已被撤回`, "NOT_FOUND");
2582
+ const { msg, peer } = await fetchMsgById(payload.message_id, this.deps.messageUnique, this.deps.msgApi);
2634
2583
  await this.deps.msgApi.setMsgEmojiLike(peer, {
2635
2584
  msgSeq: msg.msgSeq,
2636
2585
  emojiId: String(payload.emoji_id),
@@ -3424,15 +3373,27 @@ function makeAuthorize(token) {
3424
3373
  return hasAccessTokenQuery(ctx.url, token);
3425
3374
  };
3426
3375
  }
3376
+ /**
3377
+ * OneBot 客户端标识头:koishi 的 onebot 适配器按
3378
+ * x-client-role === "Universal" 与 x-self-id 校验 WS 连接(NapCat 同款头),
3379
+ * 缺失会被 close(1008) 拒绝——连接虽握手成功但应用层不认。
3380
+ */
3381
+ function oneBotClientHeaders(selfUin) {
3382
+ return {
3383
+ "X-Self-ID": selfUin,
3384
+ "x-client-role": "Universal",
3385
+ "User-Agent": "OneBot/11"
3386
+ };
3387
+ }
3427
3388
  /** 按配置装配 OB11 传输。 */
3428
3389
  function assembleOb11Transports(opts) {
3429
- const { config, broadcaster, handleRequest } = opts;
3390
+ const { config, broadcaster, handleRequest, selfUin } = opts;
3430
3391
  const servers = [];
3431
3392
  const transports = [];
3432
3393
  assembleHttpServers(config, handleRequest, servers);
3433
3394
  assembleWsServers(config, handleRequest, servers);
3434
- assembleHttpPosts(config, transports);
3435
- assembleWsReverses(config, handleRequest, transports);
3395
+ assembleHttpPosts(config, selfUin, transports);
3396
+ assembleWsReverses(config, selfUin, handleRequest, transports);
3436
3397
  for (const t of transports) broadcaster.register(t);
3437
3398
  for (const s of servers) broadcaster.register(s);
3438
3399
  return {
@@ -3474,19 +3435,27 @@ function assembleWsServers(config, handleRequest, servers) {
3474
3435
  }
3475
3436
  }
3476
3437
  /** 装配 HTTP 正向上报 client 列表(fire-and-forget)。 */
3477
- function assembleHttpPosts(config, transports) {
3438
+ function assembleHttpPosts(config, selfUin, transports) {
3478
3439
  for (const item of config.httpPostUrls) if (item.enabled && item.url !== void 0) {
3479
- const headers = authorizeHeader(item.token ?? config.token);
3480
- const options = { url: item.url };
3481
- if (headers !== void 0) options.headers = headers;
3440
+ const headers = {
3441
+ "x-self-id": selfUin,
3442
+ ...authorizeHeader(item.token ?? config.token) ?? {}
3443
+ };
3444
+ const options = {
3445
+ url: item.url,
3446
+ headers
3447
+ };
3482
3448
  if (item.timeoutMs !== void 0) options.timeoutMs = item.timeoutMs;
3483
3449
  transports.push(new HttpClient(options));
3484
3450
  }
3485
3451
  }
3486
3452
  /** 装配正向 WS client 列表(双向,心跳 + 重连 + 证书选项)。 */
3487
- function assembleWsReverses(config, handleRequest, transports) {
3453
+ function assembleWsReverses(config, selfUin, handleRequest, transports) {
3488
3454
  for (const item of config.wsReverseUrls) if (item.enabled && item.url !== void 0) {
3489
- const headers = authorizeHeader(item.token ?? config.token);
3455
+ const headers = {
3456
+ ...oneBotClientHeaders(selfUin),
3457
+ ...authorizeHeader(item.token ?? config.token) ?? {}
3458
+ };
3490
3459
  const reconnect = {
3491
3460
  enabled: true,
3492
3461
  delayMs: item.reconnectDelayMs
@@ -3494,11 +3463,11 @@ function assembleWsReverses(config, handleRequest, transports) {
3494
3463
  if (item.maxReconnectAttempts !== void 0) reconnect.maxAttempts = item.maxReconnectAttempts;
3495
3464
  const options = {
3496
3465
  url: item.url,
3466
+ headers,
3497
3467
  heartbeat: { intervalMs: item.heartbeatInterval },
3498
3468
  reconnect,
3499
3469
  rejectUnauthorized: item.rejectUnauthorized
3500
3470
  };
3501
- if (headers !== void 0) options.headers = headers;
3502
3471
  const client = new WsClient(options);
3503
3472
  client.onRequest = handleRequest;
3504
3473
  transports.push(client);
@@ -3552,6 +3521,7 @@ var NapukettoOneBot11Adapter = class extends BaseProtocolAdapter {
3552
3521
  this.transports = assembleOb11Transports({
3553
3522
  config,
3554
3523
  broadcaster,
3524
+ selfUin: this.selfUin,
3555
3525
  handleRequest: (req, respond) => {
3556
3526
  this.handleRequest(req, respond).catch((err) => {
3557
3527
  let message = String(err);
@@ -3611,16 +3581,14 @@ var NapukettoOneBot11Adapter = class extends BaseProtocolAdapter {
3611
3581
  subscribe() {
3612
3582
  if (this.unsubscribe !== null) return;
3613
3583
  this.unsubscribe = this.msgChannel.on("Msg/onRecvMsg", (msgs) => {
3614
- const list = Array.isArray(msgs) ? msgs : [msgs];
3615
- for (const msg of list) {
3616
- if (!msg || typeof msg !== "object") continue;
3584
+ forEachRawMessage(msgs, (msg) => {
3617
3585
  if (hasGrayTip(msg)) {
3618
3586
  this.broadcastNotice(msg).catch(() => {});
3619
- continue;
3587
+ return;
3620
3588
  }
3621
- if (!this.reportSelfMessage && String(msg.senderUin) === this.selfUin) continue;
3589
+ if (!this.reportSelfMessage && String(msg.senderUin) === this.selfUin) return;
3622
3590
  this.broadcastMessageEvent(msg);
3623
- }
3591
+ });
3624
3592
  });
3625
3593
  }
3626
3594
  /**