1use reqwest::{Error, Response};
2use serde_json::Value;
3use std::collections::HashMap;
4use std::time::Duration;
5use twapi_oauth::calc_oauth_header;
6
7use crate::build_client;
8
9pub async fn get_bearer_token_response(
10 consumer_key: &str,
11 consumer_secret: &str,
12 timeout_sec: Option<Duration>,
13) -> Result<Response, Error> {
14 let key = base64::encode(&format!("{}:{}", consumer_key, consumer_secret));
15 let client = build_client(timeout_sec);
16 client
17 .post("https://api.twitter.com/oauth2/token")
18 .header(
19 "Content-Type",
20 "application/x-www-form-urlencoded;charset=UTF-8",
21 )
22 .header("Authorization", &format!("Basic {}", key))
23 .body("grant_type=client_credentials")
24 .send()
25 .await
26}
27
28pub async fn get_bearer_token(
29 consumer_key: &str,
30 consumer_secret: &str,
31 timeout_sec: Option<Duration>,
32) -> Result<Option<String>, Error> {
33 let json: Value = get_bearer_token_response(consumer_key, consumer_secret, timeout_sec)
34 .await?
35 .json()
36 .await?;
37 match json["access_token"].as_str() {
38 Some(access_token) => Ok(Some(access_token.to_string())),
39 None => Ok(None),
40 }
41}
42
43pub async fn request_token_response(
44 consumer_key: &str,
45 consumer_secret: &str,
46 oauth_callback: &str,
47 x_auth_access_type: Option<&str>,
48 timeout_sec: Option<Duration>,
49) -> Result<Response, Error> {
50 let uri = "https://api.twitter.com/oauth/request_token";
51 let mut header_options = vec![("oauth_callback", oauth_callback)];
52 if let Some(x_auth_access_type) = x_auth_access_type {
53 header_options.push(("x_auth_access_type", x_auth_access_type));
54 }
55 let signed = calc_oauth_header(
56 &format!("{}&", consumer_secret),
57 consumer_key,
58 &header_options,
59 "POST",
60 uri,
61 &vec![],
62 );
63 let client = build_client(timeout_sec);
64 client
65 .post(uri)
66 .header("Authorization", &format!("OAuth {}", signed))
67 .send()
68 .await
69}
70
71pub async fn request_token(
72 consumer_key: &str,
73 consumer_secret: &str,
74 oauth_callback: &str,
75 x_auth_access_type: Option<&str>,
76 timeout_sec: Option<Duration>,
77) -> Result<HashMap<String, String>, Error> {
78 let response = request_token_response(
79 consumer_key,
80 consumer_secret,
81 oauth_callback,
82 x_auth_access_type,
83 timeout_sec,
84 )
85 .await?;
86 Ok(parse_oauth_body(response).await)
87}
88
89pub async fn access_token_response(
90 consumer_key: &str,
91 consumer_secret: &str,
92 oauth_token: &str,
93 oauth_token_secret: &str,
94 oauth_verifier: &str,
95 timeout_sec: Option<Duration>,
96) -> Result<Response, Error> {
97 let uri = "https://api.twitter.com/oauth/access_token";
98 let signed = calc_oauth_header(
99 &format!("{}&{}", consumer_secret, oauth_token_secret),
100 consumer_key,
101 &vec![
102 ("oauth_token", oauth_token),
103 ("oauth_verifier", oauth_verifier),
104 ],
105 "POST",
106 uri,
107 &vec![],
108 );
109 let client = build_client(timeout_sec);
110 client
111 .post(uri)
112 .header("Authorization", &format!("OAuth {}", signed))
113 .send()
114 .await
115}
116
117pub async fn access_token(
118 consumer_key: &str,
119 consumer_secret: &str,
120 oauth_token: &str,
121 oauth_token_secret: &str,
122 oauth_verifier: &str,
123 timeout_sec: Option<Duration>,
124) -> Result<HashMap<String, String>, Error> {
125 let response = access_token_response(
126 consumer_key,
127 consumer_secret,
128 oauth_token,
129 oauth_token_secret,
130 oauth_verifier,
131 timeout_sec,
132 )
133 .await?;
134 Ok(parse_oauth_body(response).await)
135}
136
137async fn parse_oauth_body(response: Response) -> HashMap<String, String> {
138 let status = response.status();
139 let mut result = HashMap::new();
140 match response.text().await {
141 Ok(body) => {
142 if status.is_success() {
143 for item in body.split("&") {
144 let mut pair = item.split("=");
145 if let Some(key) = pair.next() {
146 result.insert(key.to_owned(), pair.next().unwrap_or("").to_owned());
147 }
148 }
149 } else {
150 result.insert("twapi_request_body".to_owned(), body.clone());
151 result.insert(
152 "twapi_request_status_code".to_owned(),
153 status.as_str().to_owned(),
154 );
155 }
156 }
157 Err(_) => {}
158 }
159 result
160}