from datetime import datetime from typing import Dict, Any, List from zoneinfo import ZoneInfo from bson import ObjectId from discord import ApplicationContext, Attachment, SlashCommandGroup, option, AutocompleteContext from discord.ext.commands import Cog from discord.utils import basic_autocomplete from classes import PycordEvent, PycordGuild from classes.pycord_bot import PycordBot from modules.database import col_events async def get_event(ctx: AutocompleteContext): query: Dict[str, Any] = {"ended": None, "ends": {"$gt": datetime.now(tz=ZoneInfo("UTC"))}} event_names: List[str] = [] async for result in col_events.find(query): event_names.append(result["name"]) return event_names async def validate_event_validity( ctx: ApplicationContext, name: str, start_date: datetime | None, finish_date: datetime | None, guild_timezone: ZoneInfo, event_id: ObjectId | None = None, ) -> None: if start_date > finish_date: await ctx.respond("Start date must be before finish date") return elif start_date < datetime.now(tz=guild_timezone): await ctx.respond("Start date must not be in the past") return query: Dict[str, Any] = {"name": name, "ended": None, "ends": {"$gt": datetime.now(tz=ZoneInfo("UTC"))}} if event_id is not None: query["_id"] = {"$ne": event_id} if (await col_events.find_one(query)) is not None: await ctx.respond("There can only be one active event with the same name") return class Event(Cog): """Cog with event management commands.""" def __init__(self, bot: PycordBot): self.bot: PycordBot = bot # TODO Introduce i18n command_group: SlashCommandGroup = SlashCommandGroup("event", "Event management") # TODO Implement the command @command_group.command( name="create", description="Create new event", ) @option("name", description="Name of the event", required=True) @option("start", description="Date when the event starts (DD.MM.YYYY HH:MM)", required=True) @option("finish", description="Date when the event finishes (DD.MM.YYYY HH:MM)", required=True) @option("thumbnail", description="Thumbnail of the event", required=False) async def command_event_create( self, ctx: ApplicationContext, name: str, start: str, finish: str, thumbnail: Attachment = None, ) -> None: guild: PycordGuild = await self.bot.find_guild(ctx.guild.id) if not guild.is_configured(): await ctx.respond("Guild is not configured.") return guild_timezone: ZoneInfo = ZoneInfo(guild.timezone) try: start_date: datetime = datetime.strptime(start, "%d.%m.%Y %H:%M") finish_date: datetime = datetime.strptime(finish, "%d.%m.%Y %H:%M") start_date = start_date.replace(tzinfo=guild_timezone) finish_date = finish_date.replace(tzinfo=guild_timezone) except ValueError: await ctx.respond("Could not parse start and finish dates.") return await validate_event_validity(ctx, name, start_date, finish_date, guild_timezone) event: PycordEvent = await self.bot.create_event( name=name, guild_id=guild.id, creator_id=ctx.author.id, starts=start_date.astimezone(ZoneInfo("UTC")), ends=finish_date.astimezone(ZoneInfo("UTC")), thumbnail_id=thumbnail.id if thumbnail else None, ) await ctx.respond("Event has been created.") # TODO Implement the command @command_group.command( name="edit", description="Edit event", ) @option( "event", description="Name of the event", autocomplete=basic_autocomplete(get_event), required=True ) @option("name", description="New name of the event", required=False) @option("start", description="Date when the event starts (DD.MM.YYYY HH:MM)", required=False) @option("finish", description="Date when the event finishes (DD.MM.YYYY HH:MM)", required=False) @option("thumbnail", description="Thumbnail of the event", required=False) async def command_event_edit( self, ctx: ApplicationContext, event: str, name: str = None, start: str = None, finish: str = None, thumbnail: Attachment = None, ) -> None: guild: PycordGuild = await self.bot.find_guild(ctx.guild.id) event: PycordEvent = await self.bot.find_event( event_name=name ) if not guild.is_configured(): await ctx.respond("Guild is not configured.") return guild_timezone: ZoneInfo = ZoneInfo(guild.timezone) if start is not None: try: start_date: datetime = datetime.strptime(start, "%d.%m.%Y %H:%M") start_date = start_date.replace(tzinfo=guild_timezone) await event.set_start_date(start_date) except ValueError: await ctx.respond("Could not parse the start date.") return if finish is not None: try: finish_date: datetime = datetime.strptime(finish, "%d.%m.%Y %H:%M") finish_date = finish_date.replace(tzinfo=guild_timezone) await event.set_end_date(finish_date) except ValueError: await ctx.respond("Could not parse the finish date.") return await validate_event_validity(ctx, name, start_date, finish_date, guild_timezone) # TODO Implement the command @command_group.command( name="cancel", description="Cancel event", ) @option("name", description="Name of the event", required=True) async def command_event_cancel( self, ctx: ApplicationContext, name: str, ) -> None: guild: PycordGuild = await self.bot.find_guild(ctx.guild.id) await ctx.respond("Not implemented.") def setup(bot: PycordBot) -> None: bot.add_cog(Event(bot))