How to get all server member IDs? Discord.js - javascript

I am new to Discord.js which means I mostly copy the code and I've got a problem. I cant seem to find a way to get all my server member IDs in the serverUsers array. I know there are already a lot of questions that have answers, but the solutions don't seem to work for me, this one put in only the bot's, that is running the script, user ID.
client.on("ready", readyDiscord);
function readyDiscord() {
console.log("Authentication complete");
console.log("Going online");
var serverUsers = []
const MyServer = client.guilds.cache.get("MyServerID");
MyServer.members.cache.forEach(member => serverUsers.push(member.user.id));
console.log(serverUsers);
}
Thanks

You can easily do this without going through such trouble
const serverUsers = client.guilds.cache.get("Server ID").members.cache.map(member => member.id);
console.log(serverUsers);
Docs:
members

Related

Discord.js - How do you use guildUpdate from the client class?

I’m setting up logging for my Discord bot and I don’t know how to use guildUpdate from the client class. The docs say to use the oldGuild and newGuild parameters, but how do you actually check what is being changed (like the server name, region, etc) and display that change? Here’s an example from another bot: https://imgur.com/a/Hj1yhZq
Any help would be appreciated. Thanks!
In JavaScript, there is no built in function to compare the two guild objects, so you would have manually look for the differences in the different parameters. For example:
client.on("guildUpdate", (oldGuild, newGuild) => {
if (oldGuild.description !== newGuild.description) {
let embed = new Discord.MessageEmbed()
.setTitle("changes")
.setDescription(`Description changed from ${oldGuild.description} to ${newGuild.description}`)
somechannel.send(embed)
}
});
guildUpdate gives you 2 parameters. One is the guild before it was updated, and one is the guild after it was updated.
To see which value has been updated, you can check if the new value equals the old value. If it doesn't, it has been updated.
client.on("guildUpdate", (oldGuild, newGuild) => {
if (oldGuild.name !== newGuild.name) console.log(`The guild name has been updated. From ${oldGuild.name} to ${newGuild.name}`);
if (oldGuild.region !== newGuild.region) console.log(`The region has been updated. From ${oldGuild.region} to ${newGuild.region}`);
// etc
});

Issues Looping members in an Async function, Discord.js

I am new here, and myself and my friend are very stuck with our Discord bot code where we are trying to have my discord bot check if someone is "streaming" to twitch by looping through members that have a Role called "Twitch Streamer". The problem is, we know the first half of the code works which checks if the owner, Me, is streaming, but for some reason, the second half of the code stopped working and is spitting out something in the console of "undefined". If I paste the code, could someone be willing to help us out? We are completely lost and running out of ideas.
My approach on task like this would be like this Discord.js v12 approach
Filter out the users with specific role - loop iteration decrease - optimization.
const streamers = server.members.cache.filter(user => user.roles.cache.has(StreamerRole);
To check if streamer is currently streaming at a time we are using loop. We are pushing streamers that are streaming to array with their IDs (d.js snowflakes).
const activeStreamers = [];
const IS_STREAMING = new Discord.Activity({
type: 'STREAMING',
...rest
})
streamers.forEach(streamer => {
const streamerActivity = streamer.presence.activities.has(IS_STREAMING);
if(streamerActivity) activeStreamers.push(streamer.user.id);
}

(Discord.js) Select a random user from voice channel

I'm trying to make a bot for voice applications for my server. I want it to have a command that picks a random user from a voice channel. I tried to do it in a few different ways, but none of them have worked, please help.
(edit)
Here is my not pretty code
else if (args[0].toLowerCase() === 'pick') {
const GuildMember = message.guild.members;
const channels = message.guild.channels.filter(c => c.ID === config.voiceChannel && c.type === 'voice');
const toCheck = Discord.Collection.array(channels.members);
message.reply(message.author + ', now pick ' + toCheck.random());
}
message.delete().catch(O_o=>{});
(another edit)
This is an error i'm getting "TypeError: Discord.Collection.array is not a function"
Your error is telling you that you are not using Collecion.array() properly. It looks as though you are trying to use it as a constructor or factory method, but it's a converter function. It needs an existing collection to work.
Secondly, your filter method will return a collection. Since you are filtering by an ID, it should be a collection of 1, but still a collection. Collections do not have a members property so channels.members is undefined. You need to extract the first (and what should be only) channel from the collection.
Fortunately, all of this can be handled by replacing a single line. You don't even need to convert it to an array because VoiceChannel.members is also a collection, and Collection has a random function of it's own.
const toCheck = channels.first().members;
On another note, because you are filtering the channel by it's unique ID, filtering by c.type is redundant. It will always be a voice channel unless you misconfigured config.voiceChannel.
Update
It also occured to me after posting that you could use this instead of your filter:
const channel = message.guild.channels.get(config.voiceChannel);
This returns the single channel, rather than a collection, by ID and is better because you aren't iterating over the collection and it simplifies the code. If you do this you don't need to use the changed line above at all, remove that line and use the following:
message.reply(message.author + ', now pick ' + channel.members.random());
Tarazed, thanks for your answer it helped me to figure it out.
For people that want to pick a random person from a voice channel too, here is the code that you can put in you command:
const GuildMember = message.guild.members;
const channel = message.guild.channels.get('voiceChannelID'); //replace voiceChannelID with your voice Channel ID
let toCheck = channel.members;
message.reply('a random person: ' + toCheck.random());

Insert multiple entries to SQL Server with Node.js

I am rewriting an old API for which I am trying to insert multiple values at once into a MSSQL-Server (2008) database using the node module mssql. Now, I am capable of doing this somehow, but I want to this following best practices. I've done my research and tried a lot of things to accomplish my target. However, I was not able to find a single solution which works just right.
Before
You may wonder:
Well, you are rewriting this API, so there must be a way this has been done before and that was working?
Sure, you're right, it was working before, but... not in a way I'd feel comfortable with using in the rewrite. Let me show you how it was done before (little bit of abstraction added of course):
const request = new sql.Request(connection);
let query = "INSERT INTO tbl (col1, col2, col3, col4) VALUES ";
for (/*basic for loop w/ counter variable i*/) {
query += "(1, #col2" + [i] + ", #col3" + [i] + ", (SELECT x FROM y WHERE z = #someParam" + [i] + "))";
// a check whether to add a comma or not
request.input("col2" + [i], sql.Int(), values[i]);
// ...
}
request.query(query, function(err, recordset) {
// ...
}
While this is working, again, I don't quite think this could be called anything like 'best practice'. Also this shows the biggest problem: a subselect is used to insert a value.
What I tried so far
The easy way
At first I tried the probably easiest thing:
// simplified
const sQuery = "INSERT INTO tbl (col1, col2, col3, col4) VALUES (1, #col2, #col3, (SELECT x FROM y WHERE z = #col4));";
oPool.request().then(oRequest => {
return oRequest
.input("col2", sql.Int(), aValues.map(oValue => oValue.col2))
.input("col3", sql.Int(), aValues.map(oValue => oValue.col3))
.input("col4", sql.Int(), aValues.map(oValue => oValue.col4))
.query(sQuery);
});
I'd say, this was a pretty good guess and actually working relative fine.
Except for the part, that ignores every item after the first one... which makes this pretty useless. So, I tried...
Request.multiple = true
...and I thought, it would do the job. But - surprise - it doesn't, still only the first item is inserted.
Using '?' for parameters
At this point I really started the search for a solution, as the second one was only a quick search in the modules documentation.
I stumbled upon this answer and tried it immediately.
Didn't take long for my terminal to spit out a
RequestError: Incorrect syntax near '?'.
So much for that.
Bulk inserting
Some further research led to bulk inserting.
Pretty interesting, cool feature and excellent updating of the question with the solution by the OP!
I had some struggle getting started here, but eventually it looked really good: Multiple records were inserted and the values seemed okay.
Until I added the subquery. Using it as value for a column declared didn't cause any error, however when checking the values of the table, it simply displayed a 0 as value for this column. Not a big surprise at all, but everybody can dream, right?
The lazy way
I don't really know what to think about this:
// simplified
Promise.all(aValues.map(oValue => {
return oPool.request().then(oRequest =>
oRequest
.input("col2", sql.Int, oValue.col2)
.input("col3", sql.Int, oValue.col3)
.input("col4", sql.Int, oValue.col4)
.query(sQuery);
});
});
It does the job, but if any of the request fails for whichever reason, the other, non-failing inserts, will still be executed, even though this should not be possible.
Lazy + Transaction
As continuing even if some fail was the major problem with the last method, I tried building a transaction around it. All querys are successful? Good, commit. Any query has an errpr? Well, just rollback than. So I build a transaction, moved my Promise.all construct into it and tried again.
Aaand the next error pops up in my terminal:
TransactionError: Can't acquire connection for the request. There is another request in progress.
If you came this far, I don't need to tell you what the problem is.
Summary
What I didn't try yet (and I don't think I will try this) is using the transaction way and calling the statements sequentially. I do not believe that this is be the way to go.
And I also don't think the lazy way is the one that should be used, as it uses single requests for every record to insert, when this could somehow be done using only one request. It's just that this somehow is, I don't know, not in my head right now. So please, if you have anything that could help me, tell me.
Also, if you see anything else that's wrong with my code, feel free to point it out. I am not considering myself as a beginner, but I also don't think that learning will ever end. :)
The way I solved this was using PQueue library with concurrency 1. Its slow due to concurrency of one but it works with thousands of queries:
const transaction = sql.transaction();
const request = transaction.request();
const queue = new PQueue({ concurrency: 1 });
// being transaction
await transaction.begin();
for (const query of queries) {
queue.add(async () => {
try {
await request.query(query);
} catch (err) {
// stop pending transactions
await queue.clear();
await queue.onIdle();
// rollback transaction
await transaction.rollback();
// throw error
throw err;
}
});
}
// await queue
await queue.onIdle();
// comit transaction
await transaction.commit();

How to create a command that only who have one of the roles can use?

let staffrole = ['383874699941117952', '149622819158884353', '149622998180036608'];
How do you make a command that only people who have one of the roles can use it?
Thank you!
What you can do is that, on a message event, you run the command, and you can check the member's roles for one of the ones in the array.
Heres what that would look like:
client.on("message", msg => {
if(command === "whateverItIs") {
let staffrole = ['383874699941117952', '149622819158884353', '149622998180036608'];
for(i=0;i<staffrole.length;i++) {
if(msg.member.roles.filter((role) => role.id == staffrole[i]).size > 0) {
//run the code
return;
}
}
}
})
On a message event, with the determined command, the bot will check through each of the staff roles and if the message author 's roles includes one of the staffrole's then the command will run.
I would recommend doing something like this:
First, set your command name in the client's message listener:
// ... (in client message listener)
switch(command) {
case '<your command>':
runIfRoleIncluded(message);
break;
}
Next, get the role Id from the message that was sent and check if that message's role Id is in your staffrole array:
function runIfRoleIncluded(message) {
let rolesCollection = message.member.roles;
let staffrole = ['383874699941117952', '149622819158884353', '149622998180036608'];
// only 1 member can send a message, so get role collection's first key (id)
let messageRoleId = rolesCollection.firstKey();
// check if message's role id is in staff role array
if (staffrole.includes(messageRoleId)) {
// do stuff here
// ...
}
}
The .roles property of the .member object is a Collection, which is why you have to use the .firstKey() method. You can also turn the Collection into a normal js Array, but the way I outlined above is easier.
Started looking at this... Don't know the discord space very well but got an example bot and with a hello world ping, also found this pretty sweet Github gist that lays out fairly well how to build what amounts to a command switch statement. Making a lot of guesses here -- as a note for future questions it would be very helpful for you to add in some code on what you are trying to do -- a single variable set to an array isn't much to go on...
After Reading what #Raymond Zhang said, because, yeh that's what I was doing...
this is straight out of the Github gist I linked ->
...
if(command === "kick") {
if(!message.member.roles.some(r=>["Administrator","Moderator"].includes(r.name)) )
return message.reply("Sorry, you don't have permissions to use this!");
...
I have tested this and it works great, although it checks against the roles name not a number. It would help if you updated you answer to explain your process. More info = better answer. :)

Categories