Objective: Build a storyteller bot for Alexa to read bedtime stories from children's classics.
Note: By default, Alexa reads books from your Kindle account. This bot instead fetches plot summaries of famous Walt Disney classics from Wikipedia.
Steps:
[1] Create Alexa Skill:Create a "Tell_Story" intent, and provide sample utterances for bot training, e.g. "read the story of Pinocchio", "tell me Cinderella's story", or "story of Aladdin"etc. Create a slot "Story" of type "Fairy_Tales". Fairy Tale is a list of sample fairy-tale names, e.g. "Cinderella", "Snow White", "Pinocchio", "Beauty and the Beast" etc.
[2] Get Story Text from Wikipedia:Create one text-file per story and name it as "story-name.txt". This file contains the story-plot of famous Walt Disney Classics from Wikipedia. e.g. see:
https://en.wikipedia.org/wiki/Pinocchio_(1940_film)#Plot or https://en.wikipedia.org/wiki/Cinderella_(1950_film)#Plot
I have shared few sample story plots at: https://github.com/pamruta/Alexa/tree/master/FairyTell/Text
Upload story text files in S3 bucket, to access it from your AWS lambda function.
[3] Create AWS Lambda Function:The lambda function implements bot actions to perform once the intent is invoked.
I have shared my lambda_function.py script on Git-Hub at: https://github.com/pamruta/Alexa/tree/master/FairyTell
This script is divided into 3 sections:
[a] Story Teller: Includes functions to read the story-text from S3 bucket. If the invoked intent includes a specific story-name, e.g. "tell me the story of Cinderella", it uses the given "story" slot-value to read the corresponding file in S3. If no story-name is mentioned in the intent, e.g. "read a story", "tell me a story", the tell_random_story() function picks a random story using the s3.list_objects() function.
##### Read Story Text from S3 Bucket #####
# read story text from S3 bucket
def tell_story(story_name):
import boto3
s3 = boto3.client('s3')
filename = story_name + ".txt"
# get story text
data = s3.get_object(Bucket="fairytell", Key=filename)
story_text = data['Body'].read()
# narration for speech output
speech_text = "Reading the story of " + story_name + ". " + story_text
return(speech_text)
# pick a random story to tell
def tell_random_story():
# get available stories in S3 'fairytell' bucket
import boto3
s3 = boto3.client('s3')
story_list = []
list_output = s3.list_objects_v2(Bucket="fairytell")
for item in list_output['Contents']:
story_list.append(item['Key'])
# pick a random story to read
import random
filename = random.sample(story_list, 1)[0]
# remove the file extension
import re
story_name = re.sub(r".txt", '', filename)
# read story content
data = s3.get_object(Bucket="fairytell", Key=filename)
story_text = data['Body'].read()
# narration for speech output
speech_text = "Reading the story of " + story_name + ". " + story_text
return(story_name, speech_text)
[b] Build Response: This section includes functions to create speech output from story text, as well as, a card layout to display on the screen while narrating the story. The card layout uses the story title along with the images stored in S3 bucket at: https://s3.amazonaws.com/fairytell/Images/
##### Helper Functions to Create Speech and Card Output #####
# creates display card with a given title and image
def build_SimpleCard(story_name, image_url):
card = { 'type' : 'Standard', 'title' : story_name, 'image' : {'smallImageUrl' : image_url} }
return card
# produces speech output
def build_PlainSpeech(speech_text):
speech = { 'type' : 'PlainText', 'text' : speech_text }
return speech
# builds response
def build_response(message):
response = {}
response['response'] = message
return response
# creates speech and text output
def compile_speechlet(story_name, speech_text, image_url):
speechlet = {}
speechlet['outputSpeech'] = build_PlainSpeech(speech_text)
speechlet['card'] = build_SimpleCard(story_name, image_url)
speechlet['shouldEndSession'] = True
return build_response(speechlet)
[c] Intent Handler: The main function to handle the "Tell Story" intent. It checks if the story-name is specified in the "Story" slot. If not, it calls the tell_random_story() function, which randomly picks a story to tell.
##### Intent Handler #####
# handling the tell_story intent
def intent_handler(event, context):
# get intent name
intent_name = event['request']['intent']['name']
if intent_name == "tell_story":
# if the story name is specified in the intent
if event['request']['intent']['slots']['story'].has_key('value'):
story_name_raw = event['request']['intent']['slots']['story']['value']
# format story-name string to match the filename in S3 bucket
words = story_name_raw.split()
story_name = ""
for w in words:
if story_name == "":
story_name = w.capitalize()
else:
story_name = story_name + "-" + w.capitalize()
speech_text = tell_story(story_name)
# if no story name is mentioned, pick a random story in S3 bucket
else:
(story_name, speech_text) = tell_random_story()
image_url = "https://s3.amazonaws.com/fairytell/Images/" + story_name + ".jpg"
return compile_speechlet(story_name, speech_text, image_url)
# intent not recognized
else:
return compile_speechlet("Sorry", "Sorry, I couldn't understand. Please try again..", "")
[4] Connect Alexa Skill with AWS Lambda:Add "Alexa Skill" as input trigger for lambda function. This invokes the lambda function whenever the Alexa Skill is called. Configure the input trigger using the Alexa Skill ID, and use the lambda function's ARN as Endpoint for Alexa Skill.
[5] Build and Test your bot using the Alexa Simulator!You can invoke your Alexa skill using the "invocation name". e.g. "Alexa, ask fairytell bot to read a story"..
[6] What's Next - Create a smart toy robot for kids to read bedtime stories..
Comments