nix/modules/home-manager/accounts/email/default.nix

399 lines
11 KiB
Nix

{
lib,
config,
pkgs,
inputs,
...
}:
with lib; let
cfg = config.modules.email;
secretsDirectory = "${(builtins.toString inputs.nix-private)}/secrets";
in {
options.modules.email = {
enable = mkEnableOption "Enable email accounts configuration";
accountConfigs = mkOption {
type = types.attrsOf types.attrs;
default = false;
description = "List of account variables used to create accounts";
};
primary = mkOption {
type = types.str;
default = false;
description = "name of primary account";
};
};
config = mkIf cfg.enable {
sops = let
secretList =
lib.mapAttrs' (
name: value:
nameValuePair (value.secret.key) {
sopsFile = "${secretsDirectory}/${value.secret.file}";
}
)
cfg.accountConfigs;
in {
secrets = secretList;
};
accounts.email = let
defaultSetting = {
mbsync = {
enable = true;
create = "both";
expunge = "both";
remove = "both";
patterns = [
"*"
# Trash is a pain in the ass to manage with notmuch
"!Trash"
];
};
msmtp = {
enable = true;
};
notmuch = {
enable = true;
};
neomutt = {
enable = true;
showDefaultMailbox = false;
};
};
accountsList = lib.mapAttrs (name: value: lib.recursiveUpdate defaultSetting value.config) cfg.accountConfigs;
in {
maildirBasePath = "mail";
accounts = lib.recursiveUpdate accountsList {"${cfg.primary}".primary = true;};
};
programs.afew = let
mailMoverRules = lib.mergeAttrsList (
lib.attrsets.mapAttrsToList (
n: v:
if lib.hasAttrByPath ["afew" "mailMover"] v
then v.afew.mailMover
else {}
)
cfg.accountConfigs
);
mailFilterRules = lib.flatten (
lib.attrsets.mapAttrsToList (
n: v:
if lib.hasAttrByPath ["afew" "filters"] v
then v.afew.filters
else []
)
cfg.accountConfigs
);
in {
enable = true;
extraConfig = ''
[FolderNameFilter]
folder_explicit_list = archives
folder_transforms = archives:archived
maildir_separator = /
[MailMover]
folders = ${lib.concatStringsSep " " (lib.unique (lib.mapAttrsToList (n: v: "${n}") mailMoverRules))}
rename = True
${lib.concatStringsSep "\n" (lib.mapAttrsToList (n: v: "${n} = ${toString v}") mailMoverRules)}
[SpamFilter]
[KillThreadsFilter]
[ArchiveSentMailsFilter]
sent_tag = sent
${lib.concatImapStrings (p: e: "\n\n[Filter.${toString p}]\n ${lib.concatStringsSep "\n" (lib.mapAttrsToList (n: v: "${n} = ${v}") e)}") mailFilterRules}
[InboxFilter]
'';
};
programs.neomutt = let
accountMacros =
lib.imap1 (
i: elem:
elem
// {
map = ["index" "pager"];
key = "<F${(toString (i + 1))}>";
}
) (
lib.attrsets.mapAttrsToList (
n: c: {"action" = "<sync-mailbox><refresh><enter-command>source ~/.config/neomutt/${n}<enter><change-vfolder>Unread:${c.config.address}<enter>";}
)
cfg.accountConfigs
);
in {
enable = true;
unmailboxes = true;
changeFolderWhenSourcingAccount = true;
sourcePrimaryAccount = false;
editor = "nvim +/^$/ +':nohl'";
settings = {
sleep_time = "1";
mbox_type = "Maildir";
header_color_partial = "yes";
duplicate_threads = "yes";
index_format = "'%4C| %24.24?GS?%F %GS &%F? %?GR?%GR &%?GU?%GU & ??%?GA?%GA & ?%?GE?%GE & ?%?M?󰘕 %s&%s? %* %?g? %g? %<[y?%<[m?%<[d?%9[%H:%M ]&%9[%a %d ]>&%9[%b %d ]>&%9[%m/%y ]>'";
sort_aux = "last-date-sent";
mail_check = "120";
hidden_tags = "inbox,unread,draft,flagged,passed,replied,signed,encrypted,attachment,sent";
markers = "no";
wrap = "90";
smart_wrap = "yes";
reflow_text = "yes";
reflow_wrap = "90";
text_flowed = "yes";
search_context = "3";
pager_context = "5";
pager_index_lines = "10";
rfc2047_parameters = "yes";
edit_headers = "yes";
send_charset = "utf-8";
envelope_from = "yes";
use_envelope_from = "no";
my_status = "' %o/%m | %l 󰉉 | %f %* Sort: %s-%S Pos: %P '";
my_pager = "' %F | %s %* Pos: %P '";
compose_format = "' COMPOSE %a | 󰉉 %l'";
query_command = "'${pkgs.khard}/bin/khard email --parsable --search-in-source-files %s'";
virtual_spoolfile = "yes";
mail_check_stats = "yes";
mh_purge = "yes";
mailcap_path = "${config.xdg.configHome}/neomutt/mailcap";
};
extraConfig = ''
${(builtins.readFile ./files/theme.muttrc)}
set reverse_name
charset-hook ^iso-8859-1$ cp1252
ignore *
unignore from date subject to cc bcc tags user-agent x-mailer
# Attachment
auto_view text/x-vcard text/html text/enriched text/calendar
alternative_order text/html text/enriched text/plain text/*
tag-transforms "attachment" "󰁦" \
"encrypted" "󱧈" \
"signed" "󱅞" \
"unread" "" \
"replied" ""
tag-formats "attachment" "GA" \
"encrypted" "GE" \
"signed" "GS" \
"unread" "GU" \
"replied" "GR"
${lib.concatStrings (lib.attrsets.mapAttrsToList (_: v:
if lib.hasAttrByPath ["neomuttHooks"] v
then v.neomuttHooks
else "")
cfg.accountConfigs)}
# manually source first account instead of use home-manager parameter because
# of $my_pager expansion does not work as this variable is not already set
source ${config.xdg.configHome}/neomutt/${cfg.primary}
'';
binds = [
{
map = ["attach" "browser" "index" "pager"];
key = "g";
action = "noop";
}
{
map = ["attach" "browser" "index" "pager"];
key = "G";
action = "noop";
}
{
map = ["index"];
key = "q";
action = "noop";
}
{
map = ["pager"];
key = "Q";
action = "noop";
}
{
map = ["attach" "browser" "index"];
key = "gg";
action = "first-entry";
}
{
map = ["attach" "browser" "index"];
key = "G";
action = "last-entry";
}
{
map = ["pager"];
key = "gg";
action = "top";
}
{
map = ["pager"];
key = "G";
action = "bottom";
}
{
map = ["pager"];
key = "k";
action = "previous-line";
}
{
map = ["pager"];
key = "j";
action = "next-line";
}
# Scrolling
{
map = ["attach" "browser" "pager" "index"];
key = "\\CF";
action = "next-page";
}
{
map = ["attach" "browser" "pager" "index"];
key = "\\CB";
action = "previous-page";
}
{
map = ["attach" "browser" "pager" "index"];
key = "\\Cu";
action = "half-up";
}
{
map = ["attach" "browser" "pager" "index"];
key = "\\Cd";
action = "half-down";
}
{
map = ["browser" "pager"];
key = "\\Ce";
action = "next-line";
}
{
map = ["browser" "pager"];
key = "\\Cy";
action = "previous-line";
}
{
map = ["index"];
key = "\\Ce";
action = "next-line";
}
{
map = ["index"];
key = "\\Cy";
action = "previous-line";
}
# Reply
{
map = ["pager" "index"];
key = "R";
action = "group-reply";
}
# sidebar
{
map = ["index" "pager"];
key = "<f12>";
action = "sidebar-toggle-visible";
}
{
map = ["index" "pager"];
key = "{";
action = "sidebar-prev";
}
{
map = ["index" "pager"];
key = "}";
action = "sidebar-next";
}
{
map = ["index" "pager"];
key = "|";
action = "sidebar-open";
}
# open virtual folder
{
map = ["index" "pager"];
key = "X";
action = "noop";
}
{
map = ["index" "pager"];
key = "X";
action = "change-vfolder";
}
# read entire thread of the current message
{
map = ["index" "pager"];
key = "+";
action = "entire-thread";
}
# generate virtual folder from query
{
map = ["index" "pager"];
key = "\\eX";
action = "vfolder-from-query";
}
# generate virtual folder from query with time window
{
map = ["index" "pager"];
key = "\\CD";
action = "modify-tags";
}
# Editor
{
map = ["editor"];
key = "<Tab>";
action = "complete-query";
}
{
map = ["editor"];
key = "^T";
action = "complete";
}
];
macros =
[
{
map = ["pager"];
key = "\\CB";
action = "<pipe-message>${pkgs.urlscan}/bin/urlscan -d -c --color true<Enter>";
}
]
++ accountMacros;
};
programs.mbsync = {
enable = true;
};
services.mbsync = {
enable = true;
postExec = "${pkgs.notmuch}/bin/notmuch new";
};
programs.notmuch = {
enable = true;
new.tags = ["new"];
hooks.postNew = ''
${pkgs.afew}/bin/afew --tag --new
${pkgs.afew}/bin/afew --move --all
'';
};
programs.msmtp = {
enable = true;
};
home.file."${config.xdg.configHome}/urlscan/config.json".source = ./files/urlscan.config.json;
xdg.configFile."neomutt/mailcap".text = ''
text/html; ${pkgs.xdg-utils}/bin/xdg-open %s; nametemplate=%html
text/html; ${pkgs.w3m}/bin/w3m -I %{charset} -cols 90 -T text/html %s; copiousoutput
application/pdf; ${pkgs.xdg-utils}/bin/xdg-open %s &
image/*; ${pkgs.xdg-utils}/bin/xdg-open %s &
'';
};
}