nadeo_api/client/
client_builder.rs

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
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
use crate::auth::o_auth::OAuthInfo;
use crate::auth::{AuthInfo, AuthType};
use crate::request::metadata::MetaData;
use crate::Result;
use crate::{auth, Error, NadeoClient};
use futures::future::join3;
use reqwest::Client;
use thiserror::Error;

type EMail = String;
type Username = String;
type Password = String;
type Identifier = String;
type Secret = String;

#[derive(Debug, Clone, Default)]
pub struct NadeoClientBuilder {
    normal_auth: Option<(EMail, Password)>,
    server_auth: Option<(Username, Password)>,
    o_auth: Option<(Identifier, Secret)>,
    user_agent: Option<String>,
}

impl NadeoClientBuilder {
    /// Adds credentials for using [`AuthType::NadeoServices`] and [`AuthType::NadeoLiveServices`].
    pub fn with_normal_auth(mut self, email: &str, password: &str) -> Self {
        self.normal_auth = Some((email.to_string(), password.to_string()));

        self
    }

    /// Adds credentials for using [`AuthType::NadeoServices`] and [`AuthType::NadeoLiveServices`] using a server account.
    /// [`NadeoClientBuilder`] will prefer [`NadeoClientBuilder::with_normal_auth`] if `with_normal_auth` and `with_server_auth` are added.
    pub fn with_server_auth(mut self, username: &str, password: &str) -> Self {
        self.server_auth = Some((username.to_string(), password.to_string()));

        self
    }

    /// Adds credentials for using [`AuthType::OAuth`].
    pub fn with_oauth(mut self, identifier: &str, secret: &str) -> Self {
        self.o_auth = Some((identifier.to_string(), secret.to_string()));

        self
    }

    /// Adds a UserAgent which is sent along with each [`NadeoRequest`].
    /// This is required because Ubisoft blocks some default UserAgents.
    /// An example of a *good* UserAgent is:
    /// - `My amazing app / my.email.address@gmail.com`
    ///
    /// # Examples
    ///
    /// ```rust
    /// # use nadeo_api::NadeoClient;
    /// let client = NadeoClient::builder()
    ///     .with_normal_auth("my_email", "my_password")
    ///     .user_agent("API Testing / mustermann.max@gmail.com") // not a real email
    ///     .build()
    ///     .await?;
    /// ```
    ///
    /// [`NadeoRequest`]: crate::NadeoRequest
    pub fn user_agent(mut self, user_agent: &str) -> Self {
        self.user_agent = Some(user_agent.to_string());

        self
    }

    /// Trys to build a [`NadeoClient`].
    pub async fn build(self) -> Result<NadeoClient> {
        if self.o_auth.is_none() && self.normal_auth.is_none() && self.server_auth.is_none() {
            return Err(Error::from(NadeoClientBuilderError::MissingCredentials));
        }
        if self.user_agent.is_none() {
            return Err(Error::from(NadeoClientBuilderError::MissingUserAgent));
        }

        let meta_data = MetaData {
            user_agent: self.user_agent.unwrap(),
        };

        let client = Client::new();

        // Ubisoft auth ticket
        let mut ticket = String::new();
        if let Some(ref auth) = self.normal_auth {
            ticket = auth::get_ubi_auth_ticket(&auth.0, &auth.1, &meta_data, &client).await?;
        }

        // NadeoServices
        let normal_auth_future = async {
            if self.normal_auth.is_some() {
                Some(AuthInfo::new(AuthType::NadeoServices, &ticket, &meta_data, &client).await)
            } else if let Some((ref username, ref password)) = self.server_auth {
                Some(
                    AuthInfo::new_server(
                        AuthType::NadeoServices,
                        &meta_data,
                        username,
                        password,
                        &client,
                    )
                    .await,
                )
            } else {
                None
            }
        };
        // NadeoLiveServices
        let live_auth_future = async {
            if self.normal_auth.is_some() {
                Some(AuthInfo::new(AuthType::NadeoLiveServices, &ticket, &meta_data, &client).await)
            } else if let Some((ref username, ref password)) = self.server_auth {
                Some(
                    AuthInfo::new_server(
                        AuthType::NadeoLiveServices,
                        &meta_data,
                        username,
                        password,
                        &client,
                    )
                    .await,
                )
            } else {
                None
            }
        };
        // OAuth
        let oauth_future = async {
            if let Some(auth) = self.o_auth {
                Some(OAuthInfo::new(&auth.0, &auth.1, &client).await)
            } else {
                None
            }
        };
        // execute requests
        let (normal_auth_res, live_auth_res, oauth_res) =
            join3(normal_auth_future, live_auth_future, oauth_future).await;

        let mut normal_auth = None;
        let mut live_auth = None;
        let mut o_auth = None;

        // extract results
        if let Some(auth) = normal_auth_res {
            normal_auth = Some(auth?);
        }
        if let Some(auth) = live_auth_res {
            live_auth = Some(auth?);
        }
        if let Some(auth) = oauth_res {
            o_auth = Some(auth?);
        }

        Ok(NadeoClient {
            client,
            normal_auth,
            live_auth,
            o_auth,
            meta_data,
        })
    }
}

#[derive(Error, Debug)]
pub enum NadeoClientBuilderError {
    #[error("No credentials were provided. At least 1 auth method is required")]
    MissingCredentials,
    #[error("No UserAgent was provided")]
    MissingUserAgent,
}