add incomplete account linker
This commit is contained in:
parent
c936d9a0ac
commit
d8ffc179e0
2 changed files with 152 additions and 30 deletions
|
@ -0,0 +1,86 @@
|
|||
use std::collections::HashMap;
|
||||
use reqwest::{Url, cookie::{CookieStore}, header::COOKIE, header::{SET_COOKIE, USER_AGENT}};
|
||||
use serde::{Serialize, Deserialize};
|
||||
use serde_json::Value;
|
||||
use steamguard_cli::{SteamGuardAccount, steamapi::Session};
|
||||
use log::*;
|
||||
|
||||
#[derive(Debug, Clone)]
|
||||
pub struct AccountLinker {
|
||||
device_id: String,
|
||||
phone_number: String,
|
||||
pub account: SteamGuardAccount,
|
||||
client: reqwest::blocking::Client,
|
||||
}
|
||||
|
||||
impl AccountLinker {
|
||||
pub fn new() -> AccountLinker {
|
||||
return AccountLinker{
|
||||
device_id: generate_device_id(),
|
||||
phone_number: String::from(""),
|
||||
account: SteamGuardAccount::new(),
|
||||
client: reqwest::blocking::ClientBuilder::new()
|
||||
.cookie_store(true)
|
||||
.build()
|
||||
.unwrap(),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn link(&self, session: &mut Session) {
|
||||
let mut params = HashMap::new();
|
||||
params.insert("access_token", session.token.clone());
|
||||
params.insert("steamid", session.steam_id.to_string());
|
||||
params.insert("device_identifier", self.device_id.clone());
|
||||
params.insert("authenticator_type", String::from("1"));
|
||||
params.insert("sms_phone_id", String::from("1"));
|
||||
}
|
||||
|
||||
fn has_phone(&self, session: &Session) -> bool {
|
||||
return self._phoneajax(session, "has_phone", "null");
|
||||
}
|
||||
|
||||
fn _phoneajax(&self, session: &Session, op: &str, arg: &str) -> bool {
|
||||
trace!("_phoneajax: op={}", op);
|
||||
let url = "https://steamcommunity.com".parse::<Url>().unwrap();
|
||||
let cookies = reqwest::cookie::Jar::default();
|
||||
cookies.add_cookie_str("mobileClientVersion=0 (2.1.3)", &url);
|
||||
cookies.add_cookie_str("mobileClient=android", &url);
|
||||
cookies.add_cookie_str("Steam_Language=english", &url);
|
||||
|
||||
let mut params = HashMap::new();
|
||||
params.insert("op", op);
|
||||
params.insert("arg", arg);
|
||||
params.insert("sessionid", session.session_id.as_str());
|
||||
if op == "check_sms_code" {
|
||||
params.insert("checkfortos", "0");
|
||||
params.insert("skipvoip", "1");
|
||||
}
|
||||
|
||||
let resp = self.client
|
||||
.post("https://steamcommunity.com/steamguard/phoneajax")
|
||||
.header(COOKIE, cookies.cookies(&url).unwrap())
|
||||
.send()
|
||||
.unwrap();
|
||||
|
||||
let result: Value = resp.json().unwrap();
|
||||
if result["has_phone"] != Value::Null {
|
||||
trace!("found has_phone field");
|
||||
return result["has_phone"].as_bool().unwrap();
|
||||
} else if result["success"] != Value::Null {
|
||||
trace!("found success field");
|
||||
return result["success"].as_bool().unwrap();
|
||||
} else {
|
||||
trace!("did not find any expected field");
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn generate_device_id() -> String {
|
||||
return format!("android:{}", uuid::Uuid::new_v4().to_string());
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Deserialize)]
|
||||
pub struct AddAuthenticatorResponse {
|
||||
pub response: SteamGuardAccount
|
||||
}
|
96
src/main.rs
96
src/main.rs
|
@ -1,6 +1,7 @@
|
|||
extern crate rpassword;
|
||||
use borrow::BorrowMut;
|
||||
use io::Write;
|
||||
use steamapi::Session;
|
||||
use steamguard_cli::*;
|
||||
use ::std::*;
|
||||
use text_io::read;
|
||||
|
@ -12,6 +13,7 @@ use regex::Regex;
|
|||
#[macro_use]
|
||||
extern crate lazy_static;
|
||||
mod accountmanager;
|
||||
mod accountlinker;
|
||||
|
||||
lazy_static! {
|
||||
static ref CAPTCHA_VALID_CHARS: Regex = Regex::new("^([A-H]|[J-N]|[P-R]|[T-Z]|[2-4]|[7-9]|[@%&])+$").unwrap();
|
||||
|
@ -67,6 +69,10 @@ fn main() {
|
|||
.help("Accept all open trade confirmations. Does not open interactive interface.")
|
||||
)
|
||||
)
|
||||
.subcommand(
|
||||
App::new("setup")
|
||||
.about("Set up a new account with steamguard-cli")
|
||||
)
|
||||
.get_matches();
|
||||
|
||||
|
||||
|
@ -88,6 +94,15 @@ fn main() {
|
|||
}
|
||||
|
||||
manifest.load_accounts();
|
||||
|
||||
if matches.is_present("setup") {
|
||||
info!("setup");
|
||||
let mut linker = accountlinker::AccountLinker::new();
|
||||
do_login(&mut linker.account);
|
||||
// linker.link(linker.account.session.expect("no login session"));
|
||||
return;
|
||||
}
|
||||
|
||||
let mut selected_accounts: Vec<SteamGuardAccount> = vec![];
|
||||
if matches.is_present("all") {
|
||||
// manifest.accounts.iter().map(|a| selected_accounts.push(a.b));
|
||||
|
@ -111,36 +126,9 @@ fn main() {
|
|||
|
||||
if matches.is_present("trade") {
|
||||
info!("trade");
|
||||
for account in selected_accounts.iter_mut() {
|
||||
let _ = std::io::stdout().flush();
|
||||
let password = rpassword::prompt_password_stdout("Password: ").unwrap();
|
||||
trace!("password: {}", password);
|
||||
let mut login = steamapi::UserLogin::new(account.account_name.clone(), password);
|
||||
let mut loops = 0;
|
||||
loop {
|
||||
match login.login() {
|
||||
steamapi::LoginResult::Ok(s) => {
|
||||
account.session = Option::Some(s);
|
||||
break;
|
||||
}
|
||||
steamapi::LoginResult::Need2FA => {
|
||||
let server_time = steamapi::get_server_time();
|
||||
login.twofactor_code = account.generate_code(server_time);
|
||||
}
|
||||
steamapi::LoginResult::NeedCaptcha{ captcha_gid } => {
|
||||
login.captcha_text = prompt_captcha_text(&captcha_gid);
|
||||
}
|
||||
r => {
|
||||
error!("Fatal login result: {:?}", r);
|
||||
return;
|
||||
}
|
||||
}
|
||||
loops += 1;
|
||||
if loops > 2 {
|
||||
error!("Too many loops. Aborting login process, to avoid getting rate limited.");
|
||||
return;
|
||||
}
|
||||
}
|
||||
for a in selected_accounts.iter_mut() {
|
||||
let mut account = a; // why is this necessary?
|
||||
do_login(&mut account);
|
||||
|
||||
info!("Checking for trade confirmations");
|
||||
account.get_trade_confirmations();
|
||||
|
@ -198,3 +186,51 @@ fn prompt_captcha_text(captcha_gid: &String) -> String {
|
|||
}
|
||||
return captcha_text;
|
||||
}
|
||||
|
||||
fn do_login(account: &mut SteamGuardAccount) {
|
||||
if account.account_name.len() > 0 {
|
||||
println!("Username: {}", account.account_name);
|
||||
} else {
|
||||
print!("Username: ");
|
||||
account.account_name = prompt();
|
||||
}
|
||||
let _ = std::io::stdout().flush();
|
||||
let password = rpassword::prompt_password_stdout("Password: ").unwrap();
|
||||
if password.len() > 0 {
|
||||
debug!("password is present");
|
||||
} else {
|
||||
debug!("password is empty");
|
||||
}
|
||||
// TODO: reprompt if password is empty
|
||||
let mut login = steamapi::UserLogin::new(account.account_name.clone(), password);
|
||||
let mut loops = 0;
|
||||
loop {
|
||||
match login.login() {
|
||||
steamapi::LoginResult::Ok(s) => {
|
||||
account.session = Option::Some(s);
|
||||
break;
|
||||
}
|
||||
steamapi::LoginResult::Need2FA => {
|
||||
let server_time = steamapi::get_server_time();
|
||||
login.twofactor_code = account.generate_code(server_time);
|
||||
}
|
||||
steamapi::LoginResult::NeedCaptcha{ captcha_gid } => {
|
||||
login.captcha_text = prompt_captcha_text(&captcha_gid);
|
||||
}
|
||||
steamapi::LoginResult::NeedEmail => {
|
||||
println!("You should have received an email with a code.");
|
||||
print!("Enter code");
|
||||
login.email_code = prompt();
|
||||
}
|
||||
r => {
|
||||
error!("Fatal login result: {:?}", r);
|
||||
return;
|
||||
}
|
||||
}
|
||||
loops += 1;
|
||||
if loops > 2 {
|
||||
error!("Too many loops. Aborting login process, to avoid getting rate limited.");
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
Loading…
Reference in a new issue