@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/lib/wss-endpoint.js
CHANGED
|
@@ -1,36 +1,46 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* OneBot11 reverse WSS endpoint — accepts inbound WebSocket from OneBot implementation.
|
|
3
3
|
*/
|
|
4
|
-
import {
|
|
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,
|
|
13
|
-
|
|
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
|
|
17
|
-
|
|
17
|
+
management;
|
|
18
|
+
control = createRecallEndpointControl((id) => this.recallMessage(id));
|
|
19
|
+
content;
|
|
18
20
|
#ws;
|
|
19
21
|
#wsRelease;
|
|
20
|
-
#
|
|
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
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
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
|
|
63
|
-
this.#
|
|
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.
|
|
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.
|
|
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
|
-
|
|
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
|
|
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
|
|
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.#
|
|
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
|
-
|
|
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
|
-
|
|
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": "
|
|
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
|
|
45
|
-
"@zhin.js/core": "1.5.
|
|
46
|
-
"@zhin.js/
|
|
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.
|
|
49
|
-
"@zhin.js/permission": "1.0.
|
|
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.
|
|
58
|
-
"@zhin.js/host-http": "1.0.
|
|
59
|
-
"zhin.js": "6.0.
|
|
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
|
|
64
|
-
"@zhin.js/agent": "1.1.
|
|
65
|
-
"@zhin.js/command": "1.0.
|
|
66
|
-
"@zhin.js/core": "1.5.
|
|
67
|
-
"zhin.js": "6.0.
|
|
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
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
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:
|
|
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
|
|
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 {
|
|
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
|
-
|
|
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 (!
|
|
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(
|
|
23
|
+
void receiveOneBotLikeSideEvent(emit, {
|
|
24
24
|
adapter: 'onebot11',
|
|
25
25
|
endpointKey,
|
|
26
26
|
platform: 'onebot',
|
package/src/ws-endpoint.ts
CHANGED
|
@@ -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
|
|
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
|
|
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
|
|
60
|
-
readonly
|
|
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
|
|
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
|
|
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
|
|
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.
|
|
138
|
+
await callOnebot11Client(this.client, 'delete_msg', { message_id: Number(messageId) });
|
|
142
139
|
}
|
|
143
140
|
|
|
144
|
-
|
|
145
|
-
|
|
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
|
-
|
|
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
|
|
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
|
|
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
|
-
|
|
232
|
+
ingest: (ev) => this.client.ingest(ev as Parameters<Onebot11Client['ingest']>[0]),
|
|
244
233
|
});
|
|
245
234
|
});
|
|
246
235
|
|
package/src/ws-transport.ts
CHANGED
|
@@ -25,7 +25,7 @@ export function handleOneBot11WsMessage(
|
|
|
25
25
|
options: {
|
|
26
26
|
readonly endpointId: string;
|
|
27
27
|
readonly pending: Map<string, OneBot11PendingAction>;
|
|
28
|
-
readonly
|
|
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
|
-
|
|
41
|
-
else pending.reject(new Error(`OneBot11 retcode=${resp.retcode}`));
|
|
40
|
+
pending.resolve(resp);
|
|
42
41
|
}
|
|
43
42
|
return;
|
|
44
43
|
}
|
|
45
|
-
options.
|
|
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<
|
|
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:
|
|
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';
|