diff --git a/backend/setup.cfg b/backend/setup.cfg index d0df5e2..6f11b83 100644 --- a/backend/setup.cfg +++ b/backend/setup.cfg @@ -34,6 +34,8 @@ install_requires = pytoml requests scrypt + Mastodon.py + pydantic[email] [options.packages.find] where = src diff --git a/backend/src/kibicara/platforms/mastodon/__init__.py b/backend/src/kibicara/platforms/mastodon/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/backend/src/kibicara/platforms/mastodon/bot.py b/backend/src/kibicara/platforms/mastodon/bot.py new file mode 100644 index 0000000..cab0fae --- /dev/null +++ b/backend/src/kibicara/platforms/mastodon/bot.py @@ -0,0 +1,101 @@ +# Copyright (C) 2020 by Thomas Lindner +# Copyright (C) 2020 by Cathy Hu +# Copyright (C) 2020 by Martin Rey +# +# SPDX-License-Identifier: 0BSD + +from asyncio import get_event_loop, sleep +from kibicara.platformapi import Censor, Spawner, Message +from kibicara.platforms.mastodon.model import MastodonAccount +from logging import getLogger + +from mastodon import Mastodon, MastodonError +from asyncio import gather +import re + +logger = getLogger(__name__) + + +class MastodonBot(Censor): + def __init__(self, mastodon_account_model): + super().__init__(mastodon_account_model.hood) + self.model = mastodon_account_model + self.enabled = self.model.enabled + self.polling_interval_sec = 60 + + @classmethod + async def destroy_hood(cls, hood): + """Removes all its database entries.""" + for mastodon in await MastodonAccount.objects.filter(hood=hood).all(): + await mastodon.delete() + + async def run(self): + try: + await self.model.instance.load() + self.account = Mastodon( + client_id=self.model.instance.client_id, + client_secret=self.model.instance.client_secret, + api_base_url=self.model.instance.name, + access_token=self.model.access_token, + ) + account_details = await get_event_loop().run_in_executor( + None, self.account.account_verify_credentials + ) + if username := account_details.get("username"): + await self.model.update(username=username) + await gather(self.poll(), self.push()) + except Exception as e: + logger.debug("Bot {0} threw an Error: {1}".format(self.model.hood.name, e)) + finally: + logger.debug("Bot {0} stopped.".format(self.model.hood.name)) + + async def poll(self): + """Get new mentions and DMs from Mastodon""" + while True: + try: + notifications = await get_event_loop().run_in_executor( + None, self.account.notifications + ) + except MastodonError as e: + logger.warning("%s in hood %s" % (e, self.model.hood.name)) + continue + for status in notifications: + try: + status_id = int(status["status"]["id"]) + except KeyError: + self.account.notifications_dismiss(status["id"]) + continue # ignore notifications which don't have a status + text = re.sub(r"<[^>]*>", "", status["status"]["content"]) + text = re.sub( + "(?<=^|(?<=[^a-zA-Z0-9-_.]))@([A-Za-z]+[A-Za-z0-9-_]+)", "", text + ) + logger.debug( + "Mastodon in %s received toot #%s: %s" + % (self.model.hood.name, status_id, text) + ) + if status["status"]["visibility"] == "public": + await self.publish(Message(text, toot_id=status_id)) + else: + await self.publish(Message(text)) + await get_event_loop().run_in_executor( + None, self.account.notifications_dismiss, status["id"] + ) + await sleep(self.polling_interval_sec) + + async def push(self): + """Push new Ticketfrei reports to Mastodon; if source is mastodon, boost it.""" + while True: + message = await self.receive() + if hasattr(message, "toot_id"): + logger.debug("Boosting post %s: %s" % (message.toot_id, message.text)) + await get_event_loop().run_in_executor( + None, self.account.status_reblog, message.toot_id + ) + else: + logger.debug("Posting message: %s" % (message.text,)) + await get_event_loop().run_in_executor( + None, self.account.status_post, message.text + ) + + +spawner = Spawner(MastodonAccount, MastodonBot) diff --git a/backend/src/kibicara/platforms/mastodon/model.py b/backend/src/kibicara/platforms/mastodon/model.py new file mode 100644 index 0000000..a6a1d34 --- /dev/null +++ b/backend/src/kibicara/platforms/mastodon/model.py @@ -0,0 +1,30 @@ +# Copyright (C) 2020 by Thomas Lindner +# Copyright (C) 2020 by Martin Rey +# +# SPDX-License-Identifier: 0BSD + +from ormantic import ForeignKey, Integer, Text, Boolean, Model + +from kibicara.model import Hood, Mapping + + +class MastodonInstance(Model): + id: Integer(primary_key=True) = None + name: Text() + client_id: Text() + client_secret: Text() + + class Mapping(Mapping): + table_name = "mastodoninstances" + + +class MastodonAccount(Model): + id: Integer(primary_key=True) = None + hood: ForeignKey(Hood) + instance: ForeignKey(MastodonInstance) + access_token: Text() + username: Text(allow_null=True) = None + enabled: Boolean() = False + + class Mapping(Mapping): + table_name = "mastodonaccounts" diff --git a/backend/src/kibicara/platforms/mastodon/webapi.py b/backend/src/kibicara/platforms/mastodon/webapi.py new file mode 100644 index 0000000..c10ce44 --- /dev/null +++ b/backend/src/kibicara/platforms/mastodon/webapi.py @@ -0,0 +1,184 @@ +# Copyright (C) 2020 by Cathy Hu +# Copyright (C) 2020 by Martin Rey +# +# SPDX-License-Identifier: 0BSD + +from asyncio import get_event_loop +from fastapi import APIRouter, Depends, HTTPException, Response, status +from ormantic.exceptions import NoMatch +from pydantic import BaseModel, validate_email, validator +from sqlite3 import IntegrityError + +from kibicara.config import config +from kibicara.platforms.mastodon.bot import spawner +from kibicara.platforms.mastodon.model import MastodonAccount, MastodonInstance +from kibicara.webapi.hoods import get_hood, get_hood_unauthorized + +from mastodon import Mastodon, MastodonNetworkError +from mastodon.errors import MastodonIllegalArgumentError + +from logging import getLogger + +logger = getLogger(__name__) + + +class BodyMastodonPublic(BaseModel): + username: str + instance: str + + +class BodyMastodonAccount(BaseModel): + email: str + instance_url: str + password: str + + @validator("email") + def validate_email(cls, value): + return validate_email(value) + + +async def get_mastodon(mastodon_id, hood=Depends(get_hood)): + try: + return await MastodonAccount.objects.get(id=mastodon_id, hood=hood) + except NoMatch: + raise HTTPException(status_code=status.HTTP_404_NOT_FOUND) + + +async def get_mastodon_instance(instance_url: str) -> MastodonInstance: + """Return a MastodonInstance ORM object with valid client_id and client_secret. + + :param: instance_url: the API base URL of the mastodon server + :return the MastodonInstance ORM object + """ + try: + return await MastodonInstance.objects.get(name=instance_url) + except NoMatch: + app_name = config.get("frontend_url") + client_id, client_secret = Mastodon.create_app( + app_name, api_base_url=instance_url + ) + await MastodonInstance.objects.create( + name=instance_url, client_id=client_id, client_secret=client_secret + ) + return await MastodonInstance.objects.get(name=instance_url) + + +router = APIRouter() +twitter_callback_router = APIRouter() + + +@router.get( + "/public", + # TODO response_model, + operation_id="get_mastodons_public", +) +async def mastodon_read_all_public(hood=Depends(get_hood_unauthorized)): + mastodonbots = await MastodonAccount.objects.filter(hood=hood).all() + mbots = [] + for mbot in mastodonbots: + if mbot.enabled == 1 and mbot.username: + instance = await MastodonInstance.objects.get(id=mbot.instance) + mbots.append( + BodyMastodonPublic(username=mbot.username, instance=instance.name) + ) + return mbots + + +@router.get( + "/", + # TODO response_model, + operation_id="get_mastodons", +) +async def mastodon_read_all(hood=Depends(get_hood)): + return await MastodonAccount.objects.filter(hood=hood).all() + + +@router.delete( + "/{mastodon_id}", + status_code=status.HTTP_204_NO_CONTENT, + # TODO response_model + operation_id="delete_mastodon", +) +async def mastodon_delete(mastodon=Depends(get_mastodon)): + spawner.stop(mastodon) + await mastodon.instance.load() + object_with_instance = await MastodonAccount.objects.filter( + instance=mastodon.instance + ).all() + if len(object_with_instance) == 1 and object_with_instance[0] == mastodon: + await mastodon.instance.delete() + await mastodon.delete() + return Response(status_code=status.HTTP_204_NO_CONTENT) + + +@router.get( + "/{mastodon_id}/status", + status_code=status.HTTP_200_OK, + # TODO response_model + operation_id="status_mastodon", +) +async def mastodon_status(mastodon=Depends(get_mastodon)): + return {"status": spawner.get(mastodon).status.name} + + +@router.post( + "/{mastodon_id}/start", + status_code=status.HTTP_200_OK, + # TODO response_model + operation_id="start_mastodon", +) +async def mastodon_start(mastodon=Depends(get_mastodon)): + await mastodon.update(enabled=True) + spawner.get(mastodon).start() + return {} + + +@router.post( + "/{mastodon_id}/stop", + status_code=status.HTTP_200_OK, + # TODO response_model + operation_id="stop_mastodon", +) +async def mastodon_stop(mastodon=Depends(get_mastodon)): + await mastodon.update(enabled=False) + spawner.get(mastodon).stop() + return {} + + +@router.post( + "/", + status_code=status.HTTP_201_CREATED, + # TODO response_model + operation_id="create_mastodon", +) +async def mastodon_create(values: BodyMastodonAccount, hood=Depends(get_hood)): + """Add a Mastodon Account to a Ticketfrei account. + + open questions: + can the instance_url have different ways of writing? + + :param: values: a BodyMastodonAccount object in json + :param: hood: the hood ORM object + """ + try: + instance = await get_mastodon_instance(values.instance_url) + except MastodonNetworkError: + raise HTTPException(422, "Invalid Mastodon Instance") + account = Mastodon( + instance.client_id, instance.client_secret, api_base_url=values.instance_url + ) + try: + access_token = await get_event_loop().run_in_executor( + None, account.log_in, values.email, values.password + ) + logger.debug(f"{access_token}") + mastodon = await MastodonAccount.objects.create( + hood=hood, instance=instance, access_token=access_token, enabled=True + ) + spawner.start(mastodon) + return mastodon + except MastodonIllegalArgumentError: + logger.warning("Login to Mastodon failed.", exc_info=True) + raise HTTPException(status_code=status.HTTP_422_INVALID_INPUT) + except IntegrityError: + raise HTTPException(status_code=status.HTTP_409_CONFLICT) diff --git a/backend/src/kibicara/webapi/__init__.py b/backend/src/kibicara/webapi/__init__.py index 297ac3e..af769c3 100644 --- a/backend/src/kibicara/webapi/__init__.py +++ b/backend/src/kibicara/webapi/__init__.py @@ -15,6 +15,7 @@ from fastapi import APIRouter from kibicara.platforms.email.webapi import router as email_router from kibicara.platforms.telegram.webapi import router as telegram_router from kibicara.platforms.test.webapi import router as test_router +from kibicara.platforms.mastodon.webapi import router as mastodon_router from kibicara.platforms.twitter.webapi import router as twitter_router from kibicara.platforms.twitter.webapi import twitter_callback_router from kibicara.webapi.admin import router as admin_router @@ -37,6 +38,9 @@ hoods_router.include_router( hoods_router.include_router( twitter_router, prefix="/{hood_id}/twitter", tags=["twitter"] ) +hoods_router.include_router( + mastodon_router, prefix="/{hood_id}/mastodon", tags=["mastodon"] +) router.include_router(twitter_callback_router, prefix="/twitter", tags=["twitter"]) hoods_router.include_router(email_router, prefix="/{hood_id}/email", tags=["email"]) router.include_router(hoods_router, prefix="/hoods") diff --git a/backend/tests/tests_mastodon/conftest.py b/backend/tests/tests_mastodon/conftest.py new file mode 100644 index 0000000..ea31bbb --- /dev/null +++ b/backend/tests/tests_mastodon/conftest.py @@ -0,0 +1,34 @@ +# Copyright (C) 2020 by Cathy Hu +# Copyright (C) 2020 by Martin Rey +# +# SPDX-License-Identifier: 0BSD + +from pytest import fixture + +from kibicara.model import Hood +from kibicara.platforms.mastodon.model import MastodonAccount, MastodonInstance + + +@fixture(scope="function") +def mastodon_instance(event_loop): + return event_loop.run_until_complete( + MastodonInstance.objects.create( + name="inst4nce", + client_id="cl13nt_id", + client_secret="cl13nt_s3cr3t", + ) + ) + + +@fixture(scope="function") +def mastodon_account(event_loop, hood_id, mastodon_instance): + hood = event_loop.run_until_complete(Hood.objects.get(id=hood_id)) + return event_loop.run_until_complete( + MastodonAccount.objects.create( + hood=hood, + instance=mastodon_instance, + access_token="t0k3n", + enabled=True, + username="us3r", + ) + ) diff --git a/backend/tests/tests_mastodon/test_api_mastodon_create_bot.py b/backend/tests/tests_mastodon/test_api_mastodon_create_bot.py new file mode 100644 index 0000000..f5a885f --- /dev/null +++ b/backend/tests/tests_mastodon/test_api_mastodon_create_bot.py @@ -0,0 +1,106 @@ +# Copyright (C) 2020 by Cathy Hu +# Copyright (C) 2020 by Martin Rey +# +# SPDX-License-Identifier: 0BSD + +from fastapi import status +from pytest import fixture, mark +from mastodon.Mastodon import Mastodon + +from kibicara.platforms import mastodon +from kibicara.platforms.mastodon.model import MastodonAccount, MastodonInstance + + +@fixture(scope="function") +def disable_spawner(monkeypatch): + class DoNothing: + def start(self, bot): + assert bot is not None + + monkeypatch.setattr(mastodon.webapi, "spawner", DoNothing()) + + +@mark.parametrize( + "body", + [ + { + "instance_url": "botsin.space", + "email": "test@example.org", + "password": "string", + } + ], +) +def test_mastodon_create_bot( + event_loop, + client, + disable_spawner, + hood_id, + auth_header, + monkeypatch, + body, +): + def log_in_mock(self, username, password): + return "acc3ss_t0ken" + + monkeypatch.setattr(Mastodon, "log_in", log_in_mock) + + response = client.post( + "/api/hoods/{0}/mastodon/".format(hood_id), + json=body, + headers=auth_header, + ) + print(response.json()) + assert response.status_code == status.HTTP_201_CREATED + bot_id = response.json()["id"] + mastodon_obj = event_loop.run_until_complete(MastodonAccount.objects.get(id=bot_id)) + assert response.json()["access_token"] == mastodon_obj.access_token + mastodon_instance = event_loop.run_until_complete( + MastodonInstance.objects.get(id=mastodon_obj.instance.id) + ) + assert ( + response.json()["instance"]["name"] + == body["instance_url"] + == mastodon_instance.name + ) + assert response.json()["hood"]["id"] == mastodon_obj.hood.id + assert response.json()["instance"]["client_id"] == mastodon_instance.client_id + assert ( + response.json()["instance"]["client_secret"] == mastodon_instance.client_secret + ) + assert mastodon_obj.enabled + + +@mark.parametrize( + "body", + [ + {"instance_url": "botsin.space", "email": "notanemail", "password": "asdf1234"}, + {"instance_url": "wrong", "email": "asdf@example.org", "password": "asdf1234"}, + ], +) +def test_mastodon_invalid_input( + event_loop, + client, + disable_spawner, + hood_id, + auth_header, + monkeypatch, + body, +): + response = client.post( + "/api/hoods/{0}/mastodon/".format(hood_id), + json=body, + headers=auth_header, + ) + assert response.status_code == status.HTTP_422_UNPROCESSABLE_ENTITY + + +def test_mastodon_create_mastodon_invalid_id(client, auth_header): + response = client.post("/api/hoods/1337/mastodon/", headers=auth_header) + assert response.status_code == status.HTTP_404_NOT_FOUND + response = client.post("/api/hoods/wrong/mastodon/", headers=auth_header) + assert response.status_code == status.HTTP_422_UNPROCESSABLE_ENTITY + + +def test_mastodon_create_unauthorized(client, hood_id): + response = client.post("/api/hoods/{hood_id}/mastodon/") + assert response.status_code == status.HTTP_401_UNAUTHORIZED diff --git a/backend/tests/tests_mastodon/test_api_mastodon_delete_bot.py b/backend/tests/tests_mastodon/test_api_mastodon_delete_bot.py new file mode 100644 index 0000000..1bebac3 --- /dev/null +++ b/backend/tests/tests_mastodon/test_api_mastodon_delete_bot.py @@ -0,0 +1,48 @@ +# Copyright (C) 2020 by Cathy Hu +# Copyright (C) 2020 by Martin Rey +# +# SPDX-License-Identifier: 0BSD + +from fastapi import status +from ormantic.exceptions import NoMatch +from pytest import raises + +from kibicara.platforms.mastodon.model import MastodonAccount + + +def test_mastodon_delete_bot(client, event_loop, mastodon_account, auth_header): + response = client.delete( + "/api/hoods/{0}/mastodon/{1}".format( + mastodon_account.hood.id, mastodon_account.id + ), + headers=auth_header, + ) + assert response.status_code == status.HTTP_204_NO_CONTENT + with raises(NoMatch): + event_loop.run_until_complete( + MastodonAccount.objects.get(id=mastodon_account.id) + ) + + +def test_mastodon_delete_bot_invalid_id(client, auth_header, hood_id): + response = client.delete("/api/hoods/1337/mastodon/123", headers=auth_header) + assert response.status_code == status.HTTP_404_NOT_FOUND + response = client.delete("/api/hoods/wrong/mastodon/123", headers=auth_header) + assert response.status_code == status.HTTP_422_UNPROCESSABLE_ENTITY + response = client.delete( + "/api/hoods/{0}/mastodon/7331".format(hood_id), headers=auth_header + ) + assert response.status_code == status.HTTP_404_NOT_FOUND + response = client.delete( + "/api/hoods/{0}/mastodon/wrong".format(hood_id), headers=auth_header + ) + assert response.status_code == status.HTTP_404_NOT_FOUND + + +def test_mastodon_delete_bot_unauthorized(client, mastodon_account): + response = client.delete( + "/api/hoods/{0}/mastodon/{1}".format( + mastodon_account.hood.id, mastodon_account.id + ) + ) + assert response.status_code == status.HTTP_401_UNAUTHORIZED diff --git a/backend/tests/tests_mastodon/test_api_mastodon_get_bots.py b/backend/tests/tests_mastodon/test_api_mastodon_get_bots.py new file mode 100644 index 0000000..d3da4ac --- /dev/null +++ b/backend/tests/tests_mastodon/test_api_mastodon_get_bots.py @@ -0,0 +1,50 @@ +# Copyright (C) 2020 by Cathy Hu +# Copyright (C) 2020 by Martin Rey +# +# SPDX-License-Identifier: 0BSD + +from fastapi import status + +from kibicara.platforms.mastodon.model import MastodonAccount + + +def test_mastodon_get_bots( + client, auth_header, event_loop, hood_id, mastodon_account, mastodon_instance +): + mastodon2 = event_loop.run_until_complete( + MastodonAccount.objects.create( + hood=mastodon_account.hood, + instance=mastodon_instance, + access_token="4cc3ss", + enabled=True, + username="us4r", + ) + ) + response = client.get( + "/api/hoods/{0}/mastodon".format(mastodon_account.hood.id), headers=auth_header + ) + assert response.status_code == status.HTTP_200_OK + assert response.json()[0]["id"] == mastodon_account.id + assert response.json()[0]["access_token"] == mastodon_account.access_token + assert response.json()[1]["id"] == mastodon2.id + assert response.json()[1]["access_token"] == mastodon2.access_token + + +def test_mastodon_get_bots_invalid_id(client, auth_header, hood_id): + response = client.get("/api/hoods/1337/mastodon", headers=auth_header) + assert response.status_code == status.HTTP_404_NOT_FOUND + response = client.get("/api/hoods/wrong/mastodon", headers=auth_header) + assert response.status_code == status.HTTP_422_UNPROCESSABLE_ENTITY + + +def test_mastodon_get_bots_unauthorized(client, hood_id): + response = client.get("/api/hoods/{0}/mastodon".format(hood_id)) + assert response.status_code == status.HTTP_401_UNAUTHORIZED + + +def test_mastodon_public(client, mastodon_account, mastodon_instance, event_loop): + response = client.get( + "/api/hoods/{0}/mastodon/public".format(mastodon_account.hood.id) + ) + assert response.json()[0]["username"] == mastodon_account.username + assert response.json()[0]["instance"] == mastodon_instance.name diff --git a/frontend/src/app/core/api/.openapi-generator/FILES b/frontend/src/app/core/api/.openapi-generator/FILES index 9505d8c..708d04f 100644 --- a/frontend/src/app/core/api/.openapi-generator/FILES +++ b/frontend/src/app/core/api/.openapi-generator/FILES @@ -6,6 +6,7 @@ api/api.ts api/badwords.service.ts api/email.service.ts api/hoods.service.ts +api/mastodon.service.ts api/telegram.service.ts api/test.service.ts api/triggers.service.ts @@ -16,9 +17,10 @@ git_push.sh index.ts model/bodyAccessToken.ts model/bodyAdmin.ts -model/bodyAdminLoginAdminLoginPost.ts model/bodyBadWord.ts model/bodyHood.ts +model/bodyLogin.ts +model/bodyMastodonAccount.ts model/bodyPassword.ts model/bodySubscriber.ts model/bodyTelegram.ts diff --git a/frontend/src/app/core/api/api.module.ts b/frontend/src/app/core/api/api.module.ts index 606119d..b3a7ddb 100644 --- a/frontend/src/app/core/api/api.module.ts +++ b/frontend/src/app/core/api/api.module.ts @@ -6,6 +6,7 @@ import { AdminService } from './api/admin.service'; import { BadwordsService } from './api/badwords.service'; import { EmailService } from './api/email.service'; import { HoodsService } from './api/hoods.service'; +import { MastodonService } from './api/mastodon.service'; import { TelegramService } from './api/telegram.service'; import { TestService } from './api/test.service'; import { TriggersService } from './api/triggers.service'; diff --git a/frontend/src/app/core/api/api/admin.service.ts b/frontend/src/app/core/api/api/admin.service.ts index ab1b2c9..1556c39 100644 --- a/frontend/src/app/core/api/api/admin.service.ts +++ b/frontend/src/app/core/api/api/admin.service.ts @@ -33,7 +33,7 @@ import { Configuration } from '../configurat }) export class AdminService { - protected basePath = 'http://localhost'; + protected basePath = 'http://localhost/api'; public defaultHeaders = new HttpHeaders(); public configuration = new Configuration(); public encoder: HttpParameterCodec; @@ -256,7 +256,7 @@ export class AdminService { /** * Admin Read - * Get a list of all hoods of a given admin. + * Get a list of all hoods of a given admin. * @param observe set whether or not to return the data Observable as the body, response or events. defaults to returning the body. * @param reportProgress flag to report request and response progress. */ @@ -305,7 +305,7 @@ export class AdminService { /** * Admin Hood Read All - * Get a list of all hoods of a given admin. + * Get a list of all hoods of a given admin. * @param observe set whether or not to return the data Observable as the body, response or events. defaults to returning the body. * @param reportProgress flag to report request and response progress. */ diff --git a/frontend/src/app/core/api/api/api.ts b/frontend/src/app/core/api/api/api.ts index b9c2089..69e9b19 100644 --- a/frontend/src/app/core/api/api/api.ts +++ b/frontend/src/app/core/api/api/api.ts @@ -6,6 +6,8 @@ export * from './email.service'; import { EmailService } from './email.service'; export * from './hoods.service'; import { HoodsService } from './hoods.service'; +export * from './mastodon.service'; +import { MastodonService } from './mastodon.service'; export * from './telegram.service'; import { TelegramService } from './telegram.service'; export * from './test.service'; @@ -14,4 +16,4 @@ export * from './triggers.service'; import { TriggersService } from './triggers.service'; export * from './twitter.service'; import { TwitterService } from './twitter.service'; -export const APIS = [AdminService, BadwordsService, EmailService, HoodsService, TelegramService, TestService, TriggersService, TwitterService]; +export const APIS = [AdminService, BadwordsService, EmailService, HoodsService, MastodonService, TelegramService, TestService, TriggersService, TwitterService]; diff --git a/frontend/src/app/core/api/api/badwords.service.ts b/frontend/src/app/core/api/api/badwords.service.ts index ccc62f2..1959f28 100644 --- a/frontend/src/app/core/api/api/badwords.service.ts +++ b/frontend/src/app/core/api/api/badwords.service.ts @@ -30,7 +30,7 @@ import { Configuration } from '../configurat }) export class BadwordsService { - protected basePath = 'http://localhost'; + protected basePath = 'http://localhost/api'; public defaultHeaders = new HttpHeaders(); public configuration = new Configuration(); public encoder: HttpParameterCodec; @@ -154,7 +154,7 @@ export class BadwordsService { /** * Badword Delete - * Deletes badword with id **badword_id** for hood with id **hood_id**. + * Deletes badword with id **badword_id** for hood with id **hood_id**. * @param badwordId * @param hoodId * @param observe set whether or not to return the data Observable as the body, response or events. defaults to returning the body. @@ -211,7 +211,7 @@ export class BadwordsService { /** * Badword Read - * Reads badword with id **badword_id** for hood with id **hood_id**. + * Reads badword with id **badword_id** for hood with id **hood_id**. * @param badwordId * @param hoodId * @param observe set whether or not to return the data Observable as the body, response or events. defaults to returning the body. @@ -268,7 +268,7 @@ export class BadwordsService { /** * Badword Read All - * Get all badwords of hood with id **hood_id**. + * Get all badwords of hood with id **hood_id**. * @param hoodId * @param observe set whether or not to return the data Observable as the body, response or events. defaults to returning the body. * @param reportProgress flag to report request and response progress. diff --git a/frontend/src/app/core/api/api/email.service.ts b/frontend/src/app/core/api/api/email.service.ts index bd64617..602ddc5 100644 --- a/frontend/src/app/core/api/api/email.service.ts +++ b/frontend/src/app/core/api/api/email.service.ts @@ -32,7 +32,7 @@ import { Configuration } from '../configurat }) export class EmailService { - protected basePath = 'http://localhost'; + protected basePath = 'http://localhost/api'; public defaultHeaders = new HttpHeaders(); public configuration = new Configuration(); public encoder: HttpParameterCodec; @@ -207,7 +207,7 @@ export class EmailService { /** * Email Delete - * Delete an Email bot. Stops and deletes the Email bot. :param hood: Hood the Email bot belongs to. + * Delete an Email bot. Stops and deletes the Email bot. :param hood: Hood the Email bot belongs to. * @param emailId * @param hoodId * @param observe set whether or not to return the data Observable as the body, response or events. defaults to returning the body. diff --git a/frontend/src/app/core/api/api/hoods.service.ts b/frontend/src/app/core/api/api/hoods.service.ts index 51ed5ac..5450566 100644 --- a/frontend/src/app/core/api/api/hoods.service.ts +++ b/frontend/src/app/core/api/api/hoods.service.ts @@ -30,7 +30,7 @@ import { Configuration } from '../configurat }) export class HoodsService { - protected basePath = 'http://localhost'; + protected basePath = 'http://localhost/api'; public defaultHeaders = new HttpHeaders(); public configuration = new Configuration(); public encoder: HttpParameterCodec; @@ -150,7 +150,7 @@ export class HoodsService { /** * Hood Delete - * Deletes hood with id **hood_id**. + * Deletes hood with id **hood_id**. * @param hoodId * @param observe set whether or not to return the data Observable as the body, response or events. defaults to returning the body. * @param reportProgress flag to report request and response progress. @@ -203,7 +203,7 @@ export class HoodsService { /** * Hood Read - * Get hood with id **hood_id**. + * Get hood with id **hood_id**. * @param hoodId * @param observe set whether or not to return the data Observable as the body, response or events. defaults to returning the body. * @param reportProgress flag to report request and response progress. @@ -249,7 +249,7 @@ export class HoodsService { /** * Hood Read All - * Get all existing hoods. + * Get all existing hoods. * @param observe set whether or not to return the data Observable as the body, response or events. defaults to returning the body. * @param reportProgress flag to report request and response progress. */ diff --git a/frontend/src/app/core/api/api/mastodon.service.ts b/frontend/src/app/core/api/api/mastodon.service.ts new file mode 100644 index 0000000..17cc2e5 --- /dev/null +++ b/frontend/src/app/core/api/api/mastodon.service.ts @@ -0,0 +1,534 @@ +/** + * FastAPI + * No description provided (generated by Openapi Generator https://github.com/openapitools/openapi-generator) + * + * The version of the OpenAPI document: 0.1.0 + * + * + * NOTE: This class is auto generated by OpenAPI Generator (https://openapi-generator.tech). + * https://openapi-generator.tech + * Do not edit the class manually. + */ +/* tslint:disable:no-unused-variable member-ordering */ + +import { Inject, Injectable, Optional } from '@angular/core'; +import { HttpClient, HttpHeaders, HttpParams, + HttpResponse, HttpEvent, HttpParameterCodec } from '@angular/common/http'; +import { CustomHttpParameterCodec } from '../encoder'; +import { Observable } from 'rxjs'; + +import { BodyMastodonAccount } from '../model/models'; +import { HTTPValidationError } from '../model/models'; + +import { BASE_PATH, COLLECTION_FORMATS } from '../variables'; +import { Configuration } from '../configuration'; + + + +@Injectable({ + providedIn: 'root' +}) +export class MastodonService { + + protected basePath = 'http://localhost/api'; + public defaultHeaders = new HttpHeaders(); + public configuration = new Configuration(); + public encoder: HttpParameterCodec; + + constructor(protected httpClient: HttpClient, @Optional()@Inject(BASE_PATH) basePath: string, @Optional() configuration: Configuration) { + if (configuration) { + this.configuration = configuration; + } + if (typeof this.configuration.basePath !== 'string') { + if (typeof basePath !== 'string') { + basePath = this.basePath; + } + this.configuration.basePath = basePath; + } + this.encoder = this.configuration.encoder || new CustomHttpParameterCodec(); + } + + + private addToHttpParams(httpParams: HttpParams, value: any, key?: string): HttpParams { + if (typeof value === "object" && value instanceof Date === false) { + httpParams = this.addToHttpParamsRecursive(httpParams, value); + } else { + httpParams = this.addToHttpParamsRecursive(httpParams, value, key); + } + return httpParams; + } + + private addToHttpParamsRecursive(httpParams: HttpParams, value?: any, key?: string): HttpParams { + if (value == null) { + return httpParams; + } + + if (typeof value === "object") { + if (Array.isArray(value)) { + (value as any[]).forEach( elem => httpParams = this.addToHttpParamsRecursive(httpParams, elem, key)); + } else if (value instanceof Date) { + if (key != null) { + httpParams = httpParams.append(key, + (value as Date).toISOString().substr(0, 10)); + } else { + throw Error("key may not be null if value is Date"); + } + } else { + Object.keys(value).forEach( k => httpParams = this.addToHttpParamsRecursive( + httpParams, value[k], key != null ? `${key}.${k}` : k)); + } + } else if (key != null) { + httpParams = httpParams.append(key, value); + } else { + throw Error("key may not be null if value is not object or array"); + } + return httpParams; + } + + /** + * Mastodon Create + * Add a Mastodon Account to a Ticketfrei account. open questions: can the instance_url have different ways of writing? :param: values: a BodyMastodonAccount object in json :param: hood: the hood ORM object + * @param hoodId + * @param bodyMastodonAccount + * @param observe set whether or not to return the data Observable as the body, response or events. defaults to returning the body. + * @param reportProgress flag to report request and response progress. + */ + public createMastodon(hoodId: number, bodyMastodonAccount: BodyMastodonAccount, observe?: 'body', reportProgress?: boolean, options?: {httpHeaderAccept?: 'application/json'}): Observable; + public createMastodon(hoodId: number, bodyMastodonAccount: BodyMastodonAccount, observe?: 'response', reportProgress?: boolean, options?: {httpHeaderAccept?: 'application/json'}): Observable>; + public createMastodon(hoodId: number, bodyMastodonAccount: BodyMastodonAccount, observe?: 'events', reportProgress?: boolean, options?: {httpHeaderAccept?: 'application/json'}): Observable>; + public createMastodon(hoodId: number, bodyMastodonAccount: BodyMastodonAccount, observe: any = 'body', reportProgress: boolean = false, options?: {httpHeaderAccept?: 'application/json'}): Observable { + if (hoodId === null || hoodId === undefined) { + throw new Error('Required parameter hoodId was null or undefined when calling createMastodon.'); + } + if (bodyMastodonAccount === null || bodyMastodonAccount === undefined) { + throw new Error('Required parameter bodyMastodonAccount was null or undefined when calling createMastodon.'); + } + + let headers = this.defaultHeaders; + + let credential: string | undefined; + // authentication (OAuth2PasswordBearer) required + credential = this.configuration.lookupCredential('OAuth2PasswordBearer'); + if (credential) { + headers = headers.set('Authorization', 'Bearer ' + credential); + } + + let httpHeaderAcceptSelected: string | undefined = options && options.httpHeaderAccept; + if (httpHeaderAcceptSelected === undefined) { + // to determine the Accept header + const httpHeaderAccepts: string[] = [ + 'application/json' + ]; + httpHeaderAcceptSelected = this.configuration.selectHeaderAccept(httpHeaderAccepts); + } + if (httpHeaderAcceptSelected !== undefined) { + headers = headers.set('Accept', httpHeaderAcceptSelected); + } + + + // to determine the Content-Type header + const consumes: string[] = [ + 'application/json' + ]; + const httpContentTypeSelected: string | undefined = this.configuration.selectHeaderContentType(consumes); + if (httpContentTypeSelected !== undefined) { + headers = headers.set('Content-Type', httpContentTypeSelected); + } + + let responseType: 'text' | 'json' = 'json'; + if(httpHeaderAcceptSelected && httpHeaderAcceptSelected.startsWith('text')) { + responseType = 'text'; + } + + return this.httpClient.post(`${this.configuration.basePath}/api/hoods/${encodeURIComponent(String(hoodId))}/mastodon/`, + bodyMastodonAccount, + { + responseType: responseType, + withCredentials: this.configuration.withCredentials, + headers: headers, + observe: observe, + reportProgress: reportProgress + } + ); + } + + /** + * Mastodon Delete + * @param mastodonId + * @param hoodId + * @param observe set whether or not to return the data Observable as the body, response or events. defaults to returning the body. + * @param reportProgress flag to report request and response progress. + */ + public deleteMastodon(mastodonId: any, hoodId: number, observe?: 'body', reportProgress?: boolean, options?: {httpHeaderAccept?: 'application/json'}): Observable; + public deleteMastodon(mastodonId: any, hoodId: number, observe?: 'response', reportProgress?: boolean, options?: {httpHeaderAccept?: 'application/json'}): Observable>; + public deleteMastodon(mastodonId: any, hoodId: number, observe?: 'events', reportProgress?: boolean, options?: {httpHeaderAccept?: 'application/json'}): Observable>; + public deleteMastodon(mastodonId: any, hoodId: number, observe: any = 'body', reportProgress: boolean = false, options?: {httpHeaderAccept?: 'application/json'}): Observable { + if (mastodonId === null || mastodonId === undefined) { + throw new Error('Required parameter mastodonId was null or undefined when calling deleteMastodon.'); + } + if (hoodId === null || hoodId === undefined) { + throw new Error('Required parameter hoodId was null or undefined when calling deleteMastodon.'); + } + + let headers = this.defaultHeaders; + + let credential: string | undefined; + // authentication (OAuth2PasswordBearer) required + credential = this.configuration.lookupCredential('OAuth2PasswordBearer'); + if (credential) { + headers = headers.set('Authorization', 'Bearer ' + credential); + } + + let httpHeaderAcceptSelected: string | undefined = options && options.httpHeaderAccept; + if (httpHeaderAcceptSelected === undefined) { + // to determine the Accept header + const httpHeaderAccepts: string[] = [ + 'application/json' + ]; + httpHeaderAcceptSelected = this.configuration.selectHeaderAccept(httpHeaderAccepts); + } + if (httpHeaderAcceptSelected !== undefined) { + headers = headers.set('Accept', httpHeaderAcceptSelected); + } + + + let responseType: 'text' | 'json' = 'json'; + if(httpHeaderAcceptSelected && httpHeaderAcceptSelected.startsWith('text')) { + responseType = 'text'; + } + + return this.httpClient.delete(`${this.configuration.basePath}/api/hoods/${encodeURIComponent(String(hoodId))}/mastodon/${encodeURIComponent(String(mastodonId))}`, + { + responseType: responseType, + withCredentials: this.configuration.withCredentials, + headers: headers, + observe: observe, + reportProgress: reportProgress + } + ); + } + + /** + * Mastodon Read + * @param mastodonId + * @param hoodId + * @param observe set whether or not to return the data Observable as the body, response or events. defaults to returning the body. + * @param reportProgress flag to report request and response progress. + */ + public getMastodon(mastodonId: any, hoodId: number, observe?: 'body', reportProgress?: boolean, options?: {httpHeaderAccept?: 'application/json'}): Observable; + public getMastodon(mastodonId: any, hoodId: number, observe?: 'response', reportProgress?: boolean, options?: {httpHeaderAccept?: 'application/json'}): Observable>; + public getMastodon(mastodonId: any, hoodId: number, observe?: 'events', reportProgress?: boolean, options?: {httpHeaderAccept?: 'application/json'}): Observable>; + public getMastodon(mastodonId: any, hoodId: number, observe: any = 'body', reportProgress: boolean = false, options?: {httpHeaderAccept?: 'application/json'}): Observable { + if (mastodonId === null || mastodonId === undefined) { + throw new Error('Required parameter mastodonId was null or undefined when calling getMastodon.'); + } + if (hoodId === null || hoodId === undefined) { + throw new Error('Required parameter hoodId was null or undefined when calling getMastodon.'); + } + + let headers = this.defaultHeaders; + + let credential: string | undefined; + // authentication (OAuth2PasswordBearer) required + credential = this.configuration.lookupCredential('OAuth2PasswordBearer'); + if (credential) { + headers = headers.set('Authorization', 'Bearer ' + credential); + } + + let httpHeaderAcceptSelected: string | undefined = options && options.httpHeaderAccept; + if (httpHeaderAcceptSelected === undefined) { + // to determine the Accept header + const httpHeaderAccepts: string[] = [ + 'application/json' + ]; + httpHeaderAcceptSelected = this.configuration.selectHeaderAccept(httpHeaderAccepts); + } + if (httpHeaderAcceptSelected !== undefined) { + headers = headers.set('Accept', httpHeaderAcceptSelected); + } + + + let responseType: 'text' | 'json' = 'json'; + if(httpHeaderAcceptSelected && httpHeaderAcceptSelected.startsWith('text')) { + responseType = 'text'; + } + + return this.httpClient.get(`${this.configuration.basePath}/api/hoods/${encodeURIComponent(String(hoodId))}/mastodon/${encodeURIComponent(String(mastodonId))}`, + { + responseType: responseType, + withCredentials: this.configuration.withCredentials, + headers: headers, + observe: observe, + reportProgress: reportProgress + } + ); + } + + /** + * Mastodon Read All + * @param hoodId + * @param observe set whether or not to return the data Observable as the body, response or events. defaults to returning the body. + * @param reportProgress flag to report request and response progress. + */ + public getMastodons(hoodId: number, observe?: 'body', reportProgress?: boolean, options?: {httpHeaderAccept?: 'application/json'}): Observable; + public getMastodons(hoodId: number, observe?: 'response', reportProgress?: boolean, options?: {httpHeaderAccept?: 'application/json'}): Observable>; + public getMastodons(hoodId: number, observe?: 'events', reportProgress?: boolean, options?: {httpHeaderAccept?: 'application/json'}): Observable>; + public getMastodons(hoodId: number, observe: any = 'body', reportProgress: boolean = false, options?: {httpHeaderAccept?: 'application/json'}): Observable { + if (hoodId === null || hoodId === undefined) { + throw new Error('Required parameter hoodId was null or undefined when calling getMastodons.'); + } + + let headers = this.defaultHeaders; + + let credential: string | undefined; + // authentication (OAuth2PasswordBearer) required + credential = this.configuration.lookupCredential('OAuth2PasswordBearer'); + if (credential) { + headers = headers.set('Authorization', 'Bearer ' + credential); + } + + let httpHeaderAcceptSelected: string | undefined = options && options.httpHeaderAccept; + if (httpHeaderAcceptSelected === undefined) { + // to determine the Accept header + const httpHeaderAccepts: string[] = [ + 'application/json' + ]; + httpHeaderAcceptSelected = this.configuration.selectHeaderAccept(httpHeaderAccepts); + } + if (httpHeaderAcceptSelected !== undefined) { + headers = headers.set('Accept', httpHeaderAcceptSelected); + } + + + let responseType: 'text' | 'json' = 'json'; + if(httpHeaderAcceptSelected && httpHeaderAcceptSelected.startsWith('text')) { + responseType = 'text'; + } + + return this.httpClient.get(`${this.configuration.basePath}/api/hoods/${encodeURIComponent(String(hoodId))}/mastodon/`, + { + responseType: responseType, + withCredentials: this.configuration.withCredentials, + headers: headers, + observe: observe, + reportProgress: reportProgress + } + ); + } + + /** + * Mastodon Read All Public + * @param hoodId + * @param observe set whether or not to return the data Observable as the body, response or events. defaults to returning the body. + * @param reportProgress flag to report request and response progress. + */ + public getMastodonsPublic(hoodId: number, observe?: 'body', reportProgress?: boolean, options?: {httpHeaderAccept?: 'application/json'}): Observable; + public getMastodonsPublic(hoodId: number, observe?: 'response', reportProgress?: boolean, options?: {httpHeaderAccept?: 'application/json'}): Observable>; + public getMastodonsPublic(hoodId: number, observe?: 'events', reportProgress?: boolean, options?: {httpHeaderAccept?: 'application/json'}): Observable>; + public getMastodonsPublic(hoodId: number, observe: any = 'body', reportProgress: boolean = false, options?: {httpHeaderAccept?: 'application/json'}): Observable { + if (hoodId === null || hoodId === undefined) { + throw new Error('Required parameter hoodId was null or undefined when calling getMastodonsPublic.'); + } + + let headers = this.defaultHeaders; + + let httpHeaderAcceptSelected: string | undefined = options && options.httpHeaderAccept; + if (httpHeaderAcceptSelected === undefined) { + // to determine the Accept header + const httpHeaderAccepts: string[] = [ + 'application/json' + ]; + httpHeaderAcceptSelected = this.configuration.selectHeaderAccept(httpHeaderAccepts); + } + if (httpHeaderAcceptSelected !== undefined) { + headers = headers.set('Accept', httpHeaderAcceptSelected); + } + + + let responseType: 'text' | 'json' = 'json'; + if(httpHeaderAcceptSelected && httpHeaderAcceptSelected.startsWith('text')) { + responseType = 'text'; + } + + return this.httpClient.get(`${this.configuration.basePath}/api/hoods/${encodeURIComponent(String(hoodId))}/mastodon/public`, + { + responseType: responseType, + withCredentials: this.configuration.withCredentials, + headers: headers, + observe: observe, + reportProgress: reportProgress + } + ); + } + + /** + * Mastodon Start + * @param mastodonId + * @param hoodId + * @param observe set whether or not to return the data Observable as the body, response or events. defaults to returning the body. + * @param reportProgress flag to report request and response progress. + */ + public startMastodon(mastodonId: any, hoodId: number, observe?: 'body', reportProgress?: boolean, options?: {httpHeaderAccept?: 'application/json'}): Observable; + public startMastodon(mastodonId: any, hoodId: number, observe?: 'response', reportProgress?: boolean, options?: {httpHeaderAccept?: 'application/json'}): Observable>; + public startMastodon(mastodonId: any, hoodId: number, observe?: 'events', reportProgress?: boolean, options?: {httpHeaderAccept?: 'application/json'}): Observable>; + public startMastodon(mastodonId: any, hoodId: number, observe: any = 'body', reportProgress: boolean = false, options?: {httpHeaderAccept?: 'application/json'}): Observable { + if (mastodonId === null || mastodonId === undefined) { + throw new Error('Required parameter mastodonId was null or undefined when calling startMastodon.'); + } + if (hoodId === null || hoodId === undefined) { + throw new Error('Required parameter hoodId was null or undefined when calling startMastodon.'); + } + + let headers = this.defaultHeaders; + + let credential: string | undefined; + // authentication (OAuth2PasswordBearer) required + credential = this.configuration.lookupCredential('OAuth2PasswordBearer'); + if (credential) { + headers = headers.set('Authorization', 'Bearer ' + credential); + } + + let httpHeaderAcceptSelected: string | undefined = options && options.httpHeaderAccept; + if (httpHeaderAcceptSelected === undefined) { + // to determine the Accept header + const httpHeaderAccepts: string[] = [ + 'application/json' + ]; + httpHeaderAcceptSelected = this.configuration.selectHeaderAccept(httpHeaderAccepts); + } + if (httpHeaderAcceptSelected !== undefined) { + headers = headers.set('Accept', httpHeaderAcceptSelected); + } + + + let responseType: 'text' | 'json' = 'json'; + if(httpHeaderAcceptSelected && httpHeaderAcceptSelected.startsWith('text')) { + responseType = 'text'; + } + + return this.httpClient.post(`${this.configuration.basePath}/api/hoods/${encodeURIComponent(String(hoodId))}/mastodon/${encodeURIComponent(String(mastodonId))}/start`, + null, + { + responseType: responseType, + withCredentials: this.configuration.withCredentials, + headers: headers, + observe: observe, + reportProgress: reportProgress + } + ); + } + + /** + * Mastodon Status + * @param mastodonId + * @param hoodId + * @param observe set whether or not to return the data Observable as the body, response or events. defaults to returning the body. + * @param reportProgress flag to report request and response progress. + */ + public statusMastodon(mastodonId: any, hoodId: number, observe?: 'body', reportProgress?: boolean, options?: {httpHeaderAccept?: 'application/json'}): Observable; + public statusMastodon(mastodonId: any, hoodId: number, observe?: 'response', reportProgress?: boolean, options?: {httpHeaderAccept?: 'application/json'}): Observable>; + public statusMastodon(mastodonId: any, hoodId: number, observe?: 'events', reportProgress?: boolean, options?: {httpHeaderAccept?: 'application/json'}): Observable>; + public statusMastodon(mastodonId: any, hoodId: number, observe: any = 'body', reportProgress: boolean = false, options?: {httpHeaderAccept?: 'application/json'}): Observable { + if (mastodonId === null || mastodonId === undefined) { + throw new Error('Required parameter mastodonId was null or undefined when calling statusMastodon.'); + } + if (hoodId === null || hoodId === undefined) { + throw new Error('Required parameter hoodId was null or undefined when calling statusMastodon.'); + } + + let headers = this.defaultHeaders; + + let credential: string | undefined; + // authentication (OAuth2PasswordBearer) required + credential = this.configuration.lookupCredential('OAuth2PasswordBearer'); + if (credential) { + headers = headers.set('Authorization', 'Bearer ' + credential); + } + + let httpHeaderAcceptSelected: string | undefined = options && options.httpHeaderAccept; + if (httpHeaderAcceptSelected === undefined) { + // to determine the Accept header + const httpHeaderAccepts: string[] = [ + 'application/json' + ]; + httpHeaderAcceptSelected = this.configuration.selectHeaderAccept(httpHeaderAccepts); + } + if (httpHeaderAcceptSelected !== undefined) { + headers = headers.set('Accept', httpHeaderAcceptSelected); + } + + + let responseType: 'text' | 'json' = 'json'; + if(httpHeaderAcceptSelected && httpHeaderAcceptSelected.startsWith('text')) { + responseType = 'text'; + } + + return this.httpClient.get(`${this.configuration.basePath}/api/hoods/${encodeURIComponent(String(hoodId))}/mastodon/${encodeURIComponent(String(mastodonId))}/status`, + { + responseType: responseType, + withCredentials: this.configuration.withCredentials, + headers: headers, + observe: observe, + reportProgress: reportProgress + } + ); + } + + /** + * Mastodon Stop + * @param mastodonId + * @param hoodId + * @param observe set whether or not to return the data Observable as the body, response or events. defaults to returning the body. + * @param reportProgress flag to report request and response progress. + */ + public stopMastodon(mastodonId: any, hoodId: number, observe?: 'body', reportProgress?: boolean, options?: {httpHeaderAccept?: 'application/json'}): Observable; + public stopMastodon(mastodonId: any, hoodId: number, observe?: 'response', reportProgress?: boolean, options?: {httpHeaderAccept?: 'application/json'}): Observable>; + public stopMastodon(mastodonId: any, hoodId: number, observe?: 'events', reportProgress?: boolean, options?: {httpHeaderAccept?: 'application/json'}): Observable>; + public stopMastodon(mastodonId: any, hoodId: number, observe: any = 'body', reportProgress: boolean = false, options?: {httpHeaderAccept?: 'application/json'}): Observable { + if (mastodonId === null || mastodonId === undefined) { + throw new Error('Required parameter mastodonId was null or undefined when calling stopMastodon.'); + } + if (hoodId === null || hoodId === undefined) { + throw new Error('Required parameter hoodId was null or undefined when calling stopMastodon.'); + } + + let headers = this.defaultHeaders; + + let credential: string | undefined; + // authentication (OAuth2PasswordBearer) required + credential = this.configuration.lookupCredential('OAuth2PasswordBearer'); + if (credential) { + headers = headers.set('Authorization', 'Bearer ' + credential); + } + + let httpHeaderAcceptSelected: string | undefined = options && options.httpHeaderAccept; + if (httpHeaderAcceptSelected === undefined) { + // to determine the Accept header + const httpHeaderAccepts: string[] = [ + 'application/json' + ]; + httpHeaderAcceptSelected = this.configuration.selectHeaderAccept(httpHeaderAccepts); + } + if (httpHeaderAcceptSelected !== undefined) { + headers = headers.set('Accept', httpHeaderAcceptSelected); + } + + + let responseType: 'text' | 'json' = 'json'; + if(httpHeaderAcceptSelected && httpHeaderAcceptSelected.startsWith('text')) { + responseType = 'text'; + } + + return this.httpClient.post(`${this.configuration.basePath}/api/hoods/${encodeURIComponent(String(hoodId))}/mastodon/${encodeURIComponent(String(mastodonId))}/stop`, + null, + { + responseType: responseType, + withCredentials: this.configuration.withCredentials, + headers: headers, + observe: observe, + reportProgress: reportProgress + } + ); + } + +} diff --git a/frontend/src/app/core/api/api/telegram.service.ts b/frontend/src/app/core/api/api/telegram.service.ts index 6bb2672..ef0ec80 100644 --- a/frontend/src/app/core/api/api/telegram.service.ts +++ b/frontend/src/app/core/api/api/telegram.service.ts @@ -30,7 +30,7 @@ import { Configuration } from '../configurat }) export class TelegramService { - protected basePath = 'http://localhost'; + protected basePath = 'http://localhost/api'; public defaultHeaders = new HttpHeaders(); public configuration = new Configuration(); public encoder: HttpParameterCodec; diff --git a/frontend/src/app/core/api/api/test.service.ts b/frontend/src/app/core/api/api/test.service.ts index 1a148e3..33c065d 100644 --- a/frontend/src/app/core/api/api/test.service.ts +++ b/frontend/src/app/core/api/api/test.service.ts @@ -30,7 +30,7 @@ import { Configuration } from '../configurat }) export class TestService { - protected basePath = 'http://localhost'; + protected basePath = 'http://localhost/api'; public defaultHeaders = new HttpHeaders(); public configuration = new Configuration(); public encoder: HttpParameterCodec; diff --git a/frontend/src/app/core/api/api/triggers.service.ts b/frontend/src/app/core/api/api/triggers.service.ts index cd8dffd..f72c5c8 100644 --- a/frontend/src/app/core/api/api/triggers.service.ts +++ b/frontend/src/app/core/api/api/triggers.service.ts @@ -30,7 +30,7 @@ import { Configuration } from '../configurat }) export class TriggersService { - protected basePath = 'http://localhost'; + protected basePath = 'http://localhost/api'; public defaultHeaders = new HttpHeaders(); public configuration = new Configuration(); public encoder: HttpParameterCodec; @@ -154,7 +154,7 @@ export class TriggersService { /** * Trigger Delete - * Deletes trigger with id **trigger_id** for hood with id **hood_id**. + * Deletes trigger with id **trigger_id** for hood with id **hood_id**. * @param triggerId * @param hoodId * @param observe set whether or not to return the data Observable as the body, response or events. defaults to returning the body. @@ -211,7 +211,7 @@ export class TriggersService { /** * Trigger Read - * Reads trigger with id **trigger_id** for hood with id **hood_id**. + * Reads trigger with id **trigger_id** for hood with id **hood_id**. * @param triggerId * @param hoodId * @param observe set whether or not to return the data Observable as the body, response or events. defaults to returning the body. @@ -268,7 +268,7 @@ export class TriggersService { /** * Trigger Read All - * Get all triggers of hood with id **hood_id**. + * Get all triggers of hood with id **hood_id**. * @param hoodId * @param observe set whether or not to return the data Observable as the body, response or events. defaults to returning the body. * @param reportProgress flag to report request and response progress. diff --git a/frontend/src/app/core/api/api/twitter.service.ts b/frontend/src/app/core/api/api/twitter.service.ts index e91fb01..b686fe9 100644 --- a/frontend/src/app/core/api/api/twitter.service.ts +++ b/frontend/src/app/core/api/api/twitter.service.ts @@ -29,7 +29,7 @@ import { Configuration } from '../configurat }) export class TwitterService { - protected basePath = 'http://localhost'; + protected basePath = 'http://localhost/api'; public defaultHeaders = new HttpHeaders(); public configuration = new Configuration(); public encoder: HttpParameterCodec; diff --git a/frontend/src/app/core/api/model/bodyLogin.ts b/frontend/src/app/core/api/model/bodyLogin.ts new file mode 100644 index 0000000..a98113b --- /dev/null +++ b/frontend/src/app/core/api/model/bodyLogin.ts @@ -0,0 +1,22 @@ +/** + * FastAPI + * No description provided (generated by Openapi Generator https://github.com/openapitools/openapi-generator) + * + * The version of the OpenAPI document: 0.1.0 + * + * + * NOTE: This class is auto generated by OpenAPI Generator (https://openapi-generator.tech). + * https://openapi-generator.tech + * Do not edit the class manually. + */ + + +export interface BodyLogin { + grant_type?: string; + username: string; + password: string; + scope?: string; + client_id?: string; + client_secret?: string; +} + diff --git a/frontend/src/app/core/api/model/bodyMastodonAccount.ts b/frontend/src/app/core/api/model/bodyMastodonAccount.ts new file mode 100644 index 0000000..95c549c --- /dev/null +++ b/frontend/src/app/core/api/model/bodyMastodonAccount.ts @@ -0,0 +1,19 @@ +/** + * FastAPI + * No description provided (generated by Openapi Generator https://github.com/openapitools/openapi-generator) + * + * The version of the OpenAPI document: 0.1.0 + * + * + * NOTE: This class is auto generated by OpenAPI Generator (https://openapi-generator.tech). + * https://openapi-generator.tech + * Do not edit the class manually. + */ + + +export interface BodyMastodonAccount { + email: string; + instance_url: string; + password: string; +} + diff --git a/frontend/src/app/core/api/model/bodySubscriber.ts b/frontend/src/app/core/api/model/bodySubscriber.ts index 2ebec6d..e4cc61a 100644 --- a/frontend/src/app/core/api/model/bodySubscriber.ts +++ b/frontend/src/app/core/api/model/bodySubscriber.ts @@ -12,7 +12,7 @@ /** - * This model holds the email address of a fresh subscriber. + * This model holds the email address of a fresh subscriber. */ export interface BodySubscriber { email: string; diff --git a/frontend/src/app/core/api/model/kibicaraPlatformsEmailWebapiBodyMessage.ts b/frontend/src/app/core/api/model/kibicaraPlatformsEmailWebapiBodyMessage.ts index b41cde6..04bf795 100644 --- a/frontend/src/app/core/api/model/kibicaraPlatformsEmailWebapiBodyMessage.ts +++ b/frontend/src/app/core/api/model/kibicaraPlatformsEmailWebapiBodyMessage.ts @@ -12,7 +12,7 @@ /** - * This model shows which values are supplied by the MDA listener script. + * This model shows which values are supplied by the MDA listener script. */ export interface KibicaraPlatformsEmailWebapiBodyMessage { text: string; diff --git a/frontend/src/app/core/api/model/models.ts b/frontend/src/app/core/api/model/models.ts index c74e204..b8f2443 100644 --- a/frontend/src/app/core/api/model/models.ts +++ b/frontend/src/app/core/api/model/models.ts @@ -1,8 +1,9 @@ export * from './bodyAccessToken'; export * from './bodyAdmin'; -export * from './bodyAdminLoginAdminLoginPost'; export * from './bodyBadWord'; export * from './bodyHood'; +export * from './bodyLogin'; +export * from './bodyMastodonAccount'; export * from './bodyPassword'; export * from './bodySubscriber'; export * from './bodyTelegram'; diff --git a/frontend/src/app/core/api/model/validationError.ts b/frontend/src/app/core/api/model/validationError.ts index c624e14..141a839 100644 --- a/frontend/src/app/core/api/model/validationError.ts +++ b/frontend/src/app/core/api/model/validationError.ts @@ -12,7 +12,7 @@ export interface ValidationError { - loc: Array; + loc: Array; msg: string; type: string; } diff --git a/frontend/src/app/dashboard/board/platforms/platforms.component.html b/frontend/src/app/dashboard/board/platforms/platforms.component.html index dec4623..d4c4987 100644 --- a/frontend/src/app/dashboard/board/platforms/platforms.component.html +++ b/frontend/src/app/dashboard/board/platforms/platforms.component.html @@ -39,4 +39,5 @@ + diff --git a/frontend/src/app/platforms/mastodon/mastodon-bot-card/mastodon-bot-card.component.html b/frontend/src/app/platforms/mastodon/mastodon-bot-card/mastodon-bot-card.component.html new file mode 100644 index 0000000..a4ab1a2 --- /dev/null +++ b/frontend/src/app/platforms/mastodon/mastodon-bot-card/mastodon-bot-card.component.html @@ -0,0 +1,47 @@ +
+ + + +
+ + mastodon + + +
+ + + + + @{{ mastodon.username }} + + + + + +
+ + + Unfortunately your hood admin has not configured mastodon as platform + yet. + + +
+ warning + + + +
+ \ No newline at end of file diff --git a/frontend/src/app/platforms/mastodon/mastodon-bot-card/mastodon-bot-card.component.scss b/frontend/src/app/platforms/mastodon/mastodon-bot-card/mastodon-bot-card.component.scss new file mode 100644 index 0000000..e69de29 diff --git a/frontend/src/app/platforms/mastodon/mastodon-bot-card/mastodon-bot-card.component.spec.ts b/frontend/src/app/platforms/mastodon/mastodon-bot-card/mastodon-bot-card.component.spec.ts new file mode 100644 index 0000000..fa631eb --- /dev/null +++ b/frontend/src/app/platforms/mastodon/mastodon-bot-card/mastodon-bot-card.component.spec.ts @@ -0,0 +1,23 @@ +import { ComponentFixture, TestBed } from '@angular/core/testing'; + +import { MastodonBotCardComponent } from './mastodon-bot-card.component'; + +describe('MastodonBotCardComponent', () => { + let component: MastodonBotCardComponent; + let fixture: ComponentFixture; + + beforeEach(async () => { + await TestBed.configureTestingModule({ + declarations: [ MastodonBotCardComponent ] + }) + .compileComponents(); + + fixture = TestBed.createComponent(MastodonBotCardComponent); + component = fixture.componentInstance; + fixture.detectChanges(); + }); + + it('should create', () => { + expect(component).toBeTruthy(); + }); +}); diff --git a/frontend/src/app/platforms/mastodon/mastodon-bot-card/mastodon-bot-card.component.ts b/frontend/src/app/platforms/mastodon/mastodon-bot-card/mastodon-bot-card.component.ts new file mode 100644 index 0000000..a00bbe0 --- /dev/null +++ b/frontend/src/app/platforms/mastodon/mastodon-bot-card/mastodon-bot-card.component.ts @@ -0,0 +1,27 @@ +import { Component, OnInit, Input } from '@angular/core'; +import { MastodonService } from 'src/app/core/api'; +import { MastodonBotInfoDialogComponent } from './mastodon-bot-info-dialog/mastodon-bot-info-dialog.component'; +import { MatDialog } from '@angular/material/dialog'; + +@Component({ + selector: 'app-mastodon-bot-card', + templateUrl: './mastodon-bot-card.component.html', + styleUrls: ['./mastodon-bot-card.component.scss'], +}) +export class MastodonBotCardComponent implements OnInit { + @Input() hoodId; + mastodons$; + + constructor( + private mastodonService: MastodonService, + private dialog: MatDialog + ) {} + + ngOnInit(): void { + this.mastodons$ = this.mastodonService.getMastodonsPublic(this.hoodId); + } + + onInfoClick() { + this.dialog.open(MastodonBotInfoDialogComponent); + } +} diff --git a/frontend/src/app/platforms/mastodon/mastodon-bot-card/mastodon-bot-info-dialog/mastodon-bot-info-dialog.component.html b/frontend/src/app/platforms/mastodon/mastodon-bot-card/mastodon-bot-info-dialog/mastodon-bot-info-dialog.component.html new file mode 100644 index 0000000..55ea167 --- /dev/null +++ b/frontend/src/app/platforms/mastodon/mastodon-bot-card/mastodon-bot-info-dialog/mastodon-bot-info-dialog.component.html @@ -0,0 +1,54 @@ + +
+

How to communicate with the hood via Telegram?

+ + + + How to subscribe to the hood via Telegram? + +
    +
  1. + Click on the telegram bot name that is shown in the telegram card. +
  2. +
  3. + Start messaging the telegram bot that the link leads to by writing a + message containing only the word /start. Done! +
  4. +
+
+ + + How to send a broadcast message to the hood? + +

+ Write a direct message to the bot. This message will be broadcasted to + the other platforms. +

+
+ + + How to receive messages? + +

+ If you subscribed to the bot, you will automatically receive the + messages of your hood from the bot. +

+
+ + + How to stop receiving messages? + +

+ Write a message with content /stop to the bot. You + should receive a message from the bot which confirms that the + unsubscription was successful. +

+
+
+
+
+ \ No newline at end of file diff --git a/frontend/src/app/platforms/mastodon/mastodon-bot-card/mastodon-bot-info-dialog/mastodon-bot-info-dialog.component.scss b/frontend/src/app/platforms/mastodon/mastodon-bot-card/mastodon-bot-info-dialog/mastodon-bot-info-dialog.component.scss new file mode 100644 index 0000000..e69de29 diff --git a/frontend/src/app/platforms/mastodon/mastodon-bot-card/mastodon-bot-info-dialog/mastodon-bot-info-dialog.component.ts b/frontend/src/app/platforms/mastodon/mastodon-bot-card/mastodon-bot-info-dialog/mastodon-bot-info-dialog.component.ts new file mode 100644 index 0000000..0a303ec --- /dev/null +++ b/frontend/src/app/platforms/mastodon/mastodon-bot-card/mastodon-bot-info-dialog/mastodon-bot-info-dialog.component.ts @@ -0,0 +1,12 @@ +import { Component, OnInit } from '@angular/core'; + +@Component({ + selector: 'app-mastodon-bot-info-dialog', + templateUrl: './mastodon-bot-info-dialog.component.html', + styleUrls: ['./mastodon-bot-info-dialog.component.scss'] +}) +export class MastodonBotInfoDialogComponent implements OnInit { + constructor() {} + + ngOnInit(): void {} +} diff --git a/frontend/src/app/platforms/mastodon/mastodon-settings/mastodon-dialog/mastodon-dialog.component.html b/frontend/src/app/platforms/mastodon/mastodon-settings/mastodon-dialog/mastodon-dialog.component.html new file mode 100644 index 0000000..f18716c --- /dev/null +++ b/frontend/src/app/platforms/mastodon/mastodon-settings/mastodon-dialog/mastodon-dialog.component.html @@ -0,0 +1,46 @@ +

Create new inbox

+ + +
+ + Mastodon Instance URL + + Mastodon Instance URL is required + + + Mastodon E-Mail + + Mastodon E-Mail is required + + + Mastodon Password + + Mastodon Password is required + +
+
+ + + + + diff --git a/frontend/src/app/platforms/mastodon/mastodon-settings/mastodon-dialog/mastodon-dialog.component.scss b/frontend/src/app/platforms/mastodon/mastodon-settings/mastodon-dialog/mastodon-dialog.component.scss new file mode 100644 index 0000000..dbb2947 --- /dev/null +++ b/frontend/src/app/platforms/mastodon/mastodon-settings/mastodon-dialog/mastodon-dialog.component.scss @@ -0,0 +1,26 @@ +.input { + display: grid; + grid-template-rows: 1fr 1fr 1fr; + width: 100%; + } + + form { + margin-top: 10px; + height: 100%; + } + + textarea { + height: 120px; + } + + .example-image { + margin-left: 10%; + margin-right: 10%; + width: 80%; + @media screen and (max-width: 600px) { + width: 100%; + margin-left: 0%; + margin-right: 0%; + } + } + \ No newline at end of file diff --git a/frontend/src/app/platforms/mastodon/mastodon-settings/mastodon-dialog/mastodon-dialog.component.spec.ts b/frontend/src/app/platforms/mastodon/mastodon-settings/mastodon-dialog/mastodon-dialog.component.spec.ts new file mode 100644 index 0000000..16f7cde --- /dev/null +++ b/frontend/src/app/platforms/mastodon/mastodon-settings/mastodon-dialog/mastodon-dialog.component.spec.ts @@ -0,0 +1,23 @@ +import { ComponentFixture, TestBed } from '@angular/core/testing'; + +import { MastodonDialogComponent } from './mastodon-dialog.component'; + +describe('MastodonDialogComponent', () => { + let component: MastodonDialogComponent; + let fixture: ComponentFixture; + + beforeEach(async () => { + await TestBed.configureTestingModule({ + declarations: [ MastodonDialogComponent ] + }) + .compileComponents(); + + fixture = TestBed.createComponent(MastodonDialogComponent); + component = fixture.componentInstance; + fixture.detectChanges(); + }); + + it('should create', () => { + expect(component).toBeTruthy(); + }); +}); diff --git a/frontend/src/app/platforms/mastodon/mastodon-settings/mastodon-dialog/mastodon-dialog.component.ts b/frontend/src/app/platforms/mastodon/mastodon-settings/mastodon-dialog/mastodon-dialog.component.ts new file mode 100644 index 0000000..162162e --- /dev/null +++ b/frontend/src/app/platforms/mastodon/mastodon-settings/mastodon-dialog/mastodon-dialog.component.ts @@ -0,0 +1,79 @@ +import { Component, OnInit, Inject } from '@angular/core'; +import { Validators, UntypedFormBuilder, UntypedFormGroup } from '@angular/forms'; +import { MatDialogRef, MAT_DIALOG_DATA } from '@angular/material/dialog'; +import { MastodonService } from 'src/app/core/api'; +import { MatSnackBar } from '@angular/material/snack-bar'; +import { first } from 'rxjs/operators'; + +@Component({ + selector: 'app-mastodon-dialog', + templateUrl: './mastodon-dialog.component.html', + styleUrls: ['./mastodon-dialog.component.scss'], +}) +export class MastodonDialogComponent implements OnInit { + form: UntypedFormGroup; + + constructor( + public dialogRef: MatDialogRef, + private formBuilder: UntypedFormBuilder, + private mastodonService: MastodonService, + private snackBar: MatSnackBar, + @Inject(MAT_DIALOG_DATA) public data + ) {} + + ngOnInit(): void { + this.form = this.formBuilder.group({ + email: ['', Validators.required], + password: ['', Validators.required], + instance_url: ['', Validators.required], + }); + + if (this.data.mastodonId) { + this.mastodonService + .getMastodon(this.data.mastodonId, this.data.hoodId) + .subscribe((data) => { + this.form.controls.email.setValue(data.email); + this.form.controls.password.setValue(data.password); + this.form.controls.instance_url.setValue(data.instance_url); + }); + } + } + + onCancel() { + this.dialogRef.close(); + } + + success() { + this.dialogRef.close(); + } + + error() { + this.snackBar.open('Invalid API Key. Try again!', 'Close', { + duration: 2000, + }); + } + + onSubmit() { + if (this.form.invalid) { + return; + } + + const response = { + email: this.form.controls.email.value, + instance_url: this.form.controls.instance_url.value, + password: this.form.controls.password.value + } + + this.mastodonService + .createMastodon(this.data.hoodId, response) + .pipe(first()) + .subscribe( + () => { + this.success(); + }, + () => { + this.error(); + } + ); +} +} diff --git a/frontend/src/app/platforms/mastodon/mastodon-settings/mastodon-settings.component.html b/frontend/src/app/platforms/mastodon/mastodon-settings/mastodon-settings.component.html new file mode 100644 index 0000000..15b5ca1 --- /dev/null +++ b/frontend/src/app/platforms/mastodon/mastodon-settings/mastodon-settings.component.html @@ -0,0 +1,69 @@ + + +
+ + Mastodon + + +
+ + + + + + + + +
+ @{{ mastodon.username }} + + +
+ + + + + + +
+
+ warning + + + +
+
+
+ \ No newline at end of file diff --git a/frontend/src/app/platforms/mastodon/mastodon-settings/mastodon-settings.component.scss b/frontend/src/app/platforms/mastodon/mastodon-settings/mastodon-settings.component.scss new file mode 100644 index 0000000..5265644 --- /dev/null +++ b/frontend/src/app/platforms/mastodon/mastodon-settings/mastodon-settings.component.scss @@ -0,0 +1,23 @@ +.entry { + display: grid; + grid-template-columns: 4fr 40px 20px; + width: 100%; + align-items: center; + } + + .platform-title { + display: grid; + grid-template-columns: 1fr 40px; + width: 100%; + align-items: center; + } + + .platform-heading { + align-self: flex-end; + } + + .mastodon { + background-image: url("../../../../assets/mastodon.png"); + background-size: cover; + } + \ No newline at end of file diff --git a/frontend/src/app/platforms/mastodon/mastodon-settings/mastodon-settings.component.spec.ts b/frontend/src/app/platforms/mastodon/mastodon-settings/mastodon-settings.component.spec.ts new file mode 100644 index 0000000..d03c29d --- /dev/null +++ b/frontend/src/app/platforms/mastodon/mastodon-settings/mastodon-settings.component.spec.ts @@ -0,0 +1,23 @@ +import { ComponentFixture, TestBed } from '@angular/core/testing'; + +import { MastodonSettingsComponent } from './mastodon-settings.component'; + +describe('MastodonSettingsComponent', () => { + let component: MastodonSettingsComponent; + let fixture: ComponentFixture; + + beforeEach(async () => { + await TestBed.configureTestingModule({ + declarations: [ MastodonSettingsComponent ] + }) + .compileComponents(); + + fixture = TestBed.createComponent(MastodonSettingsComponent); + component = fixture.componentInstance; + fixture.detectChanges(); + }); + + it('should create', () => { + expect(component).toBeTruthy(); + }); +}); diff --git a/frontend/src/app/platforms/mastodon/mastodon-settings/mastodon-settings.component.ts b/frontend/src/app/platforms/mastodon/mastodon-settings/mastodon-settings.component.ts new file mode 100644 index 0000000..ffbd679 --- /dev/null +++ b/frontend/src/app/platforms/mastodon/mastodon-settings/mastodon-settings.component.ts @@ -0,0 +1,102 @@ +import { Component, OnInit, Input } from '@angular/core'; +import { MastodonService } from 'src/app/core/api'; +import { Observable } from 'rxjs'; +import { MastodonBotInfoDialogComponent } from '../mastodon-bot-card/mastodon-bot-info-dialog/mastodon-bot-info-dialog.component'; +import { MatDialog } from '@angular/material/dialog'; +import { MastodonDialogComponent } from './mastodon-dialog/mastodon-dialog.component'; +import { YesNoDialogComponent } from 'src/app/shared/yes-no-dialog/yes-no-dialog.component'; +import { MatSnackBar } from '@angular/material/snack-bar'; + +@Component({ + selector: 'app-mastodon-settings', + templateUrl: './mastodon-settings.component.html', + styleUrls: ['./mastodon-settings.component.scss'], +}) +export class MastodonSettingsComponent implements OnInit { + @Input() hoodId; + mastodons$: Observable>; + + constructor( + private mastodonService: MastodonService, + public dialog: MatDialog, + private snackBar: MatSnackBar + ) {} + + ngOnInit(): void { + this.reload(); + } + + private reload() { + this.mastodons$ = this.mastodonService.getMastodons(this.hoodId); + } + + onInfoClick() { + this.dialog.open(MastodonBotInfoDialogComponent); + } + + onDelete(mastodonId) { + const dialogRef = this.dialog.open(YesNoDialogComponent, { + data: { + title: 'Warning', + content: + 'This will also delete the list of subscribers of the mastodon bot.', + }, + }); + + dialogRef.afterClosed().subscribe((response) => { + if (response) { + this.mastodonService + .deleteMastodon(mastodonId, this.hoodId) + .subscribe(() => { + this.reload(); + }); + } + }); + } + + onCreate() { + const dialogRef = this.dialog.open(MastodonDialogComponent, { + data: { hoodId: this.hoodId }, + }); + + dialogRef.afterClosed().subscribe(() => { + this.reload(); + }); + } + + onEdit(mastodonId) { + const dialogRef = this.dialog.open(MastodonDialogComponent, { + data: { hoodId: this.hoodId, mastodonId }, + }); + + dialogRef.afterClosed().subscribe(() => { + this.reload(); + }); + } + + onChange(mastodon) { + if (mastodon.enabled === 0) { + this.mastodonService.startMastodon(mastodon.id, this.hoodId).subscribe( + () => {}, + (error) => { + this.snackBar.open('Could not start. Check your settings.', 'Close', { + duration: 2000, + }); + } + ); + } else if (mastodon.enabled === 1) { + this.mastodonService.stopMastodon(mastodon.id, this.hoodId).subscribe( + () => {}, + (error) => { + this.snackBar.open('Could not stop. Check your settings.', 'Close', { + duration: 2000, + }); + } + ); + } + // TODO yeah i know this is bad, implement disabling/enabling + setTimeout(() => { + this.reload(); + }, 100); + } +} diff --git a/frontend/src/app/platforms/platforms-info-page/platforms-info-page.component.html b/frontend/src/app/platforms/platforms-info-page/platforms-info-page.component.html index b7d8ed4..3d3b646 100644 --- a/frontend/src/app/platforms/platforms-info-page/platforms-info-page.component.html +++ b/frontend/src/app/platforms/platforms-info-page/platforms-info-page.component.html @@ -3,4 +3,5 @@ + diff --git a/frontend/src/app/platforms/platforms.module.ts b/frontend/src/app/platforms/platforms.module.ts index e84b289..b9bb530 100644 --- a/frontend/src/app/platforms/platforms.module.ts +++ b/frontend/src/app/platforms/platforms.module.ts @@ -20,6 +20,10 @@ import { TelegramBotInfoDialogComponent } from './telegram/telegram-bot-card/tel import { TwitterBotInfoDialogComponent } from './twitter/twitter-bot-card/twitter-bot-info-dialog/twitter-bot-info-dialog.component'; import { EmailConfirmationComponent } from './email/email-confirmation/email-confirmation.component'; import { EmailUnsubscribeComponent } from './email/email-unsubscribe/email-unsubscribe.component'; +import { MastodonBotCardComponent } from './mastodon/mastodon-bot-card/mastodon-bot-card.component'; +import { MastodonSettingsComponent } from './mastodon/mastodon-settings/mastodon-settings.component'; +import { MastodonDialogComponent } from './mastodon/mastodon-settings/mastodon-dialog/mastodon-dialog.component'; +import { MastodonBotInfoDialogComponent } from './mastodon/mastodon-bot-card/mastodon-bot-info-dialog/mastodon-bot-info-dialog.component'; @NgModule({ declarations: [ @@ -42,10 +46,15 @@ import { EmailUnsubscribeComponent } from './email/email-unsubscribe/email-unsub TwitterBotInfoDialogComponent, EmailConfirmationComponent, EmailUnsubscribeComponent, + MastodonBotCardComponent, + MastodonSettingsComponent, + MastodonDialogComponent, + MastodonBotInfoDialogComponent ], imports: [CommonModule, SharedModule], exports: [ TelegramSettingsComponent, + MastodonSettingsComponent, TwitterSettingsComponent, EmailSettingsComponent, PlatformsInfoPageComponent, diff --git a/frontend/src/assets/mastodon.png b/frontend/src/assets/mastodon.png new file mode 100644 index 0000000..b09a98b Binary files /dev/null and b/frontend/src/assets/mastodon.png differ diff --git a/setup.sh b/setup.sh index be53487..13b674f 100755 --- a/setup.sh +++ b/setup.sh @@ -6,4 +6,4 @@ ln -sf ../../git-hooks/commit-msg .git/hooks/commit-msg # create virtualenv virtualenv -p $(which python3.10) backend/.venv -backend/.venv/bin/pip install tox black +backend/.venv/bin/pip install tox black pytest pytest-aiohttp