@omnizap-system/omnizap 2.5.12

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 (425) hide show
  1. package/.clusterfuzzlite/Dockerfile +10 -0
  2. package/.env.example +907 -0
  3. package/.github/codeql/codeql-config.yml +10 -0
  4. package/.github/dependabot.yml +35 -0
  5. package/.github/workflows/ci.yml +73 -0
  6. package/.github/workflows/codeql.yml +106 -0
  7. package/.github/workflows/db-migration-check.yml +98 -0
  8. package/.github/workflows/dependency-review.yml +22 -0
  9. package/.github/workflows/deploy.yml +95 -0
  10. package/.github/workflows/release.yml +106 -0
  11. package/.github/workflows/security-attest-provenance.yml +51 -0
  12. package/.github/workflows/security-gitleaks.yml +34 -0
  13. package/.github/workflows/security-runner-hardening.yml +31 -0
  14. package/.github/workflows/security-scorecard.yml +44 -0
  15. package/.github/workflows/security-zap-baseline.yml +44 -0
  16. package/.github/workflows/security-zap-full-scan.yml +43 -0
  17. package/.github/workflows/security-zizmor.yml +36 -0
  18. package/.github/workflows/wiki-sync.yml +44 -0
  19. package/.gitleaks.toml +15 -0
  20. package/.prettierrc +34 -0
  21. package/CODE_OF_CONDUCT.md +114 -0
  22. package/LICENSE +56 -0
  23. package/README.md +110 -0
  24. package/SECURITY.md +110 -0
  25. package/app/config/index.js +4 -0
  26. package/app/configParts/adminIdentity.js +92 -0
  27. package/app/configParts/baileysConfig.js +1818 -0
  28. package/app/configParts/groupUtils.js +692 -0
  29. package/app/configParts/loggerConfig.js +394 -0
  30. package/app/configParts/messagePersistenceService.js +305 -0
  31. package/app/connection/baileysCompatibility.test.js +40 -0
  32. package/app/connection/baileysDbAuthState.js +344 -0
  33. package/app/connection/socketController.js +2243 -0
  34. package/app/controllers/messageController.js +7 -0
  35. package/app/controllers/messagePipeline/commandMiddleware.js +146 -0
  36. package/app/controllers/messagePipeline/conversationMiddleware.js +183 -0
  37. package/app/controllers/messagePipeline/messagePipelineMiddlewares.test.js +522 -0
  38. package/app/controllers/messagePipeline/postProcessingMiddleware.js +41 -0
  39. package/app/controllers/messagePipeline/preProcessingMiddlewares.js +166 -0
  40. package/app/controllers/messageProcessingPipeline.js +699 -0
  41. package/app/modules/adminModule/AGENT.md +4056 -0
  42. package/app/modules/adminModule/adminAiHelpService.js +56 -0
  43. package/app/modules/adminModule/adminConfigRuntime.js +177 -0
  44. package/app/modules/adminModule/commandConfig.json +7122 -0
  45. package/app/modules/adminModule/groupCommandHandlers.js +1823 -0
  46. package/app/modules/adminModule/groupCommandHandlers.test.js +350 -0
  47. package/app/modules/adminModule/groupEventHandlers.js +399 -0
  48. package/app/modules/aiModule/AGENT.md +547 -0
  49. package/app/modules/aiModule/aiAiHelpService.js +14 -0
  50. package/app/modules/aiModule/aiConfigRuntime.js +135 -0
  51. package/app/modules/aiModule/catCommand.js +967 -0
  52. package/app/modules/aiModule/commandConfig.json +981 -0
  53. package/app/modules/analyticsModule/messageAnalysisEventRepository.js +83 -0
  54. package/app/modules/gameModule/AGENT.md +196 -0
  55. package/app/modules/gameModule/commandConfig.json +366 -0
  56. package/app/modules/gameModule/diceCommand.js +42 -0
  57. package/app/modules/gameModule/gameAiHelpService.js +14 -0
  58. package/app/modules/gameModule/gameConfigRuntime.js +68 -0
  59. package/app/modules/menuModule/AGENT.md +205 -0
  60. package/app/modules/menuModule/commandConfig.json +366 -0
  61. package/app/modules/menuModule/common.js +316 -0
  62. package/app/modules/menuModule/menuAiHelpService.js +14 -0
  63. package/app/modules/menuModule/menuConfigRuntime.js +68 -0
  64. package/app/modules/menuModule/menus.js +66 -0
  65. package/app/modules/playModule/AGENT.md +321 -0
  66. package/app/modules/playModule/commandConfig.json +584 -0
  67. package/app/modules/playModule/playAiHelpService.js +14 -0
  68. package/app/modules/playModule/playCommand.js +1417 -0
  69. package/app/modules/playModule/playConfigRuntime.js +68 -0
  70. package/app/modules/quoteModule/AGENT.md +199 -0
  71. package/app/modules/quoteModule/commandConfig.json +366 -0
  72. package/app/modules/quoteModule/quoteAiHelpService.js +14 -0
  73. package/app/modules/quoteModule/quoteCommand.js +842 -0
  74. package/app/modules/quoteModule/quoteConfigRuntime.js +68 -0
  75. package/app/modules/rpgPokemonModule/AGENT.md +229 -0
  76. package/app/modules/rpgPokemonModule/commandConfig.json +386 -0
  77. package/app/modules/rpgPokemonModule/rpgBattleCanvasRenderer.js +795 -0
  78. package/app/modules/rpgPokemonModule/rpgBattleService.js +2110 -0
  79. package/app/modules/rpgPokemonModule/rpgBattleService.test.js +770 -0
  80. package/app/modules/rpgPokemonModule/rpgEvolutionUtils.js +22 -0
  81. package/app/modules/rpgPokemonModule/rpgPokemonAiHelpService.js +14 -0
  82. package/app/modules/rpgPokemonModule/rpgPokemonCommand.js +174 -0
  83. package/app/modules/rpgPokemonModule/rpgPokemonConfigRuntime.js +68 -0
  84. package/app/modules/rpgPokemonModule/rpgPokemonDomain.js +192 -0
  85. package/app/modules/rpgPokemonModule/rpgPokemonDomain.test.js +93 -0
  86. package/app/modules/rpgPokemonModule/rpgPokemonEvolution.test.js +46 -0
  87. package/app/modules/rpgPokemonModule/rpgPokemonMessages.js +746 -0
  88. package/app/modules/rpgPokemonModule/rpgPokemonRepository.js +1847 -0
  89. package/app/modules/rpgPokemonModule/rpgPokemonService.js +6839 -0
  90. package/app/modules/rpgPokemonModule/rpgProfileCanvasRenderer.js +354 -0
  91. package/app/modules/statsModule/AGENT.md +320 -0
  92. package/app/modules/statsModule/commandConfig.json +540 -0
  93. package/app/modules/statsModule/globalRankingCommand.js +64 -0
  94. package/app/modules/statsModule/rankingCommand.js +41 -0
  95. package/app/modules/statsModule/rankingCommon.js +1305 -0
  96. package/app/modules/statsModule/statsAiHelpService.js +14 -0
  97. package/app/modules/statsModule/statsConfigRuntime.js +68 -0
  98. package/app/modules/stickerModule/AGENT.md +692 -0
  99. package/app/modules/stickerModule/addStickerMetadata.js +239 -0
  100. package/app/modules/stickerModule/commandConfig.json +1216 -0
  101. package/app/modules/stickerModule/convertToWebp.js +367 -0
  102. package/app/modules/stickerModule/stickerAiHelpService.js +14 -0
  103. package/app/modules/stickerModule/stickerCommand.js +446 -0
  104. package/app/modules/stickerModule/stickerConfigRuntime.js +68 -0
  105. package/app/modules/stickerModule/stickerConvertCommand.js +159 -0
  106. package/app/modules/stickerModule/stickerTextCommand.js +653 -0
  107. package/app/modules/stickerPackModule/AGENT.md +215 -0
  108. package/app/modules/stickerPackModule/autoPackCollectorRuntime.js +20 -0
  109. package/app/modules/stickerPackModule/autoPackCollectorService.js +357 -0
  110. package/app/modules/stickerPackModule/commandConfig.json +387 -0
  111. package/app/modules/stickerPackModule/domainEventOutboxRepository.js +227 -0
  112. package/app/modules/stickerPackModule/domainEvents.js +52 -0
  113. package/app/modules/stickerPackModule/semanticReclassificationEngine.js +429 -0
  114. package/app/modules/stickerPackModule/semanticReclassificationEngine.test.js +75 -0
  115. package/app/modules/stickerPackModule/semanticThemeClusterService.js +544 -0
  116. package/app/modules/stickerPackModule/stickerAssetClassificationRepository.js +400 -0
  117. package/app/modules/stickerPackModule/stickerAssetRepository.js +400 -0
  118. package/app/modules/stickerPackModule/stickerAssetReprocessQueueRepository.js +175 -0
  119. package/app/modules/stickerPackModule/stickerAutoPackByTagsRuntime.js +3702 -0
  120. package/app/modules/stickerPackModule/stickerClassificationBackgroundRuntime.js +559 -0
  121. package/app/modules/stickerPackModule/stickerClassificationService.js +557 -0
  122. package/app/modules/stickerPackModule/stickerDedicatedTaskWorkerRuntime.js +249 -0
  123. package/app/modules/stickerPackModule/stickerDomainEventBus.js +65 -0
  124. package/app/modules/stickerPackModule/stickerDomainEventConsumerRuntime.js +208 -0
  125. package/app/modules/stickerPackModule/stickerMarketplaceDriftService.js +99 -0
  126. package/app/modules/stickerPackModule/stickerObjectStorageService.js +285 -0
  127. package/app/modules/stickerPackModule/stickerPackAiHelpService.js +14 -0
  128. package/app/modules/stickerPackModule/stickerPackCommandHandlers.js +1148 -0
  129. package/app/modules/stickerPackModule/stickerPackConfigRuntime.js +68 -0
  130. package/app/modules/stickerPackModule/stickerPackEngagementRepository.js +152 -0
  131. package/app/modules/stickerPackModule/stickerPackErrors.js +30 -0
  132. package/app/modules/stickerPackModule/stickerPackInteractionEventRepository.js +101 -0
  133. package/app/modules/stickerPackModule/stickerPackItemRepository.js +432 -0
  134. package/app/modules/stickerPackModule/stickerPackMarketplaceService.js +313 -0
  135. package/app/modules/stickerPackModule/stickerPackMessageService.js +268 -0
  136. package/app/modules/stickerPackModule/stickerPackRepository.js +450 -0
  137. package/app/modules/stickerPackModule/stickerPackScoreSnapshotRepository.js +179 -0
  138. package/app/modules/stickerPackModule/stickerPackScoreSnapshotRuntime.js +271 -0
  139. package/app/modules/stickerPackModule/stickerPackService.js +733 -0
  140. package/app/modules/stickerPackModule/stickerPackServiceRuntime.js +32 -0
  141. package/app/modules/stickerPackModule/stickerPackUtils.js +107 -0
  142. package/app/modules/stickerPackModule/stickerStorageService.js +559 -0
  143. package/app/modules/stickerPackModule/stickerWorkerPipelineRuntime.js +242 -0
  144. package/app/modules/stickerPackModule/stickerWorkerTaskQueueRepository.js +242 -0
  145. package/app/modules/systemMetricsModule/AGENT.md +193 -0
  146. package/app/modules/systemMetricsModule/commandConfig.json +344 -0
  147. package/app/modules/systemMetricsModule/pingCommand.js +399 -0
  148. package/app/modules/systemMetricsModule/systemMetricsAiHelpService.js +14 -0
  149. package/app/modules/systemMetricsModule/systemMetricsConfigRuntime.js +68 -0
  150. package/app/modules/tiktokModule/AGENT.md +196 -0
  151. package/app/modules/tiktokModule/commandConfig.json +366 -0
  152. package/app/modules/tiktokModule/tiktokAiHelpService.js +14 -0
  153. package/app/modules/tiktokModule/tiktokCommand.js +716 -0
  154. package/app/modules/tiktokModule/tiktokConfigRuntime.js +68 -0
  155. package/app/modules/userModule/AGENT.md +200 -0
  156. package/app/modules/userModule/commandConfig.json +386 -0
  157. package/app/modules/userModule/userAiHelpService.js +14 -0
  158. package/app/modules/userModule/userCommand.js +1155 -0
  159. package/app/modules/userModule/userConfigRuntime.js +68 -0
  160. package/app/modules/waifuPicsModule/AGENT.md +431 -0
  161. package/app/modules/waifuPicsModule/commandConfig.json +780 -0
  162. package/app/modules/waifuPicsModule/waifuPicsAiHelpService.js +14 -0
  163. package/app/modules/waifuPicsModule/waifuPicsCommand.js +586 -0
  164. package/app/modules/waifuPicsModule/waifuPicsConfigRuntime.js +68 -0
  165. package/app/observability/metrics.js +766 -0
  166. package/app/services/ai/aiHelpResponseCacheRepository.js +280 -0
  167. package/app/services/ai/aiLearningRepository.js +400 -0
  168. package/app/services/ai/commandConfigEnrichmentRepository.js +769 -0
  169. package/app/services/ai/commandConfigEnrichmentService.js +452 -0
  170. package/app/services/ai/commandConfigValidationService.js +443 -0
  171. package/app/services/ai/commandToolBuilderService.js +192 -0
  172. package/app/services/ai/conversationRouterService.js +516 -0
  173. package/app/services/ai/geminiService.js +115 -0
  174. package/app/services/ai/geminiService.test.js +87 -0
  175. package/app/services/ai/globalModuleAiHelpService.js +1412 -0
  176. package/app/services/ai/globalToolCallingService.js +203 -0
  177. package/app/services/ai/messageCommandExecutionService.js +391 -0
  178. package/app/services/ai/moduleAiHelpCoreService.js +1099 -0
  179. package/app/services/ai/moduleAiHelpWrapperFactory.js +65 -0
  180. package/app/services/ai/moduleCommandConfigRuntimeService.js +113 -0
  181. package/app/services/ai/moduleToolExecutorService.js +464 -0
  182. package/app/services/ai/moduleToolRegistryService.js +178 -0
  183. package/app/services/ai/toolCandidateSelectorService.js +781 -0
  184. package/app/services/auth/googleWebLinkService.js +80 -0
  185. package/app/services/auth/whatsappLoginLinkService.js +230 -0
  186. package/app/services/external/pokeApiService.js +398 -0
  187. package/app/services/group/groupMetadataService.js +311 -0
  188. package/app/services/infra/dbWriteQueue.js +874 -0
  189. package/app/services/infra/featureFlagService.js +131 -0
  190. package/app/services/infra/queueUtils.js +55 -0
  191. package/app/services/messaging/captchaService.js +491 -0
  192. package/app/services/messaging/messagePersistenceService.js +1 -0
  193. package/app/services/messaging/newsBroadcastService.js +347 -0
  194. package/app/services/sticker/stickerFocusService.js +347 -0
  195. package/app/services/sticker/stickerFocusService.test.js +43 -0
  196. package/app/store/aiPromptStore.js +38 -0
  197. package/app/store/conversationSessionStore.js +131 -0
  198. package/app/store/groupConfigStore.js +58 -0
  199. package/app/store/premiumUserStore.js +54 -0
  200. package/app/utils/antiLink/antiLinkModule.js +700 -0
  201. package/app/utils/http/getImageBufferModule.js +18 -0
  202. package/app/utils/json/jsonSanitizer.js +113 -0
  203. package/app/utils/json/jsonSanitizer.test.js +40 -0
  204. package/app/utils/systemMetrics/systemMetricsModule.js +88 -0
  205. package/app/workers/aiLearningWorker.js +605 -0
  206. package/app/workers/commandConfigEnrichmentWorker.js +242 -0
  207. package/database/index.js +2075 -0
  208. package/database/init.js +151 -0
  209. package/database/migrations/.gitkeep +0 -0
  210. package/database/migrations/20260307_d0_hardening_down.sql +64 -0
  211. package/database/migrations/20260307_d0_hardening_up.sql +79 -0
  212. package/database/migrations/20260307_d1_terms_acceptance_down.sql +11 -0
  213. package/database/migrations/20260307_d1_terms_acceptance_up.sql +37 -0
  214. package/database/migrations/20260307_d2_auth_hardening_down.sql +75 -0
  215. package/database/migrations/20260307_d2_auth_hardening_up.sql +100 -0
  216. package/database/migrations/20260314_d7_canonical_sender_down.sql +53 -0
  217. package/database/migrations/20260314_d7_canonical_sender_up.sql +114 -0
  218. package/database/migrations/20260406_d30_security_analytics_down.sql +95 -0
  219. package/database/migrations/20260406_d30_security_analytics_up.sql +292 -0
  220. package/database/migrations/20260407_d31_web_google_session_token_hardening_down.sql +2 -0
  221. package/database/migrations/20260407_d31_web_google_session_token_hardening_up.sql +17 -0
  222. package/database/migrations/20260408_d32_ai_help_response_cache_down.sql +1 -0
  223. package/database/migrations/20260408_d32_ai_help_response_cache_up.sql +22 -0
  224. package/database/migrations/20260409_d33_ai_learning_tables_down.sql +4 -0
  225. package/database/migrations/20260409_d33_ai_learning_tables_up.sql +52 -0
  226. package/database/migrations/20260410_d34_command_config_enrichment_down.sql +3 -0
  227. package/database/migrations/20260410_d34_command_config_enrichment_up.sql +48 -0
  228. package/database/schema.sql +1186 -0
  229. package/docker-compose.yml +104 -0
  230. package/docs/audits/stickerCatalogController-out-of-scope.md +103 -0
  231. package/docs/audits/stickerCatalogController-symbols.md +58 -0
  232. package/docs/compliance/acceptable-use-policy-2026-03-07.md +35 -0
  233. package/docs/compliance/dpa-b2b-standard-2026-03-07.md +80 -0
  234. package/docs/compliance/monthly-compliance-checklist-2026-03-07.md +88 -0
  235. package/docs/compliance/notice-and-takedown-policy-2026-03-07.md +34 -0
  236. package/docs/compliance/privacy-policy-2026-03-07.md +75 -0
  237. package/docs/compliance/subprocessors-inventory-2026-03-07.md +16 -0
  238. package/docs/database/production-db-evolution-runbook-2026q1.md +365 -0
  239. package/docs/security/dsar-lgpd-runbook-2026-03-07.md +86 -0
  240. package/docs/security/incident-response-lgpd-anpd-runbook-2026-03-07.md +77 -0
  241. package/docs/security/network-hardening-runbook-2026-03-07.md +137 -0
  242. package/docs/seo/omnizap-seo-playbook-br-2026-02-28.md +238 -0
  243. package/docs/seo/satellite-page-template.md +116 -0
  244. package/docs/seo/satellite-pages-phase1.json +364 -0
  245. package/docs/wiki/Home.md +120 -0
  246. package/docs/wiki/pair-extraordinaire-2026-03-08.md +3 -0
  247. package/docs/wiki/recent-changes-2026-03-08.md +47 -0
  248. package/ecosystem.prod.config.cjs +135 -0
  249. package/eslint.config.js +89 -0
  250. package/index.js +488 -0
  251. package/ml/clip_classifier/Dockerfile +18 -0
  252. package/ml/clip_classifier/README.md +118 -0
  253. package/ml/clip_classifier/adaptive_scoring.py +40 -0
  254. package/ml/clip_classifier/classifier.py +654 -0
  255. package/ml/clip_classifier/embedding_store.py +481 -0
  256. package/ml/clip_classifier/env_loader.py +15 -0
  257. package/ml/clip_classifier/llm_label_expander.py +144 -0
  258. package/ml/clip_classifier/main.py +213 -0
  259. package/ml/clip_classifier/requirements.txt +10 -0
  260. package/ml/clip_classifier/similarity_engine.py +74 -0
  261. package/new-logo.png +0 -0
  262. package/observability/alert-rules.yml +60 -0
  263. package/observability/grafana/dashboards/omnizap-mysql.json +136 -0
  264. package/observability/grafana/dashboards/omnizap-overview.json +170 -0
  265. package/observability/grafana/provisioning/dashboards/dashboards.yml +11 -0
  266. package/observability/grafana/provisioning/datasources/datasources.yml +15 -0
  267. package/observability/loki-config.yml +38 -0
  268. package/observability/mysql-setup.sql +46 -0
  269. package/observability/prometheus.yml +35 -0
  270. package/observability/promtail-config.yml +84 -0
  271. package/observability/sticker-catalog-slo.md +83 -0
  272. package/observability/sticker-scale-hardening-rollout.md +128 -0
  273. package/package.json +144 -0
  274. package/public/apple-touch-icon.png +0 -0
  275. package/public/assets/css/commands-react.input.css +71 -0
  276. package/public/assets/css/create-pack-react.input.css +31 -0
  277. package/public/assets/css/home-react.input.css +106 -0
  278. package/public/assets/css/login-react.input.css +58 -0
  279. package/public/assets/css/stickers-react.input.css +18 -0
  280. package/public/assets/css/terms-react.input.css +115 -0
  281. package/public/assets/css/user-react.input.css +57 -0
  282. package/public/assets/images/brand-icon-192.png +0 -0
  283. package/public/assets/images/brand-logo-128.webp +0 -0
  284. package/public/assets/images/hero-banner-1280.jpg +0 -0
  285. package/public/comandos/commands-catalog.json +4517 -0
  286. package/public/css/api-docs.css +161 -0
  287. package/public/css/stickers-admin.css +1288 -0
  288. package/public/css/styles.css +679 -0
  289. package/public/css/systemadm/admin.css +474 -0
  290. package/public/css/systemadm/base.css +73 -0
  291. package/public/css/systemadm/components.css +662 -0
  292. package/public/css/systemadm/layout.css +229 -0
  293. package/public/css/systemadm/tokens.css +56 -0
  294. package/public/favicon-16x16.png +0 -0
  295. package/public/favicon-32x32.png +0 -0
  296. package/public/favicon.ico +0 -0
  297. package/public/js/apps/apiDocsApp.js +235 -0
  298. package/public/js/apps/commandsReactApp.js +528 -0
  299. package/public/js/apps/createPackApp.js +1646 -0
  300. package/public/js/apps/homeReactApp.js +942 -0
  301. package/public/js/apps/loginReactApp.js +496 -0
  302. package/public/js/apps/stickersAdminApp.js +1753 -0
  303. package/public/js/apps/stickersApp.js +3797 -0
  304. package/public/js/apps/termsReactApp.js +528 -0
  305. package/public/js/apps/userApp.js +2540 -0
  306. package/public/js/apps/userProfile/actions.js +66 -0
  307. package/public/js/apps/userReactApp.js +547 -0
  308. package/public/js/catalog.js +950 -0
  309. package/public/pages/api-docs.html +40 -0
  310. package/public/pages/aup.html +158 -0
  311. package/public/pages/comandos.html +41 -0
  312. package/public/pages/dpa.html +227 -0
  313. package/public/pages/home.html +45 -0
  314. package/public/pages/licenca.html +182 -0
  315. package/public/pages/login.html +40 -0
  316. package/public/pages/notice-and-takedown.html +234 -0
  317. package/public/pages/politica-de-privacidade.html +251 -0
  318. package/public/pages/seo-bot-whatsapp-para-grupo.html +350 -0
  319. package/public/pages/seo-bot-whatsapp-sem-programar.html +350 -0
  320. package/public/pages/seo-como-automatizar-avisos-no-whatsapp.html +350 -0
  321. package/public/pages/seo-como-criar-comandos-whatsapp.html +350 -0
  322. package/public/pages/seo-como-evitar-spam-no-whatsapp.html +350 -0
  323. package/public/pages/seo-como-moderar-grupo-whatsapp.html +350 -0
  324. package/public/pages/seo-como-organizar-comunidade-whatsapp.html +350 -0
  325. package/public/pages/seo-melhor-bot-whatsapp-para-grupos.html +350 -0
  326. package/public/pages/stickers-admin.html +31 -0
  327. package/public/pages/stickers-create.html +41 -0
  328. package/public/pages/stickers.html +45 -0
  329. package/public/pages/suboperadores.html +237 -0
  330. package/public/pages/termos-de-uso-texto-integral.html +241 -0
  331. package/public/pages/termos-de-uso.html +41 -0
  332. package/public/pages/user-password-reset.html +32 -0
  333. package/public/pages/user-systemadm.html +508 -0
  334. package/public/pages/user.html +39 -0
  335. package/public/robots.txt +9 -0
  336. package/public/site.webmanifest +24 -0
  337. package/public/sitemap.xml +98 -0
  338. package/schemas/command-config.schema.json +582 -0
  339. package/scripts/baileys-compat-smoke.mjs +12 -0
  340. package/scripts/cache-bust.mjs +142 -0
  341. package/scripts/deploy.sh +916 -0
  342. package/scripts/email-broadcast-terms-update.mjs +170 -0
  343. package/scripts/enrich-command-discovery-fields.mjs +286 -0
  344. package/scripts/generate-command-config-schema.mjs +273 -0
  345. package/scripts/generate-commands-catalog.mjs +308 -0
  346. package/scripts/generate-module-agents.mjs +631 -0
  347. package/scripts/generate-seo-satellite-pages.mjs +400 -0
  348. package/scripts/github-deploy-notify.mjs +174 -0
  349. package/scripts/github-release-notify.mjs +219 -0
  350. package/scripts/release.sh +599 -0
  351. package/scripts/run-codeql-local.sh +116 -0
  352. package/scripts/run-prettier-all.mjs +25 -0
  353. package/scripts/security-smoketest.mjs +581 -0
  354. package/scripts/sticker-catalog-loadtest.mjs +210 -0
  355. package/scripts/sticker-worker-task.mjs +119 -0
  356. package/scripts/sync-readme-snapshot.mjs +133 -0
  357. package/scripts/validate-command-config-schema.mjs +130 -0
  358. package/scripts/validate-command-configs.mjs +15 -0
  359. package/scripts/wiki-sync.sh +191 -0
  360. package/server/auth/googleWebAuth/googleWebAuthRuntime.js +62 -0
  361. package/server/auth/googleWebAuth/googleWebAuthService.js +807 -0
  362. package/server/auth/jwt/webJwtService.js +147 -0
  363. package/server/auth/stickerCatalogAuthContext.js +165 -0
  364. package/server/auth/termsAcceptance/termsAcceptanceHandler.js +189 -0
  365. package/server/auth/userPassword/index.js +14 -0
  366. package/server/auth/userPassword/userPasswordAuthService.js +422 -0
  367. package/server/auth/userPassword/userPasswordCrypto.js +199 -0
  368. package/server/auth/userPassword/userPasswordCrypto.test.js +76 -0
  369. package/server/auth/userPassword/userPasswordRecoveryService.js +728 -0
  370. package/server/auth/validation/authSchemas.js +236 -0
  371. package/server/auth/webAccount/webAccountHandlers.js +1434 -0
  372. package/server/controllers/admin/adminBanService.js +138 -0
  373. package/server/controllers/admin/adminPanelHandlers.js +2083 -0
  374. package/server/controllers/admin/stickerCatalogAdminContext.js +17 -0
  375. package/server/controllers/admin/systemAdminController.js +201 -0
  376. package/server/controllers/email/emailAutomationController.js +239 -0
  377. package/server/controllers/metricsController.js +21 -0
  378. package/server/controllers/seo/stickerCatalogSeoContext.js +514 -0
  379. package/server/controllers/sticker/nonCatalogHandlers.js +303 -0
  380. package/server/controllers/sticker/stickerCatalogController.js +4700 -0
  381. package/server/controllers/system/contactController.js +115 -0
  382. package/server/controllers/system/githubController.js +137 -0
  383. package/server/controllers/system/stickerCatalogSystemContext.js +758 -0
  384. package/server/controllers/system/storageController.js +154 -0
  385. package/server/controllers/system/systemController.js +135 -0
  386. package/server/controllers/system/systemMetricsController.js +156 -0
  387. package/server/controllers/system/visitController.js +90 -0
  388. package/server/controllers/userController.js +145 -0
  389. package/server/email/emailAutomationRuntime.js +225 -0
  390. package/server/email/emailAutomationService.js +125 -0
  391. package/server/email/emailOutboxRepository.js +282 -0
  392. package/server/email/emailTemplateService.js +480 -0
  393. package/server/email/emailTransportService.js +156 -0
  394. package/server/http/clientIp.js +95 -0
  395. package/server/http/httpRequestUtils.js +262 -0
  396. package/server/http/httpRequestUtils.test.js +80 -0
  397. package/server/http/httpServer.js +180 -0
  398. package/server/http/requestContext.js +20 -0
  399. package/server/http/siteRoutingUtils.js +87 -0
  400. package/server/index.js +1 -0
  401. package/server/middleware/cachePolicy.js +26 -0
  402. package/server/middleware/cachePolicyHelpers.js +1 -0
  403. package/server/middleware/endpointRateLimit.js +181 -0
  404. package/server/middleware/rateLimit.js +70 -0
  405. package/server/middleware/requireAdminAuth.js +48 -0
  406. package/server/middleware/securityHeaders.js +97 -0
  407. package/server/routes/admin/systemAdminRouter.js +64 -0
  408. package/server/routes/email/emailAutomationRouter.js +46 -0
  409. package/server/routes/health/healthRouter.js +41 -0
  410. package/server/routes/indexRouter.js +234 -0
  411. package/server/routes/metrics/metricsRouter.js +58 -0
  412. package/server/routes/static/staticPageRouter.js +134 -0
  413. package/server/routes/sticker/catalogHandlers/catalogAdminHttp.js +105 -0
  414. package/server/routes/sticker/catalogHandlers/catalogAuthHttp.js +77 -0
  415. package/server/routes/sticker/catalogHandlers/catalogPublicHttp.js +120 -0
  416. package/server/routes/sticker/catalogHandlers/catalogUploadHttp.js +83 -0
  417. package/server/routes/sticker/catalogRouter.js +77 -0
  418. package/server/routes/sticker/stickerApiRouter.js +84 -0
  419. package/server/routes/sticker/stickerDataRouter.js +145 -0
  420. package/server/routes/sticker/stickerSiteRouter.js +43 -0
  421. package/server/routes/user/userApiPaths.js +66 -0
  422. package/server/routes/user/userRouter.js +65 -0
  423. package/server/utils/safePath.js +26 -0
  424. package/utils/logger/loggerModule.js +35 -0
  425. package/vite.config.mjs +38 -0
@@ -0,0 +1,699 @@
1
+ import 'dotenv/config';
2
+
3
+ import { isAdminCommand } from '../modules/adminModule/groupCommandHandlers.js';
4
+ import { explicarComandoGlobal, registerGlobalHelpCommandExecution } from '../services/ai/globalModuleAiHelpService.js';
5
+ import { extractSupportedStickerMediaDetails, processSticker } from '../modules/stickerModule/stickerCommand.js';
6
+ import { detectAllMediaTypes, extractMessageContent, getExpiration, getJidServer, isGroupJid, isStatusJid, isSameJidUser, normalizeJid, normalizeWAPresence, resolveBotJid, extractSenderInfoFromMessage, resolveUserId, resolveAddressingModeFromMessageKey, resolveCanonicalWhatsAppJid, parseEnvBool, parseEnvInt } from '../config/index.js';
7
+ import { isUserAdmin } from '../config/index.js';
8
+ import { isAdminSenderAsync } from '../config/index.js';
9
+ import { executeQuery, TABLES } from '../../database/index.js';
10
+ import logger from '#logger';
11
+ import { handleAntiLink } from '../utils/antiLink/antiLinkModule.js';
12
+ import { maybeCaptureIncomingSticker } from '../modules/stickerPackModule/stickerPackCommandHandlers.js';
13
+ import groupConfigStore from '../store/groupConfigStore.js';
14
+ import { sendAndStore } from '../configParts/messagePersistenceService.js';
15
+ import { resolveCaptchaByMessage } from '../services/messaging/captchaService.js';
16
+ import { buildWhatsAppGoogleLoginUrl } from '../services/auth/whatsappLoginLinkService.js';
17
+ import { isWhatsAppUserLinkedToGoogleWebAccount } from '../services/auth/googleWebLinkService.js';
18
+ import { createMessageAnalysisEvent } from '../modules/analyticsModule/messageAnalysisEventRepository.js';
19
+ import { routeConversationMessage } from '../services/ai/conversationRouterService.js';
20
+ import { executeMessageCommandRoute, isKnownNonAdminCommand } from '../services/ai/messageCommandExecutionService.js';
21
+ import { canSendMessageInStickerFocus, registerMessageUsageInStickerFocus, resolveStickerFocusMessageClassification, resolveStickerFocusState, shouldSendStickerFocusWarning } from '../services/sticker/stickerFocusService.js';
22
+ import { createPreProcessingMiddlewares } from './messagePipeline/preProcessingMiddlewares.js';
23
+ import { createConversationMiddleware } from './messagePipeline/conversationMiddleware.js';
24
+ import { createCommandMiddleware } from './messagePipeline/commandMiddleware.js';
25
+ import { createPostProcessingMiddleware } from './messagePipeline/postProcessingMiddleware.js';
26
+
27
+ const DEFAULT_COMMAND_PREFIX = process.env.COMMAND_PREFIX || '/';
28
+ const COMMAND_REACT_EMOJI = process.env.COMMAND_REACT_EMOJI || '🤖';
29
+ const START_LOGIN_TRIGGER =
30
+ String(process.env.WHATSAPP_LOGIN_TRIGGER || 'iniciar')
31
+ .trim()
32
+ .toLowerCase() || 'iniciar';
33
+ const MESSAGE_ANALYTICS_ENABLED = parseEnvBool(process.env.MESSAGE_ANALYTICS_ENABLED, true);
34
+ const MESSAGE_ANALYTICS_SOURCE =
35
+ String(process.env.MESSAGE_ANALYTICS_SOURCE || 'whatsapp')
36
+ .trim()
37
+ .slice(0, 32) || 'whatsapp';
38
+ const MESSAGE_COMMAND_DEDUPE_TTL_MS = parseEnvInt(process.env.MESSAGE_COMMAND_DEDUPE_TTL_MS, 120_000, 15_000, 30 * 60 * 1000);
39
+ const MESSAGE_REPLY_PRESENCE_BEFORE = normalizeWAPresence(process.env.MESSAGE_REPLY_PRESENCE_BEFORE, 'composing');
40
+ const MESSAGE_REPLY_PRESENCE_AFTER = normalizeWAPresence(process.env.MESSAGE_REPLY_PRESENCE_AFTER, 'paused');
41
+ const MESSAGE_REPLY_PRESENCE_DELAY_MS = parseEnvInt(process.env.MESSAGE_REPLY_PRESENCE_DELAY_MS, 280, 0, 3_000);
42
+ const MESSAGE_REPLY_PRESENCE_SUBSCRIBE = parseEnvBool(process.env.MESSAGE_REPLY_PRESENCE_SUBSCRIBE, true);
43
+ const WHATSAPP_COMMAND_REQUIRES_GOOGLE_LOGIN = parseEnvBool(process.env.WHATSAPP_COMMAND_REQUIRES_GOOGLE_LOGIN, true);
44
+ const SITE_ORIGIN =
45
+ String(process.env.SITE_ORIGIN || process.env.WHATSAPP_LOGIN_BASE_URL || 'https://omnizap.shop')
46
+ .trim()
47
+ .replace(/\/+$/, '') || 'https://omnizap.shop';
48
+ const SITE_LOGIN_URL = `${SITE_ORIGIN}/login/`;
49
+ const SITE_GROUP_LOGIN_URL = `${SITE_ORIGIN}/login`;
50
+
51
+ let messageAnalyticsTableMissingLogged = false;
52
+ const recentCommandExecutions = new Map();
53
+
54
+ const pruneRecentCommandExecutions = (nowMs = Date.now()) => {
55
+ for (const [cacheKey, expiresAt] of recentCommandExecutions.entries()) {
56
+ if (expiresAt <= nowMs) {
57
+ recentCommandExecutions.delete(cacheKey);
58
+ }
59
+ }
60
+ };
61
+
62
+ const buildCommandExecutionCacheKey = (chatId, messageId) => {
63
+ const normalizedChatId = String(chatId || '').trim();
64
+ const normalizedMessageId = String(messageId || '').trim();
65
+ if (!normalizedChatId || !normalizedMessageId) return '';
66
+ return `${normalizedChatId}:${normalizedMessageId}`;
67
+ };
68
+
69
+ const isDuplicateCommandExecution = (chatId, messageId) => {
70
+ const cacheKey = buildCommandExecutionCacheKey(chatId, messageId);
71
+ if (!cacheKey) return false;
72
+
73
+ const nowMs = Date.now();
74
+ const expiresAt = recentCommandExecutions.get(cacheKey) || 0;
75
+ if (expiresAt <= nowMs) {
76
+ recentCommandExecutions.delete(cacheKey);
77
+ return false;
78
+ }
79
+
80
+ return true;
81
+ };
82
+
83
+ const markCommandExecution = (chatId, messageId) => {
84
+ const cacheKey = buildCommandExecutionCacheKey(chatId, messageId);
85
+ if (!cacheKey) return;
86
+ pruneRecentCommandExecutions(Date.now());
87
+ recentCommandExecutions.set(cacheKey, Date.now() + MESSAGE_COMMAND_DEDUPE_TTL_MS);
88
+ };
89
+
90
+ const normalizeTriggerText = (value) =>
91
+ String(value || '')
92
+ .trim()
93
+ .toLowerCase()
94
+ .normalize('NFD')
95
+ .replace(/[\u0300-\u036f]/g, '');
96
+
97
+ const isStartLoginTrigger = (text) => normalizeTriggerText(text) === START_LOGIN_TRIGGER;
98
+
99
+ const resolveCanonicalSenderJidFromMessage = async ({ messageInfo, senderJid }) => {
100
+ const key = messageInfo?.key || {};
101
+ const senderInfo = extractSenderInfoFromMessage(messageInfo);
102
+ let canonicalUserId = resolveCanonicalWhatsAppJid(senderInfo?.jid, senderInfo?.remoteJidAlt, senderInfo?.participantAlt, key.remoteJidAlt, key.participantAlt, key.participant, key.remoteJid, senderJid);
103
+
104
+ try {
105
+ const resolvedUserId = await resolveUserId(senderInfo);
106
+ canonicalUserId = resolveCanonicalWhatsAppJid(resolvedUserId, canonicalUserId, senderInfo?.jid, senderInfo?.remoteJidAlt, senderInfo?.participantAlt);
107
+ } catch (error) {
108
+ logger.warn('Falha ao resolver ID canonico do remetente.', {
109
+ action: 'resolve_sender_canonical_id_failed',
110
+ error: error?.message,
111
+ });
112
+ }
113
+
114
+ return canonicalUserId;
115
+ };
116
+
117
+ const resolveSenderContext = async ({ messageInfo, isGroupMessage, remoteJid }) => {
118
+ const key = messageInfo?.key || {};
119
+ const senderInfo = extractSenderInfoFromMessage(messageInfo);
120
+ let resolvedUserId = '';
121
+
122
+ try {
123
+ const resolved = await resolveUserId(senderInfo);
124
+ resolvedUserId = String(resolved || '').trim();
125
+ } catch (error) {
126
+ logger.debug('Falha ao resolver senderId via lidMapService.', {
127
+ action: 'resolve_sender_context_failed',
128
+ error: error?.message,
129
+ });
130
+ }
131
+
132
+ const senderJidCandidates = isGroupMessage ? [resolvedUserId, senderInfo?.jid, senderInfo?.participantAlt, key.participantAlt, key.participant, key.remoteJidAlt, remoteJid] : [resolvedUserId, senderInfo?.jid, senderInfo?.participantAlt, key.remoteJidAlt, key.remoteJid, remoteJid];
133
+
134
+ const canonicalSender = resolveCanonicalWhatsAppJid(...senderJidCandidates);
135
+ const fallbackSender = senderJidCandidates.find((candidate) => String(candidate || '').trim()) || '';
136
+ const senderJid = canonicalSender || String(fallbackSender || '').trim();
137
+ const addressingMode = resolveAddressingModeFromMessageKey(key, senderInfo);
138
+
139
+ const senderIdentity = isGroupMessage
140
+ ? {
141
+ participant: key?.participant || null,
142
+ participantAlt: key?.participantAlt || null,
143
+ jid: senderJid || null,
144
+ }
145
+ : senderJid;
146
+
147
+ return {
148
+ senderJid,
149
+ senderIdentity,
150
+ senderInfo,
151
+ resolvedUserId,
152
+ addressingMode,
153
+ };
154
+ };
155
+
156
+ const sendReply = (sock, remoteJid, messageInfo, expirationMessage, content, options = {}) =>
157
+ sendAndStore(sock, remoteJid, content, {
158
+ quoted: messageInfo,
159
+ ephemeralExpiration: expirationMessage,
160
+ presenceBefore: MESSAGE_REPLY_PRESENCE_BEFORE,
161
+ presenceAfter: MESSAGE_REPLY_PRESENCE_AFTER,
162
+ presenceDelayMs: MESSAGE_REPLY_PRESENCE_DELAY_MS,
163
+ presenceSubscribe: MESSAGE_REPLY_PRESENCE_SUBSCRIBE,
164
+ ...(options || {}),
165
+ });
166
+
167
+ const maybeHandleStartLoginMessage = async ({ sock, messageInfo, extractedText, senderName, senderJid, remoteJid, expirationMessage, isMessageFromBot, isGroupMessage }) => {
168
+ if (isMessageFromBot || !isStartLoginTrigger(extractedText)) return false;
169
+
170
+ if (isGroupMessage) {
171
+ await sendReply(sock, remoteJid, messageInfo, expirationMessage, {
172
+ text: 'Por seguranca, envie *iniciar* no privado do bot para receber seu link de login.',
173
+ });
174
+ return true;
175
+ }
176
+
177
+ const key = messageInfo?.key || {};
178
+ const senderInfo = extractSenderInfoFromMessage(messageInfo);
179
+ const canonicalUserId = await resolveCanonicalSenderJidFromMessage({ messageInfo, senderJid });
180
+
181
+ const loginUrl = buildWhatsAppGoogleLoginUrl({ userId: canonicalUserId });
182
+ if (!loginUrl) {
183
+ logger.warn('Nao foi possivel montar link de login para mensagem "iniciar".', {
184
+ action: 'login_link_missing_user_phone',
185
+ remoteServer: getJidServer(key.remoteJid || ''),
186
+ remoteAltServer: getJidServer(key.remoteJidAlt || ''),
187
+ participantServer: getJidServer(key.participant || ''),
188
+ participantAltServer: getJidServer(key.participantAlt || ''),
189
+ hasLid: Boolean(senderInfo?.lid),
190
+ hasJid: Boolean(senderInfo?.jid),
191
+ });
192
+ await sendReply(sock, remoteJid, messageInfo, expirationMessage, {
193
+ text: 'Nao consegui identificar seu numero de WhatsApp para o login. Tente novamente em alguns segundos.',
194
+ });
195
+ return true;
196
+ }
197
+
198
+ const safeName = String(senderName || '').trim();
199
+ const greeting = safeName ? `Oi, *${safeName}*!` : 'Oi!';
200
+ await sendReply(sock, remoteJid, messageInfo, expirationMessage, {
201
+ text: `${greeting}\n\n` + 'Para continuar no OmniZap, faca login com Google neste link:\n' + `${loginUrl}\n\n` + 'Seu numero do WhatsApp sera vinculado automaticamente a conta logada.',
202
+ });
203
+
204
+ return true;
205
+ };
206
+
207
+ /**
208
+ * Resolve o prefixo de comandos.
209
+ * Usa o prefixo do grupo quando existir, senão usa o padrão.
210
+ */
211
+ const resolveCommandPrefix = async (isGroupMessage, remoteJid, groupConfig = null) => {
212
+ if (!isGroupMessage) return DEFAULT_COMMAND_PREFIX;
213
+ const config = groupConfig || (await groupConfigStore.getGroupConfig(remoteJid));
214
+ if (!config || typeof config.commandPrefix !== 'string') {
215
+ return DEFAULT_COMMAND_PREFIX;
216
+ }
217
+ const prefix = config.commandPrefix.trim();
218
+ return prefix || DEFAULT_COMMAND_PREFIX;
219
+ };
220
+
221
+ const resolveBotIdentityCandidates = (sockUser = {}) => {
222
+ const candidates = new Set();
223
+ const addCandidate = (value) => {
224
+ const normalized = normalizeJid(String(value || '').trim());
225
+ if (!normalized) return;
226
+ candidates.add(normalized);
227
+ };
228
+
229
+ addCandidate(sockUser?.id);
230
+ addCandidate(sockUser?.jid);
231
+ addCandidate(sockUser?.lid);
232
+ addCandidate(resolveBotJid(sockUser?.id));
233
+
234
+ return Array.from(candidates);
235
+ };
236
+
237
+ const formatRemainingMinutesLabel = (remainingMs) => {
238
+ const safeMs = Math.max(0, Number(remainingMs) || 0);
239
+ const remainingMinutes = Math.ceil(safeMs / (60 * 1000));
240
+ return Math.max(1, remainingMinutes);
241
+ };
242
+
243
+ const formatStickerFocusRuleLabel = ({ messageAllowanceCount, messageCooldownMinutes }) => {
244
+ const allowanceCount = Math.max(1, Math.floor(Number(messageAllowanceCount) || 1));
245
+ const cooldownMinutes = Math.max(1, Math.floor(Number(messageCooldownMinutes) || 1));
246
+ const messageLabel = allowanceCount === 1 ? 'mensagem' : 'mensagens';
247
+ return `${allowanceCount} ${messageLabel} a cada ${cooldownMinutes} min`;
248
+ };
249
+
250
+ /**
251
+ * Executa um comando com tratamento de erro.
252
+ * Captura erros síncronos e promessas rejeitadas.
253
+ */
254
+ const runCommand = (label, handler) => {
255
+ try {
256
+ return Promise.resolve(handler())
257
+ .then(() => ({ ok: true }))
258
+ .catch((error) => {
259
+ logger.error(`Erro ao executar comando ${label}:`, error?.message);
260
+ return { ok: false, error };
261
+ });
262
+ } catch (error) {
263
+ logger.error(`Erro ao executar comando ${label}:`, error?.message);
264
+ return Promise.resolve({ ok: false, error });
265
+ }
266
+ };
267
+
268
+ const normalizeMessageKind = (mediaEntries, extractedText) => {
269
+ if (Array.isArray(mediaEntries) && mediaEntries.length > 0) {
270
+ const primaryType =
271
+ String(mediaEntries[0]?.mediaType || '')
272
+ .trim()
273
+ .toLowerCase() || 'media';
274
+ return primaryType.slice(0, 48);
275
+ }
276
+
277
+ const safeText = String(extractedText || '').trim();
278
+ if (!safeText || safeText === 'Mensagem vazia') return 'empty';
279
+ if (safeText.startsWith('[') && safeText.endsWith(']')) {
280
+ return safeText.slice(1, -1).trim().toLowerCase().replace(/\s+/g, '_').slice(0, 48);
281
+ }
282
+ return 'text';
283
+ };
284
+
285
+ const normalizeAnalysisErrorCode = (error) =>
286
+ String(error?.code || error?.name || 'processing_error')
287
+ .trim()
288
+ .toLowerCase()
289
+ .replace(/[^a-z0-9_-]/g, '_')
290
+ .slice(0, 96) || 'processing_error';
291
+
292
+ const buildCommandErrorHelpText = async ({ command, commandRoute, commandPrefix, isGroupMessage, isSenderAdmin, isSenderOwner }) => {
293
+ const normalizedCommand = String(command || '')
294
+ .trim()
295
+ .toLowerCase();
296
+ if (!normalizedCommand) return '';
297
+
298
+ if (commandRoute === 'unknown') {
299
+ return '';
300
+ }
301
+
302
+ try {
303
+ const helpResult = await explicarComandoGlobal(normalizedCommand, {
304
+ commandPrefix,
305
+ isGroupMessage,
306
+ isSenderAdmin,
307
+ isSenderOwner,
308
+ });
309
+ return String(helpResult?.text || '').trim();
310
+ } catch (error) {
311
+ logger.warn('Falha ao gerar ajuda IA para erro de comando.', {
312
+ action: 'command_error_ai_help_failed',
313
+ command: normalizedCommand,
314
+ commandRoute,
315
+ error: error?.message,
316
+ });
317
+ return '';
318
+ }
319
+ };
320
+
321
+ const persistMessageAnalysisEvent = (payload) => {
322
+ if (!MESSAGE_ANALYTICS_ENABLED) return;
323
+ void createMessageAnalysisEvent(payload).catch((error) => {
324
+ if (error?.code === 'ER_NO_SUCH_TABLE') {
325
+ if (messageAnalyticsTableMissingLogged) return;
326
+ messageAnalyticsTableMissingLogged = true;
327
+ logger.warn('Tabela de analytics de mensagens ainda não existe. Execute a migracao 20260301_0028.', {
328
+ action: 'message_analysis_table_missing',
329
+ });
330
+ return;
331
+ }
332
+
333
+ logger.warn('Falha ao persistir analytics de mensagem.', {
334
+ action: 'message_analysis_insert_failed',
335
+ error: error?.message,
336
+ });
337
+ });
338
+ };
339
+
340
+ const buildSiteLoginUrlForUser = (canonicalUserId) => buildWhatsAppGoogleLoginUrl({ userId: canonicalUserId }) || SITE_LOGIN_URL;
341
+
342
+ const ensureUserHasGoogleWebLoginForCommand = async ({ sock, messageInfo, senderJid, remoteJid, expirationMessage, commandPrefix, canonicalUserId = '', knownHasGoogleLogin } = {}) => {
343
+ const isGroupMessage = isGroupJid(remoteJid);
344
+ const normalizedCanonicalUserId = String(canonicalUserId || '').trim();
345
+ const resolvedCanonicalUserId = normalizedCanonicalUserId || (await resolveCanonicalSenderJidFromMessage({ messageInfo, senderJid }));
346
+ const hasKnownGoogleLogin = typeof knownHasGoogleLogin === 'boolean';
347
+ let linked = Boolean(knownHasGoogleLogin ? knownHasGoogleLogin : false);
348
+
349
+ if (!hasKnownGoogleLogin) {
350
+ try {
351
+ linked = await isWhatsAppUserLinkedToGoogleWebAccount({
352
+ ownerJid: resolvedCanonicalUserId || senderJid,
353
+ });
354
+ } catch (error) {
355
+ logger.warn('Falha ao validar vínculo Google Web para comando do WhatsApp. Comando liberado por fallback.', {
356
+ action: 'whatsapp_command_google_link_check_failed',
357
+ error: error?.message,
358
+ });
359
+ return {
360
+ allowed: true,
361
+ canonicalUserId: resolvedCanonicalUserId,
362
+ loginUrl: '',
363
+ };
364
+ }
365
+ }
366
+
367
+ if (linked) {
368
+ return {
369
+ allowed: true,
370
+ canonicalUserId: resolvedCanonicalUserId,
371
+ loginUrl: '',
372
+ };
373
+ }
374
+
375
+ const loginUrl = isGroupMessage ? SITE_GROUP_LOGIN_URL : buildSiteLoginUrlForUser(resolvedCanonicalUserId || senderJid);
376
+ const loginMessage = isGroupMessage ? `Para usar os comandos do bot, você precisa estar logado no site com sua conta Google.\n\nAcesse:\n${loginUrl}` : `Para usar os comandos do bot, você precisa estar logado no site com sua conta Google.\n\nCadastre-se / faça login em:\n${loginUrl}\n\nDepois volte aqui e envie o comando novamente (ex.: ${commandPrefix}menu).`;
377
+
378
+ await sendReply(sock, remoteJid, messageInfo, expirationMessage, {
379
+ text: loginMessage,
380
+ });
381
+
382
+ return {
383
+ allowed: false,
384
+ canonicalUserId: resolvedCanonicalUserId,
385
+ loginUrl,
386
+ };
387
+ };
388
+
389
+ const mergeAnalysisMetadata = (analysisPayload, metadataPatch = {}) => {
390
+ if (!metadataPatch || typeof metadataPatch !== 'object') return;
391
+ analysisPayload.metadata = {
392
+ ...analysisPayload.metadata,
393
+ ...metadataPatch,
394
+ };
395
+ };
396
+
397
+ const stopMessagePipeline = (ctx, processingResult = '', metadataPatch = null) => {
398
+ if (processingResult) {
399
+ ctx.analysisPayload.processingResult = processingResult;
400
+ }
401
+ if (metadataPatch) {
402
+ mergeAnalysisMetadata(ctx.analysisPayload, metadataPatch);
403
+ }
404
+ ctx.pipelineStopped = true;
405
+ return { stop: true };
406
+ };
407
+
408
+ const ensureGroupConfigForContext = async (ctx) => {
409
+ if (!ctx.isGroupMessage) return null;
410
+ if (ctx.groupConfigLoaded) return ctx.groupConfig;
411
+ ctx.groupConfigLoaded = true;
412
+ ctx.groupConfig = await groupConfigStore.getGroupConfig(ctx.remoteJid);
413
+ return ctx.groupConfig;
414
+ };
415
+
416
+ const ensureCommandPrefixForContext = async (ctx) => {
417
+ if (!ctx.isGroupMessage) {
418
+ ctx.commandPrefix = DEFAULT_COMMAND_PREFIX;
419
+ } else {
420
+ const activeGroupConfig = await ensureGroupConfigForContext(ctx);
421
+ ctx.commandPrefix = await resolveCommandPrefix(true, ctx.remoteJid, activeGroupConfig);
422
+ }
423
+ ctx.analysisPayload.commandPrefix = ctx.commandPrefix;
424
+ return ctx.commandPrefix;
425
+ };
426
+
427
+ const resolveCanonicalSenderJidForContext = async (ctx) => {
428
+ if (!ctx.memo.canonicalSenderJidPromise) {
429
+ ctx.memo.canonicalSenderJidPromise = resolveCanonicalSenderJidFromMessage({
430
+ messageInfo: ctx.messageInfo,
431
+ senderJid: ctx.senderJid,
432
+ });
433
+ }
434
+ return ctx.memo.canonicalSenderJidPromise;
435
+ };
436
+
437
+ const resolveSenderAdminForContext = async (ctx, { mode = 'identity' } = {}) => {
438
+ if (!ctx.isGroupMessage) return false;
439
+
440
+ const normalizedMode =
441
+ String(mode || '')
442
+ .trim()
443
+ .toLowerCase() === 'jid'
444
+ ? 'jid'
445
+ : 'identity';
446
+ const memoKey = normalizedMode === 'jid' ? 'senderAdminByJidPromise' : 'senderAdminByIdentityPromise';
447
+ if (!ctx.memo[memoKey]) {
448
+ const adminTarget = normalizedMode === 'jid' ? ctx.senderJid : ctx.senderIdentity;
449
+ ctx.memo[memoKey] = isUserAdmin(ctx.remoteJid, adminTarget);
450
+ }
451
+ return ctx.memo[memoKey];
452
+ };
453
+
454
+ const resolveSenderOwnerForContext = async (ctx) => {
455
+ if (!ctx.memo.senderOwnerPromise) {
456
+ ctx.memo.senderOwnerPromise = isAdminSenderAsync(ctx.senderIdentity);
457
+ }
458
+ return ctx.memo.senderOwnerPromise;
459
+ };
460
+
461
+ const resolveHasGoogleLoginForContext = async (ctx) => {
462
+ if (ctx.isMessageFromBot || !WHATSAPP_COMMAND_REQUIRES_GOOGLE_LOGIN) {
463
+ return undefined;
464
+ }
465
+
466
+ if (!ctx.memo.hasGoogleLoginPromise) {
467
+ ctx.memo.hasGoogleLoginPromise = (async () => {
468
+ const canonicalSenderJid = await resolveCanonicalSenderJidForContext(ctx);
469
+ if (!canonicalSenderJid) return undefined;
470
+ return isWhatsAppUserLinkedToGoogleWebAccount({
471
+ ownerJid: canonicalSenderJid,
472
+ });
473
+ })();
474
+ }
475
+ return ctx.memo.hasGoogleLoginPromise;
476
+ };
477
+
478
+ const createMessagePipelineContext = async ({ messageInfo, upsertType, isNotifyUpsert, sock }) => {
479
+ const key = messageInfo?.key || {};
480
+ const remoteJid = key?.remoteJid;
481
+ if (!remoteJid) return null;
482
+
483
+ const isGroupMessage = isGroupJid(remoteJid);
484
+ const extractedText = extractMessageContent(messageInfo);
485
+ const { senderJid, senderIdentity, addressingMode } = await resolveSenderContext({
486
+ messageInfo,
487
+ isGroupMessage,
488
+ remoteJid,
489
+ });
490
+
491
+ const senderName = messageInfo?.pushName;
492
+ const expirationMessage = getExpiration(messageInfo);
493
+ const botJidCandidates = resolveBotIdentityCandidates(sock?.user || {});
494
+ const botJid = botJidCandidates[0] || null;
495
+ const isMessageFromBot = Boolean(key?.fromMe) || botJidCandidates.some((candidate) => isSameJidUser(senderJid, candidate));
496
+ const mediaEntries = detectAllMediaTypes(messageInfo?.message, false);
497
+ const mediaTypes = mediaEntries
498
+ .map((entry) =>
499
+ String(entry?.mediaType || '')
500
+ .trim()
501
+ .toLowerCase(),
502
+ )
503
+ .filter(Boolean)
504
+ .slice(0, 10);
505
+
506
+ const analysisPayload = {
507
+ messageId: key?.id || null,
508
+ chatId: remoteJid || null,
509
+ senderId: senderJid || null,
510
+ senderName,
511
+ upsertType,
512
+ source: MESSAGE_ANALYTICS_SOURCE,
513
+ isGroup: isGroupMessage,
514
+ isFromBot: isMessageFromBot,
515
+ isCommand: false,
516
+ commandName: null,
517
+ commandArgsCount: 0,
518
+ commandKnown: null,
519
+ commandPrefix: DEFAULT_COMMAND_PREFIX,
520
+ messageKind: normalizeMessageKind(mediaEntries, extractedText),
521
+ hasMedia: mediaEntries.length > 0,
522
+ mediaCount: mediaEntries.length,
523
+ textLength: String(extractedText || '').length,
524
+ processingResult: 'processed',
525
+ errorCode: null,
526
+ metadata: {
527
+ media_types: mediaTypes,
528
+ start_login_trigger: isStartLoginTrigger(extractedText),
529
+ upsert_type: upsertType,
530
+ is_notify_upsert: isNotifyUpsert,
531
+ is_history_append: upsertType === 'append',
532
+ addressing_mode: addressingMode || null,
533
+ participant_alt: key?.participantAlt || null,
534
+ remote_jid_alt: key?.remoteJidAlt || null,
535
+ },
536
+ };
537
+
538
+ return {
539
+ sock,
540
+ messageInfo,
541
+ key,
542
+ remoteJid,
543
+ isGroupMessage,
544
+ extractedText,
545
+ senderJid,
546
+ senderIdentity,
547
+ senderName,
548
+ expirationMessage,
549
+ botJidCandidates,
550
+ botJid,
551
+ isMessageFromBot,
552
+ commandPrefix: DEFAULT_COMMAND_PREFIX,
553
+ groupConfig: null,
554
+ groupConfigLoaded: false,
555
+ mediaEntries,
556
+ upsertType,
557
+ isNotifyUpsert,
558
+ isCommandMessage: false,
559
+ hasCommandPrefix: false,
560
+ analysisPayload,
561
+ pipelineStopped: false,
562
+ memo: Object.create(null),
563
+ };
564
+ };
565
+
566
+ const { touchSenderLastSeenMiddleware, ignoreUnprocessableMessageMiddleware, applyGroupPolicyMiddleware, resolveCaptchaMiddleware, handleStartLoginTriggerMiddleware, detectCommandIntentMiddleware, applyStickerFocusMiddleware } = createPreProcessingMiddlewares({
567
+ executeQuery,
568
+ TABLES,
569
+ isStatusJid,
570
+ stopMessagePipeline,
571
+ handleAntiLink,
572
+ ensureCommandPrefixForContext,
573
+ resolveCaptchaByMessage,
574
+ maybeHandleStartLoginMessage,
575
+ mergeAnalysisMetadata,
576
+ ensureGroupConfigForContext,
577
+ resolveStickerFocusState,
578
+ resolveStickerFocusMessageClassification,
579
+ resolveSenderAdminForContext,
580
+ isUserAdmin,
581
+ canSendMessageInStickerFocus,
582
+ registerMessageUsageInStickerFocus,
583
+ shouldSendStickerFocusWarning,
584
+ sendReply,
585
+ formatStickerFocusRuleLabel,
586
+ formatRemainingMinutesLabel,
587
+ logger,
588
+ });
589
+
590
+ const routeConversationMiddleware = createConversationMiddleware({
591
+ logger,
592
+ resolveSenderAdminForContext,
593
+ resolveSenderOwnerForContext,
594
+ resolveHasGoogleLoginForContext,
595
+ isUserAdmin,
596
+ isAdminSenderAsync,
597
+ resolveCanonicalSenderJidForContext,
598
+ isWhatsAppUserLinkedToGoogleWebAccount,
599
+ WHATSAPP_COMMAND_REQUIRES_GOOGLE_LOGIN,
600
+ ensureUserHasGoogleWebLoginForCommand,
601
+ executeMessageCommandRoute,
602
+ isAdminCommand,
603
+ runCommand,
604
+ sendReply,
605
+ routeConversationMessage,
606
+ stopMessagePipeline,
607
+ });
608
+
609
+ const executeCommandMiddleware = createCommandMiddleware({
610
+ isAdminCommand,
611
+ isKnownNonAdminCommand,
612
+ isDuplicateCommandExecution,
613
+ markCommandExecution,
614
+ MESSAGE_COMMAND_DEDUPE_TTL_MS,
615
+ stopMessagePipeline,
616
+ WHATSAPP_COMMAND_REQUIRES_GOOGLE_LOGIN,
617
+ resolveCanonicalSenderJidForContext,
618
+ ensureUserHasGoogleWebLoginForCommand,
619
+ SITE_LOGIN_URL,
620
+ COMMAND_REACT_EMOJI,
621
+ sendAndStore,
622
+ executeMessageCommandRoute,
623
+ runCommand,
624
+ sendReply,
625
+ registerGlobalHelpCommandExecution,
626
+ logger,
627
+ normalizeAnalysisErrorCode,
628
+ resolveSenderAdminForContext,
629
+ isUserAdmin,
630
+ buildCommandErrorHelpText,
631
+ mergeAnalysisMetadata,
632
+ });
633
+
634
+ const runPostProcessingMiddleware = createPostProcessingMiddleware({
635
+ runCommand,
636
+ maybeCaptureIncomingSticker,
637
+ extractSupportedStickerMediaDetails,
638
+ ensureGroupConfigForContext,
639
+ mergeAnalysisMetadata,
640
+ processSticker,
641
+ normalizeAnalysisErrorCode,
642
+ });
643
+
644
+ const MESSAGE_PIPELINE_MIDDLEWARES = [touchSenderLastSeenMiddleware, ignoreUnprocessableMessageMiddleware, applyGroupPolicyMiddleware, resolveCaptchaMiddleware, handleStartLoginTriggerMiddleware, detectCommandIntentMiddleware, applyStickerFocusMiddleware, routeConversationMiddleware, executeCommandMiddleware, runPostProcessingMiddleware];
645
+
646
+ const runMessagePipeline = async (ctx) => {
647
+ for (const middleware of MESSAGE_PIPELINE_MIDDLEWARES) {
648
+ if (ctx.pipelineStopped) break;
649
+ const result = await middleware(ctx);
650
+ if (result?.stop) break;
651
+ }
652
+ };
653
+
654
+ /**
655
+ * Lida com atualizações do WhatsApp, sejam mensagens ou eventos genéricos.
656
+ *
657
+ * @param {Object} update - Objeto contendo a atualização do WhatsApp.
658
+ */
659
+ export const handleMessagesThroughPipeline = async (update, sock) => {
660
+ if (update.messages && Array.isArray(update.messages)) {
661
+ try {
662
+ const upsertType = update?.type || null;
663
+ const isNotifyUpsert = upsertType === 'notify';
664
+
665
+ for (const messageInfo of update.messages) {
666
+ const context = await createMessagePipelineContext({
667
+ messageInfo,
668
+ upsertType,
669
+ isNotifyUpsert,
670
+ sock,
671
+ });
672
+ if (!context) continue;
673
+
674
+ try {
675
+ await runMessagePipeline(context);
676
+ } catch (messageError) {
677
+ context.analysisPayload.processingResult = 'error';
678
+ context.analysisPayload.errorCode = normalizeAnalysisErrorCode(messageError);
679
+ logger.error('Erro ao processar mensagem individual:', {
680
+ error: messageError?.message,
681
+ messageId: context.key?.id || null,
682
+ remoteJid: context.remoteJid,
683
+ });
684
+ } finally {
685
+ persistMessageAnalysisEvent(context.analysisPayload);
686
+ }
687
+ }
688
+ } catch (error) {
689
+ logger.error('Erro ao processar mensagens:', error?.message);
690
+ }
691
+ } else {
692
+ logger.info('🔄 Processando evento recebido:', {
693
+ eventType: update?.type || 'unknown',
694
+ eventData: update,
695
+ });
696
+ }
697
+ };
698
+
699
+ export const handleMessages = handleMessagesThroughPipeline;