+
+ def local?(%Object{data: %{"id" => id}}) do
+ String.starts_with?(id, Pleroma.Web.Endpoint.url() <> "/")
+ end
+
+ def replies(object, opts \\ []) do
+ object = Object.normalize(object, fetch: false)
+
+ query =
+ Object
+ |> where(
+ [o],
+ fragment("(?)->>'inReplyTo' = ?", o.data, ^object.data["id"])
+ )
+ |> order_by([o], asc: o.id)
+
+ if opts[:self_only] do
+ actor = object.data["actor"]
+ where(query, [o], fragment("(?)->>'actor' = ?", o.data, ^actor))
+ else
+ query
+ end
+ end
+
+ def self_replies(object, opts \\ []),
+ do: replies(object, Keyword.put(opts, :self_only, true))
+
+ def tags(%Object{data: %{"tag" => tags}}) when is_list(tags), do: tags
+
+ def tags(_), do: []
+
+ def hashtags(%Object{} = object) do
+ # Note: always using embedded hashtags regardless whether they are migrated to hashtags table
+ # (embedded hashtags stay in sync anyways, and we avoid extra joins and preload hassle)
+ embedded_hashtags(object)
+ end
+
+ def embedded_hashtags(%Object{data: data}) do
+ object_data_hashtags(data)
+ end
+
+ def embedded_hashtags(_), do: []
+
+ def object_data_hashtags(%{"tag" => tags}) when is_list(tags) do
+ tags
+ |> Enum.filter(fn
+ %{"type" => "Hashtag"} = data -> Map.has_key?(data, "name")
+ plain_text when is_bitstring(plain_text) -> true
+ _ -> false
+ end)
+ |> Enum.map(fn
+ %{"name" => "#" <> hashtag} -> String.downcase(hashtag)
+ %{"name" => hashtag} -> String.downcase(hashtag)
+ hashtag when is_bitstring(hashtag) -> String.downcase(hashtag)
+ end)
+ |> Enum.uniq()
+ # Note: "" elements (plain text) might occur in `data.tag` for incoming objects
+ |> Enum.filter(&(&1 not in [nil, ""]))
+ end
+
+ def object_data_hashtags(_), do: []