Unexpected Identifier in node.js - 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.

Related

Node fluent-ffmpeg screenshots on Windows

The fluent-ffmpeg package has a function that will create screenshots at even intervals in a video for a given count. The problem with this is that it often spawns a ENAMETOOLONG error on Windows.
To get around this, I copied the relevant parts of the source code that I needed and instead of running it all as one long command, split it up into n many commands (where n is the amount of screenshots).
This seems to work at first. It (sometimes) gets the first screenshot, but then it throws the same ENAMETOOLONG error. How can I get around this?
Here is what I have attmpted:
const count = 50 // just an example
const dest = path.join(os.tmpdir(), `screenshots_${Date.now()}`)
const interval = 100 / (1 + count)
let timemarks = Array(count)
.fill(0).map((_, i) => (interval * (i + 1)) + '%')
.map(mark => {
if (('' + mark).match(/^([\d.]+)%$/)) {
// #ts-ignore
return videoDuration * parseFloat(mark) / 100
} else {
return mark
}
})
.map(mark => {
if (typeof mark === 'number') {
return mark
}
if (mark.indexOf(':') === -1 && mark.indexOf('.') >= 0) {
return Number(mark)
}
const parts = mark.split(':')
let secs = Number(parts.pop())
if (parts.length) {
secs += Number(parts.pop()) * 60
}
if (parts.length) {
secs += Number(parts.pop()) * 3600
}
return secs
})
.sort((a, b) => a - b)
// my first attempt:
for (const mark of timemarks) {
this.video
.screenshot({
count: 1,
folder: dest,
timemarks: [mark],
})
}
// second attempt:
let i = 0
for (const mark of timemarks) {
const filename = `frame_${i.toString().padStart(count.toString().length, '0')}.jpg`
this.video
.setStartTime(mark)
.output(path.join(dest, filename))
.frames(1)
.run()
i++
}
For context, this.video is just ffmpeg(videoFilePath).
My first attempt, as I previously stated, just throws the same error and doesn't really get anywhere. Sometimes I get the first screenshot, most times I do not.
The second attempt doesn't even do anything. It seems to take it's time doing something, but I don't actually get any screenshots. If they saved anywhere, I don't know where.
Is anyone able to help me with this?

TypeError: Cannot read property ' ' error with discord.js

So I have figured out how to set up a simple database with discord.js in a users.json file and my !start cmnd works to create the users database, but when me and my cousin tried the !daily cmnds, the cmnd seems to be fine but I get this error: TypeError: Cannot read property 'a number' of undefined. I believe the number refers to my user number or database number (a number means an actual long number, not "a number").
Also here is the code that goes along with this that is in my index.js file:
var UserJSON = JSON.parse(Fs.readFileSync('./DB/users.json'));
UserJSON[message.author.id] = {
bal: 0,
lastclaim: 0,
}
Fs.writeFileSync('./DB/users.json', JSON.stringify(UserJSON));
let SuccessEmbed = new Discord.MessageEmbed();
SuccessEmbed.setTitle("**SUCCESS**");
SuccessEmbed.setDescription("You have joined the economy! type !help to get started");
message.channel.send(SuccessEmbed);
return;
}
if (args[0] == "daily") {
let userJSON = JSON.parse(Fs.readFileSync('./DB/users.json'));
if (Math.floor(new Date().getTime() - UserJSON[message.author.id].lastclaim) / (1000 * 60 * 60 * 24) < 1) {
let WarningEmbed = new Discord.MessageEmbed()
WarningEmbed.setTitle("**ERROR**");
WarningEmbed.setDescription("You have claimed today already");
message.channel.send(WarningEmbed);
return;
}
UserJSON[message.author.id].bal += 500;
UserJSON[message.author.id].lastclaim = new Date().getTime();
Fs.writeFileSync('./DB/users.json', JSON.stringify(UserJSON));
let SuccessEmbed = new Discord.MessageEmbed();
SuccessEmbed.setTitle("**SUCCESS**");
SuccessEmbed.setDescription("You have claimed a daily reward of 500 coins!");
message.channel.send(SuccessEmbed);
}
}
})
Also to specify, the ./DB/users.json refers to the folder DB for database and users.json is the file that stores the databases.
Here is what the user.json file looks like:
{"*my database number*":{"bal":0,"lastclaim":0},"*my cousin's database number*":{"bal":0,"lastclaim":0}}
Is there any code I need to add into my index.js file to stop this from happening. If possible, answer as soon as possible so I can get this error worked out. Thank You!
Edit: I somehow figured this out by re-doing it and this is the finished product if anyone wants to start an economy bot:
const Discord = require("discord.js");
const client = new Discord.Client();
const Fs = require("fs");
const prefix = "!";
client.on("ready", () => {
console.log("Ready!");
});
client.on("message", async (message) => {
if(message.content.startsWith(prefix)) {
var args = message.content.substr(prefix.length)
.toLowerCase()
.split(" ");
if (args[0] == "start") {
let UserJSON = JSON.parse(Fs.readFileSync("./DB/users.json"));
UserJSON[message.author.id] = {
bal: 0,
lastclaim: 0,
}
Fs.writeFileSync("./DB/users.json", JSON.stringify(UserJSON));
let SuccessEmbed = new Discord.MessageEmbed();
SuccessEmbed.setTitle("**SUCCESS**");
SuccessEmbed.setDescription("You have joined the economy! type !help to get started");
message.channel.send(SuccessEmbed);
return;
}
if (args[0] == "daily") {
let UserJSON = JSON.parse(Fs.readFileSync("./DB/users.json"));
if (Math.floor(new Date().getTime() - UserJSON[message.author.id].lastclaim) / (1000 * 60 * 60 * 24) < 1) {
let WarningEmbed = new Discord.MessageEmbed()
WarningEmbed.setTitle("**ERROR**");
WarningEmbed.setDescription("You have claimed today already");
message.channel.send(WarningEmbed);
return;
}
UserJSON[message.author.id].bal += 500;
UserJSON[message.author.id].lastclaim = new Date().getTime();
Fs.writeFileSync("./DB/users.json", JSON.stringify(UserJSON));
let SuccessEmbed = new Discord.MessageEmbed();
SuccessEmbed.setTitle("**SUCCESS**");
SuccessEmbed.setDescription("You have claimed a daily reward of 500 discord coins!");
message.channel.send(SuccessEmbed);
}
}
})
client.login('your token');
also remember to make a DB folder with an users.json file
I realized that the problem with the code is that instead of vars, it needed to be let before the UserJSON, so the line of code should read:
let UserJSON = JSON.parse(Fs.readFileSync("./DB/users.json"));
UserJSON[message.author.id] = {
bal: 0,
lastclaim: 0,
}

Random number generator does not produce an expected answer Node JS

In a Node JS script, a formula is being used to generate a random number between two values. Here is the function randomIntInc:
function randomIntInc(low, high) {
return Math.floor(Math.random() * (high - low + 1) + low);
}
The function is called with two values as low and high:
let randomNumber = randomIntInc(requestedStart, requestedEnd);
console.log(requestedStart) before executing the above line produces 542 (expected)
console.log(requestedEnd) before executing the above line produces 592 (expected)
Now, running the above line and logging the output produces any value, generally within 6 and 50.
What is happening? Logging before executing the random number shows the correct ranges, but the outputted number just does not fall within the given range.
Here's the full code (related code at 113) (please ignore bad practice code that's unrelated / memes or easter eggs):
// Copyright (C) 2018, Julian Lachniet, Jacob Wysko
// Dependencies
var Discord = require('discord.io'),
logger = require('winston'),
auth = require('../auth.json'),
rn = require('random-number'),
http = require('http'),
request = require('request');
// Authorize connection
var bot = new Discord.Client({
token: auth.token,
autorun: true
});
// GOOGLE SHEETS API
const fs = require('fs');
const readline = require('readline');
const {google} = require('googleapis');
const SCOPES = ['https://www.googleapis.com/auth/spreadsheets.readonly'];
const TOKEN_PATH = 'credentials.json';
// If bot disconnects, try to reconnect
bot.on('disconnect', function(msg, code) {
if (code === 0) return console.error(msg);
bot.connect();
});
// Returns the current timestamp
function timeStamp() {
return "[" + new Date().toString().split(" G")[0] + "]";
}
// Returns a random integer
function randomIntInc(low, high) {
return Math.floor(Math.random() * (high - low + 1) + low);
}
// When a message is recieved
bot.on('message', function(user, userID, channelID, message, evt) {
let valid = false;
// Log what the user sent (verifies it wasn't sent by the bot)
if (user !== 'teacher-quotes-bot') {
console.log(timeStamp() + " " + user + ": " + message);
}
// Sends a message with text "message", logs it
function sendMessage(message) {
bot.sendMessage({
to: channelID,
message: message
});
console.log(timeStamp() + " Bot: " + message);
valid = true;
}
// Sends a message based on a list of commands
function infoMessage(commands, response) {
if (commands.includes(input)) {
sendMessage(response);
}
validCommands.push(commands);
}
// If the message began with a '!' (e.g., !cmd)
if (message.substring(0, 1) === '!') {
var args = message.substring(1).split(' ');
var input = args[0];
var validCommands = [];
// Commands
infoMessage(['accuracy', 'disclaimer', 'terms'], '<#440534402171404289>');
infoMessage(['android', 'app'], 'https://play.google.com/store/apps/details?id=com.jacobwysko.teacherquotes');
infoMessage(['format', 'formats', 'formatting'], '"Quote text." - Teacher, MM/DD/YYYY');
infoMessage(['code', 'git', 'github', 'roadmap', 'trello', 'source', 'sourcecode'], 'https://github.com/wyskoj/TeacherQuotesVersion3\nhttps://github.com/jlachniet/TeacherQuotesBot');
infoMessage(['faq', 'help', '?'], "You've been <#440504422028804107>ed.");
infoMessage(['ping', 'pong'], 'Pong!');
infoMessage(['rule', 'rules'], "You've been <#440500062011916298>ed");
infoMessage(['apple', 'ios', 'web', 'website'], 'http://jacobwysko.com/teacherquotes');
infoMessage(['cmd', 'cmds', 'command', 'commands'], 'Valid Commands:\n - accuracy\n - app\n - format\n - github\n - help\n - ping\n - rules\n - website');
// Easter eggs
infoMessage(['ivefallenandicantgetup'], 'Stay right there, ' + user + "! Don't worry, help is on the way!");
infoMessage(['goddammitimpregnant'], '...for the fifteenth damn time!');
infoMessage(['yeah'], 'boiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiii');
infoMessage(['ohhi'], 'mark');
infoMessage(['poopity'], 'scoop');
switch (input) {
case 'fact':
case 'randomfact':
// Sends a random fact
request('https://spreadsheets.google.com/feeds/list/1ggvma51cj7ryPqfxGv47ZPLuP72keZmFbGE-KBHqbuU/4/public/values?alt=json', function(error, response, body) {
let boi = body;
let output = JSON.parse(boi);
let options = {
min: 0,
max: 11,
integer: true
};
let randomNumber = rn(options);
sendMessage(output["feed"]["entry"][randomNumber]["gsx$randomfact"]["$t"]);
});
break;
case 'randomquote':
// Sends a random quote
if (args.length !== 2) {
request('https://spreadsheets.google.com/feeds/list/1ggvma51cj7ryPqfxGv47ZPLuP72keZmFbGE-KBHqbuU/1/public/values?alt=json', function(error, response, body) {
let output = JSON.parse(body);
let options = {
min: 0,
max: output["feed"]["entry"].length,
integer: true
};
let randomNumber = rn(options);
sendMessage(('"' + output["feed"]["entry"][randomNumber]["gsx$quote"]["$t"] + '" - ' + output["feed"]["entry"][randomNumber]["gsx$teacherquoted"]["$t"] + ", " + output["feed"]["entry"][randomNumber]["gsx$date"]["$t"]))
});
} else {
console.log("SPECIFICS TEACHER");
let requestedTeacher = args[1];
request('https://spreadsheets.google.com/feeds/list/1ggvma51cj7ryPqfxGv47ZPLuP72keZmFbGE-KBHqbuU/1/public/values?alt=json', function (error, response, body) {
let quotes = JSON.parse(body);
console.log(quotes);
request('https://spreadsheets.google.com/feeds/list/1ggvma51cj7ryPqfxGv47ZPLuP72keZmFbGE-KBHqbuU/2/public/values?alt=json', function (error, response, body){
let ranges = JSON.parse(body);
console.log(ranges);
let teachers = [];
for (i = 0; i < ranges["feed"]["entry"].length; i++){
teachers.push(ranges["feed"]["entry"][i]["gsx$teacher"]["$t"]);
}
let teacherStart = [];
for (i = 0; i < ranges["feed"]["entry"].length; i++){
teacherStart.push(ranges["feed"]["entry"][i]["gsx$rangestart"]["$t"]);
}
let teacherEnd = [];
for (i = 0; i < ranges["feed"]["entry"].length; i++){
teacherEnd.push(ranges["feed"]["entry"][i]["gsx$rangeend"]["$t"]);
}
let requestedTeacherId = teachers.indexOf(requestedTeacher);
let requestedStart = teacherStart[requestedTeacherId];
let requestedEnd = teacherEnd[requestedTeacherId];
console.log(requestedStart);
console.log(requestedEnd);
let randomNumber = randomIntInc(requestedStart, requestedEnd);
console.log(requestedTeacher);
console.log(randomNumber);
});
});
}
break;
}
if (!valid && !['fact', 'randomfact', 'randomquote'].includes(input)) {
sendMessage('Invalid command. Type "!command" for a list of commands.');
}
} else
if (user !== 'teacher-quotes-bot' && channelID === '465330717782441986') {
let validQuoteRegex = /".+"\s-\s\w+,\s\d{2}\/\d{2}\/\d{4}/;
if (validQuoteRegex.test(message)) { // If the message sent was a valid quote
let justMessage = message.split('"')[1];
let justTeacher = message.split('"')[2].substring(3, message.split('"')[2].length - 12);
let justDate = message.substring(message.length - 10, message.length);
if (justTeacher.indexOf("(") > -1) {
justTeacher = justTeacher.substring(0, justTeacher.indexOf("("));
}
sendMessage("Message: " + justMessage + "\nTeacher: " + justTeacher + "\nDate: " + justDate);
} else {
sendMessage("Invalid Quote");
}
}
});
It appears the returned JSON is storing the values assigned to requestedStart and requestedEnd as strings rather than numbers. If that is the case, you will need to convert them to numbers before performing the arithmetic for the random number range.
function randomIntInc(low, high) {
console.log({
low,
high,
'(high - low) + 1': (high - low) + 1,
'(high - low + 1) + low': (high - low + 1) + low
});
return Math.floor(Math.random() * (high - low + 1) + low);
}
console.log({
strings: randomIntInc('10', '20'),
numbers: randomIntInc(10, 20)
});
Stepping through the string invocation, '10' and '20', results in the following operations:
('20' - '10' + 1): the order of operations will cause the subtraction operations to be done first which will automatically convert both strings to numbers and result in 10. The final addition will simply add the two number and produce 11.
The key part is the last addition which attempts to add a number with a string. This will cause the number, '11', to be converted to a string and then concatenated with the string, '10', which results in '1110'. This can be confusing unless you remember that the addition symbol is also used for string concatenation.
Finally the multiplication uses the random number times a string value of '1110' which results in a value of 0 - 11.1.

Blockly while loop argument

I have created a little maze with a robot and I use Blockly to generate code to try to solve it. I can move the robot using Javascript commands which are Blockly blocks. So far so good.
I am currently breaking my head over arguments of if-statements and while loops. Mainly, I have tried two things:
Blockly maze
create a variable, 'not_goal_reached' which says whether or not the robot has reached the goal position (cross). Code:
function not_done() {
var goal_location = get_goal_position()
var goal_x = goal_location[0];
var goal_y = goal_location[1];
console.log('in not done');
//console.log(player.x!= goal_x || player.y != goal_y)
return (player.x!= goal_x || player.y != goal_y);
};
Blockly.Blocks['not_goal_reached'] = {
init: function() {
this.appendDummyInput()
.appendField("not at goal")
this.setOutput(true, "Boolean");
this.setColour(230);
this.setTooltip('');
this.setHelpUrl('');
}
};
Blockly.JavaScript['not_goal_reached'] = function(block) {
var code = 'not_done()';
// TODO: Change ORDER_NONE to the correct strength.
//console.log(code)
return [code, Blockly.JavaScript.ORDER_ATOMIC];
};
However, when using this block in an If or While statement. I always get a Javascript error that does not help me to find the solution:
TypeError: Cannot read property 'toBoolean' of undefined
at Interpreter.stepConditionalExpression (acorn_interpreter.js:148)
at Interpreter.step (acorn_interpreter.js:45)
at nextStep (index.html:79)
I use the Acorn js interpreter:
window.LoopTrap = 2000;
//Blockly.JavaScript.INFINITE_LOOP_TRAP = 'if(--window.LoopTrap == 0) throw "Infinite loop.";\n';
var code = Blockly.JavaScript.workspaceToCode(workspace);
console.log(code);
var myInterpreter = new Interpreter(code, initInterpreter);
//Blockly.JavaScript.INFINITE_LOOP_TRAP = null
var counter = 0;
function nextStep() {
try {
if (myInterpreter.step()) {
counter+=1;
console.log(counter);
if (counter < window.LoopTrap) {
window.setTimeout(nextStep, 30);
}
else {
throw "Infinite Loop!"
}
}
}
catch (e) {
//alert(e);
console.log(e)
}
}
nextStep();
Problem: javascript error I can not solve :(
I created my own While block that does not require input. This While block checks internally whether or not the robot has reached the goal and then processes the DO statements:
Blockly.Blocks['repeat_forever'] = {
init: function() {
this.appendDummyInput()
.appendField("While not at goal");
this.appendStatementInput("DO")
.appendField("Do");
this.setPreviousStatement(true);
this.setColour(230);
this.setTooltip('');
this.setHelpUrl('');
}
};
Blockly.JavaScript['repeat_forever'] = function(block) {
var branch = Blockly.JavaScript.statementToCode(block, 'DO');
// TODO: Assemble JavaScript into code variable.
//if (Blockly.JavaScript.INFINITE_LOOP_TRAP) {
// branch = Blockly.JavaScript.INFINITE_LOOP_TRAP.replace(/%1/g,
// '\'block_id_' + block.id + '\'') + branch;
// console.log(branch);
//}
var code = 'while (' + not_done() + ') {' + branch + '}';
console.log(code)
return [code, Blockly.JavaScript.ORDER_ATOMIC];
};
This works, BUT, here I have the problem that my internal function 'not_done' is only evaluated once (at code generation) to while(true) (since the first time the robot is of course not at the goal location yet). This block correctly applies the DO codes but does not halt (since while (true)). If I add quotes around 'not_done()' the function is evaluated once apparently, but then I receive the same Javascript error as above (Cannot read property 'toBoolean' of undefined)
Am I missing something here? Thanks a lot for your time!
Greetings
K
It seems that you setTimeout which cannot be reached while the while loop runs.

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