Skip to content
This repository has been archived by the owner on Sep 17, 2024. It is now read-only.

Commit

Permalink
Make Reminder Manager helper
Browse files Browse the repository at this point in the history
  • Loading branch information
ASmallSquishySquid committed Jan 22, 2024
1 parent c824342 commit dc2e695
Show file tree
Hide file tree
Showing 2 changed files with 152 additions and 99 deletions.
133 changes: 34 additions & 99 deletions cogs/reminders.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,22 +2,19 @@
import os
from typing import List, Optional

import asyncio
import discord
from discord import app_commands
from discord.app_commands import locale_str as _T
from discord.ext import commands
from discord.ext import tasks

from helpers import constants
from helpers.database import Database
from helpers.pagebuttons import PageButtons
from helpers.remindermanager import ReminderManager

class Reminders(commands.Cog):
def __init__(self, bot: commands.Bot):
self.bot = bot
self.reminder_cache = []
self.lock = asyncio.Lock()
self.get_reminders.start()

async def cog_unload(self):
Expand All @@ -27,35 +24,30 @@ async def cog_unload(self):
# every day, on startup time
@tasks.loop(hours=24)
async def get_reminders(self):
async with self.lock:
self.reminder_cache = Database.select("*", "reminders", """WHERE date >= datetime("now", "localtime") AND date < datetime("now", "localtime", "1 day")""")
ReminderManager.rebuild()

if not self.send_reminders.is_running():
self.send_reminders.start()

@tasks.loop(minutes=1)
async def send_reminders(self):
later = []
async with self.lock:
for reminder in self.reminder_cache:
if reminder[3] < (datetime.datetime.now() + datetime.timedelta(minutes=1)):
requester = self.bot.get_user(reminder[1])
if requester is None:
requester = await self.bot.fetch_user(reminder[1])

embed_message = discord.Embed(
title=f"Reminder! {constants.DEFAULT_EMOTE}",
description=reminder[2],
color=discord.Color.og_blurple())
embed_message.add_field(
name="Time",
value=discord.utils.format_dt(reminder[3], style="R"))

buttons = SnoozeButtons(self, reminder[2])
buttons.message = await requester.send(embed=embed_message, view=buttons)
else:
later.append(reminder)
self.reminder_cache = later
upcoming = ReminderManager.get_all_upcoming_reminders()

for reminder in upcoming:
requester = self.bot.get_user(reminder[1])
if requester is None:
requester = await self.bot.fetch_user(reminder[1])

embed_message = discord.Embed(
title=f"Reminder! {constants.DEFAULT_EMOTE}",
description=reminder[2],
color=discord.Color.og_blurple())
embed_message.add_field(
name="Time",
value=discord.utils.format_dt(reminder[3], style="R"))

buttons = SnoozeButtons(self, reminder[2])
buttons.message = await requester.send(embed=embed_message, view=buttons)

@commands.hybrid_group(
description=_T("reminders"),
Expand All @@ -79,9 +71,7 @@ async def reminders(self, ctx: commands.Context,
description="Whether to show the reminder IDs")):

now = datetime.datetime.now()
total = Database.count(
"reminders",
f"""WHERE userId = {ctx.author.id} AND date > "{now}" """)
total = ReminderManager.get_upcoming_reminders_count(ctx.author.id)

if total == 0:
await ctx.send(f"There are no upcoming reminders {constants.DEFAULT_EMOTE}")
Expand Down Expand Up @@ -158,17 +148,7 @@ async def set(self, ctx: commands.Context,
elif lower_component.endswith("s"):
date_param = date_param + datetime.timedelta(seconds=int(lower_component[:-1]))

Database.insert(
"reminders(userId, reminder, date)",
f"""{ctx.author.id}, "{reminder}", "{date_param}" """)

reminder_id = Database.select(
"id", "reminders",
f"""WHERE date = "{date_param}" AND reminder = "{reminder}" AND userId = {ctx.author.id}""")[0][0]

if date_param < (datetime.datetime.now() + datetime.timedelta(days=1)):
async with self.lock:
self.reminder_cache.append((reminder_id, ctx.author.id, reminder, date_param))
reminder_id = ReminderManager.add_reminder(reminder, date_param, ctx.author.id)

embed_message = discord.Embed(
title=f"Reminder created {constants.DEFAULT_EMOTE}",
Expand All @@ -178,7 +158,7 @@ async def set(self, ctx: commands.Context,
name="Scheduled Time",
value=discord.utils.format_dt(date_param))

buttons = DeleteButton(self, embed_message, reminder_id)
buttons = DeleteButton(embed_message, reminder_id)
buttons.message = await ctx.send(embed=embed_message, view=buttons)

@set.autocomplete("when")
Expand Down Expand Up @@ -219,7 +199,7 @@ async def remind_error(self, ctx, error):
@app_commands.default_permissions()
@app_commands.guilds(int(os.getenv(constants.TEST_SERVER_ENV)))
async def debug(self, ctx: commands.Context):
await ctx.send(self.reminder_cache)
await ctx.send(ReminderManager.get_cache())

@reminders.command(
description=_T("delete"),
Expand All @@ -233,62 +213,33 @@ async def delete(self, ctx: commands.Context,
displayed_name="reminder ID",
description="The ID of the reminder being deleted")):

reminders = Database.select(
"*", "reminders",
f"WHERE id = {reminder_id} and userId = {ctx.author.id}")
if len(reminders) == 0:
if not ReminderManager.remove_reminder(ctx.author.id, reminder_id):
await ctx.send(f"You can't delete reminders that aren't yours! {constants.DEFAULT_EMOTE}")
return

Database.delete("reminders", f"WHERE id = {reminder_id}")

await self.remove_from_reminders(reminder_id)

await ctx.send(f"Ok, deleted reminder {reminder_id} {constants.DEFAULT_EMOTE}")

@delete.autocomplete("reminder_id")
async def delete_autocomplete(self, interaction: discord.Interaction,
current: int,) -> List[app_commands.Choice[int]]:

top_5_ids = Database.select(
"id", "reminders",
f"""WHERE userId = {interaction.user.id} AND date > "{datetime.datetime.now()}"
ORDER BY date
LIMIT 5
""")
top_5 = ReminderManager.get_reminder_page(
interaction.user.id, datetime.datetime.now(), 5, 0)

matched = [
app_commands.Choice(name=reminder_id[0], value=reminder_id[0])
for reminder_id in top_5_ids if str(current) in str(reminder_id[0])
app_commands.Choice(name=reminder[0], value=reminder[0])
for reminder in top_5 if str(current) in str(reminder[0])
]

return matched

async def remove_from_reminders(self, reminder_id: int):
"""Removes the reminder with the provided id from the cache
Args:
id (int): The reminder ID
"""
async with self.lock:
for i in range(len(self.reminder_cache)):
if self.reminder_cache[i][0] == reminder_id:
self.reminder_cache.remove(self.reminder_cache[i])
break

def build_reminders_embed(self, user_id: int,
time_now: datetime.datetime, count: int,
index: int, total:int, show_id: bool) -> discord.Embed:

# Don't want to load math module into memory for one function
num_pages = int(total / count) + (1 if total % count != 0 else 0)

reminder_page = Database.select(
"id, reminder, date", "reminders",
f"""WHERE userId = {user_id} AND date > "{time_now}"
ORDER BY date
LIMIT {count} OFFSET {index * count}
""")
reminder_page = ReminderManager.get_reminder_page(user_id, time_now, count, index)

if show_id:
ids = "\n".join([str(reminder[0]) for reminder in reminder_page])
Expand Down Expand Up @@ -346,19 +297,7 @@ async def __add_time(self: discord.ui.View, interaction: discord.Interaction, de

date_param = datetime.datetime.now() + datetime.timedelta(minutes=delay)

Database.insert(
"reminders(userId, reminder, date)",
f"""{interaction.user.id}, "{self.reminder}", "{date_param}" """)

reminder_id = Database.select(
"id", "reminders",
f"""WHERE date = "{date_param}"
AND reminder = "{self.reminder}"
AND userId = {interaction.user.id}""")[0][0]

if date_param < (datetime.datetime.now() + datetime.timedelta(days=1)):
self.reminder_instance.reminder_cache.append(
(reminder_id, interaction.user.id, self.reminder, date_param))
ReminderManager.add_reminder(self.reminder, date_param, interaction.user.id)

embed_message = discord.Embed(
title=f"Reminder snoozed {constants.DEFAULT_EMOTE}",
Expand All @@ -370,9 +309,8 @@ async def __add_time(self: discord.ui.View, interaction: discord.Interaction, de
await interaction.response.edit_message(view=self, embed=embed_message)

class DeleteButton(discord.ui.View):
def __init__(self, reminder_instance: Reminders, embed_message: discord.Embed, reminder_id: int):
def __init__(self, embed_message: discord.Embed, reminder_id: int):
super().__init__(timeout=60)
self.reminder_instance = reminder_instance
self.embed_message = embed_message
self.id = reminder_id

Expand All @@ -389,12 +327,9 @@ async def on_timeout(self):
async def delete_button(self, interaction: discord.Interaction, button: discord.ui.Button):
button.disabled = True

Database.delete("reminders", f"WHERE id = {self.id}")

await self.reminder_instance.remove_from_reminders(self.id)

self.embed_message.title = "Reminder DELETED <:romani_nervous:746062766825013269>"
await interaction.response.edit_message(embed=self.embed_message, view=self)
if ReminderManager.remove_reminder(interaction.user.id, self.id):
self.embed_message.title = "Reminder DELETED <:romani_nervous:746062766825013269>"
await interaction.response.edit_message(embed=self.embed_message, view=self)

async def setup(bot: commands.Bot):
await bot.add_cog(Reminders(bot))
118 changes: 118 additions & 0 deletions helpers/remindermanager.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,118 @@
import datetime

from helpers.database import Database

class ReminderManager():
reminder_cache = []

@classmethod
def add_reminder(cls, reminder: str, date: datetime.datetime, author_id: int) -> int:
"""Stores a reminder.
Args:
reminder (str): The reminder
date (datetime.datetime): The time
author_id (int): The author
Returns:
int: The reminder ID
"""
Database.insert(
"reminders(userId, reminder, date)",
f"""{author_id}, "{reminder}", "{date}" """)

reminder_id = Database.select(
"id", "reminders",
f"""WHERE date = "{date}" AND reminder = "{reminder}" AND userId = {author_id}""")[0][0]

if date < (datetime.datetime.now() + datetime.timedelta(days=1)):
cls.reminder_cache.append((reminder_id, author_id, reminder, date))

return reminder_id

@classmethod
def get_cache(cls) -> list:
"""Returns:
list: The reminder cache
"""
return cls.reminder_cache

@staticmethod
def get_reminder_page(user_id: int, time: datetime.datetime, count: int, index: int) -> list:
"""Get a page of reminders
Args:
user_id (int): The user's ID
time (datetime.datetime): The minimum time
count (int): The number of reminders per page
index (int): The page number
Returns:
list: A list of reminders
"""
return Database.select(
"id, reminder, date", "reminders",
f"""WHERE userId = {user_id} AND date > "{time}"
ORDER BY date
LIMIT {count} OFFSET {index * count}
""")

@classmethod
def get_all_upcoming_reminders(cls) -> list:
"""Gets the reminders coming up in the next minute
Returns:
list: The upcoming reminders
"""
later = []
upcoming = []
for reminder in cls.reminder_cache:
if reminder[3] < (datetime.datetime.now() + datetime.timedelta(minutes=1)):
upcoming.append(reminder)
else:
later.append(reminder)

cls.reminder_cache = later
return upcoming

@staticmethod
def get_upcoming_reminders_count(user_id: int) -> int:
"""
Args:
user_id (int): The user's id
Returns:
int: The number of upcoming reminders
"""
return Database.count(
"reminders",
f"""WHERE userId = {user_id} AND date > "{datetime.datetime.now()}" """)

@classmethod
def rebuild(cls):
"""rebuilds the reminder cache"""
cls.reminder_cache = Database.select(
"*", "reminders",
"""WHERE date >= datetime("now", "localtime") AND date < datetime("now", "localtime", "1 day")""")

@classmethod
def remove_reminder(cls, user_id: int, reminder_id: int) -> bool:
"""Remove a reminder.
Args:
reminder_id (int): The id of the reminder
"""
reminders = Database.select(
"*", "reminders",
f"WHERE id = {reminder_id} and userId = {user_id}")
if len(reminders) == 0:
return False

Database.delete("reminders", f"WHERE id = {reminder_id}")

for i in range(len(cls.reminder_cache)):
if cls.reminder_cache[i][0] == reminder_id:
cls.reminder_cache.remove(cls.reminder_cache[i])
break

return True

0 comments on commit dc2e695

Please sign in to comment.