@zhin.js/adapter-milky 6.0.3 → 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 +70 -0
- package/adapters/milky.js +2 -2
- package/adapters/milky.ts +2 -2
- package/lib/milky-agent-deps.d.ts +2 -2
- package/lib/milky-agent-deps.js +7 -7
- package/lib/protocol.d.ts +4 -4
- package/lib/protocol.js +5 -5
- package/lib/sse-endpoint.js +18 -19
- package/lib/webhook-endpoint.js +14 -14
- package/lib/ws-endpoint.js +20 -21
- package/lib/wss-endpoint.js +16 -16
- package/package.json +15 -15
- package/schema.json +41 -5
- package/src/milky-agent-deps.ts +8 -8
- package/src/protocol.ts +8 -8
- package/src/sse-endpoint.ts +20 -21
- package/src/webhook-endpoint.ts +15 -15
- package/src/ws-endpoint.ts +21 -21
- package/src/wss-endpoint.ts +17 -16
- /package/commands/endpoint/add/{[name].js → [id].js} +0 -0
- /package/commands/endpoint/add/{[name].ts → [id].ts} +0 -0
- /package/commands/endpoint/remove/{[name].js → [id].js} +0 -0
- /package/commands/endpoint/remove/{[name].ts → [id].ts} +0 -0
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,75 @@
|
|
|
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
|
+
|
|
3
73
|
## 6.0.3
|
|
4
74
|
|
|
5
75
|
### Patch Changes
|
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') {
|
|
@@ -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
|
@@ -9,7 +9,7 @@ import type { ConversationRef } from '@zhin.js/im-contract';
|
|
|
9
9
|
export interface MilkyLegacyEndpointRow {
|
|
10
10
|
readonly context?: string;
|
|
11
11
|
readonly connection?: 'ws' | 'sse' | 'webhook' | 'wss';
|
|
12
|
-
readonly
|
|
12
|
+
readonly id?: string;
|
|
13
13
|
readonly baseUrl?: string;
|
|
14
14
|
readonly access_token?: string;
|
|
15
15
|
readonly path?: string;
|
|
@@ -19,7 +19,7 @@ export interface MilkyLegacyEndpointRow {
|
|
|
19
19
|
/** Plugin Runtime owner config (`plugins.<instanceKey>` / schema.json). */
|
|
20
20
|
export interface MilkyAdapterConfig {
|
|
21
21
|
readonly connection?: 'ws' | 'sse' | 'webhook' | 'wss';
|
|
22
|
-
readonly
|
|
22
|
+
readonly id?: string;
|
|
23
23
|
readonly baseUrl?: string;
|
|
24
24
|
readonly access_token?: string;
|
|
25
25
|
readonly path?: string;
|
|
@@ -30,7 +30,7 @@ export interface MilkyAdapterConfig {
|
|
|
30
30
|
}
|
|
31
31
|
export interface MilkyConfigBase {
|
|
32
32
|
readonly context: 'milky';
|
|
33
|
-
readonly
|
|
33
|
+
readonly id: string;
|
|
34
34
|
readonly baseUrl: string;
|
|
35
35
|
readonly access_token?: string;
|
|
36
36
|
}
|
|
@@ -123,7 +123,7 @@ export declare function isMessageReceiveEvent(event: MilkyEvent): event is Milky
|
|
|
123
123
|
* 入站归一化 → ConversationRef:`friend`(私聊)→ `private`;`group` → `group`;
|
|
124
124
|
* `temp`(群临时会话)→ 群容器内的 `private` 会话(parent = 来源群)。
|
|
125
125
|
*/
|
|
126
|
-
export declare function milkyInboundConversation(
|
|
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 纯文本视图同源双轨)。
|
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
|
};
|
|
@@ -129,9 +129,9 @@ export function isMessageReceiveEvent(event) {
|
|
|
129
129
|
* 入站归一化 → ConversationRef:`friend`(私聊)→ `private`;`group` → `group`;
|
|
130
130
|
* `temp`(群临时会话)→ 群容器内的 `private` 会话(parent = 来源群)。
|
|
131
131
|
*/
|
|
132
|
-
export function milkyInboundConversation(
|
|
132
|
+
export function milkyInboundConversation(endpointKey, data) {
|
|
133
133
|
return {
|
|
134
|
-
endpoint: { id:
|
|
134
|
+
endpoint: { id: endpointKey, adapter: endpointKey.split('\0')[0] ?? endpointKey },
|
|
135
135
|
kind: data.message_scene === 'group' ? 'group' : 'private',
|
|
136
136
|
id: String(data.peer_id),
|
|
137
137
|
...(data.message_scene === 'temp' && data.group
|
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
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
|
}
|
|
@@ -63,9 +64,9 @@ export class MilkySseEndpoint {
|
|
|
63
64
|
const { action, params } = buildSendAction(conversation, message);
|
|
64
65
|
const data = await this.callApi(action, params);
|
|
65
66
|
const messageId = formatOutboundMessageId(conversation, data?.message_seq);
|
|
66
|
-
logger.debug(formatCompact({
|
|
67
|
+
this.#logger.debug(formatCompact({
|
|
67
68
|
op: 'milky_send',
|
|
68
|
-
endpoint: this.#options.config.
|
|
69
|
+
endpoint: this.#options.config.id,
|
|
69
70
|
target: `${conversation.kind}:${conversation.id}`,
|
|
70
71
|
messageId,
|
|
71
72
|
mode: 'sse',
|
|
@@ -171,21 +172,20 @@ export class MilkySseEndpoint {
|
|
|
171
172
|
message: { conversation, id: formatInboundMessageId(data) },
|
|
172
173
|
content,
|
|
173
174
|
segments,
|
|
174
|
-
sender: String(data.sender_id),
|
|
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
|
}));
|
package/lib/webhook-endpoint.js
CHANGED
|
@@ -1,10 +1,10 @@
|
|
|
1
|
-
import { formatCompact,
|
|
1
|
+
import { formatCompact, getAdapterLogger } from '@zhin.js/logger';
|
|
2
2
|
import { readRequestBody, verifyMilkyAccessToken } from './milky-auth.js';
|
|
3
3
|
import { createMilkyEndpointManagement } from './endpoint-management.js';
|
|
4
4
|
import { registerMilkyAgentEndpoint } from './milky-agent-deps.js';
|
|
5
5
|
import { buildSendAction, callApi, extractInboundAudioUrl, formatInboundContent, formatInboundMessageId, formatInboundSegments, formatOutboundMessageId, formatOutboundSegments, isMentioned, milkyInboundConversation, parseMessageReceiveData, parseMilkyMessageId, senderNickname, } from './protocol.js';
|
|
6
|
-
const logger = getLogger('milky');
|
|
7
6
|
export class MilkyWebhookEndpoint {
|
|
7
|
+
#logger;
|
|
8
8
|
#options;
|
|
9
9
|
#callApi;
|
|
10
10
|
management = createMilkyEndpointManagement(this);
|
|
@@ -13,6 +13,7 @@ export class MilkyWebhookEndpoint {
|
|
|
13
13
|
#started = false;
|
|
14
14
|
#unregisterAgent;
|
|
15
15
|
constructor(options) {
|
|
16
|
+
this.#logger = getAdapterLogger('milky', options.config.id);
|
|
16
17
|
this.#options = options;
|
|
17
18
|
this.#callApi = options.callApi ?? callApi;
|
|
18
19
|
}
|
|
@@ -20,11 +21,11 @@ export class MilkyWebhookEndpoint {
|
|
|
20
21
|
if (this.#started)
|
|
21
22
|
return;
|
|
22
23
|
this.#started = true;
|
|
23
|
-
this.#unregisterAgent = registerMilkyAgentEndpoint(this.#options.config.
|
|
24
|
+
this.#unregisterAgent = registerMilkyAgentEndpoint(this.#options.config.id, this);
|
|
24
25
|
this.#setupRoutes();
|
|
25
|
-
logger.info(formatCompact({
|
|
26
|
+
this.#logger.info(formatCompact({
|
|
26
27
|
op: 'listen',
|
|
27
|
-
endpoint: this.#options.config.
|
|
28
|
+
endpoint: this.#options.config.id,
|
|
28
29
|
mode: 'webhook',
|
|
29
30
|
path: this.#options.config.path,
|
|
30
31
|
}));
|
|
@@ -42,16 +43,16 @@ export class MilkyWebhookEndpoint {
|
|
|
42
43
|
this.#unregisterAgent?.();
|
|
43
44
|
this.#unregisterAgent = undefined;
|
|
44
45
|
this.#started = false;
|
|
45
|
-
logger.debug(formatCompact({ op: 'disconnect'
|
|
46
|
+
this.#logger.debug(formatCompact({ op: 'disconnect' }));
|
|
46
47
|
}
|
|
47
48
|
async send({ conversation, payload }) {
|
|
48
49
|
const message = formatOutboundSegments(payload);
|
|
49
50
|
const { action, params } = buildSendAction(conversation, message);
|
|
50
51
|
const data = await this.callApi(action, params);
|
|
51
52
|
const messageId = formatOutboundMessageId(conversation, data?.message_seq);
|
|
52
|
-
logger.debug(formatCompact({
|
|
53
|
+
this.#logger.debug(formatCompact({
|
|
53
54
|
op: 'milky_send',
|
|
54
|
-
endpoint: this.#options.config.
|
|
55
|
+
endpoint: this.#options.config.id,
|
|
55
56
|
target: `${conversation.kind}:${conversation.id}`,
|
|
56
57
|
messageId,
|
|
57
58
|
}));
|
|
@@ -156,21 +157,20 @@ export class MilkyWebhookEndpoint {
|
|
|
156
157
|
message: { conversation, id: formatInboundMessageId(data) },
|
|
157
158
|
content,
|
|
158
159
|
segments,
|
|
159
|
-
sender: String(data.sender_id),
|
|
160
|
+
sender: { id: String(data.sender_id), name: nickname },
|
|
161
|
+
endpointId: this.#options.config.id,
|
|
162
|
+
...(mentioned ? { mentioned: true } : {}),
|
|
160
163
|
metadata: Object.freeze({
|
|
161
164
|
message_scene: data.message_scene,
|
|
162
165
|
peer_id: String(data.peer_id),
|
|
163
166
|
sender_id: String(data.sender_id),
|
|
164
167
|
message_seq: data.message_seq,
|
|
165
|
-
endpoint: this.#options.config.name,
|
|
166
168
|
time: data.time ?? event.time,
|
|
167
169
|
self_id: event.self_id != null ? String(event.self_id) : undefined,
|
|
168
170
|
...(nickname ? { nickname } : {}),
|
|
169
|
-
...(mentioned ? { mentioned: true } : {}),
|
|
170
|
-
...(audioUrl ? { audio_url: audioUrl } : {}),
|
|
171
171
|
}),
|
|
172
172
|
}).catch((err) => {
|
|
173
|
-
logger.warn(formatCompact({
|
|
173
|
+
this.#logger.warn(formatCompact({
|
|
174
174
|
op: 'milky_gateway_receive_failed',
|
|
175
175
|
target,
|
|
176
176
|
error: err instanceof Error ? err.message : String(err),
|
|
@@ -206,7 +206,7 @@ export class MilkyWebhookEndpoint {
|
|
|
206
206
|
response.end(JSON.stringify({ status: 'ok' }));
|
|
207
207
|
}
|
|
208
208
|
catch (error) {
|
|
209
|
-
logger.error('Milky webhook error:', error);
|
|
209
|
+
this.#logger.error('Milky webhook error:', error);
|
|
210
210
|
if (!response.headersSent) {
|
|
211
211
|
response.writeHead(500, { 'Content-Type': 'application/json' });
|
|
212
212
|
response.end(JSON.stringify({ message: 'Internal Server Error' }));
|
package/lib/ws-endpoint.js
CHANGED
|
@@ -3,13 +3,13 @@
|
|
|
3
3
|
*/
|
|
4
4
|
import WebSocket from 'ws';
|
|
5
5
|
import { createEndpointLifecycle, } from '@zhin.js/adapter';
|
|
6
|
-
import { formatCompact,
|
|
6
|
+
import { formatCompact, getAdapterLogger } from '@zhin.js/logger';
|
|
7
7
|
import { createMilkyEndpointManagement } from './endpoint-management.js';
|
|
8
8
|
import { registerMilkyAgentEndpoint } from './milky-agent-deps.js';
|
|
9
9
|
import { buildSendAction, buildWsConnectOptions, callApi, extractInboundAudioUrl, formatInboundContent, formatInboundMessageId, formatInboundSegments, formatOutboundMessageId, formatOutboundSegments, isMentioned, milkyInboundConversation, parseMessageReceiveData, parseMilkyMessageId, senderNickname, } from './protocol.js';
|
|
10
|
-
const logger = getLogger('milky');
|
|
11
10
|
const WS_OPEN = 1;
|
|
12
11
|
export class MilkyWsEndpoint {
|
|
12
|
+
#logger;
|
|
13
13
|
#options;
|
|
14
14
|
#callApi;
|
|
15
15
|
management = createMilkyEndpointManagement(this);
|
|
@@ -18,10 +18,11 @@ export class MilkyWsEndpoint {
|
|
|
18
18
|
#open = false;
|
|
19
19
|
#unregisterAgent;
|
|
20
20
|
constructor(options) {
|
|
21
|
+
this.#logger = getAdapterLogger('milky', options.config.id);
|
|
21
22
|
this.#options = options;
|
|
22
23
|
this.#callApi = options.callApi ?? callApi;
|
|
23
24
|
this.#lifecycle = createEndpointLifecycle({
|
|
24
|
-
name: options.config.
|
|
25
|
+
name: options.config.id,
|
|
25
26
|
// reconnect_interval 旧语义为固定间隔:multiplier 1 + 无 jitter + 不封顶
|
|
26
27
|
reconnect: {
|
|
27
28
|
initialIntervalMs: options.config.reconnect_interval,
|
|
@@ -34,7 +35,7 @@ export class MilkyWsEndpoint {
|
|
|
34
35
|
async start() {
|
|
35
36
|
if (this.#lifecycle.started)
|
|
36
37
|
return;
|
|
37
|
-
this.#unregisterAgent = registerMilkyAgentEndpoint(this.#options.config.
|
|
38
|
+
this.#unregisterAgent = registerMilkyAgentEndpoint(this.#options.config.id, this);
|
|
38
39
|
try {
|
|
39
40
|
await this.#lifecycle.start((handle) => this.#connect(handle));
|
|
40
41
|
}
|
|
@@ -71,9 +72,9 @@ export class MilkyWsEndpoint {
|
|
|
71
72
|
const { action, params } = buildSendAction(conversation, message);
|
|
72
73
|
const data = await this.callApi(action, params);
|
|
73
74
|
const messageId = formatOutboundMessageId(conversation, data?.message_seq);
|
|
74
|
-
logger.debug(formatCompact({
|
|
75
|
+
this.#logger.debug(formatCompact({
|
|
75
76
|
op: 'milky_send',
|
|
76
|
-
endpoint: this.#options.config.
|
|
77
|
+
endpoint: this.#options.config.id,
|
|
77
78
|
target: `${conversation.kind}:${conversation.id}`,
|
|
78
79
|
messageId,
|
|
79
80
|
}));
|
|
@@ -180,21 +181,20 @@ export class MilkyWsEndpoint {
|
|
|
180
181
|
message: { conversation, id: formatInboundMessageId(data) },
|
|
181
182
|
content,
|
|
182
183
|
segments,
|
|
183
|
-
sender: String(data.sender_id),
|
|
184
|
+
sender: { id: String(data.sender_id), name: nickname },
|
|
185
|
+
endpointId: this.#options.config.id,
|
|
186
|
+
...(mentioned ? { mentioned: true } : {}),
|
|
184
187
|
metadata: Object.freeze({
|
|
185
188
|
message_scene: data.message_scene,
|
|
186
189
|
peer_id: String(data.peer_id),
|
|
187
190
|
sender_id: String(data.sender_id),
|
|
188
191
|
message_seq: data.message_seq,
|
|
189
|
-
endpoint: this.#options.config.name,
|
|
190
192
|
time: data.time ?? event.time,
|
|
191
193
|
self_id: event.self_id != null ? String(event.self_id) : undefined,
|
|
192
194
|
...(nickname ? { nickname } : {}),
|
|
193
|
-
...(mentioned ? { mentioned: true } : {}),
|
|
194
|
-
...(audioUrl ? { audio_url: audioUrl } : {}),
|
|
195
195
|
}),
|
|
196
196
|
}).catch((err) => {
|
|
197
|
-
logger.warn(formatCompact({
|
|
197
|
+
this.#logger.warn(formatCompact({
|
|
198
198
|
op: 'milky_gateway_receive_failed',
|
|
199
199
|
target,
|
|
200
200
|
error: err instanceof Error ? err.message : String(err),
|
|
@@ -222,14 +222,14 @@ export class MilkyWsEndpoint {
|
|
|
222
222
|
return;
|
|
223
223
|
settled = true;
|
|
224
224
|
if (!this.#options.config.access_token) {
|
|
225
|
-
logger.warn(formatCompact({
|
|
226
|
-
endpoint: this.#options.config.
|
|
225
|
+
this.#logger.warn(formatCompact({
|
|
226
|
+
endpoint: this.#options.config.id,
|
|
227
227
|
ok: false,
|
|
228
228
|
error: 'missing access_token',
|
|
229
229
|
}));
|
|
230
230
|
}
|
|
231
|
-
logger.debug(formatCompact({
|
|
232
|
-
endpoint: this.#options.config.
|
|
231
|
+
this.#logger.debug(formatCompact({
|
|
232
|
+
endpoint: this.#options.config.id,
|
|
233
233
|
mode: 'ws',
|
|
234
234
|
url: safeUrl,
|
|
235
235
|
}));
|
|
@@ -262,9 +262,8 @@ export class MilkyWsEndpoint {
|
|
|
262
262
|
: codeNum === 1006
|
|
263
263
|
? ' [异常关闭]'
|
|
264
264
|
: '';
|
|
265
|
-
logger.warn(formatCompact({
|
|
265
|
+
this.#logger.warn(formatCompact({
|
|
266
266
|
op: 'disconnect',
|
|
267
|
-
endpoint: this.#options.config.name,
|
|
268
267
|
code: codeNum,
|
|
269
268
|
error: `${reasonStr || 'closed'}${codeHint}`,
|
|
270
269
|
reconnect_ms: this.#options.config.reconnect_interval,
|
|
@@ -278,9 +277,9 @@ export class MilkyWsEndpoint {
|
|
|
278
277
|
});
|
|
279
278
|
ws.on('error', (err) => {
|
|
280
279
|
const error = err instanceof Error ? err : new Error(String(err));
|
|
281
|
-
logger.warn(formatCompact({
|
|
280
|
+
this.#logger.warn(formatCompact({
|
|
282
281
|
op: 'ws_error',
|
|
283
|
-
endpoint: this.#options.config.
|
|
282
|
+
endpoint: this.#options.config.id,
|
|
284
283
|
ok: false,
|
|
285
284
|
error: error.message,
|
|
286
285
|
}));
|
|
@@ -304,9 +303,9 @@ export class MilkyWsEndpoint {
|
|
|
304
303
|
this.admit(event);
|
|
305
304
|
}
|
|
306
305
|
catch (error) {
|
|
307
|
-
logger.warn(formatCompact({
|
|
306
|
+
this.#logger.warn(formatCompact({
|
|
308
307
|
op: 'milky_parse_failed',
|
|
309
|
-
endpoint: this.#options.config.
|
|
308
|
+
endpoint: this.#options.config.id,
|
|
310
309
|
error: error instanceof Error ? error.message : String(error),
|
|
311
310
|
}));
|
|
312
311
|
}
|
package/lib/wss-endpoint.js
CHANGED
|
@@ -2,14 +2,14 @@
|
|
|
2
2
|
* Milky reverse WSS endpoint — httpHostToken WS upgrade inbound + baseUrl HTTP API outbound.
|
|
3
3
|
*/
|
|
4
4
|
import { clearInterval } from 'node:timers';
|
|
5
|
-
import { formatCompact,
|
|
5
|
+
import { formatCompact, getAdapterLogger } from '@zhin.js/logger';
|
|
6
6
|
import { verifyMilkyAccessToken } from './milky-auth.js';
|
|
7
7
|
import { createMilkyEndpointManagement } from './endpoint-management.js';
|
|
8
8
|
import { registerMilkyAgentEndpoint } from './milky-agent-deps.js';
|
|
9
9
|
import { buildSendAction, callApi, extractInboundAudioUrl, formatInboundContent, formatInboundMessageId, formatInboundSegments, formatOutboundMessageId, formatOutboundSegments, isMentioned, milkyInboundConversation, parseMessageReceiveData, parseMilkyMessageId, senderNickname, } from './protocol.js';
|
|
10
|
-
const logger = getLogger('milky');
|
|
11
10
|
const WS_OPEN = 1;
|
|
12
11
|
export class MilkyWssEndpoint {
|
|
12
|
+
#logger;
|
|
13
13
|
#options;
|
|
14
14
|
#callApi;
|
|
15
15
|
management = createMilkyEndpointManagement(this);
|
|
@@ -20,6 +20,7 @@ export class MilkyWssEndpoint {
|
|
|
20
20
|
#started = false;
|
|
21
21
|
#unregisterAgent;
|
|
22
22
|
constructor(options) {
|
|
23
|
+
this.#logger = getAdapterLogger('milky', options.config.id);
|
|
23
24
|
this.#options = options;
|
|
24
25
|
this.#callApi = options.callApi ?? callApi;
|
|
25
26
|
}
|
|
@@ -27,14 +28,14 @@ export class MilkyWssEndpoint {
|
|
|
27
28
|
if (this.#started)
|
|
28
29
|
return;
|
|
29
30
|
this.#started = true;
|
|
30
|
-
this.#unregisterAgent = registerMilkyAgentEndpoint(this.#options.config.
|
|
31
|
+
this.#unregisterAgent = registerMilkyAgentEndpoint(this.#options.config.id, this);
|
|
31
32
|
const handle = this.#options.http.ws(this.#options.config.path);
|
|
32
33
|
this.#wsRelease = handle.onConnection((connection) => {
|
|
33
34
|
this.#acceptConnection(connection);
|
|
34
35
|
});
|
|
35
|
-
logger.info(formatCompact({
|
|
36
|
+
this.#logger.info(formatCompact({
|
|
36
37
|
op: 'listen',
|
|
37
|
-
endpoint: this.#options.config.
|
|
38
|
+
endpoint: this.#options.config.id,
|
|
38
39
|
mode: 'wss',
|
|
39
40
|
path: this.#options.config.path,
|
|
40
41
|
}));
|
|
@@ -71,9 +72,9 @@ export class MilkyWssEndpoint {
|
|
|
71
72
|
const { action, params } = buildSendAction(conversation, message);
|
|
72
73
|
const data = await this.callApi(action, params);
|
|
73
74
|
const messageId = formatOutboundMessageId(conversation, data?.message_seq);
|
|
74
|
-
logger.debug(formatCompact({
|
|
75
|
+
this.#logger.debug(formatCompact({
|
|
75
76
|
op: 'milky_send',
|
|
76
|
-
endpoint: this.#options.config.
|
|
77
|
+
endpoint: this.#options.config.id,
|
|
77
78
|
target: `${conversation.kind}:${conversation.id}`,
|
|
78
79
|
messageId,
|
|
79
80
|
}));
|
|
@@ -178,21 +179,20 @@ export class MilkyWssEndpoint {
|
|
|
178
179
|
message: { conversation, id: formatInboundMessageId(data) },
|
|
179
180
|
content,
|
|
180
181
|
segments,
|
|
181
|
-
sender: String(data.sender_id),
|
|
182
|
+
sender: { id: String(data.sender_id) },
|
|
183
|
+
endpointId: this.#options.config.id,
|
|
184
|
+
...(mentioned ? { mentioned: true } : {}),
|
|
182
185
|
metadata: Object.freeze({
|
|
183
186
|
message_scene: data.message_scene,
|
|
184
187
|
peer_id: String(data.peer_id),
|
|
185
188
|
sender_id: String(data.sender_id),
|
|
186
189
|
message_seq: data.message_seq,
|
|
187
|
-
endpoint: this.#options.config.name,
|
|
188
190
|
time: data.time ?? event.time,
|
|
189
191
|
self_id: event.self_id != null ? String(event.self_id) : undefined,
|
|
190
192
|
...(nickname ? { nickname } : {}),
|
|
191
|
-
...(mentioned ? { mentioned: true } : {}),
|
|
192
|
-
...(audioUrl ? { audio_url: audioUrl } : {}),
|
|
193
193
|
}),
|
|
194
194
|
}).catch((err) => {
|
|
195
|
-
logger.warn(formatCompact({
|
|
195
|
+
this.#logger.warn(formatCompact({
|
|
196
196
|
op: 'milky_gateway_receive_failed',
|
|
197
197
|
target,
|
|
198
198
|
error: err instanceof Error ? err.message : String(err),
|
|
@@ -222,8 +222,8 @@ export class MilkyWssEndpoint {
|
|
|
222
222
|
if (this.#ws === socket)
|
|
223
223
|
this.#ws = undefined;
|
|
224
224
|
});
|
|
225
|
-
logger.debug(formatCompact({
|
|
226
|
-
endpoint: this.#options.config.
|
|
225
|
+
this.#logger.debug(formatCompact({
|
|
226
|
+
endpoint: this.#options.config.id,
|
|
227
227
|
mode: 'wss',
|
|
228
228
|
peer: connection.request.socket.remoteAddress,
|
|
229
229
|
}));
|
|
@@ -241,9 +241,9 @@ export class MilkyWssEndpoint {
|
|
|
241
241
|
this.admit(event);
|
|
242
242
|
}
|
|
243
243
|
catch (error) {
|
|
244
|
-
logger.warn(formatCompact({
|
|
244
|
+
this.#logger.warn(formatCompact({
|
|
245
245
|
op: 'milky_parse_failed',
|
|
246
|
-
endpoint: this.#options.config.
|
|
246
|
+
endpoint: this.#options.config.id,
|
|
247
247
|
error: error instanceof Error ? error.message : String(error),
|
|
248
248
|
}));
|
|
249
249
|
}
|