@napuketto/loader 0.0.20 → 0.0.21
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/dist/host/self-host.cjs +77 -1
- package/package.json +1 -1
package/dist/host/self-host.cjs
CHANGED
|
@@ -271,6 +271,81 @@ async function toPeer(params, uinToUid) {
|
|
|
271
271
|
}
|
|
272
272
|
throw new Error("缺 peerUid(或 peerUin 且未注入 uinToUid)");
|
|
273
273
|
}
|
|
274
|
+
/** at 元素判断(宽松结构;排除 @全体——target="all" 走 atType=1,无需转换)。 */
|
|
275
|
+
function isLooseAtElement(value) {
|
|
276
|
+
if (typeof value !== "object" || value === null) return false;
|
|
277
|
+
const el = value;
|
|
278
|
+
return el["type"] === "at" && typeof el["target"] === "string" && el["target"] !== "all";
|
|
279
|
+
}
|
|
280
|
+
/** 收集元素中的 at(非 @全体)及其索引。 */
|
|
281
|
+
function collectAtItems(elements) {
|
|
282
|
+
const items = [];
|
|
283
|
+
for (let i = 0; i < elements.length; i++) {
|
|
284
|
+
const el = elements[i];
|
|
285
|
+
if (isLooseAtElement(el)) items.push({
|
|
286
|
+
index: i,
|
|
287
|
+
uin: el.target
|
|
288
|
+
});
|
|
289
|
+
}
|
|
290
|
+
return items;
|
|
291
|
+
}
|
|
292
|
+
/** 群成员昵称 → display(可选依赖 groupCache.getMember;无昵称返回 undefined)。 */
|
|
293
|
+
async function memberDisplay(ctx, groupCode, uid) {
|
|
294
|
+
if (ctx.groupCache.getMember === void 0) return;
|
|
295
|
+
const member = await ctx.groupCache.getMember(groupCode, uid);
|
|
296
|
+
if (member === void 0 || member.nick === void 0 || member.nick === "") return;
|
|
297
|
+
return member.nick;
|
|
298
|
+
}
|
|
299
|
+
/**
|
|
300
|
+
* 单个 at 元素归一化:target uin → uid(u_ 前缀直通)+ display 补全。
|
|
301
|
+
* 返回 null 表示目标未解析(调用方保留原元素,保底发送)。
|
|
302
|
+
*/
|
|
303
|
+
async function normalizeAtElement(ctx, groupCode, prev, uidMap) {
|
|
304
|
+
const uid = prev.target.startsWith("u_") ? prev.target : uidMap.get(prev.target) ?? "";
|
|
305
|
+
if (uid === "") return null;
|
|
306
|
+
const next = {
|
|
307
|
+
type: "at",
|
|
308
|
+
target: uid
|
|
309
|
+
};
|
|
310
|
+
if (prev.display !== void 0 && prev.display !== "") {
|
|
311
|
+
next.display = prev.display;
|
|
312
|
+
return next;
|
|
313
|
+
}
|
|
314
|
+
const nick = await memberDisplay(ctx, groupCode, uid);
|
|
315
|
+
if (nick !== void 0) next.display = nick;
|
|
316
|
+
return next;
|
|
317
|
+
}
|
|
318
|
+
/**
|
|
319
|
+
* 发送前 at 目标归一化(P2-19 同构:OB11 协议层 at.qq uin → uid 后才进 kernel)。
|
|
320
|
+
*
|
|
321
|
+
* 背景(koishi-plugin-adapter-napuketto issue #1「无法 @ 指定群成员」):
|
|
322
|
+
* koishi 调用方给的 at.target 是 QQ 号(uin——session.userId 即 senderUin),
|
|
323
|
+
* 而 NT 的 atUid 契约是 u_ 开头的 uid。不转则 QQ 解析 at 失败,只能回退显示
|
|
324
|
+
* content 兜底 "@" → 表现为「@号后面为空」。群聊才有 at 语义,私聊/临时会话跳过。
|
|
325
|
+
*
|
|
326
|
+
* 归一化:uin 批量 uinToUid → uid(u_ 前缀直通不转);有 groupCache.getMember
|
|
327
|
+
* 时补 display(群成员昵称,kernel toSendElements 用它作 TEXT content,
|
|
328
|
+
* 避免 @ 后无显示名)。转换失败/缺注入:保留原元素(不阻塞发送,保底)。
|
|
329
|
+
*/
|
|
330
|
+
async function resolveAtElements(chatType, peerUid, elements, ctx) {
|
|
331
|
+
if (chatType !== 2 || ctx.uinToUid === void 0) return elements;
|
|
332
|
+
const atItems = collectAtItems(elements);
|
|
333
|
+
if (atItems.length === 0) return elements;
|
|
334
|
+
const uins = atItems.filter((item) => !item.uin.startsWith("u_"));
|
|
335
|
+
let uidMap = /* @__PURE__ */ new Map();
|
|
336
|
+
if (uins.length > 0) try {
|
|
337
|
+
uidMap = await ctx.uinToUid(uins.map((item) => item.uin));
|
|
338
|
+
} catch {
|
|
339
|
+
uidMap = /* @__PURE__ */ new Map();
|
|
340
|
+
}
|
|
341
|
+
const out = [...elements];
|
|
342
|
+
for (const item of atItems) {
|
|
343
|
+
const prev = out[item.index];
|
|
344
|
+
const next = await normalizeAtElement(ctx, peerUid, prev, uidMap);
|
|
345
|
+
if (next !== null) out[item.index] = next;
|
|
346
|
+
}
|
|
347
|
+
return out;
|
|
348
|
+
}
|
|
274
349
|
/** 构造动作表(map 顺序即注册顺序,重复动作名后注册覆盖)。 */
|
|
275
350
|
function createIpcActions(ctx) {
|
|
276
351
|
const actions = /* @__PURE__ */ new Map();
|
|
@@ -278,7 +353,8 @@ function createIpcActions(ctx) {
|
|
|
278
353
|
actions.set("msg.sendMessage", async (params) => {
|
|
279
354
|
const peer = await toPeer(params, ctx.uinToUid);
|
|
280
355
|
const elements = Array.isArray(params["elements"]) ? params["elements"] : [];
|
|
281
|
-
|
|
356
|
+
const resolved = await resolveAtElements(peer.chatType, peer.peerUid, elements, ctx);
|
|
357
|
+
return ctx.msgApi.sendMessage(peer, resolved);
|
|
282
358
|
});
|
|
283
359
|
actions.set("msg.recallMessage", async (params) => {
|
|
284
360
|
const peer = await toPeer(params, ctx.uinToUid);
|