diff --git a/.env.template b/.env.template index 28a6e9f18bd18860af253353208391ddb4967d56..3ecd2f42e570d18533e024c24d4c51d24b2e2079 100644 --- a/.env.template +++ b/.env.template @@ -38,6 +38,8 @@ DISCORD_CALMDOWN_ROLE=<ID of "Calmdown" role> DISCORD_BOTUEBUNGSPLATZ_CHANNEL=<ID of 'bot-übungsplatz' channel> DISCORD_ELM_STREET_CHANNEL=<ID of elm street channel> DISCORD_HALLOWEEN_CATEGORY=<ID of Halloween category> +DISCORD_SEASONAL_EVENTS_CATEGORY=<ID of Seasonal Events Category> +DISCORD_ADVENT_CALENDAR_CHANNEL_2021=<ID of advent calendar chanel for 2021> # JSON Files DISCORD_ROLES_FILE=<File name for roles JSON file> @@ -51,7 +53,9 @@ DISCORD_CALMDOWN_FILE=<File name for calmdowns JSON file> DISCORD_MODULE_COURSE_FILE=<File name for module course JSON file> DISCORD_MODULE_DATA_FILE=<File name for module data JSON file> DISCORD_TIMER_FILE=<File name for running timers JSON file> +DISCORD_ADVENT_CALENDAR_FILE=<File name for advent calendar JSON file> # Misc DISCORD_DATE_TIME_FORMAT=<Date and time format used for commands like %d.%m.%Y %H:%M> DISCORD_IDEE_REACT_QTY=<Amount of reactions to a submitted idea, neccessary to create a github issue (amount is including botys own reaction)> +DISCORD_ADVENT_CALENDAR_START=<Start date and time for advent calendar. Something like "01.12.2021 00:00"> diff --git a/cogs/christmas.py b/cogs/christmas.py index 90d67c460d3c0a4344937a0e91aa00e1537913ee..9c530f52b33cb2263233e1b1b6e85f4d1aa64d3d 100644 --- a/cogs/christmas.py +++ b/cogs/christmas.py @@ -1,44 +1,169 @@ +import asyncio import json import os -from datetime import datetime +from datetime import datetime, timedelta -from disnake.ext import commands +from disnake import ApplicationCommandInteraction, Member +from disnake.ext import commands, tasks +from dotenv import load_dotenv import utils +load_dotenv() + + +def create_advent_calendar(): + advent_calendar = [] + startdate = utils.date_from_string(os.getenv("DISCORD_ADVENT_CALENDAR_START")).astimezone() + + for i in range(0, 24): + advent_calendar.append({ + "number": i + 1, + "date": utils.date_to_string(startdate + timedelta(days=i)), + "assigned": False, + "opened": False + }) + + return advent_calendar + class Christmas(commands.Cog): def __init__(self, bot): self.bot = bot - self.channel_id = int(os.getenv("DISCORD_ADVENT_CALENDAR_CHANNEL", "0")) - self.advent_calendar = [] - self.load_advent_calendar() - - def load_advent_calendar(self): - advent_calendar_file = open("data/advent_calendar.json", mode='r') - self.advent_calendar = json.load(advent_calendar_file) - - @commands.Cog.listener() - async def on_raw_reaction_add(self, payload): - if payload.message_id == int(os.getenv("DISCORD_ADVENT_CALENDAR_MESSAGE")): - roles = {} - guild = await self.bot.fetch_guild(payload.guild_id) - member = await guild.fetch_member(payload.user_id) - channel = await self.bot.fetch_channel(payload.channel_id) - message = await channel.fetch_message(payload.message_id) - await message.clear_reactions() - - for role in guild.roles: - roles[str(role.id)] = role - - today = datetime.now() - day = today.day if today.day <= 24 else 24 - - if today < datetime(year=2020, month=12, day=1): - return - - for i in range(0, day): - door = self.advent_calendar[i] - if payload.emoji.name == door["emote"]: - await member.add_roles(roles[door["role"]]) - await utils.send_dm(member, f"Glückwunsch, du hast gerade {door['name']} geöffnet") + self.seasonal_events_category = int(os.getenv("DISCORD_SEASONAL_EVENTS_CATEGORY")) + self.advent_calendar_channel = int(os.getenv("DISCORD_ADVENT_CALENDAR_CHANNEL_2021")) + self.file_name = os.getenv("DISCORD_ADVENT_CALENDAR_FILE") + self.advent_calendar = self.load() + self.advent_calendar_loop.start() + + def load(self): + with open(self.file_name, mode='r') as f: + advent_calendar = json.load(f) + + if len(advent_calendar) == 0: + advent_calendar = create_advent_calendar() + + return advent_calendar + + def save(self): + with open(self.file_name, mode='w') as f: + json.dump(self.advent_calendar, f) + + @commands.slash_command(name="advent", guild_ids=[int(os.getenv('DISCORD_GUILD'))]) + async def cmd_advent(self, interaction: ApplicationCommandInteraction): + pass + + @cmd_advent.sub_command(name="list", description="Erhalte die Liste aller Türchen mit Zuordnung und Thema") + @commands.check(utils.is_mod) + async def cmd_advent_list(self, interaction: ApplicationCommandInteraction): + message = f"__**Adventskalender 2021**__\n\n" + + for day in self.advent_calendar: + message += f"{day['number']}. " + if day["assigned"]: + message += f"<@!{day['assignee']}>: \"{day['name']}\"" + else: + message += f"noch nicht zugewiesen" + + message += "\n" + + await interaction.response.send_message(message, ephemeral=True) + + @cmd_advent.sub_command(name="assign", description="Einer Person ein Türchen zuweisen", + guild_ids=[int(os.getenv('DISCORD_GUILD'))]) + @commands.check(utils.is_mod) + async def cmd_advent_assign(self, interaction: ApplicationCommandInteraction, day: int, member: Member, name: str): + if self.advent_calendar[day - 1]["assigned"]: + await interaction.response.send_message("Das gewählte Türchen ist bereits vergeben. \n" + "Wenn du das Türchen an jemand anderen vergeben möchtest, oder das " + "Thema ändern möchtest, verwende `/advent reassign`.", + ephemeral=True) + else: + await interaction.response.defer(ephemeral=True) + await self.assign_day(day, member, name) + await interaction.edit_original_message(content="Das gewählte Türchen wurde vergeben.") + + @cmd_advent.sub_command(name="reassign", description="Ein Türchen neu zuweisen", + guild_ids=[int(os.getenv('DISCORD_GUILD'))]) + @commands.check(utils.is_mod) + async def cmd_advent_reassign(self, interaction: ApplicationCommandInteraction, day: int, member: Member, + name: str): + if not self.advent_calendar[day - 1]["assigned"]: + await interaction.response.send_message("Das gewählte Türchen ist noch nicht vergeben. \n" + "Bitte verwende `/advent assign` um das Türchen an " + "jemanden zu vergeben.", ephemeral=True) + else: + await interaction.response.defer(ephemeral=True) + channel = await self.bot.fetch_channel(self.advent_calendar[day - 1]["channel"]) + old_member = await self.bot.fetch_user(self.advent_calendar[day - 1]["assignee"]) + await channel.set_permissions(old_member, overwrite=None) + await self.assign_day(day, member, name) + await interaction.edit_original_message(content="Das gewählte Türchen wurde neu vergeben.") + + @cmd_advent.sub_command(name="remaining", description="Noch nicht zugewiesene Türchen ausgeben lassen.", + guild_ids=[int(os.getenv('DISCORD_GUILD'))]) + @commands.check(utils.is_mod) + async def cmd_advent_remaining(self, interaction: ApplicationCommandInteraction): + message = f"Noch verfügbare Türchen: " + + for day in self.advent_calendar: + if not day["assigned"]: + message += f"{day['number']}, " + + await interaction.response.send_message(message[:-2], ephemeral=True) + + async def assign_day(self, day: int, member: Member, name: str): + category = await self.bot.fetch_channel(self.seasonal_events_category) + channel = await category.create_text_channel(f"{day}-{name}") + await channel.set_permissions(member.roles[0], view_channel=False) + await channel.set_permissions(member, view_channel=True) + await channel.send(f"Vielen Dank {member.mention}, dass du für das {day}. Türchen etwas zum Thema {name} " + f"vorbereiten möchtest. Dieser Channel ist für dich gedacht. Du kannst hier deinen Beitrag " + f"vorbereiten.\n\n" + f"Am {day}.12.2021 um 00:00 werden alle Nachrichten von dir, die in diesem Channel bis " + f"dahin geschrieben wurden, in einen eigenen Thread für diesen Tag übernommen.\n\n" + f"Beachte bitte, dass Sticker nicht verwendet werden können. Das gleiche gilt für Emojis, " + f"die nicht von diesem Server sind.\n\n" + f"Das Mod-Team wünscht dir viel Spaß bei der Vorbereitung.") + self.advent_calendar[day - 1]["channel"] = channel.id + self.advent_calendar[day - 1]["assigned"] = True + self.advent_calendar[day - 1]["assignee"] = member.id + self.advent_calendar[day - 1]["name"] = name + self.save() + + async def open(self, day): + source_channel = await self.bot.fetch_channel(day["channel"]) + assignee = await self.bot.fetch_user(day["assignee"]) + target_channel = await self.bot.fetch_channel(self.advent_calendar_channel) + thread_name = f"{day['number']}. {day['name']}" + + message = await target_channel.send(f"{day['number']}. \"{day['name']}\", ein Beitrag von {assignee.mention}:") + thread = await message.create_thread(name=thread_name, auto_archive_duration=1440) + day["thread"] = thread.id + day["opened"] = True + + async for msg in source_channel.history(limit=None, oldest_first=True): + if msg.author == assignee: + if len(msg.stickers) > 0: + continue + files = await utils.files_from_attachments(msg.attachments) + await thread.send(content=msg.content, embeds=msg.embeds, files=files) + + await thread.send("--------------------------\nBeginn der Diskussion\n--------------------------") + + self.save() + + @tasks.loop(seconds=10) + async def advent_calendar_loop(self): + now = datetime.now() + for day in self.advent_calendar: + if not day["opened"]: + due_date = utils.date_from_string(day["date"]) + if due_date <= now: + await self.open(day) + else: + return + + @advent_calendar_loop.before_loop + async def before_advent_calendar_loop(self): + await asyncio.sleep(10 - datetime.now().second % 10) diff --git a/utils.py b/utils.py index f66beb2bd5aa74dfcc8e4bc7603619258f323d7a..0959473e7c6bd4bb32d05e686e04912e572cd2a9 100644 --- a/utils.py +++ b/utils.py @@ -1,12 +1,16 @@ import os - -import disnake import re +from datetime import datetime +import disnake from disnake import ButtonStyle +from dotenv import load_dotenv from views.dialog_view import DialogView +load_dotenv() +DATE_TIME_FMT = os.getenv("DISCORD_DATE_TIME_FORMAT") + async def send_dm(user, message, embed=None): """ Send DM to a user/member """ @@ -55,3 +59,19 @@ async def confirm(channel, title, description, message="", custom_prefix="", cal {"emoji": "ðŸ‘", "custom_id": f"{custom_prefix}_yes", "style": ButtonStyle.green}, {"emoji": "👎", "custom_id": f"{custom_prefix}_no", "style": ButtonStyle.red}, ])) + + +def date_to_string(date: datetime): + return date.strftime(DATE_TIME_FMT) + + +def date_from_string(date: str): + return datetime.strptime(date, DATE_TIME_FMT) + + +async def files_from_attachments(attachments): + files = [] + for attachment in attachments: + files.append(await attachment.to_file(spoiler=attachment.is_spoiler())) + + return files