diff options
Diffstat (limited to 'modules/communication/neomutt')
| -rw-r--r-- | modules/communication/neomutt/account/flavors/gmail.nix | 1 | ||||
| -rw-r--r-- | modules/communication/neomutt/lib.nix | 20 | ||||
| -rw-r--r-- | modules/communication/neomutt/mail/mbsync.nix | 104 | ||||
| -rw-r--r-- | modules/communication/neomutt/neomutt-sync.sh | 108 |
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" |
