commit
3d02241642
@ -1,241 +0,0 @@
|
||||
use crate::error::DatabaseResult;
|
||||
use crate::models::*;
|
||||
use crate::schema::*;
|
||||
use crate::PoolConnection;
|
||||
use diesel::dsl::count;
|
||||
use diesel::prelude::*;
|
||||
use diesel::{delete, insert_into};
|
||||
use std::any;
|
||||
use std::fmt::Debug;
|
||||
use std::str::FromStr;
|
||||
use std::time::SystemTime;
|
||||
use tokio_diesel::*;
|
||||
|
||||
#[derive(Clone)]
|
||||
pub struct Database {
|
||||
pool: PoolConnection,
|
||||
}
|
||||
|
||||
unsafe impl Send for Database {}
|
||||
unsafe impl Sync for Database {}
|
||||
|
||||
impl Database {
|
||||
pub fn new(pool: PoolConnection) -> Self {
|
||||
Self { pool }
|
||||
}
|
||||
|
||||
/// Returns a guild setting from the database
|
||||
pub async fn get_guild_setting<T: 'static>(
|
||||
&self,
|
||||
guild_id: u64,
|
||||
key: String,
|
||||
) -> DatabaseResult<Option<T>>
|
||||
where
|
||||
T: FromStr,
|
||||
{
|
||||
use guild_settings::dsl;
|
||||
log::debug!("Retrieving setting '{}' for guild {}", key, guild_id);
|
||||
|
||||
let entries: Vec<GuildSetting> = dsl::guild_settings
|
||||
.filter(dsl::guild_id.eq(guild_id as i64))
|
||||
.filter(dsl::key.eq(key))
|
||||
.load_async::<GuildSetting>(&self.pool)
|
||||
.await?;
|
||||
log::trace!("Result is {:?}", entries);
|
||||
|
||||
if let Some(first) = entries.first() {
|
||||
if any::TypeId::of::<T>() == any::TypeId::of::<bool>() {
|
||||
Ok(first
|
||||
.value
|
||||
.clone()
|
||||
.unwrap_or("false".to_string())
|
||||
.parse::<T>()
|
||||
.ok())
|
||||
} else {
|
||||
Ok(first.value.clone().and_then(|v| v.parse::<T>().ok()))
|
||||
}
|
||||
} else {
|
||||
return Ok(None);
|
||||
}
|
||||
}
|
||||
|
||||
/// Upserting a guild setting
|
||||
pub async fn set_guild_setting<T>(
|
||||
&self,
|
||||
guild_id: u64,
|
||||
key: String,
|
||||
value: T,
|
||||
) -> DatabaseResult<()>
|
||||
where
|
||||
T: ToString + Debug,
|
||||
{
|
||||
use guild_settings::dsl;
|
||||
log::debug!("Setting '{}' to '{:?}' for guild {}", key, value, guild_id);
|
||||
|
||||
insert_into(dsl::guild_settings)
|
||||
.values(GuildSettingInsert {
|
||||
guild_id: guild_id as i64,
|
||||
key: key.to_string(),
|
||||
value: value.to_string(),
|
||||
})
|
||||
.on_conflict((dsl::guild_id, dsl::key))
|
||||
.do_update()
|
||||
.set(dsl::value.eq(value.to_string()))
|
||||
.execute_async(&self.pool)
|
||||
.await?;
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// Deletes a guild setting
|
||||
pub async fn delete_guild_setting(&self, guild_id: u64, key: String) -> DatabaseResult<()> {
|
||||
use guild_settings::dsl;
|
||||
delete(dsl::guild_settings)
|
||||
.filter(dsl::guild_id.eq(guild_id as i64))
|
||||
.filter(dsl::key.eq(key))
|
||||
.execute_async(&self.pool)
|
||||
.await?;
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// Returns a list of all guild playlists
|
||||
pub async fn get_guild_playlists(&self, guild_id: u64) -> DatabaseResult<Vec<GuildPlaylist>> {
|
||||
use guild_playlists::dsl;
|
||||
log::debug!("Retrieving guild playlists for guild {}", guild_id);
|
||||
|
||||
let playlists: Vec<GuildPlaylist> = dsl::guild_playlists
|
||||
.filter(dsl::guild_id.eq(guild_id as i64))
|
||||
.load_async::<GuildPlaylist>(&self.pool)
|
||||
.await?;
|
||||
|
||||
Ok(playlists)
|
||||
}
|
||||
|
||||
/// Returns a guild playlist by name
|
||||
pub async fn get_guild_playlist(
|
||||
&self,
|
||||
guild_id: u64,
|
||||
name: String,
|
||||
) -> DatabaseResult<Option<GuildPlaylist>> {
|
||||
use guild_playlists::dsl;
|
||||
log::debug!("Retriving guild playlist '{}' for guild {}", name, guild_id);
|
||||
|
||||
let playlists: Vec<GuildPlaylist> = dsl::guild_playlists
|
||||
.filter(dsl::guild_id.eq(guild_id as i64))
|
||||
.filter(dsl::name.eq(name))
|
||||
.load_async::<GuildPlaylist>(&self.pool)
|
||||
.await?;
|
||||
|
||||
Ok(playlists.into_iter().next())
|
||||
}
|
||||
|
||||
/// Adds a new playlist to the database overwriting the old one
|
||||
pub async fn add_guild_playlist(
|
||||
&self,
|
||||
guild_id: u64,
|
||||
name: String,
|
||||
url: String,
|
||||
) -> DatabaseResult<()> {
|
||||
use guild_playlists::dsl;
|
||||
log::debug!("Inserting guild playlist '{}' for guild {}", name, guild_id);
|
||||
|
||||
insert_into(dsl::guild_playlists)
|
||||
.values(GuildPlaylistInsert {
|
||||
guild_id: guild_id as i64,
|
||||
name: name.clone(),
|
||||
url: url.clone(),
|
||||
})
|
||||
.on_conflict((dsl::guild_id, dsl::name))
|
||||
.do_update()
|
||||
.set(dsl::url.eq(url))
|
||||
.execute_async(&self.pool)
|
||||
.await?;
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// Returns a list of all gifs in the database
|
||||
pub async fn get_all_gifs(&self) -> DatabaseResult<Vec<Gif>> {
|
||||
use gifs::dsl;
|
||||
log::debug!("Loading all gifs from the database");
|
||||
|
||||
let gifs: Vec<Gif> = dsl::gifs.load_async::<Gif>(&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;
|
||||
log::debug!("Searching for gifs in category '{}'", category);
|
||||
|
||||
let gifs: Vec<Gif> = dsl::gifs
|
||||
.filter(dsl::category.eq(category))
|
||||
.load_async::<Gif>(&self.pool)
|
||||
.await?;
|
||||
Ok(gifs)
|
||||
}
|
||||
|
||||
/// Adds a gif to the database
|
||||
pub async fn add_gif(
|
||||
&self,
|
||||
url: &str,
|
||||
category: Option<String>,
|
||||
name: Option<String>,
|
||||
) -> DatabaseResult<()> {
|
||||
use gifs::dsl;
|
||||
log::debug!(
|
||||
"Inserting gif with url '{}' and name {:?} and category {:?}",
|
||||
url,
|
||||
name,
|
||||
category
|
||||
);
|
||||
insert_into(dsl::gifs)
|
||||
.values(GifInsert {
|
||||
url: url.to_string(),
|
||||
name,
|
||||
category,
|
||||
})
|
||||
.execute_async(&self.pool)
|
||||
.await?;
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// Adds a command statistic to the database
|
||||
pub async fn add_statistic(
|
||||
&self,
|
||||
version: &str,
|
||||
command: &str,
|
||||
executed_at: SystemTime,
|
||||
success: bool,
|
||||
error_msg: Option<String>,
|
||||
) -> DatabaseResult<()> {
|
||||
use statistics::dsl;
|
||||
log::trace!("Adding statistic to database");
|
||||
insert_into(dsl::statistics)
|
||||
.values(StatisticInsert {
|
||||
version: version.to_string(),
|
||||
command: command.to_string(),
|
||||
executed_at,
|
||||
success,
|
||||
error_msg,
|
||||
})
|
||||
.execute_async(&self.pool)
|
||||
.await?;
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// Returns the total number of commands executed
|
||||
pub async fn get_total_commands_statistic(&self) -> DatabaseResult<u64> {
|
||||
use statistics::dsl;
|
||||
log::trace!("Querying total number of commands");
|
||||
let total_count: i64 = dsl::statistics
|
||||
.select(count(dsl::id))
|
||||
.first_async::<i64>(&self.pool)
|
||||
.await?;
|
||||
|
||||
Ok(total_count as u64)
|
||||
}
|
||||
}
|
@ -0,0 +1,57 @@
|
||||
use diesel::insert_into;
|
||||
use diesel::prelude::*;
|
||||
use tokio_diesel::*;
|
||||
|
||||
use crate::error::DatabaseResult;
|
||||
use crate::models::*;
|
||||
use crate::schema::*;
|
||||
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;
|
||||
log::debug!("Loading all gifs from the database");
|
||||
|
||||
let gifs: Vec<Gif> = dsl::gifs.load_async::<Gif>(&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;
|
||||
log::debug!("Searching for gifs in category '{}'", category);
|
||||
|
||||
let gifs: Vec<Gif> = dsl::gifs
|
||||
.filter(dsl::category.eq(category))
|
||||
.load_async::<Gif>(&self.pool)
|
||||
.await?;
|
||||
Ok(gifs)
|
||||
}
|
||||
|
||||
/// Adds a gif to the database
|
||||
pub async fn add_gif(
|
||||
&self,
|
||||
url: &str,
|
||||
category: Option<String>,
|
||||
name: Option<String>,
|
||||
) -> DatabaseResult<()> {
|
||||
use gifs::dsl;
|
||||
log::debug!(
|
||||
"Inserting gif with url '{}' and name {:?} and category {:?}",
|
||||
url,
|
||||
name,
|
||||
category
|
||||
);
|
||||
insert_into(dsl::gifs)
|
||||
.values(GifInsert {
|
||||
url: url.to_string(),
|
||||
name,
|
||||
category,
|
||||
})
|
||||
.execute_async(&self.pool)
|
||||
.await?;
|
||||
|
||||
Ok(())
|
||||
}
|
||||
}
|
@ -0,0 +1,66 @@
|
||||
use diesel::insert_into;
|
||||
use diesel::prelude::*;
|
||||
use tokio_diesel::*;
|
||||
|
||||
use crate::error::DatabaseResult;
|
||||
use crate::models::*;
|
||||
use crate::schema::*;
|
||||
use crate::Database;
|
||||
|
||||
impl Database {
|
||||
/// Returns a list of all guild playlists
|
||||
pub async fn get_guild_playlists(&self, guild_id: u64) -> DatabaseResult<Vec<GuildPlaylist>> {
|
||||
use guild_playlists::dsl;
|
||||
log::debug!("Retrieving guild playlists for guild {}", guild_id);
|
||||
|
||||
let playlists: Vec<GuildPlaylist> = dsl::guild_playlists
|
||||
.filter(dsl::guild_id.eq(guild_id as i64))
|
||||
.load_async::<GuildPlaylist>(&self.pool)
|
||||
.await?;
|
||||
|
||||
Ok(playlists)
|
||||
}
|
||||
|
||||
/// Returns a guild playlist by name
|
||||
pub async fn get_guild_playlist(
|
||||
&self,
|
||||
guild_id: u64,
|
||||
name: String,
|
||||
) -> DatabaseResult<Option<GuildPlaylist>> {
|
||||
use guild_playlists::dsl;
|
||||
log::debug!("Retriving guild playlist '{}' for guild {}", name, guild_id);
|
||||
|
||||
let playlists: Vec<GuildPlaylist> = dsl::guild_playlists
|
||||
.filter(dsl::guild_id.eq(guild_id as i64))
|
||||
.filter(dsl::name.eq(name))
|
||||
.load_async::<GuildPlaylist>(&self.pool)
|
||||
.await?;
|
||||
|
||||
Ok(playlists.into_iter().next())
|
||||
}
|
||||
|
||||
/// Adds a new playlist to the database overwriting the old one
|
||||
pub async fn add_guild_playlist(
|
||||
&self,
|
||||
guild_id: u64,
|
||||
name: String,
|
||||
url: String,
|
||||
) -> DatabaseResult<()> {
|
||||
use guild_playlists::dsl;
|
||||
log::debug!("Inserting guild playlist '{}' for guild {}", name, guild_id);
|
||||
|
||||
insert_into(dsl::guild_playlists)
|
||||
.values(GuildPlaylistInsert {
|
||||
guild_id: guild_id as i64,
|
||||
name: name.clone(),
|
||||
url: url.clone(),
|
||||
})
|
||||
.on_conflict((dsl::guild_id, dsl::name))
|
||||
.do_update()
|
||||
.set(dsl::url.eq(url))
|
||||
.execute_async(&self.pool)
|
||||
.await?;
|
||||
|
||||
Ok(())
|
||||
}
|
||||
}
|
@ -0,0 +1,89 @@
|
||||
use std::any;
|
||||
use std::fmt::Debug;
|
||||
use std::str::FromStr;
|
||||
|
||||
use diesel::prelude::*;
|
||||
use diesel::{delete, insert_into};
|
||||
use tokio_diesel::*;
|
||||
|
||||
use crate::error::DatabaseResult;
|
||||
use crate::models::*;
|
||||
use crate::schema::*;
|
||||
use crate::Database;
|
||||
|
||||
impl Database {
|
||||
/// Returns a guild setting from the database
|
||||
pub async fn get_guild_setting<T: 'static>(
|
||||
&self,
|
||||
guild_id: u64,
|
||||
key: String,
|
||||
) -> DatabaseResult<Option<T>>
|
||||
where
|
||||
T: FromStr,
|
||||
{
|
||||
use guild_settings::dsl;
|
||||
log::debug!("Retrieving setting '{}' for guild {}", key, guild_id);
|
||||
|
||||
let entries: Vec<GuildSetting> = dsl::guild_settings
|
||||
.filter(dsl::guild_id.eq(guild_id as i64))
|
||||
.filter(dsl::key.eq(key))
|
||||
.load_async::<GuildSetting>(&self.pool)
|
||||
.await?;
|
||||
log::trace!("Result is {:?}", entries);
|
||||
|
||||
if let Some(first) = entries.first() {
|
||||
if any::TypeId::of::<T>() == any::TypeId::of::<bool>() {
|
||||
Ok(first
|
||||
.value
|
||||
.clone()
|
||||
.unwrap_or("false".to_string())
|
||||
.parse::<T>()
|
||||
.ok())
|
||||
} else {
|
||||
Ok(first.value.clone().and_then(|v| v.parse::<T>().ok()))
|
||||
}
|
||||
} else {
|
||||
return Ok(None);
|
||||
}
|
||||
}
|
||||
|
||||
/// Upserting a guild setting
|
||||
pub async fn set_guild_setting<T>(
|
||||
&self,
|
||||
guild_id: u64,
|
||||
key: String,
|
||||
value: T,
|
||||
) -> DatabaseResult<()>
|
||||
where
|
||||
T: ToString + Debug,
|
||||
{
|
||||
use guild_settings::dsl;
|
||||
log::debug!("Setting '{}' to '{:?}' for guild {}", key, value, guild_id);
|
||||
|
||||
insert_into(dsl::guild_settings)
|
||||
.values(GuildSettingInsert {
|
||||
guild_id: guild_id as i64,
|
||||
key: key.to_string(),
|
||||
value: value.to_string(),
|
||||
})
|
||||
.on_conflict((dsl::guild_id, dsl::key))
|
||||
.do_update()
|
||||
.set(dsl::value.eq(value.to_string()))
|
||||
.execute_async(&self.pool)
|
||||
.await?;
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// Deletes a guild setting
|
||||
pub async fn delete_guild_setting(&self, guild_id: u64, key: String) -> DatabaseResult<()> {
|
||||
use guild_settings::dsl;
|
||||
delete(dsl::guild_settings)
|
||||
.filter(dsl::guild_id.eq(guild_id as i64))
|
||||
.filter(dsl::key.eq(key))
|
||||
.execute_async(&self.pool)
|
||||
.await?;
|
||||
|
||||
Ok(())
|
||||
}
|
||||
}
|
@ -0,0 +1,26 @@
|
||||
pub use gifs::*;
|
||||
pub use guild_playlists::*;
|
||||
pub use guild_playlists::*;
|
||||
pub use statistics::*;
|
||||
|
||||
use crate::PoolConnection;
|
||||
|
||||
mod gifs;
|
||||
mod guild_playlists;
|
||||
mod guild_settings;
|
||||
mod statistics;
|
||||
|
||||
#[derive(Clone)]
|
||||
pub struct Database {
|
||||
pool: PoolConnection,
|
||||
}
|
||||
|
||||
unsafe impl Send for Database {}
|
||||
|
||||
unsafe impl Sync for Database {}
|
||||
|
||||
impl Database {
|
||||
pub fn new(pool: PoolConnection) -> Self {
|
||||
Self { pool }
|
||||
}
|
||||
}
|
@ -0,0 +1,50 @@
|
||||
use std::time::SystemTime;
|
||||
|
||||
use diesel::dsl::count;
|
||||
use diesel::insert_into;
|
||||
use diesel::prelude::*;
|
||||
use tokio_diesel::*;
|
||||
|
||||
use crate::error::DatabaseResult;
|
||||
use crate::models::*;
|
||||
use crate::schema::*;
|
||||
use crate::Database;
|
||||
|
||||
impl Database {
|
||||
/// Adds a command statistic to the database
|
||||
pub async fn add_statistic(
|
||||
&self,
|
||||
version: &str,
|
||||
command: &str,
|
||||
executed_at: SystemTime,
|
||||
success: bool,
|
||||
error_msg: Option<String>,
|
||||
) -> DatabaseResult<()> {
|
||||
use statistics::dsl;
|
||||
log::trace!("Adding statistic to database");
|
||||
insert_into(dsl::statistics)
|
||||
.values(StatisticInsert {
|
||||
version: version.to_string(),
|
||||
command: command.to_string(),
|
||||
executed_at,
|
||||
success,
|
||||
error_msg,
|
||||
})
|
||||
.execute_async(&self.pool)
|
||||
.await?;
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// Returns the total number of commands executed
|
||||
pub async fn get_total_commands_statistic(&self) -> DatabaseResult<u64> {
|
||||
use statistics::dsl;
|
||||
log::trace!("Querying total number of commands");
|
||||
let total_count: i64 = dsl::statistics
|
||||
.select(count(dsl::id))
|
||||
.first_async::<i64>(&self.pool)
|
||||
.await?;
|
||||
|
||||
Ok(total_count as u64)
|
||||
}
|
||||
}
|
@ -0,0 +1,42 @@
|
||||
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::macros::command;
|
||||
use serenity::framework::standard::{Args, CommandResult};
|
||||
use serenity::model::channel::Message;
|
||||
|
||||
static CATEGORY_PREFIX: &str = "pain-";
|
||||
static NOT_FOUND_PAIN: &str = "404";
|
||||
|
||||
#[command]
|
||||
#[description("Various types of pain (pain-peko)")]
|
||||
#[usage("<pain-type>")]
|
||||
#[example("peko")]
|
||||
#[min_args(1)]
|
||||
#[max_args(1)]
|
||||
#[bucket("general")]
|
||||
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())
|
||||
.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())
|
||||
.await?;
|
||||
}
|
||||
|
||||
let gif = gifs
|
||||
.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?;
|
||||
|
||||
Ok(())
|
||||
}
|
@ -0,0 +1,40 @@
|
||||
use crate::commands::music::{get_queue_for_guild, is_dj};
|
||||
use serenity::client::Context;
|
||||
use serenity::framework::standard::macros::command;
|
||||
use serenity::framework::standard::{Args, CommandResult};
|
||||
use serenity::model::channel::Message;
|
||||
|
||||
#[command]
|
||||
#[description("Moves a song in the queue from one position to a new one")]
|
||||
#[usage("<old-pos> <new-pos>")]
|
||||
#[example("102 2")]
|
||||
#[min_args(2)]
|
||||
#[max_args(2)]
|
||||
#[bucket("general")]
|
||||
#[only_in(guilds)]
|
||||
#[aliases("mvs", "movesong", "move-song")]
|
||||
async fn move_song(ctx: &Context, msg: &Message, mut args: Args) -> CommandResult {
|
||||
let guild = msg.guild(&ctx.cache).await.unwrap();
|
||||
log::debug!("Moving song for guild {}", guild.id);
|
||||
|
||||
let pos1 = args.single::<usize>()?;
|
||||
let pos2 = args.single::<usize>()?;
|
||||
|
||||
if !is_dj(ctx, guild.id, &msg.author).await? {
|
||||
msg.channel_id.say(ctx, "Requires DJ permissions").await?;
|
||||
return Ok(());
|
||||
}
|
||||
{
|
||||
let queue = get_queue_for_guild(ctx, &guild.id).await?;
|
||||
let mut queue_lock = queue.lock().await;
|
||||
queue_lock.move_position(pos1, pos2);
|
||||
}
|
||||
msg.channel_id
|
||||
.say(
|
||||
ctx,
|
||||
format!("Moved Song `{}` to new position `{}`", pos1, pos2),
|
||||
)
|
||||
.await?;
|
||||
|
||||
Ok(())
|
||||
}
|
@ -0,0 +1,37 @@
|
||||
use crate::commands::music::{get_queue_for_guild, is_dj};
|
||||
use serenity::client::Context;
|
||||
use serenity::framework::standard::macros::command;
|
||||
use serenity::framework::standard::{Args, CommandResult};
|
||||
use serenity::model::channel::Message;
|
||||
|
||||
#[command]
|
||||
#[description("Removes a song from the queue")]
|
||||
#[usage("<pos>")]
|
||||
#[example("102")]
|
||||
#[min_args(1)]
|
||||
#[max_args(1)]
|
||||
#[bucket("general")]
|
||||
#[only_in(guilds)]
|
||||
#[aliases("rms", "removesong", "remove-song")]
|
||||
async fn remove_song(ctx: &Context, msg: &Message, mut args: Args) -> CommandResult {
|
||||
let guild = msg.guild(&ctx.cache).await.unwrap();
|
||||
log::debug!("Moving song for guild {}", guild.id);
|
||||
|
||||
let pos = args.single::<usize>()?;
|
||||
|
||||
if !is_dj(ctx, guild.id, &msg.author).await? {
|
||||
msg.channel_id.say(ctx, "Requires DJ permissions").await?;
|
||||
return Ok(());
|
||||
}
|
||||
{
|
||||
let queue = get_queue_for_guild(ctx, &guild.id).await?;
|
||||
let mut queue_lock = queue.lock().await;
|
||||
queue_lock.remove(pos);
|
||||
}
|
||||
|
||||
msg.channel_id
|
||||
.say(ctx, format!("Removed Song at `{}`", pos))
|
||||
.await?;
|
||||
|
||||
Ok(())
|
||||
}
|
@ -0,0 +1,2 @@
|
||||
pub mod now_playing;
|
||||
pub mod queue;
|
@ -0,0 +1,45 @@
|
||||
use crate::providers::music::queue::Song;
|
||||
use crate::utils::error::BotResult;
|
||||
use bot_serenityutils::menu::{MenuBuilder, Page};
|
||||
use serenity::builder::CreateMessage;
|
||||
use serenity::client::Context;
|
||||
use serenity::model::id::ChannelId;
|
||||
use std::time::Duration;
|
||||
|
||||
/// Creates a new queue menu
|
||||
pub async fn create_queue_menu(
|
||||
ctx: &Context,
|
||||
channel_id: ChannelId,
|
||||
songs: Vec<(usize, Song)>,
|
||||
) -> BotResult<()> {
|
||||
let page_count = (songs.len() as f32 / 10.0).ceil() as usize;
|
||||
let pages: Vec<Page<'static>> = songs
|
||||
.chunks(10)
|
||||
.enumerate()
|
||||
.map(|(i, entries)| create_songs_page(page_count, i + 1, entries.to_vec()))
|
||||
.collect();
|
||||
|
||||
MenuBuilder::new_paginator()
|
||||
.add_pages(pages)
|
||||
.timeout(Duration::from_secs(120))
|
||||
.build(ctx, channel_id)
|
||||
.await?;
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// Creates a new page with songs
|
||||
fn create_songs_page(total_pages: usize, page: usize, songs: Vec<(usize, Song)>) -> Page<'static> {
|
||||
let mut message = CreateMessage::default();
|
||||
let description_entries: Vec<String> = songs
|
||||
.into_iter()
|
||||
.map(|(i, s)| format!("{:0>3}. {} - {}", i, s.author(), s.title()))
|
||||
.collect();
|
||||
message.embed(|e| {
|
||||
e.title("Queue")
|
||||
.description(format!("```md\n{}\n```", description_entries.join("\n")))
|
||||
.footer(|f| f.text(format!("Page {} of {}", page, total_pages)))
|
||||
});
|
||||
|
||||
Page::new_static(message)
|
||||
}
|
Loading…
Reference in New Issue