From 78b00dc8f43d7cb25ce72bc97d6236ec9944cd1f Mon Sep 17 00:00:00 2001 From: ks129 <45097959+ks129@users.noreply.github.com> Date: Sun, 28 Mar 2021 09:10:35 +0300 Subject: Remove WatchChannel parent of TalentPool and migrate first commands --- bot/exts/recruitment/talentpool/_cog.py | 80 +++++++++++++++++++++------------ 1 file changed, 52 insertions(+), 28 deletions(-) diff --git a/bot/exts/recruitment/talentpool/_cog.py b/bot/exts/recruitment/talentpool/_cog.py index b809cea17..09f2fecbd 100644 --- a/bot/exts/recruitment/talentpool/_cog.py +++ b/bot/exts/recruitment/talentpool/_cog.py @@ -1,6 +1,6 @@ import logging import textwrap -from collections import ChainMap +from collections import ChainMap, defaultdict from typing import Union from discord import Color, Embed, Member, User @@ -8,35 +8,48 @@ 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 Guild, MODERATION_ROLES, STAFF_ROLES from bot.converters import FetchedMember -from bot.exts.moderation.watchchannels._watchchannel import WatchChannel from bot.exts.recruitment.talentpool._review import Reviewer from bot.pagination import LinePaginator from bot.utils import time +from bot.utils.time import get_time_delta REASON_MAX_CHARS = 1000 log = logging.getLogger(__name__) -class TalentPool(WatchChannel, Cog, name="Talentpool"): +class TalentPool(Cog, name="Talentpool"): """Relays messages of helper candidates to a watch channel to observe them.""" def __init__(self, bot: Bot) -> None: - super().__init__( - bot, - destination=Channels.talent_pool, - webhook_id=Webhooks.talent_pool, - api_endpoint='bot/nominations', - api_default_params={'active': 'true', 'ordering': '-inserted_at'}, - logger=log, - disable_header=True, - ) - + self.bot = bot self.reviewer = Reviewer(self.__class__.__name__, bot, self) self.bot.loop.create_task(self.reviewer.reschedule_reviews()) + # Stores talentpool users in cache + self.cache = defaultdict(dict) + + async def refresh_cache(self) -> bool: + """Updates TalentPool users cache.""" + try: + data = await self.bot.api_client.get( + 'bot/nominations', + params={'active': 'true', 'ordering': '-inserted_at'} + ) + except ResponseCodeError as err: + log.exception("Failed to fetch the watched users from the API", exc_info=err) + return False + + self.cache = defaultdict(dict) + + for entry in data: + user_id = entry.pop('user') + self.cache[user_id] = entry + + return True + @group(name='talentpool', aliases=('tp', 'talent', 'nomination', 'n'), invoke_without_command=True) @has_any_role(*MODERATION_ROLES) async def nomination_group(self, ctx: Context) -> None: @@ -78,26 +91,37 @@ class TalentPool(WatchChannel, Cog, name="Talentpool"): The optional kwarg `update_cache` specifies whether the cache should be refreshed by polling the API. """ - # TODO Once the watch channel is removed, this can be done in a smarter way, without splitting and overriding - # the list_watched_users function. - watched_data = await self.prepare_watched_users_data(ctx, oldest_first, update_cache) + successful_update = False + if update_cache: + if not (successful_update := await self.refresh_cache()): + await ctx.send(":warning: Unable to update cache. Data may be inaccurate.") - if update_cache and not watched_data["updated"]: - await ctx.send(f":x: Failed to update {self.__class__.__name__} user cache, serving from cache") + nominations = self.cache.items() + if oldest_first: + nominations = reversed(nominations) lines = [] - for user_id, line in watched_data["info"].items(): - if self.watched_users[user_id]['reviewed']: + + for user_id, user_data in nominations: + member = ctx.guild.get_member(user_id) + line = f"• `{user_id}`" + if member: + line += f" ({member.name}#{member.discriminator})" + inserted_at = user_data['inserted_at'] + line += f", added {get_time_delta(inserted_at)}" + if not member: # Cross off users who left the server. + line = f"~~{line}~~" + if user_data['reviewed']: line += " *(reviewed)*" elif user_id in self.reviewer: - line += " *(scheduled)*" + line += " *(scheduled)" lines.append(line) if not lines: lines = ("There's nothing here yet.",) embed = Embed( - title=watched_data["title"], + title=f"Talent Pool active nominations ({'updated' if update_cache and successful_update else 'cached'})", color=Color.blue() ) await LinePaginator.paginate(lines, ctx, embed, empty=False) @@ -130,8 +154,8 @@ class TalentPool(WatchChannel, Cog, name="Talentpool"): await ctx.send(":x: Nominating staff members, eh? Here's a cookie :cookie:") return - if not await self.fetch_user_cache(): - await ctx.send(f":x: Failed to update the user cache; can't add {user}") + if not await self.refresh_cache(): + await ctx.send(f":x: Failed to update the cache; can't add {user}") return if len(reason) > REASON_MAX_CHARS: @@ -140,7 +164,7 @@ class TalentPool(WatchChannel, Cog, name="Talentpool"): # Manual request with `raise_for_status` as False because we want the actual response session = self.bot.api_client.session - url = self.bot.api_client._url_for(self.api_endpoint) + url = self.bot.api_client._url_for('bot/nominations') kwargs = { 'json': { 'actor': ctx.author.id, @@ -162,13 +186,13 @@ class TalentPool(WatchChannel, Cog, name="Talentpool"): else: resp.raise_for_status() - self.watched_users[user.id] = response_data + self.cache[user.id] = response_data if user.id not in self.reviewer: self.reviewer.schedule_review(user.id) history = await self.bot.api_client.get( - self.api_endpoint, + 'bot/nominations', params={ "user__id": str(user.id), "active": "false", -- cgit v1.2.3 From dda3490a0a1a55bb3ccb16b82afc0dd131f335b5 Mon Sep 17 00:00:00 2001 From: ks129 <45097959+ks129@users.noreply.github.com> Date: Sun, 28 Mar 2021 09:19:51 +0300 Subject: Use more accurate command names and docstring for talent pool commands --- bot/exts/recruitment/talentpool/_cog.py | 27 +++++++++++++-------------- 1 file changed, 13 insertions(+), 14 deletions(-) diff --git a/bot/exts/recruitment/talentpool/_cog.py b/bot/exts/recruitment/talentpool/_cog.py index 09f2fecbd..40fb90f11 100644 --- a/bot/exts/recruitment/talentpool/_cog.py +++ b/bot/exts/recruitment/talentpool/_cog.py @@ -56,25 +56,25 @@ class TalentPool(Cog, name="Talentpool"): """Highlights the activity of helper nominees by relaying their messages to the talent pool channel.""" await ctx.send_help(ctx.command) - @nomination_group.command(name='watched', aliases=('all', 'list'), root_aliases=("nominees",)) + @nomination_group.command(name='list', aliases=('all', 'watched'), root_aliases=("nominees",)) @has_any_role(*MODERATION_ROLES) - async def watched_command( + async def list_command( self, ctx: Context, oldest_first: bool = False, update_cache: bool = True ) -> None: """ - Shows the users that are currently being monitored in the talent pool. + Shows the users that are currently in the talent pool. The optional kwarg `oldest_first` can be used to order the list by oldest nomination. The optional kwarg `update_cache` can be used to update the user cache using the API before listing the users. """ - await self.list_watched_users(ctx, oldest_first=oldest_first, update_cache=update_cache) + await self.list_users(ctx, oldest_first=oldest_first, update_cache=update_cache) - async def list_watched_users( + async def list_users( self, ctx: Context, oldest_first: bool = False, @@ -130,21 +130,20 @@ class TalentPool(Cog, name="Talentpool"): @has_any_role(*MODERATION_ROLES) async def oldest_command(self, ctx: Context, update_cache: bool = True) -> None: """ - Shows talent pool monitored users ordered by oldest nomination. + Shows talent pool users ordered by oldest nomination. The optional kwarg `update_cache` can be used to update the user cache using the API before listing the users. """ - await ctx.invoke(self.watched_command, oldest_first=True, update_cache=update_cache) + await ctx.invoke(self.list_command, oldest_first=True, update_cache=update_cache) - @nomination_group.command(name='watch', aliases=('w', 'add', 'a'), root_aliases=("nominate",)) + @nomination_group.command(name='add', aliases=('w', 'a', 'watch'), root_aliases=("nominate",)) @has_any_role(*STAFF_ROLES) - async def watch_command(self, ctx: Context, user: FetchedMember, *, reason: str = '') -> None: + async def add_command(self, ctx: Context, user: FetchedMember, *, reason: str = '') -> None: """ - Relay messages sent by the given `user` to the `#talent-pool` channel. + Adds user nomination (or nomination entry) to Talent Pool. - A `reason` for adding the user to the talent pool is optional. - If given, it will be displayed in the header when relaying messages of this user to the channel. + If user already have nomination, then entry associated with existing nomination will be created. """ if user.bot: await ctx.send(f":x: I'm sorry {ctx.author}, I'm afraid I can't do that. I only watch humans.") @@ -235,9 +234,9 @@ class TalentPool(Cog, name="Talentpool"): max_size=1000 ) - @nomination_group.command(name='unwatch', aliases=('end', ), root_aliases=("unnominate",)) + @nomination_group.command(name='end', aliases=('unwatch',), root_aliases=("unnominate",)) @has_any_role(*MODERATION_ROLES) - async def unwatch_command(self, ctx: Context, user: FetchedMember, *, reason: str) -> None: + async def end_command(self, ctx: Context, user: FetchedMember, *, reason: str) -> None: """ Ends the active nomination of the specified user with the given reason. -- cgit v1.2.3 From f144bace9243659887cbb125ea915f809736fc97 Mon Sep 17 00:00:00 2001 From: ks129 <45097959+ks129@users.noreply.github.com> Date: Sun, 28 Mar 2021 09:22:13 +0300 Subject: Migrate nominations history command to non-watchchannel system --- bot/exts/recruitment/talentpool/_cog.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/bot/exts/recruitment/talentpool/_cog.py b/bot/exts/recruitment/talentpool/_cog.py index 40fb90f11..ee5dc5dae 100644 --- a/bot/exts/recruitment/talentpool/_cog.py +++ b/bot/exts/recruitment/talentpool/_cog.py @@ -210,7 +210,7 @@ class TalentPool(Cog, name="Talentpool"): async def history_command(self, ctx: Context, user: FetchedMember) -> None: """Shows the specified user's nomination history.""" result = await self.bot.api_client.get( - self.api_endpoint, + 'bot/nominations', params={ 'user__id': str(user.id), 'ordering': "-active,-inserted_at" -- cgit v1.2.3 From 807a27ec112684a2a2b8cfd2b470ab8378f0da98 Mon Sep 17 00:00:00 2001 From: ks129 <45097959+ks129@users.noreply.github.com> Date: Sun, 28 Mar 2021 09:30:46 +0300 Subject: Migrate Talent Pool Reviewer class to non-watchchannel Talent Pool --- bot/exts/recruitment/talentpool/_review.py | 20 ++++++++++---------- 1 file changed, 10 insertions(+), 10 deletions(-) diff --git a/bot/exts/recruitment/talentpool/_review.py b/bot/exts/recruitment/talentpool/_review.py index fb3461238..bc2878451 100644 --- a/bot/exts/recruitment/talentpool/_review.py +++ b/bot/exts/recruitment/talentpool/_review.py @@ -47,9 +47,9 @@ class Reviewer: log.trace("Rescheduling reviews") await self.bot.wait_until_guild_available() # TODO Once the watch channel is removed, this can be done in a smarter way, e.g create a sync function. - await self._pool.fetch_user_cache() + await self._pool.refresh_cache() - for user_id, user_data in self._pool.watched_users.items(): + for user_id, user_data in self._pool.cache.items(): if not user_data["reviewed"]: self.schedule_review(user_id) @@ -57,7 +57,7 @@ class Reviewer: """Schedules a single user for review.""" log.trace(f"Scheduling review of user with ID {user_id}") - user_data = self._pool.watched_users[user_id] + user_data = self._pool.cache[user_id] inserted_at = isoparse(user_data['inserted_at']).replace(tzinfo=None) review_at = inserted_at + timedelta(days=MAX_DAYS_IN_POOL) @@ -69,7 +69,7 @@ class Reviewer: """Format a generic review of a user and post it to the nomination voting channel.""" log.trace(f"Posting the review of {user_id}") - nomination = self._pool.watched_users[user_id] + nomination = self._pool.cache[user_id] if not nomination: log.trace(f"There doesn't appear to be an active nomination for {user_id}") return @@ -79,7 +79,7 @@ class Reviewer: 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}) + await self.bot.api_client.patch(f"bot/nominations/{nomination['id']}", json={"reviewed": True}) if not member: await channel.send( @@ -228,7 +228,7 @@ class Reviewer: """ log.trace(f"Fetching the nomination history data for {member.id}'s review") history = await self.bot.api_client.get( - self._pool.api_endpoint, + "bot/nominations", params={ "user__id": str(member.id), "active": "false", @@ -286,18 +286,18 @@ class Reviewer: Returns True if the user was successfully marked as reviewed, False otherwise. """ log.trace(f"Updating user {user_id} as reviewed") - await self._pool.fetch_user_cache() - if user_id not in self._pool.watched_users: + await self._pool.refresh_cache() + if user_id not in self._pool.cache: 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}`") return False - nomination = self._pool.watched_users[user_id] + nomination = self._pool.cache[user_id] if nomination["reviewed"]: await ctx.send("❌ This nomination was already reviewed, but here's a cookie 🍪") return False - await self.bot.api_client.patch(f"{self._pool.api_endpoint}/{nomination['id']}", json={"reviewed": True}) + await self.bot.api_client.patch(f"bot/nominations/{nomination['id']}", json={"reviewed": True}) if user_id in self._review_scheduler: self._review_scheduler.cancel(user_id) -- cgit v1.2.3 From 2d4764041da02ccee64a93c02c0dd392c31e80c4 Mon Sep 17 00:00:00 2001 From: ks129 <45097959+ks129@users.noreply.github.com> Date: Sun, 28 Mar 2021 09:31:24 +0300 Subject: Migrate unnominate command to non-watchchannel --- bot/exts/recruitment/talentpool/_cog.py | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/bot/exts/recruitment/talentpool/_cog.py b/bot/exts/recruitment/talentpool/_cog.py index ee5dc5dae..1ad40812a 100644 --- a/bot/exts/recruitment/talentpool/_cog.py +++ b/bot/exts/recruitment/talentpool/_cog.py @@ -30,13 +30,14 @@ class TalentPool(Cog, name="Talentpool"): # Stores talentpool users in cache self.cache = defaultdict(dict) + self.api_default_params = {'active': 'true', 'ordering': '-inserted_at'} async def refresh_cache(self) -> bool: """Updates TalentPool users cache.""" try: data = await self.bot.api_client.get( 'bot/nominations', - params={'active': 'true', 'ordering': '-inserted_at'} + params=self.api_default_params ) except ResponseCodeError as err: log.exception("Failed to fetch the watched users from the API", exc_info=err) @@ -243,11 +244,11 @@ class TalentPool(Cog, name="Talentpool"): Providing a `reason` is required. """ if len(reason) > REASON_MAX_CHARS: - await ctx.send(f":x: Maxiumum allowed characters for the end reason is {REASON_MAX_CHARS}.") + await ctx.send(f":x: Maximum allowed characters for the end reason is {REASON_MAX_CHARS}.") return if await self.unwatch(user.id, reason): - await ctx.send(f":white_check_mark: Messages sent by {user} will no longer be relayed") + await ctx.send(f":white_check_mark: Successfully un-nominated {user}") else: await ctx.send(":x: The specified user does not have an active nomination") @@ -349,7 +350,7 @@ class TalentPool(Cog, name="Talentpool"): 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, + 'bot/nominations', params=ChainMap( {"user__id": str(user_id)}, self.api_default_params, @@ -364,10 +365,9 @@ class TalentPool(Cog, name="Talentpool"): nomination = active_nomination[0] await self.bot.api_client.patch( - f"{self.api_endpoint}/{nomination['id']}", + f"bot/nominations/{nomination['id']}", json={'end_reason': reason, 'active': False} ) - self._remove_user(user_id) self.reviewer.cancel(user_id) -- cgit v1.2.3 From ae3c91082f1a456d37c8ce72c65509315e4a2137 Mon Sep 17 00:00:00 2001 From: ks129 <45097959+ks129@users.noreply.github.com> Date: Sun, 28 Mar 2021 09:32:58 +0300 Subject: Add missing asterisk to nominees list --- bot/exts/recruitment/talentpool/_cog.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/bot/exts/recruitment/talentpool/_cog.py b/bot/exts/recruitment/talentpool/_cog.py index 1ad40812a..4ef3af3d1 100644 --- a/bot/exts/recruitment/talentpool/_cog.py +++ b/bot/exts/recruitment/talentpool/_cog.py @@ -115,7 +115,7 @@ class TalentPool(Cog, name="Talentpool"): if user_data['reviewed']: line += " *(reviewed)*" elif user_id in self.reviewer: - line += " *(scheduled)" + line += " *(scheduled)*" lines.append(line) if not lines: -- cgit v1.2.3 From 4716b8eba3d25f21e277ee20cff2741cece2e0ed Mon Sep 17 00:00:00 2001 From: ks129 <45097959+ks129@users.noreply.github.com> Date: Sun, 28 Mar 2021 09:52:53 +0300 Subject: Migrate nomination reasons editing commands --- bot/exts/recruitment/talentpool/_cog.py | 20 ++++++++++---------- 1 file changed, 10 insertions(+), 10 deletions(-) diff --git a/bot/exts/recruitment/talentpool/_cog.py b/bot/exts/recruitment/talentpool/_cog.py index 4ef3af3d1..7824d22d7 100644 --- a/bot/exts/recruitment/talentpool/_cog.py +++ b/bot/exts/recruitment/talentpool/_cog.py @@ -267,10 +267,10 @@ class TalentPool(Cog, name="Talentpool"): return try: - nomination = await self.bot.api_client.get(f"{self.api_endpoint}/{nomination_id}") + nomination = await self.bot.api_client.get(f"bot/nominations/{nomination_id}") except ResponseCodeError as e: if e.response.status == 404: - self.log.trace(f"Nomination API 404: Can't find a nomination with id {nomination_id}") + log.trace(f"Nomination API 404: Can't find a nomination with id {nomination_id}") await ctx.send(f":x: Can't find a nomination with id `{nomination_id}`") return else: @@ -284,13 +284,13 @@ class TalentPool(Cog, name="Talentpool"): await ctx.send(f":x: {actor} doesn't have an entry in this nomination.") return - self.log.trace(f"Changing reason for nomination with id {nomination_id} of actor {actor} to {repr(reason)}") + log.trace(f"Changing reason for nomination with id {nomination_id} of actor {actor} to {repr(reason)}") await self.bot.api_client.patch( - f"{self.api_endpoint}/{nomination_id}", + f"bot/nominations/{nomination_id}", json={"actor": actor.id, "reason": reason} ) - await self.fetch_user_cache() # Update cache + await self.refresh_cache() # Update cache await ctx.send(":white_check_mark: Successfully updated nomination reason.") @nomination_edit_group.command(name='end_reason') @@ -302,10 +302,10 @@ class TalentPool(Cog, name="Talentpool"): return try: - nomination = await self.bot.api_client.get(f"{self.api_endpoint}/{nomination_id}") + nomination = await self.bot.api_client.get(f"bot/nominations/{nomination_id}") except ResponseCodeError as e: if e.response.status == 404: - self.log.trace(f"Nomination API 404: Can't find a nomination with id {nomination_id}") + log.trace(f"Nomination API 404: Can't find a nomination with id {nomination_id}") await ctx.send(f":x: Can't find a nomination with id `{nomination_id}`") return else: @@ -315,13 +315,13 @@ class TalentPool(Cog, name="Talentpool"): await ctx.send(":x: Can't edit the end reason of an active nomination.") return - self.log.trace(f"Changing end reason for nomination with id {nomination_id} to {repr(reason)}") + log.trace(f"Changing end reason for nomination with id {nomination_id} to {repr(reason)}") await self.bot.api_client.patch( - f"{self.api_endpoint}/{nomination_id}", + f"bot/nominations/{nomination_id}", json={"end_reason": reason} ) - await self.fetch_user_cache() # Update cache. + await self.refresh_cache() # Update cache. await ctx.send(":white_check_mark: Updated the end reason of the nomination!") @nomination_group.command(aliases=('mr',)) -- cgit v1.2.3 From e9a233f1f2fbc9b0204041dffa0f3ad6a1f7e8cf Mon Sep 17 00:00:00 2001 From: ks129 <45097959+ks129@users.noreply.github.com> Date: Sun, 28 Mar 2021 10:17:47 +0300 Subject: Remove talentpool channel constants --- bot/constants.py | 2 -- config-default.yml | 3 --- 2 files changed, 5 deletions(-) diff --git a/bot/constants.py b/bot/constants.py index 467a4a2c4..840f6fbf6 100644 --- a/bot/constants.py +++ b/bot/constants.py @@ -457,7 +457,6 @@ class Channels(metaclass=YAMLGetter): voice_chat: int big_brother_logs: int - talent_pool: int class Webhooks(metaclass=YAMLGetter): @@ -470,7 +469,6 @@ class Webhooks(metaclass=YAMLGetter): duck_pond: int incidents_archive: int reddit: int - talent_pool: int class Roles(metaclass=YAMLGetter): diff --git a/config-default.yml b/config-default.yml index 502f0f861..8fc1f6718 100644 --- a/config-default.yml +++ b/config-default.yml @@ -223,7 +223,6 @@ guild: # Watch big_brother_logs: &BB_LOGS 468507907357409333 - talent_pool: &TALENT_POOL 534321732593647616 moderation_categories: - *MODMAIL @@ -292,7 +291,6 @@ guild: incidents_archive: 720671599790915702 python_news: &PYNEWS_WEBHOOK 704381182279942324 reddit: 635408384794951680 - talent_pool: 569145364800602132 filter: @@ -323,7 +321,6 @@ filter: - *MESSAGE_LOG - *MOD_LOG - *STAFF_LOUNGE - - *TALENT_POOL - *USER_EVENT_A role_whitelist: -- cgit v1.2.3 From d1a2d9965fadc5292685d3ca78a75fd16f07af26 Mon Sep 17 00:00:00 2001 From: Xithrius Date: Sat, 12 Jun 2021 14:08:52 -0700 Subject: Switched back to default value getting. --- bot/exts/recruitment/talentpool/_review.py | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/bot/exts/recruitment/talentpool/_review.py b/bot/exts/recruitment/talentpool/_review.py index 585640699..4e61ecb3e 100644 --- a/bot/exts/recruitment/talentpool/_review.py +++ b/bot/exts/recruitment/talentpool/_review.py @@ -65,7 +65,7 @@ class Reviewer: """Schedules a single user for review.""" log.trace(f"Scheduling review of user with ID {user_id}") - user_data = self._pool.cache[user_id] + user_data = self._pool.cache.get(user_id) inserted_at = isoparse(user_data['inserted_at']).replace(tzinfo=None) review_at = inserted_at + timedelta(days=MAX_DAYS_IN_POOL) @@ -93,7 +93,7 @@ class Reviewer: await last_message.add_reaction(reaction) if update_database: - nomination = self._pool.cache[user_id] + nomination = self._pool.cache.get(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]]: @@ -104,7 +104,7 @@ class Reviewer: # not to accidentally insert the IDs of users that have no # active nominated by using the `watched_users.get(user_id)` # instead of `watched_users[user_id]`. - nomination = self._pool.cache[user_id] + 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 @@ -393,7 +393,7 @@ class Reviewer: await ctx.send(f":x: Can't find a currently nominated user with id `{user_id}`") return False - nomination = self._pool.cache[user_id] + nomination = self._pool.cache.get(user_id) if nomination["reviewed"]: await ctx.send(":x: This nomination was already reviewed, but here's a cookie :cookie:") return False -- cgit v1.2.3 From 2a83c5f8f7b9bfd60a50e67f20c04f31651bbcaa Mon Sep 17 00:00:00 2001 From: Chris Lovering Date: Sat, 28 Aug 2021 11:38:38 +0100 Subject: Only check URL-like objects against domain filters Previously a message such as 'https://google.com hello! flask.request.method' would be filtered due to us filtering the url shortener t.me. This commit changes to logic so that we only check parts of the messages that matched the URL regex against our blacklist, to avoid these false-positives. --- bot/exts/filters/filtering.py | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/bot/exts/filters/filtering.py b/bot/exts/filters/filtering.py index 10cc7885d..b7e91395e 100644 --- a/bot/exts/filters/filtering.py +++ b/bot/exts/filters/filtering.py @@ -478,15 +478,16 @@ class Filtering(Cog): Second return value is a reason of URL blacklisting (can be None). """ text = self.clean_input(text) - if not URL_RE.search(text): + matches = URL_RE.findall(text) + if not matches: return False, None - text = text.lower() domain_blacklist = self._get_filterlist_items("domain_name", allowed=False) for url in domain_blacklist: - if url.lower() in text: - return True, self._get_filterlist_value("domain_name", url, allowed=False)["comment"] + for match in matches: + if url.lower() in match.lower(): + return True, self._get_filterlist_value("domain_name", url, allowed=False)["comment"] return False, None -- cgit v1.2.3 From dc01b874db2f2909dd90f62847d995e7c94a17eb Mon Sep 17 00:00:00 2001 From: Chris Lovering Date: Mon, 30 Aug 2021 19:47:36 +0100 Subject: Add concurrency rules to all GitHub workflows This concurrency rule means that workflow actions will be cancelled if a new set of actions are triggered on the same ref. A ref is either a PR or a branch itself. Doing this means that if someone pushes to a PR multiple times in quick succession, workflows won't get queued up. Instead, only the workflows from the most recent changes will run, as this is all we really care about anyway. I see the benefits of this as twofold. 1. The author gets faster feedback about the most recent change pushed, rather than have to wait for all previous runs to completed 2. Other contributors don't need to wait for a queue of redundant workflows to finish. --- .github/workflows/build.yml | 4 ++++ .github/workflows/deploy.yml | 4 ++++ .github/workflows/lint-test.yml | 3 +++ .github/workflows/sentry_release.yml | 4 ++++ .github/workflows/status_embed.yaml | 4 ++++ 5 files changed, 19 insertions(+) diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index 84a671917..f8f2c8888 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -8,6 +8,10 @@ on: types: - completed +concurrency: + group: ${{ github.workflow }}-${{ github.ref }} + cancel-in-progress: true + jobs: build: if: github.event.workflow_run.conclusion == 'success' && github.event.workflow_run.event == 'push' diff --git a/.github/workflows/deploy.yml b/.github/workflows/deploy.yml index 8b809b777..0f030ebd7 100644 --- a/.github/workflows/deploy.yml +++ b/.github/workflows/deploy.yml @@ -8,6 +8,10 @@ on: types: - completed +concurrency: + group: ${{ github.workflow }}-${{ github.ref }} + cancel-in-progress: true + jobs: build: environment: production diff --git a/.github/workflows/lint-test.yml b/.github/workflows/lint-test.yml index e99e6d181..c4f87d522 100644 --- a/.github/workflows/lint-test.yml +++ b/.github/workflows/lint-test.yml @@ -6,6 +6,9 @@ on: - main pull_request: +concurrency: + group: ${{ github.workflow }}-${{ github.ref }} + cancel-in-progress: true jobs: lint-test: diff --git a/.github/workflows/sentry_release.yml b/.github/workflows/sentry_release.yml index f6a1e1f0e..48f5e50f4 100644 --- a/.github/workflows/sentry_release.yml +++ b/.github/workflows/sentry_release.yml @@ -5,6 +5,10 @@ on: branches: - main +concurrency: + group: ${{ github.workflow }}-${{ github.ref }} + cancel-in-progress: true + jobs: create_sentry_release: runs-on: ubuntu-latest diff --git a/.github/workflows/status_embed.yaml b/.github/workflows/status_embed.yaml index b6a71b887..4178c366d 100644 --- a/.github/workflows/status_embed.yaml +++ b/.github/workflows/status_embed.yaml @@ -9,6 +9,10 @@ on: types: - completed +concurrency: + group: ${{ github.workflow }}-${{ github.ref }} + cancel-in-progress: true + jobs: status_embed: # We need to send a status embed whenever the workflow -- cgit v1.2.3 From c2d1c974bb95d75b275260e869ec67d3e92d1ae1 Mon Sep 17 00:00:00 2001 From: Chris Lovering Date: Mon, 30 Aug 2021 21:23:54 +0100 Subject: Change all references to watch to nominate instead --- bot/exts/recruitment/talentpool/_cog.py | 42 ++++++++++++++++++++------------- 1 file changed, 25 insertions(+), 17 deletions(-) diff --git a/bot/exts/recruitment/talentpool/_cog.py b/bot/exts/recruitment/talentpool/_cog.py index 8db2d7eac..99567d55e 100644 --- a/bot/exts/recruitment/talentpool/_cog.py +++ b/bot/exts/recruitment/talentpool/_cog.py @@ -25,7 +25,7 @@ log = logging.getLogger(__name__) class TalentPool(Cog, name="Talentpool"): - """Relays messages of helper candidates to a watch channel to observe them.""" + """Used to nominate potential helper candidates.""" # RedisCache[str, bool] # Can contain a single key, "autoreview_enabled", with the value a bool indicating if autoreview is enabled. @@ -59,7 +59,7 @@ class TalentPool(Cog, name="Talentpool"): params=self.api_default_params ) except ResponseCodeError as err: - log.exception("Failed to fetch the watched users from the API", exc_info=err) + log.exception("Failed to fetch the currently nominated users from the API", exc_info=err) return False self.cache = defaultdict(dict) @@ -120,7 +120,11 @@ class TalentPool(Cog, name="Talentpool"): else: await ctx.send("Autoreview is currently disabled") - @nomination_group.command(name='watched', aliases=('all', 'list'), root_aliases=("nominees",)) + @nomination_group.command( + name="nominess", + aliases=("nominated", "all", "list", "watched"), + root_aliases=("nominees",) + ) @has_any_role(*MODERATION_ROLES) async def list_command( self, @@ -201,19 +205,23 @@ class TalentPool(Cog, name="Talentpool"): """ await ctx.invoke(self.list_command, oldest_first=True, update_cache=update_cache) - @nomination_group.command(name='forcewatch', aliases=('fw', 'forceadd', 'fa'), root_aliases=("forcenominate",)) + @nomination_group.command( + name="forcenominate", + aliases=("fw", "forceadd", "fa", "fn", "forcewatch"), + root_aliases=("forcenominate",) + ) @has_any_role(*MODERATION_ROLES) - async def force_watch_command(self, ctx: Context, user: MemberOrUser, *, reason: str = '') -> None: + async def force_nominate_command(self, ctx: Context, user: MemberOrUser, *, reason: str = '') -> None: """ Adds the given `user` to the talent pool, from any channel. A `reason` for adding the user to the talent pool is optional. """ - await self._watch_user(ctx, user, reason) + await self._nominate_user(ctx, user, reason) - @nomination_group.command(name='watch', aliases=('w', 'add', 'a'), root_aliases=("nominate",)) + @nomination_group.command(name='nominate', aliases=("w", "add", "a", "watch"), root_aliases=("nominate",)) @has_any_role(*STAFF_ROLES) - async def watch_command(self, ctx: Context, user: MemberOrUser, *, reason: str = '') -> None: + async def nominate_command(self, ctx: Context, user: MemberOrUser, *, reason: str = '') -> None: """ Adds the given `user` to the talent pool. @@ -224,18 +232,18 @@ class TalentPool(Cog, name="Talentpool"): if any(role.id in MODERATION_ROLES for role in ctx.author.roles): await ctx.send( f":x: Nominations should be run in the <#{Channels.nominations}> channel. " - "Use `!tp forcewatch` to override this check." + "Use `!tp forcenominate` to override this check." ) else: await ctx.send(f":x: Nominations must be run in the <#{Channels.nominations}> channel") return - await self._watch_user(ctx, user, reason) + await self._nominate_user(ctx, user, reason) - async def _watch_user(self, ctx: Context, user: MemberOrUser, reason: str) -> None: + async def _nominate_user(self, ctx: Context, user: MemberOrUser, reason: str) -> None: """Adds the given user to the talent pool.""" if user.bot: - await ctx.send(f":x: I'm sorry {ctx.author}, I'm afraid I can't do that. I only watch humans.") + await ctx.send(f":x: I'm sorry {ctx.author}, I'm afraid I can't do that. Only humans can be nominated.") return if isinstance(user, Member) and any(role.id in STAFF_ROLES for role in user.roles): @@ -325,7 +333,7 @@ class TalentPool(Cog, name="Talentpool"): @nomination_group.command(name='end', aliases=('unwatch',), root_aliases=("unnominate",)) @has_any_role(*MODERATION_ROLES) - async def unwatch_command(self, ctx: Context, user: MemberOrUser, *, reason: str) -> None: + async def end_nomination_command(self, ctx: Context, user: MemberOrUser, *, reason: str) -> None: """ Ends the active nomination of the specified user with the given reason. @@ -335,7 +343,7 @@ class TalentPool(Cog, name="Talentpool"): await ctx.send(f":x: Maximum allowed characters for the end reason is {REASON_MAX_CHARS}.") return - if await self.unwatch(user.id, reason): + if await self.end_nomination(user.id, reason): await ctx.send(f":white_check_mark: Messages sent by {user.mention} will no longer be relayed") else: await ctx.send(":x: The specified user does not have an active nomination") @@ -444,7 +452,7 @@ class TalentPool(Cog, name="Talentpool"): @Cog.listener() async def on_member_ban(self, guild: Guild, user: Union[MemberOrUser]) -> None: """Remove `user` from the talent pool after they are banned.""" - await self.unwatch(user.id, "User was banned.") + await self.end_nomination(user.id, "User was banned.") @Cog.listener() async def on_raw_reaction_add(self, payload: RawReactionActionEvent) -> None: @@ -466,7 +474,7 @@ class TalentPool(Cog, name="Talentpool"): log.info(f"Archiving nomination {message.id}") await self.reviewer.archive_vote(message, emoji == Emojis.incident_actioned) - async def unwatch(self, user_id: int, reason: str) -> bool: + async def end_nomination(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( 'bot/nominations', @@ -536,7 +544,7 @@ class TalentPool(Cog, name="Talentpool"): {entries_string} End date: {end_date} - Unwatch reason: {nomination_object["end_reason"]} + Unnominate reason: {nomination_object["end_reason"]} =============== """ ) -- cgit v1.2.3 From 20aa8b23a386ad2d570a1d771825331b07074bb1 Mon Sep 17 00:00:00 2001 From: Chris Lovering Date: Mon, 30 Aug 2021 21:27:54 +0100 Subject: Remove code that is no longer hit --- bot/exts/recruitment/talentpool/_cog.py | 4 ---- 1 file changed, 4 deletions(-) diff --git a/bot/exts/recruitment/talentpool/_cog.py b/bot/exts/recruitment/talentpool/_cog.py index 99567d55e..78f9b189b 100644 --- a/bot/exts/recruitment/talentpool/_cog.py +++ b/bot/exts/recruitment/talentpool/_cog.py @@ -47,10 +47,6 @@ class TalentPool(Cog, name="Talentpool"): """Return whether automatic posting of nomination reviews is enabled.""" return await self.talentpool_settings.get(AUTOREVIEW_ENABLED_KEY, True) - # Stores talentpool users in cache - self.cache = defaultdict(dict) - self.api_default_params = {'active': 'true', 'ordering': '-inserted_at'} - async def refresh_cache(self) -> bool: """Updates TalentPool users cache.""" try: -- cgit v1.2.3 From ffed769fbb1f450ac966564cc0ba75c2406e18ae Mon Sep 17 00:00:00 2001 From: Chris Lovering Date: Mon, 30 Aug 2021 21:39:20 +0100 Subject: Update comment to reference the new TalentPool cache name --- bot/exts/recruitment/talentpool/_review.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/bot/exts/recruitment/talentpool/_review.py b/bot/exts/recruitment/talentpool/_review.py index dfdcdcffe..1cc9c900c 100644 --- a/bot/exts/recruitment/talentpool/_review.py +++ b/bot/exts/recruitment/talentpool/_review.py @@ -103,10 +103,10 @@ class Reviewer: """Format a generic review of a user and return it with the reviewed emoji.""" log.trace(f"Formatting the review of {user_id}") - # Since `watched_users` is a defaultdict, we should take care + # Since `cache` is a defaultdict, we should take care # not to accidentally insert the IDs of users that have no - # active nominated by using the `watched_users.get(user_id)` - # instead of `watched_users[user_id]`. + # active nominated by using the `cache.get(user_id)` + # instead of `cache[user_id]`. 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}") -- cgit v1.2.3 From 6a35307ada47a1c3091b23fb3a33dea78fe63c15 Mon Sep 17 00:00:00 2001 From: Chris Lovering Date: Mon, 30 Aug 2021 21:44:48 +0100 Subject: Update api endpoint name in talent pool review --- bot/exts/recruitment/talentpool/_review.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/bot/exts/recruitment/talentpool/_review.py b/bot/exts/recruitment/talentpool/_review.py index 1cc9c900c..3ffbf93f3 100644 --- a/bot/exts/recruitment/talentpool/_review.py +++ b/bot/exts/recruitment/talentpool/_review.py @@ -97,7 +97,7 @@ class Reviewer: if update_database: nomination = self._pool.cache.get(user_id) - await self.bot.api_client.patch(f"{self._pool.api_endpoint}/{nomination['id']}", json={"reviewed": True}) + 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.""" -- cgit v1.2.3 From 892fafcd653b35c53ba48d7af42decd50b7fdda9 Mon Sep 17 00:00:00 2001 From: Chris Lovering Date: Mon, 30 Aug 2021 21:58:59 +0100 Subject: Update unnominate message to remove reference to relaying messages. --- bot/exts/recruitment/talentpool/_cog.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/bot/exts/recruitment/talentpool/_cog.py b/bot/exts/recruitment/talentpool/_cog.py index 78f9b189b..4a77de24c 100644 --- a/bot/exts/recruitment/talentpool/_cog.py +++ b/bot/exts/recruitment/talentpool/_cog.py @@ -340,7 +340,7 @@ class TalentPool(Cog, name="Talentpool"): return if await self.end_nomination(user.id, reason): - await ctx.send(f":white_check_mark: Messages sent by {user.mention} will no longer be relayed") + await ctx.send(f":white_check_mark: Successfully un-nominated {user}") else: await ctx.send(":x: The specified user does not have an active nomination") -- cgit v1.2.3 From bcb664e19c3e417c17f00562180393f601c43a5a Mon Sep 17 00:00:00 2001 From: Chris Lovering Date: Mon, 30 Aug 2021 22:04:09 +0100 Subject: Add back missing default params for TalentPool cog --- bot/exts/recruitment/talentpool/_cog.py | 1 + 1 file changed, 1 insertion(+) diff --git a/bot/exts/recruitment/talentpool/_cog.py b/bot/exts/recruitment/talentpool/_cog.py index 4a77de24c..38fefddd4 100644 --- a/bot/exts/recruitment/talentpool/_cog.py +++ b/bot/exts/recruitment/talentpool/_cog.py @@ -34,6 +34,7 @@ class TalentPool(Cog, name="Talentpool"): def __init__(self, bot: Bot) -> None: self.bot = bot self.reviewer = Reviewer(self.__class__.__name__, bot, self) + self.api_default_params = {'active': 'true', 'ordering': '-inserted_at'} self.bot.loop.create_task(self.schedule_autoreviews()) async def schedule_autoreviews(self) -> None: -- cgit v1.2.3 From ea6bd12f09c7978db24184cf09b91e1d643cffba Mon Sep 17 00:00:00 2001 From: Chris Lovering Date: Mon, 30 Aug 2021 22:04:46 +0100 Subject: use global logger in talent pool, over a self var that has been deleted --- bot/exts/recruitment/talentpool/_cog.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/bot/exts/recruitment/talentpool/_cog.py b/bot/exts/recruitment/talentpool/_cog.py index 38fefddd4..f932fc003 100644 --- a/bot/exts/recruitment/talentpool/_cog.py +++ b/bot/exts/recruitment/talentpool/_cog.py @@ -42,7 +42,7 @@ class TalentPool(Cog, name="Talentpool"): if await self.autoreview_enabled(): await self.reviewer.reschedule_reviews() else: - self.log.trace("Not scheduling reviews as autoreview is disabled.") + log.trace("Not scheduling reviews as autoreview is disabled.") async def autoreview_enabled(self) -> bool: """Return whether automatic posting of nomination reviews is enabled.""" -- cgit v1.2.3 From 5ca1fec1e9a2bb767863701bee1ffcad729331ec Mon Sep 17 00:00:00 2001 From: Chris Lovering Date: Mon, 30 Aug 2021 22:46:26 +0100 Subject: Rename list nominated user command for clarity --- bot/exts/recruitment/talentpool/_cog.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/bot/exts/recruitment/talentpool/_cog.py b/bot/exts/recruitment/talentpool/_cog.py index f932fc003..6dc36058b 100644 --- a/bot/exts/recruitment/talentpool/_cog.py +++ b/bot/exts/recruitment/talentpool/_cog.py @@ -137,9 +137,9 @@ class TalentPool(Cog, name="Talentpool"): The optional kwarg `update_cache` can be used to update the user cache using the API before listing the users. """ - await self.list_users(ctx, oldest_first=oldest_first, update_cache=update_cache) + await self.list_nominated_users(ctx, oldest_first=oldest_first, update_cache=update_cache) - async def list_users( + async def list_nominated_users( self, ctx: Context, oldest_first: bool = False, -- cgit v1.2.3 From 9ef8306c2bc8ab0285da62ba2c7c42e003f015e2 Mon Sep 17 00:00:00 2001 From: Chris Lovering Date: Mon, 30 Aug 2021 22:51:47 +0100 Subject: Fix spelling of a TalentPool command name --- bot/exts/recruitment/talentpool/_cog.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/bot/exts/recruitment/talentpool/_cog.py b/bot/exts/recruitment/talentpool/_cog.py index 6dc36058b..c0b3f8348 100644 --- a/bot/exts/recruitment/talentpool/_cog.py +++ b/bot/exts/recruitment/talentpool/_cog.py @@ -118,7 +118,7 @@ class TalentPool(Cog, name="Talentpool"): await ctx.send("Autoreview is currently disabled") @nomination_group.command( - name="nominess", + name="nominees", aliases=("nominated", "all", "list", "watched"), root_aliases=("nominees",) ) -- cgit v1.2.3 From 990bc67c7d02027d1eb3fc30654585bf77bc0d17 Mon Sep 17 00:00:00 2001 From: Chris Lovering Date: Wed, 1 Sep 2021 10:23:05 +0100 Subject: Add an extra alias to unnominate --- bot/exts/recruitment/talentpool/_cog.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/bot/exts/recruitment/talentpool/_cog.py b/bot/exts/recruitment/talentpool/_cog.py index c0b3f8348..477fff2fd 100644 --- a/bot/exts/recruitment/talentpool/_cog.py +++ b/bot/exts/recruitment/talentpool/_cog.py @@ -328,7 +328,7 @@ class TalentPool(Cog, name="Talentpool"): max_size=1000 ) - @nomination_group.command(name='end', aliases=('unwatch',), root_aliases=("unnominate",)) + @nomination_group.command(name="end", aliases=("unwatch", "unnominate"), root_aliases=("unnominate",)) @has_any_role(*MODERATION_ROLES) async def end_nomination_command(self, ctx: Context, user: MemberOrUser, *, reason: str) -> None: """ -- cgit v1.2.3 From 97017f00ecdb70359ff8cf6f4a8fe970c6aa7ad5 Mon Sep 17 00:00:00 2001 From: Xithrius Date: Fri, 3 Sep 2021 00:50:50 -0700 Subject: `Unnominate reason` to `Unnomination reason` --- bot/exts/recruitment/talentpool/_cog.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/bot/exts/recruitment/talentpool/_cog.py b/bot/exts/recruitment/talentpool/_cog.py index 477fff2fd..c2257c84b 100644 --- a/bot/exts/recruitment/talentpool/_cog.py +++ b/bot/exts/recruitment/talentpool/_cog.py @@ -541,7 +541,7 @@ class TalentPool(Cog, name="Talentpool"): {entries_string} End date: {end_date} - Unnominate reason: {nomination_object["end_reason"]} + Unnomination reason: {nomination_object["end_reason"]} =============== """ ) -- cgit v1.2.3 From a06367e1691d8ef39e2cf8f8e0d15e5922d29d01 Mon Sep 17 00:00:00 2001 From: Chris Lovering Date: Fri, 3 Sep 2021 09:42:44 +0100 Subject: Pop user from talent pool cache when unnominated --- bot/exts/recruitment/talentpool/_cog.py | 1 + 1 file changed, 1 insertion(+) diff --git a/bot/exts/recruitment/talentpool/_cog.py b/bot/exts/recruitment/talentpool/_cog.py index c2257c84b..a317c6645 100644 --- a/bot/exts/recruitment/talentpool/_cog.py +++ b/bot/exts/recruitment/talentpool/_cog.py @@ -493,6 +493,7 @@ class TalentPool(Cog, name="Talentpool"): json={'end_reason': reason, 'active': False} ) + self.cache.pop(user_id) if await self.autoreview_enabled(): self.reviewer.cancel(user_id) -- cgit v1.2.3 From da697eef39bf6a786d8293312d41b9bd634c542d Mon Sep 17 00:00:00 2001 From: Joe Banks Date: Sat, 4 Sep 2021 09:41:47 +0100 Subject: Update in accordance with python-discord/kubernetes#95 --- .github/workflows/deploy.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/deploy.yml b/.github/workflows/deploy.yml index 8b809b777..f8a8292a4 100644 --- a/.github/workflows/deploy.yml +++ b/.github/workflows/deploy.yml @@ -38,6 +38,6 @@ jobs: uses: Azure/k8s-deploy@v1 with: manifests: | - bot/deployment.yaml + namespaces/default/bot/deployment.yaml images: 'ghcr.io/python-discord/bot:${{ steps.sha_tag.outputs.tag }}' kubectl-version: 'latest' -- cgit v1.2.3 From 66f1ca3ca60b4db46b58dcc8d30a4f45e1f8d122 Mon Sep 17 00:00:00 2001 From: Chris Lovering Date: Sat, 4 Sep 2021 14:49:30 +0100 Subject: Pin platform in Dockerfile Some of our deps don't have wheels for atm processors. With Mac's M1 chips becomming more common, we should make it easier for those users to build our environments. --- Dockerfile | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Dockerfile b/Dockerfile index 4d8592590..30bf8a361 100644 --- a/Dockerfile +++ b/Dockerfile @@ -1,4 +1,4 @@ -FROM python:3.9-slim +FROM --platform=linux/amd64 python:3.9-slim # Set pip to have no saved cache ENV PIP_NO_CACHE_DIR=false \ -- cgit v1.2.3 From b4c7d61361f7ba567e6638d3721f7b3bbcf334d9 Mon Sep 17 00:00:00 2001 From: ChrisJL Date: Sat, 4 Sep 2021 23:14:15 +0100 Subject: Server command now uses correct TalentPool attr (#1810) This was attr changed when we removed the concept of a user being 'watched' while removing the talentpool. --- bot/exts/info/information.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/bot/exts/info/information.py b/bot/exts/info/information.py index bcf8c10d2..51d47b75c 100644 --- a/bot/exts/info/information.py +++ b/bot/exts/info/information.py @@ -72,7 +72,7 @@ class Information(Cog): """Return additional server info only visible in moderation channels.""" talentpool_info = "" if cog := self.bot.get_cog("Talentpool"): - talentpool_info = f"Nominated: {len(cog.watched_users)}\n" + talentpool_info = f"Nominated: {len(cog.cache)}\n" bb_info = "" if cog := self.bot.get_cog("Big Brother"): -- cgit v1.2.3 From 6bb0dba2230e9b3fcab2caaf559b749543c26abb Mon Sep 17 00:00:00 2001 From: Chris Lovering Date: Sun, 5 Sep 2021 09:34:54 +0100 Subject: Declare and refresh TalentPool.cache on init of cog This avoids issues in the server cog trying to access it before it's assigned and refreshed. I also migrated to the tasks to `scheduling.create_task()` as the created tasks currently don't have any error handling they can hide errors in development until the task object is destroyed (if that occurs at all) which logs the exception. The scheduling alternative attaches a callback which logs exceptions to prevent this. --- bot/exts/info/information.py | 3 ++- bot/exts/recruitment/talentpool/_cog.py | 9 ++++++--- bot/exts/recruitment/talentpool/_review.py | 2 -- 3 files changed, 8 insertions(+), 6 deletions(-) diff --git a/bot/exts/info/information.py b/bot/exts/info/information.py index 51d47b75c..d44886969 100644 --- a/bot/exts/info/information.py +++ b/bot/exts/info/information.py @@ -72,7 +72,8 @@ class Information(Cog): """Return additional server info only visible in moderation channels.""" talentpool_info = "" if cog := self.bot.get_cog("Talentpool"): - talentpool_info = f"Nominated: {len(cog.cache)}\n" + num_nominated = len(cog.cache) if cog.cache else 0 + talentpool_info = f"Nominated: {num_nominated}\n" bb_info = "" if cog := self.bot.get_cog("Big Brother"): diff --git a/bot/exts/recruitment/talentpool/_cog.py b/bot/exts/recruitment/talentpool/_cog.py index a317c6645..bea5ff72c 100644 --- a/bot/exts/recruitment/talentpool/_cog.py +++ b/bot/exts/recruitment/talentpool/_cog.py @@ -2,7 +2,7 @@ import logging import textwrap from collections import ChainMap, defaultdict from io import StringIO -from typing import Union +from typing import Optional, Union import discord from async_rediscache import RedisCache @@ -15,7 +15,7 @@ from bot.constants import Channels, Emojis, Guild, MODERATION_ROLES, Roles, STAF from bot.converters import MemberOrUser from bot.exts.recruitment.talentpool._review import Reviewer from bot.pagination import LinePaginator -from bot.utils import time +from bot.utils import scheduling, time from bot.utils.time import get_time_delta AUTOREVIEW_ENABLED_KEY = "autoreview_enabled" @@ -34,8 +34,11 @@ class TalentPool(Cog, name="Talentpool"): def __init__(self, bot: Bot) -> None: self.bot = bot self.reviewer = Reviewer(self.__class__.__name__, bot, self) + self.cache: Optional[defaultdict[dict]] = None self.api_default_params = {'active': 'true', 'ordering': '-inserted_at'} - self.bot.loop.create_task(self.schedule_autoreviews()) + + scheduling.create_task(self.refresh_cache(), event_loop=self.bot.loop) + scheduling.create_task(self.schedule_autoreviews(), event_loop=self.bot.loop) async def schedule_autoreviews(self) -> None: """Reschedule reviews for active nominations if autoreview is enabled.""" diff --git a/bot/exts/recruitment/talentpool/_review.py b/bot/exts/recruitment/talentpool/_review.py index 3ffbf93f3..f4aa73e75 100644 --- a/bot/exts/recruitment/talentpool/_review.py +++ b/bot/exts/recruitment/talentpool/_review.py @@ -57,8 +57,6 @@ class Reviewer: """Reschedule all active nominations to be reviewed at the appropriate time.""" log.trace("Rescheduling reviews") await self.bot.wait_until_guild_available() - # TODO Once the watch channel is removed, this can be done in a smarter way, e.g create a sync function. - await self._pool.refresh_cache() for user_id, user_data in self._pool.cache.items(): if not user_data["reviewed"]: -- cgit v1.2.3 From fddd34158b3fda284bd39970297c00e7d54d122a Mon Sep 17 00:00:00 2001 From: Chris Lovering Date: Mon, 6 Sep 2021 13:36:42 +0100 Subject: Refactor & simplifiy domain filter check --- bot/exts/filters/filtering.py | 11 +++-------- 1 file changed, 3 insertions(+), 8 deletions(-) diff --git a/bot/exts/filters/filtering.py b/bot/exts/filters/filtering.py index b7e91395e..7e698880f 100644 --- a/bot/exts/filters/filtering.py +++ b/bot/exts/filters/filtering.py @@ -478,17 +478,12 @@ class Filtering(Cog): Second return value is a reason of URL blacklisting (can be None). """ text = self.clean_input(text) - matches = URL_RE.findall(text) - if not matches: - return False, None domain_blacklist = self._get_filterlist_items("domain_name", allowed=False) - - for url in domain_blacklist: - for match in matches: - if url.lower() in match.lower(): + for match in URL_RE.finditer(text): + for url in domain_blacklist: + if url.lower() in match.group(1).lower(): return True, self._get_filterlist_value("domain_name", url, allowed=False)["comment"] - return False, None @staticmethod -- cgit v1.2.3 From cace28f844fe6ff022dfff7e2409d34fbc3d8bf4 Mon Sep 17 00:00:00 2001 From: Chris Lovering Date: Mon, 6 Sep 2021 14:18:17 +0100 Subject: Add .gg/ to invite filter regex --- bot/utils/regex.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/bot/utils/regex.py b/bot/utils/regex.py index a8efe1446..7bad1e627 100644 --- a/bot/utils/regex.py +++ b/bot/utils/regex.py @@ -6,7 +6,8 @@ INVITE_RE = re.compile( r"discordapp(?:[\.,]|dot)com(?:\/|slash)invite|" # or discordapp.com/invite/ r"discord(?:[\.,]|dot)me|" # or discord.me r"discord(?:[\.,]|dot)li|" # or discord.li - r"discord(?:[\.,]|dot)io" # or discord.io. + r"discord(?:[\.,]|dot)io|" # or discord.io. + r"(?:[\.,]|dot)gg" # or .gg/ r")(?:[\/]|slash)" # / or 'slash' r"([a-zA-Z0-9\-]+)", # the invite code itself flags=re.IGNORECASE -- cgit v1.2.3 From 160f0ecb125fe317abc644100e58ab9557ea7faa Mon Sep 17 00:00:00 2001 From: TizzySaurus <47674925+TizzySaurus@users.noreply.github.com> Date: Wed, 8 Sep 2021 09:48:22 +0100 Subject: Add support for `!infraction last` (#1804) * Add support for `!infraction last` `!infraction last` will view details of the last infraction the user made. Also changes the `Infraction` converter to use the `expanded` endpoint 1) Added `InvalidInfraction` to `bot.errors` - called when value passed to `Infraction` converter is invalid 2) Improved error messages for when an invalid infraction is passed to `!infraction {infr_id}` 3) Improved some other messages/docstrings etc. Co-authored-by: Xithrius <15021300+Xithrius@users.noreply.github.com> --- bot/converters.py | 14 +++++++++-- bot/errors.py | 19 ++++++++++++++- bot/exts/moderation/infraction/management.py | 36 +++++++++++++++------------- 3 files changed, 50 insertions(+), 19 deletions(-) diff --git a/bot/converters.py b/bot/converters.py index bd4044c7e..18bb6e4e5 100644 --- a/bot/converters.py +++ b/bot/converters.py @@ -17,6 +17,7 @@ from discord.utils import DISCORD_EPOCH, escape_markdown, snowflake_time from bot import exts from bot.api import ResponseCodeError from bot.constants import URLs +from bot.errors import InvalidInfraction from bot.exts.info.doc import _inventory_parser from bot.utils.extensions import EXTENSIONS, unqualify from bot.utils.regex import INVITE_RE @@ -558,7 +559,7 @@ class Infraction(Converter): "ordering": "-inserted_at" } - infractions = await ctx.bot.api_client.get("bot/infractions", params=params) + infractions = await ctx.bot.api_client.get("bot/infractions/expanded", params=params) if not infractions: raise BadArgument( @@ -568,7 +569,16 @@ class Infraction(Converter): return infractions[0] else: - return await ctx.bot.api_client.get(f"bot/infractions/{arg}") + try: + return await ctx.bot.api_client.get(f"bot/infractions/{arg}/expanded") + except ResponseCodeError as e: + if e.status == 404: + raise InvalidInfraction( + converter=Infraction, + original=e, + infraction_arg=arg + ) + raise e if t.TYPE_CHECKING: diff --git a/bot/errors.py b/bot/errors.py index 2633390a8..078b645f1 100644 --- a/bot/errors.py +++ b/bot/errors.py @@ -1,6 +1,9 @@ from __future__ import annotations -from typing import Hashable, TYPE_CHECKING +from typing import Hashable, TYPE_CHECKING, Union + +from discord.ext.commands import ConversionError, Converter + if TYPE_CHECKING: from bot.converters import MemberOrUser @@ -40,6 +43,20 @@ class InvalidInfractedUserError(Exception): super().__init__(reason) +class InvalidInfraction(ConversionError): + """ + Raised by the Infraction converter when trying to fetch an invalid infraction id. + + Attributes: + `infraction_arg` -- the value that we attempted to convert into an Infraction + """ + + def __init__(self, converter: Converter, original: Exception, infraction_arg: Union[int, str]): + + self.infraction_arg = infraction_arg + super().__init__(converter, original) + + class BrandingMisconfiguration(RuntimeError): """Raised by the Branding cog when a misconfigured event is encountered.""" diff --git a/bot/exts/moderation/infraction/management.py b/bot/exts/moderation/infraction/management.py index 223a124d8..d72cf8f89 100644 --- a/bot/exts/moderation/infraction/management.py +++ b/bot/exts/moderation/infraction/management.py @@ -11,9 +11,9 @@ from discord.ext.commands import Context from discord.utils import escape_markdown from bot import constants -from bot.api import ResponseCodeError from bot.bot import Bot from bot.converters import Expiry, Infraction, MemberOrUser, Snowflake, UnambiguousUser, allowed_strings +from bot.errors import InvalidInfraction from bot.exts.moderation.infraction.infractions import Infractions from bot.exts.moderation.modlog import ModLog from bot.pagination import LinePaginator @@ -45,25 +45,22 @@ class ModManagement(commands.Cog): # region: Edit infraction commands @commands.group(name='infraction', aliases=('infr', 'infractions', 'inf', 'i'), invoke_without_command=True) - async def infraction_group(self, ctx: Context, infr_id: int = None) -> None: - """Infraction manipulation commands. If `infr_id` is passed then this command fetches that infraction.""" - if infr_id is None: + async def infraction_group(self, ctx: Context, infraction: Infraction = None) -> None: + """ + Infraction manipulation commands. + + If `infraction` is passed then this command fetches that infraction. The `Infraction` converter + supports 'l', 'last' and 'recent' to get the most recent infraction made by `ctx.author`. + """ + if infraction is None: await ctx.send_help(ctx.command) return - try: - infraction_list = [await self.bot.api_client.get(f"bot/infractions/{infr_id}/expanded")] - except ResponseCodeError as e: - if e.status == 404: - await ctx.send(f":x: No infraction with ID `{infr_id}` could be found.") - return - raise e - embed = discord.Embed( - title=f"Infraction #{infr_id}", + title=f"Infraction #{infraction['id']}", colour=discord.Colour.orange() ) - await self.send_infraction_list(ctx, embed, infraction_list) + await self.send_infraction_list(ctx, embed, [infraction]) @infraction_group.command(name="append", aliases=("amend", "add", "a")) async def infraction_append( @@ -348,13 +345,20 @@ class ModManagement(commands.Cog): return all(checks) # This cannot be static (must have a __func__ attribute). - async def cog_command_error(self, ctx: Context, error: Exception) -> None: - """Send a notification to the invoking context on a Union failure.""" + async def cog_command_error(self, ctx: Context, error: commands.CommandError) -> None: + """Handles errors for commands within this cog.""" if isinstance(error, commands.BadUnionArgument): if discord.User in error.converters: await ctx.send(str(error.errors[0])) error.handled = True + elif isinstance(error, InvalidInfraction): + if error.infraction_arg.isdigit(): + await ctx.send(f":x: Could not find an infraction with id `{error.infraction_arg}`.") + else: + await ctx.send(f":x: `{error.infraction_arg}` is not a valid integer infraction id.") + error.handled = True + def setup(bot: Bot) -> None: """Load the ModManagement cog.""" -- cgit v1.2.3 From b412451d38a419c2b66815729cbc724e6fea0ab6 Mon Sep 17 00:00:00 2001 From: Chris Lovering Date: Mon, 6 Sep 2021 13:08:04 +0100 Subject: Wait until login before trying to use the bot api client --- bot/exts/recruitment/talentpool/_cog.py | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/bot/exts/recruitment/talentpool/_cog.py b/bot/exts/recruitment/talentpool/_cog.py index bea5ff72c..01a2f20e2 100644 --- a/bot/exts/recruitment/talentpool/_cog.py +++ b/bot/exts/recruitment/talentpool/_cog.py @@ -37,12 +37,14 @@ class TalentPool(Cog, name="Talentpool"): self.cache: Optional[defaultdict[dict]] = None self.api_default_params = {'active': 'true', 'ordering': '-inserted_at'} - scheduling.create_task(self.refresh_cache(), event_loop=self.bot.loop) + self.initial_refresh_task = scheduling.create_task(self.refresh_cache(), event_loop=self.bot.loop) scheduling.create_task(self.schedule_autoreviews(), event_loop=self.bot.loop) async def schedule_autoreviews(self) -> None: """Reschedule reviews for active nominations if autoreview is enabled.""" if await self.autoreview_enabled(): + # Wait for a populated cache first + await self.initial_refresh_task await self.reviewer.reschedule_reviews() else: log.trace("Not scheduling reviews as autoreview is disabled.") @@ -53,6 +55,8 @@ class TalentPool(Cog, name="Talentpool"): async def refresh_cache(self) -> bool: """Updates TalentPool users cache.""" + # Wait until logged in to ensure bot api client exists + await self.bot.wait_until_guild_available() try: data = await self.bot.api_client.get( 'bot/nominations', -- cgit v1.2.3 From 2a2b0cd34807b9a0d5292129c81d08b64ce476be Mon Sep 17 00:00:00 2001 From: Chris Lovering Date: Tue, 7 Sep 2021 12:30:46 +0100 Subject: Remove previous nominations output This raised questions from helpers when they saw that someone had many previous nominations. There is no reason why a helper needs to see this information. --- bot/exts/recruitment/talentpool/_cog.py | 11 ----------- 1 file changed, 11 deletions(-) diff --git a/bot/exts/recruitment/talentpool/_cog.py b/bot/exts/recruitment/talentpool/_cog.py index 01a2f20e2..aaafff973 100644 --- a/bot/exts/recruitment/talentpool/_cog.py +++ b/bot/exts/recruitment/talentpool/_cog.py @@ -291,18 +291,7 @@ class TalentPool(Cog, name="Talentpool"): if await self.autoreview_enabled() and user.id not in self.reviewer: self.reviewer.schedule_review(user.id) - history = await self.bot.api_client.get( - 'bot/nominations', - params={ - "user__id": str(user.id), - "active": "false", - "ordering": "-inserted_at" - } - ) - msg = f"✅ The nomination for {user.mention} has been added to the talent pool" - if history: - msg += f"\n\n({len(history)} previous nominations in total)" await ctx.send(msg) -- cgit v1.2.3 From f699c56d8321004adee51df00b5105a4d8b1d3b7 Mon Sep 17 00:00:00 2001 From: Chris Lovering Date: Wed, 8 Sep 2021 15:20:54 +0100 Subject: Use - rather than 0 for number of nominees when cache isn't ready --- bot/exts/info/information.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/bot/exts/info/information.py b/bot/exts/info/information.py index d44886969..be67910a6 100644 --- a/bot/exts/info/information.py +++ b/bot/exts/info/information.py @@ -72,7 +72,7 @@ class Information(Cog): """Return additional server info only visible in moderation channels.""" talentpool_info = "" if cog := self.bot.get_cog("Talentpool"): - num_nominated = len(cog.cache) if cog.cache else 0 + num_nominated = len(cog.cache) if cog.cache else "-" talentpool_info = f"Nominated: {num_nominated}\n" bb_info = "" -- cgit v1.2.3 From 108fb5d3dfc76c78d51126660ce891f9d1ee10eb Mon Sep 17 00:00:00 2001 From: Janine vN Date: Thu, 9 Sep 2021 10:31:48 -0400 Subject: Add string-formatting tag Adds a tag to show the string formatting mini language --- bot/resources/tags/string-formatting.md | 27 +++++++++++++++++++++++++++ 1 file changed, 27 insertions(+) create mode 100644 bot/resources/tags/string-formatting.md diff --git a/bot/resources/tags/string-formatting.md b/bot/resources/tags/string-formatting.md new file mode 100644 index 000000000..fba5577b8 --- /dev/null +++ b/bot/resources/tags/string-formatting.md @@ -0,0 +1,27 @@ +**String Formatting Mini Language** +The String Formatting Language in Python is a powerful way to tailor the display of strings and other data structures. This string formatting mini language works for f-strings and .format(). + +Take a look at some of these examples! +```py +my_num = 2134234523 +print(f"{my_num:,}") + +my_smaller_num = -30.0532234 +print(f"{my_smaller_num:=09.2f}") + +my_str = "Center me!" +print(f"{my_str:^20}") + +repr_str = "Spam \t Ham" +print(f"{repr_str!r}") +``` +Results: +``` +2,134,234,523 +-00030.05 + Center me! +'Spam \t Ham' +``` +**Full Specification & Resources** +[String Formatting Mini Language Specification](https://docs.python.org/3/library/string.html#format-specification-mini-language) +[pyformat.info](https://pyformat.info/) \ No newline at end of file -- cgit v1.2.3 From 693a91ea12b6f856da6c3a2ad6e17bcf12779d53 Mon Sep 17 00:00:00 2001 From: Janine vN Date: Thu, 9 Sep 2021 10:52:14 -0400 Subject: Add required newline to end of file --- bot/resources/tags/string-formatting.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/bot/resources/tags/string-formatting.md b/bot/resources/tags/string-formatting.md index fba5577b8..272b96af3 100644 --- a/bot/resources/tags/string-formatting.md +++ b/bot/resources/tags/string-formatting.md @@ -24,4 +24,4 @@ Results: ``` **Full Specification & Resources** [String Formatting Mini Language Specification](https://docs.python.org/3/library/string.html#format-specification-mini-language) -[pyformat.info](https://pyformat.info/) \ No newline at end of file +[pyformat.info](https://pyformat.info/) -- cgit v1.2.3 From 3d6ba16691512292c239e76c0a9ae373b3982bc2 Mon Sep 17 00:00:00 2001 From: Janine vN Date: Thu, 9 Sep 2021 12:28:13 -0400 Subject: Change formatting of examples After some discussion back and forth, I've adjusted how to display the examples and code bock to be of a more REPL-style. Additionally, a filler character for the "Center Me!" string is added to illustrate how exactly in centers it. This commit also adds some small styling changes. --- bot/resources/tags/string-formatting.md | 29 +++++++++++++---------------- 1 file changed, 13 insertions(+), 16 deletions(-) diff --git a/bot/resources/tags/string-formatting.md b/bot/resources/tags/string-formatting.md index 272b96af3..707d19c90 100644 --- a/bot/resources/tags/string-formatting.md +++ b/bot/resources/tags/string-formatting.md @@ -1,25 +1,22 @@ -**String Formatting Mini Language** -The String Formatting Language in Python is a powerful way to tailor the display of strings and other data structures. This string formatting mini language works for f-strings and .format(). +**String Formatting Mini-Language** +The String Formatting Language in Python is a powerful way to tailor the display of strings and other data structures. This string formatting mini language works for f-strings and `.format()`. Take a look at some of these examples! ```py -my_num = 2134234523 -print(f"{my_num:,}") +>>> my_num = 2134234523 +>>> print(f"{my_num:,}") +2,134,234,523 -my_smaller_num = -30.0532234 -print(f"{my_smaller_num:=09.2f}") +>>> my_smaller_num = -30.0532234 +>>> print(f"{my_smaller_num:=09.2f}") +-00030.05 -my_str = "Center me!" -print(f"{my_str:^20}") +>>> my_str = "Center me!" +>>> print(f"{my_str:-^20}") +-----Center me!----- -repr_str = "Spam \t Ham" -print(f"{repr_str!r}") -``` -Results: -``` -2,134,234,523 --00030.05 - Center me! +>>> repr_str = "Spam \t Ham" +>>> print(f"{repr_str!r}") 'Spam \t Ham' ``` **Full Specification & Resources** -- cgit v1.2.3 From 71ce990ba28dca3edf713ac28eff81065155f793 Mon Sep 17 00:00:00 2001 From: Hassan Abouelela Date: Sat, 11 Sep 2021 23:39:41 +0400 Subject: Adds Core Dev Voting To Changelog Blacklist Adds the channel used for voting on contributors to the message changelog blacklist. Signed-off-by: Hassan Abouelela --- config-default.yml | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/config-default.yml b/config-default.yml index a18fdafa5..3405934e0 100644 --- a/config-default.yml +++ b/config-default.yml @@ -157,9 +157,10 @@ guild: reddit: &REDDIT_CHANNEL 458224812528238616 # Development - dev_contrib: &DEV_CONTRIB 635950537262759947 - dev_core: &DEV_CORE 411200599653351425 - dev_log: &DEV_LOG 622895325144940554 + dev_contrib: &DEV_CONTRIB 635950537262759947 + dev_core: &DEV_CORE 411200599653351425 + dev_voting: &DEV_CORE_VOTING 839162966519447552 + dev_log: &DEV_LOG 622895325144940554 # Discussion meta: 429409067623251969 @@ -251,6 +252,7 @@ guild: - *MESSAGE_LOG - *MOD_LOG - *STAFF_VOICE + - *DEV_CORE_VOTING reminder_whitelist: - *BOT_CMD -- cgit v1.2.3 From 3c1cc75371fa8be3e1de3affa7b5a1f54b55da6b Mon Sep 17 00:00:00 2001 From: Chris Lovering Date: Fri, 17 Sep 2021 13:34:15 +0100 Subject: Add metricity to docker-compose By adding metricity to the compose, we allow it to migrate itself, rather than needing the site to do it. Defaulting 'USE_METRICITY' to false means that it will run migrations, but not actually start the bot. This means we don't add another service that needs to run all the time, which could impact some contribs on lower powered hardware. --- .gitignore | 1 + docker-compose.yml | 22 +++++++++++++++++++++- 2 files changed, 22 insertions(+), 1 deletion(-) diff --git a/.gitignore b/.gitignore index f74a142f3..177345908 100644 --- a/.gitignore +++ b/.gitignore @@ -116,6 +116,7 @@ log.* # Custom user configuration config.yml docker-compose.override.yml +metricity-config.toml # xmlrunner unittest XML reports TEST-**.xml diff --git a/docker-compose.yml b/docker-compose.yml index 0f0355dac..b3ca6baa4 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -23,6 +23,11 @@ services: POSTGRES_DB: pysite POSTGRES_PASSWORD: pysite POSTGRES_USER: pysite + healthcheck: + test: ["CMD-SHELL", "pg_isready -U pysite"] + interval: 2s + timeout: 1s + retries: 5 redis: << : *logging @@ -31,6 +36,21 @@ services: ports: - "127.0.0.1:6379:6379" + metricity: + << : *logging + restart: on-failure # USE_METRICITY=false will stop the container, so this ensures it only restarts on error + depends_on: + postgres: + condition: service_healthy + image: ghcr.io/python-discord/metricity:latest + env_file: + - .env + environment: + DATABASE_URI: postgres://pysite:pysite@postgres/metricity + USE_METRICITY: ${USE_METRICITY-false} + volumes: + - .:/tmp/bot:ro + snekbox: << : *logging << : *restart_policy @@ -56,7 +76,7 @@ services: - "127.0.0.1:8000:8000" tty: true depends_on: - - postgres + - metricity environment: DATABASE_URL: postgres://pysite:pysite@postgres:5432/pysite METRICITY_DB_URL: postgres://pysite:pysite@postgres:5432/metricity -- cgit v1.2.3 From fe248cc05a3c50beb064c43aef224c0204512df9 Mon Sep 17 00:00:00 2001 From: Chris Lovering Date: Fri, 17 Sep 2021 13:35:38 +0100 Subject: Remove duplicate roles when defining allowed_mentions --- bot/bot.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/bot/bot.py b/bot/bot.py index 914da9c98..db3d651a3 100644 --- a/bot/bot.py +++ b/bot/bot.py @@ -109,7 +109,7 @@ class Bot(commands.Bot): def create(cls) -> "Bot": """Create and return an instance of a Bot.""" loop = asyncio.get_event_loop() - allowed_roles = [discord.Object(id_) for id_ in constants.MODERATION_ROLES] + allowed_roles = list({discord.Object(id_) for id_ in constants.MODERATION_ROLES}) intents = discord.Intents.all() intents.presences = False -- cgit v1.2.3 From 802fa0dd29f35d4cf02c7be88dcec01d9ab67729 Mon Sep 17 00:00:00 2001 From: Izan Date: Mon, 20 Sep 2021 18:55:49 +0100 Subject: Remove coveralls from lint-test --- .github/workflows/lint-test.yml | 7 ------- 1 file changed, 7 deletions(-) diff --git a/.github/workflows/lint-test.yml b/.github/workflows/lint-test.yml index 619544e1a..2f42f1895 100644 --- a/.github/workflows/lint-test.yml +++ b/.github/workflows/lint-test.yml @@ -121,13 +121,6 @@ jobs: - name: Run tests and generate coverage report run: pytest -n auto --cov --disable-warnings -q - # This step will publish the coverage reports coveralls.io and - # print a "job" link in the output of the GitHub Action - - name: Publish coverage report to coveralls.io - env: - GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} - run: coveralls - # Prepare the Pull Request Payload artifact. If this fails, we # we fail silently using the `continue-on-error` option. It's # nice if this succeeds, but if it fails for any reason, it -- cgit v1.2.3 From eff7d7f2cf3f2cd91ed6654b97edd95db786fd47 Mon Sep 17 00:00:00 2001 From: Boris Muratov <8bee278@gmail.com> Date: Tue, 21 Sep 2021 23:39:52 +0300 Subject: Add `cog` as an alias to extensions command --- bot/exts/utils/extensions.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/bot/exts/utils/extensions.py b/bot/exts/utils/extensions.py index f78664527..309126d0e 100644 --- a/bot/exts/utils/extensions.py +++ b/bot/exts/utils/extensions.py @@ -36,7 +36,7 @@ class Extensions(commands.Cog): def __init__(self, bot: Bot): self.bot = bot - @group(name="extensions", aliases=("ext", "exts", "c", "cogs"), invoke_without_command=True) + @group(name="extensions", aliases=("ext", "exts", "c", "cog", "cogs"), invoke_without_command=True) async def extensions_group(self, ctx: Context) -> None: """Load, unload, reload, and list loaded extensions.""" await ctx.send_help(ctx.command) -- cgit v1.2.3 From 18b5fd814377ba059a5ca2f8f17475a59d87fd8f Mon Sep 17 00:00:00 2001 From: Izan Date: Thu, 23 Sep 2021 10:29:04 +0100 Subject: Use full paste link --- bot/resources/tags/paste.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/bot/resources/tags/paste.md b/bot/resources/tags/paste.md index 2ed51def7..8c3c2985d 100644 --- a/bot/resources/tags/paste.md +++ b/bot/resources/tags/paste.md @@ -1,6 +1,6 @@ **Pasting large amounts of code** If your code is too long to fit in a codeblock in discord, you can paste your code here: -https://paste.pydis.com/ +https://paste.pythondiscord.com/ After pasting your code, **save** it by clicking the floppy disk icon in the top right, or by typing `ctrl + S`. After doing that, the URL should **change**. Copy the URL and post it here so others can see it. -- cgit v1.2.3 From 650e739fbb66eb30502675cc3b8bd257e1ee825d Mon Sep 17 00:00:00 2001 From: Johannes Christ Date: Fri, 24 Sep 2021 19:53:58 +0200 Subject: Suppress NotFound for batch deletion. I will copy my comment from the code, as it explains why we want this: In the rare case where we found messages matching the spam filter across multiple channels, it is possible that a single channel will only contain a single message to delete. If that should be the case, discord.py will use the "delete single message" endpoint instead of the bulk delete endpoint, and the single message deletion endpoint will complain if you give it that does not exist. As this means that we have no other message to delete in this channel (and message deletes work per-channel), we can just log an exception and carry on with business. --- bot/exts/filters/antispam.py | 15 ++++++++++++++- 1 file changed, 14 insertions(+), 1 deletion(-) diff --git a/bot/exts/filters/antispam.py b/bot/exts/filters/antispam.py index 8c075fa95..72103c9fb 100644 --- a/bot/exts/filters/antispam.py +++ b/bot/exts/filters/antispam.py @@ -250,7 +250,20 @@ class AntiSpam(Cog): for message in messages: channel_messages[message.channel].append(message) for channel, messages in channel_messages.items(): - await channel.delete_messages(messages) + try: + await channel.delete_messages(messages) + except NotFound: + # In the rare case where we found messages matching the + # spam filter across multiple channels, it is possible + # that a single channel will only contain a single message + # to delete. If that should be the case, discord.py will + # use the "delete single message" endpoint instead of the + # bulk delete endpoint, and the single message deletion + # endpoint will complain if you give it that does not exist. + # As this means that we have no other message to delete in + # this channel (and message deletes work per-channel), + # we can just log an exception and carry on with business. + log.info(f"Tried to delete message `{messages[0].id}`, but message could not be found.") # Otherwise, the bulk delete endpoint will throw up. # Delete the message directly instead. -- cgit v1.2.3