Source code for discord.client
from ._http import HTTP
from ._logging import Logger
from .events import EventManager
from .gateway import DiscordWebSocket
from .flags import GatewayIntent
from .utils import MISSING, Nullable, Optional
from aiohttp import ClientSession
from collections.abc import Coroutine, Callable
from os import environ, getenv
from typing import Self
import asyncio
[docs]
class Client:
"""Represents a Discord client.
:param intents: Set of Gateway intents to associate with the client
"""
__instance: Optional[Self] = MISSING
"""Singleton Discord client instance
:meta private:
"""
def __new__(cls: type[Self], *, intents: GatewayIntent) -> Self:
if not cls.__instance:
if not isinstance(intents, GatewayIntent):
raise TypeError(f"intents: Must be an instance of {GatewayIntent}; not {intents.__class__}")
instance: Self = super().__new__(cls)
instance.__event_loop: Optional[asyncio.AbstractEventLoop] = MISSING
instance.__event_manager: EventManager = EventManager(instance)
instance.__http: HTTP = HTTP(instance)
instance.__intents: GatewayIntent = intents
instance.__session: Nullable[ClientSession] = None
instance.__socket: Nullable[DiscordWebSocket] = None
instance.__token: Optional[str] = environ.get("APPLICATION_TOKEN")
if not instance._Client__token:
from dotenv import load_dotenv
load_dotenv()
instance.__token: Optional[str] = getenv("APPLICATION_TOKEN")
if not instance._Client__token:
raise ValueError("No valid Discord application token configured")
exit()
cls.__instance: Self = instance
return cls.__instance
@property
def _http(self) -> HTTP:
"""HTTP/S connection instance to the Discord API"""
return self.__http
@property
def _loop(self):
if not self.__event_loop:
from asyncio import new_event_loop, set_event_loop
self.__event_loop = new_event_loop()
set_event_loop(self.__event_loop)
return self.__event_loop
@property
def _session(self) -> Nullable[ClientSession]:
"""Current aiohttp session of the client, if any"""
return self.__session
[docs]
async def close(self, code: int = 4000, /) -> None:
"""Close connection from the Discord API"""
await self.ws.close(code)
await self._session.close()
[docs]
def connect(self, *, gateway: bool = True) -> None:
"""Initiate a connection with the Discord API
:param gateway: Join connection with the gateway
"""
try:
async def inner() -> None:
if not isinstance(gateway, bool):
raise TypeError(f"gateway: Must be an instance of {bool}; not {gateway.__class__}")
self.__session: ClientSession = ClientSession(self._http.BASE_URL, raise_for_status = self._http._HTTP__status_check)
if gateway:
self.__socket: DiscordWebSocket = DiscordWebSocket(self)
await self.ws.connect()
self._loop.run_until_complete(inner())
except KeyboardInterrupt:
self._loop.create_task(self.close())
Logger.info("Program terminated through keyboard interrupt")
exit()
except Exception as exception:
Logger.error(exception)
else:
self._loop.create_task(self.close(1000))
[docs]
def event_listener(name: str) -> None:
"""Register a dispatch event listener
:param name: Name of the event to listen to
"""
async def decorator(function: Callable[..., Coroutine]) -> None:
self.__event_manager.add_listener(name, function)
return decorator
@property
def intents(self) -> GatewayIntent:
"""Set of Gateway intents to associate with the client."""
return self.__intents
@property
def ws(self) -> Nullable[DiscordWebSocket]:
"""WebSocket connection instance to the Discord gateway, if any."""
return self.__socket