@zhin.js/adapter-onebot12 5.0.11 → 5.0.13
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 +29 -0
- package/adapters/onebot12.js +5 -1
- package/adapters/onebot12.ts +11 -1
- package/lib/content-port.d.ts +4 -0
- package/lib/content-port.js +45 -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/webhook.d.ts +3 -1
- package/lib/webhook.js +8 -1
- 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 +11 -11
- package/src/content-port.ts +49 -0
- package/src/protocol.ts +3 -2
- package/src/side-event-dispatch.ts +48 -0
- package/src/webhook.ts +16 -2
- package/src/ws-endpoint.ts +16 -2
- package/src/wss-endpoint.ts +16 -2
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,34 @@
|
|
|
1
1
|
# @zhin.js/adapter-onebot12
|
|
2
2
|
|
|
3
|
+
## 5.0.13
|
|
4
|
+
|
|
5
|
+
### Patch Changes
|
|
6
|
+
|
|
7
|
+
- b2ef6b1: Add canonical scoped message resolution and explicit unsupported results for protocol capabilities OneBot 12 does not standardize.
|
|
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 [5969c5b]
|
|
12
|
+
- Updated dependencies [5969c5b]
|
|
13
|
+
- Updated dependencies [974772e]
|
|
14
|
+
- Updated dependencies [5969c5b]
|
|
15
|
+
- Updated dependencies [2f786bd]
|
|
16
|
+
- Updated dependencies [1312ca0]
|
|
17
|
+
- Updated dependencies [985fa22]
|
|
18
|
+
- @zhin.js/im-contract@1.0.4
|
|
19
|
+
- @zhin.js/core@1.5.12
|
|
20
|
+
- @zhin.js/adapter@1.1.11
|
|
21
|
+
- @zhin.js/host-http@1.0.11
|
|
22
|
+
- @zhin.js/command@1.0.15
|
|
23
|
+
- zhin.js@6.0.12
|
|
24
|
+
|
|
25
|
+
## 5.0.12
|
|
26
|
+
|
|
27
|
+
### Patch Changes
|
|
28
|
+
|
|
29
|
+
- @zhin.js/core@1.5.11
|
|
30
|
+
- zhin.js@6.0.11
|
|
31
|
+
|
|
3
32
|
## 5.0.11
|
|
4
33
|
|
|
5
34
|
### Patch Changes
|
package/adapters/onebot12.js
CHANGED
|
@@ -3,7 +3,7 @@
|
|
|
3
3
|
* Convention entry: discover `adapters/onebot12.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 { OneBot12WebhookEndpoint } from "../lib/webhook.js";
|
|
9
9
|
import { OneBot12WsEndpoint } from "../lib/ws-endpoint.js";
|
|
@@ -24,6 +24,7 @@ export default defineAdapter({
|
|
|
24
24
|
create(context) {
|
|
25
25
|
const config = resolveOneBot12Config(context.config);
|
|
26
26
|
const gateway = context.use(messageGatewayToken);
|
|
27
|
+
const sideEvents = context.use(sideEventGatewayToken);
|
|
27
28
|
// 注册到插件运行时状态(onebot12.endpoint list 的"运行中"数据源)
|
|
28
29
|
context.use(onebot12RuntimeStateToken).endpoints.set(config.id, {
|
|
29
30
|
id: config.id,
|
|
@@ -33,6 +34,7 @@ export default defineAdapter({
|
|
|
33
34
|
return new OneBot12WebhookEndpoint({
|
|
34
35
|
id: context.id,
|
|
35
36
|
gateway,
|
|
37
|
+
sideEvents,
|
|
36
38
|
http: context.use(httpHostToken),
|
|
37
39
|
config,
|
|
38
40
|
});
|
|
@@ -41,6 +43,7 @@ export default defineAdapter({
|
|
|
41
43
|
return new OneBot12WssEndpoint({
|
|
42
44
|
id: context.id,
|
|
43
45
|
gateway,
|
|
46
|
+
sideEvents,
|
|
44
47
|
http: context.use(httpHostToken),
|
|
45
48
|
config,
|
|
46
49
|
});
|
|
@@ -48,6 +51,7 @@ export default defineAdapter({
|
|
|
48
51
|
return new OneBot12WsEndpoint({
|
|
49
52
|
id: context.id,
|
|
50
53
|
gateway,
|
|
54
|
+
sideEvents,
|
|
51
55
|
config,
|
|
52
56
|
});
|
|
53
57
|
},
|
package/adapters/onebot12.ts
CHANGED
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
* Convention entry: discover `adapters/onebot12.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 { OneBot12WebhookEndpoint } from '../src/webhook.js';
|
|
8
8
|
import { OneBot12WsEndpoint } from '../src/ws-endpoint.js';
|
|
@@ -21,6 +21,12 @@ export { OneBot12WssEndpoint } from '../src/wss-endpoint.js';
|
|
|
21
21
|
export type { OneBot12WssEndpointOptions } from '../src/wss-endpoint.js';
|
|
22
22
|
export type { OneBot12WsSocket, OneBot12WsCreateOptions } from '../src/ws-types.js';
|
|
23
23
|
|
|
24
|
+
declare module '@zhin.js/core' {
|
|
25
|
+
interface AdapterEndpoints {
|
|
26
|
+
onebot12: import('../src/ws-endpoint.js').OneBot12WsEndpoint;
|
|
27
|
+
}
|
|
28
|
+
}
|
|
29
|
+
|
|
24
30
|
export default defineAdapter<OneBot12AdapterConfig>({
|
|
25
31
|
capabilities: ['inbound', 'outbound'],
|
|
26
32
|
// 媒体段经 upload_file 物化为 file_id(spec 正式投递);上传失败降级扩展字段透传。
|
|
@@ -32,6 +38,7 @@ export default defineAdapter<OneBot12AdapterConfig>({
|
|
|
32
38
|
create(context) {
|
|
33
39
|
const config = resolveOneBot12Config(context.config);
|
|
34
40
|
const gateway = context.use(messageGatewayToken);
|
|
41
|
+
const sideEvents = context.use(sideEventGatewayToken);
|
|
35
42
|
// 注册到插件运行时状态(onebot12.endpoint list 的"运行中"数据源)
|
|
36
43
|
context.use(onebot12RuntimeStateToken).endpoints.set(config.id, {
|
|
37
44
|
id: config.id,
|
|
@@ -41,6 +48,7 @@ export default defineAdapter<OneBot12AdapterConfig>({
|
|
|
41
48
|
return new OneBot12WebhookEndpoint({
|
|
42
49
|
id: context.id,
|
|
43
50
|
gateway,
|
|
51
|
+
sideEvents,
|
|
44
52
|
http: context.use(httpHostToken),
|
|
45
53
|
config,
|
|
46
54
|
});
|
|
@@ -49,6 +57,7 @@ export default defineAdapter<OneBot12AdapterConfig>({
|
|
|
49
57
|
return new OneBot12WssEndpoint({
|
|
50
58
|
id: context.id,
|
|
51
59
|
gateway,
|
|
60
|
+
sideEvents,
|
|
52
61
|
http: context.use(httpHostToken),
|
|
53
62
|
config,
|
|
54
63
|
});
|
|
@@ -56,6 +65,7 @@ export default defineAdapter<OneBot12AdapterConfig>({
|
|
|
56
65
|
return new OneBot12WsEndpoint({
|
|
57
66
|
id: context.id,
|
|
58
67
|
gateway,
|
|
68
|
+
sideEvents,
|
|
59
69
|
config,
|
|
60
70
|
});
|
|
61
71
|
},
|
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
export function createOneBot12ContentPort(callApi) {
|
|
2
|
+
return Object.freeze({
|
|
3
|
+
async resolve(reference, context) {
|
|
4
|
+
try {
|
|
5
|
+
context.signal.throwIfAborted();
|
|
6
|
+
if (reference.kind === 'forward') {
|
|
7
|
+
return Object.freeze({ status: 'unsupported', code: 'onebot12_forward_lookup_not_standardized' });
|
|
8
|
+
}
|
|
9
|
+
if (reference.kind === 'media') {
|
|
10
|
+
return reference.media.kind === 'file'
|
|
11
|
+
? Object.freeze({ status: 'unsupported', code: 'onebot12_opaque_media' })
|
|
12
|
+
: Object.freeze({ status: 'resolved', reference, value: reference.media });
|
|
13
|
+
}
|
|
14
|
+
const row = await callApi('get_message', { message_id: reference.message.id });
|
|
15
|
+
context.signal.throwIfAborted();
|
|
16
|
+
const message = (row.message && typeof row.message === 'object' ? row.message : row);
|
|
17
|
+
const segments = normalizeSegments(message.message ?? message.content);
|
|
18
|
+
const value = Object.freeze({
|
|
19
|
+
ref: reference.message,
|
|
20
|
+
...(message.user_id != null ? { actor: Object.freeze({ id: String(message.user_id) }) } : {}),
|
|
21
|
+
segments,
|
|
22
|
+
timestamp: toMillis(message.time),
|
|
23
|
+
});
|
|
24
|
+
return Object.freeze({ status: 'resolved', reference, value });
|
|
25
|
+
}
|
|
26
|
+
catch (error) {
|
|
27
|
+
if (context.signal.aborted)
|
|
28
|
+
return Object.freeze({ status: 'expired', code: 'turn_aborted' });
|
|
29
|
+
return Object.freeze({ status: 'failed', code: 'onebot12_content_resolution_failed', message: error instanceof Error ? error.message : String(error) });
|
|
30
|
+
}
|
|
31
|
+
},
|
|
32
|
+
});
|
|
33
|
+
}
|
|
34
|
+
function normalizeSegments(value) {
|
|
35
|
+
if (!Array.isArray(value))
|
|
36
|
+
return Object.freeze([{ type: 'text', data: Object.freeze({ text: String(value ?? '') }) }]);
|
|
37
|
+
return Object.freeze(value.map((item) => {
|
|
38
|
+
const row = item;
|
|
39
|
+
return Object.freeze({ type: String(row.type ?? 'text'), data: Object.freeze((row.data ?? {})) });
|
|
40
|
+
}));
|
|
41
|
+
}
|
|
42
|
+
function toMillis(value) {
|
|
43
|
+
const time = Number(value);
|
|
44
|
+
return Number.isFinite(time) ? (time < 10_000_000_000 ? time * 1000 : time) : Date.now();
|
|
45
|
+
}
|
package/lib/protocol.js
CHANGED
|
@@ -105,14 +105,14 @@ export function onebot12InboundConversation(endpointKey, ev) {
|
|
|
105
105
|
}
|
|
106
106
|
/** Build inbound text for MessageGateway.receive */
|
|
107
107
|
export function formatInboundContent(ev) {
|
|
108
|
-
if (typeof ev.alt_message === 'string' && ev.alt_message)
|
|
109
|
-
return ev.alt_message;
|
|
110
108
|
if (Array.isArray(ev.message)) {
|
|
111
109
|
return ev.message
|
|
112
110
|
.map((seg) => (seg.type === 'text' ? String(seg.data?.text ?? '') : ''))
|
|
113
111
|
.join('');
|
|
114
112
|
}
|
|
115
|
-
return ''
|
|
113
|
+
return typeof ev.alt_message === 'string'
|
|
114
|
+
? ev.alt_message.replace(/\[[^\]]*(?:image|audio|video|file)[^\]]*\]/gi, '').trim()
|
|
115
|
+
: '';
|
|
116
116
|
}
|
|
117
117
|
/**
|
|
118
118
|
* Runtime Message sender 必须是用户 ID(agent bridge 以 sender 与 endpointMaster 比对)。
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
import type { SideEventGateway } from '@zhin.js/core/runtime';
|
|
2
|
+
import { type getAdapterLogger } from '@zhin.js/logger';
|
|
3
|
+
import type { OneBot12Event } from './protocol.js';
|
|
4
|
+
export interface OneBot12SideEventCaller {
|
|
5
|
+
callApi(action: string, params?: Record<string, unknown>): Promise<unknown>;
|
|
6
|
+
}
|
|
7
|
+
export declare function receiveOneBot12SideEvent(sideEvents: SideEventGateway | undefined, endpointKey: string, caller: OneBot12SideEventCaller, raw: OneBot12Event, 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 receiveOneBot12SideEvent(sideEvents, endpointKey, caller, raw, logger) {
|
|
4
|
+
if (!sideEvents)
|
|
5
|
+
return;
|
|
6
|
+
const record = raw;
|
|
7
|
+
const eventType = String(record.type ?? record.post_type ?? '');
|
|
8
|
+
const detailType = String(record.detail_type ?? '');
|
|
9
|
+
const isRequest = eventType === 'request' || eventType.startsWith('request.');
|
|
10
|
+
const isFriend = detailType.includes('friend') || String(record.request_type ?? '') === 'friend';
|
|
11
|
+
void receiveOneBotLikeSideEvent(sideEvents, {
|
|
12
|
+
adapter: 'onebot12',
|
|
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: 'onebot12_side_event_failed',
|
|
31
|
+
endpoint: endpointKey,
|
|
32
|
+
type: eventType || undefined,
|
|
33
|
+
error: err instanceof Error ? err.message : String(err),
|
|
34
|
+
}));
|
|
35
|
+
});
|
|
36
|
+
}
|
package/lib/webhook.d.ts
CHANGED
|
@@ -1,11 +1,12 @@
|
|
|
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 { callOneBot12Action, type OneBot12Event, type OneBot12WebhookConfig } from './protocol.js';
|
|
6
6
|
export interface OneBot12WebhookEndpointOptions {
|
|
7
7
|
readonly id: CapabilityId;
|
|
8
8
|
readonly gateway: MessageGateway;
|
|
9
|
+
readonly sideEvents?: SideEventGateway;
|
|
9
10
|
readonly http: HttpHost;
|
|
10
11
|
readonly config: OneBot12WebhookConfig;
|
|
11
12
|
readonly callAction?: typeof callOneBot12Action;
|
|
@@ -13,6 +14,7 @@ export interface OneBot12WebhookEndpointOptions {
|
|
|
13
14
|
export declare class OneBot12WebhookEndpoint implements EndpointInstance {
|
|
14
15
|
#private;
|
|
15
16
|
readonly management: EndpointManagement;
|
|
17
|
+
readonly content: import("@zhin.js/adapter").EndpointContentPort;
|
|
16
18
|
constructor(options: OneBot12WebhookEndpointOptions);
|
|
17
19
|
start(): Promise<void>;
|
|
18
20
|
open(): void;
|
package/lib/webhook.js
CHANGED
|
@@ -1,11 +1,14 @@
|
|
|
1
1
|
import { formatCompact, getLogger } from '@zhin.js/logger';
|
|
2
2
|
import { createOneBot12EndpointManagement } from './endpoint-management.js';
|
|
3
3
|
import { buildSendMessageParams, callOneBot12Action, formatInboundContent, formatOutboundSegments, isBotMentioned, isMessageEvent, onebot12InboundConversation, senderNickname, senderUserId, uploadOneBot12MediaSegments, } from './protocol.js';
|
|
4
|
+
import { receiveOneBot12SideEvent } from './side-event-dispatch.js';
|
|
5
|
+
import { createOneBot12ContentPort } from './content-port.js';
|
|
4
6
|
import { verifyOneBotAccessToken } from './wss-auth.js';
|
|
5
7
|
const logger = getLogger('onebot12');
|
|
6
8
|
export class OneBot12WebhookEndpoint {
|
|
7
9
|
#options;
|
|
8
10
|
management = createOneBot12EndpointManagement(this);
|
|
11
|
+
content = createOneBot12ContentPort((action, params) => this.callApi(action, params));
|
|
9
12
|
#callAction;
|
|
10
13
|
#routeReleases = [];
|
|
11
14
|
#open = false;
|
|
@@ -79,8 +82,12 @@ export class OneBot12WebhookEndpoint {
|
|
|
79
82
|
return resp.data;
|
|
80
83
|
}
|
|
81
84
|
admit(ev) {
|
|
82
|
-
if (!this.#open
|
|
85
|
+
if (!this.#open)
|
|
83
86
|
return;
|
|
87
|
+
if (!isMessageEvent(ev)) {
|
|
88
|
+
receiveOneBot12SideEvent(this.#options.sideEvents, this.#options.config.id, this, ev, logger);
|
|
89
|
+
return;
|
|
90
|
+
}
|
|
84
91
|
const conversation = onebot12InboundConversation(String(this.#options.id), ev);
|
|
85
92
|
const content = formatInboundContent(ev);
|
|
86
93
|
const nickname = senderNickname(ev);
|
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 OneBot12Event, type OneBot12WsConfig } from './protocol.js';
|
|
5
5
|
import { type OneBot12WsCreateOptions, type OneBot12WsSocket } from './ws-types.js';
|
|
6
6
|
export interface OneBot12WsEndpointOptions {
|
|
7
7
|
readonly id: CapabilityId;
|
|
8
8
|
readonly gateway: MessageGateway;
|
|
9
|
+
readonly sideEvents?: SideEventGateway;
|
|
9
10
|
readonly config: OneBot12WsConfig;
|
|
10
11
|
readonly createWebSocket?: (url: string, options: OneBot12WsCreateOptions) => OneBot12WsSocket;
|
|
11
12
|
}
|
|
12
13
|
export declare class OneBot12WsEndpoint implements EndpointInstance {
|
|
13
14
|
#private;
|
|
14
15
|
readonly management: EndpointManagement;
|
|
16
|
+
readonly content: import("@zhin.js/adapter").EndpointContentPort;
|
|
15
17
|
constructor(options: OneBot12WsEndpointOptions);
|
|
16
18
|
start(): Promise<void>;
|
|
17
19
|
open(): void;
|
package/lib/ws-endpoint.js
CHANGED
|
@@ -7,11 +7,14 @@ import { createEndpointLifecycle, } from 'zhin.js/adapter';
|
|
|
7
7
|
import { formatCompact, getAdapterLogger } from '@zhin.js/logger';
|
|
8
8
|
import { createOneBot12EndpointManagement } from './endpoint-management.js';
|
|
9
9
|
import { buildSendMessageParams, buildWsConnectOptions, formatInboundContent, formatOutboundSegments, isBotMentioned, isMessageEvent, onebot12InboundConversation, senderNickname, senderUserId, uploadOneBot12MediaSegments, } from './protocol.js';
|
|
10
|
+
import { receiveOneBot12SideEvent } from './side-event-dispatch.js';
|
|
11
|
+
import { createOneBot12ContentPort } from './content-port.js';
|
|
10
12
|
import { WS_OPEN, } from './ws-types.js';
|
|
11
13
|
export class OneBot12WsEndpoint {
|
|
12
14
|
#logger;
|
|
13
15
|
#options;
|
|
14
16
|
management = createOneBot12EndpointManagement(this);
|
|
17
|
+
content = createOneBot12ContentPort((action, params) => this.callApi(action, params));
|
|
15
18
|
#lifecycle;
|
|
16
19
|
#ws;
|
|
17
20
|
#requestId = 0;
|
|
@@ -102,8 +105,12 @@ export class OneBot12WsEndpoint {
|
|
|
102
105
|
}
|
|
103
106
|
/** Test / internal: admit a parsed event when the endpoint is open. */
|
|
104
107
|
admit(ev) {
|
|
105
|
-
if (!this.#open
|
|
108
|
+
if (!this.#open)
|
|
106
109
|
return;
|
|
110
|
+
if (!isMessageEvent(ev)) {
|
|
111
|
+
receiveOneBot12SideEvent(this.#options.sideEvents, this.#options.config.id, this, ev, this.#logger);
|
|
112
|
+
return;
|
|
113
|
+
}
|
|
107
114
|
const conversation = onebot12InboundConversation(String(this.#options.id), ev);
|
|
108
115
|
const content = formatInboundContent(ev);
|
|
109
116
|
const nickname = senderNickname(ev);
|
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 OneBot12Event, type OneBot12WssConfig } from './protocol.js';
|
|
6
6
|
export interface OneBot12WssEndpointOptions {
|
|
7
7
|
readonly id: CapabilityId;
|
|
8
8
|
readonly gateway: MessageGateway;
|
|
9
|
+
readonly sideEvents?: SideEventGateway;
|
|
9
10
|
readonly http: HttpHost;
|
|
10
11
|
readonly config: OneBot12WssConfig;
|
|
11
12
|
}
|
|
12
13
|
export declare class OneBot12WssEndpoint implements EndpointInstance {
|
|
13
14
|
#private;
|
|
14
15
|
readonly management: EndpointManagement;
|
|
16
|
+
readonly content: import("@zhin.js/adapter").EndpointContentPort;
|
|
15
17
|
constructor(options: OneBot12WssEndpointOptions);
|
|
16
18
|
start(): Promise<void>;
|
|
17
19
|
open(): void;
|
package/lib/wss-endpoint.js
CHANGED
|
@@ -5,12 +5,15 @@ import { clearInterval } from 'node:timers';
|
|
|
5
5
|
import { formatCompact, getAdapterLogger } from '@zhin.js/logger';
|
|
6
6
|
import { createOneBot12EndpointManagement } from './endpoint-management.js';
|
|
7
7
|
import { buildSendMessageParams, formatInboundContent, formatOutboundSegments, isBotMentioned, isMessageEvent, onebot12InboundConversation, senderNickname, senderUserId, uploadOneBot12MediaSegments, } from './protocol.js';
|
|
8
|
+
import { receiveOneBot12SideEvent } from './side-event-dispatch.js';
|
|
9
|
+
import { createOneBot12ContentPort } from './content-port.js';
|
|
8
10
|
import { verifyOneBotAccessToken } from './wss-auth.js';
|
|
9
11
|
import { WS_OPEN } from './ws-types.js';
|
|
10
12
|
export class OneBot12WssEndpoint {
|
|
11
13
|
#logger;
|
|
12
14
|
#options;
|
|
13
15
|
management = createOneBot12EndpointManagement(this);
|
|
16
|
+
content = createOneBot12ContentPort((action, params) => this.callApi(action, params));
|
|
14
17
|
#ws;
|
|
15
18
|
#wsRelease;
|
|
16
19
|
#heartbeatTimer;
|
|
@@ -99,8 +102,12 @@ export class OneBot12WssEndpoint {
|
|
|
99
102
|
return this.#callAction(action, params);
|
|
100
103
|
}
|
|
101
104
|
admit(ev) {
|
|
102
|
-
if (!this.#open
|
|
105
|
+
if (!this.#open)
|
|
103
106
|
return;
|
|
107
|
+
if (!isMessageEvent(ev)) {
|
|
108
|
+
receiveOneBot12SideEvent(this.#options.sideEvents, this.#options.config.id, this, ev, this.#logger);
|
|
109
|
+
return;
|
|
110
|
+
}
|
|
104
111
|
const conversation = onebot12InboundConversation(String(this.#options.id), ev);
|
|
105
112
|
const nickname = senderNickname(ev);
|
|
106
113
|
const mentioned = isBotMentioned(ev);
|
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.13",
|
|
4
4
|
"description": "Zhin.js OneBot 12 adapter for Plugin Runtime (WebSocket client)",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "./lib/index.js",
|
|
@@ -40,10 +40,10 @@
|
|
|
40
40
|
},
|
|
41
41
|
"dependencies": {
|
|
42
42
|
"ws": "^8.21.1",
|
|
43
|
-
"@zhin.js/adapter": "1.1.
|
|
44
|
-
"@zhin.js/core": "1.5.
|
|
45
|
-
"@zhin.js/host-http": "1.0.
|
|
46
|
-
"@zhin.js/im-contract": "1.0.
|
|
43
|
+
"@zhin.js/adapter": "1.1.11",
|
|
44
|
+
"@zhin.js/core": "1.5.12",
|
|
45
|
+
"@zhin.js/host-http": "1.0.11",
|
|
46
|
+
"@zhin.js/im-contract": "1.0.4",
|
|
47
47
|
"@zhin.js/logger": "1.0.76"
|
|
48
48
|
},
|
|
49
49
|
"devDependencies": {
|
|
@@ -51,14 +51,14 @@
|
|
|
51
51
|
"@types/ws": "^8.18.1",
|
|
52
52
|
"typescript": "^6.0.3",
|
|
53
53
|
"vitest": "^4.1.10",
|
|
54
|
-
"@zhin.js/host-http": "1.0.
|
|
55
|
-
"zhin.js": "6.0.
|
|
54
|
+
"@zhin.js/host-http": "1.0.11",
|
|
55
|
+
"zhin.js": "6.0.12"
|
|
56
56
|
},
|
|
57
57
|
"peerDependencies": {
|
|
58
|
-
"@zhin.js/adapter": "1.1.
|
|
59
|
-
"@zhin.js/command": "1.0.
|
|
60
|
-
"@zhin.js/core": "1.5.
|
|
61
|
-
"zhin.js": "6.0.
|
|
58
|
+
"@zhin.js/adapter": "1.1.11",
|
|
59
|
+
"@zhin.js/command": "1.0.15",
|
|
60
|
+
"@zhin.js/core": "1.5.12",
|
|
61
|
+
"zhin.js": "6.0.12"
|
|
62
62
|
},
|
|
63
63
|
"peerDependenciesMeta": {
|
|
64
64
|
"@zhin.js/command": {
|
|
@@ -0,0 +1,49 @@
|
|
|
1
|
+
import type { EndpointContentPort, EndpointContentResolveContext } from 'zhin.js/adapter';
|
|
2
|
+
import type { ConversationMessage, ConversationReference } from '@zhin.js/im-contract';
|
|
3
|
+
|
|
4
|
+
type CallApi = (action: string, params?: Record<string, unknown>) => Promise<unknown>;
|
|
5
|
+
|
|
6
|
+
export function createOneBot12ContentPort(callApi: CallApi): EndpointContentPort {
|
|
7
|
+
return Object.freeze({
|
|
8
|
+
async resolve(reference: ConversationReference, context: EndpointContentResolveContext) {
|
|
9
|
+
try {
|
|
10
|
+
context.signal.throwIfAborted();
|
|
11
|
+
if (reference.kind === 'forward') {
|
|
12
|
+
return Object.freeze({ status: 'unsupported' as const, code: 'onebot12_forward_lookup_not_standardized' });
|
|
13
|
+
}
|
|
14
|
+
if (reference.kind === 'media') {
|
|
15
|
+
return reference.media.kind === 'file'
|
|
16
|
+
? Object.freeze({ status: 'unsupported' as const, code: 'onebot12_opaque_media' })
|
|
17
|
+
: Object.freeze({ status: 'resolved' as const, reference, value: reference.media });
|
|
18
|
+
}
|
|
19
|
+
const row = await callApi('get_message', { message_id: reference.message.id }) as Record<string, unknown>;
|
|
20
|
+
context.signal.throwIfAborted();
|
|
21
|
+
const message = (row.message && typeof row.message === 'object' ? row.message : row) as Record<string, unknown>;
|
|
22
|
+
const segments = normalizeSegments(message.message ?? message.content);
|
|
23
|
+
const value: ConversationMessage = Object.freeze({
|
|
24
|
+
ref: reference.message,
|
|
25
|
+
...(message.user_id != null ? { actor: Object.freeze({ id: String(message.user_id) }) } : {}),
|
|
26
|
+
segments,
|
|
27
|
+
timestamp: toMillis(message.time),
|
|
28
|
+
});
|
|
29
|
+
return Object.freeze({ status: 'resolved' as const, reference, value });
|
|
30
|
+
} catch (error) {
|
|
31
|
+
if (context.signal.aborted) return Object.freeze({ status: 'expired' as const, code: 'turn_aborted' });
|
|
32
|
+
return Object.freeze({ status: 'failed' as const, code: 'onebot12_content_resolution_failed', message: error instanceof Error ? error.message : String(error) });
|
|
33
|
+
}
|
|
34
|
+
},
|
|
35
|
+
});
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
function normalizeSegments(value: unknown) {
|
|
39
|
+
if (!Array.isArray(value)) return Object.freeze([{ type: 'text', data: Object.freeze({ text: String(value ?? '') }) }]);
|
|
40
|
+
return Object.freeze(value.map((item) => {
|
|
41
|
+
const row = item as Record<string, unknown>;
|
|
42
|
+
return Object.freeze({ type: String(row.type ?? 'text'), data: Object.freeze((row.data ?? {}) as Record<string, unknown>) });
|
|
43
|
+
}));
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
function toMillis(value: unknown): number {
|
|
47
|
+
const time = Number(value);
|
|
48
|
+
return Number.isFinite(time) ? (time < 10_000_000_000 ? time * 1000 : time) : Date.now();
|
|
49
|
+
}
|
package/src/protocol.ts
CHANGED
|
@@ -228,13 +228,14 @@ export function onebot12InboundConversation(endpointKey: string, ev: OneBot12Eve
|
|
|
228
228
|
|
|
229
229
|
/** Build inbound text for MessageGateway.receive */
|
|
230
230
|
export function formatInboundContent(ev: OneBot12Event): string {
|
|
231
|
-
if (typeof ev.alt_message === 'string' && ev.alt_message) return ev.alt_message;
|
|
232
231
|
if (Array.isArray(ev.message)) {
|
|
233
232
|
return ev.message
|
|
234
233
|
.map((seg) => (seg.type === 'text' ? String(seg.data?.text ?? '') : ''))
|
|
235
234
|
.join('');
|
|
236
235
|
}
|
|
237
|
-
return ''
|
|
236
|
+
return typeof ev.alt_message === 'string'
|
|
237
|
+
? ev.alt_message.replace(/\[[^\]]*(?:image|audio|video|file)[^\]]*\]/gi, '').trim()
|
|
238
|
+
: '';
|
|
238
239
|
}
|
|
239
240
|
|
|
240
241
|
/**
|
|
@@ -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 { OneBot12Event } from './protocol.js';
|
|
5
|
+
|
|
6
|
+
export interface OneBot12SideEventCaller {
|
|
7
|
+
callApi(action: string, params?: Record<string, unknown>): Promise<unknown>;
|
|
8
|
+
}
|
|
9
|
+
|
|
10
|
+
export function receiveOneBot12SideEvent(
|
|
11
|
+
sideEvents: SideEventGateway | undefined,
|
|
12
|
+
endpointKey: string,
|
|
13
|
+
caller: OneBot12SideEventCaller,
|
|
14
|
+
raw: OneBot12Event,
|
|
15
|
+
logger: ReturnType<typeof getAdapterLogger>,
|
|
16
|
+
): void {
|
|
17
|
+
if (!sideEvents) return;
|
|
18
|
+
const record = raw as Record<string, unknown>;
|
|
19
|
+
const eventType = String(record.type ?? record.post_type ?? '');
|
|
20
|
+
const detailType = String(record.detail_type ?? '');
|
|
21
|
+
const isRequest = eventType === 'request' || eventType.startsWith('request.');
|
|
22
|
+
const isFriend = detailType.includes('friend') || String(record.request_type ?? '') === 'friend';
|
|
23
|
+
void receiveOneBotLikeSideEvent(sideEvents, {
|
|
24
|
+
adapter: 'onebot12',
|
|
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: 'onebot12_side_event_failed',
|
|
43
|
+
endpoint: endpointKey,
|
|
44
|
+
type: eventType || undefined,
|
|
45
|
+
error: err instanceof Error ? err.message : String(err),
|
|
46
|
+
}));
|
|
47
|
+
});
|
|
48
|
+
}
|
package/src/webhook.ts
CHANGED
|
@@ -3,7 +3,7 @@
|
|
|
3
3
|
*/
|
|
4
4
|
import type { IncomingMessage, ServerResponse } from 'node:http';
|
|
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, HttpRouteRegistration } from '@zhin.js/host-http';
|
|
8
8
|
import { formatCompact, getLogger } from '@zhin.js/logger';
|
|
9
9
|
import type { CapabilityId } from 'zhin.js';
|
|
@@ -22,6 +22,8 @@ import {
|
|
|
22
22
|
type OneBot12Event,
|
|
23
23
|
type OneBot12WebhookConfig,
|
|
24
24
|
} from './protocol.js';
|
|
25
|
+
import { receiveOneBot12SideEvent } from './side-event-dispatch.js';
|
|
26
|
+
import { createOneBot12ContentPort } from './content-port.js';
|
|
25
27
|
import { verifyOneBotAccessToken } from './wss-auth.js';
|
|
26
28
|
|
|
27
29
|
const logger = getLogger('onebot12');
|
|
@@ -29,6 +31,7 @@ const logger = getLogger('onebot12');
|
|
|
29
31
|
export interface OneBot12WebhookEndpointOptions {
|
|
30
32
|
readonly id: CapabilityId;
|
|
31
33
|
readonly gateway: MessageGateway;
|
|
34
|
+
readonly sideEvents?: SideEventGateway;
|
|
32
35
|
readonly http: HttpHost;
|
|
33
36
|
readonly config: OneBot12WebhookConfig;
|
|
34
37
|
readonly callAction?: typeof callOneBot12Action;
|
|
@@ -37,6 +40,7 @@ export interface OneBot12WebhookEndpointOptions {
|
|
|
37
40
|
export class OneBot12WebhookEndpoint implements EndpointInstance {
|
|
38
41
|
readonly #options: OneBot12WebhookEndpointOptions;
|
|
39
42
|
readonly management: EndpointManagement = createOneBot12EndpointManagement(this);
|
|
43
|
+
readonly content = createOneBot12ContentPort((action, params) => this.callApi(action, params));
|
|
40
44
|
readonly #callAction: typeof callOneBot12Action;
|
|
41
45
|
#routeReleases: HttpRouteRegistration[] = [];
|
|
42
46
|
#open = false;
|
|
@@ -124,7 +128,17 @@ export class OneBot12WebhookEndpoint implements EndpointInstance {
|
|
|
124
128
|
}
|
|
125
129
|
|
|
126
130
|
admit(ev: OneBot12Event): void {
|
|
127
|
-
if (!this.#open
|
|
131
|
+
if (!this.#open) return;
|
|
132
|
+
if (!isMessageEvent(ev)) {
|
|
133
|
+
receiveOneBot12SideEvent(
|
|
134
|
+
this.#options.sideEvents,
|
|
135
|
+
this.#options.config.id,
|
|
136
|
+
this,
|
|
137
|
+
ev,
|
|
138
|
+
logger,
|
|
139
|
+
);
|
|
140
|
+
return;
|
|
141
|
+
}
|
|
128
142
|
const conversation = onebot12InboundConversation(String(this.#options.id), ev);
|
|
129
143
|
const content = formatInboundContent(ev);
|
|
130
144
|
const nickname = senderNickname(ev);
|
package/src/ws-endpoint.ts
CHANGED
|
@@ -11,7 +11,7 @@ import {
|
|
|
11
11
|
type EndpointManagement,
|
|
12
12
|
type EndpointSendRequest,
|
|
13
13
|
} from 'zhin.js/adapter';
|
|
14
|
-
import type { MessageGateway } from '@zhin.js/core/runtime';
|
|
14
|
+
import type { MessageGateway, SideEventGateway } from '@zhin.js/core/runtime';
|
|
15
15
|
import { formatCompact, getAdapterLogger } from '@zhin.js/logger';
|
|
16
16
|
import type { CapabilityId } from 'zhin.js';
|
|
17
17
|
import { createOneBot12EndpointManagement } from './endpoint-management.js';
|
|
@@ -31,6 +31,8 @@ import {
|
|
|
31
31
|
type OneBot12Event,
|
|
32
32
|
type OneBot12WsConfig,
|
|
33
33
|
} from './protocol.js';
|
|
34
|
+
import { receiveOneBot12SideEvent } from './side-event-dispatch.js';
|
|
35
|
+
import { createOneBot12ContentPort } from './content-port.js';
|
|
34
36
|
import {
|
|
35
37
|
type OneBot12WsCreateOptions,
|
|
36
38
|
type OneBot12WsSocket,
|
|
@@ -40,6 +42,7 @@ import {
|
|
|
40
42
|
export interface OneBot12WsEndpointOptions {
|
|
41
43
|
readonly id: CapabilityId;
|
|
42
44
|
readonly gateway: MessageGateway;
|
|
45
|
+
readonly sideEvents?: SideEventGateway;
|
|
43
46
|
readonly config: OneBot12WsConfig;
|
|
44
47
|
readonly createWebSocket?: (
|
|
45
48
|
url: string,
|
|
@@ -52,6 +55,7 @@ export class OneBot12WsEndpoint implements EndpointInstance {
|
|
|
52
55
|
|
|
53
56
|
readonly #options: OneBot12WsEndpointOptions;
|
|
54
57
|
readonly management: EndpointManagement = createOneBot12EndpointManagement(this);
|
|
58
|
+
readonly content = createOneBot12ContentPort((action, params) => this.callApi(action, params));
|
|
55
59
|
readonly #lifecycle: EndpointLifecycle;
|
|
56
60
|
#ws?: OneBot12WsSocket;
|
|
57
61
|
#requestId = 0;
|
|
@@ -155,7 +159,17 @@ export class OneBot12WsEndpoint implements EndpointInstance {
|
|
|
155
159
|
|
|
156
160
|
/** Test / internal: admit a parsed event when the endpoint is open. */
|
|
157
161
|
admit(ev: OneBot12Event): void {
|
|
158
|
-
if (!this.#open
|
|
162
|
+
if (!this.#open) return;
|
|
163
|
+
if (!isMessageEvent(ev)) {
|
|
164
|
+
receiveOneBot12SideEvent(
|
|
165
|
+
this.#options.sideEvents,
|
|
166
|
+
this.#options.config.id,
|
|
167
|
+
this,
|
|
168
|
+
ev,
|
|
169
|
+
this.#logger,
|
|
170
|
+
);
|
|
171
|
+
return;
|
|
172
|
+
}
|
|
159
173
|
const conversation = onebot12InboundConversation(String(this.#options.id), ev);
|
|
160
174
|
const content = formatInboundContent(ev);
|
|
161
175
|
const nickname = senderNickname(ev);
|
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';
|
|
@@ -23,12 +23,15 @@ import {
|
|
|
23
23
|
type OneBot12Event,
|
|
24
24
|
type OneBot12WssConfig,
|
|
25
25
|
} from './protocol.js';
|
|
26
|
+
import { receiveOneBot12SideEvent } from './side-event-dispatch.js';
|
|
27
|
+
import { createOneBot12ContentPort } from './content-port.js';
|
|
26
28
|
import { verifyOneBotAccessToken } from './wss-auth.js';
|
|
27
29
|
import { type OneBot12WsSocket, WS_OPEN } from './ws-types.js';
|
|
28
30
|
|
|
29
31
|
export interface OneBot12WssEndpointOptions {
|
|
30
32
|
readonly id: CapabilityId;
|
|
31
33
|
readonly gateway: MessageGateway;
|
|
34
|
+
readonly sideEvents?: SideEventGateway;
|
|
32
35
|
readonly http: HttpHost;
|
|
33
36
|
readonly config: OneBot12WssConfig;
|
|
34
37
|
}
|
|
@@ -38,6 +41,7 @@ export class OneBot12WssEndpoint implements EndpointInstance {
|
|
|
38
41
|
|
|
39
42
|
readonly #options: OneBot12WssEndpointOptions;
|
|
40
43
|
readonly management: EndpointManagement = createOneBot12EndpointManagement(this);
|
|
44
|
+
readonly content = createOneBot12ContentPort((action, params) => this.callApi(action, params));
|
|
41
45
|
#ws?: OneBot12WsSocket;
|
|
42
46
|
#wsRelease?: () => void;
|
|
43
47
|
#heartbeatTimer?: NodeJS.Timeout;
|
|
@@ -140,7 +144,17 @@ export class OneBot12WssEndpoint implements EndpointInstance {
|
|
|
140
144
|
}
|
|
141
145
|
|
|
142
146
|
admit(ev: OneBot12Event): void {
|
|
143
|
-
if (!this.#open
|
|
147
|
+
if (!this.#open) return;
|
|
148
|
+
if (!isMessageEvent(ev)) {
|
|
149
|
+
receiveOneBot12SideEvent(
|
|
150
|
+
this.#options.sideEvents,
|
|
151
|
+
this.#options.config.id,
|
|
152
|
+
this,
|
|
153
|
+
ev,
|
|
154
|
+
this.#logger,
|
|
155
|
+
);
|
|
156
|
+
return;
|
|
157
|
+
}
|
|
144
158
|
const conversation = onebot12InboundConversation(String(this.#options.id), ev);
|
|
145
159
|
const nickname = senderNickname(ev);
|
|
146
160
|
const mentioned = isBotMentioned(ev);
|