@zhin.js/adapter-qq 2.0.12 → 3.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.
Files changed (45) hide show
  1. package/CHANGELOG.md +20 -4
  2. package/README.md +9 -9
  3. package/client/Dashboard.tsx +32 -32
  4. package/dist/index.js +1 -1
  5. package/lib/adapter.d.ts +10 -9
  6. package/lib/adapter.d.ts.map +1 -1
  7. package/lib/adapter.js +33 -32
  8. package/lib/adapter.js.map +1 -1
  9. package/lib/{bot.d.ts → endpoint.d.ts} +13 -8
  10. package/lib/endpoint.d.ts.map +1 -0
  11. package/lib/{bot.js → endpoint.js} +101 -35
  12. package/lib/endpoint.js.map +1 -0
  13. package/lib/gateway-config.d.ts +3 -3
  14. package/lib/gateway-config.d.ts.map +1 -1
  15. package/lib/gateway-config.js +2 -2
  16. package/lib/gateway-config.js.map +1 -1
  17. package/lib/group-at-normalize.d.ts +1 -1
  18. package/lib/group-at-normalize.d.ts.map +1 -1
  19. package/lib/group-at-normalize.js +8 -8
  20. package/lib/group-at-normalize.js.map +1 -1
  21. package/lib/index.d.ts +1 -1
  22. package/lib/index.d.ts.map +1 -1
  23. package/lib/index.js +124 -96
  24. package/lib/index.js.map +1 -1
  25. package/lib/platform-permit.d.ts +17 -0
  26. package/lib/platform-permit.d.ts.map +1 -0
  27. package/lib/platform-permit.js +47 -0
  28. package/lib/platform-permit.js.map +1 -0
  29. package/lib/sdk-version.d.ts +1 -1
  30. package/lib/sdk-version.js +1 -1
  31. package/lib/types.d.ts +4 -4
  32. package/lib/types.d.ts.map +1 -1
  33. package/package.json +13 -10
  34. package/skills/qq/PERMITS.md +25 -0
  35. package/skills/qq/SKILL.md +1 -1
  36. package/src/adapter.ts +27 -25
  37. package/src/{bot.ts → endpoint.ts} +116 -42
  38. package/src/gateway-config.ts +5 -5
  39. package/src/group-at-normalize.ts +8 -8
  40. package/src/index.ts +118 -85
  41. package/src/platform-permit.ts +62 -0
  42. package/src/sdk-version.ts +1 -1
  43. package/src/types.ts +5 -5
  44. package/lib/bot.d.ts.map +0 -1
  45. package/lib/bot.js.map +0 -1
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@zhin.js/adapter-qq",
3
- "version": "2.0.12",
3
+ "version": "3.0.1",
4
4
  "description": "Zhin.js adapter for QQ Official Bot",
5
5
  "type": "module",
6
6
  "main": "./lib/index.js",
@@ -41,18 +41,21 @@
41
41
  "@types/react-dom": "^19.2.3",
42
42
  "lucide-react": "^1.17.0",
43
43
  "typescript": "^6.0.3",
44
- "@zhin.js/cli": "1.0.88",
45
- "@zhin.js/host-api": "0.0.4",
46
- "zhin.js": "1.0.93",
47
- "@zhin.js/contract": "1.0.1"
44
+ "@zhin.js/cli": "1.0.89",
45
+ "@zhin.js/client": "2.0.3",
46
+ "@zhin.js/contract": "1.0.1",
47
+ "@zhin.js/host-api": "1.0.1",
48
+ "@zhin.js/host-router": "1.0.1",
49
+ "@zhin.js/logger": "0.1.71",
50
+ "zhin.js": "2.0.1"
48
51
  },
49
52
  "peerDependencies": {
50
- "@zhin.js/client": "2.0.2",
51
- "@zhin.js/host-api": "0.0.4",
52
- "@zhin.js/host-router": "0.0.3",
53
+ "@zhin.js/client": "2.0.3",
54
+ "@zhin.js/host-api": "1.0.1",
53
55
  "@zhin.js/contract": "1.0.1",
54
- "@zhin.js/logger": "0.1.70",
55
- "zhin.js": "1.0.93"
56
+ "@zhin.js/host-router": "1.0.1",
57
+ "@zhin.js/logger": "0.1.71",
58
+ "zhin.js": "2.0.1"
56
59
  },
57
60
  "peerDependenciesMeta": {
58
61
  "@zhin.js/client": {
@@ -0,0 +1,25 @@
1
+ # QQ 官方频道 Platform Permits
2
+
3
+ > 与 QQ 群 `group_*` **分开命名**,仅用于 guild/channel 场景。
4
+
5
+ ## 入站字段
6
+
7
+ `message_type === guild` 时 `getGuildMember` + `getGuildInfo`(TTL 60s):
8
+
9
+ | 字段 | 取值 |
10
+ |------|------|
11
+ | `$sender.role` | `owner` · `admin` · `member` |
12
+ | `$sender.permissions` | `guild_owner` · `guild_admin` · `manage_roles` · `manage_channels` · 角色 ID |
13
+
14
+ ## Permit 词汇表
15
+
16
+ | `platform(qq,…)` | 含义 |
17
+ |------------------|------|
18
+ | `guild_owner` | 频道主 |
19
+ | `guild_admin` | 频道管理员 |
20
+ | `manage_roles` | 管理身份组 |
21
+ | `manage_channels` | 管理子频道 |
22
+
23
+ ## 工厂映射
24
+
25
+ `group_admin` → `guild_admin` · `group_owner` → `guild_owner`
@@ -5,7 +5,7 @@ platforms:
5
5
  description: >-
6
6
  QQ 官方机器人管理能力。当用户在 QQ 官方机器人(非 OneBot/NapCat)中请求群管(踢人、
7
7
  禁言、全员禁言)、频道/子频道管理、角色管理(创建/添加/移除角色)、
8
- 或查询成员详情时使用。即使用户没有明确提到 QQ 官方机器人,只要 Bot 平台是 qq
8
+ 或查询成员详情时使用。即使用户没有明确提到 QQ 官方机器人,只要 Endpoint 平台是 qq
9
9
  (官方 API)且涉及上述操作,就应触发。仅有昵称时先 list_members 查 user_id。
10
10
  keywords:
11
11
  - qq
package/src/adapter.ts CHANGED
@@ -6,10 +6,12 @@ import {
6
6
  Plugin,
7
7
  } from "zhin.js";
8
8
  import type { Router } from "@zhin.js/host-router";
9
- import { QQBot } from "./bot.js";
10
- import type { QQBotConfig, ReceiverMode } from "./types.js";
9
+ import { QQEndpoint } from "./endpoint.js";
10
+ import type { QQEndpointConfig, ReceiverMode } from "./types.js";
11
+
12
+ export class QQAdapter extends Adapter<QQEndpoint<ReceiverMode>> {
13
+ static override readonly capabilities = ['inbound', 'outbound'] as const;
11
14
 
12
- export class QQAdapter extends Adapter<QQBot<ReceiverMode>> {
13
15
  #router?: Router;
14
16
 
15
17
  constructor(plugin: Plugin, router?: Router) {
@@ -21,40 +23,40 @@ export class QQAdapter extends Adapter<QQBot<ReceiverMode>> {
21
23
  return this.#router;
22
24
  }
23
25
 
24
- createBot(config: QQBotConfig<ReceiverMode>): QQBot<ReceiverMode> {
25
- return new QQBot(this, config);
26
+ createEndpoint(config: QQEndpointConfig<ReceiverMode>): QQEndpoint<ReceiverMode> {
27
+ return new QQEndpoint(this, config);
26
28
  }
27
29
 
28
30
  // ── IGroupManagement 标准群管方法 ──────────────────────────────────
29
31
 
30
- async kickMember(botId: string, sceneId: string, userId: string) {
31
- const bot = this.bots.get(botId);
32
- if (!bot) throw new Error(`Bot ${botId} 不存在`);
33
- return bot.removeGuildMember(sceneId, userId, false);
32
+ async kickMember(endpointId: string, sceneId: string, userId: string) {
33
+ const endpoint = this.endpoints.get(endpointId);
34
+ if (!endpoint) throw new Error(`Endpoint ${endpointId} 不存在`);
35
+ return endpoint.removeGuildMember(sceneId, userId, false);
34
36
  }
35
37
 
36
- async muteMember(botId: string, sceneId: string, userId: string, duration = 600) {
37
- const bot = this.bots.get(botId);
38
- if (!bot) throw new Error(`Bot ${botId} 不存在`);
39
- return bot.muteMembers(sceneId, [userId], duration);
38
+ async muteMember(endpointId: string, sceneId: string, userId: string, duration = 600) {
39
+ const endpoint = this.endpoints.get(endpointId);
40
+ if (!endpoint) throw new Error(`Endpoint ${endpointId} 不存在`);
41
+ return endpoint.muteMembers(sceneId, [userId], duration);
40
42
  }
41
43
 
42
- async muteAll(botId: string, sceneId: string, enable = true) {
43
- const bot = this.bots.get(botId);
44
- if (!bot) throw new Error(`Bot ${botId} 不存在`);
45
- return bot.muteAll(sceneId, enable ? 600 : 0);
44
+ async muteAll(endpointId: string, sceneId: string, enable = true) {
45
+ const endpoint = this.endpoints.get(endpointId);
46
+ if (!endpoint) throw new Error(`Endpoint ${endpointId} 不存在`);
47
+ return endpoint.muteAll(sceneId, enable ? 600 : 0);
46
48
  }
47
49
 
48
- async listMembers(botId: string, sceneId: string) {
49
- const bot = this.bots.get(botId);
50
- if (!bot) throw new Error(`Bot ${botId} 不存在`);
51
- return bot.getGuildMembers(sceneId);
50
+ async listMembers(endpointId: string, sceneId: string) {
51
+ const endpoint = this.endpoints.get(endpointId);
52
+ if (!endpoint) throw new Error(`Endpoint ${endpointId} 不存在`);
53
+ return endpoint.getGuildMembers(sceneId);
52
54
  }
53
55
 
54
- async getGroupInfo(botId: string, sceneId: string) {
55
- const bot = this.bots.get(botId);
56
- if (!bot) throw new Error(`Bot ${botId} 不存在`);
57
- return bot.getGuildInfo(sceneId);
56
+ async getGroupInfo(endpointId: string, sceneId: string) {
57
+ const endpoint = this.endpoints.get(endpointId);
58
+ if (!endpoint) throw new Error(`Endpoint ${endpointId} 不存在`);
59
+ return endpoint.getGuildInfo(sceneId);
58
60
  }
59
61
 
60
62
  // ── 生命周期 ───────────────────────────────────────────────────────
@@ -1,8 +1,8 @@
1
1
  /**
2
- * QQ 官方 Bot 实现
2
+ * QQ 官方 Endpoint 实现
3
3
  */
4
4
  import {
5
- Bot,
5
+ Bot as QQOfficialClient,
6
6
  PrivateMessageEvent,
7
7
  GroupMessageEvent,
8
8
  } from "qq-official-bot";
@@ -10,14 +10,14 @@ import path from "path";
10
10
  import { formatCompact } from "@zhin.js/logger";
11
11
  import { registerFetchRoute, type RouterContext } from "@zhin.js/host-router/router";
12
12
  import {
13
- Bot as ZhinBot,
13
+ Endpoint,
14
14
  Message,
15
15
  SendOptions,
16
16
  SendContent,
17
17
  segment,
18
18
  } from "zhin.js";
19
19
  import type { MessageElement } from "zhin.js";
20
- import { ReceiverMode, type QQBotConfig, type ApplicationPlatform } from "./types.js";
20
+ import { ReceiverMode, type QQEndpointConfig, type ApplicationPlatform } from "./types.js";
21
21
  import type { QQAdapter } from "./adapter.js";
22
22
  import { normalizeQqInboundWsPayload, type QqWsPacket } from "./inbound-normalize.js";
23
23
  import { normalizeGroupAtPrefix } from "./group-at-normalize.js";
@@ -25,14 +25,37 @@ import { SDK_VERSION, SDK_VERSION_HEADER } from "./sdk-version.js";
25
25
  import { applyCustomAuthEndpoints } from "./gateway-config.js";
26
26
  import { normalizeOutboundMarkdown } from "./outbound-markdown.js";
27
27
  import { normalizeOutboundMedia } from "./outbound-media.js";
28
+ import { normalizeQqGuildSenderForPermit } from "./platform-permit.js";
28
29
 
30
+ /** 从 qq-official-bot SendResult / 审核回包中解析出站消息 ID */
31
+ export function resolveOutboundMessageId(result: unknown): string {
32
+ if (!result || typeof result !== "object") {
33
+ throw new Error("QQ 发送消息失败:响应为空");
34
+ }
35
+ const row = result as Record<string, unknown>;
36
+ const nested = row.data && typeof row.data === "object"
37
+ ? row.data as Record<string, unknown>
38
+ : undefined;
39
+ const audit = (row.message_audit ?? nested?.message_audit) as Record<string, unknown> | undefined;
40
+ const id = row.id ?? row.message_id ?? audit?.audit_id;
41
+ if (id == null || id === "") {
42
+ const code = row.code;
43
+ const msg = row.message;
44
+ throw new Error(
45
+ code != null
46
+ ? `QQ 发送消息失败(${String(code)})${msg ? `: ${String(msg)}` : ""}`
47
+ : "QQ 发送消息失败:响应缺少消息 ID",
48
+ );
49
+ }
50
+ return String(id);
51
+ }
29
52
 
30
- export class QQBot<T extends ReceiverMode, M extends ApplicationPlatform = ApplicationPlatform>
31
- extends Bot
32
- implements ZhinBot<QQBotConfig<T, M>, PrivateMessageEvent | GroupMessageEvent>
53
+ export class QQEndpoint<T extends ReceiverMode, M extends ApplicationPlatform = ApplicationPlatform>
54
+ extends QQOfficialClient
55
+ implements Endpoint<QQEndpointConfig<T, M>, PrivateMessageEvent | GroupMessageEvent>
33
56
  {
34
57
  $connected: boolean = false;
35
- declare $config: QQBotConfig<T, M>;
58
+ declare $config: QQEndpointConfig<T, M>;
36
59
  /** 平台侧机器人 user_id,用于 @ 触发匹配 */
37
60
  $platformUserId?: string;
38
61
 
@@ -44,10 +67,10 @@ export class QQBot<T extends ReceiverMode, M extends ApplicationPlatform = Appli
44
67
  return this.$config.name;
45
68
  }
46
69
 
47
- constructor(public adapter: QQAdapter, config: QQBotConfig<T, M>) {
70
+ constructor(public adapter: QQAdapter, config: QQEndpointConfig<T, M>) {
48
71
  if (!config.data_dir) config.data_dir = path.join(process.cwd(), "data");
49
72
  if (config.mode === ReceiverMode.MIDDLEWARE) {
50
- const mw = config as QQBotConfig<ReceiverMode.MIDDLEWARE, M> & {
73
+ const mw = config as QQEndpointConfig<ReceiverMode.MIDDLEWARE, M> & {
51
74
  platform?: ApplicationPlatform;
52
75
  };
53
76
  if (!mw.platform) {
@@ -84,9 +107,60 @@ export class QQBot<T extends ReceiverMode, M extends ApplicationPlatform = Appli
84
107
  super.dispatchEvent(event, wsRes);
85
108
  }
86
109
 
87
- private handleQQMessage(msg: PrivateMessageEvent | GroupMessageEvent): void {
110
+ private guildMemberCache = new Map<string, { at: number; role?: string; permissions: string[] }>();
111
+ private guildOwnerCache = new Map<string, { at: number; ownerId?: string }>();
112
+
113
+ private async enrichGuildSender(
114
+ message: ReturnType<QQEndpoint<ReceiverMode>["$formatMessage"]>,
115
+ guildId: string,
116
+ userId: string,
117
+ ): Promise<void> {
118
+ const key = `${guildId}:${userId}`;
119
+ const now = Date.now();
120
+ const cached = this.guildMemberCache.get(key);
121
+ if (cached && now - cached.at < 60_000) {
122
+ message.$sender.role = cached.role;
123
+ message.$sender.permissions = cached.permissions;
124
+ return;
125
+ }
126
+ try {
127
+ let ownerId: string | undefined;
128
+ const ownerCached = this.guildOwnerCache.get(guildId);
129
+ if (ownerCached && now - ownerCached.at < 300_000) {
130
+ ownerId = ownerCached.ownerId;
131
+ } else {
132
+ const guild = await this.getGuildInfo(guildId);
133
+ ownerId = guild?.owner_id?.toString?.() ?? guild?.owner?.id?.toString?.();
134
+ this.guildOwnerCache.set(guildId, { at: now, ownerId });
135
+ }
136
+ const member = await this.getGuildMember(guildId, userId);
137
+ const roleIds = Array.isArray(member?.roles)
138
+ ? member.roles.map((r: unknown) =>
139
+ typeof r === "string" ? r : (r as { id?: string })?.id ?? String(r),
140
+ )
141
+ : [];
142
+ const isOwner = ownerId === userId;
143
+ const isAdmin = !isOwner && roleIds.length > 0;
144
+ const normalized = normalizeQqGuildSenderForPermit({ roles: roleIds, isOwner, isAdmin });
145
+ const entry = {
146
+ at: now,
147
+ role: normalized.role,
148
+ permissions: normalized.permissions ?? [],
149
+ };
150
+ this.guildMemberCache.set(key, entry);
151
+ message.$sender.role = entry.role;
152
+ message.$sender.permissions = entry.permissions;
153
+ } catch {
154
+ // 保守拒绝
155
+ }
156
+ }
157
+
158
+ private async handleQQMessage(msg: PrivateMessageEvent | GroupMessageEvent): Promise<void> {
88
159
  try {
89
160
  const message = this.$formatMessage(msg);
161
+ if (msg.message_type === "guild" && msg.guild_id && message.$sender.id) {
162
+ await this.enrichGuildSender(message, String(msg.guild_id), message.$sender.id);
163
+ }
90
164
  this.adapter.emit("message.receive", message);
91
165
  this.pluginLogger.debug(
92
166
  `${this.$config.name} recv ${message.$channel.type}(${message.$channel.id}):${segment.raw(message.$content)}`,
@@ -128,7 +202,7 @@ export class QQBot<T extends ReceiverMode, M extends ApplicationPlatform = Appli
128
202
  }
129
203
 
130
204
  async $disconnect(): Promise<void> {
131
- // Bot 继承自 EventEmitter,清除 $connect() 注册的所有监听器
205
+ // Endpoint 继承自 EventEmitter,清除 $connect() 注册的所有监听器
132
206
  (this as unknown as import('node:events').EventEmitter).removeAllListeners();
133
207
  await this.stop();
134
208
  this.$connected = false;
@@ -168,7 +242,7 @@ export class QQBot<T extends ReceiverMode, M extends ApplicationPlatform = Appli
168
242
  return;
169
243
  }
170
244
  if (mode === ReceiverMode.WEBHOOK) {
171
- const cfg = this.$config as QQBotConfig<ReceiverMode.WEBHOOK>;
245
+ const cfg = this.$config as QQEndpointConfig<ReceiverMode.WEBHOOK>;
172
246
  this.pluginLogger.info(formatCompact({
173
247
  op: "webhook",
174
248
  mode: "standalone",
@@ -203,16 +277,16 @@ export class QQBot<T extends ReceiverMode, M extends ApplicationPlatform = Appli
203
277
 
204
278
  let content = msg.message;
205
279
  if (msg.message_type === "group" && Array.isArray(content)) {
206
- const botAtIds = [this.$platformUserId, this.$config.appid]
280
+ const endpointAtIds = [this.$platformUserId, this.$config.appid]
207
281
  .filter((id): id is string => Boolean(id))
208
282
  .map(String);
209
- content = normalizeGroupAtPrefix(content, botAtIds, raw.__zhin_group_at === true);
283
+ content = normalizeGroupAtPrefix(content, endpointAtIds, raw.__zhin_group_at === true);
210
284
  }
211
285
 
212
286
  const result = Message.from(msg, {
213
287
  $id: msg.message_id?.toString(),
214
288
  $adapter: "qq" as const,
215
- $bot: this.$config.name,
289
+ $endpoint: this.$config.name,
216
290
  $sender: {
217
291
  id: msg.sender.user_id?.toString(),
218
292
  name: msg.sender.user_name?.toString(),
@@ -233,7 +307,7 @@ export class QQBot<T extends ReceiverMode, M extends ApplicationPlatform = Appli
233
307
  return await this.adapter.sendMessage({
234
308
  ...result.$channel,
235
309
  context: "qq",
236
- bot: this.$config.name,
310
+ endpoint: this.$config.name,
237
311
  content,
238
312
  });
239
313
  },
@@ -252,22 +326,22 @@ export class QQBot<T extends ReceiverMode, M extends ApplicationPlatform = Appli
252
326
  const id = options.id.replace("direct:", "");
253
327
  const result = await this.sendDirectMessage(id, content);
254
328
  this.pluginLogger.debug(`${this.$config.name} send ${options.type}(${options.id}):${segment.raw(content)}`);
255
- return `direct-${options.id}:${result.id.toString()}`;
329
+ return `direct-${options.id}:${resolveOutboundMessageId(result)}`;
256
330
  } else {
257
331
  const result = await this.sendPrivateMessage(options.id, content);
258
332
  this.pluginLogger.debug(`${this.$config.name} send ${options.type}(${options.id}):${segment.raw(content)}`);
259
- return `private-${options.id}:${result.id.toString()}`;
333
+ return `private-${options.id}:${resolveOutboundMessageId(result)}`;
260
334
  }
261
335
  }
262
336
  case "group": {
263
337
  const result = await this.sendGroupMessage(options.id, content);
264
338
  this.pluginLogger.debug(`${this.$config.name} send ${options.type}(${options.id}):${segment.raw(content)}`);
265
- return `group-${options.id}:${result.id.toString()}`;
339
+ return `group-${options.id}:${resolveOutboundMessageId(result)}`;
266
340
  }
267
341
  case "channel": {
268
342
  const result = await this.sendGuildMessage(options.id, content);
269
343
  this.pluginLogger.debug(`${this.$config.name} send ${options.type}(${options.id}):${segment.raw(content)}`);
270
- return `channel-${options.id}:${result.id.toString()}`;
344
+ return `channel-${options.id}:${resolveOutboundMessageId(result)}`;
271
345
  }
272
346
  default:
273
347
  throw new Error(`unsupported channel type ${options.type}`);
@@ -294,7 +368,7 @@ export class QQBot<T extends ReceiverMode, M extends ApplicationPlatform = Appli
294
368
  try {
295
369
  return await this.guildService.getList();
296
370
  } catch (error) {
297
- this.pluginLogger.error(`QQ Bot ${this.$id} 获取频道列表失败:`, error);
371
+ this.pluginLogger.error(`QQ Endpoint ${this.$id} 获取频道列表失败:`, error);
298
372
  throw error;
299
373
  }
300
374
  }
@@ -307,7 +381,7 @@ export class QQBot<T extends ReceiverMode, M extends ApplicationPlatform = Appli
307
381
  try {
308
382
  return await this.guildService.getInfo(guildId);
309
383
  } catch (error) {
310
- this.pluginLogger.error(`QQ Bot ${this.$id} 获取频道详情失败:`, error);
384
+ this.pluginLogger.error(`QQ Endpoint ${this.$id} 获取频道详情失败:`, error);
311
385
  throw error;
312
386
  }
313
387
  }
@@ -320,7 +394,7 @@ export class QQBot<T extends ReceiverMode, M extends ApplicationPlatform = Appli
320
394
  try {
321
395
  return await this.channelService.getList(guildId);
322
396
  } catch (error) {
323
- this.pluginLogger.error(`QQ Bot ${this.$id} 获取子频道列表失败:`, error);
397
+ this.pluginLogger.error(`QQ Endpoint ${this.$id} 获取子频道列表失败:`, error);
324
398
  throw error;
325
399
  }
326
400
  }
@@ -333,7 +407,7 @@ export class QQBot<T extends ReceiverMode, M extends ApplicationPlatform = Appli
333
407
  try {
334
408
  return await this.channelService.getInfo(channelId);
335
409
  } catch (error) {
336
- this.pluginLogger.error(`QQ Bot ${this.$id} 获取子频道详情失败:`, error);
410
+ this.pluginLogger.error(`QQ Endpoint ${this.$id} 获取子频道详情失败:`, error);
337
411
  throw error;
338
412
  }
339
413
  }
@@ -346,7 +420,7 @@ export class QQBot<T extends ReceiverMode, M extends ApplicationPlatform = Appli
346
420
  try {
347
421
  return await this.memberService.getGuildMemberList(guildId);
348
422
  } catch (error) {
349
- this.pluginLogger.error(`QQ Bot ${this.$id} 获取频道成员列表失败:`, error);
423
+ this.pluginLogger.error(`QQ Endpoint ${this.$id} 获取频道成员列表失败:`, error);
350
424
  throw error;
351
425
  }
352
426
  }
@@ -360,7 +434,7 @@ export class QQBot<T extends ReceiverMode, M extends ApplicationPlatform = Appli
360
434
  try {
361
435
  return await this.memberService.getGuildMemberInfo(guildId, userId);
362
436
  } catch (error) {
363
- this.pluginLogger.error(`QQ Bot ${this.$id} 获取成员详情失败:`, error);
437
+ this.pluginLogger.error(`QQ Endpoint ${this.$id} 获取成员详情失败:`, error);
364
438
  throw error;
365
439
  }
366
440
  }
@@ -375,10 +449,10 @@ export class QQBot<T extends ReceiverMode, M extends ApplicationPlatform = Appli
375
449
  async removeGuildMember(guildId: string, userId: string, addBlacklist?: boolean, deleteHistoryMsg?: -1 | 0 | 3 | 7 | 15 | 30): Promise<boolean> {
376
450
  try {
377
451
  await this.memberService.kickMember(guildId, userId, deleteHistoryMsg, addBlacklist);
378
- this.pluginLogger.info(`QQ Bot ${this.$id} 踢出成员 ${userId}(频道 ${guildId})`);
452
+ this.pluginLogger.info(`QQ Endpoint ${this.$id} 踢出成员 ${userId}(频道 ${guildId})`);
379
453
  return true;
380
454
  } catch (error) {
381
- this.pluginLogger.error(`QQ Bot ${this.$id} 踢出成员失败:`, error);
455
+ this.pluginLogger.error(`QQ Endpoint ${this.$id} 踢出成员失败:`, error);
382
456
  throw error;
383
457
  }
384
458
  }
@@ -391,7 +465,7 @@ export class QQBot<T extends ReceiverMode, M extends ApplicationPlatform = Appli
391
465
  try {
392
466
  return await this.guildService.getRoles(guildId);
393
467
  } catch (error) {
394
- this.pluginLogger.error(`QQ Bot ${this.$id} 获取角色列表失败:`, error);
468
+ this.pluginLogger.error(`QQ Endpoint ${this.$id} 获取角色列表失败:`, error);
395
469
  throw error;
396
470
  }
397
471
  }
@@ -406,10 +480,10 @@ export class QQBot<T extends ReceiverMode, M extends ApplicationPlatform = Appli
406
480
  async createGuildRole(guildId: string, name: string, color?: number, hoist?: 0 | 1): Promise<any> {
407
481
  try {
408
482
  const result = await this.guildService.createRole(guildId, { name, color: color || 0, hoist: hoist ?? 0 });
409
- this.pluginLogger.info(`QQ Bot ${this.$id} 创建角色 "${name}"(频道 ${guildId})`);
483
+ this.pluginLogger.info(`QQ Endpoint ${this.$id} 创建角色 "${name}"(频道 ${guildId})`);
410
484
  return result;
411
485
  } catch (error) {
412
- this.pluginLogger.error(`QQ Bot ${this.$id} 创建角色失败:`, error);
486
+ this.pluginLogger.error(`QQ Endpoint ${this.$id} 创建角色失败:`, error);
413
487
  throw error;
414
488
  }
415
489
  }
@@ -424,10 +498,10 @@ export class QQBot<T extends ReceiverMode, M extends ApplicationPlatform = Appli
424
498
  async addMemberRole(guildId: string, channelId: string, userId: string, roleId: string): Promise<boolean> {
425
499
  try {
426
500
  await this.memberService.addMemberRole(guildId, channelId, userId, roleId);
427
- this.pluginLogger.info(`QQ Bot ${this.$id} 给成员 ${userId} 添加角色 ${roleId}(频道 ${guildId})`);
501
+ this.pluginLogger.info(`QQ Endpoint ${this.$id} 给成员 ${userId} 添加角色 ${roleId}(频道 ${guildId})`);
428
502
  return true;
429
503
  } catch (error) {
430
- this.pluginLogger.error(`QQ Bot ${this.$id} 添加角色失败:`, error);
504
+ this.pluginLogger.error(`QQ Endpoint ${this.$id} 添加角色失败:`, error);
431
505
  throw error;
432
506
  }
433
507
  }
@@ -442,10 +516,10 @@ export class QQBot<T extends ReceiverMode, M extends ApplicationPlatform = Appli
442
516
  async removeMemberRole(guildId: string, channelId: string, userId: string, roleId: string): Promise<boolean> {
443
517
  try {
444
518
  await this.memberService.removeMemberRole(guildId, channelId, userId, roleId);
445
- this.pluginLogger.info(`QQ Bot ${this.$id} 移除成员 ${userId} 的角色 ${roleId}(频道 ${guildId})`);
519
+ this.pluginLogger.info(`QQ Endpoint ${this.$id} 移除成员 ${userId} 的角色 ${roleId}(频道 ${guildId})`);
446
520
  return true;
447
521
  } catch (error) {
448
- this.pluginLogger.error(`QQ Bot ${this.$id} 移除角色失败:`, error);
522
+ this.pluginLogger.error(`QQ Endpoint ${this.$id} 移除角色失败:`, error);
449
523
  throw error;
450
524
  }
451
525
  }
@@ -460,14 +534,14 @@ export class QQBot<T extends ReceiverMode, M extends ApplicationPlatform = Appli
460
534
  try {
461
535
  if (muteSeconds > 0) {
462
536
  await this.memberService.muteMembers(guildId, userIds, muteSeconds);
463
- this.pluginLogger.info(`QQ Bot ${this.$id} 禁言成员 ${userIds.join(',')} ${muteSeconds}秒(频道 ${guildId})`);
537
+ this.pluginLogger.info(`QQ Endpoint ${this.$id} 禁言成员 ${userIds.join(',')} ${muteSeconds}秒(频道 ${guildId})`);
464
538
  } else {
465
539
  await this.memberService.unmuteMembers(guildId, userIds);
466
- this.pluginLogger.info(`QQ Bot ${this.$id} 解除成员 ${userIds.join(',')} 禁言(频道 ${guildId})`);
540
+ this.pluginLogger.info(`QQ Endpoint ${this.$id} 解除成员 ${userIds.join(',')} 禁言(频道 ${guildId})`);
467
541
  }
468
542
  return true;
469
543
  } catch (error) {
470
- this.pluginLogger.error(`QQ Bot ${this.$id} 禁言操作失败:`, error);
544
+ this.pluginLogger.error(`QQ Endpoint ${this.$id} 禁言操作失败:`, error);
471
545
  throw error;
472
546
  }
473
547
  }
@@ -481,14 +555,14 @@ export class QQBot<T extends ReceiverMode, M extends ApplicationPlatform = Appli
481
555
  try {
482
556
  if (muteSeconds > 0) {
483
557
  await this.guildService.mute(guildId, muteSeconds);
484
- this.pluginLogger.info(`QQ Bot ${this.$id} 开启全员禁言(频道 ${guildId})`);
558
+ this.pluginLogger.info(`QQ Endpoint ${this.$id} 开启全员禁言(频道 ${guildId})`);
485
559
  } else {
486
560
  await this.guildService.unmute(guildId);
487
- this.pluginLogger.info(`QQ Bot ${this.$id} 关闭全员禁言(频道 ${guildId})`);
561
+ this.pluginLogger.info(`QQ Endpoint ${this.$id} 关闭全员禁言(频道 ${guildId})`);
488
562
  }
489
563
  return true;
490
564
  } catch (error) {
491
- this.pluginLogger.error(`QQ Bot ${this.$id} 全员禁言操作失败:`, error);
565
+ this.pluginLogger.error(`QQ Endpoint ${this.$id} 全员禁言操作失败:`, error);
492
566
  throw error;
493
567
  }
494
568
  }
@@ -1,8 +1,8 @@
1
1
  import type { Logger } from "@zhin.js/logger";
2
2
  import { formatCompact } from "@zhin.js/logger";
3
- import type { Bot } from "qq-official-bot";
3
+ import type { Bot as QQOfficialBot } from "qq-official-bot";
4
4
  import { ReceiverMode } from "qq-official-bot";
5
- import type { QQBotConfig } from "./types.js";
5
+ import type { QQEndpointConfig } from "./types.js";
6
6
 
7
7
  /** qq-official-bot 默认 token 接口 */
8
8
  export const DEFAULT_ACCESS_TOKEN_URL = "https://bots.qq.com/app/getAppAccessToken";
@@ -23,15 +23,15 @@ type AuthManagerSession = {
23
23
  * SDK 仅在 websocket 模式自动读取配置;middleware / webhook 需在此补丁。
24
24
  */
25
25
  export function applyCustomAuthEndpoints(
26
- bot: Bot,
27
- config: Pick<QQBotConfig<ReceiverMode>, "mode" | "accessTokenUrl" | "gatewayUrl">,
26
+ endpoint: QQOfficialBot,
27
+ config: Pick<QQEndpointConfig<ReceiverMode>, "mode" | "accessTokenUrl" | "gatewayUrl">,
28
28
  logger: Logger,
29
29
  ): void {
30
30
  const { accessTokenUrl, gatewayUrl, mode } = config;
31
31
  if (!accessTokenUrl && !gatewayUrl) return;
32
32
 
33
33
  if (mode !== ReceiverMode.WEBSOCKET) {
34
- const session = bot.sessionManager as unknown as AuthManagerSession;
34
+ const session = endpoint.sessionManager as unknown as AuthManagerSession;
35
35
  if (accessTokenUrl) session.authManager.config.accessTokenUrl = accessTokenUrl;
36
36
  if (gatewayUrl) session.authManager.config.gatewayUrl = gatewayUrl;
37
37
  }
@@ -7,17 +7,17 @@ function segmentAtId(seg: MessageElement): string {
7
7
  return raw == null ? "" : String(raw);
8
8
  }
9
9
 
10
- function textMentionsBot(text: string, botIds: string[]): boolean {
11
- for (const id of botIds) {
10
+ function textMentionsEndpoint(text: string, endpointIds: string[]): boolean {
11
+ for (const id of endpointIds) {
12
12
  const re = new RegExp(`@${id.replace(/[.*+?^${}()|[\]\\]/g, "\\$&")}`);
13
13
  if (re.test(text)) return true;
14
14
  }
15
15
  return false;
16
16
  }
17
17
 
18
- function stripInlineAtBot(text: string, botIds: string[]): string {
18
+ function stripInlineAtEndpoint(text: string, endpointIds: string[]): string {
19
19
  let result = text;
20
- for (const id of botIds) {
20
+ for (const id of endpointIds) {
21
21
  if (!id) continue;
22
22
  const re = new RegExp(`^@${id.replace(/[.*+?^${}()|[\]\\]/g, "\\$&")}`);
23
23
  result = result.replace(re, "");
@@ -32,10 +32,10 @@ function stripInlineAtBot(text: string, botIds: string[]): string {
32
32
  */
33
33
  export function normalizeGroupAtPrefix(
34
34
  content: MessageElement[],
35
- botAtIds: string[],
35
+ endpointAtIds: string[],
36
36
  forceAt: boolean,
37
37
  ): MessageElement[] {
38
- const ids = [...new Set(botAtIds.map(String).filter(Boolean))];
38
+ const ids = [...new Set(endpointAtIds.map(String).filter(Boolean))];
39
39
  if (!ids.length || !Array.isArray(content)) return content;
40
40
 
41
41
  let mentioned = forceAt;
@@ -53,8 +53,8 @@ export function normalizeGroupAtPrefix(
53
53
  }
54
54
  if (seg.type === "text" && seg.data && typeof seg.data === "object") {
55
55
  const raw = String((seg.data as { text?: string }).text ?? "");
56
- if (textMentionsBot(raw, ids)) mentioned = true;
57
- const stripped = stripInlineAtBot(raw, ids);
56
+ if (textMentionsEndpoint(raw, ids)) mentioned = true;
57
+ const stripped = stripInlineAtEndpoint(raw, ids);
58
58
  if (stripped) {
59
59
  body.push({ ...seg, data: { ...(seg.data as object), text: stripped } });
60
60
  }