from datetime import datetime from typing import Dict, List, Any from zoneinfo import ZoneInfo from bson.errors import InvalidId from discord import ( ApplicationContext, Attachment, SlashCommandGroup, option, ) 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.utils import autocomplete_active_events, validate_event_validity 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 Introduce i18n @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("end", description="Date when the event ends (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, end: str, thumbnail: Attachment = None, ) -> None: guild: PycordGuild = await self.bot.find_guild(ctx.guild.id) if not guild.is_configured(): # TODO Make a nice message 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") end_date: datetime = datetime.strptime(end, "%d.%m.%Y %H:%M") start_date = start_date.replace(tzinfo=guild_timezone) end_date = end_date.replace(tzinfo=guild_timezone) except ValueError: # TODO Make a nice message await ctx.respond("Could not parse start and end dates.") return await validate_event_validity(ctx, name, start_date, end_date, guild_timezone) processed_media: List[Dict[str, Any]] = ( [] if thumbnail is None else await self.bot.process_attachments([thumbnail]) ) 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=end_date.astimezone(ZoneInfo("UTC")), thumbnail=processed_media[0] if thumbnail else None, ) # TODO Make a nice message await ctx.respond("Event has been created.") # TODO Introduce i18n @command_group.command( name="edit", description="Edit event", ) @option( "event", description="Name of the event", autocomplete=basic_autocomplete(autocomplete_active_events), 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("end", description="Date when the event ends (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, end: str = None, thumbnail: Attachment = None, ) -> None: guild: PycordGuild = await self.bot.find_guild(ctx.guild.id) try: pycord_event: PycordEvent = await self.bot.find_event(event_id=event) except (InvalidId, RuntimeError): # TODO Make a nice message await ctx.respond("Event was not found.") return if not guild.is_configured(): # TODO Make a nice message await ctx.respond("Guild is not configured.") return guild_timezone: ZoneInfo = ZoneInfo(guild.timezone) try: start_date: datetime = ( pycord_event.starts if start is None else datetime.strptime(start, "%d.%m.%Y %H:%M") ) start_date = start_date.replace(tzinfo=guild_timezone) except ValueError: # TODO Make a nice message await ctx.respond("Could not parse the start date.") return try: end_date: datetime = ( pycord_event.ends if end is None else datetime.strptime(end, "%d.%m.%Y %H:%M") ) end_date = end_date.replace(tzinfo=guild_timezone) except ValueError: # TODO Make a nice message await ctx.respond("Could not parse the end date.") return await validate_event_validity(ctx, name, start_date, end_date, guild_timezone) processed_media: List[Dict[str, Any]] = ( [] if thumbnail is None else await self.bot.process_attachments([thumbnail]) ) await pycord_event.update( self.bot.cache, starts=start_date, ends=end_date, name=pycord_event.name if name is None else name, thumbnail=pycord_event.thumbnail if thumbnail is None else processed_media[0], ) # TODO Make a nice message await ctx.respond("Event has been updated.") # TODO Introduce i18n @command_group.command( name="cancel", description="Cancel event", ) @option( "event", description="Name of the event", autocomplete=basic_autocomplete(autocomplete_active_events), required=True, ) @option("confirm", description="Confirmation of the operation", required=False) async def command_event_cancel( self, ctx: ApplicationContext, event: str, confirm: bool = False, ) -> None: if confirm is None or not confirm: # TODO Make a nice message await ctx.respond("Operation not confirmed.") return guild: PycordGuild = await self.bot.find_guild(ctx.guild.id) try: pycord_event: PycordEvent = await self.bot.find_event(event_id=event) except (InvalidId, RuntimeError): # TODO Make a nice message await ctx.respond("Event was not found.") return if not guild.is_configured(): # TODO Make a nice message await ctx.respond("Guild is not configured.") return start_date: datetime = pycord_event.starts.replace(tzinfo=ZoneInfo("UTC")) end_date: datetime = pycord_event.ends.replace(tzinfo=ZoneInfo("UTC")) # TODO Make ongoing events cancellable if ( pycord_event.ended is not None or end_date <= datetime.now(tz=ZoneInfo("UTC")) or start_date <= datetime.now(tz=ZoneInfo("UTC")) ): # TODO Make a nice message await ctx.respond("Finished or ongoing events cannot be cancelled.") return await pycord_event.cancel() # TODO Make a nice message await ctx.respond("Event was cancelled.") # TODO Introduce i18n @command_group.command( name="show", description="Show the details about certain event", ) @option( "event", description="Name of the event", autocomplete=basic_autocomplete(autocomplete_active_events), required=True, ) async def command_event_show(self, ctx: ApplicationContext, event: str) -> None: guild: PycordGuild = await self.bot.find_guild(ctx.guild.id) try: pycord_event: PycordEvent = await self.bot.find_event(event_id=event) except (InvalidId, RuntimeError): # TODO Make a nice message await ctx.respond("Event was not found.") return starts_date: datetime = pycord_event.starts.replace(tzinfo=ZoneInfo("UTC")) ends_date: datetime = pycord_event.ends.replace(tzinfo=ZoneInfo("UTC")) # TODO Make a nice message await ctx.respond( f"**Event details**\n\nName: {pycord_event.name}\nStarts: \nEnds: " ) def setup(bot: PycordBot) -> None: bot.add_cog(Event(bot))