// Copyright (C) 2023-2099 The crate authors. // // 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::hooks::{format_hook, Hook}; use crate::Error; use log::debug; use tokio::{signal::ctrl_c, sync::mpsc}; use xmpp::jid::{BareJid, Jid, ResourcePart}; use xmpp::parsers::message::MessageType; use xmpp::{ message::send::RawMessageSettings, muc::room::JoinRoomSettings, Agent, ClientBuilder, ClientFeature, ClientType, Event, RoomNick, }; pub struct XmppClient { is_online: bool, agent: Agent, rooms: Vec, nickname: ResourcePart, } impl XmppClient { pub fn new( jid: BareJid, password: &str, rooms: Vec, nickname: ResourcePart, ) -> XmppClient { let agent = ClientBuilder::new(jid, password) .set_client(ClientType::Bot, "xmpp-rs") .set_website("https://gitlab.com/xmpp-rs/xmpp-rs") .set_default_nick(&nickname) .enable_feature(ClientFeature::JoinRooms) .build(); XmppClient { is_online: false, agent, rooms, nickname, } } pub async fn next(&mut self) { for event in self.agent.wait_for_events().await { match event { Event::Online => { self.is_online = true; debug!("XMPP Online"); for room in &self.rooms { self.agent .join_room(JoinRoomSettings { room: room.clone(), nick: Some(RoomNick::from_resource_ref(self.nickname.as_ref())), password: None, status: Some(("en", "Hi there!")), }) .await } } Event::Disconnected(e) => { self.is_online = false; debug!("XMPP Disconnected: {e}"); } _ => { debug!("XMPP Event not supported") } } } } pub async fn receive(&mut self, mut rx: mpsc::UnboundedReceiver) { loop { tokio::select! { _ = ctrl_c() => { return; // Disconnecting }, _ = self.next() => (), wh = rx.recv() => { if let Some(hook) = wh { debug!("XMPP Bot Received Hook"); self.hook(hook).await } } } } } pub async fn hook(&mut self, wh: Hook) { debug!("XMPP Bot Processing Hook"); if let Some(display) = format_hook(&wh) { debug!("Hook: {}", display); for room in &self.rooms { self.agent .send_raw_message(RawMessageSettings { recipient: Jid::from(room.clone()), message_type: MessageType::Groupchat, message: &display, lang: Some("en"), payloads: Vec::new(), }) .await } debug!("XMPP Bot Processed Hook"); } } pub async fn disconnect(self) -> Result<(), Error> { log::info!("Disconnecting..."); Ok(self.agent.disconnect().await?) } }