From 638658af752dc157d885ae5299ce1771cda183d9 Mon Sep 17 00:00:00 2001 From: Profitroll Date: Sun, 27 Apr 2025 12:39:30 +0200 Subject: [PATCH] Added more custom exceptions and prepared everything for documentation --- classes/errors/__init__.py | 10 ++++- classes/errors/discord.py | 28 +++++++++++++ classes/errors/pycord_event.py | 20 +++++++++ classes/errors/pycord_event_stage.py | 17 ++++++++ classes/errors/pycord_user.py | 33 +++++++++++++++ classes/pycord_bot.py | 5 ++- classes/pycord_event.py | 15 ++++--- classes/pycord_event_stage.py | 9 ++-- classes/pycord_guild.py | 1 + classes/pycord_user.py | 61 +++++++++++----------------- 10 files changed, 146 insertions(+), 53 deletions(-) create mode 100644 classes/errors/discord.py create mode 100644 classes/errors/pycord_event.py create mode 100644 classes/errors/pycord_event_stage.py diff --git a/classes/errors/__init__.py b/classes/errors/__init__.py index baa580e..a0533c9 100644 --- a/classes/errors/__init__.py +++ b/classes/errors/__init__.py @@ -1,2 +1,10 @@ +from pycord_event import EventNotFoundError +from pycord_event_stage import EventStageNotFoundError, EventStageMissingSequenceError +from .discord import DiscordGuildMemberNotFoundError, DiscordCategoryNotFoundError, DiscordChannelNotFoundError from .pycord_guild import GuildNotFoundError -from .pycord_user import UserNotFoundError +from .pycord_user import ( + UserNotFoundError, + UserAlreadyRegisteredForEventError, + UserNotRegisteredForEventError, + UserAlreadyCompletedEventError, +) diff --git a/classes/errors/discord.py b/classes/errors/discord.py new file mode 100644 index 0000000..49a5b44 --- /dev/null +++ b/classes/errors/discord.py @@ -0,0 +1,28 @@ +class DiscordGuildMemberNotFoundError(Exception): + """Member was not found in a discord guild""" + + 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"Member with id {self.user_id} was not found in guild with id {self.guild_id}") + + +class DiscordCategoryNotFoundError(Exception): + """Category was not found in a discord guild""" + + def __init__(self, category_id: int, guild_id: int) -> None: + self.category_id: int = category_id + self.guild_id: int = guild_id + + super().__init__(f"Category with id {self.category_id} was not found in guild with id {self.guild_id}") + + +class DiscordChannelNotFoundError(Exception): + """Channel was not found in a discord guild""" + + def __init__(self, channel_id: int, guild_id: int) -> None: + self.channel_id: int = channel_id + self.guild_id: int = guild_id + + super().__init__(f"Channel with id {self.channel_id} was not found in guild with id {self.guild_id}") diff --git a/classes/errors/pycord_event.py b/classes/errors/pycord_event.py new file mode 100644 index 0000000..4d1ee03 --- /dev/null +++ b/classes/errors/pycord_event.py @@ -0,0 +1,20 @@ +from typing import Optional + +from bson import ObjectId + + +class EventNotFoundError(Exception): + """PycordEvent could not find event with such an ID in the database""" + + def __init__(self, event_id: Optional[str | ObjectId] = None, event_name: Optional[str] = None) -> None: + self.event_id = event_id + self.event_name = event_name + + if self.event_id is None and self.event_name is None: + raise AttributeError("Either event id or name must be provided") + + super().__init__( + f"Event with id {self.event_id} was not found" + if event_id is not None + else f"Event with name {self.event_name} was not found" + ) diff --git a/classes/errors/pycord_event_stage.py b/classes/errors/pycord_event_stage.py new file mode 100644 index 0000000..4e3d519 --- /dev/null +++ b/classes/errors/pycord_event_stage.py @@ -0,0 +1,17 @@ +from bson import ObjectId + + +class EventStageNotFoundError(Exception): + """PycordEventStage could not find event with such an ID in the database""" + + def __init__(self, stage_id: str | ObjectId) -> None: + self.stage_id = stage_id + + super().__init__(f"Stage with id {self.stage_id} was not found") + + +class EventStageMissingSequenceError(Exception): + """No sequence is provided for the event stage""" + + def __init__(self) -> None: + super().__init__("Stage does not have a defined sequence") diff --git a/classes/errors/pycord_user.py b/classes/errors/pycord_user.py index 87b10ae..85e0167 100644 --- a/classes/errors/pycord_user.py +++ b/classes/errors/pycord_user.py @@ -1,3 +1,6 @@ +from bson import ObjectId + + class UserNotFoundError(Exception): """PycordUser could not find user with such an ID in the database""" @@ -6,3 +9,33 @@ class UserNotFoundError(Exception): self.guild_id: int = guild_id super().__init__(f"User with id {self.user_id} was not found in guild {self.guild_id}") + + +class UserAlreadyRegisteredForEventError(Exception): + """PycordUser is already registered for the provided event""" + + def __init__(self, user_id: int, event_id: str | ObjectId) -> None: + self.user_id: int = user_id + self.event_id: str | ObjectId = event_id + + super().__init__(f"User with id {self.user_id} is already registered for the event {self.event_id}") + + +class UserNotRegisteredForEventError(Exception): + """PycordUser is not registered for the provided event""" + + def __init__(self, user_id: int, event_id: str | ObjectId) -> None: + self.user_id: int = user_id + self.event_id: str | ObjectId = event_id + + super().__init__(f"User with id {self.user_id} is not registered for the event {self.event_id}") + + +class UserAlreadyCompletedEventError(Exception): + """PycordUser already completed the provided event""" + + def __init__(self, user_id: int, event_id: str | ObjectId) -> None: + self.user_id: int = user_id + self.event_id: str | ObjectId = event_id + + super().__init__(f"User with id {self.user_id} already completed the event {self.event_id}") diff --git a/classes/pycord_bot.py b/classes/pycord_bot.py index 29b3a2e..c08c4d7 100644 --- a/classes/pycord_bot.py +++ b/classes/pycord_bot.py @@ -12,6 +12,7 @@ from libbot.pycord.classes import PycordBot as LibPycordBot from typing_extensions import override from classes import PycordEvent, PycordEventStage, PycordGuild, PycordUser +from classes.errors import EventStageMissingSequenceError from modules.database import col_events, col_users from modules.utils import get_logger @@ -283,8 +284,7 @@ class PycordBot(LibPycordBot): # event: PycordEvent = await self.find_event(event_id=kwargs["event_id"]) if "sequence" not in kwargs: - # TODO Create a nicer exception - raise RuntimeError("Stage must have a defined sequence") + raise EventStageMissingSequenceError() event_stage: PycordEventStage = await PycordEventStage.create(**kwargs, cache=self.cache) @@ -306,6 +306,7 @@ class PycordBot(LibPycordBot): async def find_event_stage(self, stage_id: str | ObjectId) -> PycordEventStage: return await PycordEventStage.from_id(stage_id, cache=self.cache) + # TODO Add documentation @staticmethod async def process_attachments(attachments: List[Attachment]) -> List[Dict[str, Any]]: processed_attachments: List[Dict[str, Any]] = [] diff --git a/classes/pycord_event.py b/classes/pycord_event.py index 9c64ba9..95c9599 100644 --- a/classes/pycord_event.py +++ b/classes/pycord_event.py @@ -9,6 +9,7 @@ from discord import Bot from libbot.cache.classes import Cache from pymongo.results import InsertOneResult +from classes.errors import EventNotFoundError from modules.database import col_events from modules.utils import get_logger, restore_from_cache @@ -70,16 +71,14 @@ class PycordEvent: ) if db_entry is None: - raise RuntimeError(f"Event {event_id} not found") - - # TODO Add a unique exception - # raise EventNotFoundError(event_id) + raise EventNotFoundError(event_id=event_id) if cache is not None: cache.set_json(f"{cls.__short_name__}_{event_id}", db_entry) return cls(**db_entry) + # TODO Add documentation @classmethod async def from_name(cls, event_name: str, cache: Optional[Cache] = None) -> "PycordEvent": # TODO Add sorting by creation date or something. @@ -87,16 +86,14 @@ class PycordEvent: db_entry: Dict[str, Any] | None = await cls.__collection__.find_one({"name": event_name}) if db_entry is None: - raise RuntimeError(f"Event with name {event_name} not found") - - # TODO Add a unique exception - # raise EventNotFoundError(event_name) + raise EventNotFoundError(event_name=event_name) if cache is not None: cache.set_json(f"{cls.__short_name__}_{db_entry['_id']}", db_entry) return cls(**db_entry) + # TODO Add documentation @classmethod async def create( cls, @@ -217,6 +214,7 @@ class PycordEvent: "stage_ids": self.stage_ids, } + # TODO Add documentation @staticmethod def get_defaults() -> Dict[str, Any]: return { @@ -232,6 +230,7 @@ class PycordEvent: "stage_ids": [], } + # TODO Add documentation @staticmethod def get_default_value(key: str) -> Any: if key not in PycordEvent.get_defaults(): diff --git a/classes/pycord_event_stage.py b/classes/pycord_event_stage.py index bceb989..416f6eb 100644 --- a/classes/pycord_event_stage.py +++ b/classes/pycord_event_stage.py @@ -10,6 +10,7 @@ from discord import File from libbot.cache.classes import Cache from pymongo.results import InsertOneResult +from classes.errors import EventStageNotFoundError from modules.database import col_stages from modules.utils import get_logger, restore_from_cache @@ -66,16 +67,14 @@ class PycordEventStage: ) if db_entry is None: - raise RuntimeError(f"Event stage {stage_id} not found") - - # TODO Add a unique exception - # raise EventStageNotFoundError(event_id) + raise EventStageNotFoundError(stage_id) if cache is not None: cache.set_json(f"{cls.__short_name__}_{stage_id}", db_entry) return cls(**db_entry) + # TODO Add documentation @classmethod async def create( cls, @@ -193,6 +192,7 @@ class PycordEventStage: "media": self.media, } + # TODO Add documentation @staticmethod def get_defaults() -> Dict[str, Any]: return { @@ -206,6 +206,7 @@ class PycordEventStage: "media": [], } + # TODO Add documentation @staticmethod def get_default_value(key: str) -> Any: if key not in PycordEventStage.get_defaults(): diff --git a/classes/pycord_guild.py b/classes/pycord_guild.py index 771a601..5355976 100644 --- a/classes/pycord_guild.py +++ b/classes/pycord_guild.py @@ -147,6 +147,7 @@ class PycordGuild: "timezone": self.timezone, } + # TODO Add documentation @staticmethod def get_defaults(guild_id: Optional[int] = None) -> Dict[str, Any]: return { diff --git a/classes/pycord_user.py b/classes/pycord_user.py index fa1b5dd..026f9f6 100644 --- a/classes/pycord_user.py +++ b/classes/pycord_user.py @@ -17,7 +17,15 @@ from discord.abc import GuildChannel from libbot.cache.classes import Cache from pymongo.results import InsertOneResult -from classes.errors.pycord_user import UserNotFoundError +from classes.errors import ( + UserNotFoundError, + UserAlreadyRegisteredForEventError, + UserAlreadyCompletedEventError, + UserNotRegisteredForEventError, + DiscordGuildMemberNotFoundError, + DiscordCategoryNotFoundError, + DiscordChannelNotFoundError, +) from modules.database import col_users from modules.utils import get_logger, restore_from_cache @@ -192,6 +200,7 @@ class PycordUser: cache.delete(self._get_cache_key()) + # TODO Add documentation @staticmethod def get_defaults(user_id: Optional[int] = None, guild_id: Optional[int] = None) -> Dict[str, Any]: return { @@ -205,6 +214,7 @@ class PycordUser: "completed_event_ids": [], } + # TODO Add documentation @staticmethod def get_default_value(key: str) -> Any: if key not in PycordUser.get_defaults(): @@ -226,10 +236,7 @@ class PycordUser: event_id: ObjectId = ObjectId(event_id) if isinstance(event_id, str) else event_id if event_id in self.registered_event_ids: - raise RuntimeError(f"User is already registered for event {event_id}") - - # TODO Add a unique exception - # raise UserAlreadyRegisteredForEventError(event_name) + raise UserAlreadyRegisteredForEventError(self.id, event_id) self.registered_event_ids.append(event_id) @@ -240,10 +247,7 @@ class PycordUser: event_id: ObjectId = ObjectId(event_id) if isinstance(event_id, str) else event_id if event_id not in self.registered_event_ids: - raise RuntimeError(f"User is not registered for event {event_id}") - - # TODO Add a unique exception - # raise UserNotRegisteredForEventError(event_name) + raise UserNotRegisteredForEventError(self.id, event_id) self.registered_event_ids.remove(event_id) @@ -254,15 +258,13 @@ class PycordUser: event_id: ObjectId = ObjectId(event_id) if isinstance(event_id, str) else event_id if event_id in self.completed_event_ids: - raise RuntimeError(f"User has already completed event {event_id}") - - # TODO Add a unique exception - # raise UserAlreadyCompletedEventError(event_name) + raise UserAlreadyCompletedEventError(self.id, event_id) self.completed_event_ids.append(event_id) await self._set(cache, completed_event_ids=self.completed_event_ids) + # TODO Add documentation async def setup_event_channel( self, bot: Bot, @@ -275,23 +277,13 @@ class PycordUser: return None discord_member: Member | None = guild.get_member(self.id) - discord_category: GuildChannel | None = bot.get_channel(pycord_guild.category_id) + discord_category: GuildChannel | None = bot.get_channel(pycord_guild.channel_id) if discord_member is None: - raise RuntimeError( - f"Discord guild member with ID {self.id} in guild with ID {guild.id} could not be found!" - ) - - # TODO Add a unique exception - # raise DiscordGuildMemberNotFoundError(self.id, guild.id) + raise DiscordGuildMemberNotFoundError(self.id, guild.id) if discord_category is None: - raise RuntimeError( - f"Discord category with ID {pycord_guild.category_id} in guild with ID {guild.id} could not be found!" - ) - - # TODO Add a unique exception - # raise DiscordCategoryNotFoundError(pycord_guild.category_id, guild.id) + raise DiscordCategoryNotFoundError(pycord_guild.channel_id, guild.id) permission_overwrites: Dict[Role | Member, PermissionOverwrite] = { guild.default_role: PermissionOverwrite( @@ -318,6 +310,7 @@ class PycordUser: return channel + # TODO Add documentation async def lock_event_channel( self, guild: Guild, @@ -331,20 +324,10 @@ class PycordUser: ) if discord_member is None: - raise RuntimeError( - f"Discord guild member with ID {self.id} in guild with ID {guild.id} could not be found!" - ) - - # TODO Add a unique exception - # raise DiscordGuildMemberNotFoundError(self.id, guild.id) + raise DiscordGuildMemberNotFoundError(self.id, guild.id) if discord_member is None: - raise RuntimeError( - f"Discord channel with ID {self.event_channels[str(event_id)]} in guild with ID {guild.id} could not be found!" - ) - - # TODO Add a unique exception - # raise DiscordChannelNotFoundError(self.event_channels[str(event_id)], guild.id) + raise DiscordChannelNotFoundError(self.event_channels[str(event_id)], guild.id) permission_overwrite: PermissionOverwrite = PermissionOverwrite( view_channel=not completely, @@ -375,8 +358,10 @@ class PycordUser: async def set_event_stage(self, stage_id: str | ObjectId | None, cache: Optional[Cache] = None) -> None: await self._set(cache, current_stage_id=stage_id if isinstance(stage_id, str) else ObjectId(stage_id)) + # TODO Add documentation async def jail(self, cache: Optional[Cache] = None) -> None: await self._set(cache, is_jailed=True) + # TODO Add documentation async def unjail(self, cache: Optional[Cache] = None) -> None: await self._set(cache, is_jailed=False)