LINE NotifyとAWSを用いた定期自動送信の実装

# 1． はじめに
　LINE Notify APIを使ってメッセージを送信するPythonプログラムを作成しました．さらに，AWSを用いてそのプログラムを自動で定期実行できるようにしました．駆け出しエンジニアの私でも1日で0から作成することができたので，ぜひ参考にしてみてください．
 
 　プログラムはGitubにも公開しています．https://github.com/vivy-phtela/line_notify
 
# 2． 開発のきっかけ
　アルバイトリーダーの私はLINEグループに定期的にシフト提出のリマインドを送信する必要があるのですが，いつも忘れてしまいます...これをどうにか自動化できないかと考えていたところ，LINE Notifyを見つけました．

# 3． LINE Notifyでトークンを取得
　LINE NotifyとはLINE公式が提供しているAPIで，連携するとLINE Notifyという公式アカウントを介して通知を送ることができます．公式ドキュメントは[こちら．](https://notify-bot.line.me/doc/ja/)
### STEP1:[このページ](https://notify-bot.line.me/ja/)からログイン
<img width="420" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3561867/727339c3-018b-4cc5-0e7a-a87a946f0c28.png">

使用しているLINEアカウントでログインします．

<img width="200" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3561867/ce780213-d712-8f1f-d7b9-75c6564d032c.png">

### STEP2:マイページ→トークンを発行する

<img width="420" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3561867/64125356-6c7e-ad42-8938-6e2c56833349.png">

　トークン名と通知を送信するグループを選択します．トークン名は通知の一番上に毎回表示されるのでふさわしい名前にしましょう．

<img width="420" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3561867/80a58359-161f-a574-e9b1-ec00a9dbbf0c.png">

表示されるトークンを確実にコピーしておきます(後から確認することはできません)．

<img width="420" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3561867/95646175-22f4-3954-75dc-8b1147364a55.png">

# 4． Pythonコード
　AWSで実行する前にローカル環境でテストします．YOUR_TOKENの部分に先ほどコピーしたトークンをペーストします．ローカルはPython3.11を使用しています．
```python
import requests

# 取得したトークン
token = 'YOUR_TOKEN'
# APIのURL
api_url = 'https://notify-api.line.me/api/notify'
# 送信するメッセージ
messages = 'テストメッセージです．'

# 情報を格納
token_dic = {'Authorization': 'Bearer ' + token}
send_dic = {'message': messages}

# 送信
response = requests.post(api_url, headers=token_dic, data=send_dic)

# 送信結果を出力（200: 成功時、400: リクエストが不正、401: アクセストークンが無効）
print(response.status_code)
```
Pythonファイルがあるディレクトリで以下のコマンドで実行します．
```Bash
python3 line_notify_test.py
```
LINEを開いてメッセージが送信されていることを確認します．

<img width="400" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3561867/b7b5db69-3829-e884-a428-26e1eedd5ea8.jpeg">

送信されない場合はターミナル出力を確認しましょう．
`"400"と出力されている場合→プログラムが正しいか確認する．`
`"401"と出力されている場合→アクセストークンが間違ってないか確認する．`

# 5． プログラムの定期実行
　次に作成したプログラムが定期実行されるようにします．プログラムの定期実行はUNIX系OSに標準搭載されている"cron"というスケジューラを使うのが調べた感じ一般的でした．しかし，cronはパソコンがシャットダウンしているときは動いてくれないのであまり使い物にならないと思いました...
 
　そこで，クラウド(AWS)を使えば環境に依存せず定期実行ができると考えました．AWSは従量課金制で無料利用枠もあるので，個人で少し利用する分は無料で利用することができるのが非常に魅力的です．また，AWSを利用するにあたって，ユーザ登録が必要となります．本記事ではユーザ登録の過程は省略します．[こちら](https://aws.amazon.com/jp/)から登録できます．

# 6． AWS Lambda
　AWS Lambdaとは一言で言えば，"クラウド上に定義したプログラムを設定したイベントに応答して自動的に実行するAWSのサービスの一つ"です．
 
### 関数の作成
　関数名は任意の関数名，ランタイムは今回，ローカルと同じPython3.11を選択しました．アーキテクチャもローカルと同じarm64を選択しました．
 
<img width="900" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3561867/f858aad0-5dd1-5951-f069-0119512f589d.png">

### プログラムの追加
　次にプログラムを定義します．Lambdaでは関数を定義する必要があるので関数の形に先程作成したコードを書き換えます．
 
<img width="900" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3561867/a233c355-4a0a-3836-1821-24b2309bace4.png">

### レイヤーの追加
　作成したコードを見ると，Requestsモジュールを使用していることがわかります．ローカル環境でpip3 install ~を行うように，Lambdaではレイヤーにモジュールを追加する必要があります．ページ最下部にある"レイヤーの追加"からレイヤーの追加を行います．
 
<img width="900" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3561867/f5482fde-6d1a-7080-9df4-60499c081107.png">

　レイヤーの追加にはいくつか方法がありますが，ARNを指定するのが一番簡単でした．以下のコードを入力します．ARNは[こちらのページ](https://github.com/keithrozario/Klayers?tab=readme-ov-file)から選択しました．PythonのバージョンやリージョンによってARNは異なるようです．
```
arn:aws:lambda:ap-northeast-1:770693421928:layer:Klayers-p311-arm64-requests:6
```

<img width="" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3561867/b12e528e-4677-f696-1099-81ce1911a913.png">

### テスト
　最後にTestをクリックして実際にLINEでメッセージが送信されているか確認します．
 
<img width="900" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3561867/cb3e65fe-9005-dd6d-8b04-839fa7978fb2.png">


# 7． Amazon EventBridge
　Amazon EventBridgeとは，一言で言えば，"イベントを配信するAWSのサービスの一つ"です．EventBridgeを使って実行する日時を指定して，Lambdaで定義したプログラムを実行します．

### スケジュールの作成
　EventBridgeスケジュールを選択し，スケジュールを作成します．
 
 <img width="900" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3561867/664ba89c-8cb5-8620-6422-4b01ce480965.png">
 
 ### スケジュールパターンの設定
　定期的に実行を行うためにスケジュールを設定します．cron式でスケジュールを設定します．cron式は初見ではよくわからなかったので一度ネットで検索して概要を理解することをおすすめします．私は@da-sugiさんが書かれた記事[AWS_Cron式のワイルドカード](https://qiita.com/da-sugi/items/ef3bb45a8a99a4acacb1)が非常に参考になりました．
 
<img width="900" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3561867/d9e0260d-094c-f81a-2cd2-f7b0ea905901.png">

### ターゲットの選択
　ターゲットはLambdaのInvokeを選択します．選択すると関数を選べるようになるので，先ほど作成した関数を選択します．

<img width="700" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3561867/32b7343c-d365-95da-dd30-cb4346308935.png">

### 設定・確認
　次の設定画面は特にいじらないで良いと思います．"次へ"をクリックすると確認ページに進むので確認を行って"スケジュールを作成"をクリックします．これでEventBridgeの設定は終わりです．スケジュールページに作成したスケジュールが表示されていれば成功です．

# 8. 私が作成したプログラム
　私は冒頭で述べましたが，LINEグループに定期的にシフト提出のリマインドを送信するのがこの実装の目的だったので，以下のようなプログラムを作成しました．
 ```python
import requests
import calendar
from datetime import datetime

def lambda_handler(event, context):
    # 日と年と月を取得
    year = datetime.now().year
    month = datetime.now().month
    date = datetime.now().day
    
    # その月が何日まであるかを取得
    _, last_day = calendar.monthrange(year, month)
    
    # 取得したトークン
    token = 'YOUR_TOKEN'
    # APIのURL
    api_url = 'https://notify-api.line.me/api/notify'
    
    if date == 2:
        messages = f'\n皆様\nお疲れ様です！\n⚠️3日後({month}/5)が{month}/16-{last_day}までのシフト提出期限となっています！空欄のないように入力をお願いいたします🙏'
    elif date == 5:
        messages = f'\n皆様\nお疲れ様です！\n⚠️本日が{month}/16-{last_day}までのシフト提出期限となっています！まだ入力していない方は今すぐ提出しましょう🥺'
    elif date == 17:
        messages = f'\n皆様\nお疲れ様です！\n⚠️3日後({month}/20)が{month+1}/1-15までのシフト提出期限となっています！空欄のないように入力をお願いいたします🙏'
    elif date == 20:
        messages = f'\n皆様\nお疲れ様です！\n⚠️本日が{month+1}/1-15までのシフト提出期限となっています！まだ入力していない方は今すぐ提出しましょう🥺'
    else:
        messages = '予期せぬエラーが発生しました。'
    
    # 情報を格納
    headers = {'Authorization': 'Bearer ' + token}
    data = {'message': messages}
    
    # 送信
    response = requests.post(api_url, headers=headers, data=data)
    
    # 送信結果を出力（200: 成功時、400: リクエストが不正、401: アクセストークンが無効）
    return response.status_code
 ```
 　毎月5，20日の提出締め切り日とその3日前にこのプログラムを実行するようにEventBridgeを設定しました．また，実行時の年・月・日とその月が何日まであるかを取得して，メッセージに汎用性を持たせました．

# 9． おわりに
　毎日必ず使うLINEを使って実用的なものを作ることができたので嬉しいです．また，AWSを初めて使用しましたが，クラウドサービスは非常に便利だなと感じました．今回作ったものを応用すれば，いろいろな通知をLINEに送信することができそうです．