このセクションでは、Cake Timeスキルでユーザーの次の誕生日までの日数を計算できるようにします。
ブラウザで Alexa開発者コンソールを開きます。コンソールにログインし、Cake Timeスキルを開きます。
ユーザーの次の誕生日までの日数を計算するには、追加の情報が必要です。こうした情報は、Alexa設定APIという便利な機能を使って入手できます。APIに照会するには、Cake Timeスキルが起動されたAlexa搭載デバイスのIDを指定する必要があります。
デバイスIDは、スキルコードが受け取るすべてのリクエストに含まれます。 requestEnvelopeを使ってデバイスIDを取得します。コードは次のようになります:
handlerInput.requestEnvelope.context.System.device.deviceId
SDKには、デバイスIDの取得を簡易にするユーティリティ関数が用意されています。上記の代わりに次のコードを使っても構いません。const deviceId = Alexa.getDeviceId(handlerInput.requestEnvelope)
詳細については、ASK SDKユーティリティを参照してください。
a. 開発者コンソールで、コードエディタタブをクリックします。
b. HasBirthdayLaunchRequestHandlerハンドラーのhandle()関数を探します。const attributesManagerで始まる行のすぐ上に、新しい行を追加します。次のコードをコピーして貼り付けます。:
const deviceId = handlerInput.requestEnvelope.context.System.device.deviceId;
次に、Cake Timeスキルは、取得したデバイスIDをAlexa設定APIに渡す必要があります。
それには、次の4つの情報が必要です:
SDKには、便利なビルトインのServiceClientがあります。ServiceClientを設定して使用するだけで、これらの情報を入手できます。
c. exports.handlerで始まる行まで、コードを下にスクロールします。すぐ下に新しい行を追加し、次のコードをコピーして貼り付けます:
.withApiClient(new Alexa.DefaultApiClient())
ServiceClientの設定が完了したら、コードはAPIにデバイスIDを渡すために必要な4つの情報を入手できます。
d. 保存をクリックします。
ServiceClientを使うには、ファクトリーを作成します。ファクトリーの目的は何かを作ることです。このファクトリーは、ServiceClientの作成に使います。
a. HasBirthdayLaunchRequestHandlerハンドラーのhandle()関数を探します。const deviceIdで始まる行のすぐ上に、新しい行を追加します。次のコードをコピーして貼り付けます:
const serviceClientFactory = handlerInput.serviceClientFactory;
コードのセクションは次のようになります:
b. 保存をクリックします。
コードからAlexa設定APIを呼び出すと、エラーが発生する場合があります。たとえば、APIの応答までに非常に長い時間がかかると、コードはタイムアウトする可能性があります。このため、コードをtry/catchブロックでラップする必要があります。try/catchブロックは、エラーが発生してもスキルのコードが異常終了しないようにする方法の1つです。異常終了する可能性のあるコードをtryブロックでラップします。そのブロック内のコードが異常終了すると、catchブロックが実行されてエラーを処理します。
ユーザーのAlexa搭載デバイスに設定されたタイムゾーンを取得する場合、tryブロックで、serviceClientFactoryを使って設定サービスクライアント(upsServiceClient)を取得し、デバイスIDをgetSystemTimeZone関数に渡してタイムゾーンを取得します。catchブロックはconsole.logにエラーメッセージを記録し、Alexaがユーザーに読み上げるエラーメッセージ応答を返します。
a. HasBirthdayLaunchRequestHandlerハンドラーのhandle()関数を探します。const day = sessionAttributesで始まる行のすぐ下に、新しい行を追加します。次のコードをコピーして貼り付けます:
let userTimeZone;
try {
const upsServiceClient = serviceClientFactory.getUpsServiceClient();
userTimeZone = await upsServiceClient.getSystemTimeZone(deviceId);
} catch (error) {
if (error.name !== 'ServiceError') {
return handlerInput.responseBuilder.speak("サービスへの接続がうまく行きませんでした。").getResponse();
}
console.log('error', error.message);
}
APIを使ってタイムゾーンを取得するため、応答を取得するまでに時間がかかる可能性があります。情報を非同期に取得するようコードを更新します。
前回既に、asyncキーワードとawaitキーワードを使っています。これらのキーワードをここでも使用して、APIから非同期的にタイムゾーンを取得します。awaitキーワードを先ほど追加したtry/catchブロックに含めます。次に、awaitキーワードをhandle()関数に追加します。
b. HasBirthdayLaunchRequestHandlerハンドラーのhandle()関数を探します。この関数の先頭に、async、スペースと入力します。
コードのセクションは次のようになります:
async handle(handlerInput) {
const serviceClientFactory = handlerInput.serviceClientFactory;
const deviceId = handlerInput.requestEnvelope.context.System.device.deviceId;
c. 保存をクリックします。
コードでタイムゾーンを使い、現在の日付を取得します。currentDateTime関数を使うと、ユーザーのデバイスから取得したタイムゾーンに基づいて現在の日付が返されます。この関数を、前のステップで追加したtry/catchブロックの下に追加します。
a. HasBirthdayLaunchRequestHandlerのhandle()関数で、const speechText = `おかえりなさいで始まる行を探します。すぐ上に新しい行を追加します。次のコードをコピーして貼り付けます:
// 現在の日付と時刻を取得します
const currentDateTime = new Date(new Date().toLocaleString("ja-JP", {timeZone: userTimeZone}));
b. 保存をクリックします。
Cake Timeスキルを、ユーザーのタイムゾーンで誕生日を迎える真夜中におめでとうと言うようにしたいと思います。currentDateTimeは、日付と時刻(秒単位まで)を提供するため、そのままではうまく行きません。Cake Timeスキルは、ユーザーに秒単位まで細かく誕生日をたずねないからです。このため、コードでcurrentDateTimeから年、月、日のみを抽出し、秒なしで日付を作り直す必要があります。
a. HasBirthdayLaunchRequestHandlerのhandle()関数で、先ほど追加したコード(const currentDateTime)のすぐ下に新しい行を作成します。次のコードをコピーして貼り付けます。
// 日数計算の結果に影響するため、日付から時刻を取り除きます
const currentDate = new Date(currentDateTime.getFullYear(), currentDateTime.getMonth(), currentDateTime.getDate());
const currentYear = currentDate.getFullYear();
b. 保存をクリックします。
次に、コードでユーザーの次の誕生日を判断する必要があります。まず、今年の誕生日の年と月を組み合わせます。次に、今年、既にユーザーの誕生日を過ぎているかどうかを判断します。もし過ぎていれば、コードで次の誕生日の値に1年を足します。
a. HasBirthdayLaunchRequestHandlerのhandle()関数で、先ほど追加した行(const currentDateとconst currentYear)のすぐ下に新しい行を作成します。次のコードをコピーして貼り付けます:
// 次の誕生日を取得します
let nextBirthday = Date.parse(`${month} ${day}, ${currentYear}`);
// 現在の日付が誕生日よりも後の場合、nextBirthdayに1年足します
if (currentDate.getTime() > nextBirthday) {
nextBirthday = Date.parse(`${month} ${day}, ${currentYear + 1}`);
}
b. 保存をクリックします。
コードで現在の日付とユーザーの次の誕生日を取得できたら、差を計算します。まず、それぞれの日付をUNIX時間(協定世界時(UTC)1970年1月1日00:00:00からの経過秒数から、うるう秒を引いたもの)に変換する必要があります。
次に、2つの日付間の差をミリ秒で計算して、その絶対値を求めます。
次に、算出した差をミリ秒から日数に変換し直します。1日(ミリ秒)= 24時間 X 60分 X 60秒 X 1000ミリ秒となります。
コードは次のようになります。ただし、まだ実際のコードに追加しないでください:
const oneDay = 24*60*60*1000;
const diffDays = Math.round(Math.abs((currentDate.getTime() - nextBirthday)/oneDay));
誕生日当日でなければ、コードは差を計算するだけです。このため、このコードを現在の日付がユーザーの誕生日かどうかチェックするif文にラップします。
誕生日であれば、スキルにおめでとうと言わせる必要があります。そのコードを実装するには、ユーザーの誕生日でない場合は次の誕生日までの日数を伝える内容をspeechTextにセットするというif文を追加します。
a. HasBirthdayLaunchRequestHandlerのhandle()関数で、const speechText = `おかえりなさいで始まる行を探します。この行を以下のコードに置き換えます:
const oneDay = 24*60*60*1000;
// デフォルトのspeechTextを「X歳の誕生日、おめでとうございます」に置き換えます。
let speechText = `${currentYear - year}歳のお誕生日、おめでとうございます!`;
if (currentDate.getTime() !== nextBirthday) {
const diffDays = Math.round(Math.abs((currentDate.getTime() - nextBirthday)/oneDay));
speechText = `おかえりなさい、${currentYear - year}歳のお誕生日まで、残り${diffDays}日です。`
}
b. 保存をクリックします。
a. デプロイをクリックしてスキルをビルドします。
b. テストタブでスキルを起動し、Alexaがあなたの次の誕生日までの日数を言うかどうか確認してみましょう。うまく言えたなら、おめでとうございます。
* もしテストで”There was a problem connecting to the service”の応答になる場合は、開発者アカウントの住所が正しく設定されていない可能性があります。amazon.co.jpに開発者アカウントでサインインし、「アカウントサービス」 > 「ログインとセキュリティ」のセクションで住所の設定を確認してください。
スキルが動作しない、または構文エラーが出るといった問題がある場合は、以下の完成したコードサンプルをダウンロードしてください。Alexa開発者コンソールのコードエディタタブを開き、このコードをindex.jsファイルにコピーして貼り付けます。テストする前に、必ずコードを保存してデプロイしてください。