chatwork-webhook→AWS Lambda →Googleスプレッドシートでメッセージを記録する

# 実現したいこと
chatworkの無料アカウントだと過去のメッセージが閲覧できなくなってしまったので、
閲覧期限が過ぎたメッセージを見返すために都度メッセージを記録する処理を作りたい。


# はじめに
復習を兼ねて記事にしました。
スプレッドシートウェブアプリとlambdaを併用する意味はあまりないと思います。
lambdaを使いたくなったというのと最初はメッセージの保存先をスプレッドシートにするつもりがなかったので、
結局この形になってしまいました。
特にこだわりがなければスプレッドシートウェブアプリをwebhook urlとして使った方が早いかもしれません。


# 環境
chatwork webhook 
AWS Lambda
AWS API Gateway
python 3.9
Googleスプレッドシート
GAS
Windows 11

# 実行手順
## 1 スプレッドシートウェブアプリの作成

### [1] GASプロジェクトに以下コードを転記
```
function doPost(e) {
  const post = e.postData.contents
  const sheet = SpreadsheetApp.getActiveSheet(); 
  const postObj = JSON.parse(post)
  
  const webhook_event = postObj["webhook_event"]
  insertWebhookEvent(sheet, webhook_event)
}

// 対象シートの最終行にwebhook_eventの内容をそのまま転記する関数
// sheet:対象シート webhook_event:chatwork_webhookのbody["webhook_event"]
const insertWebhookEvent = (sheet, webhook_event) => {
  const keys = Object.keys(webhook_event)
  const insertAry2d = keys.map((key) => {
    if (key === "send_time") {
      const sendDateJst = Utilities.formatDate(new Date(webhook_event[key] * 1000), "JST", "yyyy/MM/dd HH:mm:ss");
      return sendDateJst
    }
    return webhook_event[key]
  })
  
  sheet.appendRow(insertAry2d)

}
```

### [2] デプロイする

　右上のデプロイをクリック 
　-> 新しいデプロイ 
　-> ウェブアプリを選択 
　-> 適当な名称を付ける（アクセスできるユーザーは全員）
　-> URLをメモ

## 2 webhookの編集

### [1] webhookの新規作成
※この時のURLはとりあえず、メモしたスプレッドシートウェブアプリのURLを入力

![6_webhookの作成画面.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/694345/e0dc023c-51c3-a5a2-abe6-69be13e51f65.png)

### [2] 作成したwebhookのトークンをメモ

　※新規作成後、webhook設定IDの下にあります。


## 3 lambda関数の作成

### [1] aws lambdaを開く

　AWS Lambda 
　-> 関数
　-> 関数の作成をクリック

### [2] lambdaの基本情報の入力

　一から作成 
　-> 基本的な情報を入力 
　-> 右下、関数の作成をクリック

![1_lambdaの設定.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/694345/b72c9b21-e4db-52a2-5fe9-b9d4452b38d3.png)

### [3] webhookからリクエストを受け、スプレッドシートウェブアプリにpostする関数の作成

コードソース -> lambda_functionに以下の内容を転記
```
import os
import base64
import hashlib
import hmac
import json
import requests

def lambda_handler(event, context):
    # webhook編集画面のtoken
    token = os.environ["CHATWORK_API_TOKEN"]
    # リクエストヘッダー内の署名検証に使うための署名文字列
    request_signature = event["headers"]["x-chatworkwebhooksignature"]
    request_body = event["body"]
    
    digest = hmac.new(base64.b64decode(token), request_body.encode("utf-8"), hashlib.sha256).hexdigest()
    expected_signature = base64.b64encode(bytes.fromhex(digest)).decode("utf-8")

    # 署名確認
    if not(request_signature == expected_signature):        
        print("署名エラー")
        return {
            "statusCode": 410,
            "body": "SignatureDoesNotMatch"
        }
        
    url = os.environ["SPREADSHEET_URL"]

    json_item = json.dumps(event)
    j = json.loads(json_item)
    webhook_body = j["body"]
    requests.post(url, data=webhook_body.encode("utf-8"))
    
    return {
        "statusCode": 200
    }
```

🖊上記のコードの内容
　json_itemまででリクエストされた値の署名検証行っている
　署名検証で許可された場合、スプレッドシートウェブアプリにpostリクエストを行う
　スプレッドシートウェブアプリとwebhook編集画面のtokenは環境変数から取得する

### [4] 環境変数の設定

　関数作成画面中央部、設定をクリック 
　-> 左一覧から環境変数をクリック
　-> 編集をクリック 
　-> キー、値に以下を入力し1個ずつ作成し保存

|キー|値| 
|:----|:----|
|SPREADSHEET_URL|スプレッドシートウェブアプリのURL|
|CHATWORK_API_TOKEN|webhook編集画面のtoken|

![2_環境変数の設定.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/694345/fbdd3a9a-3c4a-537a-543b-1c35284ad4ba.png)


### [5] requestsライブラリが入ったzipフォルダを作成
　※ lambda環境下ではpip installができないため、一度zipにしてからアップロードする必要がある

　ローカル環境にて以下を実行
```
mkdir python
cd python
pip install -t ./ requests
cd ../
zip -r requests.zip python/
```

　※ 最初に作成するフォルダはpythonという名称である必要がある

🖊参考
[AWS Lambdaでレイヤー追加してもエラーが解決しないのはフォルダ名が問題だった](https://soypocket.com/it/aws-lambda-python-error-no-module-named/)

### [6] zipフォルダをアップロードする

　lambda ダッシュボード 
　-> レイヤー 
　-> レイヤーの作成をクリック
　-> 適当な名称を付け、zipをアップロード(他の設定は特に必要ない
　-> 作成したレイヤーのバージョンarnをメモ

![3_arnnum取得.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/694345/d81a0e78-8ce8-215d-c55d-93e0b0ef0e3c.png)



### [7] 作ったlambda関数内でrequestsを使えるようにする

　関数作成画面下部レイヤーからレイヤーの追加をクリック 
　-> arnの指定をクリック 
　-> メモしたバージョンarnを入力し追加ボタンクリック

![4_arnの指定.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/694345/15c21f34-77fa-363d-f77c-6c8a0898110e.png)


## 2 ApiGatewayの設定

### [1] HTTP APIの構築
　aws ApiGatewayを開く 
　-> apiの作成をクリック（過去に使ってないと出ないかも） 
　-> HTTP API の構築をクリック

### [2] 統合を作成

　統合のプルダウンからlambda 
　-> Lambda関数で先ほど作った関数を指定
　-> 適当なapi名を付ける 
　-> 次へ

![5_統合の作成.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/694345/65e96565-99ab-5cc6-fda3-deb29e74d550.png)

### [3] ルートの設定（ステージはなにもしない）
　メソッドをpostに変更 
　-> 次へ 
　-> ステージはそのままで次へ

### [4] 作成したapi_gatewayのurlをwebhookの編集にて入力

　とりあえず入力しておいた、スプレッドシートウェブアプリのURLを削除してapi_gatewayのurlを入力


以上で特定のメッセージルームでメッセージが飛び交うたびに
スプレッドシートに内容が転記されるようになっているはずです。

# おわりに
　最初から記事にするつもりはなかったため、ところどころ抜けがあるかもしれません。
　抜けがあったら申し訳ないです。

　また、lambdaで実装した署名検証をlambda authorizerとして別に作るのも面白そうなので、
　気が向いたら作ってみるのもいいかなと思いました。


# 参考
[チャットワークのwebhookの署名検証をPythonで実装してみた](https://zenn.dev/nnabeyang/articles/85f8dfb381a27d9086fe)
[aws lambda に外部ライブラリをインストールする方法](https://bftnagoya.hateblo.jp/entry/2021/06/23/074120)
[chatwork to Discord webhookを使ったLambda python](https://astail.net/?p=2098)
[【GAS】チャットワークのメッセージを取り出す関数とメッセージを送る関数](https://tonari-it.com/gas-chatwork-message-function/)