diff options
author | 2024-03-20 18:57:34 +0100 | |
---|---|---|
committer | 2024-03-20 10:57:34 -0700 | |
commit | d418e9f28efa8f725173975d0ba42ab5c1228700 (patch) | |
tree | 6b172d2fd8d72dc77e19965a958669227083e5ef /bot | |
parent | Bump sentry-sdk from 1.41.0 to 1.42.0 (#1476) (diff) |
Bump Pydis core to v11 (#1478)
Diffstat (limited to 'bot')
-rw-r--r-- | bot/utils/checks.py | 127 |
1 files changed, 6 insertions, 121 deletions
diff --git a/bot/utils/checks.py b/bot/utils/checks.py index bc6e7111..faba1c72 100644 --- a/bot/utils/checks.py +++ b/bot/utils/checks.py @@ -1,16 +1,9 @@ -import datetime -from collections.abc import Callable, Container, Iterable +from collections.abc import Container from discord.ext.commands import ( - BucketType, - CheckFailure, - Cog, - Command, - CommandOnCooldown, Context, - Cooldown, - CooldownMapping, ) +from pydis_core.utils.checks import in_whitelist_check as core_in_whitelist_check from pydis_core.utils.logging import get_logger from bot import constants @@ -19,22 +12,6 @@ log = get_logger(__name__) CODEJAM_CATEGORY_NAME = "Code Jam" -class InWhitelistCheckFailure(CheckFailure): - """Raised when the `in_whitelist` check fails.""" - - def __init__(self, redirect_channel: int | None): - self.redirect_channel = redirect_channel - - if redirect_channel: - redirect_message = f" here. Please use the <#{redirect_channel}> channel instead" - else: - redirect_message = "" - - error_message = f"You are not allowed to use that command{redirect_message}." - - super().__init__(error_message) - - def in_whitelist_check( ctx: Context, channels: Container[int] = (), @@ -46,57 +23,11 @@ def in_whitelist_check( """ Check if a command was issued in a whitelisted context. - The whitelists that can be provided are: - - - `channels`: a container with channel ids for whitelisted channels - - `categories`: a container with category ids for whitelisted categories - - `roles`: a container with with role ids for whitelisted roles - - If the command was invoked in a context that was not whitelisted, the member is either - redirected to the `redirect` channel that was passed (default: #bot-commands) or simply - told that they're not allowed to use this particular command (if `None` was passed). + Check bot-core's in_whitelist_check docstring for more details. """ - if redirect and redirect not in channels: - # It does not make sense for the channel whitelist to not contain the redirection - # channel (if applicable). That's why we add the redirection channel to the `channels` - # container if it's not already in it. As we allow any container type to be passed, - # we first create a tuple in order to safely add the redirection channel. - # - # Note: It's possible for the redirect channel to be in a whitelisted category, but - # there's no easy way to check that and as a channel can easily be moved in and out of - # categories, it's probably not wise to rely on its category in any case. - channels = tuple(channels) + (redirect,) - - ctx_channel = ctx.channel - if hasattr(ctx_channel, "parent"): - ctx_channel = ctx_channel.parent - - if channels and ctx_channel.id in channels: - log.trace(f"{ctx.author} may use the `{ctx.command.name}` command as they are in a whitelisted channel.") - return True - - # Only check the category id if we have a category whitelist and the channel has a `category_id` - if categories and hasattr(ctx_channel, "category_id") and ctx_channel.category_id in categories: - log.trace(f"{ctx.author} may use the `{ctx.command.name}` command as they are in a whitelisted category.") - return True - - category = getattr(ctx_channel, "category", None) - if category and category.name == CODEJAM_CATEGORY_NAME: - log.trace(f"{ctx.author} may use the `{ctx.command.name}` command as they are in a codejam team channel.") - return True - - # Only check the roles whitelist if we have one and ensure the author's roles attribute returns - # an iterable to prevent breakage in DM channels (for if we ever decide to enable commands there). - if roles and any(r.id in roles for r in getattr(ctx.author, "roles", ())): - log.trace(f"{ctx.author} may use the `{ctx.command.name}` command as they have a whitelisted role.") - return True - - log.trace(f"{ctx.author} may not use the `{ctx.command.name}` command within this context.") - - # Some commands are secret, and should produce no feedback at all. - if not fail_silently: - raise InWhitelistCheckFailure(redirect) - return False + return core_in_whitelist_check( + ctx=ctx, channels=channels, categories=categories, roles=roles, redirect=redirect, fail_silently=fail_silently + ) def with_role_check(ctx: Context, *role_ids: int) -> bool: @@ -136,49 +67,3 @@ def without_role_check(ctx: Context, *role_ids: int) -> bool: f"The result of the without_role check was {check}." ) return check - - -def cooldown_with_role_bypass(rate: int, per: float, type: BucketType = BucketType.default, *, - bypass_roles: Iterable[int]) -> Callable: - """ - Applies a cooldown to a command, but allows members with certain roles to be ignored. - - NOTE: this replaces the `Command.before_invoke` callback, which *might* introduce problems in the future. - """ - # Make it a set so lookup is hash based. - bypass = set(bypass_roles) - - # This handles the actual cooldown logic. - buckets = CooldownMapping(Cooldown(rate, per, type)) - - # Will be called after the command has been parse but before it has been invoked, ensures that - # the cooldown won't be updated if the user screws up their input to the command. - async def predicate(cog: Cog, ctx: Context) -> None: - nonlocal bypass, buckets - - if any(role.id in bypass for role in ctx.author.roles): - return - - # Cooldown logic, taken from discord.py internals. - current = ctx.message.created_at.replace(tzinfo=datetime.UTC).timestamp() - bucket = buckets.get_bucket(ctx.message) - retry_after = bucket.update_rate_limit(current) - if retry_after: - raise CommandOnCooldown(bucket, retry_after) - - def wrapper(command: Command) -> Command: - # NOTE: this could be changed if a subclass of Command were to be used. I didn't see the need for it - # so I just made it raise an error when the decorator is applied before the actual command object exists. - # - # If the `before_invoke` detail is ever a problem then I can quickly just swap over. - if not isinstance(command, Command): - raise TypeError( - "Decorator `cooldown_with_role_bypass` must be applied after the command decorator. " - "This means it has to be above the command decorator in the code." - ) - - command._before_invoke = predicate - - return command - - return wrapper |