Skip to content

Commit

Permalink
feat:auto download essential modules
Browse files Browse the repository at this point in the history
  • Loading branch information
0xbrayo committed Feb 6, 2025
1 parent 91a9d50 commit afbfdb1
Show file tree
Hide file tree
Showing 3 changed files with 143 additions and 1 deletion.
1 change: 1 addition & 0 deletions src-tauri/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@ use tauri_plugin_opener::OpenerExt;

mod logging;
mod manager;
mod modules_dl;

use log::info;
use tauri::{
Expand Down
13 changes: 12 additions & 1 deletion src-tauri/src/manager.rs
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,6 @@
/// their state.
///
/// If a module crashes, the manager will notify the user and ask if they want to restart it.
#[cfg(unix)]
use {
nix::sys::signal::{self, Signal},
Expand All @@ -33,6 +32,7 @@ use std::{env, fs, thread};
use tauri::menu::{CheckMenuItem, Menu, MenuItem, SubmenuBuilder};
use tauri_plugin_dialog::{DialogExt, MessageDialogKind};

use crate::modules_dl::has_essential_modules;
use crate::{get_app_handle, get_config, get_tray_id, HANDLE_CONDVAR};

#[derive(Debug)]
Expand All @@ -53,6 +53,8 @@ pub enum ModuleMessage {
pub struct ManagerState {
tx: Sender<ModuleMessage>,
pub modules_running: BTreeMap<String, bool>,
// TODO: the next four could be merged into one
// modules_metadata hashmap? worse for readability
pub modules_discovered: BTreeMap<String, PathBuf>,
pub modules_pid: HashMap<String, u32>,
pub modules_restart_count: HashMap<String, u32>,
Expand Down Expand Up @@ -103,6 +105,15 @@ impl ManagerState {
let quit = MenuItem::with_id(app, "quit", "Quit", true, None::<&str>)
.expect("failed to create quit menu item");

if !has_essential_modules(self.modules_discovered.keys().cloned().collect()) {
// todo!()
thread::spawn(|| {
tauri::async_runtime::block_on(async {
crate::modules_dl::download_modules().await.unwrap();
});
});
}

let mut modules_submenu_builder = SubmenuBuilder::new(app, "Modules");
for (module, running) in self.modules_running.iter() {
let label = module;
Expand Down
130 changes: 130 additions & 0 deletions src-tauri/src/modules_dl.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,130 @@
/// Downloads essential modules such as the window and afk watchers
/// Module metadata is stored in a csv file that is downloaded
/// the fields appear in the order below
/// name,os,display_server,version,arch,release_date,link
///
/// More fields can be added as long as it maintains backward compatibility
use crate::get_config;
use csv::ReaderBuilder;
use log::error;
use regex::Regex;
use std::{fs::File, io::Write, vec};
use tauri_plugin_http::reqwest;

#[cfg(target_os = "linux")]
fn is_wayland() -> bool {
std::env::var("XDG_SESSION_TYPE").unwrap_or_default() == "wayland"
}

async fn download_module(url: &str) -> Result<(), Box<dyn std::error::Error>> {
let mut response = reqwest::get(url).await?;
let file_name = url.split('/').last().unwrap();
let file_path = get_config().defaults.discovery_path.clone().join(file_name);
let mut file = File::create(file_path.clone())?;
while let Some(chunk) = response.chunk().await? {
file.write_all(&chunk)?;
}
// TODO: testing check if it matches correctly
let tar_regex = Regex::new(r"(?i)\.tar(?:\.gz)?$").unwrap();
if file_name.ends_with(".zip") {
let output = std::process::Command::new("unzip")
.arg(&file_path)
.arg("-d")
.arg(get_config().defaults.discovery_path.clone())
.output()?;
error!("{}", String::from_utf8_lossy(&output.stdout));
} else if tar_regex.is_match(file_name) {
let output = std::process::Command::new("tar")
.arg("-xvf")
.arg(&file_path)
.arg("-C")
.arg(get_config().defaults.discovery_path.clone())
.output()?;
error!("{}", String::from_utf8_lossy(&output.stdout));
}
Ok(())
}

async fn fetch_releases_file() -> Result<String, Box<dyn std::error::Error>> {
// TODO: use a better source
let url = "https://gist.githubusercontent.com/0xbrayo/f7b25a2ff9ed24ce21fa8397837265b6/raw/120ddb3d31d7f009d66f070bd4a0dc06d3c0aacf/aw-releases.csv";
let response = reqwest::get(url).await?;
let body = response.text().await?;
Ok(body)
}

pub(crate) async fn download_modules() -> Result<(), Box<dyn std::error::Error>> {
let releases = fetch_releases_file().await?;
let mut reader = ReaderBuilder::new().from_reader(releases.as_bytes());

if cfg!(target_os = "linux") {
let display_server = if is_wayland() { "wayland" } else { "x11" };
for row in reader.records() {
let row = row.expect("Malformed releases file");
if &row[1] != "linux" {
continue;
}
if !row[2].is_empty() && &row[2] != display_server {
continue;
}
let url = &row[6];
download_module(url).await?;
}
} else if cfg!(target_os = "windows") {
for row in reader.records() {
let row = row.expect("Malformed releases file");
if &row[1] != "windows" {
continue;
}
let url = &row[6];
download_module(url).await?;
}
} else if cfg!(target_os = "macos") {
for row in reader.records() {
let row = row.expect("Malformed releases file");
if &row[2] != "macos" {
continue;
}
let url = &row[6];
download_module(url).await?;
}
} else {
// should be unreachable
panic!("Unsupported OS");
}
Ok(())
}

#[cfg(target_os = "linux")]
pub(crate) fn has_essential_modules(modules: Vec<String>) -> bool {
let essential_modules = if is_wayland() {
vec!["aw-awatcher".to_string()]
} else {
vec![
"aw-watcher-afk".to_string(),
"aw-watcher-window".to_string(),
]
};

for module in essential_modules {
if !modules.iter().any(|m| m == &module) {
return false;
}
}
true
}

#[cfg(not(any(target_os = "linux")))]
pub(crate) fn has_essential_modules(modules: Vec<String>) -> bool {
let essential_modules = vec![
"aw-watcher-afk".to_string(),
"aw-watcher-window".to_string(),
];

for module in essential_modules {
if !modules.iter().any(|m| m == &module) {
return false;
}
}
true
}

0 comments on commit afbfdb1

Please sign in to comment.