@zhin.js/adapter-icqq 1.1.19 → 1.1.21
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +36 -0
- package/README.md +10 -0
- package/agents/icqq/skills/icqq-conversation/SKILL.md +2 -2
- package/agents/icqq/skills/icqq-conversation/tools/conversation_info/index.js +1 -1
- package/agents/icqq/skills/icqq-conversation/tools/conversation_info/index.ts +1 -1
- package/agents/icqq/skills/icqq-group-admin/SKILL.md +3 -3
- package/agents/icqq/skills/icqq-group-admin/tools/group_leave/index.js +1 -1
- package/agents/icqq/skills/icqq-group-admin/tools/group_leave/index.ts +1 -1
- package/agents/icqq/skills/icqq-group-admin/tools/recall_member_message/index.js +1 -1
- package/agents/icqq/skills/icqq-group-admin/tools/recall_member_message/index.ts +1 -1
- package/agents/icqq/skills/icqq-messages/SKILL.md +1 -1
- package/agents/icqq/skills/icqq-messages/tools/recall_own_message/index.js +1 -1
- package/agents/icqq/skills/icqq-messages/tools/recall_own_message/index.ts +1 -1
- package/lib/endpoint.js +2 -1
- package/lib/protocol.d.ts +2 -1
- package/lib/self-profile.d.ts +6 -0
- package/lib/self-profile.js +56 -0
- package/package.json +20 -20
- package/prompt-sections/platform/index.js +1 -1
- package/prompt-sections/platform/index.ts +1 -1
- package/schema.json +106 -1
- package/src/endpoint.ts +2 -1
- package/src/protocol.ts +2 -1
- package/src/self-profile.ts +55 -0
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,41 @@
|
|
|
1
1
|
# @zhin.js/adapter-icqq
|
|
2
2
|
|
|
3
|
+
## 1.1.21
|
|
4
|
+
|
|
5
|
+
### Patch Changes
|
|
6
|
+
|
|
7
|
+
- af8f803: Use the current Endpoint's platform profile for Agent account identity, with per-field configured fallbacks and scoped, identity-approved profile tools. Keep SOUL first-person and independent of account names. Profile ports preserve the source Client, cancellation, truthful update outcomes and private image authority.
|
|
8
|
+
|
|
9
|
+
Adapter Feature API 2.1 and Tool Feature API 1.1 describe the additive profile requirements; new Core, official adapters and generated plugins declare the corresponding floors. Existing compatible API-2.0/API-1.0 plugins remain accepted by the newer providers. npm releases remain patch.
|
|
10
|
+
|
|
11
|
+
Private IM assets now bind to the full canonical conversation and principal. Existing IM asset records that contain only the earlier lossy hash remain on disk until their original expiry, but cannot be safely rebound; recapture them. No automatic file deletion, platform profile mutation or workspace SOUL rewrite occurs during upgrade.
|
|
12
|
+
|
|
13
|
+
- Updated dependencies [b88e988]
|
|
14
|
+
- Updated dependencies [f78eb84]
|
|
15
|
+
- Updated dependencies [c15cef9]
|
|
16
|
+
- Updated dependencies [b0c02a2]
|
|
17
|
+
- Updated dependencies [af8f803]
|
|
18
|
+
- Updated dependencies [26fda97]
|
|
19
|
+
- Updated dependencies [976886c]
|
|
20
|
+
- Updated dependencies [032dc96]
|
|
21
|
+
- @zhin.js/adapter@1.1.27
|
|
22
|
+
- @zhin.js/client@1.1.23
|
|
23
|
+
- @zhin.js/im-contract@1.1.10
|
|
24
|
+
- @zhin.js/core@1.1.55
|
|
25
|
+
- @zhin.js/tool@1.1.14
|
|
26
|
+
- @zhin.js/page@1.1.13
|
|
27
|
+
- @zhin.js/agent-feature@1.1.12
|
|
28
|
+
- @zhin.js/feature-kit@1.1.11
|
|
29
|
+
- @zhin.js/permission@1.1.11
|
|
30
|
+
- @zhin.js/skill@1.1.12
|
|
31
|
+
|
|
32
|
+
## 1.1.20
|
|
33
|
+
|
|
34
|
+
### Patch Changes
|
|
35
|
+
|
|
36
|
+
- Updated dependencies [5e878ba]
|
|
37
|
+
- @zhin.js/page@1.1.12
|
|
38
|
+
|
|
3
39
|
## 1.1.19
|
|
4
40
|
|
|
5
41
|
### Patch Changes
|
package/README.md
CHANGED
|
@@ -249,3 +249,13 @@ ICQQ 要求分享、文件、语音、视频、JSON 卡片和合并转发单独
|
|
|
249
249
|
Endpoint 管理和工具反馈跟随当前操作语言;登录没有当前聊天发送者,LoginAssist 提示采用项目语言。提示文案变化不改变扫码、短信、滑块验证过程,群管理权限、审批与平台结果检查保持原规则。
|
|
250
250
|
|
|
251
251
|
Endpoint 添加用法中的调用头由当前环境的实际命令前缀与插件命名空间提供;未知时只提示所需参数,不生成猜测命令。
|
|
252
|
+
|
|
253
|
+
## 当前账号资料
|
|
254
|
+
|
|
255
|
+
`endpointSpec.profile` 接到原生 ICQQ Client 的资料卡与设置方法;Agent 通过统一的 `get_self_profile` / `set_self_profile` 工具访问当前账号,修改沿用身份类审批。平台资料优先于配置兜底,群名片仍是独立的场景资料。
|
|
256
|
+
|
|
257
|
+
- 可读:昵称、性别、年龄、个性签名、头像 URL。只投影这些公开资料,不把 SDK 返回的邮箱、QID 等一起交给模型。
|
|
258
|
+
- 可写:昵称、性别、签名、个人说明、头像。SDK 返回 `false` 时明确拒绝,不报告成功。头像输入经工具链解析为已授权图片,结果不回传图片 base64。
|
|
259
|
+
- 年龄不反推生日;当前安装的 ICQQ 1.12.3 `setBirthday` 月日编码有缺陷,因此不开放生日写入。兴趣无对应已验证接口,只能使用配置兜底。
|
|
260
|
+
|
|
261
|
+
配置的 `nickname`、`profile` 可放在适配器默认值或某个 `endpoints[]` 项中;Endpoint 逐字段覆盖默认值,只有平台未提供的字段才使用它们。配置兜底不会写回 QQ。
|
|
@@ -15,9 +15,9 @@ tools:
|
|
|
15
15
|
---
|
|
16
16
|
# icqq-conversation
|
|
17
17
|
|
|
18
|
-
Use `conversation_info` before claiming
|
|
18
|
+
Use `conversation_info` before claiming your role or available platform permissions. It uses the current endpoint account, not the sender's role. Group roles are refreshed from the platform on demand; failure is not evidence that you are an ordinary member.
|
|
19
19
|
|
|
20
|
-
Caller roles authorize a request;
|
|
20
|
+
Caller roles authorize a request; your platform roles determine whether QQ can execute it. They are independent. A successful inspection does not replace the live permission check before a management action.
|
|
21
21
|
|
|
22
22
|
Guild member lookup can be incomplete. Missing self membership means `unknown`. A `channel_admin` record does not identify which child channels it governs, so it cannot authorize an operation in the current channel.
|
|
23
23
|
|
|
@@ -3,7 +3,7 @@ import { defineAgentTool } from '@zhin.js/tool';
|
|
|
3
3
|
import { z } from 'zod';
|
|
4
4
|
import { currentConversation } from '../../../../conversation.js';
|
|
5
5
|
export default defineAgentTool({
|
|
6
|
-
description: '读取当前 ICQQ
|
|
6
|
+
description: '读取当前 ICQQ 会话及你自己的群/频道身份;区分调用者权限、你的平台权限和不支持的操作',
|
|
7
7
|
adapter: 'icqq',
|
|
8
8
|
requiresApproval: 'never',
|
|
9
9
|
inputSchema: z.object({}),
|
|
@@ -3,7 +3,7 @@ import { z } from 'zod';
|
|
|
3
3
|
import { currentConversation } from '../../../../conversation.js';
|
|
4
4
|
|
|
5
5
|
export default defineAgentTool({
|
|
6
|
-
description: '读取当前 ICQQ
|
|
6
|
+
description: '读取当前 ICQQ 会话及你自己的群/频道身份;区分调用者权限、你的平台权限和不支持的操作',
|
|
7
7
|
adapter: 'icqq',
|
|
8
8
|
requiresApproval: 'never',
|
|
9
9
|
inputSchema: z.object({}),
|
|
@@ -35,8 +35,8 @@ scopes:
|
|
|
35
35
|
|
|
36
36
|
已配置的自动审核流程可对入群 10 分钟内的明确广告直接踢出;该例外由审核流程核验,不授权本技能或普通对话绕过人工审批。
|
|
37
37
|
|
|
38
|
-
`group_leave` 仅主人(master
|
|
38
|
+
`group_leave` 仅主人(master)可发起,每次人工审批,只能退出当前群。你为群主时拒绝执行,因为 SDK 的退群会解散群。
|
|
39
39
|
|
|
40
|
-
`recall_member_message` 沿用 master/admin/trusted/owner
|
|
40
|
+
`recall_member_message` 沿用 master/admin/trusted/owner 发起权限,无需逐次审批;目标固定在当前群中的真实消息。执行前实时查询你与目标成员的角色:管理员只能撤回普通成员,群主可撤回管理员或普通成员。平台时限与执行结果仍然有效,不自动重试未确认的结果。
|
|
41
41
|
|
|
42
|
-
|
|
42
|
+
撤回你自己的消息不要求群管角色:使用 `icqq-messages` 的 `recall_own_message`。已配置自动群审核的撤回继续走原有审核路径,不因本工具新增审批,也不扩大审核类别或目标群。
|
|
@@ -4,7 +4,7 @@ import { z } from 'zod';
|
|
|
4
4
|
import { currentGroup } from '../../../../conversation.js';
|
|
5
5
|
import { messages as t } from '../../../../messages.js';
|
|
6
6
|
export default defineAgentTool({
|
|
7
|
-
description: '
|
|
7
|
+
description: '退出你当前所在的 QQ 群;仅主人可发起且每次人工审批,你是群主时拒绝执行以免解散群',
|
|
8
8
|
adapter: 'icqq',
|
|
9
9
|
scopes: ['group'],
|
|
10
10
|
currentConversationBotRoles: ['admin', 'member'],
|
|
@@ -4,7 +4,7 @@ import { currentGroup } from '../../../../conversation.js';
|
|
|
4
4
|
import { messages as t } from '../../../../messages.js';
|
|
5
5
|
|
|
6
6
|
export default defineAgentTool({
|
|
7
|
-
description: '
|
|
7
|
+
description: '退出你当前所在的 QQ 群;仅主人可发起且每次人工审批,你是群主时拒绝执行以免解散群',
|
|
8
8
|
adapter: 'icqq',
|
|
9
9
|
scopes: ['group'],
|
|
10
10
|
currentConversationBotRoles: ['admin', 'member'],
|
|
@@ -4,7 +4,7 @@ import { z } from 'zod';
|
|
|
4
4
|
import { currentGroup, recallMessage, resolveMessage } from '../../../../conversation.js';
|
|
5
5
|
import { messages as t } from '../../../../messages.js';
|
|
6
6
|
export default defineAgentTool({
|
|
7
|
-
description: '撤回当前 QQ
|
|
7
|
+
description: '撤回当前 QQ 群内他人的真实消息;需调用方具备群管发起权限及你具备实时平台权限,无需逐次审批',
|
|
8
8
|
adapter: 'icqq',
|
|
9
9
|
scopes: ['group'],
|
|
10
10
|
currentConversationBotRoles: ['owner', 'admin'],
|
|
@@ -4,7 +4,7 @@ import { currentGroup, recallMessage, resolveMessage } from '../../../../convers
|
|
|
4
4
|
import { messages as t } from '../../../../messages.js';
|
|
5
5
|
|
|
6
6
|
export default defineAgentTool({
|
|
7
|
-
description: '撤回当前 QQ
|
|
7
|
+
description: '撤回当前 QQ 群内他人的真实消息;需调用方具备群管发起权限及你具备实时平台权限,无需逐次审批',
|
|
8
8
|
adapter: 'icqq',
|
|
9
9
|
scopes: ['group'],
|
|
10
10
|
currentConversationBotRoles: ['owner', 'admin'],
|
|
@@ -23,6 +23,6 @@ Only use real message IDs from the current conversation. Do not invent IDs, sour
|
|
|
23
23
|
|
|
24
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
25
|
|
|
26
|
-
`recall_own_message` needs no management role or per-call approval, including when
|
|
26
|
+
`recall_own_message` needs no management role or per-call approval, including when you are an ordinary group member. It verifies that you 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
27
|
|
|
28
28
|
These tools currently support QQ private/group conversations, not guild channels. A failed or suppressed outbound receipt is not a delivered message.
|
|
@@ -4,7 +4,7 @@ import { z } from 'zod';
|
|
|
4
4
|
import { recallMessage, resolveMessage } from '../../../../conversation.js';
|
|
5
5
|
import { messages as t } from '../../../../messages.js';
|
|
6
6
|
export default defineAgentTool({
|
|
7
|
-
description: '
|
|
7
|
+
description: '撤回你在当前 QQ 群或私聊中自己的发言;无需群管角色或逐次审批,时限由平台决定',
|
|
8
8
|
adapter: 'icqq',
|
|
9
9
|
scopes: ['private', 'group'],
|
|
10
10
|
requiresApproval: 'never',
|
|
@@ -4,7 +4,7 @@ import { recallMessage, resolveMessage } from '../../../../conversation.js';
|
|
|
4
4
|
import { messages as t } from '../../../../messages.js';
|
|
5
5
|
|
|
6
6
|
export default defineAgentTool({
|
|
7
|
-
description: '
|
|
7
|
+
description: '撤回你在当前 QQ 群或私聊中自己的发言;无需群管角色或逐次审批,时限由平台决定',
|
|
8
8
|
adapter: 'icqq',
|
|
9
9
|
scopes: ['private', 'group'],
|
|
10
10
|
requiresApproval: 'never',
|
package/lib/endpoint.js
CHANGED
|
@@ -15,6 +15,7 @@ import { formatInboundContent, formatOutboundMessageParts, normalizeIcqqImageMed
|
|
|
15
15
|
import { ManagedIcqqClient } from './managed-client.js';
|
|
16
16
|
import { inspectGroupMember, moderateGroupMember } from './group-moderation.js';
|
|
17
17
|
import { inspectGroupMemberProfile, inspectGroupScene } from './group-scene.js';
|
|
18
|
+
import { createIcqqSelfProfile } from './self-profile.js';
|
|
18
19
|
const BOUND_EVENTS = [
|
|
19
20
|
'message.private',
|
|
20
21
|
'message.group',
|
|
@@ -163,7 +164,7 @@ export class IcqqEndpoint extends ManagedIcqqClient {
|
|
|
163
164
|
start: signal => this.#startFramework(signal), open: () => this.#openFramework(), close: () => this.#closeFramework(), stop: () => this.#stopFramework(),
|
|
164
165
|
send: request => this.#sendFramework(request), get name() { return name(); },
|
|
165
166
|
get transportState() { return state(); },
|
|
166
|
-
management: this.#management, control: this.#control,
|
|
167
|
+
management: this.#management, control: this.#control, profile: createIcqqSelfProfile(this),
|
|
167
168
|
get content() { return content(); },
|
|
168
169
|
});
|
|
169
170
|
this.#contentResolver = new IcqqContentResolver(this);
|
package/lib/protocol.d.ts
CHANGED
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import type { EndpointProfileConfiguration } from 'zhin.js/adapter';
|
|
1
2
|
import type { Sendable } from '@icqqjs/icqq';
|
|
2
3
|
import { type ConversationRef } from '@zhin.js/im-contract';
|
|
3
4
|
import { type AdapterNotifyTarget, type ResolvedAdapterNotifyTarget } from 'zhin.js/adapter';
|
|
@@ -163,7 +164,7 @@ export declare const Actions: {
|
|
|
163
164
|
readonly GET_NOTIFY: "get_notify";
|
|
164
165
|
};
|
|
165
166
|
/** One expanded ICQQ endpoint configuration produced by AdapterIndex. */
|
|
166
|
-
export interface IcqqEndpointConfig {
|
|
167
|
+
export interface IcqqEndpointConfig extends EndpointProfileConfiguration {
|
|
167
168
|
readonly id: string;
|
|
168
169
|
readonly autoReconnect?: boolean;
|
|
169
170
|
readonly outboundMedia?: 'file' | 'base64';
|
|
@@ -0,0 +1,6 @@
|
|
|
1
|
+
import type { Client } from '@icqqjs/icqq';
|
|
2
|
+
import { type EndpointProfile } from 'zhin.js/adapter';
|
|
3
|
+
type IcqqProfileClient = Pick<Client, 'uin' | 'nickname' | 'isOnline' | 'getProfile' | 'setNickname' | 'setGender' | 'setSignature' | 'setDescription' | 'setAvatar'>;
|
|
4
|
+
/** ICQQ owns the platform facts; config fallbacks and call leases belong to Adapter. */
|
|
5
|
+
export declare function createIcqqSelfProfile(client: IcqqProfileClient): EndpointProfile;
|
|
6
|
+
export {};
|
|
@@ -0,0 +1,56 @@
|
|
|
1
|
+
import { createEndpointOperation } from 'zhin.js/adapter';
|
|
2
|
+
/** ICQQ owns the platform facts; config fallbacks and call leases belong to Adapter. */
|
|
3
|
+
export function createIcqqSelfProfile(client) {
|
|
4
|
+
return Object.freeze({
|
|
5
|
+
writableFields: Object.freeze(['nickname', 'gender', 'signature', 'bio', 'avatar']),
|
|
6
|
+
get current() { return client.isOnline() ? { nickname: client.nickname } : {}; },
|
|
7
|
+
async read({ signal }) {
|
|
8
|
+
const raw = await createEndpointOperation({ signal }).prepare(() => client.getProfile(client.uin));
|
|
9
|
+
const profile = {
|
|
10
|
+
...(typeof raw.nickname === 'string' ? { nickname: raw.nickname } : {}),
|
|
11
|
+
...(raw.sex === 'male' || raw.sex === 'female' || raw.sex === 'unknown' ? { gender: raw.sex } : {}),
|
|
12
|
+
...(typeof raw.age === 'number' && Number.isFinite(raw.age) ? { age: raw.age } : {}),
|
|
13
|
+
...(typeof raw.signature === 'string' ? { signature: raw.signature } : {}),
|
|
14
|
+
...(typeof raw.avatar?.url === 'string' ? { avatar: raw.avatar.url } : {}),
|
|
15
|
+
};
|
|
16
|
+
return profile;
|
|
17
|
+
},
|
|
18
|
+
async write({ change, signal }) {
|
|
19
|
+
const operation = createEndpointOperation({ signal });
|
|
20
|
+
let accepted;
|
|
21
|
+
switch (change.field) {
|
|
22
|
+
case 'nickname':
|
|
23
|
+
accepted = await operation.run(() => client.setNickname(change.value));
|
|
24
|
+
break;
|
|
25
|
+
case 'gender': {
|
|
26
|
+
if (change.value === 'other')
|
|
27
|
+
return { status: 'rejected', message: 'ICQQ supports male, female or unknown gender' };
|
|
28
|
+
const gender = change.value === 'male' ? 1 : change.value === 'female' ? 2 : 0;
|
|
29
|
+
accepted = await operation.run(() => client.setGender(gender));
|
|
30
|
+
break;
|
|
31
|
+
}
|
|
32
|
+
case 'signature':
|
|
33
|
+
accepted = await operation.run(() => client.setSignature(change.value));
|
|
34
|
+
break;
|
|
35
|
+
case 'bio':
|
|
36
|
+
accepted = await operation.run(() => client.setDescription(change.value));
|
|
37
|
+
break;
|
|
38
|
+
case 'avatar': {
|
|
39
|
+
if (change.value.kind === 'file') {
|
|
40
|
+
return { status: 'rejected', message: 'ICQQ avatar requires resolved image bytes, URL or local path' };
|
|
41
|
+
}
|
|
42
|
+
const image = change.value.kind === 'base64' ? Buffer.from(change.value.value, 'base64') : change.value.value;
|
|
43
|
+
await operation.run(() => client.setAvatar(image));
|
|
44
|
+
// The input is not the platform CDN URL; it must not become an observed avatar.
|
|
45
|
+
return { status: 'updated' };
|
|
46
|
+
}
|
|
47
|
+
default: return { status: 'rejected', message: `ICQQ cannot safely update ${change.field}` };
|
|
48
|
+
}
|
|
49
|
+
if (accepted && change.field === 'nickname')
|
|
50
|
+
client.nickname = change.value;
|
|
51
|
+
return accepted
|
|
52
|
+
? { status: 'updated', profile: { [change.field]: change.value } }
|
|
53
|
+
: { status: 'rejected', message: `ICQQ rejected the ${change.field} change` };
|
|
54
|
+
},
|
|
55
|
+
});
|
|
56
|
+
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@zhin.js/adapter-icqq",
|
|
3
|
-
"version": "1.1.
|
|
3
|
+
"version": "1.1.21",
|
|
4
4
|
"description": "Zhin.js ICQQ adapter — direct connection to @icqqjs/icqq",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "./lib/index.js",
|
|
@@ -33,27 +33,27 @@
|
|
|
33
33
|
"directory": "plugins/adapters/icqq"
|
|
34
34
|
},
|
|
35
35
|
"dependencies": {
|
|
36
|
-
"@zhin.js/adapter": "1.1.
|
|
37
|
-
"@zhin.js/agent-feature": "1.1.
|
|
38
|
-
"@zhin.js/client": "1.1.
|
|
36
|
+
"@zhin.js/adapter": "1.1.27",
|
|
37
|
+
"@zhin.js/agent-feature": "1.1.12",
|
|
38
|
+
"@zhin.js/client": "1.1.23",
|
|
39
39
|
"@zhin.js/console-contract": "1.1.3",
|
|
40
|
-
"@zhin.js/core": "1.1.
|
|
41
|
-
"@zhin.js/feature-kit": "1.1.
|
|
42
|
-
"@zhin.js/im-contract": "1.1.
|
|
40
|
+
"@zhin.js/core": "1.1.55",
|
|
41
|
+
"@zhin.js/feature-kit": "1.1.11",
|
|
42
|
+
"@zhin.js/im-contract": "1.1.10",
|
|
43
43
|
"@zhin.js/logger": "1.1.2",
|
|
44
|
-
"@zhin.js/page": "1.1.
|
|
45
|
-
"@zhin.js/permission": "1.1.
|
|
46
|
-
"@zhin.js/skill": "1.1.
|
|
47
|
-
"@zhin.js/tool": "1.1.
|
|
44
|
+
"@zhin.js/page": "1.1.13",
|
|
45
|
+
"@zhin.js/permission": "1.1.11",
|
|
46
|
+
"@zhin.js/skill": "1.1.12",
|
|
47
|
+
"@zhin.js/tool": "1.1.14"
|
|
48
48
|
},
|
|
49
49
|
"peerDependencies": {
|
|
50
50
|
"@icqqjs/icqq": "^1.12.2",
|
|
51
51
|
"zod": "^4.0.0",
|
|
52
|
-
"@zhin.js/adapter": "^1.1.
|
|
53
|
-
"@zhin.js/command": "^1.1.
|
|
54
|
-
"@zhin.js/core": "^1.1.
|
|
55
|
-
"@zhin.js/prompt-section": "^1.1.
|
|
56
|
-
"zhin.js": "^1.1.
|
|
52
|
+
"@zhin.js/adapter": "^1.1.27",
|
|
53
|
+
"@zhin.js/command": "^1.1.13",
|
|
54
|
+
"@zhin.js/core": "^1.1.55",
|
|
55
|
+
"@zhin.js/prompt-section": "^1.1.11",
|
|
56
|
+
"zhin.js": "^1.1.20"
|
|
57
57
|
},
|
|
58
58
|
"peerDependenciesMeta": {
|
|
59
59
|
"@icqqjs/icqq": {
|
|
@@ -78,8 +78,8 @@
|
|
|
78
78
|
"typescript": "^6.0.3",
|
|
79
79
|
"vitest": "^4.1.10",
|
|
80
80
|
"zod": "^4.4.3",
|
|
81
|
-
"@zhin.js/prompt-section": "1.1.
|
|
82
|
-
"zhin.js": "1.1.
|
|
81
|
+
"@zhin.js/prompt-section": "1.1.11",
|
|
82
|
+
"zhin.js": "1.1.20"
|
|
83
83
|
},
|
|
84
84
|
"files": [
|
|
85
85
|
"adapters",
|
|
@@ -111,7 +111,7 @@
|
|
|
111
111
|
"features": [
|
|
112
112
|
{
|
|
113
113
|
"package": "@zhin.js/adapter",
|
|
114
|
-
"api": "^2.
|
|
114
|
+
"api": "^2.1.0"
|
|
115
115
|
},
|
|
116
116
|
{
|
|
117
117
|
"package": "@zhin.js/command",
|
|
@@ -119,7 +119,7 @@
|
|
|
119
119
|
},
|
|
120
120
|
{
|
|
121
121
|
"package": "@zhin.js/tool",
|
|
122
|
-
"api": "^1.
|
|
122
|
+
"api": "^1.1.0"
|
|
123
123
|
},
|
|
124
124
|
{
|
|
125
125
|
"package": "@zhin.js/skill",
|
|
@@ -4,7 +4,7 @@ export default defineAgentPromptSection({
|
|
|
4
4
|
title: 'ICQQ / QQ',
|
|
5
5
|
content: [
|
|
6
6
|
'Use the current Skill catalog; discover the intent and load the matching Skill before its private Tool.',
|
|
7
|
-
'The current IM operation selects
|
|
7
|
+
'The current IM operation selects your current endpoint Client automatically; never guess user_id or group_id.',
|
|
8
8
|
].join('\n'),
|
|
9
9
|
layer: 'tools',
|
|
10
10
|
order: 70,
|
|
@@ -4,7 +4,7 @@ export default defineAgentPromptSection({
|
|
|
4
4
|
title: 'ICQQ / QQ',
|
|
5
5
|
content: [
|
|
6
6
|
'Use the current Skill catalog; discover the intent and load the matching Skill before its private Tool.',
|
|
7
|
-
'The current IM operation selects
|
|
7
|
+
'The current IM operation selects your current endpoint Client automatically; never guess user_id or group_id.',
|
|
8
8
|
].join('\n'),
|
|
9
9
|
layer: 'tools',
|
|
10
10
|
order: 70,
|
package/schema.json
CHANGED
|
@@ -433,6 +433,27 @@
|
|
|
433
433
|
"endpoint",
|
|
434
434
|
"id"
|
|
435
435
|
]
|
|
436
|
+
},
|
|
437
|
+
"nickname": {
|
|
438
|
+
"type": [
|
|
439
|
+
"string",
|
|
440
|
+
"null"
|
|
441
|
+
],
|
|
442
|
+
"description": "账号昵称兜底",
|
|
443
|
+
"x-i18n": {
|
|
444
|
+
"en": {
|
|
445
|
+
"description": "Fallback account nickname"
|
|
446
|
+
}
|
|
447
|
+
}
|
|
448
|
+
},
|
|
449
|
+
"profile": {
|
|
450
|
+
"$ref": "#/$defs/selfProfileFallback",
|
|
451
|
+
"description": "账号资料兜底",
|
|
452
|
+
"x-i18n": {
|
|
453
|
+
"en": {
|
|
454
|
+
"description": "Fallback account profile"
|
|
455
|
+
}
|
|
456
|
+
}
|
|
436
457
|
}
|
|
437
458
|
},
|
|
438
459
|
"required": [
|
|
@@ -541,10 +562,94 @@
|
|
|
541
562
|
"endpoint",
|
|
542
563
|
"id"
|
|
543
564
|
]
|
|
565
|
+
},
|
|
566
|
+
"nickname": {
|
|
567
|
+
"type": [
|
|
568
|
+
"string",
|
|
569
|
+
"null"
|
|
570
|
+
],
|
|
571
|
+
"description": "账号昵称兜底",
|
|
572
|
+
"x-i18n": {
|
|
573
|
+
"en": {
|
|
574
|
+
"description": "Fallback account nickname"
|
|
575
|
+
}
|
|
576
|
+
}
|
|
577
|
+
},
|
|
578
|
+
"profile": {
|
|
579
|
+
"$ref": "#/$defs/selfProfileFallback",
|
|
580
|
+
"description": "账号资料兜底",
|
|
581
|
+
"x-i18n": {
|
|
582
|
+
"en": {
|
|
583
|
+
"description": "Fallback account profile"
|
|
584
|
+
}
|
|
585
|
+
}
|
|
544
586
|
}
|
|
545
587
|
},
|
|
546
588
|
"required": [
|
|
547
589
|
"master",
|
|
548
590
|
"endpoints"
|
|
549
|
-
]
|
|
591
|
+
],
|
|
592
|
+
"$defs": {
|
|
593
|
+
"selfProfileFallback": {
|
|
594
|
+
"type": "object",
|
|
595
|
+
"additionalProperties": false,
|
|
596
|
+
"properties": {
|
|
597
|
+
"gender": {
|
|
598
|
+
"type": [
|
|
599
|
+
"string",
|
|
600
|
+
"null"
|
|
601
|
+
],
|
|
602
|
+
"enum": [
|
|
603
|
+
"male",
|
|
604
|
+
"female",
|
|
605
|
+
"other",
|
|
606
|
+
"unknown",
|
|
607
|
+
null
|
|
608
|
+
]
|
|
609
|
+
},
|
|
610
|
+
"age": {
|
|
611
|
+
"type": [
|
|
612
|
+
"integer",
|
|
613
|
+
"null"
|
|
614
|
+
],
|
|
615
|
+
"minimum": 0
|
|
616
|
+
},
|
|
617
|
+
"birthday": {
|
|
618
|
+
"type": [
|
|
619
|
+
"string",
|
|
620
|
+
"null"
|
|
621
|
+
],
|
|
622
|
+
"pattern": "^\\d{4}-(0[1-9]|1[0-2])-(0[1-9]|[12]\\d|3[01])$"
|
|
623
|
+
},
|
|
624
|
+
"signature": {
|
|
625
|
+
"type": [
|
|
626
|
+
"string",
|
|
627
|
+
"null"
|
|
628
|
+
]
|
|
629
|
+
},
|
|
630
|
+
"avatar": {
|
|
631
|
+
"type": [
|
|
632
|
+
"string",
|
|
633
|
+
"null"
|
|
634
|
+
],
|
|
635
|
+
"pattern": "^(https?://[^\\s/?#]+(?:[/?#].*)?|)$"
|
|
636
|
+
},
|
|
637
|
+
"interests": {
|
|
638
|
+
"type": [
|
|
639
|
+
"array",
|
|
640
|
+
"null"
|
|
641
|
+
],
|
|
642
|
+
"items": {
|
|
643
|
+
"type": "string"
|
|
644
|
+
}
|
|
645
|
+
},
|
|
646
|
+
"bio": {
|
|
647
|
+
"type": [
|
|
648
|
+
"string",
|
|
649
|
+
"null"
|
|
650
|
+
]
|
|
651
|
+
}
|
|
652
|
+
}
|
|
653
|
+
}
|
|
654
|
+
}
|
|
550
655
|
}
|
package/src/endpoint.ts
CHANGED
|
@@ -79,6 +79,7 @@ import type { SystemMessage as IcqqSystemMessage } from './types.js';
|
|
|
79
79
|
import { ManagedIcqqClient, type IcqqBackgroundOperation } from './managed-client.js';
|
|
80
80
|
import { inspectGroupMember, moderateGroupMember } from './group-moderation.js';
|
|
81
81
|
import { inspectGroupMemberProfile, inspectGroupScene } from './group-scene.js';
|
|
82
|
+
import { createIcqqSelfProfile } from './self-profile.js';
|
|
82
83
|
|
|
83
84
|
export interface IcqqEndpointOptions {
|
|
84
85
|
readonly id: CapabilityId;
|
|
@@ -239,7 +240,7 @@ export class IcqqEndpoint extends ManagedIcqqClient implements Endpoint {
|
|
|
239
240
|
start: signal => this.#startFramework(signal), open: () => this.#openFramework(), close: () => this.#closeFramework(), stop: () => this.#stopFramework(),
|
|
240
241
|
send: request => this.#sendFramework(request), get name() { return name(); },
|
|
241
242
|
get transportState() { return state(); },
|
|
242
|
-
management: this.#management, control: this.#control,
|
|
243
|
+
management: this.#management, control: this.#control, profile: createIcqqSelfProfile(this),
|
|
243
244
|
get content() { return content(); },
|
|
244
245
|
});
|
|
245
246
|
this.#contentResolver = new IcqqContentResolver(this);
|
package/src/protocol.ts
CHANGED
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import type { EndpointProfileConfiguration } from 'zhin.js/adapter';
|
|
1
2
|
import type { MessageElem, Sendable, ShareElem } from '@icqqjs/icqq';
|
|
2
3
|
import { isMediaRef, type ConversationRef } from '@zhin.js/im-contract';
|
|
3
4
|
import { resolveAdapterNotifyTarget, type AdapterNotifyTarget, type ResolvedAdapterNotifyTarget } from 'zhin.js/adapter';
|
|
@@ -232,7 +233,7 @@ export const Actions = {
|
|
|
232
233
|
} as const;
|
|
233
234
|
|
|
234
235
|
/** One expanded ICQQ endpoint configuration produced by AdapterIndex. */
|
|
235
|
-
export interface IcqqEndpointConfig {
|
|
236
|
+
export interface IcqqEndpointConfig extends EndpointProfileConfiguration {
|
|
236
237
|
readonly id: string;
|
|
237
238
|
readonly autoReconnect?: boolean;
|
|
238
239
|
readonly outboundMedia?: 'file' | 'base64';
|
|
@@ -0,0 +1,55 @@
|
|
|
1
|
+
import type { Client } from '@icqqjs/icqq';
|
|
2
|
+
import { createEndpointOperation, type EndpointProfile } from 'zhin.js/adapter';
|
|
3
|
+
import type { SelfProfile } from '@zhin.js/im-contract';
|
|
4
|
+
|
|
5
|
+
type IcqqProfileClient = Pick<Client,
|
|
6
|
+
'uin' | 'nickname' | 'isOnline' | 'getProfile' | 'setNickname' | 'setGender' |
|
|
7
|
+
'setSignature' | 'setDescription' | 'setAvatar'>;
|
|
8
|
+
|
|
9
|
+
/** ICQQ owns the platform facts; config fallbacks and call leases belong to Adapter. */
|
|
10
|
+
export function createIcqqSelfProfile(client: IcqqProfileClient): EndpointProfile {
|
|
11
|
+
return Object.freeze<EndpointProfile>({
|
|
12
|
+
writableFields: Object.freeze(['nickname', 'gender', 'signature', 'bio', 'avatar']),
|
|
13
|
+
get current() { return client.isOnline() ? { nickname: client.nickname } : {}; },
|
|
14
|
+
async read({ signal }) {
|
|
15
|
+
const raw = await createEndpointOperation({ signal }).prepare(() => client.getProfile(client.uin));
|
|
16
|
+
const profile: SelfProfile = {
|
|
17
|
+
...(typeof raw.nickname === 'string' ? { nickname: raw.nickname } : {}),
|
|
18
|
+
...(raw.sex === 'male' || raw.sex === 'female' || raw.sex === 'unknown' ? { gender: raw.sex } : {}),
|
|
19
|
+
...(typeof raw.age === 'number' && Number.isFinite(raw.age) ? { age: raw.age } : {}),
|
|
20
|
+
...(typeof raw.signature === 'string' ? { signature: raw.signature } : {}),
|
|
21
|
+
...(typeof raw.avatar?.url === 'string' ? { avatar: raw.avatar.url } : {}),
|
|
22
|
+
};
|
|
23
|
+
return profile;
|
|
24
|
+
},
|
|
25
|
+
async write({ change, signal }) {
|
|
26
|
+
const operation = createEndpointOperation({ signal });
|
|
27
|
+
let accepted: boolean;
|
|
28
|
+
switch (change.field) {
|
|
29
|
+
case 'nickname': accepted = await operation.run(() => client.setNickname(change.value)); break;
|
|
30
|
+
case 'gender': {
|
|
31
|
+
if (change.value === 'other') return { status: 'rejected', message: 'ICQQ supports male, female or unknown gender' };
|
|
32
|
+
const gender = change.value === 'male' ? 1 : change.value === 'female' ? 2 : 0;
|
|
33
|
+
accepted = await operation.run(() => client.setGender(gender));
|
|
34
|
+
break;
|
|
35
|
+
}
|
|
36
|
+
case 'signature': accepted = await operation.run(() => client.setSignature(change.value)); break;
|
|
37
|
+
case 'bio': accepted = await operation.run(() => client.setDescription(change.value)); break;
|
|
38
|
+
case 'avatar': {
|
|
39
|
+
if (change.value.kind === 'file') {
|
|
40
|
+
return { status: 'rejected', message: 'ICQQ avatar requires resolved image bytes, URL or local path' };
|
|
41
|
+
}
|
|
42
|
+
const image = change.value.kind === 'base64' ? Buffer.from(change.value.value, 'base64') : change.value.value;
|
|
43
|
+
await operation.run(() => client.setAvatar(image));
|
|
44
|
+
// The input is not the platform CDN URL; it must not become an observed avatar.
|
|
45
|
+
return { status: 'updated' };
|
|
46
|
+
}
|
|
47
|
+
default: return { status: 'rejected', message: `ICQQ cannot safely update ${change.field}` };
|
|
48
|
+
}
|
|
49
|
+
if (accepted && change.field === 'nickname') client.nickname = change.value;
|
|
50
|
+
return accepted
|
|
51
|
+
? { status: 'updated', profile: { [change.field]: change.value } }
|
|
52
|
+
: { status: 'rejected', message: `ICQQ rejected the ${change.field} change` };
|
|
53
|
+
},
|
|
54
|
+
});
|
|
55
|
+
}
|