rustybook-messenger 0.2.1

Messenger client for Rustybook
Documentation
use rustybook_extractor::html::extract_user_name;
use rustybook_http::client::Client as HttpClient;
use tracing::{
    debug,
    trace,
    warn,
};

use super::extract::{
    extract_client_revision,
    extract_fb_dtsg,
    extract_jazoest,
    extract_lsd,
    extract_mqtt_config,
};
use super::http::build_http_client;
use super::{
    DEFAULT_USER_AGENT,
    State,
};
use crate::auth::load_cookies;
use crate::error::MessengerError;

impl State {
    pub async fn from_cookies_file(
        path: &str,
        user_agent: Option<&str>,
        proxy: Option<&str>,
    ) -> Result<Self, MessengerError> {
        let auth = load_cookies(path)?;
        let user_agent = user_agent.unwrap_or(DEFAULT_USER_AGENT).to_string();
        let http = build_http_client(&auth.cookie_header, &user_agent, proxy)?;

        let mut state = State {
            user_id: auth.user_id.clone(),
            user_agent,
            cookie_header: auth.cookie_header,
            http: Some(http),
            ..State::default()
        };

        state.bootstrap().await?;
        Ok(state)
    }

    pub async fn from_shared(
        user_id: String,
        cookie_header: String,
        http: HttpClient,
        user_agent: Option<&str>,
    ) -> Result<Self, MessengerError> {
        let user_agent = user_agent.unwrap_or(DEFAULT_USER_AGENT).to_string();

        let mut state = State {
            user_id,
            user_agent,
            cookie_header,
            http: Some(http),
            ..State::default()
        };

        state.bootstrap().await?;
        Ok(state)
    }

    async fn bootstrap(&mut self) -> Result<(), MessengerError> {
        let html = self.fetch_bootstrap_html().await?;

        self.user_name = extract_user_name(&html).map_err(|error| {
            MessengerError::State(format!("failed to extract user name from html: {error}"))
        })?;
        self.fb_dtsg = extract_fb_dtsg(&html)?;
        self.lsd = extract_lsd(&html)?;
        self.jazoest = extract_jazoest(&html)?;
        self.client_revision = extract_client_revision(&html);

        let mqtt_config = extract_mqtt_config(&html).ok_or_else(|| {
            MessengerError::State("failed to extract mqtt config from bootstrap html".to_string())
        })?;
        self.mqtt_client_id = mqtt_config.client_id;
        self.mqtt_app_id = mqtt_config.app_id;
        self.region = mqtt_config.region;

        if self.user_name.is_none() {
            trace!("user name token not found in bootstrap html");
        }

        match self.fetch_sequence_id().await {
            Ok(sequence_id) => {
                self.sequence_id = Some(sequence_id);
            }
            Err(error) => {
                warn!(?error, "failed to fetch initial sequence id");
                self.sequence_id = None;
            }
        }

        debug!(
            user_name = self.user_name.as_deref().unwrap_or(""),
            has_fb_dtsg = self.fb_dtsg.is_some(),
            has_lsd = self.lsd.is_some(),
            has_jazoest = self.jazoest.is_some(),
            client_revision = self.client_revision,
            region = self.region,
            mqtt_client_id = self.mqtt_client_id,
            sequence_id = self.sequence_id,
            mqtt_app_id = self.mqtt_app_id,
            ls_app_id = self.ls_app_id,
            ls_version_id = self.ls_version_id,
            "messenger bootstrap state"
        );

        Ok(())
    }
}