2023-12-29 23:28:57 +00:00
|
|
|
// 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/.
|
|
|
|
|
2024-12-18 18:14:02 +00:00
|
|
|
use crate::{
|
2024-12-17 16:21:08 +00:00
|
|
|
jid::{BareJid, ResourcePart, ResourceRef},
|
2024-12-18 18:14:02 +00:00
|
|
|
message::send::RawMessageSettings,
|
2023-12-29 23:28:57 +00:00
|
|
|
parsers::{
|
2024-12-18 18:14:02 +00:00
|
|
|
message::MessageType,
|
2023-12-29 23:28:57 +00:00
|
|
|
muc::Muc,
|
|
|
|
presence::{Presence, Type as PresenceType},
|
|
|
|
},
|
2024-12-18 18:14:02 +00:00
|
|
|
Agent,
|
2023-12-29 23:28:57 +00:00
|
|
|
};
|
|
|
|
|
2024-12-16 19:19:51 +00:00
|
|
|
#[derive(Clone, Debug)]
|
|
|
|
pub struct JoinRoomSettings<'a> {
|
|
|
|
pub room: BareJid,
|
2024-12-17 16:21:08 +00:00
|
|
|
pub nick: Option<ResourcePart>,
|
2024-12-16 19:19:51 +00:00
|
|
|
pub password: Option<String>,
|
|
|
|
pub status: Option<(&'a str, &'a str)>,
|
|
|
|
}
|
|
|
|
|
|
|
|
impl<'a> JoinRoomSettings<'a> {
|
|
|
|
pub fn new(room: BareJid) -> Self {
|
|
|
|
Self {
|
|
|
|
room,
|
|
|
|
nick: None,
|
|
|
|
password: None,
|
|
|
|
status: None,
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2024-12-17 16:21:08 +00:00
|
|
|
pub fn with_nick(mut self, nick: impl AsRef<ResourceRef>) -> Self {
|
2024-12-16 19:19:51 +00:00
|
|
|
self.nick = Some(nick.as_ref().into());
|
|
|
|
self
|
|
|
|
}
|
|
|
|
|
|
|
|
pub fn with_password(mut self, password: impl AsRef<str>) -> Self {
|
|
|
|
self.password = Some(password.as_ref().into());
|
|
|
|
self
|
|
|
|
}
|
|
|
|
|
|
|
|
pub fn with_status(mut self, lang: &'a str, content: &'a str) -> Self {
|
|
|
|
self.status = Some((lang, content));
|
|
|
|
self
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
/// TODO: this method should add bookmark and ensure autojoin is true
|
2024-08-20 14:54:48 +00:00
|
|
|
pub async fn join_room<'a>(agent: &mut Agent, settings: JoinRoomSettings<'a>) {
|
2024-12-16 19:19:51 +00:00
|
|
|
let JoinRoomSettings {
|
|
|
|
room,
|
|
|
|
nick,
|
|
|
|
password,
|
|
|
|
status,
|
|
|
|
} = settings;
|
|
|
|
|
2024-12-16 17:43:05 +00:00
|
|
|
if agent.rooms_joining.contains_key(&room) {
|
|
|
|
// We are already joining
|
|
|
|
warn!("Requesting to join again room {room} which is already joining...");
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
if !agent.rooms_joined.contains_key(&room) {
|
|
|
|
// We are already joined, cannot join
|
|
|
|
warn!("Requesting to join room {room} which is already joined...");
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
2023-12-29 23:28:57 +00:00
|
|
|
let mut muc = Muc::new();
|
|
|
|
if let Some(password) = password {
|
|
|
|
muc = muc.with_password(password);
|
|
|
|
}
|
|
|
|
|
2024-08-06 14:04:04 +00:00
|
|
|
let nick = if let Some(nick) = nick {
|
|
|
|
nick
|
|
|
|
} else {
|
|
|
|
agent.default_nick.read().await.clone()
|
|
|
|
};
|
|
|
|
|
2024-12-17 16:21:08 +00:00
|
|
|
let room_jid = room.with_resource(&nick);
|
2023-12-29 23:28:57 +00:00
|
|
|
let mut presence = Presence::new(PresenceType::None).with_to(room_jid);
|
|
|
|
presence.add_payload(muc);
|
2024-12-16 19:19:51 +00:00
|
|
|
|
|
|
|
let (lang, status) = status.unwrap_or(("", ""));
|
2023-12-29 23:28:57 +00:00
|
|
|
presence.set_status(String::from(lang), String::from(status));
|
2024-12-16 19:19:51 +00:00
|
|
|
|
2023-12-29 23:28:57 +00:00
|
|
|
let _ = agent.client.send_stanza(presence.into()).await;
|
2024-12-16 17:43:05 +00:00
|
|
|
|
|
|
|
agent.rooms_joining.insert(room, nick);
|
2023-12-29 23:28:57 +00:00
|
|
|
}
|
|
|
|
|
2024-12-16 19:19:51 +00:00
|
|
|
#[derive(Clone, Debug)]
|
|
|
|
pub struct LeaveRoomSettings<'a> {
|
|
|
|
pub room: BareJid,
|
|
|
|
pub status: Option<(&'a str, &'a str)>,
|
|
|
|
}
|
|
|
|
|
|
|
|
impl<'a> LeaveRoomSettings<'a> {
|
|
|
|
pub fn new(room: BareJid) -> Self {
|
|
|
|
Self { room, status: None }
|
|
|
|
}
|
|
|
|
|
|
|
|
pub fn with_status(mut self, lang: &'a str, content: &'a str) -> Self {
|
|
|
|
self.status = Some((lang, content));
|
|
|
|
self
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2023-12-29 23:28:57 +00:00
|
|
|
/// 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.
|
|
|
|
///
|
2024-01-27 17:18:58 +00:00
|
|
|
/// If successful, a `RoomLeft` event should be received later as a confirmation. See [XEP-0045](https://xmpp.org/extensions/xep-0045.html#exit).
|
2023-12-29 23:28:57 +00:00
|
|
|
///
|
2024-12-16 19:19:51 +00:00
|
|
|
/// TODO: this method should set autojoin false on bookmark
|
2024-08-20 14:54:48 +00:00
|
|
|
pub async fn leave_room<'a>(agent: &mut Agent, settings: LeaveRoomSettings<'a>) {
|
2024-12-16 19:19:51 +00:00
|
|
|
let LeaveRoomSettings { room, status } = settings;
|
|
|
|
|
2024-12-16 17:43:05 +00:00
|
|
|
if agent.rooms_leaving.contains_key(&room) {
|
|
|
|
// We are already leaving
|
|
|
|
warn!("Requesting to leave again room {room} which is already leaving...");
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
if !agent.rooms_joined.contains_key(&room) {
|
|
|
|
// We are not joined, cannot leave
|
|
|
|
warn!("Requesting to leave room {room} which is not joined...");
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
// Get currently-used nickname
|
|
|
|
let nickname = agent.rooms_joined.get(&room).unwrap();
|
|
|
|
|
2023-12-29 23:28:57 +00:00
|
|
|
// 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(
|
2024-12-16 19:19:51 +00:00
|
|
|
room.with_resource_str(nickname.as_str())
|
2023-12-29 23:28:57 +00:00
|
|
|
.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 <status/> information in the unavailable presence stanzas"
|
2024-12-16 19:19:51 +00:00
|
|
|
if let Some((lang, content)) = status {
|
|
|
|
presence.set_status(lang, content);
|
|
|
|
}
|
2023-12-29 23:28:57 +00:00
|
|
|
|
|
|
|
// 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);
|
|
|
|
}
|
2024-12-16 17:43:05 +00:00
|
|
|
|
2024-12-17 16:21:08 +00:00
|
|
|
agent.rooms_leaving.insert(room, nickname.clone());
|
2023-12-29 23:28:57 +00:00
|
|
|
}
|
2024-12-16 19:19:51 +00:00
|
|
|
|
|
|
|
#[derive(Clone, Debug)]
|
|
|
|
pub struct RoomMessageSettings<'a> {
|
|
|
|
pub room: BareJid,
|
|
|
|
pub message: &'a str,
|
|
|
|
pub lang: Option<&'a str>,
|
|
|
|
}
|
|
|
|
|
|
|
|
impl<'a> RoomMessageSettings<'a> {
|
|
|
|
pub fn new(room: BareJid, message: &'a str) -> Self {
|
|
|
|
Self {
|
|
|
|
room,
|
|
|
|
message,
|
|
|
|
lang: None,
|
|
|
|
}
|
|
|
|
}
|
2024-12-16 19:31:35 +00:00
|
|
|
|
|
|
|
pub fn with_lang(mut self, lang: &'a str) -> Self {
|
|
|
|
self.lang = Some(lang);
|
|
|
|
self
|
|
|
|
}
|
2024-12-16 19:19:51 +00:00
|
|
|
}
|
|
|
|
|
2024-08-20 14:54:48 +00:00
|
|
|
pub async fn send_room_message<'a>(agent: &mut Agent, settings: RoomMessageSettings<'a>) {
|
2024-12-16 19:19:51 +00:00
|
|
|
let RoomMessageSettings {
|
|
|
|
room,
|
|
|
|
message,
|
|
|
|
lang,
|
|
|
|
} = settings;
|
|
|
|
|
2024-12-17 18:31:52 +00:00
|
|
|
// TODO: check that room is in agent.joined_rooms
|
2024-12-17 20:23:51 +00:00
|
|
|
agent
|
|
|
|
.send_raw_message(
|
|
|
|
RawMessageSettings::new(room.into(), MessageType::Groupchat, message)
|
|
|
|
.with_lang_option(lang),
|
|
|
|
)
|
|
|
|
.await;
|
2024-12-16 19:19:51 +00:00
|
|
|
}
|