Skip to content
Snippets Groups Projects
Commit 5deb3436 authored by Marvin Weiler's avatar Marvin Weiler
Browse files

Added email notification and an config-parser

parent 3cf0e9df
No related branches found
No related tags found
No related merge requests found
# Generated by Cargo
# will have compiled files and executables
debug/
target/
# Remove Cargo.lock from gitignore if creating an executable, leave it for libraries
# More information here https://doc.rust-lang.org/cargo/guide/cargo-toml-vs-cargo-lock.html
Cargo.lock
# These are backup files generated by rustfmt
**/*.rs.bk
# MSVC Windows builds of rustc generate these, which store debugging information
*.pdb
#exclude the config file
config.json
...@@ -7,7 +7,10 @@ edition = "2021" ...@@ -7,7 +7,10 @@ edition = "2021"
[dependencies] [dependencies]
fantoccini = "0.18.0" fantoccini = "0.18.0"
scraper = "0.12.0" penguin-config = "0.1.1"
serde = "1.0.136"
tokio = { version = "1", features = ["full"] } tokio = { version = "1", features = ["full"] }
lettre = "0.9" lettre = { version = "0.10.0-rc.4", features = [
lettre_email = "0.9" "smtp-transport",
"native-tls",
] }
{"smtp_host":"","smtp_user":"","smtp_pass":"","smtp_from":"","filters":[],"recipients":[]}
use fantoccini::error::CmdError; use fantoccini::error::CmdError;
use fantoccini::{ClientBuilder, Locator}; use fantoccini::{ClientBuilder, Locator};
use lettre::transport::smtp::authentication::{Credentials, Mechanism};
use lettre::{Message, SmtpTransport, Transport};
use penguin_config::*;
use std::process::Command;
struct Product {
name: String,
price: String,
available: bool,
}
#[derive(Deserialize, PenguinConfigFile, Default)]
#[penguin_config(path = "config.json")]
struct Config {
smtp_host: String,
smtp_user: String,
smtp_pass: String,
smtp_from: String,
filters: Vec<String>,
recipients: Vec<String>,
}
#[tokio::main] #[tokio::main]
async fn main() { async fn main() {
let filters = vec!["RX 6800", "RX 6700"]; let config = Config::read_config();
let filters = config.filters;
loop {
let products = get_html_products().await.unwrap(); let products = get_html_products().await.unwrap();
let filtered: Vec<Product> = products let filtered: Vec<Product> = products
.into_iter() .into_iter()
.filter(|el| { .filter(|el| {
for s in &filters { for s in &filters {
if el.name.contains(s) { if el.name.contains(s) {
return el.available; return el.available;
}
} }
false }
}) false
.collect(); })
.collect();
if filtered.len() > 0 {
println!("The following products are available");
} else {
println!("Sorry, all products are out of stock. :/");
}
}
}
#[derive(Debug)] if filtered.len() > 0 {
pub struct Product { println!("Found {} products.\nSending mail.", filtered.len());
name: String, send_email(filtered);
price: String, } else {
available: bool, println!("Sorry, all products are out of stock. :/");
}
} }
async fn get_html_products() -> Result<Vec<Product>, CmdError> { async fn get_html_products() -> Result<Vec<Product>, CmdError> {
// start the geckodriver
let mut geckodriver = Command::new("sh")
.arg("-c")
.arg("./geckodriver")
.spawn()
.expect("sh command failed to start geckodriver");
// navigate to amd-site // navigate to amd-site
let mut webc = ClientBuilder::native() let mut webc = ClientBuilder::native()
.connect("http://localhost:4444") .connect("http://localhost:4444")
...@@ -71,5 +92,43 @@ async fn get_html_products() -> Result<Vec<Product>, CmdError> { ...@@ -71,5 +92,43 @@ async fn get_html_products() -> Result<Vec<Product>, CmdError> {
webc.close() webc.close()
.await .await
.expect("Failed to terminate webdriver sessions"); .expect("Failed to terminate webdriver sessions");
// stop the geckodriver
geckodriver.kill().expect("Failed to kill geckodriver!");
return Ok(products); return Ok(products);
} }
fn send_email(config: Config, products: Vec<Product>) -> () {
let mut message = String::from(
"The following products are available at: https://www.amd.com/de/direct-buy \n\n",
);
for product in products {
message.push_str(format!(" - {}, Price: {}\n", product.name, product.price).as_str());
}
for recipient in config.recipients {
let mail = Message::builder()
.from(
config
.smtp_from
.parse()
.expect(format!("From address: {} is invalid", config.smtp_from).as_str()),
)
.to(recipient
.parse()
.expect(format!("To adress: {} is invalid", recipient).as_str()))
.subject("Products are available at the AMD website")
.body(message)
.expect("Failed to construct email message");
let creds = Credentials::new(config.smtp_user, config.smtp_pass);
let mailer = SmtpTransport::relay(&config.smtp_host)
.expect("Failed to connect to mailerver")
.credentials(creds)
.authentication(vec![Mechanism::Plain])
.build();
mailer.send(&mail).expect("Error sending mail");
println!("Mail was send to {}.", recipient);
}
}
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment