@zhin.js/adapter-onebot12 1.1.0 → 1.1.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.
- package/CHANGELOG.md +98 -0
- package/README.md +5 -3
- package/adapters/{onebot12.js → onebot12/index.js} +10 -10
- package/adapters/{onebot12.ts → onebot12/index.ts} +17 -17
- package/commands/{endpoint/add/[id].js → onebot12/endpoint/add/[id]/index.js} +1 -1
- package/commands/onebot12/endpoint/add/[id]/index.ts +3 -0
- package/{lib/onebot12-endpoint-commands.js → commands/onebot12/endpoint/definition.js} +4 -3
- package/{src/onebot12-endpoint-commands.ts → commands/onebot12/endpoint/definition.ts} +3 -3
- package/commands/{endpoint/list.js → onebot12/endpoint/list/index.js} +1 -1
- package/commands/onebot12/endpoint/list/index.ts +3 -0
- package/commands/{endpoint/remove/[id].js → onebot12/endpoint/remove/[id]/index.js} +1 -1
- package/commands/onebot12/endpoint/remove/[id]/index.ts +3 -0
- package/lib/client.js +1 -1
- package/lib/index.d.ts +1 -1
- package/lib/onebot12-runtime-state.js +1 -1
- package/lib/protocol.d.ts +6 -22
- package/lib/protocol.js +44 -28
- package/lib/webhook.js +1 -5
- package/lib/ws-endpoint.js +16 -52
- package/lib/ws-transport.d.ts +12 -0
- package/lib/ws-transport.js +58 -0
- package/lib/ws-types.d.ts +2 -1
- package/lib/wss-endpoint.js +63 -92
- package/package.json +19 -14
- package/plugin.js +1 -1
- package/schema.json +19 -1
- package/src/client.ts +1 -1
- package/src/index.ts +0 -1
- package/src/onebot12-runtime-state.ts +1 -1
- package/src/protocol.ts +52 -51
- package/src/webhook.ts +1 -5
- package/src/ws-endpoint.ts +24 -60
- package/src/ws-transport.ts +88 -0
- package/src/ws-types.ts +3 -1
- package/src/wss-endpoint.ts +69 -99
- package/agent/skills/onebot12.md +0 -36
- package/commands/endpoint/add/[id].ts +0 -3
- package/commands/endpoint/list.ts +0 -3
- package/commands/endpoint/remove/[id].ts +0 -3
- package/lib/onebot12-endpoint-commands.d.ts +0 -1
package/src/ws-endpoint.ts
CHANGED
|
@@ -2,7 +2,6 @@
|
|
|
2
2
|
* OneBot12 WS client endpoint — outbound connect to OneBot implementation.
|
|
3
3
|
*/
|
|
4
4
|
import WebSocket from 'ws';
|
|
5
|
-
import { clearTimeout } from 'node:timers';
|
|
6
5
|
import {
|
|
7
6
|
ClientEndpoint,
|
|
8
7
|
createRecallEndpointControl,
|
|
@@ -27,8 +26,6 @@ import {
|
|
|
27
26
|
senderNickname,
|
|
28
27
|
senderUserId,
|
|
29
28
|
uploadOneBot12MediaSegments,
|
|
30
|
-
type OneBot12ActionRequest,
|
|
31
|
-
type OneBot12ActionResponse,
|
|
32
29
|
type OneBot12Event,
|
|
33
30
|
type OneBot12WsConfig,
|
|
34
31
|
} from './protocol.js';
|
|
@@ -36,10 +33,15 @@ import { receiveOneBot12SideEvent } from './side-event-dispatch.js';
|
|
|
36
33
|
import { createOneBot12ContentPort } from './content-port.js';
|
|
37
34
|
import { callOnebot12Client, createOnebot12EndpointClient, forwardOnebot12ClientEvents, type Onebot12Client } from './client.js';
|
|
38
35
|
import {
|
|
36
|
+
type OneBot12PendingAction,
|
|
39
37
|
type OneBot12WsCreateOptions,
|
|
40
38
|
type OneBot12WsSocket,
|
|
41
|
-
WS_OPEN,
|
|
42
39
|
} from './ws-types.js';
|
|
40
|
+
import {
|
|
41
|
+
callOneBot12WsAction,
|
|
42
|
+
handleOneBot12WsMessage,
|
|
43
|
+
rejectAllPending,
|
|
44
|
+
} from './ws-transport.js';
|
|
43
45
|
|
|
44
46
|
export interface OneBot12WsEndpointOptions {
|
|
45
47
|
readonly id: CapabilityId;
|
|
@@ -60,12 +62,8 @@ export class OneBot12WsEndpoint extends ClientEndpoint<Onebot12Client> {
|
|
|
60
62
|
readonly content;
|
|
61
63
|
readonly #lifecycle: EndpointLifecycle;
|
|
62
64
|
#ws?: OneBot12WsSocket;
|
|
63
|
-
#requestId = 0;
|
|
64
|
-
#pending = new Map<string,
|
|
65
|
-
resolve: (value: OneBot12ActionResponse) => void;
|
|
66
|
-
reject: (err: Error) => void;
|
|
67
|
-
timeout: NodeJS.Timeout;
|
|
68
|
-
}>();
|
|
65
|
+
#requestId = { value: 0 };
|
|
66
|
+
#pending = new Map<string, OneBot12PendingAction>();
|
|
69
67
|
|
|
70
68
|
constructor(options: OneBot12WsEndpointOptions) {
|
|
71
69
|
super();
|
|
@@ -118,11 +116,7 @@ export class OneBot12WsEndpoint extends ClientEndpoint<Onebot12Client> {
|
|
|
118
116
|
this.close();
|
|
119
117
|
// 基座负责:清重连/心跳定时器、强关 ws、唤醒 stop-during-connect 竞态
|
|
120
118
|
await this.#lifecycle.stop();
|
|
121
|
-
|
|
122
|
-
clearTimeout(pending.timeout);
|
|
123
|
-
pending.reject(new Error('连接已关闭'));
|
|
124
|
-
}
|
|
125
|
-
this.#pending.clear();
|
|
119
|
+
rejectAllPending(this.#pending);
|
|
126
120
|
this.#ws = undefined;
|
|
127
121
|
}
|
|
128
122
|
|
|
@@ -238,7 +232,15 @@ export class OneBot12WsEndpoint extends ClientEndpoint<Onebot12Client> {
|
|
|
238
232
|
});
|
|
239
233
|
|
|
240
234
|
ws.on('message', (data) => {
|
|
241
|
-
this.#
|
|
235
|
+
if (this.#ws !== ws) return;
|
|
236
|
+
this.#lifecycle.notifyHeartbeatAck();
|
|
237
|
+
handleOneBot12WsMessage(data, {
|
|
238
|
+
endpointId: this.#options.config.id,
|
|
239
|
+
pending: this.#pending,
|
|
240
|
+
ingest: (event) => this.client.ingest(
|
|
241
|
+
event as Parameters<Onebot12Client['ingest']>[0],
|
|
242
|
+
),
|
|
243
|
+
});
|
|
242
244
|
});
|
|
243
245
|
|
|
244
246
|
ws.on('close', (code, reason) => {
|
|
@@ -252,6 +254,10 @@ export class OneBot12WsEndpoint extends ClientEndpoint<Onebot12Client> {
|
|
|
252
254
|
settled = true;
|
|
253
255
|
reject(new Error(`OneBot12 WS 关闭: ${codeNum} ${reasonStr}`));
|
|
254
256
|
}
|
|
257
|
+
if (this.#ws === ws) {
|
|
258
|
+
this.#ws = undefined;
|
|
259
|
+
rejectAllPending(this.#pending);
|
|
260
|
+
}
|
|
255
261
|
// 断开日志与重连武装均由基座负责;仅曾 open 的连接才会武装重连,
|
|
256
262
|
// 初始连接失败由 start() 的拒绝路径复位,不产生僵尸重连。
|
|
257
263
|
handle.notifyClosed(`OneBot12 WS 关闭: ${codeNum} ${reasonStr || 'closed'}`);
|
|
@@ -273,49 +279,7 @@ export class OneBot12WsEndpoint extends ClientEndpoint<Onebot12Client> {
|
|
|
273
279
|
});
|
|
274
280
|
}
|
|
275
281
|
|
|
276
|
-
#
|
|
277
|
-
|
|
278
|
-
const raw = typeof data === 'string'
|
|
279
|
-
? data
|
|
280
|
-
: Buffer.isBuffer(data)
|
|
281
|
-
? data.toString()
|
|
282
|
-
: data instanceof ArrayBuffer
|
|
283
|
-
? new TextDecoder().decode(data)
|
|
284
|
-
: String(data ?? '');
|
|
285
|
-
const msg = JSON.parse(raw) as OneBot12Event | OneBot12ActionResponse;
|
|
286
|
-
if ('echo' in msg && typeof (msg as OneBot12ActionResponse).echo === 'string') {
|
|
287
|
-
const resp = msg as OneBot12ActionResponse;
|
|
288
|
-
const pending = this.#pending.get(resp.echo!);
|
|
289
|
-
if (pending) {
|
|
290
|
-
this.#pending.delete(resp.echo!);
|
|
291
|
-
clearTimeout(pending.timeout);
|
|
292
|
-
pending.resolve(resp);
|
|
293
|
-
}
|
|
294
|
-
return;
|
|
295
|
-
}
|
|
296
|
-
this.client.ingest(msg as Parameters<Onebot12Client['ingest']>[0]);
|
|
297
|
-
} catch (error) {
|
|
298
|
-
this.#logger.warn(formatCompact({
|
|
299
|
-
op: 'onebot12_parse_failed',
|
|
300
|
-
endpoint: this.#options.config.id,
|
|
301
|
-
error: error instanceof Error ? error.message : String(error),
|
|
302
|
-
}));
|
|
303
|
-
}
|
|
304
|
-
}
|
|
305
|
-
|
|
306
|
-
#callAction(action: string, params: Record<string, unknown>): Promise<OneBot12ActionResponse> {
|
|
307
|
-
if (!this.#ws || this.#ws.readyState !== WS_OPEN) {
|
|
308
|
-
return Promise.reject(new Error('WebSocket 未连接'));
|
|
309
|
-
}
|
|
310
|
-
const echo = `ob12_${++this.#requestId}`;
|
|
311
|
-
const req: OneBot12ActionRequest = { action, params, echo };
|
|
312
|
-
return new Promise((resolve, reject) => {
|
|
313
|
-
const timeout = setTimeout(() => {
|
|
314
|
-
this.#pending.delete(echo);
|
|
315
|
-
reject(new Error(`OneBot12 动作超时: ${action}`));
|
|
316
|
-
}, 30_000);
|
|
317
|
-
this.#pending.set(echo, { resolve, reject, timeout });
|
|
318
|
-
this.#ws!.send(JSON.stringify(req));
|
|
319
|
-
});
|
|
282
|
+
#callAction(action: string, params: Record<string, unknown>) {
|
|
283
|
+
return callOneBot12WsAction(this.#ws, this.#pending, this.#requestId, action, params);
|
|
320
284
|
}
|
|
321
285
|
}
|
|
@@ -0,0 +1,88 @@
|
|
|
1
|
+
import { clearTimeout, setTimeout } from 'node:timers';
|
|
2
|
+
import { formatCompact, getLogger } from '@zhin.js/logger';
|
|
3
|
+
import type {
|
|
4
|
+
OneBot12ActionRequest,
|
|
5
|
+
OneBot12ActionResponse,
|
|
6
|
+
OneBot12Event,
|
|
7
|
+
} from './protocol.js';
|
|
8
|
+
import {
|
|
9
|
+
type OneBot12PendingAction,
|
|
10
|
+
type OneBot12WsSocket,
|
|
11
|
+
WS_OPEN,
|
|
12
|
+
} from './ws-types.js';
|
|
13
|
+
|
|
14
|
+
const logger = getLogger('onebot12');
|
|
15
|
+
|
|
16
|
+
export interface OneBot12WsMessageOptions {
|
|
17
|
+
readonly endpointId: string;
|
|
18
|
+
readonly pending: Map<string, OneBot12PendingAction>;
|
|
19
|
+
readonly ingest: (event: OneBot12Event) => void;
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
export function handleOneBot12WsMessage(
|
|
23
|
+
data: unknown,
|
|
24
|
+
options: OneBot12WsMessageOptions,
|
|
25
|
+
): void {
|
|
26
|
+
try {
|
|
27
|
+
const message = JSON.parse(decodeOneBot12WsPayload(data)) as
|
|
28
|
+
| OneBot12Event
|
|
29
|
+
| OneBot12ActionResponse;
|
|
30
|
+
if ('echo' in message && typeof message.echo === 'string') {
|
|
31
|
+
const response = message as OneBot12ActionResponse;
|
|
32
|
+
const pending = options.pending.get(response.echo!);
|
|
33
|
+
if (pending) {
|
|
34
|
+
options.pending.delete(response.echo!);
|
|
35
|
+
clearTimeout(pending.timeout);
|
|
36
|
+
pending.resolve(response);
|
|
37
|
+
}
|
|
38
|
+
return;
|
|
39
|
+
}
|
|
40
|
+
options.ingest(message as OneBot12Event);
|
|
41
|
+
} catch (error) {
|
|
42
|
+
logger.warn(formatCompact({
|
|
43
|
+
op: 'onebot12_parse_failed',
|
|
44
|
+
endpoint: options.endpointId,
|
|
45
|
+
error: error instanceof Error ? error.message : String(error),
|
|
46
|
+
}));
|
|
47
|
+
}
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
export function callOneBot12WsAction(
|
|
51
|
+
ws: OneBot12WsSocket | undefined,
|
|
52
|
+
pending: Map<string, OneBot12PendingAction>,
|
|
53
|
+
requestId: { value: number },
|
|
54
|
+
action: string,
|
|
55
|
+
params: Record<string, unknown>,
|
|
56
|
+
): Promise<OneBot12ActionResponse> {
|
|
57
|
+
if (!ws || ws.readyState !== WS_OPEN) {
|
|
58
|
+
return Promise.reject(new Error('WebSocket 未连接'));
|
|
59
|
+
}
|
|
60
|
+
const echo = `ob12_${++requestId.value}`;
|
|
61
|
+
const request: OneBot12ActionRequest = { action, params, echo };
|
|
62
|
+
return new Promise((resolve, reject) => {
|
|
63
|
+
const timeout = setTimeout(() => {
|
|
64
|
+
pending.delete(echo);
|
|
65
|
+
reject(new Error(`OneBot12 动作超时: ${action}`));
|
|
66
|
+
}, 30_000);
|
|
67
|
+
pending.set(echo, { resolve, reject, timeout });
|
|
68
|
+
ws.send(JSON.stringify(request));
|
|
69
|
+
});
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
export function rejectAllPending(
|
|
73
|
+
pending: Map<string, OneBot12PendingAction>,
|
|
74
|
+
message = '连接已关闭',
|
|
75
|
+
): void {
|
|
76
|
+
for (const [, entry] of pending) {
|
|
77
|
+
clearTimeout(entry.timeout);
|
|
78
|
+
entry.reject(new Error(message));
|
|
79
|
+
}
|
|
80
|
+
pending.clear();
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
function decodeOneBot12WsPayload(data: unknown): string {
|
|
84
|
+
if (typeof data === 'string') return data;
|
|
85
|
+
if (Buffer.isBuffer(data)) return data.toString();
|
|
86
|
+
if (data instanceof ArrayBuffer) return new TextDecoder().decode(data);
|
|
87
|
+
return String(data ?? '');
|
|
88
|
+
}
|
package/src/ws-types.ts
CHANGED
|
@@ -1,3 +1,5 @@
|
|
|
1
|
+
import type { OneBot12ActionResponse } from './protocol.js';
|
|
2
|
+
|
|
1
3
|
/** Minimal WS surface used by the endpoint (real `ws` or test mock). */
|
|
2
4
|
export interface OneBot12WsSocket {
|
|
3
5
|
readonly readyState: number;
|
|
@@ -13,7 +15,7 @@ export interface OneBot12WsCreateOptions {
|
|
|
13
15
|
export const WS_OPEN = 1;
|
|
14
16
|
|
|
15
17
|
export interface OneBot12PendingAction {
|
|
16
|
-
resolve: (value:
|
|
18
|
+
resolve: (value: OneBot12ActionResponse) => void;
|
|
17
19
|
reject: (err: Error) => void;
|
|
18
20
|
timeout: NodeJS.Timeout;
|
|
19
21
|
}
|
package/src/wss-endpoint.ts
CHANGED
|
@@ -24,8 +24,6 @@ import {
|
|
|
24
24
|
senderNickname,
|
|
25
25
|
senderUserId,
|
|
26
26
|
uploadOneBot12MediaSegments,
|
|
27
|
-
type OneBot12ActionRequest,
|
|
28
|
-
type OneBot12ActionResponse,
|
|
29
27
|
type OneBot12Event,
|
|
30
28
|
type OneBot12WssConfig,
|
|
31
29
|
} from './protocol.js';
|
|
@@ -33,7 +31,12 @@ import { receiveOneBot12SideEvent } from './side-event-dispatch.js';
|
|
|
33
31
|
import { createOneBot12ContentPort } from './content-port.js';
|
|
34
32
|
import { callOnebot12Client, createOnebot12EndpointClient, forwardOnebot12ClientEvents, type Onebot12Client } from './client.js';
|
|
35
33
|
import { verifyOneBotAccessToken } from './wss-auth.js';
|
|
36
|
-
import { type
|
|
34
|
+
import { type OneBot12PendingAction, type OneBot12WsSocket } from './ws-types.js';
|
|
35
|
+
import {
|
|
36
|
+
callOneBot12WsAction,
|
|
37
|
+
handleOneBot12WsMessage,
|
|
38
|
+
rejectAllPending,
|
|
39
|
+
} from './ws-transport.js';
|
|
37
40
|
|
|
38
41
|
export interface OneBot12WssEndpointOptions {
|
|
39
42
|
readonly id: CapabilityId;
|
|
@@ -51,22 +54,19 @@ export class OneBot12WssEndpoint extends ClientEndpoint<Onebot12Client> {
|
|
|
51
54
|
readonly content;
|
|
52
55
|
#ws?: OneBot12WsSocket;
|
|
53
56
|
#wsRelease?: () => void;
|
|
54
|
-
readonly #
|
|
55
|
-
#
|
|
56
|
-
#
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
timeout: NodeJS.Timeout;
|
|
60
|
-
}>();
|
|
57
|
+
readonly #connectionLifecycle: EndpointLifecycle;
|
|
58
|
+
#connectionTask = Promise.resolve();
|
|
59
|
+
#requestId = { value: 0 };
|
|
60
|
+
#pending = new Map<string, OneBot12PendingAction>();
|
|
61
|
+
#started = false;
|
|
61
62
|
|
|
62
63
|
constructor(options: OneBot12WssEndpointOptions) {
|
|
63
64
|
super();
|
|
64
65
|
this.#logger = getAdapterLogger('onebot12', options.config.id);
|
|
65
66
|
this.#options = options;
|
|
66
|
-
this.#
|
|
67
|
-
name: options.config.id
|
|
67
|
+
this.#connectionLifecycle = createEndpointLifecycle({
|
|
68
|
+
name: `${options.config.id}:inbound`,
|
|
68
69
|
reconnect: false,
|
|
69
|
-
heartbeat: { intervalMs: options.config.heartbeat_interval },
|
|
70
70
|
});
|
|
71
71
|
this.client = createOnebot12EndpointClient(options.config, (action, params) => this.#callAction(action, params ?? {}));
|
|
72
72
|
const callApi = (action: string, params?: Record<string, unknown>) => callOnebot12Client(this.client, action, params);
|
|
@@ -82,6 +82,7 @@ export class OneBot12WssEndpoint extends ClientEndpoint<Onebot12Client> {
|
|
|
82
82
|
}
|
|
83
83
|
|
|
84
84
|
async start(): Promise<void> {
|
|
85
|
+
if (this.#started) return;
|
|
85
86
|
if (!this.#options.config.access_token) {
|
|
86
87
|
// wss 模式未配 access_token 时任何连接都会被放行(verifyOneBotAccessToken 直接 return true)
|
|
87
88
|
this.#logger.warn(formatCompact({
|
|
@@ -91,22 +92,19 @@ export class OneBot12WssEndpoint extends ClientEndpoint<Onebot12Client> {
|
|
|
91
92
|
error: 'missing access_token',
|
|
92
93
|
}));
|
|
93
94
|
}
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
this.#
|
|
97
|
-
this.#acceptConnection(connection)
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
}
|
|
105
|
-
/* ignore */
|
|
106
|
-
}
|
|
107
|
-
this.#ws = undefined;
|
|
108
|
-
});
|
|
95
|
+
const handle = this.#options.http.ws(this.#options.config.path);
|
|
96
|
+
this.#wsRelease = handle.onConnection((connection) => {
|
|
97
|
+
this.#connectionTask = this.#connectionTask
|
|
98
|
+
.then(() => this.#acceptConnection(connection))
|
|
99
|
+
.catch((error) => {
|
|
100
|
+
this.#logger.warn(formatCompact({
|
|
101
|
+
op: 'wss_connection_failed',
|
|
102
|
+
endpoint: this.#options.config.id,
|
|
103
|
+
error: error instanceof Error ? error.message : String(error),
|
|
104
|
+
}));
|
|
105
|
+
});
|
|
109
106
|
});
|
|
107
|
+
this.#started = true;
|
|
110
108
|
this.#logger.info(formatCompact({
|
|
111
109
|
op: 'listen',
|
|
112
110
|
endpoint: this.#options.config.id,
|
|
@@ -117,12 +115,13 @@ export class OneBot12WssEndpoint extends ClientEndpoint<Onebot12Client> {
|
|
|
117
115
|
|
|
118
116
|
async stop(): Promise<void> {
|
|
119
117
|
this.close();
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
this.#
|
|
118
|
+
this.#wsRelease?.();
|
|
119
|
+
this.#wsRelease = undefined;
|
|
120
|
+
await this.#connectionTask;
|
|
121
|
+
await this.#connectionLifecycle.stop();
|
|
122
|
+
rejectAllPending(this.#pending);
|
|
123
|
+
this.#ws = undefined;
|
|
124
|
+
this.#started = false;
|
|
126
125
|
}
|
|
127
126
|
|
|
128
127
|
async send({ conversation, payload }: EndpointSendRequest): Promise<string> {
|
|
@@ -195,32 +194,45 @@ export class OneBot12WssEndpoint extends ClientEndpoint<Onebot12Client> {
|
|
|
195
194
|
}));
|
|
196
195
|
}
|
|
197
196
|
|
|
198
|
-
#acceptConnection(connection: WsConnection): void {
|
|
197
|
+
async #acceptConnection(connection: WsConnection): Promise<void> {
|
|
199
198
|
if (!verifyOneBotAccessToken(this.#options.config.access_token, connection.request)) {
|
|
200
199
|
connection.socket.close(4003, 'Unauthorized');
|
|
201
200
|
return;
|
|
202
201
|
}
|
|
203
202
|
const socket = connection.socket as unknown as OneBot12WsSocket;
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
this.#
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
this.#
|
|
222
|
-
|
|
223
|
-
|
|
203
|
+
await this.#connectionLifecycle.stop();
|
|
204
|
+
rejectAllPending(this.#pending, '连接已替换');
|
|
205
|
+
await this.#connectionLifecycle.start(async (lifecycleHandle) => {
|
|
206
|
+
this.#ws = socket;
|
|
207
|
+
lifecycleHandle.onForceClose(() => {
|
|
208
|
+
try {
|
|
209
|
+
socket.close();
|
|
210
|
+
} catch {
|
|
211
|
+
/* ignore */
|
|
212
|
+
}
|
|
213
|
+
if (this.#ws === socket) this.#ws = undefined;
|
|
214
|
+
});
|
|
215
|
+
this.#connectionLifecycle.startHeartbeat(() => {
|
|
216
|
+
this.#callAction('get_status', {}).catch(() => {});
|
|
217
|
+
}, this.#options.config.heartbeat_interval);
|
|
218
|
+
socket.on('message', (data) => {
|
|
219
|
+
if (this.#ws !== socket) return;
|
|
220
|
+
this.#connectionLifecycle.notifyHeartbeatAck();
|
|
221
|
+
handleOneBot12WsMessage(data, {
|
|
222
|
+
endpointId: this.#options.config.id,
|
|
223
|
+
pending: this.#pending,
|
|
224
|
+
ingest: (event) => this.client.ingest(
|
|
225
|
+
event as Parameters<Onebot12Client['ingest']>[0],
|
|
226
|
+
),
|
|
227
|
+
});
|
|
228
|
+
});
|
|
229
|
+
socket.on('close', () => {
|
|
230
|
+
if (this.#ws === socket) {
|
|
231
|
+
this.#ws = undefined;
|
|
232
|
+
rejectAllPending(this.#pending);
|
|
233
|
+
}
|
|
234
|
+
lifecycleHandle.notifyClosed(new Error('OneBot12 reverse WebSocket closed'));
|
|
235
|
+
});
|
|
224
236
|
});
|
|
225
237
|
this.#logger.debug(formatCompact({
|
|
226
238
|
endpoint: this.#options.config.id,
|
|
@@ -229,50 +241,8 @@ export class OneBot12WssEndpoint extends ClientEndpoint<Onebot12Client> {
|
|
|
229
241
|
}));
|
|
230
242
|
}
|
|
231
243
|
|
|
232
|
-
#
|
|
233
|
-
|
|
234
|
-
const raw = typeof data === 'string'
|
|
235
|
-
? data
|
|
236
|
-
: Buffer.isBuffer(data)
|
|
237
|
-
? data.toString()
|
|
238
|
-
: data instanceof ArrayBuffer
|
|
239
|
-
? new TextDecoder().decode(data)
|
|
240
|
-
: String(data ?? '');
|
|
241
|
-
const msg = JSON.parse(raw) as OneBot12Event | OneBot12ActionResponse;
|
|
242
|
-
if ('echo' in msg && typeof (msg as OneBot12ActionResponse).echo === 'string') {
|
|
243
|
-
const resp = msg as OneBot12ActionResponse;
|
|
244
|
-
const pending = this.#pending.get(resp.echo!);
|
|
245
|
-
if (pending) {
|
|
246
|
-
this.#pending.delete(resp.echo!);
|
|
247
|
-
clearTimeout(pending.timeout);
|
|
248
|
-
pending.resolve(resp);
|
|
249
|
-
}
|
|
250
|
-
return;
|
|
251
|
-
}
|
|
252
|
-
this.client.ingest(msg as Parameters<Onebot12Client['ingest']>[0]);
|
|
253
|
-
} catch (error) {
|
|
254
|
-
this.#logger.warn(formatCompact({
|
|
255
|
-
op: 'onebot12_parse_failed',
|
|
256
|
-
endpoint: this.#options.config.id,
|
|
257
|
-
error: error instanceof Error ? error.message : String(error),
|
|
258
|
-
}));
|
|
259
|
-
}
|
|
260
|
-
}
|
|
261
|
-
|
|
262
|
-
#callAction(action: string, params: Record<string, unknown>): Promise<OneBot12ActionResponse> {
|
|
263
|
-
if (!this.#ws || this.#ws.readyState !== WS_OPEN) {
|
|
264
|
-
return Promise.reject(new Error('WebSocket 未连接'));
|
|
265
|
-
}
|
|
266
|
-
const echo = `ob12_${++this.#requestId}`;
|
|
267
|
-
const req: OneBot12ActionRequest = { action, params, echo };
|
|
268
|
-
return new Promise((resolve, reject) => {
|
|
269
|
-
const timeout = setTimeout(() => {
|
|
270
|
-
this.#pending.delete(echo);
|
|
271
|
-
reject(new Error(`OneBot12 动作超时: ${action}`));
|
|
272
|
-
}, 30_000);
|
|
273
|
-
this.#pending.set(echo, { resolve, reject, timeout });
|
|
274
|
-
this.#ws!.send(JSON.stringify(req));
|
|
275
|
-
});
|
|
244
|
+
#callAction(action: string, params: Record<string, unknown>) {
|
|
245
|
+
return callOneBot12WsAction(this.#ws, this.#pending, this.#requestId, action, params);
|
|
276
246
|
}
|
|
277
247
|
|
|
278
248
|
}
|
package/agent/skills/onebot12.md
DELETED
|
@@ -1,36 +0,0 @@
|
|
|
1
|
-
---
|
|
2
|
-
name: onebot12
|
|
3
|
-
platforms:
|
|
4
|
-
- onebot12
|
|
5
|
-
description: >-
|
|
6
|
-
OneBot 12 协议适配器:支持 WebSocket 正向连接、Webhook 回调和反向 WebSocket
|
|
7
|
-
三种连接模式,兼容 OneBot 12 标准协议。纯消息通道,无额外 AI 工具。
|
|
8
|
-
支持私聊、群聊、频道消息。
|
|
9
|
-
keywords:
|
|
10
|
-
- onebot12
|
|
11
|
-
- onebot
|
|
12
|
-
- adapter:onebot12
|
|
13
|
-
- protocol
|
|
14
|
-
- websocket
|
|
15
|
-
- webhook
|
|
16
|
-
- 协议
|
|
17
|
-
tags:
|
|
18
|
-
- onebot12
|
|
19
|
-
- protocol
|
|
20
|
-
- adapter
|
|
21
|
-
tools: []
|
|
22
|
-
---
|
|
23
|
-
|
|
24
|
-
# OneBot 12 协议适配器
|
|
25
|
-
|
|
26
|
-
纯协议适配器,收发消息。无 AI 工具可调用。
|
|
27
|
-
|
|
28
|
-
## 连接模式
|
|
29
|
-
|
|
30
|
-
- **WS 正向**:Bot 主动连接 OneBot 12 服务端
|
|
31
|
-
- **Webhook**:OneBot 12 向配置的 URL 推送事件
|
|
32
|
-
- **WS 反向**:OneBot 12 主动连接 Endpoint 的 WS 服务端
|
|
33
|
-
|
|
34
|
-
## 与 OneBot11 的区别
|
|
35
|
-
|
|
36
|
-
OneBot 12 标准化了事件格式、消息段类型和 API 响应结构。如果需要群管理等 AI 工具,使用 OneBot11 适配器。
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
export declare const onebot12EndpointCommands: import("@zhin.js/adapter").EndpointCommands<Readonly<import("@zhin.js/command").CommandDefinition<unknown, unknown, import("@zhin.js/command").CommandMessage, string | undefined>>>;
|