Retrieving data from json and a url - javascript

I have a question, what can I do to get specific data back and send to my channel when I type !test?
Normally, when I issue a request to this URL I get the following response:
http://192.168.1.12/JSON?request=getstatus&ref=4030
{"Name":"HomeSeer Devices","Version":"1.0","Devices":[{"ref":4030,"name":"ttt","location":"ttt","location2":"ttt","value":0,"status":"Off","device_type_string":"AC Input Device Unknown Sensor","last_change":"\/Date(1548247933316)\/","relationship":0,"hide_from_view":false,"associated_devices":[],"device_type":{"Device_API":4,"Device_API_Description":"Plug-In API","Device_Type":73,"Device_Type_Description":"Plug-In Type 73","Device_SubType":97,"Device_SubType_Description":"AC[16B5BB2-10]a\u0002y\u0002\u00020\u00020\u00020\u00020\u00020\u00020\u0002n\u00021\u00020"},"device_image":"","UserNote":"","UserAccess":"Any","status_image":"/images/HomeSeer/status/off.gif","voice_command":"tttt","misc":4864}]}
I want the bot to reply with that status every time I execute the !test command.
How can I do that?
Next question: how can I set it to send the request with the value parameter?
http://192.168.1.12/JSON?request=controldevicebyvalue&ref=4030&value=0
I want that if I type !Device 0 it sets value to 0 by issuing that request.
This is how I'm handling commands:
client.on('message', message => {
// If the message is "ping"
if (message.content === '!ping') {
// Send "pong" to the same channel
message.channel.send('pong');
}
});

You can use the request package from npm. You can use the command below to install it:
To use it you first need to require it, then simply put the URL you want to request to: the result will be passed to the callback:
const request = require('request');
request('http://www.google.com', function (error, response, body) {
console.log('error:', error); // Print the error if one occurred
console.log('statusCode:', response && response.statusCode); // Print the response status code if a response was received
console.log('body:', body); // Print the HTML for the Google homepage.
});
In your case, I would do it like this:
client.on('message', message => {
// Detect the command
if (message.content.startsWith('!status')) {
// Issue the request
request('http://192.168.1.12/JSON?request=getstatus&ref=4030', (error, response, body) => {
// If there has been an error, log it
if (error) console.error(error);
// Otherwise, you can reply with the JSON you got back
else message.channel.send("```json\n" + body + "\n```");
});
}
});
If you want to turn that body string into an object, you'll need to JSON.parse() it.
request('http://192.168.1.12/JSON?request=getstatus&ref=4030', (error, response, body) => {
let object = JSON.parse(body);
// Once you have the object you can get all of its properties like you'd normally do
});
Your second problem can be solved in the same way: you just need to set thonURL depending of the argument.
If you still haven't, you'll need to create an argument parser: there are a lot of ways to do that, I'll just show you the easiest one for this example:
client.on('message', message => {
let args = message.content.split(' '), // Get the arguments
command = args.shift(); // Let the first be the command
// If there's no first argument, reply with this message
if (!args[0]) return message.reply("Please enter a value.");
if (command == '!device') {
request('http://192.168.1.12/JSON?request=controldevicebyvalue&ref=4030&value=' + args[0], (error, response, body) => {
// If there has been an error, log it
if (error) console.error(error);
// Otherwise, you can reply with the JSON you got back
else message.channel.send("```json\n" + body + "\n```");
});
}
});
If you need the object from body for some reason, you can parse it as shown above.

Try installing and importing the opn module:
Command Line: $ npm install opn
Then install it into your code: const opn = require('opn')
Then something along the lines of
if (message.content == "!Device 0") {
opn('http://192.168.1.12/JSON?request=controldevicebyvalue&ref=4030&value=0');
}

Related

is it possible to block form submit with only nodejs?

The HTML file where form is in doesn't have any client side javascript code. That I cannot change directly because my task is nodejs and express.
app.post('/',function(request,response){
const htmlCode = fs.readFileSync(__dirname + '/loggain.html');
const loggaInDom = new jsDOM.JSDOM(htmlCode);
const input = request.body.nickname;
try{
if(input.length<3){
throw new Error("nickname must be at least 3 characters");
}
else{
response.cookie('nickName',input);
response.redirect('index.html');
console.log(request.cookies.nickName);
}
}
catch(error){
console.log(error);
}
});
This is part of my nodejs code.
I would like to block form submit when input.length is smaller than 3. Like event.preventDefault() in client javascript code.
Now it throws error in console, which is correct, but browser keeps loading page permanently.
I cannot directly change HTML file but probably can insert client javascript file to HTML with nodejs, but I would like to know if it is possible to do with only nodeJS
Instead of throwing the error you need to send a response to the client with an error status:
if(input.length<3){
response.status(400).send("nickname must be at least 3 characters");
}
Choose the appropriate error code depending on your error: https://en.wikipedia.org/wiki/List_of_HTTP_status_codes
You can also send json instead of text if you want some structure that your frontend can use:
if(input.length<3){
response.status(400).json({
type: "validation error",
message: "nickname must be at least 3 characters"
});
}
Centralized error handling
Alternatively you may not want standardize error handling and do the response.status() thing at one location. Express has a way to catch errors but you cannot use the throw keyword. Instead you pass your error to the next function:
app.post('/',function(request, response, next){ // NOTE: next
const htmlCode = fs.readFileSync(__dirname + '/loggain.html');
const loggaInDom = new jsDOM.JSDOM(htmlCode);
const input = request.body.nickname;
try{
if(input.length<3){
throw new Error("nickname must be at least 3 characters");
}
else{
response.cookie('nickName',input);
response.redirect('index.html');
console.log(request.cookies.nickName);
}
}
catch(error){
next(error); // This is how errors are handled in Express
}
});
Now all you need to do is write a default error handler which is a special middleware that accepts four arguments instead of three or two. Make sure this middleware is loaded last after all your routes:
app.use((error, request, response, next) => {
response.status(500).send(error.message);
});
When used with error types you can send different responses to the browser depending on types of error:
class ValidationError extends Error {
constructor(message) {
super(message);
this.name = "ValidationError"; // (2)
}
}
Then you can throw:
next(new ValidationError("nickname must be at least 3 characters"))
Which you can handle with:
app.use((error, request, response, next) => {
switch(error.name) {
case "ValidationError":
response.status(400);
break;
default:
response.status(500);
}
response.send(error.message);
});

My discord.js bot seems to be running multiple instances at the same time

So what I'm trying to do is a simple bot that can detect commands. I made a '!test' command that does a few things (replies to the message, deletes it and then deletes the answer later, but also logs the command in a channel).
It seems to work fairly well, however it spams the answer and executes the series of actions multiple times : http://prntscr.com/nkgj8m (more and more every time when I restart the bot).
I tried to delete the app and recreate it, and it worked well : the message showed up only once, until I restarted the bot.
I made a '!stop' command that destroys the client, but it doesn't work as intended : the bot is disconnected (says "stop" in my console), but almost instantly reconnects on my server (and I don't see the logs in my local console anymore).
It seems to be a little "random" though about the count of messages. Some bot messages are also sometimes not deleted at all, and are not logged as well.
Here's my code (I never really did anything in js before so I might misuse some things, or some things might be not optimal, sorry about that - I did some research and most things I think are good, or decent at least).
// Require libs
require('dotenv').config()
const Discord = require('discord.js');
// Get discord client
var client = new Discord.Client();
initialize();
// --------------------------------------------------
// INITIALIZE
// --------------------------------------------------
function initialize() {
// On ready
client.on("ready", function() {
console.log(`Logged in as ${client.user.tag}! Yup, this is the default message.`);
});
// On message
client.on("message", function(input) {
// server message
if (input.guild.available) {
// get the message content
var command = input.content.toLowerCase();
// stop command
if (command.startsWith("!stop")) {
client.destroy();
console.log("Stopped");
}
// test command
else if (command.startsWith("!test")) {
input.reply(`This is my answer to your test !`)
.then(function(output) {
consumeCommand(input, output, 5000);
})
.catch(console.error);
}
}
});
// login bot client
client.login(process.env.BOT_TOKEN);
}
// --------------------------------------------------
// CONSULE AND LOG COMMANDS
// --------------------------------------------------
// Log the output of a command, delete the input message and delete the output soon
// input, message, the user message
// output, string, is the bot output
// outputTimeout, int, is the time we should wait until deleting the bot's output
function consumeCommand(input, output, outputTimeout) {
// delete input
input.delete(0)
.then(function() {
console.log(`Deleted message ${input.content}`)
})
.catch(console.error);
// log
var logChannel = input.guild.channels.find(channel => channel.name === 'guiguibot-commands');
if (logChannel != null) {
logCommand(input, output, logChannel);
} else {
console.log("Trying to log bot command but there's no guiguibot-commands channel");
}
// delete output later if not null
if (output != null && outputTimeout != null) {
}
}
// Log the output of a command
// input, message, the user message
// msg, message, the user message
// output, string, is the bot output
function logCommand(input, output, logChannel) {
// has output
if (output != null) {
logChannel.send(`#${input.author.username} sent a command`, {
embed: {
fields: [
{
name: ":keyboard: Input :",
value: `\`${input.content}\``
},
{
name: ":robot: Output :",
value: `\`${output.content}\``
}
]
}
})
.then(() => console.log('Logged user action'))
.catch(console.error);
}
// no ouput
else {
logChannel.send(`#${input.author.id} sent a command (no output was found)`, {
embed: {
fields: [
{
name: ":keyboard: Input :",
value: `\`${input.content}\``
}
]
}
})
.then(function() {
console.log('Logged user action')
})
.catch(console.error);
}
}
So, my question would be : how do I make sure that there's only one instance of my code running ? (if I deducted the problem correctly). Any help is appreciated. Thanks!
You dont need to make an initialize() method, just do it like this:
// Require libs
require('dotenv').config()
const Discord = require('discord.js');
// Get discord client
var client = new Discord.Client();
// On ready
client.on("ready", function() {
console.log('Logged in as ${client.user.tag}! Yup, this is the default message.');
});
// On message
client.on("message", function(input) {
// server message
if (input.guild.available) {
// get the message content
var command = input.content.toLowerCase();
// stop command
if (command.startsWith("!stop")) {
client.destroy();
console.log("Stopped");
}
// test command
else if (command.startsWith("!test")) {
input.reply('This is my answer to your test !')
.then(function(output) {
consumeCommand(input, output, 5000);
})
.catch(console.error);
}
}
});
// --- CONSOLE AND LOG COMMANDs go here ---
// login bot client
client.login(process.env.BOT_TOKEN);
Fixed it by simply hosting it on Heroku like #slothiful suggested ! I guess my script was just restarted again and again and the connections to the discord server were multiplied ? Idk exactly. Fact is now it works fine, only one instance is processing my commands.

Node.js : how to handle callback with async.js and websocket?

I'm creating a REST API to store setting from a specific camera sending data through TCP.
Camera communicate through TCP with a request/response pattern.
e.g : You can send "Get rate\n" to the camera and it responds "100 fps", in order to get the current framerate of the camera.
How to get data and store it in API?
var command = "rate"; //Array with setting I want to store in my API
function getDataFromCamera(command) { // This function will be iterate in another function
async.series([
console.log('Test1'); // console log to test output
function(callback) {
CAMERA_TCP_SOCKET.send("get "+command+"\n");
callback(null,command);
},
function(callback) {
console.log('Test2'); // console log to test output
CAMERA_TCP_SOCKET.onmessage = function(event) {
callback(null, event.data); // THIS line is the problem. I can't retrieve event.data because i don't know how to callback this variable
}
}],
function(err, results) {
if (err) {
//Handle the error in some way. Here we simply throw it
throw err;
}
if (results) {
console.log(results); // expected output : // ['rate','100']
}
});
}
At the moment, I get this error:
Test1
Test2
/Users/maximecongi/Desktop/Hologram/node_modules/async/dist/async.js:966
if (fn === null) throw new Error("Callback was already called.");
^
Error: Callback was already called.
at /Users/maximecongi/Desktop/Hologram/node_modules/async/dist/async.js:966:32
at /Users/maximecongi/Desktop/Hologram/node_modules/async/dist/async.js:3885:13
at W3CWebSocket.CAMERA_TCP_SOCKET.onmessage (/Users/maximecongi/Desktop/Hologram/core/api.js:91:13)
at W3CWebSocket._dispatchEvent [as dispatchEvent] (/Users/maximecongi/Desktop/Hologram/node_modules/yaeti/lib/EventTarget.js:107:17)
at W3CWebSocket.onMessage (/Users/maximecongi/Desktop/Hologram/node_modules/websocket/lib/W3CWebSocket.js:234:14)
at WebSocketConnection.<anonymous> (/Users/maximecongi/Desktop/Hologram/node_modules/websocket/lib/W3CWebSocket.js:205:19)
at WebSocketConnection.emit (events.js:188:13)
at WebSocketConnection.processFrame (/Users/maximecongi/Desktop/Hologram/node_modules/websocket/lib/WebSocketConnection.js:554:26)
at /Users/maximecongi/Desktop/Hologram/node_modules/websocket/lib/WebSocketConnection.js:323:40
at process.internalTickCallback (internal/process/next_tick.js:70:11)
[nodemon] app crashed - waiting for file changes before starting...
How to solve my problem?
It's because you are listening on every message, and looks like you're sending 3 commands, and I guess, every command get a response. This callback after first serie is not destroyed, its alive. For me, you should check if the response is for this specific command, then execute the problematic callback.

Node HTTP request hangs forever

We've got a Node.js script that is run once a minute to check the status of our apps. Usually, it works just fine. If the service is up, it exits with 0. If it's down, it exits with 1. All is well.
But every once in a while, it just kinda stops. The console reports "Calling status API..." and stops there indefinitely. It doesn't even timeout at Node's built-in two-minute timeout. No errors, nothing. It just sits there, waiting, forever. This is a problem, because it blocks following status check jobs from running.
At this point, my whole team has looked at it and none of us can figure out what circumstance could make it hang. We've built in a start-to-finish timeout, so that we can move on to the next job, but that essentially skips a status check and creates blind spots. So, I open the question to you fine folks.
Here's the script (with names/urls removed):
#!/usr/bin/env node
// SETTINGS: -------------------------------------------------------------------------------------------------
/** URL to contact for status information. */
const STATUS_API = process.env.STATUS_API;
/** Number of attempts to make before reporting as a failure. */
const ATTEMPT_LIMIT = 3;
/** Amount of time to wait before starting another attempt, in milliseconds. */
const ATTEMPT_DELAY = 5000;
// RUNTIME: --------------------------------------------------------------------------------------------------
const URL = require('url');
const https = require('https');
// Make the first attempt.
make_attempt(1, STATUS_API);
// FUNCTIONS: ------------------------------------------------------------------------------------------------
function make_attempt(attempt_number, url) {
console.log('\n\nCONNECTION ATTEMPT:', attempt_number);
check_status(url, function (success) {
console.log('\nAttempt', success ? 'PASSED' : 'FAILED');
// If this attempt succeeded, report success.
if (success) {
console.log('\nSTATUS CHECK PASSED after', attempt_number, 'attempt(s).');
process.exit(0);
}
// Otherwise, if we have additional attempts, try again.
else if (attempt_number < ATTEMPT_LIMIT) {
setTimeout(make_attempt.bind(null, attempt_number + 1, url), ATTEMPT_DELAY);
}
// Otherwise, we're out of attempts. Report failure.
else {
console.log("\nSTATUS CHECK FAILED");
process.exit(1);
}
})
}
function check_status(url, callback) {
var handle_error = function (error) {
console.log("\tFailed.\n");
console.log('\t' + error.toString().replace(/\n\r?/g, '\n\t'));
callback(false);
};
console.log("\tCalling status API...");
try {
var options = URL.parse(url);
options.timeout = 20000;
https.get(options, function (response) {
var body = '';
response.setEncoding('utf8');
response.on('data', function (data) {body += data;});
response.on('end', function () {
console.log("\tConnected.\n");
try {
var parsed = JSON.parse(body);
if ((!parsed.started || !parsed.uptime)) {
console.log('\tReceived unexpected JSON response:');
console.log('\t\t' + JSON.stringify(parsed, null, 1).replace(/\n\r?/g, '\n\t\t'));
callback(false);
}
else {
console.log('\tReceived status details from API:');
console.log('\t\tServer started:', parsed.started);
console.log('\t\tServer uptime:', parsed.uptime);
callback(true);
}
}
catch (error) {
console.log('\tReceived unexpected non-JSON response:');
console.log('\t\t' + body.trim().replace(/\n\r?/g, '\n\t\t'));
callback(false);
}
});
}).on('error', handle_error);
}
catch (error) {
handle_error(error);
}
}
If any of you can see any places where this could possibly hang without output or timeout, that'd be very helpful!
Thank you,
James Tanner
EDIT: p.s. We use https directly, instead of request so that we don't need to do any installation when the script runs. This is because the script can run on any build machine assigned to Jenkins without a custom installation.
Aren't you missing the .end()?
http.request(options, callback).end()
Something like explained here.
Inside your response callback your not checking the status..
The .on('error', handle_error); is for errors that occur connecting to the server, status code errors are those that the server responds with after a successful connection.
Normally a 200 status response is what you would expect from a successful request..
So a small mod to your http.get to handle this should do..
eg.
https.get(options, function (response) {
if (response.statusCode != 200) {
console.log('\tHTTP statusCode not 200:');
callback(false);
return; //no point going any further
}
....

zeromq push-pull guaranteed delivery (re-transmit on error)

How can I fail a PULL to get retransmit from the pusher?
In the following example, if I uncomment the // throw ... to trigger an exception on every received message, all the PULL messages are failing, but when I put back the comment, all those messages are lost and not retransmitted by the pusher.
How can I make sure the pusher will retransmit the messages if the PULL method fail?
P.S. The following code is JavaScript in Meteor framework on top of nodejs, but it's not relevant to the problem as I suppose it's similar in every zmq implementations.
var zmq = Npm.require(modulePath + '/zmq');
var sock = zmq.socket('pull');
sock.identity = 'receiving' + process.pid;
sock.bind('tcp://172.17.4.25:5551', function(err) {
if (err) throw err
if (debug) console.log('Listening for ZMQ messages')
});
sock.on('message', Meteor.bindEnvironment(function(data) {
// throw { name: 'MessagePullFailed', message: 'Failing Pull message to check if ZMQ can recover from it' }
var jsondata = JSON.parse(data)
if (jsondata.cmd == 'upsert') {
jsondata['set']['utctime'] = new Date(jsondata['set']['utctime'])
Mon.upsert({_id: jsondata._id}, {'$set': jsondata['set']}, function(err) {
if (err) console.log(err)
})
}
}))

Categories