aboutsummaryrefslogtreecommitdiffstats
path: root/modules/communication
diff options
context:
space:
mode:
Diffstat (limited to 'modules/communication')
-rw-r--r--modules/communication/neomutt/account/flavors/gmail.nix1
-rw-r--r--modules/communication/neomutt/lib.nix20
-rw-r--r--modules/communication/neomutt/mail/mbsync.nix104
-rw-r--r--modules/communication/neomutt/neomutt-sync.sh108
4 files changed, 196 insertions, 37 deletions
diff --git a/modules/communication/neomutt/account/flavors/gmail.nix b/modules/communication/neomutt/account/flavors/gmail.nix
index ba39c58..9cb51b2 100644
--- a/modules/communication/neomutt/account/flavors/gmail.nix
+++ b/modules/communication/neomutt/account/flavors/gmail.nix
@@ -21,7 +21,6 @@
"[Gmail]/Drafts"
"[Gmail]/Sent Mail"
"[Gmail]/Spam"
- "[Gmail]/Trash"
];
extraConfig.channel = {
Sync = lib.mkDefault "All";
diff --git a/modules/communication/neomutt/lib.nix b/modules/communication/neomutt/lib.nix
new file mode 100644
index 0000000..253ada8
--- /dev/null
+++ b/modules/communication/neomutt/lib.nix
@@ -0,0 +1,20 @@
+# some basic common utility functions for neomutt
+{ lib, ... }:
+{
+ mbsyncNamesFromName =
+ name:
+ let
+ channelNameBase = lib.replaceStrings [ "@" "." ] [ "-" "_" ] name;
+ in
+ {
+ base = channelNameBase;
+ channels = {
+ full = "${channelNameBase}-full";
+ quick = "${channelNameBase}-quick";
+ };
+ servers = {
+ remote = "${channelNameBase}-remote";
+ local = "${channelNameBase}-local";
+ };
+ };
+}
diff --git a/modules/communication/neomutt/mail/mbsync.nix b/modules/communication/neomutt/mail/mbsync.nix
index 28b7b2c..1d21377 100644
--- a/modules/communication/neomutt/mail/mbsync.nix
+++ b/modules/communication/neomutt/mail/mbsync.nix
@@ -1,7 +1,107 @@
-# Mailbox synchronization
-{ config, lib, ... }:
+{
+ config,
+ lib,
+ ...
+}@inputs:
+let
+ genValue =
+ v:
+ if lib.isList v then
+ lib.concatStringsSep " " (map genValue v)
+ else if lib.isBool v then
+ lib.hm.booleans.yesNo v
+ else if lib.isInt v then
+ toString v
+ else if lib.isString v then
+ if builtins.match ".* .*" v != null then ''"${lib.escape [ ''"'' ] v}"'' else v
+ else
+ throw "Unsupported mbsync value";
+
+ genSection = name: attrs: ''
+ ${name}
+ ${lib.concatStringsSep "\n" (lib.mapAttrsToList (n: v: "${n} ${genValue v}") attrs)}
+ '';
+
+ genAccount =
+ a:
+ let
+ mbsyncNames = (import ../lib.nix inputs).mbsyncNamesFromName a.name;
+
+ quick = {
+ Far = ":${mbsyncNames.servers.remote}:\"INBOX\"";
+ Near = ":${mbsyncNames.servers.local}:\"INBOX\"";
+ Create = "Near";
+ Expunge = "None";
+ Sync = [
+ "Pull"
+ "New"
+ ];
+ SyncState = "*";
+ };
+
+ full = {
+ Far = ":${mbsyncNames.servers.remote}:";
+ Near = ":${mbsyncNames.servers.local}:";
+ Patterns = a.mbsync.patterns;
+ Create = "Near";
+ Expunge = "Both";
+ Sync = "All";
+ SyncState = "*";
+ };
+ in
+ genSection "IMAPAccount ${mbsyncNames.base}" (
+ {
+ Host = a.imap.host;
+ User = a.userName;
+ PipelineDepth = 50;
+ TLSType =
+ if !a.imap.tls.enable then
+ "None"
+ else if a.imap.tls.useStartTls then
+ "STARTTLS"
+ else
+ "IMAPS";
+ }
+ // lib.optionalAttrs (a.passwordCommand != null) {
+ PassCmd = toString a.passwordCommand;
+ }
+ // lib.optionalAttrs (a.imap.tls.certificatesFile != null) {
+ CertificateFile = toString a.imap.tls.certificatesFile;
+ }
+ // lib.optionalAttrs (a.imap.port != null) {
+ Port = a.imap.port;
+ }
+ )
+
+ + "\n"
+ + genSection "IMAPStore ${mbsyncNames.servers.remote}" {
+ Account = mbsyncNames.base;
+ }
+
+ + "\n"
+ + genSection "MaildirStore ${mbsyncNames.servers.local}" {
+ Path = "${a.maildir.absPath}/";
+ Inbox = "${a.maildir.absPath}/${a.folders.inbox}";
+ SubFolders = "Verbatim";
+ }
+
+ + "\n"
+ + genSection "Channel ${mbsyncNames.channels.quick}" quick
+
+ + "\n"
+ + genSection "Channel ${mbsyncNames.channels.full}" full
+
+ + "\n";
+
+ accounts = lib.filter (a: a.enable && a.mbsync.enable) (
+ lib.attrValues config.accounts.email.accounts
+ );
+
+in
{
config = lib.mkIf config.communication.neomutt.enable {
programs.mbsync.enable = true;
+
+ xdg.configFile."mbsync/.mbsyncrc".text = lib.concatStringsSep "\n" (map genAccount accounts);
};
}
diff --git a/modules/communication/neomutt/neomutt-sync.sh b/modules/communication/neomutt/neomutt-sync.sh
index 67e007d..ff43039 100644
--- a/modules/communication/neomutt/neomutt-sync.sh
+++ b/modules/communication/neomutt/neomutt-sync.sh
@@ -1,14 +1,31 @@
#!/usr/bin/env bash
-# Sync mail with mbsync and index new messages with notmuch,
-# showing progress via a dialog gauge.
+# Sync one mbsync channel and index new messages with notmuch.
+
+set -uo pipefail
+
+MBSYNCRC="${MBSYNCRC:-$HOME/.config/mbsync/.mbsyncrc}"
+TITLE="[󰇮 SYNCING MAIL]"
DIALOGRC=$(mktemp)
+MBSYNC_LOG=$(mktemp)
NOTMUCH_LOG=$(mktemp)
-TITLE="[󰇮 SYNCING MAIL]"
+
export DIALOGRC
-trap 'rm -f "$DIALOGRC" "$NOTMUCH_LOG"' EXIT # Clean up everything on exit
-cat << 'EOF' > "$DIALOGRC"
+trap 'rm -f "$DIALOGRC" "$MBSYNC_LOG" "$NOTMUCH_LOG"' EXIT
+
+if [[ $# -ne 1 ]]; then
+ echo "Usage: $0 <mbsync-channel>"
+ echo
+ echo "Example:"
+ echo " $0 arpitchakladar-gmail_com-quick"
+ echo " $0 arpitchakladar-gmail_com-full"
+ exit 2
+fi
+
+CHANNEL="$1"
+
+cat <<'EOF' > "$DIALOGRC"
use_shadow = ON
use_colors = ON
screen_color = (WHITE,BLACK,OFF)
@@ -19,47 +36,70 @@ border_color = (CYAN,BLACK,ON)
border2_color = (BLUE,BLACK,ON)
shadow_color = (BLACK,BLACK,ON)
-# Active buttons: magenta emphasis, matching Base16 base0E
button_active_color = (MAGENTA,BLACK,ON)
button_key_active_color = (MAGENTA,BLACK,ON)
button_label_active_color = (MAGENTA,BLACK,ON)
-# Inactive buttons: cyan secondary emphasis, matching Base16 base0C
button_inactive_color = (CYAN,BLACK,OFF)
button_key_inactive_color = (CYAN,BLACK,OFF)
button_label_inactive_color = (CYAN,BLACK,OFF)
EOF
-# Run the sync and indexing progress bar
(
- # mbsync phase
- script -q -e -c "mbsync -a" /dev/null | awk -v RS='\r' '
- {
- c_str = ""
- b_str = ""
- for(i=1; i<=NF; i++) {
- if ($i == "C:") c_str = $(i+1)
- if ($i == "B:") b_str = $(i+1)
- }
- if (b_str != "") {
- split(b_str, arr, "/")
- if (arr[2] > 0) {
- percent = int((arr[1] / arr[2]) * 100)
- printf "XXX\n%d\nSyncing | Channel: %s | Mailbox: %s\nXXX\n", percent, c_str, b_str
- }
- }
- fflush()
- }'
-
- # notmuch phase
- printf "XXX\n100\nIndexing new mail with notmuch...\nXXX\n"
- notmuch new > "$NOTMUCH_LOG" 2>&1
+ printf "XXX\n0\nSyncing: %s...\nXXX\n" "$CHANNEL"
+
+ mbsync -c "$MBSYNCRC" "$CHANNEL" > "$MBSYNC_LOG" 2>&1 &
+ PID=$!
+
+ # Keep the gauge alive while mbsync runs.
+ while kill -0 "$PID" 2>/dev/null; do
+ printf "XXX\n50\nSyncing: %s...\nXXX\n" "$CHANNEL"
+ sleep 0.5
+ done
+
+ wait "$PID"
+ MBSYNC_STATUS=$?
+
+ if (( MBSYNC_STATUS != 0 )); then
+ printf "XXX\n100\nSync failed: %s\nXXX\n" "$CHANNEL"
+ exit "$MBSYNC_STATUS"
+ fi
+
+ printf "XXX\n90\nIndexing new mail with notmuch...\nXXX\n"
+
+ notmuch new --quiet > "$NOTMUCH_LOG" 2>&1
+ NOTMUCH_STATUS=$?
+
+ printf "XXX\n100\nDone.\nXXX\n"
+
+ if (( NOTMUCH_STATUS != 0 )); then
+ exit "$NOTMUCH_STATUS"
+ fi
) | dialog --title "$TITLE" --gauge "Initializing..." 8 80 0
-# Format the captured notmuch output into a single clean line
-CLEAN_OUT=$(tr '\n' ' ' < "$NOTMUCH_LOG" | sed 's/ */ /g')
+STATUS=${PIPESTATUS[0]}
+
+MBSYNC_OUT=$(
+ tr '\n' ' ' < "$MBSYNC_LOG" |
+ sed 's/ */ /g'
+)
+
+NOTMUCH_OUT=$(
+ tr '\n' ' ' < "$NOTMUCH_LOG" |
+ sed 's/ */ /g'
+)
-# Display final results and wait for user input
-dialog --title "$TITLE" --msgbox "Done!\n$CLEAN_OUT" 8 80
+if (( STATUS == 0 )); then
+ dialog \
+ --title "$TITLE" \
+ --msgbox "Done!\n\nChannel: $CHANNEL\n\nmbsync: ${MBSYNC_OUT:-OK}\n\nnotmuch: ${NOTMUCH_OUT:-OK}" \
+ 12 80
+else
+ dialog \
+ --title "$TITLE" \
+ --msgbox "Sync failed!\n\nChannel: $CHANNEL\n\nmbsync: ${MBSYNC_OUT:-no output}\n\nnotmuch: ${NOTMUCH_OUT:-not run}" \
+ 12 80
+fi
clear
+exit "$STATUS"