use std::hash::{Hash, Hasher}; use std::time::{Duration, Instant, SystemTime, UNIX_EPOCH}; use log::debug; use meilisearch_lib::MeiliSearch; use serde::Serialize; use siphasher::sip::SipHasher; use crate::Opt; const AMPLITUDE_API_KEY: &str = "f7fba398780e06d8fe6666a9be7e3d47"; #[derive(Debug, Serialize)] struct EventProperties { database_size: u64, last_update_timestamp: Option, //timestamp number_of_documents: Vec, } impl EventProperties { async fn from(data: MeiliSearch) -> anyhow::Result { let stats = data.get_all_stats().await?; let database_size = stats.database_size; let last_update_timestamp = stats.last_update.map(|u| u.timestamp()); let number_of_documents = stats .indexes .values() .map(|index| index.number_of_documents) .collect(); Ok(EventProperties { database_size, last_update_timestamp, number_of_documents, }) } } #[derive(Debug, Serialize)] struct UserProperties<'a> { env: &'a str, start_since_days: u64, user_email: Option, server_provider: Option, } #[derive(Debug, Serialize)] struct Event<'a> { user_id: &'a str, event_type: &'a str, device_id: &'a str, time: u64, app_version: &'a str, user_properties: UserProperties<'a>, event_properties: Option, } #[derive(Debug, Serialize)] struct AmplitudeRequest<'a> { api_key: &'a str, events: Vec>, } pub async fn analytics_sender(data: MeiliSearch, opt: Opt) { let username = whoami::username(); let hostname = whoami::hostname(); let platform = whoami::platform(); let uid = username + &hostname + &platform.to_string(); let mut hasher = SipHasher::new(); uid.hash(&mut hasher); let hash = hasher.finish(); let uid = format!("{:X}", hash); let platform = platform.to_string(); let first_start = Instant::now(); loop { let n = SystemTime::now().duration_since(UNIX_EPOCH).unwrap(); let user_id = &uid; let device_id = &platform; let time = n.as_secs(); let event_type = "runtime_tick"; let elapsed_since_start = first_start.elapsed().as_secs() / 86_400; // One day let event_properties = EventProperties::from(data.clone()).await.ok(); let app_version = env!("CARGO_PKG_VERSION").to_string(); let app_version = app_version.as_str(); let user_email = std::env::var("MEILI_USER_EMAIL").ok(); let server_provider = std::env::var("MEILI_SERVER_PROVIDER").ok(); let user_properties = UserProperties { env: &opt.env, start_since_days: elapsed_since_start, user_email, server_provider, }; let event = Event { user_id, event_type, device_id, time, app_version, user_properties, event_properties, }; let request = AmplitudeRequest { api_key: AMPLITUDE_API_KEY, events: vec![event], }; let response = reqwest::Client::new() .post("https://api2.amplitude.com/2/httpapi") .timeout(Duration::from_secs(60)) // 1 minute max .json(&request) .send() .await; if let Err(e) = response { debug!("Unsuccessful call to Amplitude: {}", e); } tokio::time::sleep(Duration::from_secs(3600)).await; } }