【Lambda×Kotlin×SendGrid】自分にメール配信する機能を作ってみた

#1 はじめ

何か自分の欲しい情報を定刻に毎日配信してくれるものはないかな～
そうだ作ろう、という感じでスタート

#構成
ざっくり
CloudWatch Eventsで定刻にLambdaを実行
Lambdaはどこからか情報を取得/編集、SendGridで結果をメール配信
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/309466/ace2867c-88ff-25c0-2414-f2f119926cab.png)

LambdaでKotlinを使用する方法は過去に書いたこの記事を参照
[KotlinでAWS Lambdaを書く！](https://qiita.com/P3117/items/43fb3c7e5183feb98c24)

SendGridはクラウドメール配信サービスです。
登録後審査があるのですぐ使用開始！、とはいかないのですがFreeプランもあり良いサービスです。
※私の場合は1営業日ほどで認可されました。
[SendGrid](https://sendgrid.kke.co.jp/)

各言語のSDKもあります。
https://github.com/sendgrid/sendgrid-java#usage


#何を配信しよう？？
とりあえずQiitaの日間トレンドを取得してみようか。
トレンドを取得するAPIはないようなので自分で取得します。

ライブラリにはjsoupとgsonを使用しています。
div[data-hyperapp-app=Trend]の中のattr("data-hyperapp-props")を取得すればトレンドが取ってこれますね。
以前Rubyで同じことをしてる方の記事を見て同じ方法を使わせていただきました。
※こんなやり方をしてるのは実際のページをF12ツールで解析すればあぁ。。とわかります。

```Qiita.kt
class Qiita : Source {
    override val connection = QiitaConnection()

    //記事へのURL
    //https://qiita.com/{{name}}/items/{{uid}}
    override val information_list: MutableList<String> = mutableListOf()

    override fun run(): MutableList<String> {
        val documents = connection.getDocumentData()
        this.information_list.add("------ Qiita Daliyトレンド記事 ------${LINE_SEPARATOR}")

        documents?.forEach {
            this.information_list.add("タイトル：${it.node?.title}" + LINE_SEPARATOR)
            this.information_list.add("URL    ：https://qiita.com/${it.node?.author?.urlName}/items/${it.node?.uuid}" + LINE_SEPARATOR)
        }

        this.information_list.add("-----------------------------------$LINE_SEPARATOR")
        return this.information_list
    }
}
```


```QiitaConnection.kt

//    const val QiitaDailyTrend = "https://qiita.com/"
class QiitaConnection : Connection(SourceList.QiitaDailyTrend) {

    override fun getDocumentData(): List<Edge>? {
        val element = super.html.select("div[data-hyperapp-app=Trend]")
                                .attr("data-hyperapp-props")

        val data = gson.fromJson(element, DailyTrend::class.java)
        return data.trend?.edges
    }

}
```

gsonのPOJOの生成には以下のサイトを使用
私はここで作ったソースをIDEなどでKotlinに変換して使いました。
http://www.jsonschema2pojo.org/


余談なのですが、Gradleはこんな感じです。
今回の使うものだけでなく色々役に立つもの詰めてるのでまた使います。
(Java11向けに改造したいですが。。。。)

``` build.gradle
plugins {
    id 'org.jetbrains.kotlin.jvm' version '1.3.50'
}

ext {
    kotlin_version = "1.3.50"
    spek_version = "2.0.8"
}

group 'kotlinDailyMail'
version '1.0-SNAPSHOT'

repositories {
    mavenCentral()
    jcenter()
}

dependencies {

    implementation group: 'org.jetbrains.kotlin', name: 'kotlin-stdlib-jdk8', version: kotlin_version
    compile 'com.sendgrid:sendgrid-java:4.4.1'
    compile group: 'com.amazonaws', name: 'aws-lambda-java-core', version: '1.2.0'

    //GSON
    compile 'com.google.code.gson:gson:2.8.6'

    //Fuel
    compile 'com.github.kittinunf.fuel:fuel:1.12.0'

    //JSoup
    compile 'org.jsoup:jsoup:1.12.1'


    //JUnit5,Spek2
    testImplementation group: 'org.jetbrains.kotlin', name: 'kotlin-reflect', version: kotlin_version
    testImplementation group: 'org.jetbrains.kotlin', name: 'kotlin-stdlib-jdk8', version: kotlin_version
    testImplementation group: 'org.junit.jupiter', name: 'junit-jupiter-api', version: '5.3.1'
    testImplementation group: 'org.spekframework.spek2', name: 'spek-dsl-jvm', version: spek_version
    testRuntimeOnly group: 'org.junit.jupiter', name: 'junit-jupiter-engine', version: '5.3.1'
    testRuntimeOnly group: 'org.spekframework.spek2', name: 'spek-runner-junit5', version: spek_version
    testImplementation "org.jetbrains.kotlin:kotlin-test"
    testImplementation "org.jetbrains.kotlin:kotlin-test-junit"

    //MockK
    testImplementation "io.mockk:mockk:1.9.3"

}

test {
    useJUnitPlatform {
        includeEngines 'spek2'
    }
}

compileKotlin {
    kotlinOptions.jvmTarget = "1.8"
}
compileTestKotlin {
    kotlinOptions.jvmTarget = "1.8"
}

task buildZip(type: Zip) {
    from compileKotlin
    from processResources
    into("lib") {
        from configurations.runtime
    }
}
build.dependsOn buildZip
```

一通りコーディングが済んだらLambda関数を作成
環境変数はTZをとりあえず修正
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/309466/d3532d6a-eef6-a9ae-d521-2ca24ec76e06.png)

CloudWatch Eventsでスケジュール式を設定
Lambdaは基本UTCなので時刻を逆算して起動時間を指定する(下記はAM 8:00 に1回起動)
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/309466/4a455239-23b7-b73a-e939-60d844eee9dd.png)

試しにテスト起動
プログラムで指定したアドレスにメールが届きました。
SendGrid側で証明書の設定とかをしていないと迷惑メールに入ってるかもです。

![キャプチャ.PNG](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/309466/aae98b05-7a5f-3c13-7043-b3f32b64419f.png)

#続き
軽い気持ちでQiitaのトレンドを試しに配信するか。
と思ったら結構大変で最適な方法を見つけるのに時間かかりました。（笑）

次は情報の取得先をより増やしたり、一度に取得する情報量を調整しようと思います。

他にも開発にSpek2とか導入していたのでそちらについても記事を書こうか考え中

ちなみにこの配信機能ですが、1円も開発には使ってないです。
日々の定時実行もLambdaの無料枠に余裕で収まります。
※実装中のおやつ代は別