Skip to content

Commit 7819ffc

Browse files
authored
Merge pull request #1001 from interactions-py/unstable
chore: merge unstable into stable
2 parents 53407b1 + c89d154 commit 7819ffc

File tree

14 files changed

+198
-23
lines changed

14 files changed

+198
-23
lines changed

.pre-commit-config.yaml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -37,7 +37,7 @@ repos:
3737
types: [file, python]
3838
args: [--line-length=100]
3939
- repo: https://github.com/PyCQA/flake8
40-
rev: 4.0.1
40+
rev: 5.0.4
4141
hooks:
4242
- id: flake8
4343
name: flake8 Formatting

interactions/api/gateway/client.py

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -413,6 +413,7 @@ def _dispatch_event(self, event: str, data: dict) -> None:
413413
"ChannelPins",
414414
"MessageReaction",
415415
"MessageReactionRemove",
416+
"MessageDelete",
416417
# Extend this for everything that should not be cached
417418
]:
418419
id = None
@@ -464,7 +465,7 @@ def __modify_guild_cache():
464465

465466
elif "_update" in name:
466467
self._dispatch.dispatch(f"on_raw_{name}", obj)
467-
if not hasattr(obj, "id"):
468+
if not id:
468469
return
469470
old_obj = self._http.cache[model].get(id)
470471
if old_obj:
@@ -487,6 +488,8 @@ def __modify_guild_cache():
487488
if id:
488489
old_obj = _cache.pop(id)
489490
self._dispatch.dispatch(f"on_{name}", old_obj)
491+
elif "_delete_bulk" in name:
492+
self._dispatch.dispatch(f"on_{name}", obj)
490493

491494
else:
492495
self._dispatch.dispatch(f"on_{name}", obj)

interactions/api/models/attrs_utils.py

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -47,7 +47,11 @@ def __init__(self, kwargs_dict: dict = None, /, **other_kwargs):
4747
discord_name = attrib_name
4848

4949
if (value := kwargs.pop(discord_name, MISSING)) is not MISSING:
50-
if value is not None and attrib.metadata.get("add_client"):
50+
if (
51+
value is not None
52+
and attrib.metadata.get("add_client")
53+
and client is not None
54+
):
5155
if isinstance(value, list):
5256
for item in value:
5357
if isinstance(item, dict):

interactions/api/models/channel.py

Lines changed: 65 additions & 3 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
@@ -363,11 +364,11 @@ async def modify(
363364
)
364365
_nsfw = self.nsfw if nsfw is MISSING else nsfw
365366
_permission_overwrites = (
366-
[overwrite._json for overwrite in self.permission_overwrites]
367+
[overwrite._json for overwrite in permission_overwrites]
368+
if permission_overwrites is not MISSING
369+
else [overwrite._json for overwrite in self.permission_overwrites]
367370
if self.permission_overwrites
368371
else None
369-
if permission_overwrites is MISSING
370-
else [overwrite._json for overwrite in permission_overwrites]
371372
)
372373
_type = self.type
373374

@@ -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/emoji.py

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -36,6 +36,13 @@ class Emoji(ClientSerializerMixin):
3636
animated: Optional[bool] = field(default=None)
3737
available: Optional[bool] = field(default=None)
3838

39+
def __str__(self):
40+
return (
41+
f"<{'a' if self.animated else ''}:{self.name}:{self.id}>"
42+
if self.id is not None
43+
else self.name
44+
)
45+
3946
@classmethod
4047
async def get(
4148
cls,

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/gw.py

Lines changed: 18 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -39,6 +39,7 @@
3939
"ChannelPins",
4040
"ThreadMembers",
4141
"ThreadList",
42+
"MessageDelete",
4243
"MessageReactionRemove",
4344
"MessageReaction",
4445
"GuildIntegrations",
@@ -761,7 +762,22 @@ class Presence(ClientSerializerMixin):
761762

762763

763764
@define()
764-
class MessageReaction(DictSerializerMixin):
765+
class MessageDelete(DictSerializerMixin):
766+
"""
767+
A class object representing the gateway event ``MESSAGE_DELETE_BULK``.
768+
769+
:ivar List[Snowflake] ids: The message IDs of the event.
770+
:ivar Snowflake channel_id: The channel ID of the event.
771+
:ivar Optional[Snowflake] guild_id?: The guild ID of the event.
772+
"""
773+
774+
ids: List[Snowflake] = field(converter=convert_list(Snowflake))
775+
channel_id: Snowflake = field(converter=Snowflake)
776+
guild_id: Optional[Snowflake] = field(converter=Snowflake, default=None)
777+
778+
779+
@define()
780+
class MessageReaction(ClientSerializerMixin):
765781
"""
766782
A class object representing the gateway event ``MESSAGE_REACTION_ADD`` and ``MESSAGE_REACTION_REMOVE``.
767783
@@ -777,7 +793,7 @@ class MessageReaction(DictSerializerMixin):
777793
channel_id: Snowflake = field(converter=Snowflake)
778794
message_id: Snowflake = field(converter=Snowflake)
779795
guild_id: Optional[Snowflake] = field(converter=Snowflake, default=None)
780-
member: Optional[Member] = field(converter=Member, default=None)
796+
member: Optional[Member] = field(converter=Member, default=None, add_client=True)
781797
emoji: Optional[Emoji] = field(converter=Emoji, default=None)
782798

783799

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)

interactions/api/models/message.py

Lines changed: 11 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -910,7 +910,7 @@ async def edit(
910910
if not components:
911911
_components = []
912912
elif components is MISSING:
913-
_components = self.components
913+
_components = _build_components(components=self.components)
914914
else:
915915
_components = _build_components(components=components)
916916

@@ -1109,7 +1109,7 @@ async def create_reaction(
11091109
raise LibraryException(code=13)
11101110

11111111
_emoji = (
1112-
f":{emoji.name.replace(':', '')}:{emoji.id or ''}"
1112+
(f":{emoji.name.replace(':', '')}:{emoji.id or ''}" if emoji.id else emoji.name)
11131113
if isinstance(emoji, Emoji)
11141114
else emoji
11151115
)
@@ -1143,7 +1143,7 @@ async def remove_all_reactions_of(
11431143
raise LibraryException(code=13)
11441144

11451145
_emoji = (
1146-
f":{emoji.name.replace(':', '')}:{emoji.id or ''}"
1146+
(f":{emoji.name.replace(':', '')}:{emoji.id or ''}" if emoji.id else emoji.name)
11471147
if isinstance(emoji, Emoji)
11481148
else emoji
11491149
)
@@ -1166,7 +1166,7 @@ async def remove_own_reaction_of(
11661166
raise LibraryException(code=13)
11671167

11681168
_emoji = (
1169-
f":{emoji.name.replace(':', '')}:{emoji.id or ''}"
1169+
(f":{emoji.name.replace(':', '')}:{emoji.id or ''}" if emoji.id else emoji.name)
11701170
if isinstance(emoji, Emoji)
11711171
else emoji
11721172
)
@@ -1187,16 +1187,19 @@ async def remove_reaction_from(
11871187
:type user: Union[Member, user, int]
11881188
"""
11891189
_emoji = (
1190-
f":{emoji.name.replace(':', '')}:{emoji.id or ''}"
1190+
(f":{emoji.name.replace(':', '')}:{emoji.id or ''}" if emoji.id else emoji.name)
11911191
if isinstance(emoji, Emoji)
11921192
else emoji
11931193
)
11941194
if not self._client:
11951195
raise LibraryException(code=13)
11961196

1197-
_user_id = user if isinstance(user, int) else user.id
1197+
_user_id = user if isinstance(user, (int, Snowflake)) else user.id
11981198
return await self._client.remove_user_reaction(
1199-
channel_id=int(self.channel_id), message_id=int(self.id), user_id=_user_id, emoji=_emoji
1199+
channel_id=int(self.channel_id),
1200+
message_id=int(self.id),
1201+
user_id=int(_user_id),
1202+
emoji=_emoji,
12001203
)
12011204

12021205
async def get_users_from_reaction(
@@ -1217,7 +1220,7 @@ async def get_users_from_reaction(
12171220
_all_users: List[User] = []
12181221

12191222
_emoji = (
1220-
f":{emoji.name.replace(':', '')}:{emoji.id or ''}"
1223+
(f":{emoji.name.replace(':', '')}:{emoji.id or ''}" if emoji.id else emoji.name)
12211224
if isinstance(emoji, Emoji)
12221225
else emoji
12231226
)

interactions/base.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@
66
"__authors__",
77
)
88

9-
__version__ = "4.3.0"
9+
__version__ = "4.3.1"
1010

1111
__authors__ = {
1212
"current": [

interactions/client/context.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -255,7 +255,7 @@ async def edit(
255255

256256
if self.message.components is not None or components is not MISSING:
257257
if components is MISSING:
258-
_components = self.message.components
258+
_components = _build_components(components=self.message.components)
259259
elif not components:
260260
_components = []
261261
else:

interactions/client/models/command.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -257,7 +257,7 @@ def decorator(coro: Callable[..., Awaitable]) -> Callable[..., Awaitable]:
257257

258258
param = parameters[-1 - len(coro._options)]
259259

260-
option_type = kwargs.get("type", param.annotation)
260+
option_type = kwargs.pop("type", param.annotation)
261261
name = kwargs.pop("name", param.name)
262262
if name != param.name:
263263
kwargs["converter"] = param.name

0 commit comments

Comments
 (0)