Build generic site update checker with my cool & hip new actor system
This commit is contained in:
commit
a956e41001
1
.gitignore
vendored
Normal file
1
.gitignore
vendored
Normal file
|
@ -0,0 +1 @@
|
|||
/target
|
1182
Cargo.lock
generated
Normal file
1182
Cargo.lock
generated
Normal file
File diff suppressed because it is too large
Load diff
17
Cargo.toml
Normal file
17
Cargo.toml
Normal file
|
@ -0,0 +1,17 @@
|
|||
[package]
|
||||
name = "site-checker"
|
||||
version = "0.1.0"
|
||||
authors = ["asonix <asonix@asonix.dog>"]
|
||||
edition = "2018"
|
||||
|
||||
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
|
||||
|
||||
[dependencies]
|
||||
anyhow = "1"
|
||||
bytes = "1"
|
||||
env_logger = "0.8"
|
||||
log = "0.4"
|
||||
once_cell = "1.7.2"
|
||||
reqwest = { version = "0.11", features = ["rustls"] }
|
||||
tokio = { version = "1", features = ["full"] }
|
||||
tokio-actors = { version = "0.1.0", git = "https://git.asonix.dog/asonix/tokio-actors", branch = "main" }
|
156
src/main.rs
Normal file
156
src/main.rs
Normal file
|
@ -0,0 +1,156 @@
|
|||
use bytes::Bytes;
|
||||
use reqwest::{Client, StatusCode};
|
||||
use std::time::Duration;
|
||||
|
||||
static ANE_HREF: &'static str = "https://www.anthronewengland.com/";
|
||||
static MFF_HREF: &'static str = "https://www.furfest.org/";
|
||||
static TFF_HREF: &'static str = "https://2022.furryfiesta.org";
|
||||
|
||||
static SITES: &'static [(&'static str, &'static str)] = &[
|
||||
("Anthro New England", ANE_HREF),
|
||||
("Midwest FurFest", MFF_HREF),
|
||||
("Texas Furry Fiesta", TFF_HREF),
|
||||
];
|
||||
|
||||
#[derive(Debug)]
|
||||
enum SiteMessage {
|
||||
Check,
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
struct SiteState {
|
||||
name: String,
|
||||
href: String,
|
||||
client: Client,
|
||||
result: Option<SiteResult>,
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
struct SiteResult {
|
||||
status: StatusCode,
|
||||
bytes: Bytes,
|
||||
}
|
||||
|
||||
struct SiteResultDiff {
|
||||
status: Option<StatusCode>,
|
||||
bytes: bool,
|
||||
}
|
||||
|
||||
impl SiteState {
|
||||
async fn check(&mut self) -> anyhow::Result<()> {
|
||||
log::info!("Checking {}", self.name);
|
||||
let response = self
|
||||
.client
|
||||
.get(&self.href)
|
||||
.header("Accept", "text/html")
|
||||
.send()
|
||||
.await?;
|
||||
|
||||
let status = response.status();
|
||||
let bytes = response.bytes().await?;
|
||||
|
||||
let new_result = SiteResult { status, bytes };
|
||||
|
||||
let diff = self.result.as_ref().map(|result| result.diff(&new_result));
|
||||
|
||||
if let Some(diff) = diff {
|
||||
if diff.is_different() {
|
||||
let title = format!("{} Updated", self.name);
|
||||
let description = if let Some(status) = diff.status {
|
||||
if diff.bytes {
|
||||
format!("New status '{}' and site content changed", status)
|
||||
} else {
|
||||
format!("New status '{}'", status)
|
||||
}
|
||||
} else {
|
||||
format!("Site content changed")
|
||||
};
|
||||
|
||||
log::info!("{}", title);
|
||||
log::info!("{}", description);
|
||||
tokio::process::Command::new("notify-send")
|
||||
.args(&["-i", "appointment", &title, &description])
|
||||
.spawn()?
|
||||
.wait()
|
||||
.await?;
|
||||
}
|
||||
} else {
|
||||
log::info!(
|
||||
"First check for {}, status: {}",
|
||||
self.name,
|
||||
new_result.status
|
||||
);
|
||||
}
|
||||
|
||||
self.result = Some(new_result);
|
||||
Ok(())
|
||||
}
|
||||
|
||||
async fn handle_message(&mut self, message: SiteMessage) -> anyhow::Result<()> {
|
||||
match message {
|
||||
SiteMessage::Check => {
|
||||
self.check().await?;
|
||||
}
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
impl SiteResult {
|
||||
fn diff(&self, rhs: &SiteResult) -> SiteResultDiff {
|
||||
let status = if self.status != rhs.status {
|
||||
Some(rhs.status.clone())
|
||||
} else {
|
||||
None
|
||||
};
|
||||
|
||||
let bytes = self.bytes != rhs.bytes;
|
||||
|
||||
SiteResultDiff { status, bytes }
|
||||
}
|
||||
}
|
||||
|
||||
impl SiteResultDiff {
|
||||
fn is_different(&self) -> bool {
|
||||
self.status.is_some() || self.bytes
|
||||
}
|
||||
}
|
||||
|
||||
#[tokio::main]
|
||||
async fn main() -> anyhow::Result<()> {
|
||||
if std::env::var("RUST_LOG").is_err() {
|
||||
std::env::set_var("RUST_LOG", "info");
|
||||
}
|
||||
env_logger::init();
|
||||
|
||||
let client = Client::builder().user_agent("Site Checker").build()?;
|
||||
|
||||
let mut root_handle = tokio_actors::root();
|
||||
|
||||
for (name, href) in SITES {
|
||||
let name = name.to_string();
|
||||
let href = href.to_string();
|
||||
let client = client.clone();
|
||||
|
||||
let state = SiteState {
|
||||
name,
|
||||
href,
|
||||
client,
|
||||
result: None,
|
||||
};
|
||||
|
||||
let handle = root_handle
|
||||
.spawn_child(state, move |state, msg, _| {
|
||||
Box::pin(async move { state.handle_message(msg).await })
|
||||
})
|
||||
.await?;
|
||||
handle.every(Duration::from_secs(30 * 60), || SiteMessage::Check);
|
||||
}
|
||||
|
||||
tokio::signal::ctrl_c().await?;
|
||||
|
||||
root_handle.close().await;
|
||||
|
||||
Ok(())
|
||||
}
|
Loading…
Reference in a new issue