@zhin.js/adapter-onebot12 3.0.1 → 3.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 (70) hide show
  1. package/CHANGELOG.md +440 -0
  2. package/README.md +38 -68
  3. package/adapters/onebot12.ts +50 -0
  4. package/lib/index.d.ts +6 -19
  5. package/lib/index.js +5 -27
  6. package/lib/protocol.d.ts +151 -0
  7. package/lib/protocol.js +242 -0
  8. package/lib/webhook.d.ts +25 -0
  9. package/lib/webhook.js +140 -0
  10. package/lib/ws-endpoint.d.ts +25 -0
  11. package/lib/ws-endpoint.js +240 -0
  12. package/lib/ws-types.d.ts +16 -0
  13. package/lib/ws-types.js +1 -0
  14. package/lib/wss-auth.d.ts +2 -0
  15. package/lib/wss-auth.js +16 -0
  16. package/lib/wss-endpoint.d.ts +24 -0
  17. package/lib/wss-endpoint.js +193 -0
  18. package/package.json +42 -16
  19. package/plugin.ts +8 -0
  20. package/schema.json +40 -0
  21. package/src/index.ts +46 -37
  22. package/src/protocol.ts +387 -0
  23. package/src/webhook.ts +180 -0
  24. package/src/ws-endpoint.ts +287 -0
  25. package/src/ws-types.ts +19 -0
  26. package/src/wss-auth.ts +17 -0
  27. package/src/wss-endpoint.ts +226 -0
  28. package/lib/adapter.d.ts +0 -20
  29. package/lib/adapter.d.ts.map +0 -1
  30. package/lib/adapter.js +0 -43
  31. package/lib/adapter.js.map +0 -1
  32. package/lib/api.d.ts +0 -14
  33. package/lib/api.d.ts.map +0 -1
  34. package/lib/api.js +0 -34
  35. package/lib/api.js.map +0 -1
  36. package/lib/endpoint-webhook.d.ts +0 -25
  37. package/lib/endpoint-webhook.d.ts.map +0 -1
  38. package/lib/endpoint-webhook.js +0 -122
  39. package/lib/endpoint-webhook.js.map +0 -1
  40. package/lib/endpoint-ws.d.ts +0 -26
  41. package/lib/endpoint-ws.d.ts.map +0 -1
  42. package/lib/endpoint-ws.js +0 -214
  43. package/lib/endpoint-ws.js.map +0 -1
  44. package/lib/endpoint-wss.d.ts +0 -26
  45. package/lib/endpoint-wss.d.ts.map +0 -1
  46. package/lib/endpoint-wss.js +0 -191
  47. package/lib/endpoint-wss.js.map +0 -1
  48. package/lib/index.d.ts.map +0 -1
  49. package/lib/index.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 -77
  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/lib/utils.d.ts +0 -45
  59. package/lib/utils.d.ts.map +0 -1
  60. package/lib/utils.js +0 -62
  61. package/lib/utils.js.map +0 -1
  62. package/src/adapter.ts +0 -56
  63. package/src/api.ts +0 -48
  64. package/src/endpoint-webhook.ts +0 -136
  65. package/src/endpoint-ws.ts +0 -228
  66. package/src/endpoint-wss.ts +0 -208
  67. package/src/segment-mapper.ts +0 -1
  68. package/src/types.ts +0 -86
  69. package/src/utils.ts +0 -89
  70. /package/{skills/onebot12/SKILL.md → agent/skills/onebot12.md} +0 -0
@@ -0,0 +1,240 @@
1
+ /**
2
+ * OneBot12 WS client endpoint — outbound connect to OneBot implementation.
3
+ */
4
+ import WebSocket from 'ws';
5
+ import { clearInterval, clearTimeout } from 'node:timers';
6
+ import { formatCompact, getLogger } from '@zhin.js/logger';
7
+ import { buildSendMessageParams, buildWsConnectOptions, formatInboundContent, formatInboundTarget, formatOutboundSegments, isBotMentioned, isMessageEvent, senderNickname, senderUserId, } from './protocol.js';
8
+ import { WS_OPEN, } from './ws-types.js';
9
+ const logger = getLogger('onebot12');
10
+ export class OneBot12WsEndpoint {
11
+ #options;
12
+ #ws;
13
+ #reconnectTimer;
14
+ #heartbeatTimer;
15
+ #requestId = 0;
16
+ #pending = new Map();
17
+ #open = false;
18
+ #started = false;
19
+ #stopping = false;
20
+ constructor(options) {
21
+ this.#options = options;
22
+ }
23
+ async start() {
24
+ if (this.#started)
25
+ return;
26
+ this.#started = true;
27
+ this.#stopping = false;
28
+ await this.#connect();
29
+ }
30
+ open() {
31
+ this.#open = true;
32
+ }
33
+ close() {
34
+ this.#open = false;
35
+ }
36
+ async stop() {
37
+ this.#open = false;
38
+ this.#stopping = true;
39
+ this.#started = false;
40
+ if (this.#reconnectTimer) {
41
+ clearTimeout(this.#reconnectTimer);
42
+ this.#reconnectTimer = undefined;
43
+ }
44
+ if (this.#heartbeatTimer) {
45
+ clearInterval(this.#heartbeatTimer);
46
+ this.#heartbeatTimer = undefined;
47
+ }
48
+ for (const [, pending] of this.#pending) {
49
+ clearTimeout(pending.timeout);
50
+ pending.reject(new Error('连接已关闭'));
51
+ }
52
+ this.#pending.clear();
53
+ if (this.#ws) {
54
+ try {
55
+ this.#ws.close();
56
+ }
57
+ catch {
58
+ /* ignore */
59
+ }
60
+ this.#ws = undefined;
61
+ }
62
+ logger.debug(formatCompact({ op: 'disconnect', endpoint: this.#options.config.name }));
63
+ }
64
+ async send({ target, payload }) {
65
+ const message = formatOutboundSegments(payload);
66
+ const params = buildSendMessageParams(target, message);
67
+ const data = await this.#callAction('send_message', params);
68
+ const messageId = data?.message_id ?? '';
69
+ logger.debug(formatCompact({
70
+ op: 'onebot12_send',
71
+ endpoint: this.#options.config.name,
72
+ target,
73
+ messageId,
74
+ }));
75
+ return messageId;
76
+ }
77
+ /** Test / internal: admit a parsed event when the endpoint is open. */
78
+ admit(ev) {
79
+ if (!this.#open || !isMessageEvent(ev))
80
+ return;
81
+ const target = formatInboundTarget(ev);
82
+ const content = formatInboundContent(ev);
83
+ const nickname = senderNickname(ev);
84
+ const mentioned = isBotMentioned(ev);
85
+ void this.#options.gateway.receive({
86
+ adapter: this.#options.id,
87
+ target,
88
+ content,
89
+ sender: senderUserId(ev),
90
+ id: ev.message_id,
91
+ metadata: Object.freeze({
92
+ detail_type: ev.detail_type,
93
+ user_id: ev.user_id,
94
+ group_id: ev.group_id,
95
+ channel_id: ev.channel_id,
96
+ guild_id: ev.guild_id,
97
+ endpoint: this.#options.config.name,
98
+ time: ev.time,
99
+ ...(nickname ? { nickname } : {}),
100
+ ...(mentioned ? { mentioned: true } : {}),
101
+ }),
102
+ }).catch((err) => {
103
+ logger.warn(formatCompact({
104
+ op: 'onebot12_gateway_receive_failed',
105
+ target,
106
+ error: err instanceof Error ? err.message : String(err),
107
+ }));
108
+ });
109
+ }
110
+ async #connect() {
111
+ const { url, headers, safeUrl } = buildWsConnectOptions(this.#options.config);
112
+ const create = this.#options.createWebSocket
113
+ ?? ((connectUrl, options) => new WebSocket(connectUrl, { headers: options.headers }));
114
+ await new Promise((resolve, reject) => {
115
+ let settled = false;
116
+ const ws = create(url, { headers });
117
+ this.#ws = ws;
118
+ ws.on('open', () => {
119
+ if (settled)
120
+ return;
121
+ settled = true;
122
+ logger.debug(formatCompact({
123
+ endpoint: this.#options.config.name,
124
+ mode: 'ws',
125
+ url: safeUrl,
126
+ }));
127
+ this.#startHeartbeat();
128
+ resolve();
129
+ });
130
+ ws.on('message', (data) => {
131
+ this.#onMessage(data);
132
+ });
133
+ ws.on('close', (code, reason) => {
134
+ const reasonStr = typeof reason === 'string'
135
+ ? reason
136
+ : Buffer.isBuffer(reason)
137
+ ? reason.toString()
138
+ : String(reason ?? '');
139
+ const codeNum = typeof code === 'number' ? code : Number(code ?? 0);
140
+ logger.warn(formatCompact({
141
+ op: 'disconnect',
142
+ endpoint: this.#options.config.name,
143
+ code: codeNum,
144
+ error: reasonStr || 'closed',
145
+ reconnect_ms: this.#options.config.reconnect_interval,
146
+ }));
147
+ if (!settled) {
148
+ settled = true;
149
+ reject(new Error(`OneBot12 WS 关闭: ${codeNum} ${reasonStr}`));
150
+ }
151
+ this.#scheduleReconnect();
152
+ });
153
+ ws.on('error', (err) => {
154
+ const error = err instanceof Error ? err : new Error(String(err));
155
+ logger.warn(formatCompact({
156
+ op: 'ws_error',
157
+ endpoint: this.#options.config.name,
158
+ ok: false,
159
+ error: error.message,
160
+ }));
161
+ if (!settled) {
162
+ settled = true;
163
+ reject(error);
164
+ }
165
+ });
166
+ });
167
+ }
168
+ #onMessage(data) {
169
+ try {
170
+ const raw = typeof data === 'string'
171
+ ? data
172
+ : Buffer.isBuffer(data)
173
+ ? data.toString()
174
+ : data instanceof ArrayBuffer
175
+ ? new TextDecoder().decode(data)
176
+ : String(data ?? '');
177
+ const msg = JSON.parse(raw);
178
+ if ('echo' in msg && typeof msg.echo === 'string') {
179
+ const resp = msg;
180
+ const pending = this.#pending.get(resp.echo);
181
+ if (pending) {
182
+ this.#pending.delete(resp.echo);
183
+ clearTimeout(pending.timeout);
184
+ if (resp.status === 'ok')
185
+ pending.resolve(resp.data);
186
+ else
187
+ pending.reject(new Error(`OneBot12 retcode=${resp.retcode}: ${resp.message}`));
188
+ }
189
+ return;
190
+ }
191
+ this.admit(msg);
192
+ }
193
+ catch (error) {
194
+ logger.warn(formatCompact({
195
+ op: 'onebot12_parse_failed',
196
+ endpoint: this.#options.config.name,
197
+ error: error instanceof Error ? error.message : String(error),
198
+ }));
199
+ }
200
+ }
201
+ #callAction(action, params) {
202
+ if (!this.#ws || this.#ws.readyState !== WS_OPEN) {
203
+ return Promise.reject(new Error('WebSocket 未连接'));
204
+ }
205
+ const echo = `ob12_${++this.#requestId}`;
206
+ const req = { action, params, echo };
207
+ return new Promise((resolve, reject) => {
208
+ const timeout = setTimeout(() => {
209
+ this.#pending.delete(echo);
210
+ reject(new Error(`OneBot12 动作超时: ${action}`));
211
+ }, 30_000);
212
+ this.#pending.set(echo, { resolve, reject, timeout });
213
+ this.#ws.send(JSON.stringify(req));
214
+ });
215
+ }
216
+ #startHeartbeat() {
217
+ if (this.#heartbeatTimer) {
218
+ clearInterval(this.#heartbeatTimer);
219
+ }
220
+ this.#heartbeatTimer = setInterval(() => {
221
+ this.#callAction('get_status', {}).catch(() => { });
222
+ }, this.#options.config.heartbeat_interval);
223
+ }
224
+ #scheduleReconnect() {
225
+ if (this.#stopping || !this.#started || this.#reconnectTimer)
226
+ return;
227
+ const delay = this.#options.config.reconnect_interval;
228
+ this.#reconnectTimer = setTimeout(() => {
229
+ this.#reconnectTimer = undefined;
230
+ void this.#connect().catch((err) => {
231
+ logger.warn(formatCompact({
232
+ op: 'reconnect',
233
+ endpoint: this.#options.config.name,
234
+ ok: false,
235
+ error: err instanceof Error ? err.message : String(err),
236
+ }));
237
+ });
238
+ }, delay);
239
+ }
240
+ }
@@ -0,0 +1,16 @@
1
+ /** Minimal WS surface used by the endpoint (real `ws` or test mock). */
2
+ export interface OneBot12WsSocket {
3
+ readonly readyState: number;
4
+ send(data: string): void;
5
+ close(code?: number, reason?: string): void;
6
+ on(event: 'open' | 'message' | 'close' | 'error', listener: (...args: unknown[]) => void): void;
7
+ }
8
+ export interface OneBot12WsCreateOptions {
9
+ readonly headers?: Record<string, string>;
10
+ }
11
+ export declare const WS_OPEN = 1;
12
+ export interface OneBot12PendingAction {
13
+ resolve: (value: unknown) => void;
14
+ reject: (err: Error) => void;
15
+ timeout: NodeJS.Timeout;
16
+ }
@@ -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,24 @@
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 OneBot12Event, type OneBot12WssConfig } from './protocol.js';
6
+ export interface OneBot12WssEndpointOptions {
7
+ readonly id: CapabilityId;
8
+ readonly gateway: MessageGateway;
9
+ readonly http: HttpHost;
10
+ readonly config: OneBot12WssConfig;
11
+ }
12
+ export declare class OneBot12WssEndpoint implements EndpointInstance {
13
+ #private;
14
+ constructor(options: OneBot12WssEndpointOptions);
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
+ admit(ev: OneBot12Event): void;
24
+ }
@@ -0,0 +1,193 @@
1
+ /**
2
+ * OneBot12 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 { buildSendMessageParams, formatInboundContent, formatInboundTarget, formatOutboundSegments, isBotMentioned, isMessageEvent, senderNickname, senderUserId, } from './protocol.js';
7
+ import { verifyOneBotAccessToken } from './wss-auth.js';
8
+ import { WS_OPEN } from './ws-types.js';
9
+ const logger = getLogger('onebot12');
10
+ export class OneBot12WssEndpoint {
11
+ #options;
12
+ #ws;
13
+ #wsRelease;
14
+ #heartbeatTimer;
15
+ #requestId = 0;
16
+ #pending = new Map();
17
+ #open = false;
18
+ #started = false;
19
+ constructor(options) {
20
+ this.#options = options;
21
+ }
22
+ async start() {
23
+ if (this.#started)
24
+ return;
25
+ this.#started = true;
26
+ const handle = this.#options.http.ws(this.#options.config.path);
27
+ this.#wsRelease = handle.onConnection((connection) => {
28
+ this.#acceptConnection(connection);
29
+ });
30
+ logger.info(formatCompact({
31
+ op: 'listen',
32
+ endpoint: this.#options.config.name,
33
+ mode: 'wss',
34
+ path: this.#options.config.path,
35
+ }));
36
+ }
37
+ open() {
38
+ this.#open = true;
39
+ }
40
+ close() {
41
+ this.#open = false;
42
+ }
43
+ async stop() {
44
+ this.#open = false;
45
+ this.#wsRelease?.();
46
+ this.#wsRelease = undefined;
47
+ if (this.#heartbeatTimer) {
48
+ clearInterval(this.#heartbeatTimer);
49
+ this.#heartbeatTimer = undefined;
50
+ }
51
+ for (const [, pending] of this.#pending) {
52
+ clearTimeout(pending.timeout);
53
+ pending.reject(new Error('连接已关闭'));
54
+ }
55
+ this.#pending.clear();
56
+ if (this.#ws) {
57
+ try {
58
+ this.#ws.close();
59
+ }
60
+ catch {
61
+ /* ignore */
62
+ }
63
+ this.#ws = undefined;
64
+ }
65
+ this.#started = false;
66
+ }
67
+ async send({ target, payload }) {
68
+ const message = formatOutboundSegments(payload);
69
+ const params = buildSendMessageParams(target, message);
70
+ const data = await this.#callAction('send_message', params);
71
+ return data?.message_id ?? '';
72
+ }
73
+ admit(ev) {
74
+ if (!this.#open || !isMessageEvent(ev))
75
+ return;
76
+ const target = formatInboundTarget(ev);
77
+ const nickname = senderNickname(ev);
78
+ const mentioned = isBotMentioned(ev);
79
+ void this.#options.gateway.receive({
80
+ adapter: this.#options.id,
81
+ target,
82
+ content: formatInboundContent(ev),
83
+ sender: senderUserId(ev),
84
+ id: ev.message_id,
85
+ metadata: Object.freeze({
86
+ detail_type: ev.detail_type,
87
+ user_id: ev.user_id,
88
+ group_id: ev.group_id,
89
+ channel_id: ev.channel_id,
90
+ guild_id: ev.guild_id,
91
+ endpoint: this.#options.config.name,
92
+ time: ev.time,
93
+ ...(nickname ? { nickname } : {}),
94
+ ...(mentioned ? { mentioned: true } : {}),
95
+ }),
96
+ }).catch((err) => {
97
+ logger.warn(formatCompact({
98
+ op: 'onebot12_gateway_receive_failed',
99
+ target,
100
+ error: err instanceof Error ? err.message : String(err),
101
+ }));
102
+ });
103
+ }
104
+ #acceptConnection(connection) {
105
+ if (!verifyOneBotAccessToken(this.#options.config.access_token, connection.request)) {
106
+ connection.socket.close(4003, 'Unauthorized');
107
+ return;
108
+ }
109
+ const socket = connection.socket;
110
+ if (this.#ws) {
111
+ try {
112
+ this.#ws.close();
113
+ }
114
+ catch {
115
+ /* ignore */
116
+ }
117
+ }
118
+ this.#ws = socket;
119
+ this.#startHeartbeat();
120
+ socket.on('message', (data) => {
121
+ this.#onMessage(data);
122
+ });
123
+ socket.on('close', () => {
124
+ if (this.#ws === socket) {
125
+ this.#ws = undefined;
126
+ if (this.#heartbeatTimer) {
127
+ clearInterval(this.#heartbeatTimer);
128
+ this.#heartbeatTimer = undefined;
129
+ }
130
+ }
131
+ });
132
+ logger.debug(formatCompact({
133
+ endpoint: this.#options.config.name,
134
+ mode: 'wss',
135
+ peer: connection.request.socket.remoteAddress,
136
+ }));
137
+ }
138
+ #onMessage(data) {
139
+ try {
140
+ const raw = typeof data === 'string'
141
+ ? data
142
+ : Buffer.isBuffer(data)
143
+ ? data.toString()
144
+ : data instanceof ArrayBuffer
145
+ ? new TextDecoder().decode(data)
146
+ : String(data ?? '');
147
+ const msg = JSON.parse(raw);
148
+ if ('echo' in msg && typeof msg.echo === 'string') {
149
+ const resp = msg;
150
+ const pending = this.#pending.get(resp.echo);
151
+ if (pending) {
152
+ this.#pending.delete(resp.echo);
153
+ clearTimeout(pending.timeout);
154
+ if (resp.status === 'ok')
155
+ pending.resolve(resp.data);
156
+ else
157
+ pending.reject(new Error(`OneBot12 retcode=${resp.retcode}: ${resp.message}`));
158
+ }
159
+ return;
160
+ }
161
+ this.admit(msg);
162
+ }
163
+ catch (error) {
164
+ logger.warn(formatCompact({
165
+ op: 'onebot12_parse_failed',
166
+ endpoint: this.#options.config.name,
167
+ error: error instanceof Error ? error.message : String(error),
168
+ }));
169
+ }
170
+ }
171
+ #callAction(action, params) {
172
+ if (!this.#ws || this.#ws.readyState !== WS_OPEN) {
173
+ return Promise.reject(new Error('WebSocket 未连接'));
174
+ }
175
+ const echo = `ob12_${++this.#requestId}`;
176
+ const req = { action, params, echo };
177
+ return new Promise((resolve, reject) => {
178
+ const timeout = setTimeout(() => {
179
+ this.#pending.delete(echo);
180
+ reject(new Error(`OneBot12 动作超时: ${action}`));
181
+ }, 30_000);
182
+ this.#pending.set(echo, { resolve, reject, timeout });
183
+ this.#ws.send(JSON.stringify(req));
184
+ });
185
+ }
186
+ #startHeartbeat() {
187
+ if (this.#heartbeatTimer)
188
+ clearInterval(this.#heartbeatTimer);
189
+ this.#heartbeatTimer = setInterval(() => {
190
+ this.#callAction('get_status', {}).catch(() => { });
191
+ }, this.#options.config.heartbeat_interval);
192
+ }
193
+ }
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@zhin.js/adapter-onebot12",
3
- "version": "3.0.1",
4
- "description": "Zhin.js adapter for OneBot 12 protocol",
3
+ "version": "3.0.3",
4
+ "description": "Zhin.js OneBot 12 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,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
- "skills",
21
- "README.md"
21
+ "agent",
22
+ "README.md",
23
+ "CHANGELOG.md"
22
24
  ],
23
25
  "keywords": [
24
26
  "zhin",
@@ -36,23 +38,28 @@
36
38
  "directory": "plugins/adapters/onebot12"
37
39
  },
38
40
  "dependencies": {
39
- "ws": "^8.21.0"
41
+ "ws": "^8.21.0",
42
+ "@zhin.js/adapter": "1.0.1",
43
+ "@zhin.js/core": "1.3.5",
44
+ "@zhin.js/host-http": "1.0.1",
45
+ "@zhin.js/logger": "1.0.75",
46
+ "@zhin.js/plugin-runtime": "1.0.1"
40
47
  },
41
48
  "devDependencies": {
42
49
  "@types/node": "^26.1.0",
43
50
  "@types/ws": "^8.18.1",
44
51
  "typescript": "^6.0.3",
45
- "@zhin.js/cli": "1.0.92",
46
- "@zhin.js/client": "2.0.4",
47
- "@zhin.js/host-router": "2.0.2",
48
- "zhin.js": "4.1.1"
52
+ "vitest": "^4.1.10",
53
+ "@zhin.js/host-http": "1.0.1"
49
54
  },
50
55
  "peerDependencies": {
51
- "zhin.js": "4.1.1",
52
- "@zhin.js/host-router": "2.0.2"
56
+ "@zhin.js/adapter": "1.0.1",
57
+ "@zhin.js/core": "1.3.5",
58
+ "@zhin.js/plugin-runtime": "1.0.1",
59
+ "zhin.js": "4.1.3"
53
60
  },
54
61
  "peerDependenciesMeta": {
55
- "@zhin.js/host-router": {
62
+ "zhin.js": {
56
63
  "optional": true
57
64
  }
58
65
  },
@@ -61,11 +68,30 @@
61
68
  "email": "admin@liucl.cn",
62
69
  "url": "https://github.com/lc-cn"
63
70
  },
71
+ "publishConfig": {
72
+ "access": "public",
73
+ "registry": "https://registry.npmjs.org"
74
+ },
64
75
  "engines": {
65
76
  "node": "^20.19.0 || >=22.12.0"
66
77
  },
78
+ "zhin": {
79
+ "protocol": 1,
80
+ "type": "plugin",
81
+ "entry": "./plugin.ts",
82
+ "engine": "^1.0.0",
83
+ "runtime": "trusted",
84
+ "features": [
85
+ {
86
+ "package": "@zhin.js/adapter",
87
+ "api": "^1.0.0"
88
+ }
89
+ ],
90
+ "plugins": []
91
+ },
67
92
  "scripts": {
68
- "build": "zhin build",
69
- "clean": "rimraf lib"
93
+ "build": "tsc",
94
+ "clean": "rimraf lib",
95
+ "test": "NODE_OPTIONS=--experimental-strip-types vitest run --root ../../.. plugins/adapters/onebot12/tests"
70
96
  }
71
97
  }
package/plugin.ts ADDED
@@ -0,0 +1,8 @@
1
+ import { definePlugin } from '@zhin.js/plugin-runtime';
2
+
3
+ export default definePlugin({
4
+ name: 'onebot12',
5
+ metadata: {
6
+ displayName: 'OneBot 12 Adapter',
7
+ },
8
+ });
package/schema.json ADDED
@@ -0,0 +1,40 @@
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", "webhook", "wss"],
9
+ "default": "ws",
10
+ "description": "ws (default), webhook (httpHostToken POST), or wss (reverse WS via httpHostToken)"
11
+ },
12
+ "name": {
13
+ "type": "string",
14
+ "default": "onebot12-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": "HTTP/WS path for webhook or reverse-wss"
23
+ },
24
+ "api_url": {
25
+ "type": "string",
26
+ "description": "HTTP action endpoint for webhook outbound (required for connection: webhook send)"
27
+ },
28
+ "access_token": {
29
+ "type": "string"
30
+ },
31
+ "reconnect_interval": {
32
+ "type": "number",
33
+ "default": 5000
34
+ },
35
+ "heartbeat_interval": {
36
+ "type": "number",
37
+ "default": 30000
38
+ }
39
+ }
40
+ }