Site Map - skip to main content

Hacker Public Radio

Your ideas, projects, opinions - podcasted.

New episodes Monday through Friday.


I use email in the terminal with a combination of:

protonmail-bridge-cli
local IMAP server to fetch Protonmail mails
tmux
to run protonmail-bridge in
isync (or offlineimap etc)
to sync IMAP with maildirs
notmuch
to tag and search mails
afew
to move mails found by certain notmuch queries in certain maildirs
alot
to read, compose, search and tag mails
msmtp
to send mails
w3m (or lynx etc)
to read HTML mails in the terminal
rsync
to copy archived mails
a shell function and an alias
to make it all less ridiculous

To most, Thunderbird will be suitable, the target audience here is unreasonable people.

Key takeaways about notmuch

notmuch sort of doesn't care where your messages are stored, the folder is just one of the parameters you can search by.

notmuch sort of doesn't care about individual messages, it cares about threads. This is good because if you trashed all the messages in a thread and a new reply comes in, the previous messages in the thread will come along. Also, folders are usually not really folders, they're more like tags, so we might as well treat them as such.

Here are the principles or rules:

These are the tags that have an impact in moving mails from one maildir to another, I can also use any other tags just for organizing mails as I please.

Key takeaway about using Protonmail-bridge

When you delete a message from Protonmail via IMAP, it sits in a sort of "root" folder, a folder inaccessible other than via the All Mail folder in the web app and mobile app. To properly delete a message from Protonmail, unfortunately, you need to move it to the trash (meaning you have to sync the trash), and then delete it in the app. There is no way to properly delete it via IMAP, even if you move it to the trash, sync it, then delete it and sync it, you will find the message floating in your All Mail folder.

Snippets

#!/usr/bin/zsh
echo "Running afew lists filter."
afew -nt
echo "Tagging new mails."
cat <<-EOF | notmuch tag --batch
    +pm +mailings -new -- folder:pm/mailings AND tag:new
    +pm +inbox -new -- folder:pm/inbox AND tag:new
    +pm -new -- folder:pm/archive AND tag:new
    +pm +sent -new -- folder:pm/sent AND tag:new
    +gmail +mailings -new -- folder:gmail/inbox
EOF
echo "Untagging some archived mails."
notmuch tag -inbox -mailings -- tag:archive AND \(tag:inbox OR tag:mailings\)

Any message in gmail will have the mailings tag, always, if I delete it, it'll just be re-added.

alias pmb="/usr/bin/tmux new-session -d -s mail '/usr/bin/protonmail-bridge --cli'"

syncmail () {
    echo "Archiving mails (if any)..."
    /usr/bin/notmuch search --format=text0 --output=files \
        tag:archive AND NOT folder:local/archive \
        | rsync -v0 --no-R --files-from=- / /home/user/.mail/local/archive/new
    echo "Moving mails..."
    /usr/bin/afew -vam
    echo "Syncing mailboxes..."
    /usr/bin/mbsync -a
    echo "Updating database..."
    /usr/bin/notmuch new
}

alot () {
    if [[ $1 == "-x" ]]
    then
        echo "text/html; qutebrowser %s; nametemplate=%s.html; copiousoutput" > ~/.mailcap
    else
        echo "text/html; w3m -dump -o -document_charset=%{charset} %s; nametemplate=%s.html; copiousoutput" > ~/.mailcap
    fi
    command alot
}

The bad thing about the qutebrowser option is that for example if you're looking at a thread, you'll open one tab for each message in the thread. Not such a big deal, because usually emails that are in a thread will be better read in the terminal.

[ListMailsFilter]
[MailMover]
folders = pm/inbox pm/sent pm/mailings pm/archive pm/trash gmail/inbox
rename = True

pm/inbox = 'tag:killed OR tag:archive':pm/trash 'NOT tag:inbox':pm/archive
pm/sent = 'tag:killed OR tag:archive':pm/trash
pm/mailings = 'tag:killed OR tag:archive':pm/trash 'NOT tag:mailings':pm/archive
pm/archive = 'tag:killed OR tag:archive':pm/trash
pm/trash = 'NOT tag:killed AND NOT folder:local/archive':pm/inbox
gmail/inbox = 'tag:killed':local/trash

In summary, if I tag something killed, move it to the trash. If I untag inbox or mailings, archive it in Protonmail. If a message has the archive tag, it'll have been copied to the local archive folder in the first operation within the syncmail function, so move this copy to the trash folder, so that it can later be deleted in Protonmail's apps.

terminal_cmd = 'kitty'
[accounts]
[[protonmail]]
realname = HPR Listener
address = hprlistener@pm.me
#gpg_key = ABCDEFGH
sendmail_command = msmtp --account=protonmail -t
sent_box = maildir:///home/user/.mail/pm/sent
draft_box = maildir:///home/user/.mail/local/drafts
[[work-email]]
realname = Corporate Drone
address = drone@acme.com
#gpg_key = ABCDEFGH
sendmail_command = msmtp --account=corporate -t
sent_box = maildir:///home/user/.mail/pm/sent
draft_box = maildir:///home/user/.mail/local/drafts
[[[abook]]]
type = shellcommand
command = khard email --parsable
regexp = '^(?P<email>[^@]+@[^\t]+)\t+(?P<name>[^\t]+)'
ignorecase = True
[bindings]
g m = search tag:mailings AND NOT tag:killed
g i = search tag:inbox AND NOT tag:killed
g u = search tag:unread AND NOT tag:killed
g v = search tag:view
A = toggletags archive
M = toggletags mailings
I = t
& = toggletags killed
U = toggletags unread
V = toggletags view
s =
a =
[database]
path=/home/user/.mail

[user]
name=HPR Listener
primary_email=hprlistener@pm.me

other_email=hprcontributor@pm.me;winniethepooh@gmail.com;

[new]
tags=new
ignore=

[search]
exclude_tags=deleted;spam;

[maildir]
synchronize_flags=true
IMAPStore pm-remote
Host 127.0.0.1
Port 1143
User hprlistener@pm.me
PassCmd "pass Protonmail-Bridge"
SSLType STARTTLS
CertificateFile ~/.config/protonmail/bridge/cert.pem
PathDelimiter /

MaildirStore pm-local
Path ~/.mail/pm/
Inbox ~/.mail/pm/inbox
SubFolders Verbatim

Channel pm-inbox
Far :pm-remote:INBOX
Near :pm-local:inbox
SyncState *

Channel pm-mailings
Far :pm-remote:Folders/mailings
Near :pm-local:mailings
SyncState *

Channel pm-archive
Far :pm-remote:Archive
Near :pm-local:archive
SyncState *

Channel pm-sent
Far :pm-remote:Sent
Near :pm-local:sent
SyncState *

Channel pm-trash
Far :pm-remote:Trash
Near :pm-local:trash
Sync PushNew
SyncState *

IMAPStore gmail-remote
Host imap.gmail.com
User hprlistener@gmail.com
PassCmd "pass Google/IMAP/hprlistener@gmail.com"
AuthMechs LOGIN
SSLType IMAPS
SSLVersions TLSv1.3
PathDelimiter /

MaildirStore gmail-local
Path ~/.mail/gmail/
Inbox ~/.mail/gmail/inbox
Trash trash
SubFolders Verbatim

Channel gmail-inbox
Far :gmail-remote:INBOX
Near :gmail-local:inbox
Expunge Both
SyncState *
# Set default values for all following accounts.
defaults
logfile        ~/.msmtp.log

# Protonmail
account        protonmail
host           127.0.0.1
port           1025
from           user@pm.me
user           hprlistener@pm.me
passwordeval   "pass Protonmail-Bridge"
auth           plain
tls            on
tls_starttls   on
tls_trust_file /home/user/.config/protonmail/bridge/cert.pem

account        corporate
host           imap.acme.com
port           1025
from           drone@acme.com
user           drone@acme.com
passwordeval   "pass ACME/IMAP"

# Set a default account
account default : protonmail

a few things that I forgot to say in the show and didn't want to interject