diff --git a/.gitmodules b/.gitmodules new file mode 100644 index 0000000..fd97893 --- /dev/null +++ b/.gitmodules @@ -0,0 +1,3 @@ +[submodule "d.py"] + path = d.py + url = https://github.com/DA-344/d.py.git diff --git a/Dockerfile b/Dockerfile index 7c451c1..a41add0 100644 --- a/Dockerfile +++ b/Dockerfile @@ -2,9 +2,11 @@ FROM python:3 RUN mkdir /source -COPY ./bot.py ./github_connector.py ./requirements.txt /source/ +COPY ./bot.py ./github_connector.py ./README.md ./requirements.txt ./.example.env ./initSubmodules.sh /source/ -WORKDIR /source +WORKDIR /source/ + +RUN bash initSubmodules.sh RUN pip install -r requirements.txt diff --git a/bot.py b/bot.py index 6c9e5f3..8ecc3b6 100644 --- a/bot.py +++ b/bot.py @@ -1,9 +1,8 @@ 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 -# uncomment following lines -# import sys -# sys.path.insert(1, '/path/to/Posthorn/vendor/d.py') +import sys + +sys.path.insert(1, "./d.py") import discord from discord.ext import commands, tasks from dotenv import load_dotenv @@ -26,56 +25,70 @@ bot = commands.Bot(command_prefix="!", intents=intents) def getCommitMessage(event): - return f"{event.start_time.strftime('%Y-%m-%d')}_{event.name}" + return f"{event.start_time.strftime("%Y-%m-%d")}_{event.name}" def getFilePath(event): - return f"_events/{event.start_time.strftime('%Y')}/discord-event-{event.id}.md" + return f"_events/{event.start_time.strftime("%Y")}/discord-event-{event.id}.md" + + +days = { + 0: "MO", + 1: "TU", + 2: "WE", + 3: "TH", + 4: "FR", + 5: "SA", + 6: "SU", +} + + +def asDayStr(day): + return days[day] + + +def asDayToupleStr(day): + return f"{day[0]}{days[day[1]]}" + + +def weekdaysToString(weekdays): + asStr = map(asDayStr, weekdays) + return ",".join(asStr) + + +def n_weekdaysToString(weekdays): + asStr = map(asDayToupleStr, weekdays) + return ",".join(asStr) -def getDay(day): - match day: - case 0: - return 'MO' - case 1: - return 'TU' - case 2: - return 'WE' - case 3: - return 'TH' - case 4: - return 'FR' - case 5: - return 'SA' - case 6: - return 'SU' - case _: - raise Exception(f'no matching day for int {day}') def getRrule(event): - byDay = False - if hasattr(event.recurrence_rule, "n_weekdays"): - weekdays = event.recurrence_rule.n_weekdays - i = 0 - for week, day in weekdays: - comma = '' - if i < len(weekdays) - 1: - comma = ',' - byDay = f'{str(week)}{getDay(day)}{comma}' - i += 1 - if byDay: - return f"FREQ={event.recurrence_rule.frequency.name.upper()};INTERVAL={event.recurrence_rule.interval};BYDAY={byDay}" - else: - return f"FREQ={event.recurrence_rule.frequency.name.upper()};INTERVAL={event.recurrence_rule.interval}" + recurrence_rule = event.recurrence_rule + rrule = "" + if hasattr(recurrence_rule, "frequency") and hasattr(recurrence_rule, "interval"): + freq = f"FREQ={str.upper(recurrence_rule.frequency.name)};" + inter = f"INTERVAL={recurrence_rule.interval};" + dtstart = f'DTSTART={event.start_time.strftime("%Y%m%dT%H%M%S")}' + + if hasattr(recurrence_rule, "weekdays") and len(recurrence_rule.weekdays) != 0: + rrule = f'rrule: "{freq}{inter}BYDAY={weekdaysToString(recurrence_rule.weekdays)};{dtstart}"\n' + elif ( + hasattr(recurrence_rule, "n_weekdays") + and len(recurrence_rule.n_weekdays) != 0 + ): + rrule = f'rrule: "{freq}{inter}BYDAY={n_weekdaysToString(recurrence_rule.n_weekdays)};{dtstart}"\n' + else: + rrule = f'rrule: "{freq}{inter}{dtstart}"\n' + return rrule + def getEventYML(event): - location = "Leibnizstraรe 32, 39104 Magdeburg" - if hasattr(event, 'location'): - location = event.location - - if hasattr(event, "recurrence_rule") and hasattr(event.recurrence_rule, "frequency") and hasattr(event.recurrence_rule, "interval"): - return f'---\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: "{location}"\n rrule: "{getRrule(event)}"\n---\n<!-- event imported from discord manual changes may be overwritten -->\n{event.description}' + base = f'---\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' + comment = "<!-- event imported from discord manual changes may be overwritten -->" + if hasattr(event, "recurrence_rule"): + rrule = getRrule(event) + return f"{base}{rrule}---\n{comment}" else: - return f'---\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: "{location}"\n---\n<!-- event imported from discord manual changes may be overwritten -->\n{event.description}' + return f"{base}---\n{comment}" def getAdmins(guild): @@ -92,10 +105,10 @@ async def check_events(): for guild in bot.guilds: scheduledEvents = await guild.fetch_scheduled_events() for event in scheduledEvents: - await handleEvent(event, True) + await handleEvent(event) -async def handleEvent(event, isUpdate = False): +async def handleEvent(event, isNew=True): guild = event.guild channel = guild.system_channel admins = getAdmins(guild) @@ -107,23 +120,12 @@ async def handleEvent(event, isUpdate = False): create_or_update_file( file_path, eventYML, - f"new event: {commit_message}", + f"{commit_message}", ) - if not isUpdate: - embed = discord.Embed( - title=f"๐ New event on {event.start_time.strftime('%Y-%m-%d')}", - description=f"### {event.name}\n\n{event.description}\n\n", - color=discord.Color.light_embed() + if isNew: + await channel.send( + f"๐ New event '{event.name}' on {event.start_time.strftime("%Y-%m-%d")}." ) - if hasattr(event, "cover_image") and event.cover_image: - embed.set_image(url=event.cover_image.url) - embed.add_field(name="๐ Time", value=event.start_time.strftime("%Y-%m-%d %HUhr"), inline=True) - if hasattr(event, 'location'): - embed.add_field(name="๐ Location", value=event.location, inline=True) - eventLink = f"[๐ Netz39](https://www.netz39.de/events/2025/discord-event-{event.id})"; - embed.add_field(value=eventLink, name="", inline=False) - embed.set_footer(text="Donโt miss it! ") - await channel.send(embed=embed) else: eventJSON = json.dumps( { @@ -139,7 +141,7 @@ async def handleEvent(event, isUpdate = False): user = await bot.fetch_user(event.creator_id) await sendDm( admin, - 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}```", + 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}```", ) @@ -170,7 +172,7 @@ async def on_ready(): @bot.event async def on_scheduled_event_update(before, after): if after.status != "canceled": - await handleEvent(after, True) + await handleEvent(after, False) @bot.event @@ -207,10 +209,10 @@ async def on_reaction_add(reaction, user): create_or_update_file( messageJSON["path"], messageJSON["yml"], - f"new event: {messageJSON['fileName']}", + f"new event: {messageJSON["fileName"]}", ) await channel.send( - f"๐ New event '{messageJSON['name']}' on {messageJSON['date']}." + f"๐ New event '{messageJSON["name"]}' on {messageJSON["date"]}." ) diff --git a/d.py b/d.py new file mode 160000 index 0000000..7756a3a --- /dev/null +++ b/d.py @@ -0,0 +1 @@ +Subproject commit 7756a3a206f0792e34410e999b5280e32318880c diff --git a/initSubmodules.sh b/initSubmodules.sh new file mode 100644 index 0000000..70b4ec0 --- /dev/null +++ b/initSubmodules.sh @@ -0,0 +1,3 @@ +git clone 'https://github.com/DA-344/d.py.git' +cd d.py +git checkout 'recurrent_events' \ No newline at end of file