From 0129d53b264c72180fc5a616cd95de54da36d1d8 Mon Sep 17 00:00:00 2001 From: LordOfPolls Date: Sun, 10 Sep 2023 07:31:04 +0100 Subject: [PATCH 1/9] chore: Version bump Signed-off-by: LordOfPolls --- pyproject.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pyproject.toml b/pyproject.toml index 68778cfea..1d28bd18b 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -1,6 +1,6 @@ [tool.poetry] name = "interactions.py" -version = "5.9.2" +version = "5.10.0" description = "Easy, simple, scalable and modular: a Python API wrapper for interactions." authors = [ "LordOfPolls ", From 24d81d8e2584d2e41f983fab3856b3c32bc5c99d Mon Sep 17 00:00:00 2001 From: Sam <82487541+example-git@users.noreply.github.com> Date: Mon, 2 Oct 2023 22:51:43 -0400 Subject: [PATCH 2/9] Implemented role icon edit --- interactions/client/utils/misc_utils.py | 22 ++++++++++++++++++++++ interactions/models/discord/role.py | 24 +++++++++++++++++++++++- 2 files changed, 45 insertions(+), 1 deletion(-) diff --git a/interactions/client/utils/misc_utils.py b/interactions/client/utils/misc_utils.py index 2bc9f54d7..73b8a09c1 100644 --- a/interactions/client/utils/misc_utils.py +++ b/interactions/client/utils/misc_utils.py @@ -1,10 +1,12 @@ import functools import inspect import re +from base64 import b64encode from typing import Callable, Iterable, List, Optional, Any, Union, TYPE_CHECKING import interactions.api.events as events from interactions.client.const import T +from interactions.client.errors import BadArgument from interactions.models.discord.enums import ComponentType if TYPE_CHECKING: @@ -263,3 +265,23 @@ def nulled_boolean_get(data: dict[str, Any], key: str) -> bool: if key in data: return True if data[key] is None else bool(data[key]) return False + + +def get_mime_type_for_image(data: bytes): + if data.startswith(b"\x89\x50\x4e\x47\x0d\x0a\x1a\x0a"): + return "image/png" + elif data[0:3] == b"\xff\xd8\xff" or data[6:10] in (b"JFIF", b"Exif"): + return "image/jpeg" + elif data.startswith((b"\x47\x49\x46\x38\x37\x61", b"\x47\x49\x46\x38\x39\x61")): + return "image/gif" + elif data.startswith(b"RIFF") and data[8:12] == b"WEBP": + return "image/webp" + else: + raise BadArgument("Unsupported image type given") + + +def bytes_to_base64_data(data: bytes) -> str: + fmt = "data:{mime};base64,{data}" + mime = get_mime_type_for_image(data) + b64 = b64encode(data).decode("ascii") + return fmt.format(mime=mime, data=b64) \ No newline at end of file diff --git a/interactions/models/discord/role.py b/interactions/models/discord/role.py index 5161f912d..2ddb8a121 100644 --- a/interactions/models/discord/role.py +++ b/interactions/models/discord/role.py @@ -2,10 +2,10 @@ from typing import Any, TYPE_CHECKING import attrs - from interactions.client.const import MISSING, T, Missing from interactions.client.utils import nulled_boolean_get from interactions.client.utils.attr_converters import optional as optional_c +from interactions.client.utils.misc_utils import bytes_to_base64_data from interactions.client.utils.serializer import dict_filter from interactions.models.discord.asset import Asset from interactions.models.discord.color import COLOR_TYPES, Color, process_color @@ -187,6 +187,8 @@ async def edit( color: Color | COLOR_TYPES | None = None, hoist: bool | None = None, mentionable: bool | None = None, + icon: bytes | None = None, + unicode_emoji: str | None = None, ) -> "Role": """ Edit this role, all arguments are optional. @@ -197,6 +199,15 @@ async def edit( color: The color of the role hoist: whether the role should be displayed separately in the sidebar mentionable: whether the role should be mentionable + icon: Optional[:class:`bytes`] + A :term:`py:bytes-like object` representing the icon. Only PNG/JPEG/WebP is supported. + If this argument is passed, ``unicode_emoji`` is set to None. + Only available to guilds that contain ``ROLE_ICONS`` in :attr:`Guild.features`. + Could be ``None`` to denote removal of the icon. + unicode_emoji: Optional[:class:`str`] + The role's unicode emoji. If this argument is passed, ``icon`` is set to None. + Only available to guilds that contain ``ROLE_ICONS`` in :attr:`Guild.features`. + Returns: Role with updated information @@ -213,6 +224,17 @@ async def edit( "mentionable": mentionable, } ) + if not unicode_emoji: + if icon is None: + payload["icon"] = None + else: + payload["icon"] = bytes_to_base64_data(icon) + payload["unicode_emoji"] = None + + else: + payload["unicode_emoji"] = unicode_emoji + payload["icon"] = None + r_data = await self._client.http.modify_guild_role(self._guild_id, self.id, payload) r_data = dict(r_data) # to convert typed dict to regular dict From 8656319848986ae83c83be8e53c37e0a3adea2a6 Mon Sep 17 00:00:00 2001 From: Sam <82487541+example-git@users.noreply.github.com> Date: Mon, 2 Oct 2023 22:59:31 -0400 Subject: [PATCH 3/9] added role icon editing to the edit method --- interactions/models/discord/role.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/interactions/models/discord/role.py b/interactions/models/discord/role.py index 2ddb8a121..3a2f7bf9d 100644 --- a/interactions/models/discord/role.py +++ b/interactions/models/discord/role.py @@ -187,8 +187,8 @@ async def edit( color: Color | COLOR_TYPES | None = None, hoist: bool | None = None, mentionable: bool | None = None, - icon: bytes | None = None, - unicode_emoji: str | None = None, + icon: bytes | None = None, + unicode_emoji: str | None = None, ) -> "Role": """ Edit this role, all arguments are optional. From b3b9a67b559d4772c9a6313a652310b668fa24f8 Mon Sep 17 00:00:00 2001 From: Sam <82487541+example-git@users.noreply.github.com> Date: Mon, 2 Oct 2023 23:07:35 -0400 Subject: [PATCH 4/9] Update misc_utils.py --- interactions/client/utils/misc_utils.py | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/interactions/client/utils/misc_utils.py b/interactions/client/utils/misc_utils.py index 73b8a09c1..c4272f88f 100644 --- a/interactions/client/utils/misc_utils.py +++ b/interactions/client/utils/misc_utils.py @@ -6,7 +6,6 @@ import interactions.api.events as events from interactions.client.const import T -from interactions.client.errors import BadArgument from interactions.models.discord.enums import ComponentType if TYPE_CHECKING: @@ -277,7 +276,7 @@ def get_mime_type_for_image(data: bytes): elif data.startswith(b"RIFF") and data[8:12] == b"WEBP": return "image/webp" else: - raise BadArgument("Unsupported image type given") + raise ValueError("Unrecognized image type") def bytes_to_base64_data(data: bytes) -> str: From cdc6f9b7b8a4b3fdce4a8a4e6a14ec1745af91a9 Mon Sep 17 00:00:00 2001 From: "pre-commit-ci[bot]" <66853113+pre-commit-ci[bot]@users.noreply.github.com> Date: Tue, 3 Oct 2023 03:33:59 +0000 Subject: [PATCH 5/9] ci: correct from checks. --- interactions/client/utils/misc_utils.py | 28 ++++++++++++------------- interactions/models/discord/role.py | 1 - 2 files changed, 14 insertions(+), 15 deletions(-) diff --git a/interactions/client/utils/misc_utils.py b/interactions/client/utils/misc_utils.py index c4272f88f..71350c165 100644 --- a/interactions/client/utils/misc_utils.py +++ b/interactions/client/utils/misc_utils.py @@ -267,20 +267,20 @@ def nulled_boolean_get(data: dict[str, Any], key: str) -> bool: def get_mime_type_for_image(data: bytes): - if data.startswith(b"\x89\x50\x4e\x47\x0d\x0a\x1a\x0a"): - return "image/png" - elif data[0:3] == b"\xff\xd8\xff" or data[6:10] in (b"JFIF", b"Exif"): - return "image/jpeg" - elif data.startswith((b"\x47\x49\x46\x38\x37\x61", b"\x47\x49\x46\x38\x39\x61")): - return "image/gif" - elif data.startswith(b"RIFF") and data[8:12] == b"WEBP": - return "image/webp" - else: - raise ValueError("Unrecognized image type") + if data.startswith(b"\x89\x50\x4e\x47\x0d\x0a\x1a\x0a"): + return "image/png" + elif data[0:3] == b"\xff\xd8\xff" or data[6:10] in (b"JFIF", b"Exif"): + return "image/jpeg" + elif data.startswith((b"\x47\x49\x46\x38\x37\x61", b"\x47\x49\x46\x38\x39\x61")): + return "image/gif" + elif data.startswith(b"RIFF") and data[8:12] == b"WEBP": + return "image/webp" + else: + raise ValueError("Unrecognized image type") def bytes_to_base64_data(data: bytes) -> str: - fmt = "data:{mime};base64,{data}" - mime = get_mime_type_for_image(data) - b64 = b64encode(data).decode("ascii") - return fmt.format(mime=mime, data=b64) \ No newline at end of file + fmt = "data:{mime};base64,{data}" + mime = get_mime_type_for_image(data) + b64 = b64encode(data).decode("ascii") + return fmt.format(mime=mime, data=b64) diff --git a/interactions/models/discord/role.py b/interactions/models/discord/role.py index 3a2f7bf9d..a73e3ee1d 100644 --- a/interactions/models/discord/role.py +++ b/interactions/models/discord/role.py @@ -235,7 +235,6 @@ async def edit( payload["unicode_emoji"] = unicode_emoji payload["icon"] = None - r_data = await self._client.http.modify_guild_role(self._guild_id, self.id, payload) r_data = dict(r_data) # to convert typed dict to regular dict r_data["guild_id"] = self._guild_id From 3b1e7a9b5466adcf3bed64e48404ca8b36fa6692 Mon Sep 17 00:00:00 2001 From: Sam <82487541+example-git@users.noreply.github.com> Date: Mon, 2 Oct 2023 23:41:49 -0400 Subject: [PATCH 6/9] fixed formatting, added return formatting indicator --- interactions/client/utils/misc_utils.py | 31 +++++++++++++------------ 1 file changed, 16 insertions(+), 15 deletions(-) diff --git a/interactions/client/utils/misc_utils.py b/interactions/client/utils/misc_utils.py index c4272f88f..d1684573a 100644 --- a/interactions/client/utils/misc_utils.py +++ b/interactions/client/utils/misc_utils.py @@ -266,21 +266,22 @@ def nulled_boolean_get(data: dict[str, Any], key: str) -> bool: return False -def get_mime_type_for_image(data: bytes): - if data.startswith(b"\x89\x50\x4e\x47\x0d\x0a\x1a\x0a"): - return "image/png" - elif data[0:3] == b"\xff\xd8\xff" or data[6:10] in (b"JFIF", b"Exif"): - return "image/jpeg" - elif data.startswith((b"\x47\x49\x46\x38\x37\x61", b"\x47\x49\x46\x38\x39\x61")): - return "image/gif" - elif data.startswith(b"RIFF") and data[8:12] == b"WEBP": - return "image/webp" - else: - raise ValueError("Unrecognized image type") +def get_mime_type_for_image(data: bytes) -> str: + if data.startswith(b"\x89\x50\x4e\x47\x0d\x0a\x1a\x0a"): + result = "image/png" + elif data[0:3] == b"\xff\xd8\xff" or data[6:10] in (b"JFIF", b"Exif"): + result = "image/jpeg" + elif data.startswith((b"\x47\x49\x46\x38\x37\x61", b"\x47\x49\x46\x38\x39\x61")): + result = "image/gif" + elif data.startswith(b"RIFF") and data[8:12] == b"WEBP": + result = "image/webp" + else: + raise ValueError("Unrecognized image type") + return result if result else "" def bytes_to_base64_data(data: bytes) -> str: - fmt = "data:{mime};base64,{data}" - mime = get_mime_type_for_image(data) - b64 = b64encode(data).decode("ascii") - return fmt.format(mime=mime, data=b64) \ No newline at end of file + fmt = "data:{mime};base64,{data}" + mime = get_mime_type_for_image(data) + b64 = b64encode(data).decode("ascii") + return fmt.format(mime=mime, data=b64) if fmt else None From 7cd9e102ed5cc8d9b611dfac1814b712d60b70b7 Mon Sep 17 00:00:00 2001 From: Sam <82487541+example-git@users.noreply.github.com> Date: Tue, 3 Oct 2023 16:21:44 -0400 Subject: [PATCH 7/9] Noticed that interactions.client.utils.serializer already had the functions I added to misc_utils. Undid those changes and tidied up the code in role.py to reflect the already existing converter. Added an error raise if both icon and unicode emojis are used in one edit (the api requires only one is provided.) --- interactions/client/utils/misc_utils.py | 22 ---------------------- interactions/models/discord/role.py | 18 ++++++------------ 2 files changed, 6 insertions(+), 34 deletions(-) mode change 100644 => 100755 interactions/client/utils/misc_utils.py diff --git a/interactions/client/utils/misc_utils.py b/interactions/client/utils/misc_utils.py old mode 100644 new mode 100755 index d1684573a..2bc9f54d7 --- a/interactions/client/utils/misc_utils.py +++ b/interactions/client/utils/misc_utils.py @@ -1,7 +1,6 @@ import functools import inspect import re -from base64 import b64encode from typing import Callable, Iterable, List, Optional, Any, Union, TYPE_CHECKING import interactions.api.events as events @@ -264,24 +263,3 @@ def nulled_boolean_get(data: dict[str, Any], key: str) -> bool: if key in data: return True if data[key] is None else bool(data[key]) return False - - -def get_mime_type_for_image(data: bytes) -> str: - if data.startswith(b"\x89\x50\x4e\x47\x0d\x0a\x1a\x0a"): - result = "image/png" - elif data[0:3] == b"\xff\xd8\xff" or data[6:10] in (b"JFIF", b"Exif"): - result = "image/jpeg" - elif data.startswith((b"\x47\x49\x46\x38\x37\x61", b"\x47\x49\x46\x38\x39\x61")): - result = "image/gif" - elif data.startswith(b"RIFF") and data[8:12] == b"WEBP": - result = "image/webp" - else: - raise ValueError("Unrecognized image type") - return result if result else "" - - -def bytes_to_base64_data(data: bytes) -> str: - fmt = "data:{mime};base64,{data}" - mime = get_mime_type_for_image(data) - b64 = b64encode(data).decode("ascii") - return fmt.format(mime=mime, data=b64) if fmt else None diff --git a/interactions/models/discord/role.py b/interactions/models/discord/role.py index a73e3ee1d..8a94527af 100644 --- a/interactions/models/discord/role.py +++ b/interactions/models/discord/role.py @@ -5,8 +5,7 @@ from interactions.client.const import MISSING, T, Missing from interactions.client.utils import nulled_boolean_get from interactions.client.utils.attr_converters import optional as optional_c -from interactions.client.utils.misc_utils import bytes_to_base64_data -from interactions.client.utils.serializer import dict_filter +from interactions.client.utils.serializer import dict_filter, to_image_data from interactions.models.discord.asset import Asset from interactions.models.discord.color import COLOR_TYPES, Color, process_color from interactions.models.discord.emoji import PartialEmoji @@ -215,6 +214,9 @@ async def edit( """ color = process_color(color) + if icon and unicode_emoji: + raise ValueError("Cannot pass both icon and unicode_emoji") + payload = dict_filter( { "name": name, @@ -222,18 +224,10 @@ async def edit( "color": color, "hoist": hoist, "mentionable": mentionable, + "icon": to_image_data(icon) if icon else None, + "unicode_emoji": unicode_emoji, } ) - if not unicode_emoji: - if icon is None: - payload["icon"] = None - else: - payload["icon"] = bytes_to_base64_data(icon) - payload["unicode_emoji"] = None - - else: - payload["unicode_emoji"] = unicode_emoji - payload["icon"] = None r_data = await self._client.http.modify_guild_role(self._guild_id, self.id, payload) r_data = dict(r_data) # to convert typed dict to regular dict From d7aad9e4d4c1ee7040ee69a8a48faafeea72393b Mon Sep 17 00:00:00 2001 From: Sam <82487541+example-git@users.noreply.github.com> Date: Tue, 3 Oct 2023 17:54:13 -0400 Subject: [PATCH 8/9] Cleaned up the docstring and moved the icon serialization cleaned up the docstring to better align with requirements, moved the icon serialization so the payload's code is a bit easier to follow at a glance. --- interactions/models/discord/role.py | 15 +++++---------- 1 file changed, 5 insertions(+), 10 deletions(-) diff --git a/interactions/models/discord/role.py b/interactions/models/discord/role.py index 8a94527af..144de9ab3 100644 --- a/interactions/models/discord/role.py +++ b/interactions/models/discord/role.py @@ -198,15 +198,8 @@ async def edit( color: The color of the role hoist: whether the role should be displayed separately in the sidebar mentionable: whether the role should be mentionable - icon: Optional[:class:`bytes`] - A :term:`py:bytes-like object` representing the icon. Only PNG/JPEG/WebP is supported. - If this argument is passed, ``unicode_emoji`` is set to None. - Only available to guilds that contain ``ROLE_ICONS`` in :attr:`Guild.features`. - Could be ``None`` to denote removal of the icon. - unicode_emoji: Optional[:class:`str`] - The role's unicode emoji. If this argument is passed, ``icon`` is set to None. - Only available to guilds that contain ``ROLE_ICONS`` in :attr:`Guild.features`. - + icon: (Guild Level 2+) Bytes-like object representing the icon; supports PNG, JPEG and WebP + unicode_emoji: (Guild Level 2+) Unicode emoji for the role; can't be used with icon Returns: Role with updated information @@ -216,6 +209,8 @@ async def edit( if icon and unicode_emoji: raise ValueError("Cannot pass both icon and unicode_emoji") + if icon: + icon = to_image_data(icon) payload = dict_filter( { @@ -224,7 +219,7 @@ async def edit( "color": color, "hoist": hoist, "mentionable": mentionable, - "icon": to_image_data(icon) if icon else None, + "icon": icon, "unicode_emoji": unicode_emoji, } ) From ccf9e093c85e1942b984b08427c6229bd35dd41a Mon Sep 17 00:00:00 2001 From: Sam <82487541+example-git@users.noreply.github.com> Date: Tue, 3 Oct 2023 20:24:30 -0400 Subject: [PATCH 9/9] Added UPLOADABLE_TYPE per comment --- interactions/models/discord/role.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/interactions/models/discord/role.py b/interactions/models/discord/role.py index 144de9ab3..2f81a0f92 100644 --- a/interactions/models/discord/role.py +++ b/interactions/models/discord/role.py @@ -7,6 +7,7 @@ from interactions.client.utils.attr_converters import optional as optional_c from interactions.client.utils.serializer import dict_filter, to_image_data from interactions.models.discord.asset import Asset +from interactions.models.discord.file import UPLOADABLE_TYPE from interactions.models.discord.color import COLOR_TYPES, Color, process_color from interactions.models.discord.emoji import PartialEmoji from interactions.models.discord.enums import Permissions @@ -186,7 +187,7 @@ async def edit( color: Color | COLOR_TYPES | None = None, hoist: bool | None = None, mentionable: bool | None = None, - icon: bytes | None = None, + icon: bytes | UPLOADABLE_TYPE | None = None, unicode_emoji: str | None = None, ) -> "Role": """