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}