Lambda+API Gateway+SlackでNotionにタスクを追加

# 今回やったこと
・SlackのスラッシュコマンドでDMからNotionにタスクを追加できる機能を作成した
・ワークスペースにいるほかの人が勝手にタスクを登録できないようにユーザーIDで制限を付けた

# Notionとは
Notionとはメモやプロジェクト管理,タスク管理のためのオールインワンワークスペースのこと。
個人利用であれば無料で利用することができる。

今回は下の画像のような形式のタスクを追加するものを作成する ~~(プリパラはいいぞ)~~
![task.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/269896/e0b90495-6f9a-b04b-41dd-ce6461b086c6.png)

# Notion側の設定
[[Notion] いま人気の Notion の API を試してみる](https://zenn.dev/utah/articles/da8239aca15335)
↑このページの流れで設定を行って、シークレットとデータベースIDをメモしておいてください。
データベースIDはhttps://www.notion.so/XXXXX?v=YYYYYYYY の中のXXXXXの部分です。

# 構成
超簡単構成
![notion-api.drawio (2).png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/269896/8f8f5799-2975-e1b9-7fb4-f32b5bf30a69.png)

# Lambda関数の作成
まずはSlackbotから投げられるリクエストを処理する関数を作っていきます。

## 環境変数の設定
API_KEY → Notion APIのシークレット
DATABASE_ID → NotionのデータベースID
SLACK_TOKEN → Slack APIのOAuthトークン(後で追加)
CHANNEL → BotとのDMのチャンネルID
USER_ID → SlackのユーザーID (完成前に一度リクエストを送ってログでユーザーIDを取得しておく)

## import
```notion-slash.py
import json
import datetime
import sys
import urllib.parse
import os

import requests
import base64
```

## ハンドラ
```py
def get_request_url(end_point):
    return f'https://api.notion.com/v1/{end_point}'

def handler(event, context):
    data = event_interpretation(event) #イベント解釈へ
    
    #環境変数から情報を取得
    notion_api_key = os.environ.get("API_KEY")　#Notion APIのシークレット
    database_id = os.environ.get("DATABASE_ID") #NotionのデータベースID
    
    headers = {"Authorization": f"Bearer {notion_api_key}",
               "Content-Type": "application/json",
               "Notion-Version": "2021-05-13"}
    
    #タイトル,ステータス,デッドライン,優先度を取得(ここは送る先の設定に準拠)
    property_name = {"title":[{"text":{"content":data["title"]}}]}
    
    status = {"select": {"name": data["status"]}}
    
    dead_line = {"date":{"start": datetime.date(data["dead_line"][0],data["dead_line"][1],data["dead_line"][2]).isoformat()}}
    
    task_priority = {"rich_text":[{"text":{"content":data["priority"]}}]}
    
    body = {
        "parent": {
            "database_id": database_id},
        "properties": {
            "Name": property_name,
            "ステータス": status,
            "デッドライン": dead_line,
            "優先度": task_priority
        }}
    
    response = requests.post(get_request_url('pages'), headers=headers, data=json.dumps(body))

    #成功メッセージ送信
    message = f"タスクの追加が完了しました\nタイトル:{data['title']}\nステータス:{data['status']}\nデッドライン:{data['dead_line']}\n優先度:{data['priority']}"
    response_slack(message)

    print(response.json())
    
    return
```
## イベント解釈
イベントとして送られてくるjsonがそのままでは扱えないのでいろいろと変換＆
不正なリクエストをここではじいている
```py

def event_interpretation(event):
    #event["body"] = "タイトル,対応中,20221122,高"
    
    body_dict = {}
    
    split_body = base64.b64decode(event["body"]).decode().split("&")
    
    for status in split_body:
        status = status.split("=")
        body_dict[status[0]] = status[1]
        
    propaty_tmp = urllib.parse.unquote(body_dict["text"]).split("+")

    #不正なリクエストをチェック
    propaty_tmp = "fraud request" if body_dict["user_id"] != os.environ.get("USER_ID") else propaty_tmp
    
    #データの正当性チェック
    if propaty_tmp == "fraud request":
        message = "不正なリクエストです"
        response_slack(message)
        sys.exit()
    elif len(propaty_tmp) != 4:
        message = "送信形式が正しくありません ex:タイトル,対応中,20221122,高"
        response_slack(message)
        sys.exit()
    elif len(propaty_tmp[2]) != 8:
        message = "日付の形式が正しくありません ex:19991122"
        response_slack(message)
        sys.exit()
        
    date = [int(propaty_tmp[2][0:4]),int(propaty_tmp[2][4:6]),int(propaty_tmp[2][6:8])]
    propaty = {"title":propaty_tmp[0],"status":propaty_tmp[1],"dead_line":date,"priority":propaty_tmp[3]}
    
    return propaty
```
## メッセージ返答
BotとのDMに対してメッセージを返す関数
```py
def response_slack(message):
    TOKEN = os.environ.get("SLACK_TOKEN") #Slack APIのOAuthトークン
    CHANNEL = os.environ.get("CHANNEL") #BotとのDMのチャンネルID
    
    url = "https://slack.com/api/chat.postMessage"
    headers = {"Authorization": "Bearer "+TOKEN}
    
    data  = {
        'channel': CHANNEL,
        'text': message
    }
    r = requests.post(url, headers=headers, data=data)
    print("return ", r.json())
    
```

# API Gatewayと紐づけ
解説は省略
発行されたエンドポイントURLをコピーしておく(形式: https://hogehoge.excute-api.apnortheast-1.amazonaws.com/default/[関数名])

# SlackAPIの設定
1. https://api.slack.com/ にアクセス
1. 右上のYour Appsを選択しログインしていない場合はログインする.
1. Create New AppでFrom scratchを選択
1. App Nameとワークスペースを選択
1. 左のメニューからSlash Commandsを選択＆作成(Request URLはさっきのAPI Gatewayエンドポイント)
1. 左のメニューのOAuth＆Permissionsから必要なスコープを追加する(今回はim:write,chat:writeあたりを追加)
1. ワークスペースにアプリをインストール

# コマンド実行
(実行前にSlack APIのOAuthトークンをLambdaの環境変数に追加)

自分のDMで先ほど設定したスラッシュコマンドを実行すると,botがリアクションをくれてタスクが作成できた!!(はず・・・)
※処理に三秒以上かかるとdispatch_faildが出ますが処理は続いています

# まとめ
今回は、ちょうどNotionのタスク追加手軽にしたいなと思っていたのでSlack Botの勉強ついでに作成してみました。
今回は影響範囲が自分のNotionだけだったので省略しましたが、SHA256を利用しての認証やdispatch_failedが帰ってきてしまう問題があるので時間があるときにもう少し拡張しようと考えています。