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

Комментарии (0)