@zhin.js/adapter-icqq 1.1.27 → 1.1.29

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.
@@ -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
- const findQr = () => control.pending().find((task) => task.type === 'qrcode' || task.type === 'auth');
16
- let task = findQr();
17
- if (!task) {
18
- const other = control.pending().find((item) => item.type !== 'qrcode' && item.type !== 'auth');
19
- if (other) {
20
- const url = typeof other.payload.url === 'string' ? other.payload.url : '';
21
- return `ICQQ ${other.endpointKey} 当前是${other.type === 'slider' ? '滑块验证' : '登录验证'},请打开 ${url || '验证页面'},`
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
- task = findQr();
31
- if (!task)
28
+ active = activeIcqqChallenge(control);
29
+ if (!active || active.challenge.kind !== 'qrcode')
32
30
  return '已触发重新登录,请 5 秒后再发送 `icqq login qr` 获取二维码。';
33
31
  }
34
- const raw = typeof task.payload.image === 'string' ? task.payload.image : '';
32
+ const raw = active.challenge.media?.qrImage ?? '';
35
33
  const base64 = QR_IMAGE.exec(raw)?.[1];
36
34
  if (!base64) {
37
- return `ICQQ ${task.endpointKey} 当前是 ${task.type === 'qrcode' ? '二维码' : '登录确认'}待办,但没有可发送的图片;请打开 Console 的 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 ${task.endpointKey} 登录二维码(约 3 分钟有效)。\n`
44
- + '用手机 QQ 扫码,完成后回复 `icqq login confirm`。',
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
- const findQr = () => control.pending().find((task) => task.type === 'qrcode' || task.type === 'auth');
20
- let task = findQr();
21
- if (!task) {
22
- const other = control.pending().find((item) => item.type !== 'qrcode' && item.type !== 'auth');
23
- if (other) {
24
- const url = typeof other.payload.url === 'string' ? other.payload.url : '';
25
- return `ICQQ ${other.endpointKey} 当前是${other.type === 'slider' ? '滑块验证' : '登录验证'},请打开 ${url || '验证页面'},`
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
- task = findQr();
34
- if (!task) return '已触发重新登录,请 5 秒后再发送 `icqq login qr` 获取二维码。';
35
+ active = activeIcqqChallenge(control);
36
+ if (!active || active.challenge.kind !== 'qrcode') return '已触发重新登录,请 5 秒后再发送 `icqq login qr` 获取二维码。';
35
37
  }
36
- const raw = typeof task.payload.image === 'string' ? task.payload.image : '';
38
+ const raw = active.challenge.media?.qrImage ?? '';
37
39
  const base64 = QR_IMAGE.exec(raw)?.[1];
38
40
  if (!base64) {
39
- return `ICQQ ${task.endpointKey} 当前是 ${task.type === 'qrcode' ? '二维码' : '登录确认'}待办,但没有可发送的图片;请打开 Console 的 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 ${task.endpointKey} 登录二维码(约 3 分钟有效)。\n`
46
- + '用手机 QQ 扫码,完成后回复 `icqq login confirm`。',
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 tasks' },
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 state = picked.control.state();
14
- const lines = [
15
- `ICQQ ${state.endpointId}: ${state.online ? '在线' : '离线'}(${state.transportState})`,
16
- ];
17
- if (state.pending.length === 0) {
18
- lines.push('没有待处理的登录验证。');
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},endpoint ${task.endpointKey})`
25
- + (url ? `:${url}` : ''));
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 tasks' },
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 state = picked.control.state();
18
- const lines = [
19
- `ICQQ ${state.endpointId}: ${state.online ? '在线' : '离线'}(${state.transportState})`,
20
- ];
21
- if (state.pending.length === 0) {
22
- lines.push('没有待处理的登录验证。');
23
- } else {
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},endpoint ${task.endpointKey})`
28
- + (url ? `:${url}` : ''));
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
@@ -20,7 +20,14 @@ export declare class IcqqEndpoint extends ManagedIcqqClient implements Endpoint
20
20
  readonly endpointName: string;
21
21
  readonly loginControl: IcqqLoginControl;
22
22
  constructor(options: IcqqEndpointOptions);
23
- /** Force a login attempt (manual recovery command). */
23
+ /** Force a login attempt (Console「重新登录」, recovery command): a new login flow. */
24
24
  relogin(): Promise<void>;
25
+ /**
26
+ * Console「刷新二维码」: a new QR code in place while a QR challenge is shown; any other
27
+ * challenge cannot be refreshed alone, so this logs in again.
28
+ */
29
+ refreshLoginChallenge(): Promise<void>;
25
30
  admit(msg: IcqqInboundMessage): Promise<void>;
26
31
  }
32
+ /** Log line of a login challenge: its kind and the link's host, never the link, image or device. */
33
+ export declare function loginChallengeLogLine(kind: string, payload: Record<string, unknown> | null): Record<string, unknown>;