@zhin.js/adapter-icqq 8.0.2 → 8.0.5
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 +87 -0
- package/README.md +9 -9
- package/adapters/icqq.js +2 -2
- package/adapters/icqq.ts +2 -2
- package/lib/endpoint.d.ts +2 -5
- package/lib/endpoint.js +61 -89
- package/lib/icqq-agent-deps.d.ts +2 -2
- package/lib/icqq-agent-deps.js +7 -7
- package/lib/icqq-endpoint-commands.js +7 -7
- package/lib/icqq-inbox.d.ts +1 -1
- package/lib/icqq-inbox.js +3 -3
- package/lib/index.d.ts +1 -1
- package/lib/index.js +1 -1
- package/lib/ipc-client.d.ts +1 -1
- package/lib/ipc-client.js +1 -1
- package/lib/protocol.d.ts +20 -9
- package/lib/protocol.js +40 -34
- package/package.json +14 -13
- package/schema.json +11 -11
- package/src/endpoint.ts +80 -94
- package/src/icqq-agent-deps.ts +8 -8
- package/src/icqq-endpoint-commands.ts +7 -7
- package/src/icqq-inbox.ts +4 -4
- package/src/index.ts +2 -2
- package/src/ipc-client.ts +1 -1
- package/src/protocol.ts +47 -38
- /package/commands/endpoint/add/{[name:string=].js → [[id]].js} +0 -0
- /package/commands/endpoint/add/{[name:string=].ts → [[id]].ts} +0 -0
- /package/commands/endpoint/remove/{[name:string].js → [id].js} +0 -0
- /package/commands/endpoint/remove/{[name:string].ts → [id].ts} +0 -0
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,92 @@
|
|
|
1
1
|
# @zhin.js/adapter-icqq
|
|
2
2
|
|
|
3
|
+
## 8.0.5
|
|
4
|
+
|
|
5
|
+
### Patch Changes
|
|
6
|
+
|
|
7
|
+
- 36c7400: Replace `AgentPromptBuildContext.commMessage` with an authenticated platform projection and make Agent prompt assembly consume canonical Turn identity. Platform contributors and prompt hooks no longer receive a classic IM `Message`.
|
|
8
|
+
|
|
9
|
+
Remove the unused Message field and active-context escape hatch from PromptController; turn scheduling is keyed only by canonical session identity.
|
|
10
|
+
|
|
11
|
+
- Updated dependencies [6f9c366]
|
|
12
|
+
- Updated dependencies [373a56b]
|
|
13
|
+
- Updated dependencies [0de46a8]
|
|
14
|
+
- Updated dependencies [c106ecc]
|
|
15
|
+
- Updated dependencies [ca92e03]
|
|
16
|
+
- Updated dependencies [b0f37ae]
|
|
17
|
+
- Updated dependencies [ba08a2f]
|
|
18
|
+
- Updated dependencies [b08f7fe]
|
|
19
|
+
- Updated dependencies [daffd4c]
|
|
20
|
+
- Updated dependencies [36c7400]
|
|
21
|
+
- Updated dependencies [a9fa72e]
|
|
22
|
+
- Updated dependencies [c8de3ef]
|
|
23
|
+
- Updated dependencies [60f0fc8]
|
|
24
|
+
- Updated dependencies [574c990]
|
|
25
|
+
- Updated dependencies [d096f16]
|
|
26
|
+
- Updated dependencies [3f29623]
|
|
27
|
+
- Updated dependencies [2916852]
|
|
28
|
+
- Updated dependencies [d047869]
|
|
29
|
+
- Updated dependencies [162fa34]
|
|
30
|
+
- Updated dependencies [e62561e]
|
|
31
|
+
- Updated dependencies [3eeeb46]
|
|
32
|
+
- Updated dependencies [85d0f82]
|
|
33
|
+
- Updated dependencies [61bfc1c]
|
|
34
|
+
- Updated dependencies [04bad47]
|
|
35
|
+
- Updated dependencies [e40b048]
|
|
36
|
+
- Updated dependencies [5a5b1bb]
|
|
37
|
+
- Updated dependencies [f1708c3]
|
|
38
|
+
- Updated dependencies [d254a81]
|
|
39
|
+
- Updated dependencies [7123c47]
|
|
40
|
+
- Updated dependencies [05befc1]
|
|
41
|
+
- Updated dependencies [0663b6a]
|
|
42
|
+
- Updated dependencies [f28f9b3]
|
|
43
|
+
- Updated dependencies [9f340f7]
|
|
44
|
+
- Updated dependencies [e53444f]
|
|
45
|
+
- Updated dependencies [5eedd26]
|
|
46
|
+
- Updated dependencies [92b0dd7]
|
|
47
|
+
- Updated dependencies [f919b6f]
|
|
48
|
+
- Updated dependencies [098e411]
|
|
49
|
+
- Updated dependencies [e1b7c01]
|
|
50
|
+
- Updated dependencies [9b94f87]
|
|
51
|
+
- Updated dependencies [a7df753]
|
|
52
|
+
- @zhin.js/agent@1.1.5
|
|
53
|
+
- @zhin.js/im-contract@1.0.3
|
|
54
|
+
- @zhin.js/adapter@1.1.7
|
|
55
|
+
- @zhin.js/plugin-runtime@1.1.5
|
|
56
|
+
- @zhin.js/command@1.0.9
|
|
57
|
+
- @zhin.js/core@1.5.4
|
|
58
|
+
- zhin.js@6.0.4
|
|
59
|
+
|
|
60
|
+
## 8.0.4
|
|
61
|
+
|
|
62
|
+
### Patch Changes
|
|
63
|
+
|
|
64
|
+
- f8c7a54: fix: im
|
|
65
|
+
- Updated dependencies [f8c7a54]
|
|
66
|
+
- @zhin.js/logger@1.0.76
|
|
67
|
+
- @zhin.js/adapter@1.1.6
|
|
68
|
+
- @zhin.js/agent@1.1.4
|
|
69
|
+
- @zhin.js/command@1.0.8
|
|
70
|
+
- @zhin.js/core@1.5.3
|
|
71
|
+
- @zhin.js/im-contract@1.0.2
|
|
72
|
+
- @zhin.js/plugin-runtime@1.1.4
|
|
73
|
+
- zhin.js@6.0.3
|
|
74
|
+
|
|
75
|
+
## 8.0.3
|
|
76
|
+
|
|
77
|
+
### Patch Changes
|
|
78
|
+
|
|
79
|
+
- Updated dependencies [afc0e66]
|
|
80
|
+
- Updated dependencies [2e41ad5]
|
|
81
|
+
- Updated dependencies [9f57124]
|
|
82
|
+
- @zhin.js/core@1.5.2
|
|
83
|
+
- @zhin.js/adapter@1.1.5
|
|
84
|
+
- @zhin.js/im-contract@1.0.1
|
|
85
|
+
- @zhin.js/plugin-runtime@1.1.3
|
|
86
|
+
- @zhin.js/command@1.0.7
|
|
87
|
+
- @zhin.js/agent@1.1.3
|
|
88
|
+
- zhin.js@6.0.2
|
|
89
|
+
|
|
3
90
|
## 8.0.2
|
|
4
91
|
|
|
5
92
|
### Patch Changes
|
package/README.md
CHANGED
|
@@ -5,8 +5,8 @@ ICQQ Plugin Runtime 适配器 — 通过 [@icqqjs/cli](https://github.com/icqqjs
|
|
|
5
5
|
## 功能特性
|
|
6
6
|
|
|
7
7
|
- 群聊 / 私聊 / 群临时会话 / QQ 频道消息
|
|
8
|
-
- 入站:`messageGatewayToken`(IPC
|
|
9
|
-
- 出站:`send({
|
|
8
|
+
- 入站:`messageGatewayToken`(IPC 事件订阅),归一为 `gateway.receive({ conversation, message, content, ... })`
|
|
9
|
+
- 出站:`send({ conversation, payload })` → `send_group_msg` / `send_private_msg` / …(conversation kind/id/parent 路由)
|
|
10
10
|
- Agent 工具:`agent/tools/`(戳一戳、群管、好友列表等)保留
|
|
11
11
|
- Console Endpoint 管理:`src/endpoint.ts` 显式实现 `EndpointManagement`(好友/群/群成员列表、请求审批、删好友、踢人、禁言、设管理),Console 使用 `endpoint.friends` / `endpoint.groups` / `endpoint.group_members` 等规范 RPC
|
|
12
12
|
|
|
@@ -48,14 +48,14 @@ plugins:
|
|
|
48
48
|
|
|
49
49
|
先执行 `icqq login`,再启动 Zhin。
|
|
50
50
|
|
|
51
|
-
## Send
|
|
51
|
+
## Send conversation
|
|
52
52
|
|
|
53
|
-
| 类型 |
|
|
54
|
-
|
|
55
|
-
| 私聊 | `private:uin` |
|
|
56
|
-
| 群聊 | `group:gid` |
|
|
57
|
-
| 群临时会话 | `
|
|
58
|
-
| 频道 | `channel:guildId
|
|
53
|
+
| 类型 | conversation |
|
|
54
|
+
|------|--------------|
|
|
55
|
+
| 私聊 | `{ kind: 'private', id: uin }` |
|
|
56
|
+
| 群聊 | `{ kind: 'group', id: gid }` |
|
|
57
|
+
| 群临时会话 | `{ kind: 'private', id: uin, parent: { kind: 'group', id: gid } }` |
|
|
58
|
+
| 频道 | `{ kind: 'channel', id: channelId, parent: { kind: 'channel', id: guildId } }` |
|
|
59
59
|
|
|
60
60
|
## 架构
|
|
61
61
|
|
package/adapters/icqq.js
CHANGED
|
@@ -26,8 +26,8 @@ export default defineAdapter({
|
|
|
26
26
|
// Console loginAssist + host-router routes deferred.
|
|
27
27
|
const config = resolveIcqqConfig(context.config);
|
|
28
28
|
// 注册到插件运行时状态(icqq.endpoint list 的"运行中"数据源)
|
|
29
|
-
context.use(icqqRuntimeStateToken).endpoints.set(config.
|
|
30
|
-
|
|
29
|
+
context.use(icqqRuntimeStateToken).endpoints.set(config.id, {
|
|
30
|
+
id: config.id,
|
|
31
31
|
mode: config.rpc ? 'rpc' : 'ipc',
|
|
32
32
|
});
|
|
33
33
|
const inbox = resolveInboxHooks(context);
|
package/adapters/icqq.ts
CHANGED
|
@@ -42,8 +42,8 @@ export default defineAdapter<IcqqAdapterConfig>({
|
|
|
42
42
|
// Console loginAssist + host-router routes deferred.
|
|
43
43
|
const config = resolveIcqqConfig(context.config);
|
|
44
44
|
// 注册到插件运行时状态(icqq.endpoint list 的"运行中"数据源)
|
|
45
|
-
context.use(icqqRuntimeStateToken).endpoints.set(config.
|
|
46
|
-
|
|
45
|
+
context.use(icqqRuntimeStateToken).endpoints.set(config.id, {
|
|
46
|
+
id: config.id,
|
|
47
47
|
mode: config.rpc ? 'rpc' : 'ipc',
|
|
48
48
|
});
|
|
49
49
|
const inbox = resolveInboxHooks(context);
|
package/lib/endpoint.d.ts
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* IcqqIpcEndpoint — lifecycle, outbound, IPC subscribe/admit for ICQQ daemon.
|
|
3
3
|
*/
|
|
4
|
-
import type { EndpointControl, EndpointInstance, EndpointManagement } from '@zhin.js/adapter';
|
|
4
|
+
import type { EndpointControl, EndpointInstance, EndpointManagement, EndpointSendRequest } from '@zhin.js/adapter';
|
|
5
5
|
import type { MessageGateway } from '@zhin.js/core/runtime';
|
|
6
6
|
import type { CapabilityId } from '@zhin.js/plugin-runtime';
|
|
7
7
|
import { type IcqqInboundMessage, type IpcEvent, type IpcResponse, type ResolvedIcqqConfig } from './protocol.js';
|
|
@@ -48,10 +48,7 @@ export declare class IcqqIpcEndpoint implements EndpointInstance {
|
|
|
48
48
|
open(): void;
|
|
49
49
|
close(): void;
|
|
50
50
|
stop(): Promise<void>;
|
|
51
|
-
send({
|
|
52
|
-
readonly target: string;
|
|
53
|
-
readonly payload: unknown;
|
|
54
|
-
}): Promise<string>;
|
|
51
|
+
send({ conversation, payload }: EndpointSendRequest): Promise<string>;
|
|
55
52
|
private static readonly EMOJI_MAP;
|
|
56
53
|
private getEmojiId;
|
|
57
54
|
/** Activity-feedback / OutboundHost reaction surface. */
|
package/lib/endpoint.js
CHANGED
|
@@ -1,13 +1,13 @@
|
|
|
1
|
-
import { formatCompact,
|
|
1
|
+
import { formatCompact, getAdapterLogger, truncatePreview } from '@zhin.js/logger';
|
|
2
2
|
import { registerIcqqAgentEndpoint } from './icqq-agent-deps.js';
|
|
3
3
|
import { InboundMessageDeduper, findIcqqNestedMessageSource, isIcqqBotMentioned, isIcqqMessagePostType, normalizeIcqqInboundMessage, quotedPayloadFromIcqqSource, resolveIcqqQuoteIdFromEvent, shouldSkipSelfInboundMessage, unwrapIcqqIpcEventPayload, } from './icqq-inbound.js';
|
|
4
4
|
import { buildIcqqInboxNoticeRow, buildIcqqInboxRequestRow, buildIcqqSystemRequestRow, isIcqqNoticePayload, isIcqqRequestPayload, } from './icqq-inbox.js';
|
|
5
5
|
import { IcqqGuildCatalog, isIcqqGuildIpcEvent, normalizeIcqqGuildInboundMessage, } from './icqq-guild.js';
|
|
6
6
|
import { IpcClient } from './ipc-client.js';
|
|
7
7
|
import { materializeOutboundBase64, resolveIcqqOutboundMediaMode, } from './outbound-media.js';
|
|
8
|
-
import { Actions, formatInboundContent,
|
|
9
|
-
const logger = getLogger('icqq');
|
|
8
|
+
import { Actions, formatInboundContent, formatOutboundBody, icqqInboundConversation, icqqOutboundTarget, } from './protocol.js';
|
|
10
9
|
export class IcqqIpcEndpoint {
|
|
10
|
+
#logger;
|
|
11
11
|
#options;
|
|
12
12
|
#createIpc;
|
|
13
13
|
name;
|
|
@@ -48,8 +48,9 @@ export class IcqqIpcEndpoint {
|
|
|
48
48
|
/** 是否已有重连循环在跑(避免多次 schedule 叠套) */
|
|
49
49
|
#reconnectRunning = false;
|
|
50
50
|
constructor(options) {
|
|
51
|
+
this.#logger = getAdapterLogger('icqq', options.config.id);
|
|
51
52
|
this.#options = options;
|
|
52
|
-
this.name = options.config.
|
|
53
|
+
this.name = options.config.id;
|
|
53
54
|
this.#createIpc = options.createIpc ?? defaultCreateIpc;
|
|
54
55
|
}
|
|
55
56
|
async request(action, params) {
|
|
@@ -66,19 +67,13 @@ export class IcqqIpcEndpoint {
|
|
|
66
67
|
try {
|
|
67
68
|
await this.#bindIpcSession();
|
|
68
69
|
this.#unregisterAgent = registerIcqqAgentEndpoint(this.name, this);
|
|
69
|
-
logger.info(
|
|
70
|
-
op: 'connect',
|
|
71
|
-
endpoint: this.name,
|
|
72
|
-
mode: this.#options.config.rpc ? 'rpc' : 'ipc',
|
|
73
|
-
friends: this.friends.size,
|
|
74
|
-
groups: this.groups.size,
|
|
75
|
-
}));
|
|
70
|
+
this.#logger.info(`connected (${this.#options.config.rpc ? 'rpc' : 'ipc'}) | friends: ${this.friends.size} | groups: ${this.groups.size}`);
|
|
76
71
|
}
|
|
77
72
|
catch (error) {
|
|
78
73
|
await this.stop();
|
|
79
74
|
// Startup connect failures are surfaced once (with stack) by AdapterIndex;
|
|
80
75
|
// keep this at debug to avoid a duplicate error-level log.
|
|
81
|
-
logger.debug(`Failed to connect ICQQ IPC: ${error instanceof Error ? error.stack ?? error.message : String(error)}`);
|
|
76
|
+
this.#logger.debug(`Failed to connect ICQQ IPC: ${error instanceof Error ? error.stack ?? error.message : String(error)}`);
|
|
82
77
|
throw error;
|
|
83
78
|
}
|
|
84
79
|
}
|
|
@@ -117,10 +112,9 @@ export class IcqqIpcEndpoint {
|
|
|
117
112
|
const jitter = Math.floor(Math.random() * 400);
|
|
118
113
|
const delayMs = base + jitter;
|
|
119
114
|
// 首次断开 WARN,后续重试静默为 DEBUG,避免刷屏
|
|
120
|
-
const disconnectLog = attempt === 0 ? logger.warn.bind(logger) : logger.debug.bind(logger);
|
|
115
|
+
const disconnectLog = attempt === 0 ? this.#logger.warn.bind(this.#logger) : this.#logger.debug.bind(this.#logger);
|
|
121
116
|
disconnectLog(formatCompact({
|
|
122
117
|
op: 'disconnect',
|
|
123
|
-
endpoint: this.name,
|
|
124
118
|
ok: false,
|
|
125
119
|
delay_ms: delayMs,
|
|
126
120
|
attempt: attempt + 1,
|
|
@@ -130,18 +124,12 @@ export class IcqqIpcEndpoint {
|
|
|
130
124
|
break;
|
|
131
125
|
try {
|
|
132
126
|
await this.#bindIpcSession();
|
|
133
|
-
logger.info(
|
|
134
|
-
|
|
135
|
-
endpoint: this.name,
|
|
136
|
-
ok: true,
|
|
137
|
-
attempts: attempt + 1,
|
|
138
|
-
friends: this.friends.size,
|
|
139
|
-
groups: this.groups.size,
|
|
140
|
-
}));
|
|
127
|
+
this.#logger.info(`reconnected after ${attempt + 1} attempt(s)`
|
|
128
|
+
+ ` | friends: ${this.friends.size} | groups: ${this.groups.size}`);
|
|
141
129
|
break;
|
|
142
130
|
}
|
|
143
131
|
catch (error) {
|
|
144
|
-
const retryLog = attempt === 0 ? logger.warn.bind(logger) : logger.debug.bind(logger);
|
|
132
|
+
const retryLog = attempt === 0 ? this.#logger.warn.bind(this.#logger) : this.#logger.debug.bind(this.#logger);
|
|
145
133
|
retryLog(formatCompact({
|
|
146
134
|
op: 'reconnect',
|
|
147
135
|
endpoint: this.name,
|
|
@@ -185,9 +173,9 @@ export class IcqqIpcEndpoint {
|
|
|
185
173
|
// 置空 ipc:start 前 / stop 后的 request() 走防御性报错而非 TypeError
|
|
186
174
|
this.ipc = undefined;
|
|
187
175
|
this.#started = false;
|
|
188
|
-
logger.debug(formatCompact({ op: 'disconnect'
|
|
176
|
+
this.#logger.debug(formatCompact({ op: 'disconnect' }));
|
|
189
177
|
}
|
|
190
|
-
async send({
|
|
178
|
+
async send({ conversation, payload }) {
|
|
191
179
|
if (!this.ipc) {
|
|
192
180
|
throw new Error(`icqq endpoint ${this.name} 未连接(IPC 未初始化或已停止),无法发送消息`);
|
|
193
181
|
}
|
|
@@ -196,7 +184,7 @@ export class IcqqIpcEndpoint {
|
|
|
196
184
|
? materializeOutboundBase64(payload, mediaMode)
|
|
197
185
|
: payload;
|
|
198
186
|
const message = formatOutboundBody(content);
|
|
199
|
-
const parsed =
|
|
187
|
+
const parsed = icqqOutboundTarget(conversation);
|
|
200
188
|
let action;
|
|
201
189
|
let params;
|
|
202
190
|
switch (parsed.kind) {
|
|
@@ -227,15 +215,11 @@ export class IcqqIpcEndpoint {
|
|
|
227
215
|
}
|
|
228
216
|
const resp = await this.ipc.request(action, params);
|
|
229
217
|
if (!resp.ok) {
|
|
218
|
+
this.#logger.warn(`send failed ${conversation.kind}:${conversation.id} | ${resp.error} | ${truncatePreview(typeof message === 'string' ? message : String(message), 80)}`);
|
|
230
219
|
throw new Error(`发送消息失败: ${resp.error}`);
|
|
231
220
|
}
|
|
232
221
|
const messageId = String(resp.data?.message_id ?? `sent_${Date.now()}`);
|
|
233
|
-
logger.
|
|
234
|
-
op: 'icqq_send',
|
|
235
|
-
endpoint: this.name,
|
|
236
|
-
target,
|
|
237
|
-
messageId,
|
|
238
|
-
}));
|
|
222
|
+
this.#logger.info(`send ${conversation.kind}:${conversation.id} | id: ${messageId} | ${truncatePreview(typeof message === 'string' ? message : String(message), 80)}`);
|
|
239
223
|
return messageId;
|
|
240
224
|
}
|
|
241
225
|
// ── 消息回应(activity-feedback / typing reaction)────────────────────
|
|
@@ -265,7 +249,7 @@ export class IcqqIpcEndpoint {
|
|
|
265
249
|
id: emojiId,
|
|
266
250
|
});
|
|
267
251
|
if (!resp.ok) {
|
|
268
|
-
logger.warn(formatCompact({
|
|
252
|
+
this.#logger.warn(formatCompact({
|
|
269
253
|
op: 'add_reaction',
|
|
270
254
|
endpoint: this.name,
|
|
271
255
|
message_id: messageId,
|
|
@@ -276,7 +260,7 @@ export class IcqqIpcEndpoint {
|
|
|
276
260
|
}));
|
|
277
261
|
return null;
|
|
278
262
|
}
|
|
279
|
-
logger.debug(formatCompact({
|
|
263
|
+
this.#logger.debug(formatCompact({
|
|
280
264
|
op: 'add_reaction',
|
|
281
265
|
endpoint: this.name,
|
|
282
266
|
message_id: messageId,
|
|
@@ -287,7 +271,7 @@ export class IcqqIpcEndpoint {
|
|
|
287
271
|
return emojiId;
|
|
288
272
|
}
|
|
289
273
|
catch (error) {
|
|
290
|
-
logger.warn(formatCompact({
|
|
274
|
+
this.#logger.warn(formatCompact({
|
|
291
275
|
op: 'add_reaction',
|
|
292
276
|
endpoint: this.name,
|
|
293
277
|
message_id: messageId,
|
|
@@ -307,7 +291,7 @@ export class IcqqIpcEndpoint {
|
|
|
307
291
|
id: emojiId,
|
|
308
292
|
});
|
|
309
293
|
if (!resp.ok) {
|
|
310
|
-
logger.warn(formatCompact({
|
|
294
|
+
this.#logger.warn(formatCompact({
|
|
311
295
|
op: 'remove_reaction',
|
|
312
296
|
endpoint: this.name,
|
|
313
297
|
message_id: messageId,
|
|
@@ -318,7 +302,7 @@ export class IcqqIpcEndpoint {
|
|
|
318
302
|
}));
|
|
319
303
|
return;
|
|
320
304
|
}
|
|
321
|
-
logger.debug(formatCompact({
|
|
305
|
+
this.#logger.debug(formatCompact({
|
|
322
306
|
op: 'remove_reaction',
|
|
323
307
|
endpoint: this.name,
|
|
324
308
|
message_id: messageId,
|
|
@@ -328,7 +312,7 @@ export class IcqqIpcEndpoint {
|
|
|
328
312
|
}));
|
|
329
313
|
}
|
|
330
314
|
catch (error) {
|
|
331
|
-
logger.warn(formatCompact({
|
|
315
|
+
this.#logger.warn(formatCompact({
|
|
332
316
|
op: 'remove_reaction',
|
|
333
317
|
endpoint: this.name,
|
|
334
318
|
message_id: messageId,
|
|
@@ -345,7 +329,7 @@ export class IcqqIpcEndpoint {
|
|
|
345
329
|
try {
|
|
346
330
|
const resp = await this.request(Actions.RECALL_MSG, { message_id: messageId });
|
|
347
331
|
if (!resp.ok) {
|
|
348
|
-
logger.warn(formatCompact({
|
|
332
|
+
this.#logger.warn(formatCompact({
|
|
349
333
|
op: 'recall_message',
|
|
350
334
|
endpoint: this.name,
|
|
351
335
|
message_id: messageId,
|
|
@@ -354,7 +338,7 @@ export class IcqqIpcEndpoint {
|
|
|
354
338
|
}));
|
|
355
339
|
return;
|
|
356
340
|
}
|
|
357
|
-
logger.debug(formatCompact({
|
|
341
|
+
this.#logger.debug(formatCompact({
|
|
358
342
|
op: 'recall_message',
|
|
359
343
|
endpoint: this.name,
|
|
360
344
|
message_id: messageId,
|
|
@@ -362,7 +346,7 @@ export class IcqqIpcEndpoint {
|
|
|
362
346
|
}));
|
|
363
347
|
}
|
|
364
348
|
catch (error) {
|
|
365
|
-
logger.warn(formatCompact({
|
|
349
|
+
this.#logger.warn(formatCompact({
|
|
366
350
|
op: 'recall_message',
|
|
367
351
|
endpoint: this.name,
|
|
368
352
|
message_id: messageId,
|
|
@@ -499,13 +483,14 @@ export class IcqqIpcEndpoint {
|
|
|
499
483
|
}
|
|
500
484
|
/** Test / internal: admit when open. */
|
|
501
485
|
admit(msg) {
|
|
486
|
+
const conversation = msg.conversation;
|
|
502
487
|
if (!this.#open) {
|
|
503
|
-
logger.debug(formatCompact({
|
|
488
|
+
this.#logger.debug(formatCompact({
|
|
504
489
|
op: 'icqq_inbound_skip',
|
|
505
490
|
reason: 'endpoint_closed',
|
|
506
491
|
endpoint: this.name,
|
|
507
492
|
id: msg.id,
|
|
508
|
-
target:
|
|
493
|
+
target: `${conversation.kind}:${conversation.id}`,
|
|
509
494
|
}));
|
|
510
495
|
return;
|
|
511
496
|
}
|
|
@@ -515,34 +500,31 @@ export class IcqqIpcEndpoint {
|
|
|
515
500
|
content: msg.segments,
|
|
516
501
|
rawMessage: msg.content,
|
|
517
502
|
});
|
|
518
|
-
logger.
|
|
519
|
-
|
|
520
|
-
|
|
521
|
-
|
|
522
|
-
|
|
523
|
-
|
|
524
|
-
channelType: msg.channelType,
|
|
525
|
-
mentioned: mentioned || undefined,
|
|
526
|
-
preview: msg.content.slice(0, 80),
|
|
527
|
-
segments: summarizeSegmentTypes(msg.segments),
|
|
528
|
-
}));
|
|
503
|
+
this.#logger.info(`recv ${conversation.kind}:${conversation.id}`
|
|
504
|
+
+ (msg.sender ? ` from ${msg.sender.name ?? msg.sender.id}` : '')
|
|
505
|
+
+ (mentioned ? ' (mentioned)' : '')
|
|
506
|
+
+ ` | ${truncatePreview(msg.content, 80)}`);
|
|
507
|
+
const quoteId = msg.metadata?.quote_id;
|
|
508
|
+
const { quote_id: _dropQuoteId, ...restMetadata } = msg.metadata ?? {};
|
|
529
509
|
void this.#options.gateway.receive({
|
|
530
|
-
|
|
531
|
-
|
|
510
|
+
conversation,
|
|
511
|
+
message: { conversation, id: msg.id },
|
|
532
512
|
content: msg.content,
|
|
533
513
|
...(msg.segments ? { segments: msg.segments } : {}),
|
|
534
514
|
sender: msg.sender,
|
|
535
|
-
|
|
515
|
+
endpointId: this.name,
|
|
516
|
+
...(mentioned ? { mentioned: true } : {}),
|
|
517
|
+
...(quoteId ? { replyTo: { id: String(quoteId) } } : {}),
|
|
536
518
|
metadata: Object.freeze({
|
|
537
|
-
endpoint: this.name,
|
|
538
519
|
channelType: msg.channelType,
|
|
539
|
-
...
|
|
540
|
-
...(mentioned ? { mentioned: true } : {}),
|
|
520
|
+
...restMetadata,
|
|
541
521
|
}),
|
|
542
522
|
}).catch((err) => {
|
|
543
|
-
logger.warn(formatCompact({
|
|
544
|
-
op: '
|
|
545
|
-
|
|
523
|
+
this.#logger.warn(formatCompact({
|
|
524
|
+
op: 'recv',
|
|
525
|
+
endpoint: this.name,
|
|
526
|
+
target: `${conversation.kind}:${conversation.id}`,
|
|
527
|
+
ok: false,
|
|
546
528
|
error: err instanceof Error ? err.message : String(err),
|
|
547
529
|
}));
|
|
548
530
|
});
|
|
@@ -585,7 +567,7 @@ export class IcqqIpcEndpoint {
|
|
|
585
567
|
return;
|
|
586
568
|
const data = payload;
|
|
587
569
|
if (shouldSkipSelfInboundMessage(data)) {
|
|
588
|
-
logger.debug(formatCompact({
|
|
570
|
+
this.#logger.debug(formatCompact({
|
|
589
571
|
op: 'icqq_inbound_skip',
|
|
590
572
|
reason: 'self',
|
|
591
573
|
endpoint: this.name,
|
|
@@ -596,7 +578,7 @@ export class IcqqIpcEndpoint {
|
|
|
596
578
|
}
|
|
597
579
|
const normalized = normalizeIcqqInboundMessage(data);
|
|
598
580
|
if (!normalized) {
|
|
599
|
-
logger.debug(formatCompact({
|
|
581
|
+
this.#logger.debug(formatCompact({
|
|
600
582
|
op: 'icqq_inbound_skip',
|
|
601
583
|
reason: 'normalize_failed',
|
|
602
584
|
endpoint: this.name,
|
|
@@ -606,7 +588,7 @@ export class IcqqIpcEndpoint {
|
|
|
606
588
|
return;
|
|
607
589
|
}
|
|
608
590
|
if (!this.#inboundDeduper.shouldProcess(normalized.messageId)) {
|
|
609
|
-
logger.debug(formatCompact({
|
|
591
|
+
this.#logger.debug(formatCompact({
|
|
610
592
|
op: 'icqq_inbound_skip',
|
|
611
593
|
reason: 'dedupe',
|
|
612
594
|
endpoint: this.name,
|
|
@@ -616,14 +598,18 @@ export class IcqqIpcEndpoint {
|
|
|
616
598
|
}
|
|
617
599
|
this.admit({
|
|
618
600
|
id: normalized.messageId,
|
|
619
|
-
|
|
601
|
+
conversation: icqqInboundConversation(String(this.#options.id), {
|
|
620
602
|
channelType: normalized.channelType,
|
|
621
603
|
channelId: normalized.channelId,
|
|
622
604
|
channelParentGroupId: normalized.channelParentGroupId,
|
|
623
605
|
}),
|
|
624
606
|
content: formatInboundContent(normalized.rawMessage),
|
|
625
607
|
segments: normalized.content,
|
|
626
|
-
sender:
|
|
608
|
+
sender: {
|
|
609
|
+
id: normalized.userId,
|
|
610
|
+
name: normalized.nickname || undefined,
|
|
611
|
+
...(normalized.senderRole ? { roles: [normalized.senderRole] } : {}),
|
|
612
|
+
},
|
|
627
613
|
channelType: normalized.channelType,
|
|
628
614
|
metadata: buildIcqqQuoteMetadata(data, {
|
|
629
615
|
nickname: normalized.nickname,
|
|
@@ -637,7 +623,7 @@ export class IcqqIpcEndpoint {
|
|
|
637
623
|
return;
|
|
638
624
|
const normalized = normalizeIcqqGuildInboundMessage(payload);
|
|
639
625
|
if (!normalized) {
|
|
640
|
-
logger.debug(formatCompact({
|
|
626
|
+
this.#logger.debug(formatCompact({
|
|
641
627
|
op: 'icqq_inbound_skip',
|
|
642
628
|
reason: 'guild_normalize_failed',
|
|
643
629
|
endpoint: this.name,
|
|
@@ -646,7 +632,7 @@ export class IcqqIpcEndpoint {
|
|
|
646
632
|
}
|
|
647
633
|
this.#guildCatalog.upsertFromInbound(normalized.raw);
|
|
648
634
|
if (!this.#inboundDeduper.shouldProcess(`guild:${normalized.messageId}`)) {
|
|
649
|
-
logger.debug(formatCompact({
|
|
635
|
+
this.#logger.debug(formatCompact({
|
|
650
636
|
op: 'icqq_inbound_skip',
|
|
651
637
|
reason: 'dedupe',
|
|
652
638
|
endpoint: this.name,
|
|
@@ -656,14 +642,14 @@ export class IcqqIpcEndpoint {
|
|
|
656
642
|
}
|
|
657
643
|
this.admit({
|
|
658
644
|
id: normalized.messageId,
|
|
659
|
-
|
|
645
|
+
conversation: icqqInboundConversation(String(this.#options.id), {
|
|
660
646
|
channelType: 'channel',
|
|
661
647
|
channelId: normalized.channelId,
|
|
662
648
|
guildId: normalized.guildId,
|
|
663
649
|
}),
|
|
664
650
|
content: formatInboundContent(normalized.rawMessage),
|
|
665
651
|
segments: normalized.content,
|
|
666
|
-
sender: normalized.userId,
|
|
652
|
+
sender: { id: normalized.userId, name: normalized.nickname || undefined },
|
|
667
653
|
channelType: 'channel',
|
|
668
654
|
metadata: {
|
|
669
655
|
guildId: normalized.guildId,
|
|
@@ -674,7 +660,7 @@ export class IcqqIpcEndpoint {
|
|
|
674
660
|
/** 收件箱行公共前缀:adapter 槽 localName + endpoint live 名(uin)。 */
|
|
675
661
|
#inboxBase() {
|
|
676
662
|
const id = String(this.#options.id);
|
|
677
|
-
return { adapter: id.split('\0').pop() ?? id,
|
|
663
|
+
return { adapter: id.split('\0').pop() ?? id, endpointKey: this.name };
|
|
678
664
|
}
|
|
679
665
|
#recordInboxRequest(payload) {
|
|
680
666
|
const hooks = this.#options.inbox;
|
|
@@ -729,7 +715,7 @@ export class IcqqIpcEndpoint {
|
|
|
729
715
|
}
|
|
730
716
|
}
|
|
731
717
|
catch (error) {
|
|
732
|
-
logger.debug(formatCompact({
|
|
718
|
+
this.#logger.debug(formatCompact({
|
|
733
719
|
op: 'inbox_pull_system_msg',
|
|
734
720
|
endpoint: this.name,
|
|
735
721
|
error: error instanceof Error ? error.message : String(error),
|
|
@@ -773,22 +759,8 @@ function toNumericId(value, label) {
|
|
|
773
759
|
}
|
|
774
760
|
return n;
|
|
775
761
|
}
|
|
776
|
-
function summarizeSegmentTypes(segments) {
|
|
777
|
-
if (!segments?.length)
|
|
778
|
-
return undefined;
|
|
779
|
-
return segments
|
|
780
|
-
.map((segment) => {
|
|
781
|
-
if (typeof segment.type === 'string')
|
|
782
|
-
return segment.type;
|
|
783
|
-
if (segment.type && typeof segment.type === 'object' && 'name' in segment.type) {
|
|
784
|
-
return String(segment.type.name);
|
|
785
|
-
}
|
|
786
|
-
return '?';
|
|
787
|
-
})
|
|
788
|
-
.join(',');
|
|
789
|
-
}
|
|
790
762
|
async function defaultCreateIpc(config) {
|
|
791
|
-
const uin = Number(config.
|
|
763
|
+
const uin = Number(config.id);
|
|
792
764
|
if (config.rpc) {
|
|
793
765
|
return IpcClient.connectRpc(config.rpc);
|
|
794
766
|
}
|
package/lib/icqq-agent-deps.d.ts
CHANGED
|
@@ -14,12 +14,12 @@ export interface IcqqAgentEndpoint {
|
|
|
14
14
|
request(action: string, params?: Record<string, unknown>): Promise<IpcResponse>;
|
|
15
15
|
}
|
|
16
16
|
export interface IcqqAgentDeps {
|
|
17
|
-
getEndpoint: (
|
|
17
|
+
getEndpoint: (endpointKey: string) => IcqqAgentEndpoint;
|
|
18
18
|
getAdapter?: () => {
|
|
19
19
|
getEndpoint: (id: string) => IcqqAgentEndpoint;
|
|
20
20
|
};
|
|
21
21
|
}
|
|
22
|
-
export declare function registerIcqqAgentEndpoint(
|
|
22
|
+
export declare function registerIcqqAgentEndpoint(endpointKey: string, endpoint: IcqqAgentEndpoint): () => void;
|
|
23
23
|
/** Optional override used by tests / transitional callers. Pass `null` to clear. */
|
|
24
24
|
export declare function setIcqqAgentDeps(deps: IcqqAgentDeps | null): void;
|
|
25
25
|
export declare function getIcqqAgentDeps(): IcqqAgentDeps;
|
package/lib/icqq-agent-deps.js
CHANGED
|
@@ -4,11 +4,11 @@
|
|
|
4
4
|
*/
|
|
5
5
|
const endpoints = new Map();
|
|
6
6
|
let override = null;
|
|
7
|
-
export function registerIcqqAgentEndpoint(
|
|
8
|
-
endpoints.set(
|
|
7
|
+
export function registerIcqqAgentEndpoint(endpointKey, endpoint) {
|
|
8
|
+
endpoints.set(endpointKey, endpoint);
|
|
9
9
|
return () => {
|
|
10
|
-
if (endpoints.get(
|
|
11
|
-
endpoints.delete(
|
|
10
|
+
if (endpoints.get(endpointKey) === endpoint) {
|
|
11
|
+
endpoints.delete(endpointKey);
|
|
12
12
|
}
|
|
13
13
|
};
|
|
14
14
|
}
|
|
@@ -16,10 +16,10 @@ export function registerIcqqAgentEndpoint(endpointId, endpoint) {
|
|
|
16
16
|
export function setIcqqAgentDeps(deps) {
|
|
17
17
|
override = deps;
|
|
18
18
|
}
|
|
19
|
-
function lookup(
|
|
20
|
-
const registered = endpoints.get(
|
|
19
|
+
function lookup(endpointKey) {
|
|
20
|
+
const registered = endpoints.get(endpointKey);
|
|
21
21
|
if (!registered)
|
|
22
|
-
throw new Error(`Endpoint ${
|
|
22
|
+
throw new Error(`Endpoint ${endpointKey} 不存在`);
|
|
23
23
|
return registered;
|
|
24
24
|
}
|
|
25
25
|
export function getIcqqAgentDeps() {
|
|
@@ -4,7 +4,7 @@
|
|
|
4
4
|
* ICQQ 的凭据由外部守护进程(@icqqjs/cli)按账号持有(~/.icqq/<uin>/),
|
|
5
5
|
* 其 IPC/RPC 协议是「按 uin 连接 + auth + 账号级 action」,没有 add-account /
|
|
6
6
|
* 触发 login 的 action——扫码登录只能经 `icqq login <uin>` CLI 完成。
|
|
7
|
-
* 因此 add 不走 kv 凭据录入,而是 bindFlow:校验 uin、把 {
|
|
7
|
+
* 因此 add 不走 kv 凭据录入,而是 bindFlow:校验 uin、把 { id: '<uin>' }
|
|
8
8
|
* 追加到 plugins.icqq.endpoints,并引导用户先完成守护进程登录。
|
|
9
9
|
*/
|
|
10
10
|
import { addEndpointToConfig, createEndpointCommands } from '@zhin.js/adapter';
|
|
@@ -16,17 +16,17 @@ export const icqqEndpointCommands = createEndpointCommands({
|
|
|
16
16
|
running: (use) => use(icqqRuntimeStateToken).endpoints.values(),
|
|
17
17
|
describeEntry: (entry) => (entry.rpc ? 'rpc(远程守护进程)' : 'ipc(本地守护进程)'),
|
|
18
18
|
addDescription: '登记 ICQQ endpoint(uin 需先经 icqq login 完成扫码登录;重启 zhin 生效)',
|
|
19
|
-
bindFlow: ({
|
|
20
|
-
if (!
|
|
19
|
+
bindFlow: ({ id }) => {
|
|
20
|
+
if (!id) {
|
|
21
21
|
return '用法:icqq.endpoint add <uin>(uin 为纯数字 QQ 号,且需先执行 icqq login <uin> 完成扫码登录)';
|
|
22
22
|
}
|
|
23
|
-
if (!/^\d+$/.test(
|
|
23
|
+
if (!/^\d+$/.test(id)) {
|
|
24
24
|
return 'icqq endpoint 名必须是纯数字 QQ 号(uin)';
|
|
25
25
|
}
|
|
26
26
|
try {
|
|
27
|
-
const filePath = addEndpointToConfig('icqq', {
|
|
28
|
-
return (`✅ endpoint「${
|
|
29
|
-
`⚠️ 请确保已执行 icqq login ${
|
|
27
|
+
const filePath = addEndpointToConfig('icqq', { id });
|
|
28
|
+
return (`✅ endpoint「${id}」已追加到 ${filePath} 的 plugins.icqq.endpoints。\n` +
|
|
29
|
+
`⚠️ 请确保已执行 icqq login ${id} 启动守护进程并完成扫码登录;重启 zhin 后生效。`);
|
|
30
30
|
}
|
|
31
31
|
catch (error) {
|
|
32
32
|
return `添加失败:${error instanceof Error ? error.message : String(error)}`;
|
package/lib/icqq-inbox.d.ts
CHANGED
|
@@ -10,7 +10,7 @@ import type { IpcSystemMessage } from './types.js';
|
|
|
10
10
|
/** 行级公共前缀:adapter 槽 localName + endpoint live 名(uin)。 */
|
|
11
11
|
export interface IcqqInboxBase {
|
|
12
12
|
readonly adapter: string;
|
|
13
|
-
readonly
|
|
13
|
+
readonly endpointKey: string;
|
|
14
14
|
}
|
|
15
15
|
/** post_type=request / request.* 判定(OneBot 事件壳)。 */
|
|
16
16
|
export declare function isIcqqRequestPayload(payload: unknown): boolean;
|