updated the regex_command_attr to reflect upstream. changed add_command to not accept a name
This commit is contained in:
parent
99e1807097
commit
d5d2ac2bee
2
Cargo.lock
generated
2
Cargo.lock
generated
@ -1347,7 +1347,7 @@ checksum = "f497285884f3fcff424ffc933e56d7cbca511def0c9831a7f9b5f6153e3cc89b"
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "regex_command_attr"
|
name = "regex_command_attr"
|
||||||
version = "0.2.0"
|
version = "0.3.6"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"proc-macro2",
|
"proc-macro2",
|
||||||
"quote",
|
"quote",
|
||||||
|
@ -1,9 +1,10 @@
|
|||||||
[package]
|
[package]
|
||||||
name = "regex_command_attr"
|
name = "regex_command_attr"
|
||||||
version = "0.2.0"
|
version = "0.3.6"
|
||||||
authors = ["acdenisSK <acdenissk69@gmail.com>", "jellywx <judesouthworth@pm.me>"]
|
authors = ["acdenisSK <acdenissk69@gmail.com>", "jellywx <judesouthworth@pm.me>"]
|
||||||
edition = "2018"
|
edition = "2018"
|
||||||
description = "Procedural macros for command creation for the RegexFramework for serenity."
|
description = "Procedural macros for command creation for the Serenity library."
|
||||||
|
license = "ISC"
|
||||||
|
|
||||||
[lib]
|
[lib]
|
||||||
proc-macro = true
|
proc-macro = true
|
||||||
|
@ -1,3 +1,5 @@
|
|||||||
|
use std::fmt::{self, Write};
|
||||||
|
|
||||||
use proc_macro2::Span;
|
use proc_macro2::Span;
|
||||||
use syn::parse::{Error, Result};
|
use syn::parse::{Error, Result};
|
||||||
use syn::spanned::Spanned;
|
use syn::spanned::Spanned;
|
||||||
@ -6,8 +8,6 @@ use syn::{Attribute, Ident, Lit, LitStr, Meta, NestedMeta, Path};
|
|||||||
use crate::structures::PermissionLevel;
|
use crate::structures::PermissionLevel;
|
||||||
use crate::util::{AsOption, LitExt};
|
use crate::util::{AsOption, LitExt};
|
||||||
|
|
||||||
use std::fmt::{self, Write};
|
|
||||||
|
|
||||||
#[derive(Debug, Clone, Copy, PartialEq)]
|
#[derive(Debug, Clone, Copy, PartialEq)]
|
||||||
pub enum ValueKind {
|
pub enum ValueKind {
|
||||||
// #[<name>]
|
// #[<name>]
|
||||||
|
@ -1,10 +1,5 @@
|
|||||||
#![deny(rust_2018_idioms)]
|
#![deny(rust_2018_idioms)]
|
||||||
// FIXME: Remove this in a foreseeable future.
|
#![deny(broken_intra_doc_links)]
|
||||||
// Currently exists for backwards compatibility to previous Rust versions.
|
|
||||||
#![recursion_limit = "128"]
|
|
||||||
|
|
||||||
#[allow(unused_extern_crates)]
|
|
||||||
extern crate proc_macro;
|
|
||||||
|
|
||||||
use proc_macro::TokenStream;
|
use proc_macro::TokenStream;
|
||||||
use quote::quote;
|
use quote::quote;
|
||||||
@ -37,11 +32,66 @@ macro_rules! match_options {
|
|||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// The heart of the attribute-based framework.
|
||||||
|
///
|
||||||
|
/// This is a function attribute macro. Using this on other Rust constructs won't work.
|
||||||
|
///
|
||||||
|
/// ## Options
|
||||||
|
///
|
||||||
|
/// To alter how the framework will interpret the command,
|
||||||
|
/// you can provide options as attributes following this `#[command]` macro.
|
||||||
|
///
|
||||||
|
/// Each option has its own kind of data to stock and manipulate with.
|
||||||
|
/// They're given to the option either with the `#[option(...)]` or `#[option = ...]` syntaxes.
|
||||||
|
/// If an option doesn't require for any data to be supplied, then it's simply an empty `#[option]`.
|
||||||
|
///
|
||||||
|
/// If the input to the option is malformed, the macro will give you can error, describing
|
||||||
|
/// the correct method for passing data, and what it should be.
|
||||||
|
///
|
||||||
|
/// The list of available options, is, as follows:
|
||||||
|
///
|
||||||
|
/// | Syntax | Description | Argument explanation |
|
||||||
|
/// | ---------------------------------------------------------------------------- | -------------------------------------------------------------------------------------------------------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
|
||||||
|
/// | `#[checks(identifiers)]` | Preconditions that must met before the command's execution. | `identifiers` is a comma separated list of identifiers referencing functions marked by the `#[check]` macro |
|
||||||
|
/// | `#[aliases(names)]` | Alternative names to refer to this command. | `names` is a comma separated list of desired aliases. |
|
||||||
|
/// | `#[description(desc)]` </br> `#[description = desc]` | The command's description or summary. | `desc` is a string describing the command. |
|
||||||
|
/// | `#[usage(use)]` </br> `#[usage = use]` | The command's intended usage. | `use` is a string stating the schema for the command's usage. |
|
||||||
|
/// | `#[example(ex)]` </br> `#[example = ex]` | An example of the command's usage. May be called multiple times to add many examples at once. | `ex` is a string |
|
||||||
|
/// | `#[delimiters(delims)]` | Argument delimiters specific to this command. Overrides the global list of delimiters in the framework. | `delims` is a comma separated list of strings |
|
||||||
|
/// | `#[min_args(min)]` </br> `#[max_args(max)]` </br> `#[num_args(min_and_max)]` | The expected length of arguments that the command must receive in order to function correctly. | `min`, `max` and `min_and_max` are 16-bit, unsigned integers. |
|
||||||
|
/// | `#[required_permissions(perms)]` | Set of permissions the user must possess. | `perms` is a comma separated list of permission names.</br> These can be found at [Discord's official documentation](https://discord.com/developers/docs/topics/permissions). |
|
||||||
|
/// | `#[allowed_roles(roles)]` | Set of roles the user must possess. | `roles` is a comma separated list of role names. |
|
||||||
|
/// | `#[help_available]` </br> `#[help_available(b)]` | If the command should be displayed in the help message. | `b` is a boolean. If no boolean is provided, the value is assumed to be `true`. |
|
||||||
|
/// | `#[only_in(ctx)]` | Which environment the command can be executed in. | `ctx` is a string with the accepted values `guild`/`guilds` and `dm`/`dms` (Direct Message). |
|
||||||
|
/// | `#[bucket(name)]` </br> `#[bucket = name]` | What bucket will impact this command. | `name` is a string containing the bucket's name.</br> Refer to [the bucket example in the standard framework](https://docs.rs/serenity/*/serenity/framework/standard/struct.StandardFramework.html#method.bucket) for its usage. |
|
||||||
|
/// | `#[owners_only]` </br> `#[owners_only(b)]` | If this command is exclusive to owners. | `b` is a boolean. If no boolean is provided, the value is assumed to be `true`. |
|
||||||
|
/// | `#[owner_privilege]` </br> `#[owner_privilege(b)]` | If owners can bypass certain options. | `b` is a boolean. If no boolean is provided, the value is assumed to be `true`. |
|
||||||
|
/// | `#[sub_commands(commands)]` | The sub or children commands of this command. They are executed in the form: `this-command sub-command`. | `commands` is a comma separated list of identifiers referencing functions marked by the `#[command]` macro. |
|
||||||
|
///
|
||||||
|
/// Documentation comments (`///`) applied onto the function are interpreted as sugar for the
|
||||||
|
/// `#[description]` option. When more than one application of the option is performed,
|
||||||
|
/// the text is delimited by newlines. This mimics the behaviour of regular doc-comments,
|
||||||
|
/// which are sugar for the `#[doc = "..."]` attribute. If you wish to join lines together,
|
||||||
|
/// however, you have to end the previous lines with `\$`.
|
||||||
|
///
|
||||||
|
/// # Notes
|
||||||
|
/// The name of the command is parsed from the applied function,
|
||||||
|
/// or may be specified inside the `#[command]` attribute, a lá `#[command("foobar")]`.
|
||||||
|
///
|
||||||
|
/// This macro attribute generates static instances of `Command` and `CommandOptions`,
|
||||||
|
/// conserving the provided options.
|
||||||
|
///
|
||||||
|
/// The names of the instances are all uppercased names of the command name.
|
||||||
|
/// For example, with a name of "foo":
|
||||||
|
/// ```rust,ignore
|
||||||
|
/// pub static FOO_COMMAND_OPTIONS: CommandOptions = CommandOptions { ... };
|
||||||
|
/// pub static FOO_COMMAND: Command = Command { options: FOO_COMMAND_OPTIONS, ... };
|
||||||
|
/// ```
|
||||||
#[proc_macro_attribute]
|
#[proc_macro_attribute]
|
||||||
pub fn command(attr: TokenStream, input: TokenStream) -> TokenStream {
|
pub fn command(attr: TokenStream, input: TokenStream) -> TokenStream {
|
||||||
let mut fun = parse_macro_input!(input as CommandFun);
|
let mut fun = parse_macro_input!(input as CommandFun);
|
||||||
|
|
||||||
let lit_name = if !attr.is_empty() {
|
let _name = if !attr.is_empty() {
|
||||||
parse_macro_input!(attr as Lit).to_str()
|
parse_macro_input!(attr as Lit).to_str()
|
||||||
} else {
|
} else {
|
||||||
fun.name.to_string()
|
fun.name.to_string()
|
||||||
@ -56,18 +106,37 @@ pub fn command(attr: TokenStream, input: TokenStream) -> TokenStream {
|
|||||||
let name = values.name.to_string();
|
let name = values.name.to_string();
|
||||||
let name = &name[..];
|
let name = &name[..];
|
||||||
|
|
||||||
|
match name {
|
||||||
|
"example" => {
|
||||||
|
options
|
||||||
|
.examples
|
||||||
|
.push(propagate_err!(attributes::parse(values)));
|
||||||
|
}
|
||||||
|
"description" => {
|
||||||
|
let line: String = propagate_err!(attributes::parse(values));
|
||||||
|
util::append_line(&mut options.description, line);
|
||||||
|
}
|
||||||
|
_ => {
|
||||||
match_options!(name, values, options, span => [
|
match_options!(name, values, options, span => [
|
||||||
permission_level;
|
aliases;
|
||||||
|
usage;
|
||||||
|
required_permissions;
|
||||||
allow_slash
|
allow_slash
|
||||||
]);
|
]);
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
let Options {
|
let Options {
|
||||||
permission_level,
|
aliases,
|
||||||
|
description,
|
||||||
|
usage,
|
||||||
|
examples,
|
||||||
|
required_permissions,
|
||||||
allow_slash,
|
allow_slash,
|
||||||
} = options;
|
} = options;
|
||||||
|
|
||||||
propagate_err!(create_declaration_validations(&mut fun, DeclarFor::Command));
|
propagate_err!(create_declaration_validations(&mut fun));
|
||||||
|
|
||||||
let res = parse_quote!(serenity::framework::standard::CommandResult);
|
let res = parse_quote!(serenity::framework::standard::CommandResult);
|
||||||
create_return_type_validation(&mut fun, res);
|
create_return_type_validation(&mut fun, res);
|
||||||
@ -88,18 +157,185 @@ pub fn command(attr: TokenStream, input: TokenStream) -> TokenStream {
|
|||||||
|
|
||||||
(quote! {
|
(quote! {
|
||||||
#(#cooked)*
|
#(#cooked)*
|
||||||
|
#[allow(missing_docs)]
|
||||||
pub static #n: #command_path = #command_path {
|
pub static #n: #command_path = #command_path {
|
||||||
func: #name,
|
fun: #name,
|
||||||
name: #lit_name,
|
names: &[#_name, #(#aliases),*],
|
||||||
required_perms: #permission_level,
|
desc: #description,
|
||||||
|
usage: #usage,
|
||||||
|
examples: &[#(#examples),*],
|
||||||
|
required_permissions: #required_permissions,
|
||||||
allow_slash: #allow_slash,
|
allow_slash: #allow_slash,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
#(#cooked)*
|
||||||
|
#[allow(missing_docs)]
|
||||||
#visibility fn #name<'fut> (#(#args),*) -> ::serenity::futures::future::BoxFuture<'fut, #ret> {
|
#visibility fn #name<'fut> (#(#args),*) -> ::serenity::futures::future::BoxFuture<'fut, #ret> {
|
||||||
use ::serenity::futures::future::FutureExt;
|
use ::serenity::futures::future::FutureExt;
|
||||||
|
|
||||||
async move { #(#body)* }.boxed()
|
async move {
|
||||||
|
let _output: #ret = { #(#body)* };
|
||||||
|
#[allow(unreachable_code)]
|
||||||
|
_output
|
||||||
|
}.boxed()
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
.into()
|
.into()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// A macro that transforms `async` functions (and closures) into plain functions, whose
|
||||||
|
/// return type is a boxed [`Future`].
|
||||||
|
///
|
||||||
|
/// # Transformation
|
||||||
|
///
|
||||||
|
/// The macro transforms an `async` function, which may look like this:
|
||||||
|
///
|
||||||
|
/// ```rust,no_run
|
||||||
|
/// async fn foo(n: i32) -> i32 {
|
||||||
|
/// n + 4
|
||||||
|
/// }
|
||||||
|
/// ```
|
||||||
|
///
|
||||||
|
/// into this (some details omitted):
|
||||||
|
///
|
||||||
|
/// ```rust,no_run
|
||||||
|
/// use std::future::Future;
|
||||||
|
/// use std::pin::Pin;
|
||||||
|
///
|
||||||
|
/// fn foo(n: i32) -> Pin<Box<dyn std::future::Future<Output = i32>>> {
|
||||||
|
/// Box::pin(async move {
|
||||||
|
/// n + 4
|
||||||
|
/// })
|
||||||
|
/// }
|
||||||
|
/// ```
|
||||||
|
///
|
||||||
|
/// This transformation also applies to closures, which are converted more simply. For instance,
|
||||||
|
/// this closure:
|
||||||
|
///
|
||||||
|
/// ```rust,no_run
|
||||||
|
/// # #![feature(async_closure)]
|
||||||
|
/// #
|
||||||
|
/// async move |x: i32| {
|
||||||
|
/// x * 2 + 4
|
||||||
|
/// }
|
||||||
|
/// # ;
|
||||||
|
/// ```
|
||||||
|
///
|
||||||
|
/// is changed to:
|
||||||
|
///
|
||||||
|
/// ```rust,no_run
|
||||||
|
/// |x: i32| {
|
||||||
|
/// Box::pin(async move {
|
||||||
|
/// x * 2 + 4
|
||||||
|
/// })
|
||||||
|
/// }
|
||||||
|
/// # ;
|
||||||
|
/// ```
|
||||||
|
///
|
||||||
|
/// ## How references are handled
|
||||||
|
///
|
||||||
|
/// When a function contains references, their lifetimes are constrained to the returned
|
||||||
|
/// [`Future`]. If the above `foo` function had `&i32` as a parameter, the transformation would be
|
||||||
|
/// instead this:
|
||||||
|
///
|
||||||
|
/// ```rust,no_run
|
||||||
|
/// use std::future::Future;
|
||||||
|
/// use std::pin::Pin;
|
||||||
|
///
|
||||||
|
/// fn foo<'fut>(n: &'fut i32) -> Pin<Box<dyn std::future::Future<Output = i32> + 'fut>> {
|
||||||
|
/// Box::pin(async move {
|
||||||
|
/// *n + 4
|
||||||
|
/// })
|
||||||
|
/// }
|
||||||
|
/// ```
|
||||||
|
///
|
||||||
|
/// Explicitly specifying lifetimes (in the parameters or in the return type) or complex usage of
|
||||||
|
/// lifetimes (e.g. `'a: 'b`) is not supported.
|
||||||
|
///
|
||||||
|
/// # Necessity for the macro
|
||||||
|
///
|
||||||
|
/// The macro performs the transformation to permit the framework to store and invoke the functions.
|
||||||
|
///
|
||||||
|
/// Functions marked with the `async` keyword will wrap their return type with the [`Future`] trait,
|
||||||
|
/// which a state-machine generated by the compiler for the function will implement. This complicates
|
||||||
|
/// matters for the framework, as [`Future`] is a trait. Depending on a type that implements a trait
|
||||||
|
/// is done with two methods in Rust:
|
||||||
|
///
|
||||||
|
/// 1. static dispatch - generics
|
||||||
|
/// 2. dynamic dispatch - trait objects
|
||||||
|
///
|
||||||
|
/// First method is infeasible for the framework. Typically, the framework will contain a plethora
|
||||||
|
/// of different commands that will be stored in a single list. And due to the nature of generics,
|
||||||
|
/// generic types can only resolve to a single concrete type. If commands had a generic type for
|
||||||
|
/// their function's return type, the framework would be unable to store commands, as only a single
|
||||||
|
/// [`Future`] type from one of the commands would get resolved, preventing other commands from being
|
||||||
|
/// stored.
|
||||||
|
///
|
||||||
|
/// Second method involves heap allocations, but is the only working solution. If a trait is
|
||||||
|
/// object-safe (which [`Future`] is), the compiler can generate a table of function pointers
|
||||||
|
/// (a vtable) that correspond to certain implementations of the trait. This allows to decide
|
||||||
|
/// which implementation to use at runtime. Thus, we can use the interface for the [`Future`] trait,
|
||||||
|
/// and avoid depending on the underlying value (such as its size). To opt-in to dynamic dispatch,
|
||||||
|
/// trait objects must be used with a pointer, like references (`&` and `&mut`) or `Box`. The
|
||||||
|
/// latter is what's used by the macro, as the ownership of the value (the state-machine) must be
|
||||||
|
/// given to the caller, the framework in this case.
|
||||||
|
///
|
||||||
|
/// The macro exists to retain the normal syntax of `async` functions (and closures), while
|
||||||
|
/// granting the user the ability to pass those functions to the framework, like command functions
|
||||||
|
/// and hooks (`before`, `after`, `on_dispatch_error`, etc.).
|
||||||
|
///
|
||||||
|
/// # Notes
|
||||||
|
///
|
||||||
|
/// If applying the macro on an `async` closure, you will need to enable the `async_closure`
|
||||||
|
/// feature. Inputs to procedural macro attributes must be valid Rust code, and `async`
|
||||||
|
/// closures are not stable yet.
|
||||||
|
///
|
||||||
|
/// [`Future`]: std::future::Future
|
||||||
|
#[proc_macro_attribute]
|
||||||
|
pub fn hook(_attr: TokenStream, input: TokenStream) -> TokenStream {
|
||||||
|
let hook = parse_macro_input!(input as Hook);
|
||||||
|
|
||||||
|
match hook {
|
||||||
|
Hook::Function(mut fun) => {
|
||||||
|
let cooked = fun.cooked;
|
||||||
|
let visibility = fun.visibility;
|
||||||
|
let fun_name = fun.name;
|
||||||
|
let body = fun.body;
|
||||||
|
let ret = fun.ret;
|
||||||
|
|
||||||
|
populate_fut_lifetimes_on_refs(&mut fun.args);
|
||||||
|
let args = fun.args;
|
||||||
|
|
||||||
|
(quote! {
|
||||||
|
#(#cooked)*
|
||||||
|
#[allow(missing_docs)]
|
||||||
|
#visibility fn #fun_name<'fut>(#(#args),*) -> ::serenity::futures::future::BoxFuture<'fut, #ret> {
|
||||||
|
use ::serenity::futures::future::FutureExt;
|
||||||
|
|
||||||
|
async move {
|
||||||
|
let _output: #ret = { #(#body)* };
|
||||||
|
#[allow(unreachable_code)]
|
||||||
|
_output
|
||||||
|
}.boxed()
|
||||||
|
}
|
||||||
|
})
|
||||||
|
.into()
|
||||||
|
}
|
||||||
|
Hook::Closure(closure) => {
|
||||||
|
let cooked = closure.cooked;
|
||||||
|
let args = closure.args;
|
||||||
|
let ret = closure.ret;
|
||||||
|
let body = closure.body;
|
||||||
|
|
||||||
|
(quote! {
|
||||||
|
#(#cooked)*
|
||||||
|
|#args| #ret {
|
||||||
|
use ::serenity::futures::future::FutureExt;
|
||||||
|
|
||||||
|
async move { #body }.boxed()
|
||||||
|
}
|
||||||
|
})
|
||||||
|
.into()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
@ -1,15 +1,18 @@
|
|||||||
use crate::util::{Argument, Parenthesised};
|
use std::str::FromStr;
|
||||||
use proc_macro2::Span;
|
|
||||||
use proc_macro2::TokenStream as TokenStream2;
|
use proc_macro2::TokenStream as TokenStream2;
|
||||||
use quote::{quote, ToTokens};
|
use quote::{quote, ToTokens};
|
||||||
use syn::{
|
use syn::{
|
||||||
braced,
|
braced,
|
||||||
parse::{Error, Parse, ParseStream, Result},
|
parse::{Error, Parse, ParseStream, Result},
|
||||||
|
punctuated::Punctuated,
|
||||||
spanned::Spanned,
|
spanned::Spanned,
|
||||||
Attribute, Block, FnArg, Ident, Pat, Path, PathSegment, ReturnType, Stmt, Token, Type,
|
Attribute, Block, Expr, ExprClosure, FnArg, Ident, Pat, ReturnType, Stmt, Token, Type,
|
||||||
Visibility,
|
Visibility,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
use crate::util::{self, Argument, AsOption, Parenthesised};
|
||||||
|
|
||||||
fn parse_argument(arg: FnArg) -> Result<Argument> {
|
fn parse_argument(arg: FnArg) -> Result<Argument> {
|
||||||
match arg {
|
match arg {
|
||||||
FnArg::Typed(typed) => {
|
FnArg::Typed(typed) => {
|
||||||
@ -54,7 +57,7 @@ fn parse_argument(arg: FnArg) -> Result<Argument> {
|
|||||||
/// Test if the attribute is cooked.
|
/// Test if the attribute is cooked.
|
||||||
fn is_cooked(attr: &Attribute) -> bool {
|
fn is_cooked(attr: &Attribute) -> bool {
|
||||||
const COOKED_ATTRIBUTE_NAMES: &[&str] = &[
|
const COOKED_ATTRIBUTE_NAMES: &[&str] = &[
|
||||||
"cfg", "cfg_attr", "doc", "derive", "inline", "allow", "warn", "deny", "forbid",
|
"cfg", "cfg_attr", "derive", "inline", "allow", "warn", "deny", "forbid",
|
||||||
];
|
];
|
||||||
|
|
||||||
COOKED_ATTRIBUTE_NAMES.iter().any(|n| attr.path.is_ident(n))
|
COOKED_ATTRIBUTE_NAMES.iter().any(|n| attr.path.is_ident(n))
|
||||||
@ -100,16 +103,8 @@ impl Parse for CommandFun {
|
|||||||
fn parse(input: ParseStream<'_>) -> Result<Self> {
|
fn parse(input: ParseStream<'_>) -> Result<Self> {
|
||||||
let mut attributes = input.call(Attribute::parse_outer)?;
|
let mut attributes = input.call(Attribute::parse_outer)?;
|
||||||
|
|
||||||
// `#[doc = "..."]` is a cooked attribute but it is special-cased for commands.
|
|
||||||
for attr in &mut attributes {
|
|
||||||
// Rename documentation comment attributes (`#[doc = "..."]`) to `#[description = "..."]`.
|
// Rename documentation comment attributes (`#[doc = "..."]`) to `#[description = "..."]`.
|
||||||
if attr.path.is_ident("doc") {
|
util::rename_attributes(&mut attributes, "doc", "description");
|
||||||
attr.path = Path::from(PathSegment::from(Ident::new(
|
|
||||||
"description",
|
|
||||||
Span::call_site(),
|
|
||||||
)));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
let cooked = remove_cooked(&mut attributes);
|
let cooked = remove_cooked(&mut attributes);
|
||||||
|
|
||||||
@ -174,6 +169,115 @@ impl ToTokens for CommandFun {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[derive(Debug)]
|
||||||
|
pub struct FunctionHook {
|
||||||
|
/// `#[...]`-style attributes.
|
||||||
|
pub attributes: Vec<Attribute>,
|
||||||
|
/// Populated by cooked attributes. These are attributes outside of the realm of this crate's procedural macros
|
||||||
|
/// and will appear in generated output.
|
||||||
|
pub cooked: Vec<Attribute>,
|
||||||
|
pub visibility: Visibility,
|
||||||
|
pub name: Ident,
|
||||||
|
pub args: Vec<Argument>,
|
||||||
|
pub ret: Type,
|
||||||
|
pub body: Vec<Stmt>,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug)]
|
||||||
|
pub struct ClosureHook {
|
||||||
|
/// `#[...]`-style attributes.
|
||||||
|
pub attributes: Vec<Attribute>,
|
||||||
|
/// Populated by cooked attributes. These are attributes outside of the realm of this crate's procedural macros
|
||||||
|
/// and will appear in generated output.
|
||||||
|
pub cooked: Vec<Attribute>,
|
||||||
|
pub args: Punctuated<Pat, Token![,]>,
|
||||||
|
pub ret: ReturnType,
|
||||||
|
pub body: Box<Expr>,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug)]
|
||||||
|
pub enum Hook {
|
||||||
|
Function(FunctionHook),
|
||||||
|
Closure(ClosureHook),
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Parse for Hook {
|
||||||
|
fn parse(input: ParseStream<'_>) -> Result<Self> {
|
||||||
|
let mut attributes = input.call(Attribute::parse_outer)?;
|
||||||
|
let cooked = remove_cooked(&mut attributes);
|
||||||
|
|
||||||
|
if is_function(input) {
|
||||||
|
parse_function_hook(input, attributes, cooked).map(Self::Function)
|
||||||
|
} else {
|
||||||
|
parse_closure_hook(input, attributes, cooked).map(Self::Closure)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn is_function(input: ParseStream<'_>) -> bool {
|
||||||
|
input.peek(Token![pub]) || (input.peek(Token![async]) && input.peek2(Token![fn]))
|
||||||
|
}
|
||||||
|
|
||||||
|
fn parse_function_hook(
|
||||||
|
input: ParseStream<'_>,
|
||||||
|
attributes: Vec<Attribute>,
|
||||||
|
cooked: Vec<Attribute>,
|
||||||
|
) -> Result<FunctionHook> {
|
||||||
|
let visibility = input.parse::<Visibility>()?;
|
||||||
|
|
||||||
|
input.parse::<Token![async]>()?;
|
||||||
|
input.parse::<Token![fn]>()?;
|
||||||
|
|
||||||
|
let name = input.parse()?;
|
||||||
|
|
||||||
|
// (...)
|
||||||
|
let Parenthesised(args) = input.parse::<Parenthesised<FnArg>>()?;
|
||||||
|
|
||||||
|
let ret = match input.parse::<ReturnType>()? {
|
||||||
|
ReturnType::Type(_, t) => (*t).clone(),
|
||||||
|
ReturnType::Default => {
|
||||||
|
Type::Verbatim(TokenStream2::from_str("()").expect("Invalid str to create `()`-type"))
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
// { ... }
|
||||||
|
let bcont;
|
||||||
|
braced!(bcont in input);
|
||||||
|
let body = bcont.call(Block::parse_within)?;
|
||||||
|
|
||||||
|
let args = args
|
||||||
|
.into_iter()
|
||||||
|
.map(parse_argument)
|
||||||
|
.collect::<Result<Vec<_>>>()?;
|
||||||
|
|
||||||
|
Ok(FunctionHook {
|
||||||
|
attributes,
|
||||||
|
cooked,
|
||||||
|
visibility,
|
||||||
|
name,
|
||||||
|
args,
|
||||||
|
ret,
|
||||||
|
body,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
fn parse_closure_hook(
|
||||||
|
input: ParseStream<'_>,
|
||||||
|
attributes: Vec<Attribute>,
|
||||||
|
cooked: Vec<Attribute>,
|
||||||
|
) -> Result<ClosureHook> {
|
||||||
|
input.parse::<Token![async]>()?;
|
||||||
|
let closure = input.parse::<ExprClosure>()?;
|
||||||
|
|
||||||
|
Ok(ClosureHook {
|
||||||
|
attributes,
|
||||||
|
cooked,
|
||||||
|
args: closure.inputs,
|
||||||
|
ret: closure.output,
|
||||||
|
body: closure.body,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
#[derive(Debug)]
|
#[derive(Debug)]
|
||||||
pub enum PermissionLevel {
|
pub enum PermissionLevel {
|
||||||
Unrestricted,
|
Unrestricted,
|
||||||
@ -225,7 +329,11 @@ impl ToTokens for PermissionLevel {
|
|||||||
|
|
||||||
#[derive(Debug, Default)]
|
#[derive(Debug, Default)]
|
||||||
pub struct Options {
|
pub struct Options {
|
||||||
pub permission_level: PermissionLevel,
|
pub aliases: Vec<String>,
|
||||||
|
pub description: AsOption<String>,
|
||||||
|
pub usage: AsOption<String>,
|
||||||
|
pub examples: Vec<String>,
|
||||||
|
pub required_permissions: PermissionLevel,
|
||||||
pub allow_slash: bool,
|
pub allow_slash: bool,
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -233,8 +341,8 @@ impl Options {
|
|||||||
#[inline]
|
#[inline]
|
||||||
pub fn new() -> Self {
|
pub fn new() -> Self {
|
||||||
Self {
|
Self {
|
||||||
permission_level: PermissionLevel::default(),
|
allow_slash: true,
|
||||||
allow_slash: false,
|
..Default::default()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,4 +1,3 @@
|
|||||||
use crate::structures::CommandFun;
|
|
||||||
use proc_macro::TokenStream;
|
use proc_macro::TokenStream;
|
||||||
use proc_macro2::Span;
|
use proc_macro2::Span;
|
||||||
use proc_macro2::TokenStream as TokenStream2;
|
use proc_macro2::TokenStream as TokenStream2;
|
||||||
@ -10,9 +9,11 @@ use syn::{
|
|||||||
punctuated::Punctuated,
|
punctuated::Punctuated,
|
||||||
spanned::Spanned,
|
spanned::Spanned,
|
||||||
token::{Comma, Mut},
|
token::{Comma, Mut},
|
||||||
Ident, Lifetime, Lit, Type,
|
Attribute, Ident, Lifetime, Lit, Path, PathSegment, Type,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
use crate::structures::CommandFun;
|
||||||
|
|
||||||
pub trait LitExt {
|
pub trait LitExt {
|
||||||
fn to_str(&self) -> String;
|
fn to_str(&self) -> String;
|
||||||
fn to_bool(&self) -> bool;
|
fn to_bool(&self) -> bool;
|
||||||
@ -160,35 +161,17 @@ pub fn generate_type_validation(have: Type, expect: Type) -> syn::Stmt {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
|
pub fn create_declaration_validations(fun: &mut CommandFun) -> SynResult<()> {
|
||||||
pub enum DeclarFor {
|
if fun.args.len() > 3 {
|
||||||
Command,
|
|
||||||
Help,
|
|
||||||
Check,
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn create_declaration_validations(fun: &mut CommandFun, dec_for: DeclarFor) -> SynResult<()> {
|
|
||||||
let len = match dec_for {
|
|
||||||
DeclarFor::Command => 3,
|
|
||||||
DeclarFor::Help => 6,
|
|
||||||
DeclarFor::Check => 4,
|
|
||||||
};
|
|
||||||
|
|
||||||
if fun.args.len() > len {
|
|
||||||
return Err(Error::new(
|
return Err(Error::new(
|
||||||
fun.args.last().unwrap().span(),
|
fun.args.last().unwrap().span(),
|
||||||
format_args!("function's arity exceeds more than {} arguments", len),
|
format_args!("function's arity exceeds more than 3 arguments"),
|
||||||
));
|
));
|
||||||
}
|
}
|
||||||
|
|
||||||
let context: Type = parse_quote!(&serenity::client::Context);
|
let context: Type = parse_quote!(&serenity::client::Context);
|
||||||
let message: Type = parse_quote!(&(dyn crate::framework::CommandInvoke + Sync + Send));
|
let message: Type = parse_quote!(&(dyn crate::framework::CommandInvoke + Sync + Send));
|
||||||
let args: Type = parse_quote!(serenity::framework::standard::Args);
|
let args: Type = parse_quote!(serenity::framework::standard::Args);
|
||||||
let args2: Type = parse_quote!(&mut serenity::framework::standard::Args);
|
|
||||||
let options: Type = parse_quote!(&serenity::framework::standard::CommandOptions);
|
|
||||||
let hoptions: Type = parse_quote!(&'static serenity::framework::standard::HelpOptions);
|
|
||||||
let groups: Type = parse_quote!(&[&'static serenity::framework::standard::CommandGroup]);
|
|
||||||
let owners: Type = parse_quote!(std::collections::HashSet<serenity::model::id::UserId>);
|
|
||||||
|
|
||||||
let mut index = 0;
|
let mut index = 0;
|
||||||
|
|
||||||
@ -209,22 +192,8 @@ pub fn create_declaration_validations(fun: &mut CommandFun, dec_for: DeclarFor)
|
|||||||
|
|
||||||
spoof_or_check(context, "_ctx");
|
spoof_or_check(context, "_ctx");
|
||||||
spoof_or_check(message, "_msg");
|
spoof_or_check(message, "_msg");
|
||||||
|
|
||||||
if dec_for == DeclarFor::Check {
|
|
||||||
spoof_or_check(args2, "_args");
|
|
||||||
spoof_or_check(options, "_options");
|
|
||||||
|
|
||||||
return Ok(());
|
|
||||||
}
|
|
||||||
|
|
||||||
spoof_or_check(args, "_args");
|
spoof_or_check(args, "_args");
|
||||||
|
|
||||||
if dec_for == DeclarFor::Help {
|
|
||||||
spoof_or_check(hoptions, "_hoptions");
|
|
||||||
spoof_or_check(groups, "_groups");
|
|
||||||
spoof_or_check(owners, "_owners");
|
|
||||||
}
|
|
||||||
|
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -242,3 +211,31 @@ pub fn populate_fut_lifetimes_on_refs(args: &mut Vec<Argument>) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Renames all attributes that have a specific `name` to the `target`.
|
||||||
|
pub fn rename_attributes(attributes: &mut Vec<Attribute>, name: &str, target: &str) {
|
||||||
|
for attr in attributes {
|
||||||
|
if attr.path.is_ident(name) {
|
||||||
|
attr.path = Path::from(PathSegment::from(Ident::new(target, Span::call_site())));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn append_line(desc: &mut AsOption<String>, mut line: String) {
|
||||||
|
if line.starts_with(' ') {
|
||||||
|
line.remove(0);
|
||||||
|
}
|
||||||
|
|
||||||
|
let desc = desc.0.get_or_insert_with(String::default);
|
||||||
|
|
||||||
|
match line.rfind("\\$") {
|
||||||
|
Some(i) => {
|
||||||
|
desc.push_str(line[..i].trim_end());
|
||||||
|
desc.push(' ');
|
||||||
|
}
|
||||||
|
None => {
|
||||||
|
desc.push_str(&line);
|
||||||
|
desc.push('\n');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
@ -191,15 +191,18 @@ pub enum PermissionLevel {
|
|||||||
}
|
}
|
||||||
|
|
||||||
pub struct Command {
|
pub struct Command {
|
||||||
pub name: &'static str,
|
pub fun: CommandFn,
|
||||||
pub required_perms: PermissionLevel,
|
pub names: &'static [&'static str],
|
||||||
pub func: CommandFn,
|
pub desc: Option<&'static str>,
|
||||||
|
pub usage: Option<&'static str>,
|
||||||
|
pub examples: &'static [&'static str],
|
||||||
|
pub required_permissions: PermissionLevel,
|
||||||
pub allow_slash: bool,
|
pub allow_slash: bool,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Command {
|
impl Command {
|
||||||
async fn check_permissions(&self, ctx: &Context, guild: &Guild, member: &Member) -> bool {
|
async fn check_permissions(&self, ctx: &Context, guild: &Guild, member: &Member) -> bool {
|
||||||
if self.required_perms == PermissionLevel::Unrestricted {
|
if self.required_permissions == PermissionLevel::Unrestricted {
|
||||||
true
|
true
|
||||||
} else {
|
} else {
|
||||||
let permissions = guild.member_permissions(&ctx, &member.user).await.unwrap();
|
let permissions = guild.member_permissions(&ctx, &member.user).await.unwrap();
|
||||||
@ -208,7 +211,7 @@ impl Command {
|
|||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
if self.required_perms == PermissionLevel::Managed {
|
if self.required_permissions == PermissionLevel::Managed {
|
||||||
let pool = ctx
|
let pool = ctx
|
||||||
.data
|
.data
|
||||||
.read()
|
.read()
|
||||||
@ -262,8 +265,8 @@ SELECT role
|
|||||||
impl fmt::Debug for Command {
|
impl fmt::Debug for Command {
|
||||||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||||
f.debug_struct("Command")
|
f.debug_struct("Command")
|
||||||
.field("name", &self.name)
|
.field("name", &self.names[0])
|
||||||
.field("required_perms", &self.required_perms)
|
.field("required_permissions", &self.required_permissions)
|
||||||
.finish()
|
.finish()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -350,8 +353,10 @@ impl RegexFramework {
|
|||||||
self
|
self
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn add_command<S: ToString>(mut self, name: S, command: &'static Command) -> Self {
|
pub fn add_command(mut self, command: &'static Command) -> Self {
|
||||||
|
for name in command.names {
|
||||||
self.commands.insert(name.to_string(), command);
|
self.commands.insert(name.to_string(), command);
|
||||||
|
}
|
||||||
|
|
||||||
self
|
self
|
||||||
}
|
}
|
||||||
@ -448,16 +453,18 @@ impl Framework for RegexFramework {
|
|||||||
let member = guild.member(&ctx, &msg.author).await.unwrap();
|
let member = guild.member(&ctx, &msg.author).await.unwrap();
|
||||||
|
|
||||||
if command.check_permissions(&ctx, &guild, &member).await {
|
if command.check_permissions(&ctx, &guild, &member).await {
|
||||||
(command.func)(
|
(command.fun)(
|
||||||
&ctx,
|
&ctx,
|
||||||
&msg,
|
&msg,
|
||||||
Args::new(&args, &[Delimiter::Single(' ')]),
|
Args::new(&args, &[Delimiter::Single(' ')]),
|
||||||
)
|
)
|
||||||
.await
|
.await
|
||||||
.unwrap();
|
.unwrap();
|
||||||
} else if command.required_perms == 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;
|
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_perms == PermissionLevel::Restricted {
|
} else if command.required_permissions
|
||||||
|
== PermissionLevel::Restricted
|
||||||
|
{
|
||||||
let _ = msg
|
let _ = msg
|
||||||
.channel_id
|
.channel_id
|
||||||
.say(&ctx, "You must be an Admin to do this command")
|
.say(&ctx, "You must be an Admin to do this command")
|
||||||
|
100
src/main.rs
100
src/main.rs
@ -184,35 +184,33 @@ async fn main() -> Result<(), Box<dyn std::error::Error + Send + Sync>> {
|
|||||||
.case_insensitive(true)
|
.case_insensitive(true)
|
||||||
.ignore_bots(true)
|
.ignore_bots(true)
|
||||||
// info commands
|
// info commands
|
||||||
.add_command("help", &HELP_COMMAND)
|
.add_command(&HELP_COMMAND)
|
||||||
.add_command("info", &INFO_COMMAND)
|
.add_command(&INFO_COMMAND)
|
||||||
.add_command("invite", &INFO_COMMAND)
|
.add_command(&INFO_COMMAND)
|
||||||
.add_command("donate", &INFO_COMMAND)
|
.add_command(&INFO_COMMAND)
|
||||||
// play commands
|
// play commands
|
||||||
.add_command("loop", &LOOP_PLAY_COMMAND)
|
.add_command(&LOOP_PLAY_COMMAND)
|
||||||
.add_command("play", &PLAY_COMMAND)
|
.add_command(&PLAY_COMMAND)
|
||||||
.add_command("p", &PLAY_COMMAND)
|
.add_command(&STOP_PLAYING_COMMAND)
|
||||||
.add_command("stop", &STOP_PLAYING_COMMAND)
|
.add_command(&DISCONNECT_COMMAND)
|
||||||
.add_command("disconnect", &DISCONNECT_COMMAND)
|
|
||||||
.add_command("dc", &DISCONNECT_COMMAND)
|
|
||||||
// sound management commands
|
// sound management commands
|
||||||
.add_command("upload", &UPLOAD_NEW_SOUND_COMMAND)
|
.add_command(&UPLOAD_NEW_SOUND_COMMAND)
|
||||||
.add_command("delete", &DELETE_SOUND_COMMAND)
|
.add_command(&DELETE_SOUND_COMMAND)
|
||||||
.add_command("list", &LIST_SOUNDS_COMMAND)
|
.add_command(&LIST_SOUNDS_COMMAND)
|
||||||
.add_command("public", &CHANGE_PUBLIC_COMMAND)
|
.add_command(&CHANGE_PUBLIC_COMMAND)
|
||||||
// setting commands
|
// setting commands
|
||||||
.add_command("prefix", &CHANGE_PREFIX_COMMAND)
|
.add_command(&CHANGE_PREFIX_COMMAND)
|
||||||
.add_command("roles", &SET_ALLOWED_ROLES_COMMAND)
|
.add_command(&SET_ALLOWED_ROLES_COMMAND)
|
||||||
.add_command("volume", &CHANGE_VOLUME_COMMAND)
|
.add_command(&CHANGE_VOLUME_COMMAND)
|
||||||
.add_command("allow_greet", &ALLOW_GREET_SOUNDS_COMMAND)
|
.add_command(&ALLOW_GREET_SOUNDS_COMMAND)
|
||||||
.add_command("greet", &SET_GREET_SOUND_COMMAND)
|
.add_command(&SET_GREET_SOUND_COMMAND)
|
||||||
// search commands
|
// search commands
|
||||||
.add_command("search", &SEARCH_SOUNDS_COMMAND)
|
.add_command(&SEARCH_SOUNDS_COMMAND)
|
||||||
.add_command("popular", &SHOW_POPULAR_SOUNDS_COMMAND)
|
.add_command(&SHOW_POPULAR_SOUNDS_COMMAND)
|
||||||
.add_command("random", &SHOW_RANDOM_SOUNDS_COMMAND);
|
.add_command(&SHOW_RANDOM_SOUNDS_COMMAND);
|
||||||
|
|
||||||
if audio_index.is_some() {
|
if audio_index.is_some() {
|
||||||
framework = framework.add_command("ambience", &PLAY_AMBIENCE_COMMAND);
|
framework = framework.add_command(&PLAY_AMBIENCE_COMMAND);
|
||||||
}
|
}
|
||||||
|
|
||||||
framework = framework.build();
|
framework = framework.build();
|
||||||
@ -425,18 +423,14 @@ Please select a category from the following:
|
|||||||
}
|
}
|
||||||
|
|
||||||
#[command]
|
#[command]
|
||||||
#[permission_level(Managed)]
|
#[aliases("p")]
|
||||||
|
#[required_permissions(Managed)]
|
||||||
async fn play(
|
async fn play(
|
||||||
ctx: &Context,
|
ctx: &Context,
|
||||||
invoke: &(dyn CommandInvoke + Sync + Send),
|
invoke: &(dyn CommandInvoke + Sync + Send),
|
||||||
args: Args,
|
args: Args,
|
||||||
) -> CommandResult {
|
) -> CommandResult {
|
||||||
let guild = invoke
|
let guild = invoke.guild(ctx.cache.clone()).await.unwrap();
|
||||||
.guild_id()
|
|
||||||
.unwrap()
|
|
||||||
.to_guild_cached(&ctx)
|
|
||||||
.await
|
|
||||||
.unwrap();
|
|
||||||
|
|
||||||
invoke
|
invoke
|
||||||
.channel_id()
|
.channel_id()
|
||||||
@ -450,7 +444,7 @@ async fn play(
|
|||||||
}
|
}
|
||||||
|
|
||||||
#[command]
|
#[command]
|
||||||
#[permission_level(Managed)]
|
#[required_permissions(Managed)]
|
||||||
async fn loop_play(
|
async fn loop_play(
|
||||||
ctx: &Context,
|
ctx: &Context,
|
||||||
invoke: &(dyn CommandInvoke + Sync + Send),
|
invoke: &(dyn CommandInvoke + Sync + Send),
|
||||||
@ -528,8 +522,8 @@ async fn play_cmd(ctx: &Context, guild: Guild, user_id: UserId, args: Args, loop
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[command]
|
#[command("ambience")]
|
||||||
#[permission_level(Managed)]
|
#[required_permissions(Managed)]
|
||||||
async fn play_ambience(
|
async fn play_ambience(
|
||||||
ctx: &Context,
|
ctx: &Context,
|
||||||
invoke: &(dyn CommandInvoke + Sync + Send),
|
invoke: &(dyn CommandInvoke + Sync + Send),
|
||||||
@ -617,8 +611,8 @@ __Available ambience sounds:__
|
|||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
#[command]
|
#[command("stop")]
|
||||||
#[permission_level(Managed)]
|
#[required_permissions(Managed)]
|
||||||
async fn stop_playing(
|
async fn stop_playing(
|
||||||
ctx: &Context,
|
ctx: &Context,
|
||||||
invoke: &(dyn CommandInvoke + Sync + Send),
|
invoke: &(dyn CommandInvoke + Sync + Send),
|
||||||
@ -639,7 +633,8 @@ async fn stop_playing(
|
|||||||
}
|
}
|
||||||
|
|
||||||
#[command]
|
#[command]
|
||||||
#[permission_level(Managed)]
|
#[aliases("dc")]
|
||||||
|
#[required_permissions(Managed)]
|
||||||
async fn disconnect(
|
async fn disconnect(
|
||||||
ctx: &Context,
|
ctx: &Context,
|
||||||
invoke: &(dyn CommandInvoke + Sync + Send),
|
invoke: &(dyn CommandInvoke + Sync + Send),
|
||||||
@ -654,6 +649,7 @@ async fn disconnect(
|
|||||||
}
|
}
|
||||||
|
|
||||||
#[command]
|
#[command]
|
||||||
|
#[aliases("invite")]
|
||||||
async fn info(
|
async fn info(
|
||||||
ctx: &Context,
|
ctx: &Context,
|
||||||
invoke: &(dyn CommandInvoke + Sync + Send),
|
invoke: &(dyn CommandInvoke + Sync + Send),
|
||||||
@ -690,8 +686,8 @@ There is a maximum sound limit per user. This can be removed by subscribing at *
|
|||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
#[command]
|
#[command("volume")]
|
||||||
#[permission_level(Managed)]
|
#[required_permissions(Managed)]
|
||||||
async fn change_volume(
|
async fn change_volume(
|
||||||
ctx: &Context,
|
ctx: &Context,
|
||||||
invoke: &(dyn CommandInvoke + Sync + Send),
|
invoke: &(dyn CommandInvoke + Sync + Send),
|
||||||
@ -755,8 +751,8 @@ async fn change_volume(
|
|||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
#[command]
|
#[command("prefix")]
|
||||||
#[permission_level(Restricted)]
|
#[required_permissions(Restricted)]
|
||||||
async fn change_prefix(
|
async fn change_prefix(
|
||||||
ctx: &Context,
|
ctx: &Context,
|
||||||
invoke: &(dyn CommandInvoke + Sync + Send),
|
invoke: &(dyn CommandInvoke + Sync + Send),
|
||||||
@ -838,7 +834,7 @@ async fn change_prefix(
|
|||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
#[command]
|
#[command("upload")]
|
||||||
#[allow_slash(false)]
|
#[allow_slash(false)]
|
||||||
async fn upload_new_sound(
|
async fn upload_new_sound(
|
||||||
ctx: &Context,
|
ctx: &Context,
|
||||||
@ -971,8 +967,8 @@ async fn upload_new_sound(
|
|||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
#[command]
|
#[command("roles")]
|
||||||
#[permission_level(Restricted)]
|
#[required_permissions(Restricted)]
|
||||||
#[allow_slash(false)]
|
#[allow_slash(false)]
|
||||||
async fn set_allowed_roles(
|
async fn set_allowed_roles(
|
||||||
ctx: &Context,
|
ctx: &Context,
|
||||||
@ -1060,7 +1056,7 @@ INSERT INTO roles (guild_id, role)
|
|||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
#[command]
|
#[command("list")]
|
||||||
async fn list_sounds(
|
async fn list_sounds(
|
||||||
ctx: &Context,
|
ctx: &Context,
|
||||||
invoke: &(dyn CommandInvoke + Sync + Send),
|
invoke: &(dyn CommandInvoke + Sync + Send),
|
||||||
@ -1121,7 +1117,7 @@ async fn list_sounds(
|
|||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
#[command]
|
#[command("public")]
|
||||||
async fn change_public(
|
async fn change_public(
|
||||||
ctx: &Context,
|
ctx: &Context,
|
||||||
invoke: &(dyn CommandInvoke + Sync + Send),
|
invoke: &(dyn CommandInvoke + Sync + Send),
|
||||||
@ -1186,7 +1182,7 @@ async fn change_public(
|
|||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
#[command]
|
#[command("delete")]
|
||||||
async fn delete_sound(
|
async fn delete_sound(
|
||||||
ctx: &Context,
|
ctx: &Context,
|
||||||
invoke: &(dyn CommandInvoke + Sync + Send),
|
invoke: &(dyn CommandInvoke + Sync + Send),
|
||||||
@ -1290,7 +1286,7 @@ fn format_search_results(search_results: Vec<Sound>) -> CreateGenericResponse {
|
|||||||
CreateGenericResponse::new().embed(|e| e.title(title).fields(field_iter))
|
CreateGenericResponse::new().embed(|e| e.title(title).fields(field_iter))
|
||||||
}
|
}
|
||||||
|
|
||||||
#[command]
|
#[command("search")]
|
||||||
async fn search_sounds(
|
async fn search_sounds(
|
||||||
ctx: &Context,
|
ctx: &Context,
|
||||||
invoke: &(dyn CommandInvoke + Sync + Send),
|
invoke: &(dyn CommandInvoke + Sync + Send),
|
||||||
@ -1322,7 +1318,7 @@ async fn search_sounds(
|
|||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
#[command]
|
#[command("popular")]
|
||||||
async fn show_popular_sounds(
|
async fn show_popular_sounds(
|
||||||
ctx: &Context,
|
ctx: &Context,
|
||||||
invoke: &(dyn CommandInvoke + Sync + Send),
|
invoke: &(dyn CommandInvoke + Sync + Send),
|
||||||
@ -1356,7 +1352,7 @@ SELECT name, id, plays, public, server_id, uploader_id
|
|||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
#[command]
|
#[command("random")]
|
||||||
async fn show_random_sounds(
|
async fn show_random_sounds(
|
||||||
ctx: &Context,
|
ctx: &Context,
|
||||||
invoke: &(dyn CommandInvoke + Sync + Send),
|
invoke: &(dyn CommandInvoke + Sync + Send),
|
||||||
@ -1391,7 +1387,7 @@ SELECT name, id, plays, public, server_id, uploader_id
|
|||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
#[command]
|
#[command("greet")]
|
||||||
async fn set_greet_sound(
|
async fn set_greet_sound(
|
||||||
ctx: &Context,
|
ctx: &Context,
|
||||||
invoke: &(dyn CommandInvoke + Sync + Send),
|
invoke: &(dyn CommandInvoke + Sync + Send),
|
||||||
@ -1457,8 +1453,8 @@ async fn set_greet_sound(
|
|||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
#[command]
|
#[command("allow_greet")]
|
||||||
#[permission_level(Managed)]
|
#[required_permissions(Managed)]
|
||||||
async fn allow_greet_sounds(
|
async fn allow_greet_sounds(
|
||||||
ctx: &Context,
|
ctx: &Context,
|
||||||
invoke: &(dyn CommandInvoke + Sync + Send),
|
invoke: &(dyn CommandInvoke + Sync + Send),
|
||||||
|
Loading…
Reference in New Issue
Block a user