1
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 3 years have passed since last update.

LINE の スマートスピーカー Clova Friends と Clova Desk の HTTP リクエストを調べる

Last updated at Posted at 2020-02-05

概要

  • LINE Clova から Custom Extension (スキル) 起動時に送信される HTTP リクエストを調べる
  • Ruby + Heroku による調査用 Web サーバ (Extension サーバー) を立てて HTTP リクエストの内容を調べる

Clova とは

LINE が提供しているスマートスピーカー。

LINE Clova公式サイト

ClovaはLINEが開発したAIアシスタントです。
話しかけるだけでLINEの送受信や家電の操作など、あなたの様々なリクエストに応えます。

Clova の HTTP リクエスト

Clova は CEK (Clova Extensions Kit) を介して HTTPS リクエストを Extension サーバーに送信している。

CEKの概要 - Clova Developer Center β

CEKは、Clova Extension(以下、Extension)を開発および配布する際に必要なツールとインターフェースを提供するプラットフォームです。ClovaプラットフォームとExtension間のデータの送受信をサポートします。Extensionは、音楽、ショッピングなどの外部のサービス(サードパーティサービス)、または家庭のIoTデバイスの制御など、Clovaの機能を拡張して、ユーザーに様々な体験を提供するWebアプリケーションです。

CEK APIのリファレンス - Clova Developer Center β

・HTTP/1.1バージョンでHTTP通信し、POSTメソッドを使用します。
・Hostとリクエストパスは、Extensionの開発者があらかじめ定義したURIに設定されます。
・リクエストボディのデータはJSON形式で、UTF-8エンコーディングを使用します。
・SignatureCEKフィールドとRSA公開鍵を使用して、Clovaから送信されたリクエストかどうかを検証することができます。

Extension サーバーとは

Clova から送信される HTTPS リクエストを受信して応答を返す Web サーバ。

Custom Extensionを作成する - Clova Developer Center β

Clova Developer Centerに登録するExtensionサーバーです。このサーバーは、Clovaがユーザーの音声入力を解析した結果や、デフォルトで提供されるインテントを渡された際に、そのインテントを処理して適切な応答を返す必要があります。

今回の調査対象

  • Clova Friends (画面なしモデル)
  • Clova Desk (画面ありモデル)
  • Clova Developer Center テストツール (Web ブラウザ上で使用できるテストツール)

Clova Friends とは

内蔵バッテリーを搭載している Clova。
画面は付いていない。

clova-friends.jpg

Clova Friends | LINE Clova公式サイト

コンパクトなサイズで、見ためもPOPな、スマートスピーカーです。
バッテリーを内蔵しているので、音楽再生、LINE通話、占いなどの便利な機能を、お出かけ先などでご利用いただくことができます。

Clova Desk とは

内蔵バッテリー、画面、赤外線送受信機を搭載している Clova。

clova-desk.jpg

Clova Desk | LINE Clova公式サイト

天気やレシピ、歌詞も画面表示でもっと楽しめる
7インチの画面で、子供と一緒の画面を見ながら、お気に入りの曲を聴いたり、料理を楽しむことができます。

Clova Desk | LINE Clova公式サイト

IRと赤外線リモコンを搭載。リモコンを探すことなく、テレビやエアコンを操作することができます。

Clova Developer Center テストツールとは

実際に Custom Extension (スキル) を配布する前にテストすることができるツール。

clova-dev.png

Extensionをテストする - Clova Developer Center β

テスト画面では、次の2種類のテストを実行できます。

・対話モデルテストモード:任意のインテントのサンプル発話を入力して、インテントやスロットの解析結果やExtensionへのリクエストメッセージを確認できます。
・シナリオテストモード:LaunchRequestからSessionEndedRequestまでの一連のシナリオをテストできます。

HTTP リクエストを受ける調査用 Web サーバ (Extension サーバー)

  • HTTP リクエスト確認用 Ruby スクリプト を Heroku に設置する
  • Clova 実機等で Custom Extension (スキル) を起動して、その際に送信される HTTP リクエストを Web サーバ (Extension サーバー) で受信してログに出力する

HTTP リクエスト確認用 Ruby スクリプト

require 'socket'

# 標準出力を同期モードに設定
$stdout.sync = true

# 接続を受け付けるポート番号を決定
# 環境変数 PORT が設定されているならそれを設定
port = 8000
port = ENV['PORT'].to_i if ENV['PORT']

# サーバー接続をオープン
server = TCPServer.open(port)

# HTTP リクエストを待ち続ける
loop do

  begin

    # TCPSocket オブジェクトを取得
    socket = server.accept

    # 受け付けた日時を出力
    puts "[info]#{Time.new}"

    # HTTP リクエスト開始行を出力
    if not req_start_line = socket.gets
      puts '[info]req_start_line is nil'
      next
    end
    puts "#{req_start_line}"

    # HTTP リクエストヘッダーを1行ずつ出力
    while req_header = socket.gets.chomp
      puts "#{req_header}"
      break if req_header == '' # ヘッダー終了
      # Content-Length ヘッダーがあれば値を変数にセット
      h = req_header.split(':')
      content_length = h[1].strip.to_i if h[0].strip.downcase == 'content-length'
    end

    # Content-Length がある場合はボディを出力
    if content_length != nil
      puts socket.read(content_length)
    end

    # HTTP レスポンスを返す
    # 本文データ
    body = <<-'__EOS__'
    {
      "version": "1.0",
      "sessionAttributes": {},
      "response": {
        "outputSpeech": {
          "type": "SimpleSpeech",
          "values": {
              "type": "PlainText",
              "lang": "ja",
              "value": "こんにちは、クローバ🍀"
          }
        },
        "card": {},
        "directives": [],
        "shouldEndSession": true
      }
    }
    __EOS__
    # ステータス行
    socket.write "HTTP/1.1 200 OK\r\n"
    # ヘッダー
    socket.write "Server: #{RUBY_DESCRIPTION}\r\n"
    socket.write "Content-Type: application/json\r\n"
    socket.write "Content-Length: #{body.bytesize}\r\n"
    socket.write "Connection: close\r\n"
    # 空行
    socket.write "\r\n"
    # 本文
    socket.write body

  rescue => e
    puts e.full_message

  ensure
    # HTTP 接続を閉じる
    puts '[info]close this socket'
    socket.close
  end

end

server.close

調査結果

留意点

  • ユーザー毎等で一意になるような値などは文字「X」による伏せ字に置き換えておく
  • Heroku 経由なので一部の HTTP ヘッダ等が加工・追加されている可能性がある
  • Signaturecek ヘッダ: Clova から送信されたかどうかを検証するためのヘッダ
  • X-B3 ではじまるヘッダ: LINE 側でつけていると思われるヘッダ (分散トレーシングシステム Zipkin で使われるヘッダ)
  • Heroku のリバースプロキシサーバ Vegur がつけていると思われるヘッダ: X-Request-Id, X-Forwarded-For, X-Forwarded-Proto, X-Forwarded-Port, Via, Connect-Time, X-Request-Start, Total-Route-Time

Clova Friends (ディスプレイ無しモデル) の HTTP リクエスト

HTTP リクエスト全体

POST /hello-clova/ HTTP/1.1
Host: example.herokuapp.com
Connection: close
User-Agent: Go-http-client/1.1
Content-Type: application/json; charset=utf-8
Signaturecek: XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX
X-B3-Flags: 0
X-B3-Parentspanid: XXXXXXXXXXXXXXXX
X-B3-Sampled: 1
X-B3-Spanid: XXXXXXXXXXXXXXXX
X-B3-Traceid: XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX
X-Clova-Experiments: 1=A&10=A&2=B&3=A&4=B&5=A&6=C&7=B&8=A&9=B
X-Clova-Request-Id: XXXXXXXX-XXXX-XXXX-XXXX-XXXXXXXXXXXX
Accept-Encoding: gzip
X-Request-Id: XXXXXXXX-XXXX-XXXX-XXXX-XXXXXXXXXXXX
X-Forwarded-For: XXX.XXX.XXX.XXX
X-Forwarded-Proto: https
X-Forwarded-Port: 443
Via: 1.1 vegur
Connect-Time: 0
X-Request-Start: 1580853072763
Total-Route-Time: 0
Content-Length: 744

{"version":"1.0","session":{"new":true,"sessionAttributes":{},"sessionId":"XXXXXXXX-XXXX-XXXX-XXXX-XXXXXXXXXXXX","user":{"userId":"UXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX"}},"context":{"System":{"application":{"applicationId":"info.maigo.lab.helloclova"},"device":{"deviceId":"XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX","display":{"size":"none","contentLayer":{"width":0,"height":0}}},"user":{"userId":"UXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX"}}},"request":{"type":"LaunchRequest","requestId":"XXXXXXXX-XXXX-XXXX-XXXX-XXXXXXXXXXXX","timestamp":"2020-02-04T21:51:12Z","locale":"ja-JP","extensionId":"info.maigo.lab.helloclova","intent":{"intent":"","name":"","slots":{}},"event":{"namespace":"","name":"","payload":null}}}

HTTP メッセージボディの JSON を整形したもの

{
  "version": "1.0",
  "session": {
    "new": true,
    "sessionAttributes": {},
    "sessionId": "XXXXXXXX-XXXX-XXXX-XXXX-XXXXXXXXXXXX",
    "user": {
      "userId": "UXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX"
    }
  },
  "context": {
    "System": {
      "application": {
        "applicationId": "info.maigo.lab.helloclova"
      },
      "device": {
        "deviceId": "XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX",
        "display": {
          "size": "none",
          "contentLayer": {
            "width": 0,
            "height": 0
          }
        }
      },
      "user": {
        "userId": "UXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX"
      }
    }
  },
  "request": {
    "type": "LaunchRequest",
    "requestId": "XXXXXXXX-XXXX-XXXX-XXXX-XXXXXXXXXXXX",
    "timestamp": "2020-02-04T21:51:12Z",
    "locale": "ja-JP",
    "extensionId": "info.maigo.lab.helloclova",
    "intent": {
      "intent": "",
      "name": "",
      "slots": {}
    },
    "event": {
      "namespace": "",
      "name": "",
      "payload": null
    }
  }
}

Clova Desk (ディスプレイ付きモデル) の HTTP リクエスト

HTTP リクエスト全体

POST /hello-clova/ HTTP/1.1
Host: example.herokuapp.com
Connection: close
User-Agent: Go-http-client/1.1
Content-Type: application/json; charset=utf-8
Signaturecek: XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX
X-B3-Flags: 0
X-B3-Parentspanid: XXXXXXXXXXXXXXXX
X-B3-Sampled: 1
X-B3-Spanid: XXXXXXXXXXXXXXXX
X-B3-Traceid: XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX
X-Clova-Experiments: 1=A&10=A&2=B&3=A&4=B&5=A&6=C&7=B&8=A&9=B
X-Clova-Request-Id: XXXXXXXX-XXXX-XXXX-XXXX-XXXXXXXXXXXX
Accept-Encoding: gzip
X-Request-Id: XXXXXXXX-XXXX-XXXX-XXXX-XXXXXXXXXXXX
X-Forwarded-For: XXX.XXX.XXX.XXX
X-Forwarded-Proto: https
X-Forwarded-Port: 443
Via: 1.1 vegur
Connect-Time: 0
X-Request-Start: 1580853042398
Total-Route-Time: 0
Content-Length: 787

{"version":"1.0","session":{"new":true,"sessionAttributes":{},"sessionId":"XXXXXXXX-XXXX-XXXX-XXXX-XXXXXXXXXXXX","user":{"userId":"UXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX"}},"context":{"System":{"application":{"applicationId":"info.maigo.lab.helloclova"},"device":{"deviceId":"XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX","display":{"size":"custom","dpi":160,"orientation":"landscape","contentLayer":{"width":1024,"height":552}}},"user":{"userId":"UXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX"}}},"request":{"type":"LaunchRequest","requestId":"XXXXXXXX-XXXX-XXXX-XXXX-XXXXXXXXXXXX","timestamp":"2020-02-04T21:50:41Z","locale":"ja-JP","extensionId":"info.maigo.lab.helloclova","intent":{"intent":"","name":"","slots":{}},"event":{"namespace":"","name":"","payload":null}}}

HTTP メッセージボディの JSON を整形したもの

{
  "version": "1.0",
  "session": {
    "new": true,
    "sessionAttributes": {},
    "sessionId": "XXXXXXXX-XXXX-XXXX-XXXX-XXXXXXXXXXXX",
    "user": {
      "userId": "UXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX"
    }
  },
  "context": {
    "System": {
      "application": {
        "applicationId": "info.maigo.lab.helloclova"
      },
      "device": {
        "deviceId": "XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX",
        "display": {
          "size": "custom",
          "dpi": 160,
          "orientation": "landscape",
          "contentLayer": {
            "width": 1024,
            "height": 552
          }
        }
      },
      "user": {
        "userId": "UXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX"
      }
    }
  },
  "request": {
    "type": "LaunchRequest",
    "requestId": "XXXXXXXX-XXXX-XXXX-XXXX-XXXXXXXXXXXX",
    "timestamp": "2020-02-04T21:50:41Z",
    "locale": "ja-JP",
    "extensionId": "info.maigo.lab.helloclova",
    "intent": {
      "intent": "",
      "name": "",
      "slots": {}
    },
    "event": {
      "namespace": "",
      "name": "",
      "payload": null
    }
  }
}

Clova Developer Center テストツール の HTTP リクエスト

HTTP リクエスト全体

POST /hello-clova/ HTTP/1.1
Host: example.herokuapp.com
Connection: close
Signaturecek: XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX
Content-Type: application/json;charset=UTF-8
User-Agent: Apache-HttpClient/4.5.6 (Java/1.8.0_202)
Accept-Encoding: gzip,deflate
X-Request-Id: XXXXXXXX-XXXX-XXXX-XXXX-XXXXXXXXXXXX
X-Forwarded-For: XXX.XXX.XXX.XXX
X-Forwarded-Proto: https
X-Forwarded-Port: 443
Via: 1.1 vegur
Connect-Time: 0
X-Request-Start: 1580853979149
Total-Route-Time: 0
Content-Length: 594

{"version":"1.0","session":{"sessionId":"XXXXXXXX-XXXX-XXXX-XXXX-XXXXXXXXXXXX","sessionAttributes":{},"user":{"userId":"XXXXXXXXXXXXXXXXXXXXXX","accessToken":"XXXXXXXX-XXXX-XXXX-XXXX-XXXXXXXXXXXX"},"new":true},"context":{"System":{"application":{"applicationId":"info.maigo.lab.helloclova"},"user":{"userId":"XXXXXXXXXXXXXXXXXXXXXX","accessToken":"XXXXXXXX-XXXX-XXXX-XXXX-XXXXXXXXXXXX"},"device":{"deviceId":"XXXXXXXX-XXXX-XXXX-XXXX-XXXXXXXXXXXX","display":{"size":"l100","orientation":"landscape","dpi":96,"contentLayer":{"width":640,"height":360}}}}},"request":{"type":"LaunchRequest"}}

HTTP メッセージボディの JSON を整形したもの

{
  "version": "1.0",
  "session": {
    "sessionId": "XXXXXXXX-XXXX-XXXX-XXXX-XXXXXXXXXXXX",
    "sessionAttributes": {},
    "user": {
      "userId": "XXXXXXXXXXXXXXXXXXXXXX",
      "accessToken": "XXXXXXXX-XXXX-XXXX-XXXX-XXXXXXXXXXXX"
    },
    "new": true
  },
  "context": {
    "System": {
      "application": {
        "applicationId": "info.maigo.lab.helloclova"
      },
      "user": {
        "userId": "XXXXXXXXXXXXXXXXXXXXXX",
        "accessToken": "XXXXXXXX-XXXX-XXXX-XXXX-XXXXXXXXXXXX"
      },
      "device": {
        "deviceId": "XXXXXXXX-XXXX-XXXX-XXXX-XXXXXXXXXXXX",
        "display": {
          "size": "l100",
          "orientation": "landscape",
          "dpi": 96,
          "contentLayer": {
            "width": 640,
            "height": 360
          }
        }
      }
    }
  },
  "request": {
    "type": "LaunchRequest"
  }
}

参考資料

1
0
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
1
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?