Remove lavalink dependencies and update to new serenity

main
trivernis 1 year ago
parent 065fc688ad
commit 118832036e
Signed by: Trivernis
GPG Key ID: DFFFCC2C7A02DB45

1039
Cargo.lock generated

File diff suppressed because it is too large Load Diff

@ -42,14 +42,11 @@ animethemes-rs = "0.4.4"
build-time = "0.1.1" build-time = "0.1.1"
tracing-subscriber = { version = "0.3.11", features = ["env-filter"] } tracing-subscriber = { version = "0.3.11", features = ["env-filter"] }
tracing = "0.1.34" tracing = "0.1.34"
serenity-rich-interaction= "0.3.1" serenity-additions = "0.3.2"
[dependencies.tokio] [dependencies.tokio]
version = "1.19.2" version = "1.19.2"
features = ["macros", "rt-multi-thread"] features = ["macros", "rt-multi-thread"]
[dependencies.lavalink-rs] [patch.crates-io]
git = "https://gitlab.com/vicky5124/lavalink-rs/" serenity-additions = { path = "../serenity-additions" }
rev = "2487c295"
features=["native", "serenity", "songbird", "tracing-log"]
default-features = false

@ -7,8 +7,8 @@ edition = "2018"
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
[dependencies] [dependencies]
tokio = { version = "1.19.2", features = ["process", "io-util"] } tokio = { version = "1.21.2", features = ["process", "io-util"] }
log = "0.4.17" log = "0.4.17"
url = "2.2.2" url = "2.3.1"
mime_guess = "2.0.4" mime_guess = "2.0.4"
rand = "0.8.5" rand = "0.8.5"

@ -11,7 +11,7 @@ pub async fn run_command_async(command: &str, args: &[&str]) -> io::Result<Strin
let stdout = String::from_utf8_lossy(&process_output.stdout[..]); let stdout = String::from_utf8_lossy(&process_output.stdout[..]);
if stderr.len() != 0 { if stderr.len() != 0 {
log::debug!("STDERR of command {}: {}", command, stderr); log::trace!("STDERR of command {}: {}", command, stderr);
} }
log::trace!("Command output is {}", stdout); log::trace!("Command output is {}", stdout);

@ -8,12 +8,12 @@ edition = "2018"
[dependencies] [dependencies]
dotenv = "0.15.0" dotenv = "0.15.0"
chrono = "0.4.19" chrono = "0.4.22"
thiserror = "1.0.31" thiserror = "1.0.37"
tracing = "0.1.34" tracing = "0.1.37"
[dependencies.sea-orm] [dependencies.sea-orm]
version = "0.9.2" version = "0.9.3"
features = ["runtime-tokio-native-tls", "sqlx-postgres"] features = ["runtime-tokio-native-tls", "sqlx-postgres"]
[dependencies.migration] [dependencies.migration]

@ -9,4 +9,5 @@ name = "migration"
path = "src/lib.rs" path = "src/lib.rs"
[dependencies] [dependencies]
sea-orm-migration = "0.9.2" sea-orm-migration = "0.9.3"
tokio = { version = "1.21.2", features = ["rt", "net", "tracing"] }

@ -1,7 +1,7 @@
use migration::Migrator; use migration::Migrator;
use sea_orm_migration::prelude::*; use sea_orm_migration::prelude::*;
#[async_std::main] #[tokio::main]
async fn main() { async fn main() {
cli::run_cli(Migrator).await; cli::run_cli(Migrator).await;
} }

@ -10,13 +10,13 @@ use serenity::framework::standard::{CommandResult, DispatchError};
use serenity::framework::StandardFramework; use serenity::framework::StandardFramework;
use serenity::model::channel::Message; use serenity::model::channel::Message;
use serenity::model::id::UserId; use serenity::model::id::UserId;
use serenity::prelude::GatewayIntents;
use serenity::Client; use serenity::Client;
use serenity_rich_interaction::RegisterRichInteractions; use serenity_additions::RegisterAdditions;
use songbird::SerenityInit; use songbird::SerenityInit;
use crate::commands::*; use crate::commands::*;
use crate::handler::{get_raw_event_handler, Handler}; use crate::handler::{get_raw_event_handler, Handler};
use crate::utils;
use crate::utils::context_data::{ use crate::utils::context_data::{
get_database_from_context, DatabaseContainer, MusicPlayers, Store, StoreData, get_database_from_context, DatabaseContainer, MusicPlayers, Store, StoreData,
}; };
@ -25,8 +25,8 @@ use crate::utils::error::{BotError, BotResult};
pub async fn get_client() -> BotResult<Client> { pub async fn get_client() -> BotResult<Client> {
let token = env::var("BOT_TOKEN").map_err(|_| BotError::MissingToken)?; let token = env::var("BOT_TOKEN").map_err(|_| BotError::MissingToken)?;
let database = get_database().await?; let database = get_database().await?;
let client = Client::builder(token) let client = Client::builder(token, GatewayIntents::all())
.register_rich_interactions_with(get_raw_event_handler()) .register_serenity_additions_with(get_raw_event_handler())
.event_handler(Handler) .event_handler(Handler)
.framework(get_framework().await) .framework(get_framework().await)
.register_songbird() .register_songbird()
@ -34,15 +34,6 @@ pub async fn get_client() -> BotResult<Client> {
.type_map_insert::<DatabaseContainer>(database) .type_map_insert::<DatabaseContainer>(database)
.type_map_insert::<MusicPlayers>(HashMap::new()) .type_map_insert::<MusicPlayers>(HashMap::new())
.await?; .await?;
let data = client.data.clone();
let current_application = client
.cache_and_http
.http
.get_current_application_info()
.await?;
utils::initialize_lavalink(data, current_application).await?;
Ok(client) Ok(client)
} }
@ -124,7 +115,7 @@ async fn before_hook(ctx: &Context, msg: &Message, _: &str) -> bool {
} }
#[hook] #[hook]
async fn dispatch_error(ctx: &Context, msg: &Message, error: DispatchError) { async fn dispatch_error(ctx: &Context, msg: &Message, error: DispatchError, command_name: &str) {
match error { match error {
DispatchError::Ratelimited(info) => { DispatchError::Ratelimited(info) => {
if info.is_first_try { if info.is_first_try {
@ -140,13 +131,19 @@ async fn dispatch_error(ctx: &Context, msg: &Message, error: DispatchError) {
DispatchError::OnlyForDM => { DispatchError::OnlyForDM => {
let _ = msg let _ = msg
.channel_id .channel_id
.say(&ctx.http, "This command only works via DM") .say(
&ctx.http,
format!("The command {command_name} only works via DM"),
)
.await; .await;
} }
DispatchError::OnlyForGuilds => { DispatchError::OnlyForGuilds => {
let _ = msg let _ = msg
.channel_id .channel_id
.say(&ctx.http, "This command only works on servers") .say(
&ctx.http,
format!("The command {command_name} only works on servers"),
)
.await; .await;
} }
DispatchError::NotEnoughArguments { min, given } => { DispatchError::NotEnoughArguments { min, given } => {

@ -4,8 +4,8 @@ use serenity::client::Context;
use serenity::framework::standard::macros::command; use serenity::framework::standard::macros::command;
use serenity::framework::standard::{Args, CommandResult}; use serenity::framework::standard::{Args, CommandResult};
use serenity::model::channel::Message; use serenity::model::channel::Message;
use serenity_rich_interaction::core::SHORT_TIMEOUT; use serenity_additions::core::SHORT_TIMEOUT;
use serenity_rich_interaction::ephemeral_message::EphemeralMessage; use serenity_additions::ephemeral_message::EphemeralMessage;
#[command] #[command]
#[description("Adds media to the database")] #[description("Adds media to the database")]

@ -4,8 +4,8 @@ use serenity::framework::standard::macros::command;
use serenity::framework::standard::{Args, CommandResult}; use serenity::framework::standard::{Args, CommandResult};
use serenity::model::channel::Message; use serenity::model::channel::Message;
use serenity::Result as SerenityResult; use serenity::Result as SerenityResult;
use serenity_rich_interaction::core::SHORT_TIMEOUT; use serenity_additions::core::SHORT_TIMEOUT;
use serenity_rich_interaction::ephemeral_message::EphemeralMessage; use serenity_additions::ephemeral_message::EphemeralMessage;
#[command] #[command]
#[description("Clears the chat (maximum 100 messages)")] #[description("Clears the chat (maximum 100 messages)")]

@ -19,6 +19,7 @@ pub async fn help(
groups: &[&'static CommandGroup], groups: &[&'static CommandGroup],
owners: HashSet<UserId>, owners: HashSet<UserId>,
) -> CommandResult { ) -> CommandResult {
tracing::debug!("Help");
let _ = help_commands::with_embeds(ctx, msg, args, help_options, groups, owners).await; let _ = help_commands::with_embeds(ctx, msg, args, help_options, groups, owners).await;
handle_autodelete(ctx, msg).await?; handle_autodelete(ctx, msg).await?;
Ok(()) Ok(())

@ -10,7 +10,6 @@ use pain::PAIN_COMMAND;
use party::PARTY_COMMAND; use party::PARTY_COMMAND;
use ping::PING_COMMAND; use ping::PING_COMMAND;
use qalc::QALC_COMMAND; use qalc::QALC_COMMAND;
use reset_lavalink::RESET_LAVALINK_COMMAND;
use shutdown::SHUTDOWN_COMMAND; use shutdown::SHUTDOWN_COMMAND;
use stats::STATS_COMMAND; use stats::STATS_COMMAND;
use time::TIME_COMMAND; use time::TIME_COMMAND;
@ -28,7 +27,6 @@ mod pain;
mod party; mod party;
mod ping; mod ping;
mod qalc; mod qalc;
mod reset_lavalink;
mod shutdown; mod shutdown;
mod stats; mod stats;
mod time; mod time;
@ -37,21 +35,7 @@ mod xkcd;
#[group] #[group]
#[commands( #[commands(
ping, ping, stats, shutdown, time, timezones, qalc, about, add_media, media, pain, clear, xkcd, fuck,
stats, party, inspirobot
shutdown,
time,
timezones,
qalc,
about,
add_media,
media,
pain,
clear,
xkcd,
fuck,
party,
inspirobot,
reset_lavalink
)] )]
pub struct Misc; pub struct Misc;

@ -1,40 +0,0 @@
use serenity::client::Context;
use serenity::framework::standard::macros::command;
use serenity::framework::standard::CommandResult;
use serenity::model::channel::Message;
use crate::commands::common::handle_autodelete;
use crate::providers::music::lavalink::Lavalink;
use crate::utils::initialize_lavalink;
use serenity_rich_interaction::core::SHORT_TIMEOUT;
use serenity_rich_interaction::ephemeral_message::EphemeralMessage;
use std::mem;
use std::sync::Arc;
#[command]
#[description("Resets the lavalink connection")]
#[aliases("reconnect_lavalink", "reset-lavalink", "reconnect-lavalink")]
#[num_args(0)]
#[owners_only]
async fn reset_lavalink(ctx: &Context, msg: &Message) -> CommandResult {
let app_info = ctx.http.get_current_application_info().await?;
destroy_lavalink(ctx).await;
initialize_lavalink(Arc::clone(&ctx.data), app_info).await?;
EphemeralMessage::create(&ctx.http, msg.channel_id, SHORT_TIMEOUT, |m| {
m.content("Reconnected to lavalink")
})
.await?;
handle_autodelete(ctx, msg).await?;
Ok(())
}
async fn destroy_lavalink(ctx: &Context) {
let mut data = ctx.data.write().await;
{
let lava_client = data.remove::<Lavalink>().unwrap();
mem::drop(lava_client);
}
}

@ -33,7 +33,7 @@ async fn stats(ctx: &Context, msg: &Message) -> CommandResult {
let uptime = own_process.run_time(); let uptime = own_process.run_time();
let uptime = ChronoDuration::from_std(Duration::from_secs(uptime)).unwrap(); let uptime = ChronoDuration::from_std(Duration::from_secs(uptime)).unwrap();
let total_commands_executed = database.get_total_commands_statistic().await?; let total_commands_executed = database.get_total_commands_statistic().await?;
let shard_count = ctx.cache.shard_count().await; let shard_count = ctx.cache.shard_count();
let discord_info = format!( let discord_info = format!(
r#" r#"

@ -6,8 +6,8 @@ use serenity::model::channel::Message;
use crate::commands::common::handle_autodelete; use crate::commands::common::handle_autodelete;
use crate::commands::music::{get_music_player_for_guild, DJ_CHECK}; use crate::commands::music::{get_music_player_for_guild, DJ_CHECK};
use crate::messages::music::no_voicechannel::create_no_voicechannel_message; use crate::messages::music::no_voicechannel::create_no_voicechannel_message;
use serenity_rich_interaction::core::SHORT_TIMEOUT; use serenity_additions::core::SHORT_TIMEOUT;
use serenity_rich_interaction::ephemeral_message::EphemeralMessage; use serenity_additions::ephemeral_message::EphemeralMessage;
#[command] #[command]
#[only_in(guilds)] #[only_in(guilds)]
@ -17,7 +17,7 @@ use serenity_rich_interaction::ephemeral_message::EphemeralMessage;
#[bucket("general")] #[bucket("general")]
#[checks(DJ)] #[checks(DJ)]
async fn clear_queue(ctx: &Context, msg: &Message) -> CommandResult { async fn clear_queue(ctx: &Context, msg: &Message) -> CommandResult {
let guild = msg.guild(&ctx.cache).await.unwrap(); let guild = msg.guild(&ctx.cache).unwrap();
tracing::debug!("Clearing queue for guild {}", guild.id); tracing::debug!("Clearing queue for guild {}", guild.id);
let player = if let Some(player) = get_music_player_for_guild(ctx, guild.id).await { let player = if let Some(player) = get_music_player_for_guild(ctx, guild.id).await {

@ -15,7 +15,7 @@ use crate::messages::music::now_playing::create_now_playing_msg;
#[aliases("nowplaying", "np")] #[aliases("nowplaying", "np")]
#[bucket("general")] #[bucket("general")]
async fn current(ctx: &Context, msg: &Message) -> CommandResult { async fn current(ctx: &Context, msg: &Message) -> CommandResult {
let guild = msg.guild(&ctx.cache).await.unwrap(); let guild = msg.guild(&ctx.cache).unwrap();
tracing::debug!("Displaying current song for queue in {}", guild.id); tracing::debug!("Displaying current song for queue in {}", guild.id);
let player = if let Some(player) = get_music_player_for_guild(ctx, guild.id).await { let player = if let Some(player) = get_music_player_for_guild(ctx, guild.id).await {

@ -1,62 +0,0 @@
use serenity::framework::standard::macros::command;
use serenity::framework::standard::{Args, CommandError, CommandResult};
use serenity::model::channel::Message;
use serenity::prelude::*;
use crate::commands::common::handle_autodelete;
use crate::commands::music::{get_music_player_for_guild, DJ_CHECK};
use crate::messages::music::no_voicechannel::create_no_voicechannel_message;
use serenity_rich_interaction::core::{MEDIUM_TIMEOUT, SHORT_TIMEOUT};
use serenity_rich_interaction::ephemeral_message::EphemeralMessage;
#[command]
#[only_in(guilds)]
#[description("Loads an equalizer preset")]
#[usage("<preset>")]
#[num_args(1)]
#[example("bass")]
#[bucket("general")]
#[checks(DJ)]
async fn equalize(ctx: &Context, msg: &Message, mut args: Args) -> CommandResult {
let guild = msg.guild(&ctx.cache).await.unwrap();
tracing::debug!("Changing equalizer for {}", guild.id);
let preset = args.single::<String>().unwrap();
let player = if let Some(player) = get_music_player_for_guild(ctx, guild.id).await {
player
} else {
return create_no_voicechannel_message(&ctx.http, msg.channel_id)
.await
.map_err(CommandError::from);
};
let bands = match preset.to_lowercase().as_str() {
"metal" => lavalink_rs::EQ_METAL,
"boost" => lavalink_rs::EQ_BOOST,
"base" => lavalink_rs::EQ_BASE,
"piano" => lavalink_rs::EQ_PIANO,
_ => {
EphemeralMessage::create(&ctx.http, msg.channel_id, MEDIUM_TIMEOUT, |m| {
m.content(format!(
"Unknown preset '{}'. Available are 'metal', 'boost', 'base' and 'piano'",
preset
))
})
.await?;
handle_autodelete(ctx, msg).await?;
return Ok(());
}
};
{
let mut player = player.lock().await;
player.equalize_all(bands).await?;
}
EphemeralMessage::create(&ctx.http, msg.channel_id, SHORT_TIMEOUT, |m| {
m.content(format!("🎛️ Changed equalizer to '{}'", preset))
})
.await?;
handle_autodelete(ctx, msg).await?;
Ok(())
}

@ -1,33 +0,0 @@
use serenity::framework::standard::macros::command;
use serenity::framework::standard::{CommandError, CommandResult};
use serenity::model::channel::Message;
use serenity::prelude::*;
use crate::commands::common::handle_autodelete;
use crate::commands::music::{get_music_player_for_guild, DJ_CHECK};
use crate::messages::music::equalizer::create_equalizer_message;
use crate::messages::music::no_voicechannel::create_no_voicechannel_message;
#[command]
#[only_in(guilds)]
#[description("Displays the equalizer for the music player")]
#[usage("")]
#[bucket("general")]
#[checks(DJ)]
async fn equalizer(ctx: &Context, msg: &Message) -> CommandResult {
let guild = msg.guild(&ctx.cache).await.unwrap();
tracing::debug!("Displaying equalizer for guild {}", guild.id);
let player = if let Some(player) = get_music_player_for_guild(ctx, guild.id).await {
player
} else {
return create_no_voicechannel_message(&ctx.http, msg.channel_id)
.await
.map_err(CommandError::from);
};
create_equalizer_message(&ctx, msg.channel_id, player).await?;
handle_autodelete(ctx, msg).await?;
Ok(())
}

@ -7,8 +7,8 @@ use crate::commands::common::handle_autodelete;
use crate::commands::music::{get_channel_for_author, get_music_player_for_guild, is_dj}; use crate::commands::music::{get_channel_for_author, get_music_player_for_guild, is_dj};
use crate::providers::music::player::MusicPlayer; use crate::providers::music::player::MusicPlayer;
use serenity::model::id::ChannelId; use serenity::model::id::ChannelId;
use serenity_rich_interaction::core::SHORT_TIMEOUT; use serenity_additions::core::SHORT_TIMEOUT;
use serenity_rich_interaction::ephemeral_message::EphemeralMessage; use serenity_additions::ephemeral_message::EphemeralMessage;
#[command] #[command]
#[only_in(guilds)] #[only_in(guilds)]
@ -16,7 +16,7 @@ use serenity_rich_interaction::ephemeral_message::EphemeralMessage;
#[usage("")] #[usage("")]
#[bucket("general")] #[bucket("general")]
async fn join(ctx: &Context, msg: &Message, mut args: Args) -> CommandResult { async fn join(ctx: &Context, msg: &Message, mut args: Args) -> CommandResult {
let guild = msg.guild(&ctx.cache).await.unwrap(); let guild = msg.guild(&ctx.cache).unwrap();
let channel_id = if let Ok(arg) = args.single::<u64>() { let channel_id = if let Ok(arg) = args.single::<u64>() {
if is_dj(ctx, guild.id, &msg.author).await? { if is_dj(ctx, guild.id, &msg.author).await? {
ChannelId(arg) ChannelId(arg)

@ -6,8 +6,8 @@ use serenity::model::channel::Message;
use crate::commands::common::handle_autodelete; use crate::commands::common::handle_autodelete;
use crate::commands::music::DJ_CHECK; use crate::commands::music::DJ_CHECK;
use crate::utils::context_data::MusicPlayers; use crate::utils::context_data::MusicPlayers;
use serenity_rich_interaction::core::SHORT_TIMEOUT; use serenity_additions::core::SHORT_TIMEOUT;
use serenity_rich_interaction::ephemeral_message::EphemeralMessage; use serenity_additions::ephemeral_message::EphemeralMessage;
#[command] #[command]
#[only_in(guilds)] #[only_in(guilds)]
@ -17,13 +17,13 @@ use serenity_rich_interaction::ephemeral_message::EphemeralMessage;
#[bucket("general")] #[bucket("general")]
#[checks(DJ)] #[checks(DJ)]
async fn leave(ctx: &Context, msg: &Message) -> CommandResult { async fn leave(ctx: &Context, msg: &Message) -> CommandResult {
let guild = msg.guild(&ctx.cache).await.unwrap(); let guild = msg.guild(&ctx.cache).unwrap();
tracing::debug!("Leave request received for guild {}", guild.id); tracing::debug!("Leave request received for guild {}", guild.id);
let manager = songbird::get(ctx).await.unwrap(); let manager = songbird::get(ctx).await.unwrap();
if let Some(handler) = manager.get(guild.id) { if let Some(handler) = manager.get(guild.id) {
let mut handler_lock = handler.lock().await; let mut handler_lock = handler.lock().await;
let _ = handler_lock.leave().await; handler_lock.leave().await?;
} }
let mut data = ctx.data.write().await; let mut data = ctx.data.write().await;

@ -13,7 +13,7 @@ use crate::messages::music::no_voicechannel::create_no_voicechannel_message;
#[usage("")] #[usage("")]
#[bucket("general")] #[bucket("general")]
async fn lyrics(ctx: &Context, msg: &Message) -> CommandResult { async fn lyrics(ctx: &Context, msg: &Message) -> CommandResult {
let guild = msg.guild(&ctx.cache).await.unwrap(); let guild = msg.guild(&ctx.cache).unwrap();
tracing::debug!("Fetching lyrics for song playing in {}", guild.id); tracing::debug!("Fetching lyrics for song playing in {}", guild.id);
let player = if let Some(player) = get_music_player_for_guild(ctx, guild.id).await { let player = if let Some(player) = get_music_player_for_guild(ctx, guild.id).await {

@ -18,8 +18,6 @@ use youtube_metadata::get_video_information;
use clear_queue::CLEAR_QUEUE_COMMAND; use clear_queue::CLEAR_QUEUE_COMMAND;
use current::CURRENT_COMMAND; use current::CURRENT_COMMAND;
use equalize::EQUALIZE_COMMAND;
use equalizer::EQUALIZER_COMMAND;
use join::JOIN_COMMAND; use join::JOIN_COMMAND;
use leave::LEAVE_COMMAND; use leave::LEAVE_COMMAND;
use lyrics::LYRICS_COMMAND; use lyrics::LYRICS_COMMAND;
@ -43,8 +41,6 @@ use crate::utils::error::{BotError, BotResult};
mod clear_queue; mod clear_queue;
mod current; mod current;
mod equalize;
mod equalizer;
mod join; mod join;
mod leave; mod leave;
mod lyrics; mod lyrics;
@ -75,9 +71,7 @@ mod skip;
playlists, playlists,
lyrics, lyrics,
move_song, move_song,
remove_song, remove_song
equalizer,
equalize
)] )]
pub struct Music; pub struct Music;
@ -257,7 +251,6 @@ pub async fn check_dj(
) -> Result<(), Reason> { ) -> Result<(), Reason> {
let guild = msg let guild = msg
.guild(&ctx.cache) .guild(&ctx.cache)
.await
.ok_or(Reason::Log("Not in a guild".to_string()))?; .ok_or(Reason::Log("Not in a guild".to_string()))?;
if is_dj(ctx, guild.id, &msg.author) if is_dj(ctx, guild.id, &msg.author)

@ -5,8 +5,8 @@ use serenity::client::Context;
use serenity::framework::standard::macros::command; use serenity::framework::standard::macros::command;
use serenity::framework::standard::{Args, CommandError, CommandResult}; use serenity::framework::standard::{Args, CommandError, CommandResult};
use serenity::model::channel::Message; use serenity::model::channel::Message;
use serenity_rich_interaction::core::SHORT_TIMEOUT; use serenity_additions::core::SHORT_TIMEOUT;
use serenity_rich_interaction::ephemeral_message::EphemeralMessage; use serenity_additions::ephemeral_message::EphemeralMessage;
#[command] #[command]
#[description("Moves a song in the queue from one position to a new one")] #[description("Moves a song in the queue from one position to a new one")]
@ -18,7 +18,7 @@ use serenity_rich_interaction::ephemeral_message::EphemeralMessage;
#[aliases("mvs", "movesong", "move-song")] #[aliases("mvs", "movesong", "move-song")]
#[checks(DJ)] #[checks(DJ)]
async fn move_song(ctx: &Context, msg: &Message, mut args: Args) -> CommandResult { async fn move_song(ctx: &Context, msg: &Message, mut args: Args) -> CommandResult {
let guild = msg.guild(&ctx.cache).await.unwrap(); let guild = msg.guild(&ctx.cache).unwrap();
tracing::debug!("Moving song for guild {}", guild.id); tracing::debug!("Moving song for guild {}", guild.id);
let pos1 = args.single::<usize>()?; let pos1 = args.single::<usize>()?;

@ -6,8 +6,8 @@ use serenity::prelude::*;
use crate::commands::common::handle_autodelete; use crate::commands::common::handle_autodelete;
use crate::commands::music::{get_music_player_for_guild, DJ_CHECK}; use crate::commands::music::{get_music_player_for_guild, DJ_CHECK};
use crate::messages::music::no_voicechannel::create_no_voicechannel_message; use crate::messages::music::no_voicechannel::create_no_voicechannel_message;
use serenity_rich_interaction::core::SHORT_TIMEOUT; use serenity_additions::core::SHORT_TIMEOUT;
use serenity_rich_interaction::ephemeral_message::EphemeralMessage; use serenity_additions::ephemeral_message::EphemeralMessage;
#[command] #[command]
#[only_in(guilds)] #[only_in(guilds)]
@ -16,7 +16,7 @@ use serenity_rich_interaction::ephemeral_message::EphemeralMessage;
#[bucket("general")] #[bucket("general")]
#[checks(DJ)] #[checks(DJ)]
async fn pause(ctx: &Context, msg: &Message) -> CommandResult { async fn pause(ctx: &Context, msg: &Message) -> CommandResult {
let guild = msg.guild(&ctx.cache).await.unwrap(); let guild = msg.guild(&ctx.cache).unwrap();
tracing::debug!("Pausing playback for guild {}", guild.id); tracing::debug!("Pausing playback for guild {}", guild.id);
let player = if let Some(player) = get_music_player_for_guild(ctx, guild.id).await { let player = if let Some(player) = get_music_player_for_guild(ctx, guild.id).await {

@ -22,7 +22,7 @@ use std::sync::Arc;
async fn play(ctx: &Context, msg: &Message, args: Args) -> CommandResult { async fn play(ctx: &Context, msg: &Message, args: Args) -> CommandResult {
let query = args.message(); let query = args.message();
let guild = msg.guild(&ctx.cache).await.unwrap(); let guild = msg.guild(&ctx.cache).unwrap();
tracing::debug!("Play request received for guild {}", guild.id); tracing::debug!("Play request received for guild {}", guild.id);
let mut player = get_music_player_for_guild(ctx, guild.id).await; let mut player = get_music_player_for_guild(ctx, guild.id).await;

@ -22,7 +22,7 @@ use std::sync::Arc;
async fn play_next(ctx: &Context, msg: &Message, args: Args) -> CommandResult { async fn play_next(ctx: &Context, msg: &Message, args: Args) -> CommandResult {
let query = args.message(); let query = args.message();
let guild = msg.guild(&ctx.cache).await.unwrap(); let guild = msg.guild(&ctx.cache).unwrap();
tracing::debug!("Playing song as next song for guild {}", guild.id); tracing::debug!("Playing song as next song for guild {}", guild.id);
let mut player = get_music_player_for_guild(ctx, guild.id).await; let mut player = get_music_player_for_guild(ctx, guild.id).await;

@ -12,7 +12,7 @@ use crate::utils::context_data::get_database_from_context;
#[usage("")] #[usage("")]
#[bucket("general")] #[bucket("general")]
async fn playlists(ctx: &Context, msg: &Message) -> CommandResult { async fn playlists(ctx: &Context, msg: &Message) -> CommandResult {
let guild = msg.guild(&ctx.cache).await.unwrap(); let guild = msg.guild(&ctx.cache).unwrap();
tracing::debug!("Displaying playlists for guild {}", guild.id); tracing::debug!("Displaying playlists for guild {}", guild.id);
let database = get_database_from_context(ctx).await; let database = get_database_from_context(ctx).await;

@ -16,7 +16,7 @@ use crate::providers::music::queue::Song;
#[aliases("q")] #[aliases("q")]
#[bucket("general")] #[bucket("general")]
async fn queue(ctx: &Context, msg: &Message, mut args: Args) -> CommandResult { async fn queue(ctx: &Context, msg: &Message, mut args: Args) -> CommandResult {
let guild = msg.guild(&ctx.cache).await.unwrap(); let guild = msg.guild(&ctx.cache).unwrap();
tracing::trace!("Displaying queue for guild {}", guild.id); tracing::trace!("Displaying queue for guild {}", guild.id);
let query = args let query = args

@ -5,8 +5,8 @@ use serenity::client::Context;
use serenity::framework::standard::macros::command; use serenity::framework::standard::macros::command;
use serenity::framework::standard::{Args, CommandError, CommandResult}; use serenity::framework::standard::{Args, CommandError, CommandResult};
use serenity::model::channel::Message; use serenity::model::channel::Message;
use serenity_rich_interaction::core::SHORT_TIMEOUT; use serenity_additions::core::SHORT_TIMEOUT;
use serenity_rich_interaction::ephemeral_message::EphemeralMessage; use serenity_additions::ephemeral_message::EphemeralMessage;
#[command] #[command]
#[description("Removes a song from the queue")] #[description("Removes a song from the queue")]
@ -18,7 +18,7 @@ use serenity_rich_interaction::ephemeral_message::EphemeralMessage;
#[aliases("rms", "removesong", "remove-song")] #[aliases("rms", "removesong", "remove-song")]
#[checks(DJ)] #[checks(DJ)]
async fn remove_song(ctx: &Context, msg: &Message, mut args: Args) -> CommandResult { async fn remove_song(ctx: &Context, msg: &Message, mut args: Args) -> CommandResult {
let guild = msg.guild(&ctx.cache).await.unwrap(); let guild = msg.guild(&ctx.cache).unwrap();
tracing::debug!("Moving song for guild {}", guild.id); tracing::debug!("Moving song for guild {}", guild.id);
let pos = args.single::<usize>()?; let pos = args.single::<usize>()?;

@ -16,7 +16,7 @@ use crate::utils::context_data::get_database_from_context;
#[bucket("general")] #[bucket("general")]
#[checks(DJ)] #[checks(DJ)]
async fn save_playlist(ctx: &Context, msg: &Message, mut args: Args) -> CommandResult { async fn save_playlist(ctx: &Context, msg: &Message, mut args: Args) -> CommandResult {
let guild = msg.guild(&ctx.cache).await.unwrap(); let guild = msg.guild(&ctx.cache).unwrap();
let name: String = args.single().unwrap(); let name: String = args.single().unwrap();
let url: &str = args.remains().unwrap(); let url: &str = args.remains().unwrap();

@ -6,8 +6,8 @@ use serenity::model::channel::Message;
use crate::commands::common::handle_autodelete; use crate::commands::common::handle_autodelete;
use crate::commands::music::{get_music_player_for_guild, DJ_CHECK}; use crate::commands::music::{get_music_player_for_guild, DJ_CHECK};
use crate::messages::music::no_voicechannel::create_no_voicechannel_message; use crate::messages::music::no_voicechannel::create_no_voicechannel_message;
use serenity_rich_interaction::core::SHORT_TIMEOUT; use serenity_additions::core::SHORT_TIMEOUT;
use serenity_rich_interaction::ephemeral_message::EphemeralMessage; use serenity_additions::ephemeral_message::EphemeralMessage;
#[command] #[command]
#[only_in(guilds)] #[only_in(guilds)]
@ -17,7 +17,7 @@ use serenity_rich_interaction::ephemeral_message::EphemeralMessage;
#[bucket("general")] #[bucket("general")]
#[checks(DJ)] #[checks(DJ)]
async fn shuffle(ctx: &Context, msg: &Message) -> CommandResult { async fn shuffle(ctx: &Context, msg: &Message) -> CommandResult {
let guild = msg.guild(&ctx.cache).await.unwrap(); let guild = msg.guild(&ctx.cache).unwrap();
tracing::debug!("Shuffling queue for guild {}", guild.id); tracing::debug!("Shuffling queue for guild {}", guild.id);
let player = if let Some(player) = get_music_player_for_guild(ctx, guild.id).await { let player = if let Some(player) = get_music_player_for_guild(ctx, guild.id).await {

@ -6,8 +6,8 @@ use serenity::model::channel::Message;
use crate::commands::common::handle_autodelete; use crate::commands::common::handle_autodelete;
use crate::commands::music::{get_music_player_for_guild, DJ_CHECK}; use crate::commands::music::{get_music_player_for_guild, DJ_CHECK};
use crate::messages::music::no_voicechannel::create_no_voicechannel_message; use crate::messages::music::no_voicechannel::create_no_voicechannel_message;
use serenity_rich_interaction::core::SHORT_TIMEOUT; use serenity_additions::core::SHORT_TIMEOUT;
use serenity_rich_interaction::ephemeral_message::EphemeralMessage; use serenity_additions::ephemeral_message::EphemeralMessage;
#[command] #[command]
#[only_in(guilds)] #[only_in(guilds)]
@ -17,7 +17,7 @@ use serenity_rich_interaction::ephemeral_message::EphemeralMessage;
#[bucket("general")] #[bucket("general")]
#[checks(DJ)] #[checks(DJ)]
async fn skip(ctx: &Context, msg: &Message) -> CommandResult { async fn skip(ctx: &Context, msg: &Message) -> CommandResult {
let guild = msg.guild(&ctx.cache).await.unwrap(); let guild = msg.guild(&ctx.cache).unwrap();
tracing::debug!("Skipping song for guild {}", guild.id); tracing::debug!("Skipping song for guild {}", guild.id);
let player = if let Some(player) = get_music_player_for_guild(ctx, guild.id).await { let player = if let Some(player) = get_music_player_for_guild(ctx, guild.id).await {

@ -17,7 +17,7 @@ use crate::utils::context_data::get_database_from_context;
#[bucket("general")] #[bucket("general")]
async fn get(ctx: &Context, msg: &Message, mut args: Args) -> CommandResult { async fn get(ctx: &Context, msg: &Message, mut args: Args) -> CommandResult {
let database = get_database_from_context(ctx).await; let database = get_database_from_context(ctx).await;
let guild = msg.guild(&ctx.cache).await.unwrap(); let guild = msg.guild(&ctx.cache).unwrap();
tracing::debug!("Displaying guild setting for guild {}", guild.id); tracing::debug!("Displaying guild setting for guild {}", guild.id);
if let Some(key) = args.single::<String>().ok() { if let Some(key) = args.single::<String>().ok() {

@ -28,7 +28,7 @@ async fn set(ctx: &Context, msg: &Message, mut args: Args) -> CommandResult {
return Ok(()); return Ok(());
} }
let database = get_database_from_context(ctx).await; let database = get_database_from_context(ctx).await;
let guild = msg.guild(&ctx.cache).await.unwrap(); let guild = msg.guild(&ctx.cache).unwrap();
if let Ok(value) = args.single::<String>() { if let Ok(value) = args.single::<String>() {
database database

@ -5,8 +5,8 @@ use serenity::client::Context;
use serenity::framework::standard::macros::command; use serenity::framework::standard::macros::command;
use serenity::framework::standard::{Args, CommandResult}; use serenity::framework::standard::{Args, CommandResult};
use serenity::model::channel::Message; use serenity::model::channel::Message;
use serenity_rich_interaction::core::MEDIUM_TIMEOUT; use serenity_additions::core::MEDIUM_TIMEOUT;
use serenity_rich_interaction::ephemeral_message::EphemeralMessage; use serenity_additions::ephemeral_message::EphemeralMessage;
#[command] #[command]
#[description("Query for the opening/ending/insert song of an anime")] #[description("Query for the opening/ending/insert song of an anime")]

@ -4,7 +4,7 @@ use serenity::model::channel::GuildChannel;
use serenity::model::event::ResumedEvent; use serenity::model::event::ResumedEvent;
use serenity::model::gateway::{Activity, Ready}; use serenity::model::gateway::{Activity, Ready};
use serenity::model::guild::Member; use serenity::model::guild::Member;
use serenity::model::id::{ChannelId, GuildId}; use serenity::model::id::ChannelId;
use serenity::model::voice::VoiceState; use serenity::model::voice::VoiceState;
use serenity::prelude::*; use serenity::prelude::*;
@ -12,8 +12,8 @@ use crate::commands::music::get_music_player_for_guild;
use crate::utils::context_data::MusicPlayers; use crate::utils::context_data::MusicPlayers;
use crate::utils::delete_messages_from_database; use crate::utils::delete_messages_from_database;
use serenity::model::event; use serenity::model::event;
use serenity_rich_interaction::events::RichEventHandler; use serenity_additions::events::RichEventHandler;
use serenity_rich_interaction::Result; use serenity_additions::Result;
/// Returns the raw event handler built from a rich event handler /// Returns the raw event handler built from a rich event handler
pub fn get_raw_event_handler() -> RichEventHandler { pub fn get_raw_event_handler() -> RichEventHandler {
@ -34,8 +34,9 @@ async fn ready(ctx: &Context, _: &Ready) -> Result<()> {
tracing::info!("Ready"); tracing::info!("Ready");
delete_messages_from_database(&ctx).await?; delete_messages_from_database(&ctx).await?;
let prefix = std::env::var("BOT_PREFIX").unwrap_or("~!".to_string()); let prefix = std::env::var("BOT_PREFIX").unwrap_or("~!".to_string());
ctx.set_activity(Activity::listening(format!("{}help", prefix).as_str())) ctx.set_activity(Activity::listening(format!("{prefix}help").as_str()))
.await; .await;
tracing::info!("Fully initialized. Listening to {prefix}help");
Ok(()) Ok(())
} }
@ -50,13 +51,12 @@ impl EventHandler for Handler {
async fn voice_state_update( async fn voice_state_update(
&self, &self,
ctx: Context, ctx: Context,
guild_id: Option<GuildId>,
old_state: Option<VoiceState>, old_state: Option<VoiceState>,
new_state: VoiceState, new_state: VoiceState,
) { ) {
let mut member_count = None; let mut member_count = None;
let guild_id = if let Some(gid) = guild_id { let guild_id = if let Some(gid) = new_state.guild_id {
gid gid
} else { } else {
return; return;
@ -81,7 +81,7 @@ impl EventHandler for Handler {
} }
// handle disconnects // handle disconnects
if let (Some(state), None) = (old_state, new_state.channel_id) { if let (Some(state), None) = (old_state, new_state.channel_id) {
let current_user = ctx.cache.current_user().await; let current_user = ctx.cache.current_user();
if state.user_id == current_user.id { if state.user_id == current_user.id {
let mut data = ctx.data.write().await; let mut data = ctx.data.write().await;
@ -101,7 +101,7 @@ impl EventHandler for Handler {
async fn get_own_channel_member_count(ctx: &Context, channel_id: ChannelId) -> Option<usize> { async fn get_own_channel_member_count(ctx: &Context, channel_id: ChannelId) -> Option<usize> {
let guild_channel = get_guild_channel(ctx, channel_id).await?; let guild_channel = get_guild_channel(ctx, channel_id).await?;
let current_user = ctx.cache.current_user().await; let current_user = ctx.cache.current_user();
let members = guild_channel.members(&ctx).await.ok()?; let members = guild_channel.members(&ctx).await.ok()?;
let own_channel = members let own_channel = members
@ -119,7 +119,7 @@ async fn get_own_channel_member_count(ctx: &Context, channel_id: ChannelId) -> O
/// Returns the guild channel for a guild ID /// Returns the guild channel for a guild ID
async fn get_guild_channel(ctx: &Context, channel_id: ChannelId) -> Option<GuildChannel> { async fn get_guild_channel(ctx: &Context, channel_id: ChannelId) -> Option<GuildChannel> {
if let Some(channel) = ctx.cache.channel(channel_id).await { if let Some(channel) = ctx.cache.channel(channel_id) {
return channel.guild(); return channel.guild();
} }
let channel = ctx.http.get_channel(channel_id.0).await.ok()?; let channel = ctx.http.get_channel(channel_id.0).await.ok()?;

@ -3,7 +3,7 @@ use bot_database::models::Media;
use serenity::builder::CreateMessage; use serenity::builder::CreateMessage;
use serenity::client::Context; use serenity::client::Context;
use serenity::model::id::{ChannelId, UserId}; use serenity::model::id::{ChannelId, UserId};
use serenity_rich_interaction::menu::{MenuBuilder, Page}; use serenity_additions::menu::{MenuBuilder, Page};
use std::time::Duration; use std::time::Duration;
/// Creates a new gifs embed /// Creates a new gifs embed

@ -3,10 +3,8 @@ use crate::utils::error::BotResult;
use serenity::builder::CreateMessage; use serenity::builder::CreateMessage;
use serenity::client::Context; use serenity::client::Context;
use serenity::model::id::{ChannelId, UserId}; use serenity::model::id::{ChannelId, UserId};
use serenity_rich_interaction::core::EXTRA_LONG_TIMEOUT; use serenity_additions::core::EXTRA_LONG_TIMEOUT;
use serenity_rich_interaction::menu::{ use serenity_additions::menu::{close_menu, display_page, MenuBuilder, Page, CLOSE_MENU_EMOJI};
close_menu, display_page, MenuBuilder, Page, CLOSE_MENU_EMOJI,
};
static REFRESH_EMOJI: &str = "🔄"; static REFRESH_EMOJI: &str = "🔄";
@ -30,7 +28,7 @@ pub async fn create_inspirobot_menu(
Box::pin(async { Box::pin(async {
let message = create_inspirobot_page() let message = create_inspirobot_page()
.await .await
.map_err(|e| serenity_rich_interaction::Error::Msg(format!("{}", e)))?; .map_err(|e| serenity_additions::Error::Msg(format!("{}", e)))?;
Ok(message) Ok(message)
}) })
})) }))

@ -1,7 +1,7 @@
use crate::utils::context_data::get_database_from_context; use crate::utils::context_data::get_database_from_context;
use crate::utils::error::BotResult; use crate::utils::error::BotResult;
use serenity::client::Context; use serenity::client::Context;
use serenity_rich_interaction::core::MessageHandle; use serenity_additions::core::MessageHandle;
use std::time::{Duration, SystemTime}; use std::time::{Duration, SystemTime};
pub mod gifs; pub mod gifs;

@ -1,262 +0,0 @@
use crate::commands::music::is_dj;
use crate::providers::music::player::MusicPlayer;
use crate::utils::error::BotResult;
use serenity::builder::{CreateEmbed, CreateMessage};
use serenity::client::Context;
use serenity::model::channel::Reaction;
use serenity::model::id::ChannelId;
use serenity_rich_interaction::core::EXTRA_LONG_TIMEOUT;
use serenity_rich_interaction::menu::{display_page, Menu, MenuBuilder, Page};
use serenity_rich_interaction::Result as SerenityUtilsResult;
use std::sync::atomic::{AtomicU8, Ordering};
use std::sync::Arc;
use tokio::sync::Mutex;
use typemap_rev::TypeMapKey;
static DELETE_BUTTON: &str = "🗑️";
static NEXT_BAND_BUTTON: &str = "➡️";
static PREVIOUS_BAND_BUTTON: &str = "⬅️";
static ADD_BUTTON: &str = "";
static SUB_BUTTON: &str = "";
struct SelectedBand;
impl TypeMapKey for SelectedBand {
type Value = Arc<AtomicU8>;
}
struct Player;
impl TypeMapKey for Player {
type Value = Arc<Mutex<MusicPlayer>>;
}
/// Creates a new equalizer message
pub async fn create_equalizer_message(
ctx: &Context,
channel_id: ChannelId,
player: Arc<Mutex<MusicPlayer>>,
) -> BotResult<()> {
let selected_band = Arc::new(AtomicU8::new(0));
let selected_band_clone = Arc::clone(&selected_band);
let player_clone = Arc::clone(&player);
MenuBuilder::default()
.add_page(Page::new_builder(move || {
let player = Arc::clone(&player_clone);
let selected_band = Arc::clone(&selected_band_clone);
Box::pin(async move {
let mut page = CreateMessage::default();
let mut embed = CreateEmbed::default();
create_equalizer_embed(selected_band.load(Ordering::Relaxed), &mut embed, &player)
.await;
page.embed(|e| {
e.0.clone_from(&embed.0);
e
});
Ok(page)
})
}))
.add_control(-1, DELETE_BUTTON, |c, m, r| Box::pin(delete_menu(c, m, r)))
.add_help(DELETE_BUTTON, "Deletes this message.")
.add_control(0, PREVIOUS_BAND_BUTTON, |c, m, r| {
Box::pin(previous_band(c, m, r))
})
.add_help(PREVIOUS_BAND_BUTTON, "Selects the previous band.")
.add_control(1, NEXT_BAND_BUTTON, |c, m, r| Box::pin(next_band(c, m, r)))
.add_help(NEXT_BAND_BUTTON, "Selects the next band.")
.add_control(3, ADD_BUTTON, |c, m, r| Box::pin(add_to_band(c, m, r)))
.add_help(ADD_BUTTON, "Adds to the selected band.")
.add_control(2, SUB_BUTTON, |c, m, r| {
Box::pin(subtract_from_band(c, m, r))
})
.add_help(SUB_BUTTON, "Subtracts from the selected band")
.show_help()
.add_data::<SelectedBand>(selected_band)
.add_data::<Player>(player)
.timeout(EXTRA_LONG_TIMEOUT)
.build(ctx, channel_id)
.await?;
Ok(())
}
/// Creates a new equalizer embed
async fn create_equalizer_embed<'a>(
selected_band: u8,
embed: &'a mut CreateEmbed,
player: &Arc<Mutex<MusicPlayer>>,
) -> &'a mut CreateEmbed {
let mut description = String::new();
let bands = {
let player = player.lock().await;
player.get_equalizer().clone()
};
for i in 0..bands.len() {
if i as u8 == selected_band {
description += "⤋"
} else {
description += " ";
}
}
description += "\n";
for i in (0..11).rev() {
let eq_value = (i as f64) / 20.0 - 0.25;
for band in &bands {
if (eq_value > 0. && band >= &eq_value) || (eq_value < 0. && band <= &eq_value) {
description += "█";
} else if eq_value == 0. {
description += format!("-").as_str();
} else {
description += " ";
}
}
description += "\n";
}
for i in 0..bands.len() {
if i as u8 == selected_band {
description += "⤊"
} else {
description += " ";
}
}
embed
.title("Equalizer")
.description(format!("```\n{}\n```", description));
embed
}
/// Selects the previous band
async fn next_band(
ctx: &Context,
menu: &mut Menu<'_>,
reaction: Reaction,
) -> SerenityUtilsResult<()> {
let guild_id = reaction.guild_id.unwrap();
let user = reaction.user(&ctx).await?;
if !is_dj(ctx, guild_id, &user).await? {
return Ok(());
}
let selected_band = menu.data.get::<SelectedBand>().unwrap();
if selected_band.load(Ordering::SeqCst) >= 14 {
selected_band.store(0, Ordering::SeqCst);
} else {
selected_band.fetch_add(1, Ordering::SeqCst);
}
display_page(ctx, menu).await?;
Ok(())
}
/// Selects the previous band
async fn previous_band(
ctx: &Context,
menu: &mut Menu<'_>,
reaction: Reaction,
) -> SerenityUtilsResult<()> {
let guild_id = reaction.guild_id.unwrap();
let user = reaction.user(&ctx).await?;
if !is_dj(ctx, guild_id, &user).await? {
return Ok(());
}
let selected_band = menu.data.get::<SelectedBand>().unwrap();
if selected_band.load(Ordering::SeqCst) <= 0 {
selected_band.store(14, Ordering::SeqCst);
} else {
selected_band.fetch_sub(1, Ordering::SeqCst);
}
display_page(ctx, menu).await?;
Ok(())
}
/// Adds to the selected band
async fn add_to_band(
ctx: &Context,
menu: &mut Menu<'_>,
reaction: Reaction,
) -> SerenityUtilsResult<()> {
let guild_id = reaction.guild_id.unwrap();
let user = reaction.user(&ctx).await?;
if !is_dj(ctx, guild_id, &user).await? {
return Ok(());
}
{
let selected_band = menu
.data
.get::<SelectedBand>()
.unwrap()
.load(Ordering::Relaxed);
let player = menu.data.get::<Player>().unwrap();
let mut player = player.lock().await;
let equalizer = player.get_equalizer();
let current_value = equalizer[selected_band as usize];
if current_value < 0.25 {
player.equalize(selected_band, current_value + 0.05).await?;
}
}
display_page(ctx, menu).await?;
Ok(())
}
/// Substracts from the selected band
async fn subtract_from_band(
ctx: &Context,
menu: &mut Menu<'_>,
reaction: Reaction,
) -> SerenityUtilsResult<()> {
let guild_id = reaction.guild_id.unwrap();
let user = reaction.user(&ctx).await?;
if !is_dj(ctx, guild_id, &user).await? {
return Ok(());
}
{
let selected_band = menu
.data
.get::<SelectedBand>()
.unwrap()
.load(Ordering::Relaxed);
let player = menu.data.get::<Player>().unwrap();
let mut player = player.lock().await;
let equalizer = player.get_equalizer();
let current_value = equalizer[selected_band as usize];
if current_value > -0.25 {
player.equalize(selected_band, current_value - 0.05).await?;
}
}
display_page(ctx, menu).await?;
Ok(())
}
/// Deletes the menu
async fn delete_menu(
ctx: &Context,
menu: &mut Menu<'_>,
reaction: Reaction,
) -> SerenityUtilsResult<()> {
let guild_id = reaction.guild_id.unwrap();
let user = reaction.user(&ctx).await?;
if !is_dj(ctx, guild_id, &user).await? {
return Ok(());
}
let handle = menu.message.read().await;
ctx.http
.delete_message(handle.channel_id, handle.message_id)
.await?;
Ok(())
}

@ -1,4 +1,3 @@
pub mod equalizer;
pub mod no_voicechannel; pub mod no_voicechannel;
pub mod now_playing; pub mod now_playing;
pub mod queue; pub mod queue;

@ -1,8 +1,8 @@
use crate::utils::error::BotResult; use crate::utils::error::BotResult;
use serenity::http::Http; use serenity::http::Http;
use serenity::model::prelude::ChannelId; use serenity::model::prelude::ChannelId;
use serenity_rich_interaction::core::SHORT_TIMEOUT; use serenity_additions::core::SHORT_TIMEOUT;
use serenity_rich_interaction::ephemeral_message::EphemeralMessage; use serenity_additions::ephemeral_message::EphemeralMessage;
use std::sync::Arc; use std::sync::Arc;
/// Creates a not in a voicechannel message /// Creates a not in a voicechannel message

@ -14,9 +14,9 @@ use crate::utils::error::*;
use serenity::builder::CreateMessage; use serenity::builder::CreateMessage;
use serenity::client::Context; use serenity::client::Context;
use serenity::model::channel::Reaction; use serenity::model::channel::Reaction;
use serenity_rich_interaction::core::MessageHandle; use serenity_additions::core::MessageHandle;
use serenity_rich_interaction::menu::{Menu, MenuBuilder, Page}; use serenity_additions::menu::{Menu, MenuBuilder, Page};
use serenity_rich_interaction::Result as SerenityUtilsResult; use serenity_additions::Result as SerenityUtilsResult;
use std::env; use std::env;
use std::time::Duration; use std::time::Duration;
use tokio::sync::{Mutex, RwLock}; use tokio::sync::{Mutex, RwLock};

@ -3,7 +3,7 @@ use crate::utils::error::BotResult;
use serenity::builder::CreateMessage; use serenity::builder::CreateMessage;
use serenity::client::Context; use serenity::client::Context;
use serenity::model::id::ChannelId; use serenity::model::id::ChannelId;
use serenity_rich_interaction::menu::{MenuBuilder, Page}; use serenity_additions::menu::{MenuBuilder, Page};
use std::time::Duration; use std::time::Duration;
/// Creates a new queue menu /// Creates a new queue menu

@ -8,7 +8,7 @@ use bot_coreutils::url::get_domain_for_url;
use crate::utils::error::BotResult; use crate::utils::error::BotResult;
use rand::prelude::SliceRandom; use rand::prelude::SliceRandom;
use serenity_rich_interaction::menu::{MenuBuilder, Page}; use serenity_additions::menu::{MenuBuilder, Page};
use std::time::Duration; use std::time::Duration;
static MAX_RESULTS: usize = 6; static MAX_RESULTS: usize = 6;

@ -3,8 +3,8 @@ use animethemes_rs::models::{Anime, ThemeEntry, ThemeType};
use serenity::builder::CreateMessage; use serenity::builder::CreateMessage;
use serenity::client::Context; use serenity::client::Context;
use serenity::model::id::{ChannelId, UserId}; use serenity::model::id::{ChannelId, UserId};
use serenity_rich_interaction::core::EXTRA_LONG_TIMEOUT; use serenity_additions::core::EXTRA_LONG_TIMEOUT;
use serenity_rich_interaction::menu::{MenuBuilder, Page}; use serenity_additions::menu::{MenuBuilder, Page};
/// Creates a new Anime Theme Menu /// Creates a new Anime Theme Menu
pub async fn create_theme_menu( pub async fn create_theme_menu(

@ -2,8 +2,8 @@ use crate::utils::error::BotResult;
use serenity::builder::CreateMessage; use serenity::builder::CreateMessage;
use serenity::client::Context; use serenity::client::Context;
use serenity::model::id::{ChannelId, UserId}; use serenity::model::id::{ChannelId, UserId};
use serenity_rich_interaction::core::LONG_TIMEOUT; use serenity_additions::core::LONG_TIMEOUT;
use serenity_rich_interaction::menu::{MenuBuilder, Page}; use serenity_additions::menu::{MenuBuilder, Page};
use xkcd_search::Comic; use xkcd_search::Comic;
/// Creates a new xkcd menu /// Creates a new xkcd menu

@ -1,53 +0,0 @@
use crate::utils::context_data::MusicPlayers;
use lavalink_rs::gateway::LavalinkEventHandler;
use lavalink_rs::model::{PlayerUpdate, Stats, TrackFinish, TrackStart};
use lavalink_rs::LavalinkClient;
use serenity::async_trait;
use serenity::prelude::TypeMapKey;
use std::sync::Arc;
use tokio::sync::RwLock;
use typemap_rev::TypeMap;
pub struct LavalinkHandler {
pub data: Arc<RwLock<TypeMap>>,
}
#[async_trait]
impl LavalinkEventHandler for LavalinkHandler {
async fn track_start(&self, _client: LavalinkClient, event: TrackStart) {
tracing::info!("Track started!\nGuild: {}", event.guild_id);
}
async fn track_finish(&self, _: LavalinkClient, event: TrackFinish) {
tracing::info!("Track finished!\nGuild: {}", event.guild_id);
let player = {
let data = self.data.read().await;
let players = data.get::<MusicPlayers>().unwrap();
players.get(&event.guild_id.0).cloned()
};
if let Some(player) = player {
let mut player = player.lock().await;
if let Err(e) = player.play_next().await {
tracing::error!("Failed to play next song: {:?}", e);
}
if let Err(e) = player.update_now_playing().await {
tracing::error!("Failed to update now playing embed: {:?}", e);
}
}
}
async fn player_update(&self, _: LavalinkClient, event: PlayerUpdate) {
tracing::debug!("Received player update event: {:?}", event);
}
async fn stats(&self, _: LavalinkClient, event: Stats) {
tracing::debug!("Received stats event: {:?}", event);
}
}
pub struct Lavalink;
impl TypeMapKey for Lavalink {
type Value = Arc<LavalinkClient>;
}

@ -8,9 +8,9 @@ use responses::VideoInformation;
use youtube_dl::search_video_information; use youtube_dl::search_video_information;
pub mod inspirobot; pub mod inspirobot;
pub mod lavalink;
pub mod lyrics; pub mod lyrics;
pub mod player; pub mod player;
pub mod player_events;
pub mod queue; pub mod queue;
pub mod responses; pub mod responses;
pub mod spotify; pub mod spotify;

@ -1,26 +1,27 @@
use crate::messages::music::now_playing::update_now_playing_msg; use crate::messages::music::now_playing::update_now_playing_msg;
use crate::providers::music::lavalink::Lavalink;
use crate::providers::music::lyrics::get_lyrics; use crate::providers::music::lyrics::get_lyrics;
use crate::providers::music::queue::MusicQueue; use crate::providers::music::queue::MusicQueue;
use crate::utils::context_data::MusicPlayers; use crate::utils::context_data::MusicPlayers;
use crate::utils::error::{BotError, BotResult}; use crate::utils::error::{BotError, BotResult};
use lavalink_rs::LavalinkClient;
use serenity::prelude::TypeMap; use serenity::prelude::TypeMap;
use serenity::{ use serenity::{
client::Context, client::Context,
http::Http, http::Http,
model::id::{ChannelId, GuildId}, model::id::{ChannelId, GuildId},
}; };
use serenity_rich_interaction::core::{MessageHandle, SHORT_TIMEOUT}; use serenity_additions::core::{MessageHandle, SHORT_TIMEOUT};
use serenity_rich_interaction::ephemeral_message::EphemeralMessage; use serenity_additions::ephemeral_message::EphemeralMessage;
use songbird::tracks::TrackHandle;
use songbird::Songbird; use songbird::Songbird;
use std::mem; use std::mem;
use std::sync::Arc; use std::sync::Arc;
use std::time::Duration; use std::time::Duration;
use tokio::sync::{Mutex, RwLock}; use tokio::sync::{Mutex, RwLock};
use super::player_events::register_player_events;
pub struct MusicPlayer { pub struct MusicPlayer {
client: Arc<LavalinkClient>, manager: Arc<Songbird>,
http: Arc<Http>, http: Arc<Http>,
queue: MusicQueue, queue: MusicQueue,
guild_id: GuildId, guild_id: GuildId,
@ -28,19 +29,19 @@ pub struct MusicPlayer {
msg_channel: ChannelId, msg_channel: ChannelId,
leave_flag: bool, leave_flag: bool,
paused: bool, paused: bool,
equalizer: [f64; 15], current_track: Option<TrackHandle>,
} }
impl MusicPlayer { impl MusicPlayer {
/// Creates a new music player /// Creates a new music player
pub fn new( pub fn new(
client: Arc<LavalinkClient>, manager: Arc<Songbird>,
http: Arc<Http>, http: Arc<Http>,
guild_id: GuildId, guild_id: GuildId,
msg_channel: ChannelId, msg_channel: ChannelId,
) -> Self { ) -> Self {
Self { Self {
client, manager,
http, http,
guild_id, guild_id,
queue: MusicQueue::new(), queue: MusicQueue::new(),
@ -48,7 +49,7 @@ impl MusicPlayer {
now_playing_msg: None, now_playing_msg: None,
leave_flag: false, leave_flag: false,
paused: false, paused: false,
equalizer: [0f64; 15], current_track: None,
} }
} }
@ -60,20 +61,12 @@ impl MusicPlayer {
msg_channel_id: ChannelId, msg_channel_id: ChannelId,
) -> BotResult<Arc<Mutex<MusicPlayer>>> { ) -> BotResult<Arc<Mutex<MusicPlayer>>> {
let manager = songbird::get(ctx).await.unwrap(); let manager = songbird::get(ctx).await.unwrap();
let (handler, connection) = manager.join_gateway(guild_id, voice_channel_id).await; let (handler, _) = manager.join(guild_id, voice_channel_id).await;
let connection = connection?;
{
let mut handler = handler.lock().await;
handler.deafen(true).await?;
}
let player = { let player = {
let mut data = ctx.data.write().await; let mut data = ctx.data.write().await;
let client = data.get::<Lavalink>().unwrap();
client.create_session_with_songbird(&connection).await?;
let player = MusicPlayer::new( let player = MusicPlayer::new(
Arc::clone(client), Arc::clone(&manager),
Arc::clone(&ctx.http), Arc::clone(&ctx.http),
guild_id, guild_id,
msg_channel_id, msg_channel_id,
@ -84,6 +77,12 @@ impl MusicPlayer {
player player
}; };
{
let mut handler = handler.lock().await;
handler.deafen(true).await?;
register_player_events(player.clone(), &mut handler);
}
wait_for_disconnect( wait_for_disconnect(
Arc::clone(&ctx.data), Arc::clone(&ctx.data),
Arc::clone(&player), Arc::clone(&player),
@ -101,7 +100,9 @@ impl MusicPlayer {
/// Skips to the next song /// Skips to the next song
pub async fn skip(&mut self) -> BotResult<()> { pub async fn skip(&mut self) -> BotResult<()> {
self.client.stop(self.guild_id.0).await?; if let Some(track) = self.current_track.take() {
track.stop()?;
}
Ok(()) Ok(())
} }
@ -109,7 +110,9 @@ impl MusicPlayer {
/// Stops playback and leaves the channel /// Stops playback and leaves the channel
pub async fn stop(&mut self) -> BotResult<()> { pub async fn stop(&mut self) -> BotResult<()> {
self.queue.clear(); self.queue.clear();
self.client.stop(self.guild_id.0).await?; if let Some(track) = self.current_track.take() {
track.stop()?;
}
Ok(()) Ok(())
} }
@ -128,7 +131,9 @@ impl MusicPlayer {
pub async fn play_next(&mut self) -> BotResult<()> { pub async fn play_next(&mut self) -> BotResult<()> {
while !self.try_play_next().await? {} while !self.try_play_next().await? {}
if self.paused { if self.paused {
self.client.pause(self.guild_id).await?; if let Some(track) = self.current_track.as_ref() {
track.pause()?;
}
} }
Ok(()) Ok(())
@ -154,8 +159,8 @@ impl MusicPlayer {
tracing::debug!("Could not find playable candidate for song."); tracing::debug!("Could not find playable candidate for song.");
return Ok(false); return Ok(false);
}; };
let query_information = match self.client.auto_search_tracks(url).await { let source = match songbird::ytdl(url).await {
Ok(i) => i, Ok(s) => s,
Err(e) => { Err(e) => {
tracing::error!("Failed to search for song: {}", e); tracing::error!("Failed to search for song: {}", e);
self.send_error_message(format!( self.send_error_message(format!(
@ -168,12 +173,16 @@ impl MusicPlayer {
return Ok(false); return Ok(false);
} }
}; };
let handler_lock = self
if query_information.tracks.len() == 0 { .manager
return Ok(false); .get(self.guild_id.0)
.ok_or(BotError::MissingSongbirdClient)?;
{
let mut handler = handler_lock.lock().await;
let track_handle = handler.play_source(source);
self.current_track = Some(track_handle);
} }
let track = query_information.tracks[0].clone();
self.client.play(self.guild_id.0, track).start().await?;
self.queue.set_current(next); self.queue.set_current(next);
Ok(true) Ok(true)
@ -208,7 +217,13 @@ impl MusicPlayer {
/// Pauses playback /// Pauses playback
pub async fn toggle_paused(&mut self) -> BotResult<()> { pub async fn toggle_paused(&mut self) -> BotResult<()> {
self.paused = !self.paused; self.paused = !self.paused;
self.client.set_pause(self.guild_id.0, self.paused).await?; if let Some(track) = self.current_track.as_ref() {
if self.paused {
track.play()?;
} else {
track.pause()?;
}
}
Ok(()) Ok(())
} }
@ -242,37 +257,6 @@ impl MusicPlayer {
Ok(()) Ok(())
} }
/// Returns the equalizer
pub fn get_equalizer(&self) -> &[f64; 15] {
&self.equalizer
}
/// Equalizes a specified band
pub async fn equalize(&mut self, band: u8, value: f64) -> BotResult<()> {
if band > 15 {
return Err(BotError::from("Invalid Equalizer band"));
}
if value < -0.25 || value > 0.25 {
return Err(BotError::from("Invalid Equalizer value"));
}
self.equalizer[band as usize] = value;
self.client
.equalize_all(self.guild_id, self.equalizer)
.await?;
Ok(())
}
/// Equalizes all bands at the same time
pub async fn equalize_all(&mut self, bands: [f64; 15]) -> BotResult<()> {
self.equalizer = bands;
self.client
.equalize_all(self.guild_id, self.equalizer)
.await?;
Ok(())
}
} }
/// Stats a tokio coroutine to check for player disconnect conditions /// Stats a tokio coroutine to check for player disconnect conditions
@ -295,8 +279,11 @@ fn wait_for_disconnect(
tracing::debug!("Waiting to leave"); tracing::debug!("Waiting to leave");
if leave_in <= 0 { if leave_in <= 0 {
tracing::debug!("Leaving voice channel"); tracing::info!("Leaving voice channel");
if let Some(track) = player_lock.current_track.take() {
let _ = track.stop();
}
if let Some(handler) = manager.get(guild_id) { if let Some(handler) = manager.get(guild_id) {
let mut handler_lock = handler.lock().await; let mut handler_lock = handler.lock().await;
let _ = handler_lock.leave().await; let _ = handler_lock.leave().await;

@ -0,0 +1,30 @@
use serenity::async_trait;
use std::sync::Arc;
use serenity::prelude::Mutex;
use songbird::{Call, Event, EventContext, EventHandler, TrackEvent};
use super::player::MusicPlayer;
pub fn register_player_events(player: Arc<Mutex<MusicPlayer>>, handler: &mut Call) {
handler.add_global_event(Event::Track(TrackEvent::End), TrackEndHandler { player });
}
struct TrackEndHandler {
player: Arc<Mutex<MusicPlayer>>,
}
#[async_trait]
impl EventHandler for TrackEndHandler {
#[tracing::instrument(level = "debug", skip_all)]
async fn act(&self, _ctx: &EventContext<'_>) -> Option<Event> {
let mut player = self.player.lock().await;
if let Err(e) = player.play_next().await {
tracing::error!("Failed to play next song: {:?}", e);
}
if let Err(e) = player.update_now_playing().await {
tracing::error!("Failed to update now playing embed: {:?}", e);
}
None
}
}

@ -12,7 +12,6 @@ use bot_database::models::YoutubeSong;
pub struct MusicQueue { pub struct MusicQueue {
inner: VecDeque<Song>, inner: VecDeque<Song>,
current: Option<Song>, current: Option<Song>,
pub leave_flag: bool,
} }
impl MusicQueue { impl MusicQueue {
@ -20,7 +19,6 @@ impl MusicQueue {
Self { Self {
inner: VecDeque::new(), inner: VecDeque::new(),
current: None, current: None,
leave_flag: false,
} }
} }

@ -1,5 +1,4 @@
use lavalink_rs::error::LavalinkError; use serenity_additions::Error as SerenityUtilsError;
use serenity_rich_interaction::Error as SerenityUtilsError;
use thiserror::Error; use thiserror::Error;
pub type BotResult<T> = Result<T, BotError>; pub type BotResult<T> = Result<T, BotError>;
@ -34,7 +33,7 @@ pub enum BotError {
CliInject, CliInject,
#[error("Serenity Utils Error: {0}")] #[error("Serenity Utils Error: {0}")]
SerenityUtils(#[from] serenity_rich_interaction::Error), SerenityUtils(#[from] serenity_additions::Error),
#[error("Track Error: {0}")] #[error("Track Error: {0}")]
TrackError(#[from] songbird::error::TrackError), TrackError(#[from] songbird::error::TrackError),
@ -45,8 +44,8 @@ pub enum BotError {
#[error("YouTube Error: {0}")] #[error("YouTube Error: {0}")]
YoutubeError(#[from] youtube_metadata::error::Error), YoutubeError(#[from] youtube_metadata::error::Error),
#[error("Lavalink Error: {0}")] #[error("No songbird client for current guild")]
LavalinkError(#[from] LavalinkError), MissingSongbirdClient,
#[error("{0}")] #[error("{0}")]
Msg(String), Msg(String),

@ -1,17 +1,15 @@
use chrono::{DateTime, FixedOffset, Local}; use chrono::{DateTime, FixedOffset, Local};
use std::env;
use std::ops::Add; use std::ops::Add;
use std::sync::Arc; use std::sync::Arc;
use std::time::SystemTime; use std::time::SystemTime;
use lavalink_rs::LavalinkClient;
use serenity::client::Context; use serenity::client::Context;
use serenity::model::application::CurrentApplicationInfo;
use serenity::model::channel::Message; use serenity::model::channel::Message;
use serenity::prelude::{RwLock, TypeMap};
use tokio::time::Instant; use tokio::time::Instant;
use crate::providers::music::lavalink::{Lavalink, LavalinkHandler};
use crate::utils::context_data::get_database_from_context; use crate::utils::context_data::get_database_from_context;
use crate::utils::error::BotResult; use crate::utils::error::BotResult;
@ -92,28 +90,3 @@ pub async fn delete_messages_from_database(ctx: &Context) -> BotResult<()> {
Ok(()) Ok(())
} }
pub async fn initialize_lavalink(
data: Arc<RwLock<TypeMap>>,
current_application: CurrentApplicationInfo,
) -> BotResult<()> {
let lava_client = LavalinkClient::builder(current_application.id.0)
.set_host(env::var("LAVALINK_HOST").unwrap_or("172.0.0.1".to_string()))
.set_password(env::var("LAVALINK_PASSWORD").expect("Missing lavalink password"))
.set_port(
env::var("LAVALINK_PORT")
.ok()
.and_then(|s| s.parse().ok())
.expect("Missing lavalink port"),
)
.build(LavalinkHandler {
data: Arc::clone(&data),
})
.await?;
{
let mut data = data.write().await;
data.insert::<Lavalink>(Arc::new(lava_client));
}
Ok(())
}

Loading…
Cancel
Save