@arcaelas/whatsapp 7.4.2 → 8.2.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/build/cjs/index.d.ts +1 -1
- package/build/cjs/lib/chat/index.d.ts +55 -4
- package/build/cjs/lib/chat/index.js +31 -0
- package/build/cjs/lib/contact/index.d.ts +76 -0
- package/build/cjs/lib/contact/index.js +42 -0
- package/build/cjs/lib/message/index.d.ts +76 -0
- package/build/cjs/lib/message/index.js +81 -0
- package/build/cjs/lib/whatsapp/index.d.ts +34 -1
- package/build/cjs/lib/whatsapp/index.js +127 -5
- package/build/esm/index.d.ts +1 -1
- package/build/esm/lib/chat/index.d.ts +55 -4
- package/build/esm/lib/chat/index.js +31 -0
- package/build/esm/lib/contact/index.d.ts +76 -0
- package/build/esm/lib/contact/index.js +42 -0
- package/build/esm/lib/message/index.d.ts +76 -0
- package/build/esm/lib/message/index.js +81 -0
- package/build/esm/lib/whatsapp/index.d.ts +34 -1
- package/build/esm/lib/whatsapp/index.js +128 -6
- package/package.json +1 -1
|
@@ -288,6 +288,51 @@ export default class Message {
|
|
|
288
288
|
await this._init.engine.set(`/chat/${doc.cid}/message/${doc.id}`, serialize(doc), doc.created_at);
|
|
289
289
|
return true;
|
|
290
290
|
}
|
|
291
|
+
/**
|
|
292
|
+
* Supervisa qué le pasa a este mensaje: que lo lean, que reproduzcan su audio o que lo
|
|
293
|
+
* retiren. Vive en la clase base, así que todo tipo de mensaje lo hereda.
|
|
294
|
+
*
|
|
295
|
+
* Por dentro escucha las actualizaciones del mensaje y las traduce: `message:updated` no
|
|
296
|
+
* distingue leído de reproducido —los dos son un cambio de estado— y quien supervisa un
|
|
297
|
+
* mensaje quiere saber cuál de los dos ocurrió, no que «algo cambió».
|
|
298
|
+
* Watches what happens to this message: that it gets read, that its audio gets played, or
|
|
299
|
+
* that it is retired. It lives on the base class, so every message type inherits it.
|
|
300
|
+
*
|
|
301
|
+
* Under the hood it listens to the message's updates and translates them: `message:updated`
|
|
302
|
+
* does not tell read from played —both are a status change— and whoever watches a message
|
|
303
|
+
* wants to know which of the two happened, not that «something changed».
|
|
304
|
+
*
|
|
305
|
+
* @param handler - Recibe cada cambio / Receives every change
|
|
306
|
+
* @returns Función para dejar de supervisar / Function to stop watching
|
|
307
|
+
*/
|
|
308
|
+
watch(handler) {
|
|
309
|
+
const { wa } = this._init;
|
|
310
|
+
let seen = this._raw.status;
|
|
311
|
+
const off_updated = wa.on('message:updated', (msg) => {
|
|
312
|
+
if (msg.id === this._raw.id) {
|
|
313
|
+
const status = msg._raw.status;
|
|
314
|
+
// Sólo los avances cuentan: un mismo estado repetido no es una noticia, y sin
|
|
315
|
+
// este corte cada acuse reenviado por WhatsApp se contaría como una lectura más.
|
|
316
|
+
// Only advances count: a repeated status is not news, and without this cut every
|
|
317
|
+
// acknowledgement WhatsApp resends would count as another read.
|
|
318
|
+
if (status > seen) {
|
|
319
|
+
seen = status;
|
|
320
|
+
if (status >= proto.WebMessageInfo.Status.PLAYED)
|
|
321
|
+
handler({ name: 'played', payload: msg });
|
|
322
|
+
else if (status >= proto.WebMessageInfo.Status.READ)
|
|
323
|
+
handler({ name: 'read', payload: msg });
|
|
324
|
+
}
|
|
325
|
+
}
|
|
326
|
+
});
|
|
327
|
+
const off_deleted = wa.on('message:deleted', (msg) => {
|
|
328
|
+
if (msg.id === this._raw.id)
|
|
329
|
+
handler({ name: 'deleted', payload: msg });
|
|
330
|
+
});
|
|
331
|
+
return () => {
|
|
332
|
+
off_updated();
|
|
333
|
+
off_deleted();
|
|
334
|
+
};
|
|
335
|
+
}
|
|
291
336
|
/** Marca el mensaje como leído. / Marks the message as read. */
|
|
292
337
|
async seen() {
|
|
293
338
|
const { cid, id, author } = this._raw;
|
|
@@ -467,6 +512,23 @@ export class Audio extends Media {
|
|
|
467
512
|
get duration() { return this._media?.seconds ?? 0; }
|
|
468
513
|
/** Forma de onda 0-100 lista para pintar. / Paint-ready 0-100 waveform. */
|
|
469
514
|
get waveform() { return Array.from(to_buffer(this._media?.waveform) ?? []); }
|
|
515
|
+
/** true si ya fue reproducido: el micrófono azul que ve quien lo mandó. / true when already played: the blue mic its sender sees. */
|
|
516
|
+
get played() { return this._raw.status >= proto.WebMessageInfo.Status.PLAYED; }
|
|
517
|
+
/**
|
|
518
|
+
* Acusa el audio como reproducido. Es un aviso aparte del de leído —abrir el chat no
|
|
519
|
+
* reproduce nada—, y por eso viaja como recibo propio: quien lo mandó ve el micrófono
|
|
520
|
+
* azul sólo después de esto.
|
|
521
|
+
* Acknowledges the audio as played. It is separate from the read receipt —opening the chat
|
|
522
|
+
* plays nothing— and so travels as its own receipt: whoever sent it sees the blue mic only
|
|
523
|
+
* after this.
|
|
524
|
+
*
|
|
525
|
+
* @returns true cuando el acuse salió / true once the receipt left
|
|
526
|
+
*/
|
|
527
|
+
async play() {
|
|
528
|
+
const { cid, id, author } = this._raw;
|
|
529
|
+
await this._init.socket.sendReceipt(cid, cid.endsWith('@g.us') ? author : undefined, [id], 'played');
|
|
530
|
+
return true;
|
|
531
|
+
}
|
|
470
532
|
}
|
|
471
533
|
/** Mensaje de sticker. / Sticker message. */
|
|
472
534
|
export class Sticker extends Media {
|
|
@@ -598,6 +660,25 @@ export class VCard extends Message {
|
|
|
598
660
|
export class Event extends Message {
|
|
599
661
|
/** @internal Bloque del evento en el raw. / Raw event block. */
|
|
600
662
|
get _event() { return this._raw.raw.message?.eventMessage; }
|
|
663
|
+
/** Asistentes confirmados, acompañantes incluidos. / Confirmed attendees, companions included. */
|
|
664
|
+
get going() {
|
|
665
|
+
return (this._raw.responses ?? []).filter((entry) => entry.response === 'going').reduce((sum, entry) => sum + 1 + entry.guests, 0);
|
|
666
|
+
}
|
|
667
|
+
/**
|
|
668
|
+
* Respuestas de asistencia al evento, con el nombre resuelto de cada contacto y en orden
|
|
669
|
+
* de llegada — la última es la más reciente.
|
|
670
|
+
* Attendance responses, with each contact's resolved name, in arrival order — the last one
|
|
671
|
+
* is the most recent.
|
|
672
|
+
*/
|
|
673
|
+
async attendees() {
|
|
674
|
+
const rows = [];
|
|
675
|
+
for (const entry of this._raw.responses ?? []) {
|
|
676
|
+
const who = await this._init.wa.Contact.get(entry.author).catch(() => null);
|
|
677
|
+
const contact = (who?.phone ?? entry.author.split('@')[0].split(':')[0]);
|
|
678
|
+
rows.push({ name: who?.name ?? contact, contact, response: entry.response, guests: entry.guests });
|
|
679
|
+
}
|
|
680
|
+
return rows;
|
|
681
|
+
}
|
|
601
682
|
/** Nombre del evento. / Event name. */
|
|
602
683
|
get name() { return this._event?.name ?? ''; }
|
|
603
684
|
/** Inicio en ISO UTC. / Start as ISO UTC. */
|
|
@@ -3,6 +3,33 @@ import { Account, contact } from '../../lib/contact/index.js';
|
|
|
3
3
|
import Message, { message } from '../../lib/message/index.js';
|
|
4
4
|
import { Feed } from '../../lib/status/index.js';
|
|
5
5
|
import { type Engine } from '../../lib/store/index.js';
|
|
6
|
+
/**
|
|
7
|
+
* Lo que hace alguien al otro lado, en los términos en los que se mira: WhatsApp distingue
|
|
8
|
+
* «disponible» de «escribiendo» y de «grabando», y esa distinción es justo la que interesa
|
|
9
|
+
* vigilar. `paused` no se propaga: es dejar de escribir, no un estado en sí.
|
|
10
|
+
* What someone is doing on the other end, in the terms one watches it: WhatsApp tells «available»
|
|
11
|
+
* from «typing» and from «recording», and that distinction is exactly what is worth watching.
|
|
12
|
+
* `paused` is not propagated: it is stopping typing, not a state of its own.
|
|
13
|
+
*/
|
|
14
|
+
export type Presence = 'online' | 'offline' | 'typing' | 'recording';
|
|
15
|
+
/**
|
|
16
|
+
* Lo que se supervisa con `watch()`, en un solo sobre: `name` dice qué pasó y `payload` trae la
|
|
17
|
+
* instancia a la que le pasó, ya resuelta. Quien escucha no tiene que volver a buscar de quién
|
|
18
|
+
* se trata ni filtrar lo que no le toca.
|
|
19
|
+
* What `watch()` supervises, in a single envelope: `name` says what happened and `payload` brings
|
|
20
|
+
* the already-resolved instance it happened to. Whoever listens does not have to look up who it
|
|
21
|
+
* was nor filter out what does not concern them.
|
|
22
|
+
*/
|
|
23
|
+
export interface WatchEvent<N extends string, P> {
|
|
24
|
+
name: N;
|
|
25
|
+
payload: P;
|
|
26
|
+
}
|
|
27
|
+
/** Cada acción de alguien al otro lado, incluido dejar de escribir o de grabar. / Every action from the other end, including stopping typing or recording. */
|
|
28
|
+
export type ContactWatch = 'online' | 'offline' | 'typing' | 'recording' | 'stopped-typing' | 'stopped-recording';
|
|
29
|
+
/** Lo que le puede pasar a un mensaje ya enviado. / What can happen to an already-sent message. */
|
|
30
|
+
export type MessageWatch = 'read' | 'played' | 'deleted';
|
|
31
|
+
/** Un chat mezcla lo que hace la persona con lo que llega a la conversación. / A chat mixes what the person does with what arrives in the conversation. */
|
|
32
|
+
export type ChatWatch = ContactWatch | 'message';
|
|
6
33
|
type ChatInstance = InstanceType<ReturnType<typeof chat>>;
|
|
7
34
|
type ContactInstance = InstanceType<ReturnType<typeof contact>>;
|
|
8
35
|
interface Options {
|
|
@@ -63,8 +90,14 @@ export interface Farewell {
|
|
|
63
90
|
interface EventMap {
|
|
64
91
|
connected: [WhatsApp];
|
|
65
92
|
disconnected: [WhatsApp, Farewell];
|
|
93
|
+
/** Fallo que no tumba la conexión pero que quien la abrió necesita saber. / A failure that does not drop the connection but whoever opened it needs to know. */
|
|
94
|
+
error: [Error & {
|
|
95
|
+
code?: string;
|
|
96
|
+
}, WhatsApp];
|
|
66
97
|
'contact:created': [ContactInstance, ChatInstance, WhatsApp];
|
|
67
98
|
'contact:updated': [ContactInstance, ChatInstance, WhatsApp];
|
|
99
|
+
/** Alguien entró, salió, escribe o graba. Sólo llega de quien se esté vigilando con `Contact.watch()`. / Someone came in, left, is typing or recording. Only arrives for whoever is being watched with `Contact.watch()`. */
|
|
100
|
+
'contact:presence': [ContactInstance, ContactWatch, WhatsApp];
|
|
68
101
|
'chat:created': [ChatInstance, WhatsApp];
|
|
69
102
|
'chat:deleted': [ChatInstance, WhatsApp];
|
|
70
103
|
'chat:pinned': [ChatInstance, WhatsApp];
|
|
@@ -76,7 +109,7 @@ interface EventMap {
|
|
|
76
109
|
'message:created': [Message, ChatInstance, WhatsApp];
|
|
77
110
|
'message:updated': [Message, ChatInstance, WhatsApp];
|
|
78
111
|
'message:deleted': [Message, ChatInstance, WhatsApp];
|
|
79
|
-
'message:reacted': [Message, ChatInstance, string, WhatsApp];
|
|
112
|
+
'message:reacted': [Message, ChatInstance, string, ContactInstance, WhatsApp];
|
|
80
113
|
'message:starred': [Message, ChatInstance, WhatsApp];
|
|
81
114
|
'message:unstarred': [Message, ChatInstance, WhatsApp];
|
|
82
115
|
'message:forwarded': [Message, ChatInstance, WhatsApp];
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { Browsers, decryptPollVote, DisconnectReason, downloadMediaMessage, fetchLatestBaileysVersion, getContentType, initAuthCreds, jidNormalizedUser, makeWASocket, proto, updateMessageWithPollUpdate, } from 'baileys';
|
|
1
|
+
import { Browsers, decryptEventResponse, decryptPollVote, DisconnectReason, downloadMediaMessage, fetchLatestBaileysVersion, getContentType, initAuthCreds, jidNormalizedUser, makeWASocket, proto, updateMessageWithPollUpdate, } from 'baileys';
|
|
2
2
|
import { EventEmitter } from 'node:events';
|
|
3
3
|
import pino from 'pino';
|
|
4
4
|
import * as QRCode from 'qrcode';
|
|
@@ -34,7 +34,18 @@ const queued = (locks, path, work) => {
|
|
|
34
34
|
locks.set(path, next.catch(() => { }));
|
|
35
35
|
return next;
|
|
36
36
|
};
|
|
37
|
+
/** Traduce el vocabulario de baileys al que se vigila; lo que no está aquí no se propaga. / Translates baileys' vocabulary into the watched one; whatever is missing is not propagated. */
|
|
38
|
+
const PRESENCE = { available: 'online', unavailable: 'offline', composing: 'typing', recording: 'recording', paused: 'paused' };
|
|
37
39
|
const readable = (value) => (value && !/^\+?[\d\s·•∙⋅]+$/.test(value) ? value : null);
|
|
40
|
+
/**
|
|
41
|
+
* Refrescos de QR que dura un PIN antes de darlo por caducado. WhatsApp no avisa de la
|
|
42
|
+
* expiración, así que se mide por los ciclos de refresco (~20 s cada uno): tres son el margen
|
|
43
|
+
* observado en el que un código sigue siendo aceptado.
|
|
44
|
+
* QR refreshes a PIN lasts before it is considered expired. WhatsApp gives no expiry notice, so
|
|
45
|
+
* it is measured in refresh cycles (~20s each): three is the observed window in which a code is
|
|
46
|
+
* still accepted.
|
|
47
|
+
*/
|
|
48
|
+
const OTP_CYCLES = 3;
|
|
38
49
|
export default class WhatsApp {
|
|
39
50
|
#event = new EventEmitter();
|
|
40
51
|
#options;
|
|
@@ -77,9 +88,13 @@ export default class WhatsApp {
|
|
|
77
88
|
let retries = 0;
|
|
78
89
|
let intentional = false;
|
|
79
90
|
let silent = false;
|
|
91
|
+
let paired = false;
|
|
92
|
+
let cycles = 0;
|
|
80
93
|
let alive = null;
|
|
81
94
|
let timer = null;
|
|
82
95
|
let chain = Promise.resolve();
|
|
96
|
+
/** Qué estaba haciendo cada quien, para poder leer su `paused`. / What each one was doing, so their `paused` can be read. */
|
|
97
|
+
const doing = new Map();
|
|
83
98
|
const locks = new Map();
|
|
84
99
|
return new Promise((resolve, reject) => {
|
|
85
100
|
const start = async () => {
|
|
@@ -258,9 +273,33 @@ export default class WhatsApp {
|
|
|
258
273
|
socket.ev.on('creds.update', () => queued(locks, '/session/creds', () => engine.set('/session/creds', serialize(creds))));
|
|
259
274
|
socket.ev.on('connection.update', async ({ connection, lastDisconnect, qr }) => {
|
|
260
275
|
if (qr && !creds.registered) {
|
|
261
|
-
|
|
262
|
-
|
|
263
|
-
|
|
276
|
+
if (digits && (method ?? 'otp') === 'otp') {
|
|
277
|
+
// El QR se refresca cada ~20 s, pero el PIN vive más: pedir uno en
|
|
278
|
+
// cada refresco invalida el anterior y el que la persona está
|
|
279
|
+
// tecleando deja de servir a media escritura —el síntoma es un PIN
|
|
280
|
+
// correcto que «no lo toma»—. Por eso se cuenta el ciclo y sólo se
|
|
281
|
+
// renueva al caducar de verdad, gastando un reintento del presupuesto.
|
|
282
|
+
// The QR refreshes every ~20s, but the PIN lives longer: asking for
|
|
283
|
+
// one on each refresh invalidates the previous, and the one being
|
|
284
|
+
// typed stops working mid-typing —the symptom is a correct PIN that
|
|
285
|
+
// «is not accepted»—. So the cycle is counted and it is only renewed
|
|
286
|
+
// once it truly expires, spending one retry from the budget.
|
|
287
|
+
cycles = paired ? cycles + 1 : 0;
|
|
288
|
+
if (!paired || cycles >= OTP_CYCLES) {
|
|
289
|
+
if (paired && budget !== null && retries >= budget) {
|
|
290
|
+
this.emit('error', Object.assign(new Error('El código de vinculación expiró'), { code: 'ERR_OTP_EXPIRED' }), this);
|
|
291
|
+
}
|
|
292
|
+
else {
|
|
293
|
+
retries += paired ? 1 : 0;
|
|
294
|
+
cycles = 0;
|
|
295
|
+
paired = true;
|
|
296
|
+
await callback(await socket.requestPairingCode(digits));
|
|
297
|
+
}
|
|
298
|
+
}
|
|
299
|
+
}
|
|
300
|
+
else {
|
|
301
|
+
await callback(await QRCode.toBuffer(qr, { type: 'png', margin: 2 }));
|
|
302
|
+
}
|
|
264
303
|
}
|
|
265
304
|
if (connection === 'open') {
|
|
266
305
|
connected = true;
|
|
@@ -371,6 +410,31 @@ export default class WhatsApp {
|
|
|
371
410
|
}
|
|
372
411
|
}).catch(() => { });
|
|
373
412
|
});
|
|
413
|
+
socket.ev.on('presence.update', ({ id, presences }) => {
|
|
414
|
+
chain = chain.then(async () => {
|
|
415
|
+
const cid = await canonical(id);
|
|
416
|
+
for (const [participant, data] of Object.entries(presences)) {
|
|
417
|
+
const state = PRESENCE[data.lastKnownPresence];
|
|
418
|
+
if (state) {
|
|
419
|
+
const who = await canonical(participant || cid);
|
|
420
|
+
// `paused` es «dejó de hacer lo que hacía», y sólo el estado
|
|
421
|
+
// anterior dice qué era: sin recordarlo no se puede distinguir
|
|
422
|
+
// dejar de escribir de dejar de grabar.
|
|
423
|
+
// `paused` means «stopped doing what they were doing», and only
|
|
424
|
+
// the previous state says which: without remembering it there is
|
|
425
|
+
// no telling stopped-typing from stopped-recording.
|
|
426
|
+
const last = doing.get(who);
|
|
427
|
+
const name = state === 'paused' ? (last === 'recording' ? 'stopped-recording' : 'stopped-typing') : state;
|
|
428
|
+
if (state === 'typing' || state === 'recording')
|
|
429
|
+
doing.set(who, state);
|
|
430
|
+
else
|
|
431
|
+
doing.delete(who);
|
|
432
|
+
const card = deserialize(await engine.get(`/contact/${who}`));
|
|
433
|
+
this.emit('contact:presence', new this.Contact(card ?? { id: who, lid: null, name: null, notify: null, verified_name: null, img_url: null, status: null }), name, this);
|
|
434
|
+
}
|
|
435
|
+
}
|
|
436
|
+
}).catch(() => { });
|
|
437
|
+
});
|
|
374
438
|
socket.ev.on('lid-mapping.update', ({ lid, pn }) => {
|
|
375
439
|
chain = chain.then(async () => {
|
|
376
440
|
await remember(lid, pn);
|
|
@@ -476,7 +540,15 @@ export default class WhatsApp {
|
|
|
476
540
|
];
|
|
477
541
|
await engine.set(found.path, serialize(found.doc), found.doc.created_at);
|
|
478
542
|
const instance = new Message(init, found.doc);
|
|
479
|
-
|
|
543
|
+
// Quién reaccionó viaja en el evento: sin él, distinguir la
|
|
544
|
+
// reacción del usuario del eco de una propia obligaba a cada
|
|
545
|
+
// consumidor a llevar su propio registro de ecos con timeouts.
|
|
546
|
+
// Who reacted travels in the event: without it, telling the
|
|
547
|
+
// user's reaction from the echo of an own one forced every
|
|
548
|
+
// consumer to keep its own echo ledger with timeouts.
|
|
549
|
+
const who = await canonical(author);
|
|
550
|
+
const card = deserialize(await engine.get(`/contact/${who}`));
|
|
551
|
+
this.emit('message:reacted', instance, await instance.chat(), emoji, new this.Contact(card ?? { id: who, lid: null, name: null, notify: null, verified_name: null, img_url: null, status: null }), this);
|
|
480
552
|
}
|
|
481
553
|
continue;
|
|
482
554
|
}
|
|
@@ -493,7 +565,18 @@ export default class WhatsApp {
|
|
|
493
565
|
continue;
|
|
494
566
|
}
|
|
495
567
|
const type = { conversation: 'text', extendedTextMessage: 'text', imageMessage: 'image', videoMessage: 'video', audioMessage: 'audio' }[kind ?? ''];
|
|
496
|
-
|
|
568
|
+
// El autor de un estado puede llegar por `participant` o por su
|
|
569
|
+
// alterno según venga identificado por teléfono o por LID, y se
|
|
570
|
+
// guarda canónico como todo lo demás: con el LID crudo el estado
|
|
571
|
+
// queda a nombre de un número larguísimo que no case con ningún
|
|
572
|
+
// contacto, y para quien mira es un estado que no llegó.
|
|
573
|
+
// A status author can arrive via `participant` or its alternate
|
|
574
|
+
// depending on whether it is identified by phone or by LID, and
|
|
575
|
+
// is stored canonically like everything else: with the raw LID
|
|
576
|
+
// the status ends up under a long meaningless number matching no
|
|
577
|
+
// contact, and to whoever looks it is a status that never came.
|
|
578
|
+
const claimed = msg.key.participant ?? msg.key.participantAlt ?? '';
|
|
579
|
+
const author = claimed ? await canonical(claimed) : '';
|
|
497
580
|
if (!type || !author) {
|
|
498
581
|
continue;
|
|
499
582
|
}
|
|
@@ -521,6 +604,45 @@ export default class WhatsApp {
|
|
|
521
604
|
this.emit('feed:created', new Feed(init, doc), this);
|
|
522
605
|
continue;
|
|
523
606
|
}
|
|
607
|
+
if (kind === 'encEventResponseMessage') {
|
|
608
|
+
const enc = msg.message?.encEventResponseMessage;
|
|
609
|
+
const key = enc?.eventCreationMessageKey;
|
|
610
|
+
const found = key?.id && key.remoteJid ? await locate(key.remoteJid, key.id) : null;
|
|
611
|
+
const raw_secret = found?.doc.raw.message?.messageContextInfo?.messageSecret;
|
|
612
|
+
const secret = typeof raw_secret === 'string' ? Buffer.from(raw_secret, 'base64') : raw_secret;
|
|
613
|
+
if (found && secret && enc?.encPayload && enc.encIv) {
|
|
614
|
+
const mine = [socket.user?.lid, socket.user?.id];
|
|
615
|
+
const theirs = (from) => [from.remoteJid, from.participant, from.remoteJidAlt];
|
|
616
|
+
const responders = (msg.key.fromMe ? mine : theirs(msg.key)).filter((id) => Boolean(id));
|
|
617
|
+
const creators = (found.doc.raw.key?.fromMe ? mine : theirs(found.doc.raw.key ?? {})).filter((id) => Boolean(id));
|
|
618
|
+
for (const pair of responders.flatMap((who) => creators.map((creator) => [who, creator]))) {
|
|
619
|
+
try {
|
|
620
|
+
const parsed = decryptEventResponse({ encPayload: enc.encPayload, encIv: enc.encIv }, {
|
|
621
|
+
eventCreatorJid: jidNormalizedUser(pair[1]),
|
|
622
|
+
eventMsgId: found.doc.id,
|
|
623
|
+
eventEncKey: secret,
|
|
624
|
+
responderJid: jidNormalizedUser(pair[0]),
|
|
625
|
+
});
|
|
626
|
+
const response = { 1: 'going', 2: 'not_going', 3: 'maybe' }[parsed.response];
|
|
627
|
+
if (response) {
|
|
628
|
+
const author = await canonical(jidNormalizedUser((msg.key.fromMe ? socket.user?.id : msg.key.participant ?? cid) ?? cid));
|
|
629
|
+
found.doc.responses = [
|
|
630
|
+
...(found.doc.responses ?? []).filter((entry) => entry.author !== author),
|
|
631
|
+
{ author, response, guests: Number(parsed.extraGuestCount ?? 0), at: (Number(msg.messageTimestamp) || Math.floor(Date.now() / 1_000)) * 1_000 },
|
|
632
|
+
];
|
|
633
|
+
await engine.set(found.path, serialize(found.doc), found.doc.created_at);
|
|
634
|
+
const instance = new Message(init, found.doc);
|
|
635
|
+
this.emit('message:updated', instance, await instance.chat(), this);
|
|
636
|
+
}
|
|
637
|
+
break;
|
|
638
|
+
}
|
|
639
|
+
catch {
|
|
640
|
+
/* identidad equivocada / wrong identity */
|
|
641
|
+
}
|
|
642
|
+
}
|
|
643
|
+
}
|
|
644
|
+
continue;
|
|
645
|
+
}
|
|
524
646
|
if (kind === 'pollUpdateMessage') {
|
|
525
647
|
const key = msg.message?.pollUpdateMessage?.pollCreationMessageKey;
|
|
526
648
|
const vote = msg.message?.pollUpdateMessage?.vote;
|