@zhin.js/adapter-icqq 1.1.18 → 1.1.20
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 +44 -0
- package/README.md +12 -8
- package/adapters/icqq/index.js +4 -28
- package/adapters/icqq/index.ts +3 -34
- package/commands/icqq/endpoint/definition.js +3 -25
- package/commands/icqq/endpoint/definition.ts +3 -24
- package/commands/icqq/login/code/index.js +3 -3
- package/commands/icqq/login/code/index.ts +3 -3
- package/commands/icqq/login/confirm/index.js +3 -3
- package/commands/icqq/login/confirm/index.ts +3 -3
- package/commands/icqq/login/definition.js +2 -2
- package/commands/icqq/login/definition.ts +2 -2
- package/commands/icqq/login/qr/index.js +3 -3
- package/commands/icqq/login/qr/index.ts +3 -3
- package/commands/icqq/login/retry/index.js +3 -3
- package/commands/icqq/login/retry/index.ts +3 -3
- package/commands/icqq/login/status/index.js +3 -3
- package/commands/icqq/login/status/index.ts +3 -3
- package/lib/adapter.d.ts +13 -0
- package/lib/adapter.js +27 -0
- package/lib/client.d.ts +6 -9
- package/lib/client.js +1 -3
- package/lib/console-spec.js +11 -1
- package/lib/console.d.ts +1 -7
- package/lib/console.js +2 -5
- package/lib/endpoint.d.ts +6 -16
- package/lib/endpoint.js +195 -143
- package/lib/group-moderation.d.ts +6 -3
- package/lib/group-moderation.js +9 -8
- package/lib/group-scene.d.ts +3 -3
- package/lib/group-scene.js +8 -7
- package/lib/icqq-login-assist.d.ts +3 -1
- package/lib/icqq-login-assist.js +3 -1
- package/lib/icqq-login-control.d.ts +8 -2
- package/lib/icqq-login-control.js +22 -5
- package/lib/index.d.ts +2 -1
- package/lib/index.js +1 -1
- package/lib/protocol.d.ts +3 -0
- package/package.json +18 -18
- package/plugin.js +1 -8
- package/src/adapter.ts +29 -0
- package/src/client.ts +6 -11
- package/src/console-spec.ts +9 -0
- package/src/console.ts +2 -6
- package/src/endpoint.ts +205 -153
- package/src/group-moderation.ts +10 -10
- package/src/group-scene.ts +8 -8
- package/src/icqq-login-assist.ts +6 -2
- package/src/icqq-login-control.ts +20 -8
- package/src/index.ts +2 -1
- package/src/protocol.ts +3 -0
- package/lib/icqq-runtime-state.d.ts +0 -1
- package/lib/icqq-runtime-state.js +0 -6
- package/src/icqq-runtime-state.ts +0 -7
package/src/group-moderation.ts
CHANGED
|
@@ -1,17 +1,17 @@
|
|
|
1
1
|
import type { Client } from '@icqqjs/icqq';
|
|
2
|
-
import type
|
|
2
|
+
import { createEndpointOperation, type EndpointMemberInspectionRequest, type EndpointModerationRequest, type GroupModerationMember } from 'zhin.js/adapter';
|
|
3
3
|
|
|
4
4
|
/** Native role/join facts stay in the adapter; the moderation plugin owns policy. */
|
|
5
5
|
export async function inspectGroupMember(
|
|
6
|
-
client: Client, groupId
|
|
6
|
+
client: Client, { groupId, userId, signal }: EndpointMemberInspectionRequest,
|
|
7
7
|
): Promise<GroupModerationMember> {
|
|
8
|
-
signal
|
|
8
|
+
const operation = createEndpointOperation({ signal });
|
|
9
9
|
const group = numericId(groupId);
|
|
10
10
|
const user = numericId(userId);
|
|
11
|
-
const selfLookup = client.getGroupMemberInfo(group, client.uin, true);
|
|
11
|
+
const selfLookup = operation.run(() => client.getGroupMemberInfo(group, client.uin, true));
|
|
12
12
|
const [self, member] = await Promise.all([
|
|
13
13
|
selfLookup,
|
|
14
|
-
user === client.uin ? selfLookup : client.getGroupMemberInfo(group, user, true),
|
|
14
|
+
user === client.uin ? selfLookup : operation.run(() => client.getGroupMemberInfo(group, user, true)),
|
|
15
15
|
]);
|
|
16
16
|
signal.throwIfAborted();
|
|
17
17
|
const joinedAt = member.join_time * 1000;
|
|
@@ -29,10 +29,10 @@ export async function inspectGroupMember(
|
|
|
29
29
|
}
|
|
30
30
|
|
|
31
31
|
export async function moderateGroupMember(
|
|
32
|
-
client
|
|
33
|
-
|
|
32
|
+
{ client, isActive }: { readonly client: Client; readonly isActive: () => boolean },
|
|
33
|
+
{ groupId, userId, action, signal }: EndpointModerationRequest,
|
|
34
34
|
): Promise<void> {
|
|
35
|
-
const member = await inspectGroupMember(client, groupId, userId, signal);
|
|
35
|
+
const member = await inspectGroupMember(client, { groupId: groupId, userId: userId, signal: signal });
|
|
36
36
|
signal.throwIfAborted();
|
|
37
37
|
if (!isActive() || !member.canModerate) throw new Error('Group moderation permission is unavailable');
|
|
38
38
|
if (action.type === 'kick') {
|
|
@@ -45,8 +45,8 @@ export async function moderateGroupMember(
|
|
|
45
45
|
throw new Error('Invalid group mute duration');
|
|
46
46
|
}
|
|
47
47
|
const ok = action.type === 'kick'
|
|
48
|
-
? await client.setGroupKick(numericId(groupId), numericId(userId))
|
|
49
|
-
: await client.setGroupBan(numericId(groupId), numericId(userId), action.durationSeconds);
|
|
48
|
+
? await createEndpointOperation({ signal }).run(() => client.setGroupKick(numericId(groupId), numericId(userId)))
|
|
49
|
+
: await createEndpointOperation({ signal }).run(() => client.setGroupBan(numericId(groupId), numericId(userId), action.durationSeconds));
|
|
50
50
|
if (!ok) throw new Error('Platform did not confirm the moderation action');
|
|
51
51
|
}
|
|
52
52
|
|
package/src/group-scene.ts
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import type { Client } from '@icqqjs/icqq';
|
|
2
|
-
import type
|
|
2
|
+
import { createEndpointOperation, type EndpointGroupInspectionRequest, type EndpointMemberInspectionRequest, type GroupMemberProfile, type GroupSceneFacts } from 'zhin.js/adapter';
|
|
3
3
|
|
|
4
4
|
/** Listing a very large roster only to find admins is not worth the packet; adminIds stay unknown. */
|
|
5
5
|
export const ADMIN_SCAN_MAX_MEMBERS = 1000;
|
|
@@ -19,10 +19,10 @@ function numericId(value: string): number {
|
|
|
19
19
|
* ADR 0041 scene facts: one uncached group-info read, plus (small groups only) one roster read for
|
|
20
20
|
* admins. A roster failure leaves adminIds unknown instead of failing the other facts.
|
|
21
21
|
*/
|
|
22
|
-
export async function inspectGroupScene(client: Client, groupId
|
|
23
|
-
signal
|
|
22
|
+
export async function inspectGroupScene(client: Client, { groupId, signal }: EndpointGroupInspectionRequest): Promise<GroupSceneFacts> {
|
|
23
|
+
const operation = createEndpointOperation({ signal });
|
|
24
24
|
const group = numericId(groupId);
|
|
25
|
-
const info = await client.getGroupInfo(group, true);
|
|
25
|
+
const info = await operation.run(() => client.getGroupInfo(group, true));
|
|
26
26
|
signal.throwIfAborted();
|
|
27
27
|
if (info.group_id !== group) return {};
|
|
28
28
|
const memberCount = Number.isSafeInteger(info.member_count) && info.member_count > 0 ? info.member_count : undefined;
|
|
@@ -30,7 +30,7 @@ export async function inspectGroupScene(client: Client, groupId: string, signal:
|
|
|
30
30
|
let adminIds: string[] | undefined;
|
|
31
31
|
if (memberCount === undefined || memberCount <= ADMIN_SCAN_MAX_MEMBERS) {
|
|
32
32
|
try {
|
|
33
|
-
const roster = await client.getGroupMemberList(group);
|
|
33
|
+
const roster = await operation.run(() => client.getGroupMemberList(group));
|
|
34
34
|
signal.throwIfAborted();
|
|
35
35
|
adminIds = [...roster.values()]
|
|
36
36
|
.filter(member => member.role === 'admin' && member.group_id === group)
|
|
@@ -55,12 +55,12 @@ export async function inspectGroupScene(client: Client, groupId: string, signal:
|
|
|
55
55
|
|
|
56
56
|
/** ADR 0041 member facts from one uncached lookup (same rationale as the agent `freshMember` helper). */
|
|
57
57
|
export async function inspectGroupMemberProfile(
|
|
58
|
-
client: Client, groupId
|
|
58
|
+
client: Client, { groupId, userId, signal }: EndpointMemberInspectionRequest,
|
|
59
59
|
): Promise<GroupMemberProfile> {
|
|
60
|
-
signal
|
|
60
|
+
const operation = createEndpointOperation({ signal });
|
|
61
61
|
const group = numericId(groupId);
|
|
62
62
|
const user = numericId(userId);
|
|
63
|
-
const member = await client.getGroupMemberInfo(group, user, true);
|
|
63
|
+
const member = await operation.run(() => client.getGroupMemberInfo(group, user, true));
|
|
64
64
|
signal.throwIfAborted();
|
|
65
65
|
if (member.user_id !== user || member.group_id !== group) return {};
|
|
66
66
|
const card = typeof member.card === 'string' && member.card.trim() ? member.card.trim() : undefined;
|
package/src/icqq-login-assist.ts
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import type { Locale } from 'zhin.js/i18n';
|
|
2
2
|
import { messages as t } from './i18n.js';
|
|
3
|
-
import type { LoginAssist, LoginAssistType, PendingLoginTaskPayload } from '@zhin.js/core';
|
|
3
|
+
import type { LoginAssist, LoginAssistEndpointIdentity, LoginAssistType, PendingLoginTaskPayload } from '@zhin.js/core';
|
|
4
4
|
import { formatCompact } from '@zhin.js/logger';
|
|
5
5
|
|
|
6
6
|
type IcqqLoginClient = {
|
|
@@ -26,6 +26,8 @@ export async function runIcqqLoginAssistStep(input: {
|
|
|
26
26
|
adapter: string;
|
|
27
27
|
endpointKey: string;
|
|
28
28
|
owner: object;
|
|
29
|
+
signal?: AbortSignal;
|
|
30
|
+
endpointIdentity?: LoginAssistEndpointIdentity;
|
|
29
31
|
logger: Logger;
|
|
30
32
|
step: LoginAssistType;
|
|
31
33
|
event: unknown;
|
|
@@ -44,6 +46,7 @@ export async function runIcqqLoginAssistStep(input: {
|
|
|
44
46
|
}));
|
|
45
47
|
|
|
46
48
|
try {
|
|
49
|
+
input.signal?.throwIfAborted();
|
|
47
50
|
if (step === 'device') {
|
|
48
51
|
try {
|
|
49
52
|
await client.sendSmsCode();
|
|
@@ -56,7 +59,8 @@ export async function runIcqqLoginAssistStep(input: {
|
|
|
56
59
|
}
|
|
57
60
|
}
|
|
58
61
|
|
|
59
|
-
const value = await assist.waitForInput(adapter, endpointKey, step, payload, { owner });
|
|
62
|
+
const value = await assist.waitForInput(adapter, endpointKey, step, payload, { owner, signal: input.signal, endpointIdentity: input.endpointIdentity });
|
|
63
|
+
input.signal?.throwIfAborted();
|
|
60
64
|
const text = normalizeSubmitValue(value);
|
|
61
65
|
|
|
62
66
|
switch (step) {
|
|
@@ -1,9 +1,11 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* ICQQ login control surface exposed to the Plugin Runtime, Console and the
|
|
3
|
-
* recovery command.
|
|
4
|
-
*
|
|
3
|
+
* recovery command. Controls are projected from actual current incarnations in
|
|
4
|
+
* the owning IcqqAdapter; no plugin resource keeps a second Endpoint directory.
|
|
5
5
|
*/
|
|
6
|
-
import {
|
|
6
|
+
import { adapterFeatureId, isAdapterIndex } from 'zhin.js/adapter';
|
|
7
|
+
import type { FeatureId, PluginId } from 'zhin.js';
|
|
8
|
+
import { IcqqAdapter } from './adapter.js';
|
|
7
9
|
import type { PendingLoginTask } from '@zhin.js/core';
|
|
8
10
|
|
|
9
11
|
export interface IcqqLoginControlState {
|
|
@@ -23,8 +25,18 @@ export interface IcqqLoginControl {
|
|
|
23
25
|
cancel(taskId: string, reason?: string): boolean;
|
|
24
26
|
}
|
|
25
27
|
|
|
26
|
-
/**
|
|
27
|
-
export
|
|
28
|
-
|
|
29
|
-
'ICQQ
|
|
30
|
-
|
|
28
|
+
/** Read the owning Adapter's actual incarnations; this projection retains no independent directory. */
|
|
29
|
+
export function icqqLoginControls(context: Readonly<{ owner: PluginId | Readonly<{ id: PluginId }>; project<T>(id: FeatureId): T }>): ReadonlyMap<string, IcqqLoginControl> {
|
|
30
|
+
const index = context.project<unknown>(adapterFeatureId);
|
|
31
|
+
if (!isAdapterIndex(index)) throw new Error('ICQQ Adapter projection is unavailable');
|
|
32
|
+
const owner = typeof context.owner === 'string' ? context.owner : context.owner.id;
|
|
33
|
+
const controls = new Map<string, IcqqLoginControl>();
|
|
34
|
+
for (const adapter of index.forOwner(owner).adapters()) {
|
|
35
|
+
if (!(adapter instanceof IcqqAdapter)) continue;
|
|
36
|
+
for (const row of adapter.list()) {
|
|
37
|
+
const endpoint = adapter.getEndpoint(row.instanceId);
|
|
38
|
+
if (endpoint) controls.set(row.endpointId, endpoint.loginControl);
|
|
39
|
+
}
|
|
40
|
+
}
|
|
41
|
+
return controls;
|
|
42
|
+
}
|
package/src/index.ts
CHANGED
|
@@ -23,7 +23,6 @@ export {
|
|
|
23
23
|
export * from './types.js';
|
|
24
24
|
|
|
25
25
|
export {
|
|
26
|
-
icqqClient,
|
|
27
26
|
type IcqqClient,
|
|
28
27
|
type IcqqClientEventMap,
|
|
29
28
|
} from './client.js';
|
|
@@ -31,3 +30,5 @@ export {
|
|
|
31
30
|
export {
|
|
32
31
|
resolveIcqqInboundMessageId,
|
|
33
32
|
} from './icqq-inbound.js';
|
|
33
|
+
|
|
34
|
+
export { IcqqAdapter } from './adapter.js';
|
package/src/protocol.ts
CHANGED
|
@@ -277,6 +277,9 @@ export interface IcqqWireSegment {
|
|
|
277
277
|
}
|
|
278
278
|
|
|
279
279
|
export interface IcqqInboundMessage {
|
|
280
|
+
/** Exact SDK callback object, operation-local and never a persisted projection. */
|
|
281
|
+
readonly event?: unknown;
|
|
282
|
+
readonly platformSentAt?: number;
|
|
280
283
|
readonly id: string;
|
|
281
284
|
readonly conversation: ConversationRef;
|
|
282
285
|
readonly content: string;
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
export declare const icqqRuntimeStateToken: import("zhin.js").Token<import("@zhin.js/adapter").EndpointRuntimeState>;
|
|
@@ -1,6 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* ICQQ 插件实例的运行时状态:adapter create() 注册的 endpoint 列表。
|
|
3
|
-
* 由 plugin.ts setup() provide,adapter create 与 `icqq endpoint` 命令共享(同一 owner generation)。
|
|
4
|
-
*/
|
|
5
|
-
import { defineEndpointRuntimeStateToken } from 'zhin.js/adapter';
|
|
6
|
-
export const icqqRuntimeStateToken = defineEndpointRuntimeStateToken('icqq');
|
|
@@ -1,7 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* ICQQ 插件实例的运行时状态:adapter create() 注册的 endpoint 列表。
|
|
3
|
-
* 由 plugin.ts setup() provide,adapter create 与 `icqq endpoint` 命令共享(同一 owner generation)。
|
|
4
|
-
*/
|
|
5
|
-
import { defineEndpointRuntimeStateToken } from 'zhin.js/adapter';
|
|
6
|
-
|
|
7
|
-
export const icqqRuntimeStateToken = defineEndpointRuntimeStateToken('icqq');
|