aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--bot/cogs/antispam.py1
-rw-r--r--bot/cogs/filtering.py14
-rw-r--r--bot/cogs/moderation/modlog.py4
-rw-r--r--bot/cogs/moderation/scheduler.py6
-rw-r--r--bot/cogs/moderation/utils.py2
-rw-r--r--bot/cogs/reddit.py3
-rw-r--r--bot/cogs/watchchannels/bigbrother.py2
-rw-r--r--bot/cogs/watchchannels/talentpool.py54
-rw-r--r--bot/resources/tags/ask.md9
-rw-r--r--bot/resources/tags/traceback.md2
10 files changed, 60 insertions, 37 deletions
diff --git a/bot/cogs/antispam.py b/bot/cogs/antispam.py
index 0bcca578d..bc31cbd95 100644
--- a/bot/cogs/antispam.py
+++ b/bot/cogs/antispam.py
@@ -219,7 +219,6 @@ class AntiSpam(Cog):
# Get context and make sure the bot becomes the actor of infraction by patching the `author` attributes
context = await self.bot.get_context(msg)
context.author = self.bot.user
- context.message.author = self.bot.user
# Since we're going to invoke the tempmute command directly, we need to manually call the converter.
dt_remove_role_after = await self.expiration_date_converter.convert(context, f"{remove_role_after}S")
diff --git a/bot/cogs/filtering.py b/bot/cogs/filtering.py
index 93cc1c655..99b659bff 100644
--- a/bot/cogs/filtering.py
+++ b/bot/cogs/filtering.py
@@ -11,6 +11,7 @@ from discord import Colour, HTTPException, Member, Message, NotFound, TextChanne
from discord.ext.commands import Cog
from discord.utils import escape_markdown
+from bot.api import ResponseCodeError
from bot.bot import Bot
from bot.cogs.moderation import ModLog
from bot.constants import (
@@ -301,9 +302,16 @@ class Filtering(Cog):
'delete_date': delete_date
}
- await self.bot.api_client.post('bot/offensive-messages', json=data)
- self.schedule_msg_delete(data)
- log.trace(f"Offensive message {msg.id} will be deleted on {delete_date}")
+ try:
+ await self.bot.api_client.post('bot/offensive-messages', json=data)
+ except ResponseCodeError as e:
+ if e.status == 400 and "already exists" in e.response_json.get("id", [""])[0]:
+ log.debug(f"Offensive message {msg.id} already exists.")
+ else:
+ log.error(f"Offensive message {msg.id} failed to post: {e}")
+ else:
+ self.schedule_msg_delete(data)
+ log.trace(f"Offensive message {msg.id} will be deleted on {delete_date}")
if is_private:
channel_str = "via DM"
diff --git a/bot/cogs/moderation/modlog.py b/bot/cogs/moderation/modlog.py
index 0a63f57b8..5f30d3744 100644
--- a/bot/cogs/moderation/modlog.py
+++ b/bot/cogs/moderation/modlog.py
@@ -120,6 +120,10 @@ class ModLog(Cog, name="ModLog"):
else:
content = "@everyone"
+ # Truncate content to 2000 characters and append an ellipsis.
+ if content and len(content) > 2000:
+ content = content[:2000 - 3] + "..."
+
channel = self.bot.get_channel(channel_id)
log_message = await channel.send(
content=content,
diff --git a/bot/cogs/moderation/scheduler.py b/bot/cogs/moderation/scheduler.py
index 75028d851..051f6c52c 100644
--- a/bot/cogs/moderation/scheduler.py
+++ b/bot/cogs/moderation/scheduler.py
@@ -161,6 +161,7 @@ class InfractionScheduler:
self.schedule_expiration(infraction)
except discord.HTTPException as e:
# Accordingly display that applying the infraction failed.
+ # Don't use ctx.message.author; antispam only patches ctx.author.
confirm_msg = ":x: failed to apply"
expiry_msg = ""
log_content = ctx.author.mention
@@ -190,6 +191,7 @@ class InfractionScheduler:
await ctx.send(f"{dm_result}{confirm_msg}{infr_message}.")
# Send a log message to the mod log.
+ # Don't use ctx.message.author for the actor; antispam only patches ctx.author.
log.trace(f"Sending apply mod log for infraction #{id_}.")
await self.mod_log.send_log_message(
icon_url=icon,
@@ -198,7 +200,7 @@ class InfractionScheduler:
thumbnail=user.avatar_url_as(static_format="png"),
text=textwrap.dedent(f"""
Member: {user.mention} (`{user.id}`)
- Actor: {ctx.message.author}{dm_log_text}{expiry_log_text}
+ Actor: {ctx.author}{dm_log_text}{expiry_log_text}
Reason: {reason}
"""),
content=log_content,
@@ -242,7 +244,7 @@ class InfractionScheduler:
log_text = await self.deactivate_infraction(response[0], send_log=False)
log_text["Member"] = f"{user.mention}(`{user.id}`)"
- log_text["Actor"] = str(ctx.message.author)
+ log_text["Actor"] = str(ctx.author)
log_content = None
id_ = response[0]['id']
footer = f"ID: {id_}"
diff --git a/bot/cogs/moderation/utils.py b/bot/cogs/moderation/utils.py
index fb55287b6..f21272102 100644
--- a/bot/cogs/moderation/utils.py
+++ b/bot/cogs/moderation/utils.py
@@ -70,7 +70,7 @@ async def post_infraction(
log.trace(f"Posting {infr_type} infraction for {user} to the API.")
payload = {
- "actor": ctx.message.author.id,
+ "actor": ctx.author.id, # Don't use ctx.message.author; antispam only patches ctx.author.
"hidden": hidden,
"reason": reason,
"type": infr_type,
diff --git a/bot/cogs/reddit.py b/bot/cogs/reddit.py
index d853ab2ea..5d9e2c20b 100644
--- a/bot/cogs/reddit.py
+++ b/bot/cogs/reddit.py
@@ -10,6 +10,7 @@ from aiohttp import BasicAuth, ClientError
from discord import Colour, Embed, TextChannel
from discord.ext.commands import Cog, Context, group
from discord.ext.tasks import loop
+from discord.utils import escape_markdown
from bot.bot import Bot
from bot.constants import Channels, ERROR_REPLIES, Emojis, Reddit as RedditConfig, STAFF_ROLES, Webhooks
@@ -187,6 +188,8 @@ class Reddit(Cog):
author = data["author"]
title = textwrap.shorten(data["title"], width=64, placeholder="...")
+ # Normal brackets interfere with Markdown.
+ title = escape_markdown(title).replace("[", "⦋").replace("]", "⦌")
link = self.URL + data["permalink"]
embed.description += (
diff --git a/bot/cogs/watchchannels/bigbrother.py b/bot/cogs/watchchannels/bigbrother.py
index 4d27a6333..7aa9cec58 100644
--- a/bot/cogs/watchchannels/bigbrother.py
+++ b/bot/cogs/watchchannels/bigbrother.py
@@ -131,8 +131,8 @@ class BigBrother(WatchChannel, Cog, name="Big Brother"):
active_watches = await self.bot.api_client.get(
self.api_endpoint,
params=ChainMap(
+ {"user__id": str(user.id)},
self.api_default_params,
- {"user__id": str(user.id)}
)
)
if active_watches:
diff --git a/bot/cogs/watchchannels/talentpool.py b/bot/cogs/watchchannels/talentpool.py
index 89256e92e..a6df84c23 100644
--- a/bot/cogs/watchchannels/talentpool.py
+++ b/bot/cogs/watchchannels/talentpool.py
@@ -1,8 +1,9 @@
import logging
import textwrap
from collections import ChainMap
+from typing import Union
-from discord import Color, Embed, Member
+from discord import Color, Embed, Member, User
from discord.ext.commands import Cog, Context, group
from bot.api import ResponseCodeError
@@ -164,25 +165,10 @@ class TalentPool(WatchChannel, Cog, name="Talentpool"):
Providing a `reason` is required.
"""
- active_nomination = await self.bot.api_client.get(
- self.api_endpoint,
- params=ChainMap(
- self.api_default_params,
- {"user__id": str(user.id)}
- )
- )
-
- if not active_nomination:
+ if await self.unwatch(user.id, reason):
+ await ctx.send(f":white_check_mark: Messages sent by {user} will no longer be relayed")
+ else:
await ctx.send(":x: The specified user does not have an active nomination")
- return
-
- [nomination] = active_nomination
- await self.bot.api_client.patch(
- f"{self.api_endpoint}/{nomination['id']}",
- json={'end_reason': reason, 'active': False}
- )
- await ctx.send(f":white_check_mark: Messages sent by {user} will no longer be relayed")
- self._remove_user(user.id)
@nomination_group.group(name='edit', aliases=('e',), invoke_without_command=True)
@with_role(*MODERATION_ROLES)
@@ -220,6 +206,36 @@ class TalentPool(WatchChannel, Cog, name="Talentpool"):
await ctx.send(f":white_check_mark: Updated the {field} of the nomination!")
+ @Cog.listener()
+ async def on_member_ban(self, guild: Guild, user: Union[User, Member]) -> None:
+ """Remove `user` from the talent pool after they are banned."""
+ await self.unwatch(user.id, "User was banned.")
+
+ async def unwatch(self, user_id: int, reason: str) -> bool:
+ """End the active nomination of a user with the given reason and return True on success."""
+ active_nomination = await self.bot.api_client.get(
+ self.api_endpoint,
+ params=ChainMap(
+ {"user__id": str(user_id)},
+ self.api_default_params,
+ )
+ )
+
+ if not active_nomination:
+ log.debug(f"No active nominate exists for {user_id=}")
+ return False
+
+ log.info(f"Ending nomination: {user_id=} {reason=}")
+
+ nomination = active_nomination[0]
+ await self.bot.api_client.patch(
+ f"{self.api_endpoint}/{nomination['id']}",
+ json={'end_reason': reason, 'active': False}
+ )
+ self._remove_user(user_id)
+
+ return True
+
def _nomination_to_string(self, nomination_object: dict) -> str:
"""Creates a string representation of a nomination."""
guild = self.bot.get_guild(Guild.id)
diff --git a/bot/resources/tags/ask.md b/bot/resources/tags/ask.md
deleted file mode 100644
index e2c2a88f6..000000000
--- a/bot/resources/tags/ask.md
+++ /dev/null
@@ -1,9 +0,0 @@
-Asking good questions will yield a much higher chance of a quick response:
-
-• Don't ask to ask your question, just go ahead and tell us your problem.
-• Don't ask if anyone is knowledgeable in some area, filtering serves no purpose.
-• Try to solve the problem on your own first, we're not going to write code for you.
-• Show us the code you've tried and any errors or unexpected results it's giving.
-• Be patient while we're helping you.
-
-You can find a much more detailed explanation [on our website](https://pythondiscord.com/pages/asking-good-questions/).
diff --git a/bot/resources/tags/traceback.md b/bot/resources/tags/traceback.md
index 46ef40aa1..e770fa86d 100644
--- a/bot/resources/tags/traceback.md
+++ b/bot/resources/tags/traceback.md
@@ -11,7 +11,7 @@ ZeroDivisionError: integer division or modulo by zero
```
The best way to read your traceback is bottom to top.
-• Identify the exception raised (e.g. ZeroDivisonError)
+• Identify the exception raised (e.g. ZeroDivisionError)
• Make note of the line number, and navigate there in your program.
• Try to understand why the error occurred.