Files
QuizBot/cogs/cog_user.py

269 lines
9.4 KiB
Python

from datetime import datetime
from logging import Logger
from pathlib import Path
from typing import Any, Dict, List
from zoneinfo import ZoneInfo
from bson import ObjectId
from bson.errors import InvalidId
from discord import (
ApplicationContext,
File,
SlashCommandGroup,
TextChannel,
User,
option,
)
from discord.ext.commands import Cog
from libbot.i18n import _, in_every_locale
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, is_operation_confirmed, get_utc_now
logger: Logger = get_logger(__name__)
class CogUser(Cog):
"""Cog with user management commands."""
def __init__(self, bot: PycordBot):
self.bot: PycordBot = bot
command_group: SlashCommandGroup = SlashCommandGroup(
"user",
description=_("description", "commands", "user"),
description_localizations=in_every_locale("description", "commands", "user"),
)
@command_group.command(
name="update_channels",
description=_("description", "commands", "user_update_channels"),
description_localizations=in_every_locale("description", "commands", "user_update_channels"),
)
@option(
"user",
description=_("description", "commands", "user_update_channels", "options", "user"),
description_localizations=in_every_locale(
"description", "commands", "user_update_channels", "options", "user"
),
)
async def command_user_update_channels(self, ctx: ApplicationContext, user: User) -> None:
try:
guild: PycordGuild = await self.bot.find_guild(ctx.guild.id)
except (InvalidId, GuildNotFoundError):
await ctx.respond(self.bot._("unexpected_error", "messages", locale=ctx.locale), ephemeral=True)
return
pycord_user: PycordUser = await self.bot.find_user(user.id, ctx.guild.id)
events: List[PycordEvent] = []
utc_now: datetime = get_utc_now()
pipeline: List[Dict[str, Any]] = [
{"$match": {"id": pycord_user.id}},
{
"$lookup": {
"from": "events",
"let": {"event_ids": "$registered_event_ids"},
"pipeline": [
{
"$match": {
"$expr": {
"$and": [
{"$in": ["$_id", "$$event_ids"]},
{"$eq": ["$ended", None]},
{"$gt": ["$ends", utc_now]},
{"$lt": ["$starts", utc_now]},
{"$eq": ["$is_cancelled", False]},
]
}
}
}
],
"as": "registered_events",
}
},
{"$match": {"registered_events.0": {"$exists": True}}},
]
async with await col_users.aggregate(pipeline) as cursor:
async for result in cursor:
for registered_event in result["registered_events"]:
events.append(PycordEvent(**registered_event))
for event in events:
if pycord_user.current_event_id is not None and pycord_user.current_event_id != event._id:
continue
if pycord_user.current_event_id is None:
await pycord_user.set_event(event._id, cache=self.bot.cache)
channel: TextChannel | None = await pycord_user.fix_event_channel(
self.bot, ctx.guild, guild, event, cache=self.bot.cache
)
try:
await self.bot.notify_admins(
ctx.guild,
guild,
self.bot._("admin_user_channel_fixed", "messages", locale=ctx.locale).format(
display_name=user.display_name, mention=user.mention, event_name=event.name
),
)
except Exception as exc:
logger.error(
"Could not notify admins that user %s got their event channel for %s fixed due to: %s",
user.id,
event._id,
exc,
exc_info=exc,
)
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._("notice_event_already_started", "messages").format(event_name=event.name),
file=thumbnail,
)
stage_id: ObjectId = (
event.stage_ids[0] if pycord_user.current_stage_id is None else pycord_user.current_stage_id
)
await pycord_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))
await ctx.respond(
self.bot._("user_channels_updated", "messages", locale=ctx.locale).format(
display_name=user.display_name
)
)
# TODO Implement the command
# @command_group.command(
# name="create_channel",
# description="Create channel for the user",
# )
# @option(
# "user",
# description="Selected user",
# )
# async def command_user_create_channel(self, ctx: ApplicationContext, user: User) -> None:
# await ctx.respond("Not implemented.")
# TODO Implement the command
# @command_group.command(
# name="delete_channel",
# description="Delete user's channel",
# )
# @option(
# "user",
# description="Selected user",
# )
# @option("confirm", description="Confirmation of the operation", required=False)
# async def command_user_delete_channel(
# self, ctx: ApplicationContext, user: User, confirm: bool = False
# ) -> None:
# await ctx.respond("Not implemented.")
@command_group.command(
name="jail",
description=_("description", "commands", "user_jail"),
description_localizations=in_every_locale("description", "commands", "user_jail"),
)
@option(
"user",
description=_("description", "commands", "user_jail", "options", "user"),
description_localizations=in_every_locale(
"description", "commands", "user_jail", "options", "user"
),
)
@option(
"confirm",
description=_("description", "commands", "user_jail", "options", "confirm"),
description_localizations=in_every_locale(
"description", "commands", "user_jail", "options", "confirm"
),
required=False,
)
async def command_user_jail(self, ctx: ApplicationContext, user: User, confirm: bool = False) -> None:
if not (await is_operation_confirmed(ctx, confirm)):
return
pycord_user: PycordUser = await self.bot.find_user(user, ctx.guild)
if pycord_user.is_jailed:
await ctx.respond(
self.bot._("user_jail_already_jailed", "messages", locale=ctx.locale).format(
display_name=user.display_name
),
ephemeral=True,
)
return
await pycord_user.jail(self.bot.cache)
await ctx.respond(
self.bot._("user_jail_successful", "messages", locale=ctx.locale).format(
display_name=user.display_name
)
)
@command_group.command(
name="unjail",
description=_("description", "commands", "user_unjail"),
description_localizations=in_every_locale("description", "commands", "user_unjail"),
)
@option(
"user",
description=_("description", "commands", "user_unjail", "options", "user"),
description_localizations=in_every_locale(
"description", "commands", "user_unjail", "options", "user"
),
)
@option(
"confirm",
description=_("description", "commands", "user_unjail", "options", "confirm"),
description_localizations=in_every_locale(
"description", "commands", "user_unjail", "options", "confirm"
),
required=False,
)
async def command_user_unjail(self, ctx: ApplicationContext, user: User, confirm: bool = False) -> None:
if not (await is_operation_confirmed(ctx, confirm)):
return
pycord_user: PycordUser = await self.bot.find_user(user, ctx.guild)
if not pycord_user.is_jailed:
await ctx.respond(
self.bot._("user_unjail_not_jailed", "messages", locale=ctx.locale).format(
display_name=user.display_name
),
ephemeral=True,
)
return
await pycord_user.unjail(self.bot.cache)
await ctx.respond(
self.bot._("user_unjail_successful", "messages", locale=ctx.locale).format(
display_name=user.display_name
)
)
def setup(bot: PycordBot) -> None:
bot.add_cog(CogUser(bot))