From 9b637bfcda2bdbe9fdb7d07603a69cd4c3ef6e24 Mon Sep 17 00:00:00 2001 From: ErikrafT <139592038+erikraft@users.noreply.github.com> Date: Tue, 6 May 2025 20:11:25 -0300 Subject: [PATCH] Add files via upload --- public/scripts/content-moderation.js | 391 +++++++++++++++++++++++++++ 1 file changed, 391 insertions(+) create mode 100644 public/scripts/content-moderation.js diff --git a/public/scripts/content-moderation.js b/public/scripts/content-moderation.js new file mode 100644 index 0000000..2de7030 --- /dev/null +++ b/public/scripts/content-moderation.js @@ -0,0 +1,391 @@ +// Sistema de Moderação de Conteúdo +class ContentModeration { + constructor() { + this.blockedWords = [ + // URLs e Links suspeitos + '.gg/HpZzvY5W', '.gg/TZsbat4tw6', 'discord.gg/doncommunity', 'discord.gg/P93HBWRp', + 'bit.ly', 'encurtador.com.br', 'is.gd', 'kurl.ru', 'l1nk.dev', + 'n9.cl', 'rb.gy', 'shorturl.at', 'shre.ink', 'surl.li', 't.ly', 't.me', 'tinyurl.com', + 'u.to', 'urlzs.com', 'zzb.bz', 'steamcommunity', 'steamgift', 'steamrconmmunity', + 'steamscommunuty', 'steamshort', 'steanmecomnmunity', 'store-steaempowered', + 'share.blood-strike.com', 'casumonster.top', 'abre.ai', 'abrir.link', 'open.ai', 'open.link', + + // Palavrões e termos ofensivos + 'arromb', 'asshole', 'babac', 'bastard', 'bct', 'boceta', 'bocetas', 'boquete', 'bosta', + 'bostinha', 'buceta', 'bucetas', 'burro', 'cacete', 'caralh', 'caralho', 'corno', 'corna', + 'crlh', 'cu', 'cuckold', 'cum', 'cumshot', 'cunt', 'cunts', 'cuz', 'desgraça', 'desgraçado', + 'dick', 'escrot', 'fdp', 'foda', 'fuck', 'gay', 'idiota', 'imbecil', 'merda', 'otario', + 'otário', 'pau', 'pinto', 'porra', 'puta', 'putas', 'puto', 'quenga', 'quengo', 'retardado', + 'safado', 'shit', 'shitty', 'viad', 'viado', 'xereca', 'xoxota', 'xvideos', 'xxxvideos', + + // Termos NSFW e conteúdo adulto + '🔞', '🍆', '🍑', '🥒', '🥵', 'PORN', 'Pornografia', 'pornografía', 'pornography', + 'nude', 'nudes', 'Onlyfans', 'OnlyFans', 'Leaks', 'Hentai', 'Teen Porn', 'E-Girls Porn', + 'Latina Nudes', 'xnudes', + + // Spam e golpes + '$100', '$20 gift', '20$ gift', 'Bilhão de reais', 'Billion Dollars', 'Billion of reais', + 'Billion reais', 'buy now', 'cheap', 'click here', 'follow me', 'free gift', 'free nudes', + 'gift from steam', 'gifts', 'here', 'Hot', 'HOT', 'prize', 'vote for me', + 'withdrew $15647', 'Milhão de reais', 'Million Dollars', 'Million reais', + + // Outros termos ofensivos + 'dirty black', 'negro sujo', 'worm', 'verme', 'trash', 'worthless', 'idiot', 'imbecile', + 'shut up', 'cala a boca' + ]; + + this.spamPatterns = [ + /(.)\1{10,}/, // Caracteres repetidos (aumentado para 10+ repetições) + /(.){1000,}/, // Textos muito longos (aumentado para 1000+ caracteres) + /(.){1,10}\1{5,}/, // Padrões repetitivos + /(.){1,5}\1{10,}/, // Caracteres repetidos em sequência + /(.){1,3}\1{15,}/ // Caracteres muito repetidos + ]; + + // Mapeamento de substituições comuns + this.characterSubstitutions = { + 'a': ['4', '@', 'α', 'а'], + 'o': ['0', '@', 'о', 'ο'], + 'e': ['3', 'ε', 'е'], + 'i': ['1', '!', 'і', 'ι'], + 's': ['$', '5', 'ѕ'], + 't': ['7', 'т'], + 'b': ['8', 'в'], + 'g': ['9', 'ɡ'], + 'l': ['1', '|', 'ł'], + 'z': ['2', 'з'] + }; + + // Caracteres cirílicos que podem ser usados para enganar + this.cyrillicChars = { + 'а': 'a', // cirílico 'а' vs latino 'a' + 'е': 'e', // cirílico 'е' vs latino 'e' + 'о': 'o', // cirílico 'о' vs latino 'o' + 'с': 'c', // cirílico 'с' vs latino 'c' + 'р': 'p', // cirílico 'р' vs latino 'p' + 'у': 'y', // cirílico 'у' vs latino 'y' + 'х': 'x', // cirílico 'х' vs latino 'x' + 'і': 'i', // cirílico 'і' vs latino 'i' + 'ѕ': 's', // cirílico 'ѕ' vs latino 's' + 'т': 't', // cirílico 'т' vs latino 't' + 'в': 'b', // cirílico 'в' vs latino 'b' + 'ɡ': 'g', // cirílico 'ɡ' vs latino 'g' + 'з': 'z', // cirílico 'з' vs latino 'z' + 'м': 'm', // cirílico 'м' vs latino 'm' + 'н': 'h', // cirílico 'н' vs latino 'h' + 'к': 'k', // cirílico 'к' vs latino 'k' + 'л': 'l', // cirílico 'л' vs latino 'l' + 'д': 'd', // cirílico 'д' vs latino 'd' + 'ф': 'f', // cirílico 'ф' vs latino 'f' + 'ц': 'c', // cirílico 'ц' vs latino 'c' + 'ч': 'ch', // cirílico 'ч' vs latino 'ch' + 'ш': 'sh', // cirílico 'ш' vs latino 'sh' + 'щ': 'sch', // cirílico 'щ' vs latino 'sch' + 'ъ': '', // cirílico 'ъ' (não tem equivalente) + 'ь': '', // cirílico 'ь' (não tem equivalente) + 'ю': 'yu', // cirílico 'ю' vs latino 'yu' + 'я': 'ya' // cirílico 'я' vs latino 'ya' + }; + } + + // Normaliza o texto removendo substituições de caracteres + normalizeText(text) { + let normalized = text.toLowerCase(); + + // Substitui caracteres especiais de volta para suas formas normais + for (const [normal, substitutes] of Object.entries(this.characterSubstitutions)) { + for (const substitute of substitutes) { + normalized = normalized.replace(new RegExp(substitute, 'g'), normal); + } + } + + return normalized; + } + + // Verifica se o texto contém palavras bloqueadas mesmo com substituições + hasBlockedWordsWithSubstitutions(text) { + const normalized = this.normalizeText(text); + return this.blockedWords.some(word => normalized.includes(word.toLowerCase())); + } + + // Verifica se o arquivo é uma imagem ou vídeo + isMediaFile(file) { + return file.type.startsWith('image/') || file.type.startsWith('video/'); + } + + // Verifica se o conteúdo é NSFW + async checkNSFW(file) { + if (!this.isMediaFile(file)) return false; + + // Aqui você pode integrar com APIs de detecção de conteúdo NSFW + // Por enquanto, vamos usar uma verificação básica de nome de arquivo + const fileName = file.name.toLowerCase(); + return this.blockedWords.some(word => fileName.includes(word.toLowerCase())); + } + + // Verifica se uma URL contém caracteres cirílicos + hasCyrillicChars(url) { + const cyrillicPattern = /[\u0400-\u04FF]/; + return cyrillicPattern.test(url); + } + + // Normaliza uma URL removendo caracteres cirílicos + normalizeUrl(url) { + let normalized = url.toLowerCase(); + for (const [cyrillic, latin] of Object.entries(this.cyrillicChars)) { + normalized = normalized.replace(new RegExp(cyrillic, 'g'), latin); + } + return normalized; + } + + // Verifica se uma URL é suspeita + isSuspiciousUrl(url) { + // Verifica se contém caracteres cirílicos + if (this.hasCyrillicChars(url)) { + return true; + } + + // Verifica se a URL normalizada contém palavras bloqueadas + const normalizedUrl = this.normalizeUrl(url); + return this.blockedWords.some(word => normalizedUrl.includes(word.toLowerCase())); + } + + // Verifica se é spam ou contém palavras bloqueadas + isSpam(text) { + // Verifica se é uma URL + const urlMatch = text.match(/^(https?:\/\/[^\s]+)|([A-Za-z0-9._%+-]+@[A-Za-z0-9.-]+\.[A-Za-z]{2,})$/); + if (urlMatch) { + // Se for uma URL, verifica se é suspeita + return this.isSuspiciousUrl(urlMatch[0]); + } + + // Verifica palavras bloqueadas com substituições + if (this.hasBlockedWordsWithSubstitutions(text)) { + return true; + } + + // Verifica padrões de spam + return this.spamPatterns.some(pattern => pattern.test(text)); + } + + // Mostra o diálogo de aviso + showWarningDialog(file) { + return new Promise((resolve) => { + const dialog = document.createElement('div'); + dialog.className = 'content-warning-dialog'; + + // Cria um preview da imagem/vídeo com desfoque + let mediaPreview = ''; + if (this.isMediaFile(file)) { + const objectUrl = URL.createObjectURL(file); + if (file.type.startsWith('image/')) { + mediaPreview = `
`; + } else if (file.type.startsWith('video/')) { + mediaPreview = ` `; + } + } + + dialog.innerHTML = ` +