diff --git a/changes/955.feature.md b/changes/955.feature.md new file mode 100644 index 0000000000..6caee94563 --- /dev/null +++ b/changes/955.feature.md @@ -0,0 +1 @@ +Added support for GET /users/@me/guilds/{guild}/member diff --git a/hikari/api/rest.py b/hikari/api/rest.py index 523059543d..1072c3a15a 100644 --- a/hikari/api/rest.py +++ b/hikari/api/rest.py @@ -4894,6 +4894,40 @@ def fetch_members( doesn't require any intents. """ + @abc.abstractmethod + async def fetch_my_member(self, guild: snowflakes.SnowflakeishOr[guilds.PartialGuild]) -> guilds.Member: + """Fetch the Oauth token's associated member in a guild. + + !!! warning + This endpoint can only be used with a Bearer token. Using this + with a Bot token will result in a `hikari.errors.UnauthorizedError`. + + Returns + ------- + hikari.guilds.Member + The associated guild member. + + Raises + ------ + hikari.errors.UnauthorizedError + If you are unauthorized to make the request (invalid/missing token). + hikari.errors.NotFoundError + If the guild 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. + """ + @abc.abstractmethod async def search_members( self, diff --git a/hikari/applications.py b/hikari/applications.py index 6566d3445c..67a7361e7a 100644 --- a/hikari/applications.py +++ b/hikari/applications.py @@ -221,6 +221,9 @@ class OAuth2Scope(str, enums.Enum): This is used during authorization code grants. """ + GUILDS_MEMBERS_READ = "guilds.members.read" + """Used to read the current user's guild members.""" + @typing.final class ConnectionVisibility(int, enums.Enum): diff --git a/hikari/impl/rest.py b/hikari/impl/rest.py index c8d5c802bd..1e3f0d0044 100644 --- a/hikari/impl/rest.py +++ b/hikari/impl/rest.py @@ -2678,6 +2678,12 @@ def fetch_members( entity_factory=self._entity_factory, request_call=self._request, guild=guild ) + async def fetch_my_member(self, guild: snowflakes.SnowflakeishOr[guilds.PartialGuild]) -> guilds.Member: + route = routes.GET_MY_GUILD_MEMBER.compile(guild=guild) + response = await self._request(route) + assert isinstance(response, dict) + return self._entity_factory.deserialize_member(response, guild_id=snowflakes.Snowflake(guild)) + async def search_members( self, guild: snowflakes.SnowflakeishOr[guilds.PartialGuild], diff --git a/hikari/internal/routes.py b/hikari/internal/routes.py index b3cfd8dc03..d803993eb4 100644 --- a/hikari/internal/routes.py +++ b/hikari/internal/routes.py @@ -438,6 +438,7 @@ def compile_to_file( # @me POST_MY_CHANNELS: typing.Final[Route] = Route(POST, "/users/@me/channels") GET_MY_CONNECTIONS: typing.Final[Route] = Route(GET, "/users/@me/connections") # OAuth2 only +GET_MY_GUILD_MEMBER: typing.Final[Route] = Route(GET, "/users/@me/guilds/{guild}/member") # OAuth2 only DELETE_MY_GUILD: typing.Final[Route] = Route(DELETE, "/users/@me/guilds/{guild}") GET_MY_GUILDS: typing.Final[Route] = Route(GET, "/users/@me/guilds") diff --git a/tests/hikari/impl/test_rest.py b/tests/hikari/impl/test_rest.py index 421d7d90f7..106671c80f 100644 --- a/tests/hikari/impl/test_rest.py +++ b/tests/hikari/impl/test_rest.py @@ -3438,6 +3438,18 @@ async def test_fetch_member(self, rest_client): rest_client._request.assert_awaited_once_with(expected_route) rest_client._entity_factory.deserialize_member.assert_called_once_with({"id": "789"}, guild_id=123) + async def test_fetch_my_member(self, rest_client) -> guilds.Member: + expected_route = routes.GET_MY_GUILD_MEMBER.compile(guild=45123) + rest_client._request = mock.AsyncMock(return_value={"id": "595995"}) + + result = await rest_client.fetch_my_member(StubModel(45123)) + + assert result is rest_client._entity_factory.deserialize_member.return_value + rest_client._request.assert_awaited_once_with(expected_route) + rest_client._entity_factory.deserialize_member.assert_called_once_with( + rest_client._request.return_value, guild_id=45123 + ) + async def test_search_members(self, rest_client): member = StubModel(645234123) expected_route = routes.GET_GUILD_MEMBERS_SEARCH.compile(guild=645234123)