Added docker building.

This commit is contained in:
2023-02-21 19:56:36 +00:00
parent f5b43573c8
commit b6ab9c8888
31 changed files with 1476 additions and 75 deletions

View File

@ -8,19 +8,30 @@ use tokio::sync::RwLock;
use super::{
filters::{
chain::FilteredHandler,
filtered_handler::FilteredHandler,
message_fitlers::{
ExcludedChatsFilter, MessageDirection, MessageDirectionFilter, TextFilter,
TextMatchMethod,
ExcludedChatsFilter, MessageDirection, MessageDirectionFilter, SilentFilter,
TextFilter, TextMatchMethod,
},
},
handlers::{
basic::{get_chat_id::GetChatId, help::Help},
basic::{
currency_converter::{CurrencyConverter, CurrencyTextFilter},
get_chat_id::GetChatId,
help::Help,
},
fun::{blyaficator::Blyaficator, greeter::Greeter},
Handler,
},
};
/// Authorization function.
///
/// This function asks for login code and
/// waits for it to become available.
///
/// Also it validates two-factor authentication
/// password if it was supplied.
async fn authorize(
args: &BotConfig,
client: &Client,
@ -32,18 +43,19 @@ async fn authorize(
.await?;
let mut code = None;
// Check for code to becom available every second.
while code.is_none() {
tokio::time::sleep(Duration::from_secs(1)).await;
{
code = web_code.read().await.clone();
}
}
// Acutal signing in.
let signed_in = client.sign_in(&token, &code.unwrap()).await;
match signed_in {
// If signing i
Err(SignInError::PasswordRequired(password_token)) => {
// Note: this `prompt` method will echo the password in the console.
// Real code might want to use a better way to handle this.
// If the password was not supplied, we use the hint in panic.
let hint = password_token.hint().unwrap_or("None");
let password = args
.tfa_password
@ -60,48 +72,82 @@ async fn authorize(
Ok(())
}
/// This little function is used to execute handlers on updates and print errors
/// if something bad happens.
///
/// The reason, I created a separate function is simple. I spawn every handler as a
/// separate task and I don't care if fails.
async fn handle_with_log(handler: Box<dyn Handler>, client: Client, update_data: Update) {
if let Err(err) = handler.react(&client, &update_data).await {
log::error!("{err}");
}
}
async fn run(args: BotConfig, client: Client) {
/// Acutal logic on handling updates.
///
/// This function handles every update we get from telegram
/// and spawns correcsponding handlers.
///
/// Also, every available handler is defined here.
async fn run(args: BotConfig, client: Client) -> anyhow::Result<()> {
let handlers: Vec<FilteredHandler> = vec![
// Printing help.
FilteredHandler::new(Help).add_filter(TextFilter(&[".h"], TextMatchMethod::IMatches)),
// Greeting my fellow humans.
FilteredHandler::new(Greeter)
.add_filter(SilentFilter)
.add_filter(MessageDirectionFilter(MessageDirection::Incoming))
.add_filter(TextFilter(&["привет"], TextMatchMethod::IStartsWith))
.add_filter(ExcludedChatsFilter(args.excluded_chats)),
FilteredHandler::new(Help).add_filter(TextFilter(&[".h"], TextMatchMethod::IMatches)),
// Getting chat id.
FilteredHandler::new(GetChatId)
.add_filter(TextFilter(&[".cid"], TextMatchMethod::IMatches)),
// Make бля fun again.
FilteredHandler::new(Blyaficator)
.add_filter(TextFilter(&[".bl"], TextMatchMethod::IStartsWith)),
// Handler for converting currecies.
FilteredHandler::new(CurrencyConverter::new()?)
.add_filter(SilentFilter)
.add_filter(ExcludedChatsFilter(args.currency_excluded_chats))
.add_filter(CurrencyTextFilter),
];
loop {
// Get new update
let update = client.next_update().await;
if update.is_err() {
log::error!("{}", update.unwrap_err());
break;
}
if let Some(update_data) = update.unwrap() {
let update_ref = &update_data;
let matched_handlers = handlers
.par_iter()
.filter(move |val| val.check(update_ref))
.collect::<Vec<_>>();
for handler in matched_handlers {
tokio::spawn(handle_with_log(
handler.handler.clone(),
client.clone(),
update_data.clone(),
));
}
// We get update if there's no error
let Some(update_data) = update.ok().and_then(|inner|inner) else{
log::warn!("Empty update is found.");
continue;
};
// A reference to update, so we can easily move it.
let update_ref = &update_data;
let filtered = handlers
// A parralel iterator over matchers.
.par_iter()
// Here we get all handlers that match filters.
.filter(move |val| val.check(update_ref))
// For each matched handler we spawn a new task.
.collect::<Vec<_>>();
for handler in filtered {
tokio::spawn(handle_with_log(
handler.handler.clone(),
client.clone(),
update_data.clone(),
));
}
}
Ok(())
}
/// The main entrypoint for bot.
///
/// This function starts bot, performs login and
/// starts endless loop.
pub async fn start(args: BotConfig, web_code: Arc<RwLock<Option<String>>>) -> anyhow::Result<()> {
log::info!("Connecting to Telegram...");
let client = Client::connect(Config {
@ -115,17 +161,20 @@ pub async fn start(args: BotConfig, web_code: Arc<RwLock<Option<String>>>) -> an
},
})
.await?;
log::info!("Connected!");
if client.is_authorized().await? {
// If we already authrized, we write random token, so web won't update it.
let mut code_writer = web_code.write().await;
*code_writer = Some(String::new());
} else {
// If we don't have token, wait for it.
authorize(&args, &client, web_code).await?;
client.session().save_to_file(args.session_file.as_str())?;
}
run(args.clone(), client).await;
run(args.clone(), client).await?;
Ok(())
}