From 3ae4a08ad52fa35b3ae78cc1cca0f05bd05cc429 Mon Sep 17 00:00:00 2001 From: missytake Date: Sat, 5 Mar 2022 23:13:01 +0100 Subject: [PATCH 01/32] [doc] Document how to disable strict CORS checking --- CONTRIBUTING.md | 1 + 1 file changed, 1 insertion(+) diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index 730525d..839f43f 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -33,6 +33,7 @@ 3. Install the dependencies with `npm i` 4. Install Angular with `npm i -g @angular/cli` 5. Turn off production mode if you have not already (see above in backend). +6. Also make sure to disable strict CORS checking for testing: `sudo su -c 'echo "cors_allow_origin = \"*\"" >> /etc/kibicara.conf'` 6. Start the backend in a different terminal 7. To serve and open the application, run `ng s -o`. The application will open under [http://127.0.0.1:4200](http://127.0.0.1:4200). -- 2.43.5 From 07bc5a2686a1e069b18ff6b7fef3d49eae1e9fe6 Mon Sep 17 00:00:00 2001 From: missytake Date: Tue, 1 Mar 2022 17:41:13 +0100 Subject: [PATCH 02/32] [mastodon] First approach to a mastodon bot --- backend/setup.cfg | 1 + kibicara/platforms/mastodon/__init__.py | 0 kibicara/platforms/mastodon/bot.py | 70 +++++++++++++++++++++++++ kibicara/platforms/mastodon/model.py | 30 +++++++++++ 4 files changed, 101 insertions(+) create mode 100644 kibicara/platforms/mastodon/__init__.py create mode 100644 kibicara/platforms/mastodon/bot.py create mode 100644 kibicara/platforms/mastodon/model.py diff --git a/backend/setup.cfg b/backend/setup.cfg index d0df5e2..53d4b20 100644 --- a/backend/setup.cfg +++ b/backend/setup.cfg @@ -34,6 +34,7 @@ install_requires = pytoml requests scrypt + Mastodon.py [options.packages.find] where = src diff --git a/kibicara/platforms/mastodon/__init__.py b/kibicara/platforms/mastodon/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/kibicara/platforms/mastodon/bot.py b/kibicara/platforms/mastodon/bot.py new file mode 100644 index 0000000..e4845ee --- /dev/null +++ b/kibicara/platforms/mastodon/bot.py @@ -0,0 +1,70 @@ +# Copyright (C) 2020 by Thomas Lindner +# Copyright (C) 2020 by Cathy Hu +# Copyright (C) 2020 by Martin Rey +# +# SPDX-License-Identifier: 0BSD + +from kibicara.platformapi import Censor, Spawner, Message +from kibicara.platforms.mastodon.model import MastodonAccount + +from mastodon import Mastodon, MastodonError +from asyncio import gather +import sys + +from logging import getLogger + +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.account = Mastodon( + client_id=self.model.instance_id.client_id, + client_secret=self.model.instance_id.client_secret, + access_token=self.model.access_token, + ) + + async def run(self): + await gather(self.poll(), self.push()) + + async def poll(self): + """Get new mentions and DMs from Mastodon""" + while True: + try: + notifications = self.account.notifications() + except MastodonError: + logger.warning( + "%s in hood %s" % (sys.exc_info()[0], self.model.hood.name) + ) + continue + last_seen = int(self.model.last_seen) + for status in notifications: + status_id = int(status['status']['id']) + if status_id <= last_seen: + continue # toot was already processed in the past + if status_id > self.model.last_seen: + self.model.last_seen = status_id # save last_seen in database + text = status['status']['content'] + # sanitize toot content; see ticketfrei2 for regex magic + logger.debug( + "Mastodon in %s received message: " % (self.model.hood.name,) + ) + if status['status']['visibility'] == 'public': + await self.publish(Message(text, toot_id=status_id)) + else: + await self.publish(Message(text)) + + 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, "tood_id"): + await self.account.status_reblog(message.tood_id) + else: + await self.account.status_post(message.text) + + +spawner = Spawner(MastodonAccount, MastodonBot) diff --git a/kibicara/platforms/mastodon/model.py b/kibicara/platforms/mastodon/model.py new file mode 100644 index 0000000..722dd39 --- /dev/null +++ b/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_id: ForeignKey(MastodonInstance) + access_token: Text() + enabled: Boolean() = False + last_seen: Text() + + class Mapping(Mapping): + table_name = 'mastodonaccounts' -- 2.43.5 From 4dc4b9cfca3aae53a8f4ca06f2af05e483fdcbc0 Mon Sep 17 00:00:00 2001 From: missytake Date: Tue, 1 Mar 2022 17:53:44 +0100 Subject: [PATCH 03/32] [mastodon] Changed MastodonAccount column: instance_id -> instance --- kibicara/platforms/mastodon/bot.py | 4 ++-- kibicara/platforms/mastodon/model.py | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/kibicara/platforms/mastodon/bot.py b/kibicara/platforms/mastodon/bot.py index e4845ee..1ecc89d 100644 --- a/kibicara/platforms/mastodon/bot.py +++ b/kibicara/platforms/mastodon/bot.py @@ -22,8 +22,8 @@ class MastodonBot(Censor): self.model = mastodon_account_model self.enabled = self.model.enabled self.account = Mastodon( - client_id=self.model.instance_id.client_id, - client_secret=self.model.instance_id.client_secret, + client_id=self.model.instance.client_id, + client_secret=self.model.instance.client_secret, access_token=self.model.access_token, ) diff --git a/kibicara/platforms/mastodon/model.py b/kibicara/platforms/mastodon/model.py index 722dd39..c7a3140 100644 --- a/kibicara/platforms/mastodon/model.py +++ b/kibicara/platforms/mastodon/model.py @@ -21,7 +21,7 @@ class MastodonInstance(Model): class MastodonAccount(Model): id: Integer(primary_key=True) = None hood: ForeignKey(Hood) - instance_id: ForeignKey(MastodonInstance) + instance: ForeignKey(MastodonInstance) access_token: Text() enabled: Boolean() = False last_seen: Text() -- 2.43.5 From b1f8c08d25573df2298c2fb994df2551a181ff44 Mon Sep 17 00:00:00 2001 From: missytake Date: Tue, 1 Mar 2022 18:37:01 +0100 Subject: [PATCH 04/32] [mastodon] Some web routes to add a Mastodon Account to Ticketfrei --- kibicara/platforms/mastodon/webapi.py | 168 ++++++++++++++++++++++++++ 1 file changed, 168 insertions(+) create mode 100644 kibicara/platforms/mastodon/webapi.py diff --git a/kibicara/platforms/mastodon/webapi.py b/kibicara/platforms/mastodon/webapi.py new file mode 100644 index 0000000..03a7440 --- /dev/null +++ b/kibicara/platforms/mastodon/webapi.py @@ -0,0 +1,168 @@ +# Copyright (C) 2020 by Cathy Hu +# Copyright (C) 2020 by Martin Rey +# +# SPDX-License-Identifier: 0BSD + +from fastapi import APIRouter, Depends, HTTPException, Response, status +from ormantic.exceptions import NoMatch +from pydantic import BaseModel + +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, MastodonError + +from logging import getLogger + +logger = getLogger(__name__) + + +class BodyMastodonPublic(BaseModel): + username: str + instance: str + + +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() + return [ + BodyMastodonPublic(username=mbot.username, instance=mbot.model.instance.name) + for mbot in mastodonbots + if mbot.enabled == 1 and mbot.username + ] + + +@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.get( + '/{mastodon_id}', + # TODO response_model + operation_id='get_mastodon', +) +async def mastodon_read(mastodon=Depends(get_mastodon)): + return mastodon + + +@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.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(instance_url, username, password, hood=Depends(get_hood)): + """Add a Mastodon Account to a Ticketfrei account. + + open questions: + do we really get the username + password like this? + can the instance_url have different ways of writing? + + :param: instance_url: the API base URL of the mastodon server + :param: username: the username of the Mastodon account + :param: password: the password of the Mastodon account + :param: hood: the hood ORM object + """ + instance = await get_mastodon_instance(instance_url) + account = Mastodon( + instance.client_id, instance.client_secret, api_base_url=instance_url + ) + try: + access_token = account.log_in(username, password) + except MastodonError: + logger.warning("Login to Mastodon failed.", exc_info=True) + return # show error to user + MastodonAccount.objects.create( + hood=hood, + instance=instance, + access_token=access_token, + enabled=True, + last_seen="0", + ) -- 2.43.5 From d120d718f95ffcb3bd1d45faf165e76fa4c4d14e Mon Sep 17 00:00:00 2001 From: missytake Date: Tue, 1 Mar 2022 18:42:37 +0100 Subject: [PATCH 05/32] [mastodon] Added a TODO flair --- kibicara/platforms/mastodon/bot.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/kibicara/platforms/mastodon/bot.py b/kibicara/platforms/mastodon/bot.py index 1ecc89d..91dcd20 100644 --- a/kibicara/platforms/mastodon/bot.py +++ b/kibicara/platforms/mastodon/bot.py @@ -48,7 +48,7 @@ class MastodonBot(Censor): if status_id > self.model.last_seen: self.model.last_seen = status_id # save last_seen in database text = status['status']['content'] - # sanitize toot content; see ticketfrei2 for regex magic + # :TODO sanitize toot content; see ticketfrei2 for regex magic logger.debug( "Mastodon in %s received message: " % (self.model.hood.name,) ) -- 2.43.5 From 37f7b98c6796dfa6d60c8cd638a4677cbb1b2432 Mon Sep 17 00:00:00 2001 From: missytake Date: Wed, 2 Mar 2022 20:32:54 +0100 Subject: [PATCH 06/32] [mastodon] Import mastodon API correctly --- backend/src/kibicara/webapi/__init__.py | 4 ++++ kibicara/platforms/mastodon/webapi.py | 2 +- 2 files changed, 5 insertions(+), 1 deletion(-) diff --git a/backend/src/kibicara/webapi/__init__.py b/backend/src/kibicara/webapi/__init__.py index 297ac3e..adc99ea 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 @@ -40,3 +41,6 @@ hoods_router.include_router( 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") +hoods_router.include_router( + mastodon_router, prefix="/{hood_id}/mastodon", tags=["mastodon"] +) diff --git a/kibicara/platforms/mastodon/webapi.py b/kibicara/platforms/mastodon/webapi.py index 03a7440..bfaa7fc 100644 --- a/kibicara/platforms/mastodon/webapi.py +++ b/kibicara/platforms/mastodon/webapi.py @@ -159,7 +159,7 @@ async def mastodon_create(instance_url, username, password, hood=Depends(get_hoo except MastodonError: logger.warning("Login to Mastodon failed.", exc_info=True) return # show error to user - MastodonAccount.objects.create( + return await MastodonAccount.objects.create( hood=hood, instance=instance, access_token=access_token, -- 2.43.5 From 12935b79cbfc92ca60a472c1ae6dea3e7c6827f6 Mon Sep 17 00:00:00 2001 From: missytake Date: Thu, 3 Mar 2022 20:09:34 +0100 Subject: [PATCH 07/32] [mastodon] Load database references --- kibicara/platforms/mastodon/bot.py | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/kibicara/platforms/mastodon/bot.py b/kibicara/platforms/mastodon/bot.py index 91dcd20..8c6d9b4 100644 --- a/kibicara/platforms/mastodon/bot.py +++ b/kibicara/platforms/mastodon/bot.py @@ -8,7 +8,7 @@ from kibicara.platformapi import Censor, Spawner, Message from kibicara.platforms.mastodon.model import MastodonAccount from mastodon import Mastodon, MastodonError -from asyncio import gather +from asyncio import gather, get_event_loop import sys from logging import getLogger @@ -21,13 +21,14 @@ class MastodonBot(Censor): super().__init__(mastodon_account_model.hood) self.model = mastodon_account_model self.enabled = self.model.enabled + + async def run(self): + await self.model.instance.load() self.account = Mastodon( client_id=self.model.instance.client_id, client_secret=self.model.instance.client_secret, access_token=self.model.access_token, ) - - async def run(self): await gather(self.poll(), self.push()) async def poll(self): -- 2.43.5 From 9704ed4ddf216398fdf89b9c0cc082d7b2c225b7 Mon Sep 17 00:00:00 2001 From: missytake Date: Fri, 4 Mar 2022 18:13:20 +0100 Subject: [PATCH 08/32] [mastodon] Working now: toot reports from mastodon, but only when the next report arrives --- kibicara/platforms/mastodon/bot.py | 35 ++++++++++++++++++------------ 1 file changed, 21 insertions(+), 14 deletions(-) diff --git a/kibicara/platforms/mastodon/bot.py b/kibicara/platforms/mastodon/bot.py index 8c6d9b4..a4fcfc3 100644 --- a/kibicara/platforms/mastodon/bot.py +++ b/kibicara/platforms/mastodon/bot.py @@ -8,8 +8,8 @@ from kibicara.platformapi import Censor, Spawner, Message from kibicara.platforms.mastodon.model import MastodonAccount from mastodon import Mastodon, MastodonError -from asyncio import gather, get_event_loop -import sys +from asyncio import gather +import re from logging import getLogger @@ -27,6 +27,7 @@ class MastodonBot(Censor): 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, ) await gather(self.poll(), self.push()) @@ -36,22 +37,26 @@ class MastodonBot(Censor): while True: try: notifications = self.account.notifications() - except MastodonError: - logger.warning( - "%s in hood %s" % (sys.exc_info()[0], self.model.hood.name) - ) + except MastodonError as e: + logger.warning("%s in hood %s" % (e, self.model.hood.name)) continue last_seen = int(self.model.last_seen) for status in notifications: - status_id = int(status['status']['id']) + try: + status_id = int(status['status']['id']) + except KeyError: + continue # ignore notifications which don't have a status if status_id <= last_seen: continue # toot was already processed in the past - if status_id > self.model.last_seen: - self.model.last_seen = status_id # save last_seen in database - text = status['status']['content'] - # :TODO sanitize toot content; see ticketfrei2 for regex magic + if status_id > int(self.model.last_seen): + await self.model.update(last_seen=str(status_id)) + 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 message: " % (self.model.hood.name,) + "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)) @@ -63,9 +68,11 @@ class MastodonBot(Censor): while True: message = await self.receive() if hasattr(message, "tood_id"): - await self.account.status_reblog(message.tood_id) + logger.debug("Boosting post %s: %s" % (message.tood_id, message.text)) + self.account.status_reblog(message.tood_id) else: - await self.account.status_post(message.text) + logger.debug("Posting message: %s" % (message.text,)) + self.account.status_post(message.text) spawner = Spawner(MastodonAccount, MastodonBot) -- 2.43.5 From 5fa5a9f48e9a2ff7cf167cf12ab1bd9f4bc56d0e Mon Sep 17 00:00:00 2001 From: missytake Date: Sun, 6 Mar 2022 10:55:15 +0100 Subject: [PATCH 09/32] Revert "[doc] Document how to disable strict CORS checking" This reverts commit bd17d5321b9af1ee009f50b9a3aac23235712e5e. --- CONTRIBUTING.md | 1 - 1 file changed, 1 deletion(-) diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index 839f43f..730525d 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -33,7 +33,6 @@ 3. Install the dependencies with `npm i` 4. Install Angular with `npm i -g @angular/cli` 5. Turn off production mode if you have not already (see above in backend). -6. Also make sure to disable strict CORS checking for testing: `sudo su -c 'echo "cors_allow_origin = \"*\"" >> /etc/kibicara.conf'` 6. Start the backend in a different terminal 7. To serve and open the application, run `ng s -o`. The application will open under [http://127.0.0.1:4200](http://127.0.0.1:4200). -- 2.43.5 From fb1e88ab030d8e2b3246d7e1a0c331e683ad94fa Mon Sep 17 00:00:00 2001 From: missytake Date: Sat, 18 Mar 2023 17:38:02 +0100 Subject: [PATCH 10/32] [mastodon] Moved mastodon module to new backend directory --- {kibicara => backend/src/kibicara}/platforms/mastodon/__init__.py | 0 {kibicara => backend/src/kibicara}/platforms/mastodon/bot.py | 0 {kibicara => backend/src/kibicara}/platforms/mastodon/model.py | 0 {kibicara => backend/src/kibicara}/platforms/mastodon/webapi.py | 0 4 files changed, 0 insertions(+), 0 deletions(-) rename {kibicara => backend/src/kibicara}/platforms/mastodon/__init__.py (100%) rename {kibicara => backend/src/kibicara}/platforms/mastodon/bot.py (100%) rename {kibicara => backend/src/kibicara}/platforms/mastodon/model.py (100%) rename {kibicara => backend/src/kibicara}/platforms/mastodon/webapi.py (100%) diff --git a/kibicara/platforms/mastodon/__init__.py b/backend/src/kibicara/platforms/mastodon/__init__.py similarity index 100% rename from kibicara/platforms/mastodon/__init__.py rename to backend/src/kibicara/platforms/mastodon/__init__.py diff --git a/kibicara/platforms/mastodon/bot.py b/backend/src/kibicara/platforms/mastodon/bot.py similarity index 100% rename from kibicara/platforms/mastodon/bot.py rename to backend/src/kibicara/platforms/mastodon/bot.py diff --git a/kibicara/platforms/mastodon/model.py b/backend/src/kibicara/platforms/mastodon/model.py similarity index 100% rename from kibicara/platforms/mastodon/model.py rename to backend/src/kibicara/platforms/mastodon/model.py diff --git a/kibicara/platforms/mastodon/webapi.py b/backend/src/kibicara/platforms/mastodon/webapi.py similarity index 100% rename from kibicara/platforms/mastodon/webapi.py rename to backend/src/kibicara/platforms/mastodon/webapi.py -- 2.43.5 From 3d482dd5f52aa5897a8188a59ea75deb700962dc Mon Sep 17 00:00:00 2001 From: missytake Date: Sat, 18 Mar 2023 17:41:09 +0100 Subject: [PATCH 11/32] [mastodon] Generated openAPI routes for frontend --- .../src/app/core/api/.openapi-generator/FILES | 3 +- frontend/src/app/core/api/api.module.ts | 1 + .../src/app/core/api/api/admin.service.ts | 22 +- frontend/src/app/core/api/api/api.ts | 4 +- .../src/app/core/api/api/badwords.service.ts | 18 +- .../src/app/core/api/api/email.service.ts | 32 +- .../src/app/core/api/api/hoods.service.ts | 18 +- .../src/app/core/api/api/mastodon.service.ts | 547 ++++++++++++++++++ .../src/app/core/api/api/telegram.service.ts | 20 +- frontend/src/app/core/api/api/test.service.ts | 14 +- .../src/app/core/api/api/triggers.service.ts | 18 +- .../src/app/core/api/api/twitter.service.ts | 20 +- frontend/src/app/core/api/model/bodyLogin.ts | 22 + .../src/app/core/api/model/bodySubscriber.ts | 2 +- ...kibicaraPlatformsEmailWebapiBodyMessage.ts | 2 +- frontend/src/app/core/api/model/models.ts | 2 +- .../src/app/core/api/model/validationError.ts | 2 +- 17 files changed, 660 insertions(+), 87 deletions(-) create mode 100644 frontend/src/app/core/api/api/mastodon.service.ts create mode 100644 frontend/src/app/core/api/model/bodyLogin.ts diff --git a/frontend/src/app/core/api/.openapi-generator/FILES b/frontend/src/app/core/api/.openapi-generator/FILES index 9505d8c..f3d80a3 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,9 @@ git_push.sh index.ts model/bodyAccessToken.ts model/bodyAdmin.ts -model/bodyAdminLoginAdminLoginPost.ts model/bodyBadWord.ts model/bodyHood.ts +model/bodyLogin.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..665a6f0 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:8000/api'; public defaultHeaders = new HttpHeaders(); public configuration = new Configuration(); public encoder: HttpParameterCodec; @@ -136,7 +136,7 @@ export class AdminService { responseType = 'text'; } - return this.httpClient.post(`${this.configuration.basePath}/api/admin/confirm/${encodeURIComponent(String(registerToken))}`, + return this.httpClient.post(`${this.configuration.basePath}/admin/confirm/${encodeURIComponent(String(registerToken))}`, null, { responseType: responseType, @@ -195,7 +195,7 @@ export class AdminService { responseType = 'text'; } - return this.httpClient.post(`${this.configuration.basePath}/api/admin/reset/${encodeURIComponent(String(resetToken))}`, + return this.httpClient.post(`${this.configuration.basePath}/admin/reset/${encodeURIComponent(String(resetToken))}`, bodyPassword, { responseType: responseType, @@ -243,7 +243,7 @@ export class AdminService { responseType = 'text'; } - return this.httpClient.delete(`${this.configuration.basePath}/api/admin/`, + return this.httpClient.delete(`${this.configuration.basePath}/admin/`, { responseType: responseType, withCredentials: this.configuration.withCredentials, @@ -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. */ @@ -292,7 +292,7 @@ export class AdminService { responseType = 'text'; } - return this.httpClient.get(`${this.configuration.basePath}/api/admin/`, + return this.httpClient.get(`${this.configuration.basePath}/admin/`, { responseType: responseType, withCredentials: this.configuration.withCredentials, @@ -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. */ @@ -341,7 +341,7 @@ export class AdminService { responseType = 'text'; } - return this.httpClient.get(`${this.configuration.basePath}/api/admin/hoods/`, + return this.httpClient.get(`${this.configuration.basePath}/admin/hoods/`, { responseType: responseType, withCredentials: this.configuration.withCredentials, @@ -429,7 +429,7 @@ export class AdminService { responseType = 'text'; } - return this.httpClient.post(`${this.configuration.basePath}/api/admin/login/`, + return this.httpClient.post(`${this.configuration.basePath}/admin/login/`, convertFormParamsToString ? formParams.toString() : formParams, { responseType: responseType, @@ -485,7 +485,7 @@ export class AdminService { responseType = 'text'; } - return this.httpClient.post(`${this.configuration.basePath}/api/admin/register/`, + return this.httpClient.post(`${this.configuration.basePath}/admin/register/`, bodyAdmin, { responseType: responseType, @@ -541,7 +541,7 @@ export class AdminService { responseType = 'text'; } - return this.httpClient.post(`${this.configuration.basePath}/api/admin/reset/`, + return this.httpClient.post(`${this.configuration.basePath}/admin/reset/`, kibicaraWebapiAdminBodyEmail, { responseType: responseType, 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..da66dfa 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:8000/api'; public defaultHeaders = new HttpHeaders(); public configuration = new Configuration(); public encoder: HttpParameterCodec; @@ -140,7 +140,7 @@ export class BadwordsService { responseType = 'text'; } - return this.httpClient.post(`${this.configuration.basePath}/api/hoods/${encodeURIComponent(String(hoodId))}/badwords/`, + return this.httpClient.post(`${this.configuration.basePath}/hoods/${encodeURIComponent(String(hoodId))}/badwords/`, bodyBadWord, { responseType: responseType, @@ -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. @@ -198,7 +198,7 @@ export class BadwordsService { responseType = 'text'; } - return this.httpClient.delete(`${this.configuration.basePath}/api/hoods/${encodeURIComponent(String(hoodId))}/badwords/${encodeURIComponent(String(badwordId))}`, + return this.httpClient.delete(`${this.configuration.basePath}/hoods/${encodeURIComponent(String(hoodId))}/badwords/${encodeURIComponent(String(badwordId))}`, { responseType: responseType, withCredentials: this.configuration.withCredentials, @@ -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. @@ -255,7 +255,7 @@ export class BadwordsService { responseType = 'text'; } - return this.httpClient.get(`${this.configuration.basePath}/api/hoods/${encodeURIComponent(String(hoodId))}/badwords/${encodeURIComponent(String(badwordId))}`, + return this.httpClient.get(`${this.configuration.basePath}/hoods/${encodeURIComponent(String(hoodId))}/badwords/${encodeURIComponent(String(badwordId))}`, { responseType: responseType, withCredentials: this.configuration.withCredentials, @@ -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. @@ -308,7 +308,7 @@ export class BadwordsService { responseType = 'text'; } - return this.httpClient.get(`${this.configuration.basePath}/api/hoods/${encodeURIComponent(String(hoodId))}/badwords/`, + return this.httpClient.get(`${this.configuration.basePath}/hoods/${encodeURIComponent(String(hoodId))}/badwords/`, { responseType: responseType, withCredentials: this.configuration.withCredentials, @@ -378,7 +378,7 @@ export class BadwordsService { responseType = 'text'; } - return this.httpClient.put(`${this.configuration.basePath}/api/hoods/${encodeURIComponent(String(hoodId))}/badwords/${encodeURIComponent(String(badwordId))}`, + return this.httpClient.put(`${this.configuration.basePath}/hoods/${encodeURIComponent(String(hoodId))}/badwords/${encodeURIComponent(String(badwordId))}`, bodyBadWord, { responseType: responseType, diff --git a/frontend/src/app/core/api/api/email.service.ts b/frontend/src/app/core/api/api/email.service.ts index bd64617..526622c 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:8000/api'; public defaultHeaders = new HttpHeaders(); public configuration = new Configuration(); public encoder: HttpParameterCodec; @@ -126,7 +126,7 @@ export class EmailService { responseType = 'text'; } - return this.httpClient.post(`${this.configuration.basePath}/api/hoods/${encodeURIComponent(String(hoodId))}/email/subscribe/confirm/${encodeURIComponent(String(token))}`, + return this.httpClient.post(`${this.configuration.basePath}/hoods/${encodeURIComponent(String(hoodId))}/email/subscribe/confirm/${encodeURIComponent(String(token))}`, null, { responseType: responseType, @@ -193,7 +193,7 @@ export class EmailService { responseType = 'text'; } - return this.httpClient.post(`${this.configuration.basePath}/api/hoods/${encodeURIComponent(String(hoodId))}/email/`, + return this.httpClient.post(`${this.configuration.basePath}/hoods/${encodeURIComponent(String(hoodId))}/email/`, kibicaraPlatformsEmailWebapiBodyEmail, { responseType: responseType, @@ -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. @@ -251,7 +251,7 @@ export class EmailService { responseType = 'text'; } - return this.httpClient.delete(`${this.configuration.basePath}/api/hoods/${encodeURIComponent(String(hoodId))}/email/${encodeURIComponent(String(emailId))}`, + return this.httpClient.delete(`${this.configuration.basePath}/hoods/${encodeURIComponent(String(hoodId))}/email/${encodeURIComponent(String(emailId))}`, { responseType: responseType, withCredentials: this.configuration.withCredentials, @@ -307,7 +307,7 @@ export class EmailService { responseType = 'text'; } - return this.httpClient.get(`${this.configuration.basePath}/api/hoods/${encodeURIComponent(String(hoodId))}/email/${encodeURIComponent(String(emailId))}`, + return this.httpClient.get(`${this.configuration.basePath}/hoods/${encodeURIComponent(String(hoodId))}/email/${encodeURIComponent(String(emailId))}`, { responseType: responseType, withCredentials: this.configuration.withCredentials, @@ -359,7 +359,7 @@ export class EmailService { responseType = 'text'; } - return this.httpClient.get(`${this.configuration.basePath}/api/hoods/${encodeURIComponent(String(hoodId))}/email/`, + return this.httpClient.get(`${this.configuration.basePath}/hoods/${encodeURIComponent(String(hoodId))}/email/`, { responseType: responseType, withCredentials: this.configuration.withCredentials, @@ -404,7 +404,7 @@ export class EmailService { responseType = 'text'; } - return this.httpClient.get(`${this.configuration.basePath}/api/hoods/${encodeURIComponent(String(hoodId))}/email/public`, + return this.httpClient.get(`${this.configuration.basePath}/hoods/${encodeURIComponent(String(hoodId))}/email/public`, { responseType: responseType, withCredentials: this.configuration.withCredentials, @@ -460,7 +460,7 @@ export class EmailService { responseType = 'text'; } - return this.httpClient.get(`${this.configuration.basePath}/api/hoods/${encodeURIComponent(String(hoodId))}/email/subscribers/${encodeURIComponent(String(subscriberId))}`, + return this.httpClient.get(`${this.configuration.basePath}/hoods/${encodeURIComponent(String(hoodId))}/email/subscribers/${encodeURIComponent(String(subscriberId))}`, { responseType: responseType, withCredentials: this.configuration.withCredentials, @@ -512,7 +512,7 @@ export class EmailService { responseType = 'text'; } - return this.httpClient.get(`${this.configuration.basePath}/api/hoods/${encodeURIComponent(String(hoodId))}/email/subscribers/`, + return this.httpClient.get(`${this.configuration.basePath}/hoods/${encodeURIComponent(String(hoodId))}/email/subscribers/`, { responseType: responseType, withCredentials: this.configuration.withCredentials, @@ -571,7 +571,7 @@ export class EmailService { responseType = 'text'; } - return this.httpClient.post(`${this.configuration.basePath}/api/hoods/${encodeURIComponent(String(hoodId))}/email/messages/`, + return this.httpClient.post(`${this.configuration.basePath}/hoods/${encodeURIComponent(String(hoodId))}/email/messages/`, kibicaraPlatformsEmailWebapiBodyMessage, { responseType: responseType, @@ -624,7 +624,7 @@ export class EmailService { responseType = 'text'; } - return this.httpClient.post(`${this.configuration.basePath}/api/hoods/${encodeURIComponent(String(hoodId))}/email/start`, + return this.httpClient.post(`${this.configuration.basePath}/hoods/${encodeURIComponent(String(hoodId))}/email/start`, null, { responseType: responseType, @@ -677,7 +677,7 @@ export class EmailService { responseType = 'text'; } - return this.httpClient.get(`${this.configuration.basePath}/api/hoods/${encodeURIComponent(String(hoodId))}/email/status`, + return this.httpClient.get(`${this.configuration.basePath}/hoods/${encodeURIComponent(String(hoodId))}/email/status`, { responseType: responseType, withCredentials: this.configuration.withCredentials, @@ -729,7 +729,7 @@ export class EmailService { responseType = 'text'; } - return this.httpClient.post(`${this.configuration.basePath}/api/hoods/${encodeURIComponent(String(hoodId))}/email/stop`, + return this.httpClient.post(`${this.configuration.basePath}/hoods/${encodeURIComponent(String(hoodId))}/email/stop`, null, { responseType: responseType, @@ -789,7 +789,7 @@ export class EmailService { responseType = 'text'; } - return this.httpClient.post(`${this.configuration.basePath}/api/hoods/${encodeURIComponent(String(hoodId))}/email/subscribe/`, + return this.httpClient.post(`${this.configuration.basePath}/hoods/${encodeURIComponent(String(hoodId))}/email/subscribe/`, bodySubscriber, { responseType: responseType, @@ -840,7 +840,7 @@ export class EmailService { responseType = 'text'; } - return this.httpClient.delete(`${this.configuration.basePath}/api/hoods/${encodeURIComponent(String(hoodId))}/email/unsubscribe/${encodeURIComponent(String(token))}`, + return this.httpClient.delete(`${this.configuration.basePath}/hoods/${encodeURIComponent(String(hoodId))}/email/unsubscribe/${encodeURIComponent(String(token))}`, { responseType: responseType, withCredentials: this.configuration.withCredentials, diff --git a/frontend/src/app/core/api/api/hoods.service.ts b/frontend/src/app/core/api/api/hoods.service.ts index 51ed5ac..583c40b 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:8000/api'; public defaultHeaders = new HttpHeaders(); public configuration = new Configuration(); public encoder: HttpParameterCodec; @@ -136,7 +136,7 @@ export class HoodsService { responseType = 'text'; } - return this.httpClient.post(`${this.configuration.basePath}/api/hoods/`, + return this.httpClient.post(`${this.configuration.basePath}/hoods/`, bodyHood, { responseType: responseType, @@ -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. @@ -190,7 +190,7 @@ export class HoodsService { responseType = 'text'; } - return this.httpClient.delete(`${this.configuration.basePath}/api/hoods/${encodeURIComponent(String(hoodId))}`, + return this.httpClient.delete(`${this.configuration.basePath}/hoods/${encodeURIComponent(String(hoodId))}`, { responseType: responseType, withCredentials: this.configuration.withCredentials, @@ -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. @@ -236,7 +236,7 @@ export class HoodsService { responseType = 'text'; } - return this.httpClient.get(`${this.configuration.basePath}/api/hoods/${encodeURIComponent(String(hoodId))}`, + return this.httpClient.get(`${this.configuration.basePath}/hoods/${encodeURIComponent(String(hoodId))}`, { responseType: responseType, withCredentials: this.configuration.withCredentials, @@ -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. */ @@ -278,7 +278,7 @@ export class HoodsService { responseType = 'text'; } - return this.httpClient.get(`${this.configuration.basePath}/api/hoods/`, + return this.httpClient.get(`${this.configuration.basePath}/hoods/`, { responseType: responseType, withCredentials: this.configuration.withCredentials, @@ -344,7 +344,7 @@ export class HoodsService { responseType = 'text'; } - return this.httpClient.put(`${this.configuration.basePath}/api/hoods/${encodeURIComponent(String(hoodId))}`, + return this.httpClient.put(`${this.configuration.basePath}/hoods/${encodeURIComponent(String(hoodId))}`, bodyHood, { responseType: responseType, 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..f937044 --- /dev/null +++ b/frontend/src/app/core/api/api/mastodon.service.ts @@ -0,0 +1,547 @@ +/** + * 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 { 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:8000/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: do we really get the username + password like this? can the instance_url have different ways of writing? :param: instance_url: the API base URL of the mastodon server :param: username: the username of the Mastodon account :param: password: the password of the Mastodon account :param: hood: the hood ORM object + * @param hoodId + * @param instanceUrl + * @param username + * @param password + * @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, instanceUrl: any, username: any, password: any, observe?: 'body', reportProgress?: boolean, options?: {httpHeaderAccept?: 'application/json'}): Observable; + public createMastodon(hoodId: number, instanceUrl: any, username: any, password: any, observe?: 'response', reportProgress?: boolean, options?: {httpHeaderAccept?: 'application/json'}): Observable>; + public createMastodon(hoodId: number, instanceUrl: any, username: any, password: any, observe?: 'events', reportProgress?: boolean, options?: {httpHeaderAccept?: 'application/json'}): Observable>; + public createMastodon(hoodId: number, instanceUrl: any, username: any, password: any, 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 (instanceUrl === null || instanceUrl === undefined) { + throw new Error('Required parameter instanceUrl was null or undefined when calling createMastodon.'); + } + if (username === null || username === undefined) { + throw new Error('Required parameter username was null or undefined when calling createMastodon.'); + } + if (password === null || password === undefined) { + throw new Error('Required parameter password was null or undefined when calling createMastodon.'); + } + + let queryParameters = new HttpParams({encoder: this.encoder}); + if (instanceUrl !== undefined && instanceUrl !== null) { + queryParameters = this.addToHttpParams(queryParameters, + instanceUrl, 'instance_url'); + } + if (username !== undefined && username !== null) { + queryParameters = this.addToHttpParams(queryParameters, + username, 'username'); + } + if (password !== undefined && password !== null) { + queryParameters = this.addToHttpParams(queryParameters, + password, 'password'); + } + + 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}/hoods/${encodeURIComponent(String(hoodId))}/mastodon/`, + null, + { + params: queryParameters, + 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}/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}/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}/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}/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}/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}/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}/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..3167896 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:8000/api'; public defaultHeaders = new HttpHeaders(); public configuration = new Configuration(); public encoder: HttpParameterCodec; @@ -139,7 +139,7 @@ export class TelegramService { responseType = 'text'; } - return this.httpClient.post(`${this.configuration.basePath}/api/hoods/${encodeURIComponent(String(hoodId))}/telegram/`, + return this.httpClient.post(`${this.configuration.basePath}/hoods/${encodeURIComponent(String(hoodId))}/telegram/`, bodyTelegram, { responseType: responseType, @@ -196,7 +196,7 @@ export class TelegramService { responseType = 'text'; } - return this.httpClient.delete(`${this.configuration.basePath}/api/hoods/${encodeURIComponent(String(hoodId))}/telegram/${encodeURIComponent(String(telegramId))}`, + return this.httpClient.delete(`${this.configuration.basePath}/hoods/${encodeURIComponent(String(hoodId))}/telegram/${encodeURIComponent(String(telegramId))}`, { responseType: responseType, withCredentials: this.configuration.withCredentials, @@ -252,7 +252,7 @@ export class TelegramService { responseType = 'text'; } - return this.httpClient.get(`${this.configuration.basePath}/api/hoods/${encodeURIComponent(String(hoodId))}/telegram/${encodeURIComponent(String(telegramId))}`, + return this.httpClient.get(`${this.configuration.basePath}/hoods/${encodeURIComponent(String(hoodId))}/telegram/${encodeURIComponent(String(telegramId))}`, { responseType: responseType, withCredentials: this.configuration.withCredentials, @@ -304,7 +304,7 @@ export class TelegramService { responseType = 'text'; } - return this.httpClient.get(`${this.configuration.basePath}/api/hoods/${encodeURIComponent(String(hoodId))}/telegram/`, + return this.httpClient.get(`${this.configuration.basePath}/hoods/${encodeURIComponent(String(hoodId))}/telegram/`, { responseType: responseType, withCredentials: this.configuration.withCredentials, @@ -349,7 +349,7 @@ export class TelegramService { responseType = 'text'; } - return this.httpClient.get(`${this.configuration.basePath}/api/hoods/${encodeURIComponent(String(hoodId))}/telegram/public`, + return this.httpClient.get(`${this.configuration.basePath}/hoods/${encodeURIComponent(String(hoodId))}/telegram/public`, { responseType: responseType, withCredentials: this.configuration.withCredentials, @@ -405,7 +405,7 @@ export class TelegramService { responseType = 'text'; } - return this.httpClient.post(`${this.configuration.basePath}/api/hoods/${encodeURIComponent(String(hoodId))}/telegram/${encodeURIComponent(String(telegramId))}/start`, + return this.httpClient.post(`${this.configuration.basePath}/hoods/${encodeURIComponent(String(hoodId))}/telegram/${encodeURIComponent(String(telegramId))}/start`, null, { responseType: responseType, @@ -462,7 +462,7 @@ export class TelegramService { responseType = 'text'; } - return this.httpClient.get(`${this.configuration.basePath}/api/hoods/${encodeURIComponent(String(hoodId))}/telegram/${encodeURIComponent(String(telegramId))}/status`, + return this.httpClient.get(`${this.configuration.basePath}/hoods/${encodeURIComponent(String(hoodId))}/telegram/${encodeURIComponent(String(telegramId))}/status`, { responseType: responseType, withCredentials: this.configuration.withCredentials, @@ -518,7 +518,7 @@ export class TelegramService { responseType = 'text'; } - return this.httpClient.post(`${this.configuration.basePath}/api/hoods/${encodeURIComponent(String(hoodId))}/telegram/${encodeURIComponent(String(telegramId))}/stop`, + return this.httpClient.post(`${this.configuration.basePath}/hoods/${encodeURIComponent(String(hoodId))}/telegram/${encodeURIComponent(String(telegramId))}/stop`, null, { responseType: responseType, @@ -588,7 +588,7 @@ export class TelegramService { responseType = 'text'; } - return this.httpClient.put(`${this.configuration.basePath}/api/hoods/${encodeURIComponent(String(hoodId))}/telegram/${encodeURIComponent(String(telegramId))}`, + return this.httpClient.put(`${this.configuration.basePath}/hoods/${encodeURIComponent(String(hoodId))}/telegram/${encodeURIComponent(String(telegramId))}`, bodyTelegram, { responseType: responseType, diff --git a/frontend/src/app/core/api/api/test.service.ts b/frontend/src/app/core/api/api/test.service.ts index 1a148e3..b207662 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:8000/api'; public defaultHeaders = new HttpHeaders(); public configuration = new Configuration(); public encoder: HttpParameterCodec; @@ -126,7 +126,7 @@ export class TestService { responseType = 'text'; } - return this.httpClient.post(`${this.configuration.basePath}/api/hoods/${encodeURIComponent(String(hoodId))}/test/`, + return this.httpClient.post(`${this.configuration.basePath}/hoods/${encodeURIComponent(String(hoodId))}/test/`, null, { responseType: responseType, @@ -183,7 +183,7 @@ export class TestService { responseType = 'text'; } - return this.httpClient.delete(`${this.configuration.basePath}/api/hoods/${encodeURIComponent(String(hoodId))}/test/${encodeURIComponent(String(testId))}`, + return this.httpClient.delete(`${this.configuration.basePath}/hoods/${encodeURIComponent(String(hoodId))}/test/${encodeURIComponent(String(testId))}`, { responseType: responseType, withCredentials: this.configuration.withCredentials, @@ -252,7 +252,7 @@ export class TestService { responseType = 'text'; } - return this.httpClient.post(`${this.configuration.basePath}/api/hoods/${encodeURIComponent(String(hoodId))}/test/${encodeURIComponent(String(testId))}/messages/`, + return this.httpClient.post(`${this.configuration.basePath}/hoods/${encodeURIComponent(String(hoodId))}/test/${encodeURIComponent(String(testId))}/messages/`, kibicaraPlatformsTestWebapiBodyMessage, { responseType: responseType, @@ -309,7 +309,7 @@ export class TestService { responseType = 'text'; } - return this.httpClient.get(`${this.configuration.basePath}/api/hoods/${encodeURIComponent(String(hoodId))}/test/${encodeURIComponent(String(testId))}/messages/`, + return this.httpClient.get(`${this.configuration.basePath}/hoods/${encodeURIComponent(String(hoodId))}/test/${encodeURIComponent(String(testId))}/messages/`, { responseType: responseType, withCredentials: this.configuration.withCredentials, @@ -361,7 +361,7 @@ export class TestService { responseType = 'text'; } - return this.httpClient.get(`${this.configuration.basePath}/api/hoods/${encodeURIComponent(String(hoodId))}/test/`, + return this.httpClient.get(`${this.configuration.basePath}/hoods/${encodeURIComponent(String(hoodId))}/test/`, { responseType: responseType, withCredentials: this.configuration.withCredentials, @@ -417,7 +417,7 @@ export class TestService { responseType = 'text'; } - return this.httpClient.get(`${this.configuration.basePath}/api/hoods/${encodeURIComponent(String(hoodId))}/test/${encodeURIComponent(String(testId))}`, + return this.httpClient.get(`${this.configuration.basePath}/hoods/${encodeURIComponent(String(hoodId))}/test/${encodeURIComponent(String(testId))}`, { responseType: responseType, withCredentials: this.configuration.withCredentials, diff --git a/frontend/src/app/core/api/api/triggers.service.ts b/frontend/src/app/core/api/api/triggers.service.ts index cd8dffd..28a9f01 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:8000/api'; public defaultHeaders = new HttpHeaders(); public configuration = new Configuration(); public encoder: HttpParameterCodec; @@ -140,7 +140,7 @@ export class TriggersService { responseType = 'text'; } - return this.httpClient.post(`${this.configuration.basePath}/api/hoods/${encodeURIComponent(String(hoodId))}/triggers/`, + return this.httpClient.post(`${this.configuration.basePath}/hoods/${encodeURIComponent(String(hoodId))}/triggers/`, bodyTrigger, { responseType: responseType, @@ -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. @@ -198,7 +198,7 @@ export class TriggersService { responseType = 'text'; } - return this.httpClient.delete(`${this.configuration.basePath}/api/hoods/${encodeURIComponent(String(hoodId))}/triggers/${encodeURIComponent(String(triggerId))}`, + return this.httpClient.delete(`${this.configuration.basePath}/hoods/${encodeURIComponent(String(hoodId))}/triggers/${encodeURIComponent(String(triggerId))}`, { responseType: responseType, withCredentials: this.configuration.withCredentials, @@ -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. @@ -255,7 +255,7 @@ export class TriggersService { responseType = 'text'; } - return this.httpClient.get(`${this.configuration.basePath}/api/hoods/${encodeURIComponent(String(hoodId))}/triggers/${encodeURIComponent(String(triggerId))}`, + return this.httpClient.get(`${this.configuration.basePath}/hoods/${encodeURIComponent(String(hoodId))}/triggers/${encodeURIComponent(String(triggerId))}`, { responseType: responseType, withCredentials: this.configuration.withCredentials, @@ -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. @@ -308,7 +308,7 @@ export class TriggersService { responseType = 'text'; } - return this.httpClient.get(`${this.configuration.basePath}/api/hoods/${encodeURIComponent(String(hoodId))}/triggers/`, + return this.httpClient.get(`${this.configuration.basePath}/hoods/${encodeURIComponent(String(hoodId))}/triggers/`, { responseType: responseType, withCredentials: this.configuration.withCredentials, @@ -378,7 +378,7 @@ export class TriggersService { responseType = 'text'; } - return this.httpClient.put(`${this.configuration.basePath}/api/hoods/${encodeURIComponent(String(hoodId))}/triggers/${encodeURIComponent(String(triggerId))}`, + return this.httpClient.put(`${this.configuration.basePath}/hoods/${encodeURIComponent(String(hoodId))}/triggers/${encodeURIComponent(String(triggerId))}`, bodyTrigger, { responseType: responseType, diff --git a/frontend/src/app/core/api/api/twitter.service.ts b/frontend/src/app/core/api/api/twitter.service.ts index e91fb01..c0d3f1c 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:8000/api'; public defaultHeaders = new HttpHeaders(); public configuration = new Configuration(); public encoder: HttpParameterCodec; @@ -147,7 +147,7 @@ export class TwitterService { responseType = 'text'; } - return this.httpClient.get(`${this.configuration.basePath}/api/twitter/callback`, + return this.httpClient.get(`${this.configuration.basePath}/twitter/callback`, { params: queryParameters, responseType: responseType, @@ -201,7 +201,7 @@ export class TwitterService { responseType = 'text'; } - return this.httpClient.post(`${this.configuration.basePath}/api/hoods/${encodeURIComponent(String(hoodId))}/twitter/`, + return this.httpClient.post(`${this.configuration.basePath}/hoods/${encodeURIComponent(String(hoodId))}/twitter/`, null, { responseType: responseType, @@ -258,7 +258,7 @@ export class TwitterService { responseType = 'text'; } - return this.httpClient.delete(`${this.configuration.basePath}/api/hoods/${encodeURIComponent(String(hoodId))}/twitter/${encodeURIComponent(String(twitterId))}`, + return this.httpClient.delete(`${this.configuration.basePath}/hoods/${encodeURIComponent(String(hoodId))}/twitter/${encodeURIComponent(String(twitterId))}`, { responseType: responseType, withCredentials: this.configuration.withCredentials, @@ -314,7 +314,7 @@ export class TwitterService { responseType = 'text'; } - return this.httpClient.get(`${this.configuration.basePath}/api/hoods/${encodeURIComponent(String(hoodId))}/twitter/${encodeURIComponent(String(twitterId))}`, + return this.httpClient.get(`${this.configuration.basePath}/hoods/${encodeURIComponent(String(hoodId))}/twitter/${encodeURIComponent(String(twitterId))}`, { responseType: responseType, withCredentials: this.configuration.withCredentials, @@ -366,7 +366,7 @@ export class TwitterService { responseType = 'text'; } - return this.httpClient.get(`${this.configuration.basePath}/api/hoods/${encodeURIComponent(String(hoodId))}/twitter/`, + return this.httpClient.get(`${this.configuration.basePath}/hoods/${encodeURIComponent(String(hoodId))}/twitter/`, { responseType: responseType, withCredentials: this.configuration.withCredentials, @@ -411,7 +411,7 @@ export class TwitterService { responseType = 'text'; } - return this.httpClient.get(`${this.configuration.basePath}/api/hoods/${encodeURIComponent(String(hoodId))}/twitter/public`, + return this.httpClient.get(`${this.configuration.basePath}/hoods/${encodeURIComponent(String(hoodId))}/twitter/public`, { responseType: responseType, withCredentials: this.configuration.withCredentials, @@ -467,7 +467,7 @@ export class TwitterService { responseType = 'text'; } - return this.httpClient.post(`${this.configuration.basePath}/api/hoods/${encodeURIComponent(String(hoodId))}/twitter/${encodeURIComponent(String(twitterId))}/start`, + return this.httpClient.post(`${this.configuration.basePath}/hoods/${encodeURIComponent(String(hoodId))}/twitter/${encodeURIComponent(String(twitterId))}/start`, null, { responseType: responseType, @@ -524,7 +524,7 @@ export class TwitterService { responseType = 'text'; } - return this.httpClient.get(`${this.configuration.basePath}/api/hoods/${encodeURIComponent(String(hoodId))}/twitter/${encodeURIComponent(String(twitterId))}/status`, + return this.httpClient.get(`${this.configuration.basePath}/hoods/${encodeURIComponent(String(hoodId))}/twitter/${encodeURIComponent(String(twitterId))}/status`, { responseType: responseType, withCredentials: this.configuration.withCredentials, @@ -580,7 +580,7 @@ export class TwitterService { responseType = 'text'; } - return this.httpClient.post(`${this.configuration.basePath}/api/hoods/${encodeURIComponent(String(hoodId))}/twitter/${encodeURIComponent(String(twitterId))}/stop`, + return this.httpClient.post(`${this.configuration.basePath}/hoods/${encodeURIComponent(String(hoodId))}/twitter/${encodeURIComponent(String(twitterId))}/stop`, null, { responseType: responseType, 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/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..bc00d4b 100644 --- a/frontend/src/app/core/api/model/models.ts +++ b/frontend/src/app/core/api/model/models.ts @@ -1,8 +1,8 @@ export * from './bodyAccessToken'; export * from './bodyAdmin'; -export * from './bodyAdminLoginAdminLoginPost'; export * from './bodyBadWord'; export * from './bodyHood'; +export * from './bodyLogin'; 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; } -- 2.43.5 From 7fd716cecc96d05824376370d3fbb5522c389c3c Mon Sep 17 00:00:00 2001 From: missytake Date: Sat, 18 Mar 2023 20:58:33 +0100 Subject: [PATCH 12/32] [misc] Added pytest and pytest-aiohttp to test dependencies --- setup.sh | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) 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 -- 2.43.5 From 36638b1c64d29c4d68abc0f95d7641ac6e4a848d Mon Sep 17 00:00:00 2001 From: missytake Date: Sat, 18 Mar 2023 21:00:50 +0100 Subject: [PATCH 13/32] [mastodon] New style: double quotes instead of single quotes --- .../src/kibicara/platforms/mastodon/bot.py | 6 ++-- .../src/kibicara/platforms/mastodon/model.py | 4 +-- .../src/kibicara/platforms/mastodon/webapi.py | 34 +++++++++---------- 3 files changed, 22 insertions(+), 22 deletions(-) diff --git a/backend/src/kibicara/platforms/mastodon/bot.py b/backend/src/kibicara/platforms/mastodon/bot.py index a4fcfc3..82033e2 100644 --- a/backend/src/kibicara/platforms/mastodon/bot.py +++ b/backend/src/kibicara/platforms/mastodon/bot.py @@ -43,14 +43,14 @@ class MastodonBot(Censor): last_seen = int(self.model.last_seen) for status in notifications: try: - status_id = int(status['status']['id']) + status_id = int(status["status"]["id"]) except KeyError: continue # ignore notifications which don't have a status if status_id <= last_seen: continue # toot was already processed in the past if status_id > int(self.model.last_seen): await self.model.update(last_seen=str(status_id)) - text = re.sub(r'<[^>]*>', '', status['status']['content']) + text = re.sub(r"<[^>]*>", "", status["status"]["content"]) text = re.sub( "(?<=^|(?<=[^a-zA-Z0-9-_.]))@([A-Za-z]+[A-Za-z0-9-_]+)", "", text ) @@ -58,7 +58,7 @@ class MastodonBot(Censor): "Mastodon in %s received toot #%s: %s" % (self.model.hood.name, status_id, text) ) - if status['status']['visibility'] == 'public': + if status["status"]["visibility"] == "public": await self.publish(Message(text, toot_id=status_id)) else: await self.publish(Message(text)) diff --git a/backend/src/kibicara/platforms/mastodon/model.py b/backend/src/kibicara/platforms/mastodon/model.py index c7a3140..31de304 100644 --- a/backend/src/kibicara/platforms/mastodon/model.py +++ b/backend/src/kibicara/platforms/mastodon/model.py @@ -15,7 +15,7 @@ class MastodonInstance(Model): client_secret: Text() class Mapping(Mapping): - table_name = 'mastodoninstances' + table_name = "mastodoninstances" class MastodonAccount(Model): @@ -27,4 +27,4 @@ class MastodonAccount(Model): last_seen: Text() class Mapping(Mapping): - table_name = 'mastodonaccounts' + table_name = "mastodonaccounts" diff --git a/backend/src/kibicara/platforms/mastodon/webapi.py b/backend/src/kibicara/platforms/mastodon/webapi.py index bfaa7fc..5c6c237 100644 --- a/backend/src/kibicara/platforms/mastodon/webapi.py +++ b/backend/src/kibicara/platforms/mastodon/webapi.py @@ -55,9 +55,9 @@ twitter_callback_router = APIRouter() @router.get( - '/public', + "/public", # TODO response_model, - operation_id='get_mastodons_public', + operation_id="get_mastodons_public", ) async def mastodon_read_all_public(hood=Depends(get_hood_unauthorized)): mastodonbots = await MastodonAccount.objects.filter(hood=hood).all() @@ -69,28 +69,28 @@ async def mastodon_read_all_public(hood=Depends(get_hood_unauthorized)): @router.get( - '/', + "/", # TODO response_model, - operation_id='get_mastodons', + operation_id="get_mastodons", ) async def mastodon_read_all(hood=Depends(get_hood)): return await MastodonAccount.objects.filter(hood=hood).all() @router.get( - '/{mastodon_id}', + "/{mastodon_id}", # TODO response_model - operation_id='get_mastodon', + operation_id="get_mastodon", ) async def mastodon_read(mastodon=Depends(get_mastodon)): return mastodon @router.delete( - '/{mastodon_id}', + "/{mastodon_id}", status_code=status.HTTP_204_NO_CONTENT, # TODO response_model - operation_id='delete_mastodon', + operation_id="delete_mastodon", ) async def mastodon_delete(mastodon=Depends(get_mastodon)): spawner.stop(mastodon) @@ -99,20 +99,20 @@ async def mastodon_delete(mastodon=Depends(get_mastodon)): @router.get( - '/{mastodon_id}/status', + "/{mastodon_id}/status", status_code=status.HTTP_200_OK, # TODO response_model - operation_id='status_mastodon', + operation_id="status_mastodon", ) async def mastodon_status(mastodon=Depends(get_mastodon)): - return {'status': spawner.get(mastodon).status.name} + return {"status": spawner.get(mastodon).status.name} @router.post( - '/{mastodon_id}/start', + "/{mastodon_id}/start", status_code=status.HTTP_200_OK, # TODO response_model - operation_id='start_mastodon', + operation_id="start_mastodon", ) async def mastodon_start(mastodon=Depends(get_mastodon)): await mastodon.update(enabled=True) @@ -121,10 +121,10 @@ async def mastodon_start(mastodon=Depends(get_mastodon)): @router.post( - '/{mastodon_id}/stop', + "/{mastodon_id}/stop", status_code=status.HTTP_200_OK, # TODO response_model - operation_id='stop_mastodon', + operation_id="stop_mastodon", ) async def mastodon_stop(mastodon=Depends(get_mastodon)): await mastodon.update(enabled=False) @@ -133,10 +133,10 @@ async def mastodon_stop(mastodon=Depends(get_mastodon)): @router.post( - '/', + "/", status_code=status.HTTP_201_CREATED, # TODO response_model - operation_id='create_mastodon', + operation_id="create_mastodon", ) async def mastodon_create(instance_url, username, password, hood=Depends(get_hood)): """Add a Mastodon Account to a Ticketfrei account. -- 2.43.5 From cb88c24e2ea0a397df91919cdcc32e6d85991621 Mon Sep 17 00:00:00 2001 From: missytake Date: Sat, 18 Mar 2023 21:25:11 +0100 Subject: [PATCH 14/32] [mastodon] Change mastodon_create to accept json instead of URL parameters --- .../src/kibicara/platforms/mastodon/webapi.py | 19 +++++++++++-------- 1 file changed, 11 insertions(+), 8 deletions(-) diff --git a/backend/src/kibicara/platforms/mastodon/webapi.py b/backend/src/kibicara/platforms/mastodon/webapi.py index 5c6c237..6647633 100644 --- a/backend/src/kibicara/platforms/mastodon/webapi.py +++ b/backend/src/kibicara/platforms/mastodon/webapi.py @@ -24,6 +24,12 @@ class BodyMastodonPublic(BaseModel): instance: str +class BodyMastodonAccount(BaseModel): + email: str + instance_url: str + password: str + + async def get_mastodon(mastodon_id, hood=Depends(get_hood)): try: return await MastodonAccount.objects.get(id=mastodon_id, hood=hood) @@ -138,24 +144,21 @@ async def mastodon_stop(mastodon=Depends(get_mastodon)): # TODO response_model operation_id="create_mastodon", ) -async def mastodon_create(instance_url, username, password, hood=Depends(get_hood)): +async def mastodon_create(values: BodyMastodonAccount, hood=Depends(get_hood)): """Add a Mastodon Account to a Ticketfrei account. open questions: - do we really get the username + password like this? can the instance_url have different ways of writing? - :param: instance_url: the API base URL of the mastodon server - :param: username: the username of the Mastodon account - :param: password: the password of the Mastodon account + :param: values: a BodyMastodonAccount object in json :param: hood: the hood ORM object """ - instance = await get_mastodon_instance(instance_url) + instance = await get_mastodon_instance(values.instance_url) account = Mastodon( - instance.client_id, instance.client_secret, api_base_url=instance_url + instance.client_id, instance.client_secret, api_base_url=values.instance_url ) try: - access_token = account.log_in(username, password) + access_token = account.log_in(values.email, values.password) except MastodonError: logger.warning("Login to Mastodon failed.", exc_info=True) return # show error to user -- 2.43.5 From f533efee4f97d3c36f5d7bd7f66015a7955a94cc Mon Sep 17 00:00:00 2001 From: missytake Date: Sat, 18 Mar 2023 21:59:23 +0100 Subject: [PATCH 15/32] [mastodon] Return 422 error for invalid input when creating mastodon bot --- backend/setup.cfg | 1 + .../src/kibicara/platforms/mastodon/webapi.py | 24 +++++++++++++++---- 2 files changed, 20 insertions(+), 5 deletions(-) diff --git a/backend/setup.cfg b/backend/setup.cfg index 53d4b20..6f11b83 100644 --- a/backend/setup.cfg +++ b/backend/setup.cfg @@ -35,6 +35,7 @@ install_requires = requests scrypt Mastodon.py + pydantic[email] [options.packages.find] where = src diff --git a/backend/src/kibicara/platforms/mastodon/webapi.py b/backend/src/kibicara/platforms/mastodon/webapi.py index 6647633..7753081 100644 --- a/backend/src/kibicara/platforms/mastodon/webapi.py +++ b/backend/src/kibicara/platforms/mastodon/webapi.py @@ -5,14 +5,14 @@ from fastapi import APIRouter, Depends, HTTPException, Response, status from ormantic.exceptions import NoMatch -from pydantic import BaseModel +from pydantic import BaseModel, validate_email, validator 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, MastodonError +from mastodon import Mastodon, MastodonError, MastodonNetworkError from logging import getLogger @@ -29,6 +29,14 @@ class BodyMastodonAccount(BaseModel): instance_url: str password: str + @validator("email") + def validate_email(cls, value): + return validate_email(value) + + +class HTTPError(BaseModel): + status_code: int + async def get_mastodon(mastodon_id, hood=Depends(get_hood)): try: @@ -141,7 +149,10 @@ async def mastodon_stop(mastodon=Depends(get_mastodon)): @router.post( "/", status_code=status.HTTP_201_CREATED, - # TODO response_model + responses={ + 201: {"model": MastodonAccount}, + 422: {"model": HTTPError, "description": "Invalid Input"}, + }, operation_id="create_mastodon", ) async def mastodon_create(values: BodyMastodonAccount, hood=Depends(get_hood)): @@ -153,7 +164,10 @@ async def mastodon_create(values: BodyMastodonAccount, hood=Depends(get_hood)): :param: values: a BodyMastodonAccount object in json :param: hood: the hood ORM object """ - instance = await get_mastodon_instance(values.instance_url) + 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 ) @@ -161,7 +175,7 @@ async def mastodon_create(values: BodyMastodonAccount, hood=Depends(get_hood)): access_token = account.log_in(values.email, values.password) except MastodonError: logger.warning("Login to Mastodon failed.", exc_info=True) - return # show error to user + return HTTPException(422, "Login to Mastodon failed") return await MastodonAccount.objects.create( hood=hood, instance=instance, -- 2.43.5 From a548c2febcb7d86e2c4eeb9876a101dcde58aeb5 Mon Sep 17 00:00:00 2001 From: missytake Date: Sat, 18 Mar 2023 22:00:17 +0100 Subject: [PATCH 16/32] [tests] Testing the mastodon_create API endpoint --- .../test_api_mastodon_create_bot.py | 106 ++++++++++++++++++ 1 file changed, 106 insertions(+) create mode 100644 backend/tests/tests_mastodon/test_api_mastodon_create_bot.py 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 -- 2.43.5 From 66fff6fd7d0625392e50da2ff4bdda630e70efa5 Mon Sep 17 00:00:00 2001 From: ogdbd3h5qze42igcv8wcrqk3 Date: Sat, 18 Mar 2023 18:37:35 +0100 Subject: [PATCH 17/32] [frontend] Fix openapi-generator run for mastodon --- .../src/app/core/api/api/admin.service.ts | 16 +++++------ .../src/app/core/api/api/badwords.service.ts | 10 +++---- .../src/app/core/api/api/email.service.ts | 28 +++++++++---------- .../src/app/core/api/api/hoods.service.ts | 10 +++---- .../src/app/core/api/api/mastodon.service.ts | 16 +++++------ .../src/app/core/api/api/telegram.service.ts | 18 ++++++------ frontend/src/app/core/api/api/test.service.ts | 12 ++++---- .../src/app/core/api/api/triggers.service.ts | 10 +++---- .../src/app/core/api/api/twitter.service.ts | 18 ++++++------ 9 files changed, 69 insertions(+), 69 deletions(-) diff --git a/frontend/src/app/core/api/api/admin.service.ts b/frontend/src/app/core/api/api/admin.service.ts index 665a6f0..c8afa9f 100644 --- a/frontend/src/app/core/api/api/admin.service.ts +++ b/frontend/src/app/core/api/api/admin.service.ts @@ -136,7 +136,7 @@ export class AdminService { responseType = 'text'; } - return this.httpClient.post(`${this.configuration.basePath}/admin/confirm/${encodeURIComponent(String(registerToken))}`, + return this.httpClient.post(`${this.configuration.basePath}/api/admin/confirm/${encodeURIComponent(String(registerToken))}`, null, { responseType: responseType, @@ -195,7 +195,7 @@ export class AdminService { responseType = 'text'; } - return this.httpClient.post(`${this.configuration.basePath}/admin/reset/${encodeURIComponent(String(resetToken))}`, + return this.httpClient.post(`${this.configuration.basePath}/api/admin/reset/${encodeURIComponent(String(resetToken))}`, bodyPassword, { responseType: responseType, @@ -243,7 +243,7 @@ export class AdminService { responseType = 'text'; } - return this.httpClient.delete(`${this.configuration.basePath}/admin/`, + return this.httpClient.delete(`${this.configuration.basePath}/api/admin/`, { responseType: responseType, withCredentials: this.configuration.withCredentials, @@ -292,7 +292,7 @@ export class AdminService { responseType = 'text'; } - return this.httpClient.get(`${this.configuration.basePath}/admin/`, + return this.httpClient.get(`${this.configuration.basePath}/api/admin/`, { responseType: responseType, withCredentials: this.configuration.withCredentials, @@ -341,7 +341,7 @@ export class AdminService { responseType = 'text'; } - return this.httpClient.get(`${this.configuration.basePath}/admin/hoods/`, + return this.httpClient.get(`${this.configuration.basePath}/api/admin/hoods/`, { responseType: responseType, withCredentials: this.configuration.withCredentials, @@ -429,7 +429,7 @@ export class AdminService { responseType = 'text'; } - return this.httpClient.post(`${this.configuration.basePath}/admin/login/`, + return this.httpClient.post(`${this.configuration.basePath}/api/admin/login/`, convertFormParamsToString ? formParams.toString() : formParams, { responseType: responseType, @@ -485,7 +485,7 @@ export class AdminService { responseType = 'text'; } - return this.httpClient.post(`${this.configuration.basePath}/admin/register/`, + return this.httpClient.post(`${this.configuration.basePath}/api/admin/register/`, bodyAdmin, { responseType: responseType, @@ -541,7 +541,7 @@ export class AdminService { responseType = 'text'; } - return this.httpClient.post(`${this.configuration.basePath}/admin/reset/`, + return this.httpClient.post(`${this.configuration.basePath}/api/admin/reset/`, kibicaraWebapiAdminBodyEmail, { responseType: responseType, diff --git a/frontend/src/app/core/api/api/badwords.service.ts b/frontend/src/app/core/api/api/badwords.service.ts index da66dfa..dea8e24 100644 --- a/frontend/src/app/core/api/api/badwords.service.ts +++ b/frontend/src/app/core/api/api/badwords.service.ts @@ -140,7 +140,7 @@ export class BadwordsService { responseType = 'text'; } - return this.httpClient.post(`${this.configuration.basePath}/hoods/${encodeURIComponent(String(hoodId))}/badwords/`, + return this.httpClient.post(`${this.configuration.basePath}/api/hoods/${encodeURIComponent(String(hoodId))}/badwords/`, bodyBadWord, { responseType: responseType, @@ -198,7 +198,7 @@ export class BadwordsService { responseType = 'text'; } - return this.httpClient.delete(`${this.configuration.basePath}/hoods/${encodeURIComponent(String(hoodId))}/badwords/${encodeURIComponent(String(badwordId))}`, + return this.httpClient.delete(`${this.configuration.basePath}/api/hoods/${encodeURIComponent(String(hoodId))}/badwords/${encodeURIComponent(String(badwordId))}`, { responseType: responseType, withCredentials: this.configuration.withCredentials, @@ -255,7 +255,7 @@ export class BadwordsService { responseType = 'text'; } - return this.httpClient.get(`${this.configuration.basePath}/hoods/${encodeURIComponent(String(hoodId))}/badwords/${encodeURIComponent(String(badwordId))}`, + return this.httpClient.get(`${this.configuration.basePath}/api/hoods/${encodeURIComponent(String(hoodId))}/badwords/${encodeURIComponent(String(badwordId))}`, { responseType: responseType, withCredentials: this.configuration.withCredentials, @@ -308,7 +308,7 @@ export class BadwordsService { responseType = 'text'; } - return this.httpClient.get(`${this.configuration.basePath}/hoods/${encodeURIComponent(String(hoodId))}/badwords/`, + return this.httpClient.get(`${this.configuration.basePath}/api/hoods/${encodeURIComponent(String(hoodId))}/badwords/`, { responseType: responseType, withCredentials: this.configuration.withCredentials, @@ -378,7 +378,7 @@ export class BadwordsService { responseType = 'text'; } - return this.httpClient.put(`${this.configuration.basePath}/hoods/${encodeURIComponent(String(hoodId))}/badwords/${encodeURIComponent(String(badwordId))}`, + return this.httpClient.put(`${this.configuration.basePath}/api/hoods/${encodeURIComponent(String(hoodId))}/badwords/${encodeURIComponent(String(badwordId))}`, bodyBadWord, { responseType: responseType, diff --git a/frontend/src/app/core/api/api/email.service.ts b/frontend/src/app/core/api/api/email.service.ts index 526622c..c5c4e94 100644 --- a/frontend/src/app/core/api/api/email.service.ts +++ b/frontend/src/app/core/api/api/email.service.ts @@ -126,7 +126,7 @@ export class EmailService { responseType = 'text'; } - return this.httpClient.post(`${this.configuration.basePath}/hoods/${encodeURIComponent(String(hoodId))}/email/subscribe/confirm/${encodeURIComponent(String(token))}`, + return this.httpClient.post(`${this.configuration.basePath}/api/hoods/${encodeURIComponent(String(hoodId))}/email/subscribe/confirm/${encodeURIComponent(String(token))}`, null, { responseType: responseType, @@ -193,7 +193,7 @@ export class EmailService { responseType = 'text'; } - return this.httpClient.post(`${this.configuration.basePath}/hoods/${encodeURIComponent(String(hoodId))}/email/`, + return this.httpClient.post(`${this.configuration.basePath}/api/hoods/${encodeURIComponent(String(hoodId))}/email/`, kibicaraPlatformsEmailWebapiBodyEmail, { responseType: responseType, @@ -251,7 +251,7 @@ export class EmailService { responseType = 'text'; } - return this.httpClient.delete(`${this.configuration.basePath}/hoods/${encodeURIComponent(String(hoodId))}/email/${encodeURIComponent(String(emailId))}`, + return this.httpClient.delete(`${this.configuration.basePath}/api/hoods/${encodeURIComponent(String(hoodId))}/email/${encodeURIComponent(String(emailId))}`, { responseType: responseType, withCredentials: this.configuration.withCredentials, @@ -307,7 +307,7 @@ export class EmailService { responseType = 'text'; } - return this.httpClient.get(`${this.configuration.basePath}/hoods/${encodeURIComponent(String(hoodId))}/email/${encodeURIComponent(String(emailId))}`, + return this.httpClient.get(`${this.configuration.basePath}/api/hoods/${encodeURIComponent(String(hoodId))}/email/${encodeURIComponent(String(emailId))}`, { responseType: responseType, withCredentials: this.configuration.withCredentials, @@ -359,7 +359,7 @@ export class EmailService { responseType = 'text'; } - return this.httpClient.get(`${this.configuration.basePath}/hoods/${encodeURIComponent(String(hoodId))}/email/`, + return this.httpClient.get(`${this.configuration.basePath}/api/hoods/${encodeURIComponent(String(hoodId))}/email/`, { responseType: responseType, withCredentials: this.configuration.withCredentials, @@ -404,7 +404,7 @@ export class EmailService { responseType = 'text'; } - return this.httpClient.get(`${this.configuration.basePath}/hoods/${encodeURIComponent(String(hoodId))}/email/public`, + return this.httpClient.get(`${this.configuration.basePath}/api/hoods/${encodeURIComponent(String(hoodId))}/email/public`, { responseType: responseType, withCredentials: this.configuration.withCredentials, @@ -460,7 +460,7 @@ export class EmailService { responseType = 'text'; } - return this.httpClient.get(`${this.configuration.basePath}/hoods/${encodeURIComponent(String(hoodId))}/email/subscribers/${encodeURIComponent(String(subscriberId))}`, + return this.httpClient.get(`${this.configuration.basePath}/api/hoods/${encodeURIComponent(String(hoodId))}/email/subscribers/${encodeURIComponent(String(subscriberId))}`, { responseType: responseType, withCredentials: this.configuration.withCredentials, @@ -512,7 +512,7 @@ export class EmailService { responseType = 'text'; } - return this.httpClient.get(`${this.configuration.basePath}/hoods/${encodeURIComponent(String(hoodId))}/email/subscribers/`, + return this.httpClient.get(`${this.configuration.basePath}/api/hoods/${encodeURIComponent(String(hoodId))}/email/subscribers/`, { responseType: responseType, withCredentials: this.configuration.withCredentials, @@ -571,7 +571,7 @@ export class EmailService { responseType = 'text'; } - return this.httpClient.post(`${this.configuration.basePath}/hoods/${encodeURIComponent(String(hoodId))}/email/messages/`, + return this.httpClient.post(`${this.configuration.basePath}/api/hoods/${encodeURIComponent(String(hoodId))}/email/messages/`, kibicaraPlatformsEmailWebapiBodyMessage, { responseType: responseType, @@ -624,7 +624,7 @@ export class EmailService { responseType = 'text'; } - return this.httpClient.post(`${this.configuration.basePath}/hoods/${encodeURIComponent(String(hoodId))}/email/start`, + return this.httpClient.post(`${this.configuration.basePath}/api/hoods/${encodeURIComponent(String(hoodId))}/email/start`, null, { responseType: responseType, @@ -677,7 +677,7 @@ export class EmailService { responseType = 'text'; } - return this.httpClient.get(`${this.configuration.basePath}/hoods/${encodeURIComponent(String(hoodId))}/email/status`, + return this.httpClient.get(`${this.configuration.basePath}/api/hoods/${encodeURIComponent(String(hoodId))}/email/status`, { responseType: responseType, withCredentials: this.configuration.withCredentials, @@ -729,7 +729,7 @@ export class EmailService { responseType = 'text'; } - return this.httpClient.post(`${this.configuration.basePath}/hoods/${encodeURIComponent(String(hoodId))}/email/stop`, + return this.httpClient.post(`${this.configuration.basePath}/api/hoods/${encodeURIComponent(String(hoodId))}/email/stop`, null, { responseType: responseType, @@ -789,7 +789,7 @@ export class EmailService { responseType = 'text'; } - return this.httpClient.post(`${this.configuration.basePath}/hoods/${encodeURIComponent(String(hoodId))}/email/subscribe/`, + return this.httpClient.post(`${this.configuration.basePath}/api/hoods/${encodeURIComponent(String(hoodId))}/email/subscribe/`, bodySubscriber, { responseType: responseType, @@ -840,7 +840,7 @@ export class EmailService { responseType = 'text'; } - return this.httpClient.delete(`${this.configuration.basePath}/hoods/${encodeURIComponent(String(hoodId))}/email/unsubscribe/${encodeURIComponent(String(token))}`, + return this.httpClient.delete(`${this.configuration.basePath}/api/hoods/${encodeURIComponent(String(hoodId))}/email/unsubscribe/${encodeURIComponent(String(token))}`, { responseType: responseType, withCredentials: this.configuration.withCredentials, diff --git a/frontend/src/app/core/api/api/hoods.service.ts b/frontend/src/app/core/api/api/hoods.service.ts index 583c40b..30c4c8d 100644 --- a/frontend/src/app/core/api/api/hoods.service.ts +++ b/frontend/src/app/core/api/api/hoods.service.ts @@ -136,7 +136,7 @@ export class HoodsService { responseType = 'text'; } - return this.httpClient.post(`${this.configuration.basePath}/hoods/`, + return this.httpClient.post(`${this.configuration.basePath}/api/hoods/`, bodyHood, { responseType: responseType, @@ -190,7 +190,7 @@ export class HoodsService { responseType = 'text'; } - return this.httpClient.delete(`${this.configuration.basePath}/hoods/${encodeURIComponent(String(hoodId))}`, + return this.httpClient.delete(`${this.configuration.basePath}/api/hoods/${encodeURIComponent(String(hoodId))}`, { responseType: responseType, withCredentials: this.configuration.withCredentials, @@ -236,7 +236,7 @@ export class HoodsService { responseType = 'text'; } - return this.httpClient.get(`${this.configuration.basePath}/hoods/${encodeURIComponent(String(hoodId))}`, + return this.httpClient.get(`${this.configuration.basePath}/api/hoods/${encodeURIComponent(String(hoodId))}`, { responseType: responseType, withCredentials: this.configuration.withCredentials, @@ -278,7 +278,7 @@ export class HoodsService { responseType = 'text'; } - return this.httpClient.get(`${this.configuration.basePath}/hoods/`, + return this.httpClient.get(`${this.configuration.basePath}/api/hoods/`, { responseType: responseType, withCredentials: this.configuration.withCredentials, @@ -344,7 +344,7 @@ export class HoodsService { responseType = 'text'; } - return this.httpClient.put(`${this.configuration.basePath}/hoods/${encodeURIComponent(String(hoodId))}`, + return this.httpClient.put(`${this.configuration.basePath}/api/hoods/${encodeURIComponent(String(hoodId))}`, bodyHood, { responseType: responseType, diff --git a/frontend/src/app/core/api/api/mastodon.service.ts b/frontend/src/app/core/api/api/mastodon.service.ts index f937044..2cc0f48 100644 --- a/frontend/src/app/core/api/api/mastodon.service.ts +++ b/frontend/src/app/core/api/api/mastodon.service.ts @@ -152,7 +152,7 @@ export class MastodonService { responseType = 'text'; } - return this.httpClient.post(`${this.configuration.basePath}/hoods/${encodeURIComponent(String(hoodId))}/mastodon/`, + return this.httpClient.post(`${this.configuration.basePath}/api/hoods/${encodeURIComponent(String(hoodId))}/mastodon/`, null, { params: queryParameters, @@ -210,7 +210,7 @@ export class MastodonService { responseType = 'text'; } - return this.httpClient.delete(`${this.configuration.basePath}/hoods/${encodeURIComponent(String(hoodId))}/mastodon/${encodeURIComponent(String(mastodonId))}`, + return this.httpClient.delete(`${this.configuration.basePath}/api/hoods/${encodeURIComponent(String(hoodId))}/mastodon/${encodeURIComponent(String(mastodonId))}`, { responseType: responseType, withCredentials: this.configuration.withCredentials, @@ -266,7 +266,7 @@ export class MastodonService { responseType = 'text'; } - return this.httpClient.get(`${this.configuration.basePath}/hoods/${encodeURIComponent(String(hoodId))}/mastodon/${encodeURIComponent(String(mastodonId))}`, + return this.httpClient.get(`${this.configuration.basePath}/api/hoods/${encodeURIComponent(String(hoodId))}/mastodon/${encodeURIComponent(String(mastodonId))}`, { responseType: responseType, withCredentials: this.configuration.withCredentials, @@ -318,7 +318,7 @@ export class MastodonService { responseType = 'text'; } - return this.httpClient.get(`${this.configuration.basePath}/hoods/${encodeURIComponent(String(hoodId))}/mastodon/`, + return this.httpClient.get(`${this.configuration.basePath}/api/hoods/${encodeURIComponent(String(hoodId))}/mastodon/`, { responseType: responseType, withCredentials: this.configuration.withCredentials, @@ -363,7 +363,7 @@ export class MastodonService { responseType = 'text'; } - return this.httpClient.get(`${this.configuration.basePath}/hoods/${encodeURIComponent(String(hoodId))}/mastodon/public`, + return this.httpClient.get(`${this.configuration.basePath}/api/hoods/${encodeURIComponent(String(hoodId))}/mastodon/public`, { responseType: responseType, withCredentials: this.configuration.withCredentials, @@ -419,7 +419,7 @@ export class MastodonService { responseType = 'text'; } - return this.httpClient.post(`${this.configuration.basePath}/hoods/${encodeURIComponent(String(hoodId))}/mastodon/${encodeURIComponent(String(mastodonId))}/start`, + return this.httpClient.post(`${this.configuration.basePath}/api/hoods/${encodeURIComponent(String(hoodId))}/mastodon/${encodeURIComponent(String(mastodonId))}/start`, null, { responseType: responseType, @@ -476,7 +476,7 @@ export class MastodonService { responseType = 'text'; } - return this.httpClient.get(`${this.configuration.basePath}/hoods/${encodeURIComponent(String(hoodId))}/mastodon/${encodeURIComponent(String(mastodonId))}/status`, + return this.httpClient.get(`${this.configuration.basePath}/api/hoods/${encodeURIComponent(String(hoodId))}/mastodon/${encodeURIComponent(String(mastodonId))}/status`, { responseType: responseType, withCredentials: this.configuration.withCredentials, @@ -532,7 +532,7 @@ export class MastodonService { responseType = 'text'; } - return this.httpClient.post(`${this.configuration.basePath}/hoods/${encodeURIComponent(String(hoodId))}/mastodon/${encodeURIComponent(String(mastodonId))}/stop`, + return this.httpClient.post(`${this.configuration.basePath}/api/hoods/${encodeURIComponent(String(hoodId))}/mastodon/${encodeURIComponent(String(mastodonId))}/stop`, null, { responseType: responseType, diff --git a/frontend/src/app/core/api/api/telegram.service.ts b/frontend/src/app/core/api/api/telegram.service.ts index 3167896..ba88547 100644 --- a/frontend/src/app/core/api/api/telegram.service.ts +++ b/frontend/src/app/core/api/api/telegram.service.ts @@ -139,7 +139,7 @@ export class TelegramService { responseType = 'text'; } - return this.httpClient.post(`${this.configuration.basePath}/hoods/${encodeURIComponent(String(hoodId))}/telegram/`, + return this.httpClient.post(`${this.configuration.basePath}/api/hoods/${encodeURIComponent(String(hoodId))}/telegram/`, bodyTelegram, { responseType: responseType, @@ -196,7 +196,7 @@ export class TelegramService { responseType = 'text'; } - return this.httpClient.delete(`${this.configuration.basePath}/hoods/${encodeURIComponent(String(hoodId))}/telegram/${encodeURIComponent(String(telegramId))}`, + return this.httpClient.delete(`${this.configuration.basePath}/api/hoods/${encodeURIComponent(String(hoodId))}/telegram/${encodeURIComponent(String(telegramId))}`, { responseType: responseType, withCredentials: this.configuration.withCredentials, @@ -252,7 +252,7 @@ export class TelegramService { responseType = 'text'; } - return this.httpClient.get(`${this.configuration.basePath}/hoods/${encodeURIComponent(String(hoodId))}/telegram/${encodeURIComponent(String(telegramId))}`, + return this.httpClient.get(`${this.configuration.basePath}/api/hoods/${encodeURIComponent(String(hoodId))}/telegram/${encodeURIComponent(String(telegramId))}`, { responseType: responseType, withCredentials: this.configuration.withCredentials, @@ -304,7 +304,7 @@ export class TelegramService { responseType = 'text'; } - return this.httpClient.get(`${this.configuration.basePath}/hoods/${encodeURIComponent(String(hoodId))}/telegram/`, + return this.httpClient.get(`${this.configuration.basePath}/api/hoods/${encodeURIComponent(String(hoodId))}/telegram/`, { responseType: responseType, withCredentials: this.configuration.withCredentials, @@ -349,7 +349,7 @@ export class TelegramService { responseType = 'text'; } - return this.httpClient.get(`${this.configuration.basePath}/hoods/${encodeURIComponent(String(hoodId))}/telegram/public`, + return this.httpClient.get(`${this.configuration.basePath}/api/hoods/${encodeURIComponent(String(hoodId))}/telegram/public`, { responseType: responseType, withCredentials: this.configuration.withCredentials, @@ -405,7 +405,7 @@ export class TelegramService { responseType = 'text'; } - return this.httpClient.post(`${this.configuration.basePath}/hoods/${encodeURIComponent(String(hoodId))}/telegram/${encodeURIComponent(String(telegramId))}/start`, + return this.httpClient.post(`${this.configuration.basePath}/api/hoods/${encodeURIComponent(String(hoodId))}/telegram/${encodeURIComponent(String(telegramId))}/start`, null, { responseType: responseType, @@ -462,7 +462,7 @@ export class TelegramService { responseType = 'text'; } - return this.httpClient.get(`${this.configuration.basePath}/hoods/${encodeURIComponent(String(hoodId))}/telegram/${encodeURIComponent(String(telegramId))}/status`, + return this.httpClient.get(`${this.configuration.basePath}/api/hoods/${encodeURIComponent(String(hoodId))}/telegram/${encodeURIComponent(String(telegramId))}/status`, { responseType: responseType, withCredentials: this.configuration.withCredentials, @@ -518,7 +518,7 @@ export class TelegramService { responseType = 'text'; } - return this.httpClient.post(`${this.configuration.basePath}/hoods/${encodeURIComponent(String(hoodId))}/telegram/${encodeURIComponent(String(telegramId))}/stop`, + return this.httpClient.post(`${this.configuration.basePath}/api/hoods/${encodeURIComponent(String(hoodId))}/telegram/${encodeURIComponent(String(telegramId))}/stop`, null, { responseType: responseType, @@ -588,7 +588,7 @@ export class TelegramService { responseType = 'text'; } - return this.httpClient.put(`${this.configuration.basePath}/hoods/${encodeURIComponent(String(hoodId))}/telegram/${encodeURIComponent(String(telegramId))}`, + return this.httpClient.put(`${this.configuration.basePath}/api/hoods/${encodeURIComponent(String(hoodId))}/telegram/${encodeURIComponent(String(telegramId))}`, bodyTelegram, { responseType: responseType, diff --git a/frontend/src/app/core/api/api/test.service.ts b/frontend/src/app/core/api/api/test.service.ts index b207662..e5a5424 100644 --- a/frontend/src/app/core/api/api/test.service.ts +++ b/frontend/src/app/core/api/api/test.service.ts @@ -126,7 +126,7 @@ export class TestService { responseType = 'text'; } - return this.httpClient.post(`${this.configuration.basePath}/hoods/${encodeURIComponent(String(hoodId))}/test/`, + return this.httpClient.post(`${this.configuration.basePath}/api/hoods/${encodeURIComponent(String(hoodId))}/test/`, null, { responseType: responseType, @@ -183,7 +183,7 @@ export class TestService { responseType = 'text'; } - return this.httpClient.delete(`${this.configuration.basePath}/hoods/${encodeURIComponent(String(hoodId))}/test/${encodeURIComponent(String(testId))}`, + return this.httpClient.delete(`${this.configuration.basePath}/api/hoods/${encodeURIComponent(String(hoodId))}/test/${encodeURIComponent(String(testId))}`, { responseType: responseType, withCredentials: this.configuration.withCredentials, @@ -252,7 +252,7 @@ export class TestService { responseType = 'text'; } - return this.httpClient.post(`${this.configuration.basePath}/hoods/${encodeURIComponent(String(hoodId))}/test/${encodeURIComponent(String(testId))}/messages/`, + return this.httpClient.post(`${this.configuration.basePath}/api/hoods/${encodeURIComponent(String(hoodId))}/test/${encodeURIComponent(String(testId))}/messages/`, kibicaraPlatformsTestWebapiBodyMessage, { responseType: responseType, @@ -309,7 +309,7 @@ export class TestService { responseType = 'text'; } - return this.httpClient.get(`${this.configuration.basePath}/hoods/${encodeURIComponent(String(hoodId))}/test/${encodeURIComponent(String(testId))}/messages/`, + return this.httpClient.get(`${this.configuration.basePath}/api/hoods/${encodeURIComponent(String(hoodId))}/test/${encodeURIComponent(String(testId))}/messages/`, { responseType: responseType, withCredentials: this.configuration.withCredentials, @@ -361,7 +361,7 @@ export class TestService { responseType = 'text'; } - return this.httpClient.get(`${this.configuration.basePath}/hoods/${encodeURIComponent(String(hoodId))}/test/`, + return this.httpClient.get(`${this.configuration.basePath}/api/hoods/${encodeURIComponent(String(hoodId))}/test/`, { responseType: responseType, withCredentials: this.configuration.withCredentials, @@ -417,7 +417,7 @@ export class TestService { responseType = 'text'; } - return this.httpClient.get(`${this.configuration.basePath}/hoods/${encodeURIComponent(String(hoodId))}/test/${encodeURIComponent(String(testId))}`, + return this.httpClient.get(`${this.configuration.basePath}/api/hoods/${encodeURIComponent(String(hoodId))}/test/${encodeURIComponent(String(testId))}`, { responseType: responseType, withCredentials: this.configuration.withCredentials, diff --git a/frontend/src/app/core/api/api/triggers.service.ts b/frontend/src/app/core/api/api/triggers.service.ts index 28a9f01..8dc0e7c 100644 --- a/frontend/src/app/core/api/api/triggers.service.ts +++ b/frontend/src/app/core/api/api/triggers.service.ts @@ -140,7 +140,7 @@ export class TriggersService { responseType = 'text'; } - return this.httpClient.post(`${this.configuration.basePath}/hoods/${encodeURIComponent(String(hoodId))}/triggers/`, + return this.httpClient.post(`${this.configuration.basePath}/api/hoods/${encodeURIComponent(String(hoodId))}/triggers/`, bodyTrigger, { responseType: responseType, @@ -198,7 +198,7 @@ export class TriggersService { responseType = 'text'; } - return this.httpClient.delete(`${this.configuration.basePath}/hoods/${encodeURIComponent(String(hoodId))}/triggers/${encodeURIComponent(String(triggerId))}`, + return this.httpClient.delete(`${this.configuration.basePath}/api/hoods/${encodeURIComponent(String(hoodId))}/triggers/${encodeURIComponent(String(triggerId))}`, { responseType: responseType, withCredentials: this.configuration.withCredentials, @@ -255,7 +255,7 @@ export class TriggersService { responseType = 'text'; } - return this.httpClient.get(`${this.configuration.basePath}/hoods/${encodeURIComponent(String(hoodId))}/triggers/${encodeURIComponent(String(triggerId))}`, + return this.httpClient.get(`${this.configuration.basePath}/api/hoods/${encodeURIComponent(String(hoodId))}/triggers/${encodeURIComponent(String(triggerId))}`, { responseType: responseType, withCredentials: this.configuration.withCredentials, @@ -308,7 +308,7 @@ export class TriggersService { responseType = 'text'; } - return this.httpClient.get(`${this.configuration.basePath}/hoods/${encodeURIComponent(String(hoodId))}/triggers/`, + return this.httpClient.get(`${this.configuration.basePath}/api/hoods/${encodeURIComponent(String(hoodId))}/triggers/`, { responseType: responseType, withCredentials: this.configuration.withCredentials, @@ -378,7 +378,7 @@ export class TriggersService { responseType = 'text'; } - return this.httpClient.put(`${this.configuration.basePath}/hoods/${encodeURIComponent(String(hoodId))}/triggers/${encodeURIComponent(String(triggerId))}`, + return this.httpClient.put(`${this.configuration.basePath}/api/hoods/${encodeURIComponent(String(hoodId))}/triggers/${encodeURIComponent(String(triggerId))}`, bodyTrigger, { responseType: responseType, diff --git a/frontend/src/app/core/api/api/twitter.service.ts b/frontend/src/app/core/api/api/twitter.service.ts index c0d3f1c..c34a019 100644 --- a/frontend/src/app/core/api/api/twitter.service.ts +++ b/frontend/src/app/core/api/api/twitter.service.ts @@ -147,7 +147,7 @@ export class TwitterService { responseType = 'text'; } - return this.httpClient.get(`${this.configuration.basePath}/twitter/callback`, + return this.httpClient.get(`${this.configuration.basePath}/api/twitter/callback`, { params: queryParameters, responseType: responseType, @@ -201,7 +201,7 @@ export class TwitterService { responseType = 'text'; } - return this.httpClient.post(`${this.configuration.basePath}/hoods/${encodeURIComponent(String(hoodId))}/twitter/`, + return this.httpClient.post(`${this.configuration.basePath}/api/hoods/${encodeURIComponent(String(hoodId))}/twitter/`, null, { responseType: responseType, @@ -258,7 +258,7 @@ export class TwitterService { responseType = 'text'; } - return this.httpClient.delete(`${this.configuration.basePath}/hoods/${encodeURIComponent(String(hoodId))}/twitter/${encodeURIComponent(String(twitterId))}`, + return this.httpClient.delete(`${this.configuration.basePath}/api/hoods/${encodeURIComponent(String(hoodId))}/twitter/${encodeURIComponent(String(twitterId))}`, { responseType: responseType, withCredentials: this.configuration.withCredentials, @@ -314,7 +314,7 @@ export class TwitterService { responseType = 'text'; } - return this.httpClient.get(`${this.configuration.basePath}/hoods/${encodeURIComponent(String(hoodId))}/twitter/${encodeURIComponent(String(twitterId))}`, + return this.httpClient.get(`${this.configuration.basePath}/api/hoods/${encodeURIComponent(String(hoodId))}/twitter/${encodeURIComponent(String(twitterId))}`, { responseType: responseType, withCredentials: this.configuration.withCredentials, @@ -366,7 +366,7 @@ export class TwitterService { responseType = 'text'; } - return this.httpClient.get(`${this.configuration.basePath}/hoods/${encodeURIComponent(String(hoodId))}/twitter/`, + return this.httpClient.get(`${this.configuration.basePath}/api/hoods/${encodeURIComponent(String(hoodId))}/twitter/`, { responseType: responseType, withCredentials: this.configuration.withCredentials, @@ -411,7 +411,7 @@ export class TwitterService { responseType = 'text'; } - return this.httpClient.get(`${this.configuration.basePath}/hoods/${encodeURIComponent(String(hoodId))}/twitter/public`, + return this.httpClient.get(`${this.configuration.basePath}/api/hoods/${encodeURIComponent(String(hoodId))}/twitter/public`, { responseType: responseType, withCredentials: this.configuration.withCredentials, @@ -467,7 +467,7 @@ export class TwitterService { responseType = 'text'; } - return this.httpClient.post(`${this.configuration.basePath}/hoods/${encodeURIComponent(String(hoodId))}/twitter/${encodeURIComponent(String(twitterId))}/start`, + return this.httpClient.post(`${this.configuration.basePath}/api/hoods/${encodeURIComponent(String(hoodId))}/twitter/${encodeURIComponent(String(twitterId))}/start`, null, { responseType: responseType, @@ -524,7 +524,7 @@ export class TwitterService { responseType = 'text'; } - return this.httpClient.get(`${this.configuration.basePath}/hoods/${encodeURIComponent(String(hoodId))}/twitter/${encodeURIComponent(String(twitterId))}/status`, + return this.httpClient.get(`${this.configuration.basePath}/api/hoods/${encodeURIComponent(String(hoodId))}/twitter/${encodeURIComponent(String(twitterId))}/status`, { responseType: responseType, withCredentials: this.configuration.withCredentials, @@ -580,7 +580,7 @@ export class TwitterService { responseType = 'text'; } - return this.httpClient.post(`${this.configuration.basePath}/hoods/${encodeURIComponent(String(hoodId))}/twitter/${encodeURIComponent(String(twitterId))}/stop`, + return this.httpClient.post(`${this.configuration.basePath}/api/hoods/${encodeURIComponent(String(hoodId))}/twitter/${encodeURIComponent(String(twitterId))}/stop`, null, { responseType: responseType, -- 2.43.5 From dfd17aa27c823a539ad73990c51c81b7987d03b5 Mon Sep 17 00:00:00 2001 From: ogdbd3h5qze42igcv8wcrqk3 Date: Sun, 19 Mar 2023 01:44:30 +0100 Subject: [PATCH 18/32] [mastodon] Fix locking issue with synchronous Mastodon.py and replace last_seen with notification_dismiss --- .../src/kibicara/platforms/mastodon/bot.py | 58 +++++++++++++------ .../src/kibicara/platforms/mastodon/model.py | 2 +- .../src/kibicara/platforms/mastodon/webapi.py | 31 +++++----- 3 files changed, 58 insertions(+), 33 deletions(-) diff --git a/backend/src/kibicara/platforms/mastodon/bot.py b/backend/src/kibicara/platforms/mastodon/bot.py index 82033e2..dad7de1 100644 --- a/backend/src/kibicara/platforms/mastodon/bot.py +++ b/backend/src/kibicara/platforms/mastodon/bot.py @@ -4,15 +4,15 @@ # # 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 -from logging import getLogger - logger = getLogger(__name__) @@ -21,35 +21,49 @@ class MastodonBot(Censor): 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 Mastodon.objects.filter(hood=hood).all(): + await mastodon.delete() async def run(self): - 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, - ) - await gather(self.poll(), self.push()) + 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 = self.account.notifications() + 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 - last_seen = int(self.model.last_seen) for status in notifications: try: status_id = int(status["status"]["id"]) except KeyError: continue # ignore notifications which don't have a status - if status_id <= last_seen: - continue # toot was already processed in the past - if status_id > int(self.model.last_seen): - await self.model.update(last_seen=str(status_id)) text = re.sub(r"<[^>]*>", "", status["status"]["content"]) text = re.sub( "(?<=^|(?<=[^a-zA-Z0-9-_.]))@([A-Za-z]+[A-Za-z0-9-_]+)", "", text @@ -62,6 +76,10 @@ class MastodonBot(Censor): 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.""" @@ -69,10 +87,14 @@ class MastodonBot(Censor): message = await self.receive() if hasattr(message, "tood_id"): logger.debug("Boosting post %s: %s" % (message.tood_id, message.text)) - self.account.status_reblog(message.tood_id) + await get_event_loop().run_in_executor( + None, self.account.status_reblog, message.tood_id + ) else: logger.debug("Posting message: %s" % (message.text,)) - self.account.status_post(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 index 31de304..a6a1d34 100644 --- a/backend/src/kibicara/platforms/mastodon/model.py +++ b/backend/src/kibicara/platforms/mastodon/model.py @@ -23,8 +23,8 @@ class MastodonAccount(Model): hood: ForeignKey(Hood) instance: ForeignKey(MastodonInstance) access_token: Text() + username: Text(allow_null=True) = None enabled: Boolean() = False - last_seen: Text() class Mapping(Mapping): table_name = "mastodonaccounts" diff --git a/backend/src/kibicara/platforms/mastodon/webapi.py b/backend/src/kibicara/platforms/mastodon/webapi.py index 7753081..8119297 100644 --- a/backend/src/kibicara/platforms/mastodon/webapi.py +++ b/backend/src/kibicara/platforms/mastodon/webapi.py @@ -3,9 +3,11 @@ # # 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 @@ -13,6 +15,7 @@ from kibicara.platforms.mastodon.model import MastodonAccount, MastodonInstance from kibicara.webapi.hoods import get_hood, get_hood_unauthorized from mastodon import Mastodon, MastodonError, MastodonNetworkError +from mastodon.errors import MastodonIllegalArgumentError from logging import getLogger @@ -149,10 +152,7 @@ async def mastodon_stop(mastodon=Depends(get_mastodon)): @router.post( "/", status_code=status.HTTP_201_CREATED, - responses={ - 201: {"model": MastodonAccount}, - 422: {"model": HTTPError, "description": "Invalid Input"}, - }, + # TODO response_model operation_id="create_mastodon", ) async def mastodon_create(values: BodyMastodonAccount, hood=Depends(get_hood)): @@ -172,14 +172,17 @@ async def mastodon_create(values: BodyMastodonAccount, hood=Depends(get_hood)): instance.client_id, instance.client_secret, api_base_url=values.instance_url ) try: - access_token = account.log_in(values.email, values.password) - except MastodonError: + access_token = await get_event_loop().run_in_executor( + None, account.log_in, username, 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) - return HTTPException(422, "Login to Mastodon failed") - return await MastodonAccount.objects.create( - hood=hood, - instance=instance, - access_token=access_token, - enabled=True, - last_seen="0", - ) + raise HTTPException(status_code=status.HTTP_422_INVALID_INPUT) + except IntegrityError: + raise HTTPException(status_code=status.HTTP_409_CONFLICT) -- 2.43.5 From a61c48e99e42fc94b0a499a19dfabc8009b8727b Mon Sep 17 00:00:00 2001 From: missytake Date: Sun, 19 Mar 2023 12:39:41 +0100 Subject: [PATCH 19/32] [mastodon] Fix tests --- backend/src/kibicara/platforms/mastodon/bot.py | 2 +- backend/src/kibicara/platforms/mastodon/webapi.py | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/backend/src/kibicara/platforms/mastodon/bot.py b/backend/src/kibicara/platforms/mastodon/bot.py index dad7de1..0fdad53 100644 --- a/backend/src/kibicara/platforms/mastodon/bot.py +++ b/backend/src/kibicara/platforms/mastodon/bot.py @@ -26,7 +26,7 @@ class MastodonBot(Censor): @classmethod async def destroy_hood(cls, hood): """Removes all its database entries.""" - for mastodon in await Mastodon.objects.filter(hood=hood).all(): + for mastodon in await MastodonAccount.objects.filter(hood=hood).all(): await mastodon.delete() async def run(self): diff --git a/backend/src/kibicara/platforms/mastodon/webapi.py b/backend/src/kibicara/platforms/mastodon/webapi.py index 8119297..c6dc386 100644 --- a/backend/src/kibicara/platforms/mastodon/webapi.py +++ b/backend/src/kibicara/platforms/mastodon/webapi.py @@ -14,7 +14,7 @@ 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, MastodonError, MastodonNetworkError +from mastodon import Mastodon, MastodonNetworkError from mastodon.errors import MastodonIllegalArgumentError from logging import getLogger @@ -173,7 +173,7 @@ async def mastodon_create(values: BodyMastodonAccount, hood=Depends(get_hood)): ) try: access_token = await get_event_loop().run_in_executor( - None, account.log_in, username, password + None, account.log_in, values.email, values.password ) logger.debug(f"{access_token}") mastodon = await MastodonAccount.objects.create( -- 2.43.5 From b49c4767a0ff5f2f156dcc7c807410048213971c Mon Sep 17 00:00:00 2001 From: missytake Date: Sun, 19 Mar 2023 13:20:14 +0100 Subject: [PATCH 20/32] [mastodon] Remove redundant error class --- backend/src/kibicara/platforms/mastodon/webapi.py | 4 ---- 1 file changed, 4 deletions(-) diff --git a/backend/src/kibicara/platforms/mastodon/webapi.py b/backend/src/kibicara/platforms/mastodon/webapi.py index c6dc386..025acb0 100644 --- a/backend/src/kibicara/platforms/mastodon/webapi.py +++ b/backend/src/kibicara/platforms/mastodon/webapi.py @@ -37,10 +37,6 @@ class BodyMastodonAccount(BaseModel): return validate_email(value) -class HTTPError(BaseModel): - status_code: int - - async def get_mastodon(mastodon_id, hood=Depends(get_hood)): try: return await MastodonAccount.objects.get(id=mastodon_id, hood=hood) -- 2.43.5 From 16c325a9cbffd582cb4ba573f370c8d6e16d55d3 Mon Sep 17 00:00:00 2001 From: missytake Date: Sun, 19 Mar 2023 13:47:20 +0100 Subject: [PATCH 21/32] [tests] Test mastodon_delete route --- backend/tests/tests_mastodon/conftest.py | 30 +++++++++++++ .../test_api_mastodon_delete_bot.py | 42 +++++++++++++++++++ 2 files changed, 72 insertions(+) create mode 100644 backend/tests/tests_mastodon/conftest.py create mode 100644 backend/tests/tests_mastodon/test_api_mastodon_delete_bot.py diff --git a/backend/tests/tests_mastodon/conftest.py b/backend/tests/tests_mastodon/conftest.py new file mode 100644 index 0000000..7b3117f --- /dev/null +++ b/backend/tests/tests_mastodon/conftest.py @@ -0,0 +1,30 @@ +# 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(event_loop, hood_id): + hood = event_loop.run_until_complete(Hood.objects.get(id=hood_id)) + instance = event_loop.run_until_complete( + MastodonInstance.objects.create( + name="inst4nce", + client_id="cl13nt_id", + client_secret="cl13nt_s3cr3t", + ) + ) + return event_loop.run_until_complete( + MastodonAccount.objects.create( + hood=hood, + instance=instance, + access_token="t0k3n", + enabled=True, + username="us3r", + ) + ) 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..5123639 --- /dev/null +++ b/backend/tests/tests_mastodon/test_api_mastodon_delete_bot.py @@ -0,0 +1,42 @@ +# 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, auth_header): + response = client.delete( + '/api/hoods/{0}/mastodon/{1}'.format(mastodon.hood.id, mastodon.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.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): + response = client.delete( + '/api/hoods/{0}/mastodon/{1}'.format(mastodon.hood.id, mastodon.id) + ) + assert response.status_code == status.HTTP_401_UNAUTHORIZED -- 2.43.5 From 90469a052aeb64e11eab44ad3af93759909e2cff Mon Sep 17 00:00:00 2001 From: missytake Date: Sun, 19 Mar 2023 14:04:45 +0100 Subject: [PATCH 22/32] [tests] Replace single quotes with double quotes --- backend/tests/tests_mastodon/conftest.py | 2 +- .../tests_mastodon/test_api_mastodon_delete_bot.py | 12 ++++++------ 2 files changed, 7 insertions(+), 7 deletions(-) diff --git a/backend/tests/tests_mastodon/conftest.py b/backend/tests/tests_mastodon/conftest.py index 7b3117f..2e93e27 100644 --- a/backend/tests/tests_mastodon/conftest.py +++ b/backend/tests/tests_mastodon/conftest.py @@ -9,7 +9,7 @@ from kibicara.model import Hood from kibicara.platforms.mastodon.model import MastodonAccount, MastodonInstance -@fixture(scope='function') +@fixture(scope="function") def mastodon(event_loop, hood_id): hood = event_loop.run_until_complete(Hood.objects.get(id=hood_id)) instance = event_loop.run_until_complete( diff --git a/backend/tests/tests_mastodon/test_api_mastodon_delete_bot.py b/backend/tests/tests_mastodon/test_api_mastodon_delete_bot.py index 5123639..6375b5f 100644 --- a/backend/tests/tests_mastodon/test_api_mastodon_delete_bot.py +++ b/backend/tests/tests_mastodon/test_api_mastodon_delete_bot.py @@ -12,7 +12,7 @@ from kibicara.platforms.mastodon.model import MastodonAccount def test_mastodon_delete_bot(client, event_loop, mastodon, auth_header): response = client.delete( - '/api/hoods/{0}/mastodon/{1}'.format(mastodon.hood.id, mastodon.id), + "/api/hoods/{0}/mastodon/{1}".format(mastodon.hood.id, mastodon.id), headers=auth_header, ) assert response.status_code == status.HTTP_204_NO_CONTENT @@ -21,22 +21,22 @@ def test_mastodon_delete_bot(client, event_loop, mastodon, auth_header): def test_mastodon_delete_bot_invalid_id(client, auth_header, hood_id): - response = client.delete('/api/hoods/1337/mastodon/123', headers=auth_header) + 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) + 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 + "/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 + "/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): response = client.delete( - '/api/hoods/{0}/mastodon/{1}'.format(mastodon.hood.id, mastodon.id) + "/api/hoods/{0}/mastodon/{1}".format(mastodon.hood.id, mastodon.id) ) assert response.status_code == status.HTTP_401_UNAUTHORIZED -- 2.43.5 From f0757619a7bf933bce482f689c25cf58818af2f9 Mon Sep 17 00:00:00 2001 From: missytake Date: Sun, 19 Mar 2023 14:09:02 +0100 Subject: [PATCH 23/32] [tests] Test mastodon_read route --- .../test_api_mastodon_get_bot.py | 46 +++++++++++++++++++ 1 file changed, 46 insertions(+) create mode 100644 backend/tests/tests_mastodon/test_api_mastodon_get_bot.py diff --git a/backend/tests/tests_mastodon/test_api_mastodon_get_bot.py b/backend/tests/tests_mastodon/test_api_mastodon_get_bot.py new file mode 100644 index 0000000..f6afa8d --- /dev/null +++ b/backend/tests/tests_mastodon/test_api_mastodon_get_bot.py @@ -0,0 +1,46 @@ +# 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 MastodonInstance + + +def test_mastodon_get_bot(client, auth_header, event_loop, mastodon): + response = client.get( + "/api/hoods/{0}/mastodon/{1}".format(mastodon.hood.id, mastodon.id), + headers=auth_header, + ) + assert response.status_code == status.HTTP_200_OK + assert response.json()["id"] == mastodon.id + assert response.json()["username"] == mastodon.username + assert response.json()["access_token"] == mastodon.access_token + mastodon_instance = event_loop.run_until_complete( + MastodonInstance.objects.get(id=mastodon.instance) + ) + assert response.json()["instance"]["name"] == mastodon_instance.name + assert not response.json()["instance"].get("client_id") + assert not response.json()["instance"].get("client_secret") + + +def test_mastodon_get_bot_invalid_id(client, auth_header, hood_id): + response = client.get("/api/hoods/1337/mastodon/123", headers=auth_header) + assert response.status_code == status.HTTP_404_NOT_FOUND + response = client.get("/api/hoods/wrong/mastodon/123", headers=auth_header) + assert response.status_code == status.HTTP_422_UNPROCESSABLE_ENTITY + response = client.get( + "/api/hoods/{0}/mastodon/7331".format(hood_id), headers=auth_header + ) + assert response.status_code == status.HTTP_404_NOT_FOUND + response = client.get( + "/api/hoods/{0}/mastodon/wrong".format(hood_id), headers=auth_header + ) + assert response.status_code == status.HTTP_404_NOT_FOUND + + +def test_mastodon_get_bot_unauthorized(client, mastodon): + response = client.get( + "/api/hoods/{0}/mastodon/{1}".format(mastodon.hood.id, mastodon.id) + ) + assert response.status_code == status.HTTP_401_UNAUTHORIZED -- 2.43.5 From afd0894952074d38deac205d859829529b3f7a70 Mon Sep 17 00:00:00 2001 From: missytake Date: Sun, 19 Mar 2023 14:25:21 +0100 Subject: [PATCH 24/32] [mastodon] Return instance name in read_public API --- .../src/kibicara/platforms/mastodon/webapi.py | 13 +++-- .../test_api_mastodon_get_bots.py | 52 +++++++++++++++++++ 2 files changed, 60 insertions(+), 5 deletions(-) create mode 100644 backend/tests/tests_mastodon/test_api_mastodon_get_bots.py diff --git a/backend/src/kibicara/platforms/mastodon/webapi.py b/backend/src/kibicara/platforms/mastodon/webapi.py index 025acb0..a71f1fd 100644 --- a/backend/src/kibicara/platforms/mastodon/webapi.py +++ b/backend/src/kibicara/platforms/mastodon/webapi.py @@ -74,11 +74,14 @@ twitter_callback_router = APIRouter() ) async def mastodon_read_all_public(hood=Depends(get_hood_unauthorized)): mastodonbots = await MastodonAccount.objects.filter(hood=hood).all() - return [ - BodyMastodonPublic(username=mbot.username, instance=mbot.model.instance.name) - for mbot in mastodonbots - if mbot.enabled == 1 and mbot.username - ] + 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( 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..0417f9e --- /dev/null +++ b/backend/tests/tests_mastodon/test_api_mastodon_get_bots.py @@ -0,0 +1,52 @@ +# 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, MastodonInstance + + +def test_mastodon_get_bots(client, auth_header, event_loop, hood_id, mastodon): + mastodon_instance = event_loop.run_until_complete( + MastodonInstance.objects.get(id=mastodon.instance) + ) + mastodon2 = event_loop.run_until_complete( + MastodonAccount.objects.create( + hood=mastodon.hood, + instance=mastodon_instance, + access_token="4cc3ss", + enabled=True, + username="us4r", + ) + ) + response = client.get( + "/api/hoods/{0}/mastodon".format(mastodon.hood.id), headers=auth_header + ) + assert response.status_code == status.HTTP_200_OK + assert response.json()[0]["id"] == mastodon.id + assert response.json()[0]["access_token"] == mastodon.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, event_loop): + mastodon_instance = event_loop.run_until_complete( + MastodonInstance.objects.get(id=mastodon.instance) + ) + response = client.get("/api/hoods/{0}/mastodon/public".format(mastodon.hood.id)) + assert response.json()[0]["username"] == mastodon.username + assert response.json()[0]["instance"] == mastodon_instance.name -- 2.43.5 From 4f96dfbee7a144a9256c0ce4d5bb7681359aacb1 Mon Sep 17 00:00:00 2001 From: missytake Date: Sun, 19 Mar 2023 14:49:26 +0100 Subject: [PATCH 25/32] [mastodon] Deprecate GET mastodon route --- .../src/kibicara/platforms/mastodon/webapi.py | 9 ---- .../test_api_mastodon_get_bot.py | 46 ------------------- 2 files changed, 55 deletions(-) delete mode 100644 backend/tests/tests_mastodon/test_api_mastodon_get_bot.py diff --git a/backend/src/kibicara/platforms/mastodon/webapi.py b/backend/src/kibicara/platforms/mastodon/webapi.py index a71f1fd..421467c 100644 --- a/backend/src/kibicara/platforms/mastodon/webapi.py +++ b/backend/src/kibicara/platforms/mastodon/webapi.py @@ -93,15 +93,6 @@ async def mastodon_read_all(hood=Depends(get_hood)): return await MastodonAccount.objects.filter(hood=hood).all() -@router.get( - "/{mastodon_id}", - # TODO response_model - operation_id="get_mastodon", -) -async def mastodon_read(mastodon=Depends(get_mastodon)): - return mastodon - - @router.delete( "/{mastodon_id}", status_code=status.HTTP_204_NO_CONTENT, diff --git a/backend/tests/tests_mastodon/test_api_mastodon_get_bot.py b/backend/tests/tests_mastodon/test_api_mastodon_get_bot.py deleted file mode 100644 index f6afa8d..0000000 --- a/backend/tests/tests_mastodon/test_api_mastodon_get_bot.py +++ /dev/null @@ -1,46 +0,0 @@ -# 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 MastodonInstance - - -def test_mastodon_get_bot(client, auth_header, event_loop, mastodon): - response = client.get( - "/api/hoods/{0}/mastodon/{1}".format(mastodon.hood.id, mastodon.id), - headers=auth_header, - ) - assert response.status_code == status.HTTP_200_OK - assert response.json()["id"] == mastodon.id - assert response.json()["username"] == mastodon.username - assert response.json()["access_token"] == mastodon.access_token - mastodon_instance = event_loop.run_until_complete( - MastodonInstance.objects.get(id=mastodon.instance) - ) - assert response.json()["instance"]["name"] == mastodon_instance.name - assert not response.json()["instance"].get("client_id") - assert not response.json()["instance"].get("client_secret") - - -def test_mastodon_get_bot_invalid_id(client, auth_header, hood_id): - response = client.get("/api/hoods/1337/mastodon/123", headers=auth_header) - assert response.status_code == status.HTTP_404_NOT_FOUND - response = client.get("/api/hoods/wrong/mastodon/123", headers=auth_header) - assert response.status_code == status.HTTP_422_UNPROCESSABLE_ENTITY - response = client.get( - "/api/hoods/{0}/mastodon/7331".format(hood_id), headers=auth_header - ) - assert response.status_code == status.HTTP_404_NOT_FOUND - response = client.get( - "/api/hoods/{0}/mastodon/wrong".format(hood_id), headers=auth_header - ) - assert response.status_code == status.HTTP_404_NOT_FOUND - - -def test_mastodon_get_bot_unauthorized(client, mastodon): - response = client.get( - "/api/hoods/{0}/mastodon/{1}".format(mastodon.hood.id, mastodon.id) - ) - assert response.status_code == status.HTTP_401_UNAUTHORIZED -- 2.43.5 From 0f4b25fcde319726a359ccb737f450db6905805e Mon Sep 17 00:00:00 2001 From: missytake Date: Sun, 19 Mar 2023 17:25:37 +0100 Subject: [PATCH 26/32] [tests] Make Mastodon tests a bit more readable --- backend/tests/tests_mastodon/conftest.py | 12 ++++++--- .../test_api_mastodon_delete_bot.py | 16 ++++++++---- .../test_api_mastodon_get_bots.py | 26 +++++++++---------- 3 files changed, 31 insertions(+), 23 deletions(-) diff --git a/backend/tests/tests_mastodon/conftest.py b/backend/tests/tests_mastodon/conftest.py index 2e93e27..ea31bbb 100644 --- a/backend/tests/tests_mastodon/conftest.py +++ b/backend/tests/tests_mastodon/conftest.py @@ -10,19 +10,23 @@ from kibicara.platforms.mastodon.model import MastodonAccount, MastodonInstance @fixture(scope="function") -def mastodon(event_loop, hood_id): - hood = event_loop.run_until_complete(Hood.objects.get(id=hood_id)) - instance = event_loop.run_until_complete( +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=instance, + instance=mastodon_instance, access_token="t0k3n", enabled=True, username="us3r", diff --git a/backend/tests/tests_mastodon/test_api_mastodon_delete_bot.py b/backend/tests/tests_mastodon/test_api_mastodon_delete_bot.py index 6375b5f..1bebac3 100644 --- a/backend/tests/tests_mastodon/test_api_mastodon_delete_bot.py +++ b/backend/tests/tests_mastodon/test_api_mastodon_delete_bot.py @@ -10,14 +10,18 @@ from pytest import raises from kibicara.platforms.mastodon.model import MastodonAccount -def test_mastodon_delete_bot(client, event_loop, mastodon, auth_header): +def test_mastodon_delete_bot(client, event_loop, mastodon_account, auth_header): response = client.delete( - "/api/hoods/{0}/mastodon/{1}".format(mastodon.hood.id, mastodon.id), + "/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.id)) + event_loop.run_until_complete( + MastodonAccount.objects.get(id=mastodon_account.id) + ) def test_mastodon_delete_bot_invalid_id(client, auth_header, hood_id): @@ -35,8 +39,10 @@ def test_mastodon_delete_bot_invalid_id(client, auth_header, hood_id): assert response.status_code == status.HTTP_404_NOT_FOUND -def test_mastodon_delete_bot_unauthorized(client, mastodon): +def test_mastodon_delete_bot_unauthorized(client, mastodon_account): response = client.delete( - "/api/hoods/{0}/mastodon/{1}".format(mastodon.hood.id, mastodon.id) + "/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 index 0417f9e..d3da4ac 100644 --- a/backend/tests/tests_mastodon/test_api_mastodon_get_bots.py +++ b/backend/tests/tests_mastodon/test_api_mastodon_get_bots.py @@ -5,16 +5,15 @@ from fastapi import status -from kibicara.platforms.mastodon.model import MastodonAccount, MastodonInstance +from kibicara.platforms.mastodon.model import MastodonAccount -def test_mastodon_get_bots(client, auth_header, event_loop, hood_id, mastodon): - mastodon_instance = event_loop.run_until_complete( - MastodonInstance.objects.get(id=mastodon.instance) - ) +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.hood, + hood=mastodon_account.hood, instance=mastodon_instance, access_token="4cc3ss", enabled=True, @@ -22,11 +21,11 @@ def test_mastodon_get_bots(client, auth_header, event_loop, hood_id, mastodon): ) ) response = client.get( - "/api/hoods/{0}/mastodon".format(mastodon.hood.id), headers=auth_header + "/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.id - assert response.json()[0]["access_token"] == mastodon.access_token + 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 @@ -43,10 +42,9 @@ def test_mastodon_get_bots_unauthorized(client, hood_id): assert response.status_code == status.HTTP_401_UNAUTHORIZED -def test_mastodon_public(client, mastodon, event_loop): - mastodon_instance = event_loop.run_until_complete( - MastodonInstance.objects.get(id=mastodon.instance) +def test_mastodon_public(client, mastodon_account, mastodon_instance, event_loop): + response = client.get( + "/api/hoods/{0}/mastodon/public".format(mastodon_account.hood.id) ) - response = client.get("/api/hoods/{0}/mastodon/public".format(mastodon.hood.id)) - assert response.json()[0]["username"] == mastodon.username + assert response.json()[0]["username"] == mastodon_account.username assert response.json()[0]["instance"] == mastodon_instance.name -- 2.43.5 From 9c7607b7cad5743a99ed006ee4334286e815b90b Mon Sep 17 00:00:00 2001 From: ogdbd3h5qze42igcv8wcrqk3 Date: Sun, 19 Mar 2023 13:29:57 +0100 Subject: [PATCH 27/32] [frontend] Regenerate openapi for frontend due to mastodon API changes --- .../src/app/core/api/.openapi-generator/FILES | 1 + .../src/app/core/api/api/admin.service.ts | 2 +- .../src/app/core/api/api/badwords.service.ts | 2 +- .../src/app/core/api/api/email.service.ts | 2 +- .../src/app/core/api/api/hoods.service.ts | 2 +- .../src/app/core/api/api/mastodon.service.ts | 53 +++++++------------ .../src/app/core/api/api/telegram.service.ts | 2 +- frontend/src/app/core/api/api/test.service.ts | 2 +- .../src/app/core/api/api/triggers.service.ts | 2 +- .../src/app/core/api/api/twitter.service.ts | 2 +- .../app/core/api/model/bodyMastodonAccount.ts | 19 +++++++ frontend/src/app/core/api/model/models.ts | 1 + 12 files changed, 49 insertions(+), 41 deletions(-) create mode 100644 frontend/src/app/core/api/model/bodyMastodonAccount.ts diff --git a/frontend/src/app/core/api/.openapi-generator/FILES b/frontend/src/app/core/api/.openapi-generator/FILES index f3d80a3..708d04f 100644 --- a/frontend/src/app/core/api/.openapi-generator/FILES +++ b/frontend/src/app/core/api/.openapi-generator/FILES @@ -20,6 +20,7 @@ model/bodyAdmin.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/admin.service.ts b/frontend/src/app/core/api/api/admin.service.ts index c8afa9f..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:8000/api'; + 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/badwords.service.ts b/frontend/src/app/core/api/api/badwords.service.ts index dea8e24..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:8000/api'; + 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/email.service.ts b/frontend/src/app/core/api/api/email.service.ts index c5c4e94..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:8000/api'; + 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/hoods.service.ts b/frontend/src/app/core/api/api/hoods.service.ts index 30c4c8d..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:8000/api'; + 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/mastodon.service.ts b/frontend/src/app/core/api/api/mastodon.service.ts index 2cc0f48..17cc2e5 100644 --- a/frontend/src/app/core/api/api/mastodon.service.ts +++ b/frontend/src/app/core/api/api/mastodon.service.ts @@ -17,6 +17,7 @@ import { HttpClient, HttpHeaders, HttpParams, 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'; @@ -29,7 +30,7 @@ import { Configuration } from '../configurat }) export class MastodonService { - protected basePath = 'http://localhost:8000/api'; + protected basePath = 'http://localhost/api'; public defaultHeaders = new HttpHeaders(); public configuration = new Configuration(); public encoder: HttpParameterCodec; @@ -86,43 +87,21 @@ export class MastodonService { /** * Mastodon Create - * Add a Mastodon Account to a Ticketfrei account. open questions: do we really get the username + password like this? can the instance_url have different ways of writing? :param: instance_url: the API base URL of the mastodon server :param: username: the username of the Mastodon account :param: password: the password of the Mastodon account :param: hood: the hood ORM object + * 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 instanceUrl - * @param username - * @param password + * @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, instanceUrl: any, username: any, password: any, observe?: 'body', reportProgress?: boolean, options?: {httpHeaderAccept?: 'application/json'}): Observable; - public createMastodon(hoodId: number, instanceUrl: any, username: any, password: any, observe?: 'response', reportProgress?: boolean, options?: {httpHeaderAccept?: 'application/json'}): Observable>; - public createMastodon(hoodId: number, instanceUrl: any, username: any, password: any, observe?: 'events', reportProgress?: boolean, options?: {httpHeaderAccept?: 'application/json'}): Observable>; - public createMastodon(hoodId: number, instanceUrl: any, username: any, password: any, observe: any = 'body', reportProgress: boolean = false, options?: {httpHeaderAccept?: 'application/json'}): Observable { + 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 (instanceUrl === null || instanceUrl === undefined) { - throw new Error('Required parameter instanceUrl was null or undefined when calling createMastodon.'); - } - if (username === null || username === undefined) { - throw new Error('Required parameter username was null or undefined when calling createMastodon.'); - } - if (password === null || password === undefined) { - throw new Error('Required parameter password was null or undefined when calling createMastodon.'); - } - - let queryParameters = new HttpParams({encoder: this.encoder}); - if (instanceUrl !== undefined && instanceUrl !== null) { - queryParameters = this.addToHttpParams(queryParameters, - instanceUrl, 'instance_url'); - } - if (username !== undefined && username !== null) { - queryParameters = this.addToHttpParams(queryParameters, - username, 'username'); - } - if (password !== undefined && password !== null) { - queryParameters = this.addToHttpParams(queryParameters, - password, 'password'); + if (bodyMastodonAccount === null || bodyMastodonAccount === undefined) { + throw new Error('Required parameter bodyMastodonAccount was null or undefined when calling createMastodon.'); } let headers = this.defaultHeaders; @@ -147,15 +126,23 @@ export class MastodonService { } + // 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/`, - null, + bodyMastodonAccount, { - params: queryParameters, responseType: responseType, withCredentials: this.configuration.withCredentials, headers: headers, diff --git a/frontend/src/app/core/api/api/telegram.service.ts b/frontend/src/app/core/api/api/telegram.service.ts index ba88547..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:8000/api'; + 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 e5a5424..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:8000/api'; + 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 8dc0e7c..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:8000/api'; + 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/twitter.service.ts b/frontend/src/app/core/api/api/twitter.service.ts index c34a019..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:8000/api'; + 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/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/models.ts b/frontend/src/app/core/api/model/models.ts index bc00d4b..b8f2443 100644 --- a/frontend/src/app/core/api/model/models.ts +++ b/frontend/src/app/core/api/model/models.ts @@ -3,6 +3,7 @@ export * from './bodyAdmin'; export * from './bodyBadWord'; export * from './bodyHood'; export * from './bodyLogin'; +export * from './bodyMastodonAccount'; export * from './bodyPassword'; export * from './bodySubscriber'; export * from './bodyTelegram'; -- 2.43.5 From d897f369f7d0e9fa42e68b99f52d996bfba7e9c5 Mon Sep 17 00:00:00 2001 From: ogdbd3h5qze42igcv8wcrqk3 Date: Sun, 19 Mar 2023 18:24:19 +0100 Subject: [PATCH 28/32] [mastodon] Fix delete endpoint for mastodon --- backend/src/kibicara/platforms/mastodon/webapi.py | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/backend/src/kibicara/platforms/mastodon/webapi.py b/backend/src/kibicara/platforms/mastodon/webapi.py index 421467c..c10ce44 100644 --- a/backend/src/kibicara/platforms/mastodon/webapi.py +++ b/backend/src/kibicara/platforms/mastodon/webapi.py @@ -101,6 +101,12 @@ async def mastodon_read_all(hood=Depends(get_hood)): ) 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) -- 2.43.5 From ec0abb5e24475f9b1a3e54d6d2a0d2fa37fb3339 Mon Sep 17 00:00:00 2001 From: ogdbd3h5qze42igcv8wcrqk3 Date: Sun, 19 Mar 2023 18:25:16 +0100 Subject: [PATCH 29/32] [frontend] Add initial mastodon frontend --- .../board/platforms/platforms.component.html | 1 + .../mastodon-bot-card.component.html | 47 ++++++++ .../mastodon-bot-card.component.scss | 0 .../mastodon-bot-card.component.spec.ts | 23 ++++ .../mastodon-bot-card.component.ts | 27 +++++ .../mastodon-bot-info-dialog.component.html | 54 ++++++++++ .../mastodon-bot-info-dialog.component.scss | 0 .../mastodon-bot-info-dialog.component.ts | 12 +++ .../mastodon-dialog.component.html | 46 ++++++++ .../mastodon-dialog.component.scss | 26 +++++ .../mastodon-dialog.component.spec.ts | 23 ++++ .../mastodon-dialog.component.ts | 79 ++++++++++++++ .../mastodon-settings.component.html | 69 ++++++++++++ .../mastodon-settings.component.scss | 23 ++++ .../mastodon-settings.component.spec.ts | 23 ++++ .../mastodon-settings.component.ts | 102 ++++++++++++++++++ .../platforms-info-page.component.html | 1 + .../src/app/platforms/platforms.module.ts | 9 ++ frontend/src/assets/mastodon.png | Bin 0 -> 15086 bytes 19 files changed, 565 insertions(+) create mode 100644 frontend/src/app/platforms/mastodon/mastodon-bot-card/mastodon-bot-card.component.html create mode 100644 frontend/src/app/platforms/mastodon/mastodon-bot-card/mastodon-bot-card.component.scss create mode 100644 frontend/src/app/platforms/mastodon/mastodon-bot-card/mastodon-bot-card.component.spec.ts create mode 100644 frontend/src/app/platforms/mastodon/mastodon-bot-card/mastodon-bot-card.component.ts create mode 100644 frontend/src/app/platforms/mastodon/mastodon-bot-card/mastodon-bot-info-dialog/mastodon-bot-info-dialog.component.html create mode 100644 frontend/src/app/platforms/mastodon/mastodon-bot-card/mastodon-bot-info-dialog/mastodon-bot-info-dialog.component.scss create mode 100644 frontend/src/app/platforms/mastodon/mastodon-bot-card/mastodon-bot-info-dialog/mastodon-bot-info-dialog.component.ts create mode 100644 frontend/src/app/platforms/mastodon/mastodon-settings/mastodon-dialog/mastodon-dialog.component.html create mode 100644 frontend/src/app/platforms/mastodon/mastodon-settings/mastodon-dialog/mastodon-dialog.component.scss create mode 100644 frontend/src/app/platforms/mastodon/mastodon-settings/mastodon-dialog/mastodon-dialog.component.spec.ts create mode 100644 frontend/src/app/platforms/mastodon/mastodon-settings/mastodon-dialog/mastodon-dialog.component.ts create mode 100644 frontend/src/app/platforms/mastodon/mastodon-settings/mastodon-settings.component.html create mode 100644 frontend/src/app/platforms/mastodon/mastodon-settings/mastodon-settings.component.scss create mode 100644 frontend/src/app/platforms/mastodon/mastodon-settings/mastodon-settings.component.spec.ts create mode 100644 frontend/src/app/platforms/mastodon/mastodon-settings/mastodon-settings.component.ts create mode 100644 frontend/src/assets/mastodon.png 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 0000000000000000000000000000000000000000..b09a98bb9b0649cb67305b6663bd56b3cfb17222 GIT binary patch literal 15086 zcmdU0d303O8Gn{m+S9sq@gI+sdfIc^b8PjTdaRg0MwkFW3;~?w&Ax;HN^k)eL`<|& z1*?|DswkUO)VePfAuIt!5wnnlY=nf607(d0XL~dAX1;#+P41gFZ{8awAyE3x`Q^L! ze(Sx-yf^RrzA(&X%oyhCs~M!@nE5|snA;eJ88a|7_j_ zWUd7WLMY0(?)eIB(B2&+W{!6?WuVfgpT5rcdXgW5Nvv` zlTXHFLx{0ee-@)$>6qAkW*kas5aj+OT;#?x%!_<3%kR)l!u!t(y#MSd%g{IC*oWwi z!BAg*hyEkHCp}$phi*K?A|0leWbuQ4F<)7cX?(OI)6j{4bWxdUXs(!GyeHXW`YRO~ z##;|hFti|GgxHn7w?xUBiAHyImLY@y6-01-AL2E{N|BLCGI zHm@ej7^%@1K^cZG@xJ`q#E27Tl^Vy@H4_bgNL)SltbVfX7qxd9dk~-uLuIW_clo(3 zjZC6eYbwTP6Q}{7VT(s*IX$fgEn9;CRb-oV_{^M&&(ufiwe0$aZ1z2*B`E)WgVuD2 zj4{R!k-xQZGW++&Y~y{6*{12WTHUYD6#;pMAI)YX2)Gbv%4QoIr?5-OGhHbbX8RP+31{H}13;Axp^xmg-RbB=jsOO6>X8m5~MLvisBXbi2>&D#(l zMOV@D(7W*+0o^OtCGezjYU8dao{Q_h@Jh0NjyZ?=Z<}HM0s&HZcYj46_rO)xCGezj zYGcoTN<{tO(dH!m_PZ^wQUC2TEC+B7Qut*4rMz^;o-cMK_)pS7pVa@UT=S7r_`OgH z-yX^SYw8ksM6T=JN8s%8X9b*jXn7LfEAbz~{Y!1f#@06K|8$=91OlY+OZ*4Xzm!MD zz8!nw`>Sn8=5LShKh)Hm#6LaL+Clwy&a!qPK#IWL`2LB1DUXc(GXF_@nSVJS=Vz(^ zuGzLh1V|CU@B8!$9;r{@5jp?Py_5~1x`t%_m%HP9{C+OAJv1Jq{=4t71rZ1MZV|p;`qDAkd$xDDo@tvn>fOUWr z-lv`w^~r#||AfB&MtGMZ?@q$npa&v&Ho_H&K)9>|Li@ggXkTA!ZRls=xyNZ@#4%Lln;^u>2`ePHG?D6Zg^m`>d{8{rHZk2=_b)ZG8*) zvT}$VsRbU-H@NDBBw4QLX$bGy1AKK2@Rd~%djCTR+*_C=C;G6t9;W^Y~z5e0>?X@7V1xllDq-Fi$4)@ zxq1;WT8O$_yTRp3_}-`FpRR$!@ng&f zpcM5HV_MN(KicIPCX3`lF-6Ka5MrJp>#_RT$ zjn}te&Z1X>GdM0QyFPT=8jaq9IV)bsTdC)~_x;UxZZ+@#%z!N`J>$&%#($cb7jdnv}%p9qE^FZ4(E5@kdb>ubrw4w z_nDNp7>*se?BPke>#(NNO=~;nq#AaQy!Iovc`@flCb4fT=Lr?_hg{tx6K2ydjjK*$ zxT$Uu8%01`m~8y`(uh131?Cz5Mr(R$Q7v1j7C*^7)=V*8UpEEM1aVYry0Knk9EbPg zYxZh1=ctUTSxvo;InzqjJgagJ)=6@y4cVqg8@22<)U8JJBBE*7;ZUcOv3s`fFo08%Dq-0Dq&__}Nh{I|uODpHBQF|C(k!H{H7LD9N!k zjoIeO=i0-usiuWZQ!!V4X(DoTn(2vDHMnCrrnSc~*L^9YIfs2x?f1|!&HVZBYx*PU z12`|8DAp3DEA8QUj=2s2{|9Iw?;-L%V7}k0mh(U9xF0$ne>MT97Cf4q`{BY+zt6!&PMe6^y zOx^wr)BDo+kIk}P+cv|3wG3q_#=mR7s=S&VwROe#50<5}@0He>+Gm)4msAIl)Lr^F zDx5#YpGs#qyxuhm=a2EHvT=HP{Ci_tuJu;h)1yTDHzmjK!}yhCN3zztHaY$P#-GyG zjQBI{)J&Te`;tHn14{Al-mfa(HSZzlU9~0w-Ajs8<@;3PSBr1W_{o+S_K zMxcg)O>c{zD?0FF{A%+3uWX`XBK7Y*pf11m4N;B`0td@gyx1MxmEnUDT;$?AhX#nF_m48_|el_`_^|>_ut~r(kU9+uNi&KVYOY->#_Uu=cAAC(A{(T43j+E1pl1?>AvBtFucY6&rG&e4Z>TiLcKniQiyffVFC|Pgg!3oWCf^ zK3U+g{_a_{MAY*vUJ5++1gKq6tmpflSsANYqrXAddJ{ukGZcIEMqIiB@0 z`uF2u?Q;gP&YlYVv1Y0HgfXcnlX|xNA~pCBe$M4r)T1m<CD;mX!Fwe3$EUg?ZEfJke)g0x#Nvy3tk!F#@%t>!1!50^TF=AKdhn&y z5UDwm7!My2dK?H(%?`J$2>7oL00aW@ZQ$Qow(Bz_uA-O_vg`k*CE{7QKVhb zeC$7pfkO%sL?t{Ta{_g-7YTLq-P Date: Sun, 19 Mar 2023 19:20:09 +0100 Subject: [PATCH 30/32] [mastodon] Fix load order of API routers --- backend/src/kibicara/webapi/__init__.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/backend/src/kibicara/webapi/__init__.py b/backend/src/kibicara/webapi/__init__.py index adc99ea..af769c3 100644 --- a/backend/src/kibicara/webapi/__init__.py +++ b/backend/src/kibicara/webapi/__init__.py @@ -38,9 +38,9 @@ hoods_router.include_router( hoods_router.include_router( twitter_router, prefix="/{hood_id}/twitter", tags=["twitter"] ) -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") 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") -- 2.43.5 From f7d9baa8a34110c585f98288e5e3bac908aa4149 Mon Sep 17 00:00:00 2001 From: missytake Date: Sun, 19 Mar 2023 20:27:48 +0100 Subject: [PATCH 31/32] [mastodon] Fix: boosting public toots instead of posting them as own --- backend/src/kibicara/platforms/mastodon/bot.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/backend/src/kibicara/platforms/mastodon/bot.py b/backend/src/kibicara/platforms/mastodon/bot.py index 0fdad53..82ff364 100644 --- a/backend/src/kibicara/platforms/mastodon/bot.py +++ b/backend/src/kibicara/platforms/mastodon/bot.py @@ -85,10 +85,10 @@ class MastodonBot(Censor): """Push new Ticketfrei reports to Mastodon; if source is mastodon, boost it.""" while True: message = await self.receive() - if hasattr(message, "tood_id"): - logger.debug("Boosting post %s: %s" % (message.tood_id, message.text)) + 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.tood_id + None, self.account.status_reblog, message.toot_id ) else: logger.debug("Posting message: %s" % (message.text,)) -- 2.43.5 From 64715f5aa550b482f8f76f61192fb0a1d2d70929 Mon Sep 17 00:00:00 2001 From: missytake Date: Sun, 19 Mar 2023 20:29:25 +0100 Subject: [PATCH 32/32] [mastodon] Also dismiss notifications which have no status --- backend/src/kibicara/platforms/mastodon/bot.py | 1 + 1 file changed, 1 insertion(+) diff --git a/backend/src/kibicara/platforms/mastodon/bot.py b/backend/src/kibicara/platforms/mastodon/bot.py index 82ff364..cab0fae 100644 --- a/backend/src/kibicara/platforms/mastodon/bot.py +++ b/backend/src/kibicara/platforms/mastodon/bot.py @@ -63,6 +63,7 @@ class MastodonBot(Censor): 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( -- 2.43.5