diff --git a/xmpp/src/lib.rs b/xmpp/src/lib.rs index a4accc63..a2132888 100644 --- a/xmpp/src/lib.rs +++ b/xmpp/src/lib.rs @@ -12,8 +12,7 @@ pub use tokio_xmpp::parsers; use tokio_xmpp::parsers::{ disco::DiscoInfoResult, message::{Body, Message, MessageType}, - muc::{user::MucUser, Muc}, - presence::{Presence, Type as PresenceType}, + muc::user::MucUser, }; use tokio_xmpp::AsyncClient as TokioXmppClient; pub use tokio_xmpp::{BareJid, Element, FullJid, Jid}; @@ -27,6 +26,7 @@ pub mod event_loop; pub mod feature; pub mod iq; pub mod message; +pub mod muc; pub mod presence; pub mod pubsub; pub mod upload; @@ -63,17 +63,7 @@ impl Agent { lang: &str, status: &str, ) { - let mut muc = Muc::new(); - if let Some(password) = password { - muc = muc.with_password(password); - } - - let nick = nick.unwrap_or_else(|| self.default_nick.read().unwrap().clone()); - let room_jid = room.with_resource_str(&nick).unwrap(); - let mut presence = Presence::new(PresenceType::None).with_to(room_jid); - presence.add_payload(muc); - presence.set_status(String::from(lang), String::from(status)); - let _ = self.client.send_stanza(presence.into()).await; + muc::room::join_room(self, room, nick, password, lang, status).await } /// Send a "leave room" request to the server (specifically, an "unavailable" presence stanza). @@ -105,24 +95,7 @@ impl Agent { lang: impl Into, status: impl Into, ) { - // XEP-0045 specifies that, to leave a room, the client must send a presence stanza - // with type="unavailable". - let mut presence = Presence::new(PresenceType::Unavailable).with_to( - room_jid - .with_resource_str(nickname.as_str()) - .expect("Invalid room JID after adding resource part."), - ); - - // Optionally, the client may include a status message in the presence stanza. - // TODO: Should this be optional? The XEP says "MAY", but the method signature requires the arguments. - // XEP-0045: "The occupant MAY include normal information in the unavailable presence stanzas" - presence.set_status(lang, status); - - // Send the presence stanza. - if let Err(e) = self.client.send_stanza(presence.into()).await { - // Report any errors to the log. - error!("Failed to send leave room presence: {}", e); - } + muc::room::leave_room(self, room_jid, nickname, lang, status).await } pub async fn send_message( diff --git a/xmpp/src/muc/mod.rs b/xmpp/src/muc/mod.rs new file mode 100644 index 00000000..a3c4b462 --- /dev/null +++ b/xmpp/src/muc/mod.rs @@ -0,0 +1,7 @@ +// 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/. + +pub mod room; diff --git a/xmpp/src/muc/room.rs b/xmpp/src/muc/room.rs new file mode 100644 index 00000000..03c9d1fc --- /dev/null +++ b/xmpp/src/muc/room.rs @@ -0,0 +1,85 @@ +// 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 tokio_xmpp::{ + parsers::{ + muc::Muc, + presence::{Presence, Type as PresenceType}, + }, + BareJid, +}; + +use crate::{Agent, RoomNick}; + +pub async fn join_room( + agent: &mut Agent, + room: BareJid, + nick: Option, + password: Option, + lang: &str, + status: &str, +) { + let mut muc = Muc::new(); + if let Some(password) = password { + muc = muc.with_password(password); + } + + let nick = nick.unwrap_or_else(|| agent.default_nick.read().unwrap().clone()); + let room_jid = room.with_resource_str(&nick).unwrap(); + let mut presence = Presence::new(PresenceType::None).with_to(room_jid); + presence.add_payload(muc); + presence.set_status(String::from(lang), String::from(status)); + let _ = agent.client.send_stanza(presence.into()).await; +} + +/// Send a "leave room" request to the server (specifically, an "unavailable" presence stanza). +/// +/// The returned future will resolve when the request has been sent, +/// not when the room has actually been left. +/// +/// If successful, a `RoomLeft` event should be received later as a confirmation. +/// +/// See: https://xmpp.org/extensions/xep-0045.html#exit +/// +/// Note that this method does NOT remove the room from the auto-join list; the latter +/// is more a list of bookmarks that the account knows about and that have a flag set +/// to indicate that they should be joined automatically after connecting (see the JoinRoom event). +/// +/// Regarding the latter, see the these minutes about auto-join behavior: +/// https://docs.modernxmpp.org/meetings/2019-01-brussels/#bookmarks +/// +/// # Arguments +/// +/// * `room_jid`: The JID of the room to leave. +/// * `nickname`: The nickname to use in the room. +/// * `lang`: The language of the status message. +/// * `status`: The status message to send. +pub async fn leave_room( + agent: &mut Agent, + room_jid: BareJid, + nickname: RoomNick, + lang: impl Into, + status: impl Into, +) { + // XEP-0045 specifies that, to leave a room, the client must send a presence stanza + // with type="unavailable". + let mut presence = Presence::new(PresenceType::Unavailable).with_to( + room_jid + .with_resource_str(nickname.as_str()) + .expect("Invalid room JID after adding resource part."), + ); + + // Optionally, the client may include a status message in the presence stanza. + // TODO: Should this be optional? The XEP says "MAY", but the method signature requires the arguments. + // XEP-0045: "The occupant MAY include normal information in the unavailable presence stanzas" + presence.set_status(lang, status); + + // Send the presence stanza. + if let Err(e) = agent.client.send_stanza(presence.into()).await { + // Report any errors to the log. + error!("Failed to send leave room presence: {}", e); + } +}