use reqwest::header::HeaderMap;
use reqwest::Client;
use serde::Deserialize;
use serde_json::Value;
use std::collections::HashMap;
use std::error::Error;
type Res<T> = Result<T, Box<dyn Error>>;
#[derive(Debug, Deserialize, Clone)]
struct HistoryEntry {
time: String,
delay: i64,
}
#[derive(Debug, Deserialize, Clone)]
pub struct Proxy {
pub alive: bool,
extra: Value,
history: Vec<HistoryEntry>,
pub id: Option<String>,
pub name: String,
tfo: bool,
#[serde(rename = "type")]
pub proxy_type: String,
pub udp: bool,
xudp: bool,
pub now: Option<String>,
pub all: Option<Vec<String>>,
}
#[derive(Debug, Deserialize, Default, Clone)]
pub struct NodesData {
proxies: HashMap<String, Proxy>,
}
#[derive(Default, Clone)]
pub struct Clash {
pub base_url: String,
headers: HeaderMap,
pub passwd: String,
pub proxies: HashMap<String, Proxy>,
pub group_index: Vec<String>,
pub delays: HashMap<String, u32>,
client: Client,
}
impl Clash {
pub async fn new(base_url: &str, passwd: &str) -> Res<Self> {
let mut headers = HeaderMap::new();
headers.insert(
"Authorization",
("Bearer".to_string() + passwd).parse().unwrap(),
);
let client = Client::new();
let resp = client
.get(base_url.to_string() + "/proxies")
.headers(headers.clone())
.send()
.await?
.text()
.await?;
let proxies: NodesData = serde_json::from_str(&resp).unwrap();
let mut proxies = proxies.proxies;
let mut group_index: Vec<String> = Vec::new();
proxies.iter_mut().for_each(|(k, v)| {
if v.all.is_some() {
group_index.push(k.clone());
if let Some(all) = &mut v.all {
all.sort();
}
}
});
group_index.sort_by(|a, b| b.cmp(a));
let new_clash = Self {
base_url: base_url.to_string(),
passwd: passwd.to_string(),
headers,
proxies,
group_index,
client,
delays: HashMap::new(),
};
Ok(new_clash)
}
pub async fn check_connection(&self) -> Res<bool> {
let url = format!("{}/version", self.base_url.clone());
let resp = self
.client
.get(url)
.headers(self.headers.clone())
.send()
.await?
.json::<HashMap<String, Value>>()
.await?;
if resp.get("version").unwrap().is_string() {
Ok(true)
} else {
Ok(false)
}
}
pub async fn select_proxy(&self, selector: &str, target: &str) -> Res<String> {
let body = format!("{{\"name\":\"{}\"}}", target);
let url = format!("{}/proxies/{}", self.base_url.clone(), selector);
let resp = self
.client
.put(url)
.headers(self.headers.clone())
.body(body)
.send()
.await?
.text()
.await?;
Ok(resp)
}
pub async fn query_node_delay(&mut self, target: &str) -> Res<u32> {
let url = format!(
"{}/proxies/{}/delay{}",
self.base_url.clone(),
target,
"?url=http://www.gstatic.com/generate_204&timeout=1000"
);
let resp = self
.client
.get(url)
.headers(self.headers.clone())
.send()
.await?
.json::<HashMap<String, u32>>()
.await?;
self.delays.insert(target.to_owned(), resp["delay"]);
Ok(resp["delay"])
}
pub async fn query_group_delay(&mut self, target: &str) -> Res<HashMap<String, u32>> {
let url = format!(
"{}/group/{}/delay{}",
self.base_url.clone(),
target,
"?url=http://www.gstatic.com/generate_204&timeout=1000"
);
let resp = self
.client
.get(url.clone())
.headers(self.headers.clone())
.send()
.await?
.json::<HashMap<String, u32>>()
.await?;
resp.iter().for_each(|(k, v)| {
self.delays.insert(k.clone(), *v);
});
println!("{:#?}", url.clone());
Ok(resp)
}
pub async fn fetch_logs(&self, _level: &str) -> Res<String> {
let url = format!("{}/logs", self.base_url.clone());
let resp = self.client.get(url).send().await?;
let body = resp.text().await?;
Ok(body)
}
}