@zhin.js/adapter-onebot11 6.0.14 → 7.0.1

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,36 +1,46 @@
1
1
  /**
2
2
  * OneBot11 reverse WSS endpoint — accepts inbound WebSocket from OneBot implementation.
3
3
  */
4
- import { clearInterval } from 'node:timers';
4
+ import { ClientEndpoint, createEndpointLifecycle, createRecallEndpointControl, } from 'zhin.js/adapter';
5
5
  import { formatCompact, getAdapterLogger } from '@zhin.js/logger';
6
6
  import { createOneBot11EndpointManagement } from './endpoint-management.js';
7
- import { registerOnebot11AgentEndpoint } from './onebot11-agent-deps.js';
8
7
  import { buildSendAction, formatInboundContent, formatInboundMetadata, formatOutboundSegments, isMessageEvent, onebot11InboundConversation, senderDisplayName, senderUserId, } from './protocol.js';
9
8
  import { receiveOneBot11SideEvent } from './side-event-dispatch.js';
10
9
  import { createOneBot11ContentPort } from './content-port.js';
11
10
  import { verifyOneBotAccessToken } from './wss-auth.js';
12
- import { callOneBot11WsAction, handleOneBot11WsMessage, rejectAllPending, startOneBot11Heartbeat, } from './ws-transport.js';
13
- 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;
14
15
  #logger;
15
16
  #options;
16
- management = createOneBot11EndpointManagement(this);
17
- content = createOneBot11ContentPort((action, params) => this.callApi(action, params));
17
+ management;
18
+ control = createRecallEndpointControl((id) => this.recallMessage(id));
19
+ content;
18
20
  #ws;
19
21
  #wsRelease;
20
- #heartbeatTimer;
22
+ #lifecycle;
21
23
  #requestId = { value: 0 };
22
24
  #pending = new Map();
23
- #open = false;
24
- #started = false;
25
- #unregisterAgent;
26
25
  constructor(options) {
26
+ super();
27
27
  this.#logger = getAdapterLogger('onebot11', options.config.id);
28
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));
29
42
  }
30
43
  async start() {
31
- if (this.#started)
32
- return;
33
- this.#started = true;
34
44
  if (!this.#options.config.access_token) {
35
45
  // wss 模式未配 access_token 时任何连接都会被放行(verifyOneBotAccessToken 直接 return true)
36
46
  this.#logger.warn(formatCompact({
@@ -40,10 +50,22 @@ export class OneBot11WssEndpoint {
40
50
  error: 'missing access_token',
41
51
  }));
42
52
  }
43
- this.#unregisterAgent = registerOnebot11AgentEndpoint(this.#options.config.id, this);
44
- const handle = this.#options.http.ws(this.#options.config.path);
45
- this.#wsRelease = handle.onConnection((connection) => {
46
- 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
+ });
47
69
  });
48
70
  this.#logger.info(formatCompact({
49
71
  op: 'listen',
@@ -52,67 +74,33 @@ export class OneBot11WssEndpoint {
52
74
  path: this.#options.config.path,
53
75
  }));
54
76
  }
55
- open() {
56
- this.#open = true;
57
- }
58
- close() {
59
- this.#open = false;
60
- }
61
77
  async stop() {
62
- this.#open = false;
63
- this.#unregisterAgent?.();
64
- this.#unregisterAgent = undefined;
65
- this.#wsRelease?.();
66
- this.#wsRelease = undefined;
67
- if (this.#heartbeatTimer) {
68
- clearInterval(this.#heartbeatTimer);
69
- this.#heartbeatTimer = undefined;
70
- }
78
+ this.close();
79
+ await this.#lifecycle.stop();
71
80
  rejectAllPending(this.#pending);
72
- if (this.#ws) {
73
- try {
74
- this.#ws.close();
75
- }
76
- catch {
77
- /* ignore */
78
- }
79
- this.#ws = undefined;
80
- }
81
- this.#started = false;
82
81
  }
83
82
  async send({ conversation, payload }) {
84
83
  const message = formatOutboundSegments(payload);
85
84
  const { action, params } = buildSendAction(conversation, message);
86
- const data = await this.callApi(action, params);
85
+ const data = await callOnebot11Client(this.client, action, params);
87
86
  return data?.message_id != null ? String(data.message_id) : '';
88
87
  }
89
88
  async recallMessage(messageId) {
90
89
  if (!messageId)
91
90
  return;
92
- await this.callApi('delete_msg', { message_id: Number(messageId) });
91
+ await callOnebot11Client(this.client, 'delete_msg', { message_id: Number(messageId) });
93
92
  }
94
- callApi(action, params = {}) {
93
+ #callApi(action, params = {}) {
95
94
  return callOneBot11WsAction(this.#ws, this.#pending, this.#requestId, action, params);
96
95
  }
97
- async setTitle(groupId, userId, title, duration = -1) {
98
- await this.callApi('set_group_special_title', {
99
- group_id: groupId,
100
- user_id: userId,
101
- special_title: title,
102
- duration,
103
- });
104
- return true;
105
- }
106
- admit(ev) {
107
- if (!this.#open)
108
- return;
96
+ #admitRaw(ev) {
109
97
  if (!isMessageEvent(ev)) {
110
- 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);
111
99
  return;
112
100
  }
113
101
  const conversation = onebot11InboundConversation(String(this.#options.id), ev);
114
102
  const inbound = formatInboundMetadata(ev, this.#options.config.id);
115
- void this.#options.gateway.receive({
103
+ void this.emit('message.receive', {
116
104
  conversation,
117
105
  message: { conversation, id: String(ev.message_id) },
118
106
  content: formatInboundContent(ev),
@@ -133,6 +121,12 @@ export class OneBot11WssEndpoint {
133
121
  }));
134
122
  });
135
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
+ }
136
130
  #acceptConnection(connection) {
137
131
  if (!verifyOneBotAccessToken(this.#options.config.access_token, connection.request)) {
138
132
  connection.socket.close(4003, 'Unauthorized');
@@ -148,21 +142,26 @@ export class OneBot11WssEndpoint {
148
142
  }
149
143
  }
150
144
  this.#ws = socket;
151
- 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
+ });
152
153
  socket.on('message', (data) => {
154
+ this.#lifecycle.notifyHeartbeatAck();
153
155
  handleOneBot11WsMessage(data, {
154
156
  endpointId: this.#options.config.id,
155
157
  pending: this.#pending,
156
- admit: (ev) => this.admit(ev),
158
+ ingest: (ev) => this.client.ingest(ev),
157
159
  });
158
160
  });
159
161
  socket.on('close', () => {
160
162
  if (this.#ws === socket) {
161
163
  this.#ws = undefined;
162
- if (this.#heartbeatTimer) {
163
- clearInterval(this.#heartbeatTimer);
164
- this.#heartbeatTimer = undefined;
165
- }
164
+ this.#lifecycle.stopHeartbeat();
166
165
  }
167
166
  });
168
167
  this.#logger.debug(formatCompact({
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@zhin.js/adapter-onebot11",
3
- "version": "6.0.14",
3
+ "version": "7.0.1",
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.1.11",
45
- "@zhin.js/core": "1.5.12",
46
- "@zhin.js/host-http": "1.0.11",
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.14",
58
- "@zhin.js/host-http": "1.0.11",
59
- "zhin.js": "6.0.12"
60
+ "@zhin.js/agent": "1.1.16",
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.1.11",
64
- "@zhin.js/agent": "1.1.14",
65
- "@zhin.js/command": "1.0.15",
66
- "@zhin.js/core": "1.5.12",
67
- "zhin.js": "6.0.12"
66
+ "@zhin.js/adapter": "1.2.1",
67
+ "@zhin.js/agent": "1.1.16",
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,18 +3,18 @@
3
3
  */
4
4
  import WebSocket from 'ws';
5
5
  import {
6
+ ClientEndpoint,
7
+ createRecallEndpointControl,
6
8
  createEndpointLifecycle,
7
9
  type EndpointConnectHandle,
8
- type EndpointInstance,
10
+ type EndpointControl,
9
11
  type EndpointLifecycle,
10
12
  type EndpointManagement,
11
13
  type EndpointSendRequest,
12
14
  } from 'zhin.js/adapter';
13
- import type { MessageGateway, SideEventGateway } from '@zhin.js/core/runtime';
14
15
  import { formatCompact, getAdapterLogger } from '@zhin.js/logger';
15
16
  import type { CapabilityId } from 'zhin.js';
16
17
  import { createOneBot11EndpointManagement } from './endpoint-management.js';
17
- import { registerOnebot11AgentEndpoint } from './onebot11-agent-deps.js';
18
18
  import {
19
19
  buildSendAction,
20
20
  buildWsConnectOptions,
@@ -40,11 +40,10 @@ import {
40
40
  type OneBot11WsCreateOptions,
41
41
  type OneBot11WsSocket,
42
42
  } from './ws-types.js';
43
+ import { callOnebot11Client, createOnebot11EndpointClient, forwardOnebot11ClientEvents, type Onebot11Client } from './client.js';
43
44
 
44
45
  export interface OneBot11WsEndpointOptions {
45
46
  readonly id: CapabilityId;
46
- readonly gateway: MessageGateway;
47
- readonly sideEvents?: SideEventGateway;
48
47
  readonly config: OneBot11WsConfig;
49
48
  readonly createWebSocket?: (
50
49
  url: string,
@@ -52,22 +51,34 @@ export interface OneBot11WsEndpointOptions {
52
51
  ) => OneBot11WsSocket;
53
52
  }
54
53
 
55
- export class OneBot11WsEndpoint implements EndpointInstance {
54
+ export class OneBot11WsEndpoint extends ClientEndpoint<Onebot11Client> {
55
+ readonly client: Onebot11Client;
56
56
  readonly #logger!: ReturnType<typeof getAdapterLogger>;
57
57
 
58
58
  readonly #options: OneBot11WsEndpointOptions;
59
- readonly management: EndpointManagement = createOneBot11EndpointManagement(this);
60
- readonly content = createOneBot11ContentPort((action, params) => this.callApi(action, params));
59
+ readonly management: EndpointManagement;
60
+ readonly control: EndpointControl = createRecallEndpointControl((id) => this.recallMessage(id));
61
+ readonly content;
61
62
  readonly #lifecycle: EndpointLifecycle;
62
63
  #ws?: OneBot11WsSocket;
63
64
  #requestId = { value: 0 };
64
65
  #pending = new Map<string, OneBot11PendingAction>();
65
- #open = false;
66
- #unregisterAgent?: () => void;
67
66
 
68
67
  constructor(options: OneBot11WsEndpointOptions) {
68
+ super();
69
69
  this.#logger = getAdapterLogger('onebot11', options.config.id);
70
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
+ );
71
82
  const { config } = options;
72
83
  this.#lifecycle = createEndpointLifecycle({
73
84
  name: config.id,
@@ -84,12 +95,10 @@ export class OneBot11WsEndpoint implements EndpointInstance {
84
95
 
85
96
  async start(): Promise<void> {
86
97
  if (this.#lifecycle.started) return;
87
- // agent 注册/反注册是适配器专有依赖,留在适配器侧(见 endpoint-lifecycle 迁移指引)
88
- this.#unregisterAgent = registerOnebot11AgentEndpoint(this.#options.config.id, this);
89
98
  try {
90
99
  await this.#lifecycle.start((handle) => this.#connect(handle));
91
100
  } catch (err) {
92
- // start 失败必须清理现场(状态复位由基座保证),避免 agent 注册泄漏
101
+ // start 失败必须清理现场(状态复位由基座保证)
93
102
  if (this.#ws) {
94
103
  try {
95
104
  this.#ws.close();
@@ -98,26 +107,14 @@ export class OneBot11WsEndpoint implements EndpointInstance {
98
107
  }
99
108
  this.#ws = undefined;
100
109
  }
101
- this.#unregisterAgent?.();
102
- this.#unregisterAgent = undefined;
103
110
  throw err;
104
111
  }
105
112
  }
106
113
 
107
- open(): void {
108
- this.#open = true;
109
- }
110
-
111
- close(): void {
112
- this.#open = false;
113
- }
114
-
115
114
  async stop(): Promise<void> {
116
- this.#open = false;
115
+ this.close();
117
116
  // 基座负责:清重连/心跳定时器、强关 ws、唤醒 stop-during-connect 竞态
118
117
  await this.#lifecycle.stop();
119
- this.#unregisterAgent?.();
120
- this.#unregisterAgent = undefined;
121
118
  rejectAllPending(this.#pending);
122
119
  this.#ws = undefined;
123
120
  }
@@ -125,7 +122,7 @@ export class OneBot11WsEndpoint implements EndpointInstance {
125
122
  async send({ conversation, payload }: EndpointSendRequest): Promise<string> {
126
123
  const message = formatOutboundSegments(payload);
127
124
  const { action, params } = buildSendAction(conversation, message);
128
- 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);
129
126
  const messageId = data?.message_id != null ? String(data.message_id) : '';
130
127
  this.#logger.debug(formatCompact({
131
128
  op: 'onebot11_send',
@@ -138,37 +135,22 @@ export class OneBot11WsEndpoint implements EndpointInstance {
138
135
 
139
136
  async recallMessage(messageId: string): Promise<void> {
140
137
  if (!messageId) return;
141
- await this.callApi('delete_msg', { message_id: Number(messageId) });
138
+ await callOnebot11Client(this.client, 'delete_msg', { message_id: Number(messageId) });
142
139
  }
143
140
 
144
- /** Public API for agent tools / callers. */
145
- callApi(action: string, params: Record<string, unknown> = {}): Promise<unknown> {
141
+ #callApi(
142
+ action: string,
143
+ params: Record<string, unknown> = {},
144
+ ): ReturnType<typeof callOneBot11WsAction> {
146
145
  return callOneBot11WsAction(this.#ws, this.#pending, this.#requestId, action, params);
147
146
  }
148
147
 
149
- async setTitle(
150
- groupId: number,
151
- userId: number,
152
- title: string,
153
- duration = -1,
154
- ): Promise<boolean> {
155
- await this.callApi('set_group_special_title', {
156
- group_id: groupId,
157
- user_id: userId,
158
- special_title: title,
159
- duration,
160
- });
161
- return true;
162
- }
163
-
164
- /** Test / internal: admit a parsed event when the endpoint is open. */
165
- admit(ev: OneBot11Event): void {
166
- if (!this.#open) return;
148
+ #admitRaw(ev: OneBot11Event): void {
167
149
  if (!isMessageEvent(ev)) {
168
150
  receiveOneBot11SideEvent(
169
- this.#options.sideEvents,
151
+ (name, payload) => this.emit(name, payload),
170
152
  this.#options.config.id,
171
- this,
153
+ { callApi: (action, params) => callOnebot11Client(this.client, action, params) },
172
154
  ev,
173
155
  this.#logger,
174
156
  );
@@ -177,7 +159,7 @@ export class OneBot11WsEndpoint implements EndpointInstance {
177
159
  const conversation = onebot11InboundConversation(String(this.#options.id), ev);
178
160
  const content = formatInboundContent(ev);
179
161
  const inbound = formatInboundMetadata(ev, this.#options.config.id);
180
- void this.#options.gateway.receive({
162
+ void this.emit('message.receive', {
181
163
  conversation,
182
164
  message: { conversation, id: String(ev.message_id) },
183
165
  content,
@@ -199,6 +181,13 @@ export class OneBot11WsEndpoint implements EndpointInstance {
199
181
  });
200
182
  }
201
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
+
202
191
  async #connect(handle: EndpointConnectHandle): Promise<void> {
203
192
  const { url, headers, safeUrl } = buildWsConnectOptions(this.#options.config);
204
193
  const create = this.#options.createWebSocket
@@ -240,7 +229,7 @@ export class OneBot11WsEndpoint implements EndpointInstance {
240
229
  handleOneBot11WsMessage(data, {
241
230
  endpointId: this.#options.config.id,
242
231
  pending: this.#pending,
243
- admit: (ev) => this.admit(ev),
232
+ ingest: (ev) => this.client.ingest(ev as Parameters<Onebot11Client['ingest']>[0]),
244
233
  });
245
234
  });
246
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';