1use anyhow::{bail, ensure, Result};
3use std::{
4 io::Write,
5 thread,
6 time::{Duration, Instant},
7};
8use wechat_protocol::{
9 is_retryable_login_poll_error, AccountState, DeviceProfile, LoginStatus, NativeClient,
10 NativeOptions,
11};
12
13pub fn login() -> Result<(NativeClient, AccountState)> {
14 let client = NativeClient::new(NativeOptions::default())?;
15 let device = DeviceProfile::generate("UnifiedPCLinux x86_64".into())?;
16 let qr_directory = wechat_platform::private_tempdir("wechat-protocol-example-")?;
17 let qr_path = qr_directory.path().join("login.png");
18 let mut attempt = client.start_qr_login(device, "Rust protocol example")?;
19 let mut qr = wechat_platform::OpenOptions::new()
20 .write(true)
21 .create_new(true)
22 .mode(0o600)
23 .open(&qr_path)?;
24 qr.write_all(&attempt.qr_png)?;
25 drop(qr);
26 eprintln!("Scan the private QR image: {}", qr_path.display());
27 eprintln!("Approve this example on the test account's phone.");
28 let deadline = Instant::now() + Duration::from_secs(600);
29 loop {
30 ensure!(Instant::now() < deadline, "Login example timed out");
31 match client.poll_login_guarded(&mut attempt, deadline, || false) {
32 Ok(LoginStatus::Approved) => {
33 let state = client.complete_login(&mut attempt)?;
34 eprintln!("Login complete; credentials remain in memory.");
35 return Ok((client, state));
36 }
37 Ok(LoginStatus::Pending | LoginStatus::Scanned) => {}
38 Ok(status) => bail!("Login stopped: {status:?}"),
39 Err(error) if is_retryable_login_poll_error(&error) => {}
40 Err(error) => return Err(error),
41 }
42 thread::sleep(Duration::from_secs(2));
45 }
46}