@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,66 @@
1
+ export const normalizeDigits = (value) => String(value || '').replace(/\D+/g, '');
2
+
3
+ export const formatPhone = (value) => {
4
+ const digits = normalizeDigits(value);
5
+ if (!digits) return 'Não informado';
6
+ if (digits.length <= 4) return digits;
7
+ if (digits.length <= 8) return `${digits.slice(0, -4)}-${digits.slice(-4)}`;
8
+ if (digits.length <= 10) return `(${digits.slice(0, 2)}) ${digits.slice(2, -4)}-${digits.slice(-4)}`;
9
+ if (digits.length <= 13) return `+${digits.slice(0, 2)} ${digits.slice(2, -4)}-${digits.slice(-4)}`;
10
+ return `+${digits.slice(0, 2)} ${digits.slice(2, digits.length - 4)}-${digits.slice(-4)}`;
11
+ };
12
+
13
+ export const formatNumber = (value) =>
14
+ new Intl.NumberFormat('pt-BR', {
15
+ maximumFractionDigits: 0,
16
+ }).format(Math.max(0, Number(value || 0)));
17
+
18
+ export const formatDateTime = (value) => {
19
+ const ms = Date.parse(String(value || ''));
20
+ if (!Number.isFinite(ms)) return 'Não informado';
21
+ return new Intl.DateTimeFormat('pt-BR', {
22
+ dateStyle: 'short',
23
+ timeStyle: 'short',
24
+ }).format(new Date(ms));
25
+ };
26
+
27
+ export const buildLoginRedirectPath = (loginPath) => {
28
+ const loginUrl = new URL(loginPath, window.location.origin);
29
+ loginUrl.searchParams.set('next', '/user/');
30
+ return `${loginUrl.pathname}${loginUrl.search}`;
31
+ };
32
+
33
+ export const buildWhatsAppUrl = (phoneDigits, text = '/menu') => {
34
+ const digits = normalizeDigits(phoneDigits);
35
+ const params = new URLSearchParams({
36
+ text: String(text || '').trim() || '/menu',
37
+ type: 'custom_url',
38
+ app_absent: '0',
39
+ });
40
+ if (digits) params.set('phone', digits);
41
+ return `https://api.whatsapp.com/send/?${params.toString()}`;
42
+ };
43
+
44
+ export const buildSupportWhatsAppUrl = (phoneDigits, text = 'Olá! Preciso de suporte no OmniZap.') => {
45
+ const digits = normalizeDigits(phoneDigits);
46
+ if (!digits) return '';
47
+ const safeText = String(text || '').trim() || 'Olá! Preciso de suporte no OmniZap.';
48
+ return `https://wa.me/${encodeURIComponent(digits)}?text=${encodeURIComponent(safeText)}`;
49
+ };
50
+
51
+ export const getSessionStatusLabel = (session) => {
52
+ const authenticated = Boolean(session?.authenticated);
53
+ return authenticated ? 'Sessão ativa' : 'Sessão não autenticada';
54
+ };
55
+
56
+ export const formatPackStatus = (status, visibility) => {
57
+ const safeStatus =
58
+ String(status || '')
59
+ .trim()
60
+ .toLowerCase() || 'desconhecido';
61
+ const safeVisibility =
62
+ String(visibility || '')
63
+ .trim()
64
+ .toLowerCase() || 'sem visibilidade';
65
+ return `${safeStatus} · ${safeVisibility}`;
66
+ };
@@ -0,0 +1,547 @@
1
+ import React, { useEffect, useMemo, useState } from 'react';
2
+ import { createRoot } from 'react-dom/client';
3
+ import htm from 'htm';
4
+ import { formatDateTime, formatPhone } from './userProfile/actions.js';
5
+
6
+ const html = htm.bind(React.createElement);
7
+
8
+ const DEFAULT_API_BASE_PATH = '/api';
9
+ const DEFAULT_LOGIN_PATH = '/login';
10
+ const DEFAULT_FALLBACK_AVATAR = '/assets/images/brand-logo-128.webp';
11
+
12
+ const TABS = [
13
+ { key: 'summary', label: 'Estatísticas', icon: '📊' },
14
+ { key: 'rpg', label: 'Sistema RPG', icon: '⚔️' },
15
+ { key: 'account', label: 'Segurança', icon: '🛡️' },
16
+ { key: 'support', label: 'Suporte', icon: '🎧' },
17
+ ];
18
+
19
+ const UserApp = ({ config }) => {
20
+ const [activeTab, setActiveTab] = useState('summary');
21
+ const [isLoading, setLoading] = useState(true);
22
+ const [summary, setSummary] = useState(null);
23
+ const [session, setSession] = useState(null);
24
+ const [isSidebarOpen, setSidebarOpen] = useState(false);
25
+
26
+ useEffect(() => {
27
+ const observer =
28
+ typeof globalThis.IntersectionObserver === 'function'
29
+ ? new globalThis.IntersectionObserver(
30
+ (entries) => {
31
+ entries.forEach((entry) => {
32
+ if (entry.isIntersecting) {
33
+ entry.target.classList.add('is-visible');
34
+ observer.unobserve(entry.target);
35
+ }
36
+ });
37
+ },
38
+ { threshold: 0.1 },
39
+ )
40
+ : null;
41
+
42
+ document.querySelectorAll('[data-reveal]').forEach((el, i) => {
43
+ el.style.setProperty('--reveal-delay', `${i * 60}ms`);
44
+ if (observer) {
45
+ observer.observe(el);
46
+ } else {
47
+ el.classList.add('is-visible');
48
+ }
49
+ });
50
+
51
+ return () => {
52
+ if (observer) observer.disconnect();
53
+ };
54
+ }, [activeTab, isLoading]);
55
+
56
+ useEffect(() => {
57
+ const loadData = async () => {
58
+ try {
59
+ const res = await fetch(`${config.apiBasePath}/me?view=summary`, { credentials: 'include' });
60
+ const payload = await res.json();
61
+ if (payload?.data) {
62
+ setSummary(payload.data.account);
63
+ setSession(payload.data.session);
64
+ }
65
+ } catch (err) {
66
+ console.error('Failed to load user data', err);
67
+ } finally {
68
+ setLoading(false);
69
+ }
70
+ };
71
+ loadData();
72
+ }, [config.apiBasePath]);
73
+
74
+ const authInfo = useMemo(() => {
75
+ if (!session?.user) return { href: '/login/', label: 'Entrar', image: null };
76
+ return {
77
+ href: '/user/',
78
+ label: session.user.name?.split(' ')[0] || 'Perfil',
79
+ image: summary?.picture || session.user.picture || DEFAULT_FALLBACK_AVATAR,
80
+ };
81
+ }, [session, summary]);
82
+
83
+ const rpgInfo = useMemo(() => summary?.rpg || { level: 1, xp: 0, gold: 0, karma: { score: 0, positive: 0, negative: 0 }, pvp: { matches: 0, wins: 0, losses: 0 }, inventory_count: 0, total_pokemons: 0 }, [summary]);
84
+ const usageInfo = useMemo(() => summary?.usage || { messages: 0, packs: 0, stickers: 0, activity_chart: [], insights: {}, first_message_at: null, last_message_at: null }, [summary]);
85
+
86
+ const daysMember = useMemo(() => {
87
+ if (!rpgInfo.member_since) return 0;
88
+ const diff = new Date() - new Date(rpgInfo.member_since);
89
+ return Math.floor(diff / (1000 * 60 * 60 * 24));
90
+ }, [rpgInfo.member_since]);
91
+
92
+ const handleTabChange = (key) => {
93
+ setActiveTab(key);
94
+ setSidebarOpen(false);
95
+ };
96
+
97
+ return html`
98
+ <div className="min-h-screen bg-[#020617] text-white font-sans selection:bg-primary selection:text-primary-content overflow-x-hidden">
99
+ <div className="fixed inset-0 pointer-events-none overflow-hidden">
100
+ <div className="absolute top-[-10%] left-[-10%] w-[40%] h-[40%] bg-primary/5 blur-[120px] rounded-full animate-pulse"></div>
101
+ <div className="absolute bottom-[10%] right-[-5%] w-[30%] h-[30%] bg-secondary/5 blur-[100px] rounded-full"></div>
102
+ </div>
103
+
104
+ <!-- Backdrop for mobile sidebar (Solid overlay, no blur) -->
105
+ <div onClick=${() => setSidebarOpen(false)} className=${`fixed inset-0 z-[90] bg-[#020617]/90 lg:hidden transition-opacity duration-300 ${isSidebarOpen ? 'opacity-100' : 'opacity-0 pointer-events-none'}`}></div>
106
+
107
+ <!-- Navbar -->
108
+ <header className="sticky top-0 z-50 border-b border-white/5 bg-[#020617]">
109
+ <div className="container mx-auto px-4">
110
+ <div className="flex h-16 items-center justify-between gap-4">
111
+ <div className="flex items-center gap-4">
112
+ <button onClick=${() => setSidebarOpen(true)} className="lg:hidden btn btn-ghost btn-square btn-sm bg-white/5 border border-white/10 rounded-xl">
113
+ <svg xmlns="http://www.w3.org/2000/svg" className="h-5 w-5" fill="none" viewBox="0 0 24 24" stroke="currentColor"><path strokeLinecap="round" strokeLinejoin="round" strokeWidth="3" d="M4 6h16M4 12h16M4 18h16" /></svg>
114
+ </button>
115
+
116
+ <a href="/" className="flex items-center gap-2.5 hover:opacity-80 transition-opacity">
117
+ <img src="/assets/images/brand-logo-128.webp" className="w-8 h-8 rounded-xl shadow-sm" alt="Logo" />
118
+ <span className="text-base sm:text-lg font-black tracking-tight">OmniZap<span className="text-primary">.</span></span>
119
+ </a>
120
+ </div>
121
+
122
+ <div className="flex items-center gap-3">
123
+ <button onClick=${() => window.location.assign('/login/')} className="btn btn-ghost btn-sm h-9 min-h-0 gap-2 rounded-xl bg-white/5 border border-white/10 hover:bg-error hover:text-white transition-all px-4 font-black text-[10px] uppercase tracking-widest">Sair</button>
124
+ </div>
125
+ </div>
126
+ </div>
127
+ </header>
128
+
129
+ <main className="container mx-auto max-w-6xl px-4 py-8 lg:py-16">
130
+ <div className="grid lg:grid-cols-[300px_1fr] gap-8 items-start">
131
+ <!-- Sidebar Navigation (Drawer on Mobile) -->
132
+ <aside className=${`fixed lg:sticky top-0 lg:top-32 left-0 h-full lg:h-auto w-[280px] lg:w-auto z-[100] lg:z-0 bg-[#020617] lg:bg-transparent border-r lg:border-none border-white/10 p-6 lg:p-0 space-y-6 transform transition-transform duration-300 ease-out lg:translate-x-0 ${isSidebarOpen ? 'translate-x-0' : '-translate-x-full'} shadow-[20px_0_60px_rgba(0,0,0,0.8)]`}>
133
+ <div className="lg:hidden flex items-center justify-between mb-8">
134
+ <span className="text-xs font-black uppercase tracking-[0.2em] text-white/30">Navegação</span>
135
+ <button onClick=${() => setSidebarOpen(false)} className="btn btn-ghost btn-square btn-xs">
136
+ <svg xmlns="http://www.w3.org/2000/svg" className="h-5 w-5" fill="none" viewBox="0 0 24 24" stroke="currentColor"><path strokeLinecap="round" strokeLinejoin="round" strokeWidth="2" d="M6 18L18 6M6 6l12 12" /></svg>
137
+ </button>
138
+ </div>
139
+
140
+ <div className="hidden lg:block relative group p-8 rounded-[2.5rem] bg-white/[0.03] border border-white/5 text-center space-y-6 overflow-hidden">
141
+ <div className="absolute inset-0 bg-gradient-to-b from-primary/5 to-transparent opacity-0 group-hover:opacity-100 transition-opacity duration-500"></div>
142
+
143
+ <div className="relative">
144
+ <div className="relative inline-block">
145
+ <div className="absolute inset-0 bg-primary/20 blur-xl rounded-full animate-pulse"></div>
146
+ <img src=${authInfo.image} className="relative w-24 h-24 rounded-[2rem] border-2 border-white/10 p-1.5 object-cover mx-auto shadow-2xl" />
147
+ <div className="absolute -bottom-1 -right-1 w-6 h-6 bg-success border-4 border-[#020617] rounded-full"></div>
148
+ </div>
149
+ </div>
150
+
151
+ <div className="relative space-y-1">
152
+ <h2 className="text-xl font-black tracking-tight truncate px-2">${session?.user?.name || 'Carregando...'}</h2>
153
+ <div className="inline-flex items-center gap-2 px-3 py-1 rounded-full bg-primary/10 border border-primary/20 text-primary text-[10px] font-black uppercase tracking-widest">${summary?.plan_label || 'Plano Free'}</div>
154
+ </div>
155
+
156
+ <div className="relative grid grid-cols-2 gap-2 pt-2">
157
+ <div className="p-3 rounded-2xl bg-white/5 border border-white/5 text-center">
158
+ <p className="text-[8px] font-black text-white/30 uppercase tracking-widest mb-1">Nível</p>
159
+ <p className="text-lg font-black text-primary">${rpgInfo.level}</p>
160
+ </div>
161
+ <div className="p-3 rounded-2xl bg-white/5 border border-white/5 text-center">
162
+ <p className="text-[8px] font-black text-white/30 uppercase tracking-widest mb-1">Gold</p>
163
+ <p className="text-lg font-black text-warning">💰 ${rpgInfo.gold}</p>
164
+ </div>
165
+ </div>
166
+ </div>
167
+
168
+ <nav className="p-2 rounded-[2rem] bg-white/[0.03] border border-white/5 space-y-1">
169
+ ${TABS.map(
170
+ (tab) => html`
171
+ <button onClick=${() => handleTabChange(tab.key)} className=${`w-full flex items-center gap-4 px-6 py-4 rounded-2xl font-black text-xs uppercase tracking-widest transition-all ${activeTab === tab.key ? 'bg-primary text-primary-content shadow-2xl shadow-primary/20 scale-[1.02]' : 'hover:bg-white/5 text-white/40 hover:text-white'}`}>
172
+ <span className="text-xl opacity-80">${tab.icon}</span>
173
+ ${tab.label}
174
+ </button>
175
+ `,
176
+ )}
177
+ </nav>
178
+ </aside>
179
+
180
+ <!-- Content Area -->
181
+ <div className="space-y-8">
182
+ <div data-reveal="fade-up" className="space-y-2 text-center lg:text-left pt-4 lg:pt-0">
183
+ <h1 className="text-4xl sm:text-5xl font-black tracking-tighter">Painel do <span className="text-transparent bg-clip-text bg-gradient-to-r from-primary to-emerald-400">Usuário</span></h1>
184
+ <p className="text-white/40 text-base font-medium">Bem-vindo de volta! Aqui estão suas estatísticas em tempo real.</p>
185
+ </div>
186
+
187
+ <div data-reveal="fade-up" className="relative p-1 rounded-[3rem] bg-gradient-to-br from-white/10 to-transparent">
188
+ <div className="bg-[#020617] rounded-[2.9rem] p-6 lg:p-12 min-h-[500px] overflow-hidden relative">
189
+ ${isLoading
190
+ ? html`
191
+ <div className="flex flex-col items-center justify-center h-full space-y-6 py-20 animate-in fade-in">
192
+ <div className="relative">
193
+ <div className="absolute inset-0 bg-primary/20 blur-2xl rounded-full animate-ping"></div>
194
+ <span className="loading loading-ring w-20 h-20 text-primary relative"></span>
195
+ </div>
196
+ <p className="text-[10px] font-black uppercase tracking-[0.4em] text-white/30 animate-pulse">Sincronizando com o Core...</p>
197
+ </div>
198
+ `
199
+ : html`
200
+ <div className="animate-in fade-in slide-in-from-bottom-8 duration-700">
201
+ ${activeTab === 'summary' &&
202
+ html`
203
+ <div className="grid gap-10 animate-in fade-in slide-in-from-bottom-8 duration-700">
204
+ <!-- Main Metrics Row -->
205
+ <div className="grid grid-cols-1 sm:grid-cols-3 gap-4">
206
+ <div className="p-8 rounded-[2.5rem] bg-white/[0.03] border border-white/5 space-y-2 group hover:border-primary/30 transition-colors relative overflow-hidden">
207
+ <div className="absolute -right-4 -bottom-4 text-6xl opacity-5">💬</div>
208
+ <p className="text-[10px] font-black uppercase tracking-widest text-white/20 group-hover:text-primary transition-colors relative z-10">Total de Mensagens</p>
209
+ <p className="text-4xl font-black text-white relative z-10">${usageInfo.messages.toLocaleString()}</p>
210
+ </div>
211
+ <div className="p-8 rounded-[2.5rem] bg-white/[0.03] border border-white/5 space-y-2 group hover:border-emerald-400/30 transition-colors relative overflow-hidden">
212
+ <div className="absolute -right-4 -bottom-4 text-6xl opacity-5">🖼️</div>
213
+ <p className="text-[10px] font-black uppercase tracking-widest text-white/20 group-hover:text-emerald-400 transition-colors relative z-10">Stickers Criados</p>
214
+ <p className="text-4xl font-black text-white relative z-10">${usageInfo.stickers.toLocaleString()}</p>
215
+ </div>
216
+ <div className="p-8 rounded-[2.5rem] bg-white/[0.03] border border-white/5 space-y-2 group hover:border-warning/30 transition-colors relative overflow-hidden">
217
+ <div className="absolute -right-4 -bottom-4 text-6xl opacity-5">📦</div>
218
+ <p className="text-[10px] font-black uppercase tracking-widest text-white/20 group-hover:text-warning transition-colors relative z-10">Packs de Stickers</p>
219
+ <p className="text-4xl font-black text-white relative z-10">${usageInfo.packs}</p>
220
+ </div>
221
+ </div>
222
+
223
+ <!-- Deep Insights Grid -->
224
+ <div className="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-3 gap-6">
225
+ <div className="p-6 rounded-[2rem] bg-white/[0.02] border border-white/5 space-y-4">
226
+ <h4 className="text-[10px] font-black uppercase tracking-widest text-white/30">Comandos</h4>
227
+ <div className="space-y-3">
228
+ <div className="flex justify-between items-center">
229
+ <span className="text-xs font-medium text-white/50">Total Usados</span>
230
+ <span className="text-sm font-black">${usageInfo.insights?.commands_total || 0}</span>
231
+ </div>
232
+ <div className="flex justify-between items-center">
233
+ <span className="text-xs font-medium text-white/50">Favorito</span>
234
+ <span className="text-sm font-black text-primary">${usageInfo.insights?.top_command || 'N/D'}</span>
235
+ </div>
236
+ <div className="flex justify-between items-center">
237
+ <span className="text-xs font-medium text-white/50">Uso do Favorito</span>
238
+ <span className="text-sm font-black">${usageInfo.insights?.top_command_count || 0}x</span>
239
+ </div>
240
+ </div>
241
+ </div>
242
+
243
+ <div className="p-6 rounded-[2rem] bg-white/[0.02] border border-white/5 space-y-4">
244
+ <h4 className="text-[10px] font-black uppercase tracking-widest text-white/30">Comunidade</h4>
245
+ <div className="space-y-3">
246
+ <div className="flex justify-between items-center">
247
+ <span className="text-xs font-medium text-white/50">Grupos Ativos</span>
248
+ <span className="text-sm font-black">${usageInfo.insights?.groups_active || 0}</span>
249
+ </div>
250
+ <div className="flex justify-between items-center">
251
+ <span className="text-xs font-medium text-white/50">Grupo Principal</span>
252
+ <span className="text-sm font-black text-emerald-400 truncate max-w-[120px]">${usageInfo.insights?.top_group || 'N/D'}</span>
253
+ </div>
254
+ <div className="flex justify-between items-center">
255
+ <span className="text-xs font-medium text-white/50">Tipo mais comum</span>
256
+ <span className="text-sm font-black capitalize">${usageInfo.insights?.top_message_type || 'texto'}</span>
257
+ </div>
258
+ </div>
259
+ </div>
260
+
261
+ <div className="p-6 rounded-[2rem] bg-white/[0.02] border border-white/5 space-y-4">
262
+ <h4 className="text-[10px] font-black uppercase tracking-widest text-white/30">Hábito de Uso</h4>
263
+ <div className="space-y-3">
264
+ <div className="flex justify-between items-center">
265
+ <span className="text-xs font-medium text-white/50">Horário de Pico</span>
266
+ <span className="text-sm font-black">${usageInfo.insights?.active_hour !== null ? usageInfo.insights.active_hour + ':00' : 'N/D'}</span>
267
+ </div>
268
+ <div className="flex justify-between items-center">
269
+ <span className="text-xs font-medium text-white/50">Média Diária</span>
270
+ <span className="text-sm font-black text-info">${usageInfo.insights?.avg_daily || 0} msg</span>
271
+ </div>
272
+ <div className="flex justify-between items-center">
273
+ <span className="text-xs font-medium text-white/50">Karma Atual</span>
274
+ <span className="text-sm font-black text-warning">${rpgInfo.karma?.score || 0}</span>
275
+ </div>
276
+ </div>
277
+ </div>
278
+ </div>
279
+
280
+ <!-- Activity Chart -->
281
+ ${usageInfo.activity_chart && usageInfo.activity_chart.length > 0
282
+ ? html`
283
+ <div className="p-8 rounded-[3rem] bg-white/[0.02] border border-white/5 space-y-6">
284
+ <h3 className="font-black text-lg flex items-center gap-3">
285
+ <span className="text-primary">📈</span>
286
+ Fluxo de Mensagens (7 dias)
287
+ </h3>
288
+ <div className="flex items-end justify-between gap-2 h-40 pt-4 px-2">
289
+ ${usageInfo.activity_chart.map((data) => {
290
+ const maxCount = Math.max(...usageInfo.activity_chart.map((d) => d.count), 1);
291
+ const heightPercent = Math.max((data.count / maxCount) * 100, 5);
292
+ return html`
293
+ <div key=${data.day} className="flex flex-col items-center gap-2 flex-1 group min-w-0">
294
+ <div className="w-full relative flex justify-center h-full items-end">
295
+ <div className="w-full max-w-[2rem] bg-primary/20 hover:bg-primary transition-all rounded-t-lg group-hover:shadow-[0_0_20px_rgba(34,197,94,0.4)]" style=${{ height: heightPercent + '%' }}>
296
+ <div className="absolute -top-8 left-1/2 -translate-x-1/2 opacity-0 group-hover:opacity-100 transition-opacity bg-white text-[#020617] text-[10px] font-black px-2 py-1 rounded-lg pointer-events-none whitespace-nowrap z-20">${data.count} msgs</div>
297
+ </div>
298
+ </div>
299
+ <span className="text-[8px] font-bold text-white/20 uppercase tracking-tighter truncate w-full text-center">${data.day.split('-').reverse().join('/')}</span>
300
+ </div>
301
+ `;
302
+ })}
303
+ </div>
304
+ </div>
305
+ `
306
+ : null}
307
+
308
+ <!-- RPG & Social Snippet -->
309
+ <div className="grid grid-cols-1 md:grid-cols-2 gap-6">
310
+ <div className="p-8 rounded-[2.5rem] bg-emerald-500/5 border border-emerald-500/10 space-y-4">
311
+ <h4 className="text-[10px] font-black uppercase tracking-widest text-emerald-400/40">Engajamento Social</h4>
312
+ <div className="grid grid-cols-2 gap-4">
313
+ <div className="space-y-1">
314
+ <p className="text-[8px] font-black text-white/20 uppercase">Votos Positivos</p>
315
+ <p className="text-xl font-black text-emerald-400">+${rpgInfo.karma?.positive || 0}</p>
316
+ </div>
317
+ <div className="space-y-1">
318
+ <p className="text-[8px] font-black text-white/20 uppercase">Votos Negativos</p>
319
+ <p className="text-xl font-black text-rose-500">-${rpgInfo.karma?.negative || 0}</p>
320
+ </div>
321
+ </div>
322
+ </div>
323
+ <div className="p-8 rounded-[2.5rem] bg-info/5 border border-info/10 space-y-4">
324
+ <h4 className="text-[10px] font-black uppercase tracking-widest text-info/40">Exploração</h4>
325
+ <div className="grid grid-cols-2 gap-4">
326
+ <div className="space-y-1">
327
+ <p className="text-[8px] font-black text-white/20 uppercase">Itens Inventário</p>
328
+ <p className="text-xl font-black text-white">${rpgInfo.inventory_count || 0}</p>
329
+ </div>
330
+ <div className="space-y-1">
331
+ <p className="text-[8px] font-black text-white/20 uppercase">Pokémons</p>
332
+ <p className="text-xl font-black text-white">${rpgInfo.total_pokemons || 0}</p>
333
+ </div>
334
+ </div>
335
+ </div>
336
+ </div>
337
+
338
+ <!-- Account Details -->
339
+ <div className="p-6 sm:p-10 rounded-[2.5rem] sm:rounded-[3rem] bg-primary/5 border border-primary/10 relative overflow-hidden">
340
+ <div className="absolute top-[-20%] right-[-10%] w-64 h-64 bg-primary/10 blur-[80px] rounded-full"></div>
341
+ <h3 className="relative font-black text-xl mb-8 flex items-center gap-3">
342
+ <span className="w-2 h-2 rounded-full bg-primary shadow-[0_0_10px_rgba(34,197,94,0.8)]"></span>
343
+ Detalhes da Identidade
344
+ </h3>
345
+ <div className="relative grid grid-cols-1 sm:grid-cols-2 gap-y-6 sm:gap-y-8 gap-x-12 border-b border-white/5 pb-8 mb-8">
346
+ <div>
347
+ <p className="text-[9px] font-black uppercase text-white/20 tracking-[0.2em] mb-1">Endereço de E-mail</p>
348
+ <p className="text-sm sm:text-lg font-bold text-white/80 break-all">${session?.user?.email}</p>
349
+ </div>
350
+ <div>
351
+ <p className="text-[9px] font-black uppercase text-white/20 tracking-[0.2em] mb-1">WhatsApp Vinculado</p>
352
+ <p className="text-sm sm:text-lg font-bold text-white/80">${summary?.owner_phone ? `+${formatPhone(summary.owner_phone)}` : 'Não vinculado'}</p>
353
+ </div>
354
+ <div>
355
+ <p className="text-[9px] font-black uppercase text-white/20 tracking-[0.2em] mb-1">Última Atividade</p>
356
+ <p className="text-sm sm:text-lg font-bold text-white/80">${formatDateTime(summary?.last_seen_at) || 'Ativo agora'}</p>
357
+ </div>
358
+ <div>
359
+ <p className="text-[9px] font-black uppercase text-white/20 tracking-[0.2em] mb-1">Membro Desde</p>
360
+ <p className="text-sm sm:text-lg font-bold text-white/80">${formatDateTime(rpgInfo.member_since) || 'Recentemente'}</p>
361
+ </div>
362
+ </div>
363
+
364
+ <div className="relative grid grid-cols-2 sm:grid-cols-3 gap-6">
365
+ <div>
366
+ <p className="text-[9px] font-black uppercase text-white/20 mb-1">Tempo como Membro</p>
367
+ <p className="text-sm font-black text-primary">${daysMember} dias</p>
368
+ </div>
369
+ <div>
370
+ <p className="text-[9px] font-black uppercase text-white/20 mb-1">Primeira Mensagem</p>
371
+ <p className="text-[10px] font-bold text-white/60">${formatDateTime(usageInfo.first_message_at) || 'N/D'}</p>
372
+ </div>
373
+ <div>
374
+ <p className="text-[9px] font-black uppercase text-white/20 mb-1">Última Mensagem</p>
375
+ <p className="text-[10px] font-bold text-white/60">${formatDateTime(usageInfo.last_message_at) || 'N/D'}</p>
376
+ </div>
377
+ </div>
378
+ </div>
379
+ </div>
380
+ `}
381
+ ${activeTab === 'rpg' &&
382
+ html`
383
+ <div className="space-y-10 animate-in fade-in slide-in-from-bottom-8 duration-700">
384
+ <!-- RPG Header -->
385
+ <div className="flex flex-col sm:flex-row gap-6 items-center sm:items-stretch">
386
+ <div className="flex-1 p-8 rounded-[3rem] bg-gradient-to-br from-primary/10 to-transparent border border-primary/20 relative overflow-hidden w-full">
387
+ <div className="absolute top-0 right-0 p-6 text-6xl opacity-10">⚔️</div>
388
+ <h3 className="text-sm font-black uppercase tracking-widest text-primary mb-6">Status do Treinador</h3>
389
+ <div className="flex items-baseline gap-4 mb-4">
390
+ <span className="text-6xl font-black text-white">${rpgInfo.level}</span>
391
+ <span className="text-sm font-bold text-white/40 uppercase tracking-widest">Nível Atual</span>
392
+ </div>
393
+
394
+ <div className="space-y-2 w-full max-w-sm">
395
+ <div className="flex justify-between text-[10px] font-black uppercase tracking-widest text-white/50">
396
+ <span>XP Atual: ${rpgInfo.xp}</span>
397
+ <span>Próximo Nível</span>
398
+ </div>
399
+ <div className="h-3 w-full bg-[#020617] rounded-full overflow-hidden border border-white/5">
400
+ <div className="h-full bg-gradient-to-r from-primary to-emerald-400 rounded-full transition-all duration-1000" style=${{ width: Math.min((rpgInfo.xp / (rpgInfo.level * 100)) * 100, 100) + '%' }}></div>
401
+ </div>
402
+ </div>
403
+ </div>
404
+
405
+ <div className="p-8 rounded-[3rem] bg-warning/5 border border-warning/10 flex flex-col justify-center items-center sm:min-w-[200px] w-full sm:w-auto">
406
+ <span className="text-4xl mb-4 animate-bounce">💰</span>
407
+ <span className="text-4xl font-black text-warning mb-1">${rpgInfo.gold}</span>
408
+ <span className="text-[10px] font-black uppercase tracking-widest text-warning/50">Gold Acumulado</span>
409
+ </div>
410
+ </div>
411
+
412
+ <!-- Pokemon & PvP -->
413
+ <div className="grid md:grid-cols-2 gap-6">
414
+ <!-- Active Pokemon -->
415
+ <div className="p-8 rounded-[2.5rem] bg-white/[0.03] border border-white/5 space-y-6">
416
+ <div className="flex items-center gap-3">
417
+ <span className="text-xl">🐉</span>
418
+ <h3 className="font-black text-lg">Pokémon Ativo</h3>
419
+ </div>
420
+
421
+ ${rpgInfo.active_pokemon
422
+ ? html`
423
+ <div className="flex items-center gap-6 p-6 rounded-3xl bg-white/5 border border-white/5 group hover:border-primary/30 transition-colors">
424
+ <div className="relative">
425
+ <div className="w-16 h-16 bg-white/10 rounded-full flex items-center justify-center text-3xl">
426
+ <img src=${'https://raw.githubusercontent.com/PokeAPI/sprites/master/sprites/pokemon/' + (rpgInfo.active_pokemon.is_shiny ? 'shiny/' : '') + rpgInfo.active_pokemon.poke_id + '.png'} alt="Pokemon" className="w-20 h-20 scale-125 object-contain" />
427
+ </div>
428
+ ${rpgInfo.active_pokemon.is_shiny && html`<div className="absolute -top-2 -right-2 text-warning animate-pulse">✨</div>`}
429
+ </div>
430
+ <div>
431
+ <h4 className="text-xl font-black capitalize text-white group-hover:text-primary transition-colors">${rpgInfo.active_pokemon.nickname || 'Desconhecido'}</h4>
432
+ <p className="text-xs font-bold text-white/50 uppercase tracking-widest mt-1">Nível ${rpgInfo.active_pokemon.level}</p>
433
+ </div>
434
+ </div>
435
+ `
436
+ : html`
437
+ <div className="p-6 text-center rounded-3xl bg-white/5 border border-white/5 border-dashed">
438
+ <p className="text-sm font-bold text-white/40">Nenhum Pokémon ativo no momento.</p>
439
+ </div>
440
+ `}
441
+
442
+ <div className="flex items-center justify-between p-4 rounded-2xl bg-white/5">
443
+ <span className="text-[10px] font-black uppercase tracking-widest text-white/40">Total Capturados</span>
444
+ <span className="text-lg font-black text-white">${rpgInfo.total_pokemons}</span>
445
+ </div>
446
+ </div>
447
+
448
+ <!-- PvP Stats -->
449
+ <div className="p-8 rounded-[2.5rem] bg-white/[0.03] border border-white/5 space-y-6">
450
+ <div className="flex items-center gap-3">
451
+ <span className="text-xl">🏆</span>
452
+ <h3 className="font-black text-lg">Arena PvP (Semanal)</h3>
453
+ </div>
454
+
455
+ <div className="grid grid-cols-2 gap-4">
456
+ <div className="p-4 rounded-2xl bg-white/5 text-center">
457
+ <p className="text-3xl font-black text-white mb-1">${rpgInfo.pvp?.matches || 0}</p>
458
+ <p className="text-[9px] font-black uppercase tracking-widest text-white/40">Batalhas</p>
459
+ </div>
460
+ <div className="p-4 rounded-2xl bg-success/10 border border-success/20 text-center">
461
+ <p className="text-3xl font-black text-success mb-1">${rpgInfo.pvp?.matches > 0 ? Math.round(((rpgInfo.pvp?.wins || 0) / rpgInfo.pvp.matches) * 100) : 0}%</p>
462
+ <p className="text-[9px] font-black uppercase tracking-widest text-success/60">Vitórias</p>
463
+ </div>
464
+ <div className="p-4 rounded-2xl bg-info/10 text-center">
465
+ <p className="text-xl font-black text-info mb-1">${rpgInfo.pvp?.wins || 0}</p>
466
+ <p className="text-[9px] font-black uppercase tracking-widest text-info/60">Ganha</p>
467
+ </div>
468
+ <div className="p-4 rounded-2xl bg-error/10 text-center">
469
+ <p className="text-xl font-black text-error mb-1">${rpgInfo.pvp?.losses || 0}</p>
470
+ <p className="text-[9px] font-black uppercase tracking-widest text-error/60">Perdida</p>
471
+ </div>
472
+ </div>
473
+ </div>
474
+ </div>
475
+ </div>
476
+ `}
477
+ ${activeTab === 'account' &&
478
+ html`
479
+ <div className="space-y-10 max-w-2xl mx-auto lg:mx-0">
480
+ <div className="flex items-center gap-6">
481
+ <div className="w-16 h-16 rounded-3xl bg-emerald-500/10 border border-emerald-500/20 flex items-center justify-center text-3xl">🛡️</div>
482
+ <div>
483
+ <h3 className="text-2xl font-black tracking-tight">Segurança da Conta</h3>
484
+ <p className="text-white/40 font-medium text-sm leading-relaxed">Mantenha sua chave de acesso segura e atualizada.</p>
485
+ </div>
486
+ </div>
487
+
488
+ <form className="space-y-6">
489
+ <div className="grid sm:grid-cols-2 gap-6">
490
+ <div className="space-y-2">
491
+ <label className="text-[10px] font-black uppercase tracking-widest text-white/30 ml-4">Nova Senha</label>
492
+ <input type="password" placeholder="••••••••" className="w-full h-14 bg-white/5 border border-white/10 rounded-2xl px-6 focus:border-primary outline-none transition-all font-mono" />
493
+ </div>
494
+ <div className="space-y-2">
495
+ <label className="text-[10px] font-black uppercase tracking-widest text-white/30 ml-4">Confirmar</label>
496
+ <input type="password" placeholder="••••••••" className="w-full h-14 bg-white/5 border border-white/10 rounded-2xl px-6 focus:border-primary outline-none transition-all font-mono" />
497
+ </div>
498
+ </div>
499
+ <button className="group relative inline-flex items-center justify-center">
500
+ <div className="absolute inset-0 bg-primary/20 blur-xl rounded-xl opacity-0 group-hover:opacity-100 transition-opacity"></div>
501
+ <div className="relative bg-primary text-primary-content px-10 py-4 rounded-xl font-black text-[10px] uppercase tracking-[0.2em] shadow-xl hover:scale-105 transition-all">Atualizar Credenciais</div>
502
+ </button>
503
+ </form>
504
+ </div>
505
+ `}
506
+ ${activeTab === 'support' &&
507
+ html`
508
+ <div className="text-center max-w-lg mx-auto py-16 space-y-10 relative">
509
+ <div className="absolute inset-0 bg-primary/5 blur-[100px] rounded-full"></div>
510
+ <div className="relative w-24 h-24 rounded-[2.5rem] bg-primary/10 border border-primary/20 flex items-center justify-center text-5xl mx-auto shadow-2xl">🎧</div>
511
+ <div className="relative space-y-3">
512
+ <h3 className="text-3xl font-black tracking-tighter text-white">Canal de Suporte</h3>
513
+ <p className="text-white/40 font-medium leading-relaxed">Nosso time de especialistas está pronto para te ajudar com qualquer dúvida técnica ou financeira.</p>
514
+ </div>
515
+ <a href="https://wa.me/559591122954" target="_blank" className="relative flex group">
516
+ <div className="absolute inset-0 bg-success/20 blur-2xl rounded-2xl group-hover:bg-success/40 transition-all"></div>
517
+ <div className="relative w-full bg-success text-white py-5 rounded-2xl font-black text-xs uppercase tracking-[0.3em] shadow-2xl group-hover:scale-[1.02] active:scale-95 transition-all">Iniciar Chat no WhatsApp</div>
518
+ </a>
519
+ </div>
520
+ `}
521
+ </div>
522
+ `}
523
+ </div>
524
+ </div>
525
+ </div>
526
+ </div>
527
+ </main>
528
+
529
+ <!-- Footer Minimal -->
530
+ <footer className="py-12 border-t border-white/5 mt-auto relative z-10 bg-[#020617]">
531
+ <div className="container mx-auto px-4 text-center">
532
+ <p className="text-[10px] font-black uppercase tracking-[0.5em] text-white/10">© 2026 OMNIZAP CORE · SECURE USER ENVIRONMENT</p>
533
+ </div>
534
+ </footer>
535
+ </div>
536
+ `;
537
+ };
538
+
539
+ const rootElement = document.getElementById('user-react-root');
540
+ if (rootElement) {
541
+ const config = {
542
+ apiBasePath: rootElement.dataset.apiBasePath || DEFAULT_API_BASE_PATH,
543
+ loginPath: rootElement.dataset.loginPath || DEFAULT_LOGIN_PATH,
544
+ fallbackAvatar: DEFAULT_FALLBACK_AVATAR,
545
+ };
546
+ createRoot(rootElement).render(html`<${UserApp} config=${config} />`);
547
+ }