RubyでIMAPのAPIを使ってメールをアーカイブする方法

Tips
2019年08月20日 10:00

概要

最近、Rubyを使ってメールサーバの操作をすることがあり、そこでメールをアーカイブする方法について色々調べました。
しかし、なかなかうまい方法が見つからなかったため、後学のためにどのようにしてメールのアーカイブしたのかを残しておきます。

環境

  • Ruby 2.6

結論

まず、Rubyにはimapというライブラリがあり、これを使えばメールサーバにIMAPでアクセスして操作できます。そのため、メールの操作にはこれを使います。
また、メールのアーカイブは、INBOXという場所から、アーカイブ用の場所にメールを移すことなので、あらかじめ、アーカイブ用の場所を知っておく必要があります。

出来上がったものは次になります。
このソースコードでは、example@example.comというメールアドレスからのメールをアーカイブするというものになっています。

require 'net/imap'
require 'mail'

imap_host = ENV['IMAP_HOST']
imap_usessl = true
imap_port = 993
imap = Net::IMAP.new(imap_host, imap_port, imap_usessl)

imap_user = ENV['IMAP_USER']
imap_passwd = ENV['IMAP_PASS']
imap.login(imap_user, imap_passwd)

search_str = 'example@example.com'

imap.select('INBOX') # 検索場所として受信ボックスを指定
ids = imap.search(['From', search_str]) # search_strに代入されているメールアドレスからの送信メールを取得
ids.each do |id|
  mail = imap.fetch(id, %w[RFC822 UID]) # メールの内容の取得
  m = Mail.new(mail.attr['RFC822'])

  # 検索した内容に合ったものが取れているかチェック
  next unless m.from.include? search_str

  # メールのアーカイブ
  imap.uid_copy(mail.attr['UID'], 'INBOX.Archive')
  imap.uid_store(mail.attr['UID'], '+FLAGS', [:Deleted])
  imap.expunge
end

imap.logout
imap.disconnect

メールサーバのアドレスやユーザ名やパスワードは環境変数から読み込むようにしています。

重要な部分としては、私の環境だけかもしれませんが、IMAPの検索でなぜか検索に一致しないようなメールが検索に引っかかりました。
そのため、mailというgemを別にインストールして、それを使ってメールのFromが本当に正しいのかをチェックしました。

また、メールのアーカイブはアーカイブ用の場所にメールを移動することですが、この移動はコピーして元のメールを消すということで実現しています。メールの指定は、メールにそれぞれ振られているUIDを使用しました。調べてみると、searchメソッドの戻り値でもできそうですが、私の環境ではうまくできなかったため、このような方法を取っています。メールの削除はDeletedというフラグを立てておき、それをexpungeというメソッドを使って実際の削除を行っています。

さいごに

メールの検索まではすんなりとできたのですが、実際に取得したメールが必ずしも検索しているものと一致するわけではなかったり、アーカイブの際にうまくいかなかったり、かなりハマるポイントがありました。