/ crates / teleghosts / src / main.rs
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;