Skip to content

Commit

Permalink
feat: impl option set_bot_commands which updates the Telegram-side …
Browse files Browse the repository at this point in the history
…bot's command list

- allows the user not to worry about their commands not being known.
  • Loading branch information
AndrielFR committed Jan 6, 2025
1 parent d3982c5 commit 1fbc2e8
Show file tree
Hide file tree
Showing 7 changed files with 104 additions and 9 deletions.
50 changes: 48 additions & 2 deletions lib/ferogram/src/client.rs
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,9 @@
use std::path::Path;

use grammers_client::{session::Session, Config, InitParams, ReconnectionPolicy, SignInError};
use grammers_client::{
grammers_tl_types as tl, session::Session, Config, InitParams, ReconnectionPolicy, SignInError,
};
use grammers_mtsender::ServerAddr;

use crate::{di, utils::prompt, Context, Dispatcher, ErrorHandler, Result};
Expand All @@ -29,6 +31,8 @@ pub struct Client {

/// Whether the client is connected.
is_connected: bool,
/// Whether is to update Telegram's bot commands.
set_bot_commands: bool,
/// Wheter is to wait for a `Ctrl + C` signal to close the connection and exit the app.
wait_for_ctrl_c: bool,

Expand Down Expand Up @@ -240,6 +244,35 @@ impl Client {
let err_handler = self.err_handler;
let ready_handler = self.ready_handler;

if self.set_bot_commands {
let mut commands = Vec::new();

let command_filters = dispatcher.get_commands();
for command_filter in command_filters.into_iter() {
let patterns = command_filter
.command
.split("|")
.filter(|pattern| pattern.len() > 1)
.collect::<Vec<_>>();
let description = command_filter.description;

for pattern in patterns.iter() {
commands.push(tl::enums::BotCommand::Command(tl::types::BotCommand {
command: pattern.to_string(),
description: description.to_string(),
}));
}
}

handle
.invoke(&tl::functions::bots::SetBotCommands {
scope: tl::enums::BotCommandScope::Default,
lang_code: "en".to_string(),
commands,
})
.await?;
}

let client = handle.clone();

tokio::task::spawn(async move {
Expand Down Expand Up @@ -332,7 +365,9 @@ pub struct ClientBuilder {
/// The initial parameters.
init_params: InitParams,

/// Wheter is to wait for a `Ctrl + C` signal to close the connection and exit the app.
/// Whether is to update Telegram's bot commands.
set_bot_commands: bool,
/// Whether is to wait for a `Ctrl + C` signal to close the connection and exit the app.
wait_for_ctrl_c: bool,

/// The global error handler.
Expand Down Expand Up @@ -409,6 +444,7 @@ impl ClientBuilder {
session_file: Some(session_file.to_string()),

is_connected: false,
set_bot_commands: self.set_bot_commands,
wait_for_ctrl_c: self.wait_for_ctrl_c,

err_handler: self.err_handler,
Expand Down Expand Up @@ -650,6 +686,16 @@ impl ClientBuilder {
self
}

/// Updates the Telegram-side bot's command list by collecting all the commands
/// from the dispatcher's handlers.
///
/// Only commands that has more than `1` char will be registered.
/// Ex: `start`, `help`...
pub fn set_bot_commands(mut self) -> Self {
self.set_bot_commands = true;
self
}

/// Sets the reconnection policy.
///
/// Executed when the client loses the connection or the Telegram server closes it.
Expand Down
16 changes: 15 additions & 1 deletion lib/ferogram/src/dispatcher.rs
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@
use grammers_client::{types::Chat, Client, Update};
use tokio::sync::broadcast::Sender;

use crate::{di, Context, Plugin, Result, Router};
use crate::{di, filters::Command, Context, Plugin, Result, Router};

/// A dispatcher.
///
Expand Down Expand Up @@ -123,6 +123,20 @@ impl Dispatcher {
self
}

/// Returns the commands from the routers and plugins.
pub(crate) fn get_commands(&self) -> Vec<Command> {
let mut commands = Vec::new();

commands.extend(self.routers.iter().flat_map(|router| router.get_commands()));
commands.extend(
self.plugins
.iter()
.flat_map(|plugin| plugin.router.get_commands()),
);

commands
}

/// Handle the update sent by Telegram.
///
/// Returns `Ok(())` if the update was handled.
Expand Down
10 changes: 9 additions & 1 deletion lib/ferogram/src/filter.rs
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@

//! Filters module.
use std::sync::Arc;
use std::{any::Any, sync::Arc};

use async_trait::async_trait;
use futures_util::Future;
Expand Down Expand Up @@ -54,6 +54,14 @@ pub trait Filter: CloneFilter + Send + Sync + 'static {
filter: Box::new(self),
}
}

/// Returns the filter as a `Any` trait object..
fn as_any(&self) -> &dyn Any
where
Self: Sized,
{
self
}
}

#[async_trait]
Expand Down
2 changes: 1 addition & 1 deletion lib/ferogram/src/filters/command.rs
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@ use tokio::sync::Mutex;

use crate::{Filter, Flow};

#[derive(Clone)]
#[derive(Clone, Debug)]
pub struct Command {
pub(crate) prefixes: Vec<String>,
pub(crate) command: String,
Expand Down
2 changes: 1 addition & 1 deletion lib/ferogram/src/filters/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@ mod or;
use std::sync::Arc;

pub(crate) use and::And;
use command::Command;
pub(crate) use command::Command;
use grammers_client::{
grammers_tl_types as tl,
types::{Chat, Media},
Expand Down
17 changes: 15 additions & 2 deletions lib/ferogram/src/handler.rs
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@
use grammers_client::{Client, Update};

use crate::{di, flow, ErrorHandler, Filter, Flow};
use crate::{di, filter::Command, flow, ErrorHandler, Filter, Flow};

/// A handler.
///
Expand All @@ -21,7 +21,9 @@ pub struct Handler {
update_type: UpdateType,

/// The filter.
filter: Option<Box<dyn Filter>>,
pub(crate) filter: Option<Box<dyn Filter>>,
/// The command.
pub(crate) command: Option<Command>,
/// The endpoint.
pub(crate) endpoint: Option<di::Endpoint>,
/// The error handler.
Expand All @@ -31,10 +33,13 @@ pub struct Handler {
impl Handler {
/// Creates a new [`HandlerType::NewMessage`] handler.
pub fn new_message<F: Filter>(filter: F) -> Self {
let command = filter.as_any().downcast_ref::<Command>().cloned();

Self {
update_type: UpdateType::NewMessage,

filter: Some(Box::new(filter)),
command,
endpoint: None,
err_handler: None,
}
Expand All @@ -46,17 +51,21 @@ impl Handler {
update_type: UpdateType::Raw,

filter: Some(Box::new(filter)),
command: None,
endpoint: None,
err_handler: None,
}
}

/// Creates a new [`HandlerType::MessageEdited`] handler.
pub fn message_edited<F: Filter>(filter: F) -> Self {
let command = filter.as_any().downcast_ref::<Command>().cloned();

Self {
update_type: UpdateType::MessageEdited,

filter: Some(Box::new(filter)),
command,
endpoint: None,
err_handler: None,
}
Expand All @@ -68,6 +77,7 @@ impl Handler {
update_type: UpdateType::MessageDeleted,

filter: Some(Box::new(filter)),
command: None,
endpoint: None,
err_handler: None,
}
Expand All @@ -79,6 +89,7 @@ impl Handler {
update_type: UpdateType::CallbackQuery,

filter: Some(Box::new(filter)),
command: None,
endpoint: None,
err_handler: None,
}
Expand All @@ -90,6 +101,7 @@ impl Handler {
update_type: UpdateType::InlineQuery,

filter: Some(Box::new(filter)),
command: None,
endpoint: None,
err_handler: None,
}
Expand Down Expand Up @@ -220,6 +232,7 @@ pub fn then<I, H: di::Handler>(endpoint: impl di::IntoHandler<I, Handler = H>) -
update_type: UpdateType::Raw,

filter: None,
command: None,
endpoint: Some(Box::new(endpoint.into_handler())),
err_handler: None,
}
Expand Down
16 changes: 15 additions & 1 deletion lib/ferogram/src/router.rs
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@
use async_recursion::async_recursion;
use grammers_client::Update;

use crate::{di::Injector, Handler, Result};
use crate::{di::Injector, filter::Command, Handler, Result};

/// A router.
///
Expand Down Expand Up @@ -58,6 +58,20 @@ impl Router {
self
}

/// Returns the commands from the handlers.
pub(crate) fn get_commands(&self) -> Vec<Command> {
let mut commands = Vec::new();

commands.extend(
self.handlers
.iter()
.filter_map(|handler| handler.command.clone()),
);
commands.extend(self.routers.iter().flat_map(|router| router.get_commands()));

commands
}

/// Handle the update sent by Telegram.
///
/// Returns `Ok(())` if the update was handled.
Expand Down

0 comments on commit 1fbc2e8

Please sign in to comment.