@zhin.js/adapter-icqq 8.0.13 → 9.0.0

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.
@@ -0,0 +1,132 @@
1
+ import type { LoginAssist, LoginAssistType, PendingLoginTaskPayload } from '@zhin.js/core';
2
+ import { formatCompact } from '@zhin.js/logger';
3
+
4
+ type IcqqLoginClient = {
5
+ login(password?: string): Promise<unknown>;
6
+ submitSlider(ticket: string): Promise<unknown>;
7
+ sendSmsCode(): Promise<unknown>;
8
+ submitSmsCode(code: string): Promise<unknown>;
9
+ };
10
+
11
+ type Logger = {
12
+ info(message: string): void;
13
+ warn(message: string): void;
14
+ };
15
+
16
+ /**
17
+ * Drive ICQQ login challenges through LoginAssist (refresh-safe pending tasks).
18
+ * Mirrors the classic stdin flow: qrcode/auth → login(); slider → submitSlider;
19
+ * device → sendSmsCode then submitSmsCode.
20
+ */
21
+ export async function runIcqqLoginAssistStep(input: {
22
+ assist: LoginAssist;
23
+ client: IcqqLoginClient;
24
+ adapter: string;
25
+ endpointKey: string;
26
+ owner: object;
27
+ logger: Logger;
28
+ step: LoginAssistType;
29
+ event: unknown;
30
+ }): Promise<void> {
31
+ const { assist, client, adapter, endpointKey, owner, logger, step, event } = input;
32
+ assist.cancelOwned(owner, `replaced_by_${step}`);
33
+
34
+ const payload = buildPayload(step, event);
35
+ logger.info(formatCompact({
36
+ op: 'login_assist_pending',
37
+ endpoint: endpointKey,
38
+ type: step,
39
+ message: payload.message,
40
+ ...(typeof payload.url === 'string' ? { url: payload.url } : {}),
41
+ }));
42
+
43
+ try {
44
+ if (step === 'device') {
45
+ try {
46
+ await client.sendSmsCode();
47
+ } catch (error) {
48
+ logger.warn(formatCompact({
49
+ op: 'login_assist_send_sms_failed',
50
+ endpoint: endpointKey,
51
+ error: error instanceof Error ? error.message : String(error),
52
+ }));
53
+ }
54
+ }
55
+
56
+ const value = await assist.waitForInput(adapter, endpointKey, step, payload, { owner });
57
+ const text = normalizeSubmitValue(value);
58
+
59
+ switch (step) {
60
+ case 'qrcode':
61
+ case 'auth':
62
+ await client.login();
63
+ break;
64
+ case 'slider':
65
+ await client.submitSlider(text);
66
+ break;
67
+ case 'device':
68
+ case 'sms':
69
+ await client.submitSmsCode(text);
70
+ break;
71
+ default:
72
+ await client.login();
73
+ break;
74
+ }
75
+ } catch (error) {
76
+ logger.warn(formatCompact({
77
+ op: 'login_assist_failed',
78
+ endpoint: endpointKey,
79
+ type: step,
80
+ error: error instanceof Error ? error.message : String(error),
81
+ }));
82
+ }
83
+ }
84
+
85
+ function buildPayload(step: LoginAssistType, event: unknown): PendingLoginTaskPayload {
86
+ const raw = event && typeof event === 'object' ? event as Record<string, unknown> : {};
87
+ switch (step) {
88
+ case 'qrcode':
89
+ return {
90
+ message: '扫码登录:请在手机 QQ 扫描二维码,完成后在 Console / 终端确认继续',
91
+ image: bufferToDataUrl(raw.image),
92
+ };
93
+ case 'slider':
94
+ return {
95
+ message: '滑动验证:打开链接完成滑块,将返回的 ticket(可含 randstr)提交继续',
96
+ url: typeof raw.url === 'string' ? raw.url : undefined,
97
+ };
98
+ case 'device':
99
+ return {
100
+ message: '设备锁:已请求短信验证码,请提交短信验证码继续',
101
+ url: typeof raw.url === 'string' ? raw.url : undefined,
102
+ phone: raw.phone,
103
+ };
104
+ case 'auth':
105
+ return {
106
+ message: '身份验证:完成后在 Console / 终端确认继续',
107
+ url: typeof raw.url === 'string' ? raw.url : undefined,
108
+ device: raw.device,
109
+ };
110
+ default:
111
+ return { message: `登录辅助(${step})` };
112
+ }
113
+ }
114
+
115
+ function normalizeSubmitValue(value: string | Record<string, unknown>): string {
116
+ if (typeof value === 'string') return value.trim();
117
+ if (typeof value.ticket === 'string') return value.ticket.trim();
118
+ if (typeof value.code === 'string') return value.code.trim();
119
+ if (typeof value.value === 'string') return value.value.trim();
120
+ return JSON.stringify(value);
121
+ }
122
+
123
+ function bufferToDataUrl(image: unknown): string | undefined {
124
+ if (Buffer.isBuffer(image)) {
125
+ return `data:image/png;base64,${image.toString('base64')}`;
126
+ }
127
+ if (image instanceof Uint8Array) {
128
+ return `data:image/png;base64,${Buffer.from(image).toString('base64')}`;
129
+ }
130
+ if (typeof image === 'string' && image.trim()) return image.trim();
131
+ return undefined;
132
+ }
package/src/index.ts CHANGED
@@ -15,7 +15,6 @@ export {
15
15
  export {
16
16
  IcqqEndpoint,
17
17
  type IcqqEndpointOptions,
18
- type IcqqInboxHooks,
19
18
  } from './endpoint.js';
20
19
 
21
20
  export * from './types.js';
@@ -46,18 +46,26 @@ export function resolveIcqqOutboundMediaMode(
46
46
  /**
47
47
  * file 模式:将 canonical MediaRef(`data.media`,kind=base64)物化为本机路径,
48
48
  * 并把 media 改写为 path 引用(icqq 客户端直接读盘)。
49
- * base64 模式:不改动(由 toCqString/formatOutboundBody 生成 base64://)。
49
+ * base64 模式:不改动(由 native Sendable projection 生成 base64:// file)。
50
50
  */
51
- export function materializeOutboundBase64(
51
+ export interface PreparedIcqqOutboundMedia {
52
+ readonly content: SendContent;
53
+ dispose(): void;
54
+ }
55
+
56
+ export function prepareIcqqOutboundMedia(
52
57
  content: SendContent,
53
58
  mode: IcqqOutboundMediaMode = "file",
54
- ): SendContent {
55
- if (mode === "base64") return content;
59
+ ): PreparedIcqqOutboundMedia {
60
+ if (mode === "base64") return { content, dispose() {} };
56
61
 
62
+ const createdFiles: string[] = [];
63
+ const inputWasArray = Array.isArray(content);
57
64
  const segments = Array.isArray(content) ? content : [content];
58
- return segments.map((seg) => {
65
+ const materialized = segments.map((seg) => {
59
66
  if (typeof seg === "string") return seg;
60
67
  const { type, data } = seg as MessageSegment;
68
+ if (!data || typeof data !== "object") return seg;
61
69
  const d = data as Record<string, unknown>;
62
70
  const media = d.media;
63
71
  if (!isMediaRef(media) || media.kind !== "base64") return seg;
@@ -73,6 +81,7 @@ export function materializeOutboundBase64(
73
81
  mime || (type === "image" ? "image/jpeg" : type === "video" ? "video/mp4" : "audio/mpeg"),
74
82
  type === "record" || type === "audio" ? "audio" : type,
75
83
  );
84
+ createdFiles.push(filePath);
76
85
  return {
77
86
  type,
78
87
  data: {
@@ -85,4 +94,10 @@ export function materializeOutboundBase64(
85
94
  },
86
95
  } as MessageSegment;
87
96
  });
97
+ return {
98
+ content: inputWasArray ? materialized : materialized[0]!,
99
+ dispose() {
100
+ for (const filePath of createdFiles) fs.rmSync(filePath, { force: true });
101
+ },
102
+ };
88
103
  }
package/src/protocol.ts CHANGED
@@ -1,7 +1,8 @@
1
1
  import { pickCredential } from 'zhin.js/adapter';
2
+ import type { MessageElem, Sendable } from '@icqqjs/icqq';
2
3
  import type { ConversationRef } from '@zhin.js/im-contract';
3
4
  import type { MessageSegment } from "zhin.js";
4
- import { resolveCqMediaArg } from "./cq-message.js";
5
+ import { resolveIcqqMediaFile } from "./cq-message.js";
5
6
 
6
7
  export type ActionResult = {
7
8
  id: string;
@@ -301,6 +302,7 @@ export interface IcqqInboundMessage {
301
302
  readonly sender: { readonly id: string; readonly name?: string };
302
303
  readonly channelType: 'private' | 'group' | 'channel';
303
304
  readonly metadata?: Record<string, unknown>;
305
+ readonly replyTo?: { readonly id: string };
304
306
  }
305
307
 
306
308
  export type ParsedIcqqSendTarget =
@@ -404,7 +406,20 @@ export function icqqOutboundTarget(conversation: ConversationRef): ParsedIcqqSen
404
406
  return { kind: 'channel', guildId, channelId: conversation.id };
405
407
  }
406
408
 
407
- export function formatOutboundBody(payload: unknown): string {
409
+ const ICQQ_STANDALONE_ELEMENTS = new Set([
410
+ 'record',
411
+ 'video',
412
+ 'file',
413
+ 'json',
414
+ 'long_msg',
415
+ ] as const);
416
+
417
+ /**
418
+ * Project canonical Zhin segments directly into the native ICQQ Sendable
419
+ * algebra. ICQQ's direct client does not parse Zhin's historical `[image:x]`
420
+ * notation; passing that notation would send it as literal text.
421
+ */
422
+ export function formatOutboundBody(payload: unknown): Sendable {
408
423
  if (typeof payload === 'string') {
409
424
  const text = payload.trim();
410
425
  return text || '\u200b';
@@ -422,39 +437,84 @@ export function formatOutboundBody(payload: unknown): string {
422
437
  const text = String(payload).trim();
423
438
  return text || '\u200b';
424
439
  }
425
- const joined = payload.map((seg) => {
440
+ const elements = payload.flatMap<string | MessageElem>((seg) => {
426
441
  if (typeof seg === 'string') return seg;
427
442
  const item = seg as IcqqWireSegment;
428
443
  switch (item.type) {
429
444
  case 'text':
430
445
  return String(item.data?.text ?? '');
431
446
  case 'at':
432
- case 'mention':
433
- return `[at:${item.data?.qq ?? item.data?.id ?? item.data?.target ?? ''}]`;
447
+ case 'mention': {
448
+ const target = item.data?.qq ?? item.data?.id ?? item.data?.target;
449
+ if (target === 'all') return { type: 'at', qq: 'all' };
450
+ const qq = Number(target);
451
+ if (!Number.isSafeInteger(qq) || qq <= 0) {
452
+ throw new TypeError(`Invalid ICQQ mention target: ${String(target ?? '')}`);
453
+ }
454
+ return { type: 'at', qq };
455
+ }
434
456
  case 'image': {
435
- const arg = resolveCqMediaArg('image', item.data);
436
- return arg ? `[image:${arg}]` : '';
457
+ const arg = resolveIcqqMediaFile(item.data);
458
+ if (!arg) throw new TypeError('ICQQ image segment requires canonical data.media');
459
+ return { type: 'image', file: arg };
460
+ }
461
+ case 'face': {
462
+ const id = Number(item.data?.id);
463
+ if (!Number.isSafeInteger(id) || id < 0) {
464
+ throw new TypeError(`Invalid ICQQ face id: ${String(item.data?.id ?? '')}`);
465
+ }
466
+ return { type: 'face', id };
467
+ }
468
+ case 'reply': {
469
+ const id = String(item.data?.message_id ?? item.data?.id ?? '').trim();
470
+ if (!id) throw new TypeError('ICQQ reply segment requires message_id');
471
+ return { type: 'reply', id };
437
472
  }
438
- case 'face':
439
- return `[face:${item.data?.id ?? ''}]`;
440
- case 'reply':
441
- return `[reply:${item.data?.message_id ?? item.data?.id ?? ''}]`;
442
473
  case 'record':
443
474
  case 'audio': {
444
- const arg = resolveCqMediaArg('record', item.data);
445
- return arg ? `[record:${arg}]` : '';
475
+ const arg = resolveIcqqMediaFile(item.data);
476
+ if (!arg) throw new TypeError('ICQQ audio segment requires canonical data.media');
477
+ return { type: 'record', file: arg };
446
478
  }
447
479
  case 'video': {
448
- const arg = resolveCqMediaArg('video', item.data);
449
- return arg ? `[video:${arg}]` : '';
480
+ const arg = resolveIcqqMediaFile(item.data);
481
+ if (!arg) throw new TypeError('ICQQ video segment requires canonical data.media');
482
+ return { type: 'video', file: arg };
483
+ }
484
+ case 'file': {
485
+ const arg = resolveIcqqMediaFile(item.data);
486
+ if (!arg) throw new TypeError('ICQQ file segment requires canonical data.media');
487
+ const name = String(item.data?.name ?? '').trim();
488
+ return { type: 'file', file: arg, ...(name ? { name } : {}) };
489
+ }
490
+ case 'dice':
491
+ return { type: 'dice' };
492
+ case 'rps':
493
+ return { type: 'rps' };
494
+ case 'forward': {
495
+ const resid = String(item.data?.forward_id ?? item.data?.resid ?? item.data?.id ?? '').trim();
496
+ if (!resid) throw new TypeError('ICQQ forward segment requires forward_id');
497
+ return { type: 'long_msg', resid };
450
498
  }
451
499
  case 'share':
452
500
  return formatMusicShareJson(item.data);
453
- default:
454
- return String(item.data?.text ?? '');
501
+ default: {
502
+ const text = item.data?.text;
503
+ if (typeof text === 'string' && text) return text;
504
+ throw new TypeError(`Unsupported ICQQ outbound segment: ${item.type}`);
505
+ }
455
506
  }
456
- }).join('').trim();
457
- return joined || '\u200b';
507
+ }).filter((element) => typeof element !== 'string' || element.length > 0);
508
+
509
+ if (elements.length === 0) return '\u200b';
510
+ const standalone = elements.find(
511
+ (element): element is MessageElem =>
512
+ typeof element !== 'string' && ICQQ_STANDALONE_ELEMENTS.has(element.type as never),
513
+ );
514
+ if (standalone && elements.length !== 1) {
515
+ throw new TypeError(`ICQQ ${standalone.type} element must be sent as a standalone message`);
516
+ }
517
+ return elements.length === 1 ? elements[0]! : elements;
458
518
  }
459
519
 
460
520
  export function formatInboundContent(rawMessage: string): string {
@@ -468,8 +528,8 @@ function isWireSegment(value: unknown): value is IcqqWireSegment {
468
528
  && typeof (value as { type?: unknown }).type === 'string';
469
529
  }
470
530
 
471
- function formatMusicShareJson(data: Record<string, unknown> | undefined): string {
472
- if (!data) return '';
531
+ function formatMusicShareJson(data: Record<string, unknown> | undefined): MessageElem {
532
+ if (!data) throw new TypeError('ICQQ share segment requires data');
473
533
  const config = data.config as {
474
534
  appid?: number;
475
535
  package?: string;
@@ -511,5 +571,5 @@ function formatMusicShareJson(data: Record<string, unknown> | undefined): string
511
571
  },
512
572
  },
513
573
  };
514
- return `[json:${JSON.stringify(json)}]`;
574
+ return { type: 'json', data: json };
515
575
  }
package/tools/announce.js CHANGED
@@ -10,7 +10,7 @@ export default defineAgentTool({
10
10
  content: z.string().describe('公告内容'),
11
11
  }),
12
12
  platforms: ['icqq'],
13
- permissions: ['platform(icqq,scene_admin)'],
13
+ permissions: ['role(master,admin,trusted,owner)'],
14
14
  async execute({ endpoint_id, group_id, content }) {
15
15
  const endpoint = getIcqqAgentDeps().getEndpoint(endpoint_id);
16
16
  await endpoint.sendGroupNotice(group_id, content);
package/tools/announce.ts CHANGED
@@ -9,7 +9,7 @@ export default defineAgentTool<{ endpoint_id: string; group_id: number; content:
9
9
  content: z.string().describe('公告内容'),
10
10
  }),
11
11
  platforms: ['icqq'],
12
- permissions: ['platform(icqq,scene_admin)'],
12
+ permissions: ['role(master,admin,trusted,owner)'],
13
13
  async execute({ endpoint_id, group_id, content }: { endpoint_id: string; group_id: number; content: string }) {
14
14
  const endpoint = getIcqqAgentDeps().getEndpoint(endpoint_id);
15
15
  await endpoint.sendGroupNotice(group_id, content);
package/tools/essence.js CHANGED
@@ -10,7 +10,7 @@ export default defineAgentTool({
10
10
  action: z.enum(['add', 'remove']).describe('add=设为精华, remove=移除精华'),
11
11
  }),
12
12
  platforms: ['icqq'],
13
- permissions: ['platform(icqq,scene_admin)'],
13
+ approval: 'always',
14
14
  async execute({ endpoint_id, message_id, action }) {
15
15
  const endpoint = getIcqqAgentDeps().getEndpoint(endpoint_id);
16
16
  if (action === 'add') {
package/tools/essence.ts CHANGED
@@ -9,7 +9,7 @@ export default defineAgentTool<{ endpoint_id: string; message_id: string; action
9
9
  action: z.enum(['add', 'remove']).describe('add=设为精华, remove=移除精华'),
10
10
  }),
11
11
  platforms: ['icqq'],
12
- permissions: ['platform(icqq,scene_admin)'],
12
+ approval: 'always',
13
13
  async execute({ endpoint_id, message_id, action }: { endpoint_id: string; message_id: string; action: 'add' | 'remove' }) {
14
14
  const endpoint = getIcqqAgentDeps().getEndpoint(endpoint_id);
15
15
  if (action === 'add') {
@@ -11,10 +11,11 @@ export default defineAgentTool({
11
11
  approval: 'never',
12
12
  async execute({ endpoint_id }) {
13
13
  const endpoint = getIcqqAgentDeps().getEndpoint(endpoint_id);
14
- const fl = endpoint.fl;
15
- const friends = Array.from(fl.values()).map((f) => ({
16
- user_id: f.user_id, nickname: f.nickname, remark: f.remark,
14
+ const friends = Array.from(endpoint.fl.values()).map((friend) => ({
15
+ user_id: friend.user_id,
16
+ nickname: friend.nickname,
17
+ remark: friend.remark,
17
18
  }));
18
- return { friends: friends.slice(0, 50), count: fl.size };
19
+ return { friends: friends.slice(0, 50), count: endpoint.fl.size };
19
20
  },
20
21
  });
@@ -1,6 +1,7 @@
1
1
  import { defineAgentTool } from '@zhin.js/tool';
2
2
  import { z } from 'zod';
3
3
  import { getIcqqAgentDeps } from '../src/icqq-agent-deps.js';
4
+
4
5
  export default defineAgentTool<{ endpoint_id: string }>({
5
6
  description: '获取 QQ 好友列表',
6
7
  inputSchema: z.object({
@@ -8,12 +9,13 @@ export default defineAgentTool<{ endpoint_id: string }>({
8
9
  }),
9
10
  platforms: ['icqq'],
10
11
  approval: 'never',
11
- async execute({ endpoint_id }: { endpoint_id: string }) {
12
+ async execute({ endpoint_id }) {
12
13
  const endpoint = getIcqqAgentDeps().getEndpoint(endpoint_id);
13
- const fl = endpoint.fl;
14
- const friends = Array.from(fl.values()).map((f) => ({
15
- user_id: f.user_id, nickname: f.nickname, remark: f.remark,
14
+ const friends = Array.from(endpoint.fl.values()).map((friend) => ({
15
+ user_id: friend.user_id,
16
+ nickname: friend.nickname,
17
+ remark: friend.remark,
16
18
  }));
17
- return { friends: friends.slice(0, 50), count: fl.size };
19
+ return { friends: friends.slice(0, 50), count: endpoint.fl.size };
18
20
  },
19
21
  });
@@ -11,11 +11,12 @@ export default defineAgentTool({
11
11
  approval: 'never',
12
12
  async execute({ endpoint_id }) {
13
13
  const endpoint = getIcqqAgentDeps().getEndpoint(endpoint_id);
14
- const gl = endpoint.gl;
15
- const groups = Array.from(gl.values()).map((g) => ({
16
- group_id: g.group_id, group_name: g.group_name,
17
- member_count: g.member_count, max_member_count: g.max_member_count,
14
+ const groups = Array.from(endpoint.gl.values()).map((group) => ({
15
+ group_id: group.group_id,
16
+ group_name: group.group_name,
17
+ member_count: group.member_count,
18
+ max_member_count: group.max_member_count,
18
19
  }));
19
- return { groups: groups.slice(0, 50), count: gl.size };
20
+ return { groups: groups.slice(0, 50), count: endpoint.gl.size };
20
21
  },
21
22
  });
@@ -1,6 +1,7 @@
1
1
  import { defineAgentTool } from '@zhin.js/tool';
2
2
  import { z } from 'zod';
3
3
  import { getIcqqAgentDeps } from '../src/icqq-agent-deps.js';
4
+
4
5
  export default defineAgentTool<{ endpoint_id: string }>({
5
6
  description: '获取 Endpoint 的 QQ 群列表',
6
7
  inputSchema: z.object({
@@ -8,13 +9,14 @@ export default defineAgentTool<{ endpoint_id: string }>({
8
9
  }),
9
10
  platforms: ['icqq'],
10
11
  approval: 'never',
11
- async execute({ endpoint_id }: { endpoint_id: string }) {
12
+ async execute({ endpoint_id }) {
12
13
  const endpoint = getIcqqAgentDeps().getEndpoint(endpoint_id);
13
- const gl = endpoint.gl;
14
- const groups = Array.from(gl.values()).map((g) => ({
15
- group_id: g.group_id, group_name: g.group_name,
16
- member_count: g.member_count, max_member_count: g.max_member_count,
14
+ const groups = Array.from(endpoint.gl.values()).map((group) => ({
15
+ group_id: group.group_id,
16
+ group_name: group.group_name,
17
+ member_count: group.member_count,
18
+ max_member_count: group.max_member_count,
17
19
  }));
18
- return { groups: groups.slice(0, 50), count: gl.size };
20
+ return { groups: groups.slice(0, 50), count: endpoint.gl.size };
19
21
  },
20
22
  });
@@ -0,0 +1,27 @@
1
+ // Generated by build-plugin-runtime-entries.mjs. Do not edit.
2
+ import { defineAgentTool } from '@zhin.js/tool';
3
+ import { z } from 'zod';
4
+ import { getIcqqAgentDeps } from "../lib/icqq-agent-deps.js";
5
+ export default defineAgentTool({
6
+ description: '获取 QQ 群成员信息',
7
+ inputSchema: z.object({
8
+ endpoint_id: z.string().describe('Endpoint QQ号'),
9
+ group_id: z.number().describe('目标群号'),
10
+ user_id: z.number().describe('目标成员 QQ号'),
11
+ }),
12
+ platforms: ['icqq'],
13
+ approval: 'never',
14
+ async execute({ endpoint_id, group_id, user_id }) {
15
+ const endpoint = getIcqqAgentDeps().getEndpoint(endpoint_id);
16
+ const group = endpoint.pickGroup(group_id);
17
+ if (!group)
18
+ return { success: false, message: '目标群不存在' };
19
+ const memberInfo = group.pickMember(user_id);
20
+ if (!memberInfo)
21
+ return { success: false, message: '目标成员不存在' };
22
+ return {
23
+ success: true,
24
+ member: memberInfo,
25
+ };
26
+ },
27
+ });
@@ -0,0 +1,30 @@
1
+ import { defineAgentTool } from '@zhin.js/tool';
2
+ import { z } from 'zod';
3
+ import { getIcqqAgentDeps } from '../src/icqq-agent-deps.js';
4
+ interface GroupMemberInfoInput {
5
+ endpoint_id: string;
6
+ group_id: number;
7
+ user_id: number;
8
+ }
9
+
10
+ export default defineAgentTool<GroupMemberInfoInput>({
11
+ description: '获取 QQ 群成员信息',
12
+ inputSchema: z.object({
13
+ endpoint_id: z.string().describe('Endpoint QQ号'),
14
+ group_id: z.number().describe('目标群号'),
15
+ user_id: z.number().describe('目标成员 QQ号'),
16
+ }),
17
+ platforms: ['icqq'],
18
+ approval: 'never',
19
+ async execute({ endpoint_id, group_id, user_id }) {
20
+ const endpoint = getIcqqAgentDeps().getEndpoint(endpoint_id);
21
+ const group = endpoint.pickGroup(group_id);
22
+ if (!group) return { success: false, message: '目标群不存在' };
23
+ const memberInfo = group.pickMember(user_id);
24
+ if (!memberInfo) return { success: false, message: '目标成员不存在' };
25
+ return {
26
+ success: true,
27
+ member: memberInfo,
28
+ };
29
+ },
30
+ });
package/tools/set_mute.js CHANGED
@@ -11,7 +11,7 @@ export default defineAgentTool({
11
11
  duration: z.number().describe('禁言时间(秒),0=解除禁言').default(0),
12
12
  }),
13
13
  platforms: ['icqq'],
14
- permissions: ['role(master,trusted,admin,owner)'],
14
+ approval: 'always',
15
15
  async execute({ endpoint_id, group_id, user_id, duration }) {
16
16
  const endpoint = getIcqqAgentDeps().getEndpoint(endpoint_id);
17
17
  const selfInfo = endpoint.pickMember(group_id, endpoint.uin);
package/tools/set_mute.ts CHANGED
@@ -10,7 +10,7 @@ export default defineAgentTool<{ endpoint_id: string; group_id: number; user_id:
10
10
  duration: z.number().describe('禁言时间(秒),0=解除禁言').default(0),
11
11
  }),
12
12
  platforms: ['icqq'],
13
- permissions: ['role(master,trusted,admin,owner)'],
13
+ approval: 'always',
14
14
  async execute({ endpoint_id, group_id, user_id, duration }: { endpoint_id: string; group_id: number; user_id: number; duration?: number }) {
15
15
  const endpoint = getIcqqAgentDeps().getEndpoint(endpoint_id);
16
16
  const selfInfo= endpoint.pickMember(group_id,endpoint.uin);
@@ -12,7 +12,7 @@ export default defineAgentTool({
12
12
  duration: z.number().optional().describe('持续时间(秒),-1永久').default(-1),
13
13
  }),
14
14
  platforms: ['icqq'],
15
- permissions: ['role(master,trusted)'],
15
+ approval: 'always',
16
16
  async execute({ endpoint_id, group_id, user_id, title, duration }) {
17
17
  const endpoint = getIcqqAgentDeps().getEndpoint(endpoint_id);
18
18
  const selfInfo = endpoint.pickMember(group_id, endpoint.uin);
@@ -11,7 +11,7 @@ export default defineAgentTool<{ endpoint_id: string; group_id: number; user_id:
11
11
  duration: z.number().optional().describe('持续时间(秒),-1永久').default(-1),
12
12
  }),
13
13
  platforms: ['icqq'],
14
- permissions: ['role(master,trusted)'],
14
+ approval: 'always',
15
15
  async execute({ endpoint_id, group_id, user_id, title, duration }: { endpoint_id: string; group_id: number; user_id: number; title: string; duration?: number }) {
16
16
  const endpoint = getIcqqAgentDeps().getEndpoint(endpoint_id);
17
17
  const selfInfo= endpoint.pickMember(group_id,endpoint.uin);