diff --git a/flake.lock b/flake.lock index 2303f6ba..fd31aacd 100644 --- a/flake.lock +++ b/flake.lock @@ -20,11 +20,11 @@ }, "nixpkgs": { "locked": { - "lastModified": 1709200309, - "narHash": "sha256-lKdtMbhnBNU1lr978T+wEYet3sfIXXgyiDZNEgx8CV8=", + "lastModified": 1709386671, + "narHash": "sha256-VPqfBnIJ+cfa78pd4Y5Cr6sOWVW8GYHRVucxJGmRf8Q=", "owner": "NixOS", "repo": "nixpkgs", - "rev": "ebe6e807793e7c9cc59cf81225fdee1a03413811", + "rev": "fa9a51752f1b5de583ad5213eb621be071806663", "type": "github" }, "original": { diff --git a/owmods_cli/src/cli.rs b/owmods_cli/src/cli.rs index 5a91af8b..e4f1cab5 100644 --- a/owmods_cli/src/cli.rs +++ b/owmods_cli/src/cli.rs @@ -21,6 +21,12 @@ pub struct BaseCli { help = "Assert that OWML is setup, if not simply exit with code 2 (2 is used to differentiate between a normal exit and an exit due to OWML not being setup)" )] pub assert_setup: bool, + #[arg( + global = true, + long = "analytics", + help = "Enable or disable analytics (this will be saved to the config so you won't need it in subsequent runs)" + )] + pub analytics: Option, #[arg(global = true, long = "debug", help = "Enable debug output")] pub debug: bool, } diff --git a/owmods_cli/src/main.rs b/owmods_cli/src/main.rs index d05eb00b..44e69beb 100644 --- a/owmods_cli/src/main.rs +++ b/owmods_cli/src/main.rs @@ -38,7 +38,15 @@ async fn run_from_cli(cli: BaseCli) -> Result<()> { let r = cli.recursive; let assert_setup = cli.assert_setup; - let config = Config::get(None)?; + let mut config = Config::get(None)?; + + if let Some(analytics) = cli.analytics { + if analytics != config.send_analytics { + info!("Setting send_analytics to {}", analytics); + config.send_analytics = analytics; + config.save()?; + } + } let ran_setup = matches!( &cli.command, diff --git a/owmods_core/src/analytics.rs b/owmods_core/src/analytics.rs index 99a956ff..2199762b 100644 --- a/owmods_core/src/analytics.rs +++ b/owmods_core/src/analytics.rs @@ -5,6 +5,8 @@ use log::{debug, warn}; use reqwest::Client; use serde::Serialize; +use crate::config::Config; + const MEASUREMENT_ID: &str = "G-2QQN7V5WE1"; const API_KEY: Option<&str> = option_env!("ANALYTICS_API_KEY"); @@ -78,16 +80,25 @@ impl AnalyticsPayload { /// ## Examples /// /// ```no_run -/// use owmods_core::analytics::{send_analytics_event, AnalyticsEventName}; +/// use owmods_core::{config::Config,analytics::{send_analytics_event, AnalyticsEventName}}; /// /// # tokio_test::block_on(async { /// // Time saver is the best mod! +/// let config = Config::get(None).unwrap(); /// loop { -/// send_analytics_event(AnalyticsEventName::ModInstall, "Bwc9876.TimeSaver").await; +/// send_analytics_event(AnalyticsEventName::ModInstall, "Bwc9876.TimeSaver", &config).await; /// } /// # }); /// -pub async fn send_analytics_event(event_name: AnalyticsEventName, unique_name: &str) { +pub async fn send_analytics_event( + event_name: AnalyticsEventName, + unique_name: &str, + config: &Config, +) { + if !config.send_analytics { + debug!("Skipping Analytics As It's Disabled"); + return; + } if let Some(api_key) = API_KEY { let url = format!("https://www.google-analytics.com/mp/collect?measurement_id={MEASUREMENT_ID}&api_secret={api_key}"); let client = Client::new(); diff --git a/owmods_core/src/config.rs b/owmods_core/src/config.rs index e0f713a8..3378852f 100644 --- a/owmods_core/src/config.rs +++ b/owmods_core/src/config.rs @@ -13,6 +13,10 @@ use crate::{ file::{deserialize_from_json, get_app_path, get_default_owml_path, serialize_to_json}, }; +const fn _default_true() -> bool { + true +} + /// Represents the core config, contains critical info needed by the core API #[typeshare] #[derive(Debug, Serialize, Deserialize, Clone)] @@ -28,6 +32,9 @@ pub struct Config { pub viewed_alerts: Vec, /// The last alert that was shown to the user, this is used to track if the user has seen the alert before pub last_viewed_db_alert: Option, + /// Whether or not to send analytics events + #[serde(default = "_default_true")] + pub send_analytics: bool, /// Where the config is saved, this is not serialized #[serde(skip)] pub path: PathBuf, @@ -66,6 +73,7 @@ impl Config { database_url: String::from(DEFAULT_DB_URL), alert_url: String::from(DEFAULT_ALERT_URL), last_viewed_db_alert: None, + send_analytics: true, viewed_alerts: vec![], path, }) diff --git a/owmods_core/src/download.rs b/owmods_core/src/download.rs index 8e6d8f3f..0edb347f 100644 --- a/owmods_core/src/download.rs +++ b/owmods_core/src/download.rs @@ -274,7 +274,12 @@ pub async fn download_and_install_owml( temp_dir.close()?; - send_analytics_event(AnalyticsEventName::ModRequiredInstall, OWML_UNIQUE_NAME).await; + send_analytics_event( + AnalyticsEventName::ModRequiredInstall, + OWML_UNIQUE_NAME, + config, + ) + .await; Ok(()) } @@ -442,7 +447,7 @@ pub async fn install_mod_from_url( /// /// for installed_mod in installed { /// println!("Installed {}", installed_mod.manifest.name); -/// send_analytics_event(AnalyticsEventName::ModInstall, &installed_mod.manifest.unique_name).await; +/// send_analytics_event(AnalyticsEventName::ModInstall, &installed_mod.manifest.unique_name, &config).await; /// } /// # }); /// ``` @@ -612,6 +617,7 @@ pub async fn install_mod_from_db( send_analytics_event( AnalyticsEventName::ModRequiredInstall, &installed_mod.manifest.unique_name, + config, ) .await; } @@ -642,7 +648,7 @@ pub async fn install_mod_from_db( AnalyticsEventName::ModInstall }; - send_analytics_event(mod_event, unique_name).await; + send_analytics_event(mod_event, unique_name, config).await; Ok(new_mod) } diff --git a/owmods_core/src/io.rs b/owmods_core/src/io.rs index 8399ef78..3332791e 100644 --- a/owmods_core/src/io.rs +++ b/owmods_core/src/io.rs @@ -138,7 +138,7 @@ pub async fn import_mods( install_mods_parallel(needed_install.clone(), config, remote_db, local_db).await?; for unique_name in needed_install { - send_analytics_event(AnalyticsEventName::ModInstall, &unique_name).await; + send_analytics_event(AnalyticsEventName::ModInstall, &unique_name, config).await; } Ok(()) diff --git a/owmods_core/src/updates.rs b/owmods_core/src/updates.rs index 3484b86a..fcbc2b8b 100644 --- a/owmods_core/src/updates.rs +++ b/owmods_core/src/updates.rs @@ -132,6 +132,7 @@ pub async fn update_all( send_analytics_event( AnalyticsEventName::ModUpdate, &updated_mod.manifest.unique_name, + config, ) .await; } diff --git a/owmods_core/src/validate.rs b/owmods_core/src/validate.rs index 0eb1fd34..1924c38c 100644 --- a/owmods_core/src/validate.rs +++ b/owmods_core/src/validate.rs @@ -126,7 +126,7 @@ pub async fn fix_deps( } install_mods_parallel(missing.clone(), config, remote_db, db).await?; for missing_mod in missing { - send_analytics_event(AnalyticsEventName::ModRequiredInstall, &missing_mod).await; + send_analytics_event(AnalyticsEventName::ModRequiredInstall, &missing_mod, config).await; } Ok(()) } diff --git a/owmods_gui/backend/src/commands.rs b/owmods_gui/backend/src/commands.rs index 108afaea..db12ae7c 100644 --- a/owmods_gui/backend/src/commands.rs +++ b/owmods_gui/backend/src/commands.rs @@ -682,6 +682,7 @@ pub async fn update_all_mods( send_analytics_event( AnalyticsEventName::ModUpdate, &updated_mod.manifest.unique_name, + &config, ) .await; } diff --git a/owmods_gui/frontend/src/assets/translations/english.json b/owmods_gui/frontend/src/assets/translations/english.json index 70c11ea0..4b3ad926 100644 --- a/owmods_gui/frontend/src/assets/translations/english.json +++ b/owmods_gui/frontend/src/assets/translations/english.json @@ -2,6 +2,7 @@ "ABOUT": "About", "ACTIONS": "Actions", "ALERT_URL": "Alert URL", + "ANALYTICS": "Send Analytics", "ANY": "Any", "APP_TITLE": "Outer Wilds Mod Manager", "APP_VERSION": "Version: $version$", @@ -130,6 +131,7 @@ "Success": "Success", "THEME": "Theme", "TOOLTIP_ALERT_URL": "URL used to get alerts to display in the Mod Manager.", + "TOOLTIP_ANALYTICS": "Send anonymous usage analytics when installing mods (used to track mod download counts)", "TOOLTIP_AUTO_DISABLE_DEPS": "When disabling a mod that has dependencies, disable those dependencies (except if that dependency is required by another mod)", "TOOLTIP_AUTO_ENABLE_DEPS": "When enabling a mod that has disabled dependencies, enable the dependencies without a prompt.", "TOOLTIP_CLEAR_DB_ALERTS": "Clear which alerts have been shown (and dismissed) from the mod database", diff --git a/owmods_gui/frontend/src/assets/translations/template.json b/owmods_gui/frontend/src/assets/translations/template.json index c9fcde60..dae85205 100644 --- a/owmods_gui/frontend/src/assets/translations/template.json +++ b/owmods_gui/frontend/src/assets/translations/template.json @@ -2,6 +2,7 @@ "ABOUT": "", "ACTIONS": "", "ALERT_URL": "", + "ANALYTICS": "", "ANY": "", "APP_TITLE": "", "APP_VERSION": "", @@ -130,6 +131,7 @@ "Success": "", "THEME": "", "TOOLTIP_ALERT_URL": "", + "TOOLTIP_ANALYTICS": "", "TOOLTIP_AUTO_DISABLE_DEPS": "", "TOOLTIP_AUTO_ENABLE_DEPS": "", "TOOLTIP_CLEAR_DB_ALERTS": "", diff --git a/owmods_gui/frontend/src/components/main/top-bar/settings/SettingsForm.tsx b/owmods_gui/frontend/src/components/main/top-bar/settings/SettingsForm.tsx index 06f6fa4c..2977d012 100644 --- a/owmods_gui/frontend/src/components/main/top-bar/settings/SettingsForm.tsx +++ b/owmods_gui/frontend/src/components/main/top-bar/settings/SettingsForm.tsx @@ -267,6 +267,13 @@ const SettingsForm = forwardRef(function SettingsForm(props: SettingsFormProps, /> onReset(0)} /> +