@zhin.js/adapter-onebot12 1.0.0 → 1.1.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 (90) hide show
  1. package/CHANGELOG.md +855 -0
  2. package/README.md +59 -69
  3. package/adapters/onebot12.js +50 -0
  4. package/adapters/onebot12.ts +64 -0
  5. package/{skills/onebot12/SKILL.md → agent/skills/onebot12.md} +1 -1
  6. package/commands/endpoint/add/[id].js +3 -0
  7. package/commands/endpoint/add/[id].ts +3 -0
  8. package/commands/endpoint/list.js +3 -0
  9. package/commands/endpoint/list.ts +3 -0
  10. package/commands/endpoint/remove/[id].js +3 -0
  11. package/commands/endpoint/remove/[id].ts +3 -0
  12. package/lib/client.d.ts +20 -0
  13. package/lib/client.js +33 -0
  14. package/lib/content-port.d.ts +4 -0
  15. package/lib/content-port.js +45 -0
  16. package/lib/endpoint-management.d.ts +16 -0
  17. package/lib/endpoint-management.js +47 -0
  18. package/lib/index.d.ts +10 -19
  19. package/lib/index.js +7 -27
  20. package/lib/onebot12-endpoint-commands.d.ts +1 -0
  21. package/lib/onebot12-endpoint-commands.js +25 -0
  22. package/lib/onebot12-runtime-state.d.ts +1 -0
  23. package/lib/onebot12-runtime-state.js +6 -0
  24. package/lib/protocol.d.ts +173 -0
  25. package/lib/protocol.js +415 -0
  26. package/lib/side-event-dispatch.d.ts +7 -0
  27. package/lib/side-event-dispatch.js +36 -0
  28. package/lib/webhook.d.ts +23 -0
  29. package/lib/webhook.js +161 -0
  30. package/lib/ws-endpoint.d.ts +22 -0
  31. package/lib/ws-endpoint.js +255 -0
  32. package/lib/ws-types.d.ts +16 -0
  33. package/lib/ws-types.js +1 -0
  34. package/lib/wss-auth.d.ts +2 -0
  35. package/lib/wss-auth.js +16 -0
  36. package/lib/wss-endpoint.d.ts +25 -0
  37. package/lib/wss-endpoint.js +222 -0
  38. package/package.json +63 -16
  39. package/plugin.js +14 -0
  40. package/schema.json +102 -0
  41. package/src/client.ts +81 -0
  42. package/src/content-port.ts +49 -0
  43. package/src/endpoint-management.ts +73 -0
  44. package/src/index.ts +64 -37
  45. package/src/onebot12-endpoint-commands.ts +24 -0
  46. package/src/onebot12-runtime-state.ts +7 -0
  47. package/src/protocol.ts +574 -0
  48. package/src/side-event-dispatch.ts +48 -0
  49. package/src/webhook.ts +223 -0
  50. package/src/ws-endpoint.ts +321 -0
  51. package/src/ws-types.ts +19 -0
  52. package/src/wss-auth.ts +17 -0
  53. package/src/wss-endpoint.ts +278 -0
  54. package/lib/adapter.d.ts +0 -17
  55. package/lib/adapter.d.ts.map +0 -1
  56. package/lib/adapter.js +0 -40
  57. package/lib/adapter.js.map +0 -1
  58. package/lib/api.d.ts +0 -14
  59. package/lib/api.d.ts.map +0 -1
  60. package/lib/api.js +0 -34
  61. package/lib/api.js.map +0 -1
  62. package/lib/bot-webhook.d.ts +0 -25
  63. package/lib/bot-webhook.d.ts.map +0 -1
  64. package/lib/bot-webhook.js +0 -118
  65. package/lib/bot-webhook.js.map +0 -1
  66. package/lib/bot-ws.d.ts +0 -26
  67. package/lib/bot-ws.d.ts.map +0 -1
  68. package/lib/bot-ws.js +0 -210
  69. package/lib/bot-ws.js.map +0 -1
  70. package/lib/bot-wss.d.ts +0 -26
  71. package/lib/bot-wss.d.ts.map +0 -1
  72. package/lib/bot-wss.js +0 -187
  73. package/lib/bot-wss.js.map +0 -1
  74. package/lib/index.d.ts.map +0 -1
  75. package/lib/index.js.map +0 -1
  76. package/lib/types.d.ts +0 -77
  77. package/lib/types.d.ts.map +0 -1
  78. package/lib/types.js +0 -5
  79. package/lib/types.js.map +0 -1
  80. package/lib/utils.d.ts +0 -45
  81. package/lib/utils.d.ts.map +0 -1
  82. package/lib/utils.js +0 -60
  83. package/lib/utils.js.map +0 -1
  84. package/src/adapter.ts +0 -52
  85. package/src/api.ts +0 -48
  86. package/src/bot-webhook.ts +0 -130
  87. package/src/bot-ws.ts +0 -222
  88. package/src/bot-wss.ts +0 -202
  89. package/src/types.ts +0 -86
  90. package/src/utils.ts +0 -87
package/lib/webhook.js ADDED
@@ -0,0 +1,161 @@
1
+ import { ClientEndpoint, createRecallEndpointControl, } from 'zhin.js/adapter';
2
+ import { formatCompact, getLogger } from '@zhin.js/logger';
3
+ import { createOneBot12EndpointManagement } from './endpoint-management.js';
4
+ import { buildSendMessageParams, callOneBot12Action, formatInboundContent, formatOutboundSegments, isBotMentioned, isMessageEvent, onebot12InboundConversation, senderNickname, senderUserId, uploadOneBot12MediaSegments, } from './protocol.js';
5
+ import { receiveOneBot12SideEvent } from './side-event-dispatch.js';
6
+ import { createOneBot12ContentPort } from './content-port.js';
7
+ import { callOnebot12Client, createOnebot12EndpointClient, forwardOnebot12ClientEvents } from './client.js';
8
+ import { verifyOneBotAccessToken } from './wss-auth.js';
9
+ const logger = getLogger('onebot12');
10
+ export class OneBot12WebhookEndpoint extends ClientEndpoint {
11
+ client;
12
+ #options;
13
+ management;
14
+ control = createRecallEndpointControl((id) => this.recallMessage(id));
15
+ content;
16
+ #callAction;
17
+ #routeReleases = [];
18
+ #started = false;
19
+ constructor(options) {
20
+ super();
21
+ this.#options = options;
22
+ this.#callAction = options.callAction ?? callOneBot12Action;
23
+ this.client = createOnebot12EndpointClient(options.config, (action, params) => this.#callApi(action, params));
24
+ const callApi = (action, params) => callOnebot12Client(this.client, action, params);
25
+ this.management = createOneBot12EndpointManagement({ callApi });
26
+ this.content = createOneBot12ContentPort(callApi);
27
+ this.bindClientEvents((receive) => forwardOnebot12ClientEvents(this.client, receive), (name, payload) => {
28
+ if (name === 'event')
29
+ this.#admitRaw(payload);
30
+ }, (_name, error) => this.#warnPlatformEvent(error));
31
+ }
32
+ async start() {
33
+ if (this.#started)
34
+ return;
35
+ this.#started = true;
36
+ if (!this.#options.config.access_token) {
37
+ // webhook 模式未配 access_token 时任何 POST 都会被放行(verifyOneBotAccessToken 直接 return true)
38
+ logger.warn(formatCompact({
39
+ endpoint: this.#options.config.id,
40
+ mode: 'webhook',
41
+ ok: false,
42
+ error: 'missing access_token',
43
+ }));
44
+ }
45
+ this.#setupRoutes();
46
+ logger.info(formatCompact({
47
+ op: 'listen',
48
+ endpoint: this.#options.config.id,
49
+ mode: 'webhook',
50
+ path: this.#options.config.path,
51
+ }));
52
+ }
53
+ async stop() {
54
+ this.close();
55
+ for (const release of this.#routeReleases.splice(0))
56
+ release();
57
+ this.#started = false;
58
+ logger.debug(formatCompact({ op: 'disconnect', endpoint: this.#options.config.id }));
59
+ }
60
+ async send({ conversation, payload }) {
61
+ const materialized = await uploadOneBot12MediaSegments(payload, (action, params) => callOnebot12Client(this.client, action, params), (error) => {
62
+ logger.warn(formatCompact({
63
+ op: 'onebot12_upload_failed',
64
+ endpoint: this.#options.config.id,
65
+ error: error instanceof Error ? error.message : String(error),
66
+ }));
67
+ });
68
+ const message = formatOutboundSegments(materialized);
69
+ const params = buildSendMessageParams(conversation, message);
70
+ const data = await callOnebot12Client(this.client, 'send_message', params);
71
+ const messageId = data?.message_id ?? '';
72
+ logger.debug(formatCompact({
73
+ op: 'onebot12_send',
74
+ endpoint: this.#options.config.id,
75
+ kind: conversation.kind,
76
+ conversationId: conversation.id,
77
+ messageId,
78
+ }));
79
+ return messageId;
80
+ }
81
+ async recallMessage(messageId) {
82
+ if (!messageId)
83
+ return;
84
+ await callOnebot12Client(this.client, 'delete_message', { message_id: messageId });
85
+ }
86
+ async #callApi(action, params = {}) {
87
+ const apiUrl = this.#options.config.api_url;
88
+ if (!apiUrl) {
89
+ throw new Error('OneBot12 connection:webhook requires api_url for outbound api');
90
+ }
91
+ return this.#callAction({ url: apiUrl, access_token: this.#options.config.access_token }, action, params);
92
+ }
93
+ #admitRaw(ev) {
94
+ if (!isMessageEvent(ev)) {
95
+ receiveOneBot12SideEvent((name, payload) => this.emit(name, payload), this.#options.config.id, { callApi: (action, params) => callOnebot12Client(this.client, action, params) }, ev, logger);
96
+ return;
97
+ }
98
+ const conversation = onebot12InboundConversation(String(this.#options.id), ev);
99
+ const content = formatInboundContent(ev);
100
+ const nickname = senderNickname(ev);
101
+ const mentioned = isBotMentioned(ev);
102
+ void this.emit('message.receive', {
103
+ conversation,
104
+ message: { conversation, id: ev.message_id },
105
+ content,
106
+ sender: { id: senderUserId(ev), ...(nickname ? { name: nickname } : {}) },
107
+ endpointId: this.#options.config.id,
108
+ ...(mentioned ? { mentioned: true } : {}),
109
+ metadata: Object.freeze({
110
+ detail_type: ev.detail_type,
111
+ user_id: ev.user_id,
112
+ group_id: ev.group_id,
113
+ channel_id: ev.channel_id,
114
+ guild_id: ev.guild_id,
115
+ time: ev.time,
116
+ ...(nickname ? { nickname } : {}),
117
+ }),
118
+ }).catch((err) => {
119
+ logger.warn(formatCompact({
120
+ op: 'onebot12_gateway_receive_failed',
121
+ kind: conversation.kind,
122
+ conversationId: conversation.id,
123
+ error: err instanceof Error ? err.message : String(err),
124
+ }));
125
+ });
126
+ }
127
+ #warnPlatformEvent(error) {
128
+ logger.warn(formatCompact({
129
+ op: 'onebot12_platform_event_failed',
130
+ error: error instanceof Error ? error.message : String(error),
131
+ }));
132
+ }
133
+ #setupRoutes() {
134
+ const path = this.#options.config.path;
135
+ this.#routeReleases.push(this.#options.http.route('POST', path, async (request, response) => {
136
+ await this.#handleWebhook(request, response);
137
+ }, { summary: 'OneBot12 webhook callback', tags: ['onebot12'] }));
138
+ }
139
+ async #handleWebhook(request, response) {
140
+ try {
141
+ if (!verifyOneBotAccessToken(this.#options.config.access_token, request)) {
142
+ response.writeHead(403, { 'Content-Type': 'application/json' });
143
+ response.end(JSON.stringify({ message: 'Unauthorized' }));
144
+ return;
145
+ }
146
+ if (!this.clientEventsOpen) {
147
+ response.writeHead(200, { 'Content-Type': 'application/json' });
148
+ response.end(JSON.stringify({ status: 'ok' }));
149
+ return;
150
+ }
151
+ await this.client.acceptHttp(request, response);
152
+ }
153
+ catch (error) {
154
+ logger.error('OneBot12 webhook error:', error);
155
+ if (!response.headersSent) {
156
+ response.writeHead(500, { 'Content-Type': 'application/json' });
157
+ response.end(JSON.stringify({ message: 'Internal Server Error' }));
158
+ }
159
+ }
160
+ }
161
+ }
@@ -0,0 +1,22 @@
1
+ import { ClientEndpoint, type EndpointControl, type EndpointManagement, type EndpointSendRequest } from 'zhin.js/adapter';
2
+ import type { CapabilityId } from 'zhin.js';
3
+ import { type OneBot12WsConfig } from './protocol.js';
4
+ import { type Onebot12Client } from './client.js';
5
+ import { type OneBot12WsCreateOptions, type OneBot12WsSocket } from './ws-types.js';
6
+ export interface OneBot12WsEndpointOptions {
7
+ readonly id: CapabilityId;
8
+ readonly config: OneBot12WsConfig;
9
+ readonly createWebSocket?: (url: string, options: OneBot12WsCreateOptions) => OneBot12WsSocket;
10
+ }
11
+ export declare class OneBot12WsEndpoint extends ClientEndpoint<Onebot12Client> {
12
+ #private;
13
+ readonly client: Onebot12Client;
14
+ readonly management: EndpointManagement;
15
+ readonly control: EndpointControl;
16
+ readonly content: import("@zhin.js/adapter").EndpointContentPort;
17
+ constructor(options: OneBot12WsEndpointOptions);
18
+ start(): Promise<void>;
19
+ stop(): Promise<void>;
20
+ send({ conversation, payload }: EndpointSendRequest): Promise<string>;
21
+ recallMessage(messageId: string): Promise<void>;
22
+ }
@@ -0,0 +1,255 @@
1
+ /**
2
+ * OneBot12 WS client endpoint — outbound connect to OneBot implementation.
3
+ */
4
+ import WebSocket from 'ws';
5
+ import { clearTimeout } from 'node:timers';
6
+ import { ClientEndpoint, createRecallEndpointControl, createEndpointLifecycle, } from 'zhin.js/adapter';
7
+ import { formatCompact, getAdapterLogger } from '@zhin.js/logger';
8
+ import { createOneBot12EndpointManagement } from './endpoint-management.js';
9
+ import { buildSendMessageParams, buildWsConnectOptions, formatInboundContent, formatOutboundSegments, isBotMentioned, isMessageEvent, onebot12InboundConversation, senderNickname, senderUserId, uploadOneBot12MediaSegments, } from './protocol.js';
10
+ import { receiveOneBot12SideEvent } from './side-event-dispatch.js';
11
+ import { createOneBot12ContentPort } from './content-port.js';
12
+ import { callOnebot12Client, createOnebot12EndpointClient, forwardOnebot12ClientEvents } from './client.js';
13
+ import { WS_OPEN, } from './ws-types.js';
14
+ export class OneBot12WsEndpoint extends ClientEndpoint {
15
+ client;
16
+ #logger;
17
+ #options;
18
+ management;
19
+ control = createRecallEndpointControl((id) => this.recallMessage(id));
20
+ content;
21
+ #lifecycle;
22
+ #ws;
23
+ #requestId = 0;
24
+ #pending = new Map();
25
+ constructor(options) {
26
+ super();
27
+ this.#logger = getAdapterLogger('onebot12', options.config.id);
28
+ this.#options = options;
29
+ this.client = createOnebot12EndpointClient(options.config, (action, params) => this.#callAction(action, params ?? {}));
30
+ const callApi = (action, params) => callOnebot12Client(this.client, action, params);
31
+ this.management = createOneBot12EndpointManagement({ callApi });
32
+ this.content = createOneBot12ContentPort(callApi);
33
+ this.bindClientEvents((receive) => forwardOnebot12ClientEvents(this.client, receive), (name, payload) => {
34
+ if (name === 'event')
35
+ this.#admitRaw(payload);
36
+ }, (_name, error) => this.#warnPlatformEvent(error));
37
+ const { config } = options;
38
+ this.#lifecycle = createEndpointLifecycle({
39
+ name: config.id,
40
+ reconnect: {
41
+ initialIntervalMs: config.reconnect_interval,
42
+ // 固定间隔(multiplier 1、无抖动),对齐旧 reconnect_interval 语义
43
+ multiplier: 1,
44
+ maxIntervalMs: config.reconnect_interval,
45
+ jitterMs: 0,
46
+ },
47
+ heartbeat: { intervalMs: config.heartbeat_interval },
48
+ });
49
+ }
50
+ async start() {
51
+ if (this.#lifecycle.started)
52
+ return;
53
+ try {
54
+ await this.#lifecycle.start((handle) => this.#connect(handle));
55
+ }
56
+ catch (err) {
57
+ // start 失败必须清理现场(状态复位由基座保证)
58
+ if (this.#ws) {
59
+ try {
60
+ this.#ws.close();
61
+ }
62
+ catch {
63
+ /* ignore */
64
+ }
65
+ this.#ws = undefined;
66
+ }
67
+ throw err;
68
+ }
69
+ }
70
+ async stop() {
71
+ this.close();
72
+ // 基座负责:清重连/心跳定时器、强关 ws、唤醒 stop-during-connect 竞态
73
+ await this.#lifecycle.stop();
74
+ for (const [, pending] of this.#pending) {
75
+ clearTimeout(pending.timeout);
76
+ pending.reject(new Error('连接已关闭'));
77
+ }
78
+ this.#pending.clear();
79
+ this.#ws = undefined;
80
+ }
81
+ async send({ conversation, payload }) {
82
+ const materialized = await uploadOneBot12MediaSegments(payload, (action, params) => callOnebot12Client(this.client, action, params), (error) => {
83
+ this.#logger.warn(formatCompact({
84
+ op: 'onebot12_upload_failed',
85
+ endpoint: this.#options.config.id,
86
+ error: error instanceof Error ? error.message : String(error),
87
+ }));
88
+ });
89
+ const message = formatOutboundSegments(materialized);
90
+ const params = buildSendMessageParams(conversation, message);
91
+ const data = await callOnebot12Client(this.client, 'send_message', params);
92
+ const messageId = data?.message_id ?? '';
93
+ this.#logger.debug(formatCompact({
94
+ op: 'onebot12_send',
95
+ endpoint: this.#options.config.id,
96
+ kind: conversation.kind,
97
+ conversationId: conversation.id,
98
+ messageId,
99
+ }));
100
+ return messageId;
101
+ }
102
+ async recallMessage(messageId) {
103
+ if (!messageId)
104
+ return;
105
+ await callOnebot12Client(this.client, 'delete_message', { message_id: messageId });
106
+ }
107
+ #admitRaw(ev) {
108
+ if (!isMessageEvent(ev)) {
109
+ receiveOneBot12SideEvent((name, payload) => this.emit(name, payload), this.#options.config.id, { callApi: (action, params) => callOnebot12Client(this.client, action, params) }, ev, this.#logger);
110
+ return;
111
+ }
112
+ const conversation = onebot12InboundConversation(String(this.#options.id), ev);
113
+ const content = formatInboundContent(ev);
114
+ const nickname = senderNickname(ev);
115
+ const mentioned = isBotMentioned(ev);
116
+ void this.emit('message.receive', {
117
+ conversation,
118
+ message: { conversation, id: ev.message_id },
119
+ content,
120
+ sender: { id: senderUserId(ev), name: senderNickname(ev) },
121
+ endpointId: this.#options.config.id,
122
+ ...(mentioned ? { mentioned: true } : {}),
123
+ metadata: Object.freeze({
124
+ detail_type: ev.detail_type,
125
+ user_id: ev.user_id,
126
+ group_id: ev.group_id,
127
+ channel_id: ev.channel_id,
128
+ guild_id: ev.guild_id,
129
+ time: ev.time,
130
+ ...(nickname ? { nickname } : {}),
131
+ }),
132
+ }).catch((err) => {
133
+ this.#logger.warn(formatCompact({
134
+ op: 'onebot12_gateway_receive_failed',
135
+ kind: conversation.kind,
136
+ conversationId: conversation.id,
137
+ error: err instanceof Error ? err.message : String(err),
138
+ }));
139
+ });
140
+ }
141
+ #warnPlatformEvent(error) {
142
+ this.#logger.warn(formatCompact({
143
+ op: 'onebot12_platform_event_failed',
144
+ error: error instanceof Error ? error.message : String(error),
145
+ }));
146
+ }
147
+ async #connect(handle) {
148
+ const { url, headers, safeUrl } = buildWsConnectOptions(this.#options.config);
149
+ const create = this.#options.createWebSocket
150
+ ?? ((connectUrl, options) => new WebSocket(connectUrl, { headers: options.headers }));
151
+ await new Promise((resolve, reject) => {
152
+ let settled = false;
153
+ const ws = create(url, { headers });
154
+ this.#ws = ws;
155
+ handle.onForceClose(() => {
156
+ try {
157
+ ws.close();
158
+ }
159
+ catch {
160
+ /* ignore */
161
+ }
162
+ });
163
+ ws.on('open', () => {
164
+ if (settled)
165
+ return;
166
+ settled = true;
167
+ this.#logger.debug(formatCompact({
168
+ endpoint: this.#options.config.id,
169
+ mode: 'ws',
170
+ url: safeUrl,
171
+ }));
172
+ this.#lifecycle.startHeartbeat(() => {
173
+ this.#callAction('get_status', {}).catch(() => { });
174
+ });
175
+ resolve();
176
+ });
177
+ ws.on('message', (data) => {
178
+ this.#onMessage(data);
179
+ });
180
+ ws.on('close', (code, reason) => {
181
+ const reasonStr = typeof reason === 'string'
182
+ ? reason
183
+ : Buffer.isBuffer(reason)
184
+ ? reason.toString()
185
+ : String(reason ?? '');
186
+ const codeNum = typeof code === 'number' ? code : Number(code ?? 0);
187
+ if (!settled) {
188
+ settled = true;
189
+ reject(new Error(`OneBot12 WS 关闭: ${codeNum} ${reasonStr}`));
190
+ }
191
+ // 断开日志与重连武装均由基座负责;仅曾 open 的连接才会武装重连,
192
+ // 初始连接失败由 start() 的拒绝路径复位,不产生僵尸重连。
193
+ handle.notifyClosed(`OneBot12 WS 关闭: ${codeNum} ${reasonStr || 'closed'}`);
194
+ });
195
+ ws.on('error', (err) => {
196
+ const error = err instanceof Error ? err : new Error(String(err));
197
+ this.#logger.warn(formatCompact({
198
+ op: 'ws_error',
199
+ endpoint: this.#options.config.id,
200
+ ok: false,
201
+ error: error.message,
202
+ }));
203
+ if (!settled) {
204
+ settled = true;
205
+ reject(error);
206
+ }
207
+ });
208
+ });
209
+ }
210
+ #onMessage(data) {
211
+ try {
212
+ const raw = typeof data === 'string'
213
+ ? data
214
+ : Buffer.isBuffer(data)
215
+ ? data.toString()
216
+ : data instanceof ArrayBuffer
217
+ ? new TextDecoder().decode(data)
218
+ : String(data ?? '');
219
+ const msg = JSON.parse(raw);
220
+ if ('echo' in msg && typeof msg.echo === 'string') {
221
+ const resp = msg;
222
+ const pending = this.#pending.get(resp.echo);
223
+ if (pending) {
224
+ this.#pending.delete(resp.echo);
225
+ clearTimeout(pending.timeout);
226
+ pending.resolve(resp);
227
+ }
228
+ return;
229
+ }
230
+ this.client.ingest(msg);
231
+ }
232
+ catch (error) {
233
+ this.#logger.warn(formatCompact({
234
+ op: 'onebot12_parse_failed',
235
+ endpoint: this.#options.config.id,
236
+ error: error instanceof Error ? error.message : String(error),
237
+ }));
238
+ }
239
+ }
240
+ #callAction(action, params) {
241
+ if (!this.#ws || this.#ws.readyState !== WS_OPEN) {
242
+ return Promise.reject(new Error('WebSocket 未连接'));
243
+ }
244
+ const echo = `ob12_${++this.#requestId}`;
245
+ const req = { action, params, echo };
246
+ return new Promise((resolve, reject) => {
247
+ const timeout = setTimeout(() => {
248
+ this.#pending.delete(echo);
249
+ reject(new Error(`OneBot12 动作超时: ${action}`));
250
+ }, 30_000);
251
+ this.#pending.set(echo, { resolve, reject, timeout });
252
+ this.#ws.send(JSON.stringify(req));
253
+ });
254
+ }
255
+ }
@@ -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,25 @@
1
+ /**
2
+ * OneBot12 reverse WSS endpoint — accepts inbound WebSocket from OneBot implementation.
3
+ */
4
+ import { ClientEndpoint, type EndpointControl, type EndpointManagement, type EndpointSendRequest } from 'zhin.js/adapter';
5
+ import type { HttpHost } from '@zhin.js/host-http';
6
+ import type { CapabilityId } from 'zhin.js';
7
+ import { type OneBot12WssConfig } from './protocol.js';
8
+ import { type Onebot12Client } from './client.js';
9
+ export interface OneBot12WssEndpointOptions {
10
+ readonly id: CapabilityId;
11
+ readonly http: HttpHost;
12
+ readonly config: OneBot12WssConfig;
13
+ }
14
+ export declare class OneBot12WssEndpoint extends ClientEndpoint<Onebot12Client> {
15
+ #private;
16
+ readonly client: Onebot12Client;
17
+ readonly management: EndpointManagement;
18
+ readonly control: EndpointControl;
19
+ readonly content: import("@zhin.js/adapter").EndpointContentPort;
20
+ constructor(options: OneBot12WssEndpointOptions);
21
+ start(): Promise<void>;
22
+ stop(): Promise<void>;
23
+ send({ conversation, payload }: EndpointSendRequest): Promise<string>;
24
+ recallMessage(messageId: string): Promise<void>;
25
+ }