mirror of
https://github.com/bookwyrm-social/bookwyrm.git
synced 2024-06-26 08:50:39 +00:00
5b051631ec
* update User model to allow for moved_to and also_known_as values * allow users to add aliases (also_known_as) in UI * allow users to move account to another one (moved_to) * redirect webfinger to the new account after a move * present notification to followers inviting to follow at new account Note: unlike Mastodon we're not running any unfollow/autofollow action here: users can decide for themselves This makes undoing moves easier. TODO There is still a bug with incoming Moves, at least from Mastodon. This seems to be something to do with Update activities (rather than Move, strictly).
258 lines
7.1 KiB
Python
258 lines
7.1 KiB
Python
""" activities that do things """
|
|
from dataclasses import dataclass, field
|
|
from typing import List
|
|
from django.apps import apps
|
|
|
|
from .base_activity import ActivityObject, Signature, resolve_remote_id
|
|
from .ordered_collection import CollectionItem
|
|
|
|
|
|
@dataclass(init=False)
|
|
class Verb(ActivityObject):
|
|
"""generic fields for activities"""
|
|
|
|
actor: str
|
|
object: ActivityObject
|
|
|
|
def action(self, allow_external_connections=True):
|
|
"""usually we just want to update and save"""
|
|
# self.object may return None if the object is invalid in an expected way
|
|
# ie, Question type
|
|
if self.object:
|
|
self.object.to_model(allow_external_connections=allow_external_connections)
|
|
|
|
|
|
# pylint: disable=invalid-name
|
|
@dataclass(init=False)
|
|
class Create(Verb):
|
|
"""Create activity"""
|
|
|
|
to: List[str]
|
|
cc: List[str] = field(default_factory=lambda: [])
|
|
signature: Signature = None
|
|
type: str = "Create"
|
|
|
|
|
|
# pylint: disable=invalid-name
|
|
@dataclass(init=False)
|
|
class Delete(Verb):
|
|
"""Create activity"""
|
|
|
|
to: List[str] = field(default_factory=lambda: [])
|
|
cc: List[str] = field(default_factory=lambda: [])
|
|
type: str = "Delete"
|
|
|
|
def action(self, allow_external_connections=True):
|
|
"""find and delete the activity object"""
|
|
if not self.object:
|
|
return
|
|
|
|
if isinstance(self.object, str):
|
|
# Deleted users are passed as strings. Not wild about this fix
|
|
model = apps.get_model("bookwyrm.User")
|
|
obj = model.find_existing_by_remote_id(self.object)
|
|
else:
|
|
obj = self.object.to_model(
|
|
save=False,
|
|
allow_create=False,
|
|
allow_external_connections=allow_external_connections,
|
|
)
|
|
|
|
if obj:
|
|
obj.delete()
|
|
# if we can't find it, we don't need to delete it because we don't have it
|
|
|
|
|
|
# pylint: disable=invalid-name
|
|
@dataclass(init=False)
|
|
class Update(Verb):
|
|
"""Update activity"""
|
|
|
|
to: List[str]
|
|
type: str = "Update"
|
|
|
|
def action(self, allow_external_connections=True):
|
|
"""update a model instance from the dataclass"""
|
|
if not self.object:
|
|
return
|
|
# BUG: THIS IS THROWING A DUPLIATE USERNAME ERROR WHEN WE GET AN "UPDATE" AFTER/BEFORE A "MOVE"
|
|
# FROM MASTODON - BUT ONLY SINCE WE ADDED MOVEUSER
|
|
# is it something to do with the updated User model?
|
|
self.object.to_model(
|
|
allow_create=False, allow_external_connections=allow_external_connections
|
|
)
|
|
|
|
|
|
@dataclass(init=False)
|
|
class Undo(Verb):
|
|
"""Undo an activity"""
|
|
|
|
type: str = "Undo"
|
|
|
|
def action(self, allow_external_connections=True):
|
|
"""find and remove the activity object"""
|
|
if isinstance(self.object, str):
|
|
# it may be that something should be done with these, but idk what
|
|
# this seems just to be coming from pleroma
|
|
return
|
|
|
|
# this is so hacky but it does make it work....
|
|
# (because you Reject a request and Undo a follow
|
|
model = None
|
|
if self.object.type == "Follow":
|
|
model = apps.get_model("bookwyrm.UserFollows")
|
|
obj = self.object.to_model(
|
|
model=model,
|
|
save=False,
|
|
allow_create=False,
|
|
allow_external_connections=allow_external_connections,
|
|
)
|
|
if not obj:
|
|
# this could be a follow request not a follow proper
|
|
model = apps.get_model("bookwyrm.UserFollowRequest")
|
|
obj = self.object.to_model(
|
|
model=model,
|
|
save=False,
|
|
allow_create=False,
|
|
allow_external_connections=allow_external_connections,
|
|
)
|
|
else:
|
|
obj = self.object.to_model(
|
|
model=model,
|
|
save=False,
|
|
allow_create=False,
|
|
allow_external_connections=allow_external_connections,
|
|
)
|
|
if not obj:
|
|
# if we don't have the object, we can't undo it. happens a lot with boosts
|
|
return
|
|
obj.delete()
|
|
|
|
|
|
@dataclass(init=False)
|
|
class Follow(Verb):
|
|
"""Follow activity"""
|
|
|
|
object: str
|
|
type: str = "Follow"
|
|
|
|
def action(self, allow_external_connections=True):
|
|
"""relationship save"""
|
|
self.to_model(allow_external_connections=allow_external_connections)
|
|
|
|
|
|
@dataclass(init=False)
|
|
class Block(Verb):
|
|
"""Block activity"""
|
|
|
|
object: str
|
|
type: str = "Block"
|
|
|
|
def action(self, allow_external_connections=True):
|
|
"""relationship save"""
|
|
self.to_model(allow_external_connections=allow_external_connections)
|
|
|
|
|
|
@dataclass(init=False)
|
|
class Accept(Verb):
|
|
"""Accept activity"""
|
|
|
|
object: Follow
|
|
type: str = "Accept"
|
|
|
|
def action(self, allow_external_connections=True):
|
|
"""accept a request"""
|
|
obj = self.object.to_model(save=False, allow_create=True)
|
|
obj.accept()
|
|
|
|
|
|
@dataclass(init=False)
|
|
class Reject(Verb):
|
|
"""Reject activity"""
|
|
|
|
object: Follow
|
|
type: str = "Reject"
|
|
|
|
def action(self, allow_external_connections=True):
|
|
"""reject a follow request"""
|
|
obj = self.object.to_model(save=False, allow_create=False)
|
|
obj.reject()
|
|
|
|
|
|
@dataclass(init=False)
|
|
class Add(Verb):
|
|
"""Add activity"""
|
|
|
|
target: ActivityObject
|
|
object: CollectionItem
|
|
type: str = "Add"
|
|
|
|
def action(self, allow_external_connections=True):
|
|
"""figure out the target to assign the item to a collection"""
|
|
target = resolve_remote_id(self.target)
|
|
item = self.object.to_model(save=False)
|
|
setattr(item, item.collection_field, target)
|
|
item.save()
|
|
|
|
|
|
@dataclass(init=False)
|
|
class Remove(Add):
|
|
"""Remove activity"""
|
|
|
|
type: str = "Remove"
|
|
|
|
def action(self, allow_external_connections=True):
|
|
"""find and remove the activity object"""
|
|
obj = self.object.to_model(save=False, allow_create=False)
|
|
if obj:
|
|
obj.delete()
|
|
|
|
|
|
@dataclass(init=False)
|
|
class Like(Verb):
|
|
"""a user faving an object"""
|
|
|
|
object: str
|
|
type: str = "Like"
|
|
|
|
def action(self, allow_external_connections=True):
|
|
"""like"""
|
|
self.to_model(allow_external_connections=allow_external_connections)
|
|
|
|
|
|
# pylint: disable=invalid-name
|
|
@dataclass(init=False)
|
|
class Announce(Verb):
|
|
"""boosting a status"""
|
|
|
|
published: str
|
|
to: List[str] = field(default_factory=lambda: [])
|
|
cc: List[str] = field(default_factory=lambda: [])
|
|
object: str
|
|
type: str = "Announce"
|
|
|
|
def action(self, allow_external_connections=True):
|
|
"""boost"""
|
|
self.to_model(allow_external_connections=allow_external_connections)
|
|
|
|
|
|
@dataclass(init=False)
|
|
class Move(Verb):
|
|
"""a user moving an object"""
|
|
|
|
object: str
|
|
type: str = "Move"
|
|
origin: str = None
|
|
target: str = None
|
|
|
|
def action(self, allow_external_connections=True):
|
|
"""move"""
|
|
|
|
object_is_user = resolve_remote_id(remote_id=self.object, model="User")
|
|
|
|
if object_is_user:
|
|
model = apps.get_model("bookwyrm.MoveUser")
|
|
self.to_model(model=model)
|
|
else:
|
|
return
|