@zhin.js/adapter-onebot11 4.0.2 → 5.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 (56) hide show
  1. package/CHANGELOG.md +62 -0
  2. package/README.md +41 -380
  3. package/adapters/onebot11.ts +39 -0
  4. package/agent/tools/set_title.ts +2 -2
  5. package/lib/index.d.ts +7 -0
  6. package/lib/index.js +6 -0
  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 +45 -25
  22. package/plugin.ts +13 -0
  23. package/schema.json +61 -0
  24. package/src/index.ts +56 -53
  25. package/src/onebot11-agent-deps.ts +39 -7
  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/agent/tools/set_title.js +0 -21
  33. package/lib/agent/tools/set_title.js.map +0 -1
  34. package/lib/src/adapter.js +0 -101
  35. package/lib/src/adapter.js.map +0 -1
  36. package/lib/src/endpoint-ws-client.js +0 -394
  37. package/lib/src/endpoint-ws-client.js.map +0 -1
  38. package/lib/src/endpoint-ws-server.js +0 -341
  39. package/lib/src/endpoint-ws-server.js.map +0 -1
  40. package/lib/src/index.js +0 -41
  41. package/lib/src/index.js.map +0 -1
  42. package/lib/src/onebot-get-msg.js +0 -27
  43. package/lib/src/onebot-get-msg.js.map +0 -1
  44. package/lib/src/onebot11-agent-deps.js +0 -10
  45. package/lib/src/onebot11-agent-deps.js.map +0 -1
  46. package/lib/src/segment-mapper.js +0 -2
  47. package/lib/src/segment-mapper.js.map +0 -1
  48. package/lib/src/types.js +0 -5
  49. package/lib/src/types.js.map +0 -1
  50. package/plugin.yml +0 -3
  51. package/src/adapter.ts +0 -114
  52. package/src/endpoint-ws-client.ts +0 -421
  53. package/src/endpoint-ws-server.ts +0 -382
  54. package/src/onebot-get-msg.ts +0 -33
  55. package/src/segment-mapper.ts +0 -1
  56. package/src/types.ts +0 -60
@@ -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.2",
4
- "description": "Zhin.js adapter for OneBot v11 protocol",
3
+ "version": "5.0.0",
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,14 +13,14 @@
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
- "plugin.yml",
21
+ "agent",
21
22
  "README.md",
22
- "CHANGELOG.md",
23
- "agent"
23
+ "CHANGELOG.md"
24
24
  ],
25
25
  "keywords": [
26
26
  "zhin",
@@ -32,39 +32,39 @@
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.1.0",
44
+ "@zhin.js/core": "1.4.0",
45
+ "@zhin.js/host-http": "1.0.2",
46
+ "@zhin.js/plugin-runtime": "1.1.0",
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
+ "vitest": "^4.1.10",
53
54
  "zod": "^4.4.3",
54
- "@zhin.js/cli": "1.0.93",
55
- "@zhin.js/client": "2.0.5",
56
- "@zhin.js/host-router": "2.0.3",
57
- "zhin.js": "4.1.2",
58
- "@zhin.js/agent": "1.0.3"
55
+ "@zhin.js/agent": "1.0.5",
56
+ "@zhin.js/host-http": "1.0.2"
59
57
  },
60
58
  "peerDependencies": {
61
59
  "zod": "^4.0.0",
62
- "zhin.js": "4.1.2",
63
- "@zhin.js/agent": "1.0.3",
64
- "@zhin.js/host-router": "2.0.3"
60
+ "@zhin.js/adapter": "1.1.0",
61
+ "@zhin.js/core": "1.4.0",
62
+ "@zhin.js/plugin-runtime": "1.1.0",
63
+ "zhin.js": "5.0.0",
64
+ "@zhin.js/agent": "1.0.5"
65
65
  },
66
66
  "peerDependenciesMeta": {
67
- "@zhin.js/host-router": {
67
+ "zhin.js": {
68
68
  "optional": true
69
69
  },
70
70
  "@zhin.js/agent": {
@@ -74,6 +74,11 @@
74
74
  "optional": true
75
75
  }
76
76
  },
77
+ "author": {
78
+ "name": "lc-cn",
79
+ "email": "admin@liucl.cn",
80
+ "url": "https://github.com/lc-cn"
81
+ },
77
82
  "publishConfig": {
78
83
  "access": "public",
79
84
  "registry": "https://registry.npmjs.org"
@@ -81,8 +86,23 @@
81
86
  "engines": {
82
87
  "node": "^20.19.0 || >=22.12.0"
83
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
+ },
84
103
  "scripts": {
85
- "build": "zhin build",
86
- "clean": "rimraf lib"
104
+ "build": "tsc",
105
+ "clean": "rimraf lib",
106
+ "test": "NODE_OPTIONS=--experimental-strip-types vitest run --root ../../.. plugins/adapters/onebot11/tests"
87
107
  }
88
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,61 @@
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": [
9
+ "ws",
10
+ "wss"
11
+ ],
12
+ "default": "ws",
13
+ "description": "connection: ws (forward WS client, default) or wss (reverse WS via httpHostToken)"
14
+ },
15
+ "reconnect_interval": {
16
+ "type": "number",
17
+ "default": 5000
18
+ },
19
+ "heartbeat_interval": {
20
+ "type": "number",
21
+ "default": 30000
22
+ },
23
+ "endpoints": {
24
+ "type": "array",
25
+ "description": "多账号:一个插件实例挂多个 endpoint。每项与顶层字段同构(name 必填,其余覆盖顶层)",
26
+ "items": {
27
+ "type": "object",
28
+ "additionalProperties": true,
29
+ "properties": {
30
+ "name": {
31
+ "type": "string",
32
+ "description": "OneBot11 bot name"
33
+ },
34
+ "url": {
35
+ "type": "string",
36
+ "description": "OneBot implementation WebSocket URL (required for connection: ws)"
37
+ },
38
+ "path": {
39
+ "type": "string",
40
+ "description": "WS path for reverse-wss (connection: wss)"
41
+ },
42
+ "access_token": {
43
+ "type": "string",
44
+ "description": "OneBot access token"
45
+ }
46
+ },
47
+ "required": [
48
+ "name"
49
+ ]
50
+ }
51
+ },
52
+ "commandPrefix": {
53
+ "type": "string",
54
+ "default": "",
55
+ "description": "命令前缀(默认 '' 无前缀,任意文本按命令匹配;如 '/' 要求 / 开头)。endpoints[i] 可逐项覆盖"
56
+ }
57
+ },
58
+ "required": [
59
+ "endpoints"
60
+ ]
61
+ }
package/src/index.ts CHANGED
@@ -1,57 +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';
7
- import { setOnebot11AgentDeps } from './onebot11-agent-deps.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';
8
31
 
9
- export * from './types.js';
10
- export { OneBot11WsClient } from './endpoint-ws-client.js';
11
- export { OneBot11WsServer } from './endpoint-ws-server.js';
12
- export { OneBot11Adapter, type OneBot11Bot } from './adapter.js';
32
+ export {
33
+ OneBot11WsEndpoint,
34
+ type OneBot11WsEndpointOptions,
35
+ } from './ws-endpoint.js';
13
36
 
14
- declare module 'zhin.js' {
15
- namespace Plugin {
16
- interface Contexts {
17
- router: import('@zhin.js/host-router').Router;
18
- }
19
- }
20
- interface Adapters {
21
- onebot11: OneBot11Adapter;
22
- }
23
- }
37
+ export {
38
+ OneBot11WssEndpoint,
39
+ type OneBot11WssEndpointOptions,
40
+ } from './wss-endpoint.js';
24
41
 
25
- const plugin = usePlugin();
26
- const { provide, useContext } = plugin;
27
- provide({
28
- name: 'onebot11',
29
- description: 'OneBot11 协议适配器(正向 WS / 反向 WS)',
30
- mounted: async (p: Plugin) => {
31
- const adapter = new OneBot11Adapter(p);
32
- await adapter.start();
33
- return adapter;
34
- },
35
- dispose: async (adapter: OneBot11Adapter) => {
36
- await adapter.stop();
37
- },
38
- } as unknown as Context<'onebot11'>);
42
+ export type { OneBot11WsSocket, OneBot11WsCreateOptions } from './ws-types.js';
39
43
 
40
- useContext('tool', 'onebot11', (toolService: ToolFeature, onebot11: OneBot11Adapter) => {
41
- const disposers: (() => void)[] = [];
42
- disposers.push(registerDefaultScenePlatformPermitChecker('onebot11'));
43
- setOnebot11AgentDeps({
44
- getEndpoint: (endpointId) => {
45
- const endpoint = onebot11.endpoints.get(endpointId);
46
- if (!endpoint) throw new Error(`Endpoint ${endpointId} 不存在`);
47
- return endpoint;
48
- },
49
- getAdapter: () => onebot11,
50
- });
51
- const sceneTools = createSceneManagementTools(
52
- onebot11 as unknown as ISceneManagement,
53
- 'onebot11',
54
- );
55
- disposers.push(...sceneTools.map(t => toolService.addTool(t, plugin.name)));
56
- return () => disposers.forEach(d => d());
57
- });
44
+ export {
45
+ callOneBot11WsAction,
46
+ decodeWsPayload,
47
+ handleOneBot11WsMessage,
48
+ rejectAllPending,
49
+ startOneBot11Heartbeat,
50
+ } from './ws-transport.js';
51
+
52
+ export { verifyOneBotAccessToken } from './wss-auth.js';
53
+
54
+ export {
55
+ getOnebot11AgentDeps,
56
+ registerOnebot11AgentEndpoint,
57
+ setOnebot11AgentDeps,
58
+ type Onebot11AgentDeps,
59
+ type Onebot11AgentEndpoint,
60
+ } from './onebot11-agent-deps.js';
@@ -1,17 +1,49 @@
1
- import type { OneBot11Adapter, OneBot11Bot } from './adapter.js';
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
+ }
2
15
 
3
16
  export interface Onebot11AgentDeps {
4
- getEndpoint: (endpointId: string) => OneBot11Bot;
5
- getAdapter: () => OneBot11Adapter;
17
+ getEndpoint: (endpointId: string) => Onebot11AgentEndpoint;
6
18
  }
7
19
 
8
- let _deps: Onebot11AgentDeps | null = null;
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
+ }
9
34
 
35
+ /** Optional override used by tests / transitional callers. */
10
36
  export function setOnebot11AgentDeps(deps: Onebot11AgentDeps): void {
11
- _deps = deps;
37
+ override = deps;
12
38
  }
13
39
 
14
40
  export function getOnebot11AgentDeps(): Onebot11AgentDeps {
15
- if (!_deps) throw new Error('onebot11 agent deps not initialized');
16
- return _deps;
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
+ };
17
49
  }