mirror of
https://github.com/Lilac-Rose/Lacie.git
synced 2026-07-22 02:21:33 -05:00
237 lines
9.1 KiB
Python
237 lines
9.1 KiB
Python
import discord
|
|
from discord import app_commands
|
|
from discord.ext import commands, tasks
|
|
import aiosqlite
|
|
from datetime import datetime, timedelta, timezone
|
|
import re
|
|
from pathlib import Path
|
|
|
|
|
|
def parse_timeframe(timeframe: str) -> timedelta:
|
|
"""Parse strings like '1h', '2d', '3w', '30m' into timedelta."""
|
|
pattern = r"(\d+)\s*(s|m|h|d|w)"
|
|
match = re.fullmatch(pattern, timeframe.strip().lower())
|
|
if not match:
|
|
raise ValueError("Invalid time format. Use something like '10m', '2h', '3d', or '1w'.")
|
|
|
|
value, unit = match.groups()
|
|
value = int(value)
|
|
match unit:
|
|
case "s":
|
|
return timedelta(seconds=value)
|
|
case "m":
|
|
return timedelta(minutes=value)
|
|
case "h":
|
|
return timedelta(hours=value)
|
|
case "d":
|
|
return timedelta(days=value)
|
|
case "w":
|
|
return timedelta(weeks=value)
|
|
raise ValueError("Invalid time unit.")
|
|
|
|
|
|
class ReminderCog(commands.Cog):
|
|
def __init__(self, bot):
|
|
self.bot = bot
|
|
# Database will be stored next to this Python file
|
|
self.db_path = Path(__file__).parent / "reminders.db"
|
|
|
|
async def setup_database(self):
|
|
async with aiosqlite.connect(self.db_path) as db:
|
|
await db.execute(
|
|
"""CREATE TABLE IF NOT EXISTS reminders (
|
|
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
|
user_id INTEGER,
|
|
message TEXT,
|
|
remind_at TEXT
|
|
)"""
|
|
)
|
|
await db.commit()
|
|
|
|
async def cog_load(self):
|
|
"""Called automatically when the cog is added — safe place to start background tasks."""
|
|
await self.setup_database()
|
|
if not self.check_reminders.is_running():
|
|
self.check_reminders.start()
|
|
|
|
# Create reminder command group
|
|
reminder_group = app_commands.Group(name="reminder", description="Manage your reminders")
|
|
|
|
@reminder_group.command(name="set", description="Set a reminder and get a DM when it's time")
|
|
@app_commands.describe(
|
|
timeframe="How long until reminder (e.g., '10m', '2h', '3d', '1w')",
|
|
message="What to remind you about"
|
|
)
|
|
async def reminder_set(self, interaction: discord.Interaction, timeframe: str, message: str):
|
|
try:
|
|
try:
|
|
delta = parse_timeframe(timeframe)
|
|
except ValueError as e:
|
|
return await interaction.response.send_message(str(e), ephemeral=True)
|
|
|
|
remind_at = datetime.now(timezone.utc) + delta
|
|
async with aiosqlite.connect(self.db_path) as db:
|
|
await db.execute(
|
|
"INSERT INTO reminders (user_id, message, remind_at) VALUES (?, ?, ?)",
|
|
(interaction.user.id, message, remind_at.isoformat()),
|
|
)
|
|
await db.commit()
|
|
|
|
# Format the time nicely
|
|
unix_time = int(remind_at.timestamp())
|
|
time_str = f"<t:{unix_time}:R>"
|
|
await interaction.response.send_message(
|
|
f"✅ Reminder set! I'll DM you about **'{message}'** {time_str}.",
|
|
ephemeral=True
|
|
)
|
|
except Exception as e:
|
|
print(f"Error in reminder_set: {e}")
|
|
if not interaction.response.is_done():
|
|
await interaction.response.send_message(f"❌ Error: {e}", ephemeral=True)
|
|
|
|
@reminder_group.command(name="list", description="View your active reminders")
|
|
async def reminder_list(self, interaction: discord.Interaction):
|
|
async with aiosqlite.connect(self.db_path) as db:
|
|
async with db.execute(
|
|
"SELECT id, message, remind_at FROM reminders WHERE user_id = ? ORDER BY remind_at",
|
|
(interaction.user.id,),
|
|
) as cursor:
|
|
rows = await cursor.fetchall()
|
|
|
|
if not rows:
|
|
return await interaction.response.send_message(
|
|
"You have no active reminders!", ephemeral=True
|
|
)
|
|
|
|
embed = discord.Embed(
|
|
title="📝 Your Reminders",
|
|
color=discord.Color.blurple(),
|
|
timestamp=datetime.now(timezone.utc)
|
|
)
|
|
|
|
for reminder_id, message, remind_at in rows:
|
|
remind_time = datetime.fromisoformat(remind_at)
|
|
unix_time = int(remind_time.timestamp())
|
|
time_str = f"<t:{unix_time}:R>"
|
|
|
|
# Calculate time remaining
|
|
now = datetime.now(timezone.utc)
|
|
time_diff = remind_time - now
|
|
|
|
if time_diff.total_seconds() > 0:
|
|
days = time_diff.days
|
|
hours, remainder = divmod(time_diff.seconds, 3600)
|
|
minutes, _ = divmod(remainder, 60)
|
|
|
|
if days > 0:
|
|
time_remaining = f"in {days}d {hours}h"
|
|
elif hours > 0:
|
|
time_remaining = f"in {hours}h {minutes}m"
|
|
else:
|
|
time_remaining = f"in {minutes}m"
|
|
else:
|
|
time_remaining = "overdue"
|
|
|
|
embed.add_field(
|
|
name=f"ID: {reminder_id} - {message}",
|
|
value=f"⏰ {time_str} ({time_remaining})",
|
|
inline=False
|
|
)
|
|
|
|
embed.set_footer(text="Use /reminder remove <id> to delete a reminder")
|
|
await interaction.response.send_message(embed=embed, ephemeral=True)
|
|
|
|
@reminder_group.command(name="remove", description="Remove a specific reminder by ID")
|
|
@app_commands.describe(reminder_id="The ID of the reminder to remove (from /reminder list)")
|
|
async def reminder_remove(self, interaction: discord.Interaction, reminder_id: int):
|
|
async with aiosqlite.connect(self.db_path) as db:
|
|
# First check if the reminder exists and belongs to the user
|
|
async with db.execute(
|
|
"SELECT message FROM reminders WHERE id = ? AND user_id = ?",
|
|
(reminder_id, interaction.user.id),
|
|
) as cursor:
|
|
row = await cursor.fetchone()
|
|
|
|
if not row:
|
|
return await interaction.response.send_message(
|
|
f"❌ Reminder with ID {reminder_id} not found or doesn't belong to you.",
|
|
ephemeral=True
|
|
)
|
|
|
|
message = row[0]
|
|
|
|
# Delete the reminder
|
|
await db.execute(
|
|
"DELETE FROM reminders WHERE id = ? AND user_id = ?",
|
|
(reminder_id, interaction.user.id),
|
|
)
|
|
await db.commit()
|
|
|
|
await interaction.response.send_message(
|
|
f"✅ Removed reminder: **'{message}'**",
|
|
ephemeral=True
|
|
)
|
|
|
|
@reminder_group.command(name="clear", description="Remove all your active reminders")
|
|
async def reminder_clear(self, interaction: discord.Interaction):
|
|
async with aiosqlite.connect(self.db_path) as db:
|
|
# Check how many reminders the user has
|
|
async with db.execute(
|
|
"SELECT COUNT(*) FROM reminders WHERE user_id = ?",
|
|
(interaction.user.id,),
|
|
) as cursor:
|
|
count = (await cursor.fetchone())[0]
|
|
|
|
if count == 0:
|
|
return await interaction.response.send_message(
|
|
"You have no active reminders to clear!",
|
|
ephemeral=True
|
|
)
|
|
|
|
# Delete all reminders for this user
|
|
await db.execute(
|
|
"DELETE FROM reminders WHERE user_id = ?",
|
|
(interaction.user.id,),
|
|
)
|
|
await db.commit()
|
|
|
|
await interaction.response.send_message(
|
|
f"✅ Cleared all {count} reminder(s)!",
|
|
ephemeral=True
|
|
)
|
|
|
|
@tasks.loop(seconds=60)
|
|
async def check_reminders(self):
|
|
"""Runs every minute, checks and delivers reminders."""
|
|
now = datetime.now(timezone.utc)
|
|
async with aiosqlite.connect(self.db_path) as db:
|
|
async with db.execute(
|
|
"SELECT id, user_id, message FROM reminders WHERE remind_at <= ?",
|
|
(now.isoformat(),),
|
|
) as cursor:
|
|
reminders_due = await cursor.fetchall()
|
|
|
|
for reminder_id, user_id, message in reminders_due:
|
|
user = self.bot.get_user(user_id)
|
|
if user:
|
|
try:
|
|
embed = discord.Embed(
|
|
title="⏰ Reminder!",
|
|
description=message,
|
|
color=discord.Color.blue(),
|
|
timestamp=datetime.now(timezone.utc)
|
|
)
|
|
await user.send(embed=embed)
|
|
except discord.Forbidden:
|
|
pass # user has DMs disabled or bot blocked
|
|
|
|
await db.execute("DELETE FROM reminders WHERE id = ?", (reminder_id,))
|
|
await db.commit()
|
|
|
|
@check_reminders.before_loop
|
|
async def before_check_reminders(self):
|
|
await self.bot.wait_until_ready()
|
|
|
|
|
|
async def setup(bot):
|
|
await bot.add_cog(ReminderCog(bot)) |