Skip to main content

twilight_lavalink/model/
incoming.rs

1//! Events that Lavalink sends to clients.
2
3/// The type of event that is coming in from a Lavalink message.
4#[derive(Clone, Copy, Debug, Deserialize, Eq, PartialEq, Serialize)]
5#[non_exhaustive]
6#[serde(rename_all = "camelCase")]
7pub enum Opcode {
8    /// Meta information about a track starting or ending.
9    Event,
10    /// An update about a player's current track.
11    PlayerUpdate,
12    /// Lavalink is connected and ready.
13    Ready,
14    /// Updated statistics about a node.
15    Stats,
16}
17
18use serde::{Deserialize, Serialize};
19use twilight_model::id::{
20    Id,
21    marker::{ChannelMarker, GuildMarker},
22};
23
24/// The levels of severity that an exception can have.
25#[derive(Clone, Debug, Deserialize, Eq, PartialEq, Serialize)]
26#[non_exhaustive]
27#[serde(rename_all = "camelCase")]
28pub enum Severity {
29    /// The cause is known and expected, indicates that there is nothing wrong
30    /// with the library itself.
31    Common,
32    /// The probable cause is an issue with the library or there is no way to
33    /// tell what the cause might be. This is the default level and other
34    /// levels are used in cases where the thrower has more in-depth knowledge
35    /// about the error.
36    Fault,
37    /// The cause might not be exactly known, but is possibly caused by outside
38    /// factors. For example when an outside service responds in a format that
39    /// we do not expect.
40    Suspicious,
41}
42
43/// The exception with the details attached on what happened when making a query
44/// to Lavalink.
45#[derive(Clone, Debug, Deserialize, Eq, PartialEq, Serialize)]
46#[non_exhaustive]
47#[serde(rename_all = "camelCase")]
48pub struct Exception {
49    /// The cause of the exception.
50    pub cause: String,
51    /// The message of the exception.
52    pub message: Option<String>,
53    /// The severity of the exception.
54    pub severity: Severity,
55    /// The full stack trace of the cause.
56    pub cause_stack_trace: String,
57}
58
59/// An incoming event from a Lavalink node.
60#[derive(Clone, Debug, Deserialize, PartialEq, Serialize)]
61#[non_exhaustive]
62#[serde(tag = "op", rename_all = "camelCase")]
63#[allow(clippy::large_enum_variant)]
64pub enum IncomingEvent {
65    /// Dispatched when player or voice events occur.
66    Event(Event),
67    /// Dispatched when you successfully connect to the Lavalink node.
68    Ready(Ready),
69    /// New statistics about a node and its host.
70    Stats(Stats),
71    /// An update about the information of a player.
72    PlayerUpdate(PlayerUpdate),
73}
74
75impl From<Ready> for IncomingEvent {
76    fn from(event: Ready) -> IncomingEvent {
77        Self::Ready(event)
78    }
79}
80
81impl From<Event> for IncomingEvent {
82    fn from(event: Event) -> IncomingEvent {
83        Self::Event(event)
84    }
85}
86
87impl From<PlayerUpdate> for IncomingEvent {
88    fn from(event: PlayerUpdate) -> IncomingEvent {
89        Self::PlayerUpdate(event)
90    }
91}
92
93impl From<Stats> for IncomingEvent {
94    fn from(event: Stats) -> IncomingEvent {
95        Self::Stats(event)
96    }
97}
98
99/// The Discord voice information that Lavalink uses for connection and sending
100/// information.
101#[derive(Clone, Debug, Deserialize, Eq, PartialEq, Serialize)]
102#[non_exhaustive]
103#[serde(rename_all = "camelCase")]
104pub struct VoiceState {
105    /// The Discord voice channel id the bot is connected to.
106    pub channel_id: Option<Id<ChannelMarker>>,
107    /// The Discord voice endpoint to connect to.
108    pub endpoint: String,
109    /// The Discord voice session id to authenticate with. Note this is separate
110    /// from the lavalink session id.
111    pub session_id: String,
112    /// The Discord voice token to authenticate with.
113    pub token: String,
114}
115
116/// An update of a player's status and state.
117#[derive(Clone, Debug, Deserialize, Eq, PartialEq, Serialize)]
118#[non_exhaustive]
119#[serde(rename_all = "camelCase")]
120pub struct PlayerUpdate {
121    /// The guild ID of the player.
122    pub guild_id: Id<GuildMarker>,
123    /// The new state of the player.
124    pub state: PlayerUpdateState,
125}
126
127impl PlayerUpdate {
128    /// The operation type of the `PlayerUpate` event.
129    pub const OPCODE: Opcode = Opcode::PlayerUpdate;
130}
131
132/// New statistics about a node and its host.
133#[derive(Clone, Debug, Deserialize, Eq, PartialEq, Serialize)]
134#[non_exhaustive]
135#[serde(rename_all = "camelCase")]
136pub struct PlayerUpdateState {
137    /// True when the player is connected to the voice gateway.
138    pub connected: bool,
139    /// The ping of the node to the Discord voice server in milliseconds (-1 if not connected).
140    pub ping: i64,
141    /// Track position in milliseconds. None if not playing anything.
142    pub position: i64,
143    /// Unix timestamp of the player in milliseconds.
144    pub time: i64,
145}
146
147/// Dispatched by Lavalink upon successful connection and authorization.
148#[derive(Clone, Debug, Deserialize, PartialEq, Serialize)]
149#[non_exhaustive]
150#[serde(rename_all = "camelCase")]
151pub struct Ready {
152    /// Whether this session was resumed.
153    pub resumed: bool,
154    /// The Lavalink session id of this connection. Not to be confused with a
155    /// Discord voice session id.
156    pub session_id: String,
157}
158
159/// Statistics about a node and its host.
160#[derive(Clone, Debug, Deserialize, PartialEq, Serialize)]
161#[non_exhaustive]
162#[serde(rename_all = "camelCase")]
163pub struct Stats {
164    /// CPU information about the node's host.
165    pub cpu: StatsCpu,
166    /// The frame stats of the node. `null` if the node has no players or when
167    /// retrieved via /v4/stats.
168    #[serde(skip_serializing_if = "Option::is_none")]
169    pub frame_stats: Option<StatsFrame>,
170    /// Memory information about the node's host.
171    pub memory: StatsMemory,
172    /// The current number of total players (active and not active) within
173    /// the node.
174    pub players: u64,
175    /// The current number of active players within the node.
176    pub playing_players: u64,
177    /// The uptime of the Lavalink server in seconds.
178    pub uptime: u64,
179}
180
181/// CPU information about a node and its host.
182#[derive(Clone, Debug, Deserialize, PartialEq, Serialize)]
183#[non_exhaustive]
184#[serde(rename_all = "camelCase")]
185pub struct StatsCpu {
186    /// The number of CPU cores.
187    pub cores: usize,
188    /// The load of the Lavalink server.
189    pub lavalink_load: f64,
190    /// The load of the system as a whole.
191    pub system_load: f64,
192}
193
194/// CPU information about a node and its host.
195#[derive(Clone, Debug, Deserialize, Eq, PartialEq, Serialize)]
196#[non_exhaustive]
197#[serde(rename_all = "camelCase")]
198pub struct StatsFrame {
199    /// The load of the system as a whole.
200    pub deficit: i64,
201    /// The load of the Lavalink server.
202    pub nulled: i64,
203    /// The number of CPU cores.
204    pub sent: i64,
205}
206
207/// Memory information about a node and its host.
208#[derive(Clone, Debug, Deserialize, Eq, PartialEq, Serialize)]
209#[non_exhaustive]
210#[serde(rename_all = "camelCase")]
211pub struct StatsMemory {
212    /// The number of bytes allocated.
213    pub allocated: u64,
214    /// The number of bytes free.
215    pub free: u64,
216    /// The number of bytes reservable.
217    pub reservable: u64,
218    /// The number of bytes used.
219    pub used: u64,
220}
221
222/// Information about the track returned or playing on Lavalink.
223#[derive(Clone, Debug, Deserialize, Eq, PartialEq, Serialize)]
224#[non_exhaustive]
225#[serde(rename_all = "camelCase")]
226pub struct TrackInfo {
227    /// The track artwork url.
228    pub artwork_url: Option<String>,
229    /// The track author.
230    pub author: String,
231    /// The track [ISRC](https://en.wikipedia.org/wiki/International_Standard_Recording_Code).
232    pub isrc: Option<String>,
233    /// The track identifier.
234    pub identifier: String,
235    /// Whether the track is seekable.
236    pub is_seekable: bool,
237    /// Whether the track is a stream.
238    pub is_stream: bool,
239    /// The track length in milliseconds.
240    pub length: u64,
241    /// The track position in milliseconds.
242    pub position: u64,
243    /// The track source name.
244    pub source_name: String,
245    /// The track title.
246    pub title: String,
247    /// The track uri.
248    pub uri: Option<String>,
249}
250
251/// A track object for lavalink to consume and read.
252#[derive(Clone, Debug, Deserialize, Eq, PartialEq, Serialize)]
253#[non_exhaustive]
254#[serde(rename_all = "camelCase")]
255pub struct Track {
256    /// The base64 encoded track to play
257    pub encoded: String,
258    /// Info about the track
259    pub info: TrackInfo,
260}
261
262/// Server dispatched an event. See the Event Types section for more information.
263#[derive(Clone, Debug, Deserialize, PartialEq, Serialize)]
264#[non_exhaustive]
265#[serde(rename_all = "camelCase")]
266pub struct Event {
267    /// The data of the event type.
268    #[serde(flatten)]
269    pub data: EventData,
270    /// The guild id that this was received from.
271    pub guild_id: String,
272    /// The type of event.
273    pub r#type: EventType,
274}
275
276/// The type of event being dispatched as a message from the server as the event
277/// triggers.
278#[derive(Clone, Copy, Debug, Deserialize, Eq, PartialEq, Serialize)]
279#[non_exhaustive]
280pub enum EventType {
281    /// Dispatched when a track starts playing.
282    TrackStartEvent,
283    /// Dispatched when a track ends.
284    TrackEndEvent,
285    /// Dispatched when a track throws an exception.
286    TrackExceptionEvent,
287    /// Dispatched when a track gets stuck while playing.
288    TrackStuckEvent,
289    /// Dispatched when the websocket connection to Discord voice servers is closed.
290    WebSocketClosedEvent,
291}
292
293/// The data of the server event that was dispatched when event triggers.
294#[derive(Clone, Debug, Deserialize, Eq, PartialEq, Serialize)]
295#[non_exhaustive]
296#[serde(untagged)]
297pub enum EventData {
298    /// Dispatched when a track ends.
299    TrackEndEvent(TrackEnd),
300    /// Dispatched when a track throws an exception.
301    TrackExceptionEvent(TrackException),
302    /// Dispatched when a track gets stuck while playing.
303    TrackStuckEvent(TrackStuck),
304    /// Dispatched when a track starts playing.
305    TrackStartEvent(TrackStart),
306    /// Dispatched when the websocket connection to Discord voice servers is closed.
307    WebSocketClosedEvent(WebSocketClosed),
308}
309
310/// The reason for the track ending.
311#[derive(Clone, Copy, Debug, Deserialize, Eq, PartialEq, Serialize)]
312#[non_exhaustive]
313#[serde(rename_all = "camelCase")]
314pub enum TrackEndReason {
315    /// The track was cleaned up.
316    Cleanup,
317    /// The track finished playing.
318    Finished,
319    /// The track failed to load.
320    LoadFailed,
321    /// The track was replaced
322    Replaced,
323    /// The track was stopped.
324    Stopped,
325}
326
327/// A track ended event from lavalink.
328#[derive(Clone, Debug, Deserialize, Eq, PartialEq, Serialize)]
329#[non_exhaustive]
330#[serde(rename_all = "camelCase")]
331pub struct TrackEnd {
332    /// The reason that the track ended.
333    pub reason: TrackEndReason,
334    /// The track that ended playing.
335    pub track: Track,
336}
337
338/// A track started.
339#[derive(Clone, Debug, Deserialize, Eq, PartialEq, Serialize)]
340#[non_exhaustive]
341#[serde(rename_all = "camelCase")]
342pub struct TrackStart {
343    /// The track that started playing.
344    pub track: Track,
345}
346
347/// Dispatched when a track throws an exception.
348#[derive(Clone, Debug, Deserialize, Eq, PartialEq, Serialize)]
349#[non_exhaustive]
350#[serde(rename_all = "camelCase")]
351pub struct TrackException {
352    /// The occurred exception.
353    pub exception: Exception,
354    /// The track that threw the exception.
355    pub track: Track,
356}
357
358/// Dispatched when a track gets stuck while playing.
359#[derive(Clone, Debug, Deserialize, Eq, PartialEq, Serialize)]
360#[non_exhaustive]
361#[serde(rename_all = "camelCase")]
362pub struct TrackStuck {
363    /// The threshold in milliseconds that was exceeded.
364    pub threshold_ms: u64,
365    /// The track that got stuck.
366    pub track: Track,
367}
368
369/// The voice websocket connection to Discord has been closed.
370#[derive(Clone, Debug, Deserialize, Eq, PartialEq, Serialize)]
371#[non_exhaustive]
372#[serde(rename_all = "camelCase")]
373pub struct WebSocketClosed {
374    /// True if Discord closed the connection, false if Lavalink closed it.
375    pub by_remote: bool,
376    /// [Discord websocket opcode](https://discord.com/developers/docs/topics/opcodes-and-status-codes#voice-voice-close-event-codes)
377    /// that closed the connection.
378    pub code: u64,
379    /// Reason the connection was closed.
380    pub reason: String,
381}