1

I'm having fun with the Alexa API, so I downloaded a Hello World example from here https://developer.amazon.com/appsandservices/solutions/alexa/alexa-skills-kit/getting-started-guide

I managed to made some minor changes and have Alexa say other things. But now I want to have a real world example working, so I tried to get the latest tweet for user.

so I coded a twitter function and it works, I see the tweet on my console. Besides, the downloaded example works just fine too. But now, when I try to combine them by adding my twitter function into the Alexa example, it throws the following error when trying to print the value (if I don't print it, it doesn't break):

{"errorMessage": "Exception: ReferenceError: data is not defined"}

here is the code but the modified function is getWelcomeResponse()

// Route the incoming request based on type (LaunchRequest, IntentRequest,
// etc.) The JSON body of the request is provided in the event parameter.
exports.handler = function (event, context) {
    try {
        console.log("event.session.application.applicationId=" + event.session.application.applicationId);

    /**
     * Uncomment this if statement and populate with your skill's application ID to
     * prevent someone else from configuring a skill that sends requests to this function.
     */
    /*
    if (event.session.application.applicationId !== "amzn1.echo-sdk-ams.app.[unique-value-here]") {
         context.fail("Invalid Application ID");
     }
    */

    if (event.session.new) {
        onSessionStarted({requestId: event.request.requestId}, event.session);
    }

    if (event.request.type === "LaunchRequest") {
        onLaunch(event.request,
                 event.session,
                 function callback(sessionAttributes, speechletResponse) {
                    context.succeed(buildResponse(sessionAttributes, speechletResponse));
                 });
    }  else if (event.request.type === "IntentRequest") {
        onIntent(event.request,
                 event.session,
                 function callback(sessionAttributes, speechletResponse) {
                     context.succeed(buildResponse(sessionAttributes, speechletResponse));
                 });
    } else if (event.request.type === "SessionEndedRequest") {
        onSessionEnded(event.request, event.session);
        context.succeed();
    }
} catch (e) {
    context.fail("Exception: " + e);
}
};

/**
 * Called when the session starts.
 */
function onSessionStarted(sessionStartedRequest, session) {
    console.log("onSessionStarted requestId=" + sessionStartedRequest.requestId
                + ", sessionId=" + session.sessionId);
}

/**
 * Called when the user launches the skill without specifying what they want.
 */
function onLaunch(launchRequest, session, callback) {
    console.log("onLaunch requestId=" + launchRequest.requestId
                + ", sessionId=" + session.sessionId);

    // Dispatch to your skill's launch.
    getWelcomeResponse(callback);
}

/**
 * Called when the user specifies an intent for this skill.
 */
function onIntent(intentRequest, session, callback) {
    console.log("onIntent requestId=" + intentRequest.requestId
                + ", sessionId=" + session.sessionId);

    var intent = intentRequest.intent,
        intentName = intentRequest.intent.name;

    // Dispatch to your skill's intent handlers
    if ("MyColorIsIntent" === intentName) {
        setColorInSession(intent, session, callback);
    } else if ("WhatsMyColorIntent" === intentName) {
        getColorFromSession(intent, session, callback);
    } else if ("HelpIntent" === intentName) {
        getWelcomeResponse(callback);
    } else {
        throw "Invalid intent";
    }
}

/**
 * Called when the user ends the session.
 * Is not called when the skill returns shouldEndSession=true.
 */
function onSessionEnded(sessionEndedRequest, session) {
    console.log("onSessionEnded requestId=" + sessionEndedRequest.requestId
                + ", sessionId=" + session.sessionId);
    // Add cleanup logic here
}

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

function getWelcomeResponse(callback) {


    var twit = require('twitter'),
    twitter = new twit({
        consumer_key:'***',
        consumer_secret:'***',
        access_token_key:'***',
        access_token_secret:'***'
    });

    //var count = 0;
    var util = require('util');





    params = {
      screen_name: 'kilinkis', // the user id passed in as part of the route
      count: 1 // how many tweets to return
    };

    // request data 
    twitter.get('https://api.twitter.com/1.1/statuses/user_timeline.json', params, function (data) {

      console.log(util.inspect(data[0].text));

    });

    // If we wanted to initialize the session to have some attributes we could add those here.
    var sessionAttributes = {};
    var cardTitle = "Welcome";
    /*var speechOutput = "Welcome to the Alexa Skills Kit sample, "
                + "Please tell me your favorite color by saying, "
                + "my favorite color is red";*/
    //var speechOutput=util.inspect(data[0].text);
    var speechOutput=data[0].text;
    // 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.
    var repromptText = "Please tell me your favorite color by saying, "
                + "my favorite color is red";
    var shouldEndSession = true;

    callback(sessionAttributes,
             buildSpeechletResponse(cardTitle, speechOutput, repromptText, shouldEndSession));
}

/**
 * Sets the color in the session and prepares the speech to reply to the user.
 */
function setColorInSession(intent, session, callback) {

    var cardTitle = intent.name;
    var favoriteColorSlot = intent.slots.Color;
    var repromptText = "";
    var sessionAttributes = {};
    var shouldEndSession = false;
    var speechOutput = "";

    if (favoriteColorSlot) {
        favoriteColor = favoriteColorSlot.value;
        sessionAttributes = createFavoriteColorAttributes(favoriteColor);
        speechOutput = "I now know your favorite color is " + favoriteColor + ". You can ask me "
                + "your favorite color by saying, what's my favorite color?";
        repromptText = "You can ask me your favorite color by saying, what's my favorite color?";
    } else {
        speechOutput = "I'm not sure what your favorite color is, please try again";
        repromptText = "I'm not sure what your favorite color is, you can tell me your "
                + "favorite color by saying, my favorite color is red";
    }

    callback(sessionAttributes,
             buildSpeechletResponse(cardTitle, speechOutput, repromptText, shouldEndSession));
}

function createFavoriteColorAttributes(favoriteColor) {
    return {
        favoriteColor: favoriteColor
    };
}

function getColorFromSession(intent, session, callback) {
    var cardTitle = intent.name;
    var favoriteColor;
    var repromptText = null;
    var sessionAttributes = {};
    var shouldEndSession = false;
    var speechOutput = "";

    if(session.attributes) {
        favoriteColor = session.attributes.favoriteColor;
    }

    if(favoriteColor) {
        speechOutput = "Your favorite color is " + favoriteColor + ", goodbye";
        shouldEndSession = true;
    }
    else {
        speechOutput = "I'm not sure what your favorite color is, you can say, my favorite color "
                + " is red";
    }

    // Setting repromptText to null signifies that we do not want to reprompt the user.
    // If the user does not respond or says something that is not understood, the session
    // will end.
    callback(sessionAttributes,
             buildSpeechletResponse(intent.name, speechOutput, repromptText, shouldEndSession));
}

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

function buildSpeechletResponse(title, output, repromptText, shouldEndSession) {
    return {
        outputSpeech: {
            type: "PlainText",
            text: output
        },
        card: {
            type: "Simple",
            title: "SessionSpeechlet - " + title,
            content: "SessionSpeechlet - " + output
        },
        reprompt: {
            outputSpeech: {
                type: "PlainText",
                text: repromptText
            }
        },
        shouldEndSession: shouldEndSession
    }
}

function buildResponse(sessionAttributes, speechletResponse) {
    return {
        version: "1.0",
        sessionAttributes: sessionAttributes,
        response: speechletResponse
    }
}

Can some one please guide me on what's wrong? it's probably a scope issue, I'm not sure.

thetaiko
  • 7,816
  • 2
  • 33
  • 49
kilinkis
  • 584
  • 2
  • 13
  • 26
  • Looks like "data" was defined within the block between line 119 and 123. So the console.log() should be okay. In line 132, you're setting var speechOutput=data[0].text. Does it break if you replace "data[0].text" with something else? – Christina Aug 31 '15 at 20:30
  • oh, I see what you mean. You are right, data is not visible from outside that block (it still breaks if I change that variable name). What would be the best way to make data visible from outside that block? – kilinkis Aug 31 '15 at 22:44
  • EDIT: I defined a string variable outside that block with "aaa" and then changed the content inside the block to the tweet text. But when I print it, it still has "aaa". Is it possible that the twitter.get method is not executing or something? – kilinkis Aug 31 '15 at 23:20
  • Is twitter.get() an asynchronous call? If so, your function may have finished execution before the twitter callback return. As a proof of concept, try moving the rest of the logic (line 125-140) into the twitter.get() callback and see what happens. – Christina Sep 01 '15 at 18:18
  • yes, it is asynchronous indeed. I did what you said and now the error is: `{ "errorMessage": "Task timed out after 3.00 seconds" }` – kilinkis Sep 02 '15 at 14:49
  • is the only solution to rewrite it in Java? – kilinkis Sep 03 '15 at 15:46
  • It sounds like the twitter callback was not being triggered. I would suggest you to double-check your twitter implementation, ensure it's being called, and then check if it takes more than 3 seconds to trigger since it's asynchronous. – Christina Sep 03 '15 at 23:16
  • you are right, but i dont know how to make it "wait" for the function. I'm not sure it can be done being async. I tried a couple of ways, none them worked. – kilinkis Sep 08 '15 at 20:46

1 Answers1

1

Move your call back inside the twitter get function. Then your callback will be called on a successful get from the twitter api. Also you will have access to the data object. You will probably want to add a failure case as well and include a context.fail().

If you need to, you can also update the timeout parameter under the configuration tab of the AWS console. Its under advanced settings. Also, Its often useful to take Alexa out of the equation when debugging and just get the twitter api piece working first.

    // request data 
twitter.get('https://api.twitter.com/1.1/statuses/user_timeline.json', params, function (data) {

  console.log(util.inspect(data[0].text));

  // If we wanted to initialize the session to have some attributes we could add those here.
  var sessionAttributes = {};
  var cardTitle = "Welcome";
  var speechOutput=data[0].text;
  var repromptText = "";
  var shouldEndSession = true;

  callback(sessionAttributes,
           buildSpeechletResponse(cardTitle, speechOutput, repromptText, shouldEndSession));

});
inic
  • 522
  • 4
  • 10