Files
stolat/notify.py

86 lines
2.8 KiB
Python

#!/usr/bin/env python3
# stolat, cron-based birthday notifier
# Please run once per day.
# License: GPLv3 or later
import toml, os, importlib
from datetime import datetime
scripts = []
def get_current_path() -> str:
return os.path.dirname(os.path.abspath(__file__))
def load_config() -> dict:
# Try to get configuration from current_path/list.toml
try:
path = get_current_path()
config = toml.load(path + "/list.toml")
except:
print("Sorry! It seems like I can't access list.toml. Is it in the script's working directory?")
quit(-1)
return config
def calc_age(birth_date: str) -> int:
date_as_datetime = datetime.strptime(birth_date, "%Y-%m-%d")
delta = datetime.today() - date_as_datetime
return round(delta.days / 365.25)
def notify_user(birthday: dict, user: dict, actions: dict, days_till_birthday: int):
# print(f"Got {birthday}, {user} and actions {actions}.")
for channel in user['channels']:
for notify_action in actions:
if notify_action['id'] == channel:
# User wants to receive a notification through current notify_action
try:
module = importlib.import_module("scripts." + notify_action['name_of_script'][:notify_action['name_of_script'].rfind(".py")])
func = getattr(module, notify_action['startup_function'])
func("v1", user['user'], birthday['name'], calc_age(birthday['date']), days_till_birthday, birthday['date']) # v1-formatted function call
except Exception as e:
print(f"Error: failed executing {notify_action['startup_function']}() for {user['user']} ({birthday['name']}'s {calc_age(birthday['date'])}th birthday) :(\n"
f"Exception: {e}.")
def iterate_birthdays(config: dict):
badly_formatted_birthdays = 0
today = datetime.today()
for birthday in config['birthdays']:
try:
name = birthday['name']
birthdate_as_str = birthday['date']
birthdate = datetime.strptime(birthdate_as_str, "%Y-%m-%d")
to_notify = birthday['to_notify']
except Exception as e:
badly_formatted_birthdays += 1
continue
for user in config['preferences']:
birthdate_this_year = birthdate.replace(year=today.year)
# Safeguard against no additional reminders
if not user['additional_reminders']:
user['additional_reminders'] = []
# Consider the birthday as additional reminder
# This way we check the list once.
if (birthdate_this_year - today).days + 1 in user['additional_reminders']:
notify_user(birthday, user, config['actions'], (birthdate_this_year - today).days + 1)
if badly_formatted_birthdays > 0:
print(f"Warning: found {badly_formatted_birthdays} incorrectly formatted birth dates.\n"
f" They haven't been checked for possible birthdays. Please use ISO 8601.")
def main():
config = load_config()
iterate_birthdays(config)
if __name__ == "__main__":
main()