lots of things and stuff
This commit is contained in:
213
src/cmds/info.rs
213
src/cmds/info.rs
@ -1,201 +1,11 @@
|
||||
use std::{collections::HashMap, sync::Arc};
|
||||
use crate::{Context, Error, THEME_COLOR};
|
||||
|
||||
use regex_command_attr::command;
|
||||
use serenity::{client::Context, framework::standard::CommandResult};
|
||||
/// Get additional information about the bot
|
||||
#[poise::command(slash_command, category = "Information")]
|
||||
pub async fn info(ctx: Context<'_>) -> Result<(), Error> {
|
||||
let current_user = ctx.discord().cache.current_user();
|
||||
|
||||
use crate::{
|
||||
framework::{Args, CommandInvoke, CommandKind, CreateGenericResponse, RegexFramework},
|
||||
THEME_COLOR,
|
||||
};
|
||||
|
||||
#[command]
|
||||
#[group("Information")]
|
||||
#[description("Get information on the commands of the bot")]
|
||||
#[arg(
|
||||
name = "command",
|
||||
description = "Get help for a specific command",
|
||||
kind = "String",
|
||||
required = false
|
||||
)]
|
||||
#[example("`/help` - see all commands")]
|
||||
#[example("`/help play` - get help about the `play` command")]
|
||||
pub async fn help(
|
||||
ctx: &Context,
|
||||
invoke: &(dyn CommandInvoke + Sync + Send),
|
||||
args: Args,
|
||||
) -> CommandResult {
|
||||
fn get_groups(framework: Arc<RegexFramework>) -> HashMap<&'static str, Vec<&'static str>> {
|
||||
let mut groups = HashMap::new();
|
||||
|
||||
for command in &framework.commands_ {
|
||||
let entry = groups.entry(command.group).or_insert(vec![]);
|
||||
|
||||
entry.push(command.names[0]);
|
||||
}
|
||||
|
||||
groups
|
||||
}
|
||||
|
||||
let framework = ctx
|
||||
.data
|
||||
.read()
|
||||
.await
|
||||
.get::<RegexFramework>()
|
||||
.cloned()
|
||||
.unwrap();
|
||||
|
||||
if let Some(command_name) = args.named("command") {
|
||||
if let Some(command) = framework.commands.get(command_name) {
|
||||
let examples = if command.examples.is_empty() {
|
||||
"".to_string()
|
||||
} else {
|
||||
format!(
|
||||
"**Examples**
|
||||
{}",
|
||||
command
|
||||
.examples
|
||||
.iter()
|
||||
.map(|e| format!(" • {}", e))
|
||||
.collect::<Vec<String>>()
|
||||
.join("\n")
|
||||
)
|
||||
};
|
||||
|
||||
let args = if command.args.is_empty() {
|
||||
"**Arguments**
|
||||
• *This command has no arguments*"
|
||||
.to_string()
|
||||
} else {
|
||||
format!(
|
||||
"**Arguments**
|
||||
{}",
|
||||
command
|
||||
.args
|
||||
.iter()
|
||||
.map(|a| format!(
|
||||
" • `{}` {} - {}",
|
||||
a.name,
|
||||
if a.required { "" } else { "[optional]" },
|
||||
a.description
|
||||
))
|
||||
.collect::<Vec<String>>()
|
||||
.join("\n")
|
||||
)
|
||||
};
|
||||
|
||||
invoke
|
||||
.respond(
|
||||
ctx.http.clone(),
|
||||
CreateGenericResponse::new().embed(|e| {
|
||||
e.title(format!("{} Help", command_name))
|
||||
.color(THEME_COLOR)
|
||||
.description(format!(
|
||||
"**Available In**
|
||||
`Slash Commands` {}
|
||||
` Text Commands` {}
|
||||
|
||||
**Aliases**
|
||||
{}
|
||||
|
||||
**Overview**
|
||||
• {}
|
||||
{}
|
||||
|
||||
{}",
|
||||
if command.kind != CommandKind::Text {
|
||||
"✅"
|
||||
} else {
|
||||
"❎"
|
||||
},
|
||||
if command.kind != CommandKind::Slash {
|
||||
"✅"
|
||||
} else {
|
||||
"❎"
|
||||
},
|
||||
command
|
||||
.names
|
||||
.iter()
|
||||
.map(|n| format!("`{}`", n))
|
||||
.collect::<Vec<String>>()
|
||||
.join(" "),
|
||||
command.desc,
|
||||
args,
|
||||
examples
|
||||
))
|
||||
}),
|
||||
)
|
||||
.await?;
|
||||
} else {
|
||||
let groups = get_groups(framework);
|
||||
let groups_iter = groups.iter().map(|(name, commands)| {
|
||||
(
|
||||
name,
|
||||
commands
|
||||
.iter()
|
||||
.map(|c| format!("`{}`", c))
|
||||
.collect::<Vec<String>>()
|
||||
.join(" "),
|
||||
true,
|
||||
)
|
||||
});
|
||||
|
||||
invoke
|
||||
.respond(
|
||||
ctx.http.clone(),
|
||||
CreateGenericResponse::new().embed(|e| {
|
||||
e.title("Invalid Command")
|
||||
.color(THEME_COLOR)
|
||||
.description("Type `/help command` to view more about a command below:")
|
||||
.fields(groups_iter)
|
||||
}),
|
||||
)
|
||||
.await?;
|
||||
}
|
||||
} else {
|
||||
let groups = get_groups(framework);
|
||||
let groups_iter = groups.iter().map(|(name, commands)| {
|
||||
(
|
||||
name,
|
||||
commands
|
||||
.iter()
|
||||
.map(|c| format!("`{}`", c))
|
||||
.collect::<Vec<String>>()
|
||||
.join(" "),
|
||||
true,
|
||||
)
|
||||
});
|
||||
|
||||
invoke
|
||||
.respond(
|
||||
ctx.http.clone(),
|
||||
CreateGenericResponse::new().embed(|e| {
|
||||
e.title("Help")
|
||||
.color(THEME_COLOR)
|
||||
.description("**Welcome to SoundFX!**
|
||||
To get started, upload a sound with `/upload`, or use `/search` and `/play` to look at some of the public sounds
|
||||
|
||||
Type `/help command` to view help about a command below:")
|
||||
.fields(groups_iter)
|
||||
}),
|
||||
)
|
||||
.await?;
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[command]
|
||||
#[group("Information")]
|
||||
#[aliases("invite")]
|
||||
#[description("Get additional information on the bot")]
|
||||
async fn info(
|
||||
ctx: &Context,
|
||||
invoke: &(dyn CommandInvoke + Sync + Send),
|
||||
_args: Args,
|
||||
) -> CommandResult {
|
||||
let current_user = ctx.cache.current_user();
|
||||
|
||||
invoke.respond(ctx.http.clone(), CreateGenericResponse::new()
|
||||
ctx.send(|m| m
|
||||
.embed(|e| e
|
||||
.title("Info")
|
||||
.color(THEME_COLOR)
|
||||
@ -211,15 +21,10 @@ Invite me: https://discord.com/api/oauth2/authorize?client_id={1}&permissions=31
|
||||
Developer: <@203532103185465344>
|
||||
Find me on https://discord.jellywx.com/ and on https://github.com/JellyWX :)
|
||||
|
||||
**Sound Credits**
|
||||
\"The rain falls against the parasol\" https://freesound.org/people/straget/
|
||||
\"Heavy Rain\" https://freesound.org/people/lebaston100/
|
||||
\"Rain on Windows, Interior, A\" https://freesound.org/people/InspectorJ/
|
||||
\"Seaside Waves, Close, A\" https://freesound.org/people/InspectorJ/
|
||||
\"Small River 1 - Fast - Close\" https://freesound.org/people/Pfannkuchn/
|
||||
|
||||
**An online dashboard is available!** Visit https://soundfx.jellywx.com/dashboard
|
||||
There is a maximum sound limit per user. This can be removed by subscribing at **https://patreon.com/jellywx**", current_user.name, current_user.id.as_u64())))).await?;
|
||||
There is a maximum sound limit per user. This can be removed by subscribing at **https://patreon.com/jellywx**",
|
||||
current_user.name,
|
||||
current_user.id.as_u64())))).await?;
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
@ -1,32 +1,15 @@
|
||||
use std::time::Duration;
|
||||
|
||||
use regex_command_attr::command;
|
||||
use serenity::{
|
||||
client::Context,
|
||||
framework::standard::CommandResult,
|
||||
model::id::{GuildId, RoleId},
|
||||
};
|
||||
use poise::serenity::model::id::{GuildId, RoleId};
|
||||
|
||||
use crate::{
|
||||
framework::{Args, CommandInvoke, CreateGenericResponse},
|
||||
sound::Sound,
|
||||
MySQL, MAX_SOUNDS, PATREON_GUILD, PATREON_ROLE,
|
||||
};
|
||||
use crate::{sound::Sound, Context, Error, MAX_SOUNDS, PATREON_GUILD, PATREON_ROLE};
|
||||
|
||||
#[command("upload")]
|
||||
#[group("Manage")]
|
||||
#[description("Upload a new sound to the bot")]
|
||||
#[arg(
|
||||
name = "name",
|
||||
description = "Name to upload sound to",
|
||||
kind = "String",
|
||||
required = true
|
||||
)]
|
||||
/// Upload a new sound to the bot
|
||||
#[poise::command(slash_command, rename = "upload", category = "Manage")]
|
||||
pub async fn upload_new_sound(
|
||||
ctx: &Context,
|
||||
invoke: &(dyn CommandInvoke + Sync + Send),
|
||||
args: Args,
|
||||
) -> CommandResult {
|
||||
ctx: Context<'_>,
|
||||
#[description = "Name to upload sound to"] name: String,
|
||||
) -> Result<(), Error> {
|
||||
fn is_numeric(s: &String) -> bool {
|
||||
for char in s.chars() {
|
||||
if char.is_digit(10) {
|
||||
@ -38,36 +21,27 @@ pub async fn upload_new_sound(
|
||||
true
|
||||
}
|
||||
|
||||
let new_name = args
|
||||
.named("name")
|
||||
.map(|n| n.to_string())
|
||||
.unwrap_or(String::new());
|
||||
|
||||
if !new_name.is_empty() && new_name.len() <= 20 {
|
||||
if !is_numeric(&new_name) {
|
||||
let pool = ctx
|
||||
.data
|
||||
.read()
|
||||
.await
|
||||
.get::<MySQL>()
|
||||
.cloned()
|
||||
.expect("Could not get SQLPool from data");
|
||||
if !name.is_empty() && name.len() <= 20 {
|
||||
if !is_numeric(&name) {
|
||||
let pool = ctx.data().database.clone();
|
||||
|
||||
// need to check the name is not currently in use by the user
|
||||
let count_name =
|
||||
Sound::count_named_user_sounds(invoke.author_id().0, &new_name, pool.clone())
|
||||
.await?;
|
||||
Sound::count_named_user_sounds(ctx.author().id, &name, pool.clone()).await?;
|
||||
if count_name > 0 {
|
||||
invoke.respond(ctx.http.clone(), CreateGenericResponse::new().content("You are already using that name. Please choose a unique name for your upload.")).await?;
|
||||
ctx.say(
|
||||
"You are already using that name. Please choose a unique name for your upload.",
|
||||
)
|
||||
.await?;
|
||||
} else {
|
||||
// need to check how many sounds user currently has
|
||||
let count = Sound::count_user_sounds(invoke.author_id().0, pool.clone()).await?;
|
||||
let count = Sound::count_user_sounds(ctx.author().id, pool.clone()).await?;
|
||||
let mut permit_upload = true;
|
||||
|
||||
// need to check if user is patreon or nah
|
||||
if count >= *MAX_SOUNDS {
|
||||
let patreon_guild_member = GuildId(*PATREON_GUILD)
|
||||
.member(ctx, invoke.author_id())
|
||||
.member(ctx.discord(), ctx.author().id)
|
||||
.await;
|
||||
|
||||
if let Ok(member) = patreon_guild_member {
|
||||
@ -78,19 +52,13 @@ pub async fn upload_new_sound(
|
||||
}
|
||||
|
||||
if permit_upload {
|
||||
let attachment = if let Some(attachment) = invoke
|
||||
.msg()
|
||||
.map(|m| m.attachments.get(0).map(|a| a.url.clone()))
|
||||
.flatten()
|
||||
{
|
||||
Some(attachment)
|
||||
} else {
|
||||
invoke.respond(ctx.http.clone(), CreateGenericResponse::new().content("Please now upload an audio file under 1MB in size (larger files will be automatically trimmed):")).await?;
|
||||
let attachment = {
|
||||
ctx.say("Please now upload an audio file under 1MB in size (larger files will be automatically trimmed):").await?;
|
||||
|
||||
let reply = invoke
|
||||
let reply = ctx
|
||||
.channel_id()
|
||||
.await_reply(&ctx)
|
||||
.author_id(invoke.author_id())
|
||||
.await_reply(&ctx.discord())
|
||||
.author_id(ctx.author().id)
|
||||
.timeout(Duration::from_secs(120))
|
||||
.await;
|
||||
|
||||
@ -99,20 +67,14 @@ pub async fn upload_new_sound(
|
||||
if let Some(attachment) = reply_msg.attachments.get(0) {
|
||||
Some(attachment.url.clone())
|
||||
} else {
|
||||
invoke.followup(ctx.http.clone(), CreateGenericResponse::new().content("Please upload 1 attachment following your upload command. Aborted")).await?;
|
||||
ctx.say("Please upload 1 attachment following your upload command. Aborted").await?;
|
||||
|
||||
None
|
||||
}
|
||||
}
|
||||
|
||||
None => {
|
||||
invoke
|
||||
.followup(
|
||||
ctx.http.clone(),
|
||||
CreateGenericResponse::new()
|
||||
.content("Upload timed out. Please redo the command"),
|
||||
)
|
||||
.await?;
|
||||
ctx.say("Upload timed out. Please redo the command").await?;
|
||||
|
||||
None
|
||||
}
|
||||
@ -121,91 +83,52 @@ pub async fn upload_new_sound(
|
||||
|
||||
if let Some(url) = attachment {
|
||||
match Sound::create_anon(
|
||||
&new_name,
|
||||
&name,
|
||||
url.as_str(),
|
||||
invoke.guild_id().unwrap().0,
|
||||
invoke.author_id().0,
|
||||
ctx.guild_id().unwrap(),
|
||||
ctx.author().id,
|
||||
pool,
|
||||
)
|
||||
.await
|
||||
{
|
||||
Ok(_) => {
|
||||
invoke
|
||||
.followup(
|
||||
ctx.http.clone(),
|
||||
CreateGenericResponse::new()
|
||||
.content("Sound has been uploaded"),
|
||||
)
|
||||
.await?;
|
||||
ctx.say("Sound has been uploaded").await?;
|
||||
}
|
||||
|
||||
Err(e) => {
|
||||
println!("Error occurred during upload: {:?}", e);
|
||||
invoke
|
||||
.followup(
|
||||
ctx.http.clone(),
|
||||
CreateGenericResponse::new()
|
||||
.content("Sound failed to upload."),
|
||||
)
|
||||
.await?;
|
||||
ctx.say("Sound failed to upload.").await?;
|
||||
}
|
||||
}
|
||||
}
|
||||
} else {
|
||||
invoke.respond(
|
||||
ctx.http.clone(),
|
||||
CreateGenericResponse::new().content(format!(
|
||||
ctx.say(format!(
|
||||
"You have reached the maximum number of sounds ({}). Either delete some with `/delete` or join our Patreon for unlimited uploads at **https://patreon.com/jellywx**",
|
||||
*MAX_SOUNDS,
|
||||
))).await?;
|
||||
)).await?;
|
||||
}
|
||||
}
|
||||
} else {
|
||||
invoke
|
||||
.respond(
|
||||
ctx.http.clone(),
|
||||
CreateGenericResponse::new()
|
||||
.content("Please ensure the sound name contains a non-numerical character"),
|
||||
)
|
||||
ctx.say("Please ensure the sound name contains a non-numerical character")
|
||||
.await?;
|
||||
}
|
||||
} else {
|
||||
invoke.respond(ctx.http.clone(), CreateGenericResponse::new().content("Usage: `/upload <name>`. Please ensure the name provided is less than 20 characters in length")).await?;
|
||||
ctx.say("Usage: `/upload <name>`. Please ensure the name provided is less than 20 characters in length").await?;
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[command("delete")]
|
||||
#[group("Manage")]
|
||||
#[description("Delete a sound you have uploaded")]
|
||||
#[arg(
|
||||
name = "query",
|
||||
description = "Delete sound with the specified name or ID",
|
||||
kind = "String",
|
||||
required = true
|
||||
)]
|
||||
#[example("`/delete beep` - delete the sound with the name \"beep\"")]
|
||||
/// Delete a sound you have uploaded
|
||||
#[poise::command(slash_command, rename = "delete", category = "Manage")]
|
||||
pub async fn delete_sound(
|
||||
ctx: &Context,
|
||||
invoke: &(dyn CommandInvoke + Sync + Send),
|
||||
args: Args,
|
||||
) -> CommandResult {
|
||||
let pool = ctx
|
||||
.data
|
||||
.read()
|
||||
.await
|
||||
.get::<MySQL>()
|
||||
.cloned()
|
||||
.expect("Could not get SQLPool from data");
|
||||
ctx: Context<'_>,
|
||||
#[description = "Name or ID of sound to delete"] name: String,
|
||||
) -> Result<(), Error> {
|
||||
let pool = ctx.data().database.clone();
|
||||
|
||||
let uid = invoke.author_id().0;
|
||||
let gid = invoke.guild_id().unwrap().0;
|
||||
|
||||
let name = args
|
||||
.named("query")
|
||||
.map(|s| s.to_owned())
|
||||
.unwrap_or(String::new());
|
||||
let uid = ctx.author().id.0;
|
||||
let gid = ctx.guild_id().unwrap().0;
|
||||
|
||||
let sound_vec = Sound::search_for_sound(&name, gid, uid, pool.clone(), true).await?;
|
||||
let sound_result = sound_vec.first();
|
||||
@ -213,18 +136,12 @@ pub async fn delete_sound(
|
||||
match sound_result {
|
||||
Some(sound) => {
|
||||
if sound.uploader_id != Some(uid) && sound.server_id != gid {
|
||||
invoke
|
||||
.respond(
|
||||
ctx.http.clone(),
|
||||
CreateGenericResponse::new().content(
|
||||
"You can only delete sounds from this guild or that you have uploaded.",
|
||||
),
|
||||
)
|
||||
ctx.say("You can only delete sounds from this guild or that you have uploaded.")
|
||||
.await?;
|
||||
} else {
|
||||
let has_perms = {
|
||||
if let Ok(member) = invoke.member(&ctx).await {
|
||||
if let Ok(perms) = member.permissions(&ctx) {
|
||||
if let Ok(member) = ctx.guild_id().unwrap().member(&ctx.discord(), uid).await {
|
||||
if let Ok(perms) = member.permissions(&ctx.discord()) {
|
||||
perms.manage_guild()
|
||||
} else {
|
||||
false
|
||||
@ -237,94 +154,49 @@ pub async fn delete_sound(
|
||||
if sound.uploader_id == Some(uid) || has_perms {
|
||||
sound.delete(pool).await?;
|
||||
|
||||
invoke
|
||||
.respond(
|
||||
ctx.http.clone(),
|
||||
CreateGenericResponse::new().content("Sound has been deleted"),
|
||||
)
|
||||
.await?;
|
||||
ctx.say("Sound has been deleted").await?;
|
||||
} else {
|
||||
invoke
|
||||
.respond(
|
||||
ctx.http.clone(),
|
||||
CreateGenericResponse::new().content(
|
||||
"Only server admins can delete sounds uploaded by other users.",
|
||||
),
|
||||
)
|
||||
ctx.say("Only server admins can delete sounds uploaded by other users.")
|
||||
.await?;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
None => {
|
||||
invoke
|
||||
.respond(
|
||||
ctx.http.clone(),
|
||||
CreateGenericResponse::new().content("Sound could not be found by that name."),
|
||||
)
|
||||
.await?;
|
||||
ctx.say("Sound could not be found by that name.").await?;
|
||||
}
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[command("public")]
|
||||
#[group("Manage")]
|
||||
#[description("Change a sound between public and private")]
|
||||
#[arg(
|
||||
name = "query",
|
||||
kind = "String",
|
||||
description = "Sound name or ID to change the privacy setting of",
|
||||
required = true
|
||||
)]
|
||||
#[example("`/public 12` - change sound with ID 12 to private")]
|
||||
#[example("`/public 12` - change sound with ID 12 back to public")]
|
||||
/// Change a sound between public and private
|
||||
#[poise::command(slash_command, rename = "public", category = "Manage")]
|
||||
pub async fn change_public(
|
||||
ctx: &Context,
|
||||
invoke: &(dyn CommandInvoke + Sync + Send),
|
||||
args: Args,
|
||||
) -> CommandResult {
|
||||
let pool = ctx
|
||||
.data
|
||||
.read()
|
||||
.await
|
||||
.get::<MySQL>()
|
||||
.cloned()
|
||||
.expect("Could not get SQLPool from data");
|
||||
ctx: Context<'_>,
|
||||
#[description = "Name or ID of sound to change privacy setting of"] name: String,
|
||||
) -> Result<(), Error> {
|
||||
let pool = ctx.data().database.clone();
|
||||
|
||||
let uid = invoke.author_id().as_u64().to_owned();
|
||||
let uid = ctx.author().id.0;
|
||||
let gid = ctx.guild_id().unwrap().0;
|
||||
|
||||
let name = args.named("query").unwrap();
|
||||
let gid = *invoke.guild_id().unwrap().as_u64();
|
||||
|
||||
let mut sound_vec = Sound::search_for_sound(name, gid, uid, pool.clone(), true).await?;
|
||||
let mut sound_vec = Sound::search_for_sound(&name, gid, uid, pool.clone(), true).await?;
|
||||
let sound_result = sound_vec.first_mut();
|
||||
|
||||
match sound_result {
|
||||
Some(sound) => {
|
||||
if sound.uploader_id != Some(uid) {
|
||||
invoke.respond(ctx.http.clone(), CreateGenericResponse::new().content("You can only change the visibility of sounds you have uploaded. Use `?list me` to view your sounds")).await?;
|
||||
ctx.say("You can only change the visibility of sounds you have uploaded. Use `/list` to view your sounds").await?;
|
||||
} else {
|
||||
if sound.public {
|
||||
sound.public = false;
|
||||
|
||||
invoke
|
||||
.respond(
|
||||
ctx.http.clone(),
|
||||
CreateGenericResponse::new()
|
||||
.content("Sound has been set to private 🔒"),
|
||||
)
|
||||
.await?;
|
||||
ctx.say("Sound has been set to private 🔒").await?;
|
||||
} else {
|
||||
sound.public = true;
|
||||
|
||||
invoke
|
||||
.respond(
|
||||
ctx.http.clone(),
|
||||
CreateGenericResponse::new().content("Sound has been set to public 🔓"),
|
||||
)
|
||||
.await?;
|
||||
ctx.say("Sound has been set to public 🔓").await?;
|
||||
}
|
||||
|
||||
sound.commit(pool).await?
|
||||
@ -332,12 +204,7 @@ pub async fn change_public(
|
||||
}
|
||||
|
||||
None => {
|
||||
invoke
|
||||
.respond(
|
||||
ctx.http.clone(),
|
||||
CreateGenericResponse::new().content("Sound could not be found by that name."),
|
||||
)
|
||||
.await?;
|
||||
ctx.say("Sound could not be found by that name.").await?;
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -1,6 +1,6 @@
|
||||
pub mod info;
|
||||
pub mod manage;
|
||||
pub mod play;
|
||||
pub mod search;
|
||||
pub mod settings;
|
||||
pub mod stop;
|
||||
// pub mod search;
|
||||
// pub mod settings;
|
||||
// pub mod stop;
|
||||
|
481
src/cmds/play.rs
481
src/cmds/play.rs
@ -1,389 +1,106 @@
|
||||
use std::{convert::TryFrom, time::Duration};
|
||||
|
||||
use regex_command_attr::command;
|
||||
use serenity::{
|
||||
builder::CreateActionRow,
|
||||
client::Context,
|
||||
framework::standard::CommandResult,
|
||||
model::interactions::{message_component::ButtonStyle, InteractionResponseType},
|
||||
};
|
||||
use songbird::{
|
||||
create_player, ffmpeg,
|
||||
input::{cached::Memory, Input},
|
||||
Event,
|
||||
use poise::serenity::{
|
||||
builder::CreateActionRow, model::interactions::message_component::ButtonStyle,
|
||||
};
|
||||
|
||||
use crate::{
|
||||
event_handlers::RestartTrack,
|
||||
framework::{Args, CommandInvoke, CreateGenericResponse},
|
||||
guild_data::CtxGuildData,
|
||||
join_channel, play_from_query,
|
||||
sound::Sound,
|
||||
AudioIndex, MySQL,
|
||||
};
|
||||
use crate::{play_from_query, sound::Sound, Context, Error};
|
||||
|
||||
#[command]
|
||||
#[aliases("p")]
|
||||
#[required_permissions(Managed)]
|
||||
#[group("Play")]
|
||||
#[description("Play a sound in your current voice channel")]
|
||||
#[arg(
|
||||
name = "query",
|
||||
description = "Play sound with the specified name or ID",
|
||||
kind = "String",
|
||||
required = true
|
||||
)]
|
||||
#[example("`/play ubercharge` - play sound with name \"ubercharge\" ")]
|
||||
#[example("`/play 13002` - play sound with ID 13002")]
|
||||
/// Play a sound in your current voice channel
|
||||
#[poise::command(slash_command)]
|
||||
pub async fn play(
|
||||
ctx: &Context,
|
||||
invoke: &(dyn CommandInvoke + Sync + Send),
|
||||
args: Args,
|
||||
) -> CommandResult {
|
||||
let guild = invoke.guild(ctx.cache.clone()).unwrap();
|
||||
ctx: Context<'_>,
|
||||
#[description = "Name or ID of sound to play"] name: String,
|
||||
) -> Result<(), Error> {
|
||||
let guild = ctx.guild().unwrap();
|
||||
|
||||
invoke
|
||||
.respond(
|
||||
ctx.http.clone(),
|
||||
CreateGenericResponse::new()
|
||||
.content(play_from_query(ctx, guild, invoke.author_id(), args, false).await),
|
||||
)
|
||||
ctx.say(play_from_query(&ctx, guild, ctx.author().id, &name, false).await)
|
||||
.await?;
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[command("loop")]
|
||||
#[required_permissions(Managed)]
|
||||
#[group("Play")]
|
||||
#[description("Play a sound on loop in your current voice channel")]
|
||||
#[arg(
|
||||
name = "query",
|
||||
description = "Play sound with the specified name or ID",
|
||||
kind = "String",
|
||||
required = true
|
||||
)]
|
||||
#[example("`/loop rain` - loop sound with name \"rain\" ")]
|
||||
#[example("`/loop 13002` - play sound with ID 13002")]
|
||||
/// Loop a sound in your current voice channel
|
||||
#[poise::command(slash_command)]
|
||||
pub async fn loop_play(
|
||||
ctx: &Context,
|
||||
invoke: &(dyn CommandInvoke + Sync + Send),
|
||||
args: Args,
|
||||
) -> CommandResult {
|
||||
let guild = invoke.guild(ctx.cache.clone()).unwrap();
|
||||
ctx: Context<'_>,
|
||||
#[description = "Name or ID of sound to loop"] name: String,
|
||||
) -> Result<(), Error> {
|
||||
let guild = ctx.guild().unwrap();
|
||||
|
||||
invoke
|
||||
.respond(
|
||||
ctx.http.clone(),
|
||||
CreateGenericResponse::new()
|
||||
.content(play_from_query(ctx, guild, invoke.author_id(), args, true).await),
|
||||
)
|
||||
ctx.say(play_from_query(&ctx, guild, ctx.author().id, &name, true).await)
|
||||
.await?;
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[command("ambience")]
|
||||
#[required_permissions(Managed)]
|
||||
#[group("Play")]
|
||||
#[description("Play ambient sound in your current voice channel")]
|
||||
#[arg(
|
||||
name = "name",
|
||||
description = "Play sound with the specified name",
|
||||
kind = "String",
|
||||
required = false
|
||||
)]
|
||||
#[example("`/ambience rain on tent` - play the ambient sound \"rain on tent\" ")]
|
||||
pub async fn play_ambience(
|
||||
ctx: &Context,
|
||||
invoke: &(dyn CommandInvoke + Sync + Send),
|
||||
args: Args,
|
||||
) -> CommandResult {
|
||||
let guild = invoke.guild(ctx.cache.clone()).unwrap();
|
||||
|
||||
let channel_to_join = guild
|
||||
.voice_states
|
||||
.get(&invoke.author_id())
|
||||
.and_then(|voice_state| voice_state.channel_id);
|
||||
|
||||
match channel_to_join {
|
||||
Some(user_channel) => {
|
||||
let audio_index = ctx.data.read().await.get::<AudioIndex>().cloned().unwrap();
|
||||
|
||||
if let Some(search_name) = args.named("name") {
|
||||
if let Some(filename) = audio_index.get(search_name) {
|
||||
let (track, track_handler) = create_player(
|
||||
Input::try_from(
|
||||
Memory::new(ffmpeg(format!("audio/{}", filename)).await.unwrap())
|
||||
.unwrap(),
|
||||
)
|
||||
.unwrap(),
|
||||
);
|
||||
|
||||
let (call_handler, _) = join_channel(ctx, guild.clone(), user_channel).await;
|
||||
let guild_data = ctx.guild_data(guild).await.unwrap();
|
||||
|
||||
{
|
||||
let mut lock = call_handler.lock().await;
|
||||
|
||||
lock.play(track);
|
||||
}
|
||||
|
||||
let _ = track_handler.set_volume(guild_data.read().await.volume as f32 / 100.0);
|
||||
let _ = track_handler.add_event(
|
||||
Event::Periodic(
|
||||
track_handler.metadata().duration.unwrap() - Duration::from_millis(200),
|
||||
None,
|
||||
),
|
||||
RestartTrack {},
|
||||
);
|
||||
|
||||
invoke
|
||||
.respond(
|
||||
ctx.http.clone(),
|
||||
CreateGenericResponse::new()
|
||||
.content(format!("Playing ambience **{}**", search_name)),
|
||||
)
|
||||
.await?;
|
||||
} else {
|
||||
invoke
|
||||
.respond(
|
||||
ctx.http.clone(),
|
||||
CreateGenericResponse::new().embed(|e| {
|
||||
e.title("Not Found").description(format!(
|
||||
"Could not find ambience sound by name **{}**
|
||||
|
||||
__Available ambience sounds:__
|
||||
{}",
|
||||
search_name,
|
||||
audio_index
|
||||
.keys()
|
||||
.into_iter()
|
||||
.map(|i| i.as_str())
|
||||
.collect::<Vec<&str>>()
|
||||
.join("\n")
|
||||
))
|
||||
}),
|
||||
)
|
||||
.await?;
|
||||
}
|
||||
} else {
|
||||
invoke
|
||||
.respond(
|
||||
ctx.http.clone(),
|
||||
CreateGenericResponse::new().embed(|e| {
|
||||
e.title("Available Sounds").description(
|
||||
audio_index
|
||||
.keys()
|
||||
.into_iter()
|
||||
.map(|i| i.as_str())
|
||||
.collect::<Vec<&str>>()
|
||||
.join("\n"),
|
||||
)
|
||||
}),
|
||||
)
|
||||
.await?;
|
||||
}
|
||||
}
|
||||
|
||||
None => {
|
||||
invoke
|
||||
.respond(
|
||||
ctx.http.clone(),
|
||||
CreateGenericResponse::new().content("You are not in a voice chat!"),
|
||||
)
|
||||
.await?;
|
||||
}
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[command("soundboard")]
|
||||
#[required_permissions(Managed)]
|
||||
#[group("Play")]
|
||||
#[kind(Slash)]
|
||||
#[description("Get a menu of sounds with buttons to play them")]
|
||||
#[arg(
|
||||
name = "1",
|
||||
description = "Query for sound button 1",
|
||||
kind = "String",
|
||||
required = true
|
||||
)]
|
||||
#[arg(
|
||||
name = "2",
|
||||
description = "Query for sound button 2",
|
||||
kind = "String",
|
||||
required = false
|
||||
)]
|
||||
#[arg(
|
||||
name = "3",
|
||||
description = "Query for sound button 3",
|
||||
kind = "String",
|
||||
required = false
|
||||
)]
|
||||
#[arg(
|
||||
name = "4",
|
||||
description = "Query for sound button 4",
|
||||
kind = "String",
|
||||
required = false
|
||||
)]
|
||||
#[arg(
|
||||
name = "5",
|
||||
description = "Query for sound button 5",
|
||||
kind = "String",
|
||||
required = false
|
||||
)]
|
||||
#[arg(
|
||||
name = "6",
|
||||
description = "Query for sound button 6",
|
||||
kind = "String",
|
||||
required = false
|
||||
)]
|
||||
#[arg(
|
||||
name = "7",
|
||||
description = "Query for sound button 7",
|
||||
kind = "String",
|
||||
required = false
|
||||
)]
|
||||
#[arg(
|
||||
name = "8",
|
||||
description = "Query for sound button 8",
|
||||
kind = "String",
|
||||
required = false
|
||||
)]
|
||||
#[arg(
|
||||
name = "9",
|
||||
description = "Query for sound button 9",
|
||||
kind = "String",
|
||||
required = false
|
||||
)]
|
||||
#[arg(
|
||||
name = "10",
|
||||
description = "Query for sound button 10",
|
||||
kind = "String",
|
||||
required = false
|
||||
)]
|
||||
#[arg(
|
||||
name = "11",
|
||||
description = "Query for sound button 11",
|
||||
kind = "String",
|
||||
required = false
|
||||
)]
|
||||
#[arg(
|
||||
name = "12",
|
||||
description = "Query for sound button 12",
|
||||
kind = "String",
|
||||
required = false
|
||||
)]
|
||||
#[arg(
|
||||
name = "13",
|
||||
description = "Query for sound button 13",
|
||||
kind = "String",
|
||||
required = false
|
||||
)]
|
||||
#[arg(
|
||||
name = "14",
|
||||
description = "Query for sound button 14",
|
||||
kind = "String",
|
||||
required = false
|
||||
)]
|
||||
#[arg(
|
||||
name = "15",
|
||||
description = "Query for sound button 15",
|
||||
kind = "String",
|
||||
required = false
|
||||
)]
|
||||
#[arg(
|
||||
name = "16",
|
||||
description = "Query for sound button 16",
|
||||
kind = "String",
|
||||
required = false
|
||||
)]
|
||||
#[arg(
|
||||
name = "17",
|
||||
description = "Query for sound button 17",
|
||||
kind = "String",
|
||||
required = false
|
||||
)]
|
||||
#[arg(
|
||||
name = "18",
|
||||
description = "Query for sound button 18",
|
||||
kind = "String",
|
||||
required = false
|
||||
)]
|
||||
#[arg(
|
||||
name = "19",
|
||||
description = "Query for sound button 19",
|
||||
kind = "String",
|
||||
required = false
|
||||
)]
|
||||
#[arg(
|
||||
name = "20",
|
||||
description = "Query for sound button 20",
|
||||
kind = "String",
|
||||
required = false
|
||||
)]
|
||||
#[arg(
|
||||
name = "21",
|
||||
description = "Query for sound button 21",
|
||||
kind = "String",
|
||||
required = false
|
||||
)]
|
||||
#[arg(
|
||||
name = "22",
|
||||
description = "Query for sound button 22",
|
||||
kind = "String",
|
||||
required = false
|
||||
)]
|
||||
#[arg(
|
||||
name = "23",
|
||||
description = "Query for sound button 23",
|
||||
kind = "String",
|
||||
required = false
|
||||
)]
|
||||
#[arg(
|
||||
name = "24",
|
||||
description = "Query for sound button 24",
|
||||
kind = "String",
|
||||
required = false
|
||||
)]
|
||||
#[arg(
|
||||
name = "25",
|
||||
description = "Query for sound button 25",
|
||||
kind = "String",
|
||||
required = false
|
||||
)]
|
||||
#[example("`/soundboard ubercharge` - create a soundboard with a button for the \"ubercharge\" sound effect")]
|
||||
#[example("`/soundboard 57000 24119 2 1002 13202` - create a soundboard with 5 buttons, for sounds with the IDs presented")]
|
||||
/// Get a menu of sounds with buttons to play them
|
||||
#[poise::command(slash_command, rename = "soundboard", category = "Play")]
|
||||
pub async fn soundboard(
|
||||
ctx: &Context,
|
||||
invoke: &(dyn CommandInvoke + Sync + Send),
|
||||
args: Args,
|
||||
) -> CommandResult {
|
||||
if let Some(interaction) = invoke.interaction() {
|
||||
let _ = interaction
|
||||
.create_interaction_response(&ctx, |r| {
|
||||
r.kind(InteractionResponseType::DeferredChannelMessageWithSource)
|
||||
})
|
||||
.await;
|
||||
}
|
||||
ctx: Context<'_>,
|
||||
#[description = "Name or ID of sound for button 1"] sound_1: String,
|
||||
#[description = "Name or ID of sound for button 2"] sound_2: Option<String>,
|
||||
#[description = "Name or ID of sound for button 3"] sound_3: Option<String>,
|
||||
#[description = "Name or ID of sound for button 4"] sound_4: Option<String>,
|
||||
#[description = "Name or ID of sound for button 5"] sound_5: Option<String>,
|
||||
#[description = "Name or ID of sound for button 6"] sound_6: Option<String>,
|
||||
#[description = "Name or ID of sound for button 7"] sound_7: Option<String>,
|
||||
#[description = "Name or ID of sound for button 8"] sound_8: Option<String>,
|
||||
#[description = "Name or ID of sound for button 9"] sound_9: Option<String>,
|
||||
#[description = "Name or ID of sound for button 10"] sound_10: Option<String>,
|
||||
#[description = "Name or ID of sound for button 11"] sound_11: Option<String>,
|
||||
#[description = "Name or ID of sound for button 12"] sound_12: Option<String>,
|
||||
#[description = "Name or ID of sound for button 13"] sound_13: Option<String>,
|
||||
#[description = "Name or ID of sound for button 14"] sound_14: Option<String>,
|
||||
#[description = "Name or ID of sound for button 15"] sound_15: Option<String>,
|
||||
#[description = "Name or ID of sound for button 16"] sound_16: Option<String>,
|
||||
#[description = "Name or ID of sound for button 17"] sound_17: Option<String>,
|
||||
#[description = "Name or ID of sound for button 18"] sound_18: Option<String>,
|
||||
#[description = "Name or ID of sound for button 19"] sound_19: Option<String>,
|
||||
#[description = "Name or ID of sound for button 20"] sound_20: Option<String>,
|
||||
#[description = "Name or ID of sound for button 21"] sound_21: Option<String>,
|
||||
#[description = "Name or ID of sound for button 22"] sound_22: Option<String>,
|
||||
#[description = "Name or ID of sound for button 23"] sound_23: Option<String>,
|
||||
#[description = "Name or ID of sound for button 24"] sound_24: Option<String>,
|
||||
#[description = "Name or ID of sound for button 25"] sound_25: Option<String>,
|
||||
) -> Result<(), Error> {
|
||||
ctx.defer().await?;
|
||||
|
||||
let pool = ctx
|
||||
.data
|
||||
.read()
|
||||
.await
|
||||
.get::<MySQL>()
|
||||
.cloned()
|
||||
.expect("Could not get SQLPool from data");
|
||||
let pool = ctx.data().database.clone();
|
||||
|
||||
let query_terms = [
|
||||
Some(sound_1),
|
||||
sound_2,
|
||||
sound_3,
|
||||
sound_4,
|
||||
sound_5,
|
||||
sound_6,
|
||||
sound_7,
|
||||
sound_8,
|
||||
sound_9,
|
||||
sound_10,
|
||||
sound_11,
|
||||
sound_12,
|
||||
sound_13,
|
||||
sound_14,
|
||||
sound_15,
|
||||
sound_16,
|
||||
sound_17,
|
||||
sound_18,
|
||||
sound_19,
|
||||
sound_20,
|
||||
sound_21,
|
||||
sound_22,
|
||||
sound_23,
|
||||
sound_24,
|
||||
sound_25,
|
||||
];
|
||||
|
||||
let mut sounds = vec![];
|
||||
|
||||
for n in 1..25 {
|
||||
for sound in query_terms.iter().flatten() {
|
||||
let search = Sound::search_for_sound(
|
||||
args.named(&n.to_string()).unwrap_or(&"".to_string()),
|
||||
invoke.guild_id().unwrap(),
|
||||
invoke.author_id(),
|
||||
&sound,
|
||||
ctx.guild_id().unwrap(),
|
||||
ctx.author().id,
|
||||
pool.clone(),
|
||||
true,
|
||||
)
|
||||
@ -396,29 +113,25 @@ pub async fn soundboard(
|
||||
}
|
||||
}
|
||||
|
||||
invoke
|
||||
.followup(
|
||||
ctx.http.clone(),
|
||||
CreateGenericResponse::new()
|
||||
.content("**Play a sound:**")
|
||||
.components(|c| {
|
||||
for row in sounds.as_slice().chunks(5) {
|
||||
let mut action_row: CreateActionRow = Default::default();
|
||||
for sound in row {
|
||||
action_row.create_button(|b| {
|
||||
b.style(ButtonStyle::Primary)
|
||||
.label(&sound.name)
|
||||
.custom_id(sound.id)
|
||||
});
|
||||
}
|
||||
ctx.send(|m| {
|
||||
m.content("**Play a sound:**").components(|c| {
|
||||
for row in sounds.as_slice().chunks(5) {
|
||||
let mut action_row: CreateActionRow = Default::default();
|
||||
for sound in row {
|
||||
action_row.create_button(|b| {
|
||||
b.style(ButtonStyle::Primary)
|
||||
.label(&sound.name)
|
||||
.custom_id(sound.id)
|
||||
});
|
||||
}
|
||||
|
||||
c.add_action_row(action_row);
|
||||
}
|
||||
c.add_action_row(action_row);
|
||||
}
|
||||
|
||||
c
|
||||
}),
|
||||
)
|
||||
.await?;
|
||||
c
|
||||
})
|
||||
})
|
||||
.await?;
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
@ -1,11 +1,4 @@
|
||||
use regex_command_attr::command;
|
||||
use serenity::{client::Context, framework::standard::CommandResult};
|
||||
|
||||
use crate::{
|
||||
framework::{Args, CommandInvoke, CreateGenericResponse},
|
||||
sound::Sound,
|
||||
MySQL,
|
||||
};
|
||||
use crate::sound::Sound;
|
||||
|
||||
fn format_search_results(search_results: Vec<Sound>) -> CreateGenericResponse {
|
||||
let mut current_character_count = 0;
|
||||
|
Reference in New Issue
Block a user