Updated publishers logics: using messengers to allow publications without events
This commit is contained in:
parent
10c6b62a30
commit
452dd407fd
|
@ -3,71 +3,23 @@ import logging
|
|||
|
||||
from abc import ABC, abstractmethod
|
||||
|
||||
from mobilizon_bots.event.event import MobilizonEvent
|
||||
from .exceptions import PublisherError
|
||||
|
||||
logger = logging.getLogger(__name__)
|
||||
|
||||
|
||||
class AbstractPublisher(ABC):
|
||||
"""
|
||||
Generic publisher class.
|
||||
|
||||
Shall be inherited from specific subclasses that will manage validation
|
||||
process for events and credentials, text formatting, posting, etc.
|
||||
|
||||
Class attributes:
|
||||
- ``credentials``: a ``dict`` containing every useful info that the
|
||||
current publisher will need to correctly login to its platform
|
||||
- ``event``: a ``dict`` containing every useful info from the event
|
||||
"""
|
||||
|
||||
# TODO: will the actual event be managed by its own class?
|
||||
def __init__(self, credentials: dict, event: dict):
|
||||
class AbstractMessenger(ABC):
|
||||
def __init__(self, credentials: dict, destination: dict, message: str):
|
||||
self.credentials = credentials
|
||||
self.event = event
|
||||
self.destination = destination
|
||||
self.message = message
|
||||
|
||||
def __repr__(self):
|
||||
return type(self).__name__
|
||||
|
||||
__str__ = __repr__
|
||||
|
||||
@abstractmethod
|
||||
def post(self) -> dict:
|
||||
"""
|
||||
Publishes the actual post on social media using ``data`` info.
|
||||
:return: True or False according to whether publisher was able to
|
||||
complete its task
|
||||
"""
|
||||
raise NotImplementedError
|
||||
|
||||
@abstractmethod
|
||||
def validate_credentials(self) -> dict:
|
||||
"""
|
||||
Validates credentials.
|
||||
:return: True or False according to whether credentials are valid.
|
||||
"""
|
||||
raise NotImplementedError
|
||||
|
||||
def are_credentials_valid(self) -> bool:
|
||||
try:
|
||||
self.validate_credentials()
|
||||
except Exception:
|
||||
return False
|
||||
return True
|
||||
|
||||
def is_event_valid(self) -> bool:
|
||||
try:
|
||||
self.validate_event()
|
||||
except Exception:
|
||||
return False
|
||||
return True
|
||||
|
||||
@abstractmethod
|
||||
def validate_event(self) -> dict:
|
||||
"""
|
||||
Validates publisher's event.
|
||||
:return: True or False according to whether event is valid.
|
||||
"""
|
||||
raise NotImplementedError
|
||||
|
||||
def _log_debug(self, msg, *args, **kwargs):
|
||||
self.__log(logging.DEBUG, msg, *args, **kwargs)
|
||||
|
||||
|
@ -87,6 +39,84 @@ class AbstractPublisher(ABC):
|
|||
method = inspect.currentframe().f_back.f_back.f_code.co_name
|
||||
logger.log(level, f"{self}.{method}(): {msg}", *args, **kwargs)
|
||||
|
||||
def _log_error_and_raise(self, message):
|
||||
def _log_error_and_raise(self, error_class, message):
|
||||
self._log_error(message)
|
||||
raise ValueError(message)
|
||||
raise error_class(message)
|
||||
|
||||
def are_credentials_valid(self) -> bool:
|
||||
try:
|
||||
self.validate_credentials()
|
||||
except PublisherError:
|
||||
return False
|
||||
return True
|
||||
|
||||
@abstractmethod
|
||||
def validate_credentials(self):
|
||||
"""
|
||||
Validates credentials.
|
||||
"""
|
||||
raise NotImplementedError
|
||||
|
||||
@abstractmethod
|
||||
def post(self) -> bool:
|
||||
"""
|
||||
Publishes the actual post on social media.
|
||||
:return: True or False according to whether messenger was able to
|
||||
complete its task
|
||||
"""
|
||||
raise NotImplementedError
|
||||
|
||||
def is_message_valid(self) -> bool:
|
||||
try:
|
||||
self.validate_message()
|
||||
except PublisherError:
|
||||
return False
|
||||
return True
|
||||
|
||||
@abstractmethod
|
||||
def validate_message(self):
|
||||
"""
|
||||
Validates messenger's message.
|
||||
"""
|
||||
raise NotImplementedError
|
||||
|
||||
|
||||
class AbstractPublisher(AbstractMessenger):
|
||||
"""
|
||||
Generic publisher class.
|
||||
|
||||
Shall be inherited from specific subclasses that will manage validation
|
||||
process for events and credentials, text formatting, posting, etc.
|
||||
|
||||
Class attributes:
|
||||
- ``credentials``: a ``dict`` containing every useful info that the
|
||||
current publisher will need to correctly login to its platform
|
||||
- ``event``: a ``MobilizonEvent`` containing every useful info from
|
||||
the event
|
||||
"""
|
||||
|
||||
def __init__(self, credentials: dict, destination: dict, event: MobilizonEvent):
|
||||
msg = self.get_message_from_event()
|
||||
super().__init__(credentials, destination, msg)
|
||||
self.event = event
|
||||
|
||||
def is_event_valid(self) -> bool:
|
||||
try:
|
||||
self.validate_event()
|
||||
except PublisherError:
|
||||
return False
|
||||
return True
|
||||
|
||||
@abstractmethod
|
||||
def validate_event(self):
|
||||
"""
|
||||
Validates publisher's event.
|
||||
"""
|
||||
raise NotImplementedError
|
||||
|
||||
@abstractmethod
|
||||
def get_message_from_event(self) -> str:
|
||||
"""
|
||||
Retrieves a message from the event itself.
|
||||
"""
|
||||
raise NotImplementedError
|
||||
|
|
|
@ -1,50 +1,74 @@
|
|||
import requests
|
||||
|
||||
from .abstract import AbstractPublisher
|
||||
from .exceptions import InvalidBot, InvalidCredentials, InvalidEvent, InvalidResponse
|
||||
|
||||
|
||||
class TelegramPublisher(AbstractPublisher):
|
||||
def post(self) -> bool:
|
||||
def post(self):
|
||||
chat_id = self.credentials["chat_id"]
|
||||
text = self.event["text"]
|
||||
text = self.event.description
|
||||
token = self.credentials["token"]
|
||||
post_params_kwargs = self.event.get("post_params_kwargs")
|
||||
res = requests.post(
|
||||
url=f"https://api.telegram.org/bot{token}/sendMessage",
|
||||
params=dict(chat_id=chat_id, text=text, **post_params_kwargs),
|
||||
params={"chat_id": chat_id, "text": text},
|
||||
)
|
||||
return self._validate_response(res)
|
||||
try:
|
||||
self._validate_response(res)
|
||||
return True
|
||||
except InvalidResponse:
|
||||
return False
|
||||
|
||||
def validate_credentials(self) -> bool:
|
||||
def validate_credentials(self):
|
||||
chat_id = self.credentials.get("chat_id")
|
||||
token = self.credentials.get("token")
|
||||
username = self.credentials.get("username")
|
||||
if all([chat_id, token, username]):
|
||||
# TODO: add composable errors to highlight which credentials are missing
|
||||
self._log_error_and_raise("Some credentials are missing")
|
||||
err = []
|
||||
if not chat_id:
|
||||
err.append("chat ID")
|
||||
if not token:
|
||||
err.append("token")
|
||||
if not username:
|
||||
err.append("username")
|
||||
if err:
|
||||
self._log_error_and_raise(
|
||||
InvalidCredentials, ", ".join(err) + " is/are missing"
|
||||
)
|
||||
|
||||
res = requests.get(f"https://api.telegram.org/bot{token}/getMe")
|
||||
data = self._validate_response(res)
|
||||
|
||||
if not username == data.get("result", {}).get("username"):
|
||||
self._log_error_and_raise("Found a different bot than the expected one")
|
||||
return data
|
||||
self._log_error_and_raise(
|
||||
InvalidBot, "Found a different bot than the expected one"
|
||||
)
|
||||
|
||||
def validate_event(self) -> bool:
|
||||
text = self.event.get("text")
|
||||
def validate_event(self):
|
||||
text = self.event.description
|
||||
if not (text and text.strip()):
|
||||
self._log_error_and_raise("No text was found. Invalid event")
|
||||
self._log_error_and_raise(InvalidEvent, "No text was found")
|
||||
|
||||
def _validate_response(self, res):
|
||||
res.raise_for_status()
|
||||
|
||||
try:
|
||||
data = res.json()
|
||||
except ValueError as e:
|
||||
self._log_error("Server returned invalid json data")
|
||||
raise ValueError from e
|
||||
except Exception as e:
|
||||
self._log_error_and_raise(
|
||||
InvalidResponse, f"Server returned invalid json data: {str(e)}"
|
||||
)
|
||||
|
||||
if not data.get("ok"):
|
||||
raise ValueError(f"Invalid request (response: {data})")
|
||||
self._log_error_and_raise(
|
||||
InvalidResponse, f"Invalid request (response: {data})"
|
||||
)
|
||||
|
||||
return data
|
||||
|
||||
def get_message_from_event(self) -> str:
|
||||
# TODO implement
|
||||
return ""
|
||||
|
||||
def validate_message(self):
|
||||
# TODO implement
|
||||
pass
|
||||
|
|
Loading…
Reference in New Issue