@photon-ai/advanced-imessage-kit 1.14.2 → 1.16.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/README.md CHANGED
@@ -14,7 +14,23 @@
14
14
 
15
15
  Advanced iMessage Kit is a full-featured iMessage SDK for **reading**, **sending**, and **automating** iMessage conversations on macOS. Perfect for building **AI agents**, **automation tools**, and **chat applications**.
16
16
 
17
- ---
17
+ ## Enterprise
18
+
19
+ **Advanced iMessage Kit** is the **enterprise edition** provided by **[Photon](https://photon.codes)**.
20
+
21
+ - **Provisioned iMessage Numbers** – Dedicated numbers ready for production use
22
+ - **Managed Hosting** – Fully managed infrastructure for reliability and scale
23
+ - **DevOps & Deployment Support** – Production setup, monitoring, and scaling
24
+ - **Enterprise Support** – Direct support and custom integrations
25
+
26
+ 📩 **Enterprise inquiries:**
27
+ Contact **daniel@photon.codes**
28
+
29
+ 🌐 **Book a Demo:**
30
+ https://photon.codes
31
+
32
+ If you're looking for the **free/open-source version**, please use **[iMessage Kit](https://github.com/photon-hq/imessage-kit)** instead.
33
+
18
34
 
19
35
  ## Features
20
36
 
@@ -23,6 +39,8 @@ Advanced iMessage Kit is a full-featured iMessage SDK for **reading**, **sending
23
39
  | [Send Messages](#send-messages) | Send text messages to any contact | `messages.sendMessage()` | [message-send.ts](./examples/message-send.ts) |
24
40
  | [Reply to Messages](#send-messages) | Reply inline to a specific message | `messages.sendMessage()` | [message-reply.ts](./examples/message-reply.ts) |
25
41
  | [Message Effects](#send-messages) | Send with effects (confetti, fireworks, etc.) | `messages.sendMessage()` | [message-effects.ts](./examples/message-effects.ts) |
42
+ | [Text Styles](#text-styles--animations) | Bold, italic, underline, strikethrough | `messages.sendMessage()` | [message-styled.ts](./examples/message-styled.ts) |
43
+ | [Text Animations](#text-styles--animations) | Per-character animations (shake, ripple, etc.)| `messages.sendMessage()` | [message-styled.ts](./examples/message-styled.ts) |
26
44
  | [Send Rich Links](#send-messages) | Send URLs with rich link previews | `messages.sendMessage()` | [message-rich-link.ts](./examples/message-rich-link.ts) |
27
45
  | [Schedule Messages](#scheduled-messages) | Send once or on a recurring schedule | `scheduledMessages.createScheduledMessage()` | [scheduled-message-once.ts](./examples/scheduled-message-once.ts) |
28
46
  | [Unsend Messages](#unsend-messages) | Retract a sent message | `messages.unsendMessage()` | [message-unsend.ts](./examples/message-unsend.ts) |
@@ -49,6 +67,7 @@ Advanced iMessage Kit is a full-featured iMessage SDK for **reading**, **sending
49
67
  | [Vote on Polls](#vote-on-polls) | Vote or unvote on poll options | `polls.vote()` | [poll-vote.ts](./examples/poll-vote.ts) |
50
68
  | [Add Poll Options](#add-poll-options) | Add options to existing polls | `polls.addOption()` | [poll-add-option.ts](./examples/poll-add-option.ts) |
51
69
  | [Find My Friends](#find-my-friends) | Get friends' locations | `icloud.refreshFindMyFriends()` | [findmy-friends.ts](./examples/findmy-friends.ts) |
70
+ | [Find My Watch](#find-my-watch) | Watch friends' location changes in real-time | `icloud.refreshFindMyFriends()` | [findmy-watch.ts](./examples/findmy-watch.ts) |
52
71
  | [Set Chat Background](#chat-background) | Set custom background image for chat | `chats.setBackground()` | [background-set.ts](./examples/background-set.ts) |
53
72
  | [Remove Chat Background](#chat-background) | Remove background from chat | `chats.removeBackground()` | [background-remove.ts](./examples/background-remove.ts) |
54
73
  | [Real-time Events](#real-time-events) | Listen for new messages, typing, etc. | `sdk.on()` | [listen-simple.ts](./examples/listen-simple.ts) |
@@ -100,6 +119,15 @@ interface ClientConfig {
100
119
  }
101
120
  ```
102
121
 
122
+ ### Remote Server Setup
123
+
124
+ If your SDK code runs on a different machine than the iMessage server:
125
+
126
+ - `serverUrl` points to the server machine
127
+ - `filePath` points to the SDK machine
128
+
129
+ The SDK reads local files first, then uploads the file bytes to the server.
130
+
103
131
  ---
104
132
 
105
133
  ## Core Concepts
@@ -190,6 +218,71 @@ await sdk.messages.sendMessage({
190
218
 
191
219
  > Example: [message-effects.ts](./examples/message-effects.ts)
192
220
 
221
+ ### Text Styles & Animations
222
+
223
+ Apply per-range text formatting and whole-message character animations (requires Private API):
224
+
225
+ ```typescript
226
+ // Bold a portion of text
227
+ await sdk.messages.sendMessage({
228
+ chatGuid: "iMessage;-;+1234567890",
229
+ message: "This is bold text",
230
+ textStyles: [{ start: 8, end: 12, bold: true }],
231
+ });
232
+
233
+ // Multiple styles in one message
234
+ await sdk.messages.sendMessage({
235
+ chatGuid: "iMessage;-;+1234567890",
236
+ message: "Bold here, italic there",
237
+ textStyles: [
238
+ { start: 0, end: 9, bold: true },
239
+ { start: 11, end: 23, italic: true },
240
+ ],
241
+ });
242
+
243
+ // Character animation (applies to whole message)
244
+ await sdk.messages.sendMessage({
245
+ chatGuid: "iMessage;-;+1234567890",
246
+ message: "Ripple wave!",
247
+ textAnimation: "ripple",
248
+ });
249
+
250
+ // Combine all three layers
251
+ await sdk.messages.sendMessage({
252
+ chatGuid: "iMessage;-;+1234567890",
253
+ message: "Bold shaking fireworks!",
254
+ textStyles: [{ start: 0, end: 4, bold: true }],
255
+ textAnimation: "shake",
256
+ effectId: "com.apple.messages.effect.CKFireworksEffect",
257
+ });
258
+ ```
259
+
260
+ **Text Style Properties** (per range):
261
+
262
+ | Property | Type | Description |
263
+ | --------------- | ------- | -------------- |
264
+ | `start` | number | Start index |
265
+ | `end` | number | End index |
266
+ | `bold` | boolean | Bold |
267
+ | `italic` | boolean | Italic |
268
+ | `underline` | boolean | Underline |
269
+ | `strikethrough` | boolean | Strikethrough |
270
+
271
+ **Text Animations** (whole message):
272
+
273
+ | `textAnimation` | Description |
274
+ | --------------- | ----------- |
275
+ | `"big"` | Big |
276
+ | `"small"` | Small |
277
+ | `"shake"` | Shake |
278
+ | `"nod"` | Nod |
279
+ | `"explode"` | Explode |
280
+ | `"ripple"` | Ripple |
281
+ | `"bloom"` | Bloom |
282
+ | `"jitter"` | Jitter |
283
+
284
+ > Example: [message-styled.ts](./examples/message-styled.ts)
285
+
193
286
  ### Query Messages
194
287
 
195
288
  ```typescript
@@ -337,7 +430,7 @@ const updated = await sdk.scheduledMessages.updateScheduledMessage(
337
430
  },
338
431
  scheduledFor: Date.now() + 10 * 60 * 1000,
339
432
  schedule: { type: "once" },
340
- }
433
+ },
341
434
  );
342
435
 
343
436
  await sdk.scheduledMessages.deleteScheduledMessage("scheduled-id");
@@ -528,6 +621,38 @@ const message = await sdk.attachments.sendAttachment({
528
621
  });
529
622
  ```
530
623
 
624
+ ### Send Multiple Images In One Request
625
+
626
+ Use `sendMultipartMessage()` when you want to send multiple images together as one multipart iMessage request:
627
+
628
+ ```typescript
629
+ const message = await sdk.messages.sendMultipartMessage({
630
+ chatGuid: "iMessage;+;chat123456789",
631
+ parts: [
632
+ {
633
+ partIndex: 0,
634
+ filePath: "/path/to/image-1.jpg",
635
+ },
636
+ {
637
+ partIndex: 1,
638
+ filePath: "/path/to/image-2.jpg",
639
+ },
640
+ {
641
+ partIndex: 2,
642
+ filePath: "/path/to/image-3.jpg",
643
+ },
644
+ ],
645
+ });
646
+ ```
647
+
648
+ Notes:
649
+ - `sendMultipartMessage()` uploads each file first, then sends all parts together in a single `/api/v1/message/multipart` request.
650
+ - This requires the Private API path on the server.
651
+ - The target `chatGuid` must already exist. If you only have a phone number/email, create or fetch the chat first.
652
+ - If the SDK and server are on different machines, each `filePath` must exist on the SDK machine. The SDK uploads the file bytes to the server before sending.
653
+
654
+ > Example: [message-multipart-images.ts](./examples/message-multipart-images.ts)
655
+
531
656
  ### Send Audio Messages
532
657
 
533
658
  ```typescript
@@ -595,9 +720,8 @@ const buffer = await sdk.attachments.downloadAttachment("attachment-guid", {
595
720
  });
596
721
 
597
722
  // Download Live Photo video
598
- const liveBuffer = await sdk.attachments.downloadAttachmentLive(
599
- "attachment-guid"
600
- );
723
+ const liveBuffer =
724
+ await sdk.attachments.downloadAttachmentLive("attachment-guid");
601
725
 
602
726
  // Get blurhash (for placeholders)
603
727
  const blurhash = await sdk.attachments.getAttachmentBlurhash("attachment-guid");
@@ -686,11 +810,11 @@ Check if a phone/email supports iMessage or FaceTime:
686
810
  // First parameter is the address (phone or email), not handle guid
687
811
  const hasIMessage = await sdk.handles.getHandleAvailability(
688
812
  "+1234567890",
689
- "imessage"
813
+ "imessage",
690
814
  );
691
815
  const hasFaceTime = await sdk.handles.getHandleAvailability(
692
816
  "+1234567890",
693
- "facetime"
817
+ "facetime",
694
818
  );
695
819
 
696
820
  // Choose service based on availability
@@ -852,7 +976,7 @@ sdk.on("new-message", (message) => {
852
976
 
853
977
  ## iCloud
854
978
 
855
- > Example: [findmy-friends.ts](./examples/findmy-friends.ts)
979
+ > Examples: [findmy-friends.ts](./examples/findmy-friends.ts) | [findmy-watch.ts](./examples/findmy-watch.ts)
856
980
 
857
981
  ### Find My Friends
858
982
 
@@ -870,10 +994,10 @@ const locations = await sdk.icloud.refreshFindMyFriends();
870
994
  const friend = locations.find((loc) => loc.handle === "+1234567890");
871
995
  if (friend) {
872
996
  console.log(
873
- `Coordinates: ${friend.coordinates[0]}, ${friend.coordinates[1]}`
997
+ `Coordinates: ${friend.coordinates[0]}, ${friend.coordinates[1]}`,
874
998
  );
875
999
  console.log(
876
- `Maps: https://maps.google.com/?q=${friend.coordinates[0]},${friend.coordinates[1]}`
1000
+ `Maps: https://maps.google.com/?q=${friend.coordinates[0]},${friend.coordinates[1]}`,
877
1001
  );
878
1002
  if (friend.long_address) console.log(`Address: ${friend.long_address}`);
879
1003
  }
@@ -887,6 +1011,37 @@ for (const loc of locations) {
887
1011
 
888
1012
  > Example: [findmy-friends.ts](./examples/findmy-friends.ts)
889
1013
 
1014
+ ### Find My Watch
1015
+
1016
+ Watch friends' location changes in real-time. Fetches initial locations on connect, then listens for live updates via the `new-findmy-location` event (server auto-refreshes every 30s):
1017
+
1018
+ ```typescript
1019
+ import type { FindMyLocationItem } from "@photon-ai/advanced-imessage-kit";
1020
+
1021
+ // Fetch initial locations on ready
1022
+ sdk.on("ready", async () => {
1023
+ const locations = await sdk.icloud.refreshFindMyFriends();
1024
+ for (const loc of locations) {
1025
+ console.log(`${loc.handle}`);
1026
+ console.log(` Coordinates: ${loc.coordinates[0]}, ${loc.coordinates[1]}`);
1027
+ console.log(
1028
+ ` Maps: https://maps.google.com/?q=${loc.coordinates[0]},${loc.coordinates[1]}`,
1029
+ );
1030
+ if (loc.long_address) console.log(` Address: ${loc.long_address}`);
1031
+ }
1032
+ });
1033
+
1034
+ // Listen for real-time location updates
1035
+ sdk.on("new-findmy-location", (location: FindMyLocationItem) => {
1036
+ console.log(`${location.handle} updated:`);
1037
+ console.log(
1038
+ ` Coordinates: ${location.coordinates[0]}, ${location.coordinates[1]}`,
1039
+ );
1040
+ });
1041
+ ```
1042
+
1043
+ > Example: [findmy-watch.ts](./examples/findmy-watch.ts)
1044
+
890
1045
  ---
891
1046
 
892
1047
  ## Real-time Events
@@ -1083,8 +1238,9 @@ bun run examples/<filename>.ts
1083
1238
  | [message-unsend.ts](./examples/message-unsend.ts) | Unsend messages |
1084
1239
  | [message-edit.ts](./examples/message-edit.ts) | Edit messages |
1085
1240
  | [message-reaction.ts](./examples/message-reaction.ts) | Send Tapbacks |
1086
- | [message-effects.ts](./examples/message-effects.ts) | Message effects |
1087
- | [message-search.ts](./examples/message-search.ts) | Search messages |
1241
+ | [message-effects.ts](./examples/message-effects.ts) | Message effects |
1242
+ | [message-styled.ts](./examples/message-styled.ts) | Text styles & animations |
1243
+ | [message-search.ts](./examples/message-search.ts) | Search messages |
1088
1244
  | [message-history.ts](./examples/message-history.ts) | Message history |
1089
1245
  | [message-destination-caller-id.ts](./examples/message-destination-caller-id.ts) | Destination caller ID |
1090
1246
 
@@ -1132,6 +1288,7 @@ bun run examples/<filename>.ts
1132
1288
  | [server-info.ts](./examples/server-info.ts) | Server info and logs |
1133
1289
  | [message-stats.ts](./examples/message-stats.ts) | Message statistics |
1134
1290
  | [findmy-friends.ts](./examples/findmy-friends.ts) | Find My Friends |
1291
+ | [findmy-watch.ts](./examples/findmy-watch.ts) | Find My Watch |
1135
1292
  | [auto-reply-hey.ts](./examples/auto-reply-hey.ts) | Auto reply bot |
1136
1293
 
1137
1294
  ---
package/dist/index.cjs CHANGED
@@ -164,14 +164,16 @@ function extractService(chatGuid) {
164
164
  return void 0;
165
165
  }
166
166
  async function createChatWithMessage(options) {
167
- const { http, address, message, tempGuid, subject, effectId, service } = options;
167
+ const { http, address, message, tempGuid, subject, bubbleEffect, textStyles, textAnimation, service } = options;
168
168
  try {
169
169
  const response = await http.post("/api/v1/chat/new", {
170
170
  addresses: [address],
171
171
  message,
172
172
  tempGuid,
173
173
  subject,
174
- effectId,
174
+ bubbleEffect,
175
+ textStyles,
176
+ textAnimation,
175
177
  ...service && { service }
176
178
  });
177
179
  return response.data.data?.guid;
@@ -529,6 +531,15 @@ var MessageModule = class {
529
531
  this.http = http;
530
532
  this.enqueueSend = enqueueSend;
531
533
  }
534
+ async uploadMultipartAttachment(part, fileName = part.fileName || path__namespace.default.basename(part.filePath)) {
535
+ const fileBuffer = await promises.readFile(part.filePath);
536
+ const form = new FormData__default.default();
537
+ form.append("attachment", fileBuffer, fileName);
538
+ const response = await this.http.post("/api/v1/attachment/upload", form, {
539
+ headers: form.getHeaders()
540
+ });
541
+ return response.data.data.path;
542
+ }
532
543
  async sendMessage(options) {
533
544
  return this.enqueueSend(async () => {
534
545
  const tempGuid = options.tempGuid || crypto.randomUUID();
@@ -547,13 +558,70 @@ var MessageModule = class {
547
558
  message: options.message,
548
559
  tempGuid,
549
560
  subject: options.subject,
550
- effectId: options.effectId,
561
+ bubbleEffect: options.bubbleEffect,
562
+ textStyles: options.textStyles,
563
+ textAnimation: options.textAnimation,
551
564
  service
552
565
  });
553
566
  return { guid: tempGuid, text: options.message, dateCreated: Date.now() };
554
567
  }
555
568
  });
556
569
  }
570
+ async sendMultipartMessage(options) {
571
+ return this.enqueueSend(async () => {
572
+ const tempGuid = options.tempGuid || crypto.randomUUID();
573
+ const buildPayloadPart = async (part, index) => {
574
+ const resolvedPartIndex = part.partIndex ?? index;
575
+ if ("text" in part) {
576
+ return {
577
+ partIndex: resolvedPartIndex,
578
+ text: part.text,
579
+ ...part.mention ? { mention: part.mention } : {}
580
+ };
581
+ }
582
+ const fileName = part.fileName || path__namespace.default.basename(part.filePath);
583
+ const uploadedPath = await this.uploadMultipartAttachment(part, fileName);
584
+ return {
585
+ partIndex: resolvedPartIndex,
586
+ attachment: uploadedPath,
587
+ name: fileName
588
+ };
589
+ };
590
+ const uploadParts = async () => {
591
+ const parts = [];
592
+ for (const [index, part] of options.parts.entries()) {
593
+ parts.push(await buildPayloadPart(part, index));
594
+ }
595
+ return parts;
596
+ };
597
+ const send = async (chatGuid) => {
598
+ const parts = await uploadParts();
599
+ const payload = {
600
+ chatGuid,
601
+ tempGuid,
602
+ parts,
603
+ subject: options.subject,
604
+ effectId: options.effectId,
605
+ selectedMessageGuid: options.selectedMessageGuid,
606
+ partIndex: options.partIndex ?? 0,
607
+ ddScan: options.ddScan ?? false,
608
+ attributedBody: options.attributedBody
609
+ };
610
+ const response = await this.http.post("/api/v1/message/multipart", payload);
611
+ return response.data.data;
612
+ };
613
+ try {
614
+ return await send(options.chatGuid);
615
+ } catch (error) {
616
+ if (isChatNotExistError(error)) {
617
+ throw new Error(
618
+ "Chat does not exist for multipart send. Use an existing chatGuid, or create the chat first before calling sendMultipartMessage()."
619
+ );
620
+ }
621
+ throw error;
622
+ }
623
+ });
624
+ }
557
625
  async getMessage(guid, options) {
558
626
  const response = await this.http.get(`/api/v1/message/${encodeURIComponent(guid)}`, {
559
627
  params: options?.with ? { with: options.with.join(",") } : {}
@@ -919,6 +987,7 @@ var _AdvancedIMessageKit = class _AdvancedIMessageKit extends EventEmitter.Event
919
987
  "new-server",
920
988
  "incoming-facetime",
921
989
  "ft-call-status-changed",
990
+ "new-findmy-location",
922
991
  "hello-world"
923
992
  ];
924
993
  for (const eventName of serverEvents) {