LINE WORKS API を使って出退勤をメモする BOT を作ってみる (固定メニュー)

今回は、LINE WORKS API 2.0 を使用し、出退勤時刻をメモする BOT を作ってみます。

トーク BOT とのトークルームで画面下部に表示するユーザー定義のメニューを固定メニューといいます。
手元の PowerShell を使い、あらかじめ作成しておいた BOT に対して、ボタンをクリックすると callback URL が呼ばれる [固定メニューを登録](https://developers.worksmobile.com/jp/reference/bot-persistentmenu-create?lang=ja)します。

```powershell
# Token を取得して Secure String として $secToken に入れるところは省略

$botId = 12345678  # 作った Bot の BotId を指定
$url = "https://www.worksapis.com/v1.0/bots/$botId/persistentmenu"
$contentBody = @{
    content = @{
        actions = @(
            @{
                type     = "message"
                label    = "出社しました"
                text     = "ただいま出社しました"
                postback = "comeintowork"
            }
            @{
                type     = "message"
                label    = "帰ります"
                text     = "そろそろ帰ります"
                postback = "leavetheoffice"
            }
            @{
                type     = "uri"
                label    = "使い方"
                uri = "https://line.worksmobile.com"
            }
        )
    }
}

$reqBody = $contentBody | ConvertTo-Json -depth 10
$reqBody = [System.Text.Encoding]::UTF8.GetBytes($reqBody)

Invoke-WebRequest -Uri $url -Method POST -Authentication Bearer -Token $sectoken -ContentType "application/json" -Body $reqBody
```
これで、BOT に固定メニューが登録できました。


次に、Azure Functionｓ 側の構成です。
新しい BOT を使ったので、新しい BOT の botId と BotSecret は、アプリケーション設定に BOTID2 と BOTSECRET2 として追加しておきます。
HTTP Trigger テンプレートを使って新しい関数を作成し、以下のコードを貼り付けます。

```powershell
using namespace System.Net
param($Request, $TriggerMetadata)


$clientId       = $env:CLIENTID
$clientSecret   = $env:CLIENTSECRET
$botId			= $env:BOTID2
$privateKeyFile = $env:PRIVATEKEYFILE
$svcAccount		= $env:SERVICEACCOUNT
$botSecret		= $env:BOTSECRET2

$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



$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"]) {
# 固定メニューが選択された場合の処理
  $postback = $receiveMsg.content.postback
  if ($postback -eq "comeintowork"){
    $msg = "今日も一日頑張ろう @ " +  [System.TimeZoneInfo]::ConvertTimeBySystemTimeZoneId([DateTime]($receiveMsg.issuedTime),"Tokyo Standard Time")
  }elseif ($postback -eq "leavetheoffice")
  {
    $msg = "お疲れさまでした! @ " +  [System.TimeZoneInfo]::ConvertTimeBySystemTimeZoneId([DateTime]($receiveMsg.issuedTime),"Tokyo Standard Time")
  }else{
    $msg = "固定メッセージから 「出社しました」 か 「帰ります」 を選択してください” 
  }
}
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

$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"

```
コードはちょっと多いですが、callback メッセージに応じて返信メッセージを作成する部分がちょっと変わっているだけで、Token の取得やメッセージの送信など、ほとんどは前回と同じコードです。

ここまできたら、関数の URL を取得して、BOT の callback に指定します。BOT に送信可能なメッセージとしてテキストを選択しておきます。

これで、BOT 利用の準備ができました。

作成した BOT とのトークルームを開くと、画面下部に、登録した固定メニューが表示されています。
「使い方」 ボタンを押すと、固定メニューに登録した URL が開きます。
「出社しました」または「帰ります」ボタンを選択すると、postback パラメータとともに BOT に指定した callback URL (= Azure Functins 上の関数) が呼ばれます。Azure functios 側では、callback の内容から出社か退勤を判断し、issuedTime (= ボタンがクリックされた時刻) を含めて、ユーザーにメッセージを飛ばします。
<img width="360" alt="Image.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2667330/62ad5071-ca4e-ba59-dd80-c121b0e1e646.jpeg"/>


今回はメッセージの文字列を構成してユーザーに送り返したのみですが、必要であればロジックを組み込んで、データベースに出退勤時刻を保存するようなことも可能です。