@spectrum-ts/whatsapp-business 10.0.0 → 11.1.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 +97 -19
- package/package.json +2 -2
package/dist/index.js
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { TypedEventStream, button, buttons, createClient, list } from "@photon-ai/whatsapp-business";
|
|
2
2
|
import { UnsupportedError, cloud, definePlatform, mergeStreams, stream } from "@spectrum-ts/core";
|
|
3
|
-
import { asAttachment, asContact, asCustom, asPollOption, asReaction, asText, createLogger, errorAttrs, tracedFetch } from "@spectrum-ts/core/authoring";
|
|
3
|
+
import { asAttachment, asContact, asCustom, asGroup, asPollOption, asReaction, asText, asUnsend, createLogger, errorAttrs, tracedFetch } from "@spectrum-ts/core/authoring";
|
|
4
4
|
import { extension } from "mime-types";
|
|
5
5
|
import z from "zod";
|
|
6
6
|
//#region src/auth.ts
|
|
@@ -296,6 +296,31 @@ const cachePoll = (client, messageId, poll) => {
|
|
|
296
296
|
if (first !== void 0) cache.delete(first);
|
|
297
297
|
}
|
|
298
298
|
};
|
|
299
|
+
const MAX_REACTION_CACHE_SIZE = 1e3;
|
|
300
|
+
const reactionCaches = /* @__PURE__ */ new WeakMap();
|
|
301
|
+
const reactionCacheKey = (reactedId, from) => `${reactedId}:${from}`;
|
|
302
|
+
const cacheReaction = (client, reactedId, from, entry) => {
|
|
303
|
+
let cache = reactionCaches.get(client);
|
|
304
|
+
if (!cache) {
|
|
305
|
+
cache = /* @__PURE__ */ new Map();
|
|
306
|
+
reactionCaches.set(client, cache);
|
|
307
|
+
}
|
|
308
|
+
const key = reactionCacheKey(reactedId, from);
|
|
309
|
+
cache.delete(key);
|
|
310
|
+
cache.set(key, entry);
|
|
311
|
+
if (cache.size > MAX_REACTION_CACHE_SIZE) {
|
|
312
|
+
const first = cache.keys().next().value;
|
|
313
|
+
if (first !== void 0) cache.delete(first);
|
|
314
|
+
}
|
|
315
|
+
};
|
|
316
|
+
const getCachedReaction = (client, reactedId, from) => reactionCaches.get(client)?.get(reactionCacheKey(reactedId, from));
|
|
317
|
+
const reactionTargetStub = (reactedId) => ({
|
|
318
|
+
id: reactedId,
|
|
319
|
+
content: asCustom({
|
|
320
|
+
whatsapp_type: "reaction-target",
|
|
321
|
+
stub: true
|
|
322
|
+
})
|
|
323
|
+
});
|
|
299
324
|
const optionIndexFromId = (id) => {
|
|
300
325
|
if (!id.startsWith(OPTION_ID_PREFIX)) return;
|
|
301
326
|
const index = Number(id.slice(4));
|
|
@@ -367,6 +392,14 @@ const waContactToSpectrum = (card) => {
|
|
|
367
392
|
if (card.birthday) input.birthday = card.birthday;
|
|
368
393
|
return asContact(input);
|
|
369
394
|
};
|
|
395
|
+
const groupItem = (msg, index, content) => ({
|
|
396
|
+
id: `${msg.id}:${index}`,
|
|
397
|
+
content,
|
|
398
|
+
sender: { id: msg.from },
|
|
399
|
+
space: { id: msg.from },
|
|
400
|
+
timestamp: msg.timestamp
|
|
401
|
+
});
|
|
402
|
+
const parentWamid = (id) => id.split(":")[0] ?? id;
|
|
370
403
|
const toMessages = (client, msg) => {
|
|
371
404
|
const base = {
|
|
372
405
|
sender: { id: msg.from },
|
|
@@ -387,6 +420,34 @@ const toMessages = (client, msg) => {
|
|
|
387
420
|
content: mapContent(client, msg)
|
|
388
421
|
}];
|
|
389
422
|
};
|
|
423
|
+
const mapReactionContent = (client, msg, reaction) => {
|
|
424
|
+
const reactedId = reaction.messageId;
|
|
425
|
+
if (!reaction.emoji) {
|
|
426
|
+
const cached = getCachedReaction(client, reactedId, msg.from);
|
|
427
|
+
return asUnsend({ target: cached ? {
|
|
428
|
+
id: cached.id,
|
|
429
|
+
content: asReaction({
|
|
430
|
+
emoji: cached.emoji,
|
|
431
|
+
target: reactionTargetStub(reactedId)
|
|
432
|
+
})
|
|
433
|
+
} : {
|
|
434
|
+
id: `${reactedId}:reaction:${msg.from}`,
|
|
435
|
+
content: asCustom({
|
|
436
|
+
whatsapp_type: "reaction-removed",
|
|
437
|
+
messageId: reactedId,
|
|
438
|
+
stub: true
|
|
439
|
+
})
|
|
440
|
+
} });
|
|
441
|
+
}
|
|
442
|
+
cacheReaction(client, reactedId, msg.from, {
|
|
443
|
+
id: msg.id,
|
|
444
|
+
emoji: reaction.emoji
|
|
445
|
+
});
|
|
446
|
+
return asReaction({
|
|
447
|
+
emoji: reaction.emoji,
|
|
448
|
+
target: reactionTargetStub(reactedId)
|
|
449
|
+
});
|
|
450
|
+
};
|
|
390
451
|
const mapContent = (client, msg) => {
|
|
391
452
|
const { content } = msg;
|
|
392
453
|
switch (content.type) {
|
|
@@ -394,7 +455,12 @@ const mapContent = (client, msg) => {
|
|
|
394
455
|
case "image":
|
|
395
456
|
case "video":
|
|
396
457
|
case "audio":
|
|
397
|
-
case "document":
|
|
458
|
+
case "document": {
|
|
459
|
+
const media = lazyMedia(client, content.media);
|
|
460
|
+
const caption = content.media.caption?.trim();
|
|
461
|
+
if (!caption) return media;
|
|
462
|
+
return asGroup({ items: [groupItem(msg, 0, media), groupItem(msg, 1, asText(caption))] });
|
|
463
|
+
}
|
|
398
464
|
case "sticker": return asCustom({
|
|
399
465
|
whatsapp_type: "sticker",
|
|
400
466
|
...content.sticker
|
|
@@ -403,19 +469,7 @@ const mapContent = (client, msg) => {
|
|
|
403
469
|
whatsapp_type: "location",
|
|
404
470
|
...content.location
|
|
405
471
|
});
|
|
406
|
-
case "reaction":
|
|
407
|
-
const stubTarget = {
|
|
408
|
-
id: content.reaction.messageId,
|
|
409
|
-
content: asCustom({
|
|
410
|
-
whatsapp_type: "reaction-target",
|
|
411
|
-
stub: true
|
|
412
|
-
})
|
|
413
|
-
};
|
|
414
|
-
return asReaction({
|
|
415
|
-
emoji: content.reaction.emoji,
|
|
416
|
-
target: stubTarget
|
|
417
|
-
});
|
|
418
|
-
}
|
|
472
|
+
case "reaction": return mapReactionContent(client, msg, content.reaction);
|
|
419
473
|
case "interactive": {
|
|
420
474
|
const inter = content.interactive;
|
|
421
475
|
if (inter.type === "button_reply" || inter.type === "list_reply") {
|
|
@@ -547,7 +601,19 @@ const clientStream = (client) => {
|
|
|
547
601
|
return stream((emit, end) => {
|
|
548
602
|
const pump = (async () => {
|
|
549
603
|
try {
|
|
550
|
-
for await (const event of eventStream)
|
|
604
|
+
for await (const event of eventStream) {
|
|
605
|
+
let mapped;
|
|
606
|
+
try {
|
|
607
|
+
mapped = toMessages(client, event.message);
|
|
608
|
+
} catch (error) {
|
|
609
|
+
streamLog.warn("skipping unmappable whatsapp message event", {
|
|
610
|
+
"spectrum.whatsapp.message_id": event.message.id,
|
|
611
|
+
...errorAttrs(error)
|
|
612
|
+
}, error instanceof Error ? error : void 0);
|
|
613
|
+
continue;
|
|
614
|
+
}
|
|
615
|
+
for (const m of mapped) await emit(m);
|
|
616
|
+
}
|
|
551
617
|
end();
|
|
552
618
|
} catch (e) {
|
|
553
619
|
end(e);
|
|
@@ -561,11 +627,23 @@ const clientStream = (client) => {
|
|
|
561
627
|
};
|
|
562
628
|
const messages = (clients) => mergeStreams(clients.map(clientStream));
|
|
563
629
|
const send = async (clients, spaceId, content) => {
|
|
564
|
-
if (content.type === "reply") return await replyToMessage(clients, spaceId, content.target.id, content.content);
|
|
630
|
+
if (content.type === "reply") return await replyToMessage(clients, spaceId, parentWamid(content.target.id), content.content);
|
|
565
631
|
if (content.type === "reaction") return await reactToMessage(clients, spaceId, content);
|
|
566
632
|
if (content.type === "typing") return;
|
|
567
633
|
if (content.type === "read") {
|
|
568
|
-
await primary(clients).messages.markRead(content.target.id);
|
|
634
|
+
await primary(clients).messages.markRead(parentWamid(content.target.id));
|
|
635
|
+
return;
|
|
636
|
+
}
|
|
637
|
+
if (content.type === "unsend") {
|
|
638
|
+
const unsent = content.target.content;
|
|
639
|
+
if (unsent.type !== "reaction") throw UnsupportedError.content(content.type);
|
|
640
|
+
await primary(clients).messages.send({
|
|
641
|
+
to: spaceId,
|
|
642
|
+
reaction: {
|
|
643
|
+
messageId: parentWamid(unsent.target.id),
|
|
644
|
+
emoji: ""
|
|
645
|
+
}
|
|
646
|
+
});
|
|
569
647
|
return;
|
|
570
648
|
}
|
|
571
649
|
const client = primary(clients);
|
|
@@ -624,7 +702,7 @@ const reactToMessage = async (clients, spaceId, content) => {
|
|
|
624
702
|
return toRecord(await primary(clients).messages.send({
|
|
625
703
|
to: spaceId,
|
|
626
704
|
reaction: {
|
|
627
|
-
messageId: content.target.id,
|
|
705
|
+
messageId: parentWamid(content.target.id),
|
|
628
706
|
emoji: content.emoji
|
|
629
707
|
}
|
|
630
708
|
}), spaceId, content);
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@spectrum-ts/whatsapp-business",
|
|
3
|
-
"version": "
|
|
3
|
+
"version": "11.1.0",
|
|
4
4
|
"description": "WhatsApp Business provider for spectrum-ts.",
|
|
5
5
|
"repository": {
|
|
6
6
|
"type": "git",
|
|
@@ -36,7 +36,7 @@
|
|
|
36
36
|
"zod": "^4.2.1"
|
|
37
37
|
},
|
|
38
38
|
"peerDependencies": {
|
|
39
|
-
"@spectrum-ts/core": "^
|
|
39
|
+
"@spectrum-ts/core": "^11.0.0",
|
|
40
40
|
"typescript": "^5 || ^6.0.0"
|
|
41
41
|
},
|
|
42
42
|
"license": "MIT"
|