399 lines
11 KiB
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 &
|
|
'';
|
|
};
|
|
}
|