gpipipi 0.2.2

a rust crate for the google play api
Documentation
use crate::{
    DeviceProperties,
    api::{
        endpoint::Requester,
        form::Form,
        login::{AuthToken, checkin, fetch_auth},
    },
    consts,
    error::{FetchAasError, FetchAuthTokenError, IntoGoogleError, LoginError},
};
use std::borrow::Cow;

/// a requester for converting oauth tokens into usable aas tokens
///
/// unless you have a good reason to do this manually, you should run this crate
/// as a binary to convert your token, see [main page](crate)
///
/// create one with [`OAuthRequest::new`]
pub struct OAuthRequest<'a> {
    oauth_token: &'a str,
    email: &'a str,
}

impl<'a> OAuthRequest<'a> {
    /// create a new requester with an oauth token and email, email must be the one used for getting
    /// the oauth token
    #[must_use]
    pub const fn new(oauth_token: &'a str, email: &'a str) -> Self {
        Self { oauth_token, email }
    }

    /// fetch an aas token from an oauth token
    ///
    /// # Errors
    /// - `Request` - when the HTTP request fails
    /// - `Google` -  when Google returns an error
    pub async fn fetch(&self) -> Result<String, FetchAasError> {
        let headers = [
            ("content-type", "application/x-www-form-urlencoded"),
            ("user-agent", consts::AUTH_USER_AGENT),
            ("app", consts::ANDROID_VENDING),
        ];

        let form = Form::new([
            ("Email", self.email),
            ("Token", self.oauth_token),
            ("lang", consts::LANGUAGE),
            ("google_play_services_version", consts::SERVICE_VERSION),
            ("sdk_version", consts::SDK),
            ("device_country", consts::COUNTRY_CODE),
            ("service", consts::SERVICE),
            ("callerPkg", consts::ANDROID_VENDING),
            ("callerSig", consts::CALLER_SIG),
            ("droidguard_results", "null"),
            ("get_accountid", "1"),
            ("ACCESS_TOKEN", "1"),
        ]);

        let request = Requester::new().headers(&headers);
        let mut response = request.form_request("auth", form).await?;

        let result = response.remove("Token").into_google_error_map(|| {
            response
                .remove("Error")
                .unwrap_or(Cow::Borrowed("Google didn't provide a token or an error!"))
                .to_string()
        })?;

        Ok(result.to_string())
    }
}

/// a requester for converting aas tokens into auth tokens
///
/// unless you have a really good reason to use this, you should create a [`crate::LongLivedClient`]
/// with the aas token
///
/// create one with [`AuthTokenRequest::new`]
pub struct AuthTokenRequest<'a> {
    props: &'a DeviceProperties,
    aas_token: &'a str,
    email: &'a str,
}

impl<'a> AuthTokenRequest<'a> {
    /// create a new requester with an aas token and email, email must be the one used for getting
    /// the aas token
    #[must_use]
    pub const fn new(aas_token: &'a str, email: &'a str, props: &'a DeviceProperties) -> Self {
        Self {
            aas_token,
            email,
            props,
        }
    }

    /// fetch an auth token from an aas token
    ///
    /// # Errors
    /// - `Request` - when the HTTP request fails
    /// - `Google` -  when Google returns an error
    pub async fn fetch(&self) -> Result<AuthToken, FetchAuthTokenError> {
        let checkin_response = checkin(self.props).await.map_err(LoginError::from)?;
        let gsf = checkin_response
            .android_id
            .ok_or(LoginError::NoAndroidId())?
            .cast_signed();

        let auth_token = fetch_auth(self.props, self.aas_token, self.email, gsf)
            .await
            .map_err(LoginError::from)?;

        Ok(auth_token)
    }
}