bhcli

"Strange's fork of n0tr1v's bhcli (onion)"
git clone https://git.dasho.dev/Strange/bhcli.git
Log | Files | Refs | README

commit 3c206e92f19e0e32cef7e1bf7bcc05da4fcd46c2
parent 0c8c32dd422262823632c96d5932145738c90edb
Author: Strange <StrangeGuy6228@protonmail.com>
Date:   Fri, 22 Mar 2024 18:42:39 +0530

added d and D for downlaoding and downloading_and_view ing using curl and xdg_open [linux] + make linux will build --release and cp to /opt [requires sudo]

Diffstat:
MMakefile | 8+++++++-
Msrc/main.rs | 981++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++-------------------
2 files changed, 756 insertions(+), 233 deletions(-)

diff --git a/Makefile b/Makefile @@ -33,5 +33,11 @@ rsync: rsync --recursive --times --compress --progress dist/ dkf:/home/dkf/dist/downloads-bhcli deploy: build-darwin cross-compile-windows build-linux rsync + +linux: + cargo build --release + @echo "Copying binary to /opt requires sudo privileges." + @echo "Ensure /opt is added to your \$$PATH variable to use bhcli globally from any path." + sudo cp target/release/bhcli /opt -.PHONY: build-darwin process-windows cross-compile-windows rsync +.PHONY: build-darwin process-windows cross-compile-windows rsync linux diff --git a/src/main.rs b/src/main.rs @@ -1,12 +1,9 @@ -mod util; -mod lechatphp; mod bhc; +mod lechatphp; +mod util; +use crate::lechatphp::LoginErr; use anyhow::{anyhow, Context}; -use std::process; -use log::LevelFilter; -use log4rs::append::file::FileAppender; -use log4rs::encode::pattern::PatternEncoder; use chrono::{DateTime, Datelike, NaiveDateTime, Utc}; use clap::Parser; use clipboard::ClipboardContext; @@ -21,13 +18,18 @@ use crossterm::{ execute, terminal::{disable_raw_mode, enable_raw_mode, EnterAlternateScreen, LeaveAlternateScreen}, }; +use http::StatusCode; use lazy_static::lazy_static; use linkify::LinkFinder; +use log::LevelFilter; +use log4rs::append::file::FileAppender; +use log4rs::encode::pattern::PatternEncoder; use rand::distributions::Alphanumeric; use rand::{thread_rng, Rng}; use regex::Regex; use reqwest::blocking::multipart; use reqwest::blocking::Client; +use reqwest::redirect::Policy; use rodio::{source::Source, Decoder, OutputStream}; use select::document::Document; use select::predicate::{Attr, Name}; @@ -35,13 +37,13 @@ use serde_derive::{Deserialize, Serialize}; use std::collections::HashMap; use std::io::Cursor; use std::io::{self, Write}; -use std::sync::{Arc, MutexGuard}; +use std::process; +use std::process::Command; use std::sync::Mutex; +use std::sync::{Arc, MutexGuard}; use std::thread; use std::time::Duration; use std::time::Instant; -use http::StatusCode; -use reqwest::redirect::Policy; use tui::layout::Rect; use tui::style::Color as tuiColor; use tui::{ @@ -54,7 +56,6 @@ use tui::{ }; use unicode_width::UnicodeWidthStr; use util::StatefulList; -use crate::lechatphp::LoginErr; const LANG: &str = "en"; const SEND_TO_ALL: &str = "s *"; @@ -65,7 +66,7 @@ const SOUND1: &[u8] = include_bytes!("sound1.mp3"); const DKF_URL: &str = "http://dkforestseeaaq2dqz2uflmlsybvnq2irzn4ygyvu53oazyorednviid.onion"; const BHCLI_BLOG_URL: &str = "http://dkforestseeaaq2dqz2uflmlsybvnq2irzn4ygyvu53oazyorednviid.onion/bhcli"; -const BAN_IMPOSTERS: bool = true; +// const BAN_IMPOSTERS: bool = true; const SERVER_DOWN_500_ERR: &str = "500 Internal Server Error, server down"; const SERVER_DOWN_ERR: &str = "502 Bad Gateway, server down"; const KICKED_ERR: &str = "You have been kicked"; @@ -77,7 +78,7 @@ const CAPTCHA_USED_ERR: &str = "Captcha already used or timed out"; const UNKNOWN_ERR: &str = "Unknown error"; const N0TR1V: &str = "n0tr1v"; const STUXNET: &str = "STUXNET"; -const FAGGOT: &str = "faggot"; +// const FAGGOT: &str = "faggot"; const DNMX_URL: &str = "http://hxuzjtocnzvv5g2rtg2bhwkcbupmk7rclb6lly3fo4tvqkk5oyrv3nid.onion"; lazy_static! { @@ -221,7 +222,7 @@ struct LeChatPHPClient { is_muted: Arc<Mutex<bool>>, show_sys: bool, display_guest_view: bool, - display_member_view: bool, + display_member_view: bool, display_hidden_msgs: bool, tx: crossbeam_channel::Sender<PostType>, rx: Arc<Mutex<crossbeam_channel::Receiver<PostType>>>, @@ -236,30 +237,31 @@ impl LeChatPHPClient { let mut attempt = 0; loop { match self.login() { - Err(e) => { - match e { - LoginErr::KickedErr | LoginErr::RegErr | LoginErr::NicknameErr | LoginErr::UnknownErr => { - log::error!("{}", e); + Err(e) => match e { + LoginErr::KickedErr + | LoginErr::RegErr + | LoginErr::NicknameErr + | LoginErr::UnknownErr => { + log::error!("{}", e); + break; + } + LoginErr::CaptchaFailedSolveErr => { + log::error!("{}", e); + continue; + } + LoginErr::CaptchaWgErr | LoginErr::CaptchaUsedErr => {} + LoginErr::ServerDownErr | LoginErr::ServerDown500Err => { + log::error!("{}", e); + } + LoginErr::Reqwest(err) => { + if err.is_connect() { + log::error!("{}\nIs tor proxy enabled ?", err); break; - }, - LoginErr::CaptchaFailedSolveErr => { - log::error!("{}", e); - continue; - }, - LoginErr::CaptchaWgErr | LoginErr::CaptchaUsedErr => {}, - LoginErr::ServerDownErr | LoginErr::ServerDown500Err => { - log::error!("{}", e); - }, - LoginErr::Reqwest(err) => { - if err.is_connect() { - log::error!("{}\nIs tor proxy enabled ?", err); - break; - } else if err.is_timeout() { - log::error!("timeout: {}", err); - } else { - log::error!("{}", err); - } - }, + } else if err.is_timeout() { + log::error!("timeout: {}", err); + } else { + log::error!("{}", err); + } } }, Ok(()) => { @@ -269,7 +271,7 @@ impl LeChatPHPClient { Ok(ExitSignal::Terminate) => return, Err(e) => log::error!("{:?}", e), } - }, + } } attempt += 1; if max_retry > 0 && attempt > max_retry { @@ -295,7 +297,8 @@ impl LeChatPHPClient { let send_to = self.config.keepalive_send_to.clone(); thread::spawn(move || loop { let clb = || { - tx.send(PostType::Post("<keepalive>".to_owned(), send_to.clone())).unwrap(); + tx.send(PostType::Post("<keepalive>".to_owned(), send_to.clone())) + .unwrap(); tx.send(PostType::DeleteLast).unwrap(); }; let timeout = after(Duration::from_secs(60 * 75)); @@ -323,11 +326,16 @@ impl LeChatPHPClient { let url = format!("{}?action=post&session={}", &full_url, &session); thread::spawn(move || loop { // select! macro fucks all the LSP, therefore the code gymnastic here - let clb = |v: Result<PostType, crossbeam_channel::RecvError>| { - match v { - Ok(post_type_recv) => post_msg(&client, post_type_recv, &full_url, session.clone(), &url, &last_post_tx), - Err(_) => return, - } + let clb = |v: Result<PostType, crossbeam_channel::RecvError>| match v { + Ok(post_type_recv) => post_msg( + &client, + post_type_recv, + &full_url, + session.clone(), + &url, + &last_post_tx, + ), + Err(_) => return, }; let rx = rx.lock().unwrap(); select! { @@ -364,8 +372,21 @@ impl LeChatPHPClient { let source = Decoder::new_mp3(Cursor::new(SOUND1)).unwrap(); let mut should_notify = false; - if let Err(err) = get_msgs(&client, &base_url, &page_php, &session, &username, &tx, &users, &sig, - &messages_updated_tx, &members_tag, &datetime_fmt, &messages, &mut should_notify) { + if let Err(err) = get_msgs( + &client, + &base_url, + &page_php, + &session, + &username, + &tx, + &users, + &sig, + &messages_updated_tx, + &members_tag, + &datetime_fmt, + &messages, + &mut should_notify, + ) { log::error!("{}", err); }; @@ -481,13 +502,11 @@ impl LeChatPHPClient { if self.chat_type == ClientType::BHC { //let mut resp_txt = self.client.get(&self.config.url).send().unwrap().text().unwrap(); let mut resp_txt = match self.client.get(&self.config.url).send() { - Ok(response) => { - match response.text() { - Ok(text) => text, - Err(err) => { - println!("Error: {}\nCheck Your Tor Connection", err); - process::exit(0); - } + Ok(response) => match response.text() { + Ok(text) => text, + Err(err) => { + println!("Error: {}\nCheck Your Tor Connection", err); + process::exit(0); } }, Err(err) => { @@ -497,17 +516,52 @@ impl LeChatPHPClient { }; let doc = Document::from(resp_txt.as_str()); if let Some(meta) = doc.find(Name("meta")).next() { - let meta_content = meta.attr("content").context("meta content not found").unwrap().to_owned(); + let meta_content = meta + .attr("content") + .context("meta content not found") + .unwrap() + .to_owned(); let index_url = META_REFRESH_RGX.captures(&meta_content).unwrap()[1].to_owned(); - resp_txt = self.client.get(format!("{}/{}", &self.config.url, index_url)).send().unwrap().text().unwrap(); + resp_txt = self + .client + .get(format!("{}/{}", &self.config.url, index_url)) + .send() + .unwrap() + .text() + .unwrap(); } let doc = Document::from(resp_txt.as_str()); - let form_action = doc.find(Name("form")).next().unwrap().attr("action").context("form action not found").unwrap().to_owned(); - let captcha_url = doc.find(Name("img")).next().unwrap().attr("src").context("img src not found").unwrap().to_owned(); + let form_action = doc + .find(Name("form")) + .next() + .unwrap() + .attr("action") + .context("form action not found") + .unwrap() + .to_owned(); + let captcha_url = doc + .find(Name("img")) + .next() + .unwrap() + .attr("src") + .context("img src not found") + .unwrap() + .to_owned(); - let captcha_bytes = self.client.get(format!("{}/{}", &self.config.url, captcha_url)).send().unwrap().bytes().unwrap(); + let captcha_bytes = self + .client + .get(format!("{}/{}", &self.config.url, captcha_url)) + .send() + .unwrap() + .bytes() + .unwrap(); let img = image::load_from_memory(&captcha_bytes).unwrap(); - let img_buf = image::imageops::resize(&img, img.width(), img.height(), image::imageops::FilterType::Nearest); + let img_buf = image::imageops::resize( + &img, + img.width(), + img.height(), + image::imageops::FilterType::Nearest, + ); // Save captcha as file on disk img_buf.save("captcha.gif").unwrap(); termage::display_image("captcha.gif", img.width(), img.height()); @@ -517,22 +571,46 @@ impl LeChatPHPClient { io::stdout().flush().unwrap(); io::stdin().read_line(&mut captcha_input).unwrap(); trim_newline(&mut captcha_input); - let params = vec![("input", captcha_input), ("submit", "Enter+Chat".to_owned())]; - let resp = self.client.post(format!("{}/{}", &self.config.url, form_action)).form(&params).send().unwrap(); + let params = vec![ + ("input", captcha_input), + ("submit", "Enter+Chat".to_owned()), + ]; + let resp = self + .client + .post(format!("{}/{}", &self.config.url, form_action)) + .form(&params) + .send() + .unwrap(); if resp.status() != StatusCode::SEE_OTHER { return Err(LoginErr::CaptchaWgErr); } - self.config.page_php = resp.headers().get("location").map(|v| v.to_str().unwrap()).unwrap_or(&self.config.page_php).to_string(); + self.config.page_php = resp + .headers() + .get("location") + .map(|v| v.to_str().unwrap()) + .unwrap_or(&self.config.page_php) + .to_string(); } self.session = Some(lechatphp::login( - &self.client, &self.config.url, &self.config.page_php, &self.base_client.username, - &self.base_client.password, &self.guest_color, self.manual_captcha)?); + &self.client, + &self.config.url, + &self.config.page_php, + &self.base_client.username, + &self.base_client.password, + &self.guest_color, + self.manual_captcha, + )?); Ok(()) } fn logout(&mut self) -> anyhow::Result<()> { if let Some(session) = &self.session { - lechatphp::logout(&self.client, &self.config.url, &self.config.page_php, session)?; + lechatphp::logout( + &self.client, + &self.config.url, + &self.config.page_php, + session, + )?; self.session = None; } Ok(()) @@ -573,106 +651,357 @@ impl LeChatPHPClient { }); } - fn handle_input(&mut self, events: &Events, app: &mut App, messages: &Arc<Mutex<Vec<Message>>>, users: &Arc<Mutex<Users>>) -> Result<(), ExitSignal> { + fn handle_input( + &mut self, + events: &Events, + app: &mut App, + messages: &Arc<Mutex<Vec<Message>>>, + users: &Arc<Mutex<Users>>, + ) -> Result<(), ExitSignal> { match events.next() { - Ok(Event::NeedLogin) => return Err(ExitSignal::NeedLogin), - Ok(Event::Terminate) => return Err(ExitSignal::Terminate), + Ok(Event::NeedLogin) => return Err(ExitSignal::NeedLogin), + Ok(Event::Terminate) => return Err(ExitSignal::Terminate), Ok(Event::Input(evt)) => self.handle_event(app, messages, users, evt), _ => Ok(()), } } - fn handle_event(&mut self, app: &mut App, messages: &Arc<Mutex<Vec<Message>>>, users: &Arc<Mutex<Users>>, event: event::Event) -> Result<(), ExitSignal> { + fn handle_event( + &mut self, + app: &mut App, + messages: &Arc<Mutex<Vec<Message>>>, + users: &Arc<Mutex<Users>>, + event: event::Event, + ) -> Result<(), ExitSignal> { match event { event::Event::Resize(_cols, _rows) => Ok(()), - event::Event::FocusGained => Ok(()), - event::Event::FocusLost => Ok(()), - event::Event::Paste(_) => Ok(()), - event::Event::Key(key_event) => self.handle_key_event(app, messages, users, key_event), - event::Event::Mouse(mouse_event) => self.handle_mouse_event(app, mouse_event), + event::Event::FocusGained => Ok(()), + event::Event::FocusLost => Ok(()), + event::Event::Paste(_) => Ok(()), + event::Event::Key(key_event) => self.handle_key_event(app, messages, users, key_event), + event::Event::Mouse(mouse_event) => self.handle_mouse_event(app, mouse_event), } } - fn handle_key_event(&mut self, app: &mut App, messages: &Arc<Mutex<Vec<Message>>>, users: &Arc<Mutex<Users>>, key_event: KeyEvent) -> Result<(), ExitSignal> { + fn handle_key_event( + &mut self, + app: &mut App, + messages: &Arc<Mutex<Vec<Message>>>, + users: &Arc<Mutex<Users>>, + key_event: KeyEvent, + ) -> Result<(), ExitSignal> { if app.input_mode != InputMode::Normal { self.last_key_event = None; } match app.input_mode { - InputMode::LongMessage => self.handle_long_message_mode_key_event(app, key_event, messages), - InputMode::Normal => self.handle_normal_mode_key_event(app, key_event, messages), - InputMode::Editing | - InputMode::EditingErr => self.handle_editing_mode_key_event(app, key_event, users), + InputMode::LongMessage => { + self.handle_long_message_mode_key_event(app, key_event, messages) + } + InputMode::Normal => self.handle_normal_mode_key_event(app, key_event, messages), + InputMode::Editing | InputMode::EditingErr => { + self.handle_editing_mode_key_event(app, key_event, users) + } } } - fn handle_long_message_mode_key_event(&mut self, app: &mut App, key_event: KeyEvent, messages: &Arc<Mutex<Vec<Message>>>) -> Result<(), ExitSignal> { + fn handle_long_message_mode_key_event( + &mut self, + app: &mut App, + key_event: KeyEvent, + messages: &Arc<Mutex<Vec<Message>>>, + ) -> Result<(), ExitSignal> { match key_event { - KeyEvent { code: KeyCode::Enter, modifiers: KeyModifiers::NONE, .. } | - KeyEvent { code: KeyCode::Esc, modifiers: KeyModifiers::NONE, .. } => self.handle_long_message_mode_key_event_esc(app), - KeyEvent { code: KeyCode::Char('d'), modifiers: KeyModifiers::CONTROL, .. } => self.handle_long_message_mode_key_event_ctrl_d(app, messages), + KeyEvent { + code: KeyCode::Enter, + modifiers: KeyModifiers::NONE, + .. + } + | KeyEvent { + code: KeyCode::Esc, + modifiers: KeyModifiers::NONE, + .. + } => self.handle_long_message_mode_key_event_esc(app), + KeyEvent { + code: KeyCode::Char('d'), + modifiers: KeyModifiers::CONTROL, + .. + } => self.handle_long_message_mode_key_event_ctrl_d(app, messages), _ => {} } Ok(()) } - fn handle_normal_mode_key_event(&mut self, app: &mut App, key_event: KeyEvent, messages: &Arc<Mutex<Vec<Message>>>) -> Result<(), ExitSignal> { + fn handle_normal_mode_key_event( + &mut self, + app: &mut App, + key_event: KeyEvent, + messages: &Arc<Mutex<Vec<Message>>>, + ) -> Result<(), ExitSignal> { match key_event { - KeyEvent { code: KeyCode::Char('/'), modifiers: KeyModifiers::NONE, .. } => self.handle_normal_mode_key_event_slash(app), - KeyEvent { code: KeyCode::Char('j'), modifiers: KeyModifiers::NONE, .. } | - KeyEvent { code: KeyCode::Down, modifiers: KeyModifiers::NONE, .. } => self.handle_normal_mode_key_event_down(app), - KeyEvent { code: KeyCode::Char('k'), modifiers: KeyModifiers::NONE, .. } | - KeyEvent { code: KeyCode::Up, modifiers: KeyModifiers::NONE, .. } => self.handle_normal_mode_key_event_up(app), - KeyEvent { code: KeyCode::Enter, modifiers: KeyModifiers::NONE, .. } => self.handle_normal_mode_key_event_enter(app, messages), - KeyEvent { code: KeyCode::Backspace, modifiers: KeyModifiers::NONE, .. } => self.handle_normal_mode_key_event_backspace(app, messages), - KeyEvent { code: KeyCode::Char('y'), modifiers: KeyModifiers::NONE, .. } | - KeyEvent { code: KeyCode::Char('c'), modifiers: KeyModifiers::CONTROL, .. } => self.handle_normal_mode_key_event_yank(app), - KeyEvent { code: KeyCode::Char('Y'), modifiers: KeyModifiers::SHIFT, .. } => self.handle_normal_mode_key_event_yank_link(app), - KeyEvent { code: KeyCode::Char('d'), modifiers: KeyModifiers::NONE, .. } => self.handle_normal_mode_key_event_debug(app), - KeyEvent { code: KeyCode::Char('D'), modifiers: KeyModifiers::SHIFT, .. } => self.handle_normal_mode_key_event_debug2(app), - KeyEvent { code: KeyCode::Char('m'), modifiers: KeyModifiers::NONE, .. } => self.handle_normal_mode_key_event_toggle_mute(), - KeyEvent { code: KeyCode::Char('S'), modifiers: KeyModifiers::SHIFT, .. } => self.handle_normal_mode_key_event_toggle_sys(), - KeyEvent { code: KeyCode::Char('M'), modifiers: KeyModifiers::SHIFT, .. } => self.handle_normal_mode_key_event_toggle_member_view(), - KeyEvent { code: KeyCode::Char('G'), modifiers: KeyModifiers::SHIFT, .. } => self.handle_normal_mode_key_event_toggle_guest_view(), - KeyEvent { code: KeyCode::Char('H'), modifiers: KeyModifiers::SHIFT, .. } => self.handle_normal_mode_key_event_toggle_hidden(), - KeyEvent { code: KeyCode::Char('i'), modifiers: KeyModifiers::NONE, .. } => self.handle_normal_mode_key_event_input_mode(app), - KeyEvent { code: KeyCode::Char('Q'), modifiers: KeyModifiers::SHIFT, .. } => self.handle_normal_mode_key_event_logout()?, - KeyEvent { code: KeyCode::Char('q'), modifiers: KeyModifiers::NONE, .. } => self.handle_normal_mode_key_event_exit()?, - KeyEvent { code: KeyCode::Char('t'), modifiers: KeyModifiers::NONE, .. } => self.handle_normal_mode_key_event_tag(app), - KeyEvent { code: KeyCode::Char('p'), modifiers: KeyModifiers::NONE, .. } => self.handle_normal_mode_key_event_pm(app), - KeyEvent { code: KeyCode::Char('k'), modifiers: KeyModifiers::CONTROL, .. } => self.handle_normal_mode_key_event_kick(app), - KeyEvent { code: KeyCode::Char('w'), modifiers: KeyModifiers::CONTROL, .. } => self.handle_normal_mode_key_event_warn(app), - KeyEvent { code: KeyCode::Char('u'), modifiers: KeyModifiers::CONTROL, .. } | - KeyEvent { code: KeyCode::PageUp, modifiers: KeyModifiers::NONE, .. } => self.handle_normal_mode_key_event_page_up(app), - KeyEvent { code: KeyCode::Char('d'), modifiers: KeyModifiers::CONTROL, .. } | - KeyEvent { code: KeyCode::PageDown, modifiers: KeyModifiers::NONE, .. } => self.handle_normal_mode_key_event_page_down(app), - KeyEvent { code: KeyCode::Esc, modifiers: KeyModifiers::NONE, .. } => self.handle_normal_mode_key_event_esc(app), - KeyEvent { code: KeyCode::Char('u'), modifiers: KeyModifiers::SHIFT, .. } => self.handle_normal_mode_key_event_shift_u(app), - KeyEvent { code: KeyCode::Char('g'), modifiers: KeyModifiers::NONE, .. } => self.handle_normal_mode_key_event_g(app), + KeyEvent { + code: KeyCode::Char('/'), + modifiers: KeyModifiers::NONE, + .. + } => self.handle_normal_mode_key_event_slash(app), + KeyEvent { + code: KeyCode::Char('j'), + modifiers: KeyModifiers::NONE, + .. + } + | KeyEvent { + code: KeyCode::Down, + modifiers: KeyModifiers::NONE, + .. + } => self.handle_normal_mode_key_event_down(app), + KeyEvent { + code: KeyCode::Char('k'), + modifiers: KeyModifiers::NONE, + .. + } + | KeyEvent { + code: KeyCode::Up, + modifiers: KeyModifiers::NONE, + .. + } => self.handle_normal_mode_key_event_up(app), + KeyEvent { + code: KeyCode::Enter, + modifiers: KeyModifiers::NONE, + .. + } => self.handle_normal_mode_key_event_enter(app, messages), + KeyEvent { + code: KeyCode::Backspace, + modifiers: KeyModifiers::NONE, + .. + } => self.handle_normal_mode_key_event_backspace(app, messages), + KeyEvent { + code: KeyCode::Char('y'), + modifiers: KeyModifiers::NONE, + .. + } + | KeyEvent { + code: KeyCode::Char('c'), + modifiers: KeyModifiers::CONTROL, + .. + } => self.handle_normal_mode_key_event_yank(app), + KeyEvent { + code: KeyCode::Char('Y'), + modifiers: KeyModifiers::SHIFT, + .. + } => self.handle_normal_mode_key_event_yank_link(app), + + //straneEdit + KeyEvent { + code: KeyCode::Char('D'), + modifiers: KeyModifiers::SHIFT, + .. + } => self.handle_normal_mode_key_event_download_link(app), + + //StrangeEdit + KeyEvent { + code: KeyCode::Char('d'), + modifiers: KeyModifiers::NONE, + .. + } => self.handle_normal_mode_key_event_download_and_view(app), + + // KeyEvent { + // code: KeyCode::Char('d'), + // modifiers: KeyModifiers::NONE, + // .. + // } => self.handle_normal_mode_key_event_debug(app), + // KeyEvent { + // code: KeyCode::Char('D'), + // modifiers: KeyModifiers::SHIFT, + // .. + // } => self.handle_normal_mode_key_event_debug2(app), + KeyEvent { + code: KeyCode::Char('m'), + modifiers: KeyModifiers::NONE, + .. + } => self.handle_normal_mode_key_event_toggle_mute(), + KeyEvent { + code: KeyCode::Char('S'), + modifiers: KeyModifiers::SHIFT, + .. + } => self.handle_normal_mode_key_event_toggle_sys(), + KeyEvent { + code: KeyCode::Char('M'), + modifiers: KeyModifiers::SHIFT, + .. + } => self.handle_normal_mode_key_event_toggle_member_view(), + KeyEvent { + code: KeyCode::Char('G'), + modifiers: KeyModifiers::SHIFT, + .. + } => self.handle_normal_mode_key_event_toggle_guest_view(), + KeyEvent { + code: KeyCode::Char('H'), + modifiers: KeyModifiers::SHIFT, + .. + } => self.handle_normal_mode_key_event_toggle_hidden(), + KeyEvent { + code: KeyCode::Char('i'), + modifiers: KeyModifiers::NONE, + .. + } => self.handle_normal_mode_key_event_input_mode(app), + KeyEvent { + code: KeyCode::Char('Q'), + modifiers: KeyModifiers::SHIFT, + .. + } => self.handle_normal_mode_key_event_logout()?, + KeyEvent { + code: KeyCode::Char('q'), + modifiers: KeyModifiers::NONE, + .. + } => self.handle_normal_mode_key_event_exit()?, + KeyEvent { + code: KeyCode::Char('t'), + modifiers: KeyModifiers::NONE, + .. + } => self.handle_normal_mode_key_event_tag(app), + KeyEvent { + code: KeyCode::Char('p'), + modifiers: KeyModifiers::NONE, + .. + } => self.handle_normal_mode_key_event_pm(app), + KeyEvent { + code: KeyCode::Char('k'), + modifiers: KeyModifiers::CONTROL, + .. + } => self.handle_normal_mode_key_event_kick(app), + KeyEvent { + code: KeyCode::Char('w'), + modifiers: KeyModifiers::CONTROL, + .. + } => self.handle_normal_mode_key_event_warn(app), + KeyEvent { + code: KeyCode::Char('u'), + modifiers: KeyModifiers::CONTROL, + .. + } + | KeyEvent { + code: KeyCode::PageUp, + modifiers: KeyModifiers::NONE, + .. + } => self.handle_normal_mode_key_event_page_up(app), + KeyEvent { + code: KeyCode::Char('d'), + modifiers: KeyModifiers::CONTROL, + .. + } + | KeyEvent { + code: KeyCode::PageDown, + modifiers: KeyModifiers::NONE, + .. + } => self.handle_normal_mode_key_event_page_down(app), + KeyEvent { + code: KeyCode::Esc, + modifiers: KeyModifiers::NONE, + .. + } => self.handle_normal_mode_key_event_esc(app), + KeyEvent { + code: KeyCode::Char('u'), + modifiers: KeyModifiers::SHIFT, + .. + } => self.handle_normal_mode_key_event_shift_u(app), + KeyEvent { + code: KeyCode::Char('g'), + modifiers: KeyModifiers::NONE, + .. + } => self.handle_normal_mode_key_event_g(app), _ => {} } self.last_key_event = Some(key_event.code); Ok(()) } - fn handle_editing_mode_key_event(&mut self, app: &mut App, key_event: KeyEvent, users: &Arc<Mutex<Users>>) -> Result<(), ExitSignal> { + fn handle_editing_mode_key_event( + &mut self, + app: &mut App, + key_event: KeyEvent, + users: &Arc<Mutex<Users>>, + ) -> Result<(), ExitSignal> { app.input_mode = InputMode::Editing; match key_event { - KeyEvent { code: KeyCode::Enter, modifiers: KeyModifiers::NONE, .. } => self.handle_editing_mode_key_event_enter(app)?, - KeyEvent { code: KeyCode::Tab, modifiers: KeyModifiers::NONE, .. } => self.handle_editing_mode_key_event_tab(app, users), - KeyEvent { code: KeyCode::Char('c'), modifiers: KeyModifiers::CONTROL, .. } => self.handle_editing_mode_key_event_ctrl_c(app), - KeyEvent { code: KeyCode::Char('a'), modifiers: KeyModifiers::CONTROL, .. } => self.handle_editing_mode_key_event_ctrl_a(app), - KeyEvent { code: KeyCode::Char('e'), modifiers: KeyModifiers::CONTROL, .. } => self.handle_editing_mode_key_event_ctrl_e(app), - KeyEvent { code: KeyCode::Char('f'), modifiers: KeyModifiers::CONTROL, .. } => self.handle_editing_mode_key_event_ctrl_f(app), - KeyEvent { code: KeyCode::Char('b'), modifiers: KeyModifiers::CONTROL, .. } => self.handle_editing_mode_key_event_ctrl_b(app), - KeyEvent { code: KeyCode::Char('v'), modifiers: KeyModifiers::CONTROL, .. } => self.handle_editing_mode_key_event_ctrl_v(app), - KeyEvent { code: KeyCode::Left, modifiers: KeyModifiers::NONE, .. } => self.handle_editing_mode_key_event_left(app), - KeyEvent { code: KeyCode::Right, modifiers: KeyModifiers::NONE, .. } => self.handle_editing_mode_key_event_right(app), - KeyEvent { code: KeyCode::Down, modifiers: KeyModifiers::NONE, .. } => self.handle_editing_mode_key_event_down(app), - KeyEvent { code: KeyCode::Char(c), modifiers: KeyModifiers::NONE, .. } | - KeyEvent { code: KeyCode::Char(c), modifiers: KeyModifiers::SHIFT, .. } => self.handle_editing_mode_key_event_shift_c(app, c), - KeyEvent { code: KeyCode::Backspace, modifiers: KeyModifiers::NONE, .. } => self.handle_editing_mode_key_event_backspace(app), - KeyEvent { code: KeyCode::Delete, modifiers: KeyModifiers::NONE, .. } => self.handle_editing_mode_key_event_delete(app), - KeyEvent { code: KeyCode::Esc, modifiers: KeyModifiers::NONE, .. } => self.handle_editing_mode_key_event_esc(app), + KeyEvent { + code: KeyCode::Enter, + modifiers: KeyModifiers::NONE, + .. + } => self.handle_editing_mode_key_event_enter(app)?, + KeyEvent { + code: KeyCode::Tab, + modifiers: KeyModifiers::NONE, + .. + } => self.handle_editing_mode_key_event_tab(app, users), + KeyEvent { + code: KeyCode::Char('c'), + modifiers: KeyModifiers::CONTROL, + .. + } => self.handle_editing_mode_key_event_ctrl_c(app), + KeyEvent { + code: KeyCode::Char('a'), + modifiers: KeyModifiers::CONTROL, + .. + } => self.handle_editing_mode_key_event_ctrl_a(app), + KeyEvent { + code: KeyCode::Char('e'), + modifiers: KeyModifiers::CONTROL, + .. + } => self.handle_editing_mode_key_event_ctrl_e(app), + KeyEvent { + code: KeyCode::Char('f'), + modifiers: KeyModifiers::CONTROL, + .. + } => self.handle_editing_mode_key_event_ctrl_f(app), + KeyEvent { + code: KeyCode::Char('b'), + modifiers: KeyModifiers::CONTROL, + .. + } => self.handle_editing_mode_key_event_ctrl_b(app), + KeyEvent { + code: KeyCode::Char('v'), + modifiers: KeyModifiers::CONTROL, + .. + } => self.handle_editing_mode_key_event_ctrl_v(app), + KeyEvent { + code: KeyCode::Left, + modifiers: KeyModifiers::NONE, + .. + } => self.handle_editing_mode_key_event_left(app), + KeyEvent { + code: KeyCode::Right, + modifiers: KeyModifiers::NONE, + .. + } => self.handle_editing_mode_key_event_right(app), + KeyEvent { + code: KeyCode::Down, + modifiers: KeyModifiers::NONE, + .. + } => self.handle_editing_mode_key_event_down(app), + KeyEvent { + code: KeyCode::Char(c), + modifiers: KeyModifiers::NONE, + .. + } + | KeyEvent { + code: KeyCode::Char(c), + modifiers: KeyModifiers::SHIFT, + .. + } => self.handle_editing_mode_key_event_shift_c(app, c), + KeyEvent { + code: KeyCode::Backspace, + modifiers: KeyModifiers::NONE, + .. + } => self.handle_editing_mode_key_event_backspace(app), + KeyEvent { + code: KeyCode::Delete, + modifiers: KeyModifiers::NONE, + .. + } => self.handle_editing_mode_key_event_delete(app), + KeyEvent { + code: KeyCode::Esc, + modifiers: KeyModifiers::NONE, + .. + } => self.handle_editing_mode_key_event_esc(app), _ => {} } Ok(()) @@ -683,12 +1012,20 @@ impl LeChatPHPClient { app.input_mode = InputMode::Normal; } - fn handle_long_message_mode_key_event_ctrl_d(&mut self, app: &mut App, messages: &Arc<Mutex<Vec<Message>>>) { + fn handle_long_message_mode_key_event_ctrl_d( + &mut self, + app: &mut App, + messages: &Arc<Mutex<Vec<Message>>>, + ) { if let Some(idx) = app.items.state.selected() { if let Some(item) = app.items.items.get(idx) { - self.post_msg(PostType::Clean(item.date.to_owned(), item.text.text())).unwrap(); + self.post_msg(PostType::Clean(item.date.to_owned(), item.text.text())) + .unwrap(); let mut messages = messages.lock().unwrap(); - if let Some(pos) = messages.iter().position(|m| m.date == item.date && m.text == item.text) { + if let Some(pos) = messages + .iter() + .position(|m| m.date == item.date && m.text == item.text) + { messages[pos].hide = !messages[pos].hide; } app.long_message = None; @@ -704,7 +1041,7 @@ impl LeChatPHPClient { fn handle_normal_mode_key_event_down(&mut self, app: &mut App) { app.items.next() } - + fn handle_normal_mode_key_event_slash(&mut self, app: &mut App) { app.items.unselect(); app.input = "/".to_owned(); @@ -712,13 +1049,18 @@ impl LeChatPHPClient { app.input_mode = InputMode::Editing; } - fn handle_normal_mode_key_event_enter(&mut self, app: &mut App, messages: &Arc<Mutex<Vec<Message>>>) { + fn handle_normal_mode_key_event_enter( + &mut self, + app: &mut App, + messages: &Arc<Mutex<Vec<Message>>>, + ) { if let Some(idx) = app.items.state.selected() { if let Some(item) = app.items.items.get(idx) { // If we have a filter, <enter> will "jump" to the message - if !app.filter.is_empty(){ + if !app.filter.is_empty() { let idx = messages - .lock().unwrap() + .lock() + .unwrap() .iter() .enumerate() .find(|(_, e)| e.date == item.date) @@ -733,11 +1075,18 @@ impl LeChatPHPClient { } } - fn handle_normal_mode_key_event_backspace(&mut self, app: &mut App, messages: &Arc<Mutex<Vec<Message>>>) { + fn handle_normal_mode_key_event_backspace( + &mut self, + app: &mut App, + messages: &Arc<Mutex<Vec<Message>>>, + ) { if let Some(idx) = app.items.state.selected() { if let Some(item) = app.items.items.get(idx) { let mut messages = messages.lock().unwrap(); - if let Some(pos) = messages.iter().position(|m| m.date == item.date && m.text == item.text) { + if let Some(pos) = messages + .iter() + .position(|m| m.date == item.date && m.text == item.text) + { if item.deleted { messages.remove(pos); } else { @@ -754,14 +1103,11 @@ impl LeChatPHPClient { if let Some(upload_link) = &item.upload_link { let mut ctx: ClipboardContext = ClipboardProvider::new().unwrap(); let mut out = format!("{}{}", self.config.url, upload_link); - if let Some((_, _, msg)) = - get_message(&item.text, &self.config.members_tag) - { + if let Some((_, _, msg)) = get_message(&item.text, &self.config.members_tag) { out = format!("{} {}", msg, out); } ctx.set_contents(out).unwrap(); - } else if let Some((_, _, msg)) = - get_message(&item.text, &self.config.members_tag) + } else if let Some((_, _, msg)) = get_message(&item.text, &self.config.members_tag) { let mut ctx: ClipboardContext = ClipboardProvider::new().unwrap(); ctx.set_contents(msg).unwrap(); @@ -777,7 +1123,8 @@ impl LeChatPHPClient { let mut ctx: ClipboardContext = ClipboardProvider::new().unwrap(); let out = format!("{}{}", self.config.url, upload_link); ctx.set_contents(out).unwrap(); - } else if let Some((_, _, msg)) = get_message(&item.text, &self.config.members_tag) { + } else if let Some((_, _, msg)) = get_message(&item.text, &self.config.members_tag) + { let finder = LinkFinder::new(); let links: Vec<_> = finder.links(msg.as_str()).collect(); if let Some(link) = links.get(0) { @@ -789,22 +1136,116 @@ impl LeChatPHPClient { } } - fn handle_normal_mode_key_event_debug(&mut self, app: &mut App) { + //StrangeEdit + fn handle_normal_mode_key_event_download_link(&mut self, app: &mut App) { if let Some(idx) = app.items.state.selected() { if let Some(item) = app.items.items.get(idx) { - log::error!("{:?}", item.text.text()); + if let Some(upload_link) = &item.upload_link { + let url = format!("{}{}", self.config.url, upload_link); + let _ = Command::new("curl") + .args([ + "--socks5", + "localhost:9050", + "--socks5-hostname", + "localhost:9050", + &url, + ]) + .arg("-o") + .arg("download.img") + .output() + .expect("Failed to execute curl command"); + } else if let Some((_, _, msg)) = get_message(&item.text, &self.config.members_tag) + { + let finder = LinkFinder::new(); + let links: Vec<_> = finder.links(msg.as_str()).collect(); + if let Some(link) = links.first() { + let url = link.as_str(); + let _ = Command::new("curl") + .args([ + "--socks5", + "localhost:9050", + "--socks5-hostname", + "localhost:9050", + url, + ]) + .arg("-o") + .arg("download.img") + .output() + .expect("Failed to execute curl command"); + } + } } } } - fn handle_normal_mode_key_event_debug2(&mut self, app: &mut App) { + //strageEdit + fn handle_normal_mode_key_event_download_and_view(&mut self, app: &mut App) { if let Some(idx) = app.items.state.selected() { if let Some(item) = app.items.items.get(idx) { - log::error!("{:?} {:?}", item.text, item.upload_link); + if let Some(upload_link) = &item.upload_link { + let url = format!("{}{}", self.config.url, upload_link); + let _ = Command::new("curl") + .args([ + "--socks5", + "localhost:9050", + "--socks5-hostname", + "localhost:9050", + &url, + ]) + .arg("-o") + .arg("download.img") + .output() + .expect("Failed to execute curl command"); + + let _ = Command::new("xdg-open") + .arg("./download.img") + .output() + .expect("Failed to execute sxiv command"); + } else if let Some((_, _, msg)) = get_message(&item.text, &self.config.members_tag) + { + let finder = LinkFinder::new(); + let links: Vec<_> = finder.links(msg.as_str()).collect(); + if let Some(link) = links.first() { + let url = link.as_str(); + let _ = Command::new("curl") + .args([ + "--socks5", + "localhost:9050", + "--socks5-hostname", + "localhost:9050", + url, + ]) + .arg("-o") + .arg("download.img") + .output() + .expect("Failed to execute curl command"); + + let _ = Command::new("sxiv") + .arg("./download.img") + .output() + .expect("Failed to execute sxiv command"); + } + } } } } + // fn handle_normal_mode_key_event_debug(&mut self, app: &mut App) { + // if let Some(idx) = app.items.state.selected() { + // if let Some(item) = app.items.items.get(idx) { + // log::error!("{:?}", item.text.text()); + // } + // } + // } + + // fn handle_normal_mode_key_event_debug2(&mut self, app: &mut App) { + // if let Some(idx) = app.items.state.selected() { + // if let Some(item) = app.items.items.get(idx) { + // log::error!("{:?} {:?}", item.text, item.upload_link); + // } + // } + // } + fn handle_normal_mode_key_event_toggle_mute(&mut self) { let mut is_muted = self.is_muted.lock().unwrap(); *is_muted = !*is_muted; @@ -851,11 +1292,9 @@ impl LeChatPHPClient { fn handle_normal_mode_key_event_tag(&mut self, app: &mut App) { if let Some(idx) = app.items.state.selected() { let text = &app.items.items.get(idx).unwrap().text; - if let Some(username) = get_username( - &self.base_client.username, - &text, - &self.config.members_tag, - ) { + if let Some(username) = + get_username(&self.base_client.username, &text, &self.config.members_tag) + { if text.text().starts_with(&app.members_tag) { app.input = format!("/m @{} ", username); } else { @@ -1031,26 +1470,27 @@ impl LeChatPHPClient { }, None => SEND_TO_ALL, } - .to_owned(); + .to_owned(); let msg = match captures.get(3) { Some(msg_match) => msg_match.as_str().to_owned(), None => "".to_owned(), }; self.post_msg(PostType::Upload(file_path, send_to, msg)) .unwrap(); - } - else if input.starts_with("!warn") { + } else if input.starts_with("!warn") { // StrangeEdit let msg: String = input .find('@') .map(|index| input[index..].to_string()) .unwrap_or_else(String::new); - let end_msg = format!("This is your warning - {}, will be kicked next !rules",msg); + let end_msg = format!( + "This is your warning - {}, will be kicked next !rules", + msg + ); // log::error!("The Strange end_msg is :{}",end_msg); - self.post_msg(PostType::Post(end_msg,None)).unwrap(); - } - else { + self.post_msg(PostType::Post(end_msg, None)).unwrap(); + } else { if input.starts_with("/") && !input.starts_with("/me ") { app.input_idx = input.len(); app.input = input; @@ -1072,9 +1512,9 @@ impl LeChatPHPClient { let mut prefix = ""; if parts.len() == 1 && ((parts[0] == "/kick" || parts[0] == "/k") - || parts[0] == "/pm" - || parts[0] == "/ignore" - || parts[0] == "/unignore") + || parts[0] == "/pm" + || parts[0] == "/ignore" + || parts[0] == "/unignore") { should_autocomplete = true; } else if user_prefix.starts_with("@") { @@ -1240,7 +1680,9 @@ fn autocomplete_username(users: &Arc<Mutex<Users>>, prefix: &str) -> Option<Stri let users = users.lock().unwrap(); let all_users = users.all(); let prefix_lower = prefix.to_lowercase(); - let filtered = all_users.iter().find(|(_, name)| name.to_lowercase().starts_with(&prefix_lower)); + let filtered = all_users + .iter() + .find(|(_, name)| name.to_lowercase().starts_with(&prefix_lower)); Some(filtered?.1.to_owned()) } @@ -1281,7 +1723,10 @@ enum RetryErr { Exit, } -fn retry_fn<F>(mut clb: F) where F: FnMut() -> anyhow::Result<RetryErr> { +fn retry_fn<F>(mut clb: F) +where + F: FnMut() -> anyhow::Result<RetryErr>, +{ loop { match clb() { Ok(RetryErr::Retry) => continue, @@ -1289,21 +1734,37 @@ fn retry_fn<F>(mut clb: F) where F: FnMut() -> anyhow::Result<RetryErr> { Err(err) => { log::error!("{}", err); continue; - }, + } } } } -fn post_msg(client: &Client, post_type_recv: PostType, full_url: &str, session: String, url: &str, last_post_tx: &crossbeam_channel::Sender<()>) { +fn post_msg( + client: &Client, + post_type_recv: PostType, + full_url: &str, + session: String, + url: &str, + last_post_tx: &crossbeam_channel::Sender<()>, +) { let mut should_reset_keepalive_timer = false; retry_fn(|| -> anyhow::Result<RetryErr> { let post_type = post_type_recv.clone(); let resp_text = client.get(url).send()?.text()?; let doc = Document::from(resp_text.as_str()); - let nc = doc.find(Attr("name", "nc")).next().context("nc not found")?; + let nc = doc + .find(Attr("name", "nc")) + .next() + .context("nc not found")?; let nc_value = nc.attr("value").context("nc value not found")?.to_owned(); - let postid = doc.find(Attr("name", "postid")).next().context("failed to get postid")?; - let postid_value = postid.attr("value").context("failed to get postid value")?.to_owned(); + let postid = doc + .find(Attr("name", "postid")) + .next() + .context("failed to get postid")?; + let postid_value = postid + .attr("value") + .context("failed to get postid value")? + .to_owned(); let mut params: Vec<(&str, String)> = vec![ ("lang", LANG.to_owned()), ("nc", nc_value.to_owned()), @@ -1392,10 +1853,7 @@ fn post_msg(client: &Client, post_type_recv: PostType, full_url: &str, session: ("what", "all".to_owned()), ]); } else { - params.extend(vec![ - ("sendto", "".to_owned()), - ("what", "last".to_owned()), - ]); + params.extend(vec![("sendto", "".to_owned()), ("what", "last".to_owned())]); } } PostType::Upload(file_path, send_to, msg) => { @@ -1409,13 +1867,14 @@ fn post_msg(client: &Client, post_type_recv: PostType, full_url: &str, session: .text("message", msg) .text("sendto", send_to.to_owned()) .text("what", "purge".to_owned()) - .file("file", file_path) { + .file("file", file_path) + { Ok(f) => f, Err(e) => { log::error!("{:?}", e); return Ok(RetryErr::Exit); - }, - } + } + }, ); } PostType::Clean(_, _) => {} @@ -1442,7 +1901,11 @@ fn post_msg(client: &Client, post_type_recv: PostType, full_url: &str, session: fn parse_date(date: &str, datetime_fmt: &str) -> NaiveDateTime { let now = Utc::now(); let date_fmt = format!("%Y-{}", datetime_fmt); - NaiveDateTime::parse_from_str(format!("{}-{}", now.year(), date).as_str(), date_fmt.as_str()).unwrap() + NaiveDateTime::parse_from_str( + format!("{}-{}", now.year(), date).as_str(), + date_fmt.as_str(), + ) + .unwrap() } fn get_msgs( @@ -1455,12 +1918,15 @@ fn get_msgs( users: &Arc<Mutex<Users>>, sig: &Arc<Mutex<Sig>>, messages_updated_tx: &crossbeam_channel::Sender<()>, - members_tag:&str, + members_tag: &str, datetime_fmt: &str, messages: &Arc<Mutex<Vec<Message>>>, - should_notify: &mut bool) -> anyhow::Result<()> -{ - let url = format!("{}/{}?action=view&session={}&lang={}", base_url, page_php, session, LANG); + should_notify: &mut bool, +) -> anyhow::Result<()> { + let url = format!( + "{}/{}?action=view&session={}&lang={}", + base_url, page_php, session, LANG + ); let resp_text = client.get(url).send()?.text()?; let resp_text = resp_text.replace("<br>", "\n"); let doc = Document::from(resp_text.as_str()); @@ -1474,7 +1940,16 @@ fn get_msgs( }; { let messages = messages.lock().unwrap(); - process_new_messages(&new_messages, &messages, datetime_fmt, members_tag, username, should_notify, tx, sig); + process_new_messages( + &new_messages, + &messages, + datetime_fmt, + members_tag, + username, + should_notify, + tx, + sig, + ); // Build messages vector. Tag deleted messages. update_messages(new_messages, messages, datetime_fmt); // Notify new messages has arrived. @@ -1491,15 +1966,21 @@ fn get_msgs( } fn process_new_messages( - new_messages: &Vec<Message>, messages: &MutexGuard<Vec<Message>>, - datetime_fmt: &str, members_tag: &str, username: &str, should_notify: &mut bool, + new_messages: &Vec<Message>, + messages: &MutexGuard<Vec<Message>>, + datetime_fmt: &str, + members_tag: &str, + username: &str, + should_notify: &mut bool, tx: &crossbeam_channel::Sender<PostType>, - sig: &Arc<Mutex<Sig>>) { + sig: &Arc<Mutex<Sig>>, +) { if let Some(last_known_msg) = messages.get(0) { let last_known_msg_parsed_dt = parse_date(&last_known_msg.date, datetime_fmt); - let filtered = new_messages - .iter() - .filter(|new_msg| last_known_msg_parsed_dt <= parse_date(&new_msg.date, datetime_fmt) && !(new_msg.date == last_known_msg.date && last_known_msg.text == new_msg.text)); + let filtered = new_messages.iter().filter(|new_msg| { + last_known_msg_parsed_dt <= parse_date(&new_msg.date, datetime_fmt) + && !(new_msg.date == last_known_msg.date && last_known_msg.text == new_msg.text) + }); for new_msg in filtered { if let Some((from, to_opt, msg)) = get_message(&new_msg.text, &members_tag) { // Process new messages @@ -1521,7 +2002,11 @@ fn process_new_messages( } } -fn update_messages(new_messages: Vec<Message>, mut messages: MutexGuard<Vec<Message>>, datetime_fmt: &str) { +fn update_messages( + new_messages: Vec<Message>, + mut messages: MutexGuard<Vec<Message>>, + datetime_fmt: &str, +) { let mut old_msg_ptr = 0; for new_msg in new_messages.into_iter() { loop { @@ -1568,7 +2053,12 @@ fn update_messages(new_messages: Vec<Message>, mut messages: MutexGuard<Vec<Mess messages.truncate(1000); } -fn n0tr1v_only_process_msg(from: &str, msg: &str, tx: &crossbeam_channel::Sender<PostType>, sig: &Arc<Mutex<Sig>>) { +fn n0tr1v_only_process_msg( + from: &str, + msg: &str, + tx: &crossbeam_channel::Sender<PostType>, + sig: &Arc<Mutex<Sig>>, +) { // !bhcli filters if msg == "!bhcli" { let msg = format!("@{} -> {}", from, BHCLI_BLOG_URL).to_owned(); @@ -1602,10 +2092,16 @@ fn delete_message( ]); let clean_resp_txt = client.post(full_url).form(&params).send()?.text()?; let doc = Document::from(clean_resp_txt.as_str()); - let nc = doc.find(Attr("name", "nc")).next().context("nc not found")?; + let nc = doc + .find(Attr("name", "nc")) + .next() + .context("nc not found")?; let nc_value = nc.attr("value").context("nc value not found")?.to_owned(); let msgs = extract_messages(&doc)?; - if let Some(msg) = msgs.iter().find(|m| m.date == date && m.text.text() == text) { + if let Some(msg) = msgs + .iter() + .find(|m| m.date == date && m.text.text() == text) + { let msg_id = msg.id.context("msg id not found")?; params.extend(vec![ ("nc", nc_value.to_owned()), @@ -1684,10 +2180,10 @@ impl ChatClient { c.config.datetime_fmt = params.datetime_fmt.unwrap_or("%m-%d %H:%M:%S".to_owned()); c.config.members_tag = params.members_tag.unwrap_or("[M] ".to_owned()); c.config.keepalive_send_to = None; - }, + } ClientType::Dan => { c.config = LeChatPHPConfig::new_dans_chat_config(); - }, + } ClientType::BHC => { c.config = LeChatPHPConfig::new_black_hat_chat_config(); } @@ -1852,9 +2348,7 @@ fn ask_username(username: Option<String>) -> String { } fn ask_password(password: Option<String>) -> String { - password.unwrap_or_else(|| { - rpassword::prompt_password("Password: ").unwrap() - }) + password.unwrap_or_else(|| rpassword::prompt_password("Password: ").unwrap()) } #[derive(Debug, Clone, PartialEq)] @@ -1971,9 +2465,11 @@ fn main() -> anyhow::Result<()> { let config = log4rs::config::Config::builder() .appender(log4rs::config::Appender::builder().build("logfile", Box::new(logfile))) - .build(log4rs::config::Root::builder() - .appender("logfile") - .build(LevelFilter::Error))?; + .build( + log4rs::config::Root::builder() + .appender("logfile") + .build(LevelFilter::Error), + )?; log4rs::init_config(config)?; @@ -2062,35 +2558,41 @@ fn get_message(root: &StyledText, members_tag: &str) -> Option<(String, Option<S _ => return None, }; return Some((from, None, msg)); - }, + } StyledText::Text(t) => { if t == &members_tag { let from = match children.get(children.len() - 2)? { - StyledText::Styled(_, children) => match children.get(children.len() - 1)? { - StyledText::Text(t) => t.to_owned(), - _ => return None, - }, + StyledText::Styled(_, children) => { + match children.get(children.len() - 1)? { + StyledText::Text(t) => t.to_owned(), + _ => return None, + } + } _ => return None, }; return Some((from, None, msg)); } else if t == "[" { let from = match children.get(children.len() - 2)? { - StyledText::Styled(_, children) => match children.get(children.len() - 1)? { - StyledText::Text(t) => t.to_owned(), - _ => return None, - }, + StyledText::Styled(_, children) => { + match children.get(children.len() - 1)? { + StyledText::Text(t) => t.to_owned(), + _ => return None, + } + } _ => return None, }; let to = match children.get(2)? { - StyledText::Styled(_, children) => match children.get(children.len() - 1)? { - StyledText::Text(t) => Some(t.to_owned()), - _ => return None, - }, + StyledText::Styled(_, children) => { + match children.get(children.len() - 1)? { + StyledText::Text(t) => Some(t.to_owned()), + _ => return None, + } + } _ => return None, }; return Some((from, to, msg)); } - }, + } _ => return None, } } @@ -2115,7 +2617,13 @@ struct Message { } impl Message { - fn new(id: Option<usize>, typ: MessageType, date: String, upload_link: Option<String>, text: StyledText) -> Self { + fn new( + id: Option<usize>, + typ: MessageType, + date: String, + upload_link: Option<String>, + text: StyledText, + ) -> Self { Self { id, typ, @@ -2215,22 +2723,22 @@ fn process_node(e: select::node::Node, mut color: tuiColor) -> (StyledText, Opti color = parse_color(color_match); } } - }, + } Some("font") => { if let Some(color_str) = e.attr("color") { color = parse_color(color_str); } - }, + } Some("a") => { color = tuiColor::White; if let (Some("attachement"), Some(href)) = (e.attr("class"), e.attr("href")) { upload_link = Some(href.to_owned()); } - }, + } Some("style") => { return (StyledText::None, None); - }, - _ => {}, + } + _ => {} } let mut children_texts: Vec<StyledText> = vec![]; let children = e.children(); @@ -2472,7 +2980,12 @@ fn render_long_message(f: &mut Frame<CrosstermBackend<io::Stdout>>, app: &mut Ap } } -fn render_help_txt(f: &mut Frame<CrosstermBackend<io::Stdout>>, app: &mut App, r: Rect, curr_user: &str) { +fn render_help_txt( + f: &mut Frame<CrosstermBackend<io::Stdout>>, + app: &mut App, + r: Rect, + curr_user: &str, +) { let (mut msg, style) = match app.input_mode { InputMode::Normal => ( vec![ @@ -2620,9 +3133,13 @@ fn render_messages( } } - if app.filter != "" { - if !m.text.text().to_lowercase().contains(&app.filter.to_lowercase()) { + if !m + .text + .text() + .to_lowercase() + .contains(&app.filter.to_lowercase()) + { return None; } } @@ -2634,8 +3151,8 @@ fn render_messages( let mut rows = vec![]; let date_style = match (m.deleted, m.hide) { (false, true) => Style::default().fg(tuiColor::Gray), - (false, _) => Style::default().fg(tuiColor::DarkGray), - (true, _) => Style::default().fg(tuiColor::Red), + (false, _) => Style::default().fg(tuiColor::DarkGray), + (true, _) => Style::default().fg(tuiColor::Red), }; let mut spans_vec = vec![Span::styled(m.date.clone(), date_style)]; let show_sys_sep = app.show_sys && m.typ == MessageType::SysMsg; @@ -2661,7 +3178,7 @@ fn render_messages( let style = match (m.deleted, m.hide) { (true, _) => Style::default().bg(tuiColor::Rgb(30, 0, 0)), (_, true) => Style::default().bg(tuiColor::Rgb(20, 20, 20)), - _ => Style::default(), + _ => Style::default(), }; Some(ListItem::new(rows).style(style)) }) @@ -2805,9 +3322,9 @@ impl Events { if event::poll(timeout).unwrap() { let evt = event::read().unwrap(); match evt { - CEvent::FocusGained => {}, - CEvent::FocusLost => {}, - CEvent::Paste(_) => {}, + CEvent::FocusGained => {} + CEvent::FocusLost => {} + CEvent::Paste(_) => {} CEvent::Resize(_, _) => tx.send(Event::Input(evt)).unwrap(), CEvent::Key(_) => tx.send(Event::Input(evt)).unwrap(), CEvent::Mouse(mouse_event) => {