From 2f819d1647d4c73aad6ca8fec8348beaae4bb870 Mon Sep 17 00:00:00 2001
From: Nick Hu <me@nickhu.co.uk>
Date: Tue, 9 Apr 2019 18:34:44 +0100
Subject: [PATCH] imapnotify: add service

---
 modules/modules.nix                      |   1 +
 modules/services/imapnotify-accounts.nix |  30 +++++++
 modules/services/imapnotify.nix          | 105 +++++++++++++++++++++++
 3 files changed, 136 insertions(+)
 create mode 100644 modules/services/imapnotify-accounts.nix
 create mode 100644 modules/services/imapnotify.nix

diff --git a/modules/modules.nix b/modules/modules.nix
index e2fc86df7..e4d350888 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 000000000..75b3ecd1e
--- /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 000000000..9c77cbd27
--- /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);
+  };
+}