INDEX / modules /MessageProcessor.js
akra35567's picture
Upload 18 files
7226ab4 verified
/**
* ═══════════════════════════════════════════════════════════════════════
* CLASSE: MessageProcessor
* ═══════════════════════════════════════════════════════════════════════
* Processamento inteligente de mensagens: anΓ‘lise, detecΓ§Γ£o de reply, contexto
* ExtraΓ§Γ£o de informaΓ§Γ΅es de grupos e usuΓ‘rios
* ═══════════════════════════════════════════════════════════════════════
*/
const { getContentType } = require('@whiskeysockets/baileys');
const ConfigManager = require('./ConfigManager');
let parsePhoneNumberFromString = null;
try {
// optional modern phone parsing if available
({ parsePhoneNumberFromString } = require('libphonenumber-js'));
} catch (e) {
// lib not installed β€” graceful fallback to simple digit extraction
}
class MessageProcessor {
constructor(logger = null) {
this.config = ConfigManager.getInstance();
this.logger = logger || console;
}
/**
* Extrai nΓΊmero real do usuΓ‘rio
*/
extractUserNumber(message) {
try {
const key = message.key || {};
const remoteJid = key.remoteJid || '';
// Se for PV (nΓ£o termina com @g.us)
if (!String(remoteJid).endsWith('@g.us')) {
return String(remoteJid).split('@')[0];
}
// Se for grupo, obtΓ©m do participant
if (key.participant) {
const participant = String(key.participant);
if (participant.includes('@s.whatsapp.net')) {
return participant.split('@')[0];
}
if (participant.includes('@lid')) {
const limpo = participant.split(':')[0];
const digitos = limpo.replace(/\D/g, '');
// If libphonenumber-js is available, try to normalize to E.164 (without '+')
try {
const cfg = ConfigManager.getInstance();
let defaultCountry = null;
if (cfg.BOT_NUMERO_REAL && String(cfg.BOT_NUMERO_REAL).startsWith('244')) {
defaultCountry = 'AO';
}
if (parsePhoneNumberFromString) {
const pn = defaultCountry
? parsePhoneNumberFromString(digitos, defaultCountry)
: parsePhoneNumberFromString(digitos);
if (pn && pn.isValid && pn.isValid()) {
// return E.164 without '+' to match JID numeric part
return String(pn.number).replace(/^\+/, '');
}
}
} catch (err) {
// fallback to raw digits if parsing fails
}
// Fallback: return the raw extracted digits (no forced country prefix)
if (digitos.length > 0) return digitos;
}
}
return 'desconhecido';
} catch (e) {
this.logger.error('Erro ao extrair nΓΊmero:', e.message);
return 'desconhecido';
}
}
/**
* Extrai texto de mensagem
*/
extractText(message) {
try {
const tipo = getContentType(message.message);
if (!tipo) return '';
const msg = message.message;
switch (tipo) {
case 'conversation':
return msg.conversation || '';
case 'extendedTextMessage':
return msg.extendedTextMessage?.text || '';
case 'imageMessage':
return msg.imageMessage?.caption || '';
case 'videoMessage':
return msg.videoMessage?.caption || '';
case 'audioMessage':
return '[mensagem de voz]';
case 'stickerMessage':
return '[figurinha]';
case 'documentMessage':
return msg.documentMessage?.caption || '[documento]';
default:
return '';
}
} catch (e) {
return '';
}
}
/**
* Detecta tipo de conversa (PV ou Grupo)
*/
getConversationType(message) {
const remoteJid = message.key?.remoteJid || '';
return String(remoteJid).endsWith('@g.us') ? 'grupo' : 'pv';
}
/**
* Extrai informaΓ§Γ΅es de reply
*/
extractReplyInfo(message) {
try {
const context = message.message?.extendedTextMessage?.contextInfo;
if (!context || !context.quotedMessage) return null;
const quoted = context.quotedMessage;
const tipo = getContentType(quoted);
// Extrai texto da mensagem citada
let textoMensagemCitada = '';
let tipoMidia = 'texto';
if (tipo === 'conversation') {
textoMensagemCitada = quoted.conversation || '';
tipoMidia = 'texto';
} else if (tipo === 'extendedTextMessage') {
textoMensagemCitada = quoted.extendedTextMessage?.text || '';
tipoMidia = 'texto';
} else if (tipo === 'imageMessage') {
textoMensagemCitada = quoted.imageMessage?.caption || '[imagem]';
tipoMidia = 'imagem';
} else if (tipo === 'videoMessage') {
textoMensagemCitada = quoted.videoMessage?.caption || '[vΓ­deo]';
tipoMidia = 'video';
} else if (tipo === 'audioMessage') {
textoMensagemCitada = '[Γ‘udio]';
tipoMidia = 'audio';
} else if (tipo === 'stickerMessage') {
textoMensagemCitada = '[figurinha]';
tipoMidia = 'sticker';
} else {
textoMensagemCitada = '[conteΓΊdo]';
tipoMidia = 'outro';
}
const participantJidCitado = context.participant || null;
return {
textoMensagemCitada,
tipoMidia,
participantJidCitado,
ehRespostaAoBot: this.isReplyToBot(participantJidCitado),
quemEscreveuCitacao: this.extractUserNumber({ key: { participant: participantJidCitado } })
};
} catch (e) {
this.logger.error('Erro ao extrair reply info:', e.message);
return null;
}
}
/**
* Verifica se Γ© reply ao bot
*/
isReplyToBot(jid) {
if (!jid) return false;
const jidStr = String(jid).toLowerCase();
const jidNumero = jidStr.split('@')[0].split(':')[0];
const botNumero = String(this.config.BOT_NUMERO_REAL).toLowerCase();
return jidNumero === botNumero || jidStr.includes(botNumero);
}
/**
* Detecta se tem Γ‘udio
*/
hasAudio(message) {
try {
const tipo = getContentType(message.message);
return tipo === 'audioMessage';
} catch (e) {
return false;
}
}
/**
* Detecta tipo de mΓ­dia
*/
getMediaType(message) {
try {
const tipo = getContentType(message.message);
const mimeMap = {
'imageMessage': 'imagem',
'videoMessage': 'video',
'audioMessage': 'audio',
'stickerMessage': 'sticker',
'documentMessage': 'documento'
};
return mimeMap[tipo] || 'texto';
} catch (e) {
return 'texto';
}
}
/**
* Verifica se Γ© menΓ§Γ£o do bot
*/
isBotMentioned(message) {
try {
const mentions = message.message?.extendedTextMessage?.contextInfo?.mentionedJid || [];
return mentions.some(jid => this.isReplyToBot(jid));
} catch (e) {
return false;
}
}
/**
* Extrai menΓ§Γ΅es
*/
extractMentions(message) {
try {
return message.message?.extendedTextMessage?.contextInfo?.mentionedJid || [];
} catch (e) {
return [];
}
}
/**
* Verifica se Γ© comando
*/
isCommand(text) {
if (!text) return false;
return text.trim().startsWith(this.config.PREFIXO);
}
/**
* Parseia comando
*/
parseCommand(text) {
if (!this.isCommand(text)) return null;
const args = text.slice(this.config.PREFIXO.length).trim().split(/ +/);
const comando = args.shift().toLowerCase();
return {
comando,
args,
textoCompleto: args.join(' ')
};
}
/**
* Valida taxa de requisiΓ§Γ΅es
*/
checkRateLimit(userId, windowSeconds = null, maxCalls = null) {
const window = windowSeconds || this.config.RATE_LIMIT_WINDOW;
const max = maxCalls || this.config.RATE_LIMIT_MAX_CALLS;
if (!this.rateLimitMap) {
this.rateLimitMap = new Map();
}
const now = Date.now();
const rec = this.rateLimitMap.get(userId) || [];
const filtered = rec.filter(t => (now - t) < window * 1000);
if (filtered.length >= max) {
return false;
}
filtered.push(now);
this.rateLimitMap.set(userId, filtered);
return true;
}
/**
* Sanitiza texto para seguranΓ§a
*/
sanitizeText(text, maxLength = 2000) {
if (!text) return '';
let sanitized = String(text)
.trim()
.substring(0, maxLength);
// Remove caracteres de controle perigosos
sanitized = sanitized.replace(/[\x00-\x08\x0B\x0C\x0E-\x1F\x7F]/g, '');
return sanitized;
}
/**
* Limpa cache
*/
clearCache() {
if (this.rateLimitMap) {
this.rateLimitMap.clear();
}
this.logger.info('πŸ’Ύ Cache de processamento limpo');
}
/**
* Retorna estatΓ­sticas
*/
getStats() {
return {
rateLimitEntries: this.rateLimitMap?.size || 0,
prefixo: this.config.PREFIXO
};
}
}
module.exports = MessageProcessor;