diff options
| -rw-r--r-- | email/gmail.nix | 19 | ||||
| -rw-r--r-- | email/mailsync.nix | 35 | ||||
| -rw-r--r-- | email/neomutt.nix | 10 | ||||
| -rw-r--r-- | email/notmuch.nix | 5 | ||||
| -rw-r--r-- | flake.nix | 4 | ||||
| -rw-r--r-- | shell-scripts/mailsync | 112 | ||||
| -rw-r--r-- | shell-scripts/notmuch-hook | 10 | ||||
| -rw-r--r-- | shell-scripts/sb-mailbox | 20 |
8 files changed, 200 insertions, 15 deletions
diff --git a/email/gmail.nix b/email/gmail.nix index a883b17..751d677 100644 --- a/email/gmail.nix +++ b/email/gmail.nix @@ -28,15 +28,15 @@ enable = true; create = "both"; remove = "both"; expunge = "both"; groups = { - mailboxes = { + gmail = { channels = { - Inbox = { farPattern = "INBOX"; nearPattern = "INBOX"; extraConfig = { Create = "Near"; }; }; - Archive = { farPattern = "[Gmail]/All Mail"; nearPattern = "Archive"; extraConfig = { Create = "Near"; }; }; - Spam = { farPattern = "[Gmail]/Spam"; nearPattern = "Spam"; extraConfig = { Create = "Near"; }; }; - Trash = { farPattern = "[Gmail]/Bin"; nearPattern = "Trash"; extraConfig = { Create = "Near"; }; }; - Important = { farPattern = "[Gmail]/Important"; nearPattern = "Important"; extraConfig = { Create = "Near"; }; }; - Sent = { farPattern = "[Gmail]/Sent Mail"; nearPattern = "Sent"; extraConfig = { Create = "Near"; }; }; - FarDrafts = { farPattern = "[Gmail]/Drafts"; nearPattern = "FarDrafts"; extraConfig = { Create = "Near"; }; }; + Inbox = { farPattern = "INBOX"; nearPattern = "INBOX"; extraConfig = { Create = "Near"; Expunge = "Both"; }; }; + Archive = { farPattern = "[Gmail]/All Mail"; nearPattern = "Archive"; extraConfig = { Create = "Near"; Expunge = "Both"; }; }; + Spam = { farPattern = "[Gmail]/Spam"; nearPattern = "Spam"; extraConfig = { Create = "Near"; Expunge = "Both"; }; }; + Trash = { farPattern = "[Gmail]/Bin"; nearPattern = "Trash"; extraConfig = { Create = "Near"; Expunge = "Both"; }; }; + Important = { farPattern = "[Gmail]/Important"; nearPattern = "Important"; extraConfig = { Create = "Near"; Expunge = "Both"; }; }; + Sent = { farPattern = "[Gmail]/Sent Mail"; nearPattern = "Sent"; extraConfig = { Create = "Near"; Expunge = "Both"; }; }; + FarDrafts = { farPattern = "[Gmail]/Drafts"; nearPattern = "FarDrafts"; extraConfig = { Create = "Near"; Expunge = "Both"; }; }; }; }; }; @@ -51,6 +51,9 @@ { name = "Sent"; query = "tag:sent"; } { name = "Spam"; query = "tag:spam"; } { name = "Trash"; query = "tag:trash"; } + { name = "Jobs"; query = "tag:jobs"; } + { name = "Houses"; query = "tag:houses"; } + { name = "Development"; query = "tag:dev"; } ]; }; }; diff --git a/email/mailsync.nix b/email/mailsync.nix new file mode 100644 index 0000000..dbf5d7f --- /dev/null +++ b/email/mailsync.nix @@ -0,0 +1,35 @@ +{ + flake, + config, + pkgs, + home-manager, + ... +}: { + programs.mbsync = { + enable = true; + }; + systemd.user.timers.mailsync = { + Unit = { + Description = "daemon that syncs mail"; + }; + Timer = { + OnBootSec = "5m"; + OnUnitActiveSec = "5m"; + Unit = "mailsync.service"; + }; + Install = { + WantedBy = [ "timers.target" ]; + }; + }; + systemd.user.services.mailsync = { + Unit = { + Description = "daemon that syncs mail"; + }; + Service = { + Type = "oneshot"; + RemainAfterExit = "no"; + ExecSearchPath = "${config.home.profileDirectory}/bin:/run/current-system/sw/bin"; + ExecStart = "mailsync"; + }; + }; +} diff --git a/email/neomutt.nix b/email/neomutt.nix index b8bc848..9271de2 100644 --- a/email/neomutt.nix +++ b/email/neomutt.nix @@ -128,6 +128,7 @@ enable = true; }; binds = [ + { map = ["index" "pager"]; key = "x"; action = "noop"; } { map = ["index" "pager"]; key = "i"; action = "noop"; } { map = ["index" "pager"]; key = "g"; action = "noop"; } { map = ["index"]; key = "\\Cf"; action = "noop"; } @@ -144,7 +145,6 @@ { map = ["pager"]; key = "j"; action = "next-line"; } { map = ["pager"]; key = "k"; action = "previous-line"; } { map = ["pager"]; key = "l"; action = "view-attachments"; } - { map = ["index"]; key = "D"; action = "delete-message"; } { map = ["index"]; key = "U"; action = "undelete-message"; } { map = ["index"]; key = "L"; action = "limit"; } { map = ["index"]; key = "h"; action = "noop"; } @@ -158,7 +158,6 @@ { map = ["pager"]; key = "G"; action = "bottom"; } { map = ["index" "pager" "browser"]; key = "d"; action = "half-down"; } { map = ["index" "pager" "browser"]; key = "u"; action = "half-up"; } - { map = ["index" "pager"]; key = "S"; action = "sync-mailbox"; } { map = ["index" "pager"]; key = "R"; action = "group-reply"; } { map = ["index"]; key = "\031"; action = "previous-undeleted"; } { map = ["index"]; key = "\005"; action = "next-undeleted"; } @@ -167,8 +166,13 @@ { map = ["editor"]; key = "<Tab>"; action = "complete-query"; } ]; macros = [ + { map = ["index"]; key = "X"; action = "<save-message>=Spam<enter>y"; } + { map = ["index"]; key = "A"; action = "<modify-labels-then-hide>+archive -unread -inbox<enter><mark-message>z<enter><change-folder>^<enter>'z"; } + { map = ["index"]; key = "D"; action = "<delete-message>"; } + { map = ["index" "pager"]; key = "S"; action = "<sync-mailbox>!notmuch-hook &<enter>"; } + { map = ["index"]; key = "c"; action = "<change-vfolder>?"; } + { map = ["index"]; key = "\\\\"; action = "<vfolder-from-query>"; } { map = ["browser"]; key = "h"; action = "<change-dir><kill-line>..<enter>"; } - { map = ["index"]; key = "<right>"; action = "<enter-command>toggle sidebar_visible<enter><refresh>"; } ]; }; } diff --git a/email/notmuch.nix b/email/notmuch.nix index 15bfcd9..fbafcbc 100644 --- a/email/notmuch.nix +++ b/email/notmuch.nix @@ -5,13 +5,10 @@ home-manager, ... }: { - programs.mbsync = { - enable = true; - }; programs.notmuch = { enable = true; new = { - tags = ["unread" "inbox"]; + tags = ["new"]; ignore = [".mbsyncstate" ".uidvalidity"]; }; search.excludeTags = ["deleted" "spam"]; @@ -53,6 +53,10 @@ ./home/newsboat.nix ./home/kakoune.nix ./home/mpv.nix + ./email/gmail.nix + ./email/mailsync.nix + ./email/neomutt.nix + ./email/notmuch.nix ]; extraSpecialArgs = { inherit home-manager username email; diff --git a/shell-scripts/mailsync b/shell-scripts/mailsync new file mode 100644 index 0000000..52d1fff --- /dev/null +++ b/shell-scripts/mailsync @@ -0,0 +1,112 @@ +#!/bin/sh + +# - Syncs mail for all accounts, or a single account given as an argument. +# - Displays a notification showing the number of new mails. +# - Displays a notification for each new mail with its subject displayed. +# - Runs notmuch to index new mail. +# - This script can be set up as a cron job for automated mail syncing. + +# There are many arbitrary and ugly features in this script because it is +# inherently difficult to pass environmental variables to cronjobs and other +# issues. It also should at least be compatible with Linux (and maybe BSD) with +# Xorg and MacOS as well. + +# Run only if not already running in other instance +pgrep mbsync >/dev/null && { echo "mbsync is already running."; exit ;} + +# First, we have to get the right variables for the mbsync file, the pass +# archive, notmuch and the GPG home. This is done by searching common profile +# files for variable assignments. This is ugly, but there are few options that +# will work on the maximum number of machines. +eval "$(grep -h -- \ + "^\s*\(export \)\?\(MBSYNCRC\|MPOPRC\|PASSWORD_STORE_DIR\|PASSWORD_STORE_GPG_OPTS\|NOTMUCH_CONFIG\|GNUPGHOME\|MAILSYNC_MUTE\|XDG_CONFIG_HOME\|XDG_DATA_HOME\)=" \ + "$HOME/.profile" "$HOME/.bash_profile" "$HOME/.zprofile" "$HOME/.config/zsh/.zprofile" "$HOME/.zshenv" \ + "$HOME/.config/zsh/.zshenv" "$HOME/.bashrc" "$HOME/.zshrc" "$HOME/.config/zsh/.zshrc" \ + "$HOME/.pam_environment" 2>/dev/null)" + +export GPG_TTY="$(tty)" + +[ -n "$MBSYNCRC" ] && alias mbsync="mbsync -c $MBSYNCRC" || MBSYNCRC="$HOME/.mbsyncrc" +[ -n "$MPOPRC" ] || MPOPRC="$HOME/.config/mpop/config" + +lastrun="${XDG_CONFIG_HOME:-$HOME/.config}/neomutt/.mailsynclastrun" + +# Settings are different for MacOS (Darwin) systems. +case "$(uname)" in + Darwin) notify() { osascript -e "display notification \"$2\" with title \"$1\"" ;} ;; + *) + case "$(readlink -f /sbin/init)" in + *systemd*|*openrc*) export DBUS_SESSION_BUS_ADDRESS=unix:path=/run/user/$(id -u)/bus ;; + esac + # remember if a display server is running since `ps` doesn't always contain a display + pgrepoutput="$(pgrep -ax X\(\|org\|wayland\))" + displays="$(echo "$pgrepoutput" | grep -wo "[0-9]*:[0-9]\+" | sort -u)" + [ -z "$displays" ] && [ -d /tmp/.X11-unix ] && displays=$(cd /tmp/.X11-unix && for x in X*; do echo ":${x#X}"; done) + + notify() { [ -n "$pgrepoutput" ] && for x in ${displays:-:0}; do + export DISPLAY="$x" + notify-send --app-name="mutt-wizard" "$1" "$2" + done ;} + ;; +esac + +# Check account for new mail. Notify if there is new content. +syncandnotify() { + case "$1" in + imap) mbsync -q "$2" ;; + pop) mpop -q "$2" ;; + esac + new=$(find\ + "$HOME/.local/share/mail/${2%%-*}/"[Ii][Nn][Bb][Oo][Xx]/new/ \ + "$HOME/.local/share/mail/${2%%-*}/"[Ii][Nn][Bb][Oo][Xx]/cur/ \ + -type f -newer "$lastrun" 2> /dev/null) + newcount=$(echo "$new" | sed '/^\s*$/d' | wc -l) + case 1 in + $((newcount > 5)) ) + echo "$newcount new mail for $2." + [ -z "$MAILSYNC_MUTE" ] && notify "New Mail!" "📬 $newcount new mail(s) in \`$2\` account." + ;; + $((newcount > 0)) ) + echo "$newcount new mail for $2." + [ -z "$MAILSYNC_MUTE" ] && + for file in $new; do + # Extract and decode subject and sender from mail. + subject="$(sed -n "/^Subject:/ s|Subject: *|| p" "$file" | + perl -CS -MEncode -ne 'print decode("MIME-Header", $_)')" + from="$(sed -n "/^From:/ s|From: *|| p" "$file" | + perl -CS -MEncode -ne 'print decode("MIME-Header", $_)')" + from="${from% *}" ; from="${from%\"}" ; from="${from#\"}" + notify "📧$from:" "$subject" + done + ;; + *) echo "No new mail for $2." ;; +esac +} + +allchannels="$(grep -hs "Channel" "$MBSYNCRC" "$MPOPRC" | sort -u)" + +# Get accounts to sync. All if no argument. Prefix with `error` if non-existent. +IFS=' +' +if [ -z "$1" ]; then + tosync="$allchannels" +else + tosync="$(for arg in "$@"; do for availacc in $allchannels; do + [ "$arg" = "${availacc##* }" ] && echo "$availacc" && break + done || echo "error $arg"; done)" +fi + +for channel in $tosync; do + case $channel in + Channel*) syncandnotify imap "${channel##* }" & ;; + account*) syncandnotify pop "${channel##* }" & ;; + error*) echo "ERROR: Account ${channelt##* } not found." ;; + esac +done + +wait + +notmuch-hook + +#Create a touch file that indicates the time of the last run of mailsync +touch "$lastrun" diff --git a/shell-scripts/notmuch-hook b/shell-scripts/notmuch-hook new file mode 100644 index 0000000..268f315 --- /dev/null +++ b/shell-scripts/notmuch-hook @@ -0,0 +1,10 @@ +. <(pass show personal/notmuch) +notmuch new --quiet +notmuch tag -new +jobs -- from:jobs-listing* or from:jobs-noreply* +notmuch tag -new +houses -- from:"$MAKELAAR" or thread:"{$MAKELAAR}" +notmuch tag -new -inbox +dev -- from:/.*github.com/ or thread:"{from:/.*github.com/}" + +notmuch tag -new +inbox +unread -- tag:new and path:gmail/Inbox/** +notmuch tag -new +archive -- 'path:gmail/Archive/** -path:gmail/Inbox/** -path:gmail/[Gmail]/** -path:gmail/FarDrafts/** -path:gmail/Important/** -path:gmail/Sent/**' +notmuch tag --remove-all +trash -- path:gmail/Trash/** +notmuch tag --remove-all +spam -- folder:/Spam/ diff --git a/shell-scripts/sb-mailbox b/shell-scripts/sb-mailbox new file mode 100644 index 0000000..ab1669e --- /dev/null +++ b/shell-scripts/sb-mailbox @@ -0,0 +1,20 @@ +#!/bin/sh + +# Displays number of unread mail and an loading icon if updating. +# When clicked, brings up `neomutt`. + +case $BUTTON in + 1) setsid -f "$TERMINAL" -e neomutt ;; + 2) setsid -f mailsync >/dev/null ;; + 3) notify-send "📬 Mail module" "\- Shows unread mail +- Shows 🔃 if syncing mail +- Left click opens neomutt +- Middle click syncs mail" ;; + 6) "$TERMINAL" -e "$EDITOR" "$0" ;; +esac + +unread="$(find "${XDG_DATA_HOME:-$HOME/.local/share}"/mail/*/[Ii][Nn][Bb][Oo][Xx]/new/* -type f | wc -l 2>/dev/null)" + +pidof mbsync >/dev/null 2>&1 && icon="🔃" + +[ "$unread" = "0" ] && [ "$icon" = "" ] || echo "📬$unread$icon" |
