spectrum-ts 0.4.0 → 0.6.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/chunk-UZWRB3FZ.js +624 -0
- package/dist/chunk-XZTTLPHE.js +341 -0
- package/dist/index.d.ts +137 -5
- package/dist/index.js +158 -79
- package/dist/providers/imessage/index.d.ts +1 -1
- package/dist/providers/imessage/index.js +369 -69
- package/dist/providers/terminal/index.d.ts +1 -1
- package/dist/providers/terminal/index.js +7 -3
- package/dist/providers/whatsapp-business/index.d.ts +1 -1
- package/dist/providers/whatsapp-business/index.js +289 -35
- package/dist/{types-BdWMydUJ.d.ts → types-DZMHfgYQ.d.ts} +102 -13
- package/package.json +9 -2
- package/dist/chunk-5XW4CAWS.js +0 -165
- package/dist/chunk-XEEDIGVK.js +0 -199
|
@@ -3,14 +3,17 @@ import {
|
|
|
3
3
|
} from "../../chunk-HXM64ENV.js";
|
|
4
4
|
import {
|
|
5
5
|
asAttachment,
|
|
6
|
+
asContact,
|
|
6
7
|
asCustom,
|
|
8
|
+
fromVCard,
|
|
7
9
|
mergeStreams,
|
|
8
|
-
stream
|
|
9
|
-
|
|
10
|
+
stream,
|
|
11
|
+
toVCard
|
|
12
|
+
} from "../../chunk-UZWRB3FZ.js";
|
|
10
13
|
import {
|
|
11
14
|
asText,
|
|
12
15
|
definePlatform
|
|
13
|
-
} from "../../chunk-
|
|
16
|
+
} from "../../chunk-XZTTLPHE.js";
|
|
14
17
|
|
|
15
18
|
// src/providers/imessage/index.ts
|
|
16
19
|
import { createClient as createClient2, directChat } from "@photon-ai/advanced-imessage";
|
|
@@ -104,41 +107,74 @@ async function disposeCloudAuth(clients) {
|
|
|
104
107
|
|
|
105
108
|
// src/providers/imessage/local.ts
|
|
106
109
|
import { createReadStream } from "fs";
|
|
107
|
-
import {
|
|
110
|
+
import { mkdtemp, rm, writeFile } from "fs/promises";
|
|
108
111
|
import { tmpdir } from "os";
|
|
109
|
-
import { join } from "path";
|
|
112
|
+
import { basename, join } from "path";
|
|
110
113
|
import { Readable } from "stream";
|
|
111
114
|
import {
|
|
112
115
|
readAttachmentBytes
|
|
113
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
125
|
var DEFAULT_ATTACHMENT_NAME = "attachment";
|
|
126
|
+
var VCARD_MIME_TYPES = /* @__PURE__ */ new Set([
|
|
127
|
+
"text/vcard",
|
|
128
|
+
"text/x-vcard",
|
|
129
|
+
"text/directory",
|
|
130
|
+
"application/vcard",
|
|
131
|
+
"application/x-vcard"
|
|
132
|
+
]);
|
|
133
|
+
var normalizeMimeType = (mimeType) => (mimeType.split(";")[0] ?? "").trim().toLowerCase();
|
|
134
|
+
var isVCardAttachment = (mimeType, fileName) => {
|
|
135
|
+
if (mimeType && VCARD_MIME_TYPES.has(normalizeMimeType(mimeType))) {
|
|
136
|
+
return true;
|
|
137
|
+
}
|
|
138
|
+
return Boolean(fileName?.toLowerCase().endsWith(".vcf"));
|
|
139
|
+
};
|
|
115
140
|
var toSpace = (message) => ({
|
|
116
141
|
id: message.chatId,
|
|
117
142
|
type: message.chatKind === "group" ? "group" : "dm"
|
|
118
143
|
});
|
|
119
|
-
var
|
|
144
|
+
var toAttachmentContent = (att) => {
|
|
145
|
+
const { localPath } = att;
|
|
146
|
+
return asAttachment({
|
|
147
|
+
name: att.fileName ?? DEFAULT_ATTACHMENT_NAME,
|
|
148
|
+
mimeType: att.mimeType,
|
|
149
|
+
size: att.sizeBytes,
|
|
150
|
+
read: () => readAttachmentBytes(att),
|
|
151
|
+
stream: localPath ? async () => Readable.toWeb(
|
|
152
|
+
createReadStream(localPath)
|
|
153
|
+
) : void 0
|
|
154
|
+
});
|
|
155
|
+
};
|
|
156
|
+
var toVCardContent = async (att) => {
|
|
157
|
+
try {
|
|
158
|
+
const buf = await readAttachmentBytes(att);
|
|
159
|
+
return asContact(fromVCard(buf.toString("utf8")));
|
|
160
|
+
} catch {
|
|
161
|
+
return toAttachmentContent(att);
|
|
162
|
+
}
|
|
163
|
+
};
|
|
164
|
+
var toMessages = async (message) => {
|
|
120
165
|
const base = {
|
|
121
166
|
sender: { id: message.participant ?? "" },
|
|
122
167
|
space: toSpace(message),
|
|
123
168
|
timestamp: message.createdAt
|
|
124
169
|
};
|
|
125
170
|
if (message.attachments.length > 0) {
|
|
126
|
-
return
|
|
127
|
-
|
|
128
|
-
return {
|
|
171
|
+
return Promise.all(
|
|
172
|
+
message.attachments.map(async (att) => ({
|
|
129
173
|
...base,
|
|
130
174
|
id: `${message.id}:${att.id}`,
|
|
131
|
-
content:
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
size: att.sizeBytes,
|
|
135
|
-
read: () => readAttachmentBytes(att),
|
|
136
|
-
stream: localPath ? async () => Readable.toWeb(
|
|
137
|
-
createReadStream(localPath)
|
|
138
|
-
) : void 0
|
|
139
|
-
})
|
|
140
|
-
};
|
|
141
|
-
});
|
|
175
|
+
content: isVCardAttachment(att.mimeType, att.fileName) ? await toVCardContent(att) : toAttachmentContent(att)
|
|
176
|
+
}))
|
|
177
|
+
);
|
|
142
178
|
}
|
|
143
179
|
return [
|
|
144
180
|
{
|
|
@@ -149,37 +185,60 @@ var toMessages = (message) => {
|
|
|
149
185
|
];
|
|
150
186
|
};
|
|
151
187
|
var messages = (client) => stream((emit, end) => {
|
|
188
|
+
let lastPromise = Promise.resolve();
|
|
152
189
|
client.startWatching({
|
|
153
190
|
onMessage: (message) => {
|
|
154
|
-
|
|
155
|
-
|
|
191
|
+
if (message.isFromMe) {
|
|
192
|
+
return;
|
|
193
|
+
}
|
|
194
|
+
lastPromise = lastPromise.then(() => toMessages(message)).then((ms) => {
|
|
195
|
+
for (const m of ms) {
|
|
156
196
|
emit(m);
|
|
157
197
|
}
|
|
158
|
-
}
|
|
159
|
-
end(error);
|
|
160
|
-
}
|
|
198
|
+
}).catch((error) => end(error));
|
|
161
199
|
}
|
|
162
200
|
});
|
|
163
201
|
return () => client.stopWatching();
|
|
164
202
|
});
|
|
203
|
+
var vcardFileName = (content) => {
|
|
204
|
+
const base = content.name?.formatted ?? content.user?.id ?? "contact";
|
|
205
|
+
return `${base.replace(/[^a-zA-Z0-9_\-.]/g, "_")}.vcf`;
|
|
206
|
+
};
|
|
207
|
+
var sendTempFile = async (client, spaceId, name, data) => {
|
|
208
|
+
const safeName = basename(name) || DEFAULT_ATTACHMENT_NAME;
|
|
209
|
+
const dir = await mkdtemp(join(tmpdir(), "spectrum-"));
|
|
210
|
+
const tmp = join(dir, safeName);
|
|
211
|
+
await writeFile(tmp, data);
|
|
212
|
+
try {
|
|
213
|
+
return await client.send(spaceId, { attachments: [tmp] });
|
|
214
|
+
} finally {
|
|
215
|
+
await rm(dir, { recursive: true, force: true }).catch(() => {
|
|
216
|
+
});
|
|
217
|
+
}
|
|
218
|
+
};
|
|
165
219
|
var send = async (client, spaceId, content) => {
|
|
166
220
|
switch (content.type) {
|
|
167
221
|
case "text":
|
|
168
|
-
await client.send(spaceId, content.text);
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
await
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
222
|
+
return toSendResult(await client.send(spaceId, content.text));
|
|
223
|
+
case "attachment":
|
|
224
|
+
return toSendResult(
|
|
225
|
+
await sendTempFile(client, spaceId, content.name, await content.read())
|
|
226
|
+
);
|
|
227
|
+
case "contact": {
|
|
228
|
+
const vcf = await toVCard(content);
|
|
229
|
+
return toSendResult(
|
|
230
|
+
await sendTempFile(
|
|
231
|
+
client,
|
|
232
|
+
spaceId,
|
|
233
|
+
vcardFileName(content),
|
|
234
|
+
Buffer.from(vcf, "utf8")
|
|
235
|
+
)
|
|
236
|
+
);
|
|
180
237
|
}
|
|
181
238
|
default:
|
|
182
|
-
|
|
239
|
+
throw new Error(
|
|
240
|
+
`Unsupported iMessage local content type: ${content.type}`
|
|
241
|
+
);
|
|
183
242
|
}
|
|
184
243
|
};
|
|
185
244
|
|
|
@@ -189,6 +248,148 @@ import {
|
|
|
189
248
|
messageGuid,
|
|
190
249
|
Reaction
|
|
191
250
|
} from "@photon-ai/advanced-imessage";
|
|
251
|
+
|
|
252
|
+
// src/utils/audio.ts
|
|
253
|
+
import { spawn } from "child_process";
|
|
254
|
+
import { mkdtemp as mkdtemp2, readFile, rm as rm2, writeFile as writeFile2 } from "fs/promises";
|
|
255
|
+
import { tmpdir as tmpdir2 } from "os";
|
|
256
|
+
import { join as join2 } from "path";
|
|
257
|
+
var M4A_BRANDS = /* @__PURE__ */ new Set([
|
|
258
|
+
"M4A ",
|
|
259
|
+
"M4B ",
|
|
260
|
+
"M4P ",
|
|
261
|
+
"mp42",
|
|
262
|
+
"mp41",
|
|
263
|
+
"isom",
|
|
264
|
+
"iso2"
|
|
265
|
+
]);
|
|
266
|
+
var M4A_MIME_TYPES = /* @__PURE__ */ new Set([
|
|
267
|
+
"audio/mp4",
|
|
268
|
+
"audio/mp4a-latm",
|
|
269
|
+
"audio/x-m4a",
|
|
270
|
+
"audio/aac",
|
|
271
|
+
"audio/aacp"
|
|
272
|
+
]);
|
|
273
|
+
var FFMPEG_MISSING_MESSAGE = "voice content: input is not m4a/aac and ffmpeg is unavailable. Install `ffmpeg-static` or ensure `ffmpeg` is on PATH.";
|
|
274
|
+
var isM4a = (buffer) => {
|
|
275
|
+
if (buffer.length < 12) {
|
|
276
|
+
return false;
|
|
277
|
+
}
|
|
278
|
+
if (buffer.toString("ascii", 4, 8) !== "ftyp") {
|
|
279
|
+
return false;
|
|
280
|
+
}
|
|
281
|
+
return M4A_BRANDS.has(buffer.toString("ascii", 8, 12));
|
|
282
|
+
};
|
|
283
|
+
var isM4aMimeType = (mimeType) => M4A_MIME_TYPES.has(mimeType.toLowerCase());
|
|
284
|
+
var cachedFfmpegPath;
|
|
285
|
+
var tryStaticBinary = async () => {
|
|
286
|
+
try {
|
|
287
|
+
const mod = await import("ffmpeg-static");
|
|
288
|
+
return mod.default ?? void 0;
|
|
289
|
+
} catch {
|
|
290
|
+
return void 0;
|
|
291
|
+
}
|
|
292
|
+
};
|
|
293
|
+
var resolveFfmpegPath = async () => {
|
|
294
|
+
if (cachedFfmpegPath) {
|
|
295
|
+
return cachedFfmpegPath;
|
|
296
|
+
}
|
|
297
|
+
cachedFfmpegPath = await tryStaticBinary() ?? "ffmpeg";
|
|
298
|
+
return cachedFfmpegPath;
|
|
299
|
+
};
|
|
300
|
+
var collectStream = (stream2) => {
|
|
301
|
+
if (!stream2) {
|
|
302
|
+
return Promise.resolve("");
|
|
303
|
+
}
|
|
304
|
+
return new Promise((resolve, reject) => {
|
|
305
|
+
const chunks = [];
|
|
306
|
+
stream2.on("data", (chunk) => {
|
|
307
|
+
chunks.push(typeof chunk === "string" ? Buffer.from(chunk) : chunk);
|
|
308
|
+
});
|
|
309
|
+
stream2.on("end", () => resolve(Buffer.concat(chunks).toString("utf8")));
|
|
310
|
+
stream2.on("error", reject);
|
|
311
|
+
});
|
|
312
|
+
};
|
|
313
|
+
var isMissingBinaryError = (err) => err?.code === "ENOENT";
|
|
314
|
+
var runFfmpeg = (ffmpegPath, args) => {
|
|
315
|
+
const proc = spawn(ffmpegPath, args, { stdio: ["ignore", "ignore", "pipe"] });
|
|
316
|
+
const stderr = collectStream(proc.stderr);
|
|
317
|
+
const exit = new Promise((resolve, reject) => {
|
|
318
|
+
proc.on(
|
|
319
|
+
"error",
|
|
320
|
+
(err) => reject(
|
|
321
|
+
isMissingBinaryError(err) ? new Error(FFMPEG_MISSING_MESSAGE) : err
|
|
322
|
+
)
|
|
323
|
+
);
|
|
324
|
+
proc.on("exit", (code) => resolve(code ?? -1));
|
|
325
|
+
});
|
|
326
|
+
return Promise.all([exit, stderr]).then(([code, text]) => ({
|
|
327
|
+
code,
|
|
328
|
+
stderr: text
|
|
329
|
+
}));
|
|
330
|
+
};
|
|
331
|
+
var DURATION_PATTERN = /Duration:\s*(\d+):(\d{2}):(\d{2})(?:\.(\d{1,3}))?/;
|
|
332
|
+
var parseDuration = (stderr) => {
|
|
333
|
+
const match = stderr.match(DURATION_PATTERN);
|
|
334
|
+
if (!match) {
|
|
335
|
+
return void 0;
|
|
336
|
+
}
|
|
337
|
+
const [, hh, mm, ss, frac] = match;
|
|
338
|
+
const seconds = Number(hh) * 3600 + Number(mm) * 60 + Number(ss) + Number(`0.${frac ?? 0}`);
|
|
339
|
+
return Number.isFinite(seconds) ? seconds : void 0;
|
|
340
|
+
};
|
|
341
|
+
var transcodeToM4a = async (buffer) => {
|
|
342
|
+
const ffmpeg = await resolveFfmpegPath();
|
|
343
|
+
const dir = await mkdtemp2(join2(tmpdir2(), "spectrum-voice-"));
|
|
344
|
+
const inPath = join2(dir, "in");
|
|
345
|
+
const outPath = join2(dir, "out.m4a");
|
|
346
|
+
try {
|
|
347
|
+
await writeFile2(inPath, buffer);
|
|
348
|
+
const { code, stderr } = await runFfmpeg(ffmpeg, [
|
|
349
|
+
"-y",
|
|
350
|
+
"-i",
|
|
351
|
+
inPath,
|
|
352
|
+
"-f",
|
|
353
|
+
"ipod",
|
|
354
|
+
"-c:a",
|
|
355
|
+
"aac",
|
|
356
|
+
outPath
|
|
357
|
+
]);
|
|
358
|
+
if (code !== 0) {
|
|
359
|
+
throw new Error(`ffmpeg conversion failed (exit ${code}): ${stderr}`);
|
|
360
|
+
}
|
|
361
|
+
const out = await readFile(outPath);
|
|
362
|
+
return { buffer: out, duration: parseDuration(stderr) };
|
|
363
|
+
} finally {
|
|
364
|
+
await rm2(dir, { recursive: true, force: true }).catch(() => {
|
|
365
|
+
});
|
|
366
|
+
}
|
|
367
|
+
};
|
|
368
|
+
var ensureM4a = async (buffer, mimeType) => {
|
|
369
|
+
if (isM4aMimeType(mimeType) || isM4a(buffer)) {
|
|
370
|
+
return { buffer };
|
|
371
|
+
}
|
|
372
|
+
return transcodeToM4a(buffer);
|
|
373
|
+
};
|
|
374
|
+
|
|
375
|
+
// src/providers/imessage/remote.ts
|
|
376
|
+
var toSendResult2 = (receipt) => ({
|
|
377
|
+
id: receipt.guid,
|
|
378
|
+
timestamp: /* @__PURE__ */ new Date()
|
|
379
|
+
});
|
|
380
|
+
var VCARD_MIME_TYPES2 = /* @__PURE__ */ new Set([
|
|
381
|
+
"text/vcard",
|
|
382
|
+
"text/x-vcard",
|
|
383
|
+
"text/directory",
|
|
384
|
+
"application/vcard",
|
|
385
|
+
"application/x-vcard"
|
|
386
|
+
]);
|
|
387
|
+
var isVCardAttachment2 = (mimeType, fileName) => {
|
|
388
|
+
if (mimeType && VCARD_MIME_TYPES2.has(mimeType.toLowerCase())) {
|
|
389
|
+
return true;
|
|
390
|
+
}
|
|
391
|
+
return Boolean(fileName?.toLowerCase().endsWith(".vcf"));
|
|
392
|
+
};
|
|
192
393
|
var TAPBACK_NAMES = new Set(
|
|
193
394
|
Object.values(Reaction).filter((r) => r !== "emoji" && r !== "sticker")
|
|
194
395
|
);
|
|
@@ -200,21 +401,32 @@ var baseMessage = (event) => ({
|
|
|
200
401
|
},
|
|
201
402
|
timestamp: event.timestamp
|
|
202
403
|
});
|
|
203
|
-
var
|
|
404
|
+
var toAttachmentContent2 = (client, info) => asAttachment({
|
|
405
|
+
name: info.fileName,
|
|
406
|
+
mimeType: info.mimeType,
|
|
407
|
+
size: info.totalBytes,
|
|
408
|
+
read: async () => Buffer.from(await client.attachments.downloadBuffer(info.guid)),
|
|
409
|
+
stream: async () => client.attachments.download(info.guid).stream
|
|
410
|
+
});
|
|
411
|
+
var toVCardContent2 = async (client, info) => {
|
|
412
|
+
try {
|
|
413
|
+
const buf = Buffer.from(await client.attachments.downloadBuffer(info.guid));
|
|
414
|
+
return asContact(fromVCard(buf.toString("utf8")));
|
|
415
|
+
} catch {
|
|
416
|
+
return toAttachmentContent2(client, info);
|
|
417
|
+
}
|
|
418
|
+
};
|
|
419
|
+
var toMessages2 = async (client, event) => {
|
|
204
420
|
const base = baseMessage(event);
|
|
205
421
|
const messageGuidStr = event.message.guid;
|
|
206
422
|
if (event.message.attachments.length > 0) {
|
|
207
|
-
return
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
read: async () => Buffer.from(await client.attachments.downloadBuffer(info.guid)),
|
|
215
|
-
stream: async () => client.attachments.download(info.guid).stream
|
|
216
|
-
})
|
|
217
|
-
}));
|
|
423
|
+
return Promise.all(
|
|
424
|
+
event.message.attachments.map(async (info) => ({
|
|
425
|
+
...base,
|
|
426
|
+
id: `${messageGuidStr}:${info.guid}`,
|
|
427
|
+
content: isVCardAttachment2(info.mimeType, info.fileName) ? await toVCardContent2(client, info) : toAttachmentContent2(client, info)
|
|
428
|
+
}))
|
|
429
|
+
);
|
|
218
430
|
}
|
|
219
431
|
const text = event.message.text;
|
|
220
432
|
return [
|
|
@@ -231,7 +443,10 @@ var clientStream = (client) => {
|
|
|
231
443
|
(async () => {
|
|
232
444
|
try {
|
|
233
445
|
for await (const event of sub) {
|
|
234
|
-
|
|
446
|
+
if (event.message.isFromMe) {
|
|
447
|
+
continue;
|
|
448
|
+
}
|
|
449
|
+
for (const message of await toMessages2(client, event)) {
|
|
235
450
|
emit(message);
|
|
236
451
|
}
|
|
237
452
|
}
|
|
@@ -243,6 +458,20 @@ var clientStream = (client) => {
|
|
|
243
458
|
return () => sub.close();
|
|
244
459
|
});
|
|
245
460
|
};
|
|
461
|
+
var sendVCardAttachment = (remote, name, vcf) => remote.attachments.upload({
|
|
462
|
+
data: Buffer.from(vcf, "utf8"),
|
|
463
|
+
fileName: name,
|
|
464
|
+
mimeType: "text/vcard"
|
|
465
|
+
});
|
|
466
|
+
var vcardFileName2 = (contact) => {
|
|
467
|
+
const base = contact.name?.formatted ?? contact.user?.id ?? "contact";
|
|
468
|
+
return `${base.replace(/[^a-zA-Z0-9_\-.]/g, "_")}.vcf`;
|
|
469
|
+
};
|
|
470
|
+
var sendContactAttachment = async (remote, content) => {
|
|
471
|
+
const vcf = await toVCard(content);
|
|
472
|
+
const upload = await sendVCardAttachment(remote, vcardFileName2(content), vcf);
|
|
473
|
+
return upload.guid;
|
|
474
|
+
};
|
|
246
475
|
var messages2 = (clients) => mergeStreams(clients.map(clientStream));
|
|
247
476
|
var startTyping = async (clients, spaceId) => {
|
|
248
477
|
const remote = clients[0];
|
|
@@ -261,22 +490,44 @@ var stopTyping = async (clients, spaceId) => {
|
|
|
261
490
|
var send2 = async (clients, spaceId, content) => {
|
|
262
491
|
const remote = clients[0];
|
|
263
492
|
if (!remote) {
|
|
264
|
-
|
|
493
|
+
throw new Error("No remote iMessage client available");
|
|
265
494
|
}
|
|
495
|
+
const chat = chatGuid(spaceId);
|
|
266
496
|
switch (content.type) {
|
|
267
497
|
case "text":
|
|
268
|
-
await remote.messages.send(
|
|
269
|
-
break;
|
|
498
|
+
return toSendResult2(await remote.messages.send(chat, content.text));
|
|
270
499
|
case "attachment": {
|
|
271
500
|
const attachment = await remote.attachments.upload({
|
|
272
501
|
data: await content.read(),
|
|
273
502
|
fileName: content.name,
|
|
274
503
|
mimeType: content.mimeType
|
|
275
504
|
});
|
|
276
|
-
|
|
277
|
-
|
|
505
|
+
return toSendResult2(
|
|
506
|
+
await remote.messages.send(chat, "", {
|
|
507
|
+
attachment: attachment.guid
|
|
508
|
+
})
|
|
509
|
+
);
|
|
510
|
+
}
|
|
511
|
+
case "contact": {
|
|
512
|
+
const attachment = await sendContactAttachment(remote, content);
|
|
513
|
+
return toSendResult2(await remote.messages.send(chat, "", { attachment }));
|
|
514
|
+
}
|
|
515
|
+
case "voice": {
|
|
516
|
+
const { buffer } = await ensureM4a(
|
|
517
|
+
await content.read(),
|
|
518
|
+
content.mimeType
|
|
519
|
+
);
|
|
520
|
+
const attachment = await remote.attachments.upload({
|
|
521
|
+
data: buffer,
|
|
522
|
+
fileName: content.name ?? "voice.m4a",
|
|
523
|
+
mimeType: "audio/x-m4a"
|
|
278
524
|
});
|
|
279
|
-
|
|
525
|
+
return toSendResult2(
|
|
526
|
+
await remote.messages.send(chat, "", {
|
|
527
|
+
attachment: attachment.guid,
|
|
528
|
+
audioMessage: true
|
|
529
|
+
})
|
|
530
|
+
);
|
|
280
531
|
}
|
|
281
532
|
default:
|
|
282
533
|
throw new Error(`Unsupported iMessage content type: ${content.type}`);
|
|
@@ -285,30 +536,70 @@ var send2 = async (clients, spaceId, content) => {
|
|
|
285
536
|
var replyToMessage = async (clients, spaceId, msgId, content) => {
|
|
286
537
|
const remote = clients[0];
|
|
287
538
|
if (!remote) {
|
|
288
|
-
|
|
539
|
+
throw new Error("No remote iMessage client available");
|
|
289
540
|
}
|
|
290
541
|
const chat = chatGuid(spaceId);
|
|
291
542
|
const replyTo = messageGuid(msgId);
|
|
292
543
|
switch (content.type) {
|
|
293
544
|
case "text":
|
|
294
|
-
|
|
295
|
-
|
|
545
|
+
return toSendResult2(
|
|
546
|
+
await remote.messages.send(chat, content.text, { replyTo })
|
|
547
|
+
);
|
|
296
548
|
case "attachment": {
|
|
297
549
|
const attachment = await remote.attachments.upload({
|
|
298
550
|
data: await content.read(),
|
|
299
551
|
fileName: content.name,
|
|
300
552
|
mimeType: content.mimeType
|
|
301
553
|
});
|
|
302
|
-
|
|
303
|
-
|
|
304
|
-
|
|
554
|
+
return toSendResult2(
|
|
555
|
+
await remote.messages.send(chat, "", {
|
|
556
|
+
attachment: attachment.guid,
|
|
557
|
+
replyTo
|
|
558
|
+
})
|
|
559
|
+
);
|
|
560
|
+
}
|
|
561
|
+
case "contact": {
|
|
562
|
+
const attachment = await sendContactAttachment(remote, content);
|
|
563
|
+
return toSendResult2(
|
|
564
|
+
await remote.messages.send(chat, "", { attachment, replyTo })
|
|
565
|
+
);
|
|
566
|
+
}
|
|
567
|
+
case "voice": {
|
|
568
|
+
const { buffer } = await ensureM4a(
|
|
569
|
+
await content.read(),
|
|
570
|
+
content.mimeType
|
|
571
|
+
);
|
|
572
|
+
const attachment = await remote.attachments.upload({
|
|
573
|
+
data: buffer,
|
|
574
|
+
fileName: content.name ?? "voice.m4a",
|
|
575
|
+
mimeType: "audio/x-m4a"
|
|
305
576
|
});
|
|
306
|
-
|
|
577
|
+
return toSendResult2(
|
|
578
|
+
await remote.messages.send(chat, "", {
|
|
579
|
+
attachment: attachment.guid,
|
|
580
|
+
audioMessage: true,
|
|
581
|
+
replyTo
|
|
582
|
+
})
|
|
583
|
+
);
|
|
307
584
|
}
|
|
308
585
|
default:
|
|
309
586
|
throw new Error(`Unsupported iMessage content type: ${content.type}`);
|
|
310
587
|
}
|
|
311
588
|
};
|
|
589
|
+
var editMessage = async (clients, spaceId, msgId, content) => {
|
|
590
|
+
if (content.type !== "text") {
|
|
591
|
+
throw new Error("iMessage only supports editing text content");
|
|
592
|
+
}
|
|
593
|
+
const remote = clients[0];
|
|
594
|
+
if (!remote) {
|
|
595
|
+
throw new Error("No remote iMessage client available");
|
|
596
|
+
}
|
|
597
|
+
await remote.messages.edit(
|
|
598
|
+
chatGuid(spaceId),
|
|
599
|
+
messageGuid(msgId),
|
|
600
|
+
content.text
|
|
601
|
+
);
|
|
602
|
+
};
|
|
312
603
|
var reactToMessage = async (clients, spaceId, msgId, reaction) => {
|
|
313
604
|
const remote = clients[0];
|
|
314
605
|
if (!remote) {
|
|
@@ -420,10 +711,9 @@ var imessage = definePlatform("iMessage", {
|
|
|
420
711
|
actions: {
|
|
421
712
|
send: async ({ space, content, client }) => {
|
|
422
713
|
if (isLocal(client)) {
|
|
423
|
-
await send(client, space.id, content);
|
|
424
|
-
} else {
|
|
425
|
-
await send2(client, space.id, content);
|
|
714
|
+
return await send(client, space.id, content);
|
|
426
715
|
}
|
|
716
|
+
return await send2(client, space.id, content);
|
|
427
717
|
},
|
|
428
718
|
startTyping: async ({ space, client }) => {
|
|
429
719
|
if (isLocal(client)) {
|
|
@@ -445,9 +735,19 @@ var imessage = definePlatform("iMessage", {
|
|
|
445
735
|
},
|
|
446
736
|
replyToMessage: async ({ space, messageId, content, client }) => {
|
|
447
737
|
if (isLocal(client)) {
|
|
448
|
-
|
|
738
|
+
throw new Error(
|
|
739
|
+
"iMessage local mode does not support replying to messages"
|
|
740
|
+
);
|
|
741
|
+
}
|
|
742
|
+
return await replyToMessage(client, space.id, messageId, content);
|
|
743
|
+
},
|
|
744
|
+
editMessage: async ({ space, messageId, content, client }) => {
|
|
745
|
+
if (isLocal(client)) {
|
|
746
|
+
throw new Error(
|
|
747
|
+
"iMessage local mode does not support editing messages"
|
|
748
|
+
);
|
|
449
749
|
}
|
|
450
|
-
await
|
|
750
|
+
await editMessage(client, space.id, messageId, content);
|
|
451
751
|
}
|
|
452
752
|
}
|
|
453
753
|
});
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { d as Platform, c as PlatformDef, P as ProviderMessage } from '../../types-
|
|
1
|
+
import { d as Platform, c as PlatformDef, P as ProviderMessage } from '../../types-DZMHfgYQ.js';
|
|
2
2
|
import * as node_readline from 'node:readline';
|
|
3
3
|
import z__default from 'zod';
|
|
4
4
|
import 'hotscript';
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import {
|
|
2
2
|
definePlatform
|
|
3
|
-
} from "../../chunk-
|
|
3
|
+
} from "../../chunk-XZTTLPHE.js";
|
|
4
4
|
|
|
5
5
|
// src/providers/terminal/index.ts
|
|
6
6
|
import { createInterface } from "readline";
|
|
@@ -49,9 +49,13 @@ var terminal = definePlatform("terminal", {
|
|
|
49
49
|
},
|
|
50
50
|
actions: {
|
|
51
51
|
send: async ({ content }) => {
|
|
52
|
-
if (content.type
|
|
53
|
-
|
|
52
|
+
if (content.type !== "text") {
|
|
53
|
+
throw new Error(
|
|
54
|
+
`Terminal provider only supports text content, got "${content.type}"`
|
|
55
|
+
);
|
|
54
56
|
}
|
|
57
|
+
console.log(content.text);
|
|
58
|
+
return { id: crypto.randomUUID(), timestamp: /* @__PURE__ */ new Date() };
|
|
55
59
|
}
|
|
56
60
|
}
|
|
57
61
|
});
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { M as ManagedStream } from '../../stream-DGy4geUK.js';
|
|
2
2
|
import * as z from 'zod';
|
|
3
3
|
import z__default from 'zod';
|
|
4
|
-
import { l as SchemaMessage, d as Platform, c as PlatformDef, P as ProviderMessage } from '../../types-
|
|
4
|
+
import { l as SchemaMessage, d as Platform, c as PlatformDef, P as ProviderMessage } from '../../types-DZMHfgYQ.js';
|
|
5
5
|
import * as zod_v4_core from 'zod/v4/core';
|
|
6
6
|
import { WhatsAppClient } from '@photon-ai/whatsapp-business';
|
|
7
7
|
import 'hotscript';
|