Rails6が出る前のActionMailbox入門

# ActionMailBox とは

Rails6で入るframeworkの一つです。
今までメールの送信機能として ActionMailer ではありましたが、
メール受信に関する機能がなく、そこで入ったのが ActionMailbox です。

# ドキュメントなど

- RailsGuide
    - <https://edgeguides.rubyonrails.org/action_mailbox_basics.html>
    - <https://railsguides.jp/action_mailbox_basics.html>
- edge api rails
    - <https://edgeapi.rubyonrails.org/classes/ActionMailbox.html>
    - <https://edgeapi.rubyonrails.org/>
- GitHub
    - <https://github.com/rails/actionmailbox>
    - <https://github.com/rails/rails/tree/master/actionmailbox>
- この記事書く上で参考にしたもの
    - <https://gorails.com/episodes/action-mailbox-rails-6>

# 機能を3行で

- SendGrid などのメールサービスでメールを受信したときに、Railsの専用エンドポイントに転送する
- ApplicationController と似た ApplicationMailbox でroutingを書く
- ApplicationMailbox を継承したクラスでメール受信時の処理を行う

# ActionMailbox を使うとできそうなこと

- GitHubにある、コメントのメールに返信したらGitHub上でもコメントできる、的な機能
    - 参考にした動画( https://gorails.com/episodes/action-mailbox-rails-6 )では、これを作っています。
- メールサポートシステム(メールディラーなどの代わり)
    - ActionMailer, ActionMailbox を使って、ユーザー情報とも紐づけてサポート対応をより簡単にできるかも。

# 処理の流れのイメージ

例えば、『Aさんが書いたブログにコメントをされ、その通知をAさんにメールで送信、Aさんがメールに返信したらコメントを返せる』という機能を考えてみます。

- ブログにコメントがされる
- ActionMailer でコメントされた通知をAさんにメールで送る
    - to はAさんのメールアドレス(`a-san@example.com`とします)
    - from には、ブログ・コメントのIDを埋め込んでおきます。 `reply-blog_1-comment-2@someapp.example.com` という感じ。
- Aさんはは送られてきたメールに返信する
- メールサービスがメールを受信し、railsのアプリケーションに転送
    - 受信・転送がされるように設定しておく必要があります。
        - ドメインの設定と受信時に転送する設定が必要です。
        - 詳しくは、RailsGuide や各メールサービスのドキュメントを見てください。
- `ApplicationMailbox`の routing でどのmailboxで処理をするか振り分ける
    - 基本的には、メールを受信したときの`to`で振り分けをします
        - ここでは `reply-blog_1-comment-2@someapp.example.com` というメールアドレスです。
- `FooMailbox#process` でメール受信時の処理をします。
    - `to`( `a-sann@example.com` )メールアドレスで、ユーザー情報を検索
    - `from`( `reply-blog_1-comment-2@someapp.example.com` )メールアドレスで、コメントすべきblog, commentを特定
    - `body`にある文章でコメントを追加


# 実装する上で知っておくと理解が早いこと

### RailsGuide には、ActionMailbox の最低限の情報が載っているだけです。

全体像理解してからなら十分読めると思いますが、それまではエスパーするのはなかなか難しいです。


### `ApplicationMailbox` のroutingで、受信したメールの `to`で処理を振り分ける

```ruby
# app/mailboxes/application_mailbox.rb
class ApplicationMailbox < ActionMailbox::Base
  routing /^save@/i     => :forwards
  routing /@replies\./i => :replies
  # routing :all => :foos
end

# app/mailboxes/forwards_mailbox.rb
class ForwardsMailbox < ApplicationMailbox
  def process
    # ...
  end
end

# app/mailboxes/replies_mailbox.rb
class RepliesMailbox < ApplicationMailbox
  def process
    # ...
  end
end
```

RailsGuideに書かれてるコードを参考に見てみます。
`ActionMailbox::Base`を継承した `ApplicationMailbox`に `routing`を書きます。
受信したメールの `to`が、routingに書いた正規表現にマッチしたら指定のmailboxに振り分けします。
例えば、 `topic+1@replies.example.com` というメールアドレスでメールを受信した場合、
`routing /@replies\./i => :replies`の正規表現にマッチするので、 `RepliesMailbox`クラスに振り分けられます。


### Mailboxの実装のサンプル

<https://gorails.com/episodes/action-mailbox-rails-6> で実装するものがこのような感じです。

```ruby
class RepliesMailbox < ApplicationMailbox
  MATCHER = /\Areply-(.+)@reply.example.com/i # domain部分は例で書いてあるだけです。

  before_processing :require_user

  def process
    discussion.comments.create(
      user: user,
      body: mail.decoded,
    )
  end

  private

  def user
    # ユーザーは事前に同じメールアドレスでサイトに登録されてる必要があります。
    @user ||= User.find_by(email: mail.from)
  end

  def require_user
    unless user
      # bounce_with でbounceの処理をする。ここで#processの処理は停止します
      # ユーザーが見つからない場合、bounceのメールを送信する
      bounce_with UserMailer.missing(inboud_email) 
    end
  end

  def discussion
    @discussion ||= Discussion.find(discussion_id)
  end

  # メールアドレスからdiscussion_idを取り出す
  def discussion_id
    recipient = mail.recipients.find { |r| MATCHER.match?(r) } 
    recipient[MATCHER, 1]
end
end
```

- mailbox は controller と似た感じで実装できます
    - まず `#process` が呼ばれる
    - `before_action` -> `before_processing`
    - `request` -> `mail`, `inbound_email`
        - `mail`: <https://github.com/mikel/mail> のクラスのオブジェクト
        - `inbound_email`: `ActionMailbox::InboundEmail` のレコード
    - `ActionMailbox::InboundEmail`
        - `rails action_mailbox:install`で作られる、ActiveRecordのクラス
        - 受信したメールはここに溜まっていく
        - 一定期間したら消えていく
            - <https://railsguides.jp/action_mailbox_basics.html#inboundemailsの「焼却（incineration）」>
- 上記のサンプルコードでは、受信時のメールアドレス(`to`, `recipients`)から duscussion_id を取り出しています。
    - このような実装をする場合、メールアドレスが重要なので、まずアプリ側からそういうメールアドレスを作ってユーザーにメールを送信する必要があるでしょう。
        - サイト側からメールを送るときの `from` のアドレスが重要ということです。それに返信してもらったら、受信時の`to`になるので。
        - もっと賢い方法をもしご存知でしたらぜひ教えていただけるとありがたいです :bow:
- `User`, `Discussion`, `Comment`, `UserMailer` などは当然自前で実装が必要です。
    - RailsGuide のサンプルコードの `Person`, `Forward`, `Forwards::RoutingMailer` あたりも自前実装のはずです。

### development環境で試す

<https://railsguides.jp/action_mailbox_basics.html#action-mailboxをdevelopment環境で使う>

`/rails/conductor/action_mailbox/inbound_emails` に `ActionMailbox::InboundEmail`のcontrollerが用意されています。
`/rails/conductor/action_mailbox/inbound_emails/new` でメール入力フォームが表示され、メール受信時の挙動を再現できます。

> <img width="480" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/26310/15f8b322-0841-c7f9-4dc5-76c6f62b5ac4.png">

# ローカル環境で実際のメールの受信を試してみる

Mailgun や SendGrid のようなメール送受信サービスを使う場合、
外部からのリクエストを受け付けられるようにする必要があります。

<https://ngrok.com/>
ngrokを使うと良いでしょう。
ngrokを使うと、外部からアクセスできるURLが発行できます。

いくつか手順があります。
Mailgunを例にします。

- Mailgunのアカウント登録を済ませる
- ngrokを `ngrok http 3000` 起動してURLを発行する
    - railsを3000ポート以外で起動する場合はそれに合わせて設定してください。
    - 無料で使ってる分には、発行されるURLは起動のたびに変わるので注意してください。
    - <img width="480" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/26310/9d24c8cb-f960-24d0-ecbe-92e384c378ce.png">

- Mailgun で、メールを受信したときに Rails にメールを転送するように設定する
    - <img width="640" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/26310/0c38b5fa-33ee-6645-9ccd-cbfddfbe6726.png">
    - RailsGuide に書くメールサービスごとに設定の仕方が書いてあります。
        - Mailgun の場合、 https://railsguides.jp/action_mailbox_basics.html#mailgun
        - 設定するURLは `https://<ngrokで発行されたドメイン>/rails/action_mailbox/mailgun/inbound_emails/mime`になります。
        - pathは、メールサービスごとに違うので注意してください。
- Mailgun から発行された API key を Rails の Credentials に設定する
    - <https://railsguides.jp/action_mailbox_basics.html#mailgun>
- Rails が　Mailgun を使用するように設定する
    - `config.action_mailbox.ingress = :mailgun`
    - https://railsguides.jp/action_mailbox_basics.html#mailgun
- Rails を起動する
- Mailgun で発行されたドメインに対してメールを送る
    - <img width="640" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/26310/52d42f13-ad91-c2a5-b88e-49d9b6dfdd5a.png">

これで基本的には動くはずです。
動かない場合、以下を確認してみると原因特定につながるかと思います。

- Mailgunのダッシュボード
- ngrokのダッシュボード
    - <http://127.0.0.1:4040>
- InboundEmails
    - <http://localhost:3000/rails/conductor/action_mailbox/inbound_emails/>

# おわりに

ActionMailbox の触りの紹介の記事でした。
まだこの記事を書いている段階では Rails6 はリリースされていませんし、ネットには情報がとても少ないです。
ですので、実際運用しようと実装してみると、もっと知らないといけないことはあるかもしれないですし、
ハマりどころもあるかもしれません。

ですが、この記事がとりあえず触ってみるきっかけになれば幸いです :smile: