@manybot/manybot 5.6.0 → 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 +294 -73
- package/dist/drivers/baileys/api/index.js +176 -141
- package/dist/drivers/baileys/index.js +36 -2
- package/dist/drivers/baileys/sdk/baileysSock.js +1 -1
- package/dist/drivers/whatsmeow/client.js +54 -5
- package/dist/drivers/whatsmeow/installer.js +20 -4
- package/dist/drivers/whatsmeow/supervisor.js +24 -5
- package/dist/kernel/sendFallbackGuard.js +15 -5
- package/dist/locales/en.json +3 -2
- package/dist/locales/es.json +3 -2
- package/dist/locales/pt.json +3 -2
- package/dist/main.js +23 -0
- package/package.json +2 -2
|
@@ -22,15 +22,114 @@
|
|
|
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
|
+
/**
|
|
29
|
+
* Classify a Baileys message-content payload (`WAMessageContent`) into
|
|
30
|
+
* the neutral `(type, body, mimetype)` triple used by `BotMessage`. Pure
|
|
31
|
+
* function — no side effects, no closures — so it's safe to call for
|
|
32
|
+
* both real incoming messages and the embedded `quotedMessage` content
|
|
33
|
+
* carried in `contextInfo` (which can be an `ephemeralMessage`/
|
|
34
|
+
* `viewOnceMessage` wrapper, hence the `normalizeMessageContent` call).
|
|
35
|
+
*
|
|
36
|
+
* Exported so `api/index.ts` can decode the quoted payload when
|
|
37
|
+
* synthesizing the `BotMessage` returned by `getReply()`.
|
|
38
|
+
*/
|
|
39
|
+
export function decodeContent(content) {
|
|
40
|
+
const m = normalizeMessageContent(content) ?? undefined;
|
|
41
|
+
let type = "other";
|
|
42
|
+
let body = "";
|
|
43
|
+
let mimetype;
|
|
44
|
+
if (m?.conversation) {
|
|
45
|
+
type = "text";
|
|
46
|
+
body = m.conversation;
|
|
47
|
+
}
|
|
48
|
+
else if (m?.extendedTextMessage?.text) {
|
|
49
|
+
type = "text";
|
|
50
|
+
body = m.extendedTextMessage.text ?? "";
|
|
51
|
+
}
|
|
52
|
+
else if (m?.imageMessage) {
|
|
53
|
+
type = "image";
|
|
54
|
+
body = m.imageMessage.caption ?? "";
|
|
55
|
+
mimetype = m.imageMessage.mimetype ?? undefined;
|
|
56
|
+
}
|
|
57
|
+
else if (m?.videoMessage) {
|
|
58
|
+
type = "video";
|
|
59
|
+
body = m.videoMessage.caption ?? "";
|
|
60
|
+
mimetype = m.videoMessage.mimetype ?? undefined;
|
|
61
|
+
}
|
|
62
|
+
else if (m?.audioMessage) {
|
|
63
|
+
type = "audio";
|
|
64
|
+
mimetype = m.audioMessage.mimetype ?? undefined;
|
|
65
|
+
}
|
|
66
|
+
else if (m?.documentMessage) {
|
|
67
|
+
type = "document";
|
|
68
|
+
body = m.documentMessage.caption ?? "";
|
|
69
|
+
mimetype = m.documentMessage.mimetype ?? undefined;
|
|
70
|
+
}
|
|
71
|
+
else if (m?.stickerMessage) {
|
|
72
|
+
type = "sticker";
|
|
73
|
+
mimetype = m.stickerMessage.mimetype ?? undefined;
|
|
74
|
+
}
|
|
75
|
+
return { type, body, mimetype };
|
|
76
|
+
}
|
|
28
77
|
export function createBaileysAdapter(initial) {
|
|
29
78
|
// mutable so rebind() can swap it; closure-scoped so the contract below
|
|
30
79
|
// always sees the latest sock.
|
|
31
80
|
let sock = initial.sock;
|
|
32
81
|
const store = initial.store;
|
|
33
82
|
// ── Adapter-local helpers ────────────────────────────────────────────────
|
|
83
|
+
/**
|
|
84
|
+
* Build the `quoted` option for `sock.sendMessage(jid, content, opts)`.
|
|
85
|
+
*
|
|
86
|
+
* Baileys expects the `quoted` field to be a full message-shaped object —
|
|
87
|
+
* `{ key: {...}, message: {...} }` — so it can extract both the attribution
|
|
88
|
+
* (stanzaId/participant/fromMe from `key`) and the preview content (from
|
|
89
|
+
* `message`) when generating contextInfo. Passing only `key` makes
|
|
90
|
+
* `generateWAMessageFromContent` call `normalizeMessageContent(undefined)`
|
|
91
|
+
* → `getContentType(undefined)` → `undefined`, then index it with `[]`
|
|
92
|
+
* and throw `Cannot read properties of undefined (reading 'undefined')`.
|
|
93
|
+
*
|
|
94
|
+
* We resolve the full envelope from the in-memory store, indexed by the
|
|
95
|
+
* (remoteJid, id) pair carried in the neutral `BotQuotedRef`. If the
|
|
96
|
+
* envelope is no longer present (evicted past MAX_MSGS_PER_CHAT, lost on
|
|
97
|
+
* restart, etc.) we return `undefined` rather than emit a half-formed
|
|
98
|
+
* `quoted` — degrading to a plain unquoted reply is safer than crashing
|
|
99
|
+
* the calling plugin.
|
|
100
|
+
*/
|
|
101
|
+
function buildQuotedOpts(quoted) {
|
|
102
|
+
if (!quoted?.id || !quoted?.remoteJid)
|
|
103
|
+
return undefined;
|
|
104
|
+
const raw = store.messages.get(quoted.remoteJid)?.get(quoted.id);
|
|
105
|
+
const inner = raw?.message;
|
|
106
|
+
if (!inner)
|
|
107
|
+
return undefined; // safe fallback: no quoted at all
|
|
108
|
+
// `quoted` (used by sendMessage for reply-citation) is a
|
|
109
|
+
// message-shaped object: `{ key: {...}, message: {...} }`. Baileys
|
|
110
|
+
// reads `quoted.key.*` to populate contextInfo (stanzaId, participant,
|
|
111
|
+
// fromMe) — passing the fields flat used to misattribute the reply to
|
|
112
|
+
// the bot itself. `quoted.message` is also required: Baileys' internal
|
|
113
|
+
// `generateWAMessageFromContent` calls `normalizeMessageContent(
|
|
114
|
+
// quoted.message)` and indexes the result with `getContentType(...)`,
|
|
115
|
+
// which throws if `.message` is missing.
|
|
116
|
+
return { quoted: { key: toFlatKey(quoted), message: inner } };
|
|
117
|
+
}
|
|
118
|
+
/**
|
|
119
|
+
* Translate a neutral `BotQuotedRef` into the FLAT key shape Baileys
|
|
120
|
+
* expects for `react`/`delete`/`edit`/`readMessages` (proto.IMessageKey
|
|
121
|
+
* — `{ id, remoteJid, fromMe, participant }` directly, NOT nested under
|
|
122
|
+
* a `.key` field). The nested `{key, message}` form is reserved for the
|
|
123
|
+
* `quoted` field on sendMessage — see `buildQuotedOpts` above.
|
|
124
|
+
*/
|
|
125
|
+
function toFlatKey(ref) {
|
|
126
|
+
return {
|
|
127
|
+
id: ref.id ?? null,
|
|
128
|
+
remoteJid: ref.remoteJid ?? "",
|
|
129
|
+
fromMe: !!ref.fromMe,
|
|
130
|
+
participant: ref.participant ?? undefined,
|
|
131
|
+
};
|
|
132
|
+
}
|
|
34
133
|
/** Compute sha1-hex of a normalized buffer or string. */
|
|
35
134
|
function sha1(input) {
|
|
36
135
|
const hash = createHash("sha1");
|
|
@@ -45,40 +144,7 @@ export function createBaileysAdapter(initial) {
|
|
|
45
144
|
/** Translate a Baileys WAMessage into the neutral BotMessage envelope. */
|
|
46
145
|
function toBotMessage(msg) {
|
|
47
146
|
const m = normalizeMessageContent(msg.message) ?? undefined;
|
|
48
|
-
|
|
49
|
-
let body = "";
|
|
50
|
-
let mimetype;
|
|
51
|
-
if (m?.conversation) {
|
|
52
|
-
type = "text";
|
|
53
|
-
body = m.conversation;
|
|
54
|
-
}
|
|
55
|
-
else if (m?.extendedTextMessage?.text) {
|
|
56
|
-
type = "text";
|
|
57
|
-
body = m.extendedTextMessage.text ?? "";
|
|
58
|
-
}
|
|
59
|
-
else if (m?.imageMessage) {
|
|
60
|
-
type = "image";
|
|
61
|
-
body = m.imageMessage.caption ?? "";
|
|
62
|
-
mimetype = m.imageMessage.mimetype ?? undefined;
|
|
63
|
-
}
|
|
64
|
-
else if (m?.videoMessage) {
|
|
65
|
-
type = "video";
|
|
66
|
-
body = m.videoMessage.caption ?? "";
|
|
67
|
-
mimetype = m.videoMessage.mimetype ?? undefined;
|
|
68
|
-
}
|
|
69
|
-
else if (m?.audioMessage) {
|
|
70
|
-
type = "audio";
|
|
71
|
-
mimetype = m.audioMessage.mimetype ?? undefined;
|
|
72
|
-
}
|
|
73
|
-
else if (m?.documentMessage) {
|
|
74
|
-
type = "document";
|
|
75
|
-
body = m.documentMessage.caption ?? "";
|
|
76
|
-
mimetype = m.documentMessage.mimetype ?? undefined;
|
|
77
|
-
}
|
|
78
|
-
else if (m?.stickerMessage) {
|
|
79
|
-
type = "sticker";
|
|
80
|
-
mimetype = m.stickerMessage.mimetype ?? undefined;
|
|
81
|
-
}
|
|
147
|
+
const { type, body, mimetype } = decodeContent(msg.message);
|
|
82
148
|
const key = msg.key;
|
|
83
149
|
const contextInfo = m?.extendedTextMessage?.contextInfo ??
|
|
84
150
|
m?.imageMessage?.contextInfo ??
|
|
@@ -86,6 +152,7 @@ export function createBaileysAdapter(initial) {
|
|
|
86
152
|
m?.audioMessage?.contextInfo ??
|
|
87
153
|
m?.documentMessage?.contextInfo ??
|
|
88
154
|
undefined;
|
|
155
|
+
const ciTyped = contextInfo;
|
|
89
156
|
return {
|
|
90
157
|
id: msg.key.id ?? "",
|
|
91
158
|
chatId: msg.key.remoteJid ?? "",
|
|
@@ -96,20 +163,27 @@ export function createBaileysAdapter(initial) {
|
|
|
96
163
|
body,
|
|
97
164
|
mimetype: mimetype ?? undefined,
|
|
98
165
|
pushName: msg.pushName,
|
|
99
|
-
mentionedJid:
|
|
100
|
-
quotedKey:
|
|
101
|
-
id:
|
|
166
|
+
mentionedJid: ciTyped?.mentionedJid ?? undefined,
|
|
167
|
+
quotedKey: ciTyped?.stanzaId ? {
|
|
168
|
+
id: ciTyped.stanzaId,
|
|
102
169
|
remoteJid: msg.key.remoteJid ?? undefined,
|
|
103
170
|
fromMe: false,
|
|
104
|
-
participant:
|
|
171
|
+
participant: ciTyped.participant ?? undefined,
|
|
105
172
|
} : undefined,
|
|
106
173
|
fromLid: key.participantAlt,
|
|
107
174
|
fromPn: key.participant,
|
|
108
175
|
participantAlt: key.participantAlt,
|
|
109
176
|
remoteJidAlt: key.remoteJidAlt,
|
|
110
|
-
//
|
|
177
|
+
// Driver-specific escape hatches:
|
|
178
|
+
// - pollEncKeyRaw: poll-decryption key for vote decryption
|
|
179
|
+
// - contextInfo: full IContextInfo (incl. embedded quotedMessage)
|
|
180
|
+
// so quoted-message consumers (api/index.ts
|
|
181
|
+
// buildMessageContext / downloadMedia fallback)
|
|
182
|
+
// can decode the quoted payload without going
|
|
183
|
+
// back through the store.
|
|
111
184
|
_raw: {
|
|
112
185
|
pollEncKeyRaw: m?.messageContextInfo?.messageSecret ?? undefined,
|
|
186
|
+
contextInfo: ciTyped ?? undefined,
|
|
113
187
|
},
|
|
114
188
|
};
|
|
115
189
|
}
|
|
@@ -127,6 +201,44 @@ export function createBaileysAdapter(initial) {
|
|
|
127
201
|
lid: c.lid,
|
|
128
202
|
};
|
|
129
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
|
+
}
|
|
130
242
|
const subscribers = new Map();
|
|
131
243
|
// Tracks the handlers each socket has registered so we can remove them on a
|
|
132
244
|
// fresh socket after reconnect. Declared up here because the rebind helpers
|
|
@@ -237,17 +349,17 @@ export function createBaileysAdapter(initial) {
|
|
|
237
349
|
return toSentRef(ref, jid);
|
|
238
350
|
},
|
|
239
351
|
async react(jid, target, emoji) {
|
|
240
|
-
const key =
|
|
352
|
+
const key = toFlatKey(target);
|
|
241
353
|
await sock.sendMessage(jid, { react: { text: emoji, key } });
|
|
242
354
|
},
|
|
243
355
|
async deleteMessage(jid, target, forEveryone) {
|
|
244
356
|
if (!forEveryone)
|
|
245
357
|
return;
|
|
246
|
-
const key =
|
|
358
|
+
const key = toFlatKey(target);
|
|
247
359
|
await sock.sendMessage(jid, { delete: key });
|
|
248
360
|
},
|
|
249
361
|
async editMessage(jid, target, text) {
|
|
250
|
-
const key =
|
|
362
|
+
const key = toFlatKey(target);
|
|
251
363
|
await sock.sendMessage(jid, { text, edit: key });
|
|
252
364
|
},
|
|
253
365
|
// ── presence + read ───────────────────────────────────────────────────
|
|
@@ -258,7 +370,7 @@ export function createBaileysAdapter(initial) {
|
|
|
258
370
|
await sock.sendPresenceUpdate(baileyState, jid);
|
|
259
371
|
},
|
|
260
372
|
async readMessages(keys) {
|
|
261
|
-
const baileyKeys = keys.map(
|
|
373
|
+
const baileyKeys = keys.map((k) => toFlatKey(k));
|
|
262
374
|
await sock.readMessages(baileyKeys);
|
|
263
375
|
},
|
|
264
376
|
// ── contacts ──────────────────────────────────────────────────────────
|
|
@@ -385,10 +497,30 @@ export function createBaileysAdapter(initial) {
|
|
|
385
497
|
},
|
|
386
498
|
// ── media (download) ───────────────────────────────────────────────────
|
|
387
499
|
async downloadMedia(msg, opts) {
|
|
388
|
-
//
|
|
389
|
-
//
|
|
390
|
-
//
|
|
391
|
-
|
|
500
|
+
// Resolve the Baileys message envelope needed by `downloadMediaMessage`.
|
|
501
|
+
// Preferred path: if the caller already carries the embedded message
|
|
502
|
+
// payload (synthetic `BotMessage` for a quoted message, whose
|
|
503
|
+
// `_raw.contextInfo.quotedMessage` is the full WAMessageContent),
|
|
504
|
+
// build the envelope directly from that. This avoids the silent
|
|
505
|
+
// failure mode where the quoted message's original envelope has
|
|
506
|
+
// aged out of the store's per-chat ring buffer.
|
|
507
|
+
//
|
|
508
|
+
// Fallback: the regular case (downloading media for the incoming
|
|
509
|
+
// message itself, or any other BotMessage that has a real envelope
|
|
510
|
+
// in the store) — look it up by (chatId, id).
|
|
511
|
+
const embedded = msg._raw;
|
|
512
|
+
const embeddedContent = embedded?.contextInfo?.quotedMessage;
|
|
513
|
+
const raw = embeddedContent
|
|
514
|
+
? {
|
|
515
|
+
key: {
|
|
516
|
+
id: embedded.contextInfo?.stanzaId ?? msg.id,
|
|
517
|
+
remoteJid: msg.chatId,
|
|
518
|
+
fromMe: false,
|
|
519
|
+
participant: embedded.contextInfo?.participant ?? undefined,
|
|
520
|
+
},
|
|
521
|
+
message: embeddedContent,
|
|
522
|
+
}
|
|
523
|
+
: store.messages.get(msg.chatId)?.get(msg.id);
|
|
392
524
|
if (!raw)
|
|
393
525
|
return null;
|
|
394
526
|
try {
|
|
@@ -411,6 +543,72 @@ export function createBaileysAdapter(initial) {
|
|
|
411
543
|
return null;
|
|
412
544
|
}
|
|
413
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
|
+
},
|
|
414
612
|
};
|
|
415
613
|
// ── Rebind helpers (post-reconnect in drivers/baileys/index.ts) ────────
|
|
416
614
|
//
|
|
@@ -434,6 +632,26 @@ export function createBaileysAdapter(initial) {
|
|
|
434
632
|
const updates = arg;
|
|
435
633
|
emit("messages.update", { updates });
|
|
436
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
|
+
});
|
|
437
655
|
register("messaging-history.set", (arg) => {
|
|
438
656
|
const { chats, contacts, messages } = arg;
|
|
439
657
|
emit("messaging-history.set", {
|
|
@@ -448,6 +666,9 @@ export function createBaileysAdapter(initial) {
|
|
|
448
666
|
register("chats.update", (arg) => {
|
|
449
667
|
emit("chats.update", { updates: arg });
|
|
450
668
|
});
|
|
669
|
+
register("chats.delete", (arg) => {
|
|
670
|
+
emit("chats.delete", { ids: arg });
|
|
671
|
+
});
|
|
451
672
|
register("contacts.upsert", (arg) => {
|
|
452
673
|
emit("contacts.upsert", { contacts: arg.map(contactSummary) });
|
|
453
674
|
});
|
|
@@ -458,9 +679,30 @@ export function createBaileysAdapter(initial) {
|
|
|
458
679
|
const { id, participants } = arg;
|
|
459
680
|
emit("group-participants.update", { id, participants });
|
|
460
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
|
+
});
|
|
461
686
|
register("groups.update", (arg) => {
|
|
462
687
|
emit("groups.update", { updates: arg });
|
|
463
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
|
+
});
|
|
464
706
|
register("connection.update", (arg) => {
|
|
465
707
|
const { connection, lastDisconnect } = arg;
|
|
466
708
|
emit("connection.update", {
|
|
@@ -508,31 +750,10 @@ export function createBaileysAdapter(initial) {
|
|
|
508
750
|
};
|
|
509
751
|
}
|
|
510
752
|
// ── Helpers used inside the adapter above ────────────────────────────────────
|
|
511
|
-
|
|
512
|
-
|
|
513
|
-
|
|
514
|
-
|
|
515
|
-
}
|
|
516
|
-
function toBaileysKey(ref) {
|
|
517
|
-
// Baileys expects `quoted` to be a message-shaped object with these
|
|
518
|
-
// fields nested under `.key` (quoted.key.id / .remoteJid / .fromMe /
|
|
519
|
-
// .participant) — it reads quoted.key.* to build contextInfo
|
|
520
|
-
// (stanzaId, participant, fromMe). Passing them flat (as this used to)
|
|
521
|
-
// means quoted.key is undefined inside Baileys, so participant/fromMe
|
|
522
|
-
// resolve to undefined and the quoted reply gets misattributed to the
|
|
523
|
-
// bot itself instead of the original sender. The quoted text still
|
|
524
|
-
// rendered before this fix because WhatsApp's client resolves the
|
|
525
|
-
// preview content locally via stanzaId — only the author attribution
|
|
526
|
-
// was broken.
|
|
527
|
-
return {
|
|
528
|
-
key: {
|
|
529
|
-
id: ref.id ?? null,
|
|
530
|
-
remoteJid: ref.remoteJid ?? "",
|
|
531
|
-
fromMe: !!ref.fromMe,
|
|
532
|
-
participant: ref.participant ?? undefined,
|
|
533
|
-
},
|
|
534
|
-
};
|
|
535
|
-
}
|
|
753
|
+
//
|
|
754
|
+
// `buildQuotedOpts` and `toFlatKey` are defined inside the
|
|
755
|
+
// `createBaileysAdapter` closure (so they can see the `store`) — only
|
|
756
|
+
// `toSentRef` and `silentBaileysLogger` remain module-scoped helpers.
|
|
536
757
|
function toSentRef(raw, fallbackChatId) {
|
|
537
758
|
const r = raw;
|
|
538
759
|
return {
|