diff --git a/discord/client.py b/discord/client.py index 13f00220b..87e3cb3fa 100644 --- a/discord/client.py +++ b/discord/client.py @@ -238,6 +238,7 @@ class Client: self._connection.shard_count = self.shard_count self._closed: bool = False self._ready: asyncio.Event = MISSING + self._application: Optional[AppInfo] = None self._connection._get_websocket = self._get_websocket self._connection._get_client = lambda: self @@ -345,8 +346,9 @@ class Client: """Optional[:class:`int`]: The client's application ID. If this is not passed via ``__init__`` then this is retrieved - through the gateway when an event contains the data. Usually - after :func:`~discord.on_connect` is called. + through the gateway when an event contains the data or after a call + to :meth:`~discord.Client.login`. Usually after :func:`~discord.on_connect` + is called. .. versionadded:: 2.0 """ @@ -360,6 +362,22 @@ class Client: """ return self._connection.application_flags + @property + def application(self) -> Optional[AppInfo]: + """Optional[:class:`~discord.AppInfo`]: The client's application info. + + This is retrieved on :meth:`~discord.Client.login` and is not updated + afterwards. This allows populating the application_id without requiring a + gateway connection. + + This is ``None`` if accessed before :meth:`~discord.Client.login` is called. + + .. seealso:: The :meth:`~discord.Client.application_info` API call + + .. versionadded:: 2.0 + """ + return self._application + def is_ready(self) -> bool: """:class:`bool`: Specifies if the client's internal cache is ready for use.""" return self._ready is not MISSING and self._ready.is_set() @@ -541,6 +559,13 @@ class Client: data = await self.http.static_login(token.strip()) self._connection.user = ClientUser(state=self._connection, data=data) + self._application = await self.application_info() + if self._connection.application_id is None: + self._connection.application_id = self._application.id + + if not self._connection.application_flags: + self._connection.application_flags = self._application.flags + await self.setup_hook() async def connect(self, *, reconnect: bool = True) -> None: diff --git a/discord/state.py b/discord/state.py index 773522628..e574ade27 100644 --- a/discord/state.py +++ b/discord/state.py @@ -184,6 +184,7 @@ class ConnectionState: self.shard_count: Optional[int] = None self._ready_task: Optional[asyncio.Task] = None self.application_id: Optional[int] = utils._get_as_snowflake(options, 'application_id') + self.application_flags: ApplicationFlags = utils.MISSING self.heartbeat_timeout: float = options.get('heartbeat_timeout', 60.0) self.guild_ready_timeout: float = options.get('guild_ready_timeout', 2.0) if self.guild_ready_timeout < 0: diff --git a/examples/app_commands/basic.py b/examples/app_commands/basic.py index 51a41b0a8..f646643d0 100644 --- a/examples/app_commands/basic.py +++ b/examples/app_commands/basic.py @@ -8,8 +8,8 @@ MY_GUILD = discord.Object(id=0) # replace with your guild id class MyClient(discord.Client): - def __init__(self, *, intents: discord.Intents, application_id: int): - super().__init__(intents=intents, application_id=application_id) + def __init__(self, *, intents: discord.Intents): + super().__init__(intents=intents) # A CommandTree is a special type that holds all the application command # state required to make it work. This is a separate class because it # allows all the extra state to be opt-in. @@ -29,10 +29,7 @@ class MyClient(discord.Client): intents = discord.Intents.default() - -# In order to use a basic synchronization of the app commands in the setup_hook, -# you have to replace the 0 with your bot's application_id that you find in the developer portal. -client = MyClient(intents=intents, application_id=0) +client = MyClient(intents=intents) @client.event