mirror of
https://github.com/teloxide/teloxide.git
synced 2025-01-03 17:52:12 +01:00
Add CommandDescription[s]
Add two new types - `CommandDescription` and `CommandDescriptions`, make `BotCommands` return the latter.
This commit is contained in:
parent
b3b8073a12
commit
86cc3d782f
6 changed files with 156 additions and 30 deletions
|
@ -132,7 +132,7 @@ async fn action(
|
|||
) -> Result<(), Box<dyn Error + Send + Sync>> {
|
||||
match command {
|
||||
Command::Help => {
|
||||
bot.send_message(msg.chat.id, Command::descriptions()).await?;
|
||||
bot.send_message(msg.chat.id, Command::descriptions().to_string()).await?;
|
||||
}
|
||||
Command::Kick => kick_user(bot, msg).await?,
|
||||
Command::Ban { time, unit } => ban_user(bot, msg, calc_restrict_time(time, unit)).await?,
|
||||
|
|
|
@ -50,7 +50,7 @@ async fn message_handler(
|
|||
match BotCommands::parse(text, "buttons") {
|
||||
Ok(Command::Help) => {
|
||||
// Just send the description of all commands.
|
||||
bot.send_message(m.chat.id, Command::descriptions()).await?;
|
||||
bot.send_message(m.chat.id, Command::descriptions().to_string()).await?;
|
||||
}
|
||||
Ok(Command::Start) => {
|
||||
// Create a list of buttons and send them.
|
||||
|
|
|
@ -128,9 +128,15 @@ async fn simple_commands_handler(
|
|||
let text = match cmd {
|
||||
SimpleCommand::Help => {
|
||||
if msg.from().unwrap().id == cfg.bot_maintainer {
|
||||
format!("{}\n{}", SimpleCommand::descriptions(), MaintainerCommands::descriptions())
|
||||
format!(
|
||||
"{}\n\n{}",
|
||||
SimpleCommand::descriptions(),
|
||||
MaintainerCommands::descriptions()
|
||||
)
|
||||
} else if msg.chat.is_group() || msg.chat.is_supergroup() {
|
||||
SimpleCommand::descriptions().username("USERNAME_BOT").to_string()
|
||||
} else {
|
||||
SimpleCommand::descriptions()
|
||||
SimpleCommand::descriptions().to_string()
|
||||
}
|
||||
}
|
||||
SimpleCommand::Maintainer => {
|
||||
|
|
|
@ -19,7 +19,9 @@ async fn answer(
|
|||
command: Command,
|
||||
) -> Result<(), Box<dyn Error + Send + Sync>> {
|
||||
match command {
|
||||
Command::Help => bot.send_message(message.chat.id, Command::descriptions()).await?,
|
||||
Command::Help => {
|
||||
bot.send_message(message.chat.id, Command::descriptions().to_string()).await?
|
||||
}
|
||||
Command::Username(username) => {
|
||||
bot.send_message(message.chat.id, format!("Your username is @{}.", username)).await?
|
||||
}
|
||||
|
|
|
@ -46,13 +46,18 @@
|
|||
//!
|
||||
//! [examples/admin_bot]: https://github.com/teloxide/teloxide/blob/master/examples/admin_bot/
|
||||
|
||||
use core::fmt;
|
||||
use std::{
|
||||
borrow::Cow,
|
||||
error::Error,
|
||||
fmt::{Display, Formatter},
|
||||
fmt::{Display, Formatter, Write},
|
||||
};
|
||||
|
||||
use std::marker::PhantomData;
|
||||
use teloxide_core::types::BotCommand;
|
||||
use teloxide_core::{
|
||||
requests::{Request, Requester},
|
||||
types::{BotCommand, Me},
|
||||
};
|
||||
#[cfg(feature = "macros")]
|
||||
pub use teloxide_macros::BotCommands;
|
||||
|
||||
|
@ -217,7 +222,7 @@ pub trait BotCommands: Sized {
|
|||
|
||||
/// Returns descriptions of the commands suitable to be shown to the user
|
||||
/// (for example when `/help` command is used).
|
||||
fn descriptions() -> String;
|
||||
fn descriptions() -> CommandDescriptions<'static>;
|
||||
|
||||
/// Returns a vector of [`BotCommand`] that can be used with
|
||||
/// [`set_my_commands`].
|
||||
|
@ -265,29 +270,80 @@ pub enum ParseError {
|
|||
Custom(Box<dyn Error + Send + Sync + 'static>),
|
||||
}
|
||||
|
||||
impl Display for ParseError {
|
||||
fn fmt(&self, f: &mut Formatter<'_>) -> Result<(), std::fmt::Error> {
|
||||
match self {
|
||||
ParseError::TooFewArguments { expected, found, message } => write!(
|
||||
f,
|
||||
"Too few arguments (expected {}, found {}, message = '{}')",
|
||||
expected, found, message
|
||||
),
|
||||
ParseError::TooManyArguments { expected, found, message } => write!(
|
||||
f,
|
||||
"Too many arguments (expected {}, found {}, message = '{}')",
|
||||
expected, found, message
|
||||
),
|
||||
ParseError::IncorrectFormat(e) => write!(f, "Incorrect format of command args: {}", e),
|
||||
ParseError::UnknownCommand(e) => write!(f, "Unknown command: {}", e),
|
||||
ParseError::WrongBotName(n) => write!(f, "Wrong bot name: {}", n),
|
||||
ParseError::Custom(e) => write!(f, "{}", e),
|
||||
/// Command descriptions that can be shown to the user (e.g. as a part of
|
||||
/// `/help` message)
|
||||
///
|
||||
/// Most of the time you don't need to create this struct yourself as it's
|
||||
/// returned from [`BotCommands::descriptions`].
|
||||
#[derive(Debug, Clone)]
|
||||
pub struct CommandDescriptions<'a> {
|
||||
global_description: Option<&'a str>,
|
||||
descriptions: &'a [CommandDescription<'a>],
|
||||
bot_username: Option<Cow<'a, str>>,
|
||||
}
|
||||
|
||||
/// Description of a particular command, used in [`CommandDescriptions`].
|
||||
#[derive(Debug, Clone)]
|
||||
pub struct CommandDescription<'a> {
|
||||
/// Prefix of the command, usually `/`.
|
||||
pub prefix: &'a str,
|
||||
/// The command itself, e.g. `start`.
|
||||
pub command: &'a str,
|
||||
/// Human-readable description of the command.
|
||||
pub description: &'a str,
|
||||
}
|
||||
|
||||
impl<'a> CommandDescriptions<'a> {
|
||||
/// Creates new [`CommandDescriptions`] from a list of command descriptions.
|
||||
pub fn new(descriptions: &'a [CommandDescription<'a>]) -> Self {
|
||||
Self { global_description: None, descriptions, bot_username: None }
|
||||
}
|
||||
|
||||
/// Sets the global description of these commands.
|
||||
pub fn global_description(self, global_description: &'a str) -> Self {
|
||||
Self { global_description: Some(global_description), ..self }
|
||||
}
|
||||
|
||||
/// Sets the username of the bot.
|
||||
///
|
||||
/// After this method is called, returned instance of
|
||||
/// [`CommandDescriptions`] will append `@bot_username` to all commands.
|
||||
/// This is useful in groups, to disambiguate commands for different bots.
|
||||
///
|
||||
/// ## Examples
|
||||
///
|
||||
/// ```
|
||||
/// let descriptions = CommandDescriptions::new(&[
|
||||
/// CommandDescription { prefix: "/", command: "start", description: "start this bot" },
|
||||
/// CommandDescription { prefix: "/", command: "help", description: "show this message" },
|
||||
/// ]);
|
||||
///
|
||||
/// assert_eq!(descriptions.to_string(), "/start — start this bot\n/help — show this message");
|
||||
/// assert_eq!(
|
||||
/// descriptions.username("username_of_the_bot").to_string(),
|
||||
/// "/start@username_of_the_bot — start this bot\n/help@username_of_the_bot — show this \
|
||||
/// message"
|
||||
/// );
|
||||
/// ```
|
||||
pub fn username(self, bot_username: &'a str) -> Self {
|
||||
Self { bot_username: Some(Cow::Borrowed(bot_username)), ..self }
|
||||
}
|
||||
|
||||
/// Sets the username of the bot.
|
||||
///
|
||||
/// This is the same as [`username`], but uses `get_me` method of the bot to
|
||||
/// get the username.
|
||||
///
|
||||
/// [`username`]: self::CommandDescriptions::username
|
||||
pub async fn username_from_bot(self, bot: &impl Requester) -> CommandDescriptions<'a> {
|
||||
let Me { user, .. } = bot.get_me().send().await.expect("get_me failed");
|
||||
Self {
|
||||
bot_username: Some(Cow::Owned(user.username.expect("Bots must have usernames"))),
|
||||
..self
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl std::error::Error for ParseError {}
|
||||
|
||||
/// Parses a string into a command with args.
|
||||
///
|
||||
/// This function is just a shortcut for calling [`parse_command_with_prefix`]
|
||||
|
@ -364,6 +420,68 @@ where
|
|||
Some((command, words.collect()))
|
||||
}
|
||||
|
||||
impl Display for ParseError {
|
||||
fn fmt(&self, f: &mut Formatter<'_>) -> Result<(), std::fmt::Error> {
|
||||
match self {
|
||||
ParseError::TooFewArguments { expected, found, message } => write!(
|
||||
f,
|
||||
"Too few arguments (expected {}, found {}, message = '{}')",
|
||||
expected, found, message
|
||||
),
|
||||
ParseError::TooManyArguments { expected, found, message } => write!(
|
||||
f,
|
||||
"Too many arguments (expected {}, found {}, message = '{}')",
|
||||
expected, found, message
|
||||
),
|
||||
ParseError::IncorrectFormat(e) => write!(f, "Incorrect format of command args: {}", e),
|
||||
ParseError::UnknownCommand(e) => write!(f, "Unknown command: {}", e),
|
||||
ParseError::WrongBotName(n) => write!(f, "Wrong bot name: {}", n),
|
||||
ParseError::Custom(e) => write!(f, "{}", e),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl std::error::Error for ParseError {}
|
||||
|
||||
impl Display for CommandDescriptions<'_> {
|
||||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||
if let Some(global_description) = self.global_description {
|
||||
f.write_str(global_description)?;
|
||||
f.write_str("\n\n")?;
|
||||
}
|
||||
|
||||
let mut write = |&CommandDescription { prefix, command, description }, nls| {
|
||||
if nls {
|
||||
f.write_char('\n')?;
|
||||
}
|
||||
|
||||
f.write_str(prefix)?;
|
||||
f.write_str(command)?;
|
||||
|
||||
if let Some(username) = self.bot_username.as_deref() {
|
||||
f.write_char('@')?;
|
||||
f.write_str(username)?;
|
||||
}
|
||||
|
||||
if !description.is_empty() {
|
||||
f.write_str(" — ")?;
|
||||
f.write_str(description)?;
|
||||
}
|
||||
|
||||
fmt::Result::Ok(())
|
||||
};
|
||||
|
||||
if let Some(descr) = self.descriptions.first() {
|
||||
write(descr, false)?;
|
||||
for descr in &self.descriptions[1..] {
|
||||
write(descr, true)?;
|
||||
}
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
// The rest of tests are integrational due to problems with macro expansion in
|
||||
// unit tests.
|
||||
#[cfg(test)]
|
||||
|
|
|
@ -68,7 +68,7 @@ fn many_attributes() {
|
|||
}
|
||||
|
||||
assert_eq!(DefaultCommands::Start, DefaultCommands::parse("!start", "").unwrap());
|
||||
assert_eq!(DefaultCommands::descriptions(), "!start - desc\n/help\n");
|
||||
assert_eq!(DefaultCommands::descriptions().to_string(), "!start — desc\n/help");
|
||||
}
|
||||
|
||||
#[test]
|
||||
|
@ -84,7 +84,7 @@ fn global_attributes() {
|
|||
|
||||
assert_eq!(DefaultCommands::Start, DefaultCommands::parse("/start", "MyNameBot").unwrap());
|
||||
assert_eq!(DefaultCommands::Help, DefaultCommands::parse("!help", "MyNameBot").unwrap());
|
||||
assert_eq!(DefaultCommands::descriptions(), "Bot commands\n/start\n!help\n");
|
||||
assert_eq!(DefaultCommands::descriptions().to_string(), "Bot commands\n\n/start\n!help");
|
||||
}
|
||||
|
||||
#[test]
|
||||
|
@ -194,7 +194,7 @@ fn descriptions_off() {
|
|||
Help,
|
||||
}
|
||||
|
||||
assert_eq!(DefaultCommands::descriptions(), "/help\n".to_owned());
|
||||
assert_eq!(DefaultCommands::descriptions().to_string(), "/help".to_owned());
|
||||
}
|
||||
|
||||
#[test]
|
||||
|
|
Loading…
Reference in a new issue