chatluna-plugin-onebot-toolbox 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/lib/config.d.ts +1 -4
- package/lib/config.d.ts.map +1 -1
- package/lib/index.js +54 -127
- package/lib/index.js.map +1 -1
- package/lib/onebot.d.ts +5 -48
- package/lib/onebot.d.ts.map +1 -1
- package/lib/tools.d.ts +2 -2
- package/lib/tools.d.ts.map +1 -1
- package/lib/variables.d.ts.map +1 -1
- package/package.json +1 -1
package/lib/config.d.ts
CHANGED
|
@@ -27,10 +27,7 @@ export interface Config {
|
|
|
27
27
|
};
|
|
28
28
|
debugLogging: boolean;
|
|
29
29
|
}
|
|
30
|
-
/**
|
|
31
|
-
* 原生工具的名称与描述,与源插件的默认值一致。插件不提供改名、改描述或开关工具的配置,
|
|
32
|
-
* 工具全部注册,启用范围交给 v2 的工具管理。
|
|
33
|
-
*/
|
|
30
|
+
/** 原生工具的名称与描述固定,全部注册;启用范围交给 v2 的工具管理。 */
|
|
34
31
|
export declare const NATIVE_TOOLS: Record<NativeToolKey, NativeToolInfo>;
|
|
35
32
|
export declare const NATIVE_TOOL_KEYS: NativeToolKey[];
|
|
36
33
|
export declare const DEFAULT_MEMBER_INFO_ITEMS: MemberInfoItem[];
|
package/lib/config.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"config.d.ts","sourceRoot":"","sources":["../src/config.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,MAAM,EAAE,MAAM,kBAAkB,CAAA;AAEzC,MAAM,MAAM,cAAc,GAAG,QAAQ,GAAG,OAAO,GAAG,UAAU,CAAA;AAE5D,MAAM,MAAM,aAAa,GACrB,gBAAgB,GAChB,aAAa,GACb,cAAc,GACd,mBAAmB,GACnB,YAAY,GACZ,eAAe,GACf,cAAc,GACd,sBAAsB,GACtB,eAAe,CAAA;AAEnB,MAAM,WAAW,cAAc;IAC7B,QAAQ,EAAE,MAAM,CAAA;IAChB,WAAW,EAAE,MAAM,CAAA;CACpB;AAED,MAAM,MAAM,cAAc,GACtB,QAAQ,GACR,UAAU,GACV,MAAM,GACN,OAAO,GACP,OAAO,GACP,QAAQ,GACR,KAAK,GACL,MAAM,GACN,UAAU,GACV,cAAc,CAAA;AAElB,MAAM,MAAM,aAAa,GAAG,WAAW,GAAG,SAAS,GAAG,aAAa,GAAG,YAAY,GAAG,WAAW,GAAG,WAAW,CAAA;AAE9G,MAAM,WAAW,MAAM;IACrB,cAAc,EAAE,cAAc,CAAA;IAC9B,aAAa,EAAE,OAAO,CAAA;IACtB,QAAQ,EAAE;QAAE,YAAY,EAAE,MAAM,CAAC;QAAC,KAAK,EAAE,cAAc,EAAE,CAAA;KAAE,CAAA;IAC3D,OAAO,EAAE;QAAE,YAAY,EAAE,MAAM,CAAC;QAAC,KAAK,EAAE,cAAc,EAAE,CAAA;KAAE,CAAA;IAC1D,SAAS,EAAE;QAAE,YAAY,EAAE,MAAM,CAAC;QAAC,KAAK,EAAE,aAAa,EAAE,CAAA;KAAE,CAAA;IAC3D,aAAa,EAAE;QAAE,YAAY,EAAE,MAAM,CAAA;KAAE,CAAA;IACvC,YAAY,EAAE,OAAO,CAAA;CACtB;AAED
|
|
1
|
+
{"version":3,"file":"config.d.ts","sourceRoot":"","sources":["../src/config.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,MAAM,EAAE,MAAM,kBAAkB,CAAA;AAEzC,MAAM,MAAM,cAAc,GAAG,QAAQ,GAAG,OAAO,GAAG,UAAU,CAAA;AAE5D,MAAM,MAAM,aAAa,GACrB,gBAAgB,GAChB,aAAa,GACb,cAAc,GACd,mBAAmB,GACnB,YAAY,GACZ,eAAe,GACf,cAAc,GACd,sBAAsB,GACtB,eAAe,CAAA;AAEnB,MAAM,WAAW,cAAc;IAC7B,QAAQ,EAAE,MAAM,CAAA;IAChB,WAAW,EAAE,MAAM,CAAA;CACpB;AAED,MAAM,MAAM,cAAc,GACtB,QAAQ,GACR,UAAU,GACV,MAAM,GACN,OAAO,GACP,OAAO,GACP,QAAQ,GACR,KAAK,GACL,MAAM,GACN,UAAU,GACV,cAAc,CAAA;AAElB,MAAM,MAAM,aAAa,GAAG,WAAW,GAAG,SAAS,GAAG,aAAa,GAAG,YAAY,GAAG,WAAW,GAAG,WAAW,CAAA;AAE9G,MAAM,WAAW,MAAM;IACrB,cAAc,EAAE,cAAc,CAAA;IAC9B,aAAa,EAAE,OAAO,CAAA;IACtB,QAAQ,EAAE;QAAE,YAAY,EAAE,MAAM,CAAC;QAAC,KAAK,EAAE,cAAc,EAAE,CAAA;KAAE,CAAA;IAC3D,OAAO,EAAE;QAAE,YAAY,EAAE,MAAM,CAAC;QAAC,KAAK,EAAE,cAAc,EAAE,CAAA;KAAE,CAAA;IAC1D,SAAS,EAAE;QAAE,YAAY,EAAE,MAAM,CAAC;QAAC,KAAK,EAAE,aAAa,EAAE,CAAA;KAAE,CAAA;IAC3D,aAAa,EAAE;QAAE,YAAY,EAAE,MAAM,CAAA;KAAE,CAAA;IACvC,YAAY,EAAE,OAAO,CAAA;CACtB;AAED,yCAAyC;AACzC,eAAO,MAAM,YAAY,EAAE,MAAM,CAAC,aAAa,EAAE,cAAc,CAqC9D,CAAA;AAED,eAAO,MAAM,gBAAgB,EAAgC,aAAa,EAAE,CAAA;AAE5E,eAAO,MAAM,yBAAyB,EAAE,cAAc,EAErD,CAAA;AAED,eAAO,MAAM,wBAAwB,EAAE,aAAa,EAAsE,CAAA;AA+D1H;;;GAGG;AACH,eAAO,MAAM,MAAM,EAAE,MAAM,CAAC,MAAM,CAIhC,CAAA"}
|
package/lib/index.js
CHANGED
|
@@ -10,10 +10,7 @@ function createLogger(ctx, debugLogging) {
|
|
|
10
10
|
};
|
|
11
11
|
}
|
|
12
12
|
// external/onebot-toolbox/src/config.ts
|
|
13
|
-
/**
|
|
14
|
-
* 原生工具的名称与描述,与源插件的默认值一致。插件不提供改名、改描述或开关工具的配置,
|
|
15
|
-
* 工具全部注册,启用范围交给 v2 的工具管理。
|
|
16
|
-
*/
|
|
13
|
+
/** 原生工具的名称与描述固定,全部注册;启用范围交给 v2 的工具管理。 */
|
|
17
14
|
const NATIVE_TOOLS = {
|
|
18
15
|
setSelfProfile: {
|
|
19
16
|
toolName: "set_self_profile",
|
|
@@ -188,24 +185,18 @@ function registerRequestNotices(ctx, config, log) {
|
|
|
188
185
|
}
|
|
189
186
|
// external/onebot-toolbox/src/onebot.ts
|
|
190
187
|
const isRecord = (value) => typeof value === "object" && value !== null && !Array.isArray(value);
|
|
191
|
-
/** 会话所在的 OneBot 机器人;不是 OneBot
|
|
188
|
+
/** 会话所在的 OneBot 机器人;不是 OneBot 会话或找不到机器人时为 undefined。 */
|
|
192
189
|
function oneBotOf(ctx, platform, selfId) {
|
|
193
190
|
if (platform !== "onebot" || selfId === void 0) return void 0;
|
|
194
191
|
return ctx.satori.bots.find((bot) => bot.platform === "onebot" && bot.selfId === selfId);
|
|
195
192
|
}
|
|
196
|
-
/**
|
|
197
|
-
* 调用 OneBot action 并返回 `data`。OneBot 的业务错误以正常 resolve 的响应包返回,这里按 status 与 retcode
|
|
198
|
-
* 判定失败并抛出,错误信息取实现给出的 wording/message。
|
|
199
|
-
*/
|
|
193
|
+
/** 调用 OneBot action 并返回 data,按响应包的 status 与 retcode 判定业务失败。 */
|
|
200
194
|
async function callOneBot(bot, action, params) {
|
|
201
195
|
const result = await bot.internal._request(action, params);
|
|
202
|
-
if (!isRecord(result))
|
|
203
|
-
|
|
204
|
-
const
|
|
205
|
-
|
|
206
|
-
const reason = envelope.wording || envelope.message || envelope.status;
|
|
207
|
-
const retcode = typeof envelope.retcode === "number" ? ` (retcode: ${envelope.retcode})` : "";
|
|
208
|
-
throw new Error(`${reason ? String(reason) : "OneBot returned failed"}${retcode}`);
|
|
196
|
+
if (!isRecord(result) || typeof result.status !== "string" || typeof result.retcode !== "number") throw new Error("Invalid OneBot response envelope");
|
|
197
|
+
if (result.status === "ok" && result.retcode === 0) return result.data;
|
|
198
|
+
const reason = result.wording || result.message || result.status || "OneBot returned failed";
|
|
199
|
+
throw new Error(`${String(reason)} (retcode: ${result.retcode})`);
|
|
209
200
|
}
|
|
210
201
|
/**
|
|
211
202
|
* 整数形式的 ID 转成数字,其余原样保留;OneBot 实现普遍要求数字形式的群号、QQ 号与消息 ID。
|
|
@@ -225,7 +216,7 @@ function pickFirst(...values) {
|
|
|
225
216
|
function pickText(...values) {
|
|
226
217
|
return String(pickFirst(...values) ?? "").trim();
|
|
227
218
|
}
|
|
228
|
-
/** OneBot
|
|
219
|
+
/** OneBot 使用秒,Satori 使用毫秒,统一成毫秒;空值与 0 视为没有。 */
|
|
229
220
|
function normalizeTimestamp(raw) {
|
|
230
221
|
if (raw === null || raw === void 0 || raw === "") return void 0;
|
|
231
222
|
const numeric = Number(raw);
|
|
@@ -248,22 +239,9 @@ function formatDateTime(value) {
|
|
|
248
239
|
function asMembers(value) {
|
|
249
240
|
return Array.isArray(value) ? value.filter(isRecord) : [];
|
|
250
241
|
}
|
|
251
|
-
function
|
|
242
|
+
function asRecord(value) {
|
|
252
243
|
return isRecord(value) ? value : void 0;
|
|
253
244
|
}
|
|
254
|
-
function memberUserId(member) {
|
|
255
|
-
return pickText(member.user_id, member.userId, member.id, member.qq, member.uid);
|
|
256
|
-
}
|
|
257
|
-
function memberCard(member) {
|
|
258
|
-
return pickText(member.card, member.remark, member.displayName);
|
|
259
|
-
}
|
|
260
|
-
function memberNickname(member) {
|
|
261
|
-
return pickText(member.nick, member.nickname, member.name, member.user?.nickname, member.user?.name);
|
|
262
|
-
}
|
|
263
|
-
function memberRole(member) {
|
|
264
|
-
const roles = Array.isArray(member.roles) ? member.roles : [];
|
|
265
|
-
return pickText(member.role, member.roleName, member.permission, member.identity, roles[0]);
|
|
266
|
-
}
|
|
267
245
|
// external/onebot-toolbox/src/tools.ts
|
|
268
246
|
const MISSING_GROUP = "Missing groupId. Provide groupId explicitly or run inside a group session.";
|
|
269
247
|
const MAX_MEMBER_RESULTS = 10;
|
|
@@ -340,7 +318,7 @@ async function avatarFile(scope, image) {
|
|
|
340
318
|
return image;
|
|
341
319
|
}
|
|
342
320
|
function formatMember(index, member) {
|
|
343
|
-
return `${index}. qqid:${
|
|
321
|
+
return `${index}. qqid:${pickText(member.user_id) || "未知"},群昵称:${pickText(member.card) || "无"},QQ昵称:${pickText(member.nickname) || "无"}`;
|
|
344
322
|
}
|
|
345
323
|
const NATIVE_TOOL_IMPLEMENTATIONS = {
|
|
346
324
|
setSelfProfile: {
|
|
@@ -409,7 +387,7 @@ const NATIVE_TOOL_IMPLEMENTATIONS = {
|
|
|
409
387
|
const members = asMembers(await callOneBot(scope.bot, "get_group_member_list", { group_id: numericId(groupId) }));
|
|
410
388
|
const byQQ = mode === "byQQ" || mode === "auto" && /^\d+$/.test(query);
|
|
411
389
|
const keyword = query.toLowerCase();
|
|
412
|
-
const matches = members.filter((member) => byQQ ?
|
|
390
|
+
const matches = members.filter((member) => byQQ ? pickText(member.user_id) === query : [pickText(member.card), pickText(member.nickname)].some((name) => name && name.toLowerCase().includes(keyword)));
|
|
413
391
|
if (!matches.length) return "未找到匹配的群成员。";
|
|
414
392
|
return [matches.length > MAX_MEMBER_RESULTS ? `仅显示前 ${MAX_MEMBER_RESULTS} 条,共 ${matches.length} 条匹配:` : `找到 ${matches.length} 个匹配成员:`, ...matches.slice(0, MAX_MEMBER_RESULTS).map((member, index) => formatMember(index + 1, member))].join("\n");
|
|
415
393
|
}
|
|
@@ -509,7 +487,7 @@ const NATIVE_TOOL_IMPLEMENTATIONS = {
|
|
|
509
487
|
}
|
|
510
488
|
}
|
|
511
489
|
};
|
|
512
|
-
/**
|
|
490
|
+
/** 全部原生工具的定义,名称与描述统一取自 NATIVE_TOOLS。 */
|
|
513
491
|
function nativeToolDefinitions(ctx, config, log) {
|
|
514
492
|
return NATIVE_TOOL_KEYS.map((key) => {
|
|
515
493
|
const tool = NATIVE_TOOL_IMPLEMENTATIONS[key];
|
|
@@ -544,7 +522,7 @@ function nativeToolDefinitions(ctx, config, log) {
|
|
|
544
522
|
}
|
|
545
523
|
/**
|
|
546
524
|
* 注册原生工具。工具对所有会话全局可见(v2 的工具管理页因此能列出并开关它们),
|
|
547
|
-
* 再给不在 OneBot 上的 agent
|
|
525
|
+
* 再给不在 OneBot 上的 agent 挂一层排除过滤。
|
|
548
526
|
*/
|
|
549
527
|
function registerNativeTools(ctx, config, log) {
|
|
550
528
|
const definitions = nativeToolDefinitions(ctx, config, log);
|
|
@@ -564,51 +542,14 @@ const FALLBACK_MEMBER_ITEMS = ["nickname", "userId"];
|
|
|
564
542
|
const SHUT_LIST_TIMEOUT_MS = 200;
|
|
565
543
|
const SHUT_LIST_CACHE_TTL_MS = 15e3;
|
|
566
544
|
function translateGender(value) {
|
|
567
|
-
if (value ===
|
|
568
|
-
|
|
569
|
-
|
|
570
|
-
if ([
|
|
571
|
-
"male",
|
|
572
|
-
"man",
|
|
573
|
-
"m",
|
|
574
|
-
"1",
|
|
575
|
-
"boy"
|
|
576
|
-
].includes(text)) return "男";
|
|
577
|
-
if ([
|
|
578
|
-
"female",
|
|
579
|
-
"woman",
|
|
580
|
-
"f",
|
|
581
|
-
"2",
|
|
582
|
-
"girl"
|
|
583
|
-
].includes(text)) return "女";
|
|
584
|
-
if ([
|
|
585
|
-
"未知",
|
|
586
|
-
"unknown",
|
|
587
|
-
"0",
|
|
588
|
-
"secret"
|
|
589
|
-
].includes(text)) return "";
|
|
590
|
-
return String(value);
|
|
545
|
+
if (value === "male") return "男";
|
|
546
|
+
if (value === "female") return "女";
|
|
547
|
+
return "";
|
|
591
548
|
}
|
|
592
549
|
function normalizeRole(value) {
|
|
593
|
-
|
|
594
|
-
if (
|
|
595
|
-
|
|
596
|
-
"owner",
|
|
597
|
-
"group_owner",
|
|
598
|
-
"群主"
|
|
599
|
-
].includes(text)) return "群主";
|
|
600
|
-
if ([
|
|
601
|
-
"admin",
|
|
602
|
-
"administrator",
|
|
603
|
-
"group_admin",
|
|
604
|
-
"管理员"
|
|
605
|
-
].includes(text)) return "管理员";
|
|
606
|
-
if ([
|
|
607
|
-
"member",
|
|
608
|
-
"normal",
|
|
609
|
-
"群员"
|
|
610
|
-
].includes(text)) return "群员";
|
|
611
|
-
return value;
|
|
550
|
+
if (value === "owner") return "群主";
|
|
551
|
+
if (value === "admin") return "管理员";
|
|
552
|
+
return !value || value === "member" ? "群员" : value;
|
|
612
553
|
}
|
|
613
554
|
/** 会话所在的 Satori 机器人,任意平台;预览等没有真实机器人的环境里为 undefined。 */
|
|
614
555
|
function botOf(ctx, env) {
|
|
@@ -619,12 +560,20 @@ async function fetchMember(ctx, env, userId) {
|
|
|
619
560
|
const bot = botOf(ctx, env);
|
|
620
561
|
if (!bot || !env.guildId) return void 0;
|
|
621
562
|
try {
|
|
622
|
-
if (bot.platform === "onebot") return
|
|
563
|
+
if (bot.platform === "onebot") return asRecord(await callOneBot(bot, "get_group_member_info", {
|
|
623
564
|
group_id: Number(env.guildId),
|
|
624
565
|
user_id: Number(userId),
|
|
625
566
|
no_cache: false
|
|
626
567
|
}));
|
|
627
|
-
|
|
568
|
+
const member = await bot.getGuildMember(env.guildId, userId);
|
|
569
|
+
return {
|
|
570
|
+
user_id: member.user?.id,
|
|
571
|
+
card: pickText(member.nick, member.name),
|
|
572
|
+
nickname: member.user?.name,
|
|
573
|
+
role: member.roles?.[0]?.id,
|
|
574
|
+
title: member.title,
|
|
575
|
+
join_time: member.joinedAt
|
|
576
|
+
};
|
|
628
577
|
} catch {
|
|
629
578
|
return;
|
|
630
579
|
}
|
|
@@ -634,7 +583,7 @@ function renderMember(member, items, id, fallbackName) {
|
|
|
634
583
|
const values = [];
|
|
635
584
|
for (const item of items) switch (item) {
|
|
636
585
|
case "nickname": {
|
|
637
|
-
const name = pickText(member?.card, member?.
|
|
586
|
+
const name = pickText(member?.card, member?.nickname, fallbackName, id);
|
|
638
587
|
if (name) values.push(`name:${name}`);
|
|
639
588
|
break;
|
|
640
589
|
}
|
|
@@ -642,20 +591,20 @@ function renderMember(member, items, id, fallbackName) {
|
|
|
642
591
|
if (id) values.push(`id:${id}`);
|
|
643
592
|
break;
|
|
644
593
|
case "role":
|
|
645
|
-
values.push(`群内身份:${normalizeRole(
|
|
594
|
+
values.push(`群内身份:${normalizeRole(pickText(member?.role))}`);
|
|
646
595
|
break;
|
|
647
596
|
case "level": {
|
|
648
|
-
const level = pickFirst(member?.level
|
|
597
|
+
const level = pickFirst(member?.level);
|
|
649
598
|
if (level !== void 0) values.push(`群等级:${level}`);
|
|
650
599
|
break;
|
|
651
600
|
}
|
|
652
601
|
case "title": {
|
|
653
|
-
const title = pickFirst(member?.title
|
|
602
|
+
const title = pickFirst(member?.title);
|
|
654
603
|
if (title !== void 0) values.push(`头衔:${title}`);
|
|
655
604
|
break;
|
|
656
605
|
}
|
|
657
606
|
case "gender": {
|
|
658
|
-
const gender = translateGender(member?.sex
|
|
607
|
+
const gender = translateGender(member?.sex);
|
|
659
608
|
if (gender) values.push(`性别:${gender}`);
|
|
660
609
|
break;
|
|
661
610
|
}
|
|
@@ -665,17 +614,17 @@ function renderMember(member, items, id, fallbackName) {
|
|
|
665
614
|
break;
|
|
666
615
|
}
|
|
667
616
|
case "area": {
|
|
668
|
-
const area = pickFirst(member?.area
|
|
617
|
+
const area = pickFirst(member?.area);
|
|
669
618
|
if (area !== void 0) values.push(`地区:${area}`);
|
|
670
619
|
break;
|
|
671
620
|
}
|
|
672
621
|
case "joinTime": {
|
|
673
|
-
const joinTime = formatDate(normalizeTimestamp(
|
|
622
|
+
const joinTime = formatDate(normalizeTimestamp(member?.join_time));
|
|
674
623
|
if (joinTime) values.push(`入群:${joinTime}`);
|
|
675
624
|
break;
|
|
676
625
|
}
|
|
677
626
|
case "lastSentTime": {
|
|
678
|
-
const lastSentTime = formatDateTime(normalizeTimestamp(
|
|
627
|
+
const lastSentTime = formatDateTime(normalizeTimestamp(member?.last_sent_time));
|
|
679
628
|
if (lastSentTime) values.push(`活跃:${lastSentTime}`);
|
|
680
629
|
break;
|
|
681
630
|
}
|
|
@@ -706,20 +655,12 @@ async function fetchRoles(bot, guildId, log) {
|
|
|
706
655
|
const owners = [];
|
|
707
656
|
const admins = [];
|
|
708
657
|
for (const member of members) {
|
|
709
|
-
const role =
|
|
710
|
-
const userId =
|
|
711
|
-
const name = pickText(
|
|
658
|
+
const role = member.role;
|
|
659
|
+
const userId = pickText(member.user_id);
|
|
660
|
+
const name = pickText(member.card, member.nickname, userId);
|
|
712
661
|
const label = userId ? `${name}(${userId})` : name;
|
|
713
|
-
if (
|
|
714
|
-
|
|
715
|
-
"master",
|
|
716
|
-
"leader"
|
|
717
|
-
].includes(role)) owners.push(label);
|
|
718
|
-
else if ([
|
|
719
|
-
"admin",
|
|
720
|
-
"administrator",
|
|
721
|
-
"manager"
|
|
722
|
-
].includes(role)) admins.push(label);
|
|
662
|
+
if (role === "owner") owners.push(label);
|
|
663
|
+
else if (role === "admin") admins.push(label);
|
|
723
664
|
}
|
|
724
665
|
return {
|
|
725
666
|
owners,
|
|
@@ -731,10 +672,10 @@ async function fetchRoles(bot, guildId, log) {
|
|
|
731
672
|
}
|
|
732
673
|
}
|
|
733
674
|
function renderGroupInfo(group, items, roles) {
|
|
734
|
-
const id = pickText(group.group_id
|
|
735
|
-
const name = pickText(group.group_name
|
|
736
|
-
const memberCount = group.member_count
|
|
737
|
-
const createTime = formatDate(normalizeTimestamp(group.create_time ?? group.
|
|
675
|
+
const id = pickText(group.group_id);
|
|
676
|
+
const name = pickText(group.group_name);
|
|
677
|
+
const memberCount = group.member_count;
|
|
678
|
+
const createTime = formatDate(normalizeTimestamp(group.create_time ?? group.group_create_time));
|
|
738
679
|
const values = [];
|
|
739
680
|
for (const item of items) switch (item) {
|
|
740
681
|
case "groupName":
|
|
@@ -766,42 +707,28 @@ function groupInfoProvider(ctx, config, log) {
|
|
|
766
707
|
const group = await callOneBot(bot, "get_group_info", {
|
|
767
708
|
group_id: Number(env.guildId),
|
|
768
709
|
no_cache: false
|
|
769
|
-
}).then(
|
|
710
|
+
}).then(asRecord, () => void 0);
|
|
770
711
|
if (!group) return "未能获取当前群信息。";
|
|
771
712
|
return renderGroupInfo(group, items, items.includes("ownerList") || items.includes("adminList") ? await fetchRoles(bot, env.guildId, log) : void 0);
|
|
772
713
|
};
|
|
773
714
|
}
|
|
774
715
|
var ShutListTimeout = class extends Error {};
|
|
775
|
-
/** 群号类型不被接受时的报错特征:NapCat 要数字、LLBot 要字符串,类型不对时换另一种再试一次。 */
|
|
776
|
-
const WRONG_ID_TYPE = /string|number|type|invalid|参数/i;
|
|
777
716
|
function withTimeout(promise, ms) {
|
|
778
717
|
let timer;
|
|
779
718
|
return Promise.race([promise, new Promise((_, reject) => {
|
|
780
719
|
timer = setTimeout(() => reject(new ShutListTimeout("group shut list request timed out")), ms);
|
|
781
720
|
})]).finally(() => clearTimeout(timer));
|
|
782
721
|
}
|
|
783
|
-
async function fetchShutList(bot, guildId, protocol
|
|
784
|
-
const
|
|
785
|
-
|
|
786
|
-
const request = async (groupId) => {
|
|
787
|
-
const data = await withTimeout(callOneBot(bot, "get_group_shut_list", { group_id: groupId }), SHUT_LIST_TIMEOUT_MS);
|
|
788
|
-
return Array.isArray(data) ? data : [];
|
|
789
|
-
};
|
|
790
|
-
try {
|
|
791
|
-
return await request(preferred);
|
|
792
|
-
} catch (error) {
|
|
793
|
-
if (error instanceof ShutListTimeout || !(error instanceof Error) || !WRONG_ID_TYPE.test(error.message)) throw error;
|
|
794
|
-
log.debug("group shut list retries with %s group id: %s", typeof fallback, error.message);
|
|
795
|
-
return request(fallback);
|
|
796
|
-
}
|
|
722
|
+
async function fetchShutList(bot, guildId, protocol) {
|
|
723
|
+
const data = await withTimeout(callOneBot(bot, "get_group_shut_list", { group_id: protocol === "llbot" ? guildId : Number(guildId) }), SHUT_LIST_TIMEOUT_MS);
|
|
724
|
+
return Array.isArray(data) ? data : [];
|
|
797
725
|
}
|
|
798
726
|
function renderShutItem(item) {
|
|
799
|
-
const record =
|
|
727
|
+
const record = asRecord(item);
|
|
800
728
|
if (!record) return void 0;
|
|
801
|
-
const
|
|
802
|
-
const userId = pickText(field("uin"), field("user_id"));
|
|
729
|
+
const userId = pickText(record.uin, record.user_id);
|
|
803
730
|
if (!userId) return void 0;
|
|
804
|
-
return `id:${userId},name:${pickText(
|
|
731
|
+
return `id:${userId},name:${pickText(record.cardName, record.remark, record.nick, record.nickname, userId)},禁言截至:${formatDateTime(normalizeTimestamp(pickFirst(record.shutUpTime, record.shut_up_time))) || "未知"}`;
|
|
805
732
|
}
|
|
806
733
|
function groupShutListProvider(ctx, config, log) {
|
|
807
734
|
const cache = /* @__PURE__ */ new Map();
|
|
@@ -814,7 +741,7 @@ function groupShutListProvider(ctx, config, log) {
|
|
|
814
741
|
const cached = cache.get(key);
|
|
815
742
|
if (cached && cached.expiresAt > Date.now()) return cached.value;
|
|
816
743
|
try {
|
|
817
|
-
const lines = (await fetchShutList(bot, env.guildId, config.oneBotProtocol
|
|
744
|
+
const lines = (await fetchShutList(bot, env.guildId, config.oneBotProtocol)).flatMap((item) => renderShutItem(item) ?? []);
|
|
818
745
|
const value = lines.length ? lines.join("\n") : "当前群暂无禁言成员。";
|
|
819
746
|
cache.set(key, {
|
|
820
747
|
value,
|
package/lib/index.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.js","names":[],"sources":["../src/logger.ts","../src/config.ts","../src/requests.ts","../src/onebot.ts","../src/tools.ts","../src/variables.ts","../src/index.ts"],"sourcesContent":["import type { Context } from 'cordis'\n\n/** 插件内的日志:`debugLogging` 打开时调试信息以 info 级别输出,否则走 logger 自己的 debug 级别。 */\nexport interface Logger {\n debug(format: string, ...args: unknown[]): void\n info(format: string, ...args: unknown[]): void\n warn(format: string, ...args: unknown[]): void\n}\n\nexport function createLogger(ctx: Context, debugLogging: boolean): Logger {\n const logger = ctx.logger\n return {\n debug: (format, ...args) => (debugLogging ? logger.info(format, ...args) : logger.debug(format, ...args)),\n info: (format, ...args) => logger.info(format, ...args),\n warn: (format, ...args) => logger.warn(format, ...args),\n }\n}\n","import { Schema } from '@chatluna/schema'\n\nexport type OneBotProtocol = 'napcat' | 'llbot' | 'snowluma'\n\nexport type NativeToolKey =\n | 'setSelfProfile'\n | 'setQQAvatar'\n | 'setGroupCard'\n | 'searchGroupMember'\n | 'leaveGroup'\n | 'handleRequest'\n | 'setGroupKick'\n | 'setGroupSpecialTitle'\n | 'deleteMessage'\n\nexport interface NativeToolInfo {\n toolName: string\n description: string\n}\n\nexport type MemberInfoItem =\n | 'userId'\n | 'nickname'\n | 'role'\n | 'level'\n | 'title'\n | 'gender'\n | 'age'\n | 'area'\n | 'joinTime'\n | 'lastSentTime'\n\nexport type GroupInfoItem = 'groupName' | 'groupId' | 'memberCount' | 'createTime' | 'ownerList' | 'adminList'\n\nexport interface Config {\n oneBotProtocol: OneBotProtocol\n requestNotice: boolean\n userInfo: { variableName: string, items: MemberInfoItem[] }\n botInfo: { variableName: string, items: MemberInfoItem[] }\n groupInfo: { variableName: string, items: GroupInfoItem[] }\n groupShutList: { variableName: string }\n debugLogging: boolean\n}\n\n/**\n * 原生工具的名称与描述,与源插件的默认值一致。插件不提供改名、改描述或开关工具的配置,\n * 工具全部注册,启用范围交给 v2 的工具管理。\n */\nexport const NATIVE_TOOLS: Record<NativeToolKey, NativeToolInfo> = {\n setSelfProfile: {\n toolName: 'set_self_profile',\n description: '修改自己的 QQ 资料,可设置昵称、签名和性别。',\n },\n setQQAvatar: {\n toolName: 'set_qq_avatar',\n description: '修改机器人 QQ 头像。imageUrl 填会话里图片的 ref(`<img ref=\"i…\"/>` 的 ref 属性)、HTTP/HTTPS 链接或 OneBot 端可读的本地路径;会话图片与 HTTP/HTTPS 图片会先由插件读取并转为 OneBot base64 文件,避免临时 URL 过期或 OneBot 端无法访问。',\n },\n setGroupCard: {\n toolName: 'set_group_card',\n description: '修改群成员的群昵称。',\n },\n searchGroupMember: {\n toolName: 'search_group_member',\n description: '根据 qqid 精确查询群成员的群昵称与 QQ 昵称,或根据群昵称/QQ 昵称搜索 qqid。默认查询当前群聊,可通过 groupId 查询指定群。',\n },\n leaveGroup: {\n toolName: 'set_group_leave',\n description: '让机器人退出当前群聊。',\n },\n handleRequest: {\n toolName: 'handle_request',\n description: '根据 OneBot 申请 flag 同意或拒绝好友申请、加群申请或群邀请。',\n },\n setGroupKick: {\n toolName: 'set_group_kick',\n description: '将指定成员踢出群聊。',\n },\n setGroupSpecialTitle: {\n toolName: 'set_group_special_title',\n description: '修改或清除群成员的 QQ 群专属头衔。',\n },\n deleteMessage: {\n toolName: 'delete_msg',\n description: '根据消息 ID 撤回指定消息。',\n },\n}\n\nexport const NATIVE_TOOL_KEYS = Object.keys(NATIVE_TOOLS) as NativeToolKey[]\n\nexport const DEFAULT_MEMBER_INFO_ITEMS: MemberInfoItem[] = [\n 'nickname', 'userId', 'role', 'level', 'title', 'gender', 'age', 'area', 'joinTime', 'lastSentTime',\n]\n\nexport const DEFAULT_GROUP_INFO_ITEMS: GroupInfoItem[] = ['groupName', 'groupId', 'memberCount', 'ownerList', 'adminList']\n\n/** 变量名须满足 v2 模板的变量命名规则(`@chatluna/prompt` 的 `variable`),留空表示不注册。 */\nconst VARIABLE_NAME = /^([a-z_][a-z0-9_]*)?$/i\n\nconst memberItems = (subject: string) => Schema.array(Schema.union([\n Schema.const('userId').description(`${subject} ID`),\n Schema.const('nickname').description('群昵称'),\n Schema.const('role').description('群内身份'),\n Schema.const('level').description('群等级'),\n Schema.const('title').description('群头衔'),\n Schema.const('gender').description('性别'),\n Schema.const('age').description('年龄'),\n Schema.const('area').description('地区'),\n Schema.const('joinTime').description('入群时间'),\n Schema.const('lastSentTime').description('最后发言时间'),\n])).role('checkbox').default([...DEFAULT_MEMBER_INFO_ITEMS])\n\nconst variableName = (name: string) => Schema.string().default(name).pattern(VARIABLE_NAME).extra('label', '变量名称')\n .description('在预设中以`{变量名称}`引用,留空时不注册')\n\nconst basicFields = {\n oneBotProtocol: Schema.union([\n Schema.const('napcat').description('NapCat'),\n Schema.const('llbot').description('LLBot'),\n Schema.const('snowluma').description('SnowLuma'),\n ]).default('napcat').role('radio').extra('label', 'OneBot 协议')\n .description('按所选实现调整请求参数:LLBot 不支持设置性别,群禁言列表的群号类型也不同;SnowLuma 同意好友申请后另行设置备注'),\n requestNotice: Schema.boolean().default(true).extra('label', '申请通知')\n .description('收到好友申请、加群申请或群邀请时唤醒对应会话,把申请 flag 告诉模型,供`handle_request`工具处理。好友申请与群邀请发往申请人的私聊,加群申请发往所申请的群;该工具在工具管理中禁用时不唤醒'),\n}\n\nconst variableFields = {\n userInfo: Schema.object({\n variableName: variableName('userInfo'),\n items: memberItems('用户').extra('label', '信息项').description('选择用户信息变量中显示的详细信息项'),\n }).description('用户信息变量').collapse(),\n botInfo: Schema.object({\n variableName: variableName('botInfo'),\n items: memberItems('机器人').extra('label', '信息项').description('选择机器人信息变量中显示的详细信息项'),\n }).description('机器人信息变量').collapse(),\n groupInfo: Schema.object({\n variableName: variableName('groupInfo'),\n items: Schema.array(Schema.union([\n Schema.const('groupName').description('群名称'),\n Schema.const('groupId').description('群号'),\n Schema.const('memberCount').description('成员数量'),\n Schema.const('createTime').description('创建时间(仅部分实现支持)'),\n Schema.const('ownerList').description('群主名单'),\n Schema.const('adminList').description('管理员名单'),\n ])).role('checkbox').default([...DEFAULT_GROUP_INFO_ITEMS]).extra('label', '信息项')\n .description('选择群信息变量中显示的群信息项'),\n }).description('群信息变量').collapse(),\n groupShutList: Schema.object({\n variableName: variableName('groupShutList'),\n }).description('群禁言列表变量').collapse(),\n}\n\nconst otherFields = {\n debugLogging: Schema.boolean().default(false).extra('label', '输出调试日志')\n .description('以 info 级别输出工具调用与变量解析的调试信息'),\n}\n\n/**\n * 插件配置,直接在 v2 的插件设置页编辑。顶层是分组的 intersect,SchemaForm 把每组的 description 渲染成小节标题;\n * 每个字段都写 `extra('label')`,缺 label 时 SchemaForm 会把配置键当标题显示。\n */\nexport const Config: Schema<Config> = Schema.intersect([\n Schema.object(basicFields).description('基础设置'),\n Schema.object(variableFields).description('变量'),\n Schema.object(otherFields).description('其他设置'),\n])\n","import type { Context } from 'cordis'\nimport type { Session } from '@satorijs/core'\nimport type { RouteIdentity } from '@chatluna/satori'\nimport type {} from '@chatluna/chat'\nimport type { Config } from './config'\nimport { NATIVE_TOOLS } from './config'\nimport type { Logger } from './logger'\n\ntype RequestKind = 'friend' | 'add' | 'invite'\n\n/**\n * 申请发往的会话:好友申请与群邀请发往申请人的私聊,加群申请发往所申请的群。\n * 用路由身份而不是事件 Session 唤醒:申请事件不是消息,申请人与机器人之间往往还没有会话,\n * `chat.wake` 会按身份新开一个。\n */\nexport function requestTarget(session: Session, kind: RequestKind): RouteIdentity | undefined {\n if (!session.userId) return undefined\n if (kind === 'add') {\n if (!session.guildId) return undefined\n return { platform: session.platform, selfId: session.selfId, isDirect: false, guildId: session.guildId, channelId: session.guildId, userId: session.userId }\n }\n return { platform: session.platform, selfId: session.selfId, isDirect: true, channelId: `private:${session.userId}`, userId: session.userId }\n}\n\n/** 唤醒原因即模型看到的通知,写明 flag 与调用方式,模型据此调用处理申请工具。 */\nexport function requestReason(session: Session, kind: RequestKind, toolName: string): string {\n const comment = session.content?.trim() ? `, message: ${JSON.stringify(session.content.trim())}` : ''\n const flag = `flag: ${JSON.stringify(session.messageId ?? '')}`\n if (kind === 'friend') {\n return `user ${session.userId} sent you a friend request (${flag}${comment}). Call ${toolName} with requestType \"friend\" and userId \"${session.userId}\" to approve or reject it`\n }\n if (kind === 'add') {\n return `user ${session.userId} requested to join group ${session.guildId} (${flag}${comment}). Call ${toolName} with requestType \"group\" and subType \"add\" to approve or reject it`\n }\n return `user ${session.userId} invited you to join group ${session.guildId} (${flag}${comment}). Call ${toolName} with requestType \"group\" and subType \"invite\" to approve or reject it`\n}\n\n/**\n * v2 不把 OneBot 的申请事件交给模型,`handle_request` 拿不到 flag 就无从调用,\n * 所以把申请转成一次唤醒;工具在 v2 工具管理里被禁用时跟着不唤醒。会话所在路由关闭或没选模型时 `wake` 会报错,只记调试日志。\n */\nexport function registerRequestNotices(ctx: Context, config: Config, log: Logger) {\n if (!config.requestNotice) return\n const toolName = NATIVE_TOOLS.handleRequest.toolName\n ctx.inject(['chat'], (inner) => {\n const notify = (session: Session, kind: RequestKind) => {\n if (session.platform !== 'onebot') return\n // 没有 flag 时模型无从处理申请,唤醒只会让它拿空值反复试错。\n if (!session.messageId) return log.debug('%s request without flag ignored', kind)\n // 工具在 v2 工具管理里被禁用时(全局过滤排除了它)不唤醒:模型调用不到,唤醒只会让它白跑一轮。\n if (!inner.tools.list().some((tool) => tool.name === toolName)) return log.debug('%s request ignored: %s is disabled', kind, toolName)\n const target = requestTarget(session, kind)\n if (!target) return\n inner.chat.wake(target, { reason: requestReason(session, kind, toolName), by: 'onebot-toolbox' })\n .then(() => log.debug('%s request %s delivered', kind, session.messageId))\n .catch((error: unknown) => log.debug('%s request %s not delivered: %s', kind, session.messageId, String(error)))\n }\n inner.on('friend-request', (session) => notify(session, 'friend'))\n inner.on('guild-member-request', (session) => notify(session, 'add'))\n inner.on('guild-request', (session) => notify(session, 'invite'))\n })\n}\n","import type { Context } from 'cordis'\nimport type { Bot } from '@satorijs/core'\n\n/** Koishi OneBot 适配器的原始请求:返回完整的响应包,业务失败也照常 resolve。 */\nexport interface OneBotInternal {\n _request(action: string, params: Record<string, unknown>): Promise<unknown>\n}\n\ninterface OneBotEnvelope {\n status?: unknown\n retcode?: unknown\n wording?: unknown\n message?: unknown\n data?: unknown\n}\n\nconst isRecord = (value: unknown): value is Record<string, unknown> =>\n typeof value === 'object' && value !== null && !Array.isArray(value)\n\n/** 会话所在的 OneBot 机器人;不是 OneBot 会话或机器人已离线时为 undefined。 */\nexport function oneBotOf(ctx: Context, platform: string | undefined, selfId: string | undefined): Bot | undefined {\n if (platform !== 'onebot' || selfId === undefined) return undefined\n return ctx.satori.bots.find((bot) => bot.platform === 'onebot' && bot.selfId === selfId)\n}\n\n/**\n * 调用 OneBot action 并返回 `data`。OneBot 的业务错误以正常 resolve 的响应包返回,这里按 status 与 retcode\n * 判定失败并抛出,错误信息取实现给出的 wording/message。\n */\nexport async function callOneBot(bot: Bot, action: string, params: Record<string, unknown>): Promise<unknown> {\n const internal: OneBotInternal = bot.internal\n const result: unknown = await internal._request(action, params)\n if (!isRecord(result)) return result\n const envelope: OneBotEnvelope = result\n const status = String(envelope.status ?? '').toLowerCase()\n const failed = (status !== '' && status !== 'ok') || (typeof envelope.retcode === 'number' && envelope.retcode !== 0)\n if (!failed) return envelope.data\n const reason = envelope.wording || envelope.message || envelope.status\n const retcode = typeof envelope.retcode === 'number' ? ` (retcode: ${envelope.retcode})` : ''\n throw new Error(`${reason ? String(reason) : 'OneBot returned failed'}${retcode}`)\n}\n\n/**\n * 整数形式的 ID 转成数字,其余原样保留;OneBot 实现普遍要求数字形式的群号、QQ 号与消息 ID。\n * SnowLuma 的 message_id 是 int32 哈希,常为负数,所以带负号的整数同样转换。\n */\nexport function numericId(value: string): string | number {\n return /^-?\\d+$/.test(value) ? Number(value) : value\n}\n\n/** 取第一个非空值(去掉首尾空白后不为空字符串),保留原始类型。 */\nexport function pickFirst(...values: unknown[]): string | number | undefined {\n for (const value of values) {\n if (value === null || value === undefined) continue\n if (!String(value).trim()) continue\n return typeof value === 'number' ? value : String(value)\n }\n return undefined\n}\n\nexport function pickText(...values: unknown[]): string {\n return String(pickFirst(...values) ?? '').trim()\n}\n\n/** OneBot 时间戳可能是秒或毫秒,统一成毫秒;空值与 0 视为没有。 */\nexport function normalizeTimestamp(raw: unknown): number | undefined {\n if (raw === null || raw === undefined || raw === '') return undefined\n const numeric = Number(raw)\n if (!Number.isFinite(numeric) || numeric === 0) return undefined\n return numeric < 1e11 ? numeric * 1000 : numeric\n}\n\nconst pad = (value: number) => String(value).padStart(2, '0')\n\nexport function formatDate(value: number | undefined): string {\n if (value === undefined) return ''\n const date = new Date(value)\n if (Number.isNaN(date.valueOf())) return ''\n return `${date.getFullYear()}-${pad(date.getMonth() + 1)}-${pad(date.getDate())}`\n}\n\nexport function formatDateTime(value: number | undefined): string {\n const day = formatDate(value)\n if (!day || value === undefined) return day\n const date = new Date(value)\n return `${day} ${pad(date.getHours())}:${pad(date.getMinutes())}`\n}\n\n/** 群成员信息的常见字段;各 OneBot 实现与 Satori 的字段名不同,读取时逐个尝试。 */\nexport interface MemberInfo {\n user_id?: unknown\n userId?: unknown\n id?: unknown\n qq?: unknown\n uid?: unknown\n role?: unknown\n roleName?: unknown\n permission?: unknown\n identity?: unknown\n roles?: unknown\n card?: unknown\n remark?: unknown\n displayName?: unknown\n nick?: unknown\n nickname?: unknown\n name?: unknown\n level?: unknown\n levelName?: unknown\n level_name?: unknown\n level_info?: { current_level?: unknown }\n title?: unknown\n specialTitle?: unknown\n special_title?: unknown\n sex?: unknown\n gender?: unknown\n age?: unknown\n area?: unknown\n region?: unknown\n location?: unknown\n join_time?: unknown\n joined_at?: unknown\n joinTime?: unknown\n joinedAt?: unknown\n last_sent_time?: unknown\n lastSentTime?: unknown\n lastSpeakTimestamp?: unknown\n user?: { nick?: unknown, nickname?: unknown, name?: unknown }\n}\n\nexport function asMembers(value: unknown): MemberInfo[] {\n return Array.isArray(value) ? value.filter(isRecord) : []\n}\n\nexport function asMember(value: unknown): MemberInfo | undefined {\n return isRecord(value) ? value : undefined\n}\n\nexport function memberUserId(member: MemberInfo): string {\n return pickText(member.user_id, member.userId, member.id, member.qq, member.uid)\n}\n\nexport function memberCard(member: MemberInfo): string {\n return pickText(member.card, member.remark, member.displayName)\n}\n\nexport function memberNickname(member: MemberInfo): string {\n return pickText(member.nick, member.nickname, member.name, member.user?.nickname, member.user?.name)\n}\n\nexport function memberRole(member: MemberInfo): string {\n const roles = Array.isArray(member.roles) ? member.roles : []\n return pickText(member.role, member.roleName, member.permission, member.identity, roles[0])\n}\n","import type { Context } from 'cordis'\nimport type { Bot } from '@satorijs/core'\nimport { Schema } from '@chatluna/schema'\nimport type { ToolCallContext, ToolDefinition } from '@chatluna/tool'\nimport type { Agent } from '@chatluna/agent'\nimport type { ChatFileBlock, ChatMessage } from '@chatluna/message'\nimport { fileRefTableOf } from '@chatluna/message'\nimport type {} from '@chatluna/storage-files'\nimport type { Config, NativeToolKey, OneBotProtocol } from './config'\nimport { NATIVE_TOOL_KEYS, NATIVE_TOOLS } from './config'\nimport type { Logger } from './logger'\nimport {\n asMembers,\n callOneBot,\n memberCard,\n memberNickname,\n memberUserId,\n numericId,\n oneBotOf,\n type MemberInfo,\n} from './onebot'\n\n/** 工具执行时的上下文:会话所在的机器人与群。 */\nexport interface ToolScope {\n ctx: Context\n bot: Bot\n /** 会话所在群;私聊为 undefined。 */\n guildId?: string\n protocol: OneBotProtocol\n /** 发起调用的 agent(`call.agent`),用来在会话日志里解析图片 ref;脱离会话的调用没有。 */\n agent?: object\n}\n\ninterface NativeTool<A> {\n args: Schema<any, A>\n effect: 'read' | 'write'\n run(scope: ToolScope, args: A): Promise<string>\n}\n\nconst MISSING_GROUP = 'Missing groupId. Provide groupId explicitly or run inside a group session.'\nconst MAX_MEMBER_RESULTS = 10\n\nconst groupIdArg = () => Schema.string().required(false).description('Target group ID. Defaults to current session group.')\n\nfunction groupOf(scope: ToolScope, groupId: string | undefined): string {\n const resolved = groupId?.trim() || scope.guildId?.trim()\n if (!resolved) throw new Error(MISSING_GROUP)\n return resolved\n}\n\nfunction required(value: string, name: string): string {\n const trimmed = value.trim()\n if (!trimmed) throw new Error(`${name} is required.`)\n return trimmed\n}\n\nconst GENDERS = { unknown: '0', male: '1', female: '2' }\n\nconst STORAGE_PREFIX = 'storage://'\n/** v2 给模型的文件占位符 `<img ref=\"i…\"/>`:`i` 加内容哈希的最短唯一前缀(至少 6 位)。 */\nconst FILE_REF = /^i[0-9a-f]{6,}$/\n\nconst base64File = (buffer: Buffer) => `base64://${buffer.toString('base64')}`\n\n/** HTTP/HTTPS 图片先读成 base64:OneBot 端常访问不到临时 URL,或 URL 在提交前就已过期。 */\nasync function remoteFile(url: string): Promise<string> {\n try {\n const response = await fetch(url)\n if (!response.ok) throw new Error(`HTTP ${response.status}`)\n return base64File(Buffer.from(await response.arrayBuffer()))\n } catch (error) {\n throw new Error(`无法读取头像图片:${error instanceof Error ? error.message : String(error)}`)\n }\n}\n\n/** 入站图片由 v2 下载进 `chatluna_storage`,24 小时后过期;没装存储插件时会话里只有 url 或 data 块。 */\nasync function storageFile(ctx: Context, reference: string): Promise<string> {\n const storage = ctx.get('chatluna_storage')\n if (!storage) throw new Error(`Cannot read ${reference}: the chatluna_storage service is not loaded.`)\n const file = await storage.getTempFile(reference.slice(STORAGE_PREFIX.length))\n if (!file) throw new Error(`${reference} is not in storage (it may have expired).`)\n return base64File(await file.data)\n}\n\nfunction dataFile(data: string): string {\n if (!data.startsWith('data:')) return `base64://${data}`\n const comma = data.indexOf(',')\n const body = data.slice(comma + 1)\n return data.slice(0, comma).endsWith(';base64') ? `base64://${body}` : base64File(Buffer.from(decodeURIComponent(body)))\n}\n\nfunction messagesOf(agent: object | undefined): readonly ChatMessage[] | undefined {\n const session: unknown = agent && 'session' in agent ? agent.session : undefined\n if (session === null || typeof session !== 'object' || !('messages' in session) || typeof session.messages !== 'function') return undefined\n return session.messages() as readonly ChatMessage[]\n}\n\n/**\n * v2 不把聊天图片的地址交给模型,只给 `<img ref=\"i…\"/>`,ref 要在发起调用的 agent 的会话日志里按内容哈希解析\n * (与 v2 自带的 `look` 工具、出站 `<img ref>` 同一张表)。\n */\nasync function sessionImageFile(scope: ToolScope, ref: string): Promise<string> {\n const messages = messagesOf(scope.agent)\n if (!messages) throw new Error(`Image ref ${ref} can only be resolved inside a conversation.`)\n const block: ChatFileBlock | undefined = fileRefTableOf(messages).resolve(ref)\n if (!block) throw new Error(`Image ref ${ref} is not in this conversation.`)\n if (!block.mediaType.startsWith('image/')) throw new Error(`ref ${ref} is ${block.mediaType}, not an image.`)\n // 用原图 data,不用写入时压缩的 preview。\n const { data } = block\n if (data.type === 'data') return dataFile(data.data)\n if (data.type === 'url') return remoteFile(data.url)\n if (data.type === 'reference' && data.reference.startsWith(STORAGE_PREFIX)) return storageFile(scope.ctx, data.reference)\n throw new Error(`Image ref ${ref} cannot be read.`)\n}\n\n/** 头像来源:会话图片 ref、`storage://`、HTTP/HTTPS 图片都先读成 base64 再提交,其余(OneBot 端可读的本地路径)原样提交。 */\nasync function avatarFile(scope: ToolScope, image: string): Promise<string> {\n if (FILE_REF.test(image)) return sessionImageFile(scope, image)\n if (image.startsWith(STORAGE_PREFIX)) return storageFile(scope.ctx, image)\n if (/^https?:\\/\\//i.test(image)) return remoteFile(image)\n return image\n}\n\nfunction formatMember(index: number, member: MemberInfo): string {\n return `${index}. qqid:${memberUserId(member) || '未知'},群昵称:${memberCard(member) || '无'},QQ昵称:${memberNickname(member) || '无'}`\n}\n\nexport const NATIVE_TOOL_IMPLEMENTATIONS: { [K in NativeToolKey]: NativeTool<any> } = {\n setSelfProfile: {\n args: Schema.args({\n nickname: Schema.string().min(1).description('The new nickname for the bot.'),\n signature: Schema.string().required(false).description('Optional: the new personal signature.'),\n gender: Schema.union(['unknown', 'male', 'female']).required(false).description('Optional: the new gender.'),\n }),\n effect: 'write',\n async run(scope, args: { nickname: string, signature?: string, gender?: keyof typeof GENDERS }) {\n await callOneBot(scope.bot, 'set_qq_profile', {\n nickname: args.nickname,\n ...(args.signature ? { personal_note: args.signature } : {}),\n // LLBot 的 set_qq_profile 不接受 sex。\n ...(args.gender && scope.protocol !== 'llbot' ? { sex: GENDERS[args.gender] } : {}),\n })\n return '机器人资料已更新。'\n },\n },\n setQQAvatar: {\n args: Schema.args({\n imageUrl: Schema.string().min(1).description('The image: the ref attribute of an `<img ref=\"i…\"/>` in this conversation, an HTTP/HTTPS URL, or a local file path readable by the OneBot side.'),\n }),\n effect: 'write',\n async run(scope, args: { imageUrl: string }) {\n const file = await avatarFile(scope, required(args.imageUrl, 'imageUrl'))\n await callOneBot(scope.bot, 'set_qq_avatar', { file })\n return 'QQ 头像已更新。'\n },\n },\n setGroupCard: {\n args: Schema.args({\n groupId: groupIdArg(),\n userId: Schema.string().min(1).description('Target member user ID.'),\n card: Schema.string().min(1).description('New group card for the member.'),\n }),\n effect: 'write',\n async run(scope, args: { groupId?: string, userId: string, card: string }) {\n const groupId = groupOf(scope, args.groupId)\n const userId = required(args.userId, 'userId')\n const card = required(args.card, 'card')\n await callOneBot(scope.bot, 'set_group_card', { group_id: groupId, user_id: userId, card })\n return `群昵称已更新:${userId} -> ${card}`\n },\n },\n searchGroupMember: {\n args: Schema.args({\n groupId: groupIdArg(),\n query: Schema.string().min(1).description('qqid, group card, or QQ nickname to search.'),\n mode: Schema.union(['auto', 'byName', 'byQQ']).required(false).description('Search mode. Defaults to auto.'),\n }),\n effect: 'read',\n async run(scope, args: { groupId?: string, query: string, mode?: 'auto' | 'byName' | 'byQQ' }) {\n const query = required(args.query, 'query')\n const mode = args.mode ?? 'auto'\n const groupId = groupOf(scope, args.groupId)\n const members = asMembers(await callOneBot(scope.bot, 'get_group_member_list', { group_id: numericId(groupId) }))\n const byQQ = mode === 'byQQ' || (mode === 'auto' && /^\\d+$/.test(query))\n const keyword = query.toLowerCase()\n const matches = members.filter((member) => byQQ\n ? memberUserId(member) === query\n : [memberCard(member), memberNickname(member)].some((name) => name && name.toLowerCase().includes(keyword)))\n if (!matches.length) return '未找到匹配的群成员。'\n const header = matches.length > MAX_MEMBER_RESULTS\n ? `仅显示前 ${MAX_MEMBER_RESULTS} 条,共 ${matches.length} 条匹配:`\n : `找到 ${matches.length} 个匹配成员:`\n return [header, ...matches.slice(0, MAX_MEMBER_RESULTS).map((member, index) => formatMember(index + 1, member))].join('\\n')\n },\n },\n leaveGroup: {\n args: Schema.args({ groupId: groupIdArg() }),\n effect: 'write',\n async run(scope, args: { groupId?: string }) {\n const groupId = groupOf(scope, args.groupId)\n await callOneBot(scope.bot, 'set_group_leave', { group_id: groupId })\n return `已退出群 ${groupId}。`\n },\n },\n handleRequest: {\n args: Schema.args({\n requestType: Schema.union(['friend', 'group']).description('Request type: friend or group.'),\n flag: Schema.string().min(1).description('OneBot request flag from the event.'),\n approve: Schema.boolean().description('true to approve the request, false to reject it.'),\n subType: Schema.union(['add', 'invite']).required(false).description('Required for group requests: add or invite.'),\n remark: Schema.string().required(false).description('Optional friend remark when approving a friend request.'),\n userId: Schema.string().required(false).description('Requester user ID of a friend request; needed to apply the remark on SnowLuma.'),\n reason: Schema.string().required(false).description('Optional rejection reason for a group request.'),\n }),\n effect: 'write',\n async run(scope, args: { requestType: 'friend' | 'group', flag: string, approve: boolean, subType?: 'add' | 'invite', remark?: string, userId?: string, reason?: string }) {\n const flag = required(args.flag, 'request flag')\n const verb = args.approve ? '同意' : '拒绝'\n if (args.requestType === 'friend') {\n const remark = args.remark?.trim() ?? ''\n await callOneBot(scope.bot, 'set_friend_add_request', { flag, approve: args.approve, remark })\n const message = `已${verb}好友申请 ${flag}。`\n // SnowLuma 的 set_friend_add_request 不读 remark,同意后另用 set_friend_remark 设置。\n if (scope.protocol !== 'snowluma' || !args.approve || !remark) return message\n const userId = args.userId?.trim()\n if (!userId) return `${message}未提供申请人 userId,备注未设置。`\n await callOneBot(scope.bot, 'set_friend_remark', { user_id: numericId(userId), remark })\n return `${message}备注已设置为 ${remark}。`\n }\n if (!args.subType) throw new Error('subType is required for group requests; use add or invite.')\n await callOneBot(scope.bot, 'set_group_add_request', {\n flag,\n sub_type: args.subType,\n approve: args.approve,\n reason: args.reason?.trim() ?? '',\n })\n return `已${verb}${args.subType === 'invite' ? '群邀请' : '加群申请'} ${flag}。`\n },\n },\n setGroupKick: {\n args: Schema.args({\n groupId: groupIdArg(),\n userId: Schema.string().min(1).description('Target member user ID.'),\n }),\n effect: 'write',\n async run(scope, args: { groupId?: string, userId: string }) {\n const groupId = groupOf(scope, args.groupId)\n const userId = required(args.userId, 'userId')\n await callOneBot(scope.bot, 'set_group_kick', { group_id: groupId, user_id: userId })\n return `已将用户 ${userId} 移出群 ${groupId}。`\n },\n },\n setGroupSpecialTitle: {\n args: Schema.args({\n groupId: groupIdArg(),\n userId: Schema.string().min(1).description('Target member user ID.'),\n title: Schema.string().description('New special title. Use an empty string to clear it.'),\n }),\n effect: 'write',\n async run(scope, args: { groupId?: string, userId: string, title: string }) {\n const groupId = groupOf(scope, args.groupId)\n const userId = required(args.userId, 'userId')\n const title = args.title.trim()\n await callOneBot(scope.bot, 'set_group_special_title', { group_id: groupId, user_id: userId, special_title: title })\n return title ? `群专属头衔已更新:${userId} -> ${title}` : `群专属头衔已清除:${userId}`\n },\n },\n deleteMessage: {\n args: Schema.args({\n message_id: Schema.string().min(1).description('Specific message ID to delete.'),\n }),\n effect: 'write',\n async run(scope, args: { message_id: string }) {\n const messageId = required(args.message_id, 'message_id')\n await callOneBot(scope.bot, 'delete_msg', { message_id: numericId(messageId) })\n return `Message deleted by ID ${messageId}.`\n },\n },\n}\n\n/** 全部原生工具的定义,名称与描述固定为源插件的默认值。 */\nexport function nativeToolDefinitions(ctx: Context, config: Config, log: Logger): ToolDefinition[] {\n return NATIVE_TOOL_KEYS.map((key) => {\n const tool = NATIVE_TOOL_IMPLEMENTATIONS[key]\n const { toolName: name, description } = NATIVE_TOOLS[key]\n return {\n name,\n description,\n args: tool.args,\n traits: { effect: tool.effect },\n async run(args: unknown, call: ToolCallContext) {\n const location = call.location\n const bot = oneBotOf(ctx, location?.platform, location?.selfId)\n if (!bot) throw new Error('This tool only supports OneBot platform.')\n try {\n const result = await tool.run({ ctx, bot, guildId: location?.isDirect ? undefined : location?.guild, protocol: config.oneBotProtocol, agent: call.agent }, args)\n log.debug('%s: %s', name, result)\n return result\n } catch (error) {\n const message = error instanceof Error ? error.message : String(error)\n log.warn('%s failed: %s', name, message)\n throw new Error(`${name} failed: ${message}`)\n }\n },\n }\n })\n}\n\n/**\n * 注册原生工具。工具对所有会话全局可见(v2 的工具管理页因此能列出并开关它们),\n * 再给不在 OneBot 上的 agent 挂一层排除过滤,与源插件只在 OneBot 会话授权一致。\n */\nexport function registerNativeTools(ctx: Context, config: Config, log: Logger) {\n const definitions = nativeToolDefinitions(ctx, config, log)\n for (const definition of definitions) ctx.tools.define(definition)\n const exclude = definitions.map((definition) => definition.name)\n log.info('native tools registered: %s', exclude.join(', '))\n ctx.inject(['agents'], (inner) => {\n const scope = (agent: Agent) => {\n if (agent.options.location?.platform !== 'onebot') inner.tools.filter({ exclude }, agent)\n }\n inner.on('chatluna/agent-open', ({ agent }) => scope(agent))\n for (const agent of inner.agents.list()) scope(agent)\n })\n}\n","import type { Context } from 'cordis'\nimport type { Bot } from '@satorijs/core'\nimport type { PromptEnvironment, VariableDeclaration, VariableProvider } from '@chatluna/prompt'\nimport type { Config, GroupInfoItem, MemberInfoItem, OneBotProtocol } from './config'\nimport { DEFAULT_GROUP_INFO_ITEMS } from './config'\nimport type { Logger } from './logger'\nimport {\n asMember,\n asMembers,\n callOneBot,\n formatDate,\n formatDateTime,\n memberCard,\n memberNickname,\n memberRole,\n memberUserId,\n normalizeTimestamp,\n oneBotOf,\n pickFirst,\n pickText,\n type MemberInfo,\n} from './onebot'\n\nconst FALLBACK_MEMBER_ITEMS: MemberInfoItem[] = ['nickname', 'userId']\n\nconst SHUT_LIST_TIMEOUT_MS = 200\nconst SHUT_LIST_CACHE_TTL_MS = 15_000\n\nfunction translateGender(value: unknown): string {\n if (value === null || value === undefined) return ''\n const text = String(value).trim().toLowerCase()\n if (!text) return ''\n if (['male', 'man', 'm', '1', 'boy'].includes(text)) return '男'\n if (['female', 'woman', 'f', '2', 'girl'].includes(text)) return '女'\n if (['未知', 'unknown', '0', 'secret'].includes(text)) return ''\n return String(value)\n}\n\nfunction normalizeRole(value: string): string {\n const text = value.toLowerCase()\n if (!text) return '群员'\n if (['owner', 'group_owner', '群主'].includes(text)) return '群主'\n if (['admin', 'administrator', 'group_admin', '管理员'].includes(text)) return '管理员'\n if (['member', 'normal', '群员'].includes(text)) return '群员'\n return value\n}\n\n/** 会话所在的 Satori 机器人,任意平台;预览等没有真实机器人的环境里为 undefined。 */\nfunction botOf(ctx: Context, env: PromptEnvironment): Bot | undefined {\n return ctx.satori.bots.find((bot) => bot.platform === env.platform && bot.selfId === env.selfId)\n}\n\n/** 查询群成员信息;私聊、找不到机器人或查询失败时为 undefined,变量照常按已知信息渲染。 */\nasync function fetchMember(ctx: Context, env: PromptEnvironment, userId: string): Promise<MemberInfo | undefined> {\n const bot = botOf(ctx, env)\n if (!bot || !env.guildId) return undefined\n try {\n if (bot.platform === 'onebot') {\n return asMember(await callOneBot(bot, 'get_group_member_info', {\n group_id: Number(env.guildId),\n user_id: Number(userId),\n no_cache: false,\n }))\n }\n return asMember(await bot.getGuildMember(env.guildId, userId))\n } catch {\n return undefined\n }\n}\n\n/** 用户信息与机器人信息共用的渲染:按配置的信息项逐条输出,取不到的项省略。 */\nfunction renderMember(member: MemberInfo | undefined, items: MemberInfoItem[], id: string, fallbackName: unknown): string[] {\n const values: string[] = []\n for (const item of items) {\n switch (item) {\n case 'nickname': {\n const name = pickText(member?.card, member?.remark, member?.displayName, member?.nick, member?.nickname, member?.name, fallbackName, id)\n if (name) values.push(`name:${name}`)\n break\n }\n case 'userId':\n if (id) values.push(`id:${id}`)\n break\n case 'role':\n values.push(`群内身份:${normalizeRole(member ? memberRole(member) : '')}`)\n break\n case 'level': {\n const level = pickFirst(member?.level, member?.levelName, member?.level_name, member?.level_info?.current_level)\n if (level !== undefined) values.push(`群等级:${level}`)\n break\n }\n case 'title': {\n const title = pickFirst(member?.title, member?.specialTitle, member?.special_title)\n if (title !== undefined) values.push(`头衔:${title}`)\n break\n }\n case 'gender': {\n const gender = translateGender(member?.sex ?? member?.gender)\n if (gender) values.push(`性别:${gender}`)\n break\n }\n case 'age': {\n const age = Number(member?.age)\n if (Number.isFinite(age) && age > 0) values.push(`年龄:${age}`)\n break\n }\n case 'area': {\n const area = pickFirst(member?.area, member?.region, member?.location)\n if (area !== undefined) values.push(`地区:${area}`)\n break\n }\n case 'joinTime': {\n const joinTime = formatDate(normalizeTimestamp(pickFirst(member?.join_time, member?.joined_at, member?.joinTime, member?.joinedAt)))\n if (joinTime) values.push(`入群:${joinTime}`)\n break\n }\n case 'lastSentTime': {\n const lastSentTime = formatDateTime(normalizeTimestamp(pickFirst(member?.last_sent_time, member?.lastSentTime, member?.lastSpeakTimestamp)))\n if (lastSentTime) values.push(`活跃:${lastSentTime}`)\n break\n }\n }\n }\n return values\n}\n\nconst itemsOf = (items: MemberInfoItem[]) => items.length ? items : FALLBACK_MEMBER_ITEMS\n\nexport function userInfoProvider(ctx: Context, config: Config): VariableProvider {\n return async ({ env }) => {\n // 群聊里 userId 只来自本步最新的用户消息,工具调用后的后续步骤可能缺失,退回到消息发送者。\n const userId = (env.userId ?? env.message?.senderId)?.trim()\n if (!userId) return '未知用户'\n const member = await fetchMember(ctx, env, userId)\n const values = renderMember(member, itemsOf(config.userInfo.items), userId, env.userName ?? env.message?.senderName)\n return values.length ? values.join(', ') : `id:${userId}`\n }\n}\n\nexport function botInfoProvider(ctx: Context, config: Config): VariableProvider {\n return async ({ env }) => {\n const botId = env.selfId?.trim()\n if (!botId) return '未知机器人'\n const member = await fetchMember(ctx, env, botId)\n const values = renderMember(member, itemsOf(config.botInfo.items), botId, undefined)\n return values.length ? values.join(', ') : botId\n }\n}\n\ninterface GroupInfo {\n group_id?: unknown\n groupId?: unknown\n id?: unknown\n group_name?: unknown\n groupName?: unknown\n name?: unknown\n member_count?: unknown\n memberCount?: unknown\n max_member_count?: unknown\n create_time?: unknown\n createTime?: unknown\n group_create_time?: unknown\n}\n\nasync function fetchRoles(bot: Bot, guildId: string, log: Logger): Promise<{ owners: string[], admins: string[] } | undefined> {\n try {\n const members = asMembers(await callOneBot(bot, 'get_group_member_list', { group_id: Number(guildId) }))\n if (!members.length) return undefined\n const owners: string[] = []\n const admins: string[] = []\n for (const member of members) {\n const role = memberRole(member).toLowerCase()\n const userId = memberUserId(member)\n const name = pickText(memberCard(member), memberNickname(member)) || userId\n const label = userId ? `${name}(${userId})` : name\n if (['owner', 'master', 'leader'].includes(role)) owners.push(label)\n else if (['admin', 'administrator', 'manager'].includes(role)) admins.push(label)\n }\n return { owners, admins }\n } catch (error) {\n log.debug('group roles lookup failed: %s', String(error))\n return undefined\n }\n}\n\nfunction renderGroupInfo(group: GroupInfo, items: GroupInfoItem[], roles: { owners: string[], admins: string[] } | undefined): string {\n const id = pickText(group.group_id, group.groupId, group.id)\n const name = pickText(group.group_name, group.groupName, group.name)\n const memberCount = group.member_count ?? group.memberCount ?? group.max_member_count\n const createTime = formatDate(normalizeTimestamp(group.create_time ?? group.createTime ?? group.group_create_time))\n const values: string[] = []\n for (const item of items) {\n switch (item) {\n case 'groupName':\n if (name) values.push(`群名:${name}`)\n break\n case 'groupId':\n if (id) values.push(`群号:${id}`)\n break\n case 'memberCount':\n if (memberCount !== undefined && memberCount !== null) values.push(`成员数量:${String(memberCount)}`)\n break\n case 'createTime':\n if (createTime) values.push(`创建时间:${createTime}`)\n break\n case 'ownerList':\n if (roles) values.push(`群主:${roles.owners.length ? roles.owners.join('、') : '无'}`)\n break\n case 'adminList':\n if (roles) values.push(`管理员:${roles.admins.length ? roles.admins.join('、') : '无'}`)\n break\n }\n }\n return values.length ? values.join('\\n') : id || name || '未能获取当前群信息。'\n}\n\nexport function groupInfoProvider(ctx: Context, config: Config, log: Logger): VariableProvider {\n return async ({ env }) => {\n if (!env.guildId) return ''\n if (env.platform !== 'onebot') return '当前平台暂不支持查询群信息。'\n const bot = oneBotOf(ctx, env.platform, env.selfId)\n if (!bot) return '暂无群信息。'\n const items = config.groupInfo.items.length ? config.groupInfo.items : DEFAULT_GROUP_INFO_ITEMS\n const group = await callOneBot(bot, 'get_group_info', { group_id: Number(env.guildId), no_cache: false })\n .then(asMember, () => undefined)\n if (!group) return '未能获取当前群信息。'\n const roles = items.includes('ownerList') || items.includes('adminList') ? await fetchRoles(bot, env.guildId, log) : undefined\n return renderGroupInfo(group, items, roles)\n }\n}\n\nclass ShutListTimeout extends Error {}\n\n/** 群号类型不被接受时的报错特征:NapCat 要数字、LLBot 要字符串,类型不对时换另一种再试一次。 */\nconst WRONG_ID_TYPE = /string|number|type|invalid|参数/i\n\nfunction withTimeout<T>(promise: Promise<T>, ms: number): Promise<T> {\n let timer: ReturnType<typeof setTimeout> | undefined\n return Promise.race([\n promise,\n new Promise<never>((_, reject) => {\n timer = setTimeout(() => reject(new ShutListTimeout('group shut list request timed out')), ms)\n }),\n ]).finally(() => clearTimeout(timer))\n}\n\nasync function fetchShutList(bot: Bot, guildId: string, protocol: OneBotProtocol, log: Logger): Promise<unknown[]> {\n const preferred = protocol === 'llbot' ? guildId : Number(guildId)\n const fallback = protocol === 'llbot' ? Number(guildId) : guildId\n const request = async (groupId: string | number) => {\n const data = await withTimeout(callOneBot(bot, 'get_group_shut_list', { group_id: groupId }), SHUT_LIST_TIMEOUT_MS)\n return Array.isArray(data) ? data : []\n }\n try {\n return await request(preferred)\n } catch (error) {\n if (error instanceof ShutListTimeout || !(error instanceof Error) || !WRONG_ID_TYPE.test(error.message)) throw error\n log.debug('group shut list retries with %s group id: %s', typeof fallback, error.message)\n return request(fallback)\n }\n}\n\nfunction renderShutItem(item: unknown): string | undefined {\n const record = asMember(item)\n if (!record) return undefined\n const field = (key: string) => Reflect.get(record, key)\n const userId = pickText(field('uin'), field('user_id'))\n if (!userId) return undefined\n const name = pickText(field('cardName'), field('remark'), field('nick'), field('nickname'), userId)\n const until = formatDateTime(normalizeTimestamp(pickFirst(field('shutUpTime'), field('shut_up_time')))) || '未知'\n return `id:${userId},name:${name},禁言截至:${until}`\n}\n\nexport function groupShutListProvider(ctx: Context, config: Config, log: Logger): VariableProvider {\n // 变量每一步都会重新渲染,同一个群的结果缓存 15 秒,避免每一步都请求一次。\n const cache = new Map<string, { value: string, expiresAt: number }>()\n return async ({ env }) => {\n if (!env.guildId) return ''\n if (env.platform !== 'onebot') return '当前平台暂不支持查询群禁言列表。'\n const bot = oneBotOf(ctx, env.platform, env.selfId)\n if (!bot) return '暂无群禁言列表。'\n const key = `${env.selfId}:${env.guildId}`\n const cached = cache.get(key)\n if (cached && cached.expiresAt > Date.now()) return cached.value\n try {\n const lines = (await fetchShutList(bot, env.guildId, config.oneBotProtocol, log)).flatMap((item) => renderShutItem(item) ?? [])\n const value = lines.length ? lines.join('\\n') : '当前群暂无禁言成员。'\n cache.set(key, { value, expiresAt: Date.now() + SHUT_LIST_CACHE_TTL_MS })\n return value\n } catch (error) {\n log.debug('group shut list failed: %s', String(error))\n // 部分实现在群里没有禁言成员时迟迟不响应,超时按空列表处理。\n return error instanceof ShutListTimeout ? '当前群暂无禁言成员。' : '获取群禁言列表失败。'\n }\n }\n}\n\n/** 按配置得到要注册的变量;变量名留空的不注册。 */\nexport function variableDefinitions(ctx: Context, config: Config, log: Logger): Array<[VariableDeclaration, VariableProvider]> {\n const entries: Array<[string, string, VariableProvider]> = [\n [config.userInfo.variableName, 'Current user info in this group', userInfoProvider(ctx, config)],\n [config.botInfo.variableName, 'Bot info in this group', botInfoProvider(ctx, config)],\n [config.groupInfo.variableName, 'Current group info', groupInfoProvider(ctx, config, log)],\n [config.groupShutList.variableName, 'Muted members of this group', groupShutListProvider(ctx, config, log)],\n ]\n // 都随群聊状态变化,属于可变变量:不能写进 system 区,v1 预设会被自动挪到 context 区。\n return entries\n .filter(([name]) => name.trim())\n .map(([name, description, provider]) => [{ name: name.trim(), description, mutable: true }, provider])\n}\n\nexport function registerVariables(ctx: Context, config: Config, log: Logger) {\n for (const [declaration, provider] of variableDefinitions(ctx, config, log)) {\n try {\n ctx.prompt.variable(declaration, provider)\n } catch (error) {\n // 同名变量已被其他插件注册时只跳过这一个,其余变量与工具照常可用。\n log.warn('variable %s not registered: %s', declaration.name, error instanceof Error ? error.message : String(error))\n }\n }\n}\n","import type { Context } from 'cordis'\nimport type { Config } from './config'\nimport { createLogger } from './logger'\nimport { registerRequestNotices } from './requests'\nimport { registerNativeTools } from './tools'\nimport { registerVariables } from './variables'\n\nexport * from './config'\n\nexport const name = 'chatluna-onebot-toolbox'\n\n/** 过滤工具用的 agents、唤醒用的 chat 与读取会话图片用的 chatluna_storage 是可选服务。 */\nexport const inject = ['satori', 'tools', 'prompt']\n\nexport function apply(ctx: Context, config: Config) {\n const log = createLogger(ctx, config.debugLogging)\n registerVariables(ctx, config, log)\n registerNativeTools(ctx, config, log)\n registerRequestNotices(ctx, config, log)\n}\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"index.js","names":[],"sources":["../src/logger.ts","../src/config.ts","../src/requests.ts","../src/onebot.ts","../src/tools.ts","../src/variables.ts","../src/index.ts"],"sourcesContent":["import type { Context } from 'cordis'\n\n/** 插件内的日志:`debugLogging` 打开时调试信息以 info 级别输出,否则走 logger 自己的 debug 级别。 */\nexport interface Logger {\n debug(format: string, ...args: unknown[]): void\n info(format: string, ...args: unknown[]): void\n warn(format: string, ...args: unknown[]): void\n}\n\nexport function createLogger(ctx: Context, debugLogging: boolean): Logger {\n const logger = ctx.logger\n return {\n debug: (format, ...args) => (debugLogging ? logger.info(format, ...args) : logger.debug(format, ...args)),\n info: (format, ...args) => logger.info(format, ...args),\n warn: (format, ...args) => logger.warn(format, ...args),\n }\n}\n","import { Schema } from '@chatluna/schema'\n\nexport type OneBotProtocol = 'napcat' | 'llbot' | 'snowluma'\n\nexport type NativeToolKey =\n | 'setSelfProfile'\n | 'setQQAvatar'\n | 'setGroupCard'\n | 'searchGroupMember'\n | 'leaveGroup'\n | 'handleRequest'\n | 'setGroupKick'\n | 'setGroupSpecialTitle'\n | 'deleteMessage'\n\nexport interface NativeToolInfo {\n toolName: string\n description: string\n}\n\nexport type MemberInfoItem =\n | 'userId'\n | 'nickname'\n | 'role'\n | 'level'\n | 'title'\n | 'gender'\n | 'age'\n | 'area'\n | 'joinTime'\n | 'lastSentTime'\n\nexport type GroupInfoItem = 'groupName' | 'groupId' | 'memberCount' | 'createTime' | 'ownerList' | 'adminList'\n\nexport interface Config {\n oneBotProtocol: OneBotProtocol\n requestNotice: boolean\n userInfo: { variableName: string, items: MemberInfoItem[] }\n botInfo: { variableName: string, items: MemberInfoItem[] }\n groupInfo: { variableName: string, items: GroupInfoItem[] }\n groupShutList: { variableName: string }\n debugLogging: boolean\n}\n\n/** 原生工具的名称与描述固定,全部注册;启用范围交给 v2 的工具管理。 */\nexport const NATIVE_TOOLS: Record<NativeToolKey, NativeToolInfo> = {\n setSelfProfile: {\n toolName: 'set_self_profile',\n description: '修改自己的 QQ 资料,可设置昵称、签名和性别。',\n },\n setQQAvatar: {\n toolName: 'set_qq_avatar',\n description: '修改机器人 QQ 头像。imageUrl 填会话里图片的 ref(`<img ref=\"i…\"/>` 的 ref 属性)、HTTP/HTTPS 链接或 OneBot 端可读的本地路径;会话图片与 HTTP/HTTPS 图片会先由插件读取并转为 OneBot base64 文件,避免临时 URL 过期或 OneBot 端无法访问。',\n },\n setGroupCard: {\n toolName: 'set_group_card',\n description: '修改群成员的群昵称。',\n },\n searchGroupMember: {\n toolName: 'search_group_member',\n description: '根据 qqid 精确查询群成员的群昵称与 QQ 昵称,或根据群昵称/QQ 昵称搜索 qqid。默认查询当前群聊,可通过 groupId 查询指定群。',\n },\n leaveGroup: {\n toolName: 'set_group_leave',\n description: '让机器人退出当前群聊。',\n },\n handleRequest: {\n toolName: 'handle_request',\n description: '根据 OneBot 申请 flag 同意或拒绝好友申请、加群申请或群邀请。',\n },\n setGroupKick: {\n toolName: 'set_group_kick',\n description: '将指定成员踢出群聊。',\n },\n setGroupSpecialTitle: {\n toolName: 'set_group_special_title',\n description: '修改或清除群成员的 QQ 群专属头衔。',\n },\n deleteMessage: {\n toolName: 'delete_msg',\n description: '根据消息 ID 撤回指定消息。',\n },\n}\n\nexport const NATIVE_TOOL_KEYS = Object.keys(NATIVE_TOOLS) as NativeToolKey[]\n\nexport const DEFAULT_MEMBER_INFO_ITEMS: MemberInfoItem[] = [\n 'nickname', 'userId', 'role', 'level', 'title', 'gender', 'age', 'area', 'joinTime', 'lastSentTime',\n]\n\nexport const DEFAULT_GROUP_INFO_ITEMS: GroupInfoItem[] = ['groupName', 'groupId', 'memberCount', 'ownerList', 'adminList']\n\n/** 变量名须满足 v2 模板的变量命名规则(`@chatluna/prompt` 的 `variable`),留空表示不注册。 */\nconst VARIABLE_NAME = /^([a-z_][a-z0-9_]*)?$/i\n\nconst memberItems = (subject: string) => Schema.array(Schema.union([\n Schema.const('userId').description(`${subject} ID`),\n Schema.const('nickname').description('群昵称'),\n Schema.const('role').description('群内身份'),\n Schema.const('level').description('群等级'),\n Schema.const('title').description('群头衔'),\n Schema.const('gender').description('性别'),\n Schema.const('age').description('年龄'),\n Schema.const('area').description('地区'),\n Schema.const('joinTime').description('入群时间'),\n Schema.const('lastSentTime').description('最后发言时间'),\n])).role('checkbox').default([...DEFAULT_MEMBER_INFO_ITEMS])\n\nconst variableName = (name: string) => Schema.string().default(name).pattern(VARIABLE_NAME).extra('label', '变量名称')\n .description('在预设中以`{变量名称}`引用,留空时不注册')\n\nconst basicFields = {\n oneBotProtocol: Schema.union([\n Schema.const('napcat').description('NapCat'),\n Schema.const('llbot').description('LLBot'),\n Schema.const('snowluma').description('SnowLuma'),\n ]).default('napcat').role('radio').extra('label', 'OneBot 协议')\n .description('按所选实现调整请求参数:LLBot 不支持设置性别,群禁言列表的群号类型也不同;SnowLuma 同意好友申请后另行设置备注'),\n requestNotice: Schema.boolean().default(true).extra('label', '申请通知')\n .description('收到好友申请、加群申请或群邀请时唤醒对应会话,把申请 flag 告诉模型,供`handle_request`工具处理。好友申请与群邀请发往申请人的私聊,加群申请发往所申请的群;该工具在工具管理中禁用时不唤醒'),\n}\n\nconst variableFields = {\n userInfo: Schema.object({\n variableName: variableName('userInfo'),\n items: memberItems('用户').extra('label', '信息项').description('选择用户信息变量中显示的详细信息项'),\n }).description('用户信息变量').collapse(),\n botInfo: Schema.object({\n variableName: variableName('botInfo'),\n items: memberItems('机器人').extra('label', '信息项').description('选择机器人信息变量中显示的详细信息项'),\n }).description('机器人信息变量').collapse(),\n groupInfo: Schema.object({\n variableName: variableName('groupInfo'),\n items: Schema.array(Schema.union([\n Schema.const('groupName').description('群名称'),\n Schema.const('groupId').description('群号'),\n Schema.const('memberCount').description('成员数量'),\n Schema.const('createTime').description('创建时间(仅部分实现支持)'),\n Schema.const('ownerList').description('群主名单'),\n Schema.const('adminList').description('管理员名单'),\n ])).role('checkbox').default([...DEFAULT_GROUP_INFO_ITEMS]).extra('label', '信息项')\n .description('选择群信息变量中显示的群信息项'),\n }).description('群信息变量').collapse(),\n groupShutList: Schema.object({\n variableName: variableName('groupShutList'),\n }).description('群禁言列表变量').collapse(),\n}\n\nconst otherFields = {\n debugLogging: Schema.boolean().default(false).extra('label', '输出调试日志')\n .description('以 info 级别输出工具调用与变量解析的调试信息'),\n}\n\n/**\n * 插件配置,直接在 v2 的插件设置页编辑。顶层是分组的 intersect,SchemaForm 把每组的 description 渲染成小节标题;\n * 每个字段都写 `extra('label')`,缺 label 时 SchemaForm 会把配置键当标题显示。\n */\nexport const Config: Schema<Config> = Schema.intersect([\n Schema.object(basicFields).description('基础设置'),\n Schema.object(variableFields).description('变量'),\n Schema.object(otherFields).description('其他设置'),\n])\n","import type { Context } from 'cordis'\nimport type { Session } from '@satorijs/core'\nimport type { RouteIdentity } from '@chatluna/satori'\nimport type {} from '@chatluna/chat'\nimport type { Config } from './config'\nimport { NATIVE_TOOLS } from './config'\nimport type { Logger } from './logger'\n\ntype RequestKind = 'friend' | 'add' | 'invite'\n\n/**\n * 申请发往的会话:好友申请与群邀请发往申请人的私聊,加群申请发往所申请的群。\n * 用路由身份而不是事件 Session 唤醒:申请事件不是消息,申请人与机器人之间往往还没有会话,\n * `chat.wake` 会按身份新开一个。\n */\nexport function requestTarget(session: Session, kind: RequestKind): RouteIdentity | undefined {\n if (!session.userId) return undefined\n if (kind === 'add') {\n if (!session.guildId) return undefined\n return { platform: session.platform, selfId: session.selfId, isDirect: false, guildId: session.guildId, channelId: session.guildId, userId: session.userId }\n }\n return { platform: session.platform, selfId: session.selfId, isDirect: true, channelId: `private:${session.userId}`, userId: session.userId }\n}\n\n/** 唤醒原因即模型看到的通知,写明 flag 与调用方式,模型据此调用处理申请工具。 */\nexport function requestReason(session: Session, kind: RequestKind, toolName: string): string {\n const comment = session.content?.trim() ? `, message: ${JSON.stringify(session.content.trim())}` : ''\n const flag = `flag: ${JSON.stringify(session.messageId ?? '')}`\n if (kind === 'friend') {\n return `user ${session.userId} sent you a friend request (${flag}${comment}). Call ${toolName} with requestType \"friend\" and userId \"${session.userId}\" to approve or reject it`\n }\n if (kind === 'add') {\n return `user ${session.userId} requested to join group ${session.guildId} (${flag}${comment}). Call ${toolName} with requestType \"group\" and subType \"add\" to approve or reject it`\n }\n return `user ${session.userId} invited you to join group ${session.guildId} (${flag}${comment}). Call ${toolName} with requestType \"group\" and subType \"invite\" to approve or reject it`\n}\n\n/**\n * v2 不把 OneBot 的申请事件交给模型,`handle_request` 拿不到 flag 就无从调用,\n * 所以把申请转成一次唤醒;工具在 v2 工具管理里被禁用时跟着不唤醒。会话所在路由关闭或没选模型时 `wake` 会报错,只记调试日志。\n */\nexport function registerRequestNotices(ctx: Context, config: Config, log: Logger) {\n if (!config.requestNotice) return\n const toolName = NATIVE_TOOLS.handleRequest.toolName\n ctx.inject(['chat'], (inner) => {\n const notify = (session: Session, kind: RequestKind) => {\n if (session.platform !== 'onebot') return\n // 没有 flag 时模型无从处理申请,唤醒只会让它拿空值反复试错。\n if (!session.messageId) return log.debug('%s request without flag ignored', kind)\n // 工具在 v2 工具管理里被禁用时(全局过滤排除了它)不唤醒:模型调用不到,唤醒只会让它白跑一轮。\n if (!inner.tools.list().some((tool) => tool.name === toolName)) return log.debug('%s request ignored: %s is disabled', kind, toolName)\n const target = requestTarget(session, kind)\n if (!target) return\n inner.chat.wake(target, { reason: requestReason(session, kind, toolName), by: 'onebot-toolbox' })\n .then(() => log.debug('%s request %s delivered', kind, session.messageId))\n .catch((error: unknown) => log.debug('%s request %s not delivered: %s', kind, session.messageId, String(error)))\n }\n inner.on('friend-request', (session) => notify(session, 'friend'))\n inner.on('guild-member-request', (session) => notify(session, 'add'))\n inner.on('guild-request', (session) => notify(session, 'invite'))\n })\n}\n","import type { Context } from 'cordis'\nimport type { Bot } from '@satorijs/core'\n\n/** OneBot 适配器的原始请求返回完整响应包,业务失败也照常 resolve。 */\ninterface OneBotInternal {\n _request(action: string, params: Record<string, unknown>): Promise<unknown>\n}\n\nconst isRecord = (value: unknown): value is Record<string, unknown> =>\n typeof value === 'object' && value !== null && !Array.isArray(value)\n\n/** 会话所在的 OneBot 机器人;不是 OneBot 会话或找不到机器人时为 undefined。 */\nexport function oneBotOf(ctx: Context, platform: string | undefined, selfId: string | undefined): Bot | undefined {\n if (platform !== 'onebot' || selfId === undefined) return undefined\n return ctx.satori.bots.find((bot) => bot.platform === 'onebot' && bot.selfId === selfId)\n}\n\n/** 调用 OneBot action 并返回 data,按响应包的 status 与 retcode 判定业务失败。 */\nexport async function callOneBot(bot: Bot, action: string, params: Record<string, unknown>): Promise<unknown> {\n const internal: OneBotInternal = bot.internal\n const result = await internal._request(action, params)\n if (!isRecord(result) || typeof result.status !== 'string' || typeof result.retcode !== 'number') {\n throw new Error('Invalid OneBot response envelope')\n }\n if (result.status === 'ok' && result.retcode === 0) return result.data\n const reason = result.wording || result.message || result.status || 'OneBot returned failed'\n throw new Error(`${String(reason)} (retcode: ${result.retcode})`)\n}\n\n/**\n * 整数形式的 ID 转成数字,其余原样保留;OneBot 实现普遍要求数字形式的群号、QQ 号与消息 ID。\n * SnowLuma 的 message_id 是 int32 哈希,常为负数,所以带负号的整数同样转换。\n */\nexport function numericId(value: string): string | number {\n return /^-?\\d+$/.test(value) ? Number(value) : value\n}\n\n/** 取第一个非空值(去掉首尾空白后不为空字符串),保留原始类型。 */\nexport function pickFirst(...values: unknown[]): string | number | undefined {\n for (const value of values) {\n if (value === null || value === undefined) continue\n if (!String(value).trim()) continue\n return typeof value === 'number' ? value : String(value)\n }\n return undefined\n}\n\nexport function pickText(...values: unknown[]): string {\n return String(pickFirst(...values) ?? '').trim()\n}\n\n/** OneBot 使用秒,Satori 使用毫秒,统一成毫秒;空值与 0 视为没有。 */\nexport function normalizeTimestamp(raw: unknown): number | undefined {\n if (raw === null || raw === undefined || raw === '') return undefined\n const numeric = Number(raw)\n if (!Number.isFinite(numeric) || numeric === 0) return undefined\n return numeric < 1e11 ? numeric * 1000 : numeric\n}\n\nconst pad = (value: number) => String(value).padStart(2, '0')\n\nexport function formatDate(value: number | undefined): string {\n if (value === undefined) return ''\n const date = new Date(value)\n if (Number.isNaN(date.valueOf())) return ''\n return `${date.getFullYear()}-${pad(date.getMonth() + 1)}-${pad(date.getDate())}`\n}\n\nexport function formatDateTime(value: number | undefined): string {\n const day = formatDate(value)\n if (!day || value === undefined) return day\n const date = new Date(value)\n return `${day} ${pad(date.getHours())}:${pad(date.getMinutes())}`\n}\n\n/** OneBot 群成员信息;其他平台的 Satori 成员信息在查询入口显式转换。 */\nexport interface MemberInfo {\n user_id?: unknown\n role?: unknown\n card?: unknown\n nickname?: unknown\n level?: unknown\n title?: unknown\n sex?: unknown\n age?: unknown\n area?: unknown\n join_time?: unknown\n last_sent_time?: unknown\n}\n\nexport function asMembers(value: unknown): MemberInfo[] {\n return Array.isArray(value) ? value.filter(isRecord) : []\n}\n\nexport function asRecord(value: unknown): Record<string, unknown> | undefined {\n return isRecord(value) ? value : undefined\n}\n","import type { Context } from 'cordis'\nimport type { Bot } from '@satorijs/core'\nimport { Schema } from '@chatluna/schema'\nimport type { ToolCallContext, ToolDefinition } from '@chatluna/tool'\nimport type { Agent } from '@chatluna/agent'\nimport type { ChatFileBlock, ChatMessage } from '@chatluna/message'\nimport { fileRefTableOf } from '@chatluna/message'\nimport type {} from '@chatluna/storage-files'\nimport type { Config, NativeToolKey, OneBotProtocol } from './config'\nimport { NATIVE_TOOL_KEYS, NATIVE_TOOLS } from './config'\nimport type { Logger } from './logger'\nimport {\n asMembers,\n callOneBot,\n pickText,\n numericId,\n oneBotOf,\n type MemberInfo,\n} from './onebot'\n\n/** 工具执行时的上下文:会话所在的机器人与群。 */\nexport interface ToolScope {\n ctx: Context\n bot: Bot\n /** 会话所在群;私聊为 undefined。 */\n guildId?: string\n protocol: OneBotProtocol\n /** 发起调用的 agent(`call.agent`),用来在会话日志里解析图片 ref;脱离会话的调用没有。 */\n agent?: object\n}\n\ninterface NativeTool<A> {\n args: Schema<any, A>\n effect: 'read' | 'write'\n run(scope: ToolScope, args: A): Promise<string>\n}\n\nconst MISSING_GROUP = 'Missing groupId. Provide groupId explicitly or run inside a group session.'\nconst MAX_MEMBER_RESULTS = 10\n\nconst groupIdArg = () => Schema.string().required(false).description('Target group ID. Defaults to current session group.')\n\nfunction groupOf(scope: ToolScope, groupId: string | undefined): string {\n const resolved = groupId?.trim() || scope.guildId?.trim()\n if (!resolved) throw new Error(MISSING_GROUP)\n return resolved\n}\n\nfunction required(value: string, name: string): string {\n const trimmed = value.trim()\n if (!trimmed) throw new Error(`${name} is required.`)\n return trimmed\n}\n\nconst GENDERS = { unknown: '0', male: '1', female: '2' }\n\nconst STORAGE_PREFIX = 'storage://'\n/** v2 给模型的文件占位符 `<img ref=\"i…\"/>`:`i` 加内容哈希的最短唯一前缀(至少 6 位)。 */\nconst FILE_REF = /^i[0-9a-f]{6,}$/\n\nconst base64File = (buffer: Buffer) => `base64://${buffer.toString('base64')}`\n\n/** HTTP/HTTPS 图片先读成 base64:OneBot 端常访问不到临时 URL,或 URL 在提交前就已过期。 */\nasync function remoteFile(url: string): Promise<string> {\n try {\n const response = await fetch(url)\n if (!response.ok) throw new Error(`HTTP ${response.status}`)\n return base64File(Buffer.from(await response.arrayBuffer()))\n } catch (error) {\n throw new Error(`无法读取头像图片:${error instanceof Error ? error.message : String(error)}`)\n }\n}\n\n/** 入站图片由 v2 下载进 `chatluna_storage`,24 小时后过期;没装存储插件时会话里只有 url 或 data 块。 */\nasync function storageFile(ctx: Context, reference: string): Promise<string> {\n const storage = ctx.get('chatluna_storage')\n if (!storage) throw new Error(`Cannot read ${reference}: the chatluna_storage service is not loaded.`)\n const file = await storage.getTempFile(reference.slice(STORAGE_PREFIX.length))\n if (!file) throw new Error(`${reference} is not in storage (it may have expired).`)\n return base64File(await file.data)\n}\n\nfunction dataFile(data: string): string {\n if (!data.startsWith('data:')) return `base64://${data}`\n const comma = data.indexOf(',')\n const body = data.slice(comma + 1)\n return data.slice(0, comma).endsWith(';base64') ? `base64://${body}` : base64File(Buffer.from(decodeURIComponent(body)))\n}\n\nfunction messagesOf(agent: object | undefined): readonly ChatMessage[] | undefined {\n const session: unknown = agent && 'session' in agent ? agent.session : undefined\n if (session === null || typeof session !== 'object' || !('messages' in session) || typeof session.messages !== 'function') return undefined\n return session.messages() as readonly ChatMessage[]\n}\n\n/**\n * v2 不把聊天图片的地址交给模型,只给 `<img ref=\"i…\"/>`,ref 要在发起调用的 agent 的会话日志里按内容哈希解析\n * (与 v2 自带的 `look` 工具、出站 `<img ref>` 同一张表)。\n */\nasync function sessionImageFile(scope: ToolScope, ref: string): Promise<string> {\n const messages = messagesOf(scope.agent)\n if (!messages) throw new Error(`Image ref ${ref} can only be resolved inside a conversation.`)\n const block: ChatFileBlock | undefined = fileRefTableOf(messages).resolve(ref)\n if (!block) throw new Error(`Image ref ${ref} is not in this conversation.`)\n if (!block.mediaType.startsWith('image/')) throw new Error(`ref ${ref} is ${block.mediaType}, not an image.`)\n // 用原图 data,不用写入时压缩的 preview。\n const { data } = block\n if (data.type === 'data') return dataFile(data.data)\n if (data.type === 'url') return remoteFile(data.url)\n if (data.type === 'reference' && data.reference.startsWith(STORAGE_PREFIX)) return storageFile(scope.ctx, data.reference)\n throw new Error(`Image ref ${ref} cannot be read.`)\n}\n\n/** 头像来源:会话图片 ref、`storage://`、HTTP/HTTPS 图片都先读成 base64 再提交,其余(OneBot 端可读的本地路径)原样提交。 */\nasync function avatarFile(scope: ToolScope, image: string): Promise<string> {\n if (FILE_REF.test(image)) return sessionImageFile(scope, image)\n if (image.startsWith(STORAGE_PREFIX)) return storageFile(scope.ctx, image)\n if (/^https?:\\/\\//i.test(image)) return remoteFile(image)\n return image\n}\n\nfunction formatMember(index: number, member: MemberInfo): string {\n return `${index}. qqid:${pickText(member.user_id) || '未知'},群昵称:${pickText(member.card) || '无'},QQ昵称:${pickText(member.nickname) || '无'}`\n}\n\nexport const NATIVE_TOOL_IMPLEMENTATIONS: { [K in NativeToolKey]: NativeTool<any> } = {\n setSelfProfile: {\n args: Schema.args({\n nickname: Schema.string().min(1).description('The new nickname for the bot.'),\n signature: Schema.string().required(false).description('Optional: the new personal signature.'),\n gender: Schema.union(['unknown', 'male', 'female']).required(false).description('Optional: the new gender.'),\n }),\n effect: 'write',\n async run(scope, args: { nickname: string, signature?: string, gender?: keyof typeof GENDERS }) {\n await callOneBot(scope.bot, 'set_qq_profile', {\n nickname: args.nickname,\n ...(args.signature ? { personal_note: args.signature } : {}),\n // LLBot 的 set_qq_profile 不接受 sex。\n ...(args.gender && scope.protocol !== 'llbot' ? { sex: GENDERS[args.gender] } : {}),\n })\n return '机器人资料已更新。'\n },\n },\n setQQAvatar: {\n args: Schema.args({\n imageUrl: Schema.string().min(1).description('The image: the ref attribute of an `<img ref=\"i…\"/>` in this conversation, an HTTP/HTTPS URL, or a local file path readable by the OneBot side.'),\n }),\n effect: 'write',\n async run(scope, args: { imageUrl: string }) {\n const file = await avatarFile(scope, required(args.imageUrl, 'imageUrl'))\n await callOneBot(scope.bot, 'set_qq_avatar', { file })\n return 'QQ 头像已更新。'\n },\n },\n setGroupCard: {\n args: Schema.args({\n groupId: groupIdArg(),\n userId: Schema.string().min(1).description('Target member user ID.'),\n card: Schema.string().min(1).description('New group card for the member.'),\n }),\n effect: 'write',\n async run(scope, args: { groupId?: string, userId: string, card: string }) {\n const groupId = groupOf(scope, args.groupId)\n const userId = required(args.userId, 'userId')\n const card = required(args.card, 'card')\n await callOneBot(scope.bot, 'set_group_card', { group_id: groupId, user_id: userId, card })\n return `群昵称已更新:${userId} -> ${card}`\n },\n },\n searchGroupMember: {\n args: Schema.args({\n groupId: groupIdArg(),\n query: Schema.string().min(1).description('qqid, group card, or QQ nickname to search.'),\n mode: Schema.union(['auto', 'byName', 'byQQ']).required(false).description('Search mode. Defaults to auto.'),\n }),\n effect: 'read',\n async run(scope, args: { groupId?: string, query: string, mode?: 'auto' | 'byName' | 'byQQ' }) {\n const query = required(args.query, 'query')\n const mode = args.mode ?? 'auto'\n const groupId = groupOf(scope, args.groupId)\n const members = asMembers(await callOneBot(scope.bot, 'get_group_member_list', { group_id: numericId(groupId) }))\n const byQQ = mode === 'byQQ' || (mode === 'auto' && /^\\d+$/.test(query))\n const keyword = query.toLowerCase()\n const matches = members.filter((member) => byQQ\n ? pickText(member.user_id) === query\n : [pickText(member.card), pickText(member.nickname)].some((name) => name && name.toLowerCase().includes(keyword)))\n if (!matches.length) return '未找到匹配的群成员。'\n const header = matches.length > MAX_MEMBER_RESULTS\n ? `仅显示前 ${MAX_MEMBER_RESULTS} 条,共 ${matches.length} 条匹配:`\n : `找到 ${matches.length} 个匹配成员:`\n return [header, ...matches.slice(0, MAX_MEMBER_RESULTS).map((member, index) => formatMember(index + 1, member))].join('\\n')\n },\n },\n leaveGroup: {\n args: Schema.args({ groupId: groupIdArg() }),\n effect: 'write',\n async run(scope, args: { groupId?: string }) {\n const groupId = groupOf(scope, args.groupId)\n await callOneBot(scope.bot, 'set_group_leave', { group_id: groupId })\n return `已退出群 ${groupId}。`\n },\n },\n handleRequest: {\n args: Schema.args({\n requestType: Schema.union(['friend', 'group']).description('Request type: friend or group.'),\n flag: Schema.string().min(1).description('OneBot request flag from the event.'),\n approve: Schema.boolean().description('true to approve the request, false to reject it.'),\n subType: Schema.union(['add', 'invite']).required(false).description('Required for group requests: add or invite.'),\n remark: Schema.string().required(false).description('Optional friend remark when approving a friend request.'),\n userId: Schema.string().required(false).description('Requester user ID of a friend request; needed to apply the remark on SnowLuma.'),\n reason: Schema.string().required(false).description('Optional rejection reason for a group request.'),\n }),\n effect: 'write',\n async run(scope, args: { requestType: 'friend' | 'group', flag: string, approve: boolean, subType?: 'add' | 'invite', remark?: string, userId?: string, reason?: string }) {\n const flag = required(args.flag, 'request flag')\n const verb = args.approve ? '同意' : '拒绝'\n if (args.requestType === 'friend') {\n const remark = args.remark?.trim() ?? ''\n await callOneBot(scope.bot, 'set_friend_add_request', { flag, approve: args.approve, remark })\n const message = `已${verb}好友申请 ${flag}。`\n // SnowLuma 的 set_friend_add_request 不读 remark,同意后另用 set_friend_remark 设置。\n if (scope.protocol !== 'snowluma' || !args.approve || !remark) return message\n const userId = args.userId?.trim()\n if (!userId) return `${message}未提供申请人 userId,备注未设置。`\n await callOneBot(scope.bot, 'set_friend_remark', { user_id: numericId(userId), remark })\n return `${message}备注已设置为 ${remark}。`\n }\n if (!args.subType) throw new Error('subType is required for group requests; use add or invite.')\n await callOneBot(scope.bot, 'set_group_add_request', {\n flag,\n sub_type: args.subType,\n approve: args.approve,\n reason: args.reason?.trim() ?? '',\n })\n return `已${verb}${args.subType === 'invite' ? '群邀请' : '加群申请'} ${flag}。`\n },\n },\n setGroupKick: {\n args: Schema.args({\n groupId: groupIdArg(),\n userId: Schema.string().min(1).description('Target member user ID.'),\n }),\n effect: 'write',\n async run(scope, args: { groupId?: string, userId: string }) {\n const groupId = groupOf(scope, args.groupId)\n const userId = required(args.userId, 'userId')\n await callOneBot(scope.bot, 'set_group_kick', { group_id: groupId, user_id: userId })\n return `已将用户 ${userId} 移出群 ${groupId}。`\n },\n },\n setGroupSpecialTitle: {\n args: Schema.args({\n groupId: groupIdArg(),\n userId: Schema.string().min(1).description('Target member user ID.'),\n title: Schema.string().description('New special title. Use an empty string to clear it.'),\n }),\n effect: 'write',\n async run(scope, args: { groupId?: string, userId: string, title: string }) {\n const groupId = groupOf(scope, args.groupId)\n const userId = required(args.userId, 'userId')\n const title = args.title.trim()\n await callOneBot(scope.bot, 'set_group_special_title', { group_id: groupId, user_id: userId, special_title: title })\n return title ? `群专属头衔已更新:${userId} -> ${title}` : `群专属头衔已清除:${userId}`\n },\n },\n deleteMessage: {\n args: Schema.args({\n message_id: Schema.string().min(1).description('Specific message ID to delete.'),\n }),\n effect: 'write',\n async run(scope, args: { message_id: string }) {\n const messageId = required(args.message_id, 'message_id')\n await callOneBot(scope.bot, 'delete_msg', { message_id: numericId(messageId) })\n return `Message deleted by ID ${messageId}.`\n },\n },\n}\n\n/** 全部原生工具的定义,名称与描述统一取自 NATIVE_TOOLS。 */\nexport function nativeToolDefinitions(ctx: Context, config: Config, log: Logger): ToolDefinition[] {\n return NATIVE_TOOL_KEYS.map((key) => {\n const tool = NATIVE_TOOL_IMPLEMENTATIONS[key]\n const { toolName: name, description } = NATIVE_TOOLS[key]\n return {\n name,\n description,\n args: tool.args,\n traits: { effect: tool.effect },\n async run(args: unknown, call: ToolCallContext) {\n const location = call.location\n const bot = oneBotOf(ctx, location?.platform, location?.selfId)\n if (!bot) throw new Error('This tool only supports OneBot platform.')\n try {\n const result = await tool.run({ ctx, bot, guildId: location?.isDirect ? undefined : location?.guild, protocol: config.oneBotProtocol, agent: call.agent }, args)\n log.debug('%s: %s', name, result)\n return result\n } catch (error) {\n const message = error instanceof Error ? error.message : String(error)\n log.warn('%s failed: %s', name, message)\n throw new Error(`${name} failed: ${message}`)\n }\n },\n }\n })\n}\n\n/**\n * 注册原生工具。工具对所有会话全局可见(v2 的工具管理页因此能列出并开关它们),\n * 再给不在 OneBot 上的 agent 挂一层排除过滤。\n */\nexport function registerNativeTools(ctx: Context, config: Config, log: Logger) {\n const definitions = nativeToolDefinitions(ctx, config, log)\n for (const definition of definitions) ctx.tools.define(definition)\n const exclude = definitions.map((definition) => definition.name)\n log.info('native tools registered: %s', exclude.join(', '))\n ctx.inject(['agents'], (inner) => {\n const scope = (agent: Agent) => {\n if (agent.options.location?.platform !== 'onebot') inner.tools.filter({ exclude }, agent)\n }\n inner.on('chatluna/agent-open', ({ agent }) => scope(agent))\n for (const agent of inner.agents.list()) scope(agent)\n })\n}\n","import type { Context } from 'cordis'\nimport type { Bot } from '@satorijs/core'\nimport type { PromptEnvironment, VariableDeclaration, VariableProvider } from '@chatluna/prompt'\nimport type { Config, GroupInfoItem, MemberInfoItem, OneBotProtocol } from './config'\nimport { DEFAULT_GROUP_INFO_ITEMS } from './config'\nimport type { Logger } from './logger'\nimport {\n asRecord,\n asMembers,\n callOneBot,\n formatDate,\n formatDateTime,\n normalizeTimestamp,\n oneBotOf,\n pickFirst,\n pickText,\n type MemberInfo,\n} from './onebot'\n\nconst FALLBACK_MEMBER_ITEMS: MemberInfoItem[] = ['nickname', 'userId']\n\nconst SHUT_LIST_TIMEOUT_MS = 200\nconst SHUT_LIST_CACHE_TTL_MS = 15_000\n\nfunction translateGender(value: unknown): string {\n if (value === 'male') return '男'\n if (value === 'female') return '女'\n return ''\n}\n\nfunction normalizeRole(value: string): string {\n if (value === 'owner') return '群主'\n if (value === 'admin') return '管理员'\n return !value || value === 'member' ? '群员' : value\n}\n\n/** 会话所在的 Satori 机器人,任意平台;预览等没有真实机器人的环境里为 undefined。 */\nfunction botOf(ctx: Context, env: PromptEnvironment): Bot | undefined {\n return ctx.satori.bots.find((bot) => bot.platform === env.platform && bot.selfId === env.selfId)\n}\n\n/** 查询群成员信息;私聊、找不到机器人或查询失败时为 undefined,变量照常按已知信息渲染。 */\nasync function fetchMember(ctx: Context, env: PromptEnvironment, userId: string): Promise<MemberInfo | undefined> {\n const bot = botOf(ctx, env)\n if (!bot || !env.guildId) return undefined\n try {\n if (bot.platform === 'onebot') {\n return asRecord(await callOneBot(bot, 'get_group_member_info', {\n group_id: Number(env.guildId),\n user_id: Number(userId),\n no_cache: false,\n }))\n }\n const member = await bot.getGuildMember(env.guildId, userId)\n return {\n user_id: member.user?.id,\n card: pickText(member.nick, member.name),\n nickname: member.user?.name,\n role: member.roles?.[0]?.id,\n title: member.title,\n join_time: member.joinedAt,\n }\n } catch {\n return undefined\n }\n}\n\n/** 用户信息与机器人信息共用的渲染:按配置的信息项逐条输出,取不到的项省略。 */\nfunction renderMember(member: MemberInfo | undefined, items: MemberInfoItem[], id: string, fallbackName: unknown): string[] {\n const values: string[] = []\n for (const item of items) {\n switch (item) {\n case 'nickname': {\n const name = pickText(member?.card, member?.nickname, fallbackName, id)\n if (name) values.push(`name:${name}`)\n break\n }\n case 'userId':\n if (id) values.push(`id:${id}`)\n break\n case 'role':\n values.push(`群内身份:${normalizeRole(pickText(member?.role))}`)\n break\n case 'level': {\n const level = pickFirst(member?.level)\n if (level !== undefined) values.push(`群等级:${level}`)\n break\n }\n case 'title': {\n const title = pickFirst(member?.title)\n if (title !== undefined) values.push(`头衔:${title}`)\n break\n }\n case 'gender': {\n const gender = translateGender(member?.sex)\n if (gender) values.push(`性别:${gender}`)\n break\n }\n case 'age': {\n const age = Number(member?.age)\n if (Number.isFinite(age) && age > 0) values.push(`年龄:${age}`)\n break\n }\n case 'area': {\n const area = pickFirst(member?.area)\n if (area !== undefined) values.push(`地区:${area}`)\n break\n }\n case 'joinTime': {\n const joinTime = formatDate(normalizeTimestamp(member?.join_time))\n if (joinTime) values.push(`入群:${joinTime}`)\n break\n }\n case 'lastSentTime': {\n const lastSentTime = formatDateTime(normalizeTimestamp(member?.last_sent_time))\n if (lastSentTime) values.push(`活跃:${lastSentTime}`)\n break\n }\n }\n }\n return values\n}\n\nconst itemsOf = (items: MemberInfoItem[]) => items.length ? items : FALLBACK_MEMBER_ITEMS\n\nexport function userInfoProvider(ctx: Context, config: Config): VariableProvider {\n return async ({ env }) => {\n // 群聊里 userId 只来自本步最新的用户消息,工具调用后的后续步骤可能缺失,退回到消息发送者。\n const userId = (env.userId ?? env.message?.senderId)?.trim()\n if (!userId) return '未知用户'\n const member = await fetchMember(ctx, env, userId)\n const values = renderMember(member, itemsOf(config.userInfo.items), userId, env.userName ?? env.message?.senderName)\n return values.length ? values.join(', ') : `id:${userId}`\n }\n}\n\nexport function botInfoProvider(ctx: Context, config: Config): VariableProvider {\n return async ({ env }) => {\n const botId = env.selfId?.trim()\n if (!botId) return '未知机器人'\n const member = await fetchMember(ctx, env, botId)\n const values = renderMember(member, itemsOf(config.botInfo.items), botId, undefined)\n return values.length ? values.join(', ') : botId\n }\n}\n\ninterface GroupInfo {\n group_id?: unknown\n group_name?: unknown\n member_count?: unknown\n create_time?: unknown\n group_create_time?: unknown\n}\n\nasync function fetchRoles(bot: Bot, guildId: string, log: Logger): Promise<{ owners: string[], admins: string[] } | undefined> {\n try {\n const members = asMembers(await callOneBot(bot, 'get_group_member_list', { group_id: Number(guildId) }))\n if (!members.length) return undefined\n const owners: string[] = []\n const admins: string[] = []\n for (const member of members) {\n const role = member.role\n const userId = pickText(member.user_id)\n const name = pickText(member.card, member.nickname, userId)\n const label = userId ? `${name}(${userId})` : name\n if (role === 'owner') owners.push(label)\n else if (role === 'admin') admins.push(label)\n }\n return { owners, admins }\n } catch (error) {\n log.debug('group roles lookup failed: %s', String(error))\n return undefined\n }\n}\n\nfunction renderGroupInfo(group: GroupInfo, items: GroupInfoItem[], roles: { owners: string[], admins: string[] } | undefined): string {\n const id = pickText(group.group_id)\n const name = pickText(group.group_name)\n const memberCount = group.member_count\n const createTime = formatDate(normalizeTimestamp(group.create_time ?? group.group_create_time))\n const values: string[] = []\n for (const item of items) {\n switch (item) {\n case 'groupName':\n if (name) values.push(`群名:${name}`)\n break\n case 'groupId':\n if (id) values.push(`群号:${id}`)\n break\n case 'memberCount':\n if (memberCount !== undefined && memberCount !== null) values.push(`成员数量:${String(memberCount)}`)\n break\n case 'createTime':\n if (createTime) values.push(`创建时间:${createTime}`)\n break\n case 'ownerList':\n if (roles) values.push(`群主:${roles.owners.length ? roles.owners.join('、') : '无'}`)\n break\n case 'adminList':\n if (roles) values.push(`管理员:${roles.admins.length ? roles.admins.join('、') : '无'}`)\n break\n }\n }\n return values.length ? values.join('\\n') : id || name || '未能获取当前群信息。'\n}\n\nexport function groupInfoProvider(ctx: Context, config: Config, log: Logger): VariableProvider {\n return async ({ env }) => {\n if (!env.guildId) return ''\n if (env.platform !== 'onebot') return '当前平台暂不支持查询群信息。'\n const bot = oneBotOf(ctx, env.platform, env.selfId)\n if (!bot) return '暂无群信息。'\n const items = config.groupInfo.items.length ? config.groupInfo.items : DEFAULT_GROUP_INFO_ITEMS\n const group = await callOneBot(bot, 'get_group_info', { group_id: Number(env.guildId), no_cache: false })\n .then(asRecord, () => undefined)\n if (!group) return '未能获取当前群信息。'\n const roles = items.includes('ownerList') || items.includes('adminList') ? await fetchRoles(bot, env.guildId, log) : undefined\n return renderGroupInfo(group, items, roles)\n }\n}\n\nclass ShutListTimeout extends Error {}\n\nfunction withTimeout<T>(promise: Promise<T>, ms: number): Promise<T> {\n let timer: ReturnType<typeof setTimeout> | undefined\n return Promise.race([\n promise,\n new Promise<never>((_, reject) => {\n timer = setTimeout(() => reject(new ShutListTimeout('group shut list request timed out')), ms)\n }),\n ]).finally(() => clearTimeout(timer))\n}\n\nasync function fetchShutList(bot: Bot, guildId: string, protocol: OneBotProtocol): Promise<unknown[]> {\n const groupId = protocol === 'llbot' ? guildId : Number(guildId)\n const data = await withTimeout(callOneBot(bot, 'get_group_shut_list', { group_id: groupId }), SHUT_LIST_TIMEOUT_MS)\n return Array.isArray(data) ? data : []\n}\n\nfunction renderShutItem(item: unknown): string | undefined {\n const record = asRecord(item)\n if (!record) return undefined\n const userId = pickText(record.uin, record.user_id)\n if (!userId) return undefined\n const name = pickText(record.cardName, record.remark, record.nick, record.nickname, userId)\n const until = formatDateTime(normalizeTimestamp(pickFirst(record.shutUpTime, record.shut_up_time))) || '未知'\n return `id:${userId},name:${name},禁言截至:${until}`\n}\n\nexport function groupShutListProvider(ctx: Context, config: Config, log: Logger): VariableProvider {\n // 变量每一步都会重新渲染,同一个群的结果缓存 15 秒,避免每一步都请求一次。\n const cache = new Map<string, { value: string, expiresAt: number }>()\n return async ({ env }) => {\n if (!env.guildId) return ''\n if (env.platform !== 'onebot') return '当前平台暂不支持查询群禁言列表。'\n const bot = oneBotOf(ctx, env.platform, env.selfId)\n if (!bot) return '暂无群禁言列表。'\n const key = `${env.selfId}:${env.guildId}`\n const cached = cache.get(key)\n if (cached && cached.expiresAt > Date.now()) return cached.value\n try {\n const lines = (await fetchShutList(bot, env.guildId, config.oneBotProtocol)).flatMap((item) => renderShutItem(item) ?? [])\n const value = lines.length ? lines.join('\\n') : '当前群暂无禁言成员。'\n cache.set(key, { value, expiresAt: Date.now() + SHUT_LIST_CACHE_TTL_MS })\n return value\n } catch (error) {\n log.debug('group shut list failed: %s', String(error))\n // 部分实现在群里没有禁言成员时迟迟不响应,超时按空列表处理。\n return error instanceof ShutListTimeout ? '当前群暂无禁言成员。' : '获取群禁言列表失败。'\n }\n }\n}\n\n/** 按配置得到要注册的变量;变量名留空的不注册。 */\nexport function variableDefinitions(ctx: Context, config: Config, log: Logger): Array<[VariableDeclaration, VariableProvider]> {\n const entries: Array<[string, string, VariableProvider]> = [\n [config.userInfo.variableName, 'Current user info in this group', userInfoProvider(ctx, config)],\n [config.botInfo.variableName, 'Bot info in this group', botInfoProvider(ctx, config)],\n [config.groupInfo.variableName, 'Current group info', groupInfoProvider(ctx, config, log)],\n [config.groupShutList.variableName, 'Muted members of this group', groupShutListProvider(ctx, config, log)],\n ]\n // 都随群聊状态变化,属于可变变量,不能写进 system 区。\n return entries\n .filter(([name]) => name.trim())\n .map(([name, description, provider]) => [{ name: name.trim(), description, mutable: true }, provider])\n}\n\nexport function registerVariables(ctx: Context, config: Config, log: Logger) {\n for (const [declaration, provider] of variableDefinitions(ctx, config, log)) {\n try {\n ctx.prompt.variable(declaration, provider)\n } catch (error) {\n // 同名变量已被其他插件注册时只跳过这一个,其余变量与工具照常可用。\n log.warn('variable %s not registered: %s', declaration.name, error instanceof Error ? error.message : String(error))\n }\n }\n}\n","import type { Context } from 'cordis'\nimport type { Config } from './config'\nimport { createLogger } from './logger'\nimport { registerRequestNotices } from './requests'\nimport { registerNativeTools } from './tools'\nimport { registerVariables } from './variables'\n\nexport * from './config'\n\nexport const name = 'chatluna-onebot-toolbox'\n\n/** 过滤工具用的 agents、唤醒用的 chat 与读取会话图片用的 chatluna_storage 是可选服务。 */\nexport const inject = ['satori', 'tools', 'prompt']\n\nexport function apply(ctx: Context, config: Config) {\n const log = createLogger(ctx, config.debugLogging)\n registerVariables(ctx, config, log)\n registerNativeTools(ctx, config, log)\n registerRequestNotices(ctx, config, log)\n}\n"],"mappings":"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"}
|
package/lib/onebot.d.ts
CHANGED
|
@@ -1,15 +1,8 @@
|
|
|
1
1
|
import type { Context } from 'cordis';
|
|
2
2
|
import type { Bot } from '@satorijs/core';
|
|
3
|
-
/**
|
|
4
|
-
export interface OneBotInternal {
|
|
5
|
-
_request(action: string, params: Record<string, unknown>): Promise<unknown>;
|
|
6
|
-
}
|
|
7
|
-
/** 会话所在的 OneBot 机器人;不是 OneBot 会话或机器人已离线时为 undefined。 */
|
|
3
|
+
/** 会话所在的 OneBot 机器人;不是 OneBot 会话或找不到机器人时为 undefined。 */
|
|
8
4
|
export declare function oneBotOf(ctx: Context, platform: string | undefined, selfId: string | undefined): Bot | undefined;
|
|
9
|
-
/**
|
|
10
|
-
* 调用 OneBot action 并返回 `data`。OneBot 的业务错误以正常 resolve 的响应包返回,这里按 status 与 retcode
|
|
11
|
-
* 判定失败并抛出,错误信息取实现给出的 wording/message。
|
|
12
|
-
*/
|
|
5
|
+
/** 调用 OneBot action 并返回 data,按响应包的 status 与 retcode 判定业务失败。 */
|
|
13
6
|
export declare function callOneBot(bot: Bot, action: string, params: Record<string, unknown>): Promise<unknown>;
|
|
14
7
|
/**
|
|
15
8
|
* 整数形式的 ID 转成数字,其余原样保留;OneBot 实现普遍要求数字形式的群号、QQ 号与消息 ID。
|
|
@@ -19,60 +12,24 @@ export declare function numericId(value: string): string | number;
|
|
|
19
12
|
/** 取第一个非空值(去掉首尾空白后不为空字符串),保留原始类型。 */
|
|
20
13
|
export declare function pickFirst(...values: unknown[]): string | number | undefined;
|
|
21
14
|
export declare function pickText(...values: unknown[]): string;
|
|
22
|
-
/** OneBot
|
|
15
|
+
/** OneBot 使用秒,Satori 使用毫秒,统一成毫秒;空值与 0 视为没有。 */
|
|
23
16
|
export declare function normalizeTimestamp(raw: unknown): number | undefined;
|
|
24
17
|
export declare function formatDate(value: number | undefined): string;
|
|
25
18
|
export declare function formatDateTime(value: number | undefined): string;
|
|
26
|
-
/**
|
|
19
|
+
/** OneBot 群成员信息;其他平台的 Satori 成员信息在查询入口显式转换。 */
|
|
27
20
|
export interface MemberInfo {
|
|
28
21
|
user_id?: unknown;
|
|
29
|
-
userId?: unknown;
|
|
30
|
-
id?: unknown;
|
|
31
|
-
qq?: unknown;
|
|
32
|
-
uid?: unknown;
|
|
33
22
|
role?: unknown;
|
|
34
|
-
roleName?: unknown;
|
|
35
|
-
permission?: unknown;
|
|
36
|
-
identity?: unknown;
|
|
37
|
-
roles?: unknown;
|
|
38
23
|
card?: unknown;
|
|
39
|
-
remark?: unknown;
|
|
40
|
-
displayName?: unknown;
|
|
41
|
-
nick?: unknown;
|
|
42
24
|
nickname?: unknown;
|
|
43
|
-
name?: unknown;
|
|
44
25
|
level?: unknown;
|
|
45
|
-
levelName?: unknown;
|
|
46
|
-
level_name?: unknown;
|
|
47
|
-
level_info?: {
|
|
48
|
-
current_level?: unknown;
|
|
49
|
-
};
|
|
50
26
|
title?: unknown;
|
|
51
|
-
specialTitle?: unknown;
|
|
52
|
-
special_title?: unknown;
|
|
53
27
|
sex?: unknown;
|
|
54
|
-
gender?: unknown;
|
|
55
28
|
age?: unknown;
|
|
56
29
|
area?: unknown;
|
|
57
|
-
region?: unknown;
|
|
58
|
-
location?: unknown;
|
|
59
30
|
join_time?: unknown;
|
|
60
|
-
joined_at?: unknown;
|
|
61
|
-
joinTime?: unknown;
|
|
62
|
-
joinedAt?: unknown;
|
|
63
31
|
last_sent_time?: unknown;
|
|
64
|
-
lastSentTime?: unknown;
|
|
65
|
-
lastSpeakTimestamp?: unknown;
|
|
66
|
-
user?: {
|
|
67
|
-
nick?: unknown;
|
|
68
|
-
nickname?: unknown;
|
|
69
|
-
name?: unknown;
|
|
70
|
-
};
|
|
71
32
|
}
|
|
72
33
|
export declare function asMembers(value: unknown): MemberInfo[];
|
|
73
|
-
export declare function
|
|
74
|
-
export declare function memberUserId(member: MemberInfo): string;
|
|
75
|
-
export declare function memberCard(member: MemberInfo): string;
|
|
76
|
-
export declare function memberNickname(member: MemberInfo): string;
|
|
77
|
-
export declare function memberRole(member: MemberInfo): string;
|
|
34
|
+
export declare function asRecord(value: unknown): Record<string, unknown> | undefined;
|
|
78
35
|
//# sourceMappingURL=onebot.d.ts.map
|
package/lib/onebot.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"onebot.d.ts","sourceRoot":"","sources":["../src/onebot.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,QAAQ,CAAA;AACrC,OAAO,KAAK,EAAE,GAAG,EAAE,MAAM,gBAAgB,CAAA;
|
|
1
|
+
{"version":3,"file":"onebot.d.ts","sourceRoot":"","sources":["../src/onebot.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,QAAQ,CAAA;AACrC,OAAO,KAAK,EAAE,GAAG,EAAE,MAAM,gBAAgB,CAAA;AAUzC,wDAAwD;AACxD,wBAAgB,QAAQ,CAAC,GAAG,EAAE,OAAO,EAAE,QAAQ,EAAE,MAAM,GAAG,SAAS,EAAE,MAAM,EAAE,MAAM,GAAG,SAAS,GAAG,GAAG,GAAG,SAAS,CAGhH;AAED,+DAA+D;AAC/D,wBAAsB,UAAU,CAAC,GAAG,EAAE,GAAG,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GAAG,OAAO,CAAC,OAAO,CAAC,CAS5G;AAED;;;GAGG;AACH,wBAAgB,SAAS,CAAC,KAAK,EAAE,MAAM,GAAG,MAAM,GAAG,MAAM,CAExD;AAED,qCAAqC;AACrC,wBAAgB,SAAS,CAAC,GAAG,MAAM,EAAE,OAAO,EAAE,GAAG,MAAM,GAAG,MAAM,GAAG,SAAS,CAO3E;AAED,wBAAgB,QAAQ,CAAC,GAAG,MAAM,EAAE,OAAO,EAAE,GAAG,MAAM,CAErD;AAED,+CAA+C;AAC/C,wBAAgB,kBAAkB,CAAC,GAAG,EAAE,OAAO,GAAG,MAAM,GAAG,SAAS,CAKnE;AAID,wBAAgB,UAAU,CAAC,KAAK,EAAE,MAAM,GAAG,SAAS,GAAG,MAAM,CAK5D;AAED,wBAAgB,cAAc,CAAC,KAAK,EAAE,MAAM,GAAG,SAAS,GAAG,MAAM,CAKhE;AAED,+CAA+C;AAC/C,MAAM,WAAW,UAAU;IACzB,OAAO,CAAC,EAAE,OAAO,CAAA;IACjB,IAAI,CAAC,EAAE,OAAO,CAAA;IACd,IAAI,CAAC,EAAE,OAAO,CAAA;IACd,QAAQ,CAAC,EAAE,OAAO,CAAA;IAClB,KAAK,CAAC,EAAE,OAAO,CAAA;IACf,KAAK,CAAC,EAAE,OAAO,CAAA;IACf,GAAG,CAAC,EAAE,OAAO,CAAA;IACb,GAAG,CAAC,EAAE,OAAO,CAAA;IACb,IAAI,CAAC,EAAE,OAAO,CAAA;IACd,SAAS,CAAC,EAAE,OAAO,CAAA;IACnB,cAAc,CAAC,EAAE,OAAO,CAAA;CACzB;AAED,wBAAgB,SAAS,CAAC,KAAK,EAAE,OAAO,GAAG,UAAU,EAAE,CAEtD;AAED,wBAAgB,QAAQ,CAAC,KAAK,EAAE,OAAO,GAAG,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GAAG,SAAS,CAE5E"}
|
package/lib/tools.d.ts
CHANGED
|
@@ -22,11 +22,11 @@ interface NativeTool<A> {
|
|
|
22
22
|
export declare const NATIVE_TOOL_IMPLEMENTATIONS: {
|
|
23
23
|
[K in NativeToolKey]: NativeTool<any>;
|
|
24
24
|
};
|
|
25
|
-
/**
|
|
25
|
+
/** 全部原生工具的定义,名称与描述统一取自 NATIVE_TOOLS。 */
|
|
26
26
|
export declare function nativeToolDefinitions(ctx: Context, config: Config, log: Logger): ToolDefinition[];
|
|
27
27
|
/**
|
|
28
28
|
* 注册原生工具。工具对所有会话全局可见(v2 的工具管理页因此能列出并开关它们),
|
|
29
|
-
* 再给不在 OneBot 上的 agent
|
|
29
|
+
* 再给不在 OneBot 上的 agent 挂一层排除过滤。
|
|
30
30
|
*/
|
|
31
31
|
export declare function registerNativeTools(ctx: Context, config: Config, log: Logger): void;
|
|
32
32
|
export {};
|
package/lib/tools.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"tools.d.ts","sourceRoot":"","sources":["../src/tools.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,QAAQ,CAAA;AACrC,OAAO,KAAK,EAAE,GAAG,EAAE,MAAM,gBAAgB,CAAA;AACzC,OAAO,EAAE,MAAM,EAAE,MAAM,kBAAkB,CAAA;AACzC,OAAO,KAAK,EAAmB,cAAc,EAAE,MAAM,gBAAgB,CAAA;AAKrE,OAAO,KAAK,EAAE,MAAM,EAAE,aAAa,EAAE,cAAc,EAAE,MAAM,UAAU,CAAA;AAErE,OAAO,KAAK,EAAE,MAAM,EAAE,MAAM,UAAU,CAAA;
|
|
1
|
+
{"version":3,"file":"tools.d.ts","sourceRoot":"","sources":["../src/tools.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,QAAQ,CAAA;AACrC,OAAO,KAAK,EAAE,GAAG,EAAE,MAAM,gBAAgB,CAAA;AACzC,OAAO,EAAE,MAAM,EAAE,MAAM,kBAAkB,CAAA;AACzC,OAAO,KAAK,EAAmB,cAAc,EAAE,MAAM,gBAAgB,CAAA;AAKrE,OAAO,KAAK,EAAE,MAAM,EAAE,aAAa,EAAE,cAAc,EAAE,MAAM,UAAU,CAAA;AAErE,OAAO,KAAK,EAAE,MAAM,EAAE,MAAM,UAAU,CAAA;AAUtC,4BAA4B;AAC5B,MAAM,WAAW,SAAS;IACxB,GAAG,EAAE,OAAO,CAAA;IACZ,GAAG,EAAE,GAAG,CAAA;IACR,2BAA2B;IAC3B,OAAO,CAAC,EAAE,MAAM,CAAA;IAChB,QAAQ,EAAE,cAAc,CAAA;IACxB,4DAA4D;IAC5D,KAAK,CAAC,EAAE,MAAM,CAAA;CACf;AAED,UAAU,UAAU,CAAC,CAAC;IACpB,IAAI,EAAE,MAAM,CAAC,GAAG,EAAE,CAAC,CAAC,CAAA;IACpB,MAAM,EAAE,MAAM,GAAG,OAAO,CAAA;IACxB,GAAG,CAAC,KAAK,EAAE,SAAS,EAAE,IAAI,EAAE,CAAC,GAAG,OAAO,CAAC,MAAM,CAAC,CAAA;CAChD;AA0FD,eAAO,MAAM,2BAA2B,EAAE;KAAG,CAAC,IAAI,aAAa,GAAG,UAAU,CAAC,GAAG,CAAC;CAuJhF,CAAA;AAED,wCAAwC;AACxC,wBAAgB,qBAAqB,CAAC,GAAG,EAAE,OAAO,EAAE,MAAM,EAAE,MAAM,EAAE,GAAG,EAAE,MAAM,GAAG,cAAc,EAAE,CAyBjG;AAED;;;GAGG;AACH,wBAAgB,mBAAmB,CAAC,GAAG,EAAE,OAAO,EAAE,MAAM,EAAE,MAAM,EAAE,GAAG,EAAE,MAAM,QAY5E"}
|
package/lib/variables.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"variables.d.ts","sourceRoot":"","sources":["../src/variables.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,QAAQ,CAAA;AAErC,OAAO,KAAK,EAAqB,mBAAmB,EAAE,gBAAgB,EAAE,MAAM,kBAAkB,CAAA;AAChG,OAAO,KAAK,EAAE,MAAM,EAAiD,MAAM,UAAU,CAAA;AAErF,OAAO,KAAK,EAAE,MAAM,EAAE,MAAM,UAAU,CAAA;
|
|
1
|
+
{"version":3,"file":"variables.d.ts","sourceRoot":"","sources":["../src/variables.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,QAAQ,CAAA;AAErC,OAAO,KAAK,EAAqB,mBAAmB,EAAE,gBAAgB,EAAE,MAAM,kBAAkB,CAAA;AAChG,OAAO,KAAK,EAAE,MAAM,EAAiD,MAAM,UAAU,CAAA;AAErF,OAAO,KAAK,EAAE,MAAM,EAAE,MAAM,UAAU,CAAA;AAwHtC,wBAAgB,gBAAgB,CAAC,GAAG,EAAE,OAAO,EAAE,MAAM,EAAE,MAAM,GAAG,gBAAgB,CAS/E;AAED,wBAAgB,eAAe,CAAC,GAAG,EAAE,OAAO,EAAE,MAAM,EAAE,MAAM,GAAG,gBAAgB,CAQ9E;AA8DD,wBAAgB,iBAAiB,CAAC,GAAG,EAAE,OAAO,EAAE,MAAM,EAAE,MAAM,EAAE,GAAG,EAAE,MAAM,GAAG,gBAAgB,CAa7F;AA8BD,wBAAgB,qBAAqB,CAAC,GAAG,EAAE,OAAO,EAAE,MAAM,EAAE,MAAM,EAAE,GAAG,EAAE,MAAM,GAAG,gBAAgB,CAsBjG;AAED,6BAA6B;AAC7B,wBAAgB,mBAAmB,CAAC,GAAG,EAAE,OAAO,EAAE,MAAM,EAAE,MAAM,EAAE,GAAG,EAAE,MAAM,GAAG,KAAK,CAAC,CAAC,mBAAmB,EAAE,gBAAgB,CAAC,CAAC,CAW7H;AAED,wBAAgB,iBAAiB,CAAC,GAAG,EAAE,OAAO,EAAE,MAAM,EAAE,MAAM,EAAE,GAAG,EAAE,MAAM,QAS1E"}
|