From 99a0a15b9ccd3f14b59a3090f28d675dd711ba98 Mon Sep 17 00:00:00 2001
From: 0ry5 <oscar.bloch@posteo.de>
Date: Sat, 12 Apr 2025 00:18:51 +0200
Subject: [PATCH] feat(bot): support recurring events

---
 bot.py | 102 ++++++++++++++++++++++++++++-----------------------------
 1 file changed, 50 insertions(+), 52 deletions(-)

diff --git a/bot.py b/bot.py
index d1457a5..d4c314e 100644
--- a/bot.py
+++ b/bot.py
@@ -1,20 +1,21 @@
 import json
 import re
+
+# needs the following version of discord.py for recurring events to work: https://github.com/DA-344/d.py/tree/recurrent_events
 import discord
-from discord.ext import commands
+from discord.ext import commands, tasks
 from dotenv import load_dotenv
 from os import getenv
-from github_connector import create_or_update_file, delete_file
+from github_connector import create_or_update_file, delete_file, similar_exists
 
 
 load_dotenv("./.env")
 
 CHANNEL_ID = int(getenv("CHANNEL_ID"))
+GUILD_ID = int(getenv("GUILD_ID"))
 MAINTAINERS = json.loads(getenv("MAINTAINERS"))
 DISCORD_TOKEN = getenv("DISCORD_TOKEN")
 
-print(MAINTAINERS)
-
 # Intents are required to listen to events like messages
 intents = discord.Intents.default()
 intents.message_content = True
@@ -26,7 +27,7 @@ intents.dm_messages = True
 bot = commands.Bot(command_prefix="!", intents=intents)
 
 
-def getFileName(event):
+def getCommitMessage(event):
     return f"{event.start_time.strftime("%Y-%m-%d")}_{event.name}"
 
 
@@ -35,7 +36,7 @@ def getFilePath(event):
 
 
 def getEventYML(event):
-    return f'# event imported from discord manual changes may be overwritten\n---\n \nlayout: event\ntitle: "{event.name}"\nauthor: "Netz39 e.V." \nevent:\n  start: {event.start_time.strftime("%Y-%m-%d %H:%M:%S")} \n  end:   {event.end_time.strftime("%Y-%m-%d %H:%M:%S")} \n  organizer: "Netz39 Team <kontakt@netz39.de>" \n  location: "Leibnizstr. 18, 39104 Magdeburg"\n---'
+    return f'<!-- event imported from discord manual changes may be overwritten\n -->---\n \nlayout: event\ntitle: "{event.name}"\nauthor: "Netz39 e.V." \nevent:\n  start: {event.start_time.strftime("%Y-%m-%d %H:%M:%S")} \n  end:   {event.end_time.strftime("%Y-%m-%d %H:%M:%S")} \n  organizer: "Netz39 Team <kontakt@netz39.de>" \n  location: "Leibnizstr. 18, 39104 Magdeburg"\n  frequency: "{event.recurrence_rule.frequency}"\n  interval: {event.recurrence_rule.interval} ---'
 
 
 def addMaintainer(maintainer):
@@ -48,38 +49,51 @@ def addMaintainer(maintainer):
         fileW.writelines(data)
 
 
+@tasks.loop(hours=24)  # Runs every 7 days
+async def check_events():
+    await bot.wait_until_ready()
+    guild = bot.get_guild(GUILD_ID)
+    channel = guild.get_channel(CHANNEL_ID)
+
+    if not channel:
+        return
+
+    scheduledEvents = await guild.fetch_scheduled_events()
+    for event in scheduledEvents:
+        await handleEvent(event)
+
+
 async def handleEvent(event):
     eventYML = getEventYML(event)
-    if "netz39" in event.location.lower() or (
-        "leibniz" in event.location.lower() and "18" in event.location
-    ):
-        if event.creator_id in MAINTAINERS:
-            create_or_update_file(
-                getFilePath(event),
-                eventYML,
-                f"new event: {getFileName(event)}",
+    file_path = getFilePath(event)
+    commit_message = getCommitMessage(event)
+    if event.creator_id in MAINTAINERS or similar_exists(event.name):
+        create_or_update_file(
+            file_path,
+            eventYML,
+            f"new event: {commit_message}",
+        )
+        channel = bot.get_channel(CHANNEL_ID)
+        await channel.send(
+            f"📅 New event '{event.name}' on {event.start_time.strftime("%Y-%m-%d")}."
+        )
+    else:
+        eventJSON = json.dumps(
+            {
+                "yml": eventYML,
+                "path": file_path,
+                "fileName": commit_message,
+                "name": event.name,
+                "date": event.start_time.strftime("%Y-%m-%d"),
+            },
+            indent=2,
+        )
+        for maintainer in MAINTAINERS:
+            user = await bot.fetch_user(event.creator_id)
+            await sendDm(
+                maintainer,
+                f"{user.name} added a new Event on {event.start_time.strftime("%Y")}: {event.name}. Like this message to approve.\n\n\n```json\n{eventJSON}```",
             )
-            channel = bot.get_channel(CHANNEL_ID)
-            await channel.send(
-                f"📅 New event '{event.name}' on {event.start_time.strftime("%Y-%m-%d")}."
-            )
-        else:
-            eventJSON = json.dumps(
-                {
-                    "yml": eventYML,
-                    "path": getFilePath(event),
-                    "fileName": getFileName(event),
-                    "name": event.name,
-                    "date": event.start_time.strftime("%Y-%m-%d"),
-                },
-                indent=2,
-            )
-            for maintainer in MAINTAINERS:
-                user = await bot.fetch_user(event.creator_id)
-                await sendDm(
-                    maintainer,
-                    f"{user.name} added a new Event on {event.start_time.strftime("%Y")}: {event.name}. Like this message to approve.\n\n\n```json\n{eventJSON}```",
-                )
 
 
 # Event: When the bot is ready
@@ -101,6 +115,7 @@ async def on_ready():
     print(f"Logged in as {bot.user}")
     channel = bot.get_channel(CHANNEL_ID)
     await channel.send("Hello everyone! I'm online and ready to go! 📅")
+    await check_events()
 
 
 @bot.event
@@ -146,23 +161,6 @@ async def on_reaction_add(reaction, user):
             )
 
 
-@bot.event
-async def on_message(message):
-    if isinstance(message.channel, discord.DMChannel):
-        global MAINTAINERS
-        author = message.author
-        if "add maintainer" in message.content.lower() and author.id in MAINTAINERS:
-            newMaintainer = message.content.lower().replace("add maintainer ", "")
-            if not newMaintainer in MAINTAINERS:
-                addMaintainer(int(newMaintainer))
-                await sendDm(
-                    newMaintainer,
-                    "You are now a Maintainer for posthorn! Create an event located at 'Netz39 e.V.' and it will be added to the calendar.",
-                )
-                await message.channel.send("Maintainer added!")
-    await bot.process_commands(message)
-
-
 @bot.event
 async def on_scheduled_event_delete(event):
     delete_file(getFilePath(event))