Intervals not turning on/off on command - javascript

I am working on a discord image command that sends a random image every 10 seconds. When I start the command, it works. It starts sending images every 10 seconds, but when I try to make it stop, it just won't.
What went wrong? How can I fix this?
Image.js:
var Scraper = require('images-scraper');
const google = new Scraper({
puppeteer: {
headless: true,
},
});
var myinterval;
module.exports = {
name: 'image',
description: 'Sends profile pics)',
async execute(kaoru, message, args, Discord) {
//prefix "?"
const image_query = args.join(' ');
const image_results = await google.scrape(image_query, 100);
if (!image_query)
return message.channel.send('**Please enter name of the image!**');
if (image_query) {
message.delete();
myinterval = setInterval(function () {
message.channel.send(
image_results[Math.floor(Math.random() * (100 - 1)) + 1].url,
);
}, 10000);
} else if (message.content === '?stopp') {
clearInterval(myinterval);
message.reply('pinging successfully stopped!');
}
}
};

You can't just simply check the message.content inside the execute method. It will never be ?stopp as execute only called when you send the image command (i.e. ?image search term).
What you can do instead is to set up a new message collector in the same channel and check if the incoming message is your stop command. In my example below I used createMessageCollector. I've also added plenty of comments.
const google = new Scraper({
puppeteer: {
headless: true,
},
});
module.exports = {
name: 'image',
description: 'Sends profile pics',
async execute(kaoru, message, args, Discord) {
const query = args.join(' ');
if (!query)
return message.channel.send('**Please enter name of the image!**');
const results = await google.scrape(query, 100);
// also send error message if there are no results
if (!results?.length) return message.channel.send('**No image found!**');
// you probably want to send the first image right away
// and not after 10 seconds
const image = results[Math.floor(Math.random() * results.length)];
message.channel.send(image.url);
let intervalID = setInterval(() => {
// you don't need to add +1 and take away 1, it's unnecessary
// also, use results.length instead of the hardcoded 100
// as if there are no 100 results, it will throw an error
const image = results[Math.floor(Math.random() * results.length)];
message.channel.send(image.url);
}, 10000);
// use this filter to only collect message if...
const filter = (m) =>
// the author is the same as the user who executed the command
m.author.id === message.author.id &&
// and the message content is ?stopp
m.content.toLowerCase() === '?stopp';
// set up a message collector
const collector = message.channel.createMessageCollector({
// use the filter above
filter,
// you only need to collect a single message
max: 1,
});
// it fires when a message is collected
collector.on('collect', (collected) => {
clearInterval(intervalID);
// message.reply won't work as message is no longer available
message.channel.send('Pinging successfully stopped!');
});
message.delete();
}
};

Related

Discord.JS Interval Won't Stop

My Discord bot is built using Discord.JS. I need to check whether or not a crypto transaction has reached 1 confirmation, so I have it set on an interval to check. This works, it checks using the coinbase API, and can fetch whether or not it's confirmed. The only issue is that the interval never stops, and it spams it infinitely, checking for confirmations.
Any suggestions on how I can get the interval to stop after reaching 1 confirmation?
const axios = require('axios');
const fetch = require('fetch')
const cheerio = require('cheerio');
const { clearInterval } = require("timers");
const client = new Discord.Client()
module.exports = {
name: 'check',
cooldown: 10,
description: 't',
async execute(msg) {
if(!msg.member) return
const args = msg.content.trim(1).split(/ +/g);
async function getConfirmation(){
return axios.get(`https://mempool.space/api/tx/${args[1]}`)
}
var confi = getConfirmation().then(function(response){
if(response.data.status.confirmed === true) {
return msg.lineReply(`This transaction has already reached 1 confirmation!`).catch(e=>console.log(e))
} else {
msg.reply(`I will notify you when this transaction reaches 1 confirmation.`).catch(e=>console.log(e))
setInterval(function(oner){
getConfirmation().then(function(response){
if(response.data.status.confirmed === true) {
const embed = new Discord.MessageEmbed()
.setTitle(`<a:check:849801483389501510> Transaction Confirmed`)
.setDescription(`Transaction ID:\n${response.data.txid}`)
.setColor(`GREEN`)
.setURL(`https://mempool.space/tx/${args[1]}`)
msg.channel.send(`${msg.author}`)
clearInterval(oner)
return msg.channel.send(embed).catch(error =>{
console.log(error)
})
}
}).catch(error =>{
return console.log(error)
})
}, 180000)
}
}).catch(error =>{
console.log(error)
return msg.channel.send(`Error. Probably means you didn't input an actual Transaction ID!`).catch(error =>{
console.log(error)
})
})
}
};
setInterval(fx,n) creates an interval. If you want to stop it, use clearInterval(intervalID).
If you want to execute it just once, use setTimeout(fx, n).
When you generate an interval, the function setInterval() returns an unique id.
For example:
let counter = 0;
const myInterval = setInterval(() => {
counter++;
if (counter == 10) {
clearInterval(myInterval)
}
}, 1000);
This will count up to 10 and then stops the interval.

using a message collector to set embed fields

I've got a message collector, and I'm trying to use the data collected in an embed as the title, description and embed colour. So far I've got it working so that each value returns an individual embed.
I believe this is due to the forEach in line 38, however everything else I've tried (deleting that line, returns nothing, changing to collected.messages(value) throws value is not defined, collected.messages throws its not a function) doesn't work
I'm not sure what to change it to, to get the result I'm after (which is the first answer setting the embed title, the second the embed description and the third the embed colour).
code is
module.exports = {
name: "embed",
description: "Sets up a reaction role message!",
async execute(client, message, args, Discord) {
const questions = [
'What is the message title?',
'What is the message description?',
'What is the embed colour?',
]
let counter = 0
const filter = (m) => !m.author.bot
const collector = new Discord.MessageCollector(message.channel, filter, {
max: questions.length,
time: 60000 * 5, // 5m
})
message.channel.send(questions[counter++])
collector.on('collect', (m) => {
if (counter < questions.length) {
m.channel.send(questions[counter++])
}
})
collector.on('end', (collected) => {
console.log(`Collected ${collected.size} messages`)
if (collected.size < questions.length) {
message.reply('The command has timed out')
return
}
let counter = 0
collected.forEach((value) => {
console.log(value.content)
let embed = new Discord.MessageEmbed()
.setTitle(value.content)
.setDescription(value.content)
.setColor(value.content)
let messageEmbed = message.channel.send(embed);
})
})
}
}```
Any help would be greatly appreciated
all I needed to do was set a variable to see where I was in the count, then use the variable to set the fields
let i = 0;
collected.forEach((value) => {
i++;
if (i === 1) embed.setTitle(value.content);
else if (i === 2) embed.setDescription(value.content);
else if (i === 3) embed.setColor(value.content);
})
let messageEmbed = message.channel.send(embed);```

How would I bypass a cetian role on a cooldown script discord.js/ command that restricts a certain command to a certain channel

This is the current code that I have, I would like to make it where if you have a certain role then you can bypass the cooldown, also if anyone knows how to make a command that restricts a certain command to a certain channel, instead of having this really long message.channel.id.
const Discord = require('discord.js');
const fetch = require('node-fetch');
const talkedRecently = new Set();
module.exports.run = async(client, message, args, queue, searcher, ) => {
if (talkedRecently.has(message.author.id)) {
message.channel.send("Wait 1 minute before getting typing this again. " +'<#'+ message.author.id + '>');
} else {
switch(args[0].toLowerCase()){
case 'neko':
if(message.channel.id === '739002385531404288'||
message.channel.id === '646849145289834506'||
message.channel.id === '785079847763574794'||
message.channel.id === '782891383361896469'||
message.channel.id === '784417039425994772'){
fetch('https://nekos.life/api/v2/img/lewd')
.then(res => res.json())
.then(json => {
let nekoEmbed = new Discord.MessageEmbed()
.setTitle('Lewd Nekos! (=^ļ½„Ļ‰ļ½„^=)')
.setImage(json.url)
message.channel.send(nekoEmbed)
})
}else{
return}}
talkedRecently.add(message.author.id);
setTimeout(() => {
talkedRecently.delete(message.author.id);
}, 60000);
}
}
module.exports.config = {
name: "hentai",
aliases: ['ht']
}
```
Answering Your First Question:
Simply check if the member has a certain role. If they do, construct your if statement so that it will not fire if they have that role
Make sure to use message.member when checking roles
if (talkedRecently.has(message.author.id) && !message.member.roles.cache.has('bypass role id here')) {
// Your cooldown message
}
Learn more about Roles#has
Answering your 2nd question:
You can have an array of channel id's then use includes to check if any of the id's in the array match the current channel id
const ids = ['id1', 'id2', 'id3', 'id4'] // And so on
if (ids.includes(message.channel.id)) {
// Your Code
}
Learn more about Array.prototype.includes

Reaction collector, display percentage (used for suggestions)

So I've basically set up my suggestion bot, it's a very basis one but I'm looking to add a cool feature that will collect the positive and negative reactions and display a percentage. If the positive votes are more it would display 100%, if it's 1 positive and 1 negative it would display 50% and if it's negative it's 1 negative and nothing else it would display -100%. It's very simple but I'm struggling to understand how to do it. Any ideas?
For handle reaction you can use method createReactionCollector, but the 1 one problem is: method not triggered on reaction remove. So you need use some interval to check message reaction.
time: 120000 - its time to await reaction of millisecond, change it to whats you need.
If bot go restart hadling reactions will stop...
client.on('message', message => {
if (message.content.startsWith('test')) {
let suggestion = message.content.substring(0, 4) //test length
let embed = new Discord.MessageEmbed();
embed.setAuthor(message.author.tag, message.author.displayAvatarURL({
dynamic:true,
format: "png"
}))
embed.setTitle('Suggestion')
embed.setColor('GOLD')
embed.setDescription(suggestion)
embed.setTimestamp()
message.channel.send(embed).then(msg => {
msg.react('šŸ‘').then(() => msg.react('šŸ‘Ž'))
const filter = (reaction, user) => {
return [`šŸ‘`, 'šŸ‘Ž'].includes(reaction.emoji.name)
};
let check = setInterval(handleReaction, 5000, message, msg, suggestion)
const collector = msg.createReactionCollector(filter, {
time: 120000,
});
collector.on('collect', (reaction, reactionCollector) => {
handleReaction(message, msg, suggestion)
});
collector.on('end', (reaction, reactionCollector) => {;
clearInterval(check)
});
})
}
})
function handleReaction (message, msg, suggestion) {
let embed = new Discord.MessageEmbed();
let positiveReaction = msg.reactions.cache.get('šŸ‘')
let negativeReaction = msg.reactions.cache.get('šŸ‘Ž')
let negativeCount = negativeReaction ? negativeReaction.count : 0
let positiveCount = positiveReaction ? positiveReaction.count : 0
embed.setAuthor(message.author.tag, message.author.displayAvatarURL({
dynamic:true,
format: "png"
}))
embed.setTitle('Suggestion')
embed.setColor('GOLD')
embed.setDescription(suggestion)
embed.addField('Votes', `šŸ‘ - ${(positiveCount / (positiveCount + negativeCount) * 100).toFixed(2)}%\nšŸ‘Ž - ${(negativeCount / (positiveCount + negativeCount) * 100).toFixed(2)}%`)
embed.setTimestamp()
msg.edit(embed)
}
I Don't Really Now If This Is What You Were Looking For Directly But Here Is My Old Code From An Old Bot I Had
const sayMessage = args.join(" ");
if (!args.length) {
return message.channel.send(`You didn't provide any text! <:angry:713422454688710717>`);
}
const sentMessage = await client.channels.get(< CHANNEL ID HERE >).send({embed: {
color: 700000,
author: {
name: client.user.username,
},
title: "Suggestion",
description: sayMessage,
footer: {
timestamp: new Date(),
icon_url: client.user.avatarURL,
text: "Suggestion"
}
}});
message.react('šŸ‘');
sentMessage.react('šŸ‘').then(sentMessage.react('šŸ‘Ž'))
message.channel.send("To vote for a suggestion, join my server. It should just be ..server!")
What It Does Is It Takes The Users Message And Puts It In An Embed And Reacts With Thumbs Up And Thumbs Down In The Channel Of Your Choice
But It May Need To Be Updated A Bit If Your Using V.12

Bot reacting to emojis

So, I got my code and it works just as I want it to. the message pops up changes everything, it's perfect.
Now I want to add so the bot knows when I react to its message and then does something else. What I mean is: bot sends a message with reacts, and whenever some user clicks the reaction something happens, but I have no idea how to do that.
I've tried many things like if (reaction.emoji.name === ':bomb:'), but multiple errors popped out and I didn't know how to fix that. Here's the code:
const Discord = require('discord.js');
const { prefix, token } = require('./config.json');
var lastbuffer;
lastbuffer = 0;
const client = new Discord.Client();
client.once('ready', () => {
console.log('Ready!');
});
client.on('message', message => {
if(message.content.startsWith(`${prefix}start`)){
message.delete()
setInterval(function(){
lastbuffer++;
const Buffer = new Discord.MessageEmbed()
.setColor('#8300FF')
.setTitle("**It's time to check buffers!**")
.setDescription("**It's been **" + "`" + lastbuffer + " Hour" + "`" + "** since last buffercheck, <#&675688526460878848>**." + " **Check now!**")
.setThumbnail('https://art.pixilart.com/88534e2f28b65a4.png')
.setFooter('WEEEEEWOOOOO')
.setTimestamp();
client.channels.cache.get("700296799482675230").send(Buffer).then(msg => {
msg.react('āœ…');
msg.react('šŸ’£');
msg.delete({timeout: 4000})
});
}, 5000)
}
});
client.login(token);
You are going to have to use a ReactionCollector using the createReactionCollector() method.
You can follow this guide to under ReactionCollectors better
You need to use a reaction collector.
client.channels.cache.get("700296799482675230").send(Buffer).then(async msg => {
// I'm using await here so the emojis react in the right order
await msg.react('āœ…');
await msg.react('šŸ’£');
msg.awaitReactions(
// Discord.js v12:
/* ({emoji}, user) => ['āœ…', 'šŸ’£'].includes(emoji.name) && user.id === message.author.id,
{max: 1, time: 4000, errors: ['time']} */
// Discord.js v13:
{
// only collect the emojis from the message author
filter: ({emoji}, user) => ['āœ…', 'šŸ’£'].includes(emoji.name) && user.id === message.author.id,
// stop collecting when 1 reaction has been collected or throw an error after 4 seconds
max: 1,
time: 4000,
errors: ['time']
}
)
.then(collected => {
const reaction = collected.first()
// do something
})
.catch(() => {
// I'm assuming you want to delete the message if the user didn't react in time
msg.delete()
})
What this code does:
Sends the embed (Buffer) to the channel with the id 700296799482675230
Reacts with the āœ… and then the šŸ’£ emojis on the message with the embed
Waits for a āœ… or šŸ’£ reaction from the author of the original message
If the user reacts within 4 seconds, runs the // do something part
If the user does not react within 4 seconds, deletes the message with the embed

Categories

Resources