@zhin.js/adapter-milky 6.0.2 → 6.0.5
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 +86 -0
- package/README.md +3 -3
- package/adapters/milky.js +2 -2
- package/adapters/milky.ts +2 -2
- package/lib/index.d.ts +1 -1
- package/lib/index.js +1 -1
- package/lib/milky-agent-deps.d.ts +2 -2
- package/lib/milky-agent-deps.js +7 -7
- package/lib/protocol.d.ts +15 -13
- package/lib/protocol.js +27 -30
- package/lib/sse-endpoint.d.ts +2 -5
- package/lib/sse-endpoint.js +27 -28
- package/lib/webhook-endpoint.d.ts +2 -5
- package/lib/webhook-endpoint.js +23 -23
- package/lib/ws-endpoint.d.ts +2 -5
- package/lib/ws-endpoint.js +29 -30
- package/lib/wss-endpoint.d.ts +2 -5
- package/lib/wss-endpoint.js +25 -25
- package/package.json +15 -14
- package/schema.json +41 -5
- package/src/index.ts +1 -6
- package/src/milky-agent-deps.ts +8 -8
- package/src/protocol.ts +34 -37
- package/src/sse-endpoint.ts +30 -30
- package/src/webhook-endpoint.ts +29 -25
- package/src/ws-endpoint.ts +31 -30
- package/src/wss-endpoint.ts +31 -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/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,91 @@
|
|
|
1
1
|
# @zhin.js/adapter-milky
|
|
2
2
|
|
|
3
|
+
## 6.0.5
|
|
4
|
+
|
|
5
|
+
### Patch Changes
|
|
6
|
+
|
|
7
|
+
- Updated dependencies [6f9c366]
|
|
8
|
+
- Updated dependencies [373a56b]
|
|
9
|
+
- Updated dependencies [0de46a8]
|
|
10
|
+
- Updated dependencies [c106ecc]
|
|
11
|
+
- Updated dependencies [ca92e03]
|
|
12
|
+
- Updated dependencies [b0f37ae]
|
|
13
|
+
- Updated dependencies [ba08a2f]
|
|
14
|
+
- Updated dependencies [b08f7fe]
|
|
15
|
+
- Updated dependencies [daffd4c]
|
|
16
|
+
- Updated dependencies [36c7400]
|
|
17
|
+
- Updated dependencies [a9fa72e]
|
|
18
|
+
- Updated dependencies [c8de3ef]
|
|
19
|
+
- Updated dependencies [60f0fc8]
|
|
20
|
+
- Updated dependencies [574c990]
|
|
21
|
+
- Updated dependencies [d096f16]
|
|
22
|
+
- Updated dependencies [3f29623]
|
|
23
|
+
- Updated dependencies [2916852]
|
|
24
|
+
- Updated dependencies [d047869]
|
|
25
|
+
- Updated dependencies [162fa34]
|
|
26
|
+
- Updated dependencies [e62561e]
|
|
27
|
+
- Updated dependencies [3eeeb46]
|
|
28
|
+
- Updated dependencies [85d0f82]
|
|
29
|
+
- Updated dependencies [61bfc1c]
|
|
30
|
+
- Updated dependencies [04bad47]
|
|
31
|
+
- Updated dependencies [e40b048]
|
|
32
|
+
- Updated dependencies [5a5b1bb]
|
|
33
|
+
- Updated dependencies [f1708c3]
|
|
34
|
+
- Updated dependencies [d254a81]
|
|
35
|
+
- Updated dependencies [7123c47]
|
|
36
|
+
- Updated dependencies [05befc1]
|
|
37
|
+
- Updated dependencies [0663b6a]
|
|
38
|
+
- Updated dependencies [f28f9b3]
|
|
39
|
+
- Updated dependencies [9f340f7]
|
|
40
|
+
- Updated dependencies [e53444f]
|
|
41
|
+
- Updated dependencies [5eedd26]
|
|
42
|
+
- Updated dependencies [92b0dd7]
|
|
43
|
+
- Updated dependencies [f919b6f]
|
|
44
|
+
- Updated dependencies [098e411]
|
|
45
|
+
- Updated dependencies [e1b7c01]
|
|
46
|
+
- Updated dependencies [9b94f87]
|
|
47
|
+
- Updated dependencies [a7df753]
|
|
48
|
+
- @zhin.js/agent@1.1.5
|
|
49
|
+
- @zhin.js/host-http@1.0.8
|
|
50
|
+
- @zhin.js/im-contract@1.0.3
|
|
51
|
+
- @zhin.js/adapter@1.1.7
|
|
52
|
+
- @zhin.js/plugin-runtime@1.1.5
|
|
53
|
+
- @zhin.js/command@1.0.9
|
|
54
|
+
- @zhin.js/core@1.5.4
|
|
55
|
+
- zhin.js@6.0.4
|
|
56
|
+
|
|
57
|
+
## 6.0.4
|
|
58
|
+
|
|
59
|
+
### Patch Changes
|
|
60
|
+
|
|
61
|
+
- f8c7a54: fix: im
|
|
62
|
+
- Updated dependencies [f8c7a54]
|
|
63
|
+
- @zhin.js/logger@1.0.76
|
|
64
|
+
- @zhin.js/host-http@1.0.7
|
|
65
|
+
- @zhin.js/adapter@1.1.6
|
|
66
|
+
- @zhin.js/agent@1.1.4
|
|
67
|
+
- @zhin.js/command@1.0.8
|
|
68
|
+
- @zhin.js/core@1.5.3
|
|
69
|
+
- @zhin.js/im-contract@1.0.2
|
|
70
|
+
- @zhin.js/plugin-runtime@1.1.4
|
|
71
|
+
- zhin.js@6.0.3
|
|
72
|
+
|
|
73
|
+
## 6.0.3
|
|
74
|
+
|
|
75
|
+
### Patch Changes
|
|
76
|
+
|
|
77
|
+
- Updated dependencies [afc0e66]
|
|
78
|
+
- Updated dependencies [2e41ad5]
|
|
79
|
+
- Updated dependencies [9f57124]
|
|
80
|
+
- @zhin.js/core@1.5.2
|
|
81
|
+
- @zhin.js/adapter@1.1.5
|
|
82
|
+
- @zhin.js/im-contract@1.0.1
|
|
83
|
+
- @zhin.js/plugin-runtime@1.1.3
|
|
84
|
+
- @zhin.js/command@1.0.7
|
|
85
|
+
- @zhin.js/host-http@1.0.6
|
|
86
|
+
- @zhin.js/agent@1.1.3
|
|
87
|
+
- zhin.js@6.0.2
|
|
88
|
+
|
|
3
89
|
## 6.0.2
|
|
4
90
|
|
|
5
91
|
### Patch Changes
|
package/README.md
CHANGED
|
@@ -8,7 +8,7 @@ Zhin.js [Milky](https://milky.ntqqrev.org/) 协议适配器(Plugin Runtime)
|
|
|
8
8
|
- 约定式 `defineAdapter` / `definePlugin`(无需 `usePlugin`)
|
|
9
9
|
- **正向 WebSocket**(`connection: ws`):应用连协议端 `ws(s)://baseUrl/event`
|
|
10
10
|
- `access_token` 鉴权(Bearer + query)
|
|
11
|
-
- 入站经 `messageGatewayToken`;出站 `send({
|
|
11
|
+
- 入站经 `messageGatewayToken`;出站 `send({ conversation, payload })` → HTTP `send_*_message`
|
|
12
12
|
|
|
13
13
|
## 安装
|
|
14
14
|
|
|
@@ -23,8 +23,8 @@ pnpm add @zhin.js/adapter-milky
|
|
|
23
23
|
- `@zhin.js/plugin-runtime` — `plugin.ts`(`definePlugin`)
|
|
24
24
|
- 配置经插件 `schema.json` 落到 `plugins.<instanceKey>`
|
|
25
25
|
|
|
26
|
-
入站:`gateway.receive({
|
|
27
|
-
出站:`send({
|
|
26
|
+
入站:`gateway.receive({ conversation: ConversationRef(kind: "private"|"group", id), message, content, sender, metadata })`
|
|
27
|
+
出站:`send({ conversation, payload })` → HTTP `send_private_message` / `send_group_message`(payload 已由 gateway/core 渲染;无 segment-mapper)
|
|
28
28
|
|
|
29
29
|
## 最小配置
|
|
30
30
|
|
package/adapters/milky.js
CHANGED
|
@@ -20,8 +20,8 @@ export default defineAdapter({
|
|
|
20
20
|
const config = resolveMilkyConfig(context.config);
|
|
21
21
|
const gateway = context.use(messageGatewayToken);
|
|
22
22
|
// 注册到插件运行时状态(milky.endpoint list 的"运行中"数据源)
|
|
23
|
-
context.use(milkyRuntimeStateToken).endpoints.set(config.
|
|
24
|
-
|
|
23
|
+
context.use(milkyRuntimeStateToken).endpoints.set(config.id, {
|
|
24
|
+
id: config.id,
|
|
25
25
|
mode: config.connection,
|
|
26
26
|
});
|
|
27
27
|
if (config.connection === 'webhook') {
|
package/adapters/milky.ts
CHANGED
|
@@ -43,8 +43,8 @@ export default defineAdapter<MilkyAdapterConfig>({
|
|
|
43
43
|
const config = resolveMilkyConfig(context.config);
|
|
44
44
|
const gateway = context.use(messageGatewayToken);
|
|
45
45
|
// 注册到插件运行时状态(milky.endpoint list 的"运行中"数据源)
|
|
46
|
-
context.use(milkyRuntimeStateToken).endpoints.set(config.
|
|
47
|
-
|
|
46
|
+
context.use(milkyRuntimeStateToken).endpoints.set(config.id, {
|
|
47
|
+
id: config.id,
|
|
48
48
|
mode: config.connection,
|
|
49
49
|
});
|
|
50
50
|
if (config.connection === 'webhook') {
|
package/lib/index.d.ts
CHANGED
|
@@ -1,3 +1,3 @@
|
|
|
1
|
-
export { buildSendAction, buildSseConnectOptions, buildWsConnectOptions, callApi, extractInboundAudioUrl, formatInboundContent,
|
|
1
|
+
export { buildSendAction, buildSseConnectOptions, buildWsConnectOptions, callApi, extractInboundAudioUrl, formatInboundContent, formatOutboundSegments, isMentioned, isMessageReceiveEvent, milkyInboundConversation, parseMessageReceiveData, resolveMilkyConfig, senderNickname, type MilkyAdapterConfig, type MilkyApiClientOptions, type MilkyApiResponse, type MilkyConfigBase, type MilkyEndpointConfig, type MilkyEvent, type MilkyIncomingMessage, type MilkyIncomingSegment, type MilkyOutgoingSegment, type MilkySseConfig, type MilkyWebhookConfig, type MilkyWireSegment, type MilkyWsConfig, type MilkyWssConfig, type ResolvedMilkyConfig, } from './protocol.js';
|
|
2
2
|
export { MilkySseEndpoint, MilkyWebhookEndpoint, MilkyWssEndpoint, MilkyWsEndpoint, consumeSseBuffer, openSseStream, readRequestBody, verifyMilkyAccessToken, type CreateMilkySseStream, type MilkySseEndpointOptions, type MilkyWebhookEndpointOptions, type MilkyWssEndpointOptions, type MilkyWsCreateOptions, type MilkyWsEndpointOptions, type MilkyWsSocket, } from './endpoint.js';
|
|
3
3
|
export { getMilkyAgentDeps, registerMilkyAgentEndpoint, setMilkyAgentDeps, type MilkyAgentDeps, type MilkyAgentEndpoint, } from './milky-agent-deps.js';
|
package/lib/index.js
CHANGED
|
@@ -1,3 +1,3 @@
|
|
|
1
|
-
export { buildSendAction, buildSseConnectOptions, buildWsConnectOptions, callApi, extractInboundAudioUrl, formatInboundContent,
|
|
1
|
+
export { buildSendAction, buildSseConnectOptions, buildWsConnectOptions, callApi, extractInboundAudioUrl, formatInboundContent, formatOutboundSegments, isMentioned, isMessageReceiveEvent, milkyInboundConversation, parseMessageReceiveData, resolveMilkyConfig, senderNickname, } from './protocol.js';
|
|
2
2
|
export { MilkySseEndpoint, MilkyWebhookEndpoint, MilkyWssEndpoint, MilkyWsEndpoint, consumeSseBuffer, openSseStream, readRequestBody, verifyMilkyAccessToken, } from './endpoint.js';
|
|
3
3
|
export { getMilkyAgentDeps, registerMilkyAgentEndpoint, setMilkyAgentDeps, } from './milky-agent-deps.js';
|
|
@@ -16,9 +16,9 @@ export interface MilkyAgentEndpoint {
|
|
|
16
16
|
recallMessage?(id: string): Promise<void>;
|
|
17
17
|
}
|
|
18
18
|
export interface MilkyAgentDeps {
|
|
19
|
-
getEndpoint: (
|
|
19
|
+
getEndpoint: (endpointKey: string) => MilkyAgentEndpoint;
|
|
20
20
|
}
|
|
21
|
-
export declare function registerMilkyAgentEndpoint(
|
|
21
|
+
export declare function registerMilkyAgentEndpoint(endpointKey: string, endpoint: MilkyAgentEndpoint): () => void;
|
|
22
22
|
/** Optional override used by tests / transitional callers. Pass `null` to clear. */
|
|
23
23
|
export declare function setMilkyAgentDeps(deps: MilkyAgentDeps | null): void;
|
|
24
24
|
export declare function getMilkyAgentDeps(): MilkyAgentDeps;
|
package/lib/milky-agent-deps.js
CHANGED
|
@@ -4,11 +4,11 @@
|
|
|
4
4
|
*/
|
|
5
5
|
const endpoints = new Map();
|
|
6
6
|
let override = null;
|
|
7
|
-
export function registerMilkyAgentEndpoint(
|
|
8
|
-
endpoints.set(
|
|
7
|
+
export function registerMilkyAgentEndpoint(endpointKey, endpoint) {
|
|
8
|
+
endpoints.set(endpointKey, endpoint);
|
|
9
9
|
return () => {
|
|
10
|
-
if (endpoints.get(
|
|
11
|
-
endpoints.delete(
|
|
10
|
+
if (endpoints.get(endpointKey) === endpoint) {
|
|
11
|
+
endpoints.delete(endpointKey);
|
|
12
12
|
}
|
|
13
13
|
};
|
|
14
14
|
}
|
|
@@ -20,10 +20,10 @@ export function getMilkyAgentDeps() {
|
|
|
20
20
|
if (override)
|
|
21
21
|
return override;
|
|
22
22
|
return {
|
|
23
|
-
getEndpoint(
|
|
24
|
-
const registered = endpoints.get(
|
|
23
|
+
getEndpoint(endpointKey) {
|
|
24
|
+
const registered = endpoints.get(endpointKey);
|
|
25
25
|
if (!registered)
|
|
26
|
-
throw new Error(`Endpoint ${
|
|
26
|
+
throw new Error(`Endpoint ${endpointKey} 不存在`);
|
|
27
27
|
return registered;
|
|
28
28
|
},
|
|
29
29
|
};
|
package/lib/protocol.d.ts
CHANGED
|
@@ -4,11 +4,12 @@
|
|
|
4
4
|
* Spec: https://milky.ntqqrev.org/
|
|
5
5
|
*/
|
|
6
6
|
import type { Segment } from '@zhin.js/core/runtime';
|
|
7
|
+
import type { ConversationRef } from '@zhin.js/im-contract';
|
|
7
8
|
/** Transitional legacy endpoint row (`endpoints[]` with `context: milky`). */
|
|
8
9
|
export interface MilkyLegacyEndpointRow {
|
|
9
10
|
readonly context?: string;
|
|
10
11
|
readonly connection?: 'ws' | 'sse' | 'webhook' | 'wss';
|
|
11
|
-
readonly
|
|
12
|
+
readonly id?: string;
|
|
12
13
|
readonly baseUrl?: string;
|
|
13
14
|
readonly access_token?: string;
|
|
14
15
|
readonly path?: string;
|
|
@@ -18,7 +19,7 @@ export interface MilkyLegacyEndpointRow {
|
|
|
18
19
|
/** Plugin Runtime owner config (`plugins.<instanceKey>` / schema.json). */
|
|
19
20
|
export interface MilkyAdapterConfig {
|
|
20
21
|
readonly connection?: 'ws' | 'sse' | 'webhook' | 'wss';
|
|
21
|
-
readonly
|
|
22
|
+
readonly id?: string;
|
|
22
23
|
readonly baseUrl?: string;
|
|
23
24
|
readonly access_token?: string;
|
|
24
25
|
readonly path?: string;
|
|
@@ -29,7 +30,7 @@ export interface MilkyAdapterConfig {
|
|
|
29
30
|
}
|
|
30
31
|
export interface MilkyConfigBase {
|
|
31
32
|
readonly context: 'milky';
|
|
32
|
-
readonly
|
|
33
|
+
readonly id: string;
|
|
33
34
|
readonly baseUrl: string;
|
|
34
35
|
readonly access_token?: string;
|
|
35
36
|
}
|
|
@@ -103,10 +104,6 @@ export interface MilkyOutgoingSegment {
|
|
|
103
104
|
type: string;
|
|
104
105
|
data: Record<string, unknown>;
|
|
105
106
|
}
|
|
106
|
-
export interface ParsedSendTarget {
|
|
107
|
-
readonly message_type: 'private' | 'group';
|
|
108
|
-
readonly id: string;
|
|
109
|
-
}
|
|
110
107
|
export interface MilkyApiClientOptions {
|
|
111
108
|
readonly baseUrl: string;
|
|
112
109
|
readonly access_token?: string;
|
|
@@ -122,8 +119,11 @@ export declare function parseMessageReceiveData(event: MilkyEvent): MilkyIncomin
|
|
|
122
119
|
export declare function isMessageReceiveEvent(event: MilkyEvent): event is MilkyEvent & {
|
|
123
120
|
data: MilkyIncomingMessage;
|
|
124
121
|
};
|
|
125
|
-
/**
|
|
126
|
-
|
|
122
|
+
/**
|
|
123
|
+
* 入站归一化 → ConversationRef:`friend`(私聊)→ `private`;`group` → `group`;
|
|
124
|
+
* `temp`(群临时会话)→ 群容器内的 `private` 会话(parent = 来源群)。
|
|
125
|
+
*/
|
|
126
|
+
export declare function milkyInboundConversation(endpointKey: string, data: MilkyIncomingMessage): ConversationRef;
|
|
127
127
|
export declare function formatInboundContent(data: MilkyIncomingMessage): string;
|
|
128
128
|
/**
|
|
129
129
|
* 入站消息段 → canonical Segment[](与 formatInboundContent 纯文本视图同源双轨)。
|
|
@@ -139,7 +139,6 @@ export declare function senderNickname(data: MilkyIncomingMessage): string | und
|
|
|
139
139
|
/** 消息段中含 @ 本机(mention 段 user_id 等于协议端上报的 self_id)。 */
|
|
140
140
|
export declare function isMentioned(data: MilkyIncomingMessage, selfId: number | undefined): boolean;
|
|
141
141
|
export declare function formatInboundMessageId(data: MilkyIncomingMessage): string;
|
|
142
|
-
export declare function parseSendTarget(target: string): ParsedSendTarget;
|
|
143
142
|
/**
|
|
144
143
|
* Wire-encode an already-rendered outbound payload into Milky outgoing segments.
|
|
145
144
|
* Segment canonicalization is intentionally not done here.
|
|
@@ -147,12 +146,15 @@ export declare function parseSendTarget(target: string): ParsedSendTarget;
|
|
|
147
146
|
* file 段 Milky 消息 API 无对应 wire 段(文件走 upload_* API),warn + 丢弃。
|
|
148
147
|
*/
|
|
149
148
|
export declare function formatOutboundSegments(payload: unknown): MilkyOutgoingSegment[];
|
|
150
|
-
export declare function buildSendAction(
|
|
149
|
+
export declare function buildSendAction(conversation: ConversationRef, message: MilkyOutgoingSegment[]): {
|
|
151
150
|
action: string;
|
|
152
151
|
params: Record<string, unknown>;
|
|
153
152
|
};
|
|
154
|
-
/**
|
|
155
|
-
|
|
153
|
+
/**
|
|
154
|
+
* message_seq result → gateway message id(`scene:peer:seq` 复合格式,
|
|
155
|
+
* 与 formatInboundMessageId 同源,仅供 recall 链在本端点边界内解析)。
|
|
156
|
+
*/
|
|
157
|
+
export declare function formatOutboundMessageId(conversation: ConversationRef, messageSeq: number | undefined): string;
|
|
156
158
|
export declare function parseMilkyMessageId(msgId: string): {
|
|
157
159
|
message_scene: 'friend' | 'group' | 'temp';
|
|
158
160
|
peer_id: number;
|
package/lib/protocol.js
CHANGED
|
@@ -14,8 +14,8 @@ function normalizeConnection(connection) {
|
|
|
14
14
|
export function resolveMilkyConfig(config = {}) {
|
|
15
15
|
const entry = config.endpoints?.find((item) => item.context === 'milky');
|
|
16
16
|
const connection = normalizeConnection(config.connection ?? entry?.connection);
|
|
17
|
-
const
|
|
18
|
-
|| (typeof entry?.
|
|
17
|
+
const id = (typeof config.id === 'string' && config.id)
|
|
18
|
+
|| (typeof entry?.id === 'string' && entry.id)
|
|
19
19
|
|| process.env.MILKY_BOT_NAME
|
|
20
20
|
|| 'milky-bot';
|
|
21
21
|
const baseUrl = config.baseUrl ?? entry?.baseUrl;
|
|
@@ -25,7 +25,7 @@ export function resolveMilkyConfig(config = {}) {
|
|
|
25
25
|
const access_token = config.access_token ?? entry?.access_token;
|
|
26
26
|
const base = {
|
|
27
27
|
context: 'milky',
|
|
28
|
-
|
|
28
|
+
id,
|
|
29
29
|
baseUrl,
|
|
30
30
|
access_token,
|
|
31
31
|
};
|
|
@@ -125,10 +125,19 @@ export function parseMessageReceiveData(event) {
|
|
|
125
125
|
export function isMessageReceiveEvent(event) {
|
|
126
126
|
return parseMessageReceiveData(event) != null;
|
|
127
127
|
}
|
|
128
|
-
/**
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
128
|
+
/**
|
|
129
|
+
* 入站归一化 → ConversationRef:`friend`(私聊)→ `private`;`group` → `group`;
|
|
130
|
+
* `temp`(群临时会话)→ 群容器内的 `private` 会话(parent = 来源群)。
|
|
131
|
+
*/
|
|
132
|
+
export function milkyInboundConversation(endpointKey, data) {
|
|
133
|
+
return {
|
|
134
|
+
endpoint: { id: endpointKey, adapter: endpointKey.split('\0')[0] ?? endpointKey },
|
|
135
|
+
kind: data.message_scene === 'group' ? 'group' : 'private',
|
|
136
|
+
id: String(data.peer_id),
|
|
137
|
+
...(data.message_scene === 'temp' && data.group
|
|
138
|
+
? { parent: { kind: 'group', id: String(data.group.group_id) } }
|
|
139
|
+
: {}),
|
|
140
|
+
};
|
|
132
141
|
}
|
|
133
142
|
export function formatInboundContent(data) {
|
|
134
143
|
return data.segments.map((seg) => {
|
|
@@ -290,19 +299,6 @@ export function isMentioned(data, selfId) {
|
|
|
290
299
|
export function formatInboundMessageId(data) {
|
|
291
300
|
return `${data.message_scene}:${data.peer_id}:${data.message_seq}`;
|
|
292
301
|
}
|
|
293
|
-
export function parseSendTarget(target) {
|
|
294
|
-
const sep = target.indexOf(':');
|
|
295
|
-
if (sep <= 0) {
|
|
296
|
-
return { message_type: 'private', id: target };
|
|
297
|
-
}
|
|
298
|
-
const head = target.slice(0, sep);
|
|
299
|
-
const rest = target.slice(sep + 1);
|
|
300
|
-
if (head === 'group')
|
|
301
|
-
return { message_type: 'group', id: rest };
|
|
302
|
-
if (head === 'private')
|
|
303
|
-
return { message_type: 'private', id: rest };
|
|
304
|
-
return { message_type: 'private', id: target };
|
|
305
|
-
}
|
|
306
302
|
/**
|
|
307
303
|
* 解析 image/audio/video 段的投递 uri(canonical MediaRef 唯一来源):
|
|
308
304
|
* - kind=url → http(s):// 直发;
|
|
@@ -432,13 +428,12 @@ export function formatOutboundSegments(payload) {
|
|
|
432
428
|
}
|
|
433
429
|
return out.length ? out : [{ type: 'text', data: { text: '' } }];
|
|
434
430
|
}
|
|
435
|
-
export function buildSendAction(
|
|
436
|
-
|
|
437
|
-
if (parsed.message_type === 'group') {
|
|
431
|
+
export function buildSendAction(conversation, message) {
|
|
432
|
+
if (conversation.kind === 'group') {
|
|
438
433
|
return {
|
|
439
434
|
action: 'send_group_message',
|
|
440
435
|
params: {
|
|
441
|
-
group_id: parseInt(
|
|
436
|
+
group_id: parseInt(conversation.id, 10),
|
|
442
437
|
message,
|
|
443
438
|
},
|
|
444
439
|
};
|
|
@@ -446,18 +441,20 @@ export function buildSendAction(target, message) {
|
|
|
446
441
|
return {
|
|
447
442
|
action: 'send_private_message',
|
|
448
443
|
params: {
|
|
449
|
-
user_id: parseInt(
|
|
444
|
+
user_id: parseInt(conversation.id, 10),
|
|
450
445
|
message,
|
|
451
446
|
},
|
|
452
447
|
};
|
|
453
448
|
}
|
|
454
|
-
/**
|
|
455
|
-
|
|
449
|
+
/**
|
|
450
|
+
* message_seq result → gateway message id(`scene:peer:seq` 复合格式,
|
|
451
|
+
* 与 formatInboundMessageId 同源,仅供 recall 链在本端点边界内解析)。
|
|
452
|
+
*/
|
|
453
|
+
export function formatOutboundMessageId(conversation, messageSeq) {
|
|
456
454
|
if (messageSeq == null)
|
|
457
455
|
return '';
|
|
458
|
-
const
|
|
459
|
-
|
|
460
|
-
return `${scene}:${parsed.id}:${messageSeq}`;
|
|
456
|
+
const scene = conversation.kind === 'group' ? 'group' : 'friend';
|
|
457
|
+
return `${scene}:${conversation.id}:${messageSeq}`;
|
|
461
458
|
}
|
|
462
459
|
export function parseMilkyMessageId(msgId) {
|
|
463
460
|
const parts = msgId.split(':');
|
package/lib/sse-endpoint.d.ts
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* Milky SSE client endpoint — GET text/event-stream on /event.
|
|
3
3
|
*/
|
|
4
|
-
import { type EndpointInstance, type EndpointManagement } from '@zhin.js/adapter';
|
|
4
|
+
import { type EndpointInstance, type EndpointManagement, type EndpointSendRequest } from '@zhin.js/adapter';
|
|
5
5
|
import type { MessageGateway } from '@zhin.js/core/runtime';
|
|
6
6
|
import type { CapabilityId } from '@zhin.js/plugin-runtime';
|
|
7
7
|
import { callApi, type MilkyEvent, type MilkySseConfig } from './protocol.js';
|
|
@@ -28,10 +28,7 @@ export declare class MilkySseEndpoint implements EndpointInstance {
|
|
|
28
28
|
open(): void;
|
|
29
29
|
close(): void;
|
|
30
30
|
stop(): Promise<void>;
|
|
31
|
-
send({
|
|
32
|
-
readonly target: string;
|
|
33
|
-
readonly payload: unknown;
|
|
34
|
-
}): Promise<string>;
|
|
31
|
+
send({ conversation, payload }: EndpointSendRequest): Promise<string>;
|
|
35
32
|
callApi(action: string, params?: Record<string, unknown>): Promise<unknown>;
|
|
36
33
|
recallMessage(id: string): Promise<void>;
|
|
37
34
|
kickMember(groupId: number, userId: number, rejectAddRequest?: boolean): Promise<boolean>;
|
package/lib/sse-endpoint.js
CHANGED
|
@@ -2,13 +2,13 @@
|
|
|
2
2
|
* Milky SSE client endpoint — GET text/event-stream on /event.
|
|
3
3
|
*/
|
|
4
4
|
import { createEndpointLifecycle, } from '@zhin.js/adapter';
|
|
5
|
-
import { formatCompact,
|
|
5
|
+
import { formatCompact, getAdapterLogger } from '@zhin.js/logger';
|
|
6
6
|
import { createMilkyEndpointManagement } from './endpoint-management.js';
|
|
7
7
|
import { registerMilkyAgentEndpoint } from './milky-agent-deps.js';
|
|
8
|
-
import { buildSendAction, buildSseConnectOptions, callApi, extractInboundAudioUrl, formatInboundContent, formatInboundMessageId, formatInboundSegments,
|
|
8
|
+
import { buildSendAction, buildSseConnectOptions, callApi, extractInboundAudioUrl, formatInboundContent, formatInboundMessageId, formatInboundSegments, formatOutboundMessageId, formatOutboundSegments, isMentioned, milkyInboundConversation, parseMessageReceiveData, parseMilkyMessageId, senderNickname, } from './protocol.js';
|
|
9
9
|
import { openSseStream } from './sse-client.js';
|
|
10
|
-
const logger = getLogger('milky');
|
|
11
10
|
export class MilkySseEndpoint {
|
|
11
|
+
#logger;
|
|
12
12
|
#options;
|
|
13
13
|
#callApi;
|
|
14
14
|
management = createMilkyEndpointManagement(this);
|
|
@@ -17,10 +17,11 @@ export class MilkySseEndpoint {
|
|
|
17
17
|
#open = false;
|
|
18
18
|
#unregisterAgent;
|
|
19
19
|
constructor(options) {
|
|
20
|
+
this.#logger = getAdapterLogger('milky', options.config.id);
|
|
20
21
|
this.#options = options;
|
|
21
22
|
this.#callApi = options.callApi ?? callApi;
|
|
22
23
|
this.#lifecycle = createEndpointLifecycle({
|
|
23
|
-
name: options.config.
|
|
24
|
+
name: options.config.id,
|
|
24
25
|
// reconnect_interval 旧语义为固定间隔:multiplier 1 + 无 jitter + 不封顶
|
|
25
26
|
reconnect: {
|
|
26
27
|
initialIntervalMs: options.config.reconnect_interval,
|
|
@@ -33,7 +34,7 @@ export class MilkySseEndpoint {
|
|
|
33
34
|
async start() {
|
|
34
35
|
if (this.#lifecycle.started)
|
|
35
36
|
return;
|
|
36
|
-
this.#unregisterAgent = registerMilkyAgentEndpoint(this.#options.config.
|
|
37
|
+
this.#unregisterAgent = registerMilkyAgentEndpoint(this.#options.config.id, this);
|
|
37
38
|
try {
|
|
38
39
|
await this.#lifecycle.start((handle) => this.#connect(handle));
|
|
39
40
|
}
|
|
@@ -58,15 +59,15 @@ export class MilkySseEndpoint {
|
|
|
58
59
|
this.#stream?.close();
|
|
59
60
|
this.#stream = undefined;
|
|
60
61
|
}
|
|
61
|
-
async send({
|
|
62
|
+
async send({ conversation, payload }) {
|
|
62
63
|
const message = formatOutboundSegments(payload);
|
|
63
|
-
const { action, params } = buildSendAction(
|
|
64
|
+
const { action, params } = buildSendAction(conversation, message);
|
|
64
65
|
const data = await this.callApi(action, params);
|
|
65
|
-
const messageId = formatOutboundMessageId(
|
|
66
|
-
logger.debug(formatCompact({
|
|
66
|
+
const messageId = formatOutboundMessageId(conversation, data?.message_seq);
|
|
67
|
+
this.#logger.debug(formatCompact({
|
|
67
68
|
op: 'milky_send',
|
|
68
|
-
endpoint: this.#options.config.
|
|
69
|
-
target
|
|
69
|
+
endpoint: this.#options.config.id,
|
|
70
|
+
target: `${conversation.kind}:${conversation.id}`,
|
|
70
71
|
messageId,
|
|
71
72
|
mode: 'sse',
|
|
72
73
|
}));
|
|
@@ -159,33 +160,32 @@ export class MilkySseEndpoint {
|
|
|
159
160
|
};
|
|
160
161
|
}
|
|
161
162
|
#admitMessage(data, event) {
|
|
162
|
-
const
|
|
163
|
+
const conversation = milkyInboundConversation(String(this.#options.id), data);
|
|
164
|
+
const target = `${conversation.kind}:${conversation.id}`;
|
|
163
165
|
const content = formatInboundContent(data);
|
|
164
166
|
const segments = formatInboundSegments(data);
|
|
165
167
|
const audioUrl = extractInboundAudioUrl(data);
|
|
166
168
|
const nickname = senderNickname(data);
|
|
167
169
|
const mentioned = isMentioned(data, event.self_id);
|
|
168
170
|
void this.#options.gateway.receive({
|
|
169
|
-
|
|
170
|
-
|
|
171
|
+
conversation,
|
|
172
|
+
message: { conversation, id: formatInboundMessageId(data) },
|
|
171
173
|
content,
|
|
172
174
|
segments,
|
|
173
|
-
sender: String(data.sender_id),
|
|
174
|
-
|
|
175
|
+
sender: { id: String(data.sender_id), name: nickname },
|
|
176
|
+
endpointId: this.#options.config.id,
|
|
177
|
+
...(mentioned ? { mentioned: true } : {}),
|
|
175
178
|
metadata: Object.freeze({
|
|
176
179
|
message_scene: data.message_scene,
|
|
177
180
|
peer_id: String(data.peer_id),
|
|
178
181
|
sender_id: String(data.sender_id),
|
|
179
182
|
message_seq: data.message_seq,
|
|
180
|
-
endpoint: this.#options.config.name,
|
|
181
183
|
time: data.time ?? event.time,
|
|
182
184
|
self_id: event.self_id != null ? String(event.self_id) : undefined,
|
|
183
185
|
...(nickname ? { nickname } : {}),
|
|
184
|
-
...(mentioned ? { mentioned: true } : {}),
|
|
185
|
-
...(audioUrl ? { audio_url: audioUrl } : {}),
|
|
186
186
|
}),
|
|
187
187
|
}).catch((err) => {
|
|
188
|
-
logger.warn(formatCompact({
|
|
188
|
+
this.#logger.warn(formatCompact({
|
|
189
189
|
op: 'milky_gateway_receive_failed',
|
|
190
190
|
target,
|
|
191
191
|
error: err instanceof Error ? err.message : String(err),
|
|
@@ -204,8 +204,8 @@ export class MilkySseEndpoint {
|
|
|
204
204
|
if (settled)
|
|
205
205
|
return;
|
|
206
206
|
settled = true;
|
|
207
|
-
logger.debug(formatCompact({
|
|
208
|
-
endpoint: this.#options.config.
|
|
207
|
+
this.#logger.debug(formatCompact({
|
|
208
|
+
endpoint: this.#options.config.id,
|
|
209
209
|
mode: 'sse',
|
|
210
210
|
url: safeUrl,
|
|
211
211
|
}));
|
|
@@ -213,9 +213,9 @@ export class MilkySseEndpoint {
|
|
|
213
213
|
},
|
|
214
214
|
onMessage: (data) => this.#onMessage(data),
|
|
215
215
|
onError: (error) => {
|
|
216
|
-
logger.warn(formatCompact({
|
|
216
|
+
this.#logger.warn(formatCompact({
|
|
217
217
|
op: 'sse_error',
|
|
218
|
-
endpoint: this.#options.config.
|
|
218
|
+
endpoint: this.#options.config.id,
|
|
219
219
|
ok: false,
|
|
220
220
|
error: error.message,
|
|
221
221
|
}));
|
|
@@ -237,9 +237,8 @@ export class MilkySseEndpoint {
|
|
|
237
237
|
void stream.closed.then(() => {
|
|
238
238
|
// stop-during-connect 竞态由基座静默 settle(主动停止不算失败),此处仅对运行期断开告警
|
|
239
239
|
if (this.#lifecycle.state !== 'stopped') {
|
|
240
|
-
logger.warn(formatCompact({
|
|
240
|
+
this.#logger.warn(formatCompact({
|
|
241
241
|
op: 'disconnect',
|
|
242
|
-
endpoint: this.#options.config.name,
|
|
243
242
|
mode: 'sse',
|
|
244
243
|
reconnect_ms: this.#options.config.reconnect_interval,
|
|
245
244
|
}));
|
|
@@ -259,9 +258,9 @@ export class MilkySseEndpoint {
|
|
|
259
258
|
this.admit(event);
|
|
260
259
|
}
|
|
261
260
|
catch (error) {
|
|
262
|
-
logger.warn(formatCompact({
|
|
261
|
+
this.#logger.warn(formatCompact({
|
|
263
262
|
op: 'milky_parse_failed',
|
|
264
|
-
endpoint: this.#options.config.
|
|
263
|
+
endpoint: this.#options.config.id,
|
|
265
264
|
mode: 'sse',
|
|
266
265
|
error: error instanceof Error ? error.message : String(error),
|
|
267
266
|
}));
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import type { EndpointInstance, EndpointManagement } from '@zhin.js/adapter';
|
|
1
|
+
import type { EndpointInstance, EndpointManagement, EndpointSendRequest } from '@zhin.js/adapter';
|
|
2
2
|
import type { MessageGateway } from '@zhin.js/core/runtime';
|
|
3
3
|
import type { HttpHost } from '@zhin.js/host-http';
|
|
4
4
|
import type { CapabilityId } from '@zhin.js/plugin-runtime';
|
|
@@ -18,10 +18,7 @@ export declare class MilkyWebhookEndpoint implements EndpointInstance {
|
|
|
18
18
|
open(): void;
|
|
19
19
|
close(): void;
|
|
20
20
|
stop(): Promise<void>;
|
|
21
|
-
send({
|
|
22
|
-
readonly target: string;
|
|
23
|
-
readonly payload: unknown;
|
|
24
|
-
}): Promise<string>;
|
|
21
|
+
send({ conversation, payload }: EndpointSendRequest): Promise<string>;
|
|
25
22
|
callApi(action: string, params?: Record<string, unknown>): Promise<unknown>;
|
|
26
23
|
recallMessage(id: string): Promise<void>;
|
|
27
24
|
kickMember(groupId: number, userId: number, rejectAddRequest?: boolean): Promise<boolean>;
|