v1.5.3: filter vereist titel-match of totaalscore >= 3

Regel C: doorlaten bij (titel-match >= 1) of (totaalscore >= 3).
Een treffer in de titel is een sterke OV-intentie (score 2 volstaat);
treffers alleen in de samenvatting moeten dubbel bevestigd worden.
Weert bv. woningbrand-berichten die via 'bussen rijden om'/'busverkeer'
in de lead binnenkwamen, zonder de 112/trein/staking-berichten te raken.
Gevalideerd op 30d kamerhistorie: 96 doorlaat -> 7 geblokkeerd (allemaal ruis).
This commit is contained in:
Eddy de Vink 2026-09-08 14:11:46 +02:00
commit cb6d00e707
2 changed files with 33 additions and 18 deletions

View file

@ -158,23 +158,38 @@ class RSSBot(Plugin):
except Exception: except Exception:
self.log.exception("Fatal error while polling feeds") self.log.exception("Fatal error while polling feeds")
@staticmethod
def _matches_in(text: str, match_filter: str) -> int:
"""Aantal aaneengesloten match-clusters in text (0 als geen match).
Aaneengesloten matches (bv 'NS-trein', 'openbaar vervoer') tellen als 1."""
spans = sorted((m.start(), m.end()) for m in re.finditer(match_filter, text))
if not spans:
return 0
clusters, prev_end = 1, spans[0][1]
for s, e in spans[1:]:
if s - prev_end <= 1:
prev_end = max(prev_end, e)
else:
clusters += 1
prev_end = e
return clusters
@staticmethod @staticmethod
def _filter_score(title: str, summary: str, match_filter: str) -> int: def _filter_score(title: str, summary: str, match_filter: str) -> int:
"""Score = 2× hits in titel + 1× hits in summary. Aaneengesloten matches """Score = 2× hits in titel + 1× hits in summary."""
(bv 'NS-trein', 'openbaar vervoer') tellen als 1 cluster.""" return 2 * RSSBot._matches_in(title, match_filter) + RSSBot._matches_in(summary, match_filter)
def count(text: str) -> int:
spans = sorted((m.start(), m.end()) for m in re.finditer(match_filter, text)) @staticmethod
if not spans: def _filter_passes(title: str, summary: str, match_filter: str) -> bool:
return 0 """Regel C: doorlaten bij (titel-match >= 1) of (totaalscore >= 3).
clusters, prev_end = 1, spans[0][1]
for s, e in spans[1:]: Een OV-trefwoord in de titel is een sterke intentieverklaring (score 2
if s - prev_end <= 1: volstaat). Treffers alleen in de samenvatting moeten dubbel bevestigd
prev_end = max(prev_end, e) worden (>= 3): zo houdt 'bussen rijden om' in de lead van een
else: woningbrand-bericht het artikel niet meer binnen."""
clusters += 1 if RSSBot._matches_in(title, match_filter) >= 1:
prev_end = e return True
return clusters return RSSBot._filter_score(title, summary, match_filter) >= 3
return 2 * count(title) + count(summary)
async def _send( async def _send(
self, feed: Feed, entry: Entry, sub: Subscription, *, check_filter: bool = True self, feed: Feed, entry: Entry, sub: Subscription, *, check_filter: bool = True
@ -183,7 +198,7 @@ class RSSBot(Plugin):
match_filter = self.config["match_filter"] match_filter = self.config["match_filter"]
if match_filter: if match_filter:
summary = html.unescape(re.sub(r"<[^>]+>", "", entry.summary)) summary = html.unescape(re.sub(r"<[^>]+>", "", entry.summary))
if self._filter_score(entry.title, summary, match_filter) < 2: if not self._filter_passes(entry.title, summary, match_filter):
return None return None
message = sub.notification_template.safe_substitute( message = sub.notification_template.safe_substitute(
@ -217,7 +232,7 @@ class RSSBot(Plugin):
match_filter = self.config["match_filter"] match_filter = self.config["match_filter"]
if match_filter: if match_filter:
summary = html.unescape(re.sub(r"<[^>]+>", "", entry.summary)) summary = html.unescape(re.sub(r"<[^>]+>", "", entry.summary))
if self._filter_score(entry.title, summary, match_filter) < 2: if not self._filter_passes(entry.title, summary, match_filter):
return 0, 1, 0 return 0, 1, 0
spam_sleep = self.config["spam_sleep"] spam_sleep = self.config["spam_sleep"]
if spam_sleep >= 0: if spam_sleep >= 0:

View file

@ -1,5 +1,5 @@
id: nl.eddydevink.rss id: nl.eddydevink.rss
version: 1.5.2 version: 1.5.3
modules: modules:
- buzzrss - buzzrss
main_class: RSSBot main_class: RSSBot