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.
- 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
[X] I have searched the open issues for duplicates.
[X] I have shown the entire traceback, if possible.
[X] 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.
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.
Summary
Thread member cache not accurate
Reproduction Steps
Craft an event listener for
on_thread_join
:Thread.me
Craft an event listener for
on_thread_update
:Thread.me
Thread.me
andThread.members
Craft an event listener for
on_thread_remove
:Thread.me
andThread.members
Start the bot
In your Discord client on a guild where the bot is present:
Minimal Reproducible Code
Expected Results
On handling the
THREAD_MEMBERS_UPDATE
websocket event and subsequentthread_remove
PyCord event:Thread.me
would beNone
, andThread.members
would contain the remaining members of the thread.On handling the
thread_update
event:Thread.me
would beNone
, andThread.members
would contain the remaining members of the thread.Actual Results
On handling the
THREAD_MEMBERS_UPDATE
websocket event and subsequentthread_remove
PyCord event:removed_members
attribute. Despite this, thethread_remove
PyCord event continues to return the bot user inThread.me
when handling this event.On handling the
thread_update
event:Thread
object cache is inaccurate - theme
property is aThreadMember
that is of the bot user and themembers
property is an empty list.Intents
System Information
Checklist
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.
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 callThread.fetch_members()
to populate these properties with correct information.