Posthorn/bot.py
2025-04-29 10:19:44 +00:00

227 lines
6.3 KiB
Python

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)