【完結】2時間でAlexaスキルを作ってみたけどできなかった話 その4

はじめに

もともとやろうとしていた「面接練習スキル」を当初計画通り実装することは難しいことが分かった。

そのため、いくつか割り切って以下の仕様とする。とりあえず完成を目標にする。

  • Alexaスキルは、ランダムで面接のお題を読み上げる
  • ユーザは、お題に沿って回答する。ただ、回答内容は記録されない

やってみたこと

  • 質問の内容をリストアップ
  • コードエディタで実装
  • テスト

質問内容

  • 志望理由は何ですか?
  • あなたの強みはなんですか?
  • あなたの弱みはなんですか?
  • 最近苦労したことはなんですか?
  • 最近楽しかったことはなんですか?
  • 今後やりたいことはなんですか?
  • 何か質問ありますか?ふふふ
  • いまのあなたの課題はなんですか?
  • どのようなリーダーシップの経験がありますか?
  • 仕事で大事にしていることはなんですか?

実装

コードの編集

StartIntentHandler」が呼ばれたら、ランダムに質問が1つ読み上げられるようにする。

また、「HelpIntentHandler」が呼ばれたら、使い方を説明するようにする。

それらを実装したコード全体はこちら:

/* *
 * This sample demonstrates handling intents from an Alexa skill using the Alexa Skills Kit SDK (v2).
 * Please visit https://alexa.design/cookbook for additional examples on implementing slots, dialog management,
 * session persistence, api calls, and more.
 * */
const Alexa = require('ask-sdk-core');

const LaunchRequestHandler = {
    canHandle(handlerInput) {
        return Alexa.getRequestType(handlerInput.requestEnvelope) === 'LaunchRequest';
    },
    handle(handlerInput) {
        const speakOutput = 'これから面接練習をします。質問に回答してください。よろしいですか?';

        return handlerInput.responseBuilder
            .speak(speakOutput)
            .reprompt(speakOutput)
            .getResponse();
    }
};

const StartIntentHandler = {
    canHandle(handlerInput) {
        return Alexa.getRequestType(handlerInput.requestEnvelope) === 'IntentRequest'
            && Alexa.getIntentName(handlerInput.requestEnvelope) === 'StartIntent';
    },
    handle(handlerInput) {
        // 質問リスト
        const array = [
            "志望理由は何ですか?",
            "あなたの強みはなんですか?",
            "あなたの弱みはなんですか?",
            "最近苦労したことはなんですか?",
            "最近楽しかったことはなんですか?",
            "今後やりたいことはなんですか?",
            "何か質問ありますか?ふふふ",
            "いまのあなたの課題はなんですか?",
            "どのようなリーダーシップの経験がありますか?",
            "仕事で大事にしていることはなんですか?",
        ]
        const min = Math.ceil(0);
        const max = Math.floor(array.length - 1);
        let randomInt = Math.floor(Math.random() * (max - min) + min);

        const speakOutput = array[randomInt];

        return handlerInput.responseBuilder
            .speak(speakOutput)
            //.reprompt('add a reprompt if you want to keep the session open for the user to respond')
            .getResponse();
    }
};

const HelpIntentHandler = {
    canHandle(handlerInput) {
        return Alexa.getRequestType(handlerInput.requestEnvelope) === 'IntentRequest'
            && Alexa.getIntentName(handlerInput.requestEnvelope) === 'AMAZON.HelpIntent';
    },
    handle(handlerInput) {
        const speakOutput = '面接練習のために、ランダムに質問を出しますので、面接者になったつもりで回答してください。';

        return handlerInput.responseBuilder
            .speak(speakOutput)
            .reprompt(speakOutput)
            .getResponse();
    }
};

const CancelAndStopIntentHandler = {
    canHandle(handlerInput) {
        return Alexa.getRequestType(handlerInput.requestEnvelope) === 'IntentRequest'
            && (Alexa.getIntentName(handlerInput.requestEnvelope) === 'AMAZON.CancelIntent'
                || Alexa.getIntentName(handlerInput.requestEnvelope) === 'AMAZON.StopIntent');
    },
    handle(handlerInput) {
        const speakOutput = 'Goodbye!';

        return handlerInput.responseBuilder
            .speak(speakOutput)
            .getResponse();
    }
};
/* *
 * FallbackIntent triggers when a customer says something that doesn’t map to any intents in your skill
 * It must also be defined in the language model (if the locale supports it)
 * This handler can be safely added but will be ingnored in locales that do not support it yet 
 * */
const FallbackIntentHandler = {
    canHandle(handlerInput) {
        return Alexa.getRequestType(handlerInput.requestEnvelope) === 'IntentRequest'
            && Alexa.getIntentName(handlerInput.requestEnvelope) === 'AMAZON.FallbackIntent';
    },
    handle(handlerInput) {
        const speakOutput = 'Sorry, I don\'t know about that. Please try again.';

        return handlerInput.responseBuilder
            .speak(speakOutput)
            .reprompt(speakOutput)
            .getResponse();
    }
};
/* *
 * SessionEndedRequest notifies that a session was ended. This handler will be triggered when a currently open 
 * session is closed for one of the following reasons: 1) The user says "exit" or "quit". 2) The user does not 
 * respond or says something that does not match an intent defined in your voice model. 3) An error occurs 
 * */
const SessionEndedRequestHandler = {
    canHandle(handlerInput) {
        return Alexa.getRequestType(handlerInput.requestEnvelope) === 'SessionEndedRequest';
    },
    handle(handlerInput) {
        console.log(`~~~~ Session ended: ${JSON.stringify(handlerInput.requestEnvelope)}`);
        // Any cleanup logic goes here.
        return handlerInput.responseBuilder.getResponse(); // notice we send an empty response
    }
};
/* *
 * The intent reflector is used for interaction model testing and debugging.
 * It will simply repeat the intent the user said. You can create custom handlers for your intents 
 * by defining them above, then also adding them to the request handler chain below 
 * */
const IntentReflectorHandler = {
    canHandle(handlerInput) {
        return Alexa.getRequestType(handlerInput.requestEnvelope) === 'IntentRequest';
    },
    handle(handlerInput) {
        const intentName = Alexa.getIntentName(handlerInput.requestEnvelope);
        const speakOutput = `You just triggered ${intentName}`;

        return handlerInput.responseBuilder
            .speak(speakOutput)
            //.reprompt('add a reprompt if you want to keep the session open for the user to respond')
            .getResponse();
    }
};
/**
 * Generic error handling to capture any syntax or routing errors. If you receive an error
 * stating the request handler chain is not found, you have not implemented a handler for
 * the intent being invoked or included it in the skill builder below 
 * */
const ErrorHandler = {
    canHandle() {
        return true;
    },
    handle(handlerInput, error) {
        const speakOutput = 'Sorry, I had trouble doing what you asked. Please try again.';
        console.log(`~~~~ Error handled: ${JSON.stringify(error)}`);

        return handlerInput.responseBuilder
            .speak(speakOutput)
            .reprompt(speakOutput)
            .getResponse();
    }
};

/**
 * This handler acts as the entry point for your skill, routing all request and response
 * payloads to the handlers above. Make sure any new handlers or interceptors you've
 * defined are included below. The order matters - they're processed top to bottom 
 * */
exports.handler = Alexa.SkillBuilders.custom()
    .addRequestHandlers(
        LaunchRequestHandler,
        StartIntentHandler,
        HelpIntentHandler,
        CancelAndStopIntentHandler,
        FallbackIntentHandler,
        SessionEndedRequestHandler,
        IntentReflectorHandler)
    .addErrorHandlers(
        ErrorHandler)
    .withCustomUserAgent('sample/hello-world/v1.2')
    .lambda();
インテントの作成

「StartIntentHandler」に関連付けるためのインテントを以下のように作成

  • StartIntent
    • サンプル発話
      • お願い
      • Yes
      • はい
      • めんせつをはじめて

公開準備

諸々の情報を入れる

次のドキュメントを確認し、問題がないと判断した。

  • ポリシーのテスト | Alexa Skills Kit https://developer.amazon.com/ja-JP/docs/alexa/custom-skills/policy-testing-for-an-alexa-skill.html
  • 認定の要件 | Alexa Skills Kit https://developer.amazon.com/ja-JP/docs/alexa/custom-skills/certification-requirements-for-custom-skills.html#submission-checklist

公開範囲を設定

怒られた

ここで漫然と「実行」を押したところ、次のようになった

もう一度「公開」タブから設定内容を修正しなおす。

どこを直せばよいか検討がつかないが、とりあえずスキルを利用できる国を日本だけに設定してみる

次はエラーが出なくなった

申請メニューから「はい、申請を審査します」を選択

申請されたー

最後に

とりあえずようやく1つ目のスキルができたぞー

<12/17金追記>

申請通らなかった。。

参考

特になし

コメントする

メールアドレスが公開されることはありません。 * が付いている欄は必須項目です