mirror of
https://github.com/JulienMalka/snowfield.git
synced 2025-06-08 02:09:04 +02:00
chore: update mailmover
This commit is contained in:
parent
5ae45f779a
commit
88f786527c
1 changed files with 163 additions and 0 deletions
163
home-manager-modules/mails/mailmover.lua
Normal file
163
home-manager-modules/mails/mailmover.lua
Normal file
|
@ -0,0 +1,163 @@
|
||||||
|
local match_modes = {
|
||||||
|
ALL = "all",
|
||||||
|
FIRST = "first",
|
||||||
|
UNIQUE = "unique",
|
||||||
|
}
|
||||||
|
|
||||||
|
--- Execute a shell command and return its output
|
||||||
|
local function execute_command(cmd)
|
||||||
|
local handle = io.popen(cmd)
|
||||||
|
local result = handle:read("*a")
|
||||||
|
handle:close()
|
||||||
|
return result
|
||||||
|
end
|
||||||
|
|
||||||
|
--- Get all folders from the Maildir structure
|
||||||
|
local function get_maildir_folders(maildir_path)
|
||||||
|
local cmd = "find " .. maildir_path .. " -type d -name cur | sed 's|/cur$||' | sed 's|" .. maildir_path .. "/||'"
|
||||||
|
local output = execute_command(cmd)
|
||||||
|
|
||||||
|
local folders = {}
|
||||||
|
for folder in string.gmatch(output, "([^\n]+)") do
|
||||||
|
if folder ~= "" then
|
||||||
|
folders[#folders+1] = folder
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
return folders
|
||||||
|
end
|
||||||
|
|
||||||
|
--- Check if one path is a parent of another
|
||||||
|
local function is_parent_path(parent, child)
|
||||||
|
return child:match("^" .. parent .. "/") ~= nil
|
||||||
|
end
|
||||||
|
|
||||||
|
--- Generate rules based on folder structure
|
||||||
|
local function generate_rules(maildir_path)
|
||||||
|
local folders = get_maildir_folders(maildir_path)
|
||||||
|
local rules = {}
|
||||||
|
|
||||||
|
-- Create a sorted copy of folders, from longest path to shortest
|
||||||
|
-- This ensures we process children before parents
|
||||||
|
local sorted_folders = {}
|
||||||
|
for _, folder in ipairs(folders) do
|
||||||
|
table.insert(sorted_folders, folder)
|
||||||
|
end
|
||||||
|
table.sort(sorted_folders, function(a, b)
|
||||||
|
return #a > #b
|
||||||
|
end)
|
||||||
|
|
||||||
|
-- Store each folder's subfolder tags for exclusion
|
||||||
|
local subfolder_tags = {}
|
||||||
|
for _, folder in ipairs(sorted_folders) do
|
||||||
|
subfolder_tags[folder] = {}
|
||||||
|
end
|
||||||
|
|
||||||
|
-- Collect all subfolder-specific tags for each folder
|
||||||
|
for _, folder in ipairs(sorted_folders) do
|
||||||
|
local parts = {}
|
||||||
|
for part in string.gmatch(folder, "([^/]+)") do
|
||||||
|
table.insert(parts, part)
|
||||||
|
end
|
||||||
|
|
||||||
|
-- For each folder, find its parent folders and add its deepest tag to their exclusion list
|
||||||
|
if #parts > 0 then
|
||||||
|
local deepest_tag = string.lower(parts[#parts])
|
||||||
|
local parent_path = ""
|
||||||
|
|
||||||
|
for i = 1, #parts - 1 do
|
||||||
|
if i > 1 then
|
||||||
|
parent_path = parent_path .. "/"
|
||||||
|
end
|
||||||
|
parent_path = parent_path .. parts[i]
|
||||||
|
|
||||||
|
-- Add this tag to the parent's exclusion list
|
||||||
|
if subfolder_tags[parent_path] then
|
||||||
|
table.insert(subfolder_tags[parent_path], deepest_tag)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
-- Now generate rules with proper exclusions
|
||||||
|
for _, folder in ipairs(folders) do
|
||||||
|
-- Skip Trash and Sent as they're already handled
|
||||||
|
if folder ~= "Trash" and folder ~= "Sent" then
|
||||||
|
local query_parts = {}
|
||||||
|
local exclusion_parts = {}
|
||||||
|
|
||||||
|
-- Convert each folder component to a lowercase tag requirement
|
||||||
|
for part in string.gmatch(folder, "([^/]+)") do
|
||||||
|
table.insert(query_parts, "tag:" .. string.lower(part))
|
||||||
|
end
|
||||||
|
|
||||||
|
-- Add exclusions for subfolder-specific tags
|
||||||
|
if subfolder_tags[folder] then
|
||||||
|
for _, tag in ipairs(subfolder_tags[folder]) do
|
||||||
|
table.insert(exclusion_parts, "not tag:" .. tag)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
-- Build the complete query
|
||||||
|
local query = table.concat(query_parts, " and ")
|
||||||
|
if #exclusion_parts > 0 then
|
||||||
|
query = query .. " and " .. table.concat(exclusion_parts, " and ")
|
||||||
|
end
|
||||||
|
|
||||||
|
-- Add rule for this folder
|
||||||
|
rules[#rules+1] = {
|
||||||
|
folder = folder,
|
||||||
|
query = query,
|
||||||
|
}
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
-- Sort rules by complexity (number of tags) - more specific rules first
|
||||||
|
table.sort(rules, function(a, b)
|
||||||
|
if a.folder == "Trash" then return true end
|
||||||
|
if b.folder == "Trash" then return false end
|
||||||
|
if a.folder == "Sent" then return true end
|
||||||
|
if b.folder == "Sent" then return false end
|
||||||
|
|
||||||
|
local a_count = select(2, string.gsub(a.query, "tag:", ""))
|
||||||
|
local b_count = select(2, string.gsub(b.query, "tag:", ""))
|
||||||
|
return a_count > b_count
|
||||||
|
end)
|
||||||
|
|
||||||
|
local final_rules = {}
|
||||||
|
for _, rule in ipairs(rules) do
|
||||||
|
if string.lower(rule.folder) ~= "drafts" then
|
||||||
|
table.insert(final_rules, rule)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
return final_rules
|
||||||
|
end
|
||||||
|
|
||||||
|
-- Path to maildir
|
||||||
|
local maildir_path = os.getenv("HOME") .. "/Maildir"
|
||||||
|
|
||||||
|
--- Configuration for notmuch-mailmover.
|
||||||
|
--
|
||||||
|
--- @class config
|
||||||
|
--- @field maildir string Path to the maildir
|
||||||
|
--- @field notmuch_config string Path to the notmuch configuration
|
||||||
|
--- @field rename boolean Rename the files when moving
|
||||||
|
--- @field max_age_days number Maximum age (days) of the messages to be procssed
|
||||||
|
--- @field rule_match_mode config.match_mode Match mode for rules
|
||||||
|
--- @field rules rule[] List of rules
|
||||||
|
---
|
||||||
|
--- @class rule
|
||||||
|
--- @field folder string Folder to move the messages to
|
||||||
|
--- @field query string Notmuch query to match the messages
|
||||||
|
local config = {
|
||||||
|
maildir = maildir_path,
|
||||||
|
notmuch_config = "/home/julien/.config/notmuch/default/config",
|
||||||
|
rename = true,
|
||||||
|
max_age_days = 356,
|
||||||
|
rule_match_mode = match_modes.UNIQUE,
|
||||||
|
rules = generate_rules(maildir_path),
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
return config
|
Loading…
Add table
Add a link
Reference in a new issue