Представьте, что у Вас есть веб-приложение и одной из его функций является массовая рассылка новостей Вашим пользователям.
По некоторым причинам часть email адресов пользователей нерабочие или неверно заполненные. Хорошо бы таких пользователей автоматически отписать от рассылки?
В отправляемые сообщения рассылки можно добавить специальный заголовок
Return-Path: <noreply@my-application.com>
На этот адрес будут приходить уведомления от почтового сервера о провалившихся доставках.
В итоге вся задача сводится к чтению этих уведомлений, извлечению email адреса, и пометки пользователя с этим адресом как недоступного для рассылки.
А теперь реальный пример.
В одном из наших приложений почтовый сервер добавляет прямо в тело таких уведомлений несколько полезных строк:
Final-Recipient: rfc822; nonexistent@example.com
Original-Recipient: rfc822;nonexistent@example.com
Action: failed
Status: 5.1.1
Для ruby есть IMAP-клиент в стандартной библиотеке. Подключаемся к нашему «noreply@my-application.com» ящику и читаем уведомления:
imap = Net::IMAP.new('mail.my-application.com', port: 993, ssl: true)
imap.login("noreply@my-application.com", "mysecretpassword")
imap.select("INBOX") # здесь может быть другая папка в ящике, куда отфильтровываются уведомления
imap.search('ALL').each do |message_id|
# чтение тела сообщения
message = imap.fetch(message_id, 'BODY[TEXT]')[0].attr['BODY[TEXT]']
match = message.match(/^Original-Recipient: rfc822;(.*)$/)
email = match[1].strip if match
# rails-зависимый код приложения для отписки пользователя от рассылки
if email
user = User.where(email: email).first
user.unsubscribe if user
end
# перемещаем уведомление в другую папку почтового ящика
imap.copy(message_id, 'Processed')
imap.store(message_id, '+FLAGS', [:Deleted])
end
imap.expunge
imap.logout
imap.disconnect
Для чистоты можно еще проверять код статуса. Код 5.X.X свидетельствует о неустранимой ошибке доставки, например, email адрес не существует, код 4.X.X — о временной ошибке, например, сервер-получатель не отвечает.
Данный код запускается периодически в фоне с помощью Resque, Delayed::Job, cron, или любого другого инструмента используемого в приложении. Задача решена: рассылка отправляется только на рабочие существующие email адреса.
Задача была подготовлена и решена совместно с olemskoi
P.S.: В Вашем приложении подобные уведомления возможно не будут настолько многословны, но нерабочий email все равно можно будет попытаться как-либо извлечь. Например, если использовать gmail для отправки писем, то уведомления о провалившейся доставке от него будут содержать специфический заголовок:
X-Failed-Recipients: nonexistent@example.com
который можно использовать.
Автор: kanfet