@zhin.js/adapter-qq 1.1.18 → 1.1.19

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 (102) hide show
  1. package/CHANGELOG.md +39 -0
  2. package/README.md +21 -18
  3. package/adapters/qq/index.js +4 -38
  4. package/adapters/qq/index.ts +3 -46
  5. package/agents/qq/conversation.js +1 -1
  6. package/agents/qq/conversation.ts +1 -1
  7. package/agents/qq/skills/qq/tools/add_role/index.js +2 -1
  8. package/agents/qq/skills/qq/tools/add_role/index.ts +2 -1
  9. package/agents/qq/skills/qq/tools/channel_info/index.js +1 -1
  10. package/agents/qq/skills/qq/tools/channel_info/index.ts +1 -1
  11. package/agents/qq/skills/qq/tools/create_role/index.js +1 -1
  12. package/agents/qq/skills/qq/tools/create_role/index.ts +1 -1
  13. package/agents/qq/skills/qq/tools/guild_kick_member/index.js +1 -1
  14. package/agents/qq/skills/qq/tools/guild_kick_member/index.ts +1 -1
  15. package/agents/qq/skills/qq/tools/guild_mute_member/index.js +1 -1
  16. package/agents/qq/skills/qq/tools/guild_mute_member/index.ts +1 -1
  17. package/agents/qq/skills/qq/tools/list_channels/index.js +1 -1
  18. package/agents/qq/skills/qq/tools/list_channels/index.ts +1 -1
  19. package/agents/qq/skills/qq/tools/list_roles/index.js +1 -1
  20. package/agents/qq/skills/qq/tools/list_roles/index.ts +1 -1
  21. package/agents/qq/skills/qq/tools/member_detail/index.js +1 -1
  22. package/agents/qq/skills/qq/tools/member_detail/index.ts +1 -1
  23. package/agents/qq/skills/qq/tools/remove_role/index.js +2 -1
  24. package/agents/qq/skills/qq/tools/remove_role/index.ts +2 -1
  25. package/agents/qq/skills/qq-conversation/tools/conversation_info/index.js +1 -1
  26. package/agents/qq/skills/qq-conversation/tools/conversation_info/index.ts +1 -1
  27. package/agents/qq/skills/qq-directory/tools/list_guilds/index.js +1 -1
  28. package/agents/qq/skills/qq-directory/tools/list_guilds/index.ts +1 -1
  29. package/agents/qq/skills/qq-group-admin/tools/group_kick/index.js +2 -2
  30. package/agents/qq/skills/qq-group-admin/tools/group_kick/index.ts +2 -2
  31. package/agents/qq/skills/qq-group-admin/tools/handle_join_request/index.js +1 -1
  32. package/agents/qq/skills/qq-group-admin/tools/handle_join_request/index.ts +1 -1
  33. package/agents/qq/skills/qq-group-admin/tools/set_mute/index.js +2 -2
  34. package/agents/qq/skills/qq-group-admin/tools/set_mute/index.ts +2 -2
  35. package/agents/qq/skills/qq-group-directory/tools/group_info/index.js +1 -1
  36. package/agents/qq/skills/qq-group-directory/tools/group_info/index.ts +1 -1
  37. package/agents/qq/skills/qq-group-directory/tools/group_member_info/index.js +1 -1
  38. package/agents/qq/skills/qq-group-directory/tools/group_member_info/index.ts +1 -1
  39. package/agents/qq/skills/qq-group-directory/tools/group_members/index.js +1 -1
  40. package/agents/qq/skills/qq-group-directory/tools/group_members/index.ts +1 -1
  41. package/agents/qq/skills/qq-group-state/tools/group_blacklist/index.js +1 -1
  42. package/agents/qq/skills/qq-group-state/tools/group_blacklist/index.ts +1 -1
  43. package/agents/qq/skills/qq-group-state/tools/list_join_requests/index.js +1 -1
  44. package/agents/qq/skills/qq-group-state/tools/list_join_requests/index.ts +1 -1
  45. package/agents/qq/skills/qq-group-state/tools/list_muted/index.js +1 -1
  46. package/agents/qq/skills/qq-group-state/tools/list_muted/index.ts +1 -1
  47. package/commands/qq/endpoint/definition.js +41 -36
  48. package/commands/qq/endpoint/definition.ts +41 -67
  49. package/lib/adapter.d.ts +13 -0
  50. package/lib/adapter.js +27 -0
  51. package/lib/client.d.ts +7 -9
  52. package/lib/client.js +1 -2
  53. package/lib/console.d.ts +1 -6
  54. package/lib/console.js +29 -20
  55. package/lib/endpoint.d.ts +8 -27
  56. package/lib/endpoint.js +218 -165
  57. package/lib/group-admin.d.ts +14 -9
  58. package/lib/group-admin.js +21 -20
  59. package/lib/i18n.js +4 -4
  60. package/lib/image-size.d.ts +6 -1
  61. package/lib/image-size.js +2 -3
  62. package/lib/index.d.ts +5 -4
  63. package/lib/index.js +3 -3
  64. package/lib/interaction.js +1 -1
  65. package/lib/outbound.d.ts +4 -1
  66. package/lib/outbound.js +3 -2
  67. package/lib/protocol.d.ts +2 -0
  68. package/lib/qq-console-spec.js +12 -1
  69. package/lib/qq-console.d.ts +3 -4
  70. package/lib/qq-console.js +16 -6
  71. package/lib/qq-endpoint-write.d.ts +6 -10
  72. package/lib/qq-endpoint-write.js +5 -32
  73. package/lib/qq-runtime-state.d.ts +11 -10
  74. package/lib/qq-runtime-state.js +4 -5
  75. package/lib/qq-send.d.ts +7 -14
  76. package/lib/qq-send.js +20 -6
  77. package/lib/webhook.d.ts +3 -6
  78. package/lib/webhook.js +6 -49
  79. package/lib/ws.d.ts +10 -33
  80. package/lib/ws.js +21 -70
  81. package/middlewares/bot-kind-reply/index.js +1 -2
  82. package/middlewares/bot-kind-reply/index.ts +0 -2
  83. package/package.json +19 -19
  84. package/plugin.js +5 -5
  85. package/src/adapter.ts +23 -0
  86. package/src/client.ts +4 -12
  87. package/src/console.ts +27 -20
  88. package/src/endpoint.ts +218 -160
  89. package/src/group-admin.ts +28 -26
  90. package/src/i18n.ts +4 -4
  91. package/src/image-size.ts +7 -3
  92. package/src/index.ts +5 -5
  93. package/src/interaction.ts +1 -1
  94. package/src/outbound.ts +3 -2
  95. package/src/protocol.ts +2 -0
  96. package/src/qq-console-spec.ts +12 -1
  97. package/src/qq-console.ts +14 -8
  98. package/src/qq-endpoint-write.ts +10 -38
  99. package/src/qq-runtime-state.ts +11 -16
  100. package/src/qq-send.ts +28 -12
  101. package/src/webhook.ts +7 -57
  102. package/src/ws.ts +27 -105
package/src/ws.ts CHANGED
@@ -42,50 +42,7 @@ export type QqOfficialApi = Pick<Bot,
42
42
  | 'guild'
43
43
  >;
44
44
 
45
- /** Minimal bot surface used by the endpoint (real qq-official-bot or test mock). */
46
- export interface QqBotTransport {
47
- readonly transportState?: EndpointTransportState;
48
- /** Full qq-official-bot business API; lifecycle remains owned by the Endpoint. */
49
- readonly api: QqOfficialApi;
50
- start(): Promise<void>;
51
- stop(): Promise<void>;
52
- on(event: string, listener: (...args: unknown[]) => void): void;
53
- removeAllListeners(): void;
54
- /** `source` makes it a passive reply; without it the platform treats the message as active. */
55
- sendPrivateMessage(userId: string, message: QqOutboundMessage, source?: QqReplySource, options?: QqSendOptions): Promise<unknown>;
56
- sendGroupMessage(groupId: string, message: QqOutboundMessage, source?: QqReplySource, options?: QqSendOptions): Promise<unknown>;
57
- sendGuildMessage(channelId: string, message: QqOutboundMessage, source?: QqReplySource, options?: QqSendOptions): Promise<unknown>;
58
- sendDirectMessage?(guildId: string, message: QqOutboundMessage, source?: QqReplySource, options?: QqSendOptions): Promise<unknown>;
59
- recallPrivateMessage?(userId: string, messageId: string): Promise<unknown>;
60
- recallGroupMessage?(groupId: string, messageId: string): Promise<unknown>;
61
- recallGuildMessage?(channelId: string, messageId: string): Promise<unknown>;
62
- recallDirectMessage?(guildId: string, messageId: string): Promise<unknown>;
63
- getGuilds(): Promise<unknown[]>;
64
- getChannels(guildId: string): Promise<unknown[]>;
65
- getChannelInfo(channelId: string): Promise<unknown>;
66
- getGuildMember(guildId: string, userId: string): Promise<unknown>;
67
- getGuildRoles(guildId: string): Promise<unknown[]>;
68
- createGuildRole(guildId: string, name: string, color?: number): Promise<unknown>;
69
- addMemberRole(
70
- guildId: string,
71
- channelId: string,
72
- userId: string,
73
- roleId: string,
74
- ): Promise<boolean>;
75
- removeMemberRole(
76
- guildId: string,
77
- channelId: string,
78
- userId: string,
79
- roleId: string,
80
- ): Promise<boolean>;
81
- approveGroupJoinRequest?(
82
- groupId: string,
83
- userId: string,
84
- options: ApproveJoinRequestOptions,
85
- ): Promise<unknown>;
86
- }
87
-
88
- export type CreateQqBot = (config: ResolvedQqWebsocketConfig) => QqBotTransport;
45
+ export type QqBotTransport = Bot;
89
46
 
90
47
  interface QqOfficialBotLifecycle {
91
48
  stop(): Promise<unknown>;
@@ -134,6 +91,7 @@ export function normalizeQqMessage(raw: unknown): QqInboundMessage | null {
134
91
  channel_id?: string | number;
135
92
  guild_id?: string | number;
136
93
  raw_message?: string;
94
+ timestamp?: number;
137
95
  message?: unknown;
138
96
  sender?: { user_id?: string | number; user_name?: string; roles?: (string | number)[] };
139
97
  member?: { roles?: (string | number)[] };
@@ -186,6 +144,8 @@ export function normalizeQqMessage(raw: unknown): QqInboundMessage | null {
186
144
 
187
145
  return {
188
146
  id: String(msg.message_id),
147
+ event: raw,
148
+ ...(typeof msg.timestamp === 'number' && Number.isFinite(msg.timestamp) ? { platformSentAt: msg.timestamp * 1_000 } : {}),
189
149
  content,
190
150
  channelKind,
191
151
  channelId,
@@ -286,78 +246,40 @@ function extractTextContent(message: unknown): string {
286
246
  .join('');
287
247
  }
288
248
 
289
- export function defaultCreateBot(config: ResolvedQqWebsocketConfig): QqBotTransport {
290
- const bot = new Bot({
291
- ...({ handleProcessErrors: false } as Record<string, unknown>),
292
- appid: config.appid,
293
- secret: config.secret,
294
- mode: ReceiverMode.WEBSOCKET,
295
- sandbox: config.sandbox,
296
- dataDir: path.join(process.cwd(), 'data', 'qq'),
297
- // 关闭 qq-official-bot 内置 log4js,入/出站由 getAdapterLogger('qq', …) 统一格式
298
- logLevel: 'off',
249
+ export function qqWebsocketBotOptions(config: ResolvedQqWebsocketConfig): ConstructorParameters<typeof Bot<ReceiverMode.WEBSOCKET>>[0] {
250
+ return { handleProcessErrors: false, appid: config.appid, secret: config.secret,
251
+ mode: ReceiverMode.WEBSOCKET, sandbox: config.sandbox,
252
+ dataDir: path.join(process.cwd(), 'data', 'qq'), logLevel: 'off',
299
253
  ...(config.intents ? { intents: config.intents as never } : {}),
300
254
  ...(config.accessTokenUrl ? { accessTokenUrl: config.accessTokenUrl } : {}),
301
255
  ...(config.gatewayUrl ? { gatewayUrl: config.gatewayUrl } : {}),
302
- });
256
+ };
257
+ }
303
258
 
304
- 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 };
259
+ /** SDK 1.3.3 has no public receiver configuration/state accessor. This seam only reads its verified native fields. */
260
+ export function configureQqWebsocketReceiver(bot: Bot<ReceiverMode.WEBSOCKET>, config: ResolvedQqWebsocketConfig) {
261
+ const receiver = bot.receiver as unknown as {
262
+ config: { autoReconnect: boolean; agent?: import('node:https').Agent };
263
+ isReady?: boolean; isStarted?: boolean; isClosed?: boolean; handler?: { ws?: { readyState: number } };
264
+ };
305
265
  receiver.config.autoReconnect = false;
306
266
  const agent = config.streamProxy ? createQqStreamAgent(config.streamProxy) : undefined;
307
267
  receiver.config.agent = agent;
308
- const receiverListeners: Array<{ event: string; listener: (...args: unknown[]) => void }> = [];
309
- // Keep receiver error ownership after Session.start removes its temporary listener.
310
- const receiverError = () => {};
311
- receiver.on('error', receiverError);
268
+ const observeError = () => undefined;
269
+ bot.receiver.on('error', observeError);
312
270
  return {
313
- get transportState(): EndpointTransportState {
314
- const receiver = bot.receiver as unknown as { isReady?: boolean; isStarted?: boolean; isClosed?: boolean; handler?: { ws?: { readyState: number } } };
271
+ state(): EndpointTransportState {
315
272
  const state = receiver.handler?.ws?.readyState;
316
273
  if (state === 1 && receiver.isReady && receiver.isClosed === false) return 'open';
317
274
  if (state === 0) return 'connecting';
318
- if (receiver.isStarted) return 'reconnecting';
319
- return 'closed';
320
- },
321
- api: bot,
322
- start: () => bot.start().then(() => undefined),
323
- stop: async () => {
324
- try { await stopQqOfficialBot(bot as unknown as QqOfficialBotLifecycle); }
325
- finally { agent?.destroy(); }
275
+ return receiver.isStarted ? 'reconnecting' : 'closed';
326
276
  },
327
- on: (event, listener) => {
328
- if (event === 'transport.close') { receiver.on('close', listener); receiverListeners.push({ event: 'close', listener }); }
329
- else bot.on(event as never, listener as never);
330
- },
331
- removeAllListeners: () => {
332
- for (const entry of receiverListeners.splice(0)) receiver.removeListener(entry.event, entry.listener);
333
- bot.removeAllListeners(undefined as never);
334
- },
335
- sendPrivateMessage: (userId, message, source, options) => bot.sendPrivateMessage(userId, message as Sendable, source, options),
336
- sendGroupMessage: (groupId, message, source, options) => bot.sendGroupMessage(groupId, message as Sendable, source, options),
337
- sendGuildMessage: (channelId, message, source, options) => bot.sendGuildMessage(channelId, message as Sendable, source, options),
338
- sendDirectMessage: (guildId, message, source, options) => bot.sendDirectMessage(guildId, message as Sendable, source, options),
339
- recallPrivateMessage: (userId, messageId) => bot.recallPrivateMessage(userId, messageId),
340
- recallGroupMessage: (groupId, messageId) => bot.recallGroupMessage(groupId, messageId),
341
- recallGuildMessage: (channelId, messageId) => bot.recallGuildMessage(channelId, messageId),
342
- recallDirectMessage: (guildId, messageId) => bot.recallDirectMessage(guildId, messageId),
343
- getGuilds: () => bot.guildService.getList(),
344
- getChannels: (guildId) => bot.channelService.getList(guildId),
345
- getChannelInfo: (channelId) => bot.channelService.getInfo(channelId),
346
- getGuildMember: (guildId, userId) => bot.memberService.getGuildMemberInfo(guildId, userId),
347
- getGuildRoles: (guildId) => bot.guildService.getRoles(guildId),
348
- createGuildRole: (guildId, name, color) =>
349
- bot.guildService.createRole(guildId, { name, color: color || 0, hoist: 0 }),
350
- addMemberRole: async (guildId, channelId, userId, roleId) => {
351
- await bot.memberService.addMemberRole(guildId, channelId, userId, roleId);
352
- return true;
353
- },
354
- removeMemberRole: async (guildId, channelId, userId, roleId) => {
355
- await bot.memberService.removeMemberRole(guildId, channelId, userId, roleId);
356
- return true;
357
- },
358
- approveGroupJoinRequest: (groupId, userId, options) =>
359
- (bot as { approveGroupJoinRequest?: QqBotTransport['approveGroupJoinRequest'] })
360
- .approveGroupJoinRequest?.(groupId, userId, options)
361
- ?? Promise.reject(new Error('approveGroupJoinRequest is not available')),
277
+ dispose(): void { bot.receiver.off('error', observeError); agent?.destroy(); },
362
278
  };
363
279
  }
280
+
281
+ /** Session.stop leaves Auth refresh alive for reconnect; actual instance retirement destroys it. */
282
+ export function disposeQqAuthorization(bot: Bot): void {
283
+ const session = bot.sessionManager as unknown as { authManager: { destroy(): void } };
284
+ session.authManager.destroy();
285
+ }