diff --git a/cogs/timetable.py b/cogs/timetable.py index 6e122c4..a46d391 100644 --- a/cogs/timetable.py +++ b/cogs/timetable.py @@ -1,12 +1,12 @@ import asyncio import sys -from typing import Optional, Union, Dict +from typing import Optional, Union, Dict, Callable import discord from discord.ext import commands, tasks import json from pathlib import Path -from utils import console +from utils import console, TimeTableDaySwitcherView from datetime import time, datetime, timedelta @@ -42,6 +42,27 @@ class TimeTableCog(commands.Cog): if date.timestamp() <= end_date.timestamp() and date.timestamp() >= start_date.timestamp(): return {"name": name, "start": start_date, "end": end_date} + def format_timetable_message(self, date: datetime) -> str: + """Pre-formats the timetable or error message.""" + if _break := self.are_on_break(date): + return f"No lessons on {discord.utils.format_dt(date, 'D')} - On break {_break['name']!r}." + + lessons = self.timetable.get(date.strftime("%A").lower(), []) + if not lessons: + return f"No lessons on {discord.utils.format_dt(date, 'D')}." + + blocks = [f"```\nTimetable for {date.strftime('%A')} ({date.strftime('%d/%m/%Y')}):\n```"] + for lesson in lessons: + start_datetime = date.replace(hour=lesson["start"][0], minute=lesson["start"][1]) + end_datetime = date.replace(hour=lesson["end"][0], minute=lesson["end"][1]) + text = ( + f"{discord.utils.format_dt(start_datetime, 't')} to {discord.utils.format_dt(end_datetime, 't')}" + f":\n> Lesson Name: {lesson['name']!r}\n" + f"> Tutor: **{lesson['tutor']}**\n> Room: `{lesson['room']}`" + ) + blocks.append(text) + return "\n\n".join(blocks) + def current_lesson(self, date: datetime = None) -> Optional[dict]: date = date or datetime.now() lessons = self.timetable.get(date.strftime("%A").lower(), []) @@ -106,7 +127,7 @@ class TimeTableCog(commands.Cog): async def update_timetable_message( self, - message: Union[discord.Message, discord.ApplicationContext], + message: Union[discord.Message, discord.ApplicationContext, discord.InteractionMessage], date: datetime = None, *, no_prefix: bool = False, @@ -213,21 +234,9 @@ class TimeTableCog(commands.Cog): else: date = datetime.now() - lessons = self.timetable.get(date.strftime("%A").lower(), []) - if not lessons: - return await ctx.respond(f"No lessons on {discord.utils.format_dt(date, 'D')}.") - - blocks = [f"```\nTimetable for {date.strftime('%A')}:\n```"] - for lesson in lessons: - start_datetime = date.replace(hour=lesson["start"][0], minute=lesson["start"][1]) - end_datetime = date.replace(hour=lesson["end"][0], minute=lesson["end"][1]) - text = ( - f"{discord.utils.format_dt(start_datetime, 't')} to {discord.utils.format_dt(end_datetime, 't')}" - f":\n> Lesson Name: {lesson['name']!r}\n" - f"> Tutor: **{lesson['tutor']}**\n> Room: `{lesson['room']}`" - ) - blocks.append(text) - await ctx.respond("\n\n".join(blocks)) + text = self.format_timetable_message(date) + view = TimeTableDaySwitcherView(ctx.author, self, date) + await ctx.respond(text, view=view) def setup(bot): diff --git a/utils/views.py b/utils/views.py index 9ce1705..b60a7dc 100644 --- a/utils/views.py +++ b/utils/views.py @@ -1,13 +1,18 @@ import secrets +from datetime import datetime, timedelta import discord +import typing import re import orm +from discord.ui import View from utils import send_verification_code, get_or_none, Student, VerifyCode, console, TOKEN_LENGTH, BannedStudentID +if typing.TYPE_CHECKING: + from cogs.timetable import TimeTableCog -class VerifyView(discord.ui.View): +class VerifyView(View): def __init__(self, ctx: discord.ApplicationContext): self.ctx = ctx super().__init__(timeout=300, disable_on_timeout=True) @@ -141,3 +146,55 @@ class VerifyView(discord.ui.View): ephemeral=True, delete_after=60, ) + + +class TimeTableDaySwitcherView(View): + def __init__(self, user: discord.User, instance: "TimeTableCog", date: datetime): + super().__init__(disable_on_timeout=True) + self.user = user + self.cog = instance + self.current_date = date + self.update_buttons() + + def mod_date(self, by: int): + self.current_date += timedelta(days=by) + self.update_buttons() + + def update_buttons(self): + def _format(d: datetime) -> str: + return d.strftime("(%A) %d/%m/%Y") + + day_before = self.current_date + timedelta(days=-1) + day_after = self.current_date + timedelta(days=1) + self.get_item("day_before").label = _format(day_before) + self.get_item("day_after").label = _format(day_after) + self.get_item("custom_day").label = _format(self.current_date) + + async def interaction_check(self, interaction: discord.Interaction) -> bool: + return interaction.user == self.user + + @discord.ui.button( + custom_id="day_before", + emoji="\N{leftwards black arrow}" + ) + async def day_before(self, _, interaction: discord.Interaction): + self.mod_date(-1) + await interaction.response.edit_message(self.cog.format_timetable_message(self.current_date), view=self) + + @discord.ui.button( + custom_id="custom_day", + emoji="\N{tear-off calendar}", + disabled=True, + style=discord.ButtonStyle.primary + ) + async def current_day(self, _, __): + ... + + @discord.ui.button( + custom_id="day_after", + emoji="\N{leftwards black arrow}" + ) + async def day_before(self, _, interaction: discord.Interaction): + self.mod_date(-1) + await interaction.response.edit_message(self.cog.format_timetable_message(self.current_date), view=self) +