@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,443 @@
1
+ import fs from 'node:fs';
2
+ import path from 'node:path';
3
+
4
+ const MODULES_DIR = path.resolve(process.cwd(), 'app/modules');
5
+
6
+ const normalizeToken = (value) =>
7
+ String(value || '')
8
+ .trim()
9
+ .toLowerCase();
10
+
11
+ const escapeRegExp = (value) => String(value || '').replace(/[.*+?^${}()|[\]\\]/g, '\\$&');
12
+
13
+ const readJson = (filePath) => JSON.parse(fs.readFileSync(filePath, 'utf8'));
14
+
15
+ const discoverModuleConfigFiles = () => {
16
+ let moduleEntries = [];
17
+ try {
18
+ moduleEntries = fs.readdirSync(MODULES_DIR, { withFileTypes: true });
19
+ } catch {
20
+ return [];
21
+ }
22
+
23
+ return moduleEntries
24
+ .filter((entry) => entry.isDirectory())
25
+ .map((entry) => {
26
+ const moduleDir = entry.name;
27
+ return {
28
+ moduleDir,
29
+ modulePath: path.join(MODULES_DIR, moduleDir),
30
+ configPath: path.join(MODULES_DIR, moduleDir, 'commandConfig.json'),
31
+ };
32
+ })
33
+ .filter((entry) => fs.existsSync(entry.configPath))
34
+ .sort((a, b) => a.moduleDir.localeCompare(b.moduleDir));
35
+ };
36
+
37
+ const isObject = (value) => Boolean(value && typeof value === 'object' && !Array.isArray(value));
38
+
39
+ const ensureBooleanField = (target, field, errors, context) => {
40
+ if (typeof target?.[field] !== 'boolean') {
41
+ errors.push(`${context}: campo booleano obrigatorio ausente/invalido '${field}'`);
42
+ }
43
+ };
44
+
45
+ const ensureArrayField = (target, field, errors, context) => {
46
+ if (!Array.isArray(target?.[field])) {
47
+ errors.push(`${context}: campo array obrigatorio ausente/invalido '${field}'`);
48
+ }
49
+ };
50
+
51
+ const ensureStringField = (target, field, errors, context, { allowEmpty = false } = {}) => {
52
+ const value = target?.[field];
53
+ if (typeof value !== 'string') {
54
+ errors.push(`${context}: campo string obrigatorio ausente/invalido '${field}'`);
55
+ return;
56
+ }
57
+ if (!allowEmpty && !value.trim()) {
58
+ errors.push(`${context}: campo string vazio '${field}'`);
59
+ }
60
+ };
61
+
62
+ const validateLegacyPreConditions = (preCondicoes, errors, context) => {
63
+ if (!isObject(preCondicoes)) {
64
+ errors.push(`${context}: pre_condicoes ausente/invalido`);
65
+ return;
66
+ }
67
+ const requiredBooleans = ['requer_grupo', 'requer_admin', 'requer_admin_principal', 'requer_google_login', 'requer_nsfw', 'requer_midia', 'requer_mensagem_respondida'];
68
+ for (const field of requiredBooleans) {
69
+ ensureBooleanField(preCondicoes, field, errors, `${context}.pre_condicoes`);
70
+ }
71
+ };
72
+
73
+ const validateLegacyCommandShape = (command, { moduleDir }) => {
74
+ const errors = [];
75
+ const context = `${moduleDir}.${String(command?.name || 'command')}`;
76
+
77
+ if (!isObject(command)) {
78
+ errors.push(`${context}: entrada de comando invalida`);
79
+ return errors;
80
+ }
81
+
82
+ ensureStringField(command, 'name', errors, context);
83
+ ensureArrayField(command, 'aliases', errors, context);
84
+ ensureArrayField(command, 'metodos_de_uso', errors, context);
85
+ ensureArrayField(command, 'argumentos', errors, context);
86
+ ensureStringField(command, 'categoria', errors, context);
87
+ ensureBooleanField(command, 'enabled', errors, context);
88
+ validateLegacyPreConditions(command.pre_condicoes, errors, context);
89
+
90
+ return errors;
91
+ };
92
+
93
+ const validateV2RequirementsShape = (requirements, errors, context) => {
94
+ if (!isObject(requirements)) {
95
+ errors.push(`${context}: requirements ausente/invalido`);
96
+ return;
97
+ }
98
+ const requiredBooleans = ['require_group', 'require_group_admin', 'require_bot_owner', 'require_google_login', 'require_nsfw_enabled', 'require_media', 'require_reply_message'];
99
+ for (const field of requiredBooleans) {
100
+ ensureBooleanField(requirements, field, errors, `${context}.requirements`);
101
+ }
102
+ };
103
+
104
+ const validateV2HandlerShape = (handler, errors, context) => {
105
+ if (!isObject(handler)) {
106
+ errors.push(`${context}: handler ausente/invalido`);
107
+ return;
108
+ }
109
+ ensureStringField(handler, 'file', errors, `${context}.handler`);
110
+ ensureStringField(handler, 'method', errors, `${context}.handler`);
111
+ };
112
+
113
+ const validateV2CommandShape = (command, { moduleDir, moduleKey }) => {
114
+ const errors = [];
115
+ const context = `${moduleDir}.${String(command?.name || 'command')}`;
116
+
117
+ const requiredStringFields = ['id', 'description', 'permission', 'version', 'stability', 'risk_level'];
118
+ for (const field of requiredStringFields) {
119
+ ensureStringField(command, field, errors, context);
120
+ }
121
+
122
+ ensureArrayField(command, 'usage', errors, context);
123
+ ensureArrayField(command, 'contexts', errors, context);
124
+ ensureArrayField(command, 'collected_data', errors, context);
125
+ ensureArrayField(command, 'arguments', errors, context);
126
+ ensureArrayField(command, 'dependencies', errors, context);
127
+ ensureArrayField(command, 'side_effects', errors, context);
128
+
129
+ ensureBooleanField(command, 'deprecated', errors, context);
130
+
131
+ if (!isObject(command.docs)) {
132
+ errors.push(`${context}: docs ausente/invalido`);
133
+ }
134
+ if (!isObject(command.behavior)) {
135
+ errors.push(`${context}: behavior ausente/invalido`);
136
+ }
137
+ if (!isObject(command.limits)) {
138
+ errors.push(`${context}: limits ausente/invalido`);
139
+ }
140
+ if (!isObject(command.observability)) {
141
+ errors.push(`${context}: observability ausente/invalido`);
142
+ }
143
+ if (!isObject(command.privacy)) {
144
+ errors.push(`${context}: privacy ausente/invalido`);
145
+ }
146
+ if (!isObject(command.discovery)) {
147
+ errors.push(`${context}: discovery ausente/invalido`);
148
+ }
149
+ if (!isObject(command.access)) {
150
+ errors.push(`${context}: access ausente/invalido`);
151
+ }
152
+ if (!isObject(command.plan_limits)) {
153
+ errors.push(`${context}: plan_limits ausente/invalido`);
154
+ }
155
+
156
+ validateV2RequirementsShape(command.requirements, errors, context);
157
+ validateV2HandlerShape(command.handler, errors, context);
158
+
159
+ const expectedPrefix = `${moduleKey}.`;
160
+ if (typeof command.id === 'string' && command.id.trim() && !command.id.startsWith(expectedPrefix)) {
161
+ errors.push(`${context}: id fora do prefixo esperado '${expectedPrefix}' (recebido: ${command.id})`);
162
+ }
163
+
164
+ return errors;
165
+ };
166
+
167
+ const validateModuleRootShape = (moduleEntry) => {
168
+ const errors = [];
169
+ const config = moduleEntry.config;
170
+ const context = moduleEntry.moduleDir;
171
+
172
+ if (!isObject(config)) {
173
+ errors.push(`${context}: commandConfig raiz invalido`);
174
+ return errors;
175
+ }
176
+
177
+ ensureStringField(config, 'schema_version', errors, context);
178
+ ensureStringField(config, 'module', errors, context);
179
+ ensureBooleanField(config, 'enabled', errors, context);
180
+ ensureArrayField(config, 'source_files', errors, context);
181
+ ensureArrayField(config, 'commands', errors, context);
182
+
183
+ const major = Number.parseInt(String(config?.schema_version || '').split('.')[0], 10);
184
+ if (Number.isFinite(major) && major >= 2 && !isObject(config.defaults)) {
185
+ errors.push(`${context}: defaults obrigatorio para schema_version >= 2`);
186
+ }
187
+
188
+ return errors;
189
+ };
190
+
191
+ const validateStructuralShapes = ({ modules }) => {
192
+ const errors = [];
193
+ let validatedModules = 0;
194
+ let validatedCommands = 0;
195
+
196
+ for (const moduleEntry of modules) {
197
+ const rootErrors = validateModuleRootShape(moduleEntry);
198
+ errors.push(...rootErrors);
199
+
200
+ const config = moduleEntry.config;
201
+ const commands = Array.isArray(config?.commands) ? config.commands : [];
202
+ const major = Number.parseInt(String(config?.schema_version || '').split('.')[0], 10);
203
+ const moduleKey =
204
+ String(config?.module || moduleEntry.moduleDir)
205
+ .replace(/module$/i, '')
206
+ .trim()
207
+ .toLowerCase()
208
+ .replace(/[^a-z0-9]+/g, '') || 'module';
209
+
210
+ for (const command of commands) {
211
+ errors.push(...validateLegacyCommandShape(command, moduleEntry));
212
+ if (Number.isFinite(major) && major >= 2) {
213
+ errors.push(...validateV2CommandShape(command, { moduleDir: moduleEntry.moduleDir, moduleKey }));
214
+ }
215
+ }
216
+
217
+ validatedModules += 1;
218
+ validatedCommands += commands.length;
219
+ }
220
+
221
+ return { errors, validatedModules, validatedCommands };
222
+ };
223
+
224
+ const buildCommandRef = ({ moduleName, commandName }) => `${String(moduleName || '').trim() || 'module'}.${String(commandName || '').trim() || 'command'}`;
225
+
226
+ const methodExistsInSource = (source, methodName) => {
227
+ const escaped = escapeRegExp(methodName);
228
+ const patterns = [new RegExp(`export\\s+(?:async\\s+)?function\\s+${escaped}\\b`), new RegExp(`export\\s+const\\s+${escaped}\\b`), new RegExp(`export\\s+\\{[^}]*\\b${escaped}\\b[^}]*\\}`, 's'), new RegExp(`\\bfunction\\s+${escaped}\\b`), new RegExp(`\\b${escaped}\\s*:\\s*(?:async\\s*)?\\(`), new RegExp(`^\\s*(?:async\\s+)?${escaped}\\s*\\(`, 'm')];
229
+ return patterns.some((regex) => regex.test(source));
230
+ };
231
+
232
+ const validateHandlerReferences = ({ modules }) => {
233
+ const errors = [];
234
+ const sourceCache = new Map();
235
+
236
+ for (const moduleEntry of modules) {
237
+ const major = Number.parseInt(String(moduleEntry.config?.schema_version || '').split('.')[0], 10);
238
+ const requireHandler = Number.isFinite(major) && major >= 2;
239
+ const commands = Array.isArray(moduleEntry.config?.commands) ? moduleEntry.config.commands : [];
240
+ const moduleName = moduleEntry.config?.module || moduleEntry.moduleDir;
241
+
242
+ for (const command of commands) {
243
+ if (!command || command.enabled === false) continue;
244
+ const commandName = String(command.name || '').trim();
245
+ const commandRef = buildCommandRef({ moduleName, commandName });
246
+ const handler = command.handler;
247
+
248
+ if (!handler || typeof handler !== 'object') {
249
+ if (requireHandler) {
250
+ errors.push(`${commandRef}: handler ausente para schema v2`);
251
+ }
252
+ continue;
253
+ }
254
+
255
+ const file = String(handler.file || '').trim();
256
+ const method = String(handler.method || '').trim();
257
+
258
+ if (!file || !method) {
259
+ errors.push(`${commandRef}: handler.file e handler.method sao obrigatorios`);
260
+ continue;
261
+ }
262
+
263
+ const absoluteHandlerPath = path.resolve(moduleEntry.modulePath, file);
264
+ const modulePathWithSep = moduleEntry.modulePath.endsWith(path.sep) ? moduleEntry.modulePath : `${moduleEntry.modulePath}${path.sep}`;
265
+
266
+ if (absoluteHandlerPath !== moduleEntry.modulePath && !absoluteHandlerPath.startsWith(modulePathWithSep)) {
267
+ errors.push(`${commandRef}: handler.file aponta para fora do modulo (${file})`);
268
+ continue;
269
+ }
270
+
271
+ if (!fs.existsSync(absoluteHandlerPath)) {
272
+ errors.push(`${commandRef}: handler.file nao encontrado (${file})`);
273
+ continue;
274
+ }
275
+
276
+ let source = sourceCache.get(absoluteHandlerPath);
277
+ if (!source) {
278
+ source = fs.readFileSync(absoluteHandlerPath, 'utf8');
279
+ sourceCache.set(absoluteHandlerPath, source);
280
+ }
281
+
282
+ if (!methodExistsInSource(source, method)) {
283
+ errors.push(`${commandRef}: handler.method nao encontrado em ${file} (${method})`);
284
+ }
285
+ }
286
+ }
287
+
288
+ return errors;
289
+ };
290
+
291
+ const validateGlobalCollisions = ({ modules }) => {
292
+ const errors = [];
293
+ const tokenMap = new Map();
294
+ const idMap = new Map();
295
+
296
+ const upsertMapValue = (map, key, value) => {
297
+ const normalizedKey = normalizeToken(key);
298
+ if (!normalizedKey) return;
299
+ if (!map.has(normalizedKey)) {
300
+ map.set(normalizedKey, []);
301
+ }
302
+ map.get(normalizedKey).push(value);
303
+ };
304
+
305
+ for (const moduleEntry of modules) {
306
+ const moduleName = moduleEntry.config?.module || moduleEntry.moduleDir;
307
+ const commands = Array.isArray(moduleEntry.config?.commands) ? moduleEntry.config.commands : [];
308
+
309
+ for (const command of commands) {
310
+ if (!command || command.enabled === false) continue;
311
+
312
+ const commandName = String(command.name || '').trim();
313
+ if (!commandName) continue;
314
+ const commandRef = buildCommandRef({ moduleName, commandName });
315
+ const aliases = Array.isArray(command.aliases) ? command.aliases : [];
316
+
317
+ upsertMapValue(tokenMap, commandName, {
318
+ commandRef,
319
+ moduleName,
320
+ commandName,
321
+ source: 'name',
322
+ token: commandName,
323
+ });
324
+
325
+ for (const alias of aliases) {
326
+ const aliasToken = String(alias || '').trim();
327
+ if (!aliasToken) continue;
328
+ upsertMapValue(tokenMap, aliasToken, {
329
+ commandRef,
330
+ moduleName,
331
+ commandName,
332
+ source: 'alias',
333
+ token: aliasToken,
334
+ });
335
+ }
336
+
337
+ if (command.id) {
338
+ upsertMapValue(idMap, String(command.id), {
339
+ commandRef,
340
+ moduleName,
341
+ commandName,
342
+ id: String(command.id),
343
+ });
344
+ }
345
+ }
346
+ }
347
+
348
+ for (const [token, entries] of tokenMap.entries()) {
349
+ const distinctCommands = new Map(entries.map((entry) => [entry.commandRef, entry]));
350
+ if (distinctCommands.size <= 1) continue;
351
+ const entryList = [...distinctCommands.values()].map((entry) => `${entry.commandRef} (${entry.source}:${entry.token})`).join(' | ');
352
+ errors.push(`colisao de comando/alias '${token}': ${entryList}`);
353
+ }
354
+
355
+ for (const [id, entries] of idMap.entries()) {
356
+ const distinctCommands = new Map(entries.map((entry) => [entry.commandRef, entry]));
357
+ if (distinctCommands.size <= 1) continue;
358
+ const entryList = [...distinctCommands.values()].map((entry) => entry.commandRef).join(' | ');
359
+ errors.push(`colisao de id '${id}': ${entryList}`);
360
+ }
361
+
362
+ return errors;
363
+ };
364
+
365
+ export const validateAllCommandConfigs = () => {
366
+ const modules = [];
367
+ const errors = [];
368
+ const warnings = [];
369
+
370
+ const configFiles = discoverModuleConfigFiles();
371
+ for (const entry of configFiles) {
372
+ try {
373
+ const config = readJson(entry.configPath);
374
+ modules.push({
375
+ ...entry,
376
+ config,
377
+ });
378
+ } catch (error) {
379
+ errors.push(`${entry.moduleDir}: falha ao ler/parsear commandConfig.json (${error?.message})`);
380
+ }
381
+ }
382
+
383
+ if (!modules.length) {
384
+ return {
385
+ ok: false,
386
+ errors: [...errors, 'nenhum commandConfig.json encontrado em app/modules'],
387
+ warnings,
388
+ modulesValidated: 0,
389
+ commandsValidated: 0,
390
+ };
391
+ }
392
+
393
+ const structuralResult = validateStructuralShapes({ modules });
394
+ errors.push(...structuralResult.errors);
395
+
396
+ const collisionErrors = validateGlobalCollisions({ modules });
397
+ errors.push(...collisionErrors);
398
+
399
+ const handlerErrors = validateHandlerReferences({ modules });
400
+ errors.push(...handlerErrors);
401
+
402
+ return {
403
+ ok: errors.length === 0,
404
+ errors,
405
+ warnings,
406
+ modulesValidated: structuralResult.validatedModules,
407
+ commandsValidated: structuralResult.validatedCommands,
408
+ };
409
+ };
410
+
411
+ export const formatCommandConfigValidationReport = (report, { maxErrors = 40 } = {}) => {
412
+ if (!report || typeof report !== 'object') return 'Relatorio de validacao indisponivel.';
413
+
414
+ const lines = [];
415
+ lines.push(`modules_validated=${report.modulesValidated ?? 0} commands_validated=${report.commandsValidated ?? 0}`);
416
+
417
+ const warnings = Array.isArray(report.warnings) ? report.warnings : [];
418
+ const errors = Array.isArray(report.errors) ? report.errors : [];
419
+
420
+ if (warnings.length) {
421
+ lines.push(`warnings=${warnings.length}`);
422
+ for (const warning of warnings.slice(0, maxErrors)) {
423
+ lines.push(`WARN: ${warning}`);
424
+ }
425
+ if (warnings.length > maxErrors) {
426
+ lines.push(`WARN: ... ${warnings.length - maxErrors} warnings adicionais omitidos`);
427
+ }
428
+ }
429
+
430
+ if (errors.length) {
431
+ lines.push(`errors=${errors.length}`);
432
+ for (const error of errors.slice(0, maxErrors)) {
433
+ lines.push(`ERROR: ${error}`);
434
+ }
435
+ if (errors.length > maxErrors) {
436
+ lines.push(`ERROR: ... ${errors.length - maxErrors} erros adicionais omitidos`);
437
+ }
438
+ } else {
439
+ lines.push('errors=0');
440
+ }
441
+
442
+ return lines.join('\n');
443
+ };
@@ -0,0 +1,192 @@
1
+ const normalizeText = (value) =>
2
+ String(value || '')
3
+ .trim()
4
+ .toLowerCase();
5
+
6
+ const sanitizeToken = (value, fallback = 'arg') => {
7
+ const normalized = normalizeText(value)
8
+ .normalize('NFD')
9
+ .replace(/[\u0300-\u036f]/g, '')
10
+ .replace(/[^a-z0-9_]/g, '_')
11
+ .replace(/_+/g, '_')
12
+ .replace(/^_+|_+$/g, '');
13
+
14
+ return normalized || fallback;
15
+ };
16
+
17
+ const parseArrayType = (rawType) => {
18
+ const match = /^array\s*<\s*([a-z0-9_]+)\s*>$/i.exec(String(rawType || '').trim());
19
+ if (!match) return null;
20
+ return (
21
+ String(match[1] || '')
22
+ .trim()
23
+ .toLowerCase() || 'string'
24
+ );
25
+ };
26
+
27
+ const mapArgumentTypeToJsonSchema = (rawType) => {
28
+ const normalized = normalizeText(rawType);
29
+ const arrayItemType = parseArrayType(normalized);
30
+ if (arrayItemType) {
31
+ return {
32
+ type: 'array',
33
+ items: mapArgumentTypeToJsonSchema(arrayItemType),
34
+ };
35
+ }
36
+
37
+ if (normalized === 'integer' || normalized === 'int') return { type: 'integer' };
38
+ if (normalized === 'number' || normalized === 'float' || normalized === 'double') {
39
+ return { type: 'number' };
40
+ }
41
+ if (normalized === 'boolean' || normalized === 'bool') return { type: 'boolean' };
42
+ if (normalized === 'object' || normalized === 'json') {
43
+ return {
44
+ type: 'object',
45
+ additionalProperties: true,
46
+ };
47
+ }
48
+
49
+ return { type: 'string' };
50
+ };
51
+
52
+ const buildArgumentDescription = (argument) => {
53
+ const parts = [];
54
+ const validation = String(argument?.validacao || '').trim();
55
+ const defaultValue = argument?.default;
56
+
57
+ if (validation) parts.push(`Validacao: ${validation}.`);
58
+ if (defaultValue !== undefined && defaultValue !== null && String(defaultValue).trim()) {
59
+ parts.push(`Padrao: ${String(defaultValue).trim()}.`);
60
+ }
61
+
62
+ return parts.join(' ').trim() || 'Argumento do comando.';
63
+ };
64
+
65
+ const buildArgumentSpecs = (commandEntry = {}) => {
66
+ const sourceArgs = Array.isArray(commandEntry.argumentos) ? commandEntry.argumentos : [];
67
+ const usedKeys = new Set();
68
+
69
+ return sourceArgs
70
+ .map((argument, index) => {
71
+ if (!argument || typeof argument !== 'object') return null;
72
+
73
+ const originalName = String(argument.nome || '').trim();
74
+ const fallbackName = `arg_${index + 1}`;
75
+ const baseKey = sanitizeToken(originalName || fallbackName, fallbackName);
76
+ let key = baseKey;
77
+ let suffix = 2;
78
+ while (usedKeys.has(key)) {
79
+ key = `${baseKey}_${suffix}`;
80
+ suffix += 1;
81
+ }
82
+ usedKeys.add(key);
83
+
84
+ const typeSchema = mapArgumentTypeToJsonSchema(argument.tipo);
85
+ const isRequired = argument.obrigatorio === true;
86
+
87
+ return {
88
+ key,
89
+ originalName: originalName || key,
90
+ type: typeSchema.type,
91
+ rawType: String(argument.tipo || '').trim() || 'string',
92
+ schema: {
93
+ ...typeSchema,
94
+ description: buildArgumentDescription(argument),
95
+ ...(argument.default !== undefined && argument.default !== null ? { default: argument.default } : {}),
96
+ },
97
+ required: isRequired,
98
+ defaultValue: argument.default,
99
+ validation: String(argument.validacao || '').trim() || null,
100
+ };
101
+ })
102
+ .filter(Boolean);
103
+ };
104
+
105
+ const sanitizeToolName = (value) => {
106
+ const safe = sanitizeToken(value, 'command');
107
+ return safe.slice(0, 64);
108
+ };
109
+
110
+ const buildCommandDescription = (commandEntry = {}) => {
111
+ const description = String(commandEntry.descricao || '').trim() || 'Executa um comando do bot.';
112
+ const permission = String(commandEntry.permissao_necessaria || '').trim();
113
+ const where = Array.isArray(commandEntry.local_de_uso) ? commandEntry.local_de_uso.filter(Boolean).join(', ') : '';
114
+
115
+ const extra = [];
116
+ if (permission) extra.push(`Permissao: ${permission}.`);
117
+ if (where) extra.push(`Local: ${where}.`);
118
+
119
+ return `${description}${extra.length ? ` ${extra.join(' ')}` : ''}`.trim();
120
+ };
121
+
122
+ export const buildFunctionToolFromCommandConfig = ({ moduleKey, commandEntry } = {}) => {
123
+ if (!commandEntry || commandEntry.enabled === false) return null;
124
+
125
+ const commandName = sanitizeToolName(commandEntry.name);
126
+ if (!commandName) return null;
127
+
128
+ const argumentSpecs = buildArgumentSpecs(commandEntry);
129
+ const properties = {};
130
+ const required = [];
131
+
132
+ for (const spec of argumentSpecs) {
133
+ properties[spec.key] = spec.schema;
134
+ if (spec.required) required.push(spec.key);
135
+ }
136
+
137
+ const tool = {
138
+ type: 'function',
139
+ function: {
140
+ name: commandName,
141
+ description: buildCommandDescription(commandEntry),
142
+ parameters: {
143
+ type: 'object',
144
+ properties,
145
+ required,
146
+ additionalProperties: false,
147
+ },
148
+ },
149
+ };
150
+
151
+ return {
152
+ moduleKey: String(moduleKey || '').trim() || 'module',
153
+ commandName,
154
+ argumentSpecs,
155
+ tool,
156
+ };
157
+ };
158
+
159
+ export const mapToolArgsToCommandText = (argumentSpecs = [], argsObject = {}) => {
160
+ const safeArgsObject = argsObject && typeof argsObject === 'object' ? argsObject : {};
161
+ const tokenArgs = [];
162
+ const normalizedArgs = {};
163
+
164
+ for (const spec of argumentSpecs) {
165
+ let value = safeArgsObject[spec.key];
166
+
167
+ if ((value === undefined || value === null || value === '') && spec.defaultValue !== undefined) {
168
+ value = spec.defaultValue;
169
+ }
170
+
171
+ if (value === undefined || value === null || value === '') continue;
172
+
173
+ normalizedArgs[spec.key] = value;
174
+
175
+ if (Array.isArray(value)) {
176
+ for (const item of value) {
177
+ const token = String(item || '').trim();
178
+ if (token) tokenArgs.push(token);
179
+ }
180
+ continue;
181
+ }
182
+
183
+ const token = String(value).trim();
184
+ if (token) tokenArgs.push(token);
185
+ }
186
+
187
+ return {
188
+ normalizedArgs,
189
+ args: tokenArgs,
190
+ text: tokenArgs.join(' ').trim(),
191
+ };
192
+ };