aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorGravatar ChrisJL <[email protected]>2021-04-11 20:17:49 +0100
committerGravatar GitHub <[email protected]>2021-04-11 20:17:49 +0100
commita5b586eae2408b3d1897c4543f1386b2cb6e1325 (patch)
treecbe556a219dc8ed53647393a6404f3fd56e6abf2
parentFiltering: use a more thorough regex for zalgo & invisible chars (diff)
parentMerge pull request #1414 from python-discord/dynamic-available-message-patch (diff)
Merge branch 'main' into bug/filters/1469/invis-chars
-rw-r--r--bot/constants.py1
-rw-r--r--bot/exts/help_channels/_caches.py4
-rw-r--r--bot/exts/help_channels/_cog.py48
-rw-r--r--bot/exts/recruitment/talentpool/_cog.py19
-rw-r--r--bot/exts/recruitment/talentpool/_review.py51
-rw-r--r--config-default.yml1
6 files changed, 101 insertions, 23 deletions
diff --git a/bot/constants.py b/bot/constants.py
index 547a94a0b..6d14bbb3a 100644
--- a/bot/constants.py
+++ b/bot/constants.py
@@ -412,6 +412,7 @@ class Channels(metaclass=YAMLGetter):
python_general: int
cooldown: int
+ how_to_get_help: int
attachment_log: int
message_log: int
diff --git a/bot/exts/help_channels/_caches.py b/bot/exts/help_channels/_caches.py
index e741fd20f..c5e4ee917 100644
--- a/bot/exts/help_channels/_caches.py
+++ b/bot/exts/help_channels/_caches.py
@@ -20,3 +20,7 @@ non_claimant_last_message_times = RedisCache(namespace="HelpChannels.non_claiman
# This cache maps a help channel to original question message in same channel.
# RedisCache[discord.TextChannel.id, discord.Message.id]
question_messages = RedisCache(namespace="HelpChannels.question_messages")
+
+# This cache keeps track of the dynamic message ID for
+# the continuously updated message in the #How-to-get-help channel.
+dynamic_message = RedisCache(namespace="HelpChannels.dynamic_message")
diff --git a/bot/exts/help_channels/_cog.py b/bot/exts/help_channels/_cog.py
index 18457f6a5..c18fcf875 100644
--- a/bot/exts/help_channels/_cog.py
+++ b/bot/exts/help_channels/_cog.py
@@ -21,6 +21,7 @@ NAMESPACE = "help"
HELP_CHANNEL_TOPIC = """
This is a Python help channel. You can claim your own help channel in the Python Help: Available category.
"""
+AVAILABLE_HELP_CHANNELS = "**Currently available help channel(s):** {available}"
class HelpChannels(commands.Cog):
@@ -75,6 +76,9 @@ class HelpChannels(commands.Cog):
self.last_notification: t.Optional[arrow.Arrow] = None
+ self.dynamic_message: t.Optional[int] = None
+ self.available_help_channels: t.Set[discord.TextChannel] = set()
+
# Asyncio stuff
self.queue_tasks: t.List[asyncio.Task] = []
self.init_task = self.bot.loop.create_task(self.init_cog())
@@ -123,6 +127,9 @@ class HelpChannels(commands.Cog):
# Delete to indicate that the help session has yet to receive an answer.
await _caches.non_claimant_last_message_times.delete(message.channel.id)
+ # Removing the help channel from the dynamic message, and editing/sending that message.
+ self.available_help_channels.remove(message.channel)
+
# Not awaited because it may indefinitely hold the lock while waiting for a channel.
scheduling.create_task(self.move_to_available(), name=f"help_claim_{message.id}")
@@ -240,6 +247,10 @@ class HelpChannels(commands.Cog):
for channel in channels[:abs(missing)]:
await self.unclaim_channel(channel, closed_on=_channel.ClosingReason.CLEANUP)
+ # Getting channels that need to be included in the dynamic message.
+ await self.update_available_help_channels()
+ log.trace("Dynamic available help message updated.")
+
async def init_categories(self) -> None:
"""Get the help category objects. Remove the cog if retrieval fails."""
log.trace("Getting the CategoryChannel objects for the help categories.")
@@ -284,6 +295,10 @@ class HelpChannels(commands.Cog):
# This may confuse users. So would potentially long delays for the cog to become ready.
self.close_command.enabled = True
+ # Acquiring the dynamic message ID, if it exists within the cache.
+ log.trace("Attempting to fetch How-to-get-help dynamic message ID.")
+ self.dynamic_message = await _caches.dynamic_message.get("message_id")
+
await self.init_available()
_stats.report_counts()
@@ -338,6 +353,10 @@ class HelpChannels(commands.Cog):
category_id=constants.Categories.help_available,
)
+ # Adding the help channel to the dynamic message, and editing/sending that message.
+ self.available_help_channels.add(channel)
+ await self.update_available_help_channels()
+
_stats.report_counts()
async def move_to_dormant(self, channel: discord.TextChannel) -> None:
@@ -472,3 +491,32 @@ class HelpChannels(commands.Cog):
self.queue_tasks.remove(task)
return channel
+
+ async def update_available_help_channels(self) -> None:
+ """Updates the dynamic message within #how-to-get-help for available help channels."""
+ if not self.available_help_channels:
+ self.available_help_channels = set(
+ c for c in self.available_category.channels if not _channel.is_excluded_channel(c)
+ )
+
+ available_channels = AVAILABLE_HELP_CHANNELS.format(
+ available=', '.join(c.mention for c in self.available_help_channels) or None
+ )
+
+ if self.dynamic_message is not None:
+ try:
+ log.trace("Help channels have changed, dynamic message has been edited.")
+ await self.bot.http.edit_message(
+ constants.Channels.how_to_get_help, self.dynamic_message, content=available_channels
+ )
+ except discord.NotFound:
+ pass
+ else:
+ return
+
+ log.trace("Dynamic message could not be edited or found. Creating a new one.")
+ new_dynamic_message = await self.bot.http.send_message(
+ constants.Channels.how_to_get_help, available_channels
+ )
+ self.dynamic_message = new_dynamic_message["id"]
+ await _caches.dynamic_message.set("message_id", self.dynamic_message)
diff --git a/bot/exts/recruitment/talentpool/_cog.py b/bot/exts/recruitment/talentpool/_cog.py
index fbe79382d..72604be51 100644
--- a/bot/exts/recruitment/talentpool/_cog.py
+++ b/bot/exts/recruitment/talentpool/_cog.py
@@ -1,14 +1,16 @@
import logging
import textwrap
from collections import ChainMap
+from io import StringIO
from typing import Union
+import discord
from discord import Color, Embed, Member, User
from discord.ext.commands import Cog, Context, group, has_any_role
from bot.api import ResponseCodeError
from bot.bot import Bot
-from bot.constants import Channels, Guild, MODERATION_ROLES, STAFF_ROLES, Webhooks
+from bot.constants import Channels, Emojis, Guild, MODERATION_ROLES, STAFF_ROLES, Webhooks
from bot.converters import FetchedMember
from bot.exts.moderation.watchchannels._watchchannel import WatchChannel
from bot.exts.recruitment.talentpool._review import Reviewer
@@ -330,7 +332,18 @@ class TalentPool(WatchChannel, Cog, name="Talentpool"):
"""Mark a user's nomination as reviewed and cancel the review task."""
if not await self.reviewer.mark_reviewed(ctx, user_id):
return
- await ctx.send(f"✅ The user with ID `{user_id}` was marked as reviewed.")
+ await ctx.send(f"{Emojis.check_mark} The user with ID `{user_id}` was marked as reviewed.")
+
+ @nomination_group.command(aliases=('gr',))
+ @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}")
@nomination_group.command(aliases=('review',))
@has_any_role(*MODERATION_ROLES)
@@ -340,7 +353,7 @@ class TalentPool(WatchChannel, Cog, name="Talentpool"):
return
await self.reviewer.post_review(user_id, update_database=False)
- await ctx.message.add_reaction("✅")
+ await ctx.message.add_reaction(Emojis.check_mark)
@Cog.listener()
async def on_member_ban(self, guild: Guild, user: Union[User, Member]) -> None:
diff --git a/bot/exts/recruitment/talentpool/_review.py b/bot/exts/recruitment/talentpool/_review.py
index fb3461238..11aa3b62b 100644
--- a/bot/exts/recruitment/talentpool/_review.py
+++ b/bot/exts/recruitment/talentpool/_review.py
@@ -66,26 +66,40 @@ class Reviewer:
self._review_scheduler.schedule_at(review_at, user_id, self.post_review(user_id, update_database=True))
async def post_review(self, user_id: int, update_database: bool) -> None:
- """Format a generic review of a user and post it to the nomination voting channel."""
+ """Format the review of a user and post it to the nomination voting channel."""
+ review, seen_emoji = await self.make_review(user_id)
+ if not review:
+ return
+
+ guild = self.bot.get_guild(Guild.id)
+ channel = guild.get_channel(Channels.nomination_voting)
+
log.trace(f"Posting the review of {user_id}")
+ message = (await self._bulk_send(channel, review))[-1]
+ if seen_emoji:
+ for reaction in (seen_emoji, "\N{THUMBS UP SIGN}", "\N{THUMBS DOWN SIGN}"):
+ await message.add_reaction(reaction)
+
+ if update_database:
+ nomination = self._pool.watched_users[user_id]
+ await self.bot.api_client.patch(f"{self._pool.api_endpoint}/{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 seen emoji."""
+ log.trace(f"Formatting the review of {user_id}")
nomination = self._pool.watched_users[user_id]
if not nomination:
log.trace(f"There doesn't appear to be an active nomination for {user_id}")
- return
+ return "", None
guild = self.bot.get_guild(Guild.id)
- channel = guild.get_channel(Channels.nomination_voting)
member = guild.get_member(user_id)
- if update_database:
- await self.bot.api_client.patch(f"{self._pool.api_endpoint}/{nomination['id']}", json={"reviewed": True})
-
if not member:
- await channel.send(
- f"I tried to review the user with ID `{user_id}`, but they don't appear to be on the server 😔"
- )
- return
+ return (
+ f"I tried to review the user with ID `{user_id}`, but they don't appear to be on the server :pensive:"
+ ), None
opening = f"<@&{Roles.moderators}> <@&{Roles.admins}>\n{member.mention} ({member}) for Helper!"
@@ -100,14 +114,11 @@ class Reviewer:
vote_request = (
"*Refer to their nomination and infraction histories for further details*.\n"
f"*Please react {seen_emoji} if you've seen this post."
- " Then react 👍 for approval, or 👎 for disapproval*."
+ " Then react :+1: for approval, or :-1: for disapproval*."
)
- review = "\n\n".join(part for part in (opening, current_nominations, review_body, vote_request))
-
- message = (await self._bulk_send(channel, review))[-1]
- for reaction in (seen_emoji, "👍", "👎"):
- await message.add_reaction(reaction)
+ review = "\n\n".join((opening, current_nominations, review_body, vote_request))
+ return review, seen_emoji
async def _construct_review_body(self, member: Member) -> str:
"""Formats the body of the nomination, with details of activity, infractions, and previous nominations."""
@@ -256,10 +267,10 @@ class Reviewer:
@staticmethod
def _random_ducky(guild: Guild) -> Union[Emoji, str]:
- """Picks a random ducky emoji to be used to mark the vote as seen. If no duckies found returns 👀."""
+ """Picks a random ducky emoji to be used to mark the vote as seen. If no duckies found returns :eyes:."""
duckies = [emoji for emoji in guild.emojis if emoji.name.startswith("ducky")]
if not duckies:
- return "👀"
+ return ":eyes:"
return random.choice(duckies)
@staticmethod
@@ -289,12 +300,12 @@ class Reviewer:
await self._pool.fetch_user_cache()
if user_id not in self._pool.watched_users:
log.trace(f"Can't find a nominated user with id {user_id}")
- await ctx.send(f"❌ Can't find a currently nominated user with id `{user_id}`")
+ await ctx.send(f":x: Can't find a currently nominated user with id `{user_id}`")
return False
nomination = self._pool.watched_users[user_id]
if nomination["reviewed"]:
- await ctx.send("❌ This nomination was already reviewed, but here's a cookie 🍪")
+ await ctx.send(":x: This nomination was already reviewed, but here's a cookie :cookie:")
return False
await self.bot.api_client.patch(f"{self._pool.api_endpoint}/{nomination['id']}", json={"reviewed": True})
diff --git a/config-default.yml b/config-default.yml
index 9b07d026d..8c6e18470 100644
--- a/config-default.yml
+++ b/config-default.yml
@@ -163,6 +163,7 @@ guild:
# Python Help: Available
cooldown: 720603994149486673
+ how_to_get_help: 704250143020417084
# Topical
discord_py: 343944376055103488