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();
48 let url
= format!("https://www.steg-electronics.ch/fr/search?suche={}", to_find
);
50 let client
= reqwest
::blocking
::Client
::new();
53 println!("Request: {}", url
);
54 let resp
= client
.get(&url
).send()?
;
56 if resp
.status().is_success() {
57 let html
= resp
.text()?
;
58 let document
= Html
::parse_document(&html
);
60 // A vector of (<title>, <url>), empty if nothing matches.
61 let match_items
: Vec
<(String
, String
)> =
62 document
.select(&selector
).filter_map(
64 if let (Some(title
), Some(url_to_item
)) = (element
.value().attr("title"), element
.value().attr("href")) {
65 if title
.find(to_match
).is_some() {
66 return Some((String
::from(title
), String
::from(url_to_item
)))
73 if match_items
.is_empty() {
74 println!("No matches...");
75 } else if send_email(&match_items
[..], &config
.smtp_login
, &config
.smtp_password
) {
76 println!("Some matches has been found! An e-mail has been sent!");
79 println!("Unable to send e-mail");
82 println!("Request unsuccessful:\n{:#?}", resp
);
85 thread
::sleep(time
::Duration
::from_secs(60)); // 1 min.
89 /// Return 'true' if the email has been successfully sent.
90 fn send_email(items
: &[(String
, String
)], login
: &str, password
: &str) -> bool
{
96 format!("{}\n<li><a href=\"{}\">{}</a></li>", acc
, url
, title
)
100 let email
= Email
::builder()
101 // Addresses can be specified by the tuple (email, alias)
102 .to(("greg.burri@gmail.com", "Greg Burri"))
103 .from("redmine@d-lan.net")
104 .subject("RTX 3080 AVAILABLE!")
105 .html(format!("<ul>{}</ul>", items_list_html
))
110 let mut tls_builder
= TlsConnector
::builder();
111 tls_builder
.min_protocol_version(Some(Protocol
::Tlsv10
));
113 ClientTlsParameters
::new(
114 "mail.gandi.net".to_string(),
115 tls_builder
.build().unwrap()
119 SmtpClient
::new(("mail.gandi.net", 465), ClientSecurity
::Wrapper(tls_parameters
)).unwrap()
120 .authentication_mechanism(Mechanism
::Login
)
122 .credentials(Credentials
::new(String
::from(login
), String
::from(password
)))
123 .connection_reuse(ConnectionReuseParameters
::ReuseUnlimited
)
126 let result
= mailer
.send(email
);
129 println!("Error when sending E-mail:\n{:?}", &result
);