bad-checker 0.1.0__tar.gz
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.
- bad_checker-0.1.0/PKG-INFO +55 -0
- bad_checker-0.1.0/README.md +44 -0
- bad_checker-0.1.0/pyproject.toml +16 -0
- bad_checker-0.1.0/setup.cfg +4 -0
- bad_checker-0.1.0/src/bad/__init__.py +102 -0
- bad_checker-0.1.0/src/bad_checker.egg-info/PKG-INFO +55 -0
- bad_checker-0.1.0/src/bad_checker.egg-info/SOURCES.txt +7 -0
- bad_checker-0.1.0/src/bad_checker.egg-info/dependency_links.txt +1 -0
- bad_checker-0.1.0/src/bad_checker.egg-info/top_level.txt +1 -0
|
@@ -0,0 +1,55 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: bad-checker
|
|
3
|
+
Version: 0.1.0
|
|
4
|
+
Summary: A typo-tolerant blocklist checker.
|
|
5
|
+
Author-email: coder5330 <hello@jldev.com>
|
|
6
|
+
Classifier: Programming Language :: Python :: 3
|
|
7
|
+
Classifier: License :: OSI Approved :: MIT License
|
|
8
|
+
Classifier: Operating System :: OS Independent
|
|
9
|
+
Requires-Python: >=3.7
|
|
10
|
+
Description-Content-Type: text/markdown
|
|
11
|
+
|
|
12
|
+
# bad-checker
|
|
13
|
+
|
|
14
|
+
A fuzzy, keyboard-typo- and leetspeak-aware content blocklist for Python.
|
|
15
|
+
|
|
16
|
+
Install name: `bad-checker` | Import name: `bad`
|
|
17
|
+
|
|
18
|
+
## Usage
|
|
19
|
+
|
|
20
|
+
```python
|
|
21
|
+
import bad
|
|
22
|
+
|
|
23
|
+
if "something bad" in bad:
|
|
24
|
+
print("Caught it!")
|
|
25
|
+
```
|
|
26
|
+
|
|
27
|
+
Matching combines:
|
|
28
|
+
- direct substring lookups against a blocklist
|
|
29
|
+
- leetspeak normalization (`v1agra` -> `viagra`, `fr33` -> `free`)
|
|
30
|
+
- a keyboard-proximity-aware Damerau-Levenshtein distance, so typos on
|
|
31
|
+
physically nearby keys (and adjacent-letter transpositions) are caught
|
|
32
|
+
cheaply while unrelated words are not
|
|
33
|
+
|
|
34
|
+
Add your own keywords at runtime:
|
|
35
|
+
|
|
36
|
+
```python
|
|
37
|
+
bad.add_keywords("mycompanyscam", "internaltermtoflag")
|
|
38
|
+
```
|
|
39
|
+
|
|
40
|
+
## Install
|
|
41
|
+
|
|
42
|
+
```bash
|
|
43
|
+
pip install bad-checker
|
|
44
|
+
```
|
|
45
|
+
|
|
46
|
+
## Build from source
|
|
47
|
+
|
|
48
|
+
```bash
|
|
49
|
+
pip install --upgrade build
|
|
50
|
+
python -m build
|
|
51
|
+
```
|
|
52
|
+
|
|
53
|
+
## License
|
|
54
|
+
|
|
55
|
+
MIT
|
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
# bad-checker
|
|
2
|
+
|
|
3
|
+
A fuzzy, keyboard-typo- and leetspeak-aware content blocklist for Python.
|
|
4
|
+
|
|
5
|
+
Install name: `bad-checker` | Import name: `bad`
|
|
6
|
+
|
|
7
|
+
## Usage
|
|
8
|
+
|
|
9
|
+
```python
|
|
10
|
+
import bad
|
|
11
|
+
|
|
12
|
+
if "something bad" in bad:
|
|
13
|
+
print("Caught it!")
|
|
14
|
+
```
|
|
15
|
+
|
|
16
|
+
Matching combines:
|
|
17
|
+
- direct substring lookups against a blocklist
|
|
18
|
+
- leetspeak normalization (`v1agra` -> `viagra`, `fr33` -> `free`)
|
|
19
|
+
- a keyboard-proximity-aware Damerau-Levenshtein distance, so typos on
|
|
20
|
+
physically nearby keys (and adjacent-letter transpositions) are caught
|
|
21
|
+
cheaply while unrelated words are not
|
|
22
|
+
|
|
23
|
+
Add your own keywords at runtime:
|
|
24
|
+
|
|
25
|
+
```python
|
|
26
|
+
bad.add_keywords("mycompanyscam", "internaltermtoflag")
|
|
27
|
+
```
|
|
28
|
+
|
|
29
|
+
## Install
|
|
30
|
+
|
|
31
|
+
```bash
|
|
32
|
+
pip install bad-checker
|
|
33
|
+
```
|
|
34
|
+
|
|
35
|
+
## Build from source
|
|
36
|
+
|
|
37
|
+
```bash
|
|
38
|
+
pip install --upgrade build
|
|
39
|
+
python -m build
|
|
40
|
+
```
|
|
41
|
+
|
|
42
|
+
## License
|
|
43
|
+
|
|
44
|
+
MIT
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
[build-system]
|
|
2
|
+
requires = ["setuptools>=61.0"]
|
|
3
|
+
build-backend = "setuptools.build_meta"
|
|
4
|
+
|
|
5
|
+
[project]
|
|
6
|
+
name = "bad-checker"
|
|
7
|
+
version = "0.1.0"
|
|
8
|
+
authors = [{ name="coder5330", email="hello@jldev.com" }]
|
|
9
|
+
description = "A typo-tolerant blocklist checker."
|
|
10
|
+
readme = "README.md"
|
|
11
|
+
requires-python = ">=3.7"
|
|
12
|
+
classifiers = [
|
|
13
|
+
"Programming Language :: Python :: 3",
|
|
14
|
+
"License :: OSI Approved :: MIT License",
|
|
15
|
+
"Operating System :: OS Independent",
|
|
16
|
+
]
|
|
@@ -0,0 +1,102 @@
|
|
|
1
|
+
import sys as _sys
|
|
2
|
+
import types as _types
|
|
3
|
+
|
|
4
|
+
__all__ = []
|
|
5
|
+
|
|
6
|
+
_ROWS = [
|
|
7
|
+
"`1234567890-=",
|
|
8
|
+
"qwertyuiop[]\\",
|
|
9
|
+
"asdfghjkl;'",
|
|
10
|
+
"zxcvbnm,./",
|
|
11
|
+
]
|
|
12
|
+
_ROW_OFFSETS = [0.0, 0.5, 0.75, 1.25]
|
|
13
|
+
|
|
14
|
+
_KEY_COORDS = {}
|
|
15
|
+
for _row, _text in enumerate(_ROWS):
|
|
16
|
+
for _col, _char in enumerate(_text):
|
|
17
|
+
_KEY_COORDS[_char] = (_col + _ROW_OFFSETS[_row], float(_row))
|
|
18
|
+
|
|
19
|
+
_LEET_TABLE = str.maketrans({
|
|
20
|
+
"0": "o", "1": "i", "3": "e", "4": "a", "5": "s",
|
|
21
|
+
"7": "t", "@": "a", "$": "s", "!": "i", "+": "t",
|
|
22
|
+
})
|
|
23
|
+
|
|
24
|
+
_DEFAULT_BLOCKLIST = [
|
|
25
|
+
"microsoft", "blocksi"
|
|
26
|
+
]
|
|
27
|
+
|
|
28
|
+
|
|
29
|
+
class _BadModule(_types.ModuleType):
|
|
30
|
+
@staticmethod
|
|
31
|
+
def _key_distance(c1, c2):
|
|
32
|
+
c1, c2 = c1.lower(), c2.lower()
|
|
33
|
+
if c1 == c2:
|
|
34
|
+
return 0.0
|
|
35
|
+
p1, p2 = _KEY_COORDS.get(c1), _KEY_COORDS.get(c2)
|
|
36
|
+
if p1 is None or p2 is None:
|
|
37
|
+
return 3.0
|
|
38
|
+
return ((p1[0] - p2[0]) ** 2 + (p1[1] - p2[1]) ** 2) ** 0.5
|
|
39
|
+
|
|
40
|
+
@classmethod
|
|
41
|
+
def _sub_cost(cls, c1, c2):
|
|
42
|
+
if c1.lower() == c2.lower():
|
|
43
|
+
return 0.0
|
|
44
|
+
dist = cls._key_distance(c1, c2)
|
|
45
|
+
return min(1.0, 0.3 + 0.2 * dist)
|
|
46
|
+
|
|
47
|
+
@classmethod
|
|
48
|
+
def _fuzzy_distance(cls, w1, w2):
|
|
49
|
+
m, n = len(w1), len(w2)
|
|
50
|
+
dp = [[0.0] * (n + 1) for _ in range(m + 1)]
|
|
51
|
+
for i in range(m + 1):
|
|
52
|
+
dp[i][0] = float(i)
|
|
53
|
+
for j in range(n + 1):
|
|
54
|
+
dp[0][j] = float(j)
|
|
55
|
+
|
|
56
|
+
for i in range(1, m + 1):
|
|
57
|
+
for j in range(1, n + 1):
|
|
58
|
+
cost = cls._sub_cost(w1[i - 1], w2[j - 1])
|
|
59
|
+
dp[i][j] = min(
|
|
60
|
+
dp[i - 1][j] + 1.0,
|
|
61
|
+
dp[i][j - 1] + 1.0,
|
|
62
|
+
dp[i - 1][j - 1] + cost,
|
|
63
|
+
)
|
|
64
|
+
if (i > 1 and j > 1
|
|
65
|
+
and w1[i - 1] == w2[j - 2]
|
|
66
|
+
and w1[i - 2] == w2[j - 1]):
|
|
67
|
+
dp[i][j] = min(dp[i][j], dp[i - 2][j - 2] + 0.6)
|
|
68
|
+
return dp[m][n]
|
|
69
|
+
|
|
70
|
+
@staticmethod
|
|
71
|
+
def _deleet(text):
|
|
72
|
+
return text.translate(_LEET_TABLE)
|
|
73
|
+
|
|
74
|
+
def __contains__(self, text):
|
|
75
|
+
if not isinstance(text, str):
|
|
76
|
+
return False
|
|
77
|
+
|
|
78
|
+
normalized = text.lower()
|
|
79
|
+
deleeted = self._deleet(normalized)
|
|
80
|
+
words = deleeted.split()
|
|
81
|
+
|
|
82
|
+
for keyword in self.banned_keywords:
|
|
83
|
+
keyword = keyword.lower()
|
|
84
|
+
|
|
85
|
+
if keyword in normalized or keyword in deleeted:
|
|
86
|
+
return True
|
|
87
|
+
|
|
88
|
+
if " " in keyword:
|
|
89
|
+
continue
|
|
90
|
+
|
|
91
|
+
for word in words:
|
|
92
|
+
if abs(len(word) - len(keyword)) <= 2:
|
|
93
|
+
if self._fuzzy_distance(word, keyword) <= 1.0:
|
|
94
|
+
return True
|
|
95
|
+
return False
|
|
96
|
+
|
|
97
|
+
def add_keywords(self, *keywords):
|
|
98
|
+
self.banned_keywords.extend(k.lower() for k in keywords)
|
|
99
|
+
|
|
100
|
+
_mod = _sys.modules[__name__]
|
|
101
|
+
_mod.__class__ = _BadModule
|
|
102
|
+
_mod.banned_keywords = list(_DEFAULT_BLOCKLIST)
|
|
@@ -0,0 +1,55 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: bad-checker
|
|
3
|
+
Version: 0.1.0
|
|
4
|
+
Summary: A typo-tolerant blocklist checker.
|
|
5
|
+
Author-email: coder5330 <hello@jldev.com>
|
|
6
|
+
Classifier: Programming Language :: Python :: 3
|
|
7
|
+
Classifier: License :: OSI Approved :: MIT License
|
|
8
|
+
Classifier: Operating System :: OS Independent
|
|
9
|
+
Requires-Python: >=3.7
|
|
10
|
+
Description-Content-Type: text/markdown
|
|
11
|
+
|
|
12
|
+
# bad-checker
|
|
13
|
+
|
|
14
|
+
A fuzzy, keyboard-typo- and leetspeak-aware content blocklist for Python.
|
|
15
|
+
|
|
16
|
+
Install name: `bad-checker` | Import name: `bad`
|
|
17
|
+
|
|
18
|
+
## Usage
|
|
19
|
+
|
|
20
|
+
```python
|
|
21
|
+
import bad
|
|
22
|
+
|
|
23
|
+
if "something bad" in bad:
|
|
24
|
+
print("Caught it!")
|
|
25
|
+
```
|
|
26
|
+
|
|
27
|
+
Matching combines:
|
|
28
|
+
- direct substring lookups against a blocklist
|
|
29
|
+
- leetspeak normalization (`v1agra` -> `viagra`, `fr33` -> `free`)
|
|
30
|
+
- a keyboard-proximity-aware Damerau-Levenshtein distance, so typos on
|
|
31
|
+
physically nearby keys (and adjacent-letter transpositions) are caught
|
|
32
|
+
cheaply while unrelated words are not
|
|
33
|
+
|
|
34
|
+
Add your own keywords at runtime:
|
|
35
|
+
|
|
36
|
+
```python
|
|
37
|
+
bad.add_keywords("mycompanyscam", "internaltermtoflag")
|
|
38
|
+
```
|
|
39
|
+
|
|
40
|
+
## Install
|
|
41
|
+
|
|
42
|
+
```bash
|
|
43
|
+
pip install bad-checker
|
|
44
|
+
```
|
|
45
|
+
|
|
46
|
+
## Build from source
|
|
47
|
+
|
|
48
|
+
```bash
|
|
49
|
+
pip install --upgrade build
|
|
50
|
+
python -m build
|
|
51
|
+
```
|
|
52
|
+
|
|
53
|
+
## License
|
|
54
|
+
|
|
55
|
+
MIT
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
bad
|