2008年5月30日金曜日

rubyでメールを送る

時間のかかるスクリプトとか、タスクスケジューラとかで動かしてるスクリプトとかは、終了時にメールを飛ばすようにできると無事に終わったことが確認できてうれしい。そんなわけでやり方を調べてみた。追加ライブラリを拾ってきたりとかしたくないので、標準のNet::SMTPでやる。ActionMailerでも使ったらいいのにね。
仕様は、本文に適当なメッセージを入れてログファイルを添付する。ログはテキストだから本文に貼り付けてもいいのだがせっかくだから添付の仕方を調べてみた。ただ、ヘッダのMIMEエンコードはめんどくさかったのでSubjectにはASCIIしか使わないことにした。コードは下の方に。
net/smtpだとヘッダを1から全部かかなきゃならんのが大変。multipartの肝はboundary。ヘッダには境界として使う文字列を記入。無駄にsha1とか使ってるがランダムな文字列である必要も毎回変化する必要もなく、本文中に出てこないならなんでもいい(使えない記号とか70文字までとかのルールはある)。実際にメール中で境界としてかくときは、ヘッダで指定したboundaryの頭に--をくっつける。そして、メールの最後には頭とおしりの両方に--をくっつけたboundaryを置く。というルールになっているようです。
参考:http://suika.fam.cx/~wakaba/-temp/wiki/wiki?boundary
自分しか使わないからさぼってるけどFile.openの例外はちゃんと拾うべきですね。

#!/usr/bin/ruby -Ks
require 'kconv'
require 'net/smtp'
require 'digest/sha1'

LogFile='log.txt'
MailFrom='mailfrom@example.com'
MailTo='mailto@example.com'
MailSubject=File.basename($0)+' finished'
SMTPHost='smtp.example.com'
SMTPPort=25
HELODomain='myhostname.example.com'
Boundary=Digest::SHA1.hexdigest(Time.now.to_f.to_s)
EncodeBlocksize=45

def send_mail
tmp=Array.new
tmp<<sprintf("Message-ID: <%f_%d@%s>",Time.now.to_f,$$,HELODomain)
tmp<<'Date: '+Time.now.strftime("%a, %d %b %Y %X")+' +0900 (JST)'
tmp<<'From: '+MailFrom
tmp<<'To: '+MailTo
tmp<<'Subject: '+MailSubject
tmp<<'MIME-Version: 1.0'
tmp<<'Content-Type: multipart/mixed; boundary='+Boundary
tmp<<''
tmp<<'--'+Boundary
tmp<<'Content-Type: text/plain; charset=ISO-2022-JP'
tmp<<'Content-Transfer-Encoding: 7bit'
tmp<<''
tmp<<Time.now.to_s.tojis
tmp<<'おわったよー'.tojis
tmp<<''
tmp<<'--'+Boundary
tmp<<'Content-Type: application/octet-stream;'
tmp<<' name="'+File.basename(LogFile)+'"'
tmp<<'Content-Disposition: attachment;'
tmp<<' filename="'+File.basename(LogFile)+'"'
tmp<<'Content-Transfer-Encoding: base64'
tmp<<''
File.open(LogFile){|fin|
src=fin.readlines.join('')
enc=[src].pack('m')
tmp<<enc
}
tmp<<'--'+Boundary+'--'
mailbody=tmp.join("\n")
Net::SMTP.start(SMTPHost,SMTPPort,HELODomain){|smtp|
smtp.send_mail mailbody,MailFrom,MailTo
}
end

send_mail

0 件のコメント: