@omnizap-system/omnizap 2.6.0 → 2.6.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.env.example +58 -13
- package/.github/workflows/ci.yml +5 -5
- package/.github/workflows/codeql.yml +1 -1
- package/.github/workflows/db-migration-check.yml +2 -2
- package/.github/workflows/dependency-review.yml +1 -1
- package/.github/workflows/deploy.yml +2 -2
- package/.github/workflows/release.yml +2 -2
- package/.github/workflows/security-attest-provenance.yml +2 -2
- package/.github/workflows/security-gitleaks.yml +13 -4
- package/.github/workflows/security-runner-hardening.yml +2 -2
- package/.github/workflows/security-scorecard.yml +1 -1
- package/.github/workflows/security-zap-baseline.yml +1 -1
- package/.github/workflows/security-zap-full-scan.yml +2 -1
- package/.github/workflows/security-zizmor.yml +1 -1
- package/.github/workflows/wiki-sync.yml +1 -1
- package/.gitleaksignore +9 -0
- package/CODE_OF_CONDUCT.md +2 -2
- package/GEMINI.md +64 -0
- package/README.md +52 -82
- package/SECURITY.md +1 -1
- package/app/config/index.js +2 -0
- package/app/configParts/adminIdentity.js +5 -5
- package/app/configParts/baileysConfig.js +230 -58
- package/app/configParts/groupUtils.js +5 -0
- package/app/configParts/messagePersistenceService.js +145 -4
- package/app/configParts/sessionConfig.js +157 -0
- package/app/connection/baileysCompatibility.test.js +1 -1
- package/app/connection/groupOwnerWriteStateResolver.js +109 -0
- package/app/connection/socketController.js +660 -158
- package/app/connection/socketController.multiSession.test.js +108 -0
- package/app/controllers/messageController.js +1 -1
- package/app/controllers/messagePipeline/commandMiddleware.js +12 -10
- package/app/controllers/messagePipeline/conversationMiddleware.js +2 -1
- package/app/controllers/messagePipeline/messagePipelineMiddlewares.test.js +104 -0
- package/app/controllers/messagePipeline/preProcessingMiddlewares.js +80 -2
- package/app/controllers/messageProcessingPipeline.js +93 -13
- package/app/controllers/messageProcessingPipeline.test.js +200 -0
- package/app/modules/adminModule/AGENT.md +1 -1
- package/app/modules/adminModule/commandConfig.json +3318 -1347
- package/app/modules/adminModule/groupCommandHandlers.js +858 -15
- package/app/modules/adminModule/groupCommandHandlers.test.js +378 -11
- package/app/modules/adminModule/groupWarningRepository.js +152 -0
- package/app/modules/aiModule/AGENT.md +47 -30
- package/app/modules/aiModule/aiConfigRuntime.js +1 -0
- package/app/modules/aiModule/catCommand.js +135 -27
- package/app/modules/aiModule/commandConfig.json +114 -28
- package/app/modules/analyticsModule/messageAnalysisEventRepository.js +54 -6
- package/app/modules/gameModule/AGENT.md +1 -1
- package/app/modules/gameModule/commandConfig.json +29 -0
- package/app/modules/menuModule/AGENT.md +1 -1
- package/app/modules/menuModule/commandConfig.json +45 -10
- package/app/modules/menuModule/menuCatalogService.js +190 -0
- package/app/modules/menuModule/menuCommandUsageRepository.js +109 -0
- package/app/modules/menuModule/menuDynamicService.js +511 -0
- package/app/modules/menuModule/menuDynamicService.test.js +141 -0
- package/app/modules/menuModule/menus.js +36 -5
- package/app/modules/playModule/AGENT.md +10 -5
- package/app/modules/playModule/commandConfig.json +140 -12
- package/app/modules/playModule/playCommand.js +1 -1417
- package/app/modules/playModule/playCommandConstants.js +80 -0
- package/app/modules/playModule/playCommandCore.js +361 -0
- package/app/modules/playModule/playCommandHandlers.js +41 -0
- package/app/modules/playModule/playCommandMediaClient.js +1872 -0
- package/app/modules/playModule/playConfigRuntime.js +245 -4
- package/app/modules/playModule/playModuleCriticalFlows.test.js +152 -0
- package/app/modules/quoteModule/AGENT.md +1 -1
- package/app/modules/quoteModule/commandConfig.json +29 -0
- package/app/modules/quoteModule/quoteCommand.js +3 -2
- package/app/modules/rpgPokemonModule/AGENT.md +1 -1
- package/app/modules/rpgPokemonModule/commandConfig.json +29 -0
- package/app/modules/rpgPokemonModule/rpgBattleCanvasRenderer.js +5 -4
- package/app/modules/rpgPokemonModule/rpgBattleService.test.js +2 -1
- package/app/modules/rpgPokemonModule/rpgPokemonDomain.js +2 -1
- package/app/modules/rpgPokemonModule/rpgPokemonService.js +38 -37
- package/app/modules/rpgPokemonModule/rpgProfileCanvasRenderer.js +4 -3
- package/app/modules/statsModule/AGENT.md +1 -1
- package/app/modules/statsModule/commandConfig.json +58 -0
- package/app/modules/statsModule/rankingCommon.js +5 -4
- package/app/modules/stickerModule/AGENT.md +1 -1
- package/app/modules/stickerModule/addStickerMetadata.js +4 -3
- package/app/modules/stickerModule/commandConfig.json +145 -0
- package/app/modules/stickerModule/stickerCommand.js +1 -1
- package/app/modules/stickerPackModule/AGENT.md +1 -1
- package/app/modules/stickerPackModule/autoPackCollectorService.js +5 -1
- package/app/modules/stickerPackModule/commandConfig.json +29 -0
- package/app/modules/stickerPackModule/semanticThemeClusterService.js +7 -6
- package/app/modules/stickerPackModule/stickerAutoPackByTagsRuntime.js +10 -9
- package/app/modules/stickerPackModule/stickerClassificationBackgroundRuntime.js +9 -8
- package/app/modules/stickerPackModule/stickerDomainEventConsumerRuntime.js +3 -2
- package/app/modules/stickerPackModule/stickerMarketplaceDriftService.js +2 -1
- package/app/modules/stickerPackModule/stickerPackCommandHandlers.js +80 -58
- package/app/modules/stickerPackModule/stickerPackMarketplaceService.js +2 -1
- package/app/modules/stickerPackModule/stickerPackRepository.js +2 -1
- package/app/modules/stickerPackModule/stickerPackScoreSnapshotRuntime.js +5 -4
- package/app/modules/stickerPackModule/stickerPackService.js +13 -6
- package/app/modules/stickerPackModule/stickerStorageService.js +3 -2
- package/app/modules/stickerPackModule/stickerWorkerPipelineRuntime.js +2 -1
- package/app/modules/systemMetricsModule/AGENT.md +1 -1
- package/app/modules/systemMetricsModule/commandConfig.json +29 -0
- package/app/modules/systemMetricsModule/pingCommand.js +6 -5
- package/app/modules/tiktokModule/AGENT.md +1 -1
- package/app/modules/tiktokModule/commandConfig.json +29 -0
- package/app/modules/tiktokModule/tiktokCommand.js +2 -1
- package/app/modules/userModule/AGENT.md +1 -1
- package/app/modules/userModule/commandConfig.json +29 -0
- package/app/modules/userModule/userCommand.js +72 -23
- package/app/modules/waifuPicsModule/AGENT.md +57 -27
- package/app/modules/waifuPicsModule/commandConfig.json +87 -0
- package/app/modules/waifuPicsModule/waifuPicsCommand.js +3 -2
- package/app/observability/metrics.js +136 -0
- package/app/services/ai/commandConfigEnrichmentService.js +229 -47
- package/app/services/ai/conversationRouterService.js +4 -3
- package/app/services/ai/geminiService.js +132 -7
- package/app/services/ai/geminiService.test.js +59 -2
- package/app/services/ai/globalModuleAiHelpService.js +3 -2
- package/app/services/ai/messageCommandExecutionService.js +2 -1
- package/app/services/ai/moduleAiHelpCoreService.js +45 -14
- package/app/services/ai/moduleToolExecutorService.js +3 -2
- package/app/services/ai/moduleToolRegistryService.js +2 -1
- package/app/services/ai/toolCandidateSelectorService.js +6 -5
- package/app/services/auth/googleWebLinkService.js +3 -2
- package/app/services/auth/whatsappLoginLinkService.js +3 -2
- package/app/services/external/pokeApiService.js +4 -3
- package/app/services/group/groupMetadataService.js +24 -1
- package/app/services/infra/dbWriteQueue.js +57 -26
- package/app/services/infra/featureFlagService.js +2 -1
- package/app/services/messaging/captchaService.js +3 -2
- package/app/services/messaging/newsBroadcastService.js +846 -29
- package/app/services/multiSession/assignmentBalancerService.js +457 -0
- package/app/services/multiSession/groupOwnershipRepository.js +381 -0
- package/app/services/multiSession/groupOwnershipService.js +890 -0
- package/app/services/multiSession/groupOwnershipService.test.js +309 -0
- package/app/services/multiSession/sessionRegistryService.js +293 -0
- package/app/services/sticker/stickerFocusService.js +11 -10
- package/app/store/aiPromptStore.js +36 -19
- package/app/store/conversationSessionStore.js +7 -6
- package/app/store/groupConfigStore.js +41 -5
- package/app/store/premiumUserStore.js +21 -7
- package/app/utils/antiLink/antiLinkModule.js +352 -16
- package/app/workers/aiHelperContinuousLearningWorker.js +512 -0
- package/app/workers/aiLearningWorker.js +6 -5
- package/app/workers/commandConfigEnrichmentWorker.js +4 -3
- package/database/index.js +14 -8
- package/database/migrations/20260307_d0_hardening_down.sql +1 -1
- package/database/migrations/20260314_d7_canonical_sender_down.sql +1 -1
- package/database/migrations/20260406_d30_security_analytics_down.sql +1 -1
- package/database/migrations/20260411_d35_group_community_metadata_down.sql +59 -0
- package/database/migrations/20260411_d35_group_community_metadata_up.sql +62 -0
- package/database/migrations/20260412_d36_system_config_tables_down.sql +32 -0
- package/database/migrations/20260412_d36_system_config_tables_up.sql +66 -0
- package/database/migrations/20260413_d37_group_user_warnings_down.sql +11 -0
- package/database/migrations/20260413_d37_group_user_warnings_up.sql +24 -0
- package/database/migrations/20260414_d38_multi_session_foundation_down.sql +72 -0
- package/database/migrations/20260414_d38_multi_session_foundation_up.sql +125 -0
- package/database/migrations/20260414_d39_multi_session_cutover_down.sql +103 -0
- package/database/migrations/20260414_d39_multi_session_cutover_up.sql +83 -0
- package/database/schema.sql +102 -1
- package/docker-compose.yml +4 -1
- package/docs/compliance/acceptable-use-policy-2026-03-07.md +1 -1
- package/docs/compliance/dpa-b2b-standard-2026-03-07.md +1 -1
- package/docs/compliance/privacy-policy-2026-03-07.md +4 -4
- package/docs/security/dsar-lgpd-runbook-2026-03-07.md +1 -1
- package/docs/security/incident-response-lgpd-anpd-runbook-2026-03-07.md +1 -1
- package/docs/security/network-hardening-runbook-2026-03-07.md +53 -0
- package/docs/security/omnizap-static-security-headers.conf +25 -0
- package/docs/wiki/Home.md +1 -1
- package/ecosystem.prod.config.cjs +32 -12
- package/index.js +57 -23
- package/observability/alert-rules.yml +20 -0
- package/observability/grafana/dashboards/omnizap-system-admin.json +229 -0
- package/observability/mysql-setup.sql +4 -4
- package/observability/system-admin-observability.md +26 -0
- package/package.json +20 -6
- package/public/apple-touch-icon.png +0 -0
- package/public/comandos/commands-catalog.json +2853 -3326
- 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 +3 -2
- package/public/js/apps/commandsReactApp.js +280 -99
- package/public/js/apps/createPackApp.js +11 -10
- package/public/js/apps/homeReactApp.js +181 -130
- package/public/js/apps/loginReactApp.js +1 -1
- package/public/js/apps/stickersApp.js +263 -110
- package/public/js/apps/termsReactApp.js +73 -24
- package/public/js/apps/userApp.js +4 -3
- package/public/js/apps/userPasswordResetReactApp.js +406 -0
- package/public/js/apps/userReactApp.js +355 -280
- package/public/js/apps/userSystemAdmReactApp.js +1506 -0
- package/public/pages/api-docs.html +1 -1
- package/public/pages/aup.html +2 -2
- package/public/pages/dpa.html +3 -3
- package/public/pages/licenca.html +4 -4
- package/public/pages/login.html +1 -1
- package/public/pages/notice-and-takedown.html +2 -2
- package/public/pages/politica-de-privacidade.html +6 -6
- package/public/pages/seo-bot-whatsapp-para-grupo.html +3 -3
- package/public/pages/seo-bot-whatsapp-sem-programar.html +3 -3
- package/public/pages/seo-como-automatizar-avisos-no-whatsapp.html +3 -3
- package/public/pages/seo-como-criar-comandos-whatsapp.html +3 -3
- package/public/pages/seo-como-evitar-spam-no-whatsapp.html +3 -3
- package/public/pages/seo-como-moderar-grupo-whatsapp.html +3 -3
- package/public/pages/seo-como-organizar-comunidade-whatsapp.html +3 -3
- package/public/pages/seo-melhor-bot-whatsapp-para-grupos.html +3 -3
- package/public/pages/stickers-admin.html +1 -1
- package/public/pages/stickers-create.html +1 -1
- package/public/pages/stickers.html +6 -6
- package/public/pages/suboperadores.html +2 -2
- package/public/pages/termos-de-uso-texto-integral.html +6 -6
- package/public/pages/termos-de-uso.html +3 -3
- package/public/pages/user-password-reset.html +4 -5
- package/public/pages/user-systemadm.html +9 -463
- package/public/pages/user.html +2 -2
- package/scripts/clear-whatsapp-session.sh +123 -0
- package/scripts/core-ai-mode.mjs +163 -0
- package/scripts/deploy.sh +11 -1
- package/scripts/email-broadcast-terms-update.mjs +2 -1
- package/scripts/enrich-command-config-ux-openai.mjs +492 -0
- package/scripts/generate-commands-catalog.mjs +166 -2
- package/scripts/generate-module-agents.mjs +2 -1
- package/scripts/generate-seo-satellite-pages.mjs +5 -4
- package/scripts/github-deploy-notify.mjs +2 -1
- package/scripts/github-release-notify.mjs +25 -10
- package/scripts/new-whatsapp-session.sh +317 -0
- package/scripts/release.sh +2 -19
- package/scripts/security-smoketest.mjs +6 -5
- package/scripts/security-web-surface-check.mjs +218 -0
- package/scripts/sticker-catalog-loadtest.mjs +5 -4
- package/server/auth/googleWebAuth/googleWebAuthService.js +8 -7
- package/server/auth/jwt/webJwtService.js +1 -1
- package/server/auth/stickerCatalogAuthContext.js +2 -1
- package/server/auth/termsAcceptance/termsAcceptanceHandler.js +2 -1
- package/server/auth/userPassword/userPasswordAuthService.js +2 -1
- package/server/auth/userPassword/userPasswordRecoveryService.js +4 -3
- package/server/auth/webAccount/webAccountHandlers.js +9 -10
- package/server/controllers/admin/adminPanelHandlers.js +267 -16
- package/server/controllers/admin/systemAdminController.js +267 -0
- package/server/controllers/seo/stickerCatalogSeoContext.js +10 -9
- package/server/controllers/sticker/nonCatalogHandlers.js +2 -1
- package/server/controllers/sticker/stickerCatalogController.js +23 -36
- package/server/controllers/system/contactController.js +9 -17
- package/server/controllers/system/githubController.js +3 -2
- package/server/controllers/system/stickerCatalogSystemContext.js +41 -19
- package/server/controllers/system/systemController.js +254 -1
- package/server/controllers/system/systemMetricsController.js +2 -1
- package/server/controllers/userController.js +6 -0
- package/server/email/emailTemplateService.js +5 -3
- package/server/http/httpServer.js +11 -6
- package/server/middleware/rateLimit.js +2 -1
- package/server/middleware/securityHeaders.js +20 -1
- package/server/routes/admin/systemAdminRouter.js +6 -0
- package/server/routes/indexRouter.js +30 -6
- package/server/routes/observability/grafanaProxyRouter.js +254 -0
- package/server/routes/static/staticPageRouter.js +27 -1
- package/server/utils/publicContact.js +31 -0
- package/utils/time/timeModule.js +135 -0
- package/utils/time/timeModule.test.js +65 -0
- package/utils/whatsapp/contactEnv.js +39 -0
- package/vite.config.mjs +7 -1
- package/public/assets/images/brand-icon-192.png +0 -0
- package/scripts/sync-readme-snapshot.mjs +0 -133
|
@@ -1,18 +1,20 @@
|
|
|
1
|
+
import { now as __timeNow, nowIso as __timeNowIso, toUnixMs as __timeNowMs } from '#time';
|
|
1
2
|
import { handleMenuAdmCommand } from '../menuModule/menus.js';
|
|
2
3
|
import { downloadMediaMessage, getJidServer, isLidJid, isSameJidUser, isWhatsAppJid, LID_USER_JID_SERVERS, normalizeJid, WHATSAPP_USER_JID_SERVERS } from '../../config/index.js';
|
|
3
4
|
import { isUserAdmin, createGroup, acceptGroupInvite, getGroupInfo, getGroupRequestParticipantsList, updateGroupAddMode, updateGroupSettings, updateGroupParticipants, leaveGroup, getGroupInviteCode, revokeGroupInviteCode, getGroupInfoFromInvite, updateGroupRequestParticipants, updateGroupSubject, updateGroupDescription, toggleEphemeral } from '../../config/index.js';
|
|
4
5
|
import groupConfigStore from '../../store/groupConfigStore.js';
|
|
5
6
|
import premiumUserStore from '../../store/premiumUserStore.js';
|
|
6
7
|
import logger from '#logger';
|
|
7
|
-
import { KNOWN_NETWORKS } from '../../utils/antiLink/antiLinkModule.js';
|
|
8
|
+
import { KNOWN_NETWORKS, purgeRecentMessagesForSenderCandidates } from '../../utils/antiLink/antiLinkModule.js';
|
|
8
9
|
import { getNewsStatusForGroup, startNewsBroadcastForGroup, stopNewsBroadcastForGroup } from '../../services/messaging/newsBroadcastService.js';
|
|
9
10
|
import { sendAndStore } from '../../services/messaging/messagePersistenceService.js';
|
|
10
11
|
import { clearCaptchasForGroup } from '../../services/messaging/captchaService.js';
|
|
11
12
|
import { getAdminJid, isAdminSenderAsync } from '../../config/index.js';
|
|
12
13
|
import { extractUserIdInfo, resolveUserId } from '../../config/index.js';
|
|
13
|
-
import { DEFAULT_STICKER_FOCUS_CHAT_WINDOW_MINUTES, DEFAULT_STICKER_FOCUS_MESSAGE_COOLDOWN_MINUTES, MAX_STICKER_FOCUS_CHAT_WINDOW_MINUTES, MAX_STICKER_FOCUS_MESSAGE_COOLDOWN_MINUTES, MIN_STICKER_FOCUS_CHAT_WINDOW_MINUTES, MIN_STICKER_FOCUS_MESSAGE_COOLDOWN_MINUTES, clampStickerFocusChatWindowMinutes, clampStickerFocusMessageCooldownMinutes, resolveStickerFocusState } from '../../services/sticker/stickerFocusService.js';
|
|
14
|
+
import { DEFAULT_STICKER_FOCUS_CHAT_WINDOW_MINUTES, DEFAULT_STICKER_FOCUS_MESSAGE_ALLOWANCE, DEFAULT_STICKER_FOCUS_MESSAGE_COOLDOWN_MINUTES, MAX_STICKER_FOCUS_CHAT_WINDOW_MINUTES, MAX_STICKER_FOCUS_MESSAGE_ALLOWANCE, MAX_STICKER_FOCUS_MESSAGE_COOLDOWN_MINUTES, MIN_STICKER_FOCUS_CHAT_WINDOW_MINUTES, MIN_STICKER_FOCUS_MESSAGE_ALLOWANCE, MIN_STICKER_FOCUS_MESSAGE_COOLDOWN_MINUTES, clampStickerFocusChatWindowMinutes, clampStickerFocusMessageAllowance, clampStickerFocusMessageCooldownMinutes, resolveStickerFocusState } from '../../services/sticker/stickerFocusService.js';
|
|
14
15
|
import { getAdminTextConfig, getAdminUsageText, isAdminCommandName, resolveAdminCommandName } from './adminConfigRuntime.js';
|
|
15
16
|
import { explicarComando, gerarFaqAutomatica, responderPergunta, startAdminAiHelpScheduler } from './adminAiHelpService.js';
|
|
17
|
+
import { addGroupWarning, clearGroupWarnings, countGroupWarnings, listGroupWarnings } from './groupWarningRepository.js';
|
|
16
18
|
const OWNER_JID = getAdminJid();
|
|
17
19
|
const DEFAULT_COMMAND_PREFIX = process.env.COMMAND_PREFIX || '/';
|
|
18
20
|
const ADMIN_TEXTS = getAdminTextConfig();
|
|
@@ -20,6 +22,11 @@ const GROUP_ONLY_COMMAND_MESSAGE = ADMIN_TEXTS.group_only_command_message;
|
|
|
20
22
|
const NO_PERMISSION_COMMAND_MESSAGE = ADMIN_TEXTS.no_permission_command_message;
|
|
21
23
|
const OWNER_ONLY_COMMAND_MESSAGE = ADMIN_TEXTS.owner_only_command_message;
|
|
22
24
|
const USER_JID_SERVERS = new Set([...WHATSAPP_USER_JID_SERVERS, ...LID_USER_JID_SERVERS]);
|
|
25
|
+
const MAX_WARN_REASON_CHARS = 500;
|
|
26
|
+
const WARNINGS_LIST_PREVIEW_LIMIT = 8;
|
|
27
|
+
const DEFAULT_WARN_AUTO_BAN_THRESHOLD = 3;
|
|
28
|
+
const MIN_WARN_AUTO_BAN_THRESHOLD = 1;
|
|
29
|
+
const MAX_WARN_AUTO_BAN_THRESHOLD = 50;
|
|
23
30
|
const normalizePhoneDigits = (value) => String(value || '').replace(/\D+/g, '');
|
|
24
31
|
|
|
25
32
|
const LEGACY_ADMIN_ROUTE_BY_CANONICAL = {
|
|
@@ -105,6 +112,94 @@ const getParticipantJids = (messageInfo, args) => {
|
|
|
105
112
|
return dedupeParticipantJids(args);
|
|
106
113
|
};
|
|
107
114
|
|
|
115
|
+
const normalizeWarnReason = (value) =>
|
|
116
|
+
String(value || '')
|
|
117
|
+
.replace(/\s+/g, ' ')
|
|
118
|
+
.trim()
|
|
119
|
+
.slice(0, MAX_WARN_REASON_CHARS);
|
|
120
|
+
|
|
121
|
+
const formatUserMentionToken = (jid) => {
|
|
122
|
+
const userPart = String(jid || '')
|
|
123
|
+
.split('@')[0]
|
|
124
|
+
.trim();
|
|
125
|
+
return userPart ? `@${userPart}` : '@usuario';
|
|
126
|
+
};
|
|
127
|
+
|
|
128
|
+
const formatWarnTimestamp = (value) => {
|
|
129
|
+
if (!value) return 'data desconhecida';
|
|
130
|
+
const parsed = Date.parse(String(value));
|
|
131
|
+
if (!Number.isFinite(parsed)) return String(value);
|
|
132
|
+
return new Date(parsed).toLocaleString('pt-BR');
|
|
133
|
+
};
|
|
134
|
+
|
|
135
|
+
const resolveSingleTargetFromMessage = (messageInfo, args = []) => {
|
|
136
|
+
const safeArgs = Array.isArray(args) ? args.map((value) => String(value || '').trim()).filter(Boolean) : [];
|
|
137
|
+
const contextInfo = messageInfo?.message?.extendedTextMessage?.contextInfo || {};
|
|
138
|
+
const mentionedJids = dedupeParticipantJids(contextInfo?.mentionedJid || []);
|
|
139
|
+
|
|
140
|
+
if (mentionedJids.length > 1) {
|
|
141
|
+
return {
|
|
142
|
+
targetJid: '',
|
|
143
|
+
remainingArgs: safeArgs,
|
|
144
|
+
multipleTargets: true,
|
|
145
|
+
};
|
|
146
|
+
}
|
|
147
|
+
|
|
148
|
+
if (mentionedJids.length === 1) {
|
|
149
|
+
const targetJid = mentionedJids[0];
|
|
150
|
+
const remainingArgs = [...safeArgs];
|
|
151
|
+
|
|
152
|
+
while (remainingArgs.length > 0) {
|
|
153
|
+
const token = String(remainingArgs[0] || '').trim();
|
|
154
|
+
if (!token) {
|
|
155
|
+
remainingArgs.shift();
|
|
156
|
+
continue;
|
|
157
|
+
}
|
|
158
|
+
|
|
159
|
+
const normalizedToken = normalizeParticipantJid(token);
|
|
160
|
+
if (normalizedToken && (normalizedToken === targetJid || isSameJidUser(normalizedToken, targetJid))) {
|
|
161
|
+
remainingArgs.shift();
|
|
162
|
+
continue;
|
|
163
|
+
}
|
|
164
|
+
if (token.startsWith('@')) {
|
|
165
|
+
remainingArgs.shift();
|
|
166
|
+
continue;
|
|
167
|
+
}
|
|
168
|
+
break;
|
|
169
|
+
}
|
|
170
|
+
|
|
171
|
+
return {
|
|
172
|
+
targetJid,
|
|
173
|
+
remainingArgs,
|
|
174
|
+
multipleTargets: false,
|
|
175
|
+
};
|
|
176
|
+
}
|
|
177
|
+
|
|
178
|
+
const firstArgTarget = normalizeParticipantJid(safeArgs[0] || '');
|
|
179
|
+
if (firstArgTarget) {
|
|
180
|
+
return {
|
|
181
|
+
targetJid: firstArgTarget,
|
|
182
|
+
remainingArgs: safeArgs.slice(1),
|
|
183
|
+
multipleTargets: false,
|
|
184
|
+
};
|
|
185
|
+
}
|
|
186
|
+
|
|
187
|
+
const repliedTo = dedupeParticipantJids([contextInfo?.participant || '']);
|
|
188
|
+
if (repliedTo.length === 1) {
|
|
189
|
+
return {
|
|
190
|
+
targetJid: repliedTo[0],
|
|
191
|
+
remainingArgs: safeArgs,
|
|
192
|
+
multipleTargets: false,
|
|
193
|
+
};
|
|
194
|
+
}
|
|
195
|
+
|
|
196
|
+
return {
|
|
197
|
+
targetJid: '',
|
|
198
|
+
remainingArgs: safeArgs,
|
|
199
|
+
multipleTargets: false,
|
|
200
|
+
};
|
|
201
|
+
};
|
|
202
|
+
|
|
108
203
|
const resolvePremiumTargetJid = async (targetJid) => {
|
|
109
204
|
const normalizedTarget = normalizeParticipantJid(targetJid);
|
|
110
205
|
if (!normalizedTarget) return '';
|
|
@@ -148,6 +243,12 @@ const parsePositiveInteger = (value) => {
|
|
|
148
243
|
return normalized;
|
|
149
244
|
};
|
|
150
245
|
|
|
246
|
+
const clampWarnAutoBanThreshold = (value) => {
|
|
247
|
+
const parsed = parsePositiveInteger(value);
|
|
248
|
+
if (!parsed) return DEFAULT_WARN_AUTO_BAN_THRESHOLD;
|
|
249
|
+
return Math.max(MIN_WARN_AUTO_BAN_THRESHOLD, Math.min(MAX_WARN_AUTO_BAN_THRESHOLD, parsed));
|
|
250
|
+
};
|
|
251
|
+
|
|
151
252
|
const formatStickerFocusRule = ({ messageAllowanceCount, messageCooldownMinutes }) => {
|
|
152
253
|
const allowanceCount = Math.max(1, Math.floor(Number(messageAllowanceCount) || 1));
|
|
153
254
|
const cooldownMinutes = Math.max(1, Math.floor(Number(messageCooldownMinutes) || 1));
|
|
@@ -162,6 +263,96 @@ const buildStickerFocusStatusText = ({ state, commandPrefix }) => {
|
|
|
162
263
|
return ['🖼️ *Status do modo Sticker*', '', `Modo sticker: *${state.enabled ? 'ativado' : 'desativado'}*`, `Janela de chat: *${chatWindowStatus}*`, `Regra fora da janela: *${formatStickerFocusRule(state)}*`, '', `Comandos:`, `${commandPrefix}stickermode <on|off|status>`, `${commandPrefix}chatwindow <on|off|status> [minutos]`, `${commandPrefix}stickermsglimit <minutos|status|reset>`].join('\n');
|
|
163
264
|
};
|
|
164
265
|
|
|
266
|
+
const formatListForMessage = (items = [], emptyLabel = 'nenhum') => {
|
|
267
|
+
const safeItems = Array.isArray(items) ? items.map((item) => String(item || '').trim()).filter(Boolean) : [];
|
|
268
|
+
return safeItems.length ? safeItems.join(', ') : emptyLabel;
|
|
269
|
+
};
|
|
270
|
+
|
|
271
|
+
const parseFilterValues = (values = []) => {
|
|
272
|
+
const rawText = Array.isArray(values) ? values.join(' ') : String(values || '');
|
|
273
|
+
if (!rawText.trim()) return [];
|
|
274
|
+
const normalized = rawText
|
|
275
|
+
.split(/[\s,]+/)
|
|
276
|
+
.map((value) =>
|
|
277
|
+
String(value || '')
|
|
278
|
+
.trim()
|
|
279
|
+
.toLowerCase(),
|
|
280
|
+
)
|
|
281
|
+
.filter(Boolean);
|
|
282
|
+
return Array.from(new Set(normalized)).sort((left, right) => left.localeCompare(right));
|
|
283
|
+
};
|
|
284
|
+
|
|
285
|
+
const normalizeStringList = (value) => {
|
|
286
|
+
const source = Array.isArray(value) ? value : typeof value === 'string' ? value.split(',') : [];
|
|
287
|
+
return source
|
|
288
|
+
.map((entry) =>
|
|
289
|
+
String(entry || '')
|
|
290
|
+
.trim()
|
|
291
|
+
.toLowerCase(),
|
|
292
|
+
)
|
|
293
|
+
.filter(Boolean);
|
|
294
|
+
};
|
|
295
|
+
|
|
296
|
+
const normalizeNewsFilterState = (groupConfig = {}) => {
|
|
297
|
+
const nested = groupConfig?.newsFilters && typeof groupConfig.newsFilters === 'object' ? groupConfig.newsFilters : {};
|
|
298
|
+
const sourceIds = Array.from(new Set([...normalizeStringList(groupConfig.newsSources), ...normalizeStringList(groupConfig.newsSourceIds), ...normalizeStringList(nested.sources), ...normalizeStringList(nested.sourceIds)])).sort((left, right) => left.localeCompare(right));
|
|
299
|
+
const franchiseSlugs = Array.from(new Set([...normalizeStringList(groupConfig.newsFranchises), ...normalizeStringList(groupConfig.newsFranchiseSlugs), ...normalizeStringList(nested.franchises), ...normalizeStringList(nested.franchiseSlugs)])).sort((left, right) => left.localeCompare(right));
|
|
300
|
+
const entitySlugs = Array.from(new Set([...normalizeStringList(groupConfig.newsEntities), ...normalizeStringList(groupConfig.newsEntitySlugs), ...normalizeStringList(groupConfig.newsTags), ...normalizeStringList(nested.entities), ...normalizeStringList(nested.entitySlugs), ...normalizeStringList(nested.tags)])).sort((left, right) => left.localeCompare(right));
|
|
301
|
+
const onlyTrending = Boolean(groupConfig.newsOnlyTrending || nested.onlyTrending);
|
|
302
|
+
|
|
303
|
+
return {
|
|
304
|
+
sourceIds,
|
|
305
|
+
franchiseSlugs,
|
|
306
|
+
entitySlugs,
|
|
307
|
+
onlyTrending,
|
|
308
|
+
};
|
|
309
|
+
};
|
|
310
|
+
|
|
311
|
+
const buildNewsFilterConfigPatch = (groupConfig, nextState) => {
|
|
312
|
+
const nested = groupConfig?.newsFilters && typeof groupConfig.newsFilters === 'object' ? groupConfig.newsFilters : {};
|
|
313
|
+
const safeSources = Array.isArray(nextState?.sourceIds) ? nextState.sourceIds : [];
|
|
314
|
+
const safeFranchises = Array.isArray(nextState?.franchiseSlugs) ? nextState.franchiseSlugs : [];
|
|
315
|
+
const safeEntities = Array.isArray(nextState?.entitySlugs) ? nextState.entitySlugs : [];
|
|
316
|
+
const safeOnlyTrending = Boolean(nextState?.onlyTrending);
|
|
317
|
+
|
|
318
|
+
return {
|
|
319
|
+
newsSourceIds: safeSources,
|
|
320
|
+
newsSources: safeSources,
|
|
321
|
+
newsFranchiseSlugs: safeFranchises,
|
|
322
|
+
newsFranchises: safeFranchises,
|
|
323
|
+
newsEntitySlugs: safeEntities,
|
|
324
|
+
newsEntities: safeEntities,
|
|
325
|
+
newsTags: safeEntities,
|
|
326
|
+
newsOnlyTrending: safeOnlyTrending,
|
|
327
|
+
newsFilters: {
|
|
328
|
+
...nested,
|
|
329
|
+
sourceIds: safeSources,
|
|
330
|
+
sources: safeSources,
|
|
331
|
+
franchiseSlugs: safeFranchises,
|
|
332
|
+
franchises: safeFranchises,
|
|
333
|
+
entitySlugs: safeEntities,
|
|
334
|
+
entities: safeEntities,
|
|
335
|
+
tags: safeEntities,
|
|
336
|
+
onlyTrending: safeOnlyTrending,
|
|
337
|
+
},
|
|
338
|
+
};
|
|
339
|
+
};
|
|
340
|
+
|
|
341
|
+
const formatDateTimeLabel = (value) => {
|
|
342
|
+
if (!value) return 'nunca';
|
|
343
|
+
const parsed = Date.parse(String(value));
|
|
344
|
+
if (!Number.isFinite(parsed)) return String(value);
|
|
345
|
+
return new Date(parsed).toLocaleString('pt-BR');
|
|
346
|
+
};
|
|
347
|
+
|
|
348
|
+
const buildGroupAuditText = ({ config, stickerState, newsStatus, commandPrefix }) => {
|
|
349
|
+
const prefix = String(config?.commandPrefix || '').trim() || DEFAULT_COMMAND_PREFIX;
|
|
350
|
+
const newsFilters = normalizeNewsFilterState(config);
|
|
351
|
+
const stickerWindowText = stickerState.isChatWindowOpen ? `aberta (~${Math.max(1, Math.ceil(stickerState.chatWindowRemainingMs / (60 * 1000)))} min restantes)` : 'fechada';
|
|
352
|
+
|
|
353
|
+
return ['🧾 *Auditoria do Grupo*', '', `Prefixo: *${prefix}*`, `NSFW: *${config?.nsfwEnabled ? 'ativado' : 'desativado'}*`, `AutoSticker: *${config?.autoStickerEnabled ? 'ativado' : 'desativado'}*`, `Modo Sticker: *${stickerState.enabled ? 'ativado' : 'desativado'}*`, `Janela de chat: *${stickerWindowText}*`, `Regra de texto: *${formatStickerFocusRule(stickerState)}*`, `Captcha: *${config?.captchaEnabled ? 'ativado' : 'desativado'}*`, `Auto-aprovação de solicitações: *${config?.autoApproveRequestsEnabled ? 'ativada' : 'desativada'}*`, `Antilink: *${config?.antilinkEnabled ? 'ativado' : 'desativado'}*`, `Antilink redes permitidas: ${formatListForMessage(config?.antilinkAllowedNetworks || [])}`, `Antilink domínios permitidos: ${formatListForMessage(config?.antilinkAllowedDomains || [])}`, `Notícias: *${newsStatus?.enabled ? 'ativado' : 'desativado'}*`, `Notícias enviadas: *${Number(newsStatus?.sentCount || 0)}*`, `Último envio de notícias: *${formatDateTimeLabel(newsStatus?.lastSentAt)}*`, `Filtro notícias [source]: ${formatListForMessage(newsFilters.sourceIds)}`, `Filtro notícias [franchise]: ${formatListForMessage(newsFilters.franchiseSlugs)}`, `Filtro notícias [tag/entity]: ${formatListForMessage(newsFilters.entitySlugs)}`, `Filtro notícias [somente em alta]: *${newsFilters.onlyTrending ? 'sim' : 'não'}*`, `Boas-vindas: *${config?.welcomeMessageEnabled ? 'ativadas' : 'desativadas'}*`, `Despedida: *${config?.farewellMessageEnabled ? 'ativadas' : 'desativadas'}*`, '', `Dica: use *${commandPrefix}noticiasfiltro status* para ver os filtros em detalhe.`].join('\n');
|
|
354
|
+
};
|
|
355
|
+
|
|
165
356
|
export const isAdminCommand = (command) => isAdminCommandName(command);
|
|
166
357
|
|
|
167
358
|
export async function handleAdminCommand({ command, args, text, sock, messageInfo, remoteJid, senderJid, botJid, isGroupMessage, expirationMessage, commandPrefix = DEFAULT_COMMAND_PREFIX }) {
|
|
@@ -193,7 +384,7 @@ export async function handleAdminCommand({ command, args, text, sock, messageInf
|
|
|
193
384
|
const isOwner = Boolean(OWNER_JID && (await isAdminSenderAsync(senderIdentity)));
|
|
194
385
|
|
|
195
386
|
if (!subAction) {
|
|
196
|
-
await handleMenuAdmCommand(sock, remoteJid, messageInfo, expirationMessage, commandPrefix);
|
|
387
|
+
await handleMenuAdmCommand(sock, remoteJid, messageInfo, expirationMessage, commandPrefix, []);
|
|
197
388
|
break;
|
|
198
389
|
}
|
|
199
390
|
|
|
@@ -264,14 +455,7 @@ export async function handleAdminCommand({ command, args, text, sock, messageInf
|
|
|
264
455
|
break;
|
|
265
456
|
}
|
|
266
457
|
|
|
267
|
-
await
|
|
268
|
-
sock,
|
|
269
|
-
remoteJid,
|
|
270
|
-
{
|
|
271
|
-
text: getAdminUsageText('menuadm', { commandPrefix, variant: 'default' }),
|
|
272
|
-
},
|
|
273
|
-
{ quoted: messageInfo, ephemeralExpiration: expirationMessage },
|
|
274
|
-
);
|
|
458
|
+
await handleMenuAdmCommand(sock, remoteJid, messageInfo, expirationMessage, commandPrefix, args);
|
|
275
459
|
break;
|
|
276
460
|
}
|
|
277
461
|
|
|
@@ -582,7 +766,7 @@ export async function handleAdminCommand({ command, args, text, sock, messageInf
|
|
|
582
766
|
minutes = clampStickerFocusChatWindowMinutes(parsed, DEFAULT_STICKER_FOCUS_CHAT_WINDOW_MINUTES);
|
|
583
767
|
}
|
|
584
768
|
|
|
585
|
-
const untilMs =
|
|
769
|
+
const untilMs = __timeNowMs() + minutes * 60 * 1000;
|
|
586
770
|
await groupConfigStore.updateGroupConfig(remoteJid, {
|
|
587
771
|
stickerFocusChatWindowUntilMs: untilMs,
|
|
588
772
|
});
|
|
@@ -626,7 +810,6 @@ export async function handleAdminCommand({ command, args, text, sock, messageInf
|
|
|
626
810
|
if (['reset', 'default', 'padrao', 'padrão'].includes(normalized)) {
|
|
627
811
|
await groupConfigStore.updateGroupConfig(remoteJid, {
|
|
628
812
|
stickerFocusMessageCooldownMinutes: DEFAULT_STICKER_FOCUS_MESSAGE_COOLDOWN_MINUTES,
|
|
629
|
-
// compatibilidade com configuração antiga
|
|
630
813
|
stickerFocusTextCooldownMinutes: DEFAULT_STICKER_FOCUS_MESSAGE_COOLDOWN_MINUTES,
|
|
631
814
|
});
|
|
632
815
|
await sendAndStore(
|
|
@@ -668,7 +851,6 @@ export async function handleAdminCommand({ command, args, text, sock, messageInf
|
|
|
668
851
|
const minutes = clampStickerFocusMessageCooldownMinutes(parsed, DEFAULT_STICKER_FOCUS_MESSAGE_COOLDOWN_MINUTES);
|
|
669
852
|
await groupConfigStore.updateGroupConfig(remoteJid, {
|
|
670
853
|
stickerFocusMessageCooldownMinutes: minutes,
|
|
671
|
-
// compatibilidade com configuração antiga
|
|
672
854
|
stickerFocusTextCooldownMinutes: minutes,
|
|
673
855
|
});
|
|
674
856
|
|
|
@@ -683,6 +865,93 @@ export async function handleAdminCommand({ command, args, text, sock, messageInf
|
|
|
683
865
|
break;
|
|
684
866
|
}
|
|
685
867
|
|
|
868
|
+
case 'stickerallowance': {
|
|
869
|
+
if (!isGroupMessage) {
|
|
870
|
+
await sendAndStore(sock, remoteJid, { text: GROUP_ONLY_COMMAND_MESSAGE }, { quoted: messageInfo, ephemeralExpiration: expirationMessage });
|
|
871
|
+
break;
|
|
872
|
+
}
|
|
873
|
+
if (!(await isUserAdmin(remoteJid, senderIdentity))) {
|
|
874
|
+
await sendAndStore(sock, remoteJid, { text: NO_PERMISSION_COMMAND_MESSAGE }, { quoted: messageInfo, ephemeralExpiration: expirationMessage });
|
|
875
|
+
break;
|
|
876
|
+
}
|
|
877
|
+
|
|
878
|
+
const rawValue = args[0];
|
|
879
|
+
const normalized = String(rawValue || '')
|
|
880
|
+
.trim()
|
|
881
|
+
.toLowerCase();
|
|
882
|
+
|
|
883
|
+
if (!normalized || normalized === 'status') {
|
|
884
|
+
const config = await groupConfigStore.getGroupConfig(remoteJid);
|
|
885
|
+
const state = resolveStickerFocusState(config);
|
|
886
|
+
await sendAndStore(
|
|
887
|
+
sock,
|
|
888
|
+
remoteJid,
|
|
889
|
+
{
|
|
890
|
+
text: `📏 Limite atual de mensagens por usuário no modo sticker: *${state.messageAllowanceCount}*.` + `\nRegra vigente: *${formatStickerFocusRule(state)}*.` + `\nUse *${commandPrefix}stickerallowance <quantidade>* para alterar.`,
|
|
891
|
+
},
|
|
892
|
+
{ quoted: messageInfo, ephemeralExpiration: expirationMessage },
|
|
893
|
+
);
|
|
894
|
+
break;
|
|
895
|
+
}
|
|
896
|
+
|
|
897
|
+
if (['reset', 'default', 'padrao', 'padrão'].includes(normalized)) {
|
|
898
|
+
await groupConfigStore.updateGroupConfig(remoteJid, {
|
|
899
|
+
stickerFocusMessageAllowance: DEFAULT_STICKER_FOCUS_MESSAGE_ALLOWANCE,
|
|
900
|
+
stickerFocusMessageAllowanceCount: DEFAULT_STICKER_FOCUS_MESSAGE_ALLOWANCE,
|
|
901
|
+
});
|
|
902
|
+
await sendAndStore(
|
|
903
|
+
sock,
|
|
904
|
+
remoteJid,
|
|
905
|
+
{
|
|
906
|
+
text: `✅ Limite restaurado para o padrão: *${DEFAULT_STICKER_FOCUS_MESSAGE_ALLOWANCE}* mensagem(ns) por janela.`,
|
|
907
|
+
},
|
|
908
|
+
{ quoted: messageInfo, ephemeralExpiration: expirationMessage },
|
|
909
|
+
);
|
|
910
|
+
break;
|
|
911
|
+
}
|
|
912
|
+
|
|
913
|
+
const parsed = parsePositiveInteger(rawValue);
|
|
914
|
+
if (!parsed) {
|
|
915
|
+
await sendAndStore(
|
|
916
|
+
sock,
|
|
917
|
+
remoteJid,
|
|
918
|
+
{
|
|
919
|
+
text: getAdminUsageText('stickerallowance', { commandPrefix }),
|
|
920
|
+
},
|
|
921
|
+
{ quoted: messageInfo, ephemeralExpiration: expirationMessage },
|
|
922
|
+
);
|
|
923
|
+
break;
|
|
924
|
+
}
|
|
925
|
+
|
|
926
|
+
if (parsed < MIN_STICKER_FOCUS_MESSAGE_ALLOWANCE || parsed > MAX_STICKER_FOCUS_MESSAGE_ALLOWANCE) {
|
|
927
|
+
await sendAndStore(
|
|
928
|
+
sock,
|
|
929
|
+
remoteJid,
|
|
930
|
+
{
|
|
931
|
+
text: `Informe uma quantidade entre ${MIN_STICKER_FOCUS_MESSAGE_ALLOWANCE} e ${MAX_STICKER_FOCUS_MESSAGE_ALLOWANCE}.`,
|
|
932
|
+
},
|
|
933
|
+
{ quoted: messageInfo, ephemeralExpiration: expirationMessage },
|
|
934
|
+
);
|
|
935
|
+
break;
|
|
936
|
+
}
|
|
937
|
+
|
|
938
|
+
const allowance = clampStickerFocusMessageAllowance(parsed, DEFAULT_STICKER_FOCUS_MESSAGE_ALLOWANCE);
|
|
939
|
+
await groupConfigStore.updateGroupConfig(remoteJid, {
|
|
940
|
+
stickerFocusMessageAllowance: allowance,
|
|
941
|
+
stickerFocusMessageAllowanceCount: allowance,
|
|
942
|
+
});
|
|
943
|
+
|
|
944
|
+
await sendAndStore(
|
|
945
|
+
sock,
|
|
946
|
+
remoteJid,
|
|
947
|
+
{
|
|
948
|
+
text: `✅ Limite de mensagens por usuário atualizado para *${allowance}* no modo sticker.`,
|
|
949
|
+
},
|
|
950
|
+
{ quoted: messageInfo, ephemeralExpiration: expirationMessage },
|
|
951
|
+
);
|
|
952
|
+
break;
|
|
953
|
+
}
|
|
954
|
+
|
|
686
955
|
case 'newgroup': {
|
|
687
956
|
if (args.length < 2) {
|
|
688
957
|
await sendAndStore(
|
|
@@ -765,7 +1034,33 @@ export async function handleAdminCommand({ command, args, text, sock, messageInf
|
|
|
765
1034
|
}
|
|
766
1035
|
try {
|
|
767
1036
|
await updateGroupParticipants(sock, remoteJid, participants, 'remove');
|
|
768
|
-
|
|
1037
|
+
|
|
1038
|
+
let deletedRecentMessages = 0;
|
|
1039
|
+
let failedRecentDeletes = 0;
|
|
1040
|
+
let requestedRecentDeletes = 0;
|
|
1041
|
+
for (const participant of participants) {
|
|
1042
|
+
const cleanupResult = await purgeRecentMessagesForSenderCandidates({
|
|
1043
|
+
sock,
|
|
1044
|
+
remoteJid,
|
|
1045
|
+
senderCandidates: [participant],
|
|
1046
|
+
});
|
|
1047
|
+
deletedRecentMessages += Number(cleanupResult?.deleted || 0);
|
|
1048
|
+
failedRecentDeletes += Number(cleanupResult?.failed || 0);
|
|
1049
|
+
requestedRecentDeletes += Number(cleanupResult?.requested || 0);
|
|
1050
|
+
}
|
|
1051
|
+
|
|
1052
|
+
const successText = deletedRecentMessages > 0 ? `Participantes removidos com sucesso.\n🧹 ${deletedRecentMessages} mensagem(ns) recentes apagadas.` : 'Participantes removidos com sucesso.';
|
|
1053
|
+
await sendAndStore(sock, remoteJid, { text: successText }, { quoted: messageInfo, ephemeralExpiration: expirationMessage });
|
|
1054
|
+
|
|
1055
|
+
logger.info('Comando ban executado com limpeza de mensagens recentes.', {
|
|
1056
|
+
action: 'admin_ban_with_recent_cleanup',
|
|
1057
|
+
groupId: remoteJid,
|
|
1058
|
+
participants,
|
|
1059
|
+
deletedRecentMessages,
|
|
1060
|
+
failedRecentDeletes,
|
|
1061
|
+
requestedRecentDeletes,
|
|
1062
|
+
});
|
|
1063
|
+
|
|
769
1064
|
const repliedTo = messageInfo.message?.extendedTextMessage?.contextInfo;
|
|
770
1065
|
if (repliedTo && containsParticipantJid(participants, repliedTo.participant)) {
|
|
771
1066
|
await sendAndStore(sock, remoteJid, {
|
|
@@ -778,6 +1073,329 @@ export async function handleAdminCommand({ command, args, text, sock, messageInf
|
|
|
778
1073
|
break;
|
|
779
1074
|
}
|
|
780
1075
|
|
|
1076
|
+
case 'warn': {
|
|
1077
|
+
if (!isGroupMessage) {
|
|
1078
|
+
await sendAndStore(sock, remoteJid, { text: GROUP_ONLY_COMMAND_MESSAGE }, { quoted: messageInfo, ephemeralExpiration: expirationMessage });
|
|
1079
|
+
break;
|
|
1080
|
+
}
|
|
1081
|
+
if (!(await isUserAdmin(remoteJid, senderIdentity))) {
|
|
1082
|
+
await sendAndStore(sock, remoteJid, { text: NO_PERMISSION_COMMAND_MESSAGE }, { quoted: messageInfo, ephemeralExpiration: expirationMessage });
|
|
1083
|
+
break;
|
|
1084
|
+
}
|
|
1085
|
+
|
|
1086
|
+
const { targetJid, remainingArgs, multipleTargets } = resolveSingleTargetFromMessage(messageInfo, args);
|
|
1087
|
+
if (multipleTargets || !targetJid) {
|
|
1088
|
+
await sendAndStore(
|
|
1089
|
+
sock,
|
|
1090
|
+
remoteJid,
|
|
1091
|
+
{
|
|
1092
|
+
text: getAdminUsageText('warn', { commandPrefix, variant: 'missing_targets' }),
|
|
1093
|
+
},
|
|
1094
|
+
{ quoted: messageInfo, ephemeralExpiration: expirationMessage },
|
|
1095
|
+
);
|
|
1096
|
+
break;
|
|
1097
|
+
}
|
|
1098
|
+
|
|
1099
|
+
const reason = normalizeWarnReason(remainingArgs.join(' '));
|
|
1100
|
+
const targetMention = formatUserMentionToken(targetJid);
|
|
1101
|
+
|
|
1102
|
+
try {
|
|
1103
|
+
await addGroupWarning({
|
|
1104
|
+
groupId: remoteJid,
|
|
1105
|
+
participantJid: targetJid,
|
|
1106
|
+
warnedByJid: senderJid,
|
|
1107
|
+
reason: reason || null,
|
|
1108
|
+
});
|
|
1109
|
+
|
|
1110
|
+
const totalWarnings = await countGroupWarnings({
|
|
1111
|
+
groupId: remoteJid,
|
|
1112
|
+
participantJid: targetJid,
|
|
1113
|
+
});
|
|
1114
|
+
|
|
1115
|
+
const groupConfig = await groupConfigStore.getGroupConfig(remoteJid);
|
|
1116
|
+
const warnAutoBanThreshold = clampWarnAutoBanThreshold(groupConfig?.warnAutoBanThreshold);
|
|
1117
|
+
const shouldAutoBan = totalWarnings >= warnAutoBanThreshold && !containsParticipantJid([targetJid], botJid);
|
|
1118
|
+
let autoBanSucceeded = false;
|
|
1119
|
+
let autoBanError = '';
|
|
1120
|
+
|
|
1121
|
+
if (shouldAutoBan) {
|
|
1122
|
+
try {
|
|
1123
|
+
await updateGroupParticipants(sock, remoteJid, [targetJid], 'remove');
|
|
1124
|
+
autoBanSucceeded = true;
|
|
1125
|
+
} catch (error) {
|
|
1126
|
+
autoBanError = error?.message || 'falha desconhecida';
|
|
1127
|
+
logger.warn('Falha ao aplicar auto-ban por limite de advertências.', {
|
|
1128
|
+
action: 'admin_warn_auto_ban_failed',
|
|
1129
|
+
groupId: remoteJid,
|
|
1130
|
+
targetJid,
|
|
1131
|
+
warnAutoBanThreshold,
|
|
1132
|
+
totalWarnings,
|
|
1133
|
+
error: autoBanError,
|
|
1134
|
+
});
|
|
1135
|
+
}
|
|
1136
|
+
}
|
|
1137
|
+
|
|
1138
|
+
const replyLines = [`⚠️ Advertência registrada para ${targetMention}.`, `Motivo: ${reason || 'não informado'}`, `Total de advertências neste grupo: *${totalWarnings}*.`, `Auto-ban configurado para: *${warnAutoBanThreshold}* advertência(s).`];
|
|
1139
|
+
if (autoBanSucceeded) {
|
|
1140
|
+
replyLines.push(`🚫 Limite atingido: ${targetMention} foi removido(a) automaticamente do grupo.`);
|
|
1141
|
+
} else if (autoBanError) {
|
|
1142
|
+
replyLines.push(`⚠️ Limite atingido, mas não consegui remover automaticamente: ${autoBanError}`);
|
|
1143
|
+
}
|
|
1144
|
+
|
|
1145
|
+
await sendAndStore(
|
|
1146
|
+
sock,
|
|
1147
|
+
remoteJid,
|
|
1148
|
+
{
|
|
1149
|
+
text: replyLines.join('\n'),
|
|
1150
|
+
mentions: [targetJid],
|
|
1151
|
+
},
|
|
1152
|
+
{ quoted: messageInfo, ephemeralExpiration: expirationMessage },
|
|
1153
|
+
);
|
|
1154
|
+
} catch (error) {
|
|
1155
|
+
logger.warn('Falha ao registrar advertência no grupo.', {
|
|
1156
|
+
action: 'admin_warn_failed',
|
|
1157
|
+
groupId: remoteJid,
|
|
1158
|
+
senderJid,
|
|
1159
|
+
targetJid,
|
|
1160
|
+
error: error?.message,
|
|
1161
|
+
});
|
|
1162
|
+
await sendAndStore(sock, remoteJid, { text: `Não foi possível registrar a advertência. Detalhes: ${error.message}` }, { quoted: messageInfo, ephemeralExpiration: expirationMessage });
|
|
1163
|
+
}
|
|
1164
|
+
break;
|
|
1165
|
+
}
|
|
1166
|
+
|
|
1167
|
+
case 'warnings': {
|
|
1168
|
+
if (!isGroupMessage) {
|
|
1169
|
+
await sendAndStore(sock, remoteJid, { text: GROUP_ONLY_COMMAND_MESSAGE }, { quoted: messageInfo, ephemeralExpiration: expirationMessage });
|
|
1170
|
+
break;
|
|
1171
|
+
}
|
|
1172
|
+
if (!(await isUserAdmin(remoteJid, senderIdentity))) {
|
|
1173
|
+
await sendAndStore(sock, remoteJid, { text: NO_PERMISSION_COMMAND_MESSAGE }, { quoted: messageInfo, ephemeralExpiration: expirationMessage });
|
|
1174
|
+
break;
|
|
1175
|
+
}
|
|
1176
|
+
|
|
1177
|
+
const { targetJid, multipleTargets } = resolveSingleTargetFromMessage(messageInfo, args);
|
|
1178
|
+
if (multipleTargets || !targetJid) {
|
|
1179
|
+
await sendAndStore(
|
|
1180
|
+
sock,
|
|
1181
|
+
remoteJid,
|
|
1182
|
+
{
|
|
1183
|
+
text: getAdminUsageText('warnings', { commandPrefix, variant: 'missing_targets' }),
|
|
1184
|
+
},
|
|
1185
|
+
{ quoted: messageInfo, ephemeralExpiration: expirationMessage },
|
|
1186
|
+
);
|
|
1187
|
+
break;
|
|
1188
|
+
}
|
|
1189
|
+
|
|
1190
|
+
const targetMention = formatUserMentionToken(targetJid);
|
|
1191
|
+
|
|
1192
|
+
try {
|
|
1193
|
+
const [totalWarnings, warningRows] = await Promise.all([
|
|
1194
|
+
countGroupWarnings({
|
|
1195
|
+
groupId: remoteJid,
|
|
1196
|
+
participantJid: targetJid,
|
|
1197
|
+
}),
|
|
1198
|
+
listGroupWarnings({
|
|
1199
|
+
groupId: remoteJid,
|
|
1200
|
+
participantJid: targetJid,
|
|
1201
|
+
limit: WARNINGS_LIST_PREVIEW_LIMIT,
|
|
1202
|
+
}),
|
|
1203
|
+
]);
|
|
1204
|
+
|
|
1205
|
+
if (totalWarnings <= 0) {
|
|
1206
|
+
await sendAndStore(
|
|
1207
|
+
sock,
|
|
1208
|
+
remoteJid,
|
|
1209
|
+
{
|
|
1210
|
+
text: `✅ ${targetMention} não possui advertências registradas neste grupo.`,
|
|
1211
|
+
mentions: [targetJid],
|
|
1212
|
+
},
|
|
1213
|
+
{ quoted: messageInfo, ephemeralExpiration: expirationMessage },
|
|
1214
|
+
);
|
|
1215
|
+
break;
|
|
1216
|
+
}
|
|
1217
|
+
|
|
1218
|
+
const historyLines = (warningRows || []).map((warningRow, index) => `${index + 1}. ${formatWarnTimestamp(warningRow?.createdAt)} - ${warningRow?.reason || 'Sem motivo informado'}`);
|
|
1219
|
+
if (totalWarnings > historyLines.length) {
|
|
1220
|
+
historyLines.push(`... e mais ${totalWarnings - historyLines.length} advertência(s).`);
|
|
1221
|
+
}
|
|
1222
|
+
|
|
1223
|
+
await sendAndStore(
|
|
1224
|
+
sock,
|
|
1225
|
+
remoteJid,
|
|
1226
|
+
{
|
|
1227
|
+
text: [`📋 Histórico de advertências de ${targetMention}`, `Total neste grupo: *${totalWarnings}*`, '', ...historyLines].join('\n'),
|
|
1228
|
+
mentions: [targetJid],
|
|
1229
|
+
},
|
|
1230
|
+
{ quoted: messageInfo, ephemeralExpiration: expirationMessage },
|
|
1231
|
+
);
|
|
1232
|
+
} catch (error) {
|
|
1233
|
+
logger.warn('Falha ao consultar histórico de advertências no grupo.', {
|
|
1234
|
+
action: 'admin_warnings_failed',
|
|
1235
|
+
groupId: remoteJid,
|
|
1236
|
+
senderJid,
|
|
1237
|
+
targetJid,
|
|
1238
|
+
error: error?.message,
|
|
1239
|
+
});
|
|
1240
|
+
await sendAndStore(sock, remoteJid, { text: `Não foi possível consultar as advertências. Detalhes: ${error.message}` }, { quoted: messageInfo, ephemeralExpiration: expirationMessage });
|
|
1241
|
+
}
|
|
1242
|
+
break;
|
|
1243
|
+
}
|
|
1244
|
+
|
|
1245
|
+
case 'clearwarn': {
|
|
1246
|
+
if (!isGroupMessage) {
|
|
1247
|
+
await sendAndStore(sock, remoteJid, { text: GROUP_ONLY_COMMAND_MESSAGE }, { quoted: messageInfo, ephemeralExpiration: expirationMessage });
|
|
1248
|
+
break;
|
|
1249
|
+
}
|
|
1250
|
+
if (!(await isUserAdmin(remoteJid, senderIdentity))) {
|
|
1251
|
+
await sendAndStore(sock, remoteJid, { text: NO_PERMISSION_COMMAND_MESSAGE }, { quoted: messageInfo, ephemeralExpiration: expirationMessage });
|
|
1252
|
+
break;
|
|
1253
|
+
}
|
|
1254
|
+
|
|
1255
|
+
const { targetJid, remainingArgs, multipleTargets } = resolveSingleTargetFromMessage(messageInfo, args);
|
|
1256
|
+
if (multipleTargets || !targetJid) {
|
|
1257
|
+
await sendAndStore(
|
|
1258
|
+
sock,
|
|
1259
|
+
remoteJid,
|
|
1260
|
+
{
|
|
1261
|
+
text: getAdminUsageText('clearwarn', { commandPrefix, variant: 'missing_targets' }),
|
|
1262
|
+
},
|
|
1263
|
+
{ quoted: messageInfo, ephemeralExpiration: expirationMessage },
|
|
1264
|
+
);
|
|
1265
|
+
break;
|
|
1266
|
+
}
|
|
1267
|
+
|
|
1268
|
+
const rawScope = String(remainingArgs?.[0] || '')
|
|
1269
|
+
.trim()
|
|
1270
|
+
.toLowerCase();
|
|
1271
|
+
const clearAll = rawScope === 'all';
|
|
1272
|
+
const amountToClear = !rawScope || clearAll ? 1 : parsePositiveInteger(rawScope);
|
|
1273
|
+
|
|
1274
|
+
if (!clearAll && rawScope && !amountToClear) {
|
|
1275
|
+
await sendAndStore(
|
|
1276
|
+
sock,
|
|
1277
|
+
remoteJid,
|
|
1278
|
+
{
|
|
1279
|
+
text: getAdminUsageText('clearwarn', { commandPrefix, variant: 'invalid_amount' }),
|
|
1280
|
+
},
|
|
1281
|
+
{ quoted: messageInfo, ephemeralExpiration: expirationMessage },
|
|
1282
|
+
);
|
|
1283
|
+
break;
|
|
1284
|
+
}
|
|
1285
|
+
|
|
1286
|
+
try {
|
|
1287
|
+
const clearResult = await clearGroupWarnings({
|
|
1288
|
+
groupId: remoteJid,
|
|
1289
|
+
participantJid: targetJid,
|
|
1290
|
+
clearAll,
|
|
1291
|
+
limit: amountToClear || 1,
|
|
1292
|
+
});
|
|
1293
|
+
|
|
1294
|
+
const targetMention = formatUserMentionToken(targetJid);
|
|
1295
|
+
if (clearResult.removedCount <= 0) {
|
|
1296
|
+
await sendAndStore(
|
|
1297
|
+
sock,
|
|
1298
|
+
remoteJid,
|
|
1299
|
+
{
|
|
1300
|
+
text: `ℹ️ ${targetMention} não possui advertências para remover neste grupo.`,
|
|
1301
|
+
mentions: [targetJid],
|
|
1302
|
+
},
|
|
1303
|
+
{ quoted: messageInfo, ephemeralExpiration: expirationMessage },
|
|
1304
|
+
);
|
|
1305
|
+
break;
|
|
1306
|
+
}
|
|
1307
|
+
|
|
1308
|
+
const removedLabel = clearAll ? `todas as advertências (${clearResult.removedCount})` : `${clearResult.removedCount} advertência(s)`;
|
|
1309
|
+
await sendAndStore(
|
|
1310
|
+
sock,
|
|
1311
|
+
remoteJid,
|
|
1312
|
+
{
|
|
1313
|
+
text: `🧹 Limpeza concluída para ${targetMention}: removi *${removedLabel}*.\nAdvertências restantes neste grupo: *${clearResult.remainingCount}*.`,
|
|
1314
|
+
mentions: [targetJid],
|
|
1315
|
+
},
|
|
1316
|
+
{ quoted: messageInfo, ephemeralExpiration: expirationMessage },
|
|
1317
|
+
);
|
|
1318
|
+
} catch (error) {
|
|
1319
|
+
logger.warn('Falha ao limpar advertências no grupo.', {
|
|
1320
|
+
action: 'admin_clearwarn_failed',
|
|
1321
|
+
groupId: remoteJid,
|
|
1322
|
+
senderJid,
|
|
1323
|
+
targetJid,
|
|
1324
|
+
clearAll,
|
|
1325
|
+
error: error?.message,
|
|
1326
|
+
});
|
|
1327
|
+
await sendAndStore(sock, remoteJid, { text: `Não foi possível limpar advertências. Detalhes: ${error.message}` }, { quoted: messageInfo, ephemeralExpiration: expirationMessage });
|
|
1328
|
+
}
|
|
1329
|
+
break;
|
|
1330
|
+
}
|
|
1331
|
+
|
|
1332
|
+
case 'warnlimit': {
|
|
1333
|
+
if (!isGroupMessage) {
|
|
1334
|
+
await sendAndStore(sock, remoteJid, { text: GROUP_ONLY_COMMAND_MESSAGE }, { quoted: messageInfo, ephemeralExpiration: expirationMessage });
|
|
1335
|
+
break;
|
|
1336
|
+
}
|
|
1337
|
+
if (!(await isUserAdmin(remoteJid, senderIdentity))) {
|
|
1338
|
+
await sendAndStore(sock, remoteJid, { text: NO_PERMISSION_COMMAND_MESSAGE }, { quoted: messageInfo, ephemeralExpiration: expirationMessage });
|
|
1339
|
+
break;
|
|
1340
|
+
}
|
|
1341
|
+
|
|
1342
|
+
const action = String(args?.[0] || '')
|
|
1343
|
+
.trim()
|
|
1344
|
+
.toLowerCase();
|
|
1345
|
+
const groupConfig = await groupConfigStore.getGroupConfig(remoteJid);
|
|
1346
|
+
const currentThreshold = clampWarnAutoBanThreshold(groupConfig?.warnAutoBanThreshold);
|
|
1347
|
+
|
|
1348
|
+
if (!action || action === 'status') {
|
|
1349
|
+
await sendAndStore(
|
|
1350
|
+
sock,
|
|
1351
|
+
remoteJid,
|
|
1352
|
+
{
|
|
1353
|
+
text: `⚖️ Limite atual de auto-ban por advertências: *${currentThreshold}*.\nUse ${commandPrefix}warnlimit <qtd> para atualizar ou ${commandPrefix}warnlimit reset para voltar ao padrão.`,
|
|
1354
|
+
},
|
|
1355
|
+
{ quoted: messageInfo, ephemeralExpiration: expirationMessage },
|
|
1356
|
+
);
|
|
1357
|
+
break;
|
|
1358
|
+
}
|
|
1359
|
+
|
|
1360
|
+
if (action === 'reset') {
|
|
1361
|
+
await groupConfigStore.updateGroupConfig(remoteJid, { warnAutoBanThreshold: null });
|
|
1362
|
+
await sendAndStore(
|
|
1363
|
+
sock,
|
|
1364
|
+
remoteJid,
|
|
1365
|
+
{
|
|
1366
|
+
text: `✅ Limite de auto-ban resetado para o padrão: *${DEFAULT_WARN_AUTO_BAN_THRESHOLD}* advertência(s).`,
|
|
1367
|
+
},
|
|
1368
|
+
{ quoted: messageInfo, ephemeralExpiration: expirationMessage },
|
|
1369
|
+
);
|
|
1370
|
+
break;
|
|
1371
|
+
}
|
|
1372
|
+
|
|
1373
|
+
const parsedThreshold = parsePositiveInteger(action);
|
|
1374
|
+
if (!parsedThreshold) {
|
|
1375
|
+
await sendAndStore(
|
|
1376
|
+
sock,
|
|
1377
|
+
remoteJid,
|
|
1378
|
+
{
|
|
1379
|
+
text: getAdminUsageText('warnlimit', { commandPrefix, variant: 'invalid_value' }),
|
|
1380
|
+
},
|
|
1381
|
+
{ quoted: messageInfo, ephemeralExpiration: expirationMessage },
|
|
1382
|
+
);
|
|
1383
|
+
break;
|
|
1384
|
+
}
|
|
1385
|
+
|
|
1386
|
+
const nextThreshold = Math.max(MIN_WARN_AUTO_BAN_THRESHOLD, Math.min(MAX_WARN_AUTO_BAN_THRESHOLD, parsedThreshold));
|
|
1387
|
+
await groupConfigStore.updateGroupConfig(remoteJid, { warnAutoBanThreshold: nextThreshold });
|
|
1388
|
+
await sendAndStore(
|
|
1389
|
+
sock,
|
|
1390
|
+
remoteJid,
|
|
1391
|
+
{
|
|
1392
|
+
text: `✅ Limite de auto-ban atualizado para *${nextThreshold}* advertência(s).`,
|
|
1393
|
+
},
|
|
1394
|
+
{ quoted: messageInfo, ephemeralExpiration: expirationMessage },
|
|
1395
|
+
);
|
|
1396
|
+
break;
|
|
1397
|
+
}
|
|
1398
|
+
|
|
781
1399
|
case 'up': {
|
|
782
1400
|
if (!isGroupMessage) {
|
|
783
1401
|
await sendAndStore(sock, remoteJid, { text: GROUP_ONLY_COMMAND_MESSAGE }, { quoted: messageInfo, ephemeralExpiration: expirationMessage });
|
|
@@ -1758,6 +2376,231 @@ ${JSON.stringify(response, null, 2)}`,
|
|
|
1758
2376
|
break;
|
|
1759
2377
|
}
|
|
1760
2378
|
|
|
2379
|
+
case 'noticiasfiltro': {
|
|
2380
|
+
if (!isGroupMessage) {
|
|
2381
|
+
await sendAndStore(sock, remoteJid, { text: GROUP_ONLY_COMMAND_MESSAGE }, { quoted: messageInfo, ephemeralExpiration: expirationMessage });
|
|
2382
|
+
break;
|
|
2383
|
+
}
|
|
2384
|
+
if (!(await isUserAdmin(remoteJid, senderIdentity))) {
|
|
2385
|
+
await sendAndStore(sock, remoteJid, { text: NO_PERMISSION_COMMAND_MESSAGE }, { quoted: messageInfo, ephemeralExpiration: expirationMessage });
|
|
2386
|
+
break;
|
|
2387
|
+
}
|
|
2388
|
+
|
|
2389
|
+
const groupConfig = await groupConfigStore.getGroupConfig(remoteJid);
|
|
2390
|
+
const currentFilters = normalizeNewsFilterState(groupConfig);
|
|
2391
|
+
const scope = String(args[0] || '')
|
|
2392
|
+
.trim()
|
|
2393
|
+
.toLowerCase();
|
|
2394
|
+
const scopeToKey = {
|
|
2395
|
+
source: 'sourceIds',
|
|
2396
|
+
franchise: 'franchiseSlugs',
|
|
2397
|
+
tag: 'entitySlugs',
|
|
2398
|
+
entity: 'entitySlugs',
|
|
2399
|
+
};
|
|
2400
|
+
|
|
2401
|
+
const buildStatusText = (filters) => ['🧪 *Filtros de notícias deste grupo*', '', `Sources: ${formatListForMessage(filters.sourceIds)}`, `Franchises: ${formatListForMessage(filters.franchiseSlugs)}`, `Tags/Entities: ${formatListForMessage(filters.entitySlugs)}`, `Somente em alta: *${filters.onlyTrending ? 'sim' : 'não'}*`, '', 'Exemplos:', `${commandPrefix}noticiasfiltro source add ann,mal`, `${commandPrefix}noticiasfiltro franchise add one-piece`, `${commandPrefix}noticiasfiltro tag add shounen`, `${commandPrefix}noticiasfiltro trending on`].join('\n');
|
|
2402
|
+
|
|
2403
|
+
if (!scope || scope === 'status' || scope === 'list') {
|
|
2404
|
+
await sendAndStore(
|
|
2405
|
+
sock,
|
|
2406
|
+
remoteJid,
|
|
2407
|
+
{
|
|
2408
|
+
text: buildStatusText(currentFilters),
|
|
2409
|
+
},
|
|
2410
|
+
{ quoted: messageInfo, ephemeralExpiration: expirationMessage },
|
|
2411
|
+
);
|
|
2412
|
+
break;
|
|
2413
|
+
}
|
|
2414
|
+
|
|
2415
|
+
if (scope === 'reset') {
|
|
2416
|
+
const resetFilters = {
|
|
2417
|
+
sourceIds: [],
|
|
2418
|
+
franchiseSlugs: [],
|
|
2419
|
+
entitySlugs: [],
|
|
2420
|
+
onlyTrending: false,
|
|
2421
|
+
};
|
|
2422
|
+
await groupConfigStore.updateGroupConfig(remoteJid, buildNewsFilterConfigPatch(groupConfig, resetFilters));
|
|
2423
|
+
await sendAndStore(
|
|
2424
|
+
sock,
|
|
2425
|
+
remoteJid,
|
|
2426
|
+
{
|
|
2427
|
+
text: '✅ Filtros de notícias resetados com sucesso.',
|
|
2428
|
+
},
|
|
2429
|
+
{ quoted: messageInfo, ephemeralExpiration: expirationMessage },
|
|
2430
|
+
);
|
|
2431
|
+
break;
|
|
2432
|
+
}
|
|
2433
|
+
|
|
2434
|
+
if (scope === 'trending') {
|
|
2435
|
+
const action = String(args[1] || '')
|
|
2436
|
+
.trim()
|
|
2437
|
+
.toLowerCase();
|
|
2438
|
+
if (!action || !['on', 'off', 'status'].includes(action)) {
|
|
2439
|
+
await sendAndStore(
|
|
2440
|
+
sock,
|
|
2441
|
+
remoteJid,
|
|
2442
|
+
{
|
|
2443
|
+
text: getAdminUsageText('noticiasfiltro', { commandPrefix, variant: 'trending' }),
|
|
2444
|
+
},
|
|
2445
|
+
{ quoted: messageInfo, ephemeralExpiration: expirationMessage },
|
|
2446
|
+
);
|
|
2447
|
+
break;
|
|
2448
|
+
}
|
|
2449
|
+
|
|
2450
|
+
if (action === 'status') {
|
|
2451
|
+
await sendAndStore(
|
|
2452
|
+
sock,
|
|
2453
|
+
remoteJid,
|
|
2454
|
+
{
|
|
2455
|
+
text: `📈 Filtro "somente em alta" está *${currentFilters.onlyTrending ? 'ativado' : 'desativado'}*.`,
|
|
2456
|
+
},
|
|
2457
|
+
{ quoted: messageInfo, ephemeralExpiration: expirationMessage },
|
|
2458
|
+
);
|
|
2459
|
+
break;
|
|
2460
|
+
}
|
|
2461
|
+
|
|
2462
|
+
const nextFilters = {
|
|
2463
|
+
...currentFilters,
|
|
2464
|
+
onlyTrending: action === 'on',
|
|
2465
|
+
};
|
|
2466
|
+
await groupConfigStore.updateGroupConfig(remoteJid, buildNewsFilterConfigPatch(groupConfig, nextFilters));
|
|
2467
|
+
await sendAndStore(
|
|
2468
|
+
sock,
|
|
2469
|
+
remoteJid,
|
|
2470
|
+
{
|
|
2471
|
+
text: `✅ Filtro "somente em alta" ${nextFilters.onlyTrending ? 'ativado' : 'desativado'} com sucesso.`,
|
|
2472
|
+
},
|
|
2473
|
+
{ quoted: messageInfo, ephemeralExpiration: expirationMessage },
|
|
2474
|
+
);
|
|
2475
|
+
break;
|
|
2476
|
+
}
|
|
2477
|
+
|
|
2478
|
+
const filterKey = scopeToKey[scope];
|
|
2479
|
+
if (!filterKey) {
|
|
2480
|
+
await sendAndStore(
|
|
2481
|
+
sock,
|
|
2482
|
+
remoteJid,
|
|
2483
|
+
{
|
|
2484
|
+
text: getAdminUsageText('noticiasfiltro', { commandPrefix, variant: 'invalid_scope' }),
|
|
2485
|
+
},
|
|
2486
|
+
{ quoted: messageInfo, ephemeralExpiration: expirationMessage },
|
|
2487
|
+
);
|
|
2488
|
+
break;
|
|
2489
|
+
}
|
|
2490
|
+
|
|
2491
|
+
const action = String(args[1] || '')
|
|
2492
|
+
.trim()
|
|
2493
|
+
.toLowerCase();
|
|
2494
|
+
if (!action || !['add', 'remove', 'list', 'clear'].includes(action)) {
|
|
2495
|
+
await sendAndStore(
|
|
2496
|
+
sock,
|
|
2497
|
+
remoteJid,
|
|
2498
|
+
{
|
|
2499
|
+
text: getAdminUsageText('noticiasfiltro', { commandPrefix, variant: 'invalid_action' }),
|
|
2500
|
+
},
|
|
2501
|
+
{ quoted: messageInfo, ephemeralExpiration: expirationMessage },
|
|
2502
|
+
);
|
|
2503
|
+
break;
|
|
2504
|
+
}
|
|
2505
|
+
|
|
2506
|
+
const scopeLabel = filterKey === 'sourceIds' ? 'sources' : filterKey === 'franchiseSlugs' ? 'franchises' : 'tags/entities';
|
|
2507
|
+
|
|
2508
|
+
if (action === 'list') {
|
|
2509
|
+
await sendAndStore(
|
|
2510
|
+
sock,
|
|
2511
|
+
remoteJid,
|
|
2512
|
+
{
|
|
2513
|
+
text: `📋 Filtro de notícias (${scopeLabel}): ${formatListForMessage(currentFilters[filterKey])}`,
|
|
2514
|
+
},
|
|
2515
|
+
{ quoted: messageInfo, ephemeralExpiration: expirationMessage },
|
|
2516
|
+
);
|
|
2517
|
+
break;
|
|
2518
|
+
}
|
|
2519
|
+
|
|
2520
|
+
if (action === 'clear') {
|
|
2521
|
+
const nextFilters = {
|
|
2522
|
+
...currentFilters,
|
|
2523
|
+
[filterKey]: [],
|
|
2524
|
+
};
|
|
2525
|
+
await groupConfigStore.updateGroupConfig(remoteJid, buildNewsFilterConfigPatch(groupConfig, nextFilters));
|
|
2526
|
+
await sendAndStore(
|
|
2527
|
+
sock,
|
|
2528
|
+
remoteJid,
|
|
2529
|
+
{
|
|
2530
|
+
text: `✅ Filtro de notícias (${scopeLabel}) limpo com sucesso.`,
|
|
2531
|
+
},
|
|
2532
|
+
{ quoted: messageInfo, ephemeralExpiration: expirationMessage },
|
|
2533
|
+
);
|
|
2534
|
+
break;
|
|
2535
|
+
}
|
|
2536
|
+
|
|
2537
|
+
const values = parseFilterValues(args.slice(2));
|
|
2538
|
+
if (values.length === 0) {
|
|
2539
|
+
await sendAndStore(
|
|
2540
|
+
sock,
|
|
2541
|
+
remoteJid,
|
|
2542
|
+
{
|
|
2543
|
+
text: getAdminUsageText('noticiasfiltro', { commandPrefix, variant: 'missing_values' }),
|
|
2544
|
+
},
|
|
2545
|
+
{ quoted: messageInfo, ephemeralExpiration: expirationMessage },
|
|
2546
|
+
);
|
|
2547
|
+
break;
|
|
2548
|
+
}
|
|
2549
|
+
|
|
2550
|
+
const currentSet = new Set(currentFilters[filterKey]);
|
|
2551
|
+
if (action === 'add') {
|
|
2552
|
+
values.forEach((value) => currentSet.add(value));
|
|
2553
|
+
} else {
|
|
2554
|
+
values.forEach((value) => currentSet.delete(value));
|
|
2555
|
+
}
|
|
2556
|
+
|
|
2557
|
+
const nextFilters = {
|
|
2558
|
+
...currentFilters,
|
|
2559
|
+
[filterKey]: Array.from(currentSet).sort((left, right) => left.localeCompare(right)),
|
|
2560
|
+
};
|
|
2561
|
+
await groupConfigStore.updateGroupConfig(remoteJid, buildNewsFilterConfigPatch(groupConfig, nextFilters));
|
|
2562
|
+
await sendAndStore(
|
|
2563
|
+
sock,
|
|
2564
|
+
remoteJid,
|
|
2565
|
+
{
|
|
2566
|
+
text: `✅ Filtro de notícias (${scopeLabel}) atualizado: ${formatListForMessage(nextFilters[filterKey])}`,
|
|
2567
|
+
},
|
|
2568
|
+
{ quoted: messageInfo, ephemeralExpiration: expirationMessage },
|
|
2569
|
+
);
|
|
2570
|
+
break;
|
|
2571
|
+
}
|
|
2572
|
+
|
|
2573
|
+
case 'grupoaudit': {
|
|
2574
|
+
if (!isGroupMessage) {
|
|
2575
|
+
await sendAndStore(sock, remoteJid, { text: GROUP_ONLY_COMMAND_MESSAGE }, { quoted: messageInfo, ephemeralExpiration: expirationMessage });
|
|
2576
|
+
break;
|
|
2577
|
+
}
|
|
2578
|
+
if (!(await isUserAdmin(remoteJid, senderIdentity))) {
|
|
2579
|
+
await sendAndStore(sock, remoteJid, { text: NO_PERMISSION_COMMAND_MESSAGE }, { quoted: messageInfo, ephemeralExpiration: expirationMessage });
|
|
2580
|
+
break;
|
|
2581
|
+
}
|
|
2582
|
+
|
|
2583
|
+
const groupConfig = await groupConfigStore.getGroupConfig(remoteJid);
|
|
2584
|
+
const stickerState = resolveStickerFocusState(groupConfig);
|
|
2585
|
+
const newsStatus = await getNewsStatusForGroup(remoteJid);
|
|
2586
|
+
const auditText = buildGroupAuditText({
|
|
2587
|
+
config: groupConfig,
|
|
2588
|
+
stickerState,
|
|
2589
|
+
newsStatus,
|
|
2590
|
+
commandPrefix,
|
|
2591
|
+
});
|
|
2592
|
+
|
|
2593
|
+
await sendAndStore(
|
|
2594
|
+
sock,
|
|
2595
|
+
remoteJid,
|
|
2596
|
+
{
|
|
2597
|
+
text: auditText,
|
|
2598
|
+
},
|
|
2599
|
+
{ quoted: messageInfo, ephemeralExpiration: expirationMessage },
|
|
2600
|
+
);
|
|
2601
|
+
break;
|
|
2602
|
+
}
|
|
2603
|
+
|
|
1761
2604
|
case 'noticias': {
|
|
1762
2605
|
if (!isGroupMessage) {
|
|
1763
2606
|
await sendAndStore(sock, remoteJid, { text: GROUP_ONLY_COMMAND_MESSAGE }, { quoted: messageInfo, ephemeralExpiration: expirationMessage });
|