init repo
This commit is contained in:
parent
ba8b46c8a7
commit
a8a5fed56f
4 changed files with 275 additions and 0 deletions
7
.example.env
Normal file
7
.example.env
Normal file
|
@ -0,0 +1,7 @@
|
|||
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]'
|
173
bot.py
Normal file
173
bot.py
Normal file
|
@ -0,0 +1,173 @@
|
|||
import json
|
||||
import re
|
||||
import discord
|
||||
from discord.ext import commands
|
||||
from dotenv import load_dotenv
|
||||
from os import getenv
|
||||
from github_connector import create_or_update_file, delete_file
|
||||
|
||||
|
||||
load_dotenv("./.env")
|
||||
|
||||
CHANNEL_ID = int(getenv("CHANNEL_ID"))
|
||||
MAINTAINERS = json.loads(getenv("MAINTAINERS"))
|
||||
DISCORD_TOKEN = getenv("DISCORD_TOKEN")
|
||||
|
||||
print(MAINTAINERS)
|
||||
|
||||
# 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 getFileName(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"
|
||||
|
||||
|
||||
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---'
|
||||
|
||||
|
||||
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)
|
||||
|
||||
|
||||
async def handleEvent(event):
|
||||
eventYML = getEventYML(event)
|
||||
if "netz39" in event.location.lower() or (
|
||||
"leibniz" in event.location.lower() and "18" in event.location
|
||||
):
|
||||
if event.creator_id in MAINTAINERS:
|
||||
create_or_update_file(
|
||||
getFilePath(event),
|
||||
eventYML,
|
||||
f"new event: {getFileName(event)}",
|
||||
)
|
||||
channel = bot.get_channel(CHANNEL_ID)
|
||||
await channel.send(
|
||||
f"📅 New event '{event.name}' on {event.start_time.strftime("%Y-%m-%d")}."
|
||||
)
|
||||
else:
|
||||
eventJSON = json.dumps(
|
||||
{
|
||||
"yml": eventYML,
|
||||
"path": getFilePath(event),
|
||||
"fileName": getFileName(event),
|
||||
"name": event.name,
|
||||
"date": event.start_time.strftime("%Y-%m-%d"),
|
||||
},
|
||||
indent=2,
|
||||
)
|
||||
for maintainer in MAINTAINERS:
|
||||
user = await bot.fetch_user(event.creator_id)
|
||||
await sendDm(
|
||||
maintainer,
|
||||
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}")
|
||||
channel = bot.get_channel(CHANNEL_ID)
|
||||
await channel.send("Hello everyone! I'm online and ready to go! 📅")
|
||||
|
||||
|
||||
@bot.event
|
||||
async def on_scheduled_event_update(event):
|
||||
await handleEvent(event)
|
||||
|
||||
|
||||
@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):
|
||||
if not user.id in MAINTAINERS:
|
||||
return # Ignore non-maintainers
|
||||
if user.bot:
|
||||
return # Ignore bot reactions
|
||||
|
||||
if (
|
||||
isinstance(reaction.message.channel, discord.DMChannel)
|
||||
and reaction.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"]}",
|
||||
)
|
||||
channel = bot.get_channel(CHANNEL_ID)
|
||||
await channel.send(
|
||||
f"📅 New event '{messageJSON["name"]}' on {messageJSON["date"]}."
|
||||
)
|
||||
|
||||
|
||||
@bot.event
|
||||
async def on_message(message):
|
||||
if isinstance(message.channel, discord.DMChannel):
|
||||
global MAINTAINERS
|
||||
author = message.author
|
||||
if "add maintainer" in message.content.lower() and author.id in MAINTAINERS:
|
||||
newMaintainer = message.content.lower().replace("add maintainer ", "")
|
||||
if not newMaintainer in MAINTAINERS:
|
||||
addMaintainer(int(newMaintainer))
|
||||
await sendDm(
|
||||
newMaintainer,
|
||||
"You are now a Maintainer for posthorn! Create an event located at 'Netz39 e.V.' and it will be added to the calendar.",
|
||||
)
|
||||
await message.channel.send("Maintainer added!")
|
||||
await bot.process_commands(message)
|
||||
|
||||
|
||||
@bot.event
|
||||
async def on_scheduled_event_delete(event):
|
||||
delete_file(getFilePath(event))
|
||||
channel = bot.get_channel(CHANNEL_ID)
|
||||
await channel.send(f"❌ Event has been canceled: {event.name}")
|
||||
|
||||
|
||||
bot.run(DISCORD_TOKEN)
|
71
github_connector.py
Normal file
71
github_connector.py
Normal file
|
@ -0,0 +1,71 @@
|
|||
import requests
|
||||
import json
|
||||
from dotenv import load_dotenv
|
||||
from os import getenv
|
||||
import base64
|
||||
|
||||
load_dotenv()
|
||||
|
||||
# load env vars
|
||||
REPO_OWNER = getenv("REPO_OWNER")
|
||||
REPO_NAME = getenv("REPO_NAME")
|
||||
BASE_BRANCH = getenv("BASE_BRANCH")
|
||||
GITHUB_TOKEN = getenv("GITHUB_TOKEN")
|
||||
|
||||
# GitHub API URL
|
||||
API_URL = f"https://api.github.com/repos/{REPO_OWNER}/{REPO_NAME}"
|
||||
|
||||
# Headers for authentication
|
||||
HEADERS = {
|
||||
"Authorization": f"token {GITHUB_TOKEN}",
|
||||
"Accept": "application/vnd.github.v3+json",
|
||||
}
|
||||
|
||||
|
||||
def get_file_sha(file_path):
|
||||
url = f"{API_URL}/contents/{file_path}?ref={BASE_BRANCH}"
|
||||
response = requests.get(url, headers=HEADERS)
|
||||
|
||||
if response.status_code == 200:
|
||||
return response.json()["sha"]
|
||||
else:
|
||||
raise Exception(f"Error getting file SHA: {response.json()}")
|
||||
|
||||
|
||||
def delete_file(file_path):
|
||||
url = f"{API_URL}/contents/{file_path}"
|
||||
data = {
|
||||
"message": f"Deleting file: {file_path}",
|
||||
"sha": get_file_sha(file_path),
|
||||
"branch": BASE_BRANCH,
|
||||
}
|
||||
response = requests.delete(url, headers=HEADERS, json=data)
|
||||
|
||||
if not response.status_code == 200 and not response.status_code == 404:
|
||||
raise Exception(f"Error deleting file: {response.json()}")
|
||||
|
||||
|
||||
def create_or_update_file(file_path, content, commit_message):
|
||||
url = f"{API_URL}/contents/{file_path}"
|
||||
b = base64.b64encode(bytes(content, "utf-8"))
|
||||
base64_str = b.decode("utf-8")
|
||||
|
||||
# Check if file exists
|
||||
response = requests.get(url, headers=HEADERS)
|
||||
if response.status_code == 200:
|
||||
sha = response.json()["sha"]
|
||||
else:
|
||||
sha = None
|
||||
|
||||
data = {
|
||||
"message": commit_message,
|
||||
"content": base64_str,
|
||||
"branch": BASE_BRANCH,
|
||||
}
|
||||
|
||||
if sha:
|
||||
data["sha"] = sha # If file exists, update it
|
||||
|
||||
response = requests.put(url, headers=HEADERS, json=data)
|
||||
if not response.status_code in [200, 201]:
|
||||
raise Exception(f"Error committing file: {response.json()}")
|
24
requirements.txt
Normal file
24
requirements.txt
Normal file
|
@ -0,0 +1,24 @@
|
|||
aiohappyeyeballs==2.6.1
|
||||
aiohttp==3.11.14
|
||||
aiosignal==1.3.2
|
||||
attrs==25.3.0
|
||||
certifi==2025.1.31
|
||||
cffi==1.17.1
|
||||
charset-normalizer==3.4.1
|
||||
cryptography==44.0.2
|
||||
Deprecated==1.2.18
|
||||
discord.py==2.5.2
|
||||
frozenlist==1.5.0
|
||||
idna==3.10
|
||||
multidict==6.2.0
|
||||
propcache==0.3.1
|
||||
pycparser==2.22
|
||||
PyGithub==2.6.1
|
||||
PyJWT==2.10.1
|
||||
PyNaCl==1.5.0
|
||||
python-dotenv==1.1.0
|
||||
requests==2.32.3
|
||||
typing_extensions==4.13.0
|
||||
urllib3==2.3.0
|
||||
wrapt==1.17.2
|
||||
yarl==1.18.3
|
Loading…
Add table
Reference in a new issue