ずいぶんと前に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