#Code just ... exits?

1 messages · Page 1 of 1 (latest)

upbeat geyser
#

I have the following code and it just ... "exits" in the middle. When I step through using the debugger, it only goes through a few channels before execution just seems to stop. I don't know how better to explain what happens. The final logging message is never hit, so it somehow gets out of the method without actually executing all the code.

DiscordChannel mostActiveChannel = accessibleChannels.FirstOrDefault();
int activityCount = 0;

//foreach (var channel in randomOrdered)
foreach (var channel in discordServer.Channels.Values)
    {
    Console.WriteLine("checking channel: " + channel.Name);
    string channelName = channel.Name;
    IReadOnlyList<DiscordMessage> messages = await channel.GetMessagesAsync(limit: searchHistory);
    if (messages == null) { continue; }
    int messageCount = messages.Count(message => message.Author.Id == member.Id);
    if (messageCount <= activityCount) { continue; }

    mostActiveChannel = channel;
    activityCount = messageCount;
}

Console.WriteLine("All channels checked");```
dim crystal
#

what's the context around that code

upbeat geyser
#

It's in a method designed to find which channel on a server that a given user was most recently active in. I'm testing it using a command in the Discord server.

dim crystal
#

hook CommandErrored/SlashCommandErrored

upbeat geyser
#

Ah, alright, thanks!

#

Where is that? I assumed it was an event on the client, but seems not.

dim crystal
#

on your respective command extension

upbeat geyser
#

Ah, just found it

#

Looks like this is the signature for my event handler.

CommandErrorHandler(CommandsNextExtension sender, CommandErrorEventArgs args)```
Where would I find the error message or information?
dim crystal
#

have you tried using intellisense on args

upbeat geyser
#

Yeah, I'm poking around. Wasn't sure what I'm looking for though.

#

I guess this? args.Exception.Message

dim crystal
#

i'd print the entire exception

#

type name, message and stacktrace

upbeat geyser
#

Part of my confusion is that I don't know that it's an exception I'm trying to catch. I don't know what is actually happening.

dim crystal
#

if control flow is suddenly jumping away you're trying to catch an exception

#

either that or you wrote goto

upbeat geyser
#

Ah, alright. (Nope, no gotos)

upbeat geyser
#

Ah, Cannot get the messages of a Category channel.

#

I think that's the problem. Thank you!

#

Yep, that was it. Thanks!

#

🤔 I may have spoken too soon.

DiscordMember discordMember = author as DiscordMember;
if (discordMember == null) { return; }

DiscordRole activeRole = server.Roles.Values.FirstOrDefault(role => string.Equals(role.Name, "Active", StringComparison.OrdinalIgnoreCase));
if (activeRole == null) { return; }

bool hasRole = discordMember.Roles.Any(role => string.Equals(role.Name, activeRole.Name, StringComparison.OrdinalIgnoreCase));
if (hasRole) { return; }

await discordMember.GrantRoleAsync(activeRole);```
I'm getting a "NotFoundException" (Message is simply "404") on the last line. Both `discordMember` and `activeRole` are null checked though (and it's not a NullReferenceException).

What might be going on?
tawny tendon
#

still in command context?

upbeat geyser
#

Yep

tawny tendon
#

Can you use Context#Member instead of author? I think the cast could break smth

upbeat geyser
#

I'm getting the user from a Message.Author object. I don't have access to the context where this is being called.

tawny tendon
#

hm, maybe try to fetch the member by the author id

upbeat geyser
#

How do I do that?

#

I don't have a context object, which I think I need for that?

tawny tendon
#

DiscordGuild#GetMemberAsync

#

although i think discordMember.Roles would also be a NRE if the cast fcks it up

upbeat geyser
#

Ah, gotcha

#

Ah, so member.Roles can return null if there are no roles?

tawny tendon
#

if it would not be a member which you got through author, the field would not exists because a User dont has roles. if a member has no roles its an empty collection

upbeat geyser
#

Makes sense, although if it's not a member, the null check on line two should have aborted.

tawny tendon
#

i think there is a field on the exception which can contains the error message from discord maybe there is a more verbose error message

upbeat geyser
#

Yeah, it just says "404"

tawny tendon
#

NotFoundExceptin.JsonMessage?

upbeat geyser
#

...no. Just Error.Message. I'll check that one

#

Where is that field? There is no Error.JsonMessage property

tawny tendon
#

it should be on your exception instance

upbeat geyser
#

Nope, don't have that.

dim crystal
#

cast to NotFoundException first

upbeat geyser
#

🤔 Alright

#

It just says "unknown Member." But that doesn't make sense since I'm already null-checking discordMember on line 2. If it was null, it shouldn't get to the last line.

#

Is it possible get a DiscordMember object that does not exist? Like, maybe that is someone that has left the server?

#

If so, how can I check for that?

dim crystal
#

cache Troll

tawny tendon
dim crystal
#

you can only check by explicitly making a request for the member object

#

would probably require DSharpPlus.Rest

tawny tendon
#

GetUserAsync has a parameter to skip cache

dim crystal
#

does GetMember?

tawny tendon
#

i meant getMemberAsync sry

upbeat geyser
#

I'm using this to get the DiscordMember

DiscordMember discordMember = await server.GetMemberAsync(id);```
tawny tendon
#

public async Task<DiscordMember> GetMemberAsync(ulong userId, bool updateCache = false)

upbeat geyser
#

Ah, I see there's an option. yep

#

So I should pass true then? and if the person is no longer on the server, then the result will be null, correct?

tawny tendon
#

yes

upbeat geyser
#

Excellent, thanks!

#

Sigh well that didn't work. I still get the same exact error on that line

tawny tendon
#

sry

#

had to check myself

upbeat geyser
#

Wait, then what's the point of the second argument then? Since I'm getting that exception anyway?

tawny tendon
#

It could be that the lib has the member cached but the member left. the second argument ensures that the api is querried

upbeat geyser
#

I get the same result either way. I don't understand the difference.

#

So I wrapped that call in a try/catch block but the method still doesn't finish executing.

#

I still get the same error

tawny tendon
#

i cant reproduce your error

upbeat geyser
#

Alright. Well, I get it consistently. Any suggestions as to what else might be causing it? Or causing it to occur there?

tawny tendon
#

can you provide your full method/more context?

upbeat geyser
#

Sure

#

These are the methods. Error occurs on the GrantRoleAsync line.

AddActiveRoleCommand is part of a CommandModule.
AddActiveRoleToAll is in a helper.
AddActiveRole is in the same helper.

public async Task AddActiveRole(DiscordGuild server, DiscordMember discordMember)
{
    if (server == null) { return; }
    if (discordMember == null) { return; }

    //  Ignore messages not sent from my server.
    if (!string.Equals(server.Name, SERVER_NAME, StringComparison.OrdinalIgnoreCase)) { return; }

    //  This role is assigned to anyone that speaks
    DiscordRole activeRole = server.Roles.Values.FirstOrDefault(role => string.Equals(role.Name, ACTIVE_ROLE, StringComparison.OrdinalIgnoreCase));
    if (activeRole == null) { return; }

    IEnumerable<DiscordRole> memberRoles = discordMember.Roles;
    if (memberRoles == null) { return; }

    bool hasRole = memberRoles.Any(role => string.Equals(role.Name, activeRole.Name, StringComparison.OrdinalIgnoreCase));
    if (hasRole) { return; }

    try
    {
        await discordMember.GrantRoleAsync(activeRole);
    }
    catch (NotFoundException e) { }
}```
`AddActiveRole` is called from `AddActiveRoleToAll`
```cs
public async Task<int> AddActiveRoleToAll(DiscordGuild server)
{
    List<ulong> activeIds = new List<ulong>();

    foreach (DiscordChannel channel in server.Channels.Values)
    {
        if (channel.IsCategory) { continue; }

        IReadOnlyList<DiscordMessage> messages = await channel.GetMessagesAsync(limit: 500);

        for (int i = 0, end = messages.Count; i < end; i++)
        {
            DiscordUser author = messages[i].Author;
            ulong id = author.Id;
            if (activeIds.Contains(id)) { continue; }

            activeIds.Add(id);

            DiscordMember discordMember = await server.GetMemberAsync(id,updateCache: true);
            await AddActiveRole(server, discordMember);
        }
    }

    return activeIds.Count;
}```
`AddActiveRoleToAll` is called from `AddActiveRoleCommand`
```cs
[Command("AddActiveRole")]
[Description("Adds the \"active\" role to anyone on the server that has ever said anything.")]
[RequireUserPermissions(Permissions.UseApplicationCommands)]
public async Task AddActiveRoleCommand(CommandContext context)
{
    await context.Channel.SendMessageAsync("Adding role to active users...");
    DiscordHelper helper = new DiscordHelper();
    int added = await helper.AddActiveRoleToAll(context.Guild);
    await context.Channel.SendMessageAsync("Role added to " + added + " users.");
}```
`AddActiveRoleCommand` is called from within a channel on a server using a bot command.
upbeat geyser
#

Yeah, I don't know what the problem is. I've added a ton of logging. I've wrapped everything in try/catch. I keep getting the same problem. Does anybody have any idea?

NotFoundException
Not found: 404
   at DSharpPlus.Net.DiscordApiClient.GetGuildMemberAsync(UInt64 guild_id, UInt64 user_id)
   at DSharpPlus.Entities.DiscordGuild.GetMemberAsync(UInt64 userId, Boolean updateCache)
   at YADB2.Helpers.DiscordHelper.AddActiveRoleToAll(DiscordGuild server, DiscordChannel reportingChannel) in C:\Users\p\Desktop\Projects\YADB2\YADB2\Helpers\DiscordHelper.cs:line 275
   at YADB2.Modules.AdminModule.AddActiveRoleCommand(CommandContext context) in C:\Users\p\Desktop\Projects\YADB2\YADB2\Modules\AdminModule.cs:line 32
   at DSharpPlus.CommandsNext.Command.ExecuteAsync(CommandContext ctx)```
sturdy rover
#

If any of those 500 messages you're iterating through contains a message from a user who left the guild, you'll get a 404 since they aren't actually members of the guild (you cannot get a DiscordMember object for the user and assign them a role)

#

You should just try-catch it and fail fast when you get a 404

#

Alternatively you could fetch the list of members in the guild in that moment then compare to the user IDs of all the message authors in each channel

upbeat geyser
#

Yeah, that's what I ended up doing

#

GetMemberAsync() should just return null if the id isn't found on the server, instead of throwing an exception. "Not finding something" is a perfect valid result from a "go find something" instruction.

dim crystal
#

it returns the API response as an exception

#

which is the c# equivalent of a HTTP error

#

the library is (supposed to be) consistent in this behaviour, if you find any examples to the opposite those should be fixed

upbeat geyser
#

Ah, so that's what everything does? Alright, good to know.