2020-07-05 18:33:58 +00:00
|
|
|
# Copyright (C) 2020 by Cathy Hu <cathy.hu@fau.de>
|
2020-09-28 22:39:32 +00:00
|
|
|
# Copyright (C) 2020 by Martin Rey <martin.rey@mailbox.org>
|
2020-07-05 18:33:58 +00:00
|
|
|
#
|
|
|
|
# SPDX-License-Identifier: 0BSD
|
|
|
|
|
2020-07-06 09:13:59 +00:00
|
|
|
from fastapi import APIRouter, Depends, HTTPException, Response, status
|
|
|
|
from kibicara.config import config
|
2020-07-05 18:33:58 +00:00
|
|
|
from kibicara.platforms.twitter.bot import spawner
|
|
|
|
from kibicara.platforms.twitter.model import Twitter
|
2020-09-03 15:36:49 +00:00
|
|
|
from kibicara.webapi.hoods import get_hood, get_hood_unauthorized
|
2020-07-06 09:13:59 +00:00
|
|
|
from logging import getLogger
|
|
|
|
from sqlite3 import IntegrityError
|
2020-07-06 12:35:20 +00:00
|
|
|
from ormantic.exceptions import NoMatch
|
2020-07-06 09:13:59 +00:00
|
|
|
from peony.oauth_dance import get_oauth_token, get_access_token
|
2020-07-18 20:27:39 +00:00
|
|
|
from peony.exceptions import NotAuthenticated
|
2020-09-03 15:36:49 +00:00
|
|
|
from pydantic import BaseModel
|
2020-07-06 09:13:59 +00:00
|
|
|
|
|
|
|
|
|
|
|
logger = getLogger(__name__)
|
|
|
|
|
|
|
|
|
2020-09-03 15:36:49 +00:00
|
|
|
class BodyTwitterPublic(BaseModel):
|
|
|
|
username: str
|
|
|
|
|
|
|
|
|
2020-07-06 09:13:59 +00:00
|
|
|
async def get_twitter(twitter_id: int, hood=Depends(get_hood)):
|
|
|
|
try:
|
|
|
|
return await Twitter.objects.get(id=twitter_id, hood=hood)
|
|
|
|
except NoMatch:
|
|
|
|
raise HTTPException(status_code=status.HTTP_404_NOT_FOUND)
|
2020-07-05 18:33:58 +00:00
|
|
|
|
|
|
|
|
|
|
|
router = APIRouter()
|
2020-07-06 09:13:59 +00:00
|
|
|
twitter_callback_router = APIRouter()
|
|
|
|
|
|
|
|
|
2020-09-03 15:36:49 +00:00
|
|
|
@router.get(
|
|
|
|
'/public',
|
|
|
|
# TODO response_model,
|
|
|
|
operation_id='get_twitters_public',
|
|
|
|
)
|
|
|
|
async def twitter_read_all_public(hood=Depends(get_hood_unauthorized)):
|
|
|
|
twitterbots = await Twitter.objects.filter(hood=hood).all()
|
|
|
|
return [
|
|
|
|
BodyTwitterPublic(username=twitterbot.username)
|
|
|
|
for twitterbot in twitterbots
|
2020-09-09 12:19:21 +00:00
|
|
|
if twitterbot.verified == 1 and twitterbot.enabled == 1 and twitterbot.username
|
2020-09-03 15:36:49 +00:00
|
|
|
]
|
|
|
|
|
|
|
|
|
2020-07-25 11:38:10 +00:00
|
|
|
@router.get(
|
|
|
|
'/',
|
|
|
|
# TODO response_model,
|
2020-07-25 19:46:39 +00:00
|
|
|
operation_id='get_twitters',
|
2020-07-25 11:38:10 +00:00
|
|
|
)
|
2020-07-06 12:50:11 +00:00
|
|
|
async def twitter_read_all(hood=Depends(get_hood)):
|
|
|
|
return await Twitter.objects.filter(hood=hood).all()
|
|
|
|
|
|
|
|
|
2020-07-25 11:38:10 +00:00
|
|
|
@router.get(
|
|
|
|
'/{twitter_id}',
|
|
|
|
# TODO response_model
|
2020-07-25 19:46:39 +00:00
|
|
|
operation_id='get_twitter',
|
2020-07-25 11:38:10 +00:00
|
|
|
)
|
2020-07-06 12:50:11 +00:00
|
|
|
async def twitter_read(twitter=Depends(get_twitter)):
|
|
|
|
return twitter
|
|
|
|
|
|
|
|
|
2020-07-25 11:38:10 +00:00
|
|
|
@router.delete(
|
|
|
|
'/{twitter_id}',
|
|
|
|
status_code=status.HTTP_204_NO_CONTENT,
|
|
|
|
# TODO response_model
|
2020-07-25 19:46:39 +00:00
|
|
|
operation_id='delete_twitter',
|
2020-07-25 11:38:10 +00:00
|
|
|
)
|
2020-07-06 12:50:11 +00:00
|
|
|
async def twitter_delete(twitter=Depends(get_twitter)):
|
|
|
|
spawner.stop(twitter)
|
|
|
|
await twitter.delete()
|
2020-09-11 00:58:39 +00:00
|
|
|
return Response(status_code=status.HTTP_204_NO_CONTENT)
|
2020-07-06 12:50:11 +00:00
|
|
|
|
|
|
|
|
2020-07-25 11:38:10 +00:00
|
|
|
@router.get(
|
|
|
|
'/{twitter_id}/status',
|
|
|
|
status_code=status.HTTP_200_OK,
|
|
|
|
# TODO response_model
|
2020-07-25 19:46:39 +00:00
|
|
|
operation_id='status_twitter',
|
2020-07-25 11:38:10 +00:00
|
|
|
)
|
2020-07-17 21:36:14 +00:00
|
|
|
async def twitter_status(twitter=Depends(get_twitter)):
|
|
|
|
return {'status': spawner.get(twitter).status.name}
|
|
|
|
|
|
|
|
|
2020-07-25 11:38:10 +00:00
|
|
|
@router.post(
|
|
|
|
'/{twitter_id}/start',
|
|
|
|
status_code=status.HTTP_200_OK,
|
|
|
|
# TODO response_model
|
2020-07-25 19:46:39 +00:00
|
|
|
operation_id='start_twitter',
|
2020-07-25 11:38:10 +00:00
|
|
|
)
|
2020-07-17 21:36:14 +00:00
|
|
|
async def twitter_start(twitter=Depends(get_twitter)):
|
|
|
|
await twitter.update(enabled=True)
|
|
|
|
spawner.get(twitter).start()
|
|
|
|
return {}
|
|
|
|
|
|
|
|
|
2020-07-25 11:38:10 +00:00
|
|
|
@router.post(
|
|
|
|
'/{twitter_id}/stop',
|
|
|
|
status_code=status.HTTP_200_OK,
|
|
|
|
# TODO response_model
|
2020-07-25 19:46:39 +00:00
|
|
|
operation_id='stop_twitter',
|
2020-07-25 11:38:10 +00:00
|
|
|
)
|
2020-07-17 21:36:14 +00:00
|
|
|
async def twitter_stop(twitter=Depends(get_twitter)):
|
|
|
|
await twitter.update(enabled=False)
|
|
|
|
spawner.get(twitter).stop()
|
|
|
|
return {}
|
|
|
|
|
|
|
|
|
2020-07-25 11:38:10 +00:00
|
|
|
@router.post(
|
|
|
|
'/',
|
|
|
|
status_code=status.HTTP_201_CREATED,
|
|
|
|
# TODO response_model
|
2020-07-25 19:46:39 +00:00
|
|
|
operation_id='create_twitter',
|
2020-07-25 11:38:10 +00:00
|
|
|
)
|
2020-07-06 09:13:59 +00:00
|
|
|
async def twitter_create(response: Response, hood=Depends(get_hood)):
|
2020-07-17 21:36:14 +00:00
|
|
|
"""
|
|
|
|
`https://api.twitter.com/oauth/authorize?oauth_token=`
|
|
|
|
"""
|
2020-07-06 09:13:59 +00:00
|
|
|
try:
|
2020-09-06 18:55:10 +00:00
|
|
|
# Purge Twitter corpses
|
|
|
|
for corpse in await Twitter.objects.filter(hood=hood, verified=False).all():
|
|
|
|
await corpse.delete()
|
|
|
|
# Create Twitter
|
2020-07-06 14:11:05 +00:00
|
|
|
request_token = await get_oauth_token(
|
2020-07-08 11:22:53 +00:00
|
|
|
config['twitter']['consumer_key'],
|
|
|
|
config['twitter']['consumer_secret'],
|
2020-09-28 22:39:32 +00:00
|
|
|
callback_uri="{0}/dashboard/twitter-callback?hood={1}".format(
|
|
|
|
config["frontend_url"], hood.id
|
|
|
|
),
|
2020-07-06 09:13:59 +00:00
|
|
|
)
|
2020-07-06 14:11:05 +00:00
|
|
|
if request_token['oauth_callback_confirmed'] != 'true':
|
2020-07-06 09:13:59 +00:00
|
|
|
raise HTTPException(status_code=status.HTTP_503_SERVICE_UNAVAILABLE)
|
2020-07-06 14:11:05 +00:00
|
|
|
twitter = await Twitter.objects.create(
|
|
|
|
hood=hood,
|
|
|
|
access_token=request_token['oauth_token'],
|
|
|
|
access_token_secret=request_token['oauth_token_secret'],
|
2020-07-06 09:13:59 +00:00
|
|
|
)
|
2020-09-28 22:39:32 +00:00
|
|
|
response.headers['Location'] = str(twitter.id)
|
2020-07-06 09:13:59 +00:00
|
|
|
return twitter
|
|
|
|
except IntegrityError:
|
|
|
|
raise HTTPException(status_code=status.HTTP_409_CONFLICT)
|
2020-07-18 20:27:39 +00:00
|
|
|
except (KeyError, ValueError, NotAuthenticated):
|
|
|
|
raise HTTPException(status_code=status.HTTP_500_INTERNAL_SERVER_ERROR)
|
2020-07-06 09:13:59 +00:00
|
|
|
|
|
|
|
|
2020-07-25 11:38:10 +00:00
|
|
|
@twitter_callback_router.get(
|
|
|
|
'/callback',
|
|
|
|
# TODO response_model
|
2020-07-25 19:46:39 +00:00
|
|
|
operation_id='callback_twitter',
|
2020-07-25 11:38:10 +00:00
|
|
|
)
|
2020-09-05 19:32:10 +00:00
|
|
|
async def twitter_read_callback(
|
|
|
|
oauth_token: str, oauth_verifier: str, hood=Depends(get_hood)
|
|
|
|
):
|
2020-07-06 09:13:59 +00:00
|
|
|
try:
|
|
|
|
twitter = await Twitter.objects.filter(access_token=oauth_token).get()
|
|
|
|
access_token = await get_access_token(
|
2020-07-08 11:22:53 +00:00
|
|
|
config['twitter']['consumer_key'],
|
|
|
|
config['twitter']['consumer_secret'],
|
2020-07-06 09:13:59 +00:00
|
|
|
twitter.access_token,
|
|
|
|
twitter.access_token_secret,
|
|
|
|
oauth_verifier,
|
|
|
|
)
|
|
|
|
await twitter.update(
|
|
|
|
access_token=access_token['oauth_token'],
|
|
|
|
access_token_secret=access_token['oauth_token_secret'],
|
2020-07-17 21:36:14 +00:00
|
|
|
verified=True,
|
|
|
|
enabled=True,
|
2020-07-06 09:13:59 +00:00
|
|
|
)
|
|
|
|
spawner.start(twitter)
|
2020-07-18 20:27:39 +00:00
|
|
|
return {}
|
2020-07-06 09:13:59 +00:00
|
|
|
except IntegrityError:
|
|
|
|
raise HTTPException(status_code=status.HTTP_409_CONFLICT)
|
2020-07-06 12:35:20 +00:00
|
|
|
except NoMatch:
|
|
|
|
raise HTTPException(status_code=status.HTTP_404_NOT_FOUND)
|
2020-07-18 20:27:39 +00:00
|
|
|
except (KeyError, ValueError, NotAuthenticated):
|
|
|
|
raise HTTPException(status_code=status.HTTP_500_INTERNAL_SERVER_ERROR)
|