use std::sync::Arc;
use crate::{
Client, Config, InvocationError, ShutdownToken, TransportKind,
restart::{ConnectionRestartPolicy, NeverRestart},
retry::{AutoSleep, RetryPolicy},
session_backend::{BinaryFileBackend, InMemoryBackend, SessionBackend, StringSessionBackend},
socks5::Socks5Config,
};
pub struct ClientBuilder {
api_id: i32,
api_hash: String,
dc_addr: Option<String>,
retry_policy: Arc<dyn RetryPolicy>,
restart_policy: Arc<dyn ConnectionRestartPolicy>,
socks5: Option<Socks5Config>,
mtproxy: Option<crate::proxy::MtProxyConfig>,
allow_ipv6: bool,
transport: TransportKind,
session_backend: Arc<dyn SessionBackend>,
catch_up: bool,
device_model: String,
system_version: String,
app_version: String,
system_lang_code: String,
lang_pack: String,
lang_code: String,
}
impl Default for ClientBuilder {
fn default() -> Self {
Self {
api_id: 0,
api_hash: String::new(),
dc_addr: None,
retry_policy: Arc::new(AutoSleep::default()),
restart_policy: Arc::new(NeverRestart),
socks5: None,
mtproxy: None,
allow_ipv6: false,
transport: TransportKind::Abridged,
session_backend: Arc::new(BinaryFileBackend::new("layer.session")),
catch_up: false,
device_model: "Linux".to_string(),
system_version: "1.0".to_string(),
app_version: env!("CARGO_PKG_VERSION").to_string(),
system_lang_code: "en".to_string(),
lang_pack: String::new(),
lang_code: "en".to_string(),
}
}
}
impl ClientBuilder {
pub fn api_id(mut self, id: i32) -> Self {
self.api_id = id;
self
}
pub fn api_hash(mut self, hash: impl Into<String>) -> Self {
self.api_hash = hash.into();
self
}
pub fn session(mut self, path: impl AsRef<std::path::Path>) -> Self {
self.session_backend = Arc::new(BinaryFileBackend::new(path.as_ref()));
self
}
pub fn session_string(mut self, s: impl Into<String>) -> Self {
self.session_backend = Arc::new(StringSessionBackend::new(s));
self
}
pub fn in_memory(mut self) -> Self {
self.session_backend = Arc::new(InMemoryBackend::new());
self
}
pub fn session_backend(mut self, backend: Arc<dyn SessionBackend>) -> Self {
self.session_backend = backend;
self
}
pub fn catch_up(mut self, enabled: bool) -> Self {
self.catch_up = enabled;
self
}
pub fn dc_addr(mut self, addr: impl Into<String>) -> Self {
self.dc_addr = Some(addr.into());
self
}
pub fn socks5(mut self, proxy: Socks5Config) -> Self {
self.socks5 = Some(proxy);
self
}
pub fn mtproxy(mut self, proxy: crate::proxy::MtProxyConfig) -> Self {
self.transport = proxy.transport.clone();
self.mtproxy = Some(proxy);
self
}
pub fn proxy_link(mut self, url: &str) -> Self {
if url.is_empty() {
return self;
}
if let Some(cfg) = crate::proxy::parse_proxy_link(url) {
self.transport = cfg.transport.clone();
self.mtproxy = Some(cfg);
}
self
}
pub fn allow_ipv6(mut self, allow: bool) -> Self {
self.allow_ipv6 = allow;
self
}
pub fn transport(mut self, kind: TransportKind) -> Self {
self.transport = kind;
self
}
pub fn retry_policy(mut self, policy: Arc<dyn RetryPolicy>) -> Self {
self.retry_policy = policy;
self
}
pub fn restart_policy(mut self, policy: Arc<dyn ConnectionRestartPolicy>) -> Self {
self.restart_policy = policy;
self
}
pub fn device_model(mut self, model: impl Into<String>) -> Self {
self.device_model = model.into();
self
}
pub fn system_version(mut self, version: impl Into<String>) -> Self {
self.system_version = version.into();
self
}
pub fn app_version(mut self, version: impl Into<String>) -> Self {
self.app_version = version.into();
self
}
pub fn system_lang_code(mut self, code: impl Into<String>) -> Self {
self.system_lang_code = code.into();
self
}
pub fn lang_pack(mut self, pack: impl Into<String>) -> Self {
self.lang_pack = pack.into();
self
}
pub fn lang_code(mut self, code: impl Into<String>) -> Self {
self.lang_code = code.into();
self
}
pub fn build(self) -> Result<Config, BuilderError> {
if self.api_id == 0 {
return Err(BuilderError::MissingApiId);
}
if self.api_hash.is_empty() {
return Err(BuilderError::MissingApiHash);
}
Ok(Config {
api_id: self.api_id,
api_hash: self.api_hash,
dc_addr: self.dc_addr,
retry_policy: self.retry_policy,
restart_policy: self.restart_policy,
socks5: self.socks5,
mtproxy: self.mtproxy,
allow_ipv6: self.allow_ipv6,
transport: self.transport,
session_backend: self.session_backend,
catch_up: self.catch_up,
device_model: self.device_model,
system_version: self.system_version,
app_version: self.app_version,
system_lang_code: self.system_lang_code,
lang_pack: self.lang_pack,
lang_code: self.lang_code,
})
}
pub async fn connect(self) -> Result<(Client, ShutdownToken), BuilderError> {
let cfg = self.build()?;
Client::connect(cfg).await.map_err(BuilderError::Connect)
}
}
#[derive(Debug)]
pub enum BuilderError {
MissingApiId,
MissingApiHash,
Connect(InvocationError),
}
impl std::fmt::Display for BuilderError {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
match self {
Self::MissingApiId => f.write_str("ClientBuilder: api_id not set"),
Self::MissingApiHash => f.write_str("ClientBuilder: api_hash not set"),
Self::Connect(e) => write!(f, "ClientBuilder: connect failed: {e}"),
}
}
}
impl std::error::Error for BuilderError {
fn source(&self) -> Option<&(dyn std::error::Error + 'static)> {
match self {
Self::Connect(e) => Some(e),
_ => None,
}
}
}