Merge pull request #26 from Trivernis/develop

Fixes and Features :)
feature/events v0.7.1
Trivernis 3 years ago committed by GitHub
commit 88a491ebfd
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23

7
Cargo.lock generated

@ -195,7 +195,7 @@ dependencies = [
[[package]]
name = "bot-database"
version = "0.5.0"
version = "0.6.0"
dependencies = [
"chrono",
"diesel",
@ -209,7 +209,7 @@ dependencies = [
[[package]]
name = "bot-serenityutils"
version = "0.2.3"
version = "0.2.4"
dependencies = [
"futures",
"log 0.4.14",
@ -1095,6 +1095,7 @@ dependencies = [
"serde",
"serde-aux",
"serde_json",
"serenity",
"songbird",
"tokio",
"tokio-native-tls",
@ -2521,7 +2522,7 @@ checksum = "cda74da7e1a664f795bb1f8a87ec406fb89a02522cf6e50620d016add6dbbf5c"
[[package]]
name = "tobi-rs"
version = "0.7.0"
version = "0.7.1"
dependencies = [
"aspotify",
"bot-coreutils",

@ -1,6 +1,6 @@
[package]
name = "tobi-rs"
version = "0.7.0"
version = "0.7.1"
authors = ["trivernis <trivernis@protonmail.com>"]
edition = "2018"
@ -39,4 +39,4 @@ rustc_version_runtime = "0.2.0"
trigram = "0.4.4"
typemap_rev = "0.1.5"
youtube-metadata = "0.1.1"
lavalink-rs = {version="0.7.1", features=["native"]}
lavalink-rs = {version="0.7.1", features=["native", "serenity"]}

@ -1,6 +1,6 @@
[package]
name = "bot-database"
version = "0.5.0"
version = "0.6.0"
authors = ["trivernis <trivernis@protonmail.com>"]
edition = "2018"

@ -0,0 +1,2 @@
-- This file should undo anything in `up.sql`
ALTER TABLE media RENAME TO gifs;

@ -0,0 +1,2 @@
-- Your SQL goes here
ALTER TABLE gifs RENAME TO media;

@ -9,42 +9,42 @@ use crate::Database;
impl Database {
/// Returns a list of all gifs in the database
pub async fn get_all_gifs(&self) -> DatabaseResult<Vec<Gif>> {
use gifs::dsl;
pub async fn get_all_media(&self) -> DatabaseResult<Vec<Media>> {
use media::dsl;
log::debug!("Loading all gifs from the database");
let gifs: Vec<Gif> = dsl::gifs.load_async::<Gif>(&self.pool).await?;
let gifs: Vec<Media> = dsl::media.load_async::<Media>(&self.pool).await?;
Ok(gifs)
}
/// Returns a list of gifs by assigned category
pub async fn get_gifs_by_category(&self, category: &str) -> DatabaseResult<Vec<Gif>> {
use gifs::dsl;
pub async fn get_media_by_category(&self, category: &str) -> DatabaseResult<Vec<Media>> {
use media::dsl;
log::debug!("Searching for gifs in category '{}'", category);
let gifs: Vec<Gif> = dsl::gifs
let gifs: Vec<Media> = dsl::media
.filter(dsl::category.eq(category))
.load_async::<Gif>(&self.pool)
.load_async::<Media>(&self.pool)
.await?;
Ok(gifs)
}
/// Adds a gif to the database
pub async fn add_gif(
pub async fn add_media(
&self,
url: &str,
category: Option<String>,
name: Option<String>,
) -> DatabaseResult<()> {
use gifs::dsl;
use media::dsl;
log::debug!(
"Inserting gif with url '{}' and name {:?} and category {:?}",
url,
name,
category
);
insert_into(dsl::gifs)
.values(GifInsert {
insert_into(dsl::media)
.values(MediaInsert {
url: url.to_string(),
name,
category,

@ -1,16 +1,16 @@
pub use ephemeral_messages::*;
pub use gifs::*;
pub use guild_playlists::*;
pub use guild_playlists::*;
pub use media::*;
pub use statistics::*;
pub use youtube_songs::*;
use crate::PoolConnection;
mod ephemeral_messages;
mod gifs;
mod guild_playlists;
mod guild_settings;
mod media;
mod statistics;
mod youtube_songs;

@ -32,7 +32,7 @@ pub struct GuildPlaylistInsert {
}
#[derive(Queryable, Debug, Clone)]
pub struct Gif {
pub struct Media {
pub id: i64,
pub category: Option<String>,
pub name: Option<String>,
@ -40,8 +40,8 @@ pub struct Gif {
}
#[derive(Insertable, Debug)]
#[table_name = "gifs"]
pub struct GifInsert {
#[table_name = "media"]
pub struct MediaInsert {
pub category: Option<String>,
pub name: Option<String>,
pub url: String,

@ -6,15 +6,6 @@ table! {
}
}
table! {
gifs (id) {
id -> Int8,
category -> Nullable<Varchar>,
name -> Nullable<Varchar>,
url -> Varchar,
}
}
table! {
guild_playlists (guild_id, name) {
guild_id -> Int8,
@ -31,6 +22,15 @@ table! {
}
}
table! {
media (id) {
id -> Int8,
category -> Nullable<Varchar>,
name -> Nullable<Varchar>,
url -> Varchar,
}
}
table! {
statistics (id) {
id -> Int8,
@ -56,9 +56,9 @@ table! {
allow_tables_to_appear_in_same_query!(
ephemeral_messages,
gifs,
guild_playlists,
guild_settings,
media,
statistics,
youtube_songs,
);

@ -1,6 +1,6 @@
[package]
name = "bot-serenityutils"
version = "0.2.3"
version = "0.2.4"
authors = ["trivernis <trivernis@protonmail.com>"]
edition = "2018"

@ -124,7 +124,7 @@ pub async fn toggle_help(
}
/// Displays the menu page
async fn display_page(ctx: &Context, menu: &mut Menu<'_>) -> SerenityUtilsResult<()> {
pub async fn display_page(ctx: &Context, menu: &mut Menu<'_>) -> SerenityUtilsResult<()> {
log::debug!("Displaying page {}", menu.current_page);
let page = menu
.pages

@ -8,14 +8,14 @@ use serenity::framework::standard::{Args, CommandResult};
use serenity::model::channel::Message;
#[command]
#[description("Simple ping test command")]
#[description("Adds media to the database")]
#[usage("<url> [<category>] [<name>]")]
#[bucket("general")]
#[aliases("add-gif", "addgif")]
#[aliases("add_gif", "add-gif", "addgif", "add-media", "addmedia")]
#[min_args(1)]
#[max_args(3)]
#[owners_only]
async fn add_gif(ctx: &Context, msg: &Message, mut args: Args) -> CommandResult {
async fn add_media(ctx: &Context, msg: &Message, mut args: Args) -> CommandResult {
let url = args.single::<String>()?;
if !url::is_valid(&url) {
@ -26,10 +26,10 @@ async fn add_gif(ctx: &Context, msg: &Message, mut args: Args) -> CommandResult
let name = args.single_quoted::<String>().ok();
let database = get_database_from_context(&ctx).await;
database.add_gif(&url, category, name).await?;
database.add_media(&url, category, name).await?;
EphemeralMessage::create(&ctx.http, msg.channel_id, SHORT_TIMEOUT, |c| {
c.reference_message(msg)
.content("Gif added to the database.")
.content("Media entry added to the database.")
})
.await?;

@ -1,4 +1,4 @@
use crate::messages::gifs::create_gifs_menu;
use crate::messages::gifs::create_media_menu;
use crate::utils::context_data::get_database_from_context;
use serenity::client::Context;
use serenity::framework::standard::macros::command;
@ -9,10 +9,10 @@ use serenity::model::channel::Message;
#[description("Displays a list of all gifs used by the bot")]
#[bucket("general")]
#[only_in(guilds)]
async fn gifs(ctx: &Context, msg: &Message) -> CommandResult {
async fn media(ctx: &Context, msg: &Message) -> CommandResult {
let database = get_database_from_context(ctx).await;
let gifs = database.get_all_gifs().await?;
create_gifs_menu(ctx, msg.channel_id, gifs).await?;
let gifs = database.get_all_media().await?;
create_media_menu(ctx, msg.channel_id, gifs).await?;
Ok(())
}

@ -1,9 +1,9 @@
use serenity::framework::standard::macros::group;
use about::ABOUT_COMMAND;
use add_gif::ADD_GIF_COMMAND;
use add_media::ADD_MEDIA_COMMAND;
use clear::CLEAR_COMMAND;
use gifs::GIFS_COMMAND;
use media::MEDIA_COMMAND;
use pain::PAIN_COMMAND;
use ping::PING_COMMAND;
use qalc::QALC_COMMAND;
@ -13,10 +13,10 @@ use time::TIME_COMMAND;
use timezones::TIMEZONES_COMMAND;
mod about;
mod add_gif;
mod add_media;
mod clear;
mod gifs;
pub(crate) mod help;
mod media;
mod pain;
mod ping;
mod qalc;
@ -27,6 +27,6 @@ mod timezones;
#[group]
#[commands(
ping, stats, shutdown, time, timezones, qalc, about, add_gif, gifs, pain, clear
ping, stats, shutdown, time, timezones, qalc, about, add_media, media, pain, clear
)]
pub struct Misc;

@ -20,23 +20,23 @@ async fn pain(ctx: &Context, msg: &Message, args: Args) -> CommandResult {
log::debug!("Got pain command");
let pain_type = args.message().to_lowercase();
let database = get_database_from_context(ctx).await;
let mut gifs = database
.get_gifs_by_category(format!("{}{}", CATEGORY_PREFIX, pain_type).as_str())
let mut media = database
.get_media_by_category(format!("{}{}", CATEGORY_PREFIX, pain_type).as_str())
.await?;
if gifs.is_empty() {
log::debug!("No gif found for pain {}. Using 404", pain_type);
gifs = database
.get_gifs_by_category(format!("{}{}", CATEGORY_PREFIX, NOT_FOUND_PAIN).as_str())
if media.is_empty() {
log::debug!("No media found for pain {}. Using 404", pain_type);
media = database
.get_media_by_category(format!("{}{}", CATEGORY_PREFIX, NOT_FOUND_PAIN).as_str())
.await?;
}
let gif = gifs
let entry = media
.into_iter()
.choose(&mut rand::thread_rng())
.ok_or(BotError::from("No gifs found."))?;
log::trace!("Gif for pain is {:?}", gif);
msg.reply(ctx, gif.url).await?;
log::trace!("Gif for pain is {:?}", entry);
msg.reply(ctx, entry.url).await?;
Ok(())
}

@ -0,0 +1,62 @@
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 bot_serenityutils::core::{MEDIUM_TIMEOUT, SHORT_TIMEOUT};
use bot_serenityutils::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();
log::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(())
}

@ -0,0 +1,33 @@
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();
log::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(())
}

@ -11,22 +11,6 @@ use serenity::model::user::User;
use songbird::Songbird;
use tokio::sync::Mutex;
use clear_queue::CLEAR_QUEUE_COMMAND;
use current::CURRENT_COMMAND;
use join::JOIN_COMMAND;
use leave::LEAVE_COMMAND;
use lyrics::LYRICS_COMMAND;
use move_song::MOVE_SONG_COMMAND;
use pause::PAUSE_COMMAND;
use play::PLAY_COMMAND;
use play_next::PLAY_NEXT_COMMAND;
use playlists::PLAYLISTS_COMMAND;
use queue::QUEUE_COMMAND;
use remove_song::REMOVE_SONG_COMMAND;
use save_playlist::SAVE_PLAYLIST_COMMAND;
use shuffle::SHUFFLE_COMMAND;
use skip::SKIP_COMMAND;
use crate::providers::music::player::MusicPlayer;
use crate::providers::music::queue::Song;
use crate::providers::music::{add_youtube_song_to_database, youtube_dl};
@ -41,6 +25,8 @@ use youtube_metadata::get_video_information;
mod clear_queue;
mod current;
mod equalize;
mod equalizer;
mod join;
mod leave;
mod lyrics;
@ -55,6 +41,24 @@ mod save_playlist;
mod shuffle;
mod skip;
use clear_queue::CLEAR_QUEUE_COMMAND;
use current::CURRENT_COMMAND;
use equalize::EQUALIZE_COMMAND;
use equalizer::EQUALIZER_COMMAND;
use join::JOIN_COMMAND;
use leave::LEAVE_COMMAND;
use lyrics::LYRICS_COMMAND;
use move_song::MOVE_SONG_COMMAND;
use pause::PAUSE_COMMAND;
use play::PLAY_COMMAND;
use play_next::PLAY_NEXT_COMMAND;
use playlists::PLAYLISTS_COMMAND;
use queue::QUEUE_COMMAND;
use remove_song::REMOVE_SONG_COMMAND;
use save_playlist::SAVE_PLAYLIST_COMMAND;
use shuffle::SHUFFLE_COMMAND;
use skip::SKIP_COMMAND;
#[group]
#[commands(
join,
@ -71,7 +75,9 @@ mod skip;
playlists,
lyrics,
move_song,
remove_song
remove_song,
equalizer,
equalize
)]
pub struct Music;

@ -0,0 +1,14 @@
use crate::commands::weeb::post_random_media;
use serenity::client::Context;
use serenity::framework::standard::macros::command;
use serenity::framework::standard::CommandResult;
use serenity::model::channel::Message;
#[command]
#[description("Posts a random fubuki")]
#[usage("")]
#[aliases("scatman")]
#[bucket("general")]
async fn fubuki(ctx: &Context, msg: &Message) -> CommandResult {
post_random_media(ctx, msg, "fubuki").await
}

@ -0,0 +1,14 @@
use crate::commands::weeb::post_random_media;
use serenity::client::Context;
use serenity::framework::standard::macros::command;
use serenity::framework::standard::CommandResult;
use serenity::model::channel::Message;
#[command]
#[description("Posts a random korone gif")]
#[usage("")]
#[aliases("yubi")]
#[bucket("general")]
async fn korone(ctx: &Context, msg: &Message) -> CommandResult {
post_random_media(ctx, msg, "korone").await
}

@ -1,26 +1,13 @@
use crate::utils::context_data::get_database_from_context;
use crate::utils::error::BotError;
use rand::prelude::IteratorRandom;
use crate::commands::weeb::post_random_media;
use serenity::client::Context;
use serenity::framework::standard::macros::command;
use serenity::framework::standard::CommandResult;
use serenity::model::channel::Message;
static GIF_CATEGORY: &str = "matsuri";
#[command]
#[description("Posts a random matsuri gif")]
#[usage("")]
#[bucket("general")]
async fn matsuri(ctx: &Context, msg: &Message) -> CommandResult {
let database = get_database_from_context(ctx).await;
let gifs = database.get_gifs_by_category(GIF_CATEGORY).await?;
let gif = gifs
.into_iter()
.choose(&mut rand::thread_rng())
.ok_or(BotError::from("No gifs found."))?;
msg.channel_id.say(ctx, gif.url).await?;
Ok(())
post_random_media(ctx, msg, "matsuri").await
}

@ -0,0 +1,14 @@
use crate::commands::weeb::post_random_media;
use serenity::client::Context;
use serenity::framework::standard::macros::command;
use serenity::framework::standard::CommandResult;
use serenity::model::channel::Message;
#[command]
#[description("Posts a random miko")]
#[usage("")]
#[aliases("faq", "elite")]
#[bucket("general")]
async fn miko(ctx: &Context, msg: &Message) -> CommandResult {
post_random_media(ctx, msg, "miko").await
}

@ -1,13 +1,42 @@
use serenity::framework::standard::macros::group;
mod fubuki;
mod korone;
mod matsuri;
mod miko;
mod pekofy;
mod rushia;
mod sauce;
use crate::utils::context_data::get_database_from_context;
use crate::utils::error::BotError;
use rand::prelude::IteratorRandom;
use serenity::client::Context;
use serenity::framework::standard::CommandResult;
use serenity::model::channel::Message;
use fubuki::FUBUKI_COMMAND;
use korone::KORONE_COMMAND;
use matsuri::MATSURI_COMMAND;
use miko::MIKO_COMMAND;
use pekofy::PEKOFY_COMMAND;
use rushia::RUSHIA_COMMAND;
use sauce::SAUCE_COMMAND;
#[group]
#[commands(pekofy, sauce, matsuri)]
#[commands(pekofy, sauce, matsuri, korone, rushia, fubuki, miko)]
pub struct Weeb;
/// Posts a random media entry with the given category
async fn post_random_media(ctx: &Context, msg: &Message, category: &str) -> CommandResult {
let database = get_database_from_context(ctx).await;
let media = database.get_media_by_category(category).await?;
let gif = media
.into_iter()
.choose(&mut rand::thread_rng())
.ok_or(BotError::from("No media found."))?;
msg.channel_id.say(ctx, gif.url).await?;
Ok(())
}

@ -7,7 +7,7 @@ use serenity::{framework::standard::macros::command, prelude::*};
use crate::utils::context_data::get_database_from_context;
use crate::utils::error::{BotError, BotResult};
use crate::utils::get_previous_message_or_reply;
use bot_database::models::Gif;
use bot_database::models::Media;
// return a normal peko in most cases
static PEKOS: &[&str] = &[
@ -18,7 +18,7 @@ static PEKOS: &[&str] = &[
"🇵 🇪 🇰 🇴",
"p3k0",
];
static GIF_CATEGORY: &str = "pain-peko";
static MEDIA_CATEGORY: &str = "pain-peko";
#[command]
#[description("Pekofy messages")]
@ -48,7 +48,7 @@ async fn pekofy(ctx: &Context, msg: &Message, args: Args) -> CommandResult {
alpha_lowercase.retain(|c| c.is_alphanumeric());
let pekofied: String = if alpha_lowercase == "pain" {
random_pain_gif(ctx).await?.url
random_pain_media(ctx).await?.url
} else if PEKOS.contains(&&*alpha_lowercase) {
random_peko()
} else {
@ -114,10 +114,10 @@ fn random_peko() -> String {
}
/// Chooses a random pain peko gif
async fn random_pain_gif(ctx: &Context) -> BotResult<Gif> {
async fn random_pain_media(ctx: &Context) -> BotResult<Media> {
let database = get_database_from_context(ctx).await;
let gifs = database.get_gifs_by_category(GIF_CATEGORY).await?;
let gifs = database.get_media_by_category(MEDIA_CATEGORY).await?;
gifs.into_iter()
.choose(&mut rand::thread_rng())
.ok_or(BotError::from("No gifs found"))
.ok_or(BotError::from("No media found"))
}

@ -0,0 +1,14 @@
use crate::commands::weeb::post_random_media;
use serenity::client::Context;
use serenity::framework::standard::macros::command;
use serenity::framework::standard::CommandResult;
use serenity::model::channel::Message;
#[command]
#[description("Posts a random rushia")]
#[usage("")]
#[aliases("cuttingboard", "cutting-board", "petan")]
#[bucket("general")]
async fn rushia(ctx: &Context, msg: &Message) -> CommandResult {
post_random_media(ctx, msg, "rushia").await
}

@ -1,5 +1,5 @@
use crate::utils::error::BotResult;
use bot_database::models::Gif;
use bot_database::models::Media;
use bot_serenityutils::menu::{MenuBuilder, Page};
use serenity::builder::CreateMessage;
use serenity::client::Context;
@ -7,16 +7,16 @@ use serenity::model::id::ChannelId;
use std::time::Duration;
/// Creates a new gifs embed
pub async fn create_gifs_menu(
pub async fn create_media_menu(
ctx: &Context,
channel_id: ChannelId,
gifs: Vec<Gif>,
media: Vec<Media>,
) -> BotResult<()> {
let total_pages = (gifs.len() as f32 / 10.0).ceil() as usize;
let pages: Vec<Page> = gifs
let total_pages = (media.len() as f32 / 10.0).ceil() as usize;
let pages: Vec<Page> = media
.chunks(10)
.enumerate()
.map(|(page, gifs)| create_gifs_page(page + 1, total_pages, gifs.to_vec()))
.map(|(page, media)| create_media_page(page + 1, total_pages, media.to_vec()))
.collect();
MenuBuilder::new_paginator()
@ -30,21 +30,21 @@ pub async fn create_gifs_menu(
}
/// Creates a new gif page
pub fn create_gifs_page(page: usize, total_pages: usize, gifs: Vec<Gif>) -> Page<'static> {
pub fn create_media_page(page: usize, total_pages: usize, media: Vec<Media>) -> Page<'static> {
let mut message = CreateMessage::default();
let description_lines: Vec<String> = gifs
let description_lines: Vec<String> = media
.into_iter()
.map(|g| {
.map(|m| {
format!(
"{} - {} - [Source]({})",
g.category.unwrap_or("*N/A*".to_string()),
g.name.unwrap_or("*N/A*".to_string()),
g.url
m.category.unwrap_or("*N/A*".to_string()),
m.name.unwrap_or("*N/A*".to_string()),
m.url
)
})
.collect();
message.embed(|e| {
e.title("Gifs")
e.title("Media")
.description(description_lines.join("\n"))
.footer(|f| f.text(format!("Page {} of {}", page, total_pages)))
});

@ -0,0 +1,262 @@
use crate::commands::music::is_dj;
use crate::providers::music::player::MusicPlayer;
use crate::utils::error::BotResult;
use bot_serenityutils::core::EXTRA_LONG_TIMEOUT;
use bot_serenityutils::error::SerenityUtilsResult;
use bot_serenityutils::menu::{display_page, Menu, MenuBuilder, Page};
use serenity::builder::{CreateEmbed, CreateMessage};
use serenity::client::Context;
use serenity::model::channel::Reaction;
use serenity::model::id::ChannelId;
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,3 +1,4 @@
pub mod equalizer;
pub mod no_voicechannel;
pub mod now_playing;
pub mod queue;

@ -3,7 +3,7 @@ use crate::providers::music::lavalink::Lavalink;
use crate::providers::music::lyrics::get_lyrics;
use crate::providers::music::queue::MusicQueue;
use crate::utils::context_data::MusicPlayers;
use crate::utils::error::BotResult;
use crate::utils::error::{BotError, BotResult};
use bot_serenityutils::core::{MessageHandle, SHORT_TIMEOUT};
use bot_serenityutils::ephemeral_message::EphemeralMessage;
use lavalink_rs::LavalinkClient;
@ -28,6 +28,7 @@ pub struct MusicPlayer {
msg_channel: ChannelId,
leave_flag: bool,
paused: bool,
equalizer: [f64; 15],
}
impl MusicPlayer {
@ -47,6 +48,7 @@ impl MusicPlayer {
now_playing_msg: None,
leave_flag: false,
paused: false,
equalizer: [0f64; 15],
}
}
@ -120,6 +122,9 @@ impl MusicPlayer {
/// Plays the next song in the queue
pub async fn play_next(&mut self) -> BotResult<()> {
while !self.try_play_next().await? {}
if self.paused {
self.client.pause(self.guild_id).await?;
}
Ok(())
}
@ -232,6 +237,37 @@ impl MusicPlayer {
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

Loading…
Cancel
Save