From 90b88ae7d75d86071e5c305acb55927bd9b07b19 Mon Sep 17 00:00:00 2001 From: Luca <87448287+LUCA-PYTHON@users.noreply.github.com> Date: Sat, 14 Mar 2026 15:05:49 +0100 Subject: [PATCH 1/4] Add interactive calendar view with navigation buttons --- datein/calendar.py | 118 ++++++++++++++++++++++++++++++++++++++++++--- 1 file changed, 111 insertions(+), 7 deletions(-) diff --git a/datein/calendar.py b/datein/calendar.py index ee6e685..b1f87ac 100644 --- a/datein/calendar.py +++ b/datein/calendar.py @@ -7,6 +7,91 @@ from datetime import date import io + +class CalendarView(discord.ui.View): + def __init__(self, cog, scope, month, year, day, user_id): + super().__init__(timeout=300) + self.cog = cog + self.scope = scope + self.month = month + self.year = year + self.day = day + self.user_id = user_id + self.message = None + + async def update_calendar(self, interaction: discord.Interaction): + file = await self.cog.generate_calendar_image( + interaction, self.scope, self.month, self.year, self.day + ) + + if self.message is None: + await interaction.response.send_message(file=file, view=self) + self.message = await interaction.original_response() + else: + await interaction.response.edit_message(attachments=[file], view=self) + + @discord.ui.button(label="⬅️", style=discord.ButtonStyle.secondary) + async def back(self, interaction: discord.Interaction, button: discord.ui.Button): + if interaction.user.id != self.user_id: + return await interaction.response.send_message( + "You cannot control this calendar.", ephemeral=True + ) + + if self.day: + self.day -= 1 + if self.day < 1: + self.month -= 1 + if self.month < 1: + self.month = 12 + self.year -= 1 + self.day = calendar.monthrange(self.year, self.month)[1] + else: + self.month -= 1 + if self.month < 1: + self.month = 12 + self.year -= 1 + + await self.update_calendar(interaction) + + @discord.ui.button(label="today", style=discord.ButtonStyle.primary) + async def today(self, interaction: discord.Interaction, button: discord.ui.Button): + if interaction.user.id != self.user_id: + return await interaction.response.send_message( + "You cannot control this calendar.", ephemeral=True + ) + + today = date.today() + self.year = today.year + self.month = today.month + self.day = None + + await self.update_calendar(interaction) + + @discord.ui.button(label="➡️", style=discord.ButtonStyle.secondary) + async def forward(self, interaction: discord.Interaction, button: discord.ui.Button): + if interaction.user.id != self.user_id: + return await interaction.response.send_message( + "You cannot control this calendar.", ephemeral=True + ) + + if self.day: + self.day += 1 + last_day = calendar.monthrange(self.year, self.month)[1] + if self.day > last_day: + self.day = 1 + self.month += 1 + if self.month > 12: + self.month = 1 + self.year += 1 + else: + self.month += 1 + if self.month > 12: + self.month = 1 + self.year += 1 + + await self.update_calendar(interaction) + + class TaskCalendar(commands.Cog): def __init__(self, bot): self.bot = bot @@ -33,6 +118,11 @@ async def calendar( month = month or today.month year = year or today.year + view = CalendarView(self, scope, month, year, day, interaction.user.id) + await view.update_calendar(interaction) + + async def generate_calendar_image(self, interaction, scope, month, year, day): + today = date.today() async with self.bot.pool.acquire() as conn: async with conn.cursor() as cur: if scope.lower() == "guild": @@ -86,6 +176,7 @@ async def calendar( try: font_title = ImageFont.truetype("arial.ttf", 70 * scale) + font_subtitle = ImageFont.truetype("arial.ttf", 50 * scale) font_weekday = ImageFont.truetype("arial.ttf", 30 * scale) font_day = ImageFont.truetype("arial.ttf", 34 * scale) font_task_title = ImageFont.truetype("arial.ttf", 32 * scale) @@ -93,6 +184,7 @@ async def calendar( font_event = ImageFont.truetype("arial.ttf", 22 * scale) except: font_title = ImageFont.load_default() + font_subtitle = ImageFont.load_default() font_weekday = ImageFont.load_default() font_day = ImageFont.load_default() font_task_title = ImageFont.load_default() @@ -102,11 +194,18 @@ async def calendar( if day: draw.text( (width // 2, 80 * scale), - f"Tasks for {day}.{month}.{year}", + f"{calendar.day_name[date(year, month, day).weekday()]}, {month}.{day}.{year}", fill=text_color, font=font_title, anchor="mm" ) + draw.text( + (width // 2, 150 * scale), + f"{interaction.guild.name if scope.lower() == 'guild' else interaction.user.name}`s Tasks", + fill=text_color, + font=font_subtitle, + anchor="mm" + ) y_offset = 200 * scale task_spacing = 100 * scale @@ -154,7 +253,7 @@ async def calendar( (width//2, height//2), "No tasks for this day.", fill=subtext_color, - font=font_event, + font=font_weekday, anchor="mm" ) @@ -220,15 +319,20 @@ async def calendar( more_text, fill=subtext_color, font=font_event, anchor="lm" ) + draw.text( + (width // 2, height - 35 * scale), + f"{interaction.guild.name if scope.lower() == 'guild' else interaction.user.name}`s Tasks", + fill=subtext_color, + font=font_task_title, + anchor="mm" + ) + final_img = img.resize((1600, 1100), Image.LANCZOS) with io.BytesIO() as image_binary: final_img.save(image_binary, "PNG", optimize=True) image_binary.seek(0) - file = discord.File(fp=image_binary, filename="calendar.png") - await interaction.response.send_message( - file=file, - content=f"📅 **{scope.title()} Calendar** – {calendar.month_name[month]} {year}\nUser: {interaction.user.mention}" - ) + return discord.File(fp=image_binary, filename="calendar.png") + async def setup(bot): await bot.add_cog(TaskCalendar(bot)) From f3b232ff70b32ba6ddc6893214410a1d872fdcd6 Mon Sep 17 00:00:00 2001 From: Luca <87448287+LUCA-PYTHON@users.noreply.github.com> Date: Sat, 14 Mar 2026 19:33:43 +0100 Subject: [PATCH 2/4] Refactor task creation and editing modals - Added tags, priority and status - Updatet `/task list` with multial pages --- datein/tasks.py | 460 ++++++++++++++++++++++++++++++++++-------------- 1 file changed, 328 insertions(+), 132 deletions(-) diff --git a/datein/tasks.py b/datein/tasks.py index 467b027..e61d585 100644 --- a/datein/tasks.py +++ b/datein/tasks.py @@ -1,9 +1,10 @@ import discord import logging from discord.ext import commands, tasks -from discord import app_commands +from discord import app_commands, ui, Interaction, Embed, ButtonStyle from datetime import datetime import pytz +import math from typing import Literal import aiomysql @@ -22,6 +23,7 @@ logger.addHandler(handler) +# Helper function to send error messages async def send_error(interaction, error, embed_description: str, embed_color: discord.Color, embed_icon_url: str, @@ -45,40 +47,15 @@ async def send_error(interaction, error, embed_description: str, logger.error(f"Unbekannter Fehler aufgetreten! {error}") +#Create Task Modal & View class CreateModal(discord.ui.Modal, title="📩 - Create Task"): - def __init__(self, table_type: str, view: "TaskView"): + def __init__(self, table_type: str): super().__init__() self.table_type = table_type - self.view = view - - self.title_modal = discord.ui.TextInput( - label="Task Title", - placeholder="e.g. program a Discord bot", - max_length=50, - required=True - ) - - self.des = discord.ui.TextInput( - label="Task Description", - placeholder="e.g. programm a Discord bot für NexoryOrg (moderation and logging)", - max_length=500, - required=True - ) - - self.time = discord.ui.TextInput( - label="Finish date", - placeholder="YYYY-MM-DD", - max_length=10, - required=True - ) - - self.remindme = discord.ui.TextInput( - label="Remind me", - placeholder="Type 'yes' if you want to be reminded when the task is due.", - max_length=3, - required=False - ) - + self.title_modal = discord.ui.TextInput(label="Task Title", placeholder="e.g. program a Discord bot", max_length=50, required=True) + self.des = discord.ui.TextInput(label="Task Description", placeholder="e.g. programm a Discord bot für NexoryOrg (moderation and logging)", max_length=500, required=True) + self.time = discord.ui.TextInput(label="Finish date", placeholder="YYYY-MM-DD", max_length=10, required=True) + self.remindme = discord.ui.TextInput(label="Remind me", placeholder="Type 'yes' if you want to be reminded", max_length=3, required=False) self.add_item(self.title_modal) self.add_item(self.des) self.add_item(self.time) @@ -88,16 +65,11 @@ async def on_submit(self, interaction: discord.Interaction): try: date = datetime.strptime(self.time.value, "%Y-%m-%d").date() today = datetime.now().date() - if date <= today: - return await interaction.response.send_message( - "⛔ - The date must be in the future! Please enter a future date.", - ephemeral=True - ) - + return await interaction.response.send_message("⛔ - The date must be in the future! Please enter a future date.", ephemeral=True) + remindme_bool = self.remindme.value.lower() == "yes" async with interaction.client.pool.acquire() as conn: async with conn.cursor() as cur: - if self.table_type == "user": table_name = "nexory_user_tasks" table_term = "userID" @@ -106,54 +78,136 @@ async def on_submit(self, interaction: discord.Interaction): table_name = "nexory_guild_tasks" table_term = "guildID" id_value = interaction.guild.id - - await cur.execute( - f"SELECT 1 FROM {table_name} WHERE {table_term}=%s AND title=%s", - (id_value, self.title_modal.value) - ) - + await cur.execute(f"SELECT 1 FROM {table_name} WHERE {table_term}=%s AND title=%s", (id_value, self.title_modal.value)) if await cur.fetchone(): - return await interaction.response.send_message( - "⛔ - Please don't create the same task twice.", - ephemeral=True - ) - - if self.remindme.value.lower() == "yes": - self.remindme = True - else: - self.remindme = False - - await cur.execute( - f"INSERT INTO {table_name} ({table_term}, title, des, date, remindme) VALUES (%s, %s, %s, %s, %s)", - (id_value, self.title_modal.value, self.des.value, date, self.remindme) - ) - await conn.commit() - + return await interaction.response.send_message("⛔ - Please don't create the same task twice.", ephemeral=True) + if self.table_type == "guild": + async with interaction.client.pool.acquire() as conn: + async with conn.cursor() as cur: + await cur.execute("SELECT tag FROM nexory_guild_custom_tags WHERE guildID=%s", (interaction.guild.id,)) + rows = await cur.fetchall() + db_tags = [r[0] for r in rows] if rows else [] + fixed_tags = ["#termin", "#task", "#work"] + all_tags = db_tags + fixed_tags + else: + all_tags = ["#termin", "#task", "#work"] await interaction.response.send_message( - f"`✅` - Task **{self.title_modal.value}** created successfully.", + view=CreateView( + client=interaction.client, + table_type=self.table_type, + title=self.title_modal.value, + des=self.des.value, + date=date, + remindme=remindme_bool, + guild=interaction.guild if self.table_type=="guild" else None, + tag_options=all_tags + ), ephemeral=True ) - except Exception as e: - await send_error( - interaction, - e, - "Es ist ein unbekannter Fehler aufgetreten.", - discord.Color.red(), - interaction.user.display_avatar.url, - "Fehlermeldung", - "https://github.com/NexoryOrg" - ) + await send_error(interaction, e, "Es ist ein unbekannter Fehler aufgetreten.", discord.Color.red(), interaction.user.display_avatar.url, "Fehlermeldung", "https://github.com/NexoryOrg") +class CreateView(discord.ui.LayoutView): + def __init__(self, client, table_type: str, title: str, des: str, date: datetime, remindme: bool, guild, tag_options): + super().__init__(timeout=300) + self.client = client + self.table_type = table_type + self.title = title + self.des = des + self.date = date + self.remindme = remindme + self.guild = guild + self.selected_tag = None + self.selected_priority = None + self.tag_options = tag_options + self._build() + def _build(self): + self.clear_items() + container = discord.ui.Container(accent_color=discord.Color.green().value) + container.add_item(discord.ui.TextDisplay("# 📩 - Create Task")) + container.add_item(discord.ui.Separator()) + self.tag_select = discord.ui.Select( + placeholder="Select a tag for the task", + options=[discord.SelectOption(label=tag, value=tag) for tag in self.tag_options] + ) + async def tag_callback(interaction: discord.Interaction): + self.selected_tag = self.tag_select.values[0] + await interaction.response.defer() + self.tag_select.callback = tag_callback + container.add_item(discord.ui.ActionRow(self.tag_select)) + self.priority_select = discord.ui.Select( + placeholder="Select a priority for the task", + options=[ + discord.SelectOption(label="Not important", value="not important"), + discord.SelectOption(label="Normal", value="normal"), + discord.SelectOption(label="Important", value="important") + ] + ) + async def priority_callback(interaction: discord.Interaction): + self.selected_priority = self.priority_select.values[0] + await interaction.response.defer() + self.priority_select.callback = priority_callback + container.add_item(discord.ui.ActionRow(self.priority_select)) + save_btn = discord.ui.Button(label="Save", style=discord.ButtonStyle.green) + cancel_btn = discord.ui.Button(label="Cancel", style=discord.ButtonStyle.red) + save_btn.callback = self.save_clicked + cancel_btn.callback = self.cancel_clicked + container.add_item(discord.ui.ActionRow(save_btn, cancel_btn)) + self.add_item(container) + + async def save_clicked(self, interaction: discord.Interaction): + if not self.selected_tag or not self.selected_priority: + self.selected_priority = "normal" + + async with self.client.pool.acquire() as conn: + async with conn.cursor() as cur: + if self.table_type == "user": + table_name = "nexory_user_tasks" + table_term = "userID" + id_value = interaction.user.id + else: + table_name = "nexory_guild_tasks" + table_term = "guildID" + id_value = interaction.guild.id + await cur.execute( + f"INSERT INTO {table_name} ({table_term}, title, des, date, remindme, tag, priority) VALUES (%s,%s,%s,%s,%s,%s,%s)", + (id_value, self.title, self.des, self.date, self.remindme, self.selected_tag, self.selected_priority) + ) + await conn.commit() + success_view = discord.ui.LayoutView() + container = discord.ui.Container(accent_color=discord.Color.green().value) + container.add_item(discord.ui.TextDisplay(f"# ✅ Task '{self.title}' created!")) + container.add_item(discord.ui.Separator()) + container.add_item(discord.ui.TextDisplay(f"**Tag**: {self.selected_tag}")) + container.add_item(discord.ui.TextDisplay(f"**Priority**: {self.selected_priority}")) + success_view.add_item(container) + await interaction.response.edit_message(view=success_view) + self.stop() + + async def cancel_clicked(self, interaction: discord.Interaction): + cancel_view = discord.ui.LayoutView() + container = discord.ui.Container(accent_color=discord.Color.red().value) + container.add_item(discord.ui.TextDisplay("# ❌ Task creation canceled")) + container.add_item(discord.ui.Separator()) + container.add_item(discord.ui.TextDisplay("The configuration process was aborted.")) + cancel_view.add_item(container) + await interaction.response.edit_message(view=cancel_view) + self.stop() + + +#Edit Task Modal & View class EditModal(discord.ui.Modal, title="📝 - Edit Task"): - def __init__(self, table_type: str, title: str, view: "TaskView"): + def __init__(self, table_type: str, title: str, view: "TaskView", guild=None): super().__init__() self.table_type = table_type self.original_title = title self.view = view + self.guild = guild self.title_value = None + self.selected_tag = None + self.selected_priority = None self.edit_title_modal = discord.ui.TextInput( label=f"Task Title (old: {title})", @@ -178,7 +232,7 @@ def __init__(self, table_type: str, title: str, view: "TaskView"): self.remindme = discord.ui.TextInput( label="Remind me", - placeholder="Type 'no' if you don`t want to be reminded when the task is due.", + placeholder="Type 'no' if you don't want to be reminded", max_length=3, required=False ) @@ -192,7 +246,6 @@ async def on_submit(self, interaction: discord.Interaction): try: async with interaction.client.pool.acquire() as conn: async with conn.cursor() as cur: - if self.table_type == "user": table_name = "nexory_user_tasks" table_term = "userID" @@ -203,83 +256,233 @@ async def on_submit(self, interaction: discord.Interaction): id_value = interaction.guild.id await cur.execute( - f"""SELECT title, des, date, remindme FROM {table_name} - WHERE {table_term}=%s AND title=%s""", + f"SELECT title, des, date, remindme, tag, priority FROM {table_name} WHERE {table_term}=%s AND title=%s", (id_value, self.original_title) ) row = await cur.fetchone() - if not row: - return await interaction.response.send_message( - "⛔ - Task not found.", - ephemeral=True - ) + return await interaction.response.send_message("⛔ - Task not found.", ephemeral=True) - old_title, old_des, old_date, old_remind = row + old_title, old_des, old_date, old_remind, old_tag, old_priority = row new_title = self.edit_title_modal.value.strip() or old_title - new_des = self.edit_des.value.strip() or old_des - if self.edit_time.value.strip(): try: edit_date = datetime.strptime(self.edit_time.value.strip(), "%Y-%m-%d").date() if edit_date <= datetime.now().date(): - return await interaction.response.send_message( - "⛔ - The date must be in the future!", - ephemeral=True - ) + return await interaction.response.send_message("⛔ - The date must be in the future!", ephemeral=True) except ValueError: - return await interaction.response.send_message( - "⛔ - Invalid date format! Use YYYY-MM-DD.", - ephemeral=True - ) + return await interaction.response.send_message("⛔ - Invalid date format! Use YYYY-MM-DD.", ephemeral=True) else: edit_date = old_date if self.remindme.value.strip(): new_remind = True if self.remindme.value.strip().lower() == "yes" else False else: - new_remind = True if old_remind else False + new_remind = old_remind if new_title != old_title: - await cur.execute( - f"""SELECT 1 FROM {table_name} - WHERE {table_term}=%s AND title=%s""", - (id_value, new_title) - ) + await cur.execute(f"SELECT 1 FROM {table_name} WHERE {table_term}=%s AND title=%s", (id_value, new_title)) if await cur.fetchone(): - return await interaction.response.send_message( - "⛔ - A task with that title already exists.", - ephemeral=True - ) + return await interaction.response.send_message("⛔ - A task with that title already exists.", ephemeral=True) + + if self.table_type == "guild": + await cur.execute("SELECT tag FROM nexory_guild_custom_tags WHERE guildID=%s", (interaction.guild.id,)) + rows = await cur.fetchall() + db_tags = [r[0] for r in rows] if rows else [] + fixed_tags = ["#termin", "#task", "#work"] + all_tags = db_tags + fixed_tags + self.selected_tag = old_tag if old_tag in all_tags else None + else: + all_tags = ["#termin", "#task", "#work"] + self.selected_tag = old_tag + + + priority_options = ["not important", "normal", "important"] + self.selected_priority = old_priority if old_priority in priority_options else "normal" await cur.execute( - f"""UPDATE {table_name} - SET title=%s, des=%s, date=%s, remindme=%s - WHERE {table_term}=%s AND title=%s""", + f"UPDATE {table_name} SET title=%s, des=%s, date=%s, remindme=%s WHERE {table_term}=%s AND title=%s", (new_title, new_des, edit_date, new_remind, id_value, old_title) ) - await conn.commit() - self.title_value = new_title await interaction.response.send_message( - f"`🔁` - Edited Task **{self.title_value}** successfully.", + view=EditView( + bot=interaction.client, + table_type=self.table_type, + title=new_title, + des=new_des, + date=edit_date, + remindme=new_remind, + guild=interaction.guild, + selected_tag=self.selected_tag, + selected_priority=self.selected_priority, + tag_options=all_tags + ), ephemeral=True ) + except Exception as e: - await send_error( - interaction, e, - "Es ist ein unbekannter Fehler aufgetreten.", - discord.Color.red(), - interaction.user.display_avatar.url, - "Fehlermeldung", - "https://github.com/NexoryOrg" + await send_error(interaction, e, "Es ist ein unbekannter Fehler aufgetreten.", discord.Color.red(), interaction.user.display_avatar.url, "Fehlermeldung", "https://github.com/NexoryOrg") + +class EditView(discord.ui.LayoutView): + def __init__(self, bot, table_type, title, des, date, remindme, guild, selected_tag, selected_priority, tag_options): + super().__init__(timeout=300) + self.bot = bot + self.table_type = table_type + self.title = title + self.des = des + self.date = date + self.remindme = remindme + self.guild = guild + self.selected_tag = selected_tag + self.selected_priority = selected_priority + self.tag_options = tag_options + self._build() + + def _build(self): + self.clear_items() + container = discord.ui.Container(accent_color=discord.Color.green().value) + container.add_item(discord.ui.TextDisplay(f"# `🔁` - Task **{self.title}** updated!\nDo you want to edit Tag & Priority?")) + container.add_item(discord.ui.Separator()) + + container.add_item(discord.ui.TextDisplay(f"**Edit Tag (old: {self.selected_tag})**:")) + + self.tag_select = discord.ui.Select( + placeholder="Select a tag", + options=[discord.SelectOption(label=tag, value=tag) for tag in self.tag_options], + ) + async def tag_callback(interaction: discord.Interaction): + self.selected_tag = self.tag_select.values[0] + await interaction.response.defer() + self.tag_select.callback = tag_callback + container.add_item(discord.ui.ActionRow(self.tag_select)) + + container.add_item(discord.ui.TextDisplay(f"**Edit priority (old: {self.selected_priority})**:")) + + self.priority_select = discord.ui.Select( + placeholder="Select priority", + options=[ + discord.SelectOption(label="Not important", value="not important"), + discord.SelectOption(label="Normal", value="normal"), + discord.SelectOption(label="Important", value="important") + ], + ) + async def priority_callback(interaction: discord.Interaction): + self.selected_priority = self.priority_select.values[0] + await interaction.response.defer() + self.priority_select.callback = priority_callback + container.add_item(discord.ui.ActionRow(self.priority_select)) + + save_btn = discord.ui.Button(label="Save", style=discord.ButtonStyle.green) + cancel_btn = discord.ui.Button(label="Cancel", style=discord.ButtonStyle.red) + save_btn.callback = self.save_clicked + cancel_btn.callback = self.cancel_clicked + container.add_item(discord.ui.ActionRow(save_btn, cancel_btn)) + + self.add_item(container) + + async def save_clicked(self, interaction: discord.Interaction): + async with self.bot.pool.acquire() as conn: + async with conn.cursor() as cur: + table_name = "nexory_guild_tasks" if self.table_type=="guild" else "nexory_user_tasks" + table_term = "guildID" if self.table_type=="guild" else "userID" + id_value = self.guild.id if self.table_type=="guild" else interaction.user.id + await cur.execute( + f"UPDATE {table_name} SET title=%s, des=%s, date=%s, remindme=%s, tag=%s, priority=%s WHERE {table_term}=%s AND title=%s", + (self.title, self.des, self.date, self.remindme, self.selected_tag, self.selected_priority, id_value, self.title) + ) + await conn.commit() + success_view = discord.ui.LayoutView() + container = discord.ui.Container(accent_color=discord.Color.green().value) + container.add_item(discord.ui.TextDisplay(f"# ✅ Task '{self.title}' updated!")) + container.add_item(discord.ui.Separator()) + container.add_item(discord.ui.TextDisplay(f"Tag: {self.selected_tag}")) + container.add_item(discord.ui.TextDisplay(f"Priority: {self.selected_priority}")) + success_view.add_item(container) + await interaction.response.edit_message(view=success_view) + self.stop() + + async def cancel_clicked(self, interaction: discord.Interaction): + cancel_view = discord.ui.LayoutView() + container = discord.ui.Container(accent_color=discord.Color.red().value) + container.add_item(discord.ui.TextDisplay("# ❌ Edit canceled")) + container.add_item(discord.ui.Separator()) + container.add_item(discord.ui.TextDisplay("The configuration process was aborted.")) + cancel_view.add_item(container) + await interaction.response.edit_message(view=cancel_view) + self.stop() + + +#Tasklist management View +class TaskListView(ui.View): + def __init__(self, tasks, scope, userID): + super().__init__(timeout=300) + self.tasks = tasks + self.scope = scope + self.userID = userID + self.current_page = 0 + self.tasks_per_page = 5 + self.max_page = math.ceil(len(tasks) / self.tasks_per_page) - 1 + + self.previous_button.disabled = True + if len(tasks) <= self.tasks_per_page: + self.next_button.disabled = True + + def get_embed(self): + start = self.current_page * self.tasks_per_page + end = start + self.tasks_per_page + page_tasks = self.tasks[start:end] + + title = "Start" if self.current_page == 0 else f"Page {self.current_page + 1}/{self.max_page + 1}" + + embed = Embed( + title=f"{self.scope.title()} Tasks - {title}", + color=discord.Color.dark_blue(), + timestamp=datetime.now() + ) + + for t_title, des, date in page_tasks: + embed.add_field( + name=f"__Title: {t_title}__", + value=f"Description:\n*{des}*\nDue:\n*{date}*", + inline=False ) + embed.set_footer(text='Use the edit option in "/task" to view details about each task.') + embed.set_author(name="Task List") + return embed + + @ui.button(label="⬅️", style=discord.ButtonStyle.primary, row=0) + async def previous_button(self, interaction: Interaction, button: discord.ui.Button): + if interaction.user.id != self.userID: + await interaction.response.send_message("You cannot control this pagination.", ephemeral=True) + return + + self.current_page = max(self.current_page - 1, 0) + self.previous_button.disabled = self.current_page == 0 + self.next_button.disabled = self.current_page == self.max_page + + await interaction.response.edit_message(embed=self.get_embed(), view=self) + + @ui.button(label="➡️", style=discord.ButtonStyle.primary, row=0) + async def next_button(self, interaction: Interaction, button: discord.ui.Button): + if interaction.user.id != self.userID: + await interaction.response.send_message("You cannot control this pagination.", ephemeral=True) + return + + self.current_page = min(self.current_page + 1, self.max_page) + self.previous_button.disabled = self.current_page == 0 + self.next_button.disabled = self.current_page == self.max_page + + await interaction.response.edit_message(embed=self.get_embed(), view=self) + + +#Task Management View class TaskView(discord.ui.LayoutView): def __init__(self, bot, table_type: str, user_id=None, guild_id=None): super().__init__(timeout=300) @@ -353,7 +556,7 @@ def _build(self): ) async def create_cb(interaction: discord.Interaction): - modal = CreateModal(self.table_type, view=self) + modal = CreateModal(self.table_type) await interaction.response.send_modal(modal) create_btn.callback = create_cb @@ -458,13 +661,13 @@ async def list_sc(interaction: discord.Interaction): id_value = self.guild_id await cur.execute( - f"SELECT title, des, date, remindme FROM {table_name} WHERE {table_term}=%s AND title=%s", + f"SELECT title, des, date, remindme, tag, status, priority FROM {table_name} WHERE {table_term}=%s AND title=%s", (id_value, value) ) row = await cur.fetchone() if row: - title, des, date, remind = row + title, des, date, remind, tag, status, priority = row if remind == 1: remind = "Yes" @@ -481,6 +684,9 @@ async def list_sc(interaction: discord.Interaction): embed.add_field(name="Description", value=des, inline=False) embed.add_field(name="Finish Date", value=date, inline=False) embed.add_field(name="Remind me", value=remind, inline=False) + embed.add_field(name="Tag", value=tag, inline=False) + embed.add_field(name="Status", value=status, inline=False) + embed.add_field(name="Priority", value=priority, inline=False) embed.set_footer(text="https://github.com/NexoryOrg") await interaction.response.send_message(embed=embed, ephemeral=True) else: @@ -684,19 +890,9 @@ async def list_tasks(self, interaction: discord.Interaction, scope: Literal["gui await interaction.response.send_message("No tasks found.", ephemeral=True) return - embed = discord.Embed( - title=f"{scope.title()} Tasks", - color=discord.Color.dark_blue(), - timestamp=datetime.now() - ) - - for title, des, date in rows: - embed.add_field(name=title, value=f"{des}\nDue: {date}", inline=False) - - embed.set_footer(text="https://github.com/NexoryOrg") - embed.set_author(name="Task List", icon_url=interaction.user.display_avatar.url) - await interaction.response.send_message(embed=embed, ephemeral=True) + view = TaskListView(rows, scope, interaction.user.id) + await interaction.response.send_message(embed=view.get_embed(), view=view, ephemeral=True) async def setup(bot: commands.Bot): - await bot.add_cog(tasks(bot)) \ No newline at end of file + await bot.add_cog(tasks(bot)) From 44c25f4959d243e15ffdf2213326c94f30729d79 Mon Sep 17 00:00:00 2001 From: Luca <87448287+LUCA-PYTHON@users.noreply.github.com> Date: Sat, 14 Mar 2026 19:33:56 +0100 Subject: [PATCH 3/4] Add new columns and tables for user and guild tasks --- main.py | 12 ++++++++---- 1 file changed, 8 insertions(+), 4 deletions(-) diff --git a/main.py b/main.py index 6e9608f..cea5878 100644 --- a/main.py +++ b/main.py @@ -58,23 +58,27 @@ async def setup_hook(self): async with conn.cursor() as cur: await cur.execute( "CREATE TABLE IF NOT EXISTS nexory_user_tasks(" - "userID BIGINT, title VARCHAR(50), des LONGTEXT, date DATE, remindme BOOLEAN DEFAULT FALSE)" + "userID BIGINT, title VARCHAR(50), des LONGTEXT, date DATE, remindme BOOLEAN DEFAULT FALSE, tag VARCHAR(10), status VARCHAR(10) DEFAULT 'open', priority VARCHAR(20) DEFAULT 'normal')" ) print("Tabelle nexory_user_tasks überprüft/erstellt") await cur.execute( "CREATE TABLE IF NOT EXISTS nexory_guild_tasks(" - "guildID BIGINT, title VARCHAR(50), des LONGTEXT, date DATE, remindme BOOLEAN DEFAULT FALSE)" + "guildID BIGINT, title VARCHAR(50), des LONGTEXT, date DATE, remindme BOOLEAN DEFAULT FALSE, tag VARCHAR(10), status VARCHAR(10) DEFAULT 'open', priority VARCHAR(20) DEFAULT 'normal')" ) print("Tabelle nexory_guild_tasks überprüft/erstellt") await cur.execute( "CREATE TABLE IF NOT EXISTS nexory_guild_config (guildID BIGINT, reminde_channel BIGINT, mode TEXT)" ) - print("Tabelle nexory_guild_config überprüft/erstellt" ) + print("Tabelle nexory_guild_config überprüft/erstellt") await cur.execute( "CREATE TABLE IF NOT EXISTS nexory_user_config (userID BIGINT, mode TEXT)" ) - print("Tabelle nexory_user_config überprüft/erstellt" ) + print("Tabelle nexory_user_config überprüft/erstellt") + await cur.execute( + "CREATE TABLE IF NOT EXISTS nexory_guild_custom_tags (guildID BIGINT, tag VARCHAR(10))" + ) + print("Tabelle nexory_guild_custom_tags überprüft/erstellt") async def on_ready(self): From 41532931468bb05be3b36c25e093e487ce76913e Mon Sep 17 00:00:00 2001 From: Luca <87448287+LUCA-PYTHON@users.noreply.github.com> Date: Sat, 14 Mar 2026 19:45:57 +0100 Subject: [PATCH 4/4] Add filter and set_status commands for tasks --- datein/tasks.py | 56 +++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 56 insertions(+) diff --git a/datein/tasks.py b/datein/tasks.py index e61d585..ba076d0 100644 --- a/datein/tasks.py +++ b/datein/tasks.py @@ -762,6 +762,7 @@ async def create_user(self, interaction: discord.Interaction): view.message = await interaction.response.send_message(view=view) @task.command(name="guild", description="Create a new Guild-Task") + @commands.guild_only() @commands.has_permissions(administrator=True) async def create_guild(self, interaction: discord.Interaction): view = TaskView(self.bot, "guild", guild_id=interaction.guild.id) @@ -868,6 +869,7 @@ async def before_reminder(self): @task.command(name="list", description="List all tasks for the user or guild") + @commands.guild_only() async def list_tasks(self, interaction: discord.Interaction, scope: Literal["guild", "user"]): if scope == "user": table_name = "nexory_user_tasks" @@ -894,5 +896,59 @@ async def list_tasks(self, interaction: discord.Interaction, scope: Literal["gui await interaction.response.send_message(embed=view.get_embed(), view=view, ephemeral=True) + @task.command(name="filter", description="Filter tasks by tag/status/user/priority") + @commands.guild_only() + async def filter_tasks(self, interaction: discord.Interaction, scope: Literal["guild", "user"], filter_by: Literal["tag", "status", "priority"], filter_value: str): + if scope == "user": + table_name = "nexory_user_tasks" + id_value = interaction.user.id + id_field = "userID" + else: + table_name = "nexory_guild_tasks" + id_value = interaction.guild.id + id_field = "guildID" + + async with self.bot.pool.acquire() as conn: + async with conn.cursor() as cur: + await cur.execute( + f"SELECT title, des, date FROM {table_name} WHERE {id_field}=%s AND {filter_by}=%s", + (id_value, filter_value) + ) + rows = await cur.fetchall() + + if not rows: + await interaction.response.send_message("No tasks found with that filter.", ephemeral=True) + return + + message = "" + for row in rows: + title, des, date = row + message += f"**{title}** - {des} ({date})\n" + + await interaction.response.send_message(message[:2000], ephemeral=True) + + + @task.command(name="set_status", description="Set the status of a task (open/working/closed)") + async def set_status(self, interaction: discord.Interaction, scope: Literal["guild", "user"], title: str, status: Literal["open", "working", "closed"]): + if scope == "user": + table_name = "nexory_user_tasks" + id_value = interaction.user.id + id_field = "userID" + else: + table_name = "nexory_guild_tasks" + id_value = interaction.guild.id + id_field = "guildID" + + async with self.bot.pool.acquire() as conn: + async with conn.cursor() as cur: + await cur.execute( + f"UPDATE {table_name} SET status=%s WHERE {id_field}=%s AND title=%s", + (status, id_value, title) + ) + await conn.commit() + + await interaction.response.send_message(f"Status of task '{title}' set to '{status}'.", ephemeral=True) + + async def setup(bot: commands.Bot): await bot.add_cog(tasks(bot))