So I made a bot and one of the commands reqiures it to wait. While someone else is using that how do I let another user, use that command as well and not wait until the other person is finished?
if(command ==='rm'){
var hithere2 = message.content
var ret = hithere2.replace('-rm ','');
var result = ret.slice('&').split(',');
let remindercontent = result[0]
let remindertime = result[1]
if(!isNaN(remindertime)){
if(remindertime % 1 == 0){
message.channel.send({embed: {
color: Math.floor(Math.random() * 16777214) + 1,
title: "Reminder set for " + remindercontent,
description: 'It will go off in ' + remindertime + ' seconds',
}})
let sleeptime = remindertime * 1000
sleep(sleeptime);
message.channel.send('<#' + message.author.id + '>')
message.channel.send({embed: {
color: Math.floor(Math.random() * 16777214) + 1,
title: `BEEEP`,
description: `${remindertime} seconds ago you wanted to remind yourself to **${remindercontent}**`,
}})
}
}else {
message.channel.send('Please enter a valid number after the comma!');
}
Above is the command for the thing that I need to run simultaneously. I have the system-sleep mudule installed.
Have you tried the setTimeout function? I'd usually consider using sleep() functions like that bad practise since you're stalling the thread.
Something like this might work for you (haven't tried this, you may need to pass arguments to that function):
setTimeout( function() {
message.channel.send('<#' + message.author.id + '>')
message.channel.send({embed: {
color: Math.floor(Math.random() * 16777214) + 1,
title: `BEEEP`,
description: `${remindertime} seconds ago you wanted to remind yourself to
**${remindercontent}**`,
}})
}, sleeptime);
To show what a mean jj, here's a code snippet as an example:
var commentInXMilliseconds = (delay) => {
const text = "New time";
setTimeout( function() {
console.log(`${text} ${new Date()}`);
}, delay);
}
console.log(`Start time: ${new Date()}`);
commentInXMilliseconds(2000);
Related
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,
}
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 :)
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.
There is an existing example that updates progress bar in place: https://github.com/nathanpeck/clui/blob/master/examples/progress.js (60 lines in total)
There is much simpler example directly in the readme: https://github.com/nathanpeck/clui/blob/master/README.md (4 lines only)
var clui = require('clui');
var Progress = clui.Progress;
var thisProgressBar = new Progress(20);
console.log(thisProgressBar.update(10, 30));
So I've added setInterval so that it updates the progress. I've also added line to clear the console and new console.log with each interval:
var clui = require('clui');
var Progress = clui.Progress;
var value = 50;
var thisProgressBar = new Progress(20);
console.log(thisProgressBar.update(value, 100));
var intervalId = setInterval(function() {
value++;
// console.log("Updated value: " + value);
process.stdout.write('\033c'); // clearing the console
console.log(thisProgressBar.update(value, 100));
if (value === 100) {
clearInterval(intervalId);
}
}, 500)
In the example with countdown spinner - https://github.com/nathanpeck/clui/blob/master/README.md#spinnerstatustext - I don't have to clear the console and do console.log.
Ideally I would like to do just thisProgressBar.update(value, 100) and the progress bar should be updated in place easily... Not sure my approach is good, and before I start replicating 60 lines solution I want to ask!
Opened GitHub issue as well for extra visibility: https://github.com/nathanpeck/clui/issues/22
Tried creating https://runkit.com/embed/umrs8pxg9geg demo but it is not real console.
You can use npm log-with-statusbar package. How about this:
var clui = require("clui");
var log = require("log-with-statusbar")();
var Progress = clui.Progress;
var value = 50;
var thisProgressBar = new Progress(20);
// console.log(thisProgressBar.update(value, 100));
log.setStatusBarText([thisProgressBar.update(value, 100)]);
var intervalId = setInterval(function() {
value++;
// console.log("Updated value: " + value);
log("Updated value: " + value);
// process.stdout.write('\033c'); // clearing the console
log.setStatusBarText([thisProgressBar.update(value, 100)]);
// console.log(thisProgressBar.update(value, 100));
if (value === 100) {
clearInterval(intervalId);
}
}, 500);
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