如何在Lambda函数中捕获超时消息错误以了解Alexa的技能?

时间:2019-03-27 17:46:37

标签: node.js amazon-web-services aws-lambda alexa

我正在构建与AWS lambda函数相关的Alexa技能。出于某些目的,我需要发出http请求,由于我无法预测的连接延迟,这可能会失败。问题是,如果我使用的内存超过128Mb,或者执行时间超过7000ms,lambda会中断我的执行。由于我不想增加时间限制,因此我正在寻找一种方法来防止lambda向我的Alexa技能发送错误消息,该消息将告诉我发生了问题,但向自己发送了一条消息,指出连接可能很慢,Alexa无法找不到用户的要求。

我试图访问控制台报告以检查是否存在超时错误,但是我是Node.js的新手,所以我无法阻止lambda将此错误发送给Alexa。

这是我的lambda代码:

var https = require('https');
const fs = require('fs');


let speechOutput;
let reprompt;
let welcomeOutput = "Salve Guardiano, per ora puoi chiedermi cosa porta Xur questa settimana, quali cala la notte ci sono, dove si trova focolai o anche in che stato si trova la città sognante.";
let welcomeReprompt = "Come posso aiutarti?";
"use strict";
const Alexa = require('alexa-sdk');
const APP_ID = undefined;  // TODO replace with your app ID (OPTIONAL).
speechOutput = '';


const handlers = {
    'LaunchRequest': function () {
        this.emit(':ask', welcomeOutput, welcomeReprompt);
    },
    'AMAZON.HelpIntent': function () {
        speechOutput = 'Per ora puoi chiedermi cosa porta Xur questa settimana, quali cala la notte ci sono, dove si trova focolai o anche in che stato si trova la città sognante.';
        this.emit(':ask', speechOutput);
    },
   'AMAZON.CancelIntent': function () {
        speechOutput = 'Ok, cancello.';
        this.emit(':tell', speechOutput);
    },
   'AMAZON.StopIntent': function () {
        speechOutput = 'Arrivederci, non camperare troppo.';
        this.emit(':tell', speechOutput);
   },
   'SessionEndedRequest': function () {
        speechOutput = '';
        //this.emit(':saveState', true);//uncomment to save attributes to db on session end
        this.emit(':tell', speechOutput);
   },
    'AMAZON.NavigateHomeIntent': function () {
        speechOutput = '';

        //any intent slot variables are listed here for convenience


        //Your custom intent handling goes here
        speechOutput = "Chiedimi aiuto.";
        this.emit(":ask", speechOutput);
    },
    'informazioni_settimanali': async function () {

        //prendo il codice corrispondente allo slot comunicato
        let activityID = this.event.request.intent.slots.activity.resolutions.resolutionsPerAuthority[0].values[0].value.id;
        //prendo il token di accesso
        let token = this.event.context.System.user.accessToken;

        speechOutput = '';

        speechOutput = await richiesta_activities(activityID);
        speechOutput = speechOutput + "Posso fare altro per la tua luce?"

        this.emit(":ask", speechOutput );
    },  
    'xur_location': async function () {
        speechOutput='Non ho ancora sviluppato questa funzionalità. Posso aiutarti in altro modo?';
        this.emit(":ask", speechOutput, speechOutput);
    },
    'what_sells_xur': async function () {
        //prendo il token di accesso
        let token = this.event.context.System.user.accessToken;
        if(token==null){
            speechOutput = 'Per richiedere questo tipo di informazioni devi collegare il tuo account Bungie alla skill Spettro dall\'app Alexa.';
            this.emit(":tellWithLinkAccountCard",speechOutput);

        }else{
            speechOutput='';
            speechOutput = await xur_inventory(token);
            speechOutput = speechOutput + " Posso fare altro per la tua luce?"
            this.emit(":ask", speechOutput );
        }
    },
    'Unhandled': function () {
        speechOutput = "Perdonami, non ho capito.";
        this.emit(':ask', speechOutput, speechOutput);
    }
};

exports.handler = (event, context,callback) => {
    const alexa = Alexa.handler(event, context);
    alexa.appId = APP_ID;
    // To enable string internationalization (i18n) features, set a resources object.
    //alexa.resources = languageStrings;
    alexa.registerHandlers(handlers);
    //alexa.dynamoDBTableName = 'DYNAMODB_TABLE_NAME'; //uncomment this line to save attributes to DB
    alexa.execute();
};

//    END of Intent Handlers {} ========================================================================================


async function xur_inventory(token){
    let Hash='2190858386';  //xur's hash
    let endpoint = '/Platform/Destiny2/2/Profile/4611686018456602435/Character/2305843009289717384/Vendors/?components=vendorsales';
    const response = await httpTokenGet(endpoint,token);
    let ans='';
    //console.log(response);
    if(response.Response.sales.data[Hash]!=null){
        let items = new Array();
        var i=0;
        let temp;
        temp = response.Response.sales.data[Hash].saleItems;
        for(var key in temp){
            //console.log(key);
            //console.log(temp[key].itemHash);
            items[i]=temp[key].itemHash;
            i++;
        }

        //LETTURA AGGIORNATA DAL WEB
        let jsonendpoint = '/it/reducedCollectableInventoryItems.json';
        const DestinyItems = await httpJsonGet(jsonendpoint);
        //console.log(DestinyItems);
        let itemsPlain = new Array();
        let itemsType = new Array();

        for(let i=0;i<items.length;i++){
            if(DestinyItems[items[i]]!=null){
                itemsPlain[i]=DestinyItems[items[i]].displayProperties.name;
                itemsType[i]=DestinyItems[items[i]].itemTypeDisplayName;
                //console.log(itemsPlain[i]);
                //console.log(itemsType[i]);
            }
        }
        ans = "Ecco cosa vende Xur: ";
        for (var i=0;i<itemsPlain.length;i++){
            ans = ans + itemsPlain[i] + " ("+ itemsType[i]+"). ";
        }
        return ans;

    }else {
        return "Mi spiace non riesco a trovare Xur. ";
    }
}

async function richiesta_activities(activityID,token){
    //this function returns the activity's infos selected by the user
    let endpoint = '';
    if(activityID==0){
        //cala la notte
        let Hash='2171429505';

        endpoint = '/Platform/Destiny2/Milestones/';
        const response = await httpGet(endpoint);
        let nightfalls = new Array();
        if(response.Response[Hash]!=null){
            for(let i=0;i<3;i++){
                nightfalls[i]=response.Response[Hash].activities[i].activityHash;
            }


            //LETTURA AGGIORNATA DAL WEB
            let jsonendpoint = '/en/reducedActivities.json';
            const Destinyactivities = await httpJsonGet(jsonendpoint);

            let nightfallsPlain = new Array();

            for(let i=0;i<3;i++){
                nightfallsPlain[i]=Destinyactivities[nightfalls[i]].displayProperties.name;
                console.log(nightfallsPlain[i]);
            }
            let ans = "Ecco i cala la notte che ho trovato: ";
            for (var i=0;i<3;i++){
                let end=nightfallsPlain[i].lengt;
                nightfallsPlain[i]=nightfallsPlain[i].substring(10,end);
                ans = ans + nightfallsPlain[i] + ", "; 
            }
            return ans;
        }
        else{
            return "Purtroppo non riesco ad aiutarti.";
        }
    }
    else if(activityID==1){
        //focolai
        let Hash = '463010297';
        endpoint = '/Platform/Destiny2/Milestones/';
        const response = await httpGet(endpoint);
        if(response.Response[Hash]!=null){
            var flashpoint = response.Response[Hash].availableQuests[0].questItemHash;
            console.log(flashpoint);
            var text = '';

            //LETTURA DAL WEB
            let jsonendpoint = 'https://destiny.plumbing/en/raw/DestinyInventoryItemDefinition.json';
            const DestinyFlashpoint = await httpJsonGet(jsonendpoint);

            if(DestinyFlashpoint[flashpoint]!=null){
                console.log(DestinyFlashpoint[flashpoint]);
                let flashpointPlain = DestinyFlashpoint[flashpoint].displayProperties.name;
                let location=flashpointPlain.substring(10,flashpointPlain.length);

                return "Questa settimana, il focolaio si trova su "+location+".";
            }else{
                return "Purtroppo non riesco ad aiutarti.";
            }

        }else{
            return "Purtroppo non riesco ad aiutarti.";
        }
    }else{
        return "Devo ancora sviluppare le altre attività.";
    }
}


function httpTokenGet(endpoint,token) {
  return new Promise(((resolve, reject) => {
    var options = {
        host: 'www.bungie.net',
        path: endpoint,
        method: 'GET',
        headers: {'X-API-KEY': 'xxxxxxxxxxxxxxxxxxxxxx', 
        'Authorization': 'Bearer '+ token},
    };

    const request = https.request(options, (response) => {
      response.setEncoding('utf8');
      let returnData = '';

      response.on('data', (chunk) => {
        returnData += chunk;
      });

      response.on('end', () => {
        resolve(JSON.parse(returnData));
      });

      response.on('error', (error) => {
        reject(error);
      });
    });
    request.end();
  }));
}

function httpGet(endpoint) {
  return new Promise(((resolve, reject) => {
    var options = {
        host: 'www.bungie.net',
        path: endpoint,
        method: 'GET',
        headers: {'X-API-KEY': 'xxxxxxxxxxxxxxxxxxxxxx'},
    };

    const request = https.request(options, (response) => {
      response.setEncoding('utf8');
      let returnData = '';

      response.on('data', (chunk) => {
        returnData += chunk;
      });

      response.on('end', () => {
        resolve(JSON.parse(returnData));
      });

      response.on('error', (error) => {
        reject(error);
      });
    });
    request.end();
  }));
}


function httpJsonGet(endpoint) {
  return new Promise(((resolve, reject) => {
    var options = {
        host: 'destiny.plumbing',
        path: endpoint,
        method: 'GET',
    };

    const request = https.request(options, (response) => {
      response.setEncoding('utf8');
      let returnData = '';

      response.on('data', (chunk) => {
        returnData += chunk;
      });

      response.on('end', () => {
        resolve(JSON.parse(returnData));
      });

      response.on('error', (error) => {
        reject(error);
      });
    });
    request.end();
  }));
}

此问题可能在函数async function richiesta_activities(activityID,token)中出现

//LETTURA DAL WEB
            let jsonendpoint = 'https://destiny.plumbing/en/raw/DestinyInventoryItemDefinition.json';
            const DestinyFlashpoint = await httpJsonGet(jsonendpoint);

当我打电话给await httpJsonGet(jsonendpoint);

事实上,在测试lambda控制台时,会记录以下内容:

Response:
{
  "errorMessage": "2019-03-27T17:21:48.437Z 855210e7-4c89-474b-af57-0bdd3e9d89d5 Task timed out after 7.01 seconds"
}

Request ID:
"855210e7-4c89-474b-af57-0bdd3e9d89d5"

Function Logs:
START RequestId: 855210e7-4c89-474b-af57-0bdd3e9d89d5 Version: $LATEST
2019-03-27T17:21:41.520Z    855210e7-4c89-474b-af57-0bdd3e9d89d5    Warning: Application ID is not set
END RequestId: 855210e7-4c89-474b-af57-0bdd3e9d89d5
REPORT RequestId: 855210e7-4c89-474b-af57-0bdd3e9d89d5  Duration: 7007.93 ms    Billed Duration: 7000 ms    Memory Size: 128 MB Max Memory Used: 128 MB 
2019-03-27T17:21:48.437Z 855210e7-4c89-474b-af57-0bdd3e9d89d5 Task timed out after 7.01 seconds

但是Alexa说这项技术存在问题。 如何防止这种情况并独自处理以不返回错误?

编辑:感谢@Deiv超时处理程序的工作。

我仍然想找到一种访问lambda REPORT的方法,因为我注意到即使执行时间少于7000ms,我也可能会内存不足,并且很好地处理这种情况。

1 个答案:

答案 0 :(得分:0)

您可以在函数本身超时之前设置内部超时和带有成功消息的回调。看起来可能像这样:

const self = this;
setTimeout(function(){
  //after 6 seconds, exit function
  self.emit(':tell', "The connection is too slow, nothing was found.");
  process.exit();
}, 6000);
//rest of your code

由于setTimeout()异步运行,因此不会阻塞其余代码,但是它将在6秒钟后返回一条成功消息,从而避免了超时错误。

由OP编辑: 我添加了const self = this;,因为在函数范围内这毫无意义。 感谢您@Deiv,现在可以使用了。

我仍然想找到一种访问lambda REPORT的方法,因为我注意到即使执行时间少于7000ms,我也可能会内存不足,并且很好地处理这种情况。