@photon-ai/advanced-imessage-kit 1.11.0 → 1.11.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -18,37 +18,39 @@ Advanced iMessage Kit is a full-featured iMessage SDK for **reading**, **sending
18
18
 
19
19
  ## Features
20
20
 
21
- | Feature | Description | Method | Example |
22
- | ---------------------------------------------------------- | --------------------------------------------- | ---------------------------------- | --------------------------------------------------------------- |
23
- | [Send Messages](#send-messages) | Send text messages to any contact | `messages.sendMessage()` | [message-send.ts](./examples/message-send.ts) |
24
- | [Reply to Messages](#send-messages) | Reply inline to a specific message | `messages.sendMessage()` | [message-reply.ts](./examples/message-reply.ts) |
25
- | [Message Effects](#send-messages) | Send with effects (confetti, fireworks, etc.) | `messages.sendMessage()` | [message-effects.ts](./examples/message-effects.ts) |
21
+ | Feature | Description | Method | Example |
22
+ | ---------------------------------------------------------- | --------------------------------------------- | -------------------------------------------- | ----------------------------------------------------------------- |
23
+ | [Send Messages](#send-messages) | Send text messages to any contact | `messages.sendMessage()` | [message-send.ts](./examples/message-send.ts) |
24
+ | [Reply to Messages](#send-messages) | Reply inline to a specific message | `messages.sendMessage()` | [message-reply.ts](./examples/message-reply.ts) |
25
+ | [Message Effects](#send-messages) | Send with effects (confetti, fireworks, etc.) | `messages.sendMessage()` | [message-effects.ts](./examples/message-effects.ts) |
26
26
  | [Schedule Messages](#scheduled-messages) | Send once or on a recurring schedule | `scheduledMessages.createScheduledMessage()` | [scheduled-message-once.ts](./examples/scheduled-message-once.ts) |
27
- | [Unsend Messages](#unsend-messages) | Retract a sent message | `messages.unsendMessage()` | [message-unsend.ts](./examples/message-unsend.ts) |
28
- | [Edit Messages](#edit-messages) | Edit a sent message | `messages.editMessage()` | [message-edit.ts](./examples/message-edit.ts) |
29
- | [Send Tapbacks](#send-tapbacks) | React with ❤️ 👍 👎 😂 ‼️ ❓ | `messages.sendReaction()` | [message-reaction.ts](./examples/message-reaction.ts) |
30
- | [Query Messages](#query-messages) | Search and filter message history | `messages.getMessages()` | [message-search.ts](./examples/message-search.ts) |
31
- | [Message History](#get-chat-messages) | View messages, reactions, polls, stickers | `chats.getChatMessages()` | [message-history.ts](./examples/message-history.ts) |
32
- | [Send Attachments](#send-attachments) | Send images, files, documents | `attachments.sendAttachment()` | [message-attachment.ts](./examples/message-attachment.ts) |
33
- | [Send Audio Messages](#send-audio-messages) | Send voice messages | `attachments.sendAttachment()` | [message-audio.ts](./examples/message-audio.ts) |
34
- | [Send Stickers](#send-stickers) | Send sticker as standalone message | `attachments.sendSticker()` | [message-sticker.ts](./examples/message-sticker.ts) |
35
- | [Reply Stickers](#send-stickers) | Attach sticker to a message bubble | `attachments.sendSticker()` | [message-reply-sticker.ts](./examples/message-reply-sticker.ts) |
36
- | [Download Attachments](#download-attachments) | Download received files and media | `attachments.downloadAttachment()` | [attachment-download.ts](./examples/attachment-download.ts) |
37
- | [Get Chats](#get-chats) | List all conversations | `chats.getChats()` | [chat-fetch.ts](./examples/chat-fetch.ts) |
38
- | [Get Chat Participants](#get-chat-participants) | View group chat participants | `chats.getChat()` | [chat-participants.ts](./examples/chat-participants.ts) |
39
- | [Manage Group Chats](#manage-group-chats) | Add/remove members, rename groups | `chats.addParticipant()` | [chat-group.ts](./examples/chat-group.ts) |
40
- | [Typing Indicators](#typing-indicators) | Show "typing..." status | `chats.startTyping()` | [message-typing.ts](./examples/message-typing.ts) |
41
- | [Get Contacts](#get-contacts) | Fetch device contacts | `contacts.getContacts()` | [contact-list.ts](./examples/contact-list.ts) |
42
- | [Share Contact Card](#share-contact-card) | Share your contact info in chat | `contacts.shareContactCard()` | [message-contact-card.ts](./examples/message-contact-card.ts) |
43
- | [Check iMessage Availability](#check-service-availability) | Verify if contact uses iMessage | `handles.getHandleAvailability()` | [service-check.ts](./examples/service-check.ts) |
44
- | [Server Info](#get-server-info) | Get server status and config | `server.getServerInfo()` | [server-info.ts](./examples/server-info.ts) |
45
- | [Message Statistics](#message-statistics) | Get message counts and analytics | `server.getMessageStats()` | [message-stats.ts](./examples/message-stats.ts) |
46
- | [Create Polls](#create-polls) | Create interactive polls in chat | `polls.create()` | [poll-create.ts](./examples/poll-create.ts) |
47
- | [Vote on Polls](#vote-on-polls) | Vote or unvote on poll options | `polls.vote()` | [poll-vote.ts](./examples/poll-vote.ts) |
48
- | [Add Poll Options](#add-poll-options) | Add options to existing polls | `polls.addOption()` | [poll-add-option.ts](./examples/poll-add-option.ts) |
49
- | [Find My Friends](#find-my-friends) | Get friends' locations | `icloud.refreshFindMyFriends()` | [findmy-friends.ts](./examples/findmy-friends.ts) |
50
- | [Real-time Events](#real-time-events) | Listen for new messages, typing, etc. | `sdk.on()` | [listen-simple.ts](./examples/listen-simple.ts) |
51
- | [Auto Reply](#real-time-events) | Build automated reply bots | `sdk.on()` | [auto-reply-hey.ts](./examples/auto-reply-hey.ts) |
27
+ | [Unsend Messages](#unsend-messages) | Retract a sent message | `messages.unsendMessage()` | [message-unsend.ts](./examples/message-unsend.ts) |
28
+ | [Edit Messages](#edit-messages) | Edit a sent message | `messages.editMessage()` | [message-edit.ts](./examples/message-edit.ts) |
29
+ | [Send Tapbacks](#send-tapbacks) | React with ❤️ 👍 👎 😂 ‼️ ❓ | `messages.sendReaction()` | [message-reaction.ts](./examples/message-reaction.ts) |
30
+ | [Query Messages](#query-messages) | Search and filter message history | `messages.getMessages()` | [message-search.ts](./examples/message-search.ts) |
31
+ | [Message History](#get-chat-messages) | View messages, reactions, polls, stickers | `chats.getChatMessages()` | [message-history.ts](./examples/message-history.ts) |
32
+ | [Send Attachments](#send-attachments) | Send images, files, documents | `attachments.sendAttachment()` | [message-attachment.ts](./examples/message-attachment.ts) |
33
+ | [Send Audio Messages](#send-audio-messages) | Send voice messages | `attachments.sendAttachment()` | [message-audio.ts](./examples/message-audio.ts) |
34
+ | [Send Stickers](#send-stickers) | Send sticker as standalone message | `attachments.sendSticker()` | [message-sticker.ts](./examples/message-sticker.ts) |
35
+ | [Reply Stickers](#send-stickers) | Attach sticker to a message bubble | `attachments.sendSticker()` | [message-reply-sticker.ts](./examples/message-reply-sticker.ts) |
36
+ | [Download Attachments](#download-attachments) | Download received files and media | `attachments.downloadAttachment()` | [attachment-download.ts](./examples/attachment-download.ts) |
37
+ | [Get Chats](#get-chats) | List all conversations | `chats.getChats()` | [chat-fetch.ts](./examples/chat-fetch.ts) |
38
+ | [Get Chat Participants](#get-chat-participants) | View group chat participants | `chats.getChat()` | [chat-participants.ts](./examples/chat-participants.ts) |
39
+ | [Manage Group Chats](#manage-group-chats) | Add/remove members, rename groups | `chats.addParticipant()` | [chat-group.ts](./examples/chat-group.ts) |
40
+ | [Typing Indicators](#typing-indicators) | Show "typing..." status | `chats.startTyping()` | [message-typing.ts](./examples/message-typing.ts) |
41
+ | [Get Contacts](#get-contacts) | Fetch device contacts | `contacts.getContacts()` | [contact-list.ts](./examples/contact-list.ts) |
42
+ | [Share Contact Card](#share-contact-card) | Share your contact info in chat | `contacts.shareContactCard()` | [message-contact-card.ts](./examples/message-contact-card.ts) |
43
+ | [Check iMessage Availability](#check-service-availability) | Verify if contact uses iMessage | `handles.getHandleAvailability()` | [service-check.ts](./examples/service-check.ts) |
44
+ | [Server Info](#get-server-info) | Get server status and config | `server.getServerInfo()` | [server-info.ts](./examples/server-info.ts) |
45
+ | [Message Statistics](#message-statistics) | Get message counts and analytics | `server.getMessageStats()` | [message-stats.ts](./examples/message-stats.ts) |
46
+ | [Create Polls](#create-polls) | Create interactive polls in chat | `polls.create()` | [poll-create.ts](./examples/poll-create.ts) |
47
+ | [Vote on Polls](#vote-on-polls) | Vote or unvote on poll options | `polls.vote()` | [poll-vote.ts](./examples/poll-vote.ts) |
48
+ | [Add Poll Options](#add-poll-options) | Add options to existing polls | `polls.addOption()` | [poll-add-option.ts](./examples/poll-add-option.ts) |
49
+ | [Find My Friends](#find-my-friends) | Get friends' locations | `icloud.refreshFindMyFriends()` | [findmy-friends.ts](./examples/findmy-friends.ts) |
50
+ | [Set Chat Background](#chat-background) | Set custom background image for chat | `chats.setBackground()` | [background-set.ts](./examples/background-set.ts) |
51
+ | [Remove Chat Background](#chat-background) | Remove background from chat | `chats.removeBackground()` | [background-remove.ts](./examples/background-remove.ts) |
52
+ | [Real-time Events](#real-time-events) | Listen for new messages, typing, etc. | `sdk.on()` | [listen-simple.ts](./examples/listen-simple.ts) |
53
+ | [Auto Reply](#real-time-events) | Build automated reply bots | `sdk.on()` | [auto-reply-hey.ts](./examples/auto-reply-hey.ts) |
52
54
 
53
55
  ---
54
56
 
@@ -314,16 +316,19 @@ const daily = await sdk.scheduledMessages.createScheduledMessage({
314
316
  ```typescript
315
317
  const scheduledMessages = await sdk.scheduledMessages.getScheduledMessages();
316
318
 
317
- const updated = await sdk.scheduledMessages.updateScheduledMessage("scheduled-id", {
318
- type: "send-message",
319
- payload: {
320
- chatGuid: "any;-;+1234567890",
321
- message: "Updated message!",
322
- method: "apple-script",
323
- },
324
- scheduledFor: Date.now() + 10 * 60 * 1000,
325
- schedule: { type: "once" },
326
- });
319
+ const updated = await sdk.scheduledMessages.updateScheduledMessage(
320
+ "scheduled-id",
321
+ {
322
+ type: "send-message",
323
+ payload: {
324
+ chatGuid: "any;-;+1234567890",
325
+ message: "Updated message!",
326
+ method: "apple-script",
327
+ },
328
+ scheduledFor: Date.now() + 10 * 60 * 1000,
329
+ schedule: { type: "once" },
330
+ }
331
+ );
327
332
 
328
333
  await sdk.scheduledMessages.deleteScheduledMessage("scheduled-id");
329
334
  ```
@@ -332,7 +337,7 @@ await sdk.scheduledMessages.deleteScheduledMessage("scheduled-id");
332
337
 
333
338
  ## Chats
334
339
 
335
- > Examples: [chat-fetch.ts](./examples/chat-fetch.ts) | [chat-group.ts](./examples/chat-group.ts) | [message-typing.ts](./examples/message-typing.ts)
340
+ > Examples: [chat-fetch.ts](./examples/chat-fetch.ts) | [chat-group.ts](./examples/chat-group.ts) | [message-typing.ts](./examples/message-typing.ts) | [background-set.ts](./examples/background-set.ts) | [background-remove.ts](./examples/background-remove.ts)
336
341
 
337
342
  ### Get Chats
338
343
 
@@ -466,6 +471,30 @@ await sdk.chats.removeGroupIcon("chat-guid");
466
471
 
467
472
  > Example: [chat-group.ts](./examples/chat-group.ts)
468
473
 
474
+ ### Chat Background
475
+
476
+ Set, get, or remove custom background images for individual chats:
477
+
478
+ ```typescript
479
+ // Get current background info
480
+ const bgInfo = await sdk.chats.getBackground("chat-guid");
481
+ console.log(`Has background: ${bgInfo.hasBackground}`);
482
+ if (bgInfo.hasBackground) {
483
+ console.log(`Background ID: ${bgInfo.backgroundId}`);
484
+ console.log(`Image URL: ${bgInfo.imageUrl}`);
485
+ }
486
+
487
+ // Set a background image
488
+ await sdk.chats.setBackground("chat-guid", {
489
+ filePath: "/path/to/image.png",
490
+ });
491
+
492
+ // Remove background
493
+ await sdk.chats.removeBackground("chat-guid");
494
+ ```
495
+
496
+ > Examples: [background-set.ts](./examples/background-set.ts) | [background-remove.ts](./examples/background-remove.ts)
497
+
469
498
  ---
470
499
 
471
500
  ## Attachments
@@ -1049,6 +1078,8 @@ bun run examples/<filename>.ts
1049
1078
  | [chat-participants.ts](./examples/chat-participants.ts) | Get group participants |
1050
1079
  | [chat-group.ts](./examples/chat-group.ts) | Manage groups |
1051
1080
  | [message-typing.ts](./examples/message-typing.ts) | Typing indicators |
1081
+ | [background-set.ts](./examples/background-set.ts) | Set chat background |
1082
+ | [background-remove.ts](./examples/background-remove.ts) | Remove chat background |
1052
1083
 
1053
1084
  ### Contacts & Services
1054
1085
 
package/dist/index.cjs CHANGED
@@ -769,6 +769,8 @@ var _AdvancedIMessageKit = class _AdvancedIMessageKit extends EventEmitter.Event
769
769
  // the same message content with different GUIDs due to unstable Socket.IO connections.
770
770
  // This is especially problematic when the polling transport causes connection issues.
771
771
  __publicField(this, "processedMessages", /* @__PURE__ */ new Set());
772
+ // Last message timestamp for reconnect recovery
773
+ __publicField(this, "lastMessageTime", 0);
772
774
  // Send queue for sequential message delivery
773
775
  //
774
776
  // Purpose: Ensure all outgoing messages (text, attachments, stickers, etc.) from
@@ -779,6 +781,12 @@ var _AdvancedIMessageKit = class _AdvancedIMessageKit extends EventEmitter.Event
779
781
  // Purpose: Prevent duplicate 'ready' events when both legacy mode (no API key)
780
782
  // and auth-ok events occur, which would cause user callbacks to fire twice.
781
783
  __publicField(this, "readyEmitted", false);
784
+ // Flag to track if socket event listeners have been attached
785
+ //
786
+ // Purpose: Prevent duplicate event listeners when connect() is called multiple times
787
+ // or after close(). Without this, each connect() call would add new listeners,
788
+ // causing events to fire multiple times.
789
+ __publicField(this, "listenersAttached", false);
782
790
  this.config = {
783
791
  serverUrl: "http://localhost:1234",
784
792
  logLevel: "info",
@@ -810,8 +818,18 @@ var _AdvancedIMessageKit = class _AdvancedIMessageKit extends EventEmitter.Event
810
818
  // Only WebSocket - polling disabled to prevent message duplication
811
819
  timeout: 1e4,
812
820
  // 10 second timeout to avoid overly frequent reconnections
813
- forceNew: true
821
+ forceNew: true,
814
822
  // Force new connection to avoid connection state pollution
823
+ reconnection: true,
824
+ // Enable auto-reconnection (default, but explicit for clarity)
825
+ reconnectionAttempts: Number.POSITIVE_INFINITY,
826
+ // Never give up
827
+ reconnectionDelay: 100,
828
+ // Start with 100ms delay (fast initial reconnect)
829
+ reconnectionDelayMax: 2e3,
830
+ // Max 2 seconds between attempts
831
+ randomizationFactor: 0.1
832
+ // Low randomization for more consistent reconnect timing
815
833
  });
816
834
  const enqueueSend = this.enqueueSend.bind(this);
817
835
  this.attachments = new AttachmentModule(this.http, enqueueSend);
@@ -851,6 +869,17 @@ var _AdvancedIMessageKit = class _AdvancedIMessageKit extends EventEmitter.Event
851
869
  return super.removeListener(event, listener);
852
870
  }
853
871
  async connect() {
872
+ if (!this.listenersAttached) {
873
+ this.listenersAttached = true;
874
+ this.attachSocketListeners();
875
+ }
876
+ if (this.socket.connected) {
877
+ this.logger.info("Already connected to iMessage server");
878
+ return;
879
+ }
880
+ this.socket.connect();
881
+ }
882
+ attachSocketListeners() {
854
883
  const serverEvents = [
855
884
  "new-message",
856
885
  "message-updated",
@@ -879,6 +908,9 @@ var _AdvancedIMessageKit = class _AdvancedIMessageKit extends EventEmitter.Event
879
908
  return;
880
909
  }
881
910
  this.processedMessages.add(message.guid);
911
+ if (message.dateCreated && message.dateCreated > this.lastMessageTime) {
912
+ this.lastMessageTime = message.dateCreated;
913
+ }
882
914
  }
883
915
  }
884
916
  if (args.length > 0) {
@@ -888,15 +920,32 @@ var _AdvancedIMessageKit = class _AdvancedIMessageKit extends EventEmitter.Event
888
920
  }
889
921
  });
890
922
  }
891
- this.socket.on("disconnect", () => {
892
- this.logger.info("Disconnected from iMessage server");
923
+ this.socket.on("disconnect", (reason) => {
924
+ this.logger.info(`Disconnected from iMessage server (reason: ${reason})`);
893
925
  this.readyEmitted = false;
894
926
  this.emit("disconnect");
927
+ if (reason === "io server disconnect") {
928
+ this.logger.info("Server disconnected, manually triggering reconnect...");
929
+ this.socket.connect();
930
+ }
931
+ });
932
+ this.socket.io.on("reconnect_attempt", (attempt) => {
933
+ this.logger.info(`Reconnection attempt #${attempt}...`);
934
+ });
935
+ this.socket.io.on("reconnect", (attempt) => {
936
+ this.logger.info(`Reconnected successfully after ${attempt} attempt(s)`);
895
937
  });
896
- this.socket.on("auth-ok", () => {
938
+ this.socket.io.on("reconnect_error", (error) => {
939
+ this.logger.warn(`Reconnection error: ${error.message}`);
940
+ });
941
+ this.socket.io.on("reconnect_failed", () => {
942
+ this.logger.error("All reconnection attempts failed");
943
+ });
944
+ this.socket.on("auth-ok", async () => {
897
945
  this.logger.info("Authentication successful");
898
946
  if (!this.readyEmitted) {
899
947
  this.readyEmitted = true;
948
+ await this.recoverMissedMessages();
900
949
  this.emit("ready");
901
950
  }
902
951
  });
@@ -904,27 +953,51 @@ var _AdvancedIMessageKit = class _AdvancedIMessageKit extends EventEmitter.Event
904
953
  this.logger.error(`Authentication failed: ${error.message} ${error.reason ? `(${error.reason})` : ""}`);
905
954
  this.emit("error", new Error(`Authentication failed: ${error.message}`));
906
955
  });
907
- if (this.socket.connected) {
908
- this.logger.info("Already connected to iMessage server");
909
- return;
910
- }
911
- this.socket.once("connect", () => {
956
+ this.socket.on("connect", async () => {
912
957
  this.logger.info("Connected to iMessage server, waiting for authentication...");
913
958
  if (!this.config.apiKey) {
914
959
  this.logger.info("No API key provided, skipping authentication (legacy server mode)");
915
960
  if (!this.readyEmitted) {
916
961
  this.readyEmitted = true;
962
+ await this.recoverMissedMessages();
917
963
  this.emit("ready");
918
964
  }
919
965
  }
920
966
  });
921
- if (!this.socket.connected) {
922
- this.socket.connect();
923
- }
967
+ this.socket.on("connect_error", (error) => {
968
+ this.logger.warn(`Connection error: ${error.message}`);
969
+ });
924
970
  }
925
971
  async close() {
926
972
  this.socket.disconnect();
927
973
  }
974
+ async recoverMissedMessages() {
975
+ if (this.lastMessageTime <= 0) return;
976
+ try {
977
+ const after = this.lastMessageTime;
978
+ const messages = await this.messages.getMessages({
979
+ after,
980
+ sort: "ASC",
981
+ limit: 100
982
+ });
983
+ if (messages.length === 0) {
984
+ this.logger.debug("No missed messages to recover");
985
+ return;
986
+ }
987
+ this.logger.info(`Recovering ${messages.length} missed message(s)`);
988
+ for (const msg of messages) {
989
+ if (msg.guid && !this.processedMessages.has(msg.guid)) {
990
+ this.processedMessages.add(msg.guid);
991
+ if (msg.dateCreated && msg.dateCreated > this.lastMessageTime) {
992
+ this.lastMessageTime = msg.dateCreated;
993
+ }
994
+ super.emit("new-message", msg);
995
+ }
996
+ }
997
+ } catch (e) {
998
+ this.logger.warn(`Failed to recover missed messages: ${e}`);
999
+ }
1000
+ }
928
1001
  /**
929
1002
  * Clear processed message records (prevent memory leaks)
930
1003
  * @param maxSize Maximum number of messages to retain, default 1000