@periskope/baileys 6.8.0-alpha.1 → 7.0.0-alpha-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.
Files changed (53) hide show
  1. package/README.md +0 -6
  2. package/lib/Signal/Group/group_cipher.d.ts +1 -0
  3. package/lib/Signal/Group/group_cipher.d.ts.map +1 -1
  4. package/lib/Signal/Group/group_cipher.js +36 -28
  5. package/lib/Signal/Group/group_cipher.js.map +1 -1
  6. package/lib/Signal/Group/queue-job.d.ts +1 -0
  7. package/lib/Signal/Group/queue-job.d.ts.map +1 -1
  8. package/lib/Signal/Group/queue-job.js +3 -0
  9. package/lib/Signal/Group/queue-job.js.map +1 -1
  10. package/lib/Signal/libsignal.d.ts.map +1 -1
  11. package/lib/Signal/libsignal.js +41 -76
  12. package/lib/Signal/libsignal.js.map +1 -1
  13. package/lib/Signal/lid-mapping.d.ts +1 -3
  14. package/lib/Signal/lid-mapping.d.ts.map +1 -1
  15. package/lib/Signal/lid-mapping.js +3 -29
  16. package/lib/Signal/lid-mapping.js.map +1 -1
  17. package/lib/Socket/business.d.ts.map +1 -1
  18. package/lib/Socket/chats.d.ts.map +1 -1
  19. package/lib/Socket/chats.js +4 -3
  20. package/lib/Socket/chats.js.map +1 -1
  21. package/lib/Socket/communities.d.ts.map +1 -1
  22. package/lib/Socket/groups.d.ts.map +1 -1
  23. package/lib/Socket/index.d.ts.map +1 -1
  24. package/lib/Socket/messages-recv.d.ts.map +1 -1
  25. package/lib/Socket/messages-recv.js +302 -272
  26. package/lib/Socket/messages-recv.js.map +1 -1
  27. package/lib/Socket/messages-send.d.ts.map +1 -1
  28. package/lib/Socket/messages-send.js +1 -1
  29. package/lib/Socket/messages-send.js.map +1 -1
  30. package/lib/Socket/newsletter.d.ts.map +1 -1
  31. package/lib/Socket/socket.d.ts.map +1 -1
  32. package/lib/Socket/socket.js +57 -43
  33. package/lib/Socket/socket.js.map +1 -1
  34. package/lib/Types/Auth.d.ts +2 -4
  35. package/lib/Types/Auth.d.ts.map +1 -1
  36. package/lib/Utils/auth-utils.d.ts.map +1 -1
  37. package/lib/Utils/auth-utils.js +78 -362
  38. package/lib/Utils/auth-utils.js.map +1 -1
  39. package/lib/Utils/decode-wa-message.d.ts +0 -5
  40. package/lib/Utils/decode-wa-message.d.ts.map +1 -1
  41. package/lib/Utils/decode-wa-message.js +3 -24
  42. package/lib/Utils/decode-wa-message.js.map +1 -1
  43. package/lib/Utils/generics.d.ts.map +1 -1
  44. package/lib/Utils/generics.js +16 -6
  45. package/lib/Utils/generics.js.map +1 -1
  46. package/lib/Utils/process-message.d.ts +2 -2
  47. package/lib/Utils/process-message.d.ts.map +1 -1
  48. package/lib/Utils/process-message.js +2 -2
  49. package/lib/Utils/process-message.js.map +1 -1
  50. package/lib/WAM/encode.d.ts.map +1 -1
  51. package/lib/WAM/encode.js +0 -1
  52. package/lib/WAM/encode.js.map +1 -1
  53. package/package.json +2 -1
@@ -7,7 +7,7 @@ import { DEFAULT_CACHE_TTLS, KEY_BUNDLE_TYPE, MIN_PREKEY_COUNT } from '../Defaul
7
7
  import { WAMessageStatus, WAMessageStubType } from '../Types/index.js';
8
8
  import { aesDecryptCTR, aesEncryptGCM, cleanMessage, Curve, decodeMediaRetryNode, decodeMessageNode, decryptMessageNode, delay, derivePairingCodeKey, encodeBigEndian, encodeSignedDeviceIdentity, getCallStatusFromNode, getHistoryMsg, getNextPreKeys, getStatusFromReceiptType, hkdf, MISSING_KEYS_ERROR_TEXT, NACK_REASONS, NO_MESSAGE_FOUND_ERROR_TEXT, unixTimestampSeconds, xmppPreKey, xmppSignedPreKey } from '../Utils/index.js';
9
9
  import { makeMutex } from '../Utils/make-mutex.js';
10
- import { areJidsSameUser, getAllBinaryNodeChildren, getBinaryNodeChild, getBinaryNodeChildBuffer, getBinaryNodeChildren, getBinaryNodeChildString, isJidGroup, isJidStatusBroadcast, isLidUser, jidDecode, jidNormalizedUser, S_WHATSAPP_NET } from '../WABinary/index.js';
10
+ import { areJidsSameUser, getAllBinaryNodeChildren, getBinaryNodeChild, getBinaryNodeChildBuffer, getBinaryNodeChildren, getBinaryNodeChildString, isJidGroup, isJidStatusBroadcast, isLidUser, isPnUser, jidDecode, jidNormalizedUser, S_WHATSAPP_NET } from '../WABinary/index.js';
11
11
  import { extractGroupMetadata } from './groups.js';
12
12
  import { makeMessagesSocket } from './messages-send.js';
13
13
  export const makeMessagesRecvSocket = (config) => {
@@ -32,187 +32,6 @@ export const makeMessagesRecvSocket = (config) => {
32
32
  useClones: false
33
33
  });
34
34
  let sendActiveReceipts = false;
35
- const fetchMessageHistory = async (count, oldestMsgKey, oldestMsgTimestamp) => {
36
- if (!authState.creds.me?.id) {
37
- throw new Boom('Not authenticated');
38
- }
39
- const pdoMessage = {
40
- historySyncOnDemandRequest: {
41
- chatJid: oldestMsgKey.remoteJid,
42
- oldestMsgFromMe: oldestMsgKey.fromMe,
43
- oldestMsgId: oldestMsgKey.id,
44
- oldestMsgTimestampMs: oldestMsgTimestamp,
45
- onDemandMsgCount: count
46
- },
47
- peerDataOperationRequestType: proto.Message.PeerDataOperationRequestType.HISTORY_SYNC_ON_DEMAND
48
- };
49
- return sendPeerDataOperationMessage(pdoMessage);
50
- };
51
- const requestPlaceholderResend = async (messageKey) => {
52
- if (!authState.creds.me?.id) {
53
- throw new Boom('Not authenticated');
54
- }
55
- if (placeholderResendCache.get(messageKey?.id)) {
56
- logger.debug({ messageKey }, 'already requested resend');
57
- return;
58
- }
59
- else {
60
- placeholderResendCache.set(messageKey?.id, true);
61
- }
62
- await delay(5000);
63
- if (!placeholderResendCache.get(messageKey?.id)) {
64
- logger.debug({ messageKey }, 'message received while resend requested');
65
- return 'RESOLVED';
66
- }
67
- const pdoMessage = {
68
- placeholderMessageResendRequest: [
69
- {
70
- messageKey
71
- }
72
- ],
73
- peerDataOperationRequestType: proto.Message.PeerDataOperationRequestType.PLACEHOLDER_MESSAGE_RESEND
74
- };
75
- setTimeout(() => {
76
- if (placeholderResendCache.get(messageKey?.id)) {
77
- logger.debug({ messageKey }, 'PDO message without response after 15 seconds. Phone possibly offline');
78
- placeholderResendCache.del(messageKey?.id);
79
- }
80
- }, 15000);
81
- return sendPeerDataOperationMessage(pdoMessage);
82
- };
83
- // Handles mex newsletter notifications
84
- const handleMexNewsletterNotification = async (node) => {
85
- const mexNode = getBinaryNodeChild(node, 'mex');
86
- if (!mexNode?.content) {
87
- logger.warn({ node }, 'Invalid mex newsletter notification');
88
- return;
89
- }
90
- let data;
91
- try {
92
- data = JSON.parse(mexNode.content.toString());
93
- }
94
- catch (error) {
95
- logger.error({ err: error, node }, 'Failed to parse mex newsletter notification');
96
- return;
97
- }
98
- const operation = data?.operation;
99
- const updates = data?.updates;
100
- if (!updates || !operation) {
101
- logger.warn({ data }, 'Invalid mex newsletter notification content');
102
- return;
103
- }
104
- logger.info({ operation, updates }, 'got mex newsletter notification');
105
- switch (operation) {
106
- case 'NotificationNewsletterUpdate':
107
- for (const update of updates) {
108
- if (update.jid && update.settings && Object.keys(update.settings).length > 0) {
109
- ev.emit('newsletter-settings.update', {
110
- id: update.jid,
111
- update: update.settings
112
- });
113
- }
114
- }
115
- break;
116
- case 'NotificationNewsletterAdminPromote':
117
- for (const update of updates) {
118
- if (update.jid && update.user) {
119
- ev.emit('newsletter-participants.update', {
120
- id: update.jid,
121
- author: node.attrs.from,
122
- user: update.user,
123
- new_role: 'ADMIN',
124
- action: 'promote'
125
- });
126
- }
127
- }
128
- break;
129
- default:
130
- logger.info({ operation, data }, 'Unhandled mex newsletter notification');
131
- break;
132
- }
133
- };
134
- // Handles newsletter notifications
135
- const handleNewsletterNotification = async (node) => {
136
- const from = node.attrs.from;
137
- const child = getAllBinaryNodeChildren(node)[0];
138
- const author = node.attrs.participant;
139
- logger.info({ from, child }, 'got newsletter notification');
140
- switch (child.tag) {
141
- case 'reaction':
142
- const reactionUpdate = {
143
- id: from,
144
- server_id: child.attrs.message_id,
145
- reaction: {
146
- code: getBinaryNodeChildString(child, 'reaction'),
147
- count: 1
148
- }
149
- };
150
- ev.emit('newsletter.reaction', reactionUpdate);
151
- break;
152
- case 'view':
153
- const viewUpdate = {
154
- id: from,
155
- server_id: child.attrs.message_id,
156
- count: parseInt(child.content?.toString() || '0', 10)
157
- };
158
- ev.emit('newsletter.view', viewUpdate);
159
- break;
160
- case 'participant':
161
- const participantUpdate = {
162
- id: from,
163
- author,
164
- user: child.attrs.jid,
165
- action: child.attrs.action,
166
- new_role: child.attrs.role
167
- };
168
- ev.emit('newsletter-participants.update', participantUpdate);
169
- break;
170
- case 'update':
171
- const settingsNode = getBinaryNodeChild(child, 'settings');
172
- if (settingsNode) {
173
- const update = {};
174
- const nameNode = getBinaryNodeChild(settingsNode, 'name');
175
- if (nameNode?.content)
176
- update.name = nameNode.content.toString();
177
- const descriptionNode = getBinaryNodeChild(settingsNode, 'description');
178
- if (descriptionNode?.content)
179
- update.description = descriptionNode.content.toString();
180
- ev.emit('newsletter-settings.update', {
181
- id: from,
182
- update
183
- });
184
- }
185
- break;
186
- case 'message':
187
- const plaintextNode = getBinaryNodeChild(child, 'plaintext');
188
- if (plaintextNode?.content) {
189
- try {
190
- const contentBuf = typeof plaintextNode.content === 'string'
191
- ? Buffer.from(plaintextNode.content, 'binary')
192
- : Buffer.from(plaintextNode.content);
193
- const messageProto = proto.Message.decode(contentBuf);
194
- const fullMessage = proto.WebMessageInfo.create({
195
- key: {
196
- remoteJid: from,
197
- id: child.attrs.message_id || child.attrs.server_id,
198
- fromMe: false
199
- },
200
- message: messageProto,
201
- messageTimestamp: +child.attrs.t
202
- });
203
- await upsertMessage(fullMessage, 'append');
204
- logger.info('Processed plaintext newsletter message');
205
- }
206
- catch (error) {
207
- logger.error({ error }, 'Failed to decode plaintext newsletter message');
208
- }
209
- }
210
- break;
211
- default:
212
- logger.warn({ node }, 'Unknown newsletter notification');
213
- break;
214
- }
215
- };
216
35
  const sendMessageAck = async ({ tag, attrs, content }, errorCode) => {
217
36
  const stanza = {
218
37
  tag: 'ack',
@@ -410,8 +229,8 @@ export const makeMessagesRecvSocket = (config) => {
410
229
  ev.emit('creds.update', update);
411
230
  }
412
231
  await sendNode(receipt);
413
- logger.info({ msgAttrs: node.attrs, retryCount }, 'sent retry receipt');
414
- }, authState?.creds?.me?.id || 'sendRetryRequest');
232
+ logger.info({ msgAttrs: node.attrs, retryCount, shouldRecreateSession, recreateReason }, 'sent retry receipt');
233
+ });
415
234
  };
416
235
  const handleEncryptNotification = async (node) => {
417
236
  const from = node.attrs.from;
@@ -835,7 +654,7 @@ export const makeMessagesRecvSocket = (config) => {
835
654
  if (typeof status !== 'undefined' &&
836
655
  // basically, we only want to know when a message from us has been delivered to/read by the other person
837
656
  // or another device of ours has read some messages
838
- (status >= proto.WebMessageInfo.Status.SERVER_ACK)) {
657
+ status >= proto.WebMessageInfo.Status.SERVER_ACK) {
839
658
  if (isJidGroup(remoteJid) || isJidStatusBroadcast(remoteJid)) {
840
659
  const updateKey = status === proto.WebMessageInfo.Status.DELIVERY_ACK ? 'receiptTimestamp' : 'readTimestamp';
841
660
  if (attrs.participant) {
@@ -946,7 +765,7 @@ export const makeMessagesRecvSocket = (config) => {
946
765
  // TODO: temporary fix for crashes and issues resulting of failed msmsg decryption
947
766
  if (encNode && encNode.attrs.type === 'msmsg') {
948
767
  logger.debug({ key: node.attrs.key }, 'ignored msmsg');
949
- await sendMessageAck(node);
768
+ await sendMessageAck(node, NACK_REASONS.MissingMessageSecret);
950
769
  return;
951
770
  }
952
771
  let response;
@@ -989,105 +808,183 @@ export const makeMessagesRecvSocket = (config) => {
989
808
  await lidMapping.storeLIDPNMapping(msg.key.participant || msg.key.remoteJid, alt);
990
809
  }
991
810
  }
992
- if (msg.key?.remoteJid && msg.key?.id && messageRetryManager) {
993
- messageRetryManager.addRecentMessage(msg.key.remoteJid, msg.key.id, msg.message);
811
+ }
812
+ if (msg.key?.remoteJid && msg.key?.id && messageRetryManager) {
813
+ messageRetryManager.addRecentMessage(msg.key.remoteJid, msg.key.id, msg.message);
814
+ logger.debug({
815
+ jid: msg.key.remoteJid,
816
+ id: msg.key.id
817
+ }, 'Added message to recent cache for retry receipts');
818
+ }
819
+ if (msg.message?.protocolMessage?.lidMigrationMappingSyncMessage?.encodedMappingPayload) {
820
+ try {
821
+ const payload = msg.message.protocolMessage.lidMigrationMappingSyncMessage.encodedMappingPayload;
822
+ const decoded = proto.LIDMigrationMappingSyncPayload.decode(payload);
994
823
  logger.debug({
995
- jid: msg.key.remoteJid,
996
- id: msg.key.id
997
- }, 'Added message to recent cache for retry receipts');
824
+ mappingCount: decoded.pnToLidMappings?.length || 0,
825
+ timestamp: decoded.chatDbMigrationTimestamp
826
+ }, 'Received LID migration sync message from server');
827
+ const lidMapping = signalRepository.getLIDMappingStore();
828
+ if (decoded.pnToLidMappings && decoded.pnToLidMappings.length > 0) {
829
+ for (const mapping of decoded.pnToLidMappings) {
830
+ const pn = `${mapping.pn}@s.whatsapp.net`;
831
+ // Use latestLid if available, otherwise assignedLid (proper LID refresh)
832
+ const lidValue = mapping.latestLid || mapping.assignedLid;
833
+ const lid = `${lidValue}@lid`;
834
+ await lidMapping.storeLIDPNMapping(lid, pn);
835
+ logger.debug({
836
+ pn,
837
+ lid,
838
+ assignedLid: mapping.assignedLid,
839
+ latestLid: mapping.latestLid,
840
+ usedLatest: !!mapping.latestLid
841
+ }, 'Stored server-provided PN-LID mapping');
842
+ }
843
+ }
998
844
  }
999
- try {
1000
- await Promise.all([
1001
- processingMutex.mutex(async () => {
1002
- await decrypt();
1003
- // message failed to decrypt
1004
- if (msg.messageStubType === proto.WebMessageInfo.StubType.CIPHERTEXT) {
1005
- if (msg?.messageStubParameters?.[0] === MISSING_KEYS_ERROR_TEXT) {
1006
- return sendMessageAck(node, NACK_REASONS.ParsingError);
1007
- }
1008
- const errorMessage = msg?.messageStubParameters?.[0] || '';
1009
- const isPreKeyError = errorMessage.includes('PreKey');
1010
- console.debug(`[handleMessage] Attempting retry request for failed decryption`);
1011
- // Handle both pre-key and normal retries in single mutex
1012
- retryMutex.mutex(async () => {
1013
- try {
1014
- if (!ws.isOpen) {
1015
- logger.debug({ node }, 'Connection closed, skipping retry');
1016
- return;
1017
- }
1018
- if (getBinaryNodeChild(node, 'unavailable')) {
1019
- logger.debug('Message unavailable, skipping retry');
1020
- return;
1021
- }
1022
- // Handle pre-key errors with upload and delay
1023
- if (isPreKeyError) {
1024
- logger.info({ error: errorMessage }, 'PreKey error detected, uploading and retrying');
1025
- try {
1026
- logger.debug('Uploading pre-keys for error recovery');
1027
- await uploadPreKeys(5);
1028
- logger.debug('Waiting for server to process new pre-keys');
1029
- await delay(1000);
1030
- }
1031
- catch (uploadErr) {
1032
- logger.error({ uploadErr }, 'Pre-key upload failed, proceeding with retry anyway');
1033
- }
1034
- }
1035
- const encNode = getBinaryNodeChild(node, 'enc');
1036
- await sendRetryRequest(node, !encNode);
1037
- if (retryRequestDelayMs) {
1038
- await delay(retryRequestDelayMs);
1039
- }
845
+ catch (error) {
846
+ logger.error({ error }, 'Failed to process LID migration sync message');
847
+ }
848
+ }
849
+ try {
850
+ await Promise.all([
851
+ processingMutex.mutex(async () => {
852
+ await decrypt();
853
+ // message failed to decrypt
854
+ if (msg.messageStubType === proto.WebMessageInfo.StubType.CIPHERTEXT) {
855
+ if (msg?.messageStubParameters?.[0] === MISSING_KEYS_ERROR_TEXT) {
856
+ return sendMessageAck(node, NACK_REASONS.ParsingError);
857
+ }
858
+ const errorMessage = msg?.messageStubParameters?.[0] || '';
859
+ const isPreKeyError = errorMessage.includes('PreKey');
860
+ console.debug(`[handleMessage] Attempting retry request for failed decryption`);
861
+ // Handle both pre-key and normal retries in single mutex
862
+ retryMutex.mutex(async () => {
863
+ try {
864
+ if (!ws.isOpen) {
865
+ logger.debug({ node }, 'Connection closed, skipping retry');
866
+ return;
867
+ }
868
+ if (getBinaryNodeChild(node, 'unavailable')) {
869
+ logger.debug('Message unavailable, skipping retry');
870
+ return;
1040
871
  }
1041
- catch (err) {
1042
- logger.error({ err, isPreKeyError }, 'Failed to handle retry, attempting basic retry');
1043
- // Still attempt retry even if pre-key upload failed
872
+ // Handle pre-key errors with upload and delay
873
+ if (isPreKeyError) {
874
+ logger.info({ error: errorMessage }, 'PreKey error detected, uploading and retrying');
1044
875
  try {
1045
- const encNode = getBinaryNodeChild(node, 'enc');
1046
- await sendRetryRequest(node, !encNode);
876
+ logger.debug('Uploading pre-keys for error recovery');
877
+ await uploadPreKeys(5);
878
+ logger.debug('Waiting for server to process new pre-keys');
879
+ await delay(1000);
1047
880
  }
1048
- catch (retryErr) {
1049
- logger.error({ retryErr }, 'Failed to send retry after error handling');
881
+ catch (uploadErr) {
882
+ logger.error({ uploadErr }, 'Pre-key upload failed, proceeding with retry anyway');
1050
883
  }
1051
884
  }
1052
- });
1053
- }
1054
- else {
1055
- // no type in the receipt => message delivered
1056
- let type = undefined;
1057
- let participant = msg.key.participant;
1058
- if (category === 'peer') {
1059
- // special peer message
1060
- type = 'peer_msg';
1061
- }
1062
- else if (msg.key.fromMe) {
1063
- // message was sent by us from a different device
1064
- type = 'sender';
1065
- // need to specially handle this case
1066
- if (isLidUser(msg.key.remoteJid) || isLidUser(msg.key.remoteJidAlt)) {
1067
- participant = author; // TODO: investigate sending receipts to LIDs and not PNs
885
+ const encNode = getBinaryNodeChild(node, 'enc');
886
+ await sendRetryRequest(node, !encNode);
887
+ if (retryRequestDelayMs) {
888
+ await delay(retryRequestDelayMs);
1068
889
  }
1069
890
  }
1070
- else if (!sendActiveReceipts) {
1071
- type = 'inactive';
891
+ catch (err) {
892
+ logger.error({ err, isPreKeyError }, 'Failed to handle retry, attempting basic retry');
893
+ // Still attempt retry even if pre-key upload failed
894
+ try {
895
+ const encNode = getBinaryNodeChild(node, 'enc');
896
+ await sendRetryRequest(node, !encNode);
897
+ }
898
+ catch (retryErr) {
899
+ logger.error({ retryErr }, 'Failed to send retry after error handling');
900
+ }
1072
901
  }
1073
- await sendReceipt(msg.key.remoteJid, participant, [msg.key.id], type);
1074
- // send ack for history message
1075
- const isAnyHistoryMsg = getHistoryMsg(msg.message);
1076
- if (isAnyHistoryMsg) {
1077
- const jid = jidNormalizedUser(msg.key.remoteJid);
1078
- await sendReceipt(jid, undefined, [msg.key.id], 'hist_sync');
902
+ });
903
+ }
904
+ else {
905
+ // no type in the receipt => message delivered
906
+ let type = undefined;
907
+ let participant = msg.key.participant;
908
+ if (category === 'peer') {
909
+ // special peer message
910
+ type = 'peer_msg';
911
+ }
912
+ else if (msg.key.fromMe) {
913
+ // message was sent by us from a different device
914
+ type = 'sender';
915
+ // need to specially handle this case
916
+ if (isLidUser(msg.key.remoteJid) || isLidUser(msg.key.remoteJidAlt)) {
917
+ participant = author; // TODO: investigate sending receipts to LIDs and not PNs
1079
918
  }
1080
919
  }
1081
- cleanMessage(msg, authState.creds.me.id);
1082
- await sendMessageAck(node);
1083
- await upsertMessage(msg, node.attrs.offline ? 'append' : 'notify');
1084
- })
1085
- ]);
1086
- }
1087
- catch (error) {
1088
- logger.error({ error, node }, 'error in handling message');
1089
- }
920
+ else if (!sendActiveReceipts) {
921
+ type = 'inactive';
922
+ }
923
+ await sendReceipt(msg.key.remoteJid, participant, [msg.key.id], type);
924
+ // send ack for history message
925
+ const isAnyHistoryMsg = getHistoryMsg(msg.message);
926
+ if (isAnyHistoryMsg) {
927
+ const jid = jidNormalizedUser(msg.key.remoteJid);
928
+ await sendReceipt(jid, undefined, [msg.key.id], 'hist_sync');
929
+ }
930
+ }
931
+ cleanMessage(msg, authState.creds.me.id);
932
+ await sendMessageAck(node);
933
+ await upsertMessage(msg, node.attrs.offline ? 'append' : 'notify');
934
+ })
935
+ ]);
936
+ }
937
+ catch (error) {
938
+ logger.error({ error, node }, 'error in handling message');
939
+ }
940
+ };
941
+ const fetchMessageHistory = async (count, oldestMsgKey, oldestMsgTimestamp) => {
942
+ if (!authState.creds.me?.id) {
943
+ throw new Boom('Not authenticated');
944
+ }
945
+ const pdoMessage = {
946
+ historySyncOnDemandRequest: {
947
+ chatJid: oldestMsgKey.remoteJid,
948
+ oldestMsgFromMe: oldestMsgKey.fromMe,
949
+ oldestMsgId: oldestMsgKey.id,
950
+ oldestMsgTimestampMs: oldestMsgTimestamp,
951
+ onDemandMsgCount: count
952
+ },
953
+ peerDataOperationRequestType: proto.Message.PeerDataOperationRequestType.HISTORY_SYNC_ON_DEMAND
954
+ };
955
+ return sendPeerDataOperationMessage(pdoMessage);
956
+ };
957
+ const requestPlaceholderResend = async (messageKey) => {
958
+ if (!authState.creds.me?.id) {
959
+ throw new Boom('Not authenticated');
960
+ }
961
+ if (placeholderResendCache.get(messageKey?.id)) {
962
+ logger.debug({ messageKey }, 'already requested resend');
963
+ return;
964
+ }
965
+ else {
966
+ placeholderResendCache.set(messageKey?.id, true);
1090
967
  }
968
+ await delay(5000);
969
+ if (!placeholderResendCache.get(messageKey?.id)) {
970
+ logger.debug({ messageKey }, 'message received while resend requested');
971
+ return 'RESOLVED';
972
+ }
973
+ const pdoMessage = {
974
+ placeholderMessageResendRequest: [
975
+ {
976
+ messageKey
977
+ }
978
+ ],
979
+ peerDataOperationRequestType: proto.Message.PeerDataOperationRequestType.PLACEHOLDER_MESSAGE_RESEND
980
+ };
981
+ setTimeout(() => {
982
+ if (placeholderResendCache.get(messageKey?.id)) {
983
+ logger.debug({ messageKey }, 'PDO message without response after 15 seconds. Phone possibly offline');
984
+ placeholderResendCache.del(messageKey?.id);
985
+ }
986
+ }, 15000);
987
+ return sendPeerDataOperationMessage(pdoMessage);
1091
988
  };
1092
989
  const handleCall = async (node) => {
1093
990
  let status;
@@ -1222,6 +1119,139 @@ export const makeMessagesRecvSocket = (config) => {
1222
1119
  processNodeWithBuffer(node, identifier, exec);
1223
1120
  }
1224
1121
  };
1122
+ // Handles newsletter notifications
1123
+ async function handleNewsletterNotification(node) {
1124
+ const from = node.attrs.from;
1125
+ const child = getAllBinaryNodeChildren(node)[0];
1126
+ const author = node.attrs.participant;
1127
+ logger.info({ from, child }, 'got newsletter notification');
1128
+ switch (child.tag) {
1129
+ case 'reaction':
1130
+ const reactionUpdate = {
1131
+ id: from,
1132
+ server_id: child.attrs.message_id,
1133
+ reaction: {
1134
+ code: getBinaryNodeChildString(child, 'reaction'),
1135
+ count: 1
1136
+ }
1137
+ };
1138
+ ev.emit('newsletter.reaction', reactionUpdate);
1139
+ break;
1140
+ case 'view':
1141
+ const viewUpdate = {
1142
+ id: from,
1143
+ server_id: child.attrs.message_id,
1144
+ count: parseInt(child.content?.toString() || '0', 10)
1145
+ };
1146
+ ev.emit('newsletter.view', viewUpdate);
1147
+ break;
1148
+ case 'participant':
1149
+ const participantUpdate = {
1150
+ id: from,
1151
+ author,
1152
+ user: child.attrs.jid,
1153
+ action: child.attrs.action,
1154
+ new_role: child.attrs.role
1155
+ };
1156
+ ev.emit('newsletter-participants.update', participantUpdate);
1157
+ break;
1158
+ case 'update':
1159
+ const settingsNode = getBinaryNodeChild(child, 'settings');
1160
+ if (settingsNode) {
1161
+ const update = {};
1162
+ const nameNode = getBinaryNodeChild(settingsNode, 'name');
1163
+ if (nameNode?.content)
1164
+ update.name = nameNode.content.toString();
1165
+ const descriptionNode = getBinaryNodeChild(settingsNode, 'description');
1166
+ if (descriptionNode?.content)
1167
+ update.description = descriptionNode.content.toString();
1168
+ ev.emit('newsletter-settings.update', {
1169
+ id: from,
1170
+ update
1171
+ });
1172
+ }
1173
+ break;
1174
+ case 'message':
1175
+ const plaintextNode = getBinaryNodeChild(child, 'plaintext');
1176
+ if (plaintextNode?.content) {
1177
+ try {
1178
+ const contentBuf = typeof plaintextNode.content === 'string'
1179
+ ? Buffer.from(plaintextNode.content, 'binary')
1180
+ : Buffer.from(plaintextNode.content);
1181
+ const messageProto = proto.Message.decode(contentBuf);
1182
+ const fullMessage = proto.WebMessageInfo.create({
1183
+ key: {
1184
+ remoteJid: from,
1185
+ id: child.attrs.message_id || child.attrs.server_id,
1186
+ fromMe: false
1187
+ },
1188
+ message: messageProto,
1189
+ messageTimestamp: +child.attrs.t
1190
+ });
1191
+ await upsertMessage(fullMessage, 'append');
1192
+ logger.info('Processed plaintext newsletter message');
1193
+ }
1194
+ catch (error) {
1195
+ logger.error({ error }, 'Failed to decode plaintext newsletter message');
1196
+ }
1197
+ }
1198
+ break;
1199
+ default:
1200
+ logger.warn({ node }, 'Unknown newsletter notification');
1201
+ break;
1202
+ }
1203
+ }
1204
+ // Handles mex newsletter notifications
1205
+ async function handleMexNewsletterNotification(node) {
1206
+ const mexNode = getBinaryNodeChild(node, 'mex');
1207
+ if (!mexNode?.content) {
1208
+ logger.warn({ node }, 'Invalid mex newsletter notification');
1209
+ return;
1210
+ }
1211
+ let data;
1212
+ try {
1213
+ data = JSON.parse(mexNode.content.toString());
1214
+ }
1215
+ catch (error) {
1216
+ logger.error({ err: error, node }, 'Failed to parse mex newsletter notification');
1217
+ return;
1218
+ }
1219
+ const operation = data?.operation;
1220
+ const updates = data?.updates;
1221
+ if (!updates || !operation) {
1222
+ logger.warn({ data }, 'Invalid mex newsletter notification content');
1223
+ return;
1224
+ }
1225
+ logger.info({ operation, updates }, 'got mex newsletter notification');
1226
+ switch (operation) {
1227
+ case 'NotificationNewsletterUpdate':
1228
+ for (const update of updates) {
1229
+ if (update.jid && update.settings && Object.keys(update.settings).length > 0) {
1230
+ ev.emit('newsletter-settings.update', {
1231
+ id: update.jid,
1232
+ update: update.settings
1233
+ });
1234
+ }
1235
+ }
1236
+ break;
1237
+ case 'NotificationNewsletterAdminPromote':
1238
+ for (const update of updates) {
1239
+ if (update.jid && update.user) {
1240
+ ev.emit('newsletter-participants.update', {
1241
+ id: update.jid,
1242
+ author: node.attrs.from,
1243
+ user: update.user,
1244
+ new_role: 'ADMIN',
1245
+ action: 'promote'
1246
+ });
1247
+ }
1248
+ }
1249
+ break;
1250
+ default:
1251
+ logger.info({ operation, data }, 'Unhandled mex newsletter notification');
1252
+ break;
1253
+ }
1254
+ }
1225
1255
  // recv a message
1226
1256
  ws.on('CB:message', (node) => {
1227
1257
  processNode('message', node, 'processing message', handleMessage);