Azure Functions を使って、LINE WORKS のオウム返し Bot を作ってみる (その2:AzureFunctions準備編)

前回は、LINE WORKS の Developer Console で、オウム返し Bot の作成準備を行いました。
今回は、いよいよ Azure Functions で、Bot 本体を準備していきます。

まずは、Azure Portal へアクセスし、`リソースの作成` から関数アプリを作成します。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2667330/9a810515-0125-ba35-ce0e-498274964980.png)
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2667330/b693b841-e0fa-264c-ee98-4f933edef6ed.png)
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2667330/62813a57-ac27-a432-60b8-cd899742ffa7.png)
ランタイム スタックには `PowerShell Core`を選択します。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2667330/c536da1c-2ae8-a061-f20e-8cc035aae453.png)
> 今回は、お手軽に試せるということで、プランの種類として `消費量（サーバーレス）` を選択しました。このプランでは、アクセスがが来てから環境を起動し、しばらくアクセスが無いと環境を終了します。
> このため、メッセージを受けてからボットがオウム返しするまで、若干 (30秒程度？) 時間がかかることがあります。
> LINE WORKS の処理が遅いのではなく、`消費量（サーバーレス）` プランを利用した Azure Functions の制約ですので、その旨ご理解ください m(＿,＿)m

[確認および作成] をクリックして先に進みましょう。デプロイの完了まで数分かかります。

作成された関数アプリに移動します。見つからない場合には、［すべてのサービス]-[すべてのリリース]から見つけます。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2667330/11edd28d-f739-3d89-8ceb-3690a30ecaca.png)


さて、これまでは、PowerShell での JWT の処理には [powershell-jwt](https://github.com/Nucleware/powershell-jwt) をインストールして利用してきました。Azure Function で PowerShell Module を使う場合には、モジュールをインストールするのではなく、`Save-Module` Cmdlet で実行環境に保存する必要があります。

まずは、[開発ツール]-[高度なツール]-[移動] から Kudu に移動します。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2667330/dda13a25-07a9-4d05-edc0-d0fa3600d916.png)

［Debug Console］-[PowerShell] で PowerShell コンソールを開きます。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2667330/dbe38e33-59cf-1349-c082-24cef3c822d1.png)

コンソール領域に以下のコマンドを入力します
```
cd site\wwwroot
mkdir modules
[Net.ServicePointManager]::SecurityProtocol = [Net.SecurityProtocolType]::Tls12
Install-PackageProvider nuget -Force -Scope CurrentUser
Save-Module powershell-jwt -Path .\modules 
```
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2667330/653ebc79-76a0-f288-d027-8276db80a6d6.png)

さらに、LINE WORKS の　Developers Console でダウンロードした PrivateKey ファイルの拡張子を txt に変更し、ページ上部ファイル・フォルダ領域にドラッグアンドドロップして、アップロードします。（拡張子が .key のフィルはアップロードができないようです。）
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2667330/e5b77060-3059-f18d-81fe-2a11f6699f8e.png)

認証に必要な情報は、PowerShell 内にベタに書くこともできますが、せっかくなので、Azure Functions の環境変数を利用してみましょう。
［設定]-[構成]から[アプリケーション設定］ タブを開き、［新しいアプリケーション設定］をクリックして LINE WORKS の Developers Console で取得した認証に必要な情報を保存していきます。最期に[保存]ボタンをクリックするのを忘れないでください。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2667330/5db65a04-bef1-612e-b1ed-c17782c02758.png)

|名前|取得元|例|
|-|-|-|
|BOTID|BotID (BotNo)|1234567|
|CLIENTID|Client ID| A0B1c3d4e5f6g7h8I9|
|CLIENTSECRET|Client Secet|AbCdEfGhIj|
|SERVICEACCOUNT|Service Account|abcde.serviceaccount@yourcompany|
|PRIVATEKEYFILE|アップロードしたPrivateKeyFile名|private_20220101123456.txt|
|BOTSECRET|Bot Secret|lkjlekjlkjSLJKDLKDJLElkjl|


ここまできたらあと一息。［関数]-[関数]-[作成] で関数を作成します。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2667330/1b36d56d-9fc4-a7f5-439b-e03c93f7bc3a.png)
開発環境は ［ポータルでの開発］ を選択し、テンプレートには ［HTTP trigger］ を選びます。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2667330/81c5a2c1-8d29-0f78-a228-c54aa0cbd284.png)

作成された ［HttpTrigger1］ へ移動し、［コードとテスト］ をクリックするとエディタが開きますので、
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2667330/9037f84d-8d58-09eb-8a09-6cb258fb1363.png)

次のコードでまるっと置き換えて保存します。
```PowerShell
using namespace System.Net
param($Request, $TriggerMetadata)


$clientId       = $env:CLIENTID
$clientSecret   = $env:CLIENTSECRET
$privateKeyFile = $env:PRIVATEKEYFILE
$svcAccount		= $env:SERVICEACCOUNT
$botSecret		= $env:BOTSECRET
$Scope 			= "bot"
$body = $Request.RawBody
$receiveMsg = $body | convertfrom-json 

# GET Access Token 
$PrivKeyPath    = "D:\home\site\wwwroot\" + $privateKeyFile 

$rsaPrivateKey = Get-Content $PrivKeyPath -AsByteStream

$iat = [int](Get-Date -UFormat %s)
$exp = $iat + 3600

$payload = @{
    sub = $SvcAccount
    iat = $iat
}
    
$jwt = New-JWT -Algorithm 'RS256' -SecretKey $rsaPrivateKey -PayloadClaims $payload -ExpiryTimestamp $exp -Issuer $clientId
    
$requestHeader = @{
    'Content-Type' = 'application/x-www-form-urlencoded'
}

$requestBody = @{
    assertion     = $jwt
    grant_type    = 'urn:ietf:params:oauth:grant-type:jwt-bearer'
    client_id     = $clientId
    client_secret = $clientSecret
    scope         = $Scope
}

$url = 'https://auth.worksmobile.com/oauth2/v2.0/token'
$response = Invoke-RestMethod -Uri $url -Method POST -Headers $requestHeader -Body $requestBody

$sectoken = ConvertTo-SecureString $response.access_token -AsPlainText

# Signature check

$oHMACSHA256 = New-Object System.Security.Cryptography.HMACSHA256
$oHMACSHA256.key = [Text.Encoding]::ASCII.GetBytes($BotSecret)

$readBuf =  [System.Text.Encoding]::UTF8.GetBytes($body)

$signature_rawout = $oHMACSHA256.ComputeHash($readBuf)
$signatureB64 = [Convert]::ToBase64String($signature_rawout)

if ($signatureB64.ToString() -eq $Request.Headers["X-Works-Signature"]) {
  $msg = $receiveMsg.content.text
}
else {
  $msg =  "怪しい奴め！"
}

# オウム返しするぞ
$reqBody =@{
	content = @{
		type = 'text'
		text = $msg
	}
}

$reqBody = $reqBody | convertto-json
$reqBody = [System.Text.Encoding]::UTF8.GetBytes($reqBody ) # hack for MOJI-BAKE

$userId = $receiveMsg.source.userId
$botId = $Request.Headers["X-Works-BotId"]

$URL = "https://www.worksapis.com/v1.0/bots/$botId/users/$userId/messages"
Invoke-WebRequest -Method POST -Uri $URL  -body $reqBody -Authentication Bearer -Token $secToken -ContentType "application/json"
```
コードを見ても、1/3 が AccessToken 取得、1/3 が Signature のチェックで、実際に Bot メッセージを送っている部分は本当に簡単ですね。

右上の ［関数URLの取得］ で、Azure Functions で作成した関数を呼び出すための URL を取得しておきます。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2667330/8595e643-f08f-2746-5f51-4b48eb1838b6.png)


最期にもう一度 LINE WORKS の Developer Console へ行き、Callback URL を有効にして先程保存した URL を貼り付けます。送信可能なメッセージタイプに `テキスト` を指定します。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2667330/278b72dc-cc40-064d-23a7-1c6d4042b126.png)


オウム返し Bot の作成作業は以上です。
オウム返し Bot に話しかけると、そっくりそのまま返してくれます。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2667330/1fe96909-8baf-9d2e-0de5-348bbef83362.png)



> Bot Secret について
> Bot へのメッセージ送信は、Callback URL さえわかっていれば、認証もなく誰でもアクセスすることができます。
> Bot に対して発行される Bot Secret を用いてメッセージのハッシュを計算し、X-WORKS-Signature ヘッダーの値と比較することで、本当に LINE WORKS から送られたメッセージであるかを検証することができます。
> 運用環境で Bot の Callback を利用する場合には、メッセージの検証を必ず行うようにしてください。