twilight_lavalink/model/
outgoing.rs

1//! Events that clients send to Lavalink.
2use serde::{Deserialize, Serialize};
3use twilight_model::{
4    gateway::payload::incoming::VoiceServerUpdate,
5    id::{Id, marker::GuildMarker},
6};
7
8/// The track on the player. The encoded and identifier are mutually exclusive.
9/// We don't support userData field currently.
10#[derive(Clone, Debug, Deserialize, Eq, PartialEq, Serialize)]
11#[non_exhaustive]
12#[serde(rename_all = "camelCase")]
13pub struct UpdatePlayerTrack {
14    /// The string of the track to play.
15    #[serde(flatten)]
16    pub track_string: TrackOption,
17}
18
19/// Used to play a specific track. These are mutually exclusive.
20/// When identifier is used, Lavalink will try to resolve the identifier as a
21/// single track. An HTTP 400 error is returned when resolving a playlist,
22/// search result, or no tracks.
23#[derive(Clone, Debug, Deserialize, Eq, PartialEq, Serialize)]
24#[serde(rename_all = "lowercase")]
25pub enum TrackOption {
26    /// The base64 encoded track to play. null stops the current track.
27    Encoded(Option<String>),
28    /// The identifier of the track to play.
29    Identifier(String),
30}
31
32/// An outgoing event to send to Lavalink.
33#[derive(Clone, Debug, Deserialize, PartialEq, Serialize)]
34#[non_exhaustive]
35#[serde(untagged)]
36pub enum OutgoingEvent {
37    /// Destroy a player for a guild.
38    Destroy(Destroy),
39    /// Equalize a player.
40    Equalizer(Equalizer),
41    /// Pause or unpause a player.
42    Pause(Pause),
43    /// Play a track.
44    Play(Play),
45    /// Seek a player's active track to a new position.
46    Seek(Seek),
47    /// Stop a player.
48    Stop(Stop),
49    /// A combined voice server and voice state update.
50    VoiceUpdate(VoiceUpdate),
51    /// Set the volume of a player.
52    Volume(Volume),
53}
54
55impl From<Destroy> for OutgoingEvent {
56    fn from(event: Destroy) -> OutgoingEvent {
57        Self::Destroy(event)
58    }
59}
60
61impl From<Equalizer> for OutgoingEvent {
62    fn from(event: Equalizer) -> OutgoingEvent {
63        Self::Equalizer(event)
64    }
65}
66
67impl From<Pause> for OutgoingEvent {
68    fn from(event: Pause) -> OutgoingEvent {
69        Self::Pause(event)
70    }
71}
72
73impl From<Play> for OutgoingEvent {
74    fn from(event: Play) -> OutgoingEvent {
75        Self::Play(event)
76    }
77}
78
79impl From<Seek> for OutgoingEvent {
80    fn from(event: Seek) -> OutgoingEvent {
81        Self::Seek(event)
82    }
83}
84
85impl From<Stop> for OutgoingEvent {
86    fn from(event: Stop) -> OutgoingEvent {
87        Self::Stop(event)
88    }
89}
90
91impl From<VoiceUpdate> for OutgoingEvent {
92    fn from(event: VoiceUpdate) -> OutgoingEvent {
93        Self::VoiceUpdate(event)
94    }
95}
96
97impl From<Volume> for OutgoingEvent {
98    fn from(event: Volume) -> OutgoingEvent {
99        Self::Volume(event)
100    }
101}
102
103impl OutgoingEvent {
104    /// ID of the destination guild of this event.
105    pub const fn guild_id(&self) -> Id<GuildMarker> {
106        match self {
107            Self::VoiceUpdate(voice_update) => voice_update.guild_id,
108            Self::Play(play) => play.guild_id,
109            Self::Destroy(destroy) => destroy.guild_id,
110            Self::Equalizer(equalize) => equalize.guild_id,
111            Self::Pause(pause) => pause.guild_id,
112            Self::Seek(seek) => seek.guild_id,
113            Self::Stop(stop) => stop.guild_id,
114            Self::Volume(volume) => volume.guild_id,
115        }
116    }
117
118    /// Whether this event replaces the currently playing track.
119    pub(crate) const fn no_replace(&self) -> bool {
120        match self {
121            Self::Play(play) => play.no_replace,
122            Self::Stop(_) => false,
123            _ => true,
124        }
125    }
126}
127
128/// Destroy a player from a node.
129#[derive(Clone, Debug, Deserialize, Eq, PartialEq, Serialize)]
130#[non_exhaustive]
131#[serde(rename_all = "camelCase")]
132pub struct Destroy {
133    /// The guild ID of the player.
134    pub guild_id: Id<GuildMarker>,
135}
136
137impl Destroy {
138    /// Create a new destroy event.
139    pub const fn new(guild_id: Id<GuildMarker>) -> Self {
140        Self { guild_id }
141    }
142}
143
144impl From<Id<GuildMarker>> for Destroy {
145    fn from(guild_id: Id<GuildMarker>) -> Self {
146        Self { guild_id }
147    }
148}
149
150/// Filters to pass to the update player endpoint.
151#[derive(Clone, Debug, Deserialize, PartialEq, Serialize)]
152#[non_exhaustive]
153#[serde(rename_all = "camelCase")]
154pub enum Filters {
155    /// Adjusts 15 different bands
156    Equalizer(Equalizer),
157}
158
159/// Equalize a player.
160#[derive(Clone, Debug, Deserialize, PartialEq, Serialize)]
161#[non_exhaustive]
162#[serde(rename_all = "camelCase")]
163pub struct Equalizer {
164    /// The bands to use as part of the equalizer.
165    pub equalizer: Vec<EqualizerBand>,
166    /// The guild ID of the player.
167    #[serde(skip_serializing)]
168    pub guild_id: Id<GuildMarker>,
169}
170
171impl Equalizer {
172    /// Create a new equalizer event.
173    pub fn new(guild_id: Id<GuildMarker>, bands: Vec<EqualizerBand>) -> Self {
174        Self::from((guild_id, bands))
175    }
176}
177
178impl From<(Id<GuildMarker>, Vec<EqualizerBand>)> for Equalizer {
179    fn from((guild_id, bands): (Id<GuildMarker>, Vec<EqualizerBand>)) -> Self {
180        Self {
181            equalizer: bands,
182            guild_id,
183        }
184    }
185}
186
187/// A band of the equalizer event.
188#[derive(Clone, Debug, Deserialize, PartialEq, Serialize)]
189#[non_exhaustive]
190#[serde(rename_all = "camelCase")]
191pub struct EqualizerBand {
192    /// The band.
193    pub band: i64,
194    /// The gain.
195    pub gain: f64,
196}
197
198impl EqualizerBand {
199    /// Create a new equalizer band.
200    pub fn new(band: i64, gain: f64) -> Self {
201        Self::from((band, gain))
202    }
203}
204
205impl From<(i64, f64)> for EqualizerBand {
206    fn from((band, gain): (i64, f64)) -> Self {
207        Self { band, gain }
208    }
209}
210
211/// Pause or unpause a player.
212#[derive(Clone, Debug, Deserialize, Eq, PartialEq, Serialize)]
213#[non_exhaustive]
214#[serde(rename_all = "camelCase")]
215pub struct Pause {
216    /// The guild ID of the player.
217    pub guild_id: Id<GuildMarker>,
218    /// Whether to pause the player.
219    ///
220    /// Set to `true` to pause or `false` to resume.
221    pub paused: bool,
222}
223
224impl Pause {
225    /// Create a new pause event.
226    ///
227    /// Set to `true` to pause the player or `false` to resume it.
228    pub fn new(guild_id: Id<GuildMarker>, pause: bool) -> Self {
229        Self::from((guild_id, pause))
230    }
231}
232
233impl From<(Id<GuildMarker>, bool)> for Pause {
234    fn from((guild_id, pause): (Id<GuildMarker>, bool)) -> Self {
235        Self {
236            guild_id,
237            paused: pause,
238        }
239    }
240}
241
242/// Play a track, optionally specifying to not skip the current track.
243#[derive(Clone, Debug, Deserialize, Eq, PartialEq, Serialize)]
244#[non_exhaustive]
245#[serde(rename_all = "camelCase")]
246pub struct Play {
247    /// The position in milliseconds to end the track.
248    ///
249    /// `Some(None)` resets this if it was set previously.
250    #[serde(skip_serializing_if = "Option::is_none")]
251    pub end_time: Option<Option<u64>>,
252    /// The guild ID of the player.
253    #[serde(skip_serializing)]
254    pub guild_id: Id<GuildMarker>,
255    /// Whether or not to replace the currently playing track with this new
256    /// track.
257    ///
258    /// Set to `true` to keep playing the current playing track, or `false`
259    /// to replace the current playing track with a new one.
260    #[serde(skip_serializing)]
261    pub no_replace: bool,
262    /// The position in milliseconds to start the track from.
263    #[serde(skip_serializing_if = "Option::is_none")]
264    pub position: Option<u64>,
265    /// Whether the player is paused
266    #[serde(skip_serializing_if = "Option::is_none")]
267    pub paused: Option<bool>,
268    /// Information about the track to play.
269    #[serde(skip_serializing_if = "Option::is_none")]
270    pub track: Option<UpdatePlayerTrack>,
271    /// The player volume, in percentage, from 0 to 1000
272    #[serde(skip_serializing_if = "Option::is_none")]
273    pub volume: Option<u64>,
274}
275
276impl Play {
277    /// Create a new play event.
278    pub fn new(
279        guild_id: Id<GuildMarker>,
280        track: impl Into<String>,
281        start_time: impl Into<Option<u64>>,
282        end_time: impl Into<Option<u64>>,
283        no_replace: bool,
284    ) -> Self {
285        Self::from((guild_id, track, start_time, end_time, no_replace))
286    }
287}
288
289impl<T: Into<String>> From<(Id<GuildMarker>, T)> for Play {
290    fn from((guild_id, track): (Id<GuildMarker>, T)) -> Self {
291        Self::from((guild_id, track, None, None, true))
292    }
293}
294
295impl<T: Into<String>, S: Into<Option<u64>>> From<(Id<GuildMarker>, T, S)> for Play {
296    fn from((guild_id, track, start_time): (Id<GuildMarker>, T, S)) -> Self {
297        Self::from((guild_id, track, start_time, None, true))
298    }
299}
300
301impl<T: Into<String>, S: Into<Option<u64>>, E: Into<Option<u64>>> From<(Id<GuildMarker>, T, S, E)>
302    for Play
303{
304    fn from((guild_id, track, start_time, end_time): (Id<GuildMarker>, T, S, E)) -> Self {
305        Self::from((guild_id, track, start_time, end_time, true))
306    }
307}
308
309impl<T: Into<String>, S: Into<Option<u64>>, E: Into<Option<u64>>>
310    From<(Id<GuildMarker>, T, S, E, bool)> for Play
311{
312    fn from(
313        (guild_id, track, start_time, end_time, no_replace): (Id<GuildMarker>, T, S, E, bool),
314    ) -> Self {
315        Self {
316            guild_id,
317            no_replace,
318            position: start_time.into(),
319            end_time: Some(end_time.into()),
320            volume: None,
321            paused: None,
322            track: Some(UpdatePlayerTrack {
323                track_string: TrackOption::Encoded(Some(track.into())),
324            }),
325        }
326    }
327}
328
329/// Seek a player's active track to a new position.
330#[derive(Clone, Debug, Deserialize, Eq, PartialEq, Serialize)]
331#[non_exhaustive]
332#[serde(rename_all = "camelCase")]
333pub struct Seek {
334    /// The guild ID of the player.
335    #[serde(skip_serializing)]
336    pub guild_id: Id<GuildMarker>,
337    /// The position in milliseconds to seek to.
338    pub position: i64,
339}
340
341impl Seek {
342    /// Create a new seek event.
343    pub fn new(guild_id: Id<GuildMarker>, position: i64) -> Self {
344        Self::from((guild_id, position))
345    }
346}
347
348impl From<(Id<GuildMarker>, i64)> for Seek {
349    fn from((guild_id, position): (Id<GuildMarker>, i64)) -> Self {
350        Self { guild_id, position }
351    }
352}
353
354/// Stop a player.
355#[derive(Clone, Debug, Deserialize, Eq, PartialEq, Serialize)]
356#[non_exhaustive]
357#[serde(rename_all = "camelCase")]
358pub struct Stop {
359    /// The guild ID of the player.
360    #[serde(skip_serializing)]
361    pub guild_id: Id<GuildMarker>,
362    /// The track object to pass set to null
363    pub track: UpdatePlayerTrack,
364}
365
366impl Stop {
367    /// Create a new stop event.
368    pub fn new(guild_id: Id<GuildMarker>) -> Self {
369        Self::from(guild_id)
370    }
371}
372
373impl From<Id<GuildMarker>> for Stop {
374    fn from(guild_id: Id<GuildMarker>) -> Self {
375        Self {
376            guild_id,
377            track: UpdatePlayerTrack {
378                track_string: TrackOption::Encoded(None),
379            },
380        }
381    }
382}
383/// The voice payload for the combined server and state to send to lavalink.
384#[derive(Clone, Debug, Deserialize, Eq, PartialEq, Serialize)]
385#[non_exhaustive]
386#[serde(rename_all = "camelCase")]
387pub struct Voice {
388    /// The Discord voice endpoint to connect to.
389    pub endpoint: String,
390    /// The Discord voice session id to authenticate with. This is separate from the session id of lavalink.
391    pub session_id: String,
392    /// The Discord voice token to authenticate with.
393    pub token: String,
394}
395
396/// A combined voice server and voice state update.
397#[derive(Clone, Debug, Deserialize, Eq, PartialEq, Serialize)]
398#[non_exhaustive]
399#[serde(rename_all = "camelCase")]
400pub struct VoiceUpdate {
401    /// The guild ID of the player.
402    #[serde(skip_serializing)]
403    pub guild_id: Id<GuildMarker>,
404    /// The voice payload for the combined server and state to send to lavalink.
405    pub voice: Voice,
406}
407
408impl VoiceUpdate {
409    /// Create a new voice update event.
410    pub fn new(
411        guild_id: Id<GuildMarker>,
412        session_id: impl Into<String>,
413        event: VoiceServerUpdate,
414    ) -> Self {
415        Self::from((guild_id, session_id, event))
416    }
417}
418
419impl<T: Into<String>> From<(Id<GuildMarker>, T, VoiceServerUpdate)> for VoiceUpdate {
420    fn from((guild_id, session_id, event): (Id<GuildMarker>, T, VoiceServerUpdate)) -> Self {
421        Self {
422            guild_id,
423            voice: Voice {
424                token: event.token,
425                endpoint: event.endpoint.unwrap_or("NO_ENDPOINT_RETURNED".to_string()),
426                session_id: session_id.into(),
427            },
428        }
429    }
430}
431
432/// Set the volume of a player.
433#[derive(Clone, Debug, Deserialize, Eq, PartialEq, Serialize)]
434#[non_exhaustive]
435#[serde(rename_all = "camelCase")]
436pub struct Volume {
437    /// The guild ID of the player.
438    #[serde(skip_serializing)]
439    pub guild_id: Id<GuildMarker>,
440    /// The volume of the player from 0 to 1000. 100 is the default.
441    pub volume: i64,
442}
443
444impl Volume {
445    /// Create a new volume event.
446    pub fn new(guild_id: Id<GuildMarker>, volume: i64) -> Self {
447        Self::from((guild_id, volume))
448    }
449}
450
451impl From<(Id<GuildMarker>, i64)> for Volume {
452    fn from((guild_id, volume): (Id<GuildMarker>, i64)) -> Self {
453        Self { guild_id, volume }
454    }
455}