まずは動かしてみた
話してることは同じだけど、データベース見るように中身変えました。#awslambda#dynamodb#alexa#初心者ですpic.twitter.com/wiDwBCjHfn
— heihei (@heihei15408697) December 9, 2020
前と何が変わったの?
実は前とAlexaが話している内容は変わりがない。
↓前回の記事
https://qiita.com/heihei15408697/items/6ce26775b26daed044e4
Node.js上で配列にしていたものをデータベース使うように変えた!!
実際に苦労した点は・・・
下記のQiita記事にしてるのでそちら参照!
・「Alexa、Lambdaと連携する方法を教えて」と聞いても、答えがよく分からなかったので自分でやったことメモ
・LambdaからDynamoDBを使うときの権限エラー解消メモ
・Lambda→DynamoDBが非同期処理と知らないやつがとった行動
これをやってみての感想
データベースを導入することで一気にシステムっぽくなる。
ちょっと動かすだけなら、前回までの配列でいいと思うけど、メンテナンスを考えると、やっぱりデータベースがいいんだろうな。
あと、僕個人としてはLambdaもDynamoDBも使ったことなかったので、すごく勉強になったし、楽しかった!!
Lambdaで動かしているNode.jsのソース
※Lambdaに取り込む際にnpmでインストールしたask-sdk-coreとi18nextをすべてzipにして取り込んでます。
// 使用するモジュールを定義constAlexa=require('ask-sdk-core');consti18n=require('i18next');const{getRequestType,getIntentName,getSlotValue,getDialogState,}=require('ask-sdk-core');constAWS=require('aws-sdk');constdocumentClient=newAWS.DynamoDB.DocumentClient()constGetNewFactHandler={canHandle(handlerInput){constrequest=handlerInput.requestEnvelope.request;returnrequest.type==='LaunchRequest'||(request.type==='IntentRequest'&&request.intent.name==='GetNewFactIntent');},handle(handlerInput){constrequestAttributes=handlerInput.attributesManager.getRequestAttributes();constrandomFact=requestAttributes.t('FACTS');constspeakOutput=requestAttributes.t('GET_FACT_MESSAGE')+randomFact;returnhandlerInput.responseBuilder.speak(speakOutput).withSimpleCard(requestAttributes.t('SKILL_NAME'),randomFact).getResponse();},};constHelpHandler={canHandle(handlerInput){constrequest=handlerInput.requestEnvelope.request;returnrequest.type==='IntentRequest'&&request.intent.name==='AMAZON.HelpIntent';},handle(handlerInput){constrequestAttributes=handlerInput.attributesManager.getRequestAttributes();returnhandlerInput.responseBuilder.speak(requestAttributes.t('HELP_MESSAGE')).reprompt(requestAttributes.t('HELP_REPROMPT')).getResponse();},};constFallbackHandler={canHandle(handlerInput){constrequest=handlerInput.requestEnvelope.request;returnrequest.type==='IntentRequest'&&request.intent.name==='AMAZON.FallbackIntent';},handle(handlerInput){constrequestAttributes=handlerInput.attributesManager.getRequestAttributes();returnhandlerInput.responseBuilder.speak(requestAttributes.t('FALLBACK_MESSAGE')).reprompt(requestAttributes.t('FALLBACK_REPROMPT')).getResponse();},};constExitHandler={canHandle(handlerInput){constrequest=handlerInput.requestEnvelope.request;returnrequest.type==='IntentRequest'&&(request.intent.name==='AMAZON.CancelIntent'||request.intent.name==='AMAZON.StopIntent');},handle(handlerInput){constrequestAttributes=handlerInput.attributesManager.getRequestAttributes();returnhandlerInput.responseBuilder.speak(requestAttributes.t('STOP_MESSAGE')).getResponse();},};constSessionEndedRequestHandler={canHandle(handlerInput){constrequest=handlerInput.requestEnvelope.request;returnrequest.type==='SessionEndedRequest';},handle(handlerInput){console.log(`Session ended with reason: ${handlerInput.requestEnvelope.request.reason}`);returnhandlerInput.responseBuilder.getResponse();},};constErrorHandler={canHandle(){returntrue;},handle(handlerInput,error){console.log(`Error handled: ${error.message}`);console.log(`Error stack: ${error.stack}`);constrequestAttributes=handlerInput.attributesManager.getRequestAttributes();returnhandlerInput.responseBuilder.speak(requestAttributes.t('ERROR_MESSAGE')).reprompt(requestAttributes.t('ERROR_MESSAGE')).getResponse();},};constLocalizationInterceptor={process(handlerInput){constlocalizationClient=i18n.init({lng:handlerInput.requestEnvelope.request.locale,resources:languageStrings,returnObjects:true});localizationClient.localize=functionlocalize(){constargs=arguments;constvalue=i18n.t(...args);if(Array.isArray(value)){returnvalue[Math.floor(Math.random()*value.length)];}returnvalue;};constattributes=handlerInput.attributesManager.getRequestAttributes();attributes.t=functiontranslate(...args){returnlocalizationClient.localize(...args);}}};constHelloWorldIntentHandler={canHandle(handlerInput){console.log('called HelloWorldIntentHandler.canHandle');constrequest=handlerInput.requestEnvelope.request;returnrequest.type==='IntentRequest' &&request.intent.name==='HelloWorldIntent';},handle(handlerInput){console.log('called HelloWorldIntentHandler.handle');returnhandlerInput.responseBuilder.speak("こんにちはインテントが呼ばれました。").getResponse();}};// 今回の処理constSearchSkillHandler={canHandle(handlerInput){console.log('called SearchSkillHandler.canHandle');constrequest=handlerInput.requestEnvelope.request;returnrequest.type==='IntentRequest' &&request.intent.name==='SearchSkillIntent';},// DynamoDBからのデータ取得処理は非同期だったので、関数にasyncを設定asynchandle(handlerInput){constskillsNameValue=getSlotValue(handlerInput.requestEnvelope,'skills');console.log('called SearchSkillHandler.handle');constparams={TableName:'skillmap',KeyConditionExpression:'skill = :skill',ExpressionAttributeValues:{':skill':`${skillsNameValue}`}};letskillsPersonName='';// 実際にDynamoDBからデータ取得するところについては、await設定+promiseを設定awaitdocumentClient.query(params,(err,data)=>{if(err){console.log(JSON.stringify(err,null,2));}else{console.log(JSON.stringify(data,null,2));console.log('取得したもの name : '+data.Items[0].name);console.log('取得したもの skill : '+data.Items[0].skill);skillsPersonName=data.Items[0].name;console.log('取得したもの skillsPersonName1 : '+skillsPersonName);}}).promise();console.log('取得したもの skillsPersonName2 : '+skillsPersonName);returnhandlerInput.responseBuilder.speak(`${skillsNameValue}`+"は"+`${skillsPersonName}`+"さんが詳しいです。").getResponse();}};constskillBuilder=Alexa.SkillBuilders.custom();// 使用するハンドラーをここで定義exports.handler=skillBuilder.addRequestHandlers(GetNewFactHandler,SearchSkillHandler,HelloWorldIntentHandler,HelpHandler,ExitHandler,FallbackHandler,SessionEndedRequestHandler,).addRequestInterceptors(LocalizationInterceptor).addErrorHandlers(ErrorHandler).withCustomUserAgent('sample/basic-fact/v2').lambda();constjpData={translation:{SKILL_NAME:'テスト',},};constlanguageStrings={'ja':jpData,};