Various changes
[python_utils.git] / profanity_filter.py
1 #!/usr/bin/env python3
2
3 import logging
4 import random
5 import string
6 import sys
7
8 import nltk
9 from nltk.stem import PorterStemmer
10
11 import string_utils
12
13
14 logger = logging.getLogger(__name__)
15
16
17 class ProfanityFilter(object):
18     def __init__(self):
19         self.bad_words = set([
20             'acrotomophilia',
21             'anal',
22             'analingus',
23             'anally',
24             'anilingus',
25             'anus',
26             'arsehol',
27             'arsehole',
28             'ass',
29             'asses',
30             'asshol',
31             'asshole',
32             'assmunch',
33             'auto erot',
34             'auto erotic',
35             'autoerotic',
36             'babeland',
37             'babi batter',
38             'baby batter',
39             'ball gag',
40             'ball gravi',
41             'ball gravy',
42             'ball kick',
43             'ball kicking',
44             'ball lick',
45             'ball licking',
46             'ball sack',
47             'ball suck',
48             'ball sucking',
49             'ball zack',
50             'bangbro',
51             'bangbros',
52             'bare legal',
53             'bareback',
54             'barely legal',
55             'barenak',
56             'barenaked',
57             'bastardo',
58             'bastinado',
59             'bbc',
60             'bbw',
61             'bdsm',
62             'beaver cleaver',
63             'beaver lip',
64             'beaver lips',
65             'bestial',
66             'bestiality',
67             'bi curiou',
68             'bi curious',
69             'big black',
70             'big breasts',
71             'big knocker',
72             'big knockers',
73             'big tit',
74             'big tits',
75             'bimbo',
76             'birdlock',
77             'bitch',
78             'bitches',
79             'black cock',
80             'blond action',
81             'blond on blond',
82             'blonde action',
83             'blow j',
84             'blow job',
85             'blow my',
86             'blow me',
87             'blow ourselv',
88             'blow ourselves',
89             'blow your load',
90             'blue waffl',
91             'blue waffle',
92             'blumpkin',
93             'bollock',
94             'bollocks',
95             'bondag',
96             'bondage',
97             'boner',
98             'boob',
99             'boobs',
100             'booti call',
101             'booty call',
102             'breast',
103             'breasts',
104             'brown shower',
105             'brown showers',
106             'brunett action',
107             'brunette action',
108             'bukkak',
109             'bukkake',
110             'bulldyk',
111             'bulldyke',
112             'bullet vibe',
113             'bullshit',
114             'bung hole',
115             'bunghol',
116             'bunghole',
117             'busti',
118             'busty',
119             'butt',
120             'buttcheek',
121             'buttcheeks',
122             'butthol',
123             'butthole',
124             'camel toe',
125             'camgirl',
126             'camslut',
127             'camwhore',
128             'carpet muncher',
129             'carpetmuncher',
130             'chocol rosebud',
131             'chocolate rosebuds',
132             'circlejerk',
133             'chink',
134             'cleveland steamer',
135             'clit',
136             'clitori',
137             'clitoris',
138             'clover clamp',
139             'clover clamps',
140             'clusterfuck',
141             'cock',
142             'cocks',
143             'coprolagnia',
144             'coprophilia',
145             'cornhol',
146             'cornhole',
147             'cream pie',
148             'creampi',
149             'creampie',
150             'cum',
151             'cumming',
152             'cunnilingu',
153             'cunnilingus',
154             'cunt',
155             'damn',
156             'darki',
157             'darkie',
158             'date rape',
159             'daterap',
160             'daterape',
161             'deep throat',
162             'deepthroat',
163             'dick',
164             'dildo',
165             'dirti pillow',
166             'dirti sanchez',
167             'dirty pillow',
168             'dirty sanchez',
169             'dog style',
170             'doggi style',
171             'doggie style',
172             'doggiestyl',
173             'doggiestyle',
174             'doggystyle',
175             'dolcett',
176             'domination',
177             'dominatrix',
178             'domm',
179             'dommes',
180             'donkey punch',
181             'doubl dick',
182             'doubl dong',
183             'doubl penetr',
184             'double dick',
185             'double dong',
186             'double penetration',
187             'dp action',
188             'dtf',
189             'eat my ass',
190             'ecchi',
191             'ejacul',
192             'erection',
193             'erotic',
194             'erotism',
195             'escort',
196             'ethical slut',
197             'eunuch',
198             'faggot',
199             'fecal',
200             'felch',
201             'fellatio',
202             'feltch',
203             'female squirting',
204             'femdom',
205             'figging',
206             'fingered',
207             'fingering',
208             'fingers',
209             'fisted',
210             'fisting',
211             'fists',
212             'foot fetish',
213             'footjob',
214             'frotting',
215             'fuck button',
216             'fuck',
217             'fucked',
218             'fucker',
219             'fuckhead',
220             'fuckin',
221             'fucking',
222             'fudge packer',
223             'fudgepack',
224             'fudgepacker',
225             'futanari',
226             'g spot',
227             'g-spot',
228             'gang bang',
229             'gay sex',
230             'gee spot',
231             'genital',
232             'giant cock',
233             'girl gone wild',
234             'girl on top',
235             'girl on',
236             'goatcx',
237             'goatse',
238             'goddamn',
239             'gokkun',
240             'golden shower',
241             'goo girl',
242             'goodpoop',
243             'goregasm',
244             'grope',
245             'group sex',
246             'gspot',
247             'guro',
248             'hand job',
249             'handjob',
250             'hard core',
251             'hardcore',
252             'hentai',
253             'homoerotic',
254             'honkey',
255             'hooker',
256             'horni',
257             'horny',
258             'hot chick',
259             'how to kill',
260             'how to murder',
261             'huge fat',
262             'humped',
263             'humping',
264             'humps',
265             'incest',
266             'intercourse',
267             'jack off',
268             'jail bait',
269             'jailbait',
270             'jerk off',
271             'jigaboo',
272             'jiggaboo',
273             'jiggerboo',
274             'jizz',
275             'jugg',
276             'kike',
277             'kinbaku',
278             'kinkster',
279             'kinky',
280             'knobbing',
281             'leather restraint',
282             'lemon party',
283             'lolita',
284             'lovemaking',
285             'make me come',
286             'male squirting',
287             'masturb',
288             'menage a trois',
289             'milf',
290             'missionary position',
291             'motherfuck',
292             'mound of venus',
293             'mr hand',
294             'muff diver',
295             'muffdiv',
296             'muffdiving',
297             'nambla',
298             'nawashi',
299             'negro',
300             'neonazi',
301             'nig nog',
302             'nigga',
303             'nigger',
304             'nimphomania',
305             'nipple',
306             'nip',
307             'not safe for',
308             'nsfl',
309             'nsfw',
310             'nude',
311             'nudes',
312             'nudity',
313             'nut sack',
314             'nutsack',
315             'nympho',
316             'nymphomania',
317             'octopussy',
318             'omorashi',
319             'one night stand',
320             'orgasm',
321             'orgy',
322             'paedophil',
323             'paedophile',
324             'panties',
325             'panty',
326             'pedobear',
327             'pedophil',
328             'pedophile',
329             'pee',
330             'pegging',
331             'peni',
332             'penis',
333             'phone sex',
334             'pigfucker',
335             'piss pig',
336             'piss',
337             'pissing',
338             'pisspig',
339             'playboy',
340             'pleasure chest',
341             'pole smoker',
342             'ponyplay',
343             'poof',
344             'poop chute',
345             'poopchute',
346             'porn',
347             'pornhub',
348             'porno',
349             'pornographi',
350             'pornography',
351             'prince albert',
352             'pthc',
353             'pube',
354             'pussi',
355             'pussies',
356             'pussy',
357             'queaf',
358             'queer',
359             'raghead',
360             'raging boner',
361             'rape',
362             'raping',
363             'rapist',
364             'rectum',
365             'reverse cowgirl',
366             'rimjob',
367             'rimming',
368             'rosy palm',
369             'rusty trombone',
370             's & m',
371             's&m',
372             's+m',
373             'sadism',
374             'scat',
375             'schlong',
376             'scissoring',
377             'semen',
378             'sex',
379             'sexi',
380             'sexo',
381             'sexy',
382             'shaved beaver',
383             'shaved pussy',
384             'shemale',
385             'shibari',
386             'shit',
387             'shota',
388             'shrimping',
389             'slanteye',
390             'slut',
391             'smut',
392             'snatch',
393             'snm',
394             'snowballing',
395             'sodomi',
396             'sodomize',
397             'sodomy',
398             'spic',
399             'spooge',
400             'spread legs',
401             'squirting',
402             'strap on',
403             'strapon',
404             'strappado',
405             'strip club',
406             'style doggy',
407             'suck',
408             'suicide girls',
409             'sultry women',
410             'swastika',
411             'swinger',
412             'taint',
413             'tainted love',
414             'taste my',
415             'tea bagging',
416             'threesome',
417             'throating',
418             'tied up',
419             'tight white',
420             'tit',
421             'tits',
422             'titti',
423             'titties',
424             'titty',
425             'tongue in',
426             'topless',
427             'tosser',
428             'towelhead',
429             'tranny',
430             'tribadism',
431             'tub girl',
432             'tubgirl',
433             'tushy',
434             'twat',
435             'twink',
436             'twinki',
437             'twinkie',
438             'undress',
439             'upskirt',
440             'urethra play',
441             'urophilia',
442             'vag',
443             'vagina',
444             'venus mound',
445             'vibrator',
446             'violet blue',
447             'violet wand',
448             'vorarephilia',
449             'voyeur',
450             'vulva',
451             'wank',
452             'wet dream',
453             'wetback',
454             'white power',
455             'whore',
456             'women rapping',
457             'wrapping men',
458             'wrinkled starfish',
459             'xx',
460             'xxx',
461             'yaoi',
462             'yellow shower',
463             'yiffy',
464             'zoophilia',
465         ])
466         self.stemmer = PorterStemmer()
467
468     def _normalize(self, text: str) -> str:
469         result = text.lower()
470         result = result.replace("_", " ")
471         for x in string.punctuation:
472             result = result.replace(x, "")
473         chunks = [
474             self.stemmer.stem(word) for word in nltk.word_tokenize(result)
475         ]
476         return ' '.join(chunks)
477
478     def contains_bad_word(self, text: str) -> bool:
479         words = nltk.word_tokenize(text)
480         for word in words:
481             if self.is_bad_word(word):
482                 logger.debug(f'"{word}" is profanity')
483                 return True
484
485         if len(words) > 1:
486             for bigram in string_utils.ngrams_presplit(words, 2):
487                 bigram = ' '.join(bigram)
488                 if self.is_bad_word(bigram):
489                     logger.debug('"{bigram}" is profanity')
490                     return True
491
492         if len(words) > 2:
493             for trigram in string_utils.ngrams_presplit(words, 3):
494                 trigram = ' '.join(trigram)
495                 if self.is_bad_word(trigram):
496                     logger.debug('"{trigram}" is profanity')
497                     return True
498         return False
499
500     def is_bad_word(self, word: str) -> bool:
501         return (
502             word in self.bad_words or
503             self._normalize(word) in self.bad_words
504         )
505
506     def obscure_bad_words(self, text: str) -> str:
507
508         def obscure(word: str):
509             out = ''
510             last = ''
511             for letter in word:
512                 if letter.isspace():
513                     out += letter
514                 else:
515                     while True:
516                         char = random.choice(['#', '%', '!', '@', '&', '*'])
517                         if last != char:
518                             last = char
519                             out += char
520                             break
521             return out
522
523         words = nltk.word_tokenize(text)
524         words.append('')
525         words.append('')
526         words.append('')
527         out = ''
528
529         cursor = 0
530         while cursor < len(words) - 3:
531             word = words[cursor]
532             bigram = word + ' ' + words[cursor + 1]
533             trigram = bigram + ' ' + words[cursor + 2]
534             if self.is_bad_word(trigram):
535                 out += obscure(trigram) + ' '
536                 cursor += 3
537             elif self.is_bad_word(bigram):
538                 out += obscure(bigram) + ' '
539                 cursor += 2
540             elif self.is_bad_word(word):
541                 out += obscure(word) + ' '
542                 cursor += 1
543             else:
544                 out += word + ' '
545                 cursor += 1
546         return out.strip()
547
548
549 def main() -> None:
550     pf = ProfanityFilter()
551     phrase = ' '.join(sys.argv[1:])
552     print(pf.contains_bad_word(phrase))
553     print(pf.obscure_bad_words(phrase))
554     sys.exit(0)
555
556
557 if __name__ == '__main__':
558     main()