kopia lustrzana https://gitlab.com/marnanel/chapeau
Follow -> FollowUser; Unfollow -> UnfollowUser
Since k.t.models also has a Follow class, it was getting confusing. urls also doesn't "import *" the views classes any more, for clarity. There were some classes duplicated between persons.py and statuses.py; they have been confined to persons.py. __init__.py has content.main
rodzic
8a0deb6c93
commit
b00f844d84
|
@ -5,48 +5,48 @@
|
||||||
# Licensed under the GNU Public License v2.
|
# Licensed under the GNU Public License v2.
|
||||||
|
|
||||||
from django.urls import path
|
from django.urls import path
|
||||||
from .views import *
|
import kepi.trilby_api.views as views
|
||||||
|
|
||||||
urlpatterns = [
|
urlpatterns = [
|
||||||
|
|
||||||
path('api/v1/instance', Instance.as_view()),
|
path('api/v1/instance', views.Instance.as_view()),
|
||||||
path('api/v1/instance/', Instance.as_view()), # keep tootstream happy
|
path('api/v1/instance/', views.Instance.as_view()), # keep tootstream happy
|
||||||
path('api/v1/apps', Apps.as_view()),
|
path('api/v1/apps', views.Apps.as_view()),
|
||||||
|
|
||||||
path('api/v1/accounts/verify_credentials', VerifyCredentials.as_view()),
|
path('api/v1/accounts/verify_credentials', views.VerifyCredentials.as_view()),
|
||||||
path('api/v1/accounts/update_credentials',
|
path('api/v1/accounts/update_credentials',
|
||||||
UpdateCredentials.as_view()),
|
views.UpdateCredentials.as_view()),
|
||||||
|
|
||||||
path('api/v1/accounts/search', AccountsSearch.as_view()),
|
path('api/v1/accounts/search', views.AccountsSearch.as_view()),
|
||||||
|
|
||||||
path('api/v1/accounts/<user>', User.as_view()),
|
path('api/v1/accounts/<user>', views.User.as_view()),
|
||||||
path('api/v1/accounts/<user>/statuses', Statuses.as_view()),
|
path('api/v1/accounts/<user>/statuses', views.Statuses.as_view()),
|
||||||
path('api/v1/accounts/<user>/following', Following.as_view()),
|
path('api/v1/accounts/<user>/following', views.Following.as_view()),
|
||||||
path('api/v1/accounts/<user>/followers', Followers.as_view()),
|
path('api/v1/accounts/<user>/followers', views.Followers.as_view()),
|
||||||
path('api/v1/accounts/<user>/follow', Follow.as_view()),
|
path('api/v1/accounts/<user>/follow', views.FollowUser.as_view()),
|
||||||
path('api/v1/accounts/<user>/unfollow', Unfollow.as_view()),
|
path('api/v1/accounts/<user>/unfollow', views.UnfollowUser.as_view()),
|
||||||
|
|
||||||
path('api/v1/statuses', Statuses.as_view()),
|
path('api/v1/statuses', views.Statuses.as_view()),
|
||||||
path('api/v1/statuses/<status>', SpecificStatus.as_view()),
|
path('api/v1/statuses/<status>', views.SpecificStatus.as_view()),
|
||||||
path('api/v1/statuses/<status>/context', StatusContext.as_view()),
|
path('api/v1/statuses/<status>/context', views.StatusContext.as_view()),
|
||||||
|
|
||||||
# Favourite, aka like
|
# Favourite, aka like
|
||||||
path('api/v1/statuses/<status>/favourite', Favourite.as_view()),
|
path('api/v1/statuses/<status>/favourite', views.Favourite.as_view()),
|
||||||
path('api/v1/statuses/<status>/unfavourite', Unfavourite.as_view()),
|
path('api/v1/statuses/<status>/unfavourite', views.Unfavourite.as_view()),
|
||||||
path('api/v1/statuses/<status>/favourited_by', StatusFavouritedBy.as_view()),
|
path('api/v1/statuses/<status>/favourited_by', views.StatusFavouritedBy.as_view()),
|
||||||
|
|
||||||
# Reblog, aka boost
|
# Reblog, aka boost
|
||||||
path('api/v1/statuses/<status>/reblog', Reblog.as_view()),
|
path('api/v1/statuses/<status>/reblog', views.Reblog.as_view()),
|
||||||
path('api/v1/statuses/<status>/unreblog', Unreblog.as_view()),
|
path('api/v1/statuses/<status>/unreblog', views.Unreblog.as_view()),
|
||||||
path('api/v1/statuses/<status>/reblogged_by', StatusRebloggedBy.as_view()),
|
path('api/v1/statuses/<status>/reblogged_by', views.StatusRebloggedBy.as_view()),
|
||||||
|
|
||||||
path('api/v1/notifications', Notifications.as_view()),
|
path('api/v1/notifications', views.Notifications.as_view()),
|
||||||
path('api/v1/filters', Filters.as_view()),
|
path('api/v1/filters', views.Filters.as_view()),
|
||||||
path('api/v1/custom_emojis', Emojis.as_view()),
|
path('api/v1/custom_emojis', views.Emojis.as_view()),
|
||||||
path('api/v1/timelines/public', PublicTimeline.as_view()),
|
path('api/v1/timelines/public', views.PublicTimeline.as_view()),
|
||||||
path('api/v1/timelines/home', HomeTimeline.as_view()),
|
path('api/v1/timelines/home', views.HomeTimeline.as_view()),
|
||||||
|
|
||||||
path('api/v1/search', Search.as_view()),
|
path('api/v1/search', views.Search.as_view()),
|
||||||
|
|
||||||
path('users/<username>/feed', UserFeed.as_view()),
|
path('users/<username>/feed', views.UserFeed.as_view()),
|
||||||
]
|
]
|
||||||
|
|
|
@ -1,73 +1,38 @@
|
||||||
# trilby_api/views/__init__.py
|
from .oauth import *
|
||||||
#
|
from .other import *
|
||||||
# Part of kepi.
|
from .persons import *
|
||||||
# Copyright (c) 2018-2021 Marnanel Thurman.
|
from .statuses import *
|
||||||
# Licensed under the GNU Public License v2.
|
from .timelines import *
|
||||||
|
|
||||||
from .other import \
|
|
||||||
Instance, \
|
|
||||||
Emojis, \
|
|
||||||
Filters, \
|
|
||||||
Search, \
|
|
||||||
AccountsSearch
|
|
||||||
|
|
||||||
from .statuses import \
|
|
||||||
Favourite, Unfavourite, \
|
|
||||||
Reblog, Unreblog, \
|
|
||||||
SpecificStatus, \
|
|
||||||
Statuses, \
|
|
||||||
StatusContext, \
|
|
||||||
StatusFavouritedBy, \
|
|
||||||
StatusRebloggedBy, \
|
|
||||||
Notifications
|
|
||||||
|
|
||||||
from .persons import \
|
|
||||||
Follow, Unfollow, \
|
|
||||||
UpdateCredentials, \
|
|
||||||
VerifyCredentials,\
|
|
||||||
User, \
|
|
||||||
Followers, Following
|
|
||||||
|
|
||||||
from .oauth import \
|
|
||||||
Apps, \
|
|
||||||
fix_oauth2_redirects
|
|
||||||
|
|
||||||
from .timelines import \
|
|
||||||
PublicTimeline, \
|
|
||||||
HomeTimeline, \
|
|
||||||
UserFeed
|
|
||||||
|
|
||||||
__all__ = [
|
__all__ = [
|
||||||
# other
|
'AbstractTimeline',
|
||||||
'Instance',
|
|
||||||
'Emojis',
|
|
||||||
'Filters',
|
|
||||||
'Search',
|
|
||||||
'AccountsSearch',
|
'AccountsSearch',
|
||||||
|
'Apps',
|
||||||
# statuses
|
'DoSomethingWithPerson',
|
||||||
'Favourite', 'Unfavourite',
|
'DoSomethingWithStatus',
|
||||||
'Reblog', 'Unreblog',
|
'Emojis',
|
||||||
|
'Favourite',
|
||||||
|
'Filters',
|
||||||
|
'FollowUser',
|
||||||
|
'Followers',
|
||||||
|
'Followers_or_Following',
|
||||||
|
'Following',
|
||||||
|
'HomeTimeline',
|
||||||
|
'Instance',
|
||||||
|
'Notifications',
|
||||||
|
'PublicTimeline',
|
||||||
|
'Reblog',
|
||||||
|
'Search',
|
||||||
'SpecificStatus',
|
'SpecificStatus',
|
||||||
'Statuses',
|
|
||||||
'StatusContext',
|
'StatusContext',
|
||||||
|
'Statuses',
|
||||||
'StatusFavouritedBy',
|
'StatusFavouritedBy',
|
||||||
'StatusRebloggedBy',
|
'StatusRebloggedBy',
|
||||||
'Notifications',
|
'Unfavourite',
|
||||||
|
'UnfollowUser',
|
||||||
# persons
|
'Unreblog',
|
||||||
'Follow', 'Unfollow',
|
|
||||||
'UpdateCredentials',
|
'UpdateCredentials',
|
||||||
'VerifyCredentials',
|
|
||||||
'User',
|
'User',
|
||||||
'Followers', 'Following',
|
|
||||||
|
|
||||||
# oauth
|
|
||||||
'Apps',
|
|
||||||
'fix_oauth2_redirects',
|
|
||||||
|
|
||||||
# timelines
|
|
||||||
'PublicTimeline',
|
|
||||||
'HomeTimeline',
|
|
||||||
'UserFeed',
|
'UserFeed',
|
||||||
|
'VerifyCredentials',
|
||||||
]
|
]
|
||||||
|
|
|
@ -71,7 +71,7 @@ class DoSomethingWithPerson(generics.GenericAPIView):
|
||||||
reason = 'Done',
|
reason = 'Done',
|
||||||
)
|
)
|
||||||
|
|
||||||
class Follow(DoSomethingWithPerson):
|
class FollowUser(DoSomethingWithPerson):
|
||||||
|
|
||||||
def _do_something_with(self, the_person, request):
|
def _do_something_with(self, the_person, request):
|
||||||
|
|
||||||
|
@ -119,7 +119,7 @@ class Follow(DoSomethingWithPerson):
|
||||||
except IntegrityError:
|
except IntegrityError:
|
||||||
logger.info(' -- not creating a follow; it already exists')
|
logger.info(' -- not creating a follow; it already exists')
|
||||||
|
|
||||||
class Unfollow(DoSomethingWithPerson):
|
class UnfollowUser(DoSomethingWithPerson):
|
||||||
|
|
||||||
def _do_something_with(self, the_person, request):
|
def _do_something_with(self, the_person, request):
|
||||||
|
|
||||||
|
@ -142,76 +142,6 @@ class Unfollow(DoSomethingWithPerson):
|
||||||
logger.info(' -- not unfollowing; they weren\'t following '+\
|
logger.info(' -- not unfollowing; they weren\'t following '+\
|
||||||
'in the first place')
|
'in the first place')
|
||||||
|
|
||||||
class UpdateCredentials(generics.GenericAPIView):
|
|
||||||
|
|
||||||
def patch(self, request, *args, **kwargs):
|
|
||||||
|
|
||||||
if request.user is None:
|
|
||||||
logger.debug(' -- user not logged in')
|
|
||||||
return error_response(401, 'Not logged in')
|
|
||||||
|
|
||||||
who = request.user.localperson
|
|
||||||
|
|
||||||
# The Mastodon spec doesn't say what to do
|
|
||||||
# if the user submits field names which don't
|
|
||||||
# exist!
|
|
||||||
|
|
||||||
unknown_fields = []
|
|
||||||
|
|
||||||
# FIXME: the data in "v" needs cleaning.
|
|
||||||
|
|
||||||
logger.info('-- updating user: %s', who)
|
|
||||||
|
|
||||||
for f,v in request.data.items():
|
|
||||||
|
|
||||||
logger.info(' -- setting %s = %s', f, v)
|
|
||||||
|
|
||||||
if f=='discoverable':
|
|
||||||
raise Http404("discoverable is not yet supported")
|
|
||||||
elif f=='bot':
|
|
||||||
who.bot = v
|
|
||||||
elif f=='display_name':
|
|
||||||
who.display_name = v
|
|
||||||
elif f=='note':
|
|
||||||
who.note = v
|
|
||||||
elif f=='avatar':
|
|
||||||
raise Http404("images are not yet supported")
|
|
||||||
elif f=='header':
|
|
||||||
raise Http404("images are not yet supported")
|
|
||||||
elif f=='locked':
|
|
||||||
who.locked = v
|
|
||||||
elif f=='source[privacy]':
|
|
||||||
who.default_visibility = v
|
|
||||||
elif f=='source[sensitive]':
|
|
||||||
who.default_sensitive = v
|
|
||||||
elif f=='source[language]':
|
|
||||||
who.language = v
|
|
||||||
elif f=='fields_attributes':
|
|
||||||
raise Http404("fields are not yet supported")
|
|
||||||
else:
|
|
||||||
logger.info(' -- field does not exist')
|
|
||||||
unknown_fields.append(f)
|
|
||||||
|
|
||||||
if unknown_fields:
|
|
||||||
logger.info(' -- aborting because of unknown fields')
|
|
||||||
raise Http404(f"some fields do not exist: {unknown_fields}")
|
|
||||||
|
|
||||||
who.save()
|
|
||||||
logger.info(' -- done.')
|
|
||||||
|
|
||||||
serializer = UserSerializerWithSource(
|
|
||||||
who,
|
|
||||||
context = {
|
|
||||||
'request': request,
|
|
||||||
},
|
|
||||||
)
|
|
||||||
|
|
||||||
return JsonResponse(
|
|
||||||
serializer.data,
|
|
||||||
status = 200,
|
|
||||||
reason = 'Done',
|
|
||||||
)
|
|
||||||
|
|
||||||
###########################
|
###########################
|
||||||
|
|
||||||
class VerifyCredentials(generics.GenericAPIView):
|
class VerifyCredentials(generics.GenericAPIView):
|
||||||
|
|
|
@ -183,117 +183,6 @@ class Unreblog(DoSomethingWithStatus):
|
||||||
|
|
||||||
###########################
|
###########################
|
||||||
|
|
||||||
class DoSomethingWithPerson(generics.GenericAPIView):
|
|
||||||
|
|
||||||
serializer_class = UserSerializer
|
|
||||||
queryset = trilby_models.Person.objects.all()
|
|
||||||
|
|
||||||
def _do_something_with(self, the_person, request):
|
|
||||||
raise NotImplementedError()
|
|
||||||
|
|
||||||
def post(self, request, *args, **kwargs):
|
|
||||||
|
|
||||||
if request.user is None:
|
|
||||||
logger.debug(' -- user not logged in')
|
|
||||||
return error_response(401, 'Not logged in')
|
|
||||||
|
|
||||||
try:
|
|
||||||
the_person = get_object_or_404(
|
|
||||||
self.get_queryset(),
|
|
||||||
id = int(kwargs['user']),
|
|
||||||
)
|
|
||||||
except ValueError:
|
|
||||||
return error_response(404, 'Non-decimal ID')
|
|
||||||
|
|
||||||
result = self._do_something_with(the_person, request)
|
|
||||||
|
|
||||||
if result is None:
|
|
||||||
result = the_person
|
|
||||||
|
|
||||||
serializer = UserSerializer(
|
|
||||||
result,
|
|
||||||
context = {
|
|
||||||
'request': request,
|
|
||||||
},
|
|
||||||
)
|
|
||||||
|
|
||||||
return JsonResponse(
|
|
||||||
serializer.data,
|
|
||||||
status = 200,
|
|
||||||
reason = 'Done',
|
|
||||||
)
|
|
||||||
|
|
||||||
class Follow(DoSomethingWithPerson):
|
|
||||||
|
|
||||||
def _do_something_with(self, the_person, request):
|
|
||||||
|
|
||||||
try:
|
|
||||||
|
|
||||||
if the_person.auto_follow:
|
|
||||||
offer = None
|
|
||||||
else:
|
|
||||||
number = random.randint(0, 0xffffffff)
|
|
||||||
offer = uri_to_url(settings.KEPI['FOLLOW_REQUEST_LINK'] % {
|
|
||||||
'username': request.user.username,
|
|
||||||
'number': number,
|
|
||||||
})
|
|
||||||
|
|
||||||
follow = trilby_models.Follow(
|
|
||||||
follower = request.user.localperson,
|
|
||||||
following = the_person,
|
|
||||||
offer = offer,
|
|
||||||
)
|
|
||||||
|
|
||||||
with transaction.atomic():
|
|
||||||
follow.save(
|
|
||||||
send_signal = True,
|
|
||||||
)
|
|
||||||
|
|
||||||
logger.info(' -- follow: %s', follow)
|
|
||||||
logger.debug(' -- offer ID: %s', offer)
|
|
||||||
|
|
||||||
if the_person.auto_follow:
|
|
||||||
follow_back = trilby_models.Follow(
|
|
||||||
follower = the_person,
|
|
||||||
following = request.user.localperson,
|
|
||||||
offer = None,
|
|
||||||
)
|
|
||||||
|
|
||||||
with transaction.atomic():
|
|
||||||
follow_back.save(
|
|
||||||
send_signal = True,
|
|
||||||
)
|
|
||||||
|
|
||||||
logger.info(' -- follow back: %s', follow_back)
|
|
||||||
|
|
||||||
return the_person
|
|
||||||
|
|
||||||
except IntegrityError:
|
|
||||||
logger.info(' -- not creating a follow; it already exists')
|
|
||||||
|
|
||||||
class Unfollow(DoSomethingWithPerson):
|
|
||||||
|
|
||||||
def _do_something_with(self, the_person, request):
|
|
||||||
|
|
||||||
try:
|
|
||||||
follow = trilby_models.Follow.objects.get(
|
|
||||||
follower = request.user.localperson,
|
|
||||||
following = the_person,
|
|
||||||
)
|
|
||||||
|
|
||||||
logger.info(' -- unfollowing: %s', follow)
|
|
||||||
|
|
||||||
with transaction.atomic():
|
|
||||||
follow.delete(
|
|
||||||
send_signal = True,
|
|
||||||
)
|
|
||||||
|
|
||||||
return the_person
|
|
||||||
|
|
||||||
except trilby_models.Follow.DoesNotExist:
|
|
||||||
logger.info(' -- not unfollowing; they weren\'t following '+\
|
|
||||||
'in the first place')
|
|
||||||
|
|
||||||
class SpecificStatus(generics.GenericAPIView):
|
class SpecificStatus(generics.GenericAPIView):
|
||||||
|
|
||||||
queryset = trilby_models.Status.objects.filter(remote_url=None)
|
queryset = trilby_models.Status.objects.filter(remote_url=None)
|
||||||
|
|
Ładowanie…
Reference in New Issue