From afeb86a3953f8283a5646a811c5096b86c001f4e Mon Sep 17 00:00:00 2001 From: Slipstream Date: Wed, 11 Jun 2025 14:26:49 -0600 Subject: [PATCH] Add guild widget support (#59) --- disagreement/client.py | 18 +++++++++++++++ disagreement/http.py | 38 ++++++++++++++++++++++---------- disagreement/models.py | 10 +++++++++ tests/test_widget.py | 50 ++++++++++++++++++++++++++++++++++++++++++ 4 files changed, 104 insertions(+), 12 deletions(-) create mode 100644 tests/test_widget.py diff --git a/disagreement/client.py b/disagreement/client.py index fbd096a..3c42629 100644 --- a/disagreement/client.py +++ b/disagreement/client.py @@ -1430,6 +1430,24 @@ class Client: await self._http.delete_guild_template(guild_id, template_code) + async def fetch_widget(self, guild_id: Snowflake) -> Dict[str, Any]: + """|coro| Fetch a guild's widget settings.""" + + if self._closed: + raise DisagreementException("Client is closed.") + + return await self._http.get_guild_widget(guild_id) + + async def edit_widget( + self, guild_id: Snowflake, payload: Dict[str, Any] + ) -> Dict[str, Any]: + """|coro| Edit a guild's widget settings.""" + + if self._closed: + raise DisagreementException("Client is closed.") + + return await self._http.edit_guild_widget(guild_id, payload) + async def fetch_scheduled_events( self, guild_id: Snowflake ) -> List["ScheduledEvent"]: diff --git a/disagreement/http.py b/disagreement/http.py index 3a81990..c73d9d3 100644 --- a/disagreement/http.py +++ b/disagreement/http.py @@ -369,18 +369,18 @@ class HTTPClient: ) async def delete_user_reaction( - self, - channel_id: "Snowflake", - message_id: "Snowflake", - emoji: str, - user_id: "Snowflake", - ) -> None: - """Removes another user's reaction from a message.""" - encoded = quote(emoji) - await self.request( - "DELETE", - f"/channels/{channel_id}/messages/{message_id}/reactions/{encoded}/{user_id}", - ) + self, + channel_id: "Snowflake", + message_id: "Snowflake", + emoji: str, + user_id: "Snowflake", + ) -> None: + """Removes another user's reaction from a message.""" + encoded = quote(emoji) + await self.request( + "DELETE", + f"/channels/{channel_id}/messages/{message_id}/reactions/{encoded}/{user_id}", + ) async def get_reactions( self, channel_id: "Snowflake", message_id: "Snowflake", emoji: str @@ -678,6 +678,20 @@ class HTTPClient: """Fetches a guild object for a given guild ID.""" return await self.request("GET", f"/guilds/{guild_id}") + async def get_guild_widget(self, guild_id: "Snowflake") -> Dict[str, Any]: + """Fetches the guild widget settings.""" + + return await self.request("GET", f"/guilds/{guild_id}/widget") + + async def edit_guild_widget( + self, guild_id: "Snowflake", payload: Dict[str, Any] + ) -> Dict[str, Any]: + """Edits the guild widget settings.""" + + return await self.request( + "PATCH", f"/guilds/{guild_id}/widget", payload=payload + ) + 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") diff --git a/disagreement/models.py b/disagreement/models.py index 2aa8b74..e47f366 100644 --- a/disagreement/models.py +++ b/disagreement/models.py @@ -1131,6 +1131,16 @@ class Guild: def __repr__(self) -> str: return f"" + async def fetch_widget(self) -> Dict[str, Any]: + """|coro| Fetch this guild's widget settings.""" + + return await self._client.fetch_widget(self.id) + + async def edit_widget(self, payload: Dict[str, Any]) -> Dict[str, Any]: + """|coro| Edit this guild's widget settings.""" + + return await self._client.edit_widget(self.id, payload) + async def fetch_members(self, *, limit: Optional[int] = None) -> List["Member"]: """|coro| diff --git a/tests/test_widget.py b/tests/test_widget.py new file mode 100644 index 0000000..45a3e13 --- /dev/null +++ b/tests/test_widget.py @@ -0,0 +1,50 @@ +import pytest +from types import SimpleNamespace +from unittest.mock import AsyncMock + +from disagreement.http import HTTPClient +from disagreement.client import Client + + +@pytest.mark.asyncio +async def test_get_guild_widget_calls_request(): + http = HTTPClient(token="t") + http.request = AsyncMock(return_value={}) + await http.get_guild_widget("1") + http.request.assert_called_once_with("GET", "/guilds/1/widget") + + +@pytest.mark.asyncio +async def test_edit_guild_widget_calls_request(): + http = HTTPClient(token="t") + http.request = AsyncMock(return_value={}) + payload = {"enabled": True} + await http.edit_guild_widget("1", payload) + http.request.assert_called_once_with("PATCH", "/guilds/1/widget", payload=payload) + + +@pytest.mark.asyncio +async def test_client_fetch_widget_returns_data(): + http = SimpleNamespace(get_guild_widget=AsyncMock(return_value={"enabled": True})) + client = Client.__new__(Client) + client._http = http + client._closed = False + + data = await client.fetch_widget("1") + + http.get_guild_widget.assert_awaited_once_with("1") + assert data == {"enabled": True} + + +@pytest.mark.asyncio +async def test_client_edit_widget_returns_data(): + http = SimpleNamespace(edit_guild_widget=AsyncMock(return_value={"enabled": False})) + client = Client.__new__(Client) + client._http = http + client._closed = False + + payload = {"enabled": False} + data = await client.edit_widget("1", payload) + + http.edit_guild_widget.assert_awaited_once_with("1", payload) + assert data == {"enabled": False}