Repository: hikari-py/hikari
Pull request #681 (2021-06-26 04:24:19)

Use Signature's eval_str argument in resolve_signature (3.10+ specific)
### Summary
Use Signature's eval_str argument in resolve_signature (3.10+ specific)

### Checklist
<!-- Make sure to tick all the following boxes by putting an `x` in between (like this `[x]`) -->
- [x] I have run `nox` and all the pipelines have passed.
- [x] I have made unittests according to the code I have added/modified/deleted.

### Related issues
<!--
To mention an issue use `#issue-id` and to mention a merge request use `!merge-request-id`
To close/fix an issue use `Close #issue-id` or `Fix #issue-id` (depending on the merge request)
-->


davfsa: How does this differentiate from us evaluating them? 

Patch:
diff --git a/CODE_OF_CONDUCT.md b/CODE_OF_CONDUCT.md
index b4097697..e541e35a 100644
--- a/CODE_OF_CONDUCT.md
+++ b/CODE_OF_CONDUCT.md
@@ -55,7 +55,7 @@ further defined and clarified by project maintainers.
 ## Enforcement
 
 Instances of abusive, harassing, or otherwise unacceptable behavior may be
-reported by contacting the project team at nekoka.tt@outlook.com. All
+reported by contacting the project team at davfsa@gmail.com. All
 complaints will be reviewed and investigated and will result in a response that
 is deemed necessary and appropriate to the circumstances. The project team is
 obligated to maintain confidentiality with regard to the reporter of an incident.
diff --git a/hikari/api/rest.py b/hikari/api/rest.py
index 841c661f..b24f1b09 100644
--- a/hikari/api/rest.py
+++ b/hikari/api/rest.py
@@ -513,7 +513,7 @@ async def edit_permission_overwrites(
         channel: snowflakes.SnowflakeishOr[channels_.GuildChannel],
         target: snowflakes.Snowflakeish,
         *,
-        target_type: channels_.PermissionOverwriteType,
+        target_type: typing.Union[channels_.PermissionOverwriteType, int],
         allow: undefined.UndefinedOr[permissions_.Permissions] = undefined.UNDEFINED,
         deny: undefined.UndefinedOr[permissions_.Permissions] = undefined.UNDEFINED,
         reason: undefined.UndefinedOr[str] = undefined.UNDEFINED,
@@ -528,7 +528,7 @@ async def edit_permission_overwrites(
             snowflakes.Snowflakeish, users.PartialUser, guilds.PartialRole, channels_.PermissionOverwrite
         ],
         *,
-        target_type: undefined.UndefinedOr[channels_.PermissionOverwriteType] = undefined.UNDEFINED,
+        target_type: undefined.UndefinedOr[typing.Union[channels_.PermissionOverwriteType, int]] = undefined.UNDEFINED,
         allow: undefined.UndefinedOr[permissions_.Permissions] = undefined.UNDEFINED,
         deny: undefined.UndefinedOr[permissions_.Permissions] = undefined.UNDEFINED,
         reason: undefined.UndefinedOr[str] = undefined.UNDEFINED,
@@ -546,7 +546,7 @@ async def edit_permission_overwrites(
 
         Other Parameters
         ----------------
-        target_type : hikari.undefined.UndefinedOr[hikari.channels.PermissionOverwriteType]
+        target_type : hikari.undefined.UndefinedOr[typing.Union[hikari.channels.PermissionOverwriteType, int]]
             If provided, the type of the target to update. If unset, will attempt to get
             the type from `target`.
         allow : hikari.undefined.UndefinedOr[hikari.permissions.Permissions]
diff --git a/hikari/channels.py b/hikari/channels.py
index f1f2d73a..30cad18c 100644
--- a/hikari/channels.py
+++ b/hikari/channels.py
@@ -67,8 +67,10 @@
     from hikari import iterators
     from hikari import messages
     from hikari import users
+    from hikari import voices
     from hikari import webhooks
     from hikari.api import special_endpoints
+    from hikari.internal import time
 
 
 @typing.final
@@ -287,7 +289,7 @@ class PermissionOverwrite(snowflakes.Unique):
     )
     """The ID of this entity."""
 
-    type: typing.Union[PermissionOverwriteType, str] = attr.field(
+    type: typing.Union[PermissionOverwriteType, int] = attr.field(
         converter=PermissionOverwriteType, hash=True, repr=True
     )
     """The type of entity this overwrite targets."""
@@ -338,6 +340,42 @@ class PartialChannel(snowflakes.Unique):
     def __str__(self) -> str:
         return self.name if self.name is not None else f"Unnamed {self.__class__.__name__} ID {self.id}"
 
+    async def delete(self) -> PartialChannel:
+        """Delete a channel in a guild, or close a DM.
+
+        Returns
+        -------
+        hikari.channels.PartialChannel
+            Object of the channel that was deleted.
+
+        Raises
+        ------
+        hikari.errors.UnauthorizedError
+            If you are unauthorized to make the request (invalid/missing token).
+        hikari.errors.ForbiddenError
+            If you are missing the `MANAGE_CHANNEL` permission in the channel.
+        hikari.errors.NotFoundError
+            If the channel is not found.
+        hikari.errors.RateLimitTooLongError
+            Raised in the event that a rate limit occurs that is
+            longer than `max_rate_limit` when making a request.
+        hikari.errors.RateLimitedError
+            Usually, Hikari will handle and retry on hitting
+            rate-limits automatically. This includes most bucket-specific
+            rate-limits and global rate-limits. In some rare edge cases,
+            however, Discord implements other undocumented rules for
+            rate-limiting, such as limits per attribute. These cannot be
+            detected or handled normally by Hikari due to their undocumented
+            nature, and will trigger this exception if they occur.
+        hikari.errors.InternalServerError
+            If an internal error occurs on Discord while handling the request.
+
+        !!! note
+            For Public servers, the set 'Rules' or 'Guidelines' channels and the
+            'Public Server Updates' channel cannot be deleted.
+        """
+        return await self.app.rest.delete_channel(self.id)
+
 
 class TextChannel(PartialChannel, abc.ABC):
     """A channel that can have text messages in it."""
@@ -346,7 +384,7 @@ class TextChannel(PartialChannel, abc.ABC):
     __slots__: typing.Sequence[str] = ()
 
     # TODO: add examples to this and the REST method this invokes.
-    def history(
+    def fetch_history(
         self,
         *,
         before: undefined.UndefinedOr[snowflakes.SearchableSnowflakeishOr[snowflakes.Unique]] = undefined.UNDEFINED,
@@ -400,6 +438,45 @@ def history(
         """  # noqa: E501 - Line too long
         return self.app.rest.fetch_messages(self.id, before=before, after=after, around=around)
 
+    async def fetch_message(self, message: snowflakes.SnowflakeishOr[messages.PartialMessage]) -> messages.Message:
+        """Fetch a specific message in the given text channel.
+
+        Parameters
+        ----------
+        message : hikari.snowflakes.SnowflakeishOr[hikari.messages.PartialMessage]
+            The message to fetch. This may be the object or the ID of an
+            existing channel.
+
+        Returns
+        -------
+        hikari.messages.Message
+            The requested message.
+
+        Raises
+        ------
+        hikari.errors.UnauthorizedError
+            If you are unauthorized to make the request (invalid/missing token).
+        hikari.errors.ForbiddenError
+            If you are missing the `READ_MESSAGE_HISTORY` in the channel.
+        hikari.errors.NotFoundError
+            If the channel is not found or the message is not found in the
+            given text channel.
+        hikari.errors.RateLimitTooLongError
+            Raised in the event that a rate limit occurs that is
+            longer than `max_rate_limit` when making a request.
+        hikari.errors.RateLimitedError
+            Usually, Hikari will handle and retry on hitting
+            rate-limits automatically. This includes most bucket-specific
+            rate-limits and global rate-limits. In some rare edge cases,
+            however, Discord implements other undocumented rules for
+            rate-limiting, such as limits per attribute. These cannot be
+            detected or handled normally by Hikari due to their undocumented
+            nature, and will trigger this exception if they occur.
+        hikari.errors.InternalServerError
+            If an internal error occurs on Discord while handling the request.
+        """
+        return await self.app.rest.fetch_message(self.id, message)
+
     async def send(
         self,
         content: undefined.UndefinedOr[typing.Any] = undefined.UNDEFINED,
@@ -577,6 +654,159 @@ def trigger_typing(self) -> special_endpoints.TypingIndicator:
         """
         return self.app.rest.trigger_typing(self.id)
 
+    async def fetch_pins(self) -> typing.Sequence[messages.Message]:
+        """Fetch the pinned messages in this text channel.
+
+        Returns
+        -------
+        typing.Sequence[hikari.messages.Message]
+            The pinned messages in this text channel.
+
+        Raises
+        ------
+        hikari.errors.UnauthorizedError
+            If you are unauthorized to make the request (invalid/missing token).
+        hikari.errors.ForbiddenError
+            If you are missing the `READ_MESSAGES` in the channel.
+        hikari.errors.NotFoundError
+            If the channel is not found.
+        hikari.errors.RateLimitTooLongError
+            Raised in the event that a rate limit occurs that is
+            longer than `max_rate_limit` when making a request.
+        hikari.errors.RateLimitedError
+            Usually, Hikari will handle and retry on hitting
+            rate-limits automatically. This includes most bucket-specific
+            rate-limits and global rate-limits. In some rare edge cases,
+            however, Discord implements other undocumented rules for
+            rate-limiting, such as limits per attribute. These cannot be
+            detected or handled normally by Hikari due to their undocumented
+            nature, and will trigger this exception if they occur.
+        hikari.errors.InternalServerError
+            If an internal error occurs on Discord while handling the request.
+        """
+        return await self.app.rest.fetch_pins(self.id)
+
+    async def pin_message(self, message: snowflakes.SnowflakeishOr[messages.PartialMessage]) -> None:
+        """Pin an existing message in the text channel.
+
+        Parameters
+        ----------
+        message : hikari.snowflakes.SnowflakeishOr[hikari.messages.PartialMessage]
+            The message to pin. This may be the object or the ID
+            of an existing message.
+
+        Raises
+        ------
+        hikari.errors.UnauthorizedError
+            If you are unauthorized to make the request (invalid/missing token).
+        hikari.errors.ForbiddenError
+            If you are missing the `MANAGE_MESSAGES` in the channel.
+        hikari.errors.NotFoundError
+            If the channel is not found, or if the message does not exist in
+            the given channel.
+        hikari.errors.RateLimitTooLongError
+            Raised in the event that a rate limit occurs that is
+            longer than `max_rate_limit` when making a request.
+        hikari.errors.RateLimitedError
+            Usually, Hikari will handle and retry on hitting
+            rate-limits automatically. This includes most bucket-specific
+            rate-limits and global rate-limits. In some rare edge cases,
+            however, Discord implements other undocumented rules for
+            rate-limiting, such as limits per attribute. These cannot be
+            detected or handled normally by Hikari due to their undocumented
+            nature, and will trigger this exception if they occur.
+        hikari.errors.InternalServerError
+            If an internal error occurs on Discord while handling the request.
+        """
+        return await self.app.rest.pin_message(self.id, message)
+
+    async def unpin_message(self, message: snowflakes.SnowflakeishOr[messages.PartialMessage]) -> None:
+        """Unpin a given message from the text channel.
+
+        Parameters
+        ----------
+        message : hikari.snowflakes.SnowflakeishOr[hikari.messages.PartialMessage]
+            The message to unpin. This may be the object or the ID of an
+            existing message.
+
+        Raises
+        ------
+        hikari.errors.UnauthorizedError
+            If you are unauthorized to make the request (invalid/missing token).
+        hikari.errors.ForbiddenError
+            If you are missing the `MANAGE_MESSAGES` permission.
+        hikari.errors.NotFoundError
+            If the channel is not found or the message is not a pinned message
+            in the given channel.
+        hikari.errors.RateLimitTooLongError
+            Raised in the event that a rate limit occurs that is
+            longer than `max_rate_limit` when making a request.
+        hikari.errors.RateLimitedError
+            Usually, Hikari will handle and retry on hitting
+            rate-limits automatically. This includes most bucket-specific
+            rate-limits and global rate-limits. In some rare edge cases,
+            however, Discord implements other undocumented rules for
+            rate-limiting, such as limits per attribute. These cannot be
+            detected or handled normally by Hikari due to their undocumented
+            nature, and will trigger this exception if they occur.
+        hikari.errors.InternalServerError
+            If an internal error occurs on Discord while handling the request.
+        """
+        return await self.app.rest.unpin_message(self.id, message)
+
+    async def delete_messages(
+        self,
+        messages: typing.Union[
+            snowflakes.SnowflakeishOr[messages.PartialMessage],
+            snowflakes.SnowflakeishIterable[messages.PartialMessage],
+        ],
+        /,
+        *other_messages: snowflakes.SnowflakeishOr[messages.PartialMessage],
+    ) -> None:
+        """Bulk-delete messages from the channel.
+
+        Parameters
+        ----------
+        messages : typing.Union[hikari.snowflakes.SnowflakeishOr[hikari.messages.PartialMessage], hikari.snowflakes.SnowflakeishIterable[hikari.messages.PartialMessage]]
+            Either the object/ID of an existing message to delete or an iterable
+            of the objects and/or IDs of existing messages to delete.
+
+        Other Parameters
+        ----------------
+        *other_messages : hikari.snowflakes.SnowflakeishOr[hikari.messages.PartialMessage]
+            The objects and/or IDs of other existing messages to delete.
+
+        !!! note
+            This API endpoint will only be able to delete 100 messages
+            at a time. For anything more than this, multiple requests will
+            be executed one-after-the-other, since the rate limits for this
+            endpoint do not favour more than one request per bucket.
+
+            If one message is left over from chunking per 100 messages, or
+            only one message is passed to this coroutine function, then the
+            logic is expected to defer to `delete_message`. The implication
+            of this is that the `delete_message` endpoint is ratelimited
+            by a different bucket with different usage rates.
+
+        !!! warning
+            This endpoint is not atomic. If an error occurs midway through
+            a bulk delete, you will **not** be able to revert any changes made
+            up to this point.
+
+        !!! warning
+            Specifying any messages more than 14 days old will cause the call
+            to fail, potentially with partial completion.
+
+        Raises
+        ------
+        hikari.errors.BulkDeleteError
+            An error containing the messages successfully deleted, and the
+            messages that were not removed. The
+            `builtins.BaseException.__cause__` of the exception will be the
+            original error that terminated this process.
+        """  # noqa: E501 - Line too long
+        return await self.app.rest.delete_messages(self.id, messages, *other_messages)
+
 
 @attr.define(hash=True, kw_only=True, weakref_slot=False)
 class PrivateChannel(PartialChannel):
@@ -741,6 +971,247 @@ def shard_id(self) -> typing.Optional[int]:
 
         return None
 
+    async def fetch_guild(self) -> guilds.PartialGuild:
+        """Fetch the guild linked to this channel.
+
+        Parameters
+        ----------
+        guild : hikari.snowflakes.SnowflakeishOr[hikari.guilds.PartialGuild]
+            The guild to fetch. This can be the object
+            or the ID of an existing guild.
+
+        Returns
+        -------
+        hikari.guilds.RESTGuild
+            The requested guild.
+
+        Raises
+        ------
+        hikari.errors.ForbiddenError
+            If you are not part of the guild.
+        hikari.errors.NotFoundError
+            If the guild is not found.
+        hikari.errors.UnauthorizedError
+            If you are unauthorized to make the request (invalid/missing token).
+        hikari.errors.RateLimitTooLongError
+            Raised in the event that a rate limit occurs that is
+            longer than `max_rate_limit` when making a request.
+        hikari.errors.RateLimitedError
+            Usually, Hikari will handle and retry on hitting
+            rate-limits automatically. This includes most bucket-specific
+            rate-limits and global rate-limits. In some rare edge cases,
+            however, Discord implements other undocumented rules for
+            rate-limiting, such as limits per attribute. These cannot be
+            detected or handled normally by Hikari due to their undocumented
+            nature, and will trigger this exception if they occur.
+        hikari.errors.InternalServerError
+            If an internal error occurs on Discord while handling the request.
+        """
+        return await self.app.rest.fetch_guild(self.guild_id)
+
+    async def edit_overwrite(
+        self,
+        target: typing.Union[snowflakes.Snowflakeish, users.PartialUser, guilds.PartialRole, PermissionOverwrite],
+        *,
+        target_type: undefined.UndefinedOr[typing.Union[PermissionOverwriteType, int]] = undefined.UNDEFINED,
+        allow: undefined.UndefinedOr[permissions.Permissions] = undefined.UNDEFINED,
+        deny: undefined.UndefinedOr[permissions.Permissions] = undefined.UNDEFINED,
+        reason: undefined.UndefinedOr[str] = undefined.UNDEFINED,
+    ) -> None:
+        """Edit permissions for a specific entity in the given guild channel.
+
+           This creates new overwrite for the channel, if there no other overwrites present.
+
+        Parameters
+        ----------
+        target : typing.Union[hikari.users.PartialUser, hikari.guilds.PartialRole, hikari.channels.PermissionOverwrite, hikari.snowflakes.Snowflakeish]
+            The channel overwrite to edit. This may be the object or the ID of an
+            existing overwrite.
+
+        Other Parameters
+        ----------------
+        target_type : hikari.undefined.UndefinedOr[typing.Union[hikari.channels.PermissionOverwriteType, int]]
+            If provided, the type of the target to update. If unset, will attempt to get
+            the type from `target`.
+        allow : hikari.undefined.UndefinedOr[hikari.permissions.Permissions]
+            If provided, the new vale of all allowed permissions.
+        deny : hikari.undefined.UndefinedOr[hikari.permissions.Permissions]
+            If provided, the new vale of all disallowed permissions.
+        reason : hikari.undefined.UndefinedOr[builtins.str]
+            If provided, the reason that will be recorded in the audit logs.
+            Maximum of 512 characters.
+
+        Raises
+        ------
+        builtins.TypeError
+            If `target_type` is unset and we were unable to determine the type
+            from `target`.
+        hikari.errors.BadRequestError
+            If any of the fields that are passed have an invalid value.
+        hikari.errors.UnauthorizedError
+            If you are unauthorized to make the request (invalid/missing token).
+        hikari.errors.ForbiddenError
+            If you are missing the `MANAGE_PERMISSIONS` permission in the channel.
+        hikari.errors.NotFoundError
+            If the channel is not found or the target is not found if it is
+            a role.
+        hikari.errors.RateLimitTooLongError
+            Raised in the event that a rate limit occurs that is
+            longer than `max_rate_limit` when making a request.
+        hikari.errors.RateLimitedError
+            Usually, Hikari will handle and retry on hitting
+            rate-limits automatically. This includes most bucket-specific
+            rate-limits and global rate-limits. In some rare edge cases,
+            however, Discord implements other undocumented rules for
+            rate-limiting, such as limits per attribute. These cannot be
+            detected or handled normally by Hikari due to their undocumented
+            nature, and will trigger this exception if they occur.
+        hikari.errors.InternalServerError
+            If an internal error occurs on Discord while handling the request.
+        """  # noqa: E501 - Line too long
+        if target_type is undefined.UNDEFINED:
+            assert not isinstance(
+                target, int
+            ), "Cannot determine the type of the target to update. Try specifying 'target_type' manually."
+            return await self.app.rest.edit_permission_overwrites(
+                self.id, target, allow=allow, deny=deny, reason=reason
+            )
+
+        return await self.app.rest.edit_permission_overwrites(
+            self.id, typing.cast(int, target), target_type=target_type, allow=allow, deny=deny, reason=reason
+        )
+
+    async def remove_overwrite(
+        self,
+        target: snowflakes.SnowflakeishOr[
+            typing.Union[PermissionOverwrite, guilds.PartialRole, users.PartialUser, snowflakes.Snowflakeish]
+        ],
+    ) -> None:
+        """Delete a custom permission for an entity in a given guild channel.
+
+        Parameters
+        ----------
+        target : typing.Union[hikari.users.PartialUser, hikari.guilds.PartialRole, hikari.channels.PermissionOverwrite, hikari.snowflakes.Snowflakeish]
+            The channel overwrite to delete.
+
+        Raises
+        ------
+        hikari.errors.UnauthorizedError
+            If you are unauthorized to make the request (invalid/missing token).
+        hikari.errors.ForbiddenError
+            If you are missing the `MANAGE_PERMISSIONS` permission in the channel.
+        hikari.errors.NotFoundError
+            If the channel is not found or the target is not found.
+        hikari.errors.RateLimitTooLongError
+            Raised in the event that a rate limit occurs that is
+            longer than `max_rate_limit` when making a request.
+        hikari.errors.RateLimitedError
+            Usually, Hikari will handle and retry on hitting
+            rate-limits automatically. This includes most bucket-specific
+            rate-limits and global rate-limits. In some rare edge cases,
+            however, Discord implements other undocumented rules for
+            rate-limiting, such as limits per attribute. These cannot be
+            detected or handled normally by Hikari due to their undocumented
+            nature, and will trigger this exception if they occur.
+        hikari.errors.InternalServerError
+            If an internal error occurs on Discord while handling the request.
+        """  # noqa: E501 - Line too long
+        return await self.app.rest.delete_permission_overwrite(self.id, target)
+
+    async def edit(
+        self,
+        *,
+        name: undefined.UndefinedOr[str] = undefined.UNDEFINED,
+        position: undefined.UndefinedOr[int] = undefined.UNDEFINED,
+        topic: undefined.UndefinedOr[str] = undefined.UNDEFINED,
+        nsfw: undefined.UndefinedOr[bool] = undefined.UNDEFINED,
+        bitrate: undefined.UndefinedOr[int] = undefined.UNDEFINED,
+        video_quality_mode: undefined.UndefinedOr[typing.Union[VideoQualityMode, int]] = undefined.UNDEFINED,
+        user_limit: undefined.UndefinedOr[int] = undefined.UNDEFINED,
+        rate_limit_per_user: undefined.UndefinedOr[time.Intervalish] = undefined.UNDEFINED,
+        region: undefined.UndefinedOr[voices.VoiceRegionish] = undefined.UNDEFINED,
+        permission_overwrites: undefined.UndefinedOr[typing.Sequence[PermissionOverwrite]] = undefined.UNDEFINED,
+        parent_category: undefined.UndefinedOr[snowflakes.SnowflakeishOr[GuildCategory]] = undefined.UNDEFINED,
+        reason: undefined.UndefinedOr[str] = undefined.UNDEFINED,
+    ) -> PartialChannel:
+        """Edit the text channel.
+
+        Other Parameters
+        ----------------
+        name : hikari.undefined.UndefinedOr[[builtins.str]
+            If provided, the new name for the channel.
+        position : hikari.undefined.UndefinedOr[[builtins.int]
+            If provided, the new position for the channel.
+        topic : hikari.undefined.UndefinedOr[builtins.str]
+            If provided, the new topic for the channel.
+        nsfw : hikari.undefined.UndefinedOr[builtins.bool]
+            If provided, whether the channel should be marked as NSFW or not.
+        bitrate : hikari.undefined.UndefinedOr[builtins.int]
+            If provided, the new bitrate for the channel.
+        video_quality_mode: hikari.undefined.UndefinedOr[typing.Union[hikari.channels.VideoQualityMode, builtins.int]]
+            If provided, the new video quality mode for the channel.
+        user_limit : hikari.undefined.UndefinedOr[builtins.int]
+            If provided, the new user limit in the channel.
+        rate_limit_per_user : hikari.undefined.UndefinedOr[hikari.internal.time.Intervalish]
+            If provided, the new rate limit per user in the channel.
+        region : hikari.undefined.UndefinedOr[hikari.voices.VoiceRegionish]
+            If provided, the voice region to set for this channel. Passing
+            `builtins.None` here will set it to "auto" mode where the used
+            region will be decided based on the first person who connects to it
+            when it's empty.
+        permission_overwrites : hikari.undefined.UndefinedOr[typing.Sequence[hikari.channels.PermissionOverwrite]]
+            If provided, the new permission overwrites for the channel.
+        parent_category : hikari.undefined.UndefinedOr[hikari.snowflakes.SnowflakeishOr[hikari.channels.GuildCategory]]
+            If provided, the new guild category for the channel.
+        reason : hikari.undefined.UndefinedOr[builtins.str]
+            If provided, the reason that will be recorded in the audit logs.
+            Maximum of 512 characters.
+
+        Returns
+        -------
+        hikari.channels.PartialChannel
+            The edited channel.
+
+        Raises
+        ------
+        hikari.errors.BadRequestError
+            If any of the fields that are passed have an invalid value.
+        hikari.errors.UnauthorizedError
+            If you are unauthorized to make the request (invalid/missing token).
+        hikari.errors.ForbiddenError
+            If you are missing permissions to edit the channel.
+        hikari.errors.NotFoundError
+            If the channel is not found.
+        hikari.errors.RateLimitTooLongError
+            Raised in the event that a rate limit occurs that is
+            longer than `max_rate_limit` when making a request.
+        hikari.errors.RateLimitedError
+            Usually, Hikari will handle and retry on hitting
+            rate-limits automatically. This includes most bucket-specific
+            rate-limits and global rate-limits. In some rare edge cases,
+            however, Discord implements other undocumented rules for
+            rate-limiting, such as limits per attribute. These cannot be
+            detected or handled normally by Hikari due to their undocumented
+            nature, and will trigger this exception if they occur.
+        hikari.errors.InternalServerError
+            If an internal error occurs on Discord while handling the request.
+        """  # noqa: E501 - Line too long
+        return await self.app.rest.edit_channel(
+            self.id,
+            name=name,
+            position=position,
+            topic=topic,
+            nsfw=nsfw,
+            bitrate=bitrate,
+            video_quality_mode=video_quality_mode,
+            user_limit=user_limit,
+            rate_limit_per_user=rate_limit_per_user,
+            region=region,
+            permission_overwrites=permission_overwrites,
+            parent_category=parent_category,
+            reason=reason,
+        )
+
 
 @attr.define(hash=True, kw_only=True, weakref_slot=False)
 class GuildCategory(GuildChannel):
diff --git a/hikari/impl/rest.py b/hikari/impl/rest.py
index 5f486c83..110a65cd 100644
--- a/hikari/impl/rest.py
+++ b/hikari/impl/rest.py
@@ -1001,7 +1001,7 @@ async def edit_permission_overwrites(
             snowflakes.Snowflakeish, users.PartialUser, guilds.PartialRole, channels_.PermissionOverwrite
         ],
         *,
-        target_type: undefined.UndefinedOr[typing.Union[channels_.PermissionOverwriteType, str]] = undefined.UNDEFINED,
+        target_type: undefined.UndefinedOr[typing.Union[channels_.PermissionOverwriteType, int]] = undefined.UNDEFINED,
         allow: undefined.UndefinedOr[permissions_.Permissions] = undefined.UNDEFINED,
         deny: undefined.UndefinedOr[permissions_.Permissions] = undefined.UNDEFINED,
         reason: undefined.UndefinedOr[str] = undefined.UNDEFINED,
diff --git a/hikari/internal/reflect.py b/hikari/internal/reflect.py
index 2e4bbd64..8166e80b 100644
--- a/hikari/internal/reflect.py
+++ b/hikari/internal/reflect.py
@@ -28,6 +28,7 @@
 
 import functools
 import inspect
+import sys
 import typing
 
 if typing.TYPE_CHECKING:
@@ -56,6 +57,9 @@ def resolve_signature(func: typing.Callable[..., typing.Any]) -> inspect.Signatu
         A `inspect.Signature` object with all forward reference annotations
         resolved.
     """
+    if sys.version_info >= (3, 10):
+        return inspect.signature(func, eval_str=True)
+
     signature = inspect.signature(func)
     resolved_typehints = typing.get_type_hints(func)
     params = []
diff --git a/hikari/internal/ux.py b/hikari/internal/ux.py
index c14aa81d..1c584cae 100644
--- a/hikari/internal/ux.py
+++ b/hikari/internal/ux.py
@@ -136,6 +136,10 @@ def init_logging(
     logging.captureWarnings(True)
 
 
+_UNCONDITIONAL_ANSI_FLAGS: typing.Final[typing.FrozenSet[str]] = frozenset(("PYCHARM_HOSTED", "WT_SESSION"))
+"""Set of env variables which always indicate that ANSI flags should be included."""
+
+
 def print_banner(package: typing.Optional[str], allow_color: bool, force_color: bool) -> None:
     """Print a banner of choice to `sys.stdout`.
 
@@ -223,30 +227,34 @@ def supports_color(allow_color: bool, force_color: bool) -> bool:
     # isatty is not always implemented, https://code.djangoproject.com/ticket/6223
     is_a_tty = hasattr(sys.stdout, "isatty") and sys.stdout.isatty()
 
-    if os.getenv("CLICOLOR_FORCE", "0") != "0" or force_color:
+    clicolor = os.environ.get("CLICOLOR")
+    if os.environ.get("CLICOLOR_FORCE", "0") != "0" or force_color:
         # https://bixense.com/clicolors/
         return True
-    elif os.getenv("CLICOLOR", "0") != "0" and is_a_tty:
+    if clicolor is not None and clicolor != "0" and is_a_tty:
         # https://bixense.com/clicolors/
         return True
-    elif os.getenv("COLORTERM", "").casefold() in ("truecolor", "24bit"):
+    if clicolor == "0":
+        # https://bixense.com/clicolors/
+        return False
+    if os.environ.get("COLORTERM", "").casefold() in ("truecolor", "24bit"):
         # Seems to be used by Gnome Terminal, and Tmpod will beat me if I don't add it.
         # https://gist.github.com/XVilka/8346728#true-color-detection
         return True
 
     plat = sys.platform
-    color_support = False
-
-    if plat != "Pocket PC":
-        if plat == "win32":
-            color_support |= os.getenv("TERM_PROGRAM", None) in ("mintty", "Terminus")
-            color_support |= "ANSICON" in os.environ
-            color_support &= is_a_tty
-        else:
-            color_support = is_a_tty
+    if plat == "Pocket PC":
+        return False
 
-        color_support |= bool(os.getenv("PYCHARM_HOSTED", ""))
+    color_support = False
+    if plat == "win32":
+        color_support |= os.environ.get("TERM_PROGRAM") in ("mintty", "Terminus")
+        color_support |= "ANSICON" in os.environ
+        color_support &= is_a_tty
+    else:
+        color_support = is_a_tty
 
+    color_support |= bool(os.environ.keys() & _UNCONDITIONAL_ANSI_FLAGS)
     return color_support
 
 


Test patch:
diff --git a/tests/hikari/internal/test_reflect.py b/tests/hikari/internal/test_reflect.py
index ff8d382d..672235b1 100644
--- a/tests/hikari/internal/test_reflect.py
+++ b/tests/hikari/internal/test_reflect.py
@@ -19,12 +19,18 @@
 # LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
 # OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
 # SOFTWARE.
+import inspect
+import sys
 import typing
 
+import mock
+import pytest
+
 from hikari.internal import reflect
 
 
-class TestResolveSignature:
+@pytest.mark.skipif(sys.version_info >= (3, 10), reason="This strategy is specific to 3.10 > versions")
+class TestResolveSignatureOldStrategy:
     def test_handles_normal_references(self):
         def foo(bar: str, bat: int) -> str:
             ...
@@ -110,3 +116,14 @@ def foo(bar: typing.Optional[typing.Iterator[int]]):
 
         signature = reflect.resolve_signature(foo)
         assert signature.parameters["bar"].annotation == typing.Optional[typing.Iterator[int]]
+
+
+@pytest.mark.skipif(sys.version_info < (3, 10), reason="This strategy is specific to 3.10 <= versions")
+def test_resolve_signature():
+    foo = object()
+
+    with mock.patch.object(inspect, "signature") as signature:
+        sig = reflect.resolve_signature(foo)
+
+    assert sig is signature.return_value
+    signature.assert_called_once_with(foo, eval_str=True)
diff --git a/tests/hikari/internal/test_ux.py b/tests/hikari/internal/test_ux.py
index 45672c5d..7c76df4b 100644
--- a/tests/hikari/internal/test_ux.py
+++ b/tests/hikari/internal/test_ux.py
@@ -233,48 +233,36 @@ def test_when_not_allow_color(self):
         assert ux.supports_color(False, True) is False
 
     def test_when_CLICOLOR_FORCE_in_env(self):
-        with mock.patch.object(os, "getenv", return_value="1") as getenv:
+        with mock.patch.dict(os.environ, {"CLICOLOR_FORCE": "1"}, clear=True):
             assert ux.supports_color(True, False) is True
 
-        getenv.assert_called_once_with("CLICOLOR_FORCE", "0")
-
     def test_when_force_color(self):
-        with mock.patch.object(os, "getenv", return_value="0") as getenv:
+        with mock.patch.dict(os.environ, {"CLICOLOR_FORCE": "0"}, clear=True):
             assert ux.supports_color(True, True) is True
 
-        getenv.assert_called_once_with("CLICOLOR_FORCE", "0")
-
     def test_when_CLICOLOR_and_is_a_tty(self):
         with mock.patch.object(sys.stdout, "isatty", return_value=True):
-            with mock.patch.object(os, "getenv", side_effect=["0", "1"]) as getenv:
+            with mock.patch.dict(os.environ, {"CLICOLOR_FORCE": "0", "CLICOLOR": "1"}, clear=True):
                 assert ux.supports_color(True, False) is True
 
-        assert getenv.call_count == 2
-        getenv.assert_has_calls([mock.call("CLICOLOR_FORCE", "0"), mock.call("CLICOLOR", "0")])
+    def test_when_CLICOLOR_is_0(self):
+        with mock.patch.object(sys.stdout, "isatty", return_value=True):
+            with mock.patch.dict(os.environ, {"CLICOLOR_FORCE": "0", "CLICOLOR": "0"}, clear=True):
+                assert ux.supports_color(True, False) is False
 
     @pytest.mark.parametrize("colorterm", ["truecolor", "24bit", "TRUECOLOR", "24BIT"])
     def test_when_COLORTERM_has_correct_value(self, colorterm):
-        with mock.patch.object(os, "getenv", side_effect=["0", "0", colorterm]) as getenv:
+        with mock.patch.dict(os.environ, {"COLORTERM": colorterm}, clear=True):
             assert ux.supports_color(True, False) is True
 
-        assert getenv.call_count == 3
-        getenv.assert_has_calls(
-            [mock.call("CLICOLOR_FORCE", "0"), mock.call("CLICOLOR", "0"), mock.call("COLORTERM", "")]
-        )
-
     def test_when_plat_is_Pocket_PC(self):
         stack = contextlib.ExitStack()
-        getenv = stack.enter_context(mock.patch.object(os, "getenv", return_value="0"))
+        stack.enter_context(mock.patch.dict(os.environ, {}, clear=True))
         stack.enter_context(mock.patch.object(sys, "platform", new="Pocket PC"))
 
         with stack:
             assert ux.supports_color(True, False) is False
 
-        assert getenv.call_count == 3
-        getenv.assert_has_calls(
-            [mock.call("CLICOLOR_FORCE", "0"), mock.call("CLICOLOR", "0"), mock.call("COLORTERM", "")]
-        )
-
     @pytest.mark.parametrize(
         ("term_program", "ansicon", "isatty", "expected"),
         [
@@ -288,78 +276,49 @@ def test_when_plat_is_Pocket_PC(self):
         ],
     )
     def test_when_plat_is_win32(self, term_program, ansicon, isatty, expected):
+        environ = {"TERM_PROGRAM": term_program}
+        if ansicon:
+            environ["ANSICON"] = "ooga booga"
+
         stack = contextlib.ExitStack()
-        getenv = stack.enter_context(mock.patch.object(os, "getenv", side_effect=["0", "0", "", term_program, ""]))
+        stack.enter_context(mock.patch.dict(os.environ, environ, clear=True))
         stack.enter_context(mock.patch.object(sys.stdout, "isatty", return_value=isatty))
         stack.enter_context(mock.patch.object(sys, "platform", new="win32"))
-        stack.enter_context(mock.patch.object(os, "environ", new=["ANSICON"] if ansicon else []))
 
         with stack:
             assert ux.supports_color(True, False) is expected
 
-        assert getenv.call_count == 5
-        getenv.assert_has_calls(
-            [
-                mock.call("CLICOLOR_FORCE", "0"),
-                mock.call("CLICOLOR", "0"),
-                mock.call("COLORTERM", ""),
-                mock.call("TERM_PROGRAM", None),
-                mock.call("PYCHARM_HOSTED", ""),
-            ]
-        )
-
     @pytest.mark.parametrize("isatty", [True, False])
     def test_when_plat_is_not_win32(self, isatty):
         stack = contextlib.ExitStack()
-        getenv = stack.enter_context(mock.patch.object(os, "getenv", side_effect=["0", "0", "", ""]))
+        stack.enter_context(mock.patch.dict(os.environ, {}, clear=True))
         stack.enter_context(mock.patch.object(sys.stdout, "isatty", return_value=isatty))
         stack.enter_context(mock.patch.object(sys, "platform", new="linux"))
 
         with stack:
             assert ux.supports_color(True, False) is isatty
 
-        assert getenv.call_count == 4
-        getenv.assert_has_calls(
-            [
-                mock.call("CLICOLOR_FORCE", "0"),
-                mock.call("CLICOLOR", "0"),
-                mock.call("COLORTERM", ""),
-                mock.call("PYCHARM_HOSTED", ""),
-            ]
-        )
-
     @pytest.mark.parametrize("isatty", [True, False])
     @pytest.mark.parametrize("plat", ["linux", "win32"])
     def test_when_PYCHARM_HOSTED(self, isatty, plat):
         stack = contextlib.ExitStack()
-        getenv = stack.enter_context(mock.patch.object(os, "getenv", return_value="0"))
+        stack.enter_context(mock.patch.dict(os.environ, {"PYCHARM_HOSTED": "OOGA BOOGA"}, clear=True))
         stack.enter_context(mock.patch.object(sys.stdout, "isatty", return_value=isatty))
         stack.enter_context(mock.patch.object(sys, "platform", new=plat))
 
         with stack:
             assert ux.supports_color(True, False) is True
 
-        if plat == "win32":
-            assert getenv.call_count == 5
-            getenv.assert_has_calls(
-                [
-                    mock.call("CLICOLOR_FORCE", "0"),
-                    mock.call("CLICOLOR", "0"),
-                    mock.call("COLORTERM", ""),
-                    mock.call("TERM_PROGRAM", None),
-                    mock.call("PYCHARM_HOSTED", ""),
-                ]
-            )
-        else:
-            assert getenv.call_count == 4
-            getenv.assert_has_calls(
-                [
-                    mock.call("CLICOLOR_FORCE", "0"),
-                    mock.call("CLICOLOR", "0"),
-                    mock.call("COLORTERM", ""),
-                    mock.call("PYCHARM_HOSTED", ""),
-                ]
-            )
+    @pytest.mark.parametrize("isatty", [True, False])
+    @pytest.mark.parametrize("plat", ["linux", "win32"])
+    def test_when_WT_SESSION(self, isatty, plat):
+        stack = contextlib.ExitStack()
+        stack.enter_context(mock.patch.dict(os.environ, {"WT_SESSION": "OOGA BOOGA"}, clear=True))
+        stack.enter_context(mock.patch.object(sys.stdout, "isatty", return_value=isatty))
+        stack.enter_context(mock.patch.object(sys, "platform", new=plat))
+
+        with stack:
+            assert ux.supports_color(True, False) is True
 
 
 class TestHikariVersion:
diff --git a/tests/hikari/test_channels.py b/tests/hikari/test_channels.py
index 0b65028f..c21de3eb 100644
--- a/tests/hikari/test_channels.py
+++ b/tests/hikari/test_channels.py
@@ -28,6 +28,7 @@
 from hikari import files
 from hikari import permissions
 from hikari import snowflakes
+from hikari import undefined
 from hikari import users
 from tests.hikari import hikari_test_helpers
 
@@ -194,10 +195,10 @@ def model(self, mock_app):
             type=channels.ChannelType.GUILD_TEXT,
         )
 
-    async def test_history(self, model):
+    async def test_fetch_history(self, model):
         model.app.rest.fetch_messages = mock.AsyncMock()
 
-        await model.history(
+        await model.fetch_history(
             before=datetime.datetime(2020, 4, 1, 1, 0, 0),
             after=datetime.datetime(2020, 4, 1, 0, 0, 0),
             around=datetime.datetime(2020, 4, 1, 0, 30, 0),
@@ -210,6 +211,41 @@ async def test_history(self, model):
             around=datetime.datetime(2020, 4, 1, 0, 30, 0),
         )
 
+    async def test_fetch_message(self, model):
+        model.app.rest.fetch_message = mock.AsyncMock()
+
+        assert await model.fetch_message(133742069) == model.app.rest.fetch_message.return_value
+
+        model.app.rest.fetch_message.assert_awaited_once_with(12345679, 133742069)
+
+    async def test_fetch_pins(self, model):
+        model.app.rest.fetch_pins = mock.AsyncMock()
+
+        await model.fetch_pins()
+
+        model.app.rest.fetch_pins.assert_awaited_once_with(12345679)
+
+    async def test_pin_message(self, model):
+        model.app.rest.pin_message = mock.AsyncMock()
+
+        assert await model.pin_message(77790) == model.app.rest.pin_message.return_value
+
+        model.app.rest.pin_message.assert_awaited_once_with(12345679, 77790)
+
+    async def test_unpin_message(self, model):
+        model.app.rest.unpin_message = mock.AsyncMock()
+
+        assert await model.unpin_message(77790) == model.app.rest.unpin_message.return_value
+
+        model.app.rest.unpin_message.assert_awaited_once_with(12345679, 77790)
+
+    async def test_delete_messages(self, model):
+        model.app.rest.delete_messages = mock.AsyncMock()
+
+        await model.delete_messages([77790, 88890, 1800], 1337)
+
+        model.app.rest.delete_messages.assert_awaited_once_with(12345679, [77790, 88890, 1800], 1337)
+
     async def test_send(self, model):
         model.app.rest.create_message = mock.AsyncMock()
         mock_attachment = object()
@@ -283,3 +319,84 @@ def test_shard_id_property_when_guild_id_is_not_None(self, model):
 
     def test_mention_property(self, model):
         assert model.mention == "<#69420>"
+
+    @pytest.mark.asyncio()
+    async def test_edit_overwrite(self, model):
+        model.app.rest.edit_permission_overwrites = mock.AsyncMock()
+        user = mock.Mock(users.PartialUser)
+        await model.edit_overwrite(
+            333,
+            target_type=user,
+            allow=permissions.Permissions.BAN_MEMBERS,
+            deny=permissions.Permissions.CONNECT,
+            reason="vrooom vroom",
+        )
+
+        model.app.rest.edit_permission_overwrites.assert_called_once_with(
+            69420,
+            333,
+            target_type=user,
+            allow=permissions.Permissions.BAN_MEMBERS,
+            deny=permissions.Permissions.CONNECT,
+            reason="vrooom vroom",
+        )
+
+    @pytest.mark.asyncio()
+    async def test_edit_overwrite_target_type_none(self, model):
+        model.app.rest.edit_permission_overwrites = mock.AsyncMock()
+        user = mock.Mock(users.PartialUser)
+        await model.edit_overwrite(
+            user, allow=permissions.Permissions.BAN_MEMBERS, deny=permissions.Permissions.CONNECT, reason="vrooom vroom"
+        )
+
+        model.app.rest.edit_permission_overwrites.assert_called_once_with(
+            69420,
+            user,
+            allow=permissions.Permissions.BAN_MEMBERS,
+            deny=permissions.Permissions.CONNECT,
+            reason="vrooom vroom",
+        )
+
+    @pytest.mark.asyncio()
+    async def test_remove_overwrite(self, model):
+        model.app.rest.delete_permission_overwrite = mock.AsyncMock()
+
+        await model.remove_overwrite(333)
+
+        model.app.rest.delete_permission_overwrite.assert_called_once_with(
+            69420,
+            333,
+        )
+
+    @pytest.mark.asyncio()
+    async def test_fetch_guild(self, model):
+        model.app.rest.fetch_guild = mock.AsyncMock()
+
+        assert await model.fetch_guild() == model.app.rest.fetch_guild.return_value
+
+        model.app.rest.fetch_guild.assert_awaited_once_with(123456789)
+
+    @pytest.mark.asyncio()
+    async def test_edit(self, model):
+        model.app.rest.edit_channel = mock.AsyncMock()
+
+        assert (
+            await model.edit(name="Supa fast boike", bitrate=420, reason="left right")
+            == model.app.rest.edit_channel.return_value
+        )
+
+        model.app.rest.edit_channel.assert_awaited_once_with(
+            69420,
+            name="Supa fast boike",
+            position=undefined.UNDEFINED,
+            topic=undefined.UNDEFINED,
+            nsfw=undefined.UNDEFINED,
+            bitrate=420,
+            video_quality_mode=undefined.UNDEFINED,
+            user_limit=undefined.UNDEFINED,
+            rate_limit_per_user=undefined.UNDEFINED,
+            region=undefined.UNDEFINED,
+            permission_overwrites=undefined.UNDEFINED,
+            parent_category=undefined.UNDEFINED,
+            reason="left right",
+        )