-1

I am trying to build an alexa custom skill. I am facing an issue where I am trying to get a responses from the user for a question which the skill asks the user.

User : Tell me marks of student1 ?
Alexa: Subject
User: Maths
Alexa : student1 marks in maths is {xyz}

or if user doesnt give any input:

User : Tell me marks of student1 ?
Alexa: Subject
User: No Answer
Alexa : Gives marks of all subject for student1.

I am using Node.js. Please tell me how to do this.

KMittal
  • 602
  • 1
  • 7
  • 21
  • Please add more details, where exactly do you encounter with a problem. Your question is so vague. Are you using slots? – SpaceDust__ May 22 '17 at 15:37
  • Please suggest a general approach for this , I am new to this , and just started couple of days back . – KMittal May 22 '17 at 16:25

3 Answers3

4

It's your lucky day. This is how I would do this with multiple MODES in Node.js.

Disclaimer: I am providing this lengthy response because I am desperate for reputation points and would like to become a developer evangelist. ;)

Given the Node.js SDK and it's many features this is the format I would use.

// To zip and upload to lambda
// cd Desktop/StudentSkill
// sudo rm -r foo.zip
// zip -r foo.zip .s

'use strict';
var Alexa = require("alexa-sdk");
var appId = 'YOUR-AMAZON-ALEXA-SKILL-ID';

exports.handler = function(event, context, callback) {
    var alexa = Alexa.handler(event, context);
    alexa.appId = appId;
    alexa.registerHandlers(newSessionHandlers, studentSkillSessionHandlers, specificClassSessionHandlers, functionHandlers);
    alexa.execute();
};


var states = {
    STUDENTMODE:    '_STUDENTMODE',
    CLASSMODE:      '_CLASSMODE',
};

//Variables
var myStudents = {'josh':{'math':'A Plus','english':'A Plus','gym':'C'},'sam':{'math':'A Plus','english':'A minus','gym':'B Plus'}}; //add more classes. 

//Could add an intent to create new students.
//var newStudent = {name:{'math':null,'english':null,'gym':null}};

//Could also add an intent to set grades

var newSessionHandlers = {
    'NewSession': function() {
        this.handler.state = states.STUDENTMODE;
        var message = `Welcome to the School Skill, You may add students, edit grades, and review grades. For a list of uses say information. `; //this skill only contains reviewing grades.
        var reprompt = ` try saying, grades for josh. `;
        this.emit(':ask',message,reprompt);
    },
    'Unhandled': function() {
        console.log("UNHANDLED");
        this.emit('NewSession');
    }
};
/////////////////////////////////
var studentSkillSessionHandlers = Alexa.CreateStateHandler(states.STUDENTMODE, {//Your location
    'NewSession': function () {
        this.emit('NewSession'); // Uses the handler in newSessionHandlers
    },
    //Primary Intents
    'GetGradeIntent': function () { // Sampe Utterance: Tell me the marks of {student}    or    Grades for {student}
      this.handler.state = states.CLASSMODE;  //Change mode to accept a class, the intent handler getClassIntent is only available in CLASSMODE
      this.attributes['CURRENTSTUDENT'] = this.event.request.intent.slots.student.value;
      var message = ` which of `+this.attributes['CURRENTSTUDENT']+`'s classes would you like the grade for, name a class or say all. `;
      var reprompt = message;
      this.emit(':ask',message,reprompt);
    },
    //Help Intents
    "InformationIntent": function() {
      console.log("INFORMATION");
      var message = ` Try saying, Tell me the marks of josh.  `;
      this.emit(':ask', message, message);
    },
    "AMAZON.StopIntent": function() {
      console.log("STOPINTENT");
      this.emit(':tell', "Goodbye!");
    },
    "AMAZON.CancelIntent": function() {
      console.log("CANCELINTENT");
      this.emit(':tell', "Goodbye!");
    },
    'AMAZON.HelpIntent': function() {
        var message = helpMessage;
        this.emit(':ask', message, message);
    },
    //Unhandled
    'Unhandled': function() {
        console.log("UNHANDLED");
        var reprompt = ` That was not an appropriate response. which student would you like grades for.  Say, grades for josh. `;
        this.emit(':ask', reprompt, reprompt);
    }
});

////////////////////////////
/////////////////////////////////
var specificClassSessionHandlers = Alexa.CreateStateHandler(states.CLASSMODE, {//Your location
    'NewSession': function () {
        this.emit('NewSession'); // Uses the handler in newSessionHandlers
    },
    //Primary Intents
    'GetClassIntent': function () { // {className} class. ex: gym class, math class, english class.  It helps to have a word that's not a slot. but amazon may pick it up correctly if you just say {className}
      this.attributes['CLASSNAME'] = this.event.request.intent.slots.className.value;
      var message = ``;
      var reprompt = ``;
      if(this.attributes['CLASSNAME'] != undefined){
        message = ` I didn't get that class name.  would you please repeat it. `;
        reprompt = message;
      }else{
        grade = myStudents[this.attributes['CURRENTSTUDENT']][this.attributes['CLASSNAME']];
        if(grade != undefined){
          this.handler.state = states.STUDENTMODE; //Answer was present. return to student mode.
          message = this.attributes['CURRENTSTUDENT']+`'s `+[this.attributes['CLASSNAME']+` grade is `+aAn(grade)+` `+grade+`. What else would you like to know?`; //Josh's math grade is an A plus.
          reprompt = `what else would you like to know?`;
        }else{
          message = this.attributes['CURRENTSTUDENT']+` does not appear to have a grade for `+[this.attributes['CLASSNAME']+` please try again with a different class or say back.`;
          reprompt = `please try again with a different class or say back.`;
        }
      }
      var message = this.attributes['FROM'] + ' .. '+ ProFirstCity;
      var reprompt = ProFirstReprompt;
      this.emit(':ask',message,reprompt);
    },
    "AllIntent": function() {// Utterance: All, All Classes
      message = ``;
      //Not going to code.
      //Pseudo code
      // for each in json object myStudents[this.attributes['CURRENTSTUDENT']] append to message class name and grade.
      this.emit(':ask', message, message);
    },
    "BackIntent": function() {// Utterance: Back, go back
      var message = ` Who's grade would you like to know. try saying, grades for josh. `;
      this.emit(':ask', message, message);
    },
    //Help Intents
    "InformationIntent": function() {
      console.log("INFORMATION");
      var message = ` You've been asked for which of `+this.attributes['CURRENTSTUDENT']+`'s classes you'd his grade. Please name a class or say back. `;
      this.emit(':ask', message, 'Name a class or say back.');
    },
    "AMAZON.StopIntent": function() {
      console.log("STOPINTENT");
      this.emit(':tell', "Goodbye!");
    },
    "AMAZON.CancelIntent": function() {
      console.log("CANCELINTENT");
      this.emit(':tell', "Goodbye!");
    },
    'AMAZON.HelpIntent': function() {
        var message = helpMessage;
        this.emit(':ask', message, message);
    },
    //Unhandled
    'Unhandled': function() {
        console.log("UNHANDLED");
        var reprompt = ' That was not an appropriate response. Name a class or say back.';
        this.emit(':ask', reprompt, reprompt);
    }
});

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

var functionHandlers = {//NOT USED IN THIS APP //Note tied to a specific mode.

};


//#############################HELPERS VVVV#########################
function getRandomInt(min, max) {
    return Math.floor(Math.random() * (max - min + 1)) + min;
}
function clone(a) {
   return JSON.parse(JSON.stringify(a));
}
function responseRandomizer(responseType){
  let len = responseType.length;
  let index = getRandomInt(0,len-1);
  return responseType[index];
}
var vowels = {}
function aAn(word){
  if(word != ''){
    let first = word[0];
    if(/[aAeEiIoOuU]/.test(first)){
      return 'an';
    }else{
      return 'a';
    }
  }else{
    return '';
  }
}

Please note: this code was adapted from a live skill but has not been tested on it's own.

To start off requesting a follow up question you need to understand the different stateHandlers. When you invoke a new skill it goes to the newSessionHandlers from there you can run some sort of setup code and then change the MODE to a lobby to capture the main intents for the skill. I've named this lobby STUDENTMODE. Inside STUDENTMODE you can ask for the grades of a student and you could theoretically create a new student or add a class or what not. If you use the existing intent GetGradeIntent and supply it an apropriate name it will save the name of the student in the session state and change the mode to CLASSMODE which only accepts the Intents ClassNameIntent and BackIntent. If you try to invoke some other intent you will be reprompted for the name of a class by the UnhandledIntent. Upon provideing an appropriate class or saying "all" you will be provided a response and the mode will be changed back to STUDENTMODE. this plops you back in the lobby where you can ask questions about other students. Voila!

This process of changing modes is much better than Multi Part Intent Schema's like "Tell me grades for {studentName} in {mathClass}". While this can certainly work one reason modes are better is that they allow you to properly handle errors if one of the input values is incorrect, like student name or class name. You can easily ask for a single piece of information instead of asking the user to restate the entire multi part intent. It also allows you to handle getting multiple pieces of information one small piece at a time with ample instructions as apposed to allowing Alexa to continue to ask reprompt questions until all the required slots are filled.

One items I didn't cover.
Where are you storing your students? I have them hard coded into the lambda function. You could connect to amazon's dynamodb and store your session states there so they are available on the next session. that's actually as simple as adding.

alexa.dynamoDBTableName = 'NAME-OF-YOUR-DynamoDB-TABLE'; //You literally dont need any other code it just does the saving and loading??!! WHAT?

to your function here.

exports.handler = function(event, context, callback) {
    var alexa = Alexa.handler(event, context);
    alexa.appId = appId;
    alexa.dynamoDBTableName = 'NAME-OF-YOUR-DynamoDB-TABLE'; //You literally don't need any other code it just does the saving and loading??!! WHAT?
    alexa.registerHandlers(newSessionHandlers, studentSkillSessionHandlers, specificClassSessionHandlers, functionHandlers);
    alexa.execute();
};

You'll need to create a dynamoDB data table and an IAm permission to allow your lambda function to access it. Then, magically, Alexa will create a single row in your data table for each unique user. A single teacher could easily add the students in their class. However, if you're looking for each teacher in a school to access one master database this is likely not the correct approach. There are likely other tutorials on how to connect Alexa to a single data table across multiple users.

I believe the core concern of your question was answered by the different MODES where you can block out unwanted intents. If you found this response helpful I take payment in trident layers and reputation. Thanks!

Caleb Gates
  • 914
  • 7
  • 20
2

Well, it is complicated. There is couple of approaches here you can either follow a dialogue scheme

https://developer.amazon.com/public/solutions/alexa/alexa-skills-kit/docs/dialog-interface-reference

Or you can use intents.

you will have Student intents and Subject Intent.

  "intents": [
    {
      "intent": "Student",
      "slots": [
        {
          "name": "Students",
          "type": "LIST_OF_STUDENTS"
        }
      ]
    },
    {
      "intent": "Subject",
      "slots": [
        {
          "name": "Subjects",
          "type": "LIST_OF_SUBJECTS"
        }
      ]
    }

You will need a dynamo db table where you keep Student name, and in your skill scheme, you will have a list of students, and subjects.

I can not write the whole skill for you, it is way too complicated.

Just follow a tutorial then ask a specific question. https://github.com/alexa/alexa-skills-kit-sdk-for-nodejs

MAKE ME A SKILL is not a question....

SpaceDust__
  • 4,844
  • 4
  • 43
  • 82
2

On thing to be aware of is that Alexa doesn't respond to "no answer". So you'll need to coach the user to respond with "all" or "none".

Ron Lisle
  • 1,164
  • 6
  • 11