Detect a reaction Discord.js - discord

Help! I'm developing an RPG bot which it needs to detect reactions. But there are two problems:
It only reacts 1 emoji instead of 4
It tries to detect the reaction only once
Here's my code (There are some things which are in portuguese):
if (message.channel.name === "rpg") {
const HP = 0;
const atk = 0;
const def = 0;
const emoji = message.guild.emojis.cache.find(emoji => emoji.name === 'SurvivalManos');
message.react(emoji);
const random = Math.ceil(Math.random() * (3 - 0) + 0);
const rpgembed = new Discord.MessageEmbed()
.setColor('#ffd15e')
.setTitle('RPG')
.setThumbnail('https://cdn.discordapp.com/attachments/732552438283894824/733256439602086019/New_Project_3.png')
.setTimestamp()
.setFooter('Feito por R0dr1G02M4R5 com <3; Desenhos de Kunyk');
if (random == 1) {
const HP = 50;
const atk = 10;
const def = 5;
rpgembed.setDescription('Um slime verde selvagem apareceu!').setImage('https://media.discordapp.net/attachments/696805791319064618/733967904890159134/SPOILER_New_Piskel.png?width=585&height=585').addFields(
{ name: 'HP', value: HP, inline: true },
{ name: 'Atk', value: atk, inline: true },
{ name: 'Def', value: def, inline: true }
);
} else if (random == 2) {
const HP = 100;
const atk = 5;
const def = 10;
rpgembed.setDescription('Um slime vermelho selvagem apareceu!').setImage('https://media.discordapp.net/attachments/696805791319064618/733968480390610955/SPOILER_New_Piskel_1.png?width=585&height=585').addFields(
{ name: 'HP', value: HP, inline: true },
{ name: 'Atk', value: atk, inline: true },
{ name: 'Def', value: def, inline: true }
);
}
else if (random == 3) {
const HP = 25;
const atk = 10;
const def = 10;
rpgembed.setDescription('Um feiticeiro fantasma selvagem apareceu!').setImage('https://media.discordapp.net/attachments/696805791319064618/733968482122727494/SPOILER_New_Piskel_2.png?width=585&height=585').addFields(
{ name: 'HP', value: HP, inline: true },
{ name: 'Atk', value: atk, inline: true },
{ name: 'Def', value: def, inline: true }
);
}
message.channel.send(rpgembed).then(function(message) {
message.react('⚔').then(() => message.react('🛡')).then(() => message.react('🍖')).then(() => message.react('🏃'))
const filter = (reaction, user) => {
return ['⚔', '🛡', '🍖', '🏃'].includes(reaction.emoji.name) && user.id === message.author.id;
};
message.awaitReactions(filter, { max: 1, time: 60000, errors: ['time'] })
.then(collected => {
const reaction = collected.first();
console.log('he logs');
if (Discord.MessageReaction.emoji === '⚔') {
message.channel.send("He atacs");
console.log("He ataks");
act = false;
} else if (Discord.MessageReaction.emoji === '🛡') {
message.channel.send("He defens");
console.log("He defens");
act = false;
} else if (Discord.MessageReaction.emoji === '🍖') {
message.channel.send("He itz");
console.log("He itz");
act = false;
} else if (Discord.MessageReaction.emoji === '🏃') {
message.channel.send("He rans");
console.log("He rans");
act = false;
}
})
});
In the console.log('He logs') after message.awaitReactions(filter, { max: 1, time: 60000, errors: ['time'] }) .then(collected => { const reaction = collected.first(); if you check the console, there's only one "He logs"
Plz help. Thanks

It only reacts 1 emoji instead of 4
message.react('⚔');
message.react('🛡');
message.react('🍖');
message.react('🏃');
It tries to detect the reaction only once
message.awaitReactions(filter, { max: 1, time: 60000, errors: ['time'] })
Passing max: 1 makes it wait for only 1 reaction. More info here and here

Related

Planning the calculation of progress percentages in the Progress Bar

I need to plan and implement the calculation of progress bar based on this object that i received from the backend :
due: {
coref: 1,
extract: 4,
keyconcepts: 1,
question-text-cmas: 1,
question-text-fibq: 1,
question-text-mcq: 1,
rank: 1,
summary-1: 1,
text: 1,
topic: 1
}
I already build the progress bar component, now i need to think of this impelementation.
This is the function that i started to implement :
const propertiesRef = useRef({
extract: { curr: 0, max: 15 },
keyconcepts: { curr: 0, max: 20 },
question: {
cmas: { curr: 0, max: 10 },
fibq: { crr: 0, max: 10 },
mcq: { curr: 0, max: 10 },
},
rank: { curr: 0, max: 5 },
summary: { curr: 0, max: 15 },
text: { curr: 0, max: 10 },
topic: { curr: 0, max: 5 },
allOver: 0,
}); // In this object i'll save the progress
const getProcess = async () => {
let conditionLoop = true;
do {
setTimeout(async () => {
await axios
.get(`Route`, { withCredentials: true }) //From here i get the due object
.then((res) => {
conditionLoop = res.data.due;
if (res?.data?.due) {
for (let key in propertiesRef.current) {
if (res.data.due.hasOwn(key)) {
console.log(key, res.data.due[key]);
}
if (res.data.due.hasOwn("question-text-cmas")) {
console.log(res.data.due);
}
if (res.data.due.hasOwn("question-text-fibq")) {
console.log(res.data.due);
}
if (res.data.due.hasOwn("question-text-mcq")) {
console.log(res.data.due);
}
}
} else {
propertiesRef.current.allOver = 1;
conditionloop=false;
}
console.log(propertiesRef.current);
});
}, 2000);
} while (conditionLoop);
};
This happens in my app while i generate some unit summary.
Important to say : When each property is done, it removed by the backend from the due object, and each property that have value 1 means that it still pending, when it's more than 1 it means that it is in progress.
Thanks in advance.
A simple minded solution would be to just remember the first due count, and compare it to the current one.
Something like this:
let totalProgress = useRef(null);
let progressFraction = useRef(null);
const progressFetchInterval = 2000;
const getProgress = async () => {
setTimeout(async () => {
await axios.get(`Route`, { withCredentials: true }).then((res) => {
fraction = getFraction(res);
getFraction.current = fraction;
if (fraction === 1) totalProgress.current = null;
else getProgress();
});
}, progressFetchInterval);
};
const getFraction = async (res) => {
// ... request validation code ...
const dueMap = res.data.due;
const dueCount = Object.values(dueMap).reduce((a, b) => a + b, 0);
if (totalProgress.current === null) totalProgress.current = dueCount;
return (totalProgress.current - dueCount) / totalProgress.current;
};

Args cannot read proprety of undefined ("0")

So basically I have that command that use modals to create giveaways but there is one line bothering me. It is right under the run: async line. I have been trying to search the discord documentation about a change that happened from v13 to v14 (yes this code uses discord.js v1.14.5) but I have found nothing. The error is the following: <rejected> TypeError: Cannot read properties of undefined (reading '0'). If you need anymore code, ask me (like the sub files or the rest of the code). Thanks in advance!
Code:
const {
ChannelType,
ButtonBuilder,
ActionRowBuilder,
ComponentType,
TextInputStyle,
TextInputBuilder,
ModalBuilder,
ButtonStyle,
ApplicationCommandOptionType,
} = require("discord.js");
const ems = require("enhanced-ms");
const start = require("./sub/start");
const pause = require("./sub/pause");
const resume = require("./sub/resume");
const end = require("./sub/end");
const reroll = require("./sub/reroll");
const list = require("./sub/list");
const edit = require("./sub/edit");
module.exports = {
name: "giveaway",
description: "Start a giveaway!",
type: 1,
id: "Giveaway",
options: [
{
name: "start",
description: "start a giveaway",
type: ApplicationCommandOptionType.Subcommand,
options: [
{
name: "channel",
description: "the channel to start the giveaway in",
type: ApplicationCommandOptionType.Channel,
channelTypes: [ChannelType.GuildText],
required: true,
},
],
},
{
name: "pause",
description: "pause a giveaway",
type: ApplicationCommandOptionType.Subcommand,
options: [
{
name: "message_id",
description: "the message id of the giveaway",
type: ApplicationCommandOptionType.String,
required: true,
},
],
},
{
name: "resume",
description: "resume a paused giveaway",
type: ApplicationCommandOptionType.Subcommand,
options: [
{
name: "message_id",
description: "the message id of the giveaway",
type: ApplicationCommandOptionType.String,
required: true,
},
],
},
{
name: "end",
description: "end a giveaway",
type: ApplicationCommandOptionType.Subcommand,
options: [
{
name: "message_id",
description: "the message id of the giveaway",
type: ApplicationCommandOptionType.String,
required: true,
},
],
},
{
name: "reroll",
description: "reroll a giveaway",
type: ApplicationCommandOptionType.Subcommand,
options: [
{
name: "message_id",
description: "the message id of the giveaway",
type: ApplicationCommandOptionType.String,
required: true,
},
],
},
{
name: "list",
description: "list all giveaways",
type: ApplicationCommandOptionType.Subcommand,
},
{
name: "edit",
description: "edit a giveaway",
type: ApplicationCommandOptionType.Subcommand,
options: [
{
name: "message_id",
description: "the message id of the giveaway",
type: ApplicationCommandOptionType.String,
required: true,
},
{
name: "add_duration",
description: "the number of minutes to add to the giveaway duration",
type: ApplicationCommandOptionType.Integer,
required: false,
},
{
name: "new_prize",
description: "the new prize",
type: ApplicationCommandOptionType.String,
required: false,
},
{
name: "new_winners",
description: "the new number of winners",
type: ApplicationCommandOptionType.Integer,
required: false,
},
],
},
],
permissions: {
DEFAULT_MEMBER_PERMISSIONS: "ManageMessages" // User permissions needed
},
/**
* #param {ChatInputCommandInteraction} interaction
*/
run: async (client, interaction, config, db, args, message) => {
const sub = args[0]?.toLowerCase();
//line above
let response;
//
if (sub === "start") {
if (!args[1]) return message.safeReply("Incorrect usage! Please provide a channel to start the giveaway in");
const match = message.guild.findMatchingChannels(args[1]);
if (!match.length) return message.safeReply(`No channel found matching ${args[1]}`);
return await runModalSetup(message, match[0]);
}
//
else if (sub === "pause") {
const messageId = args[1];
response = await pause(message.member, messageId);
}
//
else if (sub === "resume") {
const messageId = args[1];
response = await resume(message.member, messageId);
}
//
else if (sub === "end") {
const messageId = args[1];
response = await end(message.member, messageId);
}
//
else if (sub === "reroll") {
const messageId = args[1];
response = await reroll(message.member, messageId);
}
//
else if (sub === "list") {
response = await list(message.member);
}
//
else if (sub === "edit") {
const messageId = args[1];
if (!messageId) return message.safeReply("Incorrect usage! Please provide a message id");
return await runModalEdit(message, messageId);
}
//
else response = "Not a valid sub command";
await message.safeReply(response);
},
async interactionRun(interaction) {
const sub = interaction.options.getSubcommand();
let response;
//
if (sub === "start") {
const channel = interaction.options.getChannel("channel");
return await runModalSetup(interaction, channel);
}
//
else if (sub === "pause") {
const messageId = interaction.options.getString("message_id");
response = await pause(interaction.member, messageId);
}
//
else if (sub === "resume") {
const messageId = interaction.options.getString("message_id");
response = await resume(interaction.member, messageId);
}
//
else if (sub === "end") {
const messageId = interaction.options.getString("message_id");
response = await end(interaction.member, messageId);
}
//
else if (sub === "reroll") {
const messageId = interaction.options.getString("message_id");
response = await reroll(interaction.member, messageId);
}
//
else if (sub === "list") {
response = await list(interaction.member);
}
//
else if (sub === "edit") {
const messageId = interaction.options.getString("message_id");
const addDurationMs = ems(interaction.options.getInteger("add_duration"));
if (!addDurationMs) {
return interaction.followUp("Not a valid duration");
}
const newPrize = interaction.options.getString("new_prize");
const newWinnerCount = interaction.options.getInteger("new_winners");
response = await edit(interaction.member, messageId, addDurationMs, newPrize, newWinnerCount);
}
//
else response = "Invalid subcommand";
await interaction.followUp(response);
},
};
//...
Command handler:
const client = require("../index");
const { PermissionsBitField, Routes, REST, User } = require('discord.js');
const fs = require("fs");
const colors = require("colors");
module.exports = (client, config) => {
console.log("0------------------| Application commands Handler:".blue);
let commands = [];
// Slash commands handler:
fs.readdirSync('./commands/slash/').forEach((dir) => {
console.log('[!] Started loading slash commands...'.yellow);
const SlashCommands = fs.readdirSync(`./commands/slash/${dir}`).filter((file) => file.endsWith('.js'));
for (let file of SlashCommands) {
let pull = require(`../commands/slash/${dir}/${file}`);
if (pull.name, pull.description, pull.type == 1) {
client.slash_commands.set(pull.name, pull);
console.log(`[HANDLER - SLASH] Loaded a file: ${pull.name} (#${client.slash_commands.size})`.brightGreen);
commands.push({
name: pull.name,
description: pull.description,
type: pull.type || 1,
options: pull.options ? pull.options : null,
default_permission: pull.permissions.DEFAULT_PERMISSIONS ? pull.permissions.DEFAULT_PERMISSIONS : null,
default_member_permissions: pull.permissions.DEFAULT_MEMBER_PERMISSIONS ? PermissionsBitField.resolve(pull.permissions.DEFAULT_MEMBER_PERMISSIONS).toString() : null
});
} else {
console.log(`[HANDLER - SLASH] Couldn't load the file ${file}, missing module name value, description, or type isn't 1.`.red)
continue;
};
};
});
// User commands handler:
fs.readdirSync('./commands/user/').forEach((dir) => {
console.log('[!] Started loading user commands...'.yellow);
const UserCommands = fs.readdirSync(`./commands/user/${dir}`).filter((file) => file.endsWith('.js'));
for (let file of UserCommands) {
let pull = require(`../commands/user/${dir}/${file}`);
if (pull.name, pull.type == 2) {
client.user_commands.set(pull.name, pull);
console.log(`[HANDLER - USER] Loaded a file: ${pull.name} (#${client.user_commands.size})`.brightGreen);
commands.push({
name: pull.name,
type: pull.type || 2,
});
} else {
console.log(`[HANDLER - USER] Couldn't load the file ${file}, missing module name value or type isn't 2.`.red)
continue;
};
};
});
// Message commands handler:
fs.readdirSync('./commands/message/').forEach((dir) => {
console.log('[!] Started loading message commands...'.yellow);
const UserCommands = fs.readdirSync(`./commands/message/${dir}`).filter((file) => file.endsWith('.js'));
for (let file of UserCommands) {
let pull = require(`../commands/message/${dir}/${file}`);
if (pull.name, pull.type == 3) {
client.message_commands.set(pull.name, pull);
console.log(`[HANDLER - MESSAGE] Loaded a file: ${pull.name} (#${client.user_commands.size})`.brightGreen);
commands.push({
name: pull.name,
type: pull.type || 3,
});
} else {
console.log(`[HANDLER - MESSAGE] Couldn't load the file ${file}, missing module name value or type isn't 2.`.red)
continue;
};
};
});
// Registering all the application commands:
if (!config.Client.ID) {
console.log("[CRASH] You need to provide your bot ID in config.js!".red + "\n");
return process.exit();
};
const rest = new REST({ version: '10' }).setToken(config.Client.TOKEN || process.env.TOKEN);
(async () => {
console.log('[HANDLER] Started registering all the application commands.'.yellow);
try {
await rest.put(
Routes.applicationCommands(config.Client.ID),
{ body: commands }
);
console.log('[HANDLER] Successfully registered all the application commands.'.brightGreen);
} catch (err) {
console.log(err);
}
})();
};
InteractionCreate.js:
const { EmbedBuilder } = require("discord.js");
const client = require("../../index");
const config = require("../../config/config.js");
const { QuickDB } = require("quick.db");
const db = new QuickDB();
module.exports = {
name: "interactionCreate"
};
client.on('interactionCreate', async (interaction) => {
if (interaction.isChatInputCommand()) {
const command = client.slash_commands.get(interaction.commandName);
if (!command) return;
try {
command.run(client, interaction, config, db);
} catch (e) {
console.error(e)
};
};
if (interaction.isUserContextMenuCommand()) { // User:
const command = client.user_commands.get(interaction.commandName);
if (!command) return;
try {
command.run(client, interaction, config, db);
} catch (e) {
console.error(e)
};
};
if (interaction.isMessageContextMenuCommand()) { // Message:
const command = client.message_commands.get(interaction.commandName);
if (!command) return;
try {
command.run(client, interaction, config, db);
} catch (e) {
console.error(e)
};
};
if (interaction.isModalSubmit()) { // Modals:
const modal = client.modals.get(interaction.customId);
if (!modal) return interaction.reply({
embeds: [
new EmbedBuilder()
.setDescription('Something went wrong... Probably the Modal ID is not defined in the modals handler.')
.setColor('Red')
],
ephemeral: true
});
try {
modal.run(client, interaction, config, db);
} catch (e) {
console.error(e)
};
}
});

Select Menu message collector returns interaction has already been acknowledged when ran twice

The command that I'm trying to do this with is a help command with multiple sub-menu's like the FM-bot.
Whenever I run my command twice, it always returns Interaction has already been acknowledged..
You have to select one of the sub-menus though first for this to happen, otherwise the command loads fine.
Here's the code for the command:
const categories = [
{
label: `AniList Commands`,
description: `Show's you the AniList commands`,
id: 'anilist',
commandFolder: "anilist",
color: '#0099ff',
filterFn: (x) => x.endsWith(".js")
},
{
label: `General Commands`,
description: `Show's you the general commands`,
id: 'general',
commandFolder: "general",
color: '#ea9e07',
filterFn: (x) => x.endsWith(".js") && x != "help.js" && x != "birthday.js"
},
{
label: `Hentai Commands`,
description: `Show's you the Hentai commands`,
id: 'hentai',
commandFolder: "hentai",
color: '#fb2347',
filterFn: (x) => x.endsWith(".js")
}
]
function parseCategory(category) {
const commandFiles = fs.readdirSync(path.join(__dirname, `../${category.commandFolder}`)).filter(category.filterFn);
const commands = [];
for (const file of commandFiles) {
const command = require(`../${category.commandFolder}/${file}`);
if (command.name && command.description && command.type) {
commands.push({ name: command.name, description: command.description });
}
}
return commands;
}
const row = new MessageActionRow()
.addComponents(
new MessageSelectMenu()
.setCustomId(`1`)
.setPlaceholder(`Select one of the available categories`)
.addOptions(categories.map(category => {
return {
label: category.label,
description: category.description,
value: category.id
}
}))
)
const embed = new MessageEmbed()
.setTitle(`Here are all the bot commands`)
.setDescription(`Select one of the command categories using the dropdown menu.`)
await message.channel.send({ content: ` `, embeds: [embed], components: [row] })
let categoryContents = {};
for (const category of categories) {
categoryContents[category.id] = parseCategory(category)
}
let categoryEmbeds = {};
for (const category of categories) {
const commands = categoryContents[category.id];
if (commands.length > 0) {
const embed = new MessageEmbed()
.setTitle(`${category.label}`)
.setColor(category.color)
.setFooter(`${commands.length} commands in this category`)
.setTimestamp();
for (const command of commands) {
embed.addField(`${process.env.prefix}${command.name}`, `${command.description}`);
}
categoryEmbeds[category.id] = embed;
}
}
let collector = message.channel.createMessageComponentCollector({
componentType: `SELECT_MENU`,
idle: 10_000,
});
collector.on(`collect`, async (collected) => {
let value = collected.values[0];
await collected.update({ embeds: [categoryEmbeds[value]] });
})
Any help would be appreciated!

Change variable in array of objects in state React

What I wanted to do is whenever I click button to change variable in states array of objects. I did it this way, but is there any easier way to do it?
completeItem = (id) => {
const item = this.state.items.filter(el =>{
return el.id === id;
});
item[0].isCompleted = true;
const state = this.state.items;
const arr = this.state.items.filter(el =>{
if(el.id === id){
state[id-1] = item[0];
}
return state;
});
this.setState({
items:[...arr],
});
}
Try this solution...
completeItem = (id) => {
const items = this.state.items.filter(el =>{
if(el.id === id){
el.isCompleted = true;
}
return el;
});
this.setState({
items : [...items],
});
}
If the solution is helpful please let me know!
May compromise a little performance, but it is more readable.
completeItem = (id) => {
const newList = [...this.state.items]
const index = newList.findIndex(el => el.id === id);
newList[index].isCompleted = true
this.setState({ items: newList });
}
Here is how you can do it:
const items = [{
id: 1,
name: "one",
isCompleted: false
},
{
id: 2,
name: "two",
isCompleted: false
},
{
id: 3,
name: "three",
isCompleted: false
},
{
id: 4,
name: "four",
isCompleted: false
}
]
completeItem = (id) => {
const result = items.map(e => {
if (e.id === id) {
return { ...e,
isCompleted: true
}
} else {
return e;
}
});
console.log(result)
}
completeItem(2)

Update state that depends on other calculated state in React-Hooks

I want to update a state (data) that depends on other calculated state (server)
setServer(prevTweets =>
[...json, ...prevTweets].filter(
(e, i, arr) => i === arr.findIndex(t => t.tweetId === e.tweetId)
)
)
The data above will be used to set the state below (data) :
let totalPositive = 0;
let totalNegative = 0;
let totalNeutral = 0;
server.forEach(tweet => {
if(tweet.sentiment >0) totalPositive++;
if(tweet.sentiment < 0) totalNegative++;
if(tweet.sentiment ===0) totalNeutral++;
})
setData([
{ name: 'Positive', value: totalPositive },
{ name: 'Negative', value: totalNegative },
{ name: 'Neutral', value: totalNeutral },
])
Since it's asynchronous, the setData operation is always late. I know that I can use useEffect but apparently it will make an infinite loop and it's not right to use it in this case.
If you set the new data before you set the server you'd skip one render:
//still defaults to server so if you do't pass anything it;s still the same
const setNewData = (newServer = server) => {
const [
totalPositive,
totalNegative,
totalNeutral,
] = newServer.reduce(
([pos, neg, neu], { sentiment }) =>
sentiment > 0
? [pos + 1, neg, neu]
: sentiment < 0
? [pos, neg + 1, neu]
: [pos, neg, neu + 1],
[0, 0, 0]
);
setData([
{ name: 'Positive', value: totalPositive },
{ name: 'Negative', value: totalNegative },
{ name: 'Neutral', value: totalNeutral },
]);
};
setServer(prevTweets => {
const newServer = uniqueByTweetId([
...json,
...prevTweets,
]);
setNewData(newServer);
return newServer;
});
Unrelated to the question but could be important is that the way you get unique values could be improved. You could get unique values in one pass without having to call find index many times:
const uniqueBy = getter => arr => {
const items = new Map();
return arr.filter(item => {
const key = getter(item);
const ret = items.get(key);
items.set(key,true);
return !ret;
});
};
const data = [
{ id: 1 },
{ id: 2 },
{ id: 3 },
{ id: 4 },
{ id: 5 },
{ id: 1 },
{ id: 7 },
{ id: 1 },
{ id: 7 },
{ id: 8 },
{ id: 1 },
];
const uniqueById = uniqueBy(i => i.id);
console.log(uniqueById(data));

Resources