見出し画像

AWS + Python + LINEbotでゴミ出しリマインドをする【前編】

はじめに

はじめまして、N.Iです。
いつでもゴミ出し可能なマンションから曜日別のゴミ出しのマンションに引っ越し「ゴミの日覚えられん!」となったのでLINEbotを作成しました。

【前編】では、LINE Messaging API・AWS Lambda・Amazon EventBridgeを使って、ゴミの日の前日21時にリマインドが送られてくる機能を実装します。
【後編】では、LINE Messaging API・AWS Lambda・API Gatewayを使って、ユーザーが「燃えるゴミ」と送ったら「【収集日】毎週火曜日と金曜日」と返するような機能を実装します。

この記事を書いているALHには 20名を超えるAWS資格コンプリート者が在籍中!

使用技術

・Python3.9
・LINE Messaging API
・AWS Lambda
・Amazon EventBridge

簡単な機能の説明

1.EventBridgeが定期的にLambda関数を実行する
2.Lambda関数が実行され、現在日時がゴミの日の前日の場合はLINEbotからメッセージが届く

手順

LINE公式アカウントの作成とLINE Messaging APIの設定

こちらの記事を参考にしました。料金のことについても記載があります。
【LINE Messaging API】を使ってブロードキャストメッセージを送信する

今回は同居人にもメッセージを送りたかったので、上記記事と同じく、ブロードキャストメッセージ(作成したLINEアカウントと友だちになっているすべてのユーザーに送信するメッセージ)を送信します。
チャンネルアクセストークンは後程使いますので、発行の上控えておいてください。

Lambda関数作成

実行したいことは以下の3つです。
・現在日時を取得して、「第〇週の〇曜日」と変換する
・明日がゴミの日である場合、「明日は〇〇ゴミの日です」というメッセージを定義する
 (例)毎週火曜日が燃えるゴミの日の場合、今日が月曜日であれば、「明日は燃えるゴミの日です」というメッセージを定義する
・定義された送信メッセージを送信する

1つずつ分けて説明します。

① 現在日時を取得して、「第〇週の〇曜日」と変換する

from datetime import datetime, timedelta, timezone

# 「第n週」か取得
def get_nth_week(day):
  return (day - 1) // 7 + 1

# 「第n(週)X曜日」か取得
# datetime.weekdayは月曜が0、日曜が6で返す
def get_nth_dow(year,month,day):
  nth_week = get_nth_week(day)
  nth_day = datetime(year,month,day).weekday()
  return {"nth_week" : nth_week, "nth_day" : nth_day}

# 現在日時を取得する
JST = timezone(timedelta(hours=+9), 'JST')
current_at = datetime.now(JST)
year = current_at.year
month = current_at.month
day = current_at.day + 1   # 前日にLINEを送るため、+1をする
nth_week = get_nth_dow(year,month,day)["nth_week"]
nth_day = get_nth_dow(year,month,day)["nth_day"]

これで変数nth_weekに「第〇週」、変数nth_dayに「〇曜日」が代入されている状態になりました。
コードの書き方はこちらの記事を参考にしました。
Pythonで任意の日付がその月の第何週目・何曜日かを取得

② LINEbotからメッセージを送信するための設定をする

from linebot import (LineBotApi, WebhookHandler)
from linebot.models import (MessageEvent, TextMessage, TextSendMessage)
from linebot.exceptions import (LineBotApiError, InvalidSignatureError)

# LINEbotの送信先設定をする
def notify_lastnight(body):
  line_bot_api =  LineBotApi(channel_access_token = "控えたチャンネルアクセストークン")
  line_bot_api.broadcast(TextSendMessage(text = body))

今回はline-bot-sdk-pythonを使ってみました。
公式リファレンスはこちら

コードの書き方はこちらの記事を参考にしました。
Python、LINE BOTで一斉送信

③ 明日がゴミの日である場合、「明日は〇〇ゴミの日です」というメッセージを定義する
条件式がちょっと多いですし、居住地域モロバレになってしまうので、フェイクを入れて一部だけコードを載せます。

# ゴミ出しの曜日のパターンと送信メッセージの定義
  # 毎週火曜日と金曜日は燃えるゴミ
  if (nth_day == 1) or (nth_day == 4):
    body = "明日は燃えるゴミの日です。準備をしましょう!"
    notify_lastnight(body)

  # 第2・第4水曜日は空き缶・空きびん
  if ((nth_week == 2) and (nth_day == 2)) or ((nth_week == 4) and (nth_day == 2)):
    body = "明日は空き缶・空きびんの日です。準備をしましょう!"
    notify_lastnight(body)

①で取得した「第〇週」「〇曜日」という情報を条件式に組み込んで、現在日時に応じてメッセージを変えます。
条件式がtrueであった場合に②で作成した関数notify_lastnight()を呼び出しています。これによって、明日がゴミの日でない場合は、LINEメッセージを送らないようになります。

①~③のコードを組み合わせると、下記のようになります。

ine_bot_garbage.py

from datetime import datetime, timedelta, timezone
from linebot import (LineBotApi, WebhookHandler)
from linebot.models import (MessageEvent, TextMessage, TextSendMessage)
from linebot.exceptions import (LineBotApiError, InvalidSignatureError)

# 「第n週」か取得
def get_nth_week(day):
  return (day - 1) // 7 + 1

# 「第n(週)X曜日」か取得
# datetime.weekdayは月曜が0、日曜が6で返す
def get_nth_dow(year,month,day):
  nth_week = get_nth_week(day)
  nth_day = datetime(year,month,day).weekday()
  return {"nth_week" : nth_week, "nth_day" : nth_day}

# LINEbotの送信先設定をする
def notify_lastnight(body):
  line_bot_api =  LineBotApi(channel_access_token = "控えたチャンネルアクセストークン")
  line_bot_api.broadcast(TextSendMessage(text = body))

def lambda_handler(event, context):
  # 現在の日時を取得する
  JST = timezone(timedelta(hours=+9), 'JST')
  current_at = datetime.now(JST)
  year = current_at.year
  month = current_at.month
  day = current_at.day + 1   # 前日にLINEを送るため、+1をする
  nth_week = get_nth_dow(year,month,day)["nth_week"]
  nth_day = get_nth_dow(year,month,day)["nth_day"]

  # ゴミ出しの曜日のパターンと送信メッセージの定義
  # 毎週火曜日と金曜日は燃えるゴミ
  if (nth_day == 1) or (nth_day == 4):
    body = "明日は燃えるゴミの日です。準備をしましょう!"
    notify_lastnight(body)

  # 第2・第4水曜日は空き缶・空きびん
  if ((nth_week == 2) and (nth_day == 2)) or ((nth_week == 4) and (nth_day == 2)):
    body = "明日は空き缶・空きびんの日です。準備をしましょう!"
    notify_lastnight(body)

  return {
    "statusCode" : 200,
    "body" : "ok"
  }


④ Lambda Layersの設定をする

③までのステップでコードはすべて書けましたが、このままではline-bot-sdkは使えません。
line-bot-sdkのようにPythonの標準ライブラリでないものを使うには、Lambda Layersを利用します。
Lambda Layers用のパッケージを作る手順は以下のとおりです。

pythonフォルダを作成し、そのフォルダ下にline-bot-sdkをインストールする

 フォルダ名を「python」としなければ、Lambda関数がライブラリを参照できないので要注意です。
 AWS公式ドキュメントに詳しい説明があります。

作成したpythonフォルダをzip化する

 ツールで圧縮しても、コードで圧縮しても、どちらでも大丈夫です。
 作成したzipファイルの容量が10MBを超える場合はS3にアップロードしてください。

zipファイルをLambda Layersに登録する

① AWS Lambdaの「レイヤー」から「レイヤーの作成」を選択する


②「レイヤー設定」
 「名前」を設定し、「.zipファイルをアップロード」を選択してアップロードを行い、「互換性のあるアーキテクチャ」は「x86_64」を選択し、「互換性のあるランタイム」はLambda関数のランタイムと同じものを選択して、「作成」を押すとLambda Layerができます。


③ AWS Lambdaの「関数」から「Layers」を押す


④ 「レイヤーの追加」を押す


⑤ 「レイヤーを選択」
 「カスタムレイヤー」を選択し、「カスタムレイヤー」のプルダウンから先ほど作成したLambda Layerを選び、「追加」を押します。

これでline-bot-sdkが使えるようになります!

EventBridgeでLambda関数の定期実行を設定する

実行するLambda関数の設定はできたので、それを定期実行させる設定をします。Amazon EventBridgeを使うと簡単です。

① Amazon EventBridgeの画面を開き、「ルールを作成」を押す


② 「ルールの詳細を定義」
「名前」を設定し、「イベントパス」はデフォルトのまま、「ルールタイプ」は「スケジュール」を選択して、「次へ」を押します。


③ 「スケジュールを定義」
「特定の時刻に実行されるきめ細かいスケジュール」を選択し、Cron式を設定します。Cron式の書き方は公式ドキュメントを参考にしてください。
また、スケジュールはUSTで設定されるので、JSTに合わせるためには時差を考慮してください。
【AWS】Amazon EventBridgeのcron式の時差について
「以降10回のトリガー日」で「ローカルタイムゾーン」を選ぶと、日本時間でいつ実行されるかが出てきます。


④ 「ターゲットを選択」
「ターゲットタイプ」で「AWSのサービス」を選択し、「ターゲットを選択」で「Lambda関数」を選択し、「機能」で定期実行させたいLambda関数を選択し、「次へ」を押してください。


⑤ タグの必要があれば設定し、「レビューと作成」でルールの内容を確認した後「作成」を押す。


感想

LINEが生活に欠かせなくなっている人間なので、さくっと実装できる割に得られるメリットが大きく大満足です。
AWS LambdaとLINE Messaging APIの組み合わせは、小規模利用の場合だとコストもかからないですし、初心者の学習にちょうど良いなと感じました。
後編をなるべくはやく公開できるようにがんばります!


↓ ↓ ↓ ↓ ↓ ↓ ↓ ↓ 採用サイトはこちら ↓ ↓ ↓ ↓ ↓ ↓ ↓ ↓


↓ ↓ ↓ ↓ ↓ ↓ ↓ ↓ ALHについてはこちら ↓ ↓ ↓ ↓ ↓ ↓ ↓ ↓


↓ ↓ ↓ ↓ ↓ ↓ ↓ ↓ もっとALHについて知りたい? ↓ ↓ ↓ ↓ ↓ ↓ ↓ ↓





この記事が参加している募集