Im trying to develop a little warn system for my Discord Bot. If someone types !warn #mention Reason, it should store the data in a JSON File. It works, but only with one User in one Guild. What I want is, that
the JSON File looks like this:
{
"superniceguildid":
{
"member": "636302212787601408",
"warns": 3
},
{
"meber": "7837439745387549"
"warns": 1
}
}
Now only this exists:
{
"627818561947041816": {
"guild": "636302212787601408",
"warns": 3
},
}
How can I do it, that the File is generating like above?
My current code is this:
module.exports = {
name: 'warn',
description: "test",
execute(message, args){
const { Client, MessageEmbed } = require("discord.js")
const client = new Client()
const fs = require("fs")
const ms = require("ms")
warns = JSON.parse(fs.readFileSync("./warns.json", "utf8"))
client.servers = require ("./servers.json")
let guild = client.servers[message.guild.id].message
/*Embeds*/
const oops = new MessageEmbed()
.setTitle("Error")
.setColor("RED")
.setDescription("You cant warn a member. Please ask a Moderator")
.setAuthor("MemeBot", "this is a link")
const Mod = new MessageEmbed()
.setTitle("Error")
.setColor("RED")
.setDescription("You cant warn a Moderator.")
.setAuthor("MemeBot", "linkhere xD")
/**Commands */
let wUser = message.mentions.users.first() || message.guild.members.cache.fetch(`${args[0]}`)
if (!wUser) return message.channel.send("Are you sure, that this was a User? I think it wasn't one...")
let wReason = args.join(" ").slice(27)
if (!wReason) return message.channel.send("Please tell me, why you want to warn this person. Because, you know, it's a warn :D");
if(!message.member.hasPermission("KICK_MEMBERS")) return message.channel.send(oops)
if(wUser.hasPermission("KICK_MEMBERS")) return message.channel.send(Mod)
if(!warns[message.guild.id]) warns[message.guild.id] = {
user: wUser.id,
warns: 0
}
warns[wUser.id].warns++
fs.writeFile("./warns.json", JSON.stringify(warns, null, 4), err => {
if(err) console.log(err)
});
let warnEmbed = new MessageEmbed()
.setTitle("Warned")
.setColor("YELLOW")
.addField("Warned User", `${wUser}`)
.addField("Moderator", `${message.author.id}`)
.addField("Reason", `${wReason}`)
.addField("Number of Warnings", warns[wUser.id].warns)
.addField("Warned at", `${message.createdAt}`)
let warnonEmbed = new MessageEmbed()
.setTitle("Warned")
.setColor("YELLOW")
.addField("Warned on", `${message.guild.name}`)
.addField("Moderator", `${message.author}`)
.addField("Reason", `${wReason}`)
.addField("Warned at", `${message.createdAt}`)
let logchannel = message.guild.channels.cache.find(c => c.id === 'id');
if(!logchannel) return
wUser.send(warnonEmbed)
logchannel.send(warnEmbed)
}
}
That particular layout doesn't make a lot of hierarchical sense. You might want to nest the user inside the guild and any parameters belonging to the user inside that. Something like this...
"superniceguildid":
{
"636302212787601408":
{
"warns": 3
},
"7837439745387549":
{
"warns": 1
}
},
Accessing it then would be as easy as using something like the following:
let guildWarns = warns["superniceguildid"];
let userWarns = guildWarns["636302212787601408"];
let numberOfWarns = userWarns.warns;
you can combine that as well.
let numberOfWarns = warns["superniceguildid"]["636302212787601408"].warns;
Of course, remember that if it doesn't exist it will be undefined.
Related
this is my ../Events/Guild/guildMemberAdd.js https://sourceb.in/iEEfLj7uM7
im trying to set placeholders that will in turn give out an output like
Welcome to OnlyScoped.gg #azz#5271! We're glad to have you as the 500th member.
but output is
Welcome to OnlyScoped.gg <#undefined>! We're glad to have you join us as the undefinedth member.`
../Commands/Moderation/setup-welcome.js
const {Message, Client, SlashCommandBuilder, PermissionFlagsBits} = require("discord.js");
const welcomeSchema = require("../../Models/Welcome");
const {model, Schema} = require("mongoose");
module.exports = {
data: new SlashCommandBuilder()
.setName("setup-welcome")
.setDescription("Set up your welcome message for the discord bot.")
.setDefaultMemberPermissions(PermissionFlagsBits.Administrator)
.addChannelOption(option =>
option.setName("channel")
.setDescription("Channel for welcome messages.")
.setRequired(true)
)
.addStringOption(option =>
option.setName("welcome-message")
.setDescription("Enter your welcome message.")
.setRequired(true)
)
.addRoleOption(option =>
option.setName("welcome-role")
.setDescription("Enter your welcome role.")
.setRequired(true)
),
async execute(interaction) {
const {channel, options} = interaction;
const welcomeChannel = options.getChannel("channel");
const welcomeMessage = options.getString("welcome-message");
const roleId = options.getRole("welcome-role");
if(!interaction.guild.members.me.permissions.has(PermissionFlagsBits.SendMessages)) {
interaction.reply({content: "I don't have permissions for this.", ephemeral: true});
}
welcomeSchema.findOne({Guild: interaction.guild.id}, async (err, data) => {
if(!data) {
const newWelcome = await welcomeSchema.create({
Guild: interaction.guild.id,
Channel: welcomeChannel.id,
Msg: welcomeMessage,
Role: roleId.id
});
}
interaction.reply({content: 'Succesfully created a welcome message', ephemeral: true});
})
}
}
../Models/Welcome.js
const { model, Schema } = require("mongoose");
let welcomeSchema = new Schema({
Guild: String,
Channel: String,
Msg: String,
Role: String,
});
module.exports = model("Welcome", welcomeSchema);
im attempting to use string.replace()but its not working as expected
i decided to put it in guildMemberAdd.js since when a member joins this gets runs so it would be unwise to place it in setup-welcome.js or Welcome.js since those are not listening for anything.
for reference here's my package.json:
https://sourceb.in/FMBgygjyoh
for the record i cant find any of the id's like member.id or member.count so those are wild guesses as to what they are. it could very well just be that as im still learning v14 this is my first project in it.
one other way i thought could work is if i just pass it off as an interpolated string in mongodb but it seems that the only string is with "" so i cant use default ones like ${member.count} so i decided to add placeholders
The basics of formatting a template are this:
const string = "Welcome to OnlyScoped.gg {tagUser}! We're glad to have you as the {memberCount} member.";
string = string.replace(/{tagUser}/g, member.toString());
string = string.replace(/{memberCount}/g, '500th');
return string; // "Welcome to OnlyScoped.gg <#123456789012345678>! We're glad to have you as the 500th member.";
To make something extensible, put template strings like this somewhere in your configuration:
{
"welcome_message": "Welcome to OnlyScoped.gg {tagUser}! We're glad to have you as the {ordinal:memberCount} member."
}
and make a function
function formatMessage(template, lookup) {
let output = template;
output = output.replace(/{ordinal:([^}]*)}/g, (_, target) => withOrdinalSuffix(lookup(target)));
output = output.replace(/{([^}]*)}/g, (_, target) => lookup(target));
return output;
}
// https://stackoverflow.com/a/31615643/3310334
// turn 1 into '1st', 500 into '500th', 502 into '502nd'
function withOrdinalSuffix(n) {
var s = ["th", "st", "nd", "rd"],
v = n % 100;
return n + (s[(v - 20) % 10] || s[v] || s[0]);
}
and then use the function with a template and the lookup function:
client.on('guildMemberAdd', member => {
const welcomeMessageTemplate = config.welcome_message;
const memberCount = member.guild.members.filter(member => !member.user.bot).size;
const lookup = (item) => {
const items = {
memberCount,
tagUser: member.toString()
};
return items[item];
};
const welcomeMessage = formatMessage(welcomeMessageTemplate, lookup);
const welcomeChannel = member.guild.channels.cache.find(channel => channel.name === 'welcome');
welcomeChannel.send(welcomeMessage);
});
The main issue I can see is incorrect property names as you mention in the question.
DiscordJS Docs: GuildMember
member.id => The Members ID
member.user.username => The Members username
member.guild.name => The Server's Name
member.guild.memberCount => Number of users within the Server
I'd advise the user to input data in a specific format like Hello {userName}!. Then you could run a program like
while (string.includes('{userName}')) {
string = string.replace('{userName}', member.user.username);
}
first issue: all the embeds in my code stopped working - no matter what command I try to run if it has an embed in it I get the error: DiscordAPIError: Cannot send an empty message
second issue: I'm currently programming a mute command with a mongoDB database, it puts everything I need it in the database however if I try to mute someone it ends up only muting them for 1s by default, basically completely ignoring the second argument. heres what I want the command to do: when you mute someone you need to provide the user id and a time (works in ms) + reason then it puts it in the data base.
here's the code: [P.S. Im not getting an error message, it just doesnt work properly like I want it to]
const mongo = require('../mongo.js')
const muteSchema = require('../schemas/mute-schema.js')
const Discord = require('discord.js')
const ms = require ("ms")
module.exports = {
commands: 'mute',
minArgs: 2,
expectedArgs: "<Target user's #> <time> <reason>",
requiredRoles: ['Staff'],
callback: async (message, arguments) => {
const target = message.mentions.users.first() || message.guild.members.cache.get(arguments[0])
if (!target) {
message.channel.send('Please specify someone to mute.')
return
}
const { guild, channel } = message
arguments.shift()
const mutedRole = message.guild.roles.cache.find(role => role.name === 'muted');
const guildId = message.guild.id
const userId = target.id
const reason = arguments.join(' ')
const user = target
const arg2=arguments[2]
const mute = {
author: message.member.user.tag,
timestamp: new Date().getTime(),
reason,
}
await mongo().then(async (mongoose) => {
try {
await muteSchema.findOneAndUpdate(
{
guildId,
userId,
},
{
guildId,
userId,
$push: {
mutes: mute,
},
},
{
upsert: true,
}
)
} finally {
mongoose.connection.close()
}
})
message.delete()
user.roles.add(mutedRole)
setTimeout(function() {
user.roles.remove(mutedRole)
}, ms(`${arg2}`));
try{
message.channel.send(`works`)
}
catch(error){
const embed3 = new Discord.MessageEmbed()
.setDescription(`✅ I Couldn't DM them but **${target} has been muted || ${reason}**`)
.setColor('#004d00')
message.channel.send({ embeds: [embed3] });
}
},
}
djs v13 has a new update where you need to send embeds like this:
const exampleEmbed = new Discord.MessageEmbed()
.setTitle('example')
.setDescription('example')
.setColor('RANDOM')
message.channel.send({embed: [exampleEmbed]});
I'm setting up Slash Commands with my Discord.JS bot.
I have a /rank command in the XP/leveling portion of my bot, but when I check interaction.member.presence for displaying it in the rank card, it returns null.
I've tried to look up this problem and look through the documentation of Discord.JS, but nobody else seems to have had this problem yet and I don't see anything in the documentation or Discord.JS guide to help solve this problem.
Here is the /rank command:
const Discord = require("discord.js");
const SQLite = require("better-sqlite3");
const sql = new SQLite('./mainDB.sqlite')
const client = new Discord.Client({ intents: [Discord.Intents.FLAGS.GUILDS, Discord.Intents.FLAGS.GUILD_MESSAGES] });
const canvacord = require("canvacord");
module.exports = {
name: 'rank',
aliases: ['rank'],
description: "Get your rank or another member's rank",
cooldown: 3,
category: "Leveling",
async execute(interaction) {
if(!interaction.isCommand()) return console.log("yes");
await interaction.deferReply()
.then(console.log("a"))
.catch(console.error);
let user = interaction.user;
client.getScore = sql.prepare("SELECT * FROM levels WHERE user = ? AND guild = ?");
client.setScore = sql.prepare("INSERT OR REPLACE INTO levels (id, user, guild, xp, level, totalXP) VALUES (#id, #user, #guild, #xp, #level, #totalXP);");
const top10 = sql.prepare("SELECT * FROM levels WHERE guild = ? ORDER BY totalXP").all(interaction.guild.id);
let score = client.getScore.get(user.id, interaction.guild.id);
if (!score) {
return interaction.editReply(`This user does not have any XP yet!`)
}
const levelInfo = score.level
const nextXP = levelInfo * 2 * 250 + 250
const xpInfo = score.xp;
const totalXP = score.totalXP
let rank = top10.sort((a, b) => {
return b.totalXP - a.totalXP
});
let ranking = rank.map(x => x.totalXP).indexOf(totalXP) + 1
//if (!interaction.guild.me.hasPermission("ATTACH_FILES")) return interaction.editReply(`**Missing Permission**: ATTACH_FILES or MESSAGE ATTACHMENTS`);
try {
var cardBg = sql.prepare("SELECT bg FROM background WHERE user = ? AND guild = ?").get(user.id, message.guild.id).bg;
var bgType = "IMAGE";
} catch (e) {
var cardBg = "#000000";
var bgType = "COLOR";
}
console.log(interaction.member.presence);
const rankCard = new canvacord.Rank()
.setAvatar(user.displayAvatarURL({
format: "jpg"
}))
.setStatus(interaction.member.presence.status, true, 1)
.setCurrentXP(xpInfo)
.setRequiredXP(nextXP)
.setProgressBar("#5AC0DE", "COLOR")
.setUsername(user.username)
.setDiscriminator(user.discriminator)
.setRank(ranking)
.setLevel(levelInfo)
.setLevelColor("#5AC0DE")
.renderEmojis(true)
.setBackground(bgType, cardBg);
rankCard.build()
.then(data => {
const attachment = new Discord.MessageAttachment(data, "RankCard.png");
return interaction.editReply({attachments: [attachment]});
});
}
}
Notes:
I do have the Presence Intent enabled.
Sorry if this seems like too little information. It's just what I
know so far, and I can't think of anything that I can do about it.
I know this command is very messy. I'm not asking how to fix that. I will fix that later.
Even though you have the PRESENCE INTENT enabled, you need to specify that you will be using the aforementioned intent in ClientOptions.
const client = new Discord.Client({
intents: [Discord.Intents.FLAGS.GUILDS, Discord.Intents.FLAGS.GUILD_MESSAGES, Discord.Intents.FLAGS.GUILD_PRESENCES],
});
First You need to enable intent
const client = new Discord.Client({
intents: [
Discord.Intents.FLAGS.GUILD_PRESENCES
],
});
Now we need Create a var of member
let member = message.mentions.members.first()
For get the presence of members use #presence
console.log(member.presence)
Will get results as
Presence {
userId: 'USER_ID',
guild: [Guild],
status: 'online',
activities: [Array],
clientStatus: [Object]
}
Hello.
I coded a simple MessageEmbed function (with Discord.JS) and every time that I call it, the new embed that is sent in the channel has his field who adds up with the precedent
(e.g.: if the embed should have 2 fields, the next time that the command will be called it will have 2*2 the required fields. If you call it again, 3*2, 4*2, etc.).
When I restart the bot it reset. I tried to reset the embed value but it didn't affect the problem.
Could you help me please ?
Here is my JS command :
module.exports = {
name: 'drive',
execute(client, message, args, embed) {
message.channel.send(embed
.setColor('#0099ff')
.setTitle('abcdedfg')
.setDescription('abcdedfg \n\u200B')
.setThumbnail('abcdedfg')
.addFields(
{ name: 'abcdedfg :', value: 'link' },
{ name: 'abcdedfg :', value: 'link \n\u200B' },
)
.setFooter('abcdedfg'))
.catch(console.error);
}
}
And here is my main if needed :
const fs = require('fs');
const { Client, Collection, MessageEmbed } = require('discord.js');
const { TOKEN, PREFIX } = require('./config/config');
const client = new Client();
client.commands = new Collection();
const commandFiles = fs.readdirSync('./commands').filter(file => file.endsWith('.js'));
const embed = new MessageEmbed();
for (const file of commandFiles) {
const command = require(`./commands/${file}`);
client.commands.set(command.name, command);
}
client.on('message', message => {
if (!message.content.startsWith(PREFIX) || message.author.bot) return;
const args = message.content.slice(PREFIX.length).split(/ +/);
const command = args.shift().toLowerCase();
if (!client.commands.has(command)) return;
client.commands.get(command).execute(client, message, args, embed);
});
client.login(TOKEN);
I found the answer to block the fields from being added every times to the the fields array here on StackOverFlow.
So the code answer is addind a embed.fields = []; at the end :
module.exports = {
name: 'drive',
execute(client, message, args, embed) {
message.channel.send(embed
.setColor('#0099ff')
.setTitle('abcdedfg')
.setDescription('abcdedfg \n\u200B')
.setThumbnail('abcdedfg')
.addFields(
{ name: 'abcdedfg :', value: 'link' },
{ name: 'abcdedfg :', value: 'link \n\u200B' },
)
.setFooter('abcdedfg'))
.catch(console.error);
embed.fields = [];
}
}
Its quite simple. don't add fields. adding fields is what adds new fields so if you do not want them do not add them. else, clear the old ones before adding new ones.
My recommendation, use this;
module.exports = {
name: 'drive',
execute(client, message, args, embed) {
message.channel.send(embed
.setColor('#0099ff')
.setTitle('abcdedfg')
.setDescription('abcdedfg \n\u200B')
.setThumbnail('abcdedfg')
.setFooter('abcdedfg'))
.catch(console.error);
}
}
Try this and let's see.
As long as the first fields exist, you don't ever have to add.
I have script for warn command and i need help with that, because this
code working, this command saving warns in warnings.json, but when i warn someone that warn be in every guild i want only in one guild warns. Please help :D
const { MessageEmbed } = require("discord.js")
const fs = require('fs')
const warns = JSON.parse(fs.readFileSync("./warnings.json", "utf8"))
const moment = require('moment')
module.exports = {
name: "warn",
description: "Wysyła ankiete",
guildOnly: true,
cooldown: 5,
run(msg, args) {
// Embed na permisjebota
const permisjebota = new MessageEmbed()
.setTitle("⛔ Nie mam uprawnień! :O")
.setColor('ffff00')
.setDescription("Nie mam uprawnień do tej komendy! Daj mi uprawnienia lub skonsultuj się z adminem serwera")
.setTimestamp()
// Embed na permisje dla użytkownika
const permisje = new MessageEmbed()
.setTitle("Nie masz permisji do tej komendy! :O")
.setColor('ffff00')
.setDescription("Nie masz uprawnień do tej komendy! Jeżeli uważasz, że to błąd skonsultuj się z adminem serwera!")
if (!msg.member.guild.me.hasPermission("ADMINISTRATOR"))
return msg.channel.send(permisjebota)
if (!msg.member.hasPermission("MANAGE_MESSAGES")) return msg.channel.send(permisje)
if(!args[0, 1]) {
const bananekbot = new MessageEmbed()
.setTitle("Nie podałeś argumentów!")
.setColor('ffff00')
.setDescription("Poprawne użycie: `m!warn <nick> <powód>`")
return msg.channel.send(bananekbot)
}
var warnUser = msg.guild.member(msg.mentions.users.first() || msg.guild.members.get(args[0]))
var reason = args.slice(1).join(" ")
if (!warnUser) return msg.channel.send("Brak argumentu poprawne użycie: m!warn <nick> <powód>")
if (!warns[warnUser.id]) warns[warnUser.id] = {
warns: 0,
}
warns[warnUser.id].warns++
fs.writeFile("./warnings.json", JSON.stringify(warns), (err) =>{
if(err) console.log(err)
})
const warnembed = new MessageEmbed()
.setTitle("✅ Nadano warna")
.setColor('ffff00')
.setTimestamp()
.setDescription(`Użytkownik: ${warnUser} (${warnUser.id})
Nadający warna: ${msg.author}
Powód: ${reason}`)
return msg.channel.send(warnembed)
}
}
A potential solution to this is to have warnings.json as an array, and in side containing an object with guild id, warns and anything else you need to store. So warnings.json ends up something like this.
[
{
"guild_id": "12345678901234",
"an_extra": "Thing",
"warns": [
"your_warns",
"are_here",
"store_in_any_format"
]
},
{
"guild_id": "12345678901234",
"an_extra": "Guild",
"warns": [
"so_on",
"And so forth",
"go the warns and guilds"
]
}
]
This way, you can simply do Array.find(guild => guild.guild_id === msg.guild.id) (Assuming the guild you are trying to access is msg.guild).
Learn more on MDN about Array.prototype.find()