scratchattach 2.1.15b0__py3-none-any.whl → 3.0.0b1__py3-none-any.whl
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.
- cli/__about__.py +1 -0
- cli/__init__.py +26 -0
- cli/cmd/__init__.py +4 -0
- cli/cmd/group.py +127 -0
- cli/cmd/login.py +60 -0
- cli/cmd/profile.py +7 -0
- cli/cmd/sessions.py +5 -0
- cli/context.py +142 -0
- cli/db.py +66 -0
- cli/namespace.py +14 -0
- {scratchattach/cloud → cloud}/_base.py +112 -87
- {scratchattach/cloud → cloud}/cloud.py +16 -16
- {scratchattach/editor → editor}/__init__.py +2 -1
- {scratchattach/editor → editor}/asset.py +26 -14
- {scratchattach/editor → editor}/backpack_json.py +3 -5
- {scratchattach/editor → editor}/base.py +2 -4
- {scratchattach/editor → editor}/block.py +27 -22
- {scratchattach/editor → editor}/blockshape.py +1 -1
- {scratchattach/editor → editor}/build_defaulting.py +2 -2
- editor/commons.py +145 -0
- {scratchattach/editor → editor}/field.py +1 -1
- {scratchattach/editor → editor}/inputs.py +6 -3
- {scratchattach/editor → editor}/meta.py +10 -7
- {scratchattach/editor → editor}/monitor.py +10 -8
- {scratchattach/editor → editor}/mutation.py +68 -11
- {scratchattach/editor → editor}/pallete.py +1 -3
- {scratchattach/editor → editor}/prim.py +4 -0
- {scratchattach/editor → editor}/project.py +118 -16
- {scratchattach/editor → editor}/sprite.py +25 -15
- {scratchattach/editor → editor}/vlb.py +2 -2
- {scratchattach/eventhandlers → eventhandlers}/_base.py +1 -0
- {scratchattach/eventhandlers → eventhandlers}/cloud_events.py +26 -6
- {scratchattach/eventhandlers → eventhandlers}/cloud_recorder.py +4 -4
- {scratchattach/eventhandlers → eventhandlers}/cloud_requests.py +139 -54
- {scratchattach/eventhandlers → eventhandlers}/cloud_server.py +6 -3
- {scratchattach/eventhandlers → eventhandlers}/cloud_storage.py +1 -2
- eventhandlers/filterbot.py +163 -0
- other/other_apis.py +598 -0
- {scratchattach-2.1.15b0.dist-info → scratchattach-3.0.0b1.dist-info}/METADATA +7 -11
- scratchattach-3.0.0b1.dist-info/RECORD +79 -0
- {scratchattach-2.1.15b0.dist-info → scratchattach-3.0.0b1.dist-info}/WHEEL +1 -1
- scratchattach-3.0.0b1.dist-info/entry_points.txt +2 -0
- scratchattach-3.0.0b1.dist-info/top_level.txt +7 -0
- {scratchattach/site → site}/_base.py +32 -5
- site/activity.py +426 -0
- {scratchattach/site → site}/alert.py +4 -5
- {scratchattach/site → site}/backpack_asset.py +2 -1
- {scratchattach/site → site}/classroom.py +80 -73
- {scratchattach/site → site}/cloud_activity.py +43 -29
- {scratchattach/site → site}/comment.py +86 -100
- {scratchattach/site → site}/forum.py +8 -4
- site/placeholder.py +132 -0
- {scratchattach/site → site}/project.py +228 -122
- {scratchattach/site → site}/session.py +156 -71
- {scratchattach/site → site}/studio.py +139 -46
- site/typed_dicts.py +151 -0
- {scratchattach/site → site}/user.py +511 -215
- {scratchattach/utils → utils}/commons.py +12 -4
- {scratchattach/utils → utils}/encoder.py +7 -4
- {scratchattach/utils → utils}/enums.py +1 -0
- {scratchattach/utils → utils}/exceptions.py +36 -2
- utils/optional_async.py +154 -0
- utils/requests.py +306 -0
- scratchattach/__init__.py +0 -29
- scratchattach/editor/commons.py +0 -273
- scratchattach/eventhandlers/filterbot.py +0 -161
- scratchattach/other/other_apis.py +0 -284
- scratchattach/site/activity.py +0 -382
- scratchattach/utils/requests.py +0 -93
- scratchattach-2.1.15b0.dist-info/RECORD +0 -66
- scratchattach-2.1.15b0.dist-info/top_level.txt +0 -1
- {scratchattach/cloud → cloud}/__init__.py +0 -0
- {scratchattach/editor → editor}/code_translation/__init__.py +0 -0
- {scratchattach/editor → editor}/code_translation/parse.py +0 -0
- {scratchattach/editor → editor}/comment.py +0 -0
- {scratchattach/editor → editor}/extension.py +0 -0
- {scratchattach/editor → editor}/twconfig.py +0 -0
- {scratchattach/eventhandlers → eventhandlers}/__init__.py +0 -0
- {scratchattach/eventhandlers → eventhandlers}/combine.py +0 -0
- {scratchattach/eventhandlers → eventhandlers}/message_events.py +0 -0
- {scratchattach/other → other}/__init__.py +0 -0
- {scratchattach/other → other}/project_json_capabilities.py +0 -0
- {scratchattach-2.1.15b0.dist-info → scratchattach-3.0.0b1.dist-info}/licenses/LICENSE +0 -0
- {scratchattach/site → site}/__init__.py +0 -0
- {scratchattach/site → site}/browser_cookie3_stub.py +0 -0
- {scratchattach/site → site}/browser_cookies.py +0 -0
- {scratchattach/utils → utils}/__init__.py +0 -0
scratchattach/editor/commons.py
DELETED
|
@@ -1,273 +0,0 @@
|
|
|
1
|
-
"""
|
|
2
|
-
Shared functions used by the editor module
|
|
3
|
-
"""
|
|
4
|
-
from __future__ import annotations
|
|
5
|
-
|
|
6
|
-
import json
|
|
7
|
-
import random
|
|
8
|
-
import string
|
|
9
|
-
from typing import Optional, Final, Any, TYPE_CHECKING, Union
|
|
10
|
-
from enum import Enum, EnumMeta
|
|
11
|
-
|
|
12
|
-
if TYPE_CHECKING:
|
|
13
|
-
from . import sprite, build_defaulting
|
|
14
|
-
|
|
15
|
-
SpriteInput = Union[sprite.Sprite, build_defaulting._SetSprite]
|
|
16
|
-
else:
|
|
17
|
-
SpriteInput = Any
|
|
18
|
-
|
|
19
|
-
from scratchattach.utils import exceptions
|
|
20
|
-
|
|
21
|
-
DIGITS: Final[tuple[str, ...]] = tuple("0123456789")
|
|
22
|
-
|
|
23
|
-
ID_CHARS: Final[str] = string.ascii_letters + string.digits # + string.punctuation
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
# Strangely enough, it seems like something in string.punctuation causes issues. Not sure why
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
def _read_json_number(_str: str) -> float | int:
|
|
30
|
-
ret = ''
|
|
31
|
-
|
|
32
|
-
minus = _str[0] == '-'
|
|
33
|
-
if minus:
|
|
34
|
-
ret += '-'
|
|
35
|
-
_str = _str[1:]
|
|
36
|
-
|
|
37
|
-
def read_fraction(sub: str):
|
|
38
|
-
sub_ret = ''
|
|
39
|
-
if sub[0] == '.':
|
|
40
|
-
sub_ret += '.'
|
|
41
|
-
sub = sub[1:]
|
|
42
|
-
while sub[0] in DIGITS:
|
|
43
|
-
sub_ret += sub[0]
|
|
44
|
-
sub = sub[1:]
|
|
45
|
-
|
|
46
|
-
return sub_ret, sub
|
|
47
|
-
|
|
48
|
-
def read_exponent(sub: str):
|
|
49
|
-
sub_ret = ''
|
|
50
|
-
if sub[0].lower() == 'e':
|
|
51
|
-
sub_ret += sub[0]
|
|
52
|
-
sub = sub[1:]
|
|
53
|
-
|
|
54
|
-
if sub[0] in "-+":
|
|
55
|
-
sub_ret += sub[0]
|
|
56
|
-
sub = sub[1:]
|
|
57
|
-
|
|
58
|
-
if sub[0] not in DIGITS:
|
|
59
|
-
raise exceptions.UnclosedJSONError(f"Invalid exponent {sub}")
|
|
60
|
-
|
|
61
|
-
while sub[0] in DIGITS:
|
|
62
|
-
sub_ret += sub[0]
|
|
63
|
-
sub = sub[1:]
|
|
64
|
-
|
|
65
|
-
return sub_ret
|
|
66
|
-
|
|
67
|
-
if _str[0] == '0':
|
|
68
|
-
ret += '0'
|
|
69
|
-
_str = _str[1:]
|
|
70
|
-
|
|
71
|
-
elif _str[0] in DIGITS[1:9]:
|
|
72
|
-
while _str[0] in DIGITS:
|
|
73
|
-
ret += _str[0]
|
|
74
|
-
_str = _str[1:]
|
|
75
|
-
|
|
76
|
-
frac, _str = read_fraction(_str)
|
|
77
|
-
ret += frac
|
|
78
|
-
|
|
79
|
-
ret += read_exponent(_str)
|
|
80
|
-
|
|
81
|
-
return json.loads(ret)
|
|
82
|
-
|
|
83
|
-
# todo: consider if this should be moved to util.commons instead of editor.commons
|
|
84
|
-
# note: this is currently unused code
|
|
85
|
-
def consume_json(_str: str, i: int = 0) -> str | float | int | dict | list | bool | None:
|
|
86
|
-
"""
|
|
87
|
-
*'gobble up some JSON until we hit something not quite so tasty'*
|
|
88
|
-
|
|
89
|
-
Reads a JSON string and stops at the natural end (i.e. when brackets close, or when quotes end, etc.)
|
|
90
|
-
"""
|
|
91
|
-
# Named by ChatGPT
|
|
92
|
-
section = ''.join(_str[i:])
|
|
93
|
-
if section.startswith("true"):
|
|
94
|
-
return True
|
|
95
|
-
elif section.startswith("false"):
|
|
96
|
-
return False
|
|
97
|
-
elif section.startswith("null"):
|
|
98
|
-
return None
|
|
99
|
-
elif section[0] in "0123456789.-":
|
|
100
|
-
return _read_json_number(section)
|
|
101
|
-
|
|
102
|
-
depth = 0
|
|
103
|
-
json_text = ''
|
|
104
|
-
out_string = True
|
|
105
|
-
|
|
106
|
-
for char in section:
|
|
107
|
-
json_text += char
|
|
108
|
-
|
|
109
|
-
if char == '"':
|
|
110
|
-
if len(json_text) > 1:
|
|
111
|
-
unescaped = json_text[-2] != '\\'
|
|
112
|
-
else:
|
|
113
|
-
unescaped = True
|
|
114
|
-
if unescaped:
|
|
115
|
-
out_string ^= True
|
|
116
|
-
if out_string:
|
|
117
|
-
depth -= 1
|
|
118
|
-
else:
|
|
119
|
-
depth += 1
|
|
120
|
-
|
|
121
|
-
if out_string:
|
|
122
|
-
if char in "[{":
|
|
123
|
-
depth += 1
|
|
124
|
-
elif char in "}]":
|
|
125
|
-
depth -= 1
|
|
126
|
-
|
|
127
|
-
if depth == 0 and json_text.strip():
|
|
128
|
-
return json.loads(json_text.strip())
|
|
129
|
-
|
|
130
|
-
raise exceptions.UnclosedJSONError(f"Unclosed JSON string, read {json_text}")
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
def is_partial_json(_str: str, i: int = 0) -> bool:
|
|
134
|
-
try:
|
|
135
|
-
consume_json(_str, i)
|
|
136
|
-
return True
|
|
137
|
-
|
|
138
|
-
except exceptions.UnclosedJSONError:
|
|
139
|
-
return False
|
|
140
|
-
|
|
141
|
-
except ValueError:
|
|
142
|
-
return False
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
def is_valid_json(_str: Any) -> bool:
|
|
146
|
-
"""
|
|
147
|
-
Try to load a json string, if it fails, return False, else return true.
|
|
148
|
-
"""
|
|
149
|
-
try:
|
|
150
|
-
json.loads(_str)
|
|
151
|
-
return True
|
|
152
|
-
except (ValueError, TypeError):
|
|
153
|
-
return False
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
def noneless_update(obj: dict, update: dict) -> None:
|
|
157
|
-
"""
|
|
158
|
-
equivalent to dict.update, except and values of None are not assigned
|
|
159
|
-
"""
|
|
160
|
-
for key, value in update.items():
|
|
161
|
-
if value is not None:
|
|
162
|
-
obj[key] = value
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
def remove_nones(obj: dict) -> None:
|
|
166
|
-
"""
|
|
167
|
-
Removes all None values from a dict.
|
|
168
|
-
:param obj: Dictionary to remove all None values.
|
|
169
|
-
"""
|
|
170
|
-
nones = []
|
|
171
|
-
for key, value in obj.items():
|
|
172
|
-
if value is None:
|
|
173
|
-
nones.append(key)
|
|
174
|
-
for key in nones:
|
|
175
|
-
del obj[key]
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
def safe_get(lst: list | tuple, _i: int, default: Optional[Any] = None) -> Any:
|
|
179
|
-
"""
|
|
180
|
-
Like dict.get() but for lists
|
|
181
|
-
"""
|
|
182
|
-
if len(lst) <= _i:
|
|
183
|
-
return default
|
|
184
|
-
else:
|
|
185
|
-
return lst[_i]
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
def trim_final_nones(lst: list) -> list:
|
|
189
|
-
"""
|
|
190
|
-
Removes the last None values from a list until a non-None value is hit.
|
|
191
|
-
:param lst: list which will **not** be modified.
|
|
192
|
-
"""
|
|
193
|
-
i = len(lst)
|
|
194
|
-
for item in lst[::-1]:
|
|
195
|
-
if item is not None:
|
|
196
|
-
break
|
|
197
|
-
i -= 1
|
|
198
|
-
return lst[:i]
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
def dumps_ifnn(obj: Any) -> Optional[str]:
|
|
202
|
-
"""
|
|
203
|
-
Return json.dumps(obj) if the object is not None
|
|
204
|
-
"""
|
|
205
|
-
if obj is None:
|
|
206
|
-
return None
|
|
207
|
-
else:
|
|
208
|
-
return json.dumps(obj)
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
def gen_id() -> str:
|
|
212
|
-
"""
|
|
213
|
-
Generate an id for scratch blocks/variables/lists/broadcasts
|
|
214
|
-
|
|
215
|
-
The old 'naïve' method but that chances of a repeat are so miniscule
|
|
216
|
-
Have to check if whitespace chars break it
|
|
217
|
-
May later add checking within sprites so that we don't need such long ids (we can save space this way)
|
|
218
|
-
"""
|
|
219
|
-
return ''.join(random.choices(ID_CHARS, k=20))
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
def sanitize_fn(filename: str):
|
|
223
|
-
"""
|
|
224
|
-
Removes illegal chars from a filename
|
|
225
|
-
:return: Sanitized filename
|
|
226
|
-
"""
|
|
227
|
-
# Maybe could import a slugify module, but it's a bit overkill
|
|
228
|
-
ret = ''
|
|
229
|
-
for char in filename:
|
|
230
|
-
if char in string.ascii_letters + string.digits + "-_":
|
|
231
|
-
ret += char
|
|
232
|
-
else:
|
|
233
|
-
ret += '_'
|
|
234
|
-
return ret
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
def get_folder_name(name: str) -> str | None:
|
|
238
|
-
"""
|
|
239
|
-
Get the name of the folder if this is a turbowarp-style costume name
|
|
240
|
-
"""
|
|
241
|
-
if name.startswith('//'):
|
|
242
|
-
return None
|
|
243
|
-
|
|
244
|
-
if '//' in name:
|
|
245
|
-
return name.split('//')[0]
|
|
246
|
-
else:
|
|
247
|
-
return None
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
def get_name_nofldr(name: str) -> str:
|
|
251
|
-
"""
|
|
252
|
-
Get the sprite/asset name without the folder name
|
|
253
|
-
"""
|
|
254
|
-
fldr = get_folder_name(name)
|
|
255
|
-
if fldr is None:
|
|
256
|
-
return name
|
|
257
|
-
else:
|
|
258
|
-
return name[len(fldr) + 2:]
|
|
259
|
-
|
|
260
|
-
# Parent enum class
|
|
261
|
-
class SingletonMeta(EnumMeta):
|
|
262
|
-
|
|
263
|
-
def __call__(self, value=0, *args, **kwds):
|
|
264
|
-
if value != 0:
|
|
265
|
-
raise ValueError("Value must be 0.")
|
|
266
|
-
old_bases = self.__bases__
|
|
267
|
-
self.__bases__ = old_bases + (Enum,)
|
|
268
|
-
result = super().__call__(value, *args, **kwds)
|
|
269
|
-
self.__bases__ = old_bases
|
|
270
|
-
return result
|
|
271
|
-
|
|
272
|
-
Singleton = Enum
|
|
273
|
-
|
|
@@ -1,161 +0,0 @@
|
|
|
1
|
-
"""FilterBot class"""
|
|
2
|
-
from __future__ import annotations
|
|
3
|
-
|
|
4
|
-
from .message_events import MessageEvents
|
|
5
|
-
import time
|
|
6
|
-
|
|
7
|
-
class HardFilter:
|
|
8
|
-
|
|
9
|
-
def __init__(self, filter_name="UntitledFilter", *, equals=None, contains=None, author_name=None, project_id=None, profile=None, case_sensitive=False):
|
|
10
|
-
self.equals=equals
|
|
11
|
-
self.contains=contains
|
|
12
|
-
self.author_name=author_name
|
|
13
|
-
self.project_id=project_id
|
|
14
|
-
self.profile=profile
|
|
15
|
-
self.case_sensitive=case_sensitive
|
|
16
|
-
self.filter_name = filter_name
|
|
17
|
-
|
|
18
|
-
def apply(self, content, author_name, source_id):
|
|
19
|
-
if not self.case_sensitive:
|
|
20
|
-
content = content.lower()
|
|
21
|
-
if self.equals is not None:
|
|
22
|
-
if self.case_sensitive:
|
|
23
|
-
if self.equals == content:
|
|
24
|
-
return True
|
|
25
|
-
else:
|
|
26
|
-
if self.equals.lower() == content:
|
|
27
|
-
return True
|
|
28
|
-
if self.contains is not None:
|
|
29
|
-
if self.case_sensitive:
|
|
30
|
-
if self.contains.lower() in content:
|
|
31
|
-
return True
|
|
32
|
-
else:
|
|
33
|
-
if self.contains in content:
|
|
34
|
-
return True
|
|
35
|
-
if self.author_name == author_name:
|
|
36
|
-
return True
|
|
37
|
-
if self.project_id == source_id or self.profile == source_id:
|
|
38
|
-
return True
|
|
39
|
-
return False
|
|
40
|
-
|
|
41
|
-
class SoftFilter(HardFilter):
|
|
42
|
-
def __init__(self, score:float, filter_name="UntitledFilter", *, equals=None, contains=None, author_name=None, project_id=None, profile=None, case_sensitive=False):
|
|
43
|
-
self.score = score
|
|
44
|
-
super().__init__(filter_name, equals=equals, contains=contains, author_name=author_name, project_id=project_id, profile=profile, case_sensitive=case_sensitive)
|
|
45
|
-
|
|
46
|
-
class SpamFilter(HardFilter):
|
|
47
|
-
def __init__(self, filter_name="UntitledFilter", *, equals=None, contains=None, author_name=None, project_id=None, profile=None, case_sensitive=False):
|
|
48
|
-
self.memory = []
|
|
49
|
-
super().__init__(filter_name, equals=equals, contains=contains, author_name=author_name, project_id=project_id, profile=profile, case_sensitive=case_sensitive)
|
|
50
|
-
|
|
51
|
-
def apply(self, content, author_name, source_id):
|
|
52
|
-
applies = super().apply(content, author_name, source_id)
|
|
53
|
-
if not applies:
|
|
54
|
-
return False
|
|
55
|
-
self.memory.insert(0, {"content":content, "time":time.time()})
|
|
56
|
-
print(content, self.memory)
|
|
57
|
-
for comment in list(self.memory)[1:]:
|
|
58
|
-
if comment["time"] < time.time() -300:
|
|
59
|
-
self.memory.remove(comment)
|
|
60
|
-
if comment["content"].lower() == content.lower():
|
|
61
|
-
return True
|
|
62
|
-
return False
|
|
63
|
-
|
|
64
|
-
class Filterbot(MessageEvents):
|
|
65
|
-
|
|
66
|
-
# The Filterbot class is built upon MessageEvents, similar to how CloudEvents is built upon CloudEvents
|
|
67
|
-
|
|
68
|
-
def __init__(self, user, *, log_deletions=True):
|
|
69
|
-
super().__init__(user)
|
|
70
|
-
self.hard_filters = []
|
|
71
|
-
self.soft_filters = []
|
|
72
|
-
self.spam_filters = []
|
|
73
|
-
self.log_deletions = log_deletions
|
|
74
|
-
self.event(self.on_message, thread=False)
|
|
75
|
-
self.update_interval = 2
|
|
76
|
-
|
|
77
|
-
def add_filter(self, filter_obj):
|
|
78
|
-
if isinstance(filter_obj, SoftFilter):
|
|
79
|
-
self.soft_filters.append(filter_obj)
|
|
80
|
-
elif isinstance(filter_obj, SpamFilter): # careful: SpamFilter is also HardFilter due to inheritence
|
|
81
|
-
self.spam_filters.append(filter_obj)
|
|
82
|
-
elif isinstance(filter_obj, HardFilter):
|
|
83
|
-
self.hard_filters.append(filter_obj)
|
|
84
|
-
|
|
85
|
-
def add_f4f_filter(self):
|
|
86
|
-
self.add_filter(HardFilter("(f4f_filter) 'f4f'", contains="f4f"))
|
|
87
|
-
self.add_filter(HardFilter("(f4f_filter) 'follow me'", contains="follow me"))
|
|
88
|
-
self.add_filter(HardFilter("(f4f_filter) 'follow @'", contains="follow @"))
|
|
89
|
-
self.add_filter(HardFilter("(f4f_filter) f 4 f'", contains="f 4 f"))
|
|
90
|
-
self.add_filter(HardFilter("(f4f_filter) 'follow for'", contains="follow for"))
|
|
91
|
-
|
|
92
|
-
def add_ads_filter(self):
|
|
93
|
-
self.add_filter(SoftFilter(1, "(ads_filter) links", contains="scratch.mit.edu/projects/"))
|
|
94
|
-
self.add_filter(SoftFilter(-1, "(ads_filter) feedback", contains="feedback"))
|
|
95
|
-
self.add_filter(HardFilter("(ads_filter) 'check out my'", contains="check out my"))
|
|
96
|
-
self.add_filter(HardFilter("(ads_filter) 'play my'", contains="play my"))
|
|
97
|
-
self.add_filter(SoftFilter(1, "(ads_filter) 'advertis'", contains="advertis"))
|
|
98
|
-
|
|
99
|
-
def add_spam_filter(self):
|
|
100
|
-
self.add_filter(SpamFilter("(spam_filter)", contains=""))
|
|
101
|
-
|
|
102
|
-
def add_genalpha_nonsense_filter(self):
|
|
103
|
-
self.add_filter(HardFilter("(genalpha_nonsene_filter) 'skibidi'", contains="skibidi"))
|
|
104
|
-
self.add_filter(HardFilter("[genalpha_nonsene_filter) 'rizzler'", contains="rizzler"))
|
|
105
|
-
self.add_filter(HardFilter("(genalpha_nonsene_filter) 'fanum tax'", contains="fanum tax"))
|
|
106
|
-
|
|
107
|
-
def on_message(self, message):
|
|
108
|
-
if message.type == "addcomment":
|
|
109
|
-
delete = False
|
|
110
|
-
content = message.comment_fragment
|
|
111
|
-
|
|
112
|
-
if message.comment_type == 0: # project comment
|
|
113
|
-
source_id = message.comment_obj_id
|
|
114
|
-
if self.user._session.connect_project(message.comment_obj_id).author_name != self.user.username:
|
|
115
|
-
return # no permission to delete
|
|
116
|
-
if message.comment_type == 1: # profile comment
|
|
117
|
-
source_id = message.comment_obj_title
|
|
118
|
-
if message.comment_obj_title != self.user.username:
|
|
119
|
-
return # no permission to delete
|
|
120
|
-
if message.comment_type == 2: # studio comment
|
|
121
|
-
return # studio comments aren't handled
|
|
122
|
-
|
|
123
|
-
# Apply hard filters
|
|
124
|
-
for hard_filter in self.hard_filters:
|
|
125
|
-
if hard_filter.apply(content, message.actor_username, source_id):
|
|
126
|
-
delete=True
|
|
127
|
-
if self.log_deletions:
|
|
128
|
-
print(f"DETECTED: #{message.comment_id} violates hard filter: {hard_filter.filter_name}")
|
|
129
|
-
break
|
|
130
|
-
|
|
131
|
-
# Apply spam filters
|
|
132
|
-
if delete is False:
|
|
133
|
-
for spam_filter in self.spam_filters:
|
|
134
|
-
if spam_filter.apply(content, message.actor_username, source_id):
|
|
135
|
-
delete=True
|
|
136
|
-
if self.log_deletions:
|
|
137
|
-
print(f"DETECTED: #{message.comment_id} violates spam filter: {spam_filter.filter_name}")
|
|
138
|
-
break
|
|
139
|
-
|
|
140
|
-
# Apply soft filters
|
|
141
|
-
if delete is False:
|
|
142
|
-
score = 0
|
|
143
|
-
violated_filers = []
|
|
144
|
-
for soft_filter in self.soft_filters:
|
|
145
|
-
if soft_filter.apply(content, message.actor_username, source_id):
|
|
146
|
-
score += soft_filter.score
|
|
147
|
-
violated_filers.append(soft_filter.name)
|
|
148
|
-
if score >= 1:
|
|
149
|
-
print(f"DETECTED: #{message.comment_id} violates too many soft filters: {violated_filers}")
|
|
150
|
-
delete = True
|
|
151
|
-
|
|
152
|
-
if delete is True:
|
|
153
|
-
try:
|
|
154
|
-
message.target().delete()
|
|
155
|
-
if self.log_deletions:
|
|
156
|
-
print(f"DELETED: #{message.comment_id} by f{message.actor_username}: '{content}'")
|
|
157
|
-
except Exception as e:
|
|
158
|
-
if self.log_deletions:
|
|
159
|
-
print(f"DELETION FAILED: #{message.comment_id} by f{message.actor_username}: '{content}'")
|
|
160
|
-
|
|
161
|
-
|