v1.3.0: paywall-filter (Telegraaf/NHD premium) + staking-fix

- exclude_paywalled config (default true): verwijdert feedparser-entries met
  <premium>true</premium> vóór opslag (Telegraaf en NHD markeren dit; AD/
  Parool/Trouw/VK/NRC niet)
- v8-migratie: entry.paywalled BOOLEAN
- staking-fix (v1.2.3): losse 'staking/stakingen' uit filter; alleen ov-
  specifiek: ov-staking, bus/tram/metro/spoor/conducteurs-staking,
  'openbaar vervoer'
- config-proof: match_filter altijd enkelregelig YAML
This commit is contained in:
eddy 2026-09-05 20:18:13 +02:00
commit 5d75f490fc
5 changed files with 27 additions and 11 deletions

View file

@ -51,12 +51,12 @@ rss_change_level = EventType.find("xyz.maubot.rss", t_class=EventType.Class.STAT
DEFAULT_MATCH_FILTER = (
r"(?i)"
r"\b(?:ns|arriva|qbuzz|connexxion|cxx|transdev|keolis|ret|gvb|ebs|htm|syntus|breng|u-ov|allgo|flixbus|blauwnet|r-net|valleilijn|waterbus|eurostar|thalys|randstadrail|gvu|ov)\b"
r"|\b(?:bus|tram|metro)(?:halte|haltes|lijn|lijnen|station|stations|verkeer|net)?\b"
r"|\b(?:bus|tram|metro)(?:halte|haltes|lijn|lijnen|station|stations|verkeer|net|staking)?\b"
r"|\btrein(?:en|verkeer|stel(?:len)?|staking|ongeluk|reizigers?|station|stations)?\b"
r"|\b(?:spoorweg|spoorbaan|spoorlijn|spoorverkeer|spoorwegovergang|spoorstoring|perron|perrons|overweg|overwegen|seinstoring|wisselstoring|sein|ontsporing|ontspoord)\b"
r"|\b(?:dienstregeling|reizigers?|inchecken|uitchecken|pont|ponten|lightrail|pendelbus|shuttlebus)\b"
r"|\b(?:spoorweg|spoorbaan|spoorlijn|spoorverkeer|spoorwegovergang|spoorstoring|perron|perrons|overweg|overwegen|seinstoring|wisselstoring|sein|ontsporing|ontspoord|spoorstaking)\b"
r"|\b(?:openbaar vervoer|dienstregeling|reizigers?|inchecken|uitchecken|pont|ponten|lightrail|pendelbus|shuttlebus)\b"
r"|\b(?:ov-chipkaart|chipkaart|ovpay|ov-pay|reisproduct|studentenreisproduct|vervoersbewijs|vervoerbewijs|opwaarderen)\b"
r"|\b(?:ov-staking|staking|stakingen)\b"
r"|\b(?:ov-staking|conducteursstaking)\b"
)
@ -69,6 +69,7 @@ class Config(BaseProxyConfig):
helper.copy("command_prefix")
helper.copy("notification_template")
helper.copy("allow_filter")
helper.copy("exclude_paywalled")
helper.copy("match_filter")
helper.copy("admins")
@ -90,6 +91,7 @@ class BoolArgument(command.Argument):
class RSSBot(Plugin):
dbm: DBManager
exclude_paywalled: bool = False
poll_task: asyncio.Future
poll_sema: asyncio.Semaphore | None
http: aiohttp.ClientSession
@ -117,6 +119,8 @@ class RSSBot(Plugin):
self.poll_sema = (
asyncio.Semaphore(poll_parallelism_limit) if poll_parallelism_limit > 0 else None
)
# single-instance fork: class-level since _parse_rss is a classmethod
type(self).exclude_paywalled = bool(self.config["exclude_paywalled"])
async def stop(self) -> None:
await super().stop()
@ -311,7 +315,10 @@ class RSSBot(Plugin):
feed.title = feed_data.get("title", feed.url)
feed.subtitle = feed_data.get("description", "")
feed.link = feed_data.get("link", "")
return feed, [cls._parse_rss_entry(feed.id, entry) for entry in parsed_data.entries]
entries = [cls._parse_rss_entry(feed.id, entry) for entry in parsed_data.entries]
if cls.exclude_paywalled:
entries = [e for e in entries if not e.paywalled]
return feed, entries
@classmethod
def _parse_rss_entry(cls, feed_id: int, entry: Any) -> Entry:
@ -333,6 +340,7 @@ class RSSBot(Plugin):
title=getattr(entry, "title", ""),
summary=getattr(entry, "description", "").strip(),
link=getattr(entry, "link", ""),
paywalled=str(getattr(entry, "premium", "")).lower() == "true",
)
@staticmethod