How to edit/replace embeds in discordjs-commando - node.js

I am making a Type racing minigame with my discord bot, the code works... but I want to change the messages it sends to embeds, im new to Commando and it wont let me use the discord.js functions im used to using
I need to change all the bots responses to embeds, and make it so when it sends a new embed it just edits the old one so it isnt spamming messages. Here is my code:
const Commando = require('discord.js-commando')
const { words } = require('../../util/fast-type-words')
const example = {
channelId: {
message: 'message object',
stage: 'string',
counter: 'number',
currentWord: 'string',
remainingWords: ['words here'],
points: {
userId: 'points',
},
},
}
const games = {}
const stages = {
STARTING: (counter) => {
return `A new "fast type" game is starting in ${counter}s!`
},
IN_GAME: (word) => {
let spacedWord = ''
for (const character of [...word]) {
spacedWord += character
spacedWord += ' '
}
return `The new word is **${spacedWord}**!`
},
ENDING: (points) => {
const sorted = Object.keys(points).sort((a, b) => {
return points[b] - points[a]
})
let results = ''
for (const key of sorted) {
const amount = points[key]
results += `<#${key}> had ${amount} point${amount === 1 ? '' : 's'}\n`
}
return `The game is now over Here's how everyone did:\n\n${results}------------------`
},
}
const selectWord = (game) => {
game.currentWord =
game.remainingWords[Math.floor(Math.random() * game.remainingWords.length)]
const index = game.remainingWords.indexOf(game.currentWord)
game.remainingWords.splice(index, 1)
}
const gameLoop = () => {
for (const key in games) {
const game = games[key]
const { message, stage } = game
if (stage === 'STARTING') {
let string = stages[stage](game.counter)
message.edit(string)
if (game.counter <= 0) {
game.stage = 'IN_GAME'
game.counter = 15
selectWord(game)
string = stages[game.stage](game.currentWord)
message.edit(string)
}
} else if (stage === 'IN_GAME') {
if (game.counter <= 0) {
game.stage = 'ENDING'
const string = stages[game.stage](game.points)
message.edit(string)
// Delete the game
delete games[key]
continue
}
}
--game.counter
}
setTimeout(gameLoop, 1000)
}
module.exports = class FastTypeGame extends Commando.Command {
constructor(client) {
super(client, {
name: 'fasttype',
group: 'games',
memberName: 'fasttype',
description: 'Starts a fast type game',
userPermissions: ['ADMINISTRATOR'],
})
client.on('message', (message) => {
const { channel, content, member } = message
const { id } = channel
const game = games[id]
if (game && game.currentWord && !member.user.bot) {
message.delete()
if (
game.stage === 'IN_GAME' &&
content.toLowerCase() === game.currentWord.toLowerCase()
) {
game.currentWord = null
const seconds = 2
const { points } = game
points[member.id] = points[member.id] || 0
message
.reply(`You won! +1 point (${++points[member.id]} total)`)
.then((newMessage) => {
newMessage.delete({
timeout: 1000 * seconds,
})
})
setTimeout(() => {
if (game.stage === 'IN_GAME') {
selectWord(game)
const string = stages[game.stage](game.currentWord)
game.message.edit(string)
}
}, 1000 * seconds)
}
}
})
gameLoop()
}
async run(message) {
const { channel } = message
message.delete()
channel.send('Preparing game...').then((message) => {
games[channel.id] = {
message,
stage: 'STARTING',
counter: 5,
remainingWords: [...words],
points: {
'719805930547445772': 4,
'723819104045105172': 1,
},
}
})
}
}

First change embeded content is not related to discord.js-commando to change the content of sended embeded message you need to get Message Object then using edit() method to pass the new embed content to it:
-Bonus: You can also edit text message into embed message.
Docs for edit method: https://discord.js.org/#/docs/main/stable/class/Message?scrollTo=edit
Example code:
let youCurrentMessage = await channel.send(embedContent);
yourCurrentMessage.edit(newEmbedContent);
yourCurrentMessage.edit(newEmbedContent2);
// If you edit message in other command , session.You need message id
let yourCurrentMessage = await msg.channel.messages.fetch(editMessageId);
yourCurrentMessage.edit(newEmbedContent);

Related

How is this messageComponentCollector not working? (discord.js v13)

I'm trying to make a message component collector on this message and to only read Select Menu components.
However, the collector seems to exist but it doesn't collect. As when I change something in the Select Menu, the menu says "This interaction failed."
Here's the code:
if (wikiEmbeds.length > 1) {
let selectMenuOptions = []
for (let i = 0; i < wikiEmbeds.length; i++) {
const selectedContent = wikiEmbeds[i];
if (i == 0) {
selectMenuOptions.push(
{
label: selectedContent.tableName,
value: `${i}`,
default: true
}
)
} else {
selectMenuOptions.push(
{
label: selectedContent.tableName,
value: `${i}`
}
)
}
}
let messageRow = new Discord.MessageActionRow()
.addComponents(
new Discord.MessageSelectMenu({
})
.setCustomId("ds3-bosses-menu")
.addOptions(selectMenuOptions)
)
const initialMessage = await interaction.reply({ embeds: [wikiEmbeds[0].embed], components: [messageRow], fetchReply: true })
const filter = (i) => { }
const collector = initialMessage.channel.createMessageComponentCollector({
filter,
componentType: 'SELECT_MENU',
time: 30000
})
console.log(collector)
collector.on("collect", (selectInteraction) => {
selectInteraction.reply(" . ")
})
}
wikiEmbeds is an array of tables, all of them like this { tableName: string, embed: embed }[]
I am pretty sure it's your filter. Your filter must return true to collect the SelectMenu. Try const filter = (i) => true;

How can I efficiently filter multiple values in mongodb

I'm trying to filter some data by themes and am puzzled as to how I can go about doing it. Say I have two items with the themes ['People', 'Technology', 'Culture'] and ['Economy', 'Technology', 'Culture'], if the query is Technology, I am able to see both of these items appearing. But if the query is Technology and Culture, I'm not able to see either of them because ["Technology", "Culture"] =/= ['People', 'Technology', 'Culture'] and vice versa. My code searches for the exact list, not it's components, if the query was Technology and Culture then I want both of those items to show up since it is inside that list.
I'm not sure how to do this, I'm using MERN stack and here is my backend code:
const Project = require('../models/projectModel')
const mongoose = require('mongoose')
// get all projects
const getProjects = async (req, res) => {
const projects = await Project.find().sort({ createdAt: -1 })
// const test = await Project.find({assignment_type: 1 || 2}).sort({ createdAt: -1 })
// console.log(test)
res.status(200).json(projects)
}
// get filtered project
const getFilteredProjects = async (req, res) => {
var request = {}
console.log(req.query.sdg)
console.log('t' + req.query.theme)
console.log('a' + req.query.assignment_type)
// var t = ["Economical", "Technological"]
// const test = await Project.find({theme: ("Technological" && "Economical")}).sort({ createdAt: -1 })
// const test = await Project.find({
// $and:
// }).sort({ createdAt: -1 })
// console.log(test)
// Function to separate commas from string
function separateCommas(str) {
let t = []
for (let i = 0; i < str.length; i++) {
if (str[i] === ',') {
console.log(i)
t.push(i)
}
}
let themeArray = []
if (t.length === 1) {
let theme1 = str.slice(0, t[0])
let theme2 = str.slice(t[0]+1)
themeArray.push(theme1)
themeArray.push(theme2)
}
if (t.length === 2) {
let theme1 = str.slice(0, t[0])
let theme2 = str.slice(t[0]+1, t[1])
let theme3 = str.slice(t[1]+1)
themeArray.push(theme1)
themeArray.push(theme2)
themeArray.push(theme3)
}
request["theme"] = themeArray.sort()
}
// See if sdg selected
if (req.query.sdg !== '') {
request["sdg"] = req.query.sdg
}
// See if assignment type selected
if (req.query.assignment_type !== '') {
request["assignment_type"] = req.query.assignment_type
}
// See if theme selected
if (req.query.theme !== '') {
if (req.query.theme.length > 14) {
separateCommas(req.query.theme)
}
else {
request["theme"] = req.query.theme
}
}
console.log(request)
const projects = await Project.find(request).sort({ createdAt: -1 })
res.status(200).json(projects)
}
module.exports = {
getProjects,
getProject,
createProject,
deleteProject,
updateProject,
getFilteredProjects
}
This is how my backend code receives the data from the database, it sends it in this format where there can be multiple theme's:
{
sdg: 'SDG 2: Zero Hunger',
assignment_type: 'Discussion Topics',
theme: 'Economy'
}

Unhandled rejection Error: A required argument is missing

so I was using this file called revertranks.js and I tried to run it with node revertranks.js.
When I run it, there would be this error saying the following:
"Unhandled rejection Error: A required argument is missing"
I was wondering why this was happening so I was checking this website and tried other solutions, however, none worked. One which made the error go away actually just made the code stop.
Here is the code:
* About:
* Reverts all rank change actions of a group after a specified date, with the option to filter by user.
*/
// Settings
const cookie = process.env.Cookie || '' // Roblox account .ROBLOSECURITY cookie
const options = {
group: (process.env.GroupId), // Group ID
userId: (process.env.AA1), // Revert rank changes made by this specified user - NOTE: When <= 0 any rank changes will be reverted
afterDate: new Date('2021-01-01 00:00 CDT') // Date after which any rank changes will be reverted
}
// Dependencies
const rbx = require('noblox.js')
const logUpdate = require('log-update')
// Main
let scanning = true
const logItems = {
scanned: 0,
filtered: 0,
reverted: 0,
failed: 0
}
async function getAuditLogPage (getAuditLogOptions, cursor) {
getAuditLogOptions.cursor = cursor || ''
const auditLogPage = await rbx.getAuditLog(getAuditLogOptions)
return auditLogPage
}
function filterAuditLogItems (auditLogItems) {
const filteredAuditLogItems = []
for (const auditLogItem of auditLogItems) {
if (Date.parse(auditLogItem.created) > options.afterDate) {
logItems.filtered++
filteredAuditLogItems.push(auditLogItem.description)
}
}
return filteredAuditLogItems
}
async function revertAuditLogItems (auditLogItems) {
for (const auditLogItem of auditLogItems) {
const setRankOptions = {
group: options.group,
target: auditLogItem.TargetId,
roleset: auditLogItem.OldRoleSetId
}
await rbx.setRank(setRankOptions)
.then(() => {
logItems.reverted++
})
.catch((e) => {
logItems.failed++
})
}
}
rbx.setCookie(process.env.Cookie)
.then(async () => {
console.time('Time taken')
const logUpdater = setInterval(() => {
logUpdate(`Scanned: ${logItems.scanned}\nFiltered: ${logItems.filtered}\nReverted: ${logItems.reverted}\nFailed: ${logItems.failed}`)
if (!scanning && logItems.reverted + logItems.failed === logItems.filtered) {
clearInterval(logUpdater)
console.timeEnd('Time taken')
}
}, 100)
const getAuditLogOptions = {
group: options.group,
actionType: 'changeRank',
userId: options.userId > 0 ? options.userId : null,
limit: 100
}
let auditLogPage = await getAuditLogPage(getAuditLogOptions)
logItems.scanned += auditLogPage.data.length
await revertAuditLogItems(filterAuditLogItems(auditLogPage.data))
while (auditLogPage.nextPageCursor !== null && Date.parse(auditLogPage.data[auditLogPage.data.length - 1].created) > options.afterDate) {
auditLogPage = await getAuditLogPage(getAuditLogOptions, auditLogPage.nextPageCursor)
logItems.scanned += auditLogPage.data.length
await revertAuditLogItems(filterAuditLogItems(auditLogPage.data))
}
scanning = false
})
Please help me, I need this to revert an action on a website using a bot.
Here is the error message since someone asked: https://imgur.com/a/U9HJW7O
I apologize for the previous message, I put some of the code I was experimenting with.

How to make a command randomly fail

Okay so on my bot I have a beg command but the peoblem is I want it to sometimes fail.
For example one time it might say "Someone gave you $5" but if it fails it would say "Famous Person: NOPE"
The code below chooses a random text from a file where all the text is.
Is there anyway to mke it randomly fail so a user does not get money?
const { RichEmbed } = require("discord.js");
const { stripIndents } = require("common-tags");
const { prefix } = require("../../botconfig.json");
const db = require('quick.db')
let bal = require("../../database/balance.json");
const fs = require('fs');
const cooldowns = new Map();
const humanizeDuration = require('humanize-duration');
//Set cooldown
module.exports = {
name: "beg",
aliases: [],
category: "economy",
description: "Gets you money",
usage: "[command | alias]",
run: async (client, message, args) => {
const cooldown = cooldowns.get(message.author.id);
if (cooldown) {
const remaining = humanizeDuration(cooldown - Date.now(),{ units: ['s'],round: true });
let cEmbed = new RichEmbed()
.setColor("RANDOM")
.setTitle("Slow down, cmon!")
.setDescription(`You dont want to be like a cry baby! You will be able to beg in \`${remaining}\` just you wait!\n\nWhile you wait why not follow our [Twitter](https://twitter.com/switchoffical)`)
return message.channel.send(cEmbed)
.catch(console.error);
} else {
if(!bal[message.author.id]){
bal[message.author.id] = {
balance: 0
};
}
const Jwork = require('../../beg.json');
const JworkR = Jwork[Math.floor(Math.random() * Jwork.length)];
var random = Math.floor(Math.random() * 20) + 3;
let curBal = bal[message.author.id].balance
bal[message.author.id].balance = curBal + random;
fs.writeFile('././database/balance.json', JSON.stringify(bal, null, 2), (err) => {
let embed = new RichEmbed()
.setColor("RANDOM")
.setDescription(`**\ ${message.author.username}**, ${JworkR} 💵 **${random}**`)
message.channel.send(embed)
if (err) console.log(err)
});
//Adds the user to the set so that they can't talk for a minute
cooldowns.set(message.author.id, Date.now() + 10000);
setTimeout(() => cooldowns.delete(message.author.id), 10000);
}
}
}
I just don't know how to make it fail
So what you can do is run a Math.floor((Math.random()-0.001)*4) store it to a variable. Now you have a random number from 0 to 3 (4 different numbers/outcomes). And then check whether your new variable equals 0. if(failChance === 0) if it's true just don't do the add bal cmd.
Example:
...
} else {
var failChance = Math.floor((Math.random()-0.001)*4);
if(failChance === 0){
message.channel.send('FAILURE');
return;
}
if(!bal[message.author.id]){
...

start telegraf scene only by command using telegraf.js

I'm trying to start some scene only by exact command /start
I'm using Telegraf.js library
that's not going well when I do middleware it starts automatically when I send some other input and not just /start
how can I solve it? thanks.
bot.use(session())
const userWizard = new WizardScene('user-wizard',
(ctx) => {
ctx.reply("What is your name?");
//Necessary for store the input
ctx.scene.session.user = {};
//Store the telegram user id
ctx.scene.session.user.userId = ctx.from.id;
return ctx.wizard.next();
},
(ctx) => {
//Validate the name
if (ctx.message.text.length < 1 || ctx.message.text.length > 12) {
return ctx.reply("Name entered has an invalid length!");
}
//Store the entered name
ctx.scene.session.user.name = ctx.message.text;
ctx.reply("What is your last name?");
return ctx.wizard.next();
},
async (ctx) => {
//Validate last name
if (ctx.message.text.length > 30) {
return ctx.reply("Last name has an invalid length");
}
ctx.scene.session.user.lastName = ctx.message.text;
//Store the user in a separate controller
// userController.StoreUser(ctx.scene.session.user);
return ctx.scene.leave(); //<- Leaving a scene will clear the session automatically
}
);
const stage = new Stage([userWizard], { default: 'user-wizard' })
bot.use(stage)
bot.command('/start', ctx => {
stage.start(ctx)
}
)
bot.launch()
Try to replace this part in your code:
const stage = new Stage([userWizard], { default: 'user-wizard' })
bot.use(stage)
bot.command('/start',ctx => {
stage.start(ctx)
}
)
With this:
const stage = new Stage([userWizard]);
bot.use(session());
bot.use(stage.middleware());
bot.command('/start',(ctx) => ctx.scene.enter('user-wizard'));
bot.launch();

Resources