From 83e432254b20d5b0aee24db7ed2a8e4ff9f752c6 Mon Sep 17 00:00:00 2001 From: DaXcess Date: Thu, 15 Aug 2024 08:35:55 +0200 Subject: [PATCH 01/11] Reduce the amount of panics --- CHANGELOG.md | 7 +++++- Cargo.lock | 2 +- Cargo.toml | 2 +- src/commands/music/disconnect.rs | 2 +- src/commands/music/join.rs | 38 ++++++++++++++++++++++++++------ src/commands/music/lyrics.rs | 2 +- src/commands/music/playing.rs | 2 +- src/commands/music/stop.rs | 2 +- 8 files changed, 43 insertions(+), 14 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index c23f017..1937c8c 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,6 +1,11 @@ # Changelog -## 2.2.0 | TBD +## 2.2.1 | TBD + +- Fixed a bug where uncached guilds would panic the bot +- Fixed small issue with embed styling + +## 2.2.0 | August 13th 2024 ### Changes diff --git a/Cargo.lock b/Cargo.lock index a8274f6..6c808fe 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -3721,7 +3721,7 @@ dependencies = [ [[package]] name = "spoticord" -version = "2.2.0" +version = "2.2.1" dependencies = [ "anyhow", "dotenvy", diff --git a/Cargo.toml b/Cargo.toml index 7fa4cfb..deee9a0 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "spoticord" -version = "2.2.0" +version = "2.2.1" edition = "2021" rust-version = "1.75.0" diff --git a/src/commands/music/disconnect.rs b/src/commands/music/disconnect.rs index b36d999..5c45701 100644 --- a/src/commands/music/disconnect.rs +++ b/src/commands/music/disconnect.rs @@ -9,7 +9,7 @@ use crate::bot::Context; #[poise::command(slash_command, guild_only)] pub async fn disconnect(ctx: Context<'_>) -> Result<(), Error> { let manager = ctx.data(); - let guild = ctx.guild().expect("poise lied to me").id; + let guild = ctx.guild_id().expect("poise lied to me"); let Some(session) = manager.get_session(SessionQuery::Guild(guild)) else { ctx.send( diff --git a/src/commands/music/join.rs b/src/commands/music/join.rs index 258fa6e..d522f1a 100644 --- a/src/commands/music/join.rs +++ b/src/commands/music/join.rs @@ -4,7 +4,7 @@ use anyhow::Result; use log::error; use poise::CreateReply; use serenity::all::{ - Channel, ChannelId, CreateEmbed, CreateEmbedAuthor, CreateEmbedFooter, Guild, UserId, + Channel, ChannelId, CreateEmbed, CreateEmbedAuthor, CreateEmbedFooter, UserId, }; use spoticord_database::error::DatabaseError; use spoticord_session::manager::SessionQuery; @@ -15,9 +15,30 @@ use crate::bot::Context; /// Join the current voice channel #[poise::command(slash_command, guild_only)] pub async fn join(ctx: Context<'_>) -> Result<()> { - let guild: Guild = ctx.guild().expect("poise lied to me").clone(); + let guild = ctx.guild_id().expect("poise lied to me"); let manager = ctx.data(); + let Some(guild) = guild + .to_guild_cached(ctx.serenity_context()) + .map(|guild| guild.clone()) + else { + error!("Unable to fetch guild from cache, how did we get here?"); + + ctx.send( + CreateReply::default() + .embed( + CreateEmbed::new() + .title("An error occured") + .description("This server hasn't been cached yet?") + .color(Colors::Error), + ) + .ephemeral(true), + ) + .await?; + + return Ok(()); + }; + let Some(channel) = guild .voice_states .get(&ctx.author().id) @@ -98,8 +119,6 @@ pub async fn join(ctx: Context<'_>) -> Result<()> { return Ok(()); } - ctx.defer().await?; - let mut session_opt = manager.get_session(SessionQuery::Guild(guild.id)); // Check if this server already has a session active @@ -134,7 +153,8 @@ pub async fn join(ctx: Context<'_>) -> Result<()> { "You are already using Spoticord in `{}`\n\n\ Stop playing in that server first before starting a new session.", spoticord_utils::discord::escape(server_name) - )), + )) + .color(Colors::Error), ) .ephemeral(true), ) @@ -143,6 +163,8 @@ pub async fn join(ctx: Context<'_>) -> Result<()> { return Ok(()); } + ctx.defer().await?; + if let Some(session) = &session_opt { if session.voice_channel() != channel { session.disconnect().await; @@ -163,7 +185,7 @@ pub async fn join(ctx: Context<'_>) -> Result<()> { CreateEmbed::new() .title("Failed to reactivate session") .description( - "An error occured whilst trying to reactivate the session.", + "An error occured whilst trying to reactivate the session. Please try again.", ) .color(Colors::Error), ) @@ -190,7 +212,9 @@ pub async fn join(ctx: Context<'_>) -> Result<()> { .embed( CreateEmbed::new() .title("Failed to create session") - .description("An error occured whilst trying to create a session.") + .description( + "An error occured whilst trying to create a session. Please try again.", + ) .color(Colors::Error), ) .ephemeral(true), diff --git a/src/commands/music/lyrics.rs b/src/commands/music/lyrics.rs index a2a5428..1021ee6 100644 --- a/src/commands/music/lyrics.rs +++ b/src/commands/music/lyrics.rs @@ -10,7 +10,7 @@ use crate::bot::Context; #[poise::command(slash_command, guild_only)] pub async fn lyrics(ctx: Context<'_>) -> Result<()> { let manager = ctx.data(); - let guild = ctx.guild().expect("poise lied to me").id; + let guild = ctx.guild_id().expect("poise lied to me"); let Some(session) = manager.get_session(SessionQuery::Guild(guild)) else { ctx.send( diff --git a/src/commands/music/playing.rs b/src/commands/music/playing.rs index b6d46f4..45f31d8 100644 --- a/src/commands/music/playing.rs +++ b/src/commands/music/playing.rs @@ -10,7 +10,7 @@ use crate::bot::Context; #[poise::command(slash_command, guild_only)] pub async fn playing(ctx: Context<'_>) -> Result<()> { let manager = ctx.data(); - let guild = ctx.guild().expect("poise lied to me").id; + let guild = ctx.guild_id().expect("poise lied to me"); let Some(session) = manager.get_session(SessionQuery::Guild(guild)) else { ctx.send( diff --git a/src/commands/music/stop.rs b/src/commands/music/stop.rs index 90e3a2b..e044fe8 100644 --- a/src/commands/music/stop.rs +++ b/src/commands/music/stop.rs @@ -9,7 +9,7 @@ use crate::bot::Context; #[poise::command(slash_command, guild_only)] pub async fn stop(ctx: Context<'_>) -> Result<(), Error> { let manager = ctx.data(); - let guild = ctx.guild().expect("poise lied to me").id; + let guild = ctx.guild_id().expect("poise lied to me"); let Some(session) = manager.get_session(SessionQuery::Guild(guild)) else { ctx.send( From c7cb26a1f8377ca0622a0e8861d494abda39dc15 Mon Sep 17 00:00:00 2001 From: DaXcess Date: Mon, 19 Aug 2024 10:50:17 +0200 Subject: [PATCH 02/11] Add update behavior settings to /playing --- spoticord_player/src/info.rs | 7 ++ spoticord_session/src/lib.rs | 21 ++++- spoticord_session/src/playback_embed.rs | 117 +++++++++++++++++++----- src/commands/music/playing.rs | 14 ++- 4 files changed, 129 insertions(+), 30 deletions(-) diff --git a/spoticord_player/src/info.rs b/spoticord_player/src/info.rs index 62bf692..32facec 100644 --- a/spoticord_player/src/info.rs +++ b/spoticord_player/src/info.rs @@ -65,6 +65,13 @@ impl PlaybackInfo { } } + pub fn album_name(&self) -> Option { + match &self.audio_item.unique_fields { + UniqueFields::Episode { .. } => None, + UniqueFields::Track { album, .. } => Some(album.to_string()), + } + } + pub fn thumbnail(&self) -> String { self.audio_item .covers diff --git a/spoticord_session/src/lib.rs b/spoticord_session/src/lib.rs index f77c6f3..7b86285 100644 --- a/spoticord_session/src/lib.rs +++ b/spoticord_session/src/lib.rs @@ -31,7 +31,11 @@ pub enum SessionCommand { GetPlayer(oneshot::Sender), GetActive(oneshot::Sender), - CreatePlaybackEmbed(SessionHandle, CommandInteraction), + CreatePlaybackEmbed( + SessionHandle, + CommandInteraction, + playback_embed::UpdateBehavior, + ), CreateLyricsEmbed(SessionHandle, CommandInteraction), Reactivate(UserId, oneshot::Sender>), @@ -207,8 +211,8 @@ impl Session { SessionCommand::GetPlayer(sender) => _ = sender.send(self.player.clone()), SessionCommand::GetActive(sender) => _ = sender.send(self.active), - SessionCommand::CreatePlaybackEmbed(handle, interaction) => { - match PlaybackEmbed::create(self, handle, interaction).await { + SessionCommand::CreatePlaybackEmbed(handle, interaction, behavior) => { + match PlaybackEmbed::create(self, handle, interaction, behavior).await { Ok(Some(playback_embed)) => { self.playback_embed = Some(playback_embed); } @@ -274,8 +278,10 @@ impl Session { PlayerEvent::TrackChanged(_) => {} } + let force_edit = matches!(event, PlayerEvent::TrackChanged(_)); + if let Some(playback_embed) = &self.playback_embed { - if playback_embed.invoke_update().await.is_err() { + if playback_embed.invoke_update(force_edit).await.is_err() { self.playback_embed = None; } } @@ -457,11 +463,16 @@ impl SessionHandle { /// Create a playback embed as a response to an interaction /// /// This playback embed will automatically update when certain events happen - pub async fn create_playback_embed(&self, interaction: CommandInteraction) -> Result<()> { + pub async fn create_playback_embed( + &self, + interaction: CommandInteraction, + behavior: playback_embed::UpdateBehavior, + ) -> Result<()> { self.commands .send(SessionCommand::CreatePlaybackEmbed( self.clone(), interaction, + behavior, )) .await?; diff --git a/spoticord_session/src/playback_embed.rs b/spoticord_session/src/playback_embed.rs index 82bd0d5..2070d03 100644 --- a/spoticord_session/src/playback_embed.rs +++ b/spoticord_session/src/playback_embed.rs @@ -1,11 +1,12 @@ use anyhow::{anyhow, Result}; use log::{error, trace}; +use poise::ChoiceParameter; use serenity::{ all::{ ButtonStyle, CommandInteraction, ComponentInteraction, ComponentInteractionCollector, Context, CreateActionRow, CreateButton, CreateEmbed, CreateEmbedAuthor, CreateEmbedFooter, CreateInteractionResponse, CreateInteractionResponseFollowup, - CreateInteractionResponseMessage, EditMessage, Message, User, + CreateInteractionResponseMessage, CreateMessage, EditMessage, Message, User, }, futures::StreamExt, }; @@ -18,7 +19,25 @@ use crate::{Session, SessionHandle}; #[derive(Debug)] pub enum Command { - InvokeUpdate, + InvokeUpdate(bool), +} + +#[derive(Debug, Default, ChoiceParameter)] +pub enum UpdateBehavior { + #[default] + Default, + Static, + Pinned, +} + +impl UpdateBehavior { + pub fn is_static(&self) -> bool { + matches!(self, Self::Static) + } + + pub fn is_pinned(&self) -> bool { + matches!(self, Self::Pinned) + } } pub struct PlaybackEmbed { @@ -29,6 +48,8 @@ pub struct PlaybackEmbed { last_update: Instant, update_in: Option, + force_edit: bool, + update_behavior: UpdateBehavior, rx: mpsc::Receiver, } @@ -38,6 +59,7 @@ impl PlaybackEmbed { session: &Session, handle: SessionHandle, interaction: CommandInteraction, + update_behavior: UpdateBehavior, ) -> Result> { let ctx = session.context.clone(); @@ -69,6 +91,11 @@ impl PlaybackEmbed { ) .await?; + // If this is a static embed, we don't need to return any handles + if update_behavior.is_static() { + return Ok(None); + } + // Retrieve message instead of editing interaction response, as those tokens are only valid for 15 minutes let message = interaction.get_response(&ctx).await?; @@ -84,6 +111,8 @@ impl PlaybackEmbed { message, last_update: Instant::now(), update_in: None, + force_edit: false, + update_behavior, rx, }; @@ -121,7 +150,7 @@ impl PlaybackEmbed { tokio::time::sleep(update_in).await; } }, if self.update_in.is_some() => { - if self.update_embed().await.is_break() { + if self.update_embed(self.force_edit).await.is_break() { break; } } @@ -133,15 +162,16 @@ impl PlaybackEmbed { trace!("Received command: {command:?}"); match command { - Command::InvokeUpdate => { + Command::InvokeUpdate(force_edit) => { if self.last_update.elapsed() < Duration::from_secs(2) { if self.update_in.is_some() { return ControlFlow::Continue(()); } self.update_in = Some(Duration::from_secs(2) - self.last_update.elapsed()); + self.force_edit = force_edit; } else { - self.update_embed().await?; + self.update_embed(force_edit).await?; } } } @@ -216,7 +246,7 @@ impl PlaybackEmbed { Ok((player, playback_info, owner)) } - async fn update_embed(&mut self) -> ControlFlow<(), ()> { + async fn update_embed(&mut self, force_edit: bool) -> ControlFlow<(), ()> { self.update_in = None; let Ok(owner) = self.session.owner().await else { @@ -246,20 +276,45 @@ impl PlaybackEmbed { } }; - if let Err(why) = self - .message - .edit( - &self.ctx, - EditMessage::new() - .embed(build_embed(&playback_info, &owner)) - .components(vec![build_buttons(self.id, playback_info.playing())]), - ) - .await - { - error!("Failed to update playback embed: {why}"); + let should_pin = !force_edit && self.update_behavior.is_pinned(); - return ControlFlow::Break(()); - }; + if should_pin { + self.message.delete(&self.ctx).await.ok(); + + match self + .message + .channel_id + .send_message( + &self.ctx, + CreateMessage::new() + .embed(build_embed(&playback_info, &owner)) + .components(vec![build_buttons(self.id, playback_info.playing())]), + ) + .await + { + Ok(message) => self.message = message, + Err(why) => { + error!("Failed to update playback embed: {why}"); + + return ControlFlow::Break(()); + } + }; + } else { + if let Err(why) = self + .message + .edit( + &self.ctx, + EditMessage::new() + .embed(build_embed(&playback_info, &owner)) + .components(vec![build_buttons(self.id, playback_info.playing())]), + ) + .await + { + error!("Failed to update playback embed: {why}"); + + return ControlFlow::Break(()); + } + } self.last_update = Instant::now(); @@ -267,6 +322,18 @@ impl PlaybackEmbed { } async fn update_not_playing(&mut self) -> Result<()> { + // If pinned, try to delete old message and send new one + if self.update_behavior.is_pinned() { + self.message.delete(&self.ctx).await.ok(); + self.message = self + .message + .channel_id + .send_message(&self.ctx, CreateMessage::new().embed(not_playing_embed())) + .await?; + + return Ok(()); + } + self.message .edit(&self.ctx, EditMessage::new().embed(not_playing_embed())) .await?; @@ -284,8 +351,8 @@ impl PlaybackEmbedHandle { !self.tx.is_closed() } - pub async fn invoke_update(&self) -> Result<()> { - self.tx.send(Command::InvokeUpdate).await?; + pub async fn invoke_update(&self, force_edit: bool) -> Result<()> { + self.tx.send(Command::InvokeUpdate(force_edit)).await?; Ok(()) } @@ -331,7 +398,13 @@ fn build_embed(playback_info: &PlaybackInfo, owner: &User) -> CreateEmbed { .collect::>() .join(", "); - description += &format!("By {artists}\n\n"); + description += &format!("By {artists}\n"); + } + + if let Some(album_name) = playback_info.album_name() { + description += &format!("In album: **{album_name}**\n\n"); + } else { + description += "\n"; } if let Some(show_name) = playback_info.show_name() { diff --git a/src/commands/music/playing.rs b/src/commands/music/playing.rs index 45f31d8..2ce81b1 100644 --- a/src/commands/music/playing.rs +++ b/src/commands/music/playing.rs @@ -1,14 +1,19 @@ use anyhow::Result; use poise::CreateReply; use serenity::all::CreateEmbed; -use spoticord_session::manager::SessionQuery; +use spoticord_session::{manager::SessionQuery, playback_embed::UpdateBehavior}; use spoticord_utils::discord::Colors; use crate::bot::Context; /// Show details of the current song that is being played #[poise::command(slash_command, guild_only)] -pub async fn playing(ctx: Context<'_>) -> Result<()> { +pub async fn playing( + ctx: Context<'_>, + #[description = "How Spoticord should update this information"] update_behavior: Option< + UpdateBehavior, + >, +) -> Result<()> { let manager = ctx.data(); let guild = ctx.guild_id().expect("poise lied to me"); @@ -33,7 +38,10 @@ pub async fn playing(ctx: Context<'_>) -> Result<()> { }; session - .create_playback_embed(context.interaction.clone()) + .create_playback_embed( + context.interaction.clone(), + update_behavior.unwrap_or_default(), + ) .await?; Ok(()) From ec2db908106c04774f278aebba3cdd8c634cb32a Mon Sep 17 00:00:00 2001 From: DaXcess Date: Mon, 19 Aug 2024 10:51:29 +0200 Subject: [PATCH 03/11] Inverse!!! --- spoticord_session/src/lib.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/spoticord_session/src/lib.rs b/spoticord_session/src/lib.rs index 7b86285..0ace627 100644 --- a/spoticord_session/src/lib.rs +++ b/spoticord_session/src/lib.rs @@ -278,7 +278,7 @@ impl Session { PlayerEvent::TrackChanged(_) => {} } - let force_edit = matches!(event, PlayerEvent::TrackChanged(_)); + let force_edit = !matches!(event, PlayerEvent::TrackChanged(_)); if let Some(playback_embed) = &self.playback_embed { if playback_embed.invoke_update(force_edit).await.is_err() { From bba96969e220e25a71db67dbb4f71ff299273e9d Mon Sep 17 00:00:00 2001 From: DaXcess Date: Mon, 19 Aug 2024 12:05:34 +0200 Subject: [PATCH 04/11] General housekeeping - Updated dependencies - Updated rust version in Dockerfile - Updated workflow scripts to newer versions - Replaced lazy_static with LazyLock - Added descriptions to update behavior --- .github/workflows/build-push.yml | 6 +-- .github/workflows/cargo-clippy.yml | 2 +- CHANGELOG.md | 4 ++ Cargo.lock | 68 ++++++++++++++++--------- Cargo.toml | 2 +- Dockerfile | 2 +- spoticord_audio/Cargo.toml | 2 +- spoticord_config/Cargo.toml | 1 - spoticord_config/src/env.rs | 29 ++++++----- spoticord_database/Cargo.toml | 6 +-- spoticord_player/Cargo.toml | 2 +- spoticord_session/Cargo.toml | 2 +- spoticord_session/src/playback_embed.rs | 5 ++ 13 files changed, 82 insertions(+), 49 deletions(-) diff --git a/.github/workflows/build-push.yml b/.github/workflows/build-push.yml index 093ccda..15e467e 100644 --- a/.github/workflows/build-push.yml +++ b/.github/workflows/build-push.yml @@ -18,15 +18,15 @@ jobs: runs-on: ubuntu-latest steps: - name: Checkout code - uses: actions/checkout@v3 + uses: actions/checkout@v4 - name: Set up Docker buildx id: buildx - uses: docker/setup-buildx-action@v2 + uses: docker/setup-buildx-action@v3 - name: Login to GitHub's container registry if: github.event_name != 'pull_request' - uses: docker/login-action@v2 + uses: docker/login-action@v3 with: registry: ghcr.io username: ${{ github.repository_owner }} diff --git a/.github/workflows/cargo-clippy.yml b/.github/workflows/cargo-clippy.yml index b053994..0a6bf61 100644 --- a/.github/workflows/cargo-clippy.yml +++ b/.github/workflows/cargo-clippy.yml @@ -13,7 +13,7 @@ jobs: steps: - name: Checkout code - uses: actions/checkout@v2 + uses: actions/checkout@v4 - name: Set up Rust uses: actions-rs/toolchain@v1 diff --git a/CHANGELOG.md b/CHANGELOG.md index 1937c8c..e06cb0d 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -2,8 +2,12 @@ ## 2.2.1 | TBD +- Added new option: `/playing` can now receive an updating behavior parameter - Fixed a bug where uncached guilds would panic the bot - Fixed small issue with embed styling +- Updated to Rust 1.80.1 (from 1.79.0) +- Removed `lazy_static` in favor of `LazyLock` (Rust 1.80.0+ feature) +- Updated `diesel` and addons to latest versions ## 2.2.0 | August 13th 2024 diff --git a/Cargo.lock b/Cargo.lock index 6c808fe..3576eff 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -592,14 +592,12 @@ checksum = "e8566979429cf69b49a5c740c60791108e86440e8be149bbea4fe54d2c32d6e2" [[package]] name = "deadpool" -version = "0.9.5" +version = "0.12.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "421fe0f90f2ab22016f32a9881be5134fdd71c65298917084b0c7477cbc3856e" +checksum = "6541a3916932fe57768d4be0b1ffb5ec7cbf74ca8c903fdfd5c0fe8aa958f0ed" dependencies = [ - "async-trait", "deadpool-runtime", "num_cpus", - "retain_mut", "tokio", ] @@ -681,22 +679,23 @@ dependencies = [ [[package]] name = "diesel" -version = "2.1.6" +version = "2.2.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ff236accb9a5069572099f0b350a92e9560e8e63a9b8d546162f4a5e03026bb2" +checksum = "bf97ee7261bb708fa3402fa9c17a54b70e90e3cb98afb3dc8999d5512cb03f94" dependencies = [ "bitflags 2.6.0", "byteorder", "chrono", "diesel_derives", "itoa", + "pq-sys", ] [[package]] name = "diesel-async" -version = "0.4.1" +version = "0.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "acada1517534c92d3f382217b485db8a8638f111b0e3f2a2a8e26165050f77be" +checksum = "fcb799bb6f8ca6a794462125d7b8983b0c86e6c93a33a9c55934a4a5de4409d3" dependencies = [ "async-trait", "deadpool", @@ -709,9 +708,9 @@ dependencies = [ [[package]] name = "diesel_async_migrations" -version = "0.12.0" +version = "0.13.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a700d6b83a17973b94d3065970fd2b36f1036c3fe08adcbdce1c9beb8fb25553" +checksum = "377dd8e9d0fdab3dbb66236f8f71206b98e121991449f3b363a19d7948333160" dependencies = [ "diesel", "diesel-async", @@ -732,11 +731,12 @@ dependencies = [ [[package]] name = "diesel_derives" -version = "2.1.4" +version = "2.2.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "14701062d6bed917b5c7103bdffaee1e4609279e240488ad24e7bd979ca6866c" +checksum = "d6ff2be1e7312c858b2ef974f5c7089833ae57b5311b334b30923af58e5718d8" dependencies = [ "diesel_table_macro_syntax", + "dsl_auto_type", "proc-macro2", "quote", "syn 2.0.72", @@ -744,9 +744,9 @@ dependencies = [ [[package]] name = "diesel_table_macro_syntax" -version = "0.1.0" +version = "0.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fc5557efc453706fed5e4fa85006fe9817c224c3f480a34c7e5959fd700921c5" +checksum = "209c735641a413bc68c4923a9d6ad4bcb3ca306b794edaa7eb0b3228a99ffb25" dependencies = [ "syn 2.0.72", ] @@ -779,6 +779,20 @@ version = "0.15.7" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "1aaf95b3e5c8f23aa320147307562d361db0ae0d51242340f558153b4eb2439b" +[[package]] +name = "dsl_auto_type" +version = "0.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c5d9abe6314103864cc2d8901b7ae224e0ab1a103a0a416661b4097b0779b607" +dependencies = [ + "darling 0.20.10", + "either", + "heck 0.5.0", + "proc-macro2", + "quote", + "syn 2.0.72", +] + [[package]] name = "either" version = "1.13.0" @@ -2515,6 +2529,15 @@ dependencies = [ "zerocopy", ] +[[package]] +name = "pq-sys" +version = "0.6.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a24ff9e4cf6945c988f0db7005d87747bf72864965c3529d259ad155ac41d584" +dependencies = [ + "vcpkg", +] + [[package]] name = "primal-check" version = "0.3.4" @@ -2904,12 +2927,6 @@ dependencies = [ "winreg 0.52.0", ] -[[package]] -name = "retain_mut" -version = "0.1.9" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4389f1d5789befaf6029ebd9f7dac4af7f7e3d61b69d4f30e2ac02b57e7712b0" - [[package]] name = "ring" version = "0.16.20" @@ -3753,7 +3770,6 @@ dependencies = [ name = "spoticord_config" version = "2.2.0" dependencies = [ - "lazy_static", "rspotify", "serenity", ] @@ -4217,9 +4233,9 @@ checksum = "1f3ccbac311fea05f86f61904b462b55fb3df8837a366dfc601a0161d0532f20" [[package]] name = "tokio" -version = "1.39.2" +version = "1.39.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "daa4fb1bc778bd6f04cbfc4bb2d06a7396a8f299dc33ea1900cedaa316f467b1" +checksum = "9babc99b9923bfa4804bd74722ff02c0381021eafa4db9949217e3be8e84fff5" dependencies = [ "backtrace", "bytes", @@ -4792,6 +4808,12 @@ dependencies = [ "ryu", ] +[[package]] +name = "vcpkg" +version = "0.2.15" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "accd4ea62f7bb7a82fe23066fb0957d48ef677f6eeb8215f372f52e48bb32426" + [[package]] name = "vergen" version = "8.3.2" diff --git a/Cargo.toml b/Cargo.toml index deee9a0..1ce2152 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -38,7 +38,7 @@ log = "0.4.22" poise = "0.6.1" serenity = "0.12.2" songbird = { version = "0.4.3", features = ["simd-json"] } -tokio = { version = "1.39.2", features = ["full"] } +tokio = { version = "1.39.3", features = ["full"] } [profile.release] opt-level = 3 diff --git a/Dockerfile b/Dockerfile index 4346d68..24df520 100644 --- a/Dockerfile +++ b/Dockerfile @@ -1,5 +1,5 @@ # Builder -FROM --platform=linux/amd64 rust:1.79.0-buster AS builder +FROM --platform=linux/amd64 rust:1.80.1-slim AS builder WORKDIR /app diff --git a/spoticord_audio/Cargo.toml b/spoticord_audio/Cargo.toml index 8f27a75..b787a4e 100644 --- a/spoticord_audio/Cargo.toml +++ b/spoticord_audio/Cargo.toml @@ -8,5 +8,5 @@ edition = "2021" [dependencies] librespot = { git = "https://github.com/SpoticordMusic/librespot.git", version = "0.5.0-dev", default-features = false } songbird = { version = "0.4.3", features = ["simd-json"] } -tokio = { version = "1.39.2", features = ["sync"], default-features = false } +tokio = { version = "1.39.3", features = ["sync"], default-features = false } zerocopy = "0.7.35" diff --git a/spoticord_config/Cargo.toml b/spoticord_config/Cargo.toml index 86879c1..ef8a227 100644 --- a/spoticord_config/Cargo.toml +++ b/spoticord_config/Cargo.toml @@ -4,7 +4,6 @@ version = "2.2.0" edition = "2021" [dependencies] -lazy_static = "1.5.0" rspotify = { version = "0.13.2", default-features = false, features = [ "client-reqwest", "reqwest-rustls-tls", diff --git a/spoticord_config/src/env.rs b/spoticord_config/src/env.rs index 4aecea4..651bde9 100644 --- a/spoticord_config/src/env.rs +++ b/spoticord_config/src/env.rs @@ -1,14 +1,17 @@ -use lazy_static::lazy_static; +use std::sync::LazyLock; -lazy_static! { - pub static ref DISCORD_TOKEN: String = - std::env::var("DISCORD_TOKEN").expect("missing DISCORD_TOKEN environment variable"); - pub static ref DATABASE_URL: String = - std::env::var("DATABASE_URL").expect("missing DATABASE_URL environment variable"); - pub static ref LINK_URL: String = - std::env::var("LINK_URL").expect("missing LINK_URL environment variable"); - pub static ref SPOTIFY_CLIENT_ID: String = - std::env::var("SPOTIFY_CLIENT_ID").expect("missing SPOTIFY_CLIENT_ID environment variable"); - pub static ref SPOTIFY_CLIENT_SECRET: String = std::env::var("SPOTIFY_CLIENT_SECRET") - .expect("missing SPOTIFY_CLIENT_SECRET environment variable"); -} +pub static DISCORD_TOKEN: LazyLock = LazyLock::new(|| { + std::env::var("DISCORD_TOKEN").expect("missing DISCORD_TOKEN environment variable") +}); +pub static DATABASE_URL: LazyLock = LazyLock::new(|| { + std::env::var("DATABASE_URL").expect("missing DATABASE_URL environment variable") +}); +pub static LINK_URL: LazyLock = + LazyLock::new(|| std::env::var("LINK_URL").expect("missing LINK_URL environment variable")); +pub static SPOTIFY_CLIENT_ID: LazyLock = LazyLock::new(|| { + std::env::var("SPOTIFY_CLIENT_ID").expect("missing SPOTIFY_CLIENT_ID environment variable") +}); +pub static SPOTIFY_CLIENT_SECRET: LazyLock = LazyLock::new(|| { + std::env::var("SPOTIFY_CLIENT_SECRET") + .expect("missing SPOTIFY_CLIENT_SECRET environment variable") +}); diff --git a/spoticord_database/Cargo.toml b/spoticord_database/Cargo.toml index eeee740..a0fe30a 100644 --- a/spoticord_database/Cargo.toml +++ b/spoticord_database/Cargo.toml @@ -6,8 +6,8 @@ edition = "2021" [dependencies] spoticord_config = { path = "../spoticord_config" } -diesel = { version = "2.1.6", features = ["chrono"] } -diesel-async = { version = "0.4.1", features = ["deadpool", "postgres"] } +diesel = { version = "2.2.2", features = ["chrono"] } +diesel-async = { version = "0.5.0", features = ["deadpool", "postgres"] } rspotify = { version = "0.13.2", default-features = false, features = [ "client-reqwest", "reqwest-rustls-tls", @@ -15,4 +15,4 @@ rspotify = { version = "0.13.2", default-features = false, features = [ chrono = "0.4.38" thiserror = "1.0.63" rand = "0.8.5" -diesel_async_migrations = "0.12.0" +diesel_async_migrations = "0.13.0" diff --git a/spoticord_player/Cargo.toml b/spoticord_player/Cargo.toml index 624e910..b567baa 100644 --- a/spoticord_player/Cargo.toml +++ b/spoticord_player/Cargo.toml @@ -11,7 +11,7 @@ spoticord_utils = { path = "../spoticord_utils" } librespot = { git = "https://github.com/SpoticordMusic/librespot.git", version = "0.5.0-dev", default-features = false } songbird = { version = "0.4.3", features = ["simd-json"] } -tokio = { version = "1.39.2", features = ["full"] } +tokio = { version = "1.39.3", features = ["full"] } anyhow = "1.0.86" log = "0.4.22" symphonia = { version = "0.5.4", default-features = false, features = ["pcm"] } diff --git a/spoticord_session/Cargo.toml b/spoticord_session/Cargo.toml index e13754b..6b66a5f 100644 --- a/spoticord_session/Cargo.toml +++ b/spoticord_session/Cargo.toml @@ -11,7 +11,7 @@ spoticord_database = { path = "../spoticord_database" } spoticord_player = { path = "../spoticord_player" } spoticord_utils = { path = "../spoticord_utils" } -tokio = { version = "1.39.2", features = ["full"] } +tokio = { version = "1.39.3", features = ["full"] } librespot = { git = "https://github.com/SpoticordMusic/librespot.git", version = "0.5.0-dev", default-features = false } serenity = "0.12.2" songbird = { version = "0.4.3", features = ["simd-json"] } diff --git a/spoticord_session/src/playback_embed.rs b/spoticord_session/src/playback_embed.rs index 2070d03..1c38482 100644 --- a/spoticord_session/src/playback_embed.rs +++ b/spoticord_session/src/playback_embed.rs @@ -25,8 +25,13 @@ pub enum Command { #[derive(Debug, Default, ChoiceParameter)] pub enum UpdateBehavior { #[default] + #[name = "Automatically update the embed"] Default, + + #[name = "Do not update the embed"] Static, + + #[name = "Re-send the embed after track changes"] Pinned, } From d0acf77f88ae9854be29565f931b35acc124ac07 Mon Sep 17 00:00:00 2001 From: DaXcess Date: Mon, 19 Aug 2024 12:34:51 +0200 Subject: [PATCH 05/11] Fix lpq --- CHANGELOG.md | 1 + Dockerfile | 2 +- spoticord_session/src/playback_embed.rs | 18 +++++++++--------- 3 files changed, 11 insertions(+), 10 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index e06cb0d..d20c576 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -3,6 +3,7 @@ ## 2.2.1 | TBD - Added new option: `/playing` can now receive an updating behavior parameter +- Added album name to `/playing` embed - Fixed a bug where uncached guilds would panic the bot - Fixed small issue with embed styling - Updated to Rust 1.80.1 (from 1.79.0) diff --git a/Dockerfile b/Dockerfile index 24df520..89e7cb5 100644 --- a/Dockerfile +++ b/Dockerfile @@ -5,7 +5,7 @@ WORKDIR /app # Add extra build dependencies here RUN apt-get update && apt-get install -yqq \ - cmake gcc-aarch64-linux-gnu binutils-aarch64-linux-gnu + cmake gcc-aarch64-linux-gnu binutils-aarch64-linux-gnu libpq-dev COPY . . diff --git a/spoticord_session/src/playback_embed.rs b/spoticord_session/src/playback_embed.rs index 1c38482..a6f74f8 100644 --- a/spoticord_session/src/playback_embed.rs +++ b/spoticord_session/src/playback_embed.rs @@ -407,23 +407,23 @@ fn build_embed(playback_info: &PlaybackInfo, owner: &User) -> CreateEmbed { } if let Some(album_name) = playback_info.album_name() { - description += &format!("In album: **{album_name}**\n\n"); - } else { - description += "\n"; + description += &format!("Album: **{album_name}**\n"); } if let Some(show_name) = playback_info.show_name() { - description += &format!("On {show_name}\n\n"); + description += &format!("On {show_name}\n"); } + description += "\n"; + let position = playback_info.current_position(); let index = position * 20 / playback_info.duration(); - description.push_str(if playback_info.playing() { + description += if playback_info.playing() { "▶️ " } else { "⏸️ " - }); + }; for i in 0..20 { if i == index { @@ -433,12 +433,12 @@ fn build_embed(playback_info: &PlaybackInfo, owner: &User) -> CreateEmbed { } } - description.push_str("\n:alarm_clock: "); - description.push_str(&format!( + description += "\n:alarm_clock: "; + description += &format!( "{} / {}", spoticord_utils::time_to_string(position / 1000), spoticord_utils::time_to_string(playback_info.duration() / 1000) - )); + ); CreateEmbed::new() .author( From e4afe7371d20e9f63c650b9e9e96df019f6f9468 Mon Sep 17 00:00:00 2001 From: DaXcess Date: Wed, 21 Aug 2024 11:06:35 +0200 Subject: [PATCH 06/11] Docker --- Dockerfile | 24 +++++++++++++++++++++--- 1 file changed, 21 insertions(+), 3 deletions(-) diff --git a/Dockerfile b/Dockerfile index 89e7cb5..dd45446 100644 --- a/Dockerfile +++ b/Dockerfile @@ -1,12 +1,29 @@ +# This Dockerfile has been specifically crafted to be run on an AMD64 build host, where +# the build should compile for both amd64 and arm64 targets +# +# Building on any other platform, or building for only a single target will be significantly +# slower compared to a platform agnostic Dockerfile, or might not work at all +# +# This has been done to make this file be optimized for use within GitHub Actions, +# as using QEMU to compile takes way too long (multiple hours) + # Builder -FROM --platform=linux/amd64 rust:1.80.1-slim AS builder +FROM --platform=linux/amd64 rust:1.80.1-bullseye AS builder WORKDIR /app # Add extra build dependencies here -RUN apt-get update && apt-get install -yqq \ +RUN apt-get update && apt install -yqq \ cmake gcc-aarch64-linux-gnu binutils-aarch64-linux-gnu libpq-dev +# Manually compile an arm64 build of libpq +RUN wget https://ftp.postgresql.org/pub/source/v16.4/postgresql-16.4.tar.bz2 && \ + tar xjf postgresql-16.4.tar.bz2 && \ + cd postgresql-16.4 && \ + ./configure --host=aarch64-linux-gnu --enable-shared --disable-static --without-readline --without-zlib --without-icu && \ + cd src/interfaces/libpq && \ + make + COPY . . RUN rustup target add x86_64-unknown-linux-gnu aarch64-unknown-linux-gnu @@ -14,7 +31,8 @@ RUN rustup target add x86_64-unknown-linux-gnu aarch64-unknown-linux-gnu # Add `--no-default-features` if you don't want stats collection RUN --mount=type=cache,target=/usr/local/cargo/registry \ --mount=type=cache,target=/app/target \ - cargo build --release --target=x86_64-unknown-linux-gnu --target=aarch64-unknown-linux-gnu && \ + cargo build --release --target=x86_64-unknown-linux-gnu && \ + RUSTFLAGS='-L /app/postgresql-16.4/src/interfaces/libpq -C linker=aarch64-linux-gnu-gcc' cargo build --release --target=aarch64-unknown-linux-gnu && \ # Copy the executables outside of /target as it'll get unmounted after this RUN command cp /app/target/x86_64-unknown-linux-gnu/release/spoticord /app/x86_64 && \ cp /app/target/aarch64-unknown-linux-gnu/release/spoticord /app/aarch64 From eefe2561f2a59eaa000c0ca5db7d6e134e44da6f Mon Sep 17 00:00:00 2001 From: DaXcess Date: Wed, 21 Aug 2024 11:41:40 +0200 Subject: [PATCH 07/11] Update runtime to Debian 12 --- Dockerfile | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Dockerfile b/Dockerfile index dd45446..e3d0755 100644 --- a/Dockerfile +++ b/Dockerfile @@ -8,7 +8,7 @@ # as using QEMU to compile takes way too long (multiple hours) # Builder -FROM --platform=linux/amd64 rust:1.80.1-bullseye AS builder +FROM --platform=linux/amd64 rust:1.80.1-slim AS builder WORKDIR /app @@ -38,7 +38,7 @@ RUN --mount=type=cache,target=/usr/local/cargo/registry \ cp /app/target/aarch64-unknown-linux-gnu/release/spoticord /app/aarch64 # Runtime -FROM debian:buster-slim +FROM debian:bookworm-slim ARG TARGETPLATFORM ENV TARGETPLATFORM=${TARGETPLATFORM} From 3394d495fea426dff30cf4c399458a3cd059f2bb Mon Sep 17 00:00:00 2001 From: DaXcess Date: Wed, 21 Aug 2024 11:57:27 +0200 Subject: [PATCH 08/11] Fix Dockerfile --- Dockerfile | 11 ++++++----- 1 file changed, 6 insertions(+), 5 deletions(-) diff --git a/Dockerfile b/Dockerfile index e3d0755..81f1340 100644 --- a/Dockerfile +++ b/Dockerfile @@ -14,12 +14,13 @@ WORKDIR /app # Add extra build dependencies here RUN apt-get update && apt install -yqq \ - cmake gcc-aarch64-linux-gnu binutils-aarch64-linux-gnu libpq-dev + cmake gcc-aarch64-linux-gnu binutils-aarch64-linux-gnu libpq-dev curl bzip2 # Manually compile an arm64 build of libpq -RUN wget https://ftp.postgresql.org/pub/source/v16.4/postgresql-16.4.tar.bz2 && \ - tar xjf postgresql-16.4.tar.bz2 && \ - cd postgresql-16.4 && \ +ENV PGVER=16.4 +RUN curl -o postgresql.tar.bz2 https://ftp.postgresql.org/pub/source/v${PGVER}/postgresql-${PGVER}.tar.bz2 && \ + tar xjf postgresql.tar.bz2 && \ + cd postgresql-${PGVER} && \ ./configure --host=aarch64-linux-gnu --enable-shared --disable-static --without-readline --without-zlib --without-icu && \ cd src/interfaces/libpq && \ make @@ -32,7 +33,7 @@ RUN rustup target add x86_64-unknown-linux-gnu aarch64-unknown-linux-gnu RUN --mount=type=cache,target=/usr/local/cargo/registry \ --mount=type=cache,target=/app/target \ cargo build --release --target=x86_64-unknown-linux-gnu && \ - RUSTFLAGS='-L /app/postgresql-16.4/src/interfaces/libpq -C linker=aarch64-linux-gnu-gcc' cargo build --release --target=aarch64-unknown-linux-gnu && \ + RUSTFLAGS="-L /app/postgresql-${PGVER}/src/interfaces/libpq -C linker=aarch64-linux-gnu-gcc" cargo build --release --target=aarch64-unknown-linux-gnu && \ # Copy the executables outside of /target as it'll get unmounted after this RUN command cp /app/target/x86_64-unknown-linux-gnu/release/spoticord /app/x86_64 && \ cp /app/target/aarch64-unknown-linux-gnu/release/spoticord /app/aarch64 From 288811074f80975699b8cce670c9fefb7a759794 Mon Sep 17 00:00:00 2001 From: DaXcess Date: Wed, 21 Aug 2024 12:29:27 +0200 Subject: [PATCH 09/11] Bump MSRV --- CHANGELOG.md | 3 ++- COMPILING.md | 2 +- Cargo.toml | 2 +- 3 files changed, 4 insertions(+), 3 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index d20c576..b0d8c03 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -7,8 +7,9 @@ - Fixed a bug where uncached guilds would panic the bot - Fixed small issue with embed styling - Updated to Rust 1.80.1 (from 1.79.0) -- Removed `lazy_static` in favor of `LazyLock` (Rust 1.80.0+ feature) - Updated `diesel` and addons to latest versions +- Removed `lazy_static` in favor of `LazyLock` (Rust 1.80.0+ feature) +- Bumped MSRV to 1.80.0 due to the introduction of `LazyLock` ## 2.2.0 | August 13th 2024 diff --git a/COMPILING.md b/COMPILING.md index 88d2d5e..9c67f48 100644 --- a/COMPILING.md +++ b/COMPILING.md @@ -86,4 +86,4 @@ cargo build [--release] --features stats # MSRV -The current minimum supported rust version is `1.75.0` _(Checked with `cargo-msrv`)_. +The current minimum supported rust version is `1.80.0` _(Checked with `cargo-msrv`)_. diff --git a/Cargo.toml b/Cargo.toml index 1ce2152..24f4c3a 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -2,7 +2,7 @@ name = "spoticord" version = "2.2.1" edition = "2021" -rust-version = "1.75.0" +rust-version = "1.80.0" [[bin]] name = "spoticord" From 2651966ad84f472276b4109a732ba66cec50ceae Mon Sep 17 00:00:00 2001 From: DaXcess Date: Thu, 22 Aug 2024 10:00:47 +0200 Subject: [PATCH 10/11] Update CHANGELOG.md --- CHANGELOG.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index b0d8c03..825608c 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,6 +1,6 @@ # Changelog -## 2.2.1 | TBD +## 2.2.1 | August 22nd 2024 - Added new option: `/playing` can now receive an updating behavior parameter - Added album name to `/playing` embed From 9c7d29b54611a7c486e233bcf555d9be2d8075ca Mon Sep 17 00:00:00 2001 From: DaXcess Date: Thu, 22 Aug 2024 10:05:49 +0200 Subject: [PATCH 11/11] Happy clippy --- spoticord_session/src/playback_embed.rs | 26 ++++++++++++------------- 1 file changed, 12 insertions(+), 14 deletions(-) diff --git a/spoticord_session/src/playback_embed.rs b/spoticord_session/src/playback_embed.rs index a6f74f8..fc5d8c6 100644 --- a/spoticord_session/src/playback_embed.rs +++ b/spoticord_session/src/playback_embed.rs @@ -304,21 +304,19 @@ impl PlaybackEmbed { return ControlFlow::Break(()); } }; - } else { - if let Err(why) = self - .message - .edit( - &self.ctx, - EditMessage::new() - .embed(build_embed(&playback_info, &owner)) - .components(vec![build_buttons(self.id, playback_info.playing())]), - ) - .await - { - error!("Failed to update playback embed: {why}"); + } else if let Err(why) = self + .message + .edit( + &self.ctx, + EditMessage::new() + .embed(build_embed(&playback_info, &owner)) + .components(vec![build_buttons(self.id, playback_info.playing())]), + ) + .await + { + error!("Failed to update playback embed: {why}"); - return ControlFlow::Break(()); - } + return ControlFlow::Break(()); } self.last_update = Instant::now();