メールによる Twitterの参照

Twitterへのメールからの投稿 - Rubyとか Illustratorとか SFとか折紙とか続き

それでメールによる Twitterの参照を受け付けるようにしてみた、参照結果をメールで送り返す。

#!/usr/bin/ruby
# -*- coding: utf-8 -*-
require 'nkf'
require 'net/http'
require 'net/smtp'
Net::HTTP.version_1_2
Trusted = [
  '<投稿者メールアドレス>',
  ]
Twitt = Struct.new :acount, :password
Twitts = {
  '<メールアドレス@の前>' => Twitt.new('<Twitterアカウント>', '<Twitterパスワード>')
  }

sender, recipient, = ARGV
File.open('/home/<ユーザ名>/projects/'+Time.now.strftime('%Y%m%d%H%M%S'), 'w'){ |f|
f.puts sender, recipient, ''
if Trusted.include? sender then
  #File.open('/home/<ユーザ名>/projects/'+Time.now.strftime('%Y%m%d%H%M%S'), 'w'){ |f| f.puts ARGV, $stdin.read }
  twitt = Twitts[sender.split('@')[0]]
  case recipient.split('@')[0]
    when 'twitter-update' then
      null_line = false
      doing = ''
      $stdin.readlines.each do |line|
         (doing = line.chomp; break) if null_line
         null_line = true if 1 == line.length
      end # $stdin.readlines.each do |line|
      f.puts doing
      request = Net::HTTP::Post.new('/statuses/update.json')
      request.basic_auth twitt.acount, twitt.password
      response = nil
      Net::HTTP.start('twitter.com',80) do |http|
        response = http.request(request, "status=#{NKF.nkf('-Jw', doing)}")
      end # Net::HTTP.start('twitter.com',80) do |http|
      f.puts response.body
    when 'twitter-timeline' then
      null_line = false
      doing = ''
      $stdin.readlines.each do |line|
         (doing = line.chomp; break) if null_line
         null_line = true if 1 == line.length
      end # $stdin.readlines.each do |line|
      count = /\A\d+/=~doing ? doing.to_i : 20
      f.puts doing, count, ''
      request = Net::HTTP::Get.new('/statuses/friends_timeline.rss')
      request.basic_auth twitt.acount, twitt.password
      response = nil
      Net::HTTP.start('twitter.com',80) do |http|
        response = http.request(request, "count=#{count}")
      end # Net::HTTP.start('twitter.com',80) do |http|
      result = response.body.split("\n").select do |line|
        (line.include?('<description>')..line.include?('</description>')) ? true : false
      end.map do |line|
        line.sub(/^\s*<description>/, '').sub(/<\/description>\s*$/, '')
      end.map{ |line| line.gsub(/&#(\d+?);/){ [$1.to_i].pack('U') } }
      f.puts result
      Net::SMTP.start('localhost',25) do |smtp|
        smtp.send_mail <<-EOM, recipient, sender
From: #{recipient}
To: #{sender}
Subject: #{recipient.split('@')[0]}
Date: #{Time.now}
Message-Id: <#{Time.now.to_i}.#{recipient}>

#{NKF.nkf('-Wj', result.join("\n"))}
        EOM
      end # Net::SMTP.start('localhost',25) do |smtp|
    else
  end # case recipient.split('@')[0]
else# if Trusted.include? sender
  f.puts $stdin.read
end # if Trusted.include? sender
}

大きく付け加わってるのはこの辺、when 節ひとつ。

    when 'twitter-timeline' then
      null_line = false
      doing = ''
      $stdin.readlines.each do |line|
         (doing = line.chomp; break) if null_line
         null_line = true if 1 == line.length
      end # $stdin.readlines.each do |line|
      count = /\A\d+/=~doing ? doing.to_i : 20
      f.puts doing, count, ''
      request = Net::HTTP::Get.new('/statuses/friends_timeline.rss')
      request.basic_auth twitt.acount, twitt.password
      response = nil
      Net::HTTP.start('twitter.com',80) do |http|
        response = http.request(request, "count=#{count}")
      end # Net::HTTP.start('twitter.com',80) do |http|
      result = response.body.split("\n").select do |line|
        (line.include?('<description>')..line.include?('</description>')) ? true : false
      end.map do |line|
        line.sub(/^\s*<description>/, '').sub(/<\/description>\s*$/, '')
      end.map{ |line| line.gsub(/&#(\d+?);/){ [$1.to_i].pack('U') } }
      f.puts result
      Net::SMTP.start('localhost',25) do |smtp|
        smtp.send_mail <<-EOM, recipient, sender
From: #{recipient}
To: #{sender}
Subject: #{recipient.split('@')[0]}
Date: #{Time.now}
Message-Id: <#{Time.now.to_i}.#{recipient}>

#{NKF.nkf('-Wj', result.join("\n"))}
        EOM
      end # Net::SMTP.start('localhost',25) do |smtp|

メールを送るのどうしようかと思った、ActionMailer とは言わないが TMail とか使おうかとも思ったが、Net::SMTP で手書きする。その為の require を冒頭に。

require 'net/smtp'

また、case 分岐処理のところ、コマンドメールアドレス前半だけ見れば良いよね、一々SMTPサーバのアドレスまで書かない。

  case recipient.split('@')[0]
    when 'twitter-update' then

それはそれとして

本文一行目
      count = /\A\d+/=~doing ? doing.to_i : 20

本文一行目が数字っぽかったら TimeLine の参照件数と見做すことにしよう。にしてもここの一行目文字列の変数名に doing は無いよね、update方面のコピペがたたる。

フリップフロップ
      result = response.body.split("\n").select do |line|
        (line.include?('<description>')..line.include?('</description>')) ? true : false
      end.map do |line|

」タグの中だけみる、Range のフリップフロップ動作って始めてそれと意識して使った気がする。本当に初めてって事も無いだろうけど。"if /^begin/ .. /^end/ など 条件式 式 .. 式"
「end.map do 」はどうですか、僕は結構好き。

ユニコード系数値文字参照
      end.map{ |line| line.gsub(/&#(\d+?);/){ [$1.to_i].pack('U') } }

ユニコードの数値文字参照の解決 - Rubyとか Illustratorとか SFとか折紙とか

メールの返信
      Net::SMTP.start('localhost',25) do |smtp|
        smtp.send_mail <<-EOM, recipient, sender
From: #{recipient}
To: #{sender}
Subject: #{recipient.split('@')[0]}
Date: #{Time.now}
Message-Id: <#{Time.now.to_i}.#{recipient}>

#{NKF.nkf('-Wj', result.join("\n"))}
        EOM
      end # Net::SMTP.start('localhost',25) do |smtp|

メールを送る所は良いよね。Net::SMTP とはちょっとローレベルに戻り過ぎたか、まあ凝ったもんじゃなし勘弁して、大体見に行く方も Net::HTTP なんだし。
http://doc.loveruby.net/refm/api/view/library/net.smtp

課題

ちょっとコピーペースト、だからコードの繰り返し。今後コマンドメール対応増やすならなんとかしないと。

「case」分岐もどうかな、「send <メソッド名シンボル>」にしましょう。と言うことは何かクラスかモジュールを作った方が良いかな、それは何のどんなクラス(モジュール)なのだろう。

そう思うと、コマンドメール「twitter-timeline@」はどうか、Twitter APIに拠れば「friends_timeline」「user_timeline 」「mentions」くらいか、或は「statuses/friends_timeline」。どうかな。