Clova SDK で作ったカスタムスキルを色んなサーバーにデプロイしてみる

Amazon Alexa は AWS Lambda へのデプロイサンプルが、
Google Assitant は Cloud Functions へのデプロイサンプルがありますが、
LINE Clova は「外部からアクセスできるサーバーへデプロイしてください」と指定がありません。

Clova SDK のサンプルは、Express を使った Web アプリケーションになっているので、
heroku 等にデプロイするのが通常の使い方？なのかなと思っています。
[前回の記事](https://qiita.com/chibi929/items/4093bf2ff6cc8f7c5f92) では、heroku へデプロイしました。

そこで今回は [ask-sdk-core の lambda の定義](https://github.com/alexa/alexa-skills-kit-sdk-for-nodejs/blob/2.0.x/ask-sdk-core/lib/skill/factory/BaseSkillFactory.ts#L196) を参考に
Clova SDK を Fork してハンドル関数を追加し、
色々サーバーへデプロイできるようにしてみました。

# AWS Lambda

- ソースコードは Lambda にデプロイ
- POST API は API Gateway で作成

## Fork したコードに関数を追加

https://github.com/chibi929/clova-cek-sdk-nodejs/commit/d658ec99edc5e1de4e878e15fa18f041e5d54a31

## Lambda へデプロイしたコード

- https://github.com/chibi929/hello-clova/tree/master/lambda
    - コードはほとんど [前回の記事](https://qiita.com/chibi929/items/4093bf2ff6cc8f7c5f92) と同様です。(リクエスト/レスポンス界面の部分が異なります)
        - Clova SDK は npm 経由ではなく、Fork した git repository からインストールしています
    - TypeScript を使っているのでビルドとか必要です
 
```ts:index.ts
// この `@line/clova-cek-sdk-nodejs` は Fork した git repository のもの
import { Client, SpeechBuilder, Middleware } from '@line/clova-cek-sdk-nodejs';

const launchHandler = async responseHelper => {
  responseHelper.setSimpleSpeech(
    SpeechBuilder.createSpeechText('おはよう')
  );
};

const intentHandler = async responseHelper => {
  const intent = responseHelper.getIntentName();
  const sessionId = responseHelper.getSessionId();

  switch (intent) {
    case 'KinokoIntent':
      responseHelper.setSimpleSpeech(
        SpeechBuilder.createSpeechText('きのこのこのこ元気の子 from Lambda')
      );
      break;
    case 'Clova.YesIntent':
      responseHelper.setSimpleSpeech(
        SpeechBuilder.createSpeechText('はいはい')
      );
      break;
    case 'Clova.NoIntent':
      responseHelper.setSimpleSpeech(
        SpeechBuilder.createSpeechText('いえいえ')
      );
      break;
    default:
      responseHelper.setSimpleSpeech(
        SpeechBuilder.createSpeechText('なんなん')
      );
      break;
  }
};

const sessionEndedHandler = async responseHelper => { };

// ここの部分が Lambda の handler へ繋がる
// `.handle()` ではなく、追加した関数 `.lambda()` に変更している
export const handle = Client
  .configureSkill()
  .onLaunchRequest(launchHandler)
  .onIntentRequest(intentHandler)
  .onSessionEndedRequest(sessionEndedHandler)
  .lambda();
```

- KinokoIntent のハンドリングで `from Lambda` を付けてわかりやすく。

### デプロイの仕方

- zip で AWS マネジメントコンソールから手動でデプロイ

## API Gateway で POST メソッドを作成

- 詳細は割愛します
- POST メソッドで上記の Lambda へ繋げる
- API のデプロイを忘れないこと

## 動作確認

- CEK のサーバー設定に API Gateway の POST メソッドの URL を設定
- 発話モデルでテスト
<img width="703" alt="lambda.png" src="https://qiita-image-store.s3.amazonaws.com/0/53393/12da7c6d-484a-fc67-4ecc-2826ec272034.png">

# Firebase 

## Fork したコードに関数を追加

https://github.com/chibi929/clova-cek-sdk-nodejs/commit/4e447ef2713333c85290a1ace473824a539d2f37

## Cloud Functions for Firebase にデプロイしたコード

- https://github.com/chibi929/hello-clova/tree/master/firebase/functions
    - こちらもコードはほとんど一緒です
    - `firebase init` で作ったスケルトンにいつものコードを書く


```ts:index.ts
import * as functions from 'firebase-functions';
// この `@line/clova-cek-sdk-nodejs` は Fork した git repository のもの
import { Client, SpeechBuilder, Middleware } from '@line/clova-cek-sdk-nodejs';

const launchHandler = async responseHelper => {
  responseHelper.setSimpleSpeech(
    SpeechBuilder.createSpeechText('おはよう')
  );
};

const intentHandler = async responseHelper => {
  const intent = responseHelper.getIntentName();
  const sessionId = responseHelper.getSessionId();

  switch (intent) {
    case 'KinokoIntent':
      responseHelper.setSimpleSpeech(
        SpeechBuilder.createSpeechText('きのこのこのこ元気の子 from Firebase')
      );
      break;
    case 'Clova.YesIntent':
      responseHelper.setSimpleSpeech(
        SpeechBuilder.createSpeechText('はいはい')
      );
      break;
    case 'Clova.NoIntent':
      responseHelper.setSimpleSpeech(
        SpeechBuilder.createSpeechText('いえいえ')
      );
      break;
    default:
      responseHelper.setSimpleSpeech(
        SpeechBuilder.createSpeechText('なんなん')
      );
      break;
  }
};

const sessionEndedHandler = async responseHelper => { };

// ここの部分が firebase の handler に繋がる
// `.handle()` ではなく、追加した関数 `.firebase()` に変更している
const handle: any = Client
  .configureSkill()
  .onLaunchRequest(launchHandler)
  .onIntentRequest(intentHandler)
  .onSessionEndedRequest(sessionEndedHandler)
  .firebase();

// // Start writing Firebase Functions
// // https://firebase.google.com/docs/functions/typescript
export const clova = functions.https.onRequest(handle);
```

- KinokoIntent のハンドリングで `from Firebase` を付けてわかりやすく。

### デプロイの仕方

- firebase-cli で `npm run deploy`

## 動作確認

- CEK のサーバー設定に Cloud Functions for Firebase で作られた URL を設定
- 発話モデルでテスト
<img width="699" alt="firebase.png" src="https://qiita-image-store.s3.amazonaws.com/0/53393/999f00aa-5e25-4788-66e0-638909fab63e.png">

# Express を使う系 (heroku, IBM Cloud)

- こちらは [前回の記事](https://qiita.com/chibi929/items/4093bf2ff6cc8f7c5f92) 同様、手を加えていない 公式 Clova SDK を使用しています
- 違いはサービス毎に使用する `Procfile` だったり `mafifest.yml` だったり。
- 応答の `from Heroku` だったり `from Bluemix` だったり。

## heroku

- https://github.com/chibi929/hello-clova/tree/master/heroku

### デプロイの仕方

- heroku-cli で `git push heroku master`

### 動作確認

- CEK のサーバー設定に heroku app の Express の URL を設定
- 発話モデルでテスト
<img width="692" alt="heroku.png" src="https://qiita-image-store.s3.amazonaws.com/0/53393/51889e15-b6f9-b555-747e-e156cba3a104.png">


## IBM Cloud

- https://github.com/chibi929/hello-clova/tree/master/bluemix

### デプロイの仕方

- bluemix-cli で `bx app push`

### 動作確認

- CEK のサーバー設定に Cloud Foundry アプリケーション の Express の URL を設定
- 発話モデルでテスト
<img width="695" alt="bluemix.png" src="https://qiita-image-store.s3.amazonaws.com/0/53393/d9a621a3-0675-3379-209b-b366b2d8677f.png">

# まとめ

色々なサーバーにデプロイできました。

npm install で git repository を指定する場合、
ビルド生成物である `dist` ディレクトリをコミットしておかないと、
npm モジュールを import できないので、若干気になるところ。。。
Swift の extension の仕組みみたいので公式のモジュールを拡張できないかな。。。

# 追記

https://github.com/line/clova-cek-sdk-nodejs/pull/5
こちらで、`lambda()` 及び `firebase()` をマージして頂きました。

現在は Fork したブランチを使わなくても、
普通に `npm install @line/clova-cek-sdk-nodejs` して頂ければ、ご利用いただけます。