Fixed /config show, added /event show, added stubs for /stage and /guess

This commit is contained in:
2025-04-22 01:16:30 +02:00
parent 08d1ca4d33
commit f2a2c3d85f
5 changed files with 192 additions and 41 deletions

View File

@@ -1,5 +1,4 @@
from typing import List from zoneinfo import ZoneInfo, ZoneInfoNotFoundError
from zoneinfo import ZoneInfo, ZoneInfoNotFoundError, available_timezones
from discord import ( from discord import (
ApplicationContext, ApplicationContext,
@@ -7,25 +6,13 @@ 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 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
from modules.utils import autofill_timezones, autofill_languages
# 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):
@@ -47,13 +34,13 @@ class Config(Cog):
@option( @option(
"timezone", "timezone",
description="Timezone in which events take place", description="Timezone in which events take place",
autocomplete=basic_autocomplete(get_timezones), autocomplete=basic_autocomplete(autofill_timezones),
required=True, required=True,
) )
@option( @option(
"language", "language",
description="Language for bot's messages", description="Language for bot's messages",
autocomplete=basic_autocomplete(get_languages), autocomplete=basic_autocomplete(autofill_languages),
required=True, required=True,
) )
async def command_config_set( async def command_config_set(
@@ -107,12 +94,12 @@ class Config(Cog):
name="show", name="show",
description="Show the guild's configuration", description="Show the guild's configuration",
) )
async def command_config_reset(self, ctx: ApplicationContext) -> None: async def command_config_show(self, ctx: ApplicationContext) -> None:
guild: PycordGuild = await self.bot.find_guild(ctx.guild.id) guild: PycordGuild = await self.bot.find_guild(ctx.guild.id)
# TODO Make a nice message # TODO Make a nice message
await ctx.respond( await ctx.respond(
f"**Guild config**\n\nChannel: <#{guild.channel_id}>\nCategory: <#{guild.category_id}>" f"**Guild config**\n\nChannel: <#{guild.channel_id}>\nCategory: <#{guild.category_id}>\nTimezone: {guild.timezone}\nLanguage: {guild.language}"
) )

View File

@@ -1,5 +1,5 @@
from datetime import datetime from datetime import datetime
from typing import Dict, Any, List from typing import Dict, Any
from zoneinfo import ZoneInfo from zoneinfo import ZoneInfo
from bson import ObjectId from bson import ObjectId
@@ -8,8 +8,6 @@ from discord import (
Attachment, Attachment,
SlashCommandGroup, SlashCommandGroup,
option, 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
@@ -17,24 +15,10 @@ from discord.utils import basic_autocomplete
from classes import PycordEvent, PycordGuild from classes import PycordEvent, PycordGuild
from classes.pycord_bot import PycordBot from classes.pycord_bot import PycordBot
from modules.database import col_events from modules.database import col_events
from modules.utils import autofill_active_events
# TODO Move to staticmethod or to a separate module # TODO Move to staticmethod or to a separate module
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[OptionChoice] = []
async for result in col_events.find(query):
event_names.append(OptionChoice(result["name"], str(result["_id"])))
return event_names
async def validate_event_validity( async def validate_event_validity(
ctx: ApplicationContext, ctx: ApplicationContext,
name: str, name: str,
@@ -134,7 +118,10 @@ class Event(Cog):
description="Edit event", description="Edit event",
) )
@option( @option(
"event", description="Name of the event", autocomplete=basic_autocomplete(get_event), required=True "event",
description="Name of the event",
autocomplete=basic_autocomplete(autofill_active_events),
required=True,
) )
@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)
@@ -153,10 +140,12 @@ class Event(Cog):
pycord_event: PycordEvent = await self.bot.find_event(event_id=event) pycord_event: PycordEvent = await self.bot.find_event(event_id=event)
if pycord_event is None: if pycord_event is None:
# TODO Make a nice message
await ctx.respond("Event was not found.") await ctx.respond("Event was not found.")
return return
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
@@ -168,6 +157,7 @@ class Event(Cog):
) )
start_date = start_date.replace(tzinfo=guild_timezone) start_date = start_date.replace(tzinfo=guild_timezone)
except ValueError: except ValueError:
# TODO Make a nice message
await ctx.respond("Could not parse the start date.") await ctx.respond("Could not parse the start date.")
return return
@@ -177,6 +167,7 @@ class Event(Cog):
) )
end_date = end_date.replace(tzinfo=guild_timezone) end_date = end_date.replace(tzinfo=guild_timezone)
except ValueError: except ValueError:
# TODO Make a nice message
await ctx.respond("Could not parse the end date.") await ctx.respond("Could not parse the end date.")
return return
@@ -190,15 +181,19 @@ class Event(Cog):
thumbnail_id=pycord_event.thumbnail_id if thumbnail is None else thumbnail.id, thumbnail_id=pycord_event.thumbnail_id if thumbnail is None else thumbnail.id,
) )
# TODO Make a nice message
await ctx.respond("Event has been updated.") await ctx.respond("Event has been updated.")
# TODO Implement the command # TODO Introduce i18n
@command_group.command( @command_group.command(
name="cancel", name="cancel",
description="Cancel event", description="Cancel event",
) )
@option( @option(
"event", description="Name of the event", autocomplete=basic_autocomplete(get_event), required=True "event",
description="Name of the event",
autocomplete=basic_autocomplete(autofill_active_events),
required=True,
) )
async def command_event_cancel( async def command_event_cancel(
self, self,
@@ -209,10 +204,12 @@ class Event(Cog):
pycord_event: PycordEvent = await self.bot.find_event(event_id=event) pycord_event: PycordEvent = await self.bot.find_event(event_id=event)
if pycord_event is None: if pycord_event is None:
# TODO Make a nice message
await ctx.respond("Event was not found.") await ctx.respond("Event was not found.")
return return
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
@@ -225,13 +222,43 @@ class Event(Cog):
or end_date <= datetime.now(tz=ZoneInfo("UTC")) or end_date <= datetime.now(tz=ZoneInfo("UTC"))
or start_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.") await ctx.respond("Finished or ongoing events cannot be cancelled.")
return return
await pycord_event.cancel() await pycord_event.cancel()
# TODO Make a nice message
await ctx.respond("Event was cancelled.") 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(autofill_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)
pycord_event: PycordEvent = await self.bot.find_event(event_id=event)
if pycord_event is None:
# 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: <t:{int(starts_date.timestamp())}>\nEnds: <t:{int(ends_date.timestamp())}>"
)
def setup(bot: PycordBot) -> None: def setup(bot: PycordBot) -> None:
bot.add_cog(Event(bot)) bot.add_cog(Event(bot))

23
cogs/guess.py Normal file
View File

@@ -0,0 +1,23 @@
from discord import Cog, slash_command, option, ApplicationContext
from classes.pycord_bot import PycordBot
class Guess(Cog):
"""Cog with the guessing command."""
def __init__(self, bot: PycordBot):
self.bot: PycordBot = bot
# TODO Implement the command
@slash_command(
name="guess",
description="Propose an answer to the current event stage",
)
@option("answer", description="An answer to the current stage")
async def command_guess(self, ctx: ApplicationContext, answer: str) -> None:
await ctx.respond("Not implemented.")
def setup(bot: PycordBot) -> None:
bot.add_cog(Guess(bot))

View File

@@ -1,6 +1,9 @@
from discord import SlashCommandGroup, option, ApplicationContext, Attachment
from discord.ext.commands import Cog from discord.ext.commands import Cog
from discord.utils import basic_autocomplete
from classes.pycord_bot import PycordBot from classes.pycord_bot import PycordBot
from modules.utils import autofill_active_events
class Stage(Cog): class Stage(Cog):
@@ -9,7 +12,81 @@ class Stage(Cog):
def __init__(self, bot: PycordBot): def __init__(self, bot: PycordBot):
self.bot: PycordBot = bot self.bot: PycordBot = bot
# command_group: SlashCommandGroup = SlashCommandGroup("stage", "Event stage management") command_group: SlashCommandGroup = SlashCommandGroup("stage", "Event stage management")
# TODO Implement the command
# /stage add <event> <question> <answer> <media>
# TODO Maybe add an option for order?
@command_group.command(
name="add",
description="Add new event stage",
)
@option(
"event",
description="Name of the event",
autocomplete=basic_autocomplete(autofill_active_events),
required=True,
)
@option("question", description="Question to be answered", required=True)
@option("answer", description="Answer to the stage's question", required=True)
@option("media", description="Media file to be attached", required=False)
async def command_stage_add(
self,
ctx: ApplicationContext,
event: str,
question: str,
answer: str,
media: Attachment = None,
) -> None:
await ctx.respond("Not implemented.")
# TODO Implement the command
# /stage edit <event> <stage> <order> <question> <answer> <media>
@command_group.command(
name="edit",
description="Edit the event stage",
)
@option(
"event",
description="Name of the event",
autocomplete=basic_autocomplete(autofill_active_events),
required=True,
)
@option("stage", description="Stage to edit", required=True)
@option("order", description="Number in the event stages' order", required=False)
@option("question", description="Question to be answered", required=False)
@option("answer", description="Answer to the stage's question", required=False)
@option("media", description="Media file to be attached", required=False)
async def command_stage_edit(
self,
ctx: ApplicationContext,
event: str,
stage: str,
order: int,
question: str,
answer: str,
media: Attachment = None,
) -> None:
await ctx.respond("Not implemented.")
# TODO Implement the command
# /stage delete <event> <stage> <confirm>
@command_group.command(
name="delete",
description="Delete the event stage",
)
@option(
"event",
description="Name of the event",
autocomplete=basic_autocomplete(autofill_active_events),
required=True,
)
@option("stage", description="Stage to edit", required=True)
@option("confirm", description="Confirmation of the operation", required=False)
async def command_stage_delete(
self, ctx: ApplicationContext, event: str, stage: str, confirm: bool = False
) -> None:
await ctx.respond("Not implemented.")
def setup(bot: PycordBot) -> None: def setup(bot: PycordBot) -> None:

View File

@@ -1,6 +1,43 @@
from datetime import datetime
from typing import List, Dict, Any
from zoneinfo import ZoneInfo, available_timezones
from discord import AutocompleteContext, OptionChoice
from modules.database import col_events
def hex_to_int(hex_color: str) -> int: def hex_to_int(hex_color: str) -> int:
return int(hex_color.lstrip("#"), 16) return int(hex_color.lstrip("#"), 16)
def int_to_hex(integer_color: int) -> str: def int_to_hex(integer_color: int) -> str:
return "#" + format(integer_color, "06x") return "#" + format(integer_color, "06x")
# TODO Maybe move to a separate module
async def autofill_timezones(ctx: AutocompleteContext) -> List[str]:
return sorted(list(available_timezones()))
# TODO Maybe move to a separate module
async def autofill_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()
# TODO Maybe move to a separate module
async def autofill_active_events(ctx: AutocompleteContext) -> List[OptionChoice]:
query: Dict[str, Any] = {
"ended": None,
"ends": {"$gt": datetime.now(tz=ZoneInfo("UTC"))},
"cancelled": {"$ne": True},
}
event_names: List[OptionChoice] = []
async for result in col_events.find(query):
event_names.append(OptionChoice(result["name"], str(result["_id"])))
return event_names