Skip to content

Commit

Permalink
broadcast quit and nickname
Browse files Browse the repository at this point in the history
  • Loading branch information
casperstorm committed Jul 6, 2023
1 parent 603a5aa commit d6cfbf5
Show file tree
Hide file tree
Showing 6 changed files with 252 additions and 70 deletions.
60 changes: 51 additions & 9 deletions data/src/client.rs
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,18 @@ pub enum State {
Ready(Connection),
}

#[derive(Debug)]
pub enum Brodcast {
Quit(User, Option<String>),
Nickname(String, String, bool),
}

#[derive(Debug)]
pub enum Event {
Single(Message),
Brodcast(Brodcast),
}

#[derive(Debug)]
pub struct Connection {
client: Client,
Expand Down Expand Up @@ -60,15 +72,13 @@ impl Connection {
}
}

fn receive(&mut self, message: message::Encoded) -> Option<Message> {
fn receive(&mut self, message: message::Encoded) -> Option<Event> {
log::trace!("Message received => {:?}", *message);

self.handle(&message);

Message::received(message, self.nickname())
self.handle(message)
}

fn handle(&mut self, message: &message::Encoded) {
fn handle(&mut self, message: message::Encoded) -> Option<Event> {
use irc::proto::{Command, Response};

match &message.command {
Expand All @@ -77,20 +87,34 @@ impl Connection {
irc::proto::Prefix::ServerName(_) => None,
irc::proto::Prefix::Nickname(nick, _, _) => Some(nick),
}) else {
return;
return None;
};

if self.resolved_nick.as_ref() == Some(old_nick) {
self.resolved_nick = Some(nick.clone())
let changed_own_nickname = self.resolved_nick.as_ref() == Some(old_nick);
if changed_own_nickname {
self.resolved_nick = Some(nick.clone());
}

return Some(Event::Brodcast(Brodcast::Nickname(
nick.clone(),
old_nick.clone(),
changed_own_nickname,
)));
}
Command::Response(Response::RPL_WELCOME, args) => {
if let Some(nick) = args.first() {
self.resolved_nick = Some(nick.to_string());
}
}
Command::QUIT(comment) => {
let user = message.user()?;

return Some(Event::Brodcast(Brodcast::Quit(user, comment.clone())));
}
_ => {}
}

Some(Event::Single(Message::received(message, self.nickname())?))
}

fn sync(&mut self) {
Expand Down Expand Up @@ -183,7 +207,7 @@ impl Map {
self.connection(server).map(Connection::nickname)
}

pub fn receive(&mut self, server: &Server, message: message::Encoded) -> Option<Message> {
pub fn receive(&mut self, server: &Server, message: message::Encoded) -> Option<Event> {
self.connection_mut(server)
.and_then(|connection| connection.receive(message))
}
Expand All @@ -208,6 +232,24 @@ impl Map {
.unwrap_or_default()
}

pub fn get_user_channels(&self, server: &Server, nick: NickRef) -> Vec<String> {
self.connection(server)
.map(|connection| {
connection
.channels()
.iter()
.filter(|channel| {
connection
.users(channel)
.iter()
.any(|user| user.nickname() == nick)
})
.cloned()
.collect::<Vec<_>>()
})
.unwrap_or_default()
}

pub fn iter(&self) -> std::collections::btree_map::Iter<Server, State> {
self.0.iter()
}
Expand Down
49 changes: 42 additions & 7 deletions data/src/history/manager.rs
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ use crate::history::{self, History};
use crate::message::{self, Limit};
use crate::time::Posix;
use crate::user::{Nick, NickRef};
use crate::{server, Buffer, Input, Server};
use crate::{server, Buffer, Input, Server, User};

#[derive(Debug, Clone, PartialEq, Eq, Hash)]
pub struct Resource {
Expand Down Expand Up @@ -260,7 +260,7 @@ impl Manager {
}
})
.cloned();
let queries = map
let mut queries = map
.keys()
.filter_map(|kind| {
if let history::Kind::Query(nick) = kind {
Expand All @@ -272,11 +272,35 @@ impl Manager {
.cloned();

let messages = match broadcast {
Broadcast::Disconnected => {
message::broadcast::disconnected(channels, queries).collect::<Vec<_>>()
Broadcast::Disconnected => message::broadcast::disconnected(channels, queries),
Broadcast::Reconnected => message::broadcast::reconnected(channels, queries),
Broadcast::Quit {
user,
comment,
user_channels,
} => {
let user_query = queries.find(|nick| user.nickname() == *nick);

message::broadcast::quit(user_channels, user_query, &user, &comment)
}
Broadcast::Reconnected => {
message::broadcast::reconnected(channels, queries).collect::<Vec<_>>()
Broadcast::Nickname {
new_nick,
old_nick,
changed_own_nickname,
user_channels,
} => {
let user_query = queries.find(|nick| {
let old_nick = NickRef::from(old_nick.as_str());
old_nick == *nick
});

message::broadcast::nickname(
user_channels,
user_query,
&new_nick,
&old_nick,
changed_own_nickname,
)
}
};

Expand Down Expand Up @@ -438,8 +462,19 @@ impl Data {
}
}

#[derive(Debug, Clone, Copy)]
#[derive(Debug, Clone)]
pub enum Broadcast {
Disconnected,
Reconnected,
Quit {
user: User,
comment: Option<String>,
user_channels: Vec<String>,
},
Nickname {
new_nick: String,
old_nick: String,
changed_own_nickname: bool,
user_channels: Vec<String>,
},
}
122 changes: 75 additions & 47 deletions data/src/message.rs
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,24 @@ pub type Channel = String;
#[derive(Debug, Clone)]
pub struct Encoded(proto::Message);

impl Encoded {
pub fn user(&self) -> Option<User> {
fn not_empty(s: &str) -> Option<&str> {
(!s.is_empty()).then_some(s)
}

let prefix = self.prefix.as_ref()?;
match prefix {
proto::Prefix::Nickname(nickname, username, hostname) => Some(User::new(
Nick::from(nickname.as_str()),
not_empty(username),
not_empty(hostname),
)),
_ => None,
}
}
}

impl std::ops::Deref for Encoded {
type Target = proto::Message;

Expand Down Expand Up @@ -122,24 +140,8 @@ impl Message {
}
}

fn user(message: &Encoded) -> Option<User> {
fn not_empty(s: &str) -> Option<&str> {
(!s.is_empty()).then_some(s)
}

let prefix = message.prefix.as_ref()?;
match prefix {
proto::Prefix::Nickname(nickname, username, hostname) => Some(User::new(
Nick::from(nickname.as_str()),
not_empty(username),
not_empty(hostname),
)),
_ => None,
}
}

fn source(message: Encoded, our_nick: NickRef) -> Option<Source> {
let user = user(&message);
let user = message.user();

match message.0.command {
// Channel
Expand Down Expand Up @@ -252,7 +254,7 @@ fn server_time(message: &Encoded) -> DateTime<Utc> {
}

fn text(message: &Encoded, our_nick: NickRef) -> Option<String> {
let user = user(message);
let user = message.user();
match &message.command {
proto::Command::TOPIC(_, topic) => {
let user = user?;
Expand All @@ -267,7 +269,7 @@ fn text(message: &Encoded, our_nick: NickRef) -> Option<String> {
.map(|text| format!(" ({text})"))
.unwrap_or_default();

Some(format!("⟵ {user}{text} has left the channel"))
Some(format!("⟵ {user} has left the channel{text}"))
}
proto::Command::JOIN(_, _, _) | proto::Command::SAJOIN(_, _) => {
let user = user?;
Expand Down Expand Up @@ -365,54 +367,80 @@ pub(crate) mod broadcast {
use super::{Direction, Message, Sender, Source};
use crate::time::Posix;
use crate::user::Nick;
use crate::User;

fn expand(
channels: impl IntoIterator<Item = String>,
queries: impl IntoIterator<Item = Nick>,
f: fn(source: Source) -> Message,
) -> impl Iterator<Item = Message> {
include_server: bool,
text: String,
) -> Vec<Message> {
let message = |source, text| -> Message {
Message {
received_at: Posix::now(),
server_time: Utc::now(),
direction: Direction::Received,
source,
text,
}
};

channels
.into_iter()
.map(move |channel| f(Source::Channel(channel, Sender::Server)))
.map(|channel| message(Source::Channel(channel, Sender::Server), text.clone()))
.chain(
queries
.into_iter()
.map(move |nick| f(Source::Query(nick, Sender::Server))),
.map(|nick| message(Source::Query(nick, Sender::Server), text.clone())),
)
.chain(Some(f(Source::Server)))
.chain(include_server.then(|| message(Source::Server, text.clone())))
.collect()
}

pub fn disconnected(
channels: impl IntoIterator<Item = String>,
queries: impl IntoIterator<Item = Nick>,
) -> impl Iterator<Item = Message> {
fn message(source: Source) -> Message {
Message {
received_at: Posix::now(),
server_time: Utc::now(),
direction: Direction::Received,
source,
text: " ∙ connection to server lost".into(),
}
}

expand(channels, queries, message)
) -> Vec<Message> {
let text = " ∙ connection to server lost".into();
expand(channels, queries, true, text)
}

pub fn reconnected(
channels: impl IntoIterator<Item = String>,
queries: impl IntoIterator<Item = Nick>,
) -> impl Iterator<Item = Message> {
fn message(source: Source) -> Message {
Message {
received_at: Posix::now(),
server_time: Utc::now(),
direction: Direction::Received,
source,
text: " ∙ connection to server restored".into(),
}
}
) -> Vec<Message> {
let text = " ∙ connection to server restored".into();
expand(channels, queries, true, text)
}

pub fn quit(
channels: impl IntoIterator<Item = String>,
queries: impl IntoIterator<Item = Nick>,
user: &User,
comment: &Option<String>,
) -> Vec<Message> {
let comment = comment
.as_ref()
.map(|comment| format!(" ({comment})"))
.unwrap_or_default();
let text = format!("⟵ {user} has quit{comment}");

expand(channels, queries, false, text)
}

pub fn nickname(
channels: impl IntoIterator<Item = String>,
queries: impl IntoIterator<Item = Nick>,
new_nick: &str,
old_nick: &str,
changed_own_nickname: bool,
) -> Vec<Message> {
let text = if changed_own_nickname {
format!(" ∙ You're now known as {new_nick}")
} else {
format!(" ∙ {old_nick} is now known as {new_nick}")
};

expand(channels, queries, message)
expand(channels, queries, false, text)
}
}
6 changes: 6 additions & 0 deletions data/src/user.rs
Original file line number Diff line number Diff line change
Expand Up @@ -184,6 +184,12 @@ impl<'a> Ord for NickRef<'a> {
}
}

impl<'a> PartialEq<Nick> for NickRef<'a> {
fn eq(&self, other: &Nick) -> bool {
self.0.eq(other.0.as_str())
}
}

#[derive(Debug, Clone)]
pub struct AccessLevel(data::AccessLevel);

Expand Down
Loading

0 comments on commit d6cfbf5

Please sign in to comment.