From 086d808dc77eda3775c7fcb5639b80fd973c6c27 Mon Sep 17 00:00:00 2001 From: Eddy de Vink Date: Fri, 11 Sep 2026 09:07:43 +0200 Subject: [PATCH 1/5] v1.9.7: station-samenstellingen medium i.p.v. sterk/whitelist; geen dubbeltelling bus-/metrostation MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 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. --- buzzrss/bot.py | 30 +++++++++++++++++++----------- maubot.yaml | 2 +- validate_filter.py | 12 +++++++++++- 3 files changed, 31 insertions(+), 13 deletions(-) diff --git a/buzzrss/bot.py b/buzzrss/bot.py index 051630e..2247971 100644 --- a/buzzrss/bot.py +++ b/buzzrss/bot.py @@ -89,14 +89,21 @@ rss_change_level = EventType.find("xyz.maubot.rss", t_class=EventType.Class.STAT # Drie lagen, elk met een eigen regel: # 1. WHITELIST -> ALTIJD door. Alleen termen die in élke context OV # betekenen (merken: NS/ProRail/Arriva; onmiskenbaar jargon: spoorstoring, -# perron, bushalte, metrostation, conducteur, ovpay; multi-woord termen). +# perron, bushalte, conducteur, ovpay; multi-woord termen). # REGEL: twijfel je of de term ook buiten het OV kan voorkomen -> NIET hier. +# 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). # 2. GEWOGEN FAMILIES -> score = 2x titel + 1x samenvatting >= drempel (3). # - 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, -# pont, ovpay, openbaar vervoer, ...); mag niet volledig -# ontbreken, maar telt alleen mee ter ondersteuning. +# pont, ovpay, openbaar vervoer, trein-/bus-/metrostation); +# mag niet volledig ontbreken, maar telt alleen mee ter +# ondersteuning. Station-samenstellingen zijn medium: één +# station-woord náást een ander onderwerp ('pand naast +# busstation') maakt een bericht nog geen OV-bericht +# (besluit 2026-09-11). # - zwak (1): DUBBELZINNIGHEID. Woorden die (ook) buiten het OV # voorkomen. REGEL: elk dubbelzinnig woord krijgt een EIGEN # familie (niet samengevoegd), zodat twee verschillende @@ -129,9 +136,9 @@ MATCH_FILTER_FAMILIES: list[tuple[str, int]] = [ # 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(?: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"(? Date: Fri, 11 Sep 2026 09:13:10 +0200 Subject: [PATCH 2/5] =?UTF-8?q?docs:=20dubbele=20run-log-regel=20na=20depl?= =?UTF-8?q?oy=20is=20bekend=20upstream-gedrag=20=E2=80=94=20niet=20onderzo?= =?UTF-8?q?eken?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- AGENTS.md | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/AGENTS.md b/AGENTS.md index ff6fd78..31bfb7a 100644 --- a/AGENTS.md +++ b/AGENTS.md @@ -16,4 +16,11 @@ Maubot-plugin `nl.eddydevink.rss` (RSS → Matrix + WhatsApp), met een OV-nieuws (incl. `base-config.yaml`, anders `BaseMissingError`), upload met `allow_override=true`, daarna container-restart. Exacte procedure: `skill://maubot`. - **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. \ No newline at end of file +- 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. \ No newline at end of file From 341036cd1634d4f543ba85d6ce8b2709223b0eb4 Mon Sep 17 00:00:00 2001 From: Eddy de Vink Date: Fri, 11 Sep 2026 09:17:08 +0200 Subject: [PATCH 3/5] =?UTF-8?q?v1.9.8:=20titel=20weegt=20niet=20meer=202x?= =?UTF-8?q?=20=E2=80=94=20score=20=3D=20titel=20+=20samenvatting=20>=3D=20?= =?UTF-8?q?3?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 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. --- base-config.yaml | 2 +- buzzrss/bot.py | 35 +++++++++++++++++++---------------- maubot.yaml | 2 +- validate_filter.py | 8 +++++--- 4 files changed, 26 insertions(+), 21 deletions(-) diff --git a/base-config.yaml b/base-config.yaml index fc7fa59..c45ac42 100644 --- a/base-config.yaml +++ b/base-config.yaml @@ -28,7 +28,7 @@ exclude_paywalled: true # 0 = uit. title_dedup_days: 30 # 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 titelscore + samenvattingscore >= 3 (titel weegt niet extra). Zie de families en de # blacklist in buzzrss/bot.py (MATCH_FILTER_FAMILIES / MATCH_FILTER_BLACKLIST). # `!rss set-filter` en `!rss filter` tonen alleen de huidige (vaste) filter. # Users who can bypass room permission checks diff --git a/buzzrss/bot.py b/buzzrss/bot.py index 2247971..651b6eb 100644 --- a/buzzrss/bot.py +++ b/buzzrss/bot.py @@ -51,10 +51,12 @@ 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). # Per familie wordt 1× geteld per tekst (niet per cluster/herhaling). Drempel (3): -# doorlaten als 2×(titelscore) + 1×(samenvattingscore) >= MATCH_FILTER_THRESHOLD +# doorlaten als titelscore + samenvattingscore >= MATCH_FILTER_THRESHOLD +# (titel weegt NIET extra — besluit 2026-09-11: 'restaurant naast busstation' +# als enige medium-hit in de titel is geen OV-bericht). # 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 -# een tweede OV-woord bijkomen. +# Gewicht 2 (medium): moet combineren met een andere OV-familie; één medium-hit, +# in titel óf samenvatting, haalt de drempel niet. # Gewicht 1 (zwak/dubbelzinnig: los "bus", "breng", "overweg", "overwegen", "sein", # "kaartje", "opwaarderen", "rails", "pendelbus"): moet met een ander OV-woord # combineren (een tweede, andere familie — dezelfde zwakke familie in titel én @@ -94,16 +96,15 @@ rss_change_level = EventType.find("xyz.maubot.rss", t_class=EventType.Class.STAT # 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). -# 2. GEWOGEN FAMILIES -> score = 2x titel + 1x samenvatting >= drempel (3). +# 2. GEWOGEN FAMILIES -> score = titel + samenvatting >= drempel (3). # - sterk (3): onmiskenbaar OV-jargon/merken/samenvoegingen (spoorlijn, # bushalte, busverkeer, intercity, lijn 12, ...). # - medium (2): onmiskenbaar OV maar iets algemener (tram, metro, halte, -# pont, ovpay, openbaar vervoer, trein-/bus-/metrostation); -# mag niet volledig ontbreken, maar telt alleen mee ter -# ondersteuning. Station-samenstellingen zijn medium: één -# station-woord náást een ander onderwerp ('pand naast -# busstation') maakt een bericht nog geen OV-bericht -# (besluit 2026-09-11). +# pont, ovpay, openbaar vervoer, trein-/bus-/metrostation). +# 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). # - zwak (1): DUBBELZINNIGHEID. Woorden die (ook) buiten het OV # voorkomen. REGEL: elk dubbelzinnig woord krijgt een EIGEN # familie (niet samengevoegd), zodat twee verschillende @@ -394,11 +395,12 @@ class RSSBot(Plugin): @staticmethod def _filter_passes(title: str, summary: str) -> bool: - """Gewogen familiescore: doorlaten bij 2×(titelscore) + 1×(samenvattingscore) >= drempel. + """Gewogen familiescore: doorlaten bij titelscore + samenvattingscore >= drempel + (titel weegt NIET extra — één medium-hit in de titel, '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 - samenvatting. Medium (2) in de titel ook; in de samenvatting moet er een - tweede OV-woord bijkomen. + samenvatting. Medium (2) moet combineren met een andere OV-familie. Zwakke woorden (losse 'bus', 'breng', 'overweg', 'overwegen', 'sein', 'opwaarderen', 'kaartje', 'rails', 'pendelbus') moeten combineren met een ander OV-woord én minstens één zwak woord moet in de titel staan; dezelfde @@ -415,7 +417,7 @@ class RSSBot(Plugin): return True t_hits = RSSBot._fam_hits(title) s_hits = RSSBot._fam_hits(summary) - score = 2 * sum(w for _, w in t_hits) + sum(w for _, w in s_hits) + score = sum(w for _, w in t_hits) + sum(w for _, w in s_hits) if score < MATCH_FILTER_THRESHOLD: return False if max(w for _, w in t_hits + s_hits) == 1: @@ -436,7 +438,7 @@ class RSSBot(Plugin): "samenstellingen als bushalte/buslijn/bushokje/busbaan/busverkeer, treinstoring/treinverkeer/treinstaking, " "intercity/streekvervoer/veerdienst/buurtbus/belbus, 'lijn 12'\n" "- Medium (2): tram, metro, openbaar vervoer, lightrail, halte, pont, ov-chipkaart/ovpay, ov-staking, conducteur, " - "trein-/bus-/metrostation (die vereisen in de samenvatting een tweede OV-woord: een 'pand naast busstation' is geen OV-bericht)\n" + "trein-/bus-/metrostation — één medium-hit volstaat niet, moet combineren met een andere OV-familie\n" "- 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', " "'ontsporen', 'reiziger', 'vervoerder', 'sprinter' — moet combineren met een ander OV-woord, met minstens één zwak woord in de titel\n\n" @@ -445,7 +447,8 @@ class RSSBot(Plugin): "ov-chipkaart, ovpay, openbaar vervoer, ...)\n\n" "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" - "Doorlaten: `2×(titel-score) + 1×(samenvatting-score) ≥ 3`; bij uitsluitend zwakke woorden tellen alleen " + "Doorlaten: `titel-score + samenvatting-score ≥ 3` (titel weegt niet extra); " + "bij uitsluitend zwakke woorden tellen alleen " "≥2 verschillende families én een zwak woord in de titel" ) diff --git a/maubot.yaml b/maubot.yaml index 175d0f6..eecf6e1 100644 --- a/maubot.yaml +++ b/maubot.yaml @@ -1,5 +1,5 @@ id: nl.eddydevink.rss -version: 1.9.7 +version: 1.9.8 modules: - buzzrss main_class: RSSBot diff --git a/validate_filter.py b/validate_filter.py index 1196f9e..d4aef85 100755 --- a/validate_filter.py +++ b/validate_filter.py @@ -145,9 +145,11 @@ GOLDEN = [ ("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.", True), # titel-anker: 2×2=4 (medium-in-titel volstaat, zelfde regel als 'Metrostation Centraal gesloten') - ("Nieuwe fietsenstalling bij busstation", "", True), # titel-anker: 2×2=4 - ("Busstation wordt verbouwd", "Het busstation krijgt andere perrons.", True), # titel-anker: 2×2=4 + ("Pand naast busstation wordt gesloopt", "Het leegstaande pand naast het busstation gaat plat.", True), # busstation in titel ÉN samenvatting: 2+2=4 — twee velden tellen mee + ("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", "Het busstation krijgt andere perrons.", True), # busstation in titel én samenvatting: 4 + ("Metro rijdt niet", "De halte is tijdelijk gesloten.", True), # medium-combinatie over twee families: metro(2)+halte(2)=4 # 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), ("NS verlengt noodregeling op traject Zwolle", "Reizigers moeten rekening houden met minder treinen.", True), From ff7e7ac81c10f3c89f7a052129eb6540a9248d15 Mon Sep 17 00:00:00 2001 From: Eddy de Vink Date: Fri, 11 Sep 2026 09:28:09 +0200 Subject: [PATCH 4/5] =?UTF-8?q?v1.9.9:=20=C3=A9=C3=A9n=20OV-familie=20telt?= =?UTF-8?q?=201x=20per=20artikel=20=E2=80=94=20herhaling=20en=20beide=20ve?= =?UTF-8?q?lden=20wegen=20niet=20dubbel?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 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. --- base-config.yaml | 2 +- buzzrss/bot.py | 31 +++++++++++++++++++------------ maubot.yaml | 2 +- validate_filter.py | 4 ++-- 4 files changed, 23 insertions(+), 16 deletions(-) diff --git a/base-config.yaml b/base-config.yaml index c45ac42..af5b956 100644 --- a/base-config.yaml +++ b/base-config.yaml @@ -28,7 +28,7 @@ exclude_paywalled: true # 0 = uit. title_dedup_days: 30 # OV-matchfilter: VAST in code (gewogen families + blacklist), niet via config te wijzigen. -# Doorlaten als titelscore + samenvattingscore >= 3 (titel weegt niet extra). 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). # `!rss set-filter` en `!rss filter` tonen alleen de huidige (vaste) filter. # Users who can bypass room permission checks diff --git a/buzzrss/bot.py b/buzzrss/bot.py index 651b6eb..8dae00b 100644 --- a/buzzrss/bot.py +++ b/buzzrss/bot.py @@ -50,10 +50,10 @@ 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). -# Per familie wordt 1× geteld per tekst (niet per cluster/herhaling). Drempel (3): -# doorlaten als titelscore + samenvattingscore >= MATCH_FILTER_THRESHOLD -# (titel weegt NIET extra — besluit 2026-09-11: 'restaurant naast busstation' -# als enige medium-hit in de titel is geen OV-bericht). +# Per familie wordt 1× geteld per ARTIKEL (titel én samenvatting samen; herhaling +# 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 2 (medium): moet combineren met een andere OV-familie; één medium-hit, # in titel óf samenvatting, haalt de drempel niet. @@ -96,7 +96,9 @@ rss_change_level = EventType.find("xyz.maubot.rss", t_class=EventType.Class.STAT # 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). -# 2. GEWOGEN FAMILIES -> score = titel + samenvatting >= drempel (3). +# 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, # bushalte, busverkeer, intercity, lijn 12, ...). # - medium (2): onmiskenbaar OV maar iets algemener (tram, metro, halte, @@ -395,9 +397,10 @@ class RSSBot(Plugin): @staticmethod def _filter_passes(title: str, summary: str) -> bool: - """Gewogen familiescore: doorlaten bij titelscore + samenvattingscore >= drempel - (titel weegt NIET extra — één medium-hit in de titel, 'restaurant naast - busstation', is geen OV-bewijs; besluit 2026-09-11). + """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 samenvatting. Medium (2) moet combineren met een andere OV-familie. @@ -417,15 +420,18 @@ class RSSBot(Plugin): return True t_hits = RSSBot._fam_hits(title) s_hits = RSSBot._fam_hits(summary) - score = 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: 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 # én minstens één zwak woord moet in de TITEL staan. Zwakke woorden in # de samenvatting alleen zijn geen bewijs ('station' + 'pendelbus' + # '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 True @@ -447,7 +453,8 @@ class RSSBot(Plugin): "ov-chipkaart, ovpay, openbaar vervoer, ...)\n\n" "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" - "Doorlaten: `titel-score + samenvatting-score ≥ 3` (titel weegt niet extra); " + "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" ) diff --git a/maubot.yaml b/maubot.yaml index eecf6e1..c8802e0 100644 --- a/maubot.yaml +++ b/maubot.yaml @@ -1,5 +1,5 @@ id: nl.eddydevink.rss -version: 1.9.8 +version: 1.9.9 modules: - buzzrss main_class: RSSBot diff --git a/validate_filter.py b/validate_filter.py index d4aef85..450e62d 100755 --- a/validate_filter.py +++ b/validate_filter.py @@ -145,10 +145,10 @@ GOLDEN = [ ("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.", True), # busstation in titel ÉN samenvatting: 2+2=4 — twee velden tellen mee + ("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", "Het busstation krijgt andere perrons.", True), # busstation in titel én samenvatting: 4 + ("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 # 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), From f5dec5416556412bfc13b6fcb123a84063e59779 Mon Sep 17 00:00:00 2001 From: Eddy de Vink Date: Sat, 12 Sep 2026 09:11:45 +0200 Subject: [PATCH 5/5] v1.10.0: whitelist alleen titel-anker; medium+zwak uitsluitend in samenvatting geweerd --- buzzrss/bot.py | 53 ++++++++++++++++++++++++++++++++++++---------- maubot.yaml | 2 +- validate_filter.py | 14 ++++++++++++ 3 files changed, 57 insertions(+), 12 deletions(-) diff --git a/buzzrss/bot.py b/buzzrss/bot.py index 8dae00b..d431a2a 100644 --- a/buzzrss/bot.py +++ b/buzzrss/bot.py @@ -89,13 +89,17 @@ rss_change_level = EventType.find("xyz.maubot.rss", t_class=EventType.Class.STAT # een niet-OV-bericht te veel doorgelaten (gebruikersbesluit, 2026-09-10). # # Drie lagen, elk met een eigen regel: -# 1. WHITELIST -> ALTIJD door. Alleen termen die in élke context OV -# betekenen (merken: NS/ProRail/Arriva; onmiskenbaar jargon: spoorstoring, -# perron, bushalte, conducteur, ovpay; multi-woord termen). +# 1. WHITELIST -> ALTIJD door BIJ EEN HIT IN DE TITEL. Alleen termen die +# in élke context OV betekenen (merken: NS/ProRail/Arriva; onmiskenbaar jargon: +# spoorstoring, perron, bushalte, conducteur, ovpay; multi-woord termen). # REGEL: twijfel je of de term ook buiten het OV kan voorkomen -> NIET hier. # 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). @@ -107,6 +111,12 @@ rss_change_level = EventType.find("xyz.maubot.rss", t_class=EventType.Class.STAT # 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 # voorkomen. REGEL: elk dubbelzinnig woord krijgt een EIGEN # familie (niet samengevoegd), zodat twee verschillende @@ -199,10 +209,15 @@ MATCH_FILTER_BLACKLIST: frozenset[str] = frozenset({ }) # 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 -# expliciete "onmiskenbaar OV"-signaal, zodat we voor deze termen niet op de gewogen +# hit in de TITEL laat ALTIJD door (geen familiescore nodig) — dit is het expliciete +# "onmiskenbaar OV"-signaal, zodat we voor deze termen niet op de gewogen # heuristiek hoeven te leunen. Vergelijking is case-insensitive, dus ook 'nederlandse # 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): # los 'trein', 'bus', 'metro', 'tram', 'station', 'lijn', 'ov', 'reiziger', # 'vervoerder', 'sprinter', @@ -407,7 +422,12 @@ class RSSBot(Plugin): Zwakke woorden (losse 'bus', 'breng', 'overweg', 'overwegen', 'sein', 'opwaarderen', 'kaartje', 'rails', 'pendelbus') moeten combineren met een 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 om' in een woningbrand-lead, het werkwoord 'brengen', het idioom 'goed met elkaar overweg kunnen', 'een wijk opwaarderen', brandweerjargon @@ -415,8 +435,12 @@ class RSSBot(Plugin): waarin de samenvatting 'station' + 'pendelbus' + 'bus' noemt. Bewust doorgelaten: krant 'De Metro'/café 'De Tram' (medium) en atleet- 'sprinter' in combinatie met ander OV-woord.""" - # Whitelist: één onmiskenbare OV-term (titel óf samenvatting) laat altijd door. - if RSSBot._whitelist_hit(title) or RSSBot._whitelist_hit(summary): + # Whitelist: een onmiskenbare OV-term in de TITEL laat altijd door (het + # 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 t_hits = RSSBot._fam_hits(title) s_hits = RSSBot._fam_hits(summary) @@ -433,6 +457,11 @@ class RSSBot(Plugin): # 'bus' in een golfverslag telt anders op tot de drempel). if not t_hits or len(combined) < 2: 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 @staticmethod @@ -444,13 +473,15 @@ class RSSBot(Plugin): "samenstellingen als bushalte/buslijn/bushokje/busbaan/busverkeer, treinstoring/treinverkeer/treinstaking, " "intercity/streekvervoer/veerdienst/buurtbus/belbus, 'lijn 12'\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\n" + "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', " "'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" - "**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, 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, " "o.a. straatnamen rond stations ('stationsstraat', 'stationsweg', ...) en niet-OV-vormen als 'stationcar' en 'stationair'\n\n" "Doorlaten: som van unieke OV-families ≥ 3 — één familie telt 1× per artikel " diff --git a/maubot.yaml b/maubot.yaml index c8802e0..e6a42e7 100644 --- a/maubot.yaml +++ b/maubot.yaml @@ -1,5 +1,5 @@ id: nl.eddydevink.rss -version: 1.9.9 +version: 1.10.0 modules: - buzzrss main_class: RSSBot diff --git a/validate_filter.py b/validate_filter.py index 450e62d..85a7cfb 100755 --- a/validate_filter.py +++ b/validate_filter.py @@ -150,6 +150,20 @@ GOLDEN = [ ("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), ("NS verlengt noodregeling op traject Zwolle", "Reizigers moeten rekening houden met minder treinen.", True),