Compare commits

..
Author SHA1 Message Date
Eddy de Vink
f5dec54165 v1.10.0: whitelist alleen titel-anker; medium+zwak uitsluitend in samenvatting geweerd 2026-09-12 09:11:45 +02:00
Eddy de Vink
ff7e7ac81c v1.9.9: één OV-familie telt 1x per artikel — herhaling en beide velden wegen niet dubbel
Gebruikersbesluit 2026-09-11: 'busstation' 60x in een artikel is één signaal, niet
60x de score. _filter_passes combineert titel- en samenvattingshits nu per familie
(dict over beide teksten). Gevolg (regressie-pinned): 'Pand naast busstation'
(titel+samenvatting) en 'Busstation wordt verbouwd' (zonder andere OV-term) zijn
nu geweerd; combinaties over 2 families (metro+halte, metrostation+metro) blijven
door.
2026-09-11 09:28:09 +02:00
Eddy de Vink
341036cd16 v1.9.8: titel weegt niet meer 2x — score = titel + samenvatting >= 3
Gebruikersbesluit 2026-09-11: een medium-hit in de TITEL haalt het filter te
makkelijk ('restaurant naast busstation' = 2x2=4 was door). Formule gelijkgemaakt:
score = titelscore + samenvattingscore >= 3. Gevolg (regressie-pinned):
- enkel-woord-medium in titel is nu onvoldoende (fietsenstalling/restaurant-case
  geweerd); zware/sterke woorden en combinaties over 2 families blijven door.
- zwak-alleen-regel (titel-anker + >=2 families) ongewijzigd.
2026-09-11 09:17:08 +02:00
Eddy de Vink
dbf64691e7 docs: dubbele run-log-regel na deploy is bekend upstream-gedrag — niet onderzoeken 2026-09-11 09:13:10 +02:00
Eddy de Vink
086d808dc7 v1.9.7: station-samenstellingen medium i.p.v. sterk/whitelist; geen dubbeltelling bus-/metrostation
Gebruikersbesluit 2026-09-11 n.a.v. Omroep West-artikel 'Restaurant weg, pand
staat te verloederen': één 'busstation'-hit in de samenvatting ('pand naast
busstation') is geen OV-nieuws en mocht niet doorgelaten worden.

- Familie station-samenstellingen (trein/bus/metro-station): gewicht 3 -> 2
  (medium). In de samenvatting moet nu een tweede OV-woord bijkomen; in de
  titel volstaat het anker nog (2x2=4).
- 'busstation'/'metrostation' van de whitelist af (whitelist = altijd door).
- 'station' uit de voertuig-samenstelling-familie verwijderd: 'busstation'
  telde dubbel mee (via 'bus' én 'station', 2x3=6).
- validate_filter.py: 6 nieuwe regressie-cases + invariant 2-aanpassing.
2026-09-11 09:07:43 +02:00
5 changed files with 118 additions and 36 deletions

View file

@ -17,3 +17,10 @@ Maubot-plugin `nl.eddydevink.rss` (RSS → Matrix + WhatsApp), met een OV-nieuws
daarna container-restart. Exacte procedure: `skill://maubot`. daarna container-restart. Exacte procedure: `skill://maubot`.
- **NOOIT** de wwebjs-container of WhatsApp-sessies (her)starten/beëindigen — meerdere bots delen die sessies. - **NOOIT** de wwebjs-container of WhatsApp-sessies (her)starten/beëindigen — meerdere bots delen die sessies.
- Productie draait het **geïnstalleerde mbp**, niet deze werkdirectory: een commit is pas actief na build+upload+restart. - Productie draait het **geïnstalleerde mbp**, niet deze werkdirectory: een commit is pas actief na build+upload+restart.
## Bekend — NIET onderzoeken
- Na een deploy (upload + container-restart) staan er 2 verse `run complete`-regels in de run-log
binnen enkele seconden: maubot herstart de instance bij een nieuwe plugin-versie (upstream) én de
container-restart start opnieuw, elke start doet direct één poll. Dat is géén handler-lek en hoort
niet onderzocht te worden (besluit 2026-09-11). Alleen verdubbeling op élke poll-run is een
probleem. Zie `skill://maubot` → Veelgemaakte fouten.

View file

@ -28,7 +28,7 @@ exclude_paywalled: true
# 0 = uit. # 0 = uit.
title_dedup_days: 30 title_dedup_days: 30
# OV-matchfilter: VAST in code (gewogen families + blacklist), niet via config te wijzigen. # OV-matchfilter: VAST in code (gewogen families + blacklist), niet via config te wijzigen.
# Doorlaten als 2×(titelscore) + 1×(samenvattingscore) >= 3. Zie de families en de # Doorlaten als som van unieke OV-families >= 3; één familie telt 1× per artikel (titel + samenvatting; herhaling of beide velden = 1 signaal). Zie de families en de
# blacklist in buzzrss/bot.py (MATCH_FILTER_FAMILIES / MATCH_FILTER_BLACKLIST). # blacklist in buzzrss/bot.py (MATCH_FILTER_FAMILIES / MATCH_FILTER_BLACKLIST).
# `!rss set-filter` en `!rss filter` tonen alleen de huidige (vaste) filter. # `!rss set-filter` en `!rss filter` tonen alleen de huidige (vaste) filter.
# Users who can bypass room permission checks # Users who can bypass room permission checks

View file

@ -50,11 +50,13 @@ rss_change_level = EventType.find("xyz.maubot.rss", t_class=EventType.Class.STAT
# Gewogen OV-trefwoord-families voor het match-filter. Elke tuple = (regex, gewicht). # Gewogen OV-trefwoord-families voor het match-filter. Elke tuple = (regex, gewicht).
# Per familie wordt 1× geteld per tekst (niet per cluster/herhaling). Drempel (3): # Per familie wordt 1× geteld per ARTIKEL (titel én samenvatting samen; herhaling
# doorlaten als 2×(titelscore) + 1×(samenvattingscore) >= MATCH_FILTER_THRESHOLD # of het woord in beide velden = één signaal — 'busstation' 60× weegt 1×, besluit
# 2026-09-11). Drempel (3): doorlaten als de som van de unieke familie-gewichten
# >= MATCH_FILTER_THRESHOLD (titel weegt niet extra).
# Gewicht 3 (sterk, onmiskenbaar OV): één woord in titel óf samenvatting volstaat. # Gewicht 3 (sterk, onmiskenbaar OV): één woord in titel óf samenvatting volstaat.
# Gewicht 2 (medium): één woord in de titel volstaat; in de samenvatting moet er # Gewicht 2 (medium): moet combineren met een andere OV-familie; één medium-hit,
# een tweede OV-woord bijkomen. # in titel óf samenvatting, haalt de drempel niet.
# Gewicht 1 (zwak/dubbelzinnig: los "bus", "breng", "overweg", "overwegen", "sein", # Gewicht 1 (zwak/dubbelzinnig: los "bus", "breng", "overweg", "overwegen", "sein",
# "kaartje", "opwaarderen", "rails", "pendelbus"): moet met een ander OV-woord # "kaartje", "opwaarderen", "rails", "pendelbus"): moet met een ander OV-woord
# combineren (een tweede, andere familie — dezelfde zwakke familie in titel én # combineren (een tweede, andere familie — dezelfde zwakke familie in titel én
@ -87,16 +89,34 @@ rss_change_level = EventType.find("xyz.maubot.rss", t_class=EventType.Class.STAT
# een niet-OV-bericht te veel doorgelaten (gebruikersbesluit, 2026-09-10). # een niet-OV-bericht te veel doorgelaten (gebruikersbesluit, 2026-09-10).
# #
# Drie lagen, elk met een eigen regel: # Drie lagen, elk met een eigen regel:
# 1. WHITELIST -> ALTIJD door. Alleen termen die in élke context OV # 1. WHITELIST -> ALTIJD door BIJ EEN HIT IN DE TITEL. Alleen termen die
# betekenen (merken: NS/ProRail/Arriva; onmiskenbaar jargon: spoorstoring, # in élke context OV betekenen (merken: NS/ProRail/Arriva; onmiskenbaar jargon:
# perron, bushalte, metrostation, conducteur, ovpay; multi-woord termen). # spoorstoring, perron, bushalte, conducteur, ovpay; multi-woord termen).
# REGEL: twijfel je of de term ook buiten het OV kan voorkomen -> NIET hier. # REGEL: twijfel je of de term ook buiten het OV kan voorkomen -> NIET hier.
# 2. GEWOGEN FAMILIES -> score = 2x titel + 1x samenvatting >= drempel (3). # NIET: station-samenstellingen (busstation/metrostation) — een bericht over
# een pand 'naast het busstation' is geen OV-nieuws (besluit 2026-09-11);
# die wegen mee via de gewogen families (medium).
# Een whitelist-term ALLEEN in de SAMENVATTING laat NIET onvoorwaardelijk
# door: een bijzin als 'de zakelijke bezoeker is ook nodig voor het openbaar
# vervoer' in een economiebericht is geen OV-nieuws (besluit 2026-09-12).
# Zo'n hit scoort via de gewogen families (dezelfde termen staan daar ook in).
# 2. GEWOGEN FAMILIES -> score = som van unieke OV-families over het hele artikel
# (titel + samenvatting; per familie 1× per artikel — herhaling of beide velden
# tellen niet dubbel, besluit 2026-09-11) >= drempel (3).
# - sterk (3): onmiskenbaar OV-jargon/merken/samenvoegingen (spoorlijn, # - sterk (3): onmiskenbaar OV-jargon/merken/samenvoegingen (spoorlijn,
# treinstation, busverkeer, intercity, lijn 12, ...). # bushalte, busverkeer, intercity, lijn 12, ...).
# - medium (2): onmiskenbaar OV maar iets algemener (tram, metro, halte, # - medium (2): onmiskenbaar OV maar iets algemener (tram, metro, halte,
# pont, ovpay, openbaar vervoer, ...); mag niet volledig # pont, ovpay, openbaar vervoer, trein-/bus-/metrostation).
# ontbreken, maar telt alleen mee ter ondersteuning. # Moet COMBINEREN met een andere OV-familie: één medium-hit,
# in titel óf samenvatting, is onvoldoende — de titel weegt
# niet extra ('restaurant naast busstation' in de titel is
# geen OV-bericht, besluit 2026-09-11).
# EXTRA (besluit 2026-09-12): een medium + zwak-combinatie
# UITSLUITEND in de samenvatting (geen enkele hit in de
# titel) is geen OV-bewijs — 'openbaar vervoer' + 'zakelijke
# reizigers' in een economiebericht telt dan toch op tot de
# drempel. Sterke (3) families blijven in de samenvatting
# gewoon tellen.
# - zwak (1): DUBBELZINNIGHEID. Woorden die (ook) buiten het OV # - zwak (1): DUBBELZINNIGHEID. Woorden die (ook) buiten het OV
# voorkomen. REGEL: elk dubbelzinnig woord krijgt een EIGEN # voorkomen. REGEL: elk dubbelzinnig woord krijgt een EIGEN
# familie (niet samengevoegd), zodat twee verschillende # familie (niet samengevoegd), zodat twee verschillende
@ -129,9 +149,9 @@ MATCH_FILTER_FAMILIES: list[tuple[str, int]] = [
# sterk (3) # sterk (3)
(r"\b(?:ns|arriva|qbuzz|connexxion|cxx|transdev|keolis|ret|gvb|ebs|htm|syntus|u-ov|allgo|flixbus|blauwnet|r-net|valleilijn|waterbus|eurostar|thalys|randstadrail|gvu|prorail|hermes|rrreis|qliner|ov)\b", 3), # vervoerders ('Breng' staat in de zwakke groep: botst met het werkwoord "brengen") (r"\b(?:ns|arriva|qbuzz|connexxion|cxx|transdev|keolis|ret|gvb|ebs|htm|syntus|u-ov|allgo|flixbus|blauwnet|r-net|valleilijn|waterbus|eurostar|thalys|randstadrail|gvu|prorail|hermes|rrreis|qliner|ov)\b", 3), # vervoerders ('Breng' staat in de zwakke groep: botst met het werkwoord "brengen")
(r"\b(?:spoorweg|spoorba(?:an|nen)|spoorlijn|spoorverkeer|spoorwegovergang|spoorbrug|spoorstoring|perron|seinstoring|wisselstoring|spoorstaking)\w*\b", 3), # spoorinfra; let op de klinkerwisseling: 'ontsporing' (open lettergreep, één o) vs 'ontspoord'/'ontspoorde' (dicht, dubbel o) — vandaar beide stammen (r"\b(?:spoorweg|spoorba(?:an|nen)|spoorlijn|spoorverkeer|spoorwegovergang|spoorbrug|spoorstoring|perron|seinstoring|wisselstoring|spoorstaking)\w*\b", 3), # spoorinfra; let op de klinkerwisseling: 'ontsporing' (open lettergreep, één o) vs 'ontspoord'/'ontspoorde' (dicht, dubbel o) — vandaar beide stammen
(r"\b(?:treinstation|treinstations|metrostation|metrostations|busstation|busstations)\b", 3), # station-samenstellingen: onmiskenbaar OV-jargon (r"\b(?:treinstation|treinstations|metrostation|metrostations|busstation|busstations)\b", 2), # station-samenstellingen: onmiskenbaar OV, maar MEDIUM — één station-woord náást een ander onderwerp ('pand naast busstation', besluit 2026-09-11) is geen OV-bericht; in de samenvatting moet een tweede OV-woord bijkomen
(r"(?<![\w-])\bstations?\b", 1), # los 'station(s)' is ZWAK: ook tv-/radio-/regionale omroepstations ('de lokale stations'), moet combineren met een andere OV-familie (r"(?<![\w-])\bstations?\b", 1), # los 'station(s)' is ZWAK: ook tv-/radio-/regionale omroepstations ('de lokale stations'), moet combineren met een andere OV-familie
(r"\b(?:bus|tram|metro)-?(?:halte|haltes|lijn|lijnen|station|stations|verkeer|net|staking|ba(?:an|nen)|hok(?:je|jes)|chauffeur)\w*\b", 3), # voertuig-samenstelling (busstation/metrostation/bushokje/busbanen/…) (r"\b(?:bus|tram|metro)-?(?:halte|haltes|lijn|lijnen|verkeer|net|staking|ba(?:an|nen)|hok(?:je|jes)|chauffeur)\w*\b", 3), # voertuig-samenstelling (bushalte/buslijn/bushokje/busbanen/…); géén 'station': *station-samenstellingen zitten in de station-familie, anders telde 'busstation' dubbel mee (via 'bus' én 'station') — fix 2026-09-11
(r"\b(?:treinstoring|treinverkeer|treinstaking|treinongeval|treinbotsing|treinongeluk)\w*\b", 3), # trein-jargon-samenstellingen: onmiskenbaar OV (r"\b(?:treinstoring|treinverkeer|treinstaking|treinongeval|treinbotsing|treinongeluk)\w*\b", 3), # trein-jargon-samenstellingen: onmiskenbaar OV
(r"\b\w*trein\w*\b", 1), # losse 'trein' en algemene samenstellingen (stoptrein, goederentrein, treinverkeer al in bovenstaande familie) — ZWAK: 'de trein naar mijn nieuwe huis' is geen OV-jargon, moet combineren met een andere familie (r"\b\w*trein\w*\b", 1), # losse 'trein' en algemene samenstellingen (stoptrein, goederentrein, treinverkeer al in bovenstaande familie) — ZWAK: 'de trein naar mijn nieuwe huis' is geen OV-jargon, moet combineren met een andere familie
(r"\b(?:intercity|streekvervoer|stadsvervoer|veerdienst|veerboot|veerboten|veerpont|voetveer|veerverbinding|buurtbus|belbus)\w*\b", 3), # treintypes, vervoerssectoren en veren ('sprinter' staat in de zwakke groep: atleet-botsing) (r"\b(?:intercity|streekvervoer|stadsvervoer|veerdienst|veerboot|veerboten|veerpont|voetveer|veerverbinding|buurtbus|belbus)\w*\b", 3), # treintypes, vervoerssectoren en veren ('sprinter' staat in de zwakke groep: atleet-botsing)
@ -189,10 +209,15 @@ MATCH_FILTER_BLACKLIST: frozenset[str] = frozenset({
}) })
# MATCH_FILTER_WHITELIST: termen die een bericht per definitie OV maken. Eén enkele # MATCH_FILTER_WHITELIST: termen die een bericht per definitie OV maken. Eén enkele
# hit in titel óf samenvatting laat ALTIJD door (geen familiescore nodig) — dit is het # hit in de TITEL laat ALTIJD door (geen familiescore nodig) — dit is het expliciete
# expliciete "onmiskenbaar OV"-signaal, zodat we voor deze termen niet op de gewogen # "onmiskenbaar OV"-signaal, zodat we voor deze termen niet op de gewogen
# heuristiek hoeven te leunen. Vergelijking is case-insensitive, dus ook 'nederlandse # heuristiek hoeven te leunen. Vergelijking is case-insensitive, dus ook 'nederlandse
# spoorwegen', 'prorail' en 'NS' in all-caps titels tellen mee. # spoorwegen', 'prorail' en 'NS' in all-caps titels tellen mee.
# Een whitelist-hit ALLEEN in de samenvatting is géén onvoorwaardelijk signaal
# (besluit 2026-09-12: 'openbaar vervoer' als bijzin in een economiebericht is geen
# OV-nieuws); die term scoort dan via de gewogen families — voor whitelist-termen
# die geen eigen familie hebben (bv. 'treinstel': alleen zwak via \w*trein\w*) is
# een samenvatting-alleen hit dus zwak.
# Termen MOETEN in elke context OV betekenen. Niet hierop (algemeen/dubbelzinnig): # Termen MOETEN in elke context OV betekenen. Niet hierop (algemeen/dubbelzinnig):
# los 'trein', 'bus', 'metro', 'tram', 'station', 'lijn', 'ov', 'reiziger', # los 'trein', 'bus', 'metro', 'tram', 'station', 'lijn', 'ov', 'reiziger',
# 'vervoerder', 'sprinter', # 'vervoerder', 'sprinter',
@ -209,9 +234,9 @@ MATCH_FILTER_WHITELIST: frozenset[str] = frozenset({
"spoorbrug", "spoorstaking", "perron", "wisselstoring", "seinstoring", "treinstoring", "treinverkeer", "spoorbrug", "spoorstaking", "perron", "wisselstoring", "seinstoring", "treinstoring", "treinverkeer",
"treinstaking", "treinongeval", "treinbotsing", "treinstel", "treinstaking", "treinongeval", "treinbotsing", "treinstel",
# Bus/tram/metro-jargon # Bus/tram/metro-jargon
"bushalte", "bushaltes", "busstation", "busbaan", "buslijn", "busnet", "bushalte", "bushaltes", "busbaan", "buslijn", "busnet",
"busstaking", "tramhalte", "tramlijn", "tramnet", "tramverkeer", "busstaking", "tramhalte", "tramlijn", "tramnet", "tramverkeer",
"metrostation", "metrolijn", "metroverkeer", "metrolijn", "metroverkeer",
# Tarief/vervoerder-jargon # Tarief/vervoerder-jargon
"ov-chipkaart", "ov-staking", "lightrail", "ov-chipkaart", "ov-staking", "lightrail",
"conducteur", "conducteurs", "conducteur", "conducteurs",
@ -387,15 +412,22 @@ class RSSBot(Plugin):
@staticmethod @staticmethod
def _filter_passes(title: str, summary: str) -> bool: def _filter_passes(title: str, summary: str) -> bool:
"""Gewogen familiescore: doorlaten bij 2×(titelscore) + 1×(samenvattingscore) >= drempel. """Gewogen familiescore: elke OV-familie telt 1× per artikel (titel + samenvatting
samen; herhaling of beide velden = één signaal). Doorlaten bij som >= drempel.
(titel weegt NIET extra — één medium-hit, 'restaurant naast busstation',
is geen OV-bewijs; besluit 2026-09-11).
Sterke families (gewicht 3) halen de drempel al met één woord in titel óf Sterke families (gewicht 3) halen de drempel al met één woord in titel óf
samenvatting. Medium (2) in de titel ook; in de samenvatting moet er een samenvatting. Medium (2) moet combineren met een andere OV-familie.
tweede OV-woord bijkomen.
Zwakke woorden (losse 'bus', 'breng', 'overweg', 'overwegen', 'sein', Zwakke woorden (losse 'bus', 'breng', 'overweg', 'overwegen', 'sein',
'opwaarderen', 'kaartje', 'rails', 'pendelbus') moeten combineren met een 'opwaarderen', 'kaartje', 'rails', 'pendelbus') moeten combineren met een
ander OV-woord én minstens één zwak woord moet in de titel staan; dezelfde ander OV-woord én minstens één zwak woord moet in de titel staan; dezelfde
zwakke familie in titel én samenvatting is géén combinatie. Weert oa zwakke familie in titel én samenvatting is géén combinatie. Whitelist-termen
(NS, perron, bushalte, openbaar vervoer, ...) laten alleen onvoorwaardelijk
door BIJ EEN HIT IN DE TITEL; in de samenvatting wegen ze mee als gewogen
familie. Een medium + zwak-combinatie UITSLUITEND in de samenvatting (geen
enkele hit in de titel) is geen OV-bewijs ('openbaar vervoer' + 'zakelijke
reizigers' in een economiebericht, besluit 2026-09-12). Weert oa
'VW-bus' (bus matcht niet ná een koppelteken), 'schoolbus', 'bussen rijden 'VW-bus' (bus matcht niet ná een koppelteken), 'schoolbus', 'bussen rijden
om' in een woningbrand-lead, het werkwoord 'brengen', het idioom 'goed met om' in een woningbrand-lead, het werkwoord 'brengen', het idioom 'goed met
elkaar overweg kunnen', 'een wijk opwaarderen', brandweerjargon elkaar overweg kunnen', 'een wijk opwaarderen', brandweerjargon
@ -403,21 +435,33 @@ class RSSBot(Plugin):
waarin de samenvatting 'station' + 'pendelbus' + 'bus' noemt. waarin de samenvatting 'station' + 'pendelbus' + 'bus' noemt.
Bewust doorgelaten: krant 'De Metro'/café 'De Tram' (medium) en atleet- Bewust doorgelaten: krant 'De Metro'/café 'De Tram' (medium) en atleet-
'sprinter' in combinatie met ander OV-woord.""" 'sprinter' in combinatie met ander OV-woord."""
# Whitelist: één onmiskenbare OV-term (titel óf samenvatting) laat altijd door. # Whitelist: een onmiskenbare OV-term in de TITEL laat altijd door (het
if RSSBot._whitelist_hit(title) or RSSBot._whitelist_hit(summary): # artikel kondigt het onderwerp aan: 'NS verlengt noodregeling'). Een
# whitelist-term ALLEEN in de samenvatting is geen OV-bewijs ('openbaar
# vervoer' als bijzin in een economiebericht, besluit 2026-09-12) en
# scoort via de gewogen families hieronder.
if RSSBot._whitelist_hit(title):
return True return True
t_hits = RSSBot._fam_hits(title) t_hits = RSSBot._fam_hits(title)
s_hits = RSSBot._fam_hits(summary) s_hits = RSSBot._fam_hits(summary)
score = 2 * sum(w for _, w in t_hits) + sum(w for _, w in s_hits) # Eén familie telt 1× per ARTIKEL (titel én samenvatting samen): 'busstation'
# in beide velden, of 60× in de tekst, blijft één signaal (besluit 2026-09-11).
combined = dict(t_hits + s_hits)
score = sum(combined.values())
if score < MATCH_FILTER_THRESHOLD: if score < MATCH_FILTER_THRESHOLD:
return False return False
if max(w for _, w in t_hits + s_hits) == 1: if max(combined.values()) == 1:
# Alleen zwakke families: dan moeten ≥2 verschillende families meetellen # Alleen zwakke families: dan moeten ≥2 verschillende families meetellen
# én minstens één zwak woord moet in de TITEL staan. Zwakke woorden in # én minstens één zwak woord moet in de TITEL staan. Zwakke woorden in
# de samenvatting alleen zijn geen bewijs ('station' + 'pendelbus' + # de samenvatting alleen zijn geen bewijs ('station' + 'pendelbus' +
# 'bus' in een golfverslag telt anders op tot de drempel). # 'bus' in een golfverslag telt anders op tot de drempel).
if not t_hits or len({i for i, _ in t_hits + s_hits}) < 2: if not t_hits or len(combined) < 2:
return False return False
elif not t_hits and 1 in combined.values():
# Medium + zwak uitsluitend in de samenvatting, niets in de titel: geen
# OV-bewijs. 'openbaar vervoer' + 'zakelijke reizigers' in een
# economiebericht telt anders op tot de drempel (besluit 2026-09-12).
return False
return True return True
@staticmethod @staticmethod
@ -426,18 +470,23 @@ class RSSBot(Plugin):
return ( return (
"**OV-matchfilter (vast in code, gewogen families):**\n" "**OV-matchfilter (vast in code, gewogen families):**\n"
"- Sterk (3): vervoerders (NS, Arriva, ProRail, Qbuzz, ...), spoorinfra (spoorlijn, spoorwegovergang, perron, spoorstoring), " "- Sterk (3): vervoerders (NS, Arriva, ProRail, Qbuzz, ...), spoorinfra (spoorlijn, spoorwegovergang, perron, spoorstoring), "
"trein-/metro-/busstation, samenstellingen als bushalte/buslijn/bushokje/busbaan/busverkeer, treinstoring/treinverkeer/treinstaking, " "samenstellingen als bushalte/buslijn/bushokje/busbaan/busverkeer, treinstoring/treinverkeer/treinstaking, "
"intercity/streekvervoer/veerdienst/buurtbus/belbus, 'lijn 12'\n" "intercity/streekvervoer/veerdienst/buurtbus/belbus, 'lijn 12'\n"
"- Medium (2): tram, metro, openbaar vervoer, lightrail, halte, pont, ov-chipkaart/ovpay, ov-staking, conducteur\n" "- Medium (2): tram, metro, openbaar vervoer, lightrail, halte, pont, ov-chipkaart/ovpay, ov-staking, conducteur, "
"trein-/bus-/metrostation — één medium-hit volstaat niet, moet combineren met een andere OV-familie; "
"medium + zwak uitsluitend in de samenvatting (geen hit in de titel) telt niet\n"
"- Zwak (1): losse 'bus' (niet ná een koppelteken), 'pendelbus'/'shuttlebus', 'trein', 'station(s)', 'breng', 'overweg', 'overwegen', " "- Zwak (1): losse 'bus' (niet ná een koppelteken), 'pendelbus'/'shuttlebus', 'trein', 'station(s)', 'breng', 'overweg', 'overwegen', "
"'sein', 'kaartje', 'opwaarderen', 'rails', 'inchecken'/'uitchecken', 'dienstregeling', 'chipkaart', 'reisproduct', 'vervoersbewijs', " "'sein', 'kaartje', 'opwaarderen', 'rails', 'inchecken'/'uitchecken', 'dienstregeling', 'chipkaart', 'reisproduct', 'vervoersbewijs', "
"'ontsporen', 'reiziger', 'vervoerder', 'sprinter' — moet combineren met een ander OV-woord, met minstens één zwak woord in de titel\n\n" "'ontsporen', 'reiziger', 'vervoerder', 'sprinter' — moet combineren met een ander OV-woord, met minstens één zwak woord in de titel\n\n"
"**Whitelist (altijd door):** één onmiskenbare OV-term in titel óf samenvatting is genoeg " "**Whitelist (altijd door bij een hit in de TITEL):** een onmisbare OV-term in de titel is genoeg "
"(NS, ProRail, Nederlandse Spoorwegen, spoorstoring, perron, bushalte, metrostation, conducteur, " "(NS, ProRail, Nederlandse Spoorwegen, spoorstoring, perron, bushalte, conducteur, "
"ov-chipkaart, ovpay, openbaar vervoer, ...)\n\n" "ov-chipkaart, ovpay, openbaar vervoer, ...); in de samenvatting alleen weegt zo'n term mee "
"als gewogen familie (medium/sterk)\n\n"
"Uitgesloten (blacklist): woorden die wél matchen maar geen OV-waarde hebben, " "Uitgesloten (blacklist): woorden die wél matchen maar geen OV-waarde hebben, "
"o.a. straatnamen rond stations ('stationsstraat', 'stationsweg', ...) en niet-OV-vormen als 'stationcar' en 'stationair'\n\n" "o.a. straatnamen rond stations ('stationsstraat', 'stationsweg', ...) en niet-OV-vormen als 'stationcar' en 'stationair'\n\n"
"Doorlaten: `2×(titel-score) + 1×(samenvatting-score) ≥ 3`; bij uitsluitend zwakke woorden tellen alleen " "Doorlaten: som van unieke OV-families ≥ 3 — één familie telt 1× per artikel "
"(titel + samenvatting samen; herhaling of beide velden = één signaal); "
"bij uitsluitend zwakke woorden tellen alleen "
"≥2 verschillende families én een zwak woord in de titel" "≥2 verschillende families én een zwak woord in de titel"
) )

View file

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

View file

@ -90,7 +90,7 @@ for w in AMBIGUOUS:
# --- Invariant 2: onmiskenbare OV-termen mogen niet uitsluitend zwak zijn # --- Invariant 2: onmiskenbare OV-termen mogen niet uitsluitend zwak zijn
UNAMBIGUOUS = [ UNAMBIGUOUS = [
"spoorstoring", "spoorlijn", "perron", "bushalte", "bushokje", "spoorstoring", "spoorlijn", "perron", "bushalte", "bushokje",
"metrostation", "busstation", "conducteur", "ovpay", "ns", "prorail", "conducteur", "ovpay", "ns", "prorail",
"treinstoring", "halte", "openbaar vervoer", "intercity", "buurtbus", "treinstoring", "halte", "openbaar vervoer", "intercity", "buurtbus",
"veerdienst", "lijn 12", "ov-chipkaart", "veerdienst", "lijn 12", "ov-chipkaart",
] ]
@ -139,6 +139,32 @@ GOLDEN = [
("Bushalte aan de Markt wordt verplaatst", "De halte gaat 200 meter opschuiven.", True), ("Bushalte aan de Markt wordt verplaatst", "De halte gaat 200 meter opschuiven.", True),
("Conducteur bekogeld in stoptrein bij Den Haag", "De conducteur is nagekeken.", True), ("Conducteur bekogeld in stoptrein bij Den Haag", "De conducteur is nagekeken.", True),
("Nieuwe app voor ovpay", "De ovpay-app wordt dit najaar vernieuwd.", True), ("Nieuwe app voor ovpay", "De ovpay-app wordt dit najaar vernieuwd.", True),
# fix 2026-09-11: station-samenstellingen zijn medium (niet meer whitelist/
# sterk) — een enkel 'busstation' in de samenvatting naast een ander onderwerp
# ('pand naast busstation') moet GEWEERD worden, en 'busstation' telt maar 1×.
("Restaurant weg, pand staat te verloederen: 'Doorn in het oog'",
"De gemeente Midden-Delfland wil het leegstaande horecapand naast busstation Maasland "
"Viaduct kopen. Het gebouw staat al jaren leeg en maakt een vervallen indruk.", False),
("Pand naast busstation wordt gesloopt", "Het leegstaande pand naast het busstation gaat plat.", False), # één familie in titel ÉN samenvatting = één signaal: 2 < 3 (besluit 2026-09-11)
("Nieuwe fietsenstalling bij busstation", "", False), # besluit 2026-09-11: één medium-hit in de titel is onvoldoende (titel weegt 1×, was 2×)
("Restaurant naast busstation wil uitbreiden", "De eigenaar wil uitbreiden naar het naastgelegen pand.", False), # zelfde regel, letterlijk gebruikersvoorbeeld
("Busstation wordt verbouwd", "De verbouwing duurt tot het voorjaar.", False), # herhaling/één woord = 1 signaal: 2 < 3 ('busstation' 60× weegt ook 1×)
("Metro rijdt niet", "De halte is tijdelijk gesloten.", True), # medium-combinatie over twee families: metro(2)+halte(2)=4
# fix 2026-09-12: whitelist-term alleen in de SAMENVATTING is geen OV-bewijs
# (valt terug op gewogen families); medium+zwak uitsluitend in de samenvatting
# (geen hit in de titel) wordt geweerd. Echte cases 11-09-2026:
# AT5-economiebericht ('openbaar vervoer' + 'zakelijke reizigers' als bijzin)
# en Telegraaf-overstroming Japan ('openbaar vervoer ontregeld' in de tekst).
("Economische groei van de stad niet meer vanzelfsprekend: ondernemersclubs waarschuwen",
"Volgens Evers is de zakelijke bezoeker ook nodig voor het openbaar vervoer, de culturele sector en musea. "
"Het aantal zakelijke reizigers in de stad daalt.", False),
("Straat in Japan verandert in rivier door hevige regen",
"Het verkeer ligt plat en ook het openbaar vervoer is ontregeld in de getroffen regio.", False),
# recall-bewaakt: 'openbaar vervoer' in de TITEL blijft onvoorwaardelijk door
# (whitelist-titel-anker; Havenloods 11-09-2026, 'einde aan gratis ov').
("Er komt echt een einde aan gratis openbaar vervoer voor ouderen",
"De gemeente stopt vanaf januari met de gratis reizen voor 65-plussers.", True),
# busstation mag niet dubbel meetellen via 'bus' én 'station' (was 2×3=6→nu 1×2)
("Metrostation Centraal tijdelijk gesloten", "De metro's rijden niet.", True), ("Metrostation Centraal tijdelijk gesloten", "De metro's rijden niet.", True),
("NS verlengt noodregeling op traject Zwolle", "Reizigers moeten rekening houden met minder treinen.", True), ("NS verlengt noodregeling op traject Zwolle", "Reizigers moeten rekening houden met minder treinen.", True),
("Spoorstoring bij Utrecht opgeheven", "ProRail heeft de wisselstoring verholpen.", True), ("Spoorstoring bij Utrecht opgeheven", "ProRail heeft de wisselstoring verholpen.", True),