@spectrum-ts/imessage-local 11.1.0 → 12.0.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
@@ -11,7 +11,7 @@ import { imessage } from "@spectrum-ts/imessage-local";
11
11
  import { Spectrum } from "spectrum-ts";
12
12
 
13
13
  const spectrum = Spectrum({
14
- platforms: [imessage.config()],
14
+ providers: [imessage.config()],
15
15
  });
16
16
  ```
17
17
 
package/dist/index.js CHANGED
@@ -1,6 +1,6 @@
1
1
  import { IMessageSDK } from "@photon-ai/imessage-kit";
2
2
  import { UnsupportedError, appLayoutSchema, definePlatform, fromVCard, read, stream, text, toVCard } from "@spectrum-ts/core";
3
- import { asAttachment, asContact, asCustom, asReply, asText, buildPhotoAction, messageEffectSchema, photoActionSchema } from "@spectrum-ts/core/authoring";
3
+ import { asAttachment, asContact, asCustom, asReply, asText, asVoice, buildPhotoAction, messageEffectSchema, photoActionSchema } from "@spectrum-ts/core/authoring";
4
4
  import z from "zod";
5
5
  import { setTimeout } from "node:timers/promises";
6
6
  import { createReadStream } from "node:fs";
@@ -185,6 +185,18 @@ function effect(input, messageEffect) {
185
185
  //#region src/ids.ts
186
186
  const dmChatGuid = (address) => `any;-;${address}`;
187
187
  const chatTypeFromGuid = (guid) => guid.includes(";+;") ? "group" : "dm";
188
+ //#endregion
189
+ //#region ../imessage/src/shared/audio.ts
190
+ const AUDIO_MIME_PATTERN = /^audio\//i;
191
+ const CAF_MIME_TYPE = "audio/x-caf";
192
+ const CAF_UTI = "com.apple.coreaudio-format";
193
+ const GENERIC_BINARY_MIME_TYPE = "application/octet-stream";
194
+ const isCafAttachment = (attachment) => attachment.uti?.toLowerCase() === CAF_UTI || attachment.fileName?.toLowerCase().endsWith(".caf") === true;
195
+ const normalizeAppleAttachmentMimeType = (attachment) => attachment.mimeType.toLowerCase() === GENERIC_BINARY_MIME_TYPE && isCafAttachment(attachment) ? CAF_MIME_TYPE : attachment.mimeType;
196
+ const appleAudioMimeType = (attachment) => {
197
+ const mimeType = normalizeAppleAttachmentMimeType(attachment);
198
+ return AUDIO_MIME_PATTERN.test(mimeType) ? mimeType : void 0;
199
+ };
188
200
  const addTextPart = (parts, text) => {
189
201
  const trimmed = text?.trim();
190
202
  if (trimmed) parts.push({
@@ -248,7 +260,18 @@ const toAttachmentContent = (att) => {
248
260
  return asAttachment({
249
261
  id: att.id,
250
262
  name: att.fileName ?? "attachment",
251
- mimeType: att.mimeType,
263
+ mimeType: normalizeAppleAttachmentMimeType(att),
264
+ size: att.sizeBytes,
265
+ read: () => readLocalAttachment(att),
266
+ stream: localPath ? async () => Readable.toWeb(createReadStream(localPath)) : void 0
267
+ });
268
+ };
269
+ const toVoiceContent = (att, mimeType) => {
270
+ const { localPath } = att;
271
+ return asVoice({
272
+ id: att.id,
273
+ name: att.fileName ?? void 0,
274
+ mimeType,
252
275
  size: att.sizeBytes,
253
276
  read: () => readLocalAttachment(att),
254
277
  stream: localPath ? async () => Readable.toWeb(createReadStream(localPath)) : void 0
@@ -261,7 +284,11 @@ const toVCardContent = async (att) => {
261
284
  return toAttachmentContent(att);
262
285
  }
263
286
  };
264
- const localAttachmentContent = async (att) => isVCardAttachment(att.mimeType, att.fileName) ? await toVCardContent(att) : toAttachmentContent(att);
287
+ const localAttachmentContent = async (att, isVoice = false) => {
288
+ if (isVCardAttachment(att.mimeType, att.fileName)) return await toVCardContent(att);
289
+ const audioMimeType = isVoice ? appleAudioMimeType(att) : void 0;
290
+ return audioMimeType ? toVoiceContent(att, audioMimeType) : toAttachmentContent(att);
291
+ };
265
292
  //#endregion
266
293
  //#region src/local/inbound.ts
267
294
  const ATTACHMENT_JOIN_RETRY_DELAY_MS = 250;
@@ -270,6 +297,10 @@ const ATTACHMENT_JOIN_FETCH_LIMIT = 10;
270
297
  const hasAttachmentPlaceholder = (message) => message.text?.includes("") ?? false;
271
298
  const isPendingAttachmentJoin = (message) => message.attachments.length === 0 && (message.hasAttachments || hasAttachmentPlaceholder(message));
272
299
  const replyTargetId = (message) => message.threadRootMessageId ?? void 0;
300
+ const voiceAttachmentId = (message) => {
301
+ if (!message.isAudioMessage) return;
302
+ return message.attachments.find((attachment) => appleAudioMimeType(attachment))?.id;
303
+ };
273
304
  const stubReplyTarget = (space, targetId) => ({
274
305
  id: targetId,
275
306
  content: asCustom({
@@ -326,11 +357,12 @@ const toMessages = async (message) => {
326
357
  timestamp: message.createdAt
327
358
  };
328
359
  const targetId = replyTargetId(message);
360
+ const audioAttachmentId = voiceAttachmentId(message);
329
361
  if (message.attachments.length > 0) {
330
362
  if (!hasUsableTextPart(message.text)) return wrapReply(await Promise.all(message.attachments.map(async (att) => ({
331
363
  ...base,
332
364
  id: `${message.id}:${att.id}`,
333
- content: await localAttachmentContent(att)
365
+ content: await localAttachmentContent(att, att.id === audioAttachmentId)
334
366
  }))), targetId);
335
367
  const parts = toOrderedParts(message.text, message.attachments);
336
368
  const messages = [];
@@ -345,7 +377,7 @@ const toMessages = async (message) => {
345
377
  } : {
346
378
  ...base,
347
379
  id: `${message.id}:${part.attachment.id}`,
348
- content: await localAttachmentContent(part.attachment),
380
+ content: await localAttachmentContent(part.attachment, part.attachment.id === audioAttachmentId),
349
381
  partIndex: i
350
382
  });
351
383
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@spectrum-ts/imessage-local",
3
- "version": "11.1.0",
3
+ "version": "12.0.0",
4
4
  "description": "Local macOS iMessage provider for spectrum-ts.",
5
5
  "repository": {
6
6
  "type": "git",
@@ -30,7 +30,7 @@
30
30
  "zod": "^4.2.1"
31
31
  },
32
32
  "peerDependencies": {
33
- "@spectrum-ts/core": "^11.0.0",
33
+ "@spectrum-ts/core": "^12.0.0",
34
34
  "typescript": "^5 || ^6.0.0"
35
35
  },
36
36
  "license": "MIT"