This commit is contained in:
2025-05-02 12:01:23 +02:00
parent 390145ca0e
commit 80eae3f1b1
5 changed files with 165 additions and 19 deletions

View File

@@ -18,6 +18,7 @@ from classes.errors import (
EventStageMissingSequenceError,
EventStageNotFoundError,
GuildNotFoundError,
DiscordGuildMemberNotFoundError,
)
from modules.database import col_events, col_users
from modules.utils import get_logger
@@ -118,9 +119,25 @@ class PycordBot(LibPycordBot):
await user._set(self.cache, current_event_id=event._id, current_stage_id=first_stage._id)
# Create a channel for each participant
user_channel: TextChannel | None = await user.setup_event_channel(
self, guild, pycord_guild, event, cache=self.cache
)
try:
user_channel: TextChannel | None = await user.setup_event_channel(
self, guild, pycord_guild, event, cache=self.cache
)
except DiscordGuildMemberNotFoundError:
logger.error(
"Could not create and configure event channel for user %s in %s (event %s): user not found in the guild",
user.id,
guild.id,
event._id,
)
await self.notify_admins(
guild,
pycord_guild,
f"Event channel could not be created for user with ID `{user.id}` (<@{user.id}>) and event **{event.name}**: user was not found on the server.",
)
continue
if user_channel is None:
logger.error(
@@ -374,3 +391,25 @@ class PycordBot(LibPycordBot):
processed_attachments.append({"id": attachment.id, "filename": attachment.filename})
return processed_attachments
# TODO Add documentation
async def send_stage_question(
self,
channel: TextChannel,
event: PycordEvent,
stage: Optional[PycordEventStage] = None,
use_first_stage: bool = False,
) -> None:
stage: PycordEventStage = (
stage
if not use_first_stage or stage is not None
else await self.find_event_stage(event.stage_ids[0])
)
stage_files: List[File] | None = stage.get_media_files()
question_chunks: List[str] = stage.get_question_chunked(2000)
question_chunks_length: int = len(question_chunks)
for index, chunk in enumerate(question_chunks):
await channel.send(chunk, files=None if index != question_chunks_length - 1 else stage_files)

View File

@@ -275,9 +275,10 @@ class PycordUser:
guild: Guild,
pycord_guild: "PycordGuild",
pycord_event: "PycordEvent",
ignore_exists: bool = False,
cache: Optional[Cache] = None,
) -> TextChannel | None:
if str(pycord_event._id) in self.event_channels.keys():
if not ignore_exists and str(pycord_event._id) in self.event_channels.keys():
return None
discord_member: Member | None = guild.get_member(self.id)
@@ -314,6 +315,43 @@ class PycordUser:
return channel
# TODO Add documentation
async def fix_event_channel(
self,
bot: Bot,
guild: Guild,
pycord_guild: "PycordGuild",
pycord_event: "PycordEvent",
cache: Optional[Cache] = None,
) -> TextChannel | None:
# Configure channel if not set
if str(pycord_event._id) not in self.event_channels.keys():
return await self.setup_event_channel(bot, guild, pycord_guild, pycord_event, cache=cache)
discord_member: Member | None = guild.get_member(self.id)
if discord_member is None:
raise DiscordGuildMemberNotFoundError(self.id, guild.id)
channel: TextChannel = guild.get_channel(self.event_channels[str(pycord_event._id)])
if channel is None:
return await self.setup_event_channel(
bot, guild, pycord_guild, pycord_event, ignore_exists=True, cache=cache
)
await channel.set_permissions(
discord_member,
overwrite=PermissionOverwrite(
view_channel=True,
send_messages=True,
use_application_commands=True,
),
reason=f"Updated event channel of {self.id} for event {pycord_event._id}",
)
return channel
# TODO Add documentation
async def lock_event_channel(
self,

View File

@@ -1,11 +1,10 @@
from datetime import datetime
from logging import Logger
from pathlib import Path
from typing import List
from zoneinfo import ZoneInfo
from bson.errors import InvalidId
from discord import ApplicationContext, Cog, File, TextChannel, option, slash_command
from discord import ApplicationContext, Cog, TextChannel, option, slash_command, File
from discord.utils import basic_autocomplete
from libbot.i18n import _, in_every_locale
@@ -126,15 +125,7 @@ class CogRegister(Cog):
await user.set_event_stage(first_stage._id, cache=self.bot.cache)
first_stage_files: List[File] | None = first_stage.get_media_files()
question_chunks: List[str] = first_stage.get_question_chunked(2000)
question_chunks_length: int = len(question_chunks)
for index, chunk in enumerate(question_chunks):
await user_channel.send(
chunk, files=None if index != question_chunks_length - 1 else first_stage_files
)
await self.bot.send_stage_question(user_channel, pycord_event, first_stage)
def setup(bot: PycordBot) -> None:

View File

@@ -1,8 +1,17 @@
from datetime import datetime
from logging import Logger
from pathlib import Path
from typing import Any, Dict, List
from zoneinfo import ZoneInfo
from discord import Activity, ActivityType, Cog, Member
from bson import ObjectId
from bson.errors import InvalidId
from discord import Activity, ActivityType, Cog, Member, TextChannel, File
from classes import PycordEvent, PycordGuild, PycordUser
from classes.errors import GuildNotFoundError
from classes.pycord_bot import PycordBot
from modules.database import col_users
from modules.utils import get_logger
logger: Logger = get_logger(__name__)
@@ -53,10 +62,78 @@ class CogUtility(Cog):
logger.info("Set activity type to %s with message %s", activity_type, activity_message)
# TODO Implement #11
@Cog.listener()
@Cog.listener("on_member_join")
async def on_member_join(self, member: Member) -> None:
pass
try:
guild: PycordGuild = await self.bot.find_guild(member.guild.id)
except (InvalidId, GuildNotFoundError) as exc:
logger.error(
"Could not process member join event for %s in %s due to: %s",
member.id,
member.guild.id,
exc,
)
return
user: PycordUser = await self.bot.find_user(member.id, member.guild.id)
events: List[PycordEvent] = []
pipeline: List[Dict[str, Any]] = [
{"$match": {"id": user.id}},
{
"$lookup": {
"from": "events",
"localField": "registered_event_ids",
"foreignField": "_id",
"as": "registered_events",
}
},
{
"$match": {
"registered_events.ended": None,
"registered_events.ends": {"$gt": datetime.now(tz=ZoneInfo("UTC"))},
"registered_events.starts": {"$lt": datetime.now(tz=ZoneInfo("UTC"))},
"registered_events.is_cancelled": False,
}
},
]
async for result in col_users.aggregate(pipeline):
for registered_event in result["registered_events"]:
events.append(PycordEvent(**registered_event))
for event in events:
if user.current_event_id is not None and user.current_event_id != event._id:
continue
if user.current_event_id is None:
await user.set_event(event._id, cache=self.bot.cache)
channel: TextChannel | None = await user.fix_event_channel(
self.bot, member.guild, guild, event, cache=self.bot.cache
)
if channel is None:
continue
thumbnail: File | None = (
None
if event.thumbnail is None
else File(Path(f"data/{event.thumbnail['id']}"), event.thumbnail["filename"])
)
await channel.send(
self.bot._("register_already_started", "messages").format(event_name=event.name),
file=thumbnail,
)
stage_id: ObjectId = (
event.stage_ids[0] if user.current_stage_id is None else user.current_stage_id
)
await user.set_event_stage(stage_id, cache=self.bot.cache)
await self.bot.send_stage_question(channel, event, await self.bot.find_event_stage(stage_id))
def setup(bot: PycordBot) -> None:

View File

@@ -50,6 +50,7 @@ async def autocomplete_user_registered_events(ctx: AutocompleteContext) -> List[
"""Return list of active events user is registered in"""
pipeline: List[Dict[str, Any]] = [
{"$match": {"id": ctx.interaction.user.id}},
{
"$lookup": {
"from": "events",