Dean Meehan
Published © Apache-2.0

UK Top 40

Ever wanted to know the best song this week and learn how to develop an Alexa skill? Finally, you can, and be cool again!

IntermediateFull instructions provided2 hours909

Things used in this project

Story

Read more

Schematics

VUI

Code

Top40

Python
This is the skill lambda function hosted on amazon aws that takes in a request and responds with data from the server.
from __future__ import print_function
import urllib
import json

cloudlink = "http://cloud.dean.technology/api/top40"

def lambda_handler(event, context):
    """ Route the incoming request based on type (LaunchRequest, IntentRequest,
    etc.) The JSON body of the request is provided in the event parameter.
    """
    print("event.session.application.applicationId=" +
            event['session']['application']['applicationId'])

    if event['session']['new']:
        on_session_started({'requestId': event['request']['requestId']},
                           event['session'])

    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):
    """ Called when the session starts """

    print("on_session_started requestId=" + session_started_request['requestId']
          + ", sessionId=" + session['sessionId'])


def on_launch(launch_request, session):
    """ Called when the user launches the skill without specifying what they
    want
    """

    print("on_launch requestId=" + launch_request['requestId'] +
          ", sessionId=" + session['sessionId'])
    # Dispatch to your skill's launch
    return get_welcome_response()


def on_intent(intent_request, session):
    """ Called when the user specifies an intent for this skill """

    print("on_intent requestId=" + intent_request['requestId'] +
          ", sessionId=" + session['sessionId'])

    intent = intent_request['intent']
    intent_name = intent_request['intent']['name']

    if intent_name == "NumberOne":
        return run_get_Number_One(intent,session,cloudlink,1)
    elif intent_name == "TopThree":
        return run_get_Number_One(intent,session,cloudlink,3)
    elif intent_name == "TopFourty":
        return run_get_top_fourty_position(intent,session,cloudlink)
    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:
        return build_response(session_attributes, build_speechlet_response(
              "Invalid Request", "Your request could not be handled. Try asking U K Top 40, who is this weeks number seven", reprompt_text, should_end_session))

def on_session_ended(session_ended_request, session):
    """ Called when the user ends the session.

    Is not called when the skill returns should_end_session=true
    """
    print("on_session_ended requestId=" + session_ended_request['requestId'] +
          ", sessionId=" + session['sessionId'])

# --------------- Functions that control the skill's behavior ------------------

def get_welcome_response():

    session_attributes = {}
    card_title = "Welcome"
    speech_output = "Welcome to the U. K. Top 40. " \
                    "Find out this weeks entries by just asking, give me an overview of this week"
    # If the user either does not reply to the welcome message or says something
    # that is not understood, they will be prompted again with this text.
    reprompt_text = "You can also ask me, who is position twelve this week?"
    should_end_session = False
    return build_response(session_attributes, build_speechlet_response(
        card_title, speech_output, reprompt_text, should_end_session))

def handle_session_end_request():
    card_title = "Session Ended"
    speech_output = ""
    # Setting this to true ends the session and exits the skill.
    should_end_session = True
    return build_response({}, build_speechlet_response(
        card_title, speech_output, None, should_end_session))

def run_get_top_fourty_position(intent, session, link):
    session_attributes = {}
    reprompt_text = None
    should_end_session = True
    clouddata = urllib.urlopen(link) # Get your data
    data = json.load(clouddata) 
    
    
    slots = intent['slots']
    if 'Position' in slots and 'value' in slots['Position']:
        number = slots['Position']['value']
    else:
        return build_response(session_attributes, build_speechlet_response(
            "Invalid Request", "Please ask for an entry between 1 and 40", reprompt_text, should_end_session))
    
    if len(data['entries']) is not 40:
        return build_response(session_attributes, build_speechlet_response(
        "Top 40", "The UK Top 40 is currently updating on BBC Radio 1. Please try again later", reprompt_text, should_end_session))
    
    if number.isdigit() and (1 <= int(number) <= 40):
        #Save current text value
        num = number
        
        # -1 as array starts from 0
        number = int(number) -1
        print(number)
        
        
        entries = data['entries']
        entry = entries[number]
        
        artist = entry['artist']
        song = entry['title']
        
        speech_output = "This week's number "+num+" is " + song + " by " + artist
        card_title = "Entry "+num
    else:
        speech_output = "Please ask for an entry between 1 and 40."
        card_title = "Invalid Entry"
    
    speech_output = speech_output.replace("&", "and")
    speech_output = speech_output.replace("+", "by")
    speech_output = speech_output.replace("(", " ")
    speech_output = speech_output.replace(")", " ")
    
    should_end_session = True
    return build_response(session_attributes, build_speechlet_response(
        card_title, speech_output, reprompt_text, should_end_session))
        
def run_get_Number_One(intent, session, link, OneOrThree):
    session_attributes = {}
    reprompt_text = None
    should_end_session = False
    clouddata = urllib.urlopen(link) # Get your data
    data = json.load(clouddata)   
    speech_output = ""
    
    if len(data['entries']) is not 40:
        return build_response(session_attributes, build_speechlet_response(
        "Top 40", "The UK Top 40 is currently updating on BBC Radio 1. Please try again later ", reprompt_text, should_end_session))

    if OneOrThree is 1:
        artist = data['entries'][0]['artist']
        song = data['entries'][0]['title']
        speech_output = "This week's number one is " + song + " by " + artist
        card_title = "Top Entry this week"
    else:
        speech_output += "This weeks U K top fourty overview. "
        card_title = "Top 3 Overview"
        artist = data['entries'][2]['artist']
        song = data['entries'][2]['title']
        speech_output += "At number three, we have the song, " + song + " by " + artist
        artist = data['entries'][1]['artist']
        song = data['entries'][1]['title']
        speech_output += ". Followed by number two. " + song + " by " + artist
        artist = data['entries'][0]['artist']
        song = data['entries'][0]['title']
        speech_output += ". Finally This weeks number one U K top fourty is. . ." + song + " by " + artist
    
    speech_output = speech_output.replace("&", "and")
    speech_output = speech_output.replace("+", "by")
    speech_output = speech_output.replace("(", " ")
    speech_output = speech_output.replace(")", " ")
    
    should_end_session = True
    return build_response(session_attributes, build_speechlet_response(
        card_title, speech_output, reprompt_text, should_end_session))

# --------------- Helpers that build all of the responses ----------------------

def build_speechlet_response(title, output, reprompt_text, should_end_session):
    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 build_response(session_attributes, speechlet_response):
    return {
        'version': '1.0',
        'sessionAttributes': session_attributes,
        'response': speechlet_response
    }

Intent Schema

JSON
Add this to the Alexa skills development console.
{
    "intents": [
        {
            "intent": "TopFourty",
            "slots": [
                {
                    "name": "Position",
                    "type": "AMAZON.NUMBER"
                }
            ]
        },
        {
            "intent": "NumberOne"
        },
        {
            "intent": "TopThree"
        },
        {
            "intent": "AMAZON.HelpIntent"
        },
        {
            "intent": "AMAZON.StopIntent"
        }
    ]
}

Sample Uttutances

Tex
Add this to the Alexa skills development console.
TopFourty {Position}
TopFourty number {Position}
TopFourty who is position {Position}
TopFourty who is position {Position} this week
TopFourty what is {Position}
TopFourty what is number {Position}
TopFourty tell me {Position}
TopFourty tell me number {Position}
TopFourty what song is {Position}
TopFourty what is the entry for number {Position}
TopFourty what is number {Position} entry

NumberOne what is the best song to listen to
NumberOne what's the best song to listen to
NumberOne who is the top artist this week
NumberOne who's the top artist this week
NumberOne what song is top this week
NumberOne who is first this week
NumberOne who's first this week
NumberOne tell me what song is top
NumberOne tell me what artist is top

TopThree who is the top three
TopThree who's the top three
TopThree overview
TopThree give me an overview of this week
TopThree give me an overview of this week's entries
TopThree give me the top three
TopThree who is top this week
TopThree Top Three

UK Top 40

PHP
This code scrapes the Official UK Top 40 from the BBC and presents it in a restful Json API.
<?php

   

   $singles_chart_url = 'http://www.bbc.co.uk/radio1/chart/singles/print';

   $albums_chart_url = 'http://www.bbc.co.uk/radio1/chart/albums/print';

   

   // Get the mode from the user:

   $mode = 'singles';

 



//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////

 

$imgurl="http://www.bbc.co.uk/radio1/chart/singles";

include 'imageGetter.php';



//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////



   // This is an array of elements to remove from the content before stripping it:

   $newlines = array("\t", "\n", "\r", "\x20\x20", "\0", "\x0B");

   

   switch($mode)

   {

   	// They want the Singles chart, or haven't specified what they want:

   	case 'singles':

   	case '':

   	default:

   		$content = file_get_contents($singles_chart_url);

   		$start_search = '<table border="1" cellpadding="3" cellspacing="0" width="100%" style="font-family: Arial; font-size: 70%;"><tr><th>Pos.</th><th>Status</th><th>Prev.</th><th>Wks</th><th>Artist</th><th>Title</th></tr>';

   		break;

   	

   	// They want the Album chart:

   	case 'albums':

   		$content = file_get_contents($albums_chart_url);

   		$start_search = '<table border="1" cellpadding="3" cellspacing="0" width="100%" style="font-family: Arial; font-size: 70%;"><tr><th>Pos.</th><th>Status</th><th>Prev.</th><th>Wks</th><th>Artist</th><th>Title</th></tr>';

   		break;

   }

   

   $content = str_replace($newlines, "", html_entity_decode($content));

   

   // Work out where we need to start the scrape from:

   $scrape_start = strpos($content, $start_search);

   $scrape_end   = strpos($content, '</table>', $scrape_start);

   $the_table    = substr($content, $scrape_start, ($scrape_end - $scrape_start));

   $the_table    = str_replace(' style="font-family: Arial; font-size: 70%;"', '', $the_table);

   

   // Get the date and convert it to a timestamp:

   $heading_start = strpos($content, '<h2');

   $heading_end   = strpos($content, '</h2>', $heading_start);

   $the_heading   = strip_tags(substr($content, $heading_start, ($heading_end - $heading_start)));

   

   // Now loop through the rows and get the data we need:

   preg_match_all("|<tr(.*)</tr>|U", $the_table, $rows);

   

header('Content-type: application/json');

header('Cache-Control: no-cache, must-revalidate');

header('Access-Control-Allow-Origin: *');  

echo '{';

echo "\n\t" . '"chartDate" : ' . time() . ', ';

echo "\n\t" . '"retrieved" : ' . time() . ', ';

echo "\n\t" . '"entries" : ';

echo "\n\t" . '[';

$count = 0;

foreach($rows[0] as $row)

{

// Check it's OK:

if(!strpos($row, '<th'))

{

// Get the cells:

preg_match_all("|<td(.*)</td>|U", $row, $cells);

$cells = $cells[0];

$position = strip_tags($cells[0]);

$prev_pos = strip_tags($cells[2]);

$weeks	  = strip_tags($cells[3]);

$artist   = strip_tags($cells[4]);

$title    = strip_tags($cells[5]);

$prev_pos_int = ($prev_pos == 'none') ? 0 : $prev_pos;

$position_change = $prev_pos - $position;

if($position_change < 0)  	$position_string = 'down';

if($position_change == 0) 	$position_string = 'none';

if($position_change > 0)	$position_string = 'up';



$move_val = signed2unsigned($position_change);

echo "\n\t\t" . '{';

echo "\n\t\t\t" . '"position" : ' . $position . ', ';

echo "\n\t\t\t" . '"artist" : "' . $artist . '", ';

echo "\n\t\t\t" . '"title" : "' . $title . '", ';

echo "\n\t\t\t" . '"singleimgurl" : "' . $singleimage[$count + 1]. '", '; //'http://c352663.r63.cf1.rackcdn.com/parts/t-shirts/transparent.gif' . '", '; //

echo "\n\t\t\t" . '"youtubelink" : "' . "http://www.google.com/search?btnI=I'm+Feeling+Lucky&tbm=vid&q=site:www.youtube.com+" . $artist . "+" . $title.' ", ';

echo "\n\t\t\t" . '"youtubelogo" : "' . "http://ut4.azurewebsites.net/youtube.png".'" ';

echo ($count != (count($rows[0]) - 2)) ? "\n\t\t" . '}, ' : "\n\t\t" . '}';

$count++;

}

}

echo "\n\t" . ']';

echo "\n" . '}';



// Function to convert a signed integer to an unsigned integer:

function signed2unsigned($integer)

{

return ($integer < 0) ? ($integer - $integer) - $integer : $integer;

}

?>

Credits

Dean Meehan

Dean Meehan

1 project • 0 followers

Comments