{ config, lib, pkgs, ... }: # TODO multiple users # TODO inform about unused caches # cache = url: "${cfg.dataDir}/.urlwatch/cache/${hashString "sha1" url}" with import <stockholm/lib>; let cfg = config.krebs.urlwatch; # TODO assert sendmail's existence out = { options.krebs.urlwatch = api; config = lib.mkIf cfg.enable imp; }; api = { enable = mkEnableOption "krebs.urlwatch"; dataDir = mkOption { type = types.str; default = "/var/lib/urlwatch"; description = '' Directory where the urlwatch service should store its state. ''; }; from = mkOption { type = types.str; default = "${user.name}@${config.networking.hostName}.r"; description = '' Content of the From: header of the generated mails. ''; }; # TODO hooks :: attrsOf hook hooksFile = mkOption { type = with types; nullOr path; default = null; description = '' File to use as hooks.py module. ''; }; mailto = mkOption { type = types.str; default = config.krebs.build.user.mail; description = '' Content of the To: header of the generated mails. [AKA recipient :)] ''; }; onCalendar = mkOption { type = types.str; default = "04:23"; description = '' Run urlwatch at this interval. The format is described in systemd.time(7), CALENDAR EVENTS. ''; }; sendmail.enable = mkEnableOption "krebs.urlwatch.sendmail" // { default = true; }; telegram = { enable = mkEnableOption "krebs.urlwatch.telegram"; botToken = mkOption { type = types.str; }; chatId = mkOption { type = types.listOf types.str; }; }; urls = mkOption { type = with types; listOf (either str subtypes.job); default = []; description = "URL to watch."; example = [ https://nixos.org/channels/nixos-unstable/git-revision { url = http://localhost ; filter = "grep:important.*stuff"; } ]; apply = map (x: getAttr (typeOf x) { set = x; string.url = x; }); }; verbose = mkOption { type = types.bool; default = false; description = '' verbose output of urlwatch ''; }; }; urlsFile = pkgs.writeText "urls" (concatMapStringsSep "\n---\n" (x: toJSON (filterAttrs (n: v: n != "_module") x)) cfg.urls); hooksFile = cfg.hooksFile; configFile = pkgs.writeJSON "urlwatch.yaml" { display = { error = true; new = true; unchanged = false; }; report = { email = { enabled = false; from = ""; html = false; smtp = { host = "localhost"; keyring = true; port = 25; starttls = true; }; subject = "{count} changes: {jobs}"; to = ""; }; html.diff = "unified"; stdout = { color = true; enabled = true; }; ${if cfg.telegram.enable then "telegram" else null} = { enabled = cfg.telegram.enable; bot_token = cfg.telegram.botToken; chat_id = cfg.telegram.chatId; }; text = { details = true; footer = true; line_length = 75; }; }; }; imp = { systemd.timers.urlwatch = { wantedBy = [ "timers.target" ]; timerConfig = { OnCalendar = cfg.onCalendar; Persistent = "true"; }; }; systemd.services.urlwatch = { path = with pkgs; [ coreutils gnused urlwatch ]; environment = { HOME = cfg.dataDir; LC_ALL = "en_US.UTF-8"; LOCALE_ARCHIVE = "${pkgs.glibcLocales}/lib/locale/locale-archive"; SSL_CERT_FILE = "${pkgs.cacert}/etc/ssl/certs/ca-bundle.crt"; }; serviceConfig = { User = user.name; PermissionsStartOnly = "true"; PrivateTmp = "true"; SyslogIdentifier = "urlwatch"; Type = "oneshot"; ExecStart = pkgs.writeDash "urlwatch" '' set -euf cd /tmp urlwatch \ ${optionalString cfg.verbose "-v"} \ --config=${shell.escape configFile} \ ${optionalString (hooksFile != null) "--hooks=${shell.escape hooksFile}" } \ --urls=${shell.escape urlsFile} \ > changes || : ${optionalString cfg.sendmail.enable /* sh */ '' if test -s changes; then { echo Date: $(date -R) echo From: ${shell.escape cfg.from} echo Subject: $( sed -n 's/^\(CHANGED\|ERROR\|NEW\): //p' changes \ | tr '\n' ' ' ) echo To: ${shell.escape cfg.mailto} echo cat changes } | /run/wrappers/bin/sendmail -t fi ''} ''; }; }; users.users.${user.name} = { inherit (user) uid; home = cfg.dataDir; createHome = true; }; }; user = rec { name = "urlwatch"; uid = genid_uint31 name; }; subtypes.job = types.submodule { options = { url = mkOption { type = types.str; }; filter = mkOption { default = null; type = with types; nullOr str; # TODO nullOr subtypes.filter }; ignore_cached = mkOption { default = null; type = with types; nullOr bool; }; }; }; in out