@zhin.js/adapter-icqq 1.1.15 → 1.1.17
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/CHANGELOG.md +62 -0
- package/PERMITS.md +14 -18
- package/README.md +12 -0
- package/adapters/icqq/index.js +2 -0
- package/adapters/icqq/index.ts +2 -0
- package/agents/icqq/conversation.js +72 -0
- package/agents/icqq/conversation.ts +67 -0
- package/agents/icqq/members.js +16 -0
- package/agents/icqq/members.ts +17 -0
- package/agents/icqq/messages.js +144 -0
- package/agents/icqq/messages.ts +144 -0
- package/agents/icqq/skills/icqq-conversation/SKILL.md +24 -0
- package/agents/icqq/skills/icqq-conversation/tools/conversation_info/index.js +61 -0
- package/agents/icqq/skills/icqq-conversation/tools/conversation_info/index.ts +61 -0
- package/agents/icqq/skills/icqq-directory/tools/user_info/index.js +2 -1
- package/agents/icqq/skills/icqq-directory/tools/user_info/index.ts +2 -1
- package/agents/icqq/skills/icqq-group-admin/SKILL.md +11 -1
- package/agents/icqq/skills/icqq-group-admin/tools/announce/index.js +2 -1
- package/agents/icqq/skills/icqq-group-admin/tools/announce/index.ts +2 -1
- package/agents/icqq/skills/icqq-group-admin/tools/essence/index.js +3 -2
- package/agents/icqq/skills/icqq-group-admin/tools/essence/index.ts +3 -2
- package/agents/icqq/skills/icqq-group-admin/tools/group_invite/index.js +6 -5
- package/agents/icqq/skills/icqq-group-admin/tools/group_invite/index.ts +5 -4
- package/agents/icqq/skills/icqq-group-admin/tools/group_kick/index.js +4 -3
- package/agents/icqq/skills/icqq-group-admin/tools/group_kick/index.ts +4 -3
- package/agents/icqq/skills/icqq-group-admin/tools/group_leave/index.js +34 -0
- package/agents/icqq/skills/icqq-group-admin/tools/group_leave/index.ts +33 -0
- package/agents/icqq/skills/icqq-group-admin/tools/recall_member_message/index.js +34 -0
- package/agents/icqq/skills/icqq-group-admin/tools/recall_member_message/index.ts +33 -0
- package/agents/icqq/skills/icqq-group-admin/tools/set_anonymous/index.js +5 -4
- package/agents/icqq/skills/icqq-group-admin/tools/set_anonymous/index.ts +4 -3
- package/agents/icqq/skills/icqq-group-admin/tools/set_mute/index.js +8 -7
- package/agents/icqq/skills/icqq-group-admin/tools/set_mute/index.ts +6 -5
- package/agents/icqq/skills/icqq-group-admin/tools/set_title/index.js +8 -7
- package/agents/icqq/skills/icqq-group-admin/tools/set_title/index.ts +6 -5
- package/agents/icqq/skills/icqq-group-directory/tools/group_member_info/index.js +18 -7
- package/agents/icqq/skills/icqq-group-directory/tools/group_member_info/index.ts +17 -5
- package/agents/icqq/skills/icqq-group-interaction/tools/group_sign/index.js +3 -2
- package/agents/icqq/skills/icqq-group-interaction/tools/group_sign/index.ts +3 -2
- package/agents/icqq/skills/icqq-group-interaction/tools/poke/index.js +2 -1
- package/agents/icqq/skills/icqq-group-interaction/tools/poke/index.ts +2 -1
- package/agents/icqq/skills/icqq-group-state/tools/group_files/index.js +2 -1
- package/agents/icqq/skills/icqq-group-state/tools/group_files/index.ts +2 -1
- package/agents/icqq/skills/icqq-interaction/tools/send_user_like/index.js +3 -2
- package/agents/icqq/skills/icqq-interaction/tools/send_user_like/index.ts +3 -2
- package/agents/icqq/skills/icqq-messages/SKILL.md +28 -0
- package/agents/icqq/skills/icqq-messages/tools/forward_messages/index.js +42 -0
- package/agents/icqq/skills/icqq-messages/tools/forward_messages/index.ts +40 -0
- package/agents/icqq/skills/icqq-messages/tools/quote_reply/index.js +24 -0
- package/agents/icqq/skills/icqq-messages/tools/quote_reply/index.ts +24 -0
- package/agents/icqq/skills/icqq-messages/tools/recall_own_message/index.js +18 -0
- package/agents/icqq/skills/icqq-messages/tools/recall_own_message/index.ts +17 -0
- package/agents/icqq/system.md +5 -1
- package/commands/icqq/endpoint/definition.js +9 -7
- package/commands/icqq/endpoint/definition.ts +9 -9
- package/lib/endpoint.d.ts +1 -0
- package/lib/endpoint.js +12 -8
- package/lib/group-moderation.js +7 -2
- package/lib/group-scene.d.ts +11 -0
- package/lib/group-scene.js +71 -0
- package/lib/i18n.d.ts +1 -0
- package/lib/i18n.js +47 -0
- package/lib/icqq-inbound.d.ts +2 -0
- package/lib/icqq-inbound.js +3 -0
- package/lib/icqq-inbox.d.ts +5 -0
- package/lib/icqq-inbox.js +14 -0
- package/lib/icqq-login-assist.d.ts +2 -0
- package/lib/icqq-login-assist.js +8 -7
- package/lib/protocol.d.ts +3 -0
- package/lib/protocol.js +18 -3
- package/package.json +16 -16
- package/schema.json +250 -42
- package/src/endpoint.ts +13 -7
- package/src/group-moderation.ts +7 -2
- package/src/group-scene.ts +76 -0
- package/src/i18n.ts +48 -0
- package/src/icqq-inbound.ts +5 -0
- package/src/icqq-inbox.ts +15 -0
- package/src/icqq-login-assist.ts +10 -7
- package/src/protocol.ts +19 -3
|
@@ -1,4 +1,6 @@
|
|
|
1
1
|
// Generated by build-plugin-runtime-entries.mjs. Do not edit.
|
|
2
|
+
import { messages as t } from "../../../../messages.js";
|
|
3
|
+
import { freshMember } from "../../../../members.js";
|
|
2
4
|
import { defineAgentTool } from '@zhin.js/tool';
|
|
3
5
|
import { z } from 'zod';
|
|
4
6
|
export default defineAgentTool({
|
|
@@ -12,15 +14,24 @@ export default defineAgentTool({
|
|
|
12
14
|
permissions: ['role(master,admin,trusted,owner)'],
|
|
13
15
|
requiresApproval: 'never',
|
|
14
16
|
async execute({ group_id, user_id }, context) {
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
if (!memberInfo)
|
|
20
|
-
return { success: false, message: '目标成员不存在' };
|
|
17
|
+
// pickMember 返回的是实体对象,序列化后只剩 `{}`:模型会据此以为成员不在群里。按 group_kick 的口径实时查询。
|
|
18
|
+
const info = await freshMember(context.$client, group_id, user_id);
|
|
19
|
+
if (!info)
|
|
20
|
+
return { success: false, message: t(context.locale, 'memberMissing') };
|
|
21
21
|
return {
|
|
22
22
|
success: true,
|
|
23
|
-
member:
|
|
23
|
+
member: {
|
|
24
|
+
group_id: info.group_id,
|
|
25
|
+
user_id: info.user_id,
|
|
26
|
+
nickname: info.nickname,
|
|
27
|
+
card: info.card,
|
|
28
|
+
role: info.role,
|
|
29
|
+
title: info.title,
|
|
30
|
+
level: info.level,
|
|
31
|
+
join_time: info.join_time,
|
|
32
|
+
last_sent_time: info.last_sent_time,
|
|
33
|
+
shutup_time: info.shutup_time,
|
|
34
|
+
},
|
|
24
35
|
};
|
|
25
36
|
},
|
|
26
37
|
});
|
|
@@ -1,3 +1,5 @@
|
|
|
1
|
+
import { messages as t } from "../../../../messages.js";
|
|
2
|
+
import { freshMember } from "../../../../members.js";
|
|
1
3
|
import { defineAgentTool } from '@zhin.js/tool';
|
|
2
4
|
import { z } from 'zod';
|
|
3
5
|
interface GroupMemberInfoInput {
|
|
@@ -16,13 +18,23 @@ export default defineAgentTool<GroupMemberInfoInput>({
|
|
|
16
18
|
permissions: ['role(master,admin,trusted,owner)'],
|
|
17
19
|
requiresApproval: 'never',
|
|
18
20
|
async execute({ group_id, user_id }, context) {
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
if (!memberInfo) return { success: false, message: '目标成员不存在' };
|
|
21
|
+
// pickMember 返回的是实体对象,序列化后只剩 `{}`:模型会据此以为成员不在群里。按 group_kick 的口径实时查询。
|
|
22
|
+
const info = await freshMember(context.$client, group_id, user_id);
|
|
23
|
+
if (!info) return { success: false, message: t(context.locale, 'memberMissing') };
|
|
23
24
|
return {
|
|
24
25
|
success: true,
|
|
25
|
-
member:
|
|
26
|
+
member: {
|
|
27
|
+
group_id: info.group_id,
|
|
28
|
+
user_id: info.user_id,
|
|
29
|
+
nickname: info.nickname,
|
|
30
|
+
card: info.card,
|
|
31
|
+
role: info.role,
|
|
32
|
+
title: info.title,
|
|
33
|
+
level: info.level,
|
|
34
|
+
join_time: info.join_time,
|
|
35
|
+
last_sent_time: info.last_sent_time,
|
|
36
|
+
shutup_time: info.shutup_time,
|
|
37
|
+
},
|
|
26
38
|
};
|
|
27
39
|
},
|
|
28
40
|
});
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
// Generated by build-plugin-runtime-entries.mjs. Do not edit.
|
|
2
|
+
import { messages as t } from "../../../../messages.js";
|
|
2
3
|
import { defineAgentTool } from '@zhin.js/tool';
|
|
3
4
|
import { z } from 'zod';
|
|
4
5
|
export default defineAgentTool({
|
|
@@ -13,8 +14,8 @@ export default defineAgentTool({
|
|
|
13
14
|
const client = context.$client;
|
|
14
15
|
const groupInfo = client.pickGroup(group_id);
|
|
15
16
|
if (!groupInfo)
|
|
16
|
-
return { success: false, message: '
|
|
17
|
+
return { success: false, message: t(context.locale, 'groupMissing') };
|
|
17
18
|
await client.sendGroupSign(group_id);
|
|
18
|
-
return { success: true, message: '
|
|
19
|
+
return { success: true, message: t(context.locale, 'checkedIn') };
|
|
19
20
|
},
|
|
20
21
|
});
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { messages as t } from "../../../../messages.js";
|
|
1
2
|
import { defineAgentTool } from '@zhin.js/tool';
|
|
2
3
|
import { z } from 'zod';
|
|
3
4
|
export default defineAgentTool<{ group_id: number }>({
|
|
@@ -11,8 +12,8 @@ export default defineAgentTool<{ group_id: number }>({
|
|
|
11
12
|
async execute({ group_id }, context) {
|
|
12
13
|
const client = context.$client;
|
|
13
14
|
const groupInfo= client.pickGroup(group_id);
|
|
14
|
-
if(!groupInfo) return { success: false, message: '
|
|
15
|
+
if(!groupInfo) return { success: false, message: t(context.locale, 'groupMissing') };
|
|
15
16
|
await client.sendGroupSign(group_id);
|
|
16
|
-
return { success: true, message: '
|
|
17
|
+
return { success: true, message: t(context.locale, 'checkedIn') };
|
|
17
18
|
},
|
|
18
19
|
});
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
// Generated by build-plugin-runtime-entries.mjs. Do not edit.
|
|
2
|
+
import { messages as t } from "../../../../messages.js";
|
|
2
3
|
import { defineAgentTool } from '@zhin.js/tool';
|
|
3
4
|
import { z } from 'zod';
|
|
4
5
|
export default defineAgentTool({
|
|
@@ -12,6 +13,6 @@ export default defineAgentTool({
|
|
|
12
13
|
requiresApproval: 'never',
|
|
13
14
|
async execute({ group_id, user_id }, context) {
|
|
14
15
|
await context.$client.sendGroupPoke(group_id, user_id);
|
|
15
|
-
return { success: true, message:
|
|
16
|
+
return { success: true, message: t(context.locale, 'poked', { p0: user_id }) };
|
|
16
17
|
},
|
|
17
18
|
});
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { messages as t } from "../../../../messages.js";
|
|
1
2
|
import { defineAgentTool } from '@zhin.js/tool';
|
|
2
3
|
import { z } from 'zod';
|
|
3
4
|
export default defineAgentTool<{ group_id: number; user_id: number }>({
|
|
@@ -11,6 +12,6 @@ export default defineAgentTool<{ group_id: number; user_id: number }>({
|
|
|
11
12
|
requiresApproval: 'never',
|
|
12
13
|
async execute({ group_id, user_id }, context) {
|
|
13
14
|
await context.$client.sendGroupPoke(group_id, user_id);
|
|
14
|
-
return { success: true, message:
|
|
15
|
+
return { success: true, message: t(context.locale, 'poked', { p0: user_id }) };
|
|
15
16
|
},
|
|
16
17
|
});
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
// Generated by build-plugin-runtime-entries.mjs. Do not edit.
|
|
2
|
+
import { messages as t } from "../../../../messages.js";
|
|
2
3
|
import { defineAgentTool } from '@zhin.js/tool';
|
|
3
4
|
import { z } from 'zod';
|
|
4
5
|
export default defineAgentTool({
|
|
@@ -13,7 +14,7 @@ export default defineAgentTool({
|
|
|
13
14
|
async execute({ group_id }, context) {
|
|
14
15
|
const files = await context.$client.acquireGfs(group_id).ls('/');
|
|
15
16
|
if (!files.length)
|
|
16
|
-
return { files: [], message: '
|
|
17
|
+
return { files: [], message: t(context.locale, 'filesEmpty') };
|
|
17
18
|
return {
|
|
18
19
|
files: files.slice(0, 30).map((f) => 'size' in f
|
|
19
20
|
? { name: f.name, is_dir: false, size: f.size, uploader: f.user_id, upload_time: f.create_time }
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { messages as t } from "../../../../messages.js";
|
|
1
2
|
import { defineAgentTool } from '@zhin.js/tool';
|
|
2
3
|
import { z } from 'zod';
|
|
3
4
|
export default defineAgentTool<{ group_id: number }>({
|
|
@@ -11,7 +12,7 @@ export default defineAgentTool<{ group_id: number }>({
|
|
|
11
12
|
permissions: ['role(master,admin,trusted,owner)'],
|
|
12
13
|
async execute({ group_id }, context) {
|
|
13
14
|
const files = await context.$client.acquireGfs(group_id).ls('/');
|
|
14
|
-
if (!files.length) return { files: [], message: '
|
|
15
|
+
if (!files.length) return { files: [], message: t(context.locale, 'filesEmpty') };
|
|
15
16
|
return {
|
|
16
17
|
files: files.slice(0, 30).map((f) => 'size' in f
|
|
17
18
|
? { name: f.name, is_dir: false, size: f.size, uploader: f.user_id, upload_time: f.create_time }
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
// Generated by build-plugin-runtime-entries.mjs. Do not edit.
|
|
2
|
+
import { messages as t } from "../../../../messages.js";
|
|
2
3
|
import { defineAgentTool } from '@zhin.js/tool';
|
|
3
4
|
import { z } from 'zod';
|
|
4
5
|
export default defineAgentTool({
|
|
@@ -20,7 +21,7 @@ export default defineAgentTool({
|
|
|
20
21
|
}));
|
|
21
22
|
const successTimes = successArray.reduce((a, b) => a + b, 0);
|
|
22
23
|
if (!successTimes)
|
|
23
|
-
return { success: false, message: '
|
|
24
|
-
return { success: true, message:
|
|
24
|
+
return { success: false, message: t(context.locale, 'likeFailed') };
|
|
25
|
+
return { success: true, message: t(context.locale, 'likesSent', { p0: user_id, p1: successTimes }) };
|
|
25
26
|
},
|
|
26
27
|
});
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { messages as t } from "../../../../messages.js";
|
|
1
2
|
import { defineAgentTool } from '@zhin.js/tool';
|
|
2
3
|
import { z } from 'zod';
|
|
3
4
|
export default defineAgentTool<{ user_id: number }>({
|
|
@@ -16,7 +17,7 @@ export default defineAgentTool<{ user_id: number }>({
|
|
|
16
17
|
} catch { /* ignore */ }
|
|
17
18
|
}));
|
|
18
19
|
const successTimes = successArray.reduce((a, b) => a + b, 0);
|
|
19
|
-
if (!successTimes) return { success: false, message: '
|
|
20
|
-
return { success: true, message:
|
|
20
|
+
if (!successTimes) return { success: false, message: t(context.locale, 'likeFailed') };
|
|
21
|
+
return { success: true, message: t(context.locale, 'likesSent', { p0: user_id, p1: successTimes }) };
|
|
21
22
|
},
|
|
22
23
|
});
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: icqq-messages
|
|
3
|
+
platforms:
|
|
4
|
+
- icqq
|
|
5
|
+
description: ICQQ 当前会话的引用回复、真实消息合并转发,以及撤回机器人自己的发言。
|
|
6
|
+
keywords:
|
|
7
|
+
- 引用
|
|
8
|
+
- 回复原消息
|
|
9
|
+
- 转发
|
|
10
|
+
- 撤回
|
|
11
|
+
- 自己的消息
|
|
12
|
+
tools:
|
|
13
|
+
- quote_reply
|
|
14
|
+
- forward_messages
|
|
15
|
+
- recall_own_message
|
|
16
|
+
scopes:
|
|
17
|
+
- private
|
|
18
|
+
- group
|
|
19
|
+
---
|
|
20
|
+
# icqq-messages
|
|
21
|
+
|
|
22
|
+
Only use real message IDs from the current conversation. Do not invent IDs, source authors or forwarded content. The tools resolve the originals and send through the unified outbound pipeline. Cross-conversation forwarding is not supported.
|
|
23
|
+
|
|
24
|
+
`quote_reply` sends a text reply quoting one original; `forward_messages` sends a merged forward of the supplied originals. Use `inspect_conversation_reference` to read an incoming quote/forward when available; it does not send messages.
|
|
25
|
+
|
|
26
|
+
`recall_own_message` needs no management role or per-call approval, including when the bot is an ordinary group member. It verifies the bot authored the message. The platform decides the recall time window; never promise success before receiving confirmation or retry an unconfirmed result automatically. To recall another member's message, load `icqq-group-admin` instead.
|
|
27
|
+
|
|
28
|
+
These tools currently support QQ private/group conversations, not guild channels. A failed or suppressed outbound receipt is not a delivered message.
|
|
@@ -0,0 +1,42 @@
|
|
|
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 { currentConversation, resolveMessage, sendToConversation } from '../../../../conversation.js';
|
|
5
|
+
import { messages as t } from '../../../../messages.js';
|
|
6
|
+
export default defineAgentTool({
|
|
7
|
+
description: '将当前 QQ 群或私聊中的真实消息合并转发到本会话;保留真实作者和内容,不支持跨会话转发',
|
|
8
|
+
adapter: 'icqq',
|
|
9
|
+
scopes: ['private', 'group'],
|
|
10
|
+
requiresApproval: 'never',
|
|
11
|
+
inputSchema: z.object({ message_ids: z.array(z.string().min(1)).min(1).describe('当前会话的真实消息 ID(或消息头里的 m<n> 别名),按转发顺序排列') }),
|
|
12
|
+
async execute({ message_ids }, context) {
|
|
13
|
+
const conversation = currentConversation(context);
|
|
14
|
+
if (conversation.kind === 'channel')
|
|
15
|
+
throw new Error(t(context.locale, 'channelOperationUnsupported'));
|
|
16
|
+
const originals = [];
|
|
17
|
+
for (const id of message_ids)
|
|
18
|
+
originals.push(await resolveMessage(context, id));
|
|
19
|
+
const client = context.$client;
|
|
20
|
+
const target = conversation.kind === 'group' ? client.pickGroup(Number(conversation.id))
|
|
21
|
+
: conversation.parent?.kind === 'group' ? client.pickMember(Number(conversation.parent.id), Number(conversation.id))
|
|
22
|
+
: client.pickFriend(Number(conversation.id));
|
|
23
|
+
// SDK preparation uploads a resource; only the gateway below delivers a message.
|
|
24
|
+
const card = await target.makeForwardMsg(originals.map(message => ({
|
|
25
|
+
user_id: message.sender.user_id,
|
|
26
|
+
nickname: message.sender.nickname,
|
|
27
|
+
time: message.time,
|
|
28
|
+
message: message.message,
|
|
29
|
+
})));
|
|
30
|
+
context.signal.throwIfAborted();
|
|
31
|
+
const data = typeof card.data === 'string' ? JSON.parse(card.data) : card.data;
|
|
32
|
+
const detail = data?.meta?.detail;
|
|
33
|
+
if (typeof detail?.resid !== 'string' || !detail.resid || typeof detail.uniseq !== 'string' || !detail.uniseq) {
|
|
34
|
+
throw new Error('ICQQ did not return a complete merged-forward resource');
|
|
35
|
+
}
|
|
36
|
+
return sendToConversation(context, {
|
|
37
|
+
type: 'forward',
|
|
38
|
+
data: { forward_id: detail.resid },
|
|
39
|
+
platform: { icqqForwardFilename: detail.uniseq },
|
|
40
|
+
});
|
|
41
|
+
},
|
|
42
|
+
});
|
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
import { defineAgentTool } from '@zhin.js/tool';
|
|
2
|
+
import { z } from 'zod';
|
|
3
|
+
import { currentConversation, resolveMessage, sendToConversation } from '../../../../conversation.js';
|
|
4
|
+
import { messages as t } from '../../../../messages.js';
|
|
5
|
+
|
|
6
|
+
export default defineAgentTool({
|
|
7
|
+
description: '将当前 QQ 群或私聊中的真实消息合并转发到本会话;保留真实作者和内容,不支持跨会话转发',
|
|
8
|
+
adapter: 'icqq',
|
|
9
|
+
scopes: ['private', 'group'],
|
|
10
|
+
requiresApproval: 'never',
|
|
11
|
+
inputSchema: z.object({ message_ids: z.array(z.string().min(1)).min(1).describe('当前会话的真实消息 ID(或消息头里的 m<n> 别名),按转发顺序排列') }),
|
|
12
|
+
async execute({ message_ids }, context) {
|
|
13
|
+
const conversation = currentConversation(context);
|
|
14
|
+
if (conversation.kind === 'channel') throw new Error(t(context.locale, 'channelOperationUnsupported'));
|
|
15
|
+
const originals = [];
|
|
16
|
+
for (const id of message_ids) originals.push(await resolveMessage(context, id));
|
|
17
|
+
const client = context.$client;
|
|
18
|
+
const target = conversation.kind === 'group' ? client.pickGroup(Number(conversation.id))
|
|
19
|
+
: conversation.parent?.kind === 'group' ? client.pickMember(Number(conversation.parent.id), Number(conversation.id))
|
|
20
|
+
: client.pickFriend(Number(conversation.id));
|
|
21
|
+
// SDK preparation uploads a resource; only the gateway below delivers a message.
|
|
22
|
+
const card = await target.makeForwardMsg(originals.map(message => ({
|
|
23
|
+
user_id: message.sender.user_id,
|
|
24
|
+
nickname: message.sender.nickname,
|
|
25
|
+
time: message.time,
|
|
26
|
+
message: message.message,
|
|
27
|
+
})));
|
|
28
|
+
context.signal.throwIfAborted();
|
|
29
|
+
const data = typeof card.data === 'string' ? JSON.parse(card.data) : card.data;
|
|
30
|
+
const detail = data?.meta?.detail;
|
|
31
|
+
if (typeof detail?.resid !== 'string' || !detail.resid || typeof detail.uniseq !== 'string' || !detail.uniseq) {
|
|
32
|
+
throw new Error('ICQQ did not return a complete merged-forward resource');
|
|
33
|
+
}
|
|
34
|
+
return sendToConversation(context, {
|
|
35
|
+
type: 'forward',
|
|
36
|
+
data: { forward_id: detail.resid },
|
|
37
|
+
platform: { icqqForwardFilename: detail.uniseq },
|
|
38
|
+
});
|
|
39
|
+
},
|
|
40
|
+
});
|
|
@@ -0,0 +1,24 @@
|
|
|
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 { resolveMessage, sendToConversation } from '../../../../conversation.js';
|
|
5
|
+
export default defineAgentTool({
|
|
6
|
+
description: '引用当前 QQ 群或私聊中的真实消息并回复文字;无需群管角色,发送走统一链路',
|
|
7
|
+
adapter: 'icqq',
|
|
8
|
+
scopes: ['private', 'group'],
|
|
9
|
+
requiresApproval: 'never',
|
|
10
|
+
inputSchema: z.object({
|
|
11
|
+
message_id: z.string().min(1).describe('当前会话中真实的原消息 ID,或消息头里的 m<n> 别名'),
|
|
12
|
+
text: z.string().min(1).describe('本次回复的文字'),
|
|
13
|
+
}),
|
|
14
|
+
async execute({ message_id, text }, context) {
|
|
15
|
+
const original = await resolveMessage(context, message_id);
|
|
16
|
+
return sendToConversation(context, [
|
|
17
|
+
{ type: 'reply', data: { message_id }, platform: { icqqQuote: {
|
|
18
|
+
user_id: original.sender.user_id, seq: original.seq, rand: original.rand,
|
|
19
|
+
time: original.time, message: original.raw_message,
|
|
20
|
+
} } },
|
|
21
|
+
{ type: 'text', data: { text } },
|
|
22
|
+
]);
|
|
23
|
+
},
|
|
24
|
+
});
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
import { defineAgentTool } from '@zhin.js/tool';
|
|
2
|
+
import { z } from 'zod';
|
|
3
|
+
import { resolveMessage, sendToConversation } from '../../../../conversation.js';
|
|
4
|
+
|
|
5
|
+
export default defineAgentTool({
|
|
6
|
+
description: '引用当前 QQ 群或私聊中的真实消息并回复文字;无需群管角色,发送走统一链路',
|
|
7
|
+
adapter: 'icqq',
|
|
8
|
+
scopes: ['private', 'group'],
|
|
9
|
+
requiresApproval: 'never',
|
|
10
|
+
inputSchema: z.object({
|
|
11
|
+
message_id: z.string().min(1).describe('当前会话中真实的原消息 ID,或消息头里的 m<n> 别名'),
|
|
12
|
+
text: z.string().min(1).describe('本次回复的文字'),
|
|
13
|
+
}),
|
|
14
|
+
async execute({ message_id, text }, context) {
|
|
15
|
+
const original = await resolveMessage(context, message_id);
|
|
16
|
+
return sendToConversation(context, [
|
|
17
|
+
{ type: 'reply', data: { message_id }, platform: { icqqQuote: {
|
|
18
|
+
user_id: original.sender.user_id, seq: original.seq, rand: original.rand,
|
|
19
|
+
time: original.time, message: original.raw_message,
|
|
20
|
+
} } },
|
|
21
|
+
{ type: 'text', data: { text } },
|
|
22
|
+
]);
|
|
23
|
+
},
|
|
24
|
+
});
|
|
@@ -0,0 +1,18 @@
|
|
|
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 { recallMessage, resolveMessage } from '../../../../conversation.js';
|
|
5
|
+
import { messages as t } from '../../../../messages.js';
|
|
6
|
+
export default defineAgentTool({
|
|
7
|
+
description: '撤回机器人在当前 QQ 群或私聊中自己的发言;无需群管角色或逐次审批,时限由平台决定',
|
|
8
|
+
adapter: 'icqq',
|
|
9
|
+
scopes: ['private', 'group'],
|
|
10
|
+
requiresApproval: 'never',
|
|
11
|
+
inputSchema: z.object({ message_id: z.string().min(1).describe('机器人在当前会话中发送的真实消息 ID,或消息头里的 m<n> 别名') }),
|
|
12
|
+
async execute({ message_id }, context) {
|
|
13
|
+
const message = await resolveMessage(context, message_id);
|
|
14
|
+
if (message.sender.user_id !== context.$client.uin)
|
|
15
|
+
return { success: false, message: t(context.locale, 'ownMessageRequired') };
|
|
16
|
+
return recallMessage(context, message_id);
|
|
17
|
+
},
|
|
18
|
+
});
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
import { defineAgentTool } from '@zhin.js/tool';
|
|
2
|
+
import { z } from 'zod';
|
|
3
|
+
import { recallMessage, resolveMessage } from '../../../../conversation.js';
|
|
4
|
+
import { messages as t } from '../../../../messages.js';
|
|
5
|
+
|
|
6
|
+
export default defineAgentTool({
|
|
7
|
+
description: '撤回机器人在当前 QQ 群或私聊中自己的发言;无需群管角色或逐次审批,时限由平台决定',
|
|
8
|
+
adapter: 'icqq',
|
|
9
|
+
scopes: ['private', 'group'],
|
|
10
|
+
requiresApproval: 'never',
|
|
11
|
+
inputSchema: z.object({ message_id: z.string().min(1).describe('机器人在当前会话中发送的真实消息 ID,或消息头里的 m<n> 别名') }),
|
|
12
|
+
async execute({ message_id }, context) {
|
|
13
|
+
const message = await resolveMessage(context, message_id);
|
|
14
|
+
if (message.sender.user_id !== context.$client.uin) return { success: false, message: t(context.locale, 'ownMessageRequired') };
|
|
15
|
+
return recallMessage(context, message_id);
|
|
16
|
+
},
|
|
17
|
+
});
|
package/agents/icqq/system.md
CHANGED
|
@@ -1,3 +1,7 @@
|
|
|
1
1
|
# ICQQ Platform Agent
|
|
2
2
|
|
|
3
|
-
You are the icqq platform specialist. Interpret requests in the current icqq conversation and load only the narrowest
|
|
3
|
+
You are the icqq platform specialist. Interpret requests in the current icqq conversation and load only the narrowest available Skill needed for the task.
|
|
4
|
+
|
|
5
|
+
Use the current Bot snapshot for your platform role. Load `icqq-conversation` and query `conversation_info` when the role is unknown or you need more scene or permission details. Sender roles describe the requester, not you. Do not infer your role from old session summaries or another endpoint account.
|
|
6
|
+
|
|
7
|
+
Discover message and management operations from the current scene's Skill catalog; follow each tool's target, platform and approval rules. Recalling your own messages needs no group management role or per-call approval. Leaving a group requires fresh approval and is forbidden when you own the group. Do not load a Skill that is absent from the current catalog.
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
// Generated by build-plugin-runtime-entries.mjs. Do not edit.
|
|
2
|
+
import { messages as t } from "../../../lib/i18n.js";
|
|
2
3
|
import { createEndpointCommands, endpointConfigurationStoreToken } from 'zhin.js/adapter';
|
|
3
4
|
import { defineCommand } from 'zhin.js/command';
|
|
4
5
|
import { icqqRuntimeStateToken } from "../../../lib/icqq-runtime-state.js";
|
|
@@ -6,14 +7,16 @@ export const icqqEndpointCommands = createEndpointCommands({
|
|
|
6
7
|
adapterKey: 'icqq',
|
|
7
8
|
adapterDisplayName: 'ICQQ',
|
|
8
9
|
running: (use) => use(icqqRuntimeStateToken).endpoints.values(),
|
|
9
|
-
describeEntry: () => 'direct
|
|
10
|
+
describeEntry: (_entry, locale) => t(locale, 'direct'),
|
|
10
11
|
addDescription: '登记 ICQQ endpoint(重启 zhin 生效)',
|
|
11
|
-
|
|
12
|
+
addDescriptionI18n: { en: 'Register an ICQQ endpoint; restart zhin to apply' },
|
|
13
|
+
bindFlow: async ({ id, use, locale, formatCommand }) => {
|
|
12
14
|
if (!id) {
|
|
13
|
-
|
|
15
|
+
const command = formatCommand?.('icqq endpoint add');
|
|
16
|
+
return command ? t(locale, 'addUsage', { command }) : t(locale, 'uinRequired');
|
|
14
17
|
}
|
|
15
18
|
if (!/^\d+$/.test(id)) {
|
|
16
|
-
return
|
|
19
|
+
return t(locale, 'uinInvalid');
|
|
17
20
|
}
|
|
18
21
|
try {
|
|
19
22
|
const { filePath } = await use(endpointConfigurationStoreToken).add({
|
|
@@ -21,11 +24,10 @@ export const icqqEndpointCommands = createEndpointCommands({
|
|
|
21
24
|
entry: { id },
|
|
22
25
|
environment: {},
|
|
23
26
|
});
|
|
24
|
-
return (
|
|
25
|
-
`重启 zhin 后生效。`);
|
|
27
|
+
return t(locale, 'endpointAdded', { id, file: filePath });
|
|
26
28
|
}
|
|
27
29
|
catch (error) {
|
|
28
|
-
return
|
|
30
|
+
return t(locale, 'endpointAddFailed', { error: error instanceof Error ? error.message : String(error) });
|
|
29
31
|
}
|
|
30
32
|
},
|
|
31
33
|
}, defineCommand);
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { messages as t } from '../../../src/i18n.js';
|
|
1
2
|
import { createEndpointCommands, endpointConfigurationStoreToken } from 'zhin.js/adapter';
|
|
2
3
|
import { defineCommand } from 'zhin.js/command';
|
|
3
4
|
import { icqqRuntimeStateToken } from '../../../src/icqq-runtime-state.js';
|
|
@@ -6,14 +7,16 @@ export const icqqEndpointCommands = createEndpointCommands({
|
|
|
6
7
|
adapterKey: 'icqq',
|
|
7
8
|
adapterDisplayName: 'ICQQ',
|
|
8
9
|
running: (use) => use(icqqRuntimeStateToken).endpoints.values(),
|
|
9
|
-
describeEntry: () => 'direct
|
|
10
|
+
describeEntry: (_entry, locale) => t(locale, 'direct'),
|
|
10
11
|
addDescription: '登记 ICQQ endpoint(重启 zhin 生效)',
|
|
11
|
-
|
|
12
|
+
addDescriptionI18n: { en: 'Register an ICQQ endpoint; restart zhin to apply' },
|
|
13
|
+
bindFlow: async ({ id, use, locale, formatCommand }) => {
|
|
12
14
|
if (!id) {
|
|
13
|
-
|
|
15
|
+
const command = formatCommand?.('icqq endpoint add');
|
|
16
|
+
return command ? t(locale, 'addUsage', { command }) : t(locale, 'uinRequired');
|
|
14
17
|
}
|
|
15
18
|
if (!/^\d+$/.test(id)) {
|
|
16
|
-
return
|
|
19
|
+
return t(locale, 'uinInvalid');
|
|
17
20
|
}
|
|
18
21
|
try {
|
|
19
22
|
const { filePath } = await use(endpointConfigurationStoreToken).add({
|
|
@@ -21,12 +24,9 @@ export const icqqEndpointCommands = createEndpointCommands({
|
|
|
21
24
|
entry: { id },
|
|
22
25
|
environment: {},
|
|
23
26
|
});
|
|
24
|
-
return (
|
|
25
|
-
`✅ endpoint「${id}」已追加到 ${filePath} 的 plugins.icqq.endpoints。\n` +
|
|
26
|
-
`重启 zhin 后生效。`
|
|
27
|
-
);
|
|
27
|
+
return t(locale, 'endpointAdded', { id, file: filePath });
|
|
28
28
|
} catch (error) {
|
|
29
|
-
return
|
|
29
|
+
return t(locale, 'endpointAddFailed', { error: error instanceof Error ? error.message : String(error) });
|
|
30
30
|
}
|
|
31
31
|
},
|
|
32
32
|
}, defineCommand);
|
package/lib/endpoint.d.ts
CHANGED
|
@@ -9,6 +9,7 @@ export interface IcqqEndpointOptions {
|
|
|
9
9
|
readonly id: CapabilityId;
|
|
10
10
|
readonly config: ResolvedIcqqConfig;
|
|
11
11
|
readonly loginAssist: LoginAssist;
|
|
12
|
+
readonly locale?: import('zhin.js/i18n').Locale;
|
|
12
13
|
}
|
|
13
14
|
export declare class IcqqEndpoint extends Endpoint<Client> {
|
|
14
15
|
#private;
|
package/lib/endpoint.js
CHANGED
|
@@ -8,12 +8,13 @@ import { formatCompact, getAdapterLogger, truncatePreview } from '@zhin.js/logge
|
|
|
8
8
|
import { runIcqqLoginAssistStep } from './icqq-login-assist.js';
|
|
9
9
|
import { IcqqContentResolver } from './content-resolver.js';
|
|
10
10
|
import { InboundMessageDeduper, findIcqqNestedMessageSource, isIcqqBotMentioned, isIcqqMessagePostType, normalizeIcqqInboundMessage, messageLookupFromIcqqSource, resolveIcqqQuoteIdFromEvent, resolveIcqqInboundMedia, resolveIcqqSelfUserId, shouldSkipSelfInboundMessage, } from './icqq-inbound.js';
|
|
11
|
-
import { isIcqqNoticePayload, isIcqqRequestPayload, } from './icqq-inbox.js';
|
|
11
|
+
import { icqqNoticeDedupeKey, isIcqqNoticePayload, isIcqqRequestPayload, } from './icqq-inbox.js';
|
|
12
12
|
import { IcqqGuildCatalog, normalizeIcqqGuildInboundMessage, } from './icqq-guild.js';
|
|
13
13
|
import { prepareIcqqOutboundMedia, resolveIcqqOutboundMediaMode, } from './outbound-media.js';
|
|
14
14
|
import { formatInboundContent, formatOutboundMessages, normalizeIcqqImageMedia, icqqInboundConversation, icqqOutboundTarget, } from './protocol.js';
|
|
15
15
|
import { ManagedIcqqClient } from './managed-client.js';
|
|
16
16
|
import { inspectGroupMember, moderateGroupMember } from './group-moderation.js';
|
|
17
|
+
import { inspectGroupMemberProfile, inspectGroupScene } from './group-scene.js';
|
|
17
18
|
const BOUND_EVENTS = [
|
|
18
19
|
'message.private',
|
|
19
20
|
'message.group',
|
|
@@ -57,6 +58,8 @@ export class IcqqEndpoint extends Endpoint {
|
|
|
57
58
|
content;
|
|
58
59
|
management = Object.freeze({
|
|
59
60
|
inspectGroupMember: (group, user, signal) => inspectGroupMember(this.client, group, user, signal),
|
|
61
|
+
inspectGroupScene: (group, signal) => inspectGroupScene(this.client, group, signal),
|
|
62
|
+
inspectGroupMemberProfile: (group, user, signal) => inspectGroupMemberProfile(this.client, group, user, signal),
|
|
60
63
|
moderateGroupMember: (group, user, action, signal) => moderateGroupMember(this.client, group, user, action, signal, () => this.#open && !this.#retiring && !this.#stopped),
|
|
61
64
|
listFriends: async () => Array.from(this.client.fl.values()).map((f) => ({
|
|
62
65
|
user_id: f.user_id,
|
|
@@ -87,6 +90,11 @@ export class IcqqEndpoint extends Endpoint {
|
|
|
87
90
|
deleteFriend: async (userId) => {
|
|
88
91
|
await this.client.deleteFriend(Number(userId));
|
|
89
92
|
},
|
|
93
|
+
sendGroupNotice: async (groupId, content) => {
|
|
94
|
+
if (await this.client.sendGroupNotice(Number(groupId), content) === false) {
|
|
95
|
+
throw new Error('QQ did not confirm the group notice');
|
|
96
|
+
}
|
|
97
|
+
},
|
|
90
98
|
});
|
|
91
99
|
control = Object.freeze({
|
|
92
100
|
recall: async (message) => {
|
|
@@ -622,6 +630,7 @@ export class IcqqEndpoint extends Endpoint {
|
|
|
622
630
|
this.#logSystemEvent(`login.${step}`, event);
|
|
623
631
|
void runIcqqLoginAssistStep({
|
|
624
632
|
assist: this.#options.loginAssist,
|
|
633
|
+
locale: this.#options.locale,
|
|
625
634
|
client: this.client,
|
|
626
635
|
adapter: 'icqq',
|
|
627
636
|
endpointKey: this.endpointName,
|
|
@@ -737,6 +746,7 @@ export class IcqqEndpoint extends Endpoint {
|
|
|
737
746
|
channelType: normalized.channelType,
|
|
738
747
|
channelId: normalized.channelId,
|
|
739
748
|
channelParentGroupId: normalized.channelParentGroupId,
|
|
749
|
+
name: normalized.groupName,
|
|
740
750
|
});
|
|
741
751
|
const quoteId = resolveIcqqQuoteIdFromEvent(data);
|
|
742
752
|
const quoted = messageLookupFromIcqqSource(findIcqqNestedMessageSource(data));
|
|
@@ -851,13 +861,7 @@ export class IcqqEndpoint extends Endpoint {
|
|
|
851
861
|
#onNoticeEvent(payload) {
|
|
852
862
|
if (!payload || !isIcqqNoticePayload(payload))
|
|
853
863
|
return;
|
|
854
|
-
|
|
855
|
-
payload.time,
|
|
856
|
-
payload.operator_id ?? payload.user_id,
|
|
857
|
-
payload.notice_type ?? payload.type,
|
|
858
|
-
payload.sub_type,
|
|
859
|
-
].join('_');
|
|
860
|
-
if (!this.#sideEventDeduper.shouldProcess(`notice:${key}`))
|
|
864
|
+
if (!this.#sideEventDeduper.shouldProcess(icqqNoticeDedupeKey(payload)))
|
|
861
865
|
return;
|
|
862
866
|
this.#dispatchIcqqSideEvent(payload);
|
|
863
867
|
}
|
package/lib/group-moderation.js
CHANGED
|
@@ -3,17 +3,22 @@ export async function inspectGroupMember(client, groupId, userId, signal) {
|
|
|
3
3
|
signal.throwIfAborted();
|
|
4
4
|
const group = numericId(groupId);
|
|
5
5
|
const user = numericId(userId);
|
|
6
|
+
const selfLookup = client.getGroupMemberInfo(group, client.uin, true);
|
|
6
7
|
const [self, member] = await Promise.all([
|
|
7
|
-
|
|
8
|
-
client.getGroupMemberInfo(group, user, true),
|
|
8
|
+
selfLookup,
|
|
9
|
+
user === client.uin ? selfLookup : client.getGroupMemberInfo(group, user, true),
|
|
9
10
|
]);
|
|
10
11
|
signal.throwIfAborted();
|
|
11
12
|
const joinedAt = member.join_time * 1000;
|
|
13
|
+
const platformRole = member.user_id === user && member.group_id === group
|
|
14
|
+
&& (member.role === 'owner' || member.role === 'admin' || member.role === 'member')
|
|
15
|
+
? member.role : undefined;
|
|
12
16
|
return {
|
|
13
17
|
canModerate: client.isOnline() && user !== client.uin
|
|
14
18
|
&& self.user_id === client.uin && self.group_id === group
|
|
15
19
|
&& member.user_id === user && member.group_id === group
|
|
16
20
|
&& (self.role === 'owner' || self.role === 'admin') && member.role === 'member',
|
|
21
|
+
...(platformRole ? { platformRole } : {}),
|
|
17
22
|
...(Number.isSafeInteger(joinedAt) && joinedAt > 0 && joinedAt <= Date.now() ? { joinedAt } : {}),
|
|
18
23
|
};
|
|
19
24
|
}
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
import type { Client } from '@icqqjs/icqq';
|
|
2
|
+
import type { GroupMemberProfile, GroupSceneFacts } from 'zhin.js/adapter';
|
|
3
|
+
/** Listing a very large roster only to find admins is not worth the packet; adminIds stay unknown. */
|
|
4
|
+
export declare const ADMIN_SCAN_MAX_MEMBERS = 1000;
|
|
5
|
+
/**
|
|
6
|
+
* ADR 0041 scene facts: one uncached group-info read, plus (small groups only) one roster read for
|
|
7
|
+
* admins. A roster failure leaves adminIds unknown instead of failing the other facts.
|
|
8
|
+
*/
|
|
9
|
+
export declare function inspectGroupScene(client: Client, groupId: string, signal: AbortSignal): Promise<GroupSceneFacts>;
|
|
10
|
+
/** ADR 0041 member facts from one uncached lookup (same rationale as the agent `freshMember` helper). */
|
|
11
|
+
export declare function inspectGroupMemberProfile(client: Client, groupId: string, userId: string, signal: AbortSignal): Promise<GroupMemberProfile>;
|