Merge branch 'refactor-unittests' into 'main'

Draft: Refactoring Agent

See merge request xmpp-rs/xmpp-rs!299
This commit is contained in:
Werner Kroneman 2024-04-17 14:53:57 +00:00
commit e5dfc0cd62
4 changed files with 113 additions and 34 deletions

View File

@ -36,7 +36,9 @@ impl<C: ServerConnector> Agent<C> {
lang: &str,
status: &str,
) {
muc::room::join_room(self, room, nick, password, lang, status).await
// Use the provided nickname, or the default if none.
let nick = nick.unwrap_or_else(|| self.default_nick.read().unwrap().to_string());
muc::room::join_room(&mut self.client, room, &nick, password, lang, status).await
}
/// Request to leave a chatroom.
@ -57,7 +59,7 @@ impl<C: ServerConnector> Agent<C> {
lang: impl Into<String>,
status: impl Into<String>,
) {
muc::room::leave_room(self, room_jid, nickname, lang, status).await
muc::room::leave_room(&mut self.client, room_jid, nickname, lang, status).await
}
pub async fn send_message(
@ -67,7 +69,7 @@ impl<C: ServerConnector> Agent<C> {
lang: &str,
text: &str,
) {
message::send::send_message(self, recipient, type_, lang, text).await
message::send::send_message(&mut self.client, recipient, type_, lang, text).await
}
pub async fn send_room_private_message(
@ -90,7 +92,7 @@ impl<C: ServerConnector> Agent<C> {
event_loop::wait_for_events(self).await
}
pub async fn upload_file_with(&mut self, service: &str, path: &Path) {
pub async fn upload_file_with(&mut self, service: Jid, path: &Path) {
upload::send::upload_file_with(self, service, path).await
}

View File

@ -10,10 +10,17 @@ use tokio_xmpp::{
Jid,
};
use crate::Agent;
use crate::agent::TokioXmppClient;
/// Send a text message to a recipient.
///
/// # Arguments
/// - `client`: The XMPP client to use to send the message.
/// - `recipient`: The JID of the recipient.
/// - `type_`: The type of the message.
/// - `lang`: The language of the message.
pub async fn send_message<C: ServerConnector>(
agent: &mut Agent<C>,
client: &mut TokioXmppClient<C>,
recipient: Jid,
type_: MessageType,
lang: &str,
@ -24,5 +31,5 @@ pub async fn send_message<C: ServerConnector>(
message
.bodies
.insert(String::from(lang), Body(String::from(text)));
let _ = agent.client.send_stanza(message.into()).await;
let _ = client.send_stanza(message.into()).await;
}

View File

@ -13,27 +13,58 @@ use tokio_xmpp::{
BareJid,
};
use crate::{Agent, RoomNick};
use crate::agent::TokioXmppClient;
use crate::RoomNick;
pub async fn join_room<C: ServerConnector>(
agent: &mut Agent<C>,
client: &mut TokioXmppClient<C>,
room: BareJid,
nick: Option<String>,
nick: &String,
password: Option<String>,
lang: &str,
status: &str,
) {
let presence = create_muc_join_presence_stanza(room, password, lang, status, nick);
let _ = client.send_stanza(presence.into()).await;
}
/// Create a Presence stanza for joining a MUC room with a specific nickname.
///
/// # Arguments
/// - `room`: The JID of the room to join.
/// - `password`: The password to use to join the room, if any.
/// - `lang`: The language of the status message.
/// - `status`: The status message to send.
/// - `nick`: The nickname to use in the room.
pub fn create_muc_join_presence_stanza(
room: BareJid,
password: Option<String>,
lang: &str,
status: &str,
nick: &String,
) -> Presence {
// Combine the room JID with the nickname to create the JID of the room occupant.
let room_jid = room.with_resource_str(&nick).unwrap();
// Create a presence stanza.
let mut presence = Presence::new(PresenceType::None).with_to(room_jid);
// MUC presence stanza's must have a Muc payload.
let mut muc = Muc::new();
// If a password is provided, include it in the Muc payload.
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);
// Add the Muc payload to the presence stanza.
presence.add_payload(muc);
// Set the user-readable status message.
presence.set_status(String::from(lang), String::from(status));
let _ = agent.client.send_stanza(presence.into()).await;
// Return the presence stanza.
presence
}
/// Send a "leave room" request to the server (specifically, an "unavailable" presence stanza).
@ -51,17 +82,40 @@ pub async fn join_room<C: ServerConnector>(
///
/// # Arguments
///
/// * `client`: The XMPP client to use to send the status message.
/// * `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<C: ServerConnector>(
agent: &mut Agent<C>,
client: &mut TokioXmppClient<C>,
room_jid: BareJid,
nickname: RoomNick,
lang: impl Into<String>,
status: impl Into<String>,
) {
let presence = create_muc_leave_room_status(room_jid, nickname, lang, status);
// Send the presence stanza.
if let Err(e) = client.send_stanza(presence.into()).await {
// Report any errors to the log.
error!("Failed to send leave room presence: {}", e);
}
}
/// Create a Presence stanza for leaving a MUC room that we're in with a specific nickname.
///
/// # Arguments
/// - `room_jid`: The JID of the room to leave.
/// - `nickname`: The nickname we are present in the room with.
/// - `lang`: The language of the status message.
/// - `status`: The status message to send.
pub fn create_muc_leave_room_status(
room_jid: BareJid,
nickname: RoomNick,
lang: impl Into<String> + Sized,
status: impl Into<String> + Sized,
) -> Presence {
// 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(
@ -74,10 +128,5 @@ pub async fn leave_room<C: ServerConnector>(
// TODO: Should this be optional? The XEP says "MAY", but the method signature requires the arguments.
// XEP-0045: "The occupant MAY include normal <status/> 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);
}
presence
}

View File

@ -14,23 +14,44 @@ use tokio_xmpp::{
use crate::Agent;
pub async fn upload_file_with<C: ServerConnector>(
agent: &mut Agent<C>,
service: &str,
path: &Path,
) {
/// Upload a file to the HTTP server associated with a given Jid.
///
/// # Arguments
/// - `agent`: The XMPP agent through which to negociate the request.
/// - `service`: The Jid of the HTTP server to upload the file to.
/// - `path`: The path to the file to upload.
pub async fn upload_file_with<C: ServerConnector>(agent: &mut Agent<C>, service: Jid, path: &Path) {
// Create the IQ request to upload the file.
let request =
Iq::from_get("upload1", slotslot_request_for_file(path).await).with_to(service.clone());
// Record the upload request so we can handle the response later.
agent
.uploads
.push((String::from("upload1"), service, path.to_path_buf()));
// Send the request to the server.
agent.client.send_stanza(request.into()).await.unwrap();
}
/// Create a SlotRequest for a file, representing a request for a URL to upload the file to.
///
/// Note: this function is async because it accesses the file system to read the file's metadata.
///
/// # Arguments
/// - `path`: The path to the file to upload.
async fn slotslot_request_for_file(path: &Path) -> SlotRequest {
// Extract the file's name.
let name = path.file_name().unwrap().to_str().unwrap().to_string();
// Open the file and read its size.
let file = File::open(path).await.unwrap();
let size = file.metadata().await.unwrap().len();
let slot_request = SlotRequest {
// Create a SlotRequest for the file.
SlotRequest {
filename: name,
size: size,
content_type: None,
};
let to = service.parse::<Jid>().unwrap();
let request = Iq::from_get("upload1", slot_request).with_to(to.clone());
agent
.uploads
.push((String::from("upload1"), to, path.to_path_buf()));
agent.client.send_stanza(request.into()).await.unwrap();
}
}