feat(bot): add recurring events

This commit is contained in:
0ry5 2025-04-12 18:46:44 +02:00
parent 99a0a15b9c
commit bf693dea0e
4 changed files with 64 additions and 37 deletions

View file

@ -1,7 +1,5 @@
DISCORD_TOKEN="DISCORT_BOT_TOKEN" DISCORD_TOKEN="DISCORT_BOT_TOKEN"
CHANNEL_ID="DISCORD_CHANNEL_ID"
GITHUB_TOKEN="GITHUB_TOKEN_TO_PUSH_TO_MAIN" GITHUB_TOKEN="GITHUB_TOKEN_TO_PUSH_TO_MAIN"
REPO_OWNER="netz39" REPO_OWNER="netz39"
REPO_NAME="wwww.netz39.de" REPO_NAME="wwww.netz39.de"
BASE_BRANCH="main" BASE_BRANCH="main"
MAINTAINERS='[DISCORD_IDS_OF_BRANCH_MAINTAINERS]'

11
Dockerfile Normal file
View file

@ -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" ]

75
bot.py
View file

@ -1,7 +1,10 @@
import json import json
import re 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 # 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 import discord
from discord.ext import commands, tasks from discord.ext import commands, tasks
from dotenv import load_dotenv from dotenv import load_dotenv
@ -10,10 +13,6 @@ from github_connector import create_or_update_file, delete_file, similar_exists
load_dotenv("./.env") 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") DISCORD_TOKEN = getenv("DISCORD_TOKEN")
# Intents are required to listen to events like messages # Intents are required to listen to events like messages
@ -36,44 +35,45 @@ def getFilePath(event):
def getEventYML(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): def getAdmins(guild):
MAINTAINERS.append(maintainer) admins = []
with open(".env", "r") as fileR: for member in guild.members:
# read a list of lines into data if member.guild_permissions.administrator:
data = fileR.readlines() admins.append(member.id)
data[6] = f"MAINTAINERS='{json.dumps(MAINTAINERS)}'" return admins
with open(".env", "w") as fileW:
fileW.writelines(data)
@tasks.loop(hours=24) # Runs every 7 days @tasks.loop(hours=24) # Runs every 7 days
async def check_events(): async def check_events():
await bot.wait_until_ready() await bot.wait_until_ready()
guild = bot.get_guild(GUILD_ID) for guild in bot.guilds:
channel = guild.get_channel(CHANNEL_ID) scheduledEvents = await guild.fetch_scheduled_events()
for event in scheduledEvents:
if not channel: await handleEvent(event)
return
scheduledEvents = await guild.fetch_scheduled_events()
for event in scheduledEvents:
await handleEvent(event)
async def handleEvent(event): async def handleEvent(event):
guild = event.guild
channel = guild.system_channel
admins = getAdmins(guild)
eventYML = getEventYML(event) eventYML = getEventYML(event)
file_path = getFilePath(event) file_path = getFilePath(event)
commit_message = getCommitMessage(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( create_or_update_file(
file_path, file_path,
eventYML, eventYML,
f"new event: {commit_message}", f"new event: {commit_message}",
) )
channel = bot.get_channel(CHANNEL_ID)
await channel.send( await channel.send(
f"📅 New event '{event.name}' on {event.start_time.strftime("%Y-%m-%d")}." f"📅 New event '{event.name}' on {event.start_time.strftime("%Y-%m-%d")}."
) )
@ -88,10 +88,10 @@ async def handleEvent(event):
}, },
indent=2, indent=2,
) )
for maintainer in MAINTAINERS: for admin in admins:
user = await bot.fetch_user(event.creator_id) user = await bot.fetch_user(event.creator_id)
await sendDm( 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}```", 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 @bot.event
async def on_ready(): async def on_ready():
print(f"Logged in as {bot.user}") print(f"Logged in as {bot.user}")
channel = bot.get_channel(CHANNEL_ID) for guild in bot.guilds:
await channel.send("Hello everyone! I'm online and ready to go! 📅") system_channel = guild.system_channel
if system_channel:
await system_channel.send("Hello everyone! I'm online and ready to go! 📅")
await check_events() await check_events()
@bot.event @bot.event
async def on_scheduled_event_update(event): async def on_scheduled_event_update(event):
await handleEvent(event) if event.status != "canceled":
await handleEvent(event)
@bot.event @bot.event
@ -135,14 +138,18 @@ async def sendDm(userID, message):
@bot.event @bot.event
async def on_reaction_add(reaction, user): 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 return # Ignore non-maintainers
if user.bot: if user.bot:
return # Ignore bot reactions return # Ignore bot reactions
if ( if (
isinstance(reaction.message.channel, discord.DMChannel) isinstance(message.channel, discord.DMChannel) and message.author.bot
and reaction.message.author.bot
): # Check if it's a DM and from the bot ): # Check if it's a DM and from the bot
if reaction.emoji == "👍": if reaction.emoji == "👍":
messageJSON = json.loads( messageJSON = json.loads(
@ -155,7 +162,6 @@ async def on_reaction_add(reaction, user):
messageJSON["yml"], messageJSON["yml"],
f"new event: {messageJSON["fileName"]}", f"new event: {messageJSON["fileName"]}",
) )
channel = bot.get_channel(CHANNEL_ID)
await channel.send( await channel.send(
f"📅 New event '{messageJSON["name"]}' on {messageJSON["date"]}." f"📅 New event '{messageJSON["name"]}' on {messageJSON["date"]}."
) )
@ -164,7 +170,8 @@ async def on_reaction_add(reaction, user):
@bot.event @bot.event
async def on_scheduled_event_delete(event): async def on_scheduled_event_delete(event):
delete_file(getFilePath(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}") await channel.send(f"❌ Event has been canceled: {event.name}")

11
docker-compose.yml Normal file
View file

@ -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