mirror of
https://github.com/Tech-Workers-Coalition-Italia/mobilizon-reshare.git
synced 2025-02-27 08:57:45 +01:00
* fixed parsing bug * implemented events and publications endpoints split endpoints by entity removed credentials * add pagination (#179) * added pagination * integrated pagination with tortoise * added test for publications * removed converter file * moved publications to dataclasses module * implemented import pattern on dataclasses to prevent circular imports * removed redundant fetch * removed unused query * split build_publications * split failed_publications * removed redundant query functions * split publication retrieve * split all read functions * removed redundant write function * fixed lock
141 lines
4.3 KiB
Python
141 lines
4.3 KiB
Python
import re
|
||
from typing import Optional
|
||
|
||
import pkg_resources
|
||
import requests
|
||
from bs4 import BeautifulSoup
|
||
from requests import Response
|
||
|
||
from mobilizon_reshare.dataclasses import MobilizonEvent
|
||
from mobilizon_reshare.publishers.abstract import (
|
||
AbstractEventFormatter,
|
||
AbstractPlatform,
|
||
)
|
||
from mobilizon_reshare.publishers.exceptions import (
|
||
InvalidBot,
|
||
InvalidEvent,
|
||
InvalidResponse,
|
||
InvalidMessage,
|
||
)
|
||
|
||
|
||
class TelegramFormatter(AbstractEventFormatter):
|
||
default_template_path = pkg_resources.resource_filename(
|
||
"mobilizon_reshare.publishers.templates", "telegram.tmpl.j2"
|
||
)
|
||
|
||
default_recap_template_path = pkg_resources.resource_filename(
|
||
"mobilizon_reshare.publishers.templates", "telegram_recap.tmpl.j2"
|
||
)
|
||
|
||
default_recap_header_template_path = pkg_resources.resource_filename(
|
||
"mobilizon_reshare.publishers.templates", "telegram_recap_header.tmpl.j2"
|
||
)
|
||
|
||
_conf = ("publisher", "telegram")
|
||
|
||
def _validate_event(self, event: MobilizonEvent) -> None:
|
||
|
||
description = event.description
|
||
if not (description and description.strip()):
|
||
self._log_error("No description was found", raise_error=InvalidEvent)
|
||
|
||
def _validate_message(self, message: str) -> None:
|
||
|
||
if (
|
||
len("".join(BeautifulSoup(message, "html.parser").findAll(text=True)))
|
||
>= 4096
|
||
):
|
||
self._log_error("Message is too long", raise_error=InvalidMessage)
|
||
|
||
def _preprocess_message(self, message: str) -> str:
|
||
"""
|
||
This function converts HTML5 to Telegram's HTML dialect
|
||
:param message: a HTML5 string
|
||
:return: a HTML string compatible with Telegram
|
||
"""
|
||
html = BeautifulSoup(message, "html.parser")
|
||
# replacing paragraphs
|
||
for tag in html.findAll(["p", "br"]):
|
||
tag.append("\n")
|
||
tag.replaceWithChildren()
|
||
# replacing headers
|
||
for tag in html.findAll(["h1", "h2", "h3"]):
|
||
if tag.text: # only if they are not empty
|
||
tag.name = "b"
|
||
tag.insert_after("\n")
|
||
tag.insert_before("\n")
|
||
else:
|
||
tag.decompose()
|
||
# removing lists
|
||
for tag in html.findAll("ul"):
|
||
tag.unwrap()
|
||
# replacing list elements with dots
|
||
for tag in html.findAll(["li"]):
|
||
tag.insert(0, "• ")
|
||
tag.unwrap()
|
||
# cleaning html trailing whitespace
|
||
for tag in html.findAll("a"):
|
||
if "href" in tag:
|
||
tag["href"] = tag["href"].replace(" ", "").strip().lstrip()
|
||
s = str(html)
|
||
return re.sub(r"\n{2,}", "\n\n", s).strip() # remove multiple newlines
|
||
|
||
|
||
class TelegramPlatform(AbstractPlatform):
|
||
"""
|
||
Telegram publisher class.
|
||
"""
|
||
|
||
name = "telegram"
|
||
|
||
def validate_credentials(self):
|
||
res = requests.get(f"https://api.telegram.org/bot{self.conf.token}/getMe")
|
||
data = self._validate_response(res)
|
||
|
||
if not self.conf.username == data.get("result", {}).get("username"):
|
||
self._log_error(
|
||
"Found a different bot than the expected one", raise_error=InvalidBot,
|
||
)
|
||
|
||
def _send(self, message: str, event: Optional[MobilizonEvent] = None) -> Response:
|
||
return requests.post(
|
||
url=f"https://api.telegram.org/bot{self.conf.token}/sendMessage",
|
||
json={"chat_id": self.conf.chat_id, "text": message, "parse_mode": "html"},
|
||
)
|
||
|
||
def _validate_response(self, res):
|
||
try:
|
||
res.raise_for_status()
|
||
except requests.exceptions.HTTPError as e:
|
||
self._log_error(
|
||
f"Server returned invalid data: {str(e)}\n{res.text}",
|
||
raise_error=InvalidResponse,
|
||
)
|
||
|
||
try:
|
||
data = res.json()
|
||
|
||
except Exception as e:
|
||
self._log_error(
|
||
f"Server returned invalid json data: {str(e)}",
|
||
raise_error=InvalidResponse,
|
||
)
|
||
|
||
if not data.get("ok"):
|
||
self._log_error(
|
||
f"Invalid request (response: {data})", raise_error=InvalidResponse,
|
||
)
|
||
|
||
return data
|
||
|
||
|
||
class TelegramPublisher(TelegramPlatform):
|
||
|
||
_conf = ("publisher", "telegram")
|
||
|
||
|
||
class TelegramNotifier(TelegramPlatform):
|
||
|
||
_conf = ("notifier", "telegram")
|