@zhin.js/adapter-milky 7.0.0 → 7.0.2
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 +31 -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/webhook-endpoint.js
CHANGED
|
@@ -1,29 +1,36 @@
|
|
|
1
|
-
import { createRecallEndpointControl, } from 'zhin.js/adapter';
|
|
1
|
+
import { ClientEndpoint, createRecallEndpointControl, } from 'zhin.js/adapter';
|
|
2
2
|
import { formatCompact, getAdapterLogger } from '@zhin.js/logger';
|
|
3
|
-
import {
|
|
3
|
+
import { verifyMilkyAccessToken } from './milky-auth.js';
|
|
4
4
|
import { createMilkyEndpointManagement } from './endpoint-management.js';
|
|
5
|
-
import {
|
|
5
|
+
import { callMilkyClient, createMilkyEndpointClient, forwardMilkyClientEvents } from './client.js';
|
|
6
6
|
import { buildSendAction, callApi, extractInboundAudioUrl, formatInboundContent, formatInboundMessageId, formatInboundSegments, formatOutboundMessageId, formatOutboundSegments, isMentioned, milkyInboundConversation, parseMessageReceiveData, parseMilkyMessageId, senderNickname, } from './protocol.js';
|
|
7
|
-
export class MilkyWebhookEndpoint {
|
|
7
|
+
export class MilkyWebhookEndpoint extends ClientEndpoint {
|
|
8
|
+
client;
|
|
8
9
|
#logger;
|
|
9
10
|
#options;
|
|
10
11
|
#callApi;
|
|
11
|
-
management
|
|
12
|
+
management;
|
|
12
13
|
control = createRecallEndpointControl((id) => this.recallMessage(id));
|
|
13
14
|
#routeReleases = [];
|
|
14
|
-
#open = false;
|
|
15
15
|
#started = false;
|
|
16
|
-
#unregisterAgent;
|
|
17
16
|
constructor(options) {
|
|
17
|
+
super();
|
|
18
18
|
this.#logger = getAdapterLogger('milky', options.config.id);
|
|
19
19
|
this.#options = options;
|
|
20
20
|
this.#callApi = options.callApi ?? callApi;
|
|
21
|
+
this.client = createMilkyEndpointClient(options.config, this.#callApi);
|
|
22
|
+
this.management = createMilkyEndpointManagement({
|
|
23
|
+
callApi: (action, params) => callMilkyClient(this.client, action, params),
|
|
24
|
+
});
|
|
25
|
+
this.bindClientEvents((receive) => forwardMilkyClientEvents(this.client, receive), (name, payload) => {
|
|
26
|
+
if (name === 'event')
|
|
27
|
+
this.#admitRaw(payload);
|
|
28
|
+
}, (_name, error) => this.#warnPlatformEvent(error));
|
|
21
29
|
}
|
|
22
30
|
async start() {
|
|
23
31
|
if (this.#started)
|
|
24
32
|
return;
|
|
25
33
|
this.#started = true;
|
|
26
|
-
this.#unregisterAgent = registerMilkyAgentEndpoint(this.#options.config.id, this);
|
|
27
34
|
this.#setupRoutes();
|
|
28
35
|
this.#logger.info(formatCompact({
|
|
29
36
|
op: 'listen',
|
|
@@ -32,25 +39,17 @@ export class MilkyWebhookEndpoint {
|
|
|
32
39
|
path: this.#options.config.path,
|
|
33
40
|
}));
|
|
34
41
|
}
|
|
35
|
-
open() {
|
|
36
|
-
this.#open = true;
|
|
37
|
-
}
|
|
38
|
-
close() {
|
|
39
|
-
this.#open = false;
|
|
40
|
-
}
|
|
41
42
|
async stop() {
|
|
42
|
-
this
|
|
43
|
+
this.close();
|
|
43
44
|
for (const release of this.#routeReleases.splice(0))
|
|
44
45
|
release();
|
|
45
|
-
this.#unregisterAgent?.();
|
|
46
|
-
this.#unregisterAgent = undefined;
|
|
47
46
|
this.#started = false;
|
|
48
47
|
this.#logger.debug(formatCompact({ op: 'disconnect' }));
|
|
49
48
|
}
|
|
50
49
|
async send({ conversation, payload }) {
|
|
51
50
|
const message = formatOutboundSegments(payload);
|
|
52
51
|
const { action, params } = buildSendAction(conversation, message);
|
|
53
|
-
const data = await this.
|
|
52
|
+
const data = await callMilkyClient(this.client, action, params);
|
|
54
53
|
const messageId = formatOutboundMessageId(conversation, data?.message_seq);
|
|
55
54
|
this.#logger.debug(formatCompact({
|
|
56
55
|
op: 'milky_send',
|
|
@@ -60,91 +59,31 @@ export class MilkyWebhookEndpoint {
|
|
|
60
59
|
}));
|
|
61
60
|
return messageId;
|
|
62
61
|
}
|
|
63
|
-
callApi(action, params = {}) {
|
|
64
|
-
return this.#callApi(this.apiOptions(), action, params);
|
|
65
|
-
}
|
|
66
62
|
async recallMessage(id) {
|
|
67
63
|
const parsed = parseMilkyMessageId(id);
|
|
68
64
|
if (!parsed)
|
|
69
65
|
throw new Error(`Invalid message id: ${id}`);
|
|
70
66
|
if (parsed.message_scene === 'group') {
|
|
71
|
-
await this.
|
|
67
|
+
await callMilkyClient(this.client, 'recall_group_message', {
|
|
72
68
|
group_id: parsed.peer_id,
|
|
73
69
|
message_seq: parsed.message_seq,
|
|
74
70
|
});
|
|
75
71
|
}
|
|
76
72
|
else {
|
|
77
|
-
await this.
|
|
73
|
+
await callMilkyClient(this.client, 'recall_private_message', {
|
|
78
74
|
user_id: parsed.peer_id,
|
|
79
75
|
message_seq: parsed.message_seq,
|
|
80
76
|
});
|
|
81
77
|
}
|
|
82
78
|
}
|
|
83
|
-
|
|
84
|
-
await this.callApi('kick_group_member', {
|
|
85
|
-
group_id: groupId,
|
|
86
|
-
user_id: userId,
|
|
87
|
-
reject_add_request: rejectAddRequest,
|
|
88
|
-
});
|
|
89
|
-
return true;
|
|
90
|
-
}
|
|
91
|
-
async muteMember(groupId, userId, duration = 600) {
|
|
92
|
-
await this.callApi('set_group_member_mute', {
|
|
93
|
-
group_id: groupId,
|
|
94
|
-
user_id: userId,
|
|
95
|
-
duration,
|
|
96
|
-
});
|
|
97
|
-
return true;
|
|
98
|
-
}
|
|
99
|
-
async muteAll(groupId, enable = true) {
|
|
100
|
-
await this.callApi('set_group_whole_mute', { group_id: groupId, is_mute: enable });
|
|
101
|
-
return true;
|
|
102
|
-
}
|
|
103
|
-
async setAdmin(groupId, userId, enable = true) {
|
|
104
|
-
await this.callApi('set_group_member_admin', {
|
|
105
|
-
group_id: groupId,
|
|
106
|
-
user_id: userId,
|
|
107
|
-
is_set: enable,
|
|
108
|
-
});
|
|
109
|
-
return true;
|
|
110
|
-
}
|
|
111
|
-
async setCard(groupId, userId, card) {
|
|
112
|
-
await this.callApi('set_group_member_card', {
|
|
113
|
-
group_id: groupId,
|
|
114
|
-
user_id: userId,
|
|
115
|
-
card,
|
|
116
|
-
});
|
|
117
|
-
return true;
|
|
118
|
-
}
|
|
119
|
-
async setTitle(groupId, userId, title) {
|
|
120
|
-
await this.callApi('set_group_member_special_title', {
|
|
121
|
-
group_id: groupId,
|
|
122
|
-
user_id: userId,
|
|
123
|
-
special_title: title,
|
|
124
|
-
});
|
|
125
|
-
return true;
|
|
126
|
-
}
|
|
127
|
-
async setGroupName(groupId, name) {
|
|
128
|
-
await this.callApi('set_group_name', { group_id: groupId, new_group_name: name });
|
|
129
|
-
return true;
|
|
130
|
-
}
|
|
131
|
-
async getMemberList(groupId) {
|
|
132
|
-
return this.callApi('get_group_member_list', { group_id: groupId });
|
|
133
|
-
}
|
|
134
|
-
async getGroupInfo(groupId) {
|
|
135
|
-
return this.callApi('get_group_info', { group_id: groupId });
|
|
136
|
-
}
|
|
137
|
-
admit(event) {
|
|
79
|
+
#admitRaw(event) {
|
|
138
80
|
const data = parseMessageReceiveData(event);
|
|
139
|
-
if (!
|
|
81
|
+
if (!data)
|
|
140
82
|
return;
|
|
141
83
|
this.#admitMessage(data, event);
|
|
142
84
|
}
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
baseUrl: this.#options.config.baseUrl,
|
|
146
|
-
access_token: this.#options.config.access_token,
|
|
147
|
-
};
|
|
85
|
+
#warnPlatformEvent(error) {
|
|
86
|
+
this.#logger.warn(formatCompact({ op: 'milky_platform_event_failed', error: error instanceof Error ? error.message : String(error) }));
|
|
148
87
|
}
|
|
149
88
|
#admitMessage(data, event) {
|
|
150
89
|
const conversation = milkyInboundConversation(String(this.#options.id), data);
|
|
@@ -154,7 +93,7 @@ export class MilkyWebhookEndpoint {
|
|
|
154
93
|
const audioUrl = extractInboundAudioUrl(data);
|
|
155
94
|
const nickname = senderNickname(data);
|
|
156
95
|
const mentioned = isMentioned(data, event.self_id);
|
|
157
|
-
void this
|
|
96
|
+
void this.emit('message.receive', {
|
|
158
97
|
conversation,
|
|
159
98
|
message: { conversation, id: formatInboundMessageId(data) },
|
|
160
99
|
content,
|
|
@@ -192,20 +131,12 @@ export class MilkyWebhookEndpoint {
|
|
|
192
131
|
response.end(JSON.stringify({ message: 'Unauthorized' }));
|
|
193
132
|
return;
|
|
194
133
|
}
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
event = JSON.parse(raw);
|
|
199
|
-
}
|
|
200
|
-
catch {
|
|
201
|
-
response.writeHead(400, { 'Content-Type': 'application/json' });
|
|
202
|
-
response.end(JSON.stringify({ message: 'Invalid JSON' }));
|
|
134
|
+
if (!this.clientEventsOpen) {
|
|
135
|
+
response.writeHead(200, { 'Content-Type': 'application/json' });
|
|
136
|
+
response.end(JSON.stringify({ status: 'ok' }));
|
|
203
137
|
return;
|
|
204
138
|
}
|
|
205
|
-
|
|
206
|
-
this.admit(event);
|
|
207
|
-
response.writeHead(200, { 'Content-Type': 'application/json' });
|
|
208
|
-
response.end(JSON.stringify({ status: 'ok' }));
|
|
139
|
+
await this.client.acceptHttp(request, response);
|
|
209
140
|
}
|
|
210
141
|
catch (error) {
|
|
211
142
|
this.#logger.error('Milky webhook error:', error);
|
package/lib/ws-endpoint.d.ts
CHANGED
|
@@ -1,42 +1,23 @@
|
|
|
1
|
-
import {
|
|
2
|
-
import type { MessageGateway, SideEventGateway } from '@zhin.js/core/runtime';
|
|
1
|
+
import { ClientEndpoint, type EndpointControl, type EndpointManagement, type EndpointSendRequest } from 'zhin.js/adapter';
|
|
3
2
|
import type { CapabilityId } from 'zhin.js';
|
|
4
|
-
import {
|
|
3
|
+
import { type MilkyClient } from './client.js';
|
|
4
|
+
import { callApi, type MilkyWsConfig } from './protocol.js';
|
|
5
5
|
import type { MilkyWsCreateOptions, MilkyWsSocket } from './ws-types.js';
|
|
6
6
|
export interface MilkyWsEndpointOptions {
|
|
7
7
|
readonly id: CapabilityId;
|
|
8
|
-
readonly gateway: MessageGateway;
|
|
9
|
-
readonly sideEvents?: SideEventGateway;
|
|
10
8
|
readonly config: MilkyWsConfig;
|
|
11
9
|
readonly createWebSocket?: (url: string, options: MilkyWsCreateOptions) => MilkyWsSocket;
|
|
12
10
|
readonly callApi?: typeof callApi;
|
|
13
11
|
}
|
|
14
|
-
export declare class MilkyWsEndpoint
|
|
12
|
+
export declare class MilkyWsEndpoint extends ClientEndpoint<MilkyClient> {
|
|
15
13
|
#private;
|
|
14
|
+
readonly client: MilkyClient;
|
|
16
15
|
readonly management: EndpointManagement;
|
|
17
16
|
readonly control: EndpointControl;
|
|
18
17
|
constructor(options: MilkyWsEndpointOptions);
|
|
19
18
|
start(): Promise<void>;
|
|
20
|
-
open(): void;
|
|
21
19
|
close(): void;
|
|
22
20
|
stop(): Promise<void>;
|
|
23
21
|
send({ conversation, payload }: EndpointSendRequest): Promise<string>;
|
|
24
|
-
/** Public API for agent tools / callers. */
|
|
25
|
-
callApi(action: string, params?: Record<string, unknown>): Promise<unknown>;
|
|
26
22
|
recallMessage(id: string): Promise<void>;
|
|
27
|
-
kickMember(groupId: number, userId: number, rejectAddRequest?: boolean): Promise<boolean>;
|
|
28
|
-
muteMember(groupId: number, userId: number, duration?: number): Promise<boolean>;
|
|
29
|
-
muteAll(groupId: number, enable?: boolean): Promise<boolean>;
|
|
30
|
-
setAdmin(groupId: number, userId: number, enable?: boolean): Promise<boolean>;
|
|
31
|
-
setCard(groupId: number, userId: number, card: string): Promise<boolean>;
|
|
32
|
-
setTitle(groupId: number, userId: number, title: string): Promise<boolean>;
|
|
33
|
-
setGroupName(groupId: number, name: string): Promise<boolean>;
|
|
34
|
-
getMemberList(groupId: number): Promise<unknown[]>;
|
|
35
|
-
getGroupInfo(groupId: number): Promise<unknown>;
|
|
36
|
-
/** Test / internal: admit a parsed event when the endpoint is open. */
|
|
37
|
-
admit(event: MilkyEvent): void;
|
|
38
|
-
apiOptions(): {
|
|
39
|
-
baseUrl: string;
|
|
40
|
-
access_token?: string;
|
|
41
|
-
};
|
|
42
23
|
}
|
package/lib/ws-endpoint.js
CHANGED
|
@@ -2,26 +2,35 @@
|
|
|
2
2
|
* Milky WS client endpoint — outbound connect to Milky protocol server.
|
|
3
3
|
*/
|
|
4
4
|
import WebSocket from 'ws';
|
|
5
|
-
import { createRecallEndpointControl, createEndpointLifecycle, } from 'zhin.js/adapter';
|
|
5
|
+
import { ClientEndpoint, createRecallEndpointControl, createEndpointLifecycle, } from 'zhin.js/adapter';
|
|
6
6
|
import { formatCompact, getAdapterLogger } from '@zhin.js/logger';
|
|
7
7
|
import { createMilkyEndpointManagement } from './endpoint-management.js';
|
|
8
|
-
import {
|
|
8
|
+
import { callMilkyClient, createMilkyEndpointClient, forwardMilkyClientEvents, } from './client.js';
|
|
9
9
|
import { buildSendAction, buildWsConnectOptions, callApi, extractInboundAudioUrl, formatInboundContent, formatInboundMessageId, formatInboundSegments, formatOutboundMessageId, formatOutboundSegments, isMentioned, milkyInboundConversation, parseMessageReceiveData, parseMilkyMessageId, senderNickname, } from './protocol.js';
|
|
10
10
|
const WS_OPEN = 1;
|
|
11
|
-
export class MilkyWsEndpoint {
|
|
11
|
+
export class MilkyWsEndpoint extends ClientEndpoint {
|
|
12
|
+
client;
|
|
12
13
|
#logger;
|
|
13
14
|
#options;
|
|
14
15
|
#callApi;
|
|
15
|
-
management
|
|
16
|
+
management;
|
|
16
17
|
control = createRecallEndpointControl((id) => this.recallMessage(id));
|
|
17
18
|
#lifecycle;
|
|
18
19
|
#ws;
|
|
19
|
-
#
|
|
20
|
-
#unregisterAgent;
|
|
20
|
+
#clientSocketRelease;
|
|
21
21
|
constructor(options) {
|
|
22
|
+
super();
|
|
22
23
|
this.#logger = getAdapterLogger('milky', options.config.id);
|
|
23
24
|
this.#options = options;
|
|
24
25
|
this.#callApi = options.callApi ?? callApi;
|
|
26
|
+
this.client = createMilkyEndpointClient(options.config, this.#callApi);
|
|
27
|
+
this.management = createMilkyEndpointManagement({
|
|
28
|
+
callApi: (action, params) => callMilkyClient(this.client, action, params),
|
|
29
|
+
});
|
|
30
|
+
this.bindClientEvents((receive) => forwardMilkyClientEvents(this.client, receive), (name, payload) => {
|
|
31
|
+
if (name === 'event')
|
|
32
|
+
this.#admitRaw(payload);
|
|
33
|
+
}, (_name, error) => this.#warnPlatformEvent(error));
|
|
25
34
|
this.#lifecycle = createEndpointLifecycle({
|
|
26
35
|
name: options.config.id,
|
|
27
36
|
// reconnect_interval 旧语义为固定间隔:multiplier 1 + 无 jitter + 不封顶
|
|
@@ -36,28 +45,16 @@ export class MilkyWsEndpoint {
|
|
|
36
45
|
async start() {
|
|
37
46
|
if (this.#lifecycle.started)
|
|
38
47
|
return;
|
|
39
|
-
this.#
|
|
40
|
-
try {
|
|
41
|
-
await this.#lifecycle.start((handle) => this.#connect(handle));
|
|
42
|
-
}
|
|
43
|
-
catch (err) {
|
|
44
|
-
// start 失败复位由基座保证;agent 注册/反注册是适配器专有依赖,留在适配器侧
|
|
45
|
-
this.#unregisterAgent?.();
|
|
46
|
-
this.#unregisterAgent = undefined;
|
|
47
|
-
throw err;
|
|
48
|
-
}
|
|
49
|
-
}
|
|
50
|
-
open() {
|
|
51
|
-
this.#open = true;
|
|
48
|
+
await this.#lifecycle.start((handle) => this.#connect(handle));
|
|
52
49
|
}
|
|
53
50
|
close() {
|
|
54
|
-
|
|
51
|
+
super.close();
|
|
52
|
+
this.#clientSocketRelease?.();
|
|
53
|
+
this.#clientSocketRelease = undefined;
|
|
55
54
|
}
|
|
56
55
|
async stop() {
|
|
57
|
-
this
|
|
56
|
+
this.close();
|
|
58
57
|
await this.#lifecycle.stop();
|
|
59
|
-
this.#unregisterAgent?.();
|
|
60
|
-
this.#unregisterAgent = undefined;
|
|
61
58
|
if (this.#ws) {
|
|
62
59
|
try {
|
|
63
60
|
this.#ws.close();
|
|
@@ -71,7 +68,7 @@ export class MilkyWsEndpoint {
|
|
|
71
68
|
async send({ conversation, payload }) {
|
|
72
69
|
const message = formatOutboundSegments(payload);
|
|
73
70
|
const { action, params } = buildSendAction(conversation, message);
|
|
74
|
-
const data = await this.
|
|
71
|
+
const data = await callMilkyClient(this.client, action, params);
|
|
75
72
|
const messageId = formatOutboundMessageId(conversation, data?.message_seq);
|
|
76
73
|
this.#logger.debug(formatCompact({
|
|
77
74
|
op: 'milky_send',
|
|
@@ -81,93 +78,34 @@ export class MilkyWsEndpoint {
|
|
|
81
78
|
}));
|
|
82
79
|
return messageId;
|
|
83
80
|
}
|
|
84
|
-
/** Public API for agent tools / callers. */
|
|
85
|
-
callApi(action, params = {}) {
|
|
86
|
-
return this.#callApi(this.apiOptions(), action, params);
|
|
87
|
-
}
|
|
88
81
|
async recallMessage(id) {
|
|
89
82
|
const parsed = parseMilkyMessageId(id);
|
|
90
83
|
if (!parsed)
|
|
91
84
|
throw new Error(`Invalid message id: ${id}`);
|
|
92
85
|
if (parsed.message_scene === 'group') {
|
|
93
|
-
await this.
|
|
86
|
+
await callMilkyClient(this.client, 'recall_group_message', {
|
|
94
87
|
group_id: parsed.peer_id,
|
|
95
88
|
message_seq: parsed.message_seq,
|
|
96
89
|
});
|
|
97
90
|
}
|
|
98
91
|
else {
|
|
99
|
-
await this.
|
|
92
|
+
await callMilkyClient(this.client, 'recall_private_message', {
|
|
100
93
|
user_id: parsed.peer_id,
|
|
101
94
|
message_seq: parsed.message_seq,
|
|
102
95
|
});
|
|
103
96
|
}
|
|
104
97
|
}
|
|
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
|
-
/** Test / internal: admit a parsed event when the endpoint is open. */
|
|
160
|
-
admit(event) {
|
|
98
|
+
#admitRaw(event) {
|
|
161
99
|
const data = parseMessageReceiveData(event);
|
|
162
|
-
if (!
|
|
100
|
+
if (!data)
|
|
163
101
|
return;
|
|
164
102
|
this.#admitMessage(data, event);
|
|
165
103
|
}
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
};
|
|
104
|
+
#warnPlatformEvent(error) {
|
|
105
|
+
this.#logger.warn(formatCompact({
|
|
106
|
+
op: 'milky_platform_event_failed',
|
|
107
|
+
error: error instanceof Error ? error.message : String(error),
|
|
108
|
+
}));
|
|
171
109
|
}
|
|
172
110
|
#admitMessage(data, event) {
|
|
173
111
|
const conversation = milkyInboundConversation(String(this.#options.id), data);
|
|
@@ -177,7 +115,7 @@ export class MilkyWsEndpoint {
|
|
|
177
115
|
const audioUrl = extractInboundAudioUrl(data);
|
|
178
116
|
const nickname = senderNickname(data);
|
|
179
117
|
const mentioned = isMentioned(data, event.self_id);
|
|
180
|
-
void this
|
|
118
|
+
void this.emit('message.receive', {
|
|
181
119
|
conversation,
|
|
182
120
|
message: { conversation, id: formatInboundMessageId(data) },
|
|
183
121
|
content,
|
|
@@ -248,10 +186,11 @@ export class MilkyWsEndpoint {
|
|
|
248
186
|
}
|
|
249
187
|
resolve();
|
|
250
188
|
});
|
|
251
|
-
|
|
252
|
-
|
|
253
|
-
});
|
|
189
|
+
this.#clientSocketRelease?.();
|
|
190
|
+
this.#clientSocketRelease = this.client.acceptWebSocket(ws);
|
|
254
191
|
ws.on('close', (code, reason) => {
|
|
192
|
+
this.#clientSocketRelease?.();
|
|
193
|
+
this.#clientSocketRelease = undefined;
|
|
255
194
|
const reasonStr = typeof reason === 'string'
|
|
256
195
|
? reason
|
|
257
196
|
: Buffer.isBuffer(reason)
|
|
@@ -291,24 +230,4 @@ export class MilkyWsEndpoint {
|
|
|
291
230
|
});
|
|
292
231
|
});
|
|
293
232
|
}
|
|
294
|
-
#onMessage(data) {
|
|
295
|
-
try {
|
|
296
|
-
const raw = typeof data === 'string'
|
|
297
|
-
? data
|
|
298
|
-
: Buffer.isBuffer(data)
|
|
299
|
-
? data.toString()
|
|
300
|
-
: data instanceof ArrayBuffer
|
|
301
|
-
? new TextDecoder().decode(data)
|
|
302
|
-
: String(data ?? '');
|
|
303
|
-
const event = JSON.parse(raw);
|
|
304
|
-
this.admit(event);
|
|
305
|
-
}
|
|
306
|
-
catch (error) {
|
|
307
|
-
this.#logger.warn(formatCompact({
|
|
308
|
-
op: 'milky_parse_failed',
|
|
309
|
-
endpoint: this.#options.config.id,
|
|
310
|
-
error: error instanceof Error ? error.message : String(error),
|
|
311
|
-
}));
|
|
312
|
-
}
|
|
313
|
-
}
|
|
314
233
|
}
|
package/lib/wss-endpoint.d.ts
CHANGED
|
@@ -1,40 +1,25 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
1
|
+
/**
|
|
2
|
+
* Milky reverse WSS endpoint — httpHostToken WS upgrade inbound + baseUrl HTTP API outbound.
|
|
3
|
+
*/
|
|
4
|
+
import { ClientEndpoint, type EndpointControl, type EndpointManagement, type EndpointSendRequest } from 'zhin.js/adapter';
|
|
3
5
|
import type { HttpHost } from '@zhin.js/host-http';
|
|
4
6
|
import type { CapabilityId } from 'zhin.js';
|
|
5
|
-
import {
|
|
7
|
+
import { type MilkyClient } from './client.js';
|
|
8
|
+
import { callApi, type MilkyWssConfig } from './protocol.js';
|
|
6
9
|
export interface MilkyWssEndpointOptions {
|
|
7
10
|
readonly id: CapabilityId;
|
|
8
|
-
readonly gateway: MessageGateway;
|
|
9
|
-
readonly sideEvents?: SideEventGateway;
|
|
10
11
|
readonly http: HttpHost;
|
|
11
12
|
readonly config: MilkyWssConfig;
|
|
12
13
|
readonly callApi?: typeof callApi;
|
|
13
14
|
}
|
|
14
|
-
export declare class MilkyWssEndpoint
|
|
15
|
+
export declare class MilkyWssEndpoint extends ClientEndpoint<MilkyClient> {
|
|
15
16
|
#private;
|
|
17
|
+
readonly client: MilkyClient;
|
|
16
18
|
readonly management: EndpointManagement;
|
|
17
19
|
readonly control: EndpointControl;
|
|
18
20
|
constructor(options: MilkyWssEndpointOptions);
|
|
19
21
|
start(): Promise<void>;
|
|
20
|
-
open(): void;
|
|
21
|
-
close(): void;
|
|
22
22
|
stop(): Promise<void>;
|
|
23
23
|
send({ conversation, payload }: EndpointSendRequest): Promise<string>;
|
|
24
|
-
callApi(action: string, params?: Record<string, unknown>): Promise<unknown>;
|
|
25
24
|
recallMessage(id: string): Promise<void>;
|
|
26
|
-
kickMember(groupId: number, userId: number, rejectAddRequest?: boolean): Promise<boolean>;
|
|
27
|
-
muteMember(groupId: number, userId: number, duration?: number): Promise<boolean>;
|
|
28
|
-
muteAll(groupId: number, enable?: boolean): Promise<boolean>;
|
|
29
|
-
setAdmin(groupId: number, userId: number, enable?: boolean): Promise<boolean>;
|
|
30
|
-
setCard(groupId: number, userId: number, card: string): Promise<boolean>;
|
|
31
|
-
setTitle(groupId: number, userId: number, title: string): Promise<boolean>;
|
|
32
|
-
setGroupName(groupId: number, name: string): Promise<boolean>;
|
|
33
|
-
getMemberList(groupId: number): Promise<unknown[]>;
|
|
34
|
-
getGroupInfo(groupId: number): Promise<unknown>;
|
|
35
|
-
admit(event: MilkyEvent): void;
|
|
36
|
-
apiOptions(): {
|
|
37
|
-
baseUrl: string;
|
|
38
|
-
access_token?: string;
|
|
39
|
-
};
|
|
40
25
|
}
|