heliwatch/heliwatch/src/jabber.rs

102 lines
3.3 KiB
Rust

use std::str::FromStr;
use std::convert::TryFrom;
use futures::{SinkExt, StreamExt};
use tokio::sync::mpsc;
use tokio_xmpp::{AsyncClient, Packet, Event};
use xmpp_parsers::{Jid, BareJid, FullJid, Element};
use xmpp_parsers::message::{Body, Message, MessageType};
use xmpp_parsers::presence::{Presence, Show as PresenceShow, Type as PresenceType};
use xmpp_parsers::muc::Muc;
#[derive(Clone)]
pub struct Handle {
room_jid: BareJid,
tx: mpsc::Sender<Packet>,
}
impl Handle {
pub async fn send_message(&self, msg: String) {
let stanza = make_room_message(self.room_jid.clone(), msg);
self.tx.send(Packet::Stanza(stanza)).await.unwrap();
}
}
pub async fn run(jid: String, password: String, muc_jid: String) -> Handle {
let muc_jid: FullJid = match FullJid::from_str(&muc_jid) {
Ok(jid) => jid,
Err(err) => panic!("MUC Jid invalid: {:?}", err),
};
let (tx, mut rx) = mpsc::channel(1);
let handle = Handle {
room_jid: muc_jid.clone().into(),
tx: tx.clone(),
};
let mut client = AsyncClient::new(&jid, &password).unwrap();
loop {
match client.next().await {
Some(Event::Online { .. }) => {
println!("XMPP client now online at {}", jid);
let packet = Packet::Stanza(make_join_presence(muc_jid.clone()));
client.send(packet).await
.unwrap();
break;
}
Some(_) => {}
None => panic!("XMPP cannot connect"),
}
}
let (mut sink, mut stream) = client.split();
tokio::task::spawn_local(async move {
while let Some(event) = stream.next().await {
match event {
Event::Stanza(el) => {
if el.is("presence", "jabber:client") {
match Presence::try_from(el) {
Ok(presence) => {
if presence.from == Some(Jid::Full(muc_jid.clone())) {
if presence.type_ == PresenceType::Error {
println!("Failed to enter MUC {:?}", muc_jid);
} else {
println!("Entered MUC {:?}", muc_jid);
}
}
},
Err(err) => println!("Received invalid presence: {:?}", err),
}
}
}
_ => {}
}
}
panic!("XMPP end")
});
tokio::task::spawn_local(async move {
while let Some(packet) = rx.recv().await {
sink.send(packet).await.unwrap();
}
panic!("channel end")
});
handle
}
pub fn make_room_message(room_jid: BareJid, text: String) -> Element {
let mut message = Message::new(Some(Jid::Bare(room_jid)));
message.type_ = MessageType::Groupchat;
message.bodies.insert("de".to_string(), Body(text));
message.into()
}
fn make_join_presence(muc_jid: FullJid) -> Element {
let mut presence = Presence::new(PresenceType::None)
.with_to(Jid::Full(muc_jid))
.with_show(PresenceShow::Dnd);
presence.set_status("de".to_string(), "Augen und Ohren nach oben".to_string());
presence.add_payload(Muc::new());
presence.into()
}