1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
use std::ops::ControlFlow;
use std::time::Duration;

use grammers_client::session::Session;
use grammers_client::{Client, Config, ReconnectionPolicy, SignInError};
use log::{error, info};

use crate::prompt::prompt;
pub const SESSION_FILE: &str = "./data/telepAIr.session";

struct ReconectPolicy;

impl ReconnectionPolicy for ReconectPolicy {
    fn should_retry(&self, attempts: usize) -> std::ops::ControlFlow<(), std::time::Duration> {
        if attempts < 4 {
            let duration = u64::pow(2, attempts as _);
            ControlFlow::Continue(Duration::from_millis(duration))
        } else {
            ControlFlow::Break(())
        }
    }
}

pub async fn establish_telegram_connection(
    api_id: i32,
    api_hash: String,
) -> Result<(Client, bool), Box<dyn std::error::Error>> {
    info!("Connecting to Telegram...");
    let session = match Session::load_file_or_create(SESSION_FILE) {
        Ok(session) => {
            info!("Loaded our saved session");
            Ok(session)
        }
        Err(e) => {
            error!("Failed to load session, {e}");
            Err(e)
        }
    }?;

    let client = Client::connect(Config {
        session,
        api_id,
        api_hash,
        params: grammers_client::InitParams {
            reconnection_policy: &ReconectPolicy,
            ..Default::default()
        },
    })
    .await?;
    info!("Telegram Connected!");

    // If we can't save the session, sign out once we're done.
    let mut sign_out = false;

    if !client.is_authorized().await? {
        println!("Signing in...");
        let phone = prompt("Enter your phone number (international format): ")?;
        let token = client.request_login_code(&phone).await?;
        let code = prompt("Enter the code you received: ")?;
        let signed_in = client.sign_in(&token, &code).await;
        match signed_in {
            Err(SignInError::PasswordRequired(password_token)) => {
                // Note: this `prompt` method will echo the password in the console.
                //       Real code might want to use a better way to handle this.
                let hint = password_token.hint().unwrap_or("None");
                let prompt_message = format!("Enter the password (hint {}): ", &hint);
                let password = prompt(prompt_message.as_str())?;

                client
                    .check_password(password_token, password.trim())
                    .await?;
            }
            Ok(_) => (),
            Err(e) => panic!("{}", e),
        };
        info!("Signed in!");
        match client.session().save_to_file(SESSION_FILE) {
            Ok(_) => {
                info!("Saved our session")
            }
            Err(e) => {
                println!("NOTE: failed to save the session, will sign out when done: {e}");
                sign_out = true;
            }
        }
    }

    Ok((client, sign_out))
}