【2021年版】ちょっとしたRubyスクリプトからGmailで添付ファイル付きのメールを送信する

あやしいブログ記事っぽいタイトルにしてみました。まあでも2021年現在ではこれでいいかなという内容だとは思います。

昨日知り合いからの依頼で、何十とあるPDFファイルをCSVに従って自動でメール送信する手段が欲しいとのことで、ちょっとしたスクリプトを書いてみました。

下準備

  1. SMTPでメール送信したいので、Gmail側で「IMAPを有効にする」の設定が必要です
  2. SMTPで送信する際に、(いくら今だけといえど)スクリプト内にパスワードをべた書きするのは嫌なので、「アプリパスワード」という目的を絞ったトークンを発行します。そのためには二段階認証の有効化が必要なので有効化します
  3. 無事に二段階認証が有効化できたら、アプリパスワードを発行します。有効化するアプリは「メール」を選び、名前は適当につけておきます

以上でGmail側の準備は完了です

実装

mail というそのままの名前のgemがあるので、これを使います。RailsでおなじみのActionMailerも単体で使えるようですが、このgemはセットアップが簡単そうだったので採用しました。

mikel/mail: A Really Ruby Mail Library
https://github.com/mikel/mail

require "mail"

Mail.defaults do
  delivery_method :smtp,
                  { address: "smtp.gmail.com", # Gmailの指定通り
                    port: 587, # Gmailの指定通り
                    # domainはFromの記載が@以前だけに省略されている場合の補完用のドメインらしい(Wordspaceなら独自ドメインに変更)
                    domain: "gmail.com",
                    user_name: "##GmailやGoogleWordspaceのアドレス##",
                    password: "##取得したアプリパスワード##",
                    # Gmail側で可能ならTLSを有効化するよう指定されているため
                    enable_starttls_auto: true }
end

m = Mail.new do
  from "表示名 <##GmailやGoogleWordspaceのアドレス##>" # 表示名 <送信元メールアドレス>
  to "##宛先##"
  subject "【件名】送信テスト"
  add_file "./file.pdf" # 添付ファイルへのパス

  text_part do
    # マルチバイト文字列を使っているためUTF-8を指定(未指定だと警告が出ます)
    content_type "text/plain; charset=UTF-8"
    body "【本文】送信テスト"
  end
end

m.deliver

ポイントは諸々コメントに書いた通りです。文字コードの指定方法にたどり着くまでが少し大変でした(添付ファイルのContent-Typeまで上書きされてしまって変なのが来たり)

メッセージの生成の方法はハッシュだったり文字列だったり色々とスタイルが選べるようです

Module: Mail — Documentation for mail (2.7.1)
https://www.rubydoc.info/gems/mail/Mail#new-class_method

一つ気になるのがUTF-8でメールを送って文字化けしたりしないのだろうかというところですが、GmailでGUIからふつうに送信しているメール自体がUTF-8になっているようなので、たぶん大丈夫だと思います。たぶん。。

ということで無事に送信できたメールはこちら

Content-Transfer-Encodingは特に指定していませんが、base64で送信されていました。(GmailでGUIからメールを送信する場合と同じです。)

ソースコードにパスワードを埋め込むあたり突っ込みたいような気もしつつ、単発で送るだけなら今回の方法でもお手軽さを考えればありだと思います

一通り作業が済んだらアプリパスワードはGoogleの管理画面から無効化しておきましょう。

パスワードを標準入力から受け取るとか.envに入れるとかもありだと思います

PAGE TOP