From 9d615fbd66a9102d02da028dd0478bb38c584c0d Mon Sep 17 00:00:00 2001 From: FliegendeWurst <2012gdwu@web.de> Date: Mon, 4 May 2020 17:36:19 +0200 Subject: [PATCH] Error handling --- src/main.rs | 240 +++++++++++++++++++++++----------------------------- 1 file changed, 104 insertions(+), 136 deletions(-) diff --git a/src/main.rs b/src/main.rs index 05bad59..88b0b0c 100644 --- a/src/main.rs +++ b/src/main.rs @@ -10,11 +10,8 @@ use telegram_bot::types::{EditMessageText, InlineKeyboardButton, InlineKeyboardM use thiserror::Error; use tokio::task; use url::Url; -use warp::http::StatusCode; -use warp::{Filter, Rejection, Reply}; use std::collections::HashMap; -use std::convert::Infallible; use std::env; use std::sync::Arc; use std::time::Duration; @@ -25,6 +22,17 @@ static API: Lazy> = Lazy::new(|| { Arc::new(Api::new(telegram_token)) }); +static TRILIUM_TOKEN: Lazy = Lazy::new(|| { + println!("Loading passwords.."); + let trilium_user = env::var("TRILIUM_USER").expect("TRILIUM_USER not set"); + let trilium_password = env::var("TRILIUM_PASSWORD").expect("TRILIUM_PASSWORD not set"); + let client = reqwest::blocking::Client::new(); + let resp: HashMap = client.post("http://localhost:9001/api/login/token") + .json(&hashmap!{ "username" => &trilium_user, "password" => &trilium_password }) + .send().unwrap().json().unwrap(); + resp["token"].clone() +}); + static OWNER: Lazy = Lazy::new(|| { println!("Loading configuration.."); UserId::new(env::var("TELEGRAM_USER_ID").expect("TELEGRAM_USER_ID not set").parse().expect("TELEGRAM_USER_ID not numeric")) @@ -36,28 +44,16 @@ static CLIENT: Lazy = Lazy::new(Client::new); async fn main() -> Result<(), Error> { &*OWNER; - println!("Loading passwords.."); - let trilium_user = env::var("TRILIUM_USER").expect("TRILIUM_USER not set"); - let trilium_password = env::var("TRILIUM_PASSWORD").expect("TRILIUM_PASSWORD not set"); - &*API; println!("Initializing Trilium API.."); // Trilium login: // curl 'http://localhost:9001/api/login/token' -H 'User-Agent: Mozilla/5.0 ..' -H 'Accept: application/json' -H 'Accept-Language: en' --compressed -H 'Content-Type: application/json' -H 'Origin: moz-extension://13bc3fd7-5cb0-4d48-b368-76e389fd7c5f' -H 'DNT: 1' -H 'Connection: keep-alive' --data '{"username":"username","password":"insert_password_here"}' // -> {"token":"icB3xohFDpkVt7YFpbTflUYC8pucmryVGpb1DFpd6ns="} - let resp: HashMap = CLIENT.post("http://localhost:9001/api/login/token") - .json(&hashmap!{ "username" => &trilium_user, "password" => &trilium_password }) - .send().await?.json().await?; - let trilium_token = &resp["token"]; - println!("Acquired token: {}", trilium_token); + println!("Acquired token: {}", *TRILIUM_TOKEN); println!("Init done!"); - task::spawn(async move { - start_server().await; - }); - task::spawn(async move { start_polling().await; }); @@ -70,99 +66,106 @@ async fn main() -> Result<(), Error> { // Fetch new updates via long poll method let mut stream = API.stream(); while let Some(update) = stream.next().await { - // If the received update contains a new message... if update.is_err() { - println!("Error: {:?}", update.err().unwrap()); + println!("Telegram error: {:?}", update.err().unwrap()); continue; } let update = update.unwrap(); - if let UpdateKind::Message(message) = update.kind { - let now = Local::now(); - println!("[{}-{:02}-{:02} {:02}:{:02}] Receiving msg {:?}", now.year(), now.month(), now.day(), now.hour(), now.minute(), message); - if message.from.id != *OWNER { - // don't handle message - continue; - } - if let MessageKind::Text { ref data, .. } = message.kind { - if data == "/remindme" { - let mut msg = SendMessage::new(*OWNER, "in 0m: new reminder"); - msg.reply_markup(get_keyboard()); - reminder_msg = API.send(msg).await?.to_message_id(); - reminder_text = "new reminder".to_owned(); - reminder_time = chrono::Duration::minutes(0); - reminder_start = Local::now(); - continue; - } else if !reminder_text.is_empty() { - reminder_text = data.to_owned(); - let mut edit = EditMessageText::new(*OWNER, reminder_msg, format!("in {}: {}", format_time(reminder_time), reminder_text)); - edit.reply_markup(get_keyboard()); - API.send(edit).await?; - continue; - } - let is_url = false; //Url::parse(&data).is_ok(); // TODO: read this data from the Telegram json data (utf16 idxes..) - let formatted_text = if is_url { - format!("", data, data) - } else { - format!("
  • {}
", data) - }; - let title = format!("{} found at {:02}:{:02}", if is_url { "URL" } else { "content" }, now.hour(), now.minute()); - create_text_note(&CLIENT, trilium_token, - &title, - &formatted_text - ).await?; - - // answer message - if is_url { - API.send(message.text_reply("URL saved :-)")).await?; - } else { - API.send(message.text_reply("Text saved :-)")).await?; - } - } - } else if let UpdateKind::CallbackQuery(cb) = update.kind { - match &*cb.data.unwrap_or_default() { - "10m_cb" => { - reminder_time = reminder_time.checked_add(&chrono::Duration::minutes(10)).unwrap(); - let mut edit = EditMessageText::new(*OWNER, reminder_msg, format!("in {}: {}", format_time(reminder_time), reminder_text)); - edit.reply_markup(get_keyboard()); - API.send(edit).await?; - }, - "1h_cb" => { - reminder_time = reminder_time.checked_add(&chrono::Duration::hours(1)).unwrap(); - let mut edit = EditMessageText::new(*OWNER, reminder_msg, format!("in {}: {}", format_time(reminder_time), reminder_text)); - edit.reply_markup(get_keyboard()); - API.send(edit).await?; - }, - "1d_cb" => { - reminder_time = reminder_time.checked_add(&chrono::Duration::days(1)).unwrap(); - let mut edit = EditMessageText::new(*OWNER, reminder_msg, format!("in {}: {}", format_time(reminder_time), reminder_text)); - edit.reply_markup(get_keyboard()); - API.send(edit).await?; - }, - "1w_cb" => { - reminder_time = reminder_time.checked_add(&chrono::Duration::days(7)).unwrap(); - let mut edit = EditMessageText::new(*OWNER, reminder_msg, format!("in {}: {}", format_time(reminder_time), reminder_text)); - edit.reply_markup(get_keyboard()); - API.send(edit).await?; - }, - "save_cb" => { - let remind_time = reminder_start + reminder_time; - CLIENT.get("http://localhost:9001/custom/new_reminder").form(&json!({ - "time": remind_time.to_rfc3339(), - "task": reminder_text - })).send().await?.text().await?; - API.send(SendMessage::new(*OWNER, "Reminder saved :-)")).await?; - reminder_text = String::new(); - }, - _ => {} - } - } else { - println!("{:?}", update.kind); + if let Err(e) = process_one(update, &mut reminder_msg, &mut reminder_text, &mut reminder_start, &mut reminder_time).await { + println!("Error: {}", e); } } Ok(()) } +async fn process_one(update: Update, reminder_msg: &mut MessageId, reminder_text: &mut String, reminder_start: &mut DateTime, reminder_time: &mut chrono::Duration) -> Result<(), Error> { + if let UpdateKind::Message(message) = update.kind { + let now = Local::now(); + + println!("[{}-{:02}-{:02} {:02}:{:02}] Receiving msg {:?}", now.year(), now.month(), now.day(), now.hour(), now.minute(), message); + if message.from.id != *OWNER { + // don't handle message + return Ok(()); + } + if let MessageKind::Text { ref data, .. } = message.kind { + if data == "/remindme" { + let mut msg = SendMessage::new(*OWNER, "in 0m: new reminder"); + msg.reply_markup(get_keyboard()); + *reminder_msg = API.send(msg).await?.to_message_id(); + *reminder_text = "new reminder".to_owned(); + *reminder_time = chrono::Duration::minutes(0); + *reminder_start = Local::now(); + return Ok(()); + } else if !reminder_text.is_empty() { + *reminder_text = data.to_owned(); + let mut edit = EditMessageText::new(*OWNER, *reminder_msg, format!("in {}: {}", format_time(*reminder_time), reminder_text)); + edit.reply_markup(get_keyboard()); + API.send(edit).await?; + return Ok(()); + } + let is_url = false; //Url::parse(&data).is_ok(); // TODO: read this data from the Telegram json data (utf16 idxes..) + let formatted_text = if is_url { + format!("", data, data) + } else { + format!("
  • {}
", data) + }; + let title = format!("{} found at {:02}:{:02}", if is_url { "URL" } else { "content" }, now.hour(), now.minute()); + create_text_note(&CLIENT, &*TRILIUM_TOKEN, + &title, + &formatted_text + ).await?; + + // answer message + if is_url { + API.send(message.text_reply("URL saved :-)")).await?; + } else { + API.send(message.text_reply("Text saved :-)")).await?; + } + } + } else if let UpdateKind::CallbackQuery(cb) = update.kind { + match &*cb.data.unwrap_or_default() { + "10m_cb" => { + *reminder_time = reminder_time.checked_add(&chrono::Duration::minutes(10)).unwrap(); + let mut edit = EditMessageText::new(*OWNER, *reminder_msg, format!("in {}: {}", format_time(*reminder_time), reminder_text)); + edit.reply_markup(get_keyboard()); + API.send(edit).await?; + }, + "1h_cb" => { + *reminder_time = reminder_time.checked_add(&chrono::Duration::hours(1)).unwrap(); + let mut edit = EditMessageText::new(*OWNER, *reminder_msg, format!("in {}: {}", format_time(*reminder_time), reminder_text)); + edit.reply_markup(get_keyboard()); + API.send(edit).await?; + }, + "1d_cb" => { + *reminder_time = reminder_time.checked_add(&chrono::Duration::days(1)).unwrap(); + let mut edit = EditMessageText::new(*OWNER, *reminder_msg, format!("in {}: {}", format_time(*reminder_time), reminder_text)); + edit.reply_markup(get_keyboard()); + API.send(edit).await?; + }, + "1w_cb" => { + *reminder_time = reminder_time.checked_add(&chrono::Duration::days(7)).unwrap(); + let mut edit = EditMessageText::new(*OWNER, *reminder_msg, format!("in {}: {}", format_time(*reminder_time), reminder_text)); + edit.reply_markup(get_keyboard()); + API.send(edit).await?; + }, + "save_cb" => { + let remind_time = *reminder_start + *reminder_time; + CLIENT.get("http://localhost:9001/custom/new_reminder").form(&json!({ + "time": remind_time.to_rfc3339(), + "task": *reminder_text + })).send().await?.text().await?; + API.send(SendMessage::new(*OWNER, "Reminder saved :-)")).await?; + *reminder_text = String::new(); + }, + _ => {} + } + } else { + println!("{:?}", update.kind); + } + Ok(()) +} + fn get_keyboard() -> InlineKeyboardMarkup { let mut keyboard = InlineKeyboardMarkup::new(); let key = InlineKeyboardButton::callback("10m", "10m_cb"); @@ -270,36 +273,9 @@ fn format_time(diff: chrono::Duration) -> String { } } -async fn start_server() { - let heartbeat = warp::get() - .and(warp::path("heartbeat")) - .map(|| { - println!("Trilium heartbeat received"); - "OK" - }); - let task_alert = warp::post() - .and(warp::path("task_alert")) - .and(warp::path::param::()) - .and(warp::body::json()) - .and_then(notify_owner) - .recover(handle_rejection); - let routes = heartbeat.or(task_alert); - - warp::serve(routes) - .run(([127, 0, 0, 1], 19517)) - .await; -} - -async fn notify_owner(note_id: String, task: Task) -> Result { - match notify_owner_impl("..", task).await { - Err(e) => Err(warp::reject::custom(e)), - x => x.map_err(|_| unreachable!()) - } -} - -async fn notify_owner_impl(time_left: &str, task: Task) -> Result { +async fn notify_owner_impl(time_left: &str, task: Task) -> Result<(), Error> { API.send(SendMessage::new(*OWNER, format!("{}: {}", time_left, task.title))).await?; - Ok("OK") + Ok(()) } #[derive(Deserialize, Debug)] @@ -348,11 +324,3 @@ pub enum Error { #[error("telegram error: {0}")] Telegram(#[from] telegram_bot::Error), } - -impl warp::reject::Reject for Error {} - -// This function receives a `Rejection` and tries to return a custom -// value, otherwise simply passes the rejection along. -async fn handle_rejection(err: Rejection) -> Result { - Ok(warp::reply::with_status(format!("{:?}", err), StatusCode::INTERNAL_SERVER_ERROR)) -}