Repository: Cog-Creators/Red-DiscordBot

Issue:
`bot.allowed_by_whitelist_blacklist` doesn't work when passed with `guild_id` and `who_id`
### What Red version are you using?

3.4.7

### What were you trying to do?

I tried to run this code:
```py
await bot.allowed_by_whitelist_blacklist(who_id=133801473317404673, guild_id=guild.id)
```

### What did you expect to happen?

It to return a bool.

### What actually happened?

It raised an exception:
```py
  File "/home/ubuntu/red-data/cogs/CogManager/cogs/diagnoser/diagnoser.py", line 165, in _check_whitelist_blacklist_issues
    is_role_related = not await self.bot.allowed_by_whitelist_blacklist(
  File "/home/ubuntu/red-venv/lib/python3.8/site-packages/redbot/core/bot.py", line 597, in allowed_by_whitelist_blacklist
    if guild.owner_id == who.id:
AttributeError: 'Object' object has no attribute 'owner_id'
```

### How can we reproduce this error?

Running this in an eval (in a guild text channel) should be enough:
```py
await bot.allowed_by_whitelist_blacklist(who_id=133801473317404673, guild_id=guild.id)
```

This is *a bit* problematic, guild ID doesn't give us enough data to check whether the user should be permitted to do an action as we need a guild's owner id for that.

On the bright note though, this was broken for 17 months (sic!) so we can (mostly) safely change the signature here (without the need of doing a 3.x.0 release) to require `guild` rather than `guild_id` as this parameter configuration has **never** worked.
But even with that said, it would not hurt to leave the `guild_id` param (with deprecation warning and it raising an AttributeError if it can't save itself by using `bot.get_guild`) until the next 3.x.0 release just so that we don't outright break someone's usage of the method in the cases where they weren't affected.


Discussion:
Updated the PR description with my updated, less radical opinion so as not to cause any more errors than needed for cogs that happen to use this that way even though it fails for most calls.

Right now, I'm a bit unsure whether it's really worth it but I'm open to it so that we don't cause new errors just because a part of the method didn't really work.

Resolving patch:
diff --git a/redbot/core/bot.py b/redbot/core/bot.py
--- a/redbot/core/bot.py
+++ b/redbot/core/bot.py
@@ -50,7 +50,7 @@
 )
 from .rpc import RPCMixin
 from .utils import common_filters, AsyncIter
-from .utils._internal_utils import send_to_owners_with_prefix_replaced
+from .utils._internal_utils import deprecated_removed, send_to_owners_with_prefix_replaced
 
 CUSTOM_GROUPS = "CUSTOM_GROUPS"
 SHARED_API_TOKENS = "SHARED_API_TOKENS"
@@ -519,6 +519,7 @@ async def allowed_by_whitelist_blacklist(
         who: Optional[Union[discord.Member, discord.User]] = None,
         *,
         who_id: Optional[int] = None,
+        guild: Optional[discord.Guild] = None,
         guild_id: Optional[int] = None,
         role_ids: Optional[List[int]] = None,
     ) -> bool:
@@ -547,12 +548,24 @@ async def allowed_by_whitelist_blacklist(
         who_id : Optional[int]
             The id of the user or member to check
             If not providing a value for ``who``, this is a required parameter.
-        guild_id : Optional[int]
+        guild : Optional[discord.Guild]
             When used in conjunction with a provided value for ``who_id``, checks
             the lists for the corresponding guild as well.
+            This is ignored when ``who`` is passed.
+        guild_id : Optional[int]
+            When used in conjunction with a provided value for ``who_id``, checks
+            the lists for the corresponding guild as well. This should not be used
+            as it has unfixable bug that can cause it to raise an exception when
+            the guild with the given ID couldn't have been found.
+            This is ignored when ``who`` is passed.
+
+            .. deprecated-removed:: 3.4.8 30
+                Use ``guild`` parameter instead.
+
         role_ids : Optional[List[int]]
             When used with both ``who_id`` and ``guild_id``, checks the role ids provided.
             This is required for accurate checking of members in a guild if providing ids.
+            This is ignored when ``who`` is passed.
 
         Raises
         ------
@@ -568,7 +581,6 @@ async def allowed_by_whitelist_blacklist(
         # All config calls are delayed until needed in this section
         # All changes should be made keeping in mind that this is also used as a global check
 
-        guild = None
         mocked = False  # used for an accurate delayed role id expansion later.
         if not who:
             if not who_id:
@@ -576,7 +588,17 @@ async def allowed_by_whitelist_blacklist(
             mocked = True
             who = discord.Object(id=who_id)
             if guild_id:
-                guild = discord.Object(id=guild_id)
+                deprecated_removed(
+                    "`guild_id` parameter",
+                    "3.4.8",
+                    30,
+                    "Use `guild` parameter instead.",
+                    stacklevel=2,
+                )
+                if guild:
+                    raise ValueError(
+                        "`guild_id` should not be passed when `guild` is already passed."
+                    )
         else:
             guild = getattr(who, "guild", None)
 
@@ -593,6 +615,15 @@ async def allowed_by_whitelist_blacklist(
             if who.id in global_blacklist:
                 return False
 
+        if mocked and guild_id:
+            guild = self.get_guild(guild_id)
+            if guild is None:
+                # this is an AttributeError due to backwards-compatibility concerns
+                raise AttributeError(
+                    "Couldn't get the guild with the given ID. `guild` parameter needs to be used"
+                    " over the deprecated `guild_id` to resolve this."
+                )
+
         if guild:
             if guild.owner_id == who.id:
                 return True


Test patch: