Make blacklist override whitelist
All checks were successful
Test and build Docker image / docker (push) Successful in 30s

Change filter evaluation so blacklist matches take precedence over whitelist matches. Updated evaluator logic to skip entries when blacklist and whitelist both match, adjusted related branches to reflect the new decision flow, and updated a feeds.py comment to clarify the combined decision. Also updated blacklist/whitelist templates copy to reflect the new precedence and adjusted tests to expect blacklist-wins behavior.
This commit is contained in:
Joakim Hellsén 2026-05-04 22:55:53 +02:00
commit d85bc16904
Signed by: Joakim Hellsén
SSH key fingerprint: SHA256:/9h/CsExpFp+PRhsfA0xznFx2CGfTT5R/kpuFfUgEQk
7 changed files with 24 additions and 34 deletions

View file

@ -732,7 +732,7 @@ def send_to_discord(reader: Reader | None = None, feed: Feed | None = None, *, d
else:
logger.warning("No entry link found for feed %s, falling back to regular processing", entry.feed.url)
# Send the entry to Discord as it is not blacklisted or feed has a whitelist.
# Send the entry to Discord because the combined blacklist/whitelist decision allowed it.
execute_webhook(webhook, entry, reader=effective_reader)
# If we only want to send one entry, we will break the loop. This is used when testing this function.

View file

@ -121,7 +121,7 @@ def evaluate_entry_filters(
) -> EntryFilterDecision:
"""Evaluate one entry against blacklist and whitelist settings.
Whitelist matches take precedence over blacklist matches.
Blacklist matches take precedence over whitelist matches.
Args:
entry: The entry to evaluate.
@ -140,10 +140,20 @@ def evaluate_entry_filters(
has_blacklist_filters: bool = has_filter_values(normalized_blacklist_values)
has_whitelist_filters: bool = has_filter_values(normalized_whitelist_values)
if whitelist_match and blacklist_match:
if blacklist_match and whitelist_match:
return EntryFilterDecision(
should_send=True,
reason=f"Sent because {whitelist_match.description}; whitelist overrides blacklist.",
should_send=False,
reason=f"Skipped because {blacklist_match.description}; blacklist overrides whitelist.",
blacklist_match=blacklist_match,
whitelist_match=whitelist_match,
has_blacklist_filters=has_blacklist_filters,
has_whitelist_filters=has_whitelist_filters,
)
if blacklist_match:
return EntryFilterDecision(
should_send=False,
reason=f"Skipped because {blacklist_match.description}.",
blacklist_match=blacklist_match,
whitelist_match=whitelist_match,
has_blacklist_filters=has_blacklist_filters,
@ -160,16 +170,6 @@ def evaluate_entry_filters(
has_whitelist_filters=has_whitelist_filters,
)
if has_whitelist_filters and blacklist_match:
return EntryFilterDecision(
should_send=False,
reason=f"Skipped because {blacklist_match.description} and no whitelist rule matched.",
blacklist_match=blacklist_match,
whitelist_match=whitelist_match,
has_blacklist_filters=has_blacklist_filters,
has_whitelist_filters=has_whitelist_filters,
)
if has_whitelist_filters:
return EntryFilterDecision(
should_send=False,
@ -180,16 +180,6 @@ def evaluate_entry_filters(
has_whitelist_filters=has_whitelist_filters,
)
if blacklist_match:
return EntryFilterDecision(
should_send=False,
reason=f"Skipped because {blacklist_match.description}.",
blacklist_match=blacklist_match,
whitelist_match=whitelist_match,
has_blacklist_filters=has_blacklist_filters,
has_whitelist_filters=has_whitelist_filters,
)
return EntryFilterDecision(
should_send=True,
reason="Sent because no active filter blocked it.",

View file

@ -22,7 +22,7 @@
<p class="mb-2">
Plain text matching is case-insensitive and partial, so <code>orld</code> matches <code>World of Warcraft</code>.
</p>
<p class="mb-2">Whitelist matches still win. If an entry matches both, the preview keeps it as sent.</p>
<p class="mb-2">Blacklist matches win. If an entry matches both, the preview keeps it as skipped.</p>
<p class="mb-0">Keep the left side for editing and the right side for checking what gets removed.</p>
</div>
</div>

View file

@ -20,7 +20,7 @@
<p class="mb-2">
Plain text matching is case-insensitive and partial, so <code>orld</code> matches <code>World of Warcraft</code>.
</p>
<p class="mb-2">When an entry matches both lists, whitelist still wins and the preview shows it as sent.</p>
<p class="mb-2">When an entry matches both lists, blacklist wins and the preview shows it as skipped.</p>
<p class="mb-0">Saved blacklist rules remain active while you preview whitelist edits.</p>
</div>
</div>