@omnizap-system/omnizap 2.6.1 → 2.6.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 (156) hide show
  1. package/.env.example +54 -9
  2. package/.github/workflows/ci.yml +3 -3
  3. package/.github/workflows/security-runner-hardening.yml +1 -1
  4. package/.github/workflows/security-zap-full-scan.yml +1 -0
  5. package/app/config/index.js +2 -0
  6. package/app/configParts/adminIdentity.js +5 -5
  7. package/app/configParts/baileysConfig.js +226 -55
  8. package/app/configParts/groupUtils.js +5 -0
  9. package/app/configParts/messagePersistenceService.js +143 -3
  10. package/app/configParts/sessionConfig.js +157 -0
  11. package/app/connection/baileysCompatibility.test.js +1 -1
  12. package/app/connection/groupOwnerWriteStateResolver.js +109 -0
  13. package/app/connection/socketController.js +625 -124
  14. package/app/connection/socketController.multiSession.test.js +108 -0
  15. package/app/controllers/messageController.js +1 -1
  16. package/app/controllers/messagePipeline/commandMiddleware.js +12 -10
  17. package/app/controllers/messagePipeline/conversationMiddleware.js +2 -1
  18. package/app/controllers/messagePipeline/messagePipelineMiddlewares.test.js +104 -0
  19. package/app/controllers/messagePipeline/preProcessingMiddlewares.js +80 -2
  20. package/app/controllers/messageProcessingPipeline.js +88 -9
  21. package/app/controllers/messageProcessingPipeline.test.js +200 -0
  22. package/app/modules/adminModule/AGENT.md +1 -1
  23. package/app/modules/adminModule/commandConfig.json +3318 -1347
  24. package/app/modules/adminModule/groupCommandHandlers.js +856 -14
  25. package/app/modules/adminModule/groupCommandHandlers.test.js +375 -9
  26. package/app/modules/adminModule/groupWarningRepository.js +152 -0
  27. package/app/modules/aiModule/AGENT.md +47 -30
  28. package/app/modules/aiModule/aiConfigRuntime.js +1 -0
  29. package/app/modules/aiModule/catCommand.js +132 -25
  30. package/app/modules/aiModule/commandConfig.json +114 -28
  31. package/app/modules/analyticsModule/messageAnalysisEventRepository.js +54 -6
  32. package/app/modules/gameModule/AGENT.md +1 -1
  33. package/app/modules/gameModule/commandConfig.json +29 -0
  34. package/app/modules/menuModule/AGENT.md +1 -1
  35. package/app/modules/menuModule/commandConfig.json +45 -10
  36. package/app/modules/menuModule/menuCatalogService.js +190 -0
  37. package/app/modules/menuModule/menuCommandUsageRepository.js +109 -0
  38. package/app/modules/menuModule/menuDynamicService.js +511 -0
  39. package/app/modules/menuModule/menuDynamicService.test.js +141 -0
  40. package/app/modules/menuModule/menus.js +36 -5
  41. package/app/modules/playModule/AGENT.md +10 -5
  42. package/app/modules/playModule/commandConfig.json +74 -16
  43. package/app/modules/playModule/playCommandConstants.js +13 -7
  44. package/app/modules/playModule/playCommandCore.js +4 -6
  45. package/app/modules/playModule/{playCommandYtDlpClient.js → playCommandMediaClient.js} +684 -332
  46. package/app/modules/playModule/playConfigRuntime.js +5 -6
  47. package/app/modules/playModule/playModuleCriticalFlows.test.js +44 -59
  48. package/app/modules/quoteModule/AGENT.md +1 -1
  49. package/app/modules/quoteModule/commandConfig.json +29 -0
  50. package/app/modules/rpgPokemonModule/AGENT.md +1 -1
  51. package/app/modules/rpgPokemonModule/commandConfig.json +29 -0
  52. package/app/modules/statsModule/AGENT.md +1 -1
  53. package/app/modules/statsModule/commandConfig.json +58 -0
  54. package/app/modules/stickerModule/AGENT.md +1 -1
  55. package/app/modules/stickerModule/commandConfig.json +145 -0
  56. package/app/modules/stickerPackModule/AGENT.md +1 -1
  57. package/app/modules/stickerPackModule/autoPackCollectorService.js +5 -1
  58. package/app/modules/stickerPackModule/commandConfig.json +29 -0
  59. package/app/modules/stickerPackModule/stickerAutoPackByTagsRuntime.js +1 -1
  60. package/app/modules/stickerPackModule/stickerPackCommandHandlers.js +78 -57
  61. package/app/modules/stickerPackModule/stickerPackService.js +13 -6
  62. package/app/modules/systemMetricsModule/AGENT.md +1 -1
  63. package/app/modules/systemMetricsModule/commandConfig.json +29 -0
  64. package/app/modules/tiktokModule/AGENT.md +1 -1
  65. package/app/modules/tiktokModule/commandConfig.json +29 -0
  66. package/app/modules/userModule/AGENT.md +1 -1
  67. package/app/modules/userModule/commandConfig.json +29 -0
  68. package/app/modules/waifuPicsModule/AGENT.md +57 -27
  69. package/app/modules/waifuPicsModule/commandConfig.json +87 -0
  70. package/app/observability/metrics.js +136 -0
  71. package/app/services/ai/commandConfigEnrichmentService.js +229 -47
  72. package/app/services/ai/geminiService.js +131 -7
  73. package/app/services/ai/geminiService.test.js +59 -2
  74. package/app/services/ai/moduleAiHelpCoreService.js +33 -4
  75. package/app/services/group/groupMetadataService.js +24 -1
  76. package/app/services/infra/dbWriteQueue.js +51 -21
  77. package/app/services/messaging/newsBroadcastService.js +843 -27
  78. package/app/services/multiSession/assignmentBalancerService.js +457 -0
  79. package/app/services/multiSession/groupOwnershipRepository.js +381 -0
  80. package/app/services/multiSession/groupOwnershipService.js +890 -0
  81. package/app/services/multiSession/groupOwnershipService.test.js +309 -0
  82. package/app/services/multiSession/sessionRegistryService.js +293 -0
  83. package/app/store/aiPromptStore.js +36 -19
  84. package/app/store/groupConfigStore.js +41 -5
  85. package/app/store/premiumUserStore.js +21 -7
  86. package/app/utils/antiLink/antiLinkModule.js +352 -16
  87. package/app/workers/aiHelperContinuousLearningWorker.js +512 -0
  88. package/database/index.js +6 -0
  89. package/database/migrations/20260307_d0_hardening_down.sql +1 -1
  90. package/database/migrations/20260314_d7_canonical_sender_down.sql +1 -1
  91. package/database/migrations/20260406_d30_security_analytics_down.sql +1 -1
  92. package/database/migrations/20260411_d35_group_community_metadata_down.sql +59 -0
  93. package/database/migrations/20260411_d35_group_community_metadata_up.sql +62 -0
  94. package/database/migrations/20260412_d36_system_config_tables_down.sql +32 -0
  95. package/database/migrations/20260412_d36_system_config_tables_up.sql +66 -0
  96. package/database/migrations/20260413_d37_group_user_warnings_down.sql +11 -0
  97. package/database/migrations/20260413_d37_group_user_warnings_up.sql +24 -0
  98. package/database/migrations/20260414_d38_multi_session_foundation_down.sql +72 -0
  99. package/database/migrations/20260414_d38_multi_session_foundation_up.sql +125 -0
  100. package/database/migrations/20260414_d39_multi_session_cutover_down.sql +103 -0
  101. package/database/migrations/20260414_d39_multi_session_cutover_up.sql +83 -0
  102. package/database/schema.sql +102 -1
  103. package/docker-compose.yml +4 -1
  104. package/docs/compliance/acceptable-use-policy-2026-03-07.md +1 -1
  105. package/docs/compliance/privacy-policy-2026-03-07.md +2 -2
  106. package/docs/security/dsar-lgpd-runbook-2026-03-07.md +1 -1
  107. package/docs/security/network-hardening-runbook-2026-03-07.md +53 -0
  108. package/docs/security/omnizap-static-security-headers.conf +25 -0
  109. package/ecosystem.prod.config.cjs +31 -11
  110. package/index.js +52 -18
  111. package/observability/alert-rules.yml +20 -0
  112. package/observability/grafana/dashboards/omnizap-system-admin.json +229 -0
  113. package/observability/mysql-setup.sql +4 -4
  114. package/observability/system-admin-observability.md +26 -0
  115. package/package.json +12 -5
  116. package/public/comandos/commands-catalog.json +2253 -78
  117. package/public/js/apps/commandsReactApp.js +267 -87
  118. package/public/js/apps/createPackApp.js +3 -3
  119. package/public/js/apps/stickersApp.js +255 -103
  120. package/public/js/apps/termsReactApp.js +57 -8
  121. package/public/js/apps/userPasswordResetReactApp.js +406 -0
  122. package/public/js/apps/userReactApp.js +96 -47
  123. package/public/js/apps/userSystemAdmReactApp.js +1506 -0
  124. package/public/pages/politica-de-privacidade.html +1 -1
  125. package/public/pages/stickers.html +5 -5
  126. package/public/pages/termos-de-uso-texto-integral.html +1 -1
  127. package/public/pages/termos-de-uso.html +1 -1
  128. package/public/pages/user-password-reset.html +3 -4
  129. package/public/pages/user-systemadm.html +8 -462
  130. package/public/pages/user.html +1 -1
  131. package/scripts/clear-whatsapp-session.sh +123 -0
  132. package/scripts/core-ai-mode.mjs +163 -0
  133. package/scripts/deploy.sh +10 -0
  134. package/scripts/enrich-command-config-ux-openai.mjs +492 -0
  135. package/scripts/generate-commands-catalog.mjs +155 -0
  136. package/scripts/new-whatsapp-session.sh +317 -0
  137. package/scripts/security-web-surface-check.mjs +218 -0
  138. package/server/controllers/admin/adminPanelHandlers.js +253 -3
  139. package/server/controllers/admin/systemAdminController.js +267 -0
  140. package/server/controllers/sticker/stickerCatalogController.js +9 -23
  141. package/server/controllers/system/contactController.js +9 -17
  142. package/server/controllers/system/stickerCatalogSystemContext.js +27 -6
  143. package/server/controllers/system/systemController.js +254 -1
  144. package/server/controllers/userController.js +6 -0
  145. package/server/email/emailTemplateService.js +3 -2
  146. package/server/http/httpServer.js +8 -4
  147. package/server/middleware/securityHeaders.js +20 -1
  148. package/server/routes/admin/systemAdminRouter.js +6 -0
  149. package/server/routes/indexRouter.js +30 -6
  150. package/server/routes/observability/grafanaProxyRouter.js +254 -0
  151. package/server/routes/static/staticPageRouter.js +27 -1
  152. package/server/utils/publicContact.js +31 -0
  153. package/utils/whatsapp/contactEnv.js +39 -0
  154. package/vite.config.mjs +2 -1
  155. package/app/modules/playModule/local/installYtDlp.js +0 -25
  156. package/app/modules/playModule/local/ytDlpInstaller.js +0 -28
@@ -13,20 +13,164 @@ import path from 'node:path';
13
13
  import { pipeline } from 'node:stream/promises';
14
14
  import { Readable } from 'node:stream';
15
15
  import { fileURLToPath } from 'node:url';
16
+ import { getMultiSessionRuntimeConfig } from './sessionConfig.js';
17
+
18
+ /**
19
+ * Utilitários centrais de integração com Baileys.
20
+ *
21
+ * Responsabilidades deste módulo:
22
+ * - validar/normalizar JIDs e tipos de presença;
23
+ * - encapsular chamadas seguras no socket ativo;
24
+ * - extrair/detectar mídia em mensagens;
25
+ * - resolver mapeamento LID <-> JID com cache e persistência.
26
+ */
27
+
28
+ /**
29
+ * @typedef {import('@whiskeysockets/baileys').WASocket} BaileysSocket
30
+ * @typedef {import('@whiskeysockets/baileys').WAMessage} BaileysMessage
31
+ * @typedef {import('@whiskeysockets/baileys').WAProto.IMessage} BaileysProtoMessage
32
+ * @typedef {'lid'|'pn'} AddressingMode
33
+ * @typedef {{includeAllTypes?: boolean, includeQuoted?: boolean, includeUnknown?: boolean}} MediaExtractionOptions
34
+ * @typedef {{
35
+ * mediaType: string,
36
+ * mediaKey: Record<string, any>,
37
+ * messageKey: string,
38
+ * isQuoted: boolean,
39
+ * isBinary: boolean,
40
+ * isUnknownType?: boolean,
41
+ * hasUrl: boolean,
42
+ * hasDirectPath: boolean,
43
+ * hasMediaKey: boolean,
44
+ * hasFileEncSha256: boolean,
45
+ * mimetype: string|null,
46
+ * fileLength: number|string|null,
47
+ * fileName: string|null,
48
+ * caption: string|null
49
+ * }} MediaEntry
50
+ * @typedef {{
51
+ * mediaType: string,
52
+ * mediaKey: Record<string, any>,
53
+ * isQuoted: boolean,
54
+ * details: {
55
+ * messageKey: string,
56
+ * isBinary: boolean,
57
+ * isUnknownType?: boolean,
58
+ * hasUrl: boolean,
59
+ * hasDirectPath: boolean,
60
+ * hasMediaKey: boolean,
61
+ * hasFileEncSha256: boolean,
62
+ * mimetype: string|null,
63
+ * fileLength: number|string|null,
64
+ * fileName: string|null,
65
+ * caption: string|null,
66
+ * allMediaFound: MediaEntry[]|null
67
+ * }
68
+ * }} MediaExtractionResult
69
+ * @typedef {{lid?: string|null, jid?: string|null, participantAlt?: string|null}} IdentityParams
70
+ * @typedef {{jid: string|null, expiresAt: number, lastStoredAt: number|null}} LidCacheEntry
71
+ * @typedef {{
72
+ * lid: string|null,
73
+ * jid: string|null,
74
+ * participantAlt: string|null,
75
+ * remoteJid: string|null,
76
+ * remoteJidAlt: string|null,
77
+ * groupMessage: boolean
78
+ * }} SenderInfo
79
+ */
16
80
 
17
81
  const DEFAULT_BAILEYS_VERSION = [7, 0, 0];
82
+ const multiSessionRuntimeConfig = getMultiSessionRuntimeConfig();
83
+ const PRIMARY_BAILEYS_SESSION_ID = String(multiSessionRuntimeConfig?.primarySessionId || process.env.BAILEYS_AUTH_SESSION_ID || 'default').trim() || 'default';
84
+
85
+ const normalizeSessionId = (sessionId) => {
86
+ const normalized = String(sessionId || '').trim();
87
+ return normalized || PRIMARY_BAILEYS_SESSION_ID;
88
+ };
18
89
 
90
+ const sessionSocketMap = new Map();
19
91
  let activeSocket = null;
20
92
 
21
- export const setActiveSocket = (socket) => {
22
- activeSocket = socket;
93
+ /**
94
+ * Atualiza a referência global do socket ativo e o registro por sessão.
95
+ * Mantém compatibilidade com chamadas legadas que não informam `sessionId`.
96
+ * @param {BaileysSocket|null} socket Instância conectada ou `null` para limpar.
97
+ * @param {string} [sessionId=PRIMARY_BAILEYS_SESSION_ID] Sessão da conexão.
98
+ * @returns {void}
99
+ */
100
+ export const setActiveSocket = (socket, sessionId = PRIMARY_BAILEYS_SESSION_ID) => {
101
+ const safeSessionId = normalizeSessionId(sessionId);
102
+ const previousSocket = sessionSocketMap.get(safeSessionId) || null;
103
+
104
+ if (socket) {
105
+ sessionSocketMap.set(safeSessionId, socket);
106
+ } else {
107
+ sessionSocketMap.delete(safeSessionId);
108
+ }
109
+
110
+ if (safeSessionId === PRIMARY_BAILEYS_SESSION_ID) {
111
+ activeSocket = socket || null;
112
+ } else if (!socket && previousSocket && activeSocket === previousSocket) {
113
+ activeSocket = null;
114
+ }
115
+
116
+ if (!activeSocket) {
117
+ activeSocket = sessionSocketMap.get(PRIMARY_BAILEYS_SESSION_ID) || sessionSocketMap.values().next()?.value || null;
118
+ }
119
+ };
120
+
121
+ /**
122
+ * Atualiza o socket ativo de uma sessão específica.
123
+ * @param {string} sessionId Sessão alvo.
124
+ * @param {BaileysSocket|null} socket Instância conectada ou `null`.
125
+ * @returns {void}
126
+ */
127
+ export const setSocketBySession = (sessionId, socket) => {
128
+ setActiveSocket(socket, sessionId);
129
+ };
130
+
131
+ /**
132
+ * Retorna a instância de socket ativa no processo.
133
+ * @returns {BaileysSocket|null}
134
+ */
135
+ export const getActiveSocket = () => {
136
+ if (isSocketOpen(activeSocket)) return activeSocket;
137
+
138
+ const primarySocket = sessionSocketMap.get(PRIMARY_BAILEYS_SESSION_ID) || null;
139
+ if (isSocketOpen(primarySocket)) {
140
+ activeSocket = primarySocket;
141
+ return activeSocket;
142
+ }
143
+
144
+ for (const socket of sessionSocketMap.values()) {
145
+ if (isSocketOpen(socket)) {
146
+ activeSocket = socket;
147
+ return activeSocket;
148
+ }
149
+ }
150
+
151
+ activeSocket = null;
152
+ return activeSocket;
23
153
  };
24
154
 
25
- export const getActiveSocket = () => activeSocket;
155
+ /**
156
+ * Retorna o socket ativo associado a uma sessão.
157
+ * @param {string} [sessionId=PRIMARY_BAILEYS_SESSION_ID]
158
+ * @returns {BaileysSocket|null}
159
+ */
160
+ export const getSocketBySession = (sessionId = PRIMARY_BAILEYS_SESSION_ID) => {
161
+ const safeSessionId = normalizeSessionId(sessionId);
162
+ return sessionSocketMap.get(safeSessionId) || null;
163
+ };
164
+
165
+ /**
166
+ * Snapshot dos sockets ativos por sessão.
167
+ * @returns {Map<string, BaileysSocket>}
168
+ */
169
+ export const getActiveSocketsBySession = () => new Map(sessionSocketMap);
26
170
 
27
171
  /**
28
172
  * Indica se uma instância de socket está aberta para operações.
29
- * @param {object|null|undefined} socket Instância de socket.
173
+ * @param {BaileysSocket|null|undefined} socket Instância de socket.
30
174
  * @returns {boolean}
31
175
  */
32
176
  export const isSocketOpen = (socket) => {
@@ -43,7 +187,7 @@ export const isActiveSocketOpen = () => isSocketOpen(activeSocket);
43
187
 
44
188
  /**
45
189
  * Executa um método em uma instância de socket validando disponibilidade.
46
- * @param {object|null|undefined} socket Instância de socket.
190
+ * @param {BaileysSocket|null|undefined} socket Instância de socket.
47
191
  * @param {string} methodName Nome do método no socket.
48
192
  * @param {...any} args Argumentos do método.
49
193
  * @returns {Promise<any>}
@@ -69,9 +213,18 @@ export const runSocketMethod = async (socket, methodName, ...args) => {
69
213
  */
70
214
  export const runActiveSocketMethod = async (methodName, ...args) => runSocketMethod(activeSocket, methodName, ...args);
71
215
 
216
+ /**
217
+ * Executa um método no socket de uma sessão específica.
218
+ * @param {string} sessionId Sessão alvo.
219
+ * @param {string} methodName Nome do método no socket.
220
+ * @param {...any} args Argumentos do método.
221
+ * @returns {Promise<any>}
222
+ */
223
+ export const runSessionSocketMethod = async (sessionId, methodName, ...args) => runSocketMethod(getSocketBySession(sessionId), methodName, ...args);
224
+
72
225
  /**
73
226
  * Recupera a blocklist da conta conectada.
74
- * @returns {Promise<(string|undefined)[]>}
227
+ * @returns {Promise<string[]>}
75
228
  */
76
229
  export const fetchBlocklistFromActiveSocket = async () => runActiveSocketMethod('fetchBlocklist');
77
230
 
@@ -128,7 +281,8 @@ const decodeJidParts = (() => {
128
281
 
129
282
  /**
130
283
  * Tipos de mensagem conhecidos do Baileys
131
- * Mapeamento de chaves do proto.Message para tipos normalizados
284
+ * (mapeamento de chaves do proto.Message para tipos normalizados).
285
+ * @type {Record<string, string>}
132
286
  */
133
287
  export const MEDIA_TYPE_MAPPING = {
134
288
  conversation: 'text',
@@ -182,7 +336,8 @@ export const MEDIA_TYPE_MAPPING = {
182
336
  };
183
337
 
184
338
  /**
185
- * Tipos de midia que contem conteudo binario/arquivo
339
+ * Tipos de mídia que carregam conteúdo binário/arquivo.
340
+ * @type {Set<string>}
186
341
  */
187
342
  export const BINARY_MEDIA_TYPES = new Set(['image', 'video', 'videoNote', 'audio', 'voice', 'document', 'sticker']);
188
343
 
@@ -521,13 +676,22 @@ export function isWhatsAppJid(jid) {
521
676
  return Boolean(server && WHATSAPP_USER_JID_SERVERS.has(server));
522
677
  }
523
678
 
679
+ /**
680
+ * Modo de endereçamento por LID.
681
+ * @type {'lid'}
682
+ */
524
683
  export const ADDRESSING_MODE_LID = 'lid';
684
+
685
+ /**
686
+ * Modo de endereçamento por PN/JID canônico.
687
+ * @type {'pn'}
688
+ */
525
689
  export const ADDRESSING_MODE_PN = 'pn';
526
690
 
527
691
  /**
528
692
  * Normaliza um modo de endereçamento (lid/pn).
529
693
  * @param {unknown} value
530
- * @returns {'lid'|'pn'|undefined}
694
+ * @returns {AddressingMode|undefined}
531
695
  */
532
696
  export const normalizeAddressingMode = (value) => {
533
697
  if (value === undefined || value === null) return undefined;
@@ -540,8 +704,8 @@ export const normalizeAddressingMode = (value) => {
540
704
  /**
541
705
  * Resolve modo de endereçamento a partir da chave da mensagem.
542
706
  * @param {object} [key={}]
543
- * @param {object} [senderInfo={}]
544
- * @returns {'lid'|'pn'|undefined}
707
+ * @param {SenderInfo|Record<string, any>} [senderInfo={}]
708
+ * @returns {AddressingMode|undefined}
545
709
  */
546
710
  export const resolveAddressingModeFromMessageKey = (key = {}, senderInfo = {}) => {
547
711
  const explicit = normalizeAddressingMode(key?.addressingMode);
@@ -560,7 +724,7 @@ export const resolveAddressingModeFromMessageKey = (key = {}, senderInfo = {}) =
560
724
 
561
725
  /**
562
726
  * Resolve JID canônico de usuário WhatsApp a partir de candidatos.
563
- * @param {...string} candidates
727
+ * @param {...(string|null|undefined)} candidates
564
728
  * @returns {string}
565
729
  */
566
730
  export const resolveCanonicalWhatsAppJid = (...candidates) => {
@@ -716,7 +880,7 @@ export async function resolveBaileysVersion() {
716
880
 
717
881
  /**
718
882
  * Baixa a foto de perfil associada à mensagem recebida.
719
- * @param {import('@whiskeysockets/baileys').WASocket} sock - Instância conectada do socket.
883
+ * @param {BaileysSocket} sock - Instância conectada do socket.
720
884
  * @param {import('@whiskeysockets/baileys').proto.IWebMessageInfo} msg - Mensagem usada para resolver o JID.
721
885
  * @returns {Promise<Buffer|null>} Buffer da imagem ou `null` se indisponível.
722
886
  */
@@ -740,17 +904,17 @@ export async function getProfilePicBuffer(sock, msg) {
740
904
 
741
905
  /**
742
906
  * Extrai o valor de expiração de uma mensagem do WhatsApp, ou retorna 24 horas (em segundos) por padrão.
743
- * @param {{message?: object}|null|undefined} sock - Estrutura contendo a propriedade `message`.
907
+ * @param {{message?: Record<string, any>}|null|undefined} messageInfo - Estrutura contendo a propriedade `message`.
744
908
  * @returns {number} Tempo de expiração em segundos.
745
909
  */
746
- export function getExpiration(sock) {
910
+ export function getExpiration(messageInfo) {
747
911
  const DEFAULT_EXPIRATION_SECONDS = 24 * 60 * 60;
748
912
 
749
- if (!sock || typeof sock !== 'object' || !sock.message) {
913
+ if (!messageInfo || typeof messageInfo !== 'object' || !messageInfo.message) {
750
914
  return DEFAULT_EXPIRATION_SECONDS;
751
915
  }
752
916
 
753
- const normalizedMessage = normalizeMessage(sock.message);
917
+ const normalizedMessage = normalizeMessage(messageInfo.message);
754
918
  const expiration = findExpiration(normalizedMessage);
755
919
 
756
920
  return typeof expiration === 'number' ? expiration : DEFAULT_EXPIRATION_SECONDS;
@@ -758,7 +922,7 @@ export function getExpiration(sock) {
758
922
 
759
923
  /**
760
924
  * Extrai o conteúdo de texto de uma mensagem do WhatsApp.
761
- * @param {{message?: object}} messageInfo - Objeto que contém o payload da mensagem.
925
+ * @param {{message?: Record<string, any>}} messageInfo - Objeto que contém o payload da mensagem.
762
926
  * @returns {string} Conteúdo textual extraído ou descrição do tipo de mensagem.
763
927
  */
764
928
  export const extractMessageContent = ({ message }) => {
@@ -810,7 +974,7 @@ export const extractMessageContent = ({ message }) => {
810
974
 
811
975
  /**
812
976
  * Faz o download de mídia a partir de uma mensagem do Baileys.
813
- * @param {import('@whiskeysockets/baileys').WAProto.IMessage} message - Objeto da mídia a ser baixada.
977
+ * @param {BaileysProtoMessage} message - Objeto da mídia a ser baixada.
814
978
  * @param {string} type - Tipo de mídia (ex.: `image`, `video`, `audio`, `document`).
815
979
  * @param {string} outputPath - Diretório onde o arquivo será salvo.
816
980
  * @returns {Promise<string|null>} Caminho do arquivo salvo ou `null` em caso de falha.
@@ -867,10 +1031,10 @@ export const downloadMediaMessage = async (message, type, outputPath) => {
867
1031
  };
868
1032
 
869
1033
  /**
870
- * Detecta dinamicamente todos os tipos de midia em um objeto de mensagem
871
- * @param {object} messageContent - Conteudo da mensagem
872
- * @param {boolean} isQuoted - Se e de uma mensagem citada
873
- * @returns {Array} Array de objetos com detalhes da midia encontrada
1034
+ * Detecta dinamicamente os tipos de mídia presentes em um payload de mensagem.
1035
+ * @param {Record<string, any>} messageContent Conteúdo da mensagem (normal ou quoted).
1036
+ * @param {boolean} [isQuoted=false] Sinaliza se o payload veio de uma citação.
1037
+ * @returns {MediaEntry[]} Lista de mídias detectadas (pode estar vazia).
874
1038
  */
875
1039
  export function detectAllMediaTypes(messageContent, isQuoted = false) {
876
1040
  if (!messageContent || typeof messageContent !== 'object') {
@@ -914,13 +1078,10 @@ export function detectAllMediaTypes(messageContent, isQuoted = false) {
914
1078
  }
915
1079
 
916
1080
  /**
917
- * Extrai detalhes da midia da mensagem de forma dinamica
918
- * @param {object} message - O objeto da mensagem
919
- * @param {object} options - Opcoes de configuracao
920
- * @param {boolean} options.includeAllTypes - Se deve incluir todos os tipos, nao apenas binarios
921
- * @param {boolean} options.includeQuoted - Se deve incluir midia de mensagens citadas
922
- * @param {boolean} options.includeUnknown - Se deve incluir tipos desconhecidos
923
- * @returns {{mediaType: string, mediaKey: object, details: object}|null} - Detalhes da midia ou null se nao encontrada
1081
+ * Extrai a mídia primária de uma mensagem com filtros configuráveis.
1082
+ * @param {BaileysMessage|{message?: Record<string, any>}|Record<string, any>} message Objeto da mensagem.
1083
+ * @param {MediaExtractionOptions} [options={}] Opções de filtragem.
1084
+ * @returns {MediaExtractionResult|null} Estrutura da mídia primária ou `null` quando não houver mídia.
924
1085
  */
925
1086
  export function extractMediaDetails(message, options = {}) {
926
1087
  const { includeAllTypes = false, includeQuoted = true, includeUnknown = false } = options;
@@ -955,10 +1116,10 @@ export function extractMediaDetails(message, options = {}) {
955
1116
  }
956
1117
 
957
1118
  /**
958
- * Extrai todos os tipos de midia de uma mensagem
959
- * @param {object} message - O objeto da mensagem
960
- * @param {object} options - Opcoes de configuracao
961
- * @returns {Array} Array com todos os tipos de midia encontrados
1119
+ * Extrai todas as mídias detectadas de uma mensagem.
1120
+ * @param {BaileysMessage|{message?: Record<string, any>}|Record<string, any>} message Objeto da mensagem.
1121
+ * @param {MediaExtractionOptions} [options={}] Opções de filtragem.
1122
+ * @returns {MediaEntry[]} Array com todas as mídias encontradas.
962
1123
  */
963
1124
  export function extractAllMediaDetails(message, options = {}) {
964
1125
  const { includeAllTypes = true, includeQuoted = true, includeUnknown = true } = options;
@@ -968,10 +1129,10 @@ export function extractAllMediaDetails(message, options = {}) {
968
1129
  }
969
1130
 
970
1131
  /**
971
- * Verifica se uma mensagem contem midia
972
- * @param {object} message - O objeto da mensagem
973
- * @param {string} specificType - Tipo especifico para verificar (opcional)
974
- * @returns {boolean} True se contem midia
1132
+ * Verifica se uma mensagem contém mídia.
1133
+ * @param {BaileysMessage|{message?: Record<string, any>}|Record<string, any>} message Objeto da mensagem.
1134
+ * @param {string|null} [specificType=null] Tipo específico para filtrar (ex.: `image`).
1135
+ * @returns {boolean} `true` quando ao menos uma mídia compatível é encontrada.
975
1136
  */
976
1137
  export function hasMedia(message, specificType = null) {
977
1138
  const allMedia = collectMediaFromMessage(message, { includeQuoted: true });
@@ -989,8 +1150,13 @@ export function hasMedia(message, specificType = null) {
989
1150
  }
990
1151
 
991
1152
  /**
992
- * Obtem informacoes sobre os tipos de midia suportados
993
- * @returns {object} Informacoes sobre tipos de midia
1153
+ * Obtém metadados dos tipos de mídia suportados.
1154
+ * @returns {{
1155
+ * knownTypes: string[],
1156
+ * binaryTypes: string[],
1157
+ * typeMapping: Record<string, string>,
1158
+ * totalKnownTypes: number
1159
+ * }}
994
1160
  */
995
1161
  export function getMediaTypeInfo() {
996
1162
  return {
@@ -1002,9 +1168,9 @@ export function getMediaTypeInfo() {
1002
1168
  }
1003
1169
 
1004
1170
  /**
1005
- * ===============================
1171
+ * ===========================
1006
1172
  * LID Map Utilities
1007
- * ===============================
1173
+ * ===========================
1008
1174
  */
1009
1175
  const CACHE_TTL_MS = 20 * 60 * 1000;
1010
1176
  const NEGATIVE_TTL_MS = 5 * 60 * 1000;
@@ -1015,10 +1181,7 @@ const BACKFILL_SOURCE = 'backfill';
1015
1181
  const __filename = fileURLToPath(import.meta.url);
1016
1182
  const __dirname = path.dirname(__filename);
1017
1183
  const BAILEYS_AUTH_DIR = path.resolve(__dirname, '../connection/auth');
1018
- const BAILEYS_AUTH_SESSION_ID = (() => {
1019
- const raw = String(process.env.BAILEYS_AUTH_SESSION_ID || '').trim();
1020
- return raw || 'default';
1021
- })();
1184
+ const BAILEYS_AUTH_SESSION_ID = PRIMARY_BAILEYS_SESSION_ID;
1022
1185
 
1023
1186
  const lidCache = new Map();
1024
1187
  const lidWriteBuffer = new Map();
@@ -1132,7 +1295,7 @@ const maskJid = (jid) => {
1132
1295
  /**
1133
1296
  * Busca entrada do cache (com expiração).
1134
1297
  * @param {string|null|undefined} lid
1135
- * @returns {{jid: string|null, expiresAt: number, lastStoredAt: number|null}|null}
1298
+ * @returns {LidCacheEntry|null}
1136
1299
  */
1137
1300
  const getCacheEntry = (lid) => {
1138
1301
  if (!lid) return null;
@@ -1194,9 +1357,10 @@ export const getCachedJidForLid = (lid) => {
1194
1357
 
1195
1358
  /**
1196
1359
  * Divide lista em batches.
1197
- * @param {Array<any>} items
1360
+ * @template T
1361
+ * @param {T[]} items
1198
1362
  * @param {number} [limit=BATCH_LIMIT]
1199
- * @returns {Array<Array<any>>}
1363
+ * @returns {T[][]}
1200
1364
  */
1201
1365
  const buildChunks = (items, limit = BATCH_LIMIT) => {
1202
1366
  const chunks = [];
@@ -1326,7 +1490,7 @@ const buildServerLikeFilter = (column, servers) => {
1326
1490
  /**
1327
1491
  * Resolve candidatos principais de identidade de usuário.
1328
1492
  * Centraliza regra usada por `resolveUserIdCached` e `resolveUserId`.
1329
- * @param {{lid?: string|null, jid?: string|null, participantAlt?: string|null}} [params]
1493
+ * @param {IdentityParams} [params]
1330
1494
  * @returns {{directJid: string|null, lidValue: string|null, fallback: string|null}}
1331
1495
  */
1332
1496
  const resolveIdentityCandidates = ({ lid, jid, participantAlt } = {}) => {
@@ -1431,7 +1595,7 @@ export const queueLidUpdate = (lid, jid, source = 'message') => {
1431
1595
 
1432
1596
  /**
1433
1597
  * Resolve ID canônico usando apenas cache.
1434
- * @param {{lid?: string|null, jid?: string|null, participantAlt?: string|null}} [params]
1598
+ * @param {IdentityParams} [params]
1435
1599
  * @returns {string|null}
1436
1600
  */
1437
1601
  export const resolveUserIdCached = ({ lid, jid, participantAlt } = {}) => {
@@ -1445,9 +1609,9 @@ export const resolveUserIdCached = ({ lid, jid, participantAlt } = {}) => {
1445
1609
  };
1446
1610
 
1447
1611
  /**
1448
- * Extrai informacoes do remetente a partir de uma mensagem do Baileys.
1449
- * @param {import('@whiskeysockets/baileys').WAMessage} msg
1450
- * @returns {{lid: string|null, jid: string|null, participantAlt: string|null, remoteJid: string|null, remoteJidAlt: string|null, groupMessage: boolean}}
1612
+ * Extrai informações de identidade do remetente a partir da mensagem.
1613
+ * @param {BaileysMessage} msg
1614
+ * @returns {SenderInfo}
1451
1615
  */
1452
1616
  export const extractSenderInfoFromMessage = (msg) => {
1453
1617
  const remoteJid = normalizeJid(msg?.key?.remoteJid || '') || null;
@@ -1556,7 +1720,7 @@ const fetchJidByLid = async (lid) => {
1556
1720
 
1557
1721
  /**
1558
1722
  * Resolve ID canônico consultando banco se necessário.
1559
- * @param {{lid?: string|null, jid?: string|null, participantAlt?: string|null}} [params]
1723
+ * @param {IdentityParams} [params]
1560
1724
  * @returns {Promise<string|null>}
1561
1725
  */
1562
1726
  export const resolveUserId = async ({ lid, jid, participantAlt } = {}) => {
@@ -1669,6 +1833,13 @@ export const flushLidQueue = async () => {
1669
1833
  await lidFlushRunner.run();
1670
1834
  };
1671
1835
 
1836
+ /**
1837
+ * Enfileira atualização de mapa LID/JID e retorna estado simplificado.
1838
+ * @param {string} lid
1839
+ * @param {string|null} jid
1840
+ * @param {string} [source='message']
1841
+ * @returns {Promise<{stored: boolean, reconciled: boolean}>}
1842
+ */
1672
1843
  export const maybeStoreLidMap = async (lid, jid, source = 'message') => {
1673
1844
  const result = queueLidUpdate(lid, jid, source);
1674
1845
  return { stored: result.queued, reconciled: result.reconciled };
@@ -288,6 +288,7 @@ export async function getGroupInfoAsync(groupId) {
288
288
  const participants = parseParticipantsFromDb(data.participants || data.participants_json || null);
289
289
  const creation = _normalizeNumberOrNull(data.creation);
290
290
  const ephemeralDuration = _normalizeNumberOrNull(data.ephemeral_duration ?? data.ephemeralDuration);
291
+ const linkedParent = _normalizeGroupId(data.linked_parent_jid || data.linkedParent) || null;
291
292
 
292
293
  const group = {
293
294
  id: _normalizeGroupId(data.id) || normalizedGroupId,
@@ -299,6 +300,10 @@ export async function getGroupInfoAsync(groupId) {
299
300
  restrict: Boolean(data.restrict),
300
301
  announce: Boolean(data.announce),
301
302
  isCommunity: Boolean(data.is_community ?? data.isCommunity),
303
+ isCommunityAnnounce: Boolean(data.is_community_announce ?? data.isCommunityAnnounce),
304
+ linkedParent,
305
+ memberAddMode: data.member_add_mode === null || data.member_add_mode === undefined ? null : Boolean(data.member_add_mode),
306
+ joinApprovalMode: data.join_approval_mode === null || data.join_approval_mode === undefined ? null : Boolean(data.join_approval_mode),
302
307
  addressingMode: data.addressing_mode || data.addressingMode || null,
303
308
  ephemeralDuration,
304
309
  };