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 = `
+ Preview +
+
+ + + +
+
+
`; + } else if (file.type.startsWith('video/')) { + mediaPreview = `
+ +
+
+ + + +
+
+
`; + } + } + + dialog.innerHTML = ` +
+

⚠️ Aviso de Conteúdo Sensível

+ ${mediaPreview} +
+

Este conteúdo pode ser impróprio ou conter golpes.

+

Tem certeza que deseja visualizar?

+
+ + +
+
+
+ `; + + document.body.appendChild(dialog); + + // Adiciona evento para remover o URL do objeto quando o diálogo for fechado + const cleanup = () => { + if (this.isMediaFile(file)) { + URL.revokeObjectURL(objectUrl); + } + }; + + dialog.querySelector('.btn-cancel').onclick = () => { + cleanup(); + document.body.removeChild(dialog); + resolve(false); + }; + + dialog.querySelector('.btn-view').onclick = () => { + cleanup(); + document.body.removeChild(dialog); + resolve(true); + }; + }); + } + + // Processa um arquivo antes de enviar + async processFile(file) { + // Verifica spam no nome do arquivo + if (this.isSpam(file.name)) { + throw new Error('Arquivo bloqueado: Conteúdo impróprio detectado'); + } + + // Verifica conteúdo NSFW + if (await this.checkNSFW(file)) { + const shouldView = await this.showWarningDialog(file); + if (!shouldView) { + throw new Error('Arquivo bloqueado: Conteúdo impróprio'); + } + } + + return file; + } +} + +// Adiciona estilos para o diálogo de aviso +const style = document.createElement('style'); +style.textContent = ` + .content-warning-dialog { + position: fixed; + top: 0; + left: 0; + width: 100%; + height: 100%; + background: rgba(0, 0, 0, 0.9); + display: flex; + justify-content: center; + align-items: center; + z-index: 9999; + } + + .warning-content { + background: #1a1a1a; + padding: 20px; + border-radius: 12px; + max-width: 500px; + text-align: center; + color: white; + box-shadow: 0 0 20px rgba(0, 0, 0, 0.5); + } + + .media-preview { + position: relative; + width: 100%; + max-height: 300px; + margin: 20px 0; + border-radius: 8px; + overflow: hidden; + } + + .media-preview img, + .media-preview video { + width: 100%; + height: 100%; + object-fit: cover; + } + + .blurred img, + .blurred video { + filter: blur(20px); + } + + .warning-overlay { + position: absolute; + top: 0; + left: 0; + width: 100%; + height: 100%; + display: flex; + justify-content: center; + align-items: center; + background: rgba(0, 0, 0, 0.5); + } + + .warning-icon { + position: relative; + width: 64px; + height: 64px; + display: flex; + justify-content: center; + align-items: center; + } + + .warning-icon svg { + filter: drop-shadow(0 0 10px rgba(0, 0, 0, 0.5)); + } + + .warning-slash { + position: absolute; + top: 50%; + left: 50%; + transform: translate(-50%, -50%) rotate(-45deg); + font-size: 48px; + color: #ff4444; + font-weight: bold; + } + + .warning-message { + margin-top: 20px; + } + + .warning-message p { + margin: 10px 0; + font-size: 16px; + } + + .warning-buttons { + display: flex; + justify-content: center; + gap: 20px; + margin-top: 20px; + } + + .warning-buttons button { + padding: 12px 24px; + border: none; + border-radius: 6px; + cursor: pointer; + font-size: 16px; + font-weight: bold; + transition: all 0.3s ease; + } + + .btn-cancel { + background: #ff4444; + color: white; + } + + .btn-cancel:hover { + background: #ff0000; + } + + .btn-view { + background: #4CAF50; + color: white; + } + + .btn-view:hover { + background: #45a049; + } +`; +document.head.appendChild(style); + +export default ContentModeration; \ No newline at end of file