@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.
- package/.clusterfuzzlite/Dockerfile +10 -0
- package/.env.example +907 -0
- package/.github/codeql/codeql-config.yml +10 -0
- package/.github/dependabot.yml +35 -0
- package/.github/workflows/ci.yml +73 -0
- package/.github/workflows/codeql.yml +106 -0
- package/.github/workflows/db-migration-check.yml +98 -0
- package/.github/workflows/dependency-review.yml +22 -0
- package/.github/workflows/deploy.yml +95 -0
- package/.github/workflows/release.yml +106 -0
- package/.github/workflows/security-attest-provenance.yml +51 -0
- package/.github/workflows/security-gitleaks.yml +34 -0
- package/.github/workflows/security-runner-hardening.yml +31 -0
- package/.github/workflows/security-scorecard.yml +44 -0
- package/.github/workflows/security-zap-baseline.yml +44 -0
- package/.github/workflows/security-zap-full-scan.yml +43 -0
- package/.github/workflows/security-zizmor.yml +36 -0
- package/.github/workflows/wiki-sync.yml +44 -0
- package/.gitleaks.toml +15 -0
- package/.prettierrc +34 -0
- package/CODE_OF_CONDUCT.md +114 -0
- package/LICENSE +56 -0
- package/README.md +110 -0
- package/SECURITY.md +110 -0
- package/app/config/index.js +4 -0
- package/app/configParts/adminIdentity.js +92 -0
- package/app/configParts/baileysConfig.js +1818 -0
- package/app/configParts/groupUtils.js +692 -0
- package/app/configParts/loggerConfig.js +394 -0
- package/app/configParts/messagePersistenceService.js +305 -0
- package/app/connection/baileysCompatibility.test.js +40 -0
- package/app/connection/baileysDbAuthState.js +344 -0
- package/app/connection/socketController.js +2243 -0
- package/app/controllers/messageController.js +7 -0
- package/app/controllers/messagePipeline/commandMiddleware.js +146 -0
- package/app/controllers/messagePipeline/conversationMiddleware.js +183 -0
- package/app/controllers/messagePipeline/messagePipelineMiddlewares.test.js +522 -0
- package/app/controllers/messagePipeline/postProcessingMiddleware.js +41 -0
- package/app/controllers/messagePipeline/preProcessingMiddlewares.js +166 -0
- package/app/controllers/messageProcessingPipeline.js +699 -0
- package/app/modules/adminModule/AGENT.md +4056 -0
- package/app/modules/adminModule/adminAiHelpService.js +56 -0
- package/app/modules/adminModule/adminConfigRuntime.js +177 -0
- package/app/modules/adminModule/commandConfig.json +7122 -0
- package/app/modules/adminModule/groupCommandHandlers.js +1823 -0
- package/app/modules/adminModule/groupCommandHandlers.test.js +350 -0
- package/app/modules/adminModule/groupEventHandlers.js +399 -0
- package/app/modules/aiModule/AGENT.md +547 -0
- package/app/modules/aiModule/aiAiHelpService.js +14 -0
- package/app/modules/aiModule/aiConfigRuntime.js +135 -0
- package/app/modules/aiModule/catCommand.js +967 -0
- package/app/modules/aiModule/commandConfig.json +981 -0
- package/app/modules/analyticsModule/messageAnalysisEventRepository.js +83 -0
- package/app/modules/gameModule/AGENT.md +196 -0
- package/app/modules/gameModule/commandConfig.json +366 -0
- package/app/modules/gameModule/diceCommand.js +42 -0
- package/app/modules/gameModule/gameAiHelpService.js +14 -0
- package/app/modules/gameModule/gameConfigRuntime.js +68 -0
- package/app/modules/menuModule/AGENT.md +205 -0
- package/app/modules/menuModule/commandConfig.json +366 -0
- package/app/modules/menuModule/common.js +316 -0
- package/app/modules/menuModule/menuAiHelpService.js +14 -0
- package/app/modules/menuModule/menuConfigRuntime.js +68 -0
- package/app/modules/menuModule/menus.js +66 -0
- package/app/modules/playModule/AGENT.md +321 -0
- package/app/modules/playModule/commandConfig.json +584 -0
- package/app/modules/playModule/playAiHelpService.js +14 -0
- package/app/modules/playModule/playCommand.js +1417 -0
- package/app/modules/playModule/playConfigRuntime.js +68 -0
- package/app/modules/quoteModule/AGENT.md +199 -0
- package/app/modules/quoteModule/commandConfig.json +366 -0
- package/app/modules/quoteModule/quoteAiHelpService.js +14 -0
- package/app/modules/quoteModule/quoteCommand.js +842 -0
- package/app/modules/quoteModule/quoteConfigRuntime.js +68 -0
- package/app/modules/rpgPokemonModule/AGENT.md +229 -0
- package/app/modules/rpgPokemonModule/commandConfig.json +386 -0
- package/app/modules/rpgPokemonModule/rpgBattleCanvasRenderer.js +795 -0
- package/app/modules/rpgPokemonModule/rpgBattleService.js +2110 -0
- package/app/modules/rpgPokemonModule/rpgBattleService.test.js +770 -0
- package/app/modules/rpgPokemonModule/rpgEvolutionUtils.js +22 -0
- package/app/modules/rpgPokemonModule/rpgPokemonAiHelpService.js +14 -0
- package/app/modules/rpgPokemonModule/rpgPokemonCommand.js +174 -0
- package/app/modules/rpgPokemonModule/rpgPokemonConfigRuntime.js +68 -0
- package/app/modules/rpgPokemonModule/rpgPokemonDomain.js +192 -0
- package/app/modules/rpgPokemonModule/rpgPokemonDomain.test.js +93 -0
- package/app/modules/rpgPokemonModule/rpgPokemonEvolution.test.js +46 -0
- package/app/modules/rpgPokemonModule/rpgPokemonMessages.js +746 -0
- package/app/modules/rpgPokemonModule/rpgPokemonRepository.js +1847 -0
- package/app/modules/rpgPokemonModule/rpgPokemonService.js +6839 -0
- package/app/modules/rpgPokemonModule/rpgProfileCanvasRenderer.js +354 -0
- package/app/modules/statsModule/AGENT.md +320 -0
- package/app/modules/statsModule/commandConfig.json +540 -0
- package/app/modules/statsModule/globalRankingCommand.js +64 -0
- package/app/modules/statsModule/rankingCommand.js +41 -0
- package/app/modules/statsModule/rankingCommon.js +1305 -0
- package/app/modules/statsModule/statsAiHelpService.js +14 -0
- package/app/modules/statsModule/statsConfigRuntime.js +68 -0
- package/app/modules/stickerModule/AGENT.md +692 -0
- package/app/modules/stickerModule/addStickerMetadata.js +239 -0
- package/app/modules/stickerModule/commandConfig.json +1216 -0
- package/app/modules/stickerModule/convertToWebp.js +367 -0
- package/app/modules/stickerModule/stickerAiHelpService.js +14 -0
- package/app/modules/stickerModule/stickerCommand.js +446 -0
- package/app/modules/stickerModule/stickerConfigRuntime.js +68 -0
- package/app/modules/stickerModule/stickerConvertCommand.js +159 -0
- package/app/modules/stickerModule/stickerTextCommand.js +653 -0
- package/app/modules/stickerPackModule/AGENT.md +215 -0
- package/app/modules/stickerPackModule/autoPackCollectorRuntime.js +20 -0
- package/app/modules/stickerPackModule/autoPackCollectorService.js +357 -0
- package/app/modules/stickerPackModule/commandConfig.json +387 -0
- package/app/modules/stickerPackModule/domainEventOutboxRepository.js +227 -0
- package/app/modules/stickerPackModule/domainEvents.js +52 -0
- package/app/modules/stickerPackModule/semanticReclassificationEngine.js +429 -0
- package/app/modules/stickerPackModule/semanticReclassificationEngine.test.js +75 -0
- package/app/modules/stickerPackModule/semanticThemeClusterService.js +544 -0
- package/app/modules/stickerPackModule/stickerAssetClassificationRepository.js +400 -0
- package/app/modules/stickerPackModule/stickerAssetRepository.js +400 -0
- package/app/modules/stickerPackModule/stickerAssetReprocessQueueRepository.js +175 -0
- package/app/modules/stickerPackModule/stickerAutoPackByTagsRuntime.js +3702 -0
- package/app/modules/stickerPackModule/stickerClassificationBackgroundRuntime.js +559 -0
- package/app/modules/stickerPackModule/stickerClassificationService.js +557 -0
- package/app/modules/stickerPackModule/stickerDedicatedTaskWorkerRuntime.js +249 -0
- package/app/modules/stickerPackModule/stickerDomainEventBus.js +65 -0
- package/app/modules/stickerPackModule/stickerDomainEventConsumerRuntime.js +208 -0
- package/app/modules/stickerPackModule/stickerMarketplaceDriftService.js +99 -0
- package/app/modules/stickerPackModule/stickerObjectStorageService.js +285 -0
- package/app/modules/stickerPackModule/stickerPackAiHelpService.js +14 -0
- package/app/modules/stickerPackModule/stickerPackCommandHandlers.js +1148 -0
- package/app/modules/stickerPackModule/stickerPackConfigRuntime.js +68 -0
- package/app/modules/stickerPackModule/stickerPackEngagementRepository.js +152 -0
- package/app/modules/stickerPackModule/stickerPackErrors.js +30 -0
- package/app/modules/stickerPackModule/stickerPackInteractionEventRepository.js +101 -0
- package/app/modules/stickerPackModule/stickerPackItemRepository.js +432 -0
- package/app/modules/stickerPackModule/stickerPackMarketplaceService.js +313 -0
- package/app/modules/stickerPackModule/stickerPackMessageService.js +268 -0
- package/app/modules/stickerPackModule/stickerPackRepository.js +450 -0
- package/app/modules/stickerPackModule/stickerPackScoreSnapshotRepository.js +179 -0
- package/app/modules/stickerPackModule/stickerPackScoreSnapshotRuntime.js +271 -0
- package/app/modules/stickerPackModule/stickerPackService.js +733 -0
- package/app/modules/stickerPackModule/stickerPackServiceRuntime.js +32 -0
- package/app/modules/stickerPackModule/stickerPackUtils.js +107 -0
- package/app/modules/stickerPackModule/stickerStorageService.js +559 -0
- package/app/modules/stickerPackModule/stickerWorkerPipelineRuntime.js +242 -0
- package/app/modules/stickerPackModule/stickerWorkerTaskQueueRepository.js +242 -0
- package/app/modules/systemMetricsModule/AGENT.md +193 -0
- package/app/modules/systemMetricsModule/commandConfig.json +344 -0
- package/app/modules/systemMetricsModule/pingCommand.js +399 -0
- package/app/modules/systemMetricsModule/systemMetricsAiHelpService.js +14 -0
- package/app/modules/systemMetricsModule/systemMetricsConfigRuntime.js +68 -0
- package/app/modules/tiktokModule/AGENT.md +196 -0
- package/app/modules/tiktokModule/commandConfig.json +366 -0
- package/app/modules/tiktokModule/tiktokAiHelpService.js +14 -0
- package/app/modules/tiktokModule/tiktokCommand.js +716 -0
- package/app/modules/tiktokModule/tiktokConfigRuntime.js +68 -0
- package/app/modules/userModule/AGENT.md +200 -0
- package/app/modules/userModule/commandConfig.json +386 -0
- package/app/modules/userModule/userAiHelpService.js +14 -0
- package/app/modules/userModule/userCommand.js +1155 -0
- package/app/modules/userModule/userConfigRuntime.js +68 -0
- package/app/modules/waifuPicsModule/AGENT.md +431 -0
- package/app/modules/waifuPicsModule/commandConfig.json +780 -0
- package/app/modules/waifuPicsModule/waifuPicsAiHelpService.js +14 -0
- package/app/modules/waifuPicsModule/waifuPicsCommand.js +586 -0
- package/app/modules/waifuPicsModule/waifuPicsConfigRuntime.js +68 -0
- package/app/observability/metrics.js +766 -0
- package/app/services/ai/aiHelpResponseCacheRepository.js +280 -0
- package/app/services/ai/aiLearningRepository.js +400 -0
- package/app/services/ai/commandConfigEnrichmentRepository.js +769 -0
- package/app/services/ai/commandConfigEnrichmentService.js +452 -0
- package/app/services/ai/commandConfigValidationService.js +443 -0
- package/app/services/ai/commandToolBuilderService.js +192 -0
- package/app/services/ai/conversationRouterService.js +516 -0
- package/app/services/ai/geminiService.js +115 -0
- package/app/services/ai/geminiService.test.js +87 -0
- package/app/services/ai/globalModuleAiHelpService.js +1412 -0
- package/app/services/ai/globalToolCallingService.js +203 -0
- package/app/services/ai/messageCommandExecutionService.js +391 -0
- package/app/services/ai/moduleAiHelpCoreService.js +1099 -0
- package/app/services/ai/moduleAiHelpWrapperFactory.js +65 -0
- package/app/services/ai/moduleCommandConfigRuntimeService.js +113 -0
- package/app/services/ai/moduleToolExecutorService.js +464 -0
- package/app/services/ai/moduleToolRegistryService.js +178 -0
- package/app/services/ai/toolCandidateSelectorService.js +781 -0
- package/app/services/auth/googleWebLinkService.js +80 -0
- package/app/services/auth/whatsappLoginLinkService.js +230 -0
- package/app/services/external/pokeApiService.js +398 -0
- package/app/services/group/groupMetadataService.js +311 -0
- package/app/services/infra/dbWriteQueue.js +874 -0
- package/app/services/infra/featureFlagService.js +131 -0
- package/app/services/infra/queueUtils.js +55 -0
- package/app/services/messaging/captchaService.js +491 -0
- package/app/services/messaging/messagePersistenceService.js +1 -0
- package/app/services/messaging/newsBroadcastService.js +347 -0
- package/app/services/sticker/stickerFocusService.js +347 -0
- package/app/services/sticker/stickerFocusService.test.js +43 -0
- package/app/store/aiPromptStore.js +38 -0
- package/app/store/conversationSessionStore.js +131 -0
- package/app/store/groupConfigStore.js +58 -0
- package/app/store/premiumUserStore.js +54 -0
- package/app/utils/antiLink/antiLinkModule.js +700 -0
- package/app/utils/http/getImageBufferModule.js +18 -0
- package/app/utils/json/jsonSanitizer.js +113 -0
- package/app/utils/json/jsonSanitizer.test.js +40 -0
- package/app/utils/systemMetrics/systemMetricsModule.js +88 -0
- package/app/workers/aiLearningWorker.js +605 -0
- package/app/workers/commandConfigEnrichmentWorker.js +242 -0
- package/database/index.js +2075 -0
- package/database/init.js +151 -0
- package/database/migrations/.gitkeep +0 -0
- package/database/migrations/20260307_d0_hardening_down.sql +64 -0
- package/database/migrations/20260307_d0_hardening_up.sql +79 -0
- package/database/migrations/20260307_d1_terms_acceptance_down.sql +11 -0
- package/database/migrations/20260307_d1_terms_acceptance_up.sql +37 -0
- package/database/migrations/20260307_d2_auth_hardening_down.sql +75 -0
- package/database/migrations/20260307_d2_auth_hardening_up.sql +100 -0
- package/database/migrations/20260314_d7_canonical_sender_down.sql +53 -0
- package/database/migrations/20260314_d7_canonical_sender_up.sql +114 -0
- package/database/migrations/20260406_d30_security_analytics_down.sql +95 -0
- package/database/migrations/20260406_d30_security_analytics_up.sql +292 -0
- package/database/migrations/20260407_d31_web_google_session_token_hardening_down.sql +2 -0
- package/database/migrations/20260407_d31_web_google_session_token_hardening_up.sql +17 -0
- package/database/migrations/20260408_d32_ai_help_response_cache_down.sql +1 -0
- package/database/migrations/20260408_d32_ai_help_response_cache_up.sql +22 -0
- package/database/migrations/20260409_d33_ai_learning_tables_down.sql +4 -0
- package/database/migrations/20260409_d33_ai_learning_tables_up.sql +52 -0
- package/database/migrations/20260410_d34_command_config_enrichment_down.sql +3 -0
- package/database/migrations/20260410_d34_command_config_enrichment_up.sql +48 -0
- package/database/schema.sql +1186 -0
- package/docker-compose.yml +104 -0
- package/docs/audits/stickerCatalogController-out-of-scope.md +103 -0
- package/docs/audits/stickerCatalogController-symbols.md +58 -0
- package/docs/compliance/acceptable-use-policy-2026-03-07.md +35 -0
- package/docs/compliance/dpa-b2b-standard-2026-03-07.md +80 -0
- package/docs/compliance/monthly-compliance-checklist-2026-03-07.md +88 -0
- package/docs/compliance/notice-and-takedown-policy-2026-03-07.md +34 -0
- package/docs/compliance/privacy-policy-2026-03-07.md +75 -0
- package/docs/compliance/subprocessors-inventory-2026-03-07.md +16 -0
- package/docs/database/production-db-evolution-runbook-2026q1.md +365 -0
- package/docs/security/dsar-lgpd-runbook-2026-03-07.md +86 -0
- package/docs/security/incident-response-lgpd-anpd-runbook-2026-03-07.md +77 -0
- package/docs/security/network-hardening-runbook-2026-03-07.md +137 -0
- package/docs/seo/omnizap-seo-playbook-br-2026-02-28.md +238 -0
- package/docs/seo/satellite-page-template.md +116 -0
- package/docs/seo/satellite-pages-phase1.json +364 -0
- package/docs/wiki/Home.md +120 -0
- package/docs/wiki/pair-extraordinaire-2026-03-08.md +3 -0
- package/docs/wiki/recent-changes-2026-03-08.md +47 -0
- package/ecosystem.prod.config.cjs +135 -0
- package/eslint.config.js +89 -0
- package/index.js +488 -0
- package/ml/clip_classifier/Dockerfile +18 -0
- package/ml/clip_classifier/README.md +118 -0
- package/ml/clip_classifier/adaptive_scoring.py +40 -0
- package/ml/clip_classifier/classifier.py +654 -0
- package/ml/clip_classifier/embedding_store.py +481 -0
- package/ml/clip_classifier/env_loader.py +15 -0
- package/ml/clip_classifier/llm_label_expander.py +144 -0
- package/ml/clip_classifier/main.py +213 -0
- package/ml/clip_classifier/requirements.txt +10 -0
- package/ml/clip_classifier/similarity_engine.py +74 -0
- package/new-logo.png +0 -0
- package/observability/alert-rules.yml +60 -0
- package/observability/grafana/dashboards/omnizap-mysql.json +136 -0
- package/observability/grafana/dashboards/omnizap-overview.json +170 -0
- package/observability/grafana/provisioning/dashboards/dashboards.yml +11 -0
- package/observability/grafana/provisioning/datasources/datasources.yml +15 -0
- package/observability/loki-config.yml +38 -0
- package/observability/mysql-setup.sql +46 -0
- package/observability/prometheus.yml +35 -0
- package/observability/promtail-config.yml +84 -0
- package/observability/sticker-catalog-slo.md +83 -0
- package/observability/sticker-scale-hardening-rollout.md +128 -0
- package/package.json +144 -0
- package/public/apple-touch-icon.png +0 -0
- package/public/assets/css/commands-react.input.css +71 -0
- package/public/assets/css/create-pack-react.input.css +31 -0
- package/public/assets/css/home-react.input.css +106 -0
- package/public/assets/css/login-react.input.css +58 -0
- package/public/assets/css/stickers-react.input.css +18 -0
- package/public/assets/css/terms-react.input.css +115 -0
- package/public/assets/css/user-react.input.css +57 -0
- package/public/assets/images/brand-icon-192.png +0 -0
- package/public/assets/images/brand-logo-128.webp +0 -0
- package/public/assets/images/hero-banner-1280.jpg +0 -0
- package/public/comandos/commands-catalog.json +4517 -0
- package/public/css/api-docs.css +161 -0
- package/public/css/stickers-admin.css +1288 -0
- package/public/css/styles.css +679 -0
- package/public/css/systemadm/admin.css +474 -0
- package/public/css/systemadm/base.css +73 -0
- package/public/css/systemadm/components.css +662 -0
- package/public/css/systemadm/layout.css +229 -0
- package/public/css/systemadm/tokens.css +56 -0
- package/public/favicon-16x16.png +0 -0
- package/public/favicon-32x32.png +0 -0
- package/public/favicon.ico +0 -0
- package/public/js/apps/apiDocsApp.js +235 -0
- package/public/js/apps/commandsReactApp.js +528 -0
- package/public/js/apps/createPackApp.js +1646 -0
- package/public/js/apps/homeReactApp.js +942 -0
- package/public/js/apps/loginReactApp.js +496 -0
- package/public/js/apps/stickersAdminApp.js +1753 -0
- package/public/js/apps/stickersApp.js +3797 -0
- package/public/js/apps/termsReactApp.js +528 -0
- package/public/js/apps/userApp.js +2540 -0
- package/public/js/apps/userProfile/actions.js +66 -0
- package/public/js/apps/userReactApp.js +547 -0
- package/public/js/catalog.js +950 -0
- package/public/pages/api-docs.html +40 -0
- package/public/pages/aup.html +158 -0
- package/public/pages/comandos.html +41 -0
- package/public/pages/dpa.html +227 -0
- package/public/pages/home.html +45 -0
- package/public/pages/licenca.html +182 -0
- package/public/pages/login.html +40 -0
- package/public/pages/notice-and-takedown.html +234 -0
- package/public/pages/politica-de-privacidade.html +251 -0
- package/public/pages/seo-bot-whatsapp-para-grupo.html +350 -0
- package/public/pages/seo-bot-whatsapp-sem-programar.html +350 -0
- package/public/pages/seo-como-automatizar-avisos-no-whatsapp.html +350 -0
- package/public/pages/seo-como-criar-comandos-whatsapp.html +350 -0
- package/public/pages/seo-como-evitar-spam-no-whatsapp.html +350 -0
- package/public/pages/seo-como-moderar-grupo-whatsapp.html +350 -0
- package/public/pages/seo-como-organizar-comunidade-whatsapp.html +350 -0
- package/public/pages/seo-melhor-bot-whatsapp-para-grupos.html +350 -0
- package/public/pages/stickers-admin.html +31 -0
- package/public/pages/stickers-create.html +41 -0
- package/public/pages/stickers.html +45 -0
- package/public/pages/suboperadores.html +237 -0
- package/public/pages/termos-de-uso-texto-integral.html +241 -0
- package/public/pages/termos-de-uso.html +41 -0
- package/public/pages/user-password-reset.html +32 -0
- package/public/pages/user-systemadm.html +508 -0
- package/public/pages/user.html +39 -0
- package/public/robots.txt +9 -0
- package/public/site.webmanifest +24 -0
- package/public/sitemap.xml +98 -0
- package/schemas/command-config.schema.json +582 -0
- package/scripts/baileys-compat-smoke.mjs +12 -0
- package/scripts/cache-bust.mjs +142 -0
- package/scripts/deploy.sh +916 -0
- package/scripts/email-broadcast-terms-update.mjs +170 -0
- package/scripts/enrich-command-discovery-fields.mjs +286 -0
- package/scripts/generate-command-config-schema.mjs +273 -0
- package/scripts/generate-commands-catalog.mjs +308 -0
- package/scripts/generate-module-agents.mjs +631 -0
- package/scripts/generate-seo-satellite-pages.mjs +400 -0
- package/scripts/github-deploy-notify.mjs +174 -0
- package/scripts/github-release-notify.mjs +219 -0
- package/scripts/release.sh +599 -0
- package/scripts/run-codeql-local.sh +116 -0
- package/scripts/run-prettier-all.mjs +25 -0
- package/scripts/security-smoketest.mjs +581 -0
- package/scripts/sticker-catalog-loadtest.mjs +210 -0
- package/scripts/sticker-worker-task.mjs +119 -0
- package/scripts/sync-readme-snapshot.mjs +133 -0
- package/scripts/validate-command-config-schema.mjs +130 -0
- package/scripts/validate-command-configs.mjs +15 -0
- package/scripts/wiki-sync.sh +191 -0
- package/server/auth/googleWebAuth/googleWebAuthRuntime.js +62 -0
- package/server/auth/googleWebAuth/googleWebAuthService.js +807 -0
- package/server/auth/jwt/webJwtService.js +147 -0
- package/server/auth/stickerCatalogAuthContext.js +165 -0
- package/server/auth/termsAcceptance/termsAcceptanceHandler.js +189 -0
- package/server/auth/userPassword/index.js +14 -0
- package/server/auth/userPassword/userPasswordAuthService.js +422 -0
- package/server/auth/userPassword/userPasswordCrypto.js +199 -0
- package/server/auth/userPassword/userPasswordCrypto.test.js +76 -0
- package/server/auth/userPassword/userPasswordRecoveryService.js +728 -0
- package/server/auth/validation/authSchemas.js +236 -0
- package/server/auth/webAccount/webAccountHandlers.js +1434 -0
- package/server/controllers/admin/adminBanService.js +138 -0
- package/server/controllers/admin/adminPanelHandlers.js +2083 -0
- package/server/controllers/admin/stickerCatalogAdminContext.js +17 -0
- package/server/controllers/admin/systemAdminController.js +201 -0
- package/server/controllers/email/emailAutomationController.js +239 -0
- package/server/controllers/metricsController.js +21 -0
- package/server/controllers/seo/stickerCatalogSeoContext.js +514 -0
- package/server/controllers/sticker/nonCatalogHandlers.js +303 -0
- package/server/controllers/sticker/stickerCatalogController.js +4700 -0
- package/server/controllers/system/contactController.js +115 -0
- package/server/controllers/system/githubController.js +137 -0
- package/server/controllers/system/stickerCatalogSystemContext.js +758 -0
- package/server/controllers/system/storageController.js +154 -0
- package/server/controllers/system/systemController.js +135 -0
- package/server/controllers/system/systemMetricsController.js +156 -0
- package/server/controllers/system/visitController.js +90 -0
- package/server/controllers/userController.js +145 -0
- package/server/email/emailAutomationRuntime.js +225 -0
- package/server/email/emailAutomationService.js +125 -0
- package/server/email/emailOutboxRepository.js +282 -0
- package/server/email/emailTemplateService.js +480 -0
- package/server/email/emailTransportService.js +156 -0
- package/server/http/clientIp.js +95 -0
- package/server/http/httpRequestUtils.js +262 -0
- package/server/http/httpRequestUtils.test.js +80 -0
- package/server/http/httpServer.js +180 -0
- package/server/http/requestContext.js +20 -0
- package/server/http/siteRoutingUtils.js +87 -0
- package/server/index.js +1 -0
- package/server/middleware/cachePolicy.js +26 -0
- package/server/middleware/cachePolicyHelpers.js +1 -0
- package/server/middleware/endpointRateLimit.js +181 -0
- package/server/middleware/rateLimit.js +70 -0
- package/server/middleware/requireAdminAuth.js +48 -0
- package/server/middleware/securityHeaders.js +97 -0
- package/server/routes/admin/systemAdminRouter.js +64 -0
- package/server/routes/email/emailAutomationRouter.js +46 -0
- package/server/routes/health/healthRouter.js +41 -0
- package/server/routes/indexRouter.js +234 -0
- package/server/routes/metrics/metricsRouter.js +58 -0
- package/server/routes/static/staticPageRouter.js +134 -0
- package/server/routes/sticker/catalogHandlers/catalogAdminHttp.js +105 -0
- package/server/routes/sticker/catalogHandlers/catalogAuthHttp.js +77 -0
- package/server/routes/sticker/catalogHandlers/catalogPublicHttp.js +120 -0
- package/server/routes/sticker/catalogHandlers/catalogUploadHttp.js +83 -0
- package/server/routes/sticker/catalogRouter.js +77 -0
- package/server/routes/sticker/stickerApiRouter.js +84 -0
- package/server/routes/sticker/stickerDataRouter.js +145 -0
- package/server/routes/sticker/stickerSiteRouter.js +43 -0
- package/server/routes/user/userApiPaths.js +66 -0
- package/server/routes/user/userRouter.js +65 -0
- package/server/utils/safePath.js +26 -0
- package/utils/logger/loggerModule.js +35 -0
- package/vite.config.mjs +38 -0
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
import path from 'node:path';
|
|
2
|
+
import { fileURLToPath } from 'node:url';
|
|
3
|
+
import { createModuleAiHelpWrapper } from '../../services/ai/moduleAiHelpWrapperFactory.js';
|
|
4
|
+
|
|
5
|
+
const __filename = fileURLToPath(import.meta.url);
|
|
6
|
+
const __dirname = path.dirname(__filename);
|
|
7
|
+
|
|
8
|
+
export const waifuPicsAiHelpWrapper = createModuleAiHelpWrapper({
|
|
9
|
+
moduleKey: 'waifupics',
|
|
10
|
+
moduleLabel: 'comandos waifu pics',
|
|
11
|
+
envPrefix: 'WAIFUPICS_AI_HELP',
|
|
12
|
+
moduleDirPath: __dirname,
|
|
13
|
+
moduleNameFallback: 'waifuPicsModule',
|
|
14
|
+
});
|
|
@@ -0,0 +1,586 @@
|
|
|
1
|
+
import axios from 'axios';
|
|
2
|
+
|
|
3
|
+
import logger from '#logger';
|
|
4
|
+
import groupConfigStore from '../../store/groupConfigStore.js';
|
|
5
|
+
import premiumUserStore from '../../store/premiumUserStore.js';
|
|
6
|
+
import { getAdminJid, isGroupJid, isSameJidUser, normalizeJid, resolveAdminJid } from '../../config/index.js';
|
|
7
|
+
import { sendAndStore } from '../../services/messaging/messagePersistenceService.js';
|
|
8
|
+
import { getWaifuPicsCommandEntry, getWaifuPicsTextConfig, getWaifuPicsUsageText as getWaifuPicsRuntimeUsageText, resolveWaifuPicsCommandName } from './waifuPicsConfigRuntime.js';
|
|
9
|
+
|
|
10
|
+
/**
|
|
11
|
+
* Prefixo padrão de comandos do bot.
|
|
12
|
+
* @type {string}
|
|
13
|
+
*/
|
|
14
|
+
const DEFAULT_COMMAND_PREFIX = process.env.COMMAND_PREFIX || '/';
|
|
15
|
+
|
|
16
|
+
/**
|
|
17
|
+
* URL base da API Waifu.pics.
|
|
18
|
+
* @type {string}
|
|
19
|
+
*/
|
|
20
|
+
const WAIFU_PICS_BASE = (process.env.WAIFU_PICS_BASE || 'https://api.waifu.pics').replace(/\/$/, '');
|
|
21
|
+
|
|
22
|
+
/**
|
|
23
|
+
* Timeout das requisições para a Waifu.pics (em milissegundos).
|
|
24
|
+
* @type {number}
|
|
25
|
+
*/
|
|
26
|
+
const WAIFU_PICS_TIMEOUT_MS = Number.parseInt(process.env.WAIFU_PICS_TIMEOUT_MS || '15000', 10);
|
|
27
|
+
|
|
28
|
+
/**
|
|
29
|
+
* Define se conteúdo NSFW é permitido globalmente nas configurações do sistema.
|
|
30
|
+
* @type {boolean}
|
|
31
|
+
*/
|
|
32
|
+
const WAIFU_PICS_ALLOW_NSFW = process.env.WAIFU_PICS_ALLOW_NSFW === 'true';
|
|
33
|
+
const OWNER_JID = getAdminJid();
|
|
34
|
+
|
|
35
|
+
/**
|
|
36
|
+
* Categorias SFW (Safe For Work) disponíveis na Waifu.pics.
|
|
37
|
+
* @type {string[]}
|
|
38
|
+
*/
|
|
39
|
+
const SFW_CATEGORIES = ['waifu', 'neko', 'shinobu', 'megumin', 'bully', 'cuddle', 'cry', 'hug', 'awoo', 'kiss', 'lick', 'pat', 'smug', 'bonk', 'yeet', 'blush', 'smile', 'wave', 'highfive', 'handhold', 'nom', 'bite', 'glomp', 'slap', 'kill', 'kick', 'happy', 'wink', 'poke', 'dance', 'cringe'];
|
|
40
|
+
|
|
41
|
+
/**
|
|
42
|
+
* Categorias NSFW (Not Safe For Work) disponíveis na Waifu.pics.
|
|
43
|
+
* @type {string[]}
|
|
44
|
+
*/
|
|
45
|
+
const NSFW_CATEGORIES = ['waifu', 'neko', 'trap', 'blowjob'];
|
|
46
|
+
|
|
47
|
+
const COMMAND_DEFINITION_BY_TYPE = Object.freeze({
|
|
48
|
+
sfw: {
|
|
49
|
+
preferredName: 'waifu',
|
|
50
|
+
fallbackAlias: 'wp',
|
|
51
|
+
modeLabel: '📗 SFW (seguro)',
|
|
52
|
+
},
|
|
53
|
+
nsfw: {
|
|
54
|
+
preferredName: 'waifunsfw',
|
|
55
|
+
fallbackAlias: 'wpnsfw',
|
|
56
|
+
modeLabel: '🔞 NSFW (adulto)',
|
|
57
|
+
},
|
|
58
|
+
});
|
|
59
|
+
|
|
60
|
+
const FALLBACK_CATEGORIES_BY_TYPE = Object.freeze({
|
|
61
|
+
sfw: SFW_CATEGORIES,
|
|
62
|
+
nsfw: NSFW_CATEGORIES,
|
|
63
|
+
});
|
|
64
|
+
|
|
65
|
+
const DEFAULT_FALLBACK_CATEGORY = 'waifu';
|
|
66
|
+
const DEFAULT_USER_PLAN = 'comum';
|
|
67
|
+
const PREMIUM_USER_PLAN = 'premium';
|
|
68
|
+
const USER_RATE_LIMIT_MAP_MAX_SIZE = 2500;
|
|
69
|
+
|
|
70
|
+
const userPlanRateMap = globalThis.__omnizapWaifuPicsUserPlanRateMap instanceof Map ? globalThis.__omnizapWaifuPicsUserPlanRateMap : new Map();
|
|
71
|
+
globalThis.__omnizapWaifuPicsUserPlanRateMap = userPlanRateMap;
|
|
72
|
+
|
|
73
|
+
const normalizeType = (value) =>
|
|
74
|
+
String(value || '')
|
|
75
|
+
.trim()
|
|
76
|
+
.toLowerCase() === 'nsfw'
|
|
77
|
+
? 'nsfw'
|
|
78
|
+
: 'sfw';
|
|
79
|
+
|
|
80
|
+
const applyCommandPrefix = (value, commandPrefix = DEFAULT_COMMAND_PREFIX) => String(value || '').replaceAll('<prefix>', String(commandPrefix || DEFAULT_COMMAND_PREFIX));
|
|
81
|
+
|
|
82
|
+
const firstString = (...values) => {
|
|
83
|
+
for (const value of values) {
|
|
84
|
+
if (typeof value === 'string' && value.trim()) return value.trim();
|
|
85
|
+
}
|
|
86
|
+
return '';
|
|
87
|
+
};
|
|
88
|
+
|
|
89
|
+
const normalizeTokenList = (values) => {
|
|
90
|
+
const dedupe = new Set();
|
|
91
|
+
for (const value of values || []) {
|
|
92
|
+
const normalized = String(value || '')
|
|
93
|
+
.trim()
|
|
94
|
+
.toLowerCase();
|
|
95
|
+
if (!normalized) continue;
|
|
96
|
+
dedupe.add(normalized);
|
|
97
|
+
}
|
|
98
|
+
return [...dedupe];
|
|
99
|
+
};
|
|
100
|
+
|
|
101
|
+
const resolveCommandDefinition = (type = 'sfw') => {
|
|
102
|
+
const normalizedType = normalizeType(type);
|
|
103
|
+
const fallback = COMMAND_DEFINITION_BY_TYPE[normalizedType] || COMMAND_DEFINITION_BY_TYPE.sfw;
|
|
104
|
+
const canonicalName = resolveWaifuPicsCommandName(fallback.preferredName) || resolveWaifuPicsCommandName(fallback.fallbackAlias) || fallback.preferredName;
|
|
105
|
+
const entry = getWaifuPicsCommandEntry(canonicalName) || getWaifuPicsCommandEntry(fallback.fallbackAlias) || null;
|
|
106
|
+
|
|
107
|
+
return {
|
|
108
|
+
type: normalizedType,
|
|
109
|
+
canonicalName,
|
|
110
|
+
entry,
|
|
111
|
+
modeLabel: fallback.modeLabel,
|
|
112
|
+
fallbackAlias: fallback.fallbackAlias,
|
|
113
|
+
fallbackCategories: FALLBACK_CATEGORIES_BY_TYPE[normalizedType] || FALLBACK_CATEGORIES_BY_TYPE.sfw,
|
|
114
|
+
};
|
|
115
|
+
};
|
|
116
|
+
|
|
117
|
+
const resolveCommandToken = (entry, fallbackAlias = 'wp') => {
|
|
118
|
+
const tokens = normalizeTokenList([entry?.name, ...(Array.isArray(entry?.aliases) ? entry.aliases : [])]);
|
|
119
|
+
if (!tokens.length) return fallbackAlias;
|
|
120
|
+
return tokens.reduce((best, current) => (current.length < best.length ? current : best), tokens[0]);
|
|
121
|
+
};
|
|
122
|
+
|
|
123
|
+
const resolveResponseText = (entry, key, fallback = '') => {
|
|
124
|
+
const responses = entry?.responses && typeof entry.responses === 'object' ? entry.responses : {};
|
|
125
|
+
const legacyResponses = entry?.respostas_padrao && typeof entry.respostas_padrao === 'object' ? entry.respostas_padrao : {};
|
|
126
|
+
|
|
127
|
+
if (key === 'success') {
|
|
128
|
+
return firstString(responses.success, legacyResponses.sucesso, fallback);
|
|
129
|
+
}
|
|
130
|
+
if (key === 'usage_error') {
|
|
131
|
+
return firstString(responses.usage_error, legacyResponses.erro_uso, fallback);
|
|
132
|
+
}
|
|
133
|
+
if (key === 'permission_error') {
|
|
134
|
+
return firstString(responses.permission_error, legacyResponses.erro_permissao, fallback);
|
|
135
|
+
}
|
|
136
|
+
return firstString(fallback);
|
|
137
|
+
};
|
|
138
|
+
|
|
139
|
+
const resolveCommandCategories = (definition) => {
|
|
140
|
+
const entry = definition?.entry;
|
|
141
|
+
const argument = Array.isArray(entry?.arguments) ? entry.arguments[0] : null;
|
|
142
|
+
const legacyArgument = Array.isArray(entry?.argumentos) ? entry.argumentos[0] : null;
|
|
143
|
+
|
|
144
|
+
const configured = normalizeTokenList([...(Array.isArray(argument?.enum) ? argument.enum : []), ...(Array.isArray(argument?.values) ? argument.values : []), ...(Array.isArray(argument?.categories) ? argument.categories : []), ...(Array.isArray(legacyArgument?.enum) ? legacyArgument.enum : []), ...(Array.isArray(legacyArgument?.valores) ? legacyArgument.valores : []), ...(Array.isArray(legacyArgument?.categorias) ? legacyArgument.categorias : [])]);
|
|
145
|
+
|
|
146
|
+
return configured.length ? configured : [...(definition?.fallbackCategories || FALLBACK_CATEGORIES_BY_TYPE.sfw)];
|
|
147
|
+
};
|
|
148
|
+
|
|
149
|
+
const resolveDefaultCategory = (definition, categories = []) => {
|
|
150
|
+
const entry = definition?.entry;
|
|
151
|
+
const argument = Array.isArray(entry?.arguments) ? entry.arguments[0] : null;
|
|
152
|
+
const legacyArgument = Array.isArray(entry?.argumentos) ? entry.argumentos[0] : null;
|
|
153
|
+
|
|
154
|
+
const configuredDefault = firstString(argument?.default, legacyArgument?.default).trim().toLowerCase();
|
|
155
|
+
if (configuredDefault) return configuredDefault;
|
|
156
|
+
|
|
157
|
+
const safeCategories = Array.isArray(categories) ? categories : [];
|
|
158
|
+
if (safeCategories.includes(DEFAULT_FALLBACK_CATEGORY)) return DEFAULT_FALLBACK_CATEGORY;
|
|
159
|
+
return safeCategories[0] || DEFAULT_FALLBACK_CATEGORY;
|
|
160
|
+
};
|
|
161
|
+
|
|
162
|
+
const resolvePrimaryUsageLine = (definition, commandPrefix = DEFAULT_COMMAND_PREFIX) => {
|
|
163
|
+
const entry = definition?.entry;
|
|
164
|
+
const methods = [...(Array.isArray(entry?.metodos_de_uso) ? entry.metodos_de_uso : []), ...(Array.isArray(entry?.usage) ? entry.usage : [])];
|
|
165
|
+
const method = methods.find((value) => typeof value === 'string' && value.trim());
|
|
166
|
+
if (method) return applyCommandPrefix(method, commandPrefix);
|
|
167
|
+
|
|
168
|
+
const runtimeUsage = getWaifuPicsRuntimeUsageText(definition?.canonicalName || definition?.fallbackAlias || 'waifu', {
|
|
169
|
+
commandPrefix,
|
|
170
|
+
header: '',
|
|
171
|
+
});
|
|
172
|
+
const firstLine = String(runtimeUsage || '')
|
|
173
|
+
.split('\n')
|
|
174
|
+
.map((line) => line.trim())
|
|
175
|
+
.find(Boolean);
|
|
176
|
+
if (firstLine) return firstLine;
|
|
177
|
+
|
|
178
|
+
const token = resolveCommandToken(entry, definition?.fallbackAlias || 'wp');
|
|
179
|
+
return `${commandPrefix}${token} <categoria>`;
|
|
180
|
+
};
|
|
181
|
+
|
|
182
|
+
/**
|
|
183
|
+
* Quebra uma lista de categorias em sub-listas (chunks) para melhor formatação visual no WhatsApp.
|
|
184
|
+
*
|
|
185
|
+
* @param {string[]} categories - Lista de nomes das categorias.
|
|
186
|
+
* @param {number} [chunkSize=6] - Tamanho de cada sub-lista.
|
|
187
|
+
* @returns {string[][]} Array de chunks.
|
|
188
|
+
*/
|
|
189
|
+
const chunkCategories = (categories, chunkSize = 6) => {
|
|
190
|
+
const chunks = [];
|
|
191
|
+
for (let index = 0; index < categories.length; index += chunkSize) {
|
|
192
|
+
chunks.push(categories.slice(index, index + chunkSize));
|
|
193
|
+
}
|
|
194
|
+
return chunks;
|
|
195
|
+
};
|
|
196
|
+
|
|
197
|
+
/**
|
|
198
|
+
* Formata uma lista de categorias em múltiplas linhas usando separadores visuais (bullets).
|
|
199
|
+
*
|
|
200
|
+
* @param {string[]} categories - Lista de nomes das categorias.
|
|
201
|
+
* @returns {string} String formatada para exibição.
|
|
202
|
+
*/
|
|
203
|
+
const formatCategoriesList = (categories) =>
|
|
204
|
+
chunkCategories(categories)
|
|
205
|
+
.map((chunk) => `• ${chunk.join(' • ')}`)
|
|
206
|
+
.join('\n');
|
|
207
|
+
|
|
208
|
+
const toUsageCommandLine = (value, fallback) => {
|
|
209
|
+
const line = String(value || '')
|
|
210
|
+
.trim()
|
|
211
|
+
.replace(/^\*\s*|\s*\*$/g, '');
|
|
212
|
+
return line || fallback;
|
|
213
|
+
};
|
|
214
|
+
|
|
215
|
+
const parsePositiveInt = (value) => {
|
|
216
|
+
const parsed = Number.parseInt(String(value ?? ''), 10);
|
|
217
|
+
return Number.isFinite(parsed) && parsed > 0 ? parsed : null;
|
|
218
|
+
};
|
|
219
|
+
|
|
220
|
+
const normalizePlanName = (value) =>
|
|
221
|
+
String(value || '')
|
|
222
|
+
.trim()
|
|
223
|
+
.toLowerCase() === PREMIUM_USER_PLAN
|
|
224
|
+
? PREMIUM_USER_PLAN
|
|
225
|
+
: DEFAULT_USER_PLAN;
|
|
226
|
+
|
|
227
|
+
const normalizePlansList = (values = []) => normalizeTokenList(values).filter((plan) => plan === DEFAULT_USER_PLAN || plan === PREMIUM_USER_PLAN);
|
|
228
|
+
|
|
229
|
+
const resolveAccessPolicy = (entry) => {
|
|
230
|
+
const access = entry?.access && typeof entry.access === 'object' ? entry.access : {};
|
|
231
|
+
const legacyAccess = entry?.acesso && typeof entry.acesso === 'object' ? entry.acesso : {};
|
|
232
|
+
const limitsAccess = entry?.limits?.access && typeof entry.limits.access === 'object' ? entry.limits.access : {};
|
|
233
|
+
const limitsLegacyAccess = limitsAccess?.legacy && typeof limitsAccess.legacy === 'object' ? limitsAccess.legacy : {};
|
|
234
|
+
|
|
235
|
+
const premiumOnly = Boolean(access.premium_only ?? limitsAccess.premium_only ?? legacyAccess.somente_premium ?? limitsLegacyAccess.somente_premium ?? false);
|
|
236
|
+
const allowedPlans = normalizePlansList([...(Array.isArray(access.allowed_plans) ? access.allowed_plans : []), ...(Array.isArray(limitsAccess.allowed_plans) ? limitsAccess.allowed_plans : []), ...(Array.isArray(legacyAccess.planos_permitidos) ? legacyAccess.planos_permitidos : []), ...(Array.isArray(limitsLegacyAccess.planos_permitidos) ? limitsLegacyAccess.planos_permitidos : [])]);
|
|
237
|
+
|
|
238
|
+
return {
|
|
239
|
+
premiumOnly,
|
|
240
|
+
allowedPlans: allowedPlans.length ? allowedPlans : [DEFAULT_USER_PLAN, PREMIUM_USER_PLAN],
|
|
241
|
+
};
|
|
242
|
+
};
|
|
243
|
+
|
|
244
|
+
const resolvePlanLimitConfig = (entry, userPlan = DEFAULT_USER_PLAN) => {
|
|
245
|
+
const planLimits = entry?.plan_limits && typeof entry.plan_limits === 'object' ? entry.plan_limits : {};
|
|
246
|
+
const legacyPlanLimits = entry?.limite_uso_por_plano && typeof entry.limite_uso_por_plano === 'object' ? entry.limite_uso_por_plano : {};
|
|
247
|
+
const nestedPlanLimits = entry?.limits?.plan_limits && typeof entry.limits.plan_limits === 'object' ? entry.limits.plan_limits : {};
|
|
248
|
+
const mergedLimits = {
|
|
249
|
+
...legacyPlanLimits,
|
|
250
|
+
...nestedPlanLimits,
|
|
251
|
+
...planLimits,
|
|
252
|
+
};
|
|
253
|
+
|
|
254
|
+
const normalizedPlan = normalizePlanName(userPlan);
|
|
255
|
+
const selected = (mergedLimits[normalizedPlan] && typeof mergedLimits[normalizedPlan] === 'object' ? mergedLimits[normalizedPlan] : null) || (mergedLimits[DEFAULT_USER_PLAN] && typeof mergedLimits[DEFAULT_USER_PLAN] === 'object' ? mergedLimits[DEFAULT_USER_PLAN] : null);
|
|
256
|
+
|
|
257
|
+
if (!selected) return null;
|
|
258
|
+
|
|
259
|
+
const max = parsePositiveInt(selected.max);
|
|
260
|
+
const windowMs = parsePositiveInt(selected.janela_ms);
|
|
261
|
+
if (!max || !windowMs) return null;
|
|
262
|
+
|
|
263
|
+
const rawScope = String(selected.escopo || '')
|
|
264
|
+
.trim()
|
|
265
|
+
.toLowerCase();
|
|
266
|
+
const scope = rawScope === 'grupo' || rawScope === 'group' ? 'grupo' : rawScope === 'global' ? 'global' : 'usuario';
|
|
267
|
+
|
|
268
|
+
return {
|
|
269
|
+
max,
|
|
270
|
+
windowMs,
|
|
271
|
+
scope,
|
|
272
|
+
};
|
|
273
|
+
};
|
|
274
|
+
|
|
275
|
+
const buildRateScopeKey = ({ scope, senderKey, remoteJid }) => {
|
|
276
|
+
if (scope === 'grupo') return `group:${String(remoteJid || '').trim() || 'unknown'}`;
|
|
277
|
+
if (scope === 'global') return 'global:all';
|
|
278
|
+
return `user:${String(senderKey || '').trim() || 'unknown'}`;
|
|
279
|
+
};
|
|
280
|
+
|
|
281
|
+
const pruneRateMapIfNeeded = (now = Date.now()) => {
|
|
282
|
+
if (userPlanRateMap.size <= USER_RATE_LIMIT_MAP_MAX_SIZE) return;
|
|
283
|
+
|
|
284
|
+
for (const [key, value] of userPlanRateMap.entries()) {
|
|
285
|
+
if (!value || Number(value.resetAt) <= now) {
|
|
286
|
+
userPlanRateMap.delete(key);
|
|
287
|
+
}
|
|
288
|
+
if (userPlanRateMap.size <= USER_RATE_LIMIT_MAP_MAX_SIZE) return;
|
|
289
|
+
}
|
|
290
|
+
|
|
291
|
+
const overflow = userPlanRateMap.size - USER_RATE_LIMIT_MAP_MAX_SIZE;
|
|
292
|
+
if (overflow <= 0) return;
|
|
293
|
+
|
|
294
|
+
let removed = 0;
|
|
295
|
+
for (const key of userPlanRateMap.keys()) {
|
|
296
|
+
userPlanRateMap.delete(key);
|
|
297
|
+
removed += 1;
|
|
298
|
+
if (removed >= overflow) break;
|
|
299
|
+
}
|
|
300
|
+
};
|
|
301
|
+
|
|
302
|
+
const checkPlanUsageRateLimit = ({ commandKey, userPlan, scope, max, windowMs, senderKey, remoteJid }) => {
|
|
303
|
+
if (!max || !windowMs) return { limited: false, remainingMs: 0 };
|
|
304
|
+
|
|
305
|
+
const now = Date.now();
|
|
306
|
+
const scopeKey = buildRateScopeKey({ scope, senderKey, remoteJid });
|
|
307
|
+
const cacheKey = `${String(commandKey || 'waifu')}:${normalizePlanName(userPlan)}:${scopeKey}`;
|
|
308
|
+
const current = userPlanRateMap.get(cacheKey);
|
|
309
|
+
|
|
310
|
+
if (!current || Number(current.resetAt) <= now) {
|
|
311
|
+
userPlanRateMap.set(cacheKey, {
|
|
312
|
+
count: 1,
|
|
313
|
+
resetAt: now + windowMs,
|
|
314
|
+
});
|
|
315
|
+
pruneRateMapIfNeeded(now);
|
|
316
|
+
return { limited: false, remainingMs: 0 };
|
|
317
|
+
}
|
|
318
|
+
|
|
319
|
+
if (Number(current.count) >= max) {
|
|
320
|
+
return {
|
|
321
|
+
limited: true,
|
|
322
|
+
remainingMs: Math.max(0, Number(current.resetAt) - now),
|
|
323
|
+
};
|
|
324
|
+
}
|
|
325
|
+
|
|
326
|
+
current.count += 1;
|
|
327
|
+
userPlanRateMap.set(cacheKey, current);
|
|
328
|
+
return { limited: false, remainingMs: 0 };
|
|
329
|
+
};
|
|
330
|
+
|
|
331
|
+
const formatDuration = (ms) => {
|
|
332
|
+
const totalSeconds = Math.max(1, Math.ceil((Number(ms) || 0) / 1000));
|
|
333
|
+
const minutes = Math.floor(totalSeconds / 60);
|
|
334
|
+
const seconds = totalSeconds % 60;
|
|
335
|
+
|
|
336
|
+
if (minutes > 0 && seconds > 0) return `${minutes}m ${seconds}s`;
|
|
337
|
+
if (minutes > 0) return `${minutes}m`;
|
|
338
|
+
return `${seconds}s`;
|
|
339
|
+
};
|
|
340
|
+
|
|
341
|
+
const formatWindowDuration = (windowMs) => {
|
|
342
|
+
const safeWindowMs = Math.max(1000, Number(windowMs) || 1000);
|
|
343
|
+
if (safeWindowMs % 60000 === 0) {
|
|
344
|
+
const minutes = Math.max(1, Math.round(safeWindowMs / 60000));
|
|
345
|
+
return `${minutes} min`;
|
|
346
|
+
}
|
|
347
|
+
if (safeWindowMs % 1000 === 0) {
|
|
348
|
+
const seconds = Math.max(1, Math.round(safeWindowMs / 1000));
|
|
349
|
+
return `${seconds}s`;
|
|
350
|
+
}
|
|
351
|
+
return `${safeWindowMs}ms`;
|
|
352
|
+
};
|
|
353
|
+
|
|
354
|
+
const resolveSenderCandidates = (senderJid) => {
|
|
355
|
+
const normalized = normalizeJid(senderJid || '');
|
|
356
|
+
const raw = String(senderJid || '').trim();
|
|
357
|
+
return [...new Set([normalized, raw].filter(Boolean))];
|
|
358
|
+
};
|
|
359
|
+
|
|
360
|
+
const isPremiumSender = async (senderJid) => {
|
|
361
|
+
const senderCandidates = resolveSenderCandidates(senderJid);
|
|
362
|
+
if (!senderCandidates.length) return false;
|
|
363
|
+
|
|
364
|
+
const adminJid = (await resolveAdminJid().catch(() => null)) || OWNER_JID;
|
|
365
|
+
const normalizedAdmin = normalizeJid(adminJid || '');
|
|
366
|
+
if (normalizedAdmin && senderCandidates.some((sender) => sender === normalizedAdmin || isSameJidUser(sender, normalizedAdmin))) {
|
|
367
|
+
return true;
|
|
368
|
+
}
|
|
369
|
+
|
|
370
|
+
try {
|
|
371
|
+
const premiumUsers = await premiumUserStore.getPremiumUsers();
|
|
372
|
+
const premiumCandidates = Array.from(new Set((Array.isArray(premiumUsers) ? premiumUsers : []).map((jid) => normalizeJid(jid || '')).filter(Boolean)));
|
|
373
|
+
if (!premiumCandidates.length) return false;
|
|
374
|
+
return senderCandidates.some((sender) => premiumCandidates.some((premiumJid) => premiumJid === sender || isSameJidUser(premiumJid, sender)));
|
|
375
|
+
} catch (error) {
|
|
376
|
+
logger.warn('handleWaifuPicsCommand: falha ao consultar premiumUserStore.', { error: error?.message });
|
|
377
|
+
return false;
|
|
378
|
+
}
|
|
379
|
+
};
|
|
380
|
+
|
|
381
|
+
const resolveSenderPlan = async (senderJid) => {
|
|
382
|
+
if (!senderJid) return DEFAULT_USER_PLAN;
|
|
383
|
+
return (await isPremiumSender(senderJid)) ? PREMIUM_USER_PLAN : DEFAULT_USER_PLAN;
|
|
384
|
+
};
|
|
385
|
+
|
|
386
|
+
/**
|
|
387
|
+
* Envia uma mensagem detalhada com as instruções de uso e as categorias disponíveis para o usuário.
|
|
388
|
+
*
|
|
389
|
+
* @param {import('@whiskeysockets/baileys').WASocket} sock - Instância do socket Baileys ativa.
|
|
390
|
+
* @param {string} remoteJid - JID (identificador) do chat de destino.
|
|
391
|
+
* @param {import('@whiskeysockets/baileys').proto.IWebMessageInfo} messageInfo - Objeto da mensagem original para fins de resposta (reply).
|
|
392
|
+
* @param {number|undefined} expirationMessage - Tempo de expiração (efêmero) da mensagem em segundos.
|
|
393
|
+
* @param {'sfw'|'nsfw'} type - Tipo de conteúdo desejado (seguro ou adulto).
|
|
394
|
+
* @param {string} [commandPrefix] - Prefixo customizado para os comandos.
|
|
395
|
+
* @returns {Promise<void>}
|
|
396
|
+
*/
|
|
397
|
+
const sendUsage = async (sock, remoteJid, messageInfo, expirationMessage, type, commandPrefix = DEFAULT_COMMAND_PREFIX) => {
|
|
398
|
+
const definition = resolveCommandDefinition(type);
|
|
399
|
+
const list = resolveCommandCategories(definition);
|
|
400
|
+
const usageErrorText = applyCommandPrefix(resolveResponseText(definition.entry, 'usage_error', ''), commandPrefix);
|
|
401
|
+
const usageLine = toUsageCommandLine(resolvePrimaryUsageLine(definition, commandPrefix), `${commandPrefix}${definition.fallbackAlias} <categoria>`);
|
|
402
|
+
|
|
403
|
+
await sendAndStore(
|
|
404
|
+
sock,
|
|
405
|
+
remoteJid,
|
|
406
|
+
{
|
|
407
|
+
text: ['🖼️ *Waifu pics*', '', usageErrorText, usageErrorText ? '' : null, `Modo: *${definition.modeLabel}*`, `Use: *${usageLine}*`, '', formatCategoriesList(list), '', `ℹ️ Dica: use *${commandPrefix}menu anime* para ver SFW e NSFW juntos.`].filter((line) => line !== null && line !== undefined).join('\n'),
|
|
408
|
+
},
|
|
409
|
+
{ quoted: messageInfo, ephemeralExpiration: expirationMessage },
|
|
410
|
+
);
|
|
411
|
+
};
|
|
412
|
+
|
|
413
|
+
/**
|
|
414
|
+
* Realiza uma requisição HTTP à API Waifu.pics para obter a URL de uma imagem aleatória.
|
|
415
|
+
*
|
|
416
|
+
* @param {'sfw'|'nsfw'} type - Tipo de conteúdo (sfw ou nsfw).
|
|
417
|
+
* @param {string} category - Nome da categoria desejada.
|
|
418
|
+
* @returns {Promise<string|null>} Retorna a URL da imagem em caso de sucesso ou null em caso de falha na resposta.
|
|
419
|
+
* @throws {Error} Pode lançar erro se a requisição falhar (ex: timeout ou erro de rede).
|
|
420
|
+
*/
|
|
421
|
+
const fetchWaifuPics = async (type, category) => {
|
|
422
|
+
const url = `${WAIFU_PICS_BASE}/${type}/${category}`;
|
|
423
|
+
const { data } = await axios.get(url, { timeout: WAIFU_PICS_TIMEOUT_MS });
|
|
424
|
+
return data?.url || null;
|
|
425
|
+
};
|
|
426
|
+
|
|
427
|
+
/**
|
|
428
|
+
* Handler principal para o processamento de comandos do módulo Waifu.pics.
|
|
429
|
+
*
|
|
430
|
+
* Este handler coordena o fluxo completo:
|
|
431
|
+
* 1. Validação de permissões globais e locais (especialmente para conteúdo NSFW).
|
|
432
|
+
* 2. Validação da categoria solicitada contra as listas permitidas.
|
|
433
|
+
* 3. Busca da imagem através da integração com a API externa.
|
|
434
|
+
* 4. Envio do resultado (imagem com legenda) ou de mensagens de erro/ajuda ao usuário.
|
|
435
|
+
*
|
|
436
|
+
* @param {Object} params - Parâmetros do handler.
|
|
437
|
+
* @param {import('@whiskeysockets/baileys').WASocket} params.sock - Instância do socket Baileys ativa.
|
|
438
|
+
* @param {string} params.remoteJid - JID do chat de destino.
|
|
439
|
+
* @param {import('@whiskeysockets/baileys').proto.IWebMessageInfo} params.messageInfo - Objeto da mensagem original recebida.
|
|
440
|
+
* @param {number|undefined} params.expirationMessage - Tempo de expiração configurado para mensagens no chat.
|
|
441
|
+
* @param {string} params.text - Texto bruto enviado pelo usuário após o comando.
|
|
442
|
+
* @param {string|undefined} params.senderJid - JID do remetente para aplicar plano e limites de uso.
|
|
443
|
+
* @param {'sfw'|'nsfw'} [params.type='sfw'] - Tipo de busca (padrão é 'sfw').
|
|
444
|
+
* @param {string} [params.commandPrefix] - Prefixo utilizado para disparar o comando.
|
|
445
|
+
* @returns {Promise<void>}
|
|
446
|
+
*/
|
|
447
|
+
export async function handleWaifuPicsCommand({ sock, remoteJid, messageInfo, expirationMessage, text, senderJid, type = 'sfw', commandPrefix = DEFAULT_COMMAND_PREFIX }) {
|
|
448
|
+
const definition = resolveCommandDefinition(type);
|
|
449
|
+
const categoryList = resolveCommandCategories(definition);
|
|
450
|
+
const fallbackCategory = resolveDefaultCategory(definition, categoryList);
|
|
451
|
+
const category = (text || '').trim().toLowerCase() || fallbackCategory;
|
|
452
|
+
const isGroupMessage = isGroupJid(remoteJid);
|
|
453
|
+
const userPlan = await resolveSenderPlan(senderJid);
|
|
454
|
+
const accessPolicy = resolveAccessPolicy(definition.entry);
|
|
455
|
+
|
|
456
|
+
// Verifica se o recurso NSFW está habilitado globalmente via variáveis de ambiente.
|
|
457
|
+
if (definition.type === 'nsfw' && !WAIFU_PICS_ALLOW_NSFW) {
|
|
458
|
+
const permissionText = applyCommandPrefix(resolveResponseText(definition.entry, 'permission_error', ''), commandPrefix);
|
|
459
|
+
await sendAndStore(
|
|
460
|
+
sock,
|
|
461
|
+
remoteJid,
|
|
462
|
+
{
|
|
463
|
+
text: permissionText ? `⚠️ Conteúdo NSFW desativado. Habilite WAIFU_PICS_ALLOW_NSFW=true no .env.\n${permissionText}` : '⚠️ Conteúdo NSFW desativado. Habilite WAIFU_PICS_ALLOW_NSFW=true no .env.',
|
|
464
|
+
},
|
|
465
|
+
{ quoted: messageInfo, ephemeralExpiration: expirationMessage },
|
|
466
|
+
);
|
|
467
|
+
return;
|
|
468
|
+
}
|
|
469
|
+
|
|
470
|
+
// Em grupo, exige NSFW ativo na configuração local; no privado, essa trava não se aplica.
|
|
471
|
+
if (definition.type === 'nsfw' && isGroupMessage) {
|
|
472
|
+
const config = await groupConfigStore.getGroupConfig(remoteJid);
|
|
473
|
+
if (!config?.nsfwEnabled) {
|
|
474
|
+
const permissionText = applyCommandPrefix(resolveResponseText(definition.entry, 'permission_error', ''), commandPrefix);
|
|
475
|
+
await sendAndStore(
|
|
476
|
+
sock,
|
|
477
|
+
remoteJid,
|
|
478
|
+
{
|
|
479
|
+
text: permissionText || `🔞 NSFW está desativado neste grupo. Um admin pode ativar com ${commandPrefix}nsfw on.`,
|
|
480
|
+
},
|
|
481
|
+
{ quoted: messageInfo, ephemeralExpiration: expirationMessage },
|
|
482
|
+
);
|
|
483
|
+
return;
|
|
484
|
+
}
|
|
485
|
+
}
|
|
486
|
+
|
|
487
|
+
const userPlanAllowed = !accessPolicy.allowedPlans.length || accessPolicy.allowedPlans.includes(userPlan);
|
|
488
|
+
const premiumOnlyBlocked = accessPolicy.premiumOnly && userPlan !== PREMIUM_USER_PLAN;
|
|
489
|
+
if (!userPlanAllowed || premiumOnlyBlocked) {
|
|
490
|
+
const permissionText = applyCommandPrefix(resolveResponseText(definition.entry, 'permission_error', ''), commandPrefix);
|
|
491
|
+
const allowedPlansLabel = accessPolicy.allowedPlans.length ? accessPolicy.allowedPlans.join(', ') : `${DEFAULT_USER_PLAN}, ${PREMIUM_USER_PLAN}`;
|
|
492
|
+
const defaultText = premiumOnlyBlocked ? '⭐ Este comando é exclusivo para usuários premium.' : `🔒 Seu plano atual (*${userPlan}*) não tem acesso a este comando.`;
|
|
493
|
+
await sendAndStore(
|
|
494
|
+
sock,
|
|
495
|
+
remoteJid,
|
|
496
|
+
{
|
|
497
|
+
text: [defaultText, `Planos permitidos: *${allowedPlansLabel}*.`, permissionText].filter(Boolean).join('\n'),
|
|
498
|
+
},
|
|
499
|
+
{ quoted: messageInfo, ephemeralExpiration: expirationMessage },
|
|
500
|
+
);
|
|
501
|
+
return;
|
|
502
|
+
}
|
|
503
|
+
|
|
504
|
+
// Valida se a categoria informada é suportada pela API para o tipo escolhido.
|
|
505
|
+
if (!categoryList.includes(category)) {
|
|
506
|
+
await sendUsage(sock, remoteJid, messageInfo, expirationMessage, definition.type, commandPrefix);
|
|
507
|
+
return;
|
|
508
|
+
}
|
|
509
|
+
|
|
510
|
+
const planLimit = resolvePlanLimitConfig(definition.entry, userPlan);
|
|
511
|
+
if (planLimit) {
|
|
512
|
+
const senderKey = normalizeJid(senderJid || '') || String(senderJid || remoteJid || '').trim() || 'unknown';
|
|
513
|
+
const limitState = checkPlanUsageRateLimit({
|
|
514
|
+
commandKey: definition.canonicalName || definition.fallbackAlias || definition.type,
|
|
515
|
+
userPlan,
|
|
516
|
+
scope: planLimit.scope,
|
|
517
|
+
max: planLimit.max,
|
|
518
|
+
windowMs: planLimit.windowMs,
|
|
519
|
+
senderKey,
|
|
520
|
+
remoteJid,
|
|
521
|
+
});
|
|
522
|
+
|
|
523
|
+
if (limitState.limited) {
|
|
524
|
+
await sendAndStore(
|
|
525
|
+
sock,
|
|
526
|
+
remoteJid,
|
|
527
|
+
{
|
|
528
|
+
text: `⏳ Limite de uso do plano *${userPlan}* atingido: *${planLimit.max}* uso(s) a cada *${formatWindowDuration(planLimit.windowMs)}*.\nTente novamente em *${formatDuration(limitState.remainingMs)}*.`,
|
|
529
|
+
},
|
|
530
|
+
{ quoted: messageInfo, ephemeralExpiration: expirationMessage },
|
|
531
|
+
);
|
|
532
|
+
return;
|
|
533
|
+
}
|
|
534
|
+
}
|
|
535
|
+
|
|
536
|
+
try {
|
|
537
|
+
const imageUrl = await fetchWaifuPics(definition.type, category);
|
|
538
|
+
if (!imageUrl) {
|
|
539
|
+
await sendAndStore(sock, remoteJid, { text: '❌ Não foi possível obter a imagem agora. Tente novamente.' }, { quoted: messageInfo, ephemeralExpiration: expirationMessage });
|
|
540
|
+
return;
|
|
541
|
+
}
|
|
542
|
+
|
|
543
|
+
// Envia a imagem obtida com uma legenda identificando o tipo e a categoria.
|
|
544
|
+
await sendAndStore(
|
|
545
|
+
sock,
|
|
546
|
+
remoteJid,
|
|
547
|
+
{
|
|
548
|
+
image: { url: imageUrl },
|
|
549
|
+
caption: `🖼️ ${definition.type.toUpperCase()} • ${category}`,
|
|
550
|
+
},
|
|
551
|
+
{ quoted: messageInfo, ephemeralExpiration: expirationMessage },
|
|
552
|
+
);
|
|
553
|
+
} catch (error) {
|
|
554
|
+
logger.error('handleWaifuPicsCommand: erro ao consultar a API Waifu.pics.', {
|
|
555
|
+
error: error.message,
|
|
556
|
+
type: definition.type,
|
|
557
|
+
command: definition.canonicalName,
|
|
558
|
+
category,
|
|
559
|
+
});
|
|
560
|
+
await sendAndStore(sock, remoteJid, { text: '❌ Erro ao consultar a Waifu.pics. Tente novamente.' }, { quoted: messageInfo, ephemeralExpiration: expirationMessage });
|
|
561
|
+
}
|
|
562
|
+
}
|
|
563
|
+
|
|
564
|
+
/**
|
|
565
|
+
* Gera uma string formatada contendo o texto de ajuda e o catálogo de categorias do módulo Waifu.pics.
|
|
566
|
+
*
|
|
567
|
+
* @param {string} [commandPrefix] - Prefixo a ser exibido nos exemplos de comando.
|
|
568
|
+
* @returns {string} Texto de ajuda formatado.
|
|
569
|
+
*/
|
|
570
|
+
export const getWaifuPicsUsageText = (commandPrefix = DEFAULT_COMMAND_PREFIX) => {
|
|
571
|
+
const sfwDefinition = resolveCommandDefinition('sfw');
|
|
572
|
+
const nsfwDefinition = resolveCommandDefinition('nsfw');
|
|
573
|
+
|
|
574
|
+
const sfwCategories = resolveCommandCategories(sfwDefinition);
|
|
575
|
+
const nsfwCategories = resolveCommandCategories(nsfwDefinition);
|
|
576
|
+
|
|
577
|
+
const sfwUsageLine = toUsageCommandLine(resolvePrimaryUsageLine(sfwDefinition, commandPrefix), `${commandPrefix}${sfwDefinition.fallbackAlias} <categoria>`);
|
|
578
|
+
const nsfwUsageLine = toUsageCommandLine(resolvePrimaryUsageLine(nsfwDefinition, commandPrefix), `${commandPrefix}${nsfwDefinition.fallbackAlias} <categoria>`);
|
|
579
|
+
|
|
580
|
+
const usageHeader = firstString(getWaifuPicsTextConfig()?.usage_header, '🖼️ *Waifu pics — Categorias*');
|
|
581
|
+
const sampleCategory = resolveDefaultCategory(sfwDefinition, sfwCategories);
|
|
582
|
+
const sampleToken = resolveCommandToken(sfwDefinition.entry, sfwDefinition.fallbackAlias);
|
|
583
|
+
const sampleCommand = `${commandPrefix}${sampleToken} ${sampleCategory}`;
|
|
584
|
+
|
|
585
|
+
return [usageHeader, '', `📗 *${COMMAND_DEFINITION_BY_TYPE.sfw.modeLabel.replace(/^📗\s*/, '')}*`, `Comando: *${sfwUsageLine}*`, formatCategoriesList(sfwCategories), '', `🔞 *${COMMAND_DEFINITION_BY_TYPE.nsfw.modeLabel.replace(/^🔞\s*/, '')}*`, `Comando: *${nsfwUsageLine}*`, formatCategoriesList(nsfwCategories), '', `Ex.: *${sampleCommand}*`].join('\n');
|
|
586
|
+
};
|
|
@@ -0,0 +1,68 @@
|
|
|
1
|
+
import path from 'node:path';
|
|
2
|
+
import { fileURLToPath } from 'node:url';
|
|
3
|
+
|
|
4
|
+
import { createModuleCommandConfigRuntime } from '../../services/ai/moduleCommandConfigRuntimeService.js';
|
|
5
|
+
|
|
6
|
+
const __filename = fileURLToPath(import.meta.url);
|
|
7
|
+
const __dirname = path.dirname(__filename);
|
|
8
|
+
const CONFIG_PATH = path.join(__dirname, 'commandConfig.json');
|
|
9
|
+
|
|
10
|
+
const DEFAULT_TEXTS = {
|
|
11
|
+
usage_header: '',
|
|
12
|
+
};
|
|
13
|
+
|
|
14
|
+
const runtime = createModuleCommandConfigRuntime({
|
|
15
|
+
configPath: CONFIG_PATH,
|
|
16
|
+
fallbackConfig: {
|
|
17
|
+
module: 'waifuPicsModule',
|
|
18
|
+
commands: [],
|
|
19
|
+
textos: DEFAULT_TEXTS,
|
|
20
|
+
},
|
|
21
|
+
});
|
|
22
|
+
|
|
23
|
+
const renderUsageMethod = (method, commandPrefix) => String(method || '').replaceAll('<prefix>', String(commandPrefix || '/'));
|
|
24
|
+
|
|
25
|
+
const resolveUsageLines = (entry, variant) => {
|
|
26
|
+
if (!entry || typeof entry !== 'object') return [];
|
|
27
|
+
|
|
28
|
+
const usageMessages = entry?.mensagens_uso && typeof entry.mensagens_uso === 'object' ? entry.mensagens_uso : null;
|
|
29
|
+
|
|
30
|
+
if (usageMessages) {
|
|
31
|
+
const variantKey = typeof variant === 'string' ? variant.trim() : '';
|
|
32
|
+
const picked = (variantKey && usageMessages[variantKey]) || usageMessages.default || null;
|
|
33
|
+
if (Array.isArray(picked)) {
|
|
34
|
+
return picked.filter(Boolean).map((value) => String(value));
|
|
35
|
+
}
|
|
36
|
+
if (typeof picked === 'string' && picked.trim()) {
|
|
37
|
+
return [picked.trim()];
|
|
38
|
+
}
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
const methods = Array.isArray(entry?.metodos_de_uso) ? entry.metodos_de_uso : [];
|
|
42
|
+
return methods.filter(Boolean).map((value) => String(value));
|
|
43
|
+
};
|
|
44
|
+
|
|
45
|
+
export const getWaifuPicsModuleConfig = () => runtime.getModuleConfig();
|
|
46
|
+
|
|
47
|
+
export const resolveWaifuPicsCommandName = (command) => runtime.resolveCommandName(command);
|
|
48
|
+
export const getWaifuPicsCommandEntry = (command) => runtime.getCommandEntry(command);
|
|
49
|
+
export const listEnabledWaifuPicsCommands = () => runtime.listEnabledCommands();
|
|
50
|
+
|
|
51
|
+
export const getWaifuPicsTextConfig = () => {
|
|
52
|
+
const config = getWaifuPicsModuleConfig();
|
|
53
|
+
const raw = config?.textos && typeof config.textos === 'object' ? config.textos : {};
|
|
54
|
+
return {
|
|
55
|
+
...DEFAULT_TEXTS,
|
|
56
|
+
...raw,
|
|
57
|
+
};
|
|
58
|
+
};
|
|
59
|
+
|
|
60
|
+
export const getWaifuPicsUsageText = (command, { commandPrefix = '/', header, variant } = {}) => {
|
|
61
|
+
const entry = getWaifuPicsCommandEntry(command);
|
|
62
|
+
const methods = resolveUsageLines(entry, variant);
|
|
63
|
+
if (!methods.length) return '';
|
|
64
|
+
|
|
65
|
+
const prefixHeader = typeof header === 'string' ? header : getWaifuPicsTextConfig().usage_header || DEFAULT_TEXTS.usage_header;
|
|
66
|
+
const lines = methods.map((method) => renderUsageMethod(method, commandPrefix));
|
|
67
|
+
return prefixHeader ? [prefixHeader, ...lines].join('\n') : lines.join('\n');
|
|
68
|
+
};
|