From e340cf2def5f7a17c2eb3e8b81f4dde47dabe9b7 Mon Sep 17 00:00:00 2001 From: Lilac-Rose Date: Wed, 12 Nov 2025 00:09:04 +0100 Subject: [PATCH] made it so commits can get sent to multiple channels and consolodated the reminder cog --- events/git_webhook.py | 45 ++++++++++----- reminders/reminder.py | 130 +++++++++++++++++++++++++++++++++++++----- 2 files changed, 146 insertions(+), 29 deletions(-) diff --git a/events/git_webhook.py b/events/git_webhook.py index 45499e0..a8ffdab 100644 --- a/events/git_webhook.py +++ b/events/git_webhook.py @@ -8,7 +8,8 @@ from dotenv import load_dotenv load_dotenv() -COMMIT_CHANNEL_ID = 876777562599194644 +# Multiple channels to send commit notifications to +COMMIT_CHANNEL_IDS = [876777562599194644, 1437941632849940563] USER_ID_TO_PING = 252130669919076352 WEBHOOK_SECRET = os.getenv("GITHUB_WEBHOOK_SECRET", "") WEBHOOK_PORT = int(os.getenv("WEBHOOK_PORT", 5000)) @@ -30,6 +31,7 @@ class GitWebhook(commands.Cog): await self.site.start() print(f"✅ Git webhook server started on port {WEBHOOK_PORT}") print(f" Configure GitHub to send webhooks to: http://185.187.170.61:{WEBHOOK_PORT}/webhook") + print(f" Sending notifications to {len(COMMIT_CHANNEL_IDS)} channel(s)") async def cog_unload(self): """Stop the webhook server when cog unloads.""" @@ -74,20 +76,27 @@ class GitWebhook(commands.Cog): print("✅ Received GitHub ping event - webhook is configured correctly!") return web.json_response({"status": "pong"}, status=200) - # Get the Discord channel - channel = self.bot.get_channel(COMMIT_CHANNEL_ID) - if not channel: - print(f"❌ Channel {COMMIT_CHANNEL_ID} not found!") - return web.json_response({"error": "Channel not found"}, status=500) + # Get all Discord channels + channels = [] + for channel_id in COMMIT_CHANNEL_IDS: + channel = self.bot.get_channel(channel_id) + if channel: + channels.append(channel) + else: + print(f"⚠️ Channel {channel_id} not found!") + + if not channels: + print(f"❌ No valid channels found!") + return web.json_response({"error": "No channels found"}, status=500) # Handle GitHub push events if 'commits' in data and 'repository' in data: - await self.handle_github_push(data, channel) + await self.handle_github_push(data, channels) return web.json_response({"status": "success"}, status=200) # Handle GitLab push events elif 'project' in data and 'commits' in data: - await self.handle_gitlab_push(data, channel) + await self.handle_gitlab_push(data, channels) return web.json_response({"status": "success"}, status=200) print(f"⚠️ Unknown webhook format. Keys in data: {list(data.keys())}") @@ -99,7 +108,7 @@ class GitWebhook(commands.Cog): traceback.print_exc() return web.json_response({"error": str(e)}, status=500) - async def handle_github_push(self, data, channel): + async def handle_github_push(self, data, channels): """Handle GitHub push webhook.""" repo_name = data['repository']['full_name'] repo_url = data['repository']['html_url'] @@ -154,10 +163,14 @@ class GitWebhook(commands.Cog): embed.set_author(name=pusher, icon_url="https://github.githubassets.com/images/modules/logos_page/GitHub-Mark.png") embed.set_footer(text="GitHub") - # Send to Discord - await channel.send(f"<@{USER_ID_TO_PING}>", embed=embed) + # Send to all Discord channels + for channel in channels: + try: + await channel.send(f"<@{USER_ID_TO_PING}>", embed=embed) + except Exception as e: + print(f"❌ Failed to send to channel {channel.id}: {e}") - async def handle_gitlab_push(self, data, channel): + async def handle_gitlab_push(self, data, channels): """Handle GitLab push webhook.""" repo_name = data['project']['path_with_namespace'] repo_url = data['project']['web_url'] @@ -211,8 +224,12 @@ class GitWebhook(commands.Cog): embed.set_author(name=pusher) embed.set_footer(text="GitLab") - # Send to Discord - await channel.send(f"<@{USER_ID_TO_PING}>", embed=embed) + # Send to all Discord channels + for channel in channels: + try: + await channel.send(f"<@{USER_ID_TO_PING}>", embed=embed) + except Exception as e: + print(f"❌ Failed to send to channel {channel.id}: {e}") async def setup(bot): await bot.add_cog(GitWebhook(bot)) \ No newline at end of file diff --git a/reminders/reminder.py b/reminders/reminder.py index cb9b39d..38a331d 100644 --- a/reminders/reminder.py +++ b/reminders/reminder.py @@ -33,7 +33,7 @@ def parse_timeframe(timeframe: str) -> timedelta: class ReminderCog(commands.Cog): def __init__(self, bot): self.bot = bot - # ✅ Database will be stored next to this Python file + # Database will be stored next to this Python file self.db_path = Path(__file__).parent / "reminders.db" async def setup_database(self): @@ -54,9 +54,16 @@ class ReminderCog(commands.Cog): if not self.check_reminders.is_running(): self.check_reminders.start() - @app_commands.command(name="remind", description="Set a reminder and get a DM when it's time.") - async def remind(self, interaction: discord.Interaction, timeframe: str, reminder: str): - """Example: /remind 1h Take a break""" + # 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): + """Example: /reminder set 1h Take a break""" try: delta = parse_timeframe(timeframe) except ValueError as e: @@ -66,19 +73,22 @@ class ReminderCog(commands.Cog): async with aiosqlite.connect(self.db_path) as db: await db.execute( "INSERT INTO reminders (user_id, message, remind_at) VALUES (?, ?, ?)", - (interaction.user.id, reminder, remind_at.isoformat()), + (interaction.user.id, message, remind_at.isoformat()), ) await db.commit() + # Format the time nicely + time_str = remind_at.strftime("%Y-%m-%d %H:%M UTC") await interaction.response.send_message( - f"Got it! I'll DM you about **'{reminder}'** in {timeframe}.", ephemeral=True + f"✅ Reminder set! I'll DM you about **'{message}'** at {time_str} ({timeframe} from now).", + ephemeral=True ) - @app_commands.command(name="reminders", description="View your active reminders.") - async def reminders(self, interaction: discord.Interaction): + @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 message, remind_at FROM reminders WHERE user_id = ? ORDER BY remind_at", + "SELECT id, message, remind_at FROM reminders WHERE user_id = ? ORDER BY remind_at", (interaction.user.id,), ) as cursor: rows = await cursor.fetchall() @@ -89,17 +99,101 @@ class ReminderCog(commands.Cog): ) embed = discord.Embed( - title="Your Reminders", + title="📝 Your Reminders", color=discord.Color.blurple(), timestamp=datetime.now(timezone.utc) ) - for message, remind_at in rows: - remind_time = datetime.fromisoformat(remind_at).strftime("%Y-%m-%d %H:%M UTC") - embed.add_field(name=message, value=f"⏰ {remind_time}", inline=False) + for reminder_id, message, remind_at in rows: + remind_time = datetime.fromisoformat(remind_at) + time_str = remind_time.strftime("%Y-%m-%d %H:%M UTC") + + # 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 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.""" @@ -115,7 +209,13 @@ class ReminderCog(commands.Cog): user = self.bot.get_user(user_id) if user: try: - await user.send(f"⏰ **Reminder:** {message}") + 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 @@ -128,4 +228,4 @@ class ReminderCog(commands.Cog): async def setup(bot): - await bot.add_cog(ReminderCog(bot)) + await bot.add_cog(ReminderCog(bot)) \ No newline at end of file