I'm trying to get my bot to "dynamically" get the name of a command.
title is defined in module.exports as the name of the command that will show up in the Embed; however in 1 specific command where I get a joke from an API, this.title returns as Undefined instead of the command's name.
It only happens in this command.
I've tried using just title however that just returns "node".
What did I do wrong?
Code:
const Discord = require("discord.js");
const { title } = require("process");
const config = require("../data.json");
module.exports = {
name: "dadjoke",
title: "Dad Joke",
description: "A (not so) very funny dadjoke.",
execute(client, message, args) {
const https = require('https');
const url = "icanhazdadjoke.com";
var options = {
host: url,
port: 443,
path: "/",
headers: {"Accept":"application/json", "User-Agent":"crazyBot (https://github.com/acrazytown/crazybot)"}
}
function getData(options) {
https.get(options, (resp) => {
let data = '';
resp.on('data', (chunk) => {
data += chunk;
});
resp.on('end', () => {
const embed = new Discord.MessageEmbed()
.setColor(config.crazyBot.settings.accent_color)
.setAuthor("crazyBot", config.crazyBot.settings.icon_url)
.setTitle(this.title)
.setDescription(JSON.parse(data).joke)
.setFooter(`Powered by ${url}`);
message.channel.send(embed);
});
}).on("error", (err) => {
console.log("Error: " + err.message);
});
}
getData(options)
}
}
Because the function getData() doesn't have an object reference, the value of this within the function is undefined whereas the value of this within execute() is defined as the object being exported. One simple solution to this is to use Function.bind() to set the this keyword of getData() to the object reference.
This is how that would look in your execute() function:
execute(client, message, args) {
const https = require('https');
const url = "icanhazdadjoke.com";
var options = {
host: url,
port: 443,
path: "/",
headers: {"Accept":"application/json", "User-Agent":"crazyBot (https://github.com/acrazytown/crazybot)"}
}
var getData = function(options) {
https.get(options, (resp) => {
let data = '';
resp.on('data', (chunk) => {
data += chunk;
});
resp.on('end', () => {
const embed = new Discord.MessageEmbed()
.setColor(config.crazyBot.settings.accent_color)
.setAuthor("crazyBot", config.crazyBot.settings.icon_url)
.setTitle(this.title)
.setDescription(JSON.parse(data).joke)
.setFooter(`Powered by ${url}`);
message.channel.send(embed);
});
}).on("error", (err) => {
console.log("Error: " + err.message);
});
}.bind(this);
getData(options)
}
Relevant resources:
https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_objects/Function/bind
Related
I have the following error message in the server console.
property queryResult of 'undefined' or 'null'. TypeError: Cannot
My code is below
invoke:async (conversation, done) => {
// Get query from incoming message
const text = conversation.text();
var query = conversation.properties().query;
conversation.logger().info('Query '+query );
// Set modules
const soapRequest = require('easy-soap-request');
const path = require('path');
const fs = require('fs');
const xml2js = require('xml2js');
const { match } = require('assert');
//SOAP URL
const url = 'https://cap.zudo.com/ws/apf/ticketing/MOS?WSDL';
//Set headers
const sampleHeaders = {
'user-agent': 'sampleTest',
'Content-Type': 'application/xml;charset=UTF-8',
};
const filenameIn = path.join(__dirname, '/request.txt');
const filenameOut = filenameIn
//Replace Query variable inside request.txt fileContent
var REGEX = /<TKT:ProjectNum>(.+)<\/TKT:ProjectNum>/;
var fileContent = fs.readFileSync(filenameIn, 'utf8');
fileContent = fileContent.replace(
REGEX,
'<TKT:ProjectNum>' + query + '</TKT:ProjectNum>'
);
//Write the query
fs.writeFileSync(filenameOut, fileContent, 'utf8');
const xml = fs.readFileSync(path.join(__dirname, '/request.txt'), 'utf-8');
//Callback
let { queryResult } = await soapRequest({ url: url, headers: sampleHeaders, xml: xml, timeout: 10000}).then(results => {
return results;
}).catch(e => {
conversation.logger().info("ERROR "+e);
conversation.reply(e).transition('NOK').keepTurn(true);
done();
});
let { headers, body, statusCode } = await queryResult ;
xml2js.parseString(body,{ mergeAttrs: true }, (err, result) => {
if(err) {
conversation.logger().info("ERROR "+err);
conversation.reply(e).transition('NOK').keepTurn(true);
done();
}
conversation.logger().info("res: "+result);
conversation.reply(result).transition('OK').keepTurn(true);
done();
})
}
Can any one help me how to resolve the issue.
Before destructure an object, please carefully check if it's undefined or null.
const res = await soapRequest({ url: url, headers: sampleHeaders, xml: xml, timeout: 10000 }).then(results => {
return results;
}).catch(e => {
conversation.logger().info("ERROR " + e);
conversation.reply(e).transition('NOK').keepTurn(true);
done();
});
if (res === undefined || res === null) {
// do something when res is undefined or null
} else {
const { queryResult } = res; // safely destructure res now
// further processing...
}
You have one more error in your code:
let { headers, body, statusCode } = await queryResult;
queryResult is not a function, you don't need to use await with it.
I am trying to connect alexa with my website. I created an Alexa skill (following the guide) that works in connecting with a website like "numbersapi" it asks the user for a number and reads the answer given by the site. This is the code:
function httpGet(query) {
return new Promise((resolve, reject) => {
var options = {
host: 'numbersapi.com',
path: '/' + encodeURIComponent(query),
method: 'GET',
};
var req = http.request(options, res => {
res.setEncoding('utf8');
var responseString = "";
//accept incoming data asynchronously
res.on('data', (chunk) => {
responseString = responseString + chunk;
});
//return the data when streaming is complete
res.on('end', () => {
resolve(responseString);
});
res.on('error',(error) => {
reject(error);
});
});
req.end();
});
}
And the Intent:
const questionIntentHandler = {
canHandle(handlerInput) {
return handlerInput.requestEnvelope.request.type === 'IntentRequest'
&& handlerInput.requestEnvelope.request.intent.name === 'questionIntent';
},
handle(handlerInput) {
const theNumber = handlerInput.requestEnvelope.request.intent.slots.info.value;
var query = parseInt(theNumber);
return new Promise((resolve,reject) => {
httpGet(query).then((response) => {
resolve(handlerInput.responseBuilder
.speak(response)
.withShouldEndSession(false)
.getResponse());
}).catch ((error) => {
resolve(handlerInput.responseBuilder.speak('Request error').getResponse());
});
});
},
};
It works perfectly.Now I'm trying to use it for my very simple website but I can't get it to work. I created a simple form in a jsp that sends a number and opens another jsp page where a string is displayed based on the number. Changing the number to display in the url works, but when I try to apply the number through alexa it doesn't work. I think I have problems with the options.
var options = {
host: 'localhost',
path: '/SearchFact?info=' + encodeURIComponent(query),
method: 'GET',
};
Can someone help me?
I have to functions called: getMatchDataApi() and saveApiDataToDb(). getMatchDataApi() function returns value from an api and saveApiDataToDb() function is used to store getMatchDataApi() value into firestore database.
function getMatchDataApi() {
var options = {
method: "GET",
hostname: "dev132-cricket-live-scores-v1.p.rapidapi.com",
port: null,
path: "/scorecards.php?seriesid=2141&matchid=43431",
headers: {
"x-rapidapi-host": "dev132-cricket-live-scores-v1.p.rapidapi.com",
"x-rapidapi-key": "63e55e4f7fmsh8711fb1c0bd9ec2p1d8b4bjsne2b8db0a1a82"
},
json: true
};
var req = http.request(options, res => {
var chunks = [];
res.on("data", chunk => {
chunks.push(chunk);
});
res.on("end", () => {
var body = Buffer.concat(chunks);
var json = JSON.parse(body);
playerName = json.fullScorecardAwards.manOfTheMatchName;
console.log("player name", playerName);
});
});
req.end();
}
async function saveApiDataToDb() {
await getMatchDataApi();
var name = playerName;
console.log("Aman Singh", name);
}
Here i am using async function. So that first i want it should execute this getMatchDataApi() first and returns the value and after that it should print value inside this function saveApiDataToDb().
And then i am calling saveApiDataToDb() as follow:
exports.storeMatchData = functions.https.onRequest((request, response) => {
saveApiDataToDb()
});
Yes, you can use async/await in cloud functions. But, you can't access/fetch the data outside the google servers in the Spark Plan (Free Plan).
Hope this helps.
Modify your functions/index.js file like this way:
const functions = require('firebase-functions');
const request = require('request');
exports.storeMatchData = functions.https.onRequest( async (req, res) => {
let body = '';
await getMatchDataApi().then(data => body = data).catch(err => res.status(400).end(err));
if (!body) {
return res.status(404).end('Unable to fetch the app data :/');
}
// let json = JSON.parse(body);
// playerName = json.fullScorecardAwards.manOfTheMatchName;
// console.log("Aman Singh", playerName);
res.send(body);
});
function getMatchDataApi() {
const options = {
url: 'https://dev132-cricket-live-scores-v1.p.rapidapi.com/scorecards.php?seriesid=2141&matchid=43431',
headers: {
"x-rapidapi-host": "dev132-cricket-live-scores-v1.p.rapidapi.com",
"x-rapidapi-key": "63e55e4f7fmsh8711fb1c0bd9ec2p1d8b4bjsne2b8db0a1a82"
},
};
return cURL(options);
}
function cURL(obj, output = 'body') {
return new Promise((resolve, reject) => {
request(obj, (error, response, body) => {
if (error)
reject(error);
else if (response.statusCode != 200)
reject(`cURL Error: ${response.statusCode} ${response.statusMessage}`);
else if (response.headers['content-type'].match(/json/i) && output == 'body')
resolve(JSON.parse(body));
else if (output == 'body')
resolve(body);
else
resolve(response);
});
});
}
I try to solve my issue using promise in cloud functions. so it could help someone.
This is my cloud function
exports.storeMatchData = functions.https.onRequest((request, response) => {
a().then(
result => {
saveApiDataToDb(result);
},
error => {}
);
});
This is the function from which i am calling api and resolving its data first what i want
var options = {
method: "GET",
hostname: "dev132-cricket-live-scores-v1.p.rapidapi.com",
port: null,
path: "/scorecards.php?seriesid=2141&matchid=43431",
headers: {
"x-rapidapi-host": "dev132-cricket-live-scores-v1.p.rapidapi.com",
"x-rapidapi-key": "63e55e4f7fmsh8711fb1c0bd9ec2p1d8b4bjsne2b8db0a1a82"
},
json: true
};
var options1 = {
method: "GET",
hostname: "dev132-cricket-live-scores-v1.p.rapidapi.com",
port: null,
path: "/matches.php?completedlimit=5&inprogresslimit=5&upcomingLimit=5",
headers: {
"x-rapidapi-host": "dev132-cricket-live-scores-v1.p.rapidapi.com",
"x-rapidapi-key": "63e55e4f7fmsh8711fb1c0bd9ec2p1d8b4bjsne2b8db0a1a82"
}
};
var a = function getMatchDataApi() {
// Return new promise
return new Promise((resolve, reject) => {
// Do async job
let firstTask = new Promise((resolve, reject) => {
var req = http.request(options, res => {
var chunks = [];
var arr = [];
res.on("data", chunk => {
chunks.push(chunk);
});
res.on("end", () => {
var body = Buffer.concat(chunks);
var json = JSON.parse(body);
const playerName = json.fullScorecardAwards.manOfTheMatchName;
resolve(playerName);
});
});
req.end();
});
let secondTask = new Promise((resolve, reject) => {
var req = http.request(options1, res => {
var chunks = [];
var arr = [];
res.on("data", chunk => {
chunks.push(chunk);
});
res.on("end", () => {
var body = Buffer.concat(chunks);
var json = JSON.parse(body);
const playerName = json;
resolve(playerName);
});
});
req.end();
});
Promise.all([firstTask, secondTask]).then(
result => {
resolve(result);
},
error => {
reject(error);
}
);
});
};
This is the function in which I am going to use getMatchDataApi() values after resolving in this function.
function saveApiDataToDb(data) {
console.log("Name of player", data[0]);
}
I'm attempting a simple request:
var options = {
host: 'hookb.in',
path: '/8PMoEa9kbaCXgXYxOmdr5',
method: 'POST'
};
var req = http.request(options, (res) => {
var body = context.bindingData.name;
res.on("data", (chunk) => {
body += chunk;
});
res.on("end", () => {
context.res = body;
});
}).on("error", (error1) => {
context.log('error');
context.res = {
status: 500,
body: error1
};
});
req.end();
context.done();
However, there's no response (and no request received by the target here https://hookbin.com/8PMoEa9kbaCXgXYxOmdr).
What am I doing wrong? Is there a special way to create an https request inside of an azure function?
var Jimp = require("jimp");
var http = require('https');
module.exports = async function (context, myBlob) {
context.log("JavaScript blob trigger function processed blob \n Name:", context.bindingData.name, "\n Blob Size:", myBlob.length, "Bytes");
context.log(process.env.ImageConvertedWebHook);
Jimp.read(myBlob, function (err, image) {
image.getBufferAsync(Jimp.MIME_TIFF, function (error, imageData) {
context.log('Node.JS blob trigger function resized ' + context.bindingData.name + ' to ' + image.bitmap.width + 'x' + image.bitmap.height);
context.bindings.outputBlob = imageData;
var options = {
host: 'hookb.in',
path: '/8PMoEa9kbaCXgXYxOmdr5',
method: 'POST'
};
var req = http.request(options, (res) => {
var body = context.bindingData.name;
res.on("data", (chunk) => {
body += chunk;
});
res.on("end", () => {
context.res = body;
});
}).on("error", (error1) => {
context.log('error');
context.res = {
status: 500,
body: error1
};
});
req.end();
context.done();
});
});
};
I've also attempted this way:
const data = 'buy milk biotch';
var options = {
host: 'hookb.in',
path: '/8PMoEa9kbaCXgXYxOmdr',
method: 'POST',
port: 443,
headers: {
'Content-Type': 'application/json',
'Content-Length': data.length
}
};
const req = https.request(options, res => {
context.log(`statusCode: ${res.statusCode}`)
res.on('data', d => {
context.log(d)
})
})
req.on('error', error1 => {
context.log(error1)
})
req.write(data)
req.end()
This is a working example of how to request Azure AD v2 endpoint to get access token inside of your Azure Function V3 (node runtime)
var http = require('https');
module.exports = function (context, req) {
var body = "";
body += 'grant_type=' + req.query['grant_type'];
body += '&client_id=' + req.query['client_id'];
body += '&client_secret=' + req.query['client_secret'];
body += '&code=' + req.query['code'];
const options = {
hostname: 'login.microsoftonline.com',
port: 443,
path: '/ZZZZZZZZ-bc69-4c8b-8e91-11f3a181c2bb/oauth2/v2.0/token',
method: 'POST',
headers: {
'Content-Type': 'application/x-www-form-urlencoded',
'Content-Length': body.length
}
}
var response = '';
const request = http.request(options, (res) => {
context.log(`statusCode: ${res.statusCode}`)
res.on('data', (d) => {
response += d;
})
res.on('end', (d) => {
context.res = {
body: response
}
context.done();
})
})
request.on('error', (error) => {
context.log.error(error)
context.done();
})
request.write(body);
request.end();
};
Differences between this and OP's: function is not marked as async (module.exports = function) and I'm using context.done(); to let the runtime know when an async action (https request in our case) is done. context.done(); is in two places: 'end' and 'error' callbacks.
I believe async/await + promises should be used instead of callbacks if you want to use an async function - link
Using the async and await keywords helps avoid both of these errors.
You should use the Node.js utility function util.promisify to turn
error-first callback-style functions into awaitable functions.
Not a JS dev.
I had the same issue but after removing async from module.exports = async function (context, myBlob), this should be work. If you want to treat this as a async function, this might be helpful.
Seems like naming conflict issue .Couple of variables to change in your code:
Change below
var http = require('https');
To
var httpMod = require('https');
Change below
const req = https.request(options, res => {
context.log(`statusCode: ${res.statusCode}`)
res.on('data', d => {
context.log(d)
})
})
To
const customReq = httpMod.request(options, res => {
context.log(`statusCode: ${res.statusCode}`)
res.on('data', d => {
context.log(d)
})
})
Hope it helps.
I am currently working with azure functions in javascript. In my function, I am first getting a specific element from my CosmoDB (this is the async/await part). I get a result and then I want to do an https POST request. However, my problem is, that it never finished the HTTPs request and I don't really know why. What am I doing wrong?
(As you can see I tried 2 different ways of doing the request, once with the standard https function and the commented out the part with npm request package. However, both ways won't work).
Here is my code:
const CosmosClient = require('#azure/cosmos').CosmosClient;
var https = require('https');
var http = require('http');
var request = require('request');
const endpoint = "someEndpoint";
const masterKey = "anymasterkey";
const database = {
"id": "Database"
};
const container = {
"id": "Container1"
};
const databaseId = database.id;
const containerId = container.id;
const client = new CosmosClient({
endpoint: endpoint,
auth: {
masterKey: masterKey
}
});
module.exports = function (context, req) {
const country = "de";
const bban = 12345678;
const querySpec = {
query: "SELECT * FROM Container1 f WHERE f.country = #country AND f.bban = #bban",
parameters: [{
name: "#country",
value: country
},
{
name: "#bban",
value: bban
}
]
};
getContainers(querySpec).then((results) => {
const result = results[0];
context.log('here before request');
var options = {
host: 'example.com',
port: '80',
path: '/test',
method: 'POST'
};
// Set up the request
var req = http.request(options, (res) => {
var body = "";
context.log('request');
res.on("data", (chunk) => {
body += chunk;
});
res.on("end", () => {
context.res = body;
context.done();
});
}).on("error", (error) => {
context.log('error');
context.res = {
status: 500,
body: error
};
context.done();
});
req.end();
// request({
// baseUrl: 'someURL',
// port: 443,
// uri: 'someuri',
// method: 'POST',
// headers: {
// 'Content-Type': 'text/xml;charset=UTF-8',
// 'SOAPAction': 'someaction'
// },
// function (error, response, body) {
// context.log('inside request')
// if (error) {
// context.log('error', error);
// } else {
// context.log('response');
// }
// }
// })
})
};
async function getContainers(querySpec) {
const {container, database} = await init();
return new Promise(async (resolve, reject) => {
const {
result: results
} = await container.items.query(querySpec).toArray();
resolve(results);
})
}
async function init() {
const {
database
} = await client.databases.createIfNotExists({
id: databaseId
});
const {
container
} = await database.containers.createIfNotExists({
id: containerId
});
return {
database,
container
};
}
The last thing that happens is the print of "here before request". After that the function just does nothing until it timesout. So what am I doing wrong? Can't I just this combination of await/async and requests?
As commented you are not sending any data to the POST call. You need to have a req.write before the req.end
req.write(data);
req.end();
That is why the POST call is failing for you. After this fix, it should work