RustとLambdaでClovaスキル作る 前編

RustとLambdaでClovaスキル作る 前編:

前回、google homeアプリ作りました。
RustとLambdaでなんか作る 後編 (リクガメの食べ物しらべるghomeアプリ作った)

今回も手段が目的です。

ちょっと難易度上げてClovaスキルに同じやつを作ってみましょう。

Dialogflowがない分、作りこみが大変です。

汎用性もちょっと考えつつ、少し時間をかけてつくる。


下調べ1 clovaのreqestとレスポンス

他に公開してるクイズがあるのでそれのテスト画面でチェックしてみました。


Request

{ 
    "version": "1.0", 
    "session": { 
        "sessionId": "37564251-297a-4961-8f02-xxxxxxxx", 
        "sessionAttributes": {}, 
        "user": { 
            "userId": "xxxxxxxx-K-fZUQ", 
            "accessToken": "ead11e3a-a7c6-44de-a2fb-xxxxxxx" 
        }, 
        "new": true 
    }, 
    "context": { 
        "System": { 
            "application": { 
                "applicationId": "tokyo.ikegami.xxxxx" 
            }, 
            "user": { 
                "userId": "xxxxxxxx-K-fZUQ", 
                "accessToken": "ead11e3a-a7c6-44de-a2fb-xxxxxxxx" 
            }, 
            "device": { 
                "deviceId": "b323e077-56ef-4c83-98ee-xxxxxxxx", 
                "display": { 
                    "size": "l100", 
                    "orientation": "landscape", 
                    "dpi": 96, 
                    "contentLayer": { 
                        "width": 640, 
                        "height": 360 
                    } 
                } 
            } 
        } 
    }, 
    "request": { 
        "type": "IntentRequest", 
        "intent": { 
            "name": "AnswerIntent", 
            "slots": { 
                "answer": { 
                    "name": "answer", 
                    "value": "キッシュ" 
                } 
            } 
        } 
    } 
} 


response

例1
{ 
    "response": { 
        "card": {}, 
        "directives": [], 
        "outputSpeech": { 
            "type": "SimpleSpeech", 
            "values": { 
                "lang": "ja", 
                "type": "PlainText", 
                "value": "キッシュって10回言ってみて!" 
            } 
        }, 
        "reprompt": { 
            "outputSpeech": { 
                "type": "SimpleSpeech", 
                "values": { 
                    "lang": "ja", 
                    "type": "PlainText", 
                    "value": "キッシュだよ。10回ね" 
                } 
            } 
        }, 
        "shouldEndSession": false 
    }, 
    "sessionAttributes": { 
        "cnta": 0, 
        "cntq": 0, 
        "history": [], 
        "quiz": { 
            "a": [ 
                "タルト", 
                "たると", 
                "tart" 
            ], 
            "chanq": "キッシュ", 
            "i": "\n繰り返しワード:キッシュ\nキッシュも美味しいけどキッシュはお菓子じゃないよね。いちごタルトが王道?\n考案者:ゆみちゃん", 
            "key": 13, 
            "q": "パートシュクレという生地で作った器の上に、果物などを盛り付けたお菓子はなーんだ?" 
        } 
    }, 
    "version": "1.0" 
} 
例2
{ 
    "response": { 
        "card": {}, 
        "directives": [], 
        "outputSpeech": { 
            "type": "SimpleSpeech", 
            "values": { 
                "lang": "ja", 
                "type": "PlainText", 
                "value": "遊んでくれてありがとう。またやってね!" 
            } 
        }, 
        "shouldEndSession": true 
    }, 
    "sessionAttributes": {}, 
    "version": "1.0" 
} 


下調べ2 Rustとjson

さて、もうちょっといいjsonの扱い方はないもんか。

前回みたいに階層ごとにstruct作ってたらめっちゃややこい事になる。

でもまあ、serde-rs/jsonみてたら事前に定義してなくてもなんとかなるっぽかった。

main.rs
use std::error::Error; 
 
use lambda_http::{lambda, Request, Response, Body}; 
use lambda_runtime::{error::HandlerError, Context}; 
use log::{self, info}; 
use simple_logger; 
 
extern crate serde_derive; 
extern crate serde; 
extern crate serde_json; 
 
fn main() -> Result<(), Box<dyn Error>> { 
    simple_logger::init_with_level(log::Level::Info).unwrap(); 
    lambda!(handler); 
 
    Ok(()) 
} 
 
fn handler(e: Request, _c: Context) -> Result<Response<Body>, HandlerError> { 
  let v: serde_json::Value = serde_json::from_slice(e.body().as_ref()).unwrap(); 
  info!("{:#?}", v); 
  Ok( 
    Response::builder() 
     .status(200) 
     .header("Content-Type", "application/json; charset=UTF-8") 
     .body( 
        format!("{}", serde_json::json!({ 
          "response": { 
              "card": {}, 
              "directives": [], 
              "outputSpeech": { 
                  "type": "SimpleSpeech", 
                  "values": { 
                      "lang": "ja", 
                      "type": "PlainText", 
                      "value": v["request"]["type"] 
                  } 
              }, 
              "shouldEndSession": true 
          }, 
          "sessionAttributes": {}, 
          "version": "1.0" 
        })).into(), 
      ) 
     .expect("none") 
  ) 
} 
作成したら npx serverless deployします。

サーバーレスのテンプレは前回と同じく、https://github.com/softprops/serverless-aws-rustです。


clovaスキル作ってみる


  1. https://clova-developers.line.biz/ を開き、ログインします。
  2. スキル設定を開き、新規チャネル作成(プロバイダがない場合はそれも)
  3. チャネル名を「リクガメの食べ物」としました。
  4. 基本情報とか入力
  5. webhookのURLは作成したlambdaのURLに
  6. で、対話モデルは下記の感じでとりあえず。


65.png


foodSlotというのを作って、それには小松菜みたいなフード名を入れる想定、

それをaskIntentで拾う想定です。

設定したら一回ビルドしておきます。

で、テスト画面で試すと・・



66.png


ちゃんと返ってきました。

お、意外とスムーズか?


よし、一回リリースしておこう。

なんか意外といけるかもしれないので、だいたい作って申請してしまいました。

ちょっとシンプルすぎてどうかなとは思うけど。

ソースは雑なので次の後編で申請結果と共にさらしますよ。

ちなみにjsonのStringにダブルクォートがついたままになってて、その状態で無理やり比較している苦しい状況。。来週まで時間取れそうにないんだけど、来週にちゃんと整えよう。

というわけで前編はここまで。

コメント

このブログの人気の投稿

投稿時間:2021-06-17 05:05:34 RSSフィード2021-06-17 05:00 分まとめ(1274件)

投稿時間:2021-06-20 02:06:12 RSSフィード2021-06-20 02:00 分まとめ(3871件)

投稿時間:2020-12-01 09:41:49 RSSフィード2020-12-01 09:00 分まとめ(69件)