@zhin.js/adapter-onebot11 1.1.18 → 1.1.20
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 +46 -0
- package/README.md +13 -6
- package/adapters/onebot11/index.js +3 -26
- package/adapters/onebot11/index.ts +2 -32
- package/commands/onebot11/endpoint/definition.js +0 -2
- package/commands/onebot11/endpoint/definition.ts +0 -2
- package/lib/adapter.d.ts +20 -0
- package/lib/adapter.js +26 -0
- package/lib/client.d.ts +13 -11
- package/lib/client.js +35 -12
- package/lib/console.d.ts +1 -1
- package/lib/console.js +2 -5
- package/lib/content-port.d.ts +2 -2
- package/lib/content-port.js +2 -2
- package/lib/endpoint-management.d.ts +2 -2
- package/lib/endpoint-management.js +8 -8
- package/lib/identity.d.ts +3 -0
- package/lib/identity.js +32 -0
- package/lib/index.d.ts +3 -2
- package/lib/index.js +3 -2
- package/lib/self-id.js +1 -1
- package/lib/side-event-dispatch.d.ts +8 -1
- package/lib/side-event-dispatch.js +2 -1
- package/lib/ws-endpoint.d.ts +5 -12
- package/lib/ws-endpoint.js +75 -50
- package/lib/ws-transport.d.ts +18 -11
- package/lib/ws-transport.js +28 -56
- package/lib/ws-types.d.ts +0 -6
- package/lib/wss-endpoint.d.ts +5 -12
- package/lib/wss-endpoint.js +76 -53
- package/package.json +18 -18
- package/plugin.js +0 -3
- package/src/adapter.ts +32 -0
- package/src/client.ts +38 -28
- package/src/console.ts +2 -5
- package/src/content-port.ts +4 -4
- package/src/endpoint-management.ts +15 -12
- package/src/identity.ts +30 -0
- package/src/index.ts +4 -4
- package/src/self-id.ts +1 -1
- package/src/side-event-dispatch.ts +9 -7
- package/src/ws-endpoint.ts +71 -64
- package/src/ws-transport.ts +41 -82
- package/src/ws-types.ts +0 -7
- package/src/wss-endpoint.ts +73 -68
- package/lib/onebot11-runtime-state.d.ts +0 -1
- package/lib/onebot11-runtime-state.js +0 -6
- package/src/onebot11-runtime-state.ts +0 -7
package/src/wss-endpoint.ts
CHANGED
|
@@ -3,13 +3,15 @@ import { EndpointDeliveryError } from '@zhin.js/im-contract';
|
|
|
3
3
|
* OneBot11 reverse WSS endpoint — accepts inbound WebSocket from OneBot implementation.
|
|
4
4
|
*/
|
|
5
5
|
import {
|
|
6
|
-
|
|
6
|
+
createEndpointSpecification, endpointSpec,
|
|
7
|
+
type Endpoint, type EndpointSpecification,
|
|
7
8
|
createEndpointLifecycle,
|
|
8
9
|
createRecallEndpointControl,
|
|
9
10
|
type EndpointLifecycle,
|
|
10
11
|
type EndpointControl,
|
|
11
12
|
type EndpointManagement,
|
|
12
13
|
type EndpointSendRequest,
|
|
14
|
+
type PendingRpcRequest,
|
|
13
15
|
type EndpointTransportState,
|
|
14
16
|
} from 'zhin.js/adapter';
|
|
15
17
|
import type { HttpHost, WsConnection } from '@zhin.js/host-http';
|
|
@@ -31,20 +33,15 @@ import {
|
|
|
31
33
|
import { receiveOneBot11SideEvent } from './side-event-dispatch.js';
|
|
32
34
|
import { createOneBot11ContentPort } from './content-port.js';
|
|
33
35
|
import { verifyOneBotAccessToken } from './wss-auth.js';
|
|
36
|
+
import { createOneBot11Rpc, type OneBot11Rpc } from './ws-transport.js';
|
|
34
37
|
import {
|
|
35
|
-
callOneBot11WsAction,
|
|
36
|
-
handleOneBot11WsMessage,
|
|
37
|
-
rejectAllPending,
|
|
38
|
-
} from './ws-transport.js';
|
|
39
|
-
import { OneBot11WsEndpoint } from './ws-endpoint.js';
|
|
40
|
-
import {
|
|
41
|
-
type OneBot11PendingAction,
|
|
42
38
|
type OneBot11WsSocket,
|
|
43
39
|
} from './ws-types.js';
|
|
44
40
|
import { probeOnebot11SelfId, resolveOnebot11SelfId } from './self-id.js';
|
|
45
|
-
import { callOnebot11Client,
|
|
41
|
+
import { callOnebot11Client, readOnebot11Response, onebot11ClientOptions, forwardOnebot11ClientEvents, Onebot11Client } from './client.js';
|
|
46
42
|
|
|
47
43
|
export interface OneBot11WssEndpointOptions {
|
|
44
|
+
readonly signal?: AbortSignal;
|
|
48
45
|
readonly id: CapabilityId;
|
|
49
46
|
/** 连接后 best-effort 调用 get_login_info 上报账号(Adapter 工厂开启)。 */
|
|
50
47
|
readonly probeLoginInfo?: boolean;
|
|
@@ -52,25 +49,30 @@ export interface OneBot11WssEndpointOptions {
|
|
|
52
49
|
readonly config: OneBot11WssConfig;
|
|
53
50
|
}
|
|
54
51
|
|
|
55
|
-
export class OneBot11WssEndpoint extends
|
|
56
|
-
readonly
|
|
52
|
+
export class OneBot11WssEndpoint extends Onebot11Client implements Endpoint {
|
|
53
|
+
readonly [endpointSpec]: EndpointSpecification;
|
|
54
|
+
#open = false;
|
|
57
55
|
readonly #logger!: ReturnType<typeof getAdapterLogger>;
|
|
58
56
|
|
|
59
57
|
readonly #options: OneBot11WssEndpointOptions;
|
|
60
|
-
readonly management: EndpointManagement;
|
|
61
|
-
readonly control: EndpointControl = createRecallEndpointControl((id) => this
|
|
62
|
-
readonly content;
|
|
58
|
+
readonly #management: EndpointManagement;
|
|
59
|
+
readonly #control: EndpointControl = createRecallEndpointControl((id, signal?: AbortSignal) => this.#recallMessage(id, signal));
|
|
60
|
+
readonly #content;
|
|
61
|
+
readonly #releaseClientEvents: () => void;
|
|
63
62
|
#ws?: OneBot11WsSocket;
|
|
64
63
|
#wsRelease?: () => void;
|
|
65
64
|
readonly #connectionLifecycle: EndpointLifecycle;
|
|
66
65
|
#connectionTask = Promise.resolve();
|
|
67
|
-
#
|
|
68
|
-
#pending = new Map<string, OneBot11PendingAction>();
|
|
66
|
+
readonly #rpc: OneBot11Rpc;
|
|
69
67
|
#started = false;
|
|
70
68
|
#stopped = false;
|
|
71
69
|
|
|
72
70
|
constructor(options: OneBot11WssEndpointOptions) {
|
|
73
|
-
|
|
71
|
+
const rpc = createOneBot11Rpc({ endpointId: options.config.id, signal: options.signal,
|
|
72
|
+
socket: () => this.#connectionLifecycle.state === 'closed' ? undefined : this.#ws,
|
|
73
|
+
});
|
|
74
|
+
super(onebot11ClientOptions(options.config, (action, params) => rpc.call({ action, params })));
|
|
75
|
+
this.#rpc = rpc;
|
|
74
76
|
this.#logger = getAdapterLogger('onebot11', options.config.id);
|
|
75
77
|
this.#options = options;
|
|
76
78
|
this.#connectionLifecycle = createEndpointLifecycle({
|
|
@@ -78,20 +80,28 @@ export class OneBot11WssEndpoint extends ClientEndpoint<Onebot11Client> {
|
|
|
78
80
|
reconnect: false,
|
|
79
81
|
heartbeat: { intervalMs: options.config.heartbeat_interval, watchdogMisses: 2 },
|
|
80
82
|
});
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
this
|
|
84
|
-
|
|
85
|
-
this
|
|
86
|
-
|
|
87
|
-
(
|
|
83
|
+
const callApi = (request: PendingRpcRequest) => this.#rpc.call(request).then(readOnebot11Response);
|
|
84
|
+
this.#management = createOneBot11EndpointManagement({ callApi });
|
|
85
|
+
this.#content = createOneBot11ContentPort(callApi);
|
|
86
|
+
const readTransportState = () => this.#transportState;
|
|
87
|
+
this[endpointSpec] = createEndpointSpecification(this, {
|
|
88
|
+
name: options.config.id,
|
|
89
|
+
start: () => this.#start(), open: () => { this.#open = true; },
|
|
90
|
+
close: () => { this.#open = false; }, stop: () => this.#stop(),
|
|
91
|
+
send: request => this.#send(request),
|
|
92
|
+
get transportState() { return readTransportState(); },
|
|
93
|
+
management: this.#management, control: this.#control, content: this.#content,
|
|
94
|
+
});
|
|
95
|
+
this.#releaseClientEvents = forwardOnebot11ClientEvents(this, (name, payload) => {
|
|
96
|
+
void this[endpointSpec].events.run(() => {
|
|
97
|
+
if (this.#stopped || (!this.#open && !this.#started)) return;
|
|
98
|
+
void this[endpointSpec].events.platform(name, payload).catch(error => this.#warnPlatformEvent(error));
|
|
88
99
|
if (name === 'event') this.#admitRaw(payload as OneBot11Event);
|
|
89
|
-
}
|
|
90
|
-
|
|
91
|
-
);
|
|
100
|
+
}).catch(error => this.#warnPlatformEvent(error));
|
|
101
|
+
});
|
|
92
102
|
}
|
|
93
103
|
|
|
94
|
-
get transportState(): EndpointTransportState {
|
|
104
|
+
get #transportState(): EndpointTransportState {
|
|
95
105
|
if (this.#stopped) return 'stopped';
|
|
96
106
|
if (!this.#started) return 'idle';
|
|
97
107
|
if (this.#connectionLifecycle.state === 'closed') return 'closed';
|
|
@@ -99,7 +109,7 @@ export class OneBot11WssEndpoint extends ClientEndpoint<Onebot11Client> {
|
|
|
99
109
|
return this.#connectionLifecycle.state === 'connecting' ? 'connecting' : 'idle';
|
|
100
110
|
}
|
|
101
111
|
|
|
102
|
-
async start(): Promise<void> {
|
|
112
|
+
async #start(): Promise<void> {
|
|
103
113
|
if (this.#started) return;
|
|
104
114
|
this.#stopped = false;
|
|
105
115
|
if (!this.#options.config.access_token) {
|
|
@@ -132,23 +142,27 @@ export class OneBot11WssEndpoint extends ClientEndpoint<Onebot11Client> {
|
|
|
132
142
|
}));
|
|
133
143
|
}
|
|
134
144
|
|
|
135
|
-
async stop(): Promise<void> {
|
|
145
|
+
async #stop(): Promise<void> {
|
|
136
146
|
this.#stopped = true;
|
|
137
|
-
this
|
|
147
|
+
this.#open = false;
|
|
138
148
|
this.#wsRelease?.();
|
|
139
149
|
this.#wsRelease = undefined;
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
150
|
+
try {
|
|
151
|
+
await this.#connectionTask;
|
|
152
|
+
await this.#connectionLifecycle.stop();
|
|
153
|
+
} finally {
|
|
154
|
+
this.#rpc.dispose();
|
|
155
|
+
this.#releaseClientEvents();
|
|
156
|
+
this.#ws = undefined;
|
|
157
|
+
this.#started = false;
|
|
158
|
+
this[endpointSpec].reportSelfUserId(undefined);
|
|
159
|
+
}
|
|
146
160
|
}
|
|
147
161
|
|
|
148
|
-
async send({ conversation, payload }: EndpointSendRequest): Promise<string> {
|
|
162
|
+
async #send({ conversation, payload, signal }: EndpointSendRequest): Promise<string> {
|
|
149
163
|
const message = formatOutboundSegments(payload);
|
|
150
164
|
const { action, params } = buildSendAction(conversation, message);
|
|
151
|
-
const data =
|
|
165
|
+
const data = readOnebot11Response<{ message_id?: number | string }>(await this.#rpc.call({ action, params, signal }));
|
|
152
166
|
const rawId = data?.message_id;
|
|
153
167
|
if ((typeof rawId !== 'number' && typeof rawId !== 'string') || String(rawId).trim() === '' || (typeof rawId === 'number' && !Number.isFinite(rawId))) {
|
|
154
168
|
throw new EndpointDeliveryError('delivery_unconfirmed', 'Message delivery is unconfirmed: platform returned no message_id', 'unknown');
|
|
@@ -156,34 +170,29 @@ export class OneBot11WssEndpoint extends ClientEndpoint<Onebot11Client> {
|
|
|
156
170
|
return String(rawId);
|
|
157
171
|
}
|
|
158
172
|
|
|
159
|
-
async recallMessage(messageId: string): Promise<void> {
|
|
173
|
+
async #recallMessage(messageId: string, signal?: AbortSignal): Promise<void> {
|
|
160
174
|
if (!messageId) return;
|
|
161
|
-
await
|
|
175
|
+
await this.#rpc.call({ action: 'delete_msg', params: { message_id: Number(messageId) }, signal }).then(readOnebot11Response);
|
|
162
176
|
}
|
|
163
177
|
|
|
164
|
-
#callApi(
|
|
165
|
-
action: string,
|
|
166
|
-
params: Record<string, unknown> = {},
|
|
167
|
-
): ReturnType<typeof callOneBot11WsAction> {
|
|
168
|
-
return callOneBot11WsAction(this.#connectionLifecycle.state === 'closed' ? undefined : this.#ws, this.#pending, this.#requestId, action, params);
|
|
169
|
-
}
|
|
170
178
|
|
|
171
179
|
#admitRaw(ev: OneBot11Event): void {
|
|
172
180
|
const selfId = resolveOnebot11SelfId(ev.self_id);
|
|
173
|
-
if (!this.#stopped && selfId
|
|
181
|
+
if (!this.#stopped && selfId) this[endpointSpec].reportSelfUserId(selfId);
|
|
174
182
|
if (!isMessageEvent(ev)) {
|
|
175
|
-
receiveOneBot11SideEvent(
|
|
176
|
-
(name, payload) => this.emit(name, payload),
|
|
177
|
-
this.#options.config.id,
|
|
178
|
-
{ callApi: (action, params) => callOnebot11Client(this
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
);
|
|
183
|
+
receiveOneBot11SideEvent(ev, {
|
|
184
|
+
emit: (name, payload) => this[endpointSpec].events.emit(name, payload),
|
|
185
|
+
endpointKey: this.#options.config.id,
|
|
186
|
+
caller: { callApi: (action, params) => callOnebot11Client({ client: this, action, params }) },
|
|
187
|
+
logger: this.#logger,
|
|
188
|
+
});
|
|
182
189
|
return;
|
|
183
190
|
}
|
|
184
|
-
const conversation = onebot11InboundConversation(String(this.#options.id), ev);
|
|
191
|
+
const conversation = { ...onebot11InboundConversation(String(this.#options.id), ev), endpoint: this[endpointSpec].identity };
|
|
185
192
|
const inbound = formatInboundMetadata(ev, this.#options.config.id);
|
|
186
|
-
void this.
|
|
193
|
+
void this[endpointSpec].events.message({
|
|
194
|
+
event: ev,
|
|
195
|
+
...(typeof ev.time === 'number' ? { platformSentAt: ev.time * 1000 } : {}),
|
|
187
196
|
conversation,
|
|
188
197
|
message: { conversation, id: String(ev.message_id) },
|
|
189
198
|
content: formatInboundContent(ev),
|
|
@@ -207,14 +216,14 @@ export class OneBot11WssEndpoint extends ClientEndpoint<Onebot11Client> {
|
|
|
207
216
|
|
|
208
217
|
/** Best-effort 登录账号探测;失败仅 debug,不影响连接。 */
|
|
209
218
|
async #probeSelfUserId(): Promise<void> {
|
|
210
|
-
const selfId = await probeOnebot11SelfId(this
|
|
219
|
+
const selfId = await probeOnebot11SelfId(this, (error) => {
|
|
211
220
|
this.#logger.debug(formatCompact({
|
|
212
221
|
op: 'onebot11_get_login_info_failed',
|
|
213
222
|
error: error instanceof Error ? error.message : String(error),
|
|
214
223
|
}));
|
|
215
224
|
});
|
|
216
|
-
if (this.#stopped || this
|
|
217
|
-
if (selfId
|
|
225
|
+
if (this.#stopped || this.#transportState !== 'open') return;
|
|
226
|
+
if (selfId) this[endpointSpec].reportSelfUserId(selfId);
|
|
218
227
|
}
|
|
219
228
|
|
|
220
229
|
#warnPlatformEvent(error: unknown): void {
|
|
@@ -235,12 +244,12 @@ export class OneBot11WssEndpoint extends ClientEndpoint<Onebot11Client> {
|
|
|
235
244
|
}
|
|
236
245
|
const socket = connection.socket as unknown as OneBot11WsSocket;
|
|
237
246
|
await this.#connectionLifecycle.stop();
|
|
238
|
-
|
|
247
|
+
this.#rpc.disconnect('连接已替换');
|
|
239
248
|
await this.#connectionLifecycle.start(async (lifecycleHandle) => {
|
|
240
249
|
this.#ws = socket;
|
|
241
250
|
lifecycleHandle.onForceClose(() => {
|
|
242
251
|
if (this.#ws === socket) {
|
|
243
|
-
|
|
252
|
+
this.#rpc.disconnect();
|
|
244
253
|
}
|
|
245
254
|
try {
|
|
246
255
|
socket.close();
|
|
@@ -262,11 +271,7 @@ export class OneBot11WssEndpoint extends ClientEndpoint<Onebot11Client> {
|
|
|
262
271
|
socket.on('message', (data) => {
|
|
263
272
|
if (this.#ws !== socket || this.#connectionLifecycle.state === 'closed') return;
|
|
264
273
|
this.#connectionLifecycle.notifyHeartbeatAck();
|
|
265
|
-
|
|
266
|
-
endpointId: this.#options.config.id,
|
|
267
|
-
pending: this.#pending,
|
|
268
|
-
ingest: (ev) => this.client.ingest(ev as Parameters<Onebot11Client['ingest']>[0]),
|
|
269
|
-
});
|
|
274
|
+
this.#rpc.receive(data, (ev) => this.ingest(ev as Parameters<Onebot11Client['ingest']>[0]));
|
|
270
275
|
});
|
|
271
276
|
socket.on('error', (error) => {
|
|
272
277
|
if (this.#ws !== socket) return;
|
|
@@ -276,7 +281,7 @@ export class OneBot11WssEndpoint extends ClientEndpoint<Onebot11Client> {
|
|
|
276
281
|
if (this.#ws !== socket) return;
|
|
277
282
|
if (this.#ws === socket) {
|
|
278
283
|
this.#ws = undefined;
|
|
279
|
-
|
|
284
|
+
this.#rpc.disconnect();
|
|
280
285
|
}
|
|
281
286
|
lifecycleHandle.notifyClosed(new Error('OneBot11 reverse WebSocket closed'));
|
|
282
287
|
});
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
export declare const onebot11RuntimeStateToken: import("zhin.js").Token<import("@zhin.js/adapter").EndpointRuntimeState>;
|
|
@@ -1,6 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* OneBot11 插件实例的运行时状态:adapter create() 注册的 endpoint 列表。
|
|
3
|
-
* 由 plugin.ts setup() provide,adapter create 与 `onebot11 endpoint` 命令共享(同一 owner generation)。
|
|
4
|
-
*/
|
|
5
|
-
import { defineEndpointRuntimeStateToken } from 'zhin.js/adapter';
|
|
6
|
-
export const onebot11RuntimeStateToken = defineEndpointRuntimeStateToken('onebot11');
|
|
@@ -1,7 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* OneBot11 插件实例的运行时状态:adapter create() 注册的 endpoint 列表。
|
|
3
|
-
* 由 plugin.ts setup() provide,adapter create 与 `onebot11 endpoint` 命令共享(同一 owner generation)。
|
|
4
|
-
*/
|
|
5
|
-
import { defineEndpointRuntimeStateToken } from 'zhin.js/adapter';
|
|
6
|
-
|
|
7
|
-
export const onebot11RuntimeStateToken = defineEndpointRuntimeStateToken('onebot11');
|