@spectrum-ts/whatsapp-business 11.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.
Files changed (2) hide show
  1. package/dist/index.js +80 -15
  2. package/package.json +1 -1
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, asGroup, 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));
@@ -395,6 +420,34 @@ const toMessages = (client, msg) => {
395
420
  content: mapContent(client, msg)
396
421
  }];
397
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
+ };
398
451
  const mapContent = (client, msg) => {
399
452
  const { content } = msg;
400
453
  switch (content.type) {
@@ -416,19 +469,7 @@ const mapContent = (client, msg) => {
416
469
  whatsapp_type: "location",
417
470
  ...content.location
418
471
  });
419
- case "reaction": {
420
- const stubTarget = {
421
- id: content.reaction.messageId,
422
- content: asCustom({
423
- whatsapp_type: "reaction-target",
424
- stub: true
425
- })
426
- };
427
- return asReaction({
428
- emoji: content.reaction.emoji,
429
- target: stubTarget
430
- });
431
- }
472
+ case "reaction": return mapReactionContent(client, msg, content.reaction);
432
473
  case "interactive": {
433
474
  const inter = content.interactive;
434
475
  if (inter.type === "button_reply" || inter.type === "list_reply") {
@@ -560,7 +601,19 @@ const clientStream = (client) => {
560
601
  return stream((emit, end) => {
561
602
  const pump = (async () => {
562
603
  try {
563
- for await (const event of eventStream) for (const m of toMessages(client, event.message)) await emit(m);
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
+ }
564
617
  end();
565
618
  } catch (e) {
566
619
  end(e);
@@ -581,6 +634,18 @@ const send = async (clients, spaceId, content) => {
581
634
  await primary(clients).messages.markRead(parentWamid(content.target.id));
582
635
  return;
583
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
+ });
647
+ return;
648
+ }
584
649
  const client = primary(clients);
585
650
  switch (content.type) {
586
651
  case "text": return toRecord(await client.messages.send({
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@spectrum-ts/whatsapp-business",
3
- "version": "11.0.0",
3
+ "version": "11.1.0",
4
4
  "description": "WhatsApp Business provider for spectrum-ts.",
5
5
  "repository": {
6
6
  "type": "git",