diff --git a/classes/errors/pycord_user.py b/classes/errors/pycord_user.py index 7576b68..87b10ae 100644 --- a/classes/errors/pycord_user.py +++ b/classes/errors/pycord_user.py @@ -1,7 +1,8 @@ class UserNotFoundError(Exception): """PycordUser could not find user with such an ID in the database""" - def __init__(self, user_id: int) -> None: - self.user_id = user_id + def __init__(self, user_id: int, guild_id: int) -> None: + self.user_id: int = user_id + self.guild_id: int = guild_id - super().__init__(f"User with id {self.user_id} was not found") + super().__init__(f"User with id {self.user_id} was not found in guild {self.guild_id}") diff --git a/classes/pycord_bot.py b/classes/pycord_bot.py index 46c4b56..d1aa614 100644 --- a/classes/pycord_bot.py +++ b/classes/pycord_bot.py @@ -192,11 +192,12 @@ class PycordBot(LibPycordBot): await management_channel.send(message) - async def find_user(self, user: int | User) -> PycordUser: + async def find_user(self, user: int | User, guild: int | Guild) -> PycordUser: """Find User by its ID or User object. Args: user (int | User): ID or User object to extract ID from + guild (int | Guild): ID or Guild object to extract ID from Returns: PycordUser: User object @@ -204,11 +205,10 @@ class PycordBot(LibPycordBot): Raises: UserNotFoundException: User was not found and creation was not allowed """ - return ( - await PycordUser.from_id(user, cache=self.cache) - if isinstance(user, int) - else await PycordUser.from_id(user.id, cache=self.cache) - ) + user_id: int = user if isinstance(user, int) else user.id + guild_id: int = guild if isinstance(guild, int) else guild.id + + return await PycordUser.from_id(user_id, guild_id, cache=self.cache) async def find_guild(self, guild: int | Guild) -> PycordGuild: """Find Guild by its ID or Guild object. diff --git a/classes/pycord_user.py b/classes/pycord_user.py index e062f2e..356dc25 100644 --- a/classes/pycord_user.py +++ b/classes/pycord_user.py @@ -51,12 +51,13 @@ class PycordUser: @classmethod async def from_id( - cls, user_id: int, allow_creation: bool = True, cache: Optional[Cache] = None + cls, user_id: int, guild_id: int, allow_creation: bool = True, cache: Optional[Cache] = None ) -> "PycordUser": """Find user in database and create new record if user does not exist. Args: user_id (int): User's Discord ID + guild_id (int): User's guild Discord ID allow_creation (:obj:`bool`, optional): Create new user record if none found in the database cache (:obj:`Cache`, optional): Cache engine to get the cache from @@ -66,25 +67,27 @@ class PycordUser: Raises: UserNotFoundError: User was not found and creation was not allowed """ - cached_entry: Dict[str, Any] | None = restore_from_cache(cls.__short_name__, user_id, cache=cache) + cached_entry: Dict[str, Any] | None = restore_from_cache( + cls.__short_name__, f"{user_id}_{guild_id}", cache=cache + ) if cached_entry is not None: return cls(**cached_entry) - db_entry = await cls.__collection__.find_one({"id": user_id}) + db_entry = await cls.__collection__.find_one({"id": user_id, "guild_id": guild_id}) if db_entry is None: if not allow_creation: - raise UserNotFoundError(user_id) + raise UserNotFoundError(user_id, guild_id) - db_entry = PycordUser.get_defaults(user_id) + db_entry = PycordUser.get_defaults(user_id, guild_id) insert_result: InsertOneResult = await cls.__collection__.insert_one(db_entry) db_entry["_id"] = insert_result.inserted_id if cache is not None: - cache.set_json(f"{cls.__short_name__}_{user_id}", db_entry) + cache.set_json(f"{cls.__short_name__}_{user_id}_{guild_id}", db_entry) return cls(**db_entry) @@ -367,3 +370,9 @@ class PycordUser: await self._set( cache, current_stage_id=stage_id if isinstance(stage_id, str) else ObjectId(stage_id) ) + + async def jail(self, cache: Optional[Cache] = None) -> None: + await self._set(cache, is_jailed=True) + + async def unjail(self, cache: Optional[Cache] = None) -> None: + await self._set(cache, is_jailed=False) diff --git a/cogs/guess.py b/cogs/guess.py index fea3e55..7a89f52 100644 --- a/cogs/guess.py +++ b/cogs/guess.py @@ -28,7 +28,14 @@ class Guess(Cog): await ctx.respond("Guild is not configured.") return - user: PycordUser = await self.bot.find_user(ctx.author) + user: PycordUser = await self.bot.find_user(ctx.author, ctx.guild) + + if user.is_jailed: + # TODO Make a nice message + await ctx.respond( + "You are jailed and cannot interact with events. Please, contact the administrator." + ) + return if user.current_event_id is None or user.current_stage_id is None: # TODO Make a nice message diff --git a/cogs/register.py b/cogs/register.py index 8700c2f..9c75dfa 100644 --- a/cogs/register.py +++ b/cogs/register.py @@ -40,7 +40,14 @@ class Register(Cog): await ctx.respond("Guild is not configured.") return - user: PycordUser = await self.bot.find_user(ctx.author) + user: PycordUser = await self.bot.find_user(ctx.author, ctx.guild) + + if user.is_jailed: + # TODO Make a nice message + await ctx.respond( + "You are jailed and cannot interact with events. Please, contact the administrator." + ) + return if pycord_event._id in user.registered_event_ids: # TODO Make a nice message diff --git a/cogs/unregister.py b/cogs/unregister.py index fdffe7e..be4d521 100644 --- a/cogs/unregister.py +++ b/cogs/unregister.py @@ -44,7 +44,14 @@ class Unregister(Cog): await ctx.respond("Guild is not configured.") return - user: PycordUser = await self.bot.find_user(ctx.author) + user: PycordUser = await self.bot.find_user(ctx.author, ctx.guild) + + if user.is_jailed: + # TODO Make a nice message + await ctx.respond( + "You are jailed and cannot interact with events. Please, contact the administrator." + ) + return if pycord_event._id not in user.registered_event_ids: # TODO Make a nice message diff --git a/cogs/user.py b/cogs/user.py index 9d0102e..2d24fe4 100644 --- a/cogs/user.py +++ b/cogs/user.py @@ -6,6 +6,7 @@ from discord import ( ) from discord.ext.commands import Cog +from classes import PycordUser from classes.pycord_bot import PycordBot @@ -57,7 +58,7 @@ class User(Cog): ) -> None: await ctx.respond("Not implemented.") - # TODO Implement the command + # TODO Introduce i18n @command_group.command( name="jail", description="Jail the user", @@ -68,9 +69,25 @@ class User(Cog): ) @option("confirm", description="Confirmation of the operation", required=False) async def command_user_jail(self, ctx: ApplicationContext, user: User, confirm: bool = False) -> None: - await ctx.respond("Not implemented.") + if confirm is None or not confirm: + await ctx.respond(self.bot._("operation_unconfirmed", "messages", locale=ctx.locale)) + return - # TODO Implement the command + pycord_user: PycordUser = await self.bot.find_user(user, ctx.guild) + + if pycord_user.is_jailed: + # TODO Make a nice message + await ctx.respond(f"User **{user.display_name}** is already jailed.") + return + + await pycord_user.jail(self.bot.cache) + + # TODO Make a nice message + await ctx.respond( + f"User **{user.display_name}** has been jailed and cannot interact with events anymore." + ) + + # TODO Introduce i18n @command_group.command( name="unjail", description="Unjail the user", @@ -81,7 +98,23 @@ class User(Cog): ) @option("confirm", description="Confirmation of the operation", required=False) async def command_user_unjail(self, ctx: ApplicationContext, user: User, confirm: bool = False) -> None: - await ctx.respond("Not implemented.") + if confirm is None or not confirm: + await ctx.respond(self.bot._("operation_unconfirmed", "messages", locale=ctx.locale)) + return + + pycord_user: PycordUser = await self.bot.find_user(user, ctx.guild) + + if not pycord_user.is_jailed: + # TODO Make a nice message + await ctx.respond(f"User **{user.display_name}** is not jailed.") + return + + await pycord_user.unjail(self.bot.cache) + + # TODO Make a nice message + await ctx.respond( + f"User **{user.display_name}** has been unjailed and can interact with events again." + ) def setup(bot: PycordBot) -> None: diff --git a/modules/utils/autocomplete_utils.py b/modules/utils/autocomplete_utils.py index e463b1c..06454a2 100644 --- a/modules/utils/autocomplete_utils.py +++ b/modules/utils/autocomplete_utils.py @@ -16,12 +16,10 @@ async def autocomplete_timezones(ctx: AutocompleteContext) -> List[str]: return sorted(list(available_timezones())) -@deprecated +@deprecated("Messages will not be displayed on per-user basis") async def autocomplete_languages(ctx: AutocompleteContext) -> List[str]: """Return locales supported by the bot""" - # TODO Discord normally uses a different set of locales. - # For example, "en" being "en-US", etc. This will require changes to locale handling later. return ctx.bot.locales.keys()