Files
nosibakabot/bot/msgprocessor.py
T
2024-11-07 17:03:58 +03:00

108 lines
3.9 KiB
Python

from typing import Callable
from dataclasses import dataclass
from exception import UrlRemoverNotImplementedException
from urllib.parse import urlparse, parse_qs, urlencode, urlunparse
from utils import find_all_string_entries
@dataclass(init=True)
class TrackerRemovalProcessorMessage:
fromUsername: str
text: str
@dataclass(frozen=True, init=True)
class TrackerRemovalResult:
needsToReply: bool
text: str
class TrackerRemovalMsgProcessor:
def __init__(self, msg: TrackerRemovalProcessorMessage):
self.__msg = msg
def process(self) -> TrackerRemovalResult:
if not self.__remove_trackers_from_msg_urls():
return TrackerRemovalResult(needsToReply=False, text="") # дальнейшие трансформации смысла не имеют
self.__emplace_sender_into_msg_text()
return TrackerRemovalResult(needsToReply=True, text=self.__msg.text) # сообщение было изменено, нужно ответ отослать
def __remove_trackers_from_msg_urls(self) -> bool:
trackers_extracted = False
# todo: вообще мы работаем с http и это юзкейс обскьюрный
# но ссылка может быть и без указания схемы, телега может распарсить
# просто строку через точки и в конце какой то домен верхнего уровня как ссылку
def is_url(url: str) -> bool:
SCHEMES = ["http://", "https://"]
return len([s for s in SCHEMES if url.startswith(s)]) != 0
lexems = self.__msg.text.split()
for i, l in enumerate(lexems):
if not is_url(l):
continue
removed_trackers_url = self.__remove_tracker(l)
if l == removed_trackers_url: # изменений урла не было
continue
trackers_extracted = True
lexems[i] = removed_trackers_url
self.__msg.text = " ".join(lexems)
return trackers_extracted
@staticmethod
def __remove_tracker(url: str) -> str:
parsed_url = urlparse(url)
if parsed_url.hostname is None:
return url
hostname = str(parsed_url.hostname)
try:
return TrackerRemoverFactory.make_remover(hostname)(url)
except UrlRemoverNotImplementedException:
return url
def __emplace_sender_into_msg_text(self):
self.__msg.text = f'Message from {self.__msg.fromUsername}:\n\n{self.__msg.text}'
class TrackerRemoverFactory:
TrackerRemover = Callable[[str], str]
@staticmethod
def make_remover(domain: str) -> TrackerRemover:
@dataclass(frozen=True, init=True)
class RemoverIdentifyer:
domains: list[str]
remover: TrackerRemoverFactory.TrackerRemover
removers_by_domain = [
RemoverIdentifyer(
domains=["youtube.com", "youtu.be"],
remover=TrackerRemoverFactory.remove_yt_trackers,
)
]
remover_one = [
r
for r in removers_by_domain
if len([d for d in r.domains if d.endswith(domain)]) != 0
]
if len(remover_one) == 0:
raise UrlRemoverNotImplementedException(domain)
return remover_one[0].remover
@staticmethod
def remove_yt_trackers(url: str) -> str:
# todo: подумать как обобщить, мб билдер стратегии поиска трекера
# но эт сильно на потом
QUERY_PARAMS_TRACKER = "si"
parsed_url = urlparse(url)
query_params = parse_qs(parsed_url.query)
if QUERY_PARAMS_TRACKER in query_params:
del query_params[QUERY_PARAMS_TRACKER]
return urlunparse(
parsed_url._replace(
query=urlencode(query_params, doseq=True)
)
)