Incoming Webhooksを使ってSlackに新型コロナウイルスの状況を告げるアプリを作ってみた

Incoming Webhooksは他のアプリからSlackにメッセージを投稿するためのシンプルな方法です。この記事ではAWS LambdaからIncoming Webhooksアプリにメッセージを送信し、Slackに投稿する方法について説明します。
2021.07.13

こんにちは、ジョン・ヒョンジェです。

皆さんは新型コロナウイルスの発生状況を検索してみたりしませんか?私は毎朝、地域別にどのぐらい感染者が発生したか検索しますが、毎回検索するのはちょっと面倒だなと思いました。それで、感染者の発生状況を自動にSlackに告げてくれるアプリを作ってみました!!今回はその話をしていきたいと思います。

はじめに

構築するアーキテクチャです。作業の流れは以下のようになります。

  • Incoming Webhooksアプリを作成する
  • 新型コロナウイルス発生状況のデータを提供するOpen APIを用意する
  • AWS Lambda作成(データの取得と加工、Incoming Webhooksにデータ送信)
  • CloudWatch Events(time-based)でLambdaの実行を自動化させる

では、早速始めましょう!!

作ってみよう

Incoming Webhooksアプリを作成

Incoming Webhooksアプリは以下のリンクに接続してとても簡単に作成し、Slackのチャンネルに追加することができます。

アプリを追加するときに出るWebhook URLはアプリにデータを送信するとき使いますので、どこかへ記録しておいてください。

私は「COVID-19感染状況を告げるオールマイト」という名前のアプリを作成しました。(最近ハマっているキャラクターです)

Open APIを用意

私は韓国の政府が運営している公共データポータルサイト新型コロナウイルス発生状況のOpen APIを使いました。Open APIの活用申請をすると、認証キーが提供され、使える各種のパラメーターの説明が見れます。

AWS Lambda作成

では、Incoming WebhooksとOpen APIが用意されましたので、以下のようにAWS Lambda関数を作成します。(Pythonで作成しました。)

import boto3
import json
import requests
import xmltodict
import urllib.parse
import datetime

def alert(event, context):

    # 時間設定
    today_time = datetime.date.today()
    yesterday_time = today_time - datetime.timedelta(days = 1)

    ## parse_data関数を呼び出す
    result_today_datas = parse_data(today_time.strftime("%Y%m%d"))
    result_yesterday_datas = parse_data(yesterday_time.strftime("%Y%m%d"))

    # S3にファイルをアップロード。(省略してもOK。ただのデータバックアップのため)
    s3 = boto3.resource('s3')
    bucket_name = "corona-log"
    file_name = "%s.json" % (today_time.strftime("%Y%m%d"))    
    obj_name = s3.Object(bucket_name, file_name)    
    obj_name.put(Body=json.dumps(result_today_datas))

    message_text = ""   #Incoming Webhooksに送信するテキスト

    for i in range(0, len(result_today_datas)): #データの加工
        region = result_today_datas[i]["region"]
        today_confirmed = result_today_datas[i]["confirmed"]
        inc_confirmed = int(result_today_datas[i]["confirmed"]) - int(result_yesterday_datas[i]["confirmed"])

        add_message = "*%s : %s名* (前日と比べ%s" % (region, today_confirmed, abs(inc_confirmed)) + ("名増加)" if inc_confirmed >=0 else "名減少)")
        if region == "Total":
            line = "========================================="
            message_text = (line + "\n").join([message_text, add_message])
        else:
            message_text = "\n".join([message_text, add_message])

    # Incoming Webhooksの設定  
    url = "<Incoming WebhooksのURL>"
    payload = {
        "attachments": [{
            "pretext" : "私が韓国の新型コロナウイルス感染者発生状況を告げに来た!! (%s)" % str(today_time),
            "color" : "#0099A6",
            "text" : message_text
        }]
    }
    headers = {
        "Content-Type" : "application/json; charset=UTF-8"
    }

    # Incoming Webhooksにデータ送信
    response = requests.post(url, data=json.dumps(payload), headers = headers)

    return response


def parse_Data(time):

    # Open APIのリクエストURL作成
    api_url = "http://openapi.data.go.kr/openapi/service/rest/Covid19/getCovid19SidoInfStateJson"
    queryParams = {
        "ServiceKey" :  "<Open APIの認証キー>",
        "startCreateDt" : time,
        "endCreateDt" : time
        }
    query_string = urllib.parse.urlencode(queryParams)
    url = api_url + "?" + query_string

    # 分析しやすくするため、JSON形式に変換
    raw_data_result = requests.get(url).text
    dict_data = xmltodict.parse(raw_data_result)
    json_datas = json.loads(json.dumps(dict_data))

    result_datas = []

    for json_data in json_datas["response"]["body"]["items"]["item"]:

        data = {}
        data["region"] = json_data["gubunEn"]   #地域
        data["confirmed"] = json_data["incDec"] #感染者数

        result_datas.append(data)

    return result_datas

parse_Data関数ではOpen APIからデータを取得して、alert関数ではそのデータを加工し、Incoming Webhooksに送信します。(S3にファイルをアップロードしていますが、省略しても構いません)

実際にIncoming Webhooksにデータを送信する部分は以下のコードになります。

# Incoming Webhooksの設定  
    url = "<Incoming WebhooksのURL>"
    payload = {
        "attachments": [{
            "pretext" : "私が韓国の新型コロナウイルス感染者発生状況を告げに来た!! (%s)" % str(today_time),
            "color" : "#0099A6",
            "text" : message_text
        }]
    }
    headers = {
        "Content-Type" : "application/json; charset=UTF-8"
    }

    # Incoming Webhooksにデータ送信
    response = requests.post(url, data=json.dumps(payload), headers = headers)

Incoming Webhooksアプリにデータを送信する方法は二つがあります。

  • POSTリクエストでJSONデータをpayloadパラメーターとして送信
  • JSONデータをPOSTリクエストのbodyとして送信する

今回はpayloadパラメーターとして送信します。

payloadにtextプロパティだけ使ってデータを送信することもできますが、attachmentsプロパティを使うともっと多くの設定ができます。設定できるプロパティは以下のリンクを参考してください。

CloudWatch Events設定

韓国時間で毎日12時にLambdaを実行させるために、CloudWatch Eventsトリガを追加します。

cron表現はUTC時間帯を使いますので、韓国時間から9時間差し引いて設定します。

確認

毎日12時Slackに新型コロナウイルスの発生状況を告げてくれるアプリが完成しました!

感想

補足ですが、payloadパラメーターにtextプロパティを使いましたが、fieldsプロパティを使って

fields = [{"title" : xxx, "desc" : xxx}, {"title" : xxx, "desc" : xxx}]
payload = {
    "attachments" : [{
        "fields" : fields
    }]
}

このようにメッセージをJSONに設定することもできます。

では、今回の記事は以上です。Incoming Webhooksを使うととても簡単にアプリが作れますので試してみてください!