Twitterへのメールからの投稿
Twitter 始めました、(hs9587) on Twitter。
で、だ。メールを送ったら Twitter投稿してくれるサービス無いかなと。いかにもありそうだけど探すの面倒だし、自分用に作った。これで出先からの投稿が出来るようになった。
環境というか方針としては、CentOS で Postfix の SMTPサービスが動いてる所にメールを送り、それを Rubyスクリプトに渡してhttp://apiwiki.twitter.com/Twitter-REST-API-Method%3A-statuses%C2%A0updateをどうこうする。
Postfix 辞典 (DESKTOP REFERENCE)
- 作者: とみたまさひろ
- 出版社/メーカー: 翔泳社
- 発売日: 2006/06/21
- メディア: 単行本
- クリック: 14回
- この商品を含むブログ (7件) を見る
Postfix が特定のアドレスへのメールを捕まえる
「Postfix 辞典」逆引きリファレンス27「外部コマンドを利用したい」を参考に「transport_maps = hash:/etc/postfix/transport」の設定をする。
/etc/postfix/transport
twitter-update@<サーバアドレス> twitter_api: twitter-timeline@<サーバアドレス> twitter_api: twitter-mentions@<サーバアドレス> twitter_api: twitter-user@<サーバアドレス> twitter_api:
取り敢えず twitter-update しか実装してないけどタイムラインぐらい将来取りたいのでキーワード書いとく。
あと、これらのアドレスが有効になるのは、「transport_maps = <云々>」の設定と同様に /etc/postfix/main.cf に「local_recipient_maps =」として(= の右辺は無し)任意の @左辺アドレスを有効にしているから。そもそもなぜそうしてるかというと、それはまあいろいろある。そちらのメールアドレス名前空間からはこの4つの名前を奪った事になる。
書き換え後は「sudo /usr/sbin/postmap /etc/postfix/transport」を忘れずに。
それを Postfix から外部コマンドへ渡す
上記キーワード「twitter_api:」は転送エージェントの指定と言う事になっている、それは下記に定義したの。
/etc/postfix/master.cf
twitter_api unix - n n - - pipe user=<ユーザ名> argv=/home/<ユーザ名>/projects/twitter_api.rb ${sender} ${recipient}
最初 master.cf での定義と思わず main.cf に書いててはまった。いろいろエラーになったり或は何も起こらなかったり、ログを見れば上記 transport でメール捕まえてはいるのだが twitter_api 見付らないとエラーになってたり。
設定は /etc/postfix/master.cf です、「Postfix 辞典」逆引きリファレンス27「外部コマンドを利用したい」の記述はちょっとわかり難かった。
改行の作法とか、「unix - n n - - pipe」の項目については適宜 Postfix の資料を見よ。
- 作者: 荒木靖宏
- 出版社/メーカー: オーム社
- 発売日: 2004/08/01
- メディア: 単行本
- 購入: 4人 クリック: 32回
- この商品を含むブログ (22件) を見る
${sender} ${recipient} 以外に使用できる変数は「Postfix 辞典」機能リファレンス「pipe」p.111 とか。subject はないのね、したらまあこの二つくらいか。
渡された Rubyスクリプトが Twitter API で投稿する
Rubyスクリプト側での処理の分岐は第二引数 ${recipient} を見ることにしよう。登録されてる ${sender} に応じた Twitterアカウントで Twitter REST API に出掛ける。取り敢えず認証は甘い、まあ、自分専用だし。変なのが来る様ならブロックしましょう(それで間に合うのか、間に合うといいな)
メール内容は標準入力でやってくる。はじめ subject を Twitter投稿内容にすることも考えたが、subject なので Base64エンコードされてる。なので本文一行目(JISコードというか、ISO-2022-JP)を投稿する事にしよう。
#!/usr/bin/ruby # -*- coding: utf-8 -*- require 'nkf' require 'net/http' 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 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 else end # case recipient else# if Trusted.include? sender f.puts $stdin.read end # if Trusted.include? sender }
初め openuri でどうかなと思ったけど、Post だし基本認証だし Net::HTTP を直接使う。あと、gem にも Twitterクライアントっぽいもの幾つかあるっぽいのだけど、よく分からないので全部手で書いた。
メール本文の一行目、空行の次の行を見分ける所どうでしょう。メール全体をパースしたりしないでいいよね、でもちょっと手抜き過ぎか。
日本語は最初化けっぽかった、NKFでユニコードにしたらなんとか。
Trusted配列と、Twittsハッシュを二つ持ってる必要は無いよね、ハッシュに(キーが)あるなら OK とかそういうロジックで良い筈。
課題とか
そもそも自前で自由に出来る SMTPサービス(Postfixサーバ)があるという前提だし、大仕掛けに過ぎるような気もする。foward処理を頑張ればここまで仕掛け大きくしなくても良い様な気もする。いかがでしょうか
transport設定いじるという大仕掛けの割りに Rubyスクリプトをユーザホームディレクトリに置いてるのも不釣合いかな。まあ、そんな特権的なユーザなんだという風に解釈しましょう。
あとは Rubyスクリプトで、${recipient} による処理の分岐やタイムラインの取得、と、それを ${sender} に送るとか。かな。
上記スクリプト自体もリファクタリングの余地あるよね、ちょっと、いろいろ、大分。