mirror of
https://github.com/Tech-Workers-Coalition-Italia/mobilizon-reshare.git
synced 2025-02-11 09:10:51 +01:00
Add abstract functioning for publishers, mock-up for Telegram publisher
This commit is contained in:
parent
07bba0f752
commit
b60971206f
@ -1 +1,4 @@
|
||||
__version__ = '0.1.0'
|
||||
|
||||
|
||||
from . import publishers
|
||||
|
40
mobilizon_bots/publishers/__init__.py
Normal file
40
mobilizon_bots/publishers/__init__.py
Normal file
@ -0,0 +1,40 @@
|
||||
from typing import Iterable
|
||||
|
||||
from . import abstract
|
||||
from . import telegram
|
||||
|
||||
# WIP
|
||||
# from . import twitter
|
||||
|
||||
|
||||
def run(publishers: Iterable[abstract.AbstractPublisher]) -> dict:
|
||||
invalid_credentials, invalid_event = [], []
|
||||
for p in publishers:
|
||||
if not p.validate_credentials():
|
||||
invalid_credentials.append(p)
|
||||
if not p.validate_event():
|
||||
invalid_event.append(p)
|
||||
if invalid_credentials or invalid_event:
|
||||
return {
|
||||
'status': 'fail',
|
||||
'description': "Validation failed for at least 1 publisher",
|
||||
'invalid_credentials': invalid_credentials,
|
||||
'invalid_event': invalid_event,
|
||||
}
|
||||
failed_publishers, successful_publishers = [], []
|
||||
for p in publishers:
|
||||
if p.post():
|
||||
successful_publishers.append(p)
|
||||
else:
|
||||
failed_publishers.append(p)
|
||||
if failed_publishers:
|
||||
return {
|
||||
'status': 'fail',
|
||||
'description': "Posting failed for at least 1 publisher",
|
||||
'failed_publishers': failed_publishers,
|
||||
'successful_publishers': successful_publishers,
|
||||
}
|
||||
return {
|
||||
'status': 'success',
|
||||
'description': "https://www.youtube.com/watch?v=2lHgmC6PBBE",
|
||||
}
|
78
mobilizon_bots/publishers/abstract.py
Normal file
78
mobilizon_bots/publishers/abstract.py
Normal file
@ -0,0 +1,78 @@
|
||||
import inspect
|
||||
import logging
|
||||
|
||||
from abc import ABC, abstractmethod
|
||||
|
||||
logging.basicConfig(
|
||||
format='%(asctime)s - %(levelname)s - %(name)s - %(message)s',
|
||||
level=logging.INFO
|
||||
)
|
||||
logger = logging.getLogger("Publishers")
|
||||
|
||||
|
||||
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):
|
||||
self.credentials = credentials
|
||||
self.event = event
|
||||
|
||||
def __repr__(self):
|
||||
return type(self).__name__
|
||||
|
||||
__str__ = __repr__
|
||||
|
||||
@abstractmethod
|
||||
def post(self) -> bool:
|
||||
"""
|
||||
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) -> bool:
|
||||
"""
|
||||
Validates credentials.
|
||||
:return: True or False according to whether credentials are valid.
|
||||
"""
|
||||
raise NotImplementedError
|
||||
|
||||
@abstractmethod
|
||||
def validate_event(self) -> bool:
|
||||
"""
|
||||
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)
|
||||
|
||||
def log_info(self, msg, *args, **kwargs):
|
||||
self.__log(logging.INFO, msg, *args, **kwargs)
|
||||
|
||||
def log_warning(self, msg, *args, **kwargs):
|
||||
self.__log(logging.WARNING, msg, *args, **kwargs)
|
||||
|
||||
def log_error(self, msg, *args, **kwargs):
|
||||
self.__log(logging.ERROR, msg, *args, **kwargs)
|
||||
|
||||
def log_critical(self, msg, *args, **kwargs):
|
||||
self.__log(logging.CRITICAL, msg, *args, **kwargs)
|
||||
|
||||
def __log(self, level, msg, *args, **kwargs):
|
||||
method = inspect.currentframe().f_back.f_back.f_code.co_name
|
||||
logger.log(level, f"{self}.{method}(): {msg}", *args, **kwargs)
|
66
mobilizon_bots/publishers/telegram.py
Normal file
66
mobilizon_bots/publishers/telegram.py
Normal file
@ -0,0 +1,66 @@
|
||||
import requests
|
||||
|
||||
from .abstract import AbstractPublisher
|
||||
|
||||
|
||||
class TelegramPublisher(AbstractPublisher):
|
||||
|
||||
def post(self) -> bool:
|
||||
chat_id = self.credentials['chat_id']
|
||||
text = self.event['text']
|
||||
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)
|
||||
)
|
||||
data = self.__validate_response(res)
|
||||
if data.get('__error'):
|
||||
self.log_error(data['__error'])
|
||||
return False
|
||||
|
||||
return True
|
||||
|
||||
def validate_credentials(self) -> bool:
|
||||
chat_id = self.credentials.get('chat_id')
|
||||
token = self.credentials.get('token')
|
||||
username = self.credentials.get('username')
|
||||
if any(a is None for a in (chat_id, token, username)):
|
||||
self.log_error("Required info is missing")
|
||||
return False
|
||||
|
||||
res = requests.get(f'https://api.telegram.org/bot{token}/getMe')
|
||||
data = self.__validate_response(res)
|
||||
if data.get('__error'):
|
||||
self.log_error(data['__error'])
|
||||
return False
|
||||
|
||||
if not username == data.get('result', {}).get('username'):
|
||||
self.log_error("Found a different bot than the expected one!")
|
||||
return False
|
||||
|
||||
return True
|
||||
|
||||
def validate_event(self) -> bool:
|
||||
text = self.event.get('text')
|
||||
if not (text and text.strip()):
|
||||
self.log_error(f"No text was found!")
|
||||
return False
|
||||
return True
|
||||
|
||||
@staticmethod
|
||||
def __validate_response(res):
|
||||
try:
|
||||
res.raise_for_status()
|
||||
except requests.exceptions.HTTPError as e:
|
||||
return {'__error': str(e)}
|
||||
|
||||
try:
|
||||
data = res.json()
|
||||
except ValueError:
|
||||
return {'__error': "Server returned invalid json data"}
|
||||
|
||||
if not data.get('ok'):
|
||||
data['__error'] = f"Invalid request (response: {data})"
|
||||
|
||||
return data
|
Loading…
x
Reference in New Issue
Block a user