[mail] Include all mail configs in dotfiles

This commit is contained in:
Adam Cooper 2023-01-01 11:28:34 -05:00
parent 3e756eed3c
commit 89ff511993
9 changed files with 614 additions and 1 deletions

2
.gitignore vendored
View file

@ -1,6 +1,6 @@
*.swp
.DS_Store
f/
bin/mail-sync.sh
**/newsboat/.newsboat/cache.db*
**/newsboat/.newsboat/history.cmdline
**/newsboat/.newsboat/ttrss-pw.txt

16
mail/aerc/accounts.conf Normal file
View file

@ -0,0 +1,16 @@
[migadu]
#
# Switching back to notmuch (from maildir experiment)
#
source = notmuch:///home/adam/Maildir
maildir-store = /home/adam/Maildir
outgoing = /home/adam/.local/bin/aerc-notmuch-send migadu
default = INBOX
from = Adam Cooper <adam@theadamcooper.com>
# notmuch does not support the copy-to directive
# copy-to = Sent
check-mail = 4m
check-mail-cmd = /home/adam/.local/bin/mail-sync.sh
check-mail-timeout = 20s

191
mail/aerc/aerc.conf Normal file
View file

@ -0,0 +1,191 @@
#
# aerc main configuration
[ui]
#
# Describes the format for each row in a mailbox view. This field is compatible
# with mutt's printf-like syntax.
#
# Default: %D %-17.17n %Z %s
index-format=%D %-17.17n %Z %s
#
# See time.Time#Format at https://godoc.org/time#Time.Format
#
# Default: 2006-01-02 03:04 PM (ISO 8601 + 12 hour time)
timestamp-format=2006-01-02 15:04
#
# Width of the sidebar, including the border.
#
# Default: 20
sidebar-width=20
#
# Message to display when viewing an empty folder.
#
# Default: (no messages)
empty-message=(no messages)
# Message to display when no folders exists or are all filtered
#
# Default: (no folders)
empty-dirlist=(no folders)
# Enable mouse events in the ui, e.g. clicking and scrolling with the mousewheel
#
# Default: false
mouse-enabled=false
#
# Ring the bell when new messages are received
#
# Default: true
new-message-bell=true
# Marker to show before a pinned tab's name.
#
# Default: `
pinned-tab-marker='`'
# Describes the format string to use for the directory list
#
# Default: %n %>r
dirlist-format=%n %>r
# List of space-separated criteria to sort the messages by, see *sort*
# command in *aerc*(1) for reference. Prefixing a criterion with "-r "
# reverses that criterion.
#
# Example: "from -r date"
#
# Default: ""
sort=-r date
# Moves to next message when the current message is deleted
#
# Default: true
next-message-on-delete=true
fuzzy-complete=true
[viewer]
#
# Specifies the pager to use when displaying emails. Note that some filters
# may add ANSI codes to add color to rendered emails, so you may want to use a
# pager which supports ANSI codes.
#
# Default: less -R
pager=less -R
#
# If an email offers several versions (multipart), you can configure which
# mimetype to prefer. For example, this can be used to prefer plaintext over
# html emails.
#
# Default: text/plain,text/html
alternatives=text/html,text/plain
#
# Default setting to determine whether to show full headers or only parsed
# ones in message viewer.
#
# Default: false
show-headers=false
#
# Layout of headers when viewing a message. To display multiple headers in the
# same row, separate them with a pipe, e.g. "From|To". Rows will be hidden if
# none of their specified headers are present in the message.
#
# Default: From|To,Cc|Bcc,Date,Subject
header-layout=From|To,Cc|Bcc,Date,Subject|Labels
# Whether to always show the mimetype of an email, even when it is just a single part
#
# Default: false
always-show-mime=false
# How long to wait after the last input before auto-completion is triggered.
#
# Default: 250ms
completion-delay=250ms
#
# Global switch for completion popovers
#
# Default: true
completion-popovers=true
[compose]
#
# Specifies the command to run the editor with. It will be shown in an embedded
# terminal, though it may also launch a graphical window if the environment
# supports it. Defaults to $EDITOR, or vi.
editor=
#
# Default header fields to display when composing a message. To display
# multiple headers in the same row, separate them with a pipe, e.g. "To|From".
#
# Default: To|From,Subject
header-layout=To|From,Subject
#
# Specifies the command to be used to tab-complete email addresses. Any
# occurrence of "%s" in the address-book-cmd will be replaced with what the
# user has typed so far.
#
# The command must output the completions to standard output, one completion
# per line. Each line must be tab-delimited, with an email address occurring as
# the first field. Only the email address field is required. The second field,
# if present, will be treated as the contact name. Additional fields are
# ignored.
address-book-cmd=khard email --parsable --remove-first-line %s
[filters]
#
# Filters allow you to pipe an email body through a shell command to render
# certain emails differently, e.g. highlighting them with ANSI escape codes.
#
# The first filter which matches the email's mimetype will be used, so order
# them from most to least specific.
#
# You can also match on non-mimetypes, by prefixing with the header to match
# against (non-case-sensitive) and a comma, e.g. subject,text will match a
# subject which contains "text". Use header,~regex to match against a regex.
subject,~^\[PATCH=awk -f /usr/local/share/aerc/filters/hldiff
text/html=/usr/local/share/aerc/filters/html
text/*=awk -f /usr/local/share/aerc/filters/plaintext
image/*=catimg -w $(tput cols) -
[triggers]
#
# Triggers specify commands to execute when certain events occur.
#
# Example:
# new-email=exec notify-send "New email from %n" "%s"
#
# Executed when a new email arrives in the selected folder
new-email=exec notify-send "New email from %n" "%s"
[templates]
# Templates are used to populate email bodies automatically.
#
# The directories where the templates are stored. It takes a colon-separated
# list of directories.
#
# default: /usr/local/share/aerc/templates/
template-dirs=/usr/local/share/aerc/templates/
# The template to be used for quoted replies.
#
# default: quoted_reply
quoted-reply=quoted_reply
# The template to be used for forward as body.
#
# default: forward_as_body
forwards=forward_as_body

106
mail/aerc/binds.conf Normal file
View file

@ -0,0 +1,106 @@
# Binds are of the form <key sequence> = <command to run>
# To use '=' in a key sequence, substitute it with "Eq": "<Ctrl+Eq>"
# If you wish to bind #, you can wrap the key sequence in quotes: "#" = quit
<C-p> = :prev-tab<Enter>
<C-n> = :next-tab<Enter>
<C-t> = :term<Enter>
[messages]
q = :quit<Enter>
j = :next<Enter>
<Down> = :next<Enter>
<C-d> = :next 50%<Enter>
<C-f> = :next 100%<Enter>
<PgDn> = :next -s 100%<Enter>
k = :prev<Enter>
<Up> = :prev<Enter>
<C-u> = :prev 50%<Enter>
<C-b> = :prev 100%<Enter>
<PgUp> = :prev -s 100%<Enter>
g = :select 0<Enter>
G = :select -1<Enter>
J = :next-folder<Enter>
K = :prev-folder<Enter>
v = :mark -t<Enter>
V = :mark -v<Enter>
<Enter> = :view<Enter>
# Replace deletion key bindings to move items to the trash
# d = :prompt 'Really delete this message?' 'delete-message'<Enter>
# D = :delete<Enter>
d = :mv Trash<Enter>
D = :mv Trash<Enter>
A = :archive flat<Enter>
C = :compose<Enter>
rr = :reply -a<Enter>
rq = :reply -aq<Enter>
Rr = :reply<Enter>
Rq = :reply -q<Enter>
c = :cf<space>
$ = :term<space>
! = :term<space>
| = :pipe<space>
/ = :search<space>
\ = :filter<space>
n = :next-result<Enter>
N = :prev-result<Enter>
[view]
q = :close<Enter>
| = :pipe<space>
D = :delete<Enter>
S = :save<space>
A = :archive flat<Enter>
f = :forward<Enter>
rr = :reply -a<Enter>
rq = :reply -aq<Enter>
Rr = :reply<Enter>
Rq = :reply -q<Enter>
H = :toggle-headers<Enter>
<C-k> = :prev-part<Enter>
<C-j> = :next-part<Enter>
J = :next<Enter>
K = :prev<Enter>
[compose]
# Keybindings used when the embedded terminal is not selected in the compose
# view
$ex = <C-x>
<C-k> = :prev-field<Enter>
<C-j> = :next-field<Enter>
<tab> = :next-field<Enter>
[compose::editor]
# Keybindings used when the embedded terminal is selected in the compose view
$noinherit = true
$ex = <C-x>
<C-k> = :prev-field<Enter>
<C-j> = :next-field<Enter>
<C-p> = :prev-tab<Enter>
<C-n> = :next-tab<Enter>
[compose::review]
# Keybindings used when reviewing a message to be sent
y = :send<Enter>
n = :abort<Enter>
p = :postpone<Enter>
q = :abort<Enter>
e = :edit<Enter>
a = :attach<space>
[terminal]
$noinherit = true
$ex = <C-x>
<C-p> = :prev-tab<Enter>
<C-n> = :next-tab<Enter>

37
mail/aerc/mail-sync.sh.example Executable file
View file

@ -0,0 +1,37 @@
#!/bin/sh
OFFLINEIMAP=$(pgrep offlineimap)
NOTMUCH=$(pgrep notmuch)
if [ -n "$OFFLINEIMAP" ] || [ -n "$NOTMUCH" ]; then
echo "Already running one instance of offlineimap or notmuch. Exiting..."
exit 0
fi
echo "Deleting messages tagged as *deleted*"
notmuch search --format=text0 --output=files tag:deleted | xargs -0 --no-run-if-empty rm -v
offlineimap -o
notmuch new
# retag all "new" messages "inbox" and "unread"
notmuch tag +inbox +unread -new -- tag:new
# mailing lists
notmuch tag +mailinglist -- to:xmonad@haskell.org
notmuch tag +mailinglist -- to:guile-user@gnu.org
notmuch tag +mailinglist -- to:zsh-users@zsh.org
notmuch tag +mailinglist -- to:qutebrowser@lists.qutebrowser.org
notmuch tag +mailinglist -- to:~rjarry/aerc-discuss@lists.sr.ht
# TODO: Confirm that we need to do this
# move tagged items across folders and retag
notmuch search --output=files tag:trash and not folder:Trash | xargs mv -t /home/adam/Maildir/Trash/cur/
notmuch tag +trash -inbox -sent -archive -junk -drafts -- folder:Trash and not tag:trash
# move unimportant institutional messages to the trash after a couple of days
notmuch tag +trash -inbox -- date:..2d and tag:mailinglist
notmuch tag +trash -inbox -- date:..2d and from:@facebookmail.com
notmuch tag +trash -inbox -- date:..2d and from:noreply@twitch.tv
notmuch tag +trash -inbox -- date:..2d and from:messages-noreply@linkedin.com

View file

@ -0,0 +1,87 @@
# .notmuch-config - Configuration file for the notmuch mail system
#
# For more information about notmuch, see https://notmuchmail.org
# Database configuration
#
# The only value supported here is 'path' which should be the top-level
# directory where your mail currently exists and to where mail will be
# delivered in the future. Files should be individual email messages.
# Notmuch will store its database within a sub-directory of the path
# configured here named ".notmuch".
#
[database]
path=/home/adam/Maildir
# User configuration
#
# Here is where you can let notmuch know how you would like to be
# addressed. Valid settings are
#
# name Your full name.
# primary_email Your primary email address.
# other_email A list (separated by ';') of other email addresses
# at which you receive email.
#
# Notmuch will use the various email addresses configured here when
# formatting replies. It will avoid including your own addresses in the
# recipient list of replies, and will set the From address based on the
# address to which the original email was addressed.
#
[user]
name=Adam Cooper
primary_email=adam@theadamcooper.com
# Configuration for "notmuch new"
#
# The following options are supported here:
#
# tags A list (separated by ';') of the tags that will be
# added to all messages incorporated by "notmuch new".
#
# ignore A list (separated by ';') of file and directory names
# that will not be searched for messages by "notmuch new".
#
# NOTE: *Every* file/directory that goes by one of those
# names will be ignored, independent of its depth/location
# in the mail store.
#
[new]
# Search configuration
#
# The following option is supported here:
#
# exclude_tags
# A ;-separated list of tags that will be excluded from
# search results by default. Using an excluded tag in a
# query will override that exclusion.
#
[search]
# Maildir compatibility configuration
#
# The following option is supported here:
#
# synchronize_flags Valid values are true and false.
#
# If true, then the following maildir flags (in message filenames)
# will be synchronized with the corresponding notmuch tags:
#
# Flag Tag
# ---- -------
# D draft
# F flagged
# P passed
# R replied
# S unread (added when 'S' flag is not present)
#
# The "notmuch new" command will notice flag changes in filenames
# and update tags, while the "notmuch tag" and "notmuch restore"
# commands will notice tag changes and update flags in filenames
#
[maildir]
synchronize_flags=true
[index]
header.Context-Transfer-Encoding=Context-Transfer-Encoding

25
mail/offlineimap/config Normal file
View file

@ -0,0 +1,25 @@
[general]
accounts = main
pythonfile = /home/adam/.config/offlineimap/offlineimap.py
maxsyncaccounts = 1
[Account main]
localrepository = main-local
remoterepository = main-remote
# autorefresh = 0.5
# quick = 10
[Repository main-local]
type = Maildir
localfolders = ~/Maildir
[Repository main-remote]
type = IMAP
remotehost = imap.migadu.com
remoteuser = adam@theadamcooper.com
remotepasseval = get_pw_from_attrs("service", "migadu")
starttls = yes
ssl = yes
sslcacertfile = /etc/ssl/certs/ca-certificates.crt
# keepalive = 60
# holdconnectionopen = yes

View file

@ -0,0 +1,151 @@
'''
offlineimap.py
This provides a handful of functions for retrieving secrets from GNOME Keyring
using the libsecret API. See the documentation for each function
'''
from gi import require_version
require_version('Secret', '1')
from gi.repository import Secret
def get_pw_from_desc(pw_desc) :
'''
This function returns the password for an item in the default keyring
which contains the description provided.
Use this function if you created a password using the dialogue in Seahorse
'''
# Get service
service = Secret.Service.get_sync(Secret.ServiceFlags.LOAD_COLLECTIONS)
# Get default keyring
keyring = Secret.Collection.for_alias_sync(service, "default", \
Secret.CollectionFlags.NONE, None)
# Get keyring items
items = keyring.get_items()
# Load secrets
Secret.Item.load_secrets_sync(items)
# Loop through items, find the matching one and return its password
password = None
for item in items :
if item.get_label() == pw_desc :
password = item.get_secret().get_text()
break
# Close connection
service.disconnect()
return password
def get_pw_from_attrs(*attr_val_pairs) :
'''
This function returns the password for an item in the default keyring
which contains all of the attribute value pairs provided as arguments.
Use this function if you created a password using the secret-tool command
or another such program that interfaces with libsecret
'''
# Check the list of attr-val pairs is present and contains an even number
# of elements
if attr_val_pairs == () :
raise TypeError("get_pw_from_attrs() at least 1 attribute-value pair " \
"must be supplied")
if len(attr_val_pairs) % 2 != 0 :
raise TypeError("get_pw_from_attrs() incomplete attribute-value " \
"pair was supplied")
# Get service
service = Secret.Service.get_sync(Secret.ServiceFlags.LOAD_COLLECTIONS)
# Get default keyring
keyring = Secret.Collection.for_alias_sync(service, "default", \
Secret.CollectionFlags.NONE, None)
# Get keyring items
items = keyring.get_items()
# Load secrets
Secret.Item.load_secrets_sync(items)
# Loop through items, find the one which contains all supplied attr_val
# pairs and return its password
password = None
for item in items :
attrs = item.get_attributes()
match = True
for x in range(0, len(attr_val_pairs), 2) :
key = attr_val_pairs[x]
value = attr_val_pairs[x + 1]
try :
if attrs[key] != value :
match = False
break
except KeyError :
match = False
break
if match :
password = item.get_secret().get_text()
break
# Close connection
service.disconnect()
return password
def get_val_from_attrs(attr, *attr_val_pairs) :
'''
This function returns the value for a given attribute. The first item
found that contains that attribute will be the one that is used. To ensure
that the correct item is chosen, any number of attribute-value pairs can
be optionally supplied as arguments and only the item which contains all
of those attr-val pairs (along with the main attr) will be used.
Use this function if you created a password using the secret-tool command
or another such program that interfaces with libsecret
'''
# Check the list of attr-val pairs contains an even number of elements
# if it exists
if attr_val_pairs != () :
if len(attr_val_pairs) % 2 != 0 :
raise TypeError("get_val_from_attrs() incomplete attribute-value " \
"pair was supplied")
# Get service
service = Secret.Service.get_sync(Secret.ServiceFlags.LOAD_COLLECTIONS)
# Get default keyring
keyring = Secret.Collection.for_alias_sync(service, "default", \
Secret.CollectionFlags.NONE, None)
# Get keyring items
items = keyring.get_items()
# Loop through items, find the one which contains the supplied attribute
# (plus any attr_val pairs if specified) and return that attribute's
# value
attr_value = None
for item in items :
attrs = item.get_attributes()
try :
attrs[attr]
except KeyError :
continue
match = True
for x in range(0, len(attr_val_pairs), 2) :
key = attr_val_pairs[x]
value = attr_val_pairs[x + 1]
try :
if attrs[key] != value :
match = False
break
except KeyError :
match = False
break
if match :
attr_value = attrs[attr]
break
# Close connection
service.disconnect()
return attr_value