@zhin.js/adapter-icqq 1.1.0 → 1.1.3
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 +107 -0
- package/README.md +15 -6
- package/adapters/{icqq.js → icqq/index.js} +6 -6
- package/adapters/{icqq.ts → icqq/index.ts} +10 -10
- package/agents/icqq/agent.json +20 -0
- package/agents/icqq/boundaries.md +3 -0
- package/agents/icqq/conventions.md +3 -0
- package/agents/icqq/skills/icqq-directory/SKILL.md +20 -0
- package/{tools/friend_list.js → agents/icqq/skills/icqq-directory/tools/friend_list/index.js} +2 -1
- package/{tools/friend_list.ts → agents/icqq/skills/icqq-directory/tools/friend_list/index.ts} +2 -1
- package/{tools/group_list.js → agents/icqq/skills/icqq-directory/tools/group_list/index.js} +2 -1
- package/{tools/group_list.ts → agents/icqq/skills/icqq-directory/tools/group_list/index.ts} +2 -1
- package/{tools/group_member_info.js → agents/icqq/skills/icqq-directory/tools/group_member_info/index.js} +3 -1
- package/{tools/group_member_info.ts → agents/icqq/skills/icqq-directory/tools/group_member_info/index.ts} +3 -1
- package/{tools/user_info.js → agents/icqq/skills/icqq-directory/tools/user_info/index.js} +1 -0
- package/{tools/user_info.ts → agents/icqq/skills/icqq-directory/tools/user_info/index.ts} +1 -0
- package/agents/icqq/skills/icqq-group-admin/SKILL.md +25 -0
- package/{tools/announce.js → agents/icqq/skills/icqq-group-admin/tools/announce/index.js} +2 -0
- package/{tools/announce.ts → agents/icqq/skills/icqq-group-admin/tools/announce/index.ts} +2 -0
- package/{tools/essence.js → agents/icqq/skills/icqq-group-admin/tools/essence/index.js} +3 -1
- package/{tools/essence.ts → agents/icqq/skills/icqq-group-admin/tools/essence/index.ts} +3 -1
- package/{tools/group_invite.js → agents/icqq/skills/icqq-group-admin/tools/group_invite/index.js} +1 -1
- package/{tools/group_invite.ts → agents/icqq/skills/icqq-group-admin/tools/group_invite/index.ts} +1 -1
- package/{tools/group_kick.js → agents/icqq/skills/icqq-group-admin/tools/group_kick/index.js} +4 -3
- package/{tools/group_kick.ts → agents/icqq/skills/icqq-group-admin/tools/group_kick/index.ts} +4 -3
- package/{tools/set_anonymous.js → agents/icqq/skills/icqq-group-admin/tools/set_anonymous/index.js} +3 -1
- package/{tools/set_anonymous.ts → agents/icqq/skills/icqq-group-admin/tools/set_anonymous/index.ts} +3 -1
- package/{tools/set_mute.js → agents/icqq/skills/icqq-group-admin/tools/set_mute/index.js} +3 -2
- package/{tools/set_mute.ts → agents/icqq/skills/icqq-group-admin/tools/set_mute/index.ts} +3 -2
- package/{tools/set_title.js → agents/icqq/skills/icqq-group-admin/tools/set_title/index.js} +3 -2
- package/{tools/set_title.ts → agents/icqq/skills/icqq-group-admin/tools/set_title/index.ts} +3 -2
- package/agents/icqq/skills/icqq-group-state/SKILL.md +17 -0
- package/{tools/group_files.js → agents/icqq/skills/icqq-group-state/tools/group_files/index.js} +3 -1
- package/{tools/group_files.ts → agents/icqq/skills/icqq-group-state/tools/group_files/index.ts} +3 -1
- package/{tools/list_muted.js → agents/icqq/skills/icqq-group-state/tools/list_muted/index.js} +2 -1
- package/{tools/list_muted.ts → agents/icqq/skills/icqq-group-state/tools/list_muted/index.ts} +2 -1
- package/agents/icqq/skills/icqq-interaction/SKILL.md +19 -0
- package/{tools/group_sign.js → agents/icqq/skills/icqq-interaction/tools/group_sign/index.js} +2 -1
- package/{tools/group_sign.ts → agents/icqq/skills/icqq-interaction/tools/group_sign/index.ts} +2 -1
- package/{tools/poke.js → agents/icqq/skills/icqq-interaction/tools/poke/index.js} +2 -1
- package/{tools/poke.ts → agents/icqq/skills/icqq-interaction/tools/poke/index.ts} +2 -1
- package/{tools/send_user_like.js → agents/icqq/skills/icqq-interaction/tools/send_user_like/index.js} +4 -3
- package/{tools/send_user_like.ts → agents/icqq/skills/icqq-interaction/tools/send_user_like/index.ts} +3 -3
- package/agents/icqq/system.md +3 -0
- package/commands/{endpoint/add/[[id]].js → icqq/endpoint/add/[[id]]/index.js} +1 -1
- package/commands/icqq/endpoint/add/[[id]]/index.ts +3 -0
- package/{lib/icqq-endpoint-commands.js → commands/icqq/endpoint/definition.js} +10 -5
- package/{src/icqq-endpoint-commands.ts → commands/icqq/endpoint/definition.ts} +9 -5
- package/commands/{endpoint/list.js → icqq/endpoint/list/index.js} +1 -1
- package/commands/icqq/endpoint/list/index.ts +3 -0
- package/commands/{endpoint/remove/[id].js → icqq/endpoint/remove/[id]/index.js} +1 -1
- package/commands/icqq/endpoint/remove/[id]/index.ts +3 -0
- package/lib/content-resolver.d.ts +10 -0
- package/lib/content-resolver.js +98 -0
- package/lib/cq-message.d.ts +1 -1
- package/lib/cq-message.js +1 -1
- package/lib/endpoint.d.ts +1 -2
- package/lib/endpoint.js +34 -77
- package/lib/icqq-inbound.js +40 -0
- package/lib/icqq-runtime-state.js +1 -1
- package/lib/index.d.ts +1 -1
- package/lib/outbound-media.d.ts +1 -1
- package/lib/outbound-media.js +1 -1
- package/lib/protocol.d.ts +4 -22
- package/lib/protocol.js +52 -55
- package/lib/types.d.ts +0 -6
- package/package.json +29 -23
- package/plugin.js +1 -1
- package/prompt-sections/platform/index.js +16 -0
- package/{agent/prompt-sections/platform.ts → prompt-sections/platform/index.ts} +3 -4
- package/schema.json +7 -1
- package/src/content-resolver.ts +131 -0
- package/src/cq-message.ts +1 -1
- package/src/endpoint.ts +43 -97
- package/src/icqq-inbound.ts +39 -0
- package/src/icqq-runtime-state.ts +1 -1
- package/src/index.ts +1 -1
- package/src/outbound-media.ts +2 -2
- package/src/protocol.ts +56 -80
- package/src/types.ts +0 -7
- package/commands/endpoint/add/[[id]].ts +0 -3
- package/commands/endpoint/list.ts +0 -3
- package/commands/endpoint/remove/[id].ts +0 -3
- package/lib/icqq-endpoint-commands.d.ts +0 -1
- /package/{agent/PERMITS.md → PERMITS.md} +0 -0
package/src/endpoint.ts
CHANGED
|
@@ -5,29 +5,25 @@ import {
|
|
|
5
5
|
type FileElem,
|
|
6
6
|
type PttElem,
|
|
7
7
|
type Sendable,
|
|
8
|
+
type ShareElem,
|
|
8
9
|
type VideoElem,
|
|
9
10
|
} from '@icqqjs/icqq';
|
|
10
11
|
import { inspect } from 'node:util';
|
|
11
12
|
import { AsyncLocalStorage } from 'node:async_hooks';
|
|
12
|
-
import {
|
|
13
|
-
|
|
14
|
-
EndpointControl,
|
|
15
|
-
EndpointManagement,
|
|
16
|
-
EndpointPendingRequest,
|
|
17
|
-
EndpointSendRequest,
|
|
13
|
+
import {
|
|
14
|
+
Endpoint,
|
|
15
|
+
type EndpointControl,
|
|
16
|
+
type EndpointManagement,
|
|
17
|
+
type EndpointPendingRequest,
|
|
18
|
+
type EndpointSendRequest,
|
|
18
19
|
} from 'zhin.js/adapter';
|
|
19
|
-
import type { EndpointContentPort
|
|
20
|
+
import type { EndpointContentPort } from '@zhin.js/adapter';
|
|
20
21
|
import { receiveOneBotLikeSideEvent, SystemEvent, toCanonicalSegments, type LoginAssist } from '@zhin.js/core';
|
|
21
|
-
import type {
|
|
22
|
-
ConversationMessage,
|
|
23
|
-
ConversationReference,
|
|
24
|
-
ForwardEntry,
|
|
25
|
-
MessageRef,
|
|
26
|
-
Segment,
|
|
27
|
-
} from '@zhin.js/im-contract';
|
|
22
|
+
import type { MessageRef } from '@zhin.js/im-contract';
|
|
28
23
|
import { formatCompact, getAdapterLogger, truncatePreview } from '@zhin.js/logger';
|
|
29
24
|
import type { CapabilityId } from 'zhin.js';
|
|
30
25
|
import { runIcqqLoginAssistStep } from './icqq-login-assist.js';
|
|
26
|
+
import { IcqqContentResolver } from './content-resolver.js';
|
|
31
27
|
import {
|
|
32
28
|
InboundMessageDeduper,
|
|
33
29
|
findIcqqNestedMessageSource,
|
|
@@ -61,7 +57,6 @@ import {
|
|
|
61
57
|
type ResolvedIcqqConfig,
|
|
62
58
|
} from './protocol.js';
|
|
63
59
|
import type { SystemMessage as IcqqSystemMessage } from './types.js';
|
|
64
|
-
import { normalizeForwardMsgResponse } from './forward-msg.js';
|
|
65
60
|
|
|
66
61
|
export interface IcqqEndpointOptions {
|
|
67
62
|
readonly id: CapabilityId;
|
|
@@ -115,87 +110,8 @@ export class IcqqEndpoint extends Endpoint<Client> {
|
|
|
115
110
|
#heldInbound: IcqqInboundMessage[] = [];
|
|
116
111
|
readonly #inflightInbound = new Set<Promise<void>>();
|
|
117
112
|
readonly #inboundOwner = new AsyncLocalStorage<symbol>();
|
|
118
|
-
readonly #
|
|
119
|
-
|
|
120
|
-
readonly content: EndpointContentPort = Object.freeze({
|
|
121
|
-
resolve: async (reference: ConversationReference, context: EndpointContentResolveContext) => {
|
|
122
|
-
context.signal.throwIfAborted();
|
|
123
|
-
if (reference.kind === 'message') {
|
|
124
|
-
const cached = this.#messageContent.get(reference.message.id);
|
|
125
|
-
return cached
|
|
126
|
-
? Object.freeze({ status: 'resolved' as const, reference, value: cached })
|
|
127
|
-
: Object.freeze({ status: 'not_found' as const, code: 'icqq_message_not_observed' });
|
|
128
|
-
}
|
|
129
|
-
if (reference.kind === 'forward') {
|
|
130
|
-
try {
|
|
131
|
-
const entries = await this.#resolveForwardEntries(
|
|
132
|
-
reference.forwardId,
|
|
133
|
-
context,
|
|
134
|
-
{ remainingEntries: context.maxEntries, path: new Set<string>() },
|
|
135
|
-
0,
|
|
136
|
-
);
|
|
137
|
-
if (entries.length === 0) return Object.freeze({ status: 'not_found' as const, code: 'icqq_forward_not_found' });
|
|
138
|
-
return Object.freeze({ status: 'resolved' as const, reference, value: Object.freeze(entries) });
|
|
139
|
-
} catch (error) {
|
|
140
|
-
if (context.signal.aborted) throw error;
|
|
141
|
-
return Object.freeze({
|
|
142
|
-
status: 'failed' as const,
|
|
143
|
-
code: 'icqq_forward_fetch_failed',
|
|
144
|
-
message: error instanceof Error ? error.message : String(error),
|
|
145
|
-
});
|
|
146
|
-
}
|
|
147
|
-
}
|
|
148
|
-
if (reference.media.kind === 'url' || reference.media.kind === 'base64' || reference.media.kind === 'path') {
|
|
149
|
-
return Object.freeze({ status: 'resolved' as const, reference, value: reference.media });
|
|
150
|
-
}
|
|
151
|
-
return Object.freeze({ status: 'unsupported' as const, code: 'icqq_media_reference_unsupported' });
|
|
152
|
-
},
|
|
153
|
-
});
|
|
154
|
-
|
|
155
|
-
async #resolveForwardEntries(
|
|
156
|
-
forwardId: string,
|
|
157
|
-
context: EndpointContentResolveContext,
|
|
158
|
-
state: { remainingEntries: number; readonly path: Set<string> },
|
|
159
|
-
depth: number,
|
|
160
|
-
): Promise<readonly ForwardEntry[]> {
|
|
161
|
-
context.signal.throwIfAborted();
|
|
162
|
-
if (state.remainingEntries <= 0 || state.path.has(forwardId)) return Object.freeze([]);
|
|
163
|
-
state.path.add(forwardId);
|
|
164
|
-
try {
|
|
165
|
-
// ICQQ does not expose a cancellable getForwardMsg API. Keep the caller's
|
|
166
|
-
// generation lease until the native operation actually settles, then honor
|
|
167
|
-
// cancellation before publishing any resolved content.
|
|
168
|
-
const raw = await this.client.getForwardMsg(forwardId);
|
|
169
|
-
context.signal.throwIfAborted();
|
|
170
|
-
const entries = normalizeForwardMsgResponse(raw).slice(0, state.remainingEntries);
|
|
171
|
-
state.remainingEntries -= entries.length;
|
|
172
|
-
const expanded: ForwardEntry[] = [];
|
|
173
|
-
for (const entry of entries) {
|
|
174
|
-
context.signal.throwIfAborted();
|
|
175
|
-
const segments: Segment[] = [];
|
|
176
|
-
for (const segment of entry.segments) {
|
|
177
|
-
if (segment.type !== 'forward' || depth >= context.maxDepth) {
|
|
178
|
-
segments.push(segment);
|
|
179
|
-
continue;
|
|
180
|
-
}
|
|
181
|
-
const nestedId = String((segment.data as { forward_id?: unknown }).forward_id ?? '').trim();
|
|
182
|
-
if (!nestedId || state.path.has(nestedId) || state.remainingEntries <= 0) {
|
|
183
|
-
segments.push(segment);
|
|
184
|
-
continue;
|
|
185
|
-
}
|
|
186
|
-
const nested = await this.#resolveForwardEntries(nestedId, context, state, depth + 1);
|
|
187
|
-
segments.push(Object.freeze({
|
|
188
|
-
...segment,
|
|
189
|
-
data: Object.freeze({ ...segment.data, ...(nested.length > 0 ? { entries: nested } : {}) }),
|
|
190
|
-
}));
|
|
191
|
-
}
|
|
192
|
-
expanded.push(Object.freeze({ ...entry, segments: Object.freeze(segments) }));
|
|
193
|
-
}
|
|
194
|
-
return Object.freeze(expanded);
|
|
195
|
-
} finally {
|
|
196
|
-
state.path.delete(forwardId);
|
|
197
|
-
}
|
|
198
|
-
}
|
|
113
|
+
readonly #contentResolver: IcqqContentResolver;
|
|
114
|
+
readonly content: EndpointContentPort;
|
|
199
115
|
|
|
200
116
|
readonly management: EndpointManagement = Object.freeze<EndpointManagement>({
|
|
201
117
|
listFriends: async () =>
|
|
@@ -275,6 +191,8 @@ export class IcqqEndpoint extends Endpoint<Client> {
|
|
|
275
191
|
super();
|
|
276
192
|
const nativeConfig = resolveNativeClientConfig(options.config);
|
|
277
193
|
this.client = new ManagedIcqqClient(Number(options.config.id), nativeConfig);
|
|
194
|
+
this.#contentResolver = new IcqqContentResolver(this.client);
|
|
195
|
+
this.content = this.#contentResolver.port;
|
|
278
196
|
this.#logger = getAdapterLogger('icqq', options.config.id);
|
|
279
197
|
this.#options = options;
|
|
280
198
|
this.endpointName = options.config.id;
|
|
@@ -436,6 +354,27 @@ export class IcqqEndpoint extends Endpoint<Client> {
|
|
|
436
354
|
target: ReturnType<typeof icqqOutboundTarget>,
|
|
437
355
|
message: Sendable,
|
|
438
356
|
): Promise<{ message_id?: unknown }> {
|
|
357
|
+
if (isIcqqShareElement(message)) {
|
|
358
|
+
switch (target.kind) {
|
|
359
|
+
case 'private':
|
|
360
|
+
await this.client.pickFriend(target.userId).share(message);
|
|
361
|
+
return {};
|
|
362
|
+
case 'group':
|
|
363
|
+
await this.client.pickGroup(target.groupId).share(message);
|
|
364
|
+
return {};
|
|
365
|
+
case 'temp':
|
|
366
|
+
await this.client.pickMember(target.groupId, target.userId).share(message);
|
|
367
|
+
return {};
|
|
368
|
+
case 'channel': {
|
|
369
|
+
const channel = this.client.pickGuild(target.guildId).channels.get(target.channelId);
|
|
370
|
+
if (!channel) {
|
|
371
|
+
throw new TypeError(`ICQQ guild channel is unavailable: ${target.guildId}/${target.channelId}`);
|
|
372
|
+
}
|
|
373
|
+
await channel.share(message);
|
|
374
|
+
return {};
|
|
375
|
+
}
|
|
376
|
+
}
|
|
377
|
+
}
|
|
439
378
|
if (isIcqqFileElement(message)) {
|
|
440
379
|
switch (target.kind) {
|
|
441
380
|
case 'private': {
|
|
@@ -800,7 +739,7 @@ export class IcqqEndpoint extends Endpoint<Client> {
|
|
|
800
739
|
const quoteId = resolveIcqqQuoteIdFromEvent(data);
|
|
801
740
|
const quoted = messageLookupFromIcqqSource(findIcqqNestedMessageSource(data));
|
|
802
741
|
if (quoteId && quoted) {
|
|
803
|
-
this.#
|
|
742
|
+
this.#contentResolver.remember(quoteId, Object.freeze({
|
|
804
743
|
ref: Object.freeze({ conversation, id: quoteId }),
|
|
805
744
|
...(quoted.sender?.id ? { actor: Object.freeze({
|
|
806
745
|
id: quoted.sender.id,
|
|
@@ -1014,6 +953,13 @@ export class IcqqEndpoint extends Endpoint<Client> {
|
|
|
1014
953
|
}
|
|
1015
954
|
}
|
|
1016
955
|
|
|
956
|
+
function isIcqqShareElement(message: Sendable): message is ShareElem {
|
|
957
|
+
return !Array.isArray(message)
|
|
958
|
+
&& typeof message === 'object'
|
|
959
|
+
&& message !== null
|
|
960
|
+
&& message.type === 'share';
|
|
961
|
+
}
|
|
962
|
+
|
|
1017
963
|
function formatIcqqSendablePreview(message: Sendable): string {
|
|
1018
964
|
if (typeof message === 'string') return message;
|
|
1019
965
|
try {
|
package/src/icqq-inbound.ts
CHANGED
|
@@ -500,6 +500,45 @@ export function icqqElementsToSegments(
|
|
|
500
500
|
});
|
|
501
501
|
break;
|
|
502
502
|
}
|
|
503
|
+
case "share": {
|
|
504
|
+
const url = typeof el.url === "string" ? el.url.trim() : "";
|
|
505
|
+
const title = typeof el.title === "string" ? el.title.trim() : "";
|
|
506
|
+
if (!url || !title) break;
|
|
507
|
+
const description = typeof el.summary === "string" && el.summary
|
|
508
|
+
? el.summary
|
|
509
|
+
: undefined;
|
|
510
|
+
const image = typeof el.image === "string" && el.image ? el.image : undefined;
|
|
511
|
+
const audio = typeof el.audio === "string" && el.audio ? el.audio : undefined;
|
|
512
|
+
const content = typeof el.content === "string" && el.content ? el.content : undefined;
|
|
513
|
+
const rawConfig = el.config && typeof el.config === "object" && !Array.isArray(el.config)
|
|
514
|
+
? el.config as Record<string, unknown>
|
|
515
|
+
: undefined;
|
|
516
|
+
const appid = Number(rawConfig?.appid);
|
|
517
|
+
const config = Number.isSafeInteger(appid) && appid > 0
|
|
518
|
+
? {
|
|
519
|
+
appid,
|
|
520
|
+
...(typeof rawConfig?.appname === "string" && rawConfig.appname
|
|
521
|
+
? { package: rawConfig.appname }
|
|
522
|
+
: {}),
|
|
523
|
+
...(typeof rawConfig?.appsign === "string" && rawConfig.appsign
|
|
524
|
+
? { sign: rawConfig.appsign }
|
|
525
|
+
: {}),
|
|
526
|
+
}
|
|
527
|
+
: undefined;
|
|
528
|
+
out.push({
|
|
529
|
+
type: "share",
|
|
530
|
+
data: {
|
|
531
|
+
url,
|
|
532
|
+
title,
|
|
533
|
+
...(description ? { description } : {}),
|
|
534
|
+
...(image ? { image } : {}),
|
|
535
|
+
...(audio ? { audio } : {}),
|
|
536
|
+
...(content ? { content } : {}),
|
|
537
|
+
...(config ? { config } : {}),
|
|
538
|
+
},
|
|
539
|
+
});
|
|
540
|
+
break;
|
|
541
|
+
}
|
|
503
542
|
default:
|
|
504
543
|
if (el.text != null && el.text !== "") {
|
|
505
544
|
out.push({ type: "text", data: { text: String(el.text) } });
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* ICQQ 插件实例的运行时状态:adapter create() 注册的 endpoint 列表。
|
|
3
|
-
* 由 plugin.ts setup() provide,adapter create 与 `icqq
|
|
3
|
+
* 由 plugin.ts setup() provide,adapter create 与 `icqq endpoint` 命令共享(同一 owner generation)。
|
|
4
4
|
*/
|
|
5
5
|
import { defineEndpointRuntimeStateToken } from 'zhin.js/adapter';
|
|
6
6
|
|
package/src/index.ts
CHANGED
package/src/outbound-media.ts
CHANGED
|
@@ -2,9 +2,9 @@ import * as fs from "node:fs";
|
|
|
2
2
|
import * as os from "node:os";
|
|
3
3
|
import * as path from "node:path";
|
|
4
4
|
import * as crypto from "node:crypto";
|
|
5
|
-
import { isMediaRef } from
|
|
5
|
+
import { isMediaRef } from '@zhin.js/im-contract';
|
|
6
6
|
import type { MessageSegment, SendContent } from "zhin.js";
|
|
7
|
-
import type { IcqqEndpointConfig } from "./
|
|
7
|
+
import type { IcqqEndpointConfig } from "./protocol.js";
|
|
8
8
|
|
|
9
9
|
export type IcqqOutboundMediaMode = "file" | "base64";
|
|
10
10
|
|
package/src/protocol.ts
CHANGED
|
@@ -1,5 +1,4 @@
|
|
|
1
|
-
import {
|
|
2
|
-
import type { MessageElem, Sendable } from '@icqqjs/icqq';
|
|
1
|
+
import type { MessageElem, Sendable, ShareElem } from '@icqqjs/icqq';
|
|
3
2
|
import type { ConversationRef } from '@zhin.js/im-contract';
|
|
4
3
|
import type { MessageSegment } from "zhin.js";
|
|
5
4
|
import { resolveIcqqMediaFile } from "./cq-message.js";
|
|
@@ -231,9 +230,9 @@ export const Actions = {
|
|
|
231
230
|
GET_NOTIFY: "get_notify",
|
|
232
231
|
} as const;
|
|
233
232
|
|
|
234
|
-
/**
|
|
235
|
-
export interface
|
|
236
|
-
readonly id
|
|
233
|
+
/** One expanded ICQQ endpoint configuration produced by AdapterIndex. */
|
|
234
|
+
export interface IcqqEndpointConfig {
|
|
235
|
+
readonly id: string;
|
|
237
236
|
readonly autoReconnect?: boolean;
|
|
238
237
|
readonly outboundMedia?: 'file' | 'base64';
|
|
239
238
|
readonly password?: string;
|
|
@@ -247,24 +246,6 @@ export interface IcqqAdapterConfig {
|
|
|
247
246
|
readonly autoServer?: boolean;
|
|
248
247
|
readonly qqnt?: boolean;
|
|
249
248
|
readonly ntLogin?: boolean;
|
|
250
|
-
/** Transitional: legacy root `endpoints[]` with `context: icqq`. */
|
|
251
|
-
readonly endpoints?: ReadonlyArray<{
|
|
252
|
-
readonly context?: string;
|
|
253
|
-
readonly id?: string;
|
|
254
|
-
readonly autoReconnect?: boolean;
|
|
255
|
-
readonly outboundMedia?: 'file' | 'base64';
|
|
256
|
-
readonly password?: string;
|
|
257
|
-
readonly platform?: number;
|
|
258
|
-
readonly ver?: string;
|
|
259
|
-
readonly dataDir?: string;
|
|
260
|
-
readonly signApiAddr?: string;
|
|
261
|
-
readonly ignoreSelf?: boolean;
|
|
262
|
-
readonly resend?: boolean;
|
|
263
|
-
readonly cacheGroupMember?: boolean;
|
|
264
|
-
readonly autoServer?: boolean;
|
|
265
|
-
readonly qqnt?: boolean;
|
|
266
|
-
readonly ntLogin?: boolean;
|
|
267
|
-
}>;
|
|
268
249
|
}
|
|
269
250
|
|
|
270
251
|
export interface ResolvedIcqqConfig {
|
|
@@ -311,27 +292,24 @@ export type ParsedIcqqSendTarget =
|
|
|
311
292
|
| { readonly kind: 'temp'; readonly groupId: number; readonly userId: number }
|
|
312
293
|
| { readonly kind: 'channel'; readonly guildId: string; readonly channelId: string };
|
|
313
294
|
|
|
314
|
-
export function resolveIcqqConfig(config:
|
|
315
|
-
const
|
|
316
|
-
const id = pickCredential(config.id, entry?.id, process.env.ICQQ_ACCOUNT);
|
|
295
|
+
export function resolveIcqqConfig(config: IcqqEndpointConfig): ResolvedIcqqConfig {
|
|
296
|
+
const id = typeof config.id === 'string' ? config.id.trim() : '';
|
|
317
297
|
if (!/^\d+$/.test(id)) {
|
|
318
|
-
throw new TypeError(
|
|
319
|
-
'ICQQ adapter requires numeric id (QQ uin) via plugins.<key>.id or ICQQ_ACCOUNT',
|
|
320
|
-
);
|
|
298
|
+
throw new TypeError('ICQQ endpoint requires a numeric id (QQ uin)');
|
|
321
299
|
}
|
|
322
|
-
const autoReconnect = config.autoReconnect ??
|
|
323
|
-
const outboundMedia = config.outboundMedia
|
|
324
|
-
const password = config.password
|
|
325
|
-
const platform = config.platform ??
|
|
326
|
-
const ver = config.ver ??
|
|
327
|
-
const dataDir = config.dataDir ??
|
|
328
|
-
const signApiAddr = config.signApiAddr
|
|
329
|
-
const ignoreSelf = config.ignoreSelf ??
|
|
330
|
-
const resend = config.resend
|
|
331
|
-
const cacheGroupMember = config.cacheGroupMember ??
|
|
332
|
-
const autoServer = config.autoServer ??
|
|
333
|
-
const qqnt = config.qqnt
|
|
334
|
-
const ntLogin = config.ntLogin
|
|
300
|
+
const autoReconnect = config.autoReconnect ?? true;
|
|
301
|
+
const outboundMedia = config.outboundMedia;
|
|
302
|
+
const password = config.password;
|
|
303
|
+
const platform = config.platform ?? 2;
|
|
304
|
+
const ver = config.ver ?? '9.1.70';
|
|
305
|
+
const dataDir = config.dataDir ?? `data/icqq/${id}`;
|
|
306
|
+
const signApiAddr = config.signApiAddr;
|
|
307
|
+
const ignoreSelf = config.ignoreSelf ?? true;
|
|
308
|
+
const resend = config.resend;
|
|
309
|
+
const cacheGroupMember = config.cacheGroupMember ?? true;
|
|
310
|
+
const autoServer = config.autoServer ?? true;
|
|
311
|
+
const qqnt = config.qqnt;
|
|
312
|
+
const ntLogin = config.ntLogin;
|
|
335
313
|
return {
|
|
336
314
|
context: 'icqq',
|
|
337
315
|
id,
|
|
@@ -411,6 +389,7 @@ const ICQQ_STANDALONE_ELEMENTS = new Set([
|
|
|
411
389
|
'video',
|
|
412
390
|
'file',
|
|
413
391
|
'json',
|
|
392
|
+
'share',
|
|
414
393
|
'long_msg',
|
|
415
394
|
] as const);
|
|
416
395
|
|
|
@@ -497,7 +476,7 @@ export function formatOutboundBody(payload: unknown): Sendable {
|
|
|
497
476
|
return { type: 'long_msg', resid };
|
|
498
477
|
}
|
|
499
478
|
case 'share':
|
|
500
|
-
return
|
|
479
|
+
return formatShare(item.data);
|
|
501
480
|
default: {
|
|
502
481
|
const text = item.data?.text;
|
|
503
482
|
if (typeof text === 'string' && text) return text;
|
|
@@ -528,48 +507,45 @@ function isWireSegment(value: unknown): value is IcqqWireSegment {
|
|
|
528
507
|
&& typeof (value as { type?: unknown }).type === 'string';
|
|
529
508
|
}
|
|
530
509
|
|
|
531
|
-
function
|
|
510
|
+
function formatShare(data: Record<string, unknown> | undefined): ShareElem {
|
|
532
511
|
if (!data) throw new TypeError('ICQQ share segment requires data');
|
|
533
512
|
const config = data.config as {
|
|
534
513
|
appid?: number;
|
|
535
514
|
package?: string;
|
|
536
|
-
icon?: string;
|
|
537
515
|
sign?: string;
|
|
538
516
|
} | undefined;
|
|
539
|
-
const title = String(data.title ?? '');
|
|
540
|
-
const
|
|
541
|
-
|
|
542
|
-
|
|
543
|
-
|
|
544
|
-
const
|
|
545
|
-
|
|
546
|
-
|
|
547
|
-
|
|
548
|
-
|
|
549
|
-
const
|
|
550
|
-
|
|
551
|
-
|
|
552
|
-
|
|
553
|
-
|
|
554
|
-
|
|
555
|
-
|
|
556
|
-
|
|
557
|
-
|
|
558
|
-
|
|
559
|
-
|
|
560
|
-
|
|
561
|
-
|
|
562
|
-
|
|
563
|
-
|
|
564
|
-
|
|
565
|
-
|
|
566
|
-
|
|
567
|
-
|
|
568
|
-
|
|
569
|
-
|
|
570
|
-
|
|
571
|
-
|
|
572
|
-
},
|
|
517
|
+
const title = String(data.title ?? '').trim();
|
|
518
|
+
const url = String(data.url ?? '').trim();
|
|
519
|
+
if (!title) throw new TypeError('ICQQ share segment requires title');
|
|
520
|
+
if (!url) throw new TypeError('ICQQ share segment requires url');
|
|
521
|
+
|
|
522
|
+
const optionalString = (value: unknown): string | undefined => {
|
|
523
|
+
const text = String(value ?? '').trim();
|
|
524
|
+
return text || undefined;
|
|
525
|
+
};
|
|
526
|
+
const appid = Number(config?.appid);
|
|
527
|
+
const appname = optionalString(config?.package);
|
|
528
|
+
const appsign = optionalString(config?.sign);
|
|
529
|
+
const shareConfig = Number.isSafeInteger(appid) && appid > 0
|
|
530
|
+
? {
|
|
531
|
+
appid,
|
|
532
|
+
...(appname ? { appname } : {}),
|
|
533
|
+
...(appsign ? { appsign } : {}),
|
|
534
|
+
}
|
|
535
|
+
: undefined;
|
|
536
|
+
const summary = optionalString(data.description) ?? optionalString(data.artist);
|
|
537
|
+
const content = optionalString(data.content);
|
|
538
|
+
const image = optionalString(data.image);
|
|
539
|
+
const audio = optionalString(data.audio);
|
|
540
|
+
|
|
541
|
+
return {
|
|
542
|
+
type: 'share',
|
|
543
|
+
title,
|
|
544
|
+
url,
|
|
545
|
+
...(summary ? { summary } : {}),
|
|
546
|
+
...(content ? { content } : {}),
|
|
547
|
+
...(image ? { image } : {}),
|
|
548
|
+
...(audio ? { audio } : {}),
|
|
549
|
+
...(shareConfig ? { config: shareConfig } : {}),
|
|
573
550
|
};
|
|
574
|
-
return { type: 'json', data: json };
|
|
575
551
|
}
|
package/src/types.ts
CHANGED
|
@@ -11,13 +11,6 @@ export interface IcqqSenderInfo {
|
|
|
11
11
|
title?: string;
|
|
12
12
|
}
|
|
13
13
|
|
|
14
|
-
export interface IcqqEndpointConfig {
|
|
15
|
-
context: "icqq";
|
|
16
|
-
name: `${number}`;
|
|
17
|
-
autoReconnect?: boolean;
|
|
18
|
-
outboundMedia?: "file" | "base64";
|
|
19
|
-
}
|
|
20
|
-
|
|
21
14
|
export interface FriendInfo {
|
|
22
15
|
user_id: number;
|
|
23
16
|
nickname: string;
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
export declare const icqqEndpointCommands: import("@zhin.js/adapter").EndpointCommands<Readonly<import("@zhin.js/command").CommandDefinition<unknown, unknown, import("@zhin.js/command").CommandMessage, string | undefined>>>;
|
|
File without changes
|