@zhin.js/adapter-onebot11 6.0.13 → 6.0.14
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 +34 -0
- package/README.md +1 -1
- package/adapters/onebot11.js +4 -1
- package/adapters/onebot11.ts +10 -1
- package/lib/content-port.d.ts +4 -0
- package/lib/content-port.js +63 -0
- package/lib/protocol.js +3 -3
- package/lib/side-event-dispatch.d.ts +7 -0
- package/lib/side-event-dispatch.js +36 -0
- package/lib/ws-endpoint.d.ts +3 -1
- package/lib/ws-endpoint.js +8 -1
- package/lib/wss-endpoint.d.ts +3 -1
- package/lib/wss-endpoint.js +8 -1
- package/package.json +14 -14
- package/src/content-port.ts +66 -0
- package/src/protocol.ts +3 -2
- package/src/side-event-dispatch.ts +48 -0
- package/src/ws-endpoint.ts +16 -2
- package/src/wss-endpoint.ts +16 -2
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,39 @@
|
|
|
1
1
|
# @zhin.js/adapter-onebot11
|
|
2
2
|
|
|
3
|
+
## 6.0.14
|
|
4
|
+
|
|
5
|
+
### Patch Changes
|
|
6
|
+
|
|
7
|
+
- ffe7463: Add generation-bound message and merged-forward resolution through the canonical endpoint content port.
|
|
8
|
+
- 5969c5b: Add SideEventGateway so adapters forward notice/request/system into HandlerIndex. HandlerContext now exposes only generation-safe capabilities and prompt ports; live Endpoint escape hatches are removed.
|
|
9
|
+
- Updated dependencies [5969c5b]
|
|
10
|
+
- Updated dependencies [d336a3f]
|
|
11
|
+
- Updated dependencies [0c82a7e]
|
|
12
|
+
- Updated dependencies [b9217e4]
|
|
13
|
+
- Updated dependencies [5969c5b]
|
|
14
|
+
- Updated dependencies [5969c5b]
|
|
15
|
+
- Updated dependencies [974772e]
|
|
16
|
+
- Updated dependencies [5969c5b]
|
|
17
|
+
- Updated dependencies [5969c5b]
|
|
18
|
+
- Updated dependencies [2f786bd]
|
|
19
|
+
- Updated dependencies [63d89f9]
|
|
20
|
+
- Updated dependencies [71c7cdd]
|
|
21
|
+
- Updated dependencies [3cca0ea]
|
|
22
|
+
- Updated dependencies [1312ca0]
|
|
23
|
+
- Updated dependencies [985fa22]
|
|
24
|
+
- Updated dependencies [04b861d]
|
|
25
|
+
- Updated dependencies [a23d544]
|
|
26
|
+
- Updated dependencies [8cddabf]
|
|
27
|
+
- Updated dependencies [dbe5081]
|
|
28
|
+
- @zhin.js/im-contract@1.0.4
|
|
29
|
+
- @zhin.js/core@1.5.12
|
|
30
|
+
- @zhin.js/adapter@1.1.11
|
|
31
|
+
- @zhin.js/agent@1.1.14
|
|
32
|
+
- @zhin.js/host-http@1.0.11
|
|
33
|
+
- @zhin.js/command@1.0.15
|
|
34
|
+
- zhin.js@6.0.12
|
|
35
|
+
- @zhin.js/permission@1.0.3
|
|
36
|
+
|
|
3
37
|
## 6.0.13
|
|
4
38
|
|
|
5
39
|
### Patch Changes
|
package/README.md
CHANGED
|
@@ -70,7 +70,7 @@ plugins:
|
|
|
70
70
|
|
|
71
71
|
## 迁移说明(Plugin Runtime)
|
|
72
72
|
|
|
73
|
-
- **notice / request
|
|
73
|
+
- **notice / request / meta 侧事件**:经 `sideEventGatewayToken` 归一后分发到 `handlers`;消息仍走 `messageGatewayToken`。
|
|
74
74
|
- **群管工具暂未迁移**:旧 Adapter 经 `createSceneManagementTools` 注册踢人 / 禁言 / 群名片等成套 agent 工具;迁移后仅保留 `onebot11_set_title`,其余群管能力可通过 `callApi`(如 `set_group_kick`、`set_group_ban`)作为逃生舱调用。
|
|
75
75
|
- **平台权限门禁**:`plugin.ts` setup 已注册 `registerDefaultScenePlatformPermitChecker('onebot11')`,`scene_admin` / `scene_owner` 依据入站 metadata 中的 sender `role`(owner / admin)判定。
|
|
76
76
|
|
package/adapters/onebot11.js
CHANGED
|
@@ -3,7 +3,7 @@
|
|
|
3
3
|
* Convention entry: discover `adapters/onebot11.ts` → defineAdapter.
|
|
4
4
|
*/
|
|
5
5
|
import { defineAdapter } from 'zhin.js/adapter';
|
|
6
|
-
import { messageGatewayToken } from '@zhin.js/core/runtime';
|
|
6
|
+
import { messageGatewayToken, sideEventGatewayToken } from '@zhin.js/core/runtime';
|
|
7
7
|
import { httpHostToken } from '@zhin.js/host-http';
|
|
8
8
|
import { OneBot11WsEndpoint } from "../lib/ws-endpoint.js";
|
|
9
9
|
import { OneBot11WssEndpoint } from "../lib/wss-endpoint.js";
|
|
@@ -21,6 +21,7 @@ export default defineAdapter({
|
|
|
21
21
|
create(context) {
|
|
22
22
|
const config = resolveOneBot11Config(context.config);
|
|
23
23
|
const gateway = context.use(messageGatewayToken);
|
|
24
|
+
const sideEvents = context.use(sideEventGatewayToken);
|
|
24
25
|
// 注册到插件运行时状态(onebot11.endpoint list 的"运行中"数据源)
|
|
25
26
|
context.use(onebot11RuntimeStateToken).endpoints.set(config.id, {
|
|
26
27
|
id: config.id,
|
|
@@ -30,6 +31,7 @@ export default defineAdapter({
|
|
|
30
31
|
return new OneBot11WssEndpoint({
|
|
31
32
|
id: context.id,
|
|
32
33
|
gateway,
|
|
34
|
+
sideEvents,
|
|
33
35
|
http: context.use(httpHostToken),
|
|
34
36
|
config,
|
|
35
37
|
});
|
|
@@ -37,6 +39,7 @@ export default defineAdapter({
|
|
|
37
39
|
return new OneBot11WsEndpoint({
|
|
38
40
|
id: context.id,
|
|
39
41
|
gateway,
|
|
42
|
+
sideEvents,
|
|
40
43
|
config,
|
|
41
44
|
});
|
|
42
45
|
},
|
package/adapters/onebot11.ts
CHANGED
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
* Convention entry: discover `adapters/onebot11.ts` → defineAdapter.
|
|
3
3
|
*/
|
|
4
4
|
import { defineAdapter } from 'zhin.js/adapter';
|
|
5
|
-
import { messageGatewayToken } from '@zhin.js/core/runtime';
|
|
5
|
+
import { messageGatewayToken, sideEventGatewayToken } from '@zhin.js/core/runtime';
|
|
6
6
|
import { httpHostToken } from '@zhin.js/host-http';
|
|
7
7
|
import { OneBot11WsEndpoint } from '../src/ws-endpoint.js';
|
|
8
8
|
import { OneBot11WssEndpoint } from '../src/wss-endpoint.js';
|
|
@@ -18,6 +18,12 @@ export { OneBot11WssEndpoint } from '../src/wss-endpoint.js';
|
|
|
18
18
|
export type { OneBot11WssEndpointOptions } from '../src/wss-endpoint.js';
|
|
19
19
|
export type { OneBot11WsSocket, OneBot11WsCreateOptions } from '../src/ws-types.js';
|
|
20
20
|
|
|
21
|
+
declare module '@zhin.js/core' {
|
|
22
|
+
interface AdapterEndpoints {
|
|
23
|
+
onebot11: import('../src/ws-endpoint.js').OneBot11WsEndpoint;
|
|
24
|
+
}
|
|
25
|
+
}
|
|
26
|
+
|
|
21
27
|
export default defineAdapter<OneBot11AdapterConfig>({
|
|
22
28
|
capabilities: ['inbound', 'outbound'],
|
|
23
29
|
// OneBot file 参数原生消费 url / base64:// 媒体;无卡片交互面,交互段降级纯文本。
|
|
@@ -28,6 +34,7 @@ export default defineAdapter<OneBot11AdapterConfig>({
|
|
|
28
34
|
create(context) {
|
|
29
35
|
const config = resolveOneBot11Config(context.config);
|
|
30
36
|
const gateway = context.use(messageGatewayToken);
|
|
37
|
+
const sideEvents = context.use(sideEventGatewayToken);
|
|
31
38
|
// 注册到插件运行时状态(onebot11.endpoint list 的"运行中"数据源)
|
|
32
39
|
context.use(onebot11RuntimeStateToken).endpoints.set(config.id, {
|
|
33
40
|
id: config.id,
|
|
@@ -37,6 +44,7 @@ export default defineAdapter<OneBot11AdapterConfig>({
|
|
|
37
44
|
return new OneBot11WssEndpoint({
|
|
38
45
|
id: context.id,
|
|
39
46
|
gateway,
|
|
47
|
+
sideEvents,
|
|
40
48
|
http: context.use(httpHostToken),
|
|
41
49
|
config,
|
|
42
50
|
});
|
|
@@ -44,6 +52,7 @@ export default defineAdapter<OneBot11AdapterConfig>({
|
|
|
44
52
|
return new OneBot11WsEndpoint({
|
|
45
53
|
id: context.id,
|
|
46
54
|
gateway,
|
|
55
|
+
sideEvents,
|
|
47
56
|
config,
|
|
48
57
|
});
|
|
49
58
|
},
|
|
@@ -0,0 +1,63 @@
|
|
|
1
|
+
import { toCanonicalSegments } from '@zhin.js/core';
|
|
2
|
+
export function createOneBot11ContentPort(callApi) {
|
|
3
|
+
return Object.freeze({
|
|
4
|
+
async resolve(reference, context) {
|
|
5
|
+
try {
|
|
6
|
+
context.signal.throwIfAborted();
|
|
7
|
+
if (reference.kind === 'media') {
|
|
8
|
+
return reference.media.kind === 'file'
|
|
9
|
+
? Object.freeze({ status: 'unsupported', code: 'onebot11_opaque_media' })
|
|
10
|
+
: Object.freeze({ status: 'resolved', reference, value: reference.media });
|
|
11
|
+
}
|
|
12
|
+
if (reference.kind === 'message') {
|
|
13
|
+
const row = await callApi('get_msg', { message_id: reference.message.id });
|
|
14
|
+
context.signal.throwIfAborted();
|
|
15
|
+
const segments = normalizeSegments(row.message ?? row.raw_message);
|
|
16
|
+
const sender = row.sender;
|
|
17
|
+
const value = Object.freeze({
|
|
18
|
+
ref: reference.message,
|
|
19
|
+
...(sender?.user_id != null ? { actor: Object.freeze({
|
|
20
|
+
id: String(sender.user_id),
|
|
21
|
+
...(sender.nickname ? { displayName: String(sender.nickname) } : {}),
|
|
22
|
+
}) } : {}),
|
|
23
|
+
segments,
|
|
24
|
+
timestamp: toMillis(row.time),
|
|
25
|
+
});
|
|
26
|
+
return Object.freeze({ status: 'resolved', reference, value });
|
|
27
|
+
}
|
|
28
|
+
const raw = await callApi('get_forward_msg', { id: reference.forwardId });
|
|
29
|
+
context.signal.throwIfAborted();
|
|
30
|
+
const nodes = Array.isArray(raw.messages) ? raw.messages.slice(0, context.maxEntries) : [];
|
|
31
|
+
const value = Object.freeze(nodes.map((node) => {
|
|
32
|
+
const row = node;
|
|
33
|
+
const sender = row.sender;
|
|
34
|
+
return Object.freeze({
|
|
35
|
+
...(sender?.user_id != null ? { actor: Object.freeze({
|
|
36
|
+
id: String(sender.user_id),
|
|
37
|
+
...(sender.nickname ? { displayName: String(sender.nickname) } : {}),
|
|
38
|
+
}) } : {}),
|
|
39
|
+
timestamp: toMillis(row.time),
|
|
40
|
+
segments: normalizeSegments(row.content ?? row.message),
|
|
41
|
+
});
|
|
42
|
+
}));
|
|
43
|
+
return Object.freeze({ status: 'resolved', reference, value, truncated: nodes.length >= context.maxEntries });
|
|
44
|
+
}
|
|
45
|
+
catch (error) {
|
|
46
|
+
if (context.signal.aborted)
|
|
47
|
+
return Object.freeze({ status: 'expired', code: 'turn_aborted' });
|
|
48
|
+
return Object.freeze({ status: 'failed', code: 'onebot11_content_resolution_failed', message: error instanceof Error ? error.message : String(error) });
|
|
49
|
+
}
|
|
50
|
+
},
|
|
51
|
+
});
|
|
52
|
+
}
|
|
53
|
+
function normalizeSegments(value) {
|
|
54
|
+
if (Array.isArray(value))
|
|
55
|
+
return Object.freeze(toCanonicalSegments(value));
|
|
56
|
+
return Object.freeze([{ type: 'text', data: Object.freeze({ text: String(value ?? '') }) }]);
|
|
57
|
+
}
|
|
58
|
+
function toMillis(value) {
|
|
59
|
+
const time = Number(value);
|
|
60
|
+
if (!Number.isFinite(time))
|
|
61
|
+
return Date.now();
|
|
62
|
+
return time < 10_000_000_000 ? time * 1000 : time;
|
|
63
|
+
}
|
package/lib/protocol.js
CHANGED
|
@@ -85,14 +85,14 @@ export function onebot11InboundConversation(endpointKey, ev) {
|
|
|
85
85
|
}
|
|
86
86
|
/** Build inbound text for MessageGateway.receive */
|
|
87
87
|
export function formatInboundContent(ev) {
|
|
88
|
-
if (typeof ev.raw_message === 'string' && ev.raw_message)
|
|
89
|
-
return ev.raw_message;
|
|
90
88
|
if (Array.isArray(ev.message)) {
|
|
91
89
|
return ev.message
|
|
92
90
|
.map((seg) => (seg.type === 'text' ? String(seg.data?.text ?? '') : ''))
|
|
93
91
|
.join('');
|
|
94
92
|
}
|
|
95
|
-
return ''
|
|
93
|
+
return typeof ev.raw_message === 'string'
|
|
94
|
+
? ev.raw_message.replace(/\[CQ:[^\]]+\]/g, '').trim()
|
|
95
|
+
: '';
|
|
96
96
|
}
|
|
97
97
|
export function senderDisplayName(ev) {
|
|
98
98
|
const name = ev.sender?.card || ev.sender?.nickname;
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
import type { SideEventGateway } from '@zhin.js/core/runtime';
|
|
2
|
+
import { type getAdapterLogger } from '@zhin.js/logger';
|
|
3
|
+
import type { OneBot11Event } from './protocol.js';
|
|
4
|
+
export interface OneBot11SideEventCaller {
|
|
5
|
+
callApi(action: string, params?: Record<string, unknown>): Promise<unknown>;
|
|
6
|
+
}
|
|
7
|
+
export declare function receiveOneBot11SideEvent(sideEvents: SideEventGateway | undefined, endpointKey: string, caller: OneBot11SideEventCaller, raw: OneBot11Event, logger: ReturnType<typeof getAdapterLogger>): void;
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
import { receiveOneBotLikeSideEvent } from '@zhin.js/core';
|
|
2
|
+
import { formatCompact } from '@zhin.js/logger';
|
|
3
|
+
export function receiveOneBot11SideEvent(sideEvents, endpointKey, caller, raw, logger) {
|
|
4
|
+
if (!sideEvents)
|
|
5
|
+
return;
|
|
6
|
+
const record = raw;
|
|
7
|
+
const postType = String(record.post_type ?? '');
|
|
8
|
+
const requestType = String(record.request_type ?? '');
|
|
9
|
+
const isRequest = postType === 'request' || postType.startsWith('request.');
|
|
10
|
+
const isFriend = requestType === 'friend' || postType.includes('friend');
|
|
11
|
+
void receiveOneBotLikeSideEvent(sideEvents, {
|
|
12
|
+
adapter: 'onebot11',
|
|
13
|
+
endpointKey,
|
|
14
|
+
platform: 'onebot',
|
|
15
|
+
raw: record,
|
|
16
|
+
...(isRequest ? {
|
|
17
|
+
approve: async (flag, remark) => {
|
|
18
|
+
await (isFriend
|
|
19
|
+
? caller.callApi('set_friend_add_request', { flag, approve: true, remark })
|
|
20
|
+
: caller.callApi('set_group_add_request', { flag, approve: true, reason: remark }));
|
|
21
|
+
},
|
|
22
|
+
reject: async (flag, reason) => {
|
|
23
|
+
await (isFriend
|
|
24
|
+
? caller.callApi('set_friend_add_request', { flag, approve: false })
|
|
25
|
+
: caller.callApi('set_group_add_request', { flag, approve: false, reason }));
|
|
26
|
+
},
|
|
27
|
+
} : {}),
|
|
28
|
+
}).catch((err) => {
|
|
29
|
+
logger.warn(formatCompact({
|
|
30
|
+
op: 'onebot11_side_event_failed',
|
|
31
|
+
endpoint: endpointKey,
|
|
32
|
+
post_type: postType || undefined,
|
|
33
|
+
error: err instanceof Error ? err.message : String(err),
|
|
34
|
+
}));
|
|
35
|
+
});
|
|
36
|
+
}
|
package/lib/ws-endpoint.d.ts
CHANGED
|
@@ -1,17 +1,19 @@
|
|
|
1
1
|
import { type EndpointInstance, type EndpointManagement, type EndpointSendRequest } from 'zhin.js/adapter';
|
|
2
|
-
import type { MessageGateway } from '@zhin.js/core/runtime';
|
|
2
|
+
import type { MessageGateway, SideEventGateway } from '@zhin.js/core/runtime';
|
|
3
3
|
import type { CapabilityId } from 'zhin.js';
|
|
4
4
|
import { type OneBot11Event, type OneBot11WsConfig } from './protocol.js';
|
|
5
5
|
import { type OneBot11WsCreateOptions, type OneBot11WsSocket } from './ws-types.js';
|
|
6
6
|
export interface OneBot11WsEndpointOptions {
|
|
7
7
|
readonly id: CapabilityId;
|
|
8
8
|
readonly gateway: MessageGateway;
|
|
9
|
+
readonly sideEvents?: SideEventGateway;
|
|
9
10
|
readonly config: OneBot11WsConfig;
|
|
10
11
|
readonly createWebSocket?: (url: string, options: OneBot11WsCreateOptions) => OneBot11WsSocket;
|
|
11
12
|
}
|
|
12
13
|
export declare class OneBot11WsEndpoint implements EndpointInstance {
|
|
13
14
|
#private;
|
|
14
15
|
readonly management: EndpointManagement;
|
|
16
|
+
readonly content: import("@zhin.js/adapter").EndpointContentPort;
|
|
15
17
|
constructor(options: OneBot11WsEndpointOptions);
|
|
16
18
|
start(): Promise<void>;
|
|
17
19
|
open(): void;
|
package/lib/ws-endpoint.js
CHANGED
|
@@ -7,11 +7,14 @@ import { formatCompact, getAdapterLogger } from '@zhin.js/logger';
|
|
|
7
7
|
import { createOneBot11EndpointManagement } from './endpoint-management.js';
|
|
8
8
|
import { registerOnebot11AgentEndpoint } from './onebot11-agent-deps.js';
|
|
9
9
|
import { buildSendAction, buildWsConnectOptions, formatInboundContent, formatInboundMetadata, formatOutboundSegments, isMessageEvent, onebot11InboundConversation, senderDisplayName, senderUserId, } from './protocol.js';
|
|
10
|
+
import { receiveOneBot11SideEvent } from './side-event-dispatch.js';
|
|
11
|
+
import { createOneBot11ContentPort } from './content-port.js';
|
|
10
12
|
import { callOneBot11WsAction, handleOneBot11WsMessage, rejectAllPending, } from './ws-transport.js';
|
|
11
13
|
export class OneBot11WsEndpoint {
|
|
12
14
|
#logger;
|
|
13
15
|
#options;
|
|
14
16
|
management = createOneBot11EndpointManagement(this);
|
|
17
|
+
content = createOneBot11ContentPort((action, params) => this.callApi(action, params));
|
|
15
18
|
#lifecycle;
|
|
16
19
|
#ws;
|
|
17
20
|
#requestId = { value: 0 };
|
|
@@ -106,8 +109,12 @@ export class OneBot11WsEndpoint {
|
|
|
106
109
|
}
|
|
107
110
|
/** Test / internal: admit a parsed event when the endpoint is open. */
|
|
108
111
|
admit(ev) {
|
|
109
|
-
if (!this.#open
|
|
112
|
+
if (!this.#open)
|
|
110
113
|
return;
|
|
114
|
+
if (!isMessageEvent(ev)) {
|
|
115
|
+
receiveOneBot11SideEvent(this.#options.sideEvents, this.#options.config.id, this, ev, this.#logger);
|
|
116
|
+
return;
|
|
117
|
+
}
|
|
111
118
|
const conversation = onebot11InboundConversation(String(this.#options.id), ev);
|
|
112
119
|
const content = formatInboundContent(ev);
|
|
113
120
|
const inbound = formatInboundMetadata(ev, this.#options.config.id);
|
package/lib/wss-endpoint.d.ts
CHANGED
|
@@ -1,17 +1,19 @@
|
|
|
1
1
|
import type { EndpointInstance, EndpointManagement, EndpointSendRequest } from 'zhin.js/adapter';
|
|
2
|
-
import type { MessageGateway } from '@zhin.js/core/runtime';
|
|
2
|
+
import type { MessageGateway, SideEventGateway } from '@zhin.js/core/runtime';
|
|
3
3
|
import type { HttpHost } from '@zhin.js/host-http';
|
|
4
4
|
import type { CapabilityId } from 'zhin.js';
|
|
5
5
|
import { type OneBot11Event, type OneBot11WssConfig } from './protocol.js';
|
|
6
6
|
export interface OneBot11WssEndpointOptions {
|
|
7
7
|
readonly id: CapabilityId;
|
|
8
8
|
readonly gateway: MessageGateway;
|
|
9
|
+
readonly sideEvents?: SideEventGateway;
|
|
9
10
|
readonly http: HttpHost;
|
|
10
11
|
readonly config: OneBot11WssConfig;
|
|
11
12
|
}
|
|
12
13
|
export declare class OneBot11WssEndpoint implements EndpointInstance {
|
|
13
14
|
#private;
|
|
14
15
|
readonly management: EndpointManagement;
|
|
16
|
+
readonly content: import("@zhin.js/adapter").EndpointContentPort;
|
|
15
17
|
constructor(options: OneBot11WssEndpointOptions);
|
|
16
18
|
start(): Promise<void>;
|
|
17
19
|
open(): void;
|
package/lib/wss-endpoint.js
CHANGED
|
@@ -6,12 +6,15 @@ import { formatCompact, getAdapterLogger } from '@zhin.js/logger';
|
|
|
6
6
|
import { createOneBot11EndpointManagement } from './endpoint-management.js';
|
|
7
7
|
import { registerOnebot11AgentEndpoint } from './onebot11-agent-deps.js';
|
|
8
8
|
import { buildSendAction, formatInboundContent, formatInboundMetadata, formatOutboundSegments, isMessageEvent, onebot11InboundConversation, senderDisplayName, senderUserId, } from './protocol.js';
|
|
9
|
+
import { receiveOneBot11SideEvent } from './side-event-dispatch.js';
|
|
10
|
+
import { createOneBot11ContentPort } from './content-port.js';
|
|
9
11
|
import { verifyOneBotAccessToken } from './wss-auth.js';
|
|
10
12
|
import { callOneBot11WsAction, handleOneBot11WsMessage, rejectAllPending, startOneBot11Heartbeat, } from './ws-transport.js';
|
|
11
13
|
export class OneBot11WssEndpoint {
|
|
12
14
|
#logger;
|
|
13
15
|
#options;
|
|
14
16
|
management = createOneBot11EndpointManagement(this);
|
|
17
|
+
content = createOneBot11ContentPort((action, params) => this.callApi(action, params));
|
|
15
18
|
#ws;
|
|
16
19
|
#wsRelease;
|
|
17
20
|
#heartbeatTimer;
|
|
@@ -101,8 +104,12 @@ export class OneBot11WssEndpoint {
|
|
|
101
104
|
return true;
|
|
102
105
|
}
|
|
103
106
|
admit(ev) {
|
|
104
|
-
if (!this.#open
|
|
107
|
+
if (!this.#open)
|
|
105
108
|
return;
|
|
109
|
+
if (!isMessageEvent(ev)) {
|
|
110
|
+
receiveOneBot11SideEvent(this.#options.sideEvents, this.#options.config.id, this, ev, this.#logger);
|
|
111
|
+
return;
|
|
112
|
+
}
|
|
106
113
|
const conversation = onebot11InboundConversation(String(this.#options.id), ev);
|
|
107
114
|
const inbound = formatInboundMetadata(ev, this.#options.config.id);
|
|
108
115
|
void this.#options.gateway.receive({
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@zhin.js/adapter-onebot11",
|
|
3
|
-
"version": "6.0.
|
|
3
|
+
"version": "6.0.14",
|
|
4
4
|
"description": "Zhin.js OneBot 11 adapter for Plugin Runtime (WebSocket client)",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "./lib/index.js",
|
|
@@ -41,12 +41,12 @@
|
|
|
41
41
|
},
|
|
42
42
|
"dependencies": {
|
|
43
43
|
"ws": "^8.21.1",
|
|
44
|
-
"@zhin.js/adapter": "1.1.
|
|
45
|
-
"@zhin.js/core": "1.5.
|
|
46
|
-
"@zhin.js/host-http": "1.0.
|
|
47
|
-
"@zhin.js/im-contract": "1.0.
|
|
44
|
+
"@zhin.js/adapter": "1.1.11",
|
|
45
|
+
"@zhin.js/core": "1.5.12",
|
|
46
|
+
"@zhin.js/host-http": "1.0.11",
|
|
47
|
+
"@zhin.js/im-contract": "1.0.4",
|
|
48
48
|
"@zhin.js/logger": "1.0.76",
|
|
49
|
-
"@zhin.js/permission": "1.0.
|
|
49
|
+
"@zhin.js/permission": "1.0.3"
|
|
50
50
|
},
|
|
51
51
|
"devDependencies": {
|
|
52
52
|
"@types/node": "^26.1.2",
|
|
@@ -54,17 +54,17 @@
|
|
|
54
54
|
"typescript": "^6.0.3",
|
|
55
55
|
"vitest": "^4.1.10",
|
|
56
56
|
"zod": "^4.4.3",
|
|
57
|
-
"@zhin.js/agent": "1.1.
|
|
58
|
-
"@zhin.js/host-http": "1.0.
|
|
59
|
-
"zhin.js": "6.0.
|
|
57
|
+
"@zhin.js/agent": "1.1.14",
|
|
58
|
+
"@zhin.js/host-http": "1.0.11",
|
|
59
|
+
"zhin.js": "6.0.12"
|
|
60
60
|
},
|
|
61
61
|
"peerDependencies": {
|
|
62
62
|
"zod": "^4.0.0",
|
|
63
|
-
"@zhin.js/adapter": "1.1.
|
|
64
|
-
"@zhin.js/agent": "1.1.
|
|
65
|
-
"@zhin.js/command": "1.0.
|
|
66
|
-
"@zhin.js/core": "1.5.
|
|
67
|
-
"zhin.js": "6.0.
|
|
63
|
+
"@zhin.js/adapter": "1.1.11",
|
|
64
|
+
"@zhin.js/agent": "1.1.14",
|
|
65
|
+
"@zhin.js/command": "1.0.15",
|
|
66
|
+
"@zhin.js/core": "1.5.12",
|
|
67
|
+
"zhin.js": "6.0.12"
|
|
68
68
|
},
|
|
69
69
|
"peerDependenciesMeta": {
|
|
70
70
|
"@zhin.js/agent": {
|
|
@@ -0,0 +1,66 @@
|
|
|
1
|
+
import type { EndpointContentPort, EndpointContentResolveContext } from 'zhin.js/adapter';
|
|
2
|
+
import { toCanonicalSegments } from '@zhin.js/core';
|
|
3
|
+
import type { ConversationMessage, ConversationReference, ForwardEntry } from '@zhin.js/im-contract';
|
|
4
|
+
|
|
5
|
+
type CallApi = (action: string, params?: Record<string, unknown>) => Promise<unknown>;
|
|
6
|
+
|
|
7
|
+
export function createOneBot11ContentPort(callApi: CallApi): EndpointContentPort {
|
|
8
|
+
return Object.freeze({
|
|
9
|
+
async resolve(reference: ConversationReference, context: EndpointContentResolveContext) {
|
|
10
|
+
try {
|
|
11
|
+
context.signal.throwIfAborted();
|
|
12
|
+
if (reference.kind === 'media') {
|
|
13
|
+
return reference.media.kind === 'file'
|
|
14
|
+
? Object.freeze({ status: 'unsupported' as const, code: 'onebot11_opaque_media' })
|
|
15
|
+
: Object.freeze({ status: 'resolved' as const, reference, value: reference.media });
|
|
16
|
+
}
|
|
17
|
+
if (reference.kind === 'message') {
|
|
18
|
+
const row = await callApi('get_msg', { message_id: reference.message.id }) as Record<string, unknown>;
|
|
19
|
+
context.signal.throwIfAborted();
|
|
20
|
+
const segments = normalizeSegments(row.message ?? row.raw_message);
|
|
21
|
+
const sender = row.sender as Record<string, unknown> | undefined;
|
|
22
|
+
const value: ConversationMessage = Object.freeze({
|
|
23
|
+
ref: reference.message,
|
|
24
|
+
...(sender?.user_id != null ? { actor: Object.freeze({
|
|
25
|
+
id: String(sender.user_id),
|
|
26
|
+
...(sender.nickname ? { displayName: String(sender.nickname) } : {}),
|
|
27
|
+
}) } : {}),
|
|
28
|
+
segments,
|
|
29
|
+
timestamp: toMillis(row.time),
|
|
30
|
+
});
|
|
31
|
+
return Object.freeze({ status: 'resolved' as const, reference, value });
|
|
32
|
+
}
|
|
33
|
+
const raw = await callApi('get_forward_msg', { id: reference.forwardId }) as Record<string, unknown>;
|
|
34
|
+
context.signal.throwIfAborted();
|
|
35
|
+
const nodes = Array.isArray(raw.messages) ? raw.messages.slice(0, context.maxEntries) : [];
|
|
36
|
+
const value: readonly ForwardEntry[] = Object.freeze(nodes.map((node) => {
|
|
37
|
+
const row = node as Record<string, unknown>;
|
|
38
|
+
const sender = row.sender as Record<string, unknown> | undefined;
|
|
39
|
+
return Object.freeze({
|
|
40
|
+
...(sender?.user_id != null ? { actor: Object.freeze({
|
|
41
|
+
id: String(sender.user_id),
|
|
42
|
+
...(sender.nickname ? { displayName: String(sender.nickname) } : {}),
|
|
43
|
+
}) } : {}),
|
|
44
|
+
timestamp: toMillis(row.time),
|
|
45
|
+
segments: normalizeSegments(row.content ?? row.message),
|
|
46
|
+
});
|
|
47
|
+
}));
|
|
48
|
+
return Object.freeze({ status: 'resolved' as const, reference, value, truncated: nodes.length >= context.maxEntries });
|
|
49
|
+
} catch (error) {
|
|
50
|
+
if (context.signal.aborted) return Object.freeze({ status: 'expired' as const, code: 'turn_aborted' });
|
|
51
|
+
return Object.freeze({ status: 'failed' as const, code: 'onebot11_content_resolution_failed', message: error instanceof Error ? error.message : String(error) });
|
|
52
|
+
}
|
|
53
|
+
},
|
|
54
|
+
});
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
function normalizeSegments(value: unknown) {
|
|
58
|
+
if (Array.isArray(value)) return Object.freeze(toCanonicalSegments(value as never));
|
|
59
|
+
return Object.freeze([{ type: 'text', data: Object.freeze({ text: String(value ?? '') }) }]);
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
function toMillis(value: unknown): number {
|
|
63
|
+
const time = Number(value);
|
|
64
|
+
if (!Number.isFinite(time)) return Date.now();
|
|
65
|
+
return time < 10_000_000_000 ? time * 1000 : time;
|
|
66
|
+
}
|
package/src/protocol.ts
CHANGED
|
@@ -198,13 +198,14 @@ export function onebot11InboundConversation(endpointKey: string, ev: OneBot11Eve
|
|
|
198
198
|
|
|
199
199
|
/** Build inbound text for MessageGateway.receive */
|
|
200
200
|
export function formatInboundContent(ev: OneBot11Event): string {
|
|
201
|
-
if (typeof ev.raw_message === 'string' && ev.raw_message) return ev.raw_message;
|
|
202
201
|
if (Array.isArray(ev.message)) {
|
|
203
202
|
return ev.message
|
|
204
203
|
.map((seg) => (seg.type === 'text' ? String(seg.data?.text ?? '') : ''))
|
|
205
204
|
.join('');
|
|
206
205
|
}
|
|
207
|
-
return ''
|
|
206
|
+
return typeof ev.raw_message === 'string'
|
|
207
|
+
? ev.raw_message.replace(/\[CQ:[^\]]+\]/g, '').trim()
|
|
208
|
+
: '';
|
|
208
209
|
}
|
|
209
210
|
|
|
210
211
|
export function senderDisplayName(ev: OneBot11Event): string {
|
|
@@ -0,0 +1,48 @@
|
|
|
1
|
+
import { receiveOneBotLikeSideEvent } from '@zhin.js/core';
|
|
2
|
+
import type { SideEventGateway } from '@zhin.js/core/runtime';
|
|
3
|
+
import { formatCompact, type getAdapterLogger } from '@zhin.js/logger';
|
|
4
|
+
import type { OneBot11Event } from './protocol.js';
|
|
5
|
+
|
|
6
|
+
export interface OneBot11SideEventCaller {
|
|
7
|
+
callApi(action: string, params?: Record<string, unknown>): Promise<unknown>;
|
|
8
|
+
}
|
|
9
|
+
|
|
10
|
+
export function receiveOneBot11SideEvent(
|
|
11
|
+
sideEvents: SideEventGateway | undefined,
|
|
12
|
+
endpointKey: string,
|
|
13
|
+
caller: OneBot11SideEventCaller,
|
|
14
|
+
raw: OneBot11Event,
|
|
15
|
+
logger: ReturnType<typeof getAdapterLogger>,
|
|
16
|
+
): void {
|
|
17
|
+
if (!sideEvents) return;
|
|
18
|
+
const record = raw as Record<string, unknown>;
|
|
19
|
+
const postType = String(record.post_type ?? '');
|
|
20
|
+
const requestType = String(record.request_type ?? '');
|
|
21
|
+
const isRequest = postType === 'request' || postType.startsWith('request.');
|
|
22
|
+
const isFriend = requestType === 'friend' || postType.includes('friend');
|
|
23
|
+
void receiveOneBotLikeSideEvent(sideEvents, {
|
|
24
|
+
adapter: 'onebot11',
|
|
25
|
+
endpointKey,
|
|
26
|
+
platform: 'onebot',
|
|
27
|
+
raw: record,
|
|
28
|
+
...(isRequest ? {
|
|
29
|
+
approve: async (flag, remark) => {
|
|
30
|
+
await (isFriend
|
|
31
|
+
? caller.callApi('set_friend_add_request', { flag, approve: true, remark })
|
|
32
|
+
: caller.callApi('set_group_add_request', { flag, approve: true, reason: remark }));
|
|
33
|
+
},
|
|
34
|
+
reject: async (flag, reason) => {
|
|
35
|
+
await (isFriend
|
|
36
|
+
? caller.callApi('set_friend_add_request', { flag, approve: false })
|
|
37
|
+
: caller.callApi('set_group_add_request', { flag, approve: false, reason }));
|
|
38
|
+
},
|
|
39
|
+
} : {}),
|
|
40
|
+
}).catch((err) => {
|
|
41
|
+
logger.warn(formatCompact({
|
|
42
|
+
op: 'onebot11_side_event_failed',
|
|
43
|
+
endpoint: endpointKey,
|
|
44
|
+
post_type: postType || undefined,
|
|
45
|
+
error: err instanceof Error ? err.message : String(err),
|
|
46
|
+
}));
|
|
47
|
+
});
|
|
48
|
+
}
|
package/src/ws-endpoint.ts
CHANGED
|
@@ -10,7 +10,7 @@ import {
|
|
|
10
10
|
type EndpointManagement,
|
|
11
11
|
type EndpointSendRequest,
|
|
12
12
|
} from 'zhin.js/adapter';
|
|
13
|
-
import type { MessageGateway } from '@zhin.js/core/runtime';
|
|
13
|
+
import type { MessageGateway, SideEventGateway } from '@zhin.js/core/runtime';
|
|
14
14
|
import { formatCompact, getAdapterLogger } from '@zhin.js/logger';
|
|
15
15
|
import type { CapabilityId } from 'zhin.js';
|
|
16
16
|
import { createOneBot11EndpointManagement } from './endpoint-management.js';
|
|
@@ -28,6 +28,8 @@ import {
|
|
|
28
28
|
type OneBot11Event,
|
|
29
29
|
type OneBot11WsConfig,
|
|
30
30
|
} from './protocol.js';
|
|
31
|
+
import { receiveOneBot11SideEvent } from './side-event-dispatch.js';
|
|
32
|
+
import { createOneBot11ContentPort } from './content-port.js';
|
|
31
33
|
import {
|
|
32
34
|
callOneBot11WsAction,
|
|
33
35
|
handleOneBot11WsMessage,
|
|
@@ -42,6 +44,7 @@ import {
|
|
|
42
44
|
export interface OneBot11WsEndpointOptions {
|
|
43
45
|
readonly id: CapabilityId;
|
|
44
46
|
readonly gateway: MessageGateway;
|
|
47
|
+
readonly sideEvents?: SideEventGateway;
|
|
45
48
|
readonly config: OneBot11WsConfig;
|
|
46
49
|
readonly createWebSocket?: (
|
|
47
50
|
url: string,
|
|
@@ -54,6 +57,7 @@ export class OneBot11WsEndpoint implements EndpointInstance {
|
|
|
54
57
|
|
|
55
58
|
readonly #options: OneBot11WsEndpointOptions;
|
|
56
59
|
readonly management: EndpointManagement = createOneBot11EndpointManagement(this);
|
|
60
|
+
readonly content = createOneBot11ContentPort((action, params) => this.callApi(action, params));
|
|
57
61
|
readonly #lifecycle: EndpointLifecycle;
|
|
58
62
|
#ws?: OneBot11WsSocket;
|
|
59
63
|
#requestId = { value: 0 };
|
|
@@ -159,7 +163,17 @@ export class OneBot11WsEndpoint implements EndpointInstance {
|
|
|
159
163
|
|
|
160
164
|
/** Test / internal: admit a parsed event when the endpoint is open. */
|
|
161
165
|
admit(ev: OneBot11Event): void {
|
|
162
|
-
if (!this.#open
|
|
166
|
+
if (!this.#open) return;
|
|
167
|
+
if (!isMessageEvent(ev)) {
|
|
168
|
+
receiveOneBot11SideEvent(
|
|
169
|
+
this.#options.sideEvents,
|
|
170
|
+
this.#options.config.id,
|
|
171
|
+
this,
|
|
172
|
+
ev,
|
|
173
|
+
this.#logger,
|
|
174
|
+
);
|
|
175
|
+
return;
|
|
176
|
+
}
|
|
163
177
|
const conversation = onebot11InboundConversation(String(this.#options.id), ev);
|
|
164
178
|
const content = formatInboundContent(ev);
|
|
165
179
|
const inbound = formatInboundMetadata(ev, this.#options.config.id);
|
package/src/wss-endpoint.ts
CHANGED
|
@@ -3,7 +3,7 @@
|
|
|
3
3
|
*/
|
|
4
4
|
import { clearInterval } from 'node:timers';
|
|
5
5
|
import type { EndpointInstance, EndpointManagement, EndpointSendRequest } from 'zhin.js/adapter';
|
|
6
|
-
import type { MessageGateway } from '@zhin.js/core/runtime';
|
|
6
|
+
import type { MessageGateway, SideEventGateway } from '@zhin.js/core/runtime';
|
|
7
7
|
import type { HttpHost, WsConnection } from '@zhin.js/host-http';
|
|
8
8
|
import { formatCompact, getAdapterLogger } from '@zhin.js/logger';
|
|
9
9
|
import type { CapabilityId } from 'zhin.js';
|
|
@@ -21,6 +21,8 @@ import {
|
|
|
21
21
|
type OneBot11Event,
|
|
22
22
|
type OneBot11WssConfig,
|
|
23
23
|
} from './protocol.js';
|
|
24
|
+
import { receiveOneBot11SideEvent } from './side-event-dispatch.js';
|
|
25
|
+
import { createOneBot11ContentPort } from './content-port.js';
|
|
24
26
|
import { verifyOneBotAccessToken } from './wss-auth.js';
|
|
25
27
|
import {
|
|
26
28
|
callOneBot11WsAction,
|
|
@@ -37,6 +39,7 @@ import {
|
|
|
37
39
|
export interface OneBot11WssEndpointOptions {
|
|
38
40
|
readonly id: CapabilityId;
|
|
39
41
|
readonly gateway: MessageGateway;
|
|
42
|
+
readonly sideEvents?: SideEventGateway;
|
|
40
43
|
readonly http: HttpHost;
|
|
41
44
|
readonly config: OneBot11WssConfig;
|
|
42
45
|
}
|
|
@@ -46,6 +49,7 @@ export class OneBot11WssEndpoint implements EndpointInstance {
|
|
|
46
49
|
|
|
47
50
|
readonly #options: OneBot11WssEndpointOptions;
|
|
48
51
|
readonly management: EndpointManagement = createOneBot11EndpointManagement(this);
|
|
52
|
+
readonly content = createOneBot11ContentPort((action, params) => this.callApi(action, params));
|
|
49
53
|
#ws?: OneBot11WsSocket;
|
|
50
54
|
#wsRelease?: () => void;
|
|
51
55
|
#heartbeatTimer?: NodeJS.Timeout;
|
|
@@ -145,7 +149,17 @@ export class OneBot11WssEndpoint implements EndpointInstance {
|
|
|
145
149
|
}
|
|
146
150
|
|
|
147
151
|
admit(ev: OneBot11Event): void {
|
|
148
|
-
if (!this.#open
|
|
152
|
+
if (!this.#open) return;
|
|
153
|
+
if (!isMessageEvent(ev)) {
|
|
154
|
+
receiveOneBot11SideEvent(
|
|
155
|
+
this.#options.sideEvents,
|
|
156
|
+
this.#options.config.id,
|
|
157
|
+
this,
|
|
158
|
+
ev,
|
|
159
|
+
this.#logger,
|
|
160
|
+
);
|
|
161
|
+
return;
|
|
162
|
+
}
|
|
149
163
|
const conversation = onebot11InboundConversation(String(this.#options.id), ev);
|
|
150
164
|
const inbound = formatInboundMetadata(ev, this.#options.config.id);
|
|
151
165
|
void this.#options.gateway.receive({
|