Discord.JS Command Issue - javascript

I've been working on this command (that I have a problem with) where when the user says u?hi, the bot replies before putting you in a set. You are then put in a timeout for 20 seconds, and while you are in the timeout, if you type u?hi, the bot replies gotta wait x seconds. When the timeout ends, they can type u?hi and the cycle keeps going.
However, I've encountered a problem. In my code, after doing u?hi, I get put in a timeout (just like how I planned). However, while in the timeout, if I type u?hi while lets say 1 seconds into the timeout, instead of the bot saying gotta wait 19 more seconds, the bot says gotta wait 19 more seconds and then starts counting down all the way to 0. Here's what I mean (Screenshot):
Here's my code:
const intervalSet = new Set();
bot.on("message", msg => {
let args = msg.content.substring(prefix.length).split(" ");
switch (args[0]) {
case "hi":
var interval = 20;
var intervalID;
if (intervalSet.has(msg.author.id)) {
intervalID = setInterval(() => {
interval -= 1;
if (interval !== 0 && args[0] === 'hi') {
msg.channel.send(`gotta wait ${interval} more seconds`);
}
if (interval === 0) {
clearInterval(intervalID);
msg.channel.send(`Ended`);
intervalSet.delete(msg.author.id);
}
}, 1000);
} else {
intervalSet.add(msg.author.id);
msg.channel.send("heyy");
}
}
});
I've tried moving the
if (interval !== 0 && args[0] === 'hi') {
msg.channel.send(`gotta wait ${interval} more seconds`);
}
part to other places of the code and changing it up but nothing seems to work. What can I do about this?

Yes that's normal with your code. What you need to do is to create a map, named cooldown. User IDs will be linked with times, so you'll be able to calculate for how long the user is in cooldown.
Here is an update of your code:
const cooldown = new Map();
bot.on("message", msg => {
let args = msg.content.substring(prefix.length).split(" ");
switch (args[0]) {
case "hi":
var interval = 20000; // use milliseconds instead of seconds
var intervalID;
if (cooldown.has(msg.author.id)) {
let cooldownTime = Date.now() - cooldown.get(msg.author.id);
let timeToWait = (interval-cooldownTime)/1000;
if(cooldownTime < interval) {
return message.channel.send(`gotta wait ${timeToWait} more seconds`);
}
}
cooldown.set(msg.author.id, Date.now());
msg.channel.send("heyy");
}
});
If you have any questions, don't hesitate to post a comment!

Related

SOLVED: Updating async functions between modules

I have been working on a simple loading animation for backend node.js projects and I ran into a problem after trying to modularize it (so that I can just require() it in other projects)
When run, this loading bar creates a spinner out of braille characters in the console and tries to take three inputs (which used to be global variables before I tried to encapsulate the function, however, when that didn't work I added them to the exported variables in module.exports):
totalSteps: the total # of steps the given function has.
currentStep: the current step the function is on.
statusMessage: the message to display while the spinner loads.
every 100ms, the spinner function updates the frame on the animation which includes the spinner, a percentage of complete tasks, the fraction of done vs not done steps, the message, and three animated trailing dots.
for some reason I can not get this function to work now that I have put it in its own module. It does not wait for the provided function to end before it prints its "Done!" message. however, the variables do seem to be shared between the two scripts, as the final "Done!" message includes the message, and the step variables in it.
Here's my code so far for the loading.js file:
module.exports = {
currentStep : 0,
totalSteps : 0,
statusMessage : '',
spinner : async function (func) {
// declare array of frames for the animation
const spinnerAnimation = ['[⠋]', '[⠙]', '[⠹]', '[⠸]', '[⠼]', '[⠴]', '[⠦]', '[⠧]', '[⠇]', '[⠏]'];
const dots = [' ', ' ', ' ', '.', '.', '.', '.. ', '.. ', '.. ', '...', '...', '...'];
let spinnerCounter = 0;
let dotCounter = 0;
// overwrite the line with the new frame every 100ms
const intervalId = setInterval(() => {
spinnerCounter = (spinnerCounter + 1) % spinnerAnimation.length;
dotCounter = (dotCounter + 1) % dots.length;
if (this.statusMessage == '') this.statusMessage = 'Loading';
if (this.currentStep <= this.totalSteps && this.currentStep >= 0 && this.totalSteps >= 1) {
// if the variables used for calculating the % value are in the correct range
// show the % and the # of steps completed out of the total
process.stdout.write(`\r\x1b[32m\x1b[?25l${spinnerAnimation[spinnerCounter]} ${Math.round(this.currentStep / this.totalSteps * 10000)/100}% (Step: ${this.currentStep}/${this.totalSteps} complete)\x1b[0m - ${this.statusMessage}${dots[dotCounter]} `);
} else {
// if not, just dont show them. that way if it fails, the animation still shows but you dont get any weird errors (or NaN/undefined placeholders)
process.stdout.write(`\r\x1b[32m\x1b[?25l${spinnerAnimation[spinnerCounter]}\x1b[0m - ${this.statusMessage}${dots[dotCounter]} `);
}
}, 100);
let error = null;
try {
// await some async operation
await func();
} catch (err) {
error = err
} finally {
clearInterval(intervalId);
if (this.statusMessage == '') this.statusMessage = 'Loading';
if (error) {
// if an error was caught, mark the bar as "Failed!" and post the message
if (this.currentStep <= this.totalSteps && this.currentStep >= 0 && this.totalSteps >= 1) {
process.stdout.write(`\r\x1b[31m[✓] ${Math.round(this.currentStep / this.totalSteps * 10000)/100}% (Step: ${this.currentStep}/${this.totalSteps})\x1b[0m - ${this.statusMessage}... >> FAILED!\x1b[?25h \n`);
} else {
process.stdout.write(`\r\x1b[32m[✓]\x1b[0m - ${this.statusMessage}... >> FAILED!\x1b[?25h \n`);
}
console.error(`\x1b[31m └-${error}\x1b[?25h\x1b[0m`);
this.currentStep = 0;
this.totalSteps = 0;
this.statusMessage = '';
return error;
} else {
// if there was no error, stop the bar and return to the script
if (this.currentStep <= this.totalSteps && this.currentStep >= 0 && this.totalSteps >= 1) {
process.stdout.write(`\r\x1b[32m[✓] ${Math.round(this.currentStep / this.totalSteps * 10000)/100}% (Step: ${this.currentStep}/${this.totalSteps})\x1b[0m - ${this.statusMessage}... >> Done!\x1b[?25h \n`);
} else {
process.stdout.write(`\r\x1b[32m[✓]\x1b[0m - ${this.statusMessage}... >> Done!\x1b[?25h \n`);
}
this.currentStep = 0;
this.totalSteps = 0;
this.statusMessage = '';
return;
}
}
}
}
And for the index.js:
const loading = require('./loading.js');
const sleep = (ms) => new Promise((resolve) => setTimeout(resolve, ms));
async function something() {
loading.totalSteps = 3;
loading.statusMessage = "doing something";
sleep(20000);
loading.currentStep = 1;
loading.statusMessage = "working harder";
sleep(2000);
loading.currentStep = 2;
loading.statusMessage = "almost done";
sleep(2000);
loading.currentStep = 3;
}
async function main() {
await loading.spinner(something);
}
main();
The output for index.js script is as follows:
[✓] 100% (Step: 3/3) - almost done... >> Done!
This is shown almost as soon as it is run, without waiting for the something() function to finish waiting, however the program does not close until the something() function finishes waiting. I don't understand why the spinner never shows up though. any ideas?
on a side note, Is there any way to make it so that the entire line is overwritten every frame? for example, if the statusMessage text is shorter than the frame before it, the end of the last frame persists into the second one because the rest was not overwritten with new text. My hacky solution to this was to just append a lot of spaces to the end of each line so that so long as the message text is short you wont see it but I realize it wont fix the problem, how does one measure the length of the last line and append the correct number of spaces to the end of the new line or clear the line entirely before writing the next one.
I understand this is probably a lot of stuff to go through so I apologize. Thank you in advance! If there's any other information I should provide please ask!

How to fix this function cooldown in javascript?

I'm making a function to clear my local storage every month and set a 25 hour cooldown timeout so it wont activated again after executed but always got reset and keep executed everytime.
Please help to fix this. Thanks
let cooldown = false;
const RECHARGE_TIME = 90000000; //25 hour cooldown
let today = new Date()
document.getElementById("todayDate").innerHTML = today.getDate();
if(today.getDate() === 1 && !cooldown){
clearHighScore();
startCooldown();
}
console.log(cooldown) //just to check the cooldown state
function clearHighScore() {
localStorage.clear();
}
function startCooldown() {
cooldown = true;
setTimeout (function(clearHighScore){ cooldown = false}, RECHARGE_TIME);
}

What am I doing bad with localStorage?

I am doing a time-to-click score table but first I have to localstorage each time I get in a game but I dont know how I have been trying everything but it still not working, I need to finish this fast, and I need help... otherwise I would try everyday to resolve this alone because I know that's the way to learn.. When I press the finished button It says that times.push() is not a function.
let times = Array.from(
{ length: 3 }
)
let interval2;
// Timer CountUp
const timerCountUp = () => {
let times = 0;
let current = times;
interval2 = setInterval(() => {
times = current++
saveTimes(times)
return times
},1000);
}
// Saves the times to localStorage
const saveTimes = (times) => {
localStorage.setItem('times', JSON.stringify(times))
}
// Read existing notes from localStorage
const getSavedNotes = () => {
const timesJSON = localStorage.getItem('times')
try {
return timesJSON ? JSON.parse(timesJSON) : []
} catch (e) {
return []
}
}
//Button which starts the countUp
start.addEventListener('click', () => {
timerCountUp();
})
// Button which stops the countUp
document.querySelector('#start_button').addEventListener('click', (e) => {
console.log('click');
times = getSavedNotes()
times.push({
score: interval2
})
if (interval) {
clearInterval(interval);
clearInterval(interval2);
}
})
You probably need to change the line times = JSON.parse(localStorage.times || []) to times = JSON.parse(localStorage.times) || [] this makes sure that if the parsing fails it will still be an array.
Even better is wrap them with try-catch just in case if your JSON string is corrupted and check if the time is an array in the finally if not set it to empty array.

Make a loop stop on variable being false

I'm trying to make a discord bot that says a message every 5 seconds when a condition or command is executed, and stop that loop when another one is executed. I have simple code and I'm not sure why its not working, I have just make it log to console for testing right now.
I have tried while loop and for loop, setinterval and they just won't stop even when I set the var to false.
var c = 0;
if (message.content == prefix + 'test') {
message.delete();
c = 1;
while (c = 1) {
setInterval(function () {
console.log(c);
}, 5000)
}
}
if (message.content == prefix + 'stoptest') {
message.delete();
c = 2;
}
if (message.content == prefix + 'check') {
message.delete();
console.log(c);
}
I'm confused why when I do the stoptest command and set the var c to 2 it still continues to log in console saying that c = 1.
when you write :
while (c = 1) {
setInterval(function () {
console.log(c);
}, 5000)
}
c = 1 is not a condition, check operator in JS ==
setInterval() is already designed to call something indefinitely so don't nest it in a while loop
if you want to write a loop that can be stopped :
let flag = true;
while(flag) {
//do something
if(anythingYouChooseHappens) {
flag = false; // that stops the loop
// or if don't want to use a variable you can just write `break` in this closure
}
}
I think while loop is not the best solution here so you can start with :
let myInterval;
myInterval = setInterval(function() {
console.log(c);
if(anythingYouChooseHappens) {
clearInterval(myInterval); //stop the 'loop'
}
}, 5000);
The reason this is not working is because the while loop is happening synchronously, meaning the code following it will never get a change to execute as the while loop will never actually exit.
If you simply want to keep posting messages until a certain event fires, a loop is not necessary. Instead, you can try:
var interval;
if (message.content == prefix + 'test') {
interval = setInterval(function () {
// Post your messages to Discord here
}, 5000)
}
if (message.content == prefix + 'stoptest' && interval) {
clearInterval(interval);
}
Here, we set an interval that starts when the message content is prefix + 'test', and we clear that interval whenever the content is prefix + 'stoptest'. This will keep your code asynchronous, meaning the rest of your code will get a chance to execute once the interval has been started.
You can try this:
var a;
if(message.content == prefix + 'ckeck'){
a = setInterval(()=>{
if(message.content, == prefix + 'stop'){
clearInterval(a);
}
},5000);

Make countdown in background task on ios react-native

Right now I have this in my componentDidMount() methode:
setInterval(() => this.getTimeUntil(getTheTimeToAdd, count++), 1000);
It calls another methode for updating every second to count down. I have a problem when the user is counting down the application might close or the iPhone goes to sleep.
I have tried to use both:
react-native-mauron85-background-geolocation
and:
react-native-background-timer
To keep the count down running even when the iPhone sleeps og the application is closed. Any suggestions?
What you can do is create new Dates for each invocation to see the elapsed time. Like
setInterval(function() {
Date d = new Date();
if (this.oldDate && this.oldDate.getTime() + DELAY_THRESHHOLD < d.getTime()) {
...
this.oldDate = d;
}
else {
... //should anything be here?
}
},500);
Since you're already using react-native-background-timer,
you can use the setInterval from the BackgroundTimer instance.
Assuming that you've linked the project correctly, you should be able to do
// In the constructor or the state
this.state = {
initialCounter: 120000, //Equivalents to 2 minutes
active: false, // To show or hide a label
}
componentWillMount() {
this.setIntervalId = BackgroundTimer.setInterval(() => {
if (this.state.initialCounter === 0) {
this.setState({
initialCounter: this.state.initialCounter,
active: true,
})
} else {
this.setState({
initialCounter: this.state.initialCounter - 1000,
active: false,
})
}
}, 1000)
}
// Make sure to unmount the listener when component unmounts
componentWillUnmount() {
BackgroundTimer.clearInterval(this.setIntervalId)
}
// Helper func to reinitiate the counter
_onUpdate = () => {
this.setState({ initialCounter: 120000, active: false })
}
// And inside your render you may display the timer by formatting with moment()
The docs are a bit misleading, since setInterval and clearInterval works on IOS too. Tested on package 2.0.1
Try this code, it should catch up every time the app wakes up.
const duration = 15; // duration in minute
const alertAt = Date.now() + duration * 60 * 1000;
const interval = setInterval(() => {
const remaining = alertAt - Date.now();
if (remaining < 0) {
// time is up
clearInterval(interval);
return;
}
console.log(remaining);
}, 10)

Categories

Resources