# Copyright (C) 2020 by Cathy Hu <cathy.hu@fau.de> # Copyright (C) 2020 by Martin Rey <martin.rey@mailbox.org> # # SPDX-License-Identifier: 0BSD from asyncio import get_event_loop from fastapi import APIRouter, Depends, HTTPException, Response, status from ormantic.exceptions import NoMatch from pydantic import BaseModel, validate_email, validator from sqlite3 import IntegrityError from kibicara.config import config from kibicara.platforms.mastodon.bot import spawner from kibicara.platforms.mastodon.model import MastodonAccount, MastodonInstance from kibicara.webapi.hoods import get_hood, get_hood_unauthorized from mastodon import Mastodon, MastodonNetworkError from mastodon.errors import MastodonIllegalArgumentError from logging import getLogger logger = getLogger(__name__) class BodyMastodonPublic(BaseModel): username: str instance: str class BodyMastodonAccount(BaseModel): email: str instance_url: str password: str @validator("email") def validate_email(cls, value): return validate_email(value) async def get_mastodon(mastodon_id, hood=Depends(get_hood)): try: return await MastodonAccount.objects.get(id=mastodon_id, hood=hood) except NoMatch: raise HTTPException(status_code=status.HTTP_404_NOT_FOUND) async def get_mastodon_instance(instance_url: str) -> MastodonInstance: """Return a MastodonInstance ORM object with valid client_id and client_secret. :param: instance_url: the API base URL of the mastodon server :return the MastodonInstance ORM object """ try: return await MastodonInstance.objects.get(name=instance_url) except NoMatch: app_name = config.get("frontend_url") client_id, client_secret = Mastodon.create_app( app_name, api_base_url=instance_url ) await MastodonInstance.objects.create( name=instance_url, client_id=client_id, client_secret=client_secret ) return await MastodonInstance.objects.get(name=instance_url) router = APIRouter() twitter_callback_router = APIRouter() @router.get( "/public", # TODO response_model, operation_id="get_mastodons_public", ) async def mastodon_read_all_public(hood=Depends(get_hood_unauthorized)): mastodonbots = await MastodonAccount.objects.filter(hood=hood).all() mbots = [] for mbot in mastodonbots: if mbot.enabled == 1 and mbot.username: instance = await MastodonInstance.objects.get(id=mbot.instance) mbots.append( BodyMastodonPublic(username=mbot.username, instance=instance.name) ) return mbots @router.get( "/", # TODO response_model, operation_id="get_mastodons", ) async def mastodon_read_all(hood=Depends(get_hood)): return await MastodonAccount.objects.filter(hood=hood).all() @router.delete( "/{mastodon_id}", status_code=status.HTTP_204_NO_CONTENT, # TODO response_model operation_id="delete_mastodon", ) async def mastodon_delete(mastodon=Depends(get_mastodon)): spawner.stop(mastodon) await mastodon.delete() return Response(status_code=status.HTTP_204_NO_CONTENT) @router.get( "/{mastodon_id}/status", status_code=status.HTTP_200_OK, # TODO response_model operation_id="status_mastodon", ) async def mastodon_status(mastodon=Depends(get_mastodon)): return {"status": spawner.get(mastodon).status.name} @router.post( "/{mastodon_id}/start", status_code=status.HTTP_200_OK, # TODO response_model operation_id="start_mastodon", ) async def mastodon_start(mastodon=Depends(get_mastodon)): await mastodon.update(enabled=True) spawner.get(mastodon).start() return {} @router.post( "/{mastodon_id}/stop", status_code=status.HTTP_200_OK, # TODO response_model operation_id="stop_mastodon", ) async def mastodon_stop(mastodon=Depends(get_mastodon)): await mastodon.update(enabled=False) spawner.get(mastodon).stop() return {} @router.post( "/", status_code=status.HTTP_201_CREATED, # TODO response_model operation_id="create_mastodon", ) async def mastodon_create(values: BodyMastodonAccount, hood=Depends(get_hood)): """Add a Mastodon Account to a Ticketfrei account. open questions: can the instance_url have different ways of writing? :param: values: a BodyMastodonAccount object in json :param: hood: the hood ORM object """ try: instance = await get_mastodon_instance(values.instance_url) except MastodonNetworkError: raise HTTPException(422, "Invalid Mastodon Instance") account = Mastodon( instance.client_id, instance.client_secret, api_base_url=values.instance_url ) try: access_token = await get_event_loop().run_in_executor( None, account.log_in, values.email, values.password ) logger.debug(f"{access_token}") mastodon = await MastodonAccount.objects.create( hood=hood, instance=instance, access_token=access_token, enabled=True ) spawner.start(mastodon) return mastodon except MastodonIllegalArgumentError: logger.warning("Login to Mastodon failed.", exc_info=True) raise HTTPException(status_code=status.HTTP_422_INVALID_INPUT) except IntegrityError: raise HTTPException(status_code=status.HTTP_409_CONFLICT)