かずきのBlog@hatena

すきな言語は C# + XAML の組み合わせ。Azure Functions も好き。最近は Go 言語勉強中。日本マイクロソフトで働いていますが、ここに書いていることは個人的なメモなので会社の公式見解ではありません。

タイプセーフな Clova Extension の開発環境を求めて on Azure Functions

やってみた。実用に耐えうるかはわからないけどメモ。

環境構築

やってみよう

Visual Studio Code の左のメニューから Azure のアイコンを選択。自分の Azure のアカウントでログインしてたら使えるはずです。(ログインしてない状態だとどうなってるのか把握できていない)

FUNCTIONS の部分にある Create New Project... を選択します。

f:id:okazuki:20180910165751p:plain

フォルダを選択して言語は JavaScript を選択します。

ターミナルを出して以下のコマンドを打ちます。(Windows だと Ctrl + Shift + ` で出せます)

npm init -y
npm i express
npm i @line/clova-cek-sdk-nodejs
npm i azure-function-express
npm i -D @types/node
tsc -init

azure-function-express は、型定義がないので追加します。とりあえず、使う関数は1つなのでさくっと自作しちゃいます。

@types/azure-function-express/index.d.ts をプロジェクトの下に作成して以下の内容を定義します。

import express from 'express';
declare module 'azure-function-express' {
    export function createHandler(app: express.Express): Function;
}

次に tsconfig.json を以下のように編集しておきます。

{
  "compilerOptions": {
    "target": "es5",                          /* Specify ECMAScript target version: 'ES3' (default), 'ES5', 'ES2015', 'ES2016', 'ES2017','ES2018' or 'ESNEXT'. */
    "module": "commonjs",                     /* Specify module code generation: 'none', 'commonjs', 'amd', 'system', 'umd', 'es2015', or 'ESNext'. */
    "lib": [
      "es2015"
    ],
    "strict": true,                           /* Enable all strict type-checking options. */
    "sourceMap": true,
    "baseUrl": "./",                       /* Base directory to resolve non-absolute module names. */
    "paths": {
      "*": ["@types/*"]
    },                           /* A series of entries which re-map imports to lookup locations relative to the 'baseUrl'. */
    "esModuleInterop": true                   /* Enables emit interoperability between CommonJS and ES Modules via creation of namespace objects for all imports. Implies 'allowSyntheticDefaultImports'. */
  }
}

tsconfig.json を編集したら Visual Studio Code を再起動しておきましょう。

続けて関数を作ります。プロジェクトを作成したときのボタンの横の方に Create Function... というボタンがあるので押します。

f:id:okazuki:20180910170826p:plain

自分のプロジェクトのフォルダを選択して Http trigger を選択して名前に clova と入力して Enter を押します。認証は Function あたりを選んでおきましょう。Anonymous にするとインターネットにさらけ出す感じになります。Function を選ぶと URL のクエリに関数を呼び出すために必要なキー文字列が必要になります。(まぁ Clova のライブラリをちゃんと使ってれば不正なリクエストは弾いてくれるので構わないといえば構わない)

clova フォルダが作成されるので、その中の index.js を削除して index.ts を作成します。中身はこんな感じです。

import { createHandler } from 'azure-function-express';
import * as clova from '@line/clova-cek-sdk-nodejs';
import express from 'express';

const skillHandler = <express.RequestHandler>clova.Client
    .configureSkill()
    .onLaunchRequest((responseHelper: clova.Context) => {
        responseHelper.setSimpleSpeech(
            clova.SpeechBuilder.createSpeechText('おはよう')
        );
    })
    .onIntentRequest((responseHelper: clova.Context) => {
        const intent = responseHelper.getIntentName();
        switch (intent) {
            case 'ThrowDiceIntent':
                responseHelper.setSimpleSpeech(
                    clova.SpeechBuilder.createSpeechText('サイコロをふるね')
                );
                break;
            default:
                responseHelper.setSimpleSpeech(
                    clova.SpeechBuilder.createSpeechText('何かインテントが来たよ')
                );
                break;
        }
    })
    .onSessionEndedRequest((responseHelper: clova.Context) => {
        // do something on session end
    })
    .handle();

const app = express();
const clovaMiddleware = clova.Middleware({ applicationId: "YOUR_APPLICATION_ID" });
app.post('/api/clova',
    (req, res, next) => {
        req.body = (<any>req).rawBody; // Clova のミドルウェアを azure-function-express で動かすためのおまじない
        next();
    },
    clovaMiddleware,
    skillHandler);

export = createHandler(app);

インテリセンス(MS 用語、コードの補間のこと)も出るよ!

f:id:okazuki:20180910173732p:plain

tsc コマンドあたりでコンパイルしてみましょう。VSCode で Ctrl + Shift + B でもコンパイルするタスクが出てきます。

実行して動作確認

ローカル実行

F5 を押すとデバッグ実行になります。ngrok http 7071 を実行してインターネットからアクセスできるようにしましょう。

Azure Functions v1 の場合の ngrok について

オプションが必要なので、これを見てね。

blog.okazuki.jp

LINE Clova の Extension から動かしてみて動作確認

サーバーの URL を ngrok で取得した URL に対して /api/clova を追加したものを設定して動かします。 index.ts.js の 33 行目の YOUR_APPLCIATION_ID のところは自分の値に書き換えてから実行してください。

うまくいけば動きます。またソースマップも出すように tsconfig.json を構成しているので、ちゃんと index.ts に対してブレークポイントをはれば以下のようにデバッグも出来ます。

f:id:okazuki:20180910173549p:plain

Azure にデプロイ

‘Deploy to Function App...` からデプロイ出来ます。

f:id:okazuki:20180910173935p:plain

既存のものにデプロイしたり、新しいものにデプロイすることも出来ます。

デプロイしたら、関数を選んで </> 関数の URL の取得 を押したら出てくる画面で関数の URL をゲットしましょう。

f:id:okazuki:20180910174602p:plain

この URL を LINE の Clova の Extension のサーバー設定に入力します。

まとめ

なるべくタイプセーフになるように TypeScript を使って Azure Functions にデプロイして試してみました。 意外と簡単だったのでいいかもしれない。

コードは以下のリポジトリにあげてます。clone してから local.settings.json ファイルを以下の内容で作成すると動くと思います。

{
  "IsEncrypted": false,
  "Values": {
    "AzureWebJobsStorage": "",
    "FUNCTIONS_WORKER_RUNTIME": "node"
  }
}

github.com