altrepo.parser: add news.news_urls parser

parent 8459f362
...@@ -185,6 +185,44 @@ tests = ["cloudpickle ; platform_python_implementation == \"CPython\"", "hypothe ...@@ -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\""] 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]] [[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" name = "certifi"
version = "2025.7.9" version = "2025.7.9"
description = "Python package for providing Mozilla's CA Bundle." description = "Python package for providing Mozilla's CA Bundle."
...@@ -1034,6 +1072,18 @@ files = [ ...@@ -1034,6 +1072,18 @@ files = [
cli = ["click (>=5.0)"] cli = ["click (>=5.0)"]
[[package]] [[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" name = "telegrinder"
version = "0.5.1" version = "0.5.1"
description = "Modern visionary telegram bot framework." description = "Modern visionary telegram bot framework."
...@@ -1228,4 +1278,4 @@ propcache = ">=0.2.1" ...@@ -1228,4 +1278,4 @@ propcache = ">=0.2.1"
[metadata] [metadata]
lock-version = "2.1" lock-version = "2.1"
python-versions = ">=3.12,<4.0" python-versions = ">=3.12,<4.0"
content-hash = "a510528d6af1e3839834d33b34c1b07dd8842f5755c5d861429e32bf4432e0af" content-hash = "a931f25d5878e10204c31cdf053d05a4587b3b5c55056e719c2f1489525c1084"
...@@ -12,7 +12,8 @@ dependencies = [ ...@@ -12,7 +12,8 @@ dependencies = [
"telegrinder (>=0.5.1,<0.6.0)", "telegrinder (>=0.5.1,<0.6.0)",
"pydantic (>=2.11.7,<3.0.0)", "pydantic (>=2.11.7,<3.0.0)",
"pydantic-settings (>=2.10.1,<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] [project.urls]
......
import aiohttp import aiohttp
from bs4 import BeautifulSoup
from typing import Literal from datetime import datetime
import calendar
import re
from . import models from . import models
...@@ -10,18 +13,54 @@ class BaseParser: ...@@ -10,18 +13,54 @@ class BaseParser:
def __init__(self, session: aiohttp.ClientSession): def __init__(self, session: aiohttp.ClientSession):
self.session = session 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: async with self.session.get(url) as resp:
resp.raise_for_status() resp.raise_for_status()
return await resp.text() return await resp.text(encoding=encoding)
class NewsInfo: class NewsInfo:
def __init__(self, client: BaseParser): def __init__(self, client: BaseParser):
self.client = client 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): async def sisyphus(self):
... ...
......
from pydantic import BaseModel from pydantic import BaseModel
from typing import List, Dict, Any 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