@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/lib/wss-endpoint.js
CHANGED
|
@@ -1,38 +1,46 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* OneBot11 reverse WSS endpoint — accepts inbound WebSocket from OneBot implementation.
|
|
3
3
|
*/
|
|
4
|
-
import {
|
|
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,
|
|
14
|
-
|
|
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
|
|
17
|
+
management;
|
|
18
18
|
control = createRecallEndpointControl((id) => this.recallMessage(id));
|
|
19
|
-
content
|
|
19
|
+
content;
|
|
20
20
|
#ws;
|
|
21
21
|
#wsRelease;
|
|
22
|
-
#
|
|
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
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
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
|
|
65
|
-
this.#
|
|
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.
|
|
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.
|
|
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
|
-
|
|
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
|
|
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
|
|
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.#
|
|
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
|
-
|
|
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
|
-
|
|
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.
|
|
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.2.
|
|
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.2.
|
|
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,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
|
|
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
|
|
59
|
+
readonly management: EndpointManagement;
|
|
62
60
|
readonly control: EndpointControl = createRecallEndpointControl((id) => this.recallMessage(id));
|
|
63
|
-
readonly content
|
|
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
|
|
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
|
|
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
|
|
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.
|
|
138
|
+
await callOnebot11Client(this.client, 'delete_msg', { message_id: Number(messageId) });
|
|
145
139
|
}
|
|
146
140
|
|
|
147
|
-
|
|
148
|
-
|
|
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
|
-
|
|
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
|
|
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
|
|
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
|
-
|
|
232
|
+
ingest: (ev) => this.client.ingest(ev as Parameters<Onebot11Client['ingest']>[0]),
|
|
247
233
|
});
|
|
248
234
|
});
|
|
249
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';
|