diff --git a/internal/reader/processor/processor.go b/internal/reader/processor/processor.go index 0bdf0f61..3c824b66 100644 --- a/internal/reader/processor/processor.go +++ b/internal/reader/processor/processor.go @@ -10,6 +10,9 @@ import ( "strings" "time" + "github.com/tdewolff/minify/v2" + "github.com/tdewolff/minify/v2/html" + "miniflux.app/v2/internal/config" "miniflux.app/v2/internal/metric" "miniflux.app/v2/internal/model" @@ -20,9 +23,6 @@ import ( "miniflux.app/v2/internal/reader/scraper" "miniflux.app/v2/internal/reader/urlcleaner" "miniflux.app/v2/internal/storage" - - "github.com/tdewolff/minify/v2" - "github.com/tdewolff/minify/v2/html" ) var customReplaceRuleRegex = regexp.MustCompile(`rewrite\("([^"]+)"\|"([^"]+)"\)`) @@ -141,6 +141,9 @@ func isBlockedEntry(feed *model.Feed, entry *model.Entry, user *model.User) bool var match bool switch parts[0] { + case "EntryDate": + datePattern := parts[1] + match = isDateMatchingPattern(entry.Date, datePattern) case "EntryTitle": match, _ = regexp.MatchString(parts[1], entry.Title) case "EntryURL": @@ -211,6 +214,9 @@ func isAllowedEntry(feed *model.Feed, entry *model.Entry, user *model.User) bool var match bool switch parts[0] { + case "EntryDate": + datePattern := parts[1] + match = isDateMatchingPattern(entry.Date, datePattern) case "EntryTitle": match, _ = regexp.MatchString(parts[1], entry.Title) case "EntryURL": @@ -462,3 +468,44 @@ func minifyEntryContent(entryContent string) string { return entryContent } + +func isDateMatchingPattern(entryDate time.Time, pattern string) bool { + if pattern == "future" { + return entryDate.After(time.Now()) + } + + parts := strings.SplitN(pattern, ":", 2) + if len(parts) != 2 { + return false + } + + operator := parts[0] + dateStr := parts[1] + + switch operator { + case "before": + targetDate, err := time.Parse("2006-01-02", dateStr) + if err != nil { + return false + } + return entryDate.Before(targetDate) + case "after": + targetDate, err := time.Parse("2006-01-02", dateStr) + if err != nil { + return false + } + return entryDate.After(targetDate) + case "between": + dates := strings.Split(dateStr, ",") + if len(dates) != 2 { + return false + } + startDate, err1 := time.Parse("2006-01-02", dates[0]) + endDate, err2 := time.Parse("2006-01-02", dates[1]) + if err1 != nil || err2 != nil { + return false + } + return entryDate.After(startDate) && entryDate.Before(endDate) + } + return false +} diff --git a/internal/reader/processor/processor_test.go b/internal/reader/processor/processor_test.go index 2a594a4a..9e228366 100644 --- a/internal/reader/processor/processor_test.go +++ b/internal/reader/processor/processor_test.go @@ -75,6 +75,12 @@ func TestAllowEntries(t *testing.T) { {&model.Feed{ID: 1, BlocklistRules: ""}, &model.Entry{Author: "Example", Tags: []string{"example", "something else"}}, &model.User{KeepFilterEntryRules: "EntryAuthor=(?i)example\nEntryTag=(?i)Test"}, true}, {&model.Feed{ID: 1, BlocklistRules: ""}, &model.Entry{Author: "Different", Tags: []string{"example", "something else"}}, &model.User{KeepFilterEntryRules: "EntryAuthor=(?i)example\nEntryTag=(?i)example"}, true}, {&model.Feed{ID: 1, BlocklistRules: ""}, &model.Entry{Author: "Different", Tags: []string{"example", "something else"}}, &model.User{KeepFilterEntryRules: "EntryAuthor=(?i)example\nEntryTag=(?i)Test"}, false}, + {&model.Feed{ID: 1, BlocklistRules: ""}, &model.Entry{Date: time.Now().Add(24 * time.Hour)}, &model.User{KeepFilterEntryRules: "EntryDate=future"}, true}, + {&model.Feed{ID: 1, BlocklistRules: ""}, &model.Entry{Date: time.Now().Add(-24 * time.Hour)}, &model.User{KeepFilterEntryRules: "EntryDate=future"}, false}, + {&model.Feed{ID: 1, BlocklistRules: ""}, &model.Entry{Date: time.Date(2024, 3, 14, 0, 0, 0, 0, time.UTC)}, &model.User{KeepFilterEntryRules: "EntryDate=before:2024-03-15"}, true}, + {&model.Feed{ID: 1, BlocklistRules: ""}, &model.Entry{Date: time.Date(2024, 3, 16, 0, 0, 0, 0, time.UTC)}, &model.User{KeepFilterEntryRules: "EntryDate=after:2024-03-15"}, true}, + {&model.Feed{ID: 1, BlocklistRules: ""}, &model.Entry{Date: time.Date(2024, 3, 10, 0, 0, 0, 0, time.UTC)}, &model.User{KeepFilterEntryRules: "EntryDate=between:2024-03-01,2024-03-15"}, true}, + {&model.Feed{ID: 1, BlocklistRules: ""}, &model.Entry{Date: time.Date(2024, 2, 28, 0, 0, 0, 0, time.UTC)}, &model.User{KeepFilterEntryRules: "EntryDate=between:2024-03-01,2024-03-15"}, false}, } for _, tc := range scenarios { diff --git a/internal/validator/user.go b/internal/validator/user.go index b461f912..a7e05edb 100644 --- a/internal/validator/user.go +++ b/internal/validator/user.go @@ -219,7 +219,7 @@ func validateMediaPlaybackRate(mediaPlaybackRate float64) *locale.LocalizedError func isValidFilterRules(filterEntryRules string, filterType string) *locale.LocalizedError { // Valid Format: FieldName=RegEx\nFieldName=RegEx... - fieldNames := []string{"EntryTitle", "EntryURL", "EntryCommentsURL", "EntryContent", "EntryAuthor", "EntryTag"} + fieldNames := []string{"EntryTitle", "EntryURL", "EntryCommentsURL", "EntryContent", "EntryAuthor", "EntryTag", "EntryDate"} rules := strings.Split(filterEntryRules, "\n") for i, rule := range rules {