Skip to content

Commit

Permalink
add BotCommand enum (#270)
Browse files Browse the repository at this point in the history
* add BotCommand enum

* migrate more commands

* more commands

* migrate remaining commands

* fix warnings

* simplify

* fix tests

* update deps
  • Loading branch information
ayrat555 authored Oct 2, 2022
1 parent 0401c68 commit f9b69e0
Show file tree
Hide file tree
Showing 30 changed files with 769 additions and 818 deletions.
16 changes: 8 additions & 8 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

196 changes: 158 additions & 38 deletions src/bot/commands.rs
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
use crate::bot::telegram_client;
use crate::bot::telegram_client::Api;
use crate::config::Config;
use crate::db::feeds;
Expand All @@ -7,12 +8,35 @@ use crate::db::telegram::NewTelegramSubscription;
use crate::models::Feed;
use crate::models::TelegramSubscription;
use diesel::r2d2::ConnectionManager;
use diesel::r2d2::Pool;
use diesel::r2d2::PooledConnection;
use diesel::PgConnection;
use frankenstein::Chat;
use frankenstein::ChatType;
use frankenstein::Message;
use std::str::FromStr;

pub use get_filter::GetFilter;
pub use get_global_filter::GetGlobalFilter;
pub use get_global_template::GetGlobalTemplate;
pub use get_template::GetTemplate;
pub use get_timezone::GetTimezone;
pub use help::Help;
pub use info::Info;
pub use list_subscriptions::ListSubscriptions;
pub use remove_filter::RemoveFilter;
pub use remove_global_filter::RemoveGlobalFilter;
pub use remove_global_template::RemoveGlobalTemplate;
pub use remove_template::RemoveTemplate;
pub use set_content_fields::SetContentFields;
pub use set_filter::SetFilter;
pub use set_global_filter::SetGlobalFilter;
pub use set_global_template::SetGlobalTemplate;
pub use set_template::SetTemplate;
pub use set_timezone::SetTimezone;
pub use start::Start;
pub use subscribe::Subscribe;
pub use unknown_command::UnknownCommand;
pub use unsubscribe::Unsubscribe;

pub mod get_filter;
pub mod get_global_filter;
Expand Down Expand Up @@ -57,56 +81,152 @@ impl From<Chat> for NewTelegramChat {
}
}

pub enum BotCommand {
UnknownCommand(String),
Help,
Subscribe(String),
Unsubscribe(String),
ListSubscriptions,
Start,
SetTimezone(String),
GetTimezone,
SetFilter(String),
GetFilter(String),
RemoveFilter(String),
SetTemplate(String),
GetTemplate(String),
RemoveTemplate(String),
GetGlobalFilter,
SetGlobalFilter(String),
RemoveGlobalFilter,
GetGlobalTemplate,
SetGlobalTemplate(String),
RemoveGlobalTemplate,
Info,
SetContentFields(String),
}

impl FromStr for BotCommand {
type Err = ();

fn from_str(command: &str) -> Result<Self, Self::Err> {
let bot_command = if !command.starts_with('/') {
BotCommand::UnknownCommand(command.to_string())
} else if command.starts_with(Help::command()) {
BotCommand::Help
} else if command.starts_with(Subscribe::command()) {
let args = parse_args(Subscribe::command(), command);

BotCommand::Subscribe(args)
} else if command.starts_with(Unsubscribe::command()) {
let args = parse_args(Unsubscribe::command(), command);

BotCommand::Unsubscribe(args)
} else if command.starts_with(ListSubscriptions::command()) {
BotCommand::ListSubscriptions
} else if command.starts_with(Start::command()) {
BotCommand::Start
} else if command.starts_with(SetTimezone::command()) {
let args = parse_args(SetTimezone::command(), command);

BotCommand::SetTimezone(args)
} else if command.starts_with(GetTimezone::command()) {
BotCommand::GetTimezone
} else if command.starts_with(SetFilter::command()) {
let args = parse_args(SetFilter::command(), command);

BotCommand::SetFilter(args)
} else if command.starts_with(GetFilter::command()) {
let args = parse_args(GetFilter::command(), command);

BotCommand::GetFilter(args)
} else if command.starts_with(RemoveFilter::command()) {
let args = parse_args(RemoveFilter::command(), command);

BotCommand::RemoveFilter(args)
} else if command.starts_with(SetTemplate::command()) {
let args = parse_args(SetTemplate::command(), command);

BotCommand::SetTemplate(args)
} else if command.starts_with(GetTemplate::command()) {
let args = parse_args(GetTemplate::command(), command);

BotCommand::GetTemplate(args)
} else if command.starts_with(RemoveTemplate::command()) {
let args = parse_args(RemoveTemplate::command(), command);

BotCommand::RemoveTemplate(args)
} else if command.starts_with(SetGlobalFilter::command()) {
let args = parse_args(SetGlobalFilter::command(), command);

BotCommand::SetGlobalFilter(args)
} else if command.starts_with(RemoveGlobalTemplate::command()) {
BotCommand::RemoveGlobalTemplate
} else if command.starts_with(GetGlobalTemplate::command()) {
BotCommand::GetGlobalTemplate
} else if command.starts_with(SetGlobalTemplate::command()) {
let args = parse_args(SetGlobalTemplate::command(), command);

BotCommand::SetGlobalTemplate(args)
} else if command.starts_with(GetGlobalFilter::command()) {
BotCommand::GetGlobalFilter
} else if command.starts_with(RemoveGlobalFilter::command()) {
BotCommand::RemoveGlobalFilter
} else if command.starts_with(Info::command()) {
BotCommand::Info
} else if command.starts_with(SetContentFields::command()) {
let args = parse_args(SetContentFields::command(), command);

BotCommand::SetContentFields(args)
} else {
BotCommand::UnknownCommand(command.to_string())
};

Ok(bot_command)
}
}

fn parse_args(command: &str, command_with_args: &str) -> String {
let handle = Config::telegram_bot_handle();
let command_with_handle = format!("{}@{}", command, handle);

if command_with_args.starts_with(&command_with_handle) {
command_with_args
.replace(&command_with_handle, "")
.trim()
.to_string()
} else {
command_with_args.replace(command, "").trim().to_string()
}
}

pub trait Command {
fn response(
&self,
db_pool: Pool<ConnectionManager<PgConnection>>,
message: &Message,
api: &Api,
) -> String;
fn response(&self) -> String;

fn execute(&self, db_pool: Pool<ConnectionManager<PgConnection>>, api: Api, message: Message) {
fn execute(&self, message: &Message) {
info!(
"{:?} wrote: {}",
message.chat.id,
message.text.as_ref().unwrap()
);

let text = self.response(db_pool, &message, &api);

self.reply_to_message(api, message, text)
let text = self.response();
self.reply_to_message(message, text)
}

fn reply_to_message(&self, api: Api, message: Message, text: String) {
fn reply_to_message(&self, message: &Message, text: String) {
if let Err(error) =
api.reply_with_text_message(message.chat.id, text, Some(message.message_id))
self.api()
.reply_with_text_message(message.chat.id, text, Some(message.message_id))
{
error!("Failed to reply to update {:?} {:?}", error, message);
}
}

fn command(&self) -> &str;

fn parse_argument(&self, full_command: &str) -> String {
let command = self.command();
let handle = Config::telegram_bot_handle();
let command_with_handle = format!("{}@{}", command, handle);

if full_command.starts_with(&command_with_handle) {
full_command
.replace(&command_with_handle, "")
.trim()
.to_string()
} else {
full_command.replace(command, "").trim().to_string()
}
}

fn fetch_db_connection(
&self,
db_pool: Pool<ConnectionManager<PgConnection>>,
) -> Result<PooledConnection<ConnectionManager<PgConnection>>, String> {
match db_pool.get() {
match crate::db::pool().get() {
Ok(connection) => Ok(connection),
Err(err) => {
error!("Failed to fetch a connection from the pool {:?}", err);
Expand All @@ -116,11 +236,15 @@ pub trait Command {
}
}

fn api(&self) -> Api {
telegram_client::api().clone()
}

fn find_subscription(
&self,
db_connection: &mut PgConnection,
chat_id: i64,
feed_url: String,
feed_url: &str,
) -> Result<TelegramSubscription, String> {
let not_exists_error = Err("Subscription does not exist".to_string());
let feed = self.find_feed(db_connection, feed_url)?;
Expand All @@ -141,11 +265,7 @@ pub trait Command {
}
}

fn find_feed(
&self,
db_connection: &mut PgConnection,
feed_url: String,
) -> Result<Feed, String> {
fn find_feed(&self, db_connection: &mut PgConnection, feed_url: &str) -> Result<Feed, String> {
match feeds::find_by_link(db_connection, feed_url) {
Some(feed) => Ok(feed),
None => Err("Feed does not exist".to_string()),
Expand Down
43 changes: 14 additions & 29 deletions src/bot/commands/get_filter.rs
Original file line number Diff line number Diff line change
@@ -1,26 +1,23 @@
use super::Command;
use super::Message;
use crate::bot::telegram_client::Api;
use diesel::r2d2::ConnectionManager;
use diesel::r2d2::Pool;
use diesel::PgConnection;
use typed_builder::TypedBuilder;

static COMMAND: &str = "/get_filter";

pub struct GetFilter {}
#[derive(TypedBuilder)]
pub struct GetFilter {
message: Message,
args: String,
}

impl GetFilter {
pub fn execute(db_pool: Pool<ConnectionManager<PgConnection>>, api: Api, message: Message) {
Self {}.execute(db_pool, api, message);
pub fn run(&self) {
self.execute(&self.message);
}

fn get_filter(
&self,
db_connection: &mut PgConnection,
message: &Message,
feed_url: String,
) -> String {
match self.find_subscription(db_connection, message.chat.id, feed_url) {
fn get_filter(&self, db_connection: &mut PgConnection) -> String {
match self.find_subscription(db_connection, self.message.chat.id, &self.args) {
Err(message) => message,
Ok(subscription) => match subscription.filter_words {
None => "You did not set a filter for this subcription".to_string(),
Expand All @@ -35,23 +32,11 @@ impl GetFilter {
}

impl Command for GetFilter {
fn response(
&self,
db_pool: Pool<ConnectionManager<PgConnection>>,
message: &Message,
_api: &Api,
) -> String {
match self.fetch_db_connection(db_pool) {
Ok(mut connection) => {
let text = message.text.as_ref().unwrap();
let argument = self.parse_argument(text);
self.get_filter(&mut connection, message, argument)
}
fn response(&self) -> String {
match self.fetch_db_connection() {
Ok(mut connection) => self.get_filter(&mut connection),

Err(error_message) => error_message,
}
}

fn command(&self) -> &str {
Self::command()
}
}
Loading

0 comments on commit f9b69e0

Please sign in to comment.