Update login, add nasu
This commit is contained in:
parent
685b47daff
commit
18bc76e2ec
3 changed files with 1068 additions and 616 deletions
1485
Cargo.lock
generated
1485
Cargo.lock
generated
File diff suppressed because it is too large
Load diff
12
Cargo.toml
12
Cargo.toml
|
|
@ -4,13 +4,15 @@ version = "0.1.0"
|
||||||
edition = "2021"
|
edition = "2021"
|
||||||
|
|
||||||
[dependencies]
|
[dependencies]
|
||||||
clap = { version = "4.5.20", features = ["derive"] }
|
argp = "0.4.1"
|
||||||
passterm = "2.0.3"
|
futures-util = "0.3.34"
|
||||||
rand = "0.8.5"
|
passterm = "2.1.0"
|
||||||
reqwest = { version = "0.12.8", features = ["blocking", "cookies", "json"] }
|
rand = "0.10.3"
|
||||||
|
reqwest = { version = "0.13.5", default-features = false, features = ["blocking", "charset", "cookies", "form", "json", "native-tls", "system-proxy"] }
|
||||||
|
reqwest-websocket = { version = "0.6.0", features = ["json"] }
|
||||||
#serde = { version = "1", features = ["derive"] }
|
#serde = { version = "1", features = ["derive"] }
|
||||||
serde_json = "1"
|
serde_json = "1"
|
||||||
#sled = "0.34.7"
|
#sled = "0.34.7"
|
||||||
#smallvec = "1.13.2"
|
#smallvec = "1.13.2"
|
||||||
tokio = { version = "1", features = [] }
|
tokio = { version = "1", features = ["macros", "rt-multi-thread"] }
|
||||||
#typed-sled = "0.2.3"
|
#typed-sled = "0.2.3"
|
||||||
|
|
|
||||||
197
src/main.rs
197
src/main.rs
|
|
@ -1,37 +1,68 @@
|
||||||
use clap::Parser;
|
use futures_util::{SinkExt, TryStreamExt};
|
||||||
use rand::Rng;
|
use rand::RngExt;
|
||||||
use reqwest::{
|
use reqwest::{
|
||||||
header::{HeaderMap, HeaderValue},
|
header::{HeaderMap, HeaderValue},
|
||||||
Method,
|
Method,
|
||||||
};
|
};
|
||||||
|
use reqwest_websocket::{Message, Upgrade};
|
||||||
use std::{collections::HashMap, time::Duration};
|
use std::{collections::HashMap, time::Duration};
|
||||||
|
|
||||||
|
const WS: &'static str = "wss://leekwars.com/ws";
|
||||||
const API: &'static str = "https://leekwars.com/api/";
|
const API: &'static str = "https://leekwars.com/api/";
|
||||||
|
|
||||||
#[derive(Parser)]
|
const WS_GARDEN_BOSS_CREATE_SQUAD: u32 = 66;
|
||||||
#[command(name = "leekwarsbot")]
|
const WS_GARDEN_BOSS_JOIN_SQUAD: u32 = 67;
|
||||||
#[command(bin_name = "leekwarsbot")]
|
const WS_GARDEN_BOSS_SQUAD: u32 = 76;
|
||||||
struct Cli {
|
const WS_GARDEN_BOSS_SQUAD_JOINED: u32 = 74;
|
||||||
#[clap(short, long)]
|
const WS_GARDEN_BOSS_ATTACK: u32 = 71;
|
||||||
username: String,
|
const WS_GARDEN_BOSS_LISTEN: u32 = 72;
|
||||||
/// Number of seconds to wait between each fight
|
|
||||||
#[clap(short, long, default_value_t = 10)]
|
enum FightType {
|
||||||
delay: u64,
|
Farmer,
|
||||||
/// Number of fights to start
|
Nasu,
|
||||||
#[clap(short, long, default_value_t = 1)]
|
|
||||||
fights: usize,
|
|
||||||
}
|
}
|
||||||
|
|
||||||
fn main() {
|
impl argp::FromArgValue for FightType {
|
||||||
let cli = Cli::parse();
|
fn from_arg_value(value: &std::ffi::OsStr) -> Result<Self, String> {
|
||||||
|
match value.to_str().unwrap() {
|
||||||
|
"farmer" => Ok(FightType::Farmer),
|
||||||
|
"nasu" => Ok(FightType::Nasu),
|
||||||
|
_ => Err("fight type must be one of farmer|nasu".into()),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// LeekWars bot
|
||||||
|
#[derive(argp::FromArgs)]
|
||||||
|
struct Cli {
|
||||||
|
/// Username
|
||||||
|
#[argp(positional)]
|
||||||
|
username: String,
|
||||||
|
/// Number of seconds to wait between each fight
|
||||||
|
#[argp(option, short = 'd', default = "10")]
|
||||||
|
delay: u64,
|
||||||
|
/// Number of fights to start
|
||||||
|
#[argp(option, short = 'n', default = "1")]
|
||||||
|
fights: usize,
|
||||||
|
/// Fight type
|
||||||
|
#[argp(option, short = 't', default = "FightType::Farmer")]
|
||||||
|
fight_type: FightType,
|
||||||
|
/// Leek id
|
||||||
|
#[argp(option, short = 'l')]
|
||||||
|
leek: Option<u32>,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[tokio::main]
|
||||||
|
async fn main() {
|
||||||
|
let cli: Cli = argp::parse_args_or_exit(argp::DEFAULT);
|
||||||
|
|
||||||
let delay = std::time::Duration::from_secs(cli.delay);
|
let delay = std::time::Duration::from_secs(cli.delay);
|
||||||
|
|
||||||
let mut rng = rand::thread_rng();
|
let mut rng = rand::rng();
|
||||||
|
|
||||||
let password = passterm::prompt_password_tty(Some("Password: ")).unwrap();
|
let password = passterm::prompt_password_tty(Some("Password: ")).unwrap();
|
||||||
|
|
||||||
let client = reqwest::blocking::Client::builder()
|
let client = reqwest::Client::builder()
|
||||||
.timeout(Duration::from_secs(30))
|
.timeout(Duration::from_secs(30))
|
||||||
.user_agent("RustyLeekwarsBot")
|
.user_agent("RustyLeekwarsBot")
|
||||||
.build()
|
.build()
|
||||||
|
|
@ -45,20 +76,21 @@ fn main() {
|
||||||
.post(format!("{}{}", API, "farmer/login-token"))
|
.post(format!("{}{}", API, "farmer/login-token"))
|
||||||
.form(&form)
|
.form(&form)
|
||||||
.send()
|
.send()
|
||||||
|
.await
|
||||||
.unwrap();
|
.unwrap();
|
||||||
//dbg!(resp.status());
|
//dbg!(resp.status());
|
||||||
//dbg!(resp.headers());
|
//dbg!(resp.headers());
|
||||||
|
|
||||||
let phpsessid = resp
|
let hostsess = resp
|
||||||
.cookies()
|
.cookies()
|
||||||
.find_map(|cookie| {
|
.find_map(|cookie| {
|
||||||
if cookie.name() == "PHPSESSID" && cookie.value() != "deleted" {
|
if cookie.name() == "__Host-sess" && cookie.value() != "deleted" {
|
||||||
Some(cookie.value().to_owned())
|
Some(cookie.value().to_owned())
|
||||||
} else {
|
} else {
|
||||||
None
|
None
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
.expect("No PHPSESSID from the API!");
|
.expect("No __Host-sess from the API!");
|
||||||
let token = resp
|
let token = resp
|
||||||
.cookies()
|
.cookies()
|
||||||
.find_map(|cookie| {
|
.find_map(|cookie| {
|
||||||
|
|
@ -73,13 +105,15 @@ fn main() {
|
||||||
let mut headers = HeaderMap::new();
|
let mut headers = HeaderMap::new();
|
||||||
headers.insert(
|
headers.insert(
|
||||||
reqwest::header::AUTHORIZATION,
|
reqwest::header::AUTHORIZATION,
|
||||||
HeaderValue::from_str(&format!("Bearer {}", token)).unwrap(),
|
HeaderValue::from_str("Bearer $").unwrap(),
|
||||||
);
|
);
|
||||||
headers.insert(
|
headers.insert(
|
||||||
reqwest::header::COOKIE,
|
reqwest::header::COOKIE,
|
||||||
HeaderValue::from_str(&format!("token={}; PHPSESSID={}", token, phpsessid)).unwrap(),
|
HeaderValue::from_str(&format!("token={}; __Host-sess={}", token, hostsess)).unwrap(),
|
||||||
);
|
);
|
||||||
|
|
||||||
|
match cli.fight_type {
|
||||||
|
FightType::Farmer => {
|
||||||
for _ in 0..cli.fights {
|
for _ in 0..cli.fights {
|
||||||
let resp: serde_json::Value = client
|
let resp: serde_json::Value = client
|
||||||
.request(
|
.request(
|
||||||
|
|
@ -88,12 +122,14 @@ fn main() {
|
||||||
)
|
)
|
||||||
.headers(headers.clone())
|
.headers(headers.clone())
|
||||||
.send()
|
.send()
|
||||||
|
.await
|
||||||
.unwrap()
|
.unwrap()
|
||||||
.json()
|
.json()
|
||||||
|
.await
|
||||||
.unwrap();
|
.unwrap();
|
||||||
//dbg!(&resp);
|
//dbg!(&resp);
|
||||||
|
|
||||||
let opponent = &resp["opponents"].as_array().unwrap()[rng.gen_range(0..5)];
|
let opponent = &resp["opponents"].as_array().unwrap()[rng.random_range(0..5)];
|
||||||
let opponent_name = opponent["name"].as_str().unwrap();
|
let opponent_name = opponent["name"].as_str().unwrap();
|
||||||
let opponent_id = opponent["id"].as_u64().unwrap();
|
let opponent_id = opponent["id"].as_u64().unwrap();
|
||||||
|
|
||||||
|
|
@ -110,10 +146,123 @@ fn main() {
|
||||||
.headers(headers.clone())
|
.headers(headers.clone())
|
||||||
.json(&map)
|
.json(&map)
|
||||||
.send()
|
.send()
|
||||||
|
.await
|
||||||
.unwrap()
|
.unwrap()
|
||||||
.json()
|
.json()
|
||||||
|
.await
|
||||||
.unwrap();
|
.unwrap();
|
||||||
//dbg!(resp);
|
//dbg!(resp);
|
||||||
std::thread::sleep(delay);
|
std::thread::sleep(delay);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
FightType::Nasu => {
|
||||||
|
headers.insert(
|
||||||
|
reqwest::header::SEC_WEBSOCKET_PROTOCOL,
|
||||||
|
HeaderValue::from_str("leek-wars, $").unwrap(),
|
||||||
|
);
|
||||||
|
headers.insert(
|
||||||
|
reqwest::header::SEC_WEBSOCKET_EXTENSIONS,
|
||||||
|
HeaderValue::from_str("permessage-deflate").unwrap(),
|
||||||
|
);
|
||||||
|
headers.insert(
|
||||||
|
reqwest::header::SEC_WEBSOCKET_KEY,
|
||||||
|
HeaderValue::from_str("mKwpFRJwZHKyJDTUdyhRIA==").unwrap(),
|
||||||
|
);
|
||||||
|
headers.insert(
|
||||||
|
reqwest::header::SEC_WEBSOCKET_VERSION,
|
||||||
|
HeaderValue::from_str("13").unwrap(),
|
||||||
|
);
|
||||||
|
headers.insert(
|
||||||
|
reqwest::header::HeaderName::from_static("sec-fetch-dest"),
|
||||||
|
HeaderValue::from_str("empty").unwrap(),
|
||||||
|
);
|
||||||
|
headers.insert(
|
||||||
|
reqwest::header::HeaderName::from_static("sec-fetch-site"),
|
||||||
|
HeaderValue::from_str("same-origin").unwrap(),
|
||||||
|
);
|
||||||
|
headers.insert(
|
||||||
|
reqwest::header::HeaderName::from_static("sec-fetch-mode"),
|
||||||
|
HeaderValue::from_str("websocket").unwrap(),
|
||||||
|
);
|
||||||
|
headers.insert(
|
||||||
|
reqwest::header::CACHE_CONTROL,
|
||||||
|
HeaderValue::from_str("no-cache").unwrap(),
|
||||||
|
);
|
||||||
|
headers.insert(
|
||||||
|
reqwest::header::ORIGIN,
|
||||||
|
HeaderValue::from_str("https://leekwars.com").unwrap(),
|
||||||
|
);
|
||||||
|
|
||||||
|
let ws_resp = client
|
||||||
|
.get(WS)
|
||||||
|
.headers(headers.clone())
|
||||||
|
.upgrade()
|
||||||
|
.protocols(["leek-wars"])
|
||||||
|
.send()
|
||||||
|
.await
|
||||||
|
.unwrap();
|
||||||
|
let mut ws = ws_resp.into_websocket().await.unwrap();
|
||||||
|
let enter_msg = Message::text_from_json(&[
|
||||||
|
serde_json::Value::Number(serde_json::Number::from(WS_GARDEN_BOSS_CREATE_SQUAD)),
|
||||||
|
serde_json::Value::Number(serde_json::Number::from(1)),
|
||||||
|
serde_json::Value::Bool(true),
|
||||||
|
serde_json::Value::Array(vec![serde_json::Value::Number(
|
||||||
|
serde_json::Number::from(cli.leek.unwrap()),
|
||||||
|
)]),
|
||||||
|
])
|
||||||
|
.unwrap();
|
||||||
|
let attack_msg = Message::text_from_json(&[serde_json::Value::Number(
|
||||||
|
serde_json::Number::from(WS_GARDEN_BOSS_ATTACK),
|
||||||
|
)])
|
||||||
|
.unwrap();
|
||||||
|
for i in 0..cli.fights {
|
||||||
|
if i > 0 {
|
||||||
|
std::thread::sleep(delay);
|
||||||
|
}
|
||||||
|
ws.flush().await.unwrap();
|
||||||
|
ws.send(enter_msg.clone()).await.unwrap();
|
||||||
|
let mut boss_id = None;
|
||||||
|
//let mut master_id = None;
|
||||||
|
while let Some(msg) = ws.try_next().await.unwrap() {
|
||||||
|
match dbg!(&msg) {
|
||||||
|
Message::Ping(b) => {
|
||||||
|
ws.send(Message::Pong(b.clone())).await.unwrap();
|
||||||
|
}
|
||||||
|
Message::Text(_) | Message::Binary(_) => {
|
||||||
|
let msg: serde_json::Value = msg.json().unwrap();
|
||||||
|
if !msg.is_array() {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
let msg = msg.as_array().unwrap();
|
||||||
|
if msg[0]
|
||||||
|
== serde_json::Value::Number(serde_json::Number::from(
|
||||||
|
WS_GARDEN_BOSS_SQUAD,
|
||||||
|
)) {
|
||||||
|
boss_id = Some(msg[1].as_object().unwrap()["id"].clone());
|
||||||
|
//master_id = Some(msg[1].as_object().unwrap()["master"].clone());
|
||||||
|
} else if msg[0]
|
||||||
|
== serde_json::Value::Number(serde_json::Number::from(
|
||||||
|
WS_GARDEN_BOSS_SQUAD_JOINED,
|
||||||
|
))
|
||||||
|
&& boss_id.is_some()
|
||||||
|
{
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
_ => {}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
let join_msg = Message::text_from_json(&[
|
||||||
|
serde_json::Value::Number(serde_json::Number::from(WS_GARDEN_BOSS_JOIN_SQUAD)),
|
||||||
|
boss_id.unwrap(),
|
||||||
|
serde_json::Value::Array(vec![serde_json::Value::Number(
|
||||||
|
serde_json::Number::from(cli.leek.unwrap()),
|
||||||
|
)]),
|
||||||
|
])
|
||||||
|
.unwrap();
|
||||||
|
ws.send(join_msg).await.unwrap();
|
||||||
|
ws.send(attack_msg.clone()).await.unwrap();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue