@manybot/manybot 5.6.1 → 5.7.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.
@@ -22,7 +22,7 @@
22
22
  * Returns/dispatches are pure adapters — error semantics, retries, fallbacks
23
23
  * all live elsewhere (sendFallbackGuard, sendGuard, pluginGuard).
24
24
  */
25
- import { normalizeMessageContent, downloadMediaMessage, jidNormalizedUser, } from "@whiskeysockets/baileys";
25
+ import { normalizeMessageContent, downloadMediaMessage, jidNormalizedUser, decryptPollVote as baileysDecryptPollVote, getAggregateVotesInPollMessage, } from "@whiskeysockets/baileys";
26
26
  import { createHash } from "node:crypto";
27
27
  import { logger } from "#logger";
28
28
  /**
@@ -201,6 +201,44 @@ export function createBaileysAdapter(initial) {
201
201
  lid: c.lid,
202
202
  };
203
203
  }
204
+ /**
205
+ * Compute every plausible JID candidate for a side of a poll-vote
206
+ * decryption. WhatsApp doesn't consistently pick the same JID shape
207
+ * (LID vs PN) when deriving the poll-vote decryption key — it depends
208
+ * on addressingMode, 1:1 vs group, and which side sent last. Trying
209
+ * to compute "the" correct JID up front causes AES-GCM auth failures
210
+ * whenever WhatsApp actually used the LID; brute-forcing candidates
211
+ * is the only reliable approach (see
212
+ * https://github.com/WhiskeySockets/Baileys/issues/2342 and #1678).
213
+ *
214
+ * For the bot's own side (`self === true`), candidates are the bot's
215
+ * `user.id` and `user.lid`. For an external side, candidates are
216
+ * the participant/remoteJid, the `participantPn` if any, and any
217
+ * LID→PN mapping the store has learned.
218
+ */
219
+ function jidCandidatesFromKey(key, sock, store, self) {
220
+ const cands = [];
221
+ if (self) {
222
+ const selfLid = sock.user?.lid;
223
+ if (selfLid)
224
+ cands.push(jidNormalizedUser(selfLid));
225
+ if (sock.user?.id)
226
+ cands.push(jidNormalizedUser(sock.user.id));
227
+ }
228
+ else {
229
+ const rawParticipant = key.participant ?? key.remoteJid;
230
+ if (rawParticipant)
231
+ cands.push(jidNormalizedUser(rawParticipant));
232
+ if (key.participantPn)
233
+ cands.push(jidNormalizedUser(key.participantPn));
234
+ if (rawParticipant?.endsWith("@lid")) {
235
+ const resolved = store.resolveJid(rawParticipant);
236
+ if (resolved && resolved !== rawParticipant)
237
+ cands.push(jidNormalizedUser(resolved));
238
+ }
239
+ }
240
+ return Array.from(new Set(cands.filter(Boolean)));
241
+ }
204
242
  const subscribers = new Map();
205
243
  // Tracks the handlers each socket has registered so we can remove them on a
206
244
  // fresh socket after reconnect. Declared up here because the rebind helpers
@@ -505,6 +543,72 @@ export function createBaileysAdapter(initial) {
505
543
  return null;
506
544
  }
507
545
  },
546
+ // ── poll decryption (Baileys-only) ───────────────────────────────────
547
+ //
548
+ // These are the only two methods on the contract that are explicitly
549
+ // Baileys-specific. whatsmeow (and any future driver) may leave them
550
+ // undefined; the only consumer today is `buildPollApi` in
551
+ // drivers/baileys/api/index.ts, which already tolerates the absence.
552
+ //
553
+ // Both live on the contract (not as a separate file-level helper)
554
+ // because the Baileys-side knowledge they encode — picking the
555
+ // correct LID-vs-PN JID on each side, knowing the bot's own
556
+ // `sock.user.id/lid`, knowing the WAMessage shape for the encrypted
557
+ // payload — would otherwise leak out of the adapter.
558
+ async decryptPollVote(opts) {
559
+ const voteRaw = store.messages.get(opts.voteKey.remoteJid ?? "")?.get(opts.voteKey.id ?? "");
560
+ const pum = voteRaw?.message?.pollUpdateMessage;
561
+ const vote = pum?.vote;
562
+ if (!vote)
563
+ return null;
564
+ const encKey = Buffer.isBuffer(opts.pollEncKey)
565
+ ? opts.pollEncKey
566
+ : Buffer.from(opts.pollEncKey, "base64");
567
+ // WhatsApp doesn't consistently use the same JID shape (LID vs PN) for
568
+ // pollCreatorJid/voterJid — it depends on addressingMode, 1:1 vs group,
569
+ // and which side sent last. Compute every plausible candidate and
570
+ // brute-force combinations until one decrypts successfully (see
571
+ // https://github.com/WhiskeySockets/Baileys/issues/2342 and #1678).
572
+ const creatorCandidates = jidCandidatesFromKey(opts.pollKey, sock, store, /*self*/ false);
573
+ const voterCandidates = jidCandidatesFromKey(opts.voteKey, sock, store, !!opts.voteKey.fromMe);
574
+ for (const pollCreatorJid of creatorCandidates) {
575
+ for (const voterJid of voterCandidates) {
576
+ try {
577
+ const decrypted = baileysDecryptPollVote(vote, {
578
+ pollCreatorJid,
579
+ pollMsgId: opts.pollKey.id ?? "",
580
+ pollEncKey: encKey,
581
+ voterJid,
582
+ });
583
+ // PollVoteMessage.selectedOptions is a list of { optionHash: Buffer | null }.
584
+ // Map to plain hex strings for the contract surface.
585
+ const selectedOptions = (decrypted.selectedOptions ?? [])
586
+ .map((o) => {
587
+ const h = o?.optionHash;
588
+ if (!h)
589
+ return null;
590
+ return Buffer.isBuffer(h) ? h.toString("hex") : Buffer.from(h).toString("hex");
591
+ })
592
+ .filter((x) => !!x);
593
+ return { selectedOptions, raw: decrypted };
594
+ }
595
+ catch {
596
+ // try next JID combination
597
+ }
598
+ }
599
+ }
600
+ return null;
601
+ },
602
+ aggregatePollVotes(opts) {
603
+ const pollRaw = store.messages.get(opts.pollKey.remoteJid ?? "")?.get(opts.pollKey.id ?? "");
604
+ if (!pollRaw?.message)
605
+ return [];
606
+ const meId = opts.selfJid ?? (sock.user?.id ? jidNormalizedUser(sock.user.id) : undefined);
607
+ const aggregated = getAggregateVotesInPollMessage(
608
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
609
+ { message: pollRaw.message, pollUpdates: opts.votes }, meId);
610
+ return aggregated;
611
+ },
508
612
  };
509
613
  // ── Rebind helpers (post-reconnect in drivers/baileys/index.ts) ────────
510
614
  //
@@ -528,6 +632,26 @@ export function createBaileysAdapter(initial) {
528
632
  const updates = arg;
529
633
  emit("messages.update", { updates });
530
634
  });
635
+ // Baileys emits one of two shapes for `messages.delete`: a `keys`
636
+ // array (per-message revoke) or `{ jid, all: true }` (chat clear).
637
+ // Normalize to a single payload here so the contract's consumer
638
+ // never has to special-case the variant.
639
+ register("messages.delete", (arg) => {
640
+ const a = arg;
641
+ if ("all" in a) {
642
+ emit("messages.delete", { keys: [], all: { jid: a.jid } });
643
+ }
644
+ else {
645
+ emit("messages.delete", {
646
+ keys: a.keys.map((k) => ({
647
+ id: k.id ?? null,
648
+ remoteJid: k.remoteJid ?? null,
649
+ fromMe: k.fromMe ?? null,
650
+ participant: k.participant ?? null,
651
+ })),
652
+ });
653
+ }
654
+ });
531
655
  register("messaging-history.set", (arg) => {
532
656
  const { chats, contacts, messages } = arg;
533
657
  emit("messaging-history.set", {
@@ -542,6 +666,9 @@ export function createBaileysAdapter(initial) {
542
666
  register("chats.update", (arg) => {
543
667
  emit("chats.update", { updates: arg });
544
668
  });
669
+ register("chats.delete", (arg) => {
670
+ emit("chats.delete", { ids: arg });
671
+ });
545
672
  register("contacts.upsert", (arg) => {
546
673
  emit("contacts.upsert", { contacts: arg.map(contactSummary) });
547
674
  });
@@ -552,9 +679,30 @@ export function createBaileysAdapter(initial) {
552
679
  const { id, participants } = arg;
553
680
  emit("group-participants.update", { id, participants });
554
681
  });
682
+ register("groups.upsert", (arg) => {
683
+ const groups = arg;
684
+ emit("groups.upsert", { groups: groups.map((g) => ({ id: g.id, subject: g.subject })) });
685
+ });
555
686
  register("groups.update", (arg) => {
556
687
  emit("groups.update", { updates: arg });
557
688
  });
689
+ register("group.join-request", (arg) => {
690
+ const a = arg;
691
+ emit("group.join-request", {
692
+ id: a.id,
693
+ author: a.author,
694
+ participant: a.participant,
695
+ action: a.action,
696
+ method: a.method ?? "unknown",
697
+ });
698
+ });
699
+ register("blocklist.set", (arg) => {
700
+ emit("blocklist.set", { blocklist: arg.blocklist });
701
+ });
702
+ register("blocklist.update", (arg) => {
703
+ const a = arg;
704
+ emit("blocklist.update", { blocklist: a.blocklist, type: a.type });
705
+ });
558
706
  register("connection.update", (arg) => {
559
707
  const { connection, lastDisconnect } = arg;
560
708
  emit("connection.update", {
@@ -28,7 +28,7 @@ import { waitForSendSlot, simulateState, typingDuration, mediaDuration, waitForE
28
28
  import { sendWithFallback } from "#kernel/sendFallbackGuard.js";
29
29
  import { buildSettingsApi } from "#settingsdb";
30
30
  import WebP from "node-webpmux";
31
- import { getAggregateVotesInPollMessage, decryptPollVote, jidNormalizedUser, } from "@whiskeysockets/baileys";
31
+ import { jidNormalizedUser, } from "@whiskeysockets/baileys";
32
32
  // ── Raw-Baileys escape hatch ─────────────────────────────────────────────────
33
33
  //
34
34
  // This whole file is the Baileys driver's plugin-context builder, so the
@@ -223,11 +223,11 @@ function bindGroupMetaInvalidation(contract) {
223
223
  if (groupMetaInvalidationBound)
224
224
  return;
225
225
  groupMetaInvalidationBound = true;
226
- const sock = rawSocketOf(contract);
227
- const ev = sock.ev;
228
- ev.on("group-participants.update", (u) => groupMetaCache.delete(u.id));
229
- ev.on("groups.update", (updates) => {
230
- for (const u of updates)
226
+ contract.on("group-participants.update", (u) => {
227
+ groupMetaCache.delete(u.id);
228
+ });
229
+ contract.on("groups.update", (p) => {
230
+ for (const u of p.updates)
231
231
  if (u.id)
232
232
  groupMetaCache.delete(u.id);
233
233
  });
@@ -1321,20 +1321,58 @@ function buildSetupSendApi(contract, store) {
1321
1321
  },
1322
1322
  };
1323
1323
  }
1324
- // ── Events API ────────────────────────────────────────────────────────────────
1324
+ const WA_EVENT_NAMES = new Set([
1325
+ "messages.upsert",
1326
+ "messages.update",
1327
+ "messages.delete",
1328
+ "messaging-history.set",
1329
+ "chats.upsert",
1330
+ "chats.update",
1331
+ "chats.delete",
1332
+ "contacts.upsert",
1333
+ "contacts.update",
1334
+ "group-participants.update",
1335
+ "groups.upsert",
1336
+ "groups.update",
1337
+ "group.join-request",
1338
+ "blocklist.set",
1339
+ "blocklist.update",
1340
+ "connection.update",
1341
+ ]);
1342
+ function assertSupportedEvent(event) {
1343
+ if (!WA_EVENT_NAMES.has(event)) {
1344
+ throw new Error(`[events] unsupported event "${event}". Supported: ${[...WA_EVENT_NAMES].join(", ")}. ` +
1345
+ `If you need this event, file an issue — adding events to WaEventName is a contract change.`);
1346
+ }
1347
+ }
1325
1348
  const listenerRegistry = new Map();
1326
- export function cleanupPluginEvents(pluginName, contract) {
1327
- const sock = rawSocketOf(contract);
1349
+ export function cleanupPluginEvents(pluginName, _contract) {
1328
1350
  const list = listenerRegistry.get(pluginName);
1329
1351
  if (list) {
1330
- for (const { event, handler } of list) {
1352
+ for (const ref of list) {
1331
1353
  try {
1332
- sock.ev.off(event, handler);
1354
+ ref.detach();
1333
1355
  }
1334
1356
  catch { }
1335
1357
  }
1336
1358
  listenerRegistry.delete(pluginName);
1337
1359
  }
1360
+ // The poll-vote subscription is created in `buildPollApi` by calling
1361
+ // `contract.on(...)` directly, so it lives outside `listenerRegistry`.
1362
+ // Detach it here so the listener doesn't outlive the plugin (and a
1363
+ // reloaded plugin can re-subscribe — `buildPollApi` gates on the
1364
+ // `boundPollPlugins` map).
1365
+ const pollDetach = boundPollPlugins.get(pluginName);
1366
+ if (pollDetach) {
1367
+ try {
1368
+ pollDetach();
1369
+ }
1370
+ catch { }
1371
+ boundPollPlugins.delete(pluginName);
1372
+ }
1373
+ // Drop the per-plugin poll registry so reloading a plugin doesn't see
1374
+ // stale PollHandles from a prior instance.
1375
+ pollRegistry.delete(pluginName);
1338
1376
  cancelPlugin(pluginName);
1339
1377
  }
1340
1378
  /**
@@ -1342,21 +1380,26 @@ export function cleanupPluginEvents(pluginName, contract) {
1342
1380
  * @param {string} pluginName
1343
1381
  */
1344
1382
  function buildEventsApi(contract, pluginName) {
1345
- const sock = rawSocketOf(contract);
1346
1383
  return {
1347
1384
  on(event, handler) {
1348
- sock.ev.on(event, handler);
1385
+ assertSupportedEvent(event);
1386
+ const wrapped = (payload) => handler(payload);
1387
+ const detach = contract.on(event, wrapped);
1349
1388
  if (!listenerRegistry.has(pluginName))
1350
1389
  listenerRegistry.set(pluginName, new Set());
1351
- const ref = { event, handler };
1390
+ const ref = { event, handler: wrapped, detach };
1352
1391
  listenerRegistry.get(pluginName).add(ref);
1353
1392
  return () => {
1354
- sock.ev.off(event, handler);
1393
+ try {
1394
+ detach();
1395
+ }
1396
+ catch { }
1355
1397
  listenerRegistry.get(pluginName)?.delete(ref);
1356
1398
  };
1357
1399
  },
1358
1400
  once(event) {
1359
- return new Promise(resolve => {
1401
+ assertSupportedEvent(event);
1402
+ return new Promise((resolve) => {
1360
1403
  const off = this.on(event, (data) => { off(); resolve(data); });
1361
1404
  });
1362
1405
  },
@@ -1364,8 +1407,12 @@ function buildEventsApi(contract, pluginName) {
1364
1407
  const list = listenerRegistry.get(pluginName);
1365
1408
  if (!list)
1366
1409
  return;
1367
- for (const { event, handler } of list)
1368
- sock.ev.off(event, handler);
1410
+ for (const ref of list) {
1411
+ try {
1412
+ ref.detach();
1413
+ }
1414
+ catch { }
1415
+ }
1369
1416
  listenerRegistry.delete(pluginName);
1370
1417
  },
1371
1418
  };
@@ -1563,11 +1610,15 @@ function buildMeApi(contract) {
1563
1610
  }
1564
1611
  // ── Poll API ──────────────────────────────────────────────────────────────────
1565
1612
  const pollRegistry = new Map();
1566
- // Rebinding must happen per socket instance a plugin name alone doesn't
1567
- // tell us whether the listener is bound to the CURRENT (post-reconnect)
1568
- // sock.ev or a dead one from before. WeakMap keyed by sock lets old
1569
- // entries fall off automatically once that socket is garbage collected.
1570
- const pollListenersBySocket = new WeakMap();
1613
+ // Per-process map of plugin names whose poll-vote subscription is bound,
1614
+ // to its unsubscribe handle. The contract handles rebinding to a fresh
1615
+ // socket on reconnect (its `on()` returns an unsubscribe that drops
1616
+ // cleanly on the dead socket's fan-out once it's torn down), so a simple
1617
+ // per-process map is enough — no per-sock WeakMap needed anymore.
1618
+ // `cleanupPluginEvents` MUST call the stored detach handle on unload so
1619
+ // the listener doesn't outlive the plugin and so a reloaded plugin can
1620
+ // re-subscribe.
1621
+ const boundPollPlugins = new Map();
1571
1622
  /**
1572
1623
  * Tracks votes for an active poll.
1573
1624
  * Obtained via ctx.poll.create().
@@ -1638,59 +1689,29 @@ function buildPollApi(contract, store, rawJid, guardOptions, pluginName) {
1638
1689
  pollRegistry.set(pluginName, new Map());
1639
1690
  const registry = pollRegistry.get(pluginName);
1640
1691
  // Keyed by creationId -> (voterKey -> latest vote entry). WhatsApp resends
1641
- // the *entire current selection* on every tap (not a diff), and Baileys'
1642
- // getAggregateVotesInPollMessage() replays whatever pollUpdates you give it
1643
- // with no dedup — so we must keep only the latest entry per voter ourselves,
1644
- // or retracted/changed votes keep counting alongside the new one.
1692
+ // the *entire current selection* on every tap (not a diff), and the
1693
+ // aggregatePollVotes() contract method replays whatever pollUpdates you
1694
+ // give it with no dedup — so we must keep only the latest entry per
1695
+ // voter ourselves, or retracted/changed votes keep counting alongside
1696
+ // the new one.
1645
1697
  const pollVotesByCreationId = new Map();
1646
- // Poll decryption needs the raw Baileys socket (sock.ev for
1647
- // messages.upsert, sock.user for self-JID shape), so we lift it off the
1648
- // contract here. The WeakMap key is the socket itself so old listeners
1649
- // fall off automatically when the socket is replaced (reconnect).
1650
- const sock = rawSocketOf(contract);
1651
- let boundPlugins = pollListenersBySocket.get(sock);
1652
- if (!boundPlugins) {
1653
- boundPlugins = new Set();
1654
- pollListenersBySocket.set(sock, boundPlugins);
1655
- }
1656
- if (!boundPlugins.has(pluginName)) {
1657
- boundPlugins.add(pluginName);
1658
- const meId = sock.user?.id ? jidNormalizedUser(sock.user.id) : "me";
1659
- // WhatsApp doesn't consistently use the same JID shape (LID vs PN) for
1660
- // pollCreatorJid/voterJid when deriving the poll-vote decryption key —
1661
- // it depends on addressingMode, 1:1 vs group, and which side sent last.
1662
- // Trying to compute "the" correct JID up front (as the old resolveAuthor
1663
- // did, always preferring participantPn) causes AES-GCM auth failures
1664
- // whenever WhatsApp actually used the LID for that message. Instead,
1665
- // gather every plausible JID for each side and brute-force combinations
1666
- // until one decrypts successfully — see
1667
- // https://github.com/WhiskeySockets/Baileys/issues/2342 and #1678.
1668
- function jidCandidates(key) {
1669
- const cands = [];
1670
- if (key.fromMe) {
1671
- const selfLid = sock.user?.lid;
1672
- if (selfLid)
1673
- cands.push(jidNormalizedUser(selfLid));
1674
- if (sock.user?.id)
1675
- cands.push(jidNormalizedUser(sock.user.id));
1676
- }
1677
- else {
1678
- const rawParticipant = key.participant ?? key.remoteJid;
1679
- if (rawParticipant)
1680
- cands.push(jidNormalizedUser(rawParticipant));
1681
- if (key.participantPn)
1682
- cands.push(jidNormalizedUser(key.participantPn));
1683
- if (rawParticipant?.endsWith("@lid")) {
1684
- const resolved = store.resolveJid(rawParticipant);
1685
- if (resolved && resolved !== rawParticipant)
1686
- cands.push(jidNormalizedUser(resolved));
1687
- }
1688
- }
1689
- return Array.from(new Set(cands.filter(Boolean)));
1690
- }
1691
- sock.ev.on("messages.upsert", async ({ messages: msgs }) => {
1698
+ // Poll decryption is Baileys-specific. Both the subscription
1699
+ // (`messages.upsert`) and the decryption/aggregation go through the
1700
+ // contract `buildPollApi` doesn't touch the raw socket anymore. The
1701
+ // contract's optional methods are only implemented by the Baileys
1702
+ // adapter; drivers that don't have poll decryption leave them off the
1703
+ // contract and we silently no-op here (a bot on a non-Baileys driver
1704
+ // simply can't track poll votes).
1705
+ if (!boundPollPlugins.has(pluginName) &&
1706
+ typeof contract.decryptPollVote === "function" &&
1707
+ typeof contract.aggregatePollVotes === "function") {
1708
+ const detach = contract.on("messages.upsert", async ({ messages: msgs }) => {
1692
1709
  for (const msg of msgs) {
1693
- const pum = msg.message?.pollUpdateMessage;
1710
+ // Filter poll-update messages by reading the raw envelope from
1711
+ // the store (the neutral `BotMessage` doesn't carry the
1712
+ // `pollUpdateMessage` field — it's a Baileys-proto detail).
1713
+ const raw = store.messages.get(msg.chatId)?.get(msg.id);
1714
+ const pum = raw?.message?.pollUpdateMessage;
1694
1715
  if (!pum)
1695
1716
  continue;
1696
1717
  const creationKey = pum.pollCreationMessageKey;
@@ -1703,44 +1724,44 @@ function buildPollApi(contract, store, rawJid, guardOptions, pluginName) {
1703
1724
  if (!storeMsg || !pollEncKeyRaw || !pum.vote)
1704
1725
  continue;
1705
1726
  try {
1706
- const pollEncKey = Buffer.isBuffer(pollEncKeyRaw)
1707
- ? pollEncKeyRaw
1708
- : Buffer.from(pollEncKeyRaw, "base64");
1709
- const creatorCandidates = jidCandidates((creationKey ?? {}));
1710
- const voterCandidates = jidCandidates(msg.key);
1711
- let decryptedVote;
1712
- for (const pollCreatorJid of creatorCandidates) {
1713
- for (const voterJid of voterCandidates) {
1714
- try {
1715
- decryptedVote = decryptPollVote(pum.vote, {
1716
- pollEncKey,
1717
- pollCreatorJid,
1718
- pollMsgId: creationId,
1719
- voterJid,
1720
- });
1721
- break;
1722
- }
1723
- catch {
1724
- // try next JID combination
1725
- }
1726
- }
1727
- if (decryptedVote)
1728
- break;
1729
- }
1730
- if (!decryptedVote) {
1731
- throw new Error(`all JID combinations failed (creator=${JSON.stringify(creatorCandidates)}, voter=${JSON.stringify(voterCandidates)})`);
1727
+ const decrypted = await contract.decryptPollVote({
1728
+ voteKey: {
1729
+ id: msg.id,
1730
+ remoteJid: msg.chatId,
1731
+ fromMe: msg.fromMe,
1732
+ participant: msg.participantAlt ?? msg.fromLid ?? msg.fromPn ?? null,
1733
+ },
1734
+ pollKey: {
1735
+ id: creationId,
1736
+ remoteJid: creationKey?.remoteJid ?? null,
1737
+ fromMe: null,
1738
+ participant: creationKey?.participant ?? null,
1739
+ },
1740
+ pollEncKey: pollEncKeyRaw,
1741
+ });
1742
+ if (!decrypted) {
1743
+ throw new Error("decryptPollVote returned null — JID candidate exhaustion or stale enc key");
1732
1744
  }
1733
- const voterKey = msg.key.fromMe
1734
- ? meId
1735
- : jidNormalizedUser(msg.key.participant ?? msg.key.remoteJid ?? "");
1745
+ const voterKey = msg.fromMe
1746
+ ? (contract.me().id ?? "me")
1747
+ : jidNormalizedUser(msg.participantAlt ?? msg.fromLid ?? msg.chatId);
1736
1748
  const votesByVoter = pollVotesByCreationId.get(creationId) ?? new Map();
1737
1749
  votesByVoter.set(voterKey, {
1738
- pollUpdateMessageKey: msg.key,
1739
- vote: decryptedVote,
1750
+ pollUpdateMessageKey: { id: msg.id, remoteJid: msg.chatId, fromMe: msg.fromMe },
1751
+ vote: decrypted,
1740
1752
  senderTimestampMs: pum.senderTimestampMs,
1741
1753
  });
1742
1754
  pollVotesByCreationId.set(creationId, votesByVoter);
1743
- const aggregated = getAggregateVotesInPollMessage({ message: storeMsg.message, pollUpdates: Array.from(votesByVoter.values()) }, meId);
1755
+ const pollAggregateOpts = contract.aggregatePollVotes;
1756
+ const aggregated = pollAggregateOpts({
1757
+ pollKey: {
1758
+ id: creationId,
1759
+ remoteJid: creationKey?.remoteJid ?? null,
1760
+ fromMe: null,
1761
+ },
1762
+ votes: Array.from(votesByVoter.values()).map((v) => v.vote),
1763
+ selfJid: contract.me().id ?? undefined,
1764
+ });
1744
1765
  handle._updateFromAggregated(aggregated);
1745
1766
  }
1746
1767
  catch (err) {
@@ -1748,6 +1769,7 @@ function buildPollApi(contract, store, rawJid, guardOptions, pluginName) {
1748
1769
  }
1749
1770
  }
1750
1771
  });
1772
+ boundPollPlugins.set(pluginName, detach);
1751
1773
  }
1752
1774
  const cooldown = (guardOptions.cooldown ?? true);
1753
1775
  const jitter = (guardOptions.jitter ?? true);
package/package.json CHANGED
@@ -5,7 +5,7 @@
5
5
  "name": "SyntaxError!",
6
6
  "email": "me@stxerr.dev"
7
7
  },
8
- "version": "5.6.1",
8
+ "version": "5.7.0",
9
9
  "license": "GPL-3.0-only",
10
10
  "private": false,
11
11
  "engines": {
@@ -40,7 +40,7 @@
40
40
  "@grpc/grpc-js": "^1.14.4",
41
41
  "@grpc/proto-loader": "^0.7.15",
42
42
  "@hapi/boom": "^10.0.1",
43
- "@whiskeysockets/baileys": "6.7.23",
43
+ "@whiskeysockets/baileys": "6.7.24",
44
44
  "node-cron": "^4.6.0",
45
45
  "node-webpmux": "^3.2.1",
46
46
  "nodemailer": "^9.0.3",