mod config;
mod http;
mod peer;
mod udp;
use peer::Peer;
#[tokio::main]
async fn main() -> Result<(), Box<dyn std::error::Error>> {
use clap::Parser;
use colored::Colorize;
use config::Config;
use std::time::Duration;
fn stats(interval: u32, leechers: u32, seeders: u32, peers: Vec<Peer>) {
println!("{}", format!("Update interval: {interval}s").yellow());
println!("{}: {seeders}, {}: {leechers}", "S".green(), "L".red());
if peers.is_empty() {
println!("{}", "No peers.".red());
} else {
for peer in peers {
println!("{peer}")
}
}
}
let config = Config::parse();
if config.http_tracker.as_ref().is_none_or(|h| h.is_empty())
&& config.udp_tracker.as_ref().is_none_or(|u| u.is_empty())
{
println!("{}", "Provide at least one HTTP or UDP tracker(s)".red());
std::process::exit(1);
}
if let Some(http_tracker) = config.http_tracker {
for url in http_tracker {
let query = http::query::Announce::new(url.as_str(), &config.info_hash, config.port)?;
println!(
"{}",
format!("Sending HTTP announce to `{query}`...").blue()
);
let mut peers = peer::new_buffer(Some(config.peers_buffer_capacity));
match http::announce(
&query,
Duration::from_secs(config.timeout),
config.proxy_url.as_deref(),
Some(&mut peers),
)
.await
{
Ok(response) => stats(
response.interval,
response.leechers,
response.seeders,
peers,
),
Err(e) => println!("{}", e.to_string().red()),
}
}
}
if let Some(udp_tracker) = config.udp_tracker {
use std::net::{Ipv4Addr, Ipv6Addr, SocketAddr, SocketAddrV4, SocketAddrV6, UdpSocket};
use udp::Server;
let timeout = Duration::from_secs(config.timeout);
for address in udp_tracker {
let server = if address.is_ipv6() {
UdpSocket::bind(SocketAddr::V6(SocketAddrV6::new(
Ipv6Addr::UNSPECIFIED,
config.port,
0,
0,
)))?
} else {
UdpSocket::bind(SocketAddr::V4(SocketAddrV4::new(
Ipv4Addr::UNSPECIFIED,
config.port,
)))?
};
server.set_read_timeout(Some(timeout))?;
server.set_write_timeout(Some(timeout))?;
println!(
"{}",
format!("Sending UDP announce to `{address}`...").blue()
);
let mut peers = peer::new_buffer(Some(config.peers_buffer_capacity));
match server.announce(&config.info_hash, &address, Some(&mut peers)) {
Ok(response) => stats(
response.interval,
response.leechers,
response.seeders,
peers,
),
Err(e) => println!("{}", e.to_string().red()),
}
}
}
Ok(())
}