Alexa Alexaに自発的に喋って欲しかったんだ
なぜやろうとしたのか
基本Alexaは発話→レスポンスという動きしかしません。
どんなスキルであっても必ずユーザーが
「Alexa○○して」といった風に話しかけて初めて動き出します。
ただUnityひいてはゲームのAIと言ったら
特定の条件下になったら今の状況を教えてくれたり、
かるく実況じみた発言をしたりと何かと勝手に喋ります。
やはりUnityでゲーム開発にAlexaを組み込むんだから
こういったことをしてほしいわけです。
どうやってやろうとしたか
基本受け身なAlexaですが、
いくつか能動的に動いてくれる場面があります。
それがこれらです。
・通知
・リマインダー
通知はあった場合黄色に光り
通知の有無を確認したら教えてくれます。
一見よさそうですが確認が必要で、
最終的に受動的になっているので今回は却下です。
次にリマインダーです。
予め予定を入れておいて、
その予定が来たら教えてくれるってやつですね。
これもユーザーが予定入れなきゃいけないので
受動的ではあるんですが、
予定を入れるのはプログラムからできるので
パッと見Alexaが勝手に喋ったように感じます。
これならばうまいこと使えば
Alexaに実況してもらったりできるかもしれません。
リマインダーを使う
Alexaでリマインダーを使うにはAPIを使います。
APIはSDKがサポートしてるそうなのでそのやり方に従ってやります。
developer.amazon.com
ではやってみましょう。
リファレンスやらGitHubやら見てて
みんなNode.js使ってたんで右にならいます。
(とはいえオンラインのエディタ使ってやるんで環境構築とかはしません)
const RemindIntentHandler = { canHandle(handlerInput) { return handlerInput.requestEnvelope.request.type === 'IntentRequest' && handlerInput.requestEnvelope.request.intent.name === 'RemindIntent'; }, // handle の前に async 追加 async handle(handlerInput) { const sec = handlerInput.requestEnvelope.request.intent.slots.sec.value; const requestEnvelope = handlerInput.requestEnvelope; const responseBuilder = handlerInput.responseBuilder; const consentToken = requestEnvelope.context.System.apiAccessToken; // check for confirmation. if not confirmed, delegate switch (requestEnvelope.request.intent.confirmationStatus) { case 'CONFIRMED': // intent is confirmed, so continue console.log('Alexa confirmed intent, so clear to create reminder'); break; case 'DENIED': // intent was explicitly not confirmed, so skip creating the reminder console.log('Alexa disconfirmed the intent; not creating reminder'); return responseBuilder .speak(`わかりました。リマインドしません。`) .getResponse(); case 'NONE': default: console.log('delegate back to Alexa to get confirmation'); return responseBuilder .addDelegateDirective() .getResponse(); } if (!consentToken) { return responseBuilder .speak(`アレクサアプリにカードを送信しました。リマインダーの権限を有効にしてください。`) .withAskForPermissionsConsentCard(PERMISSIONS) .getResponse(); } try { const client = handlerInput.serviceClientFactory.getReminderManagementServiceClient(); const reminderRequest = { trigger: { type: 'SCHEDULED_RELATIVE', // 指定秒数後にリマインド offsetInSeconds: sec, }, alertInfo: { spokenInfo: { content: [{ locale: 'ja-JP', // 日本国の日本語 text: 'お時間です', // スマホに通知する文言 }], }, }, pushNotification: { status: 'ENABLED', }, }; const reminderResponse = await client.createReminder(reminderRequest); console.log(JSON.stringify(reminderResponse)); } catch (error) { if (error.name !== 'ServiceError') { console.log(`error: ${error.stack}`); const response = responseBuilder.speak(`おっと。エラーが発生しました。`).getResponse(); return response; } throw error; } return responseBuilder .speak(`分かりました。${sec}秒後にリマインドします。`) .getResponse(); }, };
まずハンドラーに来たら必要な値を取得しておきます。
基本はNode.jsでAlexaスキル使うのと大差ないですが、
あまり見ない点としてapiAccessTokenを取得しています。
これはリマインダーAPIを使うときのヘッダー情報に
アクセストークンが必要だからです。
そして一通り値を取得したら次にこんなやつが来ます。
switch (requestEnvelope.request.intent.confirmationStatus)
これはリクエストの確認ですね。
リマインダーAPIを使用する際
当然ながらAlexaの対話モデルの方にインテントを追加するのですが、
このときにインテントの確認を有効にする必要があるそうです。
この設定を有効にしたら
Alexaがリクエストを受けた時確認メッセージを出してきます。
上記の一文はその返答を待ち、
返答によって処理を切り分ける役割を持っています。
あとは各種値によって処理を進めていくだけですね。
あとはエラーハンドリングとハンドラーの登録です。
const ErrorHandler = { canHandle(handlerInput, error) { return error.name === 'ServiceError'; }, handle(handlerInput, error) { // console.log(`ERROR STATUS: ${error.statusCode}`); console.log(`ERROR MESSAGE: ${error.message}`); // console.log(`ERROR RESPONSE: ${JSON.stringify(error.response)}`); // console.log(`ERROR STACK: ${error.stack}`); switch (error.statusCode) { case 401: return handlerInput.responseBuilder .speak(`Alexaアプリのホーム画面で、スキルに権限を付与してください。`) .withAskForPermissionsConsentCard(PERMISSIONS) .getResponse(); case 403: return handlerInput.responseBuilder .speak(`このデバイスではリマインダーを設定することができません。`) .getResponse(); default: return handlerInput.responseBuilder .speak(`リマインダーの設定でエラーが発生しました。`) .getResponse(); } }, }; exports.handler = Alexa.SkillBuilders.custom() .addRequestHandlers( LaunchRequestHandler, RemindIntentHandler, HelpIntentHandler, CancelAndStopIntentHandler, SessionEndedRequestHandler, IntentReflectorHandler) .addErrorHandlers( ErrorHandler) // 追加 .withApiClient(new Alexa.DefaultApiClient()) .lambda();
特別説明することもないですね。
そのまんまです。
さてコーディングは終わりです。
あとはモデルをビルドしつつ
ビルド時間の間にスキルの権限設定をしましょう。
Alexaでリマインダーを使うためには
・開発コンソールからアクセス権限の設定
・スマホアプリからのアクセス権限の設定
この二つ両方が必要です。
(私はスマホから設定し忘れてちょい詰まりましたw)
開発コンソールからは下の方に行くと
アクセス権限ってとこがあるのでそこからリマインダーの項目を。
スマホからはアプリ立ち上げて
スキル・ゲームの欄からスキル選んで
そこから設定です。
そして設定しているうちにビルドが終わったので
試してみましょう。
Alexaのリマインダー使えばうまい感じで喋ってくれんかと思ったが、甘かったようだorz pic.twitter.com/oKe6OuRRWE
— hakase@プログラミングなんもわからん (@hakase70945250) 2019年8月27日
う~~ん?
0点ではないが100点とは程遠い気がするぞ・・・?
あとがき
今回はAlexaでリマインダーAPIを使ってみました。
なんか設定してない文言が追加されていたり、
変に繰り返されたりなんか・・ちょっと・・・って感じでした。
この辺が直せればワンチャンスですが、
Alexaってこういうとこ融通聞かないんでどうですかね。
それでは今回はこの辺でノシ