Firebase Cloud Functions(Node.js version 8)でClova CEK SDK Nodejsを動かしてみた。
Firebase Cloud FunctionsでClova CEK SDK Nodejsを動かしてみた所、一ヵ所つまづきポイントがありましたが、ちゃんと動いたので、まとめました。
手っ取り早いコードだけ知りたい方はこちら
使うもの
Firebase Cloud Functions
いわゆるサーバーレスでAPIを作れるクラウドサービス。
AWSでいうところのAPIGatewayとLambdaが引っ付いているイメージ。
Clova CEK SDK Nodejs
ClovaをNode.jsで操作するためのSDK
事前に必要な作業
- Firebaseアカウント(Googleアカウント)の作成
- ローカルPCにNode.js(version8)インストール
https://nodejs.org/ja/download/ - LineDeveloper 登録
手順
Firebaseでプロジェクト作成
ログイン プロジェクトを追加
Firebaseコンソール( https://console.firebase.google.com/u/0/?hl=ja )にログインし、「プロジェクトを追加」を押下。プロジェクトが作成
適当なプロジェクト名をつけて「次へ」→「プロジェクトを作成」を押下、しばらく待つとプロジェクトが作成される。
Cloud Functions作成
サイドメニューから「Functions」選択し「使ってみる」を押下。
FirebaseCLIをインストール 画面指示に従い、ターミナルで以下コマンド実行。
$ npm install -f firebase-tools
デプロイ 画面指示に従い、ターミナルで以下コマンド実行。
Firebaseのどのサービスを利用するか聞かれるので「Functions」を選択。 プロジェクトの選択が聞かれるので、先ほど作ったプロジェクトを選択。 使用言語やLintなど聞かれるのでとりあえずデフォルトを選択、しばらく待つと作成される。 作成されたら、該当ディレクトリ内の/Functions/inde.jsを編集。$ cd [適用なディレクトリ] $ mkdir [プロジェクト名] $ cd [プロジェクト名] $ firebase init
6~8行目のコメントアウトを外し、保存。ターミナルで以下コマンド実行。1const functions = require('firebase-functions'); 2 3// // Create and Deploy Your First Cloud Functions 4// // https://firebase.google.com/docs/functions/write-firebase-functions 5// 6exports.helloWorld = functions.https.onRequest((request, response) => { 7 response.send("Hello from Firebase!"); 8}); 9
$ firebase deploy
動作確認
サイトをリロードすると、ダッシュボード画面が現れ、URLが表示されるので、そこにアクセス。 「Hello from Firebase!」と表示されればOK。
Clova設定
- スキル登録
Clova Extensions Kit チュートリアルに則ってClova Developer Centerでスキル登録
https://clova-developers.line.me/guide/#/CEK/Tutorials/Build_Simple_Extension.md#Step2 ExtensionサーバーのURLは、先ほどFirebaseで設定したURLの「helloWorld」を「clova/clova」に変更したものをセット発話モデルも登録例) https://us-central1-clova-firebase-001.cloudfunctions.net/helloWorld の場合、 https://us-central1-clova-firebase-001.cloudfunctions.net/clova/clova
ただ、今回は特に判定あんまりしないので、適当でOKです。 ビルドまで完了させます。 https://clova-developers.line.me/guide/#/CEK/Tutorials/Build_Simple_Extension.md#Step3
Clova CEK SDK Nodejsをインストールし、コード記述
Clova CEK SDKインストール
ターミナルで以下コマンド実行。なお、なぜ公式のSDKリポジトリを使っていないかは、後述。cd functions npm install https://github.com/TanakaMidnight/clova-cek-sdk-nodejs --save
Firebase Cloud FunctionsのNode.jsdeのバージョンを変更する
FunctionsのデフォルトのNode.jsが6で、
サンプルコードにasync/awaitなどを使っており、バージョンが8でないと動かないため、
FunctionsのNode.jsのバージョンを変更する。
functions/package.jsonを変更。
3~5行目を追加する。1{ 2 "name": "functions", 3 "engines": { 4 "node": "8" 5 }, 6 "description": "Cloud Functions for Firebase", 7 "scripts": { 8 "serve": "firebase serve --only functions", 9 "shell": "firebase functions:shell", 10 "start": "npm run shell", 11 "deploy": "firebase deploy --only functions", 12 "logs": "firebase functions:log" 13 }, 14 "dependencies": { 15 "@line/clova-cek-sdk-nodejs": "git+https://github.com/TanakaMidnight/clova-cek-sdk-nodejs.git", 16 "firebase-admin": "~6.0.0", 17 "firebase-functions": "^2.0.3" 18 }, 19 "private": true 20}
サンプルコードをもとにコード実装
functions/index.jsを
公式サンプルコード
https://github.com/line/clova-cek-sdk-nodejs#example
から一部変更します。
(変更した部分はハイライトしてます。)
なお、45行目の「YOUR_APPLICATION_ID」はClova Developer Centerで登録した、「Extension ID」をセットします。
functions/index.js1const functions = require('firebase-functions'); 2 3const clova = require('@line/clova-cek-sdk-nodejs'); 4const express = require('express'); 5const bodyParser = require('body-parser'); 6 7const clovaSkillHandler = clova.Client 8 .configureSkill() 9 .onLaunchRequest(responseHelper => { 10 responseHelper.setSimpleSpeech({ 11 lang: 'ja', 12 type: 'PlainText', 13 value: 'おはよう', 14 }); 15 }) 16 .onIntentRequest(async responseHelper => { 17 const intent = responseHelper.getIntentName(); 18 const sessionId = responseHelper.getSessionId(); 19 20 switch (intent) { 21 case 'Clova.YesIntent': 22 // Build speechObject directly for response 23 responseHelper.setSimpleSpeech({ 24 lang: 'ja', 25 type: 'PlainText', 26 value: 'はいはい', 27 }); 28 break; 29 case 'Clova.NoIntent': 30 // Or build speechObject with SpeechBuilder for response 31 responseHelper.setSimpleSpeech( 32 clova.SpeechBuilder.createSpeechText('いえいえ') 33 ); 34 break; 35 } 36 }) 37 .onSessionEndedRequest(responseHelper => { 38 const sessionId = responseHelper.getSessionId(); 39 40 // Do something on session end 41 }) 42 .handle(); 43 44const app = new express(); 45const clovaMiddleware = clova.Middleware({ applicationId: "YOUR_APPLICATION_ID" }); 46// Use `clovaMiddleware` if you want to verify signature and applicationId. 47// Please note `applicationId` is required when using this middleware. 48app.post('/clova', clovaMiddleware, clovaSkillHandler); 49 50// Or you can simply use `bodyParser.json()` to accept any request without verifying, e.g., 51//app.post('/clova', bodyParser.json(), clovaSkillHandler); 52 53exports.clova = functions.https.onRequest(app); 54
デプロイ
デプロイ時にcd ../ firebase deploy
Would you like to proceed with deletion? Selecting no will continue the rest of the deployments.
と聞かれた場合は、Yを入力。 Firebaseコンソールをリロードし、「clova」関数が出来ていることを確認します。
動作確認
Custom Extensionの表示
Clova Developer Centerを開き、スキル設定画面を開きます
https://clova-developers.line.me/cek/#/list
そして、該当のスキルの「対話モデル」の「修正」を押下します。テスト
スキルのCustom Extensionが表示されるので、サイドメニューから、「テスト」を押下すると、 テスト画面が表示されるので「発話内容」に「はい」を入力、「サービスの応答」で「はいはい」と返ってくれば正しく接続されています。 実機でも同様にスキルを起動後に「はい」と発話すると「はいはい」と返ってくるはずです。ログ確認
また、Firebaseコンソールのログ画面より、実行ログを確認することができます。
Forkした理由
firebaseでexpressを使った際の仕様として、requestオブジェクトの値が文字列でなく、自動的にObjectで返ってくるようになってます。
https://firebase.google.com/docs/functions/http-events?hl=ja#read_values_from_the_request
そして、ClovaのSDK側では文字列で受け取る前提の実装となっている為、以下のようなエラーが発生します。
TypeError: Data must be a string or a buffer
at Verify.update (crypto.js:99:16)
at checkSignature (/srv/clov.js:647:10)
at /srv/clov.js:688:21
at step (/srv/clov.js:54:23)
at Object.next (/srv/clov.js:35:53)
at fulfilled (/srv/clov.js:25:58)
at <anonymous>
at process._tickDomainCallback (internal/process/next_tick.js:228:7)
https://github.com/TanakaMidnight/clova-cek-sdk-nodejs/commit/0477cda4d5c5e6d1891d0ddf3598eda49aa3cc6b
また、SDK触りたくない場合には、「clovaMiddleware」処理しちゃう前に、
request.bodyの値を変換前のbodyの値であるrequest.rawBodyで上書きしちゃえばいけそうです。
(試してないですが。。。)
以上、Firebase Cloud Functions(Node.js version 8)でClova CEK SDK Nodejsを動かしてみた、でした。