forked from AkkomaGang/akkoma
Document follow relationship updates and cleanup
This commit is contained in:
parent
35ba48494f
commit
c9afb350e7
5 changed files with 42 additions and 34 deletions
|
@ -4,7 +4,7 @@ A Pleroma instance can be identified by "<Mastodon version> (compatible; Pleroma
|
||||||
|
|
||||||
## Flake IDs
|
## Flake IDs
|
||||||
|
|
||||||
Pleroma uses 128-bit ids as opposed to Mastodon's 64 bits. However just like Mastodon's ids they are lexically sortable strings
|
Pleroma uses 128-bit ids as opposed to Mastodon's 64 bits. However, just like Mastodon's ids, they are lexically sortable strings
|
||||||
|
|
||||||
## Timelines
|
## Timelines
|
||||||
|
|
||||||
|
@ -26,8 +26,8 @@ Has these additional fields under the `pleroma` object:
|
||||||
- `conversation_id`: the ID of the AP context the status is associated with (if any)
|
- `conversation_id`: the ID of the AP context the status is associated with (if any)
|
||||||
- `direct_conversation_id`: the ID of the Mastodon direct message conversation the status is associated with (if any)
|
- `direct_conversation_id`: the ID of the Mastodon direct message conversation the status is associated with (if any)
|
||||||
- `in_reply_to_account_acct`: the `acct` property of User entity for replied user (if any)
|
- `in_reply_to_account_acct`: the `acct` property of User entity for replied user (if any)
|
||||||
- `content`: a map consisting of alternate representations of the `content` property with the key being it's mimetype. Currently the only alternate representation supported is `text/plain`
|
- `content`: a map consisting of alternate representations of the `content` property with the key being its mimetype. Currently, the only alternate representation supported is `text/plain`
|
||||||
- `spoiler_text`: a map consisting of alternate representations of the `spoiler_text` property with the key being it's mimetype. Currently the only alternate representation supported is `text/plain`
|
- `spoiler_text`: a map consisting of alternate representations of the `spoiler_text` property with the key being its mimetype. Currently, the only alternate representation supported is `text/plain`
|
||||||
- `expires_at`: a datetime (iso8601) that states when the post will expire (be deleted automatically), or empty if the post won't expire
|
- `expires_at`: a datetime (iso8601) that states when the post will expire (be deleted automatically), or empty if the post won't expire
|
||||||
- `thread_muted`: true if the thread the post belongs to is muted
|
- `thread_muted`: true if the thread the post belongs to is muted
|
||||||
- `emoji_reactions`: A list with emoji / reaction maps. The format is `{name: "☕", count: 1, me: true}`. Contains no information about the reacting users, for that use the `/statuses/:id/reactions` endpoint.
|
- `emoji_reactions`: A list with emoji / reaction maps. The format is `{name: "☕", count: 1, me: true}`. Contains no information about the reacting users, for that use the `/statuses/:id/reactions` endpoint.
|
||||||
|
@ -170,9 +170,9 @@ Returns on success: 200 OK `{}`
|
||||||
|
|
||||||
Additional parameters can be added to the JSON body/Form data:
|
Additional parameters can be added to the JSON body/Form data:
|
||||||
|
|
||||||
- `preview`: boolean, if set to `true` the post won't be actually posted, but the status entitiy would still be rendered back. This could be useful for previewing rich text/custom emoji, for example.
|
- `preview`: boolean, if set to `true` the post won't be actually posted, but the status entity would still be rendered back. This could be useful for previewing rich text/custom emoji, for example.
|
||||||
- `content_type`: string, contain the MIME type of the status, it is transformed into HTML by the backend. You can get the list of the supported MIME types with the nodeinfo endpoint.
|
- `content_type`: string, contain the MIME type of the status, it is transformed into HTML by the backend. You can get the list of the supported MIME types with the nodeinfo endpoint.
|
||||||
- `to`: A list of nicknames (like `lain@soykaf.club` or `lain` on the local server) that will be used to determine who is going to be addressed by this post. Using this will disable the implicit addressing by mentioned names in the `status` body, only the people in the `to` list will be addressed. The normal rules for for post visibility are not affected by this and will still apply.
|
- `to`: A list of nicknames (like `lain@soykaf.club` or `lain` on the local server) that will be used to determine who is going to be addressed by this post. Using this will disable the implicit addressing by mentioned names in the `status` body, only the people in the `to` list will be addressed. The normal rules for post visibility are not affected by this and will still apply.
|
||||||
- `visibility`: string, besides standard MastoAPI values (`direct`, `private`, `unlisted`, `local` or `public`) it can be used to address a List by setting it to `list:LIST_ID`.
|
- `visibility`: string, besides standard MastoAPI values (`direct`, `private`, `unlisted`, `local` or `public`) it can be used to address a List by setting it to `list:LIST_ID`.
|
||||||
- `expires_in`: The number of seconds the posted activity should expire in. When a posted activity expires it will be deleted from the server, and a delete request for it will be federated. This needs to be longer than an hour.
|
- `expires_in`: The number of seconds the posted activity should expire in. When a posted activity expires it will be deleted from the server, and a delete request for it will be federated. This needs to be longer than an hour.
|
||||||
- `in_reply_to_conversation_id`: Will reply to a given conversation, addressing only the people who are part of the recipient set of that conversation. Sets the visibility to `direct`.
|
- `in_reply_to_conversation_id`: Will reply to a given conversation, addressing only the people who are part of the recipient set of that conversation. Sets the visibility to `direct`.
|
||||||
|
@ -279,10 +279,27 @@ Has these additional fields under the `pleroma` object:
|
||||||
|
|
||||||
## Streaming
|
## Streaming
|
||||||
|
|
||||||
|
### Chats
|
||||||
|
|
||||||
There is an additional `user:pleroma_chat` stream. Incoming chat messages will make the current chat be sent to this `user` stream. The `event` of an incoming chat message is `pleroma:chat_update`. The payload is the updated chat with the incoming chat message in the `last_message` field.
|
There is an additional `user:pleroma_chat` stream. Incoming chat messages will make the current chat be sent to this `user` stream. The `event` of an incoming chat message is `pleroma:chat_update`. The payload is the updated chat with the incoming chat message in the `last_message` field.
|
||||||
|
|
||||||
|
### Remote timelines
|
||||||
|
|
||||||
For viewing remote server timelines, there are `public:remote` and `public:remote:media` streams. Each of these accept a parameter like `?instance=lain.com`.
|
For viewing remote server timelines, there are `public:remote` and `public:remote:media` streams. Each of these accept a parameter like `?instance=lain.com`.
|
||||||
|
|
||||||
|
### Follow relationships updates
|
||||||
|
|
||||||
|
Pleroma streams follow relationships updatates as `pleroma:follow_relationships_update` events to the `user` stream.
|
||||||
|
|
||||||
|
The message playload consist of:
|
||||||
|
|
||||||
|
- `state`: a relationship state, one of `follow_pending`, `follow_accept` or `follow_reject`.
|
||||||
|
|
||||||
|
- `follower` and `following` maps with following fields:
|
||||||
|
- `id`: user ID
|
||||||
|
- `follower_count`: follower count
|
||||||
|
- `following_count`: following count
|
||||||
|
|
||||||
## User muting and thread muting
|
## User muting and thread muting
|
||||||
|
|
||||||
Both user muting and thread muting can be done for only a certain time by adding an `expires_in` parameter to the API calls and giving the expiration time in seconds.
|
Both user muting and thread muting can be done for only a certain time by adding an `expires_in` parameter to the API calls and giving the expiration time in seconds.
|
||||||
|
|
|
@ -96,7 +96,7 @@ def unfollow(%User{} = follower, %User{} = following) do
|
||||||
defp after_update(state, %User{} = follower, %User{} = following) do
|
defp after_update(state, %User{} = follower, %User{} = following) do
|
||||||
with {:ok, following} <- User.update_follower_count(following),
|
with {:ok, following} <- User.update_follower_count(following),
|
||||||
{:ok, follower} <- User.update_following_count(follower) do
|
{:ok, follower} <- User.update_following_count(follower) do
|
||||||
Pleroma.Web.Streamer.stream("relationships:update", %{
|
Pleroma.Web.Streamer.stream("follow_relationship", %{
|
||||||
state: state,
|
state: state,
|
||||||
following: following,
|
following: following,
|
||||||
follower: follower
|
follower: follower
|
||||||
|
|
|
@ -186,18 +186,15 @@ defp do_stream("direct", item) do
|
||||||
end)
|
end)
|
||||||
end
|
end
|
||||||
|
|
||||||
defp do_stream("relationships:update", item) do
|
defp do_stream("follow_relationship", item) do
|
||||||
text = StreamerView.render("relationships_update.json", item)
|
text = StreamerView.render("follow_relationships_update.json", item)
|
||||||
|
user_topic = "user:#{item.follower.id}"
|
||||||
|
|
||||||
[item.follower, item.following]
|
Logger.debug("Trying to push follow relationship update to #{user_topic}\n\n")
|
||||||
|> Enum.map(fn %{id: id} -> "user:#{id}" end)
|
|
||||||
|> Enum.each(fn user_topic ->
|
|
||||||
Logger.debug("Trying to push relationships:update to #{user_topic}\n\n")
|
|
||||||
|
|
||||||
Registry.dispatch(@registry, user_topic, fn list ->
|
Registry.dispatch(@registry, user_topic, fn list ->
|
||||||
Enum.each(list, fn {pid, _auth} ->
|
Enum.each(list, fn {pid, _auth} ->
|
||||||
send(pid, {:text, text})
|
send(pid, {:text, text})
|
||||||
end)
|
|
||||||
end)
|
end)
|
||||||
end)
|
end)
|
||||||
end
|
end
|
||||||
|
|
|
@ -74,9 +74,9 @@ def render("chat_update.json", %{chat_message_reference: cm_ref}) do
|
||||||
|> Jason.encode!()
|
|> Jason.encode!()
|
||||||
end
|
end
|
||||||
|
|
||||||
def render("relationships_update.json", item) do
|
def render("follow_relationships_update.json", item) do
|
||||||
%{
|
%{
|
||||||
event: "pleroma:relationships_update",
|
event: "pleroma:follow_relationships_update",
|
||||||
payload:
|
payload:
|
||||||
%{
|
%{
|
||||||
state: item.state,
|
state: item.state,
|
||||||
|
|
|
@ -404,15 +404,14 @@ test "it sends follow activities to the 'user:notification' stream", %{
|
||||||
refute Streamer.filtered_by_user?(user, notif)
|
refute Streamer.filtered_by_user?(user, notif)
|
||||||
end
|
end
|
||||||
|
|
||||||
test "it sends relationships updates to the 'user' stream", %{
|
test "it sends follow relationships updates to the 'user' stream", %{
|
||||||
user: user,
|
user: user,
|
||||||
token: oauth_token
|
token: oauth_token
|
||||||
} do
|
} do
|
||||||
user_id = user.id
|
user_id = user.id
|
||||||
user_url = user.ap_id
|
user_url = user.ap_id
|
||||||
follower = insert(:user)
|
other_user = insert(:user)
|
||||||
follower_token = insert(:oauth_token, user: follower)
|
other_user_id = other_user.id
|
||||||
follower_id = follower.id
|
|
||||||
|
|
||||||
body =
|
body =
|
||||||
File.read!("test/fixtures/users_mock/localhost.json")
|
File.read!("test/fixtures/users_mock/localhost.json")
|
||||||
|
@ -425,47 +424,42 @@ test "it sends relationships updates to the 'user' stream", %{
|
||||||
end)
|
end)
|
||||||
|
|
||||||
Streamer.get_topic_and_add_socket("user", user, oauth_token)
|
Streamer.get_topic_and_add_socket("user", user, oauth_token)
|
||||||
Streamer.get_topic_and_add_socket("user", follower, follower_token)
|
{:ok, _follower, _followed, _follow_activity} = CommonAPI.follow(user, other_user)
|
||||||
{:ok, _follower, _followed, _follow_activity} = CommonAPI.follow(follower, user)
|
|
||||||
|
|
||||||
# follow_pending event sent to both follower and following
|
|
||||||
assert_receive {:text, event}
|
assert_receive {:text, event}
|
||||||
assert_receive {:text, ^event}
|
|
||||||
|
|
||||||
assert %{"event" => "pleroma:relationships_update", "payload" => payload} =
|
assert %{"event" => "pleroma:follow_relationships_update", "payload" => payload} =
|
||||||
Jason.decode!(event)
|
Jason.decode!(event)
|
||||||
|
|
||||||
assert %{
|
assert %{
|
||||||
"follower" => %{
|
"follower" => %{
|
||||||
"follower_count" => 0,
|
"follower_count" => 0,
|
||||||
"following_count" => 0,
|
"following_count" => 0,
|
||||||
"id" => ^follower_id
|
"id" => ^user_id
|
||||||
},
|
},
|
||||||
"following" => %{
|
"following" => %{
|
||||||
"follower_count" => 0,
|
"follower_count" => 0,
|
||||||
"following_count" => 0,
|
"following_count" => 0,
|
||||||
"id" => ^user_id
|
"id" => ^other_user_id
|
||||||
},
|
},
|
||||||
"state" => "follow_pending"
|
"state" => "follow_pending"
|
||||||
} = Jason.decode!(payload)
|
} = Jason.decode!(payload)
|
||||||
|
|
||||||
# follow_accept event sent to both follower and following
|
|
||||||
assert_receive {:text, event}
|
assert_receive {:text, event}
|
||||||
assert_receive {:text, ^event}
|
|
||||||
|
|
||||||
assert %{"event" => "pleroma:relationships_update", "payload" => payload} =
|
assert %{"event" => "pleroma:follow_relationships_update", "payload" => payload} =
|
||||||
Jason.decode!(event)
|
Jason.decode!(event)
|
||||||
|
|
||||||
assert %{
|
assert %{
|
||||||
"follower" => %{
|
"follower" => %{
|
||||||
"follower_count" => 0,
|
"follower_count" => 0,
|
||||||
"following_count" => 1,
|
"following_count" => 1,
|
||||||
"id" => ^follower_id
|
"id" => ^user_id
|
||||||
},
|
},
|
||||||
"following" => %{
|
"following" => %{
|
||||||
"follower_count" => 1,
|
"follower_count" => 1,
|
||||||
"following_count" => 0,
|
"following_count" => 0,
|
||||||
"id" => ^user_id
|
"id" => ^other_user_id
|
||||||
},
|
},
|
||||||
"state" => "follow_accept"
|
"state" => "follow_accept"
|
||||||
} = Jason.decode!(payload)
|
} = Jason.decode!(payload)
|
||||||
|
|
Loading…
Reference in a new issue