@systemzero/baileys 1.0.4 → 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 (205) hide show
  1. package/README.md +595 -762
  2. package/lib/Defaults/index.js +9 -3
  3. package/lib/MB.cjs +8 -0
  4. package/lib/MessageBuilder.cjs +2509 -0
  5. package/lib/Socket/chats.js +12 -1
  6. package/lib/Socket/groups.js +13 -4
  7. package/lib/Socket/messages-recv.js +36 -2
  8. package/lib/Socket/messages-recv.js.bak +1273 -0
  9. package/lib/Socket/messages-send.js +2 -1
  10. package/lib/Types/Message.js +7 -0
  11. package/lib/Utils/bad-mac-handler.js +158 -0
  12. package/lib/Utils/get-best-version.js +41 -0
  13. package/lib/Utils/get-name.d.ts +1 -0
  14. package/lib/Utils/get-name.js +54 -0
  15. package/lib/Utils/group-status-detection.js +113 -0
  16. package/lib/Utils/index.js +7 -0
  17. package/lib/Utils/logger.js +4 -1
  18. package/lib/Utils/messages-media.js +42 -3
  19. package/lib/Utils/messages.js +641 -10
  20. package/lib/Utils/payment-detection.js +212 -0
  21. package/lib/Utils/payment-guard.d.ts +15 -0
  22. package/lib/Utils/payment-guard.js +142 -0
  23. package/lib/Utils/resolve-lid-phone.js +30 -0
  24. package/lib/WABinary/jid-utils.js +245 -1
  25. package/package.json +4 -3
  26. package/lib/Defaults/index.d.ts.map +0 -1
  27. package/lib/Defaults/index.js.map +0 -1
  28. package/lib/Signal/Group/ciphertext-message.d.ts.map +0 -1
  29. package/lib/Signal/Group/ciphertext-message.js.map +0 -1
  30. package/lib/Signal/Group/group-session-builder.d.ts.map +0 -1
  31. package/lib/Signal/Group/group-session-builder.js.map +0 -1
  32. package/lib/Signal/Group/group_cipher.d.ts.map +0 -1
  33. package/lib/Signal/Group/group_cipher.js.map +0 -1
  34. package/lib/Signal/Group/index.d.ts.map +0 -1
  35. package/lib/Signal/Group/index.js.map +0 -1
  36. package/lib/Signal/Group/keyhelper.d.ts.map +0 -1
  37. package/lib/Signal/Group/keyhelper.js.map +0 -1
  38. package/lib/Signal/Group/sender-chain-key.d.ts.map +0 -1
  39. package/lib/Signal/Group/sender-chain-key.js.map +0 -1
  40. package/lib/Signal/Group/sender-key-distribution-message.d.ts.map +0 -1
  41. package/lib/Signal/Group/sender-key-distribution-message.js.map +0 -1
  42. package/lib/Signal/Group/sender-key-message.d.ts.map +0 -1
  43. package/lib/Signal/Group/sender-key-message.js.map +0 -1
  44. package/lib/Signal/Group/sender-key-name.d.ts.map +0 -1
  45. package/lib/Signal/Group/sender-key-name.js.map +0 -1
  46. package/lib/Signal/Group/sender-key-record.d.ts.map +0 -1
  47. package/lib/Signal/Group/sender-key-record.js.map +0 -1
  48. package/lib/Signal/Group/sender-key-state.d.ts.map +0 -1
  49. package/lib/Signal/Group/sender-key-state.js.map +0 -1
  50. package/lib/Signal/Group/sender-message-key.d.ts.map +0 -1
  51. package/lib/Signal/Group/sender-message-key.js.map +0 -1
  52. package/lib/Signal/libsignal.d.ts.map +0 -1
  53. package/lib/Signal/libsignal.js.map +0 -1
  54. package/lib/Signal/lid-mapping.d.ts.map +0 -1
  55. package/lib/Signal/lid-mapping.js.map +0 -1
  56. package/lib/Socket/Client/index.d.ts.map +0 -1
  57. package/lib/Socket/Client/index.js.map +0 -1
  58. package/lib/Socket/Client/types.d.ts.map +0 -1
  59. package/lib/Socket/Client/types.js.map +0 -1
  60. package/lib/Socket/Client/websocket.d.ts.map +0 -1
  61. package/lib/Socket/Client/websocket.js.map +0 -1
  62. package/lib/Socket/business.d.ts.map +0 -1
  63. package/lib/Socket/business.js.map +0 -1
  64. package/lib/Socket/chats.d.ts.map +0 -1
  65. package/lib/Socket/chats.js.map +0 -1
  66. package/lib/Socket/communities.d.ts.map +0 -1
  67. package/lib/Socket/communities.js.map +0 -1
  68. package/lib/Socket/groups.d.ts.map +0 -1
  69. package/lib/Socket/groups.js.map +0 -1
  70. package/lib/Socket/index.d.ts.map +0 -1
  71. package/lib/Socket/index.js.map +0 -1
  72. package/lib/Socket/messages-recv.d.ts.map +0 -1
  73. package/lib/Socket/messages-recv.js.map +0 -1
  74. package/lib/Socket/messages-send.d.ts.map +0 -1
  75. package/lib/Socket/messages-send.js.map +0 -1
  76. package/lib/Socket/mex.d.ts.map +0 -1
  77. package/lib/Socket/mex.js.map +0 -1
  78. package/lib/Socket/newsletter.d.ts.map +0 -1
  79. package/lib/Socket/newsletter.js.map +0 -1
  80. package/lib/Socket/socket.d.ts.map +0 -1
  81. package/lib/Socket/socket.js.map +0 -1
  82. package/lib/Types/Auth.d.ts.map +0 -1
  83. package/lib/Types/Auth.js.map +0 -1
  84. package/lib/Types/Bussines.d.ts.map +0 -1
  85. package/lib/Types/Bussines.js.map +0 -1
  86. package/lib/Types/Call.d.ts.map +0 -1
  87. package/lib/Types/Call.js.map +0 -1
  88. package/lib/Types/Chat.d.ts.map +0 -1
  89. package/lib/Types/Chat.js.map +0 -1
  90. package/lib/Types/Contact.d.ts.map +0 -1
  91. package/lib/Types/Contact.js.map +0 -1
  92. package/lib/Types/Events.d.ts.map +0 -1
  93. package/lib/Types/Events.js.map +0 -1
  94. package/lib/Types/GroupMetadata.d.ts.map +0 -1
  95. package/lib/Types/GroupMetadata.js.map +0 -1
  96. package/lib/Types/Label.d.ts.map +0 -1
  97. package/lib/Types/Label.js.map +0 -1
  98. package/lib/Types/LabelAssociation.d.ts.map +0 -1
  99. package/lib/Types/LabelAssociation.js.map +0 -1
  100. package/lib/Types/Message.d.ts.map +0 -1
  101. package/lib/Types/Message.js.map +0 -1
  102. package/lib/Types/Newsletter.d.ts.map +0 -1
  103. package/lib/Types/Newsletter.js.map +0 -1
  104. package/lib/Types/Product.d.ts.map +0 -1
  105. package/lib/Types/Product.js.map +0 -1
  106. package/lib/Types/Signal.d.ts.map +0 -1
  107. package/lib/Types/Signal.js.map +0 -1
  108. package/lib/Types/Socket.d.ts.map +0 -1
  109. package/lib/Types/Socket.js.map +0 -1
  110. package/lib/Types/State.d.ts.map +0 -1
  111. package/lib/Types/State.js.map +0 -1
  112. package/lib/Types/USync.d.ts.map +0 -1
  113. package/lib/Types/USync.js.map +0 -1
  114. package/lib/Types/index.d.ts.map +0 -1
  115. package/lib/Types/index.js.map +0 -1
  116. package/lib/Utils/auth-utils.d.ts.map +0 -1
  117. package/lib/Utils/auth-utils.js.map +0 -1
  118. package/lib/Utils/browser-utils.d.ts.map +0 -1
  119. package/lib/Utils/browser-utils.js.map +0 -1
  120. package/lib/Utils/business.d.ts.map +0 -1
  121. package/lib/Utils/business.js.map +0 -1
  122. package/lib/Utils/chat-utils.d.ts.map +0 -1
  123. package/lib/Utils/chat-utils.js.map +0 -1
  124. package/lib/Utils/crypto.d.ts.map +0 -1
  125. package/lib/Utils/crypto.js.map +0 -1
  126. package/lib/Utils/decode-wa-message.d.ts.map +0 -1
  127. package/lib/Utils/decode-wa-message.js.map +0 -1
  128. package/lib/Utils/event-buffer.d.ts.map +0 -1
  129. package/lib/Utils/event-buffer.js.map +0 -1
  130. package/lib/Utils/generics.d.ts.map +0 -1
  131. package/lib/Utils/generics.js.map +0 -1
  132. package/lib/Utils/history.d.ts.map +0 -1
  133. package/lib/Utils/history.js.map +0 -1
  134. package/lib/Utils/index.d.ts.map +0 -1
  135. package/lib/Utils/index.js.map +0 -1
  136. package/lib/Utils/link-preview.d.ts.map +0 -1
  137. package/lib/Utils/link-preview.js.map +0 -1
  138. package/lib/Utils/logger.d.ts.map +0 -1
  139. package/lib/Utils/logger.js.map +0 -1
  140. package/lib/Utils/lt-hash.d.ts.map +0 -1
  141. package/lib/Utils/lt-hash.js.map +0 -1
  142. package/lib/Utils/make-mutex.d.ts.map +0 -1
  143. package/lib/Utils/make-mutex.js.map +0 -1
  144. package/lib/Utils/message-retry-manager.d.ts.map +0 -1
  145. package/lib/Utils/message-retry-manager.js.map +0 -1
  146. package/lib/Utils/messages-media.d.ts.map +0 -1
  147. package/lib/Utils/messages-media.js.map +0 -1
  148. package/lib/Utils/messages.d.ts.map +0 -1
  149. package/lib/Utils/messages.js.map +0 -1
  150. package/lib/Utils/noise-handler.d.ts.map +0 -1
  151. package/lib/Utils/noise-handler.js.map +0 -1
  152. package/lib/Utils/pre-key-manager.d.ts.map +0 -1
  153. package/lib/Utils/pre-key-manager.js.map +0 -1
  154. package/lib/Utils/process-message.d.ts.map +0 -1
  155. package/lib/Utils/process-message.js.map +0 -1
  156. package/lib/Utils/signal.d.ts.map +0 -1
  157. package/lib/Utils/signal.js.map +0 -1
  158. package/lib/Utils/use-multi-file-auth-state.d.ts.map +0 -1
  159. package/lib/Utils/use-multi-file-auth-state.js.map +0 -1
  160. package/lib/Utils/validate-connection.d.ts.map +0 -1
  161. package/lib/Utils/validate-connection.js.map +0 -1
  162. package/lib/WABinary/constants.d.ts.map +0 -1
  163. package/lib/WABinary/constants.js.map +0 -1
  164. package/lib/WABinary/decode.d.ts.map +0 -1
  165. package/lib/WABinary/decode.js.map +0 -1
  166. package/lib/WABinary/encode.d.ts.map +0 -1
  167. package/lib/WABinary/encode.js.map +0 -1
  168. package/lib/WABinary/generic-utils.d.ts.map +0 -1
  169. package/lib/WABinary/generic-utils.js.map +0 -1
  170. package/lib/WABinary/index.d.ts.map +0 -1
  171. package/lib/WABinary/index.js.map +0 -1
  172. package/lib/WABinary/jid-utils.d.ts.map +0 -1
  173. package/lib/WABinary/jid-utils.js.map +0 -1
  174. package/lib/WABinary/types.d.ts.map +0 -1
  175. package/lib/WABinary/types.js.map +0 -1
  176. package/lib/WAM/BinaryInfo.d.ts.map +0 -1
  177. package/lib/WAM/BinaryInfo.js.map +0 -1
  178. package/lib/WAM/constants.d.ts.map +0 -1
  179. package/lib/WAM/constants.js.map +0 -1
  180. package/lib/WAM/encode.d.ts.map +0 -1
  181. package/lib/WAM/encode.js.map +0 -1
  182. package/lib/WAM/index.d.ts.map +0 -1
  183. package/lib/WAM/index.js.map +0 -1
  184. package/lib/WAUSync/Protocols/USyncContactProtocol.d.ts.map +0 -1
  185. package/lib/WAUSync/Protocols/USyncContactProtocol.js.map +0 -1
  186. package/lib/WAUSync/Protocols/USyncDeviceProtocol.d.ts.map +0 -1
  187. package/lib/WAUSync/Protocols/USyncDeviceProtocol.js.map +0 -1
  188. package/lib/WAUSync/Protocols/USyncDisappearingModeProtocol.d.ts.map +0 -1
  189. package/lib/WAUSync/Protocols/USyncDisappearingModeProtocol.js.map +0 -1
  190. package/lib/WAUSync/Protocols/USyncStatusProtocol.d.ts.map +0 -1
  191. package/lib/WAUSync/Protocols/USyncStatusProtocol.js.map +0 -1
  192. package/lib/WAUSync/Protocols/UsyncBotProfileProtocol.d.ts.map +0 -1
  193. package/lib/WAUSync/Protocols/UsyncBotProfileProtocol.js.map +0 -1
  194. package/lib/WAUSync/Protocols/UsyncLIDProtocol.d.ts.map +0 -1
  195. package/lib/WAUSync/Protocols/UsyncLIDProtocol.js.map +0 -1
  196. package/lib/WAUSync/Protocols/index.d.ts.map +0 -1
  197. package/lib/WAUSync/Protocols/index.js.map +0 -1
  198. package/lib/WAUSync/USyncQuery.d.ts.map +0 -1
  199. package/lib/WAUSync/USyncQuery.js.map +0 -1
  200. package/lib/WAUSync/USyncUser.d.ts.map +0 -1
  201. package/lib/WAUSync/USyncUser.js.map +0 -1
  202. package/lib/WAUSync/index.d.ts.map +0 -1
  203. package/lib/WAUSync/index.js.map +0 -1
  204. package/lib/index.d.ts.map +0 -1
  205. package/lib/index.js.map +0 -1
@@ -0,0 +1,212 @@
1
+ /**
2
+ * Detecção de mensagens de pagamento — incluindo cobranças STEALTH
3
+ * (ocultas para admins, indecifráveis pelo bot).
4
+ *
5
+ * @author Dev Gui (original), integrado na lib para System Zero. By Josué </>
6
+ */
7
+
8
+ const MAX_PAYMENT_SCAN_DEPTH = 8;
9
+
10
+ const PAYMENT_MESSAGE_KEYS = new Set([
11
+ 'paymentInviteMessage',
12
+ 'requestPaymentMessage',
13
+ 'sendPaymentMessage',
14
+ 'cancelPaymentRequestMessage',
15
+ 'declinePaymentRequestMessage',
16
+ ]);
17
+
18
+ function canScanObject(value) {
19
+ return value && typeof value === 'object' && !(value instanceof ArrayBuffer) && !ArrayBuffer.isView(value);
20
+ }
21
+
22
+ function hasPaymentMessageKey(value, depth = 0, seenObjects = new WeakSet()) {
23
+ if (!canScanObject(value) || depth > MAX_PAYMENT_SCAN_DEPTH || seenObjects.has(value)) {
24
+ return false;
25
+ }
26
+
27
+ seenObjects.add(value);
28
+
29
+ for (const [key, childValue] of Object.entries(value)) {
30
+ if (key === 'quotedMessage') continue;
31
+
32
+ if (PAYMENT_MESSAGE_KEYS.has(key) && canScanObject(childValue)) {
33
+ return true;
34
+ }
35
+
36
+ if (hasPaymentMessageKey(childValue, depth + 1, seenObjects)) {
37
+ return true;
38
+ }
39
+ }
40
+
41
+ return false;
42
+ }
43
+
44
+ /** Verifica se a webMessage contém uma mensagem de pagamento — visível ou stealth. */
45
+ export function hasPaymentMessage(webMessage) {
46
+ return hasPaymentMessageKey(webMessage?.message);
47
+ }
48
+
49
+ function findQuotedPaymentContext(value, depth = 0, seenObjects = new WeakSet()) {
50
+ if (!canScanObject(value) || depth > MAX_PAYMENT_SCAN_DEPTH || seenObjects.has(value)) {
51
+ return null;
52
+ }
53
+
54
+ seenObjects.add(value);
55
+
56
+ const contextInfo = value.contextInfo;
57
+
58
+ if (
59
+ canScanObject(contextInfo) &&
60
+ typeof contextInfo.participant === 'string' &&
61
+ canScanObject(contextInfo.quotedMessage) &&
62
+ hasPaymentMessageKey(contextInfo.quotedMessage)
63
+ ) {
64
+ return {
65
+ participant: contextInfo.participant,
66
+ stanzaId: typeof contextInfo.stanzaId === 'string' ? contextInfo.stanzaId : undefined,
67
+ };
68
+ }
69
+
70
+ for (const [key, childValue] of Object.entries(value)) {
71
+ if (key === 'quotedMessage') continue;
72
+ const found = findQuotedPaymentContext(childValue, depth + 1, seenObjects);
73
+ if (found) return found;
74
+ }
75
+
76
+ return null;
77
+ }
78
+
79
+ /**
80
+ * Lê a marcação (quoted) de uma mensagem de pagamento — inclusive as enviadas
81
+ * de forma oculta para admins — e devolve o autor original e o id da
82
+ * mensagem citada. Ignora citações aninhadas para não atribuir o pagamento errado.
83
+ */
84
+ export function getQuotedPaymentContext(webMessage) {
85
+ return findQuotedPaymentContext(webMessage?.message);
86
+ }
87
+
88
+ // ─── Registro de envelopes (corroboração anti-forja) ──────────────────────
89
+
90
+ const ENVELOPE_TTL_MS = 60 * 60 * 1000; // 1 hora
91
+ const MAX_ENTRIES_PER_GROUP = 1000;
92
+
93
+ // registry: Map<groupJid, Map<msgId, {participant, contentState, stealth, ts}>>
94
+ const registry = new Map();
95
+
96
+ const NON_CONTENT_KEYS = new Set(['messageContextInfo', 'senderKeyDistributionMessage']);
97
+
98
+ function hasReadableContent(message) {
99
+ if (!message || typeof message !== 'object') return false;
100
+ return Object.keys(message).some((key) => !NON_CONTENT_KEYS.has(key));
101
+ }
102
+
103
+ function pruneGroup(groupMap) {
104
+ const now = Date.now();
105
+
106
+ for (const [id, entry] of groupMap) {
107
+ if (now - entry.ts > ENVELOPE_TTL_MS) {
108
+ groupMap.delete(id);
109
+ }
110
+ }
111
+
112
+ while (groupMap.size > MAX_ENTRIES_PER_GROUP) {
113
+ const oldestKey = groupMap.keys().next().value;
114
+ if (oldestKey === undefined) break;
115
+ groupMap.delete(oldestKey);
116
+ }
117
+ }
118
+
119
+ /**
120
+ * Registra o envelope de uma mensagem de grupo. `isPayment` é calculado pelo
121
+ * chamador (que já roda a detecção de pagamento) para evitar import cíclico.
122
+ */
123
+ export function recordMessageEnvelope(webMessage, isPayment) {
124
+ const remoteJid = webMessage?.key?.remoteJid;
125
+ const id = webMessage?.key?.id;
126
+ const participant = webMessage?.key?.participant;
127
+
128
+ if (!remoteJid?.endsWith('@g.us') || !id || !participant) return;
129
+
130
+ let groupMap = registry.get(remoteJid);
131
+ if (!groupMap) {
132
+ groupMap = new Map();
133
+ registry.set(remoteJid, groupMap);
134
+ }
135
+
136
+ const contentState = isPayment ? 'payment' : hasReadableContent(webMessage?.message) ? 'other' : 'unreadable';
137
+
138
+ groupMap.set(id, {
139
+ participant,
140
+ contentState,
141
+ stealth: Boolean(webMessage?.stealthMeta),
142
+ ts: Date.now(),
143
+ });
144
+
145
+ pruneGroup(groupMap);
146
+ }
147
+
148
+ /**
149
+ * Decide se uma marcação de pagamento é confiável o bastante para punir o autor.
150
+ *
151
+ * - corroborated: o bot recebeu a mensagem original, do MESMO autor, e ela era
152
+ * pagamento OU foi indecifrável (consistente com o truque stealth). Pode punir.
153
+ * - contradicted: o bot recebeu a mensagem, mas de OUTRO autor, ou ela era
154
+ * conteúdo legível que não era pagamento. É forja. Nunca punir.
155
+ * - nenhum dos dois: o bot não viu a mensagem original. Sem corroboração, não pune.
156
+ */
157
+ export function verifyQuotedAuthor({ groupJid, stanzaId, participant }) {
158
+ if (!stanzaId) return { corroborated: false, contradicted: false };
159
+
160
+ const entry = registry.get(groupJid)?.get(stanzaId);
161
+ if (!entry) return { corroborated: false, contradicted: false };
162
+
163
+ if (entry.participant !== participant) {
164
+ return { corroborated: false, contradicted: true };
165
+ }
166
+
167
+ if (entry.contentState === 'other') {
168
+ return { corroborated: false, contradicted: true };
169
+ }
170
+
171
+ return { corroborated: true, contradicted: false };
172
+ }
173
+
174
+ /** Apenas para testes: limpa o registro. */
175
+ export function __clearEnvelopeRegistry() {
176
+ registry.clear();
177
+ }
178
+
179
+ // ─── Extração de valor monetário ───────────────────────────────────────────
180
+
181
+ /**
182
+ * Extrai o valor monetário de um requestPaymentMessage / sendPaymentMessage.
183
+ * Retorna { value, currencyCode } ou null se não encontrar.
184
+ */
185
+ export function extractPaymentAmount(webMessage) {
186
+ try {
187
+ const msg =
188
+ webMessage?.message?.requestPaymentMessage ||
189
+ webMessage?.message?.sendPaymentMessage ||
190
+ webMessage?.message?.paymentInviteMessage;
191
+
192
+ if (!msg) return null;
193
+
194
+ if (msg.amount && msg.amount.value !== undefined) {
195
+ const raw = typeof msg.amount.value === 'object' ? msg.amount.value.low ?? 0 : Number(msg.amount.value);
196
+ const offset = msg.amount.offset || 100;
197
+ return {
198
+ value: raw / offset,
199
+ currencyCode: msg.amount.currencyCode || msg.currencyCodeIso4217 || '???',
200
+ };
201
+ }
202
+
203
+ if (msg.amount1000 !== undefined) {
204
+ const raw = typeof msg.amount1000 === 'object' ? msg.amount1000.low ?? 0 : Number(msg.amount1000);
205
+ return { value: raw / 1000, currencyCode: msg.currencyCodeIso4217 || '???' };
206
+ }
207
+
208
+ return null;
209
+ } catch {
210
+ return null;
211
+ }
212
+ }
@@ -0,0 +1,15 @@
1
+ export interface PaymentGuardHooks {
2
+ isPaymentMessage: (webMessage: any) => boolean;
3
+ recordEnvelope?: (webMessage: any, isPayment: boolean) => void;
4
+ onDetect: (detection: {
5
+ type: 'direct' | 'edited' | 'undecryptable';
6
+ remoteJid: string;
7
+ participant?: string;
8
+ messageId?: string;
9
+ isPreKeyError?: boolean;
10
+ errorMessage?: string;
11
+ }) => void;
12
+ treatDecryptFailureAsSuspicious?: boolean;
13
+ }
14
+
15
+ export declare const bindPaymentGuard: (sock: any, hooks: PaymentGuardHooks) => () => void;
@@ -0,0 +1,142 @@
1
+ // lib/Utils/payment-guard.js
2
+ //
3
+ // Encanamento genérico para detecção de mensagens de pagamento "stealth"
4
+ // (ocultas/editadas). A LIB não decide o que é payment — isso é lógica do
5
+ // seu bot. Aqui só garantimos que NENHUM caminho de entrada fica sem cobrir:
6
+ //
7
+ // 1) Mensagem direta normal -> 'messages.upsert'
8
+ // 2) Mensagem EDITADA depois -> 'messages.update' (protocolMessage MESSAGE_EDIT)
9
+ // 3) Mensagem que falhou decrypt -> 'messages.decrypt-failed' (emitido pela lib)
10
+ //
11
+ // Limite real (sem prometer o impossível): uma mensagem que falha no decrypt
12
+ // (PreKey/CIPHERTEXT) nunca foi lida pelo bot — não existe forma de "ver" que
13
+ // é payment. O que dá pra garantir é que isso nunca passe em silêncio: o
14
+ // evento é sempre emitido, e cabe ao seu detector decidir o que fazer com a
15
+ // falta de conteúdo (ex: tratar como suspeito, igual ao 'unreadable' que você
16
+ // já usa no seu messageEnvelopeRegistry).
17
+ //
18
+ // Você injeta sua própria lógica (paymentMessage.js, messageEnvelopeRegistry.js
19
+ // etc. do seu bot) via callbacks — a lib não importa nada do seu projeto.
20
+
21
+ /**
22
+ * @typedef {Object} PaymentGuardHooks
23
+ * @property {(webMessage: any) => boolean} isPaymentMessage
24
+ * Função que decide se um webMessage (ou webMessage sintético de edição)
25
+ * é uma mensagem de pagamento. Normalmente seu `hasPaymentMessage`.
26
+ * @property {(webMessage: any, isPayment: boolean) => void} [recordEnvelope]
27
+ * Chamado para TODA mensagem de grupo vista (payment ou não), inclusive
28
+ * falhas de decrypt (com `webMessage.message === null`). Normalmente seu
29
+ * `recordMessageEnvelope`.
30
+ * @property {(detection: PaymentDetection) => void} onDetect
31
+ * Chamado sempre que uma detecção (direta, editada ou indecifrável
32
+ * suspeita) acontece.
33
+ * @property {boolean} [treatDecryptFailureAsSuspicious=true]
34
+ */
35
+
36
+ /**
37
+ * @typedef {Object} PaymentDetection
38
+ * @property {'direct'|'edited'|'undecryptable'} type
39
+ * @property {string} remoteJid
40
+ * @property {string|undefined} participant
41
+ * @property {string|undefined} messageId
42
+ * @property {boolean} [isPreKeyError]
43
+ * @property {string} [errorMessage]
44
+ */
45
+
46
+ const extractEditedContent = (updateEntry) => updateEntry?.update?.message?.editedMessage?.message || null;
47
+
48
+ /**
49
+ * Liga o guard completo num socket Baileys. Chame uma vez, logo após criar o sock.
50
+ *
51
+ * @param {import('../Socket/index.js').WASocket} sock
52
+ * @param {PaymentGuardHooks} hooks
53
+ * @returns {() => void} função de cleanup que remove todos os listeners
54
+ */
55
+ export const bindPaymentGuard = (sock, hooks) => {
56
+ const {
57
+ isPaymentMessage,
58
+ recordEnvelope = () => {},
59
+ onDetect = () => {},
60
+ treatDecryptFailureAsSuspicious = true,
61
+ } = hooks || {};
62
+
63
+ if (typeof isPaymentMessage !== 'function') {
64
+ throw new Error('bindPaymentGuard: hooks.isPaymentMessage é obrigatório');
65
+ }
66
+
67
+ // ── 1) Mensagens diretas ────────────────────────────────────────────────
68
+ const onUpsert = ({ messages }) => {
69
+ for (const webMessage of messages || []) {
70
+ const remoteJid = webMessage?.key?.remoteJid;
71
+ if (!remoteJid?.endsWith('@g.us')) continue;
72
+
73
+ const isPayment = isPaymentMessage(webMessage);
74
+ recordEnvelope(webMessage, isPayment);
75
+
76
+ if (isPayment) {
77
+ onDetect({
78
+ type: 'direct',
79
+ remoteJid,
80
+ participant: webMessage.key?.participant,
81
+ messageId: webMessage.key?.id,
82
+ });
83
+ }
84
+ }
85
+ };
86
+
87
+ // ── 2) Mensagens editadas (golpe de edição pós-envio) ───────────────────
88
+ const onUpdate = (updates) => {
89
+ for (const u of updates || []) {
90
+ const editedContent = extractEditedContent(u);
91
+ if (!editedContent) continue;
92
+
93
+ const remoteJid = u.key?.remoteJid;
94
+ if (!remoteJid?.endsWith('@g.us')) continue;
95
+
96
+ const syntheticWebMessage = { message: editedContent, key: u.key };
97
+ const isPayment = isPaymentMessage(syntheticWebMessage);
98
+ recordEnvelope(syntheticWebMessage, isPayment);
99
+
100
+ if (isPayment) {
101
+ onDetect({
102
+ type: 'edited',
103
+ remoteJid,
104
+ participant: u.key?.participant,
105
+ messageId: u.key?.id,
106
+ });
107
+ }
108
+ }
109
+ };
110
+
111
+ // ── 3) Falha de decrypt (PreKey / CIPHERTEXT) ───────────────────────────
112
+ const onDecryptFailed = (failInfo) => {
113
+ const remoteJid = failInfo?.remoteJid;
114
+ if (!remoteJid?.endsWith('@g.us')) return;
115
+
116
+ recordEnvelope(
117
+ { key: { remoteJid, id: failInfo.messageId, participant: failInfo.participant }, message: null },
118
+ false
119
+ );
120
+
121
+ if (treatDecryptFailureAsSuspicious) {
122
+ onDetect({
123
+ type: 'undecryptable',
124
+ remoteJid,
125
+ participant: failInfo.participant,
126
+ messageId: failInfo.messageId,
127
+ isPreKeyError: failInfo.isPreKeyError,
128
+ errorMessage: failInfo.errorMessage,
129
+ });
130
+ }
131
+ };
132
+
133
+ sock.ev.on('messages.upsert', onUpsert);
134
+ sock.ev.on('messages.update', onUpdate);
135
+ sock.ev.on('messages.decrypt-failed', onDecryptFailed);
136
+
137
+ return function unbindPaymentGuard() {
138
+ sock.ev.off('messages.upsert', onUpsert);
139
+ sock.ev.off('messages.update', onUpdate);
140
+ sock.ev.off('messages.decrypt-failed', onDecryptFailed);
141
+ };
142
+ };
@@ -0,0 +1,30 @@
1
+ // lib/Utils/resolve-lid-phone.js
2
+ // Garante a resolução de telefone real a partir de um @lid em grupo.
3
+ //
4
+ // O sharedLidPhoneCache só sabe o par LID<->telefone se a lib já tiver visto
5
+ // esse mapeamento em algum evento. A fonte mais confiável é a própria
6
+ // metadata do grupo (groupMetadata), onde o WhatsApp já entrega o par pronto
7
+ // pra cada participante. Esta função força esse fetch + registro no cache.
8
+
9
+ import { sharedLidPhoneCache } from '../WABinary/index.js';
10
+
11
+ /**
12
+ * @param {import('../Socket/index.js').WASocket} sock
13
+ * @param {string} groupJid
14
+ * @param {string} lid - o @lid que você quer resolver pro telefone real
15
+ * @returns {Promise<string|undefined>} o JID de telefone (@s.whatsapp.net), ou undefined se não achar
16
+ */
17
+ export const resolveLidPhoneFromGroup = async (sock, groupJid, lid) => {
18
+ // já está no cache? não precisa nem buscar metadata de novo
19
+ const cached = sharedLidPhoneCache.getPhoneForLid(lid);
20
+ if (cached) return cached;
21
+
22
+ try {
23
+ const metadata = await sock.groupMetadata(groupJid);
24
+ // o fetch já alimenta o sharedLidPhoneCache (fix aplicado em groups.js),
25
+ // então só precisamos ler de volta depois do fetch
26
+ return sharedLidPhoneCache.getPhoneForLid(lid);
27
+ } catch {
28
+ return undefined;
29
+ }
30
+ };
@@ -93,4 +93,248 @@ export const transferDevice = (fromJid, toJid) => {
93
93
  const { server, user } = jidDecode(toJid);
94
94
  return jidEncode(user, server, deviceId);
95
95
  };
96
- //# sourceMappingURL=jid-utils.js.map
96
+ //# sourceMappingURL=jid-utils.js.map
97
+ // ══════════════════════════════════════════════════════════════════════
98
+ // Sistema LID/JID avançado — integrado da chromestaff-baileys
99
+ // + suporte a @username (novo recurso WhatsApp)
100
+ // ══════════════════════════════════════════════════════════════════════
101
+
102
+ // ── LRU Cache leve (sem dependências externas) ────────────────────────
103
+ class SimpleLRU {
104
+ constructor(maxSize = 2000, ttl = 5 * 60 * 1000) {
105
+ this.maxSize = maxSize;
106
+ this.ttl = ttl;
107
+ this.map = new Map();
108
+ }
109
+ get(key) {
110
+ const entry = this.map.get(key);
111
+ if (!entry) return undefined;
112
+ if (Date.now() - entry.ts > this.ttl) { this.map.delete(key); return undefined; }
113
+ this.map.delete(key);
114
+ this.map.set(key, entry);
115
+ return entry.value;
116
+ }
117
+ set(key, value) {
118
+ if (this.map.has(key)) this.map.delete(key);
119
+ this.map.set(key, { value, ts: Date.now() });
120
+ if (this.map.size > this.maxSize) {
121
+ const lru = this.map.keys().next().value;
122
+ if (lru) this.map.delete(lru);
123
+ }
124
+ }
125
+ }
126
+
127
+ const lidToJidCache = new SimpleLRU(2000, 5 * 60 * 1000);
128
+
129
+ // ── Cache bidirecional LID ↔ JID (singleton do módulo) ───────────────
130
+ const _sharedLidPhoneMap = new Map();
131
+ const SHARED_MAP_MAX_SIZE = 3000;
132
+
133
+ export const sharedLidPhoneCache = {
134
+ set(lid, phoneJid) {
135
+ if (!lid || !phoneJid || typeof lid !== 'string' || typeof phoneJid !== 'string') return;
136
+ if (!phoneJid.includes('@')) phoneJid = phoneJid + '@s.whatsapp.net';
137
+ if (_sharedLidPhoneMap.size > SHARED_MAP_MAX_SIZE * 2) {
138
+ const it = _sharedLidPhoneMap.keys();
139
+ const toRemove = Math.floor(_sharedLidPhoneMap.size * 0.25);
140
+ for (let i = 0; i < toRemove; i++) {
141
+ const k = it.next().value;
142
+ if (k === undefined) break;
143
+ _sharedLidPhoneMap.delete(k);
144
+ }
145
+ }
146
+ _sharedLidPhoneMap.set(lid, phoneJid);
147
+ _sharedLidPhoneMap.set(phoneJid, lid);
148
+ lidToJidCache.set(lid, phoneJid);
149
+ },
150
+ get: (key) => _sharedLidPhoneMap.get(key),
151
+ getLidForPhone(phoneJid) {
152
+ if (!phoneJid) return undefined;
153
+ const val = _sharedLidPhoneMap.get(phoneJid);
154
+ return val && val.endsWith('@lid') ? val : undefined;
155
+ },
156
+ getPhoneForLid(lid) {
157
+ if (!lid) return undefined;
158
+ const val = _sharedLidPhoneMap.get(lid);
159
+ return val && val.endsWith('@s.whatsapp.net') ? val : undefined;
160
+ },
161
+ get size() { return _sharedLidPhoneMap.size; }
162
+ };
163
+
164
+ // ── BOT_MAP estático (Meta AI bots conhecidos) ────────────────────────
165
+ const BOT_MAP_STATIC = new Map([["867051314767696","13135550002"],["1061492271844689","13135550005"],["245886058483988","13135550009"],["3509905702656130","13135550012"],["1059680132034576","13135550013"],["715681030623646","13135550014"],["1644971366323052","13135550015"],["582497970646566","13135550019"],["645459357769306","13135550022"],["294997126699143","13135550023"],["1522631578502677","13135550027"],["719421926276396","13135550030"],["1788488635002167","13135550031"],["24232338603080193","13135550033"],["689289903143209","13135550035"],["871626054177096","13135550039"],["362351902849370","13135550042"],["1744617646041527","13135550043"],["893887762270570","13135550046"],["1155032702135830","13135550047"],["333931965993883","13135550048"],["853748013058752","13135550049"],["1559068611564819","13135550053"],["890487432705716","13135550054"],["240254602395494","13135550055"],["1578420349663261","13135550062"],["322908887140421","13135550065"],["3713961535514771","13135550067"],["997884654811738","13135550070"],["403157239387035","13135550081"],["535242369074963","13135550082"],["946293427247659","13135550083"],["3664707673802291","13135550084"],["1821827464894892","13135550085"],["1760312477828757","13135550086"],["439480398712216","13135550087"],["1876735582800984","13135550088"],["984025089825661","13135550089"],["1001336351558186","13135550090"],["3739346336347061","13135550091"],["3632749426974980","13135550092"],["427864203481615","13135550093"],["1434734570493055","13135550094"],["992873449225921","13135550095"],["813087747426445","13135550096"],["806369104931434","13135550098"],["1220982902403148","13135550099"],["1365893374104393","13135550100"],["686482033622048","13135550200"],["1454999838411253","13135550201"],["718584497008509","13135550202"],["743520384213443","13135550301"],["1147715789823789","13135550302"],["1173034540372201","13135550303"],["974785541030953","13135550304"],["1122200255531507","13135550305"],["899669714813162","13135550306"],["631880108970650","13135550307"],["435816149330026","13135550308"],["1368717161184556","13135550309"],["7849963461784891","13135550310"],["3609617065968984","13135550312"],["356273980574602","13135550313"],["1043447920539760","13135550314"],["1052764336525346","13135550315"],["2631118843732685","13135550316"],["510505411332176","13135550317"],["1945664239227513","13135550318"],["1518594378764656","13135550319"],["1378821579456138","13135550320"],["490214716896013","13135550321"],["1028577858870699","13135550322"],["308915665545959","13135550323"],["845884253678900","13135550324"],["995031308616442","13135550325"],["2787365464763437","13135550326"],["1532790990671645","13135550327"],["302617036180485","13135550328"],["723376723197227","13135550329"],["8393570407377966","13135550330"],["1931159970680725","13135550331"],["401073885688605","13135550332"],["2234478453565422","13135550334"],["814748673882312","13135550335"],["26133635056281592","13135550336"],["1439804456676119","13135550337"],["889851503172161","13135550338"],["1018283232836879","13135550339"],["1012781386779537","13135559000"],["823280953239532","13135559001"],["1597090934573334","13135559002"],["485965054020343","13135559003"],["1033381648363446","13135559004"],["491802010206446","13135559005"],["1017139033184870","13135559006"],["499638325922174","13135559008"],["468946335863664","13135559009"],["1570389776875816","13135559010"],["1004342694328995","13135559011"],["1012240323971229","13135559012"],["392171787222419","13135559013"],["952081212945019","13135559016"],["444507875070178","13135559017"],["1274819440594668","13135559018"],["1397041101147050","13135559019"],["425657699872640","13135559020"],["532292852562549","13135559021"],["705863241720292","13135559022"],["476449815183959","13135559023"],["488071553854222","13135559024"],["468693832665397","13135559025"],["517422564037340","13135559026"],["819805466613825","13135559027"],["1847708235641382","13135559028"],["716282970644228","13135559029"],["521655380527741","13135559030"],["476193631941905","13135559031"],["485600497445562","13135559032"],["440217235683910","13135559033"],["523342446758478","13135559034"],["514784864360240","13135559035"],["505790121814530","13135559036"],["420008964419580","13135559037"],["492141680204555","13135559038"],["388462787271952","13135559039"],["423473920752072","13135559040"],["489574180468229","13135559041"],["432360635854105","13135559042"],["477878201669248","13135559043"],["351656951234045","13135559044"],["430178036732582","13135559045"],["434537312944552","13135559046"],["1240614300631808","13135559047"],["473135945605128","13135559048"],["423669800729310","13135559049"],["3685666705015792","13135559050"],["504196509016638","13135559051"],["346844785189449","13135559052"],["504823088911074","13135559053"],["402669415797083","13135559054"],["490939640234431","13135559055"],["875124128063715","13135559056"],["468788962654605","13135559057"],["562386196354570","13135559058"],["372159285928791","13135559059"],["531017479591050","13135559060"],["1328873881401826","13135559061"],["1608363646390484","13135559062"],["1229628561554232","13135559063"],["348802211530364","13135559064"],["3708535859420184","13135559065"],["415517767742187","13135559066"],["479330341612638","13135559067"],["480785414723083","13135559068"],["387299107507991","13135559069"],["333389813188944","13135559070"],["391794130316996","13135559071"],["457893470576314","13135559072"],["435550496166469","13135559073"],["1620162702100689","13135559074"],["867491058616043","13135559075"],["816224117357759","13135559076"],["334065176362830","13135559077"],["489973170554709","13135559078"],["473060669049665","13135559079"],["1221505815643060","13135559080"],["889000703096359","13135559081"],["475235961979883","13135559082"],["3434445653519934","13135559084"],["524503026827421","13135559085"],["1179639046403856","13135559086"],["471563305859144","13135559087"],["533896609192881","13135559088"],["365443583168041","13135559089"],["836082305329393","13135559090"],["1056787705969916","13135559091"],["503312598958357","13135559092"],["3718606738453460","13135559093"],["826066052850902","13135559094"],["1033611345091888","13135559095"],["3868390816783240","13135559096"],["7462677740498860","13135559097"],["436288576108573","13135559098"],["1047559746718900","13135559099"],["1099299455255491","13135559100"],["1202037301040633","13135559101"],["1720619402074074","13135559102"],["1030422235101467","13135559103"],["827238979523502","13135559104"],["1516443722284921","13135559105"],["1174442747196709","13135559106"],["1653165225503842","13135559107"],["1037648777635013","13135559108"],["551617757299900","13135559109"],["1158813558718726","13135559110"],["2463236450542262","13135559111"],["1550393252501466","13135559112"],["2057065188042796","13135559113"],["506163028760735","13135559114"],["2065249100538481","13135559115"],["1041382867195858","13135559116"],["886500209499603","13135559117"],["1491615624892655","13135559118"],["486563697299617","13135559119"],["1175736513679463","13135559120"],["491811473512352","13165550064"]]);
166
+
167
+ let BOT_MAP = BOT_MAP_STATIC;
168
+
169
+ // ── lidToJid — LID → JID via cache + BOT_MAP ─────────────────────────
170
+ export const lidToJid = (jid) => {
171
+ try {
172
+ if (!jid || typeof jid !== 'string') return jid;
173
+ const cached = lidToJidCache.get(jid);
174
+ if (cached) return cached;
175
+ let result = jid;
176
+ if (jid.endsWith('@lid')) {
177
+ const lidPart = jid.replace('@lid', '');
178
+ if (BOT_MAP.has(lidPart)) {
179
+ result = BOT_MAP.get(lidPart) + '@s.whatsapp.net';
180
+ } else {
181
+ const phoneFromCache = sharedLidPhoneCache.getPhoneForLid(jid);
182
+ if (phoneFromCache) result = phoneFromCache;
183
+ }
184
+ }
185
+ if (result !== jid) lidToJidCache.set(jid, result);
186
+ return result;
187
+ } catch { return jid; }
188
+ };
189
+
190
+ // ── resolveJid — resolve LID para JID se necessário ──────────────────
191
+ export const resolveJid = (jid) => {
192
+ if (typeof jid === 'string' && jid.endsWith('@lid')) return lidToJid(jid);
193
+ return jid;
194
+ };
195
+
196
+ // ── normalizeJid — normaliza qualquer formato de JID ─────────────────
197
+ // Recebe: JID, LID, número puro, @username
198
+ // Retorna: JID normalizado (@s.whatsapp.net)
199
+ export const normalizeJid = (jid) => {
200
+ if (!jid || typeof jid !== 'string') return jid;
201
+ // @username → converte via onWhatsApp — retorna como está (precisa de resolução online)
202
+ if (jid.startsWith('@')) return jid;
203
+ // LID → tenta converter para JID
204
+ if (jid.endsWith('@lid')) return lidToJid(jid);
205
+ // Já é JID normalizado
206
+ if (jid.endsWith('@s.whatsapp.net') || jid.endsWith('@g.us') || jid.endsWith('@newsletter')) return jid;
207
+ // Número puro → adiciona sufixo
208
+ if (/^\d+$/.test(jid)) return jid + '@s.whatsapp.net';
209
+ // c.us → s.whatsapp.net
210
+ if (jid.endsWith('@c.us')) return jid.replace('@c.us', '@s.whatsapp.net');
211
+ return jid;
212
+ };
213
+
214
+ // ── resolveAll — retorna { jid, lid } para qualquer entrada ──────────
215
+ // Sempre devolve os dois formatos quando possível
216
+ export const resolveAll = (jid) => {
217
+ if (!jid || typeof jid !== 'string') return { jid, lid: undefined };
218
+ if (jid.endsWith('@lid')) {
219
+ const resolved = lidToJid(jid);
220
+ return {
221
+ jid: resolved !== jid ? resolved : undefined,
222
+ lid: jid
223
+ };
224
+ }
225
+ if (jid.endsWith('@s.whatsapp.net')) {
226
+ const lid = sharedLidPhoneCache.getLidForPhone(jid);
227
+ return { jid, lid };
228
+ }
229
+ return { jid: normalizeJid(jid), lid: undefined };
230
+ };
231
+
232
+ // ── validateJid — valida se um JID é válido ───────────────────────────
233
+ export const validateJid = (jid) => {
234
+ if (!jid || typeof jid !== 'string') return { isValid: false, error: 'JID is null or not a string' };
235
+ if (!jid.includes('@')) return { isValid: false, error: 'Missing @ separator' };
236
+ const [user, server] = jid.split('@');
237
+ if (!user) return { isValid: false, error: 'Empty user part' };
238
+ const validServers = ['s.whatsapp.net', 'g.us', 'lid', 'broadcast', 'newsletter', 'c.us', 'bot', 'hosted', 'hosted.lid'];
239
+ if (!validServers.includes(server)) return { isValid: false, error: `Unknown server: ${server}` };
240
+ return { isValid: true, error: null };
241
+ };
242
+
243
+ // ── validateAndCleanJid — valida e limpa JID ─────────────────────────
244
+ export const validateAndCleanJid = (jid) => {
245
+ if (!jid || typeof jid !== 'string') return jid;
246
+ if (jid.endsWith('@lid')) return lidToJid(jid);
247
+ return jid;
248
+ };
249
+
250
+ // ── validateAndConvertLidToJid — converte LID com validação opcional ──
251
+ export const validateAndConvertLidToJid = async (jid, onWhatsApp) => {
252
+ try {
253
+ if (!jid || typeof jid !== 'string') return jid;
254
+ const convertedJid = lidToJid(jid);
255
+ if (isLidUser(convertedJid)) return convertedJid;
256
+ if (!jid.endsWith('@lid') && convertedJid === jid) {
257
+ if (onWhatsApp) {
258
+ try {
259
+ const validation = await onWhatsApp(convertedJid);
260
+ return (validation?.length > 0 && validation[0].exists) ? convertedJid : jid;
261
+ } catch { return convertedJid; }
262
+ }
263
+ return convertedJid;
264
+ }
265
+ if (convertedJid !== jid && onWhatsApp) {
266
+ try {
267
+ const validation = await onWhatsApp(convertedJid);
268
+ return (validation?.length > 0 && validation[0].exists) ? convertedJid : jid;
269
+ } catch { return convertedJid; }
270
+ }
271
+ return convertedJid;
272
+ } catch { return jid; }
273
+ };
274
+
275
+ // ── lidToJidEnhanced — alias semântico ───────────────────────────────
276
+ export const lidToJidEnhanced = (jid) => lidToJid(jid);
277
+
278
+ // ── getSenderInfo — extrai JID + LID do remetente de qualquer mensagem
279
+ export const getSenderInfo = (msg) => {
280
+ if (!msg) return { jid: undefined, lid: undefined, isValid: false };
281
+ const key = msg.key || {};
282
+ const rawJid = key.participant || key.remoteJid || '';
283
+ const isGroup = rawJid.endsWith('@g.us');
284
+ let jid = isGroup ? (key.participant || '') : rawJid;
285
+ // Normaliza
286
+ if (jid.endsWith('@lid')) {
287
+ const resolved = lidToJid(jid);
288
+ return {
289
+ lid: jid,
290
+ jid: resolved !== jid ? resolved : undefined,
291
+ raw: jid,
292
+ isGroup,
293
+ isValid: true
294
+ };
295
+ }
296
+ const lid = sharedLidPhoneCache.getLidForPhone(jid);
297
+ return { jid, lid, raw: jid, isGroup, isValid: !!jid };
298
+ };
299
+
300
+ // ── @username suporte ─────────────────────────────────────────────────
301
+ // O WhatsApp permite encontrar usuários pelo @username em vez do número.
302
+ // Retorna o JID de um username via onWhatsApp.
303
+ export const resolveUsername = async (username, onWhatsApp) => {
304
+ if (!username || !onWhatsApp) return null;
305
+ const clean = username.startsWith('@') ? username.slice(1) : username;
306
+ try {
307
+ // O WhatsApp usa o formato "username@s.whatsapp.net" para busca por username
308
+ const results = await onWhatsApp(clean + '@s.whatsapp.net');
309
+ if (results?.length > 0 && results[0].exists) {
310
+ return results[0].jid;
311
+ }
312
+ return null;
313
+ } catch { return null; }
314
+ };
315
+
316
+ // ── isUsername — verifica se é um @username do WhatsApp ──────────────
317
+ export const isUsername = (str) => {
318
+ if (!str || typeof str !== 'string') return false;
319
+ return /^@[a-zA-Z0-9._]{3,}$/.test(str);
320
+ };
321
+
322
+ // ── getBotJid — resolve JID de bot IA ────────────────────────────────
323
+ export const getBotJid = (jid) => {
324
+ try {
325
+ const sepIdx = typeof jid === 'string' ? jid.indexOf('@') : -1;
326
+ if (sepIdx < 0) return jid;
327
+ const server = jid.slice(sepIdx + 1);
328
+ if (server !== 'bot') return jid;
329
+ const user = jid.slice(0, sepIdx);
330
+ const mappedNumber = BOT_MAP.get(user);
331
+ return mappedNumber ? `${mappedNumber}@s.whatsapp.net` : jid;
332
+ } catch { return jid; }
333
+ };
334
+
335
+ // ── setBotMap — substitui o BOT_MAP em runtime ───────────────────────
336
+ export const setBotMap = (mapLike) => {
337
+ if (!mapLike) return;
338
+ if (mapLike instanceof Map) { BOT_MAP = mapLike; return; }
339
+ if (typeof mapLike === 'object') { BOT_MAP = new Map(Object.entries(mapLike)); }
340
+ };