diff --git a/internal/reader/filter/filter.go b/internal/reader/filter/filter.go index 7a160344..5376ac78 100644 --- a/internal/reader/filter/filter.go +++ b/internal/reader/filter/filter.go @@ -21,14 +21,9 @@ const ( ) func IsBlockedEntry(feed *model.Feed, entry *model.Entry, user *model.User) bool { - if user.BlockFilterEntryRules != "" { - if matchesEntryFilterRules(user.BlockFilterEntryRules, entry, feed, filterActionBlock) { - return true - } - } - - if feed.BlockFilterEntryRules != "" { - if matchesEntryFilterRules(feed.BlockFilterEntryRules, entry, feed, filterActionBlock) { + combinedRules := combineFilterRules(user.BlockFilterEntryRules, feed.BlockFilterEntryRules) + if combinedRules != "" { + if matchesEntryFilterRules(combinedRules, entry, feed, filterActionBlock) { return true } } @@ -41,12 +36,9 @@ func IsBlockedEntry(feed *model.Feed, entry *model.Entry, user *model.User) bool } func IsAllowedEntry(feed *model.Feed, entry *model.Entry, user *model.User) bool { - if user.KeepFilterEntryRules != "" { - return matchesEntryFilterRules(user.KeepFilterEntryRules, entry, feed, filterActionAllow) - } - - if feed.KeepFilterEntryRules != "" { - return matchesEntryFilterRules(feed.KeepFilterEntryRules, entry, feed, filterActionAllow) + combinedRules := combineFilterRules(user.KeepFilterEntryRules, feed.KeepFilterEntryRules) + if combinedRules != "" { + return matchesEntryFilterRules(combinedRules, entry, feed, filterActionAllow) } if feed.KeeplistRules == "" { @@ -56,6 +48,24 @@ func IsAllowedEntry(feed *model.Feed, entry *model.Entry, user *model.User) bool return matchesEntryRegexRules(feed.KeeplistRules, entry, feed, filterActionAllow) } +func combineFilterRules(userRules, feedRules string) string { + var combinedRules strings.Builder + + userRules = strings.TrimSpace(userRules) + feedRules = strings.TrimSpace(feedRules) + + if userRules != "" { + combinedRules.WriteString(userRules) + } + if feedRules != "" { + if combinedRules.Len() > 0 { + combinedRules.WriteString("\n") + } + combinedRules.WriteString(feedRules) + } + return combinedRules.String() +} + func matchesEntryFilterRules(rules string, entry *model.Entry, feed *model.Feed, filterAction filterActionType) bool { for rule := range strings.SplitSeq(rules, "\n") { if matchesRule(rule, entry) { diff --git a/internal/reader/filter/filter_test.go b/internal/reader/filter/filter_test.go index c60d43f0..b21a78b1 100644 --- a/internal/reader/filter/filter_test.go +++ b/internal/reader/filter/filter_test.go @@ -42,6 +42,11 @@ func TestBlockingEntries(t *testing.T) { {&model.Feed{ID: 1}, &model.Entry{Date: time.Date(2024, 3, 14, 0, 0, 0, 0, time.UTC)}, &model.User{BlockFilterEntryRules: "EntryDate=before:2024-03-15"}, true}, {&model.Feed{ID: 1}, &model.Entry{Date: time.Date(2024, 3, 14, 0, 0, 0, 0, time.UTC)}, &model.User{BlockFilterEntryRules: "UnknownRuleType=test"}, false}, {&model.Feed{ID: 1, BlockFilterEntryRules: "EntryURL=(?i)example\nEntryTitle=(?i)Test"}, &model.Entry{URL: "https://example.com", Title: "Some Example"}, &model.User{}, true}, + // Test cases for merged user and feed BlockFilterEntryRules + {&model.Feed{ID: 1, BlockFilterEntryRules: "EntryURL=(?i)website"}, &model.Entry{URL: "https://example.com", Title: "Some Title"}, &model.User{BlockFilterEntryRules: " EntryTitle=(?i)title "}, true}, // User rule matches + {&model.Feed{ID: 1, BlockFilterEntryRules: "EntryURL=(?i)example"}, &model.Entry{URL: "https://example.com", Title: "Some Other"}, &model.User{BlockFilterEntryRules: "EntryTitle=(?i)title"}, true}, // Feed rule matches + {&model.Feed{ID: 1, BlockFilterEntryRules: "EntryURL=(?i)example"}, &model.Entry{URL: "https://different.com", Title: "Some Other"}, &model.User{BlockFilterEntryRules: "EntryTitle=(?i)title"}, false}, // Neither rule matches + {&model.Feed{ID: 1, BlockFilterEntryRules: "EntryURL=(?i)example"}, &model.Entry{URL: "https://example.com", Title: "Some Title"}, &model.User{BlockFilterEntryRules: "EntryTitle=(?i)title"}, true}, // Both rules would match } for _, tc := range scenarios { @@ -95,6 +100,11 @@ func TestAllowEntries(t *testing.T) { {&model.Feed{ID: 1}, &model.Entry{Date: time.Date(2024, 2, 28, 0, 0, 0, 0, time.UTC)}, &model.User{KeepFilterEntryRules: "EntryDate=abcd"}, false}, // no colon in rule value {&model.Feed{ID: 1}, &model.Entry{Date: time.Date(2024, 2, 28, 0, 0, 0, 0, time.UTC)}, &model.User{KeepFilterEntryRules: "EntryDate=unknown:2024-03-15"}, false}, // unknown rule type {&model.Feed{ID: 1, KeepFilterEntryRules: "EntryURL=(?i)example\nEntryTitle=(?i)Test"}, &model.Entry{URL: "https://example.com", Title: "Some Example"}, &model.User{}, true}, + // Test cases for merged user and feed KeepFilterEntryRules + {&model.Feed{ID: 1, KeepFilterEntryRules: "EntryURL=(?i)website"}, &model.Entry{URL: "https://example.com", Title: "Some Title"}, &model.User{KeepFilterEntryRules: "EntryTitle=(?i)title"}, true}, // User rule matches + {&model.Feed{ID: 1, KeepFilterEntryRules: "EntryURL=(?i)example"}, &model.Entry{URL: "https://example.com", Title: "Some Other"}, &model.User{KeepFilterEntryRules: "EntryTitle=(?i)title"}, true}, // Feed rule matches + {&model.Feed{ID: 1, KeepFilterEntryRules: "EntryURL=(?i)example"}, &model.Entry{URL: "https://different.com", Title: "Some Other"}, &model.User{KeepFilterEntryRules: "EntryTitle=(?i)title"}, false}, // Neither rule matches + {&model.Feed{ID: 1, KeepFilterEntryRules: "EntryURL=(?i)example"}, &model.Entry{URL: "https://example.com", Title: "Some Title"}, &model.User{KeepFilterEntryRules: "EntryTitle=(?i)title"}, true}, // Both rules would match } for _, tc := range scenarios {