mirror of
https://git.gaycatgirl.sex/husky/free_twitter_api.git
synced 2024-12-22 10:31:23 -05:00
initial stuffz
This commit is contained in:
commit
7bb560b89b
4 changed files with 173 additions and 0 deletions
2
.gitignore
vendored
Normal file
2
.gitignore
vendored
Normal file
|
@ -0,0 +1,2 @@
|
||||||
|
/target
|
||||||
|
/Cargo.lock
|
12
Cargo.toml
Normal file
12
Cargo.toml
Normal file
|
@ -0,0 +1,12 @@
|
||||||
|
[package]
|
||||||
|
name = "free_twitter_api"
|
||||||
|
version = "0.1.0"
|
||||||
|
edition = "2021"
|
||||||
|
|
||||||
|
[dependencies]
|
||||||
|
isahc = "1.7.2"
|
||||||
|
rand = "0.8.5"
|
||||||
|
log = "0.4.17"
|
||||||
|
|
||||||
|
[dev-dependencies]
|
||||||
|
env_logger = "0.10.0"
|
159
src/lib.rs
Normal file
159
src/lib.rs
Normal file
|
@ -0,0 +1,159 @@
|
||||||
|
use std::net::SocketAddr;
|
||||||
|
use std::str::FromStr;
|
||||||
|
use std::sync::Arc;
|
||||||
|
use isahc::auth::{Authentication, Credentials};
|
||||||
|
use isahc::{Body, Request, Response};
|
||||||
|
use isahc::http::HeaderMap;
|
||||||
|
use isahc::prelude::*;
|
||||||
|
use log::debug;
|
||||||
|
|
||||||
|
const USER_AGENTS: [&str; 5] = [
|
||||||
|
"Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/110.0.0.0 Safari/537.36",
|
||||||
|
"Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:109.0) Gecko/20100101 Firefox/109.0",
|
||||||
|
"Mozilla/5.0 (Macintosh; Intel Mac OS X 13_2) AppleWebKit/605.1.15 (KHTML, like Gecko) Version/16.3 Safari/605.1.15",
|
||||||
|
"Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/110.0.0.0 Safari/537.36 Edg/110.0.1587.41",
|
||||||
|
"Mozilla/5.0 (Windows NT 10.0; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/110.0.0.0 Safari/537.36",
|
||||||
|
];
|
||||||
|
|
||||||
|
/// this is what we look for to find the bearer token
|
||||||
|
const SEARCH_STRING: &str = "AAAAAAAAA";
|
||||||
|
/// this is what we look for to find the main.*.js file
|
||||||
|
const MAIN_JS_SEARCH_STRING: &str = "main.";
|
||||||
|
|
||||||
|
struct Proxy {
|
||||||
|
address: String,
|
||||||
|
credentials: Option<Credentials>,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
|
||||||
|
pub enum ProxyError {
|
||||||
|
InvalidProxyFormat,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Clone, Debug)]
|
||||||
|
pub enum BearerError {
|
||||||
|
CouldntGetTwitterDotCom(isahc::Error),
|
||||||
|
CouldntFindMainJsUrl,
|
||||||
|
CouldntGetMainDotJs(isahc::Error),
|
||||||
|
CouldntFindBearerToken,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl FromStr for Proxy {
|
||||||
|
type Err = ProxyError;
|
||||||
|
|
||||||
|
fn from_str(s: &str) -> Result<Self, Self::Err> {
|
||||||
|
let mut parts = s.split(':');
|
||||||
|
let host = parts.next().ok_or(ProxyError::InvalidProxyFormat)?;
|
||||||
|
let port = parts.next().ok_or(ProxyError::InvalidProxyFormat)?;
|
||||||
|
let auth = if let Some(user) = parts.next() {
|
||||||
|
let pass = parts.next().ok_or(ProxyError::InvalidProxyFormat)?;
|
||||||
|
Some(Credentials::new(user, pass))
|
||||||
|
} else {
|
||||||
|
None
|
||||||
|
};
|
||||||
|
Ok(Proxy {
|
||||||
|
address: format!("{}:{}", host, port),
|
||||||
|
credentials: auth,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Clone)]
|
||||||
|
pub struct Client {
|
||||||
|
http_client: Arc<isahc::HttpClient>,
|
||||||
|
proxy: Option<Arc<Proxy>>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Default for Client {
|
||||||
|
fn default() -> Self {
|
||||||
|
Self::new()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Client {
|
||||||
|
pub fn new() -> Self {
|
||||||
|
Client {
|
||||||
|
http_client: Arc::new(isahc::HttpClient::new().expect("failed to create http client")),
|
||||||
|
proxy: None,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn new_with_proxy(proxy: &str) -> Self {
|
||||||
|
let proxy = proxy.parse().unwrap();
|
||||||
|
Client {
|
||||||
|
http_client: Arc::new(isahc::HttpClient::new().expect("failed to create http client")),
|
||||||
|
proxy: Some(Arc::new(proxy)),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn get(&self, url: &str, headers: &HeaderMap) -> Result<Response<Body>, isahc::Error> {
|
||||||
|
let mut request = Request::get(url);
|
||||||
|
if let Some(proxy) = &self.proxy {
|
||||||
|
request = request.proxy(Some(proxy.address.parse().expect("error parsing proxy!")));
|
||||||
|
if let Some(creds) = &proxy.credentials {
|
||||||
|
request = request.proxy_authentication(Authentication::basic());
|
||||||
|
request = request.proxy_credentials(creds.clone());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
let mut request = request.body(())?;
|
||||||
|
request.headers_mut().extend(headers.iter().map(|(k, v)| (k.clone(), v.clone())));
|
||||||
|
self.http_client.send(request)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn get_twitter_bearer_token(&self) -> Result<String, BearerError> {
|
||||||
|
let url = "https://twitter.com";
|
||||||
|
let mut headers = HeaderMap::new();
|
||||||
|
let user_agent = USER_AGENTS[rand::random::<usize>() % USER_AGENTS.len()];
|
||||||
|
headers.insert("user-agent", user_agent.parse().unwrap());
|
||||||
|
|
||||||
|
let mut response = self.get(url, &headers).map_err(BearerError::CouldntGetTwitterDotCom)?;
|
||||||
|
let body = response.text().unwrap();
|
||||||
|
let mut mainjs_url = None;
|
||||||
|
// fixme! this is really fucking cursed
|
||||||
|
for line in body.lines() {
|
||||||
|
if line.contains(MAIN_JS_SEARCH_STRING) {
|
||||||
|
let main_js_start = line.find(MAIN_JS_SEARCH_STRING).unwrap();
|
||||||
|
let main_js_end = line[main_js_start..].find('"').unwrap() + main_js_start;
|
||||||
|
let main_js_url_start = line[0..main_js_start].rfind('"').unwrap();
|
||||||
|
let main_js_url = &line[main_js_url_start + 1..main_js_end];
|
||||||
|
mainjs_url = Some(main_js_url.to_string());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
let mainjs_url = mainjs_url.ok_or(BearerError::CouldntFindMainJsUrl)?;
|
||||||
|
debug!("mainjs_url: {}", mainjs_url);
|
||||||
|
// now get the main.js file
|
||||||
|
let mut response = self.get(&mainjs_url, &headers).map_err(BearerError::CouldntGetMainDotJs)?;
|
||||||
|
let body = response.text().unwrap();
|
||||||
|
let mut token = None;
|
||||||
|
for line in body.lines() {
|
||||||
|
if line.contains(SEARCH_STRING) {
|
||||||
|
let bearer_token_start = line.find(SEARCH_STRING).unwrap();
|
||||||
|
let bearer_token_end = line[bearer_token_start..].find('"').unwrap() + bearer_token_start;
|
||||||
|
let bearer_token = &line[bearer_token_start..bearer_token_end];
|
||||||
|
token = Some(bearer_token.to_string());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
let token = token.ok_or(BearerError::CouldntFindBearerToken)?;
|
||||||
|
Ok(token)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(test)]
|
||||||
|
mod tests {
|
||||||
|
use super::*;
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn world_normal() {
|
||||||
|
let result = 2 + 2;
|
||||||
|
assert_eq!(result, 4);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn get_bearer_token() {
|
||||||
|
env_logger::init();
|
||||||
|
let client = Client::new();
|
||||||
|
let bearer_token = client.get_twitter_bearer_token();
|
||||||
|
println!("{:?}", bearer_token);
|
||||||
|
}
|
||||||
|
}
|
0
src/token_generator.rs
Normal file
0
src/token_generator.rs
Normal file
Loading…
Reference in a new issue