// Copyright (c) 2023 xmpp-rs contributors. // // This Source Code Form is subject to the terms of the Mozilla Public // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. use std::sync::Arc; use tokio::sync::RwLock; use tokio_xmpp::connect::ServerConnector; use tokio_xmpp::{ jid::{BareJid, Jid}, parsers::{ disco::{DiscoInfoResult, Feature, Identity}, ns, }, AsyncClient as TokioXmppClient, AsyncConfig, }; use crate::{Agent, ClientFeature}; #[derive(Debug)] pub enum ClientType { Bot, Pc, } impl Default for ClientType { fn default() -> Self { ClientType::Bot } } impl ToString for ClientType { fn to_string(&self) -> String { String::from(match self { ClientType::Bot => "bot", ClientType::Pc => "pc", }) } } pub struct ClientBuilder<'a, C: ServerConnector> { jid: BareJid, password: &'a str, server_connector: C, website: String, default_nick: String, lang: Vec, disco: (ClientType, String), features: Vec, resource: Option, } #[cfg(any(feature = "starttls-rust", feature = "starttls-native"))] impl ClientBuilder<'_, tokio_xmpp::connect::starttls::ServerConfig> { pub fn new<'a>( jid: BareJid, password: &'a str, ) -> ClientBuilder<'a, tokio_xmpp::connect::starttls::ServerConfig> { Self::new_with_connector( jid, password, tokio_xmpp::connect::starttls::ServerConfig::UseSrv, ) } } impl ClientBuilder<'_, C> { pub fn new_with_connector<'a>( jid: BareJid, password: &'a str, server_connector: C, ) -> ClientBuilder<'a, C> { ClientBuilder { jid, password, server_connector, website: String::from("https://gitlab.com/xmpp-rs/tokio-xmpp"), default_nick: String::from("xmpp-rs"), lang: vec![String::from("en")], disco: (ClientType::default(), String::from("tokio-xmpp")), features: vec![], resource: None, } } /// Optionally set a resource associated to this device on the client pub fn set_resource(mut self, resource: &str) -> Self { self.resource = Some(resource.to_string()); self } pub fn set_client(mut self, type_: ClientType, name: &str) -> Self { self.disco = (type_, String::from(name)); self } pub fn set_website(mut self, url: &str) -> Self { self.website = String::from(url); self } pub fn set_default_nick(mut self, nick: &str) -> Self { self.default_nick = String::from(nick); self } pub fn set_lang(mut self, lang: Vec) -> Self { self.lang = lang; self } pub fn enable_feature(mut self, feature: ClientFeature) -> Self { self.features.push(feature); self } fn make_disco(&self) -> DiscoInfoResult { let identities = vec![Identity::new( "client", self.disco.0.to_string(), "en", self.disco.1.to_string(), )]; let mut features = vec![Feature::new(ns::DISCO_INFO)]; #[cfg(feature = "avatars")] { if self.features.contains(&ClientFeature::Avatars) { features.push(Feature::new(format!("{}+notify", ns::AVATAR_METADATA))); } } if self.features.contains(&ClientFeature::JoinRooms) { features.push(Feature::new(format!("{}+notify", ns::BOOKMARKS2))); } DiscoInfoResult { node: None, identities, features, extensions: vec![], } } pub fn build(self) -> Agent { let jid: Jid = if let Some(resource) = &self.resource { self.jid.with_resource_str(resource).unwrap().into() } else { self.jid.clone().into() }; let config = AsyncConfig { jid, password: self.password.into(), server: self.server_connector.clone(), }; let mut client = TokioXmppClient::new_with_config(config); client.set_reconnect(true); self.build_impl(client) } // This function is meant to be used for testing build pub(crate) fn build_impl(self, client: TokioXmppClient) -> Agent { let disco = self.make_disco(); let node = self.website; Agent { client, default_nick: Arc::new(RwLock::new(self.default_nick)), lang: Arc::new(self.lang), disco, node, uploads: Vec::new(), awaiting_disco_bookmarks_type: false, } } }