v1.0.0 #15

Merged
profitroll merged 53 commits from dev into main 2025-05-06 21:34:32 +03:00
14 changed files with 101 additions and 44 deletions
Showing only changes of commit 11f0cc384a - Show all commits

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

@@ -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

@@ -25,17 +25,29 @@ 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

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]