@zhin.js/adapter-onebot11 1.1.18 → 1.1.19

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 (48) hide show
  1. package/CHANGELOG.md +39 -0
  2. package/README.md +13 -6
  3. package/adapters/onebot11/index.js +3 -26
  4. package/adapters/onebot11/index.ts +2 -32
  5. package/commands/onebot11/endpoint/definition.js +0 -2
  6. package/commands/onebot11/endpoint/definition.ts +0 -2
  7. package/lib/adapter.d.ts +20 -0
  8. package/lib/adapter.js +26 -0
  9. package/lib/client.d.ts +13 -11
  10. package/lib/client.js +35 -12
  11. package/lib/console.d.ts +1 -1
  12. package/lib/console.js +2 -5
  13. package/lib/content-port.d.ts +2 -2
  14. package/lib/content-port.js +2 -2
  15. package/lib/endpoint-management.d.ts +2 -2
  16. package/lib/endpoint-management.js +8 -8
  17. package/lib/identity.d.ts +3 -0
  18. package/lib/identity.js +32 -0
  19. package/lib/index.d.ts +3 -2
  20. package/lib/index.js +3 -2
  21. package/lib/self-id.js +1 -1
  22. package/lib/side-event-dispatch.d.ts +8 -1
  23. package/lib/side-event-dispatch.js +2 -1
  24. package/lib/ws-endpoint.d.ts +5 -12
  25. package/lib/ws-endpoint.js +75 -50
  26. package/lib/ws-transport.d.ts +18 -11
  27. package/lib/ws-transport.js +28 -56
  28. package/lib/ws-types.d.ts +0 -6
  29. package/lib/wss-endpoint.d.ts +5 -12
  30. package/lib/wss-endpoint.js +76 -53
  31. package/package.json +18 -18
  32. package/plugin.js +0 -3
  33. package/src/adapter.ts +32 -0
  34. package/src/client.ts +38 -28
  35. package/src/console.ts +2 -5
  36. package/src/content-port.ts +4 -4
  37. package/src/endpoint-management.ts +15 -12
  38. package/src/identity.ts +30 -0
  39. package/src/index.ts +4 -4
  40. package/src/self-id.ts +1 -1
  41. package/src/side-event-dispatch.ts +9 -7
  42. package/src/ws-endpoint.ts +71 -64
  43. package/src/ws-transport.ts +41 -82
  44. package/src/ws-types.ts +0 -7
  45. package/src/wss-endpoint.ts +73 -68
  46. package/lib/onebot11-runtime-state.d.ts +0 -1
  47. package/lib/onebot11-runtime-state.js +0 -6
  48. package/src/onebot11-runtime-state.ts +0 -7
@@ -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
- ClientEndpoint,
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, createOnebot11EndpointClient, forwardOnebot11ClientEvents, type Onebot11Client } from './client.js';
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 ClientEndpoint<Onebot11Client> {
56
- readonly client: Onebot11Client;
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.recallMessage(id));
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
- #requestId = { value: 0 };
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
- super();
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
- this.client = createOnebot11EndpointClient(options.config, (action, params) => this.#callApi(action, params));
82
- const callApi = (action: string, params?: Record<string, unknown>) => callOnebot11Client(this.client, action, params);
83
- this.management = createOneBot11EndpointManagement({ callApi });
84
- this.content = createOneBot11ContentPort(callApi);
85
- this.bindClientEvents(
86
- (receive) => forwardOnebot11ClientEvents(this.client, receive),
87
- (name, payload) => {
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
- (_name, error) => this.#warnPlatformEvent(error),
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.close();
147
+ this.#open = false;
138
148
  this.#wsRelease?.();
139
149
  this.#wsRelease = undefined;
140
- await this.#connectionTask;
141
- await this.#connectionLifecycle.stop();
142
- rejectAllPending(this.#pending);
143
- this.#ws = undefined;
144
- this.#started = false;
145
- this.reportSelfUserId(undefined);
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 = await callOnebot11Client<{ message_id?: number | string }>(this.client, action, params);
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 callOnebot11Client(this.client, 'delete_msg', { message_id: Number(messageId) });
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 && selfId !== this.selfUserId) this.reportSelfUserId(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.client, action, params) },
179
- ev,
180
- this.#logger,
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.emit('message.receive', {
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.client, (error) => {
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.transportState !== 'open') return;
217
- if (selfId && selfId !== this.selfUserId) this.reportSelfUserId(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
- rejectAllPending(this.#pending, '连接已替换');
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
- rejectAllPending(this.#pending);
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
- handleOneBot11WsMessage(data, {
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
- rejectAllPending(this.#pending);
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');