Use plain text instead of mentions to define user - javascript

Here is the code for the command:
if (command === "avatar") {
let user = message.mentions.users.first() || message.author;
let embed = new Discord.RichEmbed()
.setAuthor(`${user.username}`)
.setImage(user.displayAvatarURL)
.setColor(0x842cfc);
message.channel.send(embed),
}
This command shows the avatar of a specified user, but with how it is now, you have to tag the specified user (making them receive a notification) to show the avatar. Right now the command is .avatar #someuser but I want the command to be .avatar someuser, so it doesn't mention them.
Also, I'm pretty sure I need to use client.users.find() somehow but idk how to. Thank you for any help!

I would use Guild.members.find(). Since that contains the members, you will need to check whether the argument the user wrote matches the .nickname or the .user.username. To simplify things a little I will use .displayName instead of .nickname, so that I can always work with strings.
// argument: the string after the command, I don't know how you're currently getting it
let user = message.guild.members.find(m => [m.displayName.toLowerCase(), m.user.username.toLowerCase()].includes(argument.toLowerCase()));

Related

Hiding messages from a discord user through javascript - Soft blocking

Blocking a user is a good feature of discord, but often enough I find myself wanting to block someone temporarily: not see their messages, but only for a certain period of time and without them knowing that I did so. Theoretically such a thing should be possible to do. I found a userscript that you are supposed to paste into the discord console once you start discord that serves the purpose of hiding messages from blocked users completely. Using the same principle, if instead of searching for blocked messages I could search for messages from a specific user, and then hide them, I could soft-block them for the duration of my current session. So the idea of this is to search all messages in a channel, check if a message was posted by a certain user that I want blocked, and if so, hide it.
To do so I have to figure out how to tie messages to their authors. There appears to be a different class for each new message that a user sends. But when a user sends multiple messages in a row, the classes are different. The first message contains the link to the user's avatar, which in turn contains the user's unique discord ID that he could be identified with. But his follow-up messages don't contain his avatar. And I can't find anything else that I could use to identify a user with except for that.
In the image above the classes that have groupStart contain the avatar links, whereas the classes without it are the follow-up messages from the same user that do not. Like so:
What kind of a loop would be able to include both the messages with some specified avatar, and the follow up messages without it?
As I see it, here's how you'll have to do it:
first identify the groupStart message, and then keep looking at the next messages until you reach a new groupStart message.
Luckily JavaScript keeps elements in the order that they're displayed when you use document.getElementsByClassName so you should just be able to set a variable to true when you hit the target, and then back to false when you hit a new person. This is my code:
function block(userid) {
return setInterval(() => {
let deleteMessages = false;
[...document.getElementsByClassName('message-2qnXI6')].forEach(i => {
if(i) {
if(i.className.match('groupStart-23k01U')) {
if(([...i.children].find(i => i.className.match('contents-2mQqc9')).children[0].src || '/1/').split('/').find(i => i.match(/^\d+$/)) === userid) {
i.style.display = 'none'
deleteMessages = true
} else deleteMessages = false
}
else if(deleteMessages) i.style.display = 'none'
}
})
deleteMessages = false
}, 300)
}
The function block sets an interval for 300 seconds to:
First, get all the messages: [...document.getElementsByClassName('message-2qnXI6')]
Then for each message check to see if it's a groupStart element: if(i.className.match('groupStart-23k01U'))
If it is, it'll check to see if it's a message from the blocked user:
if(([...i.children].find(i => i.className.match('contents-2mQqc9')).children[0].src || '/1/').split('/').find(i => i.match(/^\d+$/)) === userid)
If it is, it'll delete that message and set deleteMessages to true:
i.style.display = 'none'
deleteMessages = true
If it isn't, it'll set deleteMessages to false, so that the next messages don't get deleted.
Going back, if the message isn't a groupStart message, it'll check to see if deleteMessages is true (which would be the first message in the group was from a blocked user, and therefore this message is from a blocked user). If it is, it'll delete it.
Everything should be reset with a simple ctrl+r
Just copy and paste this (the minified version of my code):
function block(e){return setInterval(()=>{let t=!1;[...document.getElementsByClassName("message-2qnXI6")].forEach(n=>{n&&(n.className.match("groupStart-23k01U")?([...n.children].find(e=>e.className.match("contents-2mQqc9")).children[0].src||"/1/").split("/").find(e=>e.match(/^\d+$/))===e?(n.style.display="none",t=!0):t=!1:t&&(n.style.display="none"))}),t=!1},300)}
into the console, and then to block someone type block('<userid>') (make sure the user id is a string and not a number), and it should start working. Also, when you do, it should return a number, just type clearInterval(number) to stop it from blocking new messages.
If you're wondering about this line:
if(([...i.children].find(i => i.className.match('contents-2mQqc9')).children[0].src || '/1/').split('/').find(i => i.match(/^\d+$/)) === userid)
There's a few hackey things I did here:
I start by getting an array of all the messages children, then I find the child with the userID in it (i.className.match('contents-2mQqc9')). If I don't I just use a placeholder to avoid errors.
Then I split via /s because the user id is one of the paths. Then I find which part of the path is the user id by checking to see if it's only numbers (i.match(/^\d+$/)). Finally once I've done all that I check to see if it's the same as the userid.
Here's a quick example:
https://cdn.discordapp.com/avatars/847295719724247388/c9af8fff1110025e6aae862492b4aa29.webp?size=256 (this isn't a real link) => ['https:', '', 'cdn.discordapp.com', 'avatars', '847295719724247388', 'c9af8fff1110025e6aae862492b4aa29.webp?size=256'] => '847295719724247388' is made completely of numbers, so: '847295719724247388'
Unfortunately, if you use this on too many messages by the same person in a row discord will really glitch out, so you'll have to watch for that (note this was testing it on 1000+ messages in a row by the same person). It seems like problems start to happen at around 100, but don't become an issue until it's up to 200 - 300, and even then it'll sometimes work if you wait for a second.
Hopefully if this doesn't work for you you'll at least have an idea of what to do.

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
});

Bot checking multiple links

I made an anti-invite command, however, the bot doesn't delete the link that is being sent in the guild where the command is enabled. v12.2
if(message.content.includes(["https://discord.gg/", "https://discord.io/", "https://discord.me/"]))
You need to check if message.content includes each possibility separately, as the method only accepts one string at a time.
Could use .some() with a callback which indexOf's to check, which is pretty short.
// mock
const message = {
content: 'Come join my room https://discord.gg/1234'
}
//
const links = ["https://discord.gg/", "https://discord.io/", "https://discord.me/"]
//
if (links.some(el => message.content.indexOf(el) !== -1)) {
// link/domain matched
}
There are other ways too do the same, like with simple regex match.

(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());

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