Alexa Skills Kit SDK for Node.js v2 入門(DB保存&まとめ)

前回のブログ

 

xp-cloud.jp

xp-cloud.jp

xp-cloud.jp

xp-cloud.jp

 

今回はデータを保存する方法と、最終的なテンプレートを紹介します。
もう忘れてるかもしれませんが、この入門シリーズはテンプレートを作ってみようという話でした。

データの保存はDynamoDBを使用します。

 

データの保存

前回使用していたindex.jsのコードを次のように変更してください。

 

const LaunchRequestHandler = {
    canHandle(handlerInput) {
        console.log('called LaunchRequestHandler.canHandle');
        return handlerInput.requestEnvelope.request.type === 'LaunchRequest';
    },
    async handle(handlerInput) {
        console.log('called LaunchRequestHandler.handle');

        //DynamoDBから取得
        let persistentAttributes = await handlerInput.attributesManager.getPersistentAttributes();
        let cnt = (persistentAttributes.cnt | 0) + 1;

        //DynamoDBへ保存
        handlerInput.attributesManager.setPersistentAttributes({cnt:cnt});
        await handlerInput.attributesManager.savePersistentAttributes();

        return handlerInput.responseBuilder
            .speak(`スキルを起動しました。このスキルは${cnt}回起動しました`)
            .reprompt('こんにちは、と言ってください')
            .getResponse();
    }
};

exports.handler = Alexa.SkillBuilders.standard()
    .addRequestHandlers(LaunchRequestHandler, //起動リクエス
(略)
    .withTableName("xp_yamaguchi") //PersistentAttributesで使うテーブル名
    .withAutoCreateTable(true) //テーブルを作る
    .lambda();

 

"withTableName"でDynamoDBのテーブル名を指定し、
"withAutoCreateTable"でテーブルを作成します。
"getPersistentAttributes"でテーブルの内容を取得し、
"setPersistentAttributes"、"savePersistentAttributes"でテーブルへ保存します。

DBに詳しい方なら不思議に思うでしょうが、このメソッドで取得・保存する際に検索条件は指定しません。

このメソッドで操作できる情報は自身の情報だけだからです。

ではどういった流れになるのか見てみましょう。

 

Alexa Skills Kit SDK for Node.js v2 入門 流れ

基本的には起動リクエストを呼んだだけの処理になります。
途中、DynamoDBから"cnt"を取得して1足して保存しています。

DynamoDBを見てみるとデータが保存されていることが分かります。

 

DynamoDB

 

テンプレート

さて、いよいよベースとなる知識は揃ったので、それらを全て入れたテンプレートを紹介します。

 

const Alexa = require('ask-sdk');

//起動リクエスト
const LaunchRequestHandler = {
    canHandle(handlerInput) {
        console.log('called LaunchRequestHandler.canHandle');
        return handlerInput.requestEnvelope.request.type === 'LaunchRequest';
    },
    handle(handlerInput) {
        console.log('called LaunchRequestHandler.handle');
        return handlerInput.responseBuilder
            .speak('スキルを起動しました。こんにちは、と言ってください')
            .reprompt('こんにちは、と言ってください')
            .getResponse();
    }
};

//ヘルプインテント
const HelpIntentHandler = {
    canHandle(handlerInput) {
        console.log('called HelpIntentHandler.canHandle');
        return handlerInput.requestEnvelope.request.type === 'IntentRequest'
                && handlerInput.requestEnvelope.request.intent.name === 'AMAZON.HelpIntent';
    },
    handle(handlerInput) {
        console.log('called HelpIntentHandler.handle');
        return handlerInput.responseBuilder
            .speak('挨拶をしますので、こんにちは、と言ってください。さぁどうぞ。')
            .reprompt('こんにちは、と言ってください。さぁどうぞ。')
            .getResponse();
    }
};

//ストップorキャンセルのインテント
const CancelAndStopIntentHandler = {
    canHandle(handlerInput) {
        console.log('called CancelAndStopIntentHandler.canHandle');
        return handlerInput.requestEnvelope.request.type === 'IntentRequest'
              && (handlerInput.requestEnvelope.request.intent.name === 'AMAZON.CancelIntent'
                || handlerInput.requestEnvelope.request.intent.name === 'AMAZON.StopIntent');
    },
    handle(handlerInput) {
        console.log('called CancelAndStopIntentHandler.handle');

        return handlerInput.responseBuilder
            .getResponse();
    }
};

//repromptしたのに命令が無い場合のハンドラー
const SessionEndedRequestHandler = {
    canHandle(handlerInput) {
        console.log('called SessionEndedRequestHandler.canHandle');
        return handlerInput.requestEnvelope.request.type === 'SessionEndedRequest';
    },
    handle(handlerInput) {
        console.log('called SessionEndedRequestHandler.handle');

        return handlerInput.responseBuilder.getResponse();
    }
};

//インテント処理が始まる前の処理
const RequestInterceptor = {
    process(handlerInput) {
        return new Promise((resolve, reject) => {
            console.log('called RequestInterceptor');
            console.log(JSON.stringify(handlerInput));

            //正常時
            resolve();

            //異常時
            //reject('error');
        });
    }
};

//インテント処理が終わった後の処理
const ResponseInterceptor = {
    process(handlerInput) {
        return new Promise((resolve, reject) => {
            console.log('called ResponseInterceptor');
            console.log(JSON.stringify(handlerInput));

            //正常時
            resolve();

            //異常時
            //reject('error');
        });
    }
};

//エラー処理
const ErrorHandler = {
    canHandle(handlerInput, error) {
        console.log('called ErrorHandler.canHandle');
        return true;
    },
    handle(handlerInput, error) {
        console.log('called ErrorHandler.handle');
        console.log(JSON.stringify({handlerInput:handlerInput, error:error}));
        return handlerInput.responseBuilder
            .speak('エラーが発生しました')
            .getResponse();
    }
};

//スキル独自のインテント.カスタムスキルでは最低1つは必要
const HelloIntentHandler = {
    canHandle(handlerInput) {
        console.log('called HelloIntentHandler.canHandle');
        return handlerInput.requestEnvelope.request.type === 'IntentRequest'
            && handlerInput.requestEnvelope.request.intent.name === 'HelloIntent';
    },
    handle(handlerInput) {
        console.log('called HelloIntentHandler.handle');

        return handlerInput.responseBuilder
            .speak('こんにちはインテントが呼ばれました。')
            .getResponse();
    }
};

exports.handler = Alexa.SkillBuilders.standard()
    .addRequestHandlers(LaunchRequestHandler,      //起動リクエスト
                        HelpIntentHandler,         //ヘルプインテント
                        CancelAndStopIntentHandler,//ストップorキャンセルのインテント
                        SessionEndedRequestHandler,//repromptしたのに命令が無い場合のインテント
                        HelloIntentHandler)     //スキル独自のインテント.カスタムスキルでは最低1つは必要
    .addRequestInterceptors(RequestInterceptor)    //インテント処理が始まる前の処理
    .addResponseInterceptors(ResponseInterceptor)  //インテント処理が終わった後の処理
    .addErrorHandlers(ErrorHandler)                //エラー処理
    .withTableName("ASK_SDK")  //テーブル名
    .withAutoCreateTable(true) //テーブルを作る
    .lambda();

 

必要最低限のリクエストハンドラー、
前処理、後処理、
エラーハンドラー、
DynamoDBのテーブル名の設定、
各処理のログ出力を詰め込んだテンプレートになっています。

処理は「こんにちは」というだけで特に処理をしていないシンプルな物になっています。
DynamoDBへ保存しなければテーブルも作られないため、このままでもテーブルは作成されませんが、気になるようなら"withTableName"と"withAutoCreateTable"は削除してしまっても構いません。

 

あとがき

最初は覚えることが多いASK SDK v2ですが、一度テンプレートを作ってしまえば後は処理を追加していくだけで簡単にスキルは作成できます。
このテンプレートを元に、みなさんもスキル公開にチャレンジしてみてください。

 

 

 

AWS相談会