Files
AV-Live/data_feeds/feeds/wikimedia.py
T
L'électron rare bd3dd89511 fix(feeds): 3 warnings externes
3 fixes pour reduire le bruit de logs des sources publiques :

wikimedia.py : ajoute User-Agent descriptif. WMF EventStreams
refuse l'UA httpx par defaut avec 403 Forbidden. Le 1.0+URL
descriptif est obligatoire pour le rate-limiting et les abuse
reports.

netzfrequenz.py : backoff exponentiel cap a 5 min sur les ws
disconnect. La source (mainsfrequenz.de) est NXDOMAIN depuis
mai 2026 ; on logguait toutes les 3 s soit ~28 800/jour. Avec
backoff x1.6 et log uniquement attempt==1 ou attempt%10, on
descend a ~30 log/jour.

config.data-only.toml : opensky poll_seconds 20 -> 60. L'API
anonyme est credit-based (10 req/min budget). 60s reste
raisonnable pour le viz tout en restaurant le credit.
2026-05-13 20:49:23 +02:00

67 lines
2.5 KiB
Python

"""Wikipedia recent changes — Wikimedia EventStreams SSE.
Firehose des modifications Wikipedia toutes langues confondues.
Tres bavard (~10-30 events/s) — on echantillonne et on emet une
fraction + des compteurs.
OSC out :
/data/wikimedia/edit lang title bot (sample)
/data/wikimedia/rate per_second
"""
from __future__ import annotations
import asyncio
import json
import logging
import random
import time
import httpx
LOG = logging.getLogger("feed.wikimedia")
URL = "https://stream.wikimedia.org/v2/stream/recentchange"
async def run(ctx) -> None:
cfg = ctx.cfg
sample = float(cfg.get("sample_rate", 0.05)) # emit 5% des edits
window = float(cfg.get("rate_window_s", 5.0))
# WMF EventStreams refuse l'User-Agent par defaut httpx (403). Il
# faut une string descriptive + URL/email pour les abuse reports.
headers = {
"Accept": "text/event-stream",
"User-Agent": "av-live-data-feeds/1.0 (https://github.com/electron-rare/AV-Live)",
}
while True:
try:
async with httpx.AsyncClient(timeout=None) as cli:
async with cli.stream("GET", URL, headers=headers) as r:
r.raise_for_status()
bucket = 0
bucket_start = time.monotonic()
async for line in r.aiter_lines():
if not line.startswith("data:"):
continue
try:
ev = json.loads(line[5:].strip())
except json.JSONDecodeError:
continue
bucket += 1
# Sample emit
if random.random() < sample:
lang = (ev.get("wiki") or "").replace("wiki", "")
title = str(ev.get("title", ""))[:64]
bot = 1.0 if ev.get("bot") else 0.0
ctx.send("edit", lang, title, bot)
# Periodic rate
now = time.monotonic()
if now - bucket_start >= window:
per_s = bucket / (now - bucket_start)
ctx.send("rate", float(per_s))
bucket = 0
bucket_start = now
except Exception as e: # noqa: BLE001
LOG.warning("wikimedia stream error: %s — reconnect 5s", e)
await asyncio.sleep(5.0)