Skip to content

Commit d026418

Browse files
Paillat-devCopilotpre-commit-ci[bot]
authored
feat: ✨ Add fetch_roles_member_counts method to Guild and corresponding HTTP route (Pycord-Development#3020)
* ✨ Add `fetch_roles_member_counts` method to `Guild` and corresponding HTTP route * 📝 CHANGELOG.md * ♻️ Add GuildRoleCounts class for mapping role IDs to member counts * ✨ Add custom __repr__ method to GuildRoleCounts for improved representation * 📝 Add usage examples for fetching member counts in GuildRoleCounts * Update discord/guild.py Co-authored-by: Copilot <[email protected]> Signed-off-by: Paillat <[email protected]> * style(pre-commit): auto fixes from pre-commit.com hooks * 📝 Document raised exception for fetching role member counts in GuildRoleCounts * ♻️ Enhance GuildRoleCounts with TypeVar and add __contains__ method for role ID checks * 📝 CHANGELOG.md --------- Signed-off-by: Paillat <[email protected]> Co-authored-by: Copilot <[email protected]> Co-authored-by: pre-commit-ci[bot] <66853113+pre-commit-ci[bot]@users.noreply.github.com>
1 parent 43b422a commit d026418

File tree

4 files changed

+131
-1
lines changed

4 files changed

+131
-1
lines changed

CHANGELOG.md

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,8 @@ These changes are available on the `master` branch, but have not yet been releas
1717
- Added `Attachment.read_chunked` and added optional `chunksize` argument to
1818
`Attachment.save` for retrieving attachments in chunks.
1919
([#2956](https://github.com/Pycord-Development/pycord/pull/2956))
20+
- Added `Guild.fetch_roles_member_counts` method and `GuildRoleCounts` class.
21+
([#3020](https://github.com/Pycord-Development/pycord/pull/3020))
2022

2123
### Changed
2224

discord/guild.py

Lines changed: 119 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -37,10 +37,13 @@
3737
Optional,
3838
Sequence,
3939
Tuple,
40+
TypeVar,
4041
Union,
4142
overload,
4243
)
4344

45+
from typing_extensions import override
46+
4447
from . import abc, utils
4548
from .asset import Asset
4649
from .automod import AutoModAction, AutoModRule, AutoModTriggerMetadata
@@ -94,7 +97,7 @@
9497
from .welcome_screen import WelcomeScreen, WelcomeScreenChannel
9598
from .widget import Widget
9699

97-
__all__ = ("BanEntry", "Guild")
100+
__all__ = ("BanEntry", "Guild", "GuildRoleCounts")
98101

99102
MISSING = utils.MISSING
100103

@@ -132,6 +135,8 @@
132135
]
133136
ByCategoryItem = Tuple[Optional[CategoryChannel], List[GuildChannel]]
134137

138+
T = TypeVar("T")
139+
135140

136141
class BanEntry(NamedTuple):
137142
reason: str | None
@@ -146,6 +151,81 @@ class _GuildLimit(NamedTuple):
146151
filesize: int
147152

148153

154+
class GuildRoleCounts(dict[int, int]):
155+
"""A dictionary subclass that maps role IDs to their member counts.
156+
157+
This class allows accessing member counts by either role ID (:class:`int`) or by
158+
a Snowflake object (which has an ``.id`` attribute).
159+
160+
.. versionadded:: 2.7
161+
"""
162+
163+
@override
164+
def __repr__(self):
165+
return f"<GuildRoleCounts {super().__repr__()}>"
166+
167+
@override
168+
def __getitem__(self, key: int | abc.Snowflake) -> int:
169+
"""Get the member count for a role.
170+
171+
Parameters
172+
----------
173+
key: Union[:class:`int`, :class:`~discord.abc.Snowflake`]
174+
The role ID or a Snowflake object (e.g., a :class:`Role`).
175+
176+
Returns
177+
-------
178+
:class:`int`
179+
The member count for the role.
180+
181+
Raises
182+
------
183+
KeyError
184+
The role ID was not found.
185+
"""
186+
if isinstance(key, abc.Snowflake):
187+
key = key.id
188+
return super().__getitem__(key)
189+
190+
@override
191+
def get(self, key: int | abc.Snowflake, default: T = None) -> int | T:
192+
"""Get the member count for a role, returning a default if not found.
193+
194+
Parameters
195+
----------
196+
key: Union[:class:`int`, :class:`~discord.abc.Snowflake`]
197+
The role ID or a Snowflake object (e.g., a :class:`Role`).
198+
default: Any
199+
The value to return if the role ID is not found.
200+
201+
Returns
202+
-------
203+
Optional[:class:`int`]
204+
The member count for the role, or ``default`` if the role is not present.
205+
"""
206+
if isinstance(key, abc.Snowflake):
207+
key = key.id
208+
return super().get(key, default)
209+
210+
@override
211+
def __contains__(self, key: int | abc.Snowflake) -> bool:
212+
"""Check if a role ID or Snowflake object is in the counts.
213+
214+
Parameters
215+
----------
216+
key: Union[:class:`int`, :class:`~discord.abc.Snowflake`]
217+
The role ID or a Snowflake object (e.g., a :class:`Role`).
218+
219+
Returns
220+
-------
221+
:class:`bool`
222+
``True`` if the role ID is present, ``False`` otherwise.
223+
"""
224+
if isinstance(key, abc.Snowflake):
225+
key = key.id
226+
return super().__contains__(key)
227+
228+
149229
class Guild(Hashable):
150230
"""Represents a Discord guild.
151231
@@ -1119,6 +1199,44 @@ def get_role(self, role_id: int, /) -> Role | None:
11191199
"""
11201200
return self._roles.get(role_id)
11211201

1202+
async def fetch_roles_member_counts(self) -> GuildRoleCounts:
1203+
"""|coro|
1204+
Fetches a mapping of role IDs to their member counts for this guild.
1205+
1206+
.. versionadded:: 2.7
1207+
1208+
Returns
1209+
-------
1210+
:class:`GuildRoleCounts`
1211+
A mapping of role IDs to their member counts. Can be accessed
1212+
with either role IDs (:class:`int`) or Snowflake objects (e.g., :class:`Role`).
1213+
1214+
Raises
1215+
------
1216+
:exc:`HTTPException`
1217+
Fetching the role member counts failed.
1218+
1219+
Examples
1220+
--------
1221+
1222+
Getting member counts using role IDs:
1223+
1224+
.. code-block:: python3
1225+
1226+
counts = await guild.fetch_roles_member_counts()
1227+
member_count = counts[123456789]
1228+
1229+
Using a role object:
1230+
1231+
.. code-block:: python3
1232+
1233+
counts = await guild.fetch_roles_member_counts()
1234+
role = guild.get_role(123456789)
1235+
member_count = counts[role]
1236+
"""
1237+
r = await self._state.http.get_roles_member_counts(self.id)
1238+
return GuildRoleCounts({int(role_id): count for role_id, count in r.items()})
1239+
11221240
@property
11231241
def default_role(self) -> Role:
11241242
"""Gets the @everyone role that all members have by default."""

discord/http.py

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2145,6 +2145,11 @@ def delete_invite(
21452145
def get_roles(self, guild_id: Snowflake) -> Response[list[role.Role]]:
21462146
return self.request(Route("GET", "/guilds/{guild_id}/roles", guild_id=guild_id))
21472147

2148+
def get_roles_member_counts(self, guild_id: Snowflake) -> Response[dict[str, int]]:
2149+
return self.request(
2150+
Route("GET", "/guilds/{guild_id}/roles/member-counts", guild_id=guild_id)
2151+
)
2152+
21482153
def get_role(self, guild_id: Snowflake, role_id: Snowflake) -> Response[role.Role]:
21492154
return self.request(
21502155
Route(

docs/api/models.rst

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -231,6 +231,11 @@ Role
231231
.. autoclass:: RoleColours
232232
:members:
233233

234+
.. attributetable:: GuildRoleCounts
235+
236+
.. autoclass:: GuildRoleCounts()
237+
:members:
238+
234239
Scheduled Event
235240
~~~~~~~~~~~~~~~
236241

0 commit comments

Comments
 (0)