@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.
- 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 +8 -3
- package/dist/onebot11/index.d.mts.map +1 -1
- package/dist/onebot11/index.mjs +291 -323
- 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 +479 -0
- package/dist/satori/index.d.mts.map +1 -0
- package/dist/satori/index.mjs +1882 -0
- package/dist/satori/index.mjs.map +1 -0
- package/package.json +9 -5
- 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");
|
|
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 =
|
|
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 =
|
|
1428
|
-
|
|
1429
|
-
|
|
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/
|
|
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
|
|
1929
|
-
|
|
1930
|
-
|
|
1931
|
-
|
|
1932
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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 =
|
|
2053
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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.
|
|
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 {
|
|
2169
|
-
|
|
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.
|
|
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
|
-
|
|
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
|
-
|
|
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 {
|
|
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 =
|
|
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 =
|
|
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
|
-
|
|
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
|
|
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
|
-
|
|
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
|
|
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 =
|
|
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 {
|
|
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 =
|
|
3480
|
-
|
|
3481
|
-
|
|
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 =
|
|
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
|
-
|
|
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
|
-
|
|
3587
|
+
return;
|
|
3620
3588
|
}
|
|
3621
|
-
if (!this.reportSelfMessage && String(msg.senderUin) === this.selfUin)
|
|
3589
|
+
if (!this.reportSelfMessage && String(msg.senderUin) === this.selfUin) return;
|
|
3622
3590
|
this.broadcastMessageEvent(msg);
|
|
3623
|
-
}
|
|
3591
|
+
});
|
|
3624
3592
|
});
|
|
3625
3593
|
}
|
|
3626
3594
|
/**
|