my-baileys 1.0.2 → 1.0.3
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/README.md +37 -0
- package/lib/Socket/message-builder.js +1 -11
- package/lib/Store/keyed-db.js +14 -1
- package/lib/Store/make-in-memory-store.js +66 -11
- package/lib/Utils/messages.js +0 -14
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -594,6 +594,34 @@ await sock.forwardMessage(jid, originalMsg);
|
|
|
594
594
|
await sock.broadcastMessage(jidList, { text: 'Announcement' });
|
|
595
595
|
```
|
|
596
596
|
|
|
597
|
+
### `sendHtml()` — experimental, read this first
|
|
598
|
+
|
|
599
|
+
```javascript
|
|
600
|
+
await sock.sendHtml(jid, '<b>Hello</b> from my bot');
|
|
601
|
+
```
|
|
602
|
+
|
|
603
|
+
This mirrors WhatsApp's own internal "Meta AI" chat message shape
|
|
604
|
+
(`botForwardedMessage` → `richResponseMessage`, internally named `GenAIUnifiedResponse` /
|
|
605
|
+
`FOAHtmlPrimitiveDemoDONOTUSE` in WhatsApp's own protocol — that `DONOTUSE` suffix is Meta's own
|
|
606
|
+
naming for an experimental feature flag, not a warning from us). It is **not** a documented,
|
|
607
|
+
stable public message type, so:
|
|
608
|
+
|
|
609
|
+
- WhatsApp's client may render it inline, show a "tap to view"/download prompt, or (if the
|
|
610
|
+
feature gets pulled server-side) not render it at all — behavior isn't guaranteed and can
|
|
611
|
+
change without notice, since your bot isn't the actual verified Meta AI account this format
|
|
612
|
+
was designed for.
|
|
613
|
+
- **Bug fixed:** earlier versions of this method unconditionally marked every message
|
|
614
|
+
`contextInfo: { isForwarded: true, forwardOrigin: 4 /* META_AI */ }` — literally claiming
|
|
615
|
+
every message was forwarded from Meta AI, which is almost certainly why some people saw it
|
|
616
|
+
require an extra tap/download before showing up (a non-verified sender claiming Meta AI
|
|
617
|
+
origin is exactly what you'd expect WhatsApp to treat cautiously). It now sends as a plain,
|
|
618
|
+
non-forwarded message by default; pass `{ contextInfo: {...} }` yourself if you have a
|
|
619
|
+
specific reason to set forwarding metadata.
|
|
620
|
+
|
|
621
|
+
If you need something guaranteed to render reliably, prefer a documented content type
|
|
622
|
+
(`sendButtonsMessage`, `sendListMessage`, a plain `{ text }`, etc.) — `sendHtml` is here for
|
|
623
|
+
experimentation, not as a recommended primary way to send rich content.
|
|
624
|
+
|
|
597
625
|
If you have `antiban: true` (or a preset) set, these all go through it too — `forwardMessage`,
|
|
598
626
|
`sendActionPoll`, and `broadcastMessage` call `sock.sendMessage()` internally, same as calling
|
|
599
627
|
it yourself. (`sendJsonMessage`/`sendCarouselMessage` use the lower-level `relayMessage()` and
|
|
@@ -920,8 +948,17 @@ store.bind(client.ev);
|
|
|
920
948
|
client.ev.on('contacts.upsert', () => {
|
|
921
949
|
console.log('New contact: ' + Object.values(store.contacts()));
|
|
922
950
|
});
|
|
951
|
+
|
|
952
|
+
// persist the whole store to disk, and restore it on the next run
|
|
953
|
+
setInterval(() => store.writeToFile('./baileys-store.json'), 10_000);
|
|
954
|
+
store.readFromFile('./baileys-store.json'); // call before store.bind() to load prior state
|
|
923
955
|
```
|
|
924
956
|
|
|
957
|
+
Two real bugs in the store's `KeyedDB` were found and fixed while adding `writeToFile`/
|
|
958
|
+
`readFromFile`: `chats.get(jid)` / `.update(jid, ...)` / `.deleteById(jid)` didn't actually work
|
|
959
|
+
before this fix (see [LITERACY.md → Changelog vs upstream](LITERACY.md#changelog-vs-upstream)
|
|
960
|
+
for the full story), so if you were relying on those specifically, they now behave as expected.
|
|
961
|
+
|
|
925
962
|
---
|
|
926
963
|
|
|
927
964
|
## Sending messages
|
|
@@ -18,13 +18,6 @@ export const makeMessageBuilderSocket = (sock) => {
|
|
|
18
18
|
const msg = generateWAMessageFromContent(jid, content, {});
|
|
19
19
|
return relayMessage(jid, msg.message, { messageId: msg.key.id, ...options });
|
|
20
20
|
};
|
|
21
|
-
|
|
22
|
-
/**
|
|
23
|
-
* Sends an HTML GenAI primitive rich response message.
|
|
24
|
-
* html: string template/raw HTML markup to be rendered.
|
|
25
|
-
* (Added from xbailsync@1.0.7 — a plain, explicit send, nothing hidden:
|
|
26
|
-
* only runs when you call it, same as every other method here.)
|
|
27
|
-
*/
|
|
28
21
|
const sendHtml = async (jid, html = '', options = {}) => {
|
|
29
22
|
const payloadData = Buffer.from(
|
|
30
23
|
JSON.stringify({
|
|
@@ -54,10 +47,7 @@ export const makeMessageBuilderSocket = (sock) => {
|
|
|
54
47
|
unifiedResponse: {
|
|
55
48
|
data: payloadData
|
|
56
49
|
},
|
|
57
|
-
contextInfo: {
|
|
58
|
-
isForwarded: true,
|
|
59
|
-
forwardOrigin: 4
|
|
60
|
-
}
|
|
50
|
+
...(options.contextInfo ? { contextInfo: options.contextInfo } : {})
|
|
61
51
|
}
|
|
62
52
|
}
|
|
63
53
|
}
|
package/lib/Store/keyed-db.js
CHANGED
|
@@ -23,7 +23,20 @@ export default class KeyedDB {
|
|
|
23
23
|
this._dict[id] = entry;
|
|
24
24
|
let inserted = false;
|
|
25
25
|
for (let i = 0; i < this._array.length; i++) {
|
|
26
|
-
|
|
26
|
+
// BUG FIX (found while adding writeToFile/readFromFile — see
|
|
27
|
+
// make-in-memory-store.js changelog note): this used to compare
|
|
28
|
+
// this._idGetter(this._array[i]) against `id` — i.e. it fed the
|
|
29
|
+
// ID-getter's output into `_compare` on both sides. That only
|
|
30
|
+
// works if idGetter ALSO doubles as a sort-priority key (which
|
|
31
|
+
// it did for the old `waChatKey`, at the cost of breaking
|
|
32
|
+
// `get()`/`update()`/`deleteById()`, since those take a *plain*
|
|
33
|
+
// id, not a composite sort key, and index into `_dict` by
|
|
34
|
+
// whatever `idGetter` actually returns). Comparing the full
|
|
35
|
+
// entries directly instead — matching @vansnowi/baileys@1.5.9's
|
|
36
|
+
// implementation — lets idGetter go back to being just "the
|
|
37
|
+
// plain unique id", with `compare` doing its own real job of
|
|
38
|
+
// ordering two full entries.
|
|
39
|
+
const cmp = this._compare(entry, this._array[i]);
|
|
27
40
|
if (cmp < 0) {
|
|
28
41
|
this._array.splice(i, 0, entry);
|
|
29
42
|
inserted = true;
|
|
@@ -6,26 +6,55 @@ import * as WABinary_1 from "../WABinary/index.js";
|
|
|
6
6
|
import makeOrderedDictionary from "./make-ordered-dictionary.js";
|
|
7
7
|
import { ObjectRepository } from "./object-repository.js";
|
|
8
8
|
import KeyedDB from "./keyed-db.js";
|
|
9
|
-
|
|
9
|
+
import { existsSync, readFileSync, writeFileSync } from "fs";
|
|
10
|
+
|
|
11
|
+
// BUG FIX (see keyed-db.js's insert() for the full story): `key` used to
|
|
12
|
+
// return a composite string (pin flag + archive flag + hex timestamp + id)
|
|
13
|
+
// that doubled as BOTH the KeyedDB dict-lookup id AND the sort-priority
|
|
14
|
+
// encoding. That broke `chats.get(jid)` / `.update(jid, ...)` /
|
|
15
|
+
// `.deleteById(jid)` for every caller passing a plain JID (which is every
|
|
16
|
+
// caller in this codebase) — `_dict` was keyed by the composite string, so a
|
|
17
|
+
// plain-JID lookup never matched. `key` is now just the plain chat id;
|
|
18
|
+
// `compare` does the actual pin/archived/most-recent-first ordering
|
|
19
|
+
// directly on two full chat entries, preserving the exact same sort order
|
|
20
|
+
// as before (pinned first, then non-archived first, then newest
|
|
21
|
+
// conversationTimestamp first, id as the final tie-break to match the old
|
|
22
|
+
// descending-string-compare behavior).
|
|
10
23
|
const waChatKey = (pin) => ({
|
|
11
|
-
key: (c) =>
|
|
12
|
-
|
|
13
|
-
(
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
24
|
+
key: (c) => c.id,
|
|
25
|
+
compare: (c1, c2) => {
|
|
26
|
+
if (pin) {
|
|
27
|
+
const p1 = c1.pinned ? 1 : 0;
|
|
28
|
+
const p2 = c2.pinned ? 1 : 0;
|
|
29
|
+
if (p1 !== p2) return p2 - p1;
|
|
30
|
+
}
|
|
31
|
+
const a1 = c1.archived ? 0 : 1;
|
|
32
|
+
const a2 = c2.archived ? 0 : 1;
|
|
33
|
+
if (a1 !== a2) return a2 - a1;
|
|
34
|
+
const t1 = c1.conversationTimestamp ? Number(c1.conversationTimestamp) : 0;
|
|
35
|
+
const t2 = c2.conversationTimestamp ? Number(c2.conversationTimestamp) : 0;
|
|
36
|
+
if (t1 !== t2) return t2 - t1;
|
|
37
|
+
return (c2.id || "").localeCompare(c1.id || "");
|
|
38
|
+
}
|
|
19
39
|
});
|
|
20
40
|
|
|
21
41
|
export const waMessageID = (m) => m.key.id || "";
|
|
22
42
|
|
|
43
|
+
// BUG FIX, same root cause as waChatKey above: `compare` used to receive
|
|
44
|
+
// the two entries' composite `key(...)` STRING outputs (via keyed-db.js's
|
|
45
|
+
// old buggy insert()), which is why plain string `.localeCompare` worked
|
|
46
|
+
// there previously — but only by coincidence, since `key` here (unlike
|
|
47
|
+
// chats') already IS the plain lookup id, not a separate sort-priority
|
|
48
|
+
// encoding. Updated to a proper entry-to-entry comparator (computing the
|
|
49
|
+
// same composite key from each full entry) so it keeps working correctly
|
|
50
|
+
// now that keyed-db.js's insert() passes full entries instead of
|
|
51
|
+
// pre-computed key strings.
|
|
23
52
|
export const waLabelAssociationKey = {
|
|
24
53
|
key: (la) =>
|
|
25
54
|
la.type === LabelAssociationType.Chat
|
|
26
55
|
? la.chatId + la.labelId
|
|
27
56
|
: la.chatId + la.messageId + la.labelId,
|
|
28
|
-
compare: (
|
|
57
|
+
compare: (a, b) => waLabelAssociationKey.key(b).localeCompare(waLabelAssociationKey.key(a))
|
|
29
58
|
};
|
|
30
59
|
|
|
31
60
|
const makeMessagesDictionary = () => makeOrderedDictionary(waMessageID);
|
|
@@ -110,7 +139,11 @@ export default function makeInMemoryStore(config) {
|
|
|
110
139
|
|
|
111
140
|
ev.on("labels.association", ({ type, association }) => {
|
|
112
141
|
if (type === "add") labelAssociations.upsert(association);
|
|
113
|
-
|
|
142
|
+
// BUG FIX: was `labelAssociations.delete(association)` — KeyedDB
|
|
143
|
+
// has no `.delete()` method (only `.deleteById(id)`), so this
|
|
144
|
+
// threw `TypeError: labelAssociations.delete is not a function`
|
|
145
|
+
// every time a label association was actually removed.
|
|
146
|
+
if (type === "remove") labelAssociations.deleteById(labelAssociationKey.key(association));
|
|
114
147
|
});
|
|
115
148
|
|
|
116
149
|
ev.on("presence.update", ({ id, presences: update }) => {
|
|
@@ -181,6 +214,26 @@ export default function makeInMemoryStore(config) {
|
|
|
181
214
|
const loadMessage = async (jid, id) => {
|
|
182
215
|
return messages[jid]?.get(id)
|
|
183
216
|
}
|
|
217
|
+
// Added from @vansnowi/baileys@1.5.9 — the two persistence entry points
|
|
218
|
+
// that were missing here even though toJSON()/fromJSON() (this fork's
|
|
219
|
+
// own, more careful versions — see fromJSON above, which reconstructs
|
|
220
|
+
// proper WebMessageInfo instances instead of leaving raw parsed JSON)
|
|
221
|
+
// already existed. Uses this fork's own shared BufferJSON.replacer/
|
|
222
|
+
// reviver (Utils/generics.js, already used by make-cache-manager-store.js)
|
|
223
|
+
// rather than duplicating a private copy, so Buffers inside the store
|
|
224
|
+
// (media keys, etc.) survive a save/load round-trip intact.
|
|
225
|
+
const writeToFile = (path) => {
|
|
226
|
+
const data = JSON.stringify(toJSON(), Utils_1.BufferJSON.replacer);
|
|
227
|
+
writeFileSync(path, data);
|
|
228
|
+
};
|
|
229
|
+
const readFromFile = (path) => {
|
|
230
|
+
if (existsSync(path)) {
|
|
231
|
+
logger?.debug?.({ path }, "reading store from file");
|
|
232
|
+
const jsonStr = readFileSync(path, { encoding: "utf-8" });
|
|
233
|
+
const json = JSON.parse(jsonStr, Utils_1.BufferJSON.reviver);
|
|
234
|
+
fromJSON(json);
|
|
235
|
+
}
|
|
236
|
+
};
|
|
184
237
|
return {
|
|
185
238
|
chats,
|
|
186
239
|
contacts,
|
|
@@ -193,6 +246,8 @@ export default function makeInMemoryStore(config) {
|
|
|
193
246
|
bind,
|
|
194
247
|
toJSON,
|
|
195
248
|
fromJSON,
|
|
249
|
+
writeToFile,
|
|
250
|
+
readFromFile,
|
|
196
251
|
loadMessage
|
|
197
252
|
};
|
|
198
253
|
}
|
package/lib/Utils/messages.js
CHANGED
|
@@ -26,11 +26,6 @@ const MessageTypeProto = {
|
|
|
26
26
|
sticker: WAProto.Message.StickerMessage,
|
|
27
27
|
document: WAProto.Message.DocumentMessage
|
|
28
28
|
};
|
|
29
|
-
// Added from baileys@6.2.7: lets generateWAMessageFromContent() accept a raw
|
|
30
|
-
// proto message-type key directly (e.g. `{ conversation: 'hi' }`), bypassing
|
|
31
|
-
// the normal content-type-detection chain below — see the final `else` in
|
|
32
|
-
// generateWAMessageFromContent(). Falls back to prepareWAMessageMedia() as
|
|
33
|
-
// before for anything not in this list.
|
|
34
29
|
const RAW_PROTO_MESSAGE_KEYS = [
|
|
35
30
|
'conversation', 'senderKeyDistributionMessage', 'imageMessage', 'contactMessage', 'locationMessage',
|
|
36
31
|
'extendedTextMessage', 'documentMessage', 'audioMessage', 'videoMessage', 'call', 'chat', 'protocolMessage',
|
|
@@ -887,8 +882,6 @@ export const generateWAMessageContent = async (message, options) => {
|
|
|
887
882
|
};
|
|
888
883
|
}
|
|
889
884
|
else if (hasNonNullishProperty(message, 'stickerPacks')) {
|
|
890
|
-
// Added from baileys@6.2.7: send a whole sticker pack (multiple
|
|
891
|
-
// .webp stickers + a tray icon, zipped) as one stickerPackMessage.
|
|
892
885
|
const pack = message.stickerPacks;
|
|
893
886
|
const rawStickers = Array.isArray(pack.stickers) ? pack.stickers : [];
|
|
894
887
|
if (rawStickers.length === 0) {
|
|
@@ -964,11 +957,6 @@ export const generateWAMessageContent = async (message, options) => {
|
|
|
964
957
|
};
|
|
965
958
|
}
|
|
966
959
|
else if (hasNonNullishProperty(message, 'sticker') && (message.pack || message.author || message.isPrivate || message.premium || message.animated)) {
|
|
967
|
-
// Added from baileys@6.2.7: `{ sticker, pack/author/isPrivate/animated/premium }`
|
|
968
|
-
// auto-converts the given image/video into a proper sticker (via makeSticker)
|
|
969
|
-
// instead of requiring an already-built .webp. Plain `{ sticker: <webp> }`
|
|
970
|
-
// with none of those extra options still falls through to the normal
|
|
971
|
-
// prepareWAMessageMedia path below, unchanged.
|
|
972
960
|
const rawBuffer = await resolveStickerBuffer(message.sticker, options.options);
|
|
973
961
|
const finalWebp = await makeSticker(rawBuffer, {
|
|
974
962
|
pack: message.pack || '',
|
|
@@ -991,8 +979,6 @@ export const generateWAMessageContent = async (message, options) => {
|
|
|
991
979
|
else {
|
|
992
980
|
const rawProtoKey = RAW_PROTO_MESSAGE_KEYS.find(k => hasNonNullishProperty(message, k));
|
|
993
981
|
if (rawProtoKey) {
|
|
994
|
-
// Added from baileys@6.2.7: pass a raw proto message-type key straight
|
|
995
|
-
// through, bypassing content-type detection entirely.
|
|
996
982
|
const built = proto.Message.fromObject({ [rawProtoKey]: message[rawProtoKey] });
|
|
997
983
|
m = { [rawProtoKey]: built[rawProtoKey] };
|
|
998
984
|
}
|