I'm trying to make an automemes command, I can get it to send memes automatically, but when I try to disable it, it sends the Automemes disabled! command, but it still sends them. Here's the code:
const { SlashCommandBuilder } = require("#discordjs/builders");
const fetch = (...args) => import("node-fetch").then(({default: fetch}) => fetch(...args));
module.exports = {
data: new SlashCommandBuilder()
.setName("automemes")
.setDescription("Sends random memes every 5 minutes (from r/memes)")
.addBooleanOption(option =>
option.setName("enabled")
.setDescription("Set the automemes to on/off")
.setRequired(true)),
async execute(client, interaction, Discord) {
let isEnabled = interaction.options.get("enabled").value;
switch (isEnabled) {
case true: interaction.reply("Automemes enabled! " + ENV.CATKISS)
break;
case false: isEnabled = false;
break;
}
async function sendMemes() {
fetch("https://meme-api.herokuapp.com/gimme/memes")
.then(res => res.json())
.then(async json => {
const Embed = new Discord.MessageEmbed()
.setTitle(json.title)
.setImage(json.url)
if (Embed.title.length > 256) return;
await interaction.channel.send({embeds: [Embed]});
});
}
isEnabled? setInterval(() => sendMemes(), 10000) : interaction.reply("Automemes disabled! " + ENV.CATKISS);
}
}
I see what you're trying to accomplish but, as stated above by #Zsolt Meszaros, your interval is never getting cleared. That means that if that interval gets activated once, it will continue perpetually until your bot shuts down. Try declaring your interval as a constant so you can enable and disable it however you choose.
const myInterval = setInterval(sendMemes, 10000) //declaring interval
async fucntion startInterval() {
myInterval; //function to start interval
}
async function disableInterval() {
clearInterval(myInterval); //function to clear interval
interaction.reply(interaction.reply("Automemes disabled! " + ENV.CATKISS));
}
isEnabled? startInterval() : disableInterval(); //added starter and stopper functions
}
}
Related
i want to make so my codes only executed every 5 mins:
client.on('presenceUpdate', async (oldPresence, newPresence) => {
const guild = client.guilds.cache.get('707085819839512628')
const rolee = guild.roles.cache.get("707416808835252245")
if(oldPresence.member.user.bot || newPresence.member.user.bot) return;
let oldState = oldPresence.activities[0]?.state
let newState = newPresence.activities[0]?.state
if(oldState === newState) return;
if(newState.includes("https://discord.gg/indos")) {
newPresence.member.roles.add(rolee)
}
})
}, 60000);
example
Good afternoon, what need do you have to execute that code every X seconds? The "Client.on (Event)" function captures all the events related to your search at the moment they are executed...
The error for which the role is not granted may be because you do not have a specific Intent enabled, so make sure to activate them and rerun the code by eliminating the function that runs every X seconds.
client.on('presenceUpdate', async (oldPresence, newPresence) => {
const guild = client.guilds.cache.get('707085819839512628')
const rolee = guild.roles.cache.get("707416808835252245")
if(oldPresence.member.user.bot || newPresence.member.user.bot) return;
let oldState = oldPresence.activities[0]?.state
let newState = newPresence.activities[0]?.state
if(oldState === newState) return;
if(newState.includes("https://discord.gg/url")) {
newPresence.member.roles.add(rolee)
}
})
I am trying to make a bot that deletes a channel ('general') after 10 min after calling execcmd() function but if the msg is sent by someone having 'idofsomeuser' then it should cancel the previous timeout and start the timer again. But it's not working.
function execcmd(msg) {
let ID = msg.author.id
let chan = msg.guild.channels.cache.find(
(channel) => channel.name === 'general'
);
x = 'idofsomeuser'
if (ID == x) {
clearTimeout(x);
ID = setTimeout(() => {
chan.delete();
}, 600000); //10min
} else {
x = setTimeout(() => {
chan.delete();
}, 600000);
}
}
execcmd(msg); //msg is received by client and passed here
Create a closure function in which you store the timeout variable. Doing this enables you to store the timeout safely without having to create a global variable to store it in.
Inside the callback function first check if the id of the author matches. If it does, clear the timeout and set it to null. Then check if the timeout is equal to null and start the timer.
So now the timer will start when the function is called for the first time, and is restarted everytime the user with the id you're looking for is found.
function createClearChannel(channelName, authorId) {
let timeout = null;
const timeoutDuration = 1000 * 60 * 10;
return function (msg) {
const isAuthor = authorId === msg.author.id;
const channel = msg.guild.channels.cache.find(
channel => channel.name === channelName
);
if (isAuthor) {
clearTimeout(timeout);
timeout = null;
}
if (timeout === null) {
timeout = setTimeout(channel.delete, timoutDuration);
}
};
}
To set this up, first call the createClearChannel function and pass the arguments below. Store the result in execcmd and call that function when a message enters.
(You could always omit these parameters and simply hardcode the channel name and id of the user, but by doing this you make the function more flexible so it can be used for other cases as well.)
Important: The createClearChannel should only be called ONCE. So don't create a new execcmd when receives each message, but use the same one everytime.
const execcmd = createClearChannel('general', 'idofsomeuser');
execcmd(msg);
I'm basically creating a bot that does a loop, and I want it to end when you say *parar but i don't know how to make it.
Here is a bit of code to explain my problem
module.exports = {
name: 'epico',
execute(message, args, Discord, client){
//this is the loop
var interval = setInterval(function(){...}, 1000)
}
The loop starts when I put *epico and I want it to stop when user sends *parar
I was trying something like this:
client.on('message', message =>{
if(message.content.startsWith('parar')){clearInterval(interval)}
}
But this keeps working until I shut down the bot (I want it to just work 1 time)
Try something like the following: Basically what you want to do is save your interval to a variable that is accessible later on in order to stop your interval again.
const Discord = require("discord.js");
const client = new Discord.Client();
let interval;
client.on("message", async (message) => {
if (message.content.startsWith("*epico")) {
return (interval = setInterval(() => {
console.log("do something");
}, 1000));
}
if (message.content.startsWith("*parar")) {
clearInterval(interval);
return console.log("stopped interval");
}
});
client.login("your-token");
I assume your are using multiple commands in several different files. If that is the case I would simply save the interval to the client object in your *epico command file since you pass the client to your execute function anyways.
module.exports = {
name: "epico",
execute(message, args, Discord, client) {
return (client.interval = setInterval(() => {
console.log("do something");
}, 1000));
},
};
And then just clear the interval in your *parar command. Also don't forget to check if client.interval is even set ;)
module.exports = {
name: "parar",
execute(message, args, Discord, client) {
client.interval && clearInterval(client.interval);
return console.log("stopped interval");
},
};
maybe you have a typo???
client.on('message', message => {
if (message.content.startsWith('parar')){
clearInterval(interval)
}
});
So I'm messing around with creating a discord bot that repeatedly pings a user until they respond/say anything in the chat (annoying, right?). The amount of times to ping the user and the time between each ping can also be adjusted if necessary. However, I can't seem to find a way to detect if the pinged user actually says something in the chat, and a way to stop the loop.
The actual pinging part of the code is in this for loop:
const ping = async () => {
for(var i = 1; i <= pingAmount; i++){
//the wait() command
await new Promise(r => setTimeout(r, pingTime * 1000));
//the actual ping
message.channel.send(`hey <#${userID}> let\'s play minecraft`);
}
//sends a message once pinging is finished
message.channel.send("Pinging Complete.");
};
I've tried nesting the following code inside that loop, but I get no results.
client.on('message', message =>{
if(message.author == taggedUser) {
message.channel.send('User has replied. Stopping pings.')
return;
}
});
Any help is appreciated!
full code below:
module.exports = {
name: 'Ping',
description: "Pings specified user until they appear",
execute(message, args, Discord){
//initialize variables
const client = new Discord.Client();
const taggedUser = message.mentions.users.first();
const userID = message.mentions.users.first().id;
//splits the command
const slicedString = message.content.split(' ');
//grabs specific numbers from command as input
const pingAmount = slicedString.slice(4,5);
const pingTime = slicedString.slice(5);
//display confirmation info in chat
message.channel.send(`So, ${message.author.username}, you want to annoy ${taggedUser.username}? Alright then lol`);
message.channel.send(`btw ${taggedUser.username}\'s user ID is ${userID} lmao`);
message.channel.send(`amount of times to ping: ${pingAmount}`);
message.channel.send(`time between pings: ${pingTime} seconds`);
//checks to make sure pingTime isnt too short
if(pingTime < 5){
if(pingTime == 1){
message.channel.send(`1 second is too short!`);
return;
} else {
message.channel.send(`${pingTime} seconds is too short!`);
return;
}
}
//timer and loop using pingAmount and pingTime as inputs
const ping = async () => {
for(var i = 1; i <= pingAmount; i++){
//the wait() command
await new Promise(r => setTimeout(r, pingTime * 1000));
//the actual ping
message.channel.send(`hey <#${userID}> let\'s play minecraft`);
const pingedUsers = [taggedUser];
// doodle message
const msg = {author: {id:1}};
// message event
const onMessage = (message) => {
if (pingedUsers.indexOf(message.author.id) != -1) {
console.log("user replied");
}
}
onMessage(msg); // nothing
pingedUsers.push(msg.author.id); // push the author id
onMessage(msg); // he replied!
}
//sends a message once pinging is finished
message.channel.send("Pinging Complete.");
};
//runs the ping function
ping();
}
}
You should be comparing the author's snowflake (id) in this case.
You can put the pinged users in a list and see if the message author is in that list.
const pingedUsers = [];
// doodle message
const msg = {author: {id:1}};
// message event
const onMessage = (message) => {
if (pingedUsers.indexOf(message.author.id) != -1) {
console.log("user replied");
}
}
onMessage(msg); // nothing
pingedUsers.push(msg.author.id); // push the author id
onMessage(msg); // he replied!
In one of my bot's dialog steps I'am lanching some operations in a setTimeout() function.
The goal is to clear that TimeOut in an other step in some conditions.
async saveAdults(step) {
if (step.result) {
step.values.adults = step.result;
const convId = step.context.activity.conversation.id;
const format = "dddd DD MMMM YYYY";
// Send partial notification in case of a delay of 5 minutes
const data = {
checkIn: step.values.checkIn,
nights: step.values.nights,
adults: "",
children: ""
};
const timer = await sendPartialNotification(convId, data);
// step.values.timer = timer;
this.notificationProp.set(step.context, timer);
await this.conversationState.saveChanges(step.context);
}
return await step.next();
}
exports.sendPartialNotification = async (convId, data) => {
const interval = 300000;
const timer = setTimeout(() => {
notify(convId, this.id, data, true);
}, interval);
return timer;
};
async notifyClient(step) {
const timer = this.notificationProp.get(step.context);
clearTimeout(timer);
// …
}
Trying to store the TimeOut object in step.values.timer or in the conversation state throws this error that indicates that it is not possible to parse the Timeout Object ...
TypeError: Converting circular structure to JSON
As solution to this, I was thinking about storing the timer in Redis ..
Is there any ideas? Thanks.
Use state, props, or equivalent to pass the value from one step to the next. In my example code below, I include a middle step asking if the client would like to cancel. This is purely for displaying output for the solution.
Initiate the timer in a lead step.
async setTimer(step) {
if (step.result) {
const convId = step.context.activity.conversation.id;
const data = {
value1: someValue1,
value2: someValue2
};
const timer = await sendPartialNotification(convId, data);
this.notificationProp = { step: step.context, timer: timer };
await this.conversationState.saveChanges(step.context);
}
return await step.next();
}
Ask the client, in an intermediary step, if they would like to cancel the timer. I have the timer set for 10 secs.
If the user cancels, the timer is cleared.
If the client declines or fails to respond before 10 secs is up, the timer is unaffected and executes.
async askClient(step) {
const timer = this.notificationProp.timer;
if (timer._idleTimeout > 0) {
const message = MessageFactory.text(
'Cancel the timer?',
null,
'expectingInput'
);
return await step.prompt('confirmPrompt', message);
}
}
Lastly, output results and notify the client.
async notifyClient(step) {
const stepResult = step.result;
step.value = { timer: this.notificationProp.timer };
if (stepResult === true) {
console.log('TIMER PRE-CLEAR ', step.value.timer);
const timer = step.value.timer;
await clearTimeout(timer);
console.log('TIMER POST-CLEAR', timer);
step.context.sendActivity('Cancelling timer');
} else {
step.context.sendActivity('Timer not cancelled');
}
return await step.next();
}
Timer not cancelled and executes:
Timer cancelled:
Hope of help!