@arcaelas/whatsapp 5.1.0 → 6.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.
- package/README.md +284 -239
- package/build/cjs/decorators.js +0 -1
- package/build/cjs/index.js +0 -1
- package/build/cjs/lib/bot/decorator.js +0 -1
- package/build/cjs/lib/bot/decorators.d.ts +2 -2
- package/build/cjs/lib/bot/decorators.js +4 -4
- package/build/cjs/lib/bot/index.js +0 -1
- package/build/cjs/lib/chat/index.d.ts +41 -0
- package/build/cjs/lib/chat/index.js +27 -8
- package/build/cjs/lib/contact/index.js +0 -1
- package/build/cjs/lib/internal.js +0 -1
- package/build/cjs/lib/message/index.d.ts +10 -0
- package/build/cjs/lib/message/index.js +79 -14
- package/build/cjs/lib/status/index.js +0 -1
- package/build/cjs/lib/store/engine/index.d.ts +14 -0
- package/build/cjs/lib/store/engine/index.js +0 -1
- package/build/cjs/lib/store/engine/lib/file_system/index.d.ts +10 -0
- package/build/cjs/lib/store/engine/lib/file_system/index.js +27 -1
- package/build/cjs/lib/store/engine/lib/index.js +0 -1
- package/build/cjs/lib/store/engine/lib/redis/index.d.ts +18 -4
- package/build/cjs/lib/store/engine/lib/redis/index.js +21 -3
- package/build/cjs/lib/store/engine/lib/s3/index.js +0 -1
- package/build/cjs/lib/store/engine/lib/sqlite/index.d.ts +16 -4
- package/build/cjs/lib/store/engine/lib/sqlite/index.js +24 -6
- package/build/cjs/lib/store/index.js +0 -1
- package/build/cjs/lib/whatsapp/index.d.ts +1 -1
- package/build/cjs/lib/whatsapp/index.js +76 -20
- package/build/esm/decorators.js +0 -1
- package/build/esm/index.js +0 -1
- package/build/esm/lib/bot/decorator.js +0 -1
- package/build/esm/lib/bot/decorators.d.ts +2 -2
- package/build/esm/lib/bot/decorators.js +4 -4
- package/build/esm/lib/bot/index.js +0 -1
- package/build/esm/lib/chat/index.d.ts +41 -0
- package/build/esm/lib/chat/index.js +27 -8
- package/build/esm/lib/contact/index.js +0 -1
- package/build/esm/lib/internal.js +0 -1
- package/build/esm/lib/message/index.d.ts +10 -0
- package/build/esm/lib/message/index.js +79 -14
- package/build/esm/lib/status/index.js +0 -1
- package/build/esm/lib/store/engine/index.d.ts +14 -0
- package/build/esm/lib/store/engine/index.js +0 -1
- package/build/esm/lib/store/engine/lib/file_system/index.d.ts +10 -0
- package/build/esm/lib/store/engine/lib/file_system/index.js +27 -1
- package/build/esm/lib/store/engine/lib/index.js +0 -1
- package/build/esm/lib/store/engine/lib/redis/index.d.ts +18 -4
- package/build/esm/lib/store/engine/lib/redis/index.js +21 -3
- package/build/esm/lib/store/engine/lib/s3/index.js +0 -1
- package/build/esm/lib/store/engine/lib/sqlite/index.d.ts +16 -4
- package/build/esm/lib/store/engine/lib/sqlite/index.js +24 -6
- package/build/esm/lib/store/index.js +0 -1
- package/build/esm/lib/whatsapp/index.d.ts +1 -1
- package/build/esm/lib/whatsapp/index.js +76 -20
- package/package.json +24 -5
- package/build/cjs/decorators.js.map +0 -1
- package/build/cjs/index.js.map +0 -1
- package/build/cjs/lib/bot/decorator.js.map +0 -1
- package/build/cjs/lib/bot/decorators.js.map +0 -1
- package/build/cjs/lib/bot/index.js.map +0 -1
- package/build/cjs/lib/chat/index.js.map +0 -1
- package/build/cjs/lib/contact/index.js.map +0 -1
- package/build/cjs/lib/internal.js.map +0 -1
- package/build/cjs/lib/message/index.js.map +0 -1
- package/build/cjs/lib/status/index.js.map +0 -1
- package/build/cjs/lib/store/engine/index.js.map +0 -1
- package/build/cjs/lib/store/engine/lib/file_system/index.js.map +0 -1
- package/build/cjs/lib/store/engine/lib/index.js.map +0 -1
- package/build/cjs/lib/store/engine/lib/redis/index.js.map +0 -1
- package/build/cjs/lib/store/engine/lib/s3/index.js.map +0 -1
- package/build/cjs/lib/store/engine/lib/sqlite/index.js.map +0 -1
- package/build/cjs/lib/store/index.js.map +0 -1
- package/build/cjs/lib/whatsapp/index.js.map +0 -1
- package/build/cjs/test.d.ts +0 -1
- package/build/cjs/test.js +0 -72
- package/build/cjs/test.js.map +0 -1
- package/build/esm/decorators.js.map +0 -1
- package/build/esm/index.js.map +0 -1
- package/build/esm/lib/bot/decorator.js.map +0 -1
- package/build/esm/lib/bot/decorators.js.map +0 -1
- package/build/esm/lib/bot/index.js.map +0 -1
- package/build/esm/lib/chat/index.js.map +0 -1
- package/build/esm/lib/contact/index.js.map +0 -1
- package/build/esm/lib/internal.js.map +0 -1
- package/build/esm/lib/message/index.js.map +0 -1
- package/build/esm/lib/status/index.js.map +0 -1
- package/build/esm/lib/store/engine/index.js.map +0 -1
- package/build/esm/lib/store/engine/lib/file_system/index.js.map +0 -1
- package/build/esm/lib/store/engine/lib/index.js.map +0 -1
- package/build/esm/lib/store/engine/lib/redis/index.js.map +0 -1
- package/build/esm/lib/store/engine/lib/s3/index.js.map +0 -1
- package/build/esm/lib/store/engine/lib/sqlite/index.js.map +0 -1
- package/build/esm/lib/store/index.js.map +0 -1
- package/build/esm/lib/whatsapp/index.js.map +0 -1
- package/build/esm/test.d.ts +0 -1
- package/build/esm/test.js +0 -67
- package/build/esm/test.js.map +0 -1
|
@@ -33,22 +33,22 @@ export interface SQLiteDatabase {
|
|
|
33
33
|
* resuelven a mano: `list` es un `ORDER BY score DESC LIMIT/OFFSET` sobre el índice (sin
|
|
34
34
|
* índice en memoria ni archivo de orden), `count` un `COUNT(*)` y el `unset` en cascada una
|
|
35
35
|
* sola sentencia por rango de `path`. Medido sobre un chat real de 55.146 mensajes frente al
|
|
36
|
-
* filesystem: 220 MB →
|
|
36
|
+
* filesystem: 220 MB → 64 MB en disco, primer `list` 115 ms → 0,6 ms y dos archivos en total.
|
|
37
37
|
*
|
|
38
38
|
* SQLite persistence driver: a single `(path, parent, score, value)` table with a
|
|
39
39
|
* `(parent, score DESC)` index. The most efficient built-in driver, since it delegates to the
|
|
40
40
|
* engine what the others hand-roll: `list` is an indexed `ORDER BY score DESC LIMIT/OFFSET`
|
|
41
41
|
* (no in-memory index, no order file), `count` a `COUNT(*)` and cascading `unset` a single
|
|
42
42
|
* range statement. Measured on a real 55,146-message chat against the filesystem: 220 MB →
|
|
43
|
-
*
|
|
43
|
+
* 64 MB on disk, first `list` 115 ms → 0.6 ms and two files in total.
|
|
44
44
|
*
|
|
45
45
|
* @example
|
|
46
46
|
* import Database from 'better-sqlite3';
|
|
47
|
-
* const engine = new SQLiteEngine(new Database('.sessions/
|
|
47
|
+
* const engine = new SQLiteEngine(new Database('.sessions/5491112345678.db'));
|
|
48
48
|
*
|
|
49
49
|
* @example
|
|
50
50
|
* import { DatabaseSync } from 'node:sqlite';
|
|
51
|
-
* const engine = new SQLiteEngine(new DatabaseSync('.sessions/
|
|
51
|
+
* const engine = new SQLiteEngine(new DatabaseSync('.sessions/5491112345678.db'));
|
|
52
52
|
*/
|
|
53
53
|
export declare class SQLiteEngine implements Engine {
|
|
54
54
|
private readonly _db;
|
|
@@ -58,6 +58,8 @@ export declare class SQLiteEngine implements Engine {
|
|
|
58
58
|
private readonly _page;
|
|
59
59
|
private readonly _total;
|
|
60
60
|
private readonly _wipe;
|
|
61
|
+
private readonly _read_blob;
|
|
62
|
+
private readonly _write_blob;
|
|
61
63
|
/**
|
|
62
64
|
* @param _db - Base SQLite ya abierta / Already open SQLite database
|
|
63
65
|
* @param table - Tabla donde viven los documentos / Table holding the documents
|
|
@@ -89,6 +91,16 @@ export declare class SQLiteEngine implements Engine {
|
|
|
89
91
|
* Counts direct children.
|
|
90
92
|
*/
|
|
91
93
|
count(path: string): Promise<number>;
|
|
94
|
+
/**
|
|
95
|
+
* Lee el binario del documento, o null si la fila no lo tiene.
|
|
96
|
+
* Reads the document binary, or null when the row has none.
|
|
97
|
+
*/
|
|
98
|
+
get_buffer(path: string): Promise<Buffer | null>;
|
|
99
|
+
/**
|
|
100
|
+
* Escribe el binario del documento como BLOB, sin JSON ni base64.
|
|
101
|
+
* Writes the document binary as a BLOB, with no JSON nor base64.
|
|
102
|
+
*/
|
|
103
|
+
set_buffer(path: string, data: Buffer, score?: number): Promise<void>;
|
|
92
104
|
/**
|
|
93
105
|
* Vacía la tabla completa.
|
|
94
106
|
* Clears the entire table.
|
|
@@ -15,22 +15,22 @@ const lib_1 = require("../../../../../lib/store/engine/lib");
|
|
|
15
15
|
* resuelven a mano: `list` es un `ORDER BY score DESC LIMIT/OFFSET` sobre el índice (sin
|
|
16
16
|
* índice en memoria ni archivo de orden), `count` un `COUNT(*)` y el `unset` en cascada una
|
|
17
17
|
* sola sentencia por rango de `path`. Medido sobre un chat real de 55.146 mensajes frente al
|
|
18
|
-
* filesystem: 220 MB →
|
|
18
|
+
* filesystem: 220 MB → 64 MB en disco, primer `list` 115 ms → 0,6 ms y dos archivos en total.
|
|
19
19
|
*
|
|
20
20
|
* SQLite persistence driver: a single `(path, parent, score, value)` table with a
|
|
21
21
|
* `(parent, score DESC)` index. The most efficient built-in driver, since it delegates to the
|
|
22
22
|
* engine what the others hand-roll: `list` is an indexed `ORDER BY score DESC LIMIT/OFFSET`
|
|
23
23
|
* (no in-memory index, no order file), `count` a `COUNT(*)` and cascading `unset` a single
|
|
24
24
|
* range statement. Measured on a real 55,146-message chat against the filesystem: 220 MB →
|
|
25
|
-
*
|
|
25
|
+
* 64 MB on disk, first `list` 115 ms → 0.6 ms and two files in total.
|
|
26
26
|
*
|
|
27
27
|
* @example
|
|
28
28
|
* import Database from 'better-sqlite3';
|
|
29
|
-
* const engine = new SQLiteEngine(new Database('.sessions/
|
|
29
|
+
* const engine = new SQLiteEngine(new Database('.sessions/5491112345678.db'));
|
|
30
30
|
*
|
|
31
31
|
* @example
|
|
32
32
|
* import { DatabaseSync } from 'node:sqlite';
|
|
33
|
-
* const engine = new SQLiteEngine(new DatabaseSync('.sessions/
|
|
33
|
+
* const engine = new SQLiteEngine(new DatabaseSync('.sessions/5491112345678.db'));
|
|
34
34
|
*/
|
|
35
35
|
class SQLiteEngine {
|
|
36
36
|
/**
|
|
@@ -49,7 +49,7 @@ class SQLiteEngine {
|
|
|
49
49
|
catch {
|
|
50
50
|
/* optional tuning */
|
|
51
51
|
}
|
|
52
|
-
_db.exec(`CREATE TABLE IF NOT EXISTS ${table} (path TEXT PRIMARY KEY, parent TEXT NOT NULL, score INTEGER NOT NULL, value TEXT NOT NULL);
|
|
52
|
+
_db.exec(`CREATE TABLE IF NOT EXISTS ${table} (path TEXT PRIMARY KEY, parent TEXT NOT NULL, score INTEGER NOT NULL, value TEXT NOT NULL DEFAULT '', binary BLOB);
|
|
53
53
|
CREATE INDEX IF NOT EXISTS ${table}_order ON ${table} (parent, score DESC);`);
|
|
54
54
|
this._read = _db.prepare(`SELECT value FROM ${table} WHERE path = ?`);
|
|
55
55
|
this._write = _db.prepare(`INSERT INTO ${table} (path, parent, score, value) VALUES (?, ?, ?, ?)
|
|
@@ -62,6 +62,9 @@ class SQLiteEngine {
|
|
|
62
62
|
this._page = _db.prepare(`SELECT value FROM ${table} WHERE parent = ? ORDER BY score DESC LIMIT ? OFFSET ?`);
|
|
63
63
|
this._total = _db.prepare(`SELECT COUNT(*) AS total FROM ${table} WHERE parent = ?`);
|
|
64
64
|
this._wipe = _db.prepare(`DELETE FROM ${table}`);
|
|
65
|
+
this._read_blob = _db.prepare(`SELECT binary FROM ${table} WHERE path = ?`);
|
|
66
|
+
this._write_blob = _db.prepare(`INSERT INTO ${table} (path, parent, score, value, binary) VALUES (?, ?, ?, '', ?)
|
|
67
|
+
ON CONFLICT(path) DO UPDATE SET binary = excluded.binary, score = excluded.score`);
|
|
65
68
|
}
|
|
66
69
|
/**
|
|
67
70
|
* Lee el valor de un documento.
|
|
@@ -102,6 +105,22 @@ class SQLiteEngine {
|
|
|
102
105
|
async count(path) {
|
|
103
106
|
return Number(this._total.get((0, lib_1.normalize_path)(path)).total);
|
|
104
107
|
}
|
|
108
|
+
/**
|
|
109
|
+
* Lee el binario del documento, o null si la fila no lo tiene.
|
|
110
|
+
* Reads the document binary, or null when the row has none.
|
|
111
|
+
*/
|
|
112
|
+
async get_buffer(path) {
|
|
113
|
+
const row = this._read_blob.get((0, lib_1.normalize_path)(path));
|
|
114
|
+
return row?.binary ? Buffer.from(row.binary) : null;
|
|
115
|
+
}
|
|
116
|
+
/**
|
|
117
|
+
* Escribe el binario del documento como BLOB, sin JSON ni base64.
|
|
118
|
+
* Writes the document binary as a BLOB, with no JSON nor base64.
|
|
119
|
+
*/
|
|
120
|
+
async set_buffer(path, data, score) {
|
|
121
|
+
const { parent } = (0, lib_1.split_path)(path);
|
|
122
|
+
this._write_blob.run((0, lib_1.normalize_path)(path), parent, score ?? Date.now(), data);
|
|
123
|
+
}
|
|
105
124
|
/**
|
|
106
125
|
* Vacía la tabla completa.
|
|
107
126
|
* Clears the entire table.
|
|
@@ -111,4 +130,3 @@ class SQLiteEngine {
|
|
|
111
130
|
}
|
|
112
131
|
}
|
|
113
132
|
exports.SQLiteEngine = SQLiteEngine;
|
|
114
|
-
//# sourceMappingURL=index.js.map
|
|
@@ -111,7 +111,7 @@ interface WhatsAppEventMap {
|
|
|
111
111
|
* Main WhatsApp client. Does not connect on instantiation.
|
|
112
112
|
*
|
|
113
113
|
* @example
|
|
114
|
-
* const wa = new WhatsApp({ engine: new FileSystemEngine(__dirname), phone:
|
|
114
|
+
* const wa = new WhatsApp({ engine: new FileSystemEngine(__dirname), phone: 5491112345678 });
|
|
115
115
|
* wa.on('message:created', (msg) => console.log(msg.caption));
|
|
116
116
|
* await wa.connect((code) => console.log(code));
|
|
117
117
|
*/
|
|
@@ -78,7 +78,7 @@ function sniff_media(data) {
|
|
|
78
78
|
* Main WhatsApp client. Does not connect on instantiation.
|
|
79
79
|
*
|
|
80
80
|
* @example
|
|
81
|
-
* const wa = new WhatsApp({ engine: new FileSystemEngine(__dirname), phone:
|
|
81
|
+
* const wa = new WhatsApp({ engine: new FileSystemEngine(__dirname), phone: 5491112345678 });
|
|
82
82
|
* wa.on('message:created', (msg) => console.log(msg.caption));
|
|
83
83
|
* await wa.connect((code) => console.log(code));
|
|
84
84
|
*/
|
|
@@ -154,6 +154,19 @@ class WhatsApp {
|
|
|
154
154
|
}
|
|
155
155
|
return null;
|
|
156
156
|
}
|
|
157
|
+
/**
|
|
158
|
+
* @internal
|
|
159
|
+
* Persiste un binario: crudo cuando el driver lo soporta, JSON con base64 si no.
|
|
160
|
+
* Persists a binary: raw when the driver supports it, base64 JSON otherwise.
|
|
161
|
+
*/
|
|
162
|
+
async #write_content(path, data) {
|
|
163
|
+
if (this.engine.set_buffer) {
|
|
164
|
+
await this.engine.set_buffer(path, data);
|
|
165
|
+
}
|
|
166
|
+
else {
|
|
167
|
+
await this.engine.set(path, (0, store_1.serialize)({ data: data.toString('base64') }));
|
|
168
|
+
}
|
|
169
|
+
}
|
|
157
170
|
/** @internal Encola una tarea en la cadena serial de handlers. / Queues a task on the serial handler chain. */
|
|
158
171
|
#enqueue(task) {
|
|
159
172
|
this.#chain = this.#chain.then(task).catch(() => { });
|
|
@@ -208,12 +221,15 @@ class WhatsApp {
|
|
|
208
221
|
result = uid;
|
|
209
222
|
}
|
|
210
223
|
else if (uid.endsWith('@lid')) {
|
|
211
|
-
|
|
224
|
+
// Los receipts direccionan por dispositivo (`…:9@lid`); el índice se guarda sin él.
|
|
225
|
+
// Receipts address per device (`…:9@lid`); the index is stored without it.
|
|
226
|
+
const lid = (0, baileys_1.jidNormalizedUser)(uid);
|
|
227
|
+
const direct = (0, store_1.deserialize)(await this.engine.get(`/lid/${lid}`));
|
|
212
228
|
if (direct) {
|
|
213
229
|
result = direct.includes('@') ? direct : `${direct}@s.whatsapp.net`;
|
|
214
230
|
}
|
|
215
231
|
else {
|
|
216
|
-
const reverse = (0, store_1.deserialize)(await this.engine.get(`/lid/${
|
|
232
|
+
const reverse = (0, store_1.deserialize)(await this.engine.get(`/lid/${lid.split('@')[0]}_reverse`));
|
|
217
233
|
if (reverse != null) {
|
|
218
234
|
result = `${reverse}@s.whatsapp.net`;
|
|
219
235
|
}
|
|
@@ -222,7 +238,7 @@ class WhatsApp {
|
|
|
222
238
|
// baileys lo conoce vía su lidMapping. Sin esto, un chat referenciado por @lid
|
|
223
239
|
// (p.ej. el pollCreationMessageKey de un voto entrante) no resuelve al PN donde
|
|
224
240
|
// realmente está guardado, y el mensaje/poll no se encuentra.
|
|
225
|
-
const pn = await this.#internals.socket?.signalRepository?.lidMapping?.getPNForLID(
|
|
241
|
+
const pn = await this.#internals.socket?.signalRepository?.lidMapping?.getPNForLID(lid).catch(() => null);
|
|
226
242
|
if (pn) {
|
|
227
243
|
// getPNForLID puede traer sufijo de dispositivo (`:0`); se normaliza para
|
|
228
244
|
// que el JID coincida con el que usa el store (sin device).
|
|
@@ -462,7 +478,7 @@ class WhatsApp {
|
|
|
462
478
|
};
|
|
463
479
|
await this.engine.set(`/status/${doc.id}`, (0, store_1.serialize)(doc), created_at);
|
|
464
480
|
if (post.content) {
|
|
465
|
-
await this
|
|
481
|
+
await this.#write_content(`/status/${doc.id}/content`, post.content);
|
|
466
482
|
}
|
|
467
483
|
result = new status_1.Feed(this, doc);
|
|
468
484
|
this.emit('feed:created', result, this);
|
|
@@ -627,6 +643,7 @@ class WhatsApp {
|
|
|
627
643
|
archived: ch.archived ?? null,
|
|
628
644
|
pinned: ch.pinned ?? null,
|
|
629
645
|
mute_end_time: ch.muteEndTime != null ? Number(ch.muteEndTime) : null,
|
|
646
|
+
unread_count: ch.unreadCount ?? null,
|
|
630
647
|
};
|
|
631
648
|
if (ch.name) {
|
|
632
649
|
raw.name = ch.name;
|
|
@@ -662,6 +679,9 @@ class WhatsApp {
|
|
|
662
679
|
if (mute_changed) {
|
|
663
680
|
patch.mute_end_time = ch.muteEndTime != null ? Number(ch.muteEndTime) : null;
|
|
664
681
|
}
|
|
682
|
+
if (ch.unreadCount != null) {
|
|
683
|
+
patch.unread_count = ch.unreadCount;
|
|
684
|
+
}
|
|
665
685
|
if (Object.keys(patch).length > 0) {
|
|
666
686
|
const merged = { ...current, ...patch };
|
|
667
687
|
await this.engine.set(`/chat/${ch.id}`, (0, store_1.serialize)(merged));
|
|
@@ -702,13 +722,14 @@ class WhatsApp {
|
|
|
702
722
|
continue;
|
|
703
723
|
}
|
|
704
724
|
if (key.remoteJid && key.id && (receipt.readTimestamp != null || receipt.playedTimestamp != null)) {
|
|
705
|
-
const
|
|
706
|
-
if (
|
|
725
|
+
const found = await this.#locate(key.remoteJid, key.id);
|
|
726
|
+
if (found) {
|
|
727
|
+
const { path, doc } = found;
|
|
707
728
|
const next = receipt.playedTimestamp != null ? PLAYED : READ;
|
|
708
729
|
if (doc.status < next) {
|
|
709
730
|
doc.status = next;
|
|
710
731
|
doc.raw.status = next;
|
|
711
|
-
await this.engine.set(
|
|
732
|
+
await this.engine.set(path, (0, store_1.serialize)(doc), doc.created_at);
|
|
712
733
|
}
|
|
713
734
|
const msg_instance = (0, message_1.message)(this, doc);
|
|
714
735
|
this.emit('message:seen', msg_instance, await msg_instance.chat(), this);
|
|
@@ -811,7 +832,7 @@ class WhatsApp {
|
|
|
811
832
|
}
|
|
812
833
|
await this.engine.set(`/status/${mid}`, (0, store_1.serialize)(feed_raw));
|
|
813
834
|
if (content_buf.length > 0) {
|
|
814
|
-
await this
|
|
835
|
+
await this.#write_content(`/status/${mid}/content`, content_buf);
|
|
815
836
|
}
|
|
816
837
|
this.emit('feed:created', new status_1.Feed(this, feed_raw), this);
|
|
817
838
|
continue;
|
|
@@ -999,7 +1020,7 @@ class WhatsApp {
|
|
|
999
1020
|
}
|
|
1000
1021
|
}
|
|
1001
1022
|
if (content_buf.length > 0) {
|
|
1002
|
-
await this
|
|
1023
|
+
await this.#write_content(`/chat/${cid}/message/${mid}/content`, content_buf);
|
|
1003
1024
|
}
|
|
1004
1025
|
const instance = (0, message_1.message)(this, doc);
|
|
1005
1026
|
const chat_instance = await instance.chat();
|
|
@@ -1010,6 +1031,33 @@ class WhatsApp {
|
|
|
1010
1031
|
}
|
|
1011
1032
|
}
|
|
1012
1033
|
}
|
|
1034
|
+
/**
|
|
1035
|
+
* Ubica el documento de un mensaje partiendo del chat crudo del key. Los updates y
|
|
1036
|
+
* receipts llegan direccionados por LID —con o sin dispositivo— mientras el documento
|
|
1037
|
+
* vive bajo el JID con el que se guardó, así que se prueban ambas formas.
|
|
1038
|
+
* Locates a message document from the raw chat in the key. Updates and receipts arrive
|
|
1039
|
+
* LID-addressed —with or without device— while the document lives under the JID it was
|
|
1040
|
+
* stored with, so both forms are tried.
|
|
1041
|
+
*
|
|
1042
|
+
* @param cid - Chat tal como viene en el key / Chat as it comes in the key
|
|
1043
|
+
* @param mid - Identificador del mensaje / Message identifier
|
|
1044
|
+
* @returns Ruta y documento, o null si no existe / Path and document, or null when missing
|
|
1045
|
+
* @internal
|
|
1046
|
+
*/
|
|
1047
|
+
async #locate(cid, mid) {
|
|
1048
|
+
const tried = new Set();
|
|
1049
|
+
for (const candidate of [await this.#resolve_jid(cid), cid, (0, baileys_1.jidNormalizedUser)(cid)]) {
|
|
1050
|
+
if (candidate && !tried.has(candidate)) {
|
|
1051
|
+
tried.add(candidate);
|
|
1052
|
+
const path = `/chat/${candidate}/message/${mid}`;
|
|
1053
|
+
const doc = (0, store_1.deserialize)(await this.engine.get(path));
|
|
1054
|
+
if (doc) {
|
|
1055
|
+
return { path, doc };
|
|
1056
|
+
}
|
|
1057
|
+
}
|
|
1058
|
+
}
|
|
1059
|
+
return null;
|
|
1060
|
+
}
|
|
1013
1061
|
/** @internal */
|
|
1014
1062
|
async #handle_messages_update(updates) {
|
|
1015
1063
|
for (const { key, update: upd } of updates) {
|
|
@@ -1021,8 +1069,9 @@ class WhatsApp {
|
|
|
1021
1069
|
if (key.remoteJid === 'status@broadcast') {
|
|
1022
1070
|
continue;
|
|
1023
1071
|
}
|
|
1024
|
-
const
|
|
1025
|
-
if (
|
|
1072
|
+
const found = await this.#locate(key.remoteJid, key.id);
|
|
1073
|
+
if (found) {
|
|
1074
|
+
const { path, doc } = found;
|
|
1026
1075
|
const raw = doc.raw ?? { key };
|
|
1027
1076
|
const upd_any = upd;
|
|
1028
1077
|
const edited_message = upd_any.message?.editedMessage?.message;
|
|
@@ -1034,7 +1083,7 @@ class WhatsApp {
|
|
|
1034
1083
|
doc.raw = raw;
|
|
1035
1084
|
doc.edited = true;
|
|
1036
1085
|
doc.caption = (0, message_1.message)(this, raw).caption;
|
|
1037
|
-
await this.engine.set(
|
|
1086
|
+
await this.engine.set(path, (0, store_1.serialize)(doc), doc.created_at);
|
|
1038
1087
|
const msg_instance = (0, message_1.message)(this, doc);
|
|
1039
1088
|
this.emit('message:updated', msg_instance, await msg_instance.chat(), this);
|
|
1040
1089
|
}
|
|
@@ -1043,7 +1092,7 @@ class WhatsApp {
|
|
|
1043
1092
|
raw.message = { ...raw.message, ...content_update };
|
|
1044
1093
|
doc.raw = raw;
|
|
1045
1094
|
doc.caption = (0, message_1.message)(this, raw).caption;
|
|
1046
|
-
await this.engine.set(
|
|
1095
|
+
await this.engine.set(path, (0, store_1.serialize)(doc), doc.created_at);
|
|
1047
1096
|
const msg_instance = (0, message_1.message)(this, doc);
|
|
1048
1097
|
this.emit('message:updated', msg_instance, await msg_instance.chat(), this);
|
|
1049
1098
|
}
|
|
@@ -1051,15 +1100,22 @@ class WhatsApp {
|
|
|
1051
1100
|
doc.starred = upd_any.starred === true;
|
|
1052
1101
|
raw.starred = doc.starred;
|
|
1053
1102
|
doc.raw = raw;
|
|
1054
|
-
await this.engine.set(
|
|
1103
|
+
await this.engine.set(path, (0, store_1.serialize)(doc), doc.created_at);
|
|
1055
1104
|
const msg_instance = (0, message_1.message)(this, doc);
|
|
1056
1105
|
this.emit(doc.starred ? 'message:starred' : 'message:unstarred', msg_instance, await msg_instance.chat(), this);
|
|
1057
1106
|
}
|
|
1058
1107
|
else if (status !== undefined) {
|
|
1059
1108
|
raw.status = status;
|
|
1060
1109
|
doc.status = status;
|
|
1110
|
+
// El rechazo del servidor viaja como stub del update; sin persistirlo el
|
|
1111
|
+
// mensaje queda en error sin decir por qué.
|
|
1112
|
+
// The server rejection travels as an update stub; without persisting it the
|
|
1113
|
+
// message stays in error without saying why.
|
|
1114
|
+
if (upd_any.messageStubParameters) {
|
|
1115
|
+
raw.messageStubParameters = upd_any.messageStubParameters;
|
|
1116
|
+
}
|
|
1061
1117
|
doc.raw = raw;
|
|
1062
|
-
await this.engine.set(
|
|
1118
|
+
await this.engine.set(path, (0, store_1.serialize)(doc), doc.created_at);
|
|
1063
1119
|
const msg_instance = (0, message_1.message)(this, doc);
|
|
1064
1120
|
this.emit('message:updated', msg_instance, await msg_instance.chat(), this);
|
|
1065
1121
|
}
|
|
@@ -1080,15 +1136,16 @@ class WhatsApp {
|
|
|
1080
1136
|
}
|
|
1081
1137
|
continue;
|
|
1082
1138
|
}
|
|
1083
|
-
const
|
|
1084
|
-
if (
|
|
1139
|
+
const found = await this.#locate(key.remoteJid, key.id);
|
|
1140
|
+
if (found) {
|
|
1141
|
+
const { path, doc } = found;
|
|
1085
1142
|
const reactor = (0, baileys_1.jidNormalizedUser)(key.participant ?? key.remoteJid);
|
|
1086
1143
|
const emoji = reaction.text ?? '';
|
|
1087
1144
|
doc.reactions = [
|
|
1088
1145
|
...(doc.reactions ?? []).filter((r) => r.author !== reactor),
|
|
1089
1146
|
...(emoji ? [{ author: reactor, emoji, at: Date.now() }] : []),
|
|
1090
1147
|
];
|
|
1091
|
-
await this.engine.set(
|
|
1148
|
+
await this.engine.set(path, (0, store_1.serialize)(doc), doc.created_at);
|
|
1092
1149
|
const msg_instance = (0, message_1.message)(this, doc);
|
|
1093
1150
|
this.emit('message:reacted', msg_instance, await msg_instance.chat(), reaction.text ?? '', this);
|
|
1094
1151
|
}
|
|
@@ -1098,4 +1155,3 @@ class WhatsApp {
|
|
|
1098
1155
|
}
|
|
1099
1156
|
exports.WhatsApp = WhatsApp;
|
|
1100
1157
|
exports.default = WhatsApp;
|
|
1101
|
-
//# sourceMappingURL=index.js.map
|
package/build/esm/decorators.js
CHANGED
package/build/esm/index.js
CHANGED
|
@@ -9,4 +9,3 @@ export { Contact, contact } from './lib/contact/index.js';
|
|
|
9
9
|
export { Chat, chat } from './lib/chat/index.js';
|
|
10
10
|
export { Message, message, Text, Image, Video, Audio, Sticker, Document, Location, Poll, VCard, Event } from './lib/message/index.js';
|
|
11
11
|
export { Feed, TTL_MS as FEED_TTL_MS } from './lib/status/index.js';
|
|
12
|
-
//# sourceMappingURL=index.js.map
|
|
@@ -82,9 +82,9 @@ export declare function delay(ms: number): (_value: unknown, context: ClassMetho
|
|
|
82
82
|
export declare const pair: () => (_value: unknown, context: ClassMethodDecoratorContext) => void;
|
|
83
83
|
/**
|
|
84
84
|
* Filtra por autor del mensaje. Acepta JID, LID o teléfono numérico; el string
|
|
85
|
-
* se normaliza
|
|
85
|
+
* se normaliza con la resolución interna del cliente y cache lazy. Un array produce match OR.
|
|
86
86
|
* Filters by message author. Accepts JID, LID or numeric phone; the string is
|
|
87
|
-
* normalized
|
|
87
|
+
* normalized through the client's internal resolution with lazy cache. An array yields OR matching.
|
|
88
88
|
*
|
|
89
89
|
* @param source - Identificador(es) permitidos o predicate sobre `msg.from` / Allowed identifier(s) or predicate on `msg.from`
|
|
90
90
|
*/
|
|
@@ -3,6 +3,7 @@
|
|
|
3
3
|
* @description Decoradores públicos del bot.
|
|
4
4
|
* Public bot decorators.
|
|
5
5
|
*/
|
|
6
|
+
import { internals } from '../../lib/internal.js';
|
|
6
7
|
import { WhatsAppBot, decorator, register_workflow_step, resolve, } from './decorator.js';
|
|
7
8
|
/**
|
|
8
9
|
* Registra el método como listener del evento indicado. Apilable: varios `@on`
|
|
@@ -114,9 +115,9 @@ export const pair = decorator((_meta, h) => {
|
|
|
114
115
|
});
|
|
115
116
|
/**
|
|
116
117
|
* Filtra por autor del mensaje. Acepta JID, LID o teléfono numérico; el string
|
|
117
|
-
* se normaliza
|
|
118
|
+
* se normaliza con la resolución interna del cliente y cache lazy. Un array produce match OR.
|
|
118
119
|
* Filters by message author. Accepts JID, LID or numeric phone; the string is
|
|
119
|
-
* normalized
|
|
120
|
+
* normalized through the client's internal resolution with lazy cache. An array yields OR matching.
|
|
120
121
|
*
|
|
121
122
|
* @param source - Identificador(es) permitidos o predicate sobre `msg.from` / Allowed identifier(s) or predicate on `msg.from`
|
|
122
123
|
*/
|
|
@@ -134,7 +135,7 @@ export function from(source) {
|
|
|
134
135
|
if (resolved === null) {
|
|
135
136
|
resolved = new Set();
|
|
136
137
|
for (const raw of list) {
|
|
137
|
-
const r = await wa.
|
|
138
|
+
const r = await internals(wa).resolve_jid(raw);
|
|
138
139
|
if (r) {
|
|
139
140
|
resolved.add(r);
|
|
140
141
|
}
|
|
@@ -247,4 +248,3 @@ export function command(pattern) {
|
|
|
247
248
|
});
|
|
248
249
|
};
|
|
249
250
|
}
|
|
250
|
-
//# sourceMappingURL=decorators.js.map
|
|
@@ -16,6 +16,7 @@ export declare class Chat {
|
|
|
16
16
|
archived?: boolean | null;
|
|
17
17
|
pinned?: number | null;
|
|
18
18
|
mute_end_time?: number | null;
|
|
19
|
+
unread_count?: number | null;
|
|
19
20
|
};
|
|
20
21
|
/**
|
|
21
22
|
* @internal Documento crudo del chat. `id` es el identificador del engine (JID, LID o
|
|
@@ -29,6 +30,7 @@ export declare class Chat {
|
|
|
29
30
|
archived?: boolean | null;
|
|
30
31
|
pinned?: number | null;
|
|
31
32
|
mute_end_time?: number | null;
|
|
33
|
+
unread_count?: number | null;
|
|
32
34
|
});
|
|
33
35
|
/** Teléfono del contacto, o el identificador crudo en grupos y LIDs. / Contact phone, or the raw identifier for groups and LIDs. */
|
|
34
36
|
get id(): string;
|
|
@@ -40,6 +42,8 @@ export declare class Chat {
|
|
|
40
42
|
get archived(): boolean;
|
|
41
43
|
/** true si el chat está fijado. / true when the chat is pinned. */
|
|
42
44
|
get pinned(): boolean;
|
|
45
|
+
/** Mensajes sin leer del chat. / Chat's unread messages. */
|
|
46
|
+
get count(): number;
|
|
43
47
|
/** Fecha ISO UTC hasta la que el chat está silenciado, o null. / ISO UTC date until the chat stays muted, or null. */
|
|
44
48
|
get muted(): string | null;
|
|
45
49
|
}
|
|
@@ -56,6 +60,7 @@ export declare function chat(wa: WhatsApp): {
|
|
|
56
60
|
archived?: boolean | null;
|
|
57
61
|
pinned?: number | null;
|
|
58
62
|
mute_end_time?: number | null;
|
|
63
|
+
unread_count?: number | null;
|
|
59
64
|
}): {
|
|
60
65
|
/**
|
|
61
66
|
* Participantes del chat: los integrantes en grupos, el contacto y yo en 1:1.
|
|
@@ -66,6 +71,15 @@ export declare function chat(wa: WhatsApp): {
|
|
|
66
71
|
* @returns Página de contactos / Contact page
|
|
67
72
|
*/
|
|
68
73
|
members(offset?: number, limit?: number): Promise<InstanceType<typeof wa.Contact>[]>;
|
|
74
|
+
/**
|
|
75
|
+
* Descripción del chat: el asunto del grupo o, en un 1:1, la bio del contacto.
|
|
76
|
+
* Es asíncrono porque ninguno de los dos vive en el documento del chat.
|
|
77
|
+
* Chat description: the group's subject or, on a 1:1, the contact's bio. It is async
|
|
78
|
+
* because neither of them lives in the chat document.
|
|
79
|
+
*
|
|
80
|
+
* @returns Descripción, o cadena vacía si no hay / Description, or an empty string when absent
|
|
81
|
+
*/
|
|
82
|
+
content(): Promise<string>;
|
|
69
83
|
/**
|
|
70
84
|
* Mensajes del chat paginados desde el más reciente.
|
|
71
85
|
* Chat messages paginated from the most recent one.
|
|
@@ -146,6 +160,7 @@ export declare function chat(wa: WhatsApp): {
|
|
|
146
160
|
archived?: boolean | null;
|
|
147
161
|
pinned?: number | null;
|
|
148
162
|
mute_end_time?: number | null;
|
|
163
|
+
unread_count?: number | null;
|
|
149
164
|
};
|
|
150
165
|
/** Teléfono del contacto, o el identificador crudo en grupos y LIDs. / Contact phone, or the raw identifier for groups and LIDs. */
|
|
151
166
|
get id(): string;
|
|
@@ -157,6 +172,8 @@ export declare function chat(wa: WhatsApp): {
|
|
|
157
172
|
get archived(): boolean;
|
|
158
173
|
/** true si el chat está fijado. / true when the chat is pinned. */
|
|
159
174
|
get pinned(): boolean;
|
|
175
|
+
/** Mensajes sin leer del chat. / Chat's unread messages. */
|
|
176
|
+
get count(): number;
|
|
160
177
|
/** Fecha ISO UTC hasta la que el chat está silenciado, o null. / ISO UTC date until the chat stays muted, or null. */
|
|
161
178
|
get muted(): string | null;
|
|
162
179
|
};
|
|
@@ -179,6 +196,15 @@ export declare function chat(wa: WhatsApp): {
|
|
|
179
196
|
* @returns Página de contactos / Contact page
|
|
180
197
|
*/
|
|
181
198
|
members(offset?: number, limit?: number): Promise<InstanceType<typeof wa.Contact>[]>;
|
|
199
|
+
/**
|
|
200
|
+
* Descripción del chat: el asunto del grupo o, en un 1:1, la bio del contacto.
|
|
201
|
+
* Es asíncrono porque ninguno de los dos vive en el documento del chat.
|
|
202
|
+
* Chat description: the group's subject or, on a 1:1, the contact's bio. It is async
|
|
203
|
+
* because neither of them lives in the chat document.
|
|
204
|
+
*
|
|
205
|
+
* @returns Descripción, o cadena vacía si no hay / Description, or an empty string when absent
|
|
206
|
+
*/
|
|
207
|
+
content(): Promise<string>;
|
|
182
208
|
/**
|
|
183
209
|
* Mensajes del chat paginados desde el más reciente.
|
|
184
210
|
* Chat messages paginated from the most recent one.
|
|
@@ -259,6 +285,7 @@ export declare function chat(wa: WhatsApp): {
|
|
|
259
285
|
archived?: boolean | null;
|
|
260
286
|
pinned?: number | null;
|
|
261
287
|
mute_end_time?: number | null;
|
|
288
|
+
unread_count?: number | null;
|
|
262
289
|
};
|
|
263
290
|
/** Teléfono del contacto, o el identificador crudo en grupos y LIDs. / Contact phone, or the raw identifier for groups and LIDs. */
|
|
264
291
|
get id(): string;
|
|
@@ -270,6 +297,8 @@ export declare function chat(wa: WhatsApp): {
|
|
|
270
297
|
get archived(): boolean;
|
|
271
298
|
/** true si el chat está fijado. / true when the chat is pinned. */
|
|
272
299
|
get pinned(): boolean;
|
|
300
|
+
/** Mensajes sin leer del chat. / Chat's unread messages. */
|
|
301
|
+
get count(): number;
|
|
273
302
|
/** Fecha ISO UTC hasta la que el chat está silenciado, o null. / ISO UTC date until the chat stays muted, or null. */
|
|
274
303
|
get muted(): string | null;
|
|
275
304
|
} | null>;
|
|
@@ -291,6 +320,15 @@ export declare function chat(wa: WhatsApp): {
|
|
|
291
320
|
* @returns Página de contactos / Contact page
|
|
292
321
|
*/
|
|
293
322
|
members(offset?: number, limit?: number): Promise<InstanceType<typeof wa.Contact>[]>;
|
|
323
|
+
/**
|
|
324
|
+
* Descripción del chat: el asunto del grupo o, en un 1:1, la bio del contacto.
|
|
325
|
+
* Es asíncrono porque ninguno de los dos vive en el documento del chat.
|
|
326
|
+
* Chat description: the group's subject or, on a 1:1, the contact's bio. It is async
|
|
327
|
+
* because neither of them lives in the chat document.
|
|
328
|
+
*
|
|
329
|
+
* @returns Descripción, o cadena vacía si no hay / Description, or an empty string when absent
|
|
330
|
+
*/
|
|
331
|
+
content(): Promise<string>;
|
|
294
332
|
/**
|
|
295
333
|
* Mensajes del chat paginados desde el más reciente.
|
|
296
334
|
* Chat messages paginated from the most recent one.
|
|
@@ -371,6 +409,7 @@ export declare function chat(wa: WhatsApp): {
|
|
|
371
409
|
archived?: boolean | null;
|
|
372
410
|
pinned?: number | null;
|
|
373
411
|
mute_end_time?: number | null;
|
|
412
|
+
unread_count?: number | null;
|
|
374
413
|
};
|
|
375
414
|
/** Teléfono del contacto, o el identificador crudo en grupos y LIDs. / Contact phone, or the raw identifier for groups and LIDs. */
|
|
376
415
|
get id(): string;
|
|
@@ -382,6 +421,8 @@ export declare function chat(wa: WhatsApp): {
|
|
|
382
421
|
get archived(): boolean;
|
|
383
422
|
/** true si el chat está fijado. / true when the chat is pinned. */
|
|
384
423
|
get pinned(): boolean;
|
|
424
|
+
/** Mensajes sin leer del chat. / Chat's unread messages. */
|
|
425
|
+
get count(): number;
|
|
385
426
|
/** Fecha ISO UTC hasta la que el chat está silenciado, o null. / ISO UTC date until the chat stays muted, or null. */
|
|
386
427
|
get muted(): string | null;
|
|
387
428
|
}[]>;
|
|
@@ -40,6 +40,10 @@ export class Chat {
|
|
|
40
40
|
get pinned() {
|
|
41
41
|
return this._raw.pinned != null;
|
|
42
42
|
}
|
|
43
|
+
/** Mensajes sin leer del chat. / Chat's unread messages. */
|
|
44
|
+
get count() {
|
|
45
|
+
return this._raw.unread_count ?? 0;
|
|
46
|
+
}
|
|
43
47
|
/** Fecha ISO UTC hasta la que el chat está silenciado, o null. / ISO UTC date until the chat stays muted, or null. */
|
|
44
48
|
get muted() {
|
|
45
49
|
const end = this._raw.mute_end_time;
|
|
@@ -90,14 +94,14 @@ export function chat(wa) {
|
|
|
90
94
|
* Participantes del grupo memoizados 15s, para no repetir `groupMetadata` en cada página.
|
|
91
95
|
* Group participants memoized for 15s, avoiding a `groupMetadata` round-trip per page.
|
|
92
96
|
*/
|
|
93
|
-
const
|
|
94
|
-
function
|
|
95
|
-
if (!
|
|
97
|
+
const groups_cache = new Map();
|
|
98
|
+
function group_meta(jid) {
|
|
99
|
+
if (!groups_cache.has(jid)) {
|
|
96
100
|
const socket = internals(wa).socket;
|
|
97
|
-
|
|
98
|
-
setTimeout(() =>
|
|
101
|
+
groups_cache.set(jid, socket ? socket.groupMetadata(jid).catch(() => ({ participants: [] })) : Promise.resolve({ participants: [] }));
|
|
102
|
+
setTimeout(() => groups_cache.delete(jid), 15_000);
|
|
99
103
|
}
|
|
100
|
-
return
|
|
104
|
+
return groups_cache.get(jid);
|
|
101
105
|
}
|
|
102
106
|
/**
|
|
103
107
|
* Contacto desde el engine, o ficha mínima local cuando no está persistido (sin red).
|
|
@@ -118,10 +122,24 @@ export function chat(wa) {
|
|
|
118
122
|
*/
|
|
119
123
|
async members(offset = 0, limit = 50) {
|
|
120
124
|
const ids = this.type === 'group'
|
|
121
|
-
? (await
|
|
125
|
+
? (await group_meta(this._raw.id)).participants.map((participant) => participant.id)
|
|
122
126
|
: [this._raw.id, internals(wa).socket?.user?.id].filter((id) => Boolean(id));
|
|
123
127
|
return Promise.all(ids.slice(offset, offset + limit).map(load_contact));
|
|
124
128
|
}
|
|
129
|
+
/**
|
|
130
|
+
* Descripción del chat: el asunto del grupo o, en un 1:1, la bio del contacto.
|
|
131
|
+
* Es asíncrono porque ninguno de los dos vive en el documento del chat.
|
|
132
|
+
* Chat description: the group's subject or, on a 1:1, the contact's bio. It is async
|
|
133
|
+
* because neither of them lives in the chat document.
|
|
134
|
+
*
|
|
135
|
+
* @returns Descripción, o cadena vacía si no hay / Description, or an empty string when absent
|
|
136
|
+
*/
|
|
137
|
+
async content() {
|
|
138
|
+
if (this.type === 'group') {
|
|
139
|
+
return (await group_meta(this._raw.id)).desc ?? '';
|
|
140
|
+
}
|
|
141
|
+
return deserialize(await wa.engine.get(`/contact/${this._raw.id}`))?.status ?? '';
|
|
142
|
+
}
|
|
125
143
|
/**
|
|
126
144
|
* Mensajes del chat paginados desde el más reciente.
|
|
127
145
|
* Chat messages paginated from the most recent one.
|
|
@@ -237,6 +255,8 @@ export function chat(wa) {
|
|
|
237
255
|
const socket = internals(wa).socket;
|
|
238
256
|
if (socket) {
|
|
239
257
|
await socket.chatModify({ markRead: true, lastMessages: await last_messages(this._raw.id) }, this._raw.id);
|
|
258
|
+
this._raw.unread_count = 0;
|
|
259
|
+
await wa.engine.set(`/chat/${this._raw.id}`, serialize(this._raw));
|
|
240
260
|
ok = true;
|
|
241
261
|
}
|
|
242
262
|
return ok;
|
|
@@ -318,4 +338,3 @@ export function chat(wa) {
|
|
|
318
338
|
}
|
|
319
339
|
};
|
|
320
340
|
}
|
|
321
|
-
//# sourceMappingURL=index.js.map
|