discord.js icon indicating copy to clipboard operation
discord.js copied to clipboard

Partial guildMemberRemove emitted when no partials are enabled

Open tmantti opened this issue 4 years ago • 11 comments

Please describe the problem you are having in as much detail as possible: Partial guildMemberRemove events are being emitted even without partials enabled.

Include a reproducible code sample here, if possible:

const client = new Discord.Client({ ws: { intents: ['GUILDS', 'GUILD_MEMBERS', 'GUILD_BANS', 'GUILD_VOICE_STATES', 'GUILD_PRESENCES', 'GUILD_MESSAGES'] } });

client.on('guildMemberRemove', (member) => {
    if (!member.joinedAt) return console.log('member.partial = ' + member.partial)
})

// => member.partial = true

Further details:

  • Discord.js version: 12.5.3
  • Node.js version: 14.16.0
  • Operating system: Debian
  • Priority this issue should have – please be realistic and elaborate if possible: medium

Relevant client options:

  • Partials: none
  • Gateway intents: GUILDS, GUILD_MEMBERS, GUILD_BANS, GUILD_VOICE_STATES, GUILD_PRESENCES, GUILD_MESSAGES

tmantti avatar May 02 '21 18:05 tmantti

Is this still happening? I think this was something related to public-stages that Discord fixed in the meantime.

SpaceEEC avatar Jun 10 '21 12:06 SpaceEEC

It is still happening. Last time just few hours ago. None of the servers the bot is on have stage channels.

tmantti avatar Jun 12 '21 19:06 tmantti

Are you still able to reproduce this bug with the Dev version?

DTrombett avatar Jul 06 '21 22:07 DTrombett

I set up a test running on 13.0.0-dev.02f55f0.1626742085 and it has the same behavior as 12.5.3

tmantti avatar Jul 26 '21 06:07 tmantti

Still reproducible on 13.2.0-dev.1629763504.c232baa with Node 16.7.0

tmantti avatar Aug 25 '21 05:08 tmantti

Ok, I think i've nailed down when this could possibly happen. The oddity is that it has to involve stage channels, which you have said aren't involved.

Anyways, as of https://github.com/discord/discord-api-docs/pull/2911 the joined_at property in GMU events can be null. I believe because you need a member to have a voice state but members that join a public stage don't actually join the server. Why does that matter? A member gets added to cache on the GMU event, but if joined_at is null then the joinedTimestamp will be set to 0 on the djs object, causing joinedAt to be null (correct) and partial to be true (incorrect). The member is not actually partial though.

I assume you were using joinedAt to show when the member joined in some sort of logs? If not, what is causing you to need to check partial right now?

I am going to fix this by improving the partial check, but it does mean joinedAt can be null when partial is false, but that is documented

(interesting tidbit, I can't seem to get an event to fire with joined_at: null, and get no events for guild members in public stages whatsoever, likely because they are being removed.)

ckohen avatar Oct 05 '21 02:10 ckohen

I can confirm that our guild does not have stage channels. We have tried them in the past, but those channels were deleted long time ago.

Yes, joinedAt is used here as a part of logging. Checking for partial was added to debug.

Is this simply caused by Discord.js relying on the member being cached when guildMemberRemove is fired?

The API only sends guild_id and user for the event, while Discord.js gives GuildMember. In that case, if the member who leaved was not cached, Discord.js would not have the joinedAt available.

I can't find anything in Discord.js documentation suggesting that the GuildMember from Client#guildMemberRemove could have null properties, while it is apparently possible.

tmantti avatar Oct 05 '21 07:10 tmantti

Ok, drilled down a bit and found that presence updates add a member based on the user only (PU doesn't come with a member), don't know why yet, but that's the only place you can possibly get that still (and is why many people have probably not encountered this, e.g. I have never used the presence intent).

discord.js does rely on the cached member when emitting events yes. However, that is what the partial is for. If the member does not exist in cache, the event is not emitted unless the partial is enabled. However, if the member is in cache, no matter its state, it will be emitted. There's an arbitrary line for what defines partiality, dependent on what other events partial members can come from. The member added in PU should not count as a full member object!

The documentation has always had joinedAt as nullable (the ? before the type indicates that) for all guild members, even in 12.5.3.

ckohen avatar Oct 05 '21 08:10 ckohen

Sounds like that could be the underlying issue. I disabled GUILD_PRESENCES on my testing bot to see if it has any effect. Thanks a lot for spending time on this!

The documentation has always had joinedAt as nullable (the ? before the type indicates that) for all guild members, even in 12.5.3.

I have totally missed that. My bad.

tmantti avatar Oct 05 '21 09:10 tmantti

Disabling GUILD_PRESENCES seems to have fixed the issue. I no longer get partial events on the testing bot, while it still occurs on a bot that has the intent enabled. Testing bot was running v13.2.0-dev.1633435371.8bc1ece

tmantti avatar Oct 11 '21 06:10 tmantti

Cool, so that confirms that is indeed where the issue comes from. I am debating whether we can safely remove the member being added in presence events in a semver minor or patch update, you'll see a linked PR whenever that decision has been made.

ckohen avatar Oct 11 '21 06:10 ckohen