API.AIのFulfillmentにIFTTTのWebhooksサービスを使う

# 問題

[API.AI](https://api.ai/)は、エージェントのintentに対して[Fulfillment](https://api.ai/docs/fulfillment)を設定することで、外部のウェブサービスに対してwebhookを発行できます。一方で、[IFTTT](https://ifttt.com/)は[Webhooks](https://ifttt.com/maker_webhooks)というwebhookを扱うサービスをサポートしています。このため、API.AIのエージェントからIFTTTのWebhooksサービスを叩くことは原理的には可能です。しかしながら、API.AIが発行するwebhookの形式はIFTTTのWebhooksサービスが受け取れる形式とは異なるため、直接叩くことができません。

```:API.AIが発行するwebhookの例（ドキュメントより抜粋）
POST https://my-service.com/action

Headers:
Content-type: application/json

POST body:
{
    "lang": "en", 
    "status": {
        "errorType": "success", 
        "code": 200
    }, 
    "timestamp": "2017-02-09T16:06:01.908Z", 
    "sessionId": "1486656220806", 
    "result": {
        ...
        "action": "greetings", 
```

```:IFTTTのWebhooksサービスが受け取るwebhookの例
POST/GET https://maker.ifttt.com/trigger/{event}/with/key/*********************

Headers:
Content-Type: application/json

POST body:
{
    "value1":"value1"
}
```

# 解決法

API.AIが発行するwebhookをIFTTTのWebhooksサービスが受け取れる形式に変換するブリッジを用意する。

# 解説

## 実装例

まず、以下のようなコードをいずれかのサーバで実行します。ここで、IFTTTのイベント名は`api_ai`に固定し、`value1`としてAPI.AIからのwebhook中の`action`を割り当てています。利用方法によっては、API.AIの`action`をIFTTTのイベント名にした方がいいかもしれません。

```js:app.js
'use strict';

const express = require('express');
const request = require('request');
const bodyParser = require('body-parser');

const app = express();
app.use(bodyParser.json());

app.post('/', (req, res) => {
  // API.AIからのwebhookのヘッダに記載されたkeyをIFTTTのkeyとして使用
  const KEY = req.headers['key'];

  // API.AIからのwebhookのresult/actionをIFTTTのイベントのvalue1として使用
  const ACTION = req.body['result']['action'];

  // IFTTTのイベント名は「api_ai」で固定
  var options = {
    uri: 'https://maker.ifttt.com/trigger/api_ai/with/key/' + KEY,
    form: {
      value1: ACTION,
      value2: '',
      value3: ''
    },
    json: true
  };

  request.post(options, (error, response, body) => {
    let responseToApiAi;

    if (!error && response.statusCode === 200) {
      console.log(body);

      responseToApiAi = {
        'speech': 'OK!',
        'displayText': 'OK!'
      };
    } else {
      console.log('error: ' + response.statusCode);

      responseToApiAi = {
        'speech': 'ERROR!',
        'displayText': 'ERROR: ' + response.statusCode
      };
    }

    res.json(responseToApiAi);
  });
});

const PORT = process.env.PORT || 8080;
app.listen(PORT, () => {
  console.log(`App listening on port ${PORT}`);
});
```

次に、API.AIのエージェントの設定Fulfillment＞WebhookでサーバのURLを記入すると共に、ヘッダにIFTTTのkeyを追加します。

![Fullfillment.png](https://qiita-image-store.s3.amazonaws.com/0/92577/5e309f69-9dc6-1931-e990-681e28e8b113.png)

これにより、API.AIがwebhookを発行すると、IFTTTのWebhooksサービスを叩いてトリガーを発生させることができます。


## 補足

IFTTTは[Google Assistant](https://ifttt.com/google_assistant)サービスもサポートしているため、簡単な音声リモコンを実現したいだけであればわざわざAPI.AIでエージェントをつくる必要はありません。

![Assistant.png](https://qiita-image-store.s3.amazonaws.com/0/92577/d2135429-7155-f96c-7dfb-bd9d0a97aa6a.png)
図：IFTTTのGoogle Assistantサービスのトリガーとしての設定項目。最大で3つまでフレーズの例を与えられる。言語は、英語、フランス語、ドイツ語、日本語の4種類から選択できる（2017年10月8日の段階において）。

しかしながら、Google Assistantをトリガーとして使用する際には、提供した3つの例に正確にマッチする必要があります。アルファベットで完結する他の言語と異なり、日本語の場合にはちょっと厄介で、ひらがな／カタカナ／漢字の表記が異なるだけでもマッチしなくなってしまいます。スマートフォンのGoogle Assistantアプリであればどのように認識されたかが確認できますが、Google Homeは音声だけのため、上手く動かない場合どこに問題があるのかがわからずストレスがたまってしまうこともあるでしょう。

こうした場合、IFTTTのGoogle Assistantサービスをそのまま使うのでなく、API.AIで簡単なエージェントを作成してそのエージェント経由で会話することにより、多少の揺れにも対応できるようになると思います。