Опыт в бэкенде, в частности в создании телеграм ботов на PHP MySQL у меня уже был, но получать электронную почту «самому» — это казалось далёким и непонятным. Открыв несколько вкладок с различными статьями по теме, я понял, что ничего не понял. Везде предлагалось использовать тонну различных инструментов, что на мой взгляд больше подходило для полноценного почтового сервиса, чем для задачи получения входящих email сообщений на VPS.
Получение входящей почты
Для первого шага мне очень помогла статья из песочницы: habr.com/ru/post/260429. Я обратил внимание на её отрицательный рейтинг, однако в ней описано ровно то, что меня интересовало. Я хотел как можно быстрее получить результат, который можно «пощупать», и с мыслями «в будущем я сделаю как надо» пошёл настраивать sendmail.
Затем я настроил домен. DNS записи:
example.com IN MX 5 mail.example.com
mail.example.com IN A XXX.XX.XXX.XXX
(ip адрес VPS)На сервере в файл
/etc/mail/virtusertable
добавил строку @example.com vasya
, тем самым определив, что вся почта, предназначенная для любых адресов на ****@example.com адресована пользователю Васе.Чтобы обрабатывать входящую почту php-скриптом, в файл
/etc/aliases
добавил строку vasya: "|php -q /home/vasya/mail.php"
.Проведя несколько тестов и убедившись, что входящая почта передаётся в php скрипт, я мог заняться её обработкой.
Получение сырой входящей почты, направленной в php выше описанным способом, реализуется в коде крайне просто:
$msg = file_get_contents("php://stdin");
Совсем другое дело это разбор почтового формата и представление данных в понятном и доступном виде. Гугл предложил мне несколько вариантов, как можно разобрать почтовый формат средствами PHP. Все найденные мной библиотеки тянули за собой установку дополнительных компонентов, однако одна из них мне показалась менее громоздкой: github.com/zbateson/mail-mime-parser. Единственное, что мне нужно было установить дополнительно, это популярный пакетный менеджер для PHP – Composer. Конечно, на обычном хостинге я с ним и не сталкивался, но его установка и дальнейшее подключение библиотеки для разбора почты не оказалось сколько-нибудь сложным.
Начало php скрипта для обработки входящей почты с использованием библиотеки zbateson/mail-mime-parser выглядит так:
<?php
require("vendor/autoload.php");
use ZBateson\MailMimeParser\MailMimeParser;
use ZBateson\MailMimeParser\Message;
$msg = file_get_contents("php://stdin");
$parser = new MailMimeParser();
$message = Message::from($msg);
Так как временная почта на мой взгляд не предполагает несколько получателей, достаточно взять только первого из возможных:
$to = $message->getHeader('To');
$email = $to->getAddresses()[0]->getEmail();
В переменной $email у нас оказывается адрес получателя вида vasyaorpetya@example.com.
Для получения контента входящих писем в библиотеке есть соответствующие методы:
$from = $message->getHeader('From')->getEmail();
$subject = $message->getHeaderValue('Subject');
$msg_text = $message->getTextContent();
$msg_html = $message->getHtmlContent();
Телеграм бот
Что должен уметь телеграм бот временной почты в первую очередь?
- Выдавать новый временный email адрес по запросу
- Присылать в чат входящие письма для этого email, пока почтовый адрес действителен
- Продлевать действие email-адреса
Вполне подходящий в данном и множестве других случаев способ получения обновлений от Телеграма – это использование Webhook. Нужен только адрес скрипта с https. Использование Certbot для настройки ssl сертификата домена подробно описано в инструкциях DO.
Для взаимодействия с Telegram Bot API я использую собственные наработки. Кто-то предпочитает использовать популярные библиотеки. Отправка сообщений с кнопками в телеграм уже давно стала привычным делом, о чём написано не мало статей.
Генерация временных email адресов по сути является выдачей следующего адреса по порядку. Я создал таблицу для email адресов в базе данных, где id типа int с автоинкрементом однозначно определяет получателя. Превращение числового id в строковый адрес осуществляется как перевод числа в другую систему счисления, где в качестве «цифр» доступен весь латинский алфавит. 26 букв по сравнению с цифрами дают неплохое сокращение длины идентификатора. Наверное, я мог бы использовать также большие буквы, цифры и некоторые символы без проблем для ещё большего сокращения длины выдаваемых адресов, но я оставил лишь маленькие латинские буквы.
Функции перевода числового id в строковый и обратно:
// $alphabet = explode(",", "a,b,c,d,e,f,g,h,i,j,k,l,m,n,o,p,q,r,s,t,u,v,w,x,y,z");
// Улучшение от @grayfolk:
$alphabet = range('a', 'z');
function num2str($n, $a) { // $a - алфавит
$b = count($a);
$r = 0;
$x = "";
while ($n) {
$r = $n%$b;
$n = ($n-$r)/$b;
$x .= $a[$r];
}
return strrev($x);
}
function str2num($s, $a) {
$n = 0;
$b = count($a);
$s = strrev($s);
for ($i = 0; $i < strlen($s); $i++) {
$n += array_search($s[$i], $a) * pow($b, $i);
}
return $n;
}
Одно из ключевых преимуществ использования сервиса временной почты — отсутствие спама. Но если адреса идут по порядку, можно составить список ближайших адресов, которые будут выданы и успешно делать рассылку. Для решения этой проблемы я добавил некоторую случайную строку к идентификатору получателя. Для различия id и случайной составляющей в адресе я решил всегда начинать случайную компоненту с цифры.
Случайную строку выдаваемого email адреса записываем в БД вместе с id получателя, id пользователя в телеграме и временем выдачи почтового ящика.
Казалось бы, можно даже не хранить входящую почту — отправили в телеграм и всё. Но как быть с html письмами? Их невозможно отобразить в сообщении в чате. Остаётся записывать входящие html сообщения в БД и показывать их на сайте, а пользователю отправлять ссылку, включающую в себя id сообщения и очередной сгенерированный пароль. Для очистки БД кроном по расписанию запускается php скрипт, удаляющий входящие html сообщения, которые были получены более часа назад.
Позже в телеграм бота я добавил кнопки, продлевающие срок действия почтового ящика на 10 или 60 минут, а также кнопку, позволяющую узнать сколько, всё-таки, осталось ему жить до того, как будет прекращён приём входящих сообщений.
Так как в телеграме мы имеем дело с зарегистрированными пользователями, можно предоставить возможность активировать свои старые почтовые ящики например с целью восстановить забытый пароль на веб-сайте или для любых других операций, требующих подтверждение с помощью email. Выданный почтовый ящик «принимает» входящие только когда это необходимо пользователю, всё остальное время возможный спам игнорируется.
Хотелки на будущее:
- Создать веб-версию [сделано]
- Настроить быструю смену почтового домена в пару кликов/команд (как?)
Ссылки
Телеграм бот: @tmpmailbot
Статья, где описана настройка sendmail
PHP библиотека для разбора электронной почты
Комментарии (17)
cat_crash
29.03.2019 22:22Идея интересна если не сказать замечательна. Но ИМХО разбирать весь почтовый поток средствами PHP — так себе затея.
1. Что вы делаете с «залипшими» тредами? Есть ли таймаут?
2. Как лимитируется память? Что будет если я пришлю 100 меговое письмо? Или одновременно тысячу 100 меговых писем?
egc12hb Автор
29.03.2019 22:56Хорошие вопросы, на которые мне только предстоит найти ответы.
Надеюсь решить их раньше, чем кто-то решит послать тысячу 100 меговых писем.
Спасибо
UPD
Совсем забыл, но часть ответов похоже есть в статье о настройке sendmail — там и лимит по соединениям, и максимальные размеры сообщений. Я ведь их ставил, но забыл)
Значит, надеюсь, их хватит на первое время
click0
29.03.2019 23:15Настроить быструю смену почтового домена в пару кликов/команд (как?)
Почтовые домены в sendmail'e лежат тут /etc/mail/local-host-names.
Через файлы virtusertable и aliases можно разруливать большим списком почтовых ящиков в разных почтовых доменах.
grayfolk
30.03.2019 03:49$alphabet = explode(",", "a,b,c,d,e,f,g,h,i,j,k,l,m,n,o,p,q,r,s,t,u,v,w,x,y,z");
$alphabet = range('a', 'z');
boenskov
30.03.2019 15:02Остаётся записывать входящие html сообщения в БД и показывать их на сайте
Если это делать без очистки html, то это очень плохая идея, т.к. письмо может содержать скрипт который может наделать пакостей.
А так, достаточно интересная идея. Можно под каждый сайт регистрировать отдельный ящик и мониторить кто из них начинает сливает базы спамерам (или у кого сливают базы)egc12hb Автор
30.03.2019 15:10Если это делать без очистки html, то это очень плохая идея, т.к. письмо может содержать скрипт который может наделать пакостей.
Думал про это, но пока не нашёл решения. Сначала хотел вырезать script тэги, но ведь js может оказаться например внутри атрибута html. Возможно стоит отображать их в iframe другого домена, но пока это слишком.
vladkorotnev
01.04.2019 09:08Можно под каждый сайт регистрировать отдельный ящик и мониторить кто из них начинает сливает базы спамерам
На Gmail это делается путём регистрации на ящик видаrealusername+site-identifier-name@gmail.com
, и последующей фильтрацией по полю адреса получателя. Большинство сайтов такие адреса пропускают, большинство непропускающих лечатся правкой регулярки или скрипта через инспектор, остальные лучше заранее не использовать, ибо раз даже валидацию почты по стандарту не осилили, страшно представлять, что же там внутри.limassolsk
01.04.2019 15:54Большое спасибо.
Не знал, что можно получать бесконечное количество синонимов к своему гугловому ящику так:
myname+2@gmail.com
Раньше использовал только так (добавление точки в любое место):
myn.ame@gmail.com
geko365
30.03.2019 15:37можно предоставить возможность активировать свои старые почтовые ящики например с целью восстановить забытый пароль на веб-сайте или для любых других операций, требующих подтверждение с помощью email.
Можно ли создавать дубликаты реально существующих адресов с целью угона важной инфы? Или это не реально?egc12hb Автор
30.03.2019 15:43Вы имеете ввиду между пользователями бота?
Можно активировать только свои, если в БД есть запись адрес-юзер.
uriy123
31.03.2019 16:25Если генерировать почту, то логичнее использовать дельтачат. Дельтачатом и чтать почту.
uriy123
31.03.2019 19:16для дельтачата можно установить свой сервер (SMTP/IMAP).
У вас как я понимаю он уже установлен
uoziod
А где же сам бот?
egc12hb Автор
Добавил