How do I get the last message sent by a specific user? - discord.js

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

Related

Can I send an alert when a message is published to a pubsub topic?

We are using pubsub & a cloud function to process a stream of incoming data. I am setting up a dead letter topic to handle cases where a message cannot be processed, as described at Cloud Pub/Sub > Guides > Handling message failures.
I've configured a subscription on the dead-letter topic to retain messages for 7 days, we're doing this using terraform:
resource "google_pubsub_subscription" "dead_letter_monitoring" {
project = var.project_id
name = "var.dead_letter_sub_name
topic = google_pubsub_topic.dead_letter.name
expiration_policy { ttl = "" }
message_retention_duration = "604800s" # 7 days
retain_acked_messages = true
ack_deadline_seconds = 600
}
We've tested our cloud function robustly and consequently our expectation is that messages will appear on this dead-letter topic very very rarely, perhaps never. Nevertheless we're putting it in place just to make sure that we catch any anomalies.
Given the rarity of which we expect messages to appear on the dead-letter-topic we need to set up an alert to send an email when such a message appears. Is it possible to do this? I've taken a look through the alerts one can create at https://console.cloud.google.com/monitoring/alerting/policies/create however I didn't see anything that could accomplish this.
I know that I could write a cloud function to consume a message from the subscription and act upon it accordingly however I'd rather not have to do that, a monitoring alert feels like a much more elegant way of achieving this.
is this possible?
Yes, you can use Cloud Monitoring for that. Create a new policy and perform that configuration
Select PubSub Topic and Published message. Observe the value every minute and count them (aligner in the advanced option). Now, in the config, when it's above 0 from the most recent value, the alert is raised.
To filter on only your topic you can add a filter by topic_id on your topic name.
Then, configure your alert to send an email. It should work!

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)

discord.js user.speaking isn't working

//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.

mIRC Execute command after X lines of chat

I'm a selflearned mIRC programmer and started very recently so my experience is quite limited. Here's the problem I have:
I'm creating a chat bot for Twitch and have created besides a raffle system a timed message for promting the stream it's on every few minutes. However, I'd also like to repeat the message after X lines of chat lines sent in the chat in-case the chat is going quickly so you don't miss out on social links and stuff for new visitors.
Pseudo-code for what I want to be done:
on !startpromote
if (broadcaster) then PromoteMessage every X amount of lines passed
else return
end
on !stoppromote
if (broadcaster) then PromoteMessage stop
else return
end
You could have a %msgCounter variable in the variables tab and then you could use the TEXT event:
on *:TEXT:#:{
INC %msgCounter
if (%msgCounter > 10) { msg $chan Promotion message. }
}
You'd have to keep track of the %msgCounter variable and reset it when it hits a certain threshold.
Not sure what you mean by broadcaster, however if you mean IRCOp than you can use isop and do:
if ($1 isop $chan) {
}
Where $1 is the user who typed the message, isop determines if the user is an operator (or maybe broadcaster) and $chan for the IRC Channel.

Resources