su6s

Я получаю много почты и привык раскладывать письма по папкам и мейлбоксам. Зачастую, манипуляции с письмами несложно автоматизировать с помощью правил (фильтров), поддерживаемых подавляющим большинством почтовых клиентов. Часто доступ к системе фильтрации почты реализован через веб-интерфейс почтовой службы. Так, интерфейсы Яндекс и Gmail позволяют создать правила. До недавнего времени я пользовался этими возможностями, но "неудобства" накапливались. В этой заметке я опишу инструмент, оказавшийся более удобным.

Нужно:

  • Независимость от настроек почтового клиента
  • Централизация места описания правил
  • Возможность оперировать временем поступления письма
  • Поддержка флагов к письму
  • Поддержка регулярных выражений при обработке полей письма
Проще говоря, захотелось реализовать конвейер, в котором одна утилита фильтрует почту, другая просматривает уже отфильтрованное. В этом случае неважно, читаю ли я почту из WEB-, GUI- или CLI-интерфейса.

Задачу фильтрации, с выполнением всех пунктов списка выше, решает утилита imapfilter.

Честно признаюсь, мне нравится повозиться с разного рода скриптами, нравится разобраться с какой-нибудь неочевидной штуковиной. В общем и целом, я люблю простые решения. Простые в духе Slackware.

Так вот, чтобы отфильтровать почту, нам потребуется настроить imapfilter. Конфигурационный файл этой утилиты представляет собой программу на языке lua.

Lua — язык несложный, пришлось, конечно, полистать документацию, отработать примеры, поэкспериментировать.

Imapfilter же обеспечивает интерфейс доступа к вашему imap-ящику, лишая нас необходимости вникать в тонкости работы протокола IMAP, предоставляя набор методов для манипуляции почтой. Например, метод list_all() позволяет посмотреть список папок и боксов вашего аккаунта, а move_messages() используется для перемещения писем, is_seen() поможет проверить было ли прочитано письмо, is_older() дает возможность управлять письмами определенного возраста.

А как же практика? Начнем!

Открываем в редакторе файл ~/.imapfilter/config.lua, если нет такого файла, то создаем. По тексту придерживаемся правила, что все зеленое идет в конфиг, синее - заметки по lua.


timeout указывает ждать ответ сервера в течение указанного времени (секунды), namespace определяет хотим ли мы вручную адресовать наши мейлбоксы на сервере или нет. false означает, что хотим.

options.timeout = 120

options.namespace = false

Добавим логины

gmail = {username1, username2}
yandex = {username3, username4}

То есть мы определили две таблицы, — gmail и yandex с соответствующими элементами.

На основе таблиц с логинами заполним информацию об аккаунтах

gmail.username1 = IMAP {
    server = 'imap.gmail.com',
    username = 'username1@gmail.com',
    password = 'pass1',
    ssl = 'ssl3'
}

gmail.username2 = IMAP {
    server = 'imap.gmail.com',
    username = 'username2@gmail.com',
    password = 'pass2',
    ssl = 'ssl3'
}

yandex.username3 = IMAP {
    server = 'imap.yandex.ru',
    username = 'username3',
    password = 'pass3',
    ssl = 'ssl3'
}

yandex.username4 = IMAP {
    server = 'imap.yandex.ru',
    username = 'username4',
    password = 'pass4',
    ssl = 'ssl3'
}

Добавим подписки на мейл-листы

Lists = {
        '@bazar2.conectiva.com.br',
        '@lists.hellug.gr'
}

Добавим мейлбоксы, в которые будут раскладываться письма из мейл-листов

(Порядок мейлбоксов должен соответствовать порядку мейл-листов)

Mailboxes = {
        'lua',
        'imapfilter'
}

Информация о количестве новых, прочитанных и т.д. письмах получается путем указания пути до нужного бокса (INBOX), потом двоеточие (:) и метод check_status().

gmail.username1.INBOX:check_status()
gmail.username2.INBOX:check_status()
yandex.username3.INBOX:check_status()
yandex.username4.INBOX:check_status()

Сохраним в переменную result письма, в теме которых находится выражение, и письма уже просмотрены

result = yandex.username4.INBOX:contain_subject('выражение') *
        yandex.username4.INBOX:is_seen()

Основной момент здесь — звездочка (*), которая играет роль логического "И"

Перемещаем письма, удовлетворяющие условию result, в мейлбокс MAILBOX

Тут стоит обратить внимание, что запись yandex.username4.MAILBOX эквивалентна
yandex.username4['MAILBOX']

yandex.username4.INBOX:move_messages(yandex.username4['MAILBOX'],result)

Работаем с мейл-листами. Рассылки будем обрабатывать пачкой. Для начала выясним количество элементов в таблице Lists

Lcount = # Lists

Теперь перебираем в цикле наши подписки, выделяем просмотренные письма

for i=1,Lcount do
result = (
                gmail.username1.INBOX:contain_from("'"..Lists[i].."'") +
                gmail.username1.INBOX:contain_to("'"..Lists[i].."'")
        ) *
       
        gmail.username1.INBOX:is_seen()

Как видно выше, правила можно группировать скобками, знак плюс (+) играет роль логического "ИЛИ". Конструкция что-то_..еще_..что-то - конкатенация, результат — что-то_еще_что-то


Раскладываем по мейлбоксам (таблица Mailboxes). Мейлбокс будет создан, если не существует
gmail.username1.INBOX:move_messages(gmail.username1["lists/"..Mailboxes[i]], result)
end

Запустить фильтр легко — достаточно выполнить команду imapfilter.

Не забудем установить права 700 на каталог ~/.imapfilter и 600 на файлы в нем.

При первом старте imapfilter скорее всего предложит сохранить сертификаты для доступа к серверу через протокол ssl, я согласился и сохранил.

В заключение замечу, что imapfilter умеет работать в режиме демона и поддерживает пайпы.

Берегите свое время.

Блог aap-blog.blogspot.com теперь будет здесь. Записи по старому адресу удалять не планирую. Переносить старое сюда пока что тоже не собираюсь.