mirror of
https://github.com/teloxide/teloxide.git
synced 2025-01-03 09:49:07 +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>> {
|
) -> Result<(), Box<dyn Error + Send + Sync>> {
|
||||||
match command {
|
match command {
|
||||||
Command::Help => {
|
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::Kick => kick_user(bot, msg).await?,
|
||||||
Command::Ban { time, unit } => ban_user(bot, msg, calc_restrict_time(time, unit)).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") {
|
match BotCommands::parse(text, "buttons") {
|
||||||
Ok(Command::Help) => {
|
Ok(Command::Help) => {
|
||||||
// Just send the description of all commands.
|
// 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) => {
|
Ok(Command::Start) => {
|
||||||
// Create a list of buttons and send them.
|
// Create a list of buttons and send them.
|
||||||
|
|
|
@ -128,9 +128,15 @@ async fn simple_commands_handler(
|
||||||
let text = match cmd {
|
let text = match cmd {
|
||||||
SimpleCommand::Help => {
|
SimpleCommand::Help => {
|
||||||
if msg.from().unwrap().id == cfg.bot_maintainer {
|
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 {
|
} else {
|
||||||
SimpleCommand::descriptions()
|
SimpleCommand::descriptions().to_string()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
SimpleCommand::Maintainer => {
|
SimpleCommand::Maintainer => {
|
||||||
|
|
|
@ -19,7 +19,9 @@ async fn answer(
|
||||||
command: Command,
|
command: Command,
|
||||||
) -> Result<(), Box<dyn Error + Send + Sync>> {
|
) -> Result<(), Box<dyn Error + Send + Sync>> {
|
||||||
match command {
|
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) => {
|
Command::Username(username) => {
|
||||||
bot.send_message(message.chat.id, format!("Your username is @{}.", username)).await?
|
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/
|
//! [examples/admin_bot]: https://github.com/teloxide/teloxide/blob/master/examples/admin_bot/
|
||||||
|
|
||||||
|
use core::fmt;
|
||||||
use std::{
|
use std::{
|
||||||
|
borrow::Cow,
|
||||||
error::Error,
|
error::Error,
|
||||||
fmt::{Display, Formatter},
|
fmt::{Display, Formatter, Write},
|
||||||
};
|
};
|
||||||
|
|
||||||
use std::marker::PhantomData;
|
use std::marker::PhantomData;
|
||||||
use teloxide_core::types::BotCommand;
|
use teloxide_core::{
|
||||||
|
requests::{Request, Requester},
|
||||||
|
types::{BotCommand, Me},
|
||||||
|
};
|
||||||
#[cfg(feature = "macros")]
|
#[cfg(feature = "macros")]
|
||||||
pub use teloxide_macros::BotCommands;
|
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
|
/// Returns descriptions of the commands suitable to be shown to the user
|
||||||
/// (for example when `/help` command is used).
|
/// (for example when `/help` command is used).
|
||||||
fn descriptions() -> String;
|
fn descriptions() -> CommandDescriptions<'static>;
|
||||||
|
|
||||||
/// Returns a vector of [`BotCommand`] that can be used with
|
/// Returns a vector of [`BotCommand`] that can be used with
|
||||||
/// [`set_my_commands`].
|
/// [`set_my_commands`].
|
||||||
|
@ -265,29 +270,80 @@ pub enum ParseError {
|
||||||
Custom(Box<dyn Error + Send + Sync + 'static>),
|
Custom(Box<dyn Error + Send + Sync + 'static>),
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Display for ParseError {
|
/// Command descriptions that can be shown to the user (e.g. as a part of
|
||||||
fn fmt(&self, f: &mut Formatter<'_>) -> Result<(), std::fmt::Error> {
|
/// `/help` message)
|
||||||
match self {
|
///
|
||||||
ParseError::TooFewArguments { expected, found, message } => write!(
|
/// Most of the time you don't need to create this struct yourself as it's
|
||||||
f,
|
/// returned from [`BotCommands::descriptions`].
|
||||||
"Too few arguments (expected {}, found {}, message = '{}')",
|
#[derive(Debug, Clone)]
|
||||||
expected, found, message
|
pub struct CommandDescriptions<'a> {
|
||||||
),
|
global_description: Option<&'a str>,
|
||||||
ParseError::TooManyArguments { expected, found, message } => write!(
|
descriptions: &'a [CommandDescription<'a>],
|
||||||
f,
|
bot_username: Option<Cow<'a, str>>,
|
||||||
"Too many arguments (expected {}, found {}, message = '{}')",
|
}
|
||||||
expected, found, message
|
|
||||||
),
|
/// Description of a particular command, used in [`CommandDescriptions`].
|
||||||
ParseError::IncorrectFormat(e) => write!(f, "Incorrect format of command args: {}", e),
|
#[derive(Debug, Clone)]
|
||||||
ParseError::UnknownCommand(e) => write!(f, "Unknown command: {}", e),
|
pub struct CommandDescription<'a> {
|
||||||
ParseError::WrongBotName(n) => write!(f, "Wrong bot name: {}", n),
|
/// Prefix of the command, usually `/`.
|
||||||
ParseError::Custom(e) => write!(f, "{}", e),
|
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.
|
/// Parses a string into a command with args.
|
||||||
///
|
///
|
||||||
/// This function is just a shortcut for calling [`parse_command_with_prefix`]
|
/// This function is just a shortcut for calling [`parse_command_with_prefix`]
|
||||||
|
@ -364,6 +420,68 @@ where
|
||||||
Some((command, words.collect()))
|
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
|
// The rest of tests are integrational due to problems with macro expansion in
|
||||||
// unit tests.
|
// unit tests.
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
|
|
|
@ -68,7 +68,7 @@ fn many_attributes() {
|
||||||
}
|
}
|
||||||
|
|
||||||
assert_eq!(DefaultCommands::Start, DefaultCommands::parse("!start", "").unwrap());
|
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]
|
#[test]
|
||||||
|
@ -84,7 +84,7 @@ fn global_attributes() {
|
||||||
|
|
||||||
assert_eq!(DefaultCommands::Start, DefaultCommands::parse("/start", "MyNameBot").unwrap());
|
assert_eq!(DefaultCommands::Start, DefaultCommands::parse("/start", "MyNameBot").unwrap());
|
||||||
assert_eq!(DefaultCommands::Help, DefaultCommands::parse("!help", "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]
|
#[test]
|
||||||
|
@ -194,7 +194,7 @@ fn descriptions_off() {
|
||||||
Help,
|
Help,
|
||||||
}
|
}
|
||||||
|
|
||||||
assert_eq!(DefaultCommands::descriptions(), "/help\n".to_owned());
|
assert_eq!(DefaultCommands::descriptions().to_string(), "/help".to_owned());
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
|
|
Loading…
Reference in a new issue