@kaikybrofc/omnizap-system 2.3.1 → 2.3.3
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/README.md +82 -483
- package/app/controllers/messageController.js +473 -255
- package/app/modules/analyticsModule/messageAnalysisEventRepository.js +83 -0
- package/app/modules/stickerModule/stickerCommand.js +7 -2
- package/app/modules/stickerModule/stickerTextCommand.js +7 -2
- package/app/modules/stickerPackModule/stickerDomainEventConsumerRuntime.js +1 -3
- package/app/modules/stickerPackModule/stickerPackCommandHandlers.js +224 -53
- package/app/observability/metrics.js +6 -3
- package/app/services/googleWebLinkService.js +77 -0
- package/app/services/lidMapService.js +83 -4
- package/database/index.js +2 -0
- package/database/migrations/20260301_0028_message_analysis_event.sql +32 -0
- package/database/migrations/20260301_0029_admin_action_audit.sql +16 -0
- package/package.json +1 -1
- package/public/index.html +12 -8
- package/public/js/apps/createPackApp.js +4 -4
- package/public/js/apps/homeApp.js +78 -34
- package/public/js/apps/loginApp.js +245 -35
- package/public/js/apps/stickersAdminApp.js +4 -10
- package/public/js/apps/stickersApp.js +1 -1
- package/public/js/apps/userApp.js +956 -55
- package/public/js/apps/userProfileApp.js +244 -0
- package/public/login/index.html +437 -101
- package/public/termos-de-uso/index.html +1 -1
- package/public/user/index.html +2 -181
- package/public/user/systemadm/index.html +774 -0
- package/server/controllers/stickerCatalog/nonCatalogHandlers.js +183 -0
- package/server/controllers/stickerCatalogController.js +1289 -368
- package/server/controllers/systemAdminController.js +141 -0
- package/server/controllers/userController.js +87 -0
- package/server/http/httpServer.js +72 -32
- package/server/middleware/cachePolicy.js +24 -0
- package/server/middleware/cachePolicyHelpers.js +1 -0
- package/server/middleware/rateLimit.js +89 -0
- package/server/middleware/requestLogger.js +16 -0
- package/server/middleware/requireAdminAuth.js +42 -0
- package/server/middleware/securityHeaders.js +6 -0
- package/server/routes/admin/systemAdminRouter.js +56 -0
- package/server/routes/health/healthRouter.js +41 -0
- package/server/routes/indexRouter.js +197 -0
- package/server/routes/metrics/metricsRouter.js +13 -0
- package/server/routes/stickerCatalog/catalogHandlers/catalogAdminHttp.js +44 -0
- package/server/routes/stickerCatalog/stickerApiRouter.js +84 -0
- package/server/routes/stickerCatalog/stickerDataRouter.js +140 -0
- package/server/routes/stickerCatalog/stickerSiteRouter.js +43 -0
- package/server/routes/user/userRouter.js +56 -0
- package/server/utils/safePath.js +26 -0
- package/server/routes/metricsRoute.js +0 -7
- package/server/routes/stickerCatalogRoute.js +0 -20
|
@@ -9,7 +9,7 @@ import { handleRankingCommand } from '../modules/statsModule/rankingCommand.js';
|
|
|
9
9
|
import { handleGlobalRankingCommand } from '../modules/statsModule/globalRankingCommand.js';
|
|
10
10
|
import { handleNoMessageCommand } from '../modules/statsModule/noMessageCommand.js';
|
|
11
11
|
import { handlePingCommand } from '../modules/systemMetricsModule/pingCommand.js';
|
|
12
|
-
import { extractMessageContent, getExpiration, getJidServer, getJidUser, isGroupJid, isSameJidUser, normalizeJid, resolveBotJid } from '../config/baileysConfig.js';
|
|
12
|
+
import { detectAllMediaTypes, extractMessageContent, getExpiration, getJidServer, getJidUser, isGroupJid, isSameJidUser, normalizeJid, resolveBotJid } from '../config/baileysConfig.js';
|
|
13
13
|
import logger from '../utils/logger/loggerModule.js';
|
|
14
14
|
import { handleAntiLink } from '../utils/antiLink/antiLinkModule.js';
|
|
15
15
|
import { handleCatCommand, handleCatImageCommand, handleCatPromptCommand } from '../modules/aiModule/catCommand.js';
|
|
@@ -27,6 +27,8 @@ import { sendAndStore } from '../services/messagePersistenceService.js';
|
|
|
27
27
|
import { resolveCaptchaByMessage } from '../services/captchaService.js';
|
|
28
28
|
import { extractSenderInfoFromMessage, resolveUserId } from '../services/lidMapService.js';
|
|
29
29
|
import { buildWhatsAppGoogleLoginUrl } from '../services/whatsappLoginLinkService.js';
|
|
30
|
+
import { isWhatsAppUserLinkedToGoogleWebAccount } from '../services/googleWebLinkService.js';
|
|
31
|
+
import { createMessageAnalysisEvent } from '../modules/analyticsModule/messageAnalysisEventRepository.js';
|
|
30
32
|
|
|
31
33
|
const DEFAULT_COMMAND_PREFIX = process.env.COMMAND_PREFIX || '/';
|
|
32
34
|
const COMMAND_REACT_EMOJI = process.env.COMMAND_REACT_EMOJI || '🤖';
|
|
@@ -36,6 +38,29 @@ const START_LOGIN_TRIGGER =
|
|
|
36
38
|
.toLowerCase() || 'iniciar';
|
|
37
39
|
const WHATSAPP_USER_SERVERS = new Set(['s.whatsapp.net', 'c.us', 'hosted']);
|
|
38
40
|
const WHATSAPP_LID_SERVERS = new Set(['lid', 'hosted.lid']);
|
|
41
|
+
const parseEnvBool = (value, fallback) => {
|
|
42
|
+
if (value === undefined || value === null || value === '') return fallback;
|
|
43
|
+
const normalized = String(value).trim().toLowerCase();
|
|
44
|
+
if (['1', 'true', 'yes', 'y', 'on'].includes(normalized)) return true;
|
|
45
|
+
if (['0', 'false', 'no', 'n', 'off'].includes(normalized)) return false;
|
|
46
|
+
return fallback;
|
|
47
|
+
};
|
|
48
|
+
const MESSAGE_ANALYTICS_ENABLED = parseEnvBool(process.env.MESSAGE_ANALYTICS_ENABLED, true);
|
|
49
|
+
const MESSAGE_ANALYTICS_SOURCE =
|
|
50
|
+
String(process.env.MESSAGE_ANALYTICS_SOURCE || 'whatsapp')
|
|
51
|
+
.trim()
|
|
52
|
+
.slice(0, 32) || 'whatsapp';
|
|
53
|
+
const WHATSAPP_COMMAND_REQUIRES_GOOGLE_LOGIN = parseEnvBool(process.env.WHATSAPP_COMMAND_REQUIRES_GOOGLE_LOGIN, true);
|
|
54
|
+
const SITE_ORIGIN =
|
|
55
|
+
String(process.env.SITE_ORIGIN || process.env.WHATSAPP_LOGIN_BASE_URL || 'https://omnizap.shop')
|
|
56
|
+
.trim()
|
|
57
|
+
.replace(/\/+$/, '') || 'https://omnizap.shop';
|
|
58
|
+
const SITE_LOGIN_URL = `${SITE_ORIGIN}/login/`;
|
|
59
|
+
const SITE_GROUP_LOGIN_URL = `${SITE_ORIGIN}/login`;
|
|
60
|
+
|
|
61
|
+
const KNOWN_MESSAGE_COMMANDS = new Set(['menu', 'sticker', 's', 'pack', 'packs', 'toimg', 'tovideo', 'tovid', 'play', 'playvid', 'tiktok', 'tt', 'cat', 'catimg', 'catimage', 'catprompt', 'iaprompt', 'promptia', 'quote', 'qc', 'wp', 'waifupics', 'wpnsfw', 'waifupicsnsfw', 'wppicshelp', 'stickertext', 'st', 'stickertextwhite', 'stw', 'stickertextblink', 'stb', 'ranking', 'rank', 'top5', 'rankingglobal', 'rankglobal', 'globalrank', 'globalranking', 'semmsg', 'zeromsg', 'nomsg', 'inativos', 'ping', 'dado', 'dice', 'user', 'usuario', 'rpg', 'aviso', 'notice']);
|
|
62
|
+
|
|
63
|
+
let messageAnalyticsTableMissingLogged = false;
|
|
39
64
|
|
|
40
65
|
const normalizeTriggerText = (value) =>
|
|
41
66
|
String(value || '')
|
|
@@ -62,6 +87,24 @@ const resolveCanonicalWhatsAppJid = (...candidates) => {
|
|
|
62
87
|
return '';
|
|
63
88
|
};
|
|
64
89
|
|
|
90
|
+
const resolveCanonicalSenderJidFromMessage = async ({ messageInfo, senderJid }) => {
|
|
91
|
+
const key = messageInfo?.key || {};
|
|
92
|
+
const senderInfo = extractSenderInfoFromMessage(messageInfo);
|
|
93
|
+
let canonicalUserId = resolveCanonicalWhatsAppJid(senderInfo?.jid, senderInfo?.lid, senderInfo?.participantAlt, key.participantAlt, key.participant, key.remoteJid, senderJid);
|
|
94
|
+
|
|
95
|
+
try {
|
|
96
|
+
const resolvedUserId = await resolveUserId(senderInfo);
|
|
97
|
+
canonicalUserId = resolveCanonicalWhatsAppJid(resolvedUserId, canonicalUserId, senderInfo?.jid, senderInfo?.lid);
|
|
98
|
+
} catch (error) {
|
|
99
|
+
logger.warn('Falha ao resolver ID canonico do remetente.', {
|
|
100
|
+
action: 'resolve_sender_canonical_id_failed',
|
|
101
|
+
error: error?.message,
|
|
102
|
+
});
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
return canonicalUserId;
|
|
106
|
+
};
|
|
107
|
+
|
|
65
108
|
const maybeHandleStartLoginMessage = async ({ sock, messageInfo, extractedText, senderName, senderJid, remoteJid, expirationMessage, isMessageFromBot, isGroupMessage }) => {
|
|
66
109
|
if (isMessageFromBot || !isStartLoginTrigger(extractedText)) return false;
|
|
67
110
|
|
|
@@ -79,15 +122,7 @@ const maybeHandleStartLoginMessage = async ({ sock, messageInfo, extractedText,
|
|
|
79
122
|
|
|
80
123
|
const key = messageInfo?.key || {};
|
|
81
124
|
const senderInfo = extractSenderInfoFromMessage(messageInfo);
|
|
82
|
-
|
|
83
|
-
try {
|
|
84
|
-
const resolvedUserId = await resolveUserId(senderInfo);
|
|
85
|
-
canonicalUserId = resolveCanonicalWhatsAppJid(resolvedUserId, canonicalUserId, senderInfo?.jid, senderInfo?.lid);
|
|
86
|
-
} catch (error) {
|
|
87
|
-
logger.warn('Falha ao resolver ID canonico para fluxo de login do WhatsApp.', {
|
|
88
|
-
error: error?.message,
|
|
89
|
-
});
|
|
90
|
-
}
|
|
125
|
+
const canonicalUserId = await resolveCanonicalSenderJidFromMessage({ messageInfo, senderJid });
|
|
91
126
|
|
|
92
127
|
const loginUrl = buildWhatsAppGoogleLoginUrl({ userId: canonicalUserId });
|
|
93
128
|
if (!loginUrl) {
|
|
@@ -155,6 +190,98 @@ const runCommand = (label, handler) => {
|
|
|
155
190
|
}
|
|
156
191
|
};
|
|
157
192
|
|
|
193
|
+
const normalizeMessageKind = (mediaEntries, extractedText) => {
|
|
194
|
+
if (Array.isArray(mediaEntries) && mediaEntries.length > 0) {
|
|
195
|
+
const primaryType =
|
|
196
|
+
String(mediaEntries[0]?.mediaType || '')
|
|
197
|
+
.trim()
|
|
198
|
+
.toLowerCase() || 'media';
|
|
199
|
+
return primaryType.slice(0, 48);
|
|
200
|
+
}
|
|
201
|
+
|
|
202
|
+
const safeText = String(extractedText || '').trim();
|
|
203
|
+
if (!safeText || safeText === 'Mensagem vazia') return 'empty';
|
|
204
|
+
if (safeText.startsWith('[') && safeText.endsWith(']')) {
|
|
205
|
+
return safeText.slice(1, -1).trim().toLowerCase().replace(/\s+/g, '_').slice(0, 48);
|
|
206
|
+
}
|
|
207
|
+
return 'text';
|
|
208
|
+
};
|
|
209
|
+
|
|
210
|
+
const normalizeAnalysisErrorCode = (error) =>
|
|
211
|
+
String(error?.code || error?.name || 'processing_error')
|
|
212
|
+
.trim()
|
|
213
|
+
.toLowerCase()
|
|
214
|
+
.replace(/[^a-z0-9_-]/g, '_')
|
|
215
|
+
.slice(0, 96) || 'processing_error';
|
|
216
|
+
|
|
217
|
+
const persistMessageAnalysisEvent = (payload) => {
|
|
218
|
+
if (!MESSAGE_ANALYTICS_ENABLED) return;
|
|
219
|
+
void createMessageAnalysisEvent(payload).catch((error) => {
|
|
220
|
+
if (error?.code === 'ER_NO_SUCH_TABLE') {
|
|
221
|
+
if (messageAnalyticsTableMissingLogged) return;
|
|
222
|
+
messageAnalyticsTableMissingLogged = true;
|
|
223
|
+
logger.warn('Tabela de analytics de mensagens ainda não existe. Execute a migracao 20260301_0028.', {
|
|
224
|
+
action: 'message_analysis_table_missing',
|
|
225
|
+
});
|
|
226
|
+
return;
|
|
227
|
+
}
|
|
228
|
+
|
|
229
|
+
logger.warn('Falha ao persistir analytics de mensagem.', {
|
|
230
|
+
action: 'message_analysis_insert_failed',
|
|
231
|
+
error: error?.message,
|
|
232
|
+
});
|
|
233
|
+
});
|
|
234
|
+
};
|
|
235
|
+
|
|
236
|
+
const buildSiteLoginUrlForUser = (canonicalUserId) => buildWhatsAppGoogleLoginUrl({ userId: canonicalUserId }) || SITE_LOGIN_URL;
|
|
237
|
+
|
|
238
|
+
const ensureUserHasGoogleWebLoginForCommand = async ({ sock, messageInfo, senderJid, remoteJid, expirationMessage, commandPrefix }) => {
|
|
239
|
+
const isGroupMessage = isGroupJid(remoteJid);
|
|
240
|
+
const canonicalUserId = await resolveCanonicalSenderJidFromMessage({ messageInfo, senderJid });
|
|
241
|
+
let linked = false;
|
|
242
|
+
try {
|
|
243
|
+
linked = await isWhatsAppUserLinkedToGoogleWebAccount({
|
|
244
|
+
ownerJid: canonicalUserId || senderJid,
|
|
245
|
+
});
|
|
246
|
+
} catch (error) {
|
|
247
|
+
logger.warn('Falha ao validar vínculo Google Web para comando do WhatsApp. Comando liberado por fallback.', {
|
|
248
|
+
action: 'whatsapp_command_google_link_check_failed',
|
|
249
|
+
error: error?.message,
|
|
250
|
+
});
|
|
251
|
+
return {
|
|
252
|
+
allowed: true,
|
|
253
|
+
canonicalUserId,
|
|
254
|
+
loginUrl: '',
|
|
255
|
+
};
|
|
256
|
+
}
|
|
257
|
+
|
|
258
|
+
if (linked) {
|
|
259
|
+
return {
|
|
260
|
+
allowed: true,
|
|
261
|
+
canonicalUserId,
|
|
262
|
+
loginUrl: '',
|
|
263
|
+
};
|
|
264
|
+
}
|
|
265
|
+
|
|
266
|
+
const loginUrl = isGroupMessage ? SITE_GROUP_LOGIN_URL : buildSiteLoginUrlForUser(canonicalUserId || senderJid);
|
|
267
|
+
const loginMessage = isGroupMessage ? `Para usar os comandos do bot, você precisa estar logado no site com sua conta Google.\n\nAcesse:\n${loginUrl}` : `Para usar os comandos do bot, você precisa estar logado no site com sua conta Google.\n\nCadastre-se / faça login em:\n${loginUrl}\n\nDepois volte aqui e envie o comando novamente (ex.: ${commandPrefix}menu).`;
|
|
268
|
+
|
|
269
|
+
await sendAndStore(
|
|
270
|
+
sock,
|
|
271
|
+
remoteJid,
|
|
272
|
+
{
|
|
273
|
+
text: loginMessage,
|
|
274
|
+
},
|
|
275
|
+
{ quoted: messageInfo, ephemeralExpiration: expirationMessage },
|
|
276
|
+
);
|
|
277
|
+
|
|
278
|
+
return {
|
|
279
|
+
allowed: false,
|
|
280
|
+
canonicalUserId,
|
|
281
|
+
loginUrl,
|
|
282
|
+
};
|
|
283
|
+
};
|
|
284
|
+
|
|
158
285
|
/**
|
|
159
286
|
* Lida com atualizações do WhatsApp, sejam mensagens ou eventos genéricos.
|
|
160
287
|
*
|
|
@@ -180,236 +307,311 @@ export const handleMessages = async (update, sock) => {
|
|
|
180
307
|
const botJid = resolveBotJid(sock?.user?.id);
|
|
181
308
|
const isMessageFromBot = Boolean(messageInfo?.key?.fromMe) || (botJid ? isSameJidUser(senderJid, botJid) : false);
|
|
182
309
|
let commandPrefix = DEFAULT_COMMAND_PREFIX;
|
|
310
|
+
const mediaEntries = detectAllMediaTypes(messageInfo?.message, false);
|
|
311
|
+
const mediaTypes = mediaEntries
|
|
312
|
+
.map((entry) =>
|
|
313
|
+
String(entry?.mediaType || '')
|
|
314
|
+
.trim()
|
|
315
|
+
.toLowerCase(),
|
|
316
|
+
)
|
|
317
|
+
.filter(Boolean)
|
|
318
|
+
.slice(0, 10);
|
|
319
|
+
const analysisPayload = {
|
|
320
|
+
messageId: messageInfo?.key?.id || null,
|
|
321
|
+
chatId: remoteJid || null,
|
|
322
|
+
senderId: senderJid || null,
|
|
323
|
+
senderName,
|
|
324
|
+
upsertType: update?.type || null,
|
|
325
|
+
source: MESSAGE_ANALYTICS_SOURCE,
|
|
326
|
+
isGroup: isGroupMessage,
|
|
327
|
+
isFromBot: isMessageFromBot,
|
|
328
|
+
isCommand: false,
|
|
329
|
+
commandName: null,
|
|
330
|
+
commandArgsCount: 0,
|
|
331
|
+
commandKnown: null,
|
|
332
|
+
commandPrefix,
|
|
333
|
+
messageKind: normalizeMessageKind(mediaEntries, extractedText),
|
|
334
|
+
hasMedia: mediaEntries.length > 0,
|
|
335
|
+
mediaCount: mediaEntries.length,
|
|
336
|
+
textLength: String(extractedText || '').length,
|
|
337
|
+
processingResult: 'processed',
|
|
338
|
+
errorCode: null,
|
|
339
|
+
metadata: {
|
|
340
|
+
media_types: mediaTypes,
|
|
341
|
+
start_login_trigger: isStartLoginTrigger(extractedText),
|
|
342
|
+
},
|
|
343
|
+
};
|
|
344
|
+
|
|
345
|
+
try {
|
|
346
|
+
/**
|
|
347
|
+
* Executa validações de grupo.
|
|
348
|
+
* Aplica o Anti-Link e resolve o prefixo do grupo.
|
|
349
|
+
* Se a mensagem for bloqueada, interrompe o processamento.
|
|
350
|
+
*/
|
|
351
|
+
if (isGroupMessage) {
|
|
352
|
+
const shouldSkip = await handleAntiLink({ sock, messageInfo, extractedText, remoteJid, senderJid, botJid });
|
|
353
|
+
|
|
354
|
+
if (shouldSkip) {
|
|
355
|
+
analysisPayload.processingResult = 'blocked_antilink';
|
|
356
|
+
analysisPayload.metadata = {
|
|
357
|
+
...analysisPayload.metadata,
|
|
358
|
+
blocked_by: 'anti_link',
|
|
359
|
+
};
|
|
360
|
+
continue;
|
|
361
|
+
}
|
|
362
|
+
commandPrefix = await resolveCommandPrefix(true, remoteJid);
|
|
363
|
+
analysisPayload.commandPrefix = commandPrefix;
|
|
364
|
+
}
|
|
183
365
|
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
continue;
|
|
366
|
+
if (isGroupMessage && !isMessageFromBot) {
|
|
367
|
+
await resolveCaptchaByMessage({
|
|
368
|
+
groupId: remoteJid,
|
|
369
|
+
senderJid,
|
|
370
|
+
senderIdentity,
|
|
371
|
+
messageKey: messageInfo.key,
|
|
372
|
+
messageInfo,
|
|
373
|
+
extractedText,
|
|
374
|
+
});
|
|
194
375
|
}
|
|
195
|
-
commandPrefix = await resolveCommandPrefix(true, remoteJid);
|
|
196
|
-
}
|
|
197
376
|
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
groupId: remoteJid,
|
|
201
|
-
senderJid,
|
|
202
|
-
senderIdentity,
|
|
203
|
-
messageKey: messageInfo.key,
|
|
377
|
+
const handledStartLogin = await maybeHandleStartLoginMessage({
|
|
378
|
+
sock,
|
|
204
379
|
messageInfo,
|
|
205
380
|
extractedText,
|
|
381
|
+
senderName,
|
|
382
|
+
senderJid,
|
|
383
|
+
remoteJid,
|
|
384
|
+
expirationMessage,
|
|
385
|
+
isMessageFromBot,
|
|
386
|
+
isGroupMessage,
|
|
206
387
|
});
|
|
207
|
-
}
|
|
208
388
|
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
isMessageFromBot,
|
|
218
|
-
isGroupMessage,
|
|
219
|
-
});
|
|
220
|
-
|
|
221
|
-
if (handledStartLogin) {
|
|
222
|
-
continue;
|
|
223
|
-
}
|
|
389
|
+
if (handledStartLogin) {
|
|
390
|
+
analysisPayload.processingResult = 'handled_start_login';
|
|
391
|
+
analysisPayload.metadata = {
|
|
392
|
+
...analysisPayload.metadata,
|
|
393
|
+
flow: 'whatsapp_google_login',
|
|
394
|
+
};
|
|
395
|
+
continue;
|
|
396
|
+
}
|
|
224
397
|
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
|
|
398
|
+
/**
|
|
399
|
+
* Envia uma reação automática quando a mensagem começa com o prefixo de comando.
|
|
400
|
+
* A falha no envio da reação não interrompe o processamento do comando.
|
|
401
|
+
*/
|
|
402
|
+
const isCommandMessage = extractedText.startsWith(commandPrefix);
|
|
403
|
+
analysisPayload.isCommand = isCommandMessage;
|
|
404
|
+
analysisPayload.commandPrefix = commandPrefix;
|
|
405
|
+
|
|
406
|
+
if (isCommandMessage) {
|
|
407
|
+
const commandBody = extractedText.substring(commandPrefix.length);
|
|
408
|
+
const match = commandBody.match(/^(\S+)([\s\S]*)$/);
|
|
409
|
+
const command = match ? match[1].toLowerCase() : '';
|
|
410
|
+
const rawArgs = match && match[2] !== undefined ? match[2].trim() : '';
|
|
411
|
+
const args = rawArgs ? rawArgs.split(/\s+/) : [];
|
|
412
|
+
const text = match && match[2] !== undefined ? match[2] : '';
|
|
413
|
+
const isAdminCommandRoute = isAdminCommand(command);
|
|
414
|
+
|
|
415
|
+
analysisPayload.commandName = command || null;
|
|
416
|
+
analysisPayload.commandArgsCount = args.length;
|
|
417
|
+
analysisPayload.commandKnown = KNOWN_MESSAGE_COMMANDS.has(command) || isAdminCommandRoute;
|
|
418
|
+
|
|
419
|
+
if (!isMessageFromBot && WHATSAPP_COMMAND_REQUIRES_GOOGLE_LOGIN) {
|
|
420
|
+
const authCheck = await ensureUserHasGoogleWebLoginForCommand({
|
|
421
|
+
sock,
|
|
422
|
+
messageInfo,
|
|
423
|
+
senderJid,
|
|
424
|
+
remoteJid,
|
|
425
|
+
expirationMessage,
|
|
426
|
+
commandPrefix,
|
|
239
427
|
});
|
|
240
|
-
|
|
241
|
-
|
|
428
|
+
|
|
429
|
+
if (!authCheck.allowed) {
|
|
430
|
+
analysisPayload.processingResult = 'auth_required';
|
|
431
|
+
analysisPayload.metadata = {
|
|
432
|
+
...analysisPayload.metadata,
|
|
433
|
+
auth_required_for_command: command || null,
|
|
434
|
+
auth_login_url: authCheck.loginUrl || SITE_LOGIN_URL,
|
|
435
|
+
};
|
|
436
|
+
continue;
|
|
437
|
+
}
|
|
242
438
|
}
|
|
243
|
-
}
|
|
244
439
|
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
|
|
253
|
-
|
|
254
|
-
|
|
255
|
-
break;
|
|
256
|
-
|
|
257
|
-
case 'sticker':
|
|
258
|
-
case 's':
|
|
259
|
-
runCommand('sticker', () => processSticker(sock, messageInfo, senderJid, remoteJid, expirationMessage, senderName, args.join(' '), { commandPrefix }));
|
|
260
|
-
break;
|
|
261
|
-
|
|
262
|
-
case 'pack':
|
|
263
|
-
case 'packs':
|
|
264
|
-
runCommand('pack', () => handlePackCommand({ sock, remoteJid, messageInfo, expirationMessage, senderJid, senderName, text, commandPrefix }));
|
|
265
|
-
break;
|
|
266
|
-
|
|
267
|
-
case 'toimg':
|
|
268
|
-
case 'tovideo':
|
|
269
|
-
case 'tovid':
|
|
270
|
-
runCommand('toimg', () => handleStickerConvertCommand({ sock, remoteJid, messageInfo, expirationMessage, senderJid }));
|
|
271
|
-
break;
|
|
272
|
-
|
|
273
|
-
case 'play':
|
|
274
|
-
runCommand('play', () => handlePlayCommand(sock, remoteJid, messageInfo, expirationMessage, text, commandPrefix));
|
|
275
|
-
break;
|
|
276
|
-
|
|
277
|
-
case 'playvid':
|
|
278
|
-
runCommand('playvid', () => handlePlayVidCommand(sock, remoteJid, messageInfo, expirationMessage, text, commandPrefix));
|
|
279
|
-
break;
|
|
280
|
-
|
|
281
|
-
case 'tiktok':
|
|
282
|
-
case 'tt':
|
|
283
|
-
runCommand('tiktok', () => handleTikTokCommand({ sock, remoteJid, messageInfo, expirationMessage, text, commandPrefix }));
|
|
284
|
-
break;
|
|
285
|
-
|
|
286
|
-
case 'cat':
|
|
287
|
-
runCommand('cat', () => handleCatCommand({ sock, remoteJid, messageInfo, expirationMessage, senderJid, text, commandPrefix }));
|
|
288
|
-
break;
|
|
289
|
-
|
|
290
|
-
case 'catimg':
|
|
291
|
-
case 'catimage':
|
|
292
|
-
runCommand('catimg', () => handleCatImageCommand({ sock, remoteJid, messageInfo, expirationMessage, senderJid, text, commandPrefix }));
|
|
293
|
-
break;
|
|
294
|
-
|
|
295
|
-
case 'catprompt':
|
|
296
|
-
case 'iaprompt':
|
|
297
|
-
case 'promptia':
|
|
298
|
-
runCommand('catprompt', () => handleCatPromptCommand({ sock, remoteJid, messageInfo, expirationMessage, senderJid, text, commandPrefix }));
|
|
299
|
-
break;
|
|
300
|
-
|
|
301
|
-
case 'quote':
|
|
302
|
-
case 'qc':
|
|
303
|
-
runCommand('quote', () => handleQuoteCommand({ sock, remoteJid, messageInfo, expirationMessage, senderJid, senderName, text, commandPrefix }));
|
|
304
|
-
break;
|
|
305
|
-
|
|
306
|
-
case 'wp':
|
|
307
|
-
case 'waifupics':
|
|
308
|
-
runCommand('waifupics', () => handleWaifuPicsCommand({ sock, remoteJid, messageInfo, expirationMessage, text, type: 'sfw', commandPrefix }));
|
|
309
|
-
break;
|
|
310
|
-
|
|
311
|
-
case 'wpnsfw':
|
|
312
|
-
case 'waifupicsnsfw':
|
|
313
|
-
runCommand('waifupicsnsfw', () => handleWaifuPicsCommand({ sock, remoteJid, messageInfo, expirationMessage, text, type: 'nsfw', commandPrefix }));
|
|
314
|
-
break;
|
|
315
|
-
|
|
316
|
-
case 'wppicshelp':
|
|
317
|
-
runCommand('wppicshelp', () => sendAndStore(sock, remoteJid, { text: getWaifuPicsUsageText(commandPrefix) }, { quoted: messageInfo, ephemeralExpiration: expirationMessage }));
|
|
318
|
-
break;
|
|
319
|
-
|
|
320
|
-
case 'stickertext':
|
|
321
|
-
case 'st':
|
|
322
|
-
runCommand('stickertext', () => processTextSticker({ sock, messageInfo, remoteJid, senderJid, senderName, text, extraText: 'PackZoeira', expirationMessage, color: 'black', commandPrefix }));
|
|
323
|
-
break;
|
|
324
|
-
|
|
325
|
-
case 'stickertextwhite':
|
|
326
|
-
case 'stw':
|
|
327
|
-
runCommand('stickertextwhite', () => processTextSticker({ sock, messageInfo, remoteJid, senderJid, senderName, text, extraText: 'PackZoeira', expirationMessage, color: 'white', commandPrefix }));
|
|
328
|
-
break;
|
|
329
|
-
|
|
330
|
-
case 'stickertextblink':
|
|
331
|
-
case 'stb':
|
|
332
|
-
runCommand('stickertextblink', () => processBlinkingTextSticker({ sock, messageInfo, remoteJid, senderJid, senderName, text, extraText: 'PackZoeira', expirationMessage, color: 'white', commandPrefix }));
|
|
333
|
-
break;
|
|
334
|
-
|
|
335
|
-
case 'ranking':
|
|
336
|
-
case 'rank':
|
|
337
|
-
case 'top5':
|
|
338
|
-
runCommand('ranking', () => handleRankingCommand({ sock, remoteJid, messageInfo, expirationMessage, isGroupMessage }));
|
|
339
|
-
break;
|
|
340
|
-
|
|
341
|
-
case 'rankingglobal':
|
|
342
|
-
case 'rankglobal':
|
|
343
|
-
case 'globalrank':
|
|
344
|
-
case 'globalranking':
|
|
345
|
-
runCommand('rankingglobal', () => handleGlobalRankingCommand({ sock, remoteJid, messageInfo, expirationMessage, isGroupMessage }));
|
|
346
|
-
break;
|
|
347
|
-
|
|
348
|
-
case 'semmsg':
|
|
349
|
-
case 'zeromsg':
|
|
350
|
-
case 'nomsg':
|
|
351
|
-
case 'inativos':
|
|
352
|
-
runCommand('semmsg', () => handleNoMessageCommand({ sock, remoteJid, messageInfo, expirationMessage, isGroupMessage, senderJid, text }));
|
|
353
|
-
break;
|
|
354
|
-
|
|
355
|
-
case 'ping':
|
|
356
|
-
runCommand('ping', () => handlePingCommand({ sock, remoteJid, messageInfo, expirationMessage }));
|
|
357
|
-
break;
|
|
358
|
-
|
|
359
|
-
case 'dado':
|
|
360
|
-
case 'dice':
|
|
361
|
-
runCommand('dado', () => handleDiceCommand({ sock, remoteJid, messageInfo, expirationMessage, args, commandPrefix }));
|
|
362
|
-
break;
|
|
363
|
-
|
|
364
|
-
case 'user':
|
|
365
|
-
case 'usuario':
|
|
366
|
-
runCommand('user', () =>
|
|
367
|
-
handleUserCommand({
|
|
368
|
-
sock,
|
|
369
|
-
remoteJid,
|
|
370
|
-
messageInfo,
|
|
371
|
-
expirationMessage,
|
|
372
|
-
senderJid,
|
|
373
|
-
args,
|
|
374
|
-
isGroupMessage,
|
|
375
|
-
commandPrefix,
|
|
376
|
-
}),
|
|
377
|
-
);
|
|
378
|
-
break;
|
|
379
|
-
|
|
380
|
-
case 'rpg':
|
|
381
|
-
runCommand('rpg', () =>
|
|
382
|
-
handleRpgPokemonCommand({
|
|
383
|
-
sock,
|
|
384
|
-
remoteJid,
|
|
385
|
-
messageInfo,
|
|
386
|
-
expirationMessage,
|
|
387
|
-
senderJid,
|
|
388
|
-
senderIdentity,
|
|
389
|
-
args,
|
|
390
|
-
commandPrefix,
|
|
391
|
-
}),
|
|
392
|
-
);
|
|
393
|
-
break;
|
|
394
|
-
|
|
395
|
-
case 'aviso':
|
|
396
|
-
case 'notice':
|
|
397
|
-
runCommand('aviso', () => handleNoticeCommand({ sock, remoteJid, messageInfo, expirationMessage, senderJid, text, commandPrefix }));
|
|
398
|
-
break;
|
|
399
|
-
|
|
400
|
-
default:
|
|
401
|
-
if (isAdminCommand(command)) {
|
|
402
|
-
runCommand('admin', () => handleAdminCommand({ command, args, text, sock, messageInfo, remoteJid, senderJid, botJid, isGroupMessage, expirationMessage, commandPrefix }));
|
|
403
|
-
break;
|
|
440
|
+
if (COMMAND_REACT_EMOJI) {
|
|
441
|
+
try {
|
|
442
|
+
await sendAndStore(sock, remoteJid, {
|
|
443
|
+
react: {
|
|
444
|
+
text: COMMAND_REACT_EMOJI,
|
|
445
|
+
key: messageInfo.key,
|
|
446
|
+
},
|
|
447
|
+
});
|
|
448
|
+
} catch (error) {
|
|
449
|
+
logger.warn('Falha ao enviar reação de comando:', error.message);
|
|
404
450
|
}
|
|
451
|
+
}
|
|
452
|
+
|
|
453
|
+
switch (command) {
|
|
454
|
+
case 'menu':
|
|
455
|
+
runCommand('menu', () => handleMenuCommand(sock, remoteJid, messageInfo, expirationMessage, senderName, commandPrefix, args));
|
|
456
|
+
break;
|
|
457
|
+
|
|
458
|
+
case 'sticker':
|
|
459
|
+
case 's':
|
|
460
|
+
runCommand('sticker', () => processSticker(sock, messageInfo, senderJid, remoteJid, expirationMessage, senderName, args.join(' '), { commandPrefix }));
|
|
461
|
+
break;
|
|
462
|
+
|
|
463
|
+
case 'pack':
|
|
464
|
+
case 'packs':
|
|
465
|
+
runCommand('pack', () => handlePackCommand({ sock, remoteJid, messageInfo, expirationMessage, senderJid, senderName, text, commandPrefix }));
|
|
466
|
+
break;
|
|
467
|
+
|
|
468
|
+
case 'toimg':
|
|
469
|
+
case 'tovideo':
|
|
470
|
+
case 'tovid':
|
|
471
|
+
runCommand('toimg', () => handleStickerConvertCommand({ sock, remoteJid, messageInfo, expirationMessage, senderJid }));
|
|
472
|
+
break;
|
|
473
|
+
|
|
474
|
+
case 'play':
|
|
475
|
+
runCommand('play', () => handlePlayCommand(sock, remoteJid, messageInfo, expirationMessage, text, commandPrefix));
|
|
476
|
+
break;
|
|
477
|
+
|
|
478
|
+
case 'playvid':
|
|
479
|
+
runCommand('playvid', () => handlePlayVidCommand(sock, remoteJid, messageInfo, expirationMessage, text, commandPrefix));
|
|
480
|
+
break;
|
|
481
|
+
|
|
482
|
+
case 'tiktok':
|
|
483
|
+
case 'tt':
|
|
484
|
+
runCommand('tiktok', () => handleTikTokCommand({ sock, remoteJid, messageInfo, expirationMessage, text, commandPrefix }));
|
|
485
|
+
break;
|
|
486
|
+
|
|
487
|
+
case 'cat':
|
|
488
|
+
runCommand('cat', () => handleCatCommand({ sock, remoteJid, messageInfo, expirationMessage, senderJid, text, commandPrefix }));
|
|
489
|
+
break;
|
|
405
490
|
|
|
406
|
-
|
|
407
|
-
|
|
408
|
-
|
|
409
|
-
|
|
410
|
-
|
|
411
|
-
|
|
412
|
-
|
|
491
|
+
case 'catimg':
|
|
492
|
+
case 'catimage':
|
|
493
|
+
runCommand('catimg', () => handleCatImageCommand({ sock, remoteJid, messageInfo, expirationMessage, senderJid, text, commandPrefix }));
|
|
494
|
+
break;
|
|
495
|
+
|
|
496
|
+
case 'catprompt':
|
|
497
|
+
case 'iaprompt':
|
|
498
|
+
case 'promptia':
|
|
499
|
+
runCommand('catprompt', () => handleCatPromptCommand({ sock, remoteJid, messageInfo, expirationMessage, senderJid, text, commandPrefix }));
|
|
500
|
+
break;
|
|
501
|
+
|
|
502
|
+
case 'quote':
|
|
503
|
+
case 'qc':
|
|
504
|
+
runCommand('quote', () => handleQuoteCommand({ sock, remoteJid, messageInfo, expirationMessage, senderJid, senderName, text, commandPrefix }));
|
|
505
|
+
break;
|
|
506
|
+
|
|
507
|
+
case 'wp':
|
|
508
|
+
case 'waifupics':
|
|
509
|
+
runCommand('waifupics', () => handleWaifuPicsCommand({ sock, remoteJid, messageInfo, expirationMessage, text, type: 'sfw', commandPrefix }));
|
|
510
|
+
break;
|
|
511
|
+
|
|
512
|
+
case 'wpnsfw':
|
|
513
|
+
case 'waifupicsnsfw':
|
|
514
|
+
runCommand('waifupicsnsfw', () => handleWaifuPicsCommand({ sock, remoteJid, messageInfo, expirationMessage, text, type: 'nsfw', commandPrefix }));
|
|
515
|
+
break;
|
|
516
|
+
|
|
517
|
+
case 'wppicshelp':
|
|
518
|
+
runCommand('wppicshelp', () => sendAndStore(sock, remoteJid, { text: getWaifuPicsUsageText(commandPrefix) }, { quoted: messageInfo, ephemeralExpiration: expirationMessage }));
|
|
519
|
+
break;
|
|
520
|
+
|
|
521
|
+
case 'stickertext':
|
|
522
|
+
case 'st':
|
|
523
|
+
runCommand('stickertext', () => processTextSticker({ sock, messageInfo, remoteJid, senderJid, senderName, text, extraText: 'PackZoeira', expirationMessage, color: 'black', commandPrefix }));
|
|
524
|
+
break;
|
|
525
|
+
|
|
526
|
+
case 'stickertextwhite':
|
|
527
|
+
case 'stw':
|
|
528
|
+
runCommand('stickertextwhite', () => processTextSticker({ sock, messageInfo, remoteJid, senderJid, senderName, text, extraText: 'PackZoeira', expirationMessage, color: 'white', commandPrefix }));
|
|
529
|
+
break;
|
|
530
|
+
|
|
531
|
+
case 'stickertextblink':
|
|
532
|
+
case 'stb':
|
|
533
|
+
runCommand('stickertextblink', () => processBlinkingTextSticker({ sock, messageInfo, remoteJid, senderJid, senderName, text, extraText: 'PackZoeira', expirationMessage, color: 'white', commandPrefix }));
|
|
534
|
+
break;
|
|
535
|
+
|
|
536
|
+
case 'ranking':
|
|
537
|
+
case 'rank':
|
|
538
|
+
case 'top5':
|
|
539
|
+
runCommand('ranking', () => handleRankingCommand({ sock, remoteJid, messageInfo, expirationMessage, isGroupMessage }));
|
|
540
|
+
break;
|
|
541
|
+
|
|
542
|
+
case 'rankingglobal':
|
|
543
|
+
case 'rankglobal':
|
|
544
|
+
case 'globalrank':
|
|
545
|
+
case 'globalranking':
|
|
546
|
+
runCommand('rankingglobal', () => handleGlobalRankingCommand({ sock, remoteJid, messageInfo, expirationMessage, isGroupMessage }));
|
|
547
|
+
break;
|
|
548
|
+
|
|
549
|
+
case 'semmsg':
|
|
550
|
+
case 'zeromsg':
|
|
551
|
+
case 'nomsg':
|
|
552
|
+
case 'inativos':
|
|
553
|
+
runCommand('semmsg', () => handleNoMessageCommand({ sock, remoteJid, messageInfo, expirationMessage, isGroupMessage, senderJid, text }));
|
|
554
|
+
break;
|
|
555
|
+
|
|
556
|
+
case 'ping':
|
|
557
|
+
runCommand('ping', () => handlePingCommand({ sock, remoteJid, messageInfo, expirationMessage }));
|
|
558
|
+
break;
|
|
559
|
+
|
|
560
|
+
case 'dado':
|
|
561
|
+
case 'dice':
|
|
562
|
+
runCommand('dado', () => handleDiceCommand({ sock, remoteJid, messageInfo, expirationMessage, args, commandPrefix }));
|
|
563
|
+
break;
|
|
564
|
+
|
|
565
|
+
case 'user':
|
|
566
|
+
case 'usuario':
|
|
567
|
+
runCommand('user', () =>
|
|
568
|
+
handleUserCommand({
|
|
569
|
+
sock,
|
|
570
|
+
remoteJid,
|
|
571
|
+
messageInfo,
|
|
572
|
+
expirationMessage,
|
|
573
|
+
senderJid,
|
|
574
|
+
args,
|
|
575
|
+
isGroupMessage,
|
|
576
|
+
commandPrefix,
|
|
577
|
+
}),
|
|
578
|
+
);
|
|
579
|
+
break;
|
|
580
|
+
|
|
581
|
+
case 'rpg':
|
|
582
|
+
runCommand('rpg', () =>
|
|
583
|
+
handleRpgPokemonCommand({
|
|
584
|
+
sock,
|
|
585
|
+
remoteJid,
|
|
586
|
+
messageInfo,
|
|
587
|
+
expirationMessage,
|
|
588
|
+
senderJid,
|
|
589
|
+
senderIdentity,
|
|
590
|
+
args,
|
|
591
|
+
commandPrefix,
|
|
592
|
+
}),
|
|
593
|
+
);
|
|
594
|
+
break;
|
|
595
|
+
|
|
596
|
+
case 'aviso':
|
|
597
|
+
case 'notice':
|
|
598
|
+
runCommand('aviso', () => handleNoticeCommand({ sock, remoteJid, messageInfo, expirationMessage, senderJid, text, commandPrefix }));
|
|
599
|
+
break;
|
|
600
|
+
|
|
601
|
+
default:
|
|
602
|
+
if (isAdminCommandRoute) {
|
|
603
|
+
runCommand('admin', () => handleAdminCommand({ command, args, text, sock, messageInfo, remoteJid, senderJid, botJid, isGroupMessage, expirationMessage, commandPrefix }));
|
|
604
|
+
break;
|
|
605
|
+
}
|
|
606
|
+
|
|
607
|
+
analysisPayload.processingResult = 'unknown_command';
|
|
608
|
+
logger.info(`Comando desconhecido recebido: ${command}`);
|
|
609
|
+
runCommand('unknown', () =>
|
|
610
|
+
sendAndStore(
|
|
611
|
+
sock,
|
|
612
|
+
remoteJid,
|
|
613
|
+
{
|
|
614
|
+
text: `❌ *Comando não reconhecido*
|
|
413
615
|
|
|
414
616
|
O comando *${command}* não está configurado ou ainda não existe.
|
|
415
617
|
|
|
@@ -418,39 +620,55 @@ Digite *${commandPrefix}menu* para ver a lista de comandos disponíveis.
|
|
|
418
620
|
|
|
419
621
|
🚧 *Fase Beta*
|
|
420
622
|
O omnizap-system ainda está em desenvolvimento e novos comandos estão sendo adicionados constantemente.`,
|
|
421
|
-
|
|
422
|
-
|
|
423
|
-
|
|
424
|
-
|
|
425
|
-
|
|
623
|
+
},
|
|
624
|
+
{ quoted: messageInfo, ephemeralExpiration: expirationMessage },
|
|
625
|
+
),
|
|
626
|
+
);
|
|
627
|
+
break;
|
|
628
|
+
}
|
|
426
629
|
}
|
|
427
|
-
}
|
|
428
630
|
|
|
429
|
-
|
|
430
|
-
|
|
431
|
-
|
|
432
|
-
|
|
433
|
-
|
|
434
|
-
|
|
435
|
-
|
|
436
|
-
|
|
437
|
-
|
|
631
|
+
if (!isMessageFromBot) {
|
|
632
|
+
runCommand('pack-capture', () =>
|
|
633
|
+
maybeCaptureIncomingSticker({
|
|
634
|
+
messageInfo,
|
|
635
|
+
senderJid,
|
|
636
|
+
isMessageFromBot,
|
|
637
|
+
}),
|
|
638
|
+
);
|
|
639
|
+
}
|
|
438
640
|
|
|
439
|
-
|
|
440
|
-
|
|
441
|
-
|
|
442
|
-
|
|
443
|
-
|
|
444
|
-
|
|
445
|
-
|
|
446
|
-
|
|
447
|
-
|
|
448
|
-
|
|
449
|
-
|
|
450
|
-
|
|
451
|
-
|
|
641
|
+
if (isGroupMessage && !isCommandMessage && !isMessageFromBot) {
|
|
642
|
+
const autoStickerMedia = extractSupportedStickerMediaDetails(messageInfo, { includeQuoted: false });
|
|
643
|
+
|
|
644
|
+
if (autoStickerMedia && autoStickerMedia.mediaType !== 'sticker') {
|
|
645
|
+
const groupConfig = await groupConfigStore.getGroupConfig(remoteJid);
|
|
646
|
+
if (groupConfig.autoStickerEnabled) {
|
|
647
|
+
analysisPayload.processingResult = 'autosticker_triggered';
|
|
648
|
+
analysisPayload.metadata = {
|
|
649
|
+
...analysisPayload.metadata,
|
|
650
|
+
auto_sticker_media_type: autoStickerMedia.mediaType || null,
|
|
651
|
+
};
|
|
652
|
+
runCommand('autosticker', () =>
|
|
653
|
+
processSticker(sock, messageInfo, senderJid, remoteJid, expirationMessage, senderName, '', {
|
|
654
|
+
includeQuotedMedia: false,
|
|
655
|
+
showAutoPackNotice: false,
|
|
656
|
+
commandPrefix,
|
|
657
|
+
}),
|
|
658
|
+
);
|
|
659
|
+
}
|
|
452
660
|
}
|
|
453
661
|
}
|
|
662
|
+
} catch (messageError) {
|
|
663
|
+
analysisPayload.processingResult = 'error';
|
|
664
|
+
analysisPayload.errorCode = normalizeAnalysisErrorCode(messageError);
|
|
665
|
+
logger.error('Erro ao processar mensagem individual:', {
|
|
666
|
+
error: messageError?.message,
|
|
667
|
+
messageId: messageInfo?.key?.id || null,
|
|
668
|
+
remoteJid,
|
|
669
|
+
});
|
|
670
|
+
} finally {
|
|
671
|
+
persistMessageAnalysisEvent(analysisPayload);
|
|
454
672
|
}
|
|
455
673
|
}
|
|
456
674
|
} catch (error) {
|