@sideid/id-profanity-filter 1.9.6 → 1.10.2

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