xmpp: new Config struct for Agent, with bookmarks_autojoin for now

Signed-off-by: pep <pep@bouah.net>
This commit is contained in:
pep 2025-11-13 20:23:45 +01:00
commit 147b79ca52
6 changed files with 84 additions and 18 deletions

View file

@ -43,6 +43,7 @@ XXXX-YY-ZZ [ RELEASER <admin@localhost> ]
stanzas. stanzas.
- Added documentation on `Event` enum. - Added documentation on `Event` enum.
- Update to edition 2024 - Update to edition 2024
- Add Config::Config struct as a user-facing Agent configuration method.
* Fixes: * Fixes:
- Use tokio::sync::RwLock not std::sync::RwLock (!432) - Use tokio::sync::RwLock not std::sync::RwLock (!432)
- The default caps node has been shortened to https://xmpp.rs since we - The default caps node has been shortened to https://xmpp.rs since we

View file

@ -12,7 +12,7 @@ use tokio::io;
use tokio::sync::RwLock; use tokio::sync::RwLock;
use crate::{ use crate::{
Error, Event, RoomNick, event_loop, Config, Error, Event, RoomNick, event_loop,
jid::{BareJid, Jid}, jid::{BareJid, Jid},
message, muc, message, muc,
parsers::disco::DiscoInfoResult, parsers::disco::DiscoInfoResult,
@ -24,6 +24,7 @@ use tokio_xmpp::{Stanza, stanzastream::StanzaToken};
pub struct Agent { pub struct Agent {
pub(crate) client: TokioXmppClient, pub(crate) client: TokioXmppClient,
pub(crate) config: Arc<RwLock<Config>>,
pub(crate) default_nick: Arc<RwLock<RoomNick>>, pub(crate) default_nick: Arc<RwLock<RoomNick>>,
pub(crate) lang: Arc<Vec<String>>, pub(crate) lang: Arc<Vec<String>>,
pub(crate) disco: DiscoInfoResult, pub(crate) disco: DiscoInfoResult,
@ -39,6 +40,7 @@ pub struct Agent {
impl Agent { impl Agent {
pub fn new( pub fn new(
client: TokioXmppClient, client: TokioXmppClient,
config: Config,
default_nick: RoomNick, default_nick: RoomNick,
lang: Vec<String>, lang: Vec<String>,
disco: DiscoInfoResult, disco: DiscoInfoResult,
@ -46,6 +48,7 @@ impl Agent {
) -> Agent { ) -> Agent {
Agent { Agent {
client, client,
config: Arc::new(RwLock::new(config)),
default_nick: Arc::new(RwLock::new(default_nick)), default_nick: Arc::new(RwLock::new(default_nick)),
lang: Arc::new(lang), lang: Arc::new(lang),
disco, disco,
@ -58,6 +61,13 @@ impl Agent {
} }
} }
/// Reset the agent configuration to the provided Config struct
// TODO: refresh everything that is affected by this reset?
pub async fn set_config(&mut self, config: Config) {
let mut c = self.config.write().await;
*c = config;
}
pub async fn disconnect(self) -> Result<(), Error> { pub async fn disconnect(self) -> Result<(), Error> {
self.client.send_end().await self.client.send_end().await
} }

View file

@ -9,7 +9,7 @@ use crate::tokio_xmpp::connect::{DnsConfig, StartTlsServerConnector};
use core::str::FromStr; use core::str::FromStr;
use crate::{ use crate::{
Agent, ClientFeature, RoomNick, Agent, ClientFeature, Config, RoomNick,
jid::{BareJid, Jid, ResourceRef}, jid::{BareJid, Jid, ResourceRef},
parsers::{ parsers::{
disco::{DiscoInfoResult, Feature, Identity}, disco::{DiscoInfoResult, Feature, Identity},
@ -43,6 +43,7 @@ pub struct ClientBuilder<'a, C: ServerConnector> {
jid: BareJid, jid: BareJid,
password: &'a str, password: &'a str,
server_connector: C, server_connector: C,
config: Config,
website: String, website: String,
default_nick: RoomNick, default_nick: RoomNick,
lang: Vec<String>, lang: Vec<String>,
@ -73,6 +74,7 @@ impl<C: ServerConnector> ClientBuilder<'_, C> {
jid, jid,
password, password,
server_connector, server_connector,
config: Config::default(),
website: String::from("https://gitlab.com/xmpp-rs/tokio-xmpp"), website: String::from("https://gitlab.com/xmpp-rs/tokio-xmpp"),
default_nick: RoomNick::from_str("xmpp-rs").unwrap(), default_nick: RoomNick::from_str("xmpp-rs").unwrap(),
lang: vec![String::from("en")], lang: vec![String::from("en")],
@ -89,6 +91,11 @@ impl<C: ServerConnector> ClientBuilder<'_, C> {
self self
} }
pub fn set_config(mut self, config: Config) -> Self {
self.config = config;
self
}
pub fn set_client(mut self, type_: ClientType, name: &str) -> Self { pub fn set_client(mut self, type_: ClientType, name: &str) -> Self {
self.disco = (type_, String::from(name)); self.disco = (type_, String::from(name));
self self
@ -169,6 +176,13 @@ impl<C: ServerConnector> ClientBuilder<'_, C> {
let disco = self.make_disco(); let disco = self.make_disco();
let node = self.website; let node = self.website;
Agent::new(client, self.default_nick, self.lang, disco, node) Agent::new(
client,
self.config,
self.default_nick,
self.lang,
disco,
node,
)
} }
} }

31
xmpp/src/config.rs Normal file
View file

@ -0,0 +1,31 @@
// Copyright (c) 2025 Crate authors
//
// 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/.
/// Store Agent configuration. Differs from state which is generated at runtime
pub struct Config {
/// Synchronize bookmarks based on autojoin flag.
/// The client will join and leave based on the value of the `autojoin` flag on the (pubsub)
/// bookmark item.
/// If this `bookmarks_autojoin` attribute is set to false, `autojoin` set to false won't make
/// the client leave a room, neither will the removal of a bookmark item. This will only happen
/// after the client is restarted, as these items won't be automatically joined anymore.
/// <https://xmpp.org/extensions/xep-0402.html#notification>
pub bookmarks_autojoin: bool,
}
impl Default for Config {
fn default() -> Self {
Self::new()
}
}
impl Config {
fn new() -> Self {
Config {
bookmarks_autojoin: true,
}
}
}

View file

@ -65,6 +65,7 @@ use parsers::message::Id as MessageId;
pub mod agent; pub mod agent;
pub mod builder; pub mod builder;
pub mod config;
pub mod delay; pub mod delay;
pub mod disco; pub mod disco;
pub mod event; pub mod event;
@ -79,6 +80,7 @@ pub mod upload;
pub use agent::Agent; pub use agent::Agent;
pub use builder::{ClientBuilder, ClientType}; pub use builder::{ClientBuilder, ClientType};
pub use config::Config;
pub use event::Event; pub use event::Event;
pub use feature::ClientFeature; pub use feature::ClientFeature;

View file

@ -66,18 +66,22 @@ pub(crate) async fn handle_event(
status: None, status: None,
}) })
.await; .await;
} else {
if agent.config.read().await.bookmarks_autojoin {
// So maybe another client of ours left the room... let's leave it too
agent.leave_room(LeaveRoomSettings::new(jid)).await;
}
} }
} else {
// So maybe another client of ours left the room... let's leave it too
agent.leave_room(LeaveRoomSettings::new(jid)).await;
} }
} }
Err(err) => println!("not bookmark: {}", err), Err(err) => println!("not bookmark: {}", err),
} }
} else if let [item] = &retracted[..] { } else if let [item] = &retracted[..] {
let jid = BareJid::from_str(&item.0).unwrap(); if agent.config.read().await.bookmarks_autojoin {
let jid = BareJid::from_str(&item.0).unwrap();
agent.leave_room(LeaveRoomSettings::new(jid)).await; agent.leave_room(LeaveRoomSettings::new(jid)).await;
}
} else { } else {
error!("No published or retracted item in pubsub event!"); error!("No published or retracted item in pubsub event!");
} }
@ -146,8 +150,10 @@ pub(crate) async fn handle_iq_result(
.await; .await;
} }
} else { } else {
// Leave the room that is no longer autojoin if agent.config.read().await.bookmarks_autojoin {
agent.leave_room(LeaveRoomSettings::new(jid)).await; // Leave the room that is no longer autojoin
agent.leave_room(LeaveRoomSettings::new(jid)).await;
}
} }
} }
Err(err) => { Err(err) => {
@ -156,16 +162,18 @@ pub(crate) async fn handle_iq_result(
} }
} }
// Now we leave the rooms that are no longer in the bookmarks if agent.config.read().await.bookmarks_autojoin {
let mut rooms_to_leave: Vec<BareJid> = Vec::new(); // Now we leave the rooms that are no longer in the bookmarks
for (room, _nick) in &agent.rooms_joined { let mut rooms_to_leave: Vec<BareJid> = Vec::new();
if !new_room_list.contains(&room) { for (room, _nick) in &agent.rooms_joined {
rooms_to_leave.push(room.clone()); if !new_room_list.contains(&room) {
rooms_to_leave.push(room.clone());
}
} }
}
for room in rooms_to_leave { for room in rooms_to_leave {
agent.leave_room(LeaveRoomSettings::new(room)).await; agent.leave_room(LeaveRoomSettings::new(room)).await;
}
} }
} }
_ => unimplemented!(), _ => unimplemented!(),