Random number generator does not produce an expected answer Node JS - 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.

Related

Problems with calling functions in other file (Firebase Functions)

I'm making an API using express in Firebas Functions. So Im having the following problem:
error:Error>>TypeError: firebase_util.updateWalletData is not a function
Basically I have a index.js, and two files (firebase and axies.js)
The problem comes in a function in axies.js:
async function firstUpdate(wallet) {
const baseUrl = 'https://game-api.skymavis.com/game-api/clients/'
const path = '/items/1'
try {
let newAdd = wallet.replace('ronin:', '0x');
let requestURL = baseUrl+newAdd + path;
console.log('wallet>>' + newAdd);
let response = await axios.get(requestURL, {raxConfig: {statusCodesToRetry: [[100, 199], [400, 429], [500, 599]]}});
if(response.status === 200) {
let requestData = response['data'];
console.log('data>>' + util.inspect(requestData, true, null, true));
console.log('request>>' + requestURL);
let total_slp = requestData.total;
let last_claim_amount = 0;
let claim_timestamp = 0;
if (requestData.blockchain_related.signature) {
last_claim_amount = requestData.blockchain_related.signature.amount;
claim_timestamp = requestData.blockchain_related.signature.timestamp;
} else {
claim_timestamp = requestData.item.created_at;
}
let next_claim_timestamp = claim_timestamp + 1296000;
let ronin_slp = requestData.blockchain_related.balance;
console.log('ron>>' + ronin_slp);
if (ronin_slp === null) {
ronin_slp = 0;
console.log('ronin value changed to>>' + ronin_slp);
}
let ingame_slp = total_slp - ronin_slp;
let lastupdate = Math.floor(+new Date() / 1000);
let walletInfo = {total_slp, last_claim_amount, claim_timestamp, next_claim_timestamp, ronin_slp, ingame_slp, lastupdate};
console.log('util>>' + util.inspect(walletInfo, true, null, true));
firebase_util.updateWalletData(wallet, walletInfo);
}
} catch(error) {
console.log('Error>>' + error);
}
}
I have a cronjob that once an hour update the information in all wallets, but this function is called when a new wallet is added, so it force an update to get the data for the first time.
In the firebase.js Im doing all the job from writing/reading all the data in firebase realtime database.
firebase.js:
async function updateWalletData(walletID, walletData) {
database.ref('ScolarsToCheck/' + walletID + '/walletData/').update(walletData);
return {'message': 'updated', 'status': 200};
}
module.exports = {setCalendar, createEmptyCalendar, checkCalendar, getAllScolars, addScolar, deleteScolar, addExpenses, deleteExpenses, addClaim, deleteClaim, getScolars, updateWalletData};
All the other stuff works great, but the calls to firebase_util.updateWalletData() just stopped working. I have a lot of functions and endpoints in index.js calling to those functions and all works smothly, except for that single call.

How to avoid having “quota exceeded for 'ReadGroup'” error in google api

I've created a bot which goes in Google Spreadsheet getting some datas before sending them by DM to 50 guild members into Discord.
However, due to high requests of datas, I've got an error message saying that I've exceeded the quota for group 'ReadGroup' and limit 'USER-100s'.
To avoid getting this error, I've created a buffer function however it still doesn't work, does anyone know how to avoid getting this limit error?
Here is the main code which is launched when I type a specific commande in Discord :
const client1 = new google.auth.JWT(keys.client_email, null, keys.private_key, ['https://www.googleapis.com/auth/spreadsheets']);
const client2 = new discord.Client();
.
.
.
let valeur1 = await liste(client1);
await sleep(100000);
console.log("End of first buffering time (100s)");
for (i = 0; i < valeur1.length; i++){
if (valeur1[i] != undefined){
try{
let valeur2 = await envoi(client1, valeur1[i]);
const user = client2.users.get(String(valeur1[i])) || await client2.fetchUser(String(valeur1[i]));
console.log("Ready to send to : " + user.id);
await user.send("The character you need to improve is : " + valeur2[0] + "\n 1. " + valeur2[1] + " = " + valeur2[2] + " >>> " + valeur2[3] + "\n 2. " + valeur2[4] + " = " + valeur2[5] + " >>> " + valeur2[6] + "\n 3. " + valeur2[7] + " = " + valeur2[8] + " >>> " + valeur2[9]);
console.log("Message sent for : " + user.id);
}
catch(err){
console.error(err);
console.log("Error detected for : " + valeur1[i]);
break;
}
}
}
Here is the first function called ("liste") which return the list of the 50 members id :
async function liste(client){
const gsapi = google.sheets({version:'v4',auth: client});
let data1 = new Array();
for (i = 0; i < 50; i++) {
const opt1 = {spreadsheetId: 'XXXXXX', range: 'Serveur!C' + (3+i)};
let data2 = await gsapi.spreadsheets.values.get(opt1);
data1.push(data2.data.values);
}
return data1;
}
And here is the second function called ("envoi") which is supposed to send the DM to the 50 different members of the guild :
async function envoi(client, id){
const gsapi = google.sheets({version:'v4',auth: client});
for (i = 0; i < 50; i++){
const opt1 = {spreadsheetId: 'XXXXXX', range: 'Discord!A' + (3+i)};
let data1 = await gsapi.spreadsheets.values.get(opt1);
if (parseInt(id) === parseInt(data1.data.values)){
const opt2 = {spreadsheetId: 'XXXXXX', range: 'Discord!C' + (3+i)};
let data2 = await gsapi.spreadsheets.values.get(opt2);
const opt3 = {spreadsheetId: 'XXXXXX', range: 'Discord!D' + (3+i)};
let data3 = await gsapi.spreadsheets.values.get(opt3);
.
.
.
const opt10 = {spreadsheetId: 'XXXXXX', range: 'Discord!K' + (3+i)};
let data10 = await gsapi.spreadsheets.values.get(opt10);
const opt11 = {spreadsheetId: 'XXXXXX', range: 'Discord!L' + (3+i)};
let data11 = await gsapi.spreadsheets.values.get(opt11);
var stats = [data2.data.values,data3.data.values,data4.data.values,data5.data.values,data6.data.values,data7.data.values,data8.data.values,data9.data.values,data10.data.values,data11.data.values];
await sleep(10000);
console.log("Extraction done for " + parseInt(id));
return stats;
}
}
console.log("Member not found");
return "erreur";
}
As a result, I would like to get all the members to get their DM. However after the 18th member, an error appear, even though I put some buffering time.
In the console.log, I get :
End of first buffering time (100s)
Extraction done for 408575708424699900
Ready to send to : 408575708424699925
Message sent for : 408575708424699925
.
.
.
Extraction done for 438420776652374000
Ready to send to : 438420776652374036
Message sent for : 438420776652374036
Error: Quota exceeded for quota group 'ReadGroup' and limit 'USER-100s' of service 'sheets.googleapis.com'
.
.
.
Error detected for : 493854774446391296
This is even more strange that the error concerns a member who already have received his DM (he is one the the first 10 members in the list)
Thanks to Tanaike, I updated my code using spreadsheets.values.batchGet() method. In that way instead of extraction values by values, I extracted a batch of values.
And then I made my formula. Now I don't have any issues anymore and even better, my script is way much quicker :)

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.

how can I reliably process thousands of HTTP requests when some may error?

I have run into this problem before for a few HTTP transactions (like a hundred or so posts). Today I'm trying to do 7k HTTP requests. This seems silly but it's the only way to interact with the target system. The best I've been able to do will stall out at about 96% of the requests done. It will then just stop and never complete the last few requests.
Perhaps I'm using OiBackoff incorrectly. It seems to be working fine but that last 3% of the GETs won't ever finish. I've let it set for 5 minutes with no requests coming back when the highest retry interval in the log was 40 seconds.
I wonder if I should do like 100 requests at a time with OiBackoff there to make sure they are all complete.
The goal here is to hit a url that has a number like CFD1234, CFD1236, CFD1238 at the end and push the result (small chunk of xml) into an array. Here's the code, the closest I have to working. Perhaps I need to try a different library? I've tried this with a promises queue and couldn't get it to run. It will work if I create an array of function closures and fire them off in sequence but it takes forever, far longer than it should.
var cnum, cnums, complete, ld, logger, oibackoff, opt, processHttpGet, request, responses, total, yamljs, _fn, _i, _len;
yamljs = require('yamljs');
request = require('request');
oibackoff = require('oibackoff').backoff({
maxTries: 10,
delayRatio: 10
});
cnums = yamljs.load('./etc/cnumbers.yaml');
responses = [];
logger = {
debug: console.log,
error: console.log
};
ld = require('lodash');
cnums = ld.uniq(cnums);
logger.debug("cnums len: " + cnums.length);
processHttpGet = function(url, opt, cb) {
return request.get(url, opt, function(error, resp, body) {
if (error != null) {
return cb(error, null);
} else if (resp.statusCode >= 400) {
return cb(resp.statusCode, null);
} else {
return cb(null, body);
}
});
};
opt = null;
total = cnums.length;
complete = 0;
_fn = function(CNumber) {
var intermediate, url;
url = "http://abc:def#abc.def.com/xyz/def/abc.asmx/GetValueByID?ID=" + CNumber;
logger.debug("getting " + url);
intermediate = (function(_this) {
return function(err, tries, delay) {
if (err != null) {
logger.debug("GET failed for " + url + ":", err);
logger.debug("tries: %d, delay: %d", tries, delay);
}
if (tries > 10) {
logger.debug("/n/n Failed max tries.");
process.exit(0);
return false;
}
};
})(this);
return oibackoff(processHttpGet, url, opt, intermediate, function(error, response) {
if (error) {
return false;
} else {
++complete;
responses.push(response);
if (complete % 100 === 0) {
console.dir({
url: url,
response: response
});
}
logger.debug("success; responses complete: " + complete + ", total: " + total + ", percentage: " + (ld.round(complete / total, 2) * 100) + "%");
if (complete >= total) {
logger.debug(responses);
return process.exit(0);
}
}
});
};
for (_i = 0, _len = cnums.length; _i < _len; _i++) {
cnum = cnums[_i];
_fn(cnum);
}
The answer to this was to use Bluebird, Promise.map and concurrency with a backoff library.
# coffee
# exports is an array of buffers
retry = (require 'u-promised').retry
Promise = require("bluebird")
# build array of buffers to post
Promise.map(exports, (buffer) ->
f = -> postToEndpoint(buffer)
retry(5, f) # post with up to 5 retries
, {concurrency: config.export.concurrency}) # 40 for my app
.then (result) ->
c = 0
ld.map(result, (x) -> c += x)
msg = "Complete. #{c} posts completed."
logger.info msg
.catch (reason) ->
logger.error reason

How to get the reply message without the original message from the Gmail API

I would like to get the reply message in a thread without the original message. However, when I use either Users.messages: GET or Users.threads: GET, I receive the reply (as desired) with the original message (undesired) as well. See screenshot below code.
(This question, as far as I can tell, was also posed here, however I did not find that the proposed solution answers the question and the poster of the proposed solution suggested I start a new question. I tried with Users.threads as Tholle suggests however received the same result.)
I'm a noob, so any and all help is greatly appreciated and I apologize if I'm missing something obvious.
Code
var gapiGETRequest = function (gapiRequestURL)
{
var xmlHttp = new XMLHttpRequest();
xmlHttp.open( "GET", gapiRequestURL, false );
xmlHttp.send( null );
return xmlHttp.responseText;
}
var gapiRequestInboxMessagesAndToken = "https://www.googleapis.com/gmail/v1/users/me/messages?q=-label%3ASENT+in%3AINBOX&access_token=" + thisToken
var allMessagesReceived = gapiGETRequest(gapiRequestInboxMessagesAndToken)
var allMessagesObject = JSON.parse(allMessagesReceived)
var messageIdsOfReceivedMessages = [];
var getIdsOfReceivedMessages = function(responseObject){
for(var i=0; i < responseObject.messages.length; i ++) {
messageIdsOfReceivedMessages.push(responseObject.messages[i].id);
}
}
var messageContentsArr = [];
var getMessageContents = function(messageIdList)
{
for(var i=0; i < messageIdList.length; i++)
{
var gapiRequestMessageWithId = "https://www.googleapis.com/gmail/v1/users/me/messages/" + messageIdList[i] + "?access_token=" + thisToken
var currentMessage = JSON.parse(gapiGETRequest(gapiRequestMessageWithId))
var encodedMessageContents = currentMessage.payload.parts[0].body.data
var decodedMessageContents = atob(encodedMessageContents.replace(/-/g, '+').replace(/_/g, '/'));
messageContentsArr.push(decodedMessageContents)
}
}
getIdsOfReceivedMessages(allMessagesObject);
getMessageContents(messageIdsOfReceivedMessages);
Response
You are getting the full reply message. When the report replied, they quoted the original message and this the text of the original is in the reply message. You may just want to do what Gmail and many other modern emails apps do and collapse/hide any reply text which begins with >.
This is my solution. It's a bit long but I tried to document it as detailed as possible.
Handles message returned by Gmail API:
https://developers.google.com/gmail/api/v1/reference/users/messages#resource
Input:
Hello. This is my reply to message.
On Thu, Apr 30, 2020 at 8:29 PM John Doe <john.doe#example.com>
wrote:
> Hey. This is my message.
>
--
John Doe
My Awesome Signature
Output:
Hello. This is my reply to message.
Code: (Unfortunately this has no syntax highlight :P)
const message = await getMessageFromGmailApi();
const text = getGoogleMessageText(message);
console.log(text, '<= AWESOME RESULT');
function getGoogleMessageText(message) {
let text = '';
const fromEmail = getGoogleMessageEmailFromHeader('From', message);
const toEmail = getGoogleMessageEmailFromHeader('To', message);
let part;
if (message.payload.parts) {
part = message.payload.parts.find((part) => part.mimeType === 'text/plain');
}
let encodedText;
if (message.payload.parts && part && part.body.data) {
encodedText = part.body.data;
} else if (message.payload.body.data) {
encodedText = message.payload.body.data;
}
if (encodedText) {
const buff = new Buffer(encodedText, 'base64');
text = buff.toString('ascii');
}
// NOTE: We need to remove history of email.
// History starts with line (example): 'On Thu, Apr 30, 2020 at 8:29 PM John Doe <john.doe#example.com> wrote:'
//
// We also don't know who wrote the last message in history, so we use the email that
// we meet first: 'fromEmail' and 'toEmail'
const fromEmailWithArrows = `<${fromEmail}>`;
const toEmailWithArrows = `<${toEmail}>`;
// NOTE: Check if email has history
const isEmailWithHistory = (!!fromEmail && text.indexOf(fromEmailWithArrows) > -1) || (!!toEmail && text.indexOf(toEmailWithArrows) > -1);
if (isEmailWithHistory) {
// NOTE: First history email with arrows
const historyEmailWithArrows = this.findFirstSubstring(fromEmailWithArrows, toEmailWithArrows, text);
// NOTE: Remove everything after `<${fromEmail}>`
text = text.substring(0, text.indexOf(historyEmailWithArrows) + historyEmailWithArrows.length);
// NOTE: Remove line that contains `<${fromEmail}>`
const fromRegExp = new RegExp(`^.*${historyEmailWithArrows}.*$`, 'mg');
text = text.replace(fromRegExp, '');
}
text = text.trim()
return text;
}
function getGoogleMessageEmailFromHeader(headerName, message) {
const header = message.payload.headers.find((header) => header.name === headerName);
if (!header) {
return null;
}
const headerValue = header.value; // John Doe <john.doe#example.com>
const email = headerValue.substring(
headerValue.lastIndexOf('<') + 1,
headerValue.lastIndexOf('>')
);
return email; // john.doe#example.com
}
function findFirstSubstring(a, b, str) {
if (str.indexOf(a) === -1) return b;
if (str.indexOf(b) === -1) return a;
return (str.indexOf(a) < str.indexOf(b))
? a
: b; // NOTE: (str.indexOf(b) < str.indexOf(a))
}
Substring the reply content alone, based on your Email id position in the full email string.
$str = "VGhhbmtzIGZvciB0aGUgbWFpbC4NCg0KT24gVHVlLCBKdWwgMTIsIDIwMjIgYXQgMjo1OCBQTSA8aW5mb0BhaXJjb25uZWN0aW5kaWEuY29tPiB3cm90ZToNCg0KPiBTLk5vIFZlbmRvciBQcm9kdWN0IEJhbmR3aWR0aCBGcm9tIExvY2F0aW9uIFRvIExvY2F0aW9uDQo-IDEgQWlydGVsIEludGVybmV0IDEwMCAyMS4xNzAyNDAxLDcyLjgzMTA2MDcwMDAwMDAxDQo-IDE5LjA0NjE5NTY4NjA2MjMxMiw3Mi44MjAyNzY0Mzc4MjA0Mw0KPg0K";
$str_raw = strtr($str, array('+' => '-', '/' => '_'));
$full_email = utf8_decode(base64_decode($str_raw));
$split_position = strpos($full_email, "your_mail_id") - 33; //33 - before that some date time available, remove those also
$final_string = substr($full_email, 0, $split_position);
echo $final_string;
Let me save your day
console.log(message.split("On")[0])
output : Hello. This is my reply to message.

Resources