Lets say that I have a messageDelete event that I want to get the data from. Since I am required to have the Channel and Message partials for the messageDelete event to fire, the message object returned in the messageDelete event will be partial.
When I'm trying to fetch this message, it returns an error saying that the fetched message is unkown.
So how can I get information like the content etc. from the deleted message?
My current code:
client.on("messageDelete", async message => {
if (message.partial) await message.fetch() // this is where the error occurs
console.log(message.content) // will only work on non partial messages
})
Is there any way around this, cause it would be useful to get the information from past deleted messages.
EDIT
Toasty recommended that I use the audit logs, to which I have used the following code
client.on("messageDelete", async message => {
console.log(message) // seeing whats avaliable in the return
if (message.partial) console.log("message was partial") // checking if the message was partial to compare with non partial messages
if (message.guild) {
const fLogs = await message.guild.fetchAuditLogs({limit:1, type:"MESSAGE_DELETE"}) //getting audit logs
const log = fLogs.entries.first()
let {executor, target} = log
console.log("Message deleted by "+executor.tag+" in "+target) // responding.
}
})
Output:
message was partial
Message deleted by CT-1409 "Echo"#0093 in 606323576714559489
So I can get the who and the (sort of) what of the message that was deleted.
I still cannot get the rest of the message information, as if I tried to fetch the message with the target id, it would give me Unkown Message again. But also when I logged the message object to start with, I noticed that there was a decent amount of information already present, which may mean some data would still be accessible from a partial message. I don't know how much, but perhaps enough for what I need.
That you can't get any information of the deleted message is probably because the message..has been deleted.
As you can read here, this is not possible and would also be a violation of the rules and a violation of the privacy of users.
But...
...if you have a command for deleting messages, you could get the information of the message before you delete it and do stuff with it
Alternatively, you could work with the audit logs
Related
I'm using npm yahoo-finance to fetch stock data. When I input a stock symbol that doesn't exist, I would like to catch the error.
const yahooFinance = require('yahoo-finance');
async function stockData() {
try {
let data = await yahooFinance.historical({symbol: "SIJGAOWSFA", from: 2020-08-23, to: 2021-08-23});
} catch (error) {
console.error(error)
}
}
stockData();
However it doesn't appear to be a typical fetch error. It's not being caught at all. By that I mean, the error you see below was not logged to the console via the console.error(error). Rather something outside the scope of this file is logging the error. When the error occurs, nothing in catch is executed.
I plan on using this in a for loop, so would like to catch the error so I can avoid executing any following functions.
A collaborator says that:
Is this from an existing project that was working and stopped working, or a new project?
If the former - everything is still working fine on my side. (Very) occasionally there are issues at yahoo that get stuck in their cache, possibly relating to DNS too. I'd suggest to clear your DNS cache and also try querying different data to see if that works.
If the latter (new project), it could be the data you're querying. Try query different data and see if it works. Usually yahoo throws back a specific error if something wrong, but it could be this.
If neither of those approaches work, but you still need to catch this sort of error, given the source code, what it does is:
if (!crumb) {
console.warn('root.Api.main context.dispatcher.stores.CrumbStore.crumb ' +
'structure no longer exists, please open an issue.');
And then continues on as normal (without throwing), and eventually returns an empty array.
If you're sure the result should contain at least one item, you can check to see if it's empty, and enter into an error state if it is.
Otherwise, if you don't know whether the array should contain values or not, another option is to overwrite console.warn so that you can detect when that exact string is passed to it.
Another option would be to fork the library so that it (properly) throws an error when not found, instead of continuing on and returning an empty array, making an empty successful result indistinguishable from an errored empty result. Change the
if (!crumb) {
console.warn('root.Api.main context.dispatcher.stores.CrumbStore.crumb ' +
'structure no longer exists, please open an issue.');
to
if (!crumb) {
throw new Error('root.Api.main context.dispatcher.stores.CrumbStore.crumb ' +
'structure no longer exists, please open an issue.');
and then you'll be able to catch it in your call to .historical.
For a specific channel in my server, I want to make it so that a user can't send two messages in a row.
Here's the code I got so far:
client.on('message', message => {
if(message.guild.id != '123478291364192834') return;
if(message.channel.id != '432874912364123984') return;
let messageauthor = ''
message.channel.messages.fetch({limit:2}).then(messages=>{
messages
.forEach(message=>{
messageauthor = message.author.id
})
})
if(messageauthor == message.author.id){
message.delete()
}
})
The idea was that it'd fetch two messages in a channel and if the message author of the second message is fetched is the same as the message author, it'd delete the message. But it doesn't do that, does anyone know why or have any solutions to the problem?
There are several problems with your code as-is.
Problem
First of all, you're checking if messageauthor is equal to message.author.id outside of your .then(). The code inside a .then() has its execution delayed because it executes asynchronously. In other words, it takes some time to fetch the messages in the channel, which is why you can only access messages in your .then(). Because the contents of your .then() execute after the rest of your code, including the code that proceeds it, you are checking if messageauthor is equal to message.author.id before the value of messageauthor is even set. Your if statement will always be checking if the empty string '' is equal to the ID of the message's author, which is always false, so the message will never be deleted.
Second, inside your .then() you are looping through the two messages you've fetched and are setting messageauthor to the ID of the message's author. But since you're doing this twice, since you fetched two total messages, that means that messageauthor is only set to the author ID of the second message. So why loop through both messages, when you can just immediately grab the author ID from the last message?
Finally, you're only checking if the author of the second message matches the ID of the current message's author. How that behaves depends on whether or not the current message is fetched along with the last message. If the current message is included in messages, then the second message literally is the current message, so that will delete every single message that anyone ever sends. If the current message isn't included in messages, then why are you fetching two messages with your .fetch()? From your question, it seems you do not want people to send two messages in a row. That would mean you only have to fetch 1 previous message and use just the message you fetched in your comparison. I will attempt to account for both of these possibilities below with two separate answers, as I am not sure whether the current message will be included.
Solution
Here's how I would suggest you revise your code, assuming the current message is included in the fetched messages (I'm assuming this will not be the case):
client.on('message', message => {
if(message.guild.id != '123478291364192834') return;
if(message.channel.id != '432874912364123984') return;
message.channel.messages.fetch({limit:2}).then(messages=>{
let firstauthor = messages.first().author.id;
if(firstauthor == message.author.id){
console.log("Deleting message because 2 messages in a row were sent");
message.delete();
}
});
})
Here's what I would do if this current message is not included in the fetched messages (which I assume will be the case, so this should work properly):
client.on('message', message => {
if(message.guild.id != '123478291364192834') return;
if(message.channel.id != '432874912364123984') return;
message.channel.messages.fetch({limit:1}).then(msg=>{
let firstauthor = msg.author.id;
if(firstauthor == message.author.id){
console.log("Deleting message because 2 messages in a row were sent");
message.delete();
}
});
});
In both of these fixes, what I've done is first and foremost I've moved the if statement into the end of the .then(). This ensures that the condition is only checked after the messages have been fetched. Second, I removed the forEach loop and replaced it with simple references to the first, or only, item in the fetched message collection. Third, I made sure it was the first message, and not the second message, that would have its author's ID compared to the current message author's ID. And finally, I added a simple console.log to help you debug the issue. If the console.log never occurs but you know it is supposed to, that means something is wrong in the code.
Feel free to try either solution, or both, out and find which one works for you. I have not tested either thoroughly, so if you find an issue let me know and I will fix it. This should give you a general idea of what the issue is in your code nevertheless, so you should hopefully be able to fix the issue on your own even if these solutions do not work. I am guessing the second of the above solutions will work best.
If I want to capture a Sentry message and include the user info set on the Sentry scope, how can I do that?
For example if I'm setting the user ID to the scope
Sentry.configureScope(scope => scope.setUser({ id: '123' }))
Then later if I want to send a Sentry message like "User 123 could not send message 456"
Sentry.captureMessage(`User ${userId} could not send message ${messageId}`)
I can get the message ID from the surrounding code but how do I access the user.id that I set on the scope in this context?
By accessing Sentry scope, you can get info like the user or anything else already configured to the Sentry scope.
The example to get the user ID would be to use Sentry local scopes like this
Sentry.withScope(scope => {
const user = scope.getUser()
Sentry.captureMessage(
`User ${user.id} could not send message ${messageId}`
)
})
However, this type of message would probably create a lot of unique messages which would probably create a lot of noise. Perhaps consider an alternative approach where the message itself can be consistent and informative while the event-specific info is added as Sentry context or tags.
For example:
Sentry.withScope(scope => {
scope.setTag('messageId', messageId)
// The user info and user.id is already part of the context in Sentry
// const user = scope.getUser()
Sentry.captureMessage(
`User could not send message`
)
})
This way, all similar occurrences will be "stacked" into one entry in Sentry and you can always click into it to expand and search by the tags if desired, etc.
According to the Message#fetch() docs, this function simply fetches the message it was called on. However, I'm not sure in which circumstance it would ever make sense to use this function.
According to the screenshot above, this method returns Promise<Message>. I'm confused, because why would you need to fetch a message you already have access to? For example:
// let's say somebody sent the message `hello`
client.on('message', (message) => {
console.log(message.content) // this would return `hello`
message.fetch((message) => console.log(message.content)) // this would also return `hello`; it's the same object
So, what's the point of it? If you didn't have access to the message, you wouldn't be able to use Message.fetch() anyways.
And I'd ask the same question for Channel#fetch, ClientUser#fetch, Guild#fetch, GuildMember#fetch, GuildPreview#fetch, etc.
If we dive into the source of the Message class and look for the fetch method, we see something like this:
/**
* Fetch this message.
* #param {boolean} [force=false] Whether to skip the cache check and request the API
* #returns {Promise<Message>}
*/
fetch(force = false) {
return this.channel.messages.fetch(this.id, true, force);
}
The fetch() method in this cases, retreives the last message posted in the channel. If you invoke this method and log the output, you see that it fetches the message you have posted. It simply returns asynchronous message object. Promise<Message>.
So let's say you post this message Hello, i'm a message. and invoke and log the .fetch() method, you see in the console the message object and content Hello, i'm a message.. All this does is fetching the message posted in the channel. Same goes for channel#fetch, ClientUser#fetch.
All it does is using the cache to fetch a channel, user, guild, message. The force argument is to receive the realtime data.
The force flag is used if you want to skip the cached message and want to do the API request.
If you wish to fetch all messages or a few messages, you can use the MessageManager class:
message.channel.messages.fetch('Message ID');
or
message.channel.messages.fetch({
limit: 5, // Amount of messages to be fetched in the channel
before: "Snowflake (ID from message to be fetched)",
after: "Snowflake (ID from message to be fetched)",
});
More on this: https://discord.js.org/#/docs/main/stable/typedef/ChannelLogsQueryOptions
The point of this fetch command is because there are many situations where your bot does not have messages in cache to access. The most basic example is 1. During development you are are editing code and restarting the bot. Now those messages are lost and the bot doesn't have full access to them.
Your running program loses internet connection - enjoy your limited access and errors.
Cached messages are limited to 100 / channel. If you want access to earlier messages you must fetch. A common example is when the bot is asked to delete messages from a channel.
In our some of our servers the messages can exceed the cache amount within a minute. Specific messages / commands need to be fetched to auto-delete after x minutes.
This calls and retrieves a dog url from random.dog, when posting the link to log it stops at one, however when using message.channel.send below it runs an infinite loop of the call, what would be the best way to prevent this and to only send one link then stop till it is called again?
const animals = require('relevant-animals')
client.on("message", (message) => {
if(message.content.includes("dog")){
animals.dog().then(s => message.channel.send(s)) ;
animals.dog().then(s => console.log(s)) ;
};
Below is console log after one request it sends one link
Below is after it is sent to the channel, it just posts links non stop rather than just the one as shown in the console
Your bot is responding to itself. You can exclude it for replying to itself by using message.author.bot.
if(!message.author.bot) {
// Do something if message doesn't come from a bot.
}
I hope this code will help you getting on the right path, good luck!
You could just do this:
if(message.author.bot) return;
This wouldn't only stop bot from executing commands etc. on itself, it would prevent any bot from using your bot.
Since it checks for author of message if bot property returns true it would return;.
And if bot property returns false it would work as normally - only for users like you and me, and many others!
You can try this by doing the following code:
console.log(message.author.bot);
It will log the boolean value of bot property of the messages author.
You can read more about booleans (true/false) here.