Add guilds property to client (#83)

This commit is contained in:
Slipstream 2025-06-15 15:21:01 -06:00 committed by GitHub
parent 6eff962682
commit a68bbe7826
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194

View File

@ -18,12 +18,12 @@ from typing import (
)
from types import ModuleType
from datetime import datetime, timedelta
from .http import HTTPClient
from .gateway import GatewayClient
from .shard_manager import ShardManager
from .event_dispatcher import EventDispatcher
from datetime import datetime, timedelta
from .http import HTTPClient
from .gateway import GatewayClient
from .shard_manager import ShardManager
from .event_dispatcher import EventDispatcher
from .enums import GatewayIntent, InteractionType, GatewayOpcode, VoiceRegion
from .errors import DisagreementException, AuthenticationError
from .typing import Typing
@ -37,8 +37,8 @@ from .ext import loader as ext_loader
from .interactions import Interaction, Snowflake
from .error_handler import setup_global_error_handler
from .voice_client import VoiceClient
from .models import Activity
from .utils import utcnow
from .models import Activity
from .utils import utcnow
if TYPE_CHECKING:
from .models import (
@ -88,15 +88,15 @@ class Client:
verbose (bool): If True, print raw HTTP and Gateway traffic for debugging.
mention_replies (bool): Whether replies mention the author by default.
allowed_mentions (Optional[Dict[str, Any]]): Default allowed mentions for messages.
http_options (Optional[Dict[str, Any]]): Extra options passed to
:class:`HTTPClient` for creating the internal
:class:`aiohttp.ClientSession`.
message_cache_maxlen (Optional[int]): Maximum number of messages to keep
in the cache. When ``None``, the cache size is unlimited.
sync_commands_on_ready (bool): If ``True``, automatically call
:meth:`Client.sync_application_commands` after the ``READY`` event
when :attr:`Client.application_id` is available.
"""
http_options (Optional[Dict[str, Any]]): Extra options passed to
:class:`HTTPClient` for creating the internal
:class:`aiohttp.ClientSession`.
message_cache_maxlen (Optional[int]): Maximum number of messages to keep
in the cache. When ``None``, the cache size is unlimited.
sync_commands_on_ready (bool): If ``True``, automatically call
:meth:`Client.sync_application_commands` after the ``READY`` event
when :attr:`Client.application_id` is available.
"""
def __init__(
self,
@ -115,9 +115,9 @@ class Client:
gateway_max_backoff: float = 60.0,
member_cache_flags: Optional[MemberCacheFlags] = None,
message_cache_maxlen: Optional[int] = None,
http_options: Optional[Dict[str, Any]] = None,
sync_commands_on_ready: bool = True,
):
http_options: Optional[Dict[str, Any]] = None,
sync_commands_on_ready: bool = True,
):
if not token:
raise ValueError("A bot token must be provided.")
@ -167,11 +167,11 @@ class Client:
self._closed: bool = False
self._ready_event: asyncio.Event = asyncio.Event()
self.user: Optional["User"] = (
None # The bot's own user object, populated on READY
)
self.start_time: Optional[datetime] = None
self.user: Optional["User"] = (
None # The bot's own user object, populated on READY
)
self.start_time: Optional[datetime] = None
# Internal Caches
self._guilds: GuildCache = GuildCache()
@ -184,9 +184,9 @@ class Client:
self._webhooks: Dict[Snowflake, "Webhook"] = {}
# Default whether replies mention the user
self.mention_replies: bool = mention_replies
self.allowed_mentions: Optional[Dict[str, Any]] = allowed_mentions
self.sync_commands_on_ready: bool = sync_commands_on_ready
self.mention_replies: bool = mention_replies
self.allowed_mentions: Optional[Dict[str, Any]] = allowed_mentions
self.sync_commands_on_ready: bool = sync_commands_on_ready
# Basic signal handling for graceful shutdown
# This might be better handled by the user's application code, but can be a nice default.
@ -240,18 +240,18 @@ class Client:
AuthenticationError: If the token is invalid.
"""
if self._closed:
raise DisagreementException("Client is closed and cannot connect.")
raise DisagreementException("Client is closed and cannot connect.")>>>>>>> master
if self.shard_count and self.shard_count > 1:
await self._initialize_shard_manager()
assert self._shard_manager is not None
await self._shard_manager.start()
print(
f"Client connected using {self.shard_count} shards, waiting for READY signal..."
)
await self.wait_until_ready()
self.start_time = utcnow()
print("Client is READY!")
return
await self._shard_manager.start()
print(
f"Client connected using {self.shard_count} shards, waiting for READY signal..."
)
await self.wait_until_ready()
self.start_time = utcnow()
print("Client is READY!")
return
await self._initialize_gateway()
assert self._gateway is not None # Should be initialized by now
@ -264,11 +264,11 @@ class Client:
await self._gateway.connect()
# After successful connection, GatewayClient's HELLO handler will trigger IDENTIFY/RESUME
# and its READY handler will set self._ready_event via dispatcher.
print("Client connected to Gateway, waiting for READY signal...")
await self.wait_until_ready() # Wait for the READY event from Gateway
self.start_time = utcnow()
print("Client is READY!")
return # Successfully connected and ready
print("Client connected to Gateway, waiting for READY signal...")
await self.wait_until_ready() # Wait for the READY event from Gateway
self.start_time = utcnow()
print("Client is READY!")
return # Successfully connected and ready
except AuthenticationError: # Non-recoverable by retry here
print("Authentication failed. Please check your bot token.")
await self.close() # Ensure cleanup
@ -376,12 +376,12 @@ class Client:
if self._gateway:
await self._gateway.close()
if self._http: # HTTPClient has its own session to close
await self._http.close()
self._ready_event.set() # Ensure any waiters for ready are unblocked
self.start_time = None
print("Client closed.")
if self._http: # HTTPClient has its own session to close
await self._http.close()
self._ready_event.set() # Ensure any waiters for ready are unblocked
self.start_time = None
print("Client closed.")
async def __aenter__(self) -> "Client":
"""Enter the context manager by connecting to Discord."""
@ -423,17 +423,22 @@ class Client:
return self._gateway.latency
return None
@property
def latency_ms(self) -> Optional[float]:
"""Returns the gateway latency in milliseconds, or ``None`` if unavailable."""
latency = getattr(self._gateway, "latency_ms", None)
return round(latency, 2) if latency is not None else None
def uptime(self) -> Optional[timedelta]:
"""Return the duration since the client connected, or ``None`` if not connected."""
if self.start_time is None:
return None
return utcnow() - self.start_time
@property
def latency_ms(self) -> Optional[float]:
"""Returns the gateway latency in milliseconds, or ``None`` if unavailable."""
latency = getattr(self._gateway, "latency_ms", None)
return round(latency, 2) if latency is not None else None
@property
def guilds(self) -> List["Guild"]:
"""Returns all guilds from the internal cache."""
return self._guilds.values()
def uptime(self) -> Optional[timedelta]:
"""Return the duration since the client connected, or ``None`` if not connected."""
if self.start_time is None:
return None
return utcnow() - self.start_time
async def wait_until_ready(self) -> None:
"""|coro|
@ -945,13 +950,13 @@ class Client:
guild.roles.append(role)
return role
def parse_guild(self, data: Dict[str, Any]) -> "Guild":
"""Parses guild data and returns a :class:`Guild` object, updating cache."""
from .models import Guild
shard_id = data.get("shard_id")
guild = Guild(data, client_instance=self, shard_id=shard_id)
self._guilds.set(guild.id, guild)
def parse_guild(self, data: Dict[str, Any]) -> "Guild":
"""Parses guild data and returns a :class:`Guild` object, updating cache."""
from .models import Guild
shard_id = data.get("shard_id")
guild = Guild(data, client_instance=self, shard_id=shard_id)
self._guilds.set(guild.id, guild)
presences = {p["user"]["id"]: p for p in data.get("presences", [])}
voice_states = {vs["user_id"]: vs for vs in data.get("voice_states", [])}
@ -1700,20 +1705,20 @@ class Client:
pass
async def on_typing_start(self, typing) -> None:
"""|coro| Called when a user starts typing in a channel."""
pass
async def on_connect(self) -> None:
"""|coro| Called when the WebSocket connection opens."""
pass
async def on_disconnect(self) -> None:
"""|coro| Called when the WebSocket connection closes."""
pass
async def on_typing_start(self, typing) -> None:
"""|coro| Called when a user starts typing in a channel."""
pass
async def on_connect(self) -> None:
"""|coro| Called when the WebSocket connection opens."""
pass
async def on_disconnect(self) -> None:
"""|coro| Called when the WebSocket connection closes."""
pass
async def on_app_command_error(
self, context: AppCommandContext, error: Exception