I've been trying to add some more fun commands to my Discord bot and one of those commands is that the user can type a command with a text and that the bot will put that text in random caps.
I have been trying to get it to work for a while now, but can only get it to work when the text is already coded in. The randomization itself does work
Example of the command:
User: +random text here
Bot: TeXt HeRe
The code I have so far is
const Discord = require("discord.js");
module.exports = {
name: "random",
category: "fun",
description: "Sends random caps text back",
usage: "+random <text>",
run: async (client, message, args) => {
//Checks if there is something to randomize
if (!args[0]) return message.reply("Please also provide text to randomize");
//Text needs to be in a const here
const str = "text the user has submitted";
//Gets the randomized text and sends it
const randified = randify(str);
message.channel.send(randified);
//Randomizes the text
function randify(string) {
const arr = string.toLowerCase().split("");
arr.forEach((e, i, a) => (Math.random() > .4) ? a[i] = e.toUpperCase() : e);
return arr.join("");
};
}
as you can see I currently need to have the text in a const and I have not been albe to get it to work without it.
Try this instead, it uses map instead of forEach as it will return an array of the characters which can then be joined.
const randified = text
.split('')
.map(char => Math.random() > 0.5 ? char.toUpperCase() : char.toLowerCase())
.join('')
Or if you want it in a separate function:
function randify(input) {
return input
.split('')
.map(char => Math.random() > 0.5 ? char.toUpperCase() : char.toLowerCase())
.join('')
}
Related
Coding a discord bot using the commando framework of discord.js v12.5, made an 'add' and 'multiply' command that adds every single number inputted by a user.
Here is the code for the 'add' command:
const Discord = require('discord.js')
const Commando = require('discord.js-commando')
module.exports = class AddCommand extends Commando.Command {
constructor(client) {
super(client, {
name: 'add',
group: 'math',
memberName: 'add',
description: 'Adds numbers',
argsType: 'multiple',
})
}
async run(message, args) {
let sum = 0
for (const arg of args) {
sum += parseInt(arg)
}
const addCommandoEmbed = new Discord.MessageEmbed()
.setTitle(`SUCCESS\n\n${args.join(' + ')} = ${sum}`)
.setColor('#1be730')
message.channel.send(addCommandoEmbed)
}
}
I don't know how to use logical operators to make it substract every single number given, and how to divide every single number given, and give the remainder at the end.
You can use MathJS library.
const mathjs = require("mathjs");
const addCommandoEmbed = new Discord.MessageEmbed()
.setTitle(`SUCCESS\n\n${args.join(' + ')} = ${mathjs.evaluate(args.join(" + "))}`)
.setColor('#1be730');
message.channel.send(addCommandoEmbed)
I'm quite new to Javascript, normally a Python person. I've looked at some other answers but my embed does not add the fields as expected. The embed itself is sent.
My Discord bot follows the guide provided by the devs (primary file, slash commands, command files). I am trying to loop through the entries in an SQLite query and add them as fields.
My command file is below.
const { SlashCommandBuilder } = require('#discordjs/builders');
const { MessageEmbed } = require('discord.js')
const sqlite = require('sqlite3').verbose();
module.exports = {
data: new SlashCommandBuilder()
.setName('rank')
.setDescription('Rank all points.'),
async execute(interaction) {
const rankEmbed = new MessageEmbed()
.setColor('#0099ff')
.setTitle('Rank Board')
let db = new sqlite.Database('./databases/ranktest.db', sqlite.OPEN_READWRITE);
let queryall = 'SELECT name, points FROM pointstable ORDER BY points DESC'
db.all(queryall, [], (err, rows) => {
if (err) {
console.log('There was an error');
} else {
rows.forEach((row) => {
console.log(row.name, row.points)
rankEmbed.addField('\u200b', `${row.name}: ${row.points}`, true);
});
}
})
return interaction.reply({embeds: [ rankEmbed ] });
}
}
I would also like to convert row.name - held as Discord IDs - to usernames i.e. MYNAME#0001. How do I do this by interaction? I was able to obtain the User ID in another command by using interaction.member.id, but in this case I need to grab them from the guild. In Python I did this with await client.fetch_user but in this case the error await is only valid in async functions and the top level bodies of modules is thrown.
Thanks.
OK I've solved the first aspect, I had the return interaction.reply in the wrong place.
Relevant snippet:
rows.forEach((row) => {
console.log(row.name, row.points)
rankEmbed.addField('\u200b', `${row.name}: ${row.points}`, false);
})
return interaction.reply({embeds: [rankEmbed ]} );
Would still appreciate an answer to the converting row.name (user ID) to user name via fetch.
I've solved the second aspect also. Add the below into the loop.
rows.forEach((row) => {
let client = interaction.client
const uname = client.users.cache.get(row.name);
rankEmbed.addField('\u200b', `${uname}: ${row.points}`, false);
I know that firestore doesn't support full text search and it giving us solution to use third party services. However I found a simple solution to simple "full text search" and I think this might help others who doesn't want to use third party services as me for such a simple task.
I'm trying to search for company name which is saved in firestore collection under my companyName which can be in any format for example "My Awesome Company". When adding new company with companyName or updating a value in companyName I'm also saving searchName with it which is the same value as company name but in lower case without spaces
searchName: removeSpace(companyName).toLowerCase()
removeSpace is my simple custom function which remove all spaces from a text
export const removeSpace = (string) => {
return string.replace(/\s/g, '');
}
That turns our company name to myawesomecompany which is saved in searchName
Now I've got a firestore function to search for company which indexing through searchName and returning companyName. Minumum search value is a searched value without last character and maximum search value is a searched value with added "zzzzzzzzzzzzzzzzzzzzzzzz" transformed to lower case. That means if you search for My Aw then min value will be mya and max value will be myawzzzzzzzzzzzzzzzzzzzzzzz
exports.handler = ((data) => {
const searchValue = data.value.replace(/\s/g, '').toLowerCase()
const minName = searchValue.substr(0, searchName.length-1)
const maxName = searchValue + "zzzzzzzzzzzzzzzzzzzzzzzz"
let list = []
const newRef = db.collection("user").where("profile.searchName", ">=", minName).where("profile.searchName", "<=", maxName)
return newRef.get()
.then(querySnapshot => {
querySnapshot.forEach(doc => {
list.push({ name: doc.data().profile.companyName})
})
return list
})
})
I didn't have time to fully test it but so far it works without any problems. Please let me know if you spot anything wrong with it. Now the question is
Is "z" character the highest value character in firestore or is there any other more decent way to add into the search value maximum amount without adding "zzzzzzzzzzzzz"?
I like your decision to preprocess the text so that it can be queried, but you could provide for a more flexible search by storing lowercase keywords with the users and searching those. In other words, transform:
"My Awesome Company"
to...
{ my: true, awesome: true, company: true }
...and test against that.
When adding/updating the property:
// save keywords on the user
let keywords = {}
companyName.split(' ').forEach(word => keywords[word.toLowerCase()] = true)
When querying:
let searchKeywords = userInputString.split(' ').map(word => word.toLowerCase())
let collection = db.collection("user")
searchKeywords.forEach(keyword => {
collection = collection.where(`keywords.${keyword}` , '==' , true);
});
With a little modification of previous answer I have made another simple text search. I'm saving keyword to an array instead of saving it in object like this
nameIndex: textIndexToArray(companyName)
where textIndexToArray is my custom function
export const textIndexToArray = (str) => {
const string = str.trim().replace(/ +(?= )/g,'')
let arr = []
for (let i = 0; i < string.trim().length; i++) {
arr.push(string.substr(0,i+1).toLowerCase());
}
return arr
}
which transfer a text into array. For example
"My Company"
will return
[m, my, my , my c, my co, my com, my comp, my compa, my compan, my company]
with nameIndex saved in firestore we can simply query the data thorough nameIndex and return companyName
exports.handler = ((data) => {
const searchValue = data.value.toLowerCase()
let list = []
const newRef = db.collection("user").where("nameIndex", "array-contains", searchValue)
return newRef.get()
.then(querySnapshot => {
querySnapshot.forEach(doc => {
list.push({ name: doc.data().companyName, })
})
return list
})
})
As I put in the question I have these commands that work correctly but are not validated, which means that if I enter a name it simply makes the record not validating if this name already exists inside the array.
It sounds simple but I've tried to validate using array.find and then compare with the value before inserting but it does not work: for example in the command track I'm trying to validate if the name you are trying to insert already exists. In the command untrack if the name does not exist send a message, because that is another problem: if the name does not exist the command deletes the last inserted record.
If someone has the knowledge on how to make such validations, I would appreciate a little help.
const args = message.content.slice(prefix.length).trim().split(/ +/g);
const command = args.shift().toLowerCase();
if (command === "track") {
let [playerName, playerType] = args;
list.push({
name: playerName,
type: playerType
}) var logger = fs.createWriteStream('./realmtrack-config.json')
logger.write(JSON.stringify(config)) message.reply(`Player being tracked!`);
}
if (command === "untrack") {
let [playerName, playerType] = args;
let entry_to_delete = list.find((e: any) => e.name === playerName);
list.splice(list.indexOf(entry_to_delete), 1);
var logger = fs.createWriteStream('./realmtrack-config.json');
logger.write(JSON.stringify(config));
message.reply(`${playerName} stopped being tracked!`);
}
You have an array of objects
let list = [
{ name: "Joe", type: "Cool" },
{ name: "Sally", type: "Cool"}
]
You could create the below function to check if "Sally" is the name of one of the objects.
function(obj) {
return obj.name === "Sally"
}
We can make this shorter with ES6 arrow functions
obj => obj.name === "Sally"
Next, we can scour list for an argument which will return true when passed to that function.
list.some(obj => obj.name === "Sally")
.some will return false only if all of the elements in an array will return false when passed to the callback function. Otherwise, it returns true.
We can add the below line near the start of your message handler, then.
const isBeingTracked = list.some(obj => obj.name === args[0])
For the track command, you could add if(!isBeingTracked) { /* ... */ } (the ! means "not"). For the untrack command, you could add if(isBeingTracked) { /* ... */ }. /* ... */ is what you would replace for actual code.
I'm trying to create a "help" command for my Discord bot, but it seems like I can't figure out why. THe purge command also doesn't work, but the rest of them are working. The kick, ping, ban and say commands are all working at the moment. And I'm also trying to figure out how to let the bot log command usage to the console. Any help would be appreciated !
client.on("message", async message => {
// This event will run on every single message received, from any channel or DM.
// It's good practice to ignore other bots. This also makes your bot ignore itself
// and not get into a spam loop (we call that "botception").
if(message.author.bot) return;
// Also good practice to ignore any message that does not start with our prefix,
// which is set in the configuration file.
if(message.content.indexOf(config.prefix) !== 0) return;
// Here we separate our "command" name, and our "arguments" for the command.
// e.g. if we have the message "+say Is this the real life?" , we'll get the following:
// command = say
// args = ["Is", "this", "the", "real", "life?"]
const args = message.content.slice(config.prefix.length).trim().split(/ +/g);
const command = args.shift().toLowerCase();
// Let's go with a few common example commands! Feel free to delete or change those.
if(command === "ping") {
// Calculates ping between sending a message and editing it, giving a nice round-trip latency.
// The second ping is an average latency between the bot and the websocket server (one-way, not round-trip)
const m = await message.channel.send("Ping?");
m.edit(`Pong! Latency is ${m.createdTimestamp - message.createdTimestamp}ms. API Latency is ${Math.round(client.ping)}ms`);
}
if(command === "say") {
// makes the bot say something and delete the message. As an example, it's open to anyone to use.
// To get the "message" itself we join the `args` back into a string with spaces:
const sayMessage = args.join(" ");
// Then we delete the command message (sneaky, right?). The catch just ignores the error with a cute smiley thing.
message.delete().catch(O_o=>{});
// And we get the bot to say the thing:
message.channel.send(sayMessage);
}
if (command === "kick") {
let modRole = message.guild.roles.find("name", "[Owner]");
if(message.member.roles.has(modRole.id)) {
let kickMember = message.guild.member(message.mentions.users.first());
message.guild.member(kickMember).kick();
message.channel.sendMessage("Member Kicked.");
} else {
return message.reply("You dont have the perms to kick members. scrub.");
}
}
if(command === "ban") {
let modRole = message.guild.roles.find("name", "[Owner]");
if(message.member.roles.has(modRole.id)) {
let banMember = message.guild.member(message.mentions.users.first());
message.guild.member(banMember).ban();
message.channel.sendMessage("Member banned.");
} else {
return message.reply("You dont have the perms to ban members. scrub.");
}
if(command === "purge") {
// This command removes all messages from all users in the channel, up to 100.
// get the delete count, as an actual number.
const deleteCount = parseInt(args[0], 10);
// Ooooh nice, combined conditions. <3
if(!deleteCount || deleteCount < 2 || deleteCount > 100)
return message.reply("Please provide a number between 2 and 100 for the number of messages to delete");
// So we get our messages, and delete them. Simple enough, right?
const fetched = await message.channel.fetchMessages({count: deleteCount});
message.channel.bulkDelete(fetched)
.catch(error => message.reply(`Couldn't delete messages because of: ${error}`));
}
if(command === "help") {
message.channel.send({embed: {
color: 3447003,
author: {
name: client.user.username,
icon_url: client.user.avatarURL
},
title: "Help",
description: "This message contains all the info of the bot's commands",
fields: [{
name: "d!help",
value: "This command can be used by everyone; displays this message"
},
{
name: "d!ping",
value: "This command can be used by everyone; it's tells the latency of the bot and the Discord API"
},
{
name: "d!kick <user>",
value: "This command can be used by [Owner]; it kicks a user"
},
{
name: "d!ban <user>",
value: "This command can be used by [Owner]; it bans a user"
},
{
name: "d!purge",
value: "This command isn't working correctly for as far as we know"
}
],
timestamp: new Date(),
footer: {
text: "© DeltBot Team"
}
}
Your fetched variable and bulkDelete needs to be in a async function since you're using await.