@photon-ai/advanced-imessage-kit 1.2.0 → 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.ts CHANGED
@@ -148,6 +148,7 @@ type MessageResponse = {
148
148
  partCount?: number | null;
149
149
  payloadData?: NodeJS.Dict<any>[];
150
150
  hasPayloadData?: boolean;
151
+ isPoll?: boolean;
151
152
  wasDeliveredQuietly?: boolean;
152
153
  didNotifyRecipient?: boolean;
153
154
  shareStatus?: number | null;
@@ -326,11 +327,59 @@ interface TypedEventEmitter {
326
327
  removeListener(event: string | symbol, listener: (...args: unknown[]) => void): this;
327
328
  }
328
329
 
330
+ interface CreatePollOptions {
331
+ chatGuid: string;
332
+ title?: string;
333
+ options: string[];
334
+ }
335
+ interface VotePollOptions {
336
+ chatGuid: string;
337
+ pollMessageGuid: string;
338
+ optionIdentifier: string;
339
+ }
340
+ interface AddPollOptionOptions {
341
+ chatGuid: string;
342
+ pollMessageGuid: string;
343
+ optionText: string;
344
+ }
345
+ interface PollOption {
346
+ optionIdentifier: string;
347
+ text: string;
348
+ attributedText: string;
349
+ creatorHandle: string;
350
+ canBeEdited: boolean;
351
+ }
352
+ interface PollVote {
353
+ voteOptionIdentifier: string;
354
+ participantHandle: string;
355
+ serverVoteTime?: number;
356
+ }
357
+ interface PollDefinition {
358
+ version: number;
359
+ item: {
360
+ title: string;
361
+ orderedPollOptions: PollOption[];
362
+ creatorHandle: string;
363
+ };
364
+ }
365
+ interface PollVoteResponse {
366
+ version: number;
367
+ item: {
368
+ votes: PollVote[];
369
+ };
370
+ }
371
+ type PollMessageResponse = MessageResponse;
372
+
329
373
  interface SendStickerOptions {
330
374
  chatGuid: string;
331
375
  filePath: string;
332
376
  fileName?: string;
333
377
  selectedMessageGuid?: string;
378
+ stickerX?: number;
379
+ stickerY?: number;
380
+ stickerScale?: number;
381
+ stickerRotation?: number;
382
+ stickerWidth?: number;
334
383
  }
335
384
 
336
385
  type ValidTapback = "love" | "like" | "dislike" | "laugh" | "emphasize" | "question";
@@ -387,7 +436,6 @@ declare class ChatModule {
387
436
  }): Promise<ChatResponse>;
388
437
  deleteChat(guid: string): Promise<void>;
389
438
  markChatRead(guid: string): Promise<void>;
390
- markChatUnread(guid: string): Promise<void>;
391
439
  leaveChat(guid: string): Promise<void>;
392
440
  addParticipant(chatGuid: string, address: string): Promise<ChatResponse>;
393
441
  removeParticipant(chatGuid: string, address: string): Promise<ChatResponse>;
@@ -528,6 +576,15 @@ declare class MessageModule {
528
576
  }): Promise<MessageResponse[]>;
529
577
  }
530
578
 
579
+ declare class PollModule {
580
+ private readonly http;
581
+ constructor(http: AxiosInstance);
582
+ create(options: CreatePollOptions): Promise<PollMessageResponse>;
583
+ vote(options: VotePollOptions): Promise<PollMessageResponse>;
584
+ unvote(options: VotePollOptions): Promise<PollMessageResponse>;
585
+ addOption(options: AddPollOptionOptions): Promise<PollMessageResponse>;
586
+ }
587
+
531
588
  declare class ScheduledMessageModule {
532
589
  private readonly http;
533
590
  constructor(http: AxiosInstance);
@@ -543,8 +600,6 @@ declare class ServerModule {
543
600
  getServerInfo(): Promise<any>;
544
601
  getMessageStats(): Promise<any>;
545
602
  getServerLogs(count?: number): Promise<string[]>;
546
- getAlerts(): Promise<any[]>;
547
- markAlertAsRead(ids: string[]): Promise<any>;
548
603
  getMediaStatistics(options?: {
549
604
  only?: string[];
550
605
  }): Promise<any>;
@@ -568,10 +623,12 @@ declare class AdvancedIMessageKit extends EventEmitter$1 implements TypedEventEm
568
623
  readonly handles: HandleModule;
569
624
  readonly facetime: FaceTimeModule;
570
625
  readonly icloud: ICloudModule;
626
+ readonly polls: PollModule;
571
627
  readonly scheduledMessages: ScheduledMessageModule;
572
628
  readonly server: ServerModule;
573
629
  private processedMessages;
574
630
  private sendQueue;
631
+ private readyEmitted;
575
632
  private constructor();
576
633
  emit<K extends keyof PhotonEventMap>(event: K, ...args: PhotonEventMap[K] extends undefined ? [] : [PhotonEventMap[K]]): boolean;
577
634
  on<K extends keyof PhotonEventMap>(event: K, listener: PhotonEventMap[K] extends undefined ? () => void : (data: PhotonEventMap[K]) => void): this;
@@ -636,4 +693,19 @@ declare const NEW_FINDMY_LOCATION = "new-findmy-location";
636
693
  declare const setGlobalLogLevel: (level: LogLevel) => void;
637
694
  declare const getLogger: (tag: string) => Logger;
638
695
 
639
- export { AdvancedIMessageKit, type Attachment, type AttachmentResponse, type BackupData, CHAT_READ_STATUS_CHANGED, type Chat, type ChatResponse, type ClientConfig, FT_CALL_STATUS_CHANGED, type FaceTimeStatusData, type FindMyLocationItem, GROUP_ICON_CHANGED, GROUP_ICON_REMOVED, GROUP_NAME_CHANGE, HELLO_WORLD, type Handle, type HandleResponse, IMESSAGE_ALIASES_REMOVED, INCOMING_FACETIME, MESSAGE_SEND_ERROR, MESSAGE_UPDATED, type Message, type MessageData, type MessageResponse, NEW_FINDMY_LOCATION, NEW_MESSAGE, NEW_SERVER, PARTICIPANT_ADDED, PARTICIPANT_LEFT, PARTICIPANT_REMOVED, type PhotonEventMap, type PhotonEventName, SCHEDULED_MESSAGE_CREATED, SCHEDULED_MESSAGE_DELETED, SCHEDULED_MESSAGE_ERROR, SCHEDULED_MESSAGE_SENT, SCHEDULED_MESSAGE_UPDATED, SDK, SERVER_UPDATE, SERVER_UPDATE_DOWNLOADING, SERVER_UPDATE_INSTALLING, SETTINGS_BACKUP_CREATED, SETTINGS_BACKUP_DELETED, SETTINGS_BACKUP_UPDATED, type ScheduledMessageData, type SendAttachmentOptions, type SendMessageOptions, type SendStickerOptions, type ServerMetadataResponse, type ServerUpdateData, THEME_BACKUP_CREATED, THEME_BACKUP_DELETED, THEME_BACKUP_UPDATED, TYPING_INDICATOR, type TypedEventEmitter, type ValidRemoveTapback, type ValidTapback, getLogger, setGlobalLogLevel };
696
+ declare function isPollMessage(message: MessageResponse): boolean;
697
+ declare function isPollVote(message: MessageResponse): boolean;
698
+ interface ParsedPoll {
699
+ title: string;
700
+ creatorHandle: string;
701
+ options: PollOption[];
702
+ }
703
+ interface ParsedPollVote {
704
+ votes: PollVote[];
705
+ }
706
+ declare function parsePollDefinition(message: MessageResponse): ParsedPoll | null;
707
+ declare function parsePollVotes(message: MessageResponse): ParsedPollVote | null;
708
+ declare function getPollSummary(message: MessageResponse): string;
709
+ declare function getPollOneLiner(message: MessageResponse): string;
710
+
711
+ export { type AddPollOptionOptions, AdvancedIMessageKit, type Attachment, type AttachmentResponse, type BackupData, CHAT_READ_STATUS_CHANGED, type Chat, type ChatResponse, type ClientConfig, type CreatePollOptions, FT_CALL_STATUS_CHANGED, type FaceTimeStatusData, type FindMyLocationItem, GROUP_ICON_CHANGED, GROUP_ICON_REMOVED, GROUP_NAME_CHANGE, HELLO_WORLD, type Handle, type HandleResponse, IMESSAGE_ALIASES_REMOVED, INCOMING_FACETIME, MESSAGE_SEND_ERROR, MESSAGE_UPDATED, type Message, type MessageData, type MessageResponse, NEW_FINDMY_LOCATION, NEW_MESSAGE, NEW_SERVER, PARTICIPANT_ADDED, PARTICIPANT_LEFT, PARTICIPANT_REMOVED, type ParsedPoll, type ParsedPollVote, type PhotonEventMap, type PhotonEventName, type PollDefinition, type PollMessageResponse, type PollOption, type PollVote, type PollVoteResponse, SCHEDULED_MESSAGE_CREATED, SCHEDULED_MESSAGE_DELETED, SCHEDULED_MESSAGE_ERROR, SCHEDULED_MESSAGE_SENT, SCHEDULED_MESSAGE_UPDATED, SDK, SERVER_UPDATE, SERVER_UPDATE_DOWNLOADING, SERVER_UPDATE_INSTALLING, SETTINGS_BACKUP_CREATED, SETTINGS_BACKUP_DELETED, SETTINGS_BACKUP_UPDATED, type ScheduledMessageData, type SendAttachmentOptions, type SendMessageOptions, type SendStickerOptions, type ServerMetadataResponse, type ServerUpdateData, THEME_BACKUP_CREATED, THEME_BACKUP_DELETED, THEME_BACKUP_UPDATED, TYPING_INDICATOR, type TypedEventEmitter, type ValidRemoveTapback, type ValidTapback, type VotePollOptions, getLogger, getPollOneLiner, getPollSummary, isPollMessage, isPollVote, parsePollDefinition, parsePollVotes, setGlobalLogLevel };
package/dist/index.js CHANGED
@@ -173,15 +173,23 @@ var AttachmentModule = class {
173
173
  const fileName = options.fileName || path__default.basename(options.filePath);
174
174
  const form = new FormData();
175
175
  form.append("attachment", await readFile(options.filePath), fileName);
176
- const { data } = await this.http.post("/api/v1/attachment/upload", form, {
176
+ form.append("name", fileName);
177
+ form.append("chatGuid", options.chatGuid);
178
+ form.append("isSticker", "true");
179
+ form.append("method", "private-api");
180
+ if (options.selectedMessageGuid) {
181
+ form.append("selectedMessageGuid", options.selectedMessageGuid);
182
+ form.append("partIndex", "0");
183
+ }
184
+ form.append("stickerX", String(options.stickerX ?? 0.5));
185
+ form.append("stickerY", String(options.stickerY ?? 0.5));
186
+ form.append("stickerScale", String(options.stickerScale ?? 0.75));
187
+ form.append("stickerRotation", String(options.stickerRotation ?? 0));
188
+ form.append("stickerWidth", String(options.stickerWidth ?? 300));
189
+ const { data } = await this.http.post("/api/v1/message/attachment", form, {
177
190
  headers: form.getHeaders()
178
191
  });
179
- const response = await this.http.post("/api/v1/message/multipart", {
180
- chatGuid: options.chatGuid,
181
- selectedMessageGuid: options.selectedMessageGuid,
182
- parts: [{ partIndex: 0, attachment: data.data.path, name: fileName }]
183
- });
184
- return response.data.data;
192
+ return data.data;
185
193
  });
186
194
  }
187
195
  };
@@ -198,35 +206,32 @@ var ChatModule = class {
198
206
  return response.data.data;
199
207
  }
200
208
  async getChat(guid, options) {
201
- const response = await this.http.get(`/api/v1/chat/${guid}`, {
209
+ const response = await this.http.get(`/api/v1/chat/${encodeURIComponent(guid)}`, {
202
210
  params: options?.with ? { with: options.with.join(",") } : {}
203
211
  });
204
212
  return response.data.data;
205
213
  }
206
214
  async updateChat(guid, options) {
207
- const response = await this.http.put(`/api/v1/chat/${guid}`, options);
215
+ const response = await this.http.put(`/api/v1/chat/${encodeURIComponent(guid)}`, options);
208
216
  return response.data.data;
209
217
  }
210
218
  async deleteChat(guid) {
211
- await this.http.delete(`/api/v1/chat/${guid}`);
219
+ await this.http.delete(`/api/v1/chat/${encodeURIComponent(guid)}`);
212
220
  }
213
221
  async markChatRead(guid) {
214
- await this.http.post(`/api/v1/chat/${guid}/read`);
215
- }
216
- async markChatUnread(guid) {
217
- await this.http.post(`/api/v1/chat/${guid}/unread`);
222
+ await this.http.post(`/api/v1/chat/${encodeURIComponent(guid)}/read`);
218
223
  }
219
224
  async leaveChat(guid) {
220
- await this.http.post(`/api/v1/chat/${guid}/leave`);
225
+ await this.http.post(`/api/v1/chat/${encodeURIComponent(guid)}/leave`);
221
226
  }
222
227
  async addParticipant(chatGuid, address) {
223
- const response = await this.http.post(`/api/v1/chat/${chatGuid}/participant`, {
228
+ const response = await this.http.post(`/api/v1/chat/${encodeURIComponent(chatGuid)}/participant`, {
224
229
  address
225
230
  });
226
231
  return response.data.data;
227
232
  }
228
233
  async removeParticipant(chatGuid, address) {
229
- const response = await this.http.delete(`/api/v1/chat/${chatGuid}/participant`, {
234
+ const response = await this.http.delete(`/api/v1/chat/${encodeURIComponent(chatGuid)}/participant`, {
230
235
  data: { address }
231
236
  });
232
237
  return response.data.data;
@@ -239,7 +244,7 @@ var ChatModule = class {
239
244
  if (options?.before !== void 0) params.before = options.before;
240
245
  if (options?.after !== void 0) params.after = options.after;
241
246
  if (options?.with) params.with = options.with.join(",");
242
- const response = await this.http.get(`/api/v1/chat/${chatGuid}/message`, {
247
+ const response = await this.http.get(`/api/v1/chat/${encodeURIComponent(chatGuid)}/message`, {
243
248
  params
244
249
  });
245
250
  return response.data.data;
@@ -249,15 +254,15 @@ var ChatModule = class {
249
254
  const fileName = path__default.basename(filePath);
250
255
  const form = new FormData();
251
256
  form.append("icon", fileBuffer, fileName);
252
- await this.http.post(`/api/v1/chat/${chatGuid}/icon`, form, {
257
+ await this.http.post(`/api/v1/chat/${encodeURIComponent(chatGuid)}/icon`, form, {
253
258
  headers: form.getHeaders()
254
259
  });
255
260
  }
256
261
  async removeGroupIcon(chatGuid) {
257
- await this.http.delete(`/api/v1/chat/${chatGuid}/icon`);
262
+ await this.http.delete(`/api/v1/chat/${encodeURIComponent(chatGuid)}/icon`);
258
263
  }
259
264
  async getGroupIcon(chatGuid) {
260
- const response = await this.http.get(`/api/v1/chat/${chatGuid}/icon`, {
265
+ const response = await this.http.get(`/api/v1/chat/${encodeURIComponent(chatGuid)}/icon`, {
261
266
  responseType: "arraybuffer"
262
267
  });
263
268
  return Buffer.from(response.data);
@@ -269,10 +274,10 @@ var ChatModule = class {
269
274
  return response.data.data;
270
275
  }
271
276
  async startTyping(chatGuid) {
272
- await this.http.post(`/api/v1/chat/${chatGuid}/typing`);
277
+ await this.http.post(`/api/v1/chat/${encodeURIComponent(chatGuid)}/typing`);
273
278
  }
274
279
  async stopTyping(chatGuid) {
275
- await this.http.delete(`/api/v1/chat/${chatGuid}/typing`);
280
+ await this.http.delete(`/api/v1/chat/${encodeURIComponent(chatGuid)}/typing`);
276
281
  }
277
282
  };
278
283
 
@@ -292,10 +297,10 @@ var ContactModule = class {
292
297
  return response.data.data;
293
298
  }
294
299
  async shareContactCard(chatGuid) {
295
- await this.http.post(`/api/v1/chat/${chatGuid}/share/contact`);
300
+ await this.http.post(`/api/v1/chat/${encodeURIComponent(chatGuid)}/share/contact`);
296
301
  }
297
302
  async shouldShareContact(chatGuid) {
298
- const response = await this.http.get(`/api/v1/chat/${chatGuid}/share/contact/status`);
303
+ const response = await this.http.get(`/api/v1/chat/${encodeURIComponent(chatGuid)}/share/contact/status`);
299
304
  return response.data.data;
300
305
  }
301
306
  };
@@ -361,6 +366,40 @@ var ICloudModule = class {
361
366
  await this.http.post("/api/v1/icloud/findmy/friends/refresh");
362
367
  }
363
368
  };
369
+
370
+ // lib/auto-create-chat.ts
371
+ function isChatNotExistError(error) {
372
+ const axiosError = error;
373
+ const errorMsg = axiosError?.response?.data?.error?.message || axiosError?.response?.data?.message || "";
374
+ const lowerMsg = errorMsg.toLowerCase();
375
+ return lowerMsg.includes("chat does not exist") || lowerMsg.includes("chat not found");
376
+ }
377
+ function extractAddress(chatGuid) {
378
+ const parts = chatGuid.split(";-;");
379
+ if (parts.length !== 2 || !parts[1]) {
380
+ return void 0;
381
+ }
382
+ return parts[1];
383
+ }
384
+ async function createChatWithMessage(options) {
385
+ const { http, address, message, tempGuid, subject, effectId } = options;
386
+ try {
387
+ const response = await http.post("/api/v1/chat/new", {
388
+ addresses: [address],
389
+ message,
390
+ tempGuid,
391
+ subject,
392
+ effectId
393
+ });
394
+ return response.data.data?.guid;
395
+ } catch (error) {
396
+ throw new Error(
397
+ `Failed to create chat with address "${address}": ${error instanceof Error ? error.message : String(error)}`
398
+ );
399
+ }
400
+ }
401
+
402
+ // modules/message.ts
364
403
  var MessageModule = class {
365
404
  constructor(http, enqueueSend = (task) => task()) {
366
405
  this.http = http;
@@ -368,12 +407,25 @@ var MessageModule = class {
368
407
  }
369
408
  async sendMessage(options) {
370
409
  return this.enqueueSend(async () => {
371
- const payload = {
372
- ...options,
373
- tempGuid: options.tempGuid || randomUUID()
374
- };
375
- const response = await this.http.post("/api/v1/message/text", payload);
376
- return response.data.data;
410
+ const tempGuid = options.tempGuid || randomUUID();
411
+ const payload = { ...options, tempGuid };
412
+ try {
413
+ const response = await this.http.post("/api/v1/message/text", payload);
414
+ return response.data.data;
415
+ } catch (error) {
416
+ if (!isChatNotExistError(error)) throw error;
417
+ const address = extractAddress(options.chatGuid);
418
+ if (!address) throw error;
419
+ await createChatWithMessage({
420
+ http: this.http,
421
+ address,
422
+ message: options.message,
423
+ tempGuid,
424
+ subject: options.subject,
425
+ effectId: options.effectId
426
+ });
427
+ return { guid: tempGuid, text: options.message, dateCreated: Date.now() };
428
+ }
377
429
  });
378
430
  }
379
431
  async getMessage(guid, options) {
@@ -462,6 +514,51 @@ var MessageModule = class {
462
514
  }
463
515
  };
464
516
 
517
+ // modules/poll.ts
518
+ var PollModule = class {
519
+ constructor(http) {
520
+ this.http = http;
521
+ }
522
+ async create(options) {
523
+ if (options.options.length < 2) {
524
+ throw new Error("Poll must have at least 2 options");
525
+ }
526
+ const { data } = await this.http.post("/api/v1/poll/create", {
527
+ chatGuid: options.chatGuid,
528
+ title: options.title ?? "",
529
+ options: options.options
530
+ });
531
+ return data.data;
532
+ }
533
+ async vote(options) {
534
+ const { data } = await this.http.post("/api/v1/poll/vote", {
535
+ chatGuid: options.chatGuid,
536
+ pollMessageGuid: options.pollMessageGuid,
537
+ optionIdentifier: options.optionIdentifier
538
+ });
539
+ return data.data;
540
+ }
541
+ async unvote(options) {
542
+ const { data } = await this.http.post("/api/v1/poll/unvote", {
543
+ chatGuid: options.chatGuid,
544
+ pollMessageGuid: options.pollMessageGuid,
545
+ optionIdentifier: options.optionIdentifier
546
+ });
547
+ return data.data;
548
+ }
549
+ async addOption(options) {
550
+ if (!options.optionText || options.optionText.trim().length === 0) {
551
+ throw new Error("Option text cannot be empty");
552
+ }
553
+ const { data } = await this.http.post("/api/v1/poll/option", {
554
+ chatGuid: options.chatGuid,
555
+ pollMessageGuid: options.pollMessageGuid,
556
+ optionText: options.optionText
557
+ });
558
+ return data.data;
559
+ }
560
+ };
561
+
465
562
  // modules/scheduled.ts
466
563
  var ScheduledMessageModule = class {
467
564
  constructor(http) {
@@ -503,14 +600,6 @@ var ServerModule = class {
503
600
  });
504
601
  return response.data.data;
505
602
  }
506
- async getAlerts() {
507
- const response = await this.http.get("/api/v1/server/alert");
508
- return response.data.data;
509
- }
510
- async markAlertAsRead(ids) {
511
- const response = await this.http.post("/api/v1/server/alert/read", { ids });
512
- return response.data.data;
513
- }
514
603
  async getMediaStatistics(options) {
515
604
  const params = {};
516
605
  if (options?.only) params.only = options.only.join(",");
@@ -545,6 +634,7 @@ var _AdvancedIMessageKit = class _AdvancedIMessageKit extends EventEmitter {
545
634
  __publicField(this, "handles");
546
635
  __publicField(this, "facetime");
547
636
  __publicField(this, "icloud");
637
+ __publicField(this, "polls");
548
638
  __publicField(this, "scheduledMessages");
549
639
  __publicField(this, "server");
550
640
  // Message deduplication feature
@@ -558,6 +648,11 @@ var _AdvancedIMessageKit = class _AdvancedIMessageKit extends EventEmitter {
558
648
  // Purpose: Ensure all outgoing messages (text, attachments, stickers, etc.) from
559
649
  // a single user/SDK instance are sent in strict order, preventing race conditions.
560
650
  __publicField(this, "sendQueue", Promise.resolve());
651
+ // Flag to track if 'ready' event has been emitted
652
+ //
653
+ // Purpose: Prevent duplicate 'ready' events when both legacy mode (no API key)
654
+ // and auth-ok events occur, which would cause user callbacks to fire twice.
655
+ __publicField(this, "readyEmitted", false);
561
656
  this.config = {
562
657
  serverUrl: "http://localhost:1234",
563
658
  logLevel: "info",
@@ -600,6 +695,7 @@ var _AdvancedIMessageKit = class _AdvancedIMessageKit extends EventEmitter {
600
695
  this.handles = new HandleModule(this.http);
601
696
  this.facetime = new FaceTimeModule(this.http);
602
697
  this.icloud = new ICloudModule(this.http);
698
+ this.polls = new PollModule(this.http);
603
699
  this.scheduledMessages = new ScheduledMessageModule(this.http);
604
700
  this.server = new ServerModule(this.http);
605
701
  }
@@ -668,11 +764,15 @@ var _AdvancedIMessageKit = class _AdvancedIMessageKit extends EventEmitter {
668
764
  }
669
765
  this.socket.on("disconnect", () => {
670
766
  this.logger.info("Disconnected from iMessage server");
767
+ this.readyEmitted = false;
671
768
  this.emit("disconnect");
672
769
  });
673
770
  this.socket.on("auth-ok", () => {
674
771
  this.logger.info("Authentication successful");
675
- this.emit("ready");
772
+ if (!this.readyEmitted) {
773
+ this.readyEmitted = true;
774
+ this.emit("ready");
775
+ }
676
776
  });
677
777
  this.socket.on("auth-error", (error) => {
678
778
  this.logger.error(`Authentication failed: ${error.message} ${error.reason ? `(${error.reason})` : ""}`);
@@ -684,6 +784,13 @@ var _AdvancedIMessageKit = class _AdvancedIMessageKit extends EventEmitter {
684
784
  }
685
785
  this.socket.once("connect", () => {
686
786
  this.logger.info("Connected to iMessage server, waiting for authentication...");
787
+ if (!this.config.apiKey) {
788
+ this.logger.info("No API key provided, skipping authentication (legacy server mode)");
789
+ if (!this.readyEmitted) {
790
+ this.readyEmitted = true;
791
+ this.emit("ready");
792
+ }
793
+ }
687
794
  });
688
795
  if (!this.socket.connected) {
689
796
  this.socket.connect();
@@ -766,6 +873,144 @@ var IMESSAGE_ALIASES_REMOVED = "imessage-aliases-removed";
766
873
  var FT_CALL_STATUS_CHANGED = "ft-call-status-changed";
767
874
  var NEW_FINDMY_LOCATION = "new-findmy-location";
768
875
 
769
- export { AdvancedIMessageKit, CHAT_READ_STATUS_CHANGED, FT_CALL_STATUS_CHANGED, GROUP_ICON_CHANGED, GROUP_ICON_REMOVED, GROUP_NAME_CHANGE, HELLO_WORLD, IMESSAGE_ALIASES_REMOVED, INCOMING_FACETIME, MESSAGE_SEND_ERROR, MESSAGE_UPDATED, NEW_FINDMY_LOCATION, NEW_MESSAGE, NEW_SERVER, PARTICIPANT_ADDED, PARTICIPANT_LEFT, PARTICIPANT_REMOVED, SCHEDULED_MESSAGE_CREATED, SCHEDULED_MESSAGE_DELETED, SCHEDULED_MESSAGE_ERROR, SCHEDULED_MESSAGE_SENT, SCHEDULED_MESSAGE_UPDATED, SDK, SERVER_UPDATE, SERVER_UPDATE_DOWNLOADING, SERVER_UPDATE_INSTALLING, SETTINGS_BACKUP_CREATED, SETTINGS_BACKUP_DELETED, SETTINGS_BACKUP_UPDATED, THEME_BACKUP_CREATED, THEME_BACKUP_DELETED, THEME_BACKUP_UPDATED, TYPING_INDICATOR, getLogger, setGlobalLogLevel };
876
+ // lib/poll-utils.ts
877
+ var POLL_BALLOON_BUNDLE_ID = "com.apple.messages.MSMessageExtensionBalloonPlugin:0000000000:com.apple.messages.Polls";
878
+ var pollCache = /* @__PURE__ */ new Map();
879
+ function cachePoll(messageGuid, poll) {
880
+ pollCache.set(messageGuid, poll);
881
+ }
882
+ function getOptionTextById(optionId) {
883
+ for (const poll of pollCache.values()) {
884
+ const option = poll.options.find((o) => o.optionIdentifier === optionId);
885
+ if (option) return option.text;
886
+ }
887
+ return null;
888
+ }
889
+ function isPollMessage(message) {
890
+ return message.balloonBundleId === POLL_BALLOON_BUNDLE_ID;
891
+ }
892
+ function isPollVote(message) {
893
+ return isPollMessage(message) && message.associatedMessageType === "4000";
894
+ }
895
+ function extractDataUrl(payloadData) {
896
+ if (!payloadData || payloadData.length === 0) return null;
897
+ const payload = payloadData[0];
898
+ if (!payload) return null;
899
+ if (payload.URL && typeof payload.URL === "string") {
900
+ return payload.URL;
901
+ }
902
+ const objects = payload.$objects;
903
+ if (Array.isArray(objects)) {
904
+ for (const obj of objects) {
905
+ if (typeof obj === "object" && obj !== null) {
906
+ if (obj["NS.relative"] && typeof obj["NS.relative"] === "object") {
907
+ const relativeObj = objects[obj["NS.relative"].UID];
908
+ if (typeof relativeObj === "string" && relativeObj.startsWith("data:,")) {
909
+ return relativeObj;
910
+ }
911
+ }
912
+ if (typeof obj === "string" && obj.startsWith("data:,")) {
913
+ return obj;
914
+ }
915
+ }
916
+ }
917
+ }
918
+ return null;
919
+ }
920
+ function parseDataUrl(dataUrl) {
921
+ try {
922
+ const prefix = "data:,";
923
+ if (!dataUrl.startsWith(prefix)) return null;
924
+ let data = dataUrl.slice(prefix.length);
925
+ const queryIndex = data.indexOf("?");
926
+ if (queryIndex !== -1) {
927
+ data = data.slice(0, queryIndex);
928
+ }
929
+ data = decodeURIComponent(data);
930
+ try {
931
+ return JSON.parse(data);
932
+ } catch {
933
+ const decoded = Buffer.from(data, "base64").toString("utf-8");
934
+ return JSON.parse(decoded);
935
+ }
936
+ } catch {
937
+ return null;
938
+ }
939
+ }
940
+ function parsePollDefinition(message) {
941
+ if (!isPollMessage(message)) return null;
942
+ if (isPollVote(message)) return null;
943
+ const dataUrl = extractDataUrl(message.payloadData);
944
+ if (!dataUrl) return null;
945
+ const data = parseDataUrl(dataUrl);
946
+ if (!data || !data.item) return null;
947
+ const parsed = {
948
+ title: data.item.title || "",
949
+ creatorHandle: data.item.creatorHandle || "",
950
+ options: data.item.orderedPollOptions || []
951
+ };
952
+ if (message.guid) {
953
+ cachePoll(message.guid, parsed);
954
+ }
955
+ return parsed;
956
+ }
957
+ function parsePollVotes(message) {
958
+ if (!isPollMessage(message)) return null;
959
+ if (!isPollVote(message)) return null;
960
+ const dataUrl = extractDataUrl(message.payloadData);
961
+ if (!dataUrl) return null;
962
+ const data = parseDataUrl(dataUrl);
963
+ if (!data || !data.item) return null;
964
+ return {
965
+ votes: data.item.votes || []
966
+ };
967
+ }
968
+ function getPollSummary(message) {
969
+ if (!isPollMessage(message)) {
970
+ return message.text || "(no text)";
971
+ }
972
+ if (isPollVote(message)) {
973
+ const voteData = parsePollVotes(message);
974
+ if (voteData && voteData.votes.length > 0) {
975
+ const votes = voteData.votes.map((v) => {
976
+ const optionText = getOptionTextById(v.voteOptionIdentifier);
977
+ const optionDisplay = optionText ? `"${optionText}"` : `option ${v.voteOptionIdentifier}`;
978
+ return `${v.participantHandle || "Someone"} voted ${optionDisplay}`;
979
+ }).join(", ");
980
+ return `[Poll Vote] ${votes}`;
981
+ }
982
+ return "[Poll Vote]";
983
+ }
984
+ const pollData = parsePollDefinition(message);
985
+ if (pollData) {
986
+ const title = pollData.title ? `"${pollData.title}"` : "(untitled poll)";
987
+ const optionsList = pollData.options.map((opt, i) => ` ${i + 1}. ${opt.text}`).join("\n");
988
+ return `[Poll] ${title}
989
+ ${optionsList}`;
990
+ }
991
+ return "[Poll] (unable to parse)";
992
+ }
993
+ function getPollOneLiner(message) {
994
+ if (!isPollMessage(message)) {
995
+ return message.text || "(no text)";
996
+ }
997
+ if (isPollVote(message)) {
998
+ const voteData = parsePollVotes(message);
999
+ if (voteData && voteData.votes.length > 0) {
1000
+ return `[Poll Vote] ${voteData.votes.length} vote(s)`;
1001
+ }
1002
+ return "[Poll Vote]";
1003
+ }
1004
+ const pollData = parsePollDefinition(message);
1005
+ if (pollData) {
1006
+ const title = pollData.title || "Poll";
1007
+ const optionsPreview = pollData.options.slice(0, 2).map((o) => o.text).join(", ");
1008
+ const moreOptions = pollData.options.length > 2 ? `, +${pollData.options.length - 2} more` : "";
1009
+ return `[${title}] ${optionsPreview}${moreOptions}`;
1010
+ }
1011
+ return "[Poll]";
1012
+ }
1013
+
1014
+ export { AdvancedIMessageKit, CHAT_READ_STATUS_CHANGED, FT_CALL_STATUS_CHANGED, GROUP_ICON_CHANGED, GROUP_ICON_REMOVED, GROUP_NAME_CHANGE, HELLO_WORLD, IMESSAGE_ALIASES_REMOVED, INCOMING_FACETIME, MESSAGE_SEND_ERROR, MESSAGE_UPDATED, NEW_FINDMY_LOCATION, NEW_MESSAGE, NEW_SERVER, PARTICIPANT_ADDED, PARTICIPANT_LEFT, PARTICIPANT_REMOVED, SCHEDULED_MESSAGE_CREATED, SCHEDULED_MESSAGE_DELETED, SCHEDULED_MESSAGE_ERROR, SCHEDULED_MESSAGE_SENT, SCHEDULED_MESSAGE_UPDATED, SDK, SERVER_UPDATE, SERVER_UPDATE_DOWNLOADING, SERVER_UPDATE_INSTALLING, SETTINGS_BACKUP_CREATED, SETTINGS_BACKUP_DELETED, SETTINGS_BACKUP_UPDATED, THEME_BACKUP_CREATED, THEME_BACKUP_DELETED, THEME_BACKUP_UPDATED, TYPING_INDICATOR, getLogger, getPollOneLiner, getPollSummary, isPollMessage, isPollVote, parsePollDefinition, parsePollVotes, setGlobalLogLevel };
770
1015
  //# sourceMappingURL=index.js.map
771
1016
  //# sourceMappingURL=index.js.map