diff options
| -rw-r--r-- | bot/cogs/antispam.py | 1 | ||||
| -rw-r--r-- | bot/cogs/filtering.py | 14 | ||||
| -rw-r--r-- | bot/cogs/moderation/modlog.py | 4 | ||||
| -rw-r--r-- | bot/cogs/moderation/scheduler.py | 6 | ||||
| -rw-r--r-- | bot/cogs/moderation/utils.py | 2 | ||||
| -rw-r--r-- | bot/cogs/reddit.py | 3 | ||||
| -rw-r--r-- | bot/cogs/watchchannels/bigbrother.py | 2 | ||||
| -rw-r--r-- | bot/cogs/watchchannels/talentpool.py | 54 | ||||
| -rw-r--r-- | bot/resources/tags/ask.md | 9 | ||||
| -rw-r--r-- | bot/resources/tags/traceback.md | 2 | 
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.   | 
