@zhin.js/adapter-qq 3.0.0 → 4.0.0

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 +22 -0
  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} +11 -8
  10. package/lib/endpoint.d.ts.map +1 -0
  11. package/lib/{bot.js → endpoint.js} +77 -31
  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 +102 -92
  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 +12 -9
  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} +90 -38
  38. package/src/gateway-config.ts +5 -5
  39. package/src/group-at-normalize.ts +8 -8
  40. package/src/index.ts +96 -81
  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": "3.0.0",
3
+ "version": "4.0.0",
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/host-api": "1.0.0",
44
+ "@zhin.js/cli": "1.0.89",
45
+ "@zhin.js/client": "2.0.3",
45
46
  "@zhin.js/contract": "1.0.1",
46
- "@zhin.js/cli": "1.0.88",
47
- "zhin.js": "2.0.0"
47
+ "@zhin.js/host-router": "2.0.0",
48
+ "@zhin.js/host-api": "2.0.0",
49
+ "zhin.js": "3.0.0",
50
+ "@zhin.js/logger": "0.1.71"
48
51
  },
49
52
  "peerDependencies": {
50
- "@zhin.js/client": "2.0.2",
53
+ "@zhin.js/client": "2.0.3",
54
+ "@zhin.js/host-api": "2.0.0",
51
55
  "@zhin.js/contract": "1.0.1",
52
- "@zhin.js/logger": "0.1.70",
53
- "@zhin.js/host-router": "1.0.0",
54
- "@zhin.js/host-api": "1.0.0",
55
- "zhin.js": "2.0.0"
56
+ "@zhin.js/host-router": "2.0.0",
57
+ "@zhin.js/logger": "0.1.71",
58
+ "zhin.js": "3.0.0"
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,6 +25,7 @@ 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
 
29
30
  /** 从 qq-official-bot SendResult / 审核回包中解析出站消息 ID */
30
31
  export function resolveOutboundMessageId(result: unknown): string {
@@ -49,12 +50,12 @@ export function resolveOutboundMessageId(result: unknown): string {
49
50
  return String(id);
50
51
  }
51
52
 
52
- export class QQBot<T extends ReceiverMode, M extends ApplicationPlatform = ApplicationPlatform>
53
- extends Bot
54
- 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>
55
56
  {
56
57
  $connected: boolean = false;
57
- declare $config: QQBotConfig<T, M>;
58
+ declare $config: QQEndpointConfig<T, M>;
58
59
  /** 平台侧机器人 user_id,用于 @ 触发匹配 */
59
60
  $platformUserId?: string;
60
61
 
@@ -66,10 +67,10 @@ export class QQBot<T extends ReceiverMode, M extends ApplicationPlatform = Appli
66
67
  return this.$config.name;
67
68
  }
68
69
 
69
- constructor(public adapter: QQAdapter, config: QQBotConfig<T, M>) {
70
+ constructor(public adapter: QQAdapter, config: QQEndpointConfig<T, M>) {
70
71
  if (!config.data_dir) config.data_dir = path.join(process.cwd(), "data");
71
72
  if (config.mode === ReceiverMode.MIDDLEWARE) {
72
- const mw = config as QQBotConfig<ReceiverMode.MIDDLEWARE, M> & {
73
+ const mw = config as QQEndpointConfig<ReceiverMode.MIDDLEWARE, M> & {
73
74
  platform?: ApplicationPlatform;
74
75
  };
75
76
  if (!mw.platform) {
@@ -106,9 +107,60 @@ export class QQBot<T extends ReceiverMode, M extends ApplicationPlatform = Appli
106
107
  super.dispatchEvent(event, wsRes);
107
108
  }
108
109
 
109
- 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> {
110
159
  try {
111
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
+ }
112
164
  this.adapter.emit("message.receive", message);
113
165
  this.pluginLogger.debug(
114
166
  `${this.$config.name} recv ${message.$channel.type}(${message.$channel.id}):${segment.raw(message.$content)}`,
@@ -150,7 +202,7 @@ export class QQBot<T extends ReceiverMode, M extends ApplicationPlatform = Appli
150
202
  }
151
203
 
152
204
  async $disconnect(): Promise<void> {
153
- // Bot 继承自 EventEmitter,清除 $connect() 注册的所有监听器
205
+ // Endpoint 继承自 EventEmitter,清除 $connect() 注册的所有监听器
154
206
  (this as unknown as import('node:events').EventEmitter).removeAllListeners();
155
207
  await this.stop();
156
208
  this.$connected = false;
@@ -190,7 +242,7 @@ export class QQBot<T extends ReceiverMode, M extends ApplicationPlatform = Appli
190
242
  return;
191
243
  }
192
244
  if (mode === ReceiverMode.WEBHOOK) {
193
- const cfg = this.$config as QQBotConfig<ReceiverMode.WEBHOOK>;
245
+ const cfg = this.$config as QQEndpointConfig<ReceiverMode.WEBHOOK>;
194
246
  this.pluginLogger.info(formatCompact({
195
247
  op: "webhook",
196
248
  mode: "standalone",
@@ -225,16 +277,16 @@ export class QQBot<T extends ReceiverMode, M extends ApplicationPlatform = Appli
225
277
 
226
278
  let content = msg.message;
227
279
  if (msg.message_type === "group" && Array.isArray(content)) {
228
- const botAtIds = [this.$platformUserId, this.$config.appid]
280
+ const endpointAtIds = [this.$platformUserId, this.$config.appid]
229
281
  .filter((id): id is string => Boolean(id))
230
282
  .map(String);
231
- content = normalizeGroupAtPrefix(content, botAtIds, raw.__zhin_group_at === true);
283
+ content = normalizeGroupAtPrefix(content, endpointAtIds, raw.__zhin_group_at === true);
232
284
  }
233
285
 
234
286
  const result = Message.from(msg, {
235
287
  $id: msg.message_id?.toString(),
236
288
  $adapter: "qq" as const,
237
- $bot: this.$config.name,
289
+ $endpoint: this.$config.name,
238
290
  $sender: {
239
291
  id: msg.sender.user_id?.toString(),
240
292
  name: msg.sender.user_name?.toString(),
@@ -255,7 +307,7 @@ export class QQBot<T extends ReceiverMode, M extends ApplicationPlatform = Appli
255
307
  return await this.adapter.sendMessage({
256
308
  ...result.$channel,
257
309
  context: "qq",
258
- bot: this.$config.name,
310
+ endpoint: this.$config.name,
259
311
  content,
260
312
  });
261
313
  },
@@ -316,7 +368,7 @@ export class QQBot<T extends ReceiverMode, M extends ApplicationPlatform = Appli
316
368
  try {
317
369
  return await this.guildService.getList();
318
370
  } catch (error) {
319
- this.pluginLogger.error(`QQ Bot ${this.$id} 获取频道列表失败:`, error);
371
+ this.pluginLogger.error(`QQ Endpoint ${this.$id} 获取频道列表失败:`, error);
320
372
  throw error;
321
373
  }
322
374
  }
@@ -329,7 +381,7 @@ export class QQBot<T extends ReceiverMode, M extends ApplicationPlatform = Appli
329
381
  try {
330
382
  return await this.guildService.getInfo(guildId);
331
383
  } catch (error) {
332
- this.pluginLogger.error(`QQ Bot ${this.$id} 获取频道详情失败:`, error);
384
+ this.pluginLogger.error(`QQ Endpoint ${this.$id} 获取频道详情失败:`, error);
333
385
  throw error;
334
386
  }
335
387
  }
@@ -342,7 +394,7 @@ export class QQBot<T extends ReceiverMode, M extends ApplicationPlatform = Appli
342
394
  try {
343
395
  return await this.channelService.getList(guildId);
344
396
  } catch (error) {
345
- this.pluginLogger.error(`QQ Bot ${this.$id} 获取子频道列表失败:`, error);
397
+ this.pluginLogger.error(`QQ Endpoint ${this.$id} 获取子频道列表失败:`, error);
346
398
  throw error;
347
399
  }
348
400
  }
@@ -355,7 +407,7 @@ export class QQBot<T extends ReceiverMode, M extends ApplicationPlatform = Appli
355
407
  try {
356
408
  return await this.channelService.getInfo(channelId);
357
409
  } catch (error) {
358
- this.pluginLogger.error(`QQ Bot ${this.$id} 获取子频道详情失败:`, error);
410
+ this.pluginLogger.error(`QQ Endpoint ${this.$id} 获取子频道详情失败:`, error);
359
411
  throw error;
360
412
  }
361
413
  }
@@ -368,7 +420,7 @@ export class QQBot<T extends ReceiverMode, M extends ApplicationPlatform = Appli
368
420
  try {
369
421
  return await this.memberService.getGuildMemberList(guildId);
370
422
  } catch (error) {
371
- this.pluginLogger.error(`QQ Bot ${this.$id} 获取频道成员列表失败:`, error);
423
+ this.pluginLogger.error(`QQ Endpoint ${this.$id} 获取频道成员列表失败:`, error);
372
424
  throw error;
373
425
  }
374
426
  }
@@ -382,7 +434,7 @@ export class QQBot<T extends ReceiverMode, M extends ApplicationPlatform = Appli
382
434
  try {
383
435
  return await this.memberService.getGuildMemberInfo(guildId, userId);
384
436
  } catch (error) {
385
- this.pluginLogger.error(`QQ Bot ${this.$id} 获取成员详情失败:`, error);
437
+ this.pluginLogger.error(`QQ Endpoint ${this.$id} 获取成员详情失败:`, error);
386
438
  throw error;
387
439
  }
388
440
  }
@@ -397,10 +449,10 @@ export class QQBot<T extends ReceiverMode, M extends ApplicationPlatform = Appli
397
449
  async removeGuildMember(guildId: string, userId: string, addBlacklist?: boolean, deleteHistoryMsg?: -1 | 0 | 3 | 7 | 15 | 30): Promise<boolean> {
398
450
  try {
399
451
  await this.memberService.kickMember(guildId, userId, deleteHistoryMsg, addBlacklist);
400
- this.pluginLogger.info(`QQ Bot ${this.$id} 踢出成员 ${userId}(频道 ${guildId})`);
452
+ this.pluginLogger.info(`QQ Endpoint ${this.$id} 踢出成员 ${userId}(频道 ${guildId})`);
401
453
  return true;
402
454
  } catch (error) {
403
- this.pluginLogger.error(`QQ Bot ${this.$id} 踢出成员失败:`, error);
455
+ this.pluginLogger.error(`QQ Endpoint ${this.$id} 踢出成员失败:`, error);
404
456
  throw error;
405
457
  }
406
458
  }
@@ -413,7 +465,7 @@ export class QQBot<T extends ReceiverMode, M extends ApplicationPlatform = Appli
413
465
  try {
414
466
  return await this.guildService.getRoles(guildId);
415
467
  } catch (error) {
416
- this.pluginLogger.error(`QQ Bot ${this.$id} 获取角色列表失败:`, error);
468
+ this.pluginLogger.error(`QQ Endpoint ${this.$id} 获取角色列表失败:`, error);
417
469
  throw error;
418
470
  }
419
471
  }
@@ -428,10 +480,10 @@ export class QQBot<T extends ReceiverMode, M extends ApplicationPlatform = Appli
428
480
  async createGuildRole(guildId: string, name: string, color?: number, hoist?: 0 | 1): Promise<any> {
429
481
  try {
430
482
  const result = await this.guildService.createRole(guildId, { name, color: color || 0, hoist: hoist ?? 0 });
431
- this.pluginLogger.info(`QQ Bot ${this.$id} 创建角色 "${name}"(频道 ${guildId})`);
483
+ this.pluginLogger.info(`QQ Endpoint ${this.$id} 创建角色 "${name}"(频道 ${guildId})`);
432
484
  return result;
433
485
  } catch (error) {
434
- this.pluginLogger.error(`QQ Bot ${this.$id} 创建角色失败:`, error);
486
+ this.pluginLogger.error(`QQ Endpoint ${this.$id} 创建角色失败:`, error);
435
487
  throw error;
436
488
  }
437
489
  }
@@ -446,10 +498,10 @@ export class QQBot<T extends ReceiverMode, M extends ApplicationPlatform = Appli
446
498
  async addMemberRole(guildId: string, channelId: string, userId: string, roleId: string): Promise<boolean> {
447
499
  try {
448
500
  await this.memberService.addMemberRole(guildId, channelId, userId, roleId);
449
- this.pluginLogger.info(`QQ Bot ${this.$id} 给成员 ${userId} 添加角色 ${roleId}(频道 ${guildId})`);
501
+ this.pluginLogger.info(`QQ Endpoint ${this.$id} 给成员 ${userId} 添加角色 ${roleId}(频道 ${guildId})`);
450
502
  return true;
451
503
  } catch (error) {
452
- this.pluginLogger.error(`QQ Bot ${this.$id} 添加角色失败:`, error);
504
+ this.pluginLogger.error(`QQ Endpoint ${this.$id} 添加角色失败:`, error);
453
505
  throw error;
454
506
  }
455
507
  }
@@ -464,10 +516,10 @@ export class QQBot<T extends ReceiverMode, M extends ApplicationPlatform = Appli
464
516
  async removeMemberRole(guildId: string, channelId: string, userId: string, roleId: string): Promise<boolean> {
465
517
  try {
466
518
  await this.memberService.removeMemberRole(guildId, channelId, userId, roleId);
467
- this.pluginLogger.info(`QQ Bot ${this.$id} 移除成员 ${userId} 的角色 ${roleId}(频道 ${guildId})`);
519
+ this.pluginLogger.info(`QQ Endpoint ${this.$id} 移除成员 ${userId} 的角色 ${roleId}(频道 ${guildId})`);
468
520
  return true;
469
521
  } catch (error) {
470
- this.pluginLogger.error(`QQ Bot ${this.$id} 移除角色失败:`, error);
522
+ this.pluginLogger.error(`QQ Endpoint ${this.$id} 移除角色失败:`, error);
471
523
  throw error;
472
524
  }
473
525
  }
@@ -482,14 +534,14 @@ export class QQBot<T extends ReceiverMode, M extends ApplicationPlatform = Appli
482
534
  try {
483
535
  if (muteSeconds > 0) {
484
536
  await this.memberService.muteMembers(guildId, userIds, muteSeconds);
485
- this.pluginLogger.info(`QQ Bot ${this.$id} 禁言成员 ${userIds.join(',')} ${muteSeconds}秒(频道 ${guildId})`);
537
+ this.pluginLogger.info(`QQ Endpoint ${this.$id} 禁言成员 ${userIds.join(',')} ${muteSeconds}秒(频道 ${guildId})`);
486
538
  } else {
487
539
  await this.memberService.unmuteMembers(guildId, userIds);
488
- this.pluginLogger.info(`QQ Bot ${this.$id} 解除成员 ${userIds.join(',')} 禁言(频道 ${guildId})`);
540
+ this.pluginLogger.info(`QQ Endpoint ${this.$id} 解除成员 ${userIds.join(',')} 禁言(频道 ${guildId})`);
489
541
  }
490
542
  return true;
491
543
  } catch (error) {
492
- this.pluginLogger.error(`QQ Bot ${this.$id} 禁言操作失败:`, error);
544
+ this.pluginLogger.error(`QQ Endpoint ${this.$id} 禁言操作失败:`, error);
493
545
  throw error;
494
546
  }
495
547
  }
@@ -503,14 +555,14 @@ export class QQBot<T extends ReceiverMode, M extends ApplicationPlatform = Appli
503
555
  try {
504
556
  if (muteSeconds > 0) {
505
557
  await this.guildService.mute(guildId, muteSeconds);
506
- this.pluginLogger.info(`QQ Bot ${this.$id} 开启全员禁言(频道 ${guildId})`);
558
+ this.pluginLogger.info(`QQ Endpoint ${this.$id} 开启全员禁言(频道 ${guildId})`);
507
559
  } else {
508
560
  await this.guildService.unmute(guildId);
509
- this.pluginLogger.info(`QQ Bot ${this.$id} 关闭全员禁言(频道 ${guildId})`);
561
+ this.pluginLogger.info(`QQ Endpoint ${this.$id} 关闭全员禁言(频道 ${guildId})`);
510
562
  }
511
563
  return true;
512
564
  } catch (error) {
513
- this.pluginLogger.error(`QQ Bot ${this.$id} 全员禁言操作失败:`, error);
565
+ this.pluginLogger.error(`QQ Endpoint ${this.$id} 全员禁言操作失败:`, error);
514
566
  throw error;
515
567
  }
516
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
  }