big upgrade UwU
This commit is contained in:
@@ -0,0 +1,125 @@
|
||||
"""Turn the many ways of naming a sticker pack into a bare pack name."""
|
||||
|
||||
from __future__ import annotations
|
||||
|
||||
import re
|
||||
from collections.abc import Iterable
|
||||
from pathlib import Path
|
||||
from urllib.parse import parse_qs, urlparse
|
||||
|
||||
from sticker_downloader.errors import InvalidPackReference
|
||||
|
||||
#: Telegram pack names are ASCII letters, digits and underscores.
|
||||
_PACK_NAME_RE = re.compile(r"^[A-Za-z0-9_]{1,64}$")
|
||||
|
||||
#: Hosts that serve ``/addstickers/<name>`` links.
|
||||
_KNOWN_HOSTS = frozenset({"t.me", "telegram.me", "telegram.dog", "telesco.pe"})
|
||||
|
||||
_ADD_STICKERS_PATHS = frozenset({"addstickers", "addemoji"})
|
||||
|
||||
|
||||
def parse_pack_name(reference: str) -> str:
|
||||
"""Extract the pack name from anything a user is likely to paste.
|
||||
|
||||
Accepts full URLs (``https://t.me/addstickers/Foo``), scheme-less links
|
||||
(``t.me/addstickers/Foo``), ``tg://addstickers?set=Foo`` deep links, a bare
|
||||
``addstickers/Foo`` path, and a plain pack name.
|
||||
|
||||
Raises:
|
||||
InvalidPackReference: if no pack name can be recovered.
|
||||
"""
|
||||
text = (reference or "").strip().strip('"').strip("'")
|
||||
if not text:
|
||||
raise InvalidPackReference("empty sticker pack reference")
|
||||
|
||||
if text.lower().startswith("tg://"):
|
||||
return _validate(_name_from_deep_link(text), reference)
|
||||
|
||||
if "/" not in text:
|
||||
return _validate(text, reference)
|
||||
|
||||
return _validate(_name_from_url(text, reference), reference)
|
||||
|
||||
|
||||
def _name_from_deep_link(text: str) -> str:
|
||||
parsed = urlparse(text)
|
||||
query = parse_qs(parsed.query)
|
||||
for key in ("set", "name"):
|
||||
values = query.get(key)
|
||||
if values and values[0].strip():
|
||||
return values[0].strip()
|
||||
return ""
|
||||
|
||||
|
||||
def _name_from_url(text: str, reference: str) -> str:
|
||||
candidate = text if "//" in text else f"https://{text}"
|
||||
parsed = urlparse(candidate)
|
||||
segments = [segment for segment in parsed.path.split("/") if segment]
|
||||
|
||||
host = parsed.netloc.lower().removeprefix("www.").split(":")[0]
|
||||
if host in _ADD_STICKERS_PATHS:
|
||||
# A path-only reference such as "addstickers/Foo".
|
||||
return segments[0] if segments else ""
|
||||
|
||||
if host not in _KNOWN_HOSTS:
|
||||
raise InvalidPackReference(
|
||||
f"{reference!r} is not a Telegram sticker link "
|
||||
"(expected something like https://t.me/addstickers/PackName)"
|
||||
)
|
||||
|
||||
if len(segments) < 2 or segments[0].lower() not in _ADD_STICKERS_PATHS:
|
||||
raise InvalidPackReference(f"{reference!r} is not an /addstickers/ link")
|
||||
return segments[1]
|
||||
|
||||
|
||||
def _validate(name: str, reference: str) -> str:
|
||||
name = name.strip()
|
||||
if not _PACK_NAME_RE.match(name):
|
||||
raise InvalidPackReference(
|
||||
f"{reference!r} does not contain a valid pack name "
|
||||
"(letters, digits and underscores only)"
|
||||
)
|
||||
return name
|
||||
|
||||
|
||||
def parse_pack_reference_lines(
|
||||
lines: Iterable[str], source: str = "input"
|
||||
) -> tuple[list[str], list[str]]:
|
||||
"""Parse pack references from lines of text.
|
||||
|
||||
Blank lines and ``#`` comments are ignored. Duplicate references collapse to
|
||||
the first occurrence so a pack is never downloaded twice in one run.
|
||||
|
||||
Returns:
|
||||
``(references, problems)`` — valid references in input order, plus a
|
||||
human-readable message for every line that could not be parsed.
|
||||
"""
|
||||
references: list[str] = []
|
||||
problems: list[str] = []
|
||||
seen: set[str] = set()
|
||||
|
||||
for line_number, raw_line in enumerate(lines, start=1):
|
||||
line = raw_line.strip()
|
||||
if not line or line.startswith("#"):
|
||||
continue
|
||||
try:
|
||||
name = parse_pack_name(line)
|
||||
except InvalidPackReference as error:
|
||||
problems.append(f"{source}:{line_number}: {error}")
|
||||
continue
|
||||
if name.lower() in seen:
|
||||
continue
|
||||
seen.add(name.lower())
|
||||
references.append(line)
|
||||
|
||||
return references, problems
|
||||
|
||||
|
||||
def read_pack_references(path: Path | str) -> tuple[list[str], list[str]]:
|
||||
"""Read pack references from a text file, one per line.
|
||||
|
||||
See :func:`parse_pack_reference_lines` for the accepted syntax.
|
||||
"""
|
||||
file_path = Path(path)
|
||||
lines = file_path.read_text(encoding="utf-8").splitlines()
|
||||
return parse_pack_reference_lines(lines, source=str(file_path))
|
||||
Reference in New Issue
Block a user