twitter_api/
lib.rs

1#![warn(bad_style)]
2// #![warn(missing_docs)]
3#![warn(unused)]
4#![warn(unused_extern_crates)]
5#![warn(unused_import_braces)]
6#![warn(unused_qualifications)]
7#![warn(unused_results)]
8
9use oauth::RequestBuilder;
10use oauth_client::Token;
11use serde::{Deserialize, Serialize};
12use std::{borrow::Cow, collections::HashMap, str};
13use thiserror::Error;
14
15pub use oauth_client as oauth;
16pub use serde_json;
17
18pub type Result<T> = std::result::Result<T, Error>;
19
20#[derive(Debug, Error)]
21#[non_exhaustive]
22pub enum Error {
23    #[error("OAuth error: {0}")]
24    Oauth(#[from] oauth::Error),
25    #[error("JSON error: {0}")]
26    Json(#[from] serde_json::Error),
27    #[error("decode string error: {0}")]
28    FromUtf8(#[from] str::Utf8Error),
29}
30
31mod api_twitter_oauth {
32    pub const REQUEST_TOKEN: &str = "https://api.twitter.com/oauth/request_token";
33    pub const AUTHORIZE: &str = "https://api.twitter.com/oauth/authorize";
34    pub const ACCESS_TOKEN: &str = "https://api.twitter.com/oauth/access_token";
35}
36
37mod api_twitter_soft {
38    pub const UPDATE_STATUS: &str = "https://api.twitter.com/1.1/statuses/update.json";
39    pub const HOME_TIMELINE: &str = "https://api.twitter.com/1.1/statuses/home_timeline.json";
40}
41
42#[derive(Clone, Debug, Serialize, Deserialize)]
43pub struct Tweet {
44    pub created_at: String,
45    pub text: String,
46}
47
48impl Tweet {
49    pub fn parse_timeline(json_str: impl AsRef<str>) -> Result<Vec<Tweet>> {
50        let tweets = serde_json::from_str(json_str.as_ref())?;
51        Ok(tweets)
52    }
53}
54
55fn split_query(query: &str) -> HashMap<Cow<'_, str>, Cow<'_, str>> {
56    let mut param = HashMap::new();
57    for q in query.split('&') {
58        let (k, v) = q.split_once('=').unwrap();
59        let _ = param.insert(k.into(), v.into());
60    }
61    param
62}
63
64pub fn get_request_token<RB>(
65    consumer: &Token<'_>,
66    client: &RB::ClientBuilder,
67) -> Result<Token<'static>>
68where
69    RB: RequestBuilder,
70    RB::ReturnValue: AsRef<[u8]>,
71{
72    let bytes: RB::ReturnValue = oauth::get::<RB>(
73        api_twitter_oauth::REQUEST_TOKEN,
74        consumer,
75        None,
76        None,
77        client,
78    )?;
79    let resp = str::from_utf8(bytes.as_ref())?;
80    let param = split_query(resp);
81    let token = Token::new(
82        param.get("oauth_token").unwrap().to_string(),
83        param.get("oauth_token_secret").unwrap().to_string(),
84    );
85    Ok(token)
86}
87
88pub fn get_authorize_url(request: &Token<'_>) -> String {
89    format!(
90        "{}?oauth_token={}",
91        api_twitter_oauth::AUTHORIZE,
92        request.key
93    )
94}
95
96pub fn get_access_token<RB>(
97    consumer: &Token<'_>,
98    request: &Token<'_>,
99    pin: &str,
100    client: &RB::ClientBuilder,
101) -> Result<Token<'static>>
102where
103    RB: RequestBuilder,
104    RB::ReturnValue: AsRef<[u8]>,
105{
106    let mut param = HashMap::new();
107    let _ = param.insert("oauth_verifier".into(), pin.into());
108    let bytes = oauth::get::<RB>(
109        api_twitter_oauth::ACCESS_TOKEN,
110        consumer,
111        Some(request),
112        Some(&param),
113        client,
114    )?;
115    let resp = str::from_utf8(bytes.as_ref())?;
116    let param = split_query(resp);
117    let token = Token::new(
118        param.get("oauth_token").unwrap().to_string(),
119        param.get("oauth_token_secret").unwrap().to_string(),
120    );
121    Ok(token)
122}
123
124/// function to update the status
125/// This function takes as arguments the consumer key, the access key, and the status (obviously)
126pub fn update_status<RB>(
127    consumer: &Token<'_>,
128    access: &Token<'_>,
129    status: &str,
130    client: &RB::ClientBuilder,
131) -> Result<()>
132where
133    RB: RequestBuilder,
134    RB::ReturnValue: AsRef<[u8]>,
135{
136    let mut param = HashMap::new();
137    let _ = param.insert("status".into(), status.into());
138    let _ = oauth::post::<RB>(
139        api_twitter_soft::UPDATE_STATUS,
140        consumer,
141        Some(access),
142        Some(&param),
143        client,
144    )?;
145    Ok(())
146}
147
148pub fn get_last_tweets<RB>(
149    consumer: &Token<'_>,
150    access: &Token<'_>,
151    client: &RB::ClientBuilder,
152) -> Result<Vec<Tweet>>
153where
154    RB: RequestBuilder,
155    RB::ReturnValue: AsRef<[u8]>,
156{
157    let bytes = oauth::get::<RB>(
158        api_twitter_soft::HOME_TIMELINE,
159        consumer,
160        Some(access),
161        None,
162        client,
163    )?;
164    let last_tweets_json = str::from_utf8(bytes.as_ref())?;
165    let ts = Tweet::parse_timeline(&last_tweets_json)?;
166    Ok(ts)
167}