diff --git a/CHANGELOG.md b/CHANGELOG.md index e01de79..c67aa04 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -8,6 +8,7 @@ In this section of the change log all current changes that have been made since * Added aarch64 support * Added cross compilation to Github Actions * Added `dev` branch to Github Actions +* Removed hardcoded URL in the /join command ## 2.1.1 | September 23rd 2023 Reduced the amount of CPU that the bot uses from ~15%-25% per user to 1%-2% per user (percentage per core, benched on an AMD Ryzen 9 5950X). diff --git a/Cargo.lock b/Cargo.lock index bd4ce83..beace56 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -2269,6 +2269,7 @@ dependencies = [ "dotenv", "env_logger 0.10.0", "hex", + "lazy_static", "librespot", "log", "protobuf", @@ -2280,7 +2281,6 @@ dependencies = [ "serenity", "songbird", "thiserror", - "time", "tokio", "zerocopy 0.7.5", ] diff --git a/Cargo.toml b/Cargo.toml index 64539d2..bf9151f 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -16,6 +16,7 @@ anyhow = "1.0.75" dotenv = "0.15.0" env_logger = "0.10.0" hex = "0.4.3" +lazy_static = "1.4.0" librespot = { version = "0.4.2", default-features = false } log = "0.4.20" protobuf = "2.28.0" @@ -27,7 +28,6 @@ serde_json = "1.0.107" serenity = { version = "0.11.6", features = ["framework", "cache", "standard_framework"], default-features = false } songbird = "0.3.2" thiserror = "1.0.48" -time = "0.3.28" tokio = { version = "1.32.0", features = ["rt", "full"] } zerocopy = "0.7.5" diff --git a/src/bot/commands/core/link.rs b/src/bot/commands/core/link.rs index 14b238f..bf606d6 100644 --- a/src/bot/commands/core/link.rs +++ b/src/bot/commands/core/link.rs @@ -8,6 +8,7 @@ use serenity::{ use crate::{ bot::commands::{respond_message, CommandOutput}, + consts::SPOTICORD_ACCOUNTS_URL, database::{Database, DatabaseError}, utils::embed::{EmbedBuilder, Status}, }; @@ -39,8 +40,11 @@ pub fn command(ctx: Context, command: ApplicationCommandInteraction) -> CommandO } if let Ok(request) = database.get_user_request(command.user.id.to_string()).await { - let base = std::env::var("SPOTICORD_ACCOUNTS_URL").expect("to be present"); - let link = format!("{}/spotify/{}", base, request.token); + let link = format!( + "{}/spotify/{}", + SPOTICORD_ACCOUNTS_URL.as_str(), + request.token + ); respond_message( &ctx, @@ -106,8 +110,11 @@ pub fn command(ctx: Context, command: ApplicationCommandInteraction) -> CommandO .await { Ok(request) => { - let base = std::env::var("SPOTICORD_ACCOUNTS_URL").expect("to be present"); - let link = format!("{}/spotify/{}", base, request.token); + let link = format!( + "{}/spotify/{}", + SPOTICORD_ACCOUNTS_URL.as_str(), + request.token + ); respond_message( &ctx, diff --git a/src/bot/commands/music/join.rs b/src/bot/commands/music/join.rs index fcb5fcc..8c5cf5f 100644 --- a/src/bot/commands/music/join.rs +++ b/src/bot/commands/music/join.rs @@ -7,6 +7,7 @@ use serenity::{ use crate::{ bot::commands::{defer_message, respond_message, update_message, CommandOutput}, + consts::SPOTICORD_ACCOUNTS_URL, session::manager::{SessionCreateError, SessionManager}, utils::embed::{EmbedBuilder, Status}, }; @@ -241,7 +242,7 @@ pub fn command(ctx: Context, command: ApplicationCommandInteraction) -> CommandO &command, EmbedBuilder::new() .title("Cannot join voice channel") - .description("You need to link your Spotify account. Use or go to [the accounts website](https://account.spoticord.com/) to get started.") + .description(format!("You need to link your Spotify account. Use or go to [the accounts website]({}) to get started.", SPOTICORD_ACCOUNTS_URL.as_str())) .status(Status::Error) .build(), ) @@ -255,7 +256,7 @@ pub fn command(ctx: Context, command: ApplicationCommandInteraction) -> CommandO &command, EmbedBuilder::new() .title("Cannot join voice channel") - .description("Spoticord no longer has access to your Spotify account. Use or go to [the accounts website](https://account.spoticord.com/) to relink your Spotify account.") + .description(format!("Spoticord no longer has access to your Spotify account. Use or go to [the accounts website]({}) to relink your Spotify account.", SPOTICORD_ACCOUNTS_URL.as_str())) .status(Status::Error) .build(), ).await; diff --git a/src/bot/commands/music/playing.rs b/src/bot/commands/music/playing.rs index 9dbe5ad..f0aae85 100644 --- a/src/bot/commands/music/playing.rs +++ b/src/bot/commands/music/playing.rs @@ -30,20 +30,22 @@ pub const NAME: &str = "playing"; pub fn command(ctx: Context, command: ApplicationCommandInteraction) -> CommandOutput { Box::pin(async move { - let not_playing = async { - respond_message( - &ctx, - &command, - EmbedBuilder::new() - .title("Cannot get track info") - .icon_url("https://spoticord.com/forbidden.png") - .description("I'm currently not playing any music in this server") - .status(Status::Error) - .build(), - true, - ) - .await; - }; + macro_rules! not_playing { + () => { + respond_message( + &ctx, + &command, + EmbedBuilder::new() + .title("Cannot get track info") + .icon_url("https://spoticord.com/forbidden.png") + .description("I'm currently not playing any music in this server") + .status(Status::Error) + .build(), + true, + ) + .await; + }; + } let data = ctx.data.read().await; let session_manager = data @@ -51,62 +53,50 @@ pub fn command(ctx: Context, command: ApplicationCommandInteraction) -> CommandO .expect("to contain a value") .clone(); - let session = match session_manager + let Some(session) = session_manager .get_session(command.guild_id.expect("to contain a value")) .await - { - Some(session) => session, - None => { - not_playing.await; + else { + not_playing!(); - return; - } + return; }; - let owner = match session.owner().await { - Some(owner) => owner, - None => { - not_playing.await; + let Some(owner) = session.owner().await else { + not_playing!(); - return; - } + return; }; // Get Playback Info from session - let pbi = match session.playback_info().await { - Some(pbi) => pbi, - None => { - not_playing.await; + let Some(pbi) = session.playback_info().await else { + not_playing!(); - return; - } + return; }; // Get owner of session - let owner = match utils::discord::get_user(&ctx, owner).await { - Some(user) => user, - None => { - // This shouldn't happen + let Some(owner) = utils::discord::get_user(&ctx, owner).await else { + // This shouldn't happen - error!("Could not find user with ID: {owner}"); + error!("Could not find user with ID: {owner}"); - respond_message( - &ctx, - &command, - EmbedBuilder::new() - .title("[INTERNAL ERROR] Cannot get track info") - .description(format!( - "Could not find user with ID `{}`\nThis is an issue with the bot!", - owner - )) - .status(Status::Error) - .build(), - true, - ) - .await; + respond_message( + &ctx, + &command, + EmbedBuilder::new() + .title("[INTERNAL ERROR] Cannot get track info") + .description(format!( + "Could not find user with ID `{}`\nThis is an issue with the bot!", + owner + )) + .status(Status::Error) + .build(), + true, + ) + .await; - return; - } + return; }; // Get metadata @@ -188,48 +178,39 @@ pub fn component(ctx: Context, mut interaction: MessageComponentInteraction) -> .clone(); // Check if session still exists - let mut session = match session_manager + let Some(mut session) = session_manager .get_session(interaction.guild_id.expect("to contain a value")) .await - { - Some(session) => session, - None => { - error_edit( - "Cannot perform action", - "I'm currently not playing any music in this server", - ) - .await; + else { + error_edit( + "Cannot perform action", + "I'm currently not playing any music in this server", + ) + .await; - return; - } + return; }; // Check if the session contains an owner - let owner = match session.owner().await { - Some(owner) => owner, - None => { - error_edit( - "Cannot change playback state", - "I'm currently not playing any music in this server", - ) - .await; + let Some(owner) = session.owner().await else { + error_edit( + "Cannot change playback state", + "I'm currently not playing any music in this server", + ) + .await; - return; - } + return; }; // Get Playback Info from session - let pbi = match session.playback_info().await { - Some(pbi) => pbi, - None => { - error_edit( - "Cannot change playback state", - "I'm currently not playing any music in this server", - ) - .await; + let Some(pbi) = session.playback_info().await else { + error_edit( + "Cannot change playback state", + "I'm currently not playing any music in this server", + ) + .await; - return; - } + return; }; // Check if the user is the owner of the session @@ -244,30 +225,27 @@ pub fn component(ctx: Context, mut interaction: MessageComponentInteraction) -> } // Get owner of session - let owner = match utils::discord::get_user(&ctx, owner).await { - Some(user) => user, - None => { - // This shouldn't happen + let Some(owner) = utils::discord::get_user(&ctx, owner).await else { + // This shouldn't happen - error!("Could not find user with ID: {owner}"); + error!("Could not find user with ID: {owner}"); - respond_component_message( - &ctx, - &interaction, - EmbedBuilder::new() - .title("[INTERNAL ERROR] Cannot get track info") - .description(format!( - "Could not find user with ID `{}`\nThis is an issue with the bot!", - owner - )) - .status(Status::Error) - .build(), - true, - ) - .await; + respond_component_message( + &ctx, + &interaction, + EmbedBuilder::new() + .title("[INTERNAL ERROR] Cannot get track info") + .description(format!( + "Could not find user with ID `{}`\nThis is an issue with the bot!", + owner + )) + .status(Status::Error) + .build(), + true, + ) + .await; - return; - } + return; }; // Send the desired command to the session @@ -370,34 +348,28 @@ async fn update_embed(interaction: &mut MessageComponentInteraction, ctx: &Conte .clone(); // Check if session still exists - let session = match session_manager + let Some(session) = session_manager .get_session(interaction.guild_id.expect("to contain a value")) .await - { - Some(session) => session, - None => { - error_edit( - "Cannot perform action", - "I'm currently not playing any music in this server", - ) - .await; + else { + error_edit( + "Cannot perform action", + "I'm currently not playing any music in this server", + ) + .await; - return; - } + return; }; // Get Playback Info from session - let pbi = match session.playback_info().await { - Some(pbi) => pbi, - None => { - error_edit( - "Cannot change playback state", - "I'm currently not playing any music in this server", - ) - .await; + let Some(pbi) = session.playback_info().await else { + error_edit( + "Cannot change playback state", + "I'm currently not playing any music in this server", + ) + .await; - return; - } + return; }; let (title, description, thumbnail) = get_metadata(&pbi); diff --git a/src/consts.rs b/src/consts.rs index c050977..d3326bf 100644 --- a/src/consts.rs +++ b/src/consts.rs @@ -1,3 +1,5 @@ +use lazy_static::lazy_static; + #[cfg(not(debug_assertions))] pub const VERSION: &str = env!("CARGO_PKG_VERSION"); @@ -8,3 +10,18 @@ pub const MOTD: &str = "some good 'ol music"; /// The time it takes for Spoticord to disconnect when no music is being played pub const DISCONNECT_TIME: u64 = 5 * 60; + +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 SPOTICORD_ACCOUNTS_URL: String = std::env::var("SPOTICORD_ACCOUNTS_URL") + .expect("missing SPOTICORD_ACCOUNTS_URL environment variable"); +} + +#[cfg(feature = "stats")] +lazy_static! { + pub static ref KV_URL: String = + std::env::var("KV_URL").expect("missing KV_URL environment variable"); +} diff --git a/src/main.rs b/src/main.rs index bc4cca2..f8b6f01 100644 --- a/src/main.rs +++ b/src/main.rs @@ -1,10 +1,18 @@ use dotenv::dotenv; -use crate::{bot::commands::CommandManager, database::Database, session::manager::SessionManager}; +#[cfg(feature = "stats")] +use crate::consts::KV_URL; + +use crate::{ + bot::commands::CommandManager, + consts::{DATABASE_URL, DISCORD_TOKEN, MOTD}, + database::Database, + session::manager::SessionManager, +}; use log::*; use serenity::{framework::StandardFramework, prelude::GatewayIntents, Client}; use songbird::SerenityInit; -use std::{any::Any, env, process::exit}; +use std::{any::Any, process::exit}; #[cfg(unix)] use tokio::signal::unix::SignalKind; @@ -41,7 +49,7 @@ async fn main() { env_logger::init(); info!("It's a good day"); - info!(" - Spoticord {}", time::OffsetDateTime::now_utc().year()); + info!(" - Spoticord, {}", MOTD); let result = dotenv(); @@ -54,19 +62,14 @@ async fn main() { warn!("No .env file found, expecting all necessary environment variables"); } - let token = env::var("DISCORD_TOKEN").expect("a token in the environment"); - let db_url = env::var("DATABASE_URL").expect("a database URL in the environment"); - #[cfg(feature = "stats")] - let stats_manager = - StatsManager::new(env::var("KV_URL").expect("a redis URL in the environment")) - .expect("Failed to connect to redis"); + let stats_manager = StatsManager::new(KV_URL.as_str()).expect("Failed to connect to redis"); let session_manager = SessionManager::new(); // Create client let mut client = Client::builder( - token, + DISCORD_TOKEN.as_str(), GatewayIntents::GUILDS | GatewayIntents::GUILD_VOICE_STATES, ) .event_handler(crate::bot::events::Handler) @@ -78,7 +81,7 @@ async fn main() { { let mut data = client.data.write().await; - data.insert::(Database::new(db_url, None)); + data.insert::(Database::new(DATABASE_URL.as_str(), None)); data.insert::(CommandManager::new()); data.insert::(session_manager.clone()); }