@zhin.js/adapter-qq 1.1.16 → 1.1.18

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 (76) hide show
  1. package/CHANGELOG.md +62 -0
  2. package/README.md +56 -5
  3. package/adapters/qq/index.js +7 -2
  4. package/adapters/qq/index.ts +7 -2
  5. package/agents/qq/boundaries.md +2 -0
  6. package/agents/qq/conversation.js +92 -0
  7. package/agents/qq/conversation.ts +99 -0
  8. package/agents/qq/messages.js +60 -0
  9. package/agents/qq/messages.ts +60 -0
  10. package/agents/qq/skills/qq/SKILL.md +8 -2
  11. package/agents/qq/skills/qq/tools/guild_kick_member/index.js +30 -0
  12. package/agents/qq/skills/qq/tools/guild_kick_member/index.ts +29 -0
  13. package/agents/qq/skills/qq/tools/guild_mute_member/index.js +31 -0
  14. package/agents/qq/skills/qq/tools/guild_mute_member/index.ts +30 -0
  15. package/agents/qq/skills/qq-conversation/SKILL.md +24 -0
  16. package/agents/qq/skills/qq-conversation/tools/conversation_info/index.js +78 -0
  17. package/agents/qq/skills/qq-conversation/tools/conversation_info/index.ts +76 -0
  18. package/agents/qq/skills/qq-group-admin/SKILL.md +34 -0
  19. package/agents/qq/skills/qq-group-admin/tools/group_kick/index.js +39 -0
  20. package/agents/qq/skills/qq-group-admin/tools/group_kick/index.ts +38 -0
  21. package/agents/qq/skills/qq-group-admin/tools/handle_join_request/index.js +33 -0
  22. package/agents/qq/skills/qq-group-admin/tools/handle_join_request/index.ts +32 -0
  23. package/agents/qq/skills/qq-group-admin/tools/recall_member_message/index.js +25 -0
  24. package/agents/qq/skills/qq-group-admin/tools/recall_member_message/index.ts +25 -0
  25. package/agents/qq/skills/qq-group-admin/tools/set_mute/index.js +37 -0
  26. package/agents/qq/skills/qq-group-admin/tools/set_mute/index.ts +36 -0
  27. package/agents/qq/skills/qq-group-directory/SKILL.md +24 -0
  28. package/agents/qq/skills/qq-group-directory/tools/group_info/index.js +16 -0
  29. package/agents/qq/skills/qq-group-directory/tools/group_info/index.ts +16 -0
  30. package/agents/qq/skills/qq-group-directory/tools/group_member_info/index.js +19 -0
  31. package/agents/qq/skills/qq-group-directory/tools/group_member_info/index.ts +19 -0
  32. package/agents/qq/skills/qq-group-directory/tools/group_members/index.js +19 -0
  33. package/agents/qq/skills/qq-group-directory/tools/group_members/index.ts +19 -0
  34. package/agents/qq/skills/qq-group-state/SKILL.md +21 -0
  35. package/agents/qq/skills/qq-group-state/tools/group_blacklist/index.js +19 -0
  36. package/agents/qq/skills/qq-group-state/tools/group_blacklist/index.ts +19 -0
  37. package/agents/qq/skills/qq-group-state/tools/list_join_requests/index.js +19 -0
  38. package/agents/qq/skills/qq-group-state/tools/list_join_requests/index.ts +19 -0
  39. package/agents/qq/skills/qq-group-state/tools/list_muted/index.js +21 -0
  40. package/agents/qq/skills/qq-group-state/tools/list_muted/index.ts +21 -0
  41. package/agents/qq/skills/qq-messages/SKILL.md +27 -0
  42. package/agents/qq/skills/qq-messages/tools/quote_reply/index.js +21 -0
  43. package/agents/qq/skills/qq-messages/tools/quote_reply/index.ts +21 -0
  44. package/agents/qq/skills/qq-messages/tools/recall_own_message/index.js +18 -0
  45. package/agents/qq/skills/qq-messages/tools/recall_own_message/index.ts +17 -0
  46. package/commands/qq/endpoint/definition.js +35 -22
  47. package/commands/qq/endpoint/definition.ts +37 -22
  48. package/lib/console.d.ts +8 -0
  49. package/lib/console.js +84 -0
  50. package/lib/endpoint.d.ts +10 -1
  51. package/lib/endpoint.js +134 -8
  52. package/lib/group-admin.d.ts +36 -0
  53. package/lib/group-admin.js +113 -0
  54. package/lib/protocol.d.ts +5 -0
  55. package/lib/protocol.js +4 -0
  56. package/lib/qq-console-spec.d.ts +17 -0
  57. package/lib/qq-console-spec.js +10 -0
  58. package/lib/qq-console.d.ts +11 -0
  59. package/lib/qq-console.js +116 -0
  60. package/lib/qq-endpoint-write.d.ts +22 -0
  61. package/lib/qq-endpoint-write.js +34 -0
  62. package/lib/qq-runtime-state.d.ts +19 -0
  63. package/lib/qq-runtime-state.js +1 -0
  64. package/package.json +19 -11
  65. package/pages/index/index.js +15 -0
  66. package/pages/index/index.ts +16 -0
  67. package/plugin.js +5 -2
  68. package/schema.json +174 -0
  69. package/src/console.ts +96 -0
  70. package/src/endpoint.ts +137 -8
  71. package/src/group-admin.ts +153 -0
  72. package/src/protocol.ts +8 -0
  73. package/src/qq-console-spec.ts +18 -0
  74. package/src/qq-console.ts +124 -0
  75. package/src/qq-endpoint-write.ts +52 -0
  76. package/src/qq-runtime-state.ts +21 -0
@@ -0,0 +1,22 @@
1
+ /**
2
+ * QQ endpoint 配置写回:扫码成功后把 appId / appSecret 写进 `.env`,
3
+ * 配置里只保留 `${ENV}` 引用,并按公域 / 私域展开 intents。
4
+ *
5
+ * 命令(`qq endpoint add`)与 Console 扫码绑定共用这一处实现。
6
+ */
7
+ import { type EndpointConfigurationStore } from 'zhin.js/adapter';
8
+ import { type QqBotKind } from './qq-intents.js';
9
+ export interface QqEndpointWriteInput {
10
+ readonly endpointId: string;
11
+ readonly appId: string;
12
+ readonly appSecret: string;
13
+ readonly botKind: QqBotKind;
14
+ }
15
+ export interface QqEndpointWriteResult {
16
+ readonly filePath: string;
17
+ readonly endpointId: string;
18
+ readonly botKind: QqBotKind;
19
+ readonly intents: readonly string[];
20
+ }
21
+ /** 写回 `.env` + `zhin.config.yml`;失败时抛原错误,由调用方决定文案。 */
22
+ export declare function writeQqEndpointWithBotKind(input: QqEndpointWriteInput, store: EndpointConfigurationStore): Promise<QqEndpointWriteResult>;
@@ -0,0 +1,34 @@
1
+ /**
2
+ * QQ endpoint 配置写回:扫码成功后把 appId / appSecret 写进 `.env`,
3
+ * 配置里只保留 `${ENV}` 引用,并按公域 / 私域展开 intents。
4
+ *
5
+ * 命令(`qq endpoint add`)与 Console 扫码绑定共用这一处实现。
6
+ */
7
+ import { buildEndpointEnvKey } from 'zhin.js/adapter';
8
+ import { defaultQqEndpointIntentFields } from './qq-intents.js';
9
+ /** 写回 `.env` + `zhin.config.yml`;失败时抛原错误,由调用方决定文案。 */
10
+ export async function writeQqEndpointWithBotKind(input, store) {
11
+ const appidKey = buildEndpointEnvKey('qq', input.endpointId, 'appid');
12
+ const secretKey = buildEndpointEnvKey('qq', input.endpointId, 'secret');
13
+ const intentFields = defaultQqEndpointIntentFields(input.botKind);
14
+ const { filePath } = await store.add({
15
+ adapterKey: 'qq',
16
+ entry: {
17
+ id: input.endpointId,
18
+ appid: `\${${appidKey}}`,
19
+ secret: `\${${secretKey}}`,
20
+ botKind: intentFields.botKind,
21
+ intents: [...intentFields.intents],
22
+ },
23
+ environment: {
24
+ [appidKey]: input.appId,
25
+ [secretKey]: input.appSecret,
26
+ },
27
+ });
28
+ return {
29
+ filePath,
30
+ endpointId: input.endpointId,
31
+ botKind: intentFields.botKind,
32
+ intents: intentFields.intents,
33
+ };
34
+ }
@@ -24,6 +24,23 @@ export interface QqPendingBotKind {
24
24
  readonly cancelCommand?: string;
25
25
  readonly commandHeads?: readonly string[];
26
26
  }
27
+ /**
28
+ * Console 发起的扫码绑定进度。凭据(appSecret)只留在内存,
29
+ * status 只暴露状态、扫码链接与目标 endpoint id。
30
+ */
31
+ export interface QqConsoleBindState {
32
+ readonly state: 'waiting' | 'scanned' | 'failed';
33
+ readonly url?: string;
34
+ readonly message?: string;
35
+ readonly error?: string;
36
+ readonly startedAt: number;
37
+ /** 扫码成功后暂存,确认公域/私域后写配置。 */
38
+ readonly pending?: {
39
+ readonly endpointId: string;
40
+ readonly appId: string;
41
+ readonly appSecret: string;
42
+ };
43
+ }
27
44
  export interface QqRuntimeState {
28
45
  /** 当前 generation 已成功创建的 endpoint(id → 描述) */
29
46
  readonly endpoints: Map<string, QqRunningEndpoint>;
@@ -31,6 +48,8 @@ export interface QqRuntimeState {
31
48
  bindFlow: QqBindFlowHandle | null;
32
49
  /** 扫码完成后等待 botKind 选择(凭据仅在内存) */
33
50
  pendingBotKind: QqPendingBotKind | null;
51
+ /** Console 页面的扫码绑定进度(与 bindFlow 共享单例)。 */
52
+ consoleBind: QqConsoleBindState | null;
34
53
  }
35
54
  export declare function createQqRuntimeState(): QqRuntimeState;
36
55
  export declare const qqRuntimeStateToken: import("zhin.js").Token<QqRuntimeState>;
@@ -9,6 +9,7 @@ export function createQqRuntimeState() {
9
9
  endpoints: new Map(),
10
10
  bindFlow: null,
11
11
  pendingBotKind: null,
12
+ consoleBind: null,
12
13
  };
13
14
  }
14
15
  export const qqRuntimeStateToken = createToken('zhin.qq.runtime-state', 'QQ adapter runtime state (running endpoints + bind flow singleton)');
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@zhin.js/adapter-qq",
3
- "version": "1.1.16",
3
+ "version": "1.1.18",
4
4
  "description": "Zhin.js QQ Official adapter for Plugin Runtime (WebSocket gateway)",
5
5
  "type": "module",
6
6
  "main": "./lib/index.js",
@@ -34,24 +34,27 @@
34
34
  },
35
35
  "dependencies": {
36
36
  "qq-official-bot": "^1.3.3",
37
- "@zhin.js/adapter": "1.1.23",
37
+ "@zhin.js/adapter": "1.1.25",
38
38
  "@zhin.js/agent-feature": "1.1.10",
39
- "@zhin.js/core": "1.1.51",
39
+ "@zhin.js/client": "1.1.21",
40
+ "@zhin.js/console-contract": "1.1.3",
41
+ "@zhin.js/core": "1.1.53",
40
42
  "@zhin.js/feature-kit": "1.1.9",
41
- "@zhin.js/host-http": "1.1.10",
42
- "@zhin.js/im-contract": "1.1.7",
43
+ "@zhin.js/host-http": "1.1.11",
44
+ "@zhin.js/im-contract": "1.1.9",
43
45
  "@zhin.js/logger": "1.1.2",
46
+ "@zhin.js/page": "1.1.10",
44
47
  "@zhin.js/permission": "1.1.9",
45
48
  "@zhin.js/skill": "1.1.10",
46
- "@zhin.js/tool": "1.1.10"
49
+ "@zhin.js/tool": "1.1.12"
47
50
  },
48
51
  "peerDependencies": {
49
52
  "zod": "^4.0.0",
50
- "@zhin.js/adapter": "^1.1.23",
53
+ "@zhin.js/adapter": "^1.1.25",
51
54
  "@zhin.js/command": "^1.1.11",
52
- "@zhin.js/core": "^1.1.51",
55
+ "@zhin.js/core": "^1.1.53",
53
56
  "@zhin.js/middleware": "^1.1.10",
54
- "zhin.js": "^1.1.16"
57
+ "zhin.js": "^1.1.18"
55
58
  },
56
59
  "peerDependenciesMeta": {
57
60
  "@zhin.js/command": {
@@ -72,13 +75,14 @@
72
75
  "typescript": "^6.0.3",
73
76
  "vitest": "^4.1.10",
74
77
  "zod": "^4.4.3",
75
- "@zhin.js/host-http": "1.1.10",
76
- "zhin.js": "1.1.16"
78
+ "@zhin.js/host-http": "1.1.11",
79
+ "zhin.js": "1.1.18"
77
80
  },
78
81
  "files": [
79
82
  "adapters",
80
83
  "commands",
81
84
  "middlewares",
85
+ "pages",
82
86
  "plugin.js",
83
87
  "schema.json",
84
88
  "src",
@@ -126,6 +130,10 @@
126
130
  {
127
131
  "package": "@zhin.js/agent-feature",
128
132
  "api": "^1.0.0"
133
+ },
134
+ {
135
+ "package": "@zhin.js/page",
136
+ "api": "^1.0.0"
129
137
  }
130
138
  ],
131
139
  "plugins": []
@@ -0,0 +1,15 @@
1
+ // Generated by build-plugin-runtime-entries.mjs. Do not edit.
2
+ import { definePage } from '@zhin.js/console-contract';
3
+ import { createAdapterConsolePage } from '@zhin.js/client';
4
+ export const meta = definePage({
5
+ title: 'QQ',
6
+ titleI18n: { en: 'QQ' },
7
+ icon: 'MessageCircle',
8
+ order: 60,
9
+ requiredPermissions: ['qq:read'],
10
+ });
11
+ /**
12
+ * Convention page: `pages/index/index.ts` → `/qq`(插件路径)。
13
+ * 连接状态、测试、endpoint 增删、扫码绑定(原本 `qq endpoint add` 的聊天流程)。
14
+ */
15
+ export default createAdapterConsolePage('qq');
@@ -0,0 +1,16 @@
1
+ import { definePage } from '@zhin.js/console-contract';
2
+ import { createAdapterConsolePage } from '@zhin.js/client';
3
+
4
+ export const meta = definePage({
5
+ title: 'QQ',
6
+ titleI18n: { en: 'QQ' },
7
+ icon: 'MessageCircle',
8
+ order: 60,
9
+ requiredPermissions: ['qq:read'],
10
+ });
11
+
12
+ /**
13
+ * Convention page: `pages/index/index.ts` → `/qq`(插件路径)。
14
+ * 连接状态、测试、endpoint 增删、扫码绑定(原本 `qq endpoint add` 的聊天流程)。
15
+ */
16
+ export default createAdapterConsolePage('qq');
package/plugin.js CHANGED
@@ -1,15 +1,18 @@
1
1
  // Generated by build-plugin-runtime-entries.mjs. Do not edit.
2
- import { definePlugin } from 'zhin.js';
2
+ import { definePlugin, pluginConsoleRpcToken } from 'zhin.js';
3
3
  import { permissionHostToken } from '@zhin.js/permission';
4
4
  import { checkQqPlatformPermit } from "./lib/platform-permit.js";
5
5
  import { createQqRuntimeState, qqRuntimeStateToken } from "./lib/qq-runtime-state.js";
6
+ import { createQqConsoleRpc } from "./lib/console.js";
6
7
  export default definePlugin({
7
8
  name: 'qq',
8
9
  metadata: {
9
10
  displayName: 'QQ Official WebSocket Adapter',
10
11
  },
11
12
  setup(context) {
12
- context.resources.provide(qqRuntimeStateToken, createQqRuntimeState());
13
+ const state = createQqRuntimeState();
14
+ context.resources.provide(qqRuntimeStateToken, state);
15
+ context.resources.provide(pluginConsoleRpcToken, createQqConsoleRpc(state, context.resources, () => context.config.get()));
13
16
  if (context.resources.has(permissionHostToken)) {
14
17
  const host = context.resources.use(permissionHostToken);
15
18
  return host.registerPlatform('qq', checkQqPlatformPermit);
package/schema.json CHANGED
@@ -197,6 +197,93 @@
197
197
  }
198
198
  },
199
199
  "description": "Optional fixed loopback TCP fault proxy for WSS only; original gateway TLS identity must match serverName."
200
+ },
201
+ "notify": {
202
+ "type": "object",
203
+ "additionalProperties": false,
204
+ "description": "掉线/登录失败时通过另一个适配器实例给 master 发通知(adapter/endpoint/kind/id 指向实际会话)",
205
+ "x-i18n": {
206
+ "en": {
207
+ "description": "Send outage/login notices to the master through another adapter instance (adapter/endpoint/kind/id identify the actual conversation)."
208
+ }
209
+ },
210
+ "properties": {
211
+ "adapter": {
212
+ "type": "string",
213
+ "description": "通知使用的适配器类型,如 qq / icqq",
214
+ "x-i18n": {
215
+ "en": {
216
+ "description": "Adapter type used for the notice, such as qq or icqq."
217
+ }
218
+ }
219
+ },
220
+ "endpoint": {
221
+ "type": "string",
222
+ "description": "该适配器的 endpoint id",
223
+ "x-i18n": {
224
+ "en": {
225
+ "description": "Endpoint id of that adapter."
226
+ }
227
+ }
228
+ },
229
+ "kind": {
230
+ "type": "string",
231
+ "enum": [
232
+ "private",
233
+ "group",
234
+ "channel"
235
+ ],
236
+ "default": "private",
237
+ "description": "会话类型,默认 private",
238
+ "x-i18n": {
239
+ "en": {
240
+ "description": "Conversation kind; defaults to private."
241
+ }
242
+ }
243
+ },
244
+ "id": {
245
+ "type": [
246
+ "string",
247
+ "number"
248
+ ],
249
+ "description": "master 在该平台上的会话 id(QQ 官方为 openid,ICQQ 为 QQ 号)",
250
+ "x-i18n": {
251
+ "en": {
252
+ "description": "Master conversation id on that platform (openid for QQ official, QQ number for ICQQ)."
253
+ }
254
+ }
255
+ },
256
+ "parent": {
257
+ "type": "object",
258
+ "additionalProperties": false,
259
+ "properties": {
260
+ "kind": {
261
+ "type": "string",
262
+ "enum": [
263
+ "private",
264
+ "group",
265
+ "channel"
266
+ ]
267
+ },
268
+ "id": {
269
+ "type": [
270
+ "string",
271
+ "number"
272
+ ]
273
+ }
274
+ },
275
+ "required": [
276
+ "kind",
277
+ "id"
278
+ ],
279
+ "description": "可选的父频道/群容器"
280
+ }
281
+ },
282
+ "required": [
283
+ "adapter",
284
+ "endpoint",
285
+ "id"
286
+ ]
200
287
  }
201
288
  },
202
289
  "required": [
@@ -240,6 +327,93 @@
240
327
  }
241
328
  },
242
329
  "description": "Optional fixed loopback TCP fault proxy for WSS only; original gateway TLS identity must match serverName."
330
+ },
331
+ "notify": {
332
+ "type": "object",
333
+ "additionalProperties": false,
334
+ "description": "掉线/登录失败时通过另一个适配器实例给 master 发通知(adapter/endpoint/kind/id 指向实际会话)",
335
+ "x-i18n": {
336
+ "en": {
337
+ "description": "Send outage/login notices to the master through another adapter instance (adapter/endpoint/kind/id identify the actual conversation)."
338
+ }
339
+ },
340
+ "properties": {
341
+ "adapter": {
342
+ "type": "string",
343
+ "description": "通知使用的适配器类型,如 qq / icqq",
344
+ "x-i18n": {
345
+ "en": {
346
+ "description": "Adapter type used for the notice, such as qq or icqq."
347
+ }
348
+ }
349
+ },
350
+ "endpoint": {
351
+ "type": "string",
352
+ "description": "该适配器的 endpoint id",
353
+ "x-i18n": {
354
+ "en": {
355
+ "description": "Endpoint id of that adapter."
356
+ }
357
+ }
358
+ },
359
+ "kind": {
360
+ "type": "string",
361
+ "enum": [
362
+ "private",
363
+ "group",
364
+ "channel"
365
+ ],
366
+ "default": "private",
367
+ "description": "会话类型,默认 private",
368
+ "x-i18n": {
369
+ "en": {
370
+ "description": "Conversation kind; defaults to private."
371
+ }
372
+ }
373
+ },
374
+ "id": {
375
+ "type": [
376
+ "string",
377
+ "number"
378
+ ],
379
+ "description": "master 在该平台上的会话 id(QQ 官方为 openid,ICQQ 为 QQ 号)",
380
+ "x-i18n": {
381
+ "en": {
382
+ "description": "Master conversation id on that platform (openid for QQ official, QQ number for ICQQ)."
383
+ }
384
+ }
385
+ },
386
+ "parent": {
387
+ "type": "object",
388
+ "additionalProperties": false,
389
+ "properties": {
390
+ "kind": {
391
+ "type": "string",
392
+ "enum": [
393
+ "private",
394
+ "group",
395
+ "channel"
396
+ ]
397
+ },
398
+ "id": {
399
+ "type": [
400
+ "string",
401
+ "number"
402
+ ]
403
+ }
404
+ },
405
+ "required": [
406
+ "kind",
407
+ "id"
408
+ ],
409
+ "description": "可选的父频道/群容器"
410
+ }
411
+ },
412
+ "required": [
413
+ "adapter",
414
+ "endpoint",
415
+ "id"
416
+ ]
243
417
  }
244
418
  },
245
419
  "required": [
package/src/console.ts ADDED
@@ -0,0 +1,96 @@
1
+ /**
2
+ * QQ Console RPC:除通用 endpoint 管理外,把 `qq endpoint add` 的扫码绑定
3
+ * (以及公域 / 私域选择、取消)搬到 Console 页面。
4
+ */
5
+ import {
6
+ endpointConfigurationStoreToken,
7
+ createAdapterConsoleRpc,
8
+ type AdapterConsoleResources,
9
+ type AdapterConsoleSpec,
10
+ type EndpointConfigurationStore,
11
+ } from 'zhin.js/adapter';
12
+ import { PluginConsoleRpcError, type PluginConsoleRpc } from 'zhin.js';
13
+ import { qqConsoleSpec } from './qq-console-spec.js';
14
+ import {
15
+ cancelQqConsoleBind,
16
+ completeQqConsoleBind,
17
+ startQqConsoleBind,
18
+ } from './qq-console.js';
19
+ import type { QqRuntimeState } from './qq-runtime-state.js';
20
+
21
+ export function createQqConsoleRpc(
22
+ state: QqRuntimeState,
23
+ resources: AdapterConsoleResources,
24
+ getConfig: () => unknown,
25
+ ): PluginConsoleRpc {
26
+ const store: EndpointConfigurationStore | undefined = resources.has(endpointConfigurationStoreToken)
27
+ ? resources.use(endpointConfigurationStoreToken)
28
+ : undefined;
29
+
30
+ const spec: AdapterConsoleSpec = {
31
+ ...qqConsoleSpec,
32
+ running: () => state.endpoints.values(),
33
+ getConfig,
34
+ extraStatus: () => {
35
+ const bind = state.consoleBind;
36
+ if (!bind) {
37
+ return { bindState: state.bindFlow ? 'binding' : 'idle' };
38
+ }
39
+ return {
40
+ bindState: bind.state,
41
+ ...(bind.url ? { scanUrl: bind.url } : {}),
42
+ ...(bind.message ? { bindMessage: bind.message } : {}),
43
+ ...(bind.pending ? { bindEndpoint: bind.pending.endpointId } : {}),
44
+ ...(bind.error ? { bindError: bind.error } : {}),
45
+ };
46
+ },
47
+ availableActions: () => {
48
+ if (!store) return [];
49
+ if (state.consoleBind?.pending) return ['qq-bind-kind', 'qq-bind-cancel'];
50
+ if (state.bindFlow || state.consoleBind?.state === 'waiting') return ['qq-bind-cancel'];
51
+ return ['qq-bind-start'];
52
+ },
53
+ actions: {
54
+ 'qq-bind-start': {
55
+ label: '扫码绑定新机器人',
56
+ labelI18n: { en: 'Bind a bot with QR' },
57
+ description: '用手机 QQ 扫码,成功后选择公域 / 私域,再一次性写入 .env 与配置。',
58
+ descriptionI18n: { en: 'Scan with mobile QQ, choose public/private, then save credentials to .env and the config.' },
59
+ handler: async () => {
60
+ if (!store) throw new PluginConsoleRpcError('unsupported', '当前运行环境不支持写入 endpoint 配置');
61
+ const { url } = await startQqConsoleBind(state);
62
+ return { message: '请用手机 QQ 扫码;扫码成功后选择公域或私域', url };
63
+ },
64
+ },
65
+ 'qq-bind-kind': {
66
+ label: '选择公域 / 私域',
67
+ labelI18n: { en: 'Choose public/private' },
68
+ description: '公域订阅 PUBLIC_GUILD_MESSAGES;私域订阅 GUILD_MESSAGES。',
69
+ descriptionI18n: { en: 'Public subscribes PUBLIC_GUILD_MESSAGES; private subscribes GUILD_MESSAGES.' },
70
+ options: [
71
+ { value: 'public', label: '公域', labelI18n: { en: 'Public' } },
72
+ { value: 'private', label: '私域', labelI18n: { en: 'Private' } },
73
+ ],
74
+ handler: async (input) => {
75
+ if (!store) throw new PluginConsoleRpcError('unsupported', '当前运行环境不支持写入 endpoint 配置');
76
+ const result = await completeQqConsoleBind(state, store, input.value);
77
+ return {
78
+ message: `已写入 endpoint「${result.endpointId}」(${result.botKind}),重启或热重载后生效`,
79
+ details: { endpoint: result.endpointId, botKind: result.botKind, intents: result.intents.join(', ') },
80
+ };
81
+ },
82
+ },
83
+ 'qq-bind-cancel': {
84
+ label: '取消扫码绑定',
85
+ labelI18n: { en: 'Cancel QR binding' },
86
+ description: '中止正在进行的扫码绑定;已写入的配置不会删除。',
87
+ descriptionI18n: { en: 'Stop the current QR binding flow; already-saved configuration is untouched.' },
88
+ handler: async () => {
89
+ const cancelled = cancelQqConsoleBind(state);
90
+ return { message: cancelled ? '已取消绑定' : '当前没有进行中的绑定流程' };
91
+ },
92
+ },
93
+ },
94
+ };
95
+ return createAdapterConsoleRpc(spec, resources);
96
+ }