Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
29 commits
Select commit Hold shift + click to select a range
256453c
Update base.py
EepyElvyra Jul 22, 2022
58ad9e3
refactor: change condition for getting error message (#973)
Damego Jul 25, 2022
1c7e5b8
revert!: revert removed fixes for attrs (#974)
Damego Jul 26, 2022
78c4966
Update client.py (#975)
Damego Jul 26, 2022
2b228a0
refactor: move ``Emoji`` to own module (#978)
Damego Jul 28, 2022
5258fa3
fix: added default None value to optional args (#982)
mAxYoLo01 Jul 29, 2022
c1c4cff
fix: fix error ``Attachment`` is not JSON serializable (#983)
Damego Jul 29, 2022
1ec46aa
chore: Version bump (#984)
FayeDel Jul 30, 2022
a8f076a
feat!: Add converters to components (#981)
Damego Jul 30, 2022
53407b1
Merge pull request #985 from interactions-py/unstable
FayeDel Jul 30, 2022
20be297
fix: fix option type parsing in option decorator (#986)
Damego Jul 30, 2022
a2eaffd
fix: fix error with converting components in edit (#987)
Damego Jul 31, 2022
a60db7c
feat: Add support for message_delete_bulk event (#990)
mAxYoLo01 Aug 1, 2022
078828f
ci: weekly check. (#991)
pre-commit-ci[bot] Aug 1, 2022
13d024a
feat: add ``__str__`` to ``Emoji`` (#988)
Damego Aug 3, 2022
6bf812f
fix: remove bug with converting empty list to actionrow (#997)
Damego Aug 3, 2022
ef46f58
fix: wrong sequence in getting channel overwrites (#998)
Damego Aug 3, 2022
18292e8
fix: fix few bugs with reactions (#993)
Damego Aug 3, 2022
d4f770e
chore: bump version to 4.3.1
EepyElvyra Aug 3, 2022
69e2d7d
Merge branch 'unstable' into verbump
EepyElvyra Aug 3, 2022
eb3c7d3
fix: add search_iterable() to __all__ (#1003)
mAxYoLo01 Aug 4, 2022
a708f2d
fix: Remove empty _client attribute in _json property (#999)
FayeDel Aug 7, 2022
41e338d
fix: add member's _client for message reactions (#994)
EepyElvyra Aug 7, 2022
ff8071c
ci: weekly check. (#1009)
pre-commit-ci[bot] Aug 8, 2022
7dcd743
refactor: change condition in update dispatch (#1004)
Damego Aug 9, 2022
cff89ce
docs: fix incorrect codeblocks (#1006)
Damego Aug 9, 2022
561f24e
Merge pull request #1000 from EdVraz/verbump
FayeDel Aug 10, 2022
c89d154
feat: Add member and channel permissions calculation functions (#969)
mAxYoLo01 Aug 10, 2022
7819ffc
Merge pull request #1001 from interactions-py/unstable
FayeDel Aug 10, 2022
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion .pre-commit-config.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -37,7 +37,7 @@ repos:
types: [file, python]
args: [--line-length=100]
- repo: https://github.com/PyCQA/flake8
rev: 4.0.1
rev: 5.0.4
hooks:
- id: flake8
name: flake8 Formatting
Expand Down
17 changes: 9 additions & 8 deletions interactions/api/gateway/client.py
Original file line number Diff line number Diff line change
Expand Up @@ -372,13 +372,9 @@ def _dispatch_event(self, event: str, data: dict) -> None:
elif data["type"] == InteractionType.MODAL_SUBMIT:
_name = f"modal_{_context.data.custom_id}"

if _context.data._json.get("components"):
if _context.data.components:
for component in _context.data.components:
if component.get("components"):
__args.append(
[_value["value"] for _value in component["components"]][0]
)
else:
if component.components:
__args.append([_value.value for _value in component.components][0])

self._dispatch.dispatch("on_modal", _context)
Expand Down Expand Up @@ -417,6 +413,7 @@ def _dispatch_event(self, event: str, data: dict) -> None:
"ChannelPins",
"MessageReaction",
"MessageReactionRemove",
"MessageDelete",
# Extend this for everything that should not be cached
]:
id = None
Expand Down Expand Up @@ -466,7 +463,10 @@ def __modify_guild_cache():
self._dispatch.dispatch(f"on_{name}", obj)
__modify_guild_cache()

elif "_update" in name and hasattr(obj, "id"):
elif "_update" in name:
self._dispatch.dispatch(f"on_raw_{name}", obj)
if not id:
return
old_obj = self._http.cache[model].get(id)
if old_obj:
before = model(**old_obj._json)
Expand All @@ -481,14 +481,15 @@ def __modify_guild_cache():
self._dispatch.dispatch(
f"on_{name}", before, old_obj
) # give previously stored and new one
return

elif "_remove" in name or "_delete" in name:
self._dispatch.dispatch(f"on_raw_{name}", obj)
__modify_guild_cache()
if id:
old_obj = _cache.pop(id)
self._dispatch.dispatch(f"on_{name}", old_obj)
elif "_delete_bulk" in name:
self._dispatch.dispatch(f"on_{name}", obj)

else:
self._dispatch.dispatch(f"on_{name}", obj)
Expand Down
5 changes: 3 additions & 2 deletions interactions/api/http/emoji.py
Original file line number Diff line number Diff line change
@@ -1,8 +1,9 @@
from typing import List, Optional

from ...api.cache import Cache
from ...api.models.guild import Emoji, Guild
from ...api.models.misc import Snowflake
from ..models.emoji import Emoji
from ..models.guild import Guild
from ..models.misc import Snowflake
from .request import _Request
from .route import Route

Expand Down
2 changes: 1 addition & 1 deletion interactions/api/http/invite.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@
from .request import _Request
from .route import Route

__all__ = ["InviteRequest"]
__all__ = ("InviteRequest",)


class InviteRequest:
Expand Down
6 changes: 3 additions & 3 deletions interactions/api/http/member.py
Original file line number Diff line number Diff line change
@@ -1,9 +1,9 @@
from typing import List, Optional

from ...api.cache import Cache
from ...api.models.guild import Guild
from ...api.models.member import Member
from ...api.models.misc import Snowflake
from ..models.guild import Guild
from ..models.member import Member
from ..models.misc import Snowflake
from .request import _Request
from .route import Route

Expand Down
3 changes: 2 additions & 1 deletion interactions/api/http/request.py
Original file line number Diff line number Diff line change
Expand Up @@ -163,7 +163,8 @@ async def request(self, route: Route, **kwargs) -> Optional[Any]:
self.buckets[route.endpoint] = _bucket
# real-time replacement/update/add if needed.
if isinstance(data, dict) and (
data.get("errors") or (data.get("code") and data.get("code") != 429)
data.get("errors")
or ((code := data.get("code")) and code != 429 and data.get("message"))
):
log.debug(
f"RETURN {response.status}: {dumps(data, indent=4, sort_keys=True)}"
Expand Down
1 change: 1 addition & 0 deletions interactions/api/models/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@
from .attrs_utils import * # noqa: F401 F403
from .audit_log import * # noqa: F401 F403
from .channel import * # noqa: F401 F403
from .emoji import * # noqa: F401 F403
from .flags import * # noqa: F401 F403
from .guild import * # noqa: F401 F403
from .gw import * # noqa: F401 F403
Expand Down
24 changes: 21 additions & 3 deletions interactions/api/models/attrs_utils.py
Original file line number Diff line number Diff line change
Expand Up @@ -47,12 +47,30 @@ def __init__(self, kwargs_dict: dict = None, /, **other_kwargs):
discord_name = attrib_name

if (value := kwargs.pop(discord_name, MISSING)) is not MISSING:
if value is not None and attrib.metadata.get("add_client"):
if (
value is not None
and attrib.metadata.get("add_client")
and client is not None
):
if isinstance(value, list):
for item in value:
item["_client"] = client
if isinstance(item, dict):
item["_client"] = client
elif isinstance(item, DictSerializerMixin):
item._client = client
else:
value["_client"] = client
if isinstance(value, dict):
value["_client"] = client
elif isinstance(value, DictSerializerMixin):
value._client = client

# make sure json is recursively handled
if isinstance(value, list):
self._json[attrib_name] = [
i._json if isinstance(i, DictSerializerMixin) else i for i in value
]
elif isinstance(value, DictSerializerMixin):
self._json[attrib_name] = value._json # type: ignore

passed_kwargs[attrib_name] = value

Expand Down
68 changes: 65 additions & 3 deletions interactions/api/models/channel.py
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@
define,
field,
)
from .flags import Permissions
from .misc import File, IDMixin, Overwrite, Snowflake
from .user import User
from .webhook import Webhook
Expand Down Expand Up @@ -363,11 +364,11 @@ async def modify(
)
_nsfw = self.nsfw if nsfw is MISSING else nsfw
_permission_overwrites = (
[overwrite._json for overwrite in self.permission_overwrites]
[overwrite._json for overwrite in permission_overwrites]
if permission_overwrites is not MISSING
else [overwrite._json for overwrite in self.permission_overwrites]
if self.permission_overwrites
else None
if permission_overwrites is MISSING
else [overwrite._json for overwrite in permission_overwrites]
)
_type = self.type

Expand Down Expand Up @@ -1207,6 +1208,67 @@ async def join(self) -> None:

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

async def get_permissions_for(self, member: "Member") -> Permissions:
"""
Returns the permissions of the member in this specific channel.

.. note::
The permissions returned by this function take into account role and
user overwrites that can be assigned to channels or categories. If you
don't need these overwrites, look into :meth:`.Member.get_guild_permissions`.

:param member: The member to get the permissions from
:type member: Member
:return: Permissions of the member in this channel
:rtype: Permissions
"""
if not self.guild_id:
return Permissions.DEFAULT

from .guild import Guild

guild = Guild(**await self._client.get_guild(int(self.guild_id)), _client=self._client)

permissions = await member.get_guild_permissions(guild)

if permissions & Permissions.ADMINISTRATOR == Permissions.ADMINISTRATOR:
return Permissions.ALL

# @everyone role overwrites
from ...client.models.utils import search_iterable

overwrite_everyone = search_iterable(
self.permission_overwrites, lambda overwrite: int(overwrite.id) == int(self.guild_id)
)
if overwrite_everyone:
permissions &= ~int(overwrite_everyone[0].deny)
permissions |= int(overwrite_everyone[0].allow)

# Apply role specific overwrites
allow, deny = 0, 0
for role_id in member.roles:
overwrite_role = search_iterable(
self.permission_overwrites, lambda overwrite: int(overwrite.id) == int(role_id)
)
if overwrite_role:
allow |= int(overwrite_role[0].allow)
deny |= int(overwrite_role[0].deny)

if deny:
permissions &= ~deny
if allow:
permissions |= allow

# Apply member specific overwrites
overwrite_member = search_iterable(
self.permission_overwrites, lambda overwrite: int(overwrite.id) == int(member.id)
)
if overwrite_member:
permissions &= ~int(overwrite_member[0].deny)
permissions |= int(overwrite_member[0].allow)

return Permissions(permissions)


@define()
class Thread(Channel):
Expand Down
125 changes: 125 additions & 0 deletions interactions/api/models/emoji.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,125 @@
from typing import TYPE_CHECKING, List, Optional, Union

from ..error import LibraryException
from .attrs_utils import ClientSerializerMixin, convert_list, define, field
from .misc import Snowflake
from .user import User

if TYPE_CHECKING:
from ..http import HTTPClient
from .guild import Guild

__all__ = ("Emoji",)


@define()
class Emoji(ClientSerializerMixin):
"""
A class objecting representing an emoji.

:ivar Optional[Snowflake] id?: Emoji id
:ivar Optional[str] name?: Emoji name.
:ivar Optional[List[int]] roles?: Roles allowed to use this emoji
:ivar Optional[User] user?: User that created this emoji
:ivar Optional[bool] require_colons?: Status denoting of this emoji must be wrapped in colons
:ivar Optional[bool] managed?: Status denoting if this emoji is managed (by an integration)
:ivar Optional[bool] animated?: Status denoting if this emoji is animated
:ivar Optional[bool] available?: Status denoting if this emoji can be used. (Can be false via server boosting)
"""

id: Optional[Snowflake] = field(converter=Snowflake, default=None)
name: Optional[str] = field(default=None)
roles: Optional[List[int]] = field(converter=convert_list(int), default=None)
user: Optional[User] = field(converter=User, default=None)
require_colons: Optional[bool] = field(default=None)
managed: Optional[bool] = field(default=None)
animated: Optional[bool] = field(default=None)
available: Optional[bool] = field(default=None)

def __str__(self):
return (
f"<{'a' if self.animated else ''}:{self.name}:{self.id}>"
if self.id is not None
else self.name
)

@classmethod
async def get(
cls,
guild_id: Union[int, Snowflake, "Guild"],
emoji_id: Union[int, Snowflake],
client: "HTTPClient",
) -> "Emoji":
"""
Gets an emoji.

:param guild_id: The id of the guild of the emoji
:type guild_id: Union[int, Snowflake, "Guild"]
:param emoji_id: The id of the emoji
:type emoji_id: Union[int, Snowflake]
:param client: The HTTPClient of your bot. Equals to ``bot._http``
:type client: HTTPClient
:return: The Emoji as object
:rtype: Emoji
"""

_guild_id = int(guild_id) if isinstance(guild_id, (int, Snowflake)) else int(guild_id.id)

res = await client.get_guild_emoji(guild_id=_guild_id, emoji_id=int(emoji_id))
return cls(**res, _client=client)

@classmethod
async def get_all_of_guild(
cls,
guild_id: Union[int, Snowflake, "Guild"],
client: "HTTPClient",
) -> List["Emoji"]:
"""
Gets all emoji of a guild.

:param guild_id: The id of the guild to get the emojis of
:type guild_id: Union[int, Snowflake, "Guild"]
:param client: The HTTPClient of your bot. Equals to ``bot._http``
:type client: HTTPClient
:return: The Emoji as list
:rtype: List[Emoji]
"""

_guild_id = int(guild_id) if isinstance(guild_id, (int, Snowflake)) else int(guild_id.id)

res = await client.get_all_emoji(guild_id=_guild_id)
return [cls(**emoji, _client=client) for emoji in res]

async def delete(
self,
guild_id: Union[int, Snowflake, "Guild"],
reason: Optional[str] = None,
) -> None:
"""
Deletes the emoji.

:param guild_id: The guild id to delete the emoji from
:type guild_id: Union[int, Snowflake, "Guild"]
:param reason?: The reason of the deletion
:type reason?: Optional[str]
"""
if not self._client:
raise LibraryException(code=13)

_guild_id = int(guild_id) if isinstance(guild_id, (int, Snowflake)) else int(guild_id.id)

return await self._client.delete_guild_emoji(
guild_id=_guild_id, emoji_id=int(self.id), reason=reason
)

@property
def url(self) -> str:
"""
Returns the emoji's URL.

:return: URL of the emoji
:rtype: str
"""
url = f"https://cdn.discordapp.com/emojis/{self.id}"
url += ".gif" if self.animated else ".png"
return url
Loading