|
25 | 25 |
|
26 | 26 | from __future__ import annotations
|
27 | 27 |
|
28 |
| -from typing import TYPE_CHECKING, Generic, TypeVar |
| 28 | +from collections.abc import Awaitable, Callable |
| 29 | +from typing import TYPE_CHECKING, Any, Generic, TypeVar, Union |
29 | 30 |
|
30 | 31 | if TYPE_CHECKING:
|
| 32 | + from typing_extensions import ParamSpec |
| 33 | + |
31 | 34 | from discord import abc
|
32 | 35 | from discord.client import Client
|
33 | 36 | from discord.raw_models import (
|
34 | 37 | RawVoiceServerUpdateEvent,
|
35 | 38 | RawVoiceStateUpdateEvent,
|
36 | 39 | )
|
37 |
| - from discord.voice.client import VoiceClient |
| 40 | + from discord.sinks import Sink |
| 41 | + |
| 42 | + P = ParamSpec('P') |
| 43 | + R = TypeVar('R') |
| 44 | + RecordCallback = Union[Callable[P, R], Callable[P, Awaitable[R]]] |
38 | 45 |
|
39 | 46 | ClientT = TypeVar("ClientT", bound="Client", covariant=True)
|
40 |
| -VoiceClientT = TypeVar('VoiceClientT', bound='VoiceClient', covariant=True) |
| 47 | +VoiceProtocolT = TypeVar('VoiceProtocolT', bound='VoiceProtocol', covariant=True) |
41 | 48 |
|
42 | 49 |
|
43 | 50 | class VoiceProtocol(Generic[ClientT]):
|
44 | 51 | """A class that represents the Discord voice protocol.
|
45 | 52 |
|
46 | 53 | .. warning::
|
47 | 54 |
|
48 |
| - If you are a end user, you **should not construct this manually** but instead |
| 55 | + If you are an end user, you **should not construct this manually** but instead |
49 | 56 | take it from the return type in :meth:`abc.Connectable.connect <VoiceChannel.connect>`.
|
50 | 57 | The parameters and methods being documented here is so third party libraries can refer to it
|
51 | 58 | when implementing their own VoiceProtocol types.
|
@@ -161,4 +168,99 @@ def cleanup(self) -> None:
|
161 | 168 | self.client._connection._remove_voice_client(key)
|
162 | 169 |
|
163 | 170 |
|
164 |
| -class RecorderProtocol(Generic[VoiceClientT]): |
| 171 | +class VoiceRecorderProtocol(Generic[VoiceProtocolT]): |
| 172 | + """A class that represents a Discord voice client recorder protocol. |
| 173 | +
|
| 174 | + .. warning:: |
| 175 | +
|
| 176 | + If you are an end user, you **should not construct this manually** but instead |
| 177 | + take it from a :class:`VoiceProtocol` implementation, like :attr:`VoiceClient.recorder`. |
| 178 | + The parameters and methods being documented here is so third party libraries can refer to it |
| 179 | + when implementing their own RecorderProtocol types. |
| 180 | +
|
| 181 | + This is an abstract class. The library provides a concrete implementation under |
| 182 | + :class:`VoiceRecorderClient`. |
| 183 | +
|
| 184 | + This class allows you to implement a protocol to allow for an external |
| 185 | + method of receiving and handling voice data. |
| 186 | +
|
| 187 | + .. versionadded:: 2.7 |
| 188 | +
|
| 189 | + Parameters |
| 190 | + ---------- |
| 191 | + client: :class:`VoiceProtocol` |
| 192 | + The voice client (or its subclasses) that are bound to this recorder. |
| 193 | + channel: :class:`abc.Connectable` |
| 194 | + The voice channel that is being recorder. If not provided, defaults to |
| 195 | + :attr:`VoiceProtocol.channel` |
| 196 | + """ |
| 197 | + |
| 198 | + def __init__(self, client: VoiceProtocolT, channel: abc.Connectable | None = None) -> None: |
| 199 | + self.client: VoiceProtocolT = client |
| 200 | + self.channel: abc.Connectable = channel or client.channel |
| 201 | + |
| 202 | + def get_ssrc(self, user_id: int) -> int: |
| 203 | + """Gets the ssrc of a user. |
| 204 | +
|
| 205 | + Parameters |
| 206 | + ---------- |
| 207 | + user_id: :class:`int` |
| 208 | + The user ID to get the ssrc from. |
| 209 | +
|
| 210 | + Returns |
| 211 | + ------- |
| 212 | + :class:`int` |
| 213 | + The ssrc for the provided user ID. |
| 214 | + """ |
| 215 | + raise NotImplementedError('subclasses must implement this') |
| 216 | + |
| 217 | + def unpack(self, data: bytes) -> bytes | None: |
| 218 | + """Takes an audio packet received from Discord and decodes it. |
| 219 | +
|
| 220 | + Parameters |
| 221 | + ---------- |
| 222 | + data: :class:`bytes` |
| 223 | + The bytes received by Discord. |
| 224 | +
|
| 225 | + Returns |
| 226 | + ------- |
| 227 | + Optional[:class:`bytes`] |
| 228 | + The unpacked bytes, or ``None`` if they could not be unpacked. |
| 229 | + """ |
| 230 | + raise NotImplementedError('subclasses must implement this') |
| 231 | + |
| 232 | + def record( |
| 233 | + self, |
| 234 | + sink: Sink, |
| 235 | + callback: RecordCallback[P, R], |
| 236 | + sync_start: bool, |
| 237 | + *callback_args: P.args, |
| 238 | + **callback_kwargs: P.kwargs, |
| 239 | + ) -> None: |
| 240 | + """Start recording audio from the current voice channel in the provided sink. |
| 241 | +
|
| 242 | + You must be in a voice channel. |
| 243 | +
|
| 244 | + Parameters |
| 245 | + ---------- |
| 246 | + sink: :class:`~discord.Sink` |
| 247 | + The sink to record to. |
| 248 | + callback: Callable[..., Any] |
| 249 | + The function called after the bot has stopped recording. This can take any arguments and |
| 250 | + can return an awaitable. |
| 251 | + sync_start: :class:`bool` |
| 252 | + Whether the subsequent recording users will start with silence. This is useful for recording |
| 253 | + audio just as it was heard. |
| 254 | +
|
| 255 | + Raises |
| 256 | + ------ |
| 257 | + RecordingException |
| 258 | + Not connected to a voice channel |
| 259 | + TypeError |
| 260 | + You did not pass a Sink object. |
| 261 | + """ |
| 262 | + raise NotImplementedError('subclasses must implement this') |
| 263 | + |
| 264 | + def stop(self) -> None: |
| 265 | + """Stops recording.""" |
| 266 | + raise NotImplementedError('subclasses must implement this') |
0 commit comments