From b9d93a90fa34360b9152cffaa708565e5069b762 Mon Sep 17 00:00:00 2001 From: Slipstream Date: Tue, 10 Jun 2025 20:37:21 -0600 Subject: [PATCH] Adds invite management functionality Introduces comprehensive invite handling capabilities including creation, deletion, and retrieval operations. Implements invite data model with proper parsing and representation methods to handle Discord invite objects and their metadata. Provides HTTP client methods for all invite-related API endpoints with appropriate error handling and response processing. Includes documentation with practical examples for common invite operations. --- disagreement/client.py | 35 +++++++++++++++++++++++++++++++++++ disagreement/http.py | 26 +++++++++++++++++++++++++- disagreement/models.py | 36 ++++++++++++++++++++++++++++++++++++ docs/invites.md | 24 ++++++++++++++++++++++++ 4 files changed, 120 insertions(+), 1 deletion(-) create mode 100644 docs/invites.md diff --git a/disagreement/client.py b/disagreement/client.py index 57c38cc..79e5757 100644 --- a/disagreement/client.py +++ b/disagreement/client.py @@ -50,6 +50,7 @@ if TYPE_CHECKING: Thread, DMChannel, Webhook, + Invite, ) from .ui.view import View from .enums import ChannelType as EnumChannelType @@ -698,6 +699,13 @@ class Client: self._webhooks[webhook.id] = webhook return webhook + def parse_invite(self, data: Dict[str, Any]) -> "Invite": + """Parses invite data into an :class:`Invite`.""" + + from .models import Invite + + return Invite.from_dict(data) + async def fetch_user(self, user_id: Snowflake) -> Optional["User"]: """Fetches a user by ID from Discord.""" if self._closed: @@ -1249,6 +1257,33 @@ class Client: await self._http.delete_webhook(webhook_id) + async def create_invite( + self, channel_id: Snowflake, payload: Dict[str, Any] + ) -> "Invite": + """|coro| Create an invite for the given channel.""" + + if self._closed: + raise DisagreementException("Client is closed.") + + return await self._http.create_invite(channel_id, payload) + + async def delete_invite(self, code: str) -> None: + """|coro| Delete an invite by code.""" + + if self._closed: + raise DisagreementException("Client is closed.") + + await self._http.delete_invite(code) + + async def fetch_invites(self, channel_id: Snowflake) -> List["Invite"]: + """|coro| Fetch all invites for a channel.""" + + if self._closed: + raise DisagreementException("Client is closed.") + + data = await self._http.get_channel_invites(channel_id) + return [self.parse_invite(inv) for inv in data] + # --- Application Command Methods --- async def process_interaction(self, interaction: Interaction) -> None: """Internal method to process an interaction from the gateway.""" diff --git a/disagreement/http.py b/disagreement/http.py index 3e83404..ee1e47c 100644 --- a/disagreement/http.py +++ b/disagreement/http.py @@ -23,7 +23,7 @@ from .interactions import InteractionResponsePayload if TYPE_CHECKING: from .client import Client - from .models import Message, Webhook, File + from .models import Message, Webhook, File, Invite from .interactions import ApplicationCommand, Snowflake # Discord API constants @@ -409,6 +409,30 @@ class HTTPClient: """Fetches a channel by ID.""" return await self.request("GET", f"/channels/{channel_id}") + async def get_channel_invites( + self, channel_id: "Snowflake" + ) -> List[Dict[str, Any]]: + """Fetches the invites for a channel.""" + + return await self.request("GET", f"/channels/{channel_id}/invites") + + async def create_invite( + self, channel_id: "Snowflake", payload: Dict[str, Any] + ) -> "Invite": + """Creates an invite for a channel.""" + + data = await self.request( + "POST", f"/channels/{channel_id}/invites", payload=payload + ) + from .models import Invite + + return Invite.from_dict(data) + + async def delete_invite(self, code: str) -> None: + """Deletes an invite by code.""" + + await self.request("DELETE", f"/invites/{code}") + async def create_webhook( self, channel_id: "Snowflake", payload: Dict[str, Any] ) -> "Webhook": diff --git a/disagreement/models.py b/disagreement/models.py index 1b28207..1fc1f45 100644 --- a/disagreement/models.py +++ b/disagreement/models.py @@ -6,6 +6,7 @@ Data models for Discord objects. import asyncio import json +from dataclasses import dataclass from typing import Any, AsyncIterator, Dict, List, Optional, TYPE_CHECKING, Union import aiohttp # pylint: disable=import-error @@ -1978,6 +1979,41 @@ class Reaction: return f"" +@dataclass +class Invite: + """Represents a Discord invite.""" + + code: str + channel_id: Optional[str] + guild_id: Optional[str] + inviter_id: Optional[str] + uses: Optional[int] + max_uses: Optional[int] + max_age: Optional[int] + temporary: Optional[bool] + created_at: Optional[str] + + @classmethod + def from_dict(cls, data: Dict[str, Any]) -> "Invite": + channel = data.get("channel") + guild = data.get("guild") + inviter = data.get("inviter") + return cls( + code=data["code"], + channel_id=(channel or {}).get("id") if channel else data.get("channel_id"), + guild_id=(guild or {}).get("id") if guild else data.get("guild_id"), + inviter_id=(inviter or {}).get("id"), + uses=data.get("uses"), + max_uses=data.get("max_uses"), + max_age=data.get("max_age"), + temporary=data.get("temporary"), + created_at=data.get("created_at"), + ) + + def __repr__(self) -> str: + return f"" + + class GuildMemberRemove: """Represents a GUILD_MEMBER_REMOVE event.""" diff --git a/docs/invites.md b/docs/invites.md new file mode 100644 index 0000000..138eca9 --- /dev/null +++ b/docs/invites.md @@ -0,0 +1,24 @@ +# Working with Invites + +The library exposes helper methods for creating and deleting invites. + +## Create an Invite + +```python +invite = await client.create_invite("1234567890", {"max_age": 3600}) +print(invite.code) +``` + +## Delete an Invite + +```python +await client.delete_invite(invite.code) +``` + +## List Invites + +```python +invites = await client.fetch_invites("1234567890") +for inv in invites: + print(inv.code, inv.uses) +```