Shranav Palakurthi
Published © GPL3+

Tick Tock Stop - A Fast Paced Game for Alexa!

Tick Tock Stop is a fast-paced game in which instead of being timed, you become the timer!

AdvancedFull instructions provided2 hours918

Things used in this project

Hardware components

Amazon Echo
Amazon Alexa Amazon Echo
A cloud-based voice assistant which you can talk to! Pretty cool, right? We will be using this as a voice interface to the user.
×1
Amazon Alexa Echo Buttons
A Bluetooth-controlled button which connects to the Amazon Alexa! We will be using these as a simple visual and physical interface to the user.
×1

Software apps and online services

AWS Lambda
Amazon Web Services AWS Lambda
This will be working as the brains of the project, handling the requests and giving the responses to the Alexa.
Alexa Skills Kit
Amazon Alexa Alexa Skills Kit
This is a kit we can use to quickly build responses for Alexa.

Story

Read more

Schematics

Structure Overview

This picture outlines how the basic Amazon Alexa system

Code

Interaction Model JSON

JSON
This is the basic interaction model for the skill.
{
    "interactionModel": {
        "languageModel": {
            "invocationName": "tick tock stop",
            "intents": [
                {
                    "name": "AMAZON.CancelIntent",
                    "samples": [
                        "cancel"
                    ]
                },
                {
                    "name": "AMAZON.HelpIntent",
                    "samples": [
                        "i dont know how to play",
                        "help me",
                        "help"
                    ]
                },
                {
                    "name": "AMAZON.StopIntent",
                    "samples": [
                        "end",
                        "stop"
                    ]
                },
                {
                    "name": "AMAZON.NavigateHomeIntent",
                    "samples": [
                        "restart",
                        "go home"
                    ]
                },
                {
                    "name": "YesIntent",
                    "slots": [],
                    "samples": [
                        "aye",
                        "sure",
                        "yeah",
                        "yes"
                    ]
                },
                {
                    "name": "NoIntent",
                    "slots": [],
                    "samples": [
                        "nope",
                        "nah",
                        "no"
                    ]
                }
            ],
            "types": []
        }
    }
}

The Main Code

JavaScript
This is the main script that runs and handles the request for Alexa
/*
  MIT LICENSE

Copyright 2018 Shranav Palakurthi

Permission is hereby granted, free of charge, to any person obtaining a copy of 
this software and associated documentation files (the "Software"), to deal in 
the Software without restriction, including without limitation the rights to 
use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of
the Software, and to permit persons to whom the Software is furnished to do so, 
subject to the following conditions:

The above copyright notice and this permission notice shall be included in all 
copies or substantial portions of the Software.

THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR 
COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER 
IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN 
CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
*/

/*
  _        ____         _
 / \  |    |     \  /  / \  |
/   \ |    |___   \/  /   \ |
|___| |    |      /\  |___| |
|   | |___ |___  /  \ |   | o

A skill by Shranav Palakurthi 
made with ❤

*/

'use strict';

const timebuffer = 10000;                                                       // Amount of 'padding' to add for echo buttons timeout during game

const Alexa = require('ask-sdk-core');                                          // Amazon Alexa SDK

const misunderstandText = 'I\'m sorry, I dont understand.';                     // What Alexa says if she doesn't understand something

const welcomeBackStrings = [                                                    // Some slightly altered phrases which welcome back the player if they restart the game
`<speak>
Welcome back to tick tock stop!
<break time="1s"/>
wait a minute!
<audio src="https://s3.amazonaws.com/ticktockstopsounds/recordScratch.mp3"/>
<break time="1s"/>
We already went over this. Press your button if you're playing!
</speak>`,
'I knew you\'d be back! press your button if you are playing.?',
`welcome back to yet ANOTHER round of tick tock stop!
Press your button if you're playing!`];

const help =                                                                    // What Alexa says if you ask for help
`Tick Tock Stop is a fast-paced game where instead of being timed,              
you become the timer! I\'ll give you a time between 5 and 10 seconds, and when 
you think that time has passed, press your echo button! 
The closest player wins! Does everybody understand?`;

const rollCallDirective = {                                                     // Directive to set up the Echo Button for Roll Call
"type": "GameEngine.StartInputHandler",
"timeout": 20000,                                                               // Time until the Alexa ignores button presses
"proxies": ["player 1", "player 2", "player 3", "player 4"],
"recognizers": {                                                                // Defines which Button Presses your Skill would like to receive as events.
  "button_down_recognizer": {
    "type": "match",
    "fuzzy": false,
    "anchor": "end",
    "pattern": [{
      "action": "down"
    }]
  },
  "button_up_recognizer": {
    "type": "match",
    "fuzzy": false,
    "anchor": "end",
    "pattern": [{
      "action": "up"
    }]
  }
},
"events": {                                                                     // These events will be sent to your Skill in a request.
  "button_down_event": {
    "meets": ["button_down_recognizer"],
    "reports": "matches",
    "shouldEndInputHandler": false
  },
  "button_up_event": {
    "meets": ["button_up_recognizer"],
    "reports": "matches",
    "shouldEndInputHandler": false
  },
  "timeout": {
    "meets": ["timed out"],
    "reports": "history",
    "shouldEndInputHandler": true
  }
}
};

const rollCallLight = {                                                         // A SetLight directive to make the echo buttons pulse.
 "type": "GadgetController.SetLight",
 "version": 1,
 "parameters": {
    "triggerEvent": "none",
    "triggerEventTimeMs": 0,
    "animations": [ 
     {
        "repeat": 10,
        "targetLights": ["1"],
        "sequence": [ 
          {
           "durationMs": 1000,
           "blend": true,
           "color": "FF0000"
          },
          {
           "durationMs": 1000,
           "blend": true,
           "color": "00FF00"
          },
          {
           "durationMs": 1000,
           "blend": true,
           "color": "0000FF"
          },
        ] 
      }
    ]
  }
};

const winnerLightDef = {                                                        // Flashes a single Echo Button a certain color
 "type": "GadgetController.SetLight",
 "version": 1,
 "parameters": {
    "triggerEvent": "none",
    "triggerEventTimeMs": 0,
    "animations": [ 
     {
        "repeat": 1,
        "targetLights": ["1"],
        "sequence": [ 
          {
           "durationMs": 2500,
           "blend": true,
           "color": "000000"
          },
          {
           "durationMs": 1000,
           "blend": true,
           "color": "00FF00"
          },
        ] 
      }
    ]
  }
};

const darkLight = {                                                             // Turns off a certain light
 "type": "GadgetController.SetLight",
 "version": 1,
 "parameters": {
    "triggerEvent": "none",
    "triggerEventTimeMs": 0,
    "animations": [ 
     {
        "repeat": 1,
        "targetLights": ["1"],
        "sequence": [ 
          {
           "durationMs": 1000,
           "blend": true,
           "color": "000000"
          },
        ] 
      }
    ]
  }
};

const LaunchRequestHandler = {                                                  // What Alexa does when the skill launches
  canHandle(handlerInput) {                                                     // Criterion to handle request
    return handlerInput.requestEnvelope.request.type === 'LaunchRequest';
  },
  handle(handlerInput) {                                                        // Request handler
    var attributes = handlerInput.attributesManager.getSessionAttributes();
    
    console.log(JSON.stringify(attributes));
    
    if (!attributes.phase) {                                                    // Set to phase 1 (startup)
      attributes.phase = 1;
    }
    
    var speechText =                                                            // Say intro
    `<speak>
    <audio src="https://s3.amazonaws.com/ticktockstopsounds/introDing.mp3"/>
    Welcome to tick tock stop!
    I\'m alexa, your host for today!
    <break time="500ms"/>
    Would you like to play?
    </speak>`;
    
    return handlerInput.responseBuilder                                         // Build and return response
      .speak(speechText)  
      .reprompt(speechText)
      .withSimpleCard('Welcome to Tick Tock Stop!', 'Would you like to play?')
      .getResponse();
  },
};

const YesIntentHandler = {                                                      // What Alexa does when the user confirms an action
  canHandle(handlerInput) {                                                     // Criterion to handle
    return handlerInput.requestEnvelope.request.type === 'IntentRequest'
      && handlerInput.requestEnvelope.request.intent.name === 'YesIntent';
  },
  handle(handlerInput) {                                                        // Handle Request
    
    var attributes = handlerInput.attributesManager.getSessionAttributes();     // Grab attributes
    
    console.log(JSON.stringify(attributes));                                    // Debug Log attributes
    
    var speechText = misunderstandText;                                         // By default say misunderstand text
    
    var shouldEnd = false;                                                      // By default, do not end skill session
    
    switch (attributes.phase) {
      case 1:                                                                   // Phase 1 (startup) response
        attributes.phase = 2;                                                   // Set to Phase 2 (help)
        speechText =                                                            // Ask if anybody needs help
        `Let\'s get this show on the road! Does everybody know how to play?`;
        return handlerInput.responseBuilder
          .speak(speechText)
          .reprompt(speechText)
          .withSimpleCard('Let\'s get Started!', 'Does everybody know how to play?')
          .getResponse();
      case 2:                                                                   // Phase 2 (help) response
        attributes.phase = 3;                                                   // Set to Phase 3 (roll call)
        speechText =                                                            // Instruct user how to do roll call to wake Echo Buttons
        `Perfect! We\'ll start by seeing how many players there are. 
        If you are playing, press your button!`;
        return handlerInput.responseBuilder
          .speak(speechText)
          .reprompt(speechText)
          .withSimpleCard('Roll Call!', 'Press your Echo Button!')
          .addDirective(rollCallDirective)                                      // Add roll call listener to response (initializes buttons)
          .addDirective(rollCallLight)                                          // Add Echo Button SetLight routine (makes buttons all rainbowy)
          .getResponse();
      case 3:                                                                   // Phase 3 (roll call) response
        speechText =                                                            // Instruct users to wake >1 echo buttons for roll call
        'I\'m restarting the roll call. If you are playing, press your button!';
        return handlerInput.responseBuilder
          .speak(speechText)
          .reprompt(speechText)
          .withSimpleCard('Restarting Roll Call.', 'Press your button!')
          .addDirective(rollCallDirective)                                      // Add roll call listener to response (initializes buttons)
          .addDirective(rollCallLight)                                          // Add Echo Button SetLight routine (makes buttons all rainbowy)
          .getResponse();
      case 4:                                                                   // Phase 4 (game confirm) response
        attributes.phase = 5;                                                   // Set to Phase 5 (game)
        speechText =                                                            // Play start sound (3 beeps)
        '<audio src="https://s3.amazonaws.com/ticktockstopsounds/start.mp3"/>';
        attributes.target = Date.now() + attributes.gameTime * 1000 + 2500;     // Calculate target date
        
        console.log(                                                            // Do a log of the target time for debugging
          'current: ' 
        + new Date() 
        + '\ttarget: ' 
        + (attributes.target - Date.now()));
        
        var gameStartDirective = rollCallDirective;                             // Create editable copy of roll call directive
        
        gameStartDirective.timeout =                                            // Set timeout to target time + 2.5 seconds + time buffer
        attributes.gameTime * 1000 
        + 2500 
        + timebuffer;
        
        return handlerInput.responseBuilder                                     // Build a response
          .speak(speechText)
          .reprompt(speechText)
          .withSimpleCard('Get Ready...', 'Listen for the high pitch!')         
          .addDirective(gameStartDirective)                                     // Add modified roll call directive for game use
          .getResponse();
      case 6:                                                                   // Phase 6 (game restart + roll call) response
        var targetAttributes = {                                                // Create empty object with only game phase in it (will explain later)
          phase: 3                                                              // Set to Phase 3 (roll call)
        };
        handlerInput.attributesManager.setSessionAttributes(targetAttributes);  // Clear session attributes with just phase object; restart with fresh session
        speechText =                                                            // Sets speech text to random welcome back string
        welcomeBackStrings[getRandomInt(welcomeBackStrings.length)];
        return handlerInput.responseBuilder
          .speak(speechText)
          .reprompt(speechText)
          .withSimpleCard('Roll Call!', 'Press your Echo Button!')
          .addDirective(rollCallDirective)                                      // Add roll call listener to response (initializes buttons)
          .addDirective(rollCallLight)                                          // Add Echo Button SetLight routine (makes buttons all rainbowy)
          .getResponse();
      case 7:                                                                   // Phase 7 (game end) response
        speechText =                                                            // Say a goodbye
        'Aww, i guess our fun ends early. I\'ll see you around then! Bye!';
        shouldEnd = true;                                                       // Tell Alexa to end the session
        break;
    }
    
    return handlerInput.responseBuilder
      .speak(speechText)
      .reprompt(speechText)
      .withShouldEndSession(shouldEnd)
      .getResponse();
  },
};

const NoIntentHandler = {                                                       // What Alexa does when the user denys an action
  canHandle(handlerInput) {                                                     // Criterion to handle
    return handlerInput.requestEnvelope.request.type === 'IntentRequest'
      && handlerInput.requestEnvelope.request.intent.name === 'NoIntent';
  },
  handle(handlerInput) {                                                        // Handle Request
    
    var attributes = handlerInput.attributesManager.getSessionAttributes();     // Grab attributes
    
    console.log(JSON.stringify(attributes));                                    // Debug log attriubutes
    
    var speechText = misunderstandText;                                         // Set default text to misunderstand
    
    var shouldEnd = false;                                                      // By default, do not end session
    
    switch (attributes.phase) {
      case 1:                                                                   // Phase 1 (intro) response
        speechText =                                                            // Say goodbye
        `It\'s been great having you! This has been Tick Tock Stop, 
        and I\'ll see you next time!`;
        shouldEnd = true;                                                       // Set session to end
        return handlerInput.responseBuilder
          .speak(speechText)
          .reprompt(speechText)
          .withShouldEndSession(shouldEnd)
          .getResponse();
      case 2:                                                                   // Phase 2 (help) response
        speechText = help;                                                      // Explain the game to the user
        break;
      case 6:                                                                   // Phase 6 (game restart + roll call)
        speechText =                                                            // Say goodbye
        'It\'s been great having you! This has been Tick Tock Stop, and I\'ll see you around.';
        shouldEnd = true;                                                       // End session
        break;
    }

    return handlerInput.responseBuilder
      .speak(speechText)
      .reprompt(speechText)
      .withShouldEndSession(shouldEnd)
      .getResponse();
  },
};

const ButtonDownHandler = {                                                     // What Alexa does when a button is pressed down
  canHandle(handlerInput) {                                                     // Criterion for handling
    console.log(handlerInput.requestEnvelope.request.events[0].name == 'button_down_event');
    
    return handlerInput.requestEnvelope.request.type == 'GameEngine.InputHandlerEvent'
      && handlerInput.requestEnvelope.request.events[0].name == 'button_down_event';
  },
  handle(handlerInput) {                                                        // Handle request
    var speechText = 'This message should not appear, please contact me.';      // Add bug message
    
    var attributes = handlerInput.attributesManager.getSessionAttributes();     // Grab attributes
    
    var request = handlerInput.requestEnvelope.request;                         // Grab request params
    
    console.log(JSON.stringify(request.events));                                // Debug log OwO
    
    switch (attributes.phase) {
      case 3:                                                                   // Phase 3 (roll call) response
        if (!attributes.players) {                                              
          attributes.players = 0;                                               // Create number of players
          attributes.playerIds = [];                                            // Create new list with player ids if not there
        }
        if (attributes.playerIds                                                // Check if button has been pressed befor
        .indexOf(request.events[0].inputEvents[0].gadgetId) == -1) {
          
          attributes.playerIds.push(request.events[0].inputEvents[0].gadgetId); // If new button, record id in playerIds
          attributes.players++;                                                 // Add new player
          
          speechText =                                                          // Play a registering sound
          `<speak>
          <audio src="soundbank://soundlibrary/ui/gameshow/amzn_ui_sfx_gameshow_neutral_response_03"/>
          </speak>`;
        } else {
          speechText = "";                                                      // Do not say something if player has already pressed their button during roll call
        }
        break;
      case 5:                                                                   // Phase 5 (game) respone
        return;                                                                 // Do not respond to button down events during game
      default:                                                                  // Default Phase
        return;                                                                 // Do not respond by default
    }

    return handlerInput.responseBuilder
      .speak(speechText)
      .reprompt(speechText)
      .getResponse();
  },
};

const ButtonUpHandler = {                                                       // What Alexa does when a button is pressed down
  canHandle(handlerInput) {                                                     // Criterion for handling
    console.log(handlerInput.requestEnvelope.request.events[0].name == 'button_up_event');
    
    return handlerInput.requestEnvelope.request.type == 'GameEngine.InputHandlerEvent'
      && handlerInput.requestEnvelope.request.events[0].name == 'button_up_event';
  },
  handle(handlerInput) {                                                        // I keep this code just in case I need to use this handler in a future update
    /*
    var speechText = 'This message should not appear, please contact me.';
    
    var attributes = handlerInput.attributesManager.getSessionAttributes();
    
    var request = handlerInput.requestEnvelope.request;
    
    console.log(JSON.stringify(request.events));
    
    switch (attributes.phase) {
      case 3:
        return;
    }

    return handlerInput.responseBuilder
      .speak(speechText)
      .reprompt(speechText)
      .withSimpleCard(':\'(', speechText)
      .getResponse();
    */
  },
};

const ButtonTimeout = {                                                         // What Alexa does when button listener times out
  canHandle(handlerInput) {                                                     // Criterion for handling
    console.log(handlerInput.requestEnvelope.request.events[0].name == 'timeout');
    
    return handlerInput.requestEnvelope.request.type == 'GameEngine.InputHandlerEvent'
      && handlerInput.requestEnvelope.request.events[0].name == 'timeout';
  },
  handle(handlerInput) {                                                        // Handle Request
    var speechText = 'This message should not appear, please contact me.';      // Add debug message
    
    var attributes = handlerInput.attributesManager.getSessionAttributes();     // Grab attributes
    
    var request = handlerInput.requestEnvelope.request;                         // Grab request
    
    console.log(JSON.stringify(request.events));                                // Debug event in request (what happened before this)
    
    switch (attributes.phase) {
      case 3:                                                                   // Phase 3 (roll call) response
        if (attributes.players > 1) {                                           // Needs at least 2 players to continue
          attributes.gameTime = getRandomInt(10) + 5;                           // Generate time needed until you press the button
          attributes.phase = 4;                                                 // Set to Phase 4 (game confirm)
          speechText =                                                          // Dictate # of player and time unti they should press their buttons
          'There are ' 
          + attributes.players 
          + ' players. You need to press your button after ' 
          + attributes.gameTime + ' seconds. Are you ready?';
        } else {
          attributes.phase = 3;                                                 // Not enough people, go back to roll call Phase 3
          speechText =                                                          // Ask if player wants to restart roll call
          'You need to have at least 2 people to play. Would you like me to redo the roll call?';
        }
        return handlerInput.responseBuilder
          .speak(speechText)
          .reprompt(speechText)
          .addDirective(darkLight)
          .getResponse();
      case 5:                                                                   // Phase 5 (game) handler
        speechText = 'time\'s up! time to see who was the closest! ';           // Initialize string with base text
        var downEvents =                                                        // Filter events for all events which include an Echo Button being pressed down before the timeout
        request.events[0].inputEvents.filter(event => event.action === 'down');
        
        console.log(JSON.stringify(downEvents));                                // Debug log the down events
        
        if (!attributes.scores) {                                               // If no scores array, initialize
          attributes.scores = [];
        }
        var usedIds = [];                                                       // Create array of button ids used
        var highscore = { id : '', score : Infinity};                           // Creat highscore object with a score of infinity. (lower scores are better in this case)
        
        for (var i = 0; i < attributes.players; i++) {                          // Iterate through button events for the amount of players
          if (usedIds.indexOf(downEvents[i].gadgetId) == -1) {                  // Check if button has been seen before
            usedIds.push(downEvents[i].gadgetId);                               // Add new id to seen ids
            
            var delta = new Date(downEvents[i].timestamp) - attributes.target   // Calculate difference between target time and the button down timestamp.
            
            attributes.scores.push({                                            // Add new score to the scores array with the id and score
              id : downEvents[i].gadgetId,
              score : delta
            });
            
            if (Math.abs(delta) < highscore.score) {                            // If the total difference is lower than the high score's difference, set button id as the new high score id.
              highscore.id = downEvents[i].gadgetId;
              highscore.score = Math.abs(delta);
            }
          }
        }
        
        console.log(JSON.stringify(highscore));                                 // Debug log player
        
        speechText +=                                                           // Add small end-game brief and play again question to base string
        'The winner\'s button is flashing. They missed by only ' 
        + highscore.score 
        + ' milliseconds! Would you like to play again?';
        
        var winnerLight = winnerLightDef;                                       // Copy winner light definition into editable object
        
        winnerLight.targetGadgets = [highscore.id];                             // Turn on the winner's light
        
        attributes.phase = 6;                                                   // Set to Phase 6 (game restart + roll call)
        
        return handlerInput.responseBuilder
          .speak(speechText)
          .reprompt(speechText)
          .addDirective(winnerLight)                                            // Tell Alexa to make winner button to flash
          .getResponse();
    }

    return handlerInput.responseBuilder
      .speak(speechText)
      .reprompt(speechText)
      .getResponse();
  },
};

const HelpIntentHandler = {                                                     // What Alexa does if the user asks for help
  canHandle(handlerInput) {                                                     // Criterion for handling
    return handlerInput.requestEnvelope.request.type === 'IntentRequest'
      && handlerInput.requestEnvelope.request.intent.name === 'AMAZON.HelpIntent';
  },
  handle(handlerInput) {                                                        // Handle Request
    const speechText = help;                                                    // Explain game to user

    return handlerInput.responseBuilder
      .speak(speechText)
      .reprompt(speechText)
      .withSimpleCard('Help', speechText)
      .getResponse();
  },
};

const CancelAndStopIntentHandler = {                                            // What Alexa does if user tells her to stop
  canHandle(handlerInput) {                                                     // Criterion for handling
    return handlerInput.requestEnvelope.request.type === 'IntentRequest'
      && (handlerInput.requestEnvelope.request.intent.name === 'AMAZON.CancelIntent'
        || handlerInput.requestEnvelope.request.intent.name === 'AMAZON.StopIntent');
  },
  handle(handlerInput) {                                                        // Handle Request
    const speechText =                                                          // Say goodbye
    'It\'s been great having you! This has been Tick Tock Stop, and I\'ll see you next time!';

    return handlerInput.responseBuilder
      .speak(speechText)
      .withSimpleCard('Bye!', speechText)
      .withShouldEndSession(true)                                               // End session
      .getResponse();
  },
};

const SessionEndedRequestHandler = {                                            // What Alexa does if session ended
  canHandle(handlerInput) {                                                     // Criterion for handling
    return handlerInput.requestEnvelope.request.type === 'SessionEndedRequest';
  },
  handle(handlerInput) {                                                        // Handle request
    console.log(                                                                // Debug log reason for session end
    `Session ended with reason: ${handlerInput.requestEnvelope.request.reason}`);

    return handlerInput.responseBuilder.getResponse();
  },
};

const ErrorHandler = {                                                          // What Alexa does if she does not understand what the user says
  canHandle() {                                                                 // Can always handle
    return true;
  },
  handle(handlerInput, error) {
    console.log(`Error handled: ${error.message}`);                             // Log error

    return handlerInput.responseBuilder
      .speak('Sorry, I can\'t understand the command. Please say again.')       // Ask user to repeat themselves
      .reprompt('Sorry, I can\'t understand the command. Please say again.')
      .getResponse();
  },
};

function getRandomInt(max) {                                                    // Denerates random integer from 0 to the variable max
  return Math.floor(Math.random() * Math.floor(max));
}

function scorer(target, pressTime, targetTime) {                                // Finds difference between 2 times
  var delta = new Date(pressTime) - new Date(target);
  
  if (delta < 0) {
    delta = Infinity;
  } else {
    delta = Math.abs(targetTime - delta);
  }
  
  return delta;
}

const skillBuilder = Alexa.SkillBuilders.custom();

var out = skillBuilder                                                          // Build response
  .addRequestHandlers(
    LaunchRequestHandler,
    HelpIntentHandler,
    CancelAndStopIntentHandler,
    SessionEndedRequestHandler,
    YesIntentHandler,
    NoIntentHandler,
    ButtonDownHandler,
    ButtonUpHandler,
    ButtonTimeout
  )
  .addErrorHandlers(ErrorHandler)
.lambda();

exports.handler = out;                                                          // Return response

Credits

Shranav Palakurthi

Shranav Palakurthi

3 projects • 8 followers

Comments