Microsoft Bot Framework 1.0 の HTTP Flow (REST)


2016/08 : This is an old post (which describes about version 1). Please see “BUILD BOT with Microsoft Bot Framework Rest Api” (which describes about version 3) as new one.
この投稿は古い情報です。最新の Microsoft Bot Framework Rest (version 3) については「BUILD BOT with Microsoft Bot Framework Rest Api」を参照してください。

こんにちは。

Microsoft Bot Framework や Skype Bot Platform について、「.NET と NodeJS しか SDK がないので不便」といった声をよく聞きます。(実は今日もチーム ミーティングでそんな話になりました。エバンジェリスト チームの会議では、そんな話をしています。。。)
そこで今回は、Microsoft Bot Framework の内部で呼ばれている HTTP Flow を紹介します。

ここで記載する内容を理解することで、SDK (C#, Node.js など) がサポートしていないさまざまな言語 (PHP, Python, Java など) で構築できるので、これまで手がつけにくいと思っていた方も、是非いろいろと遊んでみてください。

なお、今回は Microsoft Bot Framework のみ紹介しますが、Skype Bot Platform も同様に HTTP (REST) がベースとなっています。Skype Bot Platform も、いずれ Public になった際に紹介したいと思います。(現在、Skype Bot Platform は Insider Preview の位置づけです。Skype Bot Platform では、Basic 認証ではなく OAuth token、送受信のパターンも異なるなど、方式が異なっています。)

Authentication

Microsoft Bot Framework に Bot を登録する際、下図の通り、App Id とパスワードに相当する App Secret を設定・取得したはずです。
皆さんが作成する Bot と Microsoft Bot Framework の間は、この App Id と App Secret を使用した Basic 認証 (Basic Authentication) を使って保護します。

ユーザーが Bot を呼び出した際は、Microsoft Bot Framework が Basic 認証を使って App Id と Secret を Bot に渡します。Bot (皆さんが作成するアプリ) 側は受け取った App Id と Secret を確認し、正しければ処理をおこないます。(もし正しくなければ HTTP Status の Forbidden (403) を返してください。)
また、Bot がトリガーとなってユーザーを呼び出す場合は、Bot が Microsoft Bot Framework に (Basic 認証を使って) App Id と Secret を渡し、Microsoft Bot Framework はこの渡された値を確認します。

HTTP における Basic 認証の利用方法は簡単です。{App Id}:{Secret} の形式の文字列 (例えば、tsmatsuzsamplebot01:d340e614fd...) を Base64 Encode した文字列を HTTP Header に設定するだけです。例えば、下記のような形です。(受信側は、この dHNtYXRzdX... を Decode することで App Id と Secret を取り出し、その内容を確認します。)

POST https://{your bot endpoint} HTTP/1.1
Authorization: Basic dHNtYXRzdX...
Content-Type: application/json; charset=utf-8
. . .

いずれ紹介しますが、例えば、Skype Bot Platform の場合は Azure AD v2 endpoint が使用されているなど、一般に、Bot Platform ごとに異なる認証方法が使用されています。Microsoft Bot Framework では、こうした Channel ごとの差異を吸収し、Channel に応じた方式に変換して処理します。(Channel の初期設定時を除き、開発者がこの差異を意識する必要はありません。)

Request-Reply Conversation (Inbound)

天気を問い合わせたり、レストランの検索を依頼するなど、多くは利用者 (User) が Bot に尋ねて、Bot がこれに応答します。まずは、こうした Request-Reply 方式 (Bot に Inbound に Request が入ってくる方式) の場合の REST (Raw HTTP) を紹介します。

まず、利用者 (User) が Bot に Text Message を送信すると、Framework は Bot に以下の HTTP Request を渡します。
ここでは、”Hi, Matsu!” という Text Message が渡されています。

上述の通り、Authorization ヘッダーの値については、Base64 Decode により内容を検証 (確認) してください。

HTTP Request by Framework

POST https://{your bot endpoint} HTTP/1.1
Authorization: Basic dHNtYXRzdX...
Content-Type: application/json; charset=utf-8

{
  "type": "Message",
  "id": "9ir4EFUT5qi",
  "conversationId": "7CgDU8BuME645xD7qLAPzGlQETd2Pa5h7EfAqh9R71hPCV2",
  "created": "2016-05-19T04:07:18.3777251Z",
  "text": "Hi, Matsu!",
  "attachments": [],
  "from": {
    "name": "devportal",
    "channelId": "test",
    "address": "devportal",
    "id": "JMQ0KLCKN6R",
    "isBot": false
  },
  "to": {
    "name": "Tsmatsuz Sample Bot01",
    "channelId": "test",
    "address": "tsmatsuzsamplebot01",
    "id": "tsmatsuzsamplebot01",
    "isBot": true
  },
  "participants": [
    {
      "name": "devportal",
      "channelId": "test",
      "address": "devportal",
      "id": "JMQ0KLCKN6R",
      "isBot": false
    },
    {
      "name": "Tsmatsuz Sample Bot01",
      "channelId": "test",
      "address": "tsmatsuzsamplebot01",
      "id": "tsmatsuzsamplebot01",
      "isBot": true
    }
  ],
  "totalParticipants": 2,
  "mentions": [],
  "channelConversationId": "tsmatsuzsamplebot01",
  "hashtags": []
}

Bot では、利用者 (User) への Reply Message を、下記の通り HTTP Response を使って返します。(今回は、”Fine !” と返しています。)
上記の HTTP Request の通り、受信する各 Message には id (上記の 9ir4EFUT5qi) が付与されていますが、Response ではこの Id を replyToMessageId に設定します。(Bot Framework 側で Message とその応答の対応をおこなう際に、この id を使用します。)
なお、この Message Id は Message が送信されるたびに変更されますが、conversationId は利用者 (User) との一連のスレッドを識別する Id で、一連の会話の中で一意です。(プログラミングする Bot の中で、こうした Id を使ってスレッドなどを識別してください。)

HTTP Response by Bot

HTTP/1.1 200 OK
Content-Type: application/json; charset=utf-8

{
  "conversationId": "7CgDU8BuME645xD7qLAPzGlQETd2Pa5h7EfAqh9R71hPCV2",
  "text": "Fine !",
  "from": {
    "name": "Tsmatsuz Sample Bot01",
    "channelId": "test",
    "address": "tsmatsuzsamplebot01",
    "isBot": true
  },
  "to": {
    "name": "devportal",
    "channelId": "test",
    "address": "devportal",
    "isBot": false
  },
  "replyToMessageId": "9ir4EFUT5qi",
  "participants": [
    {
      "name": "devportal",
      "channelId": "test",
      "address": "devportal"
    },
    {
      "name": "Tsmatsuz Sample Bot01",
      "channelId": "test",
      "address": "tsmatsuzsamplebot01"
    }
  ],
  "totalParticipants": 2,
  "channelConversationId": "tsmatsuzsamplebot01"
}

上記の channelId は使用されている Channel (skype, slack, email などの区別) を表していて、この Channel に応じて Bot の address も変わるので注意してください。
例えば、 Skype (Skype Bot Platform) の Channel を使用する場合、address には Skype の Bot ID (下図) を指定します。

補足 : なお、上記の channelId の test は、Bot Framework Developer Portal の Web Test Client の場合の値です。

Channel の判別をおこなって、例えば、Skype の場合には絵文字 (emoticon) を返すなど、Channel ごとの独自な応答も可能です。
下記は、絵文字を返す Skype の場合の REST のサンプル (HTTP Requset, HTTP Response) です。

HTTP Request by Framework (Skype の場合)

POST https://{your bot endpoint} HTTP/1.1
Authorization: Basic dHNtYXRzdX...
Content-Type: application/json; charset=utf-8

{
  "type": "Message",
  "id": "4oij16x17tf",
  "conversationId": "1MC2mc1qR7up7bTG2w2by2v3Cay1CbFEGEQlFYQ1xX6Zc6x1",
  "created": "2016-05-19T06:11:15.965Z",
  "text": "Smile !",
  "attachments": [],
  "from": {
    "name": "8:live:tsuyoshi.matsuzaki",
    "channelId": "skype",
    "address": "8:live:tsuyoshi.matsuzaki",
    "id": "949dPd8X5wF",
    "isBot": false
  },
  "to": {
    "name": "tsmatsuzsamplebot01",
    "channelId": "skype",
    "address": "fc4a62b9-55de-49d8-b5ee-89a3235b6c7a",
    "id": "tsmatsuzsamplebot01",
    "isBot": true
  },
  "participants": [
    {
      "name": "tsmatsuzsamplebot01",
      "channelId": "skype",
      "address": "fc4a62b9-55de-49d8-b5ee-89a3235b6c7a",
      "id": "tsmatsuzsamplebot01",
      "isBot": true
    },
    {
      "name": "8:live:tsuyoshi.matsuzaki",
      "channelId": "skype",
      "address": "8:live:tsuyoshi.matsuzaki",
      "id": "949dPd8X5wF",
      "isBot": false
    }
  ],
  "totalParticipants": 2,
  "mentions": [],
  "channelMessageId": "0",
  "channelConversationId": "8:live:tsuyoshi.matsuzaki",
  "hashtags": []
}

HTTP Response by Bot (Skype の場合)

HTTP/1.1 200 OK
Content-Type: application/json; charset=utf-8

{
  "conversationId": "1MC2mc1qR7up7bTG2w2by2v3Cay1CbFEGEQlFYQ1xX6Zc6x1",
  "text": "<ss type=\"wink\">;)</ss>",
  "from": {
    "name": "tsmatsuzsamplebot01",
    "channelId": "skype",
    "address": "fc4a62b9-55de-49d8-b5ee-89a3235b6c7a",
    "isBot": true
  },
  "to": {
    "name": "8:live:tsuyoshi.matsuzaki",
    "channelId": "skype",
    "address": "8:live:tsuyoshi.matsuzaki",
    "isBot": false
  },
  "replyToMessageId": "4oij16x17tf",
  "participants": [
    {
      "name": "tsmatsuzsamplebot01",
      "channelId": "skype",
      "address": "fc4a62b9-55de-49d8-b5ee-89a3235b6c7a"
    },
    {
      "name": "8:live:tsuyoshi.matsuzaki",
      "channelId": "skype",
      "address": "8:live:tsuyoshi.matsuzaki"
    }
  ],
  "totalParticipants": 2,
  "channelMessageId": "0",
  "channelConversationId": "8:live:tsuyoshi.matsuzaki"
}

いずれ解説しますが、例えば、Skype Bot Platform の場合は、こうした Requset-Reply 方式の場合も、受信と送信の 2-leg の HTTP 呼び出し (異なる HTTP 要求) をおこないます。Microsoft Bot Framework では、こうした Channel ごとの差異を吸収して、Channel に応じた方式に変換して処理してくれます。(Microsoft Bot Framework の開発者が、こうしたパターンの違いを意識する必要はありません。)

Bot-Triggered Conversation (Outbound)

利用者 (ユーザー) の要求に回答するのではなく、イベント通知やタイマー処理など、Bot がトリガーとなって利用者に通知することも可能です。(この場合でも、もちろん、最初に、利用者がこの Bot の購読をおこないます。)
例えば、下記は、”Hey, what’s up Tsuyoshi ?” という Message を、Bot から Skype アドレス (live:tsuyoshi.matsuzaki) 宛に投げています。(Channel として Skype を使用しています。)

なお、この際、Framework (Microsoft Bot Framework) に渡す Authorization ヘッダーには上記と同じ内容を指定します。つまり、上記 (Inbound の場合) とは逆に、Bot の App Id と Secret を Bot Framework に渡すことで、Bot Framework 側がこの渡された Credential を検証します。(Secret などが誤っていた場合、Bot Framework から Forbidden (403) が返されます。)

HTTP Request by Bot

POST https://api.botframework.com/bot/v1.0/messages HTTP/1.1
Authorization: Basic dHNtYXRzdX...
Content-Type: application/json; charset=utf-8

{
  "language": "en",
  "text": "Hey, what's up Tsuyoshi ?",
  "from": {
    "name": "tsmatsuzsamplebot01",
    "channelId": "skype",
    "address": "fc4a62b9-55de-49d8-b5ee-89a3235b6c7a",
    "isBot": true
  },
  "to": {
    "name": "8:live:tsuyoshi.matsuzaki",
    "channelId": "skype",
    "address": "8:live:tsuyoshi.matsuzaki"
  }
}

HTTP Response by Framework

HTTP/1.1 200 OK
Content-Type: application/json; charset=utf-8

{
  "type": "Message",
  "id": "F2NEPKSEB6U",
  "conversationId": "Djx1lR8IAF5Y1VB8aEDla5s84dnDOb8PF6v2CWYFw05PqEHd",
  "created": "2016-05-19T06:50:05.045569Z",
  "text": "",
  "attachments": [],
  "from": {
    "name": "8:live:tsuyoshi.matsuzaki",
    "channelId": "skype",
    "address": "8:live:tsuyoshi.matsuzaki",
    "id": "949dPd8X5wF",
    "isBot": false
  },
  "to": {
    "name": "tsmatsuzsamplebot01",
    "channelId": "skype",
    "address": "fc4a62b9-55de-49d8-b5ee-89a3235b6c7a",
    "id": "tsmatsuzsamplebot01",
    "isBot": true
  },
  "replyToMessageId": "LT2mbTW4E1C",
  "participants": [
    {
      "name": "tsmatsuzsamplebot01",
      "channelId": "skype",
      "address": "fc4a62b9-55de-49d8-b5ee-89a3235b6c7a",
      "id": "tsmatsuzsamplebot01",
      "isBot": true
    },
    {
      "name": "8:live:tsuyoshi.matsuzaki",
      "channelId": "skype",
      "address": "8:live:tsuyoshi.matsuzaki",
      "id": "949dPd8X5wF",
      "isBot": false
    }
  ],
  "totalParticipants": 2,
  "mentions": [],
  "channelConversationId": "49eenlloav3",
  "hashtags": []
}

 

de:code 2016 (BUILD の recap イベント) の私のセッションでは、新しく出てきた Skype の Developer Platform のすべてを紹介します !
他に、Bot のセッション等もありますので、是非 イベントを楽しんでください。

 

Comments (1)

  1. Scott says:

    Great blog.