mixi2-js 1.5.1 → 1.5.3

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.
@@ -126,26 +126,6 @@ declare enum ApplicationRequirement {
126
126
  PERMISSION_COMMUNITY_POST_STAMP_CREATE = 10,
127
127
  PERMISSION_COMMUNITY_MEMBER_DIRECT_MESSAGE_CREATE = 11
128
128
  }
129
- interface UserAvatar {
130
- largeImageUrl: string;
131
- largeImageMimeType: string;
132
- largeImageHeight: number;
133
- largeImageWidth: number;
134
- smallImageUrl: string;
135
- smallImageMimeType: string;
136
- smallImageHeight: number;
137
- smallImageWidth: number;
138
- }
139
- interface User {
140
- userId: string;
141
- isDisabled: boolean;
142
- name: string;
143
- displayName: string;
144
- profile: string;
145
- userAvatar: UserAvatar | null;
146
- visibility: UserVisibility;
147
- accessLevel: UserAccessLevel;
148
- }
149
129
  interface MediaImage {
150
130
  largeImageUrl: string;
151
131
  largeImageMimeType: string;
@@ -178,31 +158,21 @@ interface Media {
178
158
  image?: MediaImage;
179
159
  video?: MediaVideo;
180
160
  }
181
- interface PostMediaImage {
182
- largeImageUrl: string;
183
- largeImageMimeType: string;
184
- largeImageHeight: number;
185
- largeImageWidth: number;
186
- smallImageUrl: string;
187
- smallImageMimeType: string;
188
- smallImageHeight: number;
189
- smallImageWidth: number;
190
- }
191
- interface PostMediaVideo {
192
- videoUrl: string;
193
- videoMimeType: string;
194
- videoHeight: number;
195
- videoWidth: number;
196
- previewImageUrl: string;
197
- previewImageMimeType: string;
198
- previewImageHeight: number;
199
- previewImageWidth: number;
200
- duration: number;
201
- }
202
- interface PostMedia {
161
+ type UserAvatar = MediaImage;
162
+ type PostMediaImage = MediaImage;
163
+ type PostMediaVideo = MediaVideo;
164
+ type PostMedia = Omit<Media, "mediaType"> & {
203
165
  mediaType: PostMediaType;
204
- image?: PostMediaImage;
205
- video?: PostMediaVideo;
166
+ };
167
+ interface User {
168
+ userId: string;
169
+ isDisabled: boolean;
170
+ name: string;
171
+ displayName: string;
172
+ profile: string;
173
+ userAvatar: UserAvatar | null;
174
+ visibility: UserVisibility;
175
+ accessLevel: UserAccessLevel;
206
176
  }
207
177
  interface PostMask {
208
178
  maskType: PostMaskType;
@@ -126,26 +126,6 @@ declare enum ApplicationRequirement {
126
126
  PERMISSION_COMMUNITY_POST_STAMP_CREATE = 10,
127
127
  PERMISSION_COMMUNITY_MEMBER_DIRECT_MESSAGE_CREATE = 11
128
128
  }
129
- interface UserAvatar {
130
- largeImageUrl: string;
131
- largeImageMimeType: string;
132
- largeImageHeight: number;
133
- largeImageWidth: number;
134
- smallImageUrl: string;
135
- smallImageMimeType: string;
136
- smallImageHeight: number;
137
- smallImageWidth: number;
138
- }
139
- interface User {
140
- userId: string;
141
- isDisabled: boolean;
142
- name: string;
143
- displayName: string;
144
- profile: string;
145
- userAvatar: UserAvatar | null;
146
- visibility: UserVisibility;
147
- accessLevel: UserAccessLevel;
148
- }
149
129
  interface MediaImage {
150
130
  largeImageUrl: string;
151
131
  largeImageMimeType: string;
@@ -178,31 +158,21 @@ interface Media {
178
158
  image?: MediaImage;
179
159
  video?: MediaVideo;
180
160
  }
181
- interface PostMediaImage {
182
- largeImageUrl: string;
183
- largeImageMimeType: string;
184
- largeImageHeight: number;
185
- largeImageWidth: number;
186
- smallImageUrl: string;
187
- smallImageMimeType: string;
188
- smallImageHeight: number;
189
- smallImageWidth: number;
190
- }
191
- interface PostMediaVideo {
192
- videoUrl: string;
193
- videoMimeType: string;
194
- videoHeight: number;
195
- videoWidth: number;
196
- previewImageUrl: string;
197
- previewImageMimeType: string;
198
- previewImageHeight: number;
199
- previewImageWidth: number;
200
- duration: number;
201
- }
202
- interface PostMedia {
161
+ type UserAvatar = MediaImage;
162
+ type PostMediaImage = MediaImage;
163
+ type PostMediaVideo = MediaVideo;
164
+ type PostMedia = Omit<Media, "mediaType"> & {
203
165
  mediaType: PostMediaType;
204
- image?: PostMediaImage;
205
- video?: PostMediaVideo;
166
+ };
167
+ interface User {
168
+ userId: string;
169
+ isDisabled: boolean;
170
+ name: string;
171
+ displayName: string;
172
+ profile: string;
173
+ userAvatar: UserAvatar | null;
174
+ visibility: UserVisibility;
175
+ accessLevel: UserAccessLevel;
206
176
  }
207
177
  interface PostMask {
208
178
  maskType: PostMaskType;
@@ -483,9 +483,25 @@ var ReasonFilter = class {
483
483
  //#region src/helpers/text-splitter.ts
484
484
  /** mixi2 の 1 ポストあたりの最大文字数 */
485
485
  const maxPostLength = 149;
486
+ const WORD_BREAK_CHARS = new Set([
487
+ " ",
488
+ " ",
489
+ "、",
490
+ "。",
491
+ "!",
492
+ "?",
493
+ "!",
494
+ "?",
495
+ "\n"
496
+ ]);
497
+ const graphemeSegmenter = new Intl.Segmenter("ja", { granularity: "grapheme" });
498
+ function toGraphemes(text) {
499
+ return Array.from(graphemeSegmenter.segment(text), (s) => s.segment);
500
+ }
486
501
  /**
487
502
  * 長いテキストを mixi2 の文字数制限内に収まる複数チャンクに分割するヘルパー。
488
503
  * デフォルトは 149 文字制限(mixi2 のポスト本文上限)に準拠。
504
+ * 文字数は mixi2 のカウント仕様に合わせ、絵文字(ZWJ シーケンスを含む)を 1 文字として数える。
489
505
  *
490
506
  * @example
491
507
  * const splitter = new TextSplitter();
@@ -506,20 +522,27 @@ var TextSplitter = class {
506
522
  * テキストが maxLength 以内の場合は 1 要素の配列を返す。
507
523
  */
508
524
  split(text) {
509
- if (text.length <= this.maxLength) return [text];
525
+ const graphemes = toGraphemes(text);
526
+ if (graphemes.length <= this.maxLength) return [text];
510
527
  const chunks = [];
511
- let remaining = text;
528
+ let remaining = graphemes;
512
529
  while (remaining.length > this.maxLength) {
513
530
  let splitAt = this.maxLength;
514
531
  if (this.splitOnWord) {
515
- const candidate = remaining.slice(0, this.maxLength);
516
- const lastBreak = Math.max(candidate.lastIndexOf(" "), candidate.lastIndexOf(" "), candidate.lastIndexOf("、"), candidate.lastIndexOf("。"), candidate.lastIndexOf("!"), candidate.lastIndexOf("?"), candidate.lastIndexOf("!"), candidate.lastIndexOf("?"), candidate.lastIndexOf("\n"));
532
+ let lastBreak = -1;
533
+ for (let i = this.maxLength - 1; i >= 0; i--) if (WORD_BREAK_CHARS.has(remaining[i])) {
534
+ lastBreak = i;
535
+ break;
536
+ }
517
537
  if (lastBreak > 0) splitAt = lastBreak + 1;
518
538
  }
519
- chunks.push(remaining.slice(0, splitAt).trimEnd());
520
- remaining = remaining.slice(splitAt).trimStart();
539
+ chunks.push(remaining.slice(0, splitAt).join("").trimEnd());
540
+ remaining = remaining.slice(splitAt);
541
+ let leading = 0;
542
+ while (leading < remaining.length && remaining[leading].trim() === "") leading++;
543
+ if (leading > 0) remaining = remaining.slice(leading);
521
544
  }
522
- if (remaining.length > 0) chunks.push(remaining);
545
+ if (remaining.length > 0) chunks.push(remaining.join(""));
523
546
  return chunks;
524
547
  }
525
548
  };
@@ -1,4 +1,4 @@
1
- import { C as GetCommunityMemberListRequest, G as PostMask, Z as PostPublishingType, _ as Event, a as ApplicationVersion, c as Community, d as CommunityPluginManagedEvent, g as CreatePostRequest, it as User, k as InitiatePostMediaUploadRequest, m as CommunityUsingApplication, n as Client, t as EventHandler, u as CommunityMemberChangedEvent, v as EventReason, x as GetCommunitiesUsingApplicationRequest, y as EventType } from "../event-CgwQm1Mk.cjs";
1
+ import { C as GetCommunityMemberListRequest, G as PostMask, Z as PostPublishingType, _ as Event, a as ApplicationVersion, c as Community, d as CommunityPluginManagedEvent, g as CreatePostRequest, it as User, k as InitiatePostMediaUploadRequest, m as CommunityUsingApplication, n as Client, t as EventHandler, u as CommunityMemberChangedEvent, v as EventReason, x as GetCommunitiesUsingApplicationRequest, y as EventType } from "../event-NbLU9vQn.cjs";
2
2
 
3
3
  //#region src/helpers/address.d.ts
4
4
  /**
@@ -324,6 +324,7 @@ interface TextSplitterOptions {
324
324
  /**
325
325
  * 長いテキストを mixi2 の文字数制限内に収まる複数チャンクに分割するヘルパー。
326
326
  * デフォルトは 149 文字制限(mixi2 のポスト本文上限)に準拠。
327
+ * 文字数は mixi2 のカウント仕様に合わせ、絵文字(ZWJ シーケンスを含む)を 1 文字として数える。
327
328
  *
328
329
  * @example
329
330
  * const splitter = new TextSplitter();
@@ -1,4 +1,4 @@
1
- import { C as GetCommunityMemberListRequest, G as PostMask, Z as PostPublishingType, _ as Event, a as ApplicationVersion, c as Community, d as CommunityPluginManagedEvent, g as CreatePostRequest, it as User, k as InitiatePostMediaUploadRequest, m as CommunityUsingApplication, n as Client, t as EventHandler, u as CommunityMemberChangedEvent, v as EventReason, x as GetCommunitiesUsingApplicationRequest, y as EventType } from "../event-LyIxrXkO.mjs";
1
+ import { C as GetCommunityMemberListRequest, G as PostMask, Z as PostPublishingType, _ as Event, a as ApplicationVersion, c as Community, d as CommunityPluginManagedEvent, g as CreatePostRequest, it as User, k as InitiatePostMediaUploadRequest, m as CommunityUsingApplication, n as Client, t as EventHandler, u as CommunityMemberChangedEvent, v as EventReason, x as GetCommunitiesUsingApplicationRequest, y as EventType } from "../event-DDaasL1T.mjs";
2
2
 
3
3
  //#region src/helpers/address.d.ts
4
4
  /**
@@ -324,6 +324,7 @@ interface TextSplitterOptions {
324
324
  /**
325
325
  * 長いテキストを mixi2 の文字数制限内に収まる複数チャンクに分割するヘルパー。
326
326
  * デフォルトは 149 文字制限(mixi2 のポスト本文上限)に準拠。
327
+ * 文字数は mixi2 のカウント仕様に合わせ、絵文字(ZWJ シーケンスを含む)を 1 文字として数える。
327
328
  *
328
329
  * @example
329
330
  * const splitter = new TextSplitter();
@@ -482,9 +482,25 @@ var ReasonFilter = class {
482
482
  //#region src/helpers/text-splitter.ts
483
483
  /** mixi2 の 1 ポストあたりの最大文字数 */
484
484
  const maxPostLength = 149;
485
+ const WORD_BREAK_CHARS = new Set([
486
+ " ",
487
+ " ",
488
+ "、",
489
+ "。",
490
+ "!",
491
+ "?",
492
+ "!",
493
+ "?",
494
+ "\n"
495
+ ]);
496
+ const graphemeSegmenter = new Intl.Segmenter("ja", { granularity: "grapheme" });
497
+ function toGraphemes(text) {
498
+ return Array.from(graphemeSegmenter.segment(text), (s) => s.segment);
499
+ }
485
500
  /**
486
501
  * 長いテキストを mixi2 の文字数制限内に収まる複数チャンクに分割するヘルパー。
487
502
  * デフォルトは 149 文字制限(mixi2 のポスト本文上限)に準拠。
503
+ * 文字数は mixi2 のカウント仕様に合わせ、絵文字(ZWJ シーケンスを含む)を 1 文字として数える。
488
504
  *
489
505
  * @example
490
506
  * const splitter = new TextSplitter();
@@ -505,20 +521,27 @@ var TextSplitter = class {
505
521
  * テキストが maxLength 以内の場合は 1 要素の配列を返す。
506
522
  */
507
523
  split(text) {
508
- if (text.length <= this.maxLength) return [text];
524
+ const graphemes = toGraphemes(text);
525
+ if (graphemes.length <= this.maxLength) return [text];
509
526
  const chunks = [];
510
- let remaining = text;
527
+ let remaining = graphemes;
511
528
  while (remaining.length > this.maxLength) {
512
529
  let splitAt = this.maxLength;
513
530
  if (this.splitOnWord) {
514
- const candidate = remaining.slice(0, this.maxLength);
515
- const lastBreak = Math.max(candidate.lastIndexOf(" "), candidate.lastIndexOf(" "), candidate.lastIndexOf("、"), candidate.lastIndexOf("。"), candidate.lastIndexOf("!"), candidate.lastIndexOf("?"), candidate.lastIndexOf("!"), candidate.lastIndexOf("?"), candidate.lastIndexOf("\n"));
531
+ let lastBreak = -1;
532
+ for (let i = this.maxLength - 1; i >= 0; i--) if (WORD_BREAK_CHARS.has(remaining[i])) {
533
+ lastBreak = i;
534
+ break;
535
+ }
516
536
  if (lastBreak > 0) splitAt = lastBreak + 1;
517
537
  }
518
- chunks.push(remaining.slice(0, splitAt).trimEnd());
519
- remaining = remaining.slice(splitAt).trimStart();
538
+ chunks.push(remaining.slice(0, splitAt).join("").trimEnd());
539
+ remaining = remaining.slice(splitAt);
540
+ let leading = 0;
541
+ while (leading < remaining.length && remaining[leading].trim() === "") leading++;
542
+ if (leading > 0) remaining = remaining.slice(leading);
520
543
  }
521
- if (remaining.length > 0) chunks.push(remaining);
544
+ if (remaining.length > 0) chunks.push(remaining.join(""));
522
545
  return chunks;
523
546
  }
524
547
  };
package/dist/index.cjs CHANGED
@@ -67,7 +67,8 @@ var OAuth2Authenticator = class {
67
67
  if (!response.ok) throw new Error(`Failed to acquire access token: ${response.status} ${response.statusText}`);
68
68
  const data = await response.json();
69
69
  this.accessToken = data.access_token;
70
- this.expiresAt = Date.now() + (data.expires_in - 60) * 1e3;
70
+ const refreshLead = Math.min(60, Math.floor(data.expires_in / 2));
71
+ this.expiresAt = Date.now() + (data.expires_in - refreshLead) * 1e3;
71
72
  }
72
73
  };
73
74
  //#endregion
@@ -116,8 +117,9 @@ function getStreamServiceClient() {
116
117
  }
117
118
  function getSendEventRequestType() {
118
119
  const msgDef = getPackageDefinition()["social.mixi.application.service.client_endpoint.v1.SendEventRequest"];
119
- if (!msgDef || !("type" in msgDef)) throw new Error("SendEventRequest message type not found in proto");
120
- return msgDef.type;
120
+ if (!msgDef || typeof msgDef.deserialize !== "function") throw new Error("SendEventRequest message type not found in proto");
121
+ const deserialize = msgDef.deserialize;
122
+ return { decode: (buffer) => deserialize(Buffer.isBuffer(buffer) ? buffer : Buffer.from(buffer)) };
121
123
  }
122
124
  //#endregion
123
125
  //#region src/convert.ts
@@ -132,18 +134,7 @@ function toDate(ts) {
132
134
  return null;
133
135
  }
134
136
  function convertUserAvatar(raw) {
135
- if (!raw) return null;
136
- const r = raw;
137
- return {
138
- largeImageUrl: r.largeImageUrl || "",
139
- largeImageMimeType: r.largeImageMimeType || "",
140
- largeImageHeight: r.largeImageHeight || 0,
141
- largeImageWidth: r.largeImageWidth || 0,
142
- smallImageUrl: r.smallImageUrl || "",
143
- smallImageMimeType: r.smallImageMimeType || "",
144
- smallImageHeight: r.smallImageHeight || 0,
145
- smallImageWidth: r.smallImageWidth || 0
146
- };
137
+ return convertMediaImage(raw) ?? null;
147
138
  }
148
139
  function convertUser(raw) {
149
140
  const r = raw;
@@ -206,11 +197,10 @@ function convertMediaStamp(raw) {
206
197
  };
207
198
  }
208
199
  function convertPostMedia(raw) {
209
- const r = raw;
200
+ const m = convertMedia(raw);
210
201
  return {
211
- mediaType: r.mediaType || 0,
212
- image: r.image ? convertMediaImage(r.image) : void 0,
213
- video: r.video ? convertMediaVideo(r.video) : void 0
202
+ ...m,
203
+ mediaType: m.mediaType
214
204
  };
215
205
  }
216
206
  function convertPostMask(raw) {
@@ -471,13 +461,14 @@ const TIMESTAMP_TOLERANCE = 300;
471
461
  var WebhookServer = class {
472
462
  server;
473
463
  port;
464
+ boundPort;
474
465
  publicKey;
475
466
  handler;
476
467
  syncHandling;
477
468
  constructor(options) {
478
- this.port = options.port || 8080;
469
+ this.port = options.port ?? 8080;
479
470
  this.handler = options.handler;
480
- this.syncHandling = options.syncHandling || false;
471
+ this.syncHandling = options.syncHandling ?? false;
481
472
  const derPrefix = Buffer.from("302a300506032b6570032100", "hex");
482
473
  const derKey = Buffer.concat([derPrefix, options.publicKey]);
483
474
  this.publicKey = crypto.default.createPublicKey({
@@ -571,8 +562,13 @@ var WebhookServer = class {
571
562
  }
572
563
  }
573
564
  start() {
574
- return new Promise((resolve) => {
565
+ return new Promise((resolve, reject) => {
566
+ const onError = (err) => reject(err);
567
+ this.server.once("error", onError);
575
568
  this.server.listen(this.port, () => {
569
+ this.server.off("error", onError);
570
+ const addr = this.server.address();
571
+ if (addr && typeof addr === "object" && "port" in addr) this.boundPort = addr.port;
576
572
  resolve();
577
573
  });
578
574
  });
@@ -580,13 +576,18 @@ var WebhookServer = class {
580
576
  shutdown() {
581
577
  return new Promise((resolve, reject) => {
582
578
  this.server.close((err) => {
579
+ this.boundPort = void 0;
583
580
  if (err) reject(err);
584
581
  else resolve();
585
582
  });
586
583
  });
587
584
  }
585
+ /**
586
+ * 起動済みの場合は OS が割り当てた実バインドポート、それ以外は構築時に指定されたポートを返す。
587
+ * `port: 0` を指定したケースでも、起動後は実際のリスニングポートが反映される。
588
+ */
588
589
  get address() {
589
- return `:${this.port}`;
590
+ return `:${this.boundPort ?? this.port}`;
590
591
  }
591
592
  get httpServer() {
592
593
  return this.server;
package/dist/index.d.cts CHANGED
@@ -1,4 +1,4 @@
1
- import { $ as PostVisibility, A as InitiatePostMediaUploadResponse, B as OfficialStampSet, C as GetCommunityMemberListRequest, D as GetStampsRequest, E as GetPostMediaStatusResponse, F as MediaType, G as PostMask, H as Post, I as MediaUploadStatus, J as PostMediaImage, K as PostMaskType, L as MediaUploadType, M as Media, N as MediaImage, O as GetStampsResponse, P as MediaStamp, Q as PostStamp, R as MediaVideo, S as GetCommunitiesUsingApplicationResponse, T as GetCommunityTimelineRequest, U as PostAccessLevel, V as PingEvent, W as PostCreatedEvent, X as PostMediaVideo, Y as PostMediaType, Z as PostPublishingType, _ as Event, a as ApplicationVersion, at as UserAccessLevel, b as GetCommunitiesRequest, c as Community, ct as Authenticator, d as CommunityPluginManagedEvent, et as RestrictCommunityPostRequest, f as CommunityStamp, g as CreatePostRequest, h as CommunityVisibility, i as ApplicationRequirement, it as User, j as LanguageCode, k as InitiatePostMediaUploadRequest, l as CommunityAccessLevel, lt as AuthenticatorOptions, m as CommunityUsingApplication, n as Client, nt as SendDirectMessageToCommunityMemberRequest, o as ChatMessage, ot as UserAvatar, p as CommunityStampSet, q as PostMedia, r as ClientOptions, rt as StampSetType, s as ChatMessageReceivedEvent, st as UserVisibility, t as EventHandler, tt as SendChatMessageRequest, u as CommunityMemberChangedEvent, ut as OAuth2Authenticator, v as EventReason, w as GetCommunityMemberListResponse, x as GetCommunitiesUsingApplicationRequest, y as EventType, z as OfficialStamp } from "./event-CgwQm1Mk.cjs";
1
+ import { $ as PostVisibility, A as InitiatePostMediaUploadResponse, B as OfficialStampSet, C as GetCommunityMemberListRequest, D as GetStampsRequest, E as GetPostMediaStatusResponse, F as MediaType, G as PostMask, H as Post, I as MediaUploadStatus, J as PostMediaImage, K as PostMaskType, L as MediaUploadType, M as Media, N as MediaImage, O as GetStampsResponse, P as MediaStamp, Q as PostStamp, R as MediaVideo, S as GetCommunitiesUsingApplicationResponse, T as GetCommunityTimelineRequest, U as PostAccessLevel, V as PingEvent, W as PostCreatedEvent, X as PostMediaVideo, Y as PostMediaType, Z as PostPublishingType, _ as Event, a as ApplicationVersion, at as UserAccessLevel, b as GetCommunitiesRequest, c as Community, ct as Authenticator, d as CommunityPluginManagedEvent, et as RestrictCommunityPostRequest, f as CommunityStamp, g as CreatePostRequest, h as CommunityVisibility, i as ApplicationRequirement, it as User, j as LanguageCode, k as InitiatePostMediaUploadRequest, l as CommunityAccessLevel, lt as AuthenticatorOptions, m as CommunityUsingApplication, n as Client, nt as SendDirectMessageToCommunityMemberRequest, o as ChatMessage, ot as UserAvatar, p as CommunityStampSet, q as PostMedia, r as ClientOptions, rt as StampSetType, s as ChatMessageReceivedEvent, st as UserVisibility, t as EventHandler, tt as SendChatMessageRequest, u as CommunityMemberChangedEvent, ut as OAuth2Authenticator, v as EventReason, w as GetCommunityMemberListResponse, x as GetCommunitiesUsingApplicationRequest, y as EventType, z as OfficialStamp } from "./event-NbLU9vQn.cjs";
2
2
  import http from "http";
3
3
 
4
4
  //#region src/webhook.d.ts
@@ -11,6 +11,7 @@ interface WebhookServerOptions {
11
11
  declare class WebhookServer {
12
12
  private readonly server;
13
13
  private readonly port;
14
+ private boundPort?;
14
15
  private readonly publicKey;
15
16
  private readonly handler;
16
17
  private readonly syncHandling;
@@ -19,6 +20,10 @@ declare class WebhookServer {
19
20
  private handleEvent;
20
21
  start(): Promise<void>;
21
22
  shutdown(): Promise<void>;
23
+ /**
24
+ * 起動済みの場合は OS が割り当てた実バインドポート、それ以外は構築時に指定されたポートを返す。
25
+ * `port: 0` を指定したケースでも、起動後は実際のリスニングポートが反映される。
26
+ */
22
27
  get address(): string;
23
28
  get httpServer(): http.Server;
24
29
  get eventHandlerFunc(): (req: http.IncomingMessage, res: http.ServerResponse) => Promise<void>;
package/dist/index.d.mts CHANGED
@@ -1,4 +1,4 @@
1
- import { $ as PostVisibility, A as InitiatePostMediaUploadResponse, B as OfficialStampSet, C as GetCommunityMemberListRequest, D as GetStampsRequest, E as GetPostMediaStatusResponse, F as MediaType, G as PostMask, H as Post, I as MediaUploadStatus, J as PostMediaImage, K as PostMaskType, L as MediaUploadType, M as Media, N as MediaImage, O as GetStampsResponse, P as MediaStamp, Q as PostStamp, R as MediaVideo, S as GetCommunitiesUsingApplicationResponse, T as GetCommunityTimelineRequest, U as PostAccessLevel, V as PingEvent, W as PostCreatedEvent, X as PostMediaVideo, Y as PostMediaType, Z as PostPublishingType, _ as Event, a as ApplicationVersion, at as UserAccessLevel, b as GetCommunitiesRequest, c as Community, ct as Authenticator, d as CommunityPluginManagedEvent, et as RestrictCommunityPostRequest, f as CommunityStamp, g as CreatePostRequest, h as CommunityVisibility, i as ApplicationRequirement, it as User, j as LanguageCode, k as InitiatePostMediaUploadRequest, l as CommunityAccessLevel, lt as AuthenticatorOptions, m as CommunityUsingApplication, n as Client, nt as SendDirectMessageToCommunityMemberRequest, o as ChatMessage, ot as UserAvatar, p as CommunityStampSet, q as PostMedia, r as ClientOptions, rt as StampSetType, s as ChatMessageReceivedEvent, st as UserVisibility, t as EventHandler, tt as SendChatMessageRequest, u as CommunityMemberChangedEvent, ut as OAuth2Authenticator, v as EventReason, w as GetCommunityMemberListResponse, x as GetCommunitiesUsingApplicationRequest, y as EventType, z as OfficialStamp } from "./event-LyIxrXkO.mjs";
1
+ import { $ as PostVisibility, A as InitiatePostMediaUploadResponse, B as OfficialStampSet, C as GetCommunityMemberListRequest, D as GetStampsRequest, E as GetPostMediaStatusResponse, F as MediaType, G as PostMask, H as Post, I as MediaUploadStatus, J as PostMediaImage, K as PostMaskType, L as MediaUploadType, M as Media, N as MediaImage, O as GetStampsResponse, P as MediaStamp, Q as PostStamp, R as MediaVideo, S as GetCommunitiesUsingApplicationResponse, T as GetCommunityTimelineRequest, U as PostAccessLevel, V as PingEvent, W as PostCreatedEvent, X as PostMediaVideo, Y as PostMediaType, Z as PostPublishingType, _ as Event, a as ApplicationVersion, at as UserAccessLevel, b as GetCommunitiesRequest, c as Community, ct as Authenticator, d as CommunityPluginManagedEvent, et as RestrictCommunityPostRequest, f as CommunityStamp, g as CreatePostRequest, h as CommunityVisibility, i as ApplicationRequirement, it as User, j as LanguageCode, k as InitiatePostMediaUploadRequest, l as CommunityAccessLevel, lt as AuthenticatorOptions, m as CommunityUsingApplication, n as Client, nt as SendDirectMessageToCommunityMemberRequest, o as ChatMessage, ot as UserAvatar, p as CommunityStampSet, q as PostMedia, r as ClientOptions, rt as StampSetType, s as ChatMessageReceivedEvent, st as UserVisibility, t as EventHandler, tt as SendChatMessageRequest, u as CommunityMemberChangedEvent, ut as OAuth2Authenticator, v as EventReason, w as GetCommunityMemberListResponse, x as GetCommunitiesUsingApplicationRequest, y as EventType, z as OfficialStamp } from "./event-DDaasL1T.mjs";
2
2
  import http from "http";
3
3
 
4
4
  //#region src/webhook.d.ts
@@ -11,6 +11,7 @@ interface WebhookServerOptions {
11
11
  declare class WebhookServer {
12
12
  private readonly server;
13
13
  private readonly port;
14
+ private boundPort?;
14
15
  private readonly publicKey;
15
16
  private readonly handler;
16
17
  private readonly syncHandling;
@@ -19,6 +20,10 @@ declare class WebhookServer {
19
20
  private handleEvent;
20
21
  start(): Promise<void>;
21
22
  shutdown(): Promise<void>;
23
+ /**
24
+ * 起動済みの場合は OS が割り当てた実バインドポート、それ以外は構築時に指定されたポートを返す。
25
+ * `port: 0` を指定したケースでも、起動後は実際のリスニングポートが反映される。
26
+ */
22
27
  get address(): string;
23
28
  get httpServer(): http.Server;
24
29
  get eventHandlerFunc(): (req: http.IncomingMessage, res: http.ServerResponse) => Promise<void>;
package/dist/index.mjs CHANGED
@@ -39,7 +39,8 @@ var OAuth2Authenticator = class {
39
39
  if (!response.ok) throw new Error(`Failed to acquire access token: ${response.status} ${response.statusText}`);
40
40
  const data = await response.json();
41
41
  this.accessToken = data.access_token;
42
- this.expiresAt = Date.now() + (data.expires_in - 60) * 1e3;
42
+ const refreshLead = Math.min(60, Math.floor(data.expires_in / 2));
43
+ this.expiresAt = Date.now() + (data.expires_in - refreshLead) * 1e3;
43
44
  }
44
45
  };
45
46
  //#endregion
@@ -88,8 +89,9 @@ function getStreamServiceClient() {
88
89
  }
89
90
  function getSendEventRequestType() {
90
91
  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;
92
+ if (!msgDef || typeof msgDef.deserialize !== "function") throw new Error("SendEventRequest message type not found in proto");
93
+ const deserialize = msgDef.deserialize;
94
+ return { decode: (buffer) => deserialize(Buffer.isBuffer(buffer) ? buffer : Buffer.from(buffer)) };
93
95
  }
94
96
  //#endregion
95
97
  //#region src/convert.ts
@@ -104,18 +106,7 @@ function toDate(ts) {
104
106
  return null;
105
107
  }
106
108
  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
- };
109
+ return convertMediaImage(raw) ?? null;
119
110
  }
120
111
  function convertUser(raw) {
121
112
  const r = raw;
@@ -178,11 +169,10 @@ function convertMediaStamp(raw) {
178
169
  };
179
170
  }
180
171
  function convertPostMedia(raw) {
181
- const r = raw;
172
+ const m = convertMedia(raw);
182
173
  return {
183
- mediaType: r.mediaType || 0,
184
- image: r.image ? convertMediaImage(r.image) : void 0,
185
- video: r.video ? convertMediaVideo(r.video) : void 0
174
+ ...m,
175
+ mediaType: m.mediaType
186
176
  };
187
177
  }
188
178
  function convertPostMask(raw) {
@@ -443,13 +433,14 @@ const TIMESTAMP_TOLERANCE = 300;
443
433
  var WebhookServer = class {
444
434
  server;
445
435
  port;
436
+ boundPort;
446
437
  publicKey;
447
438
  handler;
448
439
  syncHandling;
449
440
  constructor(options) {
450
- this.port = options.port || 8080;
441
+ this.port = options.port ?? 8080;
451
442
  this.handler = options.handler;
452
- this.syncHandling = options.syncHandling || false;
443
+ this.syncHandling = options.syncHandling ?? false;
453
444
  const derPrefix = Buffer.from("302a300506032b6570032100", "hex");
454
445
  const derKey = Buffer.concat([derPrefix, options.publicKey]);
455
446
  this.publicKey = crypto.createPublicKey({
@@ -543,8 +534,13 @@ var WebhookServer = class {
543
534
  }
544
535
  }
545
536
  start() {
546
- return new Promise((resolve) => {
537
+ return new Promise((resolve, reject) => {
538
+ const onError = (err) => reject(err);
539
+ this.server.once("error", onError);
547
540
  this.server.listen(this.port, () => {
541
+ this.server.off("error", onError);
542
+ const addr = this.server.address();
543
+ if (addr && typeof addr === "object" && "port" in addr) this.boundPort = addr.port;
548
544
  resolve();
549
545
  });
550
546
  });
@@ -552,13 +548,18 @@ var WebhookServer = class {
552
548
  shutdown() {
553
549
  return new Promise((resolve, reject) => {
554
550
  this.server.close((err) => {
551
+ this.boundPort = void 0;
555
552
  if (err) reject(err);
556
553
  else resolve();
557
554
  });
558
555
  });
559
556
  }
557
+ /**
558
+ * 起動済みの場合は OS が割り当てた実バインドポート、それ以外は構築時に指定されたポートを返す。
559
+ * `port: 0` を指定したケースでも、起動後は実際のリスニングポートが反映される。
560
+ */
560
561
  get address() {
561
- return `:${this.port}`;
562
+ return `:${this.boundPort ?? this.port}`;
562
563
  }
563
564
  get httpServer() {
564
565
  return this.server;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "mixi2-js",
3
- "version": "1.5.1",
3
+ "version": "1.5.3",
4
4
  "description": "TypeScript/JavaScript SDK for mixi2 Application API",
5
5
  "keywords": [
6
6
  "api",
@@ -72,9 +72,9 @@
72
72
  },
73
73
  "devDependencies": {
74
74
  "@eslint/js": "latest",
75
- "@types/node": "^25.7.0",
75
+ "@types/node": "^25.8.0",
76
76
  "consola": "^3.4.2",
77
- "dotenv": "^17.3.1",
77
+ "dotenv": "^17.4.2",
78
78
  "eslint": "latest",
79
79
  "eslint-config-prettier": "latest",
80
80
  "npm-check-updates": "^22.2.0",
@@ -185,114 +185,3 @@ service ApplicationService {
185
185
  rpc AddStampToPost(AddStampToPostRequest) returns (AddStampToPostResponse);
186
186
  rpc SendDirectMessageToCommunityMember(SendDirectMessageToCommunityMemberRequest) returns (SendDirectMessageToCommunityMemberResponse);
187
187
  }
188
-
189
-
190
- message GetUsersRequest {
191
- repeated string user_id_list = 1;
192
- }
193
-
194
- message GetUsersResponse {
195
- repeated model.v1.User users = 1;
196
- }
197
-
198
- message GetPostsRequest {
199
- repeated string post_id_list = 1;
200
- }
201
-
202
- message GetPostsResponse {
203
- repeated model.v1.Post posts = 1;
204
- }
205
-
206
- message InitiatePostMediaUploadRequest {
207
- string content_type = 1;
208
- uint64 data_size = 2;
209
- Type media_type = 3;
210
- optional string description = 4;
211
-
212
- enum Type {
213
- TYPE_UNSPECIFIED = 0;
214
- TYPE_IMAGE = 1;
215
- TYPE_VIDEO = 2;
216
- }
217
- }
218
-
219
- message InitiatePostMediaUploadResponse {
220
- string media_id = 1;
221
- string upload_url = 2;
222
- }
223
-
224
- message GetPostMediaStatusRequest {
225
- string media_id = 1;
226
- }
227
-
228
- message GetPostMediaStatusResponse {
229
- Status status = 1;
230
-
231
- enum Status {
232
- STATUS_UNSPECIFIED = 0;
233
- STATUS_UPLOAD_PENDING = 1;
234
- STATUS_PROCESSING = 2;
235
- STATUS_COMPLETED = 3;
236
- STATUS_FAILED = 4;
237
- }
238
- }
239
-
240
- message CreatePostRequest {
241
- string text = 1;
242
- optional string in_reply_to_post_id = 2;
243
- optional string quoted_post_id = 3;
244
- repeated string media_id_list = 5;
245
- optional model.v1.PostMask post_mask = 6;
246
- optional const.v1.PostPublishingType publishing_type = 7;
247
- }
248
-
249
- message CreatePostResponse {
250
- model.v1.Post post = 1;
251
- }
252
-
253
- message DeletePostRequest {
254
- string post_id = 1;
255
- }
256
-
257
- message DeletePostResponse {
258
- bool deleted = 1;
259
- }
260
-
261
- message SendChatMessageRequest {
262
- string room_id = 1;
263
- optional string text = 2;
264
- optional string media_id = 3;
265
- }
266
-
267
- message SendChatMessageResponse {
268
- model.v1.ChatMessage message = 1;
269
- }
270
-
271
- message GetStampsRequest {
272
- optional const.v1.LanguageCode official_stamp_language = 1;
273
- }
274
-
275
- message GetStampsResponse {
276
- repeated model.v1.OfficialStampSet official_stamp_sets = 1;
277
- }
278
-
279
- message AddStampToPostRequest {
280
- string post_id = 1;
281
- string stamp_id = 2;
282
- }
283
-
284
- message AddStampToPostResponse {
285
- model.v1.Post post = 1;
286
- }
287
-
288
- service ApplicationService {
289
- rpc GetUsers(GetUsersRequest) returns (GetUsersResponse);
290
- rpc GetPosts(GetPostsRequest) returns (GetPostsResponse);
291
- rpc CreatePost(CreatePostRequest) returns (CreatePostResponse);
292
- rpc DeletePost(DeletePostRequest) returns (DeletePostResponse);
293
- rpc InitiatePostMediaUpload(InitiatePostMediaUploadRequest) returns (InitiatePostMediaUploadResponse);
294
- rpc GetPostMediaStatus(GetPostMediaStatusRequest) returns (GetPostMediaStatusResponse);
295
- rpc SendChatMessage(SendChatMessageRequest) returns (SendChatMessageResponse);
296
- rpc GetStamps(GetStampsRequest) returns (GetStampsResponse);
297
- rpc AddStampToPost(AddStampToPostRequest) returns (AddStampToPostResponse);
298
- }