main.rs
1 #[global_allocator] 2 static GLOBAL: mimalloc::MiMalloc = mimalloc::MiMalloc; 3 4 use std::process::ExitCode; 5 use teleghosts::{admin, bridge, setup, sxc, tg}; 6 7 fn main() -> ExitCode { 8 #[cfg(feature = "backtraces")] 9 enforce_backtraces(); 10 11 #[cfg(all(not(feature = "logger"), not(feature = "no_logs")))] 12 init_print_logger(); 13 14 let (args, vars) = launch_options(); 15 16 let rt = tokio::runtime::Runtime::new().unwrap(); 17 rt.block_on(run(args, vars)); 18 19 eprintln!( 20 "\n\n[WARNING]: If bridge terminates due to an unforeseen error and there are any 21 buffered messages, they will be lost!" 22 ); 23 ExitCode::FAILURE 24 } 25 26 async fn run(args: setup::Args, vars: setup::Vars) { 27 #[cfg(feature = "logger")] 28 init_logger().await; 29 30 let bot_mirror = args.bot_mirror; 31 let launch_params = report_failure!(setup::wizard::run(args, vars).await); 32 33 let setup_bot = launch_params.setup_bot; 34 let bridge_task = report_failure!(setup_bot, bridge::task::init(launch_params.db_key)); 35 36 let (sxc_bot_controller, sxc_admin_access) = report_failure!( 37 setup_bot, 38 sxc::bot::start(sxc::bot::Config { 39 port: launch_params.simplex_cli.port(), 40 target_address: launch_params.target_address, 41 fwd: bridge_task.fwd_to_tg.clone(), 42 }) 43 .await 44 ); 45 46 let (tg_bot_controller, tg_admin_access) = report_failure!( 47 setup_bot, 48 tg::bot::start(tg::bot::Config { 49 token: launch_params.tg_token, 50 fwd: bridge_task.fwd_to_sxc.clone(), 51 }) 52 .await 53 ); 54 55 tokio::spawn(async move { 56 setup_bot 57 .send_message( 58 "*Setup is complete*\n\n\ 59 !2 Your Teleghosts instance is up and running!\n\n\ 60 The Teleghosts admin should contact you in a moment. \ 61 You can now safely delete this conversation", 62 ) 63 .await; 64 65 setup_bot.graceful_shutdown().await; 66 }); 67 68 if bot_mirror { 69 admin::bot_task::start( 70 tg_admin_access, 71 bridge_task.admin_access.clone(), 72 sxc_admin_access, 73 ); 74 } else { 75 admin::user_task::start( 76 tg_admin_access, 77 bridge_task.admin_access.clone(), 78 sxc_admin_access, 79 ); 80 } 81 82 let err = match bridge_task.run(tg_bot_controller, sxc_bot_controller).await { 83 Ok(err) => err, 84 Err(err) => anyhow::anyhow!("Database error: {err}"), 85 }; 86 87 log::error!(error:debug=err; "CRITICAL FAILURE"); 88 eprintln!("[ERROR]: {err:?}"); 89 tokio::time::sleep(std::time::Duration::from_secs(3)).await; 90 } 91 92 #[cfg(feature = "dev_secrets")] 93 fn launch_options() -> (setup::Args, setup::Vars) { 94 let args = setup::Args { 95 setup_mode: setup::Mode::direct(teleghosts::dev_secrets::ADDRESS.to_owned()), 96 bot_mirror: false, 97 launch_cli: false, 98 }; 99 100 let vars = setup::Vars { 101 cli_port: std::num::NonZero::new(5225), 102 db_key: Some(teleghosts::dev_secrets::KEY.to_owned()), 103 tg_token: Some(teleghosts::dev_secrets::TOKEN.to_owned()), 104 }; 105 106 (args, vars) 107 } 108 109 #[cfg(not(feature = "dev_secrets"))] 110 fn launch_options() -> (setup::Args, setup::Vars) { 111 (setup::Args::from_env(), setup::Vars::from_env()) 112 } 113 114 #[cfg(feature = "logger")] 115 async fn init_logger() { 116 let _ = tokio::fs::create_dir("./logs").await; 117 let _ = tokio::fs::rename("./logs/current.log", "./logs/previous.log").await; 118 let file = tokio::fs::File::create("./logs/current.log").await.unwrap(); 119 120 structured_logger::Builder::with_level("DEBUG") 121 .with_default_writer(structured_logger::async_json::new_writer(file)) 122 .init(); 123 } 124 125 #[cfg(all(not(feature = "logger"), not(feature = "no_logs")))] 126 fn init_print_logger() { 127 struct Printer; 128 129 struct KvPrinter<'a>(&'a dyn log::kv::Source); 130 131 impl std::fmt::Debug for KvPrinter<'_> { 132 fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result { 133 let mut visitor = f.debug_map(); 134 self.0.visit(&mut visitor).map_err(|_| std::fmt::Error)?; 135 visitor.finish() 136 } 137 } 138 139 impl std::fmt::Display for KvPrinter<'_> { 140 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { 141 struct Visitor<'a, 'b: 'a>(&'a mut std::fmt::Formatter<'b>); 142 143 impl<'kvs, 'a, 'b: 'a> log::kv::VisitSource<'kvs> for Visitor<'a, 'b> { 144 fn visit_pair( 145 &mut self, 146 key: log::kv::Key<'kvs>, 147 value: log::kv::Value<'kvs>, 148 ) -> Result<(), log::kv::Error> { 149 self.0.write_str(key.as_str())?; 150 write!(self.0, "={value}; ")?; 151 Ok(()) 152 } 153 } 154 155 let mut visitor = Visitor(f); 156 self.0.visit(&mut visitor).map_err(|_| std::fmt::Error)?; 157 Ok(()) 158 } 159 } 160 161 impl log::Log for Printer { 162 fn enabled(&self, _metadata: &log::Metadata) -> bool { 163 true 164 } 165 166 fn log(&self, record: &log::Record) { 167 if record.metadata().level() > log::Level::Warn { 168 println!( 169 "[{}] {} > {}| {}", 170 record.level(), 171 record.args(), 172 KvPrinter(record.key_values()), 173 record.target() 174 ); 175 } else { 176 eprintln!( 177 "[{}]! {} > {}| {} | file {}:{}", 178 record.level(), 179 record.args(), 180 KvPrinter(record.key_values()), 181 record.target(), 182 record.file().unwrap_or(""), 183 record.line().unwrap_or(0), 184 ) 185 } 186 } 187 188 fn flush(&self) { 189 use std::io::Write as _; 190 std::io::stdout().flush().unwrap(); 191 std::io::stderr().flush().unwrap(); 192 } 193 } 194 195 static PRINTER: Printer = Printer; 196 log::set_logger(&PRINTER).unwrap(); 197 log::set_max_level(log::LevelFilter::Debug); 198 } 199 200 #[cfg(feature = "backtraces")] 201 fn enforce_backtraces() { 202 unsafe { 203 std::env::set_var("RUST_BACKTRACE", "full"); 204 } 205 } 206 207 macro_rules! report_failure { 208 ($setup_bot:ident, $e:expr) => { 209 match $e { 210 Ok(ok) => ok, 211 Err(err) => { 212 log::error!(error:debug=err; "CRITICAL FAILURE"); 213 $setup_bot.report_failure(&err).await; 214 $setup_bot.graceful_shutdown().await; 215 eprintln!("[ERROR]: {err:?}"); 216 return; 217 } 218 } 219 }; 220 ($e:expr) => { 221 match $e { 222 Ok(ok) => ok, 223 Err(err) => { 224 log::error!(error:debug=err; "CRITICAL FAILURE"); 225 eprintln!("[ERROR]: {err:?}"); 226 return; 227 } 228 } 229 }; 230 } 231 232 use report_failure;