33from collections .abc import Callable , Container
44
55from discord .ext import commands
6- from discord .ext .commands import Command , Context
6+ from discord .ext .commands import CheckFailure , Command , Context
77from pydis_core .utils import logging
88
99from bot .constants import Channels , Month
@@ -69,28 +69,64 @@ async def guarded_listener(*args, **kwargs) -> None:
6969 return decorator
7070
7171
72- def in_month_command (* allowed_months : Month ) -> Callable :
72+ def in_month_command (* allowed_months : Month , roles : tuple [ int , ...] = () ) -> Callable :
7373 """
74- Check whether the command was invoked in one of `enabled_months`.
74+ Check whether the command was invoked in one of `allowed_months`.
75+
76+ The check can be limited to certain roles.
77+ To enable the supplied months for everyone, don't set a value for `roles`.
78+
79+ If a command is decorated several times with this, it only needs to pass one of the checks.
7580
7681 Uses the current UTC month at the time of running the predicate.
7782 """
7883 async def predicate (ctx : Context ) -> bool :
79- current_month = resolve_current_month ()
80- can_run = current_month in allowed_months
81-
82- log .debug (
83- f"Command '{ ctx .command } ' is locked to months { human_months (allowed_months )} . "
84- f"Invoking it in month { current_month !s} is { 'allowed' if can_run else 'disallowed' } ."
85- )
86- if can_run :
84+ command = ctx .command
85+ if "month_checks" not in command .extras :
86+ log .debug (f"No month checks found for command { command } ." )
8787 return True
88- raise InMonthCheckFailure (f"Command can only be used in { human_months (allowed_months )} " )
8988
90- return commands .check (predicate )
89+ everyone_error = None
90+ privileged_user = False
91+ allowed_months_for_user = set ()
92+ current_month = resolve_current_month ()
93+ for checked_roles , checked_months in command .extras ["month_checks" ].items ():
94+ if checked_roles :
95+ uroles = ctx .author .roles
96+ if not uroles or not (set (checked_roles ) & set (r .id for r in uroles )):
97+ log .debug (f"Month check for roles { checked_roles } doesn't apply to { ctx .author } ." )
98+ continue
99+
100+ if current_month in checked_months :
101+ log .debug (f"Month check for roles { checked_roles } passed for { ctx .author } ." )
102+ return True
103+
104+ log .debug (f"Month check for roles { checked_roles } didn't pass for { ctx .author } ." )
105+ if not checked_roles :
106+ everyone_error = InMonthCheckFailure (f"Command can only be used in { human_months (checked_months )} " )
107+ else :
108+ privileged_user = True
109+ allowed_months_for_user |= set (checked_months )
110+
111+ if privileged_user :
112+ allowed_months_for_user = sorted (allowed_months_for_user )
113+ raise InMonthCheckFailure (f"You can run this command only in { human_months (allowed_months_for_user )} " )
114+ if everyone_error :
115+ raise everyone_error
116+ raise CheckFailure ("You cannot run this command." )
117+
118+ def decorator (func : Command ) -> Command :
119+ if "month_checks" in func .extras :
120+ func .extras ["month_checks" ][roles ] = allowed_months
121+ return func
122+
123+ func .extras ["month_checks" ] = {roles : allowed_months }
124+ return commands .check (predicate )(func )
125+
126+ return decorator
91127
92128
93- def in_month (* allowed_months : Month ) -> Callable :
129+ def in_month (* allowed_months : Month , roles : tuple [ int , ...] = () ) -> Callable :
94130 """
95131 Universal decorator for season-locking commands and listeners alike.
96132
@@ -112,10 +148,12 @@ def decorator(callable_: Callable) -> Callable:
112148 # Functions decorated as commands are turned into instances of `Command`
113149 if isinstance (callable_ , Command ):
114150 log .debug (f"Command { callable_ .qualified_name } will be locked to { human_months (allowed_months )} " )
115- actual_deco = in_month_command (* allowed_months )
151+ actual_deco = in_month_command (* allowed_months , roles = roles )
116152
117153 # D.py will assign this attribute when `callable_` is registered as a listener
118154 elif hasattr (callable_ , "__cog_listener__" ):
155+ if roles is not None :
156+ raise ValueError ("Role restrictions are not available for listeners." )
119157 log .debug (f"Listener { callable_ .__qualname__ } will be locked to { human_months (allowed_months )} " )
120158 actual_deco = in_month_listener (* allowed_months )
121159
0 commit comments