@arcaelas/whatsapp 6.1.0 → 6.1.2

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.
@@ -52,6 +52,8 @@ const internal_1 = require("../../lib/internal");
52
52
  const message_1 = require("../../lib/message");
53
53
  const status_1 = require("../../lib/status");
54
54
  const store_1 = require("../../lib/store");
55
+ /** Rechazo del servidor: estado terminal, el único que puede retroceder el avance. / Server rejection: terminal state, the only one allowed to move the state backwards. */
56
+ const ERROR = 0;
55
57
  /** Estados legibles del mensaje que el receipt puede avanzar. / Readable message states a receipt can advance to. */
56
58
  const READ = 4;
57
59
  const PLAYED = 5;
@@ -573,15 +575,30 @@ class WhatsApp {
573
575
  * @internal
574
576
  */
575
577
  async #persist_contact(raw) {
576
- const existing = await this.engine.get(`/contact/${raw.id}`);
577
- await this.engine.set(`/contact/${raw.id}`, (0, store_1.serialize)(raw));
578
- if (raw.lid) {
579
- await this.engine.set(`/lid/${raw.lid}`, (0, store_1.serialize)(raw.id));
578
+ const current = (0, store_1.deserialize)(await this.engine.get(`/contact/${raw.id}`));
579
+ // Los upserts del re-sync llegan con los campos vacíos: sin fusionar borran el nombre
580
+ // que ya se conocía y el chat pasa a mostrar el número pelado.
581
+ // Re-sync upserts arrive with empty fields: without merging they wipe the name already
582
+ // known and the chat falls back to showing the bare number.
583
+ const doc = current
584
+ ? {
585
+ id: raw.id,
586
+ lid: raw.lid ?? current.lid,
587
+ name: raw.name ?? current.name,
588
+ notify: raw.notify ?? current.notify,
589
+ verified_name: raw.verified_name ?? current.verified_name,
590
+ img_url: raw.img_url ?? current.img_url,
591
+ status: raw.status ?? current.status,
592
+ }
593
+ : raw;
594
+ await this.engine.set(`/contact/${raw.id}`, (0, store_1.serialize)(doc));
595
+ if (doc.lid) {
596
+ await this.engine.set(`/lid/${doc.lid}`, (0, store_1.serialize)(doc.id));
580
597
  }
581
- if (existing === null) {
582
- const person = new this.Contact(raw);
583
- const cached_chat = (0, store_1.deserialize)(await this.engine.get(`/chat/${raw.id}`));
584
- this.emit('contact:created', person, new this.Chat(cached_chat ?? { id: raw.id, name: person.name }), this);
598
+ if (!current) {
599
+ const person = new this.Contact(doc);
600
+ const cached_chat = (0, store_1.deserialize)(await this.engine.get(`/chat/${doc.id}`));
601
+ this.emit('contact:created', person, new this.Chat(cached_chat ?? { id: doc.id, name: person.name }), this);
585
602
  }
586
603
  }
587
604
  /** @internal */
@@ -609,6 +626,7 @@ class WhatsApp {
609
626
  const patch = {
610
627
  ...(c.notify && { notify: c.notify }),
611
628
  ...(c.name && { name: c.name }),
629
+ ...(c.verifiedName && { verified_name: c.verifiedName }),
612
630
  ...(c.imgUrl && { img_url: c.imgUrl }),
613
631
  ...(c.status && { status: c.status }),
614
632
  ...(c.lid && { lid: c.lid }),
@@ -947,6 +965,13 @@ class WhatsApp {
947
965
  if (existing_doc.reactions) {
948
966
  doc.reactions = existing_doc.reactions;
949
967
  }
968
+ // El historial reporta el estado que tenía al sincronizarse: si acá se reescribe
969
+ // por otro cambio (edición, destacado), el estado ya conocido se conserva.
970
+ // History reports the state it had when synced: if the doc gets rewritten here
971
+ // for another change (edit, star), the state already known is kept.
972
+ if (existing_doc.status > doc.status) {
973
+ doc.status = existing_doc.status;
974
+ }
950
975
  if (existing_doc.status >= doc.status &&
951
976
  existing_doc.caption === doc.caption &&
952
977
  existing_doc.edited === doc.edited &&
@@ -958,16 +983,25 @@ class WhatsApp {
958
983
  if (!existing_doc && !doc.me) {
959
984
  const push_name = msg.pushName ?? null;
960
985
  const is_group = cid.endsWith('@g.us');
961
- if (doc.author && !(await this.engine.get(`/contact/${doc.author}`))) {
962
- await this.#persist_contact({
963
- id: doc.author,
964
- lid: msg.key.remoteJid?.endsWith('@lid') ? msg.key.remoteJid : null,
965
- name: null,
966
- notify: push_name,
967
- verified_name: msg.verifiedBizName ?? null,
968
- img_url: null,
969
- status: null,
970
- });
986
+ // El contacto se completa cuando no existe y también cuando existe sin ningún
987
+ // nombre: el mensaje trae el pushName y el nombre del negocio verificado, que
988
+ // es lo único que queda si un re-sync anterior dejó la ficha en blanco.
989
+ // The contact is filled in when missing and also when it exists with no name at
990
+ // all: the message carries the pushName and the verified business name, the only
991
+ // thing left when an earlier re-sync blanked the card.
992
+ if (doc.author) {
993
+ const known = (0, store_1.deserialize)(await this.engine.get(`/contact/${doc.author}`));
994
+ if (!known || !(known.name ?? known.notify ?? known.verified_name)) {
995
+ await this.#persist_contact({
996
+ id: doc.author,
997
+ lid: msg.key.remoteJid?.endsWith('@lid') ? msg.key.remoteJid : null,
998
+ name: null,
999
+ notify: push_name,
1000
+ verified_name: msg.verifiedBizName ?? null,
1001
+ img_url: null,
1002
+ status: null,
1003
+ });
1004
+ }
971
1005
  }
972
1006
  if (!(await this.engine.get(`/chat/${cid}`))) {
973
1007
  const chat_raw = {
@@ -1103,8 +1137,14 @@ class WhatsApp {
1103
1137
  await this.engine.set(path, (0, store_1.serialize)(doc), doc.created_at);
1104
1138
  const msg_instance = (0, message_1.message)(this, doc);
1105
1139
  this.emit(doc.starred ? 'message:starred' : 'message:unstarred', msg_instance, await msg_instance.chat(), this);
1140
+ // WhatsApp reemite los acks desordenados al reconectar (un `sent` después
1141
+ // de un `delivered`), así que el estado solo avanza; el rechazo (`error`)
1142
+ // es terminal y sí puede pisar lo que hubiera.
1143
+ // WhatsApp re-emits acks out of order on reconnect (a `sent` after a
1144
+ // `delivered`), so the state only moves forward; a rejection (`error`) is
1145
+ // terminal and may override whatever was there.
1106
1146
  }
1107
- else if (status !== undefined) {
1147
+ else if (status !== undefined && (status > doc.status || status === ERROR)) {
1108
1148
  raw.status = status;
1109
1149
  doc.status = status;
1110
1150
  // El rechazo del servidor viaja como stub del update; sin persistirlo el
@@ -13,6 +13,8 @@ import { bind } from '../../lib/internal.js';
13
13
  import { Audio, Document, Event, Image, Location, message, Message, Poll, Sticker, Text, VCard, Video, } from '../../lib/message/index.js';
14
14
  import { Feed, TTL_MS as FEED_TTL_MS } from '../../lib/status/index.js';
15
15
  import { deserialize, serialize } from '../../lib/store/index.js';
16
+ /** Rechazo del servidor: estado terminal, el único que puede retroceder el avance. / Server rejection: terminal state, the only one allowed to move the state backwards. */
17
+ const ERROR = 0;
16
18
  /** Estados legibles del mensaje que el receipt puede avanzar. / Readable message states a receipt can advance to. */
17
19
  const READ = 4;
18
20
  const PLAYED = 5;
@@ -534,15 +536,30 @@ export class WhatsApp {
534
536
  * @internal
535
537
  */
536
538
  async #persist_contact(raw) {
537
- const existing = await this.engine.get(`/contact/${raw.id}`);
538
- await this.engine.set(`/contact/${raw.id}`, serialize(raw));
539
- if (raw.lid) {
540
- await this.engine.set(`/lid/${raw.lid}`, serialize(raw.id));
539
+ const current = deserialize(await this.engine.get(`/contact/${raw.id}`));
540
+ // Los upserts del re-sync llegan con los campos vacíos: sin fusionar borran el nombre
541
+ // que ya se conocía y el chat pasa a mostrar el número pelado.
542
+ // Re-sync upserts arrive with empty fields: without merging they wipe the name already
543
+ // known and the chat falls back to showing the bare number.
544
+ const doc = current
545
+ ? {
546
+ id: raw.id,
547
+ lid: raw.lid ?? current.lid,
548
+ name: raw.name ?? current.name,
549
+ notify: raw.notify ?? current.notify,
550
+ verified_name: raw.verified_name ?? current.verified_name,
551
+ img_url: raw.img_url ?? current.img_url,
552
+ status: raw.status ?? current.status,
553
+ }
554
+ : raw;
555
+ await this.engine.set(`/contact/${raw.id}`, serialize(doc));
556
+ if (doc.lid) {
557
+ await this.engine.set(`/lid/${doc.lid}`, serialize(doc.id));
541
558
  }
542
- if (existing === null) {
543
- const person = new this.Contact(raw);
544
- const cached_chat = deserialize(await this.engine.get(`/chat/${raw.id}`));
545
- this.emit('contact:created', person, new this.Chat(cached_chat ?? { id: raw.id, name: person.name }), this);
559
+ if (!current) {
560
+ const person = new this.Contact(doc);
561
+ const cached_chat = deserialize(await this.engine.get(`/chat/${doc.id}`));
562
+ this.emit('contact:created', person, new this.Chat(cached_chat ?? { id: doc.id, name: person.name }), this);
546
563
  }
547
564
  }
548
565
  /** @internal */
@@ -570,6 +587,7 @@ export class WhatsApp {
570
587
  const patch = {
571
588
  ...(c.notify && { notify: c.notify }),
572
589
  ...(c.name && { name: c.name }),
590
+ ...(c.verifiedName && { verified_name: c.verifiedName }),
573
591
  ...(c.imgUrl && { img_url: c.imgUrl }),
574
592
  ...(c.status && { status: c.status }),
575
593
  ...(c.lid && { lid: c.lid }),
@@ -908,6 +926,13 @@ export class WhatsApp {
908
926
  if (existing_doc.reactions) {
909
927
  doc.reactions = existing_doc.reactions;
910
928
  }
929
+ // El historial reporta el estado que tenía al sincronizarse: si acá se reescribe
930
+ // por otro cambio (edición, destacado), el estado ya conocido se conserva.
931
+ // History reports the state it had when synced: if the doc gets rewritten here
932
+ // for another change (edit, star), the state already known is kept.
933
+ if (existing_doc.status > doc.status) {
934
+ doc.status = existing_doc.status;
935
+ }
911
936
  if (existing_doc.status >= doc.status &&
912
937
  existing_doc.caption === doc.caption &&
913
938
  existing_doc.edited === doc.edited &&
@@ -919,16 +944,25 @@ export class WhatsApp {
919
944
  if (!existing_doc && !doc.me) {
920
945
  const push_name = msg.pushName ?? null;
921
946
  const is_group = cid.endsWith('@g.us');
922
- if (doc.author && !(await this.engine.get(`/contact/${doc.author}`))) {
923
- await this.#persist_contact({
924
- id: doc.author,
925
- lid: msg.key.remoteJid?.endsWith('@lid') ? msg.key.remoteJid : null,
926
- name: null,
927
- notify: push_name,
928
- verified_name: msg.verifiedBizName ?? null,
929
- img_url: null,
930
- status: null,
931
- });
947
+ // El contacto se completa cuando no existe y también cuando existe sin ningún
948
+ // nombre: el mensaje trae el pushName y el nombre del negocio verificado, que
949
+ // es lo único que queda si un re-sync anterior dejó la ficha en blanco.
950
+ // The contact is filled in when missing and also when it exists with no name at
951
+ // all: the message carries the pushName and the verified business name, the only
952
+ // thing left when an earlier re-sync blanked the card.
953
+ if (doc.author) {
954
+ const known = deserialize(await this.engine.get(`/contact/${doc.author}`));
955
+ if (!known || !(known.name ?? known.notify ?? known.verified_name)) {
956
+ await this.#persist_contact({
957
+ id: doc.author,
958
+ lid: msg.key.remoteJid?.endsWith('@lid') ? msg.key.remoteJid : null,
959
+ name: null,
960
+ notify: push_name,
961
+ verified_name: msg.verifiedBizName ?? null,
962
+ img_url: null,
963
+ status: null,
964
+ });
965
+ }
932
966
  }
933
967
  if (!(await this.engine.get(`/chat/${cid}`))) {
934
968
  const chat_raw = {
@@ -1064,8 +1098,14 @@ export class WhatsApp {
1064
1098
  await this.engine.set(path, serialize(doc), doc.created_at);
1065
1099
  const msg_instance = message(this, doc);
1066
1100
  this.emit(doc.starred ? 'message:starred' : 'message:unstarred', msg_instance, await msg_instance.chat(), this);
1101
+ // WhatsApp reemite los acks desordenados al reconectar (un `sent` después
1102
+ // de un `delivered`), así que el estado solo avanza; el rechazo (`error`)
1103
+ // es terminal y sí puede pisar lo que hubiera.
1104
+ // WhatsApp re-emits acks out of order on reconnect (a `sent` after a
1105
+ // `delivered`), so the state only moves forward; a rejection (`error`) is
1106
+ // terminal and may override whatever was there.
1067
1107
  }
1068
- else if (status !== undefined) {
1108
+ else if (status !== undefined && (status > doc.status || status === ERROR)) {
1069
1109
  raw.status = status;
1070
1110
  doc.status = status;
1071
1111
  // El rechazo del servidor viaja como stub del update; sin persistirlo el
package/package.json CHANGED
@@ -72,7 +72,7 @@
72
72
  "release": "npm publish --access public"
73
73
  },
74
74
  "types": "./build/esm/index.d.ts",
75
- "version": "6.1.0",
75
+ "version": "6.1.2",
76
76
  "engines": {
77
77
  "node": ">=20"
78
78
  },