@zhin.js/adapter-qq 1.1.4 → 1.1.5

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/src/ws.ts CHANGED
@@ -2,12 +2,14 @@
2
2
  * QQ WebSocket transport: bot factory and inbound message normalization.
3
3
  */
4
4
  import path from 'node:path';
5
+ import { createQqStreamAgent } from './stream-proxy.js';
5
6
  import {
6
7
  Bot,
7
8
  ReceiverMode,
8
9
  type ApproveJoinRequestOptions,
9
10
  type Sendable,
10
11
  } from 'qq-official-bot';
12
+ import type { EndpointTransportState } from 'zhin.js/adapter';
11
13
  import type { MediaRef, Segment } from '@zhin.js/im-contract';
12
14
  import type { QqOutboundMessage } from './outbound.js';
13
15
  import type { QqChannelKind, QqInboundMessage, ResolvedQqWebsocketConfig } from './protocol.js';
@@ -36,6 +38,7 @@ export type QqOfficialApi = Pick<Bot,
36
38
 
37
39
  /** Minimal bot surface used by the endpoint (real qq-official-bot or test mock). */
38
40
  export interface QqBotTransport {
41
+ readonly transportState?: EndpointTransportState;
39
42
  /** Full qq-official-bot business API; lifecycle remains owned by the Endpoint. */
40
43
  readonly api: QqOfficialApi;
41
44
  start(): Promise<void>;
@@ -278,6 +281,7 @@ function extractTextContent(message: unknown): string {
278
281
 
279
282
  export function defaultCreateBot(config: ResolvedQqWebsocketConfig): QqBotTransport {
280
283
  const bot = new Bot({
284
+ ...({ handleProcessErrors: false } as Record<string, unknown>),
281
285
  appid: config.appid,
282
286
  secret: config.secret,
283
287
  mode: ReceiverMode.WEBSOCKET,
@@ -290,14 +294,35 @@ export function defaultCreateBot(config: ResolvedQqWebsocketConfig): QqBotTransp
290
294
  ...(config.gatewayUrl ? { gatewayUrl: config.gatewayUrl } : {}),
291
295
  });
292
296
 
297
+ const receiver = bot.receiver as unknown as { config: { autoReconnect: boolean; agent?: import('node:https').Agent }; on(event: string, listener: (...args: unknown[]) => void): void; removeListener(event: string, listener: (...args: unknown[]) => void): void };
298
+ receiver.config.autoReconnect = false;
299
+ const agent = config.streamProxy ? createQqStreamAgent(config.streamProxy) : undefined;
300
+ receiver.config.agent = agent;
301
+ const receiverListeners: Array<{ event: string; listener: (...args: unknown[]) => void }> = [];
302
+ // Keep receiver error ownership after Session.start removes its temporary listener.
303
+ const receiverError = () => {};
304
+ receiver.on('error', receiverError);
293
305
  return {
306
+ get transportState(): EndpointTransportState {
307
+ const receiver = bot.receiver as unknown as { isReady?: boolean; isStarted?: boolean; isClosed?: boolean; handler?: { ws?: { readyState: number } } };
308
+ const state = receiver.handler?.ws?.readyState;
309
+ if (state === 1 && receiver.isReady && receiver.isClosed === false) return 'open';
310
+ if (state === 0) return 'connecting';
311
+ if (receiver.isStarted) return 'reconnecting';
312
+ return 'closed';
313
+ },
294
314
  api: bot,
295
315
  start: () => bot.start().then(() => undefined),
296
- stop: () => stopQqOfficialBot(bot as unknown as QqOfficialBotLifecycle),
316
+ stop: async () => {
317
+ try { await stopQqOfficialBot(bot as unknown as QqOfficialBotLifecycle); }
318
+ finally { agent?.destroy(); }
319
+ },
297
320
  on: (event, listener) => {
298
- bot.on(event as never, listener as never);
321
+ if (event === 'transport.close') { receiver.on('close', listener); receiverListeners.push({ event: 'close', listener }); }
322
+ else bot.on(event as never, listener as never);
299
323
  },
300
324
  removeAllListeners: () => {
325
+ for (const entry of receiverListeners.splice(0)) receiver.removeListener(entry.event, entry.listener);
301
326
  bot.removeAllListeners(undefined as never);
302
327
  },
303
328
  sendPrivateMessage: (userId, message) => bot.sendPrivateMessage(userId, message as Sendable),