GoogleAssistantからWebhookでLineの特定人物へPush通知

アンドロイドスマホに搭載のGoogleAssistantもしくはGoogleHome系のアプリを作りました。
<img width="669" alt="サムネイル.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/595220/6dcef84a-9384-dba9-ae44-b61831422bf0.png">


##実際の図
GoogleAssistantで"アイで"と発話すると"hello"と返し、Line側には"アイで"がPUSH通知されます。
![1.jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/595220/156c8c65-1ec2-eb8b-5f81-b1eb78a9b688.jpeg)
![2.jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/595220/71b7a38c-1f1d-bcd4-159e-5586227a409e.jpeg)

##そもそも
GoogleAssistant→DialogFlow→LineはIntegrationsで設定すればすぐできますが、
テキストを返すことはできません。
またIntegrationsの方法は自分のLINEへはPUSH通知できますが、特定の人へPUSH通知はできません。

基本、[google homeで子供の宿題管理をする](https://qiita.com/sho0211/items/6a88ffa473980a504956)の実装手順にてできました。うまくいかなかった箇所を本記事に加えてます。

##フロー図
[google homeで子供の宿題管理をする](https://qiita.com/sho0211/items/6a88ffa473980a504956)からお借りして一部付け加えてます。
<img width="853" alt="フロー図Line改.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/595220/3d76c475-74d6-65ce-cd9a-dc5210120182.png">


##DialogFlow
テストでIntentのパラメータを$anyにしてます。
これで”アイで”と発話すればこのIntentが起動します。
![Lambda_error.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/595220/856baaf4-1b3e-3a5a-3f0b-dc60b937a00d.png)


##Lambda

なぜLambdaを経由しているのかというと、GASでデプロイしたエンドポイントは、302リダイレクトをしてしまい、DialogflowのWebhookとして使えなかったからです。

#一番のハマりポイント
[google homeで子供の宿題管理をする](https://qiita.com/sho0211/items/6a88ffa473980a504956)通り実行するとLambda関数にて下記エラーが出ます。
<img width="720" alt="Gasistant側.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/595220/20785258-5ef1-2886-27f9-bd9b27853f06.png">
該当箇所↓
```body = json.dumps(json.loads(event["body"])).encode("utf-8")```

ん？event["body"]ってなに？
jsonデータ見ても”request”なんだけど…

色々調べてみると下記に書いてありました。

・[API Gatewayの「Lambda プロキシ統合の使用」オプション](https://qiita.com/yoshidasts/items/8a31280a6331b6407c73)より

結論から言うと、API Gatewayにてプロキシ統合の使用をチェックすればOKです。
<img width="1182" alt="APIGate.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/595220/21b4663e-7580-0275-3b6a-37389bbcfa34.png">

試しにPOSTしてみたところ、Lambdaのeventには以下の値が設定されていました。

```hoge.json
{
    resource: '/mook',
    path: '/mook',
    httpMethod: 'POST',
    headers: { Accept: ' application/json' },
    queryStringParameters: { param1: 'value1' },
    pathParameters: null,
    stageVariables: null,
    requestContext: {
        accountId: 'xxxxxxxxxxxx',
        resourceId: 'xxxxxx',
        stage: 'test-invoke-stage',
        requestId: 'test-invoke-request',
        identity: {
            cognitoIdentityPoolId: null,
            accountId: 'xxxxxxxxxxxx',
            cognitoIdentityId: null,
            caller: 'xxxxxxxxxxxx',
            apiKey: 'test-invoke-api-key',
            sourceIp: 'test-invoke-source-ip',
            cognitoAuthenticationType: null,
            cognitoAuthenticationProvider: null,
            userArn: 'arn:aws:iam::xxxxxxxxxxxx:root',
            userAgent: 'Apache-HttpClient/4.5.x (Java/1.8.0_102)',
            user: 'xxxxxxxxxxxx'
        },
        resourcePath: '/mook',
        httpMethod: 'POST',
        apiId: 'xxxxxxxxxx'
    },
    body: '{"Message": "Hello World"}'
}	
```

API Gatewayがこれまで丸めていたHTTPヘッダーの情報をかなりきちんと得ることができます。

##Spreadsheet with GAS
下記GASのコードです

```javascript:code.gs
// プロパティ取得
const PROPERTIES = PropertiesService.getScriptProperties();
 
const LINE_CHANNEL_ACCESS_TOKEN = PROPERTIES.getProperty('LINE_CHANNEL_ACCESS_TOKEN');//GLG_test_bot
const USER_ID = PROPERTIES.getProperty('your_USER_ID');
 
function doPost(e) {
 try {
   let request = JSON.parse(e.postData.getDataAsString());
   let any = request.queryResult.parameters.any
   switch (any) {
     case 'アイ':
       let result = { "fulfillmentText": "hello" };//fulfillmentTextが大事！
       pushMessage(any)
       return returnAsJSON(result);
     default:
       return returnAsJSON({ "fulfillmentText": "ごめんね。よくわからなかったよ" });
   }
 } catch (ex) {
   Logger.log("Error at doPost : %s", ex)
   doc.getBody().appendParagraph(Logger.getLog())
   return returnAsJSON({ "fulfillmentText": "エラーが発生しました。ログで確認できます" });
 }
}
 
function returnAsJSON(obj) {
 return ContentService.createTextOutput(JSON.stringify(obj)).setMimeType(ContentService.MimeType.JSON);
}
 
function pushMessage(message) {
 let postData = {
   "to": USER_ID,
   "messages": [{
     "type": "text",
     "text": message,
   }]
 };
 
 let url = "https://api.line.me/v2/bot/message/push";
 let headers = {
   "Content-Type": "application/json",
   'Authorization': 'Bearer ' + LINE_CHANNEL_ACCESS_TOKEN,
 };
 
 let options = {
   "method": "post",
   "headers": headers,
   "payload": JSON.stringify(postData)
 };
 let response = UrlFetchApp.fetch(url, options);
}
```
GASはウェブ公開にてデプロイし、そのあと出てくるURLをLambda側にコピペすればOK

※Lineについて別途設定が必要ですが、特別なことはやっていないので設定等は割愛。
LineBotでググればいくらで出てきます。

あとはDialogFlow側でテストし、AWSのcloudwatchのログなどを見ながら実装する。

##今後の展望
GASが使えるのでスプレッドシートと連携して色々できます。

##参考：
[google homeで子供の宿題管理をする](https://qiita.com/sho0211/items/6a88ffa473980a504956)
[初めてのサーバーレスアプリケーション開発 ～API GatewayからLambdaを呼び出す～ ｜ Developers.IO](https://dev.classmethod.jp/cloud/aws/serverless-first-apigateway/)