@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/src/endpoint.ts
CHANGED
|
@@ -1,9 +1,14 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* IcqqIpcEndpoint — lifecycle, outbound, IPC subscribe/admit for ICQQ daemon.
|
|
3
3
|
*/
|
|
4
|
-
import type {
|
|
4
|
+
import type {
|
|
5
|
+
EndpointControl,
|
|
6
|
+
EndpointInstance,
|
|
7
|
+
EndpointManagement,
|
|
8
|
+
EndpointSendRequest,
|
|
9
|
+
} from '@zhin.js/adapter';
|
|
5
10
|
import type { MessageGateway } from '@zhin.js/core/runtime';
|
|
6
|
-
import { formatCompact,
|
|
11
|
+
import { formatCompact, getAdapterLogger, truncatePreview } from '@zhin.js/logger';
|
|
7
12
|
import type { CapabilityId } from '@zhin.js/plugin-runtime';
|
|
8
13
|
import { registerIcqqAgentEndpoint } from './icqq-agent-deps.js';
|
|
9
14
|
import {
|
|
@@ -38,9 +43,9 @@ import {
|
|
|
38
43
|
import {
|
|
39
44
|
Actions,
|
|
40
45
|
formatInboundContent,
|
|
41
|
-
formatInboundTarget,
|
|
42
46
|
formatOutboundBody,
|
|
43
|
-
|
|
47
|
+
icqqInboundConversation,
|
|
48
|
+
icqqOutboundTarget,
|
|
44
49
|
type IcqqInboundMessage,
|
|
45
50
|
type IpcEvent,
|
|
46
51
|
type IpcResponse,
|
|
@@ -48,8 +53,6 @@ import {
|
|
|
48
53
|
} from './protocol.js';
|
|
49
54
|
import type { IpcFriendInfo, IpcGroupInfo, IpcMemberInfo, IpcSystemMessage } from './types.js';
|
|
50
55
|
|
|
51
|
-
const logger = getLogger('icqq');
|
|
52
|
-
|
|
53
56
|
/** Minimal IPC surface used by the endpoint (real IpcClient or test mock). */
|
|
54
57
|
export interface IcqqIpcTransport {
|
|
55
58
|
request(action: string, params?: Record<string, unknown>): Promise<IpcResponse>;
|
|
@@ -84,6 +87,8 @@ export interface IcqqEndpointOptions {
|
|
|
84
87
|
}
|
|
85
88
|
|
|
86
89
|
export class IcqqIpcEndpoint implements EndpointInstance {
|
|
90
|
+
readonly #logger!: ReturnType<typeof getAdapterLogger>;
|
|
91
|
+
|
|
87
92
|
readonly #options: IcqqEndpointOptions;
|
|
88
93
|
readonly #createIpc: CreateIcqqIpc;
|
|
89
94
|
readonly name: string;
|
|
@@ -126,8 +131,9 @@ export class IcqqIpcEndpoint implements EndpointInstance {
|
|
|
126
131
|
#reconnectRunning = false;
|
|
127
132
|
|
|
128
133
|
constructor(options: IcqqEndpointOptions) {
|
|
134
|
+
this.#logger = getAdapterLogger('icqq', options.config.id);
|
|
129
135
|
this.#options = options;
|
|
130
|
-
this.name = options.config.
|
|
136
|
+
this.name = options.config.id;
|
|
131
137
|
this.#createIpc = options.createIpc ?? defaultCreateIpc;
|
|
132
138
|
}
|
|
133
139
|
|
|
@@ -145,18 +151,14 @@ export class IcqqIpcEndpoint implements EndpointInstance {
|
|
|
145
151
|
try {
|
|
146
152
|
await this.#bindIpcSession();
|
|
147
153
|
this.#unregisterAgent = registerIcqqAgentEndpoint(this.name, this);
|
|
148
|
-
logger.info(
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
mode: this.#options.config.rpc ? 'rpc' : 'ipc',
|
|
152
|
-
friends: this.friends.size,
|
|
153
|
-
groups: this.groups.size,
|
|
154
|
-
}));
|
|
154
|
+
this.#logger.info(
|
|
155
|
+
`connected (${this.#options.config.rpc ? 'rpc' : 'ipc'}) | friends: ${this.friends.size} | groups: ${this.groups.size}`,
|
|
156
|
+
);
|
|
155
157
|
} catch (error) {
|
|
156
158
|
await this.stop();
|
|
157
159
|
// Startup connect failures are surfaced once (with stack) by AdapterIndex;
|
|
158
160
|
// keep this at debug to avoid a duplicate error-level log.
|
|
159
|
-
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)}`);
|
|
160
162
|
throw error;
|
|
161
163
|
}
|
|
162
164
|
}
|
|
@@ -195,10 +197,9 @@ export class IcqqIpcEndpoint implements EndpointInstance {
|
|
|
195
197
|
const jitter = Math.floor(Math.random() * 400);
|
|
196
198
|
const delayMs = base + jitter;
|
|
197
199
|
// 首次断开 WARN,后续重试静默为 DEBUG,避免刷屏
|
|
198
|
-
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);
|
|
199
201
|
disconnectLog(formatCompact({
|
|
200
202
|
op: 'disconnect',
|
|
201
|
-
endpoint: this.name,
|
|
202
203
|
ok: false,
|
|
203
204
|
delay_ms: delayMs,
|
|
204
205
|
attempt: attempt + 1,
|
|
@@ -207,17 +208,13 @@ export class IcqqIpcEndpoint implements EndpointInstance {
|
|
|
207
208
|
if (this.#intentionalDisconnect) break;
|
|
208
209
|
try {
|
|
209
210
|
await this.#bindIpcSession();
|
|
210
|
-
logger.info(
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
attempts: attempt + 1,
|
|
215
|
-
friends: this.friends.size,
|
|
216
|
-
groups: this.groups.size,
|
|
217
|
-
}));
|
|
211
|
+
this.#logger.info(
|
|
212
|
+
`reconnected after ${attempt + 1} attempt(s)`
|
|
213
|
+
+ ` | friends: ${this.friends.size} | groups: ${this.groups.size}`,
|
|
214
|
+
);
|
|
218
215
|
break;
|
|
219
216
|
} catch (error) {
|
|
220
|
-
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);
|
|
221
218
|
retryLog(formatCompact({
|
|
222
219
|
op: 'reconnect',
|
|
223
220
|
endpoint: this.name,
|
|
@@ -262,10 +259,10 @@ export class IcqqIpcEndpoint implements EndpointInstance {
|
|
|
262
259
|
// 置空 ipc:start 前 / stop 后的 request() 走防御性报错而非 TypeError
|
|
263
260
|
this.ipc = undefined as unknown as IcqqIpcTransport;
|
|
264
261
|
this.#started = false;
|
|
265
|
-
logger.debug(formatCompact({ op: 'disconnect'
|
|
262
|
+
this.#logger.debug(formatCompact({ op: 'disconnect' }));
|
|
266
263
|
}
|
|
267
264
|
|
|
268
|
-
async send({
|
|
265
|
+
async send({ conversation, payload }: EndpointSendRequest): Promise<string> {
|
|
269
266
|
if (!this.ipc) {
|
|
270
267
|
throw new Error(`icqq endpoint ${this.name} 未连接(IPC 未初始化或已停止),无法发送消息`);
|
|
271
268
|
}
|
|
@@ -274,7 +271,7 @@ export class IcqqIpcEndpoint implements EndpointInstance {
|
|
|
274
271
|
? materializeOutboundBase64(payload, mediaMode)
|
|
275
272
|
: payload;
|
|
276
273
|
const message = formatOutboundBody(content);
|
|
277
|
-
const parsed =
|
|
274
|
+
const parsed = icqqOutboundTarget(conversation);
|
|
278
275
|
let action: string;
|
|
279
276
|
let params: Record<string, unknown>;
|
|
280
277
|
switch (parsed.kind) {
|
|
@@ -305,17 +302,17 @@ export class IcqqIpcEndpoint implements EndpointInstance {
|
|
|
305
302
|
}
|
|
306
303
|
const resp = await this.ipc.request(action, params);
|
|
307
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
|
+
);
|
|
308
308
|
throw new Error(`发送消息失败: ${resp.error}`);
|
|
309
309
|
}
|
|
310
310
|
const messageId = String(
|
|
311
311
|
(resp.data as { message_id?: unknown } | undefined)?.message_id ?? `sent_${Date.now()}`,
|
|
312
312
|
);
|
|
313
|
-
logger.
|
|
314
|
-
|
|
315
|
-
|
|
316
|
-
target,
|
|
317
|
-
messageId,
|
|
318
|
-
}));
|
|
313
|
+
this.#logger.info(
|
|
314
|
+
`send ${conversation.kind}:${conversation.id} | id: ${messageId} | ${truncatePreview(typeof message === 'string' ? message : String(message), 80)}`,
|
|
315
|
+
);
|
|
319
316
|
return messageId;
|
|
320
317
|
}
|
|
321
318
|
|
|
@@ -348,7 +345,7 @@ export class IcqqIpcEndpoint implements EndpointInstance {
|
|
|
348
345
|
id: emojiId,
|
|
349
346
|
});
|
|
350
347
|
if (!resp.ok) {
|
|
351
|
-
logger.warn(formatCompact({
|
|
348
|
+
this.#logger.warn(formatCompact({
|
|
352
349
|
op: 'add_reaction',
|
|
353
350
|
endpoint: this.name,
|
|
354
351
|
message_id: messageId,
|
|
@@ -359,7 +356,7 @@ export class IcqqIpcEndpoint implements EndpointInstance {
|
|
|
359
356
|
}));
|
|
360
357
|
return null;
|
|
361
358
|
}
|
|
362
|
-
logger.debug(formatCompact({
|
|
359
|
+
this.#logger.debug(formatCompact({
|
|
363
360
|
op: 'add_reaction',
|
|
364
361
|
endpoint: this.name,
|
|
365
362
|
message_id: messageId,
|
|
@@ -369,7 +366,7 @@ export class IcqqIpcEndpoint implements EndpointInstance {
|
|
|
369
366
|
}));
|
|
370
367
|
return emojiId;
|
|
371
368
|
} catch (error) {
|
|
372
|
-
logger.warn(formatCompact({
|
|
369
|
+
this.#logger.warn(formatCompact({
|
|
373
370
|
op: 'add_reaction',
|
|
374
371
|
endpoint: this.name,
|
|
375
372
|
message_id: messageId,
|
|
@@ -390,7 +387,7 @@ export class IcqqIpcEndpoint implements EndpointInstance {
|
|
|
390
387
|
id: emojiId,
|
|
391
388
|
});
|
|
392
389
|
if (!resp.ok) {
|
|
393
|
-
logger.warn(formatCompact({
|
|
390
|
+
this.#logger.warn(formatCompact({
|
|
394
391
|
op: 'remove_reaction',
|
|
395
392
|
endpoint: this.name,
|
|
396
393
|
message_id: messageId,
|
|
@@ -401,7 +398,7 @@ export class IcqqIpcEndpoint implements EndpointInstance {
|
|
|
401
398
|
}));
|
|
402
399
|
return;
|
|
403
400
|
}
|
|
404
|
-
logger.debug(formatCompact({
|
|
401
|
+
this.#logger.debug(formatCompact({
|
|
405
402
|
op: 'remove_reaction',
|
|
406
403
|
endpoint: this.name,
|
|
407
404
|
message_id: messageId,
|
|
@@ -410,7 +407,7 @@ export class IcqqIpcEndpoint implements EndpointInstance {
|
|
|
410
407
|
ok: true,
|
|
411
408
|
}));
|
|
412
409
|
} catch (error) {
|
|
413
|
-
logger.warn(formatCompact({
|
|
410
|
+
this.#logger.warn(formatCompact({
|
|
414
411
|
op: 'remove_reaction',
|
|
415
412
|
endpoint: this.name,
|
|
416
413
|
message_id: messageId,
|
|
@@ -427,7 +424,7 @@ export class IcqqIpcEndpoint implements EndpointInstance {
|
|
|
427
424
|
try {
|
|
428
425
|
const resp = await this.request(Actions.RECALL_MSG, { message_id: messageId });
|
|
429
426
|
if (!resp.ok) {
|
|
430
|
-
logger.warn(formatCompact({
|
|
427
|
+
this.#logger.warn(formatCompact({
|
|
431
428
|
op: 'recall_message',
|
|
432
429
|
endpoint: this.name,
|
|
433
430
|
message_id: messageId,
|
|
@@ -436,14 +433,14 @@ export class IcqqIpcEndpoint implements EndpointInstance {
|
|
|
436
433
|
}));
|
|
437
434
|
return;
|
|
438
435
|
}
|
|
439
|
-
logger.debug(formatCompact({
|
|
436
|
+
this.#logger.debug(formatCompact({
|
|
440
437
|
op: 'recall_message',
|
|
441
438
|
endpoint: this.name,
|
|
442
439
|
message_id: messageId,
|
|
443
440
|
ok: true,
|
|
444
441
|
}));
|
|
445
442
|
} catch (error) {
|
|
446
|
-
logger.warn(formatCompact({
|
|
443
|
+
this.#logger.warn(formatCompact({
|
|
447
444
|
op: 'recall_message',
|
|
448
445
|
endpoint: this.name,
|
|
449
446
|
message_id: messageId,
|
|
@@ -630,13 +627,14 @@ export class IcqqIpcEndpoint implements EndpointInstance {
|
|
|
630
627
|
|
|
631
628
|
/** Test / internal: admit when open. */
|
|
632
629
|
admit(msg: IcqqInboundMessage): void {
|
|
630
|
+
const conversation = msg.conversation;
|
|
633
631
|
if (!this.#open) {
|
|
634
|
-
logger.debug(formatCompact({
|
|
632
|
+
this.#logger.debug(formatCompact({
|
|
635
633
|
op: 'icqq_inbound_skip',
|
|
636
634
|
reason: 'endpoint_closed',
|
|
637
635
|
endpoint: this.name,
|
|
638
636
|
id: msg.id,
|
|
639
|
-
target:
|
|
637
|
+
target: `${conversation.kind}:${conversation.id}`,
|
|
640
638
|
}));
|
|
641
639
|
return;
|
|
642
640
|
}
|
|
@@ -646,34 +644,33 @@ export class IcqqIpcEndpoint implements EndpointInstance {
|
|
|
646
644
|
content: msg.segments,
|
|
647
645
|
rawMessage: msg.content,
|
|
648
646
|
});
|
|
649
|
-
logger.
|
|
650
|
-
|
|
651
|
-
|
|
652
|
-
|
|
653
|
-
|
|
654
|
-
|
|
655
|
-
|
|
656
|
-
|
|
657
|
-
preview: msg.content.slice(0, 80),
|
|
658
|
-
segments: summarizeSegmentTypes(msg.segments),
|
|
659
|
-
}));
|
|
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 ?? {};
|
|
660
655
|
void this.#options.gateway.receive({
|
|
661
|
-
|
|
662
|
-
|
|
656
|
+
conversation,
|
|
657
|
+
message: { conversation, id: msg.id },
|
|
663
658
|
content: msg.content,
|
|
664
659
|
...(msg.segments ? { segments: msg.segments } : {}),
|
|
665
660
|
sender: msg.sender,
|
|
666
|
-
|
|
661
|
+
endpointId: this.name,
|
|
662
|
+
...(mentioned ? { mentioned: true } : {}),
|
|
663
|
+
...(quoteId ? { replyTo: { id: String(quoteId) } } : {}),
|
|
667
664
|
metadata: Object.freeze({
|
|
668
|
-
endpoint: this.name,
|
|
669
665
|
channelType: msg.channelType,
|
|
670
|
-
...
|
|
671
|
-
...(mentioned ? { mentioned: true } : {}),
|
|
666
|
+
...restMetadata,
|
|
672
667
|
}),
|
|
673
668
|
}).catch((err) => {
|
|
674
|
-
logger.warn(formatCompact({
|
|
675
|
-
op: '
|
|
676
|
-
|
|
669
|
+
this.#logger.warn(formatCompact({
|
|
670
|
+
op: 'recv',
|
|
671
|
+
endpoint: this.name,
|
|
672
|
+
target: `${conversation.kind}:${conversation.id}`,
|
|
673
|
+
ok: false,
|
|
677
674
|
error: err instanceof Error ? err.message : String(err),
|
|
678
675
|
}));
|
|
679
676
|
});
|
|
@@ -716,7 +713,7 @@ export class IcqqIpcEndpoint implements EndpointInstance {
|
|
|
716
713
|
if (!isIcqqMessagePostType(payload)) return;
|
|
717
714
|
const data = payload as IcqqIpcMessageEvent;
|
|
718
715
|
if (shouldSkipSelfInboundMessage(data)) {
|
|
719
|
-
logger.debug(formatCompact({
|
|
716
|
+
this.#logger.debug(formatCompact({
|
|
720
717
|
op: 'icqq_inbound_skip',
|
|
721
718
|
reason: 'self',
|
|
722
719
|
endpoint: this.name,
|
|
@@ -727,7 +724,7 @@ export class IcqqIpcEndpoint implements EndpointInstance {
|
|
|
727
724
|
}
|
|
728
725
|
const normalized = normalizeIcqqInboundMessage(data);
|
|
729
726
|
if (!normalized) {
|
|
730
|
-
logger.debug(formatCompact({
|
|
727
|
+
this.#logger.debug(formatCompact({
|
|
731
728
|
op: 'icqq_inbound_skip',
|
|
732
729
|
reason: 'normalize_failed',
|
|
733
730
|
endpoint: this.name,
|
|
@@ -737,7 +734,7 @@ export class IcqqIpcEndpoint implements EndpointInstance {
|
|
|
737
734
|
return;
|
|
738
735
|
}
|
|
739
736
|
if (!this.#inboundDeduper.shouldProcess(normalized.messageId)) {
|
|
740
|
-
logger.debug(formatCompact({
|
|
737
|
+
this.#logger.debug(formatCompact({
|
|
741
738
|
op: 'icqq_inbound_skip',
|
|
742
739
|
reason: 'dedupe',
|
|
743
740
|
endpoint: this.name,
|
|
@@ -747,14 +744,18 @@ export class IcqqIpcEndpoint implements EndpointInstance {
|
|
|
747
744
|
}
|
|
748
745
|
this.admit({
|
|
749
746
|
id: normalized.messageId,
|
|
750
|
-
|
|
747
|
+
conversation: icqqInboundConversation(String(this.#options.id), {
|
|
751
748
|
channelType: normalized.channelType,
|
|
752
749
|
channelId: normalized.channelId,
|
|
753
750
|
channelParentGroupId: normalized.channelParentGroupId,
|
|
754
751
|
}),
|
|
755
752
|
content: formatInboundContent(normalized.rawMessage),
|
|
756
753
|
segments: normalized.content,
|
|
757
|
-
sender:
|
|
754
|
+
sender: {
|
|
755
|
+
id: normalized.userId,
|
|
756
|
+
name: normalized.nickname || undefined,
|
|
757
|
+
...(normalized.senderRole ? { roles: [normalized.senderRole] } : {}),
|
|
758
|
+
},
|
|
758
759
|
channelType: normalized.channelType,
|
|
759
760
|
metadata: buildIcqqQuoteMetadata(data, {
|
|
760
761
|
nickname: normalized.nickname,
|
|
@@ -770,7 +771,7 @@ export class IcqqIpcEndpoint implements EndpointInstance {
|
|
|
770
771
|
payload as Parameters<typeof normalizeIcqqGuildInboundMessage>[0],
|
|
771
772
|
);
|
|
772
773
|
if (!normalized) {
|
|
773
|
-
logger.debug(formatCompact({
|
|
774
|
+
this.#logger.debug(formatCompact({
|
|
774
775
|
op: 'icqq_inbound_skip',
|
|
775
776
|
reason: 'guild_normalize_failed',
|
|
776
777
|
endpoint: this.name,
|
|
@@ -779,7 +780,7 @@ export class IcqqIpcEndpoint implements EndpointInstance {
|
|
|
779
780
|
}
|
|
780
781
|
this.#guildCatalog.upsertFromInbound(normalized.raw);
|
|
781
782
|
if (!this.#inboundDeduper.shouldProcess(`guild:${normalized.messageId}`)) {
|
|
782
|
-
logger.debug(formatCompact({
|
|
783
|
+
this.#logger.debug(formatCompact({
|
|
783
784
|
op: 'icqq_inbound_skip',
|
|
784
785
|
reason: 'dedupe',
|
|
785
786
|
endpoint: this.name,
|
|
@@ -789,14 +790,14 @@ export class IcqqIpcEndpoint implements EndpointInstance {
|
|
|
789
790
|
}
|
|
790
791
|
this.admit({
|
|
791
792
|
id: normalized.messageId,
|
|
792
|
-
|
|
793
|
+
conversation: icqqInboundConversation(String(this.#options.id), {
|
|
793
794
|
channelType: 'channel',
|
|
794
795
|
channelId: normalized.channelId,
|
|
795
796
|
guildId: normalized.guildId,
|
|
796
797
|
}),
|
|
797
798
|
content: formatInboundContent(normalized.rawMessage),
|
|
798
799
|
segments: normalized.content,
|
|
799
|
-
sender: normalized.userId,
|
|
800
|
+
sender: { id: normalized.userId, name: normalized.nickname || undefined },
|
|
800
801
|
channelType: 'channel',
|
|
801
802
|
metadata: {
|
|
802
803
|
guildId: normalized.guildId,
|
|
@@ -806,9 +807,9 @@ export class IcqqIpcEndpoint implements EndpointInstance {
|
|
|
806
807
|
}
|
|
807
808
|
|
|
808
809
|
/** 收件箱行公共前缀:adapter 槽 localName + endpoint live 名(uin)。 */
|
|
809
|
-
#inboxBase(): { adapter: string;
|
|
810
|
+
#inboxBase(): { adapter: string; endpointKey: string } {
|
|
810
811
|
const id = String(this.#options.id);
|
|
811
|
-
return { adapter: id.split('\0').pop() ?? id,
|
|
812
|
+
return { adapter: id.split('\0').pop() ?? id, endpointKey: this.name };
|
|
812
813
|
}
|
|
813
814
|
|
|
814
815
|
#recordInboxRequest(payload: Record<string, unknown>): void {
|
|
@@ -859,7 +860,7 @@ export class IcqqIpcEndpoint implements EndpointInstance {
|
|
|
859
860
|
hooks.publish?.('endpoint:request', row);
|
|
860
861
|
}
|
|
861
862
|
} catch (error) {
|
|
862
|
-
logger.debug(formatCompact({
|
|
863
|
+
this.#logger.debug(formatCompact({
|
|
863
864
|
op: 'inbox_pull_system_msg',
|
|
864
865
|
endpoint: this.name,
|
|
865
866
|
error: error instanceof Error ? error.message : String(error),
|
|
@@ -908,23 +909,8 @@ function toNumericId(value: number | string, label: string): number {
|
|
|
908
909
|
return n;
|
|
909
910
|
}
|
|
910
911
|
|
|
911
|
-
function summarizeSegmentTypes(
|
|
912
|
-
segments: readonly { readonly type?: unknown }[] | undefined,
|
|
913
|
-
): string | undefined {
|
|
914
|
-
if (!segments?.length) return undefined;
|
|
915
|
-
return segments
|
|
916
|
-
.map((segment) => {
|
|
917
|
-
if (typeof segment.type === 'string') return segment.type;
|
|
918
|
-
if (segment.type && typeof segment.type === 'object' && 'name' in segment.type) {
|
|
919
|
-
return String((segment.type as { name: unknown }).name);
|
|
920
|
-
}
|
|
921
|
-
return '?';
|
|
922
|
-
})
|
|
923
|
-
.join(',');
|
|
924
|
-
}
|
|
925
|
-
|
|
926
912
|
async function defaultCreateIpc(config: ResolvedIcqqConfig): Promise<IcqqIpcTransport> {
|
|
927
|
-
const uin = Number(config.
|
|
913
|
+
const uin = Number(config.id);
|
|
928
914
|
if (config.rpc) {
|
|
929
915
|
return IpcClient.connectRpc(config.rpc);
|
|
930
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,
|
package/src/index.ts
CHANGED