@zhin.js/adapter-onebot11 7.0.0 → 7.0.2

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.
@@ -1,38 +1,46 @@
1
1
  /**
2
2
  * OneBot11 reverse WSS endpoint — accepts inbound WebSocket from OneBot implementation.
3
3
  */
4
- import { clearInterval } from 'node:timers';
5
- import { createRecallEndpointControl, } from 'zhin.js/adapter';
4
+ import { ClientEndpoint, createEndpointLifecycle, createRecallEndpointControl, } from 'zhin.js/adapter';
6
5
  import { formatCompact, getAdapterLogger } from '@zhin.js/logger';
7
6
  import { createOneBot11EndpointManagement } from './endpoint-management.js';
8
- import { registerOnebot11AgentEndpoint } from './onebot11-agent-deps.js';
9
7
  import { buildSendAction, formatInboundContent, formatInboundMetadata, formatOutboundSegments, isMessageEvent, onebot11InboundConversation, senderDisplayName, senderUserId, } from './protocol.js';
10
8
  import { receiveOneBot11SideEvent } from './side-event-dispatch.js';
11
9
  import { createOneBot11ContentPort } from './content-port.js';
12
10
  import { verifyOneBotAccessToken } from './wss-auth.js';
13
- import { callOneBot11WsAction, handleOneBot11WsMessage, rejectAllPending, startOneBot11Heartbeat, } from './ws-transport.js';
14
- export class OneBot11WssEndpoint {
11
+ import { callOneBot11WsAction, handleOneBot11WsMessage, rejectAllPending, } from './ws-transport.js';
12
+ import { callOnebot11Client, createOnebot11EndpointClient, forwardOnebot11ClientEvents } from './client.js';
13
+ export class OneBot11WssEndpoint extends ClientEndpoint {
14
+ client;
15
15
  #logger;
16
16
  #options;
17
- management = createOneBot11EndpointManagement(this);
17
+ management;
18
18
  control = createRecallEndpointControl((id) => this.recallMessage(id));
19
- content = createOneBot11ContentPort((action, params) => this.callApi(action, params));
19
+ content;
20
20
  #ws;
21
21
  #wsRelease;
22
- #heartbeatTimer;
22
+ #lifecycle;
23
23
  #requestId = { value: 0 };
24
24
  #pending = new Map();
25
- #open = false;
26
- #started = false;
27
- #unregisterAgent;
28
25
  constructor(options) {
26
+ super();
29
27
  this.#logger = getAdapterLogger('onebot11', options.config.id);
30
28
  this.#options = options;
29
+ this.#lifecycle = createEndpointLifecycle({
30
+ name: options.config.id,
31
+ reconnect: false,
32
+ heartbeat: { intervalMs: options.config.heartbeat_interval },
33
+ });
34
+ this.client = createOnebot11EndpointClient(options.config, (action, params) => this.#callApi(action, params));
35
+ const callApi = (action, params) => callOnebot11Client(this.client, action, params);
36
+ this.management = createOneBot11EndpointManagement({ callApi });
37
+ this.content = createOneBot11ContentPort(callApi);
38
+ this.bindClientEvents((receive) => forwardOnebot11ClientEvents(this.client, receive), (name, payload) => {
39
+ if (name === 'event')
40
+ this.#admitRaw(payload);
41
+ }, (_name, error) => this.#warnPlatformEvent(error));
31
42
  }
32
43
  async start() {
33
- if (this.#started)
34
- return;
35
- this.#started = true;
36
44
  if (!this.#options.config.access_token) {
37
45
  // wss 模式未配 access_token 时任何连接都会被放行(verifyOneBotAccessToken 直接 return true)
38
46
  this.#logger.warn(formatCompact({
@@ -42,10 +50,22 @@ export class OneBot11WssEndpoint {
42
50
  error: 'missing access_token',
43
51
  }));
44
52
  }
45
- this.#unregisterAgent = registerOnebot11AgentEndpoint(this.#options.config.id, this);
46
- const handle = this.#options.http.ws(this.#options.config.path);
47
- this.#wsRelease = handle.onConnection((connection) => {
48
- this.#acceptConnection(connection);
53
+ await this.#lifecycle.start(async (lifecycleHandle) => {
54
+ const handle = this.#options.http.ws(this.#options.config.path);
55
+ this.#wsRelease = handle.onConnection((connection) => {
56
+ this.#acceptConnection(connection);
57
+ });
58
+ lifecycleHandle.onForceClose(() => {
59
+ this.#wsRelease?.();
60
+ this.#wsRelease = undefined;
61
+ try {
62
+ this.#ws?.close();
63
+ }
64
+ catch {
65
+ /* ignore */
66
+ }
67
+ this.#ws = undefined;
68
+ });
49
69
  });
50
70
  this.#logger.info(formatCompact({
51
71
  op: 'listen',
@@ -54,67 +74,33 @@ export class OneBot11WssEndpoint {
54
74
  path: this.#options.config.path,
55
75
  }));
56
76
  }
57
- open() {
58
- this.#open = true;
59
- }
60
- close() {
61
- this.#open = false;
62
- }
63
77
  async stop() {
64
- this.#open = false;
65
- this.#unregisterAgent?.();
66
- this.#unregisterAgent = undefined;
67
- this.#wsRelease?.();
68
- this.#wsRelease = undefined;
69
- if (this.#heartbeatTimer) {
70
- clearInterval(this.#heartbeatTimer);
71
- this.#heartbeatTimer = undefined;
72
- }
78
+ this.close();
79
+ await this.#lifecycle.stop();
73
80
  rejectAllPending(this.#pending);
74
- if (this.#ws) {
75
- try {
76
- this.#ws.close();
77
- }
78
- catch {
79
- /* ignore */
80
- }
81
- this.#ws = undefined;
82
- }
83
- this.#started = false;
84
81
  }
85
82
  async send({ conversation, payload }) {
86
83
  const message = formatOutboundSegments(payload);
87
84
  const { action, params } = buildSendAction(conversation, message);
88
- const data = await this.callApi(action, params);
85
+ const data = await callOnebot11Client(this.client, action, params);
89
86
  return data?.message_id != null ? String(data.message_id) : '';
90
87
  }
91
88
  async recallMessage(messageId) {
92
89
  if (!messageId)
93
90
  return;
94
- await this.callApi('delete_msg', { message_id: Number(messageId) });
91
+ await callOnebot11Client(this.client, 'delete_msg', { message_id: Number(messageId) });
95
92
  }
96
- callApi(action, params = {}) {
93
+ #callApi(action, params = {}) {
97
94
  return callOneBot11WsAction(this.#ws, this.#pending, this.#requestId, action, params);
98
95
  }
99
- async setTitle(groupId, userId, title, duration = -1) {
100
- await this.callApi('set_group_special_title', {
101
- group_id: groupId,
102
- user_id: userId,
103
- special_title: title,
104
- duration,
105
- });
106
- return true;
107
- }
108
- admit(ev) {
109
- if (!this.#open)
110
- return;
96
+ #admitRaw(ev) {
111
97
  if (!isMessageEvent(ev)) {
112
- receiveOneBot11SideEvent(this.#options.sideEvents, this.#options.config.id, this, ev, this.#logger);
98
+ receiveOneBot11SideEvent((name, payload) => this.emit(name, payload), this.#options.config.id, { callApi: (action, params) => callOnebot11Client(this.client, action, params) }, ev, this.#logger);
113
99
  return;
114
100
  }
115
101
  const conversation = onebot11InboundConversation(String(this.#options.id), ev);
116
102
  const inbound = formatInboundMetadata(ev, this.#options.config.id);
117
- void this.#options.gateway.receive({
103
+ void this.emit('message.receive', {
118
104
  conversation,
119
105
  message: { conversation, id: String(ev.message_id) },
120
106
  content: formatInboundContent(ev),
@@ -135,6 +121,12 @@ export class OneBot11WssEndpoint {
135
121
  }));
136
122
  });
137
123
  }
124
+ #warnPlatformEvent(error) {
125
+ this.#logger.warn(formatCompact({
126
+ op: 'onebot11_platform_event_failed',
127
+ error: error instanceof Error ? error.message : String(error),
128
+ }));
129
+ }
138
130
  #acceptConnection(connection) {
139
131
  if (!verifyOneBotAccessToken(this.#options.config.access_token, connection.request)) {
140
132
  connection.socket.close(4003, 'Unauthorized');
@@ -150,21 +142,26 @@ export class OneBot11WssEndpoint {
150
142
  }
151
143
  }
152
144
  this.#ws = socket;
153
- this.#heartbeatTimer = startOneBot11Heartbeat(this.#ws, this.#options.config.heartbeat_interval, this.#heartbeatTimer);
145
+ this.#lifecycle.startHeartbeat(() => {
146
+ try {
147
+ this.#ws?.ping?.();
148
+ }
149
+ catch {
150
+ /* ignore */
151
+ }
152
+ });
154
153
  socket.on('message', (data) => {
154
+ this.#lifecycle.notifyHeartbeatAck();
155
155
  handleOneBot11WsMessage(data, {
156
156
  endpointId: this.#options.config.id,
157
157
  pending: this.#pending,
158
- admit: (ev) => this.admit(ev),
158
+ ingest: (ev) => this.client.ingest(ev),
159
159
  });
160
160
  });
161
161
  socket.on('close', () => {
162
162
  if (this.#ws === socket) {
163
163
  this.#ws = undefined;
164
- if (this.#heartbeatTimer) {
165
- clearInterval(this.#heartbeatTimer);
166
- this.#heartbeatTimer = undefined;
167
- }
164
+ this.#lifecycle.stopHeartbeat();
168
165
  }
169
166
  });
170
167
  this.#logger.debug(formatCompact({
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@zhin.js/adapter-onebot11",
3
- "version": "7.0.0",
3
+ "version": "7.0.2",
4
4
  "description": "Zhin.js OneBot 11 adapter for Plugin Runtime (WebSocket client)",
5
5
  "type": "module",
6
6
  "main": "./lib/index.js",
@@ -40,13 +40,16 @@
40
40
  "directory": "plugins/adapters/onebot11"
41
41
  },
42
42
  "dependencies": {
43
+ "@imhelper/onebot-v11": "1.0.7",
44
+ "imhelper": "1.0.7",
43
45
  "ws": "^8.21.1",
44
- "@zhin.js/adapter": "1.2.0",
45
- "@zhin.js/core": "1.5.13",
46
- "@zhin.js/host-http": "1.0.12",
46
+ "@zhin.js/adapter": "1.2.1",
47
+ "@zhin.js/core": "1.5.14",
48
+ "@zhin.js/feature-kit": "1.0.13",
49
+ "@zhin.js/host-http": "1.0.13",
47
50
  "@zhin.js/im-contract": "1.0.4",
48
- "@zhin.js/logger": "1.0.76",
49
- "@zhin.js/permission": "1.0.3"
51
+ "@zhin.js/logger": "1.0.77",
52
+ "@zhin.js/permission": "1.0.4"
50
53
  },
51
54
  "devDependencies": {
52
55
  "@types/node": "^26.1.2",
@@ -54,17 +57,17 @@
54
57
  "typescript": "^6.0.3",
55
58
  "vitest": "^4.1.10",
56
59
  "zod": "^4.4.3",
57
- "@zhin.js/agent": "1.1.15",
58
- "@zhin.js/host-http": "1.0.12",
59
- "zhin.js": "6.0.13"
60
+ "@zhin.js/agent": "1.1.17",
61
+ "@zhin.js/host-http": "1.0.13",
62
+ "zhin.js": "6.0.14"
60
63
  },
61
64
  "peerDependencies": {
62
65
  "zod": "^4.0.0",
63
- "@zhin.js/adapter": "1.2.0",
64
- "@zhin.js/agent": "1.1.15",
65
- "@zhin.js/command": "1.0.15",
66
- "@zhin.js/core": "1.5.13",
67
- "zhin.js": "6.0.13"
66
+ "@zhin.js/adapter": "1.2.1",
67
+ "@zhin.js/agent": "1.1.17",
68
+ "@zhin.js/command": "1.0.16",
69
+ "@zhin.js/core": "1.5.14",
70
+ "zhin.js": "6.0.14"
68
71
  },
69
72
  "peerDependenciesMeta": {
70
73
  "@zhin.js/agent": {
package/src/client.ts ADDED
@@ -0,0 +1,78 @@
1
+ import {
2
+ OneBotV11Client,
3
+ type OneBotV11Event as ImHelperOneBot11Event,
4
+ type OneBotV11Response,
5
+ } from '@imhelper/onebot-v11';
6
+ import { EventFactory, type EventMap, type ImHelperEventMap } from 'imhelper';
7
+ import {
8
+ defineEndpointClient,
9
+ forwardEndpointClientEvents,
10
+ type ClientEventPayloads,
11
+ } from 'zhin.js/adapter';
12
+ import type { ResolvedOneBot11Config } from './protocol.js';
13
+
14
+ export { OneBotV11Client as Onebot11Client } from '@imhelper/onebot-v11';
15
+
16
+ export type Onebot11ApiCall = (
17
+ action: string,
18
+ params?: Record<string, unknown>,
19
+ ) => Promise<OneBotV11Response>;
20
+
21
+ type Onebot11ClientEvents = ImHelperEventMap<
22
+ number,
23
+ ImHelperOneBot11Event,
24
+ EventMap<number>
25
+ >;
26
+ export type Onebot11ClientEventMap = ClientEventPayloads<Onebot11ClientEvents>;
27
+
28
+ export function createOnebot11EndpointClient(
29
+ config: ResolvedOneBot11Config,
30
+ request: Onebot11ApiCall,
31
+ ): OneBotV11Client {
32
+ return new OneBotV11Client({
33
+ baseUrl: config.connection === 'ws'
34
+ ? config.url.replace(/^ws(s?):/, 'http$1:')
35
+ : 'http://localhost',
36
+ selfId: config.id,
37
+ accessToken: config.access_token,
38
+ receiveMode: 'manual',
39
+ call: request,
40
+ });
41
+ }
42
+
43
+ export async function callOnebot11Client<T = unknown>(
44
+ client: OneBotV11Client,
45
+ action: string,
46
+ params?: Record<string, unknown>,
47
+ ): Promise<T | undefined> {
48
+ const response: OneBotV11Response<T> = await client.call<T>(action, params);
49
+ if (response.status !== 'ok') {
50
+ throw new Error(
51
+ `OneBot 11 API ${action}: retcode=${response.retcode}${response.message ? ` ${response.message}` : ''}`,
52
+ );
53
+ }
54
+ return response.data;
55
+ }
56
+
57
+ const onebot11ClientEventNames = Object.freeze([
58
+ ...EventFactory.getSupportedEventTypes<number>(),
59
+ 'event',
60
+ ]);
61
+
62
+ export function forwardOnebot11ClientEvents(
63
+ client: OneBotV11Client,
64
+ receive: (name: string, payload: unknown) => void,
65
+ ): () => void {
66
+ return forwardEndpointClientEvents(client, onebot11ClientEventNames, receive);
67
+ }
68
+
69
+ declare module '@zhin.js/feature-kit' {
70
+ interface AdapterClientRegistry {
71
+ readonly onebot11: {
72
+ readonly client: OneBotV11Client;
73
+ readonly events: Onebot11ClientEventMap;
74
+ };
75
+ }
76
+ }
77
+
78
+ export const onebot11Client = defineEndpointClient<OneBotV11Client, Onebot11ClientEventMap>('onebot11');
package/src/index.ts CHANGED
@@ -51,9 +51,18 @@ export {
51
51
  export { verifyOneBotAccessToken } from './wss-auth.js';
52
52
 
53
53
  export {
54
- getOnebot11AgentDeps,
55
- registerOnebot11AgentEndpoint,
56
- setOnebot11AgentDeps,
57
- type Onebot11AgentDeps,
58
- type Onebot11AgentEndpoint,
59
- } from './onebot11-agent-deps.js';
54
+ Onebot11Client,
55
+ onebot11Client,
56
+ type Onebot11ClientEventMap,
57
+ } from './client.js';
58
+
59
+ export type {
60
+ OneBotV11ActionUrlResolver,
61
+ OneBotV11AdapterConfig as ImHelperOneBotV11AdapterConfig,
62
+ OneBotV11Call,
63
+ OneBotV11ClientConfig,
64
+ OneBotV11Event as ImHelperOneBotV11Event,
65
+ OneBotV11Response,
66
+ } from '@imhelper/onebot-v11';
67
+ export { ProtocolError } from '@imhelper/onebot-v11';
68
+ export type { ProtocolErrorKind, ProtocolErrorOptions } from 'imhelper';
package/src/protocol.ts CHANGED
@@ -96,9 +96,10 @@ export interface OneBot11ActionRequest {
96
96
  }
97
97
 
98
98
  export interface OneBot11ActionResponse {
99
- status: string;
99
+ status: 'ok' | 'failed';
100
100
  retcode: number;
101
101
  data?: unknown;
102
+ message?: string;
102
103
  echo?: string;
103
104
  }
104
105
 
@@ -196,7 +197,7 @@ export function onebot11InboundConversation(endpointKey: string, ev: OneBot11Eve
196
197
  };
197
198
  }
198
199
 
199
- /** Build inbound text for MessageGateway.receive */
200
+ /** Build inbound text for OutboundMessageService.receive */
200
201
  export function formatInboundContent(ev: OneBot11Event): string {
201
202
  if (Array.isArray(ev.message)) {
202
203
  return ev.message
@@ -1,5 +1,5 @@
1
1
  import { receiveOneBotLikeSideEvent } from '@zhin.js/core';
2
- import type { SideEventGateway } from '@zhin.js/core/runtime';
2
+ import type { EndpointEventEmitter } from 'zhin.js/adapter';
3
3
  import { formatCompact, type getAdapterLogger } from '@zhin.js/logger';
4
4
  import type { OneBot11Event } from './protocol.js';
5
5
 
@@ -8,19 +8,19 @@ export interface OneBot11SideEventCaller {
8
8
  }
9
9
 
10
10
  export function receiveOneBot11SideEvent(
11
- sideEvents: SideEventGateway | undefined,
11
+ emit: EndpointEventEmitter,
12
12
  endpointKey: string,
13
13
  caller: OneBot11SideEventCaller,
14
14
  raw: OneBot11Event,
15
15
  logger: ReturnType<typeof getAdapterLogger>,
16
16
  ): void {
17
- if (!sideEvents) return;
17
+ if (!emit) return;
18
18
  const record = raw as Record<string, unknown>;
19
19
  const postType = String(record.post_type ?? '');
20
20
  const requestType = String(record.request_type ?? '');
21
21
  const isRequest = postType === 'request' || postType.startsWith('request.');
22
22
  const isFriend = requestType === 'friend' || postType.includes('friend');
23
- void receiveOneBotLikeSideEvent(sideEvents, {
23
+ void receiveOneBotLikeSideEvent(emit, {
24
24
  adapter: 'onebot11',
25
25
  endpointKey,
26
26
  platform: 'onebot',
@@ -3,20 +3,18 @@
3
3
  */
4
4
  import WebSocket from 'ws';
5
5
  import {
6
+ ClientEndpoint,
6
7
  createRecallEndpointControl,
7
8
  createEndpointLifecycle,
8
9
  type EndpointConnectHandle,
9
10
  type EndpointControl,
10
- type EndpointInstance,
11
11
  type EndpointLifecycle,
12
12
  type EndpointManagement,
13
13
  type EndpointSendRequest,
14
14
  } from 'zhin.js/adapter';
15
- import type { MessageGateway, SideEventGateway } from '@zhin.js/core/runtime';
16
15
  import { formatCompact, getAdapterLogger } from '@zhin.js/logger';
17
16
  import type { CapabilityId } from 'zhin.js';
18
17
  import { createOneBot11EndpointManagement } from './endpoint-management.js';
19
- import { registerOnebot11AgentEndpoint } from './onebot11-agent-deps.js';
20
18
  import {
21
19
  buildSendAction,
22
20
  buildWsConnectOptions,
@@ -42,11 +40,10 @@ import {
42
40
  type OneBot11WsCreateOptions,
43
41
  type OneBot11WsSocket,
44
42
  } from './ws-types.js';
43
+ import { callOnebot11Client, createOnebot11EndpointClient, forwardOnebot11ClientEvents, type Onebot11Client } from './client.js';
45
44
 
46
45
  export interface OneBot11WsEndpointOptions {
47
46
  readonly id: CapabilityId;
48
- readonly gateway: MessageGateway;
49
- readonly sideEvents?: SideEventGateway;
50
47
  readonly config: OneBot11WsConfig;
51
48
  readonly createWebSocket?: (
52
49
  url: string,
@@ -54,23 +51,34 @@ export interface OneBot11WsEndpointOptions {
54
51
  ) => OneBot11WsSocket;
55
52
  }
56
53
 
57
- export class OneBot11WsEndpoint implements EndpointInstance {
54
+ export class OneBot11WsEndpoint extends ClientEndpoint<Onebot11Client> {
55
+ readonly client: Onebot11Client;
58
56
  readonly #logger!: ReturnType<typeof getAdapterLogger>;
59
57
 
60
58
  readonly #options: OneBot11WsEndpointOptions;
61
- readonly management: EndpointManagement = createOneBot11EndpointManagement(this);
59
+ readonly management: EndpointManagement;
62
60
  readonly control: EndpointControl = createRecallEndpointControl((id) => this.recallMessage(id));
63
- readonly content = createOneBot11ContentPort((action, params) => this.callApi(action, params));
61
+ readonly content;
64
62
  readonly #lifecycle: EndpointLifecycle;
65
63
  #ws?: OneBot11WsSocket;
66
64
  #requestId = { value: 0 };
67
65
  #pending = new Map<string, OneBot11PendingAction>();
68
- #open = false;
69
- #unregisterAgent?: () => void;
70
66
 
71
67
  constructor(options: OneBot11WsEndpointOptions) {
68
+ super();
72
69
  this.#logger = getAdapterLogger('onebot11', options.config.id);
73
70
  this.#options = options;
71
+ this.client = createOnebot11EndpointClient(options.config, (action, params) => this.#callApi(action, params));
72
+ const callApi = (action: string, params?: Record<string, unknown>) => callOnebot11Client(this.client, action, params);
73
+ this.management = createOneBot11EndpointManagement({ callApi });
74
+ this.content = createOneBot11ContentPort(callApi);
75
+ this.bindClientEvents(
76
+ (receive) => forwardOnebot11ClientEvents(this.client, receive),
77
+ (name, payload) => {
78
+ if (name === 'event') this.#admitRaw(payload as OneBot11Event);
79
+ },
80
+ (_name, error) => this.#warnPlatformEvent(error),
81
+ );
74
82
  const { config } = options;
75
83
  this.#lifecycle = createEndpointLifecycle({
76
84
  name: config.id,
@@ -87,12 +95,10 @@ export class OneBot11WsEndpoint implements EndpointInstance {
87
95
 
88
96
  async start(): Promise<void> {
89
97
  if (this.#lifecycle.started) return;
90
- // agent 注册/反注册是适配器专有依赖,留在适配器侧(见 endpoint-lifecycle 迁移指引)
91
- this.#unregisterAgent = registerOnebot11AgentEndpoint(this.#options.config.id, this);
92
98
  try {
93
99
  await this.#lifecycle.start((handle) => this.#connect(handle));
94
100
  } catch (err) {
95
- // start 失败必须清理现场(状态复位由基座保证),避免 agent 注册泄漏
101
+ // start 失败必须清理现场(状态复位由基座保证)
96
102
  if (this.#ws) {
97
103
  try {
98
104
  this.#ws.close();
@@ -101,26 +107,14 @@ export class OneBot11WsEndpoint implements EndpointInstance {
101
107
  }
102
108
  this.#ws = undefined;
103
109
  }
104
- this.#unregisterAgent?.();
105
- this.#unregisterAgent = undefined;
106
110
  throw err;
107
111
  }
108
112
  }
109
113
 
110
- open(): void {
111
- this.#open = true;
112
- }
113
-
114
- close(): void {
115
- this.#open = false;
116
- }
117
-
118
114
  async stop(): Promise<void> {
119
- this.#open = false;
115
+ this.close();
120
116
  // 基座负责:清重连/心跳定时器、强关 ws、唤醒 stop-during-connect 竞态
121
117
  await this.#lifecycle.stop();
122
- this.#unregisterAgent?.();
123
- this.#unregisterAgent = undefined;
124
118
  rejectAllPending(this.#pending);
125
119
  this.#ws = undefined;
126
120
  }
@@ -128,7 +122,7 @@ export class OneBot11WsEndpoint implements EndpointInstance {
128
122
  async send({ conversation, payload }: EndpointSendRequest): Promise<string> {
129
123
  const message = formatOutboundSegments(payload);
130
124
  const { action, params } = buildSendAction(conversation, message);
131
- const data = await this.callApi(action, params) as { message_id?: number | string } | undefined;
125
+ const data = await callOnebot11Client<{ message_id?: number | string }>(this.client, action, params);
132
126
  const messageId = data?.message_id != null ? String(data.message_id) : '';
133
127
  this.#logger.debug(formatCompact({
134
128
  op: 'onebot11_send',
@@ -141,37 +135,22 @@ export class OneBot11WsEndpoint implements EndpointInstance {
141
135
 
142
136
  async recallMessage(messageId: string): Promise<void> {
143
137
  if (!messageId) return;
144
- await this.callApi('delete_msg', { message_id: Number(messageId) });
138
+ await callOnebot11Client(this.client, 'delete_msg', { message_id: Number(messageId) });
145
139
  }
146
140
 
147
- /** Public API for agent tools / callers. */
148
- callApi(action: string, params: Record<string, unknown> = {}): Promise<unknown> {
141
+ #callApi(
142
+ action: string,
143
+ params: Record<string, unknown> = {},
144
+ ): ReturnType<typeof callOneBot11WsAction> {
149
145
  return callOneBot11WsAction(this.#ws, this.#pending, this.#requestId, action, params);
150
146
  }
151
147
 
152
- async setTitle(
153
- groupId: number,
154
- userId: number,
155
- title: string,
156
- duration = -1,
157
- ): Promise<boolean> {
158
- await this.callApi('set_group_special_title', {
159
- group_id: groupId,
160
- user_id: userId,
161
- special_title: title,
162
- duration,
163
- });
164
- return true;
165
- }
166
-
167
- /** Test / internal: admit a parsed event when the endpoint is open. */
168
- admit(ev: OneBot11Event): void {
169
- if (!this.#open) return;
148
+ #admitRaw(ev: OneBot11Event): void {
170
149
  if (!isMessageEvent(ev)) {
171
150
  receiveOneBot11SideEvent(
172
- this.#options.sideEvents,
151
+ (name, payload) => this.emit(name, payload),
173
152
  this.#options.config.id,
174
- this,
153
+ { callApi: (action, params) => callOnebot11Client(this.client, action, params) },
175
154
  ev,
176
155
  this.#logger,
177
156
  );
@@ -180,7 +159,7 @@ export class OneBot11WsEndpoint implements EndpointInstance {
180
159
  const conversation = onebot11InboundConversation(String(this.#options.id), ev);
181
160
  const content = formatInboundContent(ev);
182
161
  const inbound = formatInboundMetadata(ev, this.#options.config.id);
183
- void this.#options.gateway.receive({
162
+ void this.emit('message.receive', {
184
163
  conversation,
185
164
  message: { conversation, id: String(ev.message_id) },
186
165
  content,
@@ -202,6 +181,13 @@ export class OneBot11WsEndpoint implements EndpointInstance {
202
181
  });
203
182
  }
204
183
 
184
+ #warnPlatformEvent(error: unknown): void {
185
+ this.#logger.warn(formatCompact({
186
+ op: 'onebot11_platform_event_failed',
187
+ error: error instanceof Error ? error.message : String(error),
188
+ }));
189
+ }
190
+
205
191
  async #connect(handle: EndpointConnectHandle): Promise<void> {
206
192
  const { url, headers, safeUrl } = buildWsConnectOptions(this.#options.config);
207
193
  const create = this.#options.createWebSocket
@@ -243,7 +229,7 @@ export class OneBot11WsEndpoint implements EndpointInstance {
243
229
  handleOneBot11WsMessage(data, {
244
230
  endpointId: this.#options.config.id,
245
231
  pending: this.#pending,
246
- admit: (ev) => this.admit(ev),
232
+ ingest: (ev) => this.client.ingest(ev as Parameters<Onebot11Client['ingest']>[0]),
247
233
  });
248
234
  });
249
235
 
@@ -25,7 +25,7 @@ export function handleOneBot11WsMessage(
25
25
  options: {
26
26
  readonly endpointId: string;
27
27
  readonly pending: Map<string, OneBot11PendingAction>;
28
- readonly admit: (ev: OneBot11Event) => void;
28
+ readonly ingest: (ev: OneBot11Event) => void;
29
29
  },
30
30
  ): void {
31
31
  try {
@@ -37,12 +37,11 @@ export function handleOneBot11WsMessage(
37
37
  if (pending) {
38
38
  options.pending.delete(resp.echo!);
39
39
  clearTimeout(pending.timeout);
40
- if (resp.status === 'ok') pending.resolve(resp.data);
41
- else pending.reject(new Error(`OneBot11 retcode=${resp.retcode}`));
40
+ pending.resolve(resp);
42
41
  }
43
42
  return;
44
43
  }
45
- options.admit(msg as OneBot11Event);
44
+ options.ingest(msg as OneBot11Event);
46
45
  } catch (error) {
47
46
  logger.warn(formatCompact({
48
47
  op: 'onebot11_parse_failed',
@@ -58,7 +57,7 @@ export function callOneBot11WsAction(
58
57
  requestId: { value: number },
59
58
  action: string,
60
59
  params: Record<string, unknown>,
61
- ): Promise<unknown> {
60
+ ): Promise<OneBot11ActionResponse> {
62
61
  if (!ws || ws.readyState !== WS_OPEN) {
63
62
  return Promise.reject(new Error('WebSocket 未连接'));
64
63
  }
package/src/ws-types.ts CHANGED
@@ -14,7 +14,8 @@ export interface OneBot11WsCreateOptions {
14
14
  export const WS_OPEN = 1;
15
15
 
16
16
  export interface OneBot11PendingAction {
17
- resolve: (value: unknown) => void;
17
+ resolve: (value: OneBot11ActionResponse) => void;
18
18
  reject: (err: Error) => void;
19
19
  timeout: NodeJS.Timeout;
20
20
  }
21
+ import type { OneBot11ActionResponse } from './protocol.js';