From eb25c7aaf29fe1489e803e9de960cc7f340b5493 Mon Sep 17 00:00:00 2001 From: Soheab_ <33902984+Soheab@users.noreply.github.com> Date: Sun, 5 May 2024 19:09:31 +0200 Subject: [PATCH 1/3] Add support for getting and editing integration_types_config --- discord/appinfo.py | 98 +++++++++++++++++++++++++++++++++++++++- discord/http.py | 1 + discord/types/appinfo.py | 7 ++- 3 files changed, 104 insertions(+), 2 deletions(-) diff --git a/discord/appinfo.py b/discord/appinfo.py index 074892d051af..85f38cd05950 100644 --- a/discord/appinfo.py +++ b/discord/appinfo.py @@ -24,7 +24,7 @@ from __future__ import annotations -from typing import List, TYPE_CHECKING, Optional +from typing import List, TYPE_CHECKING, Literal, Optional from . import utils from .asset import Asset @@ -41,6 +41,7 @@ PartialAppInfo as PartialAppInfoPayload, Team as TeamPayload, InstallParams as InstallParamsPayload, + AppIntegrationTypeConfig as AppIntegrationTypeConfigPayload, ) from .user import User from .state import ConnectionState @@ -175,6 +176,7 @@ class AppInfo: 'interactions_endpoint_url', 'redirect_uris', 'approximate_guild_count', + '_integration_types_config', ) def __init__(self, state: ConnectionState, data: AppInfoPayload): @@ -212,6 +214,9 @@ def __init__(self, state: ConnectionState, data: AppInfoPayload): self.interactions_endpoint_url: Optional[str] = data.get('interactions_endpoint_url') self.redirect_uris: List[str] = data.get('redirect_uris', []) self.approximate_guild_count: int = data.get('approximate_guild_count', 0) + self._integration_types_config: Dict[Literal["0", "1"], AppIntegrationTypeConfigPayload] = data.get( + 'integration_types_config', {} + ) def __repr__(self) -> str: return ( @@ -254,6 +259,36 @@ def flags(self) -> ApplicationFlags: """ return ApplicationFlags._from_value(self._flags) + @property + def default_guild_install_params(self) -> Optional[AppInstallParams]: + """Optional[:class:`AppInstallParams`]: The default settings for the + application's installation context in a guild. + + .. versionadded:: 2.4 + """ + if not self._integration_types_config: + return None + + try: + return AppInstallParams(self._integration_types_config['0']['oauth2_install_params']) + except KeyError: + return None + + @property + def default_user_install_params(self) -> Optional[AppInstallParams]: + """Optional[:class:`AppInstallParams`]: The default settings for the + application's installation context as a user. + + .. versionadded:: 2.4 + """ + if not self._integration_types_config: + return None + + try: + return AppInstallParams(self._integration_types_config['1']['oauth2_install_params']) + except KeyError: + return None + async def edit( self, *, @@ -268,6 +303,10 @@ async def edit( cover_image: Optional[bytes] = MISSING, interactions_endpoint_url: Optional[str] = MISSING, tags: Optional[List[str]] = MISSING, + default_guild_install_scopes: Optional[List[str]] = MISSING, + default_guild_install_permissions: Optional[Permissions] = MISSING, + default_user_install_scopes: Optional[List[str]] = MISSING, + default_user_install_permissions: Optional[Permissions] = MISSING, ) -> AppInfo: r"""|coro| @@ -309,6 +348,16 @@ async def edit( over the gateway. Can be ``None`` to remove the URL. tags: Optional[List[:class:`str`]] The new list of tags describing the functionality of the application. Can be ``None`` to remove the tags. + default_guild_install_scopes: Optional[List[:class:`str`]] + The new list of :ddocs:`OAuth2 scopes ` of + the default guild installation context. Can be ``None`` to remove the scopes. + default_guild_install_permissions: Optional[:class:`Permissions`] + The new permissions of the default guild installation context. Can be ``None`` to remove the permissions. + default_user_install_scopes: Optional[List[:class:`str`]] + The new list of :ddocs:`OAuth2 scopes ` of + the default user installation context. Can be ``None`` to remove the scopes. + default_user_install_permissions: Optional[:class:`Permissions`] + The new permissions of the default user installation context. Can be ``None`` to remove the permissions. reason: Optional[:class:`str`] The reason for editing the application. Shows up on the audit log. @@ -319,6 +368,7 @@ async def edit( ValueError The image format passed in to ``icon`` or ``cover_image`` is invalid. This is also raised when ``install_params_scopes`` and ``install_params_permissions`` are incompatible with each other. + or when ``default_guild_install_scopes`` and ``default_guild_install_permissions`` are incompatible with each other. Returns ------- @@ -383,6 +433,52 @@ async def edit( if tags is not MISSING: payload['tags'] = tags + + integration_types_config: Dict[str, Any] = {} + if default_guild_install_scopes is not MISSING or default_guild_install_permissions is not MISSING: + guild_install_params: Optional[Dict[str, Any]] = {} + if default_guild_install_scopes in (None, MISSING): + default_guild_install_scopes = [] + + if "bot" not in default_guild_install_scopes and default_guild_install_permissions is not MISSING: + raise ValueError("'bot' must be in default_guild_install_scopes if default_guild_install_permissions is set") + + if default_guild_install_permissions in (None, MISSING): + guild_install_params['permissions'] = 0 + else: + guild_install_params['permissions'] = default_guild_install_permissions.value + + guild_install_params['scopes'] = default_guild_install_scopes + + integration_types_config['0'] = {'oauth2_install_params': guild_install_params or None} + else: + if default_guild_install_permissions is not MISSING: + raise ValueError("default_guild_install_scopes must be set if default_guild_install_permissions is set") + + if default_user_install_scopes is not MISSING or default_user_install_permissions is not MISSING: + user_install_params: Optional[Dict[str, Any]] = {} + if default_user_install_scopes in (None, MISSING): + default_user_install_scopes = [] + + if "bot" not in default_user_install_scopes and default_user_install_permissions is not MISSING: + raise ValueError("'bot' must be in default_user_install_scopes if default_user_install_permissions is set") + + if default_user_install_permissions in (None, MISSING): + user_install_params['permissions'] = 0 + else: + user_install_params['permissions'] = default_user_install_permissions.value + + user_install_params['scopes'] = default_user_install_scopes + + integration_types_config['1'] = {'oauth2_install_params': user_install_params or None} + else: + if default_user_install_permissions is not MISSING: + raise ValueError("default_user_install_scopes must be set if default_user_install_permissions is set") + + if integration_types_config: + payload['integration_types_config'] = integration_types_config + + print("payload: ", integration_types_config) data = await self._state.http.edit_application_info(reason=reason, payload=payload) return AppInfo(data=data, state=self._state) diff --git a/discord/http.py b/discord/http.py index f36d191e4b0e..86f3259a2044 100644 --- a/discord/http.py +++ b/discord/http.py @@ -2508,6 +2508,7 @@ def edit_application_info(self, *, reason: Optional[str], payload: Any) -> Respo 'cover_image', 'interactions_endpoint_url ', 'tags', + 'integration_types_config', ) payload = {k: v for k, v in payload.items() if k in valid_keys} diff --git a/discord/types/appinfo.py b/discord/types/appinfo.py index e291babfa3e0..fcb3d737b34c 100644 --- a/discord/types/appinfo.py +++ b/discord/types/appinfo.py @@ -24,7 +24,7 @@ from __future__ import annotations -from typing import TypedDict, List, Optional +from typing import Literal, Dict, TypedDict, List, Optional from typing_extensions import NotRequired from .user import User @@ -37,6 +37,10 @@ class InstallParams(TypedDict): permissions: str +class AppIntegrationTypeConfig(TypedDict): + oauth2_install_params: NotRequired[InstallParams] + + class BaseAppInfo(TypedDict): id: Snowflake name: str @@ -67,6 +71,7 @@ class AppInfo(BaseAppInfo): tags: NotRequired[List[str]] install_params: NotRequired[InstallParams] custom_install_url: NotRequired[str] + integration_types_config: NotRequired[Dict[Literal["0", "1"], AppIntegrationTypeConfig]] class PartialAppInfo(BaseAppInfo, total=False): From e14b7ee02b604b93d1358c93431f040db624ae8c Mon Sep 17 00:00:00 2001 From: Soheab_ <33902984+Soheab@users.noreply.github.com> Date: Sun, 5 May 2024 19:24:55 +0200 Subject: [PATCH 2/3] Remove debug print --- discord/appinfo.py | 1 - 1 file changed, 1 deletion(-) diff --git a/discord/appinfo.py b/discord/appinfo.py index 85f38cd05950..9f147652f747 100644 --- a/discord/appinfo.py +++ b/discord/appinfo.py @@ -478,7 +478,6 @@ async def edit( if integration_types_config: payload['integration_types_config'] = integration_types_config - print("payload: ", integration_types_config) data = await self._state.http.edit_application_info(reason=reason, payload=payload) return AppInfo(data=data, state=self._state) From dfd6bc057c038270d48e5f2a63aad54e1c0c5029 Mon Sep 17 00:00:00 2001 From: Soheab_ <33902984+Soheab@users.noreply.github.com> Date: Wed, 22 Jan 2025 21:52:30 +0100 Subject: [PATCH 3/3] Refactor impl --- discord/appinfo.py | 106 +++++++++++++++++++++++++++++---------------- docs/api.rst | 8 ++++ 2 files changed, 77 insertions(+), 37 deletions(-) diff --git a/discord/appinfo.py b/discord/appinfo.py index f7209329b231..2962e8d5b6b1 100644 --- a/discord/appinfo.py +++ b/discord/appinfo.py @@ -50,6 +50,7 @@ 'AppInfo', 'PartialAppInfo', 'AppInstallParams', + 'IntegrationTypeConfig', ) @@ -219,6 +220,10 @@ def __init__(self, state: ConnectionState, data: AppInfoPayload): self.interactions_endpoint_url: Optional[str] = data.get('interactions_endpoint_url') self.redirect_uris: List[str] = data.get('redirect_uris', []) self.approximate_guild_count: int = data.get('approximate_guild_count', 0) + self.approximate_user_install_count: Optional[int] = data.get('approximate_user_install_count') + self._integration_types_config: Dict[Literal["0", "1"], AppIntegrationTypeConfigPayload] = data.get( + 'integration_types_config', {} + ) def __repr__(self) -> str: return ( @@ -262,32 +267,32 @@ def flags(self) -> ApplicationFlags: return ApplicationFlags._from_value(self._flags) @property - def default_guild_install_params(self) -> Optional[AppInstallParams]: - """Optional[:class:`AppInstallParams`]: The default settings for the + def guild_integration_config(self) -> Optional[IntegrationTypeConfig]: + """Optional[:class:`IntegrationTypeConfig`]: The default settings for the application's installation context in a guild. - .. versionadded:: 2.4 + .. versionadded:: 2.5 """ if not self._integration_types_config: return None try: - return AppInstallParams(self._integration_types_config['0']['oauth2_install_params']) + return IntegrationTypeConfig(self._integration_types_config['0']) except KeyError: return None @property - def default_user_install_params(self) -> Optional[AppInstallParams]: - """Optional[:class:`AppInstallParams`]: The default settings for the + def user_integration_config(self) -> Optional[IntegrationTypeConfig]: + """Optional[:class:`IntegrationTypeConfig`]: The default settings for the application's installation context as a user. - .. versionadded:: 2.4 + .. versionadded:: 2.5 """ if not self._integration_types_config: return None try: - return AppInstallParams(self._integration_types_config['1']['oauth2_install_params']) + return IntegrationTypeConfig(self._integration_types_config['1']) except KeyError: return None @@ -305,10 +310,10 @@ async def edit( cover_image: Optional[bytes] = MISSING, interactions_endpoint_url: Optional[str] = MISSING, tags: Optional[List[str]] = MISSING, - default_guild_install_scopes: Optional[List[str]] = MISSING, - default_guild_install_permissions: Optional[Permissions] = MISSING, - default_user_install_scopes: Optional[List[str]] = MISSING, - default_user_install_permissions: Optional[Permissions] = MISSING, + guild_install_scopes: Optional[List[str]] = MISSING, + guild_install_permissions: Optional[Permissions] = MISSING, + user_install_scopes: Optional[List[str]] = MISSING, + user_install_permissions: Optional[Permissions] = MISSING, ) -> AppInfo: r"""|coro| @@ -350,16 +355,24 @@ async def edit( over the gateway. Can be ``None`` to remove the URL. tags: Optional[List[:class:`str`]] The new list of tags describing the functionality of the application. Can be ``None`` to remove the tags. - default_guild_install_scopes: Optional[List[:class:`str`]] + guild_install_scopes: Optional[List[:class:`str`]] The new list of :ddocs:`OAuth2 scopes ` of the default guild installation context. Can be ``None`` to remove the scopes. - default_guild_install_permissions: Optional[:class:`Permissions`] + + .. versionadded: 2.5 + guild_install_permissions: Optional[:class:`Permissions`] The new permissions of the default guild installation context. Can be ``None`` to remove the permissions. - default_user_install_scopes: Optional[List[:class:`str`]] + + .. versionadded: 2.5 + user_install_scopes: Optional[List[:class:`str`]] The new list of :ddocs:`OAuth2 scopes ` of the default user installation context. Can be ``None`` to remove the scopes. - default_user_install_permissions: Optional[:class:`Permissions`] + + .. versionadded: 2.5 + user_install_permissions: Optional[:class:`Permissions`] The new permissions of the default user installation context. Can be ``None`` to remove the permissions. + + .. versionadded: 2.5 reason: Optional[:class:`str`] The reason for editing the application. Shows up on the audit log. @@ -370,7 +383,7 @@ async def edit( ValueError The image format passed in to ``icon`` or ``cover_image`` is invalid. This is also raised when ``install_params_scopes`` and ``install_params_permissions`` are incompatible with each other. - or when ``default_guild_install_scopes`` and ``default_guild_install_permissions`` are incompatible with each other. + or when ``guild_install_scopes`` and ``guild_install_permissions`` are incompatible with each other. Returns ------- @@ -437,45 +450,45 @@ async def edit( payload['tags'] = tags integration_types_config: Dict[str, Any] = {} - if default_guild_install_scopes is not MISSING or default_guild_install_permissions is not MISSING: + if guild_install_scopes is not MISSING or guild_install_permissions is not MISSING: guild_install_params: Optional[Dict[str, Any]] = {} - if default_guild_install_scopes in (None, MISSING): - default_guild_install_scopes = [] + if guild_install_scopes in (None, MISSING): + guild_install_scopes = [] - if "bot" not in default_guild_install_scopes and default_guild_install_permissions is not MISSING: - raise ValueError("'bot' must be in default_guild_install_scopes if default_guild_install_permissions is set") + if "bot" not in guild_install_scopes and guild_install_permissions is not MISSING: + raise ValueError("'bot' must be in guild_install_scopes if guild_install_permissions is set") - if default_guild_install_permissions in (None, MISSING): + if guild_install_permissions in (None, MISSING): guild_install_params['permissions'] = 0 else: - guild_install_params['permissions'] = default_guild_install_permissions.value + guild_install_params['permissions'] = guild_install_permissions.value - guild_install_params['scopes'] = default_guild_install_scopes + guild_install_params['scopes'] = guild_install_scopes integration_types_config['0'] = {'oauth2_install_params': guild_install_params or None} else: - if default_guild_install_permissions is not MISSING: - raise ValueError("default_guild_install_scopes must be set if default_guild_install_permissions is set") + if guild_install_permissions is not MISSING: + raise ValueError("guild_install_scopes must be set if guild_install_permissions is set") - if default_user_install_scopes is not MISSING or default_user_install_permissions is not MISSING: + if user_install_scopes is not MISSING or user_install_permissions is not MISSING: user_install_params: Optional[Dict[str, Any]] = {} - if default_user_install_scopes in (None, MISSING): - default_user_install_scopes = [] + if user_install_scopes in (None, MISSING): + user_install_scopes = [] - if "bot" not in default_user_install_scopes and default_user_install_permissions is not MISSING: - raise ValueError("'bot' must be in default_user_install_scopes if default_user_install_permissions is set") + if "bot" not in user_install_scopes and user_install_permissions is not MISSING: + raise ValueError("'bot' must be in user_install_scopes if user_install_permissions is set") - if default_user_install_permissions in (None, MISSING): + if user_install_permissions in (None, MISSING): user_install_params['permissions'] = 0 else: - user_install_params['permissions'] = default_user_install_permissions.value + user_install_params['permissions'] = user_install_permissions.value - user_install_params['scopes'] = default_user_install_scopes + user_install_params['scopes'] = user_install_scopes integration_types_config['1'] = {'oauth2_install_params': user_install_params or None} else: - if default_user_install_permissions is not MISSING: - raise ValueError("default_user_install_scopes must be set if default_user_install_permissions is set") + if user_install_permissions is not MISSING: + raise ValueError("user_install_scopes must be set if user_install_permissions is set") if integration_types_config: payload['integration_types_config'] = integration_types_config @@ -611,3 +624,22 @@ class AppInstallParams: def __init__(self, data: InstallParamsPayload) -> None: self.scopes: List[str] = data.get('scopes', []) self.permissions: Permissions = Permissions(int(data['permissions'])) + + +class IntegrationTypeConfig: + """Represents the default settings for the application's installation context. + + .. versionadded:: 2.5 + + Attributes + ---------- + oauth2_install_params: Optional[:class:`AppInstallParams`] + The install params for this installation context's default in-app authorization link. + """ + + def __init__(self, data: AppIntegrationTypeConfigPayload) -> None: + self.oauth2_install_params: Optional[AppInstallParams] = None + try: + self.oauth2_install_params = AppInstallParams(data['oauth2_install_params']) # type: ignore # EAFP + except KeyError: + pass diff --git a/docs/api.rst b/docs/api.rst index 8da2ba80c894..3de1fef1be61 100644 --- a/docs/api.rst +++ b/docs/api.rst @@ -80,6 +80,14 @@ AppInstallParams .. autoclass:: AppInstallParams() :members: +IntegrationTypeConfig +~~~~~~~~~~~~~~~~~~~~~~ + +.. attributetable:: IntegrationTypeConfig + +.. autoclass:: IntegrationTypeConfig() + :members: + Team ~~~~~