split types
Signed-off-by: Maxime “pep” Buquet <pep@bouah.net>
This commit is contained in:
parent
f77b6e5583
commit
ca068ba243
2 changed files with 188 additions and 160 deletions
166
src/main.rs
166
src/main.rs
|
@ -16,16 +16,13 @@
|
|||
#![feature(once_cell)]
|
||||
|
||||
mod error;
|
||||
mod types;
|
||||
|
||||
use crate::error::Error;
|
||||
use crate::types::{send_stanza, Nick, Room, ROOMS};
|
||||
|
||||
use std::collections::HashMap;
|
||||
use std::convert::TryFrom;
|
||||
use std::env::args;
|
||||
use std::iter::IntoIterator;
|
||||
use std::ops::ControlFlow;
|
||||
use std::process::exit;
|
||||
use std::sync::{LazyLock, Mutex};
|
||||
|
||||
use env_logger;
|
||||
use futures::stream::StreamExt;
|
||||
|
@ -34,148 +31,14 @@ use tokio_xmpp::Component;
|
|||
use xmpp_parsers::{
|
||||
disco::{DiscoInfoQuery, DiscoInfoResult, Feature, Identity},
|
||||
iq::{Iq, IqType},
|
||||
message::{Message, Subject, MessageType},
|
||||
muc::{
|
||||
user::{Affiliation, Item as MucItem, Role, Status as MucStatus},
|
||||
Muc, MucUser,
|
||||
},
|
||||
message::Message,
|
||||
muc::Muc,
|
||||
ns,
|
||||
presence::{Presence, Type as PresenceType},
|
||||
presence::Presence,
|
||||
stanza_error::{DefinedCondition, ErrorType, StanzaError},
|
||||
BareJid, Element, FullJid, Jid,
|
||||
BareJid, Element, Jid,
|
||||
};
|
||||
|
||||
async fn send_stanza<E: Into<Element>>(component: &mut Component, el: E) -> Result<(), Error> {
|
||||
let el: Element = el.into();
|
||||
debug!("SEND: {}", String::from(&el));
|
||||
component.send_stanza(el).await?;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub type Nick = String;
|
||||
|
||||
#[derive(Debug)]
|
||||
struct Room {
|
||||
jid: BareJid,
|
||||
occupants: HashMap<BareJid, Occupant>,
|
||||
}
|
||||
|
||||
impl Room {
|
||||
fn new(jid: BareJid) -> Self {
|
||||
Room {
|
||||
jid,
|
||||
occupants: HashMap::new(),
|
||||
}
|
||||
}
|
||||
|
||||
async fn add_session(
|
||||
&mut self,
|
||||
component: &mut Component,
|
||||
realjid: FullJid,
|
||||
nick: Nick,
|
||||
) -> Result<(), Error> {
|
||||
let bare = BareJid::from(realjid.clone());
|
||||
if let Some(occupant) = self.occupants.get_mut(&bare) {
|
||||
occupant.add_session(realjid)?;
|
||||
} else {
|
||||
debug!("{} is joining {}", realjid, self.jid);
|
||||
|
||||
// Ensure nick isn't already assigned
|
||||
let _ = self.occupants.iter().try_for_each(|(_, occupant)| {
|
||||
let nick = nick.clone();
|
||||
if occupant.nick == nick {
|
||||
return Err(Error::NickAlreadyAssigned(nick));
|
||||
}
|
||||
Ok(())
|
||||
})?;
|
||||
|
||||
// Send occupants
|
||||
debug!("Sending occupants for {}", realjid);
|
||||
let presence = Presence::new(PresenceType::None)
|
||||
.with_to(realjid.clone());
|
||||
for (_, occupant) in self.occupants.iter() {
|
||||
for session in occupant.iter() {
|
||||
let presence = presence.clone().with_from(session.clone());
|
||||
send_stanza(component, presence).await?;
|
||||
}
|
||||
}
|
||||
|
||||
// Add into occupants
|
||||
let _ = self
|
||||
.occupants
|
||||
.insert(bare.clone(), Occupant::new(realjid.clone()));
|
||||
|
||||
// Self-presence
|
||||
debug!("Sending self-presence for {}", realjid);
|
||||
let participant: FullJid = self.jid.clone().with_resource(nick);
|
||||
let status = vec![MucStatus::SelfPresence, MucStatus::AssignedNick];
|
||||
let items = vec![MucItem::new(Affiliation::Owner, Role::Moderator)];
|
||||
let self_presence = Presence::new(PresenceType::None)
|
||||
.with_from(participant)
|
||||
.with_to(realjid.clone())
|
||||
.with_payloads(vec![MucUser { status, items }.into()]);
|
||||
send_stanza(component, self_presence).await?;
|
||||
|
||||
// Send subject
|
||||
debug!("Sending subject!");
|
||||
let mut subject = Message::new(Some(Jid::Full(realjid)));
|
||||
subject.from = Some(Jid::Bare(self.jid.clone()));
|
||||
subject.subjects.insert(String::from("en"), Subject(String::from("Hanabi")));
|
||||
subject.type_ = MessageType::Groupchat;
|
||||
send_stanza(component, subject).await?;
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone)]
|
||||
struct Occupant {
|
||||
jid: BareJid,
|
||||
nick: Nick,
|
||||
sessions: Vec<FullJid>,
|
||||
}
|
||||
|
||||
impl Occupant {
|
||||
fn new(fulljid: FullJid) -> Occupant {
|
||||
Occupant {
|
||||
jid: BareJid::from(fulljid.clone()),
|
||||
nick: fulljid.resource.clone(),
|
||||
sessions: vec![fulljid],
|
||||
}
|
||||
}
|
||||
|
||||
fn add_session(&mut self, fulljid: FullJid) -> Result<(), Error> {
|
||||
if BareJid::from(fulljid.clone()) != self.jid {
|
||||
return Err(Error::MismatchJids(Jid::from(fulljid.clone())));
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
impl IntoIterator for Occupant {
|
||||
type Item = FullJid;
|
||||
type IntoIter = std::vec::IntoIter<Self::Item>;
|
||||
|
||||
fn into_iter(self) -> Self::IntoIter {
|
||||
self.sessions.into_iter()
|
||||
}
|
||||
}
|
||||
|
||||
impl Occupant {
|
||||
fn iter(&self) -> std::slice::Iter<'_, FullJid> {
|
||||
self.sessions.iter()
|
||||
}
|
||||
|
||||
fn iter_mut(&mut self) -> std::slice::IterMut<'_, FullJid> {
|
||||
self.sessions.iter_mut()
|
||||
}
|
||||
}
|
||||
|
||||
static mut ROOMS: LazyLock<Mutex<HashMap<BareJid, Room>>> =
|
||||
LazyLock::new(|| Mutex::new(HashMap::new()));
|
||||
|
||||
async fn handle_iq_disco(component: &mut Component, iq: Iq, payload: Element) {
|
||||
match DiscoInfoQuery::try_from(payload) {
|
||||
Ok(DiscoInfoQuery { node }) if node.is_none() => {
|
||||
|
@ -303,20 +166,3 @@ async fn main() {
|
|||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::*;
|
||||
|
||||
use std::str::FromStr;
|
||||
|
||||
use xmpp_parsers::{BareJid, FullJid, Jid};
|
||||
|
||||
#[test]
|
||||
fn occupant_ignore_dup_session() {
|
||||
let fulljid = FullJid::from_str("foo@bar/meh").unwrap();
|
||||
let mut occupant = Occupant::new(fulljid.clone());
|
||||
occupant.add_session(fulljid.clone());
|
||||
assert_eq!(occupant.iter().count(), 1);
|
||||
}
|
||||
}
|
||||
|
|
182
src/types.rs
Normal file
182
src/types.rs
Normal file
|
@ -0,0 +1,182 @@
|
|||
// Copyright (C) 2022-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 <https://www.gnu.org/licenses/>.
|
||||
|
||||
use crate::error::Error;
|
||||
|
||||
use std::collections::HashMap;
|
||||
use std::iter::IntoIterator;
|
||||
use std::sync::{LazyLock, Mutex};
|
||||
|
||||
use log::debug;
|
||||
use tokio_xmpp::Component;
|
||||
use xmpp_parsers::{
|
||||
message::{Message, MessageType, Subject},
|
||||
muc::{
|
||||
user::{Affiliation, Item as MucItem, Role, Status as MucStatus},
|
||||
MucUser,
|
||||
},
|
||||
presence::{Presence, Type as PresenceType},
|
||||
BareJid, Element, FullJid, Jid,
|
||||
};
|
||||
|
||||
pub(crate) async fn send_stanza<E: Into<Element>>(
|
||||
component: &mut Component,
|
||||
elem: E,
|
||||
) -> Result<(), Error> {
|
||||
let elem: Element = elem.into();
|
||||
debug!("SEND: {}", String::from(&elem));
|
||||
component.send_stanza(elem).await?;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub(crate) type Nick = String;
|
||||
|
||||
#[derive(Debug)]
|
||||
pub(crate) struct Room {
|
||||
jid: BareJid,
|
||||
occupants: HashMap<BareJid, Occupant>,
|
||||
}
|
||||
|
||||
impl Room {
|
||||
pub(crate) fn new(jid: BareJid) -> Self {
|
||||
Room {
|
||||
jid,
|
||||
occupants: HashMap::new(),
|
||||
}
|
||||
}
|
||||
|
||||
pub(crate) async fn add_session(
|
||||
&mut self,
|
||||
component: &mut Component,
|
||||
realjid: FullJid,
|
||||
nick: Nick,
|
||||
) -> Result<(), Error> {
|
||||
let bare = BareJid::from(realjid.clone());
|
||||
if let Some(occupant) = self.occupants.get_mut(&bare) {
|
||||
occupant.add_session(realjid)?;
|
||||
} else {
|
||||
debug!("{} is joining {}", realjid, self.jid);
|
||||
|
||||
// Ensure nick isn't already assigned
|
||||
let _ = self.occupants.iter().try_for_each(|(_, occupant)| {
|
||||
let nick = nick.clone();
|
||||
if occupant.nick == nick {
|
||||
return Err(Error::NickAlreadyAssigned(nick));
|
||||
}
|
||||
Ok(())
|
||||
})?;
|
||||
|
||||
// Send occupants
|
||||
debug!("Sending occupants for {}", realjid);
|
||||
let presence = Presence::new(PresenceType::None).with_to(realjid.clone());
|
||||
for (_, occupant) in self.occupants.iter() {
|
||||
for session in occupant.iter() {
|
||||
let presence = presence.clone().with_from(session.clone());
|
||||
send_stanza(component, presence).await?;
|
||||
}
|
||||
}
|
||||
|
||||
// Add into occupants
|
||||
let _ = self
|
||||
.occupants
|
||||
.insert(bare.clone(), Occupant::new(realjid.clone()));
|
||||
|
||||
// Self-presence
|
||||
debug!("Sending self-presence for {}", realjid);
|
||||
let participant: FullJid = self.jid.clone().with_resource(nick);
|
||||
let status = vec![MucStatus::SelfPresence, MucStatus::AssignedNick];
|
||||
let items = vec![MucItem::new(Affiliation::Owner, Role::Moderator)];
|
||||
let self_presence = Presence::new(PresenceType::None)
|
||||
.with_from(participant)
|
||||
.with_to(realjid.clone())
|
||||
.with_payloads(vec![MucUser { status, items }.into()]);
|
||||
send_stanza(component, self_presence).await?;
|
||||
|
||||
// Send subject
|
||||
debug!("Sending subject!");
|
||||
let mut subject = Message::new(Some(Jid::Full(realjid)));
|
||||
subject.from = Some(Jid::Bare(self.jid.clone()));
|
||||
subject
|
||||
.subjects
|
||||
.insert(String::from("en"), Subject(String::from("Hanabi")));
|
||||
subject.type_ = MessageType::Groupchat;
|
||||
send_stanza(component, subject).await?;
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone)]
|
||||
pub(crate) struct Occupant {
|
||||
jid: BareJid,
|
||||
nick: Nick,
|
||||
sessions: Vec<FullJid>,
|
||||
}
|
||||
|
||||
impl Occupant {
|
||||
fn new(fulljid: FullJid) -> Occupant {
|
||||
Occupant {
|
||||
jid: BareJid::from(fulljid.clone()),
|
||||
nick: fulljid.resource.clone(),
|
||||
sessions: vec![fulljid],
|
||||
}
|
||||
}
|
||||
|
||||
pub(crate) fn add_session(&mut self, fulljid: FullJid) -> Result<(), Error> {
|
||||
if BareJid::from(fulljid.clone()) != self.jid {
|
||||
return Err(Error::MismatchJids(Jid::from(fulljid.clone())));
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
impl IntoIterator for Occupant {
|
||||
type Item = FullJid;
|
||||
type IntoIter = std::vec::IntoIter<Self::Item>;
|
||||
|
||||
fn into_iter(self) -> Self::IntoIter {
|
||||
self.sessions.into_iter()
|
||||
}
|
||||
}
|
||||
|
||||
impl Occupant {
|
||||
fn iter(&self) -> std::slice::Iter<'_, FullJid> {
|
||||
self.sessions.iter()
|
||||
}
|
||||
|
||||
fn iter_mut(&mut self) -> std::slice::IterMut<'_, FullJid> {
|
||||
self.sessions.iter_mut()
|
||||
}
|
||||
}
|
||||
|
||||
pub(crate) static mut ROOMS: LazyLock<Mutex<HashMap<BareJid, Room>>> =
|
||||
LazyLock::new(|| Mutex::new(HashMap::new()));
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::*;
|
||||
use std::str::FromStr;
|
||||
use xmpp_parsers::FullJid;
|
||||
|
||||
#[test]
|
||||
fn occupant_ignore_dup_session() {
|
||||
let fulljid = FullJid::from_str("foo@bar/meh").unwrap();
|
||||
let mut occupant = Occupant::new(fulljid.clone());
|
||||
occupant.add_session(fulljid.clone()).unwrap();
|
||||
assert_eq!(occupant.iter().count(), 1);
|
||||
}
|
||||
}
|
Loading…
Reference in a new issue