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

@ -1,6 +1,6 @@
[package] [package]
name = "tobi-rs" name = "tobi-rs"
version = "0.7.0" version = "0.7.1"
authors = ["trivernis <trivernis@protonmail.com>"] authors = ["trivernis <trivernis@protonmail.com>"]
edition = "2018" edition = "2018"
@ -39,4 +39,4 @@ rustc_version_runtime = "0.2.0"
trigram = "0.4.4" trigram = "0.4.4"
typemap_rev = "0.1.5" typemap_rev = "0.1.5"
youtube-metadata = "0.1.1" 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] [package]
name = "bot-database" name = "bot-database"
version = "0.5.0" version = "0.6.0"
authors = ["trivernis <trivernis@protonmail.com>"] authors = ["trivernis <trivernis@protonmail.com>"]
edition = "2018" 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 { impl Database {
/// Returns a list of all gifs in the database /// Returns a list of all gifs in the database
pub async fn get_all_gifs(&self) -> DatabaseResult<Vec<Gif>> { pub async fn get_all_media(&self) -> DatabaseResult<Vec<Media>> {
use gifs::dsl; use media::dsl;
log::debug!("Loading all gifs from the database"); 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) Ok(gifs)
} }
/// Returns a list of gifs by assigned category /// Returns a list of gifs by assigned category
pub async fn get_gifs_by_category(&self, category: &str) -> DatabaseResult<Vec<Gif>> { pub async fn get_media_by_category(&self, category: &str) -> DatabaseResult<Vec<Media>> {
use gifs::dsl; use media::dsl;
log::debug!("Searching for gifs in category '{}'", category); 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)) .filter(dsl::category.eq(category))
.load_async::<Gif>(&self.pool) .load_async::<Media>(&self.pool)
.await?; .await?;
Ok(gifs) Ok(gifs)
} }
/// Adds a gif to the database /// Adds a gif to the database
pub async fn add_gif( pub async fn add_media(
&self, &self,
url: &str, url: &str,
category: Option<String>, category: Option<String>,
name: Option<String>, name: Option<String>,
) -> DatabaseResult<()> { ) -> DatabaseResult<()> {
use gifs::dsl; use media::dsl;
log::debug!( log::debug!(
"Inserting gif with url '{}' and name {:?} and category {:?}", "Inserting gif with url '{}' and name {:?} and category {:?}",
url, url,
name, name,
category category
); );
insert_into(dsl::gifs) insert_into(dsl::media)
.values(GifInsert { .values(MediaInsert {
url: url.to_string(), url: url.to_string(),
name, name,
category, category,

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

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

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

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

@ -124,7 +124,7 @@ pub async fn toggle_help(
} }
/// Displays the menu page /// 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); log::debug!("Displaying page {}", menu.current_page);
let page = menu let page = menu
.pages .pages

@ -8,14 +8,14 @@ use serenity::framework::standard::{Args, CommandResult};
use serenity::model::channel::Message; use serenity::model::channel::Message;
#[command] #[command]
#[description("Simple ping test command")] #[description("Adds media to the database")]
#[usage("<url> [<category>] [<name>]")] #[usage("<url> [<category>] [<name>]")]
#[bucket("general")] #[bucket("general")]
#[aliases("add-gif", "addgif")] #[aliases("add_gif", "add-gif", "addgif", "add-media", "addmedia")]
#[min_args(1)] #[min_args(1)]
#[max_args(3)] #[max_args(3)]
#[owners_only] #[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>()?; let url = args.single::<String>()?;
if !url::is_valid(&url) { 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 name = args.single_quoted::<String>().ok();
let database = get_database_from_context(&ctx).await; 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| { EphemeralMessage::create(&ctx.http, msg.channel_id, SHORT_TIMEOUT, |c| {
c.reference_message(msg) c.reference_message(msg)
.content("Gif added to the database.") .content("Media entry added to the database.")
}) })
.await?; .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 crate::utils::context_data::get_database_from_context;
use serenity::client::Context; use serenity::client::Context;
use serenity::framework::standard::macros::command; 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")] #[description("Displays a list of all gifs used by the bot")]
#[bucket("general")] #[bucket("general")]
#[only_in(guilds)] #[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 database = get_database_from_context(ctx).await;
let gifs = database.get_all_gifs().await?; let gifs = database.get_all_media().await?;
create_gifs_menu(ctx, msg.channel_id, gifs).await?; create_media_menu(ctx, msg.channel_id, gifs).await?;
Ok(()) Ok(())
} }

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

@ -20,23 +20,23 @@ async fn pain(ctx: &Context, msg: &Message, args: Args) -> CommandResult {
log::debug!("Got pain command"); log::debug!("Got pain command");
let pain_type = args.message().to_lowercase(); let pain_type = args.message().to_lowercase();
let database = get_database_from_context(ctx).await; let database = get_database_from_context(ctx).await;
let mut gifs = database let mut media = database
.get_gifs_by_category(format!("{}{}", CATEGORY_PREFIX, pain_type).as_str()) .get_media_by_category(format!("{}{}", CATEGORY_PREFIX, pain_type).as_str())
.await?; .await?;
if gifs.is_empty() { if media.is_empty() {
log::debug!("No gif found for pain {}. Using 404", pain_type); log::debug!("No media found for pain {}. Using 404", pain_type);
gifs = database media = database
.get_gifs_by_category(format!("{}{}", CATEGORY_PREFIX, NOT_FOUND_PAIN).as_str()) .get_media_by_category(format!("{}{}", CATEGORY_PREFIX, NOT_FOUND_PAIN).as_str())
.await?; .await?;
} }
let gif = gifs let entry = media
.into_iter() .into_iter()
.choose(&mut rand::thread_rng()) .choose(&mut rand::thread_rng())
.ok_or(BotError::from("No gifs found."))?; .ok_or(BotError::from("No gifs found."))?;
log::trace!("Gif for pain is {:?}", gif); log::trace!("Gif for pain is {:?}", entry);
msg.reply(ctx, gif.url).await?; msg.reply(ctx, entry.url).await?;
Ok(()) 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 songbird::Songbird;
use tokio::sync::Mutex; 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::player::MusicPlayer;
use crate::providers::music::queue::Song; use crate::providers::music::queue::Song;
use crate::providers::music::{add_youtube_song_to_database, youtube_dl}; 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 clear_queue;
mod current; mod current;
mod equalize;
mod equalizer;
mod join; mod join;
mod leave; mod leave;
mod lyrics; mod lyrics;
@ -55,6 +41,24 @@ mod save_playlist;
mod shuffle; mod shuffle;
mod skip; 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] #[group]
#[commands( #[commands(
join, join,
@ -71,7 +75,9 @@ mod skip;
playlists, playlists,
lyrics, lyrics,
move_song, move_song,
remove_song remove_song,
equalizer,
equalize
)] )]
pub struct Music; 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::commands::weeb::post_random_media;
use crate::utils::error::BotError;
use rand::prelude::IteratorRandom;
use serenity::client::Context; use serenity::client::Context;
use serenity::framework::standard::macros::command; use serenity::framework::standard::macros::command;
use serenity::framework::standard::CommandResult; use serenity::framework::standard::CommandResult;
use serenity::model::channel::Message; use serenity::model::channel::Message;
static GIF_CATEGORY: &str = "matsuri";
#[command] #[command]
#[description("Posts a random matsuri gif")] #[description("Posts a random matsuri gif")]
#[usage("")] #[usage("")]
#[bucket("general")] #[bucket("general")]
async fn matsuri(ctx: &Context, msg: &Message) -> CommandResult { async fn matsuri(ctx: &Context, msg: &Message) -> CommandResult {
let database = get_database_from_context(ctx).await; post_random_media(ctx, msg, "matsuri").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(())
} }

@ -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; use serenity::framework::standard::macros::group;
mod fubuki;
mod korone;
mod matsuri; mod matsuri;
mod miko;
mod pekofy; mod pekofy;
mod rushia;
mod sauce; 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 matsuri::MATSURI_COMMAND;
use miko::MIKO_COMMAND;
use pekofy::PEKOFY_COMMAND; use pekofy::PEKOFY_COMMAND;
use rushia::RUSHIA_COMMAND;
use sauce::SAUCE_COMMAND; use sauce::SAUCE_COMMAND;
#[group] #[group]
#[commands(pekofy, sauce, matsuri)] #[commands(pekofy, sauce, matsuri, korone, rushia, fubuki, miko)]
pub struct Weeb; 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::context_data::get_database_from_context;
use crate::utils::error::{BotError, BotResult}; use crate::utils::error::{BotError, BotResult};
use crate::utils::get_previous_message_or_reply; 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 // return a normal peko in most cases
static PEKOS: &[&str] = &[ static PEKOS: &[&str] = &[
@ -18,7 +18,7 @@ static PEKOS: &[&str] = &[
"🇵 🇪 🇰 🇴", "🇵 🇪 🇰 🇴",
"p3k0", "p3k0",
]; ];
static GIF_CATEGORY: &str = "pain-peko"; static MEDIA_CATEGORY: &str = "pain-peko";
#[command] #[command]
#[description("Pekofy messages")] #[description("Pekofy messages")]
@ -48,7 +48,7 @@ async fn pekofy(ctx: &Context, msg: &Message, args: Args) -> CommandResult {
alpha_lowercase.retain(|c| c.is_alphanumeric()); alpha_lowercase.retain(|c| c.is_alphanumeric());
let pekofied: String = if alpha_lowercase == "pain" { 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) { } else if PEKOS.contains(&&*alpha_lowercase) {
random_peko() random_peko()
} else { } else {
@ -114,10 +114,10 @@ fn random_peko() -> String {
} }
/// Chooses a random pain peko gif /// 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 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() gifs.into_iter()
.choose(&mut rand::thread_rng()) .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 crate::utils::error::BotResult;
use bot_database::models::Gif; use bot_database::models::Media;
use bot_serenityutils::menu::{MenuBuilder, Page}; use bot_serenityutils::menu::{MenuBuilder, Page};
use serenity::builder::CreateMessage; use serenity::builder::CreateMessage;
use serenity::client::Context; use serenity::client::Context;
@ -7,16 +7,16 @@ use serenity::model::id::ChannelId;
use std::time::Duration; use std::time::Duration;
/// Creates a new gifs embed /// Creates a new gifs embed
pub async fn create_gifs_menu( pub async fn create_media_menu(
ctx: &Context, ctx: &Context,
channel_id: ChannelId, channel_id: ChannelId,
gifs: Vec<Gif>, media: Vec<Media>,
) -> BotResult<()> { ) -> BotResult<()> {
let total_pages = (gifs.len() as f32 / 10.0).ceil() as usize; let total_pages = (media.len() as f32 / 10.0).ceil() as usize;
let pages: Vec<Page> = gifs let pages: Vec<Page> = media
.chunks(10) .chunks(10)
.enumerate() .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(); .collect();
MenuBuilder::new_paginator() MenuBuilder::new_paginator()
@ -30,21 +30,21 @@ pub async fn create_gifs_menu(
} }
/// Creates a new gif page /// 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 mut message = CreateMessage::default();
let description_lines: Vec<String> = gifs let description_lines: Vec<String> = media
.into_iter() .into_iter()
.map(|g| { .map(|m| {
format!( format!(
"{} - {} - [Source]({})", "{} - {} - [Source]({})",
g.category.unwrap_or("*N/A*".to_string()), m.category.unwrap_or("*N/A*".to_string()),
g.name.unwrap_or("*N/A*".to_string()), m.name.unwrap_or("*N/A*".to_string()),
g.url m.url
) )
}) })
.collect(); .collect();
message.embed(|e| { message.embed(|e| {
e.title("Gifs") e.title("Media")
.description(description_lines.join("\n")) .description(description_lines.join("\n"))
.footer(|f| f.text(format!("Page {} of {}", page, total_pages))) .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 no_voicechannel;
pub mod now_playing; pub mod now_playing;
pub mod queue; pub mod queue;

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

Loading…
Cancel
Save