AWS Lambda + SES + Node.js で、Connpass の新着情報を定期的にメールで取得する

※ AWS Lambda と SES の勉強がてら、作ってみました。[前記事の続き](https://qiita.com/TakeshiNickOsanai/items/46888a059fa8bae9eae0) となります。

IT系勉強会を探すときに、Connpass をよく使っています。

https://connpass.com/

Connpass では、所属するグループが新しい勉強会を登録すると、メールで通知を行ってくれます。

しかし、特定のキーワードや地域名での通知機能は存在しません。このため、自分が所属するグループ外で、面白そうな勉強会が開かれていても、情報を見過ごすことが何度かありました。

そこで、AWSを利用して

* 自分が興味あるキーワード、地域の勉強会の登録情報を定期的に確認
* 新しい勉強会が登録されたら、メールで通知する

ようにしてみました。

## やること

Connpass で、自分が興味あるキーワードの勉強会が新規登録・更新されたら、メールで通知する。キーワードは「AWS」「東京都」の2つを含むものとする。


## 仕様

AWS Lambda で、一日一回「AWS」「東京都」に関する勉強会の更新・登録情報を検索する.
新着情報があったら、その勉強会のURLを AWS SES に渡し、自分宛てにメールで送信する.


以下

* AWS SESの設定
* IAMロールの設定
* Lambda 関数の作成
* コードの記述
* テスト
* CloudWatch によるスケジュール設定

の順番で記述します。

## AWS SESの設定

SES (Simple Mail service) を利用するにはドメインもしくはメールアドレスを登録してベリファイを行う必要があります。今回はメールアドレスを登録することにしました。

2018年2月時点ではSESはアジア・パシフィック(東京)リージョンでは使えない。このため以下の3リージョンいずれかで設定する。

EU (アイルランド)
米国東部 (バージニア北部)
米国西部 (オレゴン)

今回はバージニア北部(us-east-1)で設定しました。リージョン情報は後で必要となるので控えておきます。

SESのコンソールから登録したいメールアドレスを入力します。

![lambda_01.png](https://qiita-image-store.s3.amazonaws.com/0/83767/11e5ef32-84ff-ea23-b5b9-d3eccf738abf.png)


登録したメールアドレス宛に、確認用のメールが届くので、URLをクリックすると、ベリファイが終了。登録したメールアドレスをFromとしてメールの送信が可能となります。

![lambda_03.png](https://qiita-image-store.s3.amazonaws.com/0/83767/7bc014c1-eee5-060d-2e61-315ac62ee193.png)



## IAMロールの設定

IAMロールの設定を行います。Lambda では 関数を登録する際に、任意の権限を持つロールを付与する必要があります。

Lambda関数の実行のため　「AWSLambdaExecute」、SESの操作のため「AmazonSESFullAccess」の
2つのポリシーをアタッチしたIAMロールを作成する。名前は「Lambda_test」としました。

![lambda_06.png](https://qiita-image-store.s3.amazonaws.com/0/83767/cc915d2d-f456-f32f-700f-331459b9c585.png)


## Lambda 関数の作成

AWSのコンソールからLambda を選び、新規関数を作成。

![lambda_07.png](https://qiita-image-store.s3.amazonaws.com/0/83767/9827bcfb-51e5-c321-4e27-d8b7e32ba418.png)


「一から作成」を選び、名前、ランタイム、ロールを設定する。ランタイムはNode.js 6.10、ロールは先ほど作成したロールを選択します。


## コードの記述

Node.js で、Connpass のAPIを検索=>SESでメール送信するまでのコードを登録します。

AWS Lambda でNode.jsのパッケージを利用する場合、予めzipファイルで圧縮して、Lambda 上に転送する必要があるそうです。

[Azure Functions で実装したときは、APIとの通信に「request」モジュールを利用しました](https://qiita.com/TakeshiNickOsanai/items/46888a059fa8bae9eae0#nodejs-%E3%81%AE%E3%82%B3%E3%83%BC%E3%83%89%E4%BD%9C%E6%88%90) が、パッケージをまとめてZIPを作成するのが少々手間に思えたため、Node.js の標準機能として利用可能な「https」モジュールを利用する形に書き換えました。


```javascript
exports.handler = (event, context) => {

  "use strict";

// APIとの通信のためhttpsモジュールを利用

  const https = require('https');

// API検索に利用する変数を定義 

  const DOMAIN = 'connpass.com';
  const PATH = '/api/v1/event/?keyword=aws&keyword=%E6%9D%B1%E4%BA%AC%E9%83%BD';

// AWS SESでメール送信するための各種変数を定義

  const mailTo = ['メールの送信先アドレス'];
  const mailFrom = 'SESで認証したメールアドレス';
  const AWS = require('aws-sdk');
  AWS.config = new AWS.Config();
  AWS.config.region = "us-east-1";
  const ses = new AWS.SES({
    apiVersion: '2010-12-01'
  });

// 通信結果を受け取る変数「response」と、メール文面を作るための変数「list」を定義

  let response = '';
  let list = '';

// 関数「ConnpassSearch」を利用して新着勉強会の情報を取得
// 過去24時間内にアップデートがあった勉強会のURLを変数に格納

  connpassSearch(DOMAIN, PATH).then(function(json) {
    const events = json.events;
    const dt = new Date();
    const yesterday = dt - 86400000;
    list += '「AWS」「東京都」に合致する勉強会の更新がありました。\n';
    for (let i in events) {
      let updated_at = new Date(events[i].updated_at);
      if (updated_at > yesterday) {
        list += events[i].event_url + "\n";
      }
    }

    // AWS SES へ更新・新着勉強会のURLを渡す

      postData(list);

  });


// 関数「ConnpassSearch」を定義
// Connpass のAPIを叩き、取得したデータをPromise で返す

  function connpassSearch(DOMAIN, PATH) {
    const options = {
      host: DOMAIN,
      path: PATH,
      method: 'get',
      headers: {
        'Content-Type': 'application/json',
        'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/59.0.3071.115 Safari/537.36',
      }
    };
    return new Promise((resolve, reject) => {
      const req = https.request(options, (res) => {
        res.on('data', (chunk) => {
          response += chunk;
        });
        res.on('end', (res) => {
          res = JSON.parse(response);
          resolve(res);
        });
      })
      req.on('error', (e) => {
        console.error(`problem with request: ${e.message}`);
      });
      req.end();
    });
  }


// AWS SESでメール送信を行う関数「postData」を定義

  function postData(list) {
    ses.sendEmail({
      Source: mailFrom,
      Destination: {
        ToAddresses: mailTo
      },
      Message: {
        Subject: {
          Data: 'Connpassで勉強会の更新がありました。'
        },
        Body: {
          Text: {
            Data: list,
          }
        }
      }
    }, function(err, data) {
      if (err) throw err
      console.log('Email sent:');
      console.log(data);
    });
  }
};



```

記述したコードを、Lambda のエディタ画面に貼り付けます。


実行時間を設定します。今回のコードは、REST APIの通信結果を待ってから処理を行うため、30秒に設定してみました。

![lambda_12.png](https://qiita-image-store.s3.amazonaws.com/0/83767/4823f005-97c4-207e-b0f6-7cdbc27463b7.png)


## テスト

コードの貼り付けと設定が終わったら、テストを行います。
今回のコードは何かデータを受け取ってから処理を行うわけではなく、コード単体で完結してるため、初期状態で用意されている「Hello World」のテストデータをそのまま使います。

![lambda_13.png](https://qiita-image-store.s3.amazonaws.com/0/83767/e6a13704-c170-f1d3-f019-407b57e01271.png)


設定が終わったら、テストを実行。正常終了のログが出力され、データがメールで飛んできたらテスト完了です。


## CloudWatch によるスケジュール設定

最後に、CloudWatch を設定して、毎日朝8時にコードを起動するようにします。

![lambda_15.png](https://qiita-image-store.s3.amazonaws.com/0/83767/af8ae3b3-da65-8a16-2647-f993845fb8f3.png)



今回はcron式で記述しました。Lambda のCron式設定は、[このドキュメントに書いている](
https://docs.aws.amazon.com/ja_jp/AmazonCloudWatch/latest/events/ScheduledEvents.html
) ので、書式を確認しながら設定します。日本時間のAM8時は、UTCの23時となります。

![lambda_16.png](https://qiita-image-store.s3.amazonaws.com/0/83767/be7f299f-f3c5-4cb2-3a25-a96e8f2390d3.png)



定時実行の設定が終わったら終了です。


以後、毎日朝8時に、Connpassに新規登録・もしくは更新があった勉強会のURLがメールで送信されます。

![lambda_17.png](https://qiita-image-store.s3.amazonaws.com/0/83767/528538cc-fa37-6d44-59ba-2ee88cf6013e.png)



## 参考情報

以下のドキュメントを参考にしました。

https://dev.classmethod.jp/cloud/aws/lambda-to-ses/
https://qiita.com/ysti/items/9eeca4179aab5ea847b5
https://docs.aws.amazon.com/sdk-for-javascript/v2/developer-guide/ses-examples.html
https://nodejs.org/api/http.html#http_http_request_options_callback
https://qiita.com/n0bisuke/items/33a6a6da1d6f455105f9
https://qiita.com/yoshidasts/items/2d7117ef0f31754a06ab

Lambda 全般については この書籍がとても詳しく、参考になりました。

https://www.amazon.co.jp/AWS-Lambda%E5%AE%9F%E8%B7%B5%E3%82%AC%E3%82%A4%E3%83%89-%E5%A4%A7%E6%BE%A4-%E6%96%87%E5%AD%9D-ebook/dp/B0764C5MT5

#ちなみに

[Movable Type](https://www.sixapart.jp/movabletype/)  に関する Connpass のエンドポイントは

```
https://connpass.com/api/v1/event/?keyword=Movable%20Type
```

となります。(ダイマ)