This is an old revision of the document!
Alexa Custom Skill - Stock Prices
—————————————————————————————————————————–
Author: Tristan Brodeur Email: brodeurtristan@gmail.com Date: Last modified on 01/16/17 Keywords: Alexa, Amazon Skills Kit
—————————————————————————————————————————–
Overview
—————————————————————————————————————————–
1. Create a Lambda Function
To create a lambda function, follow this tutorial: Creating a lambda function
—————————————————————————————————————————–
2. Add the code
- index.py
import requests from bs4 import BeautifulSoup ''' Amazon alexa skill to grab stock data from yahoo finance and present data to user Currently has database of ~10 companies Future updates to include database of all S&P 500 companies if data size allows ''' ################################################################## def lambda_handler(event, context): #Checks to make sure application id is same as alexa skill (links) if (event['session']['application']['applicationId'] != #Change to application id listed under your alexa skill in the developer portal "amzn1.ask.skill.XXXXXXXX-XXXX-XXXX-XXXX-XXXXXXXXXXXX"): raise ValueError("Invalid Application ID") if event["session"]["new"]: on_session_started({"requestId": event["request"]["requestId"]}, event["session"]) #check event types and call appropriate response if event["request"]["type"] == "LaunchRequest": return on_launch(event["request"], event["session"]) elif event["request"]["type"] == "IntentRequest": return on_intent(event["request"], event["session"]) elif event["request"]["type"] == "SessionEndedRequest": return on_session_ended(event["request"], event["session"]) ################################################################## def on_session_started(session_started_request, session): print "Starting new." ################################################################## def on_launch(launch_request, session): return get_welcome_response() ################################################################## def on_intent(intent_request, session): intent = intent_request["intent"] intent_name = intent_request["intent"]["name"] if intent_name == "GetCurrentPrice": return get_current_price(intent) elif intent_name == "GetHigh": return get_high(intent) elif intent_name == "GetLow": return get_low(intent) elif intent_name == "AMAZON.HelpIntent": return get_welcome_response() elif intent_name == "AMAZON.CancelIntent" or intent_name == "AMAZON.StopIntent": return handle_session_end_request() else: raise ValueError("Invalid intent") ################################################################## def on_session_ended(session_ended_request, session): print "Ending session." ################################################################## def handle_session_end_request(): card_title = "Stocker" speech_output = "Im sorry dave. Im afraid I cant do that." should_end_session = True return build_response({}, build_speechlet_response(card_title, speech_output, None, should_end_session)) ################################################################## def get_welcome_response(): #Response to user invoking stock app without parameters/intents session_attributes = {} card_title = "Stocker" speech_output = "Welcome to the Stocker skill. " \ "You can ask me for current stock prices, or " \ "ask me about stock highs and lows." reprompt_text = "Please ask me for stock prices, " \ "for example Apple stock price." should_end_session = False return build_response(session_attributes, build_speechlet_response( card_title, speech_output, reprompt_text, should_end_session)) ################################################################## def find_availability(to_find, restaurant): page = requests.get("") page_data = BeautifulSoup(page.content, "html.parser") ################################################################## def grab_data(to_find, ticker_code): if ticker_code != "EVRGRN": #send http response fro yahoo finance to grab data based on passed in ticker code page = requests.get("https://finance.yahoo.com/quote/" + ticker_code + "?p=" + ticker_code) #resolves the page content into its component parts data = BeautifulSoup(page.content, "html.parser") if to_find == "get_current_price": #grab text from appropriate class and return to current object current = data.find(class_ = "Fw(b) Fz(36px) Mb(-4px)").get_text() #find index of decimal point to seperate dollars from cents index = current.index('.') dollars = current[:index] cents = current[index+1:] #map each component in list to a string object, and join via str operator (in this case nothing, so it joins together) dollars = ''.join(map(str, dollars)) cents = ''.join(map(str, cents)) return dollars + " dollars and " + cents + " cents" elif to_find == "get_high" or to_find =="get_low": classes= data.find_all(class_ = "Ta(end) Fw(b)") #several stock values are listed under the class with the given value, so #we grab fourth object in list of classes, returning stock range stock_range = list(classes[4].get_text()) #since text we grab is not in unicode, we grab each str object and encode it with UTF8 stock_range = [x.encode('UTF8') for x in stock_range] if to_find == "get_high": high = "" for letter in stock_range: #since returned list of str objects are a range from low to high (e.g. $$ - $$), we grab second half of data for high value if letter == '-': high = stock_range[stock_range.index(letter)+2:] index = high.index('.') dollars = high[:index] cents = high[index+1:] #map each component in list to a string object, and join via str operator (in this case nothing, so it joins together) dollars = ''.join(map(str, dollars)) cents = ''.join(map(str, cents)) return dollars + " dollars and" + cents + " cents" elif to_find == "get_low": low = "" for letter in stock_range: #since returned list of str objects are a range from high to low (e.g. $$-$$), we grab first half of data for low value if letter == '-': low = stock_range[:stock_range.index(letter)-1] index = low.index('.') dollars = low[:index] cents = low[index+1:] #map each component in list to a string object, and join via str operator (in this case nothing, so it joins together) dollars = ''.join(map(str, dollars)) cents = ''.join(map(str, cents)) return dollars + " dollars and " + cents + " cents" #cor is on its way else: if to_find == "get_current_price": return "over 9000" elif to_find == "get_high" or to_find =="get_low": return "not yet available. Please ask Tristan" ################################################################## def get_current_price(intent): session_attributes = {} card_title = "Current Stock Price" speech_output = "I'm not sure which company you wanted stock prices for. " \ "Please try again." reprompt_text = "I'm not sure which company you wanted stock prices for. " \ "Please try again." should_end_session = False if "Ticker" in intent["slots"]: ticker_name = intent["slots"]["Ticker"]["value"] #grab appropriate value based on name from ticke code dict ticker_code = get_ticker_code(ticker_name.lower()) if (ticker_code != "unkn"): card_title = "Current price for " + ticker_name.title() price = grab_data(get_current_price.__name__, ticker_code) speech_output = "Current price for " + ticker_name + " is: " + price reprompt_text = "" should_end_session = True return build_response(session_attributes, build_speechlet_response( card_title, speech_output, reprompt_text, should_end_session)) ################################################################## def get_high(intent): session_attributes = {} card_title = "Stock High" speech_output = "I'm not sure which company you wanted stock prices for. " \ "Please try again." reprompt_text = "I'm not sure which company you wanted stock prices for. " \ "Please try again." should_end_session = False if "Ticker" in intent["slots"]: ticker_name = intent["slots"]["Ticker"]["value"] #grab appropriate value based on name from ticke code dict ticker_code = get_ticker_code(ticker_name.lower()) if (ticker_code != "unkn"): card_title = "High price for " + ticker_name.title() price = grab_data(get_high.__name__, ticker_code) speech_output = "Stock high for " + ticker_name + " is: " + price reprompt_text = "" should_end_session = True return build_response(session_attributes, build_speechlet_response( card_title, speech_output, reprompt_text, should_end_session)) ################################################################## def get_low(intent): session_attributes = {} card_title = "Stock Low" speech_output = "I'm not sure which company you wanted stock prices for. " \ "Please try again." reprompt_text = "I'm not sure which company you wanted stock prices for. " \ "Please try again." should_end_session = False if "Ticker" in intent["slots"]: ticker_name = intent["slots"]["Ticker"]["value"] #grab appropriate value based on name from ticke code dict ticker_code = get_ticker_code(ticker_name.lower()) if (ticker_code != "unkn"): card_title = "Low price for " + ticker_name.title() price = grab_data(get_low.__name__, ticker_code) speech_output = "Stock low for " + ticker_name + " is: " + price reprompt_text = "" should_end_session = True return build_response(session_attributes, build_speechlet_response( card_title, speech_output, reprompt_text, should_end_session)) ################################################################## def build_speechlet_response(title, output, reprompt_text, should_end_session): #return data in json format to alexa skills kit return { "outputSpeech": { "type": "PlainText", "text": output }, "card": { "type": "Simple", "title": title, "content": output }, "reprompt": { "outputSpeech": { "type": "PlainText", "text": reprompt_text } }, "shouldEndSession": should_end_session } ################################################################## def get_ticker_code(ticker_name): #function to grab associated value based on ticker_name parameter return{ 'alphabet': 'GOOGL', 'apple': 'AAPL', 'amazon':'AMZN', 'tesla':'TSLA', 'oracle':'ORCL', 'irobot':'IRBT', 'microsoft':'MSFT', 'facebook':'FB', 'intel':'INTC', 'ibm':'IBM', 'evergreen robotics': 'EVRGRN', 'netflix':'NFLX' }.get(ticker_name, "unkn") ################################################################## def build_response(session_attributes, speechlet_response): return { "version": "1.0", "sessionAttributes": session_attributes, "response": speechlet_response }
Make sure to change the application id listed in lambda_handler() to your own id once you create the Alexa Skill with Alexa Skills Kit
—————————————————————————————————————————–
The handler function takes two parameters:
- event: will contain an object with information about the request.
- context: will represent the state of the Lambda function (for
- example, how much time is left before the function times out). The
- context parameter is also used for sending a response back to the
- caller.
In this tutorial, we’ll run the web service on AWS Lambda, a service that executes code in response to events, thus saving the developer the trouble of maintaining a server. Lambda also links seamlessly to the Alexa Skills Kit, which makes it an excellent tool for running the code for Alexa Skills.
We will implement the server functionality as a python module, so to complete the tutorial, you’ll need a basic understanding of python2.7.
Every request from the Alexa Skills Kit has a type, passed in event.request.type.
In all, there are three request types, all of which the service needs to respond to separately:
- Launch request: Sent when the user launches the Skill. The
- example code calls the helper function onLaunch to initialize the
- help function.
- SessionEndedRequest: Sent when the user stops using the skill
- by saying “exit”, by not responding for a while, or if an error occurs.
- The example code calls the helper function onSessionEnded,
- which is currently just a placeholder and doesn’t do anything.
- IntentRequest: Sent when the user speaks a command that
- maps to an intent. The example code calls the helper function
- onIntent.
Amazon’s documentation explains that:
“[A]n intent represents a high-level action that fulfills a user’s spoken request. Intents can optionally have arguments called slots that collect additional information needed to fulfill the user’s request.”
When a user speaks a command, Alexa converts it to text and compares it to a list of phrases the application understands. Each phrase is mapped to an intent definition so that your code doesn’t have to worry about parsing the free-form input from the user but can rely on the intents to organize its communication with the user.
—————————————————————————————————————————–
You’ll soon see how to specify the intents for your skill, but first, let’s take a look at the onIntent function to see how Alexa handles the intents.
def on_intent(intent_request, session): intent = intent_request["intent"] intent_name = intent_request["intent"]["name"] if intent_name == "Hello": return say_hello() elif intent_name == "AMAZON.HelpIntent": return get_welcome_response() elif intent_name == "AMAZON.CancelIntent" or intent_name == "AMAZON.StopIntent": return handle_session_end_request() else: raise ValueError("Invalid intent")
As you can see, most of the function is an if..else structure that compares the intent name from the request to a set of intents the skill accepts. Depending on the intent name, it then calls a matching function.
For example, when Alexa receives a request with the intent name Hello, it calls the corresponding say_hello() function.
—————————————————————————————————————————–
Looking at the say_hello function,
def say_hello(): session_attributes = {} card_title = "Hello World" speech_output = "Hello World" reprompt_text = "" should_end_session = True return build_response(session_attributes, build_speechlet_response( card_title, speech_output, reprompt_text, should_end_session))
we can see that the function returns a build_response function that will pass arguments defined in the say_hello function.
-card_title: name of the card that will display on the Alexa app on your smartphone
-speech_output: what the Echo or Echo Dot will ouput once the function build_response function is called
-should_end_session: tells Alexa whether to end the skill session after a response has been outputed. This should be set to True in this case. If a reprompt text is defined, usually should_end_session would be set to false, however all we are doing with this function is asking for a simple output.
—————————————————————————————————————————–
After the code is added, configure your lambda function with the Alexa Skills Kit.