use std::net::SocketAddr; use anyhow::Result; use slog::{info, o, Drain, Level, Logger}; use tokio::net::UdpSocket; use tsproto::algorithms as algs; use tsproto::client::Client; use tsproto_packets::packets::*; use tsproto_types::crypto::EccKeyPrivP256; pub fn create_logger() -> Logger { let decorator = slog_term::TermDecorator::new().build(); let drain = slog_term::CompactFormat::new(decorator).build().fuse(); let drain = slog_async::Async::new(drain).build().fuse(); slog::Logger::root(drain, o!()) } pub async fn create_client( local_address: SocketAddr, remote_address: SocketAddr, logger: Logger, verbose: u8, ) -> Result { // Get P-256 ECDH key let private_key = EccKeyPrivP256::import_str( "MG0DAgeAAgEgAiAIXJBlj1hQbaH0Eq0DuLlCmH8bl+veTAO2+\ k9EQjEYSgIgNnImcmKo7ls5mExb6skfK2Tw+u54aeDr0OP1ITsC/50CIA8M5nm\ DBnmDM/gZ//4AAAAAAAAAAAAAAAAAAAAZRzOI").unwrap(); let udp_socket = UdpSocket::bind(local_address).await?; let mut con = Client::new(logger, remote_address, Box::new(udp_socket), private_key); if verbose >= 1 { tsproto::log::add_logger_with_verbosity(con.logger.clone(), verbose, &mut con) } Ok(con) } /// Returns the `initserver` command. pub async fn connect(con: &mut Client) -> Result { con.connect().await?; // Send clientinit let private_key = EccKeyPrivP256::import_str( "MG0DAgeAAgEgAiAIXJBlj1hQbaH0Eq0DuLlCmH8bl+veTAO2+\ k9EQjEYSgIgNnImcmKo7ls5mExb6skfK2Tw+u54aeDr0OP1ITsC/50CIA8M5nm\ DBnmDM/gZ//4AAAAAAAAAAAAAAAAAAAAZRzOI").unwrap(); // Compute hash cash let mut time_reporter = slog_perf::TimeReporter::new_with_level( "Compute public key hash cash level", con.logger.clone(), Level::Info, ); time_reporter.start("Compute public key hash cash level"); let private_key_as_pub = private_key.to_pub(); let offset = algs::hash_cash(&private_key_as_pub, 8); let omega = private_key_as_pub.to_ts(); time_reporter.finish(); info!(con.logger, "Computed hash cash level"; "level" => algs::get_hash_cash_level(&omega, offset), "offset" => offset); // Create clientinit packet let offset = offset.to_string(); let mut cmd = OutCommand::new(Direction::C2S, Flags::empty(), PacketType::Command, "clientinit"); cmd.write_arg("client_nickname", &"Bot"); cmd.write_arg("client_version", &"3.?.? [Build: 5680278000]"); cmd.write_arg("client_platform", &"Linux"); cmd.write_arg("client_input_hardware", &"1"); cmd.write_arg("client_output_hardware", &"1"); cmd.write_arg("client_default_channel", &""); cmd.write_arg("client_default_channel_password", &""); cmd.write_arg("client_server_password", &""); cmd.write_arg("client_meta_data", &""); cmd.write_arg( "client_version_sign", &"Hjd+N58Gv3ENhoKmGYy2bNRBsNNgm5kpiaQWxOj5HN2DXttG6REjymSwJtpJ8muC2gSwRuZi0R+8Laan5ts5CQ==", ); cmd.write_arg("client_nickname_phonetic", &""); cmd.write_arg("client_key_offset", &offset); cmd.write_arg("client_default_token", &""); cmd.write_arg("client_badges", &"Overwolf=0"); cmd.write_arg("hwid", &"923f136fb1e22ae6ce95e60255529c00,d13231b1bc33edfecfb9169cc7a63bcc"); con.send_packet(cmd.into_packet())?; Ok(con .filter_commands(|con, cmd| { Ok(if cmd.data().packet().content().starts_with(b"initserver ") { Some(cmd) } else { con.hand_back_buffer(cmd.into_buffer()); None }) }) .await?) } pub async fn disconnect(con: &mut Client) -> Result<()> { let mut cmd = OutCommand::new(Direction::C2S, Flags::empty(), PacketType::Command, "clientdisconnect"); cmd.write_arg("reasonid", &8); cmd.write_arg("reasonmsg", &"Bye"); con.send_packet(cmd.into_packet())?; con.wait_disconnect().await?; Ok(()) }