Execute conditional statement after loop finishes in nodejs - node.js

I have a for loop for checking multiple uploaded image's aspect ratio, after completing the loop i want to check ratio in if else condition to redirect user. Problem is the conditions are checked before loop finishes, I need the loop to be completed before checking conditions. I found out async whilst might be suitable here but i'm confused about the best approach for implemetation, can anyone give me workaround to perform the code sequentially.
//check image ratio
var validImageRatio = true;
for(i=0; i<req.files.propertyPhoto.length; i++){
var tempFile = req.files.propertyPhoto[i].tempFilePath.replace(/\\/g, '/');
var ratio;var width;var height;
var acceptedRatio = 3;
//get image ratio
sizeOf(tempFile, function (err, dimensions) {
width = dimensions.width;
height = dimensions.height;
ratio = width/height;
});
if (ratio < (acceptedRatio - 0.1) || ratio > (acceptedRatio + 0.1)) {
validImageRatio = false;
break;
}
}
//if ratio invalid, redirect
if (!validImageRatio) {
...
}
//if ratio valid, upload
else{
...
}

Since you're doing the check asynchronously, the synchronous code will run first. If you use async/await inside the for loop, it will block each iteration of the loop making it run slower. The approach you can go for is to use Promise.all to run the checks concurrently.
const promises = req.files.propertyPhoto.map(prop => new Promise(resolve => {
const tempFile = prop.tempFilePath.replace(/\\/g, '/');
const acceptedRatio = 3;
// get image ratio
sizeOf(tempFile, function (err, dimensions) {
const width = dimensions.width;
const height = dimensions.height;
const ratio = width / height;
if (ratio < (acceptedRatio - 0.1) || ratio > (acceptedRatio + 0.1)) {
return resolve(false);
}
resolve(true);
});
}));
const result = await Promise.all(promises);
if (result.some(r => r === false)) {
// if any of the ratio is invalid, redirect
} else {
// else upload
}

I'm guessing at what you mean, but a for-loop would complete before checking the conditions at the bottom except that you include a "break" statement. The break statement makes the for-loop stop executing and move on.

Related

Unexpected Identifier in node.js

I'm attempting to run a code with node.js and currently I am getting the error:
Unexpected identifier
The code file is as follows:
/*
This script will bruteforce a live match from the TPP API
in Defiance mode and estimate winrates.
Requirements:
-An oauth for the TPP API: https://twitchplayspokemon.tv/show_oauth_token
-Node.js: https://nodejs.org/en/download/
-sync-request for Node.js: `npm install -g sync-request` (NPM should come
installed with Node.js)
-Pokemon Showdown: https://github.com/Zarel/Pokemon-Showdown
You don't need to install its dependencies, the simulator itself does not
require them.
-My PBR mod for Pokemon Showdown: https://github.com/mathfreak231/random-
showdown-mods
enter code here`enter code here`This is required to accurately simulate PBR.
Note that it is still in development.
Setup steps:
Copy the pbr directory from random-showdown-mods into Pokemon-Showdown/mods.
Fill in the relative path to Pokemon-Showdown/sim below
Fill in your TPP API oauth token below
Change the number constants if you wish
Then just `node defiancechan.js`, sit back, and enjoy your simulation!
Note that the PBR mod is *not* entirely accurate. Known inaccuracies
include,
but are not limited to: draws are not handled properly, and Fury Cutter
still resets when it shouldn't.
*/
// Replace this with a relative path to Pokemon-Showdown/sim
const Sim = require('./../Pokemon-Showdown/sim');
// Replace this with your TPP oauth token
const TPP_OAUTH = "abc123xyz69420";
// Absolute maximum sample size.
const MAX_SAMPLE_SIZE = 1000;
// z* for your desired confidence level. This is used to calculate
// the margin of sampling error. Some common values:
// For 95% confidence: 1.959963986
// For 99% confidence: 2.575829303
// In practice this means that C% of your simulations will
// have the true proportion within the margin of error from
// the sampled proportion.
// If you don't know what any of this means, don't bother changing
// or paying attention to the "Margin of error" output at the end.
const Z_STAR = 2.575829303;
// Maximum time to run the simulation for, in milliseconds.
const MAX_TIME = 30000;
// If this is true, it prints some match logs (1 in DEBUG_EVERY) to stdout
while it's bashing them.
const DEBUG = false;
const DEBUG_EVERY = 50;
// Set this to require a .json file to bash a match there instead of a live
match.
const TEST_MATCH_PATH = null;
/////////////////////////////////////////
// DON'T EDIT ANYTHING BELOW THIS LINE //
/////////////////////////////////////////
const request = require('sync-request');
const PBR_FORMAT = {
id: 'tppbr',
name: 'TPPBR',
mod: 'pbr',
ruleset: ["PBR Sleep Clause", "PBR Freeze Clause", "PBR Self-Destruct
Clause", "PBR Destiny Bond Clause"],
banlist: [],
unbanlist: [],
debug: true,
}
/**
* because pokecat TriHard
*/
function convert_stats_table(stats) {
let newstats = {};
for (statname in stats) {
newstats[statname.toLowerCase()] = stats[statname];
}
return newstats;
}
function main() {
console.log("Getting current match...");
let match;
if (TEST_MATCH_PATH) {
match = require(TEST_MATCH_PATH);
} else {
const res = request('GET',
"https://twitchplayspokemon.tv/api/current_match", {
headers: {
'OAuth-Token': TPP_OAUTH
},
});
match = JSON.parse(res.getBody('utf8'));
}
console.log("Done.");
if ((match.base_gimmicks.includes('blind_bet') ||
match.base_gimmicks.includes('secrecy')) && !match.started) {
throw new Error("Can't analyze a match unless all the Pokemon have been
revealed.");
}
if (!match.base_gimmicks.includes('defiance')) {
console.log("WARNING: This is not a defiance match!");
}
const startTime = Date.now();
let wincounter = {
Blue: 0,
Red: 0,
draw: 0,
};
let teams = [[], []];
for (let i = 0; i < teams.length; i++) {
for (const pokemon of match.teams[i]) {
teams[i].push({
name: pokemon.ingamename,
species: pokemon.species.name, // TODO: handle forms
item: pokemon.item.name,
ability: pokemon.ability.name,
moves: pokemon.moves.map(move => move.name),
nature: pokemon.nature.name,
gender: pokemon.gender ? pokemon.gender.toUpperCase() : 'N',
evs: convert_stats_table(pokemon.evs),
ivs: convert_stats_table(pokemon.ivs),
level: pokemon.level,
shiny: pokemon.shiny,
happiness: pokemon.happiness,
});
}
}
const isRandomOrder = match.base_gimmicks.includes('random_order');
const isTraitor = match.base_gimmicks.includes('traitor');
const isFog = match.base_gimmicks.includes('fog');
let battle;
let i;
console.log("Begin simulation of matches...");
for (i = 1; i <= MAX_SAMPLE_SIZE; i++) {
battle = new Sim.Battle({
formatid: PBR_FORMAT,
});
// prevent battle from starting so we can edit stuff first
battle.started = true;
let newTeams = teams;
if (isRandomOrder || isTraitor) {
// TODO: In what order are these gimmicks applied?
newTeams = battle.deepClone(teams);
if (isRandomOrder) {
for (let team of newTeams) {
battle.shuffle(team);
}
}
if (isTraitor) {
// swap 2 pokes, same position
const p = battle.random(3);
const temp = newTeams[0][p];
newTeams[0][p] = newTeams[1][p];
newTeams[1][p] = temp;
}
}
battle.join('p1', 'Blue', 1, newTeams[0]);
battle.join('p2', 'Red', 2, newTeams[1]);
for (const side of battle.sides) {
for (const pokemon of side.pokemon) {
pokemon.originalPosition = pokemon.position;
for (const [m, move] of pokemon.baseMoveSlots.entries()) {
// change move PP
const moveData = battle.getMove(move.id);
const oldMoveData = match.teams[side.n]
[pokemon.position].moves[m];
move.pp = oldMoveData.pp;
move.maxpp = moveData.pp * (5 + oldMoveData.pp_ups) / 5;
}
pokemon.clearVolatile(); // actually update the moveslots TriHard
}
}
if (match.stage) {
battle.colosseum = match.stage;
} else if (isFog && battle.randomChance(1,2)) {
// TODO: confirm this is correct
battle.colosseum = 'courtyard';
} else {
battle.colosseum = battle.sample([
'gateway', 'mainstreet', 'waterfall', 'neon', 'crystal',
'sunnypark', 'magma', 'sunset', 'stargazer', 'lagoon',
]);
}
battle.started = false;
battle.start();
if (isFog) battle.setWeather('fog');
while (!battle.ended) {
if (battle.turn > 500) {
console.log('===BEGIN BAD MATCH LOG EXPORT===');
console.log(battle.log.join('\n'));
console.log('===END BAD MATCH LOG EXPORT===');
console.log('===BEGIN BAD MATCH INPUT LOG EXPORT===');
console.log(battle.inputLog.join('\n'));
console.log('===END BAD MATCH INPUT LOG EXPORT===');
throw new Error("The match fucked up somehow.");
}
for (const side of battle.sides) {
let result;
switch (side.currentRequest) {
case 'switch':
// TPPBR switching rules.
let target = null;
for (let i = side.active.length; i < side.pokemon.length; i++) {
const pokemon = side.pokemon[i];
if (!(pokemon.fainted) && (!target || pokemon.originalPosition
< target.originalPosition)) {
target = pokemon;
}
}
result = battle.choose(side.id, `switch ${target.position +
1}`);
if (!result) throw new Error(side.choice.error);
break;
case 'move':
// Same as TPPBR, choose random moves until one works
let tries = 0;
do {
result = battle.choose(side.id, `move ${battle.random(4) + 1}`);
if (++tries > 50) throw new Error(`${side.id} stuck on a move
choice: ${side.choice.error}`);
} while (!result);
break;
}
}
}
wincounter[battle.winner || 'draw']++;
if (DEBUG && i % DEBUG_EVERY === 0) {
console.log('===BEGIN RANDOM MATCH LOG EXPORT===');
console.log(battle.log.join('\n'));
console.log('===END RANDOM MATCH LOG EXPORT===');
console.log('===BEGIN RANDOM MATCH INPUT LOG EXPORT===');
console.log(battle.inputLog.join('\n'));
console.log('===END RANDOM MATCH INPUT LOG EXPORT===');
}
battle.destroy();
if (MAX_TIME && Date.now() - startTime >= MAX_TIME) {
break;
}
}
console.log(`Simulated ${i} battles in ${Date.now() - startTime}ms.`);
console.log(`Blue win chance: ${wincounter['Blue'] / i}`);
console.log(`Red win chance: ${wincounter['Red'] / i}`);
console.log(`Draw chance: ${wincounter['draw'] / i}`);
// Large Counts condition
if (wincounter['Blue'] >= 10 && wincounter['Red'] >= 10) {
let favoredWinrate = Math.max(wincounter['Blue'], wincounter['Red']) /
i;
let standardError = Math.sqrt(favoredWinrate * (1-favoredWinrate) / i);
console.log(`Margin of error: ${Z_STAR * standardError}`);
} else {
console.log("Counts are too small to infer a margin of error.");
}
}
main();
I've filled in all the information of my PC and whatever codes and downloads I was supposed to do stated, but I'm not sure what I've done wrong. Help would be very greatly appreciated. Thank you. If it would be easier I'll also provide the Github link I've acquired this code from. https://gist.github.com/mathfreak231/4229a28f419f4aa10fcb2eba9fdd57e9
I'm not sure if this is just how SO formats it, but there are a few extraneous linebreaks here:
On the line with ruleset: ["PBR Sleep Clause", "PBR Freeze Clause", "PBR Self-Destruct. The next line begins with Clause, which is not a valid JS keyword, and thus the error
On the line with throw new Error("Can't analyze a match unless all the Pokemon have been, again an unnecessary linebreak.
Comment line beginning with // If this is true, it prints some match logs (1 in DEBUG_EVERY) to stdout has a linebreak.
Comment line beginning with // Set this to require a .json file to bash a match there instead of a live has a linebreak.
In general, please read the error message more explicitly. It'll almost always explicitly tell you where it believes the error to be.

Node.js while loop return deffered result

What I'm trying to do is this, I have 2 users an attacker and a defender. I want the call hit() function until one of the runs out of Health, hit() should be called on turns, first attacker, then defender, then attacker and so on until one reaches 0 hp.
My idea was to do it with a while loop, with current code all I get is the console.log from hit(), an infinite loop. The data from hit() is returned correctly if it's not inside a loop ... I could simply just work in the while loop and not use the hit function but it would mean repeating a lot of code, since there will be a few things to consider before a hit actually happens.
If you have an alternative to the while loop I'm open to ideas, also I should mention I'm new at node so keep it as simple as possible please. Thank you.
This is the relevant part of the code:
var prepareAttack = function(attacker,defender) {
connectdb().done(function(connection) {
query(connection,'SELECT * FROM members WHERE name = ?', attacker).done(function(result) {
var attackerData = result[0][0]
query(connection,'SELECT * FROM members WHERE name = ?', defender).done(function(result) {
var defenderData = result[0][0]
var attackerHp = attackerData.hp
var defenderHp = defenderData.hp
while(attackerHp > 0 && defenderHp > 0) {
hit(attackerData,defenderData).done(function(result){
defenderHp = result;
console.log(defenderHp)
})
hit(defenderData, attackerData).done(function(result) {
attackerHp = result;
console.log(attackerHp)
})
}
})
})
})
}
var hit = function (attackerData, defenderData) { // simplified code just relevant parts inside.
console.log('hitting')
var defer = Q.defer()
var newHp = 0;
defer.resolve(newHp)
return defer.promise
}

Slices of image in GraphicMagick

I have read in the docs, that when you do not specify x, y in the following method, that the image is sliced in to strips:
gm(image).crop(1280, 1)
However! It still only slices as if you write the method as:
gm(image).crop(1280, 1, 0, 0)
http://www.graphicsmagick.org/GraphicsMagick.html#details-crop
Has anyone had this problem, and is there a way to force slices of the given image?
Ok, so if anyone else finds this useful, this was my solution to making a function to iterate through an image and make slices:
/*
SLICE IMAGES
*/
// saved images as an array
var images = fs.readdirSync('captures');
// amount of saved images on disk
var imageCount = images.length;
// assume there are no images currently
var imageCounter = 0;
// create a random string to ID the slices
function randomStringGenerator(length, chars) {
var result = '';
for (var i = length; i > 0; --i) result += chars[Math.round(Math.random() * (chars.length - 1))];
return result;
}
// get images function to iterate over the images saved to disk
(function getImage() {
// use 'setTimeout' to get around memory issues
setTimeout(function () {
// if there are more images than have been currently iterated through
if (imageCount > imageCounter) {
// path to current image to be sliced
var image = 'captures/' + images[imageCounter];
// use the size method to get the image width and height, useful for images submitted on mobile etc.
gm(image).size(function(err, value){
// check for errors, TO DO: put this in 'if' statement
console.log('Error: ', err);
// get current image width
var imageWidth = value.width;
// get current image height
var imageHeight = value.height;
// start slicing on first pixel
var sliceCounter = 1;
//
(function getSlices() {
// use 'setTimeout' to get around memory issues
setTimeout(function() {
// if the image height is bigger than the current slice
if (imageHeight > sliceCounter) {
// apply the random string to the slice name, time not needed here as it is in the parent image file name
var randomString = randomStringGenerator(32, '0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ');
// crop image to the full width of current image and increments of 1 pixel
gm(image).crop(imageWidth, 1, sliceCounter, 0).write('slices/slice' + randomString + '.png', function (err) {
// check for errors, TO DO: put this in 'if' statement
console.log('Error: ', err);
// increase the slice counter, to affect the next slice
sliceCounter++;
// fire function recurssively, to help with memory
getSlices();
});
} else {
// if we have sliced the whole image, increase the 'imageCounter' to iterate over next image
imageCounter++;
// get next image
getImage();
}
}, 250);
})();
});
}
}, 250);
})();

Node JS Sync check if file is being uploaded

I need to check if a file is being uploaded to a FTP server. I have no control over the server side (I'd just love a file.temp rename), so my option (best guess to my knowledge of FTP) is to ask the file size or last modified after an interval. My problem is I would need this to be sync.
function isStillUploading(ftp, filePath) {
var startFileSize = -1;
var nextFileSize = -2;
ftp.size(filePath,
function sizeReturned(error, size) {
if (!error) startFileSize = size;
else startFileSize = -3;
});
// CPU-melting style delay.
var start = Date.now();
while (Date.now() - start < 500) { }
ftp.size(filePath,
function sizeReturned(error, size) {
if (!error) nextFileSize = size;
else nextFileSize = -4;
});
// This would be better, but I have no way of having
// root f wait for this and return a correct boolean.
//setTimeout(ftp.size(filePath,
// function sizeReturned(error, size) {
// if (!error) nextFileSize = size;
// else nextFileSize = -4;
// }),
// 500);
// TODO: add checks for -1 -2 -3 -4
console.log("File size change: " + startFileSize + ":" + nextFileSize);
return (startFileSize != nextFileSize);
}
Writing this in a callback style would still imply a loop somewhere to re-check the file's size or (IMO) nasty callback nesting I really don't like. As far as code readability goes, I think just making it sync would be so much easier.
Is there a simple way of doing this or should I re-write with callbacks and events?
Thank you for your help.

Node socket.io, anything to prevent flooding?

How can I prevent someone from simply doing
while(true){client.emit('i am spammer', true)};
This sure proves to be a problem when someone has the urge to crash my node server!
Like tsrurzl said you need to implement a rate limiter (throttling sockets).
Following code example only works reliably if your socket returns a Buffer (instead of a string). The code example assumes that you will first call addRatingEntry(), and then call evalRating() immediately afterwards. Otherwise you risk a memory leak in the case where evalRating() doesn't get called at all or too late.
var rating, limit, interval;
rating = []; // rating: [*{'timestamp', 'size'}]
limit = 1048576; // limit: maximum number of bytes/characters.
interval = 1000; // interval: interval in milliseconds.
// Describes a rate limit of 1mb/s
function addRatingEntry (size) {
// Returns entry object.
return rating[(rating.push({
'timestamp': Date.now(),
'size': size
}) - 1);
}
function evalRating () {
// Removes outdated entries, computes combined size, and compares with limit variable.
// Returns true if you're connection is NOT flooding, returns false if you need to disconnect.
var i, newRating, totalSize;
// totalSize in bytes in case of underlying Buffer value, in number of characters for strings. Actual byte size in case of strings might be variable => not reliable.
newRating = [];
for (i = rating.length - 1; i >= 0; i -= 1) {
if ((Date.now() - rating[i].timestamp) < interval) {
newRating.push(rating[i]);
}
}
rating = newRating;
totalSize = 0;
for (i = newRating.length - 1; i >= 0; i -= 1) {
totalSize += newRating[i].timestamp;
}
return (totalSize > limit ? false : true);
}
// Assume connection variable already exists and has a readable stream interface
connection.on('data', function (chunk) {
addRatingEntry(chunk.length);
if (evalRating()) {
// Continue processing chunk.
} else {
// Disconnect due to flooding.
}
});
You can add extra checks, like checking whether or not the size parameter really is a number etc.
Addendum: Make sure the rating, limit and interval variables are enclosed (in a closure) per connection, and that they don't define a global rate (where each connection manipulates the same rating).
I implemented a little flood function, not perfect (see improvements below) but it will disconnect a user when he does to much request.
// Not more then 100 request in 10 seconds
let FLOOD_TIME = 10000;
let FLOOD_MAX = 100;
let flood = {
floods: {},
lastFloodClear: new Date(),
protect: (io, socket) => {
// Reset flood protection
if( Math.abs( new Date() - flood.lastFloodClear) > FLOOD_TIME ){
flood.floods = {};
flood.lastFloodClear = new Date();
}
flood.floods[socket.id] == undefined ? flood.floods[socket.id] = {} : flood.floods[socket.id];
flood.floods[socket.id].count == undefined ? flood.floods[socket.id].count = 0 : flood.floods[socket.id].count;
flood.floods[socket.id].count++;
//Disconnect the socket if he went over FLOOD_MAX in FLOOD_TIME
if( flood.floods[socket.id].count > FLOOD_MAX){
console.log('FLOODPROTECTION ', socket.id)
io.sockets.connected[socket.id].disconnect();
return false;
}
return true;
}
}
exports = module.exports = flood;
And then use it like this:
let flood = require('../modules/flood')
// ... init socket io...
socket.on('message', function () {
if(flood.protect(io, socket)){
//do stuff
}
});
Improvements would be, to add another value next to the count, how often he got disconneted and then create a banlist and dont let him connect anymore. Also when a user refreshes the page he gets a new socket.id so maybe use here a unique cookie value instead of the socket.id
Here is simple rate-limiter-flexible package example.
const app = require('http').createServer();
const io = require('socket.io')(app);
const { RateLimiterMemory } = require('rate-limiter-flexible');
app.listen(3000);
const rateLimiter = new RateLimiterMemory(
{
points: 5, // 5 points
duration: 1, // per second
});
io.on('connection', (socket) => {
socket.on('bcast', async (data) => {
try {
await rateLimiter.consume(socket.handshake.address); // consume 1 point per event from IP
socket.emit('news', { 'data': data });
socket.broadcast.emit('news', { 'data': data });
} catch(rejRes) {
// no available points to consume
// emit error or warning message
socket.emit('blocked', { 'retry-ms': rejRes.msBeforeNext });
}
});
});
Read more in official docs

Resources