Compare commits

2 Commits

Author SHA1 Message Date
11f0cc384a Changed line length to 108 in black 2025-04-27 22:06:35 +02:00
923173ebe8 Added a stub for #11 and slightly improved typing 2025-04-27 22:05:34 +02:00
16 changed files with 110 additions and 49 deletions

View File

@@ -1,10 +1,15 @@
from pycord_event import EventNotFoundError from pycord_event import EventNotFoundError
from pycord_event_stage import EventStageNotFoundError, EventStageMissingSequenceError from pycord_event_stage import EventStageMissingSequenceError, EventStageNotFoundError
from .discord import DiscordGuildMemberNotFoundError, DiscordCategoryNotFoundError, DiscordChannelNotFoundError
from .discord import (
DiscordCategoryNotFoundError,
DiscordChannelNotFoundError,
DiscordGuildMemberNotFoundError,
)
from .pycord_guild import GuildNotFoundError from .pycord_guild import GuildNotFoundError
from .pycord_user import ( from .pycord_user import (
UserNotFoundError,
UserAlreadyRegisteredForEventError,
UserNotRegisteredForEventError,
UserAlreadyCompletedEventError, UserAlreadyCompletedEventError,
UserAlreadyRegisteredForEventError,
UserNotFoundError,
UserNotRegisteredForEventError,
) )

View File

@@ -15,7 +15,9 @@ class DiscordCategoryNotFoundError(Exception):
self.category_id: int = category_id self.category_id: int = category_id
self.guild_id: int = guild_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}") super().__init__(
f"Category with id {self.category_id} was not found in guild with id {self.guild_id}"
)
class DiscordChannelNotFoundError(Exception): class DiscordChannelNotFoundError(Exception):
@@ -25,4 +27,6 @@ class DiscordChannelNotFoundError(Exception):
self.channel_id: int = channel_id self.channel_id: int = channel_id
self.guild_id: int = guild_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}") super().__init__(
f"Channel with id {self.channel_id} was not found in guild with id {self.guild_id}"
)

View File

@@ -5,7 +5,7 @@ class EventStageNotFoundError(Exception):
"""PycordEventStage could not find event with such an ID in the database""" """PycordEventStage could not find event with such an ID in the database"""
def __init__(self, stage_id: str | ObjectId) -> None: def __init__(self, stage_id: str | ObjectId) -> None:
self.stage_id = stage_id self.stage_id: str | ObjectId = stage_id
super().__init__(f"Stage with id {self.stage_id} was not found") super().__init__(f"Stage with id {self.stage_id} was not found")

View File

@@ -2,6 +2,6 @@ class GuildNotFoundError(Exception):
"""PycordGuild could not find guild with such an ID in the database""" """PycordGuild could not find guild with such an ID in the database"""
def __init__(self, guild_id: int) -> None: def __init__(self, guild_id: int) -> None:
self.guild_id = guild_id self.guild_id: int = guild_id
super().__init__(f"Guild with id {self.guild_id} was not found") super().__init__(f"Guild with id {self.guild_id} was not found")

View File

@@ -55,7 +55,9 @@ class PycordBot(LibPycordBot):
await super().close(**kwargs) await super().close(**kwargs)
async def _schedule_tasks(self) -> None: async def _schedule_tasks(self) -> None:
self.scheduler.add_job(self._execute_event_controller, trigger="cron", minute="*/1", id="event_controller") self.scheduler.add_job(
self._execute_event_controller, trigger="cron", minute="*/1", id="event_controller"
)
async def _execute_event_controller(self) -> None: async def _execute_event_controller(self) -> None:
await self._process_events_start() await self._process_events_start()
@@ -136,7 +138,9 @@ class PycordBot(LibPycordBot):
first_stage_files: List[File] | None = first_stage.get_media_files() first_stage_files: List[File] | None = first_stage.get_media_files()
await user_channel.send(f"First stage...\n\n{first_stage.question}", files=first_stage_files) await user_channel.send(
f"First stage...\n\n{first_stage.question}", files=first_stage_files
)
# TODO Make a nice message # TODO Make a nice message
await self.notify_admins( await self.notify_admins(

View File

@@ -97,7 +97,9 @@ class PycordEvent:
return cls(**db_entry) return cls(**db_entry)
@classmethod @classmethod
async def from_name(cls, event_name: str, guild_id: int, cache: Optional[Cache] = None) -> "PycordEvent": async def from_name(
cls, event_name: str, guild_id: int, cache: Optional[Cache] = None
) -> "PycordEvent":
"""Find the event by its name and construct PycordEvent from database entry. """Find the event by its name and construct PycordEvent from database entry.
If multiple events with the same name exist, the one with the greatest start date will be returned. If multiple events with the same name exist, the one with the greatest start date will be returned.
@@ -244,7 +246,9 @@ class PycordEvent:
stage_index: int = self.stage_ids.index(event_stage_id) stage_index: int = self.stage_ids.index(event_stage_id)
old_stage_index: int = old_stage_ids.index(event_stage_id) old_stage_index: int = old_stage_ids.index(event_stage_id)
logger.debug("Indexes for %s: was %s and is now %s", event_stage_id, old_stage_index, stage_index) logger.debug(
"Indexes for %s: was %s and is now %s", event_stage_id, old_stage_index, stage_index
)
if stage_index != old_stage_index: if stage_index != old_stage_index:
await (await bot.find_event_stage(event_stage_id)).update(cache, sequence=stage_index) await (await bot.find_event_stage(event_stage_id)).update(cache, sequence=stage_index)
@@ -407,7 +411,9 @@ class PycordEvent:
await self._set(cache, stage_ids=self.stage_ids) await self._set(cache, stage_ids=self.stage_ids)
await self._update_event_stage_order(bot, old_stage_ids, cache=cache) await self._update_event_stage_order(bot, old_stage_ids, cache=cache)
async def remove_stage(self, bot: "PycordBot", event_stage_id: ObjectId, cache: Optional[Cache] = None) -> None: async def remove_stage(
self, bot: "PycordBot", event_stage_id: ObjectId, cache: Optional[Cache] = None
) -> None:
"""Remove a stage from the event. """Remove a stage from the event.
Args: Args:

View File

@@ -18,13 +18,13 @@ from libbot.cache.classes import Cache
from pymongo.results import InsertOneResult from pymongo.results import InsertOneResult
from classes.errors import ( from classes.errors import (
UserNotFoundError,
UserAlreadyRegisteredForEventError,
UserAlreadyCompletedEventError,
UserNotRegisteredForEventError,
DiscordGuildMemberNotFoundError,
DiscordCategoryNotFoundError, DiscordCategoryNotFoundError,
DiscordChannelNotFoundError, DiscordChannelNotFoundError,
DiscordGuildMemberNotFoundError,
UserAlreadyCompletedEventError,
UserAlreadyRegisteredForEventError,
UserNotFoundError,
UserNotRegisteredForEventError,
) )
from modules.database import col_users from modules.database import col_users
from modules.utils import get_logger, restore_from_cache from modules.utils import get_logger, restore_from_cache
@@ -122,8 +122,12 @@ class PycordUser:
"guild_id": self.guild_id, "guild_id": self.guild_id,
"event_channels": self.event_channels, "event_channels": self.event_channels,
"is_jailed": self.is_jailed, "is_jailed": self.is_jailed,
"current_event_id": (self.current_event_id if not json_compatible else str(self.current_event_id)), "current_event_id": (
"current_stage_id": (self.current_stage_id if not json_compatible else str(self.current_stage_id)), self.current_event_id if not json_compatible else str(self.current_event_id)
),
"current_stage_id": (
self.current_stage_id if not json_compatible else str(self.current_stage_id)
),
"registered_event_ids": ( "registered_event_ids": (
self.registered_event_ids self.registered_event_ids
if not json_compatible if not json_compatible
@@ -356,7 +360,9 @@ class PycordUser:
# TODO Add documentation # TODO Add documentation
async def set_event_stage(self, stage_id: str | ObjectId | None, cache: Optional[Cache] = None) -> None: 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)) await self._set(
cache, current_stage_id=stage_id if isinstance(stage_id, str) else ObjectId(stage_id)
)
# TODO Add documentation # TODO Add documentation
async def jail(self, cache: Optional[Cache] = None) -> None: async def jail(self, cache: Optional[Cache] = None) -> None:

View File

@@ -36,14 +36,18 @@ class CogConfig(Cog):
@option( @option(
"category", "category",
description=_("description", "commands", "config_set", "options", "category"), description=_("description", "commands", "config_set", "options", "category"),
description_localizations=in_every_locale("description", "commands", "config_set", "options", "category"), description_localizations=in_every_locale(
"description", "commands", "config_set", "options", "category"
),
required=True, required=True,
) )
@option("channel", description="Text channel for admin notifications", required=True) @option("channel", description="Text channel for admin notifications", required=True)
@option( @option(
"timezone", "timezone",
description=_("description", "commands", "config_set", "options", "timezone"), description=_("description", "commands", "config_set", "options", "timezone"),
description_localizations=in_every_locale("description", "commands", "config_set", "options", "timezone"), description_localizations=in_every_locale(
"description", "commands", "config_set", "options", "timezone"
),
autocomplete=basic_autocomplete(autocomplete_timezones), autocomplete=basic_autocomplete(autocomplete_timezones),
required=True, required=True,
) )
@@ -59,7 +63,9 @@ class CogConfig(Cog):
try: try:
timezone_parsed: ZoneInfo = ZoneInfo(timezone) timezone_parsed: ZoneInfo = ZoneInfo(timezone)
except ZoneInfoNotFoundError: except ZoneInfoNotFoundError:
await ctx.respond(self.bot._("timezone_invalid", "messages", locale=ctx.locale).format(timezone=timezone)) await ctx.respond(
self.bot._("timezone_invalid", "messages", locale=ctx.locale).format(timezone=timezone)
)
return return
await guild.update( await guild.update(
@@ -79,7 +85,9 @@ class CogConfig(Cog):
@option( @option(
"confirm", "confirm",
description=_("description", "commands", "config_reset", "options", "confirm"), description=_("description", "commands", "config_reset", "options", "confirm"),
description_localizations=in_every_locale("description", "commands", "config_reset", "options", "confirm"), description_localizations=in_every_locale(
"description", "commands", "config_reset", "options", "confirm"
),
required=False, required=False,
) )
async def command_config_reset(self, ctx: ApplicationContext, confirm: bool = False) -> None: async def command_config_reset(self, ctx: ApplicationContext, confirm: bool = False) -> None:

View File

@@ -11,7 +11,7 @@ from discord import (
) )
from discord.ext.commands import Cog from discord.ext.commands import Cog
from discord.utils import basic_autocomplete from discord.utils import basic_autocomplete
from libbot.i18n import in_every_locale, _ from libbot.i18n import _, in_every_locale
from classes import PycordEvent, PycordEventStage, PycordGuild from classes import PycordEvent, PycordEventStage, PycordGuild
from classes.pycord_bot import PycordBot from classes.pycord_bot import PycordBot

View File

@@ -5,13 +5,13 @@ from typing import List
from zoneinfo import ZoneInfo from zoneinfo import ZoneInfo
from bson.errors import InvalidId from bson.errors import InvalidId
from discord import ApplicationContext, Cog, option, slash_command, TextChannel, File from discord import ApplicationContext, Cog, File, TextChannel, option, slash_command
from discord.utils import basic_autocomplete from discord.utils import basic_autocomplete
from libbot.i18n import in_every_locale, _ from libbot.i18n import _, in_every_locale
from classes import PycordEvent, PycordGuild, PycordUser, PycordEventStage from classes import PycordEvent, PycordEventStage, PycordGuild, PycordUser
from classes.pycord_bot import PycordBot from classes.pycord_bot import PycordBot
from modules.utils import autocomplete_active_events, get_unix_timestamp, get_logger from modules.utils import autocomplete_active_events, get_logger, get_unix_timestamp
logger: Logger = get_logger(__name__) logger: Logger = get_logger(__name__)

View File

@@ -4,7 +4,7 @@ from bson.errors import InvalidId
from discord import ApplicationContext, Attachment, SlashCommandGroup, option from discord import ApplicationContext, Attachment, SlashCommandGroup, option
from discord.ext.commands import Cog from discord.ext.commands import Cog
from discord.utils import basic_autocomplete from discord.utils import basic_autocomplete
from libbot.i18n import in_every_locale, _ from libbot.i18n import _, in_every_locale
from classes import PycordEvent, PycordEventStage, PycordGuild from classes import PycordEvent, PycordEventStage, PycordGuild
from classes.pycord_bot import PycordBot from classes.pycord_bot import PycordBot
@@ -67,7 +67,9 @@ class CogStage(Cog):
if not (await is_event_status_valid(ctx, pycord_event)): if not (await is_event_status_valid(ctx, pycord_event)):
return return
processed_media: List[Dict[str, Any]] = [] if media is None else await self.bot.process_attachments([media]) processed_media: List[Dict[str, Any]] = (
[] if media is None else await self.bot.process_attachments([media])
)
event_stage: PycordEventStage = await self.bot.create_event_stage( event_stage: PycordEventStage = await self.bot.create_event_stage(
event=pycord_event, event=pycord_event,
@@ -146,7 +148,9 @@ class CogStage(Cog):
await ctx.respond("Stage sequence out of range.") await ctx.respond("Stage sequence out of range.")
return return
processed_media: List[Dict[str, Any]] = [] if media is None else await self.bot.process_attachments([media]) processed_media: List[Dict[str, Any]] = (
[] if media is None else await self.bot.process_attachments([media])
)
if not (question is None and answer is None and media is None and remove_media is False): if not (question is None and answer is None and media is None and remove_media is False):
await event_stage.update( await event_stage.update(

View File

@@ -1,7 +1,7 @@
from bson.errors import InvalidId from bson.errors import InvalidId
from discord import ApplicationContext, Cog, option, slash_command from discord import ApplicationContext, Cog, option, slash_command
from discord.utils import basic_autocomplete from discord.utils import basic_autocomplete
from libbot.i18n import in_every_locale, _ from libbot.i18n import _, in_every_locale
from classes import PycordEvent, PycordGuild, PycordUser from classes import PycordEvent, PycordGuild, PycordUser
from classes.pycord_bot import PycordBot from classes.pycord_bot import PycordBot

View File

@@ -54,7 +54,9 @@ class CogUser(Cog):
description="Selected user", description="Selected user",
) )
@option("confirm", description="Confirmation of the operation", required=False) @option("confirm", description="Confirmation of the operation", required=False)
async def command_user_delete_channel(self, ctx: ApplicationContext, user: User, confirm: bool = False) -> None: async def command_user_delete_channel(
self, ctx: ApplicationContext, user: User, confirm: bool = False
) -> None:
await ctx.respond("Not implemented.") await ctx.respond("Not implemented.")
# TODO Introduce i18n # TODO Introduce i18n
@@ -81,7 +83,9 @@ class CogUser(Cog):
await pycord_user.jail(self.bot.cache) await pycord_user.jail(self.bot.cache)
# TODO Introduce i18n # TODO Introduce i18n
await ctx.respond(f"User **{user.display_name}** has been jailed and cannot interact with events anymore.") await ctx.respond(
f"User **{user.display_name}** has been jailed and cannot interact with events anymore."
)
# TODO Introduce i18n # TODO Introduce i18n
@command_group.command( @command_group.command(
@@ -107,7 +111,9 @@ class CogUser(Cog):
await pycord_user.unjail(self.bot.cache) await pycord_user.unjail(self.bot.cache)
# TODO Introduce i18n # TODO Introduce i18n
await ctx.respond(f"User **{user.display_name}** has been unjailed and can interact with events again.") await ctx.respond(
f"User **{user.display_name}** has been unjailed and can interact with events again."
)
def setup(bot: PycordBot) -> None: def setup(bot: PycordBot) -> None:

View File

@@ -1,7 +1,6 @@
from logging import Logger from logging import Logger
from discord import Activity, ActivityType, Cog from discord import Activity, ActivityType, Cog, Member
from discord.ext import commands
from classes.pycord_bot import PycordBot from classes.pycord_bot import PycordBot
from modules.utils import get_logger from modules.utils import get_logger
@@ -13,7 +12,7 @@ class CogUtility(Cog):
def __init__(self, bot: PycordBot): def __init__(self, bot: PycordBot):
self.bot: PycordBot = bot self.bot: PycordBot = bot
@commands.Cog.listener() @Cog.listener()
async def on_ready(self) -> None: async def on_ready(self) -> None:
"""Listener for the event when bot connects to Discord and becomes "ready".""" """Listener for the event when bot connects to Discord and becomes "ready"."""
logger.info("Logged in as %s", self.bot.user) logger.info("Logged in as %s", self.bot.user)
@@ -26,22 +25,39 @@ class CogUtility(Cog):
return return
if activity_type == "playing": if activity_type == "playing":
await self.bot.change_presence(activity=Activity(type=ActivityType.playing, name=activity_message)) await self.bot.change_presence(
activity=Activity(type=ActivityType.playing, name=activity_message)
)
elif activity_type == "watching": elif activity_type == "watching":
await self.bot.change_presence(activity=Activity(type=ActivityType.watching, name=activity_message)) await self.bot.change_presence(
activity=Activity(type=ActivityType.watching, name=activity_message)
)
elif activity_type == "listening": elif activity_type == "listening":
await self.bot.change_presence(activity=Activity(type=ActivityType.listening, name=activity_message)) await self.bot.change_presence(
activity=Activity(type=ActivityType.listening, name=activity_message)
)
elif activity_type == "streaming": elif activity_type == "streaming":
await self.bot.change_presence(activity=Activity(type=ActivityType.streaming, name=activity_message)) await self.bot.change_presence(
activity=Activity(type=ActivityType.streaming, name=activity_message)
)
elif activity_type == "competing": elif activity_type == "competing":
await self.bot.change_presence(activity=Activity(type=ActivityType.competing, name=activity_message)) await self.bot.change_presence(
activity=Activity(type=ActivityType.competing, name=activity_message)
)
elif activity_type == "custom": elif activity_type == "custom":
await self.bot.change_presence(activity=Activity(type=ActivityType.custom, name=activity_message)) await self.bot.change_presence(
activity=Activity(type=ActivityType.custom, name=activity_message)
)
else: else:
return return
logger.info("Set activity type to %s with message %s", activity_type, activity_message) logger.info("Set activity type to %s with message %s", activity_type, activity_message)
# TODO Implement #11
@Cog.listener()
async def on_member_join(self, member: Member) -> None:
pass
def setup(bot: PycordBot) -> None: def setup(bot: PycordBot) -> None:
bot.add_cog(CogUtility(bot)) bot.add_cog(CogUtility(bot))

View File

@@ -92,6 +92,8 @@ async def autocomplete_event_stages(ctx: AutocompleteContext) -> List[OptionChoi
event_stages: List[OptionChoice] = [] event_stages: List[OptionChoice] = []
async for result in col_stages.find(query).sort([("sequence", ASCENDING)]): async for result in col_stages.find(query).sort([("sequence", ASCENDING)]):
event_stages.append(OptionChoice(f"{result['sequence']+1} ({result['question']})", str(result["_id"]))) event_stages.append(
OptionChoice(f"{result['sequence']+1} ({result['question']})", str(result["_id"]))
)
return event_stages return event_stages

View File

@@ -5,7 +5,7 @@ readme = "README.md"
requires-python = ">=3.11" requires-python = ">=3.11"
[tool.black] [tool.black]
line-length = 118 line-length = 108
target-version = ["py311", "py312", "py313"] target-version = ["py311", "py312", "py313"]
[tool.isort] [tool.isort]