@zhin.js/adapter-qq 9.0.0 → 9.0.1

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 CHANGED
@@ -1,5 +1,16 @@
1
1
  # @zhin.js/adapter-qq
2
2
 
3
+ ## 9.0.1
4
+
5
+ ### Patch Changes
6
+
7
+ - f7ee28d: Use the shared Endpoint lifecycle for QQ WebSocket and HTTP receivers. Isolate each SDK instance and its Webhook routes, close resources created by late startup completion after cancellation, and ignore callbacks queued by replaced SDK instances. Failed startup releases resources before retrying.
8
+ - Updated dependencies [f7ee28d]
9
+ - @zhin.js/adapter@1.2.2
10
+ - @zhin.js/core@1.5.16
11
+ - zhin.js@7.0.1
12
+ - @zhin.js/agent@1.2.1
13
+
3
14
  ## 9.0.0
4
15
 
5
16
  ### Patch Changes
package/lib/endpoint.d.ts CHANGED
@@ -1,8 +1,7 @@
1
- import { Endpoint } from 'zhin.js/adapter';
2
1
  /**
3
2
  * QQ endpoints — lifecycle, outbound, admit, agent tool surface.
4
3
  */
5
- import { type EndpointControl, type EndpointManagement, type EndpointSendRequest } from 'zhin.js/adapter';
4
+ import { Endpoint, type EndpointControl, type EndpointManagement, type EndpointSendRequest } from 'zhin.js/adapter';
6
5
  import type { HttpHost } from '@zhin.js/host-http';
7
6
  import type { CapabilityId } from 'zhin.js';
8
7
  import { type QqInboundMessage, type ResolvedQqHttpConfig, type ResolvedQqWebsocketConfig } from './protocol.js';
package/lib/endpoint.js CHANGED
@@ -1,8 +1,7 @@
1
- import { Endpoint } from 'zhin.js/adapter';
2
1
  /**
3
2
  * QQ endpoints — lifecycle, outbound, admit, agent tool surface.
4
3
  */
5
- import { createRecallEndpointControl, } from 'zhin.js/adapter';
4
+ import { Endpoint, createEndpointLifecycle, createRecallEndpointControl, } from 'zhin.js/adapter';
6
5
  import { formatCompact, getAdapterLogger, truncatePreview } from '@zhin.js/logger';
7
6
  import { formatOutbound } from './outbound.js';
8
7
  import { formatInboundContent, parseCompoundMessageId, qqInboundConversation, qqOutboundKind, resolveOutboundMessageId, senderDisplayName, } from './protocol.js';
@@ -15,12 +14,14 @@ export class QqWebsocketEndpoint extends Endpoint {
15
14
  #createBot;
16
15
  #bot = null;
17
16
  #open = false;
18
- #started = false;
17
+ #lifecycle;
18
+ #cleanup;
19
19
  management = createQqEndpointManagement(() => this.#requireBot());
20
20
  control = createRecallEndpointControl((id) => this.recallMessage(id));
21
21
  constructor(options) {
22
22
  super();
23
23
  this.#logger = getAdapterLogger('qq', options.config.id);
24
+ this.#lifecycle = createEndpointLifecycle({ name: options.config.id, reconnect: false });
24
25
  this.#options = options;
25
26
  this.#createBot = options.createBot ?? defaultCreateBot;
26
27
  }
@@ -32,21 +33,51 @@ export class QqWebsocketEndpoint extends Endpoint {
32
33
  return this.#options.config.id;
33
34
  }
34
35
  async start() {
35
- if (this.#started)
36
- return;
37
- this.#started = true;
38
- try {
39
- this.#bot = this.#createBot(this.#options.config);
40
- this.#bindBot(this.#bot);
41
- await this.#bot.start();
42
- this.#logger.info(`connected (websocket) | appid: ${this.#options.config.appid}`);
43
- }
44
- catch (error) {
45
- await this.stop();
46
- this.#logger.error('Failed to connect QQ websocket:', error);
47
- const raw = error instanceof Error ? error.message : String(error);
48
- throw new Error(`QQ WebSocket 连接失败:请检查 appid/secret 是否配对、网关地址(gatewayUrl/accessTokenUrl)是否可达(原始错误:${raw})`, { cause: error });
49
- }
36
+ await this.#lifecycle.start(async (handle) => {
37
+ const bot = this.#createBot(this.#options.config);
38
+ this.#bot = bot;
39
+ let cancelled = false;
40
+ let stopping;
41
+ const stopBot = async () => {
42
+ try {
43
+ bot.removeAllListeners();
44
+ }
45
+ catch { /* continue SDK cleanup */ }
46
+ try {
47
+ await bot.stop();
48
+ }
49
+ catch { /* best-effort SDK cleanup */ }
50
+ };
51
+ const cleanup = () => {
52
+ cancelled = true;
53
+ if (this.#bot === bot)
54
+ this.#bot = null;
55
+ return stopping ??= stopBot();
56
+ };
57
+ this.#cleanup = cleanup;
58
+ handle.onForceClose(() => { void cleanup(); });
59
+ try {
60
+ this.#bindBot(bot);
61
+ await bot.start();
62
+ if (cancelled) {
63
+ // SDK startup may create resources after stop; close this exact old client again.
64
+ await stopping;
65
+ await stopBot();
66
+ return;
67
+ }
68
+ this.#logger.info(`connected (websocket) | appid: ${this.#options.config.appid}`);
69
+ }
70
+ catch (error) {
71
+ const wasCancelled = cancelled;
72
+ await cleanup();
73
+ if (wasCancelled) {
74
+ await stopBot();
75
+ return;
76
+ }
77
+ const raw = error instanceof Error ? error.message : String(error);
78
+ throw new Error(`QQ WebSocket 连接失败:请检查 appid/secret 是否配对、网关地址(gatewayUrl/accessTokenUrl)是否可达(原始错误:${raw})`, { cause: error });
79
+ }
80
+ });
50
81
  }
51
82
  open() {
52
83
  this.#open = true;
@@ -56,17 +87,10 @@ export class QqWebsocketEndpoint extends Endpoint {
56
87
  }
57
88
  async stop() {
58
89
  this.#open = false;
59
- if (this.#bot) {
60
- try {
61
- this.#bot.removeAllListeners();
62
- await this.#bot.stop();
63
- }
64
- catch {
65
- /* ignore */
66
- }
67
- this.#bot = null;
68
- }
69
- this.#started = false;
90
+ const cleanup = this.#cleanup;
91
+ this.#cleanup = undefined;
92
+ await this.#lifecycle.stop();
93
+ await cleanup?.();
70
94
  this.#logger.debug(formatCompact({ op: 'disconnect' }));
71
95
  }
72
96
  async send({ conversation, payload }) {
@@ -158,12 +182,16 @@ export class QqWebsocketEndpoint extends Endpoint {
158
182
  }
159
183
  #bindBot(bot) {
160
184
  bindQqBotInboundEvents(bot, (raw) => {
185
+ if (this.#bot !== bot)
186
+ return;
161
187
  this.#emitPlatformEvent('message', raw);
162
188
  const msg = normalizeQqMessage(raw);
163
189
  if (msg)
164
190
  this.admit(msg);
165
191
  });
166
192
  bindQqBotSideEvents(bot, (eventName, raw) => {
193
+ if (this.#bot !== bot)
194
+ return;
167
195
  this.#emitPlatformEvent(eventName, raw);
168
196
  receiveQqSideEvent((name, payload) => this.emit(name, payload), this.#options.config.id, bot, eventName, raw, this.#logger);
169
197
  });
@@ -189,14 +217,15 @@ export class QqHttpEndpoint extends Endpoint {
189
217
  #options;
190
218
  #createBot;
191
219
  #bot = null;
192
- #routeReleases = [];
193
220
  #open = false;
194
- #started = false;
221
+ #lifecycle;
222
+ #cleanup;
195
223
  management = createQqEndpointManagement(() => this.#requireBot());
196
224
  control = createRecallEndpointControl((id) => this.recallMessage(id));
197
225
  constructor(options) {
198
226
  super();
199
227
  this.#logger = getAdapterLogger('qq', options.config.id);
228
+ this.#lifecycle = createEndpointLifecycle({ name: options.config.id, reconnect: false });
200
229
  this.#options = options;
201
230
  this.#createBot = options.createBot ?? defaultCreateHttpBot;
202
231
  }
@@ -208,21 +237,57 @@ export class QqHttpEndpoint extends Endpoint {
208
237
  return this.#options.config.id;
209
238
  }
210
239
  async start() {
211
- if (this.#started)
212
- return;
213
- this.#started = true;
214
- try {
215
- this.#setupRoutes();
216
- this.#bot = this.#createBot(this.#options.config);
217
- this.#bindBot(this.#bot);
218
- await this.#bot.start();
219
- this.#logger.info(`connected (${this.#options.config.mode}) | path: ${this.#options.config.webhookPath}`);
220
- }
221
- catch (error) {
222
- await this.stop();
223
- this.#logger.error('Failed to connect QQ HTTP receiver:', error);
224
- throw error;
225
- }
240
+ await this.#lifecycle.start(async (handle) => {
241
+ const bot = this.#createBot(this.#options.config);
242
+ this.#bot = bot;
243
+ let cancelled = false;
244
+ let stopping;
245
+ const routes = [];
246
+ const stopBot = async () => {
247
+ try {
248
+ bot.removeAllListeners();
249
+ }
250
+ catch { /* continue SDK cleanup */ }
251
+ try {
252
+ await bot.stop();
253
+ }
254
+ catch { /* best-effort SDK cleanup */ }
255
+ };
256
+ const cleanup = () => {
257
+ cancelled = true;
258
+ if (this.#bot === bot)
259
+ this.#bot = null;
260
+ for (const release of routes.splice(0))
261
+ release();
262
+ return stopping ??= stopBot();
263
+ };
264
+ this.#cleanup = cleanup;
265
+ handle.onForceClose(() => { void cleanup(); });
266
+ try {
267
+ this.#bindBot(bot);
268
+ routes.push(...registerQqWebhookRoutes(this.#options.http, {
269
+ config: this.#options.config,
270
+ getBot: () => cancelled ? null : bot,
271
+ }));
272
+ await bot.start();
273
+ if (cancelled) {
274
+ // SDK startup may create resources after stop; close this exact old client again.
275
+ await stopping;
276
+ await stopBot();
277
+ return;
278
+ }
279
+ this.#logger.info(`connected (${this.#options.config.mode}) | path: ${this.#options.config.webhookPath}`);
280
+ }
281
+ catch (error) {
282
+ const wasCancelled = cancelled;
283
+ await cleanup();
284
+ if (wasCancelled) {
285
+ await stopBot();
286
+ return;
287
+ }
288
+ throw error;
289
+ }
290
+ });
226
291
  }
227
292
  open() {
228
293
  this.#open = true;
@@ -232,19 +297,10 @@ export class QqHttpEndpoint extends Endpoint {
232
297
  }
233
298
  async stop() {
234
299
  this.#open = false;
235
- for (const release of this.#routeReleases.splice(0))
236
- release();
237
- if (this.#bot) {
238
- try {
239
- this.#bot.removeAllListeners();
240
- await this.#bot.stop();
241
- }
242
- catch {
243
- /* ignore */
244
- }
245
- this.#bot = null;
246
- }
247
- this.#started = false;
300
+ const cleanup = this.#cleanup;
301
+ this.#cleanup = undefined;
302
+ await this.#lifecycle.stop();
303
+ await cleanup?.();
248
304
  this.#logger.debug(formatCompact({ op: 'disconnect' }));
249
305
  }
250
306
  async send({ conversation, payload }) {
@@ -333,20 +389,18 @@ export class QqHttpEndpoint extends Endpoint {
333
389
  }));
334
390
  });
335
391
  }
336
- #setupRoutes() {
337
- this.#routeReleases.push(...registerQqWebhookRoutes(this.#options.http, {
338
- config: this.#options.config,
339
- getBot: () => this.#bot,
340
- }));
341
- }
342
392
  #bindBot(bot) {
343
393
  bindQqBotInboundEvents(bot, (raw) => {
394
+ if (this.#bot !== bot)
395
+ return;
344
396
  this.#emitPlatformEvent('message', raw);
345
397
  const msg = normalizeQqMessage(raw);
346
398
  if (msg)
347
399
  this.admit(msg);
348
400
  });
349
401
  bindQqBotSideEvents(bot, (eventName, raw) => {
402
+ if (this.#bot !== bot)
403
+ return;
350
404
  this.#emitPlatformEvent(eventName, raw);
351
405
  receiveQqSideEvent((name, payload) => this.emit(name, payload), this.#options.config.id, bot, eventName, raw, this.#logger);
352
406
  });
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@zhin.js/adapter-qq",
3
- "version": "9.0.0",
3
+ "version": "9.0.1",
4
4
  "description": "Zhin.js QQ Official adapter for Plugin Runtime (WebSocket gateway)",
5
5
  "type": "module",
6
6
  "main": "./lib/index.js",
@@ -35,8 +35,8 @@
35
35
  },
36
36
  "dependencies": {
37
37
  "qq-official-bot": "^1.2.3",
38
- "@zhin.js/adapter": "1.2.1",
39
- "@zhin.js/core": "1.5.15",
38
+ "@zhin.js/adapter": "1.2.2",
39
+ "@zhin.js/core": "1.5.16",
40
40
  "@zhin.js/feature-kit": "1.0.13",
41
41
  "@zhin.js/host-http": "1.0.17",
42
42
  "@zhin.js/im-contract": "1.0.4",
@@ -45,12 +45,12 @@
45
45
  },
46
46
  "peerDependencies": {
47
47
  "zod": "^4.0.0",
48
- "@zhin.js/adapter": "1.2.1",
49
- "@zhin.js/agent": "1.2.0",
48
+ "@zhin.js/adapter": "1.2.2",
49
+ "@zhin.js/agent": "1.2.1",
50
50
  "@zhin.js/command": "1.0.16",
51
- "@zhin.js/core": "1.5.15",
51
+ "@zhin.js/core": "1.5.16",
52
52
  "@zhin.js/middleware": "1.0.13",
53
- "zhin.js": "7.0.0"
53
+ "zhin.js": "7.0.1"
54
54
  },
55
55
  "peerDependenciesMeta": {
56
56
  "@zhin.js/agent": {
@@ -74,9 +74,9 @@
74
74
  "typescript": "^6.0.3",
75
75
  "vitest": "^4.1.10",
76
76
  "zod": "^4.4.3",
77
- "@zhin.js/agent": "1.2.0",
77
+ "@zhin.js/agent": "1.2.1",
78
78
  "@zhin.js/host-http": "1.0.17",
79
- "zhin.js": "7.0.0"
79
+ "zhin.js": "7.0.1"
80
80
  },
81
81
  "files": [
82
82
  "adapters",
package/src/endpoint.ts CHANGED
@@ -1,8 +1,8 @@
1
- import { Endpoint } from 'zhin.js/adapter';
2
1
  /**
3
2
  * QQ endpoints — lifecycle, outbound, admit, agent tool surface.
4
3
  */
5
4
  import {
5
+ Endpoint, createEndpointLifecycle, type EndpointLifecycle,
6
6
  createRecallEndpointControl,
7
7
  type EndpointControl,
8
8
  type EndpointChannel,
@@ -59,13 +59,15 @@ export class QqWebsocketEndpoint extends Endpoint<QqBotTransport> {
59
59
  readonly #createBot: CreateQqBot;
60
60
  #bot: QqBotTransport | null = null;
61
61
  #open = false;
62
- #started = false;
62
+ readonly #lifecycle: EndpointLifecycle;
63
+ #cleanup?: () => Promise<void>;
63
64
  readonly management: EndpointManagement = createQqEndpointManagement(() => this.#requireBot());
64
65
  readonly control: EndpointControl = createRecallEndpointControl((id) => this.recallMessage(id));
65
66
 
66
67
  constructor(options: QqEndpointOptions) {
67
68
  super();
68
69
  this.#logger = getAdapterLogger('qq', options.config.id);
70
+ this.#lifecycle = createEndpointLifecycle({ name: options.config.id, reconnect: false });
69
71
  this.#options = options;
70
72
  this.#createBot = options.createBot ?? defaultCreateBot;
71
73
  }
@@ -80,24 +82,46 @@ export class QqWebsocketEndpoint extends Endpoint<QqBotTransport> {
80
82
  }
81
83
 
82
84
  async start(): Promise<void> {
83
- if (this.#started) return;
84
- this.#started = true;
85
- try {
86
- this.#bot = this.#createBot(this.#options.config);
87
- this.#bindBot(this.#bot);
88
- await this.#bot.start();
89
- this.#logger.info(
90
- `connected (websocket) | appid: ${this.#options.config.appid}`,
91
- );
92
- } catch (error) {
93
- await this.stop();
94
- this.#logger.error('Failed to connect QQ websocket:', error);
95
- const raw = error instanceof Error ? error.message : String(error);
96
- throw new Error(
97
- `QQ WebSocket 连接失败:请检查 appid/secret 是否配对、网关地址(gatewayUrl/accessTokenUrl)是否可达(原始错误:${raw})`,
98
- { cause: error },
99
- );
100
- }
85
+ await this.#lifecycle.start(async (handle) => {
86
+ const bot = this.#createBot(this.#options.config);
87
+ this.#bot = bot;
88
+ let cancelled = false;
89
+ let stopping: Promise<void> | undefined;
90
+ const stopBot = async () => {
91
+ try { bot.removeAllListeners(); } catch { /* continue SDK cleanup */ }
92
+ try { await bot.stop(); } catch { /* best-effort SDK cleanup */ }
93
+ };
94
+ const cleanup = () => {
95
+ cancelled = true;
96
+ if (this.#bot === bot) this.#bot = null;
97
+ return stopping ??= stopBot();
98
+ };
99
+ this.#cleanup = cleanup;
100
+ handle.onForceClose(() => { void cleanup(); });
101
+ try {
102
+ this.#bindBot(bot);
103
+ await bot.start();
104
+ if (cancelled) {
105
+ // SDK startup may create resources after stop; close this exact old client again.
106
+ await stopping;
107
+ await stopBot();
108
+ return;
109
+ }
110
+ this.#logger.info(`connected (websocket) | appid: ${this.#options.config.appid}`);
111
+ } catch (error) {
112
+ const wasCancelled = cancelled;
113
+ await cleanup();
114
+ if (wasCancelled) {
115
+ await stopBot();
116
+ return;
117
+ }
118
+ const raw = error instanceof Error ? error.message : String(error);
119
+ throw new Error(
120
+ `QQ WebSocket 连接失败:请检查 appid/secret 是否配对、网关地址(gatewayUrl/accessTokenUrl)是否可达(原始错误:${raw})`,
121
+ { cause: error },
122
+ );
123
+ }
124
+ });
101
125
  }
102
126
 
103
127
  open(): void {
@@ -110,16 +134,10 @@ export class QqWebsocketEndpoint extends Endpoint<QqBotTransport> {
110
134
 
111
135
  async stop(): Promise<void> {
112
136
  this.#open = false;
113
- if (this.#bot) {
114
- try {
115
- this.#bot.removeAllListeners();
116
- await this.#bot.stop();
117
- } catch {
118
- /* ignore */
119
- }
120
- this.#bot = null;
121
- }
122
- this.#started = false;
137
+ const cleanup = this.#cleanup;
138
+ this.#cleanup = undefined;
139
+ await this.#lifecycle.stop();
140
+ await cleanup?.();
123
141
  this.#logger.debug(formatCompact({ op: 'disconnect' }));
124
142
  }
125
143
 
@@ -217,11 +235,13 @@ export class QqWebsocketEndpoint extends Endpoint<QqBotTransport> {
217
235
 
218
236
  #bindBot(bot: QqBotTransport): void {
219
237
  bindQqBotInboundEvents(bot, (raw) => {
238
+ if (this.#bot !== bot) return;
220
239
  this.#emitPlatformEvent('message', raw);
221
240
  const msg = normalizeQqMessage(raw);
222
241
  if (msg) this.admit(msg);
223
242
  });
224
243
  bindQqBotSideEvents(bot, (eventName, raw) => {
244
+ if (this.#bot !== bot) return;
225
245
  this.#emitPlatformEvent(eventName, raw);
226
246
  receiveQqSideEvent(
227
247
  (name, payload) => this.emit(name, payload),
@@ -264,15 +284,16 @@ export class QqHttpEndpoint extends Endpoint<QqHttpBotTransport> {
264
284
  readonly #options: QqHttpEndpointOptions;
265
285
  readonly #createBot: CreateQqHttpBot;
266
286
  #bot: QqHttpBotTransport | null = null;
267
- #routeReleases: ReturnType<typeof registerQqWebhookRoutes> = [];
268
287
  #open = false;
269
- #started = false;
288
+ readonly #lifecycle: EndpointLifecycle;
289
+ #cleanup?: () => Promise<void>;
270
290
  readonly management: EndpointManagement = createQqEndpointManagement(() => this.#requireBot());
271
291
  readonly control: EndpointControl = createRecallEndpointControl((id) => this.recallMessage(id));
272
292
 
273
293
  constructor(options: QqHttpEndpointOptions) {
274
294
  super();
275
295
  this.#logger = getAdapterLogger('qq', options.config.id);
296
+ this.#lifecycle = createEndpointLifecycle({ name: options.config.id, reconnect: false });
276
297
  this.#options = options;
277
298
  this.#createBot = options.createBot ?? defaultCreateHttpBot;
278
299
  }
@@ -287,21 +308,48 @@ export class QqHttpEndpoint extends Endpoint<QqHttpBotTransport> {
287
308
  }
288
309
 
289
310
  async start(): Promise<void> {
290
- if (this.#started) return;
291
- this.#started = true;
292
- try {
293
- this.#setupRoutes();
294
- this.#bot = this.#createBot(this.#options.config);
295
- this.#bindBot(this.#bot);
296
- await this.#bot.start();
297
- this.#logger.info(
298
- `connected (${this.#options.config.mode}) | path: ${this.#options.config.webhookPath}`,
299
- );
300
- } catch (error) {
301
- await this.stop();
302
- this.#logger.error('Failed to connect QQ HTTP receiver:', error);
303
- throw error;
304
- }
311
+ await this.#lifecycle.start(async (handle) => {
312
+ const bot = this.#createBot(this.#options.config);
313
+ this.#bot = bot;
314
+ let cancelled = false;
315
+ let stopping: Promise<void> | undefined;
316
+ const routes: Array<() => void> = [];
317
+ const stopBot = async () => {
318
+ try { bot.removeAllListeners(); } catch { /* continue SDK cleanup */ }
319
+ try { await bot.stop(); } catch { /* best-effort SDK cleanup */ }
320
+ };
321
+ const cleanup = () => {
322
+ cancelled = true;
323
+ if (this.#bot === bot) this.#bot = null;
324
+ for (const release of routes.splice(0)) release();
325
+ return stopping ??= stopBot();
326
+ };
327
+ this.#cleanup = cleanup;
328
+ handle.onForceClose(() => { void cleanup(); });
329
+ try {
330
+ this.#bindBot(bot);
331
+ routes.push(...registerQqWebhookRoutes(this.#options.http, {
332
+ config: this.#options.config,
333
+ getBot: () => cancelled ? null : bot,
334
+ }));
335
+ await bot.start();
336
+ if (cancelled) {
337
+ // SDK startup may create resources after stop; close this exact old client again.
338
+ await stopping;
339
+ await stopBot();
340
+ return;
341
+ }
342
+ this.#logger.info(`connected (${this.#options.config.mode}) | path: ${this.#options.config.webhookPath}`);
343
+ } catch (error) {
344
+ const wasCancelled = cancelled;
345
+ await cleanup();
346
+ if (wasCancelled) {
347
+ await stopBot();
348
+ return;
349
+ }
350
+ throw error;
351
+ }
352
+ });
305
353
  }
306
354
 
307
355
  open(): void {
@@ -314,17 +362,10 @@ export class QqHttpEndpoint extends Endpoint<QqHttpBotTransport> {
314
362
 
315
363
  async stop(): Promise<void> {
316
364
  this.#open = false;
317
- for (const release of this.#routeReleases.splice(0)) release();
318
- if (this.#bot) {
319
- try {
320
- this.#bot.removeAllListeners();
321
- await this.#bot.stop();
322
- } catch {
323
- /* ignore */
324
- }
325
- this.#bot = null;
326
- }
327
- this.#started = false;
365
+ const cleanup = this.#cleanup;
366
+ this.#cleanup = undefined;
367
+ await this.#lifecycle.stop();
368
+ await cleanup?.();
328
369
  this.#logger.debug(formatCompact({ op: 'disconnect' }));
329
370
  }
330
371
 
@@ -419,20 +460,15 @@ export class QqHttpEndpoint extends Endpoint<QqHttpBotTransport> {
419
460
  });
420
461
  }
421
462
 
422
- #setupRoutes(): void {
423
- this.#routeReleases.push(...registerQqWebhookRoutes(this.#options.http, {
424
- config: this.#options.config,
425
- getBot: () => this.#bot,
426
- }));
427
- }
428
-
429
463
  #bindBot(bot: QqBotTransport): void {
430
464
  bindQqBotInboundEvents(bot, (raw) => {
465
+ if (this.#bot !== bot) return;
431
466
  this.#emitPlatformEvent('message', raw);
432
467
  const msg = normalizeQqMessage(raw);
433
468
  if (msg) this.admit(msg);
434
469
  });
435
470
  bindQqBotSideEvents(bot, (eventName, raw) => {
471
+ if (this.#bot !== bot) return;
436
472
  this.#emitPlatformEvent(eventName, raw);
437
473
  receiveQqSideEvent(
438
474
  (name, payload) => this.emit(name, payload),