altrepo.parser: add news.news_urls parser

parent 8459f362
......@@ -185,6 +185,44 @@ tests = ["cloudpickle ; platform_python_implementation == \"CPython\"", "hypothe
tests-mypy = ["mypy (>=1.11.1) ; platform_python_implementation == \"CPython\" and python_version >= \"3.10\"", "pytest-mypy-plugins ; platform_python_implementation == \"CPython\" and python_version >= \"3.10\""]
[[package]]
name = "beautifulsoup4"
version = "4.13.4"
description = "Screen-scraping library"
optional = false
python-versions = ">=3.7.0"
groups = ["main"]
files = [
{file = "beautifulsoup4-4.13.4-py3-none-any.whl", hash = "sha256:9bbbb14bfde9d79f38b8cd5f8c7c85f4b8f2523190ebed90e950a8dea4cb1c4b"},
{file = "beautifulsoup4-4.13.4.tar.gz", hash = "sha256:dbb3c4e1ceae6aefebdaf2423247260cd062430a410e38c66f2baa50a8437195"},
]
[package.dependencies]
soupsieve = ">1.2"
typing-extensions = ">=4.0.0"
[package.extras]
cchardet = ["cchardet"]
chardet = ["chardet"]
charset-normalizer = ["charset-normalizer"]
html5lib = ["html5lib"]
lxml = ["lxml"]
[[package]]
name = "bs4"
version = "0.0.2"
description = "Dummy package for Beautiful Soup (beautifulsoup4)"
optional = false
python-versions = "*"
groups = ["main"]
files = [
{file = "bs4-0.0.2-py2.py3-none-any.whl", hash = "sha256:abf8742c0805ef7f662dce4b51cca104cffe52b835238afc169142ab9b3fbccc"},
{file = "bs4-0.0.2.tar.gz", hash = "sha256:a48685c58f50fe127722417bae83fe6badf500d54b55f7e39ffe43b798653925"},
]
[package.dependencies]
beautifulsoup4 = "*"
[[package]]
name = "certifi"
version = "2025.7.9"
description = "Python package for providing Mozilla's CA Bundle."
......@@ -1034,6 +1072,18 @@ files = [
cli = ["click (>=5.0)"]
[[package]]
name = "soupsieve"
version = "2.7"
description = "A modern CSS selector implementation for Beautiful Soup."
optional = false
python-versions = ">=3.8"
groups = ["main"]
files = [
{file = "soupsieve-2.7-py3-none-any.whl", hash = "sha256:6e60cc5c1ffaf1cebcc12e8188320b72071e922c2e897f737cadce79ad5d30c4"},
{file = "soupsieve-2.7.tar.gz", hash = "sha256:ad282f9b6926286d2ead4750552c8a6142bc4c783fd66b0293547c8fe6ae126a"},
]
[[package]]
name = "telegrinder"
version = "0.5.1"
description = "Modern visionary telegram bot framework."
......@@ -1228,4 +1278,4 @@ propcache = ">=0.2.1"
[metadata]
lock-version = "2.1"
python-versions = ">=3.12,<4.0"
content-hash = "a510528d6af1e3839834d33b34c1b07dd8842f5755c5d861429e32bf4432e0af"
content-hash = "a931f25d5878e10204c31cdf053d05a4587b3b5c55056e719c2f1489525c1084"
......@@ -12,7 +12,8 @@ dependencies = [
"telegrinder (>=0.5.1,<0.6.0)",
"pydantic (>=2.11.7,<3.0.0)",
"pydantic-settings (>=2.10.1,<3.0.0)",
"peewee (>=3.18.2,<4.0.0)"
"peewee (>=3.18.2,<4.0.0)",
"bs4 (>=0.0.2,<0.0.3)"
]
[project.urls]
......
import aiohttp
from bs4 import BeautifulSoup
from typing import Literal
from datetime import datetime
import calendar
import re
from . import models
......@@ -10,18 +13,54 @@ class BaseParser:
def __init__(self, session: aiohttp.ClientSession):
self.session = session
async def get(self, url: str):
async def get(self, url: str, encoding: str = "utf-8"):
async with self.session.get(url) as resp:
resp.raise_for_status()
return await resp.text()
return await resp.text(encoding=encoding)
class NewsInfo:
def __init__(self, client: BaseParser):
self.client = client
async def news_url(self, news_type: Literal["sisyphus", "p11", "bugs"]):
...
async def news_urls(self) -> models.NewsURL:
now = datetime.now()
month_path = f"{now.year}-{calendar.month_name[now.month]}"
today_str = now.strftime("%Y%m%d")
base_url = f"https://lists.altlinux.org/pipermail/sisyphus-cybertalk/{month_path}/"
html = await self.client.get(f"{base_url}date.html", "koi8-r")
soup = BeautifulSoup(html, "html.parser")
sisyphus_id = -1
result = {"sisyphus": None, "bugs": None, "p11": None}
for li in soup.find_all("li"):
a = li.find("a")
if not a or not a.get("href"):
continue
href = a["href"]
text = a.get_text(strip=True)
match = re.search(r"(\d+)\.html", href)
if not match:
continue
msg_id = int(match.group(1))
url = base_url + href
if f"Sisyphus-{today_str} packages" in text:
result["sisyphus"] = url
sisyphus_id = msg_id
elif f"Sisyphus-{today_str} bugs" in text:
result["bugs"] = url
elif "p11/branch packages" in text and msg_id > sisyphus_id:
result["p11"] = url
if result["sisyphus"] is None:
result["p11"] = None
return models.NewsURL(**result)
async def sisyphus(self):
...
......
from pydantic import BaseModel
from typing import List, Dict, Any
class NewsURL(BaseModel):
sisyphus: str | None = None
p11: str | None = None
bugs: str | None = None
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment