まずは動かしてみた
話してることは同じだけど、データベース見るように中身変えました。#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にして取り込んでます。
// 使用するモジュールを定義
const Alexa = require('ask-sdk-core');
const i18n = require('i18next');
const {
getRequestType,
getIntentName,
getSlotValue,
getDialogState,
} = require('ask-sdk-core');
const AWS = require('aws-sdk');
const documentClient = new AWS.DynamoDB.DocumentClient()
const GetNewFactHandler = {
canHandle(handlerInput) {
const request = handlerInput.requestEnvelope.request;
return request.type === 'LaunchRequest'
|| (request.type === 'IntentRequest'
&& request.intent.name === 'GetNewFactIntent');
},
handle(handlerInput) {
const requestAttributes = handlerInput.attributesManager.getRequestAttributes();
const randomFact = requestAttributes.t('FACTS');
const speakOutput = requestAttributes.t('GET_FACT_MESSAGE') + randomFact;
return handlerInput.responseBuilder
.speak(speakOutput)
.withSimpleCard(requestAttributes.t('SKILL_NAME'), randomFact)
.getResponse();
},
};
const HelpHandler = {
canHandle(handlerInput) {
const request = handlerInput.requestEnvelope.request;
return request.type === 'IntentRequest'
&& request.intent.name === 'AMAZON.HelpIntent';
},
handle(handlerInput) {
const requestAttributes = handlerInput.attributesManager.getRequestAttributes();
return handlerInput.responseBuilder
.speak(requestAttributes.t('HELP_MESSAGE'))
.reprompt(requestAttributes.t('HELP_REPROMPT'))
.getResponse();
},
};
const FallbackHandler = {
canHandle(handlerInput) {
const request = handlerInput.requestEnvelope.request;
return request.type === 'IntentRequest'
&& request.intent.name === 'AMAZON.FallbackIntent';
},
handle(handlerInput) {
const requestAttributes = handlerInput.attributesManager.getRequestAttributes();
return handlerInput.responseBuilder
.speak(requestAttributes.t('FALLBACK_MESSAGE'))
.reprompt(requestAttributes.t('FALLBACK_REPROMPT'))
.getResponse();
},
};
const ExitHandler = {
canHandle(handlerInput) {
const request = handlerInput.requestEnvelope.request;
return request.type === 'IntentRequest'
&& (request.intent.name === 'AMAZON.CancelIntent'
|| request.intent.name === 'AMAZON.StopIntent');
},
handle(handlerInput) {
const requestAttributes = handlerInput.attributesManager.getRequestAttributes();
return handlerInput.responseBuilder
.speak(requestAttributes.t('STOP_MESSAGE'))
.getResponse();
},
};
const SessionEndedRequestHandler = {
canHandle(handlerInput) {
const request = handlerInput.requestEnvelope.request;
return request.type === 'SessionEndedRequest';
},
handle(handlerInput) {
console.log(`Session ended with reason: ${handlerInput.requestEnvelope.request.reason}`);
return handlerInput.responseBuilder.getResponse();
},
};
const ErrorHandler = {
canHandle() {
return true;
},
handle(handlerInput, error) {
console.log(`Error handled: ${error.message}`);
console.log(`Error stack: ${error.stack}`);
const requestAttributes = handlerInput.attributesManager.getRequestAttributes();
return handlerInput.responseBuilder
.speak(requestAttributes.t('ERROR_MESSAGE'))
.reprompt(requestAttributes.t('ERROR_MESSAGE'))
.getResponse();
},
};
const LocalizationInterceptor = {
process(handlerInput) {
const localizationClient = i18n.init({
lng: handlerInput.requestEnvelope.request.locale,
resources: languageStrings,
returnObjects: true
});
localizationClient.localize = function localize() {
const args = arguments;
const value = i18n.t(...args);
if (Array.isArray(value)) {
return value[Math.floor(Math.random() * value.length)];
}
return value;
};
const attributes = handlerInput.attributesManager.getRequestAttributes();
attributes.t = function translate(...args) {
return localizationClient.localize(...args);
}
}
};
const HelloWorldIntentHandler = {
canHandle(handlerInput) {
console.log('called HelloWorldIntentHandler.canHandle');
const request = handlerInput.requestEnvelope.request;
return request.type === 'IntentRequest'
&& request.intent.name === 'HelloWorldIntent';
},
handle(handlerInput) {
console.log('called HelloWorldIntentHandler.handle');
return handlerInput.responseBuilder
.speak("こんにちはインテントが呼ばれました。")
.getResponse();
}
};
// 今回の処理
const SearchSkillHandler = {
canHandle(handlerInput) {
console.log('called SearchSkillHandler.canHandle');
const request = handlerInput.requestEnvelope.request;
return request.type === 'IntentRequest'
&& request.intent.name === 'SearchSkillIntent';
},
// DynamoDBからのデータ取得処理は非同期だったので、関数にasyncを設定
async handle(handlerInput) {
const skillsNameValue = getSlotValue(handlerInput.requestEnvelope, 'skills');
console.log('called SearchSkillHandler.handle');
const params = {
TableName: 'skillmap',
KeyConditionExpression: 'skill = :skill',
ExpressionAttributeValues: {
':skill': `${skillsNameValue}`
}
};
let skillsPersonName = '';
// 実際にDynamoDBからデータ取得するところについては、await設定+promiseを設定
await documentClient.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);
return handlerInput.responseBuilder
.speak(`${skillsNameValue}` + "は" + `${skillsPersonName}` + "さんが詳しいです。")
.getResponse();
}
};
const skillBuilder = Alexa.SkillBuilders.custom();
// 使用するハンドラーをここで定義
exports.handler = skillBuilder
.addRequestHandlers(
GetNewFactHandler,
SearchSkillHandler,
HelloWorldIntentHandler,
HelpHandler,
ExitHandler,
FallbackHandler,
SessionEndedRequestHandler,
)
.addRequestInterceptors(LocalizationInterceptor)
.addErrorHandlers(ErrorHandler)
.withCustomUserAgent('sample/basic-fact/v2')
.lambda();
const jpData = {
translation: {
SKILL_NAME: 'テスト',
},
};
const languageStrings = {
'ja': jpData,
};