@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.
- package/CHANGELOG.md +38 -0
- package/README.md +23 -4
- package/adapters/onebot11.js +1 -7
- package/adapters/onebot11.ts +1 -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 +6 -13
- package/lib/ws-endpoint.js +31 -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 +9 -11
- package/lib/wss-endpoint.js +64 -65
- 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 +41 -52
- package/src/ws-transport.ts +4 -5
- package/src/ws-types.ts +2 -1
- package/src/wss-endpoint.ts +77 -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,14 +1,19 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* OneBot11 reverse WSS endpoint — accepts inbound WebSocket from OneBot implementation.
|
|
3
3
|
*/
|
|
4
|
-
import {
|
|
5
|
-
|
|
6
|
-
|
|
4
|
+
import {
|
|
5
|
+
ClientEndpoint,
|
|
6
|
+
createEndpointLifecycle,
|
|
7
|
+
createRecallEndpointControl,
|
|
8
|
+
type EndpointLifecycle,
|
|
9
|
+
type EndpointControl,
|
|
10
|
+
type EndpointManagement,
|
|
11
|
+
type EndpointSendRequest,
|
|
12
|
+
} from 'zhin.js/adapter';
|
|
7
13
|
import type { HttpHost, WsConnection } from '@zhin.js/host-http';
|
|
8
14
|
import { formatCompact, getAdapterLogger } from '@zhin.js/logger';
|
|
9
15
|
import type { CapabilityId } from 'zhin.js';
|
|
10
16
|
import { createOneBot11EndpointManagement } from './endpoint-management.js';
|
|
11
|
-
import { registerOnebot11AgentEndpoint } from './onebot11-agent-deps.js';
|
|
12
17
|
import {
|
|
13
18
|
buildSendAction,
|
|
14
19
|
formatInboundContent,
|
|
@@ -28,45 +33,57 @@ import {
|
|
|
28
33
|
callOneBot11WsAction,
|
|
29
34
|
handleOneBot11WsMessage,
|
|
30
35
|
rejectAllPending,
|
|
31
|
-
startOneBot11Heartbeat,
|
|
32
36
|
} from './ws-transport.js';
|
|
33
37
|
import { OneBot11WsEndpoint } from './ws-endpoint.js';
|
|
34
38
|
import {
|
|
35
39
|
type OneBot11PendingAction,
|
|
36
40
|
type OneBot11WsSocket,
|
|
37
41
|
} from './ws-types.js';
|
|
42
|
+
import { callOnebot11Client, createOnebot11EndpointClient, forwardOnebot11ClientEvents, type Onebot11Client } from './client.js';
|
|
38
43
|
|
|
39
44
|
export interface OneBot11WssEndpointOptions {
|
|
40
45
|
readonly id: CapabilityId;
|
|
41
|
-
readonly gateway: MessageGateway;
|
|
42
|
-
readonly sideEvents?: SideEventGateway;
|
|
43
46
|
readonly http: HttpHost;
|
|
44
47
|
readonly config: OneBot11WssConfig;
|
|
45
48
|
}
|
|
46
49
|
|
|
47
|
-
export class OneBot11WssEndpoint
|
|
50
|
+
export class OneBot11WssEndpoint extends ClientEndpoint<Onebot11Client> {
|
|
51
|
+
readonly client: Onebot11Client;
|
|
48
52
|
readonly #logger!: ReturnType<typeof getAdapterLogger>;
|
|
49
53
|
|
|
50
54
|
readonly #options: OneBot11WssEndpointOptions;
|
|
51
|
-
readonly management: EndpointManagement
|
|
52
|
-
readonly
|
|
55
|
+
readonly management: EndpointManagement;
|
|
56
|
+
readonly control: EndpointControl = createRecallEndpointControl((id) => this.recallMessage(id));
|
|
57
|
+
readonly content;
|
|
53
58
|
#ws?: OneBot11WsSocket;
|
|
54
59
|
#wsRelease?: () => void;
|
|
55
|
-
#
|
|
60
|
+
readonly #lifecycle: EndpointLifecycle;
|
|
56
61
|
#requestId = { value: 0 };
|
|
57
62
|
#pending = new Map<string, OneBot11PendingAction>();
|
|
58
|
-
#open = false;
|
|
59
|
-
#started = false;
|
|
60
|
-
#unregisterAgent?: () => void;
|
|
61
63
|
|
|
62
64
|
constructor(options: OneBot11WssEndpointOptions) {
|
|
65
|
+
super();
|
|
63
66
|
this.#logger = getAdapterLogger('onebot11', options.config.id);
|
|
64
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
|
+
);
|
|
65
84
|
}
|
|
66
85
|
|
|
67
86
|
async start(): Promise<void> {
|
|
68
|
-
if (this.#started) return;
|
|
69
|
-
this.#started = true;
|
|
70
87
|
if (!this.#options.config.access_token) {
|
|
71
88
|
// wss 模式未配 access_token 时任何连接都会被放行(verifyOneBotAccessToken 直接 return true)
|
|
72
89
|
this.#logger.warn(formatCompact({
|
|
@@ -76,13 +93,21 @@ export class OneBot11WssEndpoint implements EndpointInstance {
|
|
|
76
93
|
error: 'missing access_token',
|
|
77
94
|
}));
|
|
78
95
|
}
|
|
79
|
-
this.#
|
|
80
|
-
this.#options.config.
|
|
81
|
-
this
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
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
|
+
});
|
|
86
111
|
});
|
|
87
112
|
this.#logger.info(formatCompact({
|
|
88
113
|
op: 'listen',
|
|
@@ -92,69 +117,37 @@ export class OneBot11WssEndpoint implements EndpointInstance {
|
|
|
92
117
|
}));
|
|
93
118
|
}
|
|
94
119
|
|
|
95
|
-
open(): void {
|
|
96
|
-
this.#open = true;
|
|
97
|
-
}
|
|
98
|
-
|
|
99
|
-
close(): void {
|
|
100
|
-
this.#open = false;
|
|
101
|
-
}
|
|
102
|
-
|
|
103
120
|
async stop(): Promise<void> {
|
|
104
|
-
this
|
|
105
|
-
this.#
|
|
106
|
-
this.#unregisterAgent = undefined;
|
|
107
|
-
this.#wsRelease?.();
|
|
108
|
-
this.#wsRelease = undefined;
|
|
109
|
-
if (this.#heartbeatTimer) {
|
|
110
|
-
clearInterval(this.#heartbeatTimer);
|
|
111
|
-
this.#heartbeatTimer = undefined;
|
|
112
|
-
}
|
|
121
|
+
this.close();
|
|
122
|
+
await this.#lifecycle.stop();
|
|
113
123
|
rejectAllPending(this.#pending);
|
|
114
|
-
if (this.#ws) {
|
|
115
|
-
try {
|
|
116
|
-
this.#ws.close();
|
|
117
|
-
} catch {
|
|
118
|
-
/* ignore */
|
|
119
|
-
}
|
|
120
|
-
this.#ws = undefined;
|
|
121
|
-
}
|
|
122
|
-
this.#started = false;
|
|
123
124
|
}
|
|
124
125
|
|
|
125
126
|
async send({ conversation, payload }: EndpointSendRequest): Promise<string> {
|
|
126
127
|
const message = formatOutboundSegments(payload);
|
|
127
128
|
const { action, params } = buildSendAction(conversation, message);
|
|
128
|
-
const data = await
|
|
129
|
+
const data = await callOnebot11Client<{ message_id?: number | string }>(this.client, action, params);
|
|
129
130
|
return data?.message_id != null ? String(data.message_id) : '';
|
|
130
131
|
}
|
|
131
132
|
|
|
132
133
|
async recallMessage(messageId: string): Promise<void> {
|
|
133
134
|
if (!messageId) return;
|
|
134
|
-
await this.
|
|
135
|
+
await callOnebot11Client(this.client, 'delete_msg', { message_id: Number(messageId) });
|
|
135
136
|
}
|
|
136
137
|
|
|
137
|
-
callApi(
|
|
138
|
+
#callApi(
|
|
139
|
+
action: string,
|
|
140
|
+
params: Record<string, unknown> = {},
|
|
141
|
+
): ReturnType<typeof callOneBot11WsAction> {
|
|
138
142
|
return callOneBot11WsAction(this.#ws, this.#pending, this.#requestId, action, params);
|
|
139
143
|
}
|
|
140
144
|
|
|
141
|
-
|
|
142
|
-
await this.callApi('set_group_special_title', {
|
|
143
|
-
group_id: groupId,
|
|
144
|
-
user_id: userId,
|
|
145
|
-
special_title: title,
|
|
146
|
-
duration,
|
|
147
|
-
});
|
|
148
|
-
return true;
|
|
149
|
-
}
|
|
150
|
-
|
|
151
|
-
admit(ev: OneBot11Event): void {
|
|
152
|
-
if (!this.#open) return;
|
|
145
|
+
#admitRaw(ev: OneBot11Event): void {
|
|
153
146
|
if (!isMessageEvent(ev)) {
|
|
154
147
|
receiveOneBot11SideEvent(
|
|
155
|
-
this
|
|
148
|
+
(name, payload) => this.emit(name, payload),
|
|
156
149
|
this.#options.config.id,
|
|
157
|
-
this,
|
|
150
|
+
{ callApi: (action, params) => callOnebot11Client(this.client, action, params) },
|
|
158
151
|
ev,
|
|
159
152
|
this.#logger,
|
|
160
153
|
);
|
|
@@ -162,7 +155,7 @@ export class OneBot11WssEndpoint implements EndpointInstance {
|
|
|
162
155
|
}
|
|
163
156
|
const conversation = onebot11InboundConversation(String(this.#options.id), ev);
|
|
164
157
|
const inbound = formatInboundMetadata(ev, this.#options.config.id);
|
|
165
|
-
void this
|
|
158
|
+
void this.emit('message.receive', {
|
|
166
159
|
conversation,
|
|
167
160
|
message: { conversation, id: String(ev.message_id) },
|
|
168
161
|
content: formatInboundContent(ev),
|
|
@@ -184,6 +177,13 @@ export class OneBot11WssEndpoint implements EndpointInstance {
|
|
|
184
177
|
});
|
|
185
178
|
}
|
|
186
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
|
+
|
|
187
187
|
#acceptConnection(connection: WsConnection): void {
|
|
188
188
|
if (!verifyOneBotAccessToken(this.#options.config.access_token, connection.request)) {
|
|
189
189
|
connection.socket.close(4003, 'Unauthorized');
|
|
@@ -198,25 +198,25 @@ export class OneBot11WssEndpoint implements EndpointInstance {
|
|
|
198
198
|
}
|
|
199
199
|
}
|
|
200
200
|
this.#ws = socket;
|
|
201
|
-
this.#
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
201
|
+
this.#lifecycle.startHeartbeat(() => {
|
|
202
|
+
try {
|
|
203
|
+
this.#ws?.ping?.();
|
|
204
|
+
} catch {
|
|
205
|
+
/* ignore */
|
|
206
|
+
}
|
|
207
|
+
});
|
|
206
208
|
socket.on('message', (data) => {
|
|
209
|
+
this.#lifecycle.notifyHeartbeatAck();
|
|
207
210
|
handleOneBot11WsMessage(data, {
|
|
208
211
|
endpointId: this.#options.config.id,
|
|
209
212
|
pending: this.#pending,
|
|
210
|
-
|
|
213
|
+
ingest: (ev) => this.client.ingest(ev as Parameters<Onebot11Client['ingest']>[0]),
|
|
211
214
|
});
|
|
212
215
|
});
|
|
213
216
|
socket.on('close', () => {
|
|
214
217
|
if (this.#ws === socket) {
|
|
215
218
|
this.#ws = undefined;
|
|
216
|
-
|
|
217
|
-
clearInterval(this.#heartbeatTimer);
|
|
218
|
-
this.#heartbeatTimer = undefined;
|
|
219
|
-
}
|
|
219
|
+
this.#lifecycle.stopHeartbeat();
|
|
220
220
|
}
|
|
221
221
|
});
|
|
222
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
|
-
}
|