@zhin.js/adapter-milky 6.0.14 → 7.0.1
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 +37 -0
- package/README.md +24 -3
- package/adapters/milky.js +1 -11
- package/adapters/milky.ts +1 -11
- package/lib/client.d.ts +21 -0
- package/lib/client.js +33 -0
- package/lib/endpoint.d.ts +1 -1
- package/lib/endpoint.js +1 -1
- package/lib/index.d.ts +5 -2
- package/lib/index.js +3 -2
- package/lib/milky-auth.d.ts +0 -1
- package/lib/milky-auth.js +0 -14
- package/lib/milky-endpoint-commands.d.ts +1 -1
- package/lib/protocol.d.ts +3 -3
- package/lib/protocol.js +2 -5
- package/lib/sse-endpoint.d.ts +6 -23
- package/lib/sse-endpoint.js +25 -87
- package/lib/webhook-endpoint.d.ts +6 -23
- package/lib/webhook-endpoint.js +29 -96
- package/lib/ws-endpoint.d.ts +6 -24
- package/lib/ws-endpoint.js +36 -116
- package/lib/wss-endpoint.d.ts +9 -23
- package/lib/wss-endpoint.js +65 -148
- package/package.json +16 -13
- package/src/client.ts +80 -0
- package/src/endpoint.ts +1 -1
- package/src/index.ts +17 -7
- package/src/milky-auth.ts +0 -15
- package/src/protocol.ts +4 -7
- package/src/sse-endpoint.ts +30 -102
- package/src/webhook-endpoint.ts +37 -113
- package/src/ws-endpoint.ts +46 -128
- package/src/wss-endpoint.ts +73 -161
- package/lib/milky-agent-deps.d.ts +0 -24
- package/lib/milky-agent-deps.js +0 -30
- package/src/milky-agent-deps.ts +0 -53
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,42 @@
|
|
|
1
1
|
# @zhin.js/adapter-milky
|
|
2
2
|
|
|
3
|
+
## 7.0.1
|
|
4
|
+
|
|
5
|
+
### Patch Changes
|
|
6
|
+
|
|
7
|
+
- 4e8117c: Expose the published imhelper protocol Clients from every Endpoint, preserve native action responses, and route host-owned HTTP, WebSocket, and raw ingress through the Client APIs. Add the shared `ClientEndpoint` deep base so concrete transports inherit one open-gated Client event bridge, and move reverse WebSocket lifecycle and heartbeat cleanup onto the common lifecycle state machine.
|
|
8
|
+
- 1fc78bc: Unify native platform Client access behind the literal `adapter` discriminant. Handlers infer both native events and Clients, while command, inbound/outbound middleware, and both Agent tool authoring surfaces expose the exact operation-scoped Client through a lazy `$client` getter. Definitions without `adapter` keep `$client` typed as `unknown`, and runtime dispatch rejects adapter mismatches before resolving the Client. Bundled platform tools now use this single path instead of model-provided endpoint ids and adapter-specific dependency wrappers. Every adapter registers one Client/EventMap contract, and protocol adapters including NapCat, Milky, OneBot and Satori now produce transport-independent Client objects rather than letting Endpoint instances impersonate Clients.
|
|
9
|
+
- 2ca9a52: Upgrade the protocol Clients to imhelper 1.0.7, keep receiver ownership exclusively in Endpoint through manual mode, expose structured protocol errors, and support canonical non-message Client events.
|
|
10
|
+
- Updated dependencies [e9c6a73]
|
|
11
|
+
- Updated dependencies [4e8117c]
|
|
12
|
+
- Updated dependencies [902fa35]
|
|
13
|
+
- Updated dependencies [54bfd6b]
|
|
14
|
+
- Updated dependencies [12025ee]
|
|
15
|
+
- Updated dependencies [09b14d6]
|
|
16
|
+
- Updated dependencies [1fc78bc]
|
|
17
|
+
- @zhin.js/agent@1.1.16
|
|
18
|
+
- @zhin.js/adapter@1.2.1
|
|
19
|
+
- @zhin.js/core@1.5.14
|
|
20
|
+
- @zhin.js/host-http@1.0.13
|
|
21
|
+
- @zhin.js/command@1.0.16
|
|
22
|
+
- @zhin.js/logger@1.0.77
|
|
23
|
+
- zhin.js@6.0.14
|
|
24
|
+
- @zhin.js/feature-kit@1.0.13
|
|
25
|
+
|
|
26
|
+
## 7.0.0
|
|
27
|
+
|
|
28
|
+
### Patch Changes
|
|
29
|
+
|
|
30
|
+
- f2c532f: Expose exact per-Endpoint message operations through one validated Adapter capability model, route Core control calls through declared active capabilities, and connect existing recall, edit, reaction, and typing implementations across platform adapters.
|
|
31
|
+
- Updated dependencies [b10d058]
|
|
32
|
+
- Updated dependencies [f2c532f]
|
|
33
|
+
- Updated dependencies [3dbf990]
|
|
34
|
+
- @zhin.js/host-http@1.0.12
|
|
35
|
+
- @zhin.js/adapter@1.2.0
|
|
36
|
+
- @zhin.js/core@1.5.13
|
|
37
|
+
- @zhin.js/agent@1.1.15
|
|
38
|
+
- zhin.js@6.0.13
|
|
39
|
+
|
|
3
40
|
## 6.0.14
|
|
4
41
|
|
|
5
42
|
### 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
|
-
- 入站经 `
|
|
11
|
+
- 入站经 `Endpoint.emit(...)`;出站 `send({ conversation, payload })` → HTTP `send_*_message`
|
|
12
12
|
|
|
13
13
|
## 安装
|
|
14
14
|
|
|
@@ -19,13 +19,24 @@ pnpm add @zhin.js/adapter-milky
|
|
|
19
19
|
## Plugin Runtime
|
|
20
20
|
|
|
21
21
|
- `@zhin.js/adapter` — 约定式 `adapters/milky.ts`(`defineAdapter`)
|
|
22
|
-
- `@zhin.js/core` — `
|
|
22
|
+
- `@zhin.js/core` — `Endpoint.emit(...)` 入站、`outboundMessageToken` 出站
|
|
23
23
|
- `zhin.js` — `plugin.ts`(`definePlugin`)
|
|
24
24
|
- 配置经插件 `schema.json` 落到 `plugins.<instanceKey>`
|
|
25
25
|
|
|
26
26
|
入站:`gateway.receive({ conversation: ConversationRef(kind: "private"|"group", id), message, content, sender, metadata })`
|
|
27
27
|
出站:`send({ conversation, payload })` → HTTP `send_private_message` / `send_group_message`(payload 已由 gateway/core 渲染;无 segment-mapper)
|
|
28
28
|
|
|
29
|
+
每个 Endpoint 的 `$client` 是 `@imhelper/milky-v1` 的 `MilkyV1Client`。插件可直接调用
|
|
30
|
+
`$client.call()`、`getGroupList()` 等公开能力;`defineHandler({ adapter: 'milky', event: ... })`
|
|
31
|
+
使用 imhelper 的精确事件名与 payload 类型。HTTP、纯事件 WS/WSS 和 `ingest()` 都汇入同一个
|
|
32
|
+
Client 事件流,Endpoint 仍独占账号连接、鉴权、重连和心跳。
|
|
33
|
+
|
|
34
|
+
## 前置条件
|
|
35
|
+
|
|
36
|
+
1. 启动兼容 Milky 的实现,并记录 HTTP API 与事件连接地址。
|
|
37
|
+
2. 正向 WS/SSE 需 Zhin 主动可达实现端;Webhook/反向 WS 需实现端可达 Zhin HTTP Host。
|
|
38
|
+
3. 两端配置相同的 `access_token`,生产环境不要暴露无鉴权接口。
|
|
39
|
+
|
|
29
40
|
## 最小配置
|
|
30
41
|
|
|
31
42
|
```yaml
|
|
@@ -48,7 +59,8 @@ plugins:
|
|
|
48
59
|
| connection | 状态 |
|
|
49
60
|
|------------|------|
|
|
50
61
|
| `ws` | 已实现(推荐) |
|
|
51
|
-
| `sse` | HTTP GET `/event`(`Accept: text/event-stream`)
|
|
62
|
+
| `sse` | HTTP GET `/event`(`Accept: text/event-stream`) |
|
|
63
|
+
| `webhook` | 已实现:POST 入站 + baseUrl HTTP API 出站 |
|
|
52
64
|
| `wss` | 已实现:反向 WS(httpHostToken) |
|
|
53
65
|
|
|
54
66
|
## 鉴权
|
|
@@ -74,6 +86,15 @@ plugins:
|
|
|
74
86
|
- [Milky 通信](https://milky.ntqqrev.org/guide/communication)
|
|
75
87
|
- [适配器概览](https://zhin.js.org/essentials/adapters)
|
|
76
88
|
|
|
89
|
+
## 故障排查
|
|
90
|
+
|
|
91
|
+
| 现象 | 排查 |
|
|
92
|
+
| --- | --- |
|
|
93
|
+
| WS/SSE 无事件 | 核对 `baseUrl`、连接模式与实现端事件服务 |
|
|
94
|
+
| 401 或握手失败 | 确认 Bearer/query 中的 `access_token` 与实现端一致 |
|
|
95
|
+
| Webhook/WSS 无法连接 | 检查 HTTP Host、网络可达性与回调路径 |
|
|
96
|
+
| 能收不能发 | 检查 HTTP API 与发送动作支持情况 |
|
|
97
|
+
|
|
77
98
|
## 许可证
|
|
78
99
|
|
|
79
100
|
MIT License
|
package/adapters/milky.js
CHANGED
|
@@ -3,7 +3,6 @@
|
|
|
3
3
|
* Convention entry: discover `adapters/milky.ts` → defineAdapter.
|
|
4
4
|
*/
|
|
5
5
|
import { defineAdapter } from 'zhin.js/adapter';
|
|
6
|
-
import { messageGatewayToken, sideEventGatewayToken } from '@zhin.js/core/runtime';
|
|
7
6
|
import { httpHostToken } from '@zhin.js/host-http';
|
|
8
7
|
import { MilkySseEndpoint, MilkyWebhookEndpoint, MilkyWssEndpoint, MilkyWsEndpoint, } from "../lib/endpoint.js";
|
|
9
8
|
import { resolveMilkyConfig, } from "../lib/protocol.js";
|
|
@@ -11,6 +10,7 @@ import { milkyRuntimeStateToken } from "../lib/milky-runtime-state.js";
|
|
|
11
10
|
export { MilkySseEndpoint, MilkyWebhookEndpoint, MilkyWssEndpoint, MilkyWsEndpoint, } from "../lib/endpoint.js";
|
|
12
11
|
export default defineAdapter({
|
|
13
12
|
capabilities: ['inbound', 'outbound'],
|
|
13
|
+
operations: ['recall'],
|
|
14
14
|
// Milky 协议资源 uri 消费 http(s):// 与 base64:// 形式;无卡片交互面,交互段降级纯文本。
|
|
15
15
|
segments: {
|
|
16
16
|
outboundMedia: ['url', 'base64'],
|
|
@@ -18,8 +18,6 @@ export default defineAdapter({
|
|
|
18
18
|
},
|
|
19
19
|
create(context) {
|
|
20
20
|
const config = resolveMilkyConfig(context.config);
|
|
21
|
-
const gateway = context.use(messageGatewayToken);
|
|
22
|
-
const sideEvents = context.use(sideEventGatewayToken);
|
|
23
21
|
// 注册到插件运行时状态(milky.endpoint list 的"运行中"数据源)
|
|
24
22
|
context.use(milkyRuntimeStateToken).endpoints.set(config.id, {
|
|
25
23
|
id: config.id,
|
|
@@ -28,8 +26,6 @@ export default defineAdapter({
|
|
|
28
26
|
if (config.connection === 'webhook') {
|
|
29
27
|
return new MilkyWebhookEndpoint({
|
|
30
28
|
id: context.id,
|
|
31
|
-
gateway,
|
|
32
|
-
sideEvents,
|
|
33
29
|
http: context.use(httpHostToken),
|
|
34
30
|
config,
|
|
35
31
|
});
|
|
@@ -37,8 +33,6 @@ export default defineAdapter({
|
|
|
37
33
|
if (config.connection === 'wss') {
|
|
38
34
|
return new MilkyWssEndpoint({
|
|
39
35
|
id: context.id,
|
|
40
|
-
gateway,
|
|
41
|
-
sideEvents,
|
|
42
36
|
http: context.use(httpHostToken),
|
|
43
37
|
config,
|
|
44
38
|
});
|
|
@@ -46,15 +40,11 @@ export default defineAdapter({
|
|
|
46
40
|
if (config.connection === 'sse') {
|
|
47
41
|
return new MilkySseEndpoint({
|
|
48
42
|
id: context.id,
|
|
49
|
-
gateway,
|
|
50
|
-
sideEvents,
|
|
51
43
|
config,
|
|
52
44
|
});
|
|
53
45
|
}
|
|
54
46
|
return new MilkyWsEndpoint({
|
|
55
47
|
id: context.id,
|
|
56
|
-
gateway,
|
|
57
|
-
sideEvents,
|
|
58
48
|
config,
|
|
59
49
|
});
|
|
60
50
|
},
|
package/adapters/milky.ts
CHANGED
|
@@ -2,7 +2,6 @@
|
|
|
2
2
|
* Convention entry: discover `adapters/milky.ts` → defineAdapter.
|
|
3
3
|
*/
|
|
4
4
|
import { defineAdapter } from 'zhin.js/adapter';
|
|
5
|
-
import { messageGatewayToken, sideEventGatewayToken } from '@zhin.js/core/runtime';
|
|
6
5
|
import { httpHostToken } from '@zhin.js/host-http';
|
|
7
6
|
import {
|
|
8
7
|
MilkySseEndpoint,
|
|
@@ -34,6 +33,7 @@ export type {
|
|
|
34
33
|
|
|
35
34
|
export default defineAdapter<MilkyAdapterConfig>({
|
|
36
35
|
capabilities: ['inbound', 'outbound'],
|
|
36
|
+
operations: ['recall'],
|
|
37
37
|
// Milky 协议资源 uri 消费 http(s):// 与 base64:// 形式;无卡片交互面,交互段降级纯文本。
|
|
38
38
|
segments: {
|
|
39
39
|
outboundMedia: ['url', 'base64'],
|
|
@@ -41,8 +41,6 @@ export default defineAdapter<MilkyAdapterConfig>({
|
|
|
41
41
|
},
|
|
42
42
|
create(context) {
|
|
43
43
|
const config = resolveMilkyConfig(context.config);
|
|
44
|
-
const gateway = context.use(messageGatewayToken);
|
|
45
|
-
const sideEvents = context.use(sideEventGatewayToken);
|
|
46
44
|
// 注册到插件运行时状态(milky.endpoint list 的"运行中"数据源)
|
|
47
45
|
context.use(milkyRuntimeStateToken).endpoints.set(config.id, {
|
|
48
46
|
id: config.id,
|
|
@@ -51,8 +49,6 @@ export default defineAdapter<MilkyAdapterConfig>({
|
|
|
51
49
|
if (config.connection === 'webhook') {
|
|
52
50
|
return new MilkyWebhookEndpoint({
|
|
53
51
|
id: context.id,
|
|
54
|
-
gateway,
|
|
55
|
-
sideEvents,
|
|
56
52
|
http: context.use(httpHostToken),
|
|
57
53
|
config,
|
|
58
54
|
});
|
|
@@ -60,8 +56,6 @@ export default defineAdapter<MilkyAdapterConfig>({
|
|
|
60
56
|
if (config.connection === 'wss') {
|
|
61
57
|
return new MilkyWssEndpoint({
|
|
62
58
|
id: context.id,
|
|
63
|
-
gateway,
|
|
64
|
-
sideEvents,
|
|
65
59
|
http: context.use(httpHostToken),
|
|
66
60
|
config,
|
|
67
61
|
});
|
|
@@ -69,15 +63,11 @@ export default defineAdapter<MilkyAdapterConfig>({
|
|
|
69
63
|
if (config.connection === 'sse') {
|
|
70
64
|
return new MilkySseEndpoint({
|
|
71
65
|
id: context.id,
|
|
72
|
-
gateway,
|
|
73
|
-
sideEvents,
|
|
74
66
|
config,
|
|
75
67
|
});
|
|
76
68
|
}
|
|
77
69
|
return new MilkyWsEndpoint({
|
|
78
70
|
id: context.id,
|
|
79
|
-
gateway,
|
|
80
|
-
sideEvents,
|
|
81
71
|
config,
|
|
82
72
|
});
|
|
83
73
|
},
|
package/lib/client.d.ts
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
import { MilkyV1Client, type MilkyV1Event } from '@imhelper/milky-v1';
|
|
2
|
+
import { type EventMap, type ImHelperEventMap } from 'imhelper';
|
|
3
|
+
import { type ClientEventPayloads } from 'zhin.js/adapter';
|
|
4
|
+
import type { ResolvedMilkyConfig, callApi } from './protocol.js';
|
|
5
|
+
export { MilkyV1Client as MilkyClient } from '@imhelper/milky-v1';
|
|
6
|
+
type MilkyClientEvents = ImHelperEventMap<string, MilkyV1Event, EventMap<string>>;
|
|
7
|
+
export type MilkyClientEventMap = ClientEventPayloads<MilkyClientEvents>;
|
|
8
|
+
/** Construct the exact public imhelper Client without handing it transport ownership. */
|
|
9
|
+
export declare function createMilkyEndpointClient(config: ResolvedMilkyConfig, request: typeof callApi): MilkyV1Client;
|
|
10
|
+
/** Use the Client's complete protocol response while keeping Endpoint internals data-oriented. */
|
|
11
|
+
export declare function callMilkyClient<T = unknown>(client: MilkyV1Client, action: string, params?: Record<string, unknown>): Promise<T | undefined>;
|
|
12
|
+
export declare function forwardMilkyClientEvents(client: MilkyV1Client, receive: (name: string, payload: unknown) => void): () => void;
|
|
13
|
+
declare module '@zhin.js/feature-kit' {
|
|
14
|
+
interface AdapterClientRegistry {
|
|
15
|
+
readonly milky: {
|
|
16
|
+
readonly client: MilkyV1Client;
|
|
17
|
+
readonly events: MilkyClientEventMap;
|
|
18
|
+
};
|
|
19
|
+
}
|
|
20
|
+
}
|
|
21
|
+
export declare const milkyClient: import("@zhin.js/adapter").EndpointClientToken<MilkyV1Client, ClientEventPayloads<MilkyClientEvents>>;
|
package/lib/client.js
ADDED
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
import { MilkyV1Client, } from '@imhelper/milky-v1';
|
|
2
|
+
import { EventFactory, } from 'imhelper';
|
|
3
|
+
import { defineEndpointClient, forwardEndpointClientEvents, } from 'zhin.js/adapter';
|
|
4
|
+
export { MilkyV1Client as MilkyClient } from '@imhelper/milky-v1';
|
|
5
|
+
/** Construct the exact public imhelper Client without handing it transport ownership. */
|
|
6
|
+
export function createMilkyEndpointClient(config, request) {
|
|
7
|
+
return new MilkyV1Client({
|
|
8
|
+
baseUrl: config.baseUrl,
|
|
9
|
+
selfId: config.id,
|
|
10
|
+
accessToken: config.access_token,
|
|
11
|
+
receiveMode: 'manual',
|
|
12
|
+
call: (action, params) => request({
|
|
13
|
+
baseUrl: config.baseUrl,
|
|
14
|
+
access_token: config.access_token,
|
|
15
|
+
}, action, params),
|
|
16
|
+
});
|
|
17
|
+
}
|
|
18
|
+
/** Use the Client's complete protocol response while keeping Endpoint internals data-oriented. */
|
|
19
|
+
export async function callMilkyClient(client, action, params) {
|
|
20
|
+
const response = await client.call(action, params);
|
|
21
|
+
if (response.status !== 'ok') {
|
|
22
|
+
throw new Error(`Milky API ${action}: retcode=${response.retcode}${response.message ? ` ${response.message}` : ''}`);
|
|
23
|
+
}
|
|
24
|
+
return response.data;
|
|
25
|
+
}
|
|
26
|
+
const milkyClientEventNames = Object.freeze([
|
|
27
|
+
...EventFactory.getSupportedEventTypes(),
|
|
28
|
+
'event',
|
|
29
|
+
]);
|
|
30
|
+
export function forwardMilkyClientEvents(client, receive) {
|
|
31
|
+
return forwardEndpointClientEvents(client, milkyClientEventNames, receive);
|
|
32
|
+
}
|
|
33
|
+
export const milkyClient = defineEndpointClient('milky');
|
package/lib/endpoint.d.ts
CHANGED
|
@@ -3,5 +3,5 @@ export { MilkyWebhookEndpoint, type MilkyWebhookEndpointOptions, } from './webho
|
|
|
3
3
|
export { MilkyWssEndpoint, type MilkyWssEndpointOptions, } from './wss-endpoint.js';
|
|
4
4
|
export { MilkySseEndpoint, type MilkySseEndpointOptions, type CreateMilkySseStream, } from './sse-endpoint.js';
|
|
5
5
|
export type { MilkyWsSocket, MilkyWsCreateOptions } from './ws-types.js';
|
|
6
|
-
export { verifyMilkyAccessToken
|
|
6
|
+
export { verifyMilkyAccessToken } from './milky-auth.js';
|
|
7
7
|
export { openSseStream, consumeSseBuffer } from './sse-client.js';
|
package/lib/endpoint.js
CHANGED
|
@@ -2,5 +2,5 @@ export { MilkyWsEndpoint, } from './ws-endpoint.js';
|
|
|
2
2
|
export { MilkyWebhookEndpoint, } from './webhook-endpoint.js';
|
|
3
3
|
export { MilkyWssEndpoint, } from './wss-endpoint.js';
|
|
4
4
|
export { MilkySseEndpoint, } from './sse-endpoint.js';
|
|
5
|
-
export { verifyMilkyAccessToken
|
|
5
|
+
export { verifyMilkyAccessToken } from './milky-auth.js';
|
|
6
6
|
export { openSseStream, consumeSseBuffer } from './sse-client.js';
|
package/lib/index.d.ts
CHANGED
|
@@ -1,3 +1,6 @@
|
|
|
1
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
|
-
export { MilkySseEndpoint, MilkyWebhookEndpoint, MilkyWssEndpoint, MilkyWsEndpoint, consumeSseBuffer, openSseStream,
|
|
3
|
-
export {
|
|
2
|
+
export { MilkySseEndpoint, MilkyWebhookEndpoint, MilkyWssEndpoint, MilkyWsEndpoint, consumeSseBuffer, openSseStream, verifyMilkyAccessToken, type CreateMilkySseStream, type MilkySseEndpointOptions, type MilkyWebhookEndpointOptions, type MilkyWssEndpointOptions, type MilkyWsCreateOptions, type MilkyWsEndpointOptions, type MilkyWsSocket, } from './endpoint.js';
|
|
3
|
+
export { MilkyClient, milkyClient, type MilkyClientEventMap, } from './client.js';
|
|
4
|
+
export type { MilkyAdapterConfig as ImHelperMilkyAdapterConfig, MilkyActionUrlResolver, MilkyCall, MilkyMessageReceiveEvent, MilkyMessageRecallEvent, MilkyV1ClientConfig, MilkyV1Event, MilkyV1Response, } from '@imhelper/milky-v1';
|
|
5
|
+
export { ProtocolError } from '@imhelper/milky-v1';
|
|
6
|
+
export type { ProtocolErrorKind, ProtocolErrorOptions } from 'imhelper';
|
package/lib/index.js
CHANGED
|
@@ -1,3 +1,4 @@
|
|
|
1
1
|
export { buildSendAction, buildSseConnectOptions, buildWsConnectOptions, callApi, extractInboundAudioUrl, formatInboundContent, formatOutboundSegments, isMentioned, isMessageReceiveEvent, milkyInboundConversation, parseMessageReceiveData, resolveMilkyConfig, senderNickname, } from './protocol.js';
|
|
2
|
-
export { MilkySseEndpoint, MilkyWebhookEndpoint, MilkyWssEndpoint, MilkyWsEndpoint, consumeSseBuffer, openSseStream,
|
|
3
|
-
export {
|
|
2
|
+
export { MilkySseEndpoint, MilkyWebhookEndpoint, MilkyWssEndpoint, MilkyWsEndpoint, consumeSseBuffer, openSseStream, verifyMilkyAccessToken, } from './endpoint.js';
|
|
3
|
+
export { MilkyClient, milkyClient, } from './client.js';
|
|
4
|
+
export { ProtocolError } from '@imhelper/milky-v1';
|
package/lib/milky-auth.d.ts
CHANGED
package/lib/milky-auth.js
CHANGED
|
@@ -14,17 +14,3 @@ export function verifyMilkyAccessToken(accessToken, request) {
|
|
|
14
14
|
}
|
|
15
15
|
return false;
|
|
16
16
|
}
|
|
17
|
-
export async function readRequestBody(request) {
|
|
18
|
-
const chunks = [];
|
|
19
|
-
let size = 0;
|
|
20
|
-
for await (const chunk of request) {
|
|
21
|
-
const buffer = Buffer.isBuffer(chunk) ? chunk : Buffer.from(chunk);
|
|
22
|
-
size += buffer.length;
|
|
23
|
-
if (size > 1_048_576) {
|
|
24
|
-
request.destroy();
|
|
25
|
-
throw new Error('Request body exceeds 1MB');
|
|
26
|
-
}
|
|
27
|
-
chunks.push(buffer);
|
|
28
|
-
}
|
|
29
|
-
return Buffer.concat(chunks).toString('utf8');
|
|
30
|
-
}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
export declare const milkyEndpointCommands: import("@zhin.js/adapter").EndpointCommands<Readonly<import("@zhin.js/command").CommandDefinition<unknown, unknown, import("@zhin.js/command").CommandMessage>>>;
|
|
1
|
+
export declare const milkyEndpointCommands: import("@zhin.js/adapter").EndpointCommands<Readonly<import("@zhin.js/command").CommandDefinition<unknown, unknown, import("@zhin.js/command").CommandMessage, string | undefined>>>;
|
package/lib/protocol.d.ts
CHANGED
|
@@ -59,7 +59,7 @@ export interface MilkyWssConfig extends MilkyConfigBase {
|
|
|
59
59
|
export type ResolvedMilkyConfig = MilkyWsConfig | MilkySseConfig | MilkyWebhookConfig | MilkyWssConfig;
|
|
60
60
|
export type MilkyEndpointConfig = ResolvedMilkyConfig;
|
|
61
61
|
export interface MilkyApiResponse<T = unknown> {
|
|
62
|
-
status:
|
|
62
|
+
status: 'ok' | 'failed';
|
|
63
63
|
retcode: number;
|
|
64
64
|
data?: T;
|
|
65
65
|
message?: string;
|
|
@@ -111,9 +111,9 @@ export interface MilkyApiClientOptions {
|
|
|
111
111
|
export declare function resolveMilkyConfig(config?: MilkyAdapterConfig): ResolvedMilkyConfig;
|
|
112
112
|
/**
|
|
113
113
|
* 调用协议端 API:POST {baseUrl}/api/{apiName},Body JSON,鉴权。
|
|
114
|
-
*
|
|
114
|
+
* HTTP 层错误抛出;协议响应原样返回,由 Client 公开 call() 保留完整语义。
|
|
115
115
|
*/
|
|
116
|
-
export declare function callApi<T = unknown>(options: MilkyApiClientOptions, apiName: string, params?: Record<string, unknown>): Promise<T
|
|
116
|
+
export declare function callApi<T = unknown>(options: MilkyApiClientOptions, apiName: string, params?: Record<string, unknown>): Promise<MilkyApiResponse<T>>;
|
|
117
117
|
/** 根据 event_type 判断是否为 message_receive,并解析 data */
|
|
118
118
|
export declare function parseMessageReceiveData(event: MilkyEvent): MilkyIncomingMessage | null;
|
|
119
119
|
export declare function isMessageReceiveEvent(event: MilkyEvent): event is MilkyEvent & {
|
package/lib/protocol.js
CHANGED
|
@@ -80,7 +80,7 @@ function authQuery(access_token) {
|
|
|
80
80
|
}
|
|
81
81
|
/**
|
|
82
82
|
* 调用协议端 API:POST {baseUrl}/api/{apiName},Body JSON,鉴权。
|
|
83
|
-
*
|
|
83
|
+
* HTTP 层错误抛出;协议响应原样返回,由 Client 公开 call() 保留完整语义。
|
|
84
84
|
*/
|
|
85
85
|
export async function callApi(options, apiName, params = {}) {
|
|
86
86
|
const { baseUrl, access_token } = options;
|
|
@@ -107,10 +107,7 @@ export async function callApi(options, apiName, params = {}) {
|
|
|
107
107
|
if (res.status !== 200) {
|
|
108
108
|
throw new Error(`Milky API ${apiName}: HTTP ${res.status} ${body.message ?? text}`);
|
|
109
109
|
}
|
|
110
|
-
|
|
111
|
-
throw new Error(`Milky API ${apiName}: retcode=${body.retcode} ${body.message ?? ''}`);
|
|
112
|
-
}
|
|
113
|
-
return (body.data ?? {});
|
|
110
|
+
return body;
|
|
114
111
|
}
|
|
115
112
|
/** 根据 event_type 判断是否为 message_receive,并解析 data */
|
|
116
113
|
export function parseMessageReceiveData(event) {
|
package/lib/sse-endpoint.d.ts
CHANGED
|
@@ -1,10 +1,10 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* Milky SSE client endpoint — GET text/event-stream on /event.
|
|
3
3
|
*/
|
|
4
|
-
import { type
|
|
5
|
-
import type { MessageGateway, SideEventGateway } from '@zhin.js/core/runtime';
|
|
4
|
+
import { ClientEndpoint, type EndpointControl, type EndpointManagement, type EndpointSendRequest } from 'zhin.js/adapter';
|
|
6
5
|
import type { CapabilityId } from 'zhin.js';
|
|
7
|
-
import {
|
|
6
|
+
import { type MilkyClient } from './client.js';
|
|
7
|
+
import { callApi, type MilkySseConfig } from './protocol.js';
|
|
8
8
|
import { type SseClientHandle } from './sse-client.js';
|
|
9
9
|
export type CreateMilkySseStream = (options: {
|
|
10
10
|
readonly url: string;
|
|
@@ -15,35 +15,18 @@ export type CreateMilkySseStream = (options: {
|
|
|
15
15
|
}) => SseClientHandle;
|
|
16
16
|
export interface MilkySseEndpointOptions {
|
|
17
17
|
readonly id: CapabilityId;
|
|
18
|
-
readonly gateway: MessageGateway;
|
|
19
|
-
readonly sideEvents?: SideEventGateway;
|
|
20
18
|
readonly config: MilkySseConfig;
|
|
21
19
|
readonly createSseStream?: CreateMilkySseStream;
|
|
22
20
|
readonly callApi?: typeof callApi;
|
|
23
21
|
}
|
|
24
|
-
export declare class MilkySseEndpoint
|
|
22
|
+
export declare class MilkySseEndpoint extends ClientEndpoint<MilkyClient> {
|
|
25
23
|
#private;
|
|
24
|
+
readonly client: MilkyClient;
|
|
26
25
|
readonly management: EndpointManagement;
|
|
26
|
+
readonly control: EndpointControl;
|
|
27
27
|
constructor(options: MilkySseEndpointOptions);
|
|
28
28
|
start(): Promise<void>;
|
|
29
|
-
open(): void;
|
|
30
|
-
close(): void;
|
|
31
29
|
stop(): Promise<void>;
|
|
32
30
|
send({ conversation, payload }: EndpointSendRequest): Promise<string>;
|
|
33
|
-
callApi(action: string, params?: Record<string, unknown>): Promise<unknown>;
|
|
34
31
|
recallMessage(id: string): Promise<void>;
|
|
35
|
-
kickMember(groupId: number, userId: number, rejectAddRequest?: boolean): Promise<boolean>;
|
|
36
|
-
muteMember(groupId: number, userId: number, duration?: number): Promise<boolean>;
|
|
37
|
-
muteAll(groupId: number, enable?: boolean): Promise<boolean>;
|
|
38
|
-
setAdmin(groupId: number, userId: number, enable?: boolean): Promise<boolean>;
|
|
39
|
-
setCard(groupId: number, userId: number, card: string): Promise<boolean>;
|
|
40
|
-
setTitle(groupId: number, userId: number, title: string): Promise<boolean>;
|
|
41
|
-
setGroupName(groupId: number, name: string): Promise<boolean>;
|
|
42
|
-
getMemberList(groupId: number): Promise<unknown[]>;
|
|
43
|
-
getGroupInfo(groupId: number): Promise<unknown>;
|
|
44
|
-
admit(event: MilkyEvent): void;
|
|
45
|
-
apiOptions(): {
|
|
46
|
-
baseUrl: string;
|
|
47
|
-
access_token?: string;
|
|
48
|
-
};
|
|
49
32
|
}
|
package/lib/sse-endpoint.js
CHANGED
|
@@ -1,25 +1,34 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* Milky SSE client endpoint — GET text/event-stream on /event.
|
|
3
3
|
*/
|
|
4
|
-
import { createEndpointLifecycle, } from 'zhin.js/adapter';
|
|
4
|
+
import { ClientEndpoint, createRecallEndpointControl, createEndpointLifecycle, } from 'zhin.js/adapter';
|
|
5
5
|
import { formatCompact, getAdapterLogger } from '@zhin.js/logger';
|
|
6
6
|
import { createMilkyEndpointManagement } from './endpoint-management.js';
|
|
7
|
-
import {
|
|
7
|
+
import { callMilkyClient, createMilkyEndpointClient, forwardMilkyClientEvents } from './client.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
|
-
export class MilkySseEndpoint {
|
|
10
|
+
export class MilkySseEndpoint extends ClientEndpoint {
|
|
11
|
+
client;
|
|
11
12
|
#logger;
|
|
12
13
|
#options;
|
|
13
14
|
#callApi;
|
|
14
|
-
management
|
|
15
|
+
management;
|
|
16
|
+
control = createRecallEndpointControl((id) => this.recallMessage(id));
|
|
15
17
|
#lifecycle;
|
|
16
18
|
#stream;
|
|
17
|
-
#open = false;
|
|
18
|
-
#unregisterAgent;
|
|
19
19
|
constructor(options) {
|
|
20
|
+
super();
|
|
20
21
|
this.#logger = getAdapterLogger('milky', options.config.id);
|
|
21
22
|
this.#options = options;
|
|
22
23
|
this.#callApi = options.callApi ?? callApi;
|
|
24
|
+
this.client = createMilkyEndpointClient(options.config, this.#callApi);
|
|
25
|
+
this.management = createMilkyEndpointManagement({
|
|
26
|
+
callApi: (action, params) => callMilkyClient(this.client, action, params),
|
|
27
|
+
});
|
|
28
|
+
this.bindClientEvents((receive) => forwardMilkyClientEvents(this.client, receive), (name, payload) => {
|
|
29
|
+
if (name === 'event')
|
|
30
|
+
this.#admitRaw(payload);
|
|
31
|
+
}, (_name, error) => this.#warnPlatformEvent(error));
|
|
23
32
|
this.#lifecycle = createEndpointLifecycle({
|
|
24
33
|
name: options.config.id,
|
|
25
34
|
// reconnect_interval 旧语义为固定间隔:multiplier 1 + 无 jitter + 不封顶
|
|
@@ -34,35 +43,24 @@ export class MilkySseEndpoint {
|
|
|
34
43
|
async start() {
|
|
35
44
|
if (this.#lifecycle.started)
|
|
36
45
|
return;
|
|
37
|
-
this.#unregisterAgent = registerMilkyAgentEndpoint(this.#options.config.id, this);
|
|
38
46
|
try {
|
|
39
47
|
await this.#lifecycle.start((handle) => this.#connect(handle));
|
|
40
48
|
}
|
|
41
49
|
catch (err) {
|
|
42
50
|
// 与 WS 对齐:start 失败复位由基座保证;agent 反注册留在适配器侧,允许重试
|
|
43
|
-
this.#unregisterAgent?.();
|
|
44
|
-
this.#unregisterAgent = undefined;
|
|
45
51
|
throw err;
|
|
46
52
|
}
|
|
47
53
|
}
|
|
48
|
-
open() {
|
|
49
|
-
this.#open = true;
|
|
50
|
-
}
|
|
51
|
-
close() {
|
|
52
|
-
this.#open = false;
|
|
53
|
-
}
|
|
54
54
|
async stop() {
|
|
55
|
-
this
|
|
55
|
+
this.close();
|
|
56
56
|
await this.#lifecycle.stop();
|
|
57
|
-
this.#unregisterAgent?.();
|
|
58
|
-
this.#unregisterAgent = undefined;
|
|
59
57
|
this.#stream?.close();
|
|
60
58
|
this.#stream = undefined;
|
|
61
59
|
}
|
|
62
60
|
async send({ conversation, payload }) {
|
|
63
61
|
const message = formatOutboundSegments(payload);
|
|
64
62
|
const { action, params } = buildSendAction(conversation, message);
|
|
65
|
-
const data = await this.
|
|
63
|
+
const data = await callMilkyClient(this.client, action, params);
|
|
66
64
|
const messageId = formatOutboundMessageId(conversation, data?.message_seq);
|
|
67
65
|
this.#logger.debug(formatCompact({
|
|
68
66
|
op: 'milky_send',
|
|
@@ -73,91 +71,31 @@ export class MilkySseEndpoint {
|
|
|
73
71
|
}));
|
|
74
72
|
return messageId;
|
|
75
73
|
}
|
|
76
|
-
callApi(action, params = {}) {
|
|
77
|
-
return this.#callApi(this.apiOptions(), action, params);
|
|
78
|
-
}
|
|
79
74
|
async recallMessage(id) {
|
|
80
75
|
const parsed = parseMilkyMessageId(id);
|
|
81
76
|
if (!parsed)
|
|
82
77
|
throw new Error(`Invalid message id: ${id}`);
|
|
83
78
|
if (parsed.message_scene === 'group') {
|
|
84
|
-
await this.
|
|
79
|
+
await callMilkyClient(this.client, 'recall_group_message', {
|
|
85
80
|
group_id: parsed.peer_id,
|
|
86
81
|
message_seq: parsed.message_seq,
|
|
87
82
|
});
|
|
88
83
|
}
|
|
89
84
|
else {
|
|
90
|
-
await this.
|
|
85
|
+
await callMilkyClient(this.client, 'recall_private_message', {
|
|
91
86
|
user_id: parsed.peer_id,
|
|
92
87
|
message_seq: parsed.message_seq,
|
|
93
88
|
});
|
|
94
89
|
}
|
|
95
90
|
}
|
|
96
|
-
|
|
97
|
-
await this.callApi('kick_group_member', {
|
|
98
|
-
group_id: groupId,
|
|
99
|
-
user_id: userId,
|
|
100
|
-
reject_add_request: rejectAddRequest,
|
|
101
|
-
});
|
|
102
|
-
return true;
|
|
103
|
-
}
|
|
104
|
-
async muteMember(groupId, userId, duration = 600) {
|
|
105
|
-
await this.callApi('set_group_member_mute', {
|
|
106
|
-
group_id: groupId,
|
|
107
|
-
user_id: userId,
|
|
108
|
-
duration,
|
|
109
|
-
});
|
|
110
|
-
return true;
|
|
111
|
-
}
|
|
112
|
-
async muteAll(groupId, enable = true) {
|
|
113
|
-
await this.callApi('set_group_whole_mute', { group_id: groupId, is_mute: enable });
|
|
114
|
-
return true;
|
|
115
|
-
}
|
|
116
|
-
async setAdmin(groupId, userId, enable = true) {
|
|
117
|
-
await this.callApi('set_group_member_admin', {
|
|
118
|
-
group_id: groupId,
|
|
119
|
-
user_id: userId,
|
|
120
|
-
is_set: enable,
|
|
121
|
-
});
|
|
122
|
-
return true;
|
|
123
|
-
}
|
|
124
|
-
async setCard(groupId, userId, card) {
|
|
125
|
-
await this.callApi('set_group_member_card', {
|
|
126
|
-
group_id: groupId,
|
|
127
|
-
user_id: userId,
|
|
128
|
-
card,
|
|
129
|
-
});
|
|
130
|
-
return true;
|
|
131
|
-
}
|
|
132
|
-
async setTitle(groupId, userId, title) {
|
|
133
|
-
await this.callApi('set_group_member_special_title', {
|
|
134
|
-
group_id: groupId,
|
|
135
|
-
user_id: userId,
|
|
136
|
-
special_title: title,
|
|
137
|
-
});
|
|
138
|
-
return true;
|
|
139
|
-
}
|
|
140
|
-
async setGroupName(groupId, name) {
|
|
141
|
-
await this.callApi('set_group_name', { group_id: groupId, new_group_name: name });
|
|
142
|
-
return true;
|
|
143
|
-
}
|
|
144
|
-
async getMemberList(groupId) {
|
|
145
|
-
return this.callApi('get_group_member_list', { group_id: groupId });
|
|
146
|
-
}
|
|
147
|
-
async getGroupInfo(groupId) {
|
|
148
|
-
return this.callApi('get_group_info', { group_id: groupId });
|
|
149
|
-
}
|
|
150
|
-
admit(event) {
|
|
91
|
+
#admitRaw(event) {
|
|
151
92
|
const data = parseMessageReceiveData(event);
|
|
152
|
-
if (!
|
|
93
|
+
if (!data)
|
|
153
94
|
return;
|
|
154
95
|
this.#admitMessage(data, event);
|
|
155
96
|
}
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
baseUrl: this.#options.config.baseUrl,
|
|
159
|
-
access_token: this.#options.config.access_token,
|
|
160
|
-
};
|
|
97
|
+
#warnPlatformEvent(error) {
|
|
98
|
+
this.#logger.warn(formatCompact({ op: 'milky_platform_event_failed', error: error instanceof Error ? error.message : String(error) }));
|
|
161
99
|
}
|
|
162
100
|
#admitMessage(data, event) {
|
|
163
101
|
const conversation = milkyInboundConversation(String(this.#options.id), data);
|
|
@@ -167,7 +105,7 @@ export class MilkySseEndpoint {
|
|
|
167
105
|
const audioUrl = extractInboundAudioUrl(data);
|
|
168
106
|
const nickname = senderNickname(data);
|
|
169
107
|
const mentioned = isMentioned(data, event.self_id);
|
|
170
|
-
void this
|
|
108
|
+
void this.emit('message.receive', {
|
|
171
109
|
conversation,
|
|
172
110
|
message: { conversation, id: formatInboundMessageId(data) },
|
|
173
111
|
content,
|
|
@@ -255,7 +193,7 @@ export class MilkySseEndpoint {
|
|
|
255
193
|
#onMessage(data) {
|
|
256
194
|
try {
|
|
257
195
|
const event = JSON.parse(data);
|
|
258
|
-
this.
|
|
196
|
+
this.client.ingest(event);
|
|
259
197
|
}
|
|
260
198
|
catch (error) {
|
|
261
199
|
this.#logger.warn(formatCompact({
|