Repository: serenity-rs/serenity (language: rust)

Issue:
Message::category_id does not respect threaded messages
I believe `Cache::categories` is supposed to contain only categories. This understanding is supported by the implementation of `Cache::category_count()` which just returns its length. However some method implementations seem to incorrectly assume that the map contains all channels mapped to their respective categories. Particularly `Cache::channel_category_id` and by extension `Message::category_id` which calls the former in its implementation.

The effect is that `Message::category_id` will always return `None`, since a message's channel cannot be a category so it will not be found in `Cache::categories`. `Cache::channel_category_id` will return the `Some` variant only if the argument channel ID is itself a category, in which case it will return the same ID that its been given which doesn't seem very useful.

Getting the category ID of a message or channel is not entirely trivial because of threads, of which the parent_id is not the category_id, so these methods would be useful if they were correctly implemented. I might try to make a PR next week (if the above understanding is correct).


Discussion / hints:
A lot of the methods talked about have been removed in a general simplification (and performance improvement) of the caching system. The only problem I see is that `Message::category_id` does not understand threads, which I'm going to look into fixing now.

Fix patch:
diff --git a/src/model/channel/message.rs b/src/model/channel/message.rs
index e767dbda829..bd2b090435c 100644
--- a/src/model/channel/message.rs
+++ b/src/model/channel/message.rs
@@ -813,7 +813,26 @@ impl Message {
 
     /// Retrieves the message channel's category ID if the channel has one.
     pub async fn category_id(&self, cache_http: impl CacheHttp) -> Option<ChannelId> {
-        self.channel_id.to_channel(cache_http).await.ok()?.guild()?.parent_id
+        #[cfg(feature = "cache")]
+        if let Some(cache) = cache_http.cache() {
+            if let Some(guild) = cache.guild(self.guild_id?) {
+                let channel = guild.channels.get(&self.channel_id)?;
+                return if channel.thread_metadata.is_some() {
+                    let thread_parent = guild.channels.get(&channel.parent_id?)?;
+                    thread_parent.parent_id
+                } else {
+                    channel.parent_id
+                };
+            }
+        }
+
+        let channel = self.channel_id.to_channel(&cache_http).await.ok()?.guild()?;
+        if channel.thread_metadata.is_some() {
+            let thread_parent = channel.parent_id?.to_channel(cache_http).await.ok()?.guild()?;
+            thread_parent.parent_id
+        } else {
+            channel.parent_id
+        }
     }
 }
 


Test patch: