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

regex matches/not-matches support for sb #187

Merged
merged 1 commit into from
Jan 26, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions migrations/20230126051155_regex_filtering_for_starboards.sql
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
ALTER TABLE starboards ADD COLUMN matches TEXT;
ALTER TABLE starboards ADD COLUMN not_matches TEXT;
1 change: 1 addition & 0 deletions src/constants.rs
Original file line number Diff line number Diff line change
Expand Up @@ -45,6 +45,7 @@ pub const VOTE_RECOUNT: (u64, Duration) = (5, Duration::from_secs(30));
// Common Validation
pub const MAX_NAME_LENGTH: u32 = 32;
pub const MIN_NAME_LENGTH: u32 = 3;
pub const MAX_REGEX_LENGTH: u32 = 100;

// AutoStar Validation
pub const MAX_MAX_CHARS: i16 = 5_000;
Expand Down
11 changes: 9 additions & 2 deletions src/core/starboard/handle.rs
Original file line number Diff line number Diff line change
Expand Up @@ -62,16 +62,22 @@ pub struct RefreshMessage {
sql_message: Option<Arc<DbMessage>>,
orig_message: Option<MessageResult>,
configs: Option<Arc<Vec<Arc<StarboardConfig>>>>,
is_premium: bool,
}

impl RefreshMessage {
pub fn new(bot: Arc<StarboardBot>, message_id: Id<MessageMarker>) -> RefreshMessage {
pub fn new(
bot: Arc<StarboardBot>,
message_id: Id<MessageMarker>,
is_premium: bool,
) -> RefreshMessage {
RefreshMessage {
bot,
message_id,
configs: None,
sql_message: None,
orig_message: None,
is_premium,
}
}

Expand Down Expand Up @@ -262,9 +268,10 @@ impl RefreshStarboard {
&self.refresh.bot,
&self.config,
&orig,
embedder.orig_message.is_missing(),
&embedder.orig_message,
points,
violates_exclusive_group,
self.refresh.is_premium,
)
.await?;

Expand Down
7 changes: 5 additions & 2 deletions src/core/starboard/link_events.rs
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ use twilight_model::{

use crate::{
client::bot::StarboardBot,
core::premium::is_premium::is_guild_premium,
database::{DbMessage, Starboard, StarboardMessage, StarboardOverride},
errors::StarboardResult,
utils::{id_as_i64::GetI64, into_id::IntoId},
Expand All @@ -23,7 +24,8 @@ pub async fn handle_message_update(
None => return Ok(()),
};

let mut refresh = RefreshMessage::new(bot, event.id);
let is_premium = is_guild_premium(&bot, msg.guild_id).await?;
let mut refresh = RefreshMessage::new(bot, event.id, is_premium);
refresh.set_sql_message(msg);
refresh.refresh(true).await?;

Expand Down Expand Up @@ -100,7 +102,8 @@ pub async fn handle_message_delete(
}
};

let mut refresh = RefreshMessage::new(bot, msg.message_id.into_id());
let is_premium = is_guild_premium(&bot, msg.guild_id).await?;
let mut refresh = RefreshMessage::new(bot, msg.message_id.into_id(), is_premium);
if !must_force {
refresh.set_sql_message(msg);
}
Expand Down
87 changes: 67 additions & 20 deletions src/core/starboard/msg_status.rs
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
use crate::{
client::bot::StarboardBot, database::DbMessage, errors::StarboardResult, utils::into_id::IntoId,
cache::MessageResult, client::bot::StarboardBot, database::DbMessage, errors::StarboardResult,
utils::into_id::IntoId,
};

use super::config::StarboardConfig;
Expand All @@ -15,40 +16,86 @@ pub enum MessageStatus {

pub async fn get_message_status(
bot: &StarboardBot,
starboard_config: &StarboardConfig,
config: &StarboardConfig,
message: &DbMessage,
deleted: bool,
message_obj: &MessageResult,
points: i32,
violates_exclusive_group: bool,
is_premium: bool,
) -> StarboardResult<MessageStatus> {
let guild_id = starboard_config.starboard.guild_id.into_id();
let sb_channel_id = starboard_config.starboard.channel_id.into_id();
let deleted = matches!(message_obj, MessageResult::Missing);

let guild_id = config.starboard.guild_id.into_id();
let sb_channel_id = config.starboard.channel_id.into_id();
let sb_is_nsfw = bot
.cache
.fog_channel_nsfw(bot, guild_id, sb_channel_id)
.await?;

let sb_is_nsfw = match sb_is_nsfw {
Some(val) => val,
None => return Ok(MessageStatus::Update(starboard_config.resolved.link_edits)),
None => return Ok(MessageStatus::Update(config.resolved.link_edits)),
};

if (deleted && starboard_config.resolved.link_deletes)
if (deleted && config.resolved.link_deletes)
|| (message.is_nsfw && !sb_is_nsfw)
|| message.trashed
{
Ok(MessageStatus::Remove)
} else if message.forced_to.contains(&starboard_config.starboard.id) {
Ok(MessageStatus::Send(starboard_config.resolved.link_edits))
} else if violates_exclusive_group {
Ok(MessageStatus::Remove)
} else if message.frozen {
Ok(MessageStatus::Update(false))
} else if points >= starboard_config.resolved.required as _ {
Ok(MessageStatus::Send(starboard_config.resolved.link_edits))
} else if points <= starboard_config.resolved.required_remove as _ {
Ok(MessageStatus::Remove)
} else {
Ok(MessageStatus::Update(starboard_config.resolved.link_edits))
return Ok(MessageStatus::Remove);
}

if message.forced_to.contains(&config.starboard.id) {
return Ok(MessageStatus::Send(config.resolved.link_edits));
}
if violates_exclusive_group {
return Ok(MessageStatus::Remove);
}

if message.frozen {
return Ok(MessageStatus::Update(false));
}

if points <= config.resolved.required_remove as _ {
return Ok(MessageStatus::Remove);
}

if validate_regex(config, message_obj, is_premium) {
#[allow(clippy:collapsible_if)]
if points >= config.resolved.required as _ {
return Ok(MessageStatus::Send(config.resolved.link_edits));
}
}

Ok(MessageStatus::Update(config.resolved.link_edits))
}

fn validate_regex(config: &StarboardConfig, message_obj: &MessageResult, is_premium: bool) -> bool {
if !is_premium {
return true;
}

if config.resolved.matches.is_none() && config.resolved.not_matches.is_none() {
return true;
}

let MessageResult::Ok(message_obj) = message_obj else {
return false;
};

if let Some(re) = &config.resolved.matches {
if let Ok(re) = regex::Regex::new(re) {
if !re.is_match(&message_obj.content) {
return false;
}
}
}
if let Some(re) = &config.resolved.not_matches {
if let Ok(re) = regex::Regex::new(re) {
if re.is_match(&message_obj.content) {
return false;
}
}
}

true
}
8 changes: 5 additions & 3 deletions src/core/starboard/reaction_events.rs
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ use twilight_model::gateway::payload::incoming::{ReactionAdd, ReactionRemove};

use crate::{
client::bot::StarboardBot,
core::{emoji::SimpleEmoji, stats::refresh_xp},
core::{emoji::SimpleEmoji, premium::is_premium::is_guild_premium, stats::refresh_xp},
database::{DbMember, DbMessage, DbUser, Vote},
errors::StarboardResult,
utils::{id_as_i64::GetI64, into_id::IntoId},
Expand Down Expand Up @@ -132,7 +132,8 @@ pub async fn handle_reaction_add(
.await?;
}

let mut refresh = RefreshMessage::new(bot.clone(), event.message_id);
let is_premium = is_guild_premium(&bot, guild_id.get_i64()).await?;
let mut refresh = RefreshMessage::new(bot.clone(), event.message_id, is_premium);
refresh.set_configs(configs.into_iter().map(Arc::new).collect());
refresh.set_sql_message(orig_msg);
refresh.refresh(false).await?;
Expand Down Expand Up @@ -182,7 +183,8 @@ pub async fn handle_reaction_remove(
Vote::delete(&bot.pool, orig.message_id, config.starboard.id, user_id).await?;
}

let mut refresh = RefreshMessage::new(bot.clone(), event.message_id);
let is_premim = is_guild_premium(&bot, guild_id.get_i64()).await?;
let mut refresh = RefreshMessage::new(bot.clone(), event.message_id, is_premim);
refresh.set_sql_message(orig);
refresh.set_configs(configs.into_iter().map(Arc::new).collect());
refresh.refresh(false).await?;
Expand Down
8 changes: 6 additions & 2 deletions src/core/starboard/recount.rs
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,10 @@ use twilight_model::id::{

use crate::{
client::bot::StarboardBot,
core::emoji::{EmojiCommon, SimpleEmoji},
core::{
emoji::{EmojiCommon, SimpleEmoji},
premium::is_premium::is_guild_premium,
},
database::{DbMember, DbMessage, DbUser, Vote},
errors::StarboardResult,
utils::{id_as_i64::GetI64, into_id::IntoId},
Expand Down Expand Up @@ -85,7 +88,8 @@ pub async fn recount_votes(
}
}

let mut refresh = RefreshMessage::new(bot.clone(), message_id);
let is_premium = is_guild_premium(&bot, guild_id_i64).await?;
let mut refresh = RefreshMessage::new(bot.clone(), message_id, is_premium);
refresh.set_sql_message(orig);
refresh.refresh(false).await?;

Expand Down
2 changes: 2 additions & 0 deletions src/database/helpers/settings/overrides.rs
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,8 @@ macro_rules! call_with_override_settings {
require_image,
older_than,
newer_than,
matches,
not_matches,
enabled,
autoreact_upvote,
autoreact_downvote,
Expand Down
2 changes: 2 additions & 0 deletions src/database/helpers/settings/starboard.rs
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,8 @@ macro_rules! call_with_starboard_settings {
require_image,
older_than,
newer_than,
matches,
not_matches,
enabled,
autoreact_upvote,
autoreact_downvote,
Expand Down
4 changes: 4 additions & 0 deletions src/database/models/starboard_override_values.rs
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,10 @@ pub struct OverrideValues {
pub require_image: Option<bool>,
pub older_than: Option<i64>,
pub newer_than: Option<i64>,
#[serde(deserialize_with = "null_to_some_none", default)]
pub matches: Option<Option<String>>,
#[serde(deserialize_with = "null_to_some_none", default)]
pub not_matches: Option<Option<String>>,

// Behavior
pub enabled: Option<bool>,
Expand Down
2 changes: 2 additions & 0 deletions src/database/models/starboard_settings.rs
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,8 @@ pub struct StarboardSettings {
pub require_image: bool,
pub older_than: i64,
pub newer_than: i64,
pub matches: Option<String>,
pub not_matches: Option<String>,

// Behavior
pub enabled: bool,
Expand Down
1 change: 1 addition & 0 deletions src/database/validation/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,5 +2,6 @@ pub mod color;
pub mod cooldown;
pub mod mentions;
pub mod name;
pub mod regex;
pub mod starboard_settings;
pub mod time_delta;
20 changes: 20 additions & 0 deletions src/database/validation/regex.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
use crate::constants;

pub fn validate_regex(input: String, is_premium: bool) -> Result<Option<String>, String> {
if !is_premium {
return Err("The `matches` and `not-matches` settings require premium.".to_string());
}

if input.len() > constants::MAX_REGEX_LENGTH as usize {
return Err(format!(
"The `matches` and `not-matches` settings must be under {} characters.",
constants::MAX_REGEX_LENGTH,
));
}

if input == ".*" {
Ok(None)
} else {
Ok(Some(input))
}
}
24 changes: 24 additions & 0 deletions src/interactions/commands/chat/overrides/edit/requirements.rs
Original file line number Diff line number Diff line change
Expand Up @@ -53,6 +53,11 @@ pub struct EditRequirements {
/// How new a post must be in order for it to be voted on (e.g. "1 hour"). Use 0 to disable.
#[command(rename = "newer-than")]
newer_than: Option<String>,
/// (Premium) Content that messages must match to be starred (supports regex). Use ".*" to disable.
matches: Option<String>,
#[command(rename = "not-matches")]
/// (Premium) content that messages must not match to be starred (supports regex). Use ".*" to disable.
not_matches: Option<String>,
}

impl EditRequirements {
Expand Down Expand Up @@ -167,6 +172,25 @@ impl EditRequirements {
return Ok(());
}

if let Some(val) = self.matches {
match validation::regex::validate_regex(val, is_prem) {
Err(why) => {
ctx.respond_str(&why, true).await?;
return Ok(());
}
Ok(val) => settings.matches = Some(val),
}
}
if let Some(val) = self.not_matches {
match validation::regex::validate_regex(val, is_prem) {
Err(why) => {
ctx.respond_str(&why, true).await?;
return Ok(());
}
Ok(val) => settings.not_matches = Some(val),
}
}

StarboardOverride::update_settings(&ctx.bot.pool, ov.id, settings).await?;
ctx.respond_str(
&format!("Updated settings for override '{}'.", self.name),
Expand Down
1 change: 1 addition & 0 deletions src/interactions/commands/chat/overrides/view.rs
Original file line number Diff line number Diff line change
Expand Up @@ -73,6 +73,7 @@ impl ViewOverride {
.inline()
.build(),
)
.field(EmbedFieldBuilder::new("Regex Matching", pretty.regex).build())
.build();

ctx.respond(ctx.build_resp().embeds([embed]).build())
Expand Down
24 changes: 24 additions & 0 deletions src/interactions/commands/chat/starboard/edit/requirements.rs
Original file line number Diff line number Diff line change
Expand Up @@ -52,6 +52,11 @@ pub struct EditRequirements {
/// How new a post must be in order for it to be voted on (e.g. "1 hour"). Use 0 to disable.
#[command(rename = "newer-than")]
newer_than: Option<String>,
/// (Premium) Content that messages must match to be starred (supports regex). Use ".*" to disable.
matches: Option<String>,
#[command(rename = "not-matches")]
/// (Premium) content that messages must not match to be starred (supports regex). Use ".*" to disable.
not_matches: Option<String>,
}

impl EditRequirements {
Expand Down Expand Up @@ -154,6 +159,25 @@ impl EditRequirements {
return Ok(());
}

if let Some(val) = self.matches {
match validation::regex::validate_regex(val, is_prem) {
Err(why) => {
ctx.respond_str(&why, true).await?;
return Ok(());
}
Ok(val) => starboard.settings.matches = val,
}
}
if let Some(val) = self.not_matches {
match validation::regex::validate_regex(val, is_prem) {
Err(why) => {
ctx.respond_str(&why, true).await?;
return Ok(());
}
Ok(val) => starboard.settings.not_matches = val,
}
}

starboard.update_settings(&ctx.bot.pool).await?;
ctx.respond_str(
&format!("Updated settings for starboard '{}'.", self.name),
Expand Down
1 change: 1 addition & 0 deletions src/interactions/commands/chat/starboard/view.rs
Original file line number Diff line number Diff line change
Expand Up @@ -68,6 +68,7 @@ impl ViewStarboard {
.inline()
.build(),
)
.field(EmbedFieldBuilder::new("Regex Matching", pretty.regex).build())
.build();

ctx.respond(ctx.build_resp().embeds([embed]).build())
Expand Down
Loading