@zhin.js/adapter-icqq 1.0.71 → 1.1.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/CHANGELOG.md +1216 -52
- package/PERMITS.md +24 -0
- package/README.md +123 -429
- package/adapters/icqq/index.js +31 -0
- package/adapters/icqq/index.ts +42 -0
- package/agents/icqq/agent.json +20 -0
- package/agents/icqq/boundaries.md +3 -0
- package/agents/icqq/conventions.md +3 -0
- package/agents/icqq/skills/icqq-directory/SKILL.md +20 -0
- package/agents/icqq/skills/icqq-directory/tools/friend_list/index.js +19 -0
- package/agents/icqq/skills/icqq-directory/tools/friend_list/index.ts +19 -0
- package/agents/icqq/skills/icqq-directory/tools/group_list/index.js +20 -0
- package/agents/icqq/skills/icqq-directory/tools/group_list/index.ts +20 -0
- package/agents/icqq/skills/icqq-directory/tools/group_member_info/index.js +26 -0
- package/agents/icqq/skills/icqq-directory/tools/group_member_info/index.ts +28 -0
- package/agents/icqq/skills/icqq-directory/tools/user_info/index.js +15 -0
- package/agents/icqq/skills/icqq-directory/tools/user_info/index.ts +14 -0
- package/agents/icqq/skills/icqq-group-admin/SKILL.md +25 -0
- package/agents/icqq/skills/icqq-group-admin/tools/announce/index.js +18 -0
- package/agents/icqq/skills/icqq-group-admin/tools/announce/index.ts +17 -0
- package/agents/icqq/skills/icqq-group-admin/tools/essence/index.js +24 -0
- package/agents/icqq/skills/icqq-group-admin/tools/essence/index.ts +22 -0
- package/agents/icqq/skills/icqq-group-admin/tools/group_invite/index.js +23 -0
- package/agents/icqq/skills/icqq-group-admin/tools/group_invite/index.ts +20 -0
- package/agents/icqq/skills/icqq-group-admin/tools/group_kick/index.js +27 -0
- package/agents/icqq/skills/icqq-group-admin/tools/group_kick/index.ts +23 -0
- package/agents/icqq/skills/icqq-group-admin/tools/set_anonymous/index.js +23 -0
- package/agents/icqq/skills/icqq-group-admin/tools/set_anonymous/index.ts +21 -0
- package/agents/icqq/skills/icqq-group-admin/tools/set_mute/index.js +25 -0
- package/agents/icqq/skills/icqq-group-admin/tools/set_mute/index.ts +22 -0
- package/agents/icqq/skills/icqq-group-admin/tools/set_title/index.js +26 -0
- package/agents/icqq/skills/icqq-group-admin/tools/set_title/index.ts +23 -0
- package/agents/icqq/skills/icqq-group-state/SKILL.md +17 -0
- package/agents/icqq/skills/icqq-group-state/tools/group_files/index.js +24 -0
- package/agents/icqq/skills/icqq-group-state/tools/group_files/index.ts +22 -0
- package/agents/icqq/skills/icqq-group-state/tools/list_muted/index.js +18 -0
- package/agents/icqq/skills/icqq-group-state/tools/list_muted/index.ts +17 -0
- package/agents/icqq/skills/icqq-interaction/SKILL.md +19 -0
- package/agents/icqq/skills/icqq-interaction/tools/group_sign/index.js +20 -0
- package/agents/icqq/skills/icqq-interaction/tools/group_sign/index.ts +18 -0
- package/agents/icqq/skills/icqq-interaction/tools/poke/index.js +17 -0
- package/agents/icqq/skills/icqq-interaction/tools/poke/index.ts +16 -0
- package/agents/icqq/skills/icqq-interaction/tools/send_user_like/index.js +26 -0
- package/agents/icqq/skills/icqq-interaction/tools/send_user_like/index.ts +22 -0
- package/agents/icqq/system.md +3 -0
- package/commands/icqq/endpoint/add/[[id]]/index.js +3 -0
- package/commands/icqq/endpoint/add/[[id]]/index.ts +3 -0
- package/commands/icqq/endpoint/definition.js +31 -0
- package/commands/icqq/endpoint/definition.ts +32 -0
- package/commands/icqq/endpoint/list/index.js +3 -0
- package/commands/icqq/endpoint/list/index.ts +3 -0
- package/commands/icqq/endpoint/remove/[id]/index.js +3 -0
- package/commands/icqq/endpoint/remove/[id]/index.ts +3 -0
- package/lib/client.d.ts +19 -0
- package/lib/client.js +3 -0
- package/lib/content-resolver.d.ts +10 -0
- package/lib/content-resolver.js +98 -0
- package/lib/cq-message.d.ts +14 -0
- package/lib/cq-message.js +106 -0
- package/lib/endpoint.d.ts +30 -0
- package/lib/endpoint.js +1003 -0
- package/lib/forward-msg.d.ts +6 -0
- package/lib/forward-msg.js +107 -0
- package/lib/icqq-guild.d.ts +57 -0
- package/lib/icqq-guild.js +109 -0
- package/lib/icqq-inbound.d.ts +165 -0
- package/lib/icqq-inbound.js +651 -0
- package/lib/icqq-inbox.d.ts +25 -0
- package/lib/icqq-inbox.js +129 -0
- package/lib/icqq-login-assist.d.ts +27 -0
- package/lib/icqq-login-assist.js +109 -0
- package/lib/icqq-runtime-state.d.ts +1 -0
- package/lib/icqq-runtime-state.js +6 -0
- package/lib/index.d.ts +5 -18
- package/lib/index.js +5 -124
- package/lib/outbound-media.d.ts +14 -0
- package/lib/outbound-media.js +76 -0
- package/lib/protocol.d.ts +258 -0
- package/lib/protocol.js +422 -0
- package/lib/types.d.ts +33 -13
- package/lib/types.js +0 -1
- package/package.json +86 -32
- package/plugin.js +19 -0
- package/prompt-sections/platform/index.js +16 -0
- package/prompt-sections/platform/index.ts +16 -0
- package/schema.json +168 -0
- package/src/client.ts +29 -0
- package/src/content-resolver.ts +131 -0
- package/src/cq-message.ts +113 -0
- package/src/endpoint.ts +1084 -0
- package/src/forward-msg.ts +112 -0
- package/src/icqq-guild.ts +154 -0
- package/src/icqq-inbound.ts +814 -0
- package/src/icqq-inbox.ts +147 -0
- package/src/icqq-login-assist.ts +132 -0
- package/src/icqq-runtime-state.ts +7 -0
- package/src/index.ts +30 -149
- package/src/outbound-media.ts +103 -0
- package/src/protocol.ts +551 -0
- package/src/types.ts +35 -13
- package/LICENSE +0 -21
- package/client/ICQQManagement.tsx +0 -139
- package/client/LoginAssistPanel.tsx +0 -293
- package/client/index.tsx +0 -11
- package/client/tsconfig.json +0 -7
- package/client/utils/api.ts +0 -20
- package/dist/index.js +0 -1
- package/lib/adapter.d.ts +0 -34
- package/lib/adapter.d.ts.map +0 -1
- package/lib/adapter.js +0 -396
- package/lib/adapter.js.map +0 -1
- package/lib/bot.d.ts +0 -46
- package/lib/bot.d.ts.map +0 -1
- package/lib/bot.js +0 -457
- package/lib/bot.js.map +0 -1
- package/lib/index.d.ts.map +0 -1
- package/lib/index.js.map +0 -1
- package/lib/types.d.ts.map +0 -1
- package/lib/types.js.map +0 -1
- package/skills/icqq/SKILL.md +0 -22
- package/src/adapter.ts +0 -448
- package/src/bot.ts +0 -521
package/lib/types.d.ts
CHANGED
|
@@ -1,8 +1,4 @@
|
|
|
1
|
-
|
|
2
|
-
* ICQQ 适配器类型与配置
|
|
3
|
-
*/
|
|
4
|
-
import type { Config, GroupRole, MemberInfo } from "@icqqjs/icqq";
|
|
5
|
-
export type { Config, GroupRole, MemberInfo };
|
|
1
|
+
export type GroupRole = "owner" | "admin" | "member";
|
|
6
2
|
export interface IcqqSenderInfo {
|
|
7
3
|
id: string;
|
|
8
4
|
name: string;
|
|
@@ -13,13 +9,37 @@ export interface IcqqSenderInfo {
|
|
|
13
9
|
card?: string;
|
|
14
10
|
title?: string;
|
|
15
11
|
}
|
|
16
|
-
export interface
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
12
|
+
export interface FriendInfo {
|
|
13
|
+
user_id: number;
|
|
14
|
+
nickname: string;
|
|
15
|
+
remark?: string;
|
|
16
|
+
class_id?: number;
|
|
21
17
|
}
|
|
22
|
-
export interface
|
|
23
|
-
|
|
18
|
+
export interface GroupInfo {
|
|
19
|
+
group_id: number;
|
|
20
|
+
group_name: string;
|
|
21
|
+
member_count: number;
|
|
22
|
+
max_member_count: number;
|
|
23
|
+
owner_id?: number;
|
|
24
|
+
}
|
|
25
|
+
export interface MemberInfo {
|
|
26
|
+
user_id: number;
|
|
27
|
+
nickname: string;
|
|
28
|
+
card: string;
|
|
29
|
+
role: GroupRole;
|
|
30
|
+
title: string;
|
|
31
|
+
join_time?: number;
|
|
32
|
+
last_sent_time?: number;
|
|
33
|
+
shutup_time?: number;
|
|
34
|
+
}
|
|
35
|
+
export interface SystemMessage {
|
|
36
|
+
type: string;
|
|
37
|
+
user_id?: number;
|
|
38
|
+
nickname?: string;
|
|
39
|
+
group_id?: number;
|
|
40
|
+
group_name?: string;
|
|
41
|
+
comment?: string;
|
|
42
|
+
flag?: string;
|
|
43
|
+
seq?: number;
|
|
44
|
+
time?: number;
|
|
24
45
|
}
|
|
25
|
-
//# sourceMappingURL=types.d.ts.map
|
package/lib/types.js
CHANGED
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@zhin.js/adapter-icqq",
|
|
3
|
-
"version": "1.
|
|
4
|
-
"description": "Zhin.js adapter
|
|
3
|
+
"version": "1.1.2",
|
|
4
|
+
"description": "Zhin.js ICQQ adapter — direct connection to @icqqjs/icqq",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "./lib/index.js",
|
|
7
7
|
"types": "./lib/index.d.ts",
|
|
@@ -33,52 +33,106 @@
|
|
|
33
33
|
"type": "git",
|
|
34
34
|
"directory": "plugins/adapters/icqq"
|
|
35
35
|
},
|
|
36
|
-
"files": [
|
|
37
|
-
"src",
|
|
38
|
-
"lib",
|
|
39
|
-
"client",
|
|
40
|
-
"dist",
|
|
41
|
-
"skills",
|
|
42
|
-
"README.md",
|
|
43
|
-
"CHANGELOG.md"
|
|
44
|
-
],
|
|
45
36
|
"dependencies": {
|
|
46
|
-
"@
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
"@
|
|
50
|
-
"@
|
|
51
|
-
"@
|
|
52
|
-
"
|
|
53
|
-
"
|
|
54
|
-
"
|
|
55
|
-
"@zhin.js/console": "1.0.59",
|
|
56
|
-
"zhin.js": "1.0.57"
|
|
37
|
+
"@zhin.js/adapter": "1.1.14",
|
|
38
|
+
"@zhin.js/agent-feature": "1.1.1",
|
|
39
|
+
"@zhin.js/core": "1.1.37",
|
|
40
|
+
"@zhin.js/feature-kit": "1.1.1",
|
|
41
|
+
"@zhin.js/im-contract": "1.1.1",
|
|
42
|
+
"@zhin.js/logger": "1.1.1",
|
|
43
|
+
"@zhin.js/permission": "1.1.1",
|
|
44
|
+
"@zhin.js/skill": "1.1.1",
|
|
45
|
+
"@zhin.js/tool": "1.1.1"
|
|
57
46
|
},
|
|
58
47
|
"peerDependencies": {
|
|
59
|
-
"@
|
|
60
|
-
"
|
|
61
|
-
"@zhin.js/
|
|
62
|
-
"@zhin.js/
|
|
63
|
-
"zhin.js": "1.
|
|
48
|
+
"@icqqjs/icqq": "^1.12.2",
|
|
49
|
+
"zod": "^4.0.0",
|
|
50
|
+
"@zhin.js/adapter": "^1.1.14",
|
|
51
|
+
"@zhin.js/command": "^1.1.1",
|
|
52
|
+
"@zhin.js/core": "^1.1.37",
|
|
53
|
+
"@zhin.js/prompt-section": "^1.1.1",
|
|
54
|
+
"zhin.js": "^1.1.2"
|
|
64
55
|
},
|
|
65
56
|
"peerDependenciesMeta": {
|
|
66
|
-
"@
|
|
57
|
+
"@icqqjs/icqq": {
|
|
58
|
+
"optional": true
|
|
59
|
+
},
|
|
60
|
+
"@zhin.js/command": {
|
|
61
|
+
"optional": true
|
|
62
|
+
},
|
|
63
|
+
"@zhin.js/prompt-section": {
|
|
67
64
|
"optional": true
|
|
68
65
|
},
|
|
69
|
-
"
|
|
66
|
+
"zhin.js": {
|
|
70
67
|
"optional": true
|
|
71
68
|
},
|
|
72
|
-
"
|
|
69
|
+
"zod": {
|
|
73
70
|
"optional": true
|
|
74
71
|
}
|
|
75
72
|
},
|
|
73
|
+
"devDependencies": {
|
|
74
|
+
"@icqqjs/icqq": "^1.12.2",
|
|
75
|
+
"@types/node": "^26.1.2",
|
|
76
|
+
"typescript": "^6.0.3",
|
|
77
|
+
"vitest": "^4.1.10",
|
|
78
|
+
"zod": "^4.4.3",
|
|
79
|
+
"@zhin.js/prompt-section": "1.1.1",
|
|
80
|
+
"zhin.js": "1.1.2"
|
|
81
|
+
},
|
|
82
|
+
"files": [
|
|
83
|
+
"adapters",
|
|
84
|
+
"commands",
|
|
85
|
+
"plugin.js",
|
|
86
|
+
"schema.json",
|
|
87
|
+
"src",
|
|
88
|
+
"lib",
|
|
89
|
+
"agents",
|
|
90
|
+
"README.md",
|
|
91
|
+
"CHANGELOG.md",
|
|
92
|
+
"PERMITS.md",
|
|
93
|
+
"prompt-sections"
|
|
94
|
+
],
|
|
76
95
|
"publishConfig": {
|
|
77
96
|
"access": "public",
|
|
78
97
|
"registry": "https://registry.npmjs.org"
|
|
79
98
|
},
|
|
99
|
+
"engines": {
|
|
100
|
+
"node": "^20.19.0 || >=22.12.0"
|
|
101
|
+
},
|
|
102
|
+
"zhin": {
|
|
103
|
+
"protocol": 1,
|
|
104
|
+
"type": "plugin",
|
|
105
|
+
"entry": "./plugin.js",
|
|
106
|
+
"engine": "^1.0.0",
|
|
107
|
+
"runtime": "trusted",
|
|
108
|
+
"features": [
|
|
109
|
+
{
|
|
110
|
+
"package": "@zhin.js/adapter",
|
|
111
|
+
"api": "^1.0.0"
|
|
112
|
+
},
|
|
113
|
+
{
|
|
114
|
+
"package": "@zhin.js/command",
|
|
115
|
+
"api": "^1.0.0"
|
|
116
|
+
},
|
|
117
|
+
{
|
|
118
|
+
"package": "@zhin.js/tool",
|
|
119
|
+
"api": "^1.0.0"
|
|
120
|
+
},
|
|
121
|
+
{
|
|
122
|
+
"package": "@zhin.js/skill",
|
|
123
|
+
"api": "^1.0.0"
|
|
124
|
+
},
|
|
125
|
+
{
|
|
126
|
+
"package": "@zhin.js/agent-feature",
|
|
127
|
+
"api": "^1.0.0"
|
|
128
|
+
}
|
|
129
|
+
],
|
|
130
|
+
"plugins": []
|
|
131
|
+
},
|
|
80
132
|
"scripts": {
|
|
81
|
-
"build": "tsc
|
|
82
|
-
"clean": "rimraf lib"
|
|
133
|
+
"build": "tsc",
|
|
134
|
+
"clean": "rimraf lib",
|
|
135
|
+
"test": "pnpm run test:types && NODE_OPTIONS=--experimental-strip-types vitest run --root ../../.. plugins/adapters/icqq/tests",
|
|
136
|
+
"test:types": "tsc -p tsconfig.type-tests.json"
|
|
83
137
|
}
|
|
84
138
|
}
|
package/plugin.js
ADDED
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
// Generated by build-plugin-runtime-entries.mjs. Do not edit.
|
|
2
|
+
import { createEndpointRuntimeState } from 'zhin.js/adapter';
|
|
3
|
+
import { definePlugin } from 'zhin.js';
|
|
4
|
+
import { permissionHostToken, createSceneRolePlatformChecker } from '@zhin.js/permission';
|
|
5
|
+
import { icqqRuntimeStateToken } from "./lib/icqq-runtime-state.js";
|
|
6
|
+
export default definePlugin({
|
|
7
|
+
name: 'icqq',
|
|
8
|
+
metadata: {
|
|
9
|
+
displayName: 'ICQQ Adapter',
|
|
10
|
+
},
|
|
11
|
+
setup(context) {
|
|
12
|
+
// 运行中 endpoint 注册表(icqq endpoint list 的"运行中"数据源)
|
|
13
|
+
context.resources.provide(icqqRuntimeStateToken, createEndpointRuntimeState());
|
|
14
|
+
const disposePlatform = context.resources.has(permissionHostToken)
|
|
15
|
+
? context.resources.use(permissionHostToken).registerPlatform('icqq', createSceneRolePlatformChecker())
|
|
16
|
+
: undefined;
|
|
17
|
+
return disposePlatform;
|
|
18
|
+
},
|
|
19
|
+
});
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
// Generated by build-plugin-runtime-entries.mjs. Do not edit.
|
|
2
|
+
import { defineAgentPromptSection } from '@zhin.js/prompt-section';
|
|
3
|
+
export default defineAgentPromptSection({
|
|
4
|
+
title: 'ICQQ / QQ',
|
|
5
|
+
content: [
|
|
6
|
+
'ICQQ capabilities are grouped into directory, interaction, group-admin, and group-state Skills.',
|
|
7
|
+
'Use discover for the user intent, then load only the matching Skill before calling its private Tool.',
|
|
8
|
+
'The current IM operation selects the bot Client automatically; never guess user_id or group_id.',
|
|
9
|
+
].join('\n'),
|
|
10
|
+
layer: 'tools',
|
|
11
|
+
order: 70,
|
|
12
|
+
retention: 'preferred',
|
|
13
|
+
maxChars: 1200,
|
|
14
|
+
profiles: ['interactive'],
|
|
15
|
+
platforms: ['icqq'],
|
|
16
|
+
});
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
import { defineAgentPromptSection } from '@zhin.js/prompt-section';
|
|
2
|
+
|
|
3
|
+
export default defineAgentPromptSection({
|
|
4
|
+
title: 'ICQQ / QQ',
|
|
5
|
+
content: [
|
|
6
|
+
'ICQQ capabilities are grouped into directory, interaction, group-admin, and group-state Skills.',
|
|
7
|
+
'Use discover for the user intent, then load only the matching Skill before calling its private Tool.',
|
|
8
|
+
'The current IM operation selects the bot Client automatically; never guess user_id or group_id.',
|
|
9
|
+
].join('\n'),
|
|
10
|
+
layer: 'tools',
|
|
11
|
+
order: 70,
|
|
12
|
+
retention: 'preferred',
|
|
13
|
+
maxChars: 1200,
|
|
14
|
+
profiles: ['interactive'],
|
|
15
|
+
platforms: ['icqq'],
|
|
16
|
+
});
|
package/schema.json
ADDED
|
@@ -0,0 +1,168 @@
|
|
|
1
|
+
{
|
|
2
|
+
"$schema": "https://json-schema.org/draft/2020-12/schema",
|
|
3
|
+
"type": "object",
|
|
4
|
+
"additionalProperties": false,
|
|
5
|
+
"properties": {
|
|
6
|
+
"master": {
|
|
7
|
+
"type": ["string", "number"],
|
|
8
|
+
"description": "框架 master QQ uin(/approve、AI/工具权限);endpoints[i].master 可逐项覆盖"
|
|
9
|
+
},
|
|
10
|
+
"trusted": {
|
|
11
|
+
"type": "array",
|
|
12
|
+
"items": { "type": ["string", "number"] },
|
|
13
|
+
"description": "框架 trusted QQ uin 列表(弱于 master)"
|
|
14
|
+
},
|
|
15
|
+
"password": {
|
|
16
|
+
"type": "string",
|
|
17
|
+
"description": "QQ 密码(可选,不填则扫码登录)"
|
|
18
|
+
},
|
|
19
|
+
"platform": {
|
|
20
|
+
"type": "number",
|
|
21
|
+
"enum": [1, 2, 3, 4, 5, 6, 7],
|
|
22
|
+
"default": 1,
|
|
23
|
+
"description": "登录设备平台:1=Android, 2=aPad, 3=Watch, 4=iMac, 5=iPad, 6=Tim, 7=Custom"
|
|
24
|
+
},
|
|
25
|
+
"ver": {
|
|
26
|
+
"type": "string",
|
|
27
|
+
"description": "协议版本号,仅在对应 platform 有多个版本时有效,不填则使用最新版本"
|
|
28
|
+
},
|
|
29
|
+
"signApiAddr": {
|
|
30
|
+
"type": "string",
|
|
31
|
+
"description": "签名服务器地址;未配置时若安装了 @icqqjs/qqsign 则自动使用本地签名"
|
|
32
|
+
},
|
|
33
|
+
"dataDir": {
|
|
34
|
+
"type": "string",
|
|
35
|
+
"description": "数据存储文件夹路径,默认为主模块下的 data 文件夹"
|
|
36
|
+
},
|
|
37
|
+
"autoReconnect": {
|
|
38
|
+
"type": "boolean",
|
|
39
|
+
"default": true,
|
|
40
|
+
"description": "断线后是否自动重连(默认 true)"
|
|
41
|
+
},
|
|
42
|
+
"outboundMedia": {
|
|
43
|
+
"type": "string",
|
|
44
|
+
"enum": ["file", "base64"],
|
|
45
|
+
"default": "file",
|
|
46
|
+
"description": "出站媒体模式:file=落盘本地路径(默认); base64=CQ base64:// 内联"
|
|
47
|
+
},
|
|
48
|
+
"ignoreSelf": {
|
|
49
|
+
"type": "boolean",
|
|
50
|
+
"default": true,
|
|
51
|
+
"description": "群聊和频道中是否过滤自己的消息(默认 true)"
|
|
52
|
+
},
|
|
53
|
+
"resend": {
|
|
54
|
+
"type": "boolean",
|
|
55
|
+
"default": false,
|
|
56
|
+
"description": "被风控时是否尝试用分片发送(默认 false)"
|
|
57
|
+
},
|
|
58
|
+
"cacheGroupMember": {
|
|
59
|
+
"type": "boolean",
|
|
60
|
+
"default": true,
|
|
61
|
+
"description": "是否缓存群员列表(默认 true);群多时(500+)会多占约 100MB+ 内存"
|
|
62
|
+
},
|
|
63
|
+
"autoServer": {
|
|
64
|
+
"type": "boolean",
|
|
65
|
+
"default": true,
|
|
66
|
+
"description": "是否自动选择最优服务器(默认 true)"
|
|
67
|
+
},
|
|
68
|
+
"qqnt": {
|
|
69
|
+
"type": "boolean",
|
|
70
|
+
"default": true,
|
|
71
|
+
"description": "是否使用 QQNT 协议(默认 true)"
|
|
72
|
+
},
|
|
73
|
+
"ntLogin": {
|
|
74
|
+
"type": "boolean",
|
|
75
|
+
"description": "是否使用 NT 登录方式"
|
|
76
|
+
},
|
|
77
|
+
"endpoints": {
|
|
78
|
+
"type": "array",
|
|
79
|
+
"minItems": 1,
|
|
80
|
+
"description": "多账号:一个插件实例挂多个 endpoint。每项与顶层字段同构(id 必填,其余覆盖顶层)",
|
|
81
|
+
"items": {
|
|
82
|
+
"type": "object",
|
|
83
|
+
"additionalProperties": false,
|
|
84
|
+
"properties": {
|
|
85
|
+
"id": {
|
|
86
|
+
"type": "string",
|
|
87
|
+
"pattern": "^[0-9]+$",
|
|
88
|
+
"description": "QQ uin"
|
|
89
|
+
},
|
|
90
|
+
"master": {
|
|
91
|
+
"type": ["string", "number"],
|
|
92
|
+
"description": "本 endpoint 的 master QQ uin;覆盖顶层 master"
|
|
93
|
+
},
|
|
94
|
+
"trusted": {
|
|
95
|
+
"type": "array",
|
|
96
|
+
"items": { "type": ["string", "number"] },
|
|
97
|
+
"description": "本 endpoint 的 trusted 列表"
|
|
98
|
+
},
|
|
99
|
+
"password": {
|
|
100
|
+
"type": "string",
|
|
101
|
+
"description": "本 endpoint 的 QQ 密码"
|
|
102
|
+
},
|
|
103
|
+
"platform": {
|
|
104
|
+
"type": "number",
|
|
105
|
+
"enum": [1, 2, 3, 4, 5, 6, 7],
|
|
106
|
+
"description": "登录设备平台:1=Android, 2=aPad, 3=Watch, 4=iMac, 5=iPad, 6=Tim, 7=Custom"
|
|
107
|
+
},
|
|
108
|
+
"ver": {
|
|
109
|
+
"type": "string",
|
|
110
|
+
"description": "协议版本号"
|
|
111
|
+
},
|
|
112
|
+
"signApiAddr": {
|
|
113
|
+
"type": "string",
|
|
114
|
+
"description": "签名服务器地址"
|
|
115
|
+
},
|
|
116
|
+
"dataDir": {
|
|
117
|
+
"type": "string",
|
|
118
|
+
"description": "数据存储文件夹路径"
|
|
119
|
+
},
|
|
120
|
+
"autoReconnect": {
|
|
121
|
+
"type": "boolean",
|
|
122
|
+
"description": "断线后是否自动重连"
|
|
123
|
+
},
|
|
124
|
+
"outboundMedia": {
|
|
125
|
+
"type": "string",
|
|
126
|
+
"enum": ["file", "base64"],
|
|
127
|
+
"description": "出站媒体模式"
|
|
128
|
+
},
|
|
129
|
+
"ignoreSelf": {
|
|
130
|
+
"type": "boolean",
|
|
131
|
+
"description": "是否过滤自己的消息"
|
|
132
|
+
},
|
|
133
|
+
"resend": {
|
|
134
|
+
"type": "boolean",
|
|
135
|
+
"description": "被风控时是否分片发送"
|
|
136
|
+
},
|
|
137
|
+
"cacheGroupMember": {
|
|
138
|
+
"type": "boolean",
|
|
139
|
+
"description": "是否缓存群员列表"
|
|
140
|
+
},
|
|
141
|
+
"autoServer": {
|
|
142
|
+
"type": "boolean",
|
|
143
|
+
"description": "是否自动选择最优服务器"
|
|
144
|
+
},
|
|
145
|
+
"qqnt": {
|
|
146
|
+
"type": "boolean",
|
|
147
|
+
"description": "是否使用 QQNT 协议"
|
|
148
|
+
},
|
|
149
|
+
"ntLogin": {
|
|
150
|
+
"type": "boolean",
|
|
151
|
+
"description": "是否使用 NT 登录方式"
|
|
152
|
+
},
|
|
153
|
+
"commandPrefix": {
|
|
154
|
+
"type": "string",
|
|
155
|
+
"description": "本 endpoint 的命令前缀"
|
|
156
|
+
}
|
|
157
|
+
},
|
|
158
|
+
"required": ["id"]
|
|
159
|
+
}
|
|
160
|
+
},
|
|
161
|
+
"commandPrefix": {
|
|
162
|
+
"type": "string",
|
|
163
|
+
"default": "",
|
|
164
|
+
"description": "命令前缀(默认 '' 无前缀)。endpoints[i] 可逐项覆盖"
|
|
165
|
+
}
|
|
166
|
+
},
|
|
167
|
+
"required": ["master", "endpoints"]
|
|
168
|
+
}
|
package/src/client.ts
ADDED
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
import type { Client, EventMap } from '@icqqjs/icqq';
|
|
2
|
+
import { defineEndpointClient } from 'zhin.js/adapter';
|
|
3
|
+
|
|
4
|
+
type EventPayload<TListener> = TListener extends (
|
|
5
|
+
event: infer TEvent,
|
|
6
|
+
...rest: readonly unknown[]
|
|
7
|
+
) => unknown
|
|
8
|
+
? TEvent
|
|
9
|
+
: never;
|
|
10
|
+
|
|
11
|
+
/** The actual ICQQ SDK instance exposed by every ICQQ Endpoint event. */
|
|
12
|
+
export type IcqqClient = Client;
|
|
13
|
+
|
|
14
|
+
/** Native ICQQ event-name → first public event payload, derived from the SDK SSOT. */
|
|
15
|
+
export type IcqqClientEventMap = {
|
|
16
|
+
readonly [TName in keyof EventMap]: EventPayload<EventMap[TName]>;
|
|
17
|
+
};
|
|
18
|
+
|
|
19
|
+
declare module '@zhin.js/feature-kit' {
|
|
20
|
+
interface AdapterClientRegistry {
|
|
21
|
+
readonly icqq: {
|
|
22
|
+
readonly client: IcqqClient;
|
|
23
|
+
readonly events: IcqqClientEventMap;
|
|
24
|
+
};
|
|
25
|
+
}
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
/** Typed, generation-checked identity used by handlers, commands, tools and schedules. */
|
|
29
|
+
export const icqqClient = defineEndpointClient<IcqqClient, IcqqClientEventMap>('icqq');
|
|
@@ -0,0 +1,131 @@
|
|
|
1
|
+
import type { Client } from '@icqqjs/icqq';
|
|
2
|
+
import type {
|
|
3
|
+
EndpointContentPort,
|
|
4
|
+
EndpointContentResolveContext,
|
|
5
|
+
} from 'zhin.js/adapter';
|
|
6
|
+
import type {
|
|
7
|
+
ConversationMessage,
|
|
8
|
+
ConversationReference,
|
|
9
|
+
ForwardEntry,
|
|
10
|
+
Segment,
|
|
11
|
+
} from '@zhin.js/im-contract';
|
|
12
|
+
import { normalizeForwardMsgResponse } from './forward-msg.js';
|
|
13
|
+
|
|
14
|
+
/** Owns ICQQ-observed message content and recursive merged-forward expansion. */
|
|
15
|
+
export class IcqqContentResolver {
|
|
16
|
+
readonly #client: Client;
|
|
17
|
+
readonly #messages = new Map<string, ConversationMessage>();
|
|
18
|
+
|
|
19
|
+
readonly port: EndpointContentPort = Object.freeze({
|
|
20
|
+
resolve: (
|
|
21
|
+
reference: ConversationReference,
|
|
22
|
+
context: EndpointContentResolveContext,
|
|
23
|
+
) => this.#resolve(reference, context),
|
|
24
|
+
});
|
|
25
|
+
|
|
26
|
+
constructor(client: Client) {
|
|
27
|
+
this.#client = client;
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
remember(messageId: string, message: ConversationMessage): void {
|
|
31
|
+
this.#messages.set(messageId, message);
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
async #resolve(
|
|
35
|
+
reference: ConversationReference,
|
|
36
|
+
context: EndpointContentResolveContext,
|
|
37
|
+
) {
|
|
38
|
+
context.signal.throwIfAborted();
|
|
39
|
+
if (reference.kind === 'message') {
|
|
40
|
+
const cached = this.#messages.get(reference.message.id);
|
|
41
|
+
return cached
|
|
42
|
+
? Object.freeze({ status: 'resolved' as const, reference, value: cached })
|
|
43
|
+
: Object.freeze({ status: 'not_found' as const, code: 'icqq_message_not_observed' });
|
|
44
|
+
}
|
|
45
|
+
if (reference.kind === 'forward') {
|
|
46
|
+
try {
|
|
47
|
+
const entries = await this.#resolveForwardEntries(
|
|
48
|
+
reference.forwardId,
|
|
49
|
+
context,
|
|
50
|
+
{ remainingEntries: context.maxEntries, path: new Set<string>() },
|
|
51
|
+
0,
|
|
52
|
+
);
|
|
53
|
+
if (entries.length === 0) {
|
|
54
|
+
return Object.freeze({ status: 'not_found' as const, code: 'icqq_forward_not_found' });
|
|
55
|
+
}
|
|
56
|
+
return Object.freeze({
|
|
57
|
+
status: 'resolved' as const,
|
|
58
|
+
reference,
|
|
59
|
+
value: Object.freeze(entries),
|
|
60
|
+
});
|
|
61
|
+
} catch (error) {
|
|
62
|
+
if (context.signal.aborted) throw error;
|
|
63
|
+
return Object.freeze({
|
|
64
|
+
status: 'failed' as const,
|
|
65
|
+
code: 'icqq_forward_fetch_failed',
|
|
66
|
+
message: error instanceof Error ? error.message : String(error),
|
|
67
|
+
});
|
|
68
|
+
}
|
|
69
|
+
}
|
|
70
|
+
if (
|
|
71
|
+
reference.media.kind === 'url'
|
|
72
|
+
|| reference.media.kind === 'base64'
|
|
73
|
+
|| reference.media.kind === 'path'
|
|
74
|
+
) {
|
|
75
|
+
return Object.freeze({ status: 'resolved' as const, reference, value: reference.media });
|
|
76
|
+
}
|
|
77
|
+
return Object.freeze({
|
|
78
|
+
status: 'unsupported' as const,
|
|
79
|
+
code: 'icqq_media_reference_unsupported',
|
|
80
|
+
});
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
async #resolveForwardEntries(
|
|
84
|
+
forwardId: string,
|
|
85
|
+
context: EndpointContentResolveContext,
|
|
86
|
+
state: { remainingEntries: number; readonly path: Set<string> },
|
|
87
|
+
depth: number,
|
|
88
|
+
): Promise<readonly ForwardEntry[]> {
|
|
89
|
+
context.signal.throwIfAborted();
|
|
90
|
+
if (state.remainingEntries <= 0 || state.path.has(forwardId)) return Object.freeze([]);
|
|
91
|
+
state.path.add(forwardId);
|
|
92
|
+
try {
|
|
93
|
+
// ICQQ has no cancellable getForwardMsg API. Keep the generation lease
|
|
94
|
+
// until the native operation settles, then honor cancellation.
|
|
95
|
+
const raw = await this.#client.getForwardMsg(forwardId);
|
|
96
|
+
context.signal.throwIfAborted();
|
|
97
|
+
const entries = normalizeForwardMsgResponse(raw).slice(0, state.remainingEntries);
|
|
98
|
+
state.remainingEntries -= entries.length;
|
|
99
|
+
const expanded: ForwardEntry[] = [];
|
|
100
|
+
for (const entry of entries) {
|
|
101
|
+
context.signal.throwIfAborted();
|
|
102
|
+
const segments: Segment[] = [];
|
|
103
|
+
for (const segment of entry.segments) {
|
|
104
|
+
if (segment.type !== 'forward' || depth >= context.maxDepth) {
|
|
105
|
+
segments.push(segment);
|
|
106
|
+
continue;
|
|
107
|
+
}
|
|
108
|
+
const nestedId = String(
|
|
109
|
+
(segment.data as { forward_id?: unknown }).forward_id ?? '',
|
|
110
|
+
).trim();
|
|
111
|
+
if (!nestedId || state.path.has(nestedId) || state.remainingEntries <= 0) {
|
|
112
|
+
segments.push(segment);
|
|
113
|
+
continue;
|
|
114
|
+
}
|
|
115
|
+
const nested = await this.#resolveForwardEntries(nestedId, context, state, depth + 1);
|
|
116
|
+
segments.push(Object.freeze({
|
|
117
|
+
...segment,
|
|
118
|
+
data: Object.freeze({
|
|
119
|
+
...segment.data,
|
|
120
|
+
...(nested.length > 0 ? { entries: nested } : {}),
|
|
121
|
+
}),
|
|
122
|
+
}));
|
|
123
|
+
}
|
|
124
|
+
expanded.push(Object.freeze({ ...entry, segments: Object.freeze(segments) }));
|
|
125
|
+
}
|
|
126
|
+
return Object.freeze(expanded);
|
|
127
|
+
} finally {
|
|
128
|
+
state.path.delete(forwardId);
|
|
129
|
+
}
|
|
130
|
+
}
|
|
131
|
+
}
|