Да, я победил спам! Не «в основном», а «полностью и окончательно». Без всяких «почти». По крайней мере, на 99.9% победил. Причем этот 0.1% — не спам, прорвавшийся через фильтры, а false positives, которые не «вытащил» обратно в Inbox.
Eсли интересно — читайте под катом.
Программы и методы этой статьи могут быть адаптированны к любому из трех типов почтовых сервисов:
Gmail (используется Google Script)
Hotmail, Outlook.com, Live.com (почтовый сервис Майкрософта, используется Graph API)
Любой email сервис, поддерживающий IMAP через Basic Authentication (Login‑Password)
Собственно, false positives была проблема #1. В принципе, Gmail and Outlook.com хорошо справляются со спамом. Они отправляют его в Spam фолдер. В обоих сервисах у меня в этом фолдере в сутки было около 30 сообщений, но были случаи, когда это число доходило до 70.
В принципе ничего страшного, но ведь периодически (раз в неделю) надо заглядывать в Спам фолдер чтобы проверить, нет ли там false positive. А сделать это среди сотен спамовых сообщений не совсем тривиально.
В случае с Gmail‑ом можно настроить фильтры и применять их к сообщениям таким образом, чтобы проблемый email отправлялся миную Спам сразу в Корзину. А вот Hotmail это не позволяет: если сервис решил, что сообщение — спам, к нему не применяются фильтры (созданные в веб интерфейсе).
Вторая проблема: оба сервиса не позволяют «уничтожать» сообщения сразу, минуя Корзину. Через фильтры можно только переместить спам‑сообщение в Корзину, но не «прибить» его (кстати, mail.ru позволяет это сделать). Таким образом, даже при иделальной сортировке фильтрами в корзине скапливаются тысячи сообщений. Номинально оба сервиса говорят, что сообщения остаются в Корзине и Спаме 30 дней, но фактически это минимальный срок. В некоторых ситуациях они там могут сидеть месяцами (а то и годами), если их не вытирать вручную. Баг это или такой дизайн — я не знаю. А иметь сотни или тысячи стертых сообщений в Корзине — это вредно по разным причинам (Privacy, производительность, проблемы с поиском).
Третья проблема: имя и фамилия моей жены полностью совпадают с именем и фамилией нью‑йоркского врача (женщины). Соответственно, жена получала кучу медицинского спама и не совсем спама (рассылки медицинских журналов и реклама вполне легитимных медицинских препаратов и лекарств для применения врачами). От чего‑то удалось отписаться, но спам не прекращался.
Четвертая проблема: на аккаунт жены идут рассылки (судя по всему, медицинские) из европейских стран. Но мы не читаем по‑венгерски, по‑испански и по‑французски!
После того, как жена заявила «или я или спам», мне пришлось действовать.
Самым простым решением было бы написать простой сервис для доступа к емайлам через IMAP и делать фильтрацию программным путем в случаях, когда это невозможно сделать фильтрами. Естетственно, фильтрация будет производиться уже после получения письма, но если запускать cron job каждые 15 минут, то особой проблемы нет.
Иногда я получаю спам (на мой aol.com аккаунт), где английский Sender Name или Subject хитро закодирован и спам фильтры его не отлавливают, но с точки зрения человека все читается. Этот метод применяется если в теме письма, например, есть текст на английском и русском языке одновременно, но в случае с AOL спамом идет кодирование исключительно английского текста. Этот аккаунт в принципе не должен получать email‑ы на зыках, отличных от английского.
В качестве тестирования IMAP кода я взял мой aol.com аккаунт и вот что получилось (спасибо Claude.ai за помощьв фильтрации). Я убрал некоторые элементы из кода для этой статьи, но принцип, думаю, понятен.
Вот код:
https://github.com/comrade‑ogilvy/email‑antispam‑scripts/blob/main/imap/purge_aol_spam.py
Код по ссылке позволяет фильтровать символ «\u00ad» который используется в Subject‑e исключительно спаммерами. Именно этот символ в моем случае для моего аккаунта в aol.com является гарантированным 100%‑ым индикатором спама. Мало того, 99% спама были с этим символом.
Кроме того, теперь я могу полностью стереть сообщения с темой вроде «Привет, asmirnov» когда ваш email asmirnov@domain.tld, а вас зовут Александр Смирнов. Им не то что в Спам‑фолдере делать нечего, их, по‑хорошему, надо вообще не давать «приземлиться» в mailbox‑e
BLOCKED_NAME_KEYWORDS = ["mylogin"] # matched against decoded display name (user's login) BLOCKED_SUBJECT_KEYWORDS = ["mylogin"] # matched against decoded subject BLOCKED_DOMAINS = ["comms.aol.net", "ankerdc.com"] # e.g. ["spam.com", malicious.net"] SOFT_HYPHEN = "\u00ad"
При желании, можно полностью стереть сообщение или переместить его в Корзину.
Но у этого метода есть свои недостатки: нужен сервер, на котором в том или ином виде будут находится логин‑пароль, что не есть хорошо.
К счастью, у Гугла есть облачный сервис Google Script, который очень хорошо умеет работать с электронной почтой.
Логика кода та же, но не нужен отдельный VPS плюс не нужно писать пароль от вашего аккаунта в коде или где‑то хранить его.
который очищает мой Спам фолдер от гарантированного спама (он несколько отличается от реально работающего лода, связано со спецификой моих email‑ов, я убрал элементы, которые наверняка не будут интересны читателям Хабра). В большинстве случаев — навсегда, даже без перемещения в корзину. Например, если у sender domain отсутствуют MX и A records (From: ghsdjfadjfdjgfd@iuwadjkrei.com), то я сразу стираю это сообщение. Это правило уничтожило где‑то 90–95% спама
Есть и другие критерии:
Если сообщение в Спаме не от домена в моем (не РКН) белом списке, то переместить в Корзину.
Вот список:
var ALLOWED_TLDS = [ ".com", ".net", ".org", ".il", ".uа", ".ru", ".ca", ".cz", ];
Я, конечно получаю изредка легитимные сообщения из.uk или.es, но ни разу не было случая, что легитимное сообщение из.uk или.es было помечено Гуглом как спам (код по ссылке очищает именно Спам фолдер, а не Inbox). Пусть сразу идет в Корзину,
Eсли сообщение в моем (не РКН) черном списке, то отправить его в Корзину (хотя, надо наверное, стереть без следа)
Я точно знаю, что, к примеру, емайлы от veganinfo.com мне не нужны ни в каком варианте и никогда не будут нужны. Я не хочу их видеть и себя в mailbox‑e. Но... в данном примере, я все‑же решил не стирать немедленно такие сообщения. Их относительно мало и оно не являются проблемой. Пусть 3 дня поживут в Корзине.
Пример черного списка
var BLOCKED_DOMAINS = [ ".tk", "onlinecrm.marketing", "veganinfo.com", ];
После установки триггера скрипта на https://script.google.com на «запускать каждые 15 минут» в Спам фолдере остались, фактически только false positives. Их мало — максимум 5 в месяц, обычно меньше, но зато я их сейчас вижу без проблем.
Остальной спам сразу переходит в Корзину. Kорзина очищается этим же скриптом: остаются сообщения за последние 3 дня, мне так спокойнее.
function purgeDeletedFolder() { console.log("purgeDeletedFolder() started"); var threads = GmailApp.search('in:trash older_than:3d'); threads.forEach(function(thread) { Gmail.Users.Threads.remove('me', thread.getId()); }); console.log("purgeDeletedFolder() ended"); }
На каждое выполнение скрипта мне приходит отчет в Телеграм (на всякий случай).
function sendToTelegram() { var BOT_TOKEN = "xxxxxxxxx:zzzzzzzzzzz-wwwwwwwwwww"; var CHAT_ID = "-yyyyyyyyyyy"; var deleteRate = stats.processed > 0 ? ((stats.deleted / stats.processed) * 100).toFixed(2) : 0; var text = "Spam Cleanup Report\n\n" + "Processed: " + stats.processed + "\n" + "Deleted: " + stats.deleted + " (" + deleteRate + "%)\n" + "Permanently deleted: " + stats.permanently_deleted + "\n\n" + "SPF misaligned: " + stats.spf_misaligned + "\n" + "Infra abuse: " + stats.infra_abuse + "\n\n" + "Cache hits: " + stats.cache_hit; if (stats.permanently_deleted_list.length > 0) { text += "\n\n Permanently deleted:\n"; stats.permanently_deleted_list.slice(0, 20).forEach(function(e) { text += "• " + e + "\n"; }); if (stats.permanently_deleted_list.length > 20) { text += "...and more (" + stats.permanently_deleted_list.length + ")"; } } var url = "https://api.telegram.org/bot" + BOT_TOKEN + "/sendMessage"; UrlFetchApp.fetch(url, { method: "post", payload: { chat_id: CHAT_ID, text: text } }); }
Теперь займемся медицинским спамом.
Как я уже писал, жена получала много медицинского спама и спама с национальных европейских доменов. Фильтрация стандартными фильтрами по домену в Гугле проблематична. Например, если вы сделате фильтр для.de (Германия), то он отсеет также abc.de@domain.tld, что, очевидно, плохо. Фильтрация по медицинским терминам может отсеять легитимную переписку с врачем или родственником.
Поэтому, я скормил спам‑сообщения жены Claude.ai и он выдал свои рекоммендации и сразу же их и реализовал. Идея в том, что медицинский спам рассылается ограниченным колличеством mass mailing сервисов, которые я с радостью заблокировал. Заодно заблокировал (как и в моем случае) спам, где отправитель — несуществующий домен. По какой‑то причине, на аккаунте жены эти сообщения иногда прорывались сквозь фильтры. Теперь они идут в корзину каждые 15 минут. Опять‑таки, по идее их надо уничтожать на месте, но мне не хотелось делать такие резкие движения не на моем аккаунте.
Код очистки Inbox‑a жены:
Интересный факт: вот эти «товарищи» ответственны за 75% спама в ящике жены (почти весь «легитимный» медицинский спам идет от них:
var MEDICAL_SPAM_DOMAINS = [ "info.haymarketmedicalnetwork.com", "haymarketmedicalnetwork.com", "haymarketmedia.com", "en25.com", // Oracle Eloqua marketing platform "mdedge.com", "healio.com", "medscape.com", "givingsight.org", // <-- add: Optometry Giving Sight "bloomerang-mail.com", // <-- add: their sending platform "nejm.org", // add/remove based on what she receives "aao.org" ];
Также отправил в Корзину все сообщения не из белого списка (жены, не РКН).
Вот этот белый список:
var ALLOWED_TLDS = [ ".com", ".net", ".org", ".gov", ".il", ".ua", ".ru", ".ca", ".cz", ".de", ];
А вот это —
var KNOWN_GOOD_DOMAINS = { "gmail.com": true, "googlemail.com": true, "outlook.com": true, "hotmail.com": true, "live.com": true, "outlook.co.il": true, "hotmail.co.il": true, "mail.com": true, "fastmail.com": true, "yahoo.com": true, "icloud.com": true, "aol.com": true, "walla.com": true, "walla.co.il": true, "ukr.net": true, "i.ua": true, "mail.ru": true, "yandex.com": true, "yandex.ru": true, "ceznum.cz": true, };
список почтовых сервисов, на которые не надо выполнять DNS запросы на A и MX записи. Используется, чтобы минимизировать колличество сетевого трафика из скрипта. Иначе можно и бан получить от Гугла в какой‑то ситуации. Это не значит, что вся почта с этих доменов — легитимная. Это всего‑лишь означает, что сами домены — легитимные.
Фильтрация спама на почтовом сервисе Майкрософта (Hotmail, outlook.com, live.com)
Третий пример: фильтрация спама на Hotmail‑овском Спам фолдерe с целью его очистки для быстрого поиска false positives. Для этого используется Graph API, так как стандартная аутентификация по логину‑паролю для IMAP‑a больше не работает на сервисах Майкрософта.
Основная проблема Hotmail‑a в том, что если Майкрософт решил, что сообщение — спам, то на него не действуют пользовательские фильтры, что не позволяет сразу удалять сообщения в Корзину в момент получения. Плюс относительно большое колличество false positives, которые надо перемещать в Inbox как минимум, вручную, а как максимум — программным путем.
Пример кода (я убрал из реального кода специфические моменты, которые неинтересны 99% хабраюзеров)
https://github.com/comrade‑ogilvy/email‑antispam‑scripts/blob/main/hotmail‑outlook/purge_hotmail_spam_folder.py
Я получал десятки спам‑сообщений в день. После беседы с Claude выяснилось, что 90% спама идут с 6 доменов, еще 3–4% — от какого‑то mailer‑a который использует 4 дефиса «‑--» в поле From.
Что‑то вроде abcde@‑--‑mail.xyz.com
Такие email‑ы сразу стираются, остальные (максимум 1–2 в день) заканчивают свою жизнь в Корзине. Фактически, на этом этапе в Спам фолдере остаются исключительно false positives.
В реальном коде (не в этом примере) гарантированно false positives сразу перемещаются в Inbox.
Отдельный вопрос: почему Майкрософт ничего не делает с этой spam farm? Она «работает» уже несколько лет, что стоит отфильтровать эти несчасные 6 или 7 доменов на своих серверах еще до того, как сообщение оказывается в почтовом ящике?
Ради интереса, я пересылю статистику по очищенным сообщениям на сервис временной почты yopmail.com. У него есть RSS интерфейс, что очень удобно для беглого просмотра статистики
Это — веб интерфейс

A это — RSS интерфейс моего RSS reader‑a

Вроде всё. Если есть вопросы — пишите в комментарии или ЛС.
Отдельное спасибо Claude.de за анализ нескольких тысяч спам сообщений и рекоммендации и правки кода для программной фильтрации спама.
Автор выражает благодарность СhatGPT за генерацию изображения для обложки, Кириллу и Мефодию за буквы русского алфавита, а этрускам — за буквы латинского алфавита.
Весь код в репозитории по ссылкам опубликован по лицензии MIT. Делайте, что хотите, но я не несу ответсвенность за бесследно уничтоженные email‑ы при использовании этого кода и за любые другие последствия его использования включая, но не ограничиваясь, сбои в работе компьютера, поломки жесткого диска, потерю работы, неназначение интервью и так далее
