@spectrum-ts/imessage-local 11.0.0 → 11.2.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.js +37 -5
- package/package.json +1 -1
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
|
|
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
|
|
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
|
}
|