@arcaelas/whatsapp 4.5.0 → 6.0.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 +282 -239
- package/build/cjs/decorators.js +0 -1
- package/build/cjs/index.d.ts +6 -4
- package/build/cjs/index.js +22 -3
- package/build/cjs/lib/bot/decorator.d.ts +2 -0
- package/build/cjs/lib/bot/decorator.js +6 -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 +381 -204
- package/build/cjs/lib/chat/index.js +243 -212
- package/build/cjs/lib/contact/index.d.ts +162 -179
- package/build/cjs/lib/contact/index.js +96 -191
- package/build/cjs/lib/internal.d.ts +40 -0
- package/build/cjs/lib/internal.js +38 -0
- package/build/cjs/lib/message/index.d.ts +468 -289
- package/build/cjs/lib/message/index.js +871 -743
- package/build/cjs/lib/status/index.d.ts +60 -70
- package/build/cjs/lib/status/index.js +106 -112
- package/build/cjs/lib/store/engine/index.d.ts +35 -7
- package/build/cjs/lib/store/engine/index.js +6 -4
- package/build/cjs/lib/store/engine/lib/file_system/index.d.ts +48 -8
- package/build/cjs/lib/store/engine/lib/file_system/index.js +117 -66
- package/build/cjs/lib/store/engine/lib/index.d.ts +112 -0
- package/build/cjs/lib/store/engine/lib/index.js +212 -0
- package/build/cjs/lib/store/engine/lib/redis/index.d.ts +44 -15
- package/build/cjs/lib/store/engine/lib/redis/index.js +75 -43
- package/build/cjs/lib/store/engine/lib/s3/index.d.ts +44 -1
- package/build/cjs/lib/store/engine/lib/s3/index.js +112 -65
- package/build/cjs/lib/store/engine/lib/sqlite/index.d.ts +110 -0
- package/build/cjs/lib/store/engine/lib/sqlite/index.js +132 -0
- package/build/cjs/lib/store/index.d.ts +7 -3
- package/build/cjs/lib/store/index.js +14 -5
- package/build/cjs/lib/whatsapp/index.d.ts +106 -68
- package/build/cjs/lib/whatsapp/index.js +393 -167
- package/build/esm/decorators.js +0 -1
- package/build/esm/index.d.ts +6 -4
- package/build/esm/index.js +5 -3
- package/build/esm/lib/bot/decorator.d.ts +2 -0
- package/build/esm/lib/bot/decorator.js +6 -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 +381 -204
- package/build/esm/lib/chat/index.js +243 -212
- package/build/esm/lib/contact/index.d.ts +162 -179
- package/build/esm/lib/contact/index.js +96 -190
- package/build/esm/lib/internal.d.ts +40 -0
- package/build/esm/lib/internal.js +34 -0
- package/build/esm/lib/message/index.d.ts +468 -289
- package/build/esm/lib/message/index.js +866 -738
- package/build/esm/lib/status/index.d.ts +60 -70
- package/build/esm/lib/status/index.js +105 -111
- package/build/esm/lib/store/engine/index.d.ts +35 -7
- package/build/esm/lib/store/engine/index.js +4 -3
- package/build/esm/lib/store/engine/lib/file_system/index.d.ts +48 -8
- package/build/esm/lib/store/engine/lib/file_system/index.js +117 -66
- package/build/esm/lib/store/engine/lib/index.d.ts +112 -0
- package/build/esm/lib/store/engine/lib/index.js +205 -0
- package/build/esm/lib/store/engine/lib/redis/index.d.ts +44 -15
- package/build/esm/lib/store/engine/lib/redis/index.js +73 -41
- package/build/esm/lib/store/engine/lib/s3/index.d.ts +44 -1
- package/build/esm/lib/store/engine/lib/s3/index.js +111 -64
- package/build/esm/lib/store/engine/lib/sqlite/index.d.ts +110 -0
- package/build/esm/lib/store/engine/lib/sqlite/index.js +128 -0
- package/build/esm/lib/store/index.d.ts +7 -3
- package/build/esm/lib/store/index.js +13 -5
- package/build/esm/lib/whatsapp/index.d.ts +106 -68
- package/build/esm/lib/whatsapp/index.js +395 -169
- package/package.json +25 -6
- 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/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/redis/index.js.map +0 -1
- package/build/cjs/lib/store/engine/lib/s3/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 -71
- 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/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/redis/index.js.map +0 -1
- package/build/esm/lib/store/engine/lib/s3/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 -66
- package/build/esm/test.js.map +0 -1
|
@@ -1,33 +1,30 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
/**
|
|
3
3
|
* @file message/index.ts
|
|
4
|
-
* @description Entidad Message —
|
|
5
|
-
*
|
|
4
|
+
* @description Entidad Message — clase base con getters puros y subclases por tipo
|
|
5
|
+
* (Text, Image, Video, Audio, Sticker, Document, Location, Poll, VCard, Event);
|
|
6
|
+
* `message(wa, raw)` evalúa el tipo y retorna la instancia correcta.
|
|
7
|
+
* Message entity — pure-getter base class with per-type subclasses;
|
|
8
|
+
* `message(wa, raw)` evaluates the type and returns the right instance.
|
|
6
9
|
*/
|
|
7
10
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
8
|
-
exports.Event = exports.VCard = exports.
|
|
9
|
-
exports.build_message_doc = build_message_doc;
|
|
11
|
+
exports.Event = exports.VCard = exports.Poll = exports.Location = exports.Document = exports.Sticker = exports.Audio = exports.Video = exports.Image = exports.Text = exports.Message = void 0;
|
|
10
12
|
exports.message = message;
|
|
11
13
|
const baileys_1 = require("baileys");
|
|
12
14
|
const node_crypto_1 = require("node:crypto");
|
|
13
15
|
const node_stream_1 = require("node:stream");
|
|
16
|
+
const internal_1 = require("../../lib/internal");
|
|
14
17
|
const chat_1 = require("../../lib/chat");
|
|
15
18
|
const store_1 = require("../../lib/store");
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
MessageStatus[MessageStatus["ERROR"] = 0] = "ERROR";
|
|
19
|
-
MessageStatus[MessageStatus["PENDING"] = 1] = "PENDING";
|
|
20
|
-
MessageStatus[MessageStatus["SERVER_ACK"] = 2] = "SERVER_ACK";
|
|
21
|
-
MessageStatus[MessageStatus["DELIVERED"] = 3] = "DELIVERED";
|
|
22
|
-
MessageStatus[MessageStatus["READ"] = 4] = "READ";
|
|
23
|
-
MessageStatus[MessageStatus["PLAYED"] = 5] = "PLAYED";
|
|
24
|
-
})(MessageStatus || (exports.MessageStatus = MessageStatus = {}));
|
|
19
|
+
/** Estados legibles indexados por el status numérico de baileys. / Readable states indexed by the baileys numeric status. */
|
|
20
|
+
const STATUS = ['error', 'pending', 'sent', 'delivered', 'read', 'played'];
|
|
25
21
|
const MESSAGE_TYPE_MAP = {
|
|
26
22
|
conversation: 'text',
|
|
27
23
|
extendedTextMessage: 'text',
|
|
28
24
|
imageMessage: 'image',
|
|
29
25
|
videoMessage: 'video',
|
|
30
26
|
audioMessage: 'audio',
|
|
27
|
+
stickerMessage: 'sticker',
|
|
31
28
|
locationMessage: 'location',
|
|
32
29
|
liveLocationMessage: 'location',
|
|
33
30
|
pollCreationMessage: 'poll',
|
|
@@ -40,292 +37,441 @@ const MESSAGE_TYPE_MAP = {
|
|
|
40
37
|
eventMessage: 'event',
|
|
41
38
|
};
|
|
42
39
|
/**
|
|
43
|
-
*
|
|
40
|
+
* Desenvuelve los wrappers de contenido (view-once, documento con caption).
|
|
41
|
+
* Unwraps content wrappers (view-once, captioned document).
|
|
44
42
|
*/
|
|
45
|
-
function
|
|
46
|
-
|
|
47
|
-
const msg = raw.message ?? {};
|
|
48
|
-
const unwrapped = msg.viewOnceMessage?.message ??
|
|
43
|
+
function unwrap(msg) {
|
|
44
|
+
return (msg.viewOnceMessage?.message ??
|
|
49
45
|
msg.viewOnceMessageV2?.message ??
|
|
50
46
|
msg.viewOnceMessageV2Extension?.message ??
|
|
51
47
|
msg.documentWithCaptionMessage?.message ??
|
|
52
|
-
msg;
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
48
|
+
msg);
|
|
49
|
+
}
|
|
50
|
+
/**
|
|
51
|
+
* Convierte un número o Long del proto a number plano.
|
|
52
|
+
* Converts a proto number or Long into a plain number.
|
|
53
|
+
*/
|
|
54
|
+
function to_number(value) {
|
|
55
|
+
return Number(value?.toString() ?? 0);
|
|
56
|
+
}
|
|
57
|
+
/**
|
|
58
|
+
* Convierte los bytes del proto (Uint8Array runtime o base64 post-engine) a Buffer.
|
|
59
|
+
* Converts proto bytes (runtime Uint8Array or post-engine base64) into a Buffer.
|
|
60
|
+
*/
|
|
61
|
+
function to_buffer(value) {
|
|
62
|
+
if (value instanceof Uint8Array && value.length > 0)
|
|
63
|
+
return Buffer.from(value);
|
|
64
|
+
if (typeof value === 'string' && value.length > 0)
|
|
65
|
+
return Buffer.from(value, 'base64');
|
|
66
|
+
return null;
|
|
67
|
+
}
|
|
68
|
+
/**
|
|
69
|
+
* Envío base: resuelve el destino, cita si hay `mid`, marca view-once, persiste el doc
|
|
70
|
+
* (con su binario cuando aplica) y retorna la instancia del mensaje enviado.
|
|
71
|
+
* Base send: resolves the target, quotes when `mid` is given, flags view-once, persists
|
|
72
|
+
* the doc (with its binary when it applies) and returns the sent message instance.
|
|
73
|
+
*/
|
|
74
|
+
async function send(wa, cid, content, binary, extra = {}, doc_overrides) {
|
|
75
|
+
let result = null;
|
|
76
|
+
const jid = await (0, internal_1.internals)(wa).resolve_jid(cid);
|
|
77
|
+
const socket = (0, internal_1.internals)(wa).socket;
|
|
78
|
+
if (jid && socket) {
|
|
79
|
+
const quoted = extra.mid
|
|
80
|
+
? ((0, store_1.deserialize)(await wa.engine.get(`/chat/${jid}/message/${extra.mid}`))?.raw ?? undefined)
|
|
81
|
+
: undefined;
|
|
82
|
+
const raw = await socket.sendMessage(jid, {
|
|
83
|
+
...content,
|
|
84
|
+
...(extra.once && { viewOnce: true }),
|
|
85
|
+
...(quoted && { quoted }),
|
|
86
|
+
});
|
|
87
|
+
const sent_id = raw?.key?.id;
|
|
88
|
+
if (raw && sent_id) {
|
|
89
|
+
const instance = message(wa, raw);
|
|
90
|
+
if (doc_overrides) {
|
|
91
|
+
Object.assign(instance._raw, doc_overrides);
|
|
92
|
+
}
|
|
93
|
+
await wa.engine.set(`/chat/${jid}/message/${sent_id}`, (0, store_1.serialize)(instance._raw), instance._raw.created_at);
|
|
94
|
+
if (binary) {
|
|
95
|
+
await write_content(wa, `/chat/${jid}/message/${sent_id}/content`, binary);
|
|
96
|
+
}
|
|
97
|
+
result = instance;
|
|
98
|
+
}
|
|
99
|
+
}
|
|
100
|
+
return result;
|
|
101
|
+
}
|
|
102
|
+
/**
|
|
103
|
+
* Binario persistido de un documento: primero el crudo del engine y, si el driver no soporta
|
|
104
|
+
* binarios o el dato viene de una sesión anterior, el documento JSON con base64.
|
|
105
|
+
* A document's persisted binary: the engine's raw one first and, when the driver lacks binary
|
|
106
|
+
* support or the data comes from an older session, the base64 JSON document.
|
|
107
|
+
*
|
|
108
|
+
* @param wa - Cliente dueño / Owner client
|
|
109
|
+
* @param path - Ruta del contenido / Content path
|
|
110
|
+
* @returns Binario o null / Binary or null
|
|
111
|
+
*/
|
|
112
|
+
async function read_content(wa, path) {
|
|
113
|
+
const raw = await wa.engine.get_buffer?.(path);
|
|
114
|
+
if (raw?.length) {
|
|
115
|
+
return raw;
|
|
116
|
+
}
|
|
117
|
+
const legacy = (0, store_1.deserialize)(await wa.engine.get(path));
|
|
118
|
+
return legacy?.data ? Buffer.from(legacy.data, 'base64') : null;
|
|
119
|
+
}
|
|
120
|
+
/**
|
|
121
|
+
* Persiste el binario de un documento: crudo cuando el driver lo soporta, JSON con base64
|
|
122
|
+
* en caso contrario.
|
|
123
|
+
* Persists a document's binary: raw when the driver supports it, base64 JSON otherwise.
|
|
124
|
+
*
|
|
125
|
+
* @param wa - Cliente dueño / Owner client
|
|
126
|
+
* @param path - Ruta del contenido / Content path
|
|
127
|
+
* @param data - Binario a guardar / Binary to store
|
|
128
|
+
*/
|
|
129
|
+
async function write_content(wa, path, data) {
|
|
130
|
+
if (wa.engine.set_buffer) {
|
|
131
|
+
await wa.engine.set_buffer(path, data);
|
|
132
|
+
}
|
|
133
|
+
else {
|
|
134
|
+
await wa.engine.set(path, (0, store_1.serialize)({ data: data.toString('base64') }));
|
|
135
|
+
}
|
|
136
|
+
}
|
|
137
|
+
/**
|
|
138
|
+
* Stream del binario del mensaje: cache del engine o descarga de baileys.
|
|
139
|
+
* Message binary stream: engine cache or baileys download.
|
|
140
|
+
*/
|
|
141
|
+
async function media_stream(wa, doc) {
|
|
142
|
+
const cached = await read_content(wa, `/chat/${doc.cid}/message/${doc.id}/content`);
|
|
143
|
+
if (cached) {
|
|
144
|
+
return node_stream_1.Readable.from(cached);
|
|
145
|
+
}
|
|
146
|
+
const socket = (0, internal_1.internals)(wa).socket;
|
|
147
|
+
if (socket) {
|
|
148
|
+
try {
|
|
149
|
+
return (await (0, baileys_1.downloadMediaMessage)(doc.raw, 'stream', {}));
|
|
150
|
+
}
|
|
151
|
+
catch {
|
|
152
|
+
/* media may be expired */
|
|
153
|
+
}
|
|
154
|
+
}
|
|
155
|
+
return node_stream_1.Readable.from(Buffer.alloc(0));
|
|
156
|
+
}
|
|
157
|
+
/**
|
|
158
|
+
* Acumula un Readable en Buffer.
|
|
159
|
+
* Drains a Readable into a Buffer.
|
|
160
|
+
*/
|
|
161
|
+
async function drain(readable) {
|
|
162
|
+
const chunks = [];
|
|
163
|
+
for await (const chunk of readable) {
|
|
164
|
+
chunks.push(Buffer.isBuffer(chunk) ? chunk : Buffer.from(chunk));
|
|
165
|
+
}
|
|
166
|
+
return Buffer.concat(chunks);
|
|
107
167
|
}
|
|
108
168
|
/**
|
|
109
|
-
* Clase base del mensaje
|
|
110
|
-
*
|
|
169
|
+
* Clase base del mensaje: recibe el cliente y el raw, deriva el estado con getters y
|
|
170
|
+
* opera contra WhatsApp con sus métodos. Los estáticos reciben el cliente como primer
|
|
171
|
+
* argumento (`Message.text(wa, cid, …)`).
|
|
172
|
+
* Base message class: receives the client and the raw, derives state via getters and
|
|
173
|
+
* operates against WhatsApp with its methods. Statics take the client as their first
|
|
174
|
+
* argument (`Message.text(wa, cid, …)`).
|
|
111
175
|
*/
|
|
112
176
|
class Message {
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
177
|
+
/**
|
|
178
|
+
* @internal Cliente dueño y documento persistido. `status`, `created_at` y
|
|
179
|
+
* `deleted_at` guardan los valores numéricos del protocolo; los getters los
|
|
180
|
+
* presentan legibles. `viewed` existe únicamente en los status broadcast (Feed).
|
|
181
|
+
* Owner client and persisted document. `status`, `created_at` and `deleted_at`
|
|
182
|
+
* keep the protocol numeric values; getters expose them readable. `viewed` only
|
|
183
|
+
* exists on status broadcasts (Feed).
|
|
184
|
+
*/
|
|
185
|
+
constructor(_wa, _raw) {
|
|
186
|
+
this._wa = _wa;
|
|
187
|
+
this._raw = _raw;
|
|
120
188
|
}
|
|
189
|
+
/** Identificador del mensaje. / Message identifier. */
|
|
121
190
|
get id() {
|
|
122
|
-
return this.
|
|
191
|
+
return this._raw.id;
|
|
123
192
|
}
|
|
193
|
+
/** Identificador del chat del mensaje. / Message chat identifier. */
|
|
124
194
|
get cid() {
|
|
125
|
-
return this.
|
|
195
|
+
return this._raw.cid;
|
|
126
196
|
}
|
|
127
|
-
|
|
128
|
-
|
|
197
|
+
/** Identificador del mensaje citado, o null. / Quoted message identifier, or null. */
|
|
198
|
+
get mid() {
|
|
199
|
+
return this._raw.mid;
|
|
129
200
|
}
|
|
130
|
-
/** JID del autor
|
|
201
|
+
/** JID del autor, para acceso síncrono. / Author JID, for sync access. */
|
|
131
202
|
get from() {
|
|
132
|
-
return this.
|
|
133
|
-
}
|
|
134
|
-
/** MIME type del contenido. `text/plain` para texto, `application/json` para location/poll, mimetype real para media. */
|
|
135
|
-
get mime() {
|
|
136
|
-
return this._doc.mime;
|
|
137
|
-
}
|
|
138
|
-
get mid() {
|
|
139
|
-
return this._doc.mid;
|
|
203
|
+
return this._raw.author;
|
|
140
204
|
}
|
|
205
|
+
/** true si soy el autor. / true when I am the author. */
|
|
141
206
|
get me() {
|
|
142
|
-
return this.
|
|
207
|
+
return this._raw.me;
|
|
143
208
|
}
|
|
209
|
+
/** Tipo del mensaje. / Message type. */
|
|
210
|
+
get type() {
|
|
211
|
+
return this._raw.type;
|
|
212
|
+
}
|
|
213
|
+
/** MIME: text/plain en texto, text/json en poll/location/vcard/event, real en media. / MIME: text/plain for text, text/json for poll/location/vcard/event, actual for media. */
|
|
214
|
+
get mime() {
|
|
215
|
+
const { type } = this._raw;
|
|
216
|
+
if (type === 'text')
|
|
217
|
+
return 'text/plain';
|
|
218
|
+
if (type === 'poll' || type === 'location' || type === 'vcard' || type === 'event')
|
|
219
|
+
return 'text/json';
|
|
220
|
+
return this._raw.mime;
|
|
221
|
+
}
|
|
222
|
+
/** Texto del mensaje o caption del media. / Message text or media caption. */
|
|
144
223
|
get caption() {
|
|
145
|
-
return this.
|
|
224
|
+
return this._raw.caption;
|
|
225
|
+
}
|
|
226
|
+
/** Estado legible del mensaje. / Readable message state. */
|
|
227
|
+
get status() {
|
|
228
|
+
return STATUS[this._raw.status] ?? 'pending';
|
|
229
|
+
}
|
|
230
|
+
/** true si el mensaje fue visto. / true when the message was seen. */
|
|
231
|
+
get read() {
|
|
232
|
+
return this._raw.status >= 4;
|
|
146
233
|
}
|
|
234
|
+
/** true si está destacado. / true when starred. */
|
|
147
235
|
get starred() {
|
|
148
|
-
return this.
|
|
236
|
+
return this._raw.starred;
|
|
149
237
|
}
|
|
238
|
+
/** true si fue reenviado. / true when forwarded. */
|
|
150
239
|
get forwarded() {
|
|
151
|
-
return this.
|
|
240
|
+
return this._raw.forwarded;
|
|
152
241
|
}
|
|
242
|
+
/** true si fue editado. / true when edited. */
|
|
243
|
+
get edited() {
|
|
244
|
+
return this._raw.edited;
|
|
245
|
+
}
|
|
246
|
+
/** true si es de una sola lectura (view-once). / true when view-once. */
|
|
153
247
|
get once() {
|
|
154
|
-
|
|
248
|
+
const msg = this._raw.raw.message;
|
|
249
|
+
return Boolean(msg?.viewOnceMessage ?? msg?.viewOnceMessageV2 ?? msg?.viewOnceMessageV2Extension);
|
|
155
250
|
}
|
|
251
|
+
/** Fecha de creación en ISO UTC. / Creation date as ISO UTC. */
|
|
156
252
|
get created_at() {
|
|
157
|
-
return this.
|
|
158
|
-
}
|
|
159
|
-
get deleted_at() {
|
|
160
|
-
return this._doc.deleted_at;
|
|
253
|
+
return new Date(this._raw.created_at).toISOString();
|
|
161
254
|
}
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
get edited() {
|
|
166
|
-
return this._doc.edited;
|
|
255
|
+
/** Vencimiento del mensaje temporal en ISO UTC, o null. / Ephemeral expiration as ISO UTC, or null. */
|
|
256
|
+
get expires_at() {
|
|
257
|
+
return this._raw.deleted_at != null ? new Date(this._raw.deleted_at).toISOString() : null;
|
|
167
258
|
}
|
|
168
259
|
/**
|
|
169
|
-
* Contacto del
|
|
170
|
-
*
|
|
171
|
-
*
|
|
172
|
-
*
|
|
173
|
-
* Instance-memoized: first call hits the engine; subsequent calls reuse the
|
|
174
|
-
* same promise without further round-trips.
|
|
260
|
+
* Contacto autor del mensaje, desde el engine (ficha mínima si no está persistido).
|
|
261
|
+
* Message author contact, from the engine (minimal card when not persisted).
|
|
262
|
+
*
|
|
263
|
+
* @returns Contacto / Contact
|
|
175
264
|
*/
|
|
176
265
|
async author() {
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
const fetched = _doc.author ? await _wa.Contact.get(_doc.author) : null;
|
|
180
|
-
return (fetched ??
|
|
181
|
-
new _wa.Contact({
|
|
182
|
-
id: _doc.author,
|
|
183
|
-
lid: null,
|
|
184
|
-
name: null,
|
|
185
|
-
notify: null,
|
|
186
|
-
verified_name: null,
|
|
187
|
-
img_url: null,
|
|
188
|
-
status: null,
|
|
189
|
-
}, new _wa.Chat({ id: _doc.author, name: _doc.author.split('@')[0] })));
|
|
190
|
-
})());
|
|
266
|
+
const cached = (0, store_1.deserialize)(await this._wa.engine.get(`/contact/${this._raw.author}`));
|
|
267
|
+
return new this._wa.Contact(cached ?? { id: this._raw.author });
|
|
191
268
|
}
|
|
192
269
|
/**
|
|
193
|
-
* Chat al que pertenece el mensaje.
|
|
194
|
-
*
|
|
195
|
-
*
|
|
196
|
-
*
|
|
270
|
+
* Chat al que pertenece el mensaje.
|
|
271
|
+
* Chat the message belongs to.
|
|
272
|
+
*
|
|
273
|
+
* @returns Chat / Chat
|
|
197
274
|
*/
|
|
198
275
|
async chat() {
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
return new this._wa.Chat(cached ?? { id: this._doc.cid, name: this._doc.cid.split('@')[0] });
|
|
202
|
-
})());
|
|
276
|
+
const cached = (0, store_1.deserialize)(await this._wa.engine.get(`/chat/${this._raw.cid}`));
|
|
277
|
+
return new this._wa.Chat(cached ?? { id: this._raw.cid });
|
|
203
278
|
}
|
|
204
|
-
/**
|
|
279
|
+
/**
|
|
280
|
+
* Mensaje citado cuando hay `mid`, o null.
|
|
281
|
+
* Quoted message when `mid` exists, or null.
|
|
282
|
+
*
|
|
283
|
+
* @returns Mensaje citado o null / Quoted message or null
|
|
284
|
+
*/
|
|
285
|
+
async message() {
|
|
286
|
+
return this._raw.mid ? Message.get(this._wa, this._raw.cid, this._raw.mid) : null;
|
|
287
|
+
}
|
|
288
|
+
/**
|
|
289
|
+
* Contenido del mensaje: texto como su caption, media como binario, y
|
|
290
|
+
* poll/location/vcard/event como JSON.
|
|
291
|
+
* Message content: text as its caption, media as binary, and
|
|
292
|
+
* poll/location/vcard/event as JSON.
|
|
293
|
+
*
|
|
294
|
+
* @returns Buffer del contenido / Content buffer
|
|
295
|
+
*/
|
|
205
296
|
async content() {
|
|
206
|
-
|
|
207
|
-
return cached?.data ? Buffer.from(cached.data, 'base64') : Buffer.alloc(0);
|
|
297
|
+
return (await read_content(this._wa, `/chat/${this._raw.cid}/message/${this._raw.id}/content`)) ?? Buffer.alloc(0);
|
|
208
298
|
}
|
|
209
299
|
/**
|
|
210
|
-
*
|
|
211
|
-
*
|
|
212
|
-
*
|
|
213
|
-
*
|
|
214
|
-
* from the engine cache or, if missing, downloads from baileys. For text/location/poll
|
|
215
|
-
* it wraps the `content()` buffer in a Readable.
|
|
300
|
+
* Stream del contenido; para media descarga de baileys si el cache falta.
|
|
301
|
+
* Content stream; media falls back to a baileys download when the cache is missing.
|
|
302
|
+
*
|
|
303
|
+
* @returns Readable del contenido / Content readable
|
|
216
304
|
*/
|
|
217
305
|
async stream() {
|
|
218
|
-
return
|
|
306
|
+
return media_stream(this._wa, this._raw);
|
|
219
307
|
}
|
|
220
|
-
/**
|
|
308
|
+
/**
|
|
309
|
+
* Reacciones del mensaje agrupadas por emoji con su conteo.
|
|
310
|
+
* Message reactions grouped by emoji with their count.
|
|
311
|
+
*
|
|
312
|
+
* @returns Conteo por emoji / Per-emoji count
|
|
313
|
+
*/
|
|
314
|
+
async reactions() {
|
|
315
|
+
const counts = new Map();
|
|
316
|
+
for (const { emoji } of this._raw.reactions ?? []) {
|
|
317
|
+
counts.set(emoji, (counts.get(emoji) ?? 0) + 1);
|
|
318
|
+
}
|
|
319
|
+
return [...counts.entries()].map(([emoji, count]) => ({ emoji, count }));
|
|
320
|
+
}
|
|
321
|
+
/**
|
|
322
|
+
* Reacciona al mensaje; el emoji vacío retira la reacción.
|
|
323
|
+
* Reacts to the message; an empty emoji removes the reaction.
|
|
324
|
+
*
|
|
325
|
+
* @param emoji - Emoji o '' / Emoji or ''
|
|
326
|
+
* @returns true si se envió / true when sent
|
|
327
|
+
*/
|
|
221
328
|
async react(emoji) {
|
|
222
|
-
const { _wa, _doc } = this;
|
|
223
329
|
let ok = false;
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
|
|
330
|
+
const socket = (0, internal_1.internals)(this._wa).socket;
|
|
331
|
+
if (socket) {
|
|
332
|
+
const { cid, id, me, author } = this._raw;
|
|
333
|
+
await socket.sendMessage(cid, {
|
|
334
|
+
react: {
|
|
335
|
+
text: emoji,
|
|
336
|
+
key: { remoteJid: cid, id, fromMe: me, ...(cid.endsWith('@g.us') && !me && { participant: author }) },
|
|
337
|
+
},
|
|
227
338
|
});
|
|
228
339
|
ok = true;
|
|
229
340
|
}
|
|
230
341
|
return ok;
|
|
231
342
|
}
|
|
232
|
-
/**
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
|
|
343
|
+
/**
|
|
344
|
+
* Destaca o quita el destacado del mensaje.
|
|
345
|
+
* Stars or unstars the message.
|
|
346
|
+
*
|
|
347
|
+
* @param value - true destaca / true stars
|
|
348
|
+
* @returns true si se envió / true when sent
|
|
349
|
+
*/
|
|
350
|
+
async star(value) {
|
|
240
351
|
let ok = false;
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
await _wa._socket.relayMessage(jid, wa_msg.message, { messageId: wa_msg.key.id });
|
|
249
|
-
ok = true;
|
|
250
|
-
}
|
|
251
|
-
}
|
|
252
|
-
catch {
|
|
253
|
-
/* fallback below */
|
|
254
|
-
}
|
|
255
|
-
}
|
|
256
|
-
if (!ok) {
|
|
257
|
-
const buf = await this.content();
|
|
258
|
-
if (buf.length > 0) {
|
|
259
|
-
if (_doc.type === 'text') {
|
|
260
|
-
ok = (await _wa.Message.text(jid, buf.toString())) !== null;
|
|
261
|
-
}
|
|
262
|
-
else if (_doc.type === 'image') {
|
|
263
|
-
ok =
|
|
264
|
-
(await _wa.Message.image(jid, buf, { caption: _doc.caption || undefined })) !==
|
|
265
|
-
null;
|
|
266
|
-
}
|
|
267
|
-
else if (_doc.type === 'video') {
|
|
268
|
-
ok =
|
|
269
|
-
(await _wa.Message.video(jid, buf, { caption: _doc.caption || undefined })) !==
|
|
270
|
-
null;
|
|
271
|
-
}
|
|
272
|
-
else if (_doc.type === 'audio') {
|
|
273
|
-
ok = (await _wa.Message.audio(jid, buf)) !== null;
|
|
274
|
-
}
|
|
275
|
-
else if (_doc.type === 'location') {
|
|
276
|
-
try {
|
|
277
|
-
ok =
|
|
278
|
-
(await _wa.Message.location(jid, JSON.parse(buf.toString()))) !== null;
|
|
279
|
-
}
|
|
280
|
-
catch {
|
|
281
|
-
/* invalid payload */
|
|
282
|
-
}
|
|
283
|
-
}
|
|
284
|
-
}
|
|
285
|
-
}
|
|
286
|
-
}
|
|
352
|
+
const socket = (0, internal_1.internals)(this._wa).socket;
|
|
353
|
+
if (socket) {
|
|
354
|
+
const doc = this._raw;
|
|
355
|
+
await socket.chatModify({ star: { messages: [{ id: doc.id, fromMe: doc.me }], star: value } }, doc.cid);
|
|
356
|
+
doc.starred = value;
|
|
357
|
+
await this._wa.engine.set(`/chat/${doc.cid}/message/${doc.id}`, (0, store_1.serialize)(doc), doc.created_at);
|
|
358
|
+
ok = true;
|
|
287
359
|
}
|
|
288
360
|
return ok;
|
|
289
361
|
}
|
|
290
|
-
/**
|
|
362
|
+
/**
|
|
363
|
+
* Marca el mensaje como leído.
|
|
364
|
+
* Marks the message as read.
|
|
365
|
+
*
|
|
366
|
+
* @returns true si se envió / true when sent
|
|
367
|
+
*/
|
|
291
368
|
async seen() {
|
|
292
|
-
const { _wa, _doc } = this;
|
|
293
369
|
let ok = false;
|
|
294
|
-
|
|
295
|
-
|
|
296
|
-
|
|
297
|
-
|
|
370
|
+
const socket = (0, internal_1.internals)(this._wa).socket;
|
|
371
|
+
if (socket) {
|
|
372
|
+
const { cid, id, author } = this._raw;
|
|
373
|
+
await socket.readMessages([
|
|
374
|
+
{ remoteJid: cid, id, participant: cid.endsWith('@g.us') ? author : undefined },
|
|
298
375
|
]);
|
|
299
376
|
ok = true;
|
|
300
377
|
}
|
|
301
378
|
return ok;
|
|
302
379
|
}
|
|
303
|
-
/**
|
|
304
|
-
|
|
305
|
-
|
|
380
|
+
/**
|
|
381
|
+
* Edita el caption del mensaje propio (texto, imagen o video).
|
|
382
|
+
* Edits the own message caption (text, image or video).
|
|
383
|
+
*
|
|
384
|
+
* @param caption - Texto nuevo / New text
|
|
385
|
+
* @returns true si se editó / true when edited
|
|
386
|
+
*/
|
|
387
|
+
async edit(caption) {
|
|
388
|
+
const doc = this._raw;
|
|
306
389
|
let ok = false;
|
|
307
|
-
|
|
308
|
-
|
|
309
|
-
|
|
310
|
-
|
|
311
|
-
|
|
312
|
-
|
|
313
|
-
|
|
390
|
+
const socket = (0, internal_1.internals)(this._wa).socket;
|
|
391
|
+
if (socket && doc.me) {
|
|
392
|
+
if (doc.type === 'text') {
|
|
393
|
+
await socket.sendMessage(doc.cid, {
|
|
394
|
+
text: caption,
|
|
395
|
+
edit: { remoteJid: doc.cid, id: doc.id, fromMe: true },
|
|
396
|
+
});
|
|
397
|
+
const content_type = (0, baileys_1.getContentType)(doc.raw.message ?? {});
|
|
398
|
+
doc.raw.message =
|
|
399
|
+
content_type === 'conversation' ? { conversation: caption } : { extendedTextMessage: { text: caption } };
|
|
400
|
+
ok = true;
|
|
401
|
+
}
|
|
402
|
+
else if (doc.type === 'image' || doc.type === 'video') {
|
|
403
|
+
const media_key = doc.type === 'image' ? 'imageMessage' : 'videoMessage';
|
|
404
|
+
const media = doc.raw.message?.[media_key];
|
|
405
|
+
if (media) {
|
|
406
|
+
const edited = { [media_key]: { ...media, caption } };
|
|
407
|
+
await socket.relayMessage(doc.cid, {
|
|
408
|
+
protocolMessage: {
|
|
409
|
+
key: { remoteJid: doc.cid, id: doc.id, fromMe: true },
|
|
410
|
+
type: baileys_1.proto.Message.ProtocolMessage.Type.MESSAGE_EDIT,
|
|
411
|
+
editedMessage: edited,
|
|
412
|
+
timestampMs: Date.now(),
|
|
413
|
+
},
|
|
414
|
+
}, { messageId: (0, baileys_1.generateMessageID)() });
|
|
415
|
+
doc.raw.message = { ...doc.raw.message, ...edited };
|
|
416
|
+
ok = true;
|
|
417
|
+
}
|
|
418
|
+
}
|
|
419
|
+
if (ok) {
|
|
420
|
+
doc.caption = caption;
|
|
421
|
+
doc.edited = true;
|
|
422
|
+
await this._wa.engine.set(`/chat/${doc.cid}/message/${doc.id}`, (0, store_1.serialize)(doc), doc.created_at);
|
|
423
|
+
}
|
|
314
424
|
}
|
|
315
425
|
return ok;
|
|
316
426
|
}
|
|
317
|
-
/**
|
|
318
|
-
|
|
319
|
-
|
|
427
|
+
/**
|
|
428
|
+
* Reenvía el mensaje a otro chat.
|
|
429
|
+
* Forwards the message to another chat.
|
|
430
|
+
*
|
|
431
|
+
* @param target - CID, Chat o Contact destino / Target CID, Chat or Contact
|
|
432
|
+
* @returns true si se reenvió / true when forwarded
|
|
433
|
+
*/
|
|
434
|
+
async forward(target) {
|
|
435
|
+
const doc = this._raw;
|
|
436
|
+
const to_cid = typeof target === 'string' ? target : target instanceof chat_1.Chat ? target._raw.id : (target.jid ?? target.lid ?? '');
|
|
320
437
|
let ok = false;
|
|
321
|
-
|
|
438
|
+
const socket = (0, internal_1.internals)(this._wa).socket;
|
|
439
|
+
if (socket && to_cid) {
|
|
440
|
+
const jid = await (0, internal_1.internals)(this._wa).resolve_jid(to_cid);
|
|
441
|
+
if (jid && doc.raw.message) {
|
|
442
|
+
try {
|
|
443
|
+
const wa_msg = (0, baileys_1.generateWAMessageFromContent)(jid, (0, baileys_1.generateForwardMessageContent)(doc.raw, false), {
|
|
444
|
+
userJid: socket.user?.id ?? '',
|
|
445
|
+
});
|
|
446
|
+
if (wa_msg.message && wa_msg.key?.id) {
|
|
447
|
+
await socket.relayMessage(jid, wa_msg.message, { messageId: wa_msg.key.id });
|
|
448
|
+
ok = true;
|
|
449
|
+
}
|
|
450
|
+
}
|
|
451
|
+
catch {
|
|
452
|
+
/* forward content may be unsupported */
|
|
453
|
+
}
|
|
454
|
+
}
|
|
455
|
+
}
|
|
456
|
+
return ok;
|
|
457
|
+
}
|
|
458
|
+
/**
|
|
459
|
+
* Elimina el mensaje: solo en mi dispositivo por defecto, para todos con `true`.
|
|
460
|
+
* Deletes the message: this device only by default, for everyone with `true`.
|
|
461
|
+
*
|
|
462
|
+
* @param all - true elimina para todos / true deletes for everyone
|
|
463
|
+
* @returns true si se eliminó / true when deleted
|
|
464
|
+
*/
|
|
465
|
+
async delete(all = false) {
|
|
466
|
+
const doc = this._raw;
|
|
467
|
+
let ok = false;
|
|
468
|
+
const socket = (0, internal_1.internals)(this._wa).socket;
|
|
469
|
+
if (socket) {
|
|
322
470
|
if (all) {
|
|
323
|
-
await
|
|
324
|
-
delete: { remoteJid: doc.cid, id: doc.id, fromMe: doc.me },
|
|
325
|
-
});
|
|
471
|
+
await socket.sendMessage(doc.cid, { delete: { remoteJid: doc.cid, id: doc.id, fromMe: doc.me } });
|
|
326
472
|
}
|
|
327
473
|
else {
|
|
328
|
-
await
|
|
474
|
+
await socket.chatModify({
|
|
329
475
|
deleteForMe: {
|
|
330
476
|
deleteMedia: false,
|
|
331
477
|
key: { remoteJid: doc.cid, id: doc.id, fromMe: doc.me },
|
|
@@ -333,166 +479,379 @@ class Message {
|
|
|
333
479
|
},
|
|
334
480
|
}, doc.cid);
|
|
335
481
|
}
|
|
336
|
-
await
|
|
482
|
+
await this._wa.engine.unset(`/chat/${doc.cid}/message/${doc.id}`);
|
|
337
483
|
ok = true;
|
|
338
484
|
}
|
|
339
485
|
return ok;
|
|
340
486
|
}
|
|
341
|
-
/**
|
|
342
|
-
async
|
|
343
|
-
|
|
344
|
-
let ok = false;
|
|
345
|
-
if (wa._socket && doc.me) {
|
|
346
|
-
try {
|
|
347
|
-
await wa._socket.sendMessage(doc.cid, {
|
|
348
|
-
text,
|
|
349
|
-
edit: { remoteJid: doc.cid, id: doc.id, fromMe: true },
|
|
350
|
-
});
|
|
351
|
-
const content_type = (0, baileys_1.getContentType)(doc.raw.message ?? {});
|
|
352
|
-
if (content_type === 'conversation') {
|
|
353
|
-
doc.raw.message = { conversation: text };
|
|
354
|
-
}
|
|
355
|
-
else if (content_type === 'extendedTextMessage') {
|
|
356
|
-
doc.raw.message = { extendedTextMessage: { text } };
|
|
357
|
-
}
|
|
358
|
-
doc.caption = text;
|
|
359
|
-
doc.edited = true;
|
|
360
|
-
await wa.engine.set(`/chat/${doc.cid}/message/${doc.id}`, (0, store_1.serialize)(doc));
|
|
361
|
-
ok = true;
|
|
362
|
-
}
|
|
363
|
-
catch {
|
|
364
|
-
ok = false;
|
|
365
|
-
}
|
|
366
|
-
}
|
|
367
|
-
return ok;
|
|
487
|
+
/** Responde con texto. / Replies with text. */
|
|
488
|
+
async text(caption, extra = {}) {
|
|
489
|
+
return Message.text(this._wa, this._raw.cid, caption, { ...extra, mid: this._raw.id });
|
|
368
490
|
}
|
|
369
|
-
/**
|
|
370
|
-
|
|
371
|
-
|
|
372
|
-
const id = this.id;
|
|
373
|
-
return this._wa.on('message:updated', (updated) => {
|
|
374
|
-
if (updated.cid === cid && updated.id === id) {
|
|
375
|
-
handler(updated);
|
|
376
|
-
}
|
|
377
|
-
});
|
|
491
|
+
/** Responde con imagen. / Replies with an image. */
|
|
492
|
+
async image(buf, extra = {}) {
|
|
493
|
+
return Message.image(this._wa, this._raw.cid, buf, { ...extra, mid: this._raw.id });
|
|
378
494
|
}
|
|
379
|
-
/** Responde con
|
|
380
|
-
async
|
|
381
|
-
return this._wa.
|
|
495
|
+
/** Responde con video. / Replies with a video. */
|
|
496
|
+
async video(buf, extra = {}) {
|
|
497
|
+
return Message.video(this._wa, this._raw.cid, buf, { ...extra, mid: this._raw.id });
|
|
382
498
|
}
|
|
383
|
-
/** Responde con
|
|
384
|
-
async
|
|
385
|
-
return this._wa.
|
|
499
|
+
/** Responde con audio. / Replies with audio. */
|
|
500
|
+
async audio(buf, extra = {}) {
|
|
501
|
+
return Message.audio(this._wa, this._raw.cid, buf, { ...extra, mid: this._raw.id });
|
|
386
502
|
}
|
|
387
|
-
/** Responde con
|
|
388
|
-
async
|
|
389
|
-
return this._wa.
|
|
503
|
+
/** Responde con ubicación. / Replies with a location. */
|
|
504
|
+
async location(loc, extra = {}) {
|
|
505
|
+
return Message.location(this._wa, this._raw.cid, loc, { ...extra, mid: this._raw.id });
|
|
390
506
|
}
|
|
391
|
-
/** Responde con
|
|
392
|
-
async
|
|
393
|
-
return this._wa.
|
|
507
|
+
/** Responde con encuesta. / Replies with a poll. */
|
|
508
|
+
async poll(input, extra = {}) {
|
|
509
|
+
return Message.poll(this._wa, this._raw.cid, input, { ...extra, mid: this._raw.id });
|
|
394
510
|
}
|
|
395
|
-
/** Responde con
|
|
396
|
-
async
|
|
397
|
-
return this._wa.
|
|
511
|
+
/** Responde con documento. / Replies with a document. */
|
|
512
|
+
async document(buf, extra) {
|
|
513
|
+
return Message.document(this._wa, this._raw.cid, buf, { ...extra, mid: this._raw.id });
|
|
398
514
|
}
|
|
399
|
-
/** Responde con
|
|
400
|
-
async
|
|
401
|
-
return this._wa.
|
|
515
|
+
/** Responde con tarjeta(s) de contacto. / Replies with contact card(s). */
|
|
516
|
+
async vcard(contacts, extra = {}) {
|
|
517
|
+
return Message.vcard(this._wa, this._raw.cid, contacts, { ...extra, mid: this._raw.id });
|
|
402
518
|
}
|
|
403
|
-
/** Responde con
|
|
404
|
-
async
|
|
405
|
-
return this._wa.
|
|
519
|
+
/** Responde con evento. / Replies with an event. */
|
|
520
|
+
async event(data, extra = {}) {
|
|
521
|
+
return Message.event(this._wa, this._raw.cid, data, { ...extra, mid: this._raw.id });
|
|
522
|
+
}
|
|
523
|
+
/**
|
|
524
|
+
* Obtiene un mensaje por chat y id.
|
|
525
|
+
* Retrieves a message by chat and id.
|
|
526
|
+
*
|
|
527
|
+
* @param wa - Cliente dueño / Owner client
|
|
528
|
+
* @param cid - Identificador del chat / Chat identifier
|
|
529
|
+
* @param mid - Identificador del mensaje / Message identifier
|
|
530
|
+
* @returns Mensaje o null / Message or null
|
|
531
|
+
*/
|
|
532
|
+
static async get(wa, cid, mid) {
|
|
533
|
+
const jid = await (0, internal_1.internals)(wa).resolve_jid(cid);
|
|
534
|
+
if (jid) {
|
|
535
|
+
const doc = (0, store_1.deserialize)(await wa.engine.get(`/chat/${jid}/message/${mid}`));
|
|
536
|
+
if (doc)
|
|
537
|
+
return message(wa, doc);
|
|
538
|
+
}
|
|
539
|
+
return null;
|
|
406
540
|
}
|
|
407
|
-
/**
|
|
408
|
-
|
|
409
|
-
|
|
541
|
+
/**
|
|
542
|
+
* Pagina los mensajes de un chat desde el más reciente.
|
|
543
|
+
* Paginates chat messages from the most recent one.
|
|
544
|
+
*
|
|
545
|
+
* @param wa - Cliente dueño / Owner client
|
|
546
|
+
* @param cid - Identificador del chat / Chat identifier
|
|
547
|
+
* @param offset - Desplazamiento / Offset
|
|
548
|
+
* @param limit - Tamaño de página / Page size
|
|
549
|
+
* @returns Página de mensajes / Message page
|
|
550
|
+
*/
|
|
551
|
+
static async list(wa, cid, offset = 0, limit = 50) {
|
|
552
|
+
const jid = await (0, internal_1.internals)(wa).resolve_jid(cid);
|
|
553
|
+
if (jid) {
|
|
554
|
+
return (await wa.engine.list(`/chat/${jid}/message`, offset, limit))
|
|
555
|
+
.map((raw) => (0, store_1.deserialize)(raw))
|
|
556
|
+
.filter((doc) => doc !== null)
|
|
557
|
+
.map((doc) => message(wa, doc));
|
|
558
|
+
}
|
|
559
|
+
return [];
|
|
560
|
+
}
|
|
561
|
+
/** Envía texto. / Sends text. */
|
|
562
|
+
static async text(wa, cid, caption, extra = {}) {
|
|
563
|
+
return send(wa, cid, { text: caption }, undefined, extra);
|
|
564
|
+
}
|
|
565
|
+
/** Envía imagen. / Sends an image. */
|
|
566
|
+
static async image(wa, cid, buf, extra = {}) {
|
|
567
|
+
return send(wa, cid, { image: buf, caption: extra.caption }, buf, extra);
|
|
568
|
+
}
|
|
569
|
+
/** Envía video. / Sends a video. */
|
|
570
|
+
static async video(wa, cid, buf, extra = {}) {
|
|
571
|
+
return send(wa, cid, { video: buf, caption: extra.caption }, buf, extra);
|
|
572
|
+
}
|
|
573
|
+
/** Envía audio. / Sends audio. */
|
|
574
|
+
static async audio(wa, cid, buf, extra = {}) {
|
|
575
|
+
return send(wa, cid, { audio: buf, ptt: extra.ptt ?? true }, buf, extra);
|
|
576
|
+
}
|
|
577
|
+
/** Envía ubicación. / Sends a location. */
|
|
578
|
+
static async location(wa, cid, loc, extra = {}) {
|
|
579
|
+
return send(wa, cid, { location: { degreesLatitude: loc.lat, degreesLongitude: loc.lng } }, undefined, extra);
|
|
580
|
+
}
|
|
581
|
+
/** Envía encuesta. / Sends a poll. */
|
|
582
|
+
static async poll(wa, cid, input, extra = {}) {
|
|
583
|
+
const multiple = extra.multiple ?? false;
|
|
584
|
+
return send(wa, cid, {
|
|
585
|
+
poll: {
|
|
586
|
+
name: input.content,
|
|
587
|
+
values: input.options.map((option) => option.content),
|
|
588
|
+
selectableCount: multiple ? 0 : 1,
|
|
589
|
+
},
|
|
590
|
+
}, undefined, extra, { multiple });
|
|
591
|
+
}
|
|
592
|
+
/** Envía documento. / Sends a document. */
|
|
593
|
+
static async document(wa, cid, buf, extra) {
|
|
594
|
+
return send(wa, cid, { document: buf, fileName: extra.file_name, mimetype: extra.mimetype ?? 'application/octet-stream', caption: extra.caption }, buf, extra);
|
|
595
|
+
}
|
|
596
|
+
/** Envía tarjeta(s) de contacto. / Sends contact card(s). */
|
|
597
|
+
static async vcard(wa, cid, contacts, extra = {}) {
|
|
598
|
+
const built = contacts.map((entry) => ({
|
|
599
|
+
displayName: entry.name,
|
|
600
|
+
vcard: [
|
|
601
|
+
'BEGIN:VCARD',
|
|
602
|
+
'VERSION:3.0',
|
|
603
|
+
`FN:${entry.name}`,
|
|
604
|
+
`TEL;type=CELL;waid=${entry.phone.replace(/\D+/g, '')}:${entry.phone}`,
|
|
605
|
+
'END:VCARD',
|
|
606
|
+
].join('\n'),
|
|
607
|
+
}));
|
|
608
|
+
return send(wa, cid, { contacts: { contacts: built } }, Buffer.from(built.map((entry) => entry.vcard).join('\n'), 'utf-8'), extra);
|
|
410
609
|
}
|
|
411
|
-
/**
|
|
412
|
-
async event(data,
|
|
413
|
-
|
|
610
|
+
/** Envía evento. / Sends an event. */
|
|
611
|
+
static async event(wa, cid, data, extra = {}) {
|
|
612
|
+
const binary = Buffer.from(JSON.stringify({ name: data.name, caption: data.caption, start: data.start.getTime(), end: data.end?.getTime() ?? null }), 'utf-8');
|
|
613
|
+
return send(wa, cid, {
|
|
614
|
+
event: {
|
|
615
|
+
name: data.name,
|
|
616
|
+
description: data.caption,
|
|
617
|
+
startDate: data.start,
|
|
618
|
+
endDate: data.end,
|
|
619
|
+
location: data.place ? { degreesLatitude: data.place.lat, degreesLongitude: data.place.lng } : undefined,
|
|
620
|
+
},
|
|
621
|
+
}, binary, extra);
|
|
622
|
+
}
|
|
623
|
+
/** Reacciona a un mensaje por id. / Reacts to a message by id. */
|
|
624
|
+
static async react(wa, cid, mid, emoji) {
|
|
625
|
+
const msg = await Message.get(wa, cid, mid);
|
|
626
|
+
return msg ? msg.react(emoji) : false;
|
|
627
|
+
}
|
|
628
|
+
/** Destaca un mensaje por id. / Stars a message by id. */
|
|
629
|
+
static async star(wa, cid, mid, value) {
|
|
630
|
+
const msg = await Message.get(wa, cid, mid);
|
|
631
|
+
return msg ? msg.star(value) : false;
|
|
632
|
+
}
|
|
633
|
+
/** Marca un mensaje como leído por id. / Marks a message as read by id. */
|
|
634
|
+
static async seen(wa, cid, mid) {
|
|
635
|
+
const msg = await Message.get(wa, cid, mid);
|
|
636
|
+
return msg ? msg.seen() : false;
|
|
637
|
+
}
|
|
638
|
+
/** Edita un mensaje por id. / Edits a message by id. */
|
|
639
|
+
static async edit(wa, cid, mid, caption) {
|
|
640
|
+
const msg = await Message.get(wa, cid, mid);
|
|
641
|
+
return msg ? msg.edit(caption) : false;
|
|
642
|
+
}
|
|
643
|
+
/** Reenvía un mensaje por id. / Forwards a message by id. */
|
|
644
|
+
static async forward(wa, cid, mid, target) {
|
|
645
|
+
const msg = await Message.get(wa, cid, mid);
|
|
646
|
+
return msg ? msg.forward(target) : false;
|
|
647
|
+
}
|
|
648
|
+
/** Elimina un mensaje por id. / Deletes a message by id. */
|
|
649
|
+
static async delete(wa, cid, mid, all = false) {
|
|
650
|
+
const msg = await Message.get(wa, cid, mid);
|
|
651
|
+
return msg ? msg.delete(all) : false;
|
|
652
|
+
}
|
|
653
|
+
/** Reacciones de un mensaje por id. / Reactions of a message by id. */
|
|
654
|
+
static async reactions(wa, cid, mid) {
|
|
655
|
+
const msg = await Message.get(wa, cid, mid);
|
|
656
|
+
return msg ? msg.reactions() : [];
|
|
414
657
|
}
|
|
415
658
|
}
|
|
416
659
|
exports.Message = Message;
|
|
417
660
|
/**
|
|
418
|
-
* Mensaje de texto
|
|
419
|
-
* Text message
|
|
661
|
+
* Mensaje de texto; `preview()` expone la tarjeta del enlace embebido.
|
|
662
|
+
* Text message; `preview()` exposes the embedded link card.
|
|
420
663
|
*/
|
|
421
664
|
class Text extends Message {
|
|
422
|
-
|
|
423
|
-
|
|
424
|
-
|
|
425
|
-
|
|
426
|
-
|
|
427
|
-
|
|
428
|
-
async
|
|
429
|
-
|
|
430
|
-
|
|
431
|
-
|
|
432
|
-
|
|
433
|
-
|
|
434
|
-
|
|
435
|
-
|
|
436
|
-
|
|
437
|
-
return (await (0, baileys_1.downloadMediaMessage)(doc.raw, 'stream', {}));
|
|
438
|
-
}
|
|
439
|
-
catch {
|
|
440
|
-
/* fallback empty */
|
|
665
|
+
/**
|
|
666
|
+
* Preview del enlace del texto, o null cuando no trae metadata.
|
|
667
|
+
* Text link preview, or null when it carries no metadata.
|
|
668
|
+
*
|
|
669
|
+
* @returns { link, name, content, thumb } o null / { link, name, content, thumb } or null
|
|
670
|
+
*/
|
|
671
|
+
async preview() {
|
|
672
|
+
const ext = this._raw.raw.message?.extendedTextMessage;
|
|
673
|
+
if (ext?.matchedText && (ext.title || ext.description)) {
|
|
674
|
+
return {
|
|
675
|
+
link: ext.matchedText,
|
|
676
|
+
name: ext.title ?? null,
|
|
677
|
+
content: ext.description ?? null,
|
|
678
|
+
thumb: to_buffer(ext.jpegThumbnail),
|
|
679
|
+
};
|
|
441
680
|
}
|
|
681
|
+
return null;
|
|
442
682
|
}
|
|
443
|
-
return node_stream_1.Readable.from(Buffer.alloc(0));
|
|
444
|
-
}
|
|
445
|
-
/** @internal Acumula un Readable en Buffer. */
|
|
446
|
-
async function _drain(readable) {
|
|
447
|
-
const chunks = [];
|
|
448
|
-
for await (const chunk of readable) {
|
|
449
|
-
chunks.push(Buffer.isBuffer(chunk) ? chunk : Buffer.from(chunk));
|
|
450
|
-
}
|
|
451
|
-
return Buffer.concat(chunks);
|
|
452
683
|
}
|
|
684
|
+
exports.Text = Text;
|
|
453
685
|
/**
|
|
454
|
-
* Mensaje de imagen
|
|
455
|
-
* Image message
|
|
686
|
+
* Mensaje de imagen: dimensiones, peso y miniatura.
|
|
687
|
+
* Image message: dimensions, size and thumbnail.
|
|
456
688
|
*/
|
|
457
689
|
class Image extends Message {
|
|
690
|
+
/** @internal */
|
|
691
|
+
get _media() {
|
|
692
|
+
return unwrap(this._raw.raw.message ?? {}).imageMessage;
|
|
693
|
+
}
|
|
694
|
+
/** Ancho en píxeles. / Width in pixels. */
|
|
695
|
+
get width() {
|
|
696
|
+
return this._media?.width ?? 0;
|
|
697
|
+
}
|
|
698
|
+
/** Alto en píxeles. / Height in pixels. */
|
|
699
|
+
get height() {
|
|
700
|
+
return this._media?.height ?? 0;
|
|
701
|
+
}
|
|
702
|
+
/** Peso en bytes. / Size in bytes. */
|
|
703
|
+
get size() {
|
|
704
|
+
return to_number(this._media?.fileLength);
|
|
705
|
+
}
|
|
706
|
+
/**
|
|
707
|
+
* Miniatura JPEG embebida, o null.
|
|
708
|
+
* Embedded JPEG thumbnail, or null.
|
|
709
|
+
*
|
|
710
|
+
* @returns Buffer de la miniatura o null / Thumbnail buffer or null
|
|
711
|
+
*/
|
|
712
|
+
async thumb() {
|
|
713
|
+
return to_buffer(this._media?.jpegThumbnail);
|
|
714
|
+
}
|
|
458
715
|
async content() {
|
|
459
|
-
return
|
|
716
|
+
return drain(await this.stream());
|
|
460
717
|
}
|
|
461
718
|
}
|
|
462
719
|
exports.Image = Image;
|
|
463
720
|
/**
|
|
464
|
-
* Mensaje de video
|
|
465
|
-
* Video message
|
|
721
|
+
* Mensaje de video: dimensiones, duración, peso y miniatura.
|
|
722
|
+
* Video message: dimensions, duration, size and thumbnail.
|
|
466
723
|
*/
|
|
467
724
|
class Video extends Message {
|
|
725
|
+
/** @internal */
|
|
726
|
+
get _media() {
|
|
727
|
+
return unwrap(this._raw.raw.message ?? {}).videoMessage;
|
|
728
|
+
}
|
|
729
|
+
/** Ancho en píxeles. / Width in pixels. */
|
|
730
|
+
get width() {
|
|
731
|
+
return this._media?.width ?? 0;
|
|
732
|
+
}
|
|
733
|
+
/** Alto en píxeles. / Height in pixels. */
|
|
734
|
+
get height() {
|
|
735
|
+
return this._media?.height ?? 0;
|
|
736
|
+
}
|
|
737
|
+
/** Duración en segundos. / Duration in seconds. */
|
|
738
|
+
get duration() {
|
|
739
|
+
return this._media?.seconds ?? 0;
|
|
740
|
+
}
|
|
741
|
+
/** Peso en bytes. / Size in bytes. */
|
|
742
|
+
get size() {
|
|
743
|
+
return to_number(this._media?.fileLength);
|
|
744
|
+
}
|
|
745
|
+
/**
|
|
746
|
+
* Miniatura JPEG embebida, o null.
|
|
747
|
+
* Embedded JPEG thumbnail, or null.
|
|
748
|
+
*
|
|
749
|
+
* @returns Buffer de la miniatura o null / Thumbnail buffer or null
|
|
750
|
+
*/
|
|
751
|
+
async thumb() {
|
|
752
|
+
return to_buffer(this._media?.jpegThumbnail);
|
|
753
|
+
}
|
|
468
754
|
async content() {
|
|
469
|
-
return
|
|
755
|
+
return drain(await this.stream());
|
|
470
756
|
}
|
|
471
757
|
}
|
|
472
758
|
exports.Video = Video;
|
|
473
759
|
/**
|
|
474
|
-
* Mensaje de audio
|
|
475
|
-
*
|
|
476
|
-
* Audio message. Inherits `stream()` from the base class; `content()` drains the stream.
|
|
477
|
-
* `ptt` flags voice notes.
|
|
760
|
+
* Mensaje de audio: nota de voz o archivo, con forma de onda del protocolo.
|
|
761
|
+
* Audio message: voice note or file, with the protocol waveform.
|
|
478
762
|
*/
|
|
479
763
|
class Audio extends Message {
|
|
480
|
-
/**
|
|
764
|
+
/** @internal */
|
|
765
|
+
get _media() {
|
|
766
|
+
return unwrap(this._raw.raw.message ?? {}).audioMessage;
|
|
767
|
+
}
|
|
768
|
+
/** true si es nota de voz. / true when push-to-talk. */
|
|
481
769
|
get ptt() {
|
|
482
|
-
return this.
|
|
770
|
+
return this._media?.ptt === true;
|
|
771
|
+
}
|
|
772
|
+
/** Duración en segundos. / Duration in seconds. */
|
|
773
|
+
get duration() {
|
|
774
|
+
return this._media?.seconds ?? 0;
|
|
775
|
+
}
|
|
776
|
+
/** Peso en bytes. / Size in bytes. */
|
|
777
|
+
get size() {
|
|
778
|
+
return to_number(this._media?.fileLength);
|
|
779
|
+
}
|
|
780
|
+
/** Forma de onda 0-100 lista para pintar. / Paint-ready 0-100 waveform. */
|
|
781
|
+
get waveform() {
|
|
782
|
+
const wave = this._media?.waveform;
|
|
783
|
+
const bytes = wave instanceof Uint8Array ? wave : typeof wave === 'string' ? Buffer.from(wave, 'base64') : Buffer.alloc(0);
|
|
784
|
+
return Array.from(bytes);
|
|
483
785
|
}
|
|
484
786
|
async content() {
|
|
485
|
-
return
|
|
787
|
+
return drain(await this.stream());
|
|
486
788
|
}
|
|
487
789
|
}
|
|
488
790
|
exports.Audio = Audio;
|
|
489
791
|
/**
|
|
490
|
-
* Mensaje de
|
|
491
|
-
*
|
|
792
|
+
* Mensaje de sticker: dimensiones, peso y animación.
|
|
793
|
+
* Sticker message: dimensions, size and animation.
|
|
794
|
+
*/
|
|
795
|
+
class Sticker extends Message {
|
|
796
|
+
/** @internal */
|
|
797
|
+
get _media() {
|
|
798
|
+
return unwrap(this._raw.raw.message ?? {}).stickerMessage;
|
|
799
|
+
}
|
|
800
|
+
/** Ancho en píxeles. / Width in pixels. */
|
|
801
|
+
get width() {
|
|
802
|
+
return this._media?.width ?? 0;
|
|
803
|
+
}
|
|
804
|
+
/** Alto en píxeles. / Height in pixels. */
|
|
805
|
+
get height() {
|
|
806
|
+
return this._media?.height ?? 0;
|
|
807
|
+
}
|
|
808
|
+
/** true si el sticker es animado. / true when animated. */
|
|
809
|
+
get animated() {
|
|
810
|
+
return this._media?.isAnimated === true;
|
|
811
|
+
}
|
|
812
|
+
/** Peso en bytes. / Size in bytes. */
|
|
813
|
+
get size() {
|
|
814
|
+
return to_number(this._media?.fileLength);
|
|
815
|
+
}
|
|
816
|
+
async content() {
|
|
817
|
+
return drain(await this.stream());
|
|
818
|
+
}
|
|
819
|
+
}
|
|
820
|
+
exports.Sticker = Sticker;
|
|
821
|
+
/**
|
|
822
|
+
* Mensaje de documento; el MIME real vive en `mime` de la base.
|
|
823
|
+
* Document message; the actual MIME lives in the base `mime`.
|
|
492
824
|
*/
|
|
493
|
-
class
|
|
825
|
+
class Document extends Message {
|
|
826
|
+
/** @internal */
|
|
827
|
+
get _media() {
|
|
828
|
+
return unwrap(this._raw.raw.message ?? {}).documentMessage;
|
|
829
|
+
}
|
|
830
|
+
/** Nombre del archivo. / File name. */
|
|
831
|
+
get name() {
|
|
832
|
+
return this._media?.fileName ?? '';
|
|
833
|
+
}
|
|
834
|
+
/** Número de páginas (PDF); 0 cuando no aplica. / Page count (PDF); 0 when not applicable. */
|
|
835
|
+
get pages() {
|
|
836
|
+
return this._media?.pageCount ?? 0;
|
|
837
|
+
}
|
|
838
|
+
/** Peso en bytes. / Size in bytes. */
|
|
839
|
+
get size() {
|
|
840
|
+
return to_number(this._media?.fileLength);
|
|
841
|
+
}
|
|
842
|
+
async content() {
|
|
843
|
+
return drain(await this.stream());
|
|
844
|
+
}
|
|
845
|
+
}
|
|
846
|
+
exports.Document = Document;
|
|
847
|
+
/**
|
|
848
|
+
* Mensaje de ubicación, fija o en vivo.
|
|
849
|
+
* Location message, static or live.
|
|
850
|
+
*/
|
|
851
|
+
class Location extends Message {
|
|
852
|
+
/** @internal */
|
|
494
853
|
get _loc() {
|
|
495
|
-
return this.
|
|
854
|
+
return this._raw.raw.message?.locationMessage ?? this._raw.raw.message?.liveLocationMessage;
|
|
496
855
|
}
|
|
497
856
|
/** Latitud en grados. / Latitude in degrees. */
|
|
498
857
|
get lat() {
|
|
@@ -502,287 +861,187 @@ class Gps extends Message {
|
|
|
502
861
|
get lng() {
|
|
503
862
|
return this._loc?.degreesLongitude ?? 0;
|
|
504
863
|
}
|
|
505
|
-
/** URL de Google Maps apuntando a la coordenada. / Google Maps URL for the coordinate. */
|
|
506
|
-
get link() {
|
|
507
|
-
return `https://www.google.com/maps/@${this.lat},${this.lng},15z`;
|
|
508
|
-
}
|
|
509
864
|
/** true si es ubicación en tiempo real. / true when live location. */
|
|
510
865
|
get live() {
|
|
511
|
-
return Boolean(this.
|
|
866
|
+
return Boolean(this._raw.raw.message?.liveLocationMessage);
|
|
867
|
+
}
|
|
868
|
+
/** URL de Google Maps de la coordenada. / Google Maps URL for the coordinate. */
|
|
869
|
+
get link() {
|
|
870
|
+
return `https://www.google.com/maps/@${this.lat},${this.lng},15z`;
|
|
512
871
|
}
|
|
513
872
|
}
|
|
514
|
-
exports.
|
|
873
|
+
exports.Location = Location;
|
|
515
874
|
/**
|
|
516
|
-
* Mensaje de encuesta
|
|
517
|
-
* Poll message
|
|
875
|
+
* Mensaje de encuesta: opciones con conteo, votos por contacto y voto propio.
|
|
876
|
+
* Poll message: options with counts, per-contact votes and self-voting.
|
|
518
877
|
*/
|
|
519
878
|
class Poll extends Message {
|
|
879
|
+
/** @internal */
|
|
520
880
|
get _poll() {
|
|
521
|
-
const
|
|
522
|
-
return
|
|
881
|
+
const msg = this._raw.raw.message;
|
|
882
|
+
return msg?.pollCreationMessage ?? msg?.pollCreationMessageV2 ?? msg?.pollCreationMessageV3;
|
|
883
|
+
}
|
|
884
|
+
/** @internal Updates de voto con los bytes normalizados post-engine. / Vote updates with post-engine normalized bytes. */
|
|
885
|
+
get _updates() {
|
|
886
|
+
return (this._raw.raw.pollUpdates ?? []).map((update) => ({
|
|
887
|
+
...update,
|
|
888
|
+
vote: update.vote
|
|
889
|
+
? {
|
|
890
|
+
...update.vote,
|
|
891
|
+
selectedOptions: (update.vote.selectedOptions ?? []).map((option) => typeof option === 'string' ? Buffer.from(option, 'base64') : option),
|
|
892
|
+
}
|
|
893
|
+
: update.vote,
|
|
894
|
+
}));
|
|
523
895
|
}
|
|
524
|
-
/**
|
|
525
|
-
* true si admite múltiples respuestas. Lee primero el flag explícito persistido
|
|
526
|
-
* al enviar (caso propio, donde WhatsApp pierde el discriminador en el echo);
|
|
527
|
-
* fallback al `selectableOptionsCount` del proto (polls foráneos o de UI móvil).
|
|
528
|
-
* true if the poll accepts multiple answers. Prefers the persisted send-time
|
|
529
|
-
* flag (own polls, since WhatsApp drops the discriminator on echo); falls back
|
|
530
|
-
* to the proto `selectableOptionsCount` (foreign or mobile-UI polls).
|
|
531
|
-
*/
|
|
896
|
+
/** true si admite varias respuestas. / true when multi-select. */
|
|
532
897
|
get multiple() {
|
|
533
|
-
if (typeof this.
|
|
534
|
-
return this.
|
|
898
|
+
if (typeof this._raw.multiple === 'boolean') {
|
|
899
|
+
return this._raw.multiple;
|
|
535
900
|
}
|
|
536
901
|
return (this._poll?.selectableOptionsCount ?? 1) !== 1;
|
|
537
902
|
}
|
|
538
|
-
/** Opciones con conteo de votos
|
|
903
|
+
/** Opciones con su conteo de votos. / Options with their vote counts. */
|
|
539
904
|
get options() {
|
|
540
|
-
const
|
|
541
|
-
const
|
|
542
|
-
|
|
543
|
-
|
|
544
|
-
|
|
545
|
-
vote: u.vote
|
|
546
|
-
? {
|
|
547
|
-
...u.vote,
|
|
548
|
-
selectedOptions: (u.vote.selectedOptions ?? []).map((s) => typeof s === 'string' ? Buffer.from(s, 'base64') : s),
|
|
549
|
-
}
|
|
550
|
-
: u.vote,
|
|
551
|
-
}));
|
|
552
|
-
const aggregated = (0, baileys_1.getAggregateVotesInPollMessage)({ message: this._doc.raw.message ?? undefined, pollUpdates: updates }, this._wa._socket?.user?.id);
|
|
553
|
-
const counts = new Map(aggregated.map((a) => [a.name, a.voters.length]));
|
|
554
|
-
return opts.map((o) => ({
|
|
555
|
-
content: o.optionName ?? '',
|
|
556
|
-
count: counts.get(o.optionName ?? '') ?? 0,
|
|
905
|
+
const aggregated = (0, baileys_1.getAggregateVotesInPollMessage)({ message: this._raw.raw.message ?? undefined, pollUpdates: this._updates }, (0, internal_1.internals)(this._wa).socket?.user?.id);
|
|
906
|
+
const counts = new Map(aggregated.map((entry) => [entry.name, entry.voters.length]));
|
|
907
|
+
return (this._poll?.options ?? []).map((option) => ({
|
|
908
|
+
name: option.optionName ?? '',
|
|
909
|
+
count: counts.get(option.optionName ?? '') ?? 0,
|
|
557
910
|
}));
|
|
558
911
|
}
|
|
559
912
|
/**
|
|
560
|
-
*
|
|
561
|
-
*
|
|
562
|
-
*
|
|
563
|
-
*
|
|
564
|
-
|
|
565
|
-
|
|
566
|
-
|
|
913
|
+
* Votos individuales: opción elegida y teléfono del votante.
|
|
914
|
+
* Individual votes: chosen option and voter phone.
|
|
915
|
+
*
|
|
916
|
+
* @returns Lista de votos / Vote list
|
|
917
|
+
*/
|
|
918
|
+
async votes() {
|
|
919
|
+
const aggregated = (0, baileys_1.getAggregateVotesInPollMessage)({ message: this._raw.raw.message ?? undefined, pollUpdates: this._updates }, (0, internal_1.internals)(this._wa).socket?.user?.id);
|
|
920
|
+
const result = [];
|
|
921
|
+
for (const entry of aggregated) {
|
|
922
|
+
for (const voter of entry.voters) {
|
|
923
|
+
const jid = await (0, internal_1.internals)(this._wa).resolve_jid(voter);
|
|
924
|
+
result.push({ name: entry.name, contact: (jid ?? voter).split('@')[0].split(':')[0] });
|
|
925
|
+
}
|
|
926
|
+
}
|
|
927
|
+
return result;
|
|
928
|
+
}
|
|
929
|
+
/**
|
|
930
|
+
* Vota en la encuesta con uno o varios índices. Cifra el voto (HMAC+AES-GCM) y lo
|
|
931
|
+
* enruta al chat @lid con la identidad LID propia. Best-effort: el voto entrante
|
|
932
|
+
* descifra correcto, pero WhatsApp no propaga el voto emitido desde un dispositivo
|
|
933
|
+
* vinculado (companion).
|
|
934
|
+
* Votes in the poll with one or more indexes. Encrypts the vote (HMAC+AES-GCM) and
|
|
935
|
+
* routes it to the @lid chat with the own LID identity. Best-effort: incoming votes
|
|
936
|
+
* decrypt fine, but WhatsApp does not propagate votes emitted from a linked
|
|
937
|
+
* (companion) device.
|
|
567
938
|
*
|
|
568
|
-
*
|
|
569
|
-
*
|
|
570
|
-
* incoming vote byte-for-byte. INCOMING votes work; a self-vote from this client does NOT
|
|
571
|
-
* show on the recipient's phone when this client is a linked (companion) device — the
|
|
572
|
-
* structure, identities and addressing match a valid vote but WhatsApp won't propagate it.
|
|
573
|
-
* Best-effort.
|
|
939
|
+
* @param index - Índice o índices de la opción / Option index or indexes
|
|
940
|
+
* @returns true si el voto se emitió / true when the vote was relayed
|
|
574
941
|
*/
|
|
575
942
|
async select(index) {
|
|
576
|
-
const
|
|
577
|
-
const indices = Array.isArray(index) ? index : [index];
|
|
943
|
+
const doc = this._raw;
|
|
578
944
|
const opts = this._poll?.options ?? [];
|
|
579
|
-
const selected_options =
|
|
945
|
+
const selected_options = (Array.isArray(index) ? index : [index])
|
|
580
946
|
.filter((i) => i >= 0 && i < opts.length)
|
|
581
947
|
.map((i) => (0, baileys_1.sha256)(Buffer.from(opts[i].optionName ?? '')));
|
|
582
|
-
|
|
583
|
-
// del engine). Normaliza a Buffer antes de derivar el HMAC.
|
|
584
|
-
// messageSecret may arrive as Buffer (runtime raw) or base64 string (post engine
|
|
585
|
-
// deserialize). Normalize to Buffer before deriving the HMAC.
|
|
586
|
-
const secret_raw = _doc.raw.message?.messageContextInfo?.messageSecret;
|
|
948
|
+
const secret_raw = doc.raw.message?.messageContextInfo?.messageSecret;
|
|
587
949
|
const poll_enc_key = typeof secret_raw === 'string' ? Buffer.from(secret_raw, 'base64') : secret_raw;
|
|
588
|
-
|
|
950
|
+
const socket = (0, internal_1.internals)(this._wa).socket;
|
|
951
|
+
if (!poll_enc_key || !socket || selected_options.length === 0) {
|
|
589
952
|
return false;
|
|
590
953
|
}
|
|
591
|
-
const poll_key =
|
|
592
|
-
const self_id =
|
|
593
|
-
const self_lid =
|
|
594
|
-
|
|
595
|
-
// en @lid y derivan la clave del JID visible del stanza; enviarlo por PN lo deja
|
|
596
|
-
// indescifrable en el teléfono. El @lid destino sale del propio poll (si ya es @lid),
|
|
597
|
-
// del store de contactos, o del lidMapping de baileys.
|
|
598
|
-
// The vote is routed to the contact's @lid chat: migrated clients run the chat on
|
|
599
|
-
// @lid and derive the key from the stanza's visible JID; sending via PN leaves it
|
|
600
|
-
// undecryptable on the phone. The @lid dest comes from the poll (if already @lid),
|
|
601
|
-
// the contact store, or baileys' lidMapping.
|
|
602
|
-
let dest = _doc.cid;
|
|
954
|
+
const poll_key = doc.raw.key ?? {};
|
|
955
|
+
const self_id = socket.user?.id ?? '';
|
|
956
|
+
const self_lid = socket.user?.lid ?? '';
|
|
957
|
+
let dest = doc.cid;
|
|
603
958
|
if (poll_key.remoteJid?.endsWith('@lid')) {
|
|
604
959
|
dest = poll_key.remoteJid;
|
|
605
960
|
}
|
|
606
|
-
else if (!
|
|
607
|
-
const contact_raw = (0, store_1.deserialize)(await _wa.engine.get(`/contact/${
|
|
608
|
-
const mapped = await
|
|
609
|
-
dest = contact_raw?.lid ?? mapped ??
|
|
961
|
+
else if (!doc.cid.endsWith('@lid')) {
|
|
962
|
+
const contact_raw = (0, store_1.deserialize)(await this._wa.engine.get(`/contact/${doc.cid}`));
|
|
963
|
+
const mapped = await socket.signalRepository?.lidMapping?.getLIDForPN(doc.cid).catch(() => null);
|
|
964
|
+
dest = contact_raw?.lid ?? mapped ?? doc.cid;
|
|
610
965
|
}
|
|
611
|
-
// Identidad del HMAC atada al destino real: baileys firma con meLid cuando el envío
|
|
612
|
-
// va por @lid, así que voter y (en polls propios) creator usan el LID propio para que
|
|
613
|
-
// el receptor derive la misma clave. PN sólo si no se conoce ningún LID.
|
|
614
|
-
// HMAC identity tied to the actual destination: baileys signs with meLid for @lid
|
|
615
|
-
// sends, so voter and (on own polls) creator use our LID so the receiver derives the
|
|
616
|
-
// same key. PN only when no LID is known.
|
|
617
966
|
const self_for_send = dest.endsWith('@lid') && self_lid ? self_lid : self_id;
|
|
618
967
|
const voter_jid = (0, baileys_1.jidNormalizedUser)(self_for_send);
|
|
619
|
-
|
|
620
|
-
|
|
621
|
-
|
|
622
|
-
|
|
623
|
-
|
|
624
|
-
poll_creator_raw = poll_key.remoteJid;
|
|
625
|
-
}
|
|
626
|
-
else {
|
|
627
|
-
poll_creator_raw = (0, baileys_1.getKeyAuthor)(poll_key, self_id);
|
|
628
|
-
}
|
|
629
|
-
const poll_creator_jid = (0, baileys_1.jidNormalizedUser)(poll_creator_raw);
|
|
630
|
-
const poll_msg_id = this.id;
|
|
631
|
-
// Derivar clave y encriptar el voto (mismo algoritmo que baileys.decryptPollVote)
|
|
968
|
+
const poll_creator_jid = (0, baileys_1.jidNormalizedUser)(poll_key.fromMe
|
|
969
|
+
? self_for_send
|
|
970
|
+
: poll_key.remoteJid?.endsWith('@lid')
|
|
971
|
+
? poll_key.remoteJid
|
|
972
|
+
: (0, baileys_1.getKeyAuthor)(poll_key, self_id));
|
|
632
973
|
const sign = Buffer.concat([
|
|
633
|
-
Buffer.from(
|
|
974
|
+
Buffer.from(doc.id),
|
|
634
975
|
Buffer.from(poll_creator_jid),
|
|
635
976
|
Buffer.from(voter_jid),
|
|
636
977
|
Buffer.from('Poll Vote'),
|
|
637
978
|
new Uint8Array([1]),
|
|
638
979
|
]);
|
|
639
|
-
const
|
|
640
|
-
const enc_key = (0, baileys_1.hmacSign)(sign, key0, 'sha256');
|
|
641
|
-
const payload = baileys_1.proto.Message.PollVoteMessage.encode({
|
|
642
|
-
selectedOptions: selected_options,
|
|
643
|
-
}).finish();
|
|
644
|
-
const aad = Buffer.from(`${poll_msg_id}\x00${voter_jid}`);
|
|
980
|
+
const enc_key = (0, baileys_1.hmacSign)(sign, (0, baileys_1.hmacSign)(poll_enc_key, new Uint8Array(32), 'sha256'), 'sha256');
|
|
645
981
|
const enc_iv = (0, node_crypto_1.randomBytes)(12);
|
|
646
|
-
const enc_payload = (0, baileys_1.aesEncryptGCM)(
|
|
982
|
+
const enc_payload = (0, baileys_1.aesEncryptGCM)(baileys_1.proto.Message.PollVoteMessage.encode({ selectedOptions: selected_options }).finish(), enc_key, enc_iv, Buffer.from(`${doc.id}\x00${voter_jid}`));
|
|
647
983
|
const msg_id = (0, baileys_1.generateMessageID)();
|
|
648
|
-
|
|
649
|
-
// stanza (dest): un voto entrante que sí funciona trae la key con remoteJid = @lid del
|
|
650
|
-
// creador, coincidiendo con el chat @lid del stanza. Enviarla con remoteJid PN mientras
|
|
651
|
-
// el stanza va por @lid deja al receptor sin poder casar el voto con el poll.
|
|
652
|
-
// The pollCreationMessageKey must reference the poll with the same addressing as the
|
|
653
|
-
// stanza (dest): a working incoming vote carries the key with the creator's @lid
|
|
654
|
-
// remoteJid, matching the @lid stanza chat. A PN remoteJid on an @lid stanza leaves the
|
|
655
|
-
// receiver unable to match the vote to the poll.
|
|
656
|
-
const creation_key = dest === _doc.cid ? _doc.raw.key : { ..._doc.raw.key, remoteJid: dest };
|
|
657
|
-
await _wa._socket.relayMessage(dest, {
|
|
984
|
+
await socket.relayMessage(dest, {
|
|
658
985
|
pollUpdateMessage: {
|
|
659
|
-
pollCreationMessageKey:
|
|
986
|
+
pollCreationMessageKey: dest === doc.cid ? doc.raw.key : { ...doc.raw.key, remoteJid: dest },
|
|
660
987
|
vote: { encPayload: enc_payload, encIv: enc_iv },
|
|
661
988
|
senderTimestampMs: Date.now(),
|
|
662
989
|
},
|
|
663
990
|
}, { messageId: msg_id });
|
|
664
|
-
|
|
665
|
-
|
|
666
|
-
// `message:updated` reflejen el cambio sin esperar (nunca) un echo del servidor.
|
|
667
|
-
// baileys.relayMessage doesn't emit a local upsert on the originating companion.
|
|
668
|
-
// We merge the own vote into the doc so `options[].count` and the `message:updated`
|
|
669
|
-
// event reflect the change without waiting for a server echo that never comes.
|
|
670
|
-
const own_update_key = {
|
|
671
|
-
remoteJid: _doc.raw.key?.remoteJid ?? _doc.cid,
|
|
672
|
-
fromMe: true,
|
|
673
|
-
id: msg_id,
|
|
674
|
-
};
|
|
675
|
-
(0, baileys_1.updateMessageWithPollUpdate)(_doc.raw, {
|
|
676
|
-
pollUpdateMessageKey: own_update_key,
|
|
991
|
+
(0, baileys_1.updateMessageWithPollUpdate)(doc.raw, {
|
|
992
|
+
pollUpdateMessageKey: { remoteJid: doc.raw.key?.remoteJid ?? doc.cid, fromMe: true, id: msg_id },
|
|
677
993
|
vote: { selectedOptions: selected_options },
|
|
678
994
|
senderTimestampMs: Date.now(),
|
|
679
995
|
});
|
|
680
|
-
await _wa.engine.set(`/chat/${
|
|
681
|
-
_wa.
|
|
996
|
+
await this._wa.engine.set(`/chat/${doc.cid}/message/${doc.id}`, (0, store_1.serialize)(doc), doc.created_at);
|
|
997
|
+
this._wa.emit('message:updated', this, await this.chat(), this._wa);
|
|
682
998
|
return true;
|
|
683
999
|
}
|
|
684
1000
|
}
|
|
685
1001
|
exports.Poll = Poll;
|
|
686
1002
|
/**
|
|
687
|
-
* Mensaje de
|
|
688
|
-
*
|
|
689
|
-
* el tipo de mensaje (`documentMessage`), no el mimetype. Hereda `stream()` de la
|
|
690
|
-
* clase base; `content()` drena el stream (cache → download).
|
|
691
|
-
* Document message. WhatsApp lets you send any file (PDF, image, audio, video…)
|
|
692
|
-
* as a document; what sets it apart from image/video/audio is the message type
|
|
693
|
-
* (`documentMessage`), not the mimetype. Inherits `stream()` from the base class;
|
|
694
|
-
* `content()` drains the stream (cache → download).
|
|
695
|
-
*/
|
|
696
|
-
class Document extends Message {
|
|
697
|
-
get _doc_msg() {
|
|
698
|
-
const m = this._doc.raw.message;
|
|
699
|
-
return m?.documentMessage ?? m?.documentWithCaptionMessage?.message?.documentMessage;
|
|
700
|
-
}
|
|
701
|
-
/** Nombre del archivo. / File name. */
|
|
702
|
-
get file_name() {
|
|
703
|
-
return this._doc_msg?.fileName ?? '';
|
|
704
|
-
}
|
|
705
|
-
/** Tamaño en bytes (0 si se desconoce). / Size in bytes (0 if unknown). */
|
|
706
|
-
get size() {
|
|
707
|
-
return Number(this._doc_msg?.fileLength?.toString() ?? '0');
|
|
708
|
-
}
|
|
709
|
-
/** Número de páginas (PDF); 0 si no aplica. / Page count (PDF); 0 when not applicable. */
|
|
710
|
-
get pages() {
|
|
711
|
-
return this._doc_msg?.pageCount ?? 0;
|
|
712
|
-
}
|
|
713
|
-
/** Título del documento. / Document title. */
|
|
714
|
-
get title() {
|
|
715
|
-
return this._doc_msg?.title ?? '';
|
|
716
|
-
}
|
|
717
|
-
async content() {
|
|
718
|
-
return _drain(await this.stream());
|
|
719
|
-
}
|
|
720
|
-
}
|
|
721
|
-
exports.Document = Document;
|
|
722
|
-
/**
|
|
723
|
-
* Construye un vCard 3.0 mínimo con el teléfono enlazado como contacto de WhatsApp.
|
|
724
|
-
* Builds a minimal vCard 3.0 with the phone linked as a WhatsApp contact.
|
|
725
|
-
*
|
|
726
|
-
* @param name - Nombre mostrado / Display name
|
|
727
|
-
* @param phone - Teléfono en formato internacional / Phone in international format
|
|
728
|
-
* @returns String vCard (VCF) / vCard (VCF) string
|
|
729
|
-
*/
|
|
730
|
-
function build_vcard(name, phone) {
|
|
731
|
-
const waid = phone.replace(/\D+/g, '');
|
|
732
|
-
return [
|
|
733
|
-
'BEGIN:VCARD',
|
|
734
|
-
'VERSION:3.0',
|
|
735
|
-
`FN:${name}`,
|
|
736
|
-
`TEL;type=CELL;waid=${waid}:${phone}`,
|
|
737
|
-
'END:VCARD',
|
|
738
|
-
].join('\n');
|
|
739
|
-
}
|
|
740
|
-
/**
|
|
741
|
-
* Mensaje de tarjeta(s) de contacto (vCard). Unifica `contactMessage` (uno) y
|
|
742
|
-
* `contactsArrayMessage` (varios) bajo una sola lista `contacts`.
|
|
743
|
-
* Contact card message (vCard). Unifies `contactMessage` (single) and
|
|
744
|
-
* `contactsArrayMessage` (multiple) under a single `contacts` list.
|
|
1003
|
+
* Mensaje de tarjeta(s) de contacto.
|
|
1004
|
+
* Contact card(s) message.
|
|
745
1005
|
*/
|
|
746
1006
|
class VCard extends Message {
|
|
747
|
-
/** Contactos de la tarjeta
|
|
1007
|
+
/** Contactos de la tarjeta. / Card contacts. */
|
|
748
1008
|
get contacts() {
|
|
749
|
-
const
|
|
750
|
-
const raw =
|
|
751
|
-
return raw.map((
|
|
752
|
-
const tel = (
|
|
753
|
-
return { name:
|
|
1009
|
+
const msg = this._raw.raw.message;
|
|
1010
|
+
const raw = msg?.contactsArrayMessage?.contacts ?? (msg?.contactMessage ? [msg.contactMessage] : []);
|
|
1011
|
+
return raw.map((card) => {
|
|
1012
|
+
const tel = (card.vcard ?? '').match(/TEL[^:]*:(.+)/);
|
|
1013
|
+
return { name: card.displayName ?? '', phone: tel ? tel[1].trim() : '' };
|
|
754
1014
|
});
|
|
755
1015
|
}
|
|
756
1016
|
}
|
|
757
1017
|
exports.VCard = VCard;
|
|
758
1018
|
/**
|
|
759
|
-
* Mensaje de evento de calendario
|
|
760
|
-
*
|
|
761
|
-
* Calendar event message (`eventMessage`): name, description, date range,
|
|
762
|
-
* cancellation state, join link and location.
|
|
1019
|
+
* Mensaje de evento de calendario.
|
|
1020
|
+
* Calendar event message.
|
|
763
1021
|
*/
|
|
764
1022
|
class Event extends Message {
|
|
1023
|
+
/** @internal */
|
|
765
1024
|
get _event() {
|
|
766
|
-
return this.
|
|
1025
|
+
return this._raw.raw.message?.eventMessage;
|
|
767
1026
|
}
|
|
768
1027
|
/** Nombre del evento. / Event name. */
|
|
769
1028
|
get name() {
|
|
770
1029
|
return this._event?.name ?? '';
|
|
771
1030
|
}
|
|
772
|
-
/**
|
|
1031
|
+
/** Inicio en ISO UTC. / Start as ISO UTC. */
|
|
773
1032
|
get start() {
|
|
774
|
-
return
|
|
1033
|
+
return new Date(to_number(this._event?.startTime) * 1000).toISOString();
|
|
775
1034
|
}
|
|
776
|
-
/**
|
|
1035
|
+
/** Fin en ISO UTC, o null. / End as ISO UTC, or null. */
|
|
777
1036
|
get end() {
|
|
778
1037
|
const end = this._event?.endTime;
|
|
779
|
-
return end == null ? null :
|
|
1038
|
+
return end == null ? null : new Date(to_number(end) * 1000).toISOString();
|
|
780
1039
|
}
|
|
781
|
-
/** true si
|
|
1040
|
+
/** true si fue cancelado. / true when canceled. */
|
|
782
1041
|
get canceled() {
|
|
783
1042
|
return this._event?.isCanceled ?? false;
|
|
784
1043
|
}
|
|
785
|
-
/** Link de unión
|
|
1044
|
+
/** Link de unión, si aplica. / Join link, if any. */
|
|
786
1045
|
get link() {
|
|
787
1046
|
return this._event?.joinLink ?? '';
|
|
788
1047
|
}
|
|
@@ -794,205 +1053,74 @@ class Event extends Message {
|
|
|
794
1053
|
}
|
|
795
1054
|
exports.Event = Event;
|
|
796
1055
|
/**
|
|
797
|
-
* Factoría
|
|
798
|
-
*
|
|
1056
|
+
* Factoría de Message: evalúa el tipo y retorna la instancia de la subclase correcta.
|
|
1057
|
+
* Acepta el documento persistido o el mensaje crudo de baileys — en ese caso el
|
|
1058
|
+
* documento se deriva aquí mismo (id, tipo, autor, caption, mime, fechas).
|
|
1059
|
+
* Message factory: evaluates the type and returns the right subclass instance. Accepts
|
|
1060
|
+
* the persisted document or the raw baileys message — in that case the document is
|
|
1061
|
+
* derived right here (id, type, author, caption, mime, dates).
|
|
1062
|
+
*
|
|
1063
|
+
* @param wa - Instancia principal / Main WhatsApp instance
|
|
1064
|
+
* @param raw - Documento persistido o WAMessage crudo / Persisted document or raw WAMessage
|
|
1065
|
+
* @returns Instancia del tipo correcto / Right-type instance
|
|
799
1066
|
*/
|
|
800
|
-
function message(wa) {
|
|
801
|
-
|
|
802
|
-
|
|
803
|
-
|
|
804
|
-
|
|
805
|
-
|
|
806
|
-
|
|
807
|
-
|
|
808
|
-
|
|
809
|
-
|
|
810
|
-
|
|
811
|
-
|
|
812
|
-
|
|
813
|
-
|
|
814
|
-
|
|
815
|
-
|
|
816
|
-
|
|
817
|
-
|
|
818
|
-
|
|
819
|
-
|
|
820
|
-
|
|
821
|
-
|
|
822
|
-
|
|
823
|
-
|
|
824
|
-
|
|
825
|
-
|
|
826
|
-
|
|
827
|
-
|
|
828
|
-
|
|
829
|
-
|
|
830
|
-
|
|
831
|
-
|
|
832
|
-
|
|
833
|
-
|
|
834
|
-
|
|
835
|
-
|
|
836
|
-
|
|
837
|
-
|
|
838
|
-
|
|
839
|
-
|
|
840
|
-
|
|
841
|
-
|
|
842
|
-
}
|
|
843
|
-
const raw = await wa._socket.sendMessage(jid, {
|
|
844
|
-
...content,
|
|
845
|
-
...(quoted ? { quoted } : {}),
|
|
846
|
-
});
|
|
847
|
-
const sent_id = raw?.key?.id;
|
|
848
|
-
if (raw && sent_id) {
|
|
849
|
-
const doc = build_message_doc(raw, wa._socket?.user?.id);
|
|
850
|
-
if (doc_overrides) {
|
|
851
|
-
Object.assign(doc, doc_overrides);
|
|
852
|
-
}
|
|
853
|
-
await wa.engine.set(`/chat/${jid}/message/${sent_id}`, (0, store_1.serialize)(doc));
|
|
854
|
-
if (binary) {
|
|
855
|
-
await wa.engine.set(`/chat/${jid}/message/${sent_id}/content`, (0, store_1.serialize)({ data: binary.toString('base64') }));
|
|
856
|
-
}
|
|
857
|
-
result = await build_instance(doc);
|
|
858
|
-
}
|
|
859
|
-
}
|
|
860
|
-
return result;
|
|
1067
|
+
function message(wa, raw) {
|
|
1068
|
+
let doc;
|
|
1069
|
+
if ('key' in raw) {
|
|
1070
|
+
const key = raw.key ?? {};
|
|
1071
|
+
const unwrapped = unwrap(raw.message ?? {});
|
|
1072
|
+
const content_type = (0, baileys_1.getContentType)(unwrapped);
|
|
1073
|
+
const msg_type = MESSAGE_TYPE_MAP[content_type] ?? 'text';
|
|
1074
|
+
const msg_content = unwrapped[content_type];
|
|
1075
|
+
const context_info = msg_content?.contextInfo;
|
|
1076
|
+
const ephemeral_duration = raw.ephemeralDuration ?? context_info?.expiration;
|
|
1077
|
+
const self_id = (0, internal_1.internals)(wa).socket?.user?.id;
|
|
1078
|
+
doc = {
|
|
1079
|
+
id: key.id ?? '',
|
|
1080
|
+
cid: key.remoteJidAlt ?? key.remoteJid ?? '',
|
|
1081
|
+
mid: context_info?.stanzaId ?? null,
|
|
1082
|
+
me: key.fromMe ?? false,
|
|
1083
|
+
type: msg_type,
|
|
1084
|
+
author: key.fromMe && self_id
|
|
1085
|
+
? (0, baileys_1.jidNormalizedUser)(self_id)
|
|
1086
|
+
: key.participant || key.remoteJidAlt || key.remoteJid || '',
|
|
1087
|
+
status: raw.status ?? 1,
|
|
1088
|
+
starred: raw.starred ?? false,
|
|
1089
|
+
forwarded: context_info?.isForwarded ?? false,
|
|
1090
|
+
created_at: (Number(raw.messageTimestamp) || Math.floor(Date.now() / 1000)) * 1000,
|
|
1091
|
+
deleted_at: raw.ephemeralStartTimestamp != null && ephemeral_duration != null
|
|
1092
|
+
? (Number(raw.ephemeralStartTimestamp) + ephemeral_duration) * 1000
|
|
1093
|
+
: null,
|
|
1094
|
+
mime: typeof msg_content === 'object' && msg_type !== 'text'
|
|
1095
|
+
? (msg_content?.mimetype ?? 'application/octet-stream')
|
|
1096
|
+
: 'text/plain',
|
|
1097
|
+
caption: typeof msg_content === 'string'
|
|
1098
|
+
? msg_content
|
|
1099
|
+
: msg_type === 'event'
|
|
1100
|
+
? (msg_content?.description ?? '')
|
|
1101
|
+
: (msg_content?.caption ??
|
|
1102
|
+
msg_content?.text ??
|
|
1103
|
+
msg_content?.name ??
|
|
1104
|
+
msg_content?.displayName ??
|
|
1105
|
+
''),
|
|
1106
|
+
edited: false,
|
|
1107
|
+
raw,
|
|
1108
|
+
};
|
|
861
1109
|
}
|
|
862
|
-
|
|
863
|
-
|
|
864
|
-
|
|
865
|
-
|
|
866
|
-
|
|
867
|
-
|
|
868
|
-
|
|
869
|
-
|
|
870
|
-
|
|
871
|
-
|
|
872
|
-
|
|
873
|
-
|
|
874
|
-
|
|
875
|
-
|
|
876
|
-
/** Obtiene un mensaje por CID/MID. */
|
|
877
|
-
async get(cid, mid) {
|
|
878
|
-
const jid = await wa._resolve_jid(cid);
|
|
879
|
-
if (!jid) {
|
|
880
|
-
return null;
|
|
881
|
-
}
|
|
882
|
-
const doc = (0, store_1.deserialize)(await wa.engine.get(`/chat/${jid}/message/${mid}`));
|
|
883
|
-
return doc ? build_instance(doc) : null;
|
|
884
|
-
},
|
|
885
|
-
/** Pagina mensajes de un chat. */
|
|
886
|
-
async list(cid, offset = 0, limit = 50) {
|
|
887
|
-
const jid = await wa._resolve_jid(cid);
|
|
888
|
-
const result = [];
|
|
889
|
-
if (jid) {
|
|
890
|
-
for (const raw of await wa.engine.list(`/chat/${jid}/message`, offset, limit)) {
|
|
891
|
-
const doc = (0, store_1.deserialize)(raw);
|
|
892
|
-
if (doc) {
|
|
893
|
-
result.push(await build_instance(doc));
|
|
894
|
-
}
|
|
895
|
-
}
|
|
896
|
-
}
|
|
897
|
-
return result;
|
|
898
|
-
},
|
|
899
|
-
/** Cuenta mensajes de un chat. */
|
|
900
|
-
async count(cid) {
|
|
901
|
-
const jid = await wa._resolve_jid(cid);
|
|
902
|
-
return jid ? wa.engine.count(`/chat/${jid}/message`) : 0;
|
|
903
|
-
},
|
|
904
|
-
async text(cid, caption, opts = {}) {
|
|
905
|
-
return send(cid, { text: caption }, undefined, opts.mid);
|
|
906
|
-
},
|
|
907
|
-
async image(cid, buf, opts = {}) {
|
|
908
|
-
return send(cid, { image: buf, caption: opts.caption }, buf, opts.mid);
|
|
909
|
-
},
|
|
910
|
-
async video(cid, buf, opts = {}) {
|
|
911
|
-
return send(cid, { video: buf, caption: opts.caption }, buf, opts.mid);
|
|
912
|
-
},
|
|
913
|
-
async audio(cid, buf, opts = {}) {
|
|
914
|
-
return send(cid, { audio: buf, ptt: opts.ptt ?? true }, buf, opts.mid);
|
|
915
|
-
},
|
|
916
|
-
async location(cid, loc, opts = {}) {
|
|
917
|
-
return send(cid, { location: { degreesLatitude: loc.lat, degreesLongitude: loc.lng } }, undefined, opts.mid);
|
|
918
|
-
},
|
|
919
|
-
async poll(cid, p, opts = {}) {
|
|
920
|
-
const multiple = opts.multiple ?? false;
|
|
921
|
-
return send(cid, {
|
|
922
|
-
poll: {
|
|
923
|
-
name: p.content,
|
|
924
|
-
values: p.options.map((o) => o.content),
|
|
925
|
-
selectableCount: multiple ? 0 : 1,
|
|
926
|
-
},
|
|
927
|
-
}, undefined, opts.mid, { multiple });
|
|
928
|
-
},
|
|
929
|
-
async document(cid, buf, opts) {
|
|
930
|
-
return send(cid, {
|
|
931
|
-
document: buf,
|
|
932
|
-
fileName: opts.file_name,
|
|
933
|
-
mimetype: opts.mimetype ?? 'application/octet-stream',
|
|
934
|
-
caption: opts.caption,
|
|
935
|
-
}, buf, opts.mid);
|
|
936
|
-
},
|
|
937
|
-
async vcard(cid, contacts, opts = {}) {
|
|
938
|
-
const built = contacts.map((c) => ({
|
|
939
|
-
displayName: c.name,
|
|
940
|
-
vcard: build_vcard(c.name, c.phone),
|
|
941
|
-
}));
|
|
942
|
-
const binary = Buffer.from(built.map((c) => c.vcard).join('\n'), 'utf-8');
|
|
943
|
-
return send(cid, { contacts: { contacts: built } }, binary, opts.mid);
|
|
944
|
-
},
|
|
945
|
-
async event(cid, data, opts = {}) {
|
|
946
|
-
const binary = Buffer.from(JSON.stringify({
|
|
947
|
-
name: data.name,
|
|
948
|
-
caption: data.caption,
|
|
949
|
-
start: data.start.getTime(),
|
|
950
|
-
end: data.end?.getTime() ?? null,
|
|
951
|
-
}), 'utf-8');
|
|
952
|
-
return send(cid, {
|
|
953
|
-
event: {
|
|
954
|
-
name: data.name,
|
|
955
|
-
description: data.caption,
|
|
956
|
-
startDate: data.start,
|
|
957
|
-
endDate: data.end,
|
|
958
|
-
location: data.place
|
|
959
|
-
? { degreesLatitude: data.place.lat, degreesLongitude: data.place.lng }
|
|
960
|
-
: undefined,
|
|
961
|
-
},
|
|
962
|
-
}, binary, opts.mid);
|
|
963
|
-
},
|
|
964
|
-
async edit(cid, mid, text) {
|
|
965
|
-
const m = await api.get(cid, mid);
|
|
966
|
-
return m ? m.edit(text) : false;
|
|
967
|
-
},
|
|
968
|
-
async delete(cid, mid, all = true) {
|
|
969
|
-
const m = await api.get(cid, mid);
|
|
970
|
-
return m ? m.delete(all) : false;
|
|
971
|
-
},
|
|
972
|
-
async react(cid, mid, emoji) {
|
|
973
|
-
const m = await api.get(cid, mid);
|
|
974
|
-
return m ? m.react(emoji) : false;
|
|
975
|
-
},
|
|
976
|
-
async forward(cid, mid, target) {
|
|
977
|
-
const m = await api.get(cid, mid);
|
|
978
|
-
return m ? m.forward(target) : false;
|
|
979
|
-
},
|
|
980
|
-
async seen(cid, mid) {
|
|
981
|
-
const m = await api.get(cid, mid);
|
|
982
|
-
return m ? m.seen() : false;
|
|
983
|
-
},
|
|
984
|
-
async star(cid, mid, value) {
|
|
985
|
-
const m = await api.get(cid, mid);
|
|
986
|
-
return m ? m.star(value) : false;
|
|
987
|
-
},
|
|
988
|
-
watch(cid, mid, handler) {
|
|
989
|
-
return wa.on('message:updated', (updated) => {
|
|
990
|
-
if (updated.cid === cid && updated.id === mid) {
|
|
991
|
-
handler(updated);
|
|
992
|
-
}
|
|
993
|
-
});
|
|
994
|
-
},
|
|
1110
|
+
else {
|
|
1111
|
+
doc = raw;
|
|
1112
|
+
}
|
|
1113
|
+
const types = {
|
|
1114
|
+
text: Text,
|
|
1115
|
+
image: Image,
|
|
1116
|
+
video: Video,
|
|
1117
|
+
audio: Audio,
|
|
1118
|
+
sticker: Sticker,
|
|
1119
|
+
document: Document,
|
|
1120
|
+
location: Location,
|
|
1121
|
+
poll: Poll,
|
|
1122
|
+
vcard: VCard,
|
|
1123
|
+
event: Event,
|
|
995
1124
|
};
|
|
996
|
-
return
|
|
1125
|
+
return new (types[doc.type] ?? Message)(wa, doc);
|
|
997
1126
|
}
|
|
998
|
-
//# sourceMappingURL=index.js.map
|