1
0
Fork 0
mirror of https://framagit.org/framasoft/mobilizon.git synced 2024-12-23 16:33:28 +00:00
mobilizon/lib/federation/activity_stream/converter/post.ex
Thomas Citharel de047c8939
Various typespec and compilation improvements
Signed-off-by: Thomas Citharel <tcit@tcit.fr>
2021-09-26 17:52:20 +02:00

149 lines
4.2 KiB
Elixir

defmodule Mobilizon.Federation.ActivityStream.Converter.Post do
@moduledoc """
Post converter.
This module allows to convert posts from ActivityStream format to our own
internal one, and back.
"""
alias Mobilizon.Actors.Actor
alias Mobilizon.Federation.ActivityPub.Actor, as: ActivityPubActor
alias Mobilizon.Federation.ActivityPub.{Audience, Utils}
alias Mobilizon.Federation.ActivityStream.{Converter, Convertible}
alias Mobilizon.Federation.ActivityStream.Converter.Media, as: MediaConverter
alias Mobilizon.Posts.Post
require Logger
import Mobilizon.Federation.ActivityStream.Converter.Utils,
only: [
process_pictures: 2
]
import Mobilizon.Service.Guards, only: [is_valid_string: 1]
@behaviour Converter
defimpl Convertible, for: Post do
alias Mobilizon.Federation.ActivityStream.Converter.Post, as: PostConverter
defdelegate model_to_as(post), to: PostConverter
end
@banner_picture_name "Banner"
@doc """
Convert an post struct to an ActivityStream representation
"""
@impl Converter
@spec model_to_as(Post.t()) :: map
def model_to_as(
%Post{
author: %Actor{url: actor_url},
attributed_to: %Actor{
url: creator_url
}
} = post
) do
audience = Audience.get_audience(post)
%{
"type" => "Article",
"actor" => actor_url,
"id" => post.url,
"name" => post.title,
"content" => post.body,
"attributedTo" => creator_url,
"published" => (post.publish_at || post.inserted_at) |> to_date(),
"attachment" => [],
"draft" => post.draft
}
|> Map.merge(audience)
|> maybe_add_post_picture(post)
|> maybe_add_inline_media(post)
end
@doc """
Converts an AP object data to our internal data structure.
"""
@impl Converter
@spec as_to_model_data(map) :: map() | {:error, any()}
def as_to_model_data(
%{"type" => "Article", "actor" => creator, "attributedTo" => group_uri} = object
) do
with {:ok, %Actor{id: attributed_to_id} = group} <- get_actor(group_uri),
{:ok, %Actor{id: author_id}} <- get_actor(creator) do
[description: description, picture_id: picture_id, medias: medias] =
process_pictures(object, attributed_to_id)
%{
title: object["name"],
body: description,
url: object["id"],
attributed_to_id: attributed_to_id,
author_id: author_id,
local: false,
publish_at: object["published"],
picture_id: picture_id,
medias: medias,
visibility: get_visibility(object, group),
draft: object["draft"] == true
}
else
{:error, err} -> {:error, err}
err -> {:error, err}
end
end
@spec get_actor(String.t() | map() | nil) :: {:ok, Actor.t()} | {:error, String.t()}
defp get_actor(actor) when is_valid_string(actor),
do: actor |> Utils.get_url() |> ActivityPubActor.get_or_fetch_actor_by_url()
defp get_actor(_), do: {:error, "nil property found for actor data"}
@spec to_date(DateTime.t() | NaiveDateTime.t() | nil) :: String.t() | nil
defp to_date(nil), do: nil
defp to_date(%DateTime{} = date), do: DateTime.to_iso8601(date)
defp to_date(%NaiveDateTime{} = date), do: NaiveDateTime.to_iso8601(date)
@spec maybe_add_post_picture(map(), Post.t()) :: map()
defp maybe_add_post_picture(res, post) do
if is_nil(post.picture),
do: res,
else:
Map.update(
res,
"attachment",
[],
&(&1 ++
[
post.picture
|> MediaConverter.model_to_as()
|> Map.put("name", @banner_picture_name)
])
)
end
@spec maybe_add_inline_media(map(), Post.t()) :: map()
defp maybe_add_inline_media(res, post) do
medias = Enum.map(post.media, &MediaConverter.model_to_as/1)
Map.update(
res,
"attachment",
[],
&(&1 ++ medias)
)
end
@ap_public "https://www.w3.org/ns/activitystreams#Public"
defp get_visibility(%{"to" => to}, %Actor{
followers_url: followers_url,
members_url: members_url
}) do
cond do
@ap_public in to -> :public
followers_url in to -> :unlisted
members_url in to -> :private
end
end
end