Add Command trait method documentation and other docstrings, make p.co and p.d commands be used from the middle of a message
This commit is contained in:
Generated
+1
-1
@@ -2013,7 +2013,7 @@ checksum = "df94ce210e5bc13cb6651479fa48d14f601d9858cfe0467f43ae157023b938d3"
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "payphone"
|
name = "payphone"
|
||||||
version = "4.4.42"
|
version = "4.4.43"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"anyhow",
|
"anyhow",
|
||||||
"async-trait",
|
"async-trait",
|
||||||
|
|||||||
+1
-1
@@ -1,6 +1,6 @@
|
|||||||
[package]
|
[package]
|
||||||
name = "payphone"
|
name = "payphone"
|
||||||
version = "4.4.42"
|
version = "4.4.43"
|
||||||
edition = "2024"
|
edition = "2024"
|
||||||
default-run = "payphone"
|
default-run = "payphone"
|
||||||
|
|
||||||
|
|||||||
+119
-13
@@ -19,6 +19,7 @@ pub mod messages;
|
|||||||
pub mod permissions;
|
pub mod permissions;
|
||||||
pub mod runner;
|
pub mod runner;
|
||||||
|
|
||||||
|
/// An error occurred during command execution.
|
||||||
#[derive(Debug, Error)]
|
#[derive(Debug, Error)]
|
||||||
pub enum CommandError {
|
pub enum CommandError {
|
||||||
#[error("A database query failed")]
|
#[error("A database query failed")]
|
||||||
@@ -46,30 +47,84 @@ pub enum CommandError {
|
|||||||
DatabaseOrDiscordError(#[from] DatabaseOrDiscordError),
|
DatabaseOrDiscordError(#[from] DatabaseOrDiscordError),
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Represents a bot command.
|
||||||
|
///
|
||||||
|
/// Only the [`Command::name()`] and the [`Command::execute()`] methods must be implemented. The
|
||||||
|
/// rest have default implementations. They can all be overriden to tweak the command's behavior
|
||||||
|
/// and display in the help command.
|
||||||
#[async_trait]
|
#[async_trait]
|
||||||
pub trait Command<T, E>: Send + Sync + Debug {
|
pub trait Command<T, E>: Send + Sync + Debug {
|
||||||
|
/// The command's name.
|
||||||
|
///
|
||||||
|
/// Returns:
|
||||||
|
/// [`&'static str`] - The command's name.
|
||||||
fn name(&self) -> &'static str;
|
fn name(&self) -> &'static str;
|
||||||
|
|
||||||
|
/// The command's aliases.
|
||||||
|
///
|
||||||
|
/// Returns:
|
||||||
|
/// [`&'static [&'static str]`] - The command's aliases.
|
||||||
fn aliases(&self) -> &'static [&'static str] {
|
fn aliases(&self) -> &'static [&'static str] {
|
||||||
&[]
|
&[]
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// The command's description, shown in the help command.
|
||||||
|
///
|
||||||
|
/// Returns:
|
||||||
|
/// [`&'static str`] - The command's description.
|
||||||
fn description(&self) -> &'static str {
|
fn description(&self) -> &'static str {
|
||||||
"A Payphone command."
|
"A Payphone command."
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// The command's arguments, shown in the help command.
|
||||||
|
///
|
||||||
|
/// This does not affect parsing in any way, as that's done inside [`Command::execute()`]. This
|
||||||
|
/// is for display in the help command only.
|
||||||
|
///
|
||||||
|
/// Returns:
|
||||||
|
/// [`&'static [Argument]`] - The command's arguments.
|
||||||
fn arguments(&self) -> &'static [Argument] {
|
fn arguments(&self) -> &'static [Argument] {
|
||||||
&[]
|
&[]
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Permissions that will be checked for before running the command.
|
||||||
|
///
|
||||||
|
/// If any of the permissions fail, the command will not be run and the permission's
|
||||||
|
/// [`Permission::on_handle_denied()`] will be called.
|
||||||
|
///
|
||||||
|
/// Returns:
|
||||||
|
/// [`Vec<Box<dyn Permission<T, E> + Send + Sync>>`] - A vector of the command's permissions.
|
||||||
fn permissions(&self) -> Vec<Box<dyn Permission<T, E> + Send + Sync + 'static>> {
|
fn permissions(&self) -> Vec<Box<dyn Permission<T, E> + Send + Sync + 'static>> {
|
||||||
vec![]
|
vec![]
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Whether the command should be shown in the help command.
|
||||||
|
///
|
||||||
|
/// This is useful for commands that are not meant to be used by users directly, like internal
|
||||||
|
/// commands or easter egg commands.
|
||||||
|
///
|
||||||
|
/// Returns:
|
||||||
|
/// [`bool`] - Whether the command should be shown in the help command.
|
||||||
fn show_in_help(&self, _rt: &Runtime<T, E>, _extras: &E) -> bool {
|
fn show_in_help(&self, _rt: &Runtime<T, E>, _extras: &E) -> bool {
|
||||||
true
|
true
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Whether the command only runs when the message starts with the command prefix.
|
||||||
|
///
|
||||||
|
/// When this is false, the command will be run when the command is found on any part of the
|
||||||
|
/// message. This is useful for about-group commands, like:
|
||||||
|
///
|
||||||
|
/// ```text
|
||||||
|
/// User: You can donate $6 at p.d to get unbanned.
|
||||||
|
/// Bot: https://ko-fi.com/payphonedev
|
||||||
|
/// ```
|
||||||
|
///
|
||||||
|
/// Returns:
|
||||||
|
/// [`bool`] - Whether the command only runs when the message starts with the command prefix.
|
||||||
|
fn only_runs_if_startswith(&self) -> bool {
|
||||||
|
true
|
||||||
|
}
|
||||||
|
|
||||||
/// Returns the command name + all its aliases.
|
/// Returns the command name + all its aliases.
|
||||||
///
|
///
|
||||||
/// Returns:
|
/// Returns:
|
||||||
@@ -80,35 +135,86 @@ pub trait Command<T, E>: Send + Sync + Debug {
|
|||||||
all
|
all
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Executes the command.
|
||||||
|
///
|
||||||
|
/// Arguments:
|
||||||
|
/// * `ctx` - The command context, containing information about the command invocation and
|
||||||
|
/// helpers for responding.
|
||||||
|
/// * `args` - The raw arguments passed to the command, without any parsing. This is the part
|
||||||
|
/// of the message after the command name.
|
||||||
|
///
|
||||||
|
/// Returns:
|
||||||
|
/// [`Result<(), CommandError>`] - Ok if the command executed successfully, or an error if
|
||||||
|
/// something went wrong during execution.
|
||||||
async fn execute(&self, ctx: CommandContext<'_, T, E>, args: &str) -> Result<(), CommandError>;
|
async fn execute(&self, ctx: CommandContext<'_, T, E>, args: &str) -> Result<(), CommandError>;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// The parts of a command found in a message, without processing.
|
||||||
|
///
|
||||||
|
/// This is not guaranteed to point to a valid command, as it only finds a prefix in a message and
|
||||||
|
/// parses it without checking for command existance nor argument validity.
|
||||||
pub struct CommandParts<'a> {
|
pub struct CommandParts<'a> {
|
||||||
|
/// The name of the command.
|
||||||
pub name: &'a str,
|
pub name: &'a str,
|
||||||
|
|
||||||
|
/// The arguments passed to the command, raw as a string.
|
||||||
pub args: &'a str,
|
pub args: &'a str,
|
||||||
|
|
||||||
|
/// The index in the start-trimmed message where the command was found.
|
||||||
|
///
|
||||||
|
/// For example, given the prefix is `p.`:
|
||||||
|
/// ```text
|
||||||
|
/// Donate $6 at p.d to get unbanned.
|
||||||
|
/// ^
|
||||||
|
/// index = 13
|
||||||
|
/// ```
|
||||||
|
pub start_index: usize,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<'a> CommandParts<'a> {
|
impl<'a> CommandParts<'a> {
|
||||||
fn new(name: &'a str, args: &'a str) -> Self {
|
/// Creates a new [`CommandParts`] struct.
|
||||||
Self { name, args }
|
///
|
||||||
|
/// Arguments:
|
||||||
|
/// * `name` - The name of the command.
|
||||||
|
/// * `args` - The arguments passed to the command, raw as a string.
|
||||||
|
/// * `start_index` - The index in the start-trimmed message where the command was found.
|
||||||
|
///
|
||||||
|
/// Returns:
|
||||||
|
/// [`CommandParts`] - The created struct.
|
||||||
|
fn new(name: &'a str, args: &'a str, start_index: usize) -> Self {
|
||||||
|
Self {
|
||||||
|
name,
|
||||||
|
args,
|
||||||
|
start_index,
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Parses a message content to get the command name and arguments.
|
||||||
|
///
|
||||||
|
/// Arguments:
|
||||||
|
/// * `prefix` - The prefix to look for in the message.
|
||||||
|
/// * `message` - The message content to parse.
|
||||||
|
///
|
||||||
|
/// Returns:
|
||||||
|
/// [`Option<CommandParts>`] - The parsed command parts, or None if the prefix was not found in the
|
||||||
|
/// message.
|
||||||
pub fn get_command_parts<'a>(prefix: &str, message: &'a str) -> Option<CommandParts<'a>> {
|
pub fn get_command_parts<'a>(prefix: &str, message: &'a str) -> Option<CommandParts<'a>> {
|
||||||
let message_trimmed_start = message.trim_ascii_start();
|
let message_trimmed_start = message.trim_ascii_start();
|
||||||
|
let message_trimmed_start_lowercase = message_trimmed_start.to_ascii_lowercase();
|
||||||
|
|
||||||
if message_trimmed_start
|
let prefix_lowercase = prefix.to_ascii_lowercase();
|
||||||
.to_ascii_lowercase()
|
|
||||||
.starts_with(&prefix.to_ascii_lowercase())
|
|
||||||
{
|
|
||||||
let message = message_trimmed_start
|
|
||||||
.split_at(prefix.len())
|
|
||||||
.1
|
|
||||||
.trim_ascii_start();
|
|
||||||
|
|
||||||
match message.split_once(" ") {
|
if let Some(index) = message_trimmed_start_lowercase.find(&prefix_lowercase) {
|
||||||
Some((name, args)) => Some(CommandParts::new(name, args)),
|
let command_text = &message_trimmed_start[index + prefix.len()..];
|
||||||
None => Some(CommandParts::new(message, "")),
|
|
||||||
|
match command_text.trim_ascii_start().split_once(" ") {
|
||||||
|
Some((name, args)) => Some(CommandParts::new(name, args, index)),
|
||||||
|
None => Some(CommandParts::new(
|
||||||
|
command_text.trim_ascii_start(),
|
||||||
|
"",
|
||||||
|
index,
|
||||||
|
)),
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
None
|
None
|
||||||
|
|||||||
@@ -40,6 +40,10 @@ where
|
|||||||
});
|
});
|
||||||
|
|
||||||
if let Some(command) = command {
|
if let Some(command) = command {
|
||||||
|
if command.only_runs_if_startswith() && parts.start_index != 0 {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
let extras = extras
|
let extras = extras
|
||||||
.get_extra(&event.author, &rt)
|
.get_extra(&event.author, &rt)
|
||||||
.await
|
.await
|
||||||
|
|||||||
@@ -92,6 +92,10 @@ impl Command<State, Extras> for CodeOfConductCommand {
|
|||||||
false
|
false
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn only_runs_if_startswith(&self) -> bool {
|
||||||
|
false
|
||||||
|
}
|
||||||
|
|
||||||
async fn execute(
|
async fn execute(
|
||||||
&self,
|
&self,
|
||||||
ctx: CommandContext<'_, State, Extras>,
|
ctx: CommandContext<'_, State, Extras>,
|
||||||
@@ -126,6 +130,10 @@ impl Command<State, Extras> for DonateCommand {
|
|||||||
false
|
false
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn only_runs_if_startswith(&self) -> bool {
|
||||||
|
false
|
||||||
|
}
|
||||||
|
|
||||||
async fn execute(
|
async fn execute(
|
||||||
&self,
|
&self,
|
||||||
ctx: CommandContext<'_, State, Extras>,
|
ctx: CommandContext<'_, State, Extras>,
|
||||||
|
|||||||
Reference in New Issue
Block a user