webcheck save

This commit is contained in:
ゆめ 2023-10-01 00:21:56 -05:00
parent 2a714d029a
commit 17d43f0fd0
2 changed files with 127 additions and 19 deletions

View file

@ -21,9 +21,11 @@ use super::{
}; };
mod driver; mod driver;
mod save;
mod utd_app; mod utd_app;
pub struct WebcheckApp { pub struct WebcheckApp {
reqwest_client: reqwest::Client,
state: Mutex<WebcheckAppState>, state: Mutex<WebcheckAppState>,
} }
@ -31,7 +33,7 @@ struct WebcheckAppState {
config: Option<&'static Config>, config: Option<&'static Config>,
global_app_state: Option<Arc<Mutex<AppState>>>, global_app_state: Option<Arc<Mutex<AppState>>>,
last_response: HashMap<String, LastResponse>, last_response: HashMap<String, LastResponse>,
checkers: HashMap<String, Box<dyn WebDriverChecker + Send + Sync>>, checkers: HashMap<String, Checker>,
} }
#[derive(Serialize, Deserialize, Clone, Debug)] #[derive(Serialize, Deserialize, Clone, Debug)]
@ -54,6 +56,18 @@ async fn route_get_results(
)) ))
} }
pub enum Checker {
WebDriver(Box<dyn WebDriverChecker + Send + Sync>),
Reqwest(Box<dyn ReqwestChecker + Send + Sync>),
}
#[async_trait]
pub trait ReqwestChecker {
fn init(&mut self, config: &HashMap<String, String>) -> anyhow::Result<()>;
fn interval(&self) -> u64;
async fn check(&self, client: &reqwest::Client) -> anyhow::Result<String>;
}
#[async_trait] #[async_trait]
pub trait WebDriverChecker { pub trait WebDriverChecker {
fn init(&mut self, config: &HashMap<String, String>) -> anyhow::Result<()>; fn init(&mut self, config: &HashMap<String, String>) -> anyhow::Result<()>;
@ -64,6 +78,7 @@ pub trait WebDriverChecker {
impl WebcheckApp { impl WebcheckApp {
pub fn new() -> Self { pub fn new() -> Self {
Self { Self {
reqwest_client: reqwest::Client::new(),
state: Mutex::new(WebcheckAppState { state: Mutex::new(WebcheckAppState {
config: None, config: None,
global_app_state: None, global_app_state: None,
@ -76,25 +91,30 @@ impl WebcheckApp {
pub async fn run_single_check(self: &Self, key: &str) -> anyhow::Result<()> { pub async fn run_single_check(self: &Self, key: &str) -> anyhow::Result<()> {
let mut state = self.state.lock().await; let mut state = self.state.lock().await;
let checker = state.checkers.get_mut(key).unwrap(); let response = match state.checkers.get_mut(key).unwrap() {
Checker::WebDriver(checker) => {
let mut driver = driver::chrome::ChromeDriver::new();
driver.spawn(&["--enable-chrome-logs"])?;
tokio::time::sleep(std::time::Duration::from_secs(5)).await;
let mut driver = driver::chrome::ChromeDriver::new(); let mut caps = DesiredCapabilities::chrome();
driver.spawn(&["--enable-chrome-logs"])?; caps.set_headless().unwrap();
tokio::time::sleep(std::time::Duration::from_secs(5)).await; caps.set_disable_gpu().unwrap();
let mut caps = DesiredCapabilities::chrome(); let driver = driver.connect(caps).await?;
caps.set_headless().unwrap(); let response = match checker.check(&driver).await {
caps.set_disable_gpu().unwrap(); Ok(response) => response,
Err(e) => {
let driver = driver.connect(caps).await?; driver.quit().await?;
let response = match checker.check(&driver).await { return Err(e);
Ok(response) => response, }
Err(e) => { };
driver.quit().await?; driver.quit().await?;
return Err(e); response
} }
Checker::Reqwest(checker) => checker.check(&self.reqwest_client).await?,
}; };
driver.quit().await?; info!("Webcheck for {} returned {}", key, response);
let new_response = LastResponse { let new_response = LastResponse {
response: response.clone(), response: response.clone(),
@ -141,7 +161,10 @@ impl WebcheckApp {
let interval = { let interval = {
let state = self_clone.state.lock().await; let state = self_clone.state.lock().await;
let checker = state.checkers.get(key.as_str()).unwrap(); let checker = state.checkers.get(key.as_str()).unwrap();
checker.interval() match checker {
Checker::WebDriver(checker) => checker.interval(),
Checker::Reqwest(checker) => checker.interval(),
}
}; };
let mut ticker = tokio::time::interval(std::time::Duration::from_secs(interval)); let mut ticker = tokio::time::interval(std::time::Duration::from_secs(interval));
@ -171,8 +194,8 @@ impl App for WebcheckApp {
state.global_app_state = Some(app_state); state.global_app_state = Some(app_state);
let Some(ref config) = config.webcheck else { let Some(ref config) = config.webcheck else {
return; return;
}; };
config.keys().for_each(|key| match key.as_str() { config.keys().for_each(|key| match key.as_str() {
"utd_app" => { "utd_app" => {
@ -180,7 +203,18 @@ impl App for WebcheckApp {
checker checker
.init(config.get(key).unwrap()) .init(config.get(key).unwrap())
.expect("Failed to initialize UTDAppChecker"); .expect("Failed to initialize UTDAppChecker");
state.checkers.insert(key.clone(), Box::new(checker)); state
.checkers
.insert(key.clone(), Checker::WebDriver(Box::new(checker)));
}
"save" => {
let mut checker = save::SaveChecker::new();
checker
.init(config.get(key).unwrap())
.expect("Failed to initialize SaveChecker");
state
.checkers
.insert(key.clone(), Checker::Reqwest(Box::new(checker)));
} }
_ => panic!("Invalid key in webcheck config: {}", key), _ => panic!("Invalid key in webcheck config: {}", key),
}); });

74
src/apps/webcheck/save.rs Normal file
View file

@ -0,0 +1,74 @@
use std::collections::HashMap;
use super::ReqwestChecker;
use async_trait::async_trait;
use serde::Deserialize;
#[derive(Deserialize, Debug)]
pub struct SaveCaseCheckResp {
#[serde(rename = "caseNumber")]
case_number: String,
#[serde(rename = "createdDate")]
created_date: String,
#[serde(rename = "agencyName")]
agency_name: String,
#[serde(rename = "caseStatus")]
case_status: String,
}
pub struct SaveChecker {
config: HashMap<String, String>,
}
impl SaveChecker {
pub fn new() -> Self {
Self {
config: HashMap::new(),
}
}
}
#[async_trait]
impl ReqwestChecker for SaveChecker {
fn init(&mut self, config: &HashMap<String, String>) -> anyhow::Result<()> {
if config.get("case_id").is_none() {
return Err(anyhow::anyhow!("case_id not set"));
}
self.config = config.clone();
Ok(())
}
fn interval(&self) -> u64 {
let default_interval = "3600".to_string();
let interval = self.config.get("interval").unwrap_or(&default_interval);
interval.parse::<u64>().unwrap()
}
async fn check(&self, client: &reqwest::Client) -> anyhow::Result<String> {
let case_id = self.config.get("case_id").unwrap();
let req = client
.request(
reqwest::Method::GET,
format!(
"https://save.uscis.gov/api/save/read/cases/check/{}",
case_id
),
)
.header("Accept", "application/json")
.header("Content-Type", "application/json")
.header(
"User-Agent",
"Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:109.0) Gecko/20100101 Firefox/117.0",
)
.header(
"Referer",
format!(
"https://save.uscis.gov/save/app/client/ui/case-check/detail/{}",
case_id
),
)
.build()?;
let resp = client.execute(req).await?;
let res = resp.json::<SaveCaseCheckResp>().await?;
Ok(format!("Case {} is {}", res.case_number, res.case_status,))
}
}