discord.js user.speaking isn't working - discord

//function fires after special command every 100 milliseconds
function searchSpeaking(roleSpeak){
//checks every channel; giving you member-channel and its chanId-id
bot.channels.forEach((channel, chanId) => {
//filters out text channels
if (channel.type == 'voice'){
//checks every member in voice channel; giving you guildMember-user and his/her id
channel.members.forEach((guildMember, memberId) => {
//debug
console.log(guildMember.nickname, guildMember.speaking)
console.log("-----------------------------------")
//activates when user speaks DOESN'T WORK
if (guildMember.speaking){
//adds 'score' it means how mayn milli seconds user talks
scoreboard[memberId] += 100
console.log("user is speaking!")
//if user talks for over 30s = 30 000 milliseconds and has option to talk
if ((scoreboard[memberId] > 30 * 1000) && guildMember.roles.has(roleSpeak.id)){
//reset his score and remove his option to speak
scoreboard[memberId] = 0
guildMember.removeRole(roleSpeak)
}
}
//if user isnt talking because his option/role to talk was removed
else if (!(guildMember.roles.has(roleSpeak.id))){
//we messure how much time he/she has been muted/without talking option
scoreboard[memberId] += 100
//if he/she was muted for a minute he/she will get his option/role to talk back
if (scoreboard[id] > 60 * 1000){
//reset score and give back role
scoreboard[memberId] = 0
guildMember.addRole(roleSpeak)
}
}
})
}
})
}
So in the line under a comment ( // ) stating that this doesn't work is an if statement that never passes even tho users are speaking in channels. It is console.loging as false as well (obvious). So i can't figure out why as i understand the docs it should turn to true every time member speaks. I will just say this again (as it says in first code comment) this function fires every 100ms = 0.1s and gets a role it is supposed to change based on score further explained in code comments.
Thanks for any help working with .speaking!

With the current API, there is no way to know if a user is talking in a voice channel without joining the voice channel.
You would need to join a voice channel, get a list of the users that are talking, and then switch to another channel (if there are any members in that channel).
You would probably get rate limited very quickly using this method.
Only way without joining the channels, would be getting the list of members in voice channels, and filter those with microphone muted. You still wouldn't know if they are really talking.
If you would want to join the channel and "listen" to the speaking, you could use the event guildMemberSpeaking. This would activate whenever a user starts/stops talking in the voice channel you are.

Related

How do I get the last message sent by a specific user?

I'm currently running discord.js v13 and I'm trying to find a way of finding the last message sent by a specific user. So far, I've come up with the idea of iterating on an array of user IDs, then iterating on each channel and comparing each timestamp with the pervious timestamp found until it finds the latest one and stores it. However, this doesn't seem to work and I just keep getting today's date for ALL the users even though the majority of them haven't sent a message today.
My Code:
for(var i = 0; i < array_of_users.length; i++) {
for (const channel of message.guild.channels.cache.values()) {
if (channel.isText()) {
let messages = await channel.messages.fetch().catch(e => console.log(e));
messages.each((msg) => {
if (msg.author.id === array_of_users[i]) {
if(x == 0) {
old_date = new Date(msg.createdTimestamp).toDateString();
snowflakes.push(old_date);
x += 1;
} else {
var new_date = new Date(msg.createdTimestamp).toDateString();
let splitOld = old_date.split(" ");
let splitNew = new_date.split(" ");
if(parseInt(splitNew[3]) >= parseInt(splitOld[3])) {
year = splitNew[3];
if(getMonthInt(splitNew[1]) >= getMonthInt(splitOld[1]) ) {
monthC = splitNew[1];
if(getDayInt(splitNew[2]) >= getDayInt(splitOld[2])) {
dayNum = splitNew[2];
} else {
dayNum = splitOld[2];
}
} else {
monthC = splitOld[1];
dayNum = splitOld[2];
}
} else {
year = splitOld[3];
dayNum = splitOld[2];
monthC = splitOld[1];
}
}
}
})
}
}
console.log(`${monthC}-${dayNum}-${year}`);
}
The code above I've written to sort the dates and get the latest one, However something is wrong that it keeps getting the latest date and sometimes even future dates that haven't occurred yet. I can't seem to find the issue but I am speculating that it is originating around the if (msg.author.id === array_of_users[i]) area.
I'm trying to find a way of finding the last message sent by a specific user.
Discord.JS provides no 'built in' way of doing this. The best you can do is by grabbing their messages and comparing each of them to one another until you've exhausted your options and are left with the optimal result. So your approach is (more or less) correct, although your implementation does have some function errors that are throwing you off.
So far, I've come up with the idea of iterating on an array of user IDs, then iterating on each channel and comparing each timestamp with the pervious timestamp found until it finds the latest one and stores it.
If you only are only looking for the most recent message for a specific User, then it is redundant to be dealing with an Array of Users altogether.
However, this doesn't seem to work and I just keep getting today's date for ALL the users even though the majority of them haven't sent a message today.
There are a number of errors in your approach which could be causing this. Here's a quick few of the more generalized / lack-of-experience related errors in your solution for you to work on:
The else block where you're comparing days split up from timestamps is super convoluted. Why not just use the createdTimestamp value from the Message instance itself, then reconstruct the whole monthC-dayNum-year thing again later on using that if you need to?
You're likely going to be hitting Discord's Rate limits (rather frequently) with your script as-is. To put it in perspective, you're basically spamming requests to their API for the next X messages in channels [A, B, ...ZA, ZB] repeatedly. You can optimize this a lot by making logical inferences as to when you can safely 'exclude' a Channel from your search (e.g. if the most recent message you've found in channel X was created on Jun 18 2022, and most recent message found in channel Y was created on Mar 13 2022, you can safely avoid asking Discord for more Messages for channel Y, as it won't have any information relevant to your use case). Writing some additional logic here will pay dividends to you both in how quickly your function can execute, and how frustrating it is to debug once Discord gets upset with you for your small-scale DDOS attack against them.
If I was to solve this problem, the way I would do it would be to employ the following logic:
Given GuildMember X, grab all the Channels in the Guild they belong to.
Filter those Channels so you're only working on TextChannels.
Recursively iterate through those Channels, keeping track of the 'latest message' found belonging to GuildMember X in that Channel (when/if found). Whenever you find a new message belonging to GuildMember X in Channel Y, check if that beats the most recent message found across the entire set thus far. If so, update the best message for the set accordingly. Continually filter the Channels you're working with as you go to exclude Channels that logically cannot hold anything relevant to your search-- any time you finish a recursion, you can go back over the set of Channels and look for any one where the 'best message' in that channel does not beat the current best message. Since you're fetching 'backwards in time' (asking Discord for progressively older and older messages as you continue to recurse), any time the best message in a given channel does not beat the best message across the entire set, you can be certain that the channel does not hold the droids that you are looking for. You're only going to be fetching even older messages than the earliest one already fetched if you continue to look in that Channel, which makes that Channel irrelevant to you at that point.
Lucky for you, I had some time to kill while travelling today, and for whatever reason trying to find an optimal solution to your problem interested me enough to give it a proper shot. The tips I wrote above should help you understand the code below, and if not, leave a comment and maybe I can help you out. Here's how I would solve your problem, while paying attention to logical exclusion and avoiding doing unnecessary work / API calls.
https://codesandbox.io/s/holy-surf-50vpmf?file=/index.js

Avoid rate limit for changing voice channel name discord js 13

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.

delete bot message after 2 user messages prior

javascript is still vague to my knowledge, and I cannot be precise on the sensibility in the following code I have manufactured. I am programming a discord bot that categorizes messages into a variety of channels, and the question has approached me to how I can program the bot by deleting follow up bot messages after 2 user messages were sent subsequently after the bot message.
if (bot.on(client, user.msg = '2'));{
msg.channel('message' = msg.delete)
}
Help is to be requested and I hope my query makes sense in the context I am implying, Regards, Coder
P.S: How do I change the bot messages text color as well?
No offense, but your code was completely faulty, so I just made some new code.
bot.on('message', aysnc message => {
var messagenumber = 0; //Makes variable for the number of messages sent
if(message.member.user.username === bot.user.username) return; //If the person who posts a message is the bot, it doesn't the code below.
if(messagenumber = 1) return bulkDelete(100), messagenumber = 0 //Checks if the variable is 1, and if it is, this means it's the second time a message posted, so it deletes messages (Limit is 100) then resets variable.
messagenumber = 1; //Makes variable 1 (This is to make it know it is the the second time a message is posted for the next time).
});
I didn't test this code, so it may not work properly, but if it does work I hope it helps you with your bot.
For the P.S (Changing text color): You can use these (Replace "NoKeyWordsHere" with your message). I don't recommend this, cause it'd take a long time to add it to every message sent. (You can use these on your messages too)

Limit the sending of messages at the same time

I was wondering if there is a way to block the sending of multiple messages at the same time from different people in a channel discord. Whether his silk with discord.js or discord.py. In itself, I want the bot to take into account the first message sent and ignore others sent at the same time
cordially,
Quentin S.
One solution would be to track the timestamp of the last message listened to, and then ignore any messages sent before a set amount of time has elapsed. See this example...
let lastMessageTime;
const sec = 2; // Seconds to wait between messages.
client.on('message', message => {
if (!message.guild || message.author.bot) return;
if (lastMessageTime && message.createdTimestamp - (sec * 1000) < lastMessageTime) return;
else lastMessageTime = message.createdTimestamp;
// Continue with 'message' event.
});

How to make code that removes roles from people randomly by guild position

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

Resources