diff --git a/modules/modules.nix b/modules/modules.nix index e2fc86df..e4d35088 100644 --- a/modules/modules.nix +++ b/modules/modules.nix @@ -96,6 +96,7 @@ let (loadModule ./services/flameshot.nix { }) (loadModule ./services/gnome-keyring.nix { }) (loadModule ./services/gpg-agent.nix { }) + (loadModule ./services/imapnotify.nix { condition = hostPlatform.isLinux; }) (loadModule ./services/kbfs.nix { }) (loadModule ./services/kdeconnect.nix { }) (loadModule ./services/keepassx.nix { }) diff --git a/modules/services/imapnotify-accounts.nix b/modules/services/imapnotify-accounts.nix new file mode 100644 index 00000000..75b3ecd1 --- /dev/null +++ b/modules/services/imapnotify-accounts.nix @@ -0,0 +1,30 @@ +{ lib, ... }: + +with lib; + +{ + options.imapnotify = { + enable = mkEnableOption "imapnotify"; + + onNotify = mkOption { + type = with types; either str (attrsOf str); + default = ""; + example = "\${pkgs.mbsync}/bin/mbsync test-%s"; + description = "Shell commands to run on any event."; + }; + + onNotifyPost = mkOption { + type = with types; either str (attrsOf str); + default = ""; + example = { mail = "\${pkgs.notmuch}/bin/notmuch new && \${pkgs.libnotify}/bin/notify-send 'New mail arrived'"; }; + description = "Shell commands to run after onNotify event."; + }; + + boxes = mkOption { + type = types.listOf types.str; + default = []; + example = [ "Inbox" "[Gmail]/MyLabel" ]; + description = "IMAP folders to watch."; + }; + }; +} diff --git a/modules/services/imapnotify.nix b/modules/services/imapnotify.nix new file mode 100644 index 00000000..9c77cbd2 --- /dev/null +++ b/modules/services/imapnotify.nix @@ -0,0 +1,105 @@ +{ config, lib, pkgs, ... }: + +with lib; + +let + + cfg = config.services.imapnotify; + + safeName = lib.replaceChars ["@" ":" "\\" "[" "]"] ["-" "-" "-" "" ""]; + + imapnotifyAccounts = + filter (a: a.imapnotify.enable) + (attrValues config.accounts.email.accounts); + + genAccountUnit = account: + let + name = safeName account.name; + in + { + name = "imapnotify-${name}"; + value = { + Unit = { + Description = "imapnotify for ${name}"; + }; + + Service = { + ExecStart = "${pkgs.imapnotify}/bin/imapnotify -c ${genAccountConfig account}"; + }; + + Install = { + WantedBy = [ "default.target" ]; + }; + }; + }; + + genAccountConfig = account: + pkgs.writeText "imapnotify-${safeName account.name}-config.js" ( + let + port = + if account.imap.port != null then account.imap.port + else if account.imap.tls.enable then 993 + else 143; + + toJSON = builtins.toJSON; + in + '' + var child_process = require('child_process'); + + function getStdout(cmd) { + var stdout = child_process.execSync(cmd); + return stdout.toString().trim(); + } + + exports.host = ${toJSON account.imap.host} + exports.port = ${toJSON port}; + exports.tls = ${toJSON account.imap.tls.enable}; + exports.username = ${toJSON account.userName}; + exports.password = getStdout("${toString account.passwordCommand}"); + exports.onNotify = ${toJSON account.imapnotify.onNotify}; + exports.onNotifyPost = ${toJSON account.imapnotify.onNotifyPost}; + exports.boxes = ${toJSON account.imapnotify.boxes}; + '' + ); + +in + +{ + meta.maintainers = [ maintainers.nickhu ]; + + options = { + services.imapnotify = { + enable = mkEnableOption "imapnotify"; + }; + + accounts.email.accounts = mkOption { + type = with types; attrsOf (submodule ( + import ./imapnotify-accounts.nix + )); + }; + }; + + config = mkIf cfg.enable { + assertions = + let + checkAccounts = pred: msg: + let + badAccounts = filter pred imapnotifyAccounts; + in + { + assertion = badAccounts == []; + message = "imapnotify: Missing ${msg} for accounts: " + + concatMapStringsSep ", " (a: a.name) badAccounts; + }; + in + [ + (checkAccounts (a: a.maildir == null) "maildir configuration") + (checkAccounts (a: a.imap == null) "IMAP configuration") + (checkAccounts (a: a.passwordCommand == null) "password command") + (checkAccounts (a: a.userName == null) "username") + ]; + + systemd.user.services = + listToAttrs (map genAccountUnit imapnotifyAccounts); + }; +}