Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Thread member cache not accurate #1296

Closed
3 tasks done
erincerys opened this issue Apr 26, 2022 · 2 comments · Fixed by #2351
Closed
3 tasks done

Thread member cache not accurate #1296

erincerys opened this issue Apr 26, 2022 · 2 comments · Fixed by #2351
Labels
bug Something isn't working priority: medium Medium Priority status: todo This issue needs work
Milestone

Comments

@erincerys
Copy link

erincerys commented Apr 26, 2022

Summary

Thread member cache not accurate

Reproduction Steps

Craft an event listener for on_thread_join:

  • Make the bot add itself to threads if it isn't already present according to Thread.me

Craft an event listener for on_thread_update:

  • Make the bot add itself to unarchived threads if it isn't already present according to Thread.me
  • Log the cached thread details, including Thread.me and Thread.members

Craft an event listener for on_thread_remove:

  • Log the cached thread details, including Thread.me and Thread.members

Start the bot

In your Discord client on a guild where the bot is present:

  • Create a thread through your Discord client, and observe the bot join it
  • Remove the bot from the thread
  • Archive the thread
  • Unarchive the thread

Minimal Reproducible Code

import logging
from typing import (Optional, Union)
from discord import (Thread, Guild)
from discord.ext import commands
from bot import Bot

logger = logging.getLogger(__name__)

class ThreadManager(commands.Cog):
    """Manages the bot's presence in threads as it receives events about them."""

    def __init__(self, bot: Bot) -> None:
        super().__init__()
        self.bot = bot

    async def _get_thread_details(self, event: str, thread: Union[int, Thread], guild: Optional[Guild] = None) -> Thread:
        text = f">>> {event}: Cached thread details; "
        if guild and isinstance(thread, int):
            thread: Thread = guild.get_thread(thread)
        text += f"'{thread.name}' ({thread.id}); thread.me={thread.me}, "
        text += f"Thread.members:List[ThreadMember]={thread.members}"
        if thread.members:
            members = [thread.guild.get_member(m.id) for m in thread.members]
            text += f", Thread.members:List[Member]={members}"
        logger.debug(text)
        return thread

    async def _join_thread(self, thread: Thread) -> None:
        await thread.join()
        logger.debug(f">>> THREAD_JOIN: Joined thread '{thread.name}' ({thread.id})")

    @commands.Cog.listener("on_thread_join")
    async def create_handler(self, thread: Thread) -> None:
        thread = await self._get_thread_details("THREAD_JOIN", thread, guild=after.guild)
        if not thread.me:
            await self._join_thread(thread)

    @commands.Cog.listener("on_thread_update")
    async def update_handler(self, before: Thread, after: Thread) -> None:
        thread = await self._get_thread_details("THREAD_UPDATE", after.id, guild=after.guild)
        if (
            (before.archived and not after.archived)
            and not thread.me
        ):
            await self._join_thread(thread)

    @commands.Cog.listener("on_thread_remove")
    async def remove_handler(self, thread: Thread) -> None:
        await self._get_thread_details("THREAD_REMOVE", thread, guild=after.guild)


def setup(bot: Bot):
    bot.add_cog(ThreadManager(bot))

Expected Results

On handling the THREAD_MEMBERS_UPDATE websocket event and subsequent thread_remove PyCord event:

  • Thread.me would be None, and Thread.members would contain the remaining members of the thread.

On handling the thread_update event:

  • Thread.me would be None, and Thread.members would contain the remaining members of the thread.
  • The bot rejoins the thread.

Actual Results

On handling the THREAD_MEMBERS_UPDATE websocket event and subsequent thread_remove PyCord event:

  • Logs show the raw websocket event payload contains a removed_members attribute. Despite this, the thread_remove PyCord event continues to return the bot user in Thread.me when handling this event.

On handling the thread_update event:

  • Logs show the Thread object cache is inaccurate - the me property is a ThreadMember that is of the bot user and the members property is an empty list.
  • The bot does not rejoin the thread.

Intents

intents = discord.Intents.default()
intents.members = True

System Information

- Python v3.10.4-final
- py-cord v2.0.0-beta
    - py-cord pkg_resources: v2.0.0b3
- aiohttp v3.8.1
- system info: Linux 5.17.4-zen1-1-zen #1 ZEN SMP PREEMPT Wed, 20 Apr 2022 18:29:30 +0000

Checklist

  • I have searched the open issues for duplicates.
  • I have shown the entire traceback, if possible.
  • I have removed my token from display, if visible.

Additional Context

This is the bot's logs when the steps to reproduced are followed with my own bot, which includes code that isn't in the minimal reproduction code provided. I've heavily redacted identifiers, but left those that are important for discerning unique members.

2022-04-26 10:39:53 discord.gateway      DEBUG    For Shard ID None: WebSocket Event: {'t': 'THREAD_CREATE', 's': 16, 'op': 0, 'd': {'type': 11, 'thread_metadata': {'locked': False, 'create_timestamp': '2022-04-26T17:39:53.535000+00:00', 'auto_archive_duration': 10080, 'archived': False, 'archive_timestamp': '2022-04-26T17:39:53.535000+00:00'}, 'rate_limit_per_user': 0, 'parent_id': '[redacted]', 'owner_id': '[redacted]', 'newly_created': True, 'name': 'test', 'message_count': 0, 'member_count': 1, 'last_message_id': None, 'id': '[redacted]', 'guild_id': '[redacted]', 'flags': 0}}
2022-04-26 10:39:53 discord.client       DEBUG    Dispatching event socket_event_type
2022-04-26 10:39:53 discord.client       DEBUG    Dispatching event thread_join
2022-04-26 10:39:53 bot.events.threads DEBUG    !!! THREAD_JOIN: Handling event
2022-04-26 10:39:53 bot.events.threads DEBUG    >>> THREAD_JOIN: Event details; 'test' ([redacted]); me=None, Thread.members:List[ThreadMember]=[]
2022-04-26 10:39:53 bot.events.threads DEBUG    >>> THREAD_JOIN: Cached thread details; 'test' ([redacted]); thread.me=None, Thread.members:List[ThreadMember]=[]
2022-04-26 10:39:53 discord.gateway      DEBUG    For Shard ID None: WebSocket Event: {'t': 'THREAD_MEMBERS_UPDATE', 's': 17, 'op': 0, 'd': {'member_count': 2, 'id': '[redacted]', 'added_members': [{'user_id': '890400000000000000', 'presence': {'user': {'username': 'bot', 'public_flags': 0, 'id': '890400000000000000', 'discriminator': '[redacted]', 'bot': True, 'avatar': '7f3b35011a5cdefa9b73c07aa36d3fdd'}, 'status': 'online', 'game': {'type': 0, 'session_id': None, 'name': 'in buggy waters', 'id': 'ec0b28a579ecb4bd', 'created_at': 1650423664256}, 'client_status': {'web': 'online'}, 'activities': [{'type': 0, 'name': 'in buggy waters', 'id': 'ec0b28a579ecb4bd', 'created_at': 1650423664256}]}, 'member': {'user': {'username': 'bot', 'public_flags': 0, 'id': '890400000000000000', 'discriminator': '[redacted]', 'bot': True, 'avatar': '7f3b35011a5cdefa9b73c07aa36d3fdd'}, 'roles': ['[redacted]', '[redacted]'], 'mute': False, 'joined_at': '2022-02-03T21:40:31.159000+00:00', 'hoisted_role': '[redacted]', 'flags': 0, 'deaf': False}, 'join_timestamp': '2022-04-26T17:39:53.757651+00:00', 'id': '[redacted]', 'flags': 0}], 'guild_id': '[redacted]'}}
2022-04-26 10:39:53 discord.client       DEBUG    Dispatching event socket_event_type
2022-04-26 10:39:53 discord.client       DEBUG    Dispatching event thread_member_join
2022-04-26 10:39:53 bot.events.threads DEBUG    !!! THREAD_MEMBER_JOIN: Handling event
2022-04-26 10:39:53 bot.events.threads DEBUG    >>> THREAD_MEMBER_JOIN: Event details; 'test' ([redacted]); ThreadMember=<ThreadMember id=890400000000000000 thread_id=[redacted] joined_at=datetime.datetime(2022, 4, 26, 17, 39, 53, 757651, tzinfo=datetime.timezone.utc)>, Member=bot#[redacted]
2022-04-26 10:39:53 bot.events.threads DEBUG    >>> THREAD_MEMBER_JOIN: Cached thread details; 'test' ([redacted]); thread.me=None, Thread.members:List[ThreadMember]=[<ThreadMember id=890400000000000000 thread_id=[redacted] joined_at=datetime.datetime(2022, 4, 26, 17, 39, 53, 757651, tzinfo=datetime.timezone.utc)>], Thread.members:List[Member]=[<Member id=890400000000000000 name='bot' discriminator='[redacted]' bot=True nick=None guild=<Guild id=[redacted] name="bot development" shard_id=0 chunked=True member_count=11>>]
2022-04-26 10:39:53 discord.gateway      DEBUG    For Shard ID None: WebSocket Event: {'t': 'THREAD_MEMBERS_UPDATE', 's': 18, 'op': 0, 'd': {'member_count': 3, 'id': '[redacted]', 'added_members': [{'user_id': '897100000000000000', 'presence': {'user': {'username': 'gay bot', 'id': '897100000000000000', 'discriminator': '[redacted]', 'bot': True, 'avatar': '39801670320babab422e028653bd7225'}, 'status': 'online', 'game': {'type': 0, 'session_id': None, 'name': 'in buggy waters', 'id': 'ec0b28a579ecb4bd', 'created_at': 1650994751513}, 'client_status': {'web': 'online'}, 'activities': [{'type': 0, 'name': 'in buggy waters', 'id': 'ec0b28a579ecb4bd', 'created_at': 1650994751513}]}, 'muted': False, 'mute_config': None, 'member': {'user': {'username': 'gay bot', 'id': '897100000000000000', 'discriminator': '[redacted]', 'bot': True, 'avatar': '39801670320babab422e028653bd7225'}, 'roles': ['[redacted]', '[redacted]'], 'mute': False, 'joined_at': '2022-04-21T02:55:43.620000+00:00', 'hoisted_role': '[redacted]', 'flags': 1, 'deaf': False}, 'join_timestamp': '2022-04-26T17:39:53.846437+00:00', 'id': '[redacted]', 'flags': 1}], 'guild_id': '[redacted]'}}
2022-04-26 10:39:53 discord.client       DEBUG    Dispatching event socket_event_type
2022-04-26 10:39:53 discord.client       DEBUG    Dispatching event thread_join
2022-04-26 10:39:53 discord.gateway      DEBUG    For Shard ID None: WebSocket Event: {'t': 'THREAD_CREATE', 's': 19, 'op': 0, 'd': {'type': 11, 'thread_metadata': {'locked': False, 'create_timestamp': '2022-04-26T17:39:53.535000+00:00', 'auto_archive_duration': 10080, 'archived': False, 'archive_timestamp': '2022-04-26T17:39:53.535000+00:00'}, 'rate_limit_per_user': 0, 'parent_id': '[redacted]', 'owner_id': '[redacted]', 'name': 'test', 'message_count': 0, 'member_count': 3, 'member': {'user_id': '897100000000000000', 'muted': False, 'mute_config': None, 'join_timestamp': '2022-04-26T17:39:53.846437+00:00', 'id': '[redacted]', 'flags': 1}, 'last_message_id': None, 'id': '[redacted]', 'guild_id': '[redacted]', 'flags': 0}}
2022-04-26 10:39:53 discord.client       DEBUG    Dispatching event socket_event_type
2022-04-26 10:39:53 bot.events.threads DEBUG    !!! THREAD_JOIN: Handling event
2022-04-26 10:39:53 bot.events.threads DEBUG    >>> THREAD_JOIN: Event details; 'test' ([redacted]); me=<ThreadMember id=897100000000000000 thread_id=[redacted] joined_at=datetime.datetime(2022, 4, 26, 17, 39, 53, 846437, tzinfo=datetime.timezone.utc)>, Thread.members:List[ThreadMember]=[<ThreadMember id=890400000000000000 thread_id=[redacted] joined_at=datetime.datetime(2022, 4, 26, 17, 39, 53, 757651, tzinfo=datetime.timezone.utc)>]
2022-04-26 10:39:53 bot.events.threads DEBUG    >>> THREAD_JOIN: Cached thread details; 'test' ([redacted]); thread.me=<ThreadMember id=897100000000000000 thread_id=[redacted] joined_at=datetime.datetime(2022, 4, 26, 17, 39, 53, 846437, tzinfo=datetime.timezone.utc)>, Thread.members:List[ThreadMember]=[]
2022-04-26 10:39:53 discord.http         DEBUG    POST https://discord.com/api/v9/channels/[redacted]/thread-members/@me with None has returned 204
2022-04-26 10:39:53 discord.http         DEBUG    POST https://discord.com/api/v9/channels/[redacted]/thread-members/@me has received 
2022-04-26 10:39:53 bot.events.threads DEBUG    >>> THREAD_JOIN: Joined thread 'test' ([redacted])
2022-04-26 10:39:53 bot.events.threads DEBUG    >>> THREAD_JOIN: Cached thread details; 'test' ([redacted]); thread.me=<ThreadMember id=897100000000000000 thread_id=[redacted] joined_at=datetime.datetime(2022, 4, 26, 17, 39, 53, 846437, tzinfo=datetime.timezone.utc)>, Thread.members:List[ThreadMember]=[]
2022-04-26 10:39:53 discord.gateway      DEBUG    For Shard ID None: WebSocket Event: {'t': 'MESSAGE_CREATE', 's': 20, 'op': 0, 'd': {'type': 0, 'tts': False, 'timestamp': '2022-04-26T17:39:53.913000+00:00', 'referenced_message': None, 'pinned': False, 'nonce': '968567108596137984', 'mentions': [], 'mention_roles': [], 'mention_everyone': False, 'member': {'roles': ['[redacted]'], 'mute': False, 'joined_at': '2021-10-08T01:35:51.577000+00:00', 'hoisted_role': None, 'flags': 0, 'deaf': False}, 'id': '[redacted]', 'flags': 0, 'embeds': [], 'edited_timestamp': None, 'content': 'test', 'components': [], 'channel_id': '[redacted]', 'author': {'username': '[redacted]', 'public_flags': 0, 'id': '[reacted]', 'discriminator': '[redacted]', 'avatar_decoration': None, 'avatar': '8f6be73f6c4854b0785fff75adf80076'}, 'attachments': [], 'guild_id': '[redacted]'}}
2022-04-26 10:39:53 discord.client       DEBUG    Dispatching event socket_event_type
2022-04-26 10:39:53 discord.client       DEBUG    Dispatching event message
2022-04-26 10:39:54 discord.http         DEBUG    POST https://discord.com/api/v9/channels/[redacted]/thread-members/@me with None has returned 204
2022-04-26 10:39:54 discord.http         DEBUG    POST https://discord.com/api/v9/channels/[redacted]/thread-members/@me has received 
2022-04-26 10:39:54 bot.events.threads DEBUG    >>> THREAD_MEMBER_JOIN: Joined thread 'test' ([redacted])
2022-04-26 10:39:54 bot.events.threads DEBUG    >>> THREAD_MEMBER_JOIN: Cached thread details; 'test' ([redacted]); thread.me=<ThreadMember id=897100000000000000 thread_id=[redacted] joined_at=datetime.datetime(2022, 4, 26, 17, 39, 53, 846437, tzinfo=datetime.timezone.utc)>, Thread.members:List[ThreadMember]=[]
2022-04-26 10:39:58 discord.gateway      DEBUG    For Shard ID None: WebSocket Event: {'t': 'THREAD_MEMBERS_UPDATE', 's': 21, 'op': 0, 'd': {'removed_member_ids': ['897100000000000000'], 'member_count': 2, 'id': '[redacted]', 'guild_id': '[redacted]'}}
2022-04-26 10:39:58 discord.client       DEBUG    Dispatching event socket_event_type
2022-04-26 10:39:58 discord.client       DEBUG    Dispatching event thread_remove
2022-04-26 10:39:58 bot.events.threads DEBUG    !!! Handling thread_remove event
2022-04-26 10:39:58 bot.events.threads DEBUG    >>> THREAD_REMOVE: Event before payload; 'test' ([redacted]); me=<ThreadMember id=897100000000000000 thread_id=[redacted] joined_at=datetime.datetime(2022, 4, 26, 17, 39, 53, 846437, tzinfo=datetime.timezone.utc)>, Thread.members:List[ThreadMember]=[]
2022-04-26 10:39:58 bot.events.threads DEBUG    >>> THREAD_REMOVE: Cached thread details; 'test' ([redacted]); thread.me=<ThreadMember id=897100000000000000 thread_id=[redacted] joined_at=datetime.datetime(2022, 4, 26, 17, 39, 53, 846437, tzinfo=datetime.timezone.utc)>, Thread.members:List[ThreadMember]=[]
2022-04-26 10:39:58 discord.gateway      DEBUG    For Shard ID None: WebSocket Event: {'t': 'MESSAGE_CREATE', 's': 22, 'op': 0, 'd': {'type': 2, 'tts': False, 'timestamp': '2022-04-26T17:39:58.639000+00:00', 'pinned': False, 'mentions': [{'username': 'gay bot', 'public_flags': 0, 'member': {'roles': ['[redacted]', '[redacted]'], 'mute': False, 'joined_at': '2022-04-21T02:55:43.620000+00:00', 'hoisted_role': '[redacted]', 'flags': 1, 'deaf': False}, 'id': '897100000000000000', 'discriminator': '[redacted]', 'bot': True, 'avatar_decoration': None, 'avatar': '39801670320babab422e028653bd7225'}], 'mention_roles': [], 'mention_everyone': False, 'member': {'roles': ['[redacted]'], 'mute': False, 'joined_at': '2021-10-08T01:35:51.577000+00:00', 'hoisted_role': None, 'flags': 0, 'deaf': False}, 'id': '[redacted]', 'flags': 0, 'embeds': [], 'edited_timestamp': None, 'content': '', 'components': [], 'channel_id': '[redacted]', 'author': {'username': '[redacted]', 'public_flags': 0, 'id': '[redacted]', 'discriminator': '[redacted]', 'avatar_decoration': None, 'avatar': '8f6be73f6c4854b0785fff75adf80076'}, 'attachments': [], 'guild_id': '[redacted]'}}
2022-04-26 10:39:58 discord.client       DEBUG    Dispatching event socket_event_type
2022-04-26 10:39:58 discord.client       DEBUG    Dispatching event message
2022-04-26 10:40:02 discord.gateway      DEBUG    For Shard ID None: WebSocket Event: {'t': 'THREAD_UPDATE', 's': 23, 'op': 0, 'd': {'type': 11, 'thread_metadata': {'locked': False, 'create_timestamp': '2022-04-26T17:39:53.535000+00:00', 'auto_archive_duration': 10080, 'archived': True, 'archive_timestamp': '2022-04-26T17:40:02.737778+00:00'}, 'rate_limit_per_user': 0, 'parent_id': '[redacted]', 'owner_id': '[redacted]', 'name': 'test', 'message_count': 2, 'member_count': 2, 'last_message_id': '[redacted]', 'id': '[redacted]', 'guild_id': '[redacted]', 'flags': 0}}
2022-04-26 10:40:02 discord.client       DEBUG    Dispatching event socket_event_type
2022-04-26 10:40:02 discord.client       DEBUG    Dispatching event thread_update
2022-04-26 10:40:02 bot.events.threads DEBUG    !!! Handling thread_update event
2022-04-26 10:40:02 bot.events.threads DEBUG    >>> THREAD_UPDATE: Event before payload; 'test' ([redacted]); archived=False, Thread.members:List[ThreadMember]=[], Thread.members:List[Member]=[]
2022-04-26 10:40:02 bot.events.threads DEBUG    >>> THREAD_UPDATE: Event after payload; 'test' ([redacted]); archived=True, Thread.members:List[ThreadMember]=[], Thread.members:List[Member]=[]
2022-04-26 10:40:02 bot.events.threads DEBUG    >>> THREAD_UPDATE: Cached thread details; 'test' ([redacted]); thread.me=<ThreadMember id=897100000000000000 thread_id=[redacted] joined_at=datetime.datetime(2022, 4, 26, 17, 39, 53, 846437, tzinfo=datetime.timezone.utc)>, Thread.members:List[ThreadMember]=[]
2022-04-26 10:40:05 discord.gateway      DEBUG    For Shard ID None: WebSocket Event: {'t': 'THREAD_UPDATE', 's': 24, 'op': 0, 'd': {'type': 11, 'thread_metadata': {'locked': False, 'create_timestamp': '2022-04-26T17:39:53.535000+00:00', 'auto_archive_duration': 10080, 'archived': False, 'archive_timestamp': '2022-04-26T17:40:04.983287+00:00'}, 'rate_limit_per_user': 0, 'parent_id': '[redacted]', 'owner_id': '[redacted]', 'name': 'test', 'message_count': 2, 'member_count': 2, 'last_message_id': '[redacted]', 'id': '[redacted]', 'guild_id': '[redacted]', 'flags': 0}}
2022-04-26 10:40:05 discord.client       DEBUG    Dispatching event socket_event_type
2022-04-26 10:40:05 discord.client       DEBUG    Dispatching event thread_update
2022-04-26 10:40:05 bot.events.threads DEBUG    !!! Handling thread_update event
2022-04-26 10:40:05 bot.events.threads DEBUG    >>> THREAD_UPDATE: Event before payload; 'test' ([redacted]); archived=True, Thread.members:List[ThreadMember]=[], Thread.members:List[Member]=[]
2022-04-26 10:40:05 bot.events.threads DEBUG    >>> THREAD_UPDATE: Event after payload; 'test' ([redacted]); archived=False, Thread.members:List[ThreadMember]=[], Thread.members:List[Member]=[]
2022-04-26 10:40:05 bot.events.threads DEBUG    >>> THREAD_UPDATE: Cached thread details; 'test' ([redacted]); thread.me=<ThreadMember id=897100000000000000 thread_id=[redacted] joined_at=datetime.datetime(2022, 4, 26, 17, 39, 53, 846437, tzinfo=datetime.timezone.utc)>, Thread.members:List[ThreadMember]=[]

There are other events that are impacted by the same issue outlined here, as can be seen from the log output above. I believe y'all are aware of this behavior, in part, as it's documented that, most of the time, Thread.members is empty due to the gateway not returning anything.

Minimally, I'd like to see the documentation updated for Thread.me. It's misleading, saying that it might not be available, but not that it might be wrong. Ideally, the framework would call Thread.fetch_members() to populate these properties with correct information.

@erincerys erincerys added the unconfirmed bug A bug report that needs triaging label Apr 26, 2022
@Lulalaby
Copy link
Member

Big up for this detailed report.
We'll investigate this as fast as we can.

Thank you!

@Lulalaby Lulalaby added bug Something isn't working priority: medium Medium Priority status: todo This issue needs work and removed unconfirmed bug A bug report that needs triaging labels Apr 26, 2022
@Lulalaby
Copy link
Member

Status Update please
@Pycord-Development/maintainers

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
bug Something isn't working priority: medium Medium Priority status: todo This issue needs work
Projects
Status: Done
Development

Successfully merging a pull request may close this issue.

4 participants