@systemzero/baileys 1.0.5 β†’ 1.0.6

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 (201) hide show
  1. package/README.md +506 -601
  2. package/lib/Socket/chats.js +12 -1
  3. package/lib/Socket/groups.js +13 -4
  4. package/lib/Socket/messages-recv.js +36 -2
  5. package/lib/Socket/messages-recv.js.bak +1273 -0
  6. package/lib/Socket/messages-send.js +1 -0
  7. package/lib/Utils/bad-mac-handler.js +158 -0
  8. package/lib/Utils/get-best-version.js +41 -0
  9. package/lib/Utils/get-name.d.ts +1 -0
  10. package/lib/Utils/get-name.js +54 -0
  11. package/lib/Utils/group-status-detection.js +113 -0
  12. package/lib/Utils/index.js +7 -0
  13. package/lib/Utils/logger.js +4 -1
  14. package/lib/Utils/messages-media.js +38 -0
  15. package/lib/Utils/messages.js +113 -7
  16. package/lib/Utils/payment-detection.js +212 -0
  17. package/lib/Utils/payment-guard.d.ts +15 -0
  18. package/lib/Utils/payment-guard.js +142 -0
  19. package/lib/Utils/resolve-lid-phone.js +30 -0
  20. package/lib/WABinary/jid-utils.js +245 -1
  21. package/package.json +3 -2
  22. package/lib/Defaults/index.d.ts.map +0 -1
  23. package/lib/Defaults/index.js.map +0 -1
  24. package/lib/Signal/Group/ciphertext-message.d.ts.map +0 -1
  25. package/lib/Signal/Group/ciphertext-message.js.map +0 -1
  26. package/lib/Signal/Group/group-session-builder.d.ts.map +0 -1
  27. package/lib/Signal/Group/group-session-builder.js.map +0 -1
  28. package/lib/Signal/Group/group_cipher.d.ts.map +0 -1
  29. package/lib/Signal/Group/group_cipher.js.map +0 -1
  30. package/lib/Signal/Group/index.d.ts.map +0 -1
  31. package/lib/Signal/Group/index.js.map +0 -1
  32. package/lib/Signal/Group/keyhelper.d.ts.map +0 -1
  33. package/lib/Signal/Group/keyhelper.js.map +0 -1
  34. package/lib/Signal/Group/sender-chain-key.d.ts.map +0 -1
  35. package/lib/Signal/Group/sender-chain-key.js.map +0 -1
  36. package/lib/Signal/Group/sender-key-distribution-message.d.ts.map +0 -1
  37. package/lib/Signal/Group/sender-key-distribution-message.js.map +0 -1
  38. package/lib/Signal/Group/sender-key-message.d.ts.map +0 -1
  39. package/lib/Signal/Group/sender-key-message.js.map +0 -1
  40. package/lib/Signal/Group/sender-key-name.d.ts.map +0 -1
  41. package/lib/Signal/Group/sender-key-name.js.map +0 -1
  42. package/lib/Signal/Group/sender-key-record.d.ts.map +0 -1
  43. package/lib/Signal/Group/sender-key-record.js.map +0 -1
  44. package/lib/Signal/Group/sender-key-state.d.ts.map +0 -1
  45. package/lib/Signal/Group/sender-key-state.js.map +0 -1
  46. package/lib/Signal/Group/sender-message-key.d.ts.map +0 -1
  47. package/lib/Signal/Group/sender-message-key.js.map +0 -1
  48. package/lib/Signal/libsignal.d.ts.map +0 -1
  49. package/lib/Signal/libsignal.js.map +0 -1
  50. package/lib/Signal/lid-mapping.d.ts.map +0 -1
  51. package/lib/Signal/lid-mapping.js.map +0 -1
  52. package/lib/Socket/Client/index.d.ts.map +0 -1
  53. package/lib/Socket/Client/index.js.map +0 -1
  54. package/lib/Socket/Client/types.d.ts.map +0 -1
  55. package/lib/Socket/Client/types.js.map +0 -1
  56. package/lib/Socket/Client/websocket.d.ts.map +0 -1
  57. package/lib/Socket/Client/websocket.js.map +0 -1
  58. package/lib/Socket/business.d.ts.map +0 -1
  59. package/lib/Socket/business.js.map +0 -1
  60. package/lib/Socket/chats.d.ts.map +0 -1
  61. package/lib/Socket/chats.js.map +0 -1
  62. package/lib/Socket/communities.d.ts.map +0 -1
  63. package/lib/Socket/communities.js.map +0 -1
  64. package/lib/Socket/groups.d.ts.map +0 -1
  65. package/lib/Socket/groups.js.map +0 -1
  66. package/lib/Socket/index.d.ts.map +0 -1
  67. package/lib/Socket/index.js.map +0 -1
  68. package/lib/Socket/messages-recv.d.ts.map +0 -1
  69. package/lib/Socket/messages-recv.js.map +0 -1
  70. package/lib/Socket/messages-send.d.ts.map +0 -1
  71. package/lib/Socket/messages-send.js.map +0 -1
  72. package/lib/Socket/mex.d.ts.map +0 -1
  73. package/lib/Socket/mex.js.map +0 -1
  74. package/lib/Socket/newsletter.d.ts.map +0 -1
  75. package/lib/Socket/newsletter.js.map +0 -1
  76. package/lib/Socket/socket.d.ts.map +0 -1
  77. package/lib/Socket/socket.js.map +0 -1
  78. package/lib/Types/Auth.d.ts.map +0 -1
  79. package/lib/Types/Auth.js.map +0 -1
  80. package/lib/Types/Bussines.d.ts.map +0 -1
  81. package/lib/Types/Bussines.js.map +0 -1
  82. package/lib/Types/Call.d.ts.map +0 -1
  83. package/lib/Types/Call.js.map +0 -1
  84. package/lib/Types/Chat.d.ts.map +0 -1
  85. package/lib/Types/Chat.js.map +0 -1
  86. package/lib/Types/Contact.d.ts.map +0 -1
  87. package/lib/Types/Contact.js.map +0 -1
  88. package/lib/Types/Events.d.ts.map +0 -1
  89. package/lib/Types/Events.js.map +0 -1
  90. package/lib/Types/GroupMetadata.d.ts.map +0 -1
  91. package/lib/Types/GroupMetadata.js.map +0 -1
  92. package/lib/Types/Label.d.ts.map +0 -1
  93. package/lib/Types/Label.js.map +0 -1
  94. package/lib/Types/LabelAssociation.d.ts.map +0 -1
  95. package/lib/Types/LabelAssociation.js.map +0 -1
  96. package/lib/Types/Message.d.ts.map +0 -1
  97. package/lib/Types/Message.js.map +0 -1
  98. package/lib/Types/Newsletter.d.ts.map +0 -1
  99. package/lib/Types/Newsletter.js.map +0 -1
  100. package/lib/Types/Product.d.ts.map +0 -1
  101. package/lib/Types/Product.js.map +0 -1
  102. package/lib/Types/Signal.d.ts.map +0 -1
  103. package/lib/Types/Signal.js.map +0 -1
  104. package/lib/Types/Socket.d.ts.map +0 -1
  105. package/lib/Types/Socket.js.map +0 -1
  106. package/lib/Types/State.d.ts.map +0 -1
  107. package/lib/Types/State.js.map +0 -1
  108. package/lib/Types/USync.d.ts.map +0 -1
  109. package/lib/Types/USync.js.map +0 -1
  110. package/lib/Types/index.d.ts.map +0 -1
  111. package/lib/Types/index.js.map +0 -1
  112. package/lib/Utils/auth-utils.d.ts.map +0 -1
  113. package/lib/Utils/auth-utils.js.map +0 -1
  114. package/lib/Utils/browser-utils.d.ts.map +0 -1
  115. package/lib/Utils/browser-utils.js.map +0 -1
  116. package/lib/Utils/business.d.ts.map +0 -1
  117. package/lib/Utils/business.js.map +0 -1
  118. package/lib/Utils/chat-utils.d.ts.map +0 -1
  119. package/lib/Utils/chat-utils.js.map +0 -1
  120. package/lib/Utils/crypto.d.ts.map +0 -1
  121. package/lib/Utils/crypto.js.map +0 -1
  122. package/lib/Utils/decode-wa-message.d.ts.map +0 -1
  123. package/lib/Utils/decode-wa-message.js.map +0 -1
  124. package/lib/Utils/event-buffer.d.ts.map +0 -1
  125. package/lib/Utils/event-buffer.js.map +0 -1
  126. package/lib/Utils/generics.d.ts.map +0 -1
  127. package/lib/Utils/generics.js.map +0 -1
  128. package/lib/Utils/history.d.ts.map +0 -1
  129. package/lib/Utils/history.js.map +0 -1
  130. package/lib/Utils/index.d.ts.map +0 -1
  131. package/lib/Utils/index.js.map +0 -1
  132. package/lib/Utils/link-preview.d.ts.map +0 -1
  133. package/lib/Utils/link-preview.js.map +0 -1
  134. package/lib/Utils/logger.d.ts.map +0 -1
  135. package/lib/Utils/logger.js.map +0 -1
  136. package/lib/Utils/lt-hash.d.ts.map +0 -1
  137. package/lib/Utils/lt-hash.js.map +0 -1
  138. package/lib/Utils/make-mutex.d.ts.map +0 -1
  139. package/lib/Utils/make-mutex.js.map +0 -1
  140. package/lib/Utils/message-retry-manager.d.ts.map +0 -1
  141. package/lib/Utils/message-retry-manager.js.map +0 -1
  142. package/lib/Utils/messages-media.d.ts.map +0 -1
  143. package/lib/Utils/messages-media.js.map +0 -1
  144. package/lib/Utils/messages.d.ts.map +0 -1
  145. package/lib/Utils/messages.js.map +0 -1
  146. package/lib/Utils/noise-handler.d.ts.map +0 -1
  147. package/lib/Utils/noise-handler.js.map +0 -1
  148. package/lib/Utils/pre-key-manager.d.ts.map +0 -1
  149. package/lib/Utils/pre-key-manager.js.map +0 -1
  150. package/lib/Utils/process-message.d.ts.map +0 -1
  151. package/lib/Utils/process-message.js.map +0 -1
  152. package/lib/Utils/signal.d.ts.map +0 -1
  153. package/lib/Utils/signal.js.map +0 -1
  154. package/lib/Utils/use-multi-file-auth-state.d.ts.map +0 -1
  155. package/lib/Utils/use-multi-file-auth-state.js.map +0 -1
  156. package/lib/Utils/validate-connection.d.ts.map +0 -1
  157. package/lib/Utils/validate-connection.js.map +0 -1
  158. package/lib/WABinary/constants.d.ts.map +0 -1
  159. package/lib/WABinary/constants.js.map +0 -1
  160. package/lib/WABinary/decode.d.ts.map +0 -1
  161. package/lib/WABinary/decode.js.map +0 -1
  162. package/lib/WABinary/encode.d.ts.map +0 -1
  163. package/lib/WABinary/encode.js.map +0 -1
  164. package/lib/WABinary/generic-utils.d.ts.map +0 -1
  165. package/lib/WABinary/generic-utils.js.map +0 -1
  166. package/lib/WABinary/index.d.ts.map +0 -1
  167. package/lib/WABinary/index.js.map +0 -1
  168. package/lib/WABinary/jid-utils.d.ts.map +0 -1
  169. package/lib/WABinary/jid-utils.js.map +0 -1
  170. package/lib/WABinary/types.d.ts.map +0 -1
  171. package/lib/WABinary/types.js.map +0 -1
  172. package/lib/WAM/BinaryInfo.d.ts.map +0 -1
  173. package/lib/WAM/BinaryInfo.js.map +0 -1
  174. package/lib/WAM/constants.d.ts.map +0 -1
  175. package/lib/WAM/constants.js.map +0 -1
  176. package/lib/WAM/encode.d.ts.map +0 -1
  177. package/lib/WAM/encode.js.map +0 -1
  178. package/lib/WAM/index.d.ts.map +0 -1
  179. package/lib/WAM/index.js.map +0 -1
  180. package/lib/WAUSync/Protocols/USyncContactProtocol.d.ts.map +0 -1
  181. package/lib/WAUSync/Protocols/USyncContactProtocol.js.map +0 -1
  182. package/lib/WAUSync/Protocols/USyncDeviceProtocol.d.ts.map +0 -1
  183. package/lib/WAUSync/Protocols/USyncDeviceProtocol.js.map +0 -1
  184. package/lib/WAUSync/Protocols/USyncDisappearingModeProtocol.d.ts.map +0 -1
  185. package/lib/WAUSync/Protocols/USyncDisappearingModeProtocol.js.map +0 -1
  186. package/lib/WAUSync/Protocols/USyncStatusProtocol.d.ts.map +0 -1
  187. package/lib/WAUSync/Protocols/USyncStatusProtocol.js.map +0 -1
  188. package/lib/WAUSync/Protocols/UsyncBotProfileProtocol.d.ts.map +0 -1
  189. package/lib/WAUSync/Protocols/UsyncBotProfileProtocol.js.map +0 -1
  190. package/lib/WAUSync/Protocols/UsyncLIDProtocol.d.ts.map +0 -1
  191. package/lib/WAUSync/Protocols/UsyncLIDProtocol.js.map +0 -1
  192. package/lib/WAUSync/Protocols/index.d.ts.map +0 -1
  193. package/lib/WAUSync/Protocols/index.js.map +0 -1
  194. package/lib/WAUSync/USyncQuery.d.ts.map +0 -1
  195. package/lib/WAUSync/USyncQuery.js.map +0 -1
  196. package/lib/WAUSync/USyncUser.d.ts.map +0 -1
  197. package/lib/WAUSync/USyncUser.js.map +0 -1
  198. package/lib/WAUSync/index.d.ts.map +0 -1
  199. package/lib/WAUSync/index.js.map +0 -1
  200. package/lib/index.d.ts.map +0 -1
  201. package/lib/index.js.map +0 -1
@@ -782,6 +782,7 @@ export const makeMessagesSocket = (config) => {
782
782
  else if (message.contactsArrayMessage) return 'contact_array';
783
783
  else if (message.liveLocationMessage) return 'livelocation';
784
784
  else if (message.stickerMessage) return 'sticker';
785
+ else if (message.lottieStickerMessage) return 'sticker';
785
786
  else if (message.listMessage) return 'list';
786
787
  else if (message.listResponseMessage) return 'list_response';
787
788
  else if (message.buttonsResponseMessage) return 'buttons_response';
@@ -0,0 +1,158 @@
1
+ /**
2
+ * UtilitΓ‘rio para lidar com erros "Bad MAC", comuns em bots WhatsApp usando
3
+ * Baileys quando a sessΓ£o Signal de um par fica desincronizada (ex: o
4
+ * outro lado reinstalou o WhatsApp, trocou de aparelho, ou houve perda de
5
+ * mensagens na troca de chaves). NΓ£o Γ© uma falha do bot β€” Γ© esperado que
6
+ * aconteΓ§a ocasionalmente em qualquer cliente Signal Protocol.
7
+ *
8
+ * Este mΓ³dulo fornece funΓ§Γ΅es para detectar, contar e lidar graciosamente
9
+ * com esses erros, sem derrubar o processo do bot.
10
+ *
11
+ * @author Dev Gui (original), integrado na lib para System Zero. By JosuΓ© </>
12
+ */
13
+
14
+ import fs from 'node:fs';
15
+ import path from 'node:path';
16
+ import { errorLog, warningLog } from './logger.js';
17
+
18
+ export class BadMacHandler {
19
+ constructor(options = {}) {
20
+ this.errorCount = 0;
21
+ this.maxRetries = options.maxRetries ?? 5;
22
+ this.resetInterval = options.resetInterval ?? 300000; // 5 min
23
+ this.lastReset = Date.now();
24
+ // pasta de auth configurΓ‘vel β€” por padrΓ£o segue o padrΓ£o Baileys comum
25
+ this.authFolder = options.authFolder ?? path.resolve(process.cwd(), 'assets', 'auth', 'baileys');
26
+ }
27
+
28
+ isBadMacError(error) {
29
+ const errorMessage = error?.message || error?.toString() || '';
30
+ return (
31
+ errorMessage.includes('Bad MAC') ||
32
+ errorMessage.includes('MAC verification failed') ||
33
+ errorMessage.includes('decryption failed')
34
+ );
35
+ }
36
+
37
+ isSessionError(error) {
38
+ const errorMessage = error?.message || error?.toString() || '';
39
+ return (
40
+ errorMessage.includes('Session') ||
41
+ errorMessage.includes('signal protocol') ||
42
+ errorMessage.includes('decrypt') ||
43
+ this.isBadMacError(error)
44
+ );
45
+ }
46
+
47
+ /**
48
+ * Remove arquivos de sessΓ£o Signal problemΓ‘ticos, preservando SEMPRE as
49
+ * credenciais principais (creds.json, app-state-sync-key/version) β€” sΓ³
50
+ * descarta arquivos de sessΓ£o por par (que serΓ£o renegociados
51
+ * automaticamente na prΓ³xima troca de chaves).
52
+ */
53
+ clearProblematicSessionFiles() {
54
+ try {
55
+ if (!fs.existsSync(this.authFolder)) {
56
+ return false;
57
+ }
58
+
59
+ const PRESERVE_PATTERNS = ['app-state-sync-key', 'creds.json', 'app-state-sync-version'];
60
+ const files = fs.readdirSync(this.authFolder);
61
+ let removedCount = 0;
62
+
63
+ for (const file of files) {
64
+ const filePath = path.join(this.authFolder, file);
65
+ if (!fs.statSync(filePath).isFile()) continue;
66
+
67
+ const mustPreserve = PRESERVE_PATTERNS.some((pattern) => file.includes(pattern));
68
+ if (mustPreserve) continue;
69
+
70
+ // sΓ³ remove arquivos de sessΓ£o por par (ex: session-*.json)
71
+ if (file.startsWith('session-') || file.includes('sender-key')) {
72
+ fs.unlinkSync(filePath);
73
+ removedCount++;
74
+ }
75
+ }
76
+
77
+ if (removedCount > 0) {
78
+ warningLog(`${removedCount} arquivo(s) de sessΓ£o problemΓ‘ticos removidos. Credenciais principais preservadas.`);
79
+ return true;
80
+ }
81
+
82
+ return false;
83
+ } catch (error) {
84
+ errorLog(`Erro ao limpar arquivos de sessΓ£o: ${error.message}`);
85
+ return false;
86
+ }
87
+ }
88
+
89
+ incrementErrorCount() {
90
+ this.errorCount++;
91
+ errorLog(`Bad MAC error count: ${this.errorCount}/${this.maxRetries}`);
92
+
93
+ const now = Date.now();
94
+ if (now - this.lastReset > this.resetInterval) {
95
+ this.resetErrorCount();
96
+ }
97
+ }
98
+
99
+ resetErrorCount() {
100
+ const previousCount = this.errorCount;
101
+ this.errorCount = 0;
102
+ this.lastReset = Date.now();
103
+
104
+ if (previousCount > 0) {
105
+ warningLog(`Reset do contador de Bad MAC errors. Contador anterior: ${previousCount}`);
106
+ }
107
+ }
108
+
109
+ hasReachedLimit() {
110
+ return this.errorCount >= this.maxRetries;
111
+ }
112
+
113
+ /**
114
+ * @returns {boolean} true se o erro foi tratado (Bad MAC engolido),
115
+ * false se NÃO era Bad MAC (deve ser relançado pelo caller)
116
+ */
117
+ handleError(error, context = 'unknown') {
118
+ if (!this.isBadMacError(error)) {
119
+ return false;
120
+ }
121
+
122
+ errorLog(`Bad MAC error detectado em ${context}: ${error.message}`);
123
+ this.incrementErrorCount();
124
+
125
+ if (this.hasReachedLimit()) {
126
+ warningLog(`Limite de Bad MAC errors atingido (${this.maxRetries}). Considere reiniciar o bot ou limpar sessΓ΅es com clearProblematicSessionFiles().`);
127
+ return true;
128
+ }
129
+
130
+ warningLog(`Ignorando Bad MAC error e continuando operaΓ§Γ£o... (${this.errorCount}/${this.maxRetries})`);
131
+ return true;
132
+ }
133
+
134
+ /** Envolve uma funΓ§Γ£o async β€” engole Bad MAC, relanΓ§a qualquer outro erro. */
135
+ createSafeWrapper(fn, context) {
136
+ return async (...args) => {
137
+ try {
138
+ return await fn(...args);
139
+ } catch (error) {
140
+ if (this.handleError(error, context)) {
141
+ return null;
142
+ }
143
+ throw error;
144
+ }
145
+ };
146
+ }
147
+
148
+ getStats() {
149
+ return {
150
+ errorCount: this.errorCount,
151
+ maxRetries: this.maxRetries,
152
+ lastReset: new Date(this.lastReset).toISOString(),
153
+ timeUntilReset: Math.max(0, this.resetInterval - (Date.now() - this.lastReset)),
154
+ };
155
+ }
156
+ }
157
+
158
+ export const badMacHandler = new BadMacHandler();
@@ -0,0 +1,41 @@
1
+ // lib/Utils/get-best-version.js
2
+ //
3
+ // fetchLatestWaWebVersion() e fetchLatestBaileysVersion() SEMPRE retornam um
4
+ // objeto com `version` preenchido, mesmo quando a busca falha β€” nesse caso
5
+ // devolvem isLatest:false e um valor antigo fixo embutido na lib. Checar sΓ³
6
+ // `if (result.version)` nunca detecta a falha, porque version sempre existe.
7
+ //
8
+ // Este helper tenta as duas fontes, na ordem, e sΓ³ aceita um resultado se
9
+ // `isLatest === true` (ou seja, se realmente buscou fresco da rede). SΓ³ cai
10
+ // no valor fixo antigo se AMBAS as fontes falharem de verdade.
11
+
12
+ import { fetchLatestWaWebVersion } from './generics.js';
13
+ import { fetchLatestBaileysVersion } from './generics.js';
14
+
15
+ /**
16
+ * @param {object} [options] - passado direto pras duas funΓ§Γ΅es de fetch
17
+ * @returns {Promise<{ version: number[], isLatest: boolean, source: string }>}
18
+ */
19
+ export const getBestWaVersion = async (options = {}) => {
20
+ try {
21
+ const web = await fetchLatestWaWebVersion(options);
22
+ if (web?.isLatest && web?.version) {
23
+ return { version: web.version, isLatest: true, source: 'web.whatsapp.com' };
24
+ }
25
+ } catch {
26
+ // segue pra prΓ³xima fonte
27
+ }
28
+
29
+ try {
30
+ const github = await fetchLatestBaileysVersion(options);
31
+ if (github?.isLatest && github?.version) {
32
+ return { version: github.version, isLatest: true, source: 'github' };
33
+ }
34
+ } catch {
35
+ // segue pro fallback fixo
36
+ }
37
+
38
+ // as duas fontes falharam de verdade β€” usa o valor fixo embutido,
39
+ // mas avisa explicitamente que NÃO é a versão mais atual
40
+ return { version: [2, 3000, 1027934701], isLatest: false, source: 'fallback-fixo' };
41
+ };
@@ -0,0 +1 @@
1
+ export declare const getName: (msg: any, contactStore?: Record<string, { name?: string; notify?: string; verifiedName?: string }>) => string;
@@ -0,0 +1,54 @@
1
+ // lib/Utils/get-name.js
2
+ // Resolve o nome do remetente com cobertura total (nunca retorna vazio).
3
+ // Ordem de prioridade:
4
+ // 1) Nome salvo localmente (store/contatos) β€” mais confiΓ‘vel
5
+ // 2) verifiedName (conta business confirmada pela Meta)
6
+ // 3) notify / pushName da prΓ³pria mensagem
7
+ // 4) Nome resolvido via cache LID -> JID (se sender for @lid)
8
+ // 5) NΓΊmero de telefone formatado (fallback final, nunca undefined)
9
+
10
+ import { jidDecode, isLidUser, sharedLidPhoneCache, lidToJid } from '../WABinary/jid-utils.js';
11
+
12
+ /**
13
+ * @param {object} msg - mensagem completa do Baileys (proto.IWebMessageInfo)
14
+ * @param {object} [contactStore] - opcional: { [jid]: { name, notify, verifiedName } }
15
+ * @returns {string} nome final, garantido nΓ£o-vazio
16
+ */
17
+ export const getName = (msg, contactStore) => {
18
+ try {
19
+ const key = msg?.key || {};
20
+ let sender = key.participant || key.remoteJid || '';
21
+
22
+ // se vier @lid, tenta resolver pro JID real (telefone) primeiro,
23
+ // pois o contactStore normalmente Γ© indexado por @s.whatsapp.net
24
+ let resolvedSender = sender;
25
+ if (isLidUser(sender)) {
26
+ const phone = sharedLidPhoneCache.getPhoneForLid(sender) || lidToJid(sender);
27
+ if (phone && phone !== sender) resolvedSender = phone;
28
+ }
29
+
30
+ // 1) contato salvo localmente
31
+ const contact = contactStore?.[resolvedSender] || contactStore?.[sender];
32
+ if (contact?.name && contact.name.trim()) return contact.name.trim();
33
+
34
+ // 2) verifiedName (conta business)
35
+ if (contact?.verifiedName && contact.verifiedName.trim()) return contact.verifiedName.trim();
36
+
37
+ // 3) pushName / notify da mensagem em si
38
+ const pushName = msg?.pushName || contact?.notify;
39
+ if (pushName && pushName.trim() && pushName.trim() !== 'WhatsApp User') {
40
+ return pushName.trim();
41
+ }
42
+
43
+ // 4) fallback: nΓΊmero de telefone formatado a partir do JID resolvido
44
+ const decoded = jidDecode(resolvedSender);
45
+ if (decoded?.user) {
46
+ return `+${decoded.user}`;
47
+ }
48
+
49
+ // 5) ΓΊltimo recurso absoluto β€” nunca undefined/empty
50
+ return 'UsuΓ‘rio desconhecido';
51
+ } catch {
52
+ return 'UsuΓ‘rio desconhecido';
53
+ }
54
+ };
@@ -0,0 +1,113 @@
1
+ /**
2
+ * DetecΓ§Γ£o robusta de mensagens de status-em-grupo (marcaΓ§Γ£o de status no grupo).
3
+ *
4
+ * Cobre:
5
+ * - groupStatusMentionMessage / groupStatusMessage / groupStatusMessageV2
6
+ * - isGroupStatus: true | 1 em qualquer nΓ­vel
7
+ * - statusAttributions com type GROUP_STATUS / type 5 / groupStatus: true
8
+ * - mensagens aninhadas em wrappers (ephemeral, viewOnce, etc)
9
+ *
10
+ * @author Dev Gui (original), integrado na lib para System Zero. By JosuΓ© </>
11
+ */
12
+
13
+ const MAX_MESSAGE_UNWRAP_DEPTH = 5;
14
+ const MAX_FLAG_SCAN_DEPTH = 8;
15
+
16
+ const STRONG_GROUP_STATUS_MESSAGE_KEYS = new Set([
17
+ 'groupStatusMentionMessage',
18
+ 'groupStatusMessage',
19
+ 'groupStatusMessageV2',
20
+ ]);
21
+
22
+ const WRAPPED_MESSAGE_KEYS = [
23
+ 'ephemeralMessage',
24
+ 'viewOnceMessage',
25
+ 'viewOnceMessageV2',
26
+ 'viewOnceMessageV2Extension',
27
+ 'documentWithCaptionMessage',
28
+ 'statusMentionMessage',
29
+ 'groupStatusMentionMessage',
30
+ 'groupStatusMessage',
31
+ 'groupStatusMessageV2',
32
+ ];
33
+
34
+ export function isGroupStatusValue(value) {
35
+ return value === true || value === 1;
36
+ }
37
+
38
+ function canScanObject(value) {
39
+ return value && typeof value === 'object' && !(value instanceof ArrayBuffer) && !ArrayBuffer.isView(value);
40
+ }
41
+
42
+ function hasGroupStatusAttribution(statusAttributions) {
43
+ if (!Array.isArray(statusAttributions)) return false;
44
+
45
+ return statusAttributions.some(
46
+ (sa) =>
47
+ canScanObject(sa) &&
48
+ (Boolean(sa.groupStatus) || sa.attributionData === 'groupStatus' || sa.type === 'GROUP_STATUS' || sa.type === 5)
49
+ );
50
+ }
51
+
52
+ function hasGroupStatusContextInfo(contextInfo) {
53
+ return (
54
+ canScanObject(contextInfo) &&
55
+ (isGroupStatusValue(contextInfo.isGroupStatus) || hasGroupStatusAttribution(contextInfo.statusAttributions))
56
+ );
57
+ }
58
+
59
+ export function hasGroupStatusFlag(value, depth = 0, seenObjects = new WeakSet()) {
60
+ if (!canScanObject(value) || depth > MAX_FLAG_SCAN_DEPTH || seenObjects.has(value)) {
61
+ return false;
62
+ }
63
+
64
+ seenObjects.add(value);
65
+
66
+ for (const [key, childValue] of Object.entries(value)) {
67
+ if (key === 'quotedMessage') continue;
68
+
69
+ if (STRONG_GROUP_STATUS_MESSAGE_KEYS.has(key) && canScanObject(childValue)) {
70
+ return true;
71
+ }
72
+
73
+ if (key === 'isGroupStatus' && isGroupStatusValue(childValue)) {
74
+ return true;
75
+ }
76
+
77
+ if (key === 'statusAttributions' && hasGroupStatusAttribution(childValue)) {
78
+ return true;
79
+ }
80
+
81
+ if (key === 'contextInfo' && hasGroupStatusContextInfo(childValue)) {
82
+ return true;
83
+ }
84
+
85
+ if (hasGroupStatusFlag(childValue, depth + 1, seenObjects)) {
86
+ return true;
87
+ }
88
+ }
89
+
90
+ return false;
91
+ }
92
+
93
+ export function getCurrentMessageContentVariants(message, depth = 0, seenObjects = new WeakSet()) {
94
+ if (!canScanObject(message) || depth > MAX_MESSAGE_UNWRAP_DEPTH || seenObjects.has(message)) {
95
+ return [];
96
+ }
97
+
98
+ seenObjects.add(message);
99
+
100
+ const variants = [message];
101
+
102
+ for (const key of WRAPPED_MESSAGE_KEYS) {
103
+ const wrappedMessage = message[key]?.message;
104
+ variants.push(...getCurrentMessageContentVariants(wrappedMessage, depth + 1, seenObjects));
105
+ }
106
+
107
+ return variants;
108
+ }
109
+
110
+ /** Verifica se a webMessage Γ© uma marcaΓ§Γ£o de status em grupo, inclusive em wrappers ou nΓ­veis profundos. */
111
+ export function hasGroupStatusMessage(webMessage) {
112
+ return getCurrentMessageContentVariants(webMessage?.message).some((message) => hasGroupStatusFlag(message));
113
+ }
@@ -16,4 +16,11 @@ export * from './event-buffer.js';
16
16
  export * from './process-message.js';
17
17
  export * from './message-retry-manager.js';
18
18
  export * from './browser-utils.js';
19
+ export * from './get-name.js';
20
+ export * from './payment-guard.js';
21
+ export * from './payment-detection.js';
22
+ export * from './group-status-detection.js';
23
+ export * from './bad-mac-handler.js';
24
+ export * from './resolve-lid-phone.js';
25
+ export * from './get-best-version.js';
19
26
  //# sourceMappingURL=index.js.map
@@ -1,3 +1,6 @@
1
1
  import P from 'pino';
2
- export default P({ timestamp: () => `,"time":"${new Date().toJSON()}"` });
2
+ const logger = P({ timestamp: () => `,"time":"${new Date().toJSON()}"` });
3
+ export const errorLog = (msg) => logger.error(msg);
4
+ export const warningLog = (msg) => logger.warn(msg);
5
+ export default logger;
3
6
  //# sourceMappingURL=logger.js.map
@@ -7,12 +7,50 @@ import { tmpdir } from 'os';
7
7
  import { join } from 'path';
8
8
  import { Readable, Transform } from 'stream';
9
9
  import { URL } from 'url';
10
+ import { promisify } from 'util';
10
11
  import { proto } from '../../WAProto/index.js';
11
12
  import { DEFAULT_ORIGIN, MEDIA_HKDF_KEY_MAPPING, MEDIA_PATH_MAP } from '../Defaults/index.js';
12
13
  import { getBinaryNodeChild, getBinaryNodeChildBuffer, jidNormalizedUser } from '../WABinary/index.js';
13
14
  import { aesDecryptGCM, aesEncryptGCM, hkdf } from './crypto.js';
14
15
  import { generateMessageIDV2 } from './generics.js';
15
16
  const getTmpFilesDirectory = () => tmpdir();
17
+ const execAsync = promisify(exec);
18
+
19
+ /**
20
+ * Converte QUALQUER Γ‘udio de entrada pra opus/ogg mono 16kHz β€” o formato
21
+ * exato que o player de PTT do WhatsApp espera. Sem isso, marcar `ptt: true`
22
+ * num mp3/m4a sΓ³ troca o RΓ“TULO do mimetype sem mudar os bytes reais, e o
23
+ * WhatsApp mostra "algo errado com o arquivo de Γ‘udio" ao tentar decodificar.
24
+ *
25
+ * @param {string | Buffer} input - caminho de arquivo local, URL http(s), ou Buffer jΓ‘ em memΓ³ria
26
+ * @returns {Promise<string>} caminho do arquivo .ogg convertido (temporΓ‘rio β€” chame fs.unlink depois de usar)
27
+ */
28
+ export async function transcodeAudioToOpus(input) {
29
+ const outputPath = join(getTmpFilesDirectory(), 'ptt-' + generateMessageIDV2() + '.ogg');
30
+ let inputPath = input;
31
+ let tempInputPath;
32
+
33
+ try {
34
+ if (Buffer.isBuffer(input)) {
35
+ tempInputPath = join(getTmpFilesDirectory(), 'ptt-src-' + generateMessageIDV2());
36
+ await fs.writeFile(tempInputPath, input);
37
+ inputPath = tempInputPath;
38
+ }
39
+
40
+ // -vn: ignora qualquer vΓ­deo/capa embutida (mp3 com artwork, por exemplo)
41
+ // -ac 1 -ar 16000: mono 16kHz β€” espec exata do PTT nativo do WhatsApp
42
+ // -c:a libopus -b:a 32k: codec real opus, nΓ£o sΓ³ o rΓ³tulo
43
+ const cmd = `ffmpeg -i "${inputPath}" -y -vn -ac 1 -ar 16000 -c:a libopus -b:a 32k -f ogg "${outputPath}"`;
44
+ await execAsync(cmd);
45
+
46
+ return outputPath;
47
+ } finally {
48
+ if (tempInputPath) {
49
+ await fs.unlink(tempInputPath).catch(() => {});
50
+ }
51
+ }
52
+ }
53
+
16
54
  export const getImageProcessingLibrary = async () => {
17
55
  //@ts-ignore
18
56
  const [jimp, sharp] = await Promise.all([import('jimp').catch(() => { }), import('sharp').catch(() => { })]);
@@ -8,7 +8,7 @@ import { WAMessageStatus, WAProto, ButtonHeaderType, ButtonType, CarouselCardTyp
8
8
  import { isJidGroup, isJidNewsletter, isJidStatusBroadcast, jidNormalizedUser, isLidUser, isPnUser } from '../WABinary/index.js';
9
9
  import { sha256 } from './crypto.js';
10
10
  import { generateMessageIDV2, getKeyAuthor, unixTimestampSeconds } from './generics.js';
11
- import { downloadContentFromMessage, encryptedStream, generateThumbnail, getAudioDuration, getAudioWaveform, getRawMediaUploadData, getStream, toBuffer, getImageProcessingLibrary } from './messages-media.js';
11
+ import { downloadContentFromMessage, encryptedStream, generateThumbnail, getAudioDuration, getAudioWaveform, getRawMediaUploadData, getStream, toBuffer, getImageProcessingLibrary, transcodeAudioToOpus } from './messages-media.js';
12
12
  const MIMETYPE_MAP = {
13
13
  image: 'image/jpeg',
14
14
  video: 'video/mp4',
@@ -77,9 +77,32 @@ export const prepareWAMessageMedia = async (message, options) => {
77
77
  }
78
78
  if (!uploadData.mimetype) {
79
79
  uploadData.mimetype = MIMETYPE_MAP[mediaType];
80
- } else if (mediaType === 'audio' && uploadData.mimetype === 'audio/ogg; codecs=opus') {
81
- uploadData.mimetype = 'audio/mpeg';
82
80
  }
81
+ // ptt: true β†’ transcodifica de verdade pra opus/ogg mono 16kHz.
82
+ // Antes, isso sΓ³ trocava o RΓ“TULO do mimetype sem mudar os bytes reais,
83
+ // o que fazia o WhatsApp mostrar "algo errado com o arquivo de Γ‘udio"
84
+ // pra qualquer entrada que nΓ£o jΓ‘ fosse opus puro.
85
+ //
86
+ // IMPORTANTE: nΓ£o confia no `mimetype` que o CALLER declarou (a pessoa
87
+ // pode escrever 'audio/ogg' mesmo passando um mp3/m4a de origem) β€” sΓ³
88
+ // confia na extensΓ£o real do arquivo/URL de origem.
89
+ let pttTranscodedPath;
90
+ if (mediaType === 'audio' && uploadData.ptt === true) {
91
+ const mediaUrl = typeof uploadData.media === 'string'
92
+ ? uploadData.media
93
+ : uploadData.media?.url?.toString?.() || '';
94
+ const sourceLooksLikeOpus = /\.(opus|ogg)(\?|$)/i.test(mediaUrl);
95
+
96
+ if (!sourceLooksLikeOpus) {
97
+ const sourceForTranscode = Buffer.isBuffer(uploadData.media)
98
+ ? uploadData.media
99
+ : (typeof uploadData.media === 'string' ? uploadData.media : mediaUrl);
100
+ pttTranscodedPath = await transcodeAudioToOpus(sourceForTranscode);
101
+ uploadData.media = pttTranscodedPath;
102
+ }
103
+ uploadData.mimetype = 'audio/ogg; codecs=opus';
104
+ }
105
+
83
106
  if (cacheableKey) {
84
107
  const mediaBuff = await options.mediaCache.get(cacheableKey);
85
108
  if (mediaBuff) {
@@ -118,6 +141,10 @@ if (!uploadData.mimetype) {
118
141
  if (obj.stickerMessage) {
119
142
  obj.stickerMessage.stickerSentTs = Date.now();
120
143
  }
144
+ // lottie: stickerSentTs tambΓ©m no stickerMessage interno
145
+ if (obj.lottieStickerMessage?.message?.stickerMessage) {
146
+ obj.lottieStickerMessage.message.stickerMessage.stickerSentTs = Date.now();
147
+ }
121
148
  if (cacheableKey) {
122
149
  logger?.debug({ cacheableKey }, 'set cache');
123
150
  await options.mediaCache.set(cacheableKey, WAProto.Message.encode(obj).finish());
@@ -126,10 +153,10 @@ if (!uploadData.mimetype) {
126
153
  }
127
154
  const requiresDurationComputation = mediaType === 'audio' && typeof uploadData.seconds === 'undefined';
128
155
  const requiresThumbnailComputation = (mediaType === 'image' || mediaType === 'video') && typeof uploadData['jpegThumbnail'] === 'undefined';
129
- const requiresWaveformProcessing = mediaType === 'audio' && uploadData.ptt === true;
156
+ const requiresWaveformProcessing = mediaType === 'audio' && uploadData.ptt === true && typeof uploadData.waveform === 'undefined';
130
157
  const requiresPlaybackSpeed = mediaType === 'audio' && uploadData.ptt === true;
131
158
  const requiresAudioBackground = options.backgroundColor && mediaType === 'audio' && uploadData.ptt === true;
132
- const requiresOriginalForSomeProcessing = requiresDurationComputation || requiresThumbnailComputation;
159
+ const requiresOriginalForSomeProcessing = requiresDurationComputation || requiresThumbnailComputation || requiresWaveformProcessing;
133
160
  const { mediaKey, encFilePath, originalFilePath, fileEncSha256, fileSha256, fileLength } = await encryptedStream(uploadData.media, options.mediaTypeOverride || mediaType, {
134
161
  logger,
135
162
  saveOriginalFileIfRequired: requiresOriginalForSomeProcessing,
@@ -188,6 +215,9 @@ if (!uploadData.mimetype) {
188
215
  if (originalFilePath) {
189
216
  await fs.unlink(originalFilePath);
190
217
  }
218
+ if (pttTranscodedPath) {
219
+ await fs.unlink(pttTranscodedPath);
220
+ }
191
221
  logger?.debug('removed tmp files');
192
222
  }
193
223
  catch (error) {
@@ -472,6 +502,70 @@ const prepareNativeFlowButtons = (message) => {
472
502
  } else if (hasOptionalProperty(button, 'sections') && !!button.sections) {
473
503
  return { name: 'single_select', buttonParamsJson: JSON.stringify({ title: buttonText || 'πŸ“‹ Select', sections: button.sections, icon: buttonIcon }) };
474
504
  }
505
+ // ── BotΓ΅es estendidos (vanzxy) ──────────────────────────────────────
506
+ else if (hasOptionalProperty(button, 'reminder') && !!button.reminder) {
507
+ return { name: 'cta_reminder', buttonParamsJson: JSON.stringify({ display_text: buttonText || '⏰ Remind me', id: button.id || button.reminder, icon: buttonIcon }) };
508
+ }
509
+ else if (hasOptionalProperty(button, 'cancelReminder') && !!button.cancelReminder) {
510
+ return { name: 'cta_cancel_reminder', buttonParamsJson: JSON.stringify({ display_text: buttonText || 'πŸ”• Cancel reminder', id: button.id || button.cancelReminder, icon: buttonIcon }) };
511
+ }
512
+ else if (hasOptionalProperty(button, 'address') && !!button.address) {
513
+ return { name: 'address_message', buttonParamsJson: JSON.stringify({ display_text: buttonText || 'πŸ“ Send address', id: button.id || 'address_message' }) };
514
+ }
515
+ else if (hasOptionalProperty(button, 'location') && button.location === true) {
516
+ return { name: 'send_location', buttonParamsJson: JSON.stringify({}) };
517
+ }
518
+ else if (hasOptionalProperty(button, 'catalog') && !!button.catalog) {
519
+ return { name: 'catalog_message', buttonParamsJson: JSON.stringify({ business_phone_number: button.catalog.bizJid || button.catalog, display_text: buttonText || 'πŸ›οΈ View catalog', id: button.id || 'catalog_message' }) };
520
+ }
521
+ else if (hasOptionalProperty(button, 'products') && !!button.products) {
522
+ return { name: 'mpm', buttonParamsJson: JSON.stringify({ business_phone_number: button.bizJid || button.products.bizJid, product_ids: Array.isArray(button.products) ? button.products : button.products.ids }) };
523
+ }
524
+ else if (hasOptionalProperty(button, 'otp') && !!button.otp) {
525
+ return { name: 'otp_button', buttonParamsJson: JSON.stringify({ display_text: buttonText || 'Copy code', code: button.otp }) };
526
+ }
527
+ else if (hasOptionalProperty(button, 'oneTapOtp') && !!button.oneTapOtp) {
528
+ return { name: 'authentication_button', buttonParamsJson: JSON.stringify({ display_text: buttonText || 'Autofill', code: button.oneTapOtp }) };
529
+ }
530
+ else if (hasOptionalProperty(button, 'phoneNumber') && !!button.phoneNumber) {
531
+ return { name: 'call_button', buttonParamsJson: JSON.stringify({ display_text: buttonText || 'πŸ“ž Call', phone_number: button.phoneNumber }) };
532
+ }
533
+ else if (hasOptionalProperty(button, 'urlBtn') && !!button.urlBtn) {
534
+ return { name: 'url_button', buttonParamsJson: JSON.stringify({ display_text: buttonText || '🌐 Open', url: button.urlBtn }) };
535
+ }
536
+ else if (hasOptionalProperty(button, 'card') && !!button.card) {
537
+ return { name: 'card_message', buttonParamsJson: JSON.stringify(button.card) };
538
+ }
539
+ else if (hasOptionalProperty(button, 'orderDetails') && !!button.orderDetails) {
540
+ return { name: 'order_details', buttonParamsJson: JSON.stringify(button.orderDetails) };
541
+ }
542
+ else if (hasOptionalProperty(button, 'orderStatus') && !!button.orderStatus) {
543
+ return { name: 'order_status', buttonParamsJson: JSON.stringify(button.orderStatus) };
544
+ }
545
+ else if (hasOptionalProperty(button, 'trackOrder') && !!button.trackOrder) {
546
+ return { name: 'track_order', buttonParamsJson: JSON.stringify({ id: button.id || button.trackOrder, display_text: buttonText || '🚚 Track order' }) };
547
+ }
548
+ else if (hasOptionalProperty(button, 'reorder') && !!button.reorder) {
549
+ return { name: 'reorder', buttonParamsJson: JSON.stringify({ id: button.id || button.reorder, display_text: buttonText || 'πŸ” Reorder' }) };
550
+ }
551
+ else if (hasOptionalProperty(button, 'cancelOrder') && !!button.cancelOrder) {
552
+ return { name: 'cancel_order', buttonParamsJson: JSON.stringify({ id: button.id || button.cancelOrder, display_text: buttonText || '❌ Cancel order' }) };
553
+ }
554
+ else if (hasOptionalProperty(button, 'clearChat') && button.clearChat === true) {
555
+ return { name: 'clear_chat', buttonParamsJson: JSON.stringify({}) };
556
+ }
557
+ else if (hasOptionalProperty(button, 'screen') && !!button.screen) {
558
+ return { name: 'navigateToScreen', buttonParamsJson: JSON.stringify({ screen_name: button.screen, data: button.data || {} }) };
559
+ }
560
+ else if (hasOptionalProperty(button, 'flow') && !!button.flow) {
561
+ return { name: 'flow_action', buttonParamsJson: JSON.stringify({ flow_message_version: button.flow.version || '3', flow_id: button.flow.id, flow_cta: buttonText || button.flow.cta || 'Continue', flow_action: button.flow.action || 'navigate', flow_action_payload: button.flow.actionPayload || { screen: button.flow.screen || 'WELCOME', data: button.flow.data || {} } }) };
562
+ }
563
+ else if (hasOptionalProperty(button, 'voiceCall') && !!button.voiceCall) {
564
+ return { name: 'voice_call', buttonParamsJson: JSON.stringify({ display_text: buttonText || 'πŸ“ž Voice call', id: button.id || button.voiceCall }) };
565
+ }
566
+ else if (hasOptionalProperty(button, 'videoCall') && !!button.videoCall) {
567
+ return { name: 'video_call_button', buttonParamsJson: JSON.stringify({ display_text: buttonText || 'πŸŽ₯ Video call', id: button.id || button.videoCall }) };
568
+ }
475
569
  return button;
476
570
  }),
477
571
  messageParamsJson: JSON.stringify(messageParamsJson)
@@ -936,7 +1030,12 @@ export const generateWAMessageContent = async (message, options) => {
936
1030
  const key = m[messageType];
937
1031
  if (key && 'contextInfo' in key && !!key.contextInfo) key.contextInfo.isGroupStatus = message.groupStatus;
938
1032
  else if (key) key.contextInfo = { isGroupStatus: message.groupStatus };
939
- m = { groupStatusMessageV2: { message: m } };
1033
+ // Fix: audio PTT usa groupStatusMessage (v1) β€” V2 causa tela preta em versΓ΅es antigas do WA
1034
+ if (messageType === 'audioMessage') {
1035
+ m = { groupStatusMessage: { message: m } };
1036
+ } else {
1037
+ m = { groupStatusMessageV2: { message: m } };
1038
+ }
940
1039
  delete message.groupStatus;
941
1040
  }
942
1041
  // ── spoiler ───────────────────────────────────────────────────────────────
@@ -961,6 +1060,12 @@ export const generateWAMessageContent = async (message, options) => {
961
1060
  }
962
1061
  // ── isLottie ──────────────────────────────────────────────────────────────
963
1062
  if (hasOptionalProperty(message, 'isLottie') && !!message.isLottie) {
1063
+ // marca o stickerMessage interno com isLottie: true para o WA renderizar como premium
1064
+ if (m.stickerMessage) {
1065
+ m.stickerMessage.isLottie = true;
1066
+ m.stickerMessage.isAnimated = m.stickerMessage.isAnimated ?? false;
1067
+ m.stickerMessage.isAiSticker = false;
1068
+ }
964
1069
  m = { lottieStickerMessage: { message: m } };
965
1070
  }
966
1071
  // ── viewOnceV2 / viewOnceV2Extension ──────────────────────────────────────
@@ -1094,7 +1199,8 @@ export const normalizeMessageContent = (content) => {
1094
1199
  message?.documentWithCaptionMessage ||
1095
1200
  message?.viewOnceMessageV2 ||
1096
1201
  message?.viewOnceMessageV2Extension ||
1097
- message?.editedMessage);
1202
+ message?.editedMessage ||
1203
+ message?.lottieStickerMessage);
1098
1204
  }
1099
1205
  };
1100
1206
  export const extractMessageContent = (content) => {