diff --git a/cogs/timer.py b/cogs/timer.py index 8d28271d57109d42ced019941979c107dcdca1e3..045717966ed27b53284e6f4034c75750553f3bfb 100644 --- a/cogs/timer.py +++ b/cogs/timer.py @@ -6,16 +6,12 @@ from copy import deepcopy from datetime import datetime, timedelta import disnake -from disnake import errors, FFmpegPCMAudio, Embed, Colour, MessageInteraction, ApplicationCommandInteraction +from disnake import MessageInteraction, ApplicationCommandInteraction from disnake.ext import commands, tasks from disnake.ui import Button -from dotenv import load_dotenv -from cogs.help import help from views import timer_view -load_dotenv() - class Timer(commands.Cog): @@ -24,205 +20,126 @@ class Timer(commands.Cog): self.guild_id = int(os.getenv('DISCORD_GUILD')) self.default_names = ["Rapunzel", "Aschenputtel", "Schneewittchen", "Frau Holle", "Schneeweißchen und Rosenrot", "Gestiefelter Kater", "Bremer Stadtmusikanten"] - self.running_timers = {} self.timer_file_path = os.getenv("DISCORD_TIMER_FILE") - self.load_timers() + self.running_timers = self.load() + self.load() self.run_timer.start() - def load_timers(self): - timer_file = open(self.timer_file_path, mode='r') - self.running_timers = json.load(timer_file) - - def save_timers(self): - timer_file = open(self.timer_file_path, mode='w') - json.dump(self.running_timers, timer_file) - - @help( - syntax="!timer <working-time?> <break-time?> <name?>", - brief="Deine persönliche Eieruhr", - parameters={ - "learning-time": "Länge der Arbeitsphase in Minuten. Default: 25", - "break-time": "Länge der Pausenphase in Minuten. Default: 5", - "name": "So soll der Timer heißen. Wird ihm kein Name gegeben, nimmt er sich selbst einen." - } - ) - @commands.command(name="timer") - async def cmd_timer(self, ctx: disnake.ext.commands.Context, working_time: int = 25, break_time: int = 5, - name: str = None): - name = name if name else random.choice(self.default_names) - remaining = working_time - status = "Arbeiten" - registered = [str(ctx.author.id)] - - embed = self.create_embed(name, status, working_time, break_time, remaining, registered) - msg = await ctx.send(embed=embed, view=timer_view.TimerView()) - - self.running_timers[str(msg.id)] = {'name': name, - 'status': status, - 'working_time': working_time, - 'break_time': break_time, - 'remaining': remaining, - 'registered': registered, - 'channel': ctx.channel.id} - self.save_timers() - await self.make_sound(registered, 'roll_with_it-outro.mp3') + def load(self): + with open(self.timer_file_path, mode='r') as timer_file: + return json.load(timer_file) - @commands.slash_command(name="timer", description="Erstelle deine persönliche Eieruhr", - guild_ids=[int(os.getenv('DISCORD_GUILD'))]) - async def cmd_slash_timer(self, interaction: ApplicationCommandInteraction, working_time: int = 25, - break_time: int = 5, - name: str = None): - name = name if name else random.choice(self.default_names) - remaining = working_time - status = "Arbeiten" - registered = [str(interaction.author.id)] + def save(self): + with open(self.timer_file_path, mode='w') as timer_file: + json.dump(self.running_timers, timer_file) - embed = self.create_embed(name, status, working_time, break_time, remaining, registered) - await interaction.response.send_message(embed=embed, view=timer_view.TimerView()) - message = await interaction.original_message() + def get_view(self, disabled=False): + view = timer_view.TimerView(callback=self.on_button_click) - self.running_timers[str(message.id)] = {'name': name, - 'status': status, - 'working_time': working_time, - 'break_time': break_time, - 'remaining': remaining, - 'registered': registered, - 'channel': interaction.channel_id} - self.save_timers() - await self.make_sound(registered, 'roll_with_it-outro.mp3') + if disabled: + view.disable() - def create_embed(self, name, status, working_time, break_time, remaining, registered): - color = Colour.green() if status == "Arbeiten" else 0xFFC63A if status == "Pause" else Colour.red() - descr = f"👍 beim Timer anmelden\n\n" \ - f"👎 beim Timer abmelden\n\n" \ - f"⏩ Phase überspringen\n\n" \ - f"🔄 Timer neu starten\n\n" \ - f"🛑 Timer beenden\n" - zeiten = f"{working_time} Minuten Arbeiten\n{break_time} Minuten Pause" - remaining_value = f"{remaining} Minuten" - endzeit = (datetime.now() + timedelta(minutes=remaining)).strftime("%H:%M") - end_value = f" [bis {endzeit} Uhr]" if status != "Beendet" else "" - user_list = [self.bot.get_user(int(user_id)) for user_id in registered] - angemeldet_value = ", ".join([user.mention for user in user_list]) + return view - embed = Embed(title=name, - description=f'Jetzt: {status}', - color=color) - embed.add_field(name="Bedienung:", value=descr, inline=False) - embed.add_field(name="Zeiten:", value=zeiten, inline=False) - embed.add_field(name="verbleibende Zeit:", value=remaining_value + end_value, inline=False) - embed.add_field(name="angemeldete User:", value=angemeldet_value if registered else "-", inline=False) + async def on_button_click(self, button: Button, interaction: MessageInteraction): + custom_id = button.custom_id - return embed + if custom_id == timer_view.SUBSCRIBE: + await self.on_subscribe(button, interaction) + elif custom_id == timer_view.UNSUBSCRIBE: + await self.on_unsubscribe(button, interaction) + elif custom_id == timer_view.SKIP: + await self.on_skip(button, interaction) + elif custom_id == timer_view.RESTART: + await self.on_restart(button, interaction) + elif custom_id == timer_view.STOP: + await self.on_stop(button, interaction) - @commands.Cog.listener() - async def on_button_click(self, interaction: MessageInteraction): - button: Button = interaction.component - - if button.custom_id == "timerview:subscribe": - await self.on_subscribe(interaction) - elif button.custom_id == "timerview:unsubscribe": - await self.on_unsubscribe(interaction) - elif button.custom_id == "timverview:skip": - await self.on_skip(interaction) - elif button.custom_id == "timerview:restart": - await self.on_restart(interaction) - elif button.custom_id == "timerview:stop": - await self.on_stop(interaction) - - async def on_subscribe(self, interaction: MessageInteraction): - message = interaction.message - message_id = str(message.id) - author = interaction.author - - if timer := self.running_timers.get(message_id): - if str(author.id) not in timer['registered']: - timer['registered'].append(str(author.id)) - self.save_timers() - name, status, wt, bt, remaining, registered, _ = self.get_details(message_id) + async def on_subscribe(self, button: Button, interaction: MessageInteraction): + msg_id = str(interaction.message.id) + if timer := self.running_timers.get(msg_id): + if str(interaction.author.id) not in timer['registered']: + timer['registered'].append(str(interaction.author.id)) + self.save() + name, status, wt, bt, remaining, registered, _ = self.get_details(msg_id) embed = self.create_embed(name, status, wt, bt, remaining, registered) - await message.edit(embed=embed, view=timer_view.TimerView()) + await interaction.message.edit(embed=embed, view=self.get_view()) + await interaction.response.send_message("Du hast dich erfolgreich angemeldet", ephemeral=True) + else: + await interaction.response.send_message("Du bist bereits angemeldet.", ephemeral=True) else: await interaction.response.send_message("Etwas ist schiefgelaufen...", ephemeral=True) - async def on_unsubscribe(self, interaction: MessageInteraction): - message = interaction.message - message_id = str(message.id) - author = interaction.author - - if timer := self.running_timers.get(message_id): + async def on_unsubscribe(self, button: Button, interaction: MessageInteraction): + msg_id = str(interaction.message.id) + if timer := self.running_timers.get(msg_id): registered = timer['registered'] - if str(author.id) in registered: + if str(interaction.author.id) in registered: if len(registered) == 1: - await self.on_stop(interaction) + await self.on_stop(button, interaction) return else: - timer['registered'].remove(str(author.id)) - self.save_timers() - name, status, wt, bt, remaining, registered, _ = self.get_details(message_id) + timer['registered'].remove(str(interaction.author.id)) + self.save() + name, status, wt, bt, remaining, registered, _ = self.get_details(msg_id) embed = self.create_embed(name, status, wt, bt, remaining, registered) - await message.edit(embed=embed, view=timer_view.TimerView()) + await interaction.message.edit(embed=embed, view=self.get_view()) + await interaction.response.send_message("Du hast dich erfolgreich abgemeldet", ephemeral=True) + else: + await interaction.response.send_message("Du warst gar nicht angemeldet.", ephemeral=True) else: await interaction.response.send_message("Etwas ist schiefgelaufen...", ephemeral=True) - async def on_skip(self, interaction: MessageInteraction): - message = interaction.message - message_id = str(message.id) - author = interaction.author - - if timer := self.running_timers.get(message_id): + async def on_skip(self, button: Button, interaction: MessageInteraction): + msg_id = str(interaction.message.id) + if timer := self.running_timers.get(msg_id): registered = timer['registered'] - if str(author.id) in timer['registered']: - new_phase = await self.switch_phase(message_id) + if str(interaction.author.id) in timer['registered']: + new_phase = await self.switch_phase(msg_id) if new_phase == "Pause": await self.make_sound(registered, 'groove-intro.mp3') else: await self.make_sound(registered, 'roll_with_it-outro.mp3') + await interaction.response.send_message("Erfolgreich übersprungen", ephemeral=True) else: - # Reply with a hidden message await interaction.response.send_message("Nur angemeldete Personen können den Timer bedienen.", ephemeral=True) else: await interaction.response.send_message("Etwas ist schiefgelaufen...", ephemeral=True) - async def on_restart(self, interaction: MessageInteraction): - message = interaction.message - message_id = str(message.id) - author = interaction.author - - if timer := self.running_timers.get(message_id): + async def on_restart(self, button: Button, interaction: MessageInteraction): + msg_id = str(interaction.message.id) + if timer := self.running_timers.get(msg_id): registered = timer['registered'] - if str(author.id) in timer['registered']: + if str(interaction.author.id) in timer['registered']: timer['status'] = 'Arbeiten' timer['remaining'] = timer['working_time'] - self.save_timers() + self.save() - await self.edit_message(message_id) + await self.edit_message(msg_id) await self.make_sound(registered, 'roll_with_it-outro.mp3') + await interaction.response.send_message("Erfolgreich neugestartet", ephemeral=True) else: - # Reply with a hidden message await interaction.response.send_message("Nur angemeldete Personen können den Timer neu starten.", ephemeral=True) else: await interaction.response.send_message("Etwas ist schiefgelaufen...", ephemeral=True) - async def on_stop(self, interaction: MessageInteraction): - message = interaction.message - message_id = str(message.id) - author = interaction.author - - if timer := self.running_timers.get(message_id): + async def on_stop(self, button: Button, interaction: MessageInteraction): + msg_id = str(interaction.message.id) + if timer := self.running_timers.get(msg_id): registered = timer['registered'] - if str(author.id) in timer['registered']: - mentions = self.get_mentions(message_id) + if str(interaction.author.id) in timer['registered']: + mentions = self.get_mentions(msg_id) timer['status'] = "Beendet" timer['remaining'] = 0 timer['registered'] = [] - if new_msg_id := await self.edit_message(message_id, mentions=mentions): + await interaction.response.send_message("Erfolgreich beendet", ephemeral=True) + if new_msg_id := await self.edit_message(msg_id, mentions=mentions): await self.make_sound(registered, 'applause.mp3') self.running_timers.pop(new_msg_id) - self.save_timers() + self.save() else: # Reply with a hidden message await interaction.response.send_message("Nur angemeldete Personen können den Timer beenden.", @@ -230,6 +147,53 @@ class Timer(commands.Cog): else: await interaction.response.send_message("Etwas ist schiefgelaufen...", ephemeral=True) + def create_embed(self, name, status, working_time, break_time, remaining, registered): + color = disnake.Colour.green() if status == "Arbeiten" else 0xFFC63A if status == "Pause" else disnake.Colour.red() + descr = f"👍 beim Timer anmelden\n\n" \ + f"👎 beim Timer abmelden\n\n" \ + f"⏩ Phase überspringen\n\n" \ + f"🔄 Timer neu starten\n\n" \ + f"🛑 Timer beenden\n" + zeiten = f"{working_time} Minuten Arbeiten\n{break_time} Minuten Pause" + remaining_value = f"{remaining} Minuten" + endzeit = (datetime.now() + timedelta(minutes=remaining)).strftime("%H:%M") + end_value = f" [bis {endzeit} Uhr]" if status != "Beendet" else "" + user_list = [self.bot.get_user(int(user_id)) for user_id in registered] + angemeldet_value = ", ".join([user.mention for user in user_list]) + + embed = disnake.Embed(title=name, + description=f'Jetzt: {status}', + color=color) + embed.add_field(name="Bedienung:", value=descr, inline=False) + embed.add_field(name="Zeiten:", value=zeiten, inline=False) + embed.add_field(name="verbleibende Zeit:", value=remaining_value + end_value, inline=False) + embed.add_field(name="angemeldete User:", value=angemeldet_value if registered else "-", inline=False) + + return embed + + @commands.slash_command(name="timer", description="Erstelle deine persönliche Eieruhr") + async def cmd_timer(self, interaction: ApplicationCommandInteraction, working_time: int = 25, + break_time: int = 5, + name: str = None): + name = name if name else random.choice(self.default_names) + remaining = working_time + status = "Arbeiten" + registered = [str(interaction.author.id)] + + embed = self.create_embed(name, status, working_time, break_time, remaining, registered) + await interaction.response.send_message(embed=embed, view=self.get_view()) + message = await interaction.original_message() + + self.running_timers[str(message.id)] = {'name': name, + 'status': status, + 'working_time': working_time, + 'break_time': break_time, + 'remaining': remaining, + 'registered': registered, + 'channel': interaction.channel_id} + self.save() + await self.make_sound(registered, 'roll_with_it-outro.mp3') + async def switch_phase(self, msg_id): if timer := self.running_timers.get(msg_id): if timer['status'] == "Arbeiten": @@ -241,7 +205,7 @@ class Timer(commands.Cog): else: self.running_timers.pop(msg_id) return "Beendet" - self.save_timers() + self.save() if new_msg_id := await self.edit_message(msg_id): return self.running_timers[new_msg_id]['status'] @@ -273,19 +237,20 @@ class Timer(commands.Cog): if not mentions: mentions = self.get_mentions(msg_id) if status == "Beendet": - new_msg = await channel.send(mentions, embed=embed) + new_msg = await channel.send(mentions, embed=embed, + view=self.get_view(disabled=True)) else: - new_msg = await channel.send(mentions, embed=embed, view=timer_view.TimerView()) + new_msg = await channel.send(mentions, embed=embed, view=self.get_view()) self.running_timers[str(new_msg.id)] = self.running_timers[msg_id] self.running_timers.pop(msg_id) - self.save_timers() + self.save() msg = new_msg else: - await msg.edit(embed=embed, view=timer_view.TimerView()) + await msg.edit(embed=embed, view=self.get_view()) return str(msg.id) - except errors.NotFound: + except disnake.errors.NotFound: self.running_timers.pop(msg_id) - self.save_timers() + self.save() return None def get_mentions(self, msg_id): @@ -304,9 +269,9 @@ class Timer(commands.Cog): if channel: # If user is in a channel try: voice_client = await channel.connect() - voice_client.play(FFmpegPCMAudio(f'cogs/sounds/{filename}')) + voice_client.play(disnake.FFmpegPCMAudio(f'cogs/sounds/{filename}')) await sleep(3) - except errors.ClientException as e: + except disnake.errors.ClientException as e: print(e) for vc in self.bot.voice_clients: await vc.disconnect() @@ -334,4 +299,4 @@ class Timer(commands.Cog): @cmd_timer.error async def timer_error(self, ctx, error): await ctx.send("Das habe ich nicht verstanden. Die Timer-Syntax ist:\n" - "`!timer <learning-time?> <break-time?> <name?>`\n") + "`!timer <learning-time?> <break-time?> <name?>`\n") \ No newline at end of file diff --git a/fernuni_bot.py b/fernuni_bot.py index fedc8c028722c19dc31c72a9ad4457757045f53a..9c9e334e8ebd7cbdda0e696c39e02b673b4a4ba0 100644 --- a/fernuni_bot.py +++ b/fernuni_bot.py @@ -4,7 +4,7 @@ import disnake from disnake.ext import commands from dotenv import load_dotenv -from cogs import appointments, calmdown, christmas, emoji_hunt, github, help, learninggroups, links, \ +from cogs import appointments, calmdown, github, help, learninggroups, links, timer, \ news, polls, roles, support, text_commands, voice, welcome, xkcd, module_information from view_manager import ViewManager @@ -25,31 +25,38 @@ class Boty(commands.Bot): super().__init__(command_prefix='!', help_command=None, activity=disnake.Game(ACTIVITY), owner_id=OWNER, intents=disnake.Intents.all()) self.view_manager = ViewManager(self) + self.add_cogs() + self.persistent_views_added = False + # self.add_cog(elm_street.ElmStreet(self)) + + def is_prod(self): + return os.getenv("DISCORD_PROD") == "True" + + async def on_ready(self): + self.view_manager.on_ready() + if not self.persistent_views_added: + if timer_cog := self.get_cog("Timer"): + self.add_view(timer_cog.get_view()) + print("Client started!") + + def add_cogs(self): self.add_cog(appointments.Appointments(self)) self.add_cog(text_commands.TextCommands(self)) self.add_cog(polls.Polls(self)) self.add_cog(roles.Roles(self)) self.add_cog(welcome.Welcome(self)) - self.add_cog(christmas.Christmas(self)) self.add_cog(support.Support(self)) self.add_cog(news.News(self)) self.add_cog(links.Links(self)) self.add_cog(voice.Voice(self)) - self.add_cog(emoji_hunt.EmojiHunt(self)) self.add_cog(learninggroups.LearningGroups(self)) self.add_cog(module_information.ModuleInformation(self)) self.add_cog(xkcd.Xkcd(self)) self.add_cog(help.Help(self)) self.add_cog(calmdown.Calmdown(self)) self.add_cog(github.Github(self)) - # self.add_cog(timer.Timer(self)) - # self.add_cog(elm_street.ElmStreet(self)) - def is_prod(self): - return os.getenv("DISCORD_PROD") == "True" + self.add_cog(timer.Timer(self)) - async def on_ready(self): - self.view_manager.on_ready() - print("Client started!") bot = Boty() diff --git a/requirements.txt b/requirements.txt index 8e9f2d2ec46e5f83572f293219e5bb10f1e1d5e9..7213ab12887f08b1cf6f64776273fd4bfe3e119f 100644 --- a/requirements.txt +++ b/requirements.txt @@ -5,7 +5,7 @@ beautifulsoup4==4.9.3 certifi==2020.12.5 cffi==1.14.5 chardet==3.0.4 -disnake==2.1.2 +disnake==2.2.2 emoji==1.2.0 idna==2.10 multidict==5.1.0 diff --git a/views/timer_view.py b/views/timer_view.py index 29385136201a416cda29fdb835c502a079cbc88b..06ce1cca482a3609e0054d859653eacf0fa484bb 100644 --- a/views/timer_view.py +++ b/views/timer_view.py @@ -1,26 +1,39 @@ import disnake from disnake import MessageInteraction, ButtonStyle -from disnake.ui import Button +from disnake.ui import Button, View +SUBSCRIBE = "timerview:subscribe" +UNSUBSCRIBE = "timerview:unsubscribe" +SKIP = "timverview:skip" +RESTART = "timverview:restart" +STOP = "timverview:stop" -class TimerView(disnake.ui.View): - @disnake.ui.button(emoji="👍", style=ButtonStyle.grey, custom_id="timerview:subscribe") +class TimerView(View): + def __init__(self, callback): + super().__init__(timeout=None) + self.callback = callback + + @disnake.ui.button(emoji="👍", style=ButtonStyle.grey, custom_id=SUBSCRIBE) async def btn_subscribe(self, button: Button, interaction: MessageInteraction): - pass + await self.callback(button, interaction) - @disnake.ui.button(emoji="👎", style=ButtonStyle.grey, custom_id="timerview:unsubscribe") + @disnake.ui.button(emoji="👎", style=ButtonStyle.grey, custom_id=UNSUBSCRIBE) async def btn_unsubscribe(self, button: Button, interaction: MessageInteraction): - pass + await self.callback(button, interaction) - @disnake.ui.button(emoji="⏩", style=ButtonStyle.grey, custom_id="timverview:skip") + @disnake.ui.button(emoji="⏩", style=ButtonStyle.grey, custom_id=SKIP) async def btn_skip(self, button: Button, interaction: MessageInteraction): - pass + await self.callback(button, interaction) - @disnake.ui.button(emoji="🔄", style=ButtonStyle.grey, custom_id="timerview:restart") + @disnake.ui.button(emoji="🔄", style=ButtonStyle.grey, custom_id=RESTART) async def btn_restart(self, button: Button, interaction: MessageInteraction): - pass + await self.callback(button, interaction) - @disnake.ui.button(emoji="🛑", style=ButtonStyle.grey, custom_id="timerview:stop") + @disnake.ui.button(emoji="🛑", style=ButtonStyle.grey, custom_id=STOP) async def btn_stop(self, button: Button, interaction: MessageInteraction): - pass + await self.callback(button, interaction) + + def disable(self): + for button in self.children: + button.disabled = True