LoginSignup
6
10

More than 3 years have passed since last update.

毎朝 LINE に天気を通知するアプリを作成

Last updated at Posted at 2019-12-26

はじめに

2 歳の子がいるので、毎朝 E テレを観てるのですが、E テレでは天気が出ません
天気を確認するためだけにニュース番組にチャンネルを変えたり戻したりしてるので、天気 API から天気を取得して LINE に Push する簡単なアプリを作ろうと思います
年末の暇つぶしです
もちろん無料の範囲内で

構成

 2019-12-26 14.41.32.png

GitHub Actions を使ったことがなかったので、試しに使ってます
ただ、cron 機能しか使ってないので、これだけであれば GAS のトリガー機能とかの方が簡単で良いと思います

LINE の Developer アカウントの作成及び、アプリの登録

こちらから作成
https://developers.line.biz/ja/services/messaging-api

また、LINE Developer の管理画面に登録したアプリの QR コードがあるので、お友達登録しておく

天気 API のアカウント作成

OpenWeatherMap の API を使わせてもらいます

こちらからアカウント作成
https://home.openweathermap.org/users/sign_up

天気を取得して、LINE に Push するコード

import os
import json
import pandas as pd
from dotenv import load_dotenv
from urllib.request import urlopen
from datetime import datetime
from pytz import timezone
from linebot import LineBotApi
from linebot.models import TextSendMessage


def get_weather_icon(icon_str):
    if icon_str == "01d" or icon_str == "01n":
        return "☀️"
    elif (
        icon_str == "02d"
        or icon_str == "02n"
        or icon_str == "03d"
        or icon_str == "03n"
        or icon_str == "04d"
        or icon_str == "04n"
    ):
        return "☁️"
    elif (
        icon_str == "09d" or icon_str == "09n" or icon_str == "10d" or icon_str == "10n"
    ):
        return "☂️"
    elif icon_str == "11d" or icon_str == "11n":
        return "⚡️"
    elif icon_str == "13d" or icon_str == "13n":
        return "☃️"
    else:
        return ""


def send_to_line(df):
    texts = []
    for k, v in df:
        texts.append(f"【{k}】")
        for _, d in v.iterrows():
            texts.append(
                f"{d['time']}{get_weather_icon(d['icon'])} {d['temp']}(℃) {d['rain']}(mm/3h)"
            )
        texts.append("")

    line_bot = LineBotApi(os.getenv("LINE_ACCESS_TOKEN"))
    line_bot.push_message(
        os.getenv("LINE_USER_ID"), TextSendMessage(text="\n".join(texts))
    )


def main():
    url = "http://api.openweathermap.org/data/2.5/forecast"
    id = os.getenv("OWM_PLACE_ID")
    api_key = os.getenv("OWM_API_KEY")

    res = urlopen(f"{url}?id={id}&appid={api_key}&lang=ja&units=metric").read()
    res_json = json.loads(res)

    arr_rj = []
    for rj in res_json["list"]:
        conv_rj = {}
        timestamp = timezone("Asia/Tokyo").localize(datetime.fromtimestamp(rj["dt"]))
        conv_rj["date"] = timestamp.strftime("%m/%d %a")
        conv_rj["time"] = timestamp.strftime("%H")
        conv_rj["description"] = rj["weather"][0]["description"]
        conv_rj["icon"] = rj["weather"][0]["icon"]
        conv_rj["temp"] = round(rj["main"]["temp"])
        conv_rj["rain"] = round(rj["rain"]["3h"], 1) if "rain" in rj else 0
        arr_rj.append(conv_rj)

    send_to_line(pd.DataFrame(arr_rj).groupby("date"))


load_dotenv()
main()

上記のコードを実行すると、先程登録したアプリへ以下のように通知されます

 2019-12-25 15.30.35.png

メッセージを加工している処理が多いので、コードだけ見るとそこそこ長いのですが、天気情報取得と LINE に Push する処理だけにすると以下になります
python-dotenvを使って、トークンなどのセキュアな情報は環境変数に設定しています

OpenWeatherMapAPIから天気情報(JSON形式)を取得
def main():
    url = "http://api.openweathermap.org/data/2.5/forecast"
    id = os.getenv("OWM_PLACE_ID")
    api_key = os.getenv("OWM_API_KEY")

    res = urlopen(f"{url}?id={id}&appid={api_key}&lang=ja&units=metric").read()
    ...

今回は自分の住んでいる所だけの天気が分かれば良かったので、パラメータのidに直接住んでいる地域の ID を渡しています
地域 ID はこちらからcity.list.json.gzをダウンロードして確認できます
id以外にも lat(緯度)、lon(経度)、zip(郵便番号)をパラメータに指定して結果を取得することも可能です(今回作ったアプリは対応していません)
API_KEY はこちらから確認できます

LINEにメッセージをPush
def send_to_line(df):
    ...
    line_bot = LineBotApi(os.getenv("LINE_ACCESS_TOKEN"))
    line_bot.push_message(
        os.getenv("LINE_USER_ID"), TextSendMessage(text="Hello")

LINE_ACCESS_TOKEN は LINE Developer の管理画面から発行できます
LINE_USER_ID も同管理画面から確認できます

複数人にメッセージを Push するやり方(2019/12/28 追記)

この記事を書いた時点では、作成した天気アプリをお友達登録した人全員にメッセージが通知されるって思い込んでたんですけど、上記のやり方だと自分にしかメッセージが通知されません
(すみません、その辺全然調べずに作ってました。。)

自分以外の人にも送る手順を書きます
自分以外の人に送るには送る人の UserID が分かれば良いのですが、知る方法が少し面倒です

UserID を知りたい人がアプリをお友達登録した時に Line からこちらが用意したコールバック関数に Post リクエストで送ってもらって、その関数の中で確認するという方法を取ります
色々やり方はあると思いますが、以下に自分がやったやり方を書きます

コールバック関数を用意

GAS で作りました

var SLACK_ACCESS_TOKEN = ''

function doPost(e) {
  SlackApp.create(SLACK_ACCESS_TOKEN).postMessage(
    '#event-line',
    JSON.stringify(e),
    { username: 'kurosame-gas' }
  )
}

console.log(e)でも確認できますが、その都度 GAS 開いて確認するのは面倒なので、Slack に通知しています
eに UserID 等の情報が入っています

Webhook URL の取得

GAS の画面から公開 -> ウェブ アプリケーションとして導入から Web API として公開できます
この時公開範囲は全員(匿名も含む)にしてください
公開するとアプリの URL が画面上に出るので、控えておいてください

Webhook URL の設定

以下のように LINE Developer の管理画面で設定できます

 2019-12-28 23.14.45.png
Verifyを実行してSUCCESSが出ればオッケーです

ついでに、同画面で以下も Disabled に設定しておいてください

 2019-12-28 23.16.18.png

動作確認

作成した Line アプリのトーク画面で文字を送って、その文字と自分の UserID 等の情報が Slack に届いていれば成功です
また、他者がアプリをお友達登録したら、Slack に通知されるので、その人の UserID をゲットできます

Python から LINE に Push するコードを変更

# 変更前
line_bot.push_message(
    os.getenv("LINE_USER_ID"), TextSendMessage(text="\n".join(texts))
)

# 変更後
line_bot.multicast(
    os.getenv("LINE_USER_ID").split(","), TextSendMessage(text="\n".join(texts))
)

LINE_USER_ID にはカンマ区切りで UserID を設定しておきます

定期的に実行する

GitHub Actions の cron 機能を使って、毎朝 7 時に天気が通知されるように設定します
以下は Actions で読み込む yaml ファイルです
以下のファイルをルートディレクトリ上で.github/workflowsというディレクトリに入れておくだけで GitHub Actions が使えます

name: Weather

on:
  schedule:
    - cron: '00 22 * * *' # UTC

jobs:
  weather:
    name: Run weather
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@master
      - name: Setup Python
        uses: actions/setup-python@v1
        with:
          python-version: 3.7
      - name: Install Pipenv
        uses: dschep/install-pipenv-action@v1
      - name: Install dependencies
        run: pipenv sync
      - name: Run weather
        env:
          OWM_PLACE_ID: ${{ secrets.OWM_PLACE_ID }}
          OWM_API_KEY: ${{ secrets.OWM_API_KEY }}
          LINE_ACCESS_TOKEN: ${{ secrets.LINE_ACCESS_TOKEN }}
          LINE_USER_ID: ${{ secrets.LINE_USER_ID }}
        run: pipenv run python3 ./bots/weather.py

Python コードの中で環境変数を使っているので、Actions のサーバー上で環境変数を使えるようにしてあげる必要があります
GitHub リポジトリのSettingsタブ -> Secretsから Actions で使える環境変数を設定できます
そして上記のように secrets コンテキストを利用してランナーが環境変数を使えるようにしています

使ってみて少し気になったのが、Workflow が実行されるタイミングが cron で指定した時間より 5 分〜6 分くらい遅いですね

さいごに

LINE は妻も普段使いしているので、このアプリは使ってもらえそうです
(前に Slack 上で操作できるメモアプリを使った時は Slack を起動するのが手間になって使わなくなった)

今回実装したコードは以下にあげてます
https://github.com/kurosame/bots-python

6
10
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
6
10