I am using Discord .NET 1.0.2 and .NET Framework 4.6.1 on Visual Studio 2017 Community
It seems that at random, occasionally, a user will stop existing as far as Discord .NET/The Discord API is concerned.
Here are a few example debugging commands:
[Command("test1")]
public async Task TestCommand1(IUser user)
{
await Context.Channel.SendMessageAsync($"Your mention is: {user.Mention}, your ID is: {user.Id}");
}
[Command("test2")]
public async Task TestCommand2(SocketUser user)
{
await Context.Channel.SendMessageAsync($"Your mention is: {user.Mention}, your ID is: {user.Id}");
}
[Command("test3")]
public async Task TestCommand3(SocketGuildUser user)
{
await Context.Channel.SendMessageAsync($"Your mention is: {user.Mention}, your ID is: {user.Id}");
}
And here is where the commands are handled (Which is more or less the default command handler method provided in the Discord .NET tutorial, I added && !message.Content.ToCharArray().All(c => char.IsSymbol(c) || char.IsPunctuation(c)) to prevent the bot from spewing out: "Unknown command" when a user types something like "!?!"):
private async Task HandleCommand(SocketMessage messageParam)
{
// Don't process the command if it was a System Message
var message = messageParam as SocketUserMessage;
if (message == null) return;
// Create a number to track where the prefix ends and the command begins
int argPos = 0;
// Determine if the message is a command, based on if it starts with '/' or a mention prefix
if (!(message.HasCharPrefix(CommandPrefix, ref argPos) || message.HasMentionPrefix(Client.CurrentUser, ref argPos))) return;
// Create a Command Context
var context = new CommandContext(Client, message);
// Execute the command. (result does not indicate a return value,
// rather an object stating if the command executed succesfully)
var result = await CommandService.ExecuteAsync(context, argPos);
if (!result.IsSuccess && !message.Content.ToCharArray().All(c => char.IsSymbol(c) || char.IsPunctuation(c)))
{
await context.Channel.SendMessageAsync(result.ErrorReason);
}
}
It works fine for most users, but there is a small percentage (that is, by the way, persistent, as in, the same users will always get impacted randomly) that suffer from not being found at all by the bot.
In this scenario, for privacy purposes, we're gonna have @User01#0001 and @User02#0002.
User02#0002 gets frequently impacted with this issue, and thus cannot use many of the bot commands at all.
Let's run the debug commands.
> !test1 @User01#0001
Which returns as expected: Your mention is: @User01#0001, your ID is: XXXX "XXXX" being the correct user's ID.
Now let's run it for the other user who happens to be impacted at this time.
> !test2 @User02#0001
Which returns: "User not found", yet, the user is in the same guild the bot is and is present and a proper discord account.
It seems that it hits an exception that indeed, it cannot find the user, thus the command is not ran at all...
The exact same issue happens with !test2 and !test3 commands, so it is not a type issue.
The same issue happens when manually finding the user by ID, with either DiscordSocketClient.GetUser(ID) and SocketGuild.GetUser(ID), despite said user being present in the same server, it returns null.
GetUser only returns from the cache, and it's possible that the user isn't in the cache. Most likely because they're invisible.
We can't entirely fix this issue for the Socket entities, as Discord does not send invisible users over the websocket meaning that we can't create a socket entity for them, but for the interfaces, we could potentially make a REST request for the user information.
We don't add null values to the results collection, per the TypeReader
@foxbot
This is what I use to gather the IUser from a Discord ID that exists in my database (and present on the server and valid as well)
private IUser _user;
public IUser User => _user ?? (_user = Bot.Instance.Client.GetUser(DiscordID)) ?? (_user = Bot.Instance.Guild.GetUser(DiscordID));
It returns null for said users that are impacted with this issue.
Again, this is due to GetUser returning from the socket user cache, which may sometimes not contain a value for a given user if the user is offline or invisible.
Ah I see, that makes sense.
Is there a way to go around this issue? Even said users when they come online, they still don't seem to be recognized by the bot and it can take upwards to several hours to acknowledge them.
@AshkoreDracson Try toggling the AlwaysDownloadUser under the DiscordSocketConfig. This will attempt to fetch the entire user collection when GuildAvailable is fired.
Also, just a heads up that a similar issue regarding WS user fetching backup via REST is being tracked in #1015.
@Still34 Thank you very much! That did the trick for me!
This is correctly resolved via 8fb2c71814fad9bcab1888fb8d66d693cc98a4b1.
Discord now includes complete member objects in the MESSAGE_CREATE payload, so we now add that member to the cache when handling the dispatch.
Most helpful comment
@AshkoreDracson Try toggling the
AlwaysDownloadUserunder theDiscordSocketConfig. This will attempt to fetch the entire user collection whenGuildAvailableis fired.Also, just a heads up that a similar issue regarding WS user fetching backup via REST is being tracked in #1015.