@zhin.js/adapter-onebot11 1.1.17 → 1.1.19
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 +66 -0
- package/README.md +22 -5
- package/adapters/onebot11/index.js +3 -26
- package/adapters/onebot11/index.ts +2 -32
- package/commands/onebot11/endpoint/definition.js +0 -2
- package/commands/onebot11/endpoint/definition.ts +0 -2
- package/lib/adapter.d.ts +20 -0
- package/lib/adapter.js +26 -0
- package/lib/client.d.ts +13 -11
- package/lib/client.js +35 -12
- package/lib/console-spec.d.ts +12 -0
- package/lib/console-spec.js +12 -0
- package/lib/console.d.ts +8 -0
- package/lib/console.js +13 -0
- package/lib/content-port.d.ts +2 -2
- package/lib/content-port.js +2 -2
- package/lib/endpoint-management.d.ts +2 -2
- package/lib/endpoint-management.js +8 -8
- package/lib/identity.d.ts +3 -0
- package/lib/identity.js +32 -0
- package/lib/index.d.ts +3 -2
- package/lib/index.js +3 -2
- package/lib/self-id.js +1 -1
- package/lib/side-event-dispatch.d.ts +8 -1
- package/lib/side-event-dispatch.js +2 -1
- package/lib/ws-endpoint.d.ts +5 -12
- package/lib/ws-endpoint.js +75 -50
- package/lib/ws-transport.d.ts +18 -11
- package/lib/ws-transport.js +28 -56
- package/lib/ws-types.d.ts +0 -6
- package/lib/wss-endpoint.d.ts +5 -12
- package/lib/wss-endpoint.js +76 -53
- package/package.json +25 -17
- package/pages/index/index.js +15 -0
- package/pages/index/index.ts +16 -0
- package/plugin.js +3 -4
- package/src/adapter.ts +32 -0
- package/src/client.ts +38 -28
- package/src/console-spec.ts +17 -0
- package/src/console.ts +18 -0
- package/src/content-port.ts +4 -4
- package/src/endpoint-management.ts +15 -12
- package/src/identity.ts +30 -0
- package/src/index.ts +4 -4
- package/src/self-id.ts +1 -1
- package/src/side-event-dispatch.ts +9 -7
- package/src/ws-endpoint.ts +71 -64
- package/src/ws-transport.ts +41 -82
- package/src/ws-types.ts +0 -7
- package/src/wss-endpoint.ts +73 -68
- package/lib/onebot11-runtime-state.d.ts +0 -1
- package/lib/onebot11-runtime-state.js +0 -6
- package/src/onebot11-runtime-state.ts +0 -7
package/lib/wss-endpoint.js
CHANGED
|
@@ -2,33 +2,38 @@ import { EndpointDeliveryError } from '@zhin.js/im-contract';
|
|
|
2
2
|
/**
|
|
3
3
|
* OneBot11 reverse WSS endpoint — accepts inbound WebSocket from OneBot implementation.
|
|
4
4
|
*/
|
|
5
|
-
import {
|
|
5
|
+
import { createEndpointSpecification, endpointSpec, createEndpointLifecycle, createRecallEndpointControl, } from 'zhin.js/adapter';
|
|
6
6
|
import { formatCompact, getAdapterLogger } from '@zhin.js/logger';
|
|
7
7
|
import { createOneBot11EndpointManagement } from './endpoint-management.js';
|
|
8
8
|
import { buildSendAction, formatInboundContent, formatInboundMetadata, formatOutboundSegments, isMessageEvent, onebot11InboundConversation, senderDisplayName, senderUserId, } from './protocol.js';
|
|
9
9
|
import { receiveOneBot11SideEvent } from './side-event-dispatch.js';
|
|
10
10
|
import { createOneBot11ContentPort } from './content-port.js';
|
|
11
11
|
import { verifyOneBotAccessToken } from './wss-auth.js';
|
|
12
|
-
import {
|
|
12
|
+
import { createOneBot11Rpc } from './ws-transport.js';
|
|
13
13
|
import { probeOnebot11SelfId, resolveOnebot11SelfId } from './self-id.js';
|
|
14
|
-
import { callOnebot11Client,
|
|
15
|
-
export class OneBot11WssEndpoint extends
|
|
16
|
-
|
|
14
|
+
import { callOnebot11Client, readOnebot11Response, onebot11ClientOptions, forwardOnebot11ClientEvents, Onebot11Client } from './client.js';
|
|
15
|
+
export class OneBot11WssEndpoint extends Onebot11Client {
|
|
16
|
+
[endpointSpec];
|
|
17
|
+
#open = false;
|
|
17
18
|
#logger;
|
|
18
19
|
#options;
|
|
19
|
-
management;
|
|
20
|
-
control = createRecallEndpointControl((id) => this
|
|
21
|
-
content;
|
|
20
|
+
#management;
|
|
21
|
+
#control = createRecallEndpointControl((id, signal) => this.#recallMessage(id, signal));
|
|
22
|
+
#content;
|
|
23
|
+
#releaseClientEvents;
|
|
22
24
|
#ws;
|
|
23
25
|
#wsRelease;
|
|
24
26
|
#connectionLifecycle;
|
|
25
27
|
#connectionTask = Promise.resolve();
|
|
26
|
-
#
|
|
27
|
-
#pending = new Map();
|
|
28
|
+
#rpc;
|
|
28
29
|
#started = false;
|
|
29
30
|
#stopped = false;
|
|
30
31
|
constructor(options) {
|
|
31
|
-
|
|
32
|
+
const rpc = createOneBot11Rpc({ endpointId: options.config.id, signal: options.signal,
|
|
33
|
+
socket: () => this.#connectionLifecycle.state === 'closed' ? undefined : this.#ws,
|
|
34
|
+
});
|
|
35
|
+
super(onebot11ClientOptions(options.config, (action, params) => rpc.call({ action, params })));
|
|
36
|
+
this.#rpc = rpc;
|
|
32
37
|
this.#logger = getAdapterLogger('onebot11', options.config.id);
|
|
33
38
|
this.#options = options;
|
|
34
39
|
this.#connectionLifecycle = createEndpointLifecycle({
|
|
@@ -36,16 +41,29 @@ export class OneBot11WssEndpoint extends ClientEndpoint {
|
|
|
36
41
|
reconnect: false,
|
|
37
42
|
heartbeat: { intervalMs: options.config.heartbeat_interval, watchdogMisses: 2 },
|
|
38
43
|
});
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
this
|
|
42
|
-
|
|
43
|
-
this
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
44
|
+
const callApi = (request) => this.#rpc.call(request).then(readOnebot11Response);
|
|
45
|
+
this.#management = createOneBot11EndpointManagement({ callApi });
|
|
46
|
+
this.#content = createOneBot11ContentPort(callApi);
|
|
47
|
+
const readTransportState = () => this.#transportState;
|
|
48
|
+
this[endpointSpec] = createEndpointSpecification(this, {
|
|
49
|
+
name: options.config.id,
|
|
50
|
+
start: () => this.#start(), open: () => { this.#open = true; },
|
|
51
|
+
close: () => { this.#open = false; }, stop: () => this.#stop(),
|
|
52
|
+
send: request => this.#send(request),
|
|
53
|
+
get transportState() { return readTransportState(); },
|
|
54
|
+
management: this.#management, control: this.#control, content: this.#content,
|
|
55
|
+
});
|
|
56
|
+
this.#releaseClientEvents = forwardOnebot11ClientEvents(this, (name, payload) => {
|
|
57
|
+
void this[endpointSpec].events.run(() => {
|
|
58
|
+
if (this.#stopped || (!this.#open && !this.#started))
|
|
59
|
+
return;
|
|
60
|
+
void this[endpointSpec].events.platform(name, payload).catch(error => this.#warnPlatformEvent(error));
|
|
61
|
+
if (name === 'event')
|
|
62
|
+
this.#admitRaw(payload);
|
|
63
|
+
}).catch(error => this.#warnPlatformEvent(error));
|
|
64
|
+
});
|
|
47
65
|
}
|
|
48
|
-
get transportState() {
|
|
66
|
+
get #transportState() {
|
|
49
67
|
if (this.#stopped)
|
|
50
68
|
return 'stopped';
|
|
51
69
|
if (!this.#started)
|
|
@@ -56,7 +74,7 @@ export class OneBot11WssEndpoint extends ClientEndpoint {
|
|
|
56
74
|
return 'open';
|
|
57
75
|
return this.#connectionLifecycle.state === 'connecting' ? 'connecting' : 'idle';
|
|
58
76
|
}
|
|
59
|
-
async start() {
|
|
77
|
+
async #start() {
|
|
60
78
|
if (this.#started)
|
|
61
79
|
return;
|
|
62
80
|
this.#stopped = false;
|
|
@@ -89,47 +107,56 @@ export class OneBot11WssEndpoint extends ClientEndpoint {
|
|
|
89
107
|
path: this.#options.config.path,
|
|
90
108
|
}));
|
|
91
109
|
}
|
|
92
|
-
async stop() {
|
|
110
|
+
async #stop() {
|
|
93
111
|
this.#stopped = true;
|
|
94
|
-
this
|
|
112
|
+
this.#open = false;
|
|
95
113
|
this.#wsRelease?.();
|
|
96
114
|
this.#wsRelease = undefined;
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
115
|
+
try {
|
|
116
|
+
await this.#connectionTask;
|
|
117
|
+
await this.#connectionLifecycle.stop();
|
|
118
|
+
}
|
|
119
|
+
finally {
|
|
120
|
+
this.#rpc.dispose();
|
|
121
|
+
this.#releaseClientEvents();
|
|
122
|
+
this.#ws = undefined;
|
|
123
|
+
this.#started = false;
|
|
124
|
+
this[endpointSpec].reportSelfUserId(undefined);
|
|
125
|
+
}
|
|
103
126
|
}
|
|
104
|
-
async send({ conversation, payload }) {
|
|
127
|
+
async #send({ conversation, payload, signal }) {
|
|
105
128
|
const message = formatOutboundSegments(payload);
|
|
106
129
|
const { action, params } = buildSendAction(conversation, message);
|
|
107
|
-
const data = await
|
|
130
|
+
const data = readOnebot11Response(await this.#rpc.call({ action, params, signal }));
|
|
108
131
|
const rawId = data?.message_id;
|
|
109
132
|
if ((typeof rawId !== 'number' && typeof rawId !== 'string') || String(rawId).trim() === '' || (typeof rawId === 'number' && !Number.isFinite(rawId))) {
|
|
110
133
|
throw new EndpointDeliveryError('delivery_unconfirmed', 'Message delivery is unconfirmed: platform returned no message_id', 'unknown');
|
|
111
134
|
}
|
|
112
135
|
return String(rawId);
|
|
113
136
|
}
|
|
114
|
-
async recallMessage(messageId) {
|
|
137
|
+
async #recallMessage(messageId, signal) {
|
|
115
138
|
if (!messageId)
|
|
116
139
|
return;
|
|
117
|
-
await
|
|
118
|
-
}
|
|
119
|
-
#callApi(action, params = {}) {
|
|
120
|
-
return callOneBot11WsAction(this.#connectionLifecycle.state === 'closed' ? undefined : this.#ws, this.#pending, this.#requestId, action, params);
|
|
140
|
+
await this.#rpc.call({ action: 'delete_msg', params: { message_id: Number(messageId) }, signal }).then(readOnebot11Response);
|
|
121
141
|
}
|
|
122
142
|
#admitRaw(ev) {
|
|
123
143
|
const selfId = resolveOnebot11SelfId(ev.self_id);
|
|
124
|
-
if (!this.#stopped && selfId
|
|
125
|
-
this.reportSelfUserId(selfId);
|
|
144
|
+
if (!this.#stopped && selfId)
|
|
145
|
+
this[endpointSpec].reportSelfUserId(selfId);
|
|
126
146
|
if (!isMessageEvent(ev)) {
|
|
127
|
-
receiveOneBot11SideEvent(
|
|
147
|
+
receiveOneBot11SideEvent(ev, {
|
|
148
|
+
emit: (name, payload) => this[endpointSpec].events.emit(name, payload),
|
|
149
|
+
endpointKey: this.#options.config.id,
|
|
150
|
+
caller: { callApi: (action, params) => callOnebot11Client({ client: this, action, params }) },
|
|
151
|
+
logger: this.#logger,
|
|
152
|
+
});
|
|
128
153
|
return;
|
|
129
154
|
}
|
|
130
|
-
const conversation = onebot11InboundConversation(String(this.#options.id), ev);
|
|
155
|
+
const conversation = { ...onebot11InboundConversation(String(this.#options.id), ev), endpoint: this[endpointSpec].identity };
|
|
131
156
|
const inbound = formatInboundMetadata(ev, this.#options.config.id);
|
|
132
|
-
void this.
|
|
157
|
+
void this[endpointSpec].events.message({
|
|
158
|
+
event: ev,
|
|
159
|
+
...(typeof ev.time === 'number' ? { platformSentAt: ev.time * 1000 } : {}),
|
|
133
160
|
conversation,
|
|
134
161
|
message: { conversation, id: String(ev.message_id) },
|
|
135
162
|
content: formatInboundContent(ev),
|
|
@@ -152,16 +179,16 @@ export class OneBot11WssEndpoint extends ClientEndpoint {
|
|
|
152
179
|
}
|
|
153
180
|
/** Best-effort 登录账号探测;失败仅 debug,不影响连接。 */
|
|
154
181
|
async #probeSelfUserId() {
|
|
155
|
-
const selfId = await probeOnebot11SelfId(this
|
|
182
|
+
const selfId = await probeOnebot11SelfId(this, (error) => {
|
|
156
183
|
this.#logger.debug(formatCompact({
|
|
157
184
|
op: 'onebot11_get_login_info_failed',
|
|
158
185
|
error: error instanceof Error ? error.message : String(error),
|
|
159
186
|
}));
|
|
160
187
|
});
|
|
161
|
-
if (this.#stopped || this
|
|
188
|
+
if (this.#stopped || this.#transportState !== 'open')
|
|
162
189
|
return;
|
|
163
|
-
if (selfId
|
|
164
|
-
this.reportSelfUserId(selfId);
|
|
190
|
+
if (selfId)
|
|
191
|
+
this[endpointSpec].reportSelfUserId(selfId);
|
|
165
192
|
}
|
|
166
193
|
#warnPlatformEvent(error) {
|
|
167
194
|
this.#logger.warn(formatCompact({
|
|
@@ -180,12 +207,12 @@ export class OneBot11WssEndpoint extends ClientEndpoint {
|
|
|
180
207
|
}
|
|
181
208
|
const socket = connection.socket;
|
|
182
209
|
await this.#connectionLifecycle.stop();
|
|
183
|
-
|
|
210
|
+
this.#rpc.disconnect('连接已替换');
|
|
184
211
|
await this.#connectionLifecycle.start(async (lifecycleHandle) => {
|
|
185
212
|
this.#ws = socket;
|
|
186
213
|
lifecycleHandle.onForceClose(() => {
|
|
187
214
|
if (this.#ws === socket) {
|
|
188
|
-
|
|
215
|
+
this.#rpc.disconnect();
|
|
189
216
|
}
|
|
190
217
|
try {
|
|
191
218
|
socket.close();
|
|
@@ -212,11 +239,7 @@ export class OneBot11WssEndpoint extends ClientEndpoint {
|
|
|
212
239
|
if (this.#ws !== socket || this.#connectionLifecycle.state === 'closed')
|
|
213
240
|
return;
|
|
214
241
|
this.#connectionLifecycle.notifyHeartbeatAck();
|
|
215
|
-
|
|
216
|
-
endpointId: this.#options.config.id,
|
|
217
|
-
pending: this.#pending,
|
|
218
|
-
ingest: (ev) => this.client.ingest(ev),
|
|
219
|
-
});
|
|
242
|
+
this.#rpc.receive(data, (ev) => this.ingest(ev));
|
|
220
243
|
});
|
|
221
244
|
socket.on('error', (error) => {
|
|
222
245
|
if (this.#ws !== socket)
|
|
@@ -228,7 +251,7 @@ export class OneBot11WssEndpoint extends ClientEndpoint {
|
|
|
228
251
|
return;
|
|
229
252
|
if (this.#ws === socket) {
|
|
230
253
|
this.#ws = undefined;
|
|
231
|
-
|
|
254
|
+
this.#rpc.disconnect();
|
|
232
255
|
}
|
|
233
256
|
lifecycleHandle.notifyClosed(new Error('OneBot11 reverse WebSocket closed'));
|
|
234
257
|
});
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@zhin.js/adapter-onebot11",
|
|
3
|
-
"version": "1.1.
|
|
3
|
+
"version": "1.1.19",
|
|
4
4
|
"description": "Zhin.js OneBot 11 adapter for Plugin Runtime (WebSocket client)",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "./lib/index.js",
|
|
@@ -14,6 +14,7 @@
|
|
|
14
14
|
"files": [
|
|
15
15
|
"adapters",
|
|
16
16
|
"commands",
|
|
17
|
+
"pages",
|
|
17
18
|
"plugin.js",
|
|
18
19
|
"schema.json",
|
|
19
20
|
"src",
|
|
@@ -44,16 +45,19 @@
|
|
|
44
45
|
"@imhelper/onebot-v11": "1.0.7",
|
|
45
46
|
"imhelper": "1.0.7",
|
|
46
47
|
"ws": "^8.21.1",
|
|
47
|
-
"@zhin.js/adapter": "1.1.
|
|
48
|
-
"@zhin.js/agent-feature": "1.1.
|
|
49
|
-
"@zhin.js/
|
|
50
|
-
"@zhin.js/
|
|
51
|
-
"@zhin.js/
|
|
52
|
-
"@zhin.js/
|
|
48
|
+
"@zhin.js/adapter": "1.1.26",
|
|
49
|
+
"@zhin.js/agent-feature": "1.1.11",
|
|
50
|
+
"@zhin.js/client": "1.1.22",
|
|
51
|
+
"@zhin.js/console-contract": "1.1.3",
|
|
52
|
+
"@zhin.js/core": "1.1.54",
|
|
53
|
+
"@zhin.js/feature-kit": "1.1.10",
|
|
54
|
+
"@zhin.js/host-http": "1.1.12",
|
|
55
|
+
"@zhin.js/im-contract": "1.1.9",
|
|
53
56
|
"@zhin.js/logger": "1.1.2",
|
|
54
|
-
"@zhin.js/
|
|
55
|
-
"@zhin.js/
|
|
56
|
-
"@zhin.js/
|
|
57
|
+
"@zhin.js/page": "1.1.11",
|
|
58
|
+
"@zhin.js/permission": "1.1.10",
|
|
59
|
+
"@zhin.js/skill": "1.1.11",
|
|
60
|
+
"@zhin.js/tool": "1.1.13"
|
|
57
61
|
},
|
|
58
62
|
"devDependencies": {
|
|
59
63
|
"@types/node": "^26.1.2",
|
|
@@ -61,15 +65,15 @@
|
|
|
61
65
|
"typescript": "^6.0.3",
|
|
62
66
|
"vitest": "^4.1.10",
|
|
63
67
|
"zod": "^4.4.3",
|
|
64
|
-
"@zhin.js/host-http": "1.1.
|
|
65
|
-
"zhin.js": "1.1.
|
|
68
|
+
"@zhin.js/host-http": "1.1.12",
|
|
69
|
+
"zhin.js": "1.1.19"
|
|
66
70
|
},
|
|
67
71
|
"peerDependencies": {
|
|
68
72
|
"zod": "^4.0.0",
|
|
69
|
-
"@zhin.js/adapter": "^1.1.
|
|
70
|
-
"@zhin.js/command": "^1.1.
|
|
71
|
-
"@zhin.js/core": "^1.1.
|
|
72
|
-
"zhin.js": "^1.1.
|
|
73
|
+
"@zhin.js/adapter": "^1.1.26",
|
|
74
|
+
"@zhin.js/command": "^1.1.12",
|
|
75
|
+
"@zhin.js/core": "^1.1.54",
|
|
76
|
+
"zhin.js": "^1.1.19"
|
|
73
77
|
},
|
|
74
78
|
"peerDependenciesMeta": {
|
|
75
79
|
"@zhin.js/command": {
|
|
@@ -103,7 +107,7 @@
|
|
|
103
107
|
"features": [
|
|
104
108
|
{
|
|
105
109
|
"package": "@zhin.js/adapter",
|
|
106
|
-
"api": "^
|
|
110
|
+
"api": "^2.0.0"
|
|
107
111
|
},
|
|
108
112
|
{
|
|
109
113
|
"package": "@zhin.js/command",
|
|
@@ -120,6 +124,10 @@
|
|
|
120
124
|
{
|
|
121
125
|
"package": "@zhin.js/agent-feature",
|
|
122
126
|
"api": "^1.0.0"
|
|
127
|
+
},
|
|
128
|
+
{
|
|
129
|
+
"package": "@zhin.js/page",
|
|
130
|
+
"api": "^1.0.0"
|
|
123
131
|
}
|
|
124
132
|
],
|
|
125
133
|
"plugins": []
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
// Generated by build-plugin-runtime-entries.mjs. Do not edit.
|
|
2
|
+
import { definePage } from '@zhin.js/console-contract';
|
|
3
|
+
import { createAdapterConsolePage } from '@zhin.js/client';
|
|
4
|
+
export const meta = definePage({
|
|
5
|
+
title: 'OneBot 11',
|
|
6
|
+
titleI18n: { en: 'OneBot 11' },
|
|
7
|
+
icon: 'Bot',
|
|
8
|
+
order: 70,
|
|
9
|
+
requiredPermissions: ['onebot11:read'],
|
|
10
|
+
});
|
|
11
|
+
/**
|
|
12
|
+
* Convention page: `pages/index/index.ts` → `/onebot11`(插件路径)。
|
|
13
|
+
* 连接状态、连接测试、endpoint 增删;登录待办来自 LoginAssist(ICQQ 等)。
|
|
14
|
+
*/
|
|
15
|
+
export default createAdapterConsolePage('onebot11');
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
import { definePage } from '@zhin.js/console-contract';
|
|
2
|
+
import { createAdapterConsolePage } from '@zhin.js/client';
|
|
3
|
+
|
|
4
|
+
export const meta = definePage({
|
|
5
|
+
title: 'OneBot 11',
|
|
6
|
+
titleI18n: { en: 'OneBot 11' },
|
|
7
|
+
icon: 'Bot',
|
|
8
|
+
order: 70,
|
|
9
|
+
requiredPermissions: ['onebot11:read'],
|
|
10
|
+
});
|
|
11
|
+
|
|
12
|
+
/**
|
|
13
|
+
* Convention page: `pages/index/index.ts` → `/onebot11`(插件路径)。
|
|
14
|
+
* 连接状态、连接测试、endpoint 增删;登录待办来自 LoginAssist(ICQQ 等)。
|
|
15
|
+
*/
|
|
16
|
+
export default createAdapterConsolePage('onebot11');
|
package/plugin.js
CHANGED
|
@@ -1,15 +1,14 @@
|
|
|
1
1
|
// Generated by build-plugin-runtime-entries.mjs. Do not edit.
|
|
2
|
-
import {
|
|
3
|
-
import { definePlugin } from 'zhin.js';
|
|
2
|
+
import { definePlugin, pluginConsoleRpcToken } from 'zhin.js';
|
|
4
3
|
import { permissionHostToken, createSceneRolePlatformChecker } from '@zhin.js/permission';
|
|
5
|
-
import {
|
|
4
|
+
import { createOnebot11ConsoleRpc } from "./lib/console.js";
|
|
6
5
|
export default definePlugin({
|
|
7
6
|
name: 'onebot11',
|
|
8
7
|
metadata: {
|
|
9
8
|
displayName: 'OneBot 11 Adapter',
|
|
10
9
|
},
|
|
11
10
|
setup(context) {
|
|
12
|
-
context.resources.provide(
|
|
11
|
+
context.resources.provide(pluginConsoleRpcToken, createOnebot11ConsoleRpc(context.resources, () => context.config.get()));
|
|
13
12
|
if (context.resources.has(permissionHostToken)) {
|
|
14
13
|
const host = context.resources.use(permissionHostToken);
|
|
15
14
|
return host.registerPlatform('onebot11', createSceneRolePlatformChecker());
|
package/src/adapter.ts
ADDED
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
import { Adapter, type AdapterContext, type EndpointCreationContext } from 'zhin.js/adapter';
|
|
2
|
+
import { httpHostToken } from '@zhin.js/host-http';
|
|
3
|
+
import { resolveOneBot11Config, type OneBot11EndpointConfig } from './protocol.js';
|
|
4
|
+
import { OneBot11WsEndpoint } from './ws-endpoint.js';
|
|
5
|
+
import { OneBot11WssEndpoint } from './wss-endpoint.js';
|
|
6
|
+
import { probeOnebot11Identity } from './identity.js';
|
|
7
|
+
import type { Onebot11ClientEventMap } from './client.js';
|
|
8
|
+
|
|
9
|
+
export class Onebot11Adapter extends Adapter<OneBot11WsEndpoint | OneBot11WssEndpoint, OneBot11EndpointConfig, Onebot11ClientEventMap> {
|
|
10
|
+
readonly #context: AdapterContext<OneBot11EndpointConfig>;
|
|
11
|
+
constructor(context: AdapterContext<OneBot11EndpointConfig>) { super(); this.#context = context; }
|
|
12
|
+
override get configurationIdentityFields(): readonly string[] { return ['id']; }
|
|
13
|
+
override get configurationIdentityProbeFields(): readonly string[] { return ['connection', 'access_token', 'url']; }
|
|
14
|
+
override get configurationUpdateFields(): readonly string[] {
|
|
15
|
+
return ['connection', 'url', 'path', 'access_token', 'reconnect_interval', 'heartbeat_interval'];
|
|
16
|
+
}
|
|
17
|
+
protected override validateConfiguration(config: OneBot11EndpointConfig): void { resolveOneBot11Config(config); }
|
|
18
|
+
protected override async identifyConfiguration(input: OneBot11EndpointConfig, signal: AbortSignal): Promise<string | undefined> {
|
|
19
|
+
const config = resolveOneBot11Config(input);
|
|
20
|
+
return config.connection === 'ws' ? probeOnebot11Identity(config, signal) : undefined;
|
|
21
|
+
}
|
|
22
|
+
protected createEndpoint(input: OneBot11EndpointConfig, execution: EndpointCreationContext): OneBot11WsEndpoint | OneBot11WssEndpoint {
|
|
23
|
+
const config = resolveOneBot11Config(input);
|
|
24
|
+
return config.connection === 'wss'
|
|
25
|
+
? new OneBot11WssEndpoint({ id: execution.canonicalId, signal: execution.signal, http: this.#context.use(httpHostToken), config, probeLoginInfo: true })
|
|
26
|
+
: new OneBot11WsEndpoint({ id: execution.canonicalId, signal: execution.signal, config, probeLoginInfo: true });
|
|
27
|
+
}
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
declare module '@zhin.js/feature-kit' {
|
|
31
|
+
interface AdapterRegistry { readonly onebot11: Onebot11Adapter; }
|
|
32
|
+
}
|
package/src/client.ts
CHANGED
|
@@ -1,11 +1,11 @@
|
|
|
1
|
+
import { EndpointDeliveryError } from '@zhin.js/im-contract';
|
|
1
2
|
import {
|
|
2
3
|
OneBotV11Client,
|
|
3
4
|
type OneBotV11Event as ImHelperOneBot11Event,
|
|
4
5
|
type OneBotV11Response,
|
|
5
6
|
} from '@imhelper/onebot-v11';
|
|
6
|
-
import { EventFactory, type EventMap, type ImHelperEventMap } from 'imhelper';
|
|
7
|
+
import { ProtocolError, EventFactory, type EventMap, type ImHelperEventMap } from 'imhelper';
|
|
7
8
|
import {
|
|
8
|
-
defineEndpointClient,
|
|
9
9
|
forwardEndpointClientEvents,
|
|
10
10
|
type ClientEventPayloads,
|
|
11
11
|
} from 'zhin.js/adapter';
|
|
@@ -25,11 +25,11 @@ type Onebot11ClientEvents = ImHelperEventMap<
|
|
|
25
25
|
>;
|
|
26
26
|
export type Onebot11ClientEventMap = ClientEventPayloads<Onebot11ClientEvents>;
|
|
27
27
|
|
|
28
|
-
export function
|
|
28
|
+
export function onebot11ClientOptions(
|
|
29
29
|
config: ResolvedOneBot11Config,
|
|
30
30
|
request: Onebot11ApiCall,
|
|
31
|
-
): OneBotV11Client {
|
|
32
|
-
return
|
|
31
|
+
): ConstructorParameters<typeof OneBotV11Client>[0] {
|
|
32
|
+
return {
|
|
33
33
|
baseUrl: config.connection === 'ws'
|
|
34
34
|
? config.url.replace(/^ws(s?):/, 'http$1:')
|
|
35
35
|
: 'http://localhost',
|
|
@@ -37,21 +37,42 @@ export function createOnebot11EndpointClient(
|
|
|
37
37
|
accessToken: config.access_token,
|
|
38
38
|
receiveMode: 'manual',
|
|
39
39
|
call: request,
|
|
40
|
-
}
|
|
40
|
+
};
|
|
41
41
|
}
|
|
42
42
|
|
|
43
|
-
export
|
|
44
|
-
client: OneBotV11Client
|
|
45
|
-
action: string
|
|
46
|
-
params?: Record<string, unknown
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
43
|
+
export interface Onebot11ClientRequest {
|
|
44
|
+
readonly client: OneBotV11Client;
|
|
45
|
+
readonly action: string;
|
|
46
|
+
readonly params?: Record<string, unknown>;
|
|
47
|
+
}
|
|
48
|
+
/** Native methods retain ProtocolError; framework ports preserve the underlying delivery evidence. */
|
|
49
|
+
export async function callOnebot11Client<T = unknown>(request: Onebot11ClientRequest): Promise<T | undefined> {
|
|
50
|
+
try {
|
|
51
|
+
return readOnebot11Response<T>(await request.client.call<T>(request.action, request.params));
|
|
52
|
+
} catch (error) {
|
|
53
|
+
if (error instanceof ProtocolError) {
|
|
54
|
+
if (error.cause instanceof EndpointDeliveryError) throw error.cause;
|
|
55
|
+
if (error.response !== undefined) readOnebot11Response(error.response);
|
|
56
|
+
}
|
|
57
|
+
throw error;
|
|
53
58
|
}
|
|
54
|
-
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
/**
|
|
62
|
+
* Framework evidence follows the OB11 response envelope, including async/1 submission.
|
|
63
|
+
* @see https://github.com/botuniverse/onebot-11/blob/master/communication/http.md
|
|
64
|
+
*/
|
|
65
|
+
export function readOnebot11Response<T = unknown>(response: unknown): T | undefined {
|
|
66
|
+
if (response && typeof response === 'object') {
|
|
67
|
+
const result = response as Partial<OneBotV11Response>;
|
|
68
|
+
if (result.status === 'ok' && result.retcode === 0) return result.data as T | undefined;
|
|
69
|
+
if (result.status === 'failed' && Number.isSafeInteger(result.retcode) && result.retcode !== 0 && result.retcode !== 1) {
|
|
70
|
+
throw new EndpointDeliveryError('platform_rejected',
|
|
71
|
+
`OneBot 11 API rejected: retcode=${result.retcode}${result.message ? ` ${result.message}` : ''}`, 'rejected');
|
|
72
|
+
}
|
|
73
|
+
}
|
|
74
|
+
// OB11 async/1 acknowledges submission, never the final outcome.
|
|
75
|
+
throw new EndpointDeliveryError('delivery_unconfirmed', 'OneBot 11 response did not confirm the action result', 'unknown');
|
|
55
76
|
}
|
|
56
77
|
|
|
57
78
|
const onebot11ClientEventNames = Object.freeze([
|
|
@@ -65,14 +86,3 @@ export function forwardOnebot11ClientEvents(
|
|
|
65
86
|
): () => void {
|
|
66
87
|
return forwardEndpointClientEvents(client, onebot11ClientEventNames, receive);
|
|
67
88
|
}
|
|
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');
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* OneBot 11 Console 页面元数据(浏览器与服务端共享;不要在这里 import 服务端代码)。
|
|
3
|
+
*/
|
|
4
|
+
import type { AdapterConsoleField } from 'zhin.js/adapter';
|
|
5
|
+
|
|
6
|
+
export const onebot11ConsoleSpec = {
|
|
7
|
+
adapterKey: 'onebot11',
|
|
8
|
+
adapterDisplayName: 'OneBot 11',
|
|
9
|
+
fields: [
|
|
10
|
+
{ key: 'url', description: 'OneBot 实现 WebSocket URL(connection: ws 必填)', descriptionI18n: { en: 'OneBot WebSocket URL (required for connection: ws)' } },
|
|
11
|
+
{ key: 'path', description: 'reverse-wss 路径(connection: wss)', descriptionI18n: { en: 'Reverse WebSocket path (connection: wss)' } },
|
|
12
|
+
{ key: 'access_token', env: true, description: 'OneBot access token' },
|
|
13
|
+
] as readonly AdapterConsoleField[],
|
|
14
|
+
docsUrl: 'https://zhin.js.org/adapters/onebot11',
|
|
15
|
+
readPermission: 'onebot11:read',
|
|
16
|
+
writePermission: 'onebot11:write',
|
|
17
|
+
} as const;
|
package/src/console.ts
ADDED
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* OneBot 11 Console RPC:运行状态 + endpoint 增删(原 `onebot11 endpoint` 聊天命令)。
|
|
3
|
+
*
|
|
4
|
+
* 页面绑定 `pages/index`,读取 / 写入分别要求 `<adapter>:read` / `<adapter>:write`。
|
|
5
|
+
*/
|
|
6
|
+
import { createAdapterConsoleRpc, type AdapterConsoleResources } from 'zhin.js/adapter';
|
|
7
|
+
import type { PluginConsoleRpc } from 'zhin.js';
|
|
8
|
+
import { onebot11ConsoleSpec } from './console-spec.js';
|
|
9
|
+
|
|
10
|
+
export function createOnebot11ConsoleRpc(
|
|
11
|
+
_resources: AdapterConsoleResources,
|
|
12
|
+
getConfig: () => unknown,
|
|
13
|
+
): PluginConsoleRpc {
|
|
14
|
+
return createAdapterConsoleRpc({
|
|
15
|
+
...onebot11ConsoleSpec,
|
|
16
|
+
getConfig,
|
|
17
|
+
});
|
|
18
|
+
}
|
package/src/content-port.ts
CHANGED
|
@@ -1,8 +1,8 @@
|
|
|
1
|
-
import type { EndpointContentPort, EndpointContentResolveContext } from 'zhin.js/adapter';
|
|
1
|
+
import type { EndpointContentPort, EndpointContentResolveContext, PendingRpcRequest } from 'zhin.js/adapter';
|
|
2
2
|
import { toCanonicalSegments } from '@zhin.js/core';
|
|
3
3
|
import type { ConversationMessage, ConversationReference, ForwardEntry } from '@zhin.js/im-contract';
|
|
4
4
|
|
|
5
|
-
type CallApi = (
|
|
5
|
+
type CallApi = (request: PendingRpcRequest) => Promise<unknown>;
|
|
6
6
|
|
|
7
7
|
export function createOneBot11ContentPort(callApi: CallApi): EndpointContentPort {
|
|
8
8
|
return Object.freeze({
|
|
@@ -15,7 +15,7 @@ export function createOneBot11ContentPort(callApi: CallApi): EndpointContentPort
|
|
|
15
15
|
: Object.freeze({ status: 'resolved' as const, reference, value: reference.media });
|
|
16
16
|
}
|
|
17
17
|
if (reference.kind === 'message') {
|
|
18
|
-
const row = await callApi('get_msg', { message_id: reference.message.id }) as Record<string, unknown>;
|
|
18
|
+
const row = await callApi({ action: 'get_msg', params: { message_id: reference.message.id }, signal: context.signal }) as Record<string, unknown>;
|
|
19
19
|
context.signal.throwIfAborted();
|
|
20
20
|
const segments = normalizeSegments(row.message ?? row.raw_message);
|
|
21
21
|
const sender = row.sender as Record<string, unknown> | undefined;
|
|
@@ -30,7 +30,7 @@ export function createOneBot11ContentPort(callApi: CallApi): EndpointContentPort
|
|
|
30
30
|
});
|
|
31
31
|
return Object.freeze({ status: 'resolved' as const, reference, value });
|
|
32
32
|
}
|
|
33
|
-
const raw = await callApi('get_forward_msg', { id: reference.forwardId }) as Record<string, unknown>;
|
|
33
|
+
const raw = await callApi({ action: 'get_forward_msg', params: { id: reference.forwardId }, signal: context.signal }) as Record<string, unknown>;
|
|
34
34
|
context.signal.throwIfAborted();
|
|
35
35
|
const nodes = Array.isArray(raw.messages) ? raw.messages.slice(0, context.maxEntries) : [];
|
|
36
36
|
const value: readonly ForwardEntry[] = Object.freeze(nodes.map((node) => {
|
|
@@ -7,23 +7,26 @@
|
|
|
7
7
|
* - group → {group_id:number, name: group_name ?? name}
|
|
8
8
|
* - 群成员列表保持 OneBot11 原生形状,仅保证数组
|
|
9
9
|
*/
|
|
10
|
-
import
|
|
11
|
-
EndpointFriend,
|
|
12
|
-
EndpointGroup,
|
|
13
|
-
EndpointManagement,
|
|
10
|
+
import {
|
|
11
|
+
type EndpointFriend,
|
|
12
|
+
type EndpointGroup,
|
|
13
|
+
type EndpointManagement,
|
|
14
|
+
type EndpointManagementRequest,
|
|
15
|
+
type EndpointGroupRequest,
|
|
16
|
+
type PendingRpcRequest,
|
|
14
17
|
} from 'zhin.js/adapter';
|
|
15
18
|
|
|
16
19
|
/** 管理面只依赖 endpoint 的 callApi(ws/wss 两种传输各自实现)。 */
|
|
17
20
|
export interface OneBot11ManagementCaller {
|
|
18
|
-
callApi(
|
|
21
|
+
callApi(request: PendingRpcRequest): Promise<unknown>;
|
|
19
22
|
}
|
|
20
23
|
|
|
21
24
|
export function createOneBot11EndpointManagement(
|
|
22
25
|
endpoint: OneBot11ManagementCaller,
|
|
23
26
|
): EndpointManagement {
|
|
24
27
|
return Object.freeze<EndpointManagement>({
|
|
25
|
-
async listFriends(): Promise<readonly EndpointFriend[]> {
|
|
26
|
-
const data = await endpoint.callApi('get_friend_list');
|
|
28
|
+
async listFriends(request: EndpointManagementRequest = {}): Promise<readonly EndpointFriend[]> {
|
|
29
|
+
const data = await endpoint.callApi({ action: 'get_friend_list', signal: request.signal });
|
|
27
30
|
return toArray(data).map((value) => {
|
|
28
31
|
const friend = asRecord(value);
|
|
29
32
|
return {
|
|
@@ -33,8 +36,8 @@ export function createOneBot11EndpointManagement(
|
|
|
33
36
|
};
|
|
34
37
|
});
|
|
35
38
|
},
|
|
36
|
-
async listGroups(): Promise<readonly EndpointGroup[]> {
|
|
37
|
-
const data = await endpoint.callApi('get_group_list');
|
|
39
|
+
async listGroups(request: EndpointManagementRequest = {}): Promise<readonly EndpointGroup[]> {
|
|
40
|
+
const data = await endpoint.callApi({ action: 'get_group_list', signal: request.signal });
|
|
38
41
|
return toArray(data).map((value) => {
|
|
39
42
|
const group = asRecord(value);
|
|
40
43
|
return {
|
|
@@ -43,10 +46,10 @@ export function createOneBot11EndpointManagement(
|
|
|
43
46
|
};
|
|
44
47
|
});
|
|
45
48
|
},
|
|
46
|
-
async listGroupMembers(groupId:
|
|
47
|
-
const data = await endpoint.callApi('get_group_member_list', {
|
|
49
|
+
async listGroupMembers({ groupId, signal }: EndpointGroupRequest): Promise<readonly unknown[]> {
|
|
50
|
+
const data = await endpoint.callApi({ action: 'get_group_member_list', signal, params: {
|
|
48
51
|
group_id: toNumberId(groupId, 'group_id'),
|
|
49
|
-
});
|
|
52
|
+
} });
|
|
50
53
|
return toArray(data);
|
|
51
54
|
},
|
|
52
55
|
});
|