@sideid/id-profanity-filter 1.11.0 → 1.11.5

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (47) hide show
  1. package/.eslintrc.js +44 -44
  2. package/.github/workflows/release.yml +62 -0
  3. package/CONTRIBUTING.md +150 -150
  4. package/LICENSE +21 -21
  5. package/README.md +548 -548
  6. package/dist/index.d.ts +989 -0
  7. package/dist/index.esm.js +9 -0
  8. package/dist/index.esm.js.map +1 -1
  9. package/dist/index.js +9 -0
  10. package/dist/index.js.map +1 -1
  11. package/dist/types/index.d.ts +375 -57
  12. package/dist/types/types/index.d.ts +59 -0
  13. package/eslint.config.mjs +40 -40
  14. package/examples/advanced.ts +120 -120
  15. package/examples/basic.ts +71 -71
  16. package/examples/custom-list.ts +140 -140
  17. package/jest.config.mjs +10 -10
  18. package/package.json +2 -1
  19. package/prettierrc +6 -6
  20. package/rollup.config.mjs +40 -35
  21. package/src/constants/regions/general.ts +9 -0
  22. package/src/index.ts +15 -15
  23. package/tsconfig.json +115 -115
  24. package/.github/workflows/ci.yml +0 -0
  25. package/dist/constants/categories/index.d.ts +0 -9
  26. package/dist/constants/regions/index.d.ts +0 -8
  27. /package/dist/{config → types/config}/options.d.ts +0 -0
  28. /package/dist/{constants → types/constants}/categories/blasphemy.d.ts +0 -0
  29. /package/dist/{constants → types/constants}/categories/disgusting.d.ts +0 -0
  30. /package/dist/{constants → types/constants}/categories/drugs.d.ts +0 -0
  31. /package/dist/{constants → types/constants}/categories/insult.d.ts +0 -0
  32. /package/dist/{constants → types/constants}/categories/profanity.d.ts +0 -0
  33. /package/dist/{constants → types/constants}/categories/sexual.d.ts +0 -0
  34. /package/dist/{constants → types/constants}/categories/slur.d.ts +0 -0
  35. /package/dist/{constants → types/constants}/regions/batak.d.ts +0 -0
  36. /package/dist/{constants → types/constants}/regions/betawi.d.ts +0 -0
  37. /package/dist/{constants → types/constants}/regions/general.d.ts +0 -0
  38. /package/dist/{constants → types/constants}/regions/jawa.d.ts +0 -0
  39. /package/dist/{constants → types/constants}/regions/sunda.d.ts +0 -0
  40. /package/dist/{constants → types/constants}/wordList.d.ts +0 -0
  41. /package/dist/{core → types/core}/analyzer.d.ts +0 -0
  42. /package/dist/{core → types/core}/filter.d.ts +0 -0
  43. /package/dist/{core → types/core}/matcher.d.ts +0 -0
  44. /package/dist/{utils → types/utils}/ahoCorasick.d.ts +0 -0
  45. /package/dist/{utils → types/utils}/regexUtils.d.ts +0 -0
  46. /package/dist/{utils → types/utils}/similarityUtils.d.ts +0 -0
  47. /package/dist/{utils → types/utils}/stringUtils.d.ts +0 -0
package/README.md CHANGED
@@ -1,548 +1,548 @@
1
- # ID-Profanity-Filter
2
-
3
- ![License](https://img.shields.io/npm/l/@sideid/id-profanity-filter)
4
- ![Version](https://img.shields.io/npm/v/@sideid/id-profanity-filter)
5
- ![Downloads](https://img.shields.io/npm/dt/@sideid/id-profanity-filter)
6
-
7
- Library JavaScript/TypeScript untuk mendeteksi, menyensor, dan menganalisis kata-kata kotor dalam Bahasa Indonesia dan bahasa daerah.
8
-
9
- ## Fitur Utama
10
-
11
- - 🔍 **Deteksi Kata Kotor** - Mendeteksi kata-kata kasar/kotor dalam teks Bahasa Indonesia
12
- - ⚠️ **Analisis Konten** - Menganalisis tingkat keparahan dan kategori kata kotor
13
- - 🔒 **Penyensoran** - Menyensor kata-kata kotor dengan berbagai opsi kustomisasi
14
- - 🗺️ **Dukungan Bahasa Daerah** - Mencakup kata-kata dari berbagai daerah di Indonesia (Jawa, Sunda, Batak, dll)
15
- - 🧠 **Deteksi Cerdas** - Mendeteksi variasi ejaan, kata terpisah, dan kesamaan kata
16
- - 🔠 **Deteksi Levenshtein** - Mendeteksi kata kotor yang dimodifikasi dengan typo atau sengaja disamarkan
17
- - 🛡️ **Preset** - Preset filter siap pakai untuk berbagai kebutuhan
18
- - 🔧 **Kustomisasi** - Opsi untuk menambahkan whitelist dan daftar kata kustom
19
-
20
- ## Instalasi
21
-
22
- ```bash
23
- npm install @sideid/id-profanity-filter
24
- # atau
25
- yarn add @sideid/id-profanity-filter
26
- # atau
27
- pnpm add @sideid/id-profanity-filter
28
- ```
29
-
30
- ## Penggunaan Dasar
31
-
32
- ### JavaScript / TypeScript
33
-
34
- ```typescript
35
- import IDProfanityFilter from '@sideid/id-profanity-filter';
36
-
37
- // Buat instance filter
38
- const filter = new IDProfanityFilter();
39
-
40
- // Cek apakah teks mengandung kata kotor
41
- const teks = 'Dasar anjing kamu, jangan banyak bacot!';
42
- const hasProfanity = filter.isProfane(teks);
43
-
44
- console.log(hasProfanity); // Output: true
45
-
46
- // Filter kata kotor (sensorisasi)
47
- const hasil = filter.filter(teks);
48
- console.log(hasil.filtered); // Output: "Dasar ***** kamu, jangan banyak *****!"
49
- console.log(hasil.censored); // Output: 2 (jumlah kata yang disensor)
50
-
51
- // Analisis konten
52
- const analisis = filter.analyze(teks);
53
- console.log(analisis.severityScore); // Output: 0.65 (contoh skor keparahan)
54
- console.log(analisis.categories); // Output: ["profanity", "insult"]
55
- ```
56
-
57
- ### Menggunakan Preset
58
-
59
- ```typescript
60
- import IDProfanityFilter from '@sideid/id-profanity-filter';
61
-
62
- // Buat instance filter dengan preset
63
- const filter = new IDProfanityFilter();
64
- filter.usePreset('strict');
65
-
66
- // Atau dengan opsi tambahan
67
- filter.usePreset('childSafe', {
68
- replaceWith: '#',
69
- useRandomGrawlix: true,
70
- });
71
-
72
- const teks = 'Dasar anjing kamu, jangan banyak bacot!';
73
- const hasil = filter.filter(teks);
74
- console.log(hasil.filtered); // Output: "Dasar #@$%& kamu, jangan banyak $!@#%!"
75
- ```
76
-
77
- ### Utilitas Langsung
78
-
79
- Anda juga dapat menggunakan fungsi utilitas langsung tanpa membuat instance:
80
-
81
- ```typescript
82
- import { idFilter } from '@sideid/id-profanity-filter';
83
-
84
- const teks = 'Dasar anjing kamu, jangan banyak bacot!';
85
- const hasil = idFilter.filter(teks);
86
- console.log(hasil.filtered); // Output: "Dasar ***** kamu, jangan banyak *****!"
87
-
88
- // Menggunakan preset dengan API fungsi statis
89
- const presetResult = idFilter.filter(
90
- teks,
91
- idFilter.getPresetOptions('moderate'),
92
- );
93
- ```
94
-
95
- ## Dokumentasi API
96
-
97
- ### Kelas `IDProfanityFilter`
98
-
99
- #### Konstruktor
100
-
101
- ```typescript
102
- constructor(options?: FilterOptions)
103
- ```
104
-
105
- - **options**: Opsi konfigurasi filter (opsional)
106
-
107
- #### Metode
108
-
109
- ##### `filter(text: string): FilterResult`
110
-
111
- Menyensor kata kotor dalam teks.
112
-
113
- - **Hasil**: Objek `FilterResult` dengan properti:
114
- - `filtered`: String teks yang sudah disensor
115
- - `censored`: Jumlah kata yang disensor
116
- - `replacements`: Array berisi detail kata yang disensor
117
-
118
- ##### `isProfane(text: string): boolean`
119
-
120
- Memeriksa apakah teks mengandung kata kotor.
121
-
122
- - **Hasil**: Boolean `true` jika teks mengandung kata kotor, `false` jika tidak
123
-
124
- ##### `analyze(text: string): AnalysisResult`
125
-
126
- Menganalisis teks untuk mendapatkan informasi detail tentang kata kotor yang ditemukan.
127
-
128
- - **Hasil**: Objek `AnalysisResult` dengan properti:
129
- - `hasProfanity`: Apakah teks mengandung kata kotor
130
- - `matches`: Array kata kotor yang ditemukan
131
- - `matchDetails`: Array objek kata kotor dengan metadata
132
- - `categories`: Kategori kata kotor yang ditemukan
133
- - `regions`: Daerah asal kata kotor yang ditemukan
134
- - `severityScore`: Skor keparahan (0-1)
135
- - `similarWords`: Kata-kata yang mirip dengan kata kotor (jika deteksi kesamaan diaktifkan)
136
-
137
- ##### `batchAnalyze(texts: string[])`
138
-
139
- Menganalisis batch teks sekaligus dan memberikan ringkasan.
140
-
141
- - **Hasil**: Objek yang berisi ringkasan analisis
142
-
143
- ##### `analyzeBySentence(text: string)`
144
-
145
- Menganalisis teks per-kalimat.
146
-
147
- - **Hasil**: Array dari hasil analisis per-kalimat
148
-
149
- ##### `analyzeWithContext(text: string, contextWindowSize?: number)`
150
-
151
- Menganalisis teks dengan konteks di sekitar kata kotor.
152
-
153
- - **Hasil**: Array dari kata kotor dengan konteks di sekitarnya
154
-
155
- ##### `usePreset(presetName: string, additionalOptions?: Partial<FilterOptions>)`
156
-
157
- Menggunakan preset filter yang telah ditentukan.
158
-
159
- - **presetName**: Nama preset ('strict', 'moderate', 'light', 'childSafe', dll)
160
- - **additionalOptions**: Opsi tambahan untuk override preset (opsional)
161
-
162
- ##### `setOptions(options: Partial<FilterOptions>)`
163
-
164
- Mengubah opsi filter.
165
-
166
- ##### `setWordList(wordList: string[])`
167
-
168
- Menetapkan daftar kata kustom.
169
-
170
- ##### `addToWhitelist(word: string)`
171
-
172
- Menambahkan kata ke whitelist (akan diabaikan dalam filter).
173
-
174
- ##### `removeFromWhitelist(word: string)`
175
-
176
- Menghapus kata dari whitelist.
177
-
178
- ##### `enableIndonesianVariations()`
179
-
180
- Mengaktifkan deteksi variasi ejaan Bahasa Indonesia.
181
-
182
- ##### `enableSplitWordDetection()`
183
-
184
- Mengaktifkan deteksi kata yang dipisah.
185
-
186
- ##### `enableSimilarityDetection(threshold: number = 0.8, useLevenshtein: boolean = false, maxLevenshteinDistance: number = 2)`
187
-
188
- Mengaktifkan deteksi berdasarkan kesamaan kata.
189
-
190
- - **threshold**: Ambang batas kesamaan (0-1, default: 0.8)
191
- - **useLevenshtein**: Gunakan algoritma Levenshtein untuk deteksi (default: false)
192
- - **maxLevenshteinDistance**: Jarak edit maksimum yang diizinkan (default: 2)
193
-
194
- ##### `enableLevenshteinDetection(threshold: number = 0.8, maxDistance: number = 2)`
195
-
196
- Mengaktifkan deteksi berbasis jarak Levenshtein.
197
-
198
- - **threshold**: Ambang batas kesamaan (0-1, default: 0.8)
199
- - **maxDistance**: Jarak edit maksimum yang diizinkan (default: 2)
200
-
201
- ### Opsi Filter
202
-
203
- ```typescript
204
- interface FilterOptions {
205
- // Opsi dasar
206
- wordList?: string[]; // List kata yang ingin difilter
207
- replaceWith?: string; // Karakter pengganti untuk kata yang disensor
208
- fullWordCensor?: boolean; // Apakah menyensor seluruh kata atau sebagian
209
- detectLeetSpeak?: boolean; // Deteksi variasi penulisan (mis: a=4, e=3)
210
- whitelist?: string[]; // Kata-kata yang dikecualikan dari filter
211
- checkSubstring?: boolean; // Memeriksa substring (lebih ketat)
212
- categories?: ProfanityCategory[]; // Kategori kata yang difilter
213
- regions?: Region[]; // Daerah asal kata yang difilter
214
- severityThreshold?: number; // Tingkat keparahan minimum (0-1)
215
-
216
- // Opsi lanjutan
217
- useRandomGrawlix?: boolean; // Gunakan karakter acak untuk sensor (#@$%&!)
218
- keepFirstAndLast?: boolean; // Simpan huruf pertama dan terakhir (a***g)
219
- indonesianVariation?: boolean; // Deteksi variasi ejaan Bahasa Indonesia
220
- detectSimilarity?: boolean; // Deteksi kata berdasarkan kesamaan
221
- similarityThreshold?: number; // Ambang batas kesamaan (0-1)
222
- detectSplit?: boolean; // Deteksi kata yang dipisah (a-n-j-i-n-g)
223
- useLevenshtein?: boolean; // Gunakan algoritma Levenshtein untuk deteksi kesamaan
224
- maxLevenshteinDistance?: number; // Jarak edit maksimum untuk deteksi Levenshtein
225
- }
226
- ```
227
-
228
- ## Preset Filter
229
-
230
- Library ini menyediakan beberapa preset yang dapat digunakan:
231
-
232
- ### Preset Filter
233
-
234
- - **strict**: Filter paling ketat, mendeteksi semua jenis kata kotor
235
- - **moderate**: Filter tingkat menengah, mengabaikan kata-kata dengan tingkat keparahan rendah
236
- - **light**: Filter ringan, hanya untuk kata-kata paling sensitif
237
- - **childSafe**: Filter untuk konten anak-anak, sangat ketat
238
-
239
- ### Preset Kategori
240
-
241
- - **sexual**: Hanya memfilter kata-kata berbau seksual
242
- - **insults**: Hanya memfilter kata-kata penghinaan
243
- - **profanity**: Hanya memfilter umpatan umum
244
-
245
- ### Preset Regional
246
-
247
- - **general**: Hanya memfilter kata-kata umum di Indonesia
248
- - **jawa**: Hanya memfilter kata-kata dari Jawa
249
- - **sunda**: Hanya memfilter kata-kata dari Sunda
250
- - **betawi**: Hanya memfilter kata-kata dari Betawi
251
- - **batak**: Hanya memfilter kata-kata dari Batak
252
-
253
- ## Contoh Penggunaan Lanjutan
254
-
255
- ### Kustomisasi Opsi Filter
256
-
257
- ```typescript
258
- const filter = new IDProfanityFilter({
259
- replaceWith: '#', // Menggunakan # sebagai karakter pengganti
260
- fullWordCensor: false, // Hanya menyensor sebagian kata
261
- detectLeetSpeak: true, // Mendeteksi variasi seperti "b4b1" untuk "babi"
262
- categories: ['sexual', 'slur'], // Hanya filter kategori tertentu
263
- regions: ['jawa', 'general'], // Hanya filter dari daerah tertentu
264
- severityThreshold: 0.7, // Hanya filter kata dengan tingkat keparahan ≥ 0.7
265
-
266
- // Opsi lanjutan
267
- useRandomGrawlix: true, // Gunakan #@$%&! sebagai karakter pengganti
268
- keepFirstAndLast: true, // Simpan huruf pertama dan terakhir (a***g)
269
- indonesianVariation: true, // Deteksi variasi ejaan Indonesia
270
- detectSimilarity: true, // Deteksi kata yang mirip
271
- similarityThreshold: 0.8, // Ambang batas kesamaan
272
- detectSplit: true, // Deteksi kata yang dipisah
273
- useLevenshtein: true, // Gunakan algoritma Levenshtein
274
- maxLevenshteinDistance: 2, // Jarak Levenshtein maksimum
275
- });
276
- ```
277
-
278
- ### Menggunakan Whitelist
279
-
280
- ```typescript
281
- const filter = new IDProfanityFilter();
282
-
283
- // Menambahkan kata ke whitelist (akan diabaikan)
284
- filter.addToWhitelist('anjing');
285
-
286
- const teks =
287
- 'Anjing itu hewan peliharaan yang setia, tidak seperti bajingan itu';
288
- const hasil = filter.filter(teks);
289
-
290
- console.log(hasil.filtered);
291
- // Output: "Anjing itu hewan peliharaan yang setia, tidak seperti ******* itu"
292
- ```
293
-
294
- ### Deteksi Variasi Ejaan dan Kata Terpisah
295
-
296
- ```typescript
297
- // Aktifkan deteksi variasi ejaan dan kata terpisah
298
- const filter = new IDProfanityFilter({
299
- indonesianVariation: true,
300
- detectSplit: true,
301
- detectLeetSpeak: true,
302
- });
303
-
304
- // Uji dengan variasi ejaan
305
- const teks1 = 'Dasar kamu ini sangat bodoh, benar-benar b0d0h dan b-o-d-o-h!';
306
- const hasil1 = filter.filter(teks1);
307
- console.log(hasil1.filtered);
308
- // Output: "Dasar kamu ini sangat *****, benar-benar ***** dan *********!"
309
-
310
- // Uji dengan ejaan Indonesia yang berbeda
311
- const teks2 = 'Dia sangat djail dan djudes dengan temannya';
312
- const hasil2 = filter.filter(teks2);
313
- console.log(hasil2.filtered);
314
- // Output: "Dia sangat ***** dan ****** dengan temannya"
315
- ```
316
-
317
- ### Deteksi Berdasarkan Kesamaan (Similarity Detection)
318
-
319
- ```typescript
320
- // Aktifkan deteksi kesamaan standar
321
- const filter = new IDProfanityFilter();
322
- filter.enableSimilarityDetection(0.75); // Set threshold kesamaan ke 0.75
323
-
324
- const teks = 'Dia benar-benar anjiing dan gooblok!';
325
- const analisis = filter.analyze(teks);
326
-
327
- console.log(analisis.similarWords);
328
- /* Output:
329
- [
330
- { word: "anjiing", original: "anjing", similarity: 0.83 },
331
- { word: "gooblok", original: "goblok", similarity: 0.85 }
332
- ]
333
- */
334
-
335
- const hasil = filter.filter(teks);
336
- console.log(hasil.filtered);
337
- // Output: "Dia benar-benar ****** dan *******!"
338
- ```
339
-
340
- ### Deteksi dengan Algoritma Levenshtein Distance
341
-
342
- ```typescript
343
- // Aktifkan deteksi menggunakan algoritma Levenshtein Distance
344
- const filter = new IDProfanityFilter();
345
- filter.enableLevenshteinDetection(0.85, 2);
346
- // Threshold 0.85, maksimal 2 karakter berbeda
347
-
348
- const teks = 'Dia benar-benar konntol dan anjiing sekali!';
349
- const hasil = filter.filter(teks);
350
-
351
- console.log(hasil.filtered);
352
- // Output: "Dia benar-benar ******* dan ****** sekali!"
353
-
354
- // Atau menggunakan opsi langsung:
355
- const filterCustom = new IDProfanityFilter({
356
- detectSimilarity: true,
357
- useLevenshtein: true,
358
- similarityThreshold: 0.85,
359
- maxLevenshteinDistance: 2,
360
- });
361
-
362
- // Mendeteksi typo atau variasi disengaja
363
- const teksVariasi = 'kontool kamu kwontol anjiing';
364
- console.log(filterCustom.filter(teksVariasi).filtered);
365
- // Output: "******* kamu ******* ******"
366
- ```
367
-
368
- ### Variasi Sensor Kata
369
-
370
- ```typescript
371
- // Sensor standar (asterisk)
372
- const filter1 = new IDProfanityFilter();
373
- console.log(filter1.filter('Dasar anjing kamu!').filtered);
374
- // Output: "Dasar ***** kamu!"
375
-
376
- // Sensor dengan grawlix random
377
- const filter2 = new IDProfanityFilter({ useRandomGrawlix: true });
378
- console.log(filter2.filter('Dasar anjing kamu!').filtered);
379
- // Output: "Dasar #@$%& kamu!"
380
-
381
- // Sensor dengan menyimpan huruf pertama dan terakhir
382
- const filter3 = new IDProfanityFilter({
383
- fullWordCensor: false,
384
- keepFirstAndLast: true,
385
- });
386
- console.log(filter3.filter('Dasar anjing kamu!').filtered);
387
- // Output: "Dasar a***g kamu!"
388
- ```
389
-
390
- ### Analisis Konten Mendalam
391
-
392
- ```typescript
393
- const filter = new IDProfanityFilter();
394
- const teks = 'Dasar anjing sialan! Kamu ini memang bego dan goblok.';
395
-
396
- // Analisis teks
397
- const analisis = filter.analyze(teks);
398
- console.log(analisis);
399
-
400
- /* Output:
401
- {
402
- hasProfanity: true,
403
- matches: ['anjing', 'sialan', 'bego', 'goblok'],
404
- matchDetails: [
405
- { word: 'anjing', category: 'profanity', region: 'general', severity: 0.7, ... },
406
- { word: 'bego', category: 'insult', region: 'general', severity: 0.5, ... },
407
- ...
408
- ],
409
- categories: ['profanity', 'insult'],
410
- regions: ['general'],
411
- severityScore: 0.64
412
- }
413
- */
414
- ```
415
-
416
- ### Analisis Konteks
417
-
418
- ```typescript
419
- const filter = new IDProfanityFilter();
420
- const teks =
421
- 'Saya sangat marah dengan sikapnya, dia benar-benar anjing dan bajingan!';
422
-
423
- // Analisis konteks
424
- const konteks = filter.analyzeWithContext(teks, 3);
425
- console.log(konteks);
426
-
427
- /* Output:
428
- [
429
- {
430
- word: "anjing",
431
- context: "benar-benar anjing dan bajingan",
432
- position: { start: 43, end: 62 }
433
- },
434
- {
435
- word: "bajingan",
436
- context: "anjing dan bajingan!",
437
- position: { start: 54, end: 63 }
438
- }
439
- ]
440
- */
441
- ```
442
-
443
- ### Analisis Per-Kalimat
444
-
445
- ```typescript
446
- const filter = new IDProfanityFilter();
447
- const teks =
448
- 'Filmnya bagus sekali. Tetapi pemainnya seperti anjing, sangat buruk aktingnya.';
449
-
450
- // Analisis per-kalimat
451
- const kalimat = filter.analyzeBySentence(teks);
452
- console.log(
453
- kalimat.map(
454
- (k) =>
455
- k.sentence + (k.hasProfanity ? ' (Mengandung kata kotor)' : ' (Bersih)'),
456
- ),
457
- );
458
-
459
- /* Output:
460
- [
461
- "Filmnya bagus sekali. (Bersih)",
462
- "Tetapi pemainnya seperti anjing, sangat buruk aktingnya. (Mengandung kata kotor)"
463
- ]
464
- */
465
- ```
466
-
467
- ### Analisis Batch
468
-
469
- ```typescript
470
- const filter = new IDProfanityFilter();
471
- const komentar = [
472
- 'Film ini sangat bagus, ceritanya menarik sekali!',
473
- 'Dasar goblok, sialan kamu!',
474
- 'Anjing emang filmnya, sampah banget.',
475
- ];
476
-
477
- const hasil = filter.batchAnalyze(komentar);
478
- console.log(hasil);
479
-
480
- /* Output:
481
- {
482
- totalTexts: 3,
483
- profaneTexts: 2,
484
- cleanTexts: 1,
485
- averageSeverity: 0.62,
486
- topCategories: ['profanity', 'insult'],
487
- topRegions: ['general'],
488
- mostFrequentWords: [
489
- { word: 'anjing', count: 1 },
490
- { word: 'goblok', count: 1 },
491
- { word: 'sialan', count: 1 },
492
- { word: 'sampah', count: 1 }
493
- ]
494
- }
495
- */
496
- ```
497
-
498
- ## Dukungan Regional
499
-
500
- Library ini mendukung kata-kata kotor dari berbagai daerah di Indonesia:
501
-
502
- - 🇮🇩 **General** - Kata-kata yang umum di seluruh Indonesia
503
- - 🏝️ **Jawa** - Kata-kata dari bahasa Jawa
504
- - 🏞️ **Sunda** - Kata-kata dari bahasa Sunda
505
- - 🏙️ **Betawi** - Kata-kata dari bahasa Betawi
506
- - 🌋 **Batak** - Kata-kata dari bahasa Batak
507
-
508
- ## Kategori Kata
509
-
510
- Kata-kata dikelompokkan berdasarkan kategori:
511
-
512
- - `sexual`: Kata-kata berbau seksual
513
- - `insult`: Kata-kata penghinaan
514
- - `profanity`: Umpatan umum
515
- - `slur`: Perkataan merendahkan berdasarkan identitas
516
- - `drugs`: Terkait narkoba
517
- - `disgusting`: Kata-kata menjijikkan
518
- - `blasphemy`: Penistaan agama
519
-
520
- ## Berkontribusi
521
-
522
- Kami sangat menghargai kontribusi Anda! Untuk berkontribusi, silakan lihat [panduan kontribusi](CONTRIBUTING.md).
523
-
524
- ### Menambahkan Kata Baru
525
-
526
- Jika Anda ingin menambahkan kata baru ke database, silakan buat pull request dengan mengubah file yang sesuai di `src/constants/categories/` atau `src/constants/regions/`.
527
-
528
- Format untuk menambahkan kata baru:
529
-
530
- ```json
531
- {
532
- "word": "kata_kotor", // Kata yang akan difilter
533
- "category": "insult", // Kategori kata
534
- "region": "general", // Daerah asal kata
535
- "severity": 0.7, // Tingkat keparahan (0-1)
536
- "aliases": ["k4t4_kotor", "kata_k0t0r"], // Alias atau variasi umum
537
- "description": "Deskripsi tentang kata", // Penjelasan tentang kata (opsional)
538
- "context": "Konteks penggunaan kata" // Konteks penggunaan (opsional)
539
- }
540
- ```
541
-
542
- ## Lisensi
543
-
544
- Proyek ini dilisensikan di bawah [MIT License](LICENSE).
545
-
546
- ## Kontak & Dukungan
547
-
548
- Jika Anda memiliki pertanyaan atau saran, silakan buka issue di repositori GitHub kami.
1
+ # ID-Profanity-Filter
2
+
3
+ ![License](https://img.shields.io/npm/l/@sideid/id-profanity-filter)
4
+ ![Version](https://img.shields.io/npm/v/@sideid/id-profanity-filter)
5
+ ![Downloads](https://img.shields.io/npm/dt/@sideid/id-profanity-filter)
6
+
7
+ Library JavaScript/TypeScript untuk mendeteksi, menyensor, dan menganalisis kata-kata kotor dalam Bahasa Indonesia dan bahasa daerah.
8
+
9
+ ## Fitur Utama
10
+
11
+ - 🔍 **Deteksi Kata Kotor** - Mendeteksi kata-kata kasar/kotor dalam teks Bahasa Indonesia
12
+ - ⚠️ **Analisis Konten** - Menganalisis tingkat keparahan dan kategori kata kotor
13
+ - 🔒 **Penyensoran** - Menyensor kata-kata kotor dengan berbagai opsi kustomisasi
14
+ - 🗺️ **Dukungan Bahasa Daerah** - Mencakup kata-kata dari berbagai daerah di Indonesia (Jawa, Sunda, Batak, dll)
15
+ - 🧠 **Deteksi Cerdas** - Mendeteksi variasi ejaan, kata terpisah, dan kesamaan kata
16
+ - 🔠 **Deteksi Levenshtein** - Mendeteksi kata kotor yang dimodifikasi dengan typo atau sengaja disamarkan
17
+ - 🛡️ **Preset** - Preset filter siap pakai untuk berbagai kebutuhan
18
+ - 🔧 **Kustomisasi** - Opsi untuk menambahkan whitelist dan daftar kata kustom
19
+
20
+ ## Instalasi
21
+
22
+ ```bash
23
+ npm install @sideid/id-profanity-filter
24
+ # atau
25
+ yarn add @sideid/id-profanity-filter
26
+ # atau
27
+ pnpm add @sideid/id-profanity-filter
28
+ ```
29
+
30
+ ## Penggunaan Dasar
31
+
32
+ ### JavaScript / TypeScript
33
+
34
+ ```typescript
35
+ import IDProfanityFilter from '@sideid/id-profanity-filter';
36
+
37
+ // Buat instance filter
38
+ const filter = new IDProfanityFilter();
39
+
40
+ // Cek apakah teks mengandung kata kotor
41
+ const teks = 'Dasar anjing kamu, jangan banyak bacot!';
42
+ const hasProfanity = filter.isProfane(teks);
43
+
44
+ console.log(hasProfanity); // Output: true
45
+
46
+ // Filter kata kotor (sensorisasi)
47
+ const hasil = filter.filter(teks);
48
+ console.log(hasil.filtered); // Output: "Dasar ***** kamu, jangan banyak *****!"
49
+ console.log(hasil.censored); // Output: 2 (jumlah kata yang disensor)
50
+
51
+ // Analisis konten
52
+ const analisis = filter.analyze(teks);
53
+ console.log(analisis.severityScore); // Output: 0.65 (contoh skor keparahan)
54
+ console.log(analisis.categories); // Output: ["profanity", "insult"]
55
+ ```
56
+
57
+ ### Menggunakan Preset
58
+
59
+ ```typescript
60
+ import IDProfanityFilter from '@sideid/id-profanity-filter';
61
+
62
+ // Buat instance filter dengan preset
63
+ const filter = new IDProfanityFilter();
64
+ filter.usePreset('strict');
65
+
66
+ // Atau dengan opsi tambahan
67
+ filter.usePreset('childSafe', {
68
+ replaceWith: '#',
69
+ useRandomGrawlix: true,
70
+ });
71
+
72
+ const teks = 'Dasar anjing kamu, jangan banyak bacot!';
73
+ const hasil = filter.filter(teks);
74
+ console.log(hasil.filtered); // Output: "Dasar #@$%& kamu, jangan banyak $!@#%!"
75
+ ```
76
+
77
+ ### Utilitas Langsung
78
+
79
+ Anda juga dapat menggunakan fungsi utilitas langsung tanpa membuat instance:
80
+
81
+ ```typescript
82
+ import { idFilter } from '@sideid/id-profanity-filter';
83
+
84
+ const teks = 'Dasar anjing kamu, jangan banyak bacot!';
85
+ const hasil = idFilter.filter(teks);
86
+ console.log(hasil.filtered); // Output: "Dasar ***** kamu, jangan banyak *****!"
87
+
88
+ // Menggunakan preset dengan API fungsi statis
89
+ const presetResult = idFilter.filter(
90
+ teks,
91
+ idFilter.getPresetOptions('moderate'),
92
+ );
93
+ ```
94
+
95
+ ## Dokumentasi API
96
+
97
+ ### Kelas `IDProfanityFilter`
98
+
99
+ #### Konstruktor
100
+
101
+ ```typescript
102
+ constructor(options?: FilterOptions)
103
+ ```
104
+
105
+ - **options**: Opsi konfigurasi filter (opsional)
106
+
107
+ #### Metode
108
+
109
+ ##### `filter(text: string): FilterResult`
110
+
111
+ Menyensor kata kotor dalam teks.
112
+
113
+ - **Hasil**: Objek `FilterResult` dengan properti:
114
+ - `filtered`: String teks yang sudah disensor
115
+ - `censored`: Jumlah kata yang disensor
116
+ - `replacements`: Array berisi detail kata yang disensor
117
+
118
+ ##### `isProfane(text: string): boolean`
119
+
120
+ Memeriksa apakah teks mengandung kata kotor.
121
+
122
+ - **Hasil**: Boolean `true` jika teks mengandung kata kotor, `false` jika tidak
123
+
124
+ ##### `analyze(text: string): AnalysisResult`
125
+
126
+ Menganalisis teks untuk mendapatkan informasi detail tentang kata kotor yang ditemukan.
127
+
128
+ - **Hasil**: Objek `AnalysisResult` dengan properti:
129
+ - `hasProfanity`: Apakah teks mengandung kata kotor
130
+ - `matches`: Array kata kotor yang ditemukan
131
+ - `matchDetails`: Array objek kata kotor dengan metadata
132
+ - `categories`: Kategori kata kotor yang ditemukan
133
+ - `regions`: Daerah asal kata kotor yang ditemukan
134
+ - `severityScore`: Skor keparahan (0-1)
135
+ - `similarWords`: Kata-kata yang mirip dengan kata kotor (jika deteksi kesamaan diaktifkan)
136
+
137
+ ##### `batchAnalyze(texts: string[])`
138
+
139
+ Menganalisis batch teks sekaligus dan memberikan ringkasan.
140
+
141
+ - **Hasil**: Objek yang berisi ringkasan analisis
142
+
143
+ ##### `analyzeBySentence(text: string)`
144
+
145
+ Menganalisis teks per-kalimat.
146
+
147
+ - **Hasil**: Array dari hasil analisis per-kalimat
148
+
149
+ ##### `analyzeWithContext(text: string, contextWindowSize?: number)`
150
+
151
+ Menganalisis teks dengan konteks di sekitar kata kotor.
152
+
153
+ - **Hasil**: Array dari kata kotor dengan konteks di sekitarnya
154
+
155
+ ##### `usePreset(presetName: string, additionalOptions?: Partial<FilterOptions>)`
156
+
157
+ Menggunakan preset filter yang telah ditentukan.
158
+
159
+ - **presetName**: Nama preset ('strict', 'moderate', 'light', 'childSafe', dll)
160
+ - **additionalOptions**: Opsi tambahan untuk override preset (opsional)
161
+
162
+ ##### `setOptions(options: Partial<FilterOptions>)`
163
+
164
+ Mengubah opsi filter.
165
+
166
+ ##### `setWordList(wordList: string[])`
167
+
168
+ Menetapkan daftar kata kustom.
169
+
170
+ ##### `addToWhitelist(word: string)`
171
+
172
+ Menambahkan kata ke whitelist (akan diabaikan dalam filter).
173
+
174
+ ##### `removeFromWhitelist(word: string)`
175
+
176
+ Menghapus kata dari whitelist.
177
+
178
+ ##### `enableIndonesianVariations()`
179
+
180
+ Mengaktifkan deteksi variasi ejaan Bahasa Indonesia.
181
+
182
+ ##### `enableSplitWordDetection()`
183
+
184
+ Mengaktifkan deteksi kata yang dipisah.
185
+
186
+ ##### `enableSimilarityDetection(threshold: number = 0.8, useLevenshtein: boolean = false, maxLevenshteinDistance: number = 2)`
187
+
188
+ Mengaktifkan deteksi berdasarkan kesamaan kata.
189
+
190
+ - **threshold**: Ambang batas kesamaan (0-1, default: 0.8)
191
+ - **useLevenshtein**: Gunakan algoritma Levenshtein untuk deteksi (default: false)
192
+ - **maxLevenshteinDistance**: Jarak edit maksimum yang diizinkan (default: 2)
193
+
194
+ ##### `enableLevenshteinDetection(threshold: number = 0.8, maxDistance: number = 2)`
195
+
196
+ Mengaktifkan deteksi berbasis jarak Levenshtein.
197
+
198
+ - **threshold**: Ambang batas kesamaan (0-1, default: 0.8)
199
+ - **maxDistance**: Jarak edit maksimum yang diizinkan (default: 2)
200
+
201
+ ### Opsi Filter
202
+
203
+ ```typescript
204
+ interface FilterOptions {
205
+ // Opsi dasar
206
+ wordList?: string[]; // List kata yang ingin difilter
207
+ replaceWith?: string; // Karakter pengganti untuk kata yang disensor
208
+ fullWordCensor?: boolean; // Apakah menyensor seluruh kata atau sebagian
209
+ detectLeetSpeak?: boolean; // Deteksi variasi penulisan (mis: a=4, e=3)
210
+ whitelist?: string[]; // Kata-kata yang dikecualikan dari filter
211
+ checkSubstring?: boolean; // Memeriksa substring (lebih ketat)
212
+ categories?: ProfanityCategory[]; // Kategori kata yang difilter
213
+ regions?: Region[]; // Daerah asal kata yang difilter
214
+ severityThreshold?: number; // Tingkat keparahan minimum (0-1)
215
+
216
+ // Opsi lanjutan
217
+ useRandomGrawlix?: boolean; // Gunakan karakter acak untuk sensor (#@$%&!)
218
+ keepFirstAndLast?: boolean; // Simpan huruf pertama dan terakhir (a***g)
219
+ indonesianVariation?: boolean; // Deteksi variasi ejaan Bahasa Indonesia
220
+ detectSimilarity?: boolean; // Deteksi kata berdasarkan kesamaan
221
+ similarityThreshold?: number; // Ambang batas kesamaan (0-1)
222
+ detectSplit?: boolean; // Deteksi kata yang dipisah (a-n-j-i-n-g)
223
+ useLevenshtein?: boolean; // Gunakan algoritma Levenshtein untuk deteksi kesamaan
224
+ maxLevenshteinDistance?: number; // Jarak edit maksimum untuk deteksi Levenshtein
225
+ }
226
+ ```
227
+
228
+ ## Preset Filter
229
+
230
+ Library ini menyediakan beberapa preset yang dapat digunakan:
231
+
232
+ ### Preset Filter
233
+
234
+ - **strict**: Filter paling ketat, mendeteksi semua jenis kata kotor
235
+ - **moderate**: Filter tingkat menengah, mengabaikan kata-kata dengan tingkat keparahan rendah
236
+ - **light**: Filter ringan, hanya untuk kata-kata paling sensitif
237
+ - **childSafe**: Filter untuk konten anak-anak, sangat ketat
238
+
239
+ ### Preset Kategori
240
+
241
+ - **sexual**: Hanya memfilter kata-kata berbau seksual
242
+ - **insults**: Hanya memfilter kata-kata penghinaan
243
+ - **profanity**: Hanya memfilter umpatan umum
244
+
245
+ ### Preset Regional
246
+
247
+ - **general**: Hanya memfilter kata-kata umum di Indonesia
248
+ - **jawa**: Hanya memfilter kata-kata dari Jawa
249
+ - **sunda**: Hanya memfilter kata-kata dari Sunda
250
+ - **betawi**: Hanya memfilter kata-kata dari Betawi
251
+ - **batak**: Hanya memfilter kata-kata dari Batak
252
+
253
+ ## Contoh Penggunaan Lanjutan
254
+
255
+ ### Kustomisasi Opsi Filter
256
+
257
+ ```typescript
258
+ const filter = new IDProfanityFilter({
259
+ replaceWith: '#', // Menggunakan # sebagai karakter pengganti
260
+ fullWordCensor: false, // Hanya menyensor sebagian kata
261
+ detectLeetSpeak: true, // Mendeteksi variasi seperti "b4b1" untuk "babi"
262
+ categories: ['sexual', 'slur'], // Hanya filter kategori tertentu
263
+ regions: ['jawa', 'general'], // Hanya filter dari daerah tertentu
264
+ severityThreshold: 0.7, // Hanya filter kata dengan tingkat keparahan ≥ 0.7
265
+
266
+ // Opsi lanjutan
267
+ useRandomGrawlix: true, // Gunakan #@$%&! sebagai karakter pengganti
268
+ keepFirstAndLast: true, // Simpan huruf pertama dan terakhir (a***g)
269
+ indonesianVariation: true, // Deteksi variasi ejaan Indonesia
270
+ detectSimilarity: true, // Deteksi kata yang mirip
271
+ similarityThreshold: 0.8, // Ambang batas kesamaan
272
+ detectSplit: true, // Deteksi kata yang dipisah
273
+ useLevenshtein: true, // Gunakan algoritma Levenshtein
274
+ maxLevenshteinDistance: 2, // Jarak Levenshtein maksimum
275
+ });
276
+ ```
277
+
278
+ ### Menggunakan Whitelist
279
+
280
+ ```typescript
281
+ const filter = new IDProfanityFilter();
282
+
283
+ // Menambahkan kata ke whitelist (akan diabaikan)
284
+ filter.addToWhitelist('anjing');
285
+
286
+ const teks =
287
+ 'Anjing itu hewan peliharaan yang setia, tidak seperti bajingan itu';
288
+ const hasil = filter.filter(teks);
289
+
290
+ console.log(hasil.filtered);
291
+ // Output: "Anjing itu hewan peliharaan yang setia, tidak seperti ******* itu"
292
+ ```
293
+
294
+ ### Deteksi Variasi Ejaan dan Kata Terpisah
295
+
296
+ ```typescript
297
+ // Aktifkan deteksi variasi ejaan dan kata terpisah
298
+ const filter = new IDProfanityFilter({
299
+ indonesianVariation: true,
300
+ detectSplit: true,
301
+ detectLeetSpeak: true,
302
+ });
303
+
304
+ // Uji dengan variasi ejaan
305
+ const teks1 = 'Dasar kamu ini sangat bodoh, benar-benar b0d0h dan b-o-d-o-h!';
306
+ const hasil1 = filter.filter(teks1);
307
+ console.log(hasil1.filtered);
308
+ // Output: "Dasar kamu ini sangat *****, benar-benar ***** dan *********!"
309
+
310
+ // Uji dengan ejaan Indonesia yang berbeda
311
+ const teks2 = 'Dia sangat djail dan djudes dengan temannya';
312
+ const hasil2 = filter.filter(teks2);
313
+ console.log(hasil2.filtered);
314
+ // Output: "Dia sangat ***** dan ****** dengan temannya"
315
+ ```
316
+
317
+ ### Deteksi Berdasarkan Kesamaan (Similarity Detection)
318
+
319
+ ```typescript
320
+ // Aktifkan deteksi kesamaan standar
321
+ const filter = new IDProfanityFilter();
322
+ filter.enableSimilarityDetection(0.75); // Set threshold kesamaan ke 0.75
323
+
324
+ const teks = 'Dia benar-benar anjiing dan gooblok!';
325
+ const analisis = filter.analyze(teks);
326
+
327
+ console.log(analisis.similarWords);
328
+ /* Output:
329
+ [
330
+ { word: "anjiing", original: "anjing", similarity: 0.83 },
331
+ { word: "gooblok", original: "goblok", similarity: 0.85 }
332
+ ]
333
+ */
334
+
335
+ const hasil = filter.filter(teks);
336
+ console.log(hasil.filtered);
337
+ // Output: "Dia benar-benar ****** dan *******!"
338
+ ```
339
+
340
+ ### Deteksi dengan Algoritma Levenshtein Distance
341
+
342
+ ```typescript
343
+ // Aktifkan deteksi menggunakan algoritma Levenshtein Distance
344
+ const filter = new IDProfanityFilter();
345
+ filter.enableLevenshteinDetection(0.85, 2);
346
+ // Threshold 0.85, maksimal 2 karakter berbeda
347
+
348
+ const teks = 'Dia benar-benar konntol dan anjiing sekali!';
349
+ const hasil = filter.filter(teks);
350
+
351
+ console.log(hasil.filtered);
352
+ // Output: "Dia benar-benar ******* dan ****** sekali!"
353
+
354
+ // Atau menggunakan opsi langsung:
355
+ const filterCustom = new IDProfanityFilter({
356
+ detectSimilarity: true,
357
+ useLevenshtein: true,
358
+ similarityThreshold: 0.85,
359
+ maxLevenshteinDistance: 2,
360
+ });
361
+
362
+ // Mendeteksi typo atau variasi disengaja
363
+ const teksVariasi = 'kontool kamu kwontol anjiing';
364
+ console.log(filterCustom.filter(teksVariasi).filtered);
365
+ // Output: "******* kamu ******* ******"
366
+ ```
367
+
368
+ ### Variasi Sensor Kata
369
+
370
+ ```typescript
371
+ // Sensor standar (asterisk)
372
+ const filter1 = new IDProfanityFilter();
373
+ console.log(filter1.filter('Dasar anjing kamu!').filtered);
374
+ // Output: "Dasar ***** kamu!"
375
+
376
+ // Sensor dengan grawlix random
377
+ const filter2 = new IDProfanityFilter({ useRandomGrawlix: true });
378
+ console.log(filter2.filter('Dasar anjing kamu!').filtered);
379
+ // Output: "Dasar #@$%& kamu!"
380
+
381
+ // Sensor dengan menyimpan huruf pertama dan terakhir
382
+ const filter3 = new IDProfanityFilter({
383
+ fullWordCensor: false,
384
+ keepFirstAndLast: true,
385
+ });
386
+ console.log(filter3.filter('Dasar anjing kamu!').filtered);
387
+ // Output: "Dasar a***g kamu!"
388
+ ```
389
+
390
+ ### Analisis Konten Mendalam
391
+
392
+ ```typescript
393
+ const filter = new IDProfanityFilter();
394
+ const teks = 'Dasar anjing sialan! Kamu ini memang bego dan goblok.';
395
+
396
+ // Analisis teks
397
+ const analisis = filter.analyze(teks);
398
+ console.log(analisis);
399
+
400
+ /* Output:
401
+ {
402
+ hasProfanity: true,
403
+ matches: ['anjing', 'sialan', 'bego', 'goblok'],
404
+ matchDetails: [
405
+ { word: 'anjing', category: 'profanity', region: 'general', severity: 0.7, ... },
406
+ { word: 'bego', category: 'insult', region: 'general', severity: 0.5, ... },
407
+ ...
408
+ ],
409
+ categories: ['profanity', 'insult'],
410
+ regions: ['general'],
411
+ severityScore: 0.64
412
+ }
413
+ */
414
+ ```
415
+
416
+ ### Analisis Konteks
417
+
418
+ ```typescript
419
+ const filter = new IDProfanityFilter();
420
+ const teks =
421
+ 'Saya sangat marah dengan sikapnya, dia benar-benar anjing dan bajingan!';
422
+
423
+ // Analisis konteks
424
+ const konteks = filter.analyzeWithContext(teks, 3);
425
+ console.log(konteks);
426
+
427
+ /* Output:
428
+ [
429
+ {
430
+ word: "anjing",
431
+ context: "benar-benar anjing dan bajingan",
432
+ position: { start: 43, end: 62 }
433
+ },
434
+ {
435
+ word: "bajingan",
436
+ context: "anjing dan bajingan!",
437
+ position: { start: 54, end: 63 }
438
+ }
439
+ ]
440
+ */
441
+ ```
442
+
443
+ ### Analisis Per-Kalimat
444
+
445
+ ```typescript
446
+ const filter = new IDProfanityFilter();
447
+ const teks =
448
+ 'Filmnya bagus sekali. Tetapi pemainnya seperti anjing, sangat buruk aktingnya.';
449
+
450
+ // Analisis per-kalimat
451
+ const kalimat = filter.analyzeBySentence(teks);
452
+ console.log(
453
+ kalimat.map(
454
+ (k) =>
455
+ k.sentence + (k.hasProfanity ? ' (Mengandung kata kotor)' : ' (Bersih)'),
456
+ ),
457
+ );
458
+
459
+ /* Output:
460
+ [
461
+ "Filmnya bagus sekali. (Bersih)",
462
+ "Tetapi pemainnya seperti anjing, sangat buruk aktingnya. (Mengandung kata kotor)"
463
+ ]
464
+ */
465
+ ```
466
+
467
+ ### Analisis Batch
468
+
469
+ ```typescript
470
+ const filter = new IDProfanityFilter();
471
+ const komentar = [
472
+ 'Film ini sangat bagus, ceritanya menarik sekali!',
473
+ 'Dasar goblok, sialan kamu!',
474
+ 'Anjing emang filmnya, sampah banget.',
475
+ ];
476
+
477
+ const hasil = filter.batchAnalyze(komentar);
478
+ console.log(hasil);
479
+
480
+ /* Output:
481
+ {
482
+ totalTexts: 3,
483
+ profaneTexts: 2,
484
+ cleanTexts: 1,
485
+ averageSeverity: 0.62,
486
+ topCategories: ['profanity', 'insult'],
487
+ topRegions: ['general'],
488
+ mostFrequentWords: [
489
+ { word: 'anjing', count: 1 },
490
+ { word: 'goblok', count: 1 },
491
+ { word: 'sialan', count: 1 },
492
+ { word: 'sampah', count: 1 }
493
+ ]
494
+ }
495
+ */
496
+ ```
497
+
498
+ ## Dukungan Regional
499
+
500
+ Library ini mendukung kata-kata kotor dari berbagai daerah di Indonesia:
501
+
502
+ - 🇮🇩 **General** - Kata-kata yang umum di seluruh Indonesia
503
+ - 🏝️ **Jawa** - Kata-kata dari bahasa Jawa
504
+ - 🏞️ **Sunda** - Kata-kata dari bahasa Sunda
505
+ - 🏙️ **Betawi** - Kata-kata dari bahasa Betawi
506
+ - 🌋 **Batak** - Kata-kata dari bahasa Batak
507
+
508
+ ## Kategori Kata
509
+
510
+ Kata-kata dikelompokkan berdasarkan kategori:
511
+
512
+ - `sexual`: Kata-kata berbau seksual
513
+ - `insult`: Kata-kata penghinaan
514
+ - `profanity`: Umpatan umum
515
+ - `slur`: Perkataan merendahkan berdasarkan identitas
516
+ - `drugs`: Terkait narkoba
517
+ - `disgusting`: Kata-kata menjijikkan
518
+ - `blasphemy`: Penistaan agama
519
+
520
+ ## Berkontribusi
521
+
522
+ Kami sangat menghargai kontribusi Anda! Untuk berkontribusi, silakan lihat [panduan kontribusi](CONTRIBUTING.md).
523
+
524
+ ### Menambahkan Kata Baru
525
+
526
+ Jika Anda ingin menambahkan kata baru ke database, silakan buat pull request dengan mengubah file yang sesuai di `src/constants/categories/` atau `src/constants/regions/`.
527
+
528
+ Format untuk menambahkan kata baru:
529
+
530
+ ```json
531
+ {
532
+ "word": "kata_kotor", // Kata yang akan difilter
533
+ "category": "insult", // Kategori kata
534
+ "region": "general", // Daerah asal kata
535
+ "severity": 0.7, // Tingkat keparahan (0-1)
536
+ "aliases": ["k4t4_kotor", "kata_k0t0r"], // Alias atau variasi umum
537
+ "description": "Deskripsi tentang kata", // Penjelasan tentang kata (opsional)
538
+ "context": "Konteks penggunaan kata" // Konteks penggunaan (opsional)
539
+ }
540
+ ```
541
+
542
+ ## Lisensi
543
+
544
+ Proyek ini dilisensikan di bawah [MIT License](LICENSE).
545
+
546
+ ## Kontak & Dukungan
547
+
548
+ Jika Anda memiliki pertanyaan atau saran, silakan buka issue di repositori GitHub kami.