import json import re import sys sys.path.insert(1, "./d.py") import discord 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, similar_exists load_dotenv("./.env") DISCORD_TOKEN = getenv("DISCORD_TOKEN") # Intents are required to listen to events like messages intents = discord.Intents.default() intents.message_content = True intents.members = True intents.reactions = True intents.dm_messages = True # Create an instance of the Bot bot = commands.Bot(command_prefix="!", intents=intents) def getCommitMessage(event): 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" 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 getRrule(event): 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): 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"{base}---\n{comment}" def getAdmins(guild): admins = [] for member in guild.members: if member.guild_permissions.administrator: admins.append(member.id) return admins @tasks.loop(hours=24) # Runs every 7 days async def check_events(): await bot.wait_until_ready() for guild in bot.guilds: scheduledEvents = await guild.fetch_scheduled_events() for event in scheduledEvents: await handleEvent(event) async def handleEvent(event, isNew=True): guild = event.guild channel = guild.system_channel admins = getAdmins(guild) eventYML = getEventYML(event) file_path = getFilePath(event) commit_message = getCommitMessage(event) if event.creator_id in admins or similar_exists(event.name): create_or_update_file( file_path, eventYML, f"{commit_message}", ) if isNew: 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 admin in admins: 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}```", ) # Event: When the bot is ready @bot.event async def on_ready(): print(f"Logged in as {discord.user}") # Command: !hello @bot.command() async def hello(ctx): await ctx.send( "Hello, I am a bot to add events from discord to the Netz39 calendar on the website!" ) @bot.event async def on_ready(): print(f"Logged in as {bot.user}") for guild in bot.guilds: system_channel = guild.system_channel if system_channel: await system_channel.send("Hello everyone! I'm online and ready to go! 📅") await check_events() @bot.event async def on_scheduled_event_update(before, after): if after.status != "canceled": await handleEvent(after, False) @bot.event async def on_scheduled_event_create(event): await handleEvent(event) async def sendDm(userID, message): user = await bot.fetch_user(userID) await user.send(message) @bot.event async def on_reaction_add(reaction, user): message = reaction.message channel = message.channel guild = message.guild admins = getAdmins(guild) if not user.id in admins: return # Ignore non-maintainers if user.bot: return # Ignore bot reactions if ( isinstance(message.channel, discord.DMChannel) and message.author.bot ): # Check if it's a DM and from the bot if reaction.emoji == "👍": messageJSON = json.loads( re.search( "(?s)(?<=```json).*?(?=```)", reaction.message.content ).group() ) create_or_update_file( messageJSON["path"], messageJSON["yml"], f"new event: {messageJSON["fileName"]}", ) await channel.send( f"📅 New event '{messageJSON["name"]}' on {messageJSON["date"]}." ) @bot.event async def on_scheduled_event_delete(event): delete_file(getFilePath(event)) guild = event.guild channel = guild.system_channel await channel.send(f"❌ Event has been canceled: {event.name}") bot.run(DISCORD_TOKEN)