RubyでIMAPのメール振り分けをする

ずいぶんと前にPOPからIMAPへ切り替えてから、メールの振り分けはThunderbird側でやってたんだけど、自宅のiMacと外出用のMBAとの両方に振り分けルールを入れておく必要があった。

できればサーバ側で一元管理したいけれど、とりあえずは一部のメールだけでもとRubyでの振り分けを試してみることにした。

Rubyに標準添付されているNet/IMAPを使用する。

たとえば、Facebook(@facebookmail.com)からのメールを”Facebook”フォルダへ移動したい場合はこんな感じだ。

#!/usr/bin/env ruby

require 'net/imap'

host     = 'imap.example.com'
user     = 'user@example.com'
password = 'mypassword'

imap = Net::IMAP.new(host)
imap.authenticate('CRAM-MD5', user, password)
imap.select('INBOX')

rule = ['FROM', '@facebookmail.com']
mailbox  = "Facebook"

imap.search(rule).each do |message_id|
  imap.copy(message_id, "INBOX.#{mailbox}")
  imap.store(message_id, '+FLAGS', [:Deleted])
end
imap.expunge
imap.disconnect

認証方法

ここでは認証方法にCRAM-MD5を使用けど、Net/IMAPではLOGIN、PLAIN、DIGEST-MD5にも対応している。
自分の環境で実際に使用できる認証方法を選ぶ。

instance method Net::IMAP#authenticate

受信トレイはINBOX

メールを検索する前にselectメソッドで検索対象のメールボックスを指定する。

Thunderbirdでの受信トレイは、DovecotのIMAPサーバ側ではINBOXとして処理されている。受信トレイ以下のフォルダは実際にはフォルダの階層になっている訳ではなく、’.'(ドット)区切りで階層を表現をしたフォルダ名を持つ。

Dovecotの受信フォルダを見てみるとよく分かる。

ls -d .INBOX.*
.INBOX.&MOEw6zDeMKw-  .INBOX.Facebook  .INBOX.iTunes

“&MOEw6zDeMKw-“はThunderbird上で作成した日本語フォルダで、”メルマガ”メールボックスのこと。modified UTF-7というIMAP用のUTF-7でエンコーディングされている。

文字列のエンコードデコードはNet::IMAP.encode_utf7, Net::IMAP.decode_utf7というメソッドを使用する。

pry -r 'net/imap'
[1] pry(main)> Net::IMAP.encode_utf7('メルマガ')
=> "&MOEw6zDeMKw-"
[2] pry(main)> Net::IMAP.decode_utf7('&MOEw6zDeMKw-')
=> "メルマガ"

検索ルール

振り分けるメールを検索するにはNet::IMAP#searchを使用する。

ここではFROMだけを使用したけど、SUBJECT(件名)、UNSEEN(未読)などが使用できる。
詳細については RFC2060参照のこと。

imap.search(["FROM", "foo@example.com"])
imap.search(["SUBJECT", "hello"])
imap.search(["UNSEEN"])

メールのメールボックス移動

IMAPではメールの移動の処理がなくコピーした後、削除することによってメールボックスを移動させる。

ドキュメントの例も同様な処理を行っている。

Move all messages from April 2003 from “Mail/sent-mail” to “Mail/sent-apr03”

searchで取得したmessage_idを使用してメールのコピーと:Deleted(削除)フラグをセットする。
削除フラグをセットしたメールの削除はNet::IMAP#expungeで行う。

  imap.copy(message_id, "INBOX.#{mailbox}")
  imap.store(message_id, '+FLAGS', [:Deleted])

実際には

実際には、複数のアカウントで使用するので、次のようなコードを今のところ使用している。
特に問題なければもう少し拡張したい。

#!/usr/bin/env ruby
# -*- coding: utf-8 -*-

require 'net/imap'

class Filter
  def initialize(host, user, password)
    @imap = Net::IMAP.new(host)
    @user = user
    @password = password
  end

  def exec(&block)
    @imap.authenticate('CRAM-MD5', @user, @password)
    @imap.select('INBOX')
    block.call(self)
    @imap.expunge
    @imap.disconnect
  end

  def move(rule, mailbox)
    @imap.search(rule).each do |message_id|
      @imap.copy(message_id, "INBOX.#{mailbox}")
      @imap.store(message_id, '+FLAGS', [:Deleted])
    end
  end
end

work = Filter.new('mail.example.com', 'work@example.com', 'mypassword')
work.exec do |filter|
  filter.move(['FROM', 'fail2ban@'],         'alert')
  filter.move(['FROM', 'zabbix@'],           'alert')
  filter.move(['FROM', 'alert@example.com'], 'alert')
end

home = Filter.new('mail.example.com', 'home@example.com', 'mypassword')
home.exec do |filter|
  filter.move(['FROM', '@facebookmail.com'], 'Facebook')
  filter.move(['FROM', '@mag.example.com'],   '&MOEw6zDeMKw-') # メルマガ
end

CRONによる定期実行

とりあえず、cronで5分ごとに実行する形で運用をはじめてみている。

chmod 755 /var/www/user/cron/imap_filter.rb
crontab -e
---------------------------------------------
*/5 * * * * /var/www/user/cron/imap_filter.rb
---------------------------------------------

関連記事

    None Found

コメントを残す

メールアドレスが公開されることはありません。 が付いている欄は必須項目です