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