// Dergchat, a free XMPP client. // Copyright (C) 2023 Werner Kroneman // // This program is free software: you can redistribute it and/or modify // it under the terms of the GNU Affero General Public License as // published by the Free Software Foundation, either version 3 of the // License, or (at your option) any later version. // // This program is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU Affero General Public License for more details. // // You should have received a copy of the GNU Affero General Public License // along with this program. If not, see . use crate::types::{LoginCredentials, Message}; use dioxus::hooks::{UnboundedReceiver, UseRef}; use futures_util::stream::StreamExt; use jid::BareJid; use log::info; use std::collections::HashMap; use std::future::Future; use tokio::select; use xmpp::parsers::message::MessageType; use xmpp::Agent; async fn handle_event( event: xmpp::Event, agent: &mut xmpp::Agent, messages: &mut UseRef>>, ) { match event { xmpp::Event::JoinRoom(jid, _conference) => { println!("Will auto-join room: {}", &jid); agent.join_room(jid, None, None, "en/us", "online").await; } xmpp::Event::RoomJoined(room_jid) => { println!("Joined room: {}", &room_jid); messages.with_mut(move |m| { m.entry(room_jid.clone()).or_insert(vec![]); }); } xmpp::Event::RoomLeft(room_jid) => { println!("Left room: {}", &room_jid); messages.with_mut(move |m| { m.remove(&room_jid); }); } xmpp::Event::ChatMessage(_id, sender, body) => { println!("Message from {}: {}", &sender, &body.0); messages.with_mut(move |m| { m.entry(sender.clone()).or_insert(vec![]).push(Message { sender: sender.to_string(), body: body.0, }); }); } xmpp::Event::RoomMessage(_id, room_jid, sender_nick, body) => { println!( "Message in {} from {}: {}", &room_jid, &sender_nick, &body.0 ); messages.with_mut(move |m| { m.entry(room_jid.clone()).or_insert(vec![]).push(Message { sender: sender_nick, body: body.0, }) }); } xmpp::Event::RoomPrivateMessage(_id, room_jid, sender_nick, body) => { println!( "Private message in {} from {}: {}", &room_jid, &sender_nick, &body.0 ); messages.with_mut(move |m| { m.entry(room_jid.clone()).or_insert(vec![]).push(Message { sender: sender_nick, body: body.0, }) }); } _ => { log::debug!("Received unsupported event {:?}", event); } } } #[derive(Debug)] pub enum NetworkCommand { TryLogin { credentials: LoginCredentials }, JoinRoom { room: BareJid }, LeaveRoom { room: BareJid }, SendMessage { recipient: BareJid, message: String }, } pub fn xmpp_mainloop<'a>( agent: &'a mut Agent, mut room_data: &'a mut UseRef>>, commands: &'a mut UnboundedReceiver, ) -> impl Future + Sized + 'a { async move { loop { select! { events = agent.wait_for_events() => { if let Some(events) = events { for event in events { handle_event(event, agent, &mut room_data).await; } } else { info!("Disconnected"); } }, command = commands.next() => { if let Some(command) = command { match command { NetworkCommand::JoinRoom { room } => { agent.join_room(room.clone(), None, None, "en-us", "online").await; }, NetworkCommand::LeaveRoom { room } => { agent.leave_room( room, "dergchat".to_string(), "en-us", "User left the room.").await; }, NetworkCommand::SendMessage { recipient, message } => { agent.send_message(recipient.into(), MessageType::Groupchat, "en-us", &message).await; }, NetworkCommand::TryLogin { credentials: _ } => { panic!("Already logged in."); }, } } else { info!("Command channel closed"); break; } }, } } } }