diff --git a/.example.env b/.example.env index 3b2cc6d..dc4ae74 100644 --- a/.example.env +++ b/.example.env @@ -1,7 +1,5 @@ DISCORD_TOKEN="DISCORT_BOT_TOKEN" -CHANNEL_ID="DISCORD_CHANNEL_ID" GITHUB_TOKEN="GITHUB_TOKEN_TO_PUSH_TO_MAIN" REPO_OWNER="netz39" REPO_NAME="wwww.netz39.de" -BASE_BRANCH="main" -MAINTAINERS='[DISCORD_IDS_OF_BRANCH_MAINTAINERS]' \ No newline at end of file +BASE_BRANCH="main" \ No newline at end of file diff --git a/Dockerfile b/Dockerfile new file mode 100644 index 0000000..7c451c1 --- /dev/null +++ b/Dockerfile @@ -0,0 +1,11 @@ +FROM python:3 + +RUN mkdir /source + +COPY ./bot.py ./github_connector.py ./requirements.txt /source/ + +WORKDIR /source + +RUN pip install -r requirements.txt + +CMD [ "python3", "bot.py" ] \ No newline at end of file diff --git a/bot.py b/bot.py index d4c314e..b6da651 100644 --- a/bot.py +++ b/bot.py @@ -1,7 +1,10 @@ import json import re +# import sys + # needs the following version of discord.py for recurring events to work: https://github.com/DA-344/d.py/tree/recurrent_events +# sys.path.insert(1, path_to_discorpy_version) import discord from discord.ext import commands, tasks from dotenv import load_dotenv @@ -10,10 +13,6 @@ 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") # Intents are required to listen to events like messages @@ -36,44 +35,45 @@ 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 frequency: "{event.recurrence_rule.frequency}"\n interval: {event.recurrence_rule.interval} ---' + if 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: "Leibnizstr. 18, 39104 Magdeburg"\n frequency: "{event.recurrence_rule.frequency}"\n interval: {event.recurrence_rule.interval}\n---\n<!-- event imported from discord manual changes may be overwritten -->' + 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: "Leibnizstr. 18, 39104 Magdeburg"\n---\n<!-- event imported from discord manual changes may be overwritten -->' -def addMaintainer(maintainer): - MAINTAINERS.append(maintainer) - with open(".env", "r") as fileR: - # read a list of lines into data - data = fileR.readlines() - data[6] = f"MAINTAINERS='{json.dumps(MAINTAINERS)}'" - with open(".env", "w") as fileW: - fileW.writelines(data) +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() - 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) + for guild in bot.guilds: + scheduledEvents = await guild.fetch_scheduled_events() + for event in scheduledEvents: + await handleEvent(event) async def handleEvent(event): + 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 MAINTAINERS or similar_exists(event.name): + if event.creator_id in admins 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")}." ) @@ -88,10 +88,10 @@ async def handleEvent(event): }, indent=2, ) - for maintainer in MAINTAINERS: + for admin in admins: user = await bot.fetch_user(event.creator_id) await sendDm( - maintainer, + 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}```", ) @@ -113,14 +113,17 @@ async def hello(ctx): @bot.event 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! 📅") + 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(event): - await handleEvent(event) + if event.status != "canceled": + await handleEvent(event) @bot.event @@ -135,14 +138,18 @@ async def sendDm(userID, message): @bot.event async def on_reaction_add(reaction, user): - if not user.id in MAINTAINERS: + 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(reaction.message.channel, discord.DMChannel) - and reaction.message.author.bot + 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( @@ -155,7 +162,6 @@ async def on_reaction_add(reaction, user): messageJSON["yml"], f"new event: {messageJSON["fileName"]}", ) - channel = bot.get_channel(CHANNEL_ID) await channel.send( f"📅 New event '{messageJSON["name"]}' on {messageJSON["date"]}." ) @@ -164,7 +170,8 @@ async def on_reaction_add(reaction, user): @bot.event async def on_scheduled_event_delete(event): delete_file(getFilePath(event)) - channel = bot.get_channel(CHANNEL_ID) + guild = event.guild + channel = guild.system_channel await channel.send(f"❌ Event has been canceled: {event.name}") diff --git a/docker-compose.yml b/docker-compose.yml new file mode 100644 index 0000000..f733605 --- /dev/null +++ b/docker-compose.yml @@ -0,0 +1,11 @@ +version: "3.5" + +services: + posthorn: + image: 0ry5/posthorn:latest + environment: + - DISCORD_TOKEN=ADD_DISCORD_BOT_TOKEN_ID + - GITHUB_TOKEN=GITHUB_REPO_TOKEN + - REPO_OWNER=OrysB + - REPO_NAME=www.netz39.de + - BASE_BRANCH=main