ActionMailbox в рельсах изначально был заточен на использование платных 3rd-party сервисов. Подключить его к обычному имейлу можно было либо поднимая/перенастраивая почтовый сервер, либо через костыли. Костыли так или иначе заключались в поднятии дополнительного сервиса. Статья на Medium предлагает поднять fetachmail, заброшенный гем предлагает запускать почтовый клиент на Rust.
Ниже предлагаю решение, работающее на ActiveJob. Промежуток поллинга сообщений конфигурируется с помощью встроенных recurring jobs. Креденшиалы для почты хранятся во встроенных Rails Credentials. Решение не требудет дополнительных сервисов, если вы уже используете SolidQueue или любой другой бэк, поддерживающий рекуррентные задачи. Из коробки деплоится Kamal.
Получаем и сохраняем все непрочитанные сообщения в ящике:
# app/jobs/action_mailbox/fetch_unread_emails_job.rb
# frozen_string_literal: true
require 'net/imap'
class ActionMailbox::FetchUnreadEmailsJob < ApplicationJob
queue_as :default
def perform
each_imap_message_id do |msg_id, imap|
raw_source = imap.fetch(msg_id, 'RFC822')[0].attr['RFC822']
ActionMailbox::InboundEmail.create_and_extract_message_id!(raw_source)
imap.store(msg_id, '+FLAGS', [:Seen])
end
end
private
def each_imap_message_id
imap = Net::IMAP.new(Rails.application.credentials.dig(:mail_ingress, :host), Rails.application.credentials.dig(:mail_ingress, :port).to_i, true)
imap.login(Rails.application.credentials.dig(:mail_ingress, :username), Rails.application.credentials.dig(:mail_ingress, :password))
imap.select('INBOX')
imap.search(['UNSEEN']).each do |msg_id|
yield msg_id, imap
end
imap.logout
imap.disconnect
end
end
Чистим все прочитанные письма старше месяца:
# app/jobs/action_mailbox/cleanup_old_emails_job.rb
# frozen_string_literal: true
require 'net/imap'
class ActionMailbox::CleanupOldEmailsJob < ApplicationJob
queue_as :default
PERIOD = 1.month
def perform
imap = Net::IMAP.new(Rails.application.credentials.dig(:mail_ingress, :host), Rails.application.credentials.dig(:mail_ingress, :port).to_i, true)
imap.login(Rails.application.credentials.dig(:mail_ingress, :username), Rails.application.credentials.dig(:mail_ingress, :password))
imap.select('INBOX')
imap.search(['SEEN', 'BEFORE', PERIOD.ago.strftime('%d-%b-%Y')]).each do |msg_id|
imap.store(msg_id, '+FLAGS', [:Deleted])
end
imap.expunge
imap.logout
imap.disconnect
end
end
Конфиг для ActiveJob:
# config/recurring.yml
production:
fetch_unread_emails:
class: ActionMailbox::FetchUnreadEmailsJob
queue: background
schedule: every 3 minutes
cleanup_old_emails:
class: ActionMailbox::CleanupOldEmailsJob
queue: background
schedule: daily at 2:30 am
Так выглядят креденшиалы для `bin/rails credentials:edit`:
mail_ingress:
host: 'mail.example.com'
port: 995
username: 'superbot@example.com
password: 'h4ckerm4n'
Для англоязычной аудитории можно скидывать ссылку на gist:
https://gist.github.com/sandrew/c94bccb987d7862f09166f9b114b0995