Skip to content

Commit c89d154

Browse files
feat: Add member and channel permissions calculation functions (#969)
* feat: added member and channel permissions functions * ci: correct from checks. * docs: update docs * refactor: switch from ALL_PERMISSIONS to Permissions.ALL & DEFAULT * feat: add default permissions if in DM * fix: small fixes Co-authored-by: pre-commit-ci[bot] <66853113+pre-commit-ci[bot]@users.noreply.github.com>
1 parent 561f24e commit c89d154

File tree

3 files changed

+139
-0
lines changed

3 files changed

+139
-0
lines changed

interactions/api/models/channel.py

Lines changed: 62 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@
1111
define,
1212
field,
1313
)
14+
from .flags import Permissions
1415
from .misc import File, IDMixin, Overwrite, Snowflake
1516
from .user import User
1617
from .webhook import Webhook
@@ -1207,6 +1208,67 @@ async def join(self) -> None:
12071208

12081209
await self._client.join_thread(int(self.id))
12091210

1211+
async def get_permissions_for(self, member: "Member") -> Permissions:
1212+
"""
1213+
Returns the permissions of the member in this specific channel.
1214+
1215+
.. note::
1216+
The permissions returned by this function take into account role and
1217+
user overwrites that can be assigned to channels or categories. If you
1218+
don't need these overwrites, look into :meth:`.Member.get_guild_permissions`.
1219+
1220+
:param member: The member to get the permissions from
1221+
:type member: Member
1222+
:return: Permissions of the member in this channel
1223+
:rtype: Permissions
1224+
"""
1225+
if not self.guild_id:
1226+
return Permissions.DEFAULT
1227+
1228+
from .guild import Guild
1229+
1230+
guild = Guild(**await self._client.get_guild(int(self.guild_id)), _client=self._client)
1231+
1232+
permissions = await member.get_guild_permissions(guild)
1233+
1234+
if permissions & Permissions.ADMINISTRATOR == Permissions.ADMINISTRATOR:
1235+
return Permissions.ALL
1236+
1237+
# @everyone role overwrites
1238+
from ...client.models.utils import search_iterable
1239+
1240+
overwrite_everyone = search_iterable(
1241+
self.permission_overwrites, lambda overwrite: int(overwrite.id) == int(self.guild_id)
1242+
)
1243+
if overwrite_everyone:
1244+
permissions &= ~int(overwrite_everyone[0].deny)
1245+
permissions |= int(overwrite_everyone[0].allow)
1246+
1247+
# Apply role specific overwrites
1248+
allow, deny = 0, 0
1249+
for role_id in member.roles:
1250+
overwrite_role = search_iterable(
1251+
self.permission_overwrites, lambda overwrite: int(overwrite.id) == int(role_id)
1252+
)
1253+
if overwrite_role:
1254+
allow |= int(overwrite_role[0].allow)
1255+
deny |= int(overwrite_role[0].deny)
1256+
1257+
if deny:
1258+
permissions &= ~deny
1259+
if allow:
1260+
permissions |= allow
1261+
1262+
# Apply member specific overwrites
1263+
overwrite_member = search_iterable(
1264+
self.permission_overwrites, lambda overwrite: int(overwrite.id) == int(member.id)
1265+
)
1266+
if overwrite_member:
1267+
permissions &= ~int(overwrite_member[0].deny)
1268+
permissions |= int(overwrite_member[0].allow)
1269+
1270+
return Permissions(permissions)
1271+
12101272

12111273
@define()
12121274
class Thread(Channel):

interactions/api/models/flags.py

Lines changed: 47 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -93,6 +93,53 @@ class Permissions(IntFlag):
9393
START_EMBEDDED_ACTIVITIES = 1 << 39
9494
MODERATE_MEMBERS = 1 << 40
9595

96+
DEFAULT = (
97+
ADD_REACTIONS
98+
| VIEW_CHANNEL
99+
| SEND_MESSAGES
100+
| EMBED_LINKS
101+
| ATTACH_FILES
102+
| READ_MESSAGE_HISTORY
103+
| MENTION_EVERYONE
104+
| USE_EXTERNAL_EMOJIS
105+
)
106+
ALL = (
107+
DEFAULT
108+
| CREATE_INSTANT_INVITE
109+
| KICK_MEMBERS
110+
| BAN_MEMBERS
111+
| ADMINISTRATOR
112+
| MANAGE_CHANNELS
113+
| MANAGE_GUILD
114+
| VIEW_AUDIT_LOG
115+
| PRIORITY_SPEAKER
116+
| STREAM
117+
| SEND_TTS_MESSAGES
118+
| MANAGE_MESSAGES
119+
| VIEW_GUILD_INSIGHTS
120+
| CONNECT
121+
| SPEAK
122+
| MUTE_MEMBERS
123+
| DEAFEN_MEMBERS
124+
| MOVE_MEMBERS
125+
| USE_VAD
126+
| CHANGE_NICKNAME
127+
| MANAGE_NICKNAMES
128+
| MANAGE_ROLES
129+
| MANAGE_WEBHOOKS
130+
| MANAGE_EMOJIS_AND_STICKERS
131+
| USE_APPLICATION_COMMANDS
132+
| REQUEST_TO_SPEAK
133+
| MANAGE_EVENTS
134+
| MANAGE_THREADS
135+
| CREATE_PUBLIC_THREADS
136+
| CREATE_PRIVATE_THREADS
137+
| USE_EXTERNAL_STICKERS
138+
| SEND_MESSAGES_IN_THREADS
139+
| START_EMBEDDED_ACTIVITIES
140+
| MODERATE_MEMBERS
141+
)
142+
96143

97144
class UserFlags(IntFlag):
98145
"""An integer flag bitshift object representing the different user flags given by Discord."""

interactions/api/models/member.py

Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -380,6 +380,7 @@ async def add_to_thread(
380380
def get_avatar_url(self, guild_id: Union[int, Snowflake, "Guild"]) -> Optional[str]:
381381
"""
382382
Returns the URL of the member's avatar for the specified guild.
383+
383384
:param guild_id: The id of the guild to get the member's avatar from
384385
:type guild_id: Union[int, Snowflake, "Guild"]
385386
:return: URL of the members's avatar (None will be returned if no avatar is set)
@@ -393,3 +394,32 @@ def get_avatar_url(self, guild_id: Union[int, Snowflake, "Guild"]) -> Optional[s
393394
url = f"https://cdn.discordapp.com/guilds/{_guild_id}/users/{int(self.user.id)}/avatars/{self.avatar}"
394395
url += ".gif" if self.avatar.startswith("a_") else ".png"
395396
return url
397+
398+
async def get_guild_permissions(self, guild: "Guild") -> Permissions:
399+
"""
400+
Returns the permissions of the member for the specified guild.
401+
402+
.. note::
403+
The permissions returned by this function will not take into account role and
404+
user overwrites that can be assigned to channels or categories. If you need
405+
these overwrites, look into :meth:`.Channel.get_permissions_for`.
406+
407+
:param guild: The guild of the member
408+
:type guild: Guild
409+
:return: Base permissions of the member in the specified guild
410+
:rtype: Permissions
411+
"""
412+
if int(guild.owner_id) == int(self.id):
413+
return Permissions.ALL
414+
415+
role_everyone = await guild.get_role(int(guild.id))
416+
permissions = int(role_everyone.permissions)
417+
418+
for role_id in self.roles:
419+
role = await guild.get_role(role_id)
420+
permissions |= int(role.permissions)
421+
422+
if permissions & Permissions.ADMINISTRATOR == Permissions.ADMINISTRATOR:
423+
return Permissions.ALL
424+
425+
return Permissions(permissions)

0 commit comments

Comments
 (0)