Repository: Cog-Creators/Red-DiscordBot

Issue:
[V3 Utils/Mod] Mod helpers allow users to be passed, raising AttributeError
### Type:

- Bug

### Brief description of the problem
Following #1185, `is_admin_or_superior` and `is_mod_or_superior` allow `discord.User` to be passed in, but when this occurs, `AttributeError` is raised.

**UPDATE:** these functions really shouldn't be used for `discord.User` at all, since it doesn't make any logical sense to check for permissions on a `User` object without any guild. So to resolve this issue we should really be type-checking the arguments to the function before using it, i.e. in mod.py and filter.py.

### Expected behavior
Helpers should raise `TypeError` (as it was prior to #1185)

### Actual behavior
```py
AttributeError: 'User' object has no attribute 'guild'
  File "discord/client.py", line 223, in _run_event
    yield from coro(*args, **kwargs)
  File "filter/filter.py", line 266, in on_message_edit
    mod_or_superior = await is_mod_or_superior(self.bot, obj=author)
  File "redbot/core/utils/mod.py", line 134, in is_mod_or_superior
    server = obj.guild
```

### Steps to reproduce

1. Pass a `discord.User` into `is_admin_or_superior` or `is_mod_or_superior`
[V3 Utils/Mod] Mod helpers allow users to be passed, raising AttributeError
### Type:

- Bug

### Brief description of the problem
Following #1185, `is_admin_or_superior` and `is_mod_or_superior` allow `discord.User` to be passed in, but when this occurs, `AttributeError` is raised.

**UPDATE:** these functions really shouldn't be used for `discord.User` at all, since it doesn't make any logical sense to check for permissions on a `User` object without any guild. So to resolve this issue we should really be type-checking the arguments to the function before using it, i.e. in mod.py and filter.py.

### Expected behavior
Helpers should raise `TypeError` (as it was prior to #1185)

### Actual behavior
```py
AttributeError: 'User' object has no attribute 'guild'
  File "discord/client.py", line 223, in _run_event
    yield from coro(*args, **kwargs)
  File "filter/filter.py", line 266, in on_message_edit
    mod_or_superior = await is_mod_or_superior(self.bot, obj=author)
  File "redbot/core/utils/mod.py", line 134, in is_mod_or_superior
    server = obj.guild
```

### Steps to reproduce

1. Pass a `discord.User` into `is_admin_or_superior` or `is_mod_or_superior`


Discussion:
[Corresponding sentry issue](https://sentry.io/will-tekulve/bot-development/issues/420900834/)
[Corresponding sentry issue](https://sentry.io/will-tekulve/bot-development/issues/420900834/)

Resolving patch:
diff --git a/redbot/cogs/filter/filter.py b/redbot/cogs/filter/filter.py
--- a/redbot/cogs/filter/filter.py
+++ b/redbot/cogs/filter/filter.py
@@ -249,10 +249,12 @@ async def on_message(self, message: discord.Message):
             return
         author = message.author
         valid_user = isinstance(author, discord.Member) and not author.bot
-
+        if not valid_user:
+            return
+        
         #  Bots and mods or superior are ignored from the filter
         mod_or_superior = await is_mod_or_superior(self.bot, obj=author)
-        if not valid_user or mod_or_superior:
+        if mod_or_superior:
             return
 
         await self.check_filter(message)
@@ -261,10 +263,13 @@ async def on_message_edit(self, _, message):
         author = message.author
         if message.guild is None or self.bot.user == author:
             return
-
         valid_user = isinstance(author, discord.Member) and not author.bot
+        if not valid_user:
+            return
+        
+        #  Bots and mods or superior are ignored from the filter
         mod_or_superior = await is_mod_or_superior(self.bot, obj=author)
-        if not valid_user or mod_or_superior:
+        if mod_or_superior:
             return
 
         await self.check_filter(message)
diff --git a/redbot/cogs/mod/mod.py b/redbot/cogs/mod/mod.py
--- a/redbot/cogs/mod/mod.py
+++ b/redbot/cogs/mod/mod.py
@@ -1241,10 +1241,12 @@ async def on_message(self, message):
         if message.guild is None or self.bot.user == author:
             return
         valid_user = isinstance(author, discord.Member) and not author.bot
-
+        if not valid_user:
+            return
+        
         #  Bots and mods or superior are ignored from the filter
         mod_or_superior = await is_mod_or_superior(self.bot, obj=author)
-        if not valid_user or mod_or_superior:
+        if mod_or_superior:
             return
         deleted = await self.check_duplicates(message)
         if not deleted:
@@ -1382,4 +1384,4 @@ def are_overwrites_empty(overwrites):
     "permissions_issue": "Failed to mute user. I need the manage roles "
                          "permission and the user I'm muting must be "
                          "lower than myself in the role hierarchy."
-}
\ No newline at end of file
+}
diff --git a/redbot/core/utils/mod.py b/redbot/core/utils/mod.py
--- a/redbot/core/utils/mod.py
+++ b/redbot/core/utils/mod.py
@@ -124,7 +124,7 @@ async def is_mod_or_superior(
     user = None
     if isinstance(obj, discord.Message):
         user = obj.author
-    elif isinstance(obj, discord.Member) or isinstance(obj, discord.User):
+    elif isinstance(obj, discord.Member):
         user = obj
     elif isinstance(obj, discord.Role):
         pass
@@ -214,7 +214,7 @@ async def is_admin_or_superior(
     user = None
     if isinstance(obj, discord.Message):
         user = obj.author
-    elif isinstance(obj, discord.Member) or isinstance(obj, discord.User):
+    elif isinstance(obj, discord.Member):
         user = obj
     elif isinstance(obj, discord.Role):
         pass


Test patch: