HerokuDay 22

Heroku+Rails+Mandrillでメールを受信してデータを作成してみた

More than 3 years have passed since last update.


はじめに

メール送信によりデータを投稿してみたいことあると思います。

mandrillを利用すれば簡単に実現できたので今回はサンプルプロジェクトを作りつつ覚書がてら綴っておこうと思います。


サンプルプロジェクトの概要


  • ユーザー登録が必要なコメントが出来る掲示板アプリ

  • 特定のメールに送信すると新しい掲示板を作成できる

  • 自分の作成した掲示板にコメント通知メールが届く

  • コメント通知メールに返信すると該当の掲示板にコメントできる


サンプルプロジェクトのER図

スクリーンショット 2014-12-21 16.52.57.png

boardにもったtokenはコメント通知返信時にどのboardか特定するのに利用する感じ。


まずはメール関連以外を作成

deviseとbootstrapを利用してサクっと作成。

この時点のコードはこちら


メールを受信して掲示板を作成できるようにする


  • mandrillのgemを追加


Gemfile

gem 'mandrill-rails'



models/board.rb

class Board < ActiveRecord::Base

belongs_to :user
has_many :comments, dependent: :destroy

validates :title, presence: true
validates :content, presence: true

# 以下のメソッドを追加
def self.save_inbound_mail!(event_payload)
user = User.find_by!(email: event_payload.sender_email)
user.boards.create!(title: event_payload.subject, content: event_payload.message_body(:text))
end
end



controller/inbox_controller.rb

class InboxController < ApplicationController

include Mandrill::Rails::WebHookProcessor

skip_before_action :authenticate_user!

def handle_inbound(event_payload)
Board.save_inbound_mail!(event_payload)
end
end



config/routes.rb

Rails.application.routes.draw do

devise_for :users
resource :inbox, controller: :inbox, only: %i(show create) # 追加
resources :boards, except: %i(edit update) do
resources :comments, only: %i(create destroy)
end
root 'boards#index'
end


ここまで書いて一度herokuへdeploy。

mandrillで設定時に作成したアクションへアクセスできるかチェックするため。


  • mandrillの設定


  1. herokuからmandrillへアクセス

    mail-board_·_Resources___Heroku.png


  2. inboundの設定へ

    Transactional_Reports___Mandrill.png


  3. 受信するメールのドメインを追加

    Inbound_Domains___Mandrill1.png


  4. DNSの設定

    Inbound_Domains___Mandrill2.pngInbound_Domains___Mandrill3.png

    Route53の場合はこんな感じでMXレコードを追加。

    Route_53_Management_Console.png

    value-dominで同様の設定をしようとしたら上手くいかなかった。

    原因は深く調べてないけどルートのMXレコードがあるとサブドメインのMXレコードをうまく設定できないのかな。。。


  5. 受信メールの設定へ

    Inbound_Domains___Mandrill4.png


  6. Routeを追加

    Mailbox_Routes_for_mail-board_luckofwise_com___Mandrill5.pngMailbox_Route_for_mail-board_luckofwise_com___Mandrill7.png

    今回は board@mail-board.luckofwise.com にメールを送信すると掲示板を作成するように設定。

    Post To URLへアクセス出来ないと設定できないので予めアプリをデプロイしておく必要があります。


これでMandrillの設定は完了。

board@mail-board.luckofwise.com にメールを送信すると掲示板を作成するようになりました。

この時点のコードはこちら


コメント通知メールを送信するようにする

次に作成した掲示板にコメントが投稿されると作成者に通知メールが送信されるように改修します。


app/mailers/user_mailer.rb

class UserMailer < ActionMailer::Base

default from: 'no-reply@' + Rails.application.secrets.mail_domain_name

def comment_created(comment)
@comment = comment
mail to: comment.board.user.email, subject: '新しいコメントがつきました'
end
end



app/views/user_mailer/comment_created.html.haml

%p#{@comment.board.title}」にコメントがつきました。

%div{style: 'border: 1px dashed gray; padding: 0 10px; margin-bottom: 20px;'}
%p #{@comment.user.name} #{l @comment.created_at}

= simple_format(@comment.content)

= link_to board_url(@comment.board), board_url(@comment.board)



app/controllers/comments_controller.rb

class CommentsController < ApplicationController

before_action :set_board

def create
@comment = @board.comments.build(comment_params)
@comment.user = current_user
if @comment.save
# 以下の行を追加
UserMailer.comment_created(@comment).deliver! unless @comment.user == @comment.board.user
redirect_to @board, notice: 'コメントしました。'
else
render 'boards/show'
end
end

end


ここは特に変わったことしてないです。

この時点のコードはこちら


コメント通知メールに返信でコメントできるようにする


  • メールからboardを特定するためのtokenをセット

今までに作ったboardはよしなにセットするか作り直すかしてください。


Gemfile

gem 'uuidtools' #追加



app/models/board.rb

class Board < ActiveRecord::Base


# 以下を追加
before_create :generate_token
def generate_token
self.token = UUIDTools::UUID.random_create.to_s
end

end


  • コメント投稿用のinboundの処理を追加


app/models/comments.rb

class Comment < ActiveRecord::Base


# 以下を追加
def self.save_inbound_mail!(event_payload)
user = User.find_by!(email: event_payload.sender_email)
board = Board.find_by!(token: event_payload.recipient_emails.first.split('@').first[/^.*\+(.*)$/, 1])
board.comments.create!(user: user, content: event_payload.message_body(:text))
end
end


app/controllers/inbox/comments_controller.rb

class Inbox::CommentsController < ApplicationController

include Mandrill::Rails::WebHookProcessor

skip_before_action :authenticate_user!

def handle_inbound(event_payload)
Comment.save_inbound_mail!(event_payload)
end
end



config/routes.rb

Rails.application.routes.draw do

devise_for :users
namespace :inbox do
resource :boards, only: %i(show create)
resource :comments, only: %i(show create)
end
resources :boards, except: %i(edit update) do
resources :comments, only: %i(create destroy)
end
root 'boards#index'
end

メール受信用のコントローラーが複数になるので構成を少し変更。


  • コメント通知メールにreply_toを設定


app/mailers/user_mailer.rb

class UserMailer < ActionMailer::Base

default from: 'no-reply@' + Rails.application.secrets.mail_domain_name

def comment_created(comment)
@comment = comment
mail to: comment.board.user.email, subject: '新しいコメントがつきました',
reply_to: "comment+#{comment.board.token}@#{Rails.application.secrets.mail_domain_name}"
end
end



  • mandrillにコメント投稿用の設定を追加
    Mailbox_Routes_for_mail-board_luckofwise_com___Mandrill.png
    Mailbox_Route_for_mail-board_luckofwise_com___Mandrill1.png

これでコメント通知メールに返信してコメントを投稿することができます。

これでサンプルアプリは完成です。

この時点のコードはこちら


その他

HerokuにはMandrill以外にメール受信でpostしてくれるAddonesは存在します。

CloudMailInを利用するとドメインをもっていなくてもCloudMailInが受信用のメールアドレスを用意してくれるので気軽に試せるかと思います。


おわりに

比較的簡単にメールを送信することによりデータを登録することが出来たと思います。

Herokuのアドオンには便利なのがいっぱいありますねー。

今回作ったサンプルプロジェクトはこちらで動作の確認ができます。

https://mail-board.herokuapp.com/

(そのうち消すかもしれないですが。。。)

今回作ったコード

https://github.com/LuckOfWise/mail-board