Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

This article is a Private article. Only a writer and users who know the URL can access it.
Please change open range to public in publish setting if you want to share this article with other users.

More than 3 years have passed since last update.

Kotlin でメール送信をした話

Posted at

はじめに

この記事は 群馬高専 Advent Calendar 2021 6日目の記事です。遅れてしまって本当に申し訳ありません。13日も私が担当していますが、遅れてしまいそうです。

第23回工華祭の準備開始から終了までのお話です。これまでメール送信をしたことがなかったので手探りでやりました。また、基本的にお金がかけられないという制限の元で動いていますのでご了承ください。そのため、日記を見る程度の気持ちで読んでいただければと思います。

はじめてのメール送信

どんなシステムにするのかを話し合った結果、学籍番号に紐づけられたメールアドレスに対してメールを送る必要がある とわかりました。独自でメールサーバーを用意すれば、メールを送信できるだろう という程度の知識しかなく、とても悩みました。

まずは、SMTP(S)1 を使ってメール送信をしようと考えました。実行委員会として Google アカウントを持っていたので、Gmail についてコードを書いてみました。

ですが、セキュリティ的にも問題があるし、スマートではありません。WebAPI とか使って送信したい!Gmailじゃなく独自ドメインを使いたい!と考えました。Let's google.

SendGrid を知る

安直ですが、「mail api」とググりました。そうしたらこんなページを目にしました。

サンプルコードたくさんあるし、Java も対応してるし、私が求めていたものです。

しかし、このサービスはただ登録すればいいわけではありません。登録した後に審査があるのです。ですが、2日で審査が終わり、使えるようになりました。

さて、実際に使ってみることにしましょう。Kotlin では Java のライブラリが利用できるので、sendgrid-java を導入・参考にすれば問題ありません。

SendGridMailProvider.kt

import com.sendgrid.Method
import com.sendgrid.Request
import com.sendgrid.SendGrid
import com.sendgrid.helpers.mail.Mail
import com.sendgrid.helpers.mail.objects.Content
import com.sendgrid.helpers.mail.objects.Email
import java.io.IOException

class SendGridMailProvider(apiKey: String, fromEmailAddress: String) : MailProvider {
    private val sendGrid = SendGrid(apiKey)
    private val fromEmail = Email(fromEmailAddress)

    override fun sendMessage(toEmailAddress: String, subject: String, content: String): String {
        val toEmail = Email(toEmailAddress)
        val mail = Mail(fromEmail, subject, toEmail, Content("text/plain", content))
        val request = Request().apply {
            method = Method.POST
            endpoint = "mail/send"
            body = mail.build()
        }
        return try {
            val response = sendGrid.api(request)
            """
                To: $toEmailAddress
                StatusCode: ${response.statusCode}
                Body: ${response.body}
                Header: ${response.headers}
            """.trimIndent()
        } catch (ex: IOException) {
            throw ex
        }
    }
}

おわりです。この MailProvider を使うだけで好きにメールを送ることができます。

MailProvider?

MailProvider ってなんですか?と全員が思っているはずです。以下のようなインターフェースとして MailProivder を定義し、それを継承することで、SendGrid を使ったメール送信部分を定義します。

MailProvider.kt

interface MailProvider {
    fun sendMessage(toEmailAddress: String, subject: String, content: String): String
}

SendGridMailProvider の他にも ConsoleMailProvider を定義したり、TestMailProvider を定義すれば、開発の効率を上げたり、メールの送信を含む処理をテストにすることができます。

ConsoleMailProvider.kt

object ConsoleMailProvider : MailProvider {
    override fun sendMessage(toEmailAddress: String, subject: String, content: String): String {
        return """
            to: $toEmailAddress
            subject: $subject
            content: $content
        """.trimIndent()
    }
}

TestMailProvider

テストコードを書く時にメール送信がネックになってしまいます。メールで送ったパスワードをつかってログインできるかのテストをしたければ、メールの本文からパスワードを保存して、後から取得できるようなメソッドを定義したクラスでモックしてあければ、書けます。本当はそこらへんもテストした方がよかったのですが、テストを書くことに慣れていなかったので、実装していません。また、この文を読んでも理解できない人が大半だと思いますが、それでいいです

メール送信使いませんでした

本来、panel.kokasai.com を正式に動かすうえで必要だったのですが、要件が整理できていなかったり、開発期間を決められていなかったりと、数々の問題を抱えたままプロジェクト凍結しました。私が凍結させました。

それからは SendGrid を使う機会はありません。

QR コードを学生に配らなければならない

工華祭当日の朝、私が学校に向かっている時に電話が来ました。

「学生へのQRコード送信ができませんでした。」

絶望です。元々、SharePoint のワークフロー機能を利用してメールを送信する予定だったのですが、「できなかった」と当日になってから連絡が来ました。急いで学校に向かい、パソコンを開いて、QRコードの配布処理を書いていました。

QRコードを送ろう

先ほどの SendGridMailProvider を使ってメール送信を行う処理を書きましたが、URL にアクセスできません。SendGrid はデフォルトでメール本文に記載されたURLをトラッキングする機能があったのです。ですが、DNS設定をしなければページにアクセスできなくなるだけのゴミ機能です。元々ワンタイムパスワードを送信することを想定していたので、この機能の存在を知りませんでした。

SendGrid | メール本文内のURLが勝手に置換されてしまいます。解除できますか?

無効化して再度テスト実行。問題なく送信されました。

全学生に送ろう

あとは、全学生に送るだけです。事前にQRコードの生成を終えて、

学籍番号 QRコード

という CSV データがありましたが、データ数として 1000 ほど。CSV のパーサーライブラリを使ったことはありましたが、使っている時間はありません。テキストとして開いて、文字列置換で

...
id,qr
...

というデータを

val data = mapOf(
  // ...
  "id" to "qr",
  // ...
)

に変えてあげればいいのです。それを直接ソースコードに書いて実行です。そして、問題なく、全員にメールが送られました。ですが、この時にはもう開会式が始まっていました。

負荷分散型サーバーの問題点

間に合った〜とほっとしていたのも束の間、「URLにアクセスできない」という連絡が大量に来ました。どうやら SendGrid は負荷分散をおこなっており、設定が反映されるまでに時間が必要だったのです。ですが、本来これで問題ありません。設定を変更してから数分で一括送信を行う方が悪いです。実際、昼に再送信を行ったところ問題なく送信されました。

まとめ

メール送信を行う方法として SendGrid はとても良いと思います。審査が少し面倒かもしれませんが、一度通れば無料枠でも1ヶ月あたり12,000通のメールを送ることができます。

  1. Simple Mail Transfer Protocol (over SSL/TLS)

0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?