How to fix this error when clicking the button? - node.js

How do I make a button to copy a message?
I tried to do like this:
const rowCopyPaste = new Discord.MessageActionRow().addComponents(new Discord.MessageButton()
.setLabel('Copy and Paste')
.setStyle('SECONDARY')
.setCustomId('copy_paste_button'));
embedMessage = await interaction.user.send({
embeds: [
new Discord.MessageEmbed()
.setColor('#2F3136')
.setTitle(interaction.user.username)
.setDescription('Test')
],
components: [rowCopyPaste]
});
const CollectorCopyPaste = interaction.channel.createMessageComponentCollector({
componentType: 'BUTTON',
time: 5000 * 60,
filter: i => i.user.id === interaction.user.id && i.customId === 'copy_paste_button',
});
CollectorCopyPaste.on('collect', async i => {
i.channel.send({
embeds: [new Discord.MessageEmbed()
.setDescription('Message to be copied')
]
});
});
But when the button is clicked, this error: This interaction failed
If you can help me I will be grateful

You also need to reply to button interaction, so in your case that would be
i.reply({
embeds: [
new Discord.MessageEmbed()
.setDescription('Message to be copied')
]
});
inside of the CollectorCopyPaste.on('collect', async i => {...}) instead of
i.channel.send({
embeds: [new Discord.MessageEmbed()
.setDescription('Message to be copied')
]
});

Related

Node.js and Discord.js error Message by Coding a DC Bot

I'm trying to program a Discord Bot but I get an error message every time
error Message:
Cannot read properties of undefined (reading 'FLAGS')
My Code:
const Discord = require("discord.js");
const config = require("./config.json");
const client = new Discord.Client({
restTimeOffset: 0,
allowedMentions: {
prase: [/* "rolea", "users" */],
repliedUser: true ,
},
pratials: [ "MESSAGE", "CHANNEL", "REACTION" ],
intents: [
Discord.Intents.FLAGS.GUILDS,
//Discord.Intents.FLAGS.GUILD_MEMBERS,
//Discord.Intents.FLAGS.GUILD_BANS,
//Discord.Intents.FLAGS.GUILD_EMOJIS_AND_STICKERS,
//Discord.Intents.FLAGS.GUILD_INTEGRATIONS,
//Discord.Intents.FLAGS.GUILD_WEBHOKKS,
//Discord.Intents.FLAGS.GUILD_INVITES,
//Discord.Intents.FLAGS.GUILD_VOUCE_STATES,
//Discord.Intents.FLAGS.GUILD_PRESENCES,
Discord.Intents.FLAGS.GUILD_MESSAGES,
//Discord.Intents.FLAGS.GUILD_MESSAG_REACTIONS,
//Discord.Intents.FLAGS.GUILD_TYPING,
],
presence: {
activity: {
name: "Cool Tut",
type: "WATCHING",
url: "https://www.twitch.tv/LeCharismo"
},
status: "dnd"
}
});
const bot = client;
client.login(config.token)
/*---------------------------------------------------------------------------*/
bot.once("ready", () =>{
console.log("The Bot is online");
});
bot.on("message", message => {
console.log("a new message was send");
console.log(message);
console.log(message.content);
})
Can anyone help me I've searched a lot on the internet but couldn't find anything
I don't know what else I can do and because I'm just starting out I don't really know the programming language and that makes it even more jarring for me

How can I set a button as disabled after the 15s has passed without any interaction?? Discord v14

const config = require('../../botconfig');
module.exports = {
name: 'invite',
description: 'Crimson and Server invites',
run: async (client, interaction, args) => {
try {
const inviteEmbed = new EmbedBuilder()
.setDescription('**__Invite • Support__**\n\n<a:Arrow:735141069033046106> Want to invite Crimson to your server? Feel free to click on the **"Invite"** button.\n\n<a:Arrow:735141069033046106> Need additional help with Crimson? Come join us in our humble abode by clicking on the **"Support"** button.')
.setColor('#EE1C25')
.setFooter({ text: `Command Requested by: ${interaction.user.tag}`, iconURL: interaction.user.displayAvatarURL() })
.setTimestamp()
.setThumbnail(client.user.displayAvatarURL());
let botInvite = new ButtonBuilder()
.setStyle(ButtonStyle.Link)
.setURL(`https://discord.com/`)
.setLabel('Invite');
let support = new ButtonBuilder()
.setStyle(ButtonStyle.Link)
.setURL('https://discord.gg/')
.setLabel('Support');
let del = new ButtonBuilder()
.setLabel(`Close`)
.setCustomId(`delete`)
.setEmoji(`❌`)
.setStyle(ButtonStyle.Danger);
const inviteMsg = await interaction.reply({ embeds: [inviteEmbed], components: [new ActionRowBuilder().addComponents(botInvite, support, del)], fetchReply: true });
const collector = inviteMsg.createMessageComponentCollector({ componentType: ComponentType.Button, time: 15000 });
collector.on('collect', async i => {
if (i.user.id === interaction.user.id) {
console.log(`${i.user.tag} clicked on the ${i.customId} button.`);
} else {
await i.reply({ content: `This button is not for you!`, ephemeral: true });
}
if (i.id === 'delete') {
inviteMsg.delete();
interaction.delete();
await i.reply.defer();
}
});
collector.on('end', collected => {
console.log(`Collected ${collected.size} interactions.`);
});
} catch (err) {
console.log(err);
return interaction.reply(`\`${err}\`.`);
}
}
};
I’ve been trying to mess around with it to see if i can but I’m running out of options to try 😂
I don’t know if I can do the same thing as the embed and set it as disabled through the components thing but not sure if that’s the correct way to do it.
You can do that easily by disabling the buttons once your collector ends.
collector.on('end', collected => {
botInvite.setDisabled(true);
support.setDisabled(true);
del.setDisabled(true);
// edit the message with the components disabled
inviteMsg.edit({embeds: [inviteEmbed], components: [new ActionRowBuilder().addComponents(botInvite, support, del)]});
console.log(`Collected ${collected.size} interactions.`);
});
If you have multiple buttons that need to be disabled, this can get a little annoying to add in your code.
What you can do is creating a row with your components, adding it to your message, and then looping through the buttons.
const row = new ActionRowBuilder().addComponents(botInvite, support, del);
const inviteMsg = await interaction.reply({ embeds: [inviteEmbed], components: [row], fetchReply: true });
// when your collector ends:
collector.on('end', collected => {
row.components.forEach(c => c.setDisabled(true));
// edit message
inviteMsg.edit({embeds: [inviteEmbed], components: [row]});
console.log(`Collected ${collected.size} interactions.`);
});

How to update multiple time a discord.js's interaction using setTimeout?

I'm currently working on a discordjs v13.6 bot in typescript who post an embed object with the date of day when using /day interaction command.
In a nutshell: I make /day and an embed is posted by my bot with the current date, weather, etc...
It's work fine and I added 3 buttons under the embed:
"reload" button: it will simply update the embed (with current weather forecast).
"previous" button: to update embed's interaction to the previous day.
"next" button: to update embed's interaction to the next day.
My code work as it should like this for the 3 buttons attached to my embed:
import { MessageActionRow, MessageButton } from 'discord.js';
// All customId property are formated like: `{method}#{date}`, ie: `reload#2022-03-10` is the button who'll reload the embed to date 03/10/2022.
export const createNavigationButtons = (date) => (
new MessageActionRow().addComponents([
new MessageButton()
.setCustomId(`prev#${date}`)
.setStyle('SECONDARY')
.setEmoji("946186554517389332"),
new MessageButton()
.setCustomId(`reload#${date}`)
.setStyle('SUCCESS')
.setEmoji("946190012154794055"),
new MessageButton()
.setCustomId(`next#${date}`)
.setStyle('SECONDARY')
.setEmoji("946186699745161296")
])
)
For the logic:
import { ButtonInteraction } from 'discord.js';
import { createEmbed } from "../utils/embed";
import { createNavigationButtons } from "../utils/buttons";
import * as year from "../../resources/year.json";
import * as moment from 'moment';
import { setTimeout as wait } from 'node:timers/promises';
// This function is called in the on('interactionCreate') event, when interaction.isButton() is true
export const button = async (interaction: ButtonInteraction): Promise<void> => {
const [action, date]: string[] = interaction.customId?.split('#');
await interaction.deferUpdate();
await wait(1000);
const newDate: string = {
prev: moment(date, "YYYY-MM-DD").subtract(1, "day").format("YYYY-MM-DD"),
next: moment(date, "YYYY-MM-DD").add( 1, "day").format("YYYY-MM-DD"),
reload: date
}[action];
await interaction.editReply({
embeds: [await createEmbed(year[newDate])],
components: [createNavigationButtons(newDate)]
});
}
It works just as I wished. BUT, everybody can use theses buttons (and I don't want to send /day's answer as ephemeral, I want everybody to see the response). So, if we use /day 2022-03-10 the embed for March 10, 2022. but if the author or someone else (I don't mind) use the button, the embed will be updated with another date (and that's fine by me !). But I want to roll back my embed to the original date few seconds / minutes after the button is pressed.
I tried somes primitive way like the setTimeout like this:
export const button = async (interaction: ButtonInteraction, config: any): Promise<void> => {
// In this test, button's customId are formated like {method}#{date}#{date's origin} (where 'origin' is the original requested's date, optional)
const [action, date, origin]: string[] = interaction.customId?.split('#');
await interaction.deferUpdate();
await wait(1000);
const newDate: string = {
prev: moment(date, "YYYY-MM-DD").subtract(1, "day").format("YYYY-MM-DD"),
next: moment(date, "YYYY-MM-DD").add( 1, "day").format("YYYY-MM-DD"),
reload: date
}[action];
// Here is my setTimeout who is supposed to recursively recall this function with a "reload" and the original date
setTimeout(async () => {
interaction.customId = `reload#${origin ?? date}`;
console.log(interaction.customId);
await button(interaction, config);
}, 5000);
await interaction.editReply({
embeds: [await createEmbed(year[newDate])],
components: [createNavigationButtons(newDate)]
});
};
When I press my buttons with this, it's correctly updated but 5sec (setTimeout's value) after it end up with an error saying:
reload#2022-03-10
/home/toto/tata/node_modules/discord.js/src/structures/interfaces/InteractionResponses.js:180
if (this.deferred || this.replied) throw new Error('INTERACTION_ALREADY_REPLIED');
^
Error [INTERACTION_ALREADY_REPLIED]: The reply to this interaction has already been sent or deferred.
at ButtonInteraction.deferUpdate (/home/toto/tata/node_modules/discord.js/src/structures/interfaces/InteractionResponses.js:180:46)
at button (/home/toto/tata/src/services/button.ts:12:21)
at Timeout._onTimeout (/home/toto/tata/src/services/button.ts:28:21)
at listOnTimeout (node:internal/timers:559:17)
at processTimers (node:internal/timers:502:7) {
[Symbol(code)]: 'INTERACTION_ALREADY_REPLIED'
}
I understand that it seems I can't reupdate my interaction with the same token like that, so how shoul I achieve my goal ? may be the setTimeout isn't a propper solution (but it was quite simple to implemant so I tried it first). Any Ideas ?
I successfully reached my objective like this:
// All customId property are formated like: `{method}#{date}#{origin}`, ie: `reload#2022-03-10` is the button who'll reload the embed to date 03/10/2022.
export const createNavigationButtons = (date: string, mode?: boolean) => (
new MessageActionRow().addComponents([
new MessageButton()
.setCustomId(`prev#${date}${!mode ? `#${date}` : ''}`)
.setStyle('SECONDARY')
.setEmoji("946186554517389332"),
new MessageButton()
.setCustomId(`reload#${date}`)
.setStyle('SUCCESS')
.setEmoji("946190012154794055"),
new MessageButton()
.setCustomId(`remind#${date}`)
.setStyle('PRIMARY')
.setEmoji("946192601806155806"),
new MessageButton()
.setCustomId(`next#${date}${!mode ? `#${date}` : ''}`)
.setStyle('SECONDARY')
.setEmoji("946186699745161296")
])
);
export const createButtons = (date, mode?: boolean) => ({
components: [ createNavigationButtons(date, mode) ]
});
export const button = async (interaction: ButtonInteraction, config: any): Promise<void> => {
const [action, date, origin]: string[] = interaction.customId?.split('#');
const newDate: string = {
prev: moment(date, "YYYY-MM-DD").subtract(1, "day").format("YYYY-MM-DD"),
next: moment(date, "YYYY-MM-DD").add( 1, "day").format("YYYY-MM-DD"),
reload: date
}[action];
origin && !interaction.deferred && setTimeout(async () => {
await interaction.editReply({
embeds: [await createEmbed(year[origin], config.server)],
...createButtons(origin)
});
}, 120000);
!interaction.deferred && await interaction.deferUpdate();
await interaction.editReply({
embeds: [await createEmbed(year[newDate], config.server)],
...createButtons(newDate, true)
});
};
In a nutshell, when I first create the embed I place a #{origin} (who's a date), and when I navigate and update my embed with my buttons, I don't send origin (only {action}#{date}, not {action}#{date}#origin by passing true to my createButtons method.

Discord.js maximum number of webhooks error

Have this slash command code and turned it into webhook. It worked when I used it once but it stopped working after that. I got this error DiscordAPIError: Maximum number of webhooks reached (10). Does anyone have any idea on how to fix this?
Code:
run: async (client, interaction, args) => {
if(!interaction.member.permissions.has('MANAGE_CHANNELS')) {
return interaction.followUp({content: 'You don\'t have the required permission!', ephemeral: true})
}
const [subcommand] = args;
const embedevent = new MessageEmbed()
if(subcommand === 'create'){
const eventname = args[1]
const prize = args[2]
const sponsor = args[3]
embedevent.setDescription(`__**Event**__ <a:w_right_arrow:945334672987127869> ${eventname}\n__**Prize**__ <a:w_right_arrow:945334672987127869> ${prize}\n__**Donor**__ <a:w_right_arrow:945334672987127869> ${sponsor}`)
embedevent.setFooter(`${interaction.guild.name}`, `${interaction.guild.iconURL({ format: 'png', dynamic: true })}`)
embedevent.setTimestamp()
}
await interaction.followUp({content: `Event started!`}).then(msg => {
setTimeout(() => {
msg.delete()
}, 5000)
})
interaction.channel.createWebhook(interaction.user.username, {
avatar: interaction.user.displayAvatarURL({dynamic: true})
}).then(webhook => {
webhook.send({content: `<#&821578337075200000>`, embeds: [embedevent]})
})
}
}
You cannot fix that error, discord limits webhooks per channel (10 webhooks per channel).
However, if you don't want your code to return an error you can just chock that code into a try catch or add a .catch
Here is an example of how to handle the error:
try {
interaction.channel.createWebhook(interaction.user.username, {
avatar: interaction.user.displayAvatarURL({dynamic: true})
}).then(webhook => {
webhook.send({content: `<#&821578337075200000>`, embeds: [embedevent]})
})
} catch(e) {
return // do here something if there is an error
}

How to export and import functions for telegrams bot?

I'm create a bot telegram with two buttons. On each button I want to hang the action. I want to transfer these actions to another file. How can I do that?
const Telegraf = require("telegraf");
const session = require("telegraf/session");
const Composer = require('telegraf/composer');
const bot = new Telegraf('Token')
const first = require('./command/first');
bot.command('start', (ctx) => {
const markdown = `
Hi! Click on the button 1 or 2!`;
ctx.telegram.sendMessage(ctx.message.chat.id, markdown, {
parse_mode: 'Markdown',
reply_markup: {
keyboard: [
['1', '2'],
],
resize_keyboard: true
},
disable_notification: false
});
});
bot.use(session());
bot.use(Telegraf.log())
bot.on('1', first.hears()) ///myfunction command
bot.startPolling();
console.log(Telegraf.log());
and file ./command/first
module.exports = {
hears: function () {
console.log("debug 1");
bot.action('1', (ctx) => {
const markdown = ` Type some text...`;
ctx.telegram.sendMessage(ctx.message.chat.id, markdown, {
parse_mode: 'Markdown',
reply_markup: {
keyboard: [
['🔙 Back'],
],
resize_keyboard: true
},
disable_notification: false
});
})
}
};
but nothing works. When starting the bot writes immediately debug 1
And nothing.. Help me please!
Firstly Change:
bot.on('1', first.hears()) // on is for events
to
bot.hears('1', first.hears()) // hears listens for the specified string from bot
Then rewrite the module in /command/first to:
module.exports = {
hears: function (ctx) {
console.log("debug 1");
// Added markdown formatting
const message = `*Bold text* and _italic text_`;
ctx.telegram.sendMessage(ctx.message.chat.id, message, {
parse_mode: 'Markdown',
reply_markup: JSON.stringify({ // You also missed JSON.stringify()
keyboard: [
['🔙 Back'],
],
resize_keyboard: true
}),
disable_notification: false
});
}
}
This should work. I hope this helps.

Resources