Todays progress in my #ActivityPub adventure for the @andreas@pramari.de project: nothing much. Mostly (already) refactoring code and linting, that was compatible with my state of mind. However, I found a #tutorial that seemed reasonable to start from: https://github.com/timmot/activity-pub-tutorial In productive outcomes: the profile store now support generation of #public- and #private #keys. The generation is triggered on profile creation. I learned keys will be required in a later step to provide a signature.
Today in #ActivityPub. Progress with the Inbox for my future actor on the #fediverse
The server should now be able to receive events in the Inbox.
With that, it does already differentiate over the activity type as defined for Server to Server Interaction. Today, it does take the actor, the activity if 'Follow' and create a following
relationship later on. That will come handy once we start publishing to all Followers. It does roughly look like this:
class InboxView(View): @method_decorator(csrf_exempt) def dispatch(self, *args, **kwargs): return super().dispatch(*args, **kwargs) def post(self, request, *args, **kwargs): try: # Assuming the request payload is a valid JSON activity body = request.body.decode('utf-8') activity = json.loads(body) # Deserialize the JSON payload except ValueError: return JsonResponse({'error': 'Invalid JSON'}, status=400) # Process the incoming activity # Save the activity to the database, perform necessary actions, etc. from .models import Activity activity_type = activity.get('type') activity_actor = activity.get('actor') activity_object = activity.get('object', {}) object_type = activity_object.get("type") object_content = activity_object.get("content") match(activity_type): case 'Follow': # Store the follower in the database follower_profile, created = Profile.objects.get_or_create( username=activity_actor ) if created: follower_profile.save() profile_to_follow = Profile.objects.get(username=request.user.username) follower_profile.following.add(profile_to_follow) # Optionally, you can send a response indicating the success of the follow request response = { "type": "Follow", "actor": request.build_absolute_uri( reverse( "profile", args=[request.user.username] ) ), "object": request.build_absolute_uri( reverse( "profile", args=[activity_actor] ) ), "id": "unique-id-for-the-response", } return JsonResponse(response) case 'Create': # Save the Follow activity to the database pass case _: logger.error("Activity Actor: {}".format(activity_actor)) logger.error("Activity Object: {}".format(activity_object)) print(activity) follow_activity = Activity( actor=activity_actor, verb=f'Unkown: {activity_type}', object=activity_object ) follow_activity.save() return JsonResponse({"error": "Invalid activity type"}, status=400) # Return a success response return JsonResponse({'status': 'success'})
With that, @andreas@pramari.de comes a bit closer.
Further advancements with @andreas@pramari.de: Yakshaving. Google Cloud SDK outdated. And Python on the development machine outdated, too.
@reiver@mastodon.social kind of. I am implementing the server component that clients are calling.
/.well-known/host-meta is currently implemented as a static file.
What do I have to pay attention to in this flow?
If you are implementing a server, then you will likely have to contact other servers.
(For example, to do a remote-follow.)
You should request host-meta on the remote-server first, to see where WebFinger is. And not just assume it is at the /.well-known/ path.
My adventure in implementing #ActivityPub:
With the support of #ChatGPT I roughly understand how the protocol works and have a working implementation for at least #WebFinger and a json-LD conforming profile page.
There are some gaps in the structures, e.g. I can lookup my own user profile, @andreas@pramari.de, from microblog.pub, but not from mastodon.social yet. Obviously following does not yet work, although I have the data structures in place to store followers by profile.
Next up is the implementation of a mechanism to follow.