diff --git a/joboffers/publishers/mastodon/__init__.py b/joboffers/publishers/mastodon/__init__.py new file mode 100644 index 0000000..1e4415d --- /dev/null +++ b/joboffers/publishers/mastodon/__init__.py @@ -0,0 +1,48 @@ +import logging + +from django.conf import settings +from mastodon import Mastodon, errors + +from joboffers.utils import hash_secret +from joboffers.publishers import Publisher + + +def _repr_credentials(): + """Show a string representation of mastodon credentials.""" + # Need to convert to string, in case they are strings or they are not set + credentials_repr = ( + f' MASTODON_AUTH_TOKEN: {hash_secret(settings.MASTODON_AUTH_TOKEN)} ' + f' MASTODON_API_BASE_URL: {hash_secret(settings.MASTODON_API_BASE_URL)} ' + ) + return credentials_repr + + +ERROR_LOG_MESSAGE = ( + 'Falló al querer tootear con las siguientes credenciales (hasheadas): %s - Error: %s' +) + + +class MastodonPublisher(Publisher): + """Mastodon Publisher.""" + + name = 'Mastodon' + + def _push_to_api(self, message: str, title: str, link: str): + """Publish a message to mastodon.""" + mastodon = Mastodon( + access_token=settings.MASTODON_AUTH_TOKEN, + api_base_url=settings.MASTODON_API_BASE_URL, + ) + + try: + mastodon.status_post(message) + except errors.MastodonUnauthorizedError as err: + status = None + logging.error(ERROR_LOG_MESSAGE, _repr_credentials(), err) + except Exception as err: + status = None + logging.error("Unknown error when tooting: %s", repr(err)) + else: + status = 200 + + return status diff --git a/joboffers/publishers/mastodon/template.html b/joboffers/publishers/mastodon/template.html new file mode 100644 index 0000000..62ee88a --- /dev/null +++ b/joboffers/publishers/mastodon/template.html @@ -0,0 +1,3 @@ +Oferta: +{{ job_offer.short_description }} +{{ job_offer.get_full_url }} diff --git a/joboffers/tests/test_mastodon_publisher.py b/joboffers/tests/test_mastodon_publisher.py new file mode 100644 index 0000000..26d2f42 --- /dev/null +++ b/joboffers/tests/test_mastodon_publisher.py @@ -0,0 +1,60 @@ +from unittest.mock import patch + +import mastodon + +from ..publishers.mastodon import MastodonPublisher + +DUMMY_MESSAGE = 'message' +DUMMY_TITLE = 'title' +DUMMY_LINK = 'https://example.com' + + +class DummyAPIBad: + def __init__(self, to_raise): + self.to_raise = to_raise + + def status_post(self, *args, **kwargs): + raise self.to_raise + + +class DummyAPIOK: + def status_post(*args, **kwargs): + return + + +@patch('joboffers.publishers.mastodon.Mastodon') +def test_push_to_api_bad_credentials(mock_api, settings, caplog): + """Test exception when the credentials are wrong.""" + mock_api.return_value = DummyAPIBad(mastodon.errors.MastodonUnauthorizedError("bad auth")) + settings.MASTODON_AUTH_TOKEN = "wrong" + settings.MASTODON_API_BASE_URL = "creds" + + status = MastodonPublisher()._push_to_api(DUMMY_MESSAGE, DUMMY_TITLE, DUMMY_LINK) + assert status is None + + expected_error_message = "Falló al querer tootear con las siguientes credenciales (hasheadas)" + assert expected_error_message in caplog.text + + +@patch('joboffers.publishers.mastodon.Mastodon') +def test_push_to_api_generic_error(mock_api, settings, caplog): + """Something went wrong.""" + mock_api.return_value = DummyAPIBad(ValueError("boom")) + settings.MASTODON_AUTH_TOKEN = "good" + settings.MASTODON_API_BASE_URL = "creds" + + status = MastodonPublisher()._push_to_api(DUMMY_MESSAGE, DUMMY_TITLE, DUMMY_LINK) + assert status is None + + expected_error_message = "Unknown error when tooting: ValueError" + assert expected_error_message in caplog.text + + +@patch('joboffers.publishers.mastodon.Mastodon') +def test_push_to_api_ok(mock_api, settings): + mock_api.return_value = DummyAPIOK + settings.MASTODON_AUTH_TOKEN = "good" + settings.MASTODON_API_BASE_URL = "creds" + + status = MastodonPublisher()._push_to_api(DUMMY_MESSAGE, DUMMY_TITLE, DUMMY_LINK) + assert status == 200