Skip to content

Commit b625574

Browse files
committed
Additionally show exact Discord message format rule violations in 'showcase' extension
1 parent 2f16e31 commit b625574

File tree

5 files changed

+107
-77
lines changed

5 files changed

+107
-77
lines changed

pcbot/exts/helpforums_pre/cogs.py

+5-8
Original file line numberDiff line numberDiff line change
@@ -761,14 +761,11 @@ async def on_raw_member_remove(self, payload: discord.RawMemberRemoveEvent):
761761
forum_channel.threads,
762762
[thr async for thr in forum_channel.archived_threads(limit=20)],
763763
):
764-
if (
765-
help_thread.owner_id == payload.user.id
766-
and not (
767-
help_thread.locked
768-
or any(
769-
tag.name.lower() in ("solved", "invalid")
770-
for tag in help_thread.applied_tags
771-
)
764+
if help_thread.owner_id == payload.user.id and not (
765+
help_thread.locked
766+
or any(
767+
tag.name.lower() in ("solved", "invalid")
768+
for tag in help_thread.applied_tags
772769
)
773770
):
774771
snakecore.utils.hold_task(

pcbot/exts/showcase/cogs.py

+16-7
Original file line numberDiff line numberDiff line change
@@ -372,7 +372,7 @@ async def delete_bad_message_with_thread(
372372
def showcase_message_validity_check(
373373
self,
374374
message: discord.Message,
375-
) -> tuple[bool, str | None]:
375+
) -> tuple[bool, str | None, str | None]:
376376
"""Checks if a showcase message has the right format.
377377
378378
Returns
@@ -404,7 +404,9 @@ async def on_thread_create(self, thread: discord.Thread):
404404
except discord.NotFound:
405405
return
406406

407-
is_valid, reason = self.showcase_message_validity_check(message)
407+
is_valid, general_reason, exact_reason = self.showcase_message_validity_check(
408+
message
409+
)
408410

409411
if not is_valid:
410412
deletion_datetime = datetime.datetime.now(
@@ -414,7 +416,8 @@ async def on_thread_create(self, thread: discord.Thread):
414416
try:
415417
warn_msg = await message.reply(
416418
"### Invalid showcase message\n\n"
417-
f"{reason}\n\n"
419+
f"Failed on rule '{exact_reason}'.\n\n"
420+
f"{general_reason}\n\n"
418421
" If no changes are made, your message (and its thread/post) will be "
419422
f"deleted {snakecore.utils.create_markdown_timestamp(deletion_datetime, 'R')}."
420423
)
@@ -531,7 +534,9 @@ async def on_message(self, message: discord.Message):
531534
):
532535
return
533536

534-
is_valid, reason = self.showcase_message_validity_check(message)
537+
is_valid, general_reason, exact_reason = self.showcase_message_validity_check(
538+
message
539+
)
535540

536541
if is_valid:
537542
await self.prompt_author_for_feedback_thread(message)
@@ -543,7 +548,8 @@ async def on_message(self, message: discord.Message):
543548
try:
544549
warn_msg = await message.reply(
545550
"### Invalid showcase message\n\n"
546-
f"{reason}\n\n"
551+
f"Failed on rule '{exact_reason}'.\n\n"
552+
f"{general_reason}\n\n"
547553
" If no changes are made, your message (and its thread/post) will be "
548554
f"deleted {snakecore.utils.create_markdown_timestamp(deletion_datetime, 'R')}."
549555
)
@@ -579,7 +585,9 @@ async def on_message_edit(self, old: discord.Message, new: discord.Message):
579585
):
580586
return
581587

582-
is_valid, reason = self.showcase_message_validity_check(new)
588+
is_valid, general_reason, exact_reason = self.showcase_message_validity_check(
589+
new
590+
)
583591

584592
if not is_valid:
585593
if new.id in self.entry_message_deletion_dict:
@@ -602,7 +610,8 @@ async def on_message_edit(self, old: discord.Message, new: discord.Message):
602610
content=(
603611
"### Invalid showcase message\n\n"
604612
"Your edited showcase message is invalid.\n\n"
605-
f"{reason}\n\n"
613+
f"Failed on rule '{exact_reason}'.\n\n"
614+
f"{general_reason}\n\n"
606615
" If no changes are made, your post will be "
607616
f"deleted "
608617
+ snakecore.utils.create_markdown_timestamp(

pcbot/exts/showcase/utils/rules.py

+52-42
Original file line numberDiff line numberDiff line change
@@ -13,31 +13,31 @@ def validate(
1313
enforce_type: EnforceType,
1414
message: discord.Message,
1515
arg: Literal["any", "only", "none"] = "any",
16-
) -> tuple[Literal[False], str] | tuple[Literal[True], None]:
16+
) -> tuple[Literal[False], str, str | None] | tuple[Literal[True], None, None]:
1717
"""Validate a message for the presence of content according to the specified arguments."""
1818

1919
has_content = bool(message.content)
2020
only_content = not has_content and not (message.attachments or message.embeds)
2121

2222
if enforce_type == "always" and arg == "only" and not only_content:
23-
return (False, "Message must always contain only text content")
23+
return (False, "Message must always contain only text content", None)
2424

2525
if enforce_type == "always" and arg == "any" and not has_content:
26-
return (False, "Message must always contain text content")
26+
return (False, "Message must always contain text content", None)
2727

2828
if enforce_type == "always" and arg == "none" and has_content:
29-
return (False, "Message must always contain no text content")
29+
return (False, "Message must always contain no text content", None)
3030

3131
if enforce_type == "never" and arg == "only" and only_content:
32-
return (False, "Message must never contain only text content")
32+
return (False, "Message must never contain only text content", None)
3333

3434
if enforce_type == "never" and arg == "any" and has_content:
35-
return (False, "Message must never contain text content")
35+
return (False, "Message must never contain text content", None)
3636

3737
if enforce_type == "never" and arg == "none" and not has_content:
38-
return (False, "Message must never contain no text content")
38+
return (False, "Message must never contain no text content", None)
3939

40-
return (True, None)
40+
return (True, None, None)
4141

4242
@staticmethod
4343
def validate_arg(arg: Literal["any", "only", "none"]) -> str | None:
@@ -53,7 +53,7 @@ def validate(
5353
enforce_type: EnforceType,
5454
message: discord.Message,
5555
arg: tuple[int, int],
56-
):
56+
) -> tuple[Literal[False], str, str | None] | tuple[Literal[True], None, None]:
5757
"""Validate a message for the presence of text content within the specified length range."""
5858

5959
if not isinstance(arg, tuple) or len(arg) != 2:
@@ -77,15 +77,17 @@ def validate(
7777
return (
7878
False,
7979
f"Message must always contain text content within {min_length}-{max_length} characters",
80+
None,
8081
)
8182

8283
if enforce_type == "never" and (min_length <= content_length <= max_length):
8384
return (
8485
False,
8586
f"Message must never contain text content within {min_length}-{max_length} characters",
87+
None,
8688
)
8789

88-
return (True, None)
90+
return (True, None, None)
8991

9092
@staticmethod
9193
def validate_arg(arg: tuple[int | None, int | None]) -> str | None:
@@ -113,7 +115,7 @@ def validate(
113115
enforce_type: EnforceType,
114116
message: discord.Message,
115117
arg: Literal["any", "only", "none"],
116-
) -> tuple[Literal[False], str] | tuple[Literal[True], None]:
118+
) -> tuple[Literal[False], str, str | None] | tuple[Literal[True], None, None]:
117119
"""Validate a message for the presence of URLs according to the specified arguments."""
118120

119121
search_obj = tuple(re.finditer(URL_PATTERN, message.content))
@@ -125,24 +127,24 @@ def validate(
125127
no_urls = not any_urls
126128

127129
if enforce_type == "always" and arg == "only" and not only_urls:
128-
return (False, "Message must always contain only URLs")
130+
return (False, "Message must always contain only URLs", None)
129131

130132
if enforce_type == "always" and arg == "any" and not any_urls:
131-
return (False, "Message must always contain at least one URL")
133+
return (False, "Message must always contain at least one URL", None)
132134

133135
if enforce_type == "always" and arg == "none" and not no_urls:
134-
return (False, "Message must always contain no URLs")
136+
return (False, "Message must always contain no URLs", None)
135137

136138
if enforce_type == "never" and arg == "only" and only_urls:
137-
return (False, "Message must never contain only URLs")
139+
return (False, "Message must never contain only URLs", None)
138140

139141
if enforce_type == "never" and arg == "any" and any_urls:
140-
return (False, "Message must never contain at least one URL")
142+
return (False, "Message must never contain at least one URL", None)
141143

142144
if enforce_type == "never" and arg == "none" and no_urls:
143-
return (False, "Message must never contain no URLs")
145+
return (False, "Message must never contain no URLs", None)
144146

145-
return (True, None)
147+
return (True, None, None)
146148

147149

148150
# Rule for validating VCS URLs
@@ -154,7 +156,7 @@ def validate(
154156
enforce_type: EnforceType,
155157
message: discord.Message,
156158
arg: Literal["any", "all", "none"] = "any",
157-
) -> tuple[Literal[False], str] | tuple[Literal[True], None]:
159+
) -> tuple[Literal[False], str, str | None] | tuple[Literal[True], None, None]:
158160
"""Validate a message for the presence of VCS URLs according to the specified arguments."""
159161

160162
search_obj = tuple(re.finditer(URL_PATTERN, message.content or ""))
@@ -164,24 +166,32 @@ def validate(
164166
all_vcs_urls = not any(not is_vcs_url(link) for link in links)
165167

166168
if enforce_type == "always" and arg == "all" and not all_vcs_urls:
167-
return (False, "Message must always contain only valid VCS URLs")
169+
return (False, "Message must always contain only valid VCS URLs", None)
168170

169171
if enforce_type == "always" and arg == "any" and not any_vcs_urls:
170-
return (False, "Message must always contain at least one valid VCS URL")
172+
return (
173+
False,
174+
"Message must always contain at least one valid VCS URL",
175+
None,
176+
)
171177

172178
if enforce_type == "always" and arg == "none" and not no_vcs_urls:
173-
return (False, "Message must always contain no valid VCS URLs")
179+
return (False, "Message must always contain no valid VCS URLs", None)
174180

175181
if enforce_type == "never" and arg == "all" and all_vcs_urls:
176-
return (False, "Message must never contain only valid VCS URLs")
182+
return (False, "Message must never contain only valid VCS URLs", None)
177183

178184
if enforce_type == "never" and arg == "any" and any_vcs_urls:
179-
return (False, "Message must never contain at least one valid VCS URL")
185+
return (
186+
False,
187+
"Message must never contain at least one valid VCS URL",
188+
None,
189+
)
180190

181191
if enforce_type == "never" and arg == "none" and no_vcs_urls:
182-
return (False, "Message must never contain no valid VCS URLs")
192+
return (False, "Message must never contain no valid VCS URLs", None)
183193

184-
return (True, None)
194+
return (True, None, None)
185195

186196
@staticmethod
187197
def validate_arg(arg: Literal["any", "all", "none"]) -> str | None:
@@ -197,32 +207,32 @@ def validate(
197207
enforce_type: EnforceType,
198208
message: discord.Message,
199209
arg: Literal["any", "only", "none"],
200-
):
210+
) -> tuple[Literal[False], str, str | None] | tuple[Literal[True], None, None]:
201211
"""Validate a message for the presence of attachments according to the specified arguments."""
202212

203213
any_attachments = bool(message.attachments)
204214
only_attachments = any_attachments and not (message.content or message.embeds)
205215
no_attachments = not any_attachments
206216

207217
if enforce_type == "always" and arg == "only" and not only_attachments:
208-
return (False, "Message must always contain only attachments")
218+
return (False, "Message must always contain only attachments", None)
209219

210220
if enforce_type == "always" and arg == "any" and not any_attachments:
211-
return (False, "Message must always contain at least one attachment")
221+
return (False, "Message must always contain at least one attachment", None)
212222

213223
if enforce_type == "always" and arg == "none" and not no_attachments:
214-
return (False, "Message must always contain no attachments")
224+
return (False, "Message must always contain no attachments", None)
215225

216226
if enforce_type == "never" and arg == "only" and only_attachments:
217-
return (False, "Message must never contain only attachments")
227+
return (False, "Message must never contain only attachments", None)
218228

219229
if enforce_type == "never" and arg == "any" and any_attachments:
220-
return (False, "Message must never contain at least one attachment")
230+
return (False, "Message must never contain at least one attachment", None)
221231

222232
if enforce_type == "never" and arg == "none" and no_attachments:
223-
return (False, "Message must never contain no attachments")
233+
return (False, "Message must never contain no attachments", None)
224234

225-
return (True, None)
235+
return (True, None, None)
226236

227237

228238
class EmbedsRule(DiscordMessageRule, name="embeds"):
@@ -233,32 +243,32 @@ def validate(
233243
enforce_type: EnforceType,
234244
message: discord.Message,
235245
arg: Literal["any", "only", "none"],
236-
) -> tuple[Literal[False], str] | tuple[Literal[True], None]:
246+
) -> tuple[Literal[False], str, str | None] | tuple[Literal[True], None, None]:
237247
"""Validate a message for the presence of embeds according to the specified arguments."""
238248

239249
any_embeds = bool(message.embeds)
240250
only_embeds = any_embeds and not (message.content or message.attachments)
241251
no_embeds = not any_embeds
242252

243253
if enforce_type == "always" and arg == "only" and not only_embeds:
244-
return (False, "Message must always contain only embeds")
254+
return (False, "Message must always contain only embeds", None)
245255

246256
if enforce_type == "always" and arg == "any" and not any_embeds:
247-
return (False, "Message must always contain at least one embed")
257+
return (False, "Message must always contain at least one embed", None)
248258

249259
if enforce_type == "always" and arg == "none" and not no_embeds:
250-
return (False, "Message must always contain no embeds")
260+
return (False, "Message must always contain no embeds", None)
251261

252262
if enforce_type == "never" and arg == "only" and only_embeds:
253-
return (False, "Message must never contain only embeds")
263+
return (False, "Message must never contain only embeds", None)
254264

255265
if enforce_type == "never" and arg == "any" and any_embeds:
256-
return (False, "Message must never contain at least one embed")
266+
return (False, "Message must never contain at least one embed", None)
257267

258268
if enforce_type == "never" and arg == "none" and no_embeds:
259-
return (False, "Message must never contain no embeds")
269+
return (False, "Message must never contain no embeds", None)
260270

261-
return (True, None)
271+
return (True, None, None)
262272

263273

264274
RULE_MAPPING: dict[str, type[DiscordMessageRule]] = {

pcbot/exts/showcase/utils/utils.py

+2-2
Original file line numberDiff line numberDiff line change
@@ -59,7 +59,7 @@ def __init_subclass__(cls, name: str) -> None:
5959
@abstractmethod
6060
def validate(
6161
enforce_type: EnforceType, message: discord.Message, arg: Any = None
62-
) -> tuple[Literal[False], str] | tuple[Literal[True], None]:
62+
) -> tuple[Literal[False], str, str | None] | tuple[Literal[True], None, None]:
6363
...
6464

6565
@staticmethod
@@ -72,5 +72,5 @@ class AsyncDiscordMessageRule(DiscordMessageRule, name="AsyncDiscordMessageRule"
7272
@abstractmethod
7373
async def validate(
7474
enforce_type: EnforceType, message: discord.Message, arg: Any = None
75-
) -> tuple[Literal[False], str] | tuple[Literal[True], None]:
75+
) -> tuple[Literal[False], str, str | None] | tuple[Literal[True], None, None]:
7676
...

0 commit comments

Comments
 (0)