disagreement 0.2.0rc1__py3-none-any.whl → 0.4.0__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (38) hide show
  1. disagreement/__init__.py +2 -4
  2. disagreement/audio.py +42 -5
  3. disagreement/cache.py +43 -4
  4. disagreement/caching.py +121 -0
  5. disagreement/client.py +1682 -1535
  6. disagreement/enums.py +10 -3
  7. disagreement/error_handler.py +5 -1
  8. disagreement/errors.py +1341 -3
  9. disagreement/event_dispatcher.py +3 -5
  10. disagreement/ext/__init__.py +1 -0
  11. disagreement/ext/app_commands/__init__.py +0 -2
  12. disagreement/ext/app_commands/commands.py +0 -2
  13. disagreement/ext/app_commands/context.py +0 -2
  14. disagreement/ext/app_commands/converters.py +2 -4
  15. disagreement/ext/app_commands/decorators.py +5 -7
  16. disagreement/ext/app_commands/handler.py +1 -3
  17. disagreement/ext/app_commands/hybrid.py +0 -2
  18. disagreement/ext/commands/__init__.py +63 -61
  19. disagreement/ext/commands/cog.py +0 -2
  20. disagreement/ext/commands/converters.py +16 -5
  21. disagreement/ext/commands/core.py +728 -563
  22. disagreement/ext/commands/decorators.py +294 -219
  23. disagreement/ext/commands/errors.py +0 -2
  24. disagreement/ext/commands/help.py +0 -2
  25. disagreement/ext/commands/view.py +1 -3
  26. disagreement/gateway.py +632 -586
  27. disagreement/http.py +1362 -1041
  28. disagreement/interactions.py +0 -2
  29. disagreement/models.py +2682 -2263
  30. disagreement/shard_manager.py +0 -2
  31. disagreement/ui/view.py +167 -165
  32. disagreement/voice_client.py +263 -162
  33. {disagreement-0.2.0rc1.dist-info → disagreement-0.4.0.dist-info}/METADATA +33 -6
  34. disagreement-0.4.0.dist-info/RECORD +55 -0
  35. disagreement-0.2.0rc1.dist-info/RECORD +0 -54
  36. {disagreement-0.2.0rc1.dist-info → disagreement-0.4.0.dist-info}/WHEEL +0 -0
  37. {disagreement-0.2.0rc1.dist-info → disagreement-0.4.0.dist-info}/licenses/LICENSE +0 -0
  38. {disagreement-0.2.0rc1.dist-info → disagreement-0.4.0.dist-info}/top_level.txt +0 -0
@@ -1,5 +1,3 @@
1
- # disagreement/shard_manager.py
2
-
3
1
  """Sharding utilities for managing multiple gateway connections."""
4
2
 
5
3
  from __future__ import annotations
disagreement/ui/view.py CHANGED
@@ -1,165 +1,167 @@
1
- from __future__ import annotations
2
-
3
- import asyncio
4
- import uuid
5
- from typing import Any, Callable, Coroutine, Dict, List, Optional, TYPE_CHECKING
6
-
7
- from ..models import ActionRow
8
- from .item import Item
9
-
10
- if TYPE_CHECKING:
11
- from ..client import Client
12
- from ..interactions import Interaction
13
-
14
-
15
- class View:
16
- """Represents a container for UI components that can be sent with a message.
17
-
18
- Args:
19
- timeout (Optional[float]): The number of seconds to wait for an interaction before the view times out.
20
- Defaults to 180.
21
- """
22
-
23
- def __init__(self, *, timeout: Optional[float] = 180.0):
24
- self.timeout = timeout
25
- self.id = str(uuid.uuid4())
26
- self.__children: List[Item] = []
27
- self.__stopped = asyncio.Event()
28
- self._client: Optional[Client] = None
29
- self._message_id: Optional[str] = None
30
-
31
- for item in self.__class__.__dict__.values():
32
- if isinstance(item, Item):
33
- self.add_item(item)
34
-
35
- @property
36
- def children(self) -> List[Item]:
37
- return self.__children
38
-
39
- def add_item(self, item: Item):
40
- """Adds an item to the view."""
41
- if not isinstance(item, Item):
42
- raise TypeError("Only instances of 'Item' can be added to a View.")
43
-
44
- if len(self.__children) >= 25:
45
- raise ValueError("A view can only have a maximum of 25 components.")
46
-
47
- item._view = self
48
- self.__children.append(item)
49
-
50
- @property
51
- def message_id(self) -> Optional[str]:
52
- return self._message_id
53
-
54
- @message_id.setter
55
- def message_id(self, value: str):
56
- self._message_id = value
57
-
58
- def to_components(self) -> List[ActionRow]:
59
- """Converts the view's children into a list of ActionRow components.
60
-
61
- This retains the original, simple layout behaviour where each item is
62
- placed in its own :class:`ActionRow` to ensure backward compatibility.
63
- """
64
-
65
- rows: List[ActionRow] = []
66
-
67
- for item in self.children:
68
- if item.custom_id is None:
69
- item.custom_id = (
70
- f"{self.id}:{item.__class__.__name__}:{len(self.__children)}"
71
- )
72
-
73
- rows.append(ActionRow(components=[item]))
74
-
75
- return rows
76
-
77
- def layout_components_advanced(self) -> List[ActionRow]:
78
- """Group compatible components into rows following Discord rules."""
79
-
80
- rows: List[ActionRow] = []
81
-
82
- for item in self.children:
83
- if item.custom_id is None:
84
- item.custom_id = (
85
- f"{self.id}:{item.__class__.__name__}:{len(self.__children)}"
86
- )
87
-
88
- target_row = item.row
89
- if target_row is not None:
90
- if not 0 <= target_row <= 4:
91
- raise ValueError("Row index must be between 0 and 4.")
92
-
93
- while len(rows) <= target_row:
94
- if len(rows) >= 5:
95
- raise ValueError("A view can have at most 5 action rows.")
96
- rows.append(ActionRow())
97
-
98
- rows[target_row].add_component(item)
99
- continue
100
-
101
- placed = False
102
- for row in rows:
103
- try:
104
- row.add_component(item)
105
- placed = True
106
- break
107
- except ValueError:
108
- continue
109
-
110
- if not placed:
111
- if len(rows) >= 5:
112
- raise ValueError("A view can have at most 5 action rows.")
113
- new_row = ActionRow([item])
114
- rows.append(new_row)
115
-
116
- return rows
117
-
118
- def to_components_payload(self) -> List[Dict[str, Any]]:
119
- """Converts the view's children into a list of component dictionaries
120
- that can be sent to the Discord API."""
121
- return [row.to_dict() for row in self.to_components()]
122
-
123
- async def _dispatch(self, interaction: Interaction):
124
- """Called by the client to dispatch an interaction to the correct item."""
125
- if self.timeout is not None:
126
- self.__stopped.set() # Reset the timeout on each interaction
127
- self.__stopped.clear()
128
-
129
- if interaction.data:
130
- custom_id = interaction.data.custom_id
131
- for child in self.children:
132
- if child.custom_id == custom_id:
133
- if child.callback:
134
- await child.callback(self, interaction)
135
- break
136
-
137
- async def wait(self) -> bool:
138
- """Waits until the view has stopped interacting."""
139
- return await self.__stopped.wait()
140
-
141
- def stop(self):
142
- """Stops the view from listening to interactions."""
143
- if not self.__stopped.is_set():
144
- self.__stopped.set()
145
-
146
- async def on_timeout(self):
147
- """Called when the view times out."""
148
- pass # User can override this
149
-
150
- async def _start(self, client: Client):
151
- """Starts the view's internal listener."""
152
- self._client = client
153
- if self.timeout is not None:
154
- asyncio.create_task(self._timeout_task())
155
-
156
- async def _timeout_task(self):
157
- """The task that waits for the timeout and then stops the view."""
158
- try:
159
- await asyncio.wait_for(self.wait(), timeout=self.timeout)
160
- except asyncio.TimeoutError:
161
- self.stop()
162
- await self.on_timeout()
163
- if self._client and self._message_id:
164
- # Remove the view from the client's listeners
165
- self._client._views.pop(self._message_id, None)
1
+ from __future__ import annotations
2
+
3
+ import asyncio
4
+ import uuid
5
+ from typing import Any, Callable, Coroutine, Dict, List, Optional, TYPE_CHECKING
6
+
7
+ from ..models import ActionRow
8
+ from .item import Item
9
+
10
+ if TYPE_CHECKING:
11
+ from ..client import Client
12
+ from ..interactions import Interaction
13
+
14
+
15
+ class View:
16
+ """Represents a container for UI components that can be sent with a message.
17
+
18
+ Args:
19
+ timeout (Optional[float]): The number of seconds to wait for an interaction before the view times out.
20
+ Defaults to 180.
21
+ """
22
+
23
+ def __init__(self, *, timeout: Optional[float] = 180.0):
24
+ self.timeout = timeout
25
+ self.id = str(uuid.uuid4())
26
+ self.__children: List[Item] = []
27
+ self.__stopped = asyncio.Event()
28
+ self._client: Optional[Client] = None
29
+ self._message_id: Optional[str] = None
30
+
31
+ # The below is a bit of a hack to support items defined as class members
32
+ # e.g. button = Button(...)
33
+ for item in self.__class__.__dict__.values():
34
+ if isinstance(item, Item):
35
+ self.add_item(item)
36
+
37
+ @property
38
+ def children(self) -> List[Item]:
39
+ return self.__children
40
+
41
+ def add_item(self, item: Item):
42
+ """Adds an item to the view."""
43
+ if not isinstance(item, Item):
44
+ raise TypeError("Only instances of 'Item' can be added to a View.")
45
+
46
+ if len(self.__children) >= 25:
47
+ raise ValueError("A view can only have a maximum of 25 components.")
48
+
49
+ if self.timeout is None and item.custom_id is None:
50
+ raise ValueError(
51
+ "All components in a persistent view must have a 'custom_id'."
52
+ )
53
+
54
+ item._view = self
55
+ self.__children.append(item)
56
+
57
+ @property
58
+ def message_id(self) -> Optional[str]:
59
+ return self._message_id
60
+
61
+ @message_id.setter
62
+ def message_id(self, value: str):
63
+ self._message_id = value
64
+
65
+ def to_components(self) -> List[ActionRow]:
66
+ """Converts the view's children into a list of ActionRow components.
67
+
68
+ This retains the original, simple layout behaviour where each item is
69
+ placed in its own :class:`ActionRow` to ensure backward compatibility.
70
+ """
71
+
72
+ rows: List[ActionRow] = []
73
+
74
+ for item in self.children:
75
+ rows.append(ActionRow(components=[item]))
76
+
77
+ return rows
78
+
79
+ def layout_components_advanced(self) -> List[ActionRow]:
80
+ """Group compatible components into rows following Discord rules."""
81
+
82
+ rows: List[ActionRow] = []
83
+
84
+ for item in self.children:
85
+ if item.custom_id is None:
86
+ item.custom_id = (
87
+ f"{self.id}:{item.__class__.__name__}:{len(self.__children)}"
88
+ )
89
+
90
+ target_row = item.row
91
+ if target_row is not None:
92
+ if not 0 <= target_row <= 4:
93
+ raise ValueError("Row index must be between 0 and 4.")
94
+
95
+ while len(rows) <= target_row:
96
+ if len(rows) >= 5:
97
+ raise ValueError("A view can have at most 5 action rows.")
98
+ rows.append(ActionRow())
99
+
100
+ rows[target_row].add_component(item)
101
+ continue
102
+
103
+ placed = False
104
+ for row in rows:
105
+ try:
106
+ row.add_component(item)
107
+ placed = True
108
+ break
109
+ except ValueError:
110
+ continue
111
+
112
+ if not placed:
113
+ if len(rows) >= 5:
114
+ raise ValueError("A view can have at most 5 action rows.")
115
+ new_row = ActionRow([item])
116
+ rows.append(new_row)
117
+
118
+ return rows
119
+
120
+ def to_components_payload(self) -> List[Dict[str, Any]]:
121
+ """Converts the view's children into a list of component dictionaries
122
+ that can be sent to the Discord API."""
123
+ return [row.to_dict() for row in self.to_components()]
124
+
125
+ async def _dispatch(self, interaction: Interaction):
126
+ """Called by the client to dispatch an interaction to the correct item."""
127
+ if self.timeout is not None:
128
+ self.__stopped.set() # Reset the timeout on each interaction
129
+ self.__stopped.clear()
130
+
131
+ if interaction.data:
132
+ custom_id = interaction.data.custom_id
133
+ for child in self.children:
134
+ if child.custom_id == custom_id:
135
+ if child.callback:
136
+ await child.callback(self, interaction)
137
+ break
138
+
139
+ async def wait(self) -> bool:
140
+ """Waits until the view has stopped interacting."""
141
+ return await self.__stopped.wait()
142
+
143
+ def stop(self):
144
+ """Stops the view from listening to interactions."""
145
+ if not self.__stopped.is_set():
146
+ self.__stopped.set()
147
+
148
+ async def on_timeout(self):
149
+ """Called when the view times out."""
150
+ pass
151
+
152
+ async def _start(self, client: Client):
153
+ """Starts the view's internal listener."""
154
+ self._client = client
155
+ if self.timeout is not None:
156
+ asyncio.create_task(self._timeout_task())
157
+
158
+ async def _timeout_task(self):
159
+ """The task that waits for the timeout and then stops the view."""
160
+ try:
161
+ await asyncio.wait_for(self.wait(), timeout=self.timeout)
162
+ except asyncio.TimeoutError:
163
+ self.stop()
164
+ await self.on_timeout()
165
+ if self._client and self._message_id:
166
+ # Remove the view from the client's listeners
167
+ self._client._views.pop(self._message_id, None)