自作 Twitter メールゲートウェイ

たまに無性に何かプログラミングしたくなります.少し前に,Twitter 用のメールゲートウェイが欲しくなったので,せっかくだから自作しました.

特殊な機能は必要なかったので,純粋に投稿だけできるものにしました.全体を二つの機能,すなわちメールの body を 160 オクテット以内に縮める機能,そして Twitter API で投稿する機能に分けて,それぞれを独立したプログラムにしました.使う時は,パイプで繋げます.

セキュリティ的には,From のアドレスを strcmp(3) 型の比較で検査しているだけです.携帯からの投稿のみを許可するのでしたら,Received ヘッダを見て検査を厳しくしたりできそうですね.とりあえず以下にソースを.


まず,メールの body を取り出して,160 オクテットに縮める前処理プログラムです.これは prepare と名づけました.メール body の charset を ISO-2022-JP に固定しているところが情けないですが,私個人の要求にはこれで十分でした.

メッセージ長を160オクテット以内に縮める処理で,Ruby のマルチバイトに対応した正規表現の恩恵に授かっています.正規表現を使わずに同じ処理を記述するとしたら次のようになるでしょう.UTF-8 は各文字の先頭1オクテットで1文字に必要なオクテット数が分かるので,メッセージ文字列を先頭から走査して160オクテットになる直前までを残すようにします.正規表現の方が何をやっているのか一目瞭然ですよね.

#! /usr/bin/ruby -Ku

require 'tmail'
require 'kconv'

FROM = ARGV.shift

# reading mail source
if ARGV[0]
  mail = TMail::Mail.load(ARGV[0])
else
  mail = TMail::Mail.parse(STDIN.read)
end

if mail.from[0] != FROM
  # p [1, mail.from]
  exit 1
end
if mail.content_type != 'text/plain'
  # p [2, mail.content_type]
  exit 1
end

message = Kconv.toutf8(mail.body)
if message.length < 156
  suffix = ' [M]'
else
  suffix = '... [M]'
  while message.length >= 160 - suffix.length
    message = /.$/.match(message).pre_match
  end
end
message.chomp!
message += suffix

puts message

つぎは,Twitter API を使って投稿するプログラム.update と名づけた.

#! /usr/bin/ruby -Ku

require 'net/http'
require 'uri'

ID = ARGV.shift
PASSWORD = ARGV.shift

source = ARGV[0] || STDIN.read
exit 1 if source.length > 160

message = URI.encode(source)
res = Net::HTTP.start('twitter.com') do |http|
  req = Net::HTTP::Post.new('/statuses/update.xml')
  req.basic_auth(ID, PASSWORD)
  http.request(req, "status=#{message}")
end

puts res

これらのプログラムを適当なパスに配置し,例えば $HOME/.qmail-xyzzy に以下の様に書くことで使えるようになります.

|preline /path/to/prepare dare_sore@docomo.ne.jp | /path/to/update ID PASS > $HOME/.twitter.log