diff --git a/README.md b/README.md index 9a82cb9..e4e9d9e 100644 --- a/README.md +++ b/README.md @@ -1,4 +1,4 @@ -# ToxiBR - Moderacao de Chat Open Source +# ToxiBR - Moderação de Chat Open Source ![ToxiBR Screenshot](assets/screenshot.png) @@ -7,7 +7,7 @@ [![license](https://img.shields.io/npm/l/toxibr)](LICENSE) [![PRs Welcome](https://img.shields.io/badge/PRs-welcome-brightgreen.svg)](CONTRIBUTING.md) -Biblioteca open source de moderacao de conteudo toxico para **portugues brasileiro**. Filtra mensagens em tempo real com deteccao por contexto, anti-bypass e zero dependencias. +Biblioteca open source de moderação de conteúdo tóxico para **português brasileiro**. Filtra mensagens em tempo real com detecção por contexto, anti-bypass e zero dependências. > **Context-Aware. Anti-Bypass. < 1ms. Zero Deps.** @@ -15,7 +15,23 @@ Biblioteca open source de moderacao de conteudo toxico para **portugues brasilei --- -## Inicio rapido +## Sumário + +- [Início rápido](#início-rápido) +- [Por que ToxiBR?](#por-que-toxibr) +- [Por que contexto importa](#por-que-contexto-importa) +- [Modo censor](#modo-censor) +- [Uso avançado](#uso-avançado) +- [Camadas de filtragem](#camadas-de-filtragem) +- [Normalização anti-bypass](#normalização-anti-bypass) +- [Context-aware: proximidade](#context-aware-proximidade) +- [Exportações](#exportações) +- [Contribuindo](#contribuindo) +- [Licença](#licença) + +--- + +## Início rápido ```bash npm install toxibr @@ -32,29 +48,35 @@ if (!result.allowed) { } ``` +--- + ## Por que ToxiBR? -A maioria dos filtros de chat usa wordlists simples — bloqueia "lixo" em qualquer contexto, gera falso positivo em frases inocentes, e nao entende girias brasileiras. ToxiBR resolve isso: +A maioria dos filtros de chat usa wordlists simples — bloqueia "lixo" em qualquer contexto, gera falso positivo em frases inocentes, e não entende gírias brasileiras. ToxiBR resolve isso: -- **880+ termos e frases** — slurs, sexual, violencia, racismo, nazismo, bullying, assedio -- **Context-aware** — entende a diferenca entre "eu me sinto um lixo" (permitido) e "voce e um lixo" (bloqueado) -- **Anti-bypass** — normaliza leetspeak, homoglyphs, acentos, zero-width chars, abreviacoes BR -- **Zero dependencias** — nada de SDK pesado, roda em qualquer lugar +- **880+ termos e frases** — slurs, sexual, violência, racismo, nazismo, bullying, assédio +- **Context-aware** — entende a diferença entre "eu me sinto um lixo" (permitido) e "você é um lixo" (bloqueado) +- **Anti-bypass** — normaliza leetspeak, homoglyphs, acentos, zero-width chars, abreviações BR +- **Zero dependências** — nada de SDK pesado, roda em qualquer lugar - **< 1ms por mensagem** — feito para chat em tempo real - **Fuzzy matching** — Levenshtein pega typos intencionais (viadro, bucetra) - **Prefix matching** — pega palavras truncadas (estup -> estupro, punh -> punheta) -- **Seed word density** — detecta conteudo sexual codificado (3+ palavras suspeitas juntas) -- **Proximity detection** — analisa distancia entre pronomes e palavras ofensivas -- **Modo censor** — substitui palavras toxicas por `***` em vez de bloquear +- **Seed word density** — detecta conteúdo sexual codificado (3+ palavras suspeitas juntas) +- **Proximity detection** — analisa distância entre pronomes e palavras ofensivas +- **Modo censor** — substitui palavras tóxicas por `***` em vez de bloquear + +--- ## Por que contexto importa | Frase | Filtro tradicional | ToxiBR | |---|---|---| -| "Esse filme e incrivel!" | FLAGGED | ALLOWED — sentimento positivo | -| "Aquele boss fight e insano!" | FLAGGED | ALLOWED — contexto de gaming | -| "Me sinto um lixo" | FLAGGED | ALLOWED — auto-expressao | -| "Voce e um lixo" | FLAGGED | BLOCKED — insulto dirigido | +| "Esse filme é incrível!" | FLAGGED | ALLOWED — sentimento positivo | +| "Aquele boss fight é insano!" | FLAGGED | ALLOWED — contexto de gaming | +| "Me sinto um lixo" | FLAGGED | ALLOWED — auto-expressão | +| "Você é um lixo" | FLAGGED | BLOCKED — insulto dirigido | + +--- ## Modo censor @@ -66,7 +88,7 @@ console.log(result.censored); // "seu ********* vai se *****" console.log(result.matches); // [{ word: 'arrombado', reason: 'hard_block', ... }] ``` -### Censurar phones e links inline +### Censurar telefones e links inline ```ts import { createCensor } from 'toxibr'; @@ -81,7 +103,9 @@ const result = censor('me liga 21994709426 seu idiota'); // { censored: "me liga *********** seu ******", ... } ``` -## Uso avancado +--- + +## Uso avançado ```ts import { createFilter } from 'toxibr'; @@ -98,49 +122,57 @@ const filter = createFilter({ const result = filter('mensagem aqui'); ``` +--- + ## Camadas de filtragem | Camada | O que faz | Exemplo | |---|---|---| | **Censorship bypass** | Bloqueia `*` e `#` entre letras | `p*ta`, `v#ado` | -| **Pre-normalization** | Pega d4, Xcm, -18 antes do leetspeak | `20cm`, `d4`, `-18` | -| **Links** | URLs e dominios | `https://...`, `site.com` | -| **Telefone** | Numeros BR (5+ digitos) | `(21) 99470-9426` | -| **Emojis** | Emojis ofensivos e sequencias | `🖕`, `🍆💦` | -| **Hard-block** | 880+ termos sempre proibidos | Slurs, sexual, violencia, nazismo | +| **Pré-normalização** | Pega d4, Xcm, -18 antes do leetspeak | `20cm`, `d4`, `-18` | +| **Links** | URLs e domínios | `https://...`, `site.com` | +| **Telefone** | Números BR (5+ dígitos) | `(21) 99470-9426` | +| **Emojis** | Emojis ofensivos e sequências | `🖕`, `🍆💦` | +| **Hard-block** | 880+ termos sempre proibidos | Slurs, sexual, violência, nazismo | | **Fuzzy match** | Levenshtein para typos | `viadro` -> `viado` (dist 1) | | **Prefix match** | Palavras truncadas | `estup` -> `estupro` | -| **Context-aware** | Insulto dirigido vs auto-expressao | `"seu lixo"` vs `"me sinto um lixo"` | +| **Context-aware** | Insulto dirigido vs. auto-expressão | `"seu lixo"` vs. `"me sinto um lixo"` | | **Seed density** | 3+ palavras sexuais juntas | `"pau veiudo saco lotado leite"` | -## Normalizacao anti-bypass +--- + +## Normalização anti-bypass -Antes de checar a wordlist, o texto passa por 11 etapas de normalizacao: +Antes de checar a wordlist, o texto passa por 11 etapas de normalização: -| Tecnica | Antes | Depois | +| Técnica | Antes | Depois | |---|---|---| | Zero-width chars | `vi​ado` | `viado` | -| Homoglyphs cirilicos | `viаdо` | `viado` | +| Homoglyphs cirílicos | `viаdо` | `viado` | | Acentos | `viàdo` | `viado` | | Chars repetidos | `viiaaado` | `viado` | | Leetspeak | `3stupr0` | `estupro` | | Censura | `p*ta`, `v#ado` | bloqueado | -| Pontos/tracos | `p.u.t.a` | `puta` | -| Espacos isolados | `p u t a` | `puta` | -| Abreviacoes BR | `ppk`, `krl` | `pepeca`, `caralho` | +| Pontos/traços | `p.u.t.a` | `puta` | +| Espaços isolados | `p u t a` | `puta` | +| Abreviações BR | `ppk`, `krl` | `pepeca`, `caralho` | + +--- ## Context-aware: proximidade -O filtro usa **deteccao por proximidade** — analisa se o padrao dirigido (voce, seu, tu) esta perto da palavra ofensiva numa janela de 5 palavras: +O filtro usa **detecção por proximidade** — analisa se o padrão dirigido (você, seu, tu) está perto da palavra ofensiva numa janela de 5 palavras: ```ts -filterContent('eu me sinto um lixo'); // { allowed: true } — auto-expressao +filterContent('eu me sinto um lixo'); // { allowed: true } — auto-expressão filterContent('voce e um lixo'); // { allowed: false } — insulto dirigido filterContent('me sinto um lixo e voce e um lixo tambem'); // { allowed: false } filterContent('me sinto um lixo, queria que voce me respeitasse'); // { allowed: true } ``` -## Exportacoes +--- + +## Exportações ```ts import { @@ -148,23 +180,25 @@ import { createFilter, // cria filtro customizado censorContent, // censor default (zero config) createCensor, // censor customizado - normalize, // normaliza texto (util para debug) + normalize, // normaliza texto (útil para debug) HARD_BLOCKED, // 880+ termos hard-blocked CONTEXT_SENSITIVE, // termos context-sensitive SEXUAL_SEED_WORDS, // palavras-semente sexuais DIRECTED_PATTERNS, // regex de fala dirigida - SELF_EXPRESSION_PATTERNS, // regex de auto-expressao - ABBREVIATION_MAP, // mapa de abreviacoes BR + SELF_EXPRESSION_PATTERNS, // regex de auto-expressão + ABBREVIATION_MAP, // mapa de abreviações BR } from 'toxibr'; ``` +--- + ## Contribuindo -**ToxiBR e feito pela comunidade.** Se voce fala portugues brasileiro, sua contribuicao faz diferenca real — cada palavra adicionada protege milhares de usuarios. +**ToxiBR é feito pela comunidade.** Se você fala português brasileiro, sua contribuição faz diferença real — cada palavra adicionada protege milhares de usuários. ### Como contribuir -1. **Fork** o repositorio +1. **Fork** o repositório 2. Crie uma branch (`git checkout -b feat/nova-palavra`) 3. Adicione os termos em `src/wordlists.ts` e testes em `__tests__/filter.test.ts` 4. Rode os testes: `npm test` @@ -173,20 +207,22 @@ import { ### Formas de ajudar - Adicionar palavras/frases que faltam na wordlist -- Reportar falsos positivos (palavras que nao deveriam ser bloqueadas) -- Melhorar a deteccao de contexto -- Adicionar testes para novos cenarios -- Melhorar a documentacao +- Reportar falsos positivos (palavras que não deveriam ser bloqueadas) +- Melhorar a detecção de contexto +- Adicionar testes para novos cenários +- Melhorar a documentação Leia o guia completo: **[CONTRIBUTING.md](CONTRIBUTING.md)** -Encontrou um falso positivo ou uma palavra que deveria ser bloqueada? Use o formulario no site: **[toxibr.vercel.app](https://toxibr.vercel.app)** +Encontrou um falso positivo ou uma palavra que deveria ser bloqueada? Use o formulário no site: **[toxibr.vercel.app](https://toxibr.vercel.app)** ```bash npm test # roda os testes npm run validate # verifica duplicatas nas wordlists ``` -## Licenca +--- + +## Licença MIT