I thought I was going to be the first one to make these but as it turns out, Misskey already has this functionality, but it’s implemented in a weird way. Here’s the test post I made: https://misskey.io/notes/86wfb5uayv
There’s the URL for the quoted post, but for some reason it’s in two different fields. Of course the _misskey ones are completely custom, but I can’t seem to figure out where quoteUrl comes from and what it’s used by — it’s not in the ActivityStreams vocabulary.
There’s also this URL in the content, presumably for backwards compatibility with Mastodon, Pleroma etc.
Anyway, let’s figure out the best way to standardize these reposts.
I would suggest to send two activities: Announce a post and then Create a reply to that post. That way we don’t need any custom fields or actions. This pair ensures that all references/quotes in the Reply will be correctly understood by recipients: they will see exactly the same post as the “reposting with a quote” Actor sees.
Client app (including Web site) seeing this pair of posts can show it as it wish…
I sat down to translate tumblr posts into an ActivityPub format a little while ago, and i settled on doing “reblog with note” this way as well, although for ease of client consumption, i combined both types into one activity:
"type": ["Create", "Announce"],
"content": "this is my reply note!",
"content": "the first note!",
How would software tell the difference between repost with quote as one action and announce/boost + reply as two distinct actions? What if there’s a network error and only one of the activities makes it to the other side?
This is going to break many implementations, including mine
What does Mastodon do when it receives such an activity?
I see, actually you are thinking about some kind of a transaction, a composition of two activities sent in one network message.
Interesting. But we should find out a general way to do this, and not inventing a new format for any new combination. E.g. simply send a collection of two (or more…) activities as one message…
Yeah well, technically it’s valid JSON-LD and it would, of course, work with my implementation of JSON-LD processing algorithms, but further down the line I map ActivityPub types to Java classes for convenience and deserialize the whole thing into an object of that class. This can’t possibly work with a statically-typed language.
Of course you can. I can as well map [Create, Announce] to an internal activity type like Repost which extends Create. My point was that all possible type combinations have to be known at compile time.
But then what use it is if you’re able to parse an activity but still don’t know what to do with it? I’m not making much sense with this. Sorry. Quarantine is taking its toll.
But, honestly, I probably wouldn’t bother with the different interfaces, considering that the only types in ActivityStreams that affect the properties objects are allowed to have are Link, Object, and Activity.
Then implementations like Mastodon would display these as simple retoots boosts, losing the additional content. The way Misskey does it is actually rather smart in that Mastodon displays everything that’s in there, and Misskey itself strips the link and replaces it with its own richer UI.
the first implementation I had did use a content field on Announce, basically duplicating all of the properties a Note can have, but it felt like it was muddying the distinction between Objects and Activities, it led to a ton of special case processing and a lot of code like (attributedTo || actor) where I had to handle the complexities of processing both types of object. ultimately, I think using inReplyTo and Announce makes a lot more sense when considering the actual difference between reblog-with-note and reply-to-thread—the expected audience and context of the reply
So ultimately I decided on the Announced object here actually being your own post, which I think works well. in more thread-based forms of microblogging this would translate to replying to a thread and then boosting your own reply into your timeline, which is basically exactly what you’re doing semantically.
How would one determine if this is a “reblog with note” or a reply when having just the object? For example if someone I don’t follow posts a “reblog with note” and then someone I do follow replies to it (putting the object uri into inReplyTo as every implementation does now), my server will fetch the object and assume it’s a reply, since it doesn’t have the activity type context
I thought about this approach another time, and it looks like adding your “quote / notes” (“content” in terms of ActivityPub) directly to the “Announce” (Activity) object is the best way to implement “Repost with a quote”:
Semantically “announcing” that “quoted” object (and not OUR own notes on that) IS the activity that is supposed to be used here. From spec: “Indicates that the actor is calling the target 's attention the object.” - And we really mean that! We call attention and make our comment / quote…
We are NOT “replying” to that “someone else’s post”. Instead, we create new discussion context, for the new audience.
Both our comments/quotes (in the Announce activity’s name, summary and content properties) AND the “someone else’s post” are contained in ONE activity / message sent over the network / between systems. This is really important in order not to lose context of our “quotes” AND simply for a target audience to be able to see that “someone else’s post”.