@napuketto/adapter 0.0.1 → 0.0.3
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.
- package/README.md +15 -0
- package/dist/core/index.d.mts +1 -1
- package/dist/core-Bj5L8WeZ.mjs.map +1 -1
- package/dist/gray-tip-CcpfI1Og.d.mts +1 -0
- package/dist/{index-C1nIfPiU.d.mts → index-CmhGncOe.d.mts} +6 -4
- package/dist/index-CmhGncOe.d.mts.map +1 -0
- package/dist/onebot11/index.d.mts +110 -105
- package/dist/onebot11/index.d.mts.map +1 -1
- package/dist/onebot11/index.mjs +417 -431
- package/dist/onebot11/index.mjs.map +1 -1
- package/dist/raw-message-Du7tvR1D.mjs +59 -0
- package/dist/raw-message-Du7tvR1D.mjs.map +1 -0
- package/dist/satori/index.d.mts +488 -0
- package/dist/satori/index.d.mts.map +1 -0
- package/dist/satori/index.mjs +1891 -0
- package/dist/satori/index.mjs.map +1 -0
- package/package.json +8 -4
- package/dist/index-C1nIfPiU.d.mts.map +0 -1
package/dist/onebot11/index.mjs
CHANGED
|
@@ -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 =
|
|
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 =
|
|
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
|
|
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/
|
|
329
|
+
//#region src/onebot11/action/group/set-essence-msg.ts
|
|
326
330
|
/**
|
|
327
|
-
*
|
|
331
|
+
* set_essence_msg 动作:设置精华消息(P2-10 接 kernel GroupApi.addGroupEssence)
|
|
332
|
+
*
|
|
333
|
+
* message_id → msgId + peer(MessageUnique 反查,需群消息)→ addGroupEssence。
|
|
328
334
|
*/
|
|
329
|
-
const
|
|
330
|
-
/**
|
|
331
|
-
var
|
|
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.
|
|
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 =
|
|
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 =
|
|
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 =
|
|
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/
|
|
1324
|
+
//#region src/onebot11/action/message/resolve-peer.ts
|
|
1335
1325
|
/**
|
|
1336
|
-
*
|
|
1326
|
+
* OB11 目标 Peer 解析 / 单条消息拉取(多个动作共用,2026-08-08 克隆合并)
|
|
1337
1327
|
*
|
|
1338
|
-
*
|
|
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(
|
|
1344
|
+
throw kernelError(missingHint, "INVALID_PARAM");
|
|
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
|
+
};
|
|
1361
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
|
}
|
|
@@ -1385,90 +1396,7 @@ var ForwardFriendSingleMsgAction = class extends ForwardSingleMsgBase {
|
|
|
1385
1396
|
schema = forwardSingleMsgSchema;
|
|
1386
1397
|
};
|
|
1387
1398
|
//#endregion
|
|
1388
|
-
//#region src/onebot11/helper/
|
|
1389
|
-
/**
|
|
1390
|
-
* OneBot 11 协议配置(zod schema,归属本包,ADR-012)
|
|
1391
|
-
* 协议配置 schema 在各自协议包,kernel 只提供 ConfigBase 基类。
|
|
1392
|
-
*
|
|
1393
|
-
* 2026-08-07 多实例化(P2-18):对齐 NapCat——httpServers / httpPostUrls / wsServers /
|
|
1394
|
-
* wsReverseUrls 四个数组实例(每实例可覆盖全局 token);旧单对象字段 http/httpPost/ws/
|
|
1395
|
-
* wsReverse 废弃(zod strip 静默忽略,不兼容迁移)。
|
|
1396
|
-
*/
|
|
1397
|
-
/** HTTP 反向服务器默认端口。 */
|
|
1398
|
-
const DEFAULT_HTTP_PORT = 3e3;
|
|
1399
|
-
/** 反向 WS server 默认端口。 */
|
|
1400
|
-
const DEFAULT_WS_PORT = 3001;
|
|
1401
|
-
/** 心跳 meta 事件默认间隔(毫秒)。 */
|
|
1402
|
-
const DEFAULT_HEARTBEAT_INTERVAL_MS = 3e3;
|
|
1403
|
-
/** WS 传输 ping 默认间隔(毫秒)。 */
|
|
1404
|
-
const DEFAULT_WS_HEARTBEAT_MS = 3e4;
|
|
1405
|
-
/** WS 反向断线重连默认延迟(毫秒)。 */
|
|
1406
|
-
const DEFAULT_RECONNECT_DELAY_MS = 5e3;
|
|
1407
|
-
/** 消息内容格式(NapCat messagePostFormat)。 */
|
|
1408
|
-
const MESSAGE_FORMATS = ["array", "string"];
|
|
1409
|
-
/** 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
|
-
});
|
|
1417
|
-
/** HTTP 正向上报实例(Webhook 事件推送)。 */
|
|
1418
|
-
const httpPostSchema = z.object({
|
|
1419
|
-
enabled: z.boolean().default(false),
|
|
1420
|
-
url: z.string().optional(),
|
|
1421
|
-
/** 实例级 token,覆盖全局 token(缺省继承全局)。 */
|
|
1422
|
-
token: z.string().optional(),
|
|
1423
|
-
/** 上报超时(毫秒),缺省不超时。 */
|
|
1424
|
-
timeoutMs: z.number().int().optional()
|
|
1425
|
-
});
|
|
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
|
-
});
|
|
1436
|
-
/** 正向 WS client 实例(主动连接第三方,双向)。 */
|
|
1437
|
-
const wsReverseSchema = z.object({
|
|
1438
|
-
enabled: z.boolean().default(false),
|
|
1439
|
-
url: z.string().optional(),
|
|
1440
|
-
/** 实例级 token,覆盖全局 token(缺省继承全局)。 */
|
|
1441
|
-
token: z.string().optional(),
|
|
1442
|
-
/** 断线重连延迟(毫秒)。 */
|
|
1443
|
-
reconnectDelayMs: z.number().int().default(DEFAULT_RECONNECT_DELAY_MS),
|
|
1444
|
-
/** 最大重连次数(缺省无限)。 */
|
|
1445
|
-
maxReconnectAttempts: z.number().int().optional(),
|
|
1446
|
-
/** wss:// 是否校验证书(自签证书场景设 false,对齐 NapCat enableSelfSigned)。 */
|
|
1447
|
-
rejectUnauthorized: z.boolean().default(true),
|
|
1448
|
-
/** WS ping 间隔(毫秒)。 */
|
|
1449
|
-
heartbeatInterval: z.number().int().default(DEFAULT_WS_HEARTBEAT_MS)
|
|
1450
|
-
});
|
|
1451
|
-
/** OB11 配置 schema。 */
|
|
1452
|
-
const ob11ConfigSchema = z.object({
|
|
1453
|
-
/** 鉴权 token(全局默认,实例未指定时继承)。 */
|
|
1454
|
-
token: z.string().optional(),
|
|
1455
|
-
/** 心跳 meta 事件间隔(毫秒),0 关闭心跳。 */
|
|
1456
|
-
heartbeatInterval: z.number().int().default(DEFAULT_HEARTBEAT_INTERVAL_MS),
|
|
1457
|
-
/** 是否上报机器人自己发的消息(缺省 false = OB11 规范行为)。 */
|
|
1458
|
-
reportSelfMessage: z.boolean().default(false),
|
|
1459
|
-
/** 消息内容格式:array = 消息段数组(标准),string = CQ 码字符串。 */
|
|
1460
|
-
messagePostFormat: z.enum(MESSAGE_FORMATS).default("array"),
|
|
1461
|
-
/** HTTP 反向服务器列表。 */
|
|
1462
|
-
httpServers: z.array(httpServerSchema).default([]),
|
|
1463
|
-
/** HTTP 正向上报列表。 */
|
|
1464
|
-
httpPostUrls: z.array(httpPostSchema).default([]),
|
|
1465
|
-
/** 反向 WS server 列表。 */
|
|
1466
|
-
wsServers: z.array(wsServerSchema).default([]),
|
|
1467
|
-
/** 正向 WS client 列表。 */
|
|
1468
|
-
wsReverseUrls: z.array(wsReverseSchema).default([])
|
|
1469
|
-
});
|
|
1470
|
-
//#endregion
|
|
1471
|
-
//#region src/onebot11/helper/cqcode.ts
|
|
1399
|
+
//#region src/onebot11/helper/codec/cqcode.ts
|
|
1472
1400
|
/**
|
|
1473
1401
|
* CQ 码编解码(OneBot 11 消息段序列化格式)
|
|
1474
1402
|
*
|
|
@@ -1537,7 +1465,7 @@ function serializeCqParts(parts) {
|
|
|
1537
1465
|
return out;
|
|
1538
1466
|
}
|
|
1539
1467
|
//#endregion
|
|
1540
|
-
//#region src/onebot11/helper/
|
|
1468
|
+
//#region src/onebot11/helper/codec/segment.ts
|
|
1541
1469
|
/** OB11 媒体段 data(image/record/video 共用,url 可选)。 */
|
|
1542
1470
|
function mediaData(file, url) {
|
|
1543
1471
|
const data = { file };
|
|
@@ -1573,80 +1501,95 @@ function rawToString(raw) {
|
|
|
1573
1501
|
if (typeof raw === "string") return raw;
|
|
1574
1502
|
return JSON.stringify(raw);
|
|
1575
1503
|
}
|
|
1576
|
-
/** canonical 元素 → OB11 segment
|
|
1577
|
-
|
|
1578
|
-
|
|
1504
|
+
/** canonical 元素 → OB11 segment 转换器(键 = canonical type,判别字段收窄)。 */
|
|
1505
|
+
const CANONICAL_TO_SEGMENT = {
|
|
1506
|
+
text: (el) => ({
|
|
1579
1507
|
type: "text",
|
|
1580
1508
|
data: { text: el.text }
|
|
1581
|
-
}
|
|
1582
|
-
|
|
1509
|
+
}),
|
|
1510
|
+
at: (el) => ({
|
|
1583
1511
|
type: "at",
|
|
1584
1512
|
data: atData(el.target, el.display)
|
|
1585
|
-
}
|
|
1586
|
-
|
|
1513
|
+
}),
|
|
1514
|
+
image: (el) => ({
|
|
1587
1515
|
type: "image",
|
|
1588
1516
|
data: mediaData(el.path, el.url)
|
|
1589
|
-
}
|
|
1590
|
-
|
|
1517
|
+
}),
|
|
1518
|
+
face: (el) => ({
|
|
1591
1519
|
type: "face",
|
|
1592
1520
|
data: { id: el.id }
|
|
1593
|
-
}
|
|
1594
|
-
|
|
1521
|
+
}),
|
|
1522
|
+
voice: (el) => ({
|
|
1595
1523
|
type: "record",
|
|
1596
1524
|
data: mediaData(el.path, el.url)
|
|
1597
|
-
}
|
|
1598
|
-
|
|
1525
|
+
}),
|
|
1526
|
+
video: (el) => ({
|
|
1599
1527
|
type: "video",
|
|
1600
1528
|
data: mediaData(el.path, el.url)
|
|
1601
|
-
}
|
|
1602
|
-
|
|
1529
|
+
}),
|
|
1530
|
+
reply: (el) => ({
|
|
1603
1531
|
type: "reply",
|
|
1604
1532
|
data: { id: el.messageId }
|
|
1605
|
-
}
|
|
1606
|
-
|
|
1533
|
+
}),
|
|
1534
|
+
forward: (el) => ({
|
|
1607
1535
|
type: "forward",
|
|
1608
1536
|
data: { id: el.messageIds[0] ?? "" }
|
|
1609
|
-
}
|
|
1610
|
-
|
|
1537
|
+
}),
|
|
1538
|
+
json: (el) => ({
|
|
1611
1539
|
type: "json",
|
|
1612
1540
|
data: { data: rawToString(el.raw) }
|
|
1613
|
-
}
|
|
1614
|
-
|
|
1541
|
+
}),
|
|
1542
|
+
xml: (el) => ({
|
|
1615
1543
|
type: "xml",
|
|
1616
1544
|
data: { data: rawToString(el.raw) }
|
|
1617
|
-
}
|
|
1618
|
-
|
|
1545
|
+
})
|
|
1546
|
+
};
|
|
1547
|
+
/** canonical 元素 → OB11 segment(null = OB11 无法表达,跳过)。 */
|
|
1548
|
+
function canonicalToSegment(el) {
|
|
1549
|
+
const builder = CANONICAL_TO_SEGMENT[el.type];
|
|
1550
|
+
return builder === void 0 ? null : builder(el);
|
|
1619
1551
|
}
|
|
1620
|
-
/** OB11 segment → canonical
|
|
1621
|
-
|
|
1622
|
-
|
|
1552
|
+
/** OB11 segment → canonical 元素转换器(键 = segment type)。 */
|
|
1553
|
+
const SEGMENT_TO_CANONICAL = {
|
|
1554
|
+
text: (seg) => ({
|
|
1623
1555
|
type: "text",
|
|
1624
1556
|
text: seg.data.text
|
|
1625
|
-
}
|
|
1626
|
-
|
|
1627
|
-
|
|
1557
|
+
}),
|
|
1558
|
+
string: (seg) => ({
|
|
1559
|
+
type: "text",
|
|
1560
|
+
text: seg.data.text
|
|
1561
|
+
}),
|
|
1562
|
+
at: (seg) => canonicalAt(seg.data.qq, seg.data.name),
|
|
1563
|
+
face: (seg) => ({
|
|
1628
1564
|
type: "face",
|
|
1629
1565
|
id: seg.data.id
|
|
1630
|
-
}
|
|
1631
|
-
|
|
1632
|
-
|
|
1633
|
-
|
|
1634
|
-
|
|
1566
|
+
}),
|
|
1567
|
+
image: (seg) => canonicalMedia("image", seg.data.file, seg.data.url),
|
|
1568
|
+
record: (seg) => canonicalMedia("voice", seg.data.file, seg.data.url),
|
|
1569
|
+
video: (seg) => canonicalMedia("video", seg.data.file, seg.data.url),
|
|
1570
|
+
reply: (seg) => ({
|
|
1635
1571
|
type: "reply",
|
|
1636
1572
|
messageId: seg.data.id
|
|
1637
|
-
}
|
|
1638
|
-
|
|
1573
|
+
}),
|
|
1574
|
+
forward: (seg) => ({
|
|
1639
1575
|
type: "forward",
|
|
1640
1576
|
messageIds: [seg.data.id]
|
|
1641
|
-
}
|
|
1642
|
-
|
|
1577
|
+
}),
|
|
1578
|
+
json: (seg) => ({
|
|
1643
1579
|
type: "json",
|
|
1644
1580
|
raw: seg.data.data
|
|
1645
|
-
}
|
|
1646
|
-
|
|
1581
|
+
}),
|
|
1582
|
+
xml: (seg) => ({
|
|
1647
1583
|
type: "xml",
|
|
1648
1584
|
raw: seg.data.data
|
|
1649
|
-
}
|
|
1585
|
+
})
|
|
1586
|
+
};
|
|
1587
|
+
/** OB11 segment → canonical 元素(未知 type 兜底按 xml 处理,保持既有行为)。 */
|
|
1588
|
+
function segmentToCanonical(seg) {
|
|
1589
|
+
const builder = SEGMENT_TO_CANONICAL[seg.type];
|
|
1590
|
+
if (builder !== void 0) return builder(seg);
|
|
1591
|
+
const xmlBuilder = SEGMENT_TO_CANONICAL["xml"];
|
|
1592
|
+
return xmlBuilder === void 0 ? null : xmlBuilder(seg);
|
|
1650
1593
|
}
|
|
1651
1594
|
/** 已知 CQ 类型 → segment 构造器。 */
|
|
1652
1595
|
const CQ_SEGMENT_BUILDERS = {
|
|
@@ -1700,6 +1643,86 @@ function cqCodeToSegment(code) {
|
|
|
1700
1643
|
};
|
|
1701
1644
|
return builder(code.params);
|
|
1702
1645
|
}
|
|
1646
|
+
/** OB11 segment 数组 → CQ 码字符串(text 段转义拼接,特殊段编码)。 */
|
|
1647
|
+
function segmentsToCqMessage(segments) {
|
|
1648
|
+
let out = "";
|
|
1649
|
+
for (const seg of segments) if (seg.type === "text" || seg.type === "string") out += escapeCqText(seg.data.text);
|
|
1650
|
+
else out += encodeCqCode(seg.type, seg.data);
|
|
1651
|
+
return out;
|
|
1652
|
+
}
|
|
1653
|
+
//#endregion
|
|
1654
|
+
//#region src/onebot11/helper/config.ts
|
|
1655
|
+
/**
|
|
1656
|
+
* OneBot 11 协议配置(zod schema,归属本包,ADR-012)
|
|
1657
|
+
* 协议配置 schema 在各自协议包,kernel 只提供 ConfigBase 基类。
|
|
1658
|
+
*
|
|
1659
|
+
* 2026-08-07 多实例化(P2-18):对齐 NapCat——httpServers / httpPostUrls / wsServers /
|
|
1660
|
+
* wsReverseUrls 四个数组实例(每实例可覆盖全局 token);旧单对象字段 http/httpPost/ws/
|
|
1661
|
+
* wsReverse 废弃(zod strip 静默忽略,不兼容迁移)。
|
|
1662
|
+
*/
|
|
1663
|
+
/** HTTP 反向服务器默认端口。 */
|
|
1664
|
+
const DEFAULT_HTTP_PORT = 3e3;
|
|
1665
|
+
/** 反向 WS server 默认端口。 */
|
|
1666
|
+
const DEFAULT_WS_PORT = 3001;
|
|
1667
|
+
/** 心跳 meta 事件默认间隔(毫秒)。 */
|
|
1668
|
+
const DEFAULT_HEARTBEAT_INTERVAL_MS = 3e3;
|
|
1669
|
+
/** WS 传输 ping 默认间隔(毫秒)。 */
|
|
1670
|
+
const DEFAULT_WS_HEARTBEAT_MS = 3e4;
|
|
1671
|
+
/** WS 反向断线重连默认延迟(毫秒)。 */
|
|
1672
|
+
const DEFAULT_RECONNECT_DELAY_MS = 5e3;
|
|
1673
|
+
/** 消息内容格式(NapCat messagePostFormat)。 */
|
|
1674
|
+
const MESSAGE_FORMATS = ["array", "string"];
|
|
1675
|
+
/** HTTP 反向服务器实例(第三方 POST 指令入口)。 */
|
|
1676
|
+
const httpServerSchema = createHttpServerSchema(DEFAULT_HTTP_PORT);
|
|
1677
|
+
/** HTTP 正向上报实例(Webhook 事件推送)。 */
|
|
1678
|
+
const httpPostSchema = z.object({
|
|
1679
|
+
enabled: z.boolean().default(false),
|
|
1680
|
+
url: z.string().optional(),
|
|
1681
|
+
/** 实例级 token,覆盖全局 token(缺省继承全局)。 */
|
|
1682
|
+
token: z.string().optional(),
|
|
1683
|
+
/** 上报超时(毫秒),缺省不超时。 */
|
|
1684
|
+
timeoutMs: z.number().int().optional()
|
|
1685
|
+
});
|
|
1686
|
+
/** 反向 WS server 实例(第三方 WS 连入,带 WS ping 间隔扩展字段)。 */
|
|
1687
|
+
const wsServerSchema = createWsServerSchema(DEFAULT_WS_PORT).extend({
|
|
1688
|
+
/** WS ping 间隔(毫秒)。 */
|
|
1689
|
+
heartbeatInterval: z.number().int().default(DEFAULT_WS_HEARTBEAT_MS) });
|
|
1690
|
+
/** 正向 WS client 实例(主动连接第三方,双向)。 */
|
|
1691
|
+
const wsReverseSchema = z.object({
|
|
1692
|
+
enabled: z.boolean().default(false),
|
|
1693
|
+
url: z.string().optional(),
|
|
1694
|
+
/** 实例级 token,覆盖全局 token(缺省继承全局)。 */
|
|
1695
|
+
token: z.string().optional(),
|
|
1696
|
+
/** 断线重连延迟(毫秒)。 */
|
|
1697
|
+
reconnectDelayMs: z.number().int().default(DEFAULT_RECONNECT_DELAY_MS),
|
|
1698
|
+
/** 最大重连次数(缺省无限)。 */
|
|
1699
|
+
maxReconnectAttempts: z.number().int().optional(),
|
|
1700
|
+
/** wss:// 是否校验证书(自签证书场景设 false,对齐 NapCat enableSelfSigned)。 */
|
|
1701
|
+
rejectUnauthorized: z.boolean().default(true),
|
|
1702
|
+
/** WS ping 间隔(毫秒)。 */
|
|
1703
|
+
heartbeatInterval: z.number().int().default(DEFAULT_WS_HEARTBEAT_MS)
|
|
1704
|
+
});
|
|
1705
|
+
/** OB11 配置 schema。 */
|
|
1706
|
+
const ob11ConfigSchema = z.object({
|
|
1707
|
+
/** 鉴权 token(全局默认,实例未指定时继承)。 */
|
|
1708
|
+
token: z.string().optional(),
|
|
1709
|
+
/** 心跳 meta 事件间隔(毫秒),0 关闭心跳。 */
|
|
1710
|
+
heartbeatInterval: z.number().int().default(DEFAULT_HEARTBEAT_INTERVAL_MS),
|
|
1711
|
+
/** 是否上报机器人自己发的消息(缺省 false = OB11 规范行为)。 */
|
|
1712
|
+
reportSelfMessage: z.boolean().default(false),
|
|
1713
|
+
/** 消息内容格式:array = 消息段数组(标准),string = CQ 码字符串。 */
|
|
1714
|
+
messagePostFormat: z.enum(MESSAGE_FORMATS).default("array"),
|
|
1715
|
+
/** HTTP 反向服务器列表。 */
|
|
1716
|
+
httpServers: z.array(httpServerSchema).default([]),
|
|
1717
|
+
/** HTTP 正向上报列表。 */
|
|
1718
|
+
httpPostUrls: z.array(httpPostSchema).default([]),
|
|
1719
|
+
/** 反向 WS server 列表。 */
|
|
1720
|
+
wsServers: z.array(wsServerSchema).default([]),
|
|
1721
|
+
/** 正向 WS client 列表。 */
|
|
1722
|
+
wsReverseUrls: z.array(wsReverseSchema).default([])
|
|
1723
|
+
});
|
|
1724
|
+
//#endregion
|
|
1725
|
+
//#region src/onebot11/helper/data.ts
|
|
1703
1726
|
/** canonical 元素数组 → OB11 segment 数组(无法表达的元素静默跳过)。 */
|
|
1704
1727
|
function canonicalToSegments(elements) {
|
|
1705
1728
|
const segments = [];
|
|
@@ -1718,13 +1741,6 @@ function segmentsToCanonical(segments) {
|
|
|
1718
1741
|
}
|
|
1719
1742
|
return elements;
|
|
1720
1743
|
}
|
|
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
1744
|
/**
|
|
1729
1745
|
* 收集 canonical 元素里需要转换的 at uid / reply NT msgId(接收方向)。
|
|
1730
1746
|
* 调用方据此批量 uidToUin(一次异步调用),避免逐元素查询。
|
|
@@ -1878,23 +1894,6 @@ const MS_TO_SEC$2 = 1e3;
|
|
|
1878
1894
|
function hasGrayTip(msg) {
|
|
1879
1895
|
return msg.elements.some((el) => el.grayTipElement !== void 0);
|
|
1880
1896
|
}
|
|
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
1897
|
/** uid → uin(上下文映射缺省退化为 uid)。 */
|
|
1899
1898
|
function toUin(uid, ctx) {
|
|
1900
1899
|
const uin = ctx.uidToUin.get(uid);
|
|
@@ -1925,59 +1924,79 @@ function toRecall(msg, g, ctx) {
|
|
|
1925
1924
|
function toGroupChange(msg, g, ctx) {
|
|
1926
1925
|
const grp = g?.groupElement;
|
|
1927
1926
|
if (grp === void 0 || grp.type === void 0) return null;
|
|
1928
|
-
const
|
|
1929
|
-
|
|
1930
|
-
|
|
1931
|
-
|
|
1932
|
-
|
|
1927
|
+
const common = {
|
|
1928
|
+
time: Math.floor(Number(msg.msgTime) / MS_TO_SEC$2),
|
|
1929
|
+
selfId: Number(ctx.selfUin),
|
|
1930
|
+
groupId: Number(msg.peerUid),
|
|
1931
|
+
memberUid: grp.memberUid ?? "",
|
|
1932
|
+
adminUid: grp.adminUid ?? ""
|
|
1933
|
+
};
|
|
1933
1934
|
switch (grp.type) {
|
|
1934
|
-
case TipGroupElementType.MEMBER_ADD: return
|
|
1935
|
-
|
|
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
|
-
};
|
|
1935
|
+
case TipGroupElementType.MEMBER_ADD: return toMemberAdd(common, ctx);
|
|
1936
|
+
case TipGroupElementType.QUIT: return toQuit(common, ctx);
|
|
1954
1937
|
case TipGroupElementType.BLOCK:
|
|
1955
|
-
case TipGroupElementType.UNBLOCK: return
|
|
1956
|
-
|
|
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
|
-
}
|
|
1938
|
+
case TipGroupElementType.UNBLOCK: return toAdminChange(common, grp, ctx);
|
|
1939
|
+
case TipGroupElementType.SHUT_UP: return toBan(common, grp, ctx);
|
|
1978
1940
|
default: return null;
|
|
1979
1941
|
}
|
|
1980
1942
|
}
|
|
1943
|
+
/** 群成员增加(approve=管理员操作 / invite=普通邀请)。 */
|
|
1944
|
+
function toMemberAdd(common, ctx) {
|
|
1945
|
+
const { time, selfId, groupId, memberUid, adminUid } = common;
|
|
1946
|
+
return {
|
|
1947
|
+
time,
|
|
1948
|
+
self_id: selfId,
|
|
1949
|
+
post_type: "notice",
|
|
1950
|
+
notice_type: "group_increase",
|
|
1951
|
+
sub_type: adminUid === memberUid ? "approve" : "invite",
|
|
1952
|
+
group_id: groupId,
|
|
1953
|
+
operator_id: toUin(adminUid, ctx),
|
|
1954
|
+
user_id: toUin(memberUid, ctx)
|
|
1955
|
+
};
|
|
1956
|
+
}
|
|
1957
|
+
/** 群成员退出(leave)。 */
|
|
1958
|
+
function toQuit(common, ctx) {
|
|
1959
|
+
const { time, selfId, groupId, memberUid, adminUid } = common;
|
|
1960
|
+
return {
|
|
1961
|
+
time,
|
|
1962
|
+
self_id: selfId,
|
|
1963
|
+
post_type: "notice",
|
|
1964
|
+
notice_type: "group_decrease",
|
|
1965
|
+
sub_type: "leave",
|
|
1966
|
+
group_id: groupId,
|
|
1967
|
+
operator_id: toUin(adminUid, ctx),
|
|
1968
|
+
user_id: toUin(memberUid, ctx)
|
|
1969
|
+
};
|
|
1970
|
+
}
|
|
1971
|
+
/** 管理员变更(BLOCK=set / UNBLOCK=unset)。 */
|
|
1972
|
+
function toAdminChange(common, grp, ctx) {
|
|
1973
|
+
const { time, selfId, groupId, memberUid, adminUid } = common;
|
|
1974
|
+
return {
|
|
1975
|
+
time,
|
|
1976
|
+
self_id: selfId,
|
|
1977
|
+
post_type: "notice",
|
|
1978
|
+
notice_type: "group_admin",
|
|
1979
|
+
sub_type: grp.type === TipGroupElementType.BLOCK ? "set" : "unset",
|
|
1980
|
+
group_id: groupId,
|
|
1981
|
+
user_id: toUin(memberUid === "" ? adminUid : memberUid, ctx)
|
|
1982
|
+
};
|
|
1983
|
+
}
|
|
1984
|
+
/** 禁言(duration>0=ban / 否则 lift_ban)。 */
|
|
1985
|
+
function toBan(common, grp, ctx) {
|
|
1986
|
+
const { time, selfId, groupId, adminUid, memberUid } = common;
|
|
1987
|
+
const shutUp = grp.shutUp;
|
|
1988
|
+
return {
|
|
1989
|
+
time,
|
|
1990
|
+
self_id: selfId,
|
|
1991
|
+
post_type: "notice",
|
|
1992
|
+
notice_type: "group_ban",
|
|
1993
|
+
sub_type: Number(shutUp?.duration ?? 0) > 0 ? "ban" : "lift_ban",
|
|
1994
|
+
group_id: groupId,
|
|
1995
|
+
operator_id: toUin(shutUp?.admin?.uid ?? adminUid, ctx),
|
|
1996
|
+
user_id: toUin(shutUp?.member?.uid ?? memberUid, ctx),
|
|
1997
|
+
duration: Number(shutUp?.duration ?? 0)
|
|
1998
|
+
};
|
|
1999
|
+
}
|
|
1981
2000
|
/** RawMessage → OB11 notice 事件(无 grayTip 返回 null)。 */
|
|
1982
2001
|
function toOb11NoticeEvent(msg, ctx) {
|
|
1983
2002
|
for (const el of msg.elements) {
|
|
@@ -2024,6 +2043,48 @@ var GetForwardMsgAction = class extends BaseAction {
|
|
|
2024
2043
|
return { messages: (await this.deps.msgApi.fetchForwardMessage(peer, msgId)).map((msg) => toOb11MessageInfo(msg, this.deps.messageUnique)) };
|
|
2025
2044
|
}
|
|
2026
2045
|
};
|
|
2046
|
+
/** 历史消息公共参数(message_seq / count / 反向排序)。 */
|
|
2047
|
+
const msgHistoryParamsSchema = {
|
|
2048
|
+
/** 起始消息序号(message_seq,缺省从最新拉)。 */
|
|
2049
|
+
message_seq: z.union([z.number(), z.string()]).optional(),
|
|
2050
|
+
count: z.number().default(20),
|
|
2051
|
+
/** 是否反向排序(NapCat 扩展)。 */
|
|
2052
|
+
reverse_order: z.boolean().optional(),
|
|
2053
|
+
reverseOrder: z.boolean().optional()
|
|
2054
|
+
};
|
|
2055
|
+
/** 解析 message_seq → 起始 msgId(int32 经 MessageUnique 反查,否则原样透传)。 */
|
|
2056
|
+
function resolveStartMsgId(messageSeq, messageUnique) {
|
|
2057
|
+
if (messageSeq === void 0) return;
|
|
2058
|
+
const raw = String(messageSeq);
|
|
2059
|
+
const shortId = Number(raw);
|
|
2060
|
+
if (Number.isFinite(shortId)) return messageUnique.getMsgId(shortId) ?? raw;
|
|
2061
|
+
return raw;
|
|
2062
|
+
}
|
|
2063
|
+
/**
|
|
2064
|
+
* 拉取历史消息并翻译为 OB11 消息信息数组。
|
|
2065
|
+
* 收集全部消息 at uid → 一次批量 uidToUin → 上下文注入(P2-19)。
|
|
2066
|
+
*/
|
|
2067
|
+
async function fetchAndTranslateHistory(peer, messageSeq, count, deps) {
|
|
2068
|
+
const startMsgId = resolveStartMsgId(messageSeq, deps.messageUnique);
|
|
2069
|
+
const opts = { count };
|
|
2070
|
+
if (startMsgId !== void 0) opts.msgId = startMsgId;
|
|
2071
|
+
const msgs = await deps.msgApi.fetchMessages(peer, opts);
|
|
2072
|
+
if (msgs.length === 0) throw kernelError(`消息 ${messageSeq ?? "0"} 不存在`, "NOT_FOUND");
|
|
2073
|
+
const atUids = /* @__PURE__ */ new Set();
|
|
2074
|
+
for (const m of msgs) {
|
|
2075
|
+
const { atUids: uids } = collectReceiveNeeds(toCanonicalElements(m));
|
|
2076
|
+
for (const u of uids) atUids.add(u);
|
|
2077
|
+
}
|
|
2078
|
+
let uidToUin;
|
|
2079
|
+
if (atUids.size > 0) try {
|
|
2080
|
+
uidToUin = await deps.uidToUin([...atUids]);
|
|
2081
|
+
} catch {}
|
|
2082
|
+
const ctx = {
|
|
2083
|
+
...uidToUin !== void 0 ? { uidToUin } : {},
|
|
2084
|
+
msgIdToOb11Id: (m) => deps.messageUnique.getMessageId(m)
|
|
2085
|
+
};
|
|
2086
|
+
return { messages: msgs.map((msg) => toOb11MessageInfo(msg, deps.messageUnique, ctx)) };
|
|
2087
|
+
}
|
|
2027
2088
|
//#endregion
|
|
2028
2089
|
//#region src/onebot11/action/message/get-friend-msg-history.ts
|
|
2029
2090
|
/**
|
|
@@ -2033,10 +2094,7 @@ var GetForwardMsgAction = class extends BaseAction {
|
|
|
2033
2094
|
*/
|
|
2034
2095
|
const getFriendMsgHistorySchema = z.object({
|
|
2035
2096
|
user_id: z.number(),
|
|
2036
|
-
|
|
2037
|
-
count: z.number().default(20),
|
|
2038
|
-
reverse_order: z.boolean().optional(),
|
|
2039
|
-
reverseOrder: z.boolean().optional()
|
|
2097
|
+
...msgHistoryParamsSchema
|
|
2040
2098
|
});
|
|
2041
2099
|
/** 获取私聊历史消息(P2-10 接 kernel fetchMessages)。 */
|
|
2042
2100
|
var GetFriendMsgHistoryAction = class extends BaseAction {
|
|
@@ -2049,37 +2107,11 @@ var GetFriendMsgHistoryAction = class extends BaseAction {
|
|
|
2049
2107
|
this.deps = deps;
|
|
2050
2108
|
}
|
|
2051
2109
|
async _handle(payload) {
|
|
2052
|
-
const uid =
|
|
2053
|
-
|
|
2054
|
-
const peer = {
|
|
2110
|
+
const uid = await resolveUid(String(payload.user_id), this.deps.uinToUid);
|
|
2111
|
+
return await fetchAndTranslateHistory({
|
|
2055
2112
|
chatType: ChatType.C2C,
|
|
2056
2113
|
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)) };
|
|
2114
|
+
}, payload.message_seq, payload.count, this.deps);
|
|
2083
2115
|
}
|
|
2084
2116
|
};
|
|
2085
2117
|
//#endregion
|
|
@@ -2092,12 +2124,7 @@ var GetFriendMsgHistoryAction = class extends BaseAction {
|
|
|
2092
2124
|
*/
|
|
2093
2125
|
const getGroupMsgHistorySchema = z.object({
|
|
2094
2126
|
group_id: z.number(),
|
|
2095
|
-
|
|
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()
|
|
2127
|
+
...msgHistoryParamsSchema
|
|
2101
2128
|
});
|
|
2102
2129
|
/** 获取群历史消息(P2-10 接 kernel fetchMessages)。 */
|
|
2103
2130
|
var GetGroupMsgHistoryAction = class extends BaseAction {
|
|
@@ -2110,35 +2137,10 @@ var GetGroupMsgHistoryAction = class extends BaseAction {
|
|
|
2110
2137
|
this.deps = deps;
|
|
2111
2138
|
}
|
|
2112
2139
|
async _handle(payload) {
|
|
2113
|
-
|
|
2140
|
+
return await fetchAndTranslateHistory({
|
|
2114
2141
|
chatType: ChatType.GROUP,
|
|
2115
2142
|
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)) };
|
|
2143
|
+
}, payload.message_seq, payload.count, this.deps);
|
|
2142
2144
|
}
|
|
2143
2145
|
};
|
|
2144
2146
|
//#endregion
|
|
@@ -2154,19 +2156,38 @@ const getMediaSchema = z.object({
|
|
|
2154
2156
|
file: z.string().optional(),
|
|
2155
2157
|
message_id: z.union([z.number(), z.string()]).optional()
|
|
2156
2158
|
});
|
|
2159
|
+
/**
|
|
2160
|
+
* 反查消息并提取媒体元素路径。
|
|
2161
|
+
* 返回 null = 消息不存在;extract 返回 undefined = 无匹配元素。
|
|
2162
|
+
*/
|
|
2163
|
+
async function resolveMedia(id, label, deps, extract) {
|
|
2164
|
+
const { msgId, peer } = resolveMsgIdAndPeer(id, deps.messageUnique);
|
|
2165
|
+
const [first] = await deps.msgApi.fetchMsgsByMsgId(peer, [msgId]);
|
|
2166
|
+
if (first === void 0) throw kernelError(`消息 ${id} 不存在或已被撤回`, "NOT_FOUND");
|
|
2167
|
+
for (const el of first.elements) {
|
|
2168
|
+
const found = extract(el);
|
|
2169
|
+
if (found !== void 0) return found;
|
|
2170
|
+
}
|
|
2171
|
+
throw kernelError(`消息 ${id} 不包含${label}`, "NOT_FOUND");
|
|
2172
|
+
}
|
|
2157
2173
|
/** 从消息元素提取媒体路径与 URL(PIC/PTT,纯函数)。 */
|
|
2158
2174
|
function extractMediaPath(element) {
|
|
2159
2175
|
const pic = element.picElement;
|
|
2160
2176
|
if (pic !== void 0) {
|
|
2161
2177
|
const out = {};
|
|
2162
2178
|
const path = pic.sourcePath ?? pic.picPath;
|
|
2163
|
-
if (path !== void 0) out.
|
|
2179
|
+
if (path !== void 0) out.file = path;
|
|
2164
2180
|
if (pic.picUrl !== void 0) out.url = pic.picUrl;
|
|
2181
|
+
if (Object.keys(out).length === 0) return;
|
|
2165
2182
|
return out;
|
|
2166
2183
|
}
|
|
2167
2184
|
const ptt = element.pttElement;
|
|
2168
|
-
if (ptt !== void 0 && ptt.filePath !== void 0) return {
|
|
2169
|
-
|
|
2185
|
+
if (ptt !== void 0 && ptt.filePath !== void 0) return { file: ptt.filePath };
|
|
2186
|
+
}
|
|
2187
|
+
/** 提取语音元素(get_record 用)。 */
|
|
2188
|
+
function extractPttPath(element) {
|
|
2189
|
+
const ptt = element.pttElement;
|
|
2190
|
+
if (ptt !== void 0 && ptt.filePath !== void 0) return { file: ptt.filePath };
|
|
2170
2191
|
}
|
|
2171
2192
|
/** 获取图片信息(P2-14)。 */
|
|
2172
2193
|
var GetImageAction = class extends BaseAction {
|
|
@@ -2179,28 +2200,12 @@ var GetImageAction = class extends BaseAction {
|
|
|
2179
2200
|
this.deps = deps;
|
|
2180
2201
|
}
|
|
2181
2202
|
_handle(payload) {
|
|
2182
|
-
return this.
|
|
2203
|
+
return this.resolve(payload, "图片", "get_image");
|
|
2183
2204
|
}
|
|
2184
|
-
|
|
2185
|
-
async resolveMedia(payload, label, actionName) {
|
|
2205
|
+
resolve(payload, label, actionName) {
|
|
2186
2206
|
const id = payload.message_id ?? payload.file;
|
|
2187
2207
|
if (id === void 0) throw kernelError(`${actionName} 需要 message_id 或 file`, "INVALID_PARAM");
|
|
2188
|
-
|
|
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;
|
|
2208
|
+
return resolveMedia(id, label, this.deps, extractMediaPath);
|
|
2204
2209
|
}
|
|
2205
2210
|
};
|
|
2206
2211
|
/** 获取语音信息(P2-14)。 */
|
|
@@ -2216,14 +2221,7 @@ var GetRecordAction = class extends BaseAction {
|
|
|
2216
2221
|
async _handle(payload) {
|
|
2217
2222
|
const id = payload.message_id ?? payload.file;
|
|
2218
2223
|
if (id === void 0) throw kernelError("get_record 需要 message_id 或 file", "INVALID_PARAM");
|
|
2219
|
-
|
|
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");
|
|
2224
|
+
return await resolveMedia(id, "语音", this.deps, extractPttPath);
|
|
2227
2225
|
}
|
|
2228
2226
|
};
|
|
2229
2227
|
//#endregion
|
|
@@ -2245,9 +2243,7 @@ var GetMsgAction = class extends BaseAction {
|
|
|
2245
2243
|
this.deps = deps;
|
|
2246
2244
|
}
|
|
2247
2245
|
async _handle(payload) {
|
|
2248
|
-
const {
|
|
2249
|
-
const [msg] = await this.deps.msgApi.fetchMsgsByMsgId(peer, [msgId]);
|
|
2250
|
-
if (msg === void 0) throw kernelError(`消息 ${payload.message_id} 不存在或已被撤回`, "NOT_FOUND");
|
|
2246
|
+
const { msg } = await fetchMsgById(payload.message_id, this.deps.messageUnique, this.deps.msgApi);
|
|
2251
2247
|
const { atUids } = collectReceiveNeeds(toCanonicalElements(msg));
|
|
2252
2248
|
let uidToUin;
|
|
2253
2249
|
if (atUids.length > 0) try {
|
|
@@ -2292,8 +2288,7 @@ var MarkMsgAsReadAction = class extends BaseAction {
|
|
|
2292
2288
|
return null;
|
|
2293
2289
|
}
|
|
2294
2290
|
if (payload.user_id !== void 0) {
|
|
2295
|
-
const uid =
|
|
2296
|
-
if (uid === void 0) throw kernelError(`用户 ${payload.user_id} 的 uid 解析失败`, "INVALID_PARAM");
|
|
2291
|
+
const uid = await resolveUid(String(payload.user_id), this.deps.uinToUid);
|
|
2297
2292
|
await this.deps.msgApi.markRead({
|
|
2298
2293
|
chatType: ChatType.C2C,
|
|
2299
2294
|
peerUid: uid
|
|
@@ -2324,8 +2319,7 @@ var MarkPrivateMsgAsReadAction = class extends BaseAction {
|
|
|
2324
2319
|
this.deps = deps;
|
|
2325
2320
|
}
|
|
2326
2321
|
async _handle(payload) {
|
|
2327
|
-
const uid =
|
|
2328
|
-
if (uid === void 0) throw kernelError(`用户 ${payload.user_id} 的 uid 解析失败`, "INVALID_PARAM");
|
|
2322
|
+
const uid = await resolveUid(String(payload.user_id), this.deps.uinToUid);
|
|
2329
2323
|
await this.deps.msgApi.markRead({
|
|
2330
2324
|
chatType: ChatType.C2C,
|
|
2331
2325
|
peerUid: uid
|
|
@@ -2393,6 +2387,14 @@ function resolveSourceMessages(messages, unique) {
|
|
|
2393
2387
|
}
|
|
2394
2388
|
return out;
|
|
2395
2389
|
}
|
|
2390
|
+
/** 合并转发发送(群/私聊共用:解析源消息 → 发送 → 映射 message_id)。 */
|
|
2391
|
+
async function sendForward(messages, target, deps) {
|
|
2392
|
+
const sources = resolveSourceMessages(messages, deps.messageUnique);
|
|
2393
|
+
const [firstSource] = sources;
|
|
2394
|
+
if (firstSource === void 0) throw kernelError("合并转发需要至少一条可解析的源消息(node.id)", "INVALID_PARAM");
|
|
2395
|
+
const { msgId } = await deps.msgApi.sendForwardMessage(target, firstSource.peer, sources.map((s) => s.msgId));
|
|
2396
|
+
return { message_id: deps.messageUnique.alloc(msgId, target) };
|
|
2397
|
+
}
|
|
2396
2398
|
/** 发送群合并转发(P2-12 接 kernel sendForwardMessage)。 */
|
|
2397
2399
|
var SendGroupForwardMsgAction = class extends BaseAction {
|
|
2398
2400
|
name = "send_group_forward_msg";
|
|
@@ -2404,15 +2406,11 @@ var SendGroupForwardMsgAction = class extends BaseAction {
|
|
|
2404
2406
|
this.deps = deps;
|
|
2405
2407
|
}
|
|
2406
2408
|
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
2409
|
const target = {
|
|
2411
2410
|
chatType: ChatType.GROUP,
|
|
2412
2411
|
peerUid: String(payload.group_id)
|
|
2413
2412
|
};
|
|
2414
|
-
|
|
2415
|
-
return { message_id: this.deps.messageUnique.alloc(msgId, target) };
|
|
2413
|
+
return await sendForward(payload.messages, target, this.deps);
|
|
2416
2414
|
}
|
|
2417
2415
|
};
|
|
2418
2416
|
/** 发送私聊合并转发(P2-12)。 */
|
|
@@ -2426,31 +2424,16 @@ var SendPrivateForwardMsgAction = class extends BaseAction {
|
|
|
2426
2424
|
this.deps = deps;
|
|
2427
2425
|
}
|
|
2428
2426
|
async _handle(payload) {
|
|
2429
|
-
const
|
|
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");
|
|
2427
|
+
const uid = await resolveUid(String(payload.user_id), this.deps.uinToUid);
|
|
2434
2428
|
const target = {
|
|
2435
2429
|
chatType: ChatType.C2C,
|
|
2436
2430
|
peerUid: uid
|
|
2437
2431
|
};
|
|
2438
|
-
|
|
2439
|
-
return { message_id: this.deps.messageUnique.alloc(msgId, target) };
|
|
2432
|
+
return await sendForward(payload.messages, target, this.deps);
|
|
2440
2433
|
}
|
|
2441
2434
|
};
|
|
2442
2435
|
//#endregion
|
|
2443
2436
|
//#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
2437
|
const sendMsgSchema = z.object({
|
|
2455
2438
|
message_type: z.enum(["group", "private"]).optional(),
|
|
2456
2439
|
user_id: z.number().optional(),
|
|
@@ -2458,25 +2441,9 @@ const sendMsgSchema = z.object({
|
|
|
2458
2441
|
message: z.union([z.string(), z.array(z.unknown())]),
|
|
2459
2442
|
auto_escape: z.boolean().optional()
|
|
2460
2443
|
});
|
|
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
2444
|
/** 共享发送核心:解析 Peer → canonical 翻译(含 auto_escape)→ sendMessage → 映射 message_id。 */
|
|
2478
2445
|
async function sendOb11Message(payload, deps) {
|
|
2479
|
-
const peer = await
|
|
2446
|
+
const peer = await resolveTargetPeer(payload, deps, "send_msg 需要 group_id 或 user_id");
|
|
2480
2447
|
let canonical;
|
|
2481
2448
|
if (Array.isArray(payload.message)) canonical = segmentsToCanonical(payload.message);
|
|
2482
2449
|
else canonical = cqMessageToCanonical(payload.message);
|
|
@@ -2592,8 +2559,7 @@ var SetInputStatusAction = class extends BaseAction {
|
|
|
2592
2559
|
this.deps = deps;
|
|
2593
2560
|
}
|
|
2594
2561
|
async _handle(payload) {
|
|
2595
|
-
const uid =
|
|
2596
|
-
if (uid === void 0) throw kernelError(`用户 ${payload.user_id} 的 uid 解析失败`, "INVALID_PARAM");
|
|
2562
|
+
const uid = await resolveUid(String(payload.user_id), this.deps.uinToUid);
|
|
2597
2563
|
await this.deps.msgApi.setInputStatus({
|
|
2598
2564
|
chatType: ChatType.C2C,
|
|
2599
2565
|
peerUid: uid
|
|
@@ -2628,9 +2594,7 @@ var SetMsgEmojiLikeAction = class extends BaseAction {
|
|
|
2628
2594
|
this.deps = deps;
|
|
2629
2595
|
}
|
|
2630
2596
|
async _handle(payload) {
|
|
2631
|
-
const {
|
|
2632
|
-
const [msg] = await this.deps.msgApi.fetchMsgsByMsgId(peer, [msgId]);
|
|
2633
|
-
if (msg === void 0) throw kernelError(`消息 ${payload.message_id} 不存在或已被撤回`, "NOT_FOUND");
|
|
2597
|
+
const { msg, peer } = await fetchMsgById(payload.message_id, this.deps.messageUnique, this.deps.msgApi);
|
|
2634
2598
|
await this.deps.msgApi.setMsgEmojiLike(peer, {
|
|
2635
2599
|
msgSeq: msg.msgSeq,
|
|
2636
2600
|
emojiId: String(payload.emoji_id),
|
|
@@ -3131,6 +3095,9 @@ var TranslateEn2ZhAction = class extends BaseAction {
|
|
|
3131
3095
|
* OneBot 11 动作注册表(ADR-013 延伸)
|
|
3132
3096
|
* 各协议维护自己的 ActionRegistry,由协议 adapter 挂到请求分发。
|
|
3133
3097
|
* P2-16:deps 收敛为 { api: OneBotApi } 单聚合对象(基础设施第一项)。
|
|
3098
|
+
*
|
|
3099
|
+
* 同时是 action 域 barrel:re-export 全部动作类 + error-map + resolve-uid,
|
|
3100
|
+
* 协议外部只从本入口消费动作面。
|
|
3134
3101
|
*/
|
|
3135
3102
|
/** 构建 OB11 动作注册表(所有 OB11 动作在此注册,按组拆分控制行数)。 */
|
|
3136
3103
|
function createOb11ActionRegistry(deps) {
|
|
@@ -3424,15 +3391,27 @@ function makeAuthorize(token) {
|
|
|
3424
3391
|
return hasAccessTokenQuery(ctx.url, token);
|
|
3425
3392
|
};
|
|
3426
3393
|
}
|
|
3394
|
+
/**
|
|
3395
|
+
* OneBot 客户端标识头:koishi 的 onebot 适配器按
|
|
3396
|
+
* x-client-role === "Universal" 与 x-self-id 校验 WS 连接(NapCat 同款头),
|
|
3397
|
+
* 缺失会被 close(1008) 拒绝——连接虽握手成功但应用层不认。
|
|
3398
|
+
*/
|
|
3399
|
+
function oneBotClientHeaders(selfUin) {
|
|
3400
|
+
return {
|
|
3401
|
+
"X-Self-ID": selfUin,
|
|
3402
|
+
"x-client-role": "Universal",
|
|
3403
|
+
"User-Agent": "OneBot/11"
|
|
3404
|
+
};
|
|
3405
|
+
}
|
|
3427
3406
|
/** 按配置装配 OB11 传输。 */
|
|
3428
3407
|
function assembleOb11Transports(opts) {
|
|
3429
|
-
const { config, broadcaster, handleRequest } = opts;
|
|
3408
|
+
const { config, broadcaster, handleRequest, selfUin } = opts;
|
|
3430
3409
|
const servers = [];
|
|
3431
3410
|
const transports = [];
|
|
3432
3411
|
assembleHttpServers(config, handleRequest, servers);
|
|
3433
3412
|
assembleWsServers(config, handleRequest, servers);
|
|
3434
|
-
assembleHttpPosts(config, transports);
|
|
3435
|
-
assembleWsReverses(config, handleRequest, transports);
|
|
3413
|
+
assembleHttpPosts(config, selfUin, transports);
|
|
3414
|
+
assembleWsReverses(config, selfUin, handleRequest, transports);
|
|
3436
3415
|
for (const t of transports) broadcaster.register(t);
|
|
3437
3416
|
for (const s of servers) broadcaster.register(s);
|
|
3438
3417
|
return {
|
|
@@ -3474,19 +3453,27 @@ function assembleWsServers(config, handleRequest, servers) {
|
|
|
3474
3453
|
}
|
|
3475
3454
|
}
|
|
3476
3455
|
/** 装配 HTTP 正向上报 client 列表(fire-and-forget)。 */
|
|
3477
|
-
function assembleHttpPosts(config, transports) {
|
|
3456
|
+
function assembleHttpPosts(config, selfUin, transports) {
|
|
3478
3457
|
for (const item of config.httpPostUrls) if (item.enabled && item.url !== void 0) {
|
|
3479
|
-
const headers =
|
|
3480
|
-
|
|
3481
|
-
|
|
3458
|
+
const headers = {
|
|
3459
|
+
"x-self-id": selfUin,
|
|
3460
|
+
...authorizeHeader(item.token ?? config.token) ?? {}
|
|
3461
|
+
};
|
|
3462
|
+
const options = {
|
|
3463
|
+
url: item.url,
|
|
3464
|
+
headers
|
|
3465
|
+
};
|
|
3482
3466
|
if (item.timeoutMs !== void 0) options.timeoutMs = item.timeoutMs;
|
|
3483
3467
|
transports.push(new HttpClient(options));
|
|
3484
3468
|
}
|
|
3485
3469
|
}
|
|
3486
3470
|
/** 装配正向 WS client 列表(双向,心跳 + 重连 + 证书选项)。 */
|
|
3487
|
-
function assembleWsReverses(config, handleRequest, transports) {
|
|
3471
|
+
function assembleWsReverses(config, selfUin, handleRequest, transports) {
|
|
3488
3472
|
for (const item of config.wsReverseUrls) if (item.enabled && item.url !== void 0) {
|
|
3489
|
-
const headers =
|
|
3473
|
+
const headers = {
|
|
3474
|
+
...oneBotClientHeaders(selfUin),
|
|
3475
|
+
...authorizeHeader(item.token ?? config.token) ?? {}
|
|
3476
|
+
};
|
|
3490
3477
|
const reconnect = {
|
|
3491
3478
|
enabled: true,
|
|
3492
3479
|
delayMs: item.reconnectDelayMs
|
|
@@ -3494,11 +3481,11 @@ function assembleWsReverses(config, handleRequest, transports) {
|
|
|
3494
3481
|
if (item.maxReconnectAttempts !== void 0) reconnect.maxAttempts = item.maxReconnectAttempts;
|
|
3495
3482
|
const options = {
|
|
3496
3483
|
url: item.url,
|
|
3484
|
+
headers,
|
|
3497
3485
|
heartbeat: { intervalMs: item.heartbeatInterval },
|
|
3498
3486
|
reconnect,
|
|
3499
3487
|
rejectUnauthorized: item.rejectUnauthorized
|
|
3500
3488
|
};
|
|
3501
|
-
if (headers !== void 0) options.headers = headers;
|
|
3502
3489
|
const client = new WsClient(options);
|
|
3503
3490
|
client.onRequest = handleRequest;
|
|
3504
3491
|
transports.push(client);
|
|
@@ -3552,6 +3539,7 @@ var NapukettoOneBot11Adapter = class extends BaseProtocolAdapter {
|
|
|
3552
3539
|
this.transports = assembleOb11Transports({
|
|
3553
3540
|
config,
|
|
3554
3541
|
broadcaster,
|
|
3542
|
+
selfUin: this.selfUin,
|
|
3555
3543
|
handleRequest: (req, respond) => {
|
|
3556
3544
|
this.handleRequest(req, respond).catch((err) => {
|
|
3557
3545
|
let message = String(err);
|
|
@@ -3611,16 +3599,14 @@ var NapukettoOneBot11Adapter = class extends BaseProtocolAdapter {
|
|
|
3611
3599
|
subscribe() {
|
|
3612
3600
|
if (this.unsubscribe !== null) return;
|
|
3613
3601
|
this.unsubscribe = this.msgChannel.on("Msg/onRecvMsg", (msgs) => {
|
|
3614
|
-
|
|
3615
|
-
for (const msg of list) {
|
|
3616
|
-
if (!msg || typeof msg !== "object") continue;
|
|
3602
|
+
forEachRawMessage(msgs, (msg) => {
|
|
3617
3603
|
if (hasGrayTip(msg)) {
|
|
3618
3604
|
this.broadcastNotice(msg).catch(() => {});
|
|
3619
|
-
|
|
3605
|
+
return;
|
|
3620
3606
|
}
|
|
3621
|
-
if (!this.reportSelfMessage && String(msg.senderUin) === this.selfUin)
|
|
3607
|
+
if (!this.reportSelfMessage && String(msg.senderUin) === this.selfUin) return;
|
|
3622
3608
|
this.broadcastMessageEvent(msg);
|
|
3623
|
-
}
|
|
3609
|
+
});
|
|
3624
3610
|
});
|
|
3625
3611
|
}
|
|
3626
3612
|
/**
|