Repository: bookwyrm-social/bookwyrm

Issue:
Pleroma not recognizing follow request Accept activity
When I follow a bookwyrm user from pleroma, the follow request is received in bookwyrm and the `Accept` is sent out, ostensibly successfully, but poleroma doesn't recognize that the follow request has been approved. It appears to work fine in the other direction (following a pleroma user from bookwyrm).


Discussion:
I've reported the issue on the Pleroma bug-tracker here: <https://git.pleroma.social/pleroma/pleroma/-/issues/2537>.
From the pleroma bug-tracker, the issue appears to be that Pleroma expects the actor to have a `following` field -- technically a bug on pleroma's side, but I think it would be a straightforward addition to actor serialization on our end.

Resolving patch:
diff --git a/bookwyrm/activitypub/person.py b/bookwyrm/activitypub/person.py
--- a/bookwyrm/activitypub/person.py
+++ b/bookwyrm/activitypub/person.py
@@ -23,6 +23,7 @@ class Person(ActivityObject):
     inbox: str
     publicKey: PublicKey
     followers: str = None
+    following: str = None
     outbox: str = None
     endpoints: Dict = None
     name: str = None
diff --git a/bookwyrm/models/activitypub_mixin.py b/bookwyrm/models/activitypub_mixin.py
--- a/bookwyrm/models/activitypub_mixin.py
+++ b/bookwyrm/models/activitypub_mixin.py
@@ -1,5 +1,6 @@
 """ activitypub model functionality """
 from base64 import b64encode
+from collections import namedtuple
 from functools import reduce
 import json
 import operator
@@ -25,6 +26,15 @@
 logger = logging.getLogger(__name__)
 # I tried to separate these classes into mutliple files but I kept getting
 # circular import errors so I gave up. I'm sure it could be done though!
+
+PropertyField = namedtuple("PropertyField", ("set_activity_from_field"))
+
+
+def set_activity_from_property_field(activity, obj, field):
+    """ assign a model property value to the activity json """
+    activity[field[1]] = getattr(obj, field[0])
+
+
 class ActivitypubMixin:
     """ add this mixin for models that are AP serializable """
 
@@ -52,6 +62,11 @@ def __init__(self, *args, **kwargs):
         self.activity_fields = (
             self.image_fields + self.many_to_many_fields + self.simple_fields
         )
+        if hasattr(self, "property_fields"):
+            self.activity_fields += [
+                PropertyField(lambda a, o: set_activity_from_property_field(a, o, f))
+                for f in self.property_fields
+            ]
 
         # these are separate to avoid infinite recursion issues
         self.deserialize_reverse_fields = (
@@ -430,7 +445,7 @@ def generate_activity(obj):
         ) in obj.serialize_reverse_fields:
             related_field = getattr(obj, model_field_name)
             activity[activity_field_name] = unfurl_related_field(
-                related_field, sort_field
+                related_field, sort_field=sort_field
             )
 
     if not activity.get("id"):
@@ -440,7 +455,7 @@ def generate_activity(obj):
 
 def unfurl_related_field(related_field, sort_field=None):
     """ load reverse lookups (like public key owner or Status attachment """
-    if hasattr(related_field, "all"):
+    if sort_field and hasattr(related_field, "all"):
         return [
             unfurl_related_field(i) for i in related_field.order_by(sort_field).all()
         ]
diff --git a/bookwyrm/models/user.py b/bookwyrm/models/user.py
--- a/bookwyrm/models/user.py
+++ b/bookwyrm/models/user.py
@@ -112,6 +112,12 @@ class User(OrderedCollectionPageMixin, AbstractUser):
     )
 
     name_field = "username"
+    property_fields = [("following_link", "following")]
+
+    @property
+    def following_link(self):
+        """ just how to find out the following info """
+        return "{:s}/following".format(self.remote_id)
 
     @property
     def alt_text(self):


Test patch: