[PYTHON] Create an app that notifies LINE of the weather every morning

Introduction

I have a 2-year-old child, so I watch E-Tele every morning, but the weather doesn't come out on E-Tele. I'm switching channels back and forth to news programs just to see the weather, so I'm going to make a simple app that gets the weather from the weather API and pushes it to LINE. It's a year-end killing time Of course within the free range

Constitution

 2019-12-26 14.41.32.png

I've never used GitHub Actions, so I'm trying it out However, since I only use the cron function, I think that the GAS trigger function is easier and better if this is all.

Create a LINE Developer account and register your app

Created from here https://developers.line.biz/ja/services/messaging-api

Also, since there is a QR code of the application registered on the management screen of LINE Developer, register it as a friend.

Create a weather API account

Let me use the OpenWeatherMap API

Create an account here https://home.openweathermap.org/users/sign_up

Code to get the weather and push it to LINE

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']}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()

When you execute the above code, the app you registered earlier will be notified as follows.

 2019-12-25 15.30.35.png

Since there are many processes that process messages, it is rather long if you look only at the code, but if you only process the weather information acquisition and push to LINE, it will be as follows. I'm using python-dotenv to set secure information such as tokens in environment variables

Get weather information (JSON format) from OpenWeatherMap API


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()
    ...

This time, I just wanted to know the weather only where I live, so I passed the ID of the area where I live directly to the parameter ʻid. You can check the region ID by downloading city.list.json.gz from [here](http://bulk.openweathermap.org/sample/). In addition to ʻid, it is also possible to specify lat (latitude), lon (longitude), and zip (zip code) as parameters to get the result (this app is compatible Is not) You can check the API_KEY from here

Push a message to LINE


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 can be issued from the management screen of LINE Developer You can also check LINE_USER_ID from the same management screen.

How to push a message to multiple people (Added on 2019/12/28)

At the time of writing this article, I thought that everyone who registered the created weather app as a friend would be notified of the message, but with the above method, only I will be notified of the message. (I'm sorry, I made it without checking that area at all.)

Write a procedure to send to people other than yourself To send to someone other than yourself, you only need to know the UserID of the sender, but it is a little troublesome to know.

When a person who wants to know the UserID registers an app as a friend, Line sends a callback function prepared by us with a Post request and confirms it in that function. I think there are various ways, but I will write the way I did below

Prepare a callback function

Made with GAS

var SLACK_ACCESS_TOKEN = ''

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

You can also check it with console.log (e), but since it is troublesome to open GAS and check each time, I notify Slack. ʻE` contains information such as UserID

Get the webhook URL

You can publish as Web API from Publish-> Introduce as Web Application from the GAS screen. At this time, the disclosure range should be everyone (including anonymous) Make a note of the URL of the app as it will appear on the screen when you publish it.

Webhook URL settings

You can set it on the LINE Developer management screen as follows.

 2019-12-28 23.14.45.png It's okay if you run Verify and get SUCCESS

By the way, please also set the following to Disabled on the same screen

 2019-12-28 23.16.18.png

Operation check

If you send a character on the talk screen of the created Line app and the character and information such as your UserID reach Slack, it is successful. Also, if someone else registers your app as a friend, Slack will be notified so you can get that person's UserID.

Changed code to push from Python to LINE

#Change before
line_bot.push_message(
    os.getenv("LINE_USER_ID"), TextSendMessage(text="\n".join(texts))
)

#After change
line_bot.multicast(
    os.getenv("LINE_USER_ID").split(","), TextSendMessage(text="\n".join(texts))
)

Set UserID for LINE_USER_ID separated by commas.

Run regularly

Use the cron feature of GitHub Actions to set the weather to notify you every morning at 7am Below is the yaml file that Actions loads You can use GitHub Actions just by putting the following files in the directory .github / workflows on the root directory.

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

I'm using environment variables in my Python code, so I need to make them available on the Actions server. You can set environment variables that can be used in Actions from the Settings tab-> Secrets in the GitHub repository. And as mentioned above, we use the secrets context to allow runners to use environment variables.

After using it, I was a little worried that the workflow is executed 5 to 6 minutes later than the time specified by cron.

at the end

My wife also uses LINE on a regular basis, so it seems that this app will be used. (When I used the memo app that can be operated on Slack before, it was troublesome to start Slack and I stopped using it)

The code implemented this time is given below https://github.com/kurosame/bots-python

Recommended Posts

Create an app that notifies LINE of the weather every morning
LINE Bot that notifies you of the stocks of interest
I built an application with Lambda that notifies LINE of "likes" using the Qiita API
Create an app that guesses students with python
With LINEBot, I made an app that informs me of the "bus time"
Create an app that works well with people's reports using the COTOHA API
How to create an article from the command line
Create an app that guesses students with python-GUI version
Create a shape on the trajectory of an object
Let's create an app that authenticates with OIDC with Azure AD
I made a Line bot that guesses the gender and age of a person from an image
I made a slack bot that notifies me of the temperature
(Python) I made an app from Trello that periodically notifies slack of tasks that are about to expire.
An extension of Kivy's painting app
Heroku deployment of the first Django app that beginners are addicted to
How to create a wrapper that preserves the signature of the function to wrap
[Python] I created an app that automatically downloads the audio file of each word used for the English study app.
Create a simple app that incorporates the Fetch API of Ajax requests in Flask and explain it quickly
I created a Slack bot that confirms and notifies AWS Lambda of the expiration date of an SSL certificate