@sideid/id-profanity-filter
Version:
Library filter kata kotor dalam Bahasa Indonesia
247 lines (212 loc) • 6.61 kB
text/typescript
import { escapeRegExp } from './stringUtils';
import { RegexOptions } from '../types';
/**
* Membuat pola regex untuk mencocokkan kata
*
* @param word Kata yang akan dibuat pola regex-nya
* @param options Opsi untuk pembuatan regex
* @returns Objek RegExp
*/
export function createWordRegex(word: string, options: RegexOptions = {}): RegExp {
const {
wholeWord = true,
caseSensitive = false,
leetSpeak = true,
detectSplit = false,
indonesianVariation = false,
} = options;
// Escape karakter khusus regex
let pattern = escapeRegExp(word);
// Tambahkan variasi leet speak jika diminta
if (leetSpeak) {
pattern = addLeetSpeakVariations(pattern);
}
// Tambahkan variasi ejaan Bahasa Indonesia jika diminta
if (indonesianVariation) {
pattern = addIndonesianVariations(pattern);
}
// Tambahkan kemungkinan split jika diminta
if (detectSplit) {
pattern = addSplitVariations(pattern);
}
// Tambahkan boundary untuk whole word jika diminta
if (wholeWord) {
pattern = `\\b${pattern}\\b`;
}
// Buat regex dengan flag case-insensitive jika diminta
return new RegExp(pattern, caseSensitive ? 'g' : 'gi');
}
/**
* Menambahkan variasi leet speak ke pola regex
*
* Contoh:
* - 'a' bisa jadi '4', '@'
* - 'i' bisa jadi '1', '!'
*
* @param pattern Pola regex asli
* @returns Pola regex dengan variasi leet speak
*/
export function addLeetSpeakVariations(pattern: string): string {
const leetMap: Record<string, string[]> = {
a: ['a', '4', '@'],
b: ['b', '8', '6'],
c: ['c', '\\(', '\\{', '<'],
e: ['e', '3'],
g: ['g', '6', '9'],
i: ['i', '1', '!', '\\|'],
l: ['l', '1', '\\|'],
o: ['o', '0'],
s: ['s', '5', '\\$'],
t: ['t', '7', '\\+'],
z: ['z', '2'],
};
return pattern
.split('')
.map((char) => {
const lowerChar = char.toLowerCase();
const variations = leetMap[lowerChar];
if (variations && variations.length > 1) {
return `[${variations.join('')}]`;
}
return char;
})
.join('');
}
/**
* Menambahkan kemungkinan split/pemisahan antar karakter
*
* @param pattern Pola regex asli
* @returns Pola regex dengan kemungkinan split
*/
export function addSplitVariations(pattern: string): string {
// Instead of joining character by character with a separator pattern,
// we'll create a simpler version that matches the pattern with optional separators
// Convert each character to a pattern that allows optional separators before it
// except for the first character
let result = '';
const chars = pattern.split('');
for (let i = 0; i < chars.length; i++) {
if (i > 0) {
// Add optional separator before each character except the first
result += '[\\s\\-._*+]?';
}
result += chars[i];
}
return result;
}
/**
* Membuat regex untuk mencari kata dengan variasi spasi dan karakter penghubung
* Berguna untuk mendeteksi upaya menghindari filter dengan menambahkan spasi atau karakter lain
*
* @param word Kata yang akan dibuat pola regexnya
* @returns Objek RegExp
*/
export function createEvasionRegex(word: string): RegExp {
// Escape karakter khusus regex
const escaped = escapeRegExp(word);
// Create a regex pattern that allows any separator between characters
let result = '';
const chars = escaped.split('');
for (let i = 0; i < chars.length; i++) {
// Add the character
result += chars[i];
// Add optional separator after each character except the last
if (i < chars.length - 1) {
result += '[\\s\\-._*+]?';
}
}
return new RegExp(result, 'gi');
}
/**
* Menambahkan variasi ejaan Bahasa Indonesia
*
* @param pattern Pola regex asli
* @returns Pola regex dengan variasi ejaan Bahasa Indonesia
*/
export function addIndonesianVariations(pattern: string): string {
// Variasi ejaan dalam Bahasa Indonesia
const variationMap: Record<string, string[]> = {
c: ['c', 'k'], // contoh: becok/bekok
k: ['k', 'c', 'q'], // contoh: kacau/qacau
j: ['j', 'd'], // contoh: jualan/dualan (ejaan lama)
y: ['y', 'j'], // contoh: ya/ja
u: ['u', 'oe'], // contoh: untuk/oentoek (ejaan lama)
f: ['f', 'p', 'v'], // contoh: kafir/kapir
z: ['z', 'j', 's'], // contoh: zaman/jaman
x: ['x', 'ks'], // contoh: taxi/taksi
};
// Go through each character in the pattern and replace with variations
let result = '';
for (const char of pattern) {
const lowerChar = char.toLowerCase();
const variations = variationMap[lowerChar];
if (variations && variations.length > 1) {
// Create a character class with all variations
result += `[${variations.join('')}]`;
} else {
result += char;
}
}
return result;
}
/**
* Membuat regex untuk mencocokkan kata dengan mempertimbangkan variasi ejaan Bahasa Indonesia
*
* @param word Kata yang akan dibuat pola regexnya
* @returns Objek RegExp
*/
export function createIndonesianVariationRegex(word: string): RegExp {
const escapedWord = escapeRegExp(word);
const pattern = addIndonesianVariations(escapedWord);
return new RegExp(`\\b${pattern}\\b`, 'gi');
}
/**
* Membuat regex untuk mencocokkan kata dengan konteks
*
* @param word Kata yang akan dibuat pola regexnya
* @param contextSize Jumlah kata konteks sebelum dan sesudah
* @returns Objek RegExp
*/
export function createContextRegex(word: string, contextSize: number = 3): RegExp {
const wordPattern = escapeRegExp(word);
// Membuat pola yang menangkap beberapa kata sebelum dan setelah kata target
const pattern = `((?:\\S+\\s+){0,${contextSize}})(\\b${wordPattern}\\b)((?:\\s+\\S+){0,${contextSize}})`;
return new RegExp(pattern, 'gi');
}
/**
* Membuat regex untuk mencocokkan variasi penulisan kata
*
* @param word Kata dasar
* @returns Objek RegExp untuk mencocokkan berbagai bentuk kata
*/
export function createWordFormRegex(word: string): RegExp {
// Implementasi sederhana untuk mencocokkan berbagai imbuhan
const escapedWord = escapeRegExp(word);
// Common Indonesian prefixes and suffixes
const prefixes = [
'',
'me',
'pe',
'ber',
'di',
'ter',
'se',
'ke',
'mem',
'pem',
'bel',
'peng',
'meng',
];
const suffixes = ['', 'kan', 'an', 'i', 'nya', 'lah', 'kah'];
const patterns = [];
for (const prefix of prefixes) {
for (const suffix of suffixes) {
patterns.push(`\\b${prefix}${escapedWord}${suffix}\\b`);
}
}
if (/^[aiueo]/.test(word)) {
patterns.push(`\\bng${escapedWord}\\b`);
}
return new RegExp(patterns.join('|'), 'gi');
}