2021-03-24 22:49:09 +01:00
|
|
|
extern crate rpassword;
|
2021-03-27 17:14:34 +01:00
|
|
|
use borrow::BorrowMut;
|
2021-03-24 22:49:09 +01:00
|
|
|
use io::Write;
|
2021-03-22 02:21:29 +01:00
|
|
|
use steamguard_cli::*;
|
2021-03-24 22:49:09 +01:00
|
|
|
use ::std::*;
|
|
|
|
use text_io::read;
|
2021-03-30 21:51:26 +02:00
|
|
|
use std::{io::stdin, path::Path};
|
2021-03-27 14:31:38 +01:00
|
|
|
use clap::{App, Arg, crate_version};
|
2021-03-27 15:35:52 +01:00
|
|
|
use log::*;
|
2021-03-24 22:49:09 +01:00
|
|
|
|
2021-03-26 00:47:44 +01:00
|
|
|
mod accountmanager;
|
2021-03-22 02:21:29 +01:00
|
|
|
|
|
|
|
fn main() {
|
2021-03-27 14:31:38 +01:00
|
|
|
let matches = App::new("steamguard-cli")
|
|
|
|
.version(crate_version!())
|
|
|
|
.bin_name("steamguard")
|
|
|
|
.author("dyc3 (Carson McManus)")
|
|
|
|
.about("Generate Steam 2FA codes and confirm Steam trades from the command line.")
|
|
|
|
.arg(
|
|
|
|
Arg::with_name("username")
|
|
|
|
.long("username")
|
|
|
|
.short("u")
|
|
|
|
.help("Select the account you want by steam username. By default, the first account in the manifest is selected.")
|
|
|
|
)
|
|
|
|
.arg(
|
|
|
|
Arg::with_name("all")
|
|
|
|
.long("all")
|
|
|
|
.short("a")
|
2021-03-27 17:14:34 +01:00
|
|
|
.takes_value(false)
|
2021-03-27 14:31:38 +01:00
|
|
|
.help("Select all accounts in the manifest.")
|
|
|
|
)
|
|
|
|
.arg(
|
|
|
|
Arg::with_name("mafiles-path")
|
|
|
|
.long("mafiles-path")
|
|
|
|
.short("m")
|
|
|
|
.default_value("~/maFiles")
|
|
|
|
.help("Specify which folder your maFiles are in.")
|
|
|
|
)
|
|
|
|
.arg(
|
|
|
|
Arg::with_name("passkey")
|
|
|
|
.long("passkey")
|
|
|
|
.short("p")
|
|
|
|
.help("Specify your encryption passkey.")
|
|
|
|
)
|
2021-03-27 15:35:52 +01:00
|
|
|
.arg(
|
|
|
|
Arg::with_name("verbosity")
|
|
|
|
.short("v")
|
|
|
|
.help("Log what is going on verbosely.")
|
|
|
|
.takes_value(false)
|
|
|
|
.multiple(true)
|
|
|
|
)
|
2021-03-27 14:31:38 +01:00
|
|
|
.subcommand(
|
|
|
|
App::new("trade")
|
|
|
|
.about("Interactive interface for trade confirmations")
|
|
|
|
.arg(
|
|
|
|
Arg::with_name("accept-all")
|
|
|
|
.short("a")
|
|
|
|
.long("accept-all")
|
2021-03-27 17:14:34 +01:00
|
|
|
.takes_value(false)
|
2021-03-27 14:31:38 +01:00
|
|
|
.help("Accept all open trade confirmations. Does not open interactive interface.")
|
|
|
|
)
|
|
|
|
)
|
|
|
|
.get_matches();
|
|
|
|
|
2021-03-22 02:21:29 +01:00
|
|
|
|
2021-03-27 15:35:52 +01:00
|
|
|
let verbosity = matches.occurrences_of("verbosity") as usize + 2;
|
|
|
|
stderrlog::new()
|
|
|
|
.verbosity(verbosity)
|
|
|
|
.module(module_path!()).init().unwrap();
|
2021-03-24 22:49:09 +01:00
|
|
|
|
2021-03-27 15:35:52 +01:00
|
|
|
let path = Path::new(matches.value_of("mafiles-path").unwrap()).join("manifest.json");
|
|
|
|
let mut manifest: accountmanager::Manifest;
|
|
|
|
match accountmanager::Manifest::load(path.as_path()) {
|
|
|
|
Ok(m) => {
|
|
|
|
manifest = m;
|
2021-03-27 13:39:26 +01:00
|
|
|
}
|
|
|
|
Err(e) => {
|
2021-03-27 15:35:52 +01:00
|
|
|
error!("Could not load manifest: {}", e);
|
|
|
|
return;
|
2021-03-27 13:39:26 +01:00
|
|
|
}
|
|
|
|
}
|
2021-03-27 15:35:52 +01:00
|
|
|
|
|
|
|
manifest.load_accounts();
|
2021-03-27 17:14:34 +01:00
|
|
|
let mut selected_accounts: Vec<SteamGuardAccount> = vec![];
|
|
|
|
if matches.is_present("all") {
|
|
|
|
// manifest.accounts.iter().map(|a| selected_accounts.push(a.b));
|
|
|
|
for account in manifest.accounts {
|
|
|
|
selected_accounts.push(account.clone());
|
|
|
|
}
|
|
|
|
} else {
|
|
|
|
for account in manifest.accounts {
|
|
|
|
if !matches.is_present("username") {
|
|
|
|
selected_accounts.push(account.clone());
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
if matches.value_of("username").unwrap() == account.account_name {
|
|
|
|
selected_accounts.push(account.clone());
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
debug!("selected accounts: {:?}", selected_accounts.iter().map(|a| a.account_name.clone()).collect::<Vec<String>>());
|
|
|
|
|
2021-03-30 21:51:26 +02:00
|
|
|
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 } => {
|
|
|
|
// example captchas:
|
|
|
|
// - 3982844815370620954
|
|
|
|
// - 3982844815370767244
|
|
|
|
// - 3982844815370804220
|
|
|
|
// - 3982844815370819607
|
|
|
|
println!("Captcha required. Open this link in your web browser: https://steamcommunity.com/public/captcha.php?gid={}", captcha_gid);
|
|
|
|
print!("Enter captcha text: ");
|
|
|
|
let _ = std::io::stdout().flush();
|
|
|
|
let mut captcha_text = String::new();
|
|
|
|
stdin().read_line(&mut captcha_text).expect("Did not enter a correct string");
|
|
|
|
login.captcha_text = String::from(captcha_text.strip_suffix('\n').unwrap());
|
|
|
|
}
|
|
|
|
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;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
} else {
|
|
|
|
let server_time = steamapi::get_server_time();
|
|
|
|
for account in selected_accounts {
|
|
|
|
trace!("{:?}", account);
|
|
|
|
let code = account.generate_code(server_time);
|
|
|
|
println!("{}", code);
|
|
|
|
}
|
2021-03-27 15:35:52 +01:00
|
|
|
}
|
2021-03-22 02:21:29 +01:00
|
|
|
}
|