mixi2-js 1.2.1 → 1.3.0

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/dist/index.d.cts CHANGED
@@ -1,46 +1,50 @@
1
- import { E as EventHandler, A as Authenticator } from './client-iXbbRxc-.cjs';
2
- export { a as AuthenticatorOptions, C as ChatMessage, b as ChatMessageReceivedEvent, c as Client, d as ClientOptions, e as CreatePostRequest, f as Event, g as EventReason, h as EventType, G as GetPostMediaStatusResponse, i as GetStampsRequest, I as InitiatePostMediaUploadRequest, j as InitiatePostMediaUploadResponse, L as LanguageCode, M as Media, k as MediaImage, l as MediaStamp, m as MediaType, n as MediaUploadStatus, o as MediaUploadType, p as MediaVideo, O as OAuth2Authenticator, q as OfficialStamp, r as OfficialStampSet, P as PingEvent, s as Post, t as PostAccessLevel, u as PostCreatedEvent, v as PostMask, w as PostMaskType, x as PostMedia, y as PostMediaImage, z as PostMediaType, B as PostMediaVideo, D as PostPublishingType, F as PostStamp, H as PostVisibility, S as SendChatMessageRequest, J as StampSetType, U as User, K as UserAccessLevel, N as UserAvatar, Q as UserVisibility } from './client-iXbbRxc-.cjs';
3
- import http from 'http';
1
+ import { A as PostCreatedEvent, B as SendChatMessageRequest, C as MediaUploadType, D as PingEvent, E as OfficialStampSet, F as PostMediaType, G as UserVisibility, H as User, I as PostMediaVideo, L as PostPublishingType, M as PostMaskType, N as PostMedia, O as Post, P as PostMediaImage, R as PostStamp, S as MediaUploadStatus, T as OfficialStamp, U as UserAccessLevel, V as StampSetType, W as UserAvatar, _ as LanguageCode, a as OAuth2Authenticator, b as MediaStamp, c as ChatMessageReceivedEvent, d as EventReason, f as EventType, g as InitiatePostMediaUploadResponse, h as InitiatePostMediaUploadRequest, i as AuthenticatorOptions, j as PostMask, k as PostAccessLevel, l as CreatePostRequest, m as GetStampsRequest, n as ClientOptions, o as EventHandler, p as GetPostMediaStatusResponse, r as Authenticator, s as ChatMessage, t as Client, u as Event, v as Media, w as MediaVideo, x as MediaType, y as MediaImage, z as PostVisibility } from "./client-C90Zd_yq.cjs";
2
+ import http from "http";
4
3
 
4
+ //#region src/webhook.d.ts
5
5
  interface WebhookServerOptions {
6
- port?: number;
7
- publicKey: Buffer;
8
- handler: EventHandler;
9
- syncHandling?: boolean;
6
+ port?: number;
7
+ publicKey: Buffer;
8
+ handler: EventHandler;
9
+ syncHandling?: boolean;
10
10
  }
11
11
  declare class WebhookServer {
12
- private readonly server;
13
- private readonly port;
14
- private readonly publicKey;
15
- private readonly handler;
16
- private readonly syncHandling;
17
- constructor(options: WebhookServerOptions);
18
- private requestListener;
19
- private handleEvent;
20
- start(): Promise<void>;
21
- shutdown(): Promise<void>;
22
- get address(): string;
23
- get httpServer(): http.Server;
24
- get eventHandlerFunc(): (req: http.IncomingMessage, res: http.ServerResponse) => Promise<void>;
12
+ private readonly server;
13
+ private readonly port;
14
+ private readonly publicKey;
15
+ private readonly handler;
16
+ private readonly syncHandling;
17
+ constructor(options: WebhookServerOptions);
18
+ private requestListener;
19
+ private handleEvent;
20
+ start(): Promise<void>;
21
+ shutdown(): Promise<void>;
22
+ get address(): string;
23
+ get httpServer(): http.Server;
24
+ get eventHandlerFunc(): (req: http.IncomingMessage, res: http.ServerResponse) => Promise<void>;
25
25
  }
26
-
26
+ //#endregion
27
+ //#region src/stream.d.ts
27
28
  interface StreamWatcherOptions {
28
- streamAddress: string;
29
- authenticator: Authenticator;
30
- authKey?: string;
29
+ streamAddress: string;
30
+ authenticator: Authenticator;
31
+ authKey?: string;
32
+ /** 再接続の最大試行回数。デフォルト: 3 */
33
+ maxRetries?: number;
31
34
  }
32
35
  declare class StreamWatcher {
33
- private readonly authenticator;
34
- private readonly authKey?;
35
- private readonly streamClient;
36
- private aborted;
37
- constructor(options: StreamWatcherOptions);
38
- private getMetadata;
39
- private connect;
40
- private reconnect;
41
- watch(handler: EventHandler): Promise<void>;
42
- private handleEvent;
43
- stop(): void;
36
+ private readonly authenticator;
37
+ private readonly authKey?;
38
+ private readonly streamClient;
39
+ private readonly maxRetries;
40
+ private aborted;
41
+ constructor(options: StreamWatcherOptions);
42
+ private getMetadata;
43
+ private connect;
44
+ private reconnect;
45
+ watch(handler: EventHandler): Promise<void>;
46
+ private handleEvent;
47
+ stop(): void;
44
48
  }
45
-
46
- export { Authenticator, EventHandler, StreamWatcher, type StreamWatcherOptions, WebhookServer, type WebhookServerOptions };
49
+ //#endregion
50
+ export { type Authenticator, type AuthenticatorOptions, type ChatMessage, type ChatMessageReceivedEvent, Client, type ClientOptions, type CreatePostRequest, type Event, type EventHandler, EventReason, EventType, type GetPostMediaStatusResponse, type GetStampsRequest, type InitiatePostMediaUploadRequest, type InitiatePostMediaUploadResponse, LanguageCode, type Media, type MediaImage, type MediaStamp, MediaType, MediaUploadStatus, MediaUploadType, type MediaVideo, OAuth2Authenticator, type OfficialStamp, type OfficialStampSet, type PingEvent, type Post, PostAccessLevel, type PostCreatedEvent, type PostMask, PostMaskType, type PostMedia, type PostMediaImage, PostMediaType, type PostMediaVideo, PostPublishingType, type PostStamp, PostVisibility, type SendChatMessageRequest, StampSetType, StreamWatcher, type StreamWatcherOptions, type User, UserAccessLevel, type UserAvatar, UserVisibility, WebhookServer, type WebhookServerOptions };
@@ -0,0 +1,50 @@
1
+ import { A as PostCreatedEvent, B as SendChatMessageRequest, C as MediaUploadType, D as PingEvent, E as OfficialStampSet, F as PostMediaType, G as UserVisibility, H as User, I as PostMediaVideo, L as PostPublishingType, M as PostMaskType, N as PostMedia, O as Post, P as PostMediaImage, R as PostStamp, S as MediaUploadStatus, T as OfficialStamp, U as UserAccessLevel, V as StampSetType, W as UserAvatar, _ as LanguageCode, a as OAuth2Authenticator, b as MediaStamp, c as ChatMessageReceivedEvent, d as EventReason, f as EventType, g as InitiatePostMediaUploadResponse, h as InitiatePostMediaUploadRequest, i as AuthenticatorOptions, j as PostMask, k as PostAccessLevel, l as CreatePostRequest, m as GetStampsRequest, n as ClientOptions, o as EventHandler, p as GetPostMediaStatusResponse, r as Authenticator, s as ChatMessage, t as Client, u as Event, v as Media, w as MediaVideo, x as MediaType, y as MediaImage, z as PostVisibility } from "./client-DPeVTCYr.mjs";
2
+ import http from "http";
3
+
4
+ //#region src/webhook.d.ts
5
+ interface WebhookServerOptions {
6
+ port?: number;
7
+ publicKey: Buffer;
8
+ handler: EventHandler;
9
+ syncHandling?: boolean;
10
+ }
11
+ declare class WebhookServer {
12
+ private readonly server;
13
+ private readonly port;
14
+ private readonly publicKey;
15
+ private readonly handler;
16
+ private readonly syncHandling;
17
+ constructor(options: WebhookServerOptions);
18
+ private requestListener;
19
+ private handleEvent;
20
+ start(): Promise<void>;
21
+ shutdown(): Promise<void>;
22
+ get address(): string;
23
+ get httpServer(): http.Server;
24
+ get eventHandlerFunc(): (req: http.IncomingMessage, res: http.ServerResponse) => Promise<void>;
25
+ }
26
+ //#endregion
27
+ //#region src/stream.d.ts
28
+ interface StreamWatcherOptions {
29
+ streamAddress: string;
30
+ authenticator: Authenticator;
31
+ authKey?: string;
32
+ /** 再接続の最大試行回数。デフォルト: 3 */
33
+ maxRetries?: number;
34
+ }
35
+ declare class StreamWatcher {
36
+ private readonly authenticator;
37
+ private readonly authKey?;
38
+ private readonly streamClient;
39
+ private readonly maxRetries;
40
+ private aborted;
41
+ constructor(options: StreamWatcherOptions);
42
+ private getMetadata;
43
+ private connect;
44
+ private reconnect;
45
+ watch(handler: EventHandler): Promise<void>;
46
+ private handleEvent;
47
+ stop(): void;
48
+ }
49
+ //#endregion
50
+ export { type Authenticator, type AuthenticatorOptions, type ChatMessage, type ChatMessageReceivedEvent, Client, type ClientOptions, type CreatePostRequest, type Event, type EventHandler, EventReason, EventType, type GetPostMediaStatusResponse, type GetStampsRequest, type InitiatePostMediaUploadRequest, type InitiatePostMediaUploadResponse, LanguageCode, type Media, type MediaImage, type MediaStamp, MediaType, MediaUploadStatus, MediaUploadType, type MediaVideo, OAuth2Authenticator, type OfficialStamp, type OfficialStampSet, type PingEvent, type Post, PostAccessLevel, type PostCreatedEvent, type PostMask, PostMaskType, type PostMedia, type PostMediaImage, PostMediaType, type PostMediaVideo, PostPublishingType, type PostStamp, PostVisibility, type SendChatMessageRequest, StampSetType, StreamWatcher, type StreamWatcherOptions, type User, UserAccessLevel, type UserAvatar, UserVisibility, WebhookServer, type WebhookServerOptions };
package/dist/index.mjs ADDED
@@ -0,0 +1,559 @@
1
+ import { a as MediaUploadStatus, c as PostMaskType, d as PostVisibility, f as StampSetType, i as MediaType, l as PostMediaType, m as UserVisibility, n as EventType, o as MediaUploadType, p as UserAccessLevel, r as LanguageCode, s as PostAccessLevel, t as EventReason, u as PostPublishingType } from "./types-BrJ83qtL.mjs";
2
+ import * as grpc from "@grpc/grpc-js";
3
+ import path from "path";
4
+ import { fileURLToPath } from "url";
5
+ import * as protoLoader from "@grpc/proto-loader";
6
+ import http from "http";
7
+ import crypto from "crypto";
8
+ //#region src/auth.ts
9
+ var OAuth2Authenticator = class {
10
+ clientId;
11
+ clientSecret;
12
+ tokenUrl;
13
+ accessToken = null;
14
+ expiresAt = null;
15
+ refreshPromise = null;
16
+ constructor(options) {
17
+ this.clientId = options.clientId;
18
+ this.clientSecret = options.clientSecret;
19
+ this.tokenUrl = options.tokenUrl;
20
+ }
21
+ async getAccessToken() {
22
+ if (this.accessToken && this.expiresAt && Date.now() < this.expiresAt) return this.accessToken;
23
+ if (!this.refreshPromise) this.refreshPromise = this.refreshToken().finally(() => {
24
+ this.refreshPromise = null;
25
+ });
26
+ await this.refreshPromise;
27
+ return this.accessToken;
28
+ }
29
+ async refreshToken() {
30
+ const credentials = Buffer.from(`${encodeURIComponent(this.clientId)}:${encodeURIComponent(this.clientSecret)}`).toString("base64");
31
+ const response = await fetch(this.tokenUrl, {
32
+ method: "POST",
33
+ headers: {
34
+ "Content-Type": "application/x-www-form-urlencoded",
35
+ Authorization: `Basic ${credentials}`
36
+ },
37
+ body: "grant_type=client_credentials"
38
+ });
39
+ if (!response.ok) throw new Error(`Failed to acquire access token: ${response.status} ${response.statusText}`);
40
+ const data = await response.json();
41
+ this.accessToken = data.access_token;
42
+ this.expiresAt = Date.now() + (data.expires_in - 60) * 1e3;
43
+ }
44
+ };
45
+ //#endregion
46
+ //#region src/proto.ts
47
+ const __filename = fileURLToPath(import.meta.url);
48
+ const __dirname = path.dirname(__filename);
49
+ const PROTO_DIR = path.resolve(__dirname, "..", "proto");
50
+ const PROTO_LOADER_OPTIONS = {
51
+ keepCase: false,
52
+ longs: Number,
53
+ enums: Number,
54
+ defaults: true,
55
+ oneofs: true
56
+ };
57
+ let _packageDefinition = null;
58
+ let _grpcObject = null;
59
+ function getPackageDefinition() {
60
+ if (!_packageDefinition) _packageDefinition = protoLoader.loadSync([
61
+ "social/mixi/application/service/application_api/v1/service.proto",
62
+ "social/mixi/application/service/application_stream/v1/service.proto",
63
+ "social/mixi/application/service/client_endpoint/v1/service.proto"
64
+ ], {
65
+ ...PROTO_LOADER_OPTIONS,
66
+ includeDirs: [PROTO_DIR]
67
+ });
68
+ return _packageDefinition;
69
+ }
70
+ function getGrpcObject() {
71
+ if (!_grpcObject) _grpcObject = grpc.loadPackageDefinition(getPackageDefinition());
72
+ return _grpcObject;
73
+ }
74
+ function resolveNested(obj, path) {
75
+ const parts = path.split(".");
76
+ let current = obj;
77
+ for (const part of parts) {
78
+ current = current[part];
79
+ if (current === void 0) throw new Error(`Could not resolve gRPC path: ${path}`);
80
+ }
81
+ return current;
82
+ }
83
+ function getApiServiceClient() {
84
+ return resolveNested(getGrpcObject(), "social.mixi.application.service.application_api.v1.ApplicationService");
85
+ }
86
+ function getStreamServiceClient() {
87
+ return resolveNested(getGrpcObject(), "social.mixi.application.service.application_stream.v1.ApplicationService");
88
+ }
89
+ function getSendEventRequestType() {
90
+ const msgDef = getPackageDefinition()["social.mixi.application.service.client_endpoint.v1.SendEventRequest"];
91
+ if (!msgDef || !("type" in msgDef)) throw new Error("SendEventRequest message type not found in proto");
92
+ return msgDef.type;
93
+ }
94
+ //#endregion
95
+ //#region src/convert.ts
96
+ function toDate(ts) {
97
+ if (!ts) return null;
98
+ if (ts instanceof Date) return ts;
99
+ const obj = ts;
100
+ if (obj.seconds !== void 0) {
101
+ const ms = Number(obj.seconds) * 1e3 + Math.floor((obj.nanos || 0) / 1e6);
102
+ return new Date(ms);
103
+ }
104
+ return null;
105
+ }
106
+ function convertUserAvatar(raw) {
107
+ if (!raw) return null;
108
+ const r = raw;
109
+ return {
110
+ largeImageUrl: r.largeImageUrl || "",
111
+ largeImageMimeType: r.largeImageMimeType || "",
112
+ largeImageHeight: r.largeImageHeight || 0,
113
+ largeImageWidth: r.largeImageWidth || 0,
114
+ smallImageUrl: r.smallImageUrl || "",
115
+ smallImageMimeType: r.smallImageMimeType || "",
116
+ smallImageHeight: r.smallImageHeight || 0,
117
+ smallImageWidth: r.smallImageWidth || 0
118
+ };
119
+ }
120
+ function convertUser(raw) {
121
+ const r = raw;
122
+ return {
123
+ userId: r.userId || "",
124
+ isDisabled: r.isDisabled || false,
125
+ name: r.name || "",
126
+ displayName: r.displayName || "",
127
+ profile: r.profile || "",
128
+ userAvatar: convertUserAvatar(r.userAvatar),
129
+ visibility: r.visibility || 0,
130
+ accessLevel: r.accessLevel || 0
131
+ };
132
+ }
133
+ function convertMediaImage(raw) {
134
+ if (!raw) return void 0;
135
+ const r = raw;
136
+ return {
137
+ largeImageUrl: r.largeImageUrl || "",
138
+ largeImageMimeType: r.largeImageMimeType || "",
139
+ largeImageHeight: r.largeImageHeight || 0,
140
+ largeImageWidth: r.largeImageWidth || 0,
141
+ smallImageUrl: r.smallImageUrl || "",
142
+ smallImageMimeType: r.smallImageMimeType || "",
143
+ smallImageHeight: r.smallImageHeight || 0,
144
+ smallImageWidth: r.smallImageWidth || 0
145
+ };
146
+ }
147
+ function convertMediaVideo(raw) {
148
+ if (!raw) return void 0;
149
+ const r = raw;
150
+ return {
151
+ videoUrl: r.videoUrl || "",
152
+ videoMimeType: r.videoMimeType || "",
153
+ videoHeight: r.videoHeight || 0,
154
+ videoWidth: r.videoWidth || 0,
155
+ previewImageUrl: r.previewImageUrl || "",
156
+ previewImageMimeType: r.previewImageMimeType || "",
157
+ previewImageHeight: r.previewImageHeight || 0,
158
+ previewImageWidth: r.previewImageWidth || 0,
159
+ duration: r.duration || 0
160
+ };
161
+ }
162
+ function convertMedia(raw) {
163
+ const r = raw;
164
+ return {
165
+ mediaType: r.mediaType || 0,
166
+ image: convertMediaImage(r.image),
167
+ video: convertMediaVideo(r.video)
168
+ };
169
+ }
170
+ function convertMediaStamp(raw) {
171
+ if (!raw) return null;
172
+ const r = raw;
173
+ return {
174
+ url: r.url || "",
175
+ mimeType: r.mimeType || "",
176
+ height: r.height || 0,
177
+ width: r.width || 0
178
+ };
179
+ }
180
+ function convertPostMedia(raw) {
181
+ const r = raw;
182
+ return {
183
+ mediaType: r.mediaType || 0,
184
+ image: r.image ? convertMediaImage(r.image) : void 0,
185
+ video: r.video ? convertMediaVideo(r.video) : void 0
186
+ };
187
+ }
188
+ function convertPostMask(raw) {
189
+ if (!raw) return void 0;
190
+ const r = raw;
191
+ return {
192
+ maskType: r.maskType || 0,
193
+ caption: r.caption || ""
194
+ };
195
+ }
196
+ function convertPostStamp(raw) {
197
+ const r = raw;
198
+ return {
199
+ stamp: convertMediaStamp(r.stamp),
200
+ count: Number(r.count) || 0
201
+ };
202
+ }
203
+ function convertPost(raw) {
204
+ const r = raw;
205
+ return {
206
+ postId: r.postId || "",
207
+ isDeleted: r.isDeleted || false,
208
+ creatorId: r.creatorId || "",
209
+ text: r.text || "",
210
+ createdAt: toDate(r.createdAt),
211
+ postMediaList: (r.postMediaList || []).map(convertPostMedia),
212
+ inReplyToPostId: r.inReplyToPostId || void 0,
213
+ postMask: convertPostMask(r.postMask),
214
+ visibility: r.visibility || 0,
215
+ accessLevel: r.accessLevel || 0,
216
+ stamps: (r.stamps || []).map(convertPostStamp),
217
+ readerStampId: r.readerStampId || void 0
218
+ };
219
+ }
220
+ function convertChatMessage(raw) {
221
+ const r = raw;
222
+ return {
223
+ roomId: r.roomId || "",
224
+ messageId: r.messageId || "",
225
+ creatorId: r.creatorId || "",
226
+ text: r.text || "",
227
+ createdAt: toDate(r.createdAt),
228
+ mediaList: (r.mediaList || []).map(convertMedia),
229
+ postId: r.postId || void 0
230
+ };
231
+ }
232
+ function convertOfficialStamp(raw) {
233
+ const r = raw;
234
+ return {
235
+ stampId: r.stampId || "",
236
+ index: r.index || 0,
237
+ searchTags: r.searchTags || [],
238
+ url: r.url || ""
239
+ };
240
+ }
241
+ function convertOfficialStampSet(raw) {
242
+ const r = raw;
243
+ return {
244
+ name: r.name || "",
245
+ spriteUrl: r.spriteUrl || "",
246
+ stamps: (r.stamps || []).map(convertOfficialStamp),
247
+ stampSetId: r.stampSetId || "",
248
+ startAt: toDate(r.startAt) ?? void 0,
249
+ endAt: toDate(r.endAt) ?? void 0,
250
+ stampSetType: r.stampSetType || 0
251
+ };
252
+ }
253
+ function convertPostCreatedEvent(raw) {
254
+ const r = raw;
255
+ return {
256
+ eventReasonList: r.eventReasonList || [],
257
+ post: r.post ? convertPost(r.post) : null,
258
+ issuer: r.issuer ? convertUser(r.issuer) : null
259
+ };
260
+ }
261
+ function convertChatMessageReceivedEvent(raw) {
262
+ const r = raw;
263
+ return {
264
+ eventReasonList: r.eventReasonList || [],
265
+ message: r.message ? convertChatMessage(r.message) : null,
266
+ issuer: r.issuer ? convertUser(r.issuer) : null
267
+ };
268
+ }
269
+ function convertEvent(raw) {
270
+ const r = raw;
271
+ return {
272
+ eventId: r.eventId || "",
273
+ eventType: r.eventType || 0,
274
+ pingEvent: r.pingEvent || void 0,
275
+ postCreatedEvent: r.postCreatedEvent ? convertPostCreatedEvent(r.postCreatedEvent) : void 0,
276
+ chatMessageReceivedEvent: r.chatMessageReceivedEvent ? convertChatMessageReceivedEvent(r.chatMessageReceivedEvent) : void 0
277
+ };
278
+ }
279
+ //#endregion
280
+ //#region src/client.ts
281
+ var Client = class {
282
+ grpcClient;
283
+ authenticator;
284
+ authKey;
285
+ constructor(options) {
286
+ this.grpcClient = new (getApiServiceClient())(options.apiAddress, grpc.credentials.createSsl());
287
+ this.authenticator = options.authenticator;
288
+ this.authKey = options.authKey;
289
+ }
290
+ async getMetadata() {
291
+ const token = await this.authenticator.getAccessToken();
292
+ const metadata = new grpc.Metadata();
293
+ metadata.add("authorization", `Bearer ${token}`);
294
+ if (this.authKey) metadata.add("x-auth-key", this.authKey);
295
+ return metadata;
296
+ }
297
+ call(method, request) {
298
+ return this.getMetadata().then((metadata) => {
299
+ return new Promise((resolve, reject) => {
300
+ const fn = this.grpcClient[method];
301
+ if (!fn) {
302
+ reject(/* @__PURE__ */ new Error(`Method "${method}" not found on gRPC client`));
303
+ return;
304
+ }
305
+ fn.call(this.grpcClient, request, metadata, (err, response) => {
306
+ if (err) reject(err);
307
+ else resolve(response);
308
+ });
309
+ });
310
+ });
311
+ }
312
+ async getUsers(userIdList) {
313
+ return ((await this.call("getUsers", { userIdList })).users || []).map(convertUser);
314
+ }
315
+ async getPosts(postIdList) {
316
+ return ((await this.call("getPosts", { postIdList })).posts || []).map(convertPost);
317
+ }
318
+ async createPost(request) {
319
+ return convertPost((await this.call("createPost", request)).post);
320
+ }
321
+ async initiatePostMediaUpload(request) {
322
+ return this.call("initiatePostMediaUpload", request);
323
+ }
324
+ async getPostMediaStatus(mediaId) {
325
+ return this.call("getPostMediaStatus", { mediaId });
326
+ }
327
+ async sendChatMessage(request) {
328
+ return convertChatMessage((await this.call("sendChatMessage", request)).message);
329
+ }
330
+ async getStamps(request) {
331
+ return ((await this.call("getStamps", request || {})).officialStampSets || []).map(convertOfficialStampSet);
332
+ }
333
+ async addStampToPost(postId, stampId) {
334
+ return convertPost((await this.call("addStampToPost", {
335
+ postId,
336
+ stampId
337
+ })).post);
338
+ }
339
+ close() {
340
+ this.grpcClient.close();
341
+ }
342
+ };
343
+ //#endregion
344
+ //#region src/webhook.ts
345
+ const TIMESTAMP_TOLERANCE = 300;
346
+ var WebhookServer = class {
347
+ server;
348
+ port;
349
+ publicKey;
350
+ handler;
351
+ syncHandling;
352
+ constructor(options) {
353
+ this.port = options.port || 8080;
354
+ this.handler = options.handler;
355
+ this.syncHandling = options.syncHandling || false;
356
+ const derPrefix = Buffer.from("302a300506032b6570032100", "hex");
357
+ const derKey = Buffer.concat([derPrefix, options.publicKey]);
358
+ this.publicKey = crypto.createPublicKey({
359
+ key: derKey,
360
+ format: "der",
361
+ type: "spki"
362
+ });
363
+ this.server = http.createServer(this.requestListener.bind(this));
364
+ }
365
+ async requestListener(req, res) {
366
+ if (req.method === "GET" && req.url === "/healthz") {
367
+ res.writeHead(200);
368
+ res.end("OK");
369
+ return;
370
+ }
371
+ if (req.method === "POST" && req.url === "/events") {
372
+ await this.handleEvent(req, res);
373
+ return;
374
+ }
375
+ res.writeHead(404);
376
+ res.end("Not Found");
377
+ }
378
+ async handleEvent(req, res) {
379
+ const signatureBase64 = req.headers["x-mixi2-application-event-signature"];
380
+ if (!signatureBase64) {
381
+ res.writeHead(401);
382
+ res.end("missing x-mixi2-application-event-signature");
383
+ return;
384
+ }
385
+ const signature = Buffer.from(signatureBase64, "base64");
386
+ if (signature.length === 0) {
387
+ res.writeHead(401);
388
+ res.end("x-mixi2-application-event-signature is invalid");
389
+ return;
390
+ }
391
+ const timestamp = req.headers["x-mixi2-application-event-timestamp"];
392
+ if (!timestamp) {
393
+ res.writeHead(401);
394
+ res.end("missing x-mixi2-application-event-timestamp");
395
+ return;
396
+ }
397
+ const unixTime = parseInt(timestamp, 10);
398
+ if (isNaN(unixTime)) {
399
+ res.writeHead(401);
400
+ res.end("x-mixi2-application-event-timestamp is invalid");
401
+ return;
402
+ }
403
+ const diff = Math.floor(Date.now() / 1e3) - unixTime;
404
+ if (diff > TIMESTAMP_TOLERANCE) {
405
+ res.writeHead(401);
406
+ res.end("x-mixi2-application-event-timestamp is too old");
407
+ return;
408
+ }
409
+ if (diff < -TIMESTAMP_TOLERANCE) {
410
+ res.writeHead(401);
411
+ res.end("x-mixi2-application-event-timestamp is in the future");
412
+ return;
413
+ }
414
+ const body = await new Promise((resolve, reject) => {
415
+ const chunks = [];
416
+ req.on("data", (chunk) => chunks.push(chunk));
417
+ req.on("end", () => resolve(Buffer.concat(chunks)));
418
+ req.on("error", reject);
419
+ });
420
+ const dataToVerify = Buffer.concat([body, Buffer.from(timestamp)]);
421
+ if (!crypto.verify(null, dataToVerify, this.publicKey, signature)) {
422
+ res.writeHead(401);
423
+ res.end("Signature is invalid");
424
+ return;
425
+ }
426
+ let events;
427
+ try {
428
+ events = (getSendEventRequestType().decode(new Uint8Array(body)).events || []).map(convertEvent);
429
+ } catch {
430
+ res.writeHead(400);
431
+ res.end("Failed to parse request body");
432
+ return;
433
+ }
434
+ res.writeHead(204);
435
+ res.end();
436
+ for (const event of events) {
437
+ if (event.eventType === EventType.PING) continue;
438
+ if (this.syncHandling) try {
439
+ await this.handler.handle(event);
440
+ } catch (err) {
441
+ console.error(`Failed to handle event ${event.eventId}:`, err);
442
+ }
443
+ else Promise.resolve(this.handler.handle(event)).catch((err) => {
444
+ console.error(`Failed to handle event ${event.eventId}:`, err);
445
+ });
446
+ }
447
+ }
448
+ start() {
449
+ return new Promise((resolve) => {
450
+ this.server.listen(this.port, () => {
451
+ resolve();
452
+ });
453
+ });
454
+ }
455
+ shutdown() {
456
+ return new Promise((resolve, reject) => {
457
+ this.server.close((err) => {
458
+ if (err) reject(err);
459
+ else resolve();
460
+ });
461
+ });
462
+ }
463
+ get address() {
464
+ return `:${this.port}`;
465
+ }
466
+ get httpServer() {
467
+ return this.server;
468
+ }
469
+ get eventHandlerFunc() {
470
+ return this.handleEvent.bind(this);
471
+ }
472
+ };
473
+ //#endregion
474
+ //#region src/stream.ts
475
+ var StreamWatcher = class {
476
+ authenticator;
477
+ authKey;
478
+ streamClient;
479
+ maxRetries;
480
+ aborted = false;
481
+ constructor(options) {
482
+ this.streamClient = new (getStreamServiceClient())(options.streamAddress, grpc.credentials.createSsl());
483
+ this.authenticator = options.authenticator;
484
+ this.authKey = options.authKey;
485
+ this.maxRetries = options.maxRetries ?? 3;
486
+ }
487
+ async getMetadata() {
488
+ const token = await this.authenticator.getAccessToken();
489
+ const metadata = new grpc.Metadata();
490
+ metadata.add("authorization", `Bearer ${token}`);
491
+ if (this.authKey) metadata.add("x-auth-key", this.authKey);
492
+ return metadata;
493
+ }
494
+ async connect() {
495
+ const metadata = await this.getMetadata();
496
+ const fn = this.streamClient["subscribeEvents"];
497
+ if (!fn) throw new Error("subscribeEvents method not found on stream client");
498
+ return fn.call(this.streamClient, {}, metadata);
499
+ }
500
+ async reconnect() {
501
+ const maxRetries = this.maxRetries;
502
+ let lastError;
503
+ for (let i = 0; i < maxRetries; i++) {
504
+ if (this.aborted) throw new Error("Watcher aborted");
505
+ await new Promise((resolve) => setTimeout(resolve, Math.pow(2, i) * 1e3));
506
+ try {
507
+ return await this.connect();
508
+ } catch (err) {
509
+ lastError = err instanceof Error ? err : new Error(String(err));
510
+ console.warn(`Reconnect attempt ${i + 1}/${maxRetries} failed:`, lastError.message);
511
+ }
512
+ }
513
+ throw lastError || /* @__PURE__ */ new Error("Failed to reconnect");
514
+ }
515
+ async watch(handler) {
516
+ this.aborted = false;
517
+ let stream = await this.connect();
518
+ return new Promise((resolve, reject) => {
519
+ const setupStream = (s) => {
520
+ s.on("data", (response) => {
521
+ const events = (response.events || []).map(convertEvent);
522
+ for (const event of events) {
523
+ if (event.eventType === EventType.PING) continue;
524
+ this.handleEvent(handler, event);
525
+ }
526
+ });
527
+ let disconnected = false;
528
+ const handleDisconnect = async () => {
529
+ if (disconnected) return;
530
+ disconnected = true;
531
+ if (this.aborted) {
532
+ resolve();
533
+ return;
534
+ }
535
+ try {
536
+ stream = await this.reconnect();
537
+ setupStream(stream);
538
+ } catch (reconnectErr) {
539
+ reject(reconnectErr);
540
+ }
541
+ };
542
+ s.on("error", handleDisconnect);
543
+ s.on("end", handleDisconnect);
544
+ };
545
+ setupStream(stream);
546
+ });
547
+ }
548
+ handleEvent(handler, event) {
549
+ Promise.resolve(handler.handle(event)).catch((err) => {
550
+ console.error(`Failed to handle event ${event.eventId}:`, err);
551
+ });
552
+ }
553
+ stop() {
554
+ this.aborted = true;
555
+ this.streamClient.close();
556
+ }
557
+ };
558
+ //#endregion
559
+ export { Client, EventReason, EventType, LanguageCode, MediaType, MediaUploadStatus, MediaUploadType, OAuth2Authenticator, PostAccessLevel, PostMaskType, PostMediaType, PostPublishingType, PostVisibility, StampSetType, StreamWatcher, UserAccessLevel, UserVisibility, WebhookServer };