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 type Result
<T
> = std
::result
::Result
<T
, Box
<dyn std
::error
::Error
>>;
23 #[derive(Debug, Deserialize, Serialize)]
30 fn default() -> Self {
31 Config
{ smtp_login
: "login".to_string(), smtp_password
: "password".to_string() }
34 fn read(file_path
: &str) -> Result
<Config
> {
35 match File
::open(file_path
) {
36 Ok(file
) => from_reader(file
).map_err(|e
| e
.into()),
38 let file
= File
::create(file_path
)?
;
39 let default_config
= Config
::default();
40 to_writer(file
, &default_config
)?
;
47 const FILE_CONF
: &str = "config.ron";
49 fn main() -> Result
<()> {
50 println!("I need a RTX 3080 right now :)");
52 let to_find
= "RTX 3080";
53 let to_match
= "3080";
55 let config
= Config
::read(FILE_CONF
)?
;
57 let selector
= Selector
::parse("div.productGridElement > h2 > a:nth-child(1)").unwrap();
58 let url
= format!("https://www.steg-electronics.ch/fr/search?suche={}", to_find
);
60 let client
= reqwest
::blocking
::Client
::new();
63 println!("Request: {}", url
);
65 match client
.get(&url
).send() {
67 if resp
.status().is_success() {
68 let html
= resp
.text()?
;
69 let document
= Html
::parse_document(&html
);
71 // A vector of (<title>, <url>), empty if nothing matches.
72 let match_items
: Vec
<(String
, String
)> =
73 document
.select(&selector
).filter_map(
75 if let (Some(title
), Some(url_to_item
)) = (element
.value().attr("title"), element
.value().attr("href")) {
76 if title
.find(to_match
).is_some() {
77 return Some((String
::from(title
), String
::from(url_to_item
)))
84 if match_items
.is_empty() {
85 println!("No matches -_-ยดยด");
86 } else if send_email(&match_items
[..], &config
.smtp_login
, &config
.smtp_password
) {
87 println!("Some matches has been found! An e-mail has been sent!");
90 println!("Unable to send e-mail");
93 println!("Request unsuccessful:\n{:#?}", resp
);
96 println!("Error during request: {:?}", error
)
99 thread
::sleep(time
::Duration
::from_secs(60)); // 1 min.
103 /// Return 'true' if the email has been successfully sent.
104 fn send_email(items
: &[(String
, String
)], login
: &str, password
: &str) -> bool
{
106 let items_list_html
=
109 |acc
, (title
, url
)| {
110 format!("{}\n<li><a href=\"{}\">{}</a></li>", acc
, url
, title
)
114 let email
= Email
::builder()
115 // Addresses can be specified by the tuple (email, alias)
116 .to(("greg.burri@gmail.com", "Greg Burri"))
117 .from("redmine@d-lan.net")
118 .subject("RTX 3080 AVAILABLE!")
119 .html(format!("<ul>{}</ul>", items_list_html
))
124 let mut tls_builder
= TlsConnector
::builder();
125 tls_builder
.min_protocol_version(Some(Protocol
::Tlsv10
));
127 ClientTlsParameters
::new(
128 "mail.gandi.net".to_string(),
129 tls_builder
.build().unwrap()
133 SmtpClient
::new(("mail.gandi.net", 465), ClientSecurity
::Wrapper(tls_parameters
)).unwrap()
134 .authentication_mechanism(Mechanism
::Login
)
136 .credentials(Credentials
::new(String
::from(login
), String
::from(password
)))
137 .connection_reuse(ConnectionReuseParameters
::ReuseUnlimited
)
140 let result
= mailer
.send(email
);
143 println!("Error when sending E-mail:\n{:?}", &result
);