Discord.js v12 role.delete() doesn't work - javascript

So, i was testing this out, and that when I use this code, it doesn't actually delete the roles, is this a bug?
On the code:
client.on('message', async message => {
message.guild.roles.fetch()
.then(r => r.forEach(s =>
s.delete()
));
})
I tried other variants as well such as:
client.on('message', async message => {
message.guild.roles.cache.forEach(r => r.delete());
});
client.on('message', async message => {
message.guild.roles.cache
.map(r => r.id)
.forEach(s =>
message.guild.roles.cache.find(l => l.id === s)
.delete()
);
});
client.on('message', async message => {
const roles = message.guild.roles.cache
.map(r => r.id);
for (m=0;m<roles.length;m++) {
message.guild.roles.cache.find(s => s.id === roles[m])
.delete();
}
});
I even tried to not mass delete and just delete one:
message.guild.roles.cache.find(s => s.id === 'ROLE_ID')
.delete();
None seemed to have worked, and they all return the same error:
TypeError: Cannot read property 'delete' of undefined
or
DiscordAPIError: Invalid Role
Though I'm 100% sure that the role does exist and is valid. This error only happens when I try to delete roles. When I tried checking the roles in the server using
message.guild.roles.cache.map(r => r.id);
it sometimes returns a blank array, though there are roles in the server.

It seems that the reason for this is because Discord Guilds have default roles, mainly, #everyone, and one with the bots name, which you can't delete.
I don't know exactly how to differentiate these "default" roles from created ones, but it seems like #everyone role seems to be always Role.rawPosition= 0 and the bots roles likes to take the last position of this same index. Also throughout my testing, seems like bot roles have Role.editable = false. Created ones, but also #everyone, has it = true. Which makes things a little confusing.
With that in mind, it's up to you to create the best algorithm for what you're trying to achieve. But I can promise it's unsafe.
If you can if yourself to roles that can be deleted, Node shouldn't crash.
(CURA GAMER BOT is my test bot, so the first role was not manually created)
I'd recommend you to log all your roles cache, at least for testing, and check the roles properties in other to find out what will suit you better. Whatever if your doubt was if role.delete() was reliable, yes it seems like. But I'd recommend deleting a single role at time, having the reference to its objects anyway.
Here are some tests that worked for me (screenshot scenario) which may or may not work for you (use at your own risk):
const roles = message.guild.roles.cache;
roles.forEach(role => {
// -1 because I have only on bot role
if(role.rawPosition > 0 && role.rawPosition < (roles.size - 1)) {
role.delete();
}
});
const roles = message.guild.roles.cache;
roles.forEach(role => {
if(role.editable && role.name !== '#everyone') {
role.delete();
}
});
The safest for me, delete based on timestamp:
const roles = message.guild.roles.cache;
roles.forEach(role => {
// Created after Tue Sep 08 2020 15:55:00 GMT-0500
if(role.createdTimestamp > 1599598514000) {
role.delete();
}
});

That's because Collection.delete() requires a key, not an object. Instead of passing the role object as an argument, you should be passing the role snowflake.
message.guild.roles.cache.delete('<Role ID>')
Also, to delete all roles, you can use the Collection.clear() method, which removes all elements from the collection.
message.guild.roles.cache.clear()

Related

Discord, Mention someone when gets a role

Is there a way to mention someone immediately after getting a specific role in a specific channel?
For example when I, or any other admin, give someone a specific role the bot mentions them in a specific channel.
Here is my code:
client.on("guildMemberUpdate", (oldMember, newMember) => {
const channel = client.channels.cache.get("channelid");
// If the role(s) are present on the old member object but no longer on the new one (i.e role(s) were removed)
const removedRoles = oldMember.roles.cache.filter((role) => !newMember.roles.cache.has("roleid"));
if (removedRoles.size > 0) console.log(`The roles ${removedRoles.map((r) => r.name)} were removed from ${oldMember.displayName}.`);
// If the role(s) are present on the new member object but are not on the old one (i.e role(s) were added)
const addedRoles = newMember.roles.cache.filter((role) => !oldMember.roles.cache.has("roleid"));
if (addedRoles.size > 0) {
if (newMember.roles.cache.some((role) => role.name === "testing")) {
let embed = new Discord.MessageEmbed().setTitle("โ™ก๏น•welcome!").setDescription("lalalala").setColor("#FFB6C1").setThumbnail("https://cdn.discordapp.com/attachments/806974794461216813/817737054745526304/giffy_2.gif");
channel.send(`Welcome ${oldMember.user}`, embed);
}
console.log(`The roles ${addedRoles.map((r) => r.name)} were added to ${oldMember.displayName}.`);
}
});
This is assuming your code is exactly as shown in the question, and not using placeholder strings.
Firstly, "roleid" should be role.id without the "" when you're filtering. This will filter it properly, as should channelId where channel is defined, which I'm guessing are placeholders, but just in case.
Secondly, you'll probably want to check using addedRoles.has("roleIdToSend") as opposed to newMember.roles.cache.some(). This will DM them every time they're updated if they have the role, not if they only just got the role on that update.
Lastly, assuming you're using discord.js v13, you'll need to correct your send() method. It should look something like
channel.send({
content: `Welcome, ${oldMember.toString()}`,
embeds: [embed]
});

How Can My Discord.js Bot welcome new people to the server?

I'm trying to make it so the bot Will Auto Welcome new members on multiple servers, this is the code I have currently and it doesn't come up with an error but it doesn't work
client.on("guildMemberAdd", (member) => {
const guild = member.guild;
if (!newUsers[guild.id]) newUsers[guild.id] = new Discord.Collection();
newUsers[guild.id].set(member.id, member.user);
if (newUsers[guild.id].size > 10) {
const userlist = newUsers[guild.id].map(u => u.toString()).join(" ");
guild.channels.find(channel => channel.name === "general").send("Welcome our new users!\n" + userlist);
newUsers[guild.id].clear();
}
});
client.on("guildMemberRemove", (member) => {
const guild = member.guild;
if (newUsers[guild.id].has(member.id)) newUsers.delete(member.id);
});```
Your problem lies within your third line of code. What you do here is create a new Collection everytime someone joins. This means that the collection can never be > 10 because it's always 1.
To fix this simply create the Collection at the top of your index file and not inside a listener.
On a sidenote, I assume you want to create the collection to keep track of who is in your discord server. Thats not really a good idea because it will be newly created every time the bot restarts and since it only gets updated when someone joins or leaves it won't be accurate. A lot easier to use is the guild.members collection.
EDIT: This is how you would do something like that
client.on('guildMemberAdd', member => {
const guild = member.guild;
const userlist = guild.members.cache.map(u => `\n${u.user.username}: ${u.user.id}`);
guild.channels.find(channel => channel.name === "general").send("Welcome our new user!\n Users" + userlist);
})
Please keep in mind that this will return all members of the guild. You can only use the guildMembersChunk listener to welcome multiple members and all of those need to come from the same guild which is rather unlikely.

How to return reactor who doesnt have spesicific role

Hello so I wanted to make my giveaway cannot be entered, unless he has the requirement role
here's my code
const filter = (reaction) => {
reaction.emoji.name === '๐ŸŽ‰'
};
let collector = message.createReactionCollector(filter, {time: ms(giveawayDuration)});
collector.on('collect', (reaction, user) => {
if (reaction.emoji.name === '๐ŸŽ‰' && !reaction.member.roles.cache.has(`${roleRequirement}`)) {
console.log('he is not in')
reaction.users.remove()
}
})
I don't know why, but it doesnt remove the user's react, how do I change that
The filter is a function that should return a boolean or Promise<boolean> which in this case isn't returning anything, so probably isn't collecting any message either. (maybe you confused the brackets, you should be using in this scenario () instead of {} to specify return, or use any)
Anyway, as you want to delete any reaction that isn't :tada: you might as well just return true to collect every reaction.
There is no such object as MessageReaction.members. You will have to get the GuildMember by other means. Like:
const member = message.guild.members.cache.get(user.id);
Your final command may look like this:
const filter = reaction => true;
const collector = message.createReactionCollector(filter, { time: duration });
collector.on('collect', (reaction, user) => {
const member = message.guild.members.cache.get(user.id);
if(reaction.emoji.name !== '๐ŸŽ‰' || !member.roles.cache.has(`roleId`)) {
reaction.users.remove(user);
}
});
If I misunderstood you and you want to keep the reactions other than :tada: and filters only those in which the member isn't in the role, change the filter and remove the verification if it's :tada: in the if statement as it becomes not necessary:
// filter become this
const filter = reaction => reaction.emoji.name === '๐ŸŽ‰';
// there's no need for the :tada: if anymore
if(!member.roles.cache.has(`roleId`)) {
member is not a property of MessageReaction. Instead, use the Guild.member() function, and pass the user parameter as an argument.
if (
!message.guild.member(user).roles.cache.has(roleRequirement)
) // I removed the emote verification, as it is already included in the filter
Also, by emitting the user parameter in reaction.users.remove(), the argument defaults to this.reaction.message.client.user, which I don't think is what you want. Instead, pass the user.id property.
reaction.users.remove(user.id)
Another problem might be with the roleRequirement variable. Since you are using Collection.has(), make sure roleRequirement is the role ID, not the role object.

Discord JS // Trying to add role by reacting to the message

bot.on('messageReactionAdd', async (reaction, user) => {
// Define the emoji user add
let role = message.guild.roles.find(role => role.name === 'Alerts');
if (message.channel.name !== 'alerts') return message.reply(':x: You must go to the channel #alerts');
message.member.addRole(role);
});
Thats the part of my bot.js. I want the user to react in a certain channel and receive role Alerts
You haven't really stated what the problem is, what works and what doesn't work but I'll take a wild stab at some parts which catch my eye.
For starters you are calling properties on the variable message whilst in the code you supplied, you didn't create/set a variable named message. My guess is that you want the message to which a reaction has been added. To do that you have to use the MessageReaction parameter which is supplied in the messageReactionAdd event as reaction.
From there you can replace message.<something> with reaction.message.<something> everywhere in the code you supplied.
Something also to note is that you add the role Alerts to message.member. This won't work how you want it to, since it will give the Alerts role to the author of the original message.
What (I think) you want to do, is fetch the user who just reacted with the emoji and assign them the Alerts role. You'll have to find the member in the guild first and then assign them the Alerts role. To do this you'll have to use the User parameter and find the correct Member because you can't add a role to a User object but you can to a Member object. Below is some code which should hopefully put you on the right track.
// Fetch and store the guild (the server) in which the message was send.
const guild = reaction.message.guild;
const memberWhoReacted = guild.members.find(member => member.id === user.id);
memberWhoReacted.addRole(role);
You are using message.member variable despite not defining message.
Any of these methods won't really work in v12 so I updated it for someone else searching.
If you find any mistakes be sure to make me aware of it.
const Discord = require('discord.js');
const client = new Discord.Client({ partials: ['MESSAGE', 'CHANNEL', 'REACTION'] }); //partials arent really needed but I woudld reccomend using them because not every reaction is stored in the cache (it's new in v12)
const prefix = "-";
client.on('messageReactionAdd', async (reaction, user) => {
if (reaction.partial) { //this whole section just checks if the reaction is partial
try {
await reaction.fetch(); //fetches reaction because not every reaction is stored in the cache
} catch (error) {
console.error('Fetching message failed: ', error);
return;
}
}
if (!user.bot) {
if (reaction.emoji.id == yourEmojID) { //if the user reacted with the right emoji
const role = reaction.message.guild.roles.cache.find(r => r.id === yourRoleID); //finds role you want to assign (you could also user .name instead of .id)
const { guild } = reaction.message //store the guild of the reaction in variable
const member = guild.members.cache.find(member => member.id === user.id); //find the member who reacted (because user and member are seperate things)
member.roles.add(role); //assign selected role to member
}
}
})
Here's a quick answer, though way too late. So I'll be updating the answer with Discord.js v.12.x (or the same as Discord.js Master)
bot.on('messageReactionAdd', async (reaction, user) => {
//Filter the reaction
if (reaction.id === '<The ID of the Reaction>') {
// Define the emoji user add
let role = message.guild.roles.cache.find((role) => role.name === 'Alerts');
if (message.channel.name !== 'alerts') {
message.reply(':x: You must go to the channel #alerts');
} else {
message.member.addRole(role.id);
}
}
});

Discord.js - Getting users last activity?

I'm trying to find out if its possible to get the time/information of users last activity retrospectively using discord.js
Say I have something like
client.guilds.find('id', 'SERVER ID').fetchMembers().then(members => {
const role = members.roles.find('name', 'Newbies')
role.members.forEach(member => {
console.log(member.user.lastMessage) // null
})
})
Unless the member has posted, since the client is listening, the lastMessage is always null.
Is there a way to find the last activity? or a workaround, like a query to return all the users messages, which I can then take the most recent one from?
Effectively I want to know what date/time the user last posted so we can monitor non-contributing accounts.
Thanks
After looking thought the documentation I didn't find something neither so I came up with a manual search function.
Basically, it will scan every channels until finding a message from X user, or the end of the messages in the channel. It then compare the last messages of the users from every channels and print the last one.
It can be very long if the user hasn't write since a long time. Of course, you have to check lastMessage before trying this.
I would add a time limit maybe. Because if you have thousand of messages, the function will run eternally.
You can stop the function if the last message found is in the accepted time to not be kick/do whatever.
I made the search stop if the first message found in the pack of fetched messaged is older than the ban limit, however, if the first message is not older, remember that it means for the other, so we still need to check them (it can be avoided by checking the last message of the pack as well).
async function fetchMessageUser(chan, id, res) {
let option = {};
if (typeof res !== 'undefined'){
option = {before: res.id};
}
return await chan.fetchMessages(option)
.then(async msgs => {
if (msgs.size === 0){
return {continue: false, found: false};
};
if ((Date.now() - (msgs.first().createdTimestamp)) > 86400000 ) { // 1 day
return {continue: false, found: false};
}
let msgByAuthor = msgs.find(msg => {
return msg.author.id === id;
});
if (msgByAuthor === null){
return {continue: true, id: msgs.last().id};
} else {
return {continue: false, found: true, timestamp: msgByAuthor.createdTimestamp};
}
})
.catch(err => console.log('ERR>>', err));
}
client.on('message', async (msg) => {
let timestamp = [];
for (let [id, chan] of msg.guild.channels){
if (chan.type !== 'text'){ continue; }
let id = '587692527826763788'; // id of the user, here a non verified account
let res;
do {
res = await fetchMessageUser(chan, id, res);
} while(res.continue);
if (res.found) {
timestamp.push(res.timestamp);
}
}
console.log(timestamp);
let first = timestamp.sort((a,b) => (b-a))[0];
console.log(new Date(first));
});
A better variant would be to run it for an array of users, checking all 50 last messages from every channel, and associating each users with his most recent messages if he wrote one, and doing this until all the messages in all the channels are too old to avoid a kick/whatever. And then do something for all the users who don't have an associated messages.
I think what you need is one of Discord's built in features, namely: pruning. This feature will grab inactive members and lets you kick them. Luckily, discord.js has an API call for it and even lets you get the number of members first without actually kicking them by setting the dry parameter to true. The feature will also allow you to specify the amount of days a user has to be inactive.
Have a look at the docs: https://discord.js.org/#/docs/main/stable/class/Guild?scrollTo=pruneMembers
Hope that helps out!

Categories