@omnizap-system/omnizap 2.6.0 → 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 (261) hide show
  1. package/.env.example +58 -13
  2. package/.github/workflows/ci.yml +5 -5
  3. package/.github/workflows/codeql.yml +1 -1
  4. package/.github/workflows/db-migration-check.yml +2 -2
  5. package/.github/workflows/dependency-review.yml +1 -1
  6. package/.github/workflows/deploy.yml +2 -2
  7. package/.github/workflows/release.yml +2 -2
  8. package/.github/workflows/security-attest-provenance.yml +2 -2
  9. package/.github/workflows/security-gitleaks.yml +13 -4
  10. package/.github/workflows/security-runner-hardening.yml +2 -2
  11. package/.github/workflows/security-scorecard.yml +1 -1
  12. package/.github/workflows/security-zap-baseline.yml +1 -1
  13. package/.github/workflows/security-zap-full-scan.yml +2 -1
  14. package/.github/workflows/security-zizmor.yml +1 -1
  15. package/.github/workflows/wiki-sync.yml +1 -1
  16. package/.gitleaksignore +9 -0
  17. package/CODE_OF_CONDUCT.md +2 -2
  18. package/GEMINI.md +64 -0
  19. package/README.md +52 -82
  20. package/SECURITY.md +1 -1
  21. package/app/config/index.js +2 -0
  22. package/app/configParts/adminIdentity.js +5 -5
  23. package/app/configParts/baileysConfig.js +230 -58
  24. package/app/configParts/groupUtils.js +5 -0
  25. package/app/configParts/messagePersistenceService.js +145 -4
  26. package/app/configParts/sessionConfig.js +157 -0
  27. package/app/connection/baileysCompatibility.test.js +1 -1
  28. package/app/connection/groupOwnerWriteStateResolver.js +109 -0
  29. package/app/connection/socketController.js +660 -158
  30. package/app/connection/socketController.multiSession.test.js +108 -0
  31. package/app/controllers/messageController.js +1 -1
  32. package/app/controllers/messagePipeline/commandMiddleware.js +12 -10
  33. package/app/controllers/messagePipeline/conversationMiddleware.js +2 -1
  34. package/app/controllers/messagePipeline/messagePipelineMiddlewares.test.js +104 -0
  35. package/app/controllers/messagePipeline/preProcessingMiddlewares.js +80 -2
  36. package/app/controllers/messageProcessingPipeline.js +93 -13
  37. package/app/controllers/messageProcessingPipeline.test.js +200 -0
  38. package/app/modules/adminModule/AGENT.md +1 -1
  39. package/app/modules/adminModule/commandConfig.json +3318 -1347
  40. package/app/modules/adminModule/groupCommandHandlers.js +858 -15
  41. package/app/modules/adminModule/groupCommandHandlers.test.js +378 -11
  42. package/app/modules/adminModule/groupWarningRepository.js +152 -0
  43. package/app/modules/aiModule/AGENT.md +47 -30
  44. package/app/modules/aiModule/aiConfigRuntime.js +1 -0
  45. package/app/modules/aiModule/catCommand.js +135 -27
  46. package/app/modules/aiModule/commandConfig.json +114 -28
  47. package/app/modules/analyticsModule/messageAnalysisEventRepository.js +54 -6
  48. package/app/modules/gameModule/AGENT.md +1 -1
  49. package/app/modules/gameModule/commandConfig.json +29 -0
  50. package/app/modules/menuModule/AGENT.md +1 -1
  51. package/app/modules/menuModule/commandConfig.json +45 -10
  52. package/app/modules/menuModule/menuCatalogService.js +190 -0
  53. package/app/modules/menuModule/menuCommandUsageRepository.js +109 -0
  54. package/app/modules/menuModule/menuDynamicService.js +511 -0
  55. package/app/modules/menuModule/menuDynamicService.test.js +141 -0
  56. package/app/modules/menuModule/menus.js +36 -5
  57. package/app/modules/playModule/AGENT.md +10 -5
  58. package/app/modules/playModule/commandConfig.json +140 -12
  59. package/app/modules/playModule/playCommand.js +1 -1417
  60. package/app/modules/playModule/playCommandConstants.js +80 -0
  61. package/app/modules/playModule/playCommandCore.js +361 -0
  62. package/app/modules/playModule/playCommandHandlers.js +41 -0
  63. package/app/modules/playModule/playCommandMediaClient.js +1872 -0
  64. package/app/modules/playModule/playConfigRuntime.js +245 -4
  65. package/app/modules/playModule/playModuleCriticalFlows.test.js +152 -0
  66. package/app/modules/quoteModule/AGENT.md +1 -1
  67. package/app/modules/quoteModule/commandConfig.json +29 -0
  68. package/app/modules/quoteModule/quoteCommand.js +3 -2
  69. package/app/modules/rpgPokemonModule/AGENT.md +1 -1
  70. package/app/modules/rpgPokemonModule/commandConfig.json +29 -0
  71. package/app/modules/rpgPokemonModule/rpgBattleCanvasRenderer.js +5 -4
  72. package/app/modules/rpgPokemonModule/rpgBattleService.test.js +2 -1
  73. package/app/modules/rpgPokemonModule/rpgPokemonDomain.js +2 -1
  74. package/app/modules/rpgPokemonModule/rpgPokemonService.js +38 -37
  75. package/app/modules/rpgPokemonModule/rpgProfileCanvasRenderer.js +4 -3
  76. package/app/modules/statsModule/AGENT.md +1 -1
  77. package/app/modules/statsModule/commandConfig.json +58 -0
  78. package/app/modules/statsModule/rankingCommon.js +5 -4
  79. package/app/modules/stickerModule/AGENT.md +1 -1
  80. package/app/modules/stickerModule/addStickerMetadata.js +4 -3
  81. package/app/modules/stickerModule/commandConfig.json +145 -0
  82. package/app/modules/stickerModule/stickerCommand.js +1 -1
  83. package/app/modules/stickerPackModule/AGENT.md +1 -1
  84. package/app/modules/stickerPackModule/autoPackCollectorService.js +5 -1
  85. package/app/modules/stickerPackModule/commandConfig.json +29 -0
  86. package/app/modules/stickerPackModule/semanticThemeClusterService.js +7 -6
  87. package/app/modules/stickerPackModule/stickerAutoPackByTagsRuntime.js +10 -9
  88. package/app/modules/stickerPackModule/stickerClassificationBackgroundRuntime.js +9 -8
  89. package/app/modules/stickerPackModule/stickerDomainEventConsumerRuntime.js +3 -2
  90. package/app/modules/stickerPackModule/stickerMarketplaceDriftService.js +2 -1
  91. package/app/modules/stickerPackModule/stickerPackCommandHandlers.js +80 -58
  92. package/app/modules/stickerPackModule/stickerPackMarketplaceService.js +2 -1
  93. package/app/modules/stickerPackModule/stickerPackRepository.js +2 -1
  94. package/app/modules/stickerPackModule/stickerPackScoreSnapshotRuntime.js +5 -4
  95. package/app/modules/stickerPackModule/stickerPackService.js +13 -6
  96. package/app/modules/stickerPackModule/stickerStorageService.js +3 -2
  97. package/app/modules/stickerPackModule/stickerWorkerPipelineRuntime.js +2 -1
  98. package/app/modules/systemMetricsModule/AGENT.md +1 -1
  99. package/app/modules/systemMetricsModule/commandConfig.json +29 -0
  100. package/app/modules/systemMetricsModule/pingCommand.js +6 -5
  101. package/app/modules/tiktokModule/AGENT.md +1 -1
  102. package/app/modules/tiktokModule/commandConfig.json +29 -0
  103. package/app/modules/tiktokModule/tiktokCommand.js +2 -1
  104. package/app/modules/userModule/AGENT.md +1 -1
  105. package/app/modules/userModule/commandConfig.json +29 -0
  106. package/app/modules/userModule/userCommand.js +72 -23
  107. package/app/modules/waifuPicsModule/AGENT.md +57 -27
  108. package/app/modules/waifuPicsModule/commandConfig.json +87 -0
  109. package/app/modules/waifuPicsModule/waifuPicsCommand.js +3 -2
  110. package/app/observability/metrics.js +136 -0
  111. package/app/services/ai/commandConfigEnrichmentService.js +229 -47
  112. package/app/services/ai/conversationRouterService.js +4 -3
  113. package/app/services/ai/geminiService.js +132 -7
  114. package/app/services/ai/geminiService.test.js +59 -2
  115. package/app/services/ai/globalModuleAiHelpService.js +3 -2
  116. package/app/services/ai/messageCommandExecutionService.js +2 -1
  117. package/app/services/ai/moduleAiHelpCoreService.js +45 -14
  118. package/app/services/ai/moduleToolExecutorService.js +3 -2
  119. package/app/services/ai/moduleToolRegistryService.js +2 -1
  120. package/app/services/ai/toolCandidateSelectorService.js +6 -5
  121. package/app/services/auth/googleWebLinkService.js +3 -2
  122. package/app/services/auth/whatsappLoginLinkService.js +3 -2
  123. package/app/services/external/pokeApiService.js +4 -3
  124. package/app/services/group/groupMetadataService.js +24 -1
  125. package/app/services/infra/dbWriteQueue.js +57 -26
  126. package/app/services/infra/featureFlagService.js +2 -1
  127. package/app/services/messaging/captchaService.js +3 -2
  128. package/app/services/messaging/newsBroadcastService.js +846 -29
  129. package/app/services/multiSession/assignmentBalancerService.js +457 -0
  130. package/app/services/multiSession/groupOwnershipRepository.js +381 -0
  131. package/app/services/multiSession/groupOwnershipService.js +890 -0
  132. package/app/services/multiSession/groupOwnershipService.test.js +309 -0
  133. package/app/services/multiSession/sessionRegistryService.js +293 -0
  134. package/app/services/sticker/stickerFocusService.js +11 -10
  135. package/app/store/aiPromptStore.js +36 -19
  136. package/app/store/conversationSessionStore.js +7 -6
  137. package/app/store/groupConfigStore.js +41 -5
  138. package/app/store/premiumUserStore.js +21 -7
  139. package/app/utils/antiLink/antiLinkModule.js +352 -16
  140. package/app/workers/aiHelperContinuousLearningWorker.js +512 -0
  141. package/app/workers/aiLearningWorker.js +6 -5
  142. package/app/workers/commandConfigEnrichmentWorker.js +4 -3
  143. package/database/index.js +14 -8
  144. package/database/migrations/20260307_d0_hardening_down.sql +1 -1
  145. package/database/migrations/20260314_d7_canonical_sender_down.sql +1 -1
  146. package/database/migrations/20260406_d30_security_analytics_down.sql +1 -1
  147. package/database/migrations/20260411_d35_group_community_metadata_down.sql +59 -0
  148. package/database/migrations/20260411_d35_group_community_metadata_up.sql +62 -0
  149. package/database/migrations/20260412_d36_system_config_tables_down.sql +32 -0
  150. package/database/migrations/20260412_d36_system_config_tables_up.sql +66 -0
  151. package/database/migrations/20260413_d37_group_user_warnings_down.sql +11 -0
  152. package/database/migrations/20260413_d37_group_user_warnings_up.sql +24 -0
  153. package/database/migrations/20260414_d38_multi_session_foundation_down.sql +72 -0
  154. package/database/migrations/20260414_d38_multi_session_foundation_up.sql +125 -0
  155. package/database/migrations/20260414_d39_multi_session_cutover_down.sql +103 -0
  156. package/database/migrations/20260414_d39_multi_session_cutover_up.sql +83 -0
  157. package/database/schema.sql +102 -1
  158. package/docker-compose.yml +4 -1
  159. package/docs/compliance/acceptable-use-policy-2026-03-07.md +1 -1
  160. package/docs/compliance/dpa-b2b-standard-2026-03-07.md +1 -1
  161. package/docs/compliance/privacy-policy-2026-03-07.md +4 -4
  162. package/docs/security/dsar-lgpd-runbook-2026-03-07.md +1 -1
  163. package/docs/security/incident-response-lgpd-anpd-runbook-2026-03-07.md +1 -1
  164. package/docs/security/network-hardening-runbook-2026-03-07.md +53 -0
  165. package/docs/security/omnizap-static-security-headers.conf +25 -0
  166. package/docs/wiki/Home.md +1 -1
  167. package/ecosystem.prod.config.cjs +32 -12
  168. package/index.js +57 -23
  169. package/observability/alert-rules.yml +20 -0
  170. package/observability/grafana/dashboards/omnizap-system-admin.json +229 -0
  171. package/observability/mysql-setup.sql +4 -4
  172. package/observability/system-admin-observability.md +26 -0
  173. package/package.json +20 -6
  174. package/public/apple-touch-icon.png +0 -0
  175. package/public/comandos/commands-catalog.json +2853 -3326
  176. package/public/favicon-16x16.png +0 -0
  177. package/public/favicon-32x32.png +0 -0
  178. package/public/favicon.ico +0 -0
  179. package/public/js/apps/apiDocsApp.js +3 -2
  180. package/public/js/apps/commandsReactApp.js +280 -99
  181. package/public/js/apps/createPackApp.js +11 -10
  182. package/public/js/apps/homeReactApp.js +181 -130
  183. package/public/js/apps/loginReactApp.js +1 -1
  184. package/public/js/apps/stickersApp.js +263 -110
  185. package/public/js/apps/termsReactApp.js +73 -24
  186. package/public/js/apps/userApp.js +4 -3
  187. package/public/js/apps/userPasswordResetReactApp.js +406 -0
  188. package/public/js/apps/userReactApp.js +355 -280
  189. package/public/js/apps/userSystemAdmReactApp.js +1506 -0
  190. package/public/pages/api-docs.html +1 -1
  191. package/public/pages/aup.html +2 -2
  192. package/public/pages/dpa.html +3 -3
  193. package/public/pages/licenca.html +4 -4
  194. package/public/pages/login.html +1 -1
  195. package/public/pages/notice-and-takedown.html +2 -2
  196. package/public/pages/politica-de-privacidade.html +6 -6
  197. package/public/pages/seo-bot-whatsapp-para-grupo.html +3 -3
  198. package/public/pages/seo-bot-whatsapp-sem-programar.html +3 -3
  199. package/public/pages/seo-como-automatizar-avisos-no-whatsapp.html +3 -3
  200. package/public/pages/seo-como-criar-comandos-whatsapp.html +3 -3
  201. package/public/pages/seo-como-evitar-spam-no-whatsapp.html +3 -3
  202. package/public/pages/seo-como-moderar-grupo-whatsapp.html +3 -3
  203. package/public/pages/seo-como-organizar-comunidade-whatsapp.html +3 -3
  204. package/public/pages/seo-melhor-bot-whatsapp-para-grupos.html +3 -3
  205. package/public/pages/stickers-admin.html +1 -1
  206. package/public/pages/stickers-create.html +1 -1
  207. package/public/pages/stickers.html +6 -6
  208. package/public/pages/suboperadores.html +2 -2
  209. package/public/pages/termos-de-uso-texto-integral.html +6 -6
  210. package/public/pages/termos-de-uso.html +3 -3
  211. package/public/pages/user-password-reset.html +4 -5
  212. package/public/pages/user-systemadm.html +9 -463
  213. package/public/pages/user.html +2 -2
  214. package/scripts/clear-whatsapp-session.sh +123 -0
  215. package/scripts/core-ai-mode.mjs +163 -0
  216. package/scripts/deploy.sh +11 -1
  217. package/scripts/email-broadcast-terms-update.mjs +2 -1
  218. package/scripts/enrich-command-config-ux-openai.mjs +492 -0
  219. package/scripts/generate-commands-catalog.mjs +166 -2
  220. package/scripts/generate-module-agents.mjs +2 -1
  221. package/scripts/generate-seo-satellite-pages.mjs +5 -4
  222. package/scripts/github-deploy-notify.mjs +2 -1
  223. package/scripts/github-release-notify.mjs +25 -10
  224. package/scripts/new-whatsapp-session.sh +317 -0
  225. package/scripts/release.sh +2 -19
  226. package/scripts/security-smoketest.mjs +6 -5
  227. package/scripts/security-web-surface-check.mjs +218 -0
  228. package/scripts/sticker-catalog-loadtest.mjs +5 -4
  229. package/server/auth/googleWebAuth/googleWebAuthService.js +8 -7
  230. package/server/auth/jwt/webJwtService.js +1 -1
  231. package/server/auth/stickerCatalogAuthContext.js +2 -1
  232. package/server/auth/termsAcceptance/termsAcceptanceHandler.js +2 -1
  233. package/server/auth/userPassword/userPasswordAuthService.js +2 -1
  234. package/server/auth/userPassword/userPasswordRecoveryService.js +4 -3
  235. package/server/auth/webAccount/webAccountHandlers.js +9 -10
  236. package/server/controllers/admin/adminPanelHandlers.js +267 -16
  237. package/server/controllers/admin/systemAdminController.js +267 -0
  238. package/server/controllers/seo/stickerCatalogSeoContext.js +10 -9
  239. package/server/controllers/sticker/nonCatalogHandlers.js +2 -1
  240. package/server/controllers/sticker/stickerCatalogController.js +23 -36
  241. package/server/controllers/system/contactController.js +9 -17
  242. package/server/controllers/system/githubController.js +3 -2
  243. package/server/controllers/system/stickerCatalogSystemContext.js +41 -19
  244. package/server/controllers/system/systemController.js +254 -1
  245. package/server/controllers/system/systemMetricsController.js +2 -1
  246. package/server/controllers/userController.js +6 -0
  247. package/server/email/emailTemplateService.js +5 -3
  248. package/server/http/httpServer.js +11 -6
  249. package/server/middleware/rateLimit.js +2 -1
  250. package/server/middleware/securityHeaders.js +20 -1
  251. package/server/routes/admin/systemAdminRouter.js +6 -0
  252. package/server/routes/indexRouter.js +30 -6
  253. package/server/routes/observability/grafanaProxyRouter.js +254 -0
  254. package/server/routes/static/staticPageRouter.js +27 -1
  255. package/server/utils/publicContact.js +31 -0
  256. package/utils/time/timeModule.js +135 -0
  257. package/utils/time/timeModule.test.js +65 -0
  258. package/utils/whatsapp/contactEnv.js +39 -0
  259. package/vite.config.mjs +7 -1
  260. package/public/assets/images/brand-icon-192.png +0 -0
  261. package/scripts/sync-readme-snapshot.mjs +0 -133
@@ -357,6 +357,35 @@
357
357
  "schema": "legacy_v1_and_v2",
358
358
  "legacy_name": "figurinha",
359
359
  "legacy_fields_present": ["descricao", "metodos_de_uso", "permissao_necessaria", "local_de_uso", "informacoes_coletadas", "argumentos", "pre_condicoes", "dependencias_externas", "efeitos_colaterais", "observabilidade", "privacidade", "acesso", "limite_uso_por_plano"]
360
+ },
361
+ "user_experience": {
362
+ "resumo_usuario": "Converte imagem ou vídeo enviado em figurinha para o WhatsApp. Requer login Google e mídia. Disponível para planos comum e premium.",
363
+ "quando_usar": ["Quando você quer transformar uma foto ou vídeo enviada em uma figurinha para compartilhar no chat.", "Quando desejar adicionar texto opcional à figurinha com o texto após o comando.", "Quando usar o atalho <prefix>s para criar rapidamente a figurinha a partir da mídia recebida."],
364
+ "exemplos_reais": [
365
+ {
366
+ "situacao": "Transformar imagem recebida em figurinha (sem texto).",
367
+ "comando": "<prefix>figurinha",
368
+ "resposta_esperada": "Comando executado com sucesso.",
369
+ "variacao": "com imagem anexada."
370
+ },
371
+ {
372
+ "situacao": "Criar figurinha com texto personalizado.",
373
+ "comando": "<prefix>figurinha Olá amigos",
374
+ "resposta_esperada": "Comando executado com sucesso.",
375
+ "variacao": "texto_extra fornecido."
376
+ },
377
+ {
378
+ "situacao": "Usar atalho para vídeo e gerar figurinha.",
379
+ "comando": "<prefix>s",
380
+ "resposta_esperada": "Comando executado com sucesso.",
381
+ "variacao": "vídeo anexado."
382
+ }
383
+ ],
384
+ "resposta_esperada": ["Comando executado com sucesso.", "Formato de uso inválido. Consulte metodos_de_uso.", "Permissão insuficiente para executar este comando."],
385
+ "erros_comuns_usuario": ["Não há mídia anexada para transformar em figurinha.", "Formato de uso inválido. Use <prefix>figurinha ou <prefix>s.", "Não é possível converter conteúdo NSFW ou não aprovado.", "Você não está logado no Google ou a autenticação falhou."],
386
+ "passos_se_der_erro": ["Verifique se você enviou uma imagem ou vídeo junto ao comando.", "Tente novamente com <prefix>figurinha ou <prefix>s.", "Certifique-se de estar conectado com sua conta Google e que o login esteja ativo."],
387
+ "resumo_usuario_origem": "auto_ia_assistida",
388
+ "resumo_usuario_revisao_pendente": true
360
389
  }
361
390
  },
362
391
  {
@@ -553,6 +582,35 @@
553
582
  "schema": "legacy_v1_and_v2",
554
583
  "legacy_name": "paraimagem",
555
584
  "legacy_fields_present": ["descricao", "metodos_de_uso", "permissao_necessaria", "local_de_uso", "informacoes_coletadas", "argumentos", "pre_condicoes", "dependencias_externas", "efeitos_colaterais", "observabilidade", "privacidade", "acesso", "limite_uso_por_plano"]
585
+ },
586
+ "user_experience": {
587
+ "resumo_usuario": "Converte figurinha (sticker) para imagem ou vídeo, para você salvar/usar fora do WhatsApp. Disponível para planos comum e premium.",
588
+ "quando_usar": ["Quando você recebeu uma figurinha e quer salvá-la como imagem.", "Quando precisa de um vídeo a partir de uma figurinha (quando suportado).", "Quando quer reutilizar a figurinha em outros lugares ou projetos."],
589
+ "exemplos_reais": [
590
+ {
591
+ "situacao": "Você recebeu uma figurinha e quer salvar como imagem.",
592
+ "comando": "<prefix>paraimagem",
593
+ "resposta_esperada": "A imagem da figurinha é gerada e enviada de volta para você.",
594
+ "variacao": "paraimagem."
595
+ },
596
+ {
597
+ "situacao": "Precisa de um vídeo da figurinha (quando suportado).",
598
+ "comando": "<prefix>tovideo",
599
+ "resposta_esperada": "Vídeo da figurinha gerado e enviado.",
600
+ "variacao": "tovideo."
601
+ },
602
+ {
603
+ "situacao": "Erro ao enviar comando com formato errado.",
604
+ "comando": "<prefix>paraimagem extra",
605
+ "resposta_esperada": "Formato de uso inválido. Consulte metodos_de_uso.",
606
+ "variacao": "erro de formatação."
607
+ }
608
+ ],
609
+ "resposta_esperada": ["Imagem ou vídeo gerado a partir da figurinha enviada (quando disponível).", "Mensagens claras de erro se houver problemas de formato, permissão ou conteúdo."],
610
+ "erros_comuns_usuario": ["Formato de comando inválido ou sem figurinha na mensagem.", "Não há figurinha na mensagem para converter.", "Falha de login ou permissão inadequada (Google Login necessário).", "Tentativa de usar o comando sem indicar uma figurinha compatível."],
611
+ "passos_se_der_erro": ["Confirme que você está logado com Google.", "Envie uma figurinha válida e tente novamente com <prefix>paraimagem, <prefix>tovideo ou <prefix>tovid.", "Verifique se o prefixo do bot está correto.", "Se o problema persistir, peça suporte com print da tela e da mensagem recebida."],
612
+ "resumo_usuario_origem": "auto_ia_assistida",
613
+ "resumo_usuario_revisao_pendente": true
556
614
  }
557
615
  },
558
616
  {
@@ -771,6 +829,35 @@
771
829
  "schema": "legacy_v1_and_v2",
772
830
  "legacy_name": "figurinhatexto",
773
831
  "legacy_fields_present": ["descricao", "metodos_de_uso", "permissao_necessaria", "local_de_uso", "informacoes_coletadas", "argumentos", "pre_condicoes", "dependencias_externas", "efeitos_colaterais", "observabilidade", "privacidade", "acesso", "limite_uso_por_plano"]
832
+ },
833
+ "user_experience": {
834
+ "resumo_usuario": "Gera figurinha com texto em tema escuro. Digite o texto (até 80 caracteres, até 4 linhas) e a figurinha é criada.",
835
+ "quando_usar": ["Quando quiser transformar uma frase curta em uma figurinha para compartilhar no chat.", "Para destacar mensagens com estilo visual sem sair do chat."],
836
+ "exemplos_reais": [
837
+ {
838
+ "situacao": "Criar uma figurinha com uma frase curta para cumprimentar.",
839
+ "comando": "<prefix>figurinhatexto Olá, tudo bem?",
840
+ "resposta_esperada": "Figura gerada com o texto: 'Olá, tudo bem?'.",
841
+ "variacao": "<prefix>st Olá, tudo bem?"
842
+ },
843
+ {
844
+ "situacao": "Texto com várias linhas (até 4 linhas).",
845
+ "comando": "<prefix>figurinhatexto Linha 1\\nLinha 2\\nLinha 3\\nLinha 4",
846
+ "resposta_esperada": "Figura criada com o texto: 'Linha 1\\nLinha 2\\nLinha 3\\nLinha 4'.",
847
+ "variacao": "<prefix>st Linha 1\\nLinha 2\\nLinha 3\\nLinha 4."
848
+ },
849
+ {
850
+ "situacao": "Uso rápido com alias.",
851
+ "comando": "<prefix>st Bom dia a todos",
852
+ "resposta_esperada": "Figura gerada com o texto: 'Bom dia a todos'.",
853
+ "variacao": "<prefix>figurinhatexto Bom dia a todos."
854
+ }
855
+ ],
856
+ "resposta_esperada": ["Figura criada com sucesso e pronta para enviar no chat.", "Texto da figurinha incorporado ao sticker.", "Comando executado com sucesso."],
857
+ "erros_comuns_usuario": ["Texto com mais de 80 caracteres.", "Não informar o texto após <prefix>figurinhatexto ou <prefix>st.", "Formato de uso inválido (faltou o texto ou usou caractere não suportado).", "Usar mais de 4 linhas."],
858
+ "passos_se_der_erro": ["Verifique o texto informado: até 80 caracteres e até 4 linhas.", "Repita o comando correto, por exemplo: <prefix>figurinhatexto Seu texto aqui.", "Certifique-se de estar conectado com a conta Google, se necessário.", "Se o problema persistir, aguarde alguns minutos e tente novamente ou procure suporte."],
859
+ "resumo_usuario_origem": "auto_ia_assistida",
860
+ "resumo_usuario_revisao_pendente": true
774
861
  }
775
862
  },
776
863
  {
@@ -989,6 +1076,35 @@
989
1076
  "schema": "legacy_v1_and_v2",
990
1077
  "legacy_name": "figurinhatextobranco",
991
1078
  "legacy_fields_present": ["descricao", "metodos_de_uso", "permissao_necessaria", "local_de_uso", "informacoes_coletadas", "argumentos", "pre_condicoes", "dependencias_externas", "efeitos_colaterais", "observabilidade", "privacidade", "acesso", "limite_uso_por_plano"]
1079
+ },
1080
+ "user_experience": {
1081
+ "resumo_usuario": "Crie figurinha com texto em branco em tema claro usando <prefix>figurinhatextobranco ou <prefix>stw.",
1082
+ "quando_usar": ["Quer compartilhar uma mensagem rápida como imagem no chat.", "Precisa de até 80 caracteres, em até 4 linhas.", "Não precisa de opções adicionais ou ajustes de estilo.", "Prefere um atalho curto com <prefix>stw."],
1083
+ "exemplos_reais": [
1084
+ {
1085
+ "situacao": "Frase motivacional.",
1086
+ "comando": "<prefix>figurinhatextobranco Acredite em si mesmo!",
1087
+ "resposta_esperada": "Figurinha criada com o texto 'Acredite em si mesmo!' em tema claro.",
1088
+ "variacao": "<prefix>stw Acredite em si mesmo!"
1089
+ },
1090
+ {
1091
+ "situacao": "Bom dia.",
1092
+ "comando": "<prefix>figurinhatextobranco Bom dia, equipe!",
1093
+ "resposta_esperada": "Figurinha criada com o texto 'Bom dia, equipe!' em tema claro.",
1094
+ "variacao": "<prefix>stw Bom dia, equipe!"
1095
+ },
1096
+ {
1097
+ "situacao": "Quatro linhas.",
1098
+ "comando": "<prefix>figurinhatextobranco Linha 1 Linha 2 Linha 3 Linha 4",
1099
+ "resposta_esperada": "Figurinha criada com o texto em quatro linhas.",
1100
+ "variacao": "<prefix>stw Linha 1 Linha 2 Linha 3 Linha 4."
1101
+ }
1102
+ ],
1103
+ "resposta_esperada": ["Comando executado com sucesso."],
1104
+ "erros_comuns_usuario": ["Texto não informado após o comando.", "Texto excede 80 caracteres ou ultrapassa 4 linhas.", "Formato de uso incorreto ou sem o prefixo correto.", "Tentar usar caracteres não suportados."],
1105
+ "passos_se_der_erro": ["Certifique-se de incluir o texto após o comando, ex.: <prefix>figurinhatextobranco Seu texto.", "Verifique o tamanho: até 80 caracteres e até 4 linhas.", "Use o formato correto: <prefix>figurinhatextobranco ou <prefix>stw.", "Tente novamente. Se o erro persistir, copie o comando usado e peça ajuda."],
1106
+ "resumo_usuario_origem": "auto_ia_assistida",
1107
+ "resumo_usuario_revisao_pendente": true
992
1108
  }
993
1109
  },
994
1110
  {
@@ -1207,6 +1323,35 @@
1207
1323
  "schema": "legacy_v1_and_v2",
1208
1324
  "legacy_name": "figurinhatextopisca",
1209
1325
  "legacy_fields_present": ["descricao", "metodos_de_uso", "permissao_necessaria", "local_de_uso", "informacoes_coletadas", "argumentos", "pre_condicoes", "dependencias_externas", "efeitos_colaterais", "observabilidade", "privacidade", "acesso", "limite_uso_por_plano"]
1326
+ },
1327
+ "user_experience": {
1328
+ "resumo_usuario": "Gera figurinha animada de texto piscante com até 80 caracteres (máx. 4 linhas), usando <prefix>figurinhatextopisca ou <prefix>stb.",
1329
+ "quando_usar": ["Destacar mensagens com texto piscante em chats", "Promover ofertas rápidas ou lembretes", "Saudar ou chamar atenção com uma frase curta", "Transformar seu texto pronto em figurinha para envio rápido"],
1330
+ "exemplos_reais": [
1331
+ {
1332
+ "situacao": "Promoção rápida com destaque.",
1333
+ "comando": "<prefix>figurinhatextopisca Promoção de 50% hoje!",
1334
+ "resposta_esperada": "Figura criada com sucesso.",
1335
+ "variacao": "<prefix>stb Promoção de 50% hoje!"
1336
+ },
1337
+ {
1338
+ "situacao": "Saudação breve.",
1339
+ "comando": "<prefix>figurinhatextopisca Olá, bom dia!",
1340
+ "resposta_esperada": "Figura criada com sucesso.",
1341
+ "variacao": "<prefix>stb Olá, bom dia!"
1342
+ },
1343
+ {
1344
+ "situacao": "Lembrete com até 4 linhas.",
1345
+ "comando": "<prefix>figurinhatextopisca Lembrete: entrega hoje Confira novidades",
1346
+ "resposta_esperada": "Figura criada com sucesso.",
1347
+ "variacao": "<prefix>stb Lembrete: entrega hoje Confira novidades."
1348
+ }
1349
+ ],
1350
+ "resposta_esperada": ["Comando executado com sucesso.", "Formato de uso inválido. Consulte metodos_de_uso.", "Permissão insuficiente para executar este comando."],
1351
+ "erros_comuns_usuario": ["Texto excede 80 caracteres", "Mais de 4 linhas", "Não usar nenhum <prefix> no comando", "Texto vazio", "Texto com caracteres especiais não suportados"],
1352
+ "passos_se_der_erro": ["Verifique o tamanho do texto (≤ 80 caracteres) e o número de linhas (≤ 4).", "Tente novamente usando um dos comandos válidos com o mesmo texto.", "Confirme que você está logado via Google, conforme necessário.", "Se o erro continuar, tente novamente mais tarde ou procure suporte."],
1353
+ "resumo_usuario_origem": "auto_ia_assistida",
1354
+ "resumo_usuario_revisao_pendente": true
1210
1355
  }
1211
1356
  }
1212
1357
  ],
@@ -185,7 +185,7 @@ function checkMediaSize(mediaKey, mediaType, maxFileSize = MAX_FILE_SIZE) {
185
185
  */
186
186
  function parseStickerMetaText(text, senderName) {
187
187
  let packName = DEFAULT_STICKER_PACK_NAME;
188
- let packAuthor = senderName || 'OmniZap System';
188
+ let packAuthor = senderName || 'Omnizap';
189
189
  if (text) {
190
190
  const idx = text.indexOf('/');
191
191
  if (idx !== -1) {
@@ -7,7 +7,7 @@ Este arquivo e destinado a agentes de IA para gerar respostas no contexto dos co
7
7
  - arquivo_base: `app/modules/stickerPackModule/commandConfig.json`
8
8
  - schema_version: `2.0.0`
9
9
  - module_enabled: `true`
10
- - generated_at: `2026-03-11T02:35:17.177Z`
10
+ - generated_at: `2026-03-17T04:04:14.195Z`
11
11
 
12
12
  ## Escopo do Modulo
13
13
 
@@ -5,7 +5,11 @@ import { sanitizeText } from './stickerPackUtils.js';
5
5
  * Serviço responsável por direcionar figurinhas recém-criadas para packs automáticos.
6
6
  */
7
7
  const DEFAULT_AUTO_PACK_NAME = process.env.STICKER_PACK_AUTO_PACK_NAME || 'pack';
8
- const AUTO_PACK_TARGET_VISIBILITY = 'unlisted';
8
+ const VALID_VISIBILITIES = new Set(['public', 'unlisted', 'private']);
9
+ const AUTO_PACK_TARGET_VISIBILITY_RAW = String(process.env.STICKER_PACK_AUTO_COLLECT_VISIBILITY || process.env.STICKER_AUTO_PACK_BY_TAGS_VISIBILITY || 'public')
10
+ .trim()
11
+ .toLowerCase();
12
+ const AUTO_PACK_TARGET_VISIBILITY = VALID_VISIBILITIES.has(AUTO_PACK_TARGET_VISIBILITY_RAW) ? AUTO_PACK_TARGET_VISIBILITY_RAW : 'public';
9
13
  const AUTO_COLLECT_ENABLED = process.env.STICKER_PACK_AUTO_COLLECT_ENABLED !== 'false';
10
14
  const AUTO_PACK_NAME_MAX_LENGTH = 120;
11
15
  const AUTO_PACK_DESCRIPTION_MARKER = '[auto-pack:collector]';
@@ -378,6 +378,35 @@
378
378
  "schema": "legacy_v1_and_v2",
379
379
  "legacy_name": "pacote",
380
380
  "legacy_fields_present": ["descricao", "metodos_de_uso", "permissao_necessaria", "local_de_uso", "informacoes_coletadas", "argumentos", "pre_condicoes", "dependencias_externas", "efeitos_colaterais", "observabilidade", "privacidade", "acesso", "limite_uso_por_plano"]
381
+ },
382
+ "user_experience": {
383
+ "resumo_usuario": "Gerencie packs de figurinhas: criar, listar, adicionar figurinhas e enviar packs para contatos.",
384
+ "quando_usar": ["Criar um novo pack de figurinhas.", "Ver a lista de packs existentes.", "Adicionar figurinhas a um pack existente.", "Enviar um pack para alguém."],
385
+ "exemplos_reais": [
386
+ {
387
+ "situacao": "Criar um pack de figurinhas.",
388
+ "comando": "<prefix>pacote create meupack",
389
+ "resposta_esperada": "Pack criado com sucesso.",
390
+ "variacao": "Use nomes simples, sem aspas. Ex.: meupack."
391
+ },
392
+ {
393
+ "situacao": "Listar todos os packs.",
394
+ "comando": "<prefix>pacote list",
395
+ "resposta_esperada": "Listagem exibida com todos os packs.",
396
+ "variacao": "Pode retornar vários nomes; observe a lista completa."
397
+ },
398
+ {
399
+ "situacao": "Adicionar figurinhas a um pack existente.",
400
+ "comando": "<prefix>pacote add meupack",
401
+ "resposta_esperada": "Figurinhas adicionadas ao pack 'meupack'.",
402
+ "variacao": "Se houver figurinhas disponíveis, elas são incluídas."
403
+ }
404
+ ],
405
+ "resposta_esperada": ["Comando executado com sucesso.", "Formato de uso inválido. Consulte metodos_de_uso.", "Permissão insuficiente para executar este comando."],
406
+ "erros_comuns_usuario": ["Esquecer de usar o prefixo correto no início do comando.", "Digitar o subcomando errado ou sem o parâmetro necessário.", "Tentar adicionar figurinhas sem indicar o pack ou sem estar logado.", "Tentar enviar sem ter criado ou sem selecionar um pack existente.", "Não estar logado com Google quando o comando exigir login."],
407
+ "passos_se_der_erro": ["Use <prefix>pacote help para confirmar a sintaxe correta.", "Verifique se o subcomando está entre create, list, add ou send e inclua os parâmetros necessários.", "Confirme que você está logado com Google, conforme exigido.", "Tente novamente após ajustar o comando. Se o erro persistir, copie a mensagem de erro e tente novamente mais tarde.", "Se não resolver, aguarde alguns minutos e procure ajuda com os detalhes do comando executado."],
408
+ "resumo_usuario_origem": "auto_ia_assistida",
409
+ "resumo_usuario_revisao_pendente": true
381
410
  }
382
411
  }
383
412
  ],
@@ -1,3 +1,4 @@
1
+ import { now as __timeNow, nowIso as __timeNowIso, toUnixMs as __timeNowMs } from '#time';
1
2
  import { createHash } from 'node:crypto';
2
3
 
3
4
  import OpenAI from 'openai';
@@ -177,7 +178,7 @@ const upsertSuggestionCacheRow = async ({ suggestionText, normalizedText, semant
177
178
  };
178
179
 
179
180
  const listSemanticClusters = async () => {
180
- const now = Date.now();
181
+ const now = __timeNowMs();
181
182
  if (inMemoryClusterList.expiresAt > now && Array.isArray(inMemoryClusterList.items)) {
182
183
  return inMemoryClusterList.items;
183
184
  }
@@ -199,7 +200,7 @@ const listSemanticClusters = async () => {
199
200
  }))
200
201
  .filter((row) => row.id > 0 && Array.isArray(row.embedding) && row.embedding.length > 0);
201
202
  inMemoryClusterList = {
202
- expiresAt: Date.now() + RESOLUTION_CACHE_TTL_MS,
203
+ expiresAt: __timeNowMs() + RESOLUTION_CACHE_TTL_MS,
203
204
  items: parsed,
204
205
  };
205
206
  for (const cluster of parsed) {
@@ -316,7 +317,7 @@ const resolveSemanticCluster = async (suggestionText) => {
316
317
  if (!shouldRunSemanticClustering()) return null;
317
318
 
318
319
  const memoryCached = inMemorySuggestionCache.get(normalizedSuggestion);
319
- if (memoryCached && memoryCached.expiresAt > Date.now()) {
320
+ if (memoryCached && memoryCached.expiresAt > __timeNowMs()) {
320
321
  return memoryCached.value;
321
322
  }
322
323
 
@@ -331,7 +332,7 @@ const resolveSemanticCluster = async (suggestionText) => {
331
332
  suggestion: normalizedSuggestion,
332
333
  };
333
334
  inMemorySuggestionCache.set(normalizedSuggestion, {
334
- expiresAt: Date.now() + RESOLUTION_CACHE_TTL_MS,
335
+ expiresAt: __timeNowMs() + RESOLUTION_CACHE_TTL_MS,
335
336
  value: payload,
336
337
  });
337
338
  return payload;
@@ -360,7 +361,7 @@ const resolveSemanticCluster = async (suggestionText) => {
360
361
  similarity: payload.similarity,
361
362
  });
362
363
  inMemorySuggestionCache.set(normalizedSuggestion, {
363
- expiresAt: Date.now() + RESOLUTION_CACHE_TTL_MS,
364
+ expiresAt: __timeNowMs() + RESOLUTION_CACHE_TTL_MS,
364
365
  value: payload,
365
366
  });
366
367
  return payload;
@@ -388,7 +389,7 @@ const resolveSemanticCluster = async (suggestionText) => {
388
389
  similarity: 1,
389
390
  });
390
391
  inMemorySuggestionCache.set(normalizedSuggestion, {
391
- expiresAt: Date.now() + RESOLUTION_CACHE_TTL_MS,
392
+ expiresAt: __timeNowMs() + RESOLUTION_CACHE_TTL_MS,
392
393
  value: payload,
393
394
  });
394
395
  return payload;
@@ -1,3 +1,4 @@
1
+ import { now as __timeNow, nowIso as __timeNowIso, toUnixMs as __timeNowMs } from '#time';
1
2
  import logger from '#logger';
2
3
  import { getActiveSocket } from '../../config/index.js';
3
4
  import { normalizeJid, resolveBotJid } from '../../config/index.js';
@@ -165,7 +166,7 @@ const EFFECTIVE_COMPLETION_TRANSFER_ENABLED = COMPLETION_TRANSFER_ENABLED || IS_
165
166
  const EFFECTIVE_MIGRATION_CANDIDATE_LIMIT = IS_AGGRESSIVE_PROFILE ? Math.max(MIGRATION_CANDIDATE_LIMIT, 24) : MIGRATION_CANDIDATE_LIMIT;
166
167
  const EFFECTIVE_TRANSFER_CANDIDATE_SIMILARITY_FLOOR = IS_AGGRESSIVE_PROFILE ? Math.max(0.15, TRANSFER_CANDIDATE_SIMILARITY_FLOOR * 0.72) : TRANSFER_CANDIDATE_SIMILARITY_FLOOR;
167
168
 
168
- const EXPLICIT_OWNER = String(process.env.STICKER_AUTO_PACK_OWNER_JID || process.env.USER_ADMIN || '').trim();
169
+ const EXPLICIT_OWNER = String(process.env.STICKER_AUTO_PACK_OWNER_JID || process.env.WHATSAPP_ADMIN_JID || process.env.USER_ADMIN || '').trim();
169
170
 
170
171
  const LABEL_TO_TAG = {
171
172
  'anime illustration': 'anime',
@@ -2888,7 +2889,7 @@ const updateAutoPackMetadata = async (packId, payload) => {
2888
2889
  pack_theme_key: payload.themeKey,
2889
2890
  pack_volume: payload.volume,
2890
2891
  is_auto_pack: 1,
2891
- last_rebalanced_at: new Date(),
2892
+ last_rebalanced_at: __timeNow(),
2892
2893
  };
2893
2894
  if ('cover_sticker_id' in payload) {
2894
2895
  fields.cover_sticker_id = payload.cover_sticker_id ?? null;
@@ -2908,7 +2909,7 @@ const createAutoPackVolume = async ({ ownerJid, theme, subtheme, themeKey, group
2908
2909
  packThemeKey: themeKey,
2909
2910
  packVolume: volume,
2910
2911
  isAutoPack: true,
2911
- lastRebalancedAt: new Date(),
2912
+ lastRebalancedAt: __timeNow(),
2912
2913
  });
2913
2914
  };
2914
2915
 
@@ -3323,7 +3324,7 @@ export const runStickerAutoPackByTagsCycle = async ({ enableAdditions = true, en
3323
3324
  }
3324
3325
 
3325
3326
  running = true;
3326
- const startedAt = Date.now();
3327
+ const startedAt = __timeNowMs();
3327
3328
  const budgets = { added: 0, removed: 0 };
3328
3329
  let createdPacks = 0;
3329
3330
  let processedGroups = 0;
@@ -3400,7 +3401,7 @@ export const runStickerAutoPackByTagsCycle = async ({ enableAdditions = true, en
3400
3401
  pack_limit_skips: 0,
3401
3402
  processed_groups: 0,
3402
3403
  processed_volumes: 0,
3403
- duration_ms: Date.now() - startedAt,
3404
+ duration_ms: __timeNowMs() - startedAt,
3404
3405
  });
3405
3406
  logger.debug('Auto-pack por tags: nenhum grupo elegível neste ciclo.', {
3406
3407
  action: 'sticker_auto_pack_by_tags_idle',
@@ -3451,7 +3452,7 @@ export const runStickerAutoPackByTagsCycle = async ({ enableAdditions = true, en
3451
3452
  const fillRate = budgets.added / Math.max(1, processedVolumes * TARGET_PACK_SIZE);
3452
3453
 
3453
3454
  recordStickerAutoPackCycle({
3454
- durationMs: Date.now() - startedAt,
3455
+ durationMs: __timeNowMs() - startedAt,
3455
3456
  assetsScanned: Number(stats.assets_scanned || 0),
3456
3457
  assetsAdded: budgets.added,
3457
3458
  duplicateRate,
@@ -3468,7 +3469,7 @@ export const runStickerAutoPackByTagsCycle = async ({ enableAdditions = true, en
3468
3469
  pack_limit_skips: Number(packLimitSkips || 0),
3469
3470
  processed_groups: Number(processedGroups || 0),
3470
3471
  processed_volumes: Number(processedVolumes || 0),
3471
- duration_ms: Date.now() - startedAt,
3472
+ duration_ms: __timeNowMs() - startedAt,
3472
3473
  });
3473
3474
 
3474
3475
  logger.info('Auto-pack por tags executado.', {
@@ -3492,7 +3493,7 @@ export const runStickerAutoPackByTagsCycle = async ({ enableAdditions = true, en
3492
3493
  cohesion_rebuild_threshold: Number(COHESION_REBUILD_THRESHOLD.toFixed(6)),
3493
3494
  duplicate_skips: duplicateSkips,
3494
3495
  pack_limit_skips: packLimitSkips,
3495
- duration_ms: Date.now() - startedAt,
3496
+ duration_ms: __timeNowMs() - startedAt,
3496
3497
  duplicate_rate: Number(duplicateRate.toFixed(6)),
3497
3498
  rejection_rate: Number(rejectionRate.toFixed(6)),
3498
3499
  pack_fill_rate: Number(fillRate.toFixed(6)),
@@ -3537,7 +3538,7 @@ export const runStickerAutoPackByTagsCycle = async ({ enableAdditions = true, en
3537
3538
  pack_limit_skips: Number(packLimitSkips || 0),
3538
3539
  processed_groups: Number(processedGroups || 0),
3539
3540
  processed_volumes: Number(processedVolumes || 0),
3540
- duration_ms: Date.now() - startedAt,
3541
+ duration_ms: __timeNowMs() - startedAt,
3541
3542
  });
3542
3543
  } finally {
3543
3544
  running = false;
@@ -1,3 +1,4 @@
1
+ import { now as __timeNow, nowIso as __timeNowIso, toUnixMs as __timeNowMs } from '#time';
1
2
  import fs from 'node:fs/promises';
2
3
  import os from 'node:os';
3
4
 
@@ -299,14 +300,14 @@ const processDeterministicReclassification = async () => {
299
300
  };
300
301
 
301
302
  export const runStickerClassificationCycle = async ({ processPending = true, processReprocess = true, processDeterministic = true } = {}) => {
302
- const startedAt = Date.now();
303
+ const startedAt = __timeNowMs();
303
304
  const shouldProcessClassifier = classifierConfig.enabled;
304
305
  const shouldProcessDeterministic = deterministicReclassificationConfig.enabled;
305
306
 
306
307
  if (!BACKGROUND_ENABLED || (!shouldProcessClassifier && !shouldProcessDeterministic)) {
307
308
  recordStickerClassificationCycle({
308
309
  status: 'skipped',
309
- durationMs: Date.now() - startedAt,
310
+ durationMs: __timeNowMs() - startedAt,
310
311
  processed: 0,
311
312
  classified: 0,
312
313
  failed: 0,
@@ -325,7 +326,7 @@ export const runStickerClassificationCycle = async ({ processPending = true, pro
325
326
  const processed = Number(pendingStats?.processed || 0) + Number(reprocessStats?.processed || 0) + Number(deterministicStats?.processed || 0);
326
327
  const classified = Number(pendingStats?.classified || 0) + Number(reprocessStats?.classified || 0) + Number(deterministicStats?.updated || 0);
327
328
  const failed = Number(pendingStats?.failed || 0) + Number(reprocessStats?.failed || 0) + Number(deterministicStats?.failed || 0);
328
- const durationMs = Date.now() - startedAt;
329
+ const durationMs = __timeNowMs() - startedAt;
329
330
 
330
331
  recordStickerClassificationCycle({
331
332
  status: 'ok',
@@ -345,7 +346,7 @@ export const runStickerClassificationCycle = async ({ processPending = true, pro
345
346
  } catch (error) {
346
347
  recordStickerClassificationCycle({
347
348
  status: 'failed',
348
- durationMs: Date.now() - startedAt,
349
+ durationMs: __timeNowMs() - startedAt,
349
350
  processed: 0,
350
351
  classified: 0,
351
352
  failed: 1,
@@ -407,7 +408,7 @@ const classifyBatch = async () => {
407
408
  }
408
409
 
409
410
  running = true;
410
- const startedAt = Date.now();
411
+ const startedAt = __timeNowMs();
411
412
 
412
413
  try {
413
414
  const result = await runStickerClassificationCycle({
@@ -459,7 +460,7 @@ const classifyBatch = async () => {
459
460
  deterministic_reclassification_failed: Number(deterministic.failed || 0),
460
461
  deterministic_reclassification_batches: Number(deterministic.batches || 0),
461
462
  deterministic_reclassification_last_cursor: deterministic.last_cursor || null,
462
- duration_ms: Date.now() - startedAt,
463
+ duration_ms: __timeNowMs() - startedAt,
463
464
  batch_size: BATCH_SIZE,
464
465
  concurrency: BACKGROUND_CONCURRENCY,
465
466
  gain_count: gainCount,
@@ -472,7 +473,7 @@ const classifyBatch = async () => {
472
473
  processed: Number(processed || 0),
473
474
  classified: Number(classified || 0),
474
475
  failed: Number(failed || 0),
475
- duration_ms: Date.now() - startedAt,
476
+ duration_ms: __timeNowMs() - startedAt,
476
477
  };
477
478
  } catch (error) {
478
479
  logger.error('Falha no loop de classificação em background.', {
@@ -483,7 +484,7 @@ const classifyBatch = async () => {
483
484
  executed: true,
484
485
  reason: 'failed',
485
486
  gain_count: 0,
486
- duration_ms: Date.now() - startedAt,
487
+ duration_ms: __timeNowMs() - startedAt,
487
488
  };
488
489
  } finally {
489
490
  running = false;
@@ -1,3 +1,4 @@
1
+ import { now as __timeNow, nowIso as __timeNowIso, toUnixMs as __timeNowMs } from '#time';
1
2
  import logger from '#logger';
2
3
  import { setQueueDepth } from '../../observability/metrics.js';
3
4
  import { isFeatureEnabled } from '../../services/infra/featureFlagService.js';
@@ -51,9 +52,9 @@ const enqueueTaskSafely = async ({ taskType, payload, priority, idempotencyKey }
51
52
  };
52
53
 
53
54
  const toUnixSeconds = (value) => {
54
- if (!value) return Math.floor(Date.now() / 1000);
55
+ if (!value) return Math.floor(__timeNowMs() / 1000);
55
56
  const numeric = Date.parse(value);
56
- if (!Number.isFinite(numeric)) return Math.floor(Date.now() / 1000);
57
+ if (!Number.isFinite(numeric)) return Math.floor(__timeNowMs() / 1000);
57
58
  return Math.floor(numeric / 1000);
58
59
  };
59
60
 
@@ -1,3 +1,4 @@
1
+ import { now as __timeNow, nowIso as __timeNowIso, toUnixMs as __timeNowMs } from '#time';
1
2
  import { listClassificationCategoryDistribution } from './stickerAssetClassificationRepository.js';
2
3
 
3
4
  const clamp = (value, min, max) => Math.max(min, Math.min(max, value));
@@ -75,7 +76,7 @@ const buildDynamicWeights = (driftScore) => {
75
76
  export const getBaseMarketplaceWeights = () => BASE_WEIGHTS;
76
77
 
77
78
  export const getMarketplaceDriftSnapshot = async ({ force = false } = {}) => {
78
- const now = Date.now();
79
+ const now = __timeNowMs();
79
80
  if (!force && now < cache.expiresAt) {
80
81
  return cache;
81
82
  }