forked from lavina/lavina
1
0
Fork 0

xmpp: add support leaving MUCs via unavailable presence

This commit is contained in:
Nikita Vilunov 2024-05-26 18:18:30 +02:00
parent 43d105ab23
commit 8a0a0c0935
1 changed files with 80 additions and 31 deletions

View File

@ -23,36 +23,17 @@ impl<'a> XmppConnection<'a> {
server,
// resources in MUCs are members' personas not implemented (yet?)
resource: Some(_),
}) if server.0 == self.hostname_rooms => {
let mut muc_presence = self.retrieve_muc_presence(&name).await?;
muc_presence.id = p.id;
let subject = Message::<()> {
from: Some(Jid {
name: Some(name.clone()),
server: Server(self.hostname_rooms.clone()),
resource: None,
}),
id: None,
to: Some(Jid {
name: Some(self.user.xmpp_name.clone()),
server: Server(self.hostname.clone()),
resource: Some(self.user.xmpp_resource.clone()),
}),
r#type: MessageType::Groupchat,
lang: None,
subject: Some(Subject(None)),
body: None,
custom: vec![],
};
muc_presence.serialize(output);
let messages = self.retrieve_message_history(&name).await?;
for message in messages {
message.serialize(output)
}) if server.0 == self.hostname_rooms => match p.r#type.as_deref() {
None => {
self.join_muc(output, p.id, name).await?;
}
// The subject is the last stanza sent during a MUC join process.
subject.serialize(output);
}
Some("unavailable") => {
self.leave_muc(output, p.id, name).await?;
}
_ => {
tracing::error!("Unimplemented case")
}
},
_ => {
// TODO other presence cases
let response = Presence::<()>::default();
@ -62,6 +43,74 @@ impl<'a> XmppConnection<'a> {
Ok(())
}
async fn join_muc(&mut self, output: &mut Vec<Event<'static>>, id: Option<String>, name: Name) -> Result<()> {
// Response presence
let mut muc_presence = self.retrieve_muc_presence(&name).await?;
muc_presence.id = id;
muc_presence.serialize(output);
// N last messages from the room history before the user joined
let messages = self.retrieve_message_history(&name).await?;
for message in messages {
message.serialize(output)
}
// The subject is the last stanza sent during a MUC join process.
let subject = Message::<()> {
from: Some(Jid {
name: Some(name.clone()),
server: Server(self.hostname_rooms.clone()),
resource: None,
}),
id: None,
to: Some(Jid {
name: Some(self.user.xmpp_name.clone()),
server: Server(self.hostname.clone()),
resource: Some(self.user.xmpp_resource.clone()),
}),
r#type: MessageType::Groupchat,
lang: None,
subject: Some(Subject(None)),
body: None,
custom: vec![],
};
subject.serialize(output);
Ok(())
}
async fn leave_muc(&mut self, output: &mut Vec<Event<'static>>, id: Option<String>, name: Name) -> Result<()> {
self.user_handle.leave_room(RoomId::try_from(name.0.clone())?).await?;
let response = Presence {
to: Some(Jid {
name: Some(self.user.xmpp_name.clone()),
server: Server(self.hostname.clone()),
resource: Some(self.user.xmpp_resource.clone()),
}),
from: Some(Jid {
name: Some(name.clone()),
server: Server(self.hostname_rooms.clone()),
resource: Some(self.user.xmpp_muc_name.clone()),
}),
r#type: Some("unavailable".into()),
custom: vec![XUser {
item: XUserItem {
affiliation: Affiliation::Member,
role: Role::None,
jid: Jid {
name: Some(self.user.xmpp_name.clone()),
server: Server(self.hostname.clone()),
resource: Some(self.user.xmpp_resource.clone()),
},
},
self_presence: true,
just_created: false,
}],
..Default::default()
};
response.serialize(output);
Ok(())
}
#[tracing::instrument(skip(self, output, r#type), name = "XmppConnection::self_presence")]
async fn self_presence(&mut self, output: &mut Vec<Event<'static>>, r#type: Option<&str>) {
match r#type {
@ -92,7 +141,7 @@ impl<'a> XmppConnection<'a> {
#[tracing::instrument(skip(self), name = "XmppConnection::retrieve_muc_presence")]
async fn retrieve_muc_presence(&mut self, name: &Name) -> Result<Presence<XUser>> {
let a = self.user_handle.join_room(RoomId::try_from(name.0.clone())?).await?;
let _ = self.user_handle.join_room(RoomId::try_from(name.0.clone())?).await?;
// TODO handle bans
let response = Presence {
to: Some(Jid {
@ -273,7 +322,7 @@ mod tests {
just_created: false,
}],
..Default::default()
};
};
assert_eq!(expected, response);
drop(conn);