@zhin.js/adapter-onebot12 5.0.1 → 5.0.4
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 +55 -0
- package/README.md +3 -3
- package/adapters/onebot12.js +2 -2
- package/adapters/onebot12.ts +2 -2
- package/lib/index.d.ts +1 -1
- package/lib/index.js +1 -1
- package/lib/protocol.d.ts +13 -12
- package/lib/protocol.js +41 -43
- package/lib/webhook.d.ts +2 -5
- package/lib/webhook.js +18 -17
- package/lib/ws-endpoint.d.ts +2 -5
- package/lib/ws-endpoint.js +28 -26
- package/lib/wss-endpoint.d.ts +2 -5
- package/lib/wss-endpoint.js +25 -24
- package/package.json +13 -12
- package/schema.json +41 -5
- package/src/index.ts +1 -3
- package/src/protocol.ts +43 -51
- package/src/webhook.ts +19 -18
- package/src/ws-endpoint.ts +30 -27
- package/src/wss-endpoint.ts +27 -26
- /package/commands/endpoint/add/{[name:string].js → [id].js} +0 -0
- /package/commands/endpoint/add/{[name:string].ts → [id].ts} +0 -0
- /package/commands/endpoint/remove/{[name:string].js → [id].js} +0 -0
- /package/commands/endpoint/remove/{[name:string].ts → [id].ts} +0 -0
package/lib/wss-endpoint.js
CHANGED
|
@@ -2,13 +2,13 @@
|
|
|
2
2
|
* OneBot12 reverse WSS endpoint — accepts inbound WebSocket from OneBot implementation.
|
|
3
3
|
*/
|
|
4
4
|
import { clearInterval } from 'node:timers';
|
|
5
|
-
import { formatCompact,
|
|
5
|
+
import { formatCompact, getAdapterLogger } from '@zhin.js/logger';
|
|
6
6
|
import { createOneBot12EndpointManagement } from './endpoint-management.js';
|
|
7
|
-
import { buildSendMessageParams, formatInboundContent,
|
|
7
|
+
import { buildSendMessageParams, formatInboundContent, formatOutboundSegments, isBotMentioned, isMessageEvent, onebot12InboundConversation, senderNickname, senderUserId, uploadOneBot12MediaSegments, } from './protocol.js';
|
|
8
8
|
import { verifyOneBotAccessToken } from './wss-auth.js';
|
|
9
9
|
import { WS_OPEN } from './ws-types.js';
|
|
10
|
-
const logger = getLogger('onebot12');
|
|
11
10
|
export class OneBot12WssEndpoint {
|
|
11
|
+
#logger;
|
|
12
12
|
#options;
|
|
13
13
|
management = createOneBot12EndpointManagement(this);
|
|
14
14
|
#ws;
|
|
@@ -19,6 +19,7 @@ export class OneBot12WssEndpoint {
|
|
|
19
19
|
#open = false;
|
|
20
20
|
#started = false;
|
|
21
21
|
constructor(options) {
|
|
22
|
+
this.#logger = getAdapterLogger('onebot12', options.config.id);
|
|
22
23
|
this.#options = options;
|
|
23
24
|
}
|
|
24
25
|
async start() {
|
|
@@ -27,8 +28,8 @@ export class OneBot12WssEndpoint {
|
|
|
27
28
|
this.#started = true;
|
|
28
29
|
if (!this.#options.config.access_token) {
|
|
29
30
|
// wss 模式未配 access_token 时任何连接都会被放行(verifyOneBotAccessToken 直接 return true)
|
|
30
|
-
logger.warn(formatCompact({
|
|
31
|
-
endpoint: this.#options.config.
|
|
31
|
+
this.#logger.warn(formatCompact({
|
|
32
|
+
endpoint: this.#options.config.id,
|
|
32
33
|
mode: 'wss',
|
|
33
34
|
ok: false,
|
|
34
35
|
error: 'missing access_token',
|
|
@@ -38,9 +39,9 @@ export class OneBot12WssEndpoint {
|
|
|
38
39
|
this.#wsRelease = handle.onConnection((connection) => {
|
|
39
40
|
this.#acceptConnection(connection);
|
|
40
41
|
});
|
|
41
|
-
logger.info(formatCompact({
|
|
42
|
+
this.#logger.info(formatCompact({
|
|
42
43
|
op: 'listen',
|
|
43
|
-
endpoint: this.#options.config.
|
|
44
|
+
endpoint: this.#options.config.id,
|
|
44
45
|
mode: 'wss',
|
|
45
46
|
path: this.#options.config.path,
|
|
46
47
|
}));
|
|
@@ -75,16 +76,16 @@ export class OneBot12WssEndpoint {
|
|
|
75
76
|
}
|
|
76
77
|
this.#started = false;
|
|
77
78
|
}
|
|
78
|
-
async send({
|
|
79
|
+
async send({ conversation, payload }) {
|
|
79
80
|
const materialized = await uploadOneBot12MediaSegments(payload, (action, params) => this.callApi(action, params), (error) => {
|
|
80
|
-
logger.warn(formatCompact({
|
|
81
|
+
this.#logger.warn(formatCompact({
|
|
81
82
|
op: 'onebot12_upload_failed',
|
|
82
|
-
endpoint: this.#options.config.
|
|
83
|
+
endpoint: this.#options.config.id,
|
|
83
84
|
error: error instanceof Error ? error.message : String(error),
|
|
84
85
|
}));
|
|
85
86
|
});
|
|
86
87
|
const message = formatOutboundSegments(materialized);
|
|
87
|
-
const params = buildSendMessageParams(
|
|
88
|
+
const params = buildSendMessageParams(conversation, message);
|
|
88
89
|
const data = await this.#callAction('send_message', params);
|
|
89
90
|
return data?.message_id ?? '';
|
|
90
91
|
}
|
|
@@ -100,30 +101,30 @@ export class OneBot12WssEndpoint {
|
|
|
100
101
|
admit(ev) {
|
|
101
102
|
if (!this.#open || !isMessageEvent(ev))
|
|
102
103
|
return;
|
|
103
|
-
const
|
|
104
|
+
const conversation = onebot12InboundConversation(String(this.#options.id), ev);
|
|
104
105
|
const nickname = senderNickname(ev);
|
|
105
106
|
const mentioned = isBotMentioned(ev);
|
|
106
107
|
void this.#options.gateway.receive({
|
|
107
|
-
|
|
108
|
-
|
|
108
|
+
conversation,
|
|
109
|
+
message: { conversation, id: ev.message_id },
|
|
109
110
|
content: formatInboundContent(ev),
|
|
110
|
-
sender: senderUserId(ev),
|
|
111
|
-
|
|
111
|
+
sender: { id: senderUserId(ev), name: senderNickname(ev) },
|
|
112
|
+
endpointId: this.#options.config.id,
|
|
113
|
+
...(mentioned ? { mentioned: true } : {}),
|
|
112
114
|
metadata: Object.freeze({
|
|
113
115
|
detail_type: ev.detail_type,
|
|
114
116
|
user_id: ev.user_id,
|
|
115
117
|
group_id: ev.group_id,
|
|
116
118
|
channel_id: ev.channel_id,
|
|
117
119
|
guild_id: ev.guild_id,
|
|
118
|
-
endpoint: this.#options.config.name,
|
|
119
120
|
time: ev.time,
|
|
120
121
|
...(nickname ? { nickname } : {}),
|
|
121
|
-
...(mentioned ? { mentioned: true } : {}),
|
|
122
122
|
}),
|
|
123
123
|
}).catch((err) => {
|
|
124
|
-
logger.warn(formatCompact({
|
|
124
|
+
this.#logger.warn(formatCompact({
|
|
125
125
|
op: 'onebot12_gateway_receive_failed',
|
|
126
|
-
|
|
126
|
+
kind: conversation.kind,
|
|
127
|
+
conversationId: conversation.id,
|
|
127
128
|
error: err instanceof Error ? err.message : String(err),
|
|
128
129
|
}));
|
|
129
130
|
});
|
|
@@ -156,8 +157,8 @@ export class OneBot12WssEndpoint {
|
|
|
156
157
|
}
|
|
157
158
|
}
|
|
158
159
|
});
|
|
159
|
-
logger.debug(formatCompact({
|
|
160
|
-
endpoint: this.#options.config.
|
|
160
|
+
this.#logger.debug(formatCompact({
|
|
161
|
+
endpoint: this.#options.config.id,
|
|
161
162
|
mode: 'wss',
|
|
162
163
|
peer: connection.request.socket.remoteAddress,
|
|
163
164
|
}));
|
|
@@ -188,9 +189,9 @@ export class OneBot12WssEndpoint {
|
|
|
188
189
|
this.admit(msg);
|
|
189
190
|
}
|
|
190
191
|
catch (error) {
|
|
191
|
-
logger.warn(formatCompact({
|
|
192
|
+
this.#logger.warn(formatCompact({
|
|
192
193
|
op: 'onebot12_parse_failed',
|
|
193
|
-
endpoint: this.#options.config.
|
|
194
|
+
endpoint: this.#options.config.id,
|
|
194
195
|
error: error instanceof Error ? error.message : String(error),
|
|
195
196
|
}));
|
|
196
197
|
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@zhin.js/adapter-onebot12",
|
|
3
|
-
"version": "5.0.
|
|
3
|
+
"version": "5.0.4",
|
|
4
4
|
"description": "Zhin.js OneBot 12 adapter for Plugin Runtime (WebSocket client)",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "./lib/index.js",
|
|
@@ -40,25 +40,26 @@
|
|
|
40
40
|
},
|
|
41
41
|
"dependencies": {
|
|
42
42
|
"ws": "^8.21.1",
|
|
43
|
-
"@zhin.js/adapter": "1.1.
|
|
44
|
-
"@zhin.js/command": "1.0.
|
|
45
|
-
"@zhin.js/core": "1.5.
|
|
46
|
-
"@zhin.js/host-http": "1.0.
|
|
47
|
-
"@zhin.js/
|
|
48
|
-
"@zhin.js/
|
|
43
|
+
"@zhin.js/adapter": "1.1.7",
|
|
44
|
+
"@zhin.js/command": "1.0.9",
|
|
45
|
+
"@zhin.js/core": "1.5.4",
|
|
46
|
+
"@zhin.js/host-http": "1.0.8",
|
|
47
|
+
"@zhin.js/im-contract": "1.0.3",
|
|
48
|
+
"@zhin.js/logger": "1.0.76",
|
|
49
|
+
"@zhin.js/plugin-runtime": "1.1.5"
|
|
49
50
|
},
|
|
50
51
|
"devDependencies": {
|
|
51
52
|
"@types/node": "^26.1.2",
|
|
52
53
|
"@types/ws": "^8.18.1",
|
|
53
54
|
"typescript": "^6.0.3",
|
|
54
55
|
"vitest": "^4.1.10",
|
|
55
|
-
"@zhin.js/host-http": "1.0.
|
|
56
|
+
"@zhin.js/host-http": "1.0.8"
|
|
56
57
|
},
|
|
57
58
|
"peerDependencies": {
|
|
58
|
-
"@zhin.js/adapter": "1.1.
|
|
59
|
-
"@zhin.js/core": "1.5.
|
|
60
|
-
"@zhin.js/plugin-runtime": "1.1.
|
|
61
|
-
"zhin.js": "6.0.
|
|
59
|
+
"@zhin.js/adapter": "1.1.7",
|
|
60
|
+
"@zhin.js/core": "1.5.4",
|
|
61
|
+
"@zhin.js/plugin-runtime": "1.1.5",
|
|
62
|
+
"zhin.js": "6.0.4"
|
|
62
63
|
},
|
|
63
64
|
"peerDependenciesMeta": {
|
|
64
65
|
"zhin.js": {
|
package/schema.json
CHANGED
|
@@ -21,16 +21,48 @@
|
|
|
21
21
|
"type": "number",
|
|
22
22
|
"default": 30000
|
|
23
23
|
},
|
|
24
|
+
"master": {
|
|
25
|
+
"type": [
|
|
26
|
+
"string",
|
|
27
|
+
"number"
|
|
28
|
+
],
|
|
29
|
+
"description": "框架 master(platform user id;AI/工具权限、endpoint 管理)。endpoints[i].master 可逐项覆盖"
|
|
30
|
+
},
|
|
31
|
+
"trusted": {
|
|
32
|
+
"type": "array",
|
|
33
|
+
"items": {
|
|
34
|
+
"type": [
|
|
35
|
+
"string",
|
|
36
|
+
"number"
|
|
37
|
+
],
|
|
38
|
+
"description": "Trusted platform user id"
|
|
39
|
+
},
|
|
40
|
+
"description": "框架 trusted 用户列表(弱于 master)。endpoints[i].trusted 可逐项追加"
|
|
41
|
+
},
|
|
24
42
|
"endpoints": {
|
|
25
43
|
"type": "array",
|
|
26
|
-
"description": "多账号:一个插件实例挂多个 endpoint。每项与顶层字段同构(
|
|
44
|
+
"description": "多账号:一个插件实例挂多个 endpoint。每项与顶层字段同构(id 必填,其余覆盖顶层)",
|
|
27
45
|
"items": {
|
|
28
46
|
"type": "object",
|
|
29
47
|
"additionalProperties": true,
|
|
30
48
|
"properties": {
|
|
31
|
-
"
|
|
32
|
-
"type":
|
|
33
|
-
|
|
49
|
+
"master": {
|
|
50
|
+
"type": [
|
|
51
|
+
"string",
|
|
52
|
+
"number"
|
|
53
|
+
],
|
|
54
|
+
"description": "本 endpoint 的框架 master(platform user id);覆盖顶层 master"
|
|
55
|
+
},
|
|
56
|
+
"trusted": {
|
|
57
|
+
"type": "array",
|
|
58
|
+
"items": {
|
|
59
|
+
"type": [
|
|
60
|
+
"string",
|
|
61
|
+
"number"
|
|
62
|
+
],
|
|
63
|
+
"description": "Trusted platform user id"
|
|
64
|
+
},
|
|
65
|
+
"description": "本 endpoint 的 trusted 列表"
|
|
34
66
|
},
|
|
35
67
|
"url": {
|
|
36
68
|
"type": "string",
|
|
@@ -47,10 +79,14 @@
|
|
|
47
79
|
"access_token": {
|
|
48
80
|
"type": "string",
|
|
49
81
|
"description": "OneBot access token"
|
|
82
|
+
},
|
|
83
|
+
"id": {
|
|
84
|
+
"type": "string",
|
|
85
|
+
"description": "OneBot12 bot name"
|
|
50
86
|
}
|
|
51
87
|
},
|
|
52
88
|
"required": [
|
|
53
|
-
"
|
|
89
|
+
"id"
|
|
54
90
|
]
|
|
55
91
|
}
|
|
56
92
|
},
|
package/src/index.ts
CHANGED
|
@@ -3,14 +3,13 @@ export {
|
|
|
3
3
|
buildWsConnectOptions,
|
|
4
4
|
callOneBot12Action,
|
|
5
5
|
formatInboundContent,
|
|
6
|
-
formatInboundTarget,
|
|
7
6
|
formatOutboundSegments,
|
|
8
7
|
getChannelId,
|
|
9
8
|
isBotMentioned,
|
|
10
9
|
isMessageEvent,
|
|
11
10
|
mediaRefToOneBot12Fields,
|
|
12
11
|
mediaRefToOneBot12UploadParams,
|
|
13
|
-
|
|
12
|
+
onebot12InboundConversation,
|
|
14
13
|
resolveOneBot12Config,
|
|
15
14
|
senderNickname,
|
|
16
15
|
senderUserId,
|
|
@@ -29,7 +28,6 @@ export {
|
|
|
29
28
|
type OneBot12WireSegment,
|
|
30
29
|
type OneBot12WsConfig,
|
|
31
30
|
type OneBot12WssConfig,
|
|
32
|
-
type ParsedSendTarget,
|
|
33
31
|
type ResolvedOneBot12Config,
|
|
34
32
|
} from './protocol.js';
|
|
35
33
|
|
package/src/protocol.ts
CHANGED
|
@@ -4,6 +4,7 @@
|
|
|
4
4
|
* Spec: https://12.onebot.dev/
|
|
5
5
|
*/
|
|
6
6
|
import { isMediaRef, type MediaRef } from '@zhin.js/core';
|
|
7
|
+
import type { ConversationRef } from '@zhin.js/im-contract';
|
|
7
8
|
import { formatCompact, getLogger } from '@zhin.js/logger';
|
|
8
9
|
|
|
9
10
|
const logger = getLogger('onebot12');
|
|
@@ -12,7 +13,7 @@ const logger = getLogger('onebot12');
|
|
|
12
13
|
export interface OneBot12LegacyEndpointRow {
|
|
13
14
|
readonly context?: string;
|
|
14
15
|
readonly connection?: 'ws' | 'webhook' | 'wss';
|
|
15
|
-
readonly
|
|
16
|
+
readonly id?: string;
|
|
16
17
|
readonly access_token?: string;
|
|
17
18
|
readonly url?: string;
|
|
18
19
|
readonly path?: string;
|
|
@@ -24,7 +25,7 @@ export interface OneBot12LegacyEndpointRow {
|
|
|
24
25
|
/** Plugin Runtime owner config (`plugins.<instanceKey>` / schema.json). */
|
|
25
26
|
export interface OneBot12AdapterConfig {
|
|
26
27
|
readonly connection?: 'ws' | 'webhook' | 'wss';
|
|
27
|
-
readonly
|
|
28
|
+
readonly id?: string;
|
|
28
29
|
readonly access_token?: string;
|
|
29
30
|
readonly url?: string;
|
|
30
31
|
readonly path?: string;
|
|
@@ -37,7 +38,7 @@ export interface OneBot12AdapterConfig {
|
|
|
37
38
|
|
|
38
39
|
export interface OneBot12ConfigBase {
|
|
39
40
|
readonly context: 'onebot12';
|
|
40
|
-
readonly
|
|
41
|
+
readonly id: string;
|
|
41
42
|
readonly access_token?: string;
|
|
42
43
|
}
|
|
43
44
|
|
|
@@ -118,19 +119,13 @@ export interface OneBot12WireSegment {
|
|
|
118
119
|
readonly data?: Record<string, unknown>;
|
|
119
120
|
}
|
|
120
121
|
|
|
121
|
-
export interface ParsedSendTarget {
|
|
122
|
-
readonly detail_type: 'private' | 'group' | 'channel';
|
|
123
|
-
readonly id: string;
|
|
124
|
-
readonly guild_id?: string;
|
|
125
|
-
}
|
|
126
|
-
|
|
127
122
|
export function resolveOneBot12Config(config: OneBot12AdapterConfig = {}): ResolvedOneBot12Config {
|
|
128
123
|
const entry = config.endpoints?.find((item) => item.context === 'onebot12');
|
|
129
124
|
const connection = config.connection
|
|
130
125
|
?? entry?.connection
|
|
131
126
|
?? 'ws';
|
|
132
|
-
const
|
|
133
|
-
|| (typeof entry?.
|
|
127
|
+
const id = (typeof config.id === 'string' && config.id)
|
|
128
|
+
|| (typeof entry?.id === 'string' && entry.id)
|
|
134
129
|
|| process.env.ONEBOT12_BOT_NAME
|
|
135
130
|
|| 'onebot12-bot';
|
|
136
131
|
const access_token = config.access_token ?? entry?.access_token;
|
|
@@ -145,7 +140,7 @@ export function resolveOneBot12Config(config: OneBot12AdapterConfig = {}): Resol
|
|
|
145
140
|
return {
|
|
146
141
|
context: 'onebot12',
|
|
147
142
|
connection: 'ws',
|
|
148
|
-
|
|
143
|
+
id,
|
|
149
144
|
access_token,
|
|
150
145
|
url,
|
|
151
146
|
reconnect_interval: config.reconnect_interval ?? entry?.reconnect_interval ?? 5000,
|
|
@@ -161,7 +156,7 @@ export function resolveOneBot12Config(config: OneBot12AdapterConfig = {}): Resol
|
|
|
161
156
|
return {
|
|
162
157
|
context: 'onebot12',
|
|
163
158
|
connection: 'webhook',
|
|
164
|
-
|
|
159
|
+
id,
|
|
165
160
|
access_token,
|
|
166
161
|
path,
|
|
167
162
|
api_url: config.api_url ?? entry?.api_url,
|
|
@@ -176,7 +171,7 @@ export function resolveOneBot12Config(config: OneBot12AdapterConfig = {}): Resol
|
|
|
176
171
|
return {
|
|
177
172
|
context: 'onebot12',
|
|
178
173
|
connection: 'wss',
|
|
179
|
-
|
|
174
|
+
id,
|
|
180
175
|
access_token,
|
|
181
176
|
path,
|
|
182
177
|
heartbeat_interval: config.heartbeat_interval ?? entry?.heartbeat_interval ?? 30_000,
|
|
@@ -204,37 +199,31 @@ export function getChannelId(ev: OneBot12Event): string {
|
|
|
204
199
|
}
|
|
205
200
|
|
|
206
201
|
/**
|
|
207
|
-
*
|
|
202
|
+
* 入站归一化 → ConversationRef:`detail_type` 直映射 kind;channel 的 guild 容器
|
|
203
|
+
* 进 `parent`(kind 'channel');私聊临时会话(private 事件携带 group_id)映射为
|
|
204
|
+
* group 容器内的 private 会话。
|
|
208
205
|
*/
|
|
209
|
-
export function
|
|
210
|
-
const
|
|
211
|
-
|
|
212
|
-
: '
|
|
213
|
-
return `${detail}:${getChannelId(ev)}`;
|
|
214
|
-
}
|
|
215
|
-
|
|
216
|
-
export function parseSendTarget(target: string): ParsedSendTarget {
|
|
217
|
-
const sep = target.indexOf(':');
|
|
218
|
-
if (sep <= 0) {
|
|
219
|
-
return { detail_type: 'private', id: target };
|
|
220
|
-
}
|
|
221
|
-
const head = target.slice(0, sep);
|
|
222
|
-
const rest = target.slice(sep + 1);
|
|
223
|
-
if (head === 'private' || head === 'group') {
|
|
224
|
-
return { detail_type: head, id: rest };
|
|
206
|
+
export function onebot12InboundConversation(endpointKey: string, ev: OneBot12Event): ConversationRef {
|
|
207
|
+
const endpoint = { id: endpointKey, adapter: endpointKey.split('\0')[0] ?? endpointKey };
|
|
208
|
+
if (ev.detail_type === 'group' && ev.group_id) {
|
|
209
|
+
return { endpoint, kind: 'group', id: ev.group_id };
|
|
225
210
|
}
|
|
226
|
-
if (
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
};
|
|
234
|
-
}
|
|
235
|
-
return { detail_type: 'channel', id: rest };
|
|
211
|
+
if (ev.detail_type === 'channel' && ev.channel_id) {
|
|
212
|
+
return {
|
|
213
|
+
endpoint,
|
|
214
|
+
kind: 'channel',
|
|
215
|
+
id: ev.channel_id,
|
|
216
|
+
...(ev.guild_id ? { parent: { kind: 'channel' as const, id: ev.guild_id } } : {}),
|
|
217
|
+
};
|
|
236
218
|
}
|
|
237
|
-
return {
|
|
219
|
+
return {
|
|
220
|
+
endpoint,
|
|
221
|
+
kind: 'private',
|
|
222
|
+
id: ev.user_id ?? ev.group_id ?? '',
|
|
223
|
+
...(ev.detail_type === 'private' && ev.group_id
|
|
224
|
+
? { parent: { kind: 'group' as const, id: ev.group_id } }
|
|
225
|
+
: {}),
|
|
226
|
+
};
|
|
238
227
|
}
|
|
239
228
|
|
|
240
229
|
/** Build inbound text for MessageGateway.receive */
|
|
@@ -501,22 +490,25 @@ export async function uploadOneBot12MediaSegments(
|
|
|
501
490
|
return uploadOneMediaSegment(payload, callAction, onUploadFailed);
|
|
502
491
|
}
|
|
503
492
|
|
|
493
|
+
/**
|
|
494
|
+
* 结构化会话 → OB12 `send_message` 动作参数:kind 直映射 detail_type;
|
|
495
|
+
* channel 的 guild 容器取自 `conversation.parent`(kind 'channel')。
|
|
496
|
+
*/
|
|
504
497
|
export function buildSendMessageParams(
|
|
505
|
-
|
|
498
|
+
conversation: ConversationRef,
|
|
506
499
|
message: OneBot12Segment[],
|
|
507
500
|
): Record<string, unknown> {
|
|
508
|
-
const parsed = parseSendTarget(target);
|
|
509
501
|
const params: Record<string, unknown> = {
|
|
510
502
|
message,
|
|
511
|
-
detail_type:
|
|
503
|
+
detail_type: conversation.kind,
|
|
512
504
|
};
|
|
513
|
-
if (
|
|
514
|
-
params.user_id =
|
|
515
|
-
} else if (
|
|
516
|
-
params.group_id =
|
|
505
|
+
if (conversation.kind === 'private') {
|
|
506
|
+
params.user_id = conversation.id;
|
|
507
|
+
} else if (conversation.kind === 'group') {
|
|
508
|
+
params.group_id = conversation.id;
|
|
517
509
|
} else {
|
|
518
|
-
params.channel_id =
|
|
519
|
-
if (
|
|
510
|
+
params.channel_id = conversation.id;
|
|
511
|
+
if (conversation.parent?.kind === 'channel') params.guild_id = conversation.parent.id;
|
|
520
512
|
}
|
|
521
513
|
return params;
|
|
522
514
|
}
|
package/src/webhook.ts
CHANGED
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
* OneBot12 HTTP webhook endpoint — POST inbound + api_url outbound.
|
|
3
3
|
*/
|
|
4
4
|
import type { IncomingMessage, ServerResponse } from 'node:http';
|
|
5
|
-
import type { EndpointInstance, EndpointManagement } from '@zhin.js/adapter';
|
|
5
|
+
import type { EndpointInstance, EndpointManagement, EndpointSendRequest } from '@zhin.js/adapter';
|
|
6
6
|
import type { MessageGateway } from '@zhin.js/core/runtime';
|
|
7
7
|
import type { HttpHost, HttpRouteRegistration } from '@zhin.js/host-http';
|
|
8
8
|
import { formatCompact, getLogger } from '@zhin.js/logger';
|
|
@@ -12,10 +12,10 @@ import {
|
|
|
12
12
|
buildSendMessageParams,
|
|
13
13
|
callOneBot12Action,
|
|
14
14
|
formatInboundContent,
|
|
15
|
-
formatInboundTarget,
|
|
16
15
|
formatOutboundSegments,
|
|
17
16
|
isBotMentioned,
|
|
18
17
|
isMessageEvent,
|
|
18
|
+
onebot12InboundConversation,
|
|
19
19
|
senderNickname,
|
|
20
20
|
senderUserId,
|
|
21
21
|
uploadOneBot12MediaSegments,
|
|
@@ -53,7 +53,7 @@ export class OneBot12WebhookEndpoint implements EndpointInstance {
|
|
|
53
53
|
if (!this.#options.config.access_token) {
|
|
54
54
|
// webhook 模式未配 access_token 时任何 POST 都会被放行(verifyOneBotAccessToken 直接 return true)
|
|
55
55
|
logger.warn(formatCompact({
|
|
56
|
-
endpoint: this.#options.config.
|
|
56
|
+
endpoint: this.#options.config.id,
|
|
57
57
|
mode: 'webhook',
|
|
58
58
|
ok: false,
|
|
59
59
|
error: 'missing access_token',
|
|
@@ -62,7 +62,7 @@ export class OneBot12WebhookEndpoint implements EndpointInstance {
|
|
|
62
62
|
this.#setupRoutes();
|
|
63
63
|
logger.info(formatCompact({
|
|
64
64
|
op: 'listen',
|
|
65
|
-
endpoint: this.#options.config.
|
|
65
|
+
endpoint: this.#options.config.id,
|
|
66
66
|
mode: 'webhook',
|
|
67
67
|
path: this.#options.config.path,
|
|
68
68
|
}));
|
|
@@ -80,29 +80,30 @@ export class OneBot12WebhookEndpoint implements EndpointInstance {
|
|
|
80
80
|
this.#open = false;
|
|
81
81
|
for (const release of this.#routeReleases.splice(0)) release();
|
|
82
82
|
this.#started = false;
|
|
83
|
-
logger.debug(formatCompact({ op: 'disconnect', endpoint: this.#options.config.
|
|
83
|
+
logger.debug(formatCompact({ op: 'disconnect', endpoint: this.#options.config.id }));
|
|
84
84
|
}
|
|
85
85
|
|
|
86
|
-
async send({
|
|
86
|
+
async send({ conversation, payload }: EndpointSendRequest): Promise<string> {
|
|
87
87
|
const materialized = await uploadOneBot12MediaSegments(
|
|
88
88
|
payload,
|
|
89
89
|
(action, params) => this.callApi(action, params),
|
|
90
90
|
(error) => {
|
|
91
91
|
logger.warn(formatCompact({
|
|
92
92
|
op: 'onebot12_upload_failed',
|
|
93
|
-
endpoint: this.#options.config.
|
|
93
|
+
endpoint: this.#options.config.id,
|
|
94
94
|
error: error instanceof Error ? error.message : String(error),
|
|
95
95
|
}));
|
|
96
96
|
},
|
|
97
97
|
);
|
|
98
98
|
const message = formatOutboundSegments(materialized);
|
|
99
|
-
const params = buildSendMessageParams(
|
|
99
|
+
const params = buildSendMessageParams(conversation, message);
|
|
100
100
|
const data = await this.callApi('send_message', params) as { message_id?: string } | undefined;
|
|
101
101
|
const messageId = data?.message_id ?? '';
|
|
102
102
|
logger.debug(formatCompact({
|
|
103
103
|
op: 'onebot12_send',
|
|
104
|
-
endpoint: this.#options.config.
|
|
105
|
-
|
|
104
|
+
endpoint: this.#options.config.id,
|
|
105
|
+
kind: conversation.kind,
|
|
106
|
+
conversationId: conversation.id,
|
|
106
107
|
messageId,
|
|
107
108
|
}));
|
|
108
109
|
return messageId;
|
|
@@ -124,31 +125,31 @@ export class OneBot12WebhookEndpoint implements EndpointInstance {
|
|
|
124
125
|
|
|
125
126
|
admit(ev: OneBot12Event): void {
|
|
126
127
|
if (!this.#open || !isMessageEvent(ev)) return;
|
|
127
|
-
const
|
|
128
|
+
const conversation = onebot12InboundConversation(String(this.#options.id), ev);
|
|
128
129
|
const content = formatInboundContent(ev);
|
|
129
130
|
const nickname = senderNickname(ev);
|
|
130
131
|
const mentioned = isBotMentioned(ev);
|
|
131
132
|
void this.#options.gateway.receive({
|
|
132
|
-
|
|
133
|
-
|
|
133
|
+
conversation,
|
|
134
|
+
message: { conversation, id: ev.message_id },
|
|
134
135
|
content,
|
|
135
|
-
sender: senderUserId(ev),
|
|
136
|
-
|
|
136
|
+
sender: { id: senderUserId(ev), ...(nickname ? { name: nickname } : {}) },
|
|
137
|
+
endpointId: this.#options.config.id,
|
|
138
|
+
...(mentioned ? { mentioned: true } : {}),
|
|
137
139
|
metadata: Object.freeze({
|
|
138
140
|
detail_type: ev.detail_type,
|
|
139
141
|
user_id: ev.user_id,
|
|
140
142
|
group_id: ev.group_id,
|
|
141
143
|
channel_id: ev.channel_id,
|
|
142
144
|
guild_id: ev.guild_id,
|
|
143
|
-
endpoint: this.#options.config.name,
|
|
144
145
|
time: ev.time,
|
|
145
146
|
...(nickname ? { nickname } : {}),
|
|
146
|
-
...(mentioned ? { mentioned: true } : {}),
|
|
147
147
|
}),
|
|
148
148
|
}).catch((err) => {
|
|
149
149
|
logger.warn(formatCompact({
|
|
150
150
|
op: 'onebot12_gateway_receive_failed',
|
|
151
|
-
|
|
151
|
+
kind: conversation.kind,
|
|
152
|
+
conversationId: conversation.id,
|
|
152
153
|
error: err instanceof Error ? err.message : String(err),
|
|
153
154
|
}));
|
|
154
155
|
});
|