@zhin.js/adapter-milky 1.1.18 → 1.1.19

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.
@@ -2,7 +2,7 @@
2
2
  * Milky reverse WSS endpoint — httpHostToken WS upgrade inbound + baseUrl HTTP API outbound.
3
3
  */
4
4
  import {
5
- ClientEndpoint,
5
+ createEndpointSpecification, endpointSpec, type Endpoint, type EndpointSpecification,
6
6
  createEndpointLifecycle,
7
7
  createRecallEndpointControl,
8
8
  type EndpointLifecycle,
@@ -15,7 +15,7 @@ import { formatCompact, getAdapterLogger } from '@zhin.js/logger';
15
15
  import type { CapabilityId } from 'zhin.js';
16
16
  import { verifyMilkyAccessToken } from './milky-auth.js';
17
17
  import { createMilkyEndpointManagement } from './endpoint-management.js';
18
- import { callMilkyClient, createMilkyEndpointClient, forwardMilkyClientEvents, type MilkyClient } from './client.js';
18
+ import { createMilkyActionCaller, readMilkyResponse, type MilkyActionCaller, milkyClientOptions, forwardMilkyClientEvents, MilkyClient } from './client.js';
19
19
  import {
20
20
  buildSendAction,
21
21
  callApi,
@@ -40,19 +40,21 @@ const WS_OPEN = 1;
40
40
 
41
41
  export interface MilkyWssEndpointOptions {
42
42
  readonly id: CapabilityId;
43
+ readonly signal?: AbortSignal;
43
44
  readonly http: HttpHost;
44
45
  readonly config: MilkyWssConfig;
45
46
  readonly callApi?: typeof callApi;
46
47
  }
47
48
 
48
- export class MilkyWssEndpoint extends ClientEndpoint<MilkyClient> {
49
- readonly client: MilkyClient;
49
+ export class MilkyWssEndpoint extends MilkyClient implements Endpoint {
50
+ readonly [endpointSpec]: EndpointSpecification;
51
+ #open = false;
50
52
  readonly #logger!: ReturnType<typeof getAdapterLogger>;
51
53
 
52
54
  readonly #options: MilkyWssEndpointOptions;
53
- readonly #callApi: typeof callApi;
54
- readonly management: EndpointManagement;
55
- readonly control: EndpointControl = createRecallEndpointControl((id) => this.recallMessage(id));
55
+ readonly #management: EndpointManagement;
56
+ readonly #call: MilkyActionCaller;
57
+ readonly #control: EndpointControl = createRecallEndpointControl((id, signal) => this.#recallMessage({ id, signal }));
56
58
  #ws?: MilkyWsSocket;
57
59
  #wsRelease?: () => void;
58
60
  #clientSocketRelease?: () => void;
@@ -61,28 +63,37 @@ export class MilkyWssEndpoint extends ClientEndpoint<MilkyClient> {
61
63
  #started = false;
62
64
 
63
65
  constructor(options: MilkyWssEndpointOptions) {
64
- super();
66
+ const call = createMilkyActionCaller({ config: options.config, request: options.callApi ?? callApi, signal: options.signal });
67
+ super(milkyClientOptions({ config: options.config, call }));
68
+ this.#call = call;
65
69
  this.#logger = getAdapterLogger('milky', options.config.id);
66
70
  this.#options = options;
67
- this.#callApi = options.callApi ?? callApi;
68
71
  this.#connectionLifecycle = createEndpointLifecycle({
69
72
  name: `${options.config.id}:inbound`,
70
73
  reconnect: false,
71
74
  });
72
- this.client = createMilkyEndpointClient(options.config, this.#callApi);
73
- this.management = createMilkyEndpointManagement({
74
- callApi: (action, params) => callMilkyClient(this.client, action, params),
75
+ this.#management = createMilkyEndpointManagement({
76
+ callApi: request => call(request).then(response => readMilkyResponse(response, request.action)),
75
77
  });
76
- this.bindClientEvents(
77
- (receive) => forwardMilkyClientEvents(this.client, receive),
78
- (name, payload) => {
78
+ const readTransportState = () => this.#connectionLifecycle.state;
79
+ this[endpointSpec] = createEndpointSpecification(this, {
80
+ name: options.config.id,
81
+ start: () => this.#start(), open: () => { this.#open = true; },
82
+ close: () => { this.#open = false; }, stop: () => this.#stop(),
83
+ send: request => this.#send(request),
84
+ management: this.#management, control: this.#control,
85
+ get transportState() { return readTransportState(); },
86
+ });
87
+ forwardMilkyClientEvents(this, (name, payload) => {
88
+ void this[endpointSpec].events.run(() => {
89
+ if (!this.#open && !this.#started) return;
90
+ void this[endpointSpec].events.platform(name, payload).catch(error => this.#warnPlatformEvent(error));
79
91
  if (name === 'event') this.#admitRaw(payload as MilkyEvent);
80
- },
81
- (_name, error) => this.#warnPlatformEvent(error),
82
- );
92
+ }).catch(error => this.#warnPlatformEvent(error));
93
+ });
83
94
  }
84
95
 
85
- async start(): Promise<void> {
96
+ async #start(): Promise<void> {
86
97
  if (this.#started) return;
87
98
  this.#started = true;
88
99
  const handle = this.#options.http.ws(this.#options.config.path);
@@ -105,8 +116,8 @@ export class MilkyWssEndpoint extends ClientEndpoint<MilkyClient> {
105
116
  }));
106
117
  }
107
118
 
108
- async stop(): Promise<void> {
109
- this.close();
119
+ async #stop(): Promise<void> {
120
+ this.#open = false;
110
121
  this.#wsRelease?.();
111
122
  this.#wsRelease = undefined;
112
123
  await this.#connectionTask;
@@ -115,10 +126,10 @@ export class MilkyWssEndpoint extends ClientEndpoint<MilkyClient> {
115
126
  this.#started = false;
116
127
  }
117
128
 
118
- async send({ conversation, payload }: EndpointSendRequest): Promise<string> {
129
+ async #send({ conversation, payload, signal }: EndpointSendRequest): Promise<string> {
119
130
  const message = formatOutboundSegments(payload);
120
131
  const { action, params } = buildSendAction(conversation, message);
121
- const data = await callMilkyClient<{ message_seq?: number }>(this.client, action, params);
132
+ const data = readMilkyResponse<{ message_seq?: number }>(await this.#call({ action, params, signal }), action);
122
133
  const messageId = formatOutboundMessageId(conversation, data?.message_seq);
123
134
  this.#logger.debug(formatCompact({
124
135
  op: 'milky_send',
@@ -129,23 +140,24 @@ export class MilkyWssEndpoint extends ClientEndpoint<MilkyClient> {
129
140
  return messageId;
130
141
  }
131
142
 
132
- async recallMessage(id: string): Promise<void> {
143
+ async #recallMessage({ id, signal }: { readonly id: string; readonly signal?: AbortSignal }): Promise<void> {
133
144
  const parsed = parseMilkyMessageId(id);
134
145
  if (!parsed) throw new Error(`Invalid message id: ${id}`);
135
146
  if (parsed.message_scene === 'group') {
136
- await callMilkyClient(this.client, 'recall_group_message', {
147
+ readMilkyResponse(await this.#call({ action: 'recall_group_message', signal, params: {
137
148
  group_id: parsed.peer_id,
138
149
  message_seq: parsed.message_seq,
139
- });
150
+ } }), 'recall_group_message');
140
151
  } else {
141
- await callMilkyClient(this.client, 'recall_private_message', {
152
+ readMilkyResponse(await this.#call({ action: 'recall_private_message', signal, params: {
142
153
  user_id: parsed.peer_id,
143
154
  message_seq: parsed.message_seq,
144
- });
155
+ } }), 'recall_private_message');
145
156
  }
146
157
  }
147
158
 
148
159
  #admitRaw(event: MilkyEvent): void {
160
+ if (event.self_id != null) this[endpointSpec].reportSelfUserId(String(event.self_id));
149
161
  const data = parseMessageReceiveData(event);
150
162
  if (!data) return;
151
163
  this.#admitMessage(data, event);
@@ -156,14 +168,16 @@ export class MilkyWssEndpoint extends ClientEndpoint<MilkyClient> {
156
168
  }
157
169
 
158
170
  #admitMessage(data: MilkyIncomingMessage, event: MilkyEvent): void {
159
- const conversation = milkyInboundConversation(String(this.#options.id), data);
171
+ const conversation = { ...milkyInboundConversation(String(this.#options.id), data), endpoint: this[endpointSpec].identity };
160
172
  const target = `${conversation.kind}:${conversation.id}`;
161
173
  const content = formatInboundContent(data);
162
174
  const segments = formatInboundSegments(data);
163
175
  const audioUrl = extractInboundAudioUrl(data);
164
176
  const nickname = senderNickname(data);
165
177
  const mentioned = isMentioned(data, event.self_id);
166
- void this.emit('message.receive', {
178
+ void this[endpointSpec].events.message({
179
+ event,
180
+ ...(typeof event.time === 'number' ? { platformSentAt: event.time * 1000 } : {}),
167
181
  conversation,
168
182
  message: { conversation, id: formatInboundMessageId(data) },
169
183
  content,
@@ -198,7 +212,7 @@ export class MilkyWssEndpoint extends ClientEndpoint<MilkyClient> {
198
212
  await this.#connectionLifecycle.stop();
199
213
  await this.#connectionLifecycle.start(async (lifecycleHandle) => {
200
214
  this.#ws = socket;
201
- const releaseClientSocket = this.client.acceptWebSocket(socket);
215
+ const releaseClientSocket = this.acceptWebSocket(socket);
202
216
  this.#clientSocketRelease = releaseClientSocket;
203
217
  lifecycleHandle.onForceClose(() => {
204
218
  if (this.#clientSocketRelease === releaseClientSocket) {
@@ -1 +0,0 @@
1
- export declare const milkyRuntimeStateToken: import("zhin.js").Token<import("@zhin.js/adapter").EndpointRuntimeState>;
@@ -1,6 +0,0 @@
1
- /**
2
- * Milky 插件实例的运行时状态:adapter create() 注册的 endpoint 列表。
3
- * 由 plugin.ts setup() provide,adapter create 与 `milky endpoint` 命令共享(同一 owner generation)。
4
- */
5
- import { defineEndpointRuntimeStateToken } from 'zhin.js/adapter';
6
- export const milkyRuntimeStateToken = defineEndpointRuntimeStateToken('milky');
@@ -1,7 +0,0 @@
1
- /**
2
- * Milky 插件实例的运行时状态:adapter create() 注册的 endpoint 列表。
3
- * 由 plugin.ts setup() provide,adapter create 与 `milky endpoint` 命令共享(同一 owner generation)。
4
- */
5
- import { defineEndpointRuntimeStateToken } from 'zhin.js/adapter';
6
-
7
- export const milkyRuntimeStateToken = defineEndpointRuntimeStateToken('milky');