@zhin.js/adapter-onebot11 4.0.1 → 4.0.3

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 (66) hide show
  1. package/CHANGELOG.md +56 -0
  2. package/README.md +40 -370
  3. package/adapters/onebot11.ts +39 -0
  4. package/agent/tools/set_title.ts +21 -0
  5. package/lib/index.d.ts +7 -16
  6. package/lib/index.js +6 -56
  7. package/lib/onebot11-agent-deps.d.ts +15 -0
  8. package/lib/onebot11-agent-deps.js +30 -0
  9. package/lib/protocol.d.ts +144 -0
  10. package/lib/protocol.js +235 -0
  11. package/lib/ws-endpoint.d.ts +28 -0
  12. package/lib/ws-endpoint.js +210 -0
  13. package/lib/ws-transport.d.ts +13 -0
  14. package/lib/ws-transport.js +74 -0
  15. package/lib/ws-types.d.ts +17 -0
  16. package/lib/ws-types.js +1 -0
  17. package/lib/wss-auth.d.ts +2 -0
  18. package/lib/wss-auth.js +16 -0
  19. package/lib/wss-endpoint.d.ts +26 -0
  20. package/lib/wss-endpoint.js +143 -0
  21. package/package.json +52 -22
  22. package/plugin.ts +13 -0
  23. package/schema.json +36 -0
  24. package/src/index.ts +54 -68
  25. package/src/onebot11-agent-deps.ts +49 -0
  26. package/src/protocol.ts +370 -0
  27. package/src/ws-endpoint.ts +266 -0
  28. package/src/ws-transport.ts +101 -0
  29. package/src/ws-types.ts +20 -0
  30. package/src/wss-auth.ts +17 -0
  31. package/src/wss-endpoint.ts +189 -0
  32. package/lib/adapter.d.ts +0 -41
  33. package/lib/adapter.d.ts.map +0 -1
  34. package/lib/adapter.js +0 -101
  35. package/lib/adapter.js.map +0 -1
  36. package/lib/endpoint-ws-client.d.ts +0 -41
  37. package/lib/endpoint-ws-client.d.ts.map +0 -1
  38. package/lib/endpoint-ws-client.js +0 -408
  39. package/lib/endpoint-ws-client.js.map +0 -1
  40. package/lib/endpoint-ws-server.d.ts +0 -43
  41. package/lib/endpoint-ws-server.d.ts.map +0 -1
  42. package/lib/endpoint-ws-server.js +0 -355
  43. package/lib/endpoint-ws-server.js.map +0 -1
  44. package/lib/index.d.ts.map +0 -1
  45. package/lib/index.js.map +0 -1
  46. package/lib/onebot-get-msg.d.ts +0 -3
  47. package/lib/onebot-get-msg.d.ts.map +0 -1
  48. package/lib/onebot-get-msg.js +0 -27
  49. package/lib/onebot-get-msg.js.map +0 -1
  50. package/lib/segment-mapper.d.ts +0 -2
  51. package/lib/segment-mapper.d.ts.map +0 -1
  52. package/lib/segment-mapper.js +0 -2
  53. package/lib/segment-mapper.js.map +0 -1
  54. package/lib/types.d.ts +0 -61
  55. package/lib/types.d.ts.map +0 -1
  56. package/lib/types.js +0 -5
  57. package/lib/types.js.map +0 -1
  58. package/plugin.yml +0 -3
  59. package/src/adapter.ts +0 -114
  60. package/src/endpoint-ws-client.ts +0 -435
  61. package/src/endpoint-ws-server.ts +0 -396
  62. package/src/onebot-get-msg.ts +0 -33
  63. package/src/segment-mapper.ts +0 -1
  64. package/src/types.ts +0 -60
  65. /package/{skills/onebot11 → agent}/PERMITS.md +0 -0
  66. /package/{skills/onebot11/SKILL.md → agent/skills/onebot11.md} +0 -0
@@ -0,0 +1,74 @@
1
+ import { clearInterval, clearTimeout, setInterval, setTimeout } from 'node:timers';
2
+ import { formatCompact, getLogger } from '@zhin.js/logger';
3
+ import { WS_OPEN, } from './ws-types.js';
4
+ const logger = getLogger('onebot11');
5
+ export function decodeWsPayload(data) {
6
+ if (typeof data === 'string')
7
+ return data;
8
+ if (Buffer.isBuffer(data))
9
+ return data.toString();
10
+ if (data instanceof ArrayBuffer)
11
+ return new TextDecoder().decode(data);
12
+ return String(data ?? '');
13
+ }
14
+ export function handleOneBot11WsMessage(data, options) {
15
+ try {
16
+ const raw = decodeWsPayload(data);
17
+ const msg = JSON.parse(raw);
18
+ if ('echo' in msg && typeof msg.echo === 'string') {
19
+ const resp = msg;
20
+ const pending = options.pending.get(resp.echo);
21
+ if (pending) {
22
+ options.pending.delete(resp.echo);
23
+ clearTimeout(pending.timeout);
24
+ if (resp.status === 'ok')
25
+ pending.resolve(resp.data);
26
+ else
27
+ pending.reject(new Error(`OneBot11 retcode=${resp.retcode}`));
28
+ }
29
+ return;
30
+ }
31
+ options.admit(msg);
32
+ }
33
+ catch (error) {
34
+ logger.warn(formatCompact({
35
+ op: 'onebot11_parse_failed',
36
+ endpoint: options.endpointName,
37
+ error: error instanceof Error ? error.message : String(error),
38
+ }));
39
+ }
40
+ }
41
+ export function callOneBot11WsAction(ws, pending, requestId, action, params) {
42
+ if (!ws || ws.readyState !== WS_OPEN) {
43
+ return Promise.reject(new Error('WebSocket 未连接'));
44
+ }
45
+ const echo = `ob11_${++requestId.value}`;
46
+ const req = { action, params, echo };
47
+ return new Promise((resolve, reject) => {
48
+ const timeout = setTimeout(() => {
49
+ pending.delete(echo);
50
+ reject(new Error(`OneBot11 动作超时: ${action}`));
51
+ }, 30_000);
52
+ pending.set(echo, { resolve, reject, timeout });
53
+ ws.send(JSON.stringify(req));
54
+ });
55
+ }
56
+ export function startOneBot11Heartbeat(ws, intervalMs, existingTimer) {
57
+ if (existingTimer)
58
+ clearInterval(existingTimer);
59
+ return setInterval(() => {
60
+ try {
61
+ ws?.ping?.();
62
+ }
63
+ catch {
64
+ /* ignore */
65
+ }
66
+ }, intervalMs);
67
+ }
68
+ export function rejectAllPending(pending, message = '连接已关闭') {
69
+ for (const [, entry] of pending) {
70
+ clearTimeout(entry.timeout);
71
+ entry.reject(new Error(message));
72
+ }
73
+ pending.clear();
74
+ }
@@ -0,0 +1,17 @@
1
+ /** Minimal WS surface used by the endpoint (real `ws` or test mock). */
2
+ export interface OneBot11WsSocket {
3
+ readonly readyState: number;
4
+ send(data: string): void;
5
+ close(code?: number, reason?: string): void;
6
+ ping?(): void;
7
+ on(event: 'open' | 'message' | 'close' | 'error', listener: (...args: unknown[]) => void): void;
8
+ }
9
+ export interface OneBot11WsCreateOptions {
10
+ readonly headers?: Record<string, string>;
11
+ }
12
+ export declare const WS_OPEN = 1;
13
+ export interface OneBot11PendingAction {
14
+ resolve: (value: unknown) => void;
15
+ reject: (err: Error) => void;
16
+ timeout: NodeJS.Timeout;
17
+ }
@@ -0,0 +1 @@
1
+ export const WS_OPEN = 1;
@@ -0,0 +1,2 @@
1
+ import type { IncomingMessage } from 'node:http';
2
+ export declare function verifyOneBotAccessToken(accessToken: string | undefined, request: IncomingMessage): boolean;
@@ -0,0 +1,16 @@
1
+ export function verifyOneBotAccessToken(accessToken, request) {
2
+ if (!accessToken)
3
+ return true;
4
+ const auth = request.headers.authorization ?? '';
5
+ if (auth === `Bearer ${accessToken}`)
6
+ return true;
7
+ try {
8
+ const url = new URL(request.url ?? '/', 'http://localhost');
9
+ if (url.searchParams.get('access_token') === accessToken)
10
+ return true;
11
+ }
12
+ catch {
13
+ /* ignore */
14
+ }
15
+ return false;
16
+ }
@@ -0,0 +1,26 @@
1
+ import type { EndpointInstance } from '@zhin.js/adapter';
2
+ import type { MessageGateway } from '@zhin.js/core/runtime';
3
+ import type { HttpHost } from '@zhin.js/host-http';
4
+ import type { CapabilityId } from '@zhin.js/plugin-runtime';
5
+ import { type OneBot11Event, type OneBot11WssConfig } from './protocol.js';
6
+ export interface OneBot11WssEndpointOptions {
7
+ readonly id: CapabilityId;
8
+ readonly gateway: MessageGateway;
9
+ readonly http: HttpHost;
10
+ readonly config: OneBot11WssConfig;
11
+ }
12
+ export declare class OneBot11WssEndpoint implements EndpointInstance {
13
+ #private;
14
+ constructor(options: OneBot11WssEndpointOptions);
15
+ start(): Promise<void>;
16
+ open(): void;
17
+ close(): void;
18
+ stop(): Promise<void>;
19
+ send({ target, payload }: {
20
+ readonly target: string;
21
+ readonly payload: unknown;
22
+ }): Promise<string>;
23
+ callApi(action: string, params?: Record<string, unknown>): Promise<unknown>;
24
+ setTitle(groupId: number, userId: number, title: string, duration?: number): Promise<boolean>;
25
+ admit(ev: OneBot11Event): void;
26
+ }
@@ -0,0 +1,143 @@
1
+ /**
2
+ * OneBot11 reverse WSS endpoint — accepts inbound WebSocket from OneBot implementation.
3
+ */
4
+ import { clearInterval } from 'node:timers';
5
+ import { formatCompact, getLogger } from '@zhin.js/logger';
6
+ import { registerOnebot11AgentEndpoint } from './onebot11-agent-deps.js';
7
+ import { buildSendAction, formatInboundContent, formatInboundMetadata, formatInboundTarget, formatOutboundSegments, isMessageEvent, senderUserId, } from './protocol.js';
8
+ import { verifyOneBotAccessToken } from './wss-auth.js';
9
+ import { callOneBot11WsAction, handleOneBot11WsMessage, rejectAllPending, startOneBot11Heartbeat, } from './ws-transport.js';
10
+ const logger = getLogger('onebot11');
11
+ export class OneBot11WssEndpoint {
12
+ #options;
13
+ #ws;
14
+ #wsRelease;
15
+ #heartbeatTimer;
16
+ #requestId = { value: 0 };
17
+ #pending = new Map();
18
+ #open = false;
19
+ #started = false;
20
+ #unregisterAgent;
21
+ constructor(options) {
22
+ this.#options = options;
23
+ }
24
+ async start() {
25
+ if (this.#started)
26
+ return;
27
+ this.#started = true;
28
+ this.#unregisterAgent = registerOnebot11AgentEndpoint(this.#options.config.name, this);
29
+ const handle = this.#options.http.ws(this.#options.config.path);
30
+ this.#wsRelease = handle.onConnection((connection) => {
31
+ this.#acceptConnection(connection);
32
+ });
33
+ logger.info(formatCompact({
34
+ op: 'listen',
35
+ endpoint: this.#options.config.name,
36
+ mode: 'wss',
37
+ path: this.#options.config.path,
38
+ }));
39
+ }
40
+ open() {
41
+ this.#open = true;
42
+ }
43
+ close() {
44
+ this.#open = false;
45
+ }
46
+ async stop() {
47
+ this.#open = false;
48
+ this.#unregisterAgent?.();
49
+ this.#unregisterAgent = undefined;
50
+ this.#wsRelease?.();
51
+ this.#wsRelease = undefined;
52
+ if (this.#heartbeatTimer) {
53
+ clearInterval(this.#heartbeatTimer);
54
+ this.#heartbeatTimer = undefined;
55
+ }
56
+ rejectAllPending(this.#pending);
57
+ if (this.#ws) {
58
+ try {
59
+ this.#ws.close();
60
+ }
61
+ catch {
62
+ /* ignore */
63
+ }
64
+ this.#ws = undefined;
65
+ }
66
+ this.#started = false;
67
+ }
68
+ async send({ target, payload }) {
69
+ const message = formatOutboundSegments(payload);
70
+ const { action, params } = buildSendAction(target, message);
71
+ const data = await this.callApi(action, params);
72
+ return data?.message_id != null ? String(data.message_id) : '';
73
+ }
74
+ callApi(action, params = {}) {
75
+ return callOneBot11WsAction(this.#ws, this.#pending, this.#requestId, action, params);
76
+ }
77
+ async setTitle(groupId, userId, title, duration = -1) {
78
+ await this.callApi('set_group_special_title', {
79
+ group_id: groupId,
80
+ user_id: userId,
81
+ special_title: title,
82
+ duration,
83
+ });
84
+ return true;
85
+ }
86
+ admit(ev) {
87
+ if (!this.#open || !isMessageEvent(ev))
88
+ return;
89
+ const target = formatInboundTarget(ev);
90
+ void this.#options.gateway.receive({
91
+ adapter: this.#options.id,
92
+ target,
93
+ content: formatInboundContent(ev),
94
+ sender: senderUserId(ev),
95
+ id: String(ev.message_id),
96
+ metadata: formatInboundMetadata(ev, this.#options.config.name),
97
+ }).catch((err) => {
98
+ logger.warn(formatCompact({
99
+ op: 'onebot11_gateway_receive_failed',
100
+ target,
101
+ error: err instanceof Error ? err.message : String(err),
102
+ }));
103
+ });
104
+ }
105
+ #acceptConnection(connection) {
106
+ if (!verifyOneBotAccessToken(this.#options.config.access_token, connection.request)) {
107
+ connection.socket.close(4003, 'Unauthorized');
108
+ return;
109
+ }
110
+ const socket = connection.socket;
111
+ if (this.#ws) {
112
+ try {
113
+ this.#ws.close();
114
+ }
115
+ catch {
116
+ /* ignore */
117
+ }
118
+ }
119
+ this.#ws = socket;
120
+ this.#heartbeatTimer = startOneBot11Heartbeat(this.#ws, this.#options.config.heartbeat_interval, this.#heartbeatTimer);
121
+ socket.on('message', (data) => {
122
+ handleOneBot11WsMessage(data, {
123
+ endpointName: this.#options.config.name,
124
+ pending: this.#pending,
125
+ admit: (ev) => this.admit(ev),
126
+ });
127
+ });
128
+ socket.on('close', () => {
129
+ if (this.#ws === socket) {
130
+ this.#ws = undefined;
131
+ if (this.#heartbeatTimer) {
132
+ clearInterval(this.#heartbeatTimer);
133
+ this.#heartbeatTimer = undefined;
134
+ }
135
+ }
136
+ });
137
+ logger.debug(formatCompact({
138
+ endpoint: this.#options.config.name,
139
+ mode: 'wss',
140
+ peer: connection.request.socket.remoteAddress,
141
+ }));
142
+ }
143
+ }
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@zhin.js/adapter-onebot11",
3
- "version": "4.0.1",
4
- "description": "Zhin.js adapter for OneBot v11 protocol",
3
+ "version": "4.0.3",
4
+ "description": "Zhin.js OneBot 11 adapter for Plugin Runtime (WebSocket client)",
5
5
  "type": "module",
6
6
  "main": "./lib/index.js",
7
7
  "types": "./lib/index.d.ts",
@@ -13,12 +13,12 @@
13
13
  }
14
14
  },
15
15
  "files": [
16
+ "adapters",
17
+ "plugin.ts",
18
+ "schema.json",
16
19
  "src",
17
20
  "lib",
18
- "client",
19
- "dist",
20
- "skills",
21
- "plugin.yml",
21
+ "agent",
22
22
  "README.md",
23
23
  "CHANGELOG.md"
24
24
  ],
@@ -32,38 +32,53 @@
32
32
  "qq",
33
33
  "chatbot"
34
34
  ],
35
- "author": {
36
- "name": "lc-cn",
37
- "email": "admin@liucl.cn",
38
- "url": "https://github.com/lc-cn"
39
- },
40
35
  "license": "MIT",
41
36
  "repository": {
42
- "url": "git+https://github.com/zhinjs/zhin.git",
43
37
  "type": "git",
38
+ "url": "https://github.com/zhinjs/zhin",
44
39
  "directory": "plugins/adapters/onebot11"
45
40
  },
46
41
  "dependencies": {
47
- "ws": "^8.21.0"
42
+ "ws": "^8.21.0",
43
+ "@zhin.js/adapter": "1.0.1",
44
+ "@zhin.js/core": "1.3.5",
45
+ "@zhin.js/host-http": "1.0.1",
46
+ "@zhin.js/plugin-runtime": "1.0.1",
47
+ "@zhin.js/logger": "1.0.75"
48
48
  },
49
49
  "devDependencies": {
50
50
  "@types/node": "^26.1.0",
51
51
  "@types/ws": "^8.18.1",
52
52
  "typescript": "^6.0.3",
53
- "@zhin.js/cli": "1.0.92",
54
- "@zhin.js/client": "2.0.4",
55
- "@zhin.js/host-router": "2.0.2",
56
- "zhin.js": "4.1.1"
53
+ "vitest": "^4.1.10",
54
+ "zod": "^4.4.3",
55
+ "@zhin.js/agent": "1.0.4",
56
+ "@zhin.js/host-http": "1.0.1"
57
57
  },
58
58
  "peerDependencies": {
59
- "@zhin.js/host-router": "2.0.2",
60
- "zhin.js": "4.1.1"
59
+ "zod": "^4.0.0",
60
+ "@zhin.js/adapter": "1.0.1",
61
+ "@zhin.js/core": "1.3.5",
62
+ "@zhin.js/plugin-runtime": "1.0.1",
63
+ "zhin.js": "4.1.3",
64
+ "@zhin.js/agent": "1.0.4"
61
65
  },
62
66
  "peerDependenciesMeta": {
63
- "@zhin.js/host-router": {
67
+ "zhin.js": {
68
+ "optional": true
69
+ },
70
+ "@zhin.js/agent": {
71
+ "optional": true
72
+ },
73
+ "zod": {
64
74
  "optional": true
65
75
  }
66
76
  },
77
+ "author": {
78
+ "name": "lc-cn",
79
+ "email": "admin@liucl.cn",
80
+ "url": "https://github.com/lc-cn"
81
+ },
67
82
  "publishConfig": {
68
83
  "access": "public",
69
84
  "registry": "https://registry.npmjs.org"
@@ -71,8 +86,23 @@
71
86
  "engines": {
72
87
  "node": "^20.19.0 || >=22.12.0"
73
88
  },
89
+ "zhin": {
90
+ "protocol": 1,
91
+ "type": "plugin",
92
+ "entry": "./plugin.ts",
93
+ "engine": "^1.0.0",
94
+ "runtime": "trusted",
95
+ "features": [
96
+ {
97
+ "package": "@zhin.js/adapter",
98
+ "api": "^1.0.0"
99
+ }
100
+ ],
101
+ "plugins": []
102
+ },
74
103
  "scripts": {
75
- "build": "zhin build",
76
- "clean": "rimraf lib"
104
+ "build": "tsc",
105
+ "clean": "rimraf lib",
106
+ "test": "NODE_OPTIONS=--experimental-strip-types vitest run --root ../../.. plugins/adapters/onebot11/tests"
77
107
  }
78
108
  }
package/plugin.ts ADDED
@@ -0,0 +1,13 @@
1
+ import { definePlugin } from '@zhin.js/plugin-runtime';
2
+ import { registerDefaultScenePlatformPermitChecker } from '@zhin.js/core';
3
+
4
+ export default definePlugin({
5
+ name: 'onebot11',
6
+ metadata: {
7
+ displayName: 'OneBot 11 Adapter',
8
+ },
9
+ setup() {
10
+ // 平台权限门禁:scene_admin / scene_owner 由 sender role 判定(见 ws/wss-endpoint admit metadata)
11
+ return registerDefaultScenePlatformPermitChecker('onebot11');
12
+ },
13
+ });
package/schema.json ADDED
@@ -0,0 +1,36 @@
1
+ {
2
+ "$schema": "https://json-schema.org/draft/2020-12/schema",
3
+ "type": "object",
4
+ "additionalProperties": false,
5
+ "properties": {
6
+ "connection": {
7
+ "type": "string",
8
+ "enum": ["ws", "wss"],
9
+ "default": "ws",
10
+ "description": "connection: ws (forward WS client, default) or wss (reverse WS via httpHostToken)"
11
+ },
12
+ "name": {
13
+ "type": "string",
14
+ "default": "onebot11-bot"
15
+ },
16
+ "url": {
17
+ "type": "string",
18
+ "description": "OneBot implementation WebSocket URL (required for connection: ws)"
19
+ },
20
+ "path": {
21
+ "type": "string",
22
+ "description": "WS path for reverse-wss (connection: wss)"
23
+ },
24
+ "access_token": {
25
+ "type": "string"
26
+ },
27
+ "reconnect_interval": {
28
+ "type": "number",
29
+ "default": 5000
30
+ },
31
+ "heartbeat_interval": {
32
+ "type": "number",
33
+ "default": 30000
34
+ }
35
+ }
36
+ }
package/src/index.ts CHANGED
@@ -1,74 +1,60 @@
1
- /**
2
- * OneBot11 适配器入口:单一适配器,支持正向 WS / 反向 WS(connection: ws | wss)
3
- */
4
- import { usePlugin, type Plugin, type Context, type ISceneManagement, createSceneManagementTools, registerDefaultScenePlatformPermitChecker, type ToolFeature } from 'zhin.js';
5
- import type { Router } from '@zhin.js/host-router';
6
- import { OneBot11Adapter } from './adapter.js';
1
+ export {
2
+ buildSendAction,
3
+ buildWsConnectOptions,
4
+ extractQuoteId,
5
+ formatInboundContent,
6
+ formatInboundMetadata,
7
+ formatInboundTarget,
8
+ formatOutboundSegments,
9
+ getChannelId,
10
+ isMessageEvent,
11
+ isOneBot11BotMentioned,
12
+ parseSendTarget,
13
+ resolveOneBot11Config,
14
+ senderDisplayName,
15
+ senderNickname,
16
+ senderUserId,
17
+ type OneBot11ActionRequest,
18
+ type OneBot11ActionResponse,
19
+ type OneBot11AdapterConfig,
20
+ type OneBot11ConfigBase,
21
+ type OneBot11EndpointConfig,
22
+ type OneBot11Event,
23
+ type OneBot11Segment,
24
+ type OneBot11Sender,
25
+ type OneBot11WireSegment,
26
+ type OneBot11WsConfig,
27
+ type OneBot11WssConfig,
28
+ type ParsedSendTarget,
29
+ type ResolvedOneBot11Config,
30
+ } from './protocol.js';
7
31
 
8
- export * from './types.js';
9
- export { OneBot11WsClient } from './endpoint-ws-client.js';
10
- export { OneBot11WsServer } from './endpoint-ws-server.js';
11
- export { OneBot11Adapter, type OneBot11Bot } from './adapter.js';
32
+ export {
33
+ OneBot11WsEndpoint,
34
+ type OneBot11WsEndpointOptions,
35
+ } from './ws-endpoint.js';
12
36
 
13
- declare module 'zhin.js' {
14
- namespace Plugin {
15
- interface Contexts {
16
- router: import('@zhin.js/host-router').Router;
17
- }
18
- }
19
- interface Adapters {
20
- onebot11: OneBot11Adapter;
21
- }
22
- }
37
+ export {
38
+ OneBot11WssEndpoint,
39
+ type OneBot11WssEndpointOptions,
40
+ } from './wss-endpoint.js';
23
41
 
24
- const plugin = usePlugin();
25
- const { provide, useContext } = plugin;
26
- provide({
27
- name: 'onebot11',
28
- description: 'OneBot11 协议适配器(正向 WS / 反向 WS)',
29
- mounted: async (p: Plugin) => {
30
- const adapter = new OneBot11Adapter(p);
31
- await adapter.start();
32
- return adapter;
33
- },
34
- dispose: async (adapter: OneBot11Adapter) => {
35
- await adapter.stop();
36
- },
37
- } as unknown as Context<'onebot11'>);
42
+ export type { OneBot11WsSocket, OneBot11WsCreateOptions } from './ws-types.js';
38
43
 
39
- useContext('tool', 'onebot11', (toolService: ToolFeature, onebot11: OneBot11Adapter) => {
40
- const disposers: (() => void)[] = [];
41
- disposers.push(registerDefaultScenePlatformPermitChecker('onebot11'));
42
- const sceneTools = createSceneManagementTools(
43
- onebot11 as unknown as ISceneManagement,
44
- 'onebot11',
45
- );
46
- disposers.push(...sceneTools.map(t => toolService.addTool(t, plugin.name)));
44
+ export {
45
+ callOneBot11WsAction,
46
+ decodeWsPayload,
47
+ handleOneBot11WsMessage,
48
+ rejectAllPending,
49
+ startOneBot11Heartbeat,
50
+ } from './ws-transport.js';
47
51
 
48
- // Platform-specific tool: set title
49
- disposers.push(toolService.addTool({
50
- name: 'onebot11_set_title',
51
- description: '设置 QQ 群成员的专属头衔。只有群主才能设置。',
52
- parameters: {
53
- type: 'object',
54
- properties: {
55
- endpoint_id: { type: 'string', description: 'Endpoint 名称', contextKey: 'endpointId' },
56
- group_id: { type: 'number', description: '目标群号' },
57
- user_id: { type: 'number', description: '目标成员 QQ号' },
58
- title: { type: 'string', description: '头衔文字' },
59
- },
60
- required: ['endpoint_id', 'group_id', 'user_id', 'title'],
61
- },
62
- platforms: ['onebot11'],
63
- tags: ['onebot11'],
64
- permissions: ['platform(onebot11,scene_owner)'],
65
- execute: async (args: Record<string, any>) => {
66
- const endpoint = onebot11.endpoints.get(args.endpoint_id);
67
- if (!endpoint) throw new Error(`Endpoint ${args.endpoint_id} 不存在`);
68
- const success = await endpoint.setTitle(args.group_id, args.user_id, args.title);
69
- return { success, message: success ? `已将 ${args.user_id} 的头衔设为 "${args.title}"` : '设置失败' };
70
- },
71
- }, plugin.name));
52
+ export { verifyOneBotAccessToken } from './wss-auth.js';
72
53
 
73
- return () => disposers.forEach(d => d());
74
- });
54
+ export {
55
+ getOnebot11AgentDeps,
56
+ registerOnebot11AgentEndpoint,
57
+ setOnebot11AgentDeps,
58
+ type Onebot11AgentDeps,
59
+ type Onebot11AgentEndpoint,
60
+ } from './onebot11-agent-deps.js';
@@ -0,0 +1,49 @@
1
+ /**
2
+ * Agent tool deps for onebot11 (set_title etc.).
3
+ * Endpoints register themselves on start; tools look up by config name / endpoint id.
4
+ */
5
+
6
+ export interface Onebot11AgentEndpoint {
7
+ setTitle(
8
+ groupId: number,
9
+ userId: number,
10
+ title: string,
11
+ duration?: number,
12
+ ): Promise<boolean>;
13
+ callApi(action: string, params?: Record<string, unknown>): Promise<unknown>;
14
+ }
15
+
16
+ export interface Onebot11AgentDeps {
17
+ getEndpoint: (endpointId: string) => Onebot11AgentEndpoint;
18
+ }
19
+
20
+ const endpoints = new Map<string, Onebot11AgentEndpoint>();
21
+ let override: Onebot11AgentDeps | null = null;
22
+
23
+ export function registerOnebot11AgentEndpoint(
24
+ endpointId: string,
25
+ endpoint: Onebot11AgentEndpoint,
26
+ ): () => void {
27
+ endpoints.set(endpointId, endpoint);
28
+ return () => {
29
+ if (endpoints.get(endpointId) === endpoint) {
30
+ endpoints.delete(endpointId);
31
+ }
32
+ };
33
+ }
34
+
35
+ /** Optional override used by tests / transitional callers. */
36
+ export function setOnebot11AgentDeps(deps: Onebot11AgentDeps): void {
37
+ override = deps;
38
+ }
39
+
40
+ export function getOnebot11AgentDeps(): Onebot11AgentDeps {
41
+ if (override) return override;
42
+ return {
43
+ getEndpoint(endpointId: string): Onebot11AgentEndpoint {
44
+ const registered = endpoints.get(endpointId);
45
+ if (!registered) throw new Error(`Endpoint ${endpointId} 不存在`);
46
+ return registered;
47
+ },
48
+ };
49
+ }