disagreement 0.1.0rc3__py3-none-any.whl → 0.3.0b1__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.
- disagreement/__init__.py +2 -2
- disagreement/audio.py +17 -0
- disagreement/cache.py +31 -1
- disagreement/caching.py +120 -0
- disagreement/client.py +1658 -1349
- disagreement/color.py +100 -1
- disagreement/enums.py +63 -0
- disagreement/event_dispatcher.py +2 -2
- disagreement/ext/app_commands/commands.py +0 -2
- disagreement/ext/app_commands/handler.py +101 -30
- disagreement/ext/commands/__init__.py +65 -57
- disagreement/ext/commands/core.py +690 -513
- disagreement/ext/commands/decorators.py +298 -192
- disagreement/ext/commands/errors.py +8 -0
- disagreement/gateway.py +630 -575
- disagreement/http.py +1120 -875
- disagreement/models.py +2588 -2064
- disagreement/ui/view.py +167 -165
- disagreement/voice_client.py +244 -162
- {disagreement-0.1.0rc3.dist-info → disagreement-0.3.0b1.dist-info}/METADATA +18 -1
- {disagreement-0.1.0rc3.dist-info → disagreement-0.3.0b1.dist-info}/RECORD +24 -23
- {disagreement-0.1.0rc3.dist-info → disagreement-0.3.0b1.dist-info}/WHEEL +0 -0
- {disagreement-0.1.0rc3.dist-info → disagreement-0.3.0b1.dist-info}/licenses/LICENSE +0 -0
- {disagreement-0.1.0rc3.dist-info → disagreement-0.3.0b1.dist-info}/top_level.txt +0 -0
disagreement/voice_client.py
CHANGED
@@ -1,162 +1,244 @@
|
|
1
|
-
# disagreement/voice_client.py
|
2
|
-
"""Voice gateway and UDP audio client."""
|
3
|
-
|
4
|
-
from __future__ import annotations
|
5
|
-
|
6
|
-
import asyncio
|
7
|
-
import contextlib
|
8
|
-
import socket
|
9
|
-
|
10
|
-
|
11
|
-
|
12
|
-
|
13
|
-
|
14
|
-
|
15
|
-
|
16
|
-
|
17
|
-
|
18
|
-
|
19
|
-
|
20
|
-
|
21
|
-
|
22
|
-
|
23
|
-
|
24
|
-
|
25
|
-
|
26
|
-
|
27
|
-
|
28
|
-
|
29
|
-
|
30
|
-
|
31
|
-
|
32
|
-
|
33
|
-
|
34
|
-
|
35
|
-
|
36
|
-
|
37
|
-
|
38
|
-
|
39
|
-
|
40
|
-
|
41
|
-
self.
|
42
|
-
self.
|
43
|
-
self.
|
44
|
-
self.
|
45
|
-
self.
|
46
|
-
self.
|
47
|
-
self.
|
48
|
-
self.
|
49
|
-
self.
|
50
|
-
|
51
|
-
|
52
|
-
|
53
|
-
|
54
|
-
|
55
|
-
|
56
|
-
|
57
|
-
|
58
|
-
|
59
|
-
|
60
|
-
|
61
|
-
|
62
|
-
|
63
|
-
|
64
|
-
|
65
|
-
|
66
|
-
|
67
|
-
|
68
|
-
|
69
|
-
|
70
|
-
|
71
|
-
|
72
|
-
|
73
|
-
|
74
|
-
|
75
|
-
|
76
|
-
self.
|
77
|
-
|
78
|
-
|
79
|
-
|
80
|
-
|
81
|
-
|
82
|
-
|
83
|
-
|
84
|
-
|
85
|
-
|
86
|
-
"
|
87
|
-
|
88
|
-
|
89
|
-
|
90
|
-
|
91
|
-
|
92
|
-
|
93
|
-
|
94
|
-
|
95
|
-
|
96
|
-
|
97
|
-
self.
|
98
|
-
|
99
|
-
|
100
|
-
|
101
|
-
|
102
|
-
|
103
|
-
|
104
|
-
|
105
|
-
|
106
|
-
|
107
|
-
|
108
|
-
|
109
|
-
|
110
|
-
|
111
|
-
|
112
|
-
|
113
|
-
|
114
|
-
|
115
|
-
|
116
|
-
|
117
|
-
|
118
|
-
|
119
|
-
|
120
|
-
|
121
|
-
|
122
|
-
|
123
|
-
|
124
|
-
|
125
|
-
|
126
|
-
|
127
|
-
|
128
|
-
|
129
|
-
|
130
|
-
|
131
|
-
|
132
|
-
|
133
|
-
|
134
|
-
|
135
|
-
|
136
|
-
|
137
|
-
|
138
|
-
|
139
|
-
|
140
|
-
|
141
|
-
|
142
|
-
|
143
|
-
|
144
|
-
|
145
|
-
|
146
|
-
|
147
|
-
|
148
|
-
|
149
|
-
|
150
|
-
|
151
|
-
|
152
|
-
|
153
|
-
|
154
|
-
|
155
|
-
|
156
|
-
|
157
|
-
|
158
|
-
|
159
|
-
|
160
|
-
|
161
|
-
|
162
|
-
|
1
|
+
# disagreement/voice_client.py
|
2
|
+
"""Voice gateway and UDP audio client."""
|
3
|
+
|
4
|
+
from __future__ import annotations
|
5
|
+
|
6
|
+
import asyncio
|
7
|
+
import contextlib
|
8
|
+
import socket
|
9
|
+
import threading
|
10
|
+
from typing import TYPE_CHECKING, Optional, Sequence
|
11
|
+
|
12
|
+
import aiohttp
|
13
|
+
# The following import is correct, but may be flagged by Pylance if the virtual
|
14
|
+
# environment is not configured correctly.
|
15
|
+
from nacl.secret import SecretBox
|
16
|
+
|
17
|
+
from .audio import AudioSink, AudioSource, FFmpegAudioSource
|
18
|
+
from .models import User
|
19
|
+
|
20
|
+
if TYPE_CHECKING:
|
21
|
+
from .client import Client
|
22
|
+
|
23
|
+
|
24
|
+
class VoiceClient:
|
25
|
+
"""Handles the Discord voice WebSocket connection and UDP streaming."""
|
26
|
+
|
27
|
+
def __init__(
|
28
|
+
self,
|
29
|
+
client: Client,
|
30
|
+
endpoint: str,
|
31
|
+
session_id: str,
|
32
|
+
token: str,
|
33
|
+
guild_id: int,
|
34
|
+
user_id: int,
|
35
|
+
*,
|
36
|
+
ws=None,
|
37
|
+
udp: Optional[socket.socket] = None,
|
38
|
+
loop: Optional[asyncio.AbstractEventLoop] = None,
|
39
|
+
verbose: bool = False,
|
40
|
+
) -> None:
|
41
|
+
self.client = client
|
42
|
+
self.endpoint = endpoint
|
43
|
+
self.session_id = session_id
|
44
|
+
self.token = token
|
45
|
+
self.guild_id = str(guild_id)
|
46
|
+
self.user_id = str(user_id)
|
47
|
+
self._ws: Optional[aiohttp.ClientWebSocketResponse] = ws
|
48
|
+
self._udp = udp
|
49
|
+
self._session: Optional[aiohttp.ClientSession] = None
|
50
|
+
self._heartbeat_task: Optional[asyncio.Task] = None
|
51
|
+
self._receive_task: Optional[asyncio.Task] = None
|
52
|
+
self._udp_receive_thread: Optional[threading.Thread] = None
|
53
|
+
self._heartbeat_interval: Optional[float] = None
|
54
|
+
try:
|
55
|
+
self._loop = loop or asyncio.get_running_loop()
|
56
|
+
except RuntimeError:
|
57
|
+
self._loop = asyncio.new_event_loop()
|
58
|
+
asyncio.set_event_loop(self._loop)
|
59
|
+
self.verbose = verbose
|
60
|
+
self.ssrc: Optional[int] = None
|
61
|
+
self.secret_key: Optional[Sequence[int]] = None
|
62
|
+
self._server_ip: Optional[str] = None
|
63
|
+
self._server_port: Optional[int] = None
|
64
|
+
self._current_source: Optional[AudioSource] = None
|
65
|
+
self._play_task: Optional[asyncio.Task] = None
|
66
|
+
self._sink: Optional[AudioSink] = None
|
67
|
+
self._ssrc_map: dict[int, int] = {}
|
68
|
+
self._ssrc_lock = threading.Lock()
|
69
|
+
|
70
|
+
async def connect(self) -> None:
|
71
|
+
if self._ws is None:
|
72
|
+
self._session = aiohttp.ClientSession()
|
73
|
+
self._ws = await self._session.ws_connect(self.endpoint)
|
74
|
+
|
75
|
+
hello = await self._ws.receive_json()
|
76
|
+
self._heartbeat_interval = hello["d"]["heartbeat_interval"] / 1000
|
77
|
+
self._heartbeat_task = self._loop.create_task(self._heartbeat())
|
78
|
+
|
79
|
+
await self._ws.send_json(
|
80
|
+
{
|
81
|
+
"op": 0,
|
82
|
+
"d": {
|
83
|
+
"server_id": self.guild_id,
|
84
|
+
"user_id": self.user_id,
|
85
|
+
"session_id": self.session_id,
|
86
|
+
"token": self.token,
|
87
|
+
},
|
88
|
+
}
|
89
|
+
)
|
90
|
+
|
91
|
+
ready = await self._ws.receive_json()
|
92
|
+
data = ready["d"]
|
93
|
+
self.ssrc = data["ssrc"]
|
94
|
+
self._server_ip = data["ip"]
|
95
|
+
self._server_port = data["port"]
|
96
|
+
|
97
|
+
if self._udp is None:
|
98
|
+
self._udp = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
|
99
|
+
self._udp.connect((self._server_ip, self._server_port))
|
100
|
+
|
101
|
+
await self._ws.send_json(
|
102
|
+
{
|
103
|
+
"op": 1,
|
104
|
+
"d": {
|
105
|
+
"protocol": "udp",
|
106
|
+
"data": {
|
107
|
+
"address": self._udp.getsockname()[0],
|
108
|
+
"port": self._udp.getsockname()[1],
|
109
|
+
"mode": "xsalsa20_poly1305",
|
110
|
+
},
|
111
|
+
},
|
112
|
+
}
|
113
|
+
)
|
114
|
+
|
115
|
+
session_desc = await self._ws.receive_json()
|
116
|
+
self.secret_key = session_desc["d"].get("secret_key")
|
117
|
+
|
118
|
+
async def _heartbeat(self) -> None:
|
119
|
+
assert self._ws is not None
|
120
|
+
assert self._heartbeat_interval is not None
|
121
|
+
try:
|
122
|
+
while True:
|
123
|
+
await self._ws.send_json({"op": 3, "d": int(self._loop.time() * 1000)})
|
124
|
+
await asyncio.sleep(self._heartbeat_interval)
|
125
|
+
except asyncio.CancelledError:
|
126
|
+
pass
|
127
|
+
|
128
|
+
async def _receive_loop(self) -> None:
|
129
|
+
assert self._ws is not None
|
130
|
+
while True:
|
131
|
+
try:
|
132
|
+
msg = await self._ws.receive_json()
|
133
|
+
op = msg.get("op")
|
134
|
+
data = msg.get("d")
|
135
|
+
if op == 5: # Speaking
|
136
|
+
user_id = int(data["user_id"])
|
137
|
+
ssrc = data["ssrc"]
|
138
|
+
with self._ssrc_lock:
|
139
|
+
self._ssrc_map[ssrc] = user_id
|
140
|
+
except (asyncio.CancelledError, aiohttp.ClientError):
|
141
|
+
break
|
142
|
+
|
143
|
+
def _udp_receive_loop(self) -> None:
|
144
|
+
assert self._udp is not None
|
145
|
+
assert self.secret_key is not None
|
146
|
+
box = SecretBox(bytes(self.secret_key))
|
147
|
+
while True:
|
148
|
+
try:
|
149
|
+
packet = self._udp.recv(4096)
|
150
|
+
if len(packet) < 12:
|
151
|
+
continue
|
152
|
+
|
153
|
+
ssrc = int.from_bytes(packet[8:12], "big")
|
154
|
+
with self._ssrc_lock:
|
155
|
+
if ssrc not in self._ssrc_map:
|
156
|
+
continue
|
157
|
+
user_id = self._ssrc_map[ssrc]
|
158
|
+
user = self.client._users.get(str(user_id))
|
159
|
+
if not user:
|
160
|
+
continue
|
161
|
+
|
162
|
+
decrypted = box.decrypt(packet[12:])
|
163
|
+
if self._sink:
|
164
|
+
self._sink.write(user, decrypted)
|
165
|
+
except (socket.error, asyncio.CancelledError):
|
166
|
+
break
|
167
|
+
except Exception as e:
|
168
|
+
if self.verbose:
|
169
|
+
print(f"Error in UDP receive loop: {e}")
|
170
|
+
|
171
|
+
async def send_audio_frame(self, frame: bytes) -> None:
|
172
|
+
if not self._udp:
|
173
|
+
raise RuntimeError("UDP socket not initialised")
|
174
|
+
self._udp.send(frame)
|
175
|
+
|
176
|
+
async def _play_loop(self) -> None:
|
177
|
+
assert self._current_source is not None
|
178
|
+
try:
|
179
|
+
while True:
|
180
|
+
data = await self._current_source.read()
|
181
|
+
if not data:
|
182
|
+
break
|
183
|
+
await self.send_audio_frame(data)
|
184
|
+
finally:
|
185
|
+
await self._current_source.close()
|
186
|
+
self._current_source = None
|
187
|
+
self._play_task = None
|
188
|
+
|
189
|
+
async def stop(self) -> None:
|
190
|
+
if self._play_task:
|
191
|
+
self._play_task.cancel()
|
192
|
+
with contextlib.suppress(asyncio.CancelledError):
|
193
|
+
await self._play_task
|
194
|
+
self._play_task = None
|
195
|
+
if self._current_source:
|
196
|
+
await self._current_source.close()
|
197
|
+
self._current_source = None
|
198
|
+
|
199
|
+
async def play(self, source: AudioSource, *, wait: bool = True) -> None:
|
200
|
+
"""|coro| Play an :class:`AudioSource` on the voice connection."""
|
201
|
+
|
202
|
+
await self.stop()
|
203
|
+
self._current_source = source
|
204
|
+
self._play_task = self._loop.create_task(self._play_loop())
|
205
|
+
if wait:
|
206
|
+
await self._play_task
|
207
|
+
|
208
|
+
async def play_file(self, filename: str, *, wait: bool = True) -> None:
|
209
|
+
"""|coro| Stream an audio file or URL using FFmpeg."""
|
210
|
+
|
211
|
+
await self.play(FFmpegAudioSource(filename), wait=wait)
|
212
|
+
|
213
|
+
def listen(self, sink: AudioSink) -> None:
|
214
|
+
"""Start listening to voice and routing to a sink."""
|
215
|
+
if not isinstance(sink, AudioSink):
|
216
|
+
raise TypeError("sink must be an AudioSink instance")
|
217
|
+
|
218
|
+
self._sink = sink
|
219
|
+
if not self._udp_receive_thread:
|
220
|
+
self._udp_receive_thread = threading.Thread(
|
221
|
+
target=self._udp_receive_loop, daemon=True
|
222
|
+
)
|
223
|
+
self._udp_receive_thread.start()
|
224
|
+
|
225
|
+
async def close(self) -> None:
|
226
|
+
await self.stop()
|
227
|
+
if self._heartbeat_task:
|
228
|
+
self._heartbeat_task.cancel()
|
229
|
+
with contextlib.suppress(asyncio.CancelledError):
|
230
|
+
await self._heartbeat_task
|
231
|
+
if self._receive_task:
|
232
|
+
self._receive_task.cancel()
|
233
|
+
with contextlib.suppress(asyncio.CancelledError):
|
234
|
+
await self._receive_task
|
235
|
+
if self._ws:
|
236
|
+
await self._ws.close()
|
237
|
+
if self._session:
|
238
|
+
await self._session.close()
|
239
|
+
if self._udp:
|
240
|
+
self._udp.close()
|
241
|
+
if self._udp_receive_thread:
|
242
|
+
self._udp_receive_thread.join(timeout=1)
|
243
|
+
if self._sink:
|
244
|
+
self._sink.close()
|
@@ -1,6 +1,6 @@
|
|
1
1
|
Metadata-Version: 2.4
|
2
2
|
Name: disagreement
|
3
|
-
Version: 0.
|
3
|
+
Version: 0.3.0b1
|
4
4
|
Summary: A Python library for the Discord API.
|
5
5
|
Author-email: Slipstream <me@slipstreamm.dev>
|
6
6
|
License: BSD 3-Clause
|
@@ -23,6 +23,7 @@ Requires-Python: >=3.10
|
|
23
23
|
Description-Content-Type: text/markdown
|
24
24
|
License-File: LICENSE
|
25
25
|
Requires-Dist: aiohttp<4.0.0,>=3.9.0
|
26
|
+
Requires-Dist: PyNaCl<2.0.0,>=1.5.0
|
26
27
|
Provides-Extra: test
|
27
28
|
Requires-Dist: pytest>=8.0.0; extra == "test"
|
28
29
|
Requires-Dist: pytest-asyncio>=1.0.0; extra == "test"
|
@@ -119,6 +120,22 @@ setup_logging(logging.INFO)
|
|
119
120
|
setup_logging(logging.DEBUG, file="bot.log")
|
120
121
|
```
|
121
122
|
|
123
|
+
### HTTP Session Options
|
124
|
+
|
125
|
+
Pass additional keyword arguments to ``aiohttp.ClientSession`` using the
|
126
|
+
``http_options`` parameter when constructing :class:`disagreement.Client`:
|
127
|
+
|
128
|
+
```python
|
129
|
+
client = disagreement.Client(
|
130
|
+
token=token,
|
131
|
+
http_options={"proxy": "http://localhost:8080"},
|
132
|
+
)
|
133
|
+
```
|
134
|
+
|
135
|
+
These options are forwarded to ``HTTPClient`` when it creates the underlying
|
136
|
+
``aiohttp.ClientSession``. You can specify a custom ``connector`` or any other
|
137
|
+
session parameter supported by ``aiohttp``.
|
138
|
+
|
122
139
|
### Defining Subcommands with `AppCommandGroup`
|
123
140
|
|
124
141
|
```python
|
@@ -1,20 +1,21 @@
|
|
1
|
-
disagreement/__init__.py,sha256=
|
2
|
-
disagreement/audio.py,sha256=
|
3
|
-
disagreement/cache.py,sha256=
|
4
|
-
disagreement/
|
5
|
-
disagreement/
|
1
|
+
disagreement/__init__.py,sha256=byWcO3yzDBdtlF2I-7f72JVlNnwQnbu88kr2PUeKIdY,1183
|
2
|
+
disagreement/audio.py,sha256=nJ2A8glYVZwH5HgsqxOwqCgkOuWi0-bmgccPaV5K8-Y,3739
|
3
|
+
disagreement/cache.py,sha256=2BACo4s2n4TyJejgNlheWjFOT6AWJHweHhO_LPhwkdI,2505
|
4
|
+
disagreement/caching.py,sha256=_AKdhdWYOhMw2r6u0rVpdoU9_fwjzzDlxhTs73tgUXg,3839
|
5
|
+
disagreement/client.py,sha256=e72n9zgOUWGCjfSB0zIhFQ2WvOJijWzKrxrPRtpL7Gc,65682
|
6
|
+
disagreement/color.py,sha256=0RumZU9geS51rmmywwotmkXFc8RyQghOviRGGrHmvW4,4495
|
6
7
|
disagreement/components.py,sha256=tEYJ2RHVpIFtZuPPxZ0v8ssUw_x7ybhYBzHNsRiXXvU,5250
|
7
|
-
disagreement/enums.py,sha256=
|
8
|
+
disagreement/enums.py,sha256=Km9rzmbkYdBpba3fDAv9YYtXDROoRpKuQfkMavsiY0s,11069
|
8
9
|
disagreement/error_handler.py,sha256=c2lb6aTMnhTtITQuR6axZUtEaasYKUgmdSxAHEkeq50,1028
|
9
10
|
disagreement/errors.py,sha256=XiYVPy8uFUGVi_EIf81yK7QbC7KyN4JHplSJSWw2RRk,3185
|
10
|
-
disagreement/event_dispatcher.py,sha256=
|
11
|
-
disagreement/gateway.py,sha256=
|
12
|
-
disagreement/http.py,sha256=
|
11
|
+
disagreement/event_dispatcher.py,sha256=gdT5n9JzuWH6MXMvc4ZcoeyoSMJ9v973xC5rpufaTeY,11479
|
12
|
+
disagreement/gateway.py,sha256=Orub9imXCvq7zKqBZqYxozMaI45B23SjCDaeYzB2SHI,26920
|
13
|
+
disagreement/http.py,sha256=zA3I9QUMHNjvIxDUiAeCVFaMUyn1QdERfJ6dsxQr2A0,40864
|
13
14
|
disagreement/hybrid_context.py,sha256=VYCmcreTZdPBU9v-Cy48W38vgWO2t8nM2ulC6_z4HjU,1095
|
14
15
|
disagreement/i18n.py,sha256=1L4rcFuKP0XjHk9dVwbNh4BkLk2ZlxxZ_-tecGWa9S0,718
|
15
16
|
disagreement/interactions.py,sha256=aUZwwEOLsEds15i6G-rxmSSDCDmaxz_cfoTYS4tv6Ao,21735
|
16
17
|
disagreement/logging_config.py,sha256=4q6baQPE6X_0lfaBTFMU1uqc03x5SbJqo2hsApdDFac,686
|
17
|
-
disagreement/models.py,sha256=
|
18
|
+
disagreement/models.py,sha256=m88dlN8FrCZclb8p_lW6okljAVAnFib7-pkh1yKl1fk,95201
|
18
19
|
disagreement/oauth.py,sha256=TfDdCwg1J7osM9wDi61dtNBA5BrQk5DeQrrHsYycH34,2810
|
19
20
|
disagreement/permissions.py,sha256=7g5cIlg-evHXOL0-pmtT5EwqcB-stXot1HZSLz724sE,3008
|
20
21
|
disagreement/py.typed,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
@@ -22,23 +23,23 @@ disagreement/rate_limiter.py,sha256=ubwR_UTPs2MotipBdtqpgwQKx0IHt2I3cdfFcXTFv7g,
|
|
22
23
|
disagreement/shard_manager.py,sha256=e9F8tx_4IEOlTX3-S3t51lfJToc6Ue3RVBzoNAiVKxs,2161
|
23
24
|
disagreement/typing.py,sha256=_1oFWfZ4HyH5Q3bnF7CO24s79z-3_B5Qb69kWiwLhhU,1242
|
24
25
|
disagreement/utils.py,sha256=mz7foTCOAmUv9n8EcdZeiFarwqB14xHOG8o0p8tFuKA,2014
|
25
|
-
disagreement/voice_client.py,sha256=
|
26
|
+
disagreement/voice_client.py,sha256=ygle5grurpSKF-0y5YG6vwF7Ise5Pf1uNjz55mr6Y_4,8717
|
26
27
|
disagreement/ext/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
27
28
|
disagreement/ext/loader.py,sha256=9_uULvNAa-a6UiaeQhWglwgIrHEPKbf9bnWtSL1KV5Q,1408
|
28
29
|
disagreement/ext/tasks.py,sha256=b14KI-btikbrjPlD76md3Ggt6znrxPqr7TDarU4PYBg,7269
|
29
30
|
disagreement/ext/app_commands/__init__.py,sha256=mnQLIuGP9SzqGMPEn5YgOh2eIU7lcYoDXP06vtXZfTA,1014
|
30
|
-
disagreement/ext/app_commands/commands.py,sha256=
|
31
|
+
disagreement/ext/app_commands/commands.py,sha256=xjrVPScEezdVxVq524_E2Gm2sTa-yq44TbGMu0gyA2o,19018
|
31
32
|
disagreement/ext/app_commands/context.py,sha256=Xcm4Ka5K5uTQGviixF5LeCDdOdF9YQS5F7lZi2m--8s,20831
|
32
33
|
disagreement/ext/app_commands/converters.py,sha256=J1VEmo-7H9K7kGPJodu5FX4RmFFI1BuzhlQAEs2MsD4,21036
|
33
34
|
disagreement/ext/app_commands/decorators.py,sha256=dKiD4ZEsafRoPvfgn9zuQ9vvXXo2qYTMquHvyUM1604,23251
|
34
|
-
disagreement/ext/app_commands/handler.py,sha256=
|
35
|
+
disagreement/ext/app_commands/handler.py,sha256=KCMi5NEusuyLYo7Vk4sqLqXAJI5r3ppI0MNLUh0kU2c,28781
|
35
36
|
disagreement/ext/app_commands/hybrid.py,sha256=u3kHNbVfY3-liymgzEIkFO5YV3WM_DqwytzdN9EXWMY,3330
|
36
|
-
disagreement/ext/commands/__init__.py,sha256=
|
37
|
+
disagreement/ext/commands/__init__.py,sha256=Zko_qVPz1WUjBLcR2bnS3mKMX_YmU4AZ8kuFw9bvRRg,1363
|
37
38
|
disagreement/ext/commands/cog.py,sha256=-F2ZOXXC07r96xlt9NomRgqlIqlcxzBiha2Zhg1DVp4,6845
|
38
39
|
disagreement/ext/commands/converters.py,sha256=mh8xJr1FIiah6bdYy0KsdccfYcPii2Yc_IdhzCTw5uE,5864
|
39
|
-
disagreement/ext/commands/core.py,sha256=
|
40
|
-
disagreement/ext/commands/decorators.py,sha256=
|
41
|
-
disagreement/ext/commands/errors.py,sha256=
|
40
|
+
disagreement/ext/commands/core.py,sha256=l6-rAAzeVERASyKd-qp2ttHlt5YgzuFwHTR4_JtXZnw,27053
|
41
|
+
disagreement/ext/commands/decorators.py,sha256=BdhfiTsetu-oOIZn7gwUu2YDAmlzio7ijYFvov7m9pA,10469
|
42
|
+
disagreement/ext/commands/errors.py,sha256=L6losXKye62BqDl42fXxgkuAkG92W-OcqH9uwEgabb8,2301
|
42
43
|
disagreement/ext/commands/help.py,sha256=yw0ydupOsPwmnhsIIoxa93xjj9MAcBcGfD8BXa7V8G8,1456
|
43
44
|
disagreement/ext/commands/view.py,sha256=3Wo4gGJX9fb65qw8yHFwMjnAeJvMJAx19rZNHz-ZDUs,3315
|
44
45
|
disagreement/ui/__init__.py,sha256=PLA6eHiq9cu7JDOKS-7MKtaFhlqswjbI4AEUlpnbgO0,307
|
@@ -46,9 +47,9 @@ disagreement/ui/button.py,sha256=GHbY3-yMrvv6u674-qYONocuC1e2a0flEWjPKwJXKDo,316
|
|
46
47
|
disagreement/ui/item.py,sha256=bm-EmQEZpe8Kt8JrRw-o0uQdccgjErORcFsBqaXOcV8,1112
|
47
48
|
disagreement/ui/modal.py,sha256=w0ZEVslXzx2-RWUP4jdVB54zDuT81jpueVWZ70byFnI,4137
|
48
49
|
disagreement/ui/select.py,sha256=XjWRlWkA09QZaDDLn-wDDOWIuj0Mb4VCWJEOAaExZXw,3018
|
49
|
-
disagreement/ui/view.py,sha256=
|
50
|
-
disagreement-0.
|
51
|
-
disagreement-0.
|
52
|
-
disagreement-0.
|
53
|
-
disagreement-0.
|
54
|
-
disagreement-0.
|
50
|
+
disagreement/ui/view.py,sha256=bmDcX-YG-_exTVTfvMCDozo4D1vRQNFlXc8Ap21DVKE,5859
|
51
|
+
disagreement-0.3.0b1.dist-info/licenses/LICENSE,sha256=zfmtgJhVFHnqT7a8LAQFthXu5bP7EEHmEL99trV66Ew,1474
|
52
|
+
disagreement-0.3.0b1.dist-info/METADATA,sha256=NRhQzjjYG7vcD7X9RQYr_alVA4SnCQJvJbf8Sz1J0Tc,5417
|
53
|
+
disagreement-0.3.0b1.dist-info/WHEEL,sha256=_zCd3N1l69ArxyTb8rzEoP9TpbYXkqRFSNOD5OuxnTs,91
|
54
|
+
disagreement-0.3.0b1.dist-info/top_level.txt,sha256=t7FY_3iaYhdB6X6y9VybJ2j7UZbVeRUe9wRgH8d5Gtk,13
|
55
|
+
disagreement-0.3.0b1.dist-info/RECORD,,
|
File without changes
|
File without changes
|
File without changes
|