@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.
- package/dist/drivers/baileys/adapter.js +149 -1
- package/dist/drivers/baileys/api/index.js +128 -106
- package/package.json +2 -2
|
@@ -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 {
|
|
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
|
-
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
|
|
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
|
-
|
|
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,
|
|
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
|
|
1352
|
+
for (const ref of list) {
|
|
1331
1353
|
try {
|
|
1332
|
-
|
|
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
|
-
|
|
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
|
-
|
|
1393
|
+
try {
|
|
1394
|
+
detach();
|
|
1395
|
+
}
|
|
1396
|
+
catch { }
|
|
1355
1397
|
listenerRegistry.get(pluginName)?.delete(ref);
|
|
1356
1398
|
};
|
|
1357
1399
|
},
|
|
1358
1400
|
once(event) {
|
|
1359
|
-
|
|
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
|
|
1368
|
-
|
|
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
|
-
//
|
|
1567
|
-
//
|
|
1568
|
-
//
|
|
1569
|
-
//
|
|
1570
|
-
|
|
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
|
|
1642
|
-
//
|
|
1643
|
-
// with no dedup — so we must keep only the latest entry per
|
|
1644
|
-
// or retracted/changed votes keep counting alongside
|
|
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
|
|
1647
|
-
// messages.upsert
|
|
1648
|
-
// contract
|
|
1649
|
-
//
|
|
1650
|
-
|
|
1651
|
-
|
|
1652
|
-
|
|
1653
|
-
|
|
1654
|
-
|
|
1655
|
-
|
|
1656
|
-
|
|
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
|
-
|
|
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
|
|
1707
|
-
|
|
1708
|
-
|
|
1709
|
-
|
|
1710
|
-
|
|
1711
|
-
|
|
1712
|
-
|
|
1713
|
-
|
|
1714
|
-
|
|
1715
|
-
|
|
1716
|
-
|
|
1717
|
-
|
|
1718
|
-
|
|
1719
|
-
|
|
1720
|
-
|
|
1721
|
-
|
|
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.
|
|
1734
|
-
?
|
|
1735
|
-
: jidNormalizedUser(msg.
|
|
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.
|
|
1739
|
-
vote:
|
|
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
|
|
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.
|
|
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.
|
|
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",
|