1 use std
::{ thread
, time
, fs
::File
};
2 use scraper
::{ Html
, Selector
};
9 ConnectionReuseParameters
,
16 use lettre_email
::Email
;
17 use native_tls
::{Protocol
, TlsConnector
};
18 use ron
::{ de
::from_reader
, ser
::to_writer
};
19 use serde
::{ Deserialize
, Serialize
};
21 #[derive(Debug, Deserialize, Serialize)]
27 fn get_default_config() -> Config
{ Config
{ smtp_login
: "login".to_string(), smtp_password
: "password".to_string() } }
28 const FILE_CONF
: &str = "config.ron";
30 fn main() -> Result
<(), Box
<dyn std
::error
::Error
>> {
31 println!("I need a RTX 3080 right now :)");
33 let to_find
= "RTX 3080";
34 let to_match
= "3080";
37 match File
::open(FILE_CONF
) {
38 Ok(file
) => match from_reader(file
) { Ok(c
) => c
, Err(e
) => panic!("Failed to load config: {}", e
)},
40 let file
= File
::create(FILE_CONF
)?
;
41 let default_config
= get_default_config();
42 to_writer(file
, &default_config
)?
;
47 let selector
= Selector
::parse("div.productGridElement > h2 > a:nth-child(1)").unwrap();
50 let url
= format!("https://www.steg-electronics.ch/fr/search?suche={}", to_find
);
51 println!("Request: {}", url
);
52 let resp
= reqwest
::blocking
::get(&url
)?
;
54 if resp
.status().is_success() {
55 let html
= resp
.text()?
;
56 let document
= Html
::parse_document(&html
);
58 // A vector of (<title>, <url>), empty if nothing matches.
59 let match_items
: Vec
<(String
, String
)> =
60 document
.select(&selector
).filter_map(
62 if let (Some(title
), Some(url_to_item
)) = (element
.value().attr("title"), element
.value().attr("href")) {
63 if title
.find(to_match
).is_some() {
64 return Some((String
::from(title
), String
::from(url_to_item
)))
71 if match_items
.is_empty() {
72 println!("No matches...");
73 } else if send_email(&match_items
[..], &config
.smtp_login
, &config
.smtp_password
) {
74 println!("Some matches has been found! An e-mail has been sent!");
77 println!("Unable to send e-mail");
80 println!("Request unsuccessful:\n{:#?}", resp
);
83 thread
::sleep(time
::Duration
::from_secs(60)); // 1 min.
87 /// Return 'true' if the email has been successfully sent.
88 fn send_email(items
: &[(String
, String
)], login
: &str, password
: &str) -> bool
{
94 format!("{}\n<li><a href=\"{}\">{}</a></li>", acc
, url
, title
)
98 let email
= Email
::builder()
99 // Addresses can be specified by the tuple (email, alias)
100 .to(("greg.burri@gmail.com", "Greg Burri"))
101 .from("redmine@d-lan.net")
102 .subject("RTX 3080 AVAILABLE!")
103 .html(format!("<ul>{}</ul>", items_list_html
))
108 let mut tls_builder
= TlsConnector
::builder();
109 tls_builder
.min_protocol_version(Some(Protocol
::Tlsv10
));
111 ClientTlsParameters
::new(
112 "mail.gandi.net".to_string(),
113 tls_builder
.build().unwrap()
117 SmtpClient
::new(("mail.gandi.net", 465), ClientSecurity
::Wrapper(tls_parameters
)).unwrap()
118 .authentication_mechanism(Mechanism
::Login
)
120 .credentials(Credentials
::new(String
::from(login
), String
::from(password
)))
121 .connection_reuse(ConnectionReuseParameters
::ReuseUnlimited
)
124 let result
= mailer
.send(email
);
127 println!("Error when sending E-mail:\n{:?}", &result
);