@zhin.js/adapter-milky 7.0.0 → 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 +23 -0
- package/README.md +24 -3
- package/adapters/milky.js +0 -11
- package/adapters/milky.ts +0 -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 +5 -23
- package/lib/sse-endpoint.js +24 -87
- package/lib/webhook-endpoint.d.ts +5 -23
- package/lib/webhook-endpoint.js +28 -97
- package/lib/ws-endpoint.d.ts +5 -24
- package/lib/ws-endpoint.js +35 -116
- package/lib/wss-endpoint.d.ts +8 -23
- package/lib/wss-endpoint.js +64 -149
- 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 +27 -102
- package/src/webhook-endpoint.ts +31 -110
- package/src/ws-endpoint.ts +43 -128
- package/src/wss-endpoint.ts +67 -158
- 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/lib/wss-endpoint.js
CHANGED
|
@@ -1,39 +1,62 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* Milky reverse WSS endpoint — httpHostToken WS upgrade inbound + baseUrl HTTP API outbound.
|
|
3
3
|
*/
|
|
4
|
-
import {
|
|
5
|
-
import { createRecallEndpointControl, } from 'zhin.js/adapter';
|
|
4
|
+
import { ClientEndpoint, createEndpointLifecycle, createRecallEndpointControl, } from 'zhin.js/adapter';
|
|
6
5
|
import { formatCompact, getAdapterLogger } from '@zhin.js/logger';
|
|
7
6
|
import { verifyMilkyAccessToken } from './milky-auth.js';
|
|
8
7
|
import { createMilkyEndpointManagement } from './endpoint-management.js';
|
|
9
|
-
import {
|
|
8
|
+
import { callMilkyClient, createMilkyEndpointClient, forwardMilkyClientEvents } from './client.js';
|
|
10
9
|
import { buildSendAction, callApi, extractInboundAudioUrl, formatInboundContent, formatInboundMessageId, formatInboundSegments, formatOutboundMessageId, formatOutboundSegments, isMentioned, milkyInboundConversation, parseMessageReceiveData, parseMilkyMessageId, senderNickname, } from './protocol.js';
|
|
11
10
|
const WS_OPEN = 1;
|
|
12
|
-
export class MilkyWssEndpoint {
|
|
11
|
+
export class MilkyWssEndpoint extends ClientEndpoint {
|
|
12
|
+
client;
|
|
13
13
|
#logger;
|
|
14
14
|
#options;
|
|
15
15
|
#callApi;
|
|
16
|
-
management
|
|
16
|
+
management;
|
|
17
17
|
control = createRecallEndpointControl((id) => this.recallMessage(id));
|
|
18
18
|
#ws;
|
|
19
19
|
#wsRelease;
|
|
20
|
-
#
|
|
21
|
-
#
|
|
22
|
-
#started = false;
|
|
23
|
-
#unregisterAgent;
|
|
20
|
+
#clientSocketRelease;
|
|
21
|
+
#lifecycle;
|
|
24
22
|
constructor(options) {
|
|
23
|
+
super();
|
|
25
24
|
this.#logger = getAdapterLogger('milky', options.config.id);
|
|
26
25
|
this.#options = options;
|
|
27
26
|
this.#callApi = options.callApi ?? callApi;
|
|
27
|
+
this.#lifecycle = createEndpointLifecycle({
|
|
28
|
+
name: options.config.id,
|
|
29
|
+
reconnect: false,
|
|
30
|
+
heartbeat: { intervalMs: options.config.heartbeat_interval },
|
|
31
|
+
});
|
|
32
|
+
this.client = createMilkyEndpointClient(options.config, this.#callApi);
|
|
33
|
+
this.management = createMilkyEndpointManagement({
|
|
34
|
+
callApi: (action, params) => callMilkyClient(this.client, action, params),
|
|
35
|
+
});
|
|
36
|
+
this.bindClientEvents((receive) => forwardMilkyClientEvents(this.client, receive), (name, payload) => {
|
|
37
|
+
if (name === 'event')
|
|
38
|
+
this.#admitRaw(payload);
|
|
39
|
+
}, (_name, error) => this.#warnPlatformEvent(error));
|
|
28
40
|
}
|
|
29
41
|
async start() {
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
42
|
+
await this.#lifecycle.start(async (lifecycleHandle) => {
|
|
43
|
+
const handle = this.#options.http.ws(this.#options.config.path);
|
|
44
|
+
this.#wsRelease = handle.onConnection((connection) => {
|
|
45
|
+
this.#acceptConnection(connection);
|
|
46
|
+
});
|
|
47
|
+
lifecycleHandle.onForceClose(() => {
|
|
48
|
+
this.#wsRelease?.();
|
|
49
|
+
this.#wsRelease = undefined;
|
|
50
|
+
this.#clientSocketRelease?.();
|
|
51
|
+
this.#clientSocketRelease = undefined;
|
|
52
|
+
try {
|
|
53
|
+
this.#ws?.close();
|
|
54
|
+
}
|
|
55
|
+
catch {
|
|
56
|
+
/* ignore */
|
|
57
|
+
}
|
|
58
|
+
this.#ws = undefined;
|
|
59
|
+
});
|
|
37
60
|
});
|
|
38
61
|
this.#logger.info(formatCompact({
|
|
39
62
|
op: 'listen',
|
|
@@ -42,37 +65,14 @@ export class MilkyWssEndpoint {
|
|
|
42
65
|
path: this.#options.config.path,
|
|
43
66
|
}));
|
|
44
67
|
}
|
|
45
|
-
open() {
|
|
46
|
-
this.#open = true;
|
|
47
|
-
}
|
|
48
|
-
close() {
|
|
49
|
-
this.#open = false;
|
|
50
|
-
}
|
|
51
68
|
async stop() {
|
|
52
|
-
this
|
|
53
|
-
this.#
|
|
54
|
-
this.#unregisterAgent = undefined;
|
|
55
|
-
this.#wsRelease?.();
|
|
56
|
-
this.#wsRelease = undefined;
|
|
57
|
-
if (this.#heartbeatTimer) {
|
|
58
|
-
clearInterval(this.#heartbeatTimer);
|
|
59
|
-
this.#heartbeatTimer = undefined;
|
|
60
|
-
}
|
|
61
|
-
if (this.#ws) {
|
|
62
|
-
try {
|
|
63
|
-
this.#ws.close();
|
|
64
|
-
}
|
|
65
|
-
catch {
|
|
66
|
-
/* ignore */
|
|
67
|
-
}
|
|
68
|
-
this.#ws = undefined;
|
|
69
|
-
}
|
|
70
|
-
this.#started = false;
|
|
69
|
+
this.close();
|
|
70
|
+
await this.#lifecycle.stop();
|
|
71
71
|
}
|
|
72
72
|
async send({ conversation, payload }) {
|
|
73
73
|
const message = formatOutboundSegments(payload);
|
|
74
74
|
const { action, params } = buildSendAction(conversation, message);
|
|
75
|
-
const data = await this.
|
|
75
|
+
const data = await callMilkyClient(this.client, action, params);
|
|
76
76
|
const messageId = formatOutboundMessageId(conversation, data?.message_seq);
|
|
77
77
|
this.#logger.debug(formatCompact({
|
|
78
78
|
op: 'milky_send',
|
|
@@ -82,91 +82,31 @@ export class MilkyWssEndpoint {
|
|
|
82
82
|
}));
|
|
83
83
|
return messageId;
|
|
84
84
|
}
|
|
85
|
-
callApi(action, params = {}) {
|
|
86
|
-
return this.#callApi(this.apiOptions(), action, params);
|
|
87
|
-
}
|
|
88
85
|
async recallMessage(id) {
|
|
89
86
|
const parsed = parseMilkyMessageId(id);
|
|
90
87
|
if (!parsed)
|
|
91
88
|
throw new Error(`Invalid message id: ${id}`);
|
|
92
89
|
if (parsed.message_scene === 'group') {
|
|
93
|
-
await this.
|
|
90
|
+
await callMilkyClient(this.client, 'recall_group_message', {
|
|
94
91
|
group_id: parsed.peer_id,
|
|
95
92
|
message_seq: parsed.message_seq,
|
|
96
93
|
});
|
|
97
94
|
}
|
|
98
95
|
else {
|
|
99
|
-
await this.
|
|
96
|
+
await callMilkyClient(this.client, 'recall_private_message', {
|
|
100
97
|
user_id: parsed.peer_id,
|
|
101
98
|
message_seq: parsed.message_seq,
|
|
102
99
|
});
|
|
103
100
|
}
|
|
104
101
|
}
|
|
105
|
-
|
|
106
|
-
await this.callApi('kick_group_member', {
|
|
107
|
-
group_id: groupId,
|
|
108
|
-
user_id: userId,
|
|
109
|
-
reject_add_request: rejectAddRequest,
|
|
110
|
-
});
|
|
111
|
-
return true;
|
|
112
|
-
}
|
|
113
|
-
async muteMember(groupId, userId, duration = 600) {
|
|
114
|
-
await this.callApi('set_group_member_mute', {
|
|
115
|
-
group_id: groupId,
|
|
116
|
-
user_id: userId,
|
|
117
|
-
duration,
|
|
118
|
-
});
|
|
119
|
-
return true;
|
|
120
|
-
}
|
|
121
|
-
async muteAll(groupId, enable = true) {
|
|
122
|
-
await this.callApi('set_group_whole_mute', { group_id: groupId, is_mute: enable });
|
|
123
|
-
return true;
|
|
124
|
-
}
|
|
125
|
-
async setAdmin(groupId, userId, enable = true) {
|
|
126
|
-
await this.callApi('set_group_member_admin', {
|
|
127
|
-
group_id: groupId,
|
|
128
|
-
user_id: userId,
|
|
129
|
-
is_set: enable,
|
|
130
|
-
});
|
|
131
|
-
return true;
|
|
132
|
-
}
|
|
133
|
-
async setCard(groupId, userId, card) {
|
|
134
|
-
await this.callApi('set_group_member_card', {
|
|
135
|
-
group_id: groupId,
|
|
136
|
-
user_id: userId,
|
|
137
|
-
card,
|
|
138
|
-
});
|
|
139
|
-
return true;
|
|
140
|
-
}
|
|
141
|
-
async setTitle(groupId, userId, title) {
|
|
142
|
-
await this.callApi('set_group_member_special_title', {
|
|
143
|
-
group_id: groupId,
|
|
144
|
-
user_id: userId,
|
|
145
|
-
special_title: title,
|
|
146
|
-
});
|
|
147
|
-
return true;
|
|
148
|
-
}
|
|
149
|
-
async setGroupName(groupId, name) {
|
|
150
|
-
await this.callApi('set_group_name', { group_id: groupId, new_group_name: name });
|
|
151
|
-
return true;
|
|
152
|
-
}
|
|
153
|
-
async getMemberList(groupId) {
|
|
154
|
-
return this.callApi('get_group_member_list', { group_id: groupId });
|
|
155
|
-
}
|
|
156
|
-
async getGroupInfo(groupId) {
|
|
157
|
-
return this.callApi('get_group_info', { group_id: groupId });
|
|
158
|
-
}
|
|
159
|
-
admit(event) {
|
|
102
|
+
#admitRaw(event) {
|
|
160
103
|
const data = parseMessageReceiveData(event);
|
|
161
|
-
if (!
|
|
104
|
+
if (!data)
|
|
162
105
|
return;
|
|
163
106
|
this.#admitMessage(data, event);
|
|
164
107
|
}
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
baseUrl: this.#options.config.baseUrl,
|
|
168
|
-
access_token: this.#options.config.access_token,
|
|
169
|
-
};
|
|
108
|
+
#warnPlatformEvent(error) {
|
|
109
|
+
this.#logger.warn(formatCompact({ op: 'milky_platform_event_failed', error: error instanceof Error ? error.message : String(error) }));
|
|
170
110
|
}
|
|
171
111
|
#admitMessage(data, event) {
|
|
172
112
|
const conversation = milkyInboundConversation(String(this.#options.id), data);
|
|
@@ -176,7 +116,7 @@ export class MilkyWssEndpoint {
|
|
|
176
116
|
const audioUrl = extractInboundAudioUrl(data);
|
|
177
117
|
const nickname = senderNickname(data);
|
|
178
118
|
const mentioned = isMentioned(data, event.self_id);
|
|
179
|
-
void this
|
|
119
|
+
void this.emit('message.receive', {
|
|
180
120
|
conversation,
|
|
181
121
|
message: { conversation, id: formatInboundMessageId(data) },
|
|
182
122
|
content,
|
|
@@ -216,13 +156,24 @@ export class MilkyWssEndpoint {
|
|
|
216
156
|
}
|
|
217
157
|
}
|
|
218
158
|
this.#ws = socket;
|
|
219
|
-
this.#startHeartbeat()
|
|
220
|
-
|
|
221
|
-
|
|
159
|
+
this.#lifecycle.startHeartbeat(() => {
|
|
160
|
+
try {
|
|
161
|
+
if (this.#ws?.readyState === WS_OPEN)
|
|
162
|
+
this.#ws.ping?.();
|
|
163
|
+
}
|
|
164
|
+
catch {
|
|
165
|
+
/* ignore */
|
|
166
|
+
}
|
|
222
167
|
});
|
|
168
|
+
this.#clientSocketRelease?.();
|
|
169
|
+
this.#clientSocketRelease = this.client.acceptWebSocket(socket);
|
|
223
170
|
socket.on('close', () => {
|
|
224
|
-
|
|
171
|
+
this.#clientSocketRelease?.();
|
|
172
|
+
this.#clientSocketRelease = undefined;
|
|
173
|
+
if (this.#ws === socket) {
|
|
225
174
|
this.#ws = undefined;
|
|
175
|
+
this.#lifecycle.stopHeartbeat();
|
|
176
|
+
}
|
|
226
177
|
});
|
|
227
178
|
this.#logger.debug(formatCompact({
|
|
228
179
|
endpoint: this.#options.config.id,
|
|
@@ -230,40 +181,4 @@ export class MilkyWssEndpoint {
|
|
|
230
181
|
peer: connection.request.socket.remoteAddress,
|
|
231
182
|
}));
|
|
232
183
|
}
|
|
233
|
-
#onMessage(data) {
|
|
234
|
-
try {
|
|
235
|
-
const raw = typeof data === 'string'
|
|
236
|
-
? data
|
|
237
|
-
: Buffer.isBuffer(data)
|
|
238
|
-
? data.toString()
|
|
239
|
-
: data instanceof ArrayBuffer
|
|
240
|
-
? new TextDecoder().decode(data)
|
|
241
|
-
: String(data ?? '');
|
|
242
|
-
const event = JSON.parse(raw);
|
|
243
|
-
this.admit(event);
|
|
244
|
-
}
|
|
245
|
-
catch (error) {
|
|
246
|
-
this.#logger.warn(formatCompact({
|
|
247
|
-
op: 'milky_parse_failed',
|
|
248
|
-
endpoint: this.#options.config.id,
|
|
249
|
-
error: error instanceof Error ? error.message : String(error),
|
|
250
|
-
}));
|
|
251
|
-
}
|
|
252
|
-
}
|
|
253
|
-
#startHeartbeat() {
|
|
254
|
-
if (this.#heartbeatTimer)
|
|
255
|
-
clearInterval(this.#heartbeatTimer);
|
|
256
|
-
const interval = this.#options.config.heartbeat_interval;
|
|
257
|
-
if (interval <= 0)
|
|
258
|
-
return;
|
|
259
|
-
this.#heartbeatTimer = setInterval(() => {
|
|
260
|
-
try {
|
|
261
|
-
if (this.#ws?.readyState === WS_OPEN)
|
|
262
|
-
this.#ws.ping?.();
|
|
263
|
-
}
|
|
264
|
-
catch {
|
|
265
|
-
/* ignore */
|
|
266
|
-
}
|
|
267
|
-
}, interval);
|
|
268
|
-
}
|
|
269
184
|
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@zhin.js/adapter-milky",
|
|
3
|
-
"version": "7.0.
|
|
3
|
+
"version": "7.0.1",
|
|
4
4
|
"description": "Zhin.js Milky adapter for Plugin Runtime (WebSocket client)",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "./lib/index.js",
|
|
@@ -39,12 +39,15 @@
|
|
|
39
39
|
"directory": "plugins/adapters/milky"
|
|
40
40
|
},
|
|
41
41
|
"dependencies": {
|
|
42
|
+
"@imhelper/milky-v1": "1.0.7",
|
|
43
|
+
"imhelper": "1.0.7",
|
|
42
44
|
"ws": "^8.21.1",
|
|
43
|
-
"@zhin.js/adapter": "1.2.
|
|
44
|
-
"@zhin.js/core": "1.5.
|
|
45
|
-
"@zhin.js/
|
|
45
|
+
"@zhin.js/adapter": "1.2.1",
|
|
46
|
+
"@zhin.js/core": "1.5.14",
|
|
47
|
+
"@zhin.js/feature-kit": "1.0.13",
|
|
48
|
+
"@zhin.js/host-http": "1.0.13",
|
|
46
49
|
"@zhin.js/im-contract": "1.0.4",
|
|
47
|
-
"@zhin.js/logger": "1.0.
|
|
50
|
+
"@zhin.js/logger": "1.0.77"
|
|
48
51
|
},
|
|
49
52
|
"devDependencies": {
|
|
50
53
|
"@types/node": "^26.1.2",
|
|
@@ -52,17 +55,17 @@
|
|
|
52
55
|
"typescript": "^6.0.3",
|
|
53
56
|
"vitest": "^4.1.10",
|
|
54
57
|
"zod": "^4.4.3",
|
|
55
|
-
"@zhin.js/agent": "1.1.
|
|
56
|
-
"@zhin.js/host-http": "1.0.
|
|
57
|
-
"zhin.js": "6.0.
|
|
58
|
+
"@zhin.js/agent": "1.1.16",
|
|
59
|
+
"@zhin.js/host-http": "1.0.13",
|
|
60
|
+
"zhin.js": "6.0.14"
|
|
58
61
|
},
|
|
59
62
|
"peerDependencies": {
|
|
60
63
|
"zod": "^4.0.0",
|
|
61
|
-
"@zhin.js/adapter": "1.2.
|
|
62
|
-
"@zhin.js/agent": "1.1.
|
|
63
|
-
"@zhin.js/command": "1.0.
|
|
64
|
-
"@zhin.js/core": "1.5.
|
|
65
|
-
"zhin.js": "6.0.
|
|
64
|
+
"@zhin.js/adapter": "1.2.1",
|
|
65
|
+
"@zhin.js/agent": "1.1.16",
|
|
66
|
+
"@zhin.js/command": "1.0.16",
|
|
67
|
+
"@zhin.js/core": "1.5.14",
|
|
68
|
+
"zhin.js": "6.0.14"
|
|
66
69
|
},
|
|
67
70
|
"peerDependenciesMeta": {
|
|
68
71
|
"@zhin.js/agent": {
|
package/src/client.ts
ADDED
|
@@ -0,0 +1,80 @@
|
|
|
1
|
+
import {
|
|
2
|
+
MilkyV1Client,
|
|
3
|
+
type MilkyV1Event,
|
|
4
|
+
type MilkyV1Response,
|
|
5
|
+
} from '@imhelper/milky-v1';
|
|
6
|
+
import {
|
|
7
|
+
EventFactory,
|
|
8
|
+
type EventMap,
|
|
9
|
+
type ImHelperEventMap,
|
|
10
|
+
} from 'imhelper';
|
|
11
|
+
import {
|
|
12
|
+
defineEndpointClient,
|
|
13
|
+
forwardEndpointClientEvents,
|
|
14
|
+
type ClientEventPayloads,
|
|
15
|
+
} from 'zhin.js/adapter';
|
|
16
|
+
import type { ResolvedMilkyConfig, callApi } from './protocol.js';
|
|
17
|
+
|
|
18
|
+
export { MilkyV1Client as MilkyClient } from '@imhelper/milky-v1';
|
|
19
|
+
|
|
20
|
+
type MilkyClientEvents = ImHelperEventMap<string, MilkyV1Event, EventMap<string>>;
|
|
21
|
+
export type MilkyClientEventMap = ClientEventPayloads<MilkyClientEvents>;
|
|
22
|
+
|
|
23
|
+
/** Construct the exact public imhelper Client without handing it transport ownership. */
|
|
24
|
+
export function createMilkyEndpointClient(
|
|
25
|
+
config: ResolvedMilkyConfig,
|
|
26
|
+
request: typeof callApi,
|
|
27
|
+
): MilkyV1Client {
|
|
28
|
+
return new MilkyV1Client({
|
|
29
|
+
baseUrl: config.baseUrl,
|
|
30
|
+
selfId: config.id,
|
|
31
|
+
accessToken: config.access_token,
|
|
32
|
+
receiveMode: 'manual',
|
|
33
|
+
call: (action, params) => request(
|
|
34
|
+
{
|
|
35
|
+
baseUrl: config.baseUrl,
|
|
36
|
+
access_token: config.access_token,
|
|
37
|
+
},
|
|
38
|
+
action,
|
|
39
|
+
params,
|
|
40
|
+
),
|
|
41
|
+
});
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
/** Use the Client's complete protocol response while keeping Endpoint internals data-oriented. */
|
|
45
|
+
export async function callMilkyClient<T = unknown>(
|
|
46
|
+
client: MilkyV1Client,
|
|
47
|
+
action: string,
|
|
48
|
+
params?: Record<string, unknown>,
|
|
49
|
+
): Promise<T | undefined> {
|
|
50
|
+
const response: MilkyV1Response<T> = await client.call<T>(action, params);
|
|
51
|
+
if (response.status !== 'ok') {
|
|
52
|
+
throw new Error(
|
|
53
|
+
`Milky API ${action}: retcode=${response.retcode}${response.message ? ` ${response.message}` : ''}`,
|
|
54
|
+
);
|
|
55
|
+
}
|
|
56
|
+
return response.data;
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
const milkyClientEventNames = Object.freeze([
|
|
60
|
+
...EventFactory.getSupportedEventTypes<string>(),
|
|
61
|
+
'event',
|
|
62
|
+
]);
|
|
63
|
+
|
|
64
|
+
export function forwardMilkyClientEvents(
|
|
65
|
+
client: MilkyV1Client,
|
|
66
|
+
receive: (name: string, payload: unknown) => void,
|
|
67
|
+
): () => void {
|
|
68
|
+
return forwardEndpointClientEvents(client, milkyClientEventNames, receive);
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
declare module '@zhin.js/feature-kit' {
|
|
72
|
+
interface AdapterClientRegistry {
|
|
73
|
+
readonly milky: {
|
|
74
|
+
readonly client: MilkyV1Client;
|
|
75
|
+
readonly events: MilkyClientEventMap;
|
|
76
|
+
};
|
|
77
|
+
}
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
export const milkyClient = defineEndpointClient<MilkyV1Client, MilkyClientEventMap>('milky');
|
package/src/endpoint.ts
CHANGED
|
@@ -21,6 +21,6 @@ export {
|
|
|
21
21
|
|
|
22
22
|
export type { MilkyWsSocket, MilkyWsCreateOptions } from './ws-types.js';
|
|
23
23
|
|
|
24
|
-
export { verifyMilkyAccessToken
|
|
24
|
+
export { verifyMilkyAccessToken } from './milky-auth.js';
|
|
25
25
|
|
|
26
26
|
export { openSseStream, consumeSseBuffer } from './sse-client.js';
|
package/src/index.ts
CHANGED
|
@@ -36,7 +36,6 @@ export {
|
|
|
36
36
|
MilkyWsEndpoint,
|
|
37
37
|
consumeSseBuffer,
|
|
38
38
|
openSseStream,
|
|
39
|
-
readRequestBody,
|
|
40
39
|
verifyMilkyAccessToken,
|
|
41
40
|
type CreateMilkySseStream,
|
|
42
41
|
type MilkySseEndpointOptions,
|
|
@@ -48,9 +47,20 @@ export {
|
|
|
48
47
|
} from './endpoint.js';
|
|
49
48
|
|
|
50
49
|
export {
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
50
|
+
MilkyClient,
|
|
51
|
+
milkyClient,
|
|
52
|
+
type MilkyClientEventMap,
|
|
53
|
+
} from './client.js';
|
|
54
|
+
|
|
55
|
+
export type {
|
|
56
|
+
MilkyAdapterConfig as ImHelperMilkyAdapterConfig,
|
|
57
|
+
MilkyActionUrlResolver,
|
|
58
|
+
MilkyCall,
|
|
59
|
+
MilkyMessageReceiveEvent,
|
|
60
|
+
MilkyMessageRecallEvent,
|
|
61
|
+
MilkyV1ClientConfig,
|
|
62
|
+
MilkyV1Event,
|
|
63
|
+
MilkyV1Response,
|
|
64
|
+
} from '@imhelper/milky-v1';
|
|
65
|
+
export { ProtocolError } from '@imhelper/milky-v1';
|
|
66
|
+
export type { ProtocolErrorKind, ProtocolErrorOptions } from 'imhelper';
|
package/src/milky-auth.ts
CHANGED
|
@@ -12,18 +12,3 @@ export function verifyMilkyAccessToken(accessToken: string | undefined, request:
|
|
|
12
12
|
}
|
|
13
13
|
return false;
|
|
14
14
|
}
|
|
15
|
-
|
|
16
|
-
export async function readRequestBody(request: IncomingMessage): Promise<string> {
|
|
17
|
-
const chunks: Buffer[] = [];
|
|
18
|
-
let size = 0;
|
|
19
|
-
for await (const chunk of request) {
|
|
20
|
-
const buffer = Buffer.isBuffer(chunk) ? chunk : Buffer.from(chunk);
|
|
21
|
-
size += buffer.length;
|
|
22
|
-
if (size > 1_048_576) {
|
|
23
|
-
request.destroy();
|
|
24
|
-
throw new Error('Request body exceeds 1MB');
|
|
25
|
-
}
|
|
26
|
-
chunks.push(buffer);
|
|
27
|
-
}
|
|
28
|
-
return Buffer.concat(chunks).toString('utf8');
|
|
29
|
-
}
|
package/src/protocol.ts
CHANGED
|
@@ -77,7 +77,7 @@ export type ResolvedMilkyConfig =
|
|
|
77
77
|
export type MilkyEndpointConfig = ResolvedMilkyConfig;
|
|
78
78
|
|
|
79
79
|
export interface MilkyApiResponse<T = unknown> {
|
|
80
|
-
status:
|
|
80
|
+
status: 'ok' | 'failed';
|
|
81
81
|
retcode: number;
|
|
82
82
|
data?: T;
|
|
83
83
|
message?: string;
|
|
@@ -203,13 +203,13 @@ function authQuery(access_token?: string): string {
|
|
|
203
203
|
|
|
204
204
|
/**
|
|
205
205
|
* 调用协议端 API:POST {baseUrl}/api/{apiName},Body JSON,鉴权。
|
|
206
|
-
*
|
|
206
|
+
* HTTP 层错误抛出;协议响应原样返回,由 Client 公开 call() 保留完整语义。
|
|
207
207
|
*/
|
|
208
208
|
export async function callApi<T = unknown>(
|
|
209
209
|
options: MilkyApiClientOptions,
|
|
210
210
|
apiName: string,
|
|
211
211
|
params: Record<string, unknown> = {},
|
|
212
|
-
): Promise<T
|
|
212
|
+
): Promise<MilkyApiResponse<T>> {
|
|
213
213
|
const { baseUrl, access_token } = options;
|
|
214
214
|
const url = new URL(`/api/${apiName}`, baseUrl.replace(/\/$/, ''));
|
|
215
215
|
const q = authQuery(access_token);
|
|
@@ -235,10 +235,7 @@ export async function callApi<T = unknown>(
|
|
|
235
235
|
if (res.status !== 200) {
|
|
236
236
|
throw new Error(`Milky API ${apiName}: HTTP ${res.status} ${body.message ?? text}`);
|
|
237
237
|
}
|
|
238
|
-
|
|
239
|
-
throw new Error(`Milky API ${apiName}: retcode=${body.retcode} ${body.message ?? ''}`);
|
|
240
|
-
}
|
|
241
|
-
return (body.data ?? {}) as T;
|
|
238
|
+
return body;
|
|
242
239
|
}
|
|
243
240
|
|
|
244
241
|
/** 根据 event_type 判断是否为 message_receive,并解析 data */
|