spectrum-ts 0.6.0 → 0.6.1

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.
@@ -107,21 +107,14 @@ async function disposeCloudAuth(clients) {
107
107
 
108
108
  // src/providers/imessage/local.ts
109
109
  import { createReadStream } from "fs";
110
- import { mkdtemp, rm, writeFile } from "fs/promises";
110
+ import { mkdtemp, readFile, rm, writeFile } from "fs/promises";
111
111
  import { tmpdir } from "os";
112
112
  import { basename, join } from "path";
113
113
  import { Readable } from "stream";
114
- import {
115
- readAttachmentBytes
116
- } from "@photon-ai/imessage-kit";
117
- var toSendResult = (result) => {
118
- if (!result.message?.id) {
119
- throw new Error(
120
- "iMessage local send did not return a message id \u2014 track upstream in @photon-ai/imessage-kit"
121
- );
122
- }
123
- return { id: result.message.id, timestamp: result.sentAt };
124
- };
114
+ var synthSendResult = () => ({
115
+ id: crypto.randomUUID(),
116
+ timestamp: /* @__PURE__ */ new Date()
117
+ });
125
118
  var DEFAULT_ATTACHMENT_NAME = "attachment";
126
119
  var VCARD_MIME_TYPES = /* @__PURE__ */ new Set([
127
120
  "text/vcard",
@@ -137,17 +130,21 @@ var isVCardAttachment = (mimeType, fileName) => {
137
130
  }
138
131
  return Boolean(fileName?.toLowerCase().endsWith(".vcf"));
139
132
  };
140
- var toSpace = (message) => ({
141
- id: message.chatId,
142
- type: message.chatKind === "group" ? "group" : "dm"
143
- });
133
+ var readLocalAttachment = async (att) => {
134
+ if (!att.localPath) {
135
+ throw new Error(
136
+ `iMessage attachment ${att.id} has no local file available on disk`
137
+ );
138
+ }
139
+ return readFile(att.localPath);
140
+ };
144
141
  var toAttachmentContent = (att) => {
145
142
  const { localPath } = att;
146
143
  return asAttachment({
147
144
  name: att.fileName ?? DEFAULT_ATTACHMENT_NAME,
148
145
  mimeType: att.mimeType,
149
146
  size: att.sizeBytes,
150
- read: () => readAttachmentBytes(att),
147
+ read: () => readLocalAttachment(att),
151
148
  stream: localPath ? async () => Readable.toWeb(
152
149
  createReadStream(localPath)
153
150
  ) : void 0
@@ -155,16 +152,23 @@ var toAttachmentContent = (att) => {
155
152
  };
156
153
  var toVCardContent = async (att) => {
157
154
  try {
158
- const buf = await readAttachmentBytes(att);
155
+ const buf = await readLocalAttachment(att);
159
156
  return asContact(fromVCard(buf.toString("utf8")));
160
157
  } catch {
161
158
  return toAttachmentContent(att);
162
159
  }
163
160
  };
164
161
  var toMessages = async (message) => {
162
+ const { chatId, chatKind } = message;
163
+ if (!chatId || chatKind === "unknown") {
164
+ return [];
165
+ }
166
+ if (message.reaction !== null || message.kind !== "text" || message.retractedAt !== null) {
167
+ return [];
168
+ }
165
169
  const base = {
166
170
  sender: { id: message.participant ?? "" },
167
- space: toSpace(message),
171
+ space: { id: chatId, type: chatKind === "group" ? "group" : "dm" },
168
172
  timestamp: message.createdAt
169
173
  };
170
174
  if (message.attachments.length > 0) {
@@ -186,19 +190,23 @@ var toMessages = async (message) => {
186
190
  };
187
191
  var messages = (client) => stream((emit, end) => {
188
192
  let lastPromise = Promise.resolve();
189
- client.startWatching({
190
- onMessage: (message) => {
191
- if (message.isFromMe) {
192
- return;
193
- }
193
+ const startPromise = client.startWatching({
194
+ onIncomingMessage: (message) => {
194
195
  lastPromise = lastPromise.then(() => toMessages(message)).then((ms) => {
195
196
  for (const m of ms) {
196
197
  emit(m);
197
198
  }
198
- }).catch((error) => end(error));
199
- }
200
- });
201
- return () => client.stopWatching();
199
+ }).catch(end);
200
+ },
201
+ onError: end
202
+ }).catch(end);
203
+ return async () => {
204
+ await startPromise.catch(() => {
205
+ });
206
+ await client.stopWatching();
207
+ await lastPromise.catch(() => {
208
+ });
209
+ };
202
210
  });
203
211
  var vcardFileName = (content) => {
204
212
  const base = content.name?.formatted ?? content.user?.id ?? "contact";
@@ -210,7 +218,7 @@ var sendTempFile = async (client, spaceId, name, data) => {
210
218
  const tmp = join(dir, safeName);
211
219
  await writeFile(tmp, data);
212
220
  try {
213
- return await client.send(spaceId, { attachments: [tmp] });
221
+ await client.send({ to: spaceId, attachments: [tmp] });
214
222
  } finally {
215
223
  await rm(dir, { recursive: true, force: true }).catch(() => {
216
224
  });
@@ -219,21 +227,20 @@ var sendTempFile = async (client, spaceId, name, data) => {
219
227
  var send = async (client, spaceId, content) => {
220
228
  switch (content.type) {
221
229
  case "text":
222
- return toSendResult(await client.send(spaceId, content.text));
230
+ await client.send({ to: spaceId, text: content.text });
231
+ return synthSendResult();
223
232
  case "attachment":
224
- return toSendResult(
225
- await sendTempFile(client, spaceId, content.name, await content.read())
226
- );
233
+ await sendTempFile(client, spaceId, content.name, await content.read());
234
+ return synthSendResult();
227
235
  case "contact": {
228
236
  const vcf = await toVCard(content);
229
- return toSendResult(
230
- await sendTempFile(
231
- client,
232
- spaceId,
233
- vcardFileName(content),
234
- Buffer.from(vcf, "utf8")
235
- )
237
+ await sendTempFile(
238
+ client,
239
+ spaceId,
240
+ vcardFileName(content),
241
+ Buffer.from(vcf, "utf8")
236
242
  );
243
+ return synthSendResult();
237
244
  }
238
245
  default:
239
246
  throw new Error(
@@ -251,7 +258,7 @@ import {
251
258
 
252
259
  // src/utils/audio.ts
253
260
  import { spawn } from "child_process";
254
- import { mkdtemp as mkdtemp2, readFile, rm as rm2, writeFile as writeFile2 } from "fs/promises";
261
+ import { mkdtemp as mkdtemp2, readFile as readFile2, rm as rm2, writeFile as writeFile2 } from "fs/promises";
255
262
  import { tmpdir as tmpdir2 } from "os";
256
263
  import { join as join2 } from "path";
257
264
  var M4A_BRANDS = /* @__PURE__ */ new Set([
@@ -358,7 +365,7 @@ var transcodeToM4a = async (buffer) => {
358
365
  if (code !== 0) {
359
366
  throw new Error(`ffmpeg conversion failed (exit ${code}): ${stderr}`);
360
367
  }
361
- const out = await readFile(outPath);
368
+ const out = await readFile2(outPath);
362
369
  return { buffer: out, duration: parseDuration(stderr) };
363
370
  } finally {
364
371
  await rm2(dir, { recursive: true, force: true }).catch(() => {
@@ -373,7 +380,7 @@ var ensureM4a = async (buffer, mimeType) => {
373
380
  };
374
381
 
375
382
  // src/providers/imessage/remote.ts
376
- var toSendResult2 = (receipt) => ({
383
+ var toSendResult = (receipt) => ({
377
384
  id: receipt.guid,
378
385
  timestamp: /* @__PURE__ */ new Date()
379
386
  });
@@ -495,14 +502,14 @@ var send2 = async (clients, spaceId, content) => {
495
502
  const chat = chatGuid(spaceId);
496
503
  switch (content.type) {
497
504
  case "text":
498
- return toSendResult2(await remote.messages.send(chat, content.text));
505
+ return toSendResult(await remote.messages.send(chat, content.text));
499
506
  case "attachment": {
500
507
  const attachment = await remote.attachments.upload({
501
508
  data: await content.read(),
502
509
  fileName: content.name,
503
510
  mimeType: content.mimeType
504
511
  });
505
- return toSendResult2(
512
+ return toSendResult(
506
513
  await remote.messages.send(chat, "", {
507
514
  attachment: attachment.guid
508
515
  })
@@ -510,7 +517,7 @@ var send2 = async (clients, spaceId, content) => {
510
517
  }
511
518
  case "contact": {
512
519
  const attachment = await sendContactAttachment(remote, content);
513
- return toSendResult2(await remote.messages.send(chat, "", { attachment }));
520
+ return toSendResult(await remote.messages.send(chat, "", { attachment }));
514
521
  }
515
522
  case "voice": {
516
523
  const { buffer } = await ensureM4a(
@@ -522,7 +529,7 @@ var send2 = async (clients, spaceId, content) => {
522
529
  fileName: content.name ?? "voice.m4a",
523
530
  mimeType: "audio/x-m4a"
524
531
  });
525
- return toSendResult2(
532
+ return toSendResult(
526
533
  await remote.messages.send(chat, "", {
527
534
  attachment: attachment.guid,
528
535
  audioMessage: true
@@ -542,7 +549,7 @@ var replyToMessage = async (clients, spaceId, msgId, content) => {
542
549
  const replyTo = messageGuid(msgId);
543
550
  switch (content.type) {
544
551
  case "text":
545
- return toSendResult2(
552
+ return toSendResult(
546
553
  await remote.messages.send(chat, content.text, { replyTo })
547
554
  );
548
555
  case "attachment": {
@@ -551,7 +558,7 @@ var replyToMessage = async (clients, spaceId, msgId, content) => {
551
558
  fileName: content.name,
552
559
  mimeType: content.mimeType
553
560
  });
554
- return toSendResult2(
561
+ return toSendResult(
555
562
  await remote.messages.send(chat, "", {
556
563
  attachment: attachment.guid,
557
564
  replyTo
@@ -560,7 +567,7 @@ var replyToMessage = async (clients, spaceId, msgId, content) => {
560
567
  }
561
568
  case "contact": {
562
569
  const attachment = await sendContactAttachment(remote, content);
563
- return toSendResult2(
570
+ return toSendResult(
564
571
  await remote.messages.send(chat, "", { attachment, replyTo })
565
572
  );
566
573
  }
@@ -574,7 +581,7 @@ var replyToMessage = async (clients, spaceId, msgId, content) => {
574
581
  fileName: content.name ?? "voice.m4a",
575
582
  mimeType: "audio/x-m4a"
576
583
  });
577
- return toSendResult2(
584
+ return toSendResult(
578
585
  await remote.messages.send(chat, "", {
579
586
  attachment: attachment.guid,
580
587
  audioMessage: true,
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "spectrum-ts",
3
- "version": "0.6.0",
3
+ "version": "0.6.1",
4
4
  "type": "module",
5
5
  "main": "./dist/index.js",
6
6
  "module": "./dist/index.js",
@@ -20,7 +20,7 @@
20
20
  },
21
21
  "dependencies": {
22
22
  "@photon-ai/advanced-imessage": "^0.4.3",
23
- "@photon-ai/imessage-kit": "^3.0.0-rc.2",
23
+ "@photon-ai/imessage-kit": "^3.0.0",
24
24
  "@photon-ai/whatsapp-business": "^0.1.1",
25
25
  "@repeaterjs/repeater": "^3.0.6",
26
26
  "better-grpc": "^0.3.2",