Implemented /event edit and /edit cancel

This commit is contained in:
2025-04-21 22:49:23 +02:00
parent b72f930c94
commit 08d1ca4d33
5 changed files with 286 additions and 120 deletions

View File

@@ -87,9 +87,6 @@ class PycordBot(LibPycordBot):
await super().start(*args, **kwargs) await super().start(*args, **kwargs)
async def close(self, **kwargs) -> None: async def close(self, **kwargs) -> None:
if self.scheduler is not None:
self.scheduler.shutdown()
await super().close(**kwargs) await super().close(**kwargs)
async def find_event(self, event_id: str | ObjectId | None = None, event_name: str | None = None): async def find_event(self, event_id: str | ObjectId | None = None, event_name: str | None = None):
@@ -97,6 +94,6 @@ class PycordBot(LibPycordBot):
raise AttributeError("Either event's ID or name must be provided!") raise AttributeError("Either event's ID or name must be provided!")
if event_id is not None: if event_id is not None:
await PycordEvent.from_id(event_id, cache=self.cache) return await PycordEvent.from_id(event_id, cache=self.cache)
else: else:
await PycordEvent.from_name(event_name, cache=self.cache) return await PycordEvent.from_name(event_name, cache=self.cache)

View File

@@ -1,7 +1,8 @@
from dataclasses import dataclass from dataclasses import dataclass
from datetime import datetime, timezone from datetime import datetime
from logging import Logger from logging import Logger
from typing import Any, Dict, List, Optional from typing import Any, Dict, List, Optional
from zoneinfo import ZoneInfo
from bson import ObjectId from bson import ObjectId
from libbot.cache.classes import Cache from libbot.cache.classes import Cache
@@ -21,6 +22,7 @@ class PycordEvent:
"guild_id", "guild_id",
"created", "created",
"ended", "ended",
"cancelled",
"creator_id", "creator_id",
"starts", "starts",
"ends", "ends",
@@ -35,6 +37,7 @@ class PycordEvent:
guild_id: int guild_id: int
created: datetime created: datetime
ended: datetime | None ended: datetime | None
cancelled: bool
creator_id: int creator_id: int
starts: datetime starts: datetime
ends: datetime ends: datetime
@@ -85,12 +88,14 @@ class PycordEvent:
if db_entry is None: if db_entry is None:
raise RuntimeError(f"Event with name {event_name} not found") raise RuntimeError(f"Event with name {event_name} not found")
# TODO Add a unique exception
# raise EventNotFoundError(event_name)
if cache is not None: if cache is not None:
cache.set_json(f"{cls.__short_name__}_{db_entry['_id']}", db_entry) cache.set_json(f"{cls.__short_name__}_{db_entry['_id']}", db_entry)
return cls(**db_entry) return cls(**db_entry)
# TODO Implement this method
@classmethod @classmethod
async def create( async def create(
cls, cls,
@@ -105,8 +110,9 @@ class PycordEvent:
db_entry: Dict[str, Any] = { db_entry: Dict[str, Any] = {
"name": name, "name": name,
"guild_id": guild_id, "guild_id": guild_id,
"created": datetime.now(tz=timezone.utc), "created": datetime.now(tz=ZoneInfo("UTC")),
"ended": None, "ended": None,
"cancelled": False,
"creator_id": creator_id, "creator_id": creator_id,
"starts": starts, "starts": starts,
"ends": ends, "ends": ends,
@@ -123,7 +129,8 @@ class PycordEvent:
return cls(**db_entry) return cls(**db_entry)
async def _set(self, key: str, value: Any, cache: Optional[Cache] = None) -> None: # TODO Update the docstring
async def _set(self, cache: Optional[Cache] = None, **kwargs) -> None:
"""Set attribute data and save it into the database. """Set attribute data and save it into the database.
Args: Args:
@@ -131,36 +138,43 @@ class PycordEvent:
value (Any): Value to set value (Any): Value to set
cache (:obj:`Cache`, optional): Cache engine to write the update into cache (:obj:`Cache`, optional): Cache engine to write the update into
""" """
for key, value in kwargs.items():
if not hasattr(self, key): if not hasattr(self, key):
raise AttributeError() raise AttributeError()
setattr(self, key, value) setattr(self, key, value)
await self.__collection__.update_one({"_id": self._id}, {"$set": {key: value}}, upsert=True) await self.__collection__.update_one({"_id": self._id}, {"$set": kwargs}, upsert=True)
self._update_cache(cache) self._update_cache(cache)
logger.info("Set attribute '%s' of event %s to '%s'", key, self._id, value) logger.info("Set attributes of event %s to %s", self._id, kwargs)
async def _remove(self, key: str, cache: Optional[Cache] = None) -> None: # TODO Update the docstring
async def _remove(self, cache: Optional[Cache] = None, *args: str) -> None:
"""Remove attribute data and save it into the database. """Remove attribute data and save it into the database.
Args: Args:
key (str): Attribute to remove key (str): Attribute to remove
cache (:obj:`Cache`, optional): Cache engine to write the update into cache (:obj:`Cache`, optional): Cache engine to write the update into
""" """
attributes: Dict[str, Any] = {}
for key in args:
if not hasattr(self, key): if not hasattr(self, key):
raise AttributeError() raise AttributeError()
default_value: Any = PycordEvent.get_default_value(key) default_value: Any = self.get_default_value(key)
setattr(self, key, default_value) setattr(self, key, default_value)
await self.__collection__.update_one({"_id": self._id}, {"$set": {key: default_value}}, upsert=True) attributes[key] = default_value
await self.__collection__.update_one({"_id": self._id}, {"$set": attributes}, upsert=True)
self._update_cache(cache) self._update_cache(cache)
logger.info("Removed attribute '%s' of event %s", key, self._id) logger.info("Reset attributes %s of event %s to default values", args, self._id)
def _get_cache_key(self) -> str: def _get_cache_key(self) -> str:
return f"{self.__short_name__}_{self._id}" return f"{self.__short_name__}_{self._id}"
@@ -197,6 +211,7 @@ class PycordEvent:
"guild_id": self.guild_id, "guild_id": self.guild_id,
"created": self.created, "created": self.created,
"ended": self.ended, "ended": self.ended,
"cancelled": self.cancelled,
"creator_id": self.creator_id, "creator_id": self.creator_id,
"starts": self.starts, "starts": self.starts,
"ends": self.ends, "ends": self.ends,
@@ -211,6 +226,7 @@ class PycordEvent:
"guild_id": None, "guild_id": None,
"created": None, "created": None,
"ended": None, "ended": None,
"cancelled": False,
"creator_id": None, "creator_id": None,
"starts": None, "starts": None,
"ends": None, "ends": None,
@@ -225,6 +241,22 @@ class PycordEvent:
return PycordEvent.get_defaults()[key] return PycordEvent.get_defaults()[key]
# TODO Add documentation
async def update(
self,
cache: Optional[Cache] = None,
**kwargs,
):
await self._set(cache=cache, **kwargs)
# TODO Add documentation
async def reset(
self,
cache: Optional[Cache] = None,
*args,
):
await self._remove(cache, *args)
async def purge(self, cache: Optional[Cache] = None) -> None: async def purge(self, cache: Optional[Cache] = None) -> None:
"""Completely remove event data from database. Currently only removes the event record from events collection. """Completely remove event data from database. Currently only removes the event record from events collection.
@@ -233,3 +265,6 @@ class PycordEvent:
""" """
await self.__collection__.delete_one({"_id": self._id}) await self.__collection__.delete_one({"_id": self._id})
self._delete_cache(cache) self._delete_cache(cache)
async def cancel(self, cache: Optional[Cache] = None):
await self._set(cache, cancelled=True)

View File

@@ -17,15 +17,16 @@ logger: Logger = get_logger(__name__)
class PycordGuild: class PycordGuild:
"""Dataclass of DB entry of a guild""" """Dataclass of DB entry of a guild"""
__slots__ = ("_id", "id", "channel_id", "category_id", "timezone") __slots__ = ("_id", "id", "channel_id", "category_id", "timezone", "language")
__short_name__ = "guild" __short_name__ = "guild"
__collection__ = col_guilds __collection__ = col_guilds
_id: ObjectId _id: ObjectId
id: int id: int
channel_id: Optional[int] channel_id: int | None
category_id: Optional[int] category_id: int | None
timezone: str timezone: str
language: str | None
@classmethod @classmethod
async def from_id( async def from_id(
@@ -67,7 +68,8 @@ class PycordGuild:
return cls(**db_entry) return cls(**db_entry)
async def _set(self, key: str, value: Any, cache: Optional[Cache] = None) -> None: # TODO Update the docstring
async def _set(self, cache: Optional[Cache] = None, **kwargs) -> None:
"""Set attribute data and save it into the database. """Set attribute data and save it into the database.
Args: Args:
@@ -75,36 +77,43 @@ class PycordGuild:
value (Any): Value to set value (Any): Value to set
cache (:obj:`Cache`, optional): Cache engine to write the update into cache (:obj:`Cache`, optional): Cache engine to write the update into
""" """
for key, value in kwargs.items():
if not hasattr(self, key): if not hasattr(self, key):
raise AttributeError() raise AttributeError()
setattr(self, key, value) setattr(self, key, value)
await self.__collection__.update_one({"_id": self._id}, {"$set": {key: value}}, upsert=True) await self.__collection__.update_one({"_id": self._id}, {"$set": kwargs}, upsert=True)
self._update_cache(cache) self._update_cache(cache)
logger.info("Set attribute '%s' of guild %s to '%s'", key, self.id, value) logger.info("Set attributes of guild %s to %s", self.id, kwargs)
async def _remove(self, key: str, cache: Optional[Cache] = None) -> None: # TODO Update the docstring
async def _remove(self, cache: Optional[Cache] = None, *args: str) -> None:
"""Remove attribute data and save it into the database. """Remove attribute data and save it into the database.
Args: Args:
key (str): Attribute to remove key (str): Attribute to remove
cache (:obj:`Cache`, optional): Cache engine to write the update into cache (:obj:`Cache`, optional): Cache engine to write the update into
""" """
attributes: Dict[str, Any] = {}
for key in args:
if not hasattr(self, key): if not hasattr(self, key):
raise AttributeError() raise AttributeError()
default_value: Any = PycordGuild.get_default_value(key) default_value: Any = self.get_default_value(key)
setattr(self, key, default_value) setattr(self, key, default_value)
await self.__collection__.update_one({"_id": self._id}, {"$set": {key: default_value}}, upsert=True) attributes[key] = default_value
await self.__collection__.update_one({"_id": self._id}, {"$set": attributes}, upsert=True)
self._update_cache(cache) self._update_cache(cache)
logger.info("Removed attribute '%s' of guild %s", key, self.id) logger.info("Reset attributes %s of guild %s to default values", args, self.id)
def _get_cache_key(self) -> str: def _get_cache_key(self) -> str:
return f"{self.__short_name__}_{self.id}" return f"{self.__short_name__}_{self.id}"
@@ -141,11 +150,18 @@ class PycordGuild:
"channel_id": self.channel_id, "channel_id": self.channel_id,
"category_id": self.category_id, "category_id": self.category_id,
"timezone": self.timezone, "timezone": self.timezone,
"language": self.language,
} }
@staticmethod @staticmethod
def get_defaults(guild_id: Optional[int] = None) -> Dict[str, Any]: def get_defaults(guild_id: Optional[int] = None) -> Dict[str, Any]:
return {"id": guild_id, "channel_id": None, "category_id": None, "timezone": "UTC"} return {
"id": guild_id,
"channel_id": None,
"category_id": None,
"timezone": "UTC",
"language": None,
}
@staticmethod @staticmethod
def get_default_value(key: str) -> Any: def get_default_value(key: str) -> Any:
@@ -154,6 +170,22 @@ class PycordGuild:
return PycordGuild.get_defaults()[key] return PycordGuild.get_defaults()[key]
# TODO Add documentation
async def update(
self,
cache: Optional[Cache] = None,
**kwargs,
):
await self._set(cache=cache, **kwargs)
# TODO Add documentation
async def reset(
self,
cache: Optional[Cache] = None,
*args,
):
await self._remove(cache, *args)
async def purge(self, cache: Optional[Cache] = None) -> None: async def purge(self, cache: Optional[Cache] = None) -> None:
"""Completely remove guild data from database. Currently only removes the guild record from guilds collection. """Completely remove guild data from database. Currently only removes the guild record from guilds collection.
@@ -161,8 +193,11 @@ class PycordGuild:
cache (:obj:`Cache`, optional): Cache engine to write the update into cache (:obj:`Cache`, optional): Cache engine to write the update into
""" """
await self.__collection__.delete_one({"_id": self._id}) await self.__collection__.delete_one({"_id": self._id})
self._delete_cache(cache) self._delete_cache(cache)
logger.info("Purged guild %s (%s) from the database", self.id, self._id)
# TODO Add documentation # TODO Add documentation
def is_configured(self) -> bool: def is_configured(self) -> bool:
return ( return (
@@ -174,24 +209,32 @@ class PycordGuild:
# TODO Add documentation # TODO Add documentation
async def set_channel(self, channel_id: Optional[int] = None, cache: Optional[Cache] = None) -> None: async def set_channel(self, channel_id: Optional[int] = None, cache: Optional[Cache] = None) -> None:
await self._set("channel_id", channel_id, cache) await self._set(cache, channel_id=channel_id)
# TODO Add documentation # TODO Add documentation
async def reset_channel(self, cache: Optional[Cache] = None) -> None: async def reset_channel(self, cache: Optional[Cache] = None) -> None:
await self._remove("channel_id", cache) await self._remove(cache, "channel_id")
# TODO Add documentation # TODO Add documentation
async def set_category(self, category_id: Optional[int] = None, cache: Optional[Cache] = None) -> None: async def set_category(self, category_id: Optional[int] = None, cache: Optional[Cache] = None) -> None:
await self._set("category_id", category_id, cache) await self._set(cache, category_id=category_id)
# TODO Add documentation # TODO Add documentation
async def reset_category(self, cache: Optional[Cache] = None) -> None: async def reset_category(self, cache: Optional[Cache] = None) -> None:
await self._remove("category_id", cache) await self._remove(cache, "category_id")
# TODO Add documentation # TODO Add documentation
async def set_timezone(self, timezone: str, cache: Optional[Cache] = None) -> None: async def set_timezone(self, timezone: str, cache: Optional[Cache] = None) -> None:
await self._set("timezone", timezone, cache) await self._set(cache, timezone=timezone)
# TODO Add documentation # TODO Add documentation
async def reset_timezone(self, cache: Optional[Cache] = None) -> None: async def reset_timezone(self, cache: Optional[Cache] = None) -> None:
await self._remove("timezone", cache) await self._remove(cache, "timezone")
# TODO Add documentation
async def set_language(self, language: str, cache: Optional[Cache] = None) -> None:
await self._set(cache, language=language)
# TODO Add documentation
async def reset_language(self, cache: Optional[Cache] = None) -> None:
await self._remove(cache, "language")

View File

@@ -1,4 +1,5 @@
from zoneinfo import ZoneInfo, ZoneInfoNotFoundError from typing import List
from zoneinfo import ZoneInfo, ZoneInfoNotFoundError, available_timezones
from discord import ( from discord import (
ApplicationContext, ApplicationContext,
@@ -6,13 +7,27 @@ from discord import (
SlashCommandGroup, SlashCommandGroup,
TextChannel, TextChannel,
option, option,
AutocompleteContext,
) )
from discord.ext.commands import Cog from discord.ext.commands import Cog
from discord.utils import basic_autocomplete
from classes import PycordGuild from classes import PycordGuild
from classes.pycord_bot import PycordBot from classes.pycord_bot import PycordBot
# TODO Move to staticmethod or to a separate module
async def get_timezones(ctx: AutocompleteContext) -> List[str]:
return sorted(list(available_timezones()))
# TODO Move to staticmethod or to a separate module
async def get_languages(ctx: AutocompleteContext) -> List[str]:
# TODO Discord normally uses a different set of locales.
# For example, "en" being "en-US", etc. This will require changes to locale handling later.
return ctx.bot.locales.keys()
class Config(Cog): class Config(Cog):
"""Cog with guild configuration commands.""" """Cog with guild configuration commands."""
@@ -29,9 +44,25 @@ class Config(Cog):
) )
@option("category", description="Category where channels for each user will be created", required=True) @option("category", description="Category where channels for each user will be created", required=True)
@option("channel", description="Text channel for admin notifications", required=True) @option("channel", description="Text channel for admin notifications", required=True)
@option("timezone", description="Timezone in which events take place", required=True) @option(
"timezone",
description="Timezone in which events take place",
autocomplete=basic_autocomplete(get_timezones),
required=True,
)
@option(
"language",
description="Language for bot's messages",
autocomplete=basic_autocomplete(get_languages),
required=True,
)
async def command_config_set( async def command_config_set(
self, ctx: ApplicationContext, category: CategoryChannel, channel: TextChannel, timezone: str self,
ctx: ApplicationContext,
category: CategoryChannel,
channel: TextChannel,
timezone: str,
language: str,
) -> None: ) -> None:
guild: PycordGuild = await self.bot.find_guild(ctx.guild.id) guild: PycordGuild = await self.bot.find_guild(ctx.guild.id)
@@ -41,9 +72,13 @@ class Config(Cog):
await ctx.respond(f"Timezone {timezone} was not found.") await ctx.respond(f"Timezone {timezone} was not found.")
return return
await guild.set_channel(channel.id, cache=self.bot.cache) await guild.update(
await guild.set_category(category.id, cache=self.bot.cache) self.bot.cache,
await guild.set_timezone(str(timezone_parsed), cache=self.bot.cache) channel_id=channel.id,
category_id=category.id,
timezone=str(timezone_parsed),
language=language,
)
# TODO Make a nice message # TODO Make a nice message
await ctx.respond("Okay.") await ctx.respond("Okay.")
@@ -62,9 +97,7 @@ class Config(Cog):
guild: PycordGuild = await self.bot.find_guild(ctx.guild.id) guild: PycordGuild = await self.bot.find_guild(ctx.guild.id)
await guild.reset_channel(cache=self.bot.cache) await guild.purge(self.bot.cache)
await guild.reset_category(cache=self.bot.cache)
await guild.reset_timezone(cache=self.bot.cache)
# TODO Make a nice message # TODO Make a nice message
await ctx.respond("Okay.") await ctx.respond("Okay.")

View File

@@ -3,7 +3,14 @@ from typing import Dict, Any, List
from zoneinfo import ZoneInfo from zoneinfo import ZoneInfo
from bson import ObjectId from bson import ObjectId
from discord import ApplicationContext, Attachment, SlashCommandGroup, option, AutocompleteContext from discord import (
ApplicationContext,
Attachment,
SlashCommandGroup,
option,
AutocompleteContext,
OptionChoice,
)
from discord.ext.commands import Cog from discord.ext.commands import Cog
from discord.utils import basic_autocomplete from discord.utils import basic_autocomplete
@@ -12,13 +19,18 @@ from classes.pycord_bot import PycordBot
from modules.database import col_events from modules.database import col_events
async def get_event(ctx: AutocompleteContext): # TODO Move to staticmethod or to a separate module
query: Dict[str, Any] = {"ended": None, "ends": {"$gt": datetime.now(tz=ZoneInfo("UTC"))}} async def get_event(ctx: AutocompleteContext) -> List[OptionChoice]:
query: Dict[str, Any] = {
"ended": None,
"ends": {"$gt": datetime.now(tz=ZoneInfo("UTC"))},
"cancelled": {"$ne": True},
}
event_names: List[str] = [] event_names: List[OptionChoice] = []
async for result in col_events.find(query): async for result in col_events.find(query):
event_names.append(result["name"]) event_names.append(OptionChoice(result["name"], str(result["_id"])))
return event_names return event_names
@@ -32,18 +44,26 @@ async def validate_event_validity(
event_id: ObjectId | None = None, event_id: ObjectId | None = None,
) -> None: ) -> None:
if start_date > finish_date: if start_date > finish_date:
# TODO Make a nice message
await ctx.respond("Start date must be before finish date") await ctx.respond("Start date must be before finish date")
return return
elif start_date < datetime.now(tz=guild_timezone): elif start_date < datetime.now(tz=guild_timezone):
# TODO Make a nice message
await ctx.respond("Start date must not be in the past") await ctx.respond("Start date must not be in the past")
return return
query: Dict[str, Any] = {"name": name, "ended": None, "ends": {"$gt": datetime.now(tz=ZoneInfo("UTC"))}} query: Dict[str, Any] = {
"name": name,
"ended": None,
"ends": {"$gt": datetime.now(tz=ZoneInfo("UTC"))},
"cancelled": {"$ne": True},
}
if event_id is not None: if event_id is not None:
query["_id"] = {"$ne": event_id} query["_id"] = {"$ne": event_id}
if (await col_events.find_one(query)) is not None: if (await col_events.find_one(query)) is not None:
# TODO Make a nice message
await ctx.respond("There can only be one active event with the same name") await ctx.respond("There can only be one active event with the same name")
return return
@@ -57,26 +77,27 @@ class Event(Cog):
# TODO Introduce i18n # TODO Introduce i18n
command_group: SlashCommandGroup = SlashCommandGroup("event", "Event management") command_group: SlashCommandGroup = SlashCommandGroup("event", "Event management")
# TODO Implement the command # TODO Introduce i18n
@command_group.command( @command_group.command(
name="create", name="create",
description="Create new event", description="Create new event",
) )
@option("name", description="Name of the event", required=True) @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("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("end", description="Date when the event ends (DD.MM.YYYY HH:MM)", required=True)
@option("thumbnail", description="Thumbnail of the event", required=False) @option("thumbnail", description="Thumbnail of the event", required=False)
async def command_event_create( async def command_event_create(
self, self,
ctx: ApplicationContext, ctx: ApplicationContext,
name: str, name: str,
start: str, start: str,
finish: str, end: str,
thumbnail: Attachment = None, thumbnail: Attachment = None,
) -> None: ) -> None:
guild: PycordGuild = await self.bot.find_guild(ctx.guild.id) guild: PycordGuild = await self.bot.find_guild(ctx.guild.id)
if not guild.is_configured(): if not guild.is_configured():
# TODO Make a nice message
await ctx.respond("Guild is not configured.") await ctx.respond("Guild is not configured.")
return return
@@ -84,28 +105,30 @@ class Event(Cog):
try: try:
start_date: datetime = datetime.strptime(start, "%d.%m.%Y %H:%M") start_date: datetime = datetime.strptime(start, "%d.%m.%Y %H:%M")
finish_date: datetime = datetime.strptime(finish, "%d.%m.%Y %H:%M") end_date: datetime = datetime.strptime(end, "%d.%m.%Y %H:%M")
start_date = start_date.replace(tzinfo=guild_timezone) start_date = start_date.replace(tzinfo=guild_timezone)
finish_date = finish_date.replace(tzinfo=guild_timezone) end_date = end_date.replace(tzinfo=guild_timezone)
except ValueError: except ValueError:
await ctx.respond("Could not parse start and finish dates.") # TODO Make a nice message
await ctx.respond("Could not parse start and end dates.")
return return
await validate_event_validity(ctx, name, start_date, finish_date, guild_timezone) await validate_event_validity(ctx, name, start_date, end_date, guild_timezone)
event: PycordEvent = await self.bot.create_event( event: PycordEvent = await self.bot.create_event(
name=name, name=name,
guild_id=guild.id, guild_id=guild.id,
creator_id=ctx.author.id, creator_id=ctx.author.id,
starts=start_date.astimezone(ZoneInfo("UTC")), starts=start_date.astimezone(ZoneInfo("UTC")),
ends=finish_date.astimezone(ZoneInfo("UTC")), ends=end_date.astimezone(ZoneInfo("UTC")),
thumbnail_id=thumbnail.id if thumbnail else None, thumbnail_id=thumbnail.id if thumbnail else None,
) )
# TODO Make a nice message
await ctx.respond("Event has been created.") await ctx.respond("Event has been created.")
# TODO Implement the command # TODO Introduce i18n
@command_group.command( @command_group.command(
name="edit", name="edit",
description="Edit event", description="Edit event",
@@ -115,7 +138,7 @@ class Event(Cog):
) )
@option("name", description="New name of the event", required=False) @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("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("end", description="Date when the event ends (DD.MM.YYYY HH:MM)", required=False)
@option("thumbnail", description="Thumbnail of the event", required=False) @option("thumbnail", description="Thumbnail of the event", required=False)
async def command_event_edit( async def command_event_edit(
self, self,
@@ -123,13 +146,15 @@ class Event(Cog):
event: str, event: str,
name: str = None, name: str = None,
start: str = None, start: str = None,
finish: str = None, end: str = None,
thumbnail: Attachment = None, thumbnail: Attachment = None,
) -> None: ) -> None:
guild: PycordGuild = await self.bot.find_guild(ctx.guild.id) guild: PycordGuild = await self.bot.find_guild(ctx.guild.id)
event: PycordEvent = await self.bot.find_event( pycord_event: PycordEvent = await self.bot.find_event(event_id=event)
event_name=name
) if pycord_event is None:
await ctx.respond("Event was not found.")
return
if not guild.is_configured(): if not guild.is_configured():
await ctx.respond("Guild is not configured.") await ctx.respond("Guild is not configured.")
@@ -137,42 +162,75 @@ class Event(Cog):
guild_timezone: ZoneInfo = ZoneInfo(guild.timezone) guild_timezone: ZoneInfo = ZoneInfo(guild.timezone)
if start is not None:
try: try:
start_date: datetime = datetime.strptime(start, "%d.%m.%Y %H:%M") 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) start_date = start_date.replace(tzinfo=guild_timezone)
await event.set_start_date(start_date)
except ValueError: except ValueError:
await ctx.respond("Could not parse the start date.") await ctx.respond("Could not parse the start date.")
return return
if finish is not None:
try: try:
finish_date: datetime = datetime.strptime(finish, "%d.%m.%Y %H:%M") end_date: datetime = (
finish_date = finish_date.replace(tzinfo=guild_timezone) pycord_event.ends if end is None else datetime.strptime(end, "%d.%m.%Y %H:%M")
)
await event.set_end_date(finish_date) end_date = end_date.replace(tzinfo=guild_timezone)
except ValueError: except ValueError:
await ctx.respond("Could not parse the finish date.") await ctx.respond("Could not parse the end date.")
return return
await validate_event_validity(ctx, name, start_date, finish_date, guild_timezone) await validate_event_validity(ctx, name, start_date, end_date, guild_timezone)
await pycord_event.update(
self.bot.cache,
starts=start_date,
ends=end_date,
name=pycord_event.name if name is None else name,
thumbnail_id=pycord_event.thumbnail_id if thumbnail is None else thumbnail.id,
)
await ctx.respond("Event has been updated.")
# TODO Implement the command # TODO Implement the command
@command_group.command( @command_group.command(
name="cancel", name="cancel",
description="Cancel event", description="Cancel event",
) )
@option("name", description="Name of the event", required=True) @option(
"event", description="Name of the event", autocomplete=basic_autocomplete(get_event), required=True
)
async def command_event_cancel( async def command_event_cancel(
self, self,
ctx: ApplicationContext, ctx: ApplicationContext,
name: str, event: str,
) -> None: ) -> None:
guild: PycordGuild = await self.bot.find_guild(ctx.guild.id) guild: PycordGuild = await self.bot.find_guild(ctx.guild.id)
pycord_event: PycordEvent = await self.bot.find_event(event_id=event)
await ctx.respond("Not implemented.") if pycord_event is None:
await ctx.respond("Event was not found.")
return
if not guild.is_configured():
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"))
):
await ctx.respond("Finished or ongoing events cannot be cancelled.")
return
await pycord_event.cancel()
await ctx.respond("Event was cancelled.")
def setup(bot: PycordBot) -> None: def setup(bot: PycordBot) -> None: