diff options
Diffstat (limited to '')
| -rw-r--r-- | bot/constants.py | 12 | ||||
| -rw-r--r-- | bot/exts/recruitment/talentpool/_cog.py | 14 | ||||
| -rw-r--r-- | bot/exts/recruitment/talentpool/_review.py | 53 | 
3 files changed, 53 insertions, 26 deletions
| diff --git a/bot/constants.py b/bot/constants.py index 36b917734..93da6a906 100644 --- a/bot/constants.py +++ b/bot/constants.py @@ -683,10 +683,22 @@ class VideoPermission(metaclass=YAMLGetter):      default_permission_duration: int +class ThreadArchiveTimes(Enum): +    HOUR = 60 +    DAY = 1440 +    THREE_DAY = 4230 +    WEEK = 10080 + +  # Debug mode  DEBUG_MODE: bool = _CONFIG_YAML["debug"] == "true"  FILE_LOGS: bool = _CONFIG_YAML["file_logs"].lower() == "true" +if DEBUG_MODE: +    DEFAULT_THREAD_ARCHIVE_TIME = ThreadArchiveTimes.HOUR.value +else: +    DEFAULT_THREAD_ARCHIVE_TIME = ThreadArchiveTimes.WEEK.value +  # Paths  BOT_DIR = os.path.dirname(__file__)  PROJECT_ROOT = os.path.abspath(os.path.join(BOT_DIR, os.pardir)) diff --git a/bot/exts/recruitment/talentpool/_cog.py b/bot/exts/recruitment/talentpool/_cog.py index 2fafaec97..8fa0be5b1 100644 --- a/bot/exts/recruitment/talentpool/_cog.py +++ b/bot/exts/recruitment/talentpool/_cog.py @@ -483,12 +483,9 @@ class TalentPool(Cog, name="Talentpool"):      @has_any_role(*MODERATION_ROLES)      async def get_review(self, ctx: Context, user_id: int) -> None:          """Get the user's review as a markdown file.""" -        review = (await self.reviewer.make_review(user_id))[0] -        if review: -            file = discord.File(StringIO(review), f"{user_id}_review.md") -            await ctx.send(file=file) -        else: -            await ctx.send(f"There doesn't appear to be an active nomination for {user_id}") +        review, _, _ = await self.reviewer.make_review(user_id) +        file = discord.File(StringIO(review), f"{user_id}_review.md") +        await ctx.send(file=file)      @nomination_group.command(aliases=('review',))      @has_any_role(*MODERATION_ROLES) @@ -501,7 +498,7 @@ class TalentPool(Cog, name="Talentpool"):          await ctx.message.add_reaction(Emojis.check_mark)      @Cog.listener() -    async def on_member_ban(self, guild: Guild, user: Union[MemberOrUser]) -> None: +    async def on_member_ban(self, guild: Guild, user: MemberOrUser) -> None:          """Remove `user` from the talent pool after they are banned."""          await self.end_nomination(user.id, "User was banned.") @@ -516,6 +513,9 @@ class TalentPool(Cog, name="Talentpool"):          if payload.channel_id != Channels.nomination_voting:              return +        if payload.user_id == self.bot.user.id: +            return +          message: PartialMessage = self.bot.get_channel(payload.channel_id).get_partial_message(payload.message_id)          emoji = str(payload.emoji) diff --git a/bot/exts/recruitment/talentpool/_review.py b/bot/exts/recruitment/talentpool/_review.py index d880c524c..110ac47bc 100644 --- a/bot/exts/recruitment/talentpool/_review.py +++ b/bot/exts/recruitment/talentpool/_review.py @@ -10,12 +10,12 @@ from typing import List, Optional, Union  import arrow  from dateutil.parser import isoparse -from discord import Embed, Emoji, Member, Message, NoMoreItems, PartialMessage, TextChannel +from discord import Embed, Emoji, Member, Message, NoMoreItems, NotFound, PartialMessage, TextChannel  from discord.ext.commands import Context  from bot.api import ResponseCodeError  from bot.bot import Bot -from bot.constants import Channels, Colours, Emojis, Guild +from bot.constants import Channels, Colours, DEFAULT_THREAD_ARCHIVE_TIME, Emojis, Guild, Roles  from bot.log import get_logger  from bot.utils.members import get_or_fetch_member  from bot.utils.messages import count_unique_users_reaction, pin_no_system_message @@ -36,9 +36,8 @@ MAX_MESSAGE_SIZE = 2000  MAX_EMBED_SIZE = 4000  # Regex for finding the first message of a nomination, and extracting the nominee. -# Historic nominations will have 2 role mentions at the start, new ones won't, optionally match for this.  NOMINATION_MESSAGE_REGEX = re.compile( -    r"(?:<@&\d+> <@&\d+>\n)*?<@!?(\d+?)> \(.+#\d{4}\) for Helper!\n\n\*\*Nominated by:\*\*", +    r"<@!?(\d+)> \(.+#\d{4}\) for Helper!\n\n",      re.MULTILINE  ) @@ -78,14 +77,14 @@ class Reviewer:      async def post_review(self, user_id: int, update_database: bool) -> None:          """Format the review of a user and post it to the nomination voting channel.""" -        review, reviewed_emoji = await self.make_review(user_id) -        if not review: +        review, reviewed_emoji, nominee = await self.make_review(user_id) +        if not nominee:              return          guild = self.bot.get_guild(Guild.id)          channel = guild.get_channel(Channels.nomination_voting) -        log.trace(f"Posting the review of {user_id}") +        log.trace(f"Posting the review of {nominee} ({nominee.id})")          messages = await self._bulk_send(channel, review)          await pin_no_system_message(messages[0]) @@ -95,12 +94,18 @@ class Reviewer:              for reaction in (reviewed_emoji, "\N{THUMBS UP SIGN}", "\N{THUMBS DOWN SIGN}"):                  await last_message.add_reaction(reaction) +        thread = await last_message.create_thread( +            name=f"Nomination - {nominee}", +            auto_archive_duration=DEFAULT_THREAD_ARCHIVE_TIME +        ) +        await thread.send(fr"<@&{Roles.mod_team}> <@&{Roles.admins}>") +          if update_database:              nomination = self._pool.cache.get(user_id)              await self.bot.api_client.patch(f"bot/nominations/{nomination['id']}", json={"reviewed": True}) -    async def make_review(self, user_id: int) -> typing.Tuple[str, Optional[Emoji]]: -        """Format a generic review of a user and return it with the reviewed emoji.""" +    async def make_review(self, user_id: int) -> typing.Tuple[str, Optional[Emoji], Optional[Member]]: +        """Format a generic review of a user and return it with the reviewed emoji and the user themselves."""          log.trace(f"Formatting the review of {user_id}")          # Since `cache` is a defaultdict, we should take care @@ -110,17 +115,17 @@ class Reviewer:          nomination = self._pool.cache.get(user_id)          if not nomination:              log.trace(f"There doesn't appear to be an active nomination for {user_id}") -            return "", None +            return f"There doesn't appear to be an active nomination for {user_id}", None, None          guild = self.bot.get_guild(Guild.id) -        member = await get_or_fetch_member(guild, user_id) +        nominee = await get_or_fetch_member(guild, user_id) -        if not member: +        if not nominee:              return (                  f"I tried to review the user with ID `{user_id}`, but they don't appear to be on the server :pensive:" -            ), None +            ), None, None -        opening = f"{member.mention} ({member}) for Helper!" +        opening = f"{nominee.mention} ({nominee}) for Helper!"          current_nominations = "\n\n".join(              f"**<@{entry['actor']}>:** {entry['reason'] or '*no reason given*'}" @@ -128,7 +133,7 @@ class Reviewer:          )          current_nominations = f"**Nominated by:**\n{current_nominations}" -        review_body = await self._construct_review_body(member) +        review_body = await self._construct_review_body(nominee)          reviewed_emoji = self._random_ducky(guild)          vote_request = ( @@ -138,7 +143,7 @@ class Reviewer:          )          review = "\n\n".join((opening, current_nominations, review_body, vote_request)) -        return review, reviewed_emoji +        return review, reviewed_emoji, nominee      async def archive_vote(self, message: PartialMessage, passed: bool) -> None:          """Archive this vote to #nomination-archive.""" @@ -210,8 +215,18 @@ class Reviewer:                  colour=colour              )) +        # Thread channel IDs are the same as the message ID of the parent message. +        nomination_thread = message.guild.get_thread(message.id) +        if not nomination_thread: +            log.warning(f"Could not find a thread linked to {message.channel.id}-{message.id}") +            return +          for message_ in messages: -            await message_.delete() +            with contextlib.suppress(NotFound): +                await message_.delete() + +        with contextlib.suppress(NotFound): +            await nomination_thread.edit(archived=True)      async def _construct_review_body(self, member: Member) -> str:          """Formats the body of the nomination, with details of activity, infractions, and previous nominations.""" @@ -360,10 +375,10 @@ class Reviewer:      @staticmethod      def _random_ducky(guild: Guild) -> Union[Emoji, str]: -        """Picks a random ducky emoji. If no duckies found returns :eyes:.""" +        """Picks a random ducky emoji. If no duckies found returns 👀."""          duckies = [emoji for emoji in guild.emojis if emoji.name.startswith("ducky")]          if not duckies: -            return ":eyes:" +            return "\N{EYES}"          return random.choice(duckies)      @staticmethod | 
