@zhin.js/adapter-onebot11 7.0.0 → 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.
- package/CHANGELOG.md +24 -0
- package/README.md +23 -4
- package/adapters/onebot11.js +0 -7
- package/adapters/onebot11.ts +0 -7
- package/agent/tools/set_title.ts +11 -8
- package/lib/client.d.ts +20 -0
- package/lib/client.js +30 -0
- package/lib/index.d.ts +4 -1
- package/lib/index.js +2 -1
- package/lib/onebot11-endpoint-commands.d.ts +1 -1
- package/lib/protocol.d.ts +3 -2
- package/lib/protocol.js +1 -1
- package/lib/side-event-dispatch.d.ts +2 -2
- package/lib/side-event-dispatch.js +3 -3
- package/lib/ws-endpoint.d.ts +5 -13
- package/lib/ws-endpoint.js +30 -41
- package/lib/ws-transport.d.ts +3 -3
- package/lib/ws-transport.js +2 -5
- package/lib/ws-types.d.ts +2 -1
- package/lib/wss-endpoint.d.ts +8 -11
- package/lib/wss-endpoint.js +63 -66
- package/package.json +17 -14
- package/src/client.ts +78 -0
- package/src/index.ts +15 -6
- package/src/protocol.ts +3 -2
- package/src/side-event-dispatch.ts +4 -4
- package/src/ws-endpoint.ts +38 -52
- package/src/ws-transport.ts +4 -5
- package/src/ws-types.ts +2 -1
- package/src/wss-endpoint.ts +70 -77
- package/lib/onebot11-agent-deps.d.ts +0 -15
- package/lib/onebot11-agent-deps.js +0 -30
- package/src/onebot11-agent-deps.ts +0 -49
package/src/wss-endpoint.ts
CHANGED
|
@@ -1,20 +1,19 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* OneBot11 reverse WSS endpoint — accepts inbound WebSocket from OneBot implementation.
|
|
3
3
|
*/
|
|
4
|
-
import { clearInterval } from 'node:timers';
|
|
5
4
|
import {
|
|
5
|
+
ClientEndpoint,
|
|
6
|
+
createEndpointLifecycle,
|
|
6
7
|
createRecallEndpointControl,
|
|
8
|
+
type EndpointLifecycle,
|
|
7
9
|
type EndpointControl,
|
|
8
|
-
type EndpointInstance,
|
|
9
10
|
type EndpointManagement,
|
|
10
11
|
type EndpointSendRequest,
|
|
11
12
|
} from 'zhin.js/adapter';
|
|
12
|
-
import type { MessageGateway, SideEventGateway } from '@zhin.js/core/runtime';
|
|
13
13
|
import type { HttpHost, WsConnection } from '@zhin.js/host-http';
|
|
14
14
|
import { formatCompact, getAdapterLogger } from '@zhin.js/logger';
|
|
15
15
|
import type { CapabilityId } from 'zhin.js';
|
|
16
16
|
import { createOneBot11EndpointManagement } from './endpoint-management.js';
|
|
17
|
-
import { registerOnebot11AgentEndpoint } from './onebot11-agent-deps.js';
|
|
18
17
|
import {
|
|
19
18
|
buildSendAction,
|
|
20
19
|
formatInboundContent,
|
|
@@ -34,46 +33,57 @@ import {
|
|
|
34
33
|
callOneBot11WsAction,
|
|
35
34
|
handleOneBot11WsMessage,
|
|
36
35
|
rejectAllPending,
|
|
37
|
-
startOneBot11Heartbeat,
|
|
38
36
|
} from './ws-transport.js';
|
|
39
37
|
import { OneBot11WsEndpoint } from './ws-endpoint.js';
|
|
40
38
|
import {
|
|
41
39
|
type OneBot11PendingAction,
|
|
42
40
|
type OneBot11WsSocket,
|
|
43
41
|
} from './ws-types.js';
|
|
42
|
+
import { callOnebot11Client, createOnebot11EndpointClient, forwardOnebot11ClientEvents, type Onebot11Client } from './client.js';
|
|
44
43
|
|
|
45
44
|
export interface OneBot11WssEndpointOptions {
|
|
46
45
|
readonly id: CapabilityId;
|
|
47
|
-
readonly gateway: MessageGateway;
|
|
48
|
-
readonly sideEvents?: SideEventGateway;
|
|
49
46
|
readonly http: HttpHost;
|
|
50
47
|
readonly config: OneBot11WssConfig;
|
|
51
48
|
}
|
|
52
49
|
|
|
53
|
-
export class OneBot11WssEndpoint
|
|
50
|
+
export class OneBot11WssEndpoint extends ClientEndpoint<Onebot11Client> {
|
|
51
|
+
readonly client: Onebot11Client;
|
|
54
52
|
readonly #logger!: ReturnType<typeof getAdapterLogger>;
|
|
55
53
|
|
|
56
54
|
readonly #options: OneBot11WssEndpointOptions;
|
|
57
|
-
readonly management: EndpointManagement
|
|
55
|
+
readonly management: EndpointManagement;
|
|
58
56
|
readonly control: EndpointControl = createRecallEndpointControl((id) => this.recallMessage(id));
|
|
59
|
-
readonly content
|
|
57
|
+
readonly content;
|
|
60
58
|
#ws?: OneBot11WsSocket;
|
|
61
59
|
#wsRelease?: () => void;
|
|
62
|
-
#
|
|
60
|
+
readonly #lifecycle: EndpointLifecycle;
|
|
63
61
|
#requestId = { value: 0 };
|
|
64
62
|
#pending = new Map<string, OneBot11PendingAction>();
|
|
65
|
-
#open = false;
|
|
66
|
-
#started = false;
|
|
67
|
-
#unregisterAgent?: () => void;
|
|
68
63
|
|
|
69
64
|
constructor(options: OneBot11WssEndpointOptions) {
|
|
65
|
+
super();
|
|
70
66
|
this.#logger = getAdapterLogger('onebot11', options.config.id);
|
|
71
67
|
this.#options = options;
|
|
68
|
+
this.#lifecycle = createEndpointLifecycle({
|
|
69
|
+
name: options.config.id,
|
|
70
|
+
reconnect: false,
|
|
71
|
+
heartbeat: { intervalMs: options.config.heartbeat_interval },
|
|
72
|
+
});
|
|
73
|
+
this.client = createOnebot11EndpointClient(options.config, (action, params) => this.#callApi(action, params));
|
|
74
|
+
const callApi = (action: string, params?: Record<string, unknown>) => callOnebot11Client(this.client, action, params);
|
|
75
|
+
this.management = createOneBot11EndpointManagement({ callApi });
|
|
76
|
+
this.content = createOneBot11ContentPort(callApi);
|
|
77
|
+
this.bindClientEvents(
|
|
78
|
+
(receive) => forwardOnebot11ClientEvents(this.client, receive),
|
|
79
|
+
(name, payload) => {
|
|
80
|
+
if (name === 'event') this.#admitRaw(payload as OneBot11Event);
|
|
81
|
+
},
|
|
82
|
+
(_name, error) => this.#warnPlatformEvent(error),
|
|
83
|
+
);
|
|
72
84
|
}
|
|
73
85
|
|
|
74
86
|
async start(): Promise<void> {
|
|
75
|
-
if (this.#started) return;
|
|
76
|
-
this.#started = true;
|
|
77
87
|
if (!this.#options.config.access_token) {
|
|
78
88
|
// wss 模式未配 access_token 时任何连接都会被放行(verifyOneBotAccessToken 直接 return true)
|
|
79
89
|
this.#logger.warn(formatCompact({
|
|
@@ -83,13 +93,21 @@ export class OneBot11WssEndpoint implements EndpointInstance {
|
|
|
83
93
|
error: 'missing access_token',
|
|
84
94
|
}));
|
|
85
95
|
}
|
|
86
|
-
this.#
|
|
87
|
-
this.#options.config.
|
|
88
|
-
this
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
96
|
+
await this.#lifecycle.start(async (lifecycleHandle) => {
|
|
97
|
+
const handle = this.#options.http.ws(this.#options.config.path);
|
|
98
|
+
this.#wsRelease = handle.onConnection((connection) => {
|
|
99
|
+
this.#acceptConnection(connection);
|
|
100
|
+
});
|
|
101
|
+
lifecycleHandle.onForceClose(() => {
|
|
102
|
+
this.#wsRelease?.();
|
|
103
|
+
this.#wsRelease = undefined;
|
|
104
|
+
try {
|
|
105
|
+
this.#ws?.close();
|
|
106
|
+
} catch {
|
|
107
|
+
/* ignore */
|
|
108
|
+
}
|
|
109
|
+
this.#ws = undefined;
|
|
110
|
+
});
|
|
93
111
|
});
|
|
94
112
|
this.#logger.info(formatCompact({
|
|
95
113
|
op: 'listen',
|
|
@@ -99,69 +117,37 @@ export class OneBot11WssEndpoint implements EndpointInstance {
|
|
|
99
117
|
}));
|
|
100
118
|
}
|
|
101
119
|
|
|
102
|
-
open(): void {
|
|
103
|
-
this.#open = true;
|
|
104
|
-
}
|
|
105
|
-
|
|
106
|
-
close(): void {
|
|
107
|
-
this.#open = false;
|
|
108
|
-
}
|
|
109
|
-
|
|
110
120
|
async stop(): Promise<void> {
|
|
111
|
-
this
|
|
112
|
-
this.#
|
|
113
|
-
this.#unregisterAgent = undefined;
|
|
114
|
-
this.#wsRelease?.();
|
|
115
|
-
this.#wsRelease = undefined;
|
|
116
|
-
if (this.#heartbeatTimer) {
|
|
117
|
-
clearInterval(this.#heartbeatTimer);
|
|
118
|
-
this.#heartbeatTimer = undefined;
|
|
119
|
-
}
|
|
121
|
+
this.close();
|
|
122
|
+
await this.#lifecycle.stop();
|
|
120
123
|
rejectAllPending(this.#pending);
|
|
121
|
-
if (this.#ws) {
|
|
122
|
-
try {
|
|
123
|
-
this.#ws.close();
|
|
124
|
-
} catch {
|
|
125
|
-
/* ignore */
|
|
126
|
-
}
|
|
127
|
-
this.#ws = undefined;
|
|
128
|
-
}
|
|
129
|
-
this.#started = false;
|
|
130
124
|
}
|
|
131
125
|
|
|
132
126
|
async send({ conversation, payload }: EndpointSendRequest): Promise<string> {
|
|
133
127
|
const message = formatOutboundSegments(payload);
|
|
134
128
|
const { action, params } = buildSendAction(conversation, message);
|
|
135
|
-
const data = await
|
|
129
|
+
const data = await callOnebot11Client<{ message_id?: number | string }>(this.client, action, params);
|
|
136
130
|
return data?.message_id != null ? String(data.message_id) : '';
|
|
137
131
|
}
|
|
138
132
|
|
|
139
133
|
async recallMessage(messageId: string): Promise<void> {
|
|
140
134
|
if (!messageId) return;
|
|
141
|
-
await this.
|
|
135
|
+
await callOnebot11Client(this.client, 'delete_msg', { message_id: Number(messageId) });
|
|
142
136
|
}
|
|
143
137
|
|
|
144
|
-
callApi(
|
|
138
|
+
#callApi(
|
|
139
|
+
action: string,
|
|
140
|
+
params: Record<string, unknown> = {},
|
|
141
|
+
): ReturnType<typeof callOneBot11WsAction> {
|
|
145
142
|
return callOneBot11WsAction(this.#ws, this.#pending, this.#requestId, action, params);
|
|
146
143
|
}
|
|
147
144
|
|
|
148
|
-
|
|
149
|
-
await this.callApi('set_group_special_title', {
|
|
150
|
-
group_id: groupId,
|
|
151
|
-
user_id: userId,
|
|
152
|
-
special_title: title,
|
|
153
|
-
duration,
|
|
154
|
-
});
|
|
155
|
-
return true;
|
|
156
|
-
}
|
|
157
|
-
|
|
158
|
-
admit(ev: OneBot11Event): void {
|
|
159
|
-
if (!this.#open) return;
|
|
145
|
+
#admitRaw(ev: OneBot11Event): void {
|
|
160
146
|
if (!isMessageEvent(ev)) {
|
|
161
147
|
receiveOneBot11SideEvent(
|
|
162
|
-
this
|
|
148
|
+
(name, payload) => this.emit(name, payload),
|
|
163
149
|
this.#options.config.id,
|
|
164
|
-
this,
|
|
150
|
+
{ callApi: (action, params) => callOnebot11Client(this.client, action, params) },
|
|
165
151
|
ev,
|
|
166
152
|
this.#logger,
|
|
167
153
|
);
|
|
@@ -169,7 +155,7 @@ export class OneBot11WssEndpoint implements EndpointInstance {
|
|
|
169
155
|
}
|
|
170
156
|
const conversation = onebot11InboundConversation(String(this.#options.id), ev);
|
|
171
157
|
const inbound = formatInboundMetadata(ev, this.#options.config.id);
|
|
172
|
-
void this
|
|
158
|
+
void this.emit('message.receive', {
|
|
173
159
|
conversation,
|
|
174
160
|
message: { conversation, id: String(ev.message_id) },
|
|
175
161
|
content: formatInboundContent(ev),
|
|
@@ -191,6 +177,13 @@ export class OneBot11WssEndpoint implements EndpointInstance {
|
|
|
191
177
|
});
|
|
192
178
|
}
|
|
193
179
|
|
|
180
|
+
#warnPlatformEvent(error: unknown): void {
|
|
181
|
+
this.#logger.warn(formatCompact({
|
|
182
|
+
op: 'onebot11_platform_event_failed',
|
|
183
|
+
error: error instanceof Error ? error.message : String(error),
|
|
184
|
+
}));
|
|
185
|
+
}
|
|
186
|
+
|
|
194
187
|
#acceptConnection(connection: WsConnection): void {
|
|
195
188
|
if (!verifyOneBotAccessToken(this.#options.config.access_token, connection.request)) {
|
|
196
189
|
connection.socket.close(4003, 'Unauthorized');
|
|
@@ -205,25 +198,25 @@ export class OneBot11WssEndpoint implements EndpointInstance {
|
|
|
205
198
|
}
|
|
206
199
|
}
|
|
207
200
|
this.#ws = socket;
|
|
208
|
-
this.#
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
201
|
+
this.#lifecycle.startHeartbeat(() => {
|
|
202
|
+
try {
|
|
203
|
+
this.#ws?.ping?.();
|
|
204
|
+
} catch {
|
|
205
|
+
/* ignore */
|
|
206
|
+
}
|
|
207
|
+
});
|
|
213
208
|
socket.on('message', (data) => {
|
|
209
|
+
this.#lifecycle.notifyHeartbeatAck();
|
|
214
210
|
handleOneBot11WsMessage(data, {
|
|
215
211
|
endpointId: this.#options.config.id,
|
|
216
212
|
pending: this.#pending,
|
|
217
|
-
|
|
213
|
+
ingest: (ev) => this.client.ingest(ev as Parameters<Onebot11Client['ingest']>[0]),
|
|
218
214
|
});
|
|
219
215
|
});
|
|
220
216
|
socket.on('close', () => {
|
|
221
217
|
if (this.#ws === socket) {
|
|
222
218
|
this.#ws = undefined;
|
|
223
|
-
|
|
224
|
-
clearInterval(this.#heartbeatTimer);
|
|
225
|
-
this.#heartbeatTimer = undefined;
|
|
226
|
-
}
|
|
219
|
+
this.#lifecycle.stopHeartbeat();
|
|
227
220
|
}
|
|
228
221
|
});
|
|
229
222
|
this.#logger.debug(formatCompact({
|
|
@@ -1,15 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Agent tool deps for onebot11 (set_title etc.).
|
|
3
|
-
* Endpoints register themselves on start; tools look up by config name / endpoint id.
|
|
4
|
-
*/
|
|
5
|
-
export interface Onebot11AgentEndpoint {
|
|
6
|
-
setTitle(groupId: number, userId: number, title: string, duration?: number): Promise<boolean>;
|
|
7
|
-
callApi(action: string, params?: Record<string, unknown>): Promise<unknown>;
|
|
8
|
-
}
|
|
9
|
-
export interface Onebot11AgentDeps {
|
|
10
|
-
getEndpoint: (endpointKey: string) => Onebot11AgentEndpoint;
|
|
11
|
-
}
|
|
12
|
-
export declare function registerOnebot11AgentEndpoint(endpointKey: string, endpoint: Onebot11AgentEndpoint): () => void;
|
|
13
|
-
/** Optional override used by tests / transitional callers. */
|
|
14
|
-
export declare function setOnebot11AgentDeps(deps: Onebot11AgentDeps): void;
|
|
15
|
-
export declare function getOnebot11AgentDeps(): Onebot11AgentDeps;
|
|
@@ -1,30 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Agent tool deps for onebot11 (set_title etc.).
|
|
3
|
-
* Endpoints register themselves on start; tools look up by config name / endpoint id.
|
|
4
|
-
*/
|
|
5
|
-
const endpoints = new Map();
|
|
6
|
-
let override = null;
|
|
7
|
-
export function registerOnebot11AgentEndpoint(endpointKey, endpoint) {
|
|
8
|
-
endpoints.set(endpointKey, endpoint);
|
|
9
|
-
return () => {
|
|
10
|
-
if (endpoints.get(endpointKey) === endpoint) {
|
|
11
|
-
endpoints.delete(endpointKey);
|
|
12
|
-
}
|
|
13
|
-
};
|
|
14
|
-
}
|
|
15
|
-
/** Optional override used by tests / transitional callers. */
|
|
16
|
-
export function setOnebot11AgentDeps(deps) {
|
|
17
|
-
override = deps;
|
|
18
|
-
}
|
|
19
|
-
export function getOnebot11AgentDeps() {
|
|
20
|
-
if (override)
|
|
21
|
-
return override;
|
|
22
|
-
return {
|
|
23
|
-
getEndpoint(endpointKey) {
|
|
24
|
-
const registered = endpoints.get(endpointKey);
|
|
25
|
-
if (!registered)
|
|
26
|
-
throw new Error(`Endpoint ${endpointKey} 不存在`);
|
|
27
|
-
return registered;
|
|
28
|
-
},
|
|
29
|
-
};
|
|
30
|
-
}
|
|
@@ -1,49 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Agent tool deps for onebot11 (set_title etc.).
|
|
3
|
-
* Endpoints register themselves on start; tools look up by config name / endpoint id.
|
|
4
|
-
*/
|
|
5
|
-
|
|
6
|
-
export interface Onebot11AgentEndpoint {
|
|
7
|
-
setTitle(
|
|
8
|
-
groupId: number,
|
|
9
|
-
userId: number,
|
|
10
|
-
title: string,
|
|
11
|
-
duration?: number,
|
|
12
|
-
): Promise<boolean>;
|
|
13
|
-
callApi(action: string, params?: Record<string, unknown>): Promise<unknown>;
|
|
14
|
-
}
|
|
15
|
-
|
|
16
|
-
export interface Onebot11AgentDeps {
|
|
17
|
-
getEndpoint: (endpointKey: string) => Onebot11AgentEndpoint;
|
|
18
|
-
}
|
|
19
|
-
|
|
20
|
-
const endpoints = new Map<string, Onebot11AgentEndpoint>();
|
|
21
|
-
let override: Onebot11AgentDeps | null = null;
|
|
22
|
-
|
|
23
|
-
export function registerOnebot11AgentEndpoint(
|
|
24
|
-
endpointKey: string,
|
|
25
|
-
endpoint: Onebot11AgentEndpoint,
|
|
26
|
-
): () => void {
|
|
27
|
-
endpoints.set(endpointKey, endpoint);
|
|
28
|
-
return () => {
|
|
29
|
-
if (endpoints.get(endpointKey) === endpoint) {
|
|
30
|
-
endpoints.delete(endpointKey);
|
|
31
|
-
}
|
|
32
|
-
};
|
|
33
|
-
}
|
|
34
|
-
|
|
35
|
-
/** Optional override used by tests / transitional callers. */
|
|
36
|
-
export function setOnebot11AgentDeps(deps: Onebot11AgentDeps): void {
|
|
37
|
-
override = deps;
|
|
38
|
-
}
|
|
39
|
-
|
|
40
|
-
export function getOnebot11AgentDeps(): Onebot11AgentDeps {
|
|
41
|
-
if (override) return override;
|
|
42
|
-
return {
|
|
43
|
-
getEndpoint(endpointKey: string): Onebot11AgentEndpoint {
|
|
44
|
-
const registered = endpoints.get(endpointKey);
|
|
45
|
-
if (!registered) throw new Error(`Endpoint ${endpointKey} 不存在`);
|
|
46
|
-
return registered;
|
|
47
|
-
},
|
|
48
|
-
};
|
|
49
|
-
}
|