use std::collections::HashMap;
use clap::Parser;
pub fn default_dc_ips() -> HashMap<u32, String> {
[
(1, "149.154.175.50"),
(2, "149.154.167.51"),
(3, "149.154.175.100"),
(4, "149.154.167.91"),
(5, "149.154.171.5"),
(203, "91.105.192.100"),
]
.iter()
.map(|(k, v)| (*k, v.to_string()))
.collect()
}
pub fn default_dc_overrides() -> HashMap<u32, u32> {
[(203, 2)].iter().copied().collect()
}
fn parse_dc_ip(s: &str) -> Result<(u32, String), String> {
let (dc_s, ip_s) = s
.split_once(':')
.ok_or_else(|| format!("expected DC:IP, got {s:?}"))?;
let dc: u32 = dc_s
.parse()
.map_err(|_| format!("invalid DC number {dc_s:?}"))?;
let _: std::net::IpAddr = ip_s
.parse()
.map_err(|_| format!("invalid IP address {ip_s:?}"))?;
Ok((dc, ip_s.to_string()))
}
#[derive(Parser, Clone, Debug)]
#[command(
name = "tg-ws-proxy",
about = "Telegram MTProto WebSocket Bridge Proxy",
long_about = "Local MTProto proxy that tunnels Telegram Desktop traffic \
through WebSocket connections to Telegram DCs.\n\
Useful on networks where raw TCP to Telegram is blocked."
)]
pub struct Config {
#[arg(long, default_value = "1443", env = "TG_PORT")]
pub port: u16,
#[arg(long, default_value = "127.0.0.1", env = "TG_HOST")]
pub host: String,
#[arg(long, env = "TG_SECRET")]
pub secret: Option<String>,
#[arg(long = "dc-ip", value_name = "DC:IP", value_parser = parse_dc_ip)]
pub dc_ip: Vec<(u32, String)>,
#[arg(long = "buf-kb", default_value = "256", env = "TG_BUF_KB")]
pub buf_kb: usize,
#[arg(long = "pool-size", default_value = "4", env = "TG_POOL_SIZE")]
pub pool_size: usize,
#[arg(long = "max-connections", env = "TG_MAX_CONNECTIONS")]
pub max_connections: Option<usize>,
#[arg(short, long, env = "TG_VERBOSE")]
pub verbose: bool,
#[arg(long = "danger-accept-invalid-certs", env = "TG_SKIP_TLS_VERIFY")]
pub skip_tls_verify: bool,
#[arg(short = 'q', long, env = "TG_QUIET")]
pub quiet: bool,
}
impl Config {
pub fn from_args() -> Self {
let mut cfg = Self::parse();
if cfg.secret.is_none() {
let bytes: [u8; 16] = rand::random();
cfg.secret = Some(hex::encode(bytes));
}
if cfg.dc_ip.is_empty() {
cfg.dc_ip = vec![
(2, "149.154.167.220".to_string()),
(4, "149.154.167.220".to_string()),
];
}
cfg
}
pub fn secret_bytes(&self) -> Vec<u8> {
hex::decode(self.secret.as_deref().unwrap_or("")).expect("secret must be valid hex")
}
pub fn dc_redirects(&self) -> HashMap<u32, String> {
self.dc_ip.iter().cloned().collect()
}
#[allow(dead_code)]
pub fn buf_bytes(&self) -> usize {
self.buf_kb * 1024
}
}