chore: commit files
This commit is contained in:
6
.gitignore
vendored
Normal file
6
.gitignore
vendored
Normal file
@@ -0,0 +1,6 @@
|
|||||||
|
# User configuration
|
||||||
|
list.toml
|
||||||
|
|
||||||
|
# Python garbage
|
||||||
|
__pycache__
|
||||||
|
scripts/__pycache__
|
||||||
30
list.example.toml
Normal file
30
list.example.toml
Normal file
@@ -0,0 +1,30 @@
|
|||||||
|
|
||||||
|
preferences = [
|
||||||
|
|
||||||
|
# Bob prefers to be notified about Alice's birthday
|
||||||
|
# with SMS, while Alice prefers XMPP.
|
||||||
|
{user="bob@localhost", channels=[2]},
|
||||||
|
# Moreover, Alice prefers to be notified a week and a day before
|
||||||
|
# in addition to the standard notification on the day of the birthday.
|
||||||
|
{user="alice@localhost", channels=[1], "additional_reminders"=[1, 7]}
|
||||||
|
|
||||||
|
]
|
||||||
|
|
||||||
|
birthdays = [
|
||||||
|
|
||||||
|
# Alice will get notified about Bob's birthday, and Bob about Alice's.
|
||||||
|
{name="Bob", date="2000-01-01", to_notify=["alice@localhost"]},
|
||||||
|
{name="Alice", date="2000-01-23", to_notify=["bob@localhost"]}
|
||||||
|
|
||||||
|
]
|
||||||
|
|
||||||
|
actions = [
|
||||||
|
|
||||||
|
# Predefined, trusted scripts to run.
|
||||||
|
# Scripts are going to be searched for inside of the "scripts" directory.
|
||||||
|
{id=0, name="print information", name_of_script="sample_script.py", startup_function="print_v1_data"},
|
||||||
|
# These are not implemented!
|
||||||
|
{id=1, name="send XMPP message", name_of_script="dummy.py", startup_function="send_message"},
|
||||||
|
{id=2, name="send SMS", name_of_script="dummy.py", startup_function="send_sms"}
|
||||||
|
|
||||||
|
]
|
||||||
86
notify.py
Normal file
86
notify.py
Normal file
@@ -0,0 +1,86 @@
|
|||||||
|
#!/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, timedelta
|
||||||
|
|
||||||
|
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()
|
||||||
19
scripts/sample_script.py
Normal file
19
scripts/sample_script.py
Normal file
@@ -0,0 +1,19 @@
|
|||||||
|
#!/usr/bin/env python3
|
||||||
|
|
||||||
|
# Implement your functions here...
|
||||||
|
|
||||||
|
def print_v1_data(*args):
|
||||||
|
|
||||||
|
if args[0] == "v1":
|
||||||
|
string = (f"Called with API version {args[0]}\n"
|
||||||
|
f"to send to user {args[1]}\n"
|
||||||
|
f"about {args[2]}\n"
|
||||||
|
f"who turns {args[3]}\n"
|
||||||
|
f"in {args[4]} days\n"
|
||||||
|
f"with birthday being {args[5]}.\n")
|
||||||
|
print(string)
|
||||||
|
else:
|
||||||
|
print("Called with a wrong API version!")
|
||||||
|
|
||||||
|
if __name__ == "__main__":
|
||||||
|
print("This script isn't meant be ran interactively!")
|
||||||
Reference in New Issue
Block a user