diff --git a/disagreement/client.py b/disagreement/client.py index 2ca3087..c2c80f6 100644 --- a/disagreement/client.py +++ b/disagreement/client.py @@ -51,6 +51,7 @@ if TYPE_CHECKING: Thread, DMChannel, Webhook, + GuildTemplate, ScheduledEvent, AuditLogEntry, Invite, @@ -710,6 +711,13 @@ class Client: self._webhooks[webhook.id] = webhook return webhook + def parse_template(self, data: Dict[str, Any]) -> "GuildTemplate": + """Parses template data into a GuildTemplate object.""" + + from .models import GuildTemplate + + return GuildTemplate(data, client_instance=self) + def parse_scheduled_event(self, data: Dict[str, Any]) -> "ScheduledEvent": """Parses scheduled event data and updates cache.""" @@ -1313,6 +1321,45 @@ class Client: await self._http.delete_webhook(webhook_id) + async def fetch_templates(self, guild_id: Snowflake) -> List["GuildTemplate"]: + """|coro| Fetch all templates for a guild.""" + + if self._closed: + raise DisagreementException("Client is closed.") + + data = await self._http.get_guild_templates(guild_id) + return [self.parse_template(t) for t in data] + + async def create_template( + self, guild_id: Snowflake, payload: Dict[str, Any] + ) -> "GuildTemplate": + """|coro| Create a template for a guild.""" + + if self._closed: + raise DisagreementException("Client is closed.") + + data = await self._http.create_guild_template(guild_id, payload) + return self.parse_template(data) + + async def sync_template( + self, guild_id: Snowflake, template_code: str + ) -> "GuildTemplate": + """|coro| Sync a template to the guild's current state.""" + + if self._closed: + raise DisagreementException("Client is closed.") + + data = await self._http.sync_guild_template(guild_id, template_code) + return self.parse_template(data) + + async def delete_template(self, guild_id: Snowflake, template_code: str) -> None: + """|coro| Delete a guild template.""" + + if self._closed: + raise DisagreementException("Client is closed.") + + await self._http.delete_guild_template(guild_id, template_code) + async def fetch_scheduled_events( self, guild_id: Snowflake ) -> List["ScheduledEvent"]: diff --git a/disagreement/http.py b/disagreement/http.py index f4d3f91..c094f69 100644 --- a/disagreement/http.py +++ b/disagreement/http.py @@ -628,6 +628,33 @@ class HTTPClient: """Fetches a guild object for a given guild ID.""" return await self.request("GET", f"/guilds/{guild_id}") + async def get_guild_templates(self, guild_id: "Snowflake") -> List[Dict[str, Any]]: + """Fetches all templates for the given guild.""" + return await self.request("GET", f"/guilds/{guild_id}/templates") + + async def create_guild_template( + self, guild_id: "Snowflake", payload: Dict[str, Any] + ) -> Dict[str, Any]: + """Creates a guild template.""" + return await self.request( + "POST", f"/guilds/{guild_id}/templates", payload=payload + ) + + async def sync_guild_template( + self, guild_id: "Snowflake", template_code: str + ) -> Dict[str, Any]: + """Syncs a guild template to the guild's current state.""" + return await self.request( + "PUT", + f"/guilds/{guild_id}/templates/{template_code}", + ) + + async def delete_guild_template( + self, guild_id: "Snowflake", template_code: str + ) -> None: + """Deletes a guild template.""" + await self.request("DELETE", f"/guilds/{guild_id}/templates/{template_code}") + async def get_guild_scheduled_events( self, guild_id: "Snowflake" ) -> List[Dict[str, Any]]: diff --git a/disagreement/models.py b/disagreement/models.py index d2aac8f..c10c5a4 100644 --- a/disagreement/models.py +++ b/disagreement/models.py @@ -1516,6 +1516,33 @@ class Webhook: return self._client.parse_message(message_data) +class GuildTemplate: + """Represents a guild template.""" + + def __init__( + self, data: Dict[str, Any], client_instance: Optional["Client"] = None + ): + self._client = client_instance + self.code: str = data["code"] + self.name: str = data["name"] + self.description: Optional[str] = data.get("description") + self.usage_count: int = data.get("usage_count", 0) + self.creator_id: str = data.get("creator_id", "") + self.creator: Optional[User] = ( + User(data["creator"]) if data.get("creator") else None + ) + self.created_at: Optional[str] = data.get("created_at") + self.updated_at: Optional[str] = data.get("updated_at") + self.source_guild_id: Optional[str] = data.get("source_guild_id") + self.serialized_source_guild: Dict[str, Any] = data.get( + "serialized_source_guild", {} + ) + self.is_dirty: Optional[bool] = data.get("is_dirty") + + def __repr__(self) -> str: + return f"" + + # --- Message Components --- diff --git a/tests/test_templates.py b/tests/test_templates.py new file mode 100644 index 0000000..3a49694 --- /dev/null +++ b/tests/test_templates.py @@ -0,0 +1,42 @@ +import pytest +from types import SimpleNamespace +from unittest.mock import AsyncMock + +from disagreement.http import HTTPClient +from disagreement.client import Client +from disagreement.models import GuildTemplate + + +@pytest.mark.asyncio +async def test_get_guild_templates_calls_request(): + http = HTTPClient(token="t") + http.request = AsyncMock(return_value=[]) + await http.get_guild_templates("1") + http.request.assert_called_once_with("GET", "/guilds/1/templates") + + +@pytest.mark.asyncio +async def test_client_fetch_templates_returns_models(): + http = SimpleNamespace( + get_guild_templates=AsyncMock( + return_value=[ + { + "code": "c", + "name": "n", + "usage_count": 0, + "creator_id": "u", + "created_at": "t", + "updated_at": "t", + "source_guild_id": "g", + } + ] + ) + ) + client = Client.__new__(Client) + client._http = http + client._closed = False + + templates = await client.fetch_templates("g") + + http.get_guild_templates.assert_awaited_once_with("g") + assert isinstance(templates[0], GuildTemplate)