@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/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@zhin.js/adapter-icqq",
|
|
3
|
-
"version": "8.0.
|
|
3
|
+
"version": "8.0.6",
|
|
4
4
|
"description": "Zhin.js ICQQ adapter for Plugin Runtime (IPC daemon client)",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "./lib/index.js",
|
|
@@ -34,20 +34,20 @@
|
|
|
34
34
|
"directory": "plugins/adapters/icqq"
|
|
35
35
|
},
|
|
36
36
|
"dependencies": {
|
|
37
|
-
"@zhin.js/adapter": "1.1.
|
|
38
|
-
"@zhin.js/command": "1.0.
|
|
39
|
-
"@zhin.js/core": "1.5.
|
|
40
|
-
"@zhin.js/im-contract": "1.0.
|
|
41
|
-
"@zhin.js/logger": "1.0.
|
|
42
|
-
"@zhin.js/plugin-runtime": "1.1.
|
|
37
|
+
"@zhin.js/adapter": "1.1.7",
|
|
38
|
+
"@zhin.js/command": "1.0.10",
|
|
39
|
+
"@zhin.js/core": "1.5.5",
|
|
40
|
+
"@zhin.js/im-contract": "1.0.3",
|
|
41
|
+
"@zhin.js/logger": "1.0.76",
|
|
42
|
+
"@zhin.js/plugin-runtime": "1.1.5"
|
|
43
43
|
},
|
|
44
44
|
"peerDependencies": {
|
|
45
45
|
"zod": "^4.0.0",
|
|
46
|
-
"@zhin.js/adapter": "1.1.
|
|
47
|
-
"@zhin.js/agent": "1.1.
|
|
48
|
-
"@zhin.js/core": "1.5.
|
|
49
|
-
"@zhin.js/plugin-runtime": "1.1.
|
|
50
|
-
"zhin.js": "6.0.
|
|
46
|
+
"@zhin.js/adapter": "1.1.7",
|
|
47
|
+
"@zhin.js/agent": "1.1.6",
|
|
48
|
+
"@zhin.js/core": "1.5.5",
|
|
49
|
+
"@zhin.js/plugin-runtime": "1.1.5",
|
|
50
|
+
"zhin.js": "6.0.5"
|
|
51
51
|
},
|
|
52
52
|
"peerDependenciesMeta": {
|
|
53
53
|
"zhin.js": {
|
|
@@ -65,8 +65,8 @@
|
|
|
65
65
|
"typescript": "^6.0.3",
|
|
66
66
|
"vitest": "^4.1.10",
|
|
67
67
|
"zod": "^4.4.3",
|
|
68
|
-
"@zhin.js/agent": "1.1.
|
|
69
|
-
"zhin.js": "6.0.
|
|
68
|
+
"@zhin.js/agent": "1.1.6",
|
|
69
|
+
"zhin.js": "6.0.5"
|
|
70
70
|
},
|
|
71
71
|
"files": [
|
|
72
72
|
"adapters",
|
package/schema.json
CHANGED
|
@@ -8,7 +8,7 @@
|
|
|
8
8
|
"string",
|
|
9
9
|
"number"
|
|
10
10
|
],
|
|
11
|
-
"description": "
|
|
11
|
+
"description": "框架 master QQ uin(/approve、AI/工具权限);endpoints[i].master 可逐项覆盖"
|
|
12
12
|
},
|
|
13
13
|
"trusted": {
|
|
14
14
|
"type": "array",
|
|
@@ -19,7 +19,7 @@
|
|
|
19
19
|
],
|
|
20
20
|
"description": "Trusted QQ uin"
|
|
21
21
|
},
|
|
22
|
-
"description": "
|
|
22
|
+
"description": "框架 trusted QQ uin 列表(弱于 master)"
|
|
23
23
|
},
|
|
24
24
|
"autoReconnect": {
|
|
25
25
|
"type": "boolean",
|
|
@@ -35,21 +35,17 @@
|
|
|
35
35
|
},
|
|
36
36
|
"endpoints": {
|
|
37
37
|
"type": "array",
|
|
38
|
-
"description": "多账号:一个插件实例挂多个 endpoint。每项与顶层字段同构(
|
|
38
|
+
"description": "多账号:一个插件实例挂多个 endpoint。每项与顶层字段同构(id 必填,其余覆盖顶层)",
|
|
39
39
|
"items": {
|
|
40
40
|
"type": "object",
|
|
41
41
|
"additionalProperties": true,
|
|
42
42
|
"properties": {
|
|
43
|
-
"name": {
|
|
44
|
-
"type": "string",
|
|
45
|
-
"description": "QQ uin (must match icqq login account)"
|
|
46
|
-
},
|
|
47
43
|
"master": {
|
|
48
44
|
"type": [
|
|
49
45
|
"string",
|
|
50
46
|
"number"
|
|
51
47
|
],
|
|
52
|
-
"description": "
|
|
48
|
+
"description": "本 endpoint 的框架 master QQ uin;覆盖顶层 master"
|
|
53
49
|
},
|
|
54
50
|
"trusted": {
|
|
55
51
|
"type": "array",
|
|
@@ -60,7 +56,7 @@
|
|
|
60
56
|
],
|
|
61
57
|
"description": "Trusted QQ uin"
|
|
62
58
|
},
|
|
63
|
-
"description": "
|
|
59
|
+
"description": "本 endpoint 的 trusted 列表"
|
|
64
60
|
},
|
|
65
61
|
"rpc": {
|
|
66
62
|
"type": "object",
|
|
@@ -84,10 +80,14 @@
|
|
|
84
80
|
"port",
|
|
85
81
|
"token"
|
|
86
82
|
]
|
|
83
|
+
},
|
|
84
|
+
"id": {
|
|
85
|
+
"type": "string",
|
|
86
|
+
"description": "QQ uin (must match icqq login account)"
|
|
87
87
|
}
|
|
88
88
|
},
|
|
89
89
|
"required": [
|
|
90
|
-
"
|
|
90
|
+
"id"
|
|
91
91
|
]
|
|
92
92
|
}
|
|
93
93
|
},
|
|
@@ -101,4 +101,4 @@
|
|
|
101
101
|
"master",
|
|
102
102
|
"endpoints"
|
|
103
103
|
]
|
|
104
|
-
}
|
|
104
|
+
}
|
package/src/endpoint.ts
CHANGED
|
@@ -8,7 +8,7 @@ import type {
|
|
|
8
8
|
EndpointSendRequest,
|
|
9
9
|
} from '@zhin.js/adapter';
|
|
10
10
|
import type { MessageGateway } from '@zhin.js/core/runtime';
|
|
11
|
-
import { formatCompact,
|
|
11
|
+
import { formatCompact, getAdapterLogger, truncatePreview } from '@zhin.js/logger';
|
|
12
12
|
import type { CapabilityId } from '@zhin.js/plugin-runtime';
|
|
13
13
|
import { registerIcqqAgentEndpoint } from './icqq-agent-deps.js';
|
|
14
14
|
import {
|
|
@@ -53,8 +53,6 @@ import {
|
|
|
53
53
|
} from './protocol.js';
|
|
54
54
|
import type { IpcFriendInfo, IpcGroupInfo, IpcMemberInfo, IpcSystemMessage } from './types.js';
|
|
55
55
|
|
|
56
|
-
const logger = getLogger('icqq');
|
|
57
|
-
|
|
58
56
|
/** Minimal IPC surface used by the endpoint (real IpcClient or test mock). */
|
|
59
57
|
export interface IcqqIpcTransport {
|
|
60
58
|
request(action: string, params?: Record<string, unknown>): Promise<IpcResponse>;
|
|
@@ -89,6 +87,8 @@ export interface IcqqEndpointOptions {
|
|
|
89
87
|
}
|
|
90
88
|
|
|
91
89
|
export class IcqqIpcEndpoint implements EndpointInstance {
|
|
90
|
+
readonly #logger!: ReturnType<typeof getAdapterLogger>;
|
|
91
|
+
|
|
92
92
|
readonly #options: IcqqEndpointOptions;
|
|
93
93
|
readonly #createIpc: CreateIcqqIpc;
|
|
94
94
|
readonly name: string;
|
|
@@ -131,8 +131,9 @@ export class IcqqIpcEndpoint implements EndpointInstance {
|
|
|
131
131
|
#reconnectRunning = false;
|
|
132
132
|
|
|
133
133
|
constructor(options: IcqqEndpointOptions) {
|
|
134
|
+
this.#logger = getAdapterLogger('icqq', options.config.id);
|
|
134
135
|
this.#options = options;
|
|
135
|
-
this.name = options.config.
|
|
136
|
+
this.name = options.config.id;
|
|
136
137
|
this.#createIpc = options.createIpc ?? defaultCreateIpc;
|
|
137
138
|
}
|
|
138
139
|
|
|
@@ -150,18 +151,14 @@ export class IcqqIpcEndpoint implements EndpointInstance {
|
|
|
150
151
|
try {
|
|
151
152
|
await this.#bindIpcSession();
|
|
152
153
|
this.#unregisterAgent = registerIcqqAgentEndpoint(this.name, this);
|
|
153
|
-
logger.info(
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
mode: this.#options.config.rpc ? 'rpc' : 'ipc',
|
|
157
|
-
friends: this.friends.size,
|
|
158
|
-
groups: this.groups.size,
|
|
159
|
-
}));
|
|
154
|
+
this.#logger.info(
|
|
155
|
+
`connected (${this.#options.config.rpc ? 'rpc' : 'ipc'}) | friends: ${this.friends.size} | groups: ${this.groups.size}`,
|
|
156
|
+
);
|
|
160
157
|
} catch (error) {
|
|
161
158
|
await this.stop();
|
|
162
159
|
// Startup connect failures are surfaced once (with stack) by AdapterIndex;
|
|
163
160
|
// keep this at debug to avoid a duplicate error-level log.
|
|
164
|
-
logger.debug(`Failed to connect ICQQ IPC: ${error instanceof Error ? error.stack ?? error.message : String(error)}`);
|
|
161
|
+
this.#logger.debug(`Failed to connect ICQQ IPC: ${error instanceof Error ? error.stack ?? error.message : String(error)}`);
|
|
165
162
|
throw error;
|
|
166
163
|
}
|
|
167
164
|
}
|
|
@@ -200,10 +197,9 @@ export class IcqqIpcEndpoint implements EndpointInstance {
|
|
|
200
197
|
const jitter = Math.floor(Math.random() * 400);
|
|
201
198
|
const delayMs = base + jitter;
|
|
202
199
|
// 首次断开 WARN,后续重试静默为 DEBUG,避免刷屏
|
|
203
|
-
const disconnectLog = attempt === 0 ? logger.warn.bind(logger) : logger.debug.bind(logger);
|
|
200
|
+
const disconnectLog = attempt === 0 ? this.#logger.warn.bind(this.#logger) : this.#logger.debug.bind(this.#logger);
|
|
204
201
|
disconnectLog(formatCompact({
|
|
205
202
|
op: 'disconnect',
|
|
206
|
-
endpoint: this.name,
|
|
207
203
|
ok: false,
|
|
208
204
|
delay_ms: delayMs,
|
|
209
205
|
attempt: attempt + 1,
|
|
@@ -212,17 +208,13 @@ export class IcqqIpcEndpoint implements EndpointInstance {
|
|
|
212
208
|
if (this.#intentionalDisconnect) break;
|
|
213
209
|
try {
|
|
214
210
|
await this.#bindIpcSession();
|
|
215
|
-
logger.info(
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
attempts: attempt + 1,
|
|
220
|
-
friends: this.friends.size,
|
|
221
|
-
groups: this.groups.size,
|
|
222
|
-
}));
|
|
211
|
+
this.#logger.info(
|
|
212
|
+
`reconnected after ${attempt + 1} attempt(s)`
|
|
213
|
+
+ ` | friends: ${this.friends.size} | groups: ${this.groups.size}`,
|
|
214
|
+
);
|
|
223
215
|
break;
|
|
224
216
|
} catch (error) {
|
|
225
|
-
const retryLog = attempt === 0 ? logger.warn.bind(logger) : logger.debug.bind(logger);
|
|
217
|
+
const retryLog = attempt === 0 ? this.#logger.warn.bind(this.#logger) : this.#logger.debug.bind(this.#logger);
|
|
226
218
|
retryLog(formatCompact({
|
|
227
219
|
op: 'reconnect',
|
|
228
220
|
endpoint: this.name,
|
|
@@ -267,7 +259,7 @@ export class IcqqIpcEndpoint implements EndpointInstance {
|
|
|
267
259
|
// 置空 ipc:start 前 / stop 后的 request() 走防御性报错而非 TypeError
|
|
268
260
|
this.ipc = undefined as unknown as IcqqIpcTransport;
|
|
269
261
|
this.#started = false;
|
|
270
|
-
logger.debug(formatCompact({ op: 'disconnect'
|
|
262
|
+
this.#logger.debug(formatCompact({ op: 'disconnect' }));
|
|
271
263
|
}
|
|
272
264
|
|
|
273
265
|
async send({ conversation, payload }: EndpointSendRequest): Promise<string> {
|
|
@@ -310,17 +302,17 @@ export class IcqqIpcEndpoint implements EndpointInstance {
|
|
|
310
302
|
}
|
|
311
303
|
const resp = await this.ipc.request(action, params);
|
|
312
304
|
if (!resp.ok) {
|
|
305
|
+
this.#logger.warn(
|
|
306
|
+
`send failed ${conversation.kind}:${conversation.id} | ${resp.error} | ${truncatePreview(typeof message === 'string' ? message : String(message), 80)}`,
|
|
307
|
+
);
|
|
313
308
|
throw new Error(`发送消息失败: ${resp.error}`);
|
|
314
309
|
}
|
|
315
310
|
const messageId = String(
|
|
316
311
|
(resp.data as { message_id?: unknown } | undefined)?.message_id ?? `sent_${Date.now()}`,
|
|
317
312
|
);
|
|
318
|
-
logger.
|
|
319
|
-
|
|
320
|
-
|
|
321
|
-
target: `${conversation.kind}:${conversation.id}`,
|
|
322
|
-
messageId,
|
|
323
|
-
}));
|
|
313
|
+
this.#logger.info(
|
|
314
|
+
`send ${conversation.kind}:${conversation.id} | id: ${messageId} | ${truncatePreview(typeof message === 'string' ? message : String(message), 80)}`,
|
|
315
|
+
);
|
|
324
316
|
return messageId;
|
|
325
317
|
}
|
|
326
318
|
|
|
@@ -353,7 +345,7 @@ export class IcqqIpcEndpoint implements EndpointInstance {
|
|
|
353
345
|
id: emojiId,
|
|
354
346
|
});
|
|
355
347
|
if (!resp.ok) {
|
|
356
|
-
logger.warn(formatCompact({
|
|
348
|
+
this.#logger.warn(formatCompact({
|
|
357
349
|
op: 'add_reaction',
|
|
358
350
|
endpoint: this.name,
|
|
359
351
|
message_id: messageId,
|
|
@@ -364,7 +356,7 @@ export class IcqqIpcEndpoint implements EndpointInstance {
|
|
|
364
356
|
}));
|
|
365
357
|
return null;
|
|
366
358
|
}
|
|
367
|
-
logger.debug(formatCompact({
|
|
359
|
+
this.#logger.debug(formatCompact({
|
|
368
360
|
op: 'add_reaction',
|
|
369
361
|
endpoint: this.name,
|
|
370
362
|
message_id: messageId,
|
|
@@ -374,7 +366,7 @@ export class IcqqIpcEndpoint implements EndpointInstance {
|
|
|
374
366
|
}));
|
|
375
367
|
return emojiId;
|
|
376
368
|
} catch (error) {
|
|
377
|
-
logger.warn(formatCompact({
|
|
369
|
+
this.#logger.warn(formatCompact({
|
|
378
370
|
op: 'add_reaction',
|
|
379
371
|
endpoint: this.name,
|
|
380
372
|
message_id: messageId,
|
|
@@ -395,7 +387,7 @@ export class IcqqIpcEndpoint implements EndpointInstance {
|
|
|
395
387
|
id: emojiId,
|
|
396
388
|
});
|
|
397
389
|
if (!resp.ok) {
|
|
398
|
-
logger.warn(formatCompact({
|
|
390
|
+
this.#logger.warn(formatCompact({
|
|
399
391
|
op: 'remove_reaction',
|
|
400
392
|
endpoint: this.name,
|
|
401
393
|
message_id: messageId,
|
|
@@ -406,7 +398,7 @@ export class IcqqIpcEndpoint implements EndpointInstance {
|
|
|
406
398
|
}));
|
|
407
399
|
return;
|
|
408
400
|
}
|
|
409
|
-
logger.debug(formatCompact({
|
|
401
|
+
this.#logger.debug(formatCompact({
|
|
410
402
|
op: 'remove_reaction',
|
|
411
403
|
endpoint: this.name,
|
|
412
404
|
message_id: messageId,
|
|
@@ -415,7 +407,7 @@ export class IcqqIpcEndpoint implements EndpointInstance {
|
|
|
415
407
|
ok: true,
|
|
416
408
|
}));
|
|
417
409
|
} catch (error) {
|
|
418
|
-
logger.warn(formatCompact({
|
|
410
|
+
this.#logger.warn(formatCompact({
|
|
419
411
|
op: 'remove_reaction',
|
|
420
412
|
endpoint: this.name,
|
|
421
413
|
message_id: messageId,
|
|
@@ -432,7 +424,7 @@ export class IcqqIpcEndpoint implements EndpointInstance {
|
|
|
432
424
|
try {
|
|
433
425
|
const resp = await this.request(Actions.RECALL_MSG, { message_id: messageId });
|
|
434
426
|
if (!resp.ok) {
|
|
435
|
-
logger.warn(formatCompact({
|
|
427
|
+
this.#logger.warn(formatCompact({
|
|
436
428
|
op: 'recall_message',
|
|
437
429
|
endpoint: this.name,
|
|
438
430
|
message_id: messageId,
|
|
@@ -441,14 +433,14 @@ export class IcqqIpcEndpoint implements EndpointInstance {
|
|
|
441
433
|
}));
|
|
442
434
|
return;
|
|
443
435
|
}
|
|
444
|
-
logger.debug(formatCompact({
|
|
436
|
+
this.#logger.debug(formatCompact({
|
|
445
437
|
op: 'recall_message',
|
|
446
438
|
endpoint: this.name,
|
|
447
439
|
message_id: messageId,
|
|
448
440
|
ok: true,
|
|
449
441
|
}));
|
|
450
442
|
} catch (error) {
|
|
451
|
-
logger.warn(formatCompact({
|
|
443
|
+
this.#logger.warn(formatCompact({
|
|
452
444
|
op: 'recall_message',
|
|
453
445
|
endpoint: this.name,
|
|
454
446
|
message_id: messageId,
|
|
@@ -637,7 +629,7 @@ export class IcqqIpcEndpoint implements EndpointInstance {
|
|
|
637
629
|
admit(msg: IcqqInboundMessage): void {
|
|
638
630
|
const conversation = msg.conversation;
|
|
639
631
|
if (!this.#open) {
|
|
640
|
-
logger.debug(formatCompact({
|
|
632
|
+
this.#logger.debug(formatCompact({
|
|
641
633
|
op: 'icqq_inbound_skip',
|
|
642
634
|
reason: 'endpoint_closed',
|
|
643
635
|
endpoint: this.name,
|
|
@@ -652,33 +644,33 @@ export class IcqqIpcEndpoint implements EndpointInstance {
|
|
|
652
644
|
content: msg.segments,
|
|
653
645
|
rawMessage: msg.content,
|
|
654
646
|
});
|
|
655
|
-
logger.
|
|
656
|
-
|
|
657
|
-
|
|
658
|
-
|
|
659
|
-
|
|
660
|
-
|
|
661
|
-
|
|
662
|
-
|
|
663
|
-
preview: msg.content.slice(0, 80),
|
|
664
|
-
segments: summarizeSegmentTypes(msg.segments),
|
|
665
|
-
}));
|
|
647
|
+
this.#logger.info(
|
|
648
|
+
`recv ${conversation.kind}:${conversation.id}`
|
|
649
|
+
+ (msg.sender ? ` from ${msg.sender.name ?? msg.sender.id}` : '')
|
|
650
|
+
+ (mentioned ? ' (mentioned)' : '')
|
|
651
|
+
+ ` | ${truncatePreview(msg.content, 80)}`,
|
|
652
|
+
);
|
|
653
|
+
const quoteId = msg.metadata?.quote_id;
|
|
654
|
+
const { quote_id: _dropQuoteId, ...restMetadata } = msg.metadata ?? {};
|
|
666
655
|
void this.#options.gateway.receive({
|
|
667
656
|
conversation,
|
|
668
657
|
message: { conversation, id: msg.id },
|
|
669
658
|
content: msg.content,
|
|
670
659
|
...(msg.segments ? { segments: msg.segments } : {}),
|
|
671
660
|
sender: msg.sender,
|
|
661
|
+
endpointId: this.name,
|
|
662
|
+
...(mentioned ? { mentioned: true } : {}),
|
|
663
|
+
...(quoteId ? { replyTo: { id: String(quoteId) } } : {}),
|
|
672
664
|
metadata: Object.freeze({
|
|
673
|
-
endpoint: this.name,
|
|
674
665
|
channelType: msg.channelType,
|
|
675
|
-
...
|
|
676
|
-
...(mentioned ? { mentioned: true } : {}),
|
|
666
|
+
...restMetadata,
|
|
677
667
|
}),
|
|
678
668
|
}).catch((err) => {
|
|
679
|
-
logger.warn(formatCompact({
|
|
680
|
-
op: '
|
|
669
|
+
this.#logger.warn(formatCompact({
|
|
670
|
+
op: 'recv',
|
|
671
|
+
endpoint: this.name,
|
|
681
672
|
target: `${conversation.kind}:${conversation.id}`,
|
|
673
|
+
ok: false,
|
|
682
674
|
error: err instanceof Error ? err.message : String(err),
|
|
683
675
|
}));
|
|
684
676
|
});
|
|
@@ -721,7 +713,7 @@ export class IcqqIpcEndpoint implements EndpointInstance {
|
|
|
721
713
|
if (!isIcqqMessagePostType(payload)) return;
|
|
722
714
|
const data = payload as IcqqIpcMessageEvent;
|
|
723
715
|
if (shouldSkipSelfInboundMessage(data)) {
|
|
724
|
-
logger.debug(formatCompact({
|
|
716
|
+
this.#logger.debug(formatCompact({
|
|
725
717
|
op: 'icqq_inbound_skip',
|
|
726
718
|
reason: 'self',
|
|
727
719
|
endpoint: this.name,
|
|
@@ -732,7 +724,7 @@ export class IcqqIpcEndpoint implements EndpointInstance {
|
|
|
732
724
|
}
|
|
733
725
|
const normalized = normalizeIcqqInboundMessage(data);
|
|
734
726
|
if (!normalized) {
|
|
735
|
-
logger.debug(formatCompact({
|
|
727
|
+
this.#logger.debug(formatCompact({
|
|
736
728
|
op: 'icqq_inbound_skip',
|
|
737
729
|
reason: 'normalize_failed',
|
|
738
730
|
endpoint: this.name,
|
|
@@ -742,7 +734,7 @@ export class IcqqIpcEndpoint implements EndpointInstance {
|
|
|
742
734
|
return;
|
|
743
735
|
}
|
|
744
736
|
if (!this.#inboundDeduper.shouldProcess(normalized.messageId)) {
|
|
745
|
-
logger.debug(formatCompact({
|
|
737
|
+
this.#logger.debug(formatCompact({
|
|
746
738
|
op: 'icqq_inbound_skip',
|
|
747
739
|
reason: 'dedupe',
|
|
748
740
|
endpoint: this.name,
|
|
@@ -759,7 +751,11 @@ export class IcqqIpcEndpoint implements EndpointInstance {
|
|
|
759
751
|
}),
|
|
760
752
|
content: formatInboundContent(normalized.rawMessage),
|
|
761
753
|
segments: normalized.content,
|
|
762
|
-
sender:
|
|
754
|
+
sender: {
|
|
755
|
+
id: normalized.userId,
|
|
756
|
+
name: normalized.nickname || undefined,
|
|
757
|
+
...(normalized.senderRole ? { roles: [normalized.senderRole] } : {}),
|
|
758
|
+
},
|
|
763
759
|
channelType: normalized.channelType,
|
|
764
760
|
metadata: buildIcqqQuoteMetadata(data, {
|
|
765
761
|
nickname: normalized.nickname,
|
|
@@ -775,7 +771,7 @@ export class IcqqIpcEndpoint implements EndpointInstance {
|
|
|
775
771
|
payload as Parameters<typeof normalizeIcqqGuildInboundMessage>[0],
|
|
776
772
|
);
|
|
777
773
|
if (!normalized) {
|
|
778
|
-
logger.debug(formatCompact({
|
|
774
|
+
this.#logger.debug(formatCompact({
|
|
779
775
|
op: 'icqq_inbound_skip',
|
|
780
776
|
reason: 'guild_normalize_failed',
|
|
781
777
|
endpoint: this.name,
|
|
@@ -784,7 +780,7 @@ export class IcqqIpcEndpoint implements EndpointInstance {
|
|
|
784
780
|
}
|
|
785
781
|
this.#guildCatalog.upsertFromInbound(normalized.raw);
|
|
786
782
|
if (!this.#inboundDeduper.shouldProcess(`guild:${normalized.messageId}`)) {
|
|
787
|
-
logger.debug(formatCompact({
|
|
783
|
+
this.#logger.debug(formatCompact({
|
|
788
784
|
op: 'icqq_inbound_skip',
|
|
789
785
|
reason: 'dedupe',
|
|
790
786
|
endpoint: this.name,
|
|
@@ -801,7 +797,7 @@ export class IcqqIpcEndpoint implements EndpointInstance {
|
|
|
801
797
|
}),
|
|
802
798
|
content: formatInboundContent(normalized.rawMessage),
|
|
803
799
|
segments: normalized.content,
|
|
804
|
-
sender: normalized.userId,
|
|
800
|
+
sender: { id: normalized.userId, name: normalized.nickname || undefined },
|
|
805
801
|
channelType: 'channel',
|
|
806
802
|
metadata: {
|
|
807
803
|
guildId: normalized.guildId,
|
|
@@ -811,9 +807,9 @@ export class IcqqIpcEndpoint implements EndpointInstance {
|
|
|
811
807
|
}
|
|
812
808
|
|
|
813
809
|
/** 收件箱行公共前缀:adapter 槽 localName + endpoint live 名(uin)。 */
|
|
814
|
-
#inboxBase(): { adapter: string;
|
|
810
|
+
#inboxBase(): { adapter: string; endpointKey: string } {
|
|
815
811
|
const id = String(this.#options.id);
|
|
816
|
-
return { adapter: id.split('\0').pop() ?? id,
|
|
812
|
+
return { adapter: id.split('\0').pop() ?? id, endpointKey: this.name };
|
|
817
813
|
}
|
|
818
814
|
|
|
819
815
|
#recordInboxRequest(payload: Record<string, unknown>): void {
|
|
@@ -864,7 +860,7 @@ export class IcqqIpcEndpoint implements EndpointInstance {
|
|
|
864
860
|
hooks.publish?.('endpoint:request', row);
|
|
865
861
|
}
|
|
866
862
|
} catch (error) {
|
|
867
|
-
logger.debug(formatCompact({
|
|
863
|
+
this.#logger.debug(formatCompact({
|
|
868
864
|
op: 'inbox_pull_system_msg',
|
|
869
865
|
endpoint: this.name,
|
|
870
866
|
error: error instanceof Error ? error.message : String(error),
|
|
@@ -913,23 +909,8 @@ function toNumericId(value: number | string, label: string): number {
|
|
|
913
909
|
return n;
|
|
914
910
|
}
|
|
915
911
|
|
|
916
|
-
function summarizeSegmentTypes(
|
|
917
|
-
segments: readonly { readonly type?: unknown }[] | undefined,
|
|
918
|
-
): string | undefined {
|
|
919
|
-
if (!segments?.length) return undefined;
|
|
920
|
-
return segments
|
|
921
|
-
.map((segment) => {
|
|
922
|
-
if (typeof segment.type === 'string') return segment.type;
|
|
923
|
-
if (segment.type && typeof segment.type === 'object' && 'name' in segment.type) {
|
|
924
|
-
return String((segment.type as { name: unknown }).name);
|
|
925
|
-
}
|
|
926
|
-
return '?';
|
|
927
|
-
})
|
|
928
|
-
.join(',');
|
|
929
|
-
}
|
|
930
|
-
|
|
931
912
|
async function defaultCreateIpc(config: ResolvedIcqqConfig): Promise<IcqqIpcTransport> {
|
|
932
|
-
const uin = Number(config.
|
|
913
|
+
const uin = Number(config.id);
|
|
933
914
|
if (config.rpc) {
|
|
934
915
|
return IpcClient.connectRpc(config.rpc);
|
|
935
916
|
}
|
package/src/icqq-agent-deps.ts
CHANGED
|
@@ -18,7 +18,7 @@ export interface IcqqAgentEndpoint {
|
|
|
18
18
|
}
|
|
19
19
|
|
|
20
20
|
export interface IcqqAgentDeps {
|
|
21
|
-
getEndpoint: (
|
|
21
|
+
getEndpoint: (endpointKey: string) => IcqqAgentEndpoint;
|
|
22
22
|
getAdapter?: () => { getEndpoint: (id: string) => IcqqAgentEndpoint };
|
|
23
23
|
}
|
|
24
24
|
|
|
@@ -26,13 +26,13 @@ const endpoints = new Map<string, IcqqAgentEndpoint>();
|
|
|
26
26
|
let override: IcqqAgentDeps | null = null;
|
|
27
27
|
|
|
28
28
|
export function registerIcqqAgentEndpoint(
|
|
29
|
-
|
|
29
|
+
endpointKey: string,
|
|
30
30
|
endpoint: IcqqAgentEndpoint,
|
|
31
31
|
): () => void {
|
|
32
|
-
endpoints.set(
|
|
32
|
+
endpoints.set(endpointKey, endpoint);
|
|
33
33
|
return () => {
|
|
34
|
-
if (endpoints.get(
|
|
35
|
-
endpoints.delete(
|
|
34
|
+
if (endpoints.get(endpointKey) === endpoint) {
|
|
35
|
+
endpoints.delete(endpointKey);
|
|
36
36
|
}
|
|
37
37
|
};
|
|
38
38
|
}
|
|
@@ -42,9 +42,9 @@ export function setIcqqAgentDeps(deps: IcqqAgentDeps | null): void {
|
|
|
42
42
|
override = deps;
|
|
43
43
|
}
|
|
44
44
|
|
|
45
|
-
function lookup(
|
|
46
|
-
const registered = endpoints.get(
|
|
47
|
-
if (!registered) throw new Error(`Endpoint ${
|
|
45
|
+
function lookup(endpointKey: string): IcqqAgentEndpoint {
|
|
46
|
+
const registered = endpoints.get(endpointKey);
|
|
47
|
+
if (!registered) throw new Error(`Endpoint ${endpointKey} 不存在`);
|
|
48
48
|
return registered;
|
|
49
49
|
}
|
|
50
50
|
|
|
@@ -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';
|
|
@@ -17,18 +17,18 @@ export const icqqEndpointCommands = createEndpointCommands({
|
|
|
17
17
|
running: (use) => use(icqqRuntimeStateToken).endpoints.values(),
|
|
18
18
|
describeEntry: (entry) => (entry.rpc ? 'rpc(远程守护进程)' : 'ipc(本地守护进程)'),
|
|
19
19
|
addDescription: '登记 ICQQ endpoint(uin 需先经 icqq login 完成扫码登录;重启 zhin 生效)',
|
|
20
|
-
bindFlow: ({
|
|
21
|
-
if (!
|
|
20
|
+
bindFlow: ({ id }) => {
|
|
21
|
+
if (!id) {
|
|
22
22
|
return '用法:icqq.endpoint add <uin>(uin 为纯数字 QQ 号,且需先执行 icqq login <uin> 完成扫码登录)';
|
|
23
23
|
}
|
|
24
|
-
if (!/^\d+$/.test(
|
|
24
|
+
if (!/^\d+$/.test(id)) {
|
|
25
25
|
return 'icqq endpoint 名必须是纯数字 QQ 号(uin)';
|
|
26
26
|
}
|
|
27
27
|
try {
|
|
28
|
-
const filePath = addEndpointToConfig('icqq', {
|
|
28
|
+
const filePath = addEndpointToConfig('icqq', { id });
|
|
29
29
|
return (
|
|
30
|
-
`✅ endpoint「${
|
|
31
|
-
`⚠️ 请确保已执行 icqq login ${
|
|
30
|
+
`✅ endpoint「${id}」已追加到 ${filePath} 的 plugins.icqq.endpoints。\n` +
|
|
31
|
+
`⚠️ 请确保已执行 icqq login ${id} 启动守护进程并完成扫码登录;重启 zhin 后生效。`
|
|
32
32
|
);
|
|
33
33
|
} catch (error) {
|
|
34
34
|
return `添加失败:${error instanceof Error ? error.message : String(error)}`;
|
package/src/icqq-inbox.ts
CHANGED
|
@@ -11,7 +11,7 @@ import type { IpcSystemMessage } from './types.js';
|
|
|
11
11
|
/** 行级公共前缀:adapter 槽 localName + endpoint live 名(uin)。 */
|
|
12
12
|
export interface IcqqInboxBase {
|
|
13
13
|
readonly adapter: string;
|
|
14
|
-
readonly
|
|
14
|
+
readonly endpointKey: string;
|
|
15
15
|
}
|
|
16
16
|
|
|
17
17
|
/** post_type=request / request.* 判定(OneBot 事件壳)。 */
|
|
@@ -46,7 +46,7 @@ export function buildIcqqInboxRequestRow(
|
|
|
46
46
|
const isGroup = requestType === 'group' || groupId != null;
|
|
47
47
|
return {
|
|
48
48
|
adapter: base.adapter,
|
|
49
|
-
endpoint_id: base.
|
|
49
|
+
endpoint_id: base.endpointKey,
|
|
50
50
|
platform_request_id: platformRequestId,
|
|
51
51
|
type: isGroup ? 'group' : (requestType ?? 'friend'),
|
|
52
52
|
scene_type: isGroup ? 'group' : null,
|
|
@@ -85,7 +85,7 @@ export function buildIcqqInboxNoticeRow(
|
|
|
85
85
|
const targetId = userId != null && String(userId) !== String(actorId ?? '') ? userId : undefined;
|
|
86
86
|
return {
|
|
87
87
|
adapter: base.adapter,
|
|
88
|
-
endpoint_id: base.
|
|
88
|
+
endpoint_id: base.endpointKey,
|
|
89
89
|
platform_notice_id: platformNoticeId,
|
|
90
90
|
type: noticeType,
|
|
91
91
|
scene_type: groupId != null ? 'group' : null,
|
|
@@ -117,7 +117,7 @@ export function buildIcqqSystemRequestRow(
|
|
|
117
117
|
if (!platformRequestId) return null;
|
|
118
118
|
return {
|
|
119
119
|
adapter: base.adapter,
|
|
120
|
-
endpoint_id: base.
|
|
120
|
+
endpoint_id: base.endpointKey,
|
|
121
121
|
platform_request_id: platformRequestId,
|
|
122
122
|
type: kind,
|
|
123
123
|
scene_type: kind === 'group' ? 'group' : null,
|