replaced allow_slash with a method to disallow text commands for soundboard. made string argument selector stricter

This commit is contained in:
jellywx 2021-06-25 23:13:11 +01:00
parent 14ef6385a0
commit a38e4c808e
9 changed files with 164 additions and 47 deletions

View File

@ -5,7 +5,7 @@ use syn::parse::{Error, Result};
use syn::spanned::Spanned;
use syn::{Attribute, Ident, Lit, LitStr, Meta, NestedMeta, Path};
use crate::structures::{ApplicationCommandOptionType, Arg, PermissionLevel};
use crate::structures::{ApplicationCommandOptionType, Arg, CommandKind, PermissionLevel};
use crate::util::{AsOption, LitExt};
#[derive(Debug, Clone, Copy, PartialEq)]
@ -321,6 +321,18 @@ impl AttributeOption for PermissionLevel {
}
}
impl AttributeOption for CommandKind {
fn parse(values: Values) -> Result<Self> {
validate(&values, &[ValueKind::SingleList])?;
Ok(values
.literals
.get(0)
.map(|(_, l)| CommandKind::from_str(&*l.to_str()).unwrap())
.unwrap())
}
}
impl AttributeOption for Arg {
fn parse(values: Values) -> Result<Self> {
validate(&values, &[ValueKind::EqualsList])?;

View File

@ -70,7 +70,7 @@ pub fn command(attr: TokenStream, input: TokenStream) -> TokenStream {
aliases;
group;
required_permissions;
allow_slash
kind
]);
}
}
@ -82,7 +82,7 @@ pub fn command(attr: TokenStream, input: TokenStream) -> TokenStream {
group,
examples,
required_permissions,
allow_slash,
kind,
mut cmd_args,
} = options;
@ -152,7 +152,7 @@ pub fn command(attr: TokenStream, input: TokenStream) -> TokenStream {
group: #group,
examples: &[#(#examples),*],
required_permissions: #required_permissions,
allow_slash: #allow_slash,
kind: #kind,
args: &[#(&#arg_idents),*],
};

View File

@ -214,6 +214,55 @@ impl ToTokens for PermissionLevel {
}
}
#[derive(Debug)]
pub enum CommandKind {
Slash,
Both,
Text,
}
impl Default for CommandKind {
fn default() -> Self {
Self::Both
}
}
impl CommandKind {
pub fn from_str(s: &str) -> Option<Self> {
Some(match s.to_uppercase().as_str() {
"SLASH" => Self::Slash,
"BOTH" => Self::Both,
"TEXT" => Self::Text,
_ => return None,
})
}
}
impl ToTokens for CommandKind {
fn to_tokens(&self, stream: &mut TokenStream2) {
let path = quote!(crate::framework::CommandKind);
let variant;
match self {
Self::Slash => {
variant = quote!(Slash);
}
Self::Both => {
variant = quote!(Both);
}
Self::Text => {
variant = quote!(Text);
}
}
stream.extend(quote! {
#path::#variant
});
}
}
#[derive(Debug)]
pub(crate) enum ApplicationCommandOptionType {
SubCommand,
@ -293,7 +342,7 @@ pub(crate) struct Options {
pub group: String,
pub examples: Vec<String>,
pub required_permissions: PermissionLevel,
pub allow_slash: bool,
pub kind: CommandKind,
pub cmd_args: Vec<Arg>,
}
@ -301,7 +350,6 @@ impl Options {
#[inline]
pub fn new() -> Self {
Self {
allow_slash: true,
group: "Other".to_string(),
..Default::default()
}

View File

@ -7,6 +7,7 @@ use crate::{
THEME_COLOR,
};
use crate::framework::CommandKind;
use std::{collections::HashMap, sync::Arc};
#[command]
@ -62,29 +63,14 @@ pub async fn help(
)
};
invoke
.respond(
ctx.http.clone(),
CreateGenericResponse::new().embed(|e| {
e.title(format!("{} Help", command_name))
.color(THEME_COLOR)
.description(format!(
"**Aliases**
{}
**Overview**
{}
**Arguments**
{}
let args = if command.args.is_empty() {
"**Arguments**
*This command has no arguments*"
.to_string()
} else {
format!(
"**Arguments**
{}",
command
.names
.iter()
.map(|n| format!("`{}`", n))
.collect::<Vec<String>>()
.join(" "),
command.desc,
command
.args
.iter()
@ -95,7 +81,47 @@ pub async fn help(
a.description
))
.collect::<Vec<String>>()
.join("\n"),
.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
))
}),

View File

@ -186,6 +186,7 @@ pub async fn upload_new_sound(
kind = "String",
required = true
)]
#[example("`/delete beep` - delete the sound with the name \"beep\"")]
pub async fn delete_sound(
ctx: &Context,
invoke: &(dyn CommandInvoke + Sync + Send),
@ -278,6 +279,8 @@ pub async fn delete_sound(
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")]
pub async fn change_public(
ctx: &Context,
invoke: &(dyn CommandInvoke + Sync + Send),

View File

@ -184,14 +184,14 @@ __Available ambience sounds:__
}
#[command("soundboard")]
#[aliases("board")]
#[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 = false
required = true
)]
#[arg(
name = "2",

View File

@ -40,6 +40,8 @@ fn format_search_results(search_results: Vec<Sound>) -> CreateGenericResponse {
kind = "Boolean",
required = false
)]
#[example("`/list` - list sounds uploaded to the server you're in")]
#[example("`/list [me: True]` - list sounds you have uploaded across all servers")]
pub async fn list_sounds(
ctx: &Context,
invoke: &(dyn CommandInvoke + Sync + Send),

View File

@ -20,6 +20,9 @@ use crate::{
kind = "Integer",
required = false
)]
#[example("`/volume` - check the volume on the current server")]
#[example("`/volume 100` - reset the volume on the current server")]
#[example("`/volume 10` - set the volume on the current server to 10%")]
pub async fn change_volume(
ctx: &Context,
invoke: &(dyn CommandInvoke + Sync + Send),
@ -66,7 +69,7 @@ pub async fn change_volume(
#[command("prefix")]
#[required_permissions(Restricted)]
#[allow_slash(false)]
#[kind(Text)]
#[group("Settings")]
#[description("Change the prefix of the bot for using non-slash commands")]
#[arg(
@ -97,7 +100,7 @@ pub async fn change_prefix(
}
if let Some(prefix) = args.named("prefix") {
if prefix.len() <= 5 {
if prefix.len() <= 5 && !prefix.is_empty() {
let reply = format!("Prefix changed to `{}`", prefix);
{
@ -142,7 +145,7 @@ pub async fn change_prefix(
#[command("roles")]
#[required_permissions(Restricted)]
#[allow_slash(false)]
#[kind(Text)]
#[group("Settings")]
#[description("Change the roles allowed to use the bot")]
pub async fn set_allowed_roles(
@ -240,6 +243,8 @@ INSERT INTO roles (guild_id, role)
description = "Name or ID of sound to set as your greet sound",
required = false
)]
#[example("`/greet` - remove your join sound")]
#[example("`/greet 1523` - set your join sound to sound with ID 1523")]
pub async fn set_greet_sound(
ctx: &Context,
invoke: &(dyn CommandInvoke + Sync + Send),
@ -312,6 +317,8 @@ pub async fn set_greet_sound(
#[group("Settings")]
#[description("Configure whether users should be able to use join sounds")]
#[required_permissions(Restricted)]
#[example("`/allow_greet` - disable greet sounds in the server")]
#[example("`/allow_greet` - re-enable greet sounds in the server")]
pub async fn allow_greet_sounds(
ctx: &Context,
invoke: &(dyn CommandInvoke + Sync + Send),

View File

@ -309,6 +309,13 @@ pub enum PermissionLevel {
Restricted,
}
#[derive(Debug, PartialEq)]
pub enum CommandKind {
Slash,
Both,
Text,
}
#[derive(Debug)]
pub struct Arg {
pub name: &'static str,
@ -320,7 +327,7 @@ pub struct Arg {
impl Arg {
pub fn to_regex(&self) -> String {
match self.kind {
ApplicationCommandOptionType::String => format!(r#"(?P<{}>.*?)"#, self.name),
ApplicationCommandOptionType::String => format!(r#"(?P<{}>.+?)"#, self.name),
ApplicationCommandOptionType::Integer => format!(r#"(?P<{}>\d+)"#, self.name),
ApplicationCommandOptionType::Boolean => format!(r#"(?P<{0}>{0})?"#, self.name),
ApplicationCommandOptionType::User => format!(r#"<(@|@!)(?P<{}>\d+)>"#, self.name),
@ -343,7 +350,7 @@ pub struct Command {
pub examples: &'static [&'static str],
pub group: &'static str,
pub allow_slash: bool,
pub kind: CommandKind,
pub required_permissions: PermissionLevel,
pub args: &'static [&'static Arg],
}
@ -528,7 +535,11 @@ impl RegexFramework {
.flatten()
.map(|v| GuildId(v))
{
for command in self.commands_.iter().filter(|c| c.allow_slash) {
for command in self
.commands_
.iter()
.filter(|c| c.kind != CommandKind::Text)
{
guild_id
.create_application_command(&http, |a| {
a.name(command.names[0]).description(command.desc);
@ -577,7 +588,11 @@ impl RegexFramework {
}
}
for command in self.commands_.iter().filter(|c| c.allow_slash) {
for command in self
.commands_
.iter()
.filter(|c| c.kind != CommandKind::Text)
{
let already_created = if let Some(current_command) = current_commands
.iter()
.find(|curr| curr.name == command.names[0])
@ -752,6 +767,7 @@ impl Framework for RegexFramework {
.get(&full_match.name("cmd").unwrap().as_str().to_lowercase())
.unwrap();
if command.kind != CommandKind::Slash {
let args = full_match
.name("args")
.map(|m| m.as_str())
@ -764,7 +780,9 @@ impl Framework for RegexFramework {
(command.fun)(&ctx, &msg, Args::from(&args, command.args))
.await
.unwrap();
} else if command.required_permissions == PermissionLevel::Managed {
} else if command.required_permissions
== PermissionLevel::Managed
{
let _ = msg.channel_id.say(&ctx, "You must either be an Admin or have a role specified in `?roles` to do this command").await;
} else if command.required_permissions
== PermissionLevel::Restricted
@ -775,6 +793,7 @@ impl Framework for RegexFramework {
.await;
}
}
}
PermissionCheck::None => {
warn!("Missing enough permissions for guild {}", guild.id);