Skip to content

Instantly share code, notes, and snippets.

@v1nc
Created April 7, 2022 14:16
Show Gist options
  • Star 1 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save v1nc/1238b25a41a71e4e10737d2050d46542 to your computer and use it in GitHub Desktop.
Save v1nc/1238b25a41a71e4e10737d2050d46542 to your computer and use it in GitHub Desktop.
Moodle Telegram Notifier
"""
Telegram Moodle Notificatior
Get notifications for moodle events via telegram.
"""
import sys
from datetime import datetime, timedelta, date
import requests
import icalendar
import validators
from dateutil.rrule import *
from requests.auth import HTTPBasicAuth
import json
import requests
import config
TG_BOT_TOKEN = "" # Bot token you get from @botfather
TG_CHAT_ID = "-100" # Chat ID of a chat that the bot can write into
MOODLE_URL = "" # Your Moodle Calendar URL, exported from e.g. https://moodle.your-domain.de/calendar/export.php?
def send(bot_message):
""" Send telegram message to chat """
send_text = f"https://api.telegram.org/bot{TG_BOT_TOKEN}/sendMessage?chat_id={TG_CHAT_ID}&disable_web_page_preview=True&parse_mode=Markdown&text={bot_message}"
response = requests.get(send_text)
return response.json()
def verify_url(url):
""" Verify a calendar URL """
return validators.url(url)
def decode_component(component, component_name):
""" Decode a calendar component """
try:
return component.decoded(component_name)
except KeyError:
return None
def check_event(component):
""" Check if component is a real event """
items = component.property_items()
if len(items) > 0 and len(items[0]) > 0:
return items[0][1] == b'VEVENT'
return False
def extract_categories(lines):
""" Extract categories from the component manually """
for line in lines:
if "CATEGORIES:" in line:
return line.split("CATEGORIES:")[1]
def decode_event(component):
""" Decode entities of a component """
event = {}
event["summary"] = decode_component(component,"SUMMARY").decode().strip()
event["categories"] = extract_categories(component.content_lines()).strip()
event["date"] = decode_component(component, "DTEND").replace(tzinfo=None)+ timedelta(hours=2)
event["date_string"] = event["date"].strftime("%H:%M")
try:
event["uid"] = decode_component(component,"UID").decode()
except Exception:
event["uid"] = event["summary"]+event["categories"]
return event
def download_ical(url):
""" Download a calendar file from URL """
if not verify_url(url):
print("invalid ical url")
return False
try:
response = requests.get(url, allow_redirects=True)
gcal = icalendar.Calendar.from_ical(response.content)
return gcal
except Exception as exception:
print(exception)
return False
# load json about already send messages
try:
with open('moodle_uids.json') as json_file:
send_icals = json.load(json_file)
except Exception:
send_icals = []
# download calendar file
ical_calendar = download_ical(MOODLE_URL)
if not ical_calendar:
print("error")
sys.exit(0)
# check all calendar events
for component in ical_calendar.walk():
# only check real events
if check_event(component):
# get event details into array
event = decode_event(component)
# check if event is tomorrow and not already send
today = date.today()
tomorrow = date.today() + timedelta(days=1)
if not event["uid"] in send_icals and event["date"].date() == tomorrow:
# create message string with markdown
string = f"*Termin Morgen {event['date_string']} Uhr:*\r\n\r\n{event['categories']}\r\n➡️ *{event['summary']}*"
# send telegram message
print(send(string))
# save event uid
send_icals.append(event["uid"])
# save send message to file
with open('moodle_uids.json', 'w') as outfile:
json.dump(send_icals, outfile)
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment