@zhin.js/adapter-icqq 1.1.28 → 1.1.30
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 +70 -0
- package/README.md +59 -9
- package/adapters/icqq/index.js +3 -0
- package/adapters/icqq/index.ts +3 -0
- package/commands/icqq/login/code/index.js +41 -16
- package/commands/icqq/login/code/index.ts +37 -13
- package/commands/icqq/login/confirm/index.js +15 -9
- package/commands/icqq/login/confirm/index.ts +15 -6
- package/commands/icqq/login/definition.js +57 -3
- package/commands/icqq/login/definition.ts +62 -3
- package/commands/icqq/login/qr/index.js +14 -16
- package/commands/icqq/login/qr/index.ts +17 -15
- package/commands/icqq/login/status/index.js +32 -13
- package/commands/icqq/login/status/index.ts +30 -12
- package/lib/endpoint.d.ts +14 -1
- package/lib/endpoint.js +178 -41
- package/lib/i18n.d.ts +1 -1
- package/lib/i18n.js +283 -15
- package/lib/icqq-login-assist.d.ts +113 -20
- package/lib/icqq-login-assist.js +468 -97
- package/lib/icqq-login-control.d.ts +4 -1
- package/lib/icqq-login-descriptors.d.ts +48 -0
- package/lib/icqq-login-descriptors.js +187 -0
- package/lib/icqq-login-errors.d.ts +42 -0
- package/lib/icqq-login-errors.js +77 -0
- package/lib/sdk-logger.d.ts +14 -0
- package/lib/sdk-logger.js +44 -0
- package/package.json +45 -38
- package/plugin.js +0 -3
- package/src/endpoint.ts +176 -48
- package/src/i18n.ts +283 -15
- package/src/icqq-login-assist.ts +533 -114
- package/src/icqq-login-control.ts +4 -1
- package/src/icqq-login-descriptors.ts +200 -0
- package/src/icqq-login-errors.ts +112 -0
- package/src/sdk-logger.ts +57 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
// Generated by build-plugin-runtime-entries.mjs. Do not edit.
|
|
2
2
|
import { defineCommand } from 'zhin.js/command';
|
|
3
|
-
import { icqqLoginControls, icqqLoginForbidden, isIcqqLoginOperator, pickIcqqLoginControl, } from '../definition.js';
|
|
3
|
+
import { activeIcqqChallenge, challengeCommandHint, challengeLinks, icqqLoginControls, icqqLoginForbidden, isIcqqLoginOperator, pendingTaskLabel, pickIcqqLoginControl, } from '../definition.js';
|
|
4
4
|
const QR_IMAGE = /^data:image\/[a-z0-9.+-]+;base64,(.+)$/iu;
|
|
5
5
|
export default defineCommand({
|
|
6
6
|
description: '获取 ICQQ 登录二维码(发给当前会话)',
|
|
@@ -12,36 +12,34 @@ export default defineCommand({
|
|
|
12
12
|
if (!picked.control)
|
|
13
13
|
return picked.text ?? 'ICQQ 适配器还没有启动';
|
|
14
14
|
const control = picked.control;
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
+ '完成后回复 `icqq login code <内容>`。';
|
|
23
|
-
}
|
|
15
|
+
let active = activeIcqqChallenge(control);
|
|
16
|
+
if (active && active.challenge.kind !== 'qrcode') {
|
|
17
|
+
const links = challengeLinks(active.challenge).map((link) => `${link.label}:${link.url}`);
|
|
18
|
+
return [`ICQQ ${control.endpointId} 当前是${pendingTaskLabel(active.challenge.kind)},不是扫码登录。`, ...links,
|
|
19
|
+
challengeCommandHint(active.challenge.kind, control.endpointId)].join('\n');
|
|
20
|
+
}
|
|
21
|
+
if (!active) {
|
|
24
22
|
try {
|
|
25
23
|
await control.relogin();
|
|
26
24
|
}
|
|
27
25
|
catch {
|
|
28
26
|
// The SDK may still deliver a QR challenge asynchronously.
|
|
29
27
|
}
|
|
30
|
-
|
|
31
|
-
if (!
|
|
28
|
+
active = activeIcqqChallenge(control);
|
|
29
|
+
if (!active || active.challenge.kind !== 'qrcode')
|
|
32
30
|
return '已触发重新登录,请 5 秒后再发送 `icqq login qr` 获取二维码。';
|
|
33
31
|
}
|
|
34
|
-
const raw =
|
|
32
|
+
const raw = active.challenge.media?.qrImage ?? '';
|
|
35
33
|
const base64 = QR_IMAGE.exec(raw)?.[1];
|
|
36
34
|
if (!base64) {
|
|
37
|
-
return `ICQQ ${
|
|
35
|
+
return `ICQQ ${control.endpointId} 正在等待扫码,但没有可发送的二维码图片;请打开 Console 的 ICQQ 页面完成。`;
|
|
38
36
|
}
|
|
39
37
|
return [
|
|
40
38
|
{
|
|
41
39
|
type: 'text',
|
|
42
40
|
data: {
|
|
43
|
-
text: `ICQQ ${
|
|
44
|
-
+ '用手机 QQ
|
|
41
|
+
text: `ICQQ ${control.endpointId} 登录二维码(约 2 分钟有效)。\n`
|
|
42
|
+
+ '用手机 QQ 扫码,并在手机上确认登录,登录会自动继续;没有继续时回复 `icqq login confirm`。',
|
|
45
43
|
},
|
|
46
44
|
},
|
|
47
45
|
{
|
|
@@ -1,8 +1,12 @@
|
|
|
1
1
|
import { defineCommand } from 'zhin.js/command';
|
|
2
2
|
import {
|
|
3
|
+
activeIcqqChallenge,
|
|
4
|
+
challengeCommandHint,
|
|
5
|
+
challengeLinks,
|
|
3
6
|
icqqLoginControls,
|
|
4
7
|
icqqLoginForbidden,
|
|
5
8
|
isIcqqLoginOperator,
|
|
9
|
+
pendingTaskLabel,
|
|
6
10
|
pickIcqqLoginControl,
|
|
7
11
|
} from '../definition.js';
|
|
8
12
|
|
|
@@ -16,34 +20,32 @@ export default defineCommand({
|
|
|
16
20
|
const picked = pickIcqqLoginControl(icqqLoginControls({ project, owner }), args[0]);
|
|
17
21
|
if (!picked.control) return picked.text ?? 'ICQQ 适配器还没有启动';
|
|
18
22
|
const control = picked.control;
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
+ '完成后回复 `icqq login code <内容>`。';
|
|
27
|
-
}
|
|
23
|
+
let active = activeIcqqChallenge(control);
|
|
24
|
+
if (active && active.challenge.kind !== 'qrcode') {
|
|
25
|
+
const links = challengeLinks(active.challenge).map((link) => `${link.label}:${link.url}`);
|
|
26
|
+
return [`ICQQ ${control.endpointId} 当前是${pendingTaskLabel(active.challenge.kind)},不是扫码登录。`, ...links,
|
|
27
|
+
challengeCommandHint(active.challenge.kind, control.endpointId)].join('\n');
|
|
28
|
+
}
|
|
29
|
+
if (!active) {
|
|
28
30
|
try {
|
|
29
31
|
await control.relogin();
|
|
30
32
|
} catch {
|
|
31
33
|
// The SDK may still deliver a QR challenge asynchronously.
|
|
32
34
|
}
|
|
33
|
-
|
|
34
|
-
if (!
|
|
35
|
+
active = activeIcqqChallenge(control);
|
|
36
|
+
if (!active || active.challenge.kind !== 'qrcode') return '已触发重新登录,请 5 秒后再发送 `icqq login qr` 获取二维码。';
|
|
35
37
|
}
|
|
36
|
-
const raw =
|
|
38
|
+
const raw = active.challenge.media?.qrImage ?? '';
|
|
37
39
|
const base64 = QR_IMAGE.exec(raw)?.[1];
|
|
38
40
|
if (!base64) {
|
|
39
|
-
return `ICQQ ${
|
|
41
|
+
return `ICQQ ${control.endpointId} 正在等待扫码,但没有可发送的二维码图片;请打开 Console 的 ICQQ 页面完成。`;
|
|
40
42
|
}
|
|
41
43
|
return [
|
|
42
44
|
{
|
|
43
45
|
type: 'text',
|
|
44
46
|
data: {
|
|
45
|
-
text: `ICQQ ${
|
|
46
|
-
+ '用手机 QQ
|
|
47
|
+
text: `ICQQ ${control.endpointId} 登录二维码(约 2 分钟有效)。\n`
|
|
48
|
+
+ '用手机 QQ 扫码,并在手机上确认登录,登录会自动继续;没有继续时回复 `icqq login confirm`。',
|
|
47
49
|
},
|
|
48
50
|
},
|
|
49
51
|
{
|
|
@@ -1,30 +1,49 @@
|
|
|
1
1
|
// Generated by build-plugin-runtime-entries.mjs. Do not edit.
|
|
2
2
|
import { defineCommand } from 'zhin.js/command';
|
|
3
|
-
import { icqqLoginControls, icqqLoginForbidden, isIcqqLoginOperator, pendingTaskLabel, pickIcqqLoginControl, } from '../definition.js';
|
|
3
|
+
import { activeIcqqChallenge, challengeCommandHint, challengeLinks, icqqLoginControls, icqqLoginForbidden, isIcqqLoginOperator, pendingTaskLabel, pickIcqqLoginControl, } from '../definition.js';
|
|
4
4
|
export default defineCommand({
|
|
5
|
-
description: '查看 ICQQ
|
|
6
|
-
descriptionI18n: { en: 'Show ICQQ offline status and pending login
|
|
5
|
+
description: '查看 ICQQ 掉线状态与登录验证',
|
|
6
|
+
descriptionI18n: { en: 'Show ICQQ offline status and the pending login verification' },
|
|
7
7
|
async execute({ args, config, input, project, owner }) {
|
|
8
8
|
if (!isIcqqLoginOperator(config, input))
|
|
9
9
|
return icqqLoginForbidden();
|
|
10
10
|
const picked = pickIcqqLoginControl(icqqLoginControls({ project, owner }), args[0]);
|
|
11
11
|
if (!picked.control)
|
|
12
12
|
return picked.text ?? 'ICQQ 适配器还没有启动';
|
|
13
|
-
const
|
|
14
|
-
const
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
13
|
+
const control = picked.control;
|
|
14
|
+
const state = control.state();
|
|
15
|
+
const lines = [`ICQQ ${state.endpointId}: ${state.online ? '在线' : '离线'}(${state.transportState})`];
|
|
16
|
+
const flow = control.flow();
|
|
17
|
+
const active = activeIcqqChallenge(control);
|
|
18
|
+
if (active) {
|
|
19
|
+
const { challenge } = active;
|
|
20
|
+
lines.push(`待处理:${pendingTaskLabel(challenge.kind)}(${challenge.title})`);
|
|
21
|
+
if (challenge.help?.short)
|
|
22
|
+
lines.push(challenge.help.short);
|
|
23
|
+
(challenge.help?.steps ?? []).forEach((step, index) => {
|
|
24
|
+
lines.push(`${index + 1}. ${typeof step === 'string' ? step : step.text}`);
|
|
25
|
+
});
|
|
26
|
+
if (challenge.help?.warning)
|
|
27
|
+
lines.push(`⚠ ${challenge.help.warning}`);
|
|
28
|
+
for (const link of challengeLinks(challenge))
|
|
29
|
+
lines.push(`${link.label}:${link.url}`);
|
|
30
|
+
lines.push(challengeCommandHint(challenge.kind, state.endpointId));
|
|
19
31
|
}
|
|
20
|
-
else {
|
|
32
|
+
else if (state.pending.length > 0) {
|
|
33
|
+
// A challenge raised outside the login flow (another LoginAssist producer).
|
|
21
34
|
lines.push('待处理:');
|
|
22
35
|
for (const task of state.pending) {
|
|
23
36
|
const url = typeof task.payload.url === 'string' ? task.payload.url : undefined;
|
|
24
|
-
lines.push(`- ${pendingTaskLabel(task.type)}(${task.id}
|
|
25
|
-
|
|
37
|
+
lines.push(`- ${pendingTaskLabel(task.type)}(${task.id})${url ? `:${url}` : ''}`);
|
|
38
|
+
}
|
|
39
|
+
}
|
|
40
|
+
else {
|
|
41
|
+
lines.push('没有待处理的登录验证。');
|
|
42
|
+
const outcome = flow?.lastOutcome;
|
|
43
|
+
if (flow && (flow.state === 'failed' || flow.state === 'expired')) {
|
|
44
|
+
lines.push(`上次登录${flow.state === 'expired' ? '验证已过期' : '失败'}${outcome?.message ? `:${outcome.message}` : ''}。`
|
|
45
|
+
+ '发送 `icqq login retry` 重新登录。');
|
|
26
46
|
}
|
|
27
|
-
lines.push('二维码:`icqq login qr`;完成扫码后:`icqq login confirm`;滑块/短信:`icqq login code <内容>`。');
|
|
28
47
|
}
|
|
29
48
|
return lines.join('\n');
|
|
30
49
|
},
|
|
@@ -1,5 +1,8 @@
|
|
|
1
1
|
import { defineCommand } from 'zhin.js/command';
|
|
2
2
|
import {
|
|
3
|
+
activeIcqqChallenge,
|
|
4
|
+
challengeCommandHint,
|
|
5
|
+
challengeLinks,
|
|
3
6
|
icqqLoginControls,
|
|
4
7
|
icqqLoginForbidden,
|
|
5
8
|
isIcqqLoginOperator,
|
|
@@ -8,26 +11,41 @@ import {
|
|
|
8
11
|
} from '../definition.js';
|
|
9
12
|
|
|
10
13
|
export default defineCommand({
|
|
11
|
-
description: '查看 ICQQ
|
|
12
|
-
descriptionI18n: { en: 'Show ICQQ offline status and pending login
|
|
14
|
+
description: '查看 ICQQ 掉线状态与登录验证',
|
|
15
|
+
descriptionI18n: { en: 'Show ICQQ offline status and the pending login verification' },
|
|
13
16
|
async execute({ args, config, input, project, owner }) {
|
|
14
17
|
if (!isIcqqLoginOperator(config, input)) return icqqLoginForbidden();
|
|
15
18
|
const picked = pickIcqqLoginControl(icqqLoginControls({ project, owner }), args[0]);
|
|
16
19
|
if (!picked.control) return picked.text ?? 'ICQQ 适配器还没有启动';
|
|
17
|
-
const
|
|
18
|
-
const
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
20
|
+
const control = picked.control;
|
|
21
|
+
const state = control.state();
|
|
22
|
+
const lines = [`ICQQ ${state.endpointId}: ${state.online ? '在线' : '离线'}(${state.transportState})`];
|
|
23
|
+
const flow = control.flow();
|
|
24
|
+
const active = activeIcqqChallenge(control);
|
|
25
|
+
if (active) {
|
|
26
|
+
const { challenge } = active;
|
|
27
|
+
lines.push(`待处理:${pendingTaskLabel(challenge.kind)}(${challenge.title})`);
|
|
28
|
+
if (challenge.help?.short) lines.push(challenge.help.short);
|
|
29
|
+
(challenge.help?.steps ?? []).forEach((step, index) => {
|
|
30
|
+
lines.push(`${index + 1}. ${typeof step === 'string' ? step : step.text}`);
|
|
31
|
+
});
|
|
32
|
+
if (challenge.help?.warning) lines.push(`⚠ ${challenge.help.warning}`);
|
|
33
|
+
for (const link of challengeLinks(challenge)) lines.push(`${link.label}:${link.url}`);
|
|
34
|
+
lines.push(challengeCommandHint(challenge.kind, state.endpointId));
|
|
35
|
+
} else if (state.pending.length > 0) {
|
|
36
|
+
// A challenge raised outside the login flow (another LoginAssist producer).
|
|
24
37
|
lines.push('待处理:');
|
|
25
38
|
for (const task of state.pending) {
|
|
26
39
|
const url = typeof task.payload.url === 'string' ? task.payload.url : undefined;
|
|
27
|
-
lines.push(`- ${pendingTaskLabel(task.type)}(${task.id}
|
|
28
|
-
|
|
40
|
+
lines.push(`- ${pendingTaskLabel(task.type)}(${task.id})${url ? `:${url}` : ''}`);
|
|
41
|
+
}
|
|
42
|
+
} else {
|
|
43
|
+
lines.push('没有待处理的登录验证。');
|
|
44
|
+
const outcome = flow?.lastOutcome;
|
|
45
|
+
if (flow && (flow.state === 'failed' || flow.state === 'expired')) {
|
|
46
|
+
lines.push(`上次登录${flow.state === 'expired' ? '验证已过期' : '失败'}${outcome?.message ? `:${outcome.message}` : ''}。`
|
|
47
|
+
+ '发送 `icqq login retry` 重新登录。');
|
|
29
48
|
}
|
|
30
|
-
lines.push('二维码:`icqq login qr`;完成扫码后:`icqq login confirm`;滑块/短信:`icqq login code <内容>`。');
|
|
31
49
|
}
|
|
32
50
|
return lines.join('\n');
|
|
33
51
|
},
|
package/lib/endpoint.d.ts
CHANGED
|
@@ -14,13 +14,26 @@ export interface IcqqEndpointOptions {
|
|
|
14
14
|
/** Out-of-band outage notifier; absent in tests/hosts without outbound. */
|
|
15
15
|
readonly notifier?: AdapterOutageNotifier;
|
|
16
16
|
}
|
|
17
|
+
/**
|
|
18
|
+
* Log level of an ICQQ `system.*` event by meaning: coming online and login challenges waiting for
|
|
19
|
+
* the person (QR code, device lock, slider, identity check) are normal progress (`info`); only
|
|
20
|
+
* failures (login errors, going offline) are `warn`, so a routine login stays out of the warnings.
|
|
21
|
+
*/
|
|
22
|
+
export declare function icqqSystemEventLogLevel(type: string): 'info' | 'warn';
|
|
17
23
|
export declare class IcqqEndpoint extends ManagedIcqqClient implements Endpoint {
|
|
18
24
|
#private;
|
|
19
25
|
readonly [endpointSpec]: EndpointSpecification;
|
|
20
26
|
readonly endpointName: string;
|
|
21
27
|
readonly loginControl: IcqqLoginControl;
|
|
22
28
|
constructor(options: IcqqEndpointOptions);
|
|
23
|
-
/** Force a login attempt (
|
|
29
|
+
/** Force a login attempt (Console「重新登录」, recovery command): a new login flow. */
|
|
24
30
|
relogin(): Promise<void>;
|
|
31
|
+
/**
|
|
32
|
+
* Console「刷新二维码」: a new QR code in place while a QR challenge is shown; any other
|
|
33
|
+
* challenge cannot be refreshed alone, so this logs in again.
|
|
34
|
+
*/
|
|
35
|
+
refreshLoginChallenge(): Promise<void>;
|
|
25
36
|
admit(msg: IcqqInboundMessage): Promise<void>;
|
|
26
37
|
}
|
|
38
|
+
/** Log line of a login challenge: its kind and the link's host, never the link, image or device. */
|
|
39
|
+
export declare function loginChallengeLogLine(kind: string, payload: Record<string, unknown> | null): Record<string, unknown>;
|