I want to create a member count that excludes all bots.
I've talked to a friend that told me the code looks to be correct. But it only gives me a count of 0. Perhaps you have better eyes than him!
module.exports = async(client) => {
const guild = client.guilds.cache.get('912706237806829598');
setInterval(() => {
const memberCount = guild.members.cache.filter(member => !member.user.bot).size;
const channel = guild.channels.cache.get('959556115383861298');
channel.setName(`╭👥・Members: ${memberCount.toLocaleString()}`);
console.log('Updating Member Count');
console.log(memberCount);
}, 5000);
}
Thanks.
Tried my code and expect a value representing the amount of humans on my server, but only got 0.
There are two potential problems with your code. First, you're not fetching the member collection and are instead interacting with the cache; if I remember correctly the cache usually only shows you online members and not offline ones. Second, the dreaded issue of rate limits.
The first issue is an easy fix. You just need to fetch the members instead of using the cache before all of your setInterval code:
setInterval(async () => {
const memberCount = (await guild.members.fetch()).filter(member => !member.user.bot).size;
const channel = guild.channels.cache.get('959556115383861298');
channel.setName(`╭👥・Members: ${memberCount.toLocaleString()}`);
console.log('Updating Member Count');
console.log(memberCount);
}, 5000);
That was probably causing the issue you were having. However, there's an additional problem here, one you will run into if you use this code. Rate limits. The normal rate limit for most actions in the Discord API is, if I remember correctly, 10,000 requests per 10 mins. However, the rate limit for changing the name of a channel is just 2 requests per 10 mins (last I checked; I don't know if they've increased this limit since last year). Your code is changing the name of a channel every 5 seconds. In just 10-15 seconds, you'll surpass the rate limit.
I have created channels that keep track of server stats for my bots in the past as well, and ran into this issue when I was doing so. Luckily, I brewed up a way to bypass the rate limit issue back when I was creating my own stats system. Though changing channel names has an incredibly low rate limit, fully creating channels has the normal rate limit of 10,000 requests per 10 mins. Therefore, instead of changing the channel name directly, you could instead: a) clone the channel; b) change the clone's name while creating it; c) set the clone's position in the channel list to the same position as the actual channel; d) delete the original channel. The cloned channel will keep all of the perms, settings, etc of the original. This all assumes, of course, that the original channel isn't a text channel with important messages or such inside, as those messages would all disappear with this method. I used voice channels for my system.
Here's how this bypass could look:
setInterval(async () => {
const memberCount = (await guild.members.fetch()).filter(member => !member.user.bot).size;
const channel = guild.channels.cache.get('959556115383861298');
const pos = channel.position;
const clone = await channel.clone({
name: `╭👥・Members: ${memberCount.toLocaleString()}`
});
await clone.setPosition(pos);
console.log('Updating Member Count');
console.log(memberCount);
await channel.delete();
}, 5000);
With this approach, rate limits are no longer an issue. Note that this can still be further improved, however. For example, inside the interval you could check if the member count has changed from the previous count, and only modify the channel name if and only if the count has changed. The entire approach potentially could be changed as well; perhaps you could use guildMemberAdd and guildMemberRemove events to track when the member count increases or decreases, and only modify the name when one of those happens (and only if the member being added/removed is not a bot). But whether to add either of those optional potential improvements is up to you.
Note: Please try this code out on a test channel before using it on the actual channel you are using, just to be safe.
If you wanted to get to all members including the bot you can use memberCount
const totalCount = guild.memberCount
If you wanted to get only the members size
const memberCount = guild.members.cache.filter(member => !member.user.bot).size;
And if you only wanted to get the bot size
const botCount = guild.members.cache.filter(member => member.user.bot).size;
Full code:
const guild = client.guilds.cache.get('912706237806829598')
const memberCount = guild.members.cache.filter(member => !member.user.bot).size;
const botCount = guild.members.cache.filter(member => member.user.bot).size;
const totalCount = guild.memberCount
console.log(memberCount, botCount, totalCount)
Related
I am building a chat app in react using Firebase Firestore as backend database.
I get recent 25 messages in useEffect hook as
useEffect(() => {
const q = query(
collection(db, 'messages'),
orderBy('createdAt', 'desc'),
limit(25)
);
return onSnapshot(q, (snapshot) => {
setData(
snapshot.docs.map((doc) => {
console.log('document read');
return { ...doc.data(), id: doc.id };
})
);
});
}, []);
But this operation results in 25 document reads on page load and 50 additional on sending a message.
If more users are connected, 25 request per user happen on single message send by any user.
Is there any way to reduce the reads?
Complete code:- https://github.com/Puneet56/Converse
You don't get 25 reads in a running query with a limit of 25 resulst if you get a new one inside. As the documentation says:
When you listen to the results of a query, you are charged for a read
each time a document in the result set is added or updated. You are
also charged for a read when a document is removed from the result set
because the document has changed. (In contrast, when a document is
deleted, you are not charged for a read.)
Also, if the listener is disconnected for more than 30 minutes (for
example, if the user goes offline), you will be charged for reads as
if you had issued a brand-new query.
As stated in the docs you will be charged only for the one that is added and the one that get's out of the query limit because a new one got in. So you get probably only 2 reads per new message. I think that is a reasonable amount. I can't any way to reduce this amount in a chat App. Even if you would increase the query limit only your inital reads (if it's a fresh read with no old or empty cache) will increas to but the reads while listening would stay the same.
I'm trying to create a slash command using discord.js v13 to change the names of voice channels. I am using this code to do this :
module.exports = {
data: new SlashCommandBuilder()
.setName('name')
.setDescription('Set name for your voice channel')
.addStringOption(option => option.setName('name').setDescription('Enter your name').setRequired(true)),
async execute(interaction) {
const name = interaction.options.getString('name');
if (!interaction.member.voice.channel) await interaction.reply('Error not in a voice channel!');
else {
await interaction.member.voice.channel.setName(name);
await interaction.reply('Done!');
}
},
};
This code is fine and makes the job done. But as you know I can change the voice channel's name only 2 times per 10 minutes because of the limit rate. So if a user tries to change the voice channel's name for the third time, I won't get any error on the console, and discord js will queue this request for later and will do it after 10 minutes. But the user gets this error on discord: This interaction failed.
I want to check if there was a rate limit for my request, and if is, don't send the request and just reply to the user. Is this possible?
There is no inherent functionality that is able to handle the situation in the way you want it to, but the problem is soluble using regular old JavaScript. For example, you could use an integer to indicate how many times the command has been used and use setTimeout() to decrement it 10 minutes after the command was called. That way you can check if the int is equal to 2 in which case you skip the .setName().
There are undoubtedly other ways to implement the same or similar behavior, but, to answer your question, unfortunately the discordjs/voice library does not provide any simple way to do it.
command(client, 'explore', (message) => {
const name = message.content.replace('!explore ', '')
message.guild.channels
.create(name, {
type: 'text',
})
.then((channel) => {
const catagoryId = '847950191108554762'
channel.setParent(catagoryId)
})
const channelId = message.guild.channels.id
console.log(channelId)
})
I'm having an issue with saving the newly created channel's id so that way when the channel is done being used instead of typing the name, I can use the id to be inputted into the deletion command. Basically creating temp. predefined or newly found channels and then being able to log the Id and then with a deletion command be able to remove it without writing a name of the channel.
const channelId = message.guild.channels.id
console.log(channelId)
this was my attempt to retrieve/store then log it. I've been trying to do this on my own but have hit a roadblock.
To get a channel’s ID, just type <Channel>.id. There are multiple ways to get a channel, here is an example:
const channelID = <Message>.channel.id;
console.log(channelID);
The above example gets the ID of the message's channel
Also, <guild>.channels.id returns undefined, since guild.channels is a collection of multiple channels, and you can’t just get the id like that. This would be useless if you are trying to get the channel ID since you would have to fetch/get the channel by ID, or use a find method, which could return the wrong channel if there are multiple channels the bot has seen that goes with your condition, so what you are trying to do, it doesn’t make much sense.
im making discord bot with discord.js v12 and im trying to give role when member has 10 invites
i found invitation code from here and modified it:
if(message.channel.type != "dm" && message.channel.id === '768475421953915093') {
message.delete();
var user = message.author;
message.guild.fetchInvites()
.then
(invites =>
{
const userInvites = invites.array().filter(o => o.inviter.id === user.id);
var userInviteCount = 0;
for(var i=0; i < userInvites.length; i++)
{
var invite = userInvites[i];
userInviteCount += invite['uses'];
}
if(userInviteCount >= 10){
const guildMember = message.member;
guildMember.addRole('767542365812886656');
message.author.send(`you have access now`)
}else {
message.author.send(`sry u have ${userInviteCount} invitations. u need at least five`)
}
}
)
}
yes, it works but you can trick the system easily. If you keep rejoining from the server, the invite counter increases. How can i stop that?
To do this, you will need an external database since Discord API doesn't provide any information about who used that invite and how many times.
ALERT: It's a simple feature but can be very hard for people that don't have any coding experience, so if it's your first project, don't give up and keep trying! :D
You can use MongoDB Atlas, which is very simples and free.
And the logic will be very simple:
1 - Create a collection to store the invites data, you can do something like in this example
2 - Create a collection (you can give any name, but in this example, I will refer to this collection as new_members) to store the new members data, it should have the fields serverId (to store the guild ID), userId (to store the user ID) and inviteCode (to store the invite code or url)
3 - When a user joins the discord server, it will trigger the "guildMemberAdd" event, and you will get the invite that was used (with the example that I give in the first step), and create a new doc in the new_members collection.
4 - After creating this new data, you will search for all the docs from the same invite, and filter them by userId (there are many ways to do this, this is one of them).
5 - With this array of docs, you can use his length to determine how many people were invited, and give the role.
VERY SIMPLE EXAMPLE, THAT SHOULDN'T BE USED:
DiscordClient.on("guildMemberAdd", async (member) => {
/**
* https://github.com/AnIdiotsGuide/discordjs-bot-guide/blob/master/coding-guides/tracking-used-invites.md
* Here you will get the invite that was used, and store in the "inviteCode" variable
*/
const inviteCode = await getInviteUsed()
await NewMemberRepository.insert({
serverId: member.guild.id,
userId: member.id,
inviteCode: inviteCode
});
const joinedMembersFromTheInvite = await NewMemberRepository.find({
inviteCode: inviteCode
});
/**
* https://stackoverflow.com/questions/43374112/filter-unique-values-from-an-array-of-objects
* Here you will filter the results by userId
*/
const invitesWithUniqueUsers = filterJoinedMembersByUserId(joinedMembers)
if (invitesWithUniqueUsers >= REQUIRED_INVITES_QTD) {
/**
* Gives the role to the creator of the invite
*/
}
});
Hope it helps, good luck! :D
OBS:
Yes, Discord API provides how many times an invite was used, but not how many a specific person used this invite.
In the examples, I used something like TypeORM, but if you are using JavaScript instead TypeScript, you can use Mongoose
I'm developing a bot where at a set interval, like every hour the bot will pick a random user and remove a role from them, it will be using the positioning on the side to do this. The problem was I couldn't really find a function in Discord.JS that had a property for a guild member's position on the side.
I've tried finding other bots that do this and try to mimic them, but I didn't find anything.
I double checked the documentation but didn't find anything.
var thatguild = message.guild.id.find(`576463944298790929`);
thatguild.fetchMembers();
setInterval(function() {
let sizes = thatguild[Math.floor(Math.random() * thatguild.members.size())];
if(thatguild.member.id == `userid1` || `userid2`)
return(thatguild.member.removeRole('roleid'))
}, 1000)
I expect a bot where it randomly picks a user by the position, and remove a role from the member.
What I actually get is code and no way of knowing if it works or not.
Explanation:
Let's go through what we need to do if we want to accomplish what you want.
Acquire the desired guild.
A collection of guilds (GuildStore in master) a client is handling is available with Client.guilds.
To find a value by its key, use Map.get().
To find a value by another property or testing of an expression, use Collection.find().
To simply use the guild the message was sent in within a message event, use message.guild.
Acquire the desired role.
A collection of roles within a guild (GuildMemberRoleStore in master) a client is handling is available with Guild.roles.
Refer to the options previously listed to obtain a value from the collection.
Select a random member.
A collection of a guild's members (GuildMemberStore in master) is available with Guild.members.
Before selecting a member, the possible choices need to be limited to those with the role, so use Collection.filter().
To select a random value from a collection, use Collection.random().
Remove the role from the member.
To remove a role from a member, use GuildMember.removeRole() (GuildMember.roles.remove() in master).
If something goes wrong, make sure to catch the returned promise with a try...catch statement or catch() method.
Set an interval.
To set an interval, use setInterval().
In case something goes wrong, assign it to a variable so it can be cleared later.
Code:
function roleRoulette(guild, role) {
const possible = guild.members.filter(m => m.roles.has(role.id));
if (possible.size === 0) return clearInterval(interval);
const member = possible.random();
member.removeRole(role)
// MASTER: member.roles.remove(role)
.then(() => console.log(`Removed ${role.name} from ${member.tag}.`))
.catch(err => {
console.error(err);
clearInterval(interval);
});
}
const guild = client.guilds.get('576463944298790929');
if (!guild) return;
const role = guild.roles.get('576464298088333323');
if (!role) return;
const interval = setInterval(roleRoulette(), 60 * 1000, guild, role);
Discord.js Docs:
stable
master
It's honestly a bit hard to understand what you exactly want. I understand you want the bot to remove a random role from a random member at a set interval. Also, you want to do that based on the role hirarchy somehow, I'm guessing the highest role first? If that is the case, what you could try is this:
let randomMember = message.guild.members.cache.random()
if(randomMember.roles.cache.length === 0) return
let roleToRemove = randomMember.roles.cache.first()
for (let r in randomMember.roles.cache){
if(r.position < roleToRemove.position) roleToRemove = r;
}
await randomMember.roles.remove(roleToRemove)
Well, no matter what, if you are looking to remove roles based on their position then role.position will be required for your code in some way.
https://discord.js.org/#/docs/discord.js/main/class/Role?scrollTo=position
Good luck :)
Edit: Don't forget to exclude bots and bot roles from the removal when you implement something like this!
This is probably what I wanted:
Every hour, the bot picks a random user from the entire guild, removes a specific role from them, and repeats.
It's still hard to understand what past-me actually wanted, but here's another answer.
I don't know what version of D.JS was hot at the time, but I'm gonna use v9 in my answer.
You want an interval removing members every hour, so 1000 * 60 * 60 means 1000 milliseconds, 60 times -> 60 seconds(1 minute) * 60 times -> 1 hour.
This function inside the interval needs* to be async, so we can request a guild with ALL the members.
Then, we get the members collection and filter it in a way to remove users that don't have the role we're looking for OR if they are in an ignore list.
If no members have a role, don't do anything.
Else, pick a random member and remove the role. Repeat.
var ignore = ['...'];
var guildSel = client.guilds.get('...');
var role2Remove = '...';
setInterval(async () => {
var guild = await guildSel.fetchMembers();
var members = guild.members.filter(m=>m.roles.has(role2Remove) && !ignore.includes(m.id));
if(members.size > 0) {
var randUsr = members.random();
randUsr.removeRole(role2Remove);
} else return;
}, 1000 * 60 * 60);