@zhin.js/adapter-icqq 8.0.3 → 8.0.6
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 +83 -0
- package/adapters/icqq.js +2 -2
- package/adapters/icqq.ts +2 -2
- package/lib/endpoint.js +51 -79
- 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/protocol.d.ts +8 -5
- package/lib/protocol.js +6 -6
- package/package.json +14 -14
- package/schema.json +11 -11
- package/src/endpoint.ts +63 -82
- 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/protocol.ts +10 -10
- /package/commands/endpoint/add/{[[name]].js → [[id]].js} +0 -0
- /package/commands/endpoint/add/{[[name]].ts → [[id]].ts} +0 -0
- /package/commands/endpoint/remove/{[name].js → [id].js} +0 -0
- /package/commands/endpoint/remove/{[name].ts → [id].ts} +0 -0
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,88 @@
|
|
|
1
1
|
# @zhin.js/adapter-icqq
|
|
2
2
|
|
|
3
|
+
## 8.0.6
|
|
4
|
+
|
|
5
|
+
### Patch Changes
|
|
6
|
+
|
|
7
|
+
- Updated dependencies [2d0a622]
|
|
8
|
+
- @zhin.js/command@1.0.10
|
|
9
|
+
- @zhin.js/adapter@1.1.7
|
|
10
|
+
- @zhin.js/core@1.5.5
|
|
11
|
+
- @zhin.js/agent@1.1.6
|
|
12
|
+
- zhin.js@6.0.5
|
|
13
|
+
|
|
14
|
+
## 8.0.5
|
|
15
|
+
|
|
16
|
+
### Patch Changes
|
|
17
|
+
|
|
18
|
+
- 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`.
|
|
19
|
+
|
|
20
|
+
Remove the unused Message field and active-context escape hatch from PromptController; turn scheduling is keyed only by canonical session identity.
|
|
21
|
+
|
|
22
|
+
- Updated dependencies [6f9c366]
|
|
23
|
+
- Updated dependencies [373a56b]
|
|
24
|
+
- Updated dependencies [0de46a8]
|
|
25
|
+
- Updated dependencies [c106ecc]
|
|
26
|
+
- Updated dependencies [ca92e03]
|
|
27
|
+
- Updated dependencies [b0f37ae]
|
|
28
|
+
- Updated dependencies [ba08a2f]
|
|
29
|
+
- Updated dependencies [b08f7fe]
|
|
30
|
+
- Updated dependencies [daffd4c]
|
|
31
|
+
- Updated dependencies [36c7400]
|
|
32
|
+
- Updated dependencies [a9fa72e]
|
|
33
|
+
- Updated dependencies [c8de3ef]
|
|
34
|
+
- Updated dependencies [60f0fc8]
|
|
35
|
+
- Updated dependencies [574c990]
|
|
36
|
+
- Updated dependencies [d096f16]
|
|
37
|
+
- Updated dependencies [3f29623]
|
|
38
|
+
- Updated dependencies [2916852]
|
|
39
|
+
- Updated dependencies [d047869]
|
|
40
|
+
- Updated dependencies [162fa34]
|
|
41
|
+
- Updated dependencies [e62561e]
|
|
42
|
+
- Updated dependencies [3eeeb46]
|
|
43
|
+
- Updated dependencies [85d0f82]
|
|
44
|
+
- Updated dependencies [61bfc1c]
|
|
45
|
+
- Updated dependencies [04bad47]
|
|
46
|
+
- Updated dependencies [e40b048]
|
|
47
|
+
- Updated dependencies [5a5b1bb]
|
|
48
|
+
- Updated dependencies [f1708c3]
|
|
49
|
+
- Updated dependencies [d254a81]
|
|
50
|
+
- Updated dependencies [7123c47]
|
|
51
|
+
- Updated dependencies [05befc1]
|
|
52
|
+
- Updated dependencies [0663b6a]
|
|
53
|
+
- Updated dependencies [f28f9b3]
|
|
54
|
+
- Updated dependencies [9f340f7]
|
|
55
|
+
- Updated dependencies [e53444f]
|
|
56
|
+
- Updated dependencies [5eedd26]
|
|
57
|
+
- Updated dependencies [92b0dd7]
|
|
58
|
+
- Updated dependencies [f919b6f]
|
|
59
|
+
- Updated dependencies [098e411]
|
|
60
|
+
- Updated dependencies [e1b7c01]
|
|
61
|
+
- Updated dependencies [9b94f87]
|
|
62
|
+
- Updated dependencies [a7df753]
|
|
63
|
+
- @zhin.js/agent@1.1.5
|
|
64
|
+
- @zhin.js/im-contract@1.0.3
|
|
65
|
+
- @zhin.js/adapter@1.1.7
|
|
66
|
+
- @zhin.js/plugin-runtime@1.1.5
|
|
67
|
+
- @zhin.js/command@1.0.9
|
|
68
|
+
- @zhin.js/core@1.5.4
|
|
69
|
+
- zhin.js@6.0.4
|
|
70
|
+
|
|
71
|
+
## 8.0.4
|
|
72
|
+
|
|
73
|
+
### Patch Changes
|
|
74
|
+
|
|
75
|
+
- f8c7a54: fix: im
|
|
76
|
+
- Updated dependencies [f8c7a54]
|
|
77
|
+
- @zhin.js/logger@1.0.76
|
|
78
|
+
- @zhin.js/adapter@1.1.6
|
|
79
|
+
- @zhin.js/agent@1.1.4
|
|
80
|
+
- @zhin.js/command@1.0.8
|
|
81
|
+
- @zhin.js/core@1.5.3
|
|
82
|
+
- @zhin.js/im-contract@1.0.2
|
|
83
|
+
- @zhin.js/plugin-runtime@1.1.4
|
|
84
|
+
- zhin.js@6.0.3
|
|
85
|
+
|
|
3
86
|
## 8.0.3
|
|
4
87
|
|
|
5
88
|
### Patch Changes
|
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.js
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
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';
|
|
@@ -6,8 +6,8 @@ import { IcqqGuildCatalog, isIcqqGuildIpcEvent, normalizeIcqqGuildInboundMessage
|
|
|
6
6
|
import { IpcClient } from './ipc-client.js';
|
|
7
7
|
import { materializeOutboundBase64, resolveIcqqOutboundMediaMode, } from './outbound-media.js';
|
|
8
8
|
import { Actions, formatInboundContent, formatOutboundBody, icqqInboundConversation, icqqOutboundTarget, } from './protocol.js';
|
|
9
|
-
const logger = getLogger('icqq');
|
|
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,7 +173,7 @@ 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
178
|
async send({ conversation, payload }) {
|
|
191
179
|
if (!this.ipc) {
|
|
@@ -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: `${conversation.kind}:${conversation.id}`,
|
|
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,
|
|
@@ -501,7 +485,7 @@ export class IcqqIpcEndpoint {
|
|
|
501
485
|
admit(msg) {
|
|
502
486
|
const conversation = msg.conversation;
|
|
503
487
|
if (!this.#open) {
|
|
504
|
-
logger.debug(formatCompact({
|
|
488
|
+
this.#logger.debug(formatCompact({
|
|
505
489
|
op: 'icqq_inbound_skip',
|
|
506
490
|
reason: 'endpoint_closed',
|
|
507
491
|
endpoint: this.name,
|
|
@@ -516,33 +500,31 @@ export class IcqqIpcEndpoint {
|
|
|
516
500
|
content: msg.segments,
|
|
517
501
|
rawMessage: msg.content,
|
|
518
502
|
});
|
|
519
|
-
logger.
|
|
520
|
-
|
|
521
|
-
|
|
522
|
-
|
|
523
|
-
|
|
524
|
-
|
|
525
|
-
channelType: msg.channelType,
|
|
526
|
-
mentioned: mentioned || undefined,
|
|
527
|
-
preview: msg.content.slice(0, 80),
|
|
528
|
-
segments: summarizeSegmentTypes(msg.segments),
|
|
529
|
-
}));
|
|
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 ?? {};
|
|
530
509
|
void this.#options.gateway.receive({
|
|
531
510
|
conversation,
|
|
532
511
|
message: { conversation, id: msg.id },
|
|
533
512
|
content: msg.content,
|
|
534
513
|
...(msg.segments ? { segments: msg.segments } : {}),
|
|
535
514
|
sender: msg.sender,
|
|
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: '
|
|
523
|
+
this.#logger.warn(formatCompact({
|
|
524
|
+
op: 'recv',
|
|
525
|
+
endpoint: this.name,
|
|
545
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,
|
|
@@ -623,7 +605,11 @@ export class IcqqIpcEndpoint {
|
|
|
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,
|
|
@@ -663,7 +649,7 @@ export class IcqqIpcEndpoint {
|
|
|
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;
|
package/lib/icqq-inbox.js
CHANGED
|
@@ -27,7 +27,7 @@ export function buildIcqqInboxRequestRow(payload, base) {
|
|
|
27
27
|
const isGroup = requestType === 'group' || groupId != null;
|
|
28
28
|
return {
|
|
29
29
|
adapter: base.adapter,
|
|
30
|
-
endpoint_id: base.
|
|
30
|
+
endpoint_id: base.endpointKey,
|
|
31
31
|
platform_request_id: platformRequestId,
|
|
32
32
|
type: isGroup ? 'group' : (requestType ?? 'friend'),
|
|
33
33
|
scene_type: isGroup ? 'group' : null,
|
|
@@ -63,7 +63,7 @@ export function buildIcqqInboxNoticeRow(payload, base) {
|
|
|
63
63
|
const targetId = userId != null && String(userId) !== String(actorId ?? '') ? userId : undefined;
|
|
64
64
|
return {
|
|
65
65
|
adapter: base.adapter,
|
|
66
|
-
endpoint_id: base.
|
|
66
|
+
endpoint_id: base.endpointKey,
|
|
67
67
|
platform_notice_id: platformNoticeId,
|
|
68
68
|
type: noticeType,
|
|
69
69
|
scene_type: groupId != null ? 'group' : null,
|
|
@@ -92,7 +92,7 @@ export function buildIcqqSystemRequestRow(message, kind, base) {
|
|
|
92
92
|
return null;
|
|
93
93
|
return {
|
|
94
94
|
adapter: base.adapter,
|
|
95
|
-
endpoint_id: base.
|
|
95
|
+
endpoint_id: base.endpointKey,
|
|
96
96
|
platform_request_id: platformRequestId,
|
|
97
97
|
type: kind,
|
|
98
98
|
scene_type: kind === 'group' ? 'group' : null,
|
package/lib/protocol.d.ts
CHANGED
|
@@ -199,7 +199,7 @@ export declare const Actions: {
|
|
|
199
199
|
};
|
|
200
200
|
/** Plugin Runtime owner config (`plugins.<instanceKey>` / schema.json). */
|
|
201
201
|
export interface IcqqAdapterConfig {
|
|
202
|
-
readonly
|
|
202
|
+
readonly id?: string;
|
|
203
203
|
readonly autoReconnect?: boolean;
|
|
204
204
|
readonly outboundMedia?: 'file' | 'base64';
|
|
205
205
|
readonly rpc?: {
|
|
@@ -210,7 +210,7 @@ export interface IcqqAdapterConfig {
|
|
|
210
210
|
/** Transitional: legacy root `endpoints[]` with `context: icqq`. */
|
|
211
211
|
readonly endpoints?: ReadonlyArray<{
|
|
212
212
|
readonly context?: string;
|
|
213
|
-
readonly
|
|
213
|
+
readonly id?: string;
|
|
214
214
|
readonly autoReconnect?: boolean;
|
|
215
215
|
readonly outboundMedia?: 'file' | 'base64';
|
|
216
216
|
readonly rpc?: {
|
|
@@ -222,7 +222,7 @@ export interface IcqqAdapterConfig {
|
|
|
222
222
|
}
|
|
223
223
|
export interface ResolvedIcqqConfig {
|
|
224
224
|
readonly context: 'icqq';
|
|
225
|
-
readonly
|
|
225
|
+
readonly id: string;
|
|
226
226
|
readonly autoReconnect: boolean;
|
|
227
227
|
readonly outboundMedia?: 'file' | 'base64';
|
|
228
228
|
readonly rpc?: {
|
|
@@ -244,7 +244,10 @@ export interface IcqqInboundMessage {
|
|
|
244
244
|
* 与 content(CQ 原文纯文本视图)同源,随 gateway.receive 上送。
|
|
245
245
|
*/
|
|
246
246
|
readonly segments?: readonly MessageSegment[];
|
|
247
|
-
readonly sender:
|
|
247
|
+
readonly sender: {
|
|
248
|
+
readonly id: string;
|
|
249
|
+
readonly name?: string;
|
|
250
|
+
};
|
|
248
251
|
readonly channelType: 'private' | 'group' | 'channel';
|
|
249
252
|
readonly metadata?: Record<string, unknown>;
|
|
250
253
|
}
|
|
@@ -268,7 +271,7 @@ export declare function resolveIcqqConfig(config?: IcqqAdapterConfig): ResolvedI
|
|
|
268
271
|
* 入站归一化 → ConversationRef:群临时会话(temp)映射为群容器内的 private
|
|
269
272
|
* 会话(parent kind 'group');频道消息的所属 guild 进 `parent`(kind 'channel')。
|
|
270
273
|
*/
|
|
271
|
-
export declare function icqqInboundConversation(
|
|
274
|
+
export declare function icqqInboundConversation(endpointKey: string, input: {
|
|
272
275
|
readonly channelType: 'private' | 'group' | 'channel';
|
|
273
276
|
readonly channelId: string;
|
|
274
277
|
readonly channelParentGroupId?: string;
|
package/lib/protocol.js
CHANGED
|
@@ -188,9 +188,9 @@ export const Actions = {
|
|
|
188
188
|
};
|
|
189
189
|
export function resolveIcqqConfig(config = {}) {
|
|
190
190
|
const entry = config.endpoints?.find((item) => item.context === 'icqq');
|
|
191
|
-
const
|
|
192
|
-
if (!/^\d+$/.test(
|
|
193
|
-
throw new TypeError('ICQQ adapter requires numeric
|
|
191
|
+
const id = pickCredential(config.id, entry?.id, process.env.ICQQ_ACCOUNT);
|
|
192
|
+
if (!/^\d+$/.test(id)) {
|
|
193
|
+
throw new TypeError('ICQQ adapter requires numeric id (QQ uin) via plugins.<key>.id or ICQQ_ACCOUNT');
|
|
194
194
|
}
|
|
195
195
|
const autoReconnect = config.autoReconnect ?? entry?.autoReconnect ?? true;
|
|
196
196
|
const outboundMedia = config.outboundMedia ?? entry?.outboundMedia;
|
|
@@ -200,7 +200,7 @@ export function resolveIcqqConfig(config = {}) {
|
|
|
200
200
|
: undefined;
|
|
201
201
|
return {
|
|
202
202
|
context: 'icqq',
|
|
203
|
-
|
|
203
|
+
id,
|
|
204
204
|
autoReconnect,
|
|
205
205
|
...(outboundMedia ? { outboundMedia } : {}),
|
|
206
206
|
...(rpc ? { rpc } : {}),
|
|
@@ -210,8 +210,8 @@ export function resolveIcqqConfig(config = {}) {
|
|
|
210
210
|
* 入站归一化 → ConversationRef:群临时会话(temp)映射为群容器内的 private
|
|
211
211
|
* 会话(parent kind 'group');频道消息的所属 guild 进 `parent`(kind 'channel')。
|
|
212
212
|
*/
|
|
213
|
-
export function icqqInboundConversation(
|
|
214
|
-
const endpoint = { id:
|
|
213
|
+
export function icqqInboundConversation(endpointKey, input) {
|
|
214
|
+
const endpoint = { id: endpointKey, adapter: endpointKey.split('\0')[0] ?? endpointKey };
|
|
215
215
|
if (input.channelType === 'private' && input.channelParentGroupId) {
|
|
216
216
|
return {
|
|
217
217
|
endpoint,
|