@arcaelas/whatsapp 6.1.3 → 6.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.
@@ -17,6 +17,8 @@ export declare class Chat {
17
17
  pinned?: number | null;
18
18
  mute_end_time?: number | null;
19
19
  unread_count?: number | null;
20
+ /** Epoch ms del último mensaje: ordena la lista y sobrevive a las escrituras de flags. / Last message epoch ms: orders the list and survives flag writes. */
21
+ activity?: number | null;
20
22
  };
21
23
  /**
22
24
  * @internal Documento crudo del chat. `id` es el identificador del engine (JID, LID o
@@ -31,6 +33,8 @@ export declare class Chat {
31
33
  pinned?: number | null;
32
34
  mute_end_time?: number | null;
33
35
  unread_count?: number | null;
36
+ /** Epoch ms del último mensaje: ordena la lista y sobrevive a las escrituras de flags. / Last message epoch ms: orders the list and survives flag writes. */
37
+ activity?: number | null;
34
38
  });
35
39
  /** Teléfono del contacto, o el identificador crudo en grupos y LIDs. / Contact phone, or the raw identifier for groups and LIDs. */
36
40
  get id(): string;
@@ -61,6 +65,8 @@ export declare function chat(wa: WhatsApp): {
61
65
  pinned?: number | null;
62
66
  mute_end_time?: number | null;
63
67
  unread_count?: number | null;
68
+ /** Epoch ms del último mensaje: ordena la lista y sobrevive a las escrituras de flags. / Last message epoch ms: orders the list and survives flag writes. */
69
+ activity?: number | null;
64
70
  }): {
65
71
  /**
66
72
  * Participantes del chat: los integrantes en grupos, el contacto y yo en 1:1.
@@ -161,6 +167,8 @@ export declare function chat(wa: WhatsApp): {
161
167
  pinned?: number | null;
162
168
  mute_end_time?: number | null;
163
169
  unread_count?: number | null;
170
+ /** Epoch ms del último mensaje: ordena la lista y sobrevive a las escrituras de flags. / Last message epoch ms: orders the list and survives flag writes. */
171
+ activity?: number | null;
164
172
  };
165
173
  /** Teléfono del contacto, o el identificador crudo en grupos y LIDs. / Contact phone, or the raw identifier for groups and LIDs. */
166
174
  get id(): string;
@@ -286,6 +294,8 @@ export declare function chat(wa: WhatsApp): {
286
294
  pinned?: number | null;
287
295
  mute_end_time?: number | null;
288
296
  unread_count?: number | null;
297
+ /** Epoch ms del último mensaje: ordena la lista y sobrevive a las escrituras de flags. / Last message epoch ms: orders the list and survives flag writes. */
298
+ activity?: number | null;
289
299
  };
290
300
  /** Teléfono del contacto, o el identificador crudo en grupos y LIDs. / Contact phone, or the raw identifier for groups and LIDs. */
291
301
  get id(): string;
@@ -410,6 +420,8 @@ export declare function chat(wa: WhatsApp): {
410
420
  pinned?: number | null;
411
421
  mute_end_time?: number | null;
412
422
  unread_count?: number | null;
423
+ /** Epoch ms del último mensaje: ordena la lista y sobrevive a las escrituras de flags. / Last message epoch ms: orders the list and survives flag writes. */
424
+ activity?: number | null;
413
425
  };
414
426
  /** Teléfono del contacto, o el identificador crudo en grupos y LIDs. / Contact phone, or the raw identifier for groups and LIDs. */
415
427
  get id(): string;
@@ -68,6 +68,16 @@ function chat(wa) {
68
68
  * Último mensaje del chat en el formato que exige `chatModify`.
69
69
  * The chat's last message in the shape `chatModify` requires.
70
70
  */
71
+ /**
72
+ * Actividad del chat según su último mensaje: mantiene la posición en la lista cuando se
73
+ * reescribe el documento por fijar, archivar, silenciar o marcar leído.
74
+ * Chat activity from its last message: keeps its position in the list when the document is
75
+ * rewritten by pinning, archiving, muting or marking as read.
76
+ */
77
+ async function activity_of(cid) {
78
+ const [raw] = await wa.engine.list(`/chat/${cid}/message`, 0, 1);
79
+ return (0, store_1.deserialize)(raw ?? null)?.created_at ?? 0;
80
+ }
71
81
  async function last_messages(cid) {
72
82
  const [raw] = await wa.engine.list(`/chat/${cid}/message`, 0, 1);
73
83
  const parsed = (0, store_1.deserialize)(raw ?? null);
@@ -201,7 +211,7 @@ function chat(wa) {
201
211
  if (socket) {
202
212
  await socket.chatModify({ archive: value, lastMessages: await last_messages(this._raw.id) }, this._raw.id);
203
213
  this._raw.archived = value;
204
- await wa.engine.set(`/chat/${this._raw.id}`, (0, store_1.serialize)(this._raw));
214
+ await wa.engine.set(`/chat/${this._raw.id}`, (0, store_1.serialize)(this._raw), this._raw.activity ?? (await activity_of(this._raw.id)));
205
215
  ok = true;
206
216
  }
207
217
  return ok;
@@ -223,7 +233,7 @@ function chat(wa) {
223
233
  if (allowed) {
224
234
  await socket.chatModify({ pin: value }, this._raw.id);
225
235
  this._raw.pinned = value ? Date.now() : null;
226
- await wa.engine.set(`/chat/${this._raw.id}`, (0, store_1.serialize)(this._raw));
236
+ await wa.engine.set(`/chat/${this._raw.id}`, (0, store_1.serialize)(this._raw), this._raw.activity ?? (await activity_of(this._raw.id)));
227
237
  ok = true;
228
238
  }
229
239
  }
@@ -244,7 +254,7 @@ function chat(wa) {
244
254
  const muted = end > Date.now();
245
255
  await socket.chatModify({ mute: muted ? end : null }, this._raw.id);
246
256
  this._raw.mute_end_time = muted ? end : null;
247
- await wa.engine.set(`/chat/${this._raw.id}`, (0, store_1.serialize)(this._raw));
257
+ await wa.engine.set(`/chat/${this._raw.id}`, (0, store_1.serialize)(this._raw), this._raw.activity ?? (await activity_of(this._raw.id)));
248
258
  ok = true;
249
259
  }
250
260
  return ok;
@@ -261,7 +271,7 @@ function chat(wa) {
261
271
  if (socket) {
262
272
  await socket.chatModify({ markRead: true, lastMessages: await last_messages(this._raw.id) }, this._raw.id);
263
273
  this._raw.unread_count = 0;
264
- await wa.engine.set(`/chat/${this._raw.id}`, (0, store_1.serialize)(this._raw));
274
+ await wa.engine.set(`/chat/${this._raw.id}`, (0, store_1.serialize)(this._raw), this._raw.activity ?? (await activity_of(this._raw.id)));
265
275
  ok = true;
266
276
  }
267
277
  return ok;
@@ -656,6 +656,20 @@ class WhatsApp {
656
656
  await this.engine.set(`/lid/${lid}`, (0, store_1.serialize)(pn));
657
657
  await this.engine.set(`/lid/${pn}`, (0, store_1.serialize)(lid));
658
658
  }
659
+ /**
660
+ * Actividad del chat según su último mensaje persistido. Es el respaldo para los
661
+ * documentos que se guardaron antes de que el chat llevara su propia marca.
662
+ * Chat activity from its last persisted message. It is the fallback for documents stored
663
+ * before the chat carried its own stamp.
664
+ *
665
+ * @param cid - Identificador del chat / Chat identifier
666
+ * @returns Epoch ms del último mensaje, o 0 si el chat no tiene ninguno / Last message epoch ms, or 0 when the chat has none
667
+ * @internal
668
+ */
669
+ async #activity(cid) {
670
+ const [raw] = await this.engine.list(`/chat/${cid}/message`, 0, 1);
671
+ return (0, store_1.deserialize)(raw ?? null)?.created_at ?? 0;
672
+ }
659
673
  /** @internal */
660
674
  async #handle_chats_upsert(chats) {
661
675
  for (const ch of chats) {
@@ -672,7 +686,13 @@ class WhatsApp {
672
686
  if (ch.name) {
673
687
  raw.name = ch.name;
674
688
  }
675
- await this.engine.set(`/chat/${ch.id}`, (0, store_1.serialize)(raw));
689
+ // El sync trae la última actividad del chat; sin ella la lista quedaría ordenada
690
+ // por el momento en que se escribió cada documento.
691
+ // The sync carries the chat's last activity; without it the list would be ordered
692
+ // by the moment each document happened to be written.
693
+ const stamp = ch.conversationTimestamp != null ? Number(ch.conversationTimestamp) * 1_000 : null;
694
+ raw.activity = Math.max(stamp ?? 0, raw.activity ?? 0, await this.#activity(ch.id)) || null;
695
+ await this.engine.set(`/chat/${ch.id}`, (0, store_1.serialize)(raw), raw.activity ?? 0);
676
696
  if (current === null) {
677
697
  this.emit('chat:created', new this.Chat(raw), this);
678
698
  }
@@ -708,7 +728,9 @@ class WhatsApp {
708
728
  }
709
729
  if (Object.keys(patch).length > 0) {
710
730
  const merged = { ...current, ...patch };
711
- await this.engine.set(`/chat/${ch.id}`, (0, store_1.serialize)(merged));
731
+ // Fijar, archivar o silenciar no es actividad: el chat conserva su posición.
732
+ // Pinning, archiving or muting is not activity: the chat keeps its position.
733
+ await this.engine.set(`/chat/${ch.id}`, (0, store_1.serialize)(merged), merged.activity ?? undefined);
712
734
  if (pinned_changed) {
713
735
  this.emit(ch.pinned != null ? 'chat:pinned' : 'chat:unpinned', new this.Chat(merged), this);
714
736
  }
@@ -1013,12 +1035,22 @@ class WhatsApp {
1013
1035
  const chat_raw = {
1014
1036
  id: cid,
1015
1037
  name: is_group ? null : push_name,
1038
+ activity: doc.created_at,
1016
1039
  };
1017
- await this.engine.set(`/chat/${cid}`, (0, store_1.serialize)(chat_raw));
1040
+ await this.engine.set(`/chat/${cid}`, (0, store_1.serialize)(chat_raw), doc.created_at);
1018
1041
  this.emit('chat:created', new this.Chat(chat_raw), this);
1019
1042
  }
1020
1043
  }
1021
1044
  await this.engine.set(`/chat/${cid}/message/${mid}`, (0, store_1.serialize)(doc), doc.created_at);
1045
+ // El mensaje más nuevo define la posición del chat en la lista; un mensaje viejo
1046
+ // que llega en un re-sync no la altera.
1047
+ // The newest message defines the chat's position in the list; an old message
1048
+ // arriving in a re-sync does not move it.
1049
+ const chat_doc = (0, store_1.deserialize)(await this.engine.get(`/chat/${cid}`));
1050
+ if (chat_doc && doc.created_at > (chat_doc.activity ?? 0)) {
1051
+ chat_doc.activity = doc.created_at;
1052
+ await this.engine.set(`/chat/${cid}`, (0, store_1.serialize)(chat_doc), doc.created_at);
1053
+ }
1022
1054
  // El binario solo se materializa en la primera entrega; en re-syncs ya vive en el engine.
1023
1055
  // The binary is only materialized on first delivery; on re-syncs it already lives in the engine.
1024
1056
  let content_buf = Buffer.alloc(0);
@@ -17,6 +17,8 @@ export declare class Chat {
17
17
  pinned?: number | null;
18
18
  mute_end_time?: number | null;
19
19
  unread_count?: number | null;
20
+ /** Epoch ms del último mensaje: ordena la lista y sobrevive a las escrituras de flags. / Last message epoch ms: orders the list and survives flag writes. */
21
+ activity?: number | null;
20
22
  };
21
23
  /**
22
24
  * @internal Documento crudo del chat. `id` es el identificador del engine (JID, LID o
@@ -31,6 +33,8 @@ export declare class Chat {
31
33
  pinned?: number | null;
32
34
  mute_end_time?: number | null;
33
35
  unread_count?: number | null;
36
+ /** Epoch ms del último mensaje: ordena la lista y sobrevive a las escrituras de flags. / Last message epoch ms: orders the list and survives flag writes. */
37
+ activity?: number | null;
34
38
  });
35
39
  /** Teléfono del contacto, o el identificador crudo en grupos y LIDs. / Contact phone, or the raw identifier for groups and LIDs. */
36
40
  get id(): string;
@@ -61,6 +65,8 @@ export declare function chat(wa: WhatsApp): {
61
65
  pinned?: number | null;
62
66
  mute_end_time?: number | null;
63
67
  unread_count?: number | null;
68
+ /** Epoch ms del último mensaje: ordena la lista y sobrevive a las escrituras de flags. / Last message epoch ms: orders the list and survives flag writes. */
69
+ activity?: number | null;
64
70
  }): {
65
71
  /**
66
72
  * Participantes del chat: los integrantes en grupos, el contacto y yo en 1:1.
@@ -161,6 +167,8 @@ export declare function chat(wa: WhatsApp): {
161
167
  pinned?: number | null;
162
168
  mute_end_time?: number | null;
163
169
  unread_count?: number | null;
170
+ /** Epoch ms del último mensaje: ordena la lista y sobrevive a las escrituras de flags. / Last message epoch ms: orders the list and survives flag writes. */
171
+ activity?: number | null;
164
172
  };
165
173
  /** Teléfono del contacto, o el identificador crudo en grupos y LIDs. / Contact phone, or the raw identifier for groups and LIDs. */
166
174
  get id(): string;
@@ -286,6 +294,8 @@ export declare function chat(wa: WhatsApp): {
286
294
  pinned?: number | null;
287
295
  mute_end_time?: number | null;
288
296
  unread_count?: number | null;
297
+ /** Epoch ms del último mensaje: ordena la lista y sobrevive a las escrituras de flags. / Last message epoch ms: orders the list and survives flag writes. */
298
+ activity?: number | null;
289
299
  };
290
300
  /** Teléfono del contacto, o el identificador crudo en grupos y LIDs. / Contact phone, or the raw identifier for groups and LIDs. */
291
301
  get id(): string;
@@ -410,6 +420,8 @@ export declare function chat(wa: WhatsApp): {
410
420
  pinned?: number | null;
411
421
  mute_end_time?: number | null;
412
422
  unread_count?: number | null;
423
+ /** Epoch ms del último mensaje: ordena la lista y sobrevive a las escrituras de flags. / Last message epoch ms: orders the list and survives flag writes. */
424
+ activity?: number | null;
413
425
  };
414
426
  /** Teléfono del contacto, o el identificador crudo en grupos y LIDs. / Contact phone, or the raw identifier for groups and LIDs. */
415
427
  get id(): string;
@@ -63,6 +63,16 @@ export function chat(wa) {
63
63
  * Último mensaje del chat en el formato que exige `chatModify`.
64
64
  * The chat's last message in the shape `chatModify` requires.
65
65
  */
66
+ /**
67
+ * Actividad del chat según su último mensaje: mantiene la posición en la lista cuando se
68
+ * reescribe el documento por fijar, archivar, silenciar o marcar leído.
69
+ * Chat activity from its last message: keeps its position in the list when the document is
70
+ * rewritten by pinning, archiving, muting or marking as read.
71
+ */
72
+ async function activity_of(cid) {
73
+ const [raw] = await wa.engine.list(`/chat/${cid}/message`, 0, 1);
74
+ return deserialize(raw ?? null)?.created_at ?? 0;
75
+ }
66
76
  async function last_messages(cid) {
67
77
  const [raw] = await wa.engine.list(`/chat/${cid}/message`, 0, 1);
68
78
  const parsed = deserialize(raw ?? null);
@@ -196,7 +206,7 @@ export function chat(wa) {
196
206
  if (socket) {
197
207
  await socket.chatModify({ archive: value, lastMessages: await last_messages(this._raw.id) }, this._raw.id);
198
208
  this._raw.archived = value;
199
- await wa.engine.set(`/chat/${this._raw.id}`, serialize(this._raw));
209
+ await wa.engine.set(`/chat/${this._raw.id}`, serialize(this._raw), this._raw.activity ?? (await activity_of(this._raw.id)));
200
210
  ok = true;
201
211
  }
202
212
  return ok;
@@ -218,7 +228,7 @@ export function chat(wa) {
218
228
  if (allowed) {
219
229
  await socket.chatModify({ pin: value }, this._raw.id);
220
230
  this._raw.pinned = value ? Date.now() : null;
221
- await wa.engine.set(`/chat/${this._raw.id}`, serialize(this._raw));
231
+ await wa.engine.set(`/chat/${this._raw.id}`, serialize(this._raw), this._raw.activity ?? (await activity_of(this._raw.id)));
222
232
  ok = true;
223
233
  }
224
234
  }
@@ -239,7 +249,7 @@ export function chat(wa) {
239
249
  const muted = end > Date.now();
240
250
  await socket.chatModify({ mute: muted ? end : null }, this._raw.id);
241
251
  this._raw.mute_end_time = muted ? end : null;
242
- await wa.engine.set(`/chat/${this._raw.id}`, serialize(this._raw));
252
+ await wa.engine.set(`/chat/${this._raw.id}`, serialize(this._raw), this._raw.activity ?? (await activity_of(this._raw.id)));
243
253
  ok = true;
244
254
  }
245
255
  return ok;
@@ -256,7 +266,7 @@ export function chat(wa) {
256
266
  if (socket) {
257
267
  await socket.chatModify({ markRead: true, lastMessages: await last_messages(this._raw.id) }, this._raw.id);
258
268
  this._raw.unread_count = 0;
259
- await wa.engine.set(`/chat/${this._raw.id}`, serialize(this._raw));
269
+ await wa.engine.set(`/chat/${this._raw.id}`, serialize(this._raw), this._raw.activity ?? (await activity_of(this._raw.id)));
260
270
  ok = true;
261
271
  }
262
272
  return ok;
@@ -617,6 +617,20 @@ export class WhatsApp {
617
617
  await this.engine.set(`/lid/${lid}`, serialize(pn));
618
618
  await this.engine.set(`/lid/${pn}`, serialize(lid));
619
619
  }
620
+ /**
621
+ * Actividad del chat según su último mensaje persistido. Es el respaldo para los
622
+ * documentos que se guardaron antes de que el chat llevara su propia marca.
623
+ * Chat activity from its last persisted message. It is the fallback for documents stored
624
+ * before the chat carried its own stamp.
625
+ *
626
+ * @param cid - Identificador del chat / Chat identifier
627
+ * @returns Epoch ms del último mensaje, o 0 si el chat no tiene ninguno / Last message epoch ms, or 0 when the chat has none
628
+ * @internal
629
+ */
630
+ async #activity(cid) {
631
+ const [raw] = await this.engine.list(`/chat/${cid}/message`, 0, 1);
632
+ return deserialize(raw ?? null)?.created_at ?? 0;
633
+ }
620
634
  /** @internal */
621
635
  async #handle_chats_upsert(chats) {
622
636
  for (const ch of chats) {
@@ -633,7 +647,13 @@ export class WhatsApp {
633
647
  if (ch.name) {
634
648
  raw.name = ch.name;
635
649
  }
636
- await this.engine.set(`/chat/${ch.id}`, serialize(raw));
650
+ // El sync trae la última actividad del chat; sin ella la lista quedaría ordenada
651
+ // por el momento en que se escribió cada documento.
652
+ // The sync carries the chat's last activity; without it the list would be ordered
653
+ // by the moment each document happened to be written.
654
+ const stamp = ch.conversationTimestamp != null ? Number(ch.conversationTimestamp) * 1_000 : null;
655
+ raw.activity = Math.max(stamp ?? 0, raw.activity ?? 0, await this.#activity(ch.id)) || null;
656
+ await this.engine.set(`/chat/${ch.id}`, serialize(raw), raw.activity ?? 0);
637
657
  if (current === null) {
638
658
  this.emit('chat:created', new this.Chat(raw), this);
639
659
  }
@@ -669,7 +689,9 @@ export class WhatsApp {
669
689
  }
670
690
  if (Object.keys(patch).length > 0) {
671
691
  const merged = { ...current, ...patch };
672
- await this.engine.set(`/chat/${ch.id}`, serialize(merged));
692
+ // Fijar, archivar o silenciar no es actividad: el chat conserva su posición.
693
+ // Pinning, archiving or muting is not activity: the chat keeps its position.
694
+ await this.engine.set(`/chat/${ch.id}`, serialize(merged), merged.activity ?? undefined);
673
695
  if (pinned_changed) {
674
696
  this.emit(ch.pinned != null ? 'chat:pinned' : 'chat:unpinned', new this.Chat(merged), this);
675
697
  }
@@ -974,12 +996,22 @@ export class WhatsApp {
974
996
  const chat_raw = {
975
997
  id: cid,
976
998
  name: is_group ? null : push_name,
999
+ activity: doc.created_at,
977
1000
  };
978
- await this.engine.set(`/chat/${cid}`, serialize(chat_raw));
1001
+ await this.engine.set(`/chat/${cid}`, serialize(chat_raw), doc.created_at);
979
1002
  this.emit('chat:created', new this.Chat(chat_raw), this);
980
1003
  }
981
1004
  }
982
1005
  await this.engine.set(`/chat/${cid}/message/${mid}`, serialize(doc), doc.created_at);
1006
+ // El mensaje más nuevo define la posición del chat en la lista; un mensaje viejo
1007
+ // que llega en un re-sync no la altera.
1008
+ // The newest message defines the chat's position in the list; an old message
1009
+ // arriving in a re-sync does not move it.
1010
+ const chat_doc = deserialize(await this.engine.get(`/chat/${cid}`));
1011
+ if (chat_doc && doc.created_at > (chat_doc.activity ?? 0)) {
1012
+ chat_doc.activity = doc.created_at;
1013
+ await this.engine.set(`/chat/${cid}`, serialize(chat_doc), doc.created_at);
1014
+ }
983
1015
  // El binario solo se materializa en la primera entrega; en re-syncs ya vive en el engine.
984
1016
  // The binary is only materialized on first delivery; on re-syncs it already lives in the engine.
985
1017
  let content_buf = Buffer.alloc(0);
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.3",
75
+ "version": "6.2.0",
76
76
  "engines": {
77
77
  "node": ">=20"
78
78
  },