#!/usr/bin/env nix-shell #!nix-shell --pure -i python3 -p "python3.withPackages (ps: with ps; [ flask apprise mnemonic wtforms jq ])" from typing import Any import apprise from flask import Flask, request from mnemonic import Mnemonic import sqlite3 import os import jq import json app = Flask(__name__) import logging log = logging.getLogger("werkzeug") log.setLevel(logging.ERROR) ENV_PREFIX = "NOTIFIER_" def getenv(key: str, default: Any = None) -> Any: v = os.getenv(ENV_PREFIX + key, default) if not v: exit(f"{ENV_PREFIX+key} must be specified!") return v CONFIG_URL = getenv("URL", "http://127.0.0.1") CONFIG_PORT = int(getenv("PORT", 8080)) CONFIG_DATABASE_PATH = getenv("DATABASE_PATH", "notifications.db") CONFIG_MATRIX_BOT_NAME = getenv("MATRIX_BOT_NAME", "unset") CONFIG_MATRIX_BOT_TOKEN = getenv("MATRIX_BOT_TOKEN") CONFIG_MATRIX_HOST = getenv("MATRIX_HOST") CONFIG_PROXY_AUTH_USERNAME_HEADER = getenv("PROXY_AUTH_USERNAME_HEADER", "Remote-User") CONFIG_MAIL_USERNAME = getenv("MAIL_USERNAME") CONFIG_MAIL_PASSWORD = getenv("MAIL_PASSWORD") CONFIG_MAIL_DOMAIN = getenv("MAIL_DOMAIN") CONFIG_MAIL_HOST = getenv("MAIL_HOST") CONFIG_MAIL_PORT = int(getenv("MAIL_PORT", "465")) CONFIG_MAIL_MODE = getenv("MAIL_MODE", "ssl") script_example = rf"""#!/usr/bin/env bash #!/usr/bin/env nix-shell #!nix-shell --pure -i python3 -p "python3.withPackages (ps: with ps; [ requests ])" import requests import argparse import sys import os parser = argparse.ArgumentParser(formatter_class=argparse.ArgumentDefaultsHelpFormatter) parser.add_argument("--title", default="", help="Subject/title of the notification") parser.add_argument("--body", default="", help="`-` for stdin") parser.add_argument("--jq", default=".", help="Jq filter to use") parser.add_argument("--type", default="matrix", help="mail or matrix") parser.add_argument("--token", help="token to use") parser.add_argument("--token-file", help="file to read token from") parser.add_argument("--url", default="{CONFIG_URL}/notify", help="Notify endpoint") args = parser.parse_args() token: str = args.token if args.token_file: token = open(args.token_file, "r").read().strip() if not token: exit("No token or tokenfile specified, or empty") data = args.body if data == "-" and not sys.stdin.isatty(): data = "\n".join(sys.stdin.readlines()) headers = {{"Authorization": f"Bearer {{token}}"}} params = {{ "jq": args.jq, "type": args.type, }} if args.title: params["title"] = args.title req = requests.post(args.url, headers=headers, params=params, data=data) exit(not req.status_code == 200) """ script_example_with_token = script_example.replace( '--token"', '--token", default="||TOKEN||"', ) def get_db(): con = sqlite3.connect(CONFIG_DATABASE_PATH) cur = con.cursor() cur.execute( "CREATE TABLE IF NOT EXISTS default_room(username TEXT PRIMARY KEY, room_id TEXT NOT NULL)" ) cur.execute( "CREATE TABLE IF NOT EXISTS tokens(username TEXT PRIMARY KEY, token TEXT NOT NULL)" ) return con @app.route("/", methods=["GET", "POST"]) def index(): username = request.headers.get(CONFIG_PROXY_AUTH_USERNAME_HEADER) if not username: return ("Not authenticated", 401) # handle post stuff if request.method == "POST": action = request.form.get("action", "").lower() if "token" in action: generate_token_for_user(username) elif "room id" in action: roomid = request.form.get("room_id") if not roomid: return ("Room Id cannot be empty", 400) set_user_default_matrix_room(username, roomid) con = get_db() cur = con.cursor() res = cur.execute( "SELECT token FROM tokens WHERE username = ?", (username,) ).fetchone() token: str = "" if res: token = res[0] res = cur.execute( "SELECT room_id FROM default_room WHERE username = ?", (username,) ).fetchone() room_id: str = "" if res: room_id = res[0] # hack to make users confirm it generate_token_name: str = "tmpaction" generate_token_value: str = "Generate Token" if request.form.get(generate_token_name): generate_token_name = "action" generate_token_value = "Generate Token. Are you sure?" tmpl = f"""
This notification service has support for both matrix and email. Matrix notifications will be sent from {CONFIG_MATRIX_BOT_NAME}, be sure to invite them to the room/space if it is private. If using email, they will only be sent to your member email, and can't be sent anywhere else. You'll receive them from {CONFIG_MAIL_USERNAME}@{CONFIG_MAIL_DOMAIN}.
Param | Description | Default | Example |
---|---|---|---|
title | Title of notification | Notification | Compilation Finished! |
body | Body of notification | empty | Compilation result: success (default is nothing) |
type | type of notification | matrix | matrix |
room_id | Matrix room ID | default room | !yREJWHUMJhGROiHbtu:fricloud.dk or #na-offtopic:rend.al |
curl "{CONFIG_URL}/notify" -H "Authorization: Bearer {token}"
curl w/ specific body/title
curl "{CONFIG_URL}/notify?title=MyTitle&body=MyBody" -H "Authorization: Bearer {token}"
Python
{script_example}
Python w/ hardcoded token (DO NOT SHARE)
{script_example_with_token}
Nix Python Script HARDCODED TOKEN (DO NOT SHARE)
pkgs.writers.writePython3Bin "notify" {{
libraries = with pkgs.python3Packages; [ requests ];
doCheck = false;
}} ''{script_example_with_token}'';