Heroku Schedulerでruby-gmailを使ってGmailの送信を予約できるようにした

メールを送信しようと思ったら深夜で、でも深夜にメール送るのもなんか嫌で、結局下書き保存して後で送信することがよくあって困ります。 夜は早く寝ろというツッコミもありますが、深夜のメール以外にも何かとメールを遅らせて送信したい場面は少なくありません。
送信を予約できるような仕組みは無いのかと探したところ、いくつか見つかりました。

今あるツール

メーラー

ThunderbirdやOutlookには送信を予約できる機能があるそうです。しかし、普段からメーラーを使わずにブラウザからGmailを使っているので、わざわざインストールするのはめんどくさいです。それに予約した時間にPCの電源を入れてないと使えないと思うんですが、どうなんでしょうか。調べてません。

ブラウザ拡張機能

Right InboxBoomerang for Gmailというブラウザの拡張機能を使うと、Gmailの予約送信ができるようになります。普段のGmailの画面にボタンが追加され、そこで時間を指定できます。予約した時間になると、サーバーがGmailにアクセスしてメールを送信する仕組みだと思われます。ただし月10通までしか無料で使えないという欠点があります。

じゃあ自分で作ればいいじゃん

  • 無料、かつ件数制限無し
  • PC立ち上げっぱなしじゃなくていい

こういうのが欲しかったのですが、探した限りありませんでした。じゃあ自分で作ればいいじゃん。
無料のPaaSとかでできないかなと探していると、良さそうなものとしてHeroku Schedulerが見つかりました。

Heroku Scheduler

Herokuのアドオンの1つで、cronみたいなことを行うことができます。ただし実行間隔は固定されていて、10分/1時間/1日間隔のいずれかです。
Herokuの料金体系は少し分かりにくいですが、どうやらプロセスの動いた時間の合計が月750時間以下であれば無料みたいです。 webのプロセス数を0にして、workerのプロセスを1つだけを動かすようにすれば、ずっと動かしていても無料の範囲内に収まります。

作ってみた

使い方

  1. 件名の先頭に[1h30m]とか書いてGmailの下書きに保存しておく
  2. (約)1時間30分後に自動で送信される

とっても簡単ですね。

仕様

  1. 10分ごとに起動
  2. Gmailにアクセスして下書きメールを見る
  3. 件名の先頭に時間指定があった場合、その時間だけメールの保存時刻から過ぎていたら送信する

10分よりも細かな時間は指定出来ませんが、10分は誤差とみなせる範囲なので問題ありません。

実装

最近なんとなくRubyを勉強したいなと思っていたので、Rubyで書きました。 入門書は読んでいなくて、分からなかったらググって調べるを繰り返してたので、シンタックスとかかなり怪しいです。

Gmailのアクセスにruby-gmailというgemを使うので、まずGemfileを用意。
Gemfile

source "https://rubygems.org"
gem 'ruby-gmail'

bundlerでgemをインストール。

$ bundle install --path=vendor/bundle

スクリプトを書きます。
send-gmail-later.rb

# encoding utf-8
require 'gmail'

USERNAME = ENV['GOOGLE_USERNAME']
PASSWORD = ENV['GOOGLE_PASSWORD']

PATTERN = /^\[(\d+d)?(\d+h)?(\d+m)?\] /

Gmail.new(USERNAME, PASSWORD) do |gmail|
  now = DateTime.now

  gmail.mailbox('[Gmail]/Drafts').emails(:all).each do |draft|
    if draft.subject =~ PATTERN
      d = $1.to_i
      h = $2.to_i
      m = $3.to_i
      if d == 0 && h == 0 && m == 0
        next
      end

      send_date = draft.date + d + Rational(h * 60 + m, 1440)
      if send_date <= now
        gmail.deliver do
          to draft.to
          subject draft.subject.sub(PATTERN, '')
          body draft.body.decoded.encode('utf-8', draft.charset)
        end

        draft.date = now
        draft.archive!
      end
    end
  end
end

.bundleとvendorディレクトリは.gitignoreに追加しておきます。

$ cat << EOF > .gitignore
.bundle/
vendor/
EOF

2013-06-20追記: .bundleディレクトリは.gitignoreに追加しないほうがいいです。

gitにコミット。

$ git init
$ git add .
$ git commit -m "Initial commit"

Heroku Toolbeltをインストールして、ログインなどの初期設定をします。
自分の環境はUbuntuなのでUbuntu用をインストールします。

$ wget -qO- https://toolbelt.heroku.com/install-ubuntu.sh | sh
$ heroku login

heroku appを作成します。
このときgitのワーキングディレクトリにいると、自動でremoteにherokuが追加されます。

$ heroku create

webのプロセスは使わないので、プロセス数を0にします。

$ heroku ps:scale web=0

Googleのユーザー名とパスワードはスクリプト中には書けないので、Herokuの環境変数に登録します。

$ heroku config:add GOOGLE_USERNAME={自分のユーザー名}
$ heroku config:add GOOGLE_PASSWORD={自分のパスワード}

タイムゾーンを東京に設定して、時刻の比較をできるようにします。

$ heroku config:add TZ=Asia/Tokyo

デプロイ。

$ git push heroku master

Heroku Schedulerアドオンを追加。
このアドオン自体は無料ですが、クレジットカード情報を登録していないと追加できません。

$ heroku addons:add scheduler:standard

schedulerの設定画面を開きます。

$ heroku addons:open scheduler

Add Job...をクリックして、taskを登録します。

$ bundle exec ruby send-gmail-later.rb

frequencyをEvery 10 minutesに設定してSaveを押せば完了です。