use std::time::{Duration, SystemTime}; use std::io::Write; use std::net::TcpStream; use eframe::egui; use egui::FontFamily::Proportional; use egui::FontId; use egui::TextStyle::*; use crate::player::Player; use crate::tor::GuiTor; use crate::stats::Market; use crate::irc::Client as IRCClient; use libtor::Error as libtorError; pub struct App { pub player: Player, pub market: Market, pub irc: IRCClient, pub tor_required: bool, pub tor_started: bool, pub tor_connected: bool, pub to_data: String, pub show_irc: bool, pub irc_message: String, pub irc_connected: bool, pub show_market_data: bool, pub show_radio: bool, pub irc_stream: Result, } impl Default for App { fn default() -> Self { Self { player: Player::default(), market: Market::new(), irc: IRCClient::new(), tor_started: false, tor_required: true, tor_connected: false, show_market_data: false, show_irc: false, irc_connected: false, to_data: "".to_owned(), irc_message: "".to_owned(), show_radio: true, irc_stream: TcpStream::connect("0.0.0.0:0") } } } fn show_boolmoji(b: bool) -> String { if b { return "✔".to_owned() } else { return "☠".to_owned() } } impl eframe::App for App { fn update(&mut self, ctx: &egui::Context, _frame: &mut eframe::Frame) { let mut style = (*ctx.style()).clone(); style.text_styles = [ (Heading, FontId::new(30.0, Proportional)), (Name("Heading2".into()), FontId::new(25.0, Proportional)), (Name("Context".into()), FontId::new(23.0, Proportional)), (Body, FontId::new(18.0, Proportional)), (Monospace, FontId::new(14.0, Proportional)), (Button, FontId::new(14.0, Proportional)), (Small, FontId::new(10.0, Proportional)), ].into(); ctx.set_style(style); ctx.set_visuals(egui::Visuals::dark()); egui::TopBottomPanel::top("top").show(ctx, |ui| { ui.heading("Wownero Operations Center"); ui.label("Made by ya boi, lza_menace"); ui.hyperlink("https://lzahq.tech"); }); egui::CentralPanel::default().show(ctx, |ui| { ui.horizontal(|ui| { ui.checkbox(&mut self.tor_required, "Require Tor"); if (self.tor_connected && self.tor_required) || (! self.tor_required) { ui.checkbox(&mut self.show_market_data, "Show Market Data"); ui.checkbox(&mut self.show_irc, "Show IRC"); ui.checkbox(&mut self.show_radio, "Show Radio"); } }); // Tor if self.tor_required { if self.tor_started { ui.label( egui::RichText::new( format!( "Tor Started: {} Tor Connected: {} Proxy Up: {}", show_boolmoji(self.tor_started), show_boolmoji(self.tor_connected), show_boolmoji(self.tor_connected) ) ) ); } else { if ui.button("Connect to the Tor network").clicked() { let _t: std::thread::JoinHandle> = GuiTor::start_tor(); self.tor_started = true; self.tor_connected = true; } } } ui.separator(); // WOW!Radio if self.show_radio { ui.heading(egui::RichText::new("WOW!Radio").color(egui::Color32::WHITE)); ui.label(egui::RichText::new("Your home for the most diabolical playlist of the century, made by the skeevers, scallywags, chupacabras, snails, and whores of the Wownero community. Join da chat to peep da scoop.\n").color(egui::Color32::WHITE)); ui.horizontal(|ui| { if self.player.playing { if ui.button("⏸").clicked() { let _ = &self.player.sink.pause(); self.player.playing = false; } ui.add(egui::Slider::new(&mut self.player.volume, 0.0..=100.0)); self.player.sink.set_volume(self.player.volume / 100.0); if self.player.sink.len() != 1 { // let _ = self.player.wait_for_source(); let f = std::fs::File::open(crate::RADIO_STREAM); if let Ok(fo) = f { let file = std::io::BufReader::new(fo); let source = rodio::Decoder::new(file); if source.is_err() { return () } // let _ = self.player.sink.stop(); let _ = self.player.sink.append(source.unwrap()); let _ = self.player.sink.play(); } else { return () } } } else { if ! self.tor_connected && self.tor_required { ui.label("Connect to the Tor network first."); } else { if ui.button("▶").clicked() { if ! self.tor_connected && self.tor_required { return (); } // If stream thread is done, start again if self.player.stream_thread.is_finished() { self.player.stream_thread = self.player.start_radio_stream(self.tor_required); } let _ = self.player.sink.play(); self.player.playing = true; } } } }); // Show spinner when downloading, along with file size if ! self.player.stream_thread.is_finished() { ui.horizontal(|ui| { ui.spinner(); let size: u64 = self.player.get_radio_size(); ui.label(format!( "{:?} -> {} ({} bytes)", self.player.stream_source, crate::RADIO_STREAM, size )); }); } // Show exif metadata when radio file available to read if self.player.playing && self.player.get_radio_size() > 0 { let rt = egui::RichText::new( format!("\n{:?}", self.player.stream_exif)) .color(egui::Color32::WHITE) .size(18.0); ui.label(rt); let dur = self.player.exif_date.elapsed().unwrap(); if dur > Duration::from_secs(15) { self.player.exif_date = SystemTime::now(); self.player.stream_exif = self.player.get_song_meta().unwrap(); } if ui.button(" +1 ").clicked() { let _ = self.irc.send_tune(); } } ui.separator(); } // IRC if self.show_irc && ! self.irc_connected { self.irc_stream = self.irc.run(); self.irc_connected = true; } else if self.show_irc { egui::ScrollArea::vertical().stick_to_bottom(true).show(ui, |ui| { ui.label(self.irc.read_irc_log()); ui.horizontal(|ui| { ui.text_edit_singleline(&mut self.irc_message); if ui.button("> Send <").clicked() { let res = self.irc_stream.as_ref().unwrap().write(self.irc_message.as_bytes()); if res.is_ok() { println!("wrote {} bytes to IRC: {}", res.unwrap(), self.irc_message); } else { eprintln!("error: {:?}", res.err()); } self.irc_message = "".to_owned(); } }); if ui.button("Clear IRC Log").clicked() { let _ = std::fs::File::create(crate::IRC_LOG); } }); } // Market if self.show_market_data { egui::ComboBox::from_label("Pick currency base.") .selected_text(format!("{}", self.market.denomination)) .show_ui(ui, |ui| { ui.selectable_value(&mut self.market.denomination, "sats".to_owned(), "sats"); ui.selectable_value(&mut self.market.denomination, "usd".to_owned(), "usd"); ui.selectable_value(&mut self.market.denomination, "eth".to_owned(), "eth"); } ); if self.market.last_check_time.elapsed().unwrap() > Duration::from_secs(120) { println!("[+] Refreshing WOW market data."); self.market.store_market_data(self.tor_required); self.market.last_check_time = SystemTime::now(); } if self.market.last_check_time.elapsed().unwrap() < Duration::from_secs(30) && self.market.read_json_from_file().len() == 0 { ui.horizontal(|ui| { ui.spinner(); ui.label("Fetching market data..."); }); } self.market.last_cg_data = self.market.read_json_from_file(); let m = &self.market.last_cg_data; let md = &m["market_data"]; ui.horizontal_wrapped(|ui| { ui.vertical(|ui| { ui.label("Current Price"); ui.heading(egui::RichText::new(md["current_price"][&self.market.denomination].to_string()).strong()); ui.label("All-Time High"); ui.heading(egui::RichText::new(md["ath"][&self.market.denomination].to_string()).strong()); ui.label("All-Time Low"); ui.heading(egui::RichText::new(md["atl"][&self.market.denomination].to_string()).strong()); ui.label("Total Volume"); ui.heading(egui::RichText::new(md["total_volume"][&self.market.denomination].to_string()).strong()); ui.label("Market Cap"); ui.heading(egui::RichText::new(md["market_cap"][&self.market.denomination].to_string()).strong()); }); ui.vertical(|ui| { ui.label("PriceChg% (~24hrs)"); ui.heading(egui::RichText::new(md["price_change_percentage_24h_in_currency"][&self.market.denomination].to_string()).strong()); ui.label("PriceChg% (~7d)"); ui.heading(egui::RichText::new(md["price_change_percentage_7d_in_currency"][&self.market.denomination].to_string()).strong()); ui.label("PriceChg% (~14d)"); ui.heading(egui::RichText::new(md["price_change_percentage_14d_in_currency"][&self.market.denomination].to_string()).strong()); ui.label("PriceChg% (~30d)"); ui.heading(egui::RichText::new(md["price_change_percentage_30d_in_currency"][&self.market.denomination].to_string()).strong()); }); }); } }); } }