Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add Smart Message Filter #221

Merged
merged 11 commits into from
Feb 28, 2024
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@ Changed:
- `hidden_server_messages` has been changed to `server_messages` and additional customization has been added:
- Exclude messages [join, part, quit].
- Adjust username format.
- Exclude server messages for users who have not messaged in the last X seconds

Fixed:

Expand Down
10 changes: 6 additions & 4 deletions config.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -87,16 +87,18 @@ buffer:
input_visibility: Always

# Control different server messages.
# - exclude [boolean]: exclude the message from showing
# - exclude [All, None, !Smart seconds]:
# - Smart will show a server message if the user has sent a message
# in the given time interval (seconds) prior to the server message
# - user_format [Short, Full]: controls the username formatting
server_messages:
join:
exclude: true
exclude: All
username_format: Short
part:
exclude: false
exclude: None
quit:
exclude: false
exclude: !Smart 1200

# Channel buffer settings
channel:
Expand Down
18 changes: 13 additions & 5 deletions data/src/config/buffer.rs
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,14 @@ pub struct Buffer {
pub server_messages: ServerMessages,
}

#[derive(Debug, Copy, Clone, Default, Deserialize)]
pub enum Exclude {
#[default]
All,
None,
Smart(i64),
}

#[derive(Debug, Clone, Default, Deserialize)]
pub struct ServerMessages {
#[serde(default)]
Expand All @@ -33,18 +41,18 @@ pub struct ServerMessages {

impl ServerMessages {
pub fn get(&self, server: &source::Server) -> ServerMessage {
match server {
source::Server::Join => self.join,
source::Server::Part => self.part,
source::Server::Quit => self.quit,
match server.kind() {
source::server::Kind::Join => self.join,
source::server::Kind::Part => self.part,
source::server::Kind::Quit => self.quit,
}
}
}

#[derive(Debug, Copy, Clone, Default, Deserialize)]
pub struct ServerMessage {
#[serde(default)]
pub exclude: bool,
pub exclude: Exclude,
#[serde(default)]
pub username_format: UsernameFormat,
}
Expand Down
61 changes: 56 additions & 5 deletions data/src/history/manager.rs
Original file line number Diff line number Diff line change
@@ -1,11 +1,12 @@
use std::collections::{HashMap, HashSet};

use chrono::{DateTime, Utc};
use futures::future::BoxFuture;
use futures::{future, Future, FutureExt};
use itertools::Itertools;
use tokio::time::Instant;

use crate::config::buffer::ServerMessages;
use crate::config::buffer::{Exclude, ServerMessages};
use crate::history::{self, History};
use crate::message::{self, Limit};
use crate::time::Posix;
Expand Down Expand Up @@ -437,14 +438,47 @@ impl Data {
return None;
};

let mut most_recent_messages = HashMap::<Nick, DateTime<Utc>>::new();

let filtered = messages
.iter()
.filter(|message| {
if let message::Source::Server(Some(source)) = message.target.source() {
!server_messages.get(source).exclude
} else {
.filter(|message| match message.target.source() {
message::Source::Server(Some(source)) => {
let source_config = server_messages.get(source);

match source_config.exclude {
Exclude::All => false,
Exclude::None => true,
Exclude::Smart(seconds) => {
if let Some(nick) = source.nick() {
!smart_filter_message(
message,
&seconds,
most_recent_messages.get(nick),
)
} else if let Some(nickname) =
message.text.split(' ').collect::<Vec<_>>().get(1)
{
let nick = Nick::from(*nickname);

!smart_filter_message(
message,
&seconds,
most_recent_messages.get(&nick),
)
} else {
true
}
}
}
}
crate::message::Source::User(message_user) => {
most_recent_messages
.insert(message_user.nickname().to_owned(), message.server_time);

true
}
_ => true,
})
.collect::<Vec<_>>();

Expand Down Expand Up @@ -507,6 +541,23 @@ impl Data {
}
}

fn smart_filter_message(
message: &crate::Message,
seconds: &i64,
most_recent_message_server_time: Option<&DateTime<Utc>>,
) -> bool {
let Some(server_time) = most_recent_message_server_time else {
return true;
};

let duration_seconds = message
.server_time
.signed_duration_since(*server_time)
.num_seconds();

duration_seconds > *seconds
}

#[derive(Debug, Clone)]
pub enum Broadcast {
Connecting,
Expand Down
10 changes: 8 additions & 2 deletions data/src/message.rs
Original file line number Diff line number Diff line change
Expand Up @@ -127,11 +127,17 @@ fn target(message: Encoded, our_nick: &Nick) -> Option<Target> {
}),
Command::PART(channel, _) => Some(Target::Channel {
channel,
source: source::Source::Server(Some(source::Server::Part)),
source: source::Source::Server(Some(source::Server::new(
source::server::Kind::Part,
Some(user?.nickname().to_owned()),
))),
}),
Command::JOIN(channel, _) => Some(Target::Channel {
channel,
source: source::Source::Server(Some(source::Server::Join)),
source: source::Source::Server(Some(source::Server::new(
source::server::Kind::Join,
Some(user?.nickname().to_owned()),
))),
}),
Command::Numeric(RPL_TOPIC | RPL_TOPICWHOTIME | RPL_CHANNELMODEIS, params) => {
let channel = params.get(1)?.clone();
Expand Down
5 changes: 4 additions & 1 deletion data/src/message/broadcast.rs
Original file line number Diff line number Diff line change
Expand Up @@ -129,7 +129,10 @@ pub fn quit(
channels,
queries,
false,
Cause::Server(Some(source::Server::Quit)),
Cause::Server(Some(source::Server::new(
source::server::Kind::Quit,
Some(user.nickname().to_owned()),
))),
text,
)
}
Expand Down
59 changes: 51 additions & 8 deletions data/src/message/source.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,8 @@ use serde::{Deserialize, Serialize};

use crate::User;

pub use self::server::Server;

#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
pub enum Source {
User(User),
Expand All @@ -10,14 +12,6 @@ pub enum Source {
Internal(Internal),
}

#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Serialize, Deserialize)]
#[serde(rename_all = "lowercase")]
pub enum Server {
Join,
Part,
Quit,
}

#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Serialize, Deserialize)]
pub enum Internal {
Status(Status),
Expand All @@ -28,3 +22,52 @@ pub enum Status {
Success,
Error,
}

pub mod server {
#![allow(deprecated)]
use serde::{Deserialize, Serialize};

use crate::user::Nick;

#[derive(Debug, Clone, PartialEq, Eq, Hash, Serialize, Deserialize)]
#[serde(untagged)]
pub enum Server {
#[deprecated(note = "use Server::Details")]
Kind(Kind),
Details(Details),
}

impl Server {
pub fn new(kind: Kind, nick: Option<Nick>) -> Self {
Self::Details(Details { kind, nick })
}

pub fn kind(&self) -> Kind {
match self {
Server::Kind(kind) => *kind,
Server::Details(details) => details.kind,
}
}

pub fn nick(&self) -> Option<&Nick> {
match self {
Server::Kind(_) => None,
Server::Details(details) => details.nick.as_ref(),
}
}
}

#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Serialize, Deserialize)]
#[serde(rename_all = "lowercase")]
pub enum Kind {
Join,
Part,
Quit,
}

#[derive(Debug, Clone, PartialEq, Eq, Hash, Serialize, Deserialize)]
pub struct Details {
pub kind: Kind,
pub nick: Option<Nick>,
}
}
Loading