@photon-ai/advanced-imessage-kit 1.14.3 → 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) |
@@ -101,6 +119,15 @@ interface ClientConfig {
101
119
  }
102
120
  ```
103
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
+
104
131
  ---
105
132
 
106
133
  ## Core Concepts
@@ -191,6 +218,71 @@ await sdk.messages.sendMessage({
191
218
 
192
219
  > Example: [message-effects.ts](./examples/message-effects.ts)
193
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
+
194
286
  ### Query Messages
195
287
 
196
288
  ```typescript
@@ -529,6 +621,38 @@ const message = await sdk.attachments.sendAttachment({
529
621
  });
530
622
  ```
531
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
+
532
656
  ### Send Audio Messages
533
657
 
534
658
  ```typescript
@@ -1114,8 +1238,9 @@ bun run examples/<filename>.ts
1114
1238
  | [message-unsend.ts](./examples/message-unsend.ts) | Unsend messages |
1115
1239
  | [message-edit.ts](./examples/message-edit.ts) | Edit messages |
1116
1240
  | [message-reaction.ts](./examples/message-reaction.ts) | Send Tapbacks |
1117
- | [message-effects.ts](./examples/message-effects.ts) | Message effects |
1118
- | [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 |
1119
1244
  | [message-history.ts](./examples/message-history.ts) | Message history |
1120
1245
  | [message-destination-caller-id.ts](./examples/message-destination-caller-id.ts) | Destination caller ID |
1121
1246
 
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(",") } : {}