From b59f18e358460b40681e5a439d87a50ffdf170b8 Mon Sep 17 00:00:00 2001 From: YK Date: Sat, 12 Apr 2025 22:05:46 +0300 Subject: [PATCH] add type definition, basic request building --- .gitignore | 2 + Cargo.lock | 43 ++++++++++++++ Cargo.toml | 5 ++ src/main.rs | 166 +++++++++++++++++++++++++++++++++++++++++++++++++++- 4 files changed, 214 insertions(+), 2 deletions(-) diff --git a/.gitignore b/.gitignore index ea8c4bf..582fd6d 100644 --- a/.gitignore +++ b/.gitignore @@ -1 +1,3 @@ /target +cookie.txt +.env diff --git a/Cargo.lock b/Cargo.lock index 99b5cc0..0b9daa8 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -8,6 +8,12 @@ version = "2.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "512761e0bb2578dd7380c6baaa0f4ce03e84f95e960231d1dec8bf4d7d6e2627" +[[package]] +name = "anyhow" +version = "1.0.97" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dcfed56ad506cb2c684a14971b8861fdc3baaaae314b9e5f9bb532cbe3ba7a4f" + [[package]] name = "base64" version = "0.22.1" @@ -35,6 +41,26 @@ version = "1.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd" +[[package]] +name = "const_format" +version = "0.2.34" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "126f97965c8ad46d6d9163268ff28432e8f6a1196a55578867832e3049df63dd" +dependencies = [ + "const_format_proc_macros", +] + +[[package]] +name = "const_format_proc_macros" +version = "0.2.34" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1d57c2eccfb16dbac1f4e61e206105db5820c9d26c3c472bc17c774259ef7744" +dependencies = [ + "proc-macro2", + "quote", + "unicode-xid", +] + [[package]] name = "cookie" version = "0.18.1" @@ -102,6 +128,12 @@ dependencies = [ "litrs", ] +[[package]] +name = "dotenvy" +version = "0.15.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1aaf95b3e5c8f23aa320147307562d361db0ae0d51242340f558153b4eb2439b" + [[package]] name = "encoding_rs" version = "0.8.35" @@ -477,6 +509,11 @@ checksum = "28d3b2b1366ec20994f1fd18c3c594f05c5dd4bc44d8bb0c1c632c8d6829481f" name = "semargl" version = "0.1.0" dependencies = [ + "anyhow", + "const_format", + "dotenvy", + "serde", + "serde_json", "ureq", ] @@ -605,6 +642,12 @@ version = "1.0.18" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "5a5f39404a5da50712a4c1eecf25e90dd62b613502b7e925fd4e4d19b5c96512" +[[package]] +name = "unicode-xid" +version = "0.2.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ebc1c04c71510c7f702b52b7c350734c9ff1295c464a03335b00bb84fc54f853" + [[package]] name = "untrusted" version = "0.9.0" diff --git a/Cargo.toml b/Cargo.toml index a88fa79..73c4b7f 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -4,4 +4,9 @@ version = "0.1.0" edition = "2024" [dependencies] +anyhow = "1.0.97" +const_format = "0.2.34" +dotenvy = "0.15.7" +serde = { version = "1.0.219", features = ["derive"] } +serde_json = "1.0.140" ureq = { version = "3.0.10", features = ["charset", "cookies", "json"] } diff --git a/src/main.rs b/src/main.rs index e7a11a9..55e9f61 100644 --- a/src/main.rs +++ b/src/main.rs @@ -1,3 +1,165 @@ -fn main() { - println!("Hello, world!"); +#![allow(non_upper_case_globals)] + +use std::{ env, error::Error, fs::File, io::Read, sync::LazyLock, time::Duration }; +use anyhow::Result; +use const_format::formatcp; +use serde::{ Serialize, Deserialize }; +use ureq::{ http::Response, Agent, Body }; + +const MAX_USERS_PER_PAGE: u8 = 50; +const cookie_filename: &'static str = "cookie.txt"; +const host: &'static str = "members.tilda.ru"; +const project_id: u32 = 11795571; + + +const origin: &'static str = formatcp!("https://{host}"); +const referer: &'static str = formatcp!("{origin}/{project_id}/"); +const endpoint: &'static str = formatcp!("{origin}/api/getmembers/"); + +const user_agent: &'static str = "Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/134.0.0.0 Safari/537.36"; + +#[derive(Debug, Clone, Deserialize, Serialize)] +enum RequestSort { + #[serde(rename = "nameAsc")] + NameAsc +} + + +#[derive(Debug, Clone, Deserialize, Serialize)] +#[serde(rename_all = "camelCase")] +struct Request { + pub page: u16, + pub page_size: u8, + pub sort: RequestSort, + pub project_id: u32, +} + +impl Default for Request { + fn default () -> Self { + Self { page: 1, page_size: MAX_USERS_PER_PAGE, sort: RequestSort::NameAsc, project_id } + } +} + + +#[derive(Debug, Clone, Deserialize, Serialize)] +#[serde(untagged)] +enum Resp { + Ok (OkResp), + Err (ErrResp), +} + +#[derive(Debug, Clone, Deserialize, Serialize)] +#[serde(rename_all = "camelCase")] +struct OkResp { + pub status: String, + pub data: ResponseData +} + +#[derive(Debug, Clone, Deserialize, Serialize)] +#[serde(rename_all = "camelCase")] +struct ErrResp { + pub status: String, + pub code: String +} + +#[derive(Debug, Clone, Deserialize, Serialize)] +#[serde(rename_all = "camelCase")] +struct ResponseData { + pub total: u32, + pub total_active: u32, + pub pages: u32, + pub members: Vec, + pub page: u32, + pub page_size: u32, + + pub login_url: String, + #[serde(rename = "singUpUrl")] // [sic] + pub sign_up_url: String, +} + + +#[derive(Debug, Clone, Deserialize, Serialize)] +#[serde(rename_all = "camelCase")] +pub struct Member { + pub id: u32, + pub name: String, + pub login: String, + pub added_date: String, + pub activity: bool, + pub avatar: Option, + pub last_online_date: String, + pub groups: Vec, +} + + +#[derive(Debug, Clone, Deserialize, Serialize)] +#[serde(rename_all = "camelCase")] +pub struct Group { + pub id: u32, + pub name: String +} + +impl Request { + fn for_page (page: u16) -> Self { + let mut _self = Self::default(); + _self.page = page; + _self + } + + fn send (&self, cookie: &str) -> Result> { + let agent: Agent = Agent::config_builder() + .timeout_global(Some(Duration::from_secs(5))) + .user_agent(user_agent) + .build() + .into(); + + let builder = agent.post(endpoint) + .content_type("text/plain;charset=UTF-8") + .header("Host", host) + .header("Origin", origin) + .header("Referer", referer) + .header("Cookie", cookie); + + let body = serde_json::to_string(self)?; + + Ok(builder.send(body)?) + } +} + +/// @TODO +fn try_get_cookie () -> Result<()> { + Ok(()) +} + +fn read_cookie (filename: &str) -> Option { + let mut cookie_file = File::open(filename).ok()?; + let mut cookie = String::new(); + + cookie_file.read_to_string(&mut cookie).ok()?; + cookie = cookie.trim().to_owned(); + + if cookie.is_empty() { return None; } + + Some(cookie) +} + + + +fn retrieve_users (cookie: &str) { +} + +fn main () { + let cookie = read_cookie(cookie_filename).expect(&format!("Directory with executable should contain `{cookie_filename}` file containing a valid cookie for accessing Tilda control panel")); + let req = Request::default().send(&cookie); + + if let Ok(mut req) = req { + + let mut body = req.body_mut(); + + + println!("{:?}", body.read_json::()); + } + + // println!("{} {} {} {} {} {}", host, project_id, &*origin, &*referer, user_agent, cookie); + }