bostie.voice¶
bostie.voice
¶
ClosedReason
¶
Bases: StrEnum
Why a receiver ended: closed, disconnected, moved or began reconnecting.
Direction
¶
DisconnectReason
¶
EndReason
¶
Bases: StrEnum
How a track ended: it finished, was stopped, failed, or its connection ended.
SpeakingFlags
¶
Bases: DiscordIntFlag
AlreadyConnected
¶
Bases: VoiceError, StateError
bostie already runs a voice connection in this guild.
BufferFull
¶
Bases: VoiceError
The sink has no room for this write.
DaveError
¶
Bases: VoiceError
A DAVE end-to-end encryption step failed.
Empty
¶
Bases: VoiceError
Nothing is queued in the receiver.
NotInVoice
¶
Bases: VoiceError
The user is not in a voice channel of this guild.
ReceiverClosed
¶
Bases: VoiceError
The receiver ended and has nothing left to read.
SinkClosed
¶
Bases: VoiceError
The sink is closed.
SourceError
¶
Bases: VoiceError
A source failed to open, decode or seek.
VoiceConnectError
¶
VoiceConnectError(reason: DisconnectReason, code: int | None, detail: str)
Bases: VoiceError
A voice connection ended before it was ready.
VoiceError
¶
Bases: BostieError
Base class of voice errors.
VoiceNotConnected
¶
Bases: VoiceError
The voice connection has ended.
VoiceQueueFull
¶
Bases: VoiceError
The voice connection has too many commands waiting.
ContextVoice
¶
ContextVoice(manager: VoiceManager, guild_id: int | None, user_id: int)
Voice for the guild and the user of an interaction.
connect
async
¶
connect(*, self_deaf: bool | None = None, self_mute: bool | None = None, timeout: float | None = None, server_change_delay: float | None = None, encoder: EncoderSettings | None = None, speaking_flags: SpeakingFlags | None = None, stage: StageRole | None = None, speech_hangover: float | None = None, speech_activity: bool | None = None, stall_after: float | None = None) -> VoiceConnection
Connect to the voice channel of the user who ran the command.
Dave
dataclass
¶
Dave(protocol_version: int, epoch: int | None, privacy_code: str | None, _verification_code: Callable[[int], Awaitable[str]])
The call's end-to-end encryption, off when protocol_version is 0.
ExternalVoice
dataclass
¶
ExternalVoice(state: VoiceStateUpdate, server: VoiceServerUpdate, _bot: Bot, _guild_id: int)
The session and server an external voice engine connects with.
bostie has no media in that guild; updates() hands on what changes later.
updates
async
¶
updates() -> AsyncGenerator[AsyncIterator[VoiceServerUpdate | VoiceStateUpdate]]
Queue the guild's new voice servers and the bot's own voice states.
VoiceConnection
¶
VoiceConnection(manager: VoiceManager, guild_id: int, channel: Channel, settings: Defaults)
The bot's voice connection in one guild.
speaking
property
¶
Users talking now, mapped to the time.monotonic() they started.
sinks
property
¶
The open sinks, oldest first: every track and queue plays through one.
speech_hangover
property
writable
¶
speech_hangover: float
Seconds without speech before a user counts as stopped.
stall_after
property
writable
¶
stall_after: float
Seconds media may stop flowing before MediaStalled reports it.
speech_activity
property
writable
¶
speech_activity: bool
Whether speech events fire; off, audio is only decrypted for receivers.
sink
¶
sink(format: Format = _DEFAULT_PCM, *, volume: float = 1.0, after: Sink | None = None, crossfade: float = 0.0, filters: Filters = _NO_FILTERS, buffer: float | None = None, stall: SinkStall | None = None) -> Sink
Open a bounded writer whose audio is mixed into this connection.
With after, it stays silent until after closes, or plays out what was
written before its last drain(). With crossfade too, it starts that many
seconds before after ends and fades across, buffering that much more.
receive
¶
receive(format: Packets, *, users: Iterable[int] | None = None, exclude: Iterable[int] = (), include_self: bool = False, markers: Literal[False] = False, buffer: float | None = None, overflow: Overflow | None = None, stall: ReceiveStall | None = None) -> Receiver[Packet]
receive(format: Packets, *, users: Iterable[int] | None = None, exclude: Iterable[int] = (), include_self: bool = False, markers: Literal[True], buffer: float | None = None, overflow: Overflow | None = None, stall: ReceiveStall | None = None) -> Receiver[Packet | SpeechStart | SpeechStop | UserLeave]
receive(format: Packets, *, users: Iterable[int] | None = None, exclude: Iterable[int] = (), include_self: bool = False, markers: bool, buffer: float | None = None, overflow: Overflow | None = None, stall: ReceiveStall | None = None) -> Receiver[Packet | SpeechStart | SpeechStop | UserLeave]
receive(format: PCM | Opus = _DEFAULT_PCM, *, users: Iterable[int] | None = None, exclude: Iterable[int] = (), include_self: bool = False, markers: Literal[False] = False, mix: bool = False, buffer: float | None = None, jitter: float | None = None, overflow: Overflow | None = None, stall: ReceiveStall | None = None) -> Receiver[Frame]
receive(format: PCM | Opus = _DEFAULT_PCM, *, users: Iterable[int] | None = None, exclude: Iterable[int] = (), include_self: bool = False, markers: Literal[True], mix: bool = False, buffer: float | None = None, jitter: float | None = None, overflow: Overflow | None = None, stall: ReceiveStall | None = None) -> Receiver[Frame | SpeechStart | SpeechStop | UserLeave]
receive(format: PCM | Opus = _DEFAULT_PCM, *, users: Iterable[int] | None = None, exclude: Iterable[int] = (), include_self: bool = False, markers: bool, mix: bool = False, buffer: float | None = None, jitter: float | None = None, overflow: Overflow | None = None, stall: ReceiveStall | None = None) -> Receiver[Frame | SpeechStart | SpeechStop | UserLeave]
receive(format: ReceiveFormat = _DEFAULT_PCM, *, users: Iterable[int] | None = None, exclude: Iterable[int] = (), include_self: bool = False, markers: bool = False, mix: bool = False, buffer: float | None = None, jitter: float | None = None, overflow: Overflow | None = None, stall: ReceiveStall | None = None) -> Receiver[Any]
Open a bounded iterator over the audio this connection receives.
play
async
¶
play(source: Source, *, volume: float = 1.0, filters: Filters = _NO_FILTERS, meta: Any = None, buffer: float | None = None, stall: SinkStall | None = None) -> Track
Play a source on its own sink, mixed with anything else playing.
move
async
¶
Move to another voice or stage channel and wait until media flows there.
edit
async
¶
edit(*, self_mute: bool | None = None, self_deaf: bool | None = None, timeout: float | None = None) -> None
Change the bot's own mute or deafen, and return once Discord applied it.
wait_until
async
¶
wait_until(state: State) -> None
Wait until the connection reaches state; VoiceNotConnected if it ends.
Bound the wait with asyncio.timeout().
disconnect
async
¶
disconnect() -> None
Leave the channel and return once the connection ended, within 2 s.
VoiceManager
¶
The bot's voice connections, at most one per guild.
connect
async
¶
connect(channel_id: int, *, self_deaf: bool | None = None, self_mute: bool | None = None, timeout: float | None = None, server_change_delay: float | None = None, encoder: EncoderSettings | None = None, speaking_flags: SpeakingFlags | None = None, stage: StageRole | None = None, speech_hangover: float | None = None, speech_activity: bool | None = None, stall_after: float | None = None) -> VoiceConnection
Connect to a voice or stage channel and return once media can flow.
Each setting given overrides voice.Defaults. In a guild where the bot is already connected, this moves it and applies only the settings given.
connect_external
async
¶
connect_external(guild_id: int, channel_id: int, *, self_deaf: bool | None = None, self_mute: bool | None = None, timeout: float | None = None) -> ExternalVoice
Join a voice channel for an external voice engine and return its credentials.
bostie then has no media in that guild; external.updates() hands on what
changes later.
OpusEncoder
¶
OpusEncoder(format: PCM = _DEFAULT_PCM, *, encoder: EncoderSettings = _DEFAULT_ENCODER)
Encodes PCM into the 20 ms 48 kHz stereo packets an Opus() sink passes through.
bitrate=None means 64 kbps here, since there is no channel to follow.
DaveTransition
¶
Bases: VoiceEvent
MediaResumed
¶
MediaStalled
¶
Moved
¶
Bases: VoiceEvent
ReceptionReport
¶
Bases: Struct
How one stream reaches whoever sent the report, as RFC 3550 measures it.
RtcpPacket
¶
SpeakingUpdate
¶
Bases: VoiceEvent
SpeechStart
¶
SpeechStop
¶
StateChange
¶
Bases: VoiceEvent
UserJoin
¶
UserLeave
¶
Bases: VoiceEvent
A user left the call; from a receiver, offset is the time on its clock.
VoiceEvent
¶
PCM
dataclass
¶
Defaults
dataclass
¶
Defaults(*, self_deaf: bool = False, self_mute: bool = False, timeout: float = 10.0, server_change_delay: float = 0.0, encoder: EncoderSettings = EncoderSettings(), speaking_flags: SpeakingFlags = MICROPHONE, stage: StageRole = 'listen', sink_buffer: float = 0.2, stall: SinkStall = 'hold', speech_hangover: float = 0.2, speech_activity: bool = True, stall_after: float = 2.0, receive_buffer: float = 2.0, jitter: float = 0.1, overflow: Overflow = 'drop_oldest', receive_stall: ReceiveStall = 'wait', crossfade: float = 0.0, history: int = 0)
Voice settings every connection starts from.
speaking_flags
class-attribute
instance-attribute
¶
speaking_flags: SpeakingFlags = SpeakingFlags.MICROPHONE
EncoderSettings
dataclass
¶
EncoderSettings(*, bitrate: int | None = None, application: OpusApplication = 'audio', fec: bool = True, packet_loss: float = 0.1, complexity: int = 10, bandwidth: OpusBandwidth = 'auto', signal_type: OpusSignal = 'auto')
Opus encoder settings; bitrate=None follows the channel.
bandwidth caps the audio band and signal_type tunes for voice or music;
"auto" leaves both to libopus.
Filters
dataclass
¶
Filters(*, equalizer: Mapping[float, float] = MappingProxyType({}), bass: float = 0.0, speed: float = 1.0, pitch: float = 1.0)
Effects applied in Rust as a sink plays; the defaults change nothing.
equalizer maps band centres in Hz to gains in dB, each an octave wide; bass
is a gain in dB below 100 Hz; speed keeps the pitch and pitch the tempo.
Latency
dataclass
¶
Frame
¶
Bases: Struct
A piece of one user's audio, or of the mix, on the receiver's 48 kHz clock.
Packet
¶
Bases: Struct
One decrypted Opus packet with its RTP fields and header extensions.
Queue
¶
Queue(vc: VoiceConnection, *, volume: float = 1.0, filters: Filters = _NO_FILTERS, crossfade: float | None = None, history: int | None = None, buffer: float | None = None, stall: SinkStall | None = None)
Plays tracks back to back without gaps, or overlapping by crossfade seconds.
crossfade
instance-attribute
¶
crossfade = _check_crossfade(defaults.crossfade if crossfade is None else crossfade)
history
property
¶
The tracks that played, oldest first, as many as history keeps.
filters
property
writable
¶
filters: Filters
The effects on every track: equalizer, bass, speed and pitch.
loop
property
writable
¶
loop: Loop
What plays again: the current "track", the whole "queue", or None.
add
¶
Queue a source at the end, or at index among the upcoming tracks.
move
¶
Move an upcoming track to index among the upcoming tracks.
Track
¶
Track(queue: Queue, entry: _Entry, *, current: Callable[[_Entry], bool], seek: Callable[[_Entry, float], Awaitable[None]], stop: Callable[[_Entry], None])
A source playing, or waiting to play, on a voice connection.
TrackEnd
dataclass
¶
TrackEnd(track: Track, reason: EndReason, error: BaseException | None, may_start_next: bool)
Receiver
¶
Receiver(connection: VoiceConnection, native: Receiver, handle: Handle, format: ReceiveFormat, *, on_close: Callable[[Receiver[Any]], None], users: frozenset[int] | None, exclude: frozenset[int], include_self: bool, buffer: float, jitter: float, markers: bool, mix: bool, overflow: Overflow, stall: ReceiveStall)
A bounded async iterator over received audio, one for each subscriber.
queued
property
¶
queued: float
Seconds the oldest queued item has waited: this is the consumer's lag.
mark
¶
True once prior audio is queued or dropped, else False by 1.2s + 2*jitter.
stats
¶
stats() -> ReceiverStats
The latest counters, or the last ones read before the receiver ended.
ReceiverStats
dataclass
¶
Recorder
¶
Recorder(vc: VoiceConnection, path: str | PathLike[str], *, users: Iterable[int] | None = None, exclude: Iterable[int] = (), include_self: bool = False, align: bool = True, mix: bool = False, format: PCM = _DEFAULT_PCM, buffer: float = 30.0, jitter: float = 0.3, max_duration: float | None = None, max_size: int | None = None)
Record each user, or with mix=True the whole call, to Ogg Opus or WAV.
The path's extension picks the container; format is the WAV sample format.
It ends on its own after max_duration seconds or once its files hold
max_size bytes.
files
property
¶
The files written so far by user id; a mixed recording's key is None.
stats
¶
stats() -> ReceiverStats
The receiver's counters; it drops frames when the disk falls behind.
stop
async
¶
Write the audio heard so far, then stop and return the files.
Marker
¶
Sink
¶
Sink(native: Sink, handle: Handle, format: Format, *, buffer: float, stall: SinkStall, volume: float, crossfade: float = 0.0, filters: Filters = _NO_FILTERS, on_audio: Callable[[], None], on_close: Callable[[Sink], None])
A bounded writer whose audio is mixed into a voice connection.
filters
property
writable
¶
filters: Filters
The effects Rust applies as this sink plays; changes take effect at once.
passthrough
property
¶
passthrough: bool
Whether the last packet went out as written, without decoding.
write
async
¶
write(data: Buffer) -> None
Queue audio, waiting while the sink holds buffer seconds or more.
write_nowait
¶
write_nowait(data: Buffer) -> None
Queue the whole chunk in one native write, or raise BufferFull.
A large chunk overshoots buffer and converts in one call; write() slices it.
drain
async
¶
drain() -> bool
Wait until everything written so far has left; False if any was dropped.
clear
¶
Drop the queued audio, only the part before until, or after since.
SinkStats
dataclass
¶
SinkStats(sent: float, position: float, underruns: int, held_ticks: int, dropped: float, passthrough_packets: int)
A sink's counters in seconds; position is the playhead markers use.
Bytes
¶
Bases: _DecodedSource
Audio file contents in memory; hint is a file extension such as "ogg".
FFmpeg
¶
File
¶
Bases: _DecodedSource
A local audio file, decoded in Rust.
Source
¶
Bases: ABC
Audio to play, single use: open() once, iterate, aclose().
Items are Opus packets or PCM chunks in format.
open
abstractmethod
async
¶
open(manager: VoiceManager) -> None
Prepare the source; vc.play() calls this, or call it with bot.voice.
Stream
¶
Stream(items: AsyncIterable[bytes], format: Format)
Tags
dataclass
¶
Url
¶
Bases: _DecodedSource
A direct HTTP(S) media URL, streamed and decoded in Rust.
Utterance
dataclass
¶
versions
¶
Versions of bostie, libopus, davey and the DAVE protocol in this build.
broadcast
async
¶
broadcast(source: Source, connections: Iterable[VoiceConnection], *, encoder: EncoderSettings = _DEFAULT_ENCODER, stall: SinkStall = 'drop') -> None
Play one source on every given connection, encoding PCM once.
Connections that join later are not added; one that ends leaves the set.