ゴミ出し日の前日にLINEで通知してくれるアプリを作る(Node.js / AWS Lambda)

# ゴミ出し日の前日にLINEで通知してくれるアプリを作る

## なぜ作るのか
１ヶ月半前でしょうか、新しく引っ越したマンションの<strong>ゴミの分別が厳しい。</strong>
覚えるのもめんどくさいし、怒られるのも嫌なので、<strong>前日に通知</strong>してくれるやつ作ってしまおう。
というのがきっかけ。

<a href="https://github.com/so99ynoodles/my-trash-notifier">レポジトリ</a>

## 事前準備

### LINE Developers (Messaging API)

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/375274/ad2ab091-0fca-c7ba-3eed-4d8145777e96.png)


<a href="https://developers.line.biz/ja/">LINE Developers</a>でLINEアカウントでログインします。
<strong>[新規プロバイダー作成]</strong> - <strong>[新規チャネル作成]</strong> - <strong>[MessagingAPI]</strong>順で進み、
アプリの情報を書いていきましょう。


`メッセージ送受信設定`のところにある`アクセストークン`と、
`その他`にある`Your user ID`をメモしておきましょう。


### Amazon Web Service (AWS Lambda, Amazon CloudWatch)

<a href="https://aws.amazon.com/jp/">AWS</a>にログインし、
<a href="https://us-east-2.console.aws.amazon.com/lambda/">`Lambda`</a>で`関数の作成`を行います。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/375274/66a4ed85-e75d-8a33-f972-55c8bb3fa549.png)

これで事前準備は完了です。


## ローカルでの環境

Lambdaのコンソールで作成しても良いのですが、便宜上ローカルで開発を行います。

### npm
`npm i @line/bot-sdk moment-timezone`


### ゴミ出し日ロジックの実装

まず、曜日を扱うので、`moment-timezone`を使ってロジックを実装しました。


```utils/date.js
const moment = require('moment-timezone');

moment.tz('Asia/Tokyo').locale('ja');

const Monday = 1;
const Tuesday = 2;
const Wednesday = 3;
const Thursday = 4;
const Friday = 5;
const Saturday = 6;
const Sunday = 7;

// 今日
function getMoment(date = new Date()) {
  return moment(date)
    .tz('Asia/Tokyo')
    .locale('ja');
}

// 何番目の週か
function getWeekOfMonth(date) {
  return Math.ceil(date.date() / 7);
}

module.exports = {
  Monday: Monday,
  Tuesday: Tuesday,
  Wednesday: Wednesday,
  Thursday: Thursday,
  Friday: Friday,
  Saturday: Saturday,
  Sunday: Sunday,
  getMoment: getMoment,
  getWeekOfMonth: getWeekOfMonth,
};
```

私の住んでるところのゴミ出し日の情報を書いていきます。
`可燃ゴミは`水・土、`不燃ゴミ`は２週・４週目の火曜日、`資源ゴミ`は月曜日だったので、
以下のように書きました。

```index.js
const {
	getMoment,
	getWeekOfMonth,
	Monday,
	Tuesday,
	Wednesday,
	Saturday,
} = require('./utils/date');

const info = {
	burnable: {
		message: '明日は燃やすゴミの日です',
		days: [Wednesday, Saturday],
		weeks: [],
	},
	unburnable: {
		message: '明日は燃やさないゴミの日です',
		days: [Tuesday],
		weeks: [2, 4],
	},
	recyclable: {
		message: '明日は資源ゴミの日です',
		days: [Monday],
		weeks: [],
	},
};
...

```

そして、今日がゴミ出しの日かどうかを判断する関数を書けばOKです。

```index.js
...
// 曜日が合ってるか
const isDateDay = (date, days = []) => {
	return days.includes(date.day());
};

// 週が合ってるか
const isWeekDay = (date, weeks = []) => {
	const weekOfMonth = getWeekOfMonth(date);
	return weeks.length > 0 ? weeks.includes(weekOfMonth) : true;
};

// ゴミ出しの日か
const isTrashDay = (date, obj) => {
	return isDateDay(date, obj.days) && isWeekDay(date, obj.weeks);
};

// ゴミ出しの日だったら、メッセージを返す
const getMessage = date => {
	let message = undefined;
	Object.keys(info).forEach(key => {
		const obj = info[key];
		if (isTrashDay(date, obj)) message = obj.message;
	});
	return message;
};
...
```

### LINE Messaging APIの実装

`@line/bot-sdk`から`line`をインポート、
一番最初にメモしておいた`アクセストークン`を使います。
ですが、そのまま書くのではなく、Lambdaでも`env`設定ができるので、
`process.env.ACCESS_TOKEN`を渡しましょう。

```index.js
const line = require('@line/bot-sdk');
...

const client = new line.Client({
	channelAccessToken: process.env.ACCESS_TOKEN
});
```

最後にメイン処理の`handler`を実装します。
requestが来たら、Lambdaが`handler`が実行されます。
もし、ゴミ出し日でなかったらメッセージは送られないように作っていきます。

```index.js
exports.handler = async event => {
    //明日がゴミ出し日かどうか
    const tomorrow = getMoment().add(1, 'days');
    const message = getMessage(tomorrow);

	if (message) {
		const postMessage = {
			type:'text',
	        text: message,
		}
		try {
			await client.pushMessage(process.env.USER_ID, postMessage);
		} catch (error) {
			console.log(error)
		}
		
		return {
			statusCode: 200,
			body: JSON.stringify(postMessage)
		};
	}
	
	return {
		statusCode: 200
	}
};

```

`client.pushMessage`メソッドに`USER_ID`と、`postMessage`を渡してあげることで完成です。


## Lambdaの設定
今までローカルで作業してたフォルダーを圧縮し、`.zip`ファイルにします。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/375274/74535d2a-1e7d-bcf3-1798-6e823f387351.png)

書いたコードをアップロードできます。
そして、環境変数に`ACCESS_TOKEN`と`USER_ID`を設定しましょう。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/375274/2b1b1e34-3ba0-f42d-4cae-2dba4808b32d.png)


## CloudWatchで毎晩10時にeventを実行する

`トリガーを追加`で`CloudWatch Events`を選び、
`新しいルール`を追加します。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/375274/a9d4d85e-e46c-4b8d-c643-84b28ded3ec1.png)

私はスケジュール式で`cron式`を書きました。(時間はUTCで、日本の時間と違うので注意）
`cron(0 13 * * ? *)`

夜10:00に通知を送ってくれるようになりました。


## 最後に
<img width="414" alt="image.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/375274/f1c52b93-5ada-cd27-ded4-6fae15e823a3.png">

> <strong>いや、分別めんどくさいわ！</strong>