@zhin.js/adapter-onebot12 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 +62 -0
- package/README.md +33 -4
- package/adapters/onebot12/index.js +3 -32
- package/adapters/onebot12/index.ts +2 -38
- package/commands/onebot12/endpoint/definition.js +2 -2
- package/commands/onebot12/endpoint/definition.ts +2 -2
- package/lib/adapter.d.ts +21 -0
- package/lib/adapter.js +34 -0
- package/lib/client.d.ts +9 -11
- package/lib/client.js +23 -13
- package/lib/console-spec.d.ts +12 -0
- package/lib/console-spec.js +15 -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 +1 -1
- package/lib/endpoint-management.d.ts +5 -5
- package/lib/endpoint-management.js +17 -17
- package/lib/identity.d.ts +4 -0
- package/lib/identity.js +53 -0
- package/lib/index.d.ts +3 -2
- package/lib/index.js +2 -1
- package/lib/protocol.d.ts +32 -4
- package/lib/protocol.js +54 -53
- package/lib/response.d.ts +5 -0
- package/lib/response.js +17 -0
- package/lib/side-event-dispatch.d.ts +8 -1
- package/lib/side-event-dispatch.js +2 -1
- package/lib/webhook.d.ts +5 -11
- package/lib/webhook.js +72 -37
- package/lib/ws-endpoint.d.ts +5 -11
- package/lib/ws-endpoint.js +79 -45
- package/lib/ws-transport.d.ts +18 -10
- package/lib/ws-transport.js +26 -45
- package/lib/ws-types.d.ts +0 -6
- package/lib/wss-endpoint.d.ts +5 -11
- package/lib/wss-endpoint.js +82 -48
- package/package.json +22 -14
- package/pages/index/index.js +15 -0
- package/pages/index/index.ts +16 -0
- package/plugin.js +3 -5
- package/schema.json +15 -3
- package/src/adapter.ts +36 -0
- package/src/client.ts +26 -29
- package/src/console-spec.ts +20 -0
- package/src/console.ts +18 -0
- package/src/content-port.ts +3 -3
- package/src/endpoint-management.ts +25 -24
- package/src/identity.ts +52 -0
- package/src/index.ts +4 -1
- package/src/protocol.ts +71 -62
- package/src/response.ts +19 -0
- package/src/side-event-dispatch.ts +9 -7
- package/src/webhook.ts +70 -55
- package/src/ws-endpoint.ts +75 -63
- package/src/ws-transport.ts +37 -73
- package/src/ws-types.ts +0 -7
- package/src/wss-endpoint.ts +80 -66
- package/lib/onebot12-runtime-state.d.ts +0 -1
- package/lib/onebot12-runtime-state.js +0 -6
- package/src/onebot12-runtime-state.ts +0 -7
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@zhin.js/adapter-onebot12",
|
|
3
|
-
"version": "1.1.
|
|
3
|
+
"version": "1.1.19",
|
|
4
4
|
"description": "Zhin.js OneBot 12 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",
|
|
@@ -41,27 +42,30 @@
|
|
|
41
42
|
"@imhelper/onebot-v12": "1.0.7",
|
|
42
43
|
"imhelper": "1.0.7",
|
|
43
44
|
"ws": "^8.21.1",
|
|
44
|
-
"@zhin.js/adapter": "1.1.
|
|
45
|
-
"@zhin.js/
|
|
46
|
-
"@zhin.js/
|
|
47
|
-
"@zhin.js/
|
|
48
|
-
"@zhin.js/
|
|
45
|
+
"@zhin.js/adapter": "1.1.26",
|
|
46
|
+
"@zhin.js/client": "1.1.22",
|
|
47
|
+
"@zhin.js/console-contract": "1.1.3",
|
|
48
|
+
"@zhin.js/core": "1.1.54",
|
|
49
|
+
"@zhin.js/feature-kit": "1.1.10",
|
|
50
|
+
"@zhin.js/host-http": "1.1.12",
|
|
51
|
+
"@zhin.js/im-contract": "1.1.9",
|
|
49
52
|
"@zhin.js/logger": "1.1.2",
|
|
50
|
-
"@zhin.js/
|
|
53
|
+
"@zhin.js/page": "1.1.11",
|
|
54
|
+
"@zhin.js/skill": "1.1.11"
|
|
51
55
|
},
|
|
52
56
|
"devDependencies": {
|
|
53
57
|
"@types/node": "^26.1.2",
|
|
54
58
|
"@types/ws": "^8.18.1",
|
|
55
59
|
"typescript": "^6.0.3",
|
|
56
60
|
"vitest": "^4.1.10",
|
|
57
|
-
"@zhin.js/host-http": "1.1.
|
|
58
|
-
"zhin.js": "1.1.
|
|
61
|
+
"@zhin.js/host-http": "1.1.12",
|
|
62
|
+
"zhin.js": "1.1.19"
|
|
59
63
|
},
|
|
60
64
|
"peerDependencies": {
|
|
61
|
-
"@zhin.js/adapter": "^1.1.
|
|
62
|
-
"@zhin.js/command": "^1.1.
|
|
63
|
-
"@zhin.js/core": "^1.1.
|
|
64
|
-
"zhin.js": "^1.1.
|
|
65
|
+
"@zhin.js/adapter": "^1.1.26",
|
|
66
|
+
"@zhin.js/command": "^1.1.12",
|
|
67
|
+
"@zhin.js/core": "^1.1.54",
|
|
68
|
+
"zhin.js": "^1.1.19"
|
|
65
69
|
},
|
|
66
70
|
"peerDependenciesMeta": {
|
|
67
71
|
"@zhin.js/command": {
|
|
@@ -92,7 +96,7 @@
|
|
|
92
96
|
"features": [
|
|
93
97
|
{
|
|
94
98
|
"package": "@zhin.js/adapter",
|
|
95
|
-
"api": "^
|
|
99
|
+
"api": "^2.0.0"
|
|
96
100
|
},
|
|
97
101
|
{
|
|
98
102
|
"package": "@zhin.js/command",
|
|
@@ -101,6 +105,10 @@
|
|
|
101
105
|
{
|
|
102
106
|
"package": "@zhin.js/skill",
|
|
103
107
|
"api": "^1.0.0"
|
|
108
|
+
},
|
|
109
|
+
{
|
|
110
|
+
"package": "@zhin.js/page",
|
|
111
|
+
"api": "^1.0.0"
|
|
104
112
|
}
|
|
105
113
|
],
|
|
106
114
|
"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 12',
|
|
6
|
+
titleI18n: { en: 'OneBot 12' },
|
|
7
|
+
icon: 'Bot',
|
|
8
|
+
order: 71,
|
|
9
|
+
requiredPermissions: ['onebot12:read'],
|
|
10
|
+
});
|
|
11
|
+
/**
|
|
12
|
+
* Convention page: `pages/index/index.ts` → `/onebot12`(插件路径)。
|
|
13
|
+
* 连接状态、连接测试、endpoint 增删;登录待办来自 LoginAssist(ICQQ 等)。
|
|
14
|
+
*/
|
|
15
|
+
export default createAdapterConsolePage('onebot12');
|
|
@@ -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 12',
|
|
6
|
+
titleI18n: { en: 'OneBot 12' },
|
|
7
|
+
icon: 'Bot',
|
|
8
|
+
order: 71,
|
|
9
|
+
requiredPermissions: ['onebot12:read'],
|
|
10
|
+
});
|
|
11
|
+
|
|
12
|
+
/**
|
|
13
|
+
* Convention page: `pages/index/index.ts` → `/onebot12`(插件路径)。
|
|
14
|
+
* 连接状态、连接测试、endpoint 增删;登录待办来自 LoginAssist(ICQQ 等)。
|
|
15
|
+
*/
|
|
16
|
+
export default createAdapterConsolePage('onebot12');
|
package/plugin.js
CHANGED
|
@@ -1,14 +1,12 @@
|
|
|
1
1
|
// Generated by build-plugin-runtime-entries.mjs. Do not edit.
|
|
2
|
-
import {
|
|
3
|
-
import {
|
|
4
|
-
import { onebot12RuntimeStateToken } from "./lib/onebot12-runtime-state.js";
|
|
2
|
+
import { definePlugin, pluginConsoleRpcToken } from 'zhin.js';
|
|
3
|
+
import { createOnebot12ConsoleRpc } from "./lib/console.js";
|
|
5
4
|
export default definePlugin({
|
|
6
5
|
name: 'onebot12',
|
|
7
6
|
metadata: {
|
|
8
7
|
displayName: 'OneBot 12 Adapter',
|
|
9
8
|
},
|
|
10
9
|
setup(context) {
|
|
11
|
-
|
|
12
|
-
context.resources.provide(onebot12RuntimeStateToken, createEndpointRuntimeState());
|
|
10
|
+
context.resources.provide(pluginConsoleRpcToken, createOnebot12ConsoleRpc(context.resources, () => context.config.get()));
|
|
13
11
|
},
|
|
14
12
|
});
|
package/schema.json
CHANGED
|
@@ -52,7 +52,7 @@
|
|
|
52
52
|
"endpoints": {
|
|
53
53
|
"type": "array",
|
|
54
54
|
"minItems": 1,
|
|
55
|
-
"description": "
|
|
55
|
+
"description": "多账号:每项明确选择 platform/userId,id 为本地账号名称,其余连接字段覆盖实例默认值",
|
|
56
56
|
"items": {
|
|
57
57
|
"type": "object",
|
|
58
58
|
"additionalProperties": false,
|
|
@@ -121,15 +121,27 @@
|
|
|
121
121
|
"id": {
|
|
122
122
|
"type": "string",
|
|
123
123
|
"description": "OneBot12 bot name"
|
|
124
|
+
},
|
|
125
|
+
"platform": {
|
|
126
|
+
"type": "string",
|
|
127
|
+
"minLength": 1,
|
|
128
|
+
"description": "Selected OneBot12 bot platform"
|
|
129
|
+
},
|
|
130
|
+
"userId": {
|
|
131
|
+
"type": "string",
|
|
132
|
+
"minLength": 1,
|
|
133
|
+
"description": "Selected OneBot12 bot user ID"
|
|
124
134
|
}
|
|
125
135
|
},
|
|
126
136
|
"required": [
|
|
127
|
-
"id"
|
|
137
|
+
"id",
|
|
138
|
+
"platform",
|
|
139
|
+
"userId"
|
|
128
140
|
]
|
|
129
141
|
},
|
|
130
142
|
"x-i18n": {
|
|
131
143
|
"en": {
|
|
132
|
-
"description": "
|
|
144
|
+
"description": "Each endpoint explicitly selects platform and userId. id is the local account name; other connection fields override instance defaults."
|
|
133
145
|
}
|
|
134
146
|
}
|
|
135
147
|
},
|
package/src/adapter.ts
ADDED
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
import { Adapter, type AdapterContext, type EndpointCreationContext } from 'zhin.js/adapter';
|
|
2
|
+
import { httpHostToken } from '@zhin.js/host-http';
|
|
3
|
+
import { resolveOneBot12Config, type OneBot12EndpointConfig } from './protocol.js';
|
|
4
|
+
import { OneBot12WsEndpoint } from './ws-endpoint.js';
|
|
5
|
+
import { OneBot12WssEndpoint } from './wss-endpoint.js';
|
|
6
|
+
import { OneBot12WebhookEndpoint } from './webhook.js';
|
|
7
|
+
import { probeOnebot12Identity, probeOnebot12HttpIdentity } from './identity.js';
|
|
8
|
+
import type { Onebot12ClientEventMap } from './client.js';
|
|
9
|
+
|
|
10
|
+
export class Onebot12Adapter extends Adapter<OneBot12WsEndpoint | OneBot12WssEndpoint | OneBot12WebhookEndpoint, OneBot12EndpointConfig, Onebot12ClientEventMap> {
|
|
11
|
+
readonly #context: AdapterContext<OneBot12EndpointConfig>;
|
|
12
|
+
constructor(context: AdapterContext<OneBot12EndpointConfig>) { super(); this.#context = context; }
|
|
13
|
+
override get configurationIdentityFields(): readonly string[] { return ['id', 'platform', 'userId']; }
|
|
14
|
+
override get configurationIdentityProbeFields(): readonly string[] { return ['connection', 'access_token', 'url', 'api_url']; }
|
|
15
|
+
override get configurationUpdateFields(): readonly string[] {
|
|
16
|
+
return ['connection', 'url', 'path', 'api_url', 'access_token', 'reconnect_interval', 'heartbeat_interval'];
|
|
17
|
+
}
|
|
18
|
+
protected override validateConfiguration(config: OneBot12EndpointConfig): void { resolveOneBot12Config(config); }
|
|
19
|
+
protected override async identifyConfiguration(input: OneBot12EndpointConfig, signal: AbortSignal): Promise<string | undefined> {
|
|
20
|
+
const config = resolveOneBot12Config(input);
|
|
21
|
+
if (config.connection === 'ws') return probeOnebot12Identity(config, signal);
|
|
22
|
+
if (config.connection === 'webhook') return probeOnebot12HttpIdentity(config, signal);
|
|
23
|
+
return undefined;
|
|
24
|
+
}
|
|
25
|
+
protected createEndpoint(input: OneBot12EndpointConfig, execution: EndpointCreationContext): OneBot12WsEndpoint | OneBot12WssEndpoint | OneBot12WebhookEndpoint {
|
|
26
|
+
const config = resolveOneBot12Config(input);
|
|
27
|
+
const id = execution.canonicalId;
|
|
28
|
+
if (config.connection === 'webhook') return new OneBot12WebhookEndpoint({ id, signal: execution.signal, http: this.#context.use(httpHostToken), config });
|
|
29
|
+
if (config.connection === 'wss') return new OneBot12WssEndpoint({ id, signal: execution.signal, http: this.#context.use(httpHostToken), config });
|
|
30
|
+
return new OneBot12WsEndpoint({ id, signal: execution.signal, config });
|
|
31
|
+
}
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
declare module '@zhin.js/feature-kit' {
|
|
35
|
+
interface AdapterRegistry { readonly onebot12: Onebot12Adapter; }
|
|
36
|
+
}
|
package/src/client.ts
CHANGED
|
@@ -1,11 +1,13 @@
|
|
|
1
|
+
import { readOnebot12Response } from './response.js';
|
|
2
|
+
export { readOnebot12Response } from './response.js';
|
|
3
|
+
import { EndpointDeliveryError } from '@zhin.js/im-contract';
|
|
1
4
|
import {
|
|
2
5
|
OneBotV12Client,
|
|
3
6
|
type OneBotV12Event as ImHelperOneBot12Event,
|
|
4
7
|
type OneBotV12Response,
|
|
5
8
|
} from '@imhelper/onebot-v12';
|
|
6
|
-
import { EventFactory, type EventMap, type ImHelperEventMap } from 'imhelper';
|
|
9
|
+
import { ProtocolError, EventFactory, type EventMap, type ImHelperEventMap } from 'imhelper';
|
|
7
10
|
import {
|
|
8
|
-
defineEndpointClient,
|
|
9
11
|
forwardEndpointClientEvents,
|
|
10
12
|
type ClientEventPayloads,
|
|
11
13
|
} from 'zhin.js/adapter';
|
|
@@ -25,38 +27,44 @@ type Onebot12ClientEvents = ImHelperEventMap<
|
|
|
25
27
|
>;
|
|
26
28
|
export type Onebot12ClientEventMap = ClientEventPayloads<Onebot12ClientEvents>;
|
|
27
29
|
|
|
28
|
-
export function
|
|
30
|
+
export function onebot12ClientOptions(
|
|
29
31
|
config: ResolvedOneBot12Config,
|
|
30
32
|
request: Onebot12ApiCall,
|
|
31
|
-
): OneBotV12Client {
|
|
33
|
+
): ConstructorParameters<typeof OneBotV12Client>[0] {
|
|
32
34
|
const baseUrl = config.connection === 'ws'
|
|
33
35
|
? config.url.replace(/^ws(s?):/, 'http$1:')
|
|
34
36
|
: config.connection === 'webhook'
|
|
35
37
|
? config.api_url
|
|
36
38
|
: 'http://localhost';
|
|
37
|
-
return
|
|
39
|
+
return {
|
|
38
40
|
baseUrl,
|
|
39
|
-
|
|
41
|
+
platform: config.platform,
|
|
42
|
+
selfId: config.userId,
|
|
40
43
|
accessToken: config.access_token,
|
|
41
44
|
receiveMode: 'manual',
|
|
42
45
|
call: request,
|
|
43
|
-
}
|
|
46
|
+
};
|
|
44
47
|
}
|
|
45
48
|
|
|
46
|
-
export
|
|
47
|
-
client: OneBotV12Client
|
|
48
|
-
action: string
|
|
49
|
-
params?: Record<string, unknown
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
49
|
+
export interface Onebot12ClientRequest {
|
|
50
|
+
readonly client: OneBotV12Client;
|
|
51
|
+
readonly action: string;
|
|
52
|
+
readonly params?: Record<string, unknown>;
|
|
53
|
+
}
|
|
54
|
+
/** Native methods retain ProtocolError; framework ports preserve the underlying delivery evidence. */
|
|
55
|
+
export async function callOnebot12Client<T = unknown>(request: Onebot12ClientRequest): Promise<T | undefined> {
|
|
56
|
+
try {
|
|
57
|
+
return readOnebot12Response<T>(await request.client.call<T>(request.action, request.params));
|
|
58
|
+
} catch (error) {
|
|
59
|
+
if (error instanceof ProtocolError) {
|
|
60
|
+
if (error.cause instanceof EndpointDeliveryError) throw error.cause;
|
|
61
|
+
if (error.response !== undefined) readOnebot12Response(error.response);
|
|
62
|
+
}
|
|
63
|
+
throw error;
|
|
56
64
|
}
|
|
57
|
-
return response.data;
|
|
58
65
|
}
|
|
59
66
|
|
|
67
|
+
|
|
60
68
|
const onebot12ClientEventNames = Object.freeze([
|
|
61
69
|
...EventFactory.getSupportedEventTypes<string>(),
|
|
62
70
|
'event',
|
|
@@ -68,14 +76,3 @@ export function forwardOnebot12ClientEvents(
|
|
|
68
76
|
): () => void {
|
|
69
77
|
return forwardEndpointClientEvents(client, onebot12ClientEventNames, receive);
|
|
70
78
|
}
|
|
71
|
-
|
|
72
|
-
declare module '@zhin.js/feature-kit' {
|
|
73
|
-
interface AdapterClientRegistry {
|
|
74
|
-
readonly onebot12: {
|
|
75
|
-
readonly client: OneBotV12Client;
|
|
76
|
-
readonly events: Onebot12ClientEventMap;
|
|
77
|
-
};
|
|
78
|
-
}
|
|
79
|
-
}
|
|
80
|
-
|
|
81
|
-
export const onebot12Client = defineEndpointClient<OneBotV12Client, Onebot12ClientEventMap>('onebot12');
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* OneBot 12 Console 页面元数据(浏览器与服务端共享;不要在这里 import 服务端代码)。
|
|
3
|
+
*/
|
|
4
|
+
import type { AdapterConsoleField } from 'zhin.js/adapter';
|
|
5
|
+
|
|
6
|
+
export const onebot12ConsoleSpec = {
|
|
7
|
+
adapterKey: 'onebot12',
|
|
8
|
+
adapterDisplayName: 'OneBot 12',
|
|
9
|
+
fields: [
|
|
10
|
+
{ key: 'platform', required: true, description: 'Selected OneBot12 bot platform' },
|
|
11
|
+
{ key: 'userId', required: true, description: 'Selected OneBot12 bot user ID' },
|
|
12
|
+
{ key: 'url', description: 'OneBot 实现 WebSocket URL(connection: ws 必填)', descriptionI18n: { en: 'OneBot WebSocket URL (required for connection: ws)' } },
|
|
13
|
+
{ key: 'path', description: 'HTTP/WS 路径(webhook / reverse-wss)', descriptionI18n: { en: 'HTTP/WS path (webhook / reverse-wss)' } },
|
|
14
|
+
{ key: 'api_url', description: 'HTTP action 端点(connection: webhook 出站必填)', descriptionI18n: { en: 'HTTP action endpoint (required for outbound connection: webhook)' } },
|
|
15
|
+
{ key: 'access_token', env: true, description: 'OneBot access token' },
|
|
16
|
+
] as readonly AdapterConsoleField[],
|
|
17
|
+
docsUrl: 'https://zhin.js.org/adapters/onebot12',
|
|
18
|
+
readPermission: 'onebot12:read',
|
|
19
|
+
writePermission: 'onebot12:write',
|
|
20
|
+
} as const;
|
package/src/console.ts
ADDED
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* OneBot 12 Console RPC:运行状态 + endpoint 增删(原 `onebot12 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 { onebot12ConsoleSpec } from './console-spec.js';
|
|
9
|
+
|
|
10
|
+
export function createOnebot12ConsoleRpc(
|
|
11
|
+
_resources: AdapterConsoleResources,
|
|
12
|
+
getConfig: () => unknown,
|
|
13
|
+
): PluginConsoleRpc {
|
|
14
|
+
return createAdapterConsoleRpc({
|
|
15
|
+
...onebot12ConsoleSpec,
|
|
16
|
+
getConfig,
|
|
17
|
+
});
|
|
18
|
+
}
|
package/src/content-port.ts
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
|
-
import type { EndpointContentPort, EndpointContentResolveContext } from 'zhin.js/adapter';
|
|
1
|
+
import type { EndpointContentPort, EndpointContentResolveContext, PendingRpcRequest } from 'zhin.js/adapter';
|
|
2
2
|
import type { ConversationMessage, ConversationReference } from '@zhin.js/im-contract';
|
|
3
3
|
|
|
4
|
-
type CallApi = (
|
|
4
|
+
type CallApi = (request: PendingRpcRequest) => Promise<unknown>;
|
|
5
5
|
|
|
6
6
|
export function createOneBot12ContentPort(callApi: CallApi): EndpointContentPort {
|
|
7
7
|
return Object.freeze({
|
|
@@ -16,7 +16,7 @@ export function createOneBot12ContentPort(callApi: CallApi): EndpointContentPort
|
|
|
16
16
|
? Object.freeze({ status: 'unsupported' as const, code: 'onebot12_opaque_media' })
|
|
17
17
|
: Object.freeze({ status: 'resolved' as const, reference, value: reference.media });
|
|
18
18
|
}
|
|
19
|
-
const row = await callApi('get_message', { message_id: reference.message.id }) as Record<string, unknown>;
|
|
19
|
+
const row = await callApi({ action: 'get_message', params: { message_id: reference.message.id }, signal: context.signal }) as Record<string, unknown>;
|
|
20
20
|
context.signal.throwIfAborted();
|
|
21
21
|
const message = (row.message && typeof row.message === 'object' ? row.message : row) as Record<string, unknown>;
|
|
22
22
|
const segments = normalizeSegments(message.message ?? message.content);
|
|
@@ -3,51 +3,54 @@
|
|
|
3
3
|
* packages/im/adapter/src/endpoint-management.ts)。
|
|
4
4
|
*
|
|
5
5
|
* 动作名对齐 OB12 规范:get_friend_list / get_group_list / get_group_member_list。
|
|
6
|
-
*
|
|
7
|
-
* - friend → {user_id:
|
|
8
|
-
* - group → {group_id:
|
|
6
|
+
* 投影仅归一化展示字段;OB12 账号与会话 ID 始终保留为不透明字符串:
|
|
7
|
+
* - friend → {user_id:string, nickname: user_name, remark: user_displayname ?? ''}
|
|
8
|
+
* - group → {group_id:string, name: group_name ?? name}
|
|
9
9
|
* - 群成员列表保持 OB12 原生形状,仅保证数组
|
|
10
10
|
*/
|
|
11
|
-
import
|
|
12
|
-
EndpointFriend,
|
|
13
|
-
EndpointGroup,
|
|
14
|
-
EndpointManagement,
|
|
11
|
+
import {
|
|
12
|
+
type EndpointFriend,
|
|
13
|
+
type EndpointGroup,
|
|
14
|
+
type EndpointManagement,
|
|
15
|
+
type EndpointManagementRequest,
|
|
16
|
+
type EndpointGroupRequest,
|
|
17
|
+
type PendingRpcRequest,
|
|
15
18
|
} from 'zhin.js/adapter';
|
|
16
19
|
|
|
17
20
|
/** 管理面只依赖 endpoint 的 callApi(ws/wss/webhook 三种传输各自实现)。 */
|
|
18
21
|
export interface OneBot12ManagementCaller {
|
|
19
|
-
callApi(
|
|
22
|
+
callApi(request: PendingRpcRequest): Promise<unknown>;
|
|
20
23
|
}
|
|
21
24
|
|
|
22
25
|
export function createOneBot12EndpointManagement(
|
|
23
26
|
endpoint: OneBot12ManagementCaller,
|
|
24
27
|
): EndpointManagement {
|
|
25
28
|
return Object.freeze<EndpointManagement>({
|
|
26
|
-
async listFriends(): Promise<readonly EndpointFriend[]> {
|
|
27
|
-
const data = await endpoint.callApi('get_friend_list');
|
|
29
|
+
async listFriends(request: EndpointManagementRequest = {}): Promise<readonly EndpointFriend[]> {
|
|
30
|
+
const data = await endpoint.callApi({ action: 'get_friend_list', signal: request.signal });
|
|
28
31
|
return toArray(data).map((value) => {
|
|
29
32
|
const friend = asRecord(value);
|
|
30
33
|
return {
|
|
31
|
-
user_id:
|
|
34
|
+
user_id: readOpaqueId(friend.user_id, 'user_id'),
|
|
32
35
|
nickname: String(friend.user_name ?? friend.nickname ?? ''),
|
|
33
36
|
remark: String(friend.user_displayname ?? friend.remark ?? ''),
|
|
34
37
|
};
|
|
35
38
|
});
|
|
36
39
|
},
|
|
37
|
-
async listGroups(): Promise<readonly EndpointGroup[]> {
|
|
38
|
-
const data = await endpoint.callApi('get_group_list');
|
|
40
|
+
async listGroups(request: EndpointManagementRequest = {}): Promise<readonly EndpointGroup[]> {
|
|
41
|
+
const data = await endpoint.callApi({ action: 'get_group_list', signal: request.signal });
|
|
39
42
|
return toArray(data).map((value) => {
|
|
40
43
|
const group = asRecord(value);
|
|
41
44
|
return {
|
|
42
|
-
group_id:
|
|
45
|
+
group_id: readOpaqueId(group.group_id, 'group_id'),
|
|
43
46
|
name: String(group.group_name ?? group.name ?? ''),
|
|
44
47
|
};
|
|
45
48
|
});
|
|
46
49
|
},
|
|
47
|
-
async listGroupMembers(groupId:
|
|
48
|
-
const data = await endpoint.callApi('get_group_member_list', {
|
|
50
|
+
async listGroupMembers({ groupId, signal }: EndpointGroupRequest): Promise<readonly unknown[]> {
|
|
51
|
+
const data = await endpoint.callApi({ action: 'get_group_member_list', signal, params: {
|
|
49
52
|
group_id: groupId,
|
|
50
|
-
});
|
|
53
|
+
} });
|
|
51
54
|
return toArray(data);
|
|
52
55
|
},
|
|
53
56
|
});
|
|
@@ -63,11 +66,9 @@ function toArray(value: unknown): readonly unknown[] {
|
|
|
63
66
|
return Array.isArray(value) ? value : [];
|
|
64
67
|
}
|
|
65
68
|
|
|
66
|
-
/**
|
|
67
|
-
function
|
|
68
|
-
|
|
69
|
-
if (
|
|
70
|
-
|
|
71
|
-
}
|
|
72
|
-
return n;
|
|
69
|
+
/** Platform IDs are addresses, including nonnumeric IDs and integers beyond Number precision. */
|
|
70
|
+
function readOpaqueId(value: unknown, label: string): string {
|
|
71
|
+
if (typeof value === 'string' && value.trim()) return value;
|
|
72
|
+
if (typeof value === 'number' && Number.isSafeInteger(value)) return String(value);
|
|
73
|
+
throw new TypeError(`onebot12 ${label} must be an opaque string or safe integer platform ID`);
|
|
73
74
|
}
|
package/src/identity.ts
ADDED
|
@@ -0,0 +1,52 @@
|
|
|
1
|
+
import WebSocket from 'ws';
|
|
2
|
+
import { buildWsConnectOptions, callOneBot12Action, type OneBot12ActionResponse, type OneBot12WebhookConfig, type OneBot12WsConfig } from './protocol.js';
|
|
3
|
+
import { createOneBot12Rpc } from './ws-transport.js';
|
|
4
|
+
|
|
5
|
+
/** Separate read-only connection: never changes credentials or state of the running Endpoint. */
|
|
6
|
+
export async function probeOnebot12Identity(config: OneBot12WsConfig, signal: AbortSignal): Promise<string | undefined> {
|
|
7
|
+
signal.throwIfAborted();
|
|
8
|
+
const { url, headers } = buildWsConnectOptions(config);
|
|
9
|
+
const socket = new WebSocket(url, { headers });
|
|
10
|
+
const rpc = createOneBot12Rpc({ endpointId: config.id, signal, socket: () => socket, self: { platform: config.platform, user_id: config.userId }, });
|
|
11
|
+
let rejectOpen: (error: unknown) => void = () => undefined;
|
|
12
|
+
const abort = () => { rejectOpen(signal.reason); rpc.disconnect(); socket.terminate(); };
|
|
13
|
+
signal.addEventListener('abort', abort, { once: true });
|
|
14
|
+
socket.on('message', data => rpc.receive(data, () => undefined));
|
|
15
|
+
socket.on('close', () => { rejectOpen(new Error('Identity connection closed')); rpc.disconnect(); });
|
|
16
|
+
try {
|
|
17
|
+
await new Promise<void>((resolve, reject) => { rejectOpen = reject; socket.once('open', resolve); socket.on('error', reject); });
|
|
18
|
+
signal.throwIfAborted();
|
|
19
|
+
const self = { platform: config.platform, user_id: config.userId };
|
|
20
|
+
return await identifySelectedBot({ selected: self, query: action => rpc.call({ action }), signal });
|
|
21
|
+
} finally {
|
|
22
|
+
signal.removeEventListener('abort', abort); rpc.dispose(); socket.terminate();
|
|
23
|
+
}
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
export async function probeOnebot12HttpIdentity(config: OneBot12WebhookConfig, signal: AbortSignal): Promise<string | undefined> {
|
|
27
|
+
const self = { platform: config.platform, user_id: config.userId };
|
|
28
|
+
return identifySelectedBot({ selected: self, query: action => callOneBot12Action({ url: config.api_url, access_token: config.access_token, signal, self, action }), signal });
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
interface OneBot12IdentityProbe {
|
|
32
|
+
readonly selected: Readonly<{ platform: string; user_id: string }>;
|
|
33
|
+
readonly query: (action: string) => Promise<OneBot12ActionResponse>;
|
|
34
|
+
readonly signal: AbortSignal;
|
|
35
|
+
}
|
|
36
|
+
async function identifySelectedBot({ selected, query, signal }: OneBot12IdentityProbe): Promise<string | undefined> {
|
|
37
|
+
const status = await query('get_status');
|
|
38
|
+
signal.throwIfAborted();
|
|
39
|
+
if (status.status !== 'ok' || status.retcode !== 0) throw new Error('OneBot12 identity status lookup failed');
|
|
40
|
+
const data = status.data;
|
|
41
|
+
if (!data || typeof data !== 'object' || !('bots' in data) || !Array.isArray(data.bots)) return undefined;
|
|
42
|
+
const match = data.bots.some((bot: unknown) => {
|
|
43
|
+
if (!bot || typeof bot !== 'object' || !('self' in bot) || !bot.self || typeof bot.self !== 'object') return false;
|
|
44
|
+
return 'platform' in bot.self && 'user_id' in bot.self && bot.self.platform === selected.platform && bot.self.user_id === selected.user_id;
|
|
45
|
+
});
|
|
46
|
+
if (!match) return undefined;
|
|
47
|
+
const account = await query('get_self_info');
|
|
48
|
+
signal.throwIfAborted();
|
|
49
|
+
if (account.status !== 'ok' || account.retcode !== 0) throw new Error('OneBot12 account identity lookup failed');
|
|
50
|
+
if (!account.data || typeof account.data !== 'object' || !('user_id' in account.data) || account.data.user_id !== selected.user_id) return undefined;
|
|
51
|
+
return `onebot12:${JSON.stringify([selected.platform, selected.user_id])}`;
|
|
52
|
+
}
|
package/src/index.ts
CHANGED
|
@@ -21,6 +21,9 @@ export {
|
|
|
21
21
|
type OneBot12EndpointConfig,
|
|
22
22
|
type OneBot12Event,
|
|
23
23
|
type OneBot12HttpOptions,
|
|
24
|
+
type OneBot12HttpRequest,
|
|
25
|
+
type OneBot12MediaUpload,
|
|
26
|
+
type OneBot12MediaSource,
|
|
24
27
|
type OneBot12Segment,
|
|
25
28
|
type OneBot12Self,
|
|
26
29
|
type OneBot12WebhookConfig,
|
|
@@ -50,7 +53,6 @@ export type { OneBot12WsSocket, OneBot12WsCreateOptions } from './ws-types.js';
|
|
|
50
53
|
export { verifyOneBotAccessToken } from './wss-auth.js';
|
|
51
54
|
export {
|
|
52
55
|
Onebot12Client,
|
|
53
|
-
onebot12Client,
|
|
54
56
|
type Onebot12ClientEventMap,
|
|
55
57
|
} from './client.js';
|
|
56
58
|
|
|
@@ -64,3 +66,4 @@ export type {
|
|
|
64
66
|
} from '@imhelper/onebot-v12';
|
|
65
67
|
export { ProtocolError } from '@imhelper/onebot-v12';
|
|
66
68
|
export type { ProtocolErrorKind, ProtocolErrorOptions } from 'imhelper';
|
|
69
|
+
export { Onebot12Adapter } from './adapter.js';
|