@zhin.js/adapter-icqq 1.1.17 → 1.1.18

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.
Files changed (39) hide show
  1. package/CHANGELOG.md +46 -0
  2. package/README.md +44 -3
  3. package/adapters/icqq/index.js +10 -2
  4. package/adapters/icqq/index.ts +10 -2
  5. package/commands/icqq/login/code/index.js +28 -0
  6. package/commands/icqq/login/code/index.ts +30 -0
  7. package/commands/icqq/login/confirm/index.js +20 -0
  8. package/commands/icqq/login/confirm/index.ts +21 -0
  9. package/commands/icqq/login/definition.js +65 -0
  10. package/commands/icqq/login/definition.ts +72 -0
  11. package/commands/icqq/login/qr/index.js +53 -0
  12. package/commands/icqq/login/qr/index.ts +55 -0
  13. package/commands/icqq/login/retry/index.js +21 -0
  14. package/commands/icqq/login/retry/index.ts +23 -0
  15. package/commands/icqq/login/status/index.js +31 -0
  16. package/commands/icqq/login/status/index.ts +34 -0
  17. package/lib/console-spec.d.ts +17 -0
  18. package/lib/console-spec.js +11 -0
  19. package/lib/console.d.ts +8 -0
  20. package/lib/console.js +16 -0
  21. package/lib/endpoint.d.ts +9 -2
  22. package/lib/endpoint.js +194 -37
  23. package/lib/icqq-login-control.d.ts +18 -0
  24. package/lib/icqq-login-control.js +8 -0
  25. package/lib/index.d.ts +1 -1
  26. package/lib/index.js +1 -1
  27. package/lib/protocol.d.ts +10 -0
  28. package/lib/protocol.js +24 -9
  29. package/package.json +17 -9
  30. package/pages/index/index.js +15 -0
  31. package/pages/index/index.ts +16 -0
  32. package/plugin.js +6 -1
  33. package/schema.json +174 -0
  34. package/src/console-spec.ts +17 -0
  35. package/src/console.ts +21 -0
  36. package/src/endpoint.ts +193 -42
  37. package/src/icqq-login-control.ts +30 -0
  38. package/src/index.ts +2 -0
  39. package/src/protocol.ts +32 -8
@@ -0,0 +1,30 @@
1
+ /**
2
+ * ICQQ login control surface exposed to the Plugin Runtime, Console and the
3
+ * recovery command. One control per endpoint instance (UIN), provided by the
4
+ * ICQQ plugin setup and filled by `adapters/icqq/create()`.
5
+ */
6
+ import { createToken } from 'zhin.js';
7
+ import type { PendingLoginTask } from '@zhin.js/core';
8
+
9
+ export interface IcqqLoginControlState {
10
+ readonly endpointId: string;
11
+ readonly transportState: string;
12
+ readonly online: boolean;
13
+ readonly pending: readonly PendingLoginTask[];
14
+ }
15
+
16
+ export interface IcqqLoginControl {
17
+ readonly endpointId: string;
18
+ state(): IcqqLoginControlState;
19
+ pending(): readonly PendingLoginTask[];
20
+ /** Force a login attempt; rejects when the endpoint is stopped or the SDK login fails. */
21
+ relogin(): Promise<void>;
22
+ submit(taskId: string, value: string | Record<string, unknown>): boolean;
23
+ cancel(taskId: string, reason?: string): boolean;
24
+ }
25
+
26
+ /** `endpoint id (QQ uin)` → control for the current generation. */
27
+ export const icqqLoginControlToken = createToken<Map<string, IcqqLoginControl>>(
28
+ 'zhin.icqq.login-control',
29
+ 'ICQQ login control surface for recovery commands',
30
+ );
package/src/index.ts CHANGED
@@ -3,12 +3,14 @@ export {
3
3
  formatInboundContent,
4
4
  formatOutboundBody,
5
5
  formatOutboundMessages,
6
+ formatOutboundMessageParts,
6
7
  icqqInboundConversation,
7
8
  icqqOutboundTarget,
8
9
  type ActionResult,
9
10
  type IcqqEndpointConfig,
10
11
  type IcqqInboundMessage,
11
12
  type IcqqWireSegment,
13
+ type IcqqOutboundMessagePart,
12
14
  type ParsedIcqqSendTarget,
13
15
  type ResolvedIcqqConfig,
14
16
  } from './protocol.js';
package/src/protocol.ts CHANGED
@@ -1,5 +1,6 @@
1
1
  import type { MessageElem, Sendable, ShareElem } from '@icqqjs/icqq';
2
2
  import { isMediaRef, type ConversationRef } from '@zhin.js/im-contract';
3
+ import { resolveAdapterNotifyTarget, type AdapterNotifyTarget, type ResolvedAdapterNotifyTarget } from 'zhin.js/adapter';
3
4
  import type { MessageSegment, SendContent } from "zhin.js";
4
5
  import { resolveIcqqMediaFile } from "./cq-message.js";
5
6
 
@@ -246,6 +247,8 @@ export interface IcqqEndpointConfig {
246
247
  readonly autoServer?: boolean;
247
248
  readonly qqnt?: boolean;
248
249
  readonly ntLogin?: boolean;
250
+ /** Optional out-of-band outage notification target (usually a QQ adapter instance). */
251
+ readonly notify?: AdapterNotifyTarget;
249
252
  }
250
253
 
251
254
  export interface ResolvedIcqqConfig {
@@ -264,6 +267,7 @@ export interface ResolvedIcqqConfig {
264
267
  readonly autoServer?: boolean;
265
268
  readonly qqnt?: boolean;
266
269
  readonly ntLogin?: boolean;
270
+ readonly notify?: ResolvedAdapterNotifyTarget;
267
271
  }
268
272
 
269
273
  export interface IcqqWireSegment {
@@ -311,6 +315,7 @@ export function resolveIcqqConfig(config: IcqqEndpointConfig): ResolvedIcqqConfi
311
315
  const autoServer = config.autoServer ?? true;
312
316
  const qqnt = config.qqnt;
313
317
  const ntLogin = config.ntLogin;
318
+ const notify = resolveAdapterNotifyTarget(config.notify);
314
319
  return {
315
320
  context: 'icqq',
316
321
  id,
@@ -327,6 +332,7 @@ export function resolveIcqqConfig(config: IcqqEndpointConfig): ResolvedIcqqConfi
327
332
  ...(autoServer != null ? { autoServer } : {}),
328
333
  ...(qqnt != null ? { qqnt } : {}),
329
334
  ...(ntLogin != null ? { ntLogin } : {}),
335
+ ...(notify ? { notify } : {}),
330
336
  };
331
337
  }
332
338
 
@@ -456,19 +462,37 @@ export function formatOutboundBody(payload: unknown): Sendable {
456
462
  * quote or whitespace are dropped: a standalone element cannot carry the quote.
457
463
  */
458
464
  export function formatOutboundMessages(payload: unknown): Sendable[] {
465
+ return formatOutboundMessageParts(payload).map(part => part.message);
466
+ }
467
+
468
+ /** Native bubble and the exact source segment positions which produced it. */
469
+ export interface IcqqOutboundMessagePart {
470
+ readonly message: Sendable;
471
+ readonly sourceIndices?: readonly number[];
472
+ }
473
+
474
+ export function formatOutboundMessageParts(payload: unknown): IcqqOutboundMessagePart[] {
459
475
  if (!Array.isArray(payload)) {
460
- return isWireSegment(payload) ? formatOutboundMessages([payload]) : [formatOutboundBody(payload)];
476
+ if (isWireSegment(payload)) return formatOutboundMessageParts([payload]);
477
+ const message = formatOutboundBody(payload);
478
+ return [{ message, ...(typeof payload === 'string' && message !== payload ? {} : { sourceIndices: [0] }) }];
461
479
  }
462
- const groups: (string | MessageElem)[][] = [[]];
463
- for (const element of formatOutboundElements(payload)) {
464
- if (isStandaloneElement(element)) groups.push([element], []);
465
- else groups.at(-1)!.push(element);
480
+ const groups: { element: string | MessageElem; sourceIndex: number }[][] = [[]];
481
+ for (const [sourceIndex, source] of payload.entries()) {
482
+ for (const element of formatOutboundElements([source])) {
483
+ const item = { element, sourceIndex };
484
+ if (isStandaloneElement(element)) groups.push([item], []);
485
+ else groups.at(-1)!.push(item);
486
+ }
466
487
  }
467
- const messages = groups.filter(group => group.some(element => typeof element === 'string'
488
+ const messages = groups.filter(group => group.some(({ element }) => typeof element === 'string'
468
489
  ? element.trim() !== ''
469
490
  : element.type !== 'reply' && element.type !== 'quote'));
470
- if (messages.length === 0) return ['\u200b'];
471
- return messages.map(group => group.length === 1 ? group[0]! : group);
491
+ if (messages.length === 0) return [{ message: '\u200b', sourceIndices: [] }];
492
+ return messages.map(group => ({
493
+ message: group.length === 1 ? group[0]!.element : group.map(item => item.element),
494
+ sourceIndices: [...new Set(group.map(item => item.sourceIndex))],
495
+ }));
472
496
  }
473
497
 
474
498
  function isStandaloneElement(element: string | MessageElem): element is MessageElem {