import { AudioPlayer } from "./audio-player" ; import { LoggingHelper } from "../core/logging-helper" ; import { ServiceRequest , SessionEndedReason } from "./service-request" ; import request = require ( "request" ) ; import http = require ( "http" ) ; import { InteractionModel } from "./interaction-model" ; import { AlexaContext } from "./alexa-context" ; import { EventEmitter } from "events" ; const Logger = "ALEXA" ; export interface AlexaResponseCallback { ( error , response , request ) : void ; } export enum AlexaEvent { SessionEnded , SkillError , SkillResponse } export class Alexa { private _actionQueue : ActionQueue = new ActionQueue ( ) ; private _context : AlexaContext = null ; private _emitter : EventEmitter = null ; public constructor ( ) { this . _emitter = new EventEmitter ( ) ; } /**
     * Must be called before sending any session-based requests
     */ public startSession ( skillURL , model , audioEnabled , applicationID ? ) { let audioPlayer = null ; if ( audioEnabled ) { audioPlayer = new AudioPlayer ( this ) ; } this . _context = new AlexaContext ( skillURL , model , audioPlayer , applicationID ) ; this . _context . newSession ( ) ; return this ; } public context ( ) { return this . _context ; } private interactionModel ( ) { return this . context ( ) . interactionModel ( ) ; } param utterance param callback public spoken ( utterance , callback ? ) { let intent = this . interactionModel ( ) . sampleUtterances . intentForUtterance ( utterance ) ; if ( intent === null ) { let defaultUtterance = this . interactionModel ( ) . sampleUtterances . defaultUtterance ( ) ; intent = this . interactionModel ( ) . sampleUtterances . intentForUtterance ( defaultUtterance ) ; LoggingHelper . warn ( Logger , "No intentName matches utterance: " + utterance + ". Using fallback utterance: " + intent . utterance ) ; } this . callSkillWithIntent ( intent . intentName , intent . toJSON ( ) , callback ) ; } public launched ( callback ? ) { let serviceRequest = new ServiceRequest ( this . _context ) ; serviceRequest . launchRequest ( ) ; this . callSkill ( serviceRequest , callback ) ; } public sessionEnded ( sessionEndedReason , errorData , callback ? ) { if ( sessionEndedReason === SessionEndedReason . ERROR ) { LoggingHelper . error ( Logger , "SessionEndedRequest:\n" + JSON . stringify ( errorData , null , 2 ) ) ; } let serviceRequest = new ServiceRequest ( this . _context ) ; serviceRequest . sessionEndedRequest ( sessionEndedReason , errorData ) ; this . callSkill ( serviceRequest , callback ) ; this . context ( ) . endSession ( ) ; } param intentName param slots param callback public intended ( intentName , slots ? , callback ? ) { this . callSkillWithIntent ( intentName , slots , callback ) ; } private callSkillWithIntent ( intentName , slots ? , callback ? ) { let self = this ; try { if ( this . _context . audioPlayerEnabled ( ) && this . _context . audioPlayer ( ) . isPlaying ( ) ) { this . _context . audioPlayer ( ) . suspend ( ) ; } let serviceRequest = new ServiceRequest ( this . _context ) . intentRequest ( intentName ) ; if ( slots !== undefined && slots !== null ) { for ( let slotName of Object . keys ( slots ) ) { serviceRequest . withSlot ( slotName , slots [ slotName ] ) ; } } this . callSkill ( serviceRequest , function ( error , responseJSON , requestJSON ) { if ( callback !== undefined && callback !== null ) { callback ( error , responseJSON , requestJSON ) ; } if ( self . _context . audioPlayerEnabled ( ) && self . _context . audioPlayer ( ) . suspended ( ) ) { self . _context . audioPlayer ( ) . resume ( ) ; } } ) ; } catch ( e ) { if ( callback !== undefined && callback !== null ) { callback ( e , null , null ) ; } } } public callSkill ( serviceRequest , callback ? ) { let self = this ; this . sequence ( function ( done ) { self . callSkillImpl ( serviceRequest , callback , done ) ; } ) ; } public sequence ( action ) { this . _actionQueue . enqueue ( action ) ; } private callSkillImpl ( serviceRequest , callback , done ) { let self = this ; if ( serviceRequest . requiresSession ( ) && ! this . context ( ) . activeSession ( ) ) { this . context ( ) . newSession ( ) ; } let requestJSON = serviceRequest . toJSON ( ) ; LoggingHelper . debug ( Logger , "CALLING: " + requestJSON . request . type ) ; let responseHandler = function ( error , response , body ) { if ( self . context ( ) . activeSession ( ) ) { self . context ( ) . session ( ) . used ( ) ; if ( ! error ) { if ( body . response !== undefined && body . response . shouldEndSession ) { self . context ( ) . endSession ( ) ; } else { self . context ( ) . session ( ) . updateAttributes ( body . sessionAttributes ) ; } } } if ( error ) { if ( callback !== undefined && callback !== null ) { callback ( error , null , null ) ; } } else { if ( body . response !== undefined && body . response . directives !== undefined ) { self . _context . audioPlayer ( ) . directivesReceived ( body . response . directives ) ; } if ( callback !== undefined && callback !== null ) { callback ( null , body , requestJSON ) ; } self . _emitter . emit ( AlexaEvent [ AlexaEvent . SkillResponse ] , body , requestJSON ) ; } done ( ) ; } ; this . post ( { url : this . context ( ) . skillURL ( ) , method : "POST" , json : requestJSON , } , responseHandler ) ; } param options param responseHandler protected post ( options , responseHandler ) { request . post ( options , responseHandler ) ; } public on ( event , callback ) { this . _emitter . on ( AlexaEvent [ event ] , callback ) ; } public once ( event , callback ) { this . _emitter . once ( AlexaEvent [ event ] , callback ) ; } public stop ( onStop ) { this . _actionQueue . stop ( function ( ) { onStop ( ) ; } ) ; } } export interface AlexaAction { ( done ) : void ; } export class ActionQueue { private _queue : Array < AlexaAction > = [ ] ; private _stop : boolean = false ; private _stopCallback : Function ; public enqueue ( action ) { this . _queue . push ( action ) ; if ( this . _queue . length === 1 ) { this . next ( ) ; } } public processing ( ) { return ( this . _queue . length > 0 ) ; } public next ( ) { let self = this ; if ( this . _queue . length === 0 ) { return ; } let action = this . _queue [ 0 ] ; action ( function ( ) { self . _queue = self . _queue . slice ( 1 ) ; if ( self . _stop ) { self . _stopCallback ( ) ; } else { self . next ( ) ; } } ) ; } public stop ( onStop ) { this . _stop = true ; if ( this . processing ( ) ) { this . _stopCallback = onStop ; } else { onStop ( ) ; } } }