rubyでGmailのfrom・subject・本文を読み込みたい(Net::IMAP編)

twitterへ流し込みたいので。
参考:

初期設定

require 'net/imap'
require 'kconv'
imap = Net::IMAP.new('imap.gmail.com', 993, true)
imap.login("foo@gmail.com, "********")
imap.examine('INBOX')

Net::IMAP#select('INBOX')でも良いのだけど、読み取り専用のNet::IMAP#examine('INBOX')を使いました。

最新メールのUID確認

pry(main)> last_uid = imap.status('INBOX', ["MESSAGES"])["MESSAGES"]
=> 160229

最新メールのヘッダ読み取り

ただし上記のexamine以降に受信したメールは読み取れません。注意してください。

pry(main)> envelope = imap.fetch(last_uid, "ENVELOPE")[0].attr["ENVELOPE"]
=> #],
 sender=
  [#],
 reply_to=
  [#],
 to=
  [#],
 cc=nil,
 bcc=nil,
 in_reply_to=nil,
 message_id="<201306182133.r5ILXrjg023268@bizml125.nikkeibp.co.jp>">

最新メールのヘッダの中身を取り出す

pry(main)> envelope.date
=> "Wed, 19 Jun 2013 06:33:53 +0900"
pry(main)> envelope.subject.toutf8
=> "[NEニュース NO.3773]シャープのセンサ、ジェスチャーと照度、近接の三つを検知"
pry(main)> envelope.from
envelope.from   envelope.from=  
pry(main)> envelope.from[0].name.toutf8
=> "日経エレクトロニクス"
pry(main)> envelope.from[0].mailbox + "@" + envelope.from[0].host
=> "ne@nikkeibp.co.jp"
pry(main)> envelope.to[0].mailbox + "@" + envelope.to[0].host
=> "ne@nikkeibp.co.jp"
pry(main)> envelope.message_id
=> "<201306182133.r5ILXrjg023268@bizml125.nikkeibp.co.jp>"

文字数確認。

pry(main)> envelope.subject.toutf8.size
=> 43

foo@baa形式で取り出すメソッドmail_address_formatted

いちいち「envelope.from[0].mailbox + "@" + envelope.from[0].host」などと長いのを書くのが面倒だったので。
なお、Object#__send__メソッド
Rubyist Magazine 出張版 正しいRubyコードの書き方講座―RubyistのRubyistによる、Rubyistとそうでない人のための
のp.222に載ってるのを昨夜読んで、使ってみたくなったので書いてみましたw

class Net::IMAP::Envelope
  def mail_address_formatted(value)
    return nil unless ["from", "sender", "reply_to", "to"].include?(value)
    self.__send__(value)[0].mailbox + "@" + self.__send__(value)[0].host
  end
end

mail_address_formattedメソッドを使ってみる。

pry(main)> envelope.mail_address_formatted("from")
=> "ne@nikkeibp.co.jp"
pry(main)> envelope.mail_address_formatted("reply_to")
=> "ne@nikkeibp.co.jp"
pry(main)> envelope.mail_address_formatted("sender")
=> "ne@nikkeibp.co.jp"
pry(main)> envelope.mail_address_formatted("to")
=> "ne@nikkeibp.co.jp"

文字数確認。

pry(main)> envelope.mail_address_formatted("from").size
=> 17

本文の冒頭1500バイトを"BODY[1]<0.1500>"で取得し、utf8に変換

pry(main)> imap.fetch(last_uid, "BODY[1]<0.1500>")[0].attr["BODY[1]<0>"].toutf8
=> "****************************************************************************\r\n【NEニュース 2013年6月19日号】 No.3773\r\n             発信元◇日経BP社 日経エレクトロニクス編集\r\n             http://techon.nikkeibp.co.jp/NE/?ref=ML\r\n****************************************************************************\r\n\r\n\r\n----------------------------------------------------------------------PR----\r\n 【動画で解説】自動車メーカー15社が採用するシーメンスの開発プラットフォーム\r\n\r\n             http://h.nikkeibp.co.jp/h.jsp?no=060860 \r\n\r\nアンケートに答えて抽選で10名にAmazonギフト券5000円が当たるキャンペーン実施中\r\n----------------------------------------------------------------------------\r\n\r\n\r\nNEニュース/ ‥‥‥‥‥‥‥‥‥‥‥‥‥‥‥‥‥‥‥‥‥‥‥‥‥‥‥‥‥‥‥\r\n◆目次◆\r\n‥‥‥‥‥‥‥‥‥‥‥‥‥‥‥‥‥‥‥‥‥‥‥‥‥‥‥‥‥‥‥‥‥‥‥‥‥‥\r\n\r\n◆ニュース・ヘッドライン\r\nシャープ、ジェスチャーや照度/色合い、近接の検知が可能なセンサを発売へ\r\n「使い手がロボットを試し始めた」、“サービス・ロボット元年”の動向を聞く\r\nホンダ、HEV用電池のレアアースをモータに再利用\r\n\r\n◆NEブログ\r\n問題解決としての医療\r\n\r\n◆今日のコラム・解説\r\n【さらば端末ビジネス】第2回 消えるパソコン用ソフト\r\n\r\n◆日経エレクトロニクス/日経コミュニケーション主催セミナーのご案内\r\n「スマートWebコネク"

本文冒頭の装飾をgsubで落とす

pry(main)> imap.fetch(last_uid, "BODY[1]<0.1500>")[0].attr["BODY[1]<0>"].toutf8.gsub(/[ ‥…∴∵∞~−~――−─│└┴┼┘├┬┤┠┰┨┌┸╂┐━┃┗┻╋┛┣┳┫┝┯┥┏┷┿┓__/\+■□◆◇▲▼△▽★☆\*\=\+-]/, "").gsub(/ +/," ").gsub(/(\r\n)+/,"\n")
=> "\n【NEニュース2013年6月19日号】No.3773\n発信元日経BP日経エレクトロニクス編集\nhttp://techon.nikkeibp.co.jp/NE/?refML\nPR\n 【動画で解説】自動車メーカー15社が採用するシーメンスの開発プラットフォーム\n http://h.nikkeibp.co.jp/h.jsp?no060860 \nアンケートに答えて抽選で10名にAmazonギフト券5000円が当たるキャンペーン実施中\nNEニュース \n目次\nニュース・ヘッドライン\nシャープ、ジェスチャーや照度/色合い、近接の検知が可能なセンサを発売へ\n「使い手がロボットを試し始めた」、“サービス・ロボット元年”の動向を聞く\nホンダ、HEV用電池のレアアースをモータに再利用\nNEブログ\n問題解決としての医療\n今日のコラム・解説\n【さらば端末ビジネス】第2回消えるパソコン用ソフト\n日経エレクトロニクス/日経コミュニケーション主催セミナーのご案内\n「スマートWebコネク"

本文テキストを"RFC822.TEXT"で取得し、utf8にし、装飾を落とし、冒頭500文字を取得

pry(main)> imap.fetch(last_uid, "RFC822.TEXT")[0].attr["RFC822.TEXT"].toutf8.gsub(/[ ‥…∴∵∞~−~――−─│└┴┼┘├┬┤┠┰┨┌┸╂┐━┃┗┻╋┛┣┳┫┝┯┥┏┷┿┓__/\+■□◆◇▲▼△▽★☆\*\=\+-]/, "").gsub(/ +/," ").gsub(/(\r\n)+/,"\n")[0..499]
=> "\n【NEニュース2013年6月19日号】No.3773\n発信元日経BP日経エレクトロニクス編集\nhttp://techon.nikkeibp.co.jp/NE/?refML\nPR\n 【動画で解説】自動車メーカー15社が採用するシーメンスの開発プラットフォーム\n http://h.nikkeibp.co.jp/h.jsp?no060860 \nアンケートに答えて抽選で10名にAmazonギフト券5000円が当たるキャンペーン実施中\nNEニュース \n目次\nニュース・ヘッドライン\nシャープ、ジェスチャーや照度/色合い、近接の検知が可能なセンサを発売へ\n「使い手がロボットを試し始めた」、“サービス・ロボット元年”の動向を聞く\nホンダ、HEV用電池のレアアースをモータに再利用\nNEブログ\n問題解決としての医療\n今日のコラム・解説\n【さらば端末ビジネス】第2回消えるパソコン用ソフト\n日経エレクトロニクス/日経コミュニケーション主催セミナーのご案内\n「スマートWebコネクションズ2013」\nhttp://techon.nikkeibp.co.jp/seminar/130627.html\n日経エレクト"

Twitterでメールをお知らせするフォーマットにしてみる。

"subject《from》body"のフォーマット*1で文字数が140文字ぴったり。
URLの文字数はそのまま数えてます。変換が面倒だし、途中でぶった切ってるかもしれないから。

pry(main)> "#{envelope.subject.toutf8}《#{envelope.mail_address_formatted("from")}》#{imap.fetch(last_uid, "RFC822.TEXT")[0].attr["RFC822.TEXT"].toutf8.gsub(/[ ‥…∴∵∞―−~─│└┴┼┘├┬┤┠┰┨┌┸╂┐━┃┗┻╋┛┣┳┫┝┯┥┏┷┿┓_/\◎+■□◆◇▲▼△▽★☆\*\-\=\+]/, "").gsub(/ +/," ").gsub(/(\r\n)+/,"\n")[0..(140 - envelope.subject.toutf8.size - envelope.mail_address_formatted("from").size - 3)]}"
=> "[NEニュース NO.3773]シャープのセンサ、ジェスチャーと照度、近接の三つを検知《ne@nikkeibp.co.jp》\n【NEニュース2013年6月19日号】No.3773\n発信元日経BP日経エレクトロニクス編集\nhttp://techon.nikkeibp.co.jp/"

twitterへ流すために

ツイートするフォーマットに変換するmail_to_tweetメソッド

class Net::IMAP
  def escape_chars
    Regexp.new("[ ‥…∴∵∞~−――−~─│└┴┼┘├┬┤┠┰┨┌┸╂┐━┃┗┻╋┛┣┳┫┝┯┥┏┷┿┓__/\◎+■□◆◇▲▼△▽★☆\*\-\=\+]")
  end
  
  def mail_to_tweet(uid)
    subject = self.fetch(uid, "ENVELOPE")[0].attr["ENVELOPE"].subject.toutf8
    from = self.fetch(uid, "ENVELOPE")[0].attr["ENVELOPE"].from[0].mailbox + "@" + self.fetch(uid, "ENVELOPE")[0].attr["ENVELOPE"].from[0].host
    escape_chars
    body = self.fetch(uid, "RFC822.TEXT")[0].attr["RFC822.TEXT"].toutf8.gsub(escape_chars, "").gsub(/・+/, "・").gsub(/ +/," ").gsub(/\(+/,"(").gsub(/\)+/,")").gsub(/(\r\n)+/,"\n")
    "#{subject}《#{from}》#{body[0..(140 - 1 - 2 - subject.size - from.size)]}"
  end
end

mail_to_tweetメソッドを使ってみる。

pry(main)> imap.mail_to_tweet(last_uid)
=> "[NEニュース NO.3773]シャープのセンサ、ジェスチャーと照度、近接の三つを検知《ne@nikkeibp.co.jp》\n【NEニュース2013年6月19日号】No.3773\n発信元日経BP日経エレクトロニクス編集\nhttp://techon.nikkeibp.co.jp/"

接続終了処理

imap.logout
imap.disconnect

*1:いちいちメソッドチェーンで書くよりも変数に入れた方がいい?その通りですよw 単にpryで実験してるだけですw