Toot
This commit is contained in:
parent
6e691640de
commit
5721c5fe05
@ -65,3 +65,5 @@ config :arc,
|
|||||||
|
|
||||||
config :email_checker,
|
config :email_checker,
|
||||||
validations: [EmailChecker.Check.Format]
|
validations: [EmailChecker.Check.Format]
|
||||||
|
|
||||||
|
config :phoenix, :format_encoders, json: Jason
|
||||||
|
34
lib/mix/tasks/toot.ex
Normal file
34
lib/mix/tasks/toot.ex
Normal file
@ -0,0 +1,34 @@
|
|||||||
|
defmodule Mix.Tasks.Toot do
|
||||||
|
@moduledoc """
|
||||||
|
Creates a bot from a source
|
||||||
|
"""
|
||||||
|
|
||||||
|
use Mix.Task
|
||||||
|
alias Mobilizon.Actors
|
||||||
|
alias Mobilizon.Actors.Actor
|
||||||
|
alias Mobilizon.Repo
|
||||||
|
alias Mobilizon.Events
|
||||||
|
alias Mobilizon.Events.Comment
|
||||||
|
alias Mobilizon.Service.ActivityPub
|
||||||
|
alias Mobilizon.Service.ActivityPub.Utils
|
||||||
|
require Logger
|
||||||
|
|
||||||
|
@shortdoc "Toot to an user"
|
||||||
|
def run([from, to, content]) do
|
||||||
|
Mix.Task.run("app.start")
|
||||||
|
|
||||||
|
with %Actor{} = from <- Actors.get_actor_by_name(from),
|
||||||
|
{:ok, %Actor{} = to} <- ActivityPub.find_or_make_actor_from_nickname(to) do
|
||||||
|
comment = Utils.make_comment_data(from.url, [to.url], content)
|
||||||
|
|
||||||
|
ActivityPub.create(%{
|
||||||
|
to: [to.url],
|
||||||
|
actor: from,
|
||||||
|
object: comment,
|
||||||
|
local: true
|
||||||
|
})
|
||||||
|
else
|
||||||
|
e -> Logger.error(inspect(e))
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
@ -244,20 +244,29 @@ defmodule Mobilizon.Actors.Actor do
|
|||||||
)
|
)
|
||||||
end
|
end
|
||||||
|
|
||||||
def follow(%Actor{} = follower, %Actor{} = followed) do
|
def follow(%Actor{} = follower, %Actor{} = followed, approved \\ true) do
|
||||||
# Check if actor is locked
|
with {:suspended, false} <- {:suspended, followed.suspended},
|
||||||
# Check if followed has blocked follower
|
# Check if followed has blocked follower
|
||||||
# Check if follower already follows followed
|
{:already_following, false} <- {:already_following, following?(follower, followed)} do
|
||||||
cond do
|
do_follow(follower, followed, approved)
|
||||||
following?(follower, followed) ->
|
else
|
||||||
|
{:already_following, _} ->
|
||||||
{:error,
|
{:error,
|
||||||
"Could not follow actor: you are already following #{followed.preferred_username}"}
|
"Could not follow actor: you are already following #{followed.preferred_username}"}
|
||||||
|
|
||||||
# true -> nil
|
{:suspended, _} ->
|
||||||
# Follow the person
|
{:error, "Could not follow actor: #{followed.preferred_username} has been suspended"}
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
defp do_follow(%Actor{} = follower, %Actor{} = followed, approved \\ true) do
|
||||||
|
Actors.create_follower(%{
|
||||||
|
"actor_id" => follower.id,
|
||||||
|
"target_actor_id" => followed.id,
|
||||||
|
"approved" => approved
|
||||||
|
})
|
||||||
|
end
|
||||||
|
|
||||||
def following?(%Actor{} = follower, %Actor{followers: followers}) do
|
def following?(%Actor{} = follower, %Actor{followers: followers}) do
|
||||||
Enum.member?(followers, follower)
|
Enum.member?(followers, follower)
|
||||||
end
|
end
|
||||||
|
@ -203,21 +203,24 @@ defmodule Mobilizon.Actors do
|
|||||||
defp blank?(""), do: nil
|
defp blank?(""), do: nil
|
||||||
defp blank?(n), do: n
|
defp blank?(n), do: n
|
||||||
|
|
||||||
def insert_or_update_actor(data) do
|
def insert_or_update_actor(data, preload \\ false) do
|
||||||
cs = Actor.remote_actor_creation(data)
|
cs = Actor.remote_actor_creation(data)
|
||||||
|
|
||||||
Repo.insert(
|
actor =
|
||||||
cs,
|
Repo.insert(
|
||||||
on_conflict: [
|
cs,
|
||||||
set: [
|
on_conflict: [
|
||||||
keys: data.keys,
|
set: [
|
||||||
avatar_url: data.avatar_url,
|
keys: data.keys,
|
||||||
banner_url: data.banner_url,
|
avatar_url: data.avatar_url,
|
||||||
name: data.name
|
banner_url: data.banner_url,
|
||||||
]
|
name: data.name
|
||||||
],
|
]
|
||||||
conflict_target: [:preferred_username, :domain]
|
],
|
||||||
)
|
conflict_target: [:preferred_username, :domain]
|
||||||
|
)
|
||||||
|
|
||||||
|
if preload, do: {:ok, Repo.preload(actor, [:followers])}, else: {:ok, actor}
|
||||||
end
|
end
|
||||||
|
|
||||||
# def increase_event_count(%Actor{} = actor) do
|
# def increase_event_count(%Actor{} = actor) do
|
||||||
@ -267,8 +270,24 @@ defmodule Mobilizon.Actors do
|
|||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
def get_actor_by_url(url) do
|
@doc """
|
||||||
Repo.get_by(Actor, url: url)
|
Get an actor by it's URL (ActivityPub ID)
|
||||||
|
"""
|
||||||
|
@spec get_actor_by_url(String.t(), boolean()) :: {:ok, struct()} | {:error, :actor_not_found}
|
||||||
|
def get_actor_by_url(url, preload \\ false) do
|
||||||
|
case Repo.get_by(Actor, url: url) do
|
||||||
|
nil ->
|
||||||
|
{:error, :actor_not_found}
|
||||||
|
|
||||||
|
actor ->
|
||||||
|
if preload, do: {:ok, Repo.preload(actor, [:followers])}, else: {:ok, actor}
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
@spec get_actor_by_url!(String.t(), boolean()) :: struct()
|
||||||
|
def get_actor_by_url!(url, preload \\ false) do
|
||||||
|
actor = Repo.get_by!(Actor, url: url)
|
||||||
|
if preload, do: Repo.preload(actor, [:followers]), else: actor
|
||||||
end
|
end
|
||||||
|
|
||||||
def get_actor_by_name(name) do
|
def get_actor_by_name(name) do
|
||||||
@ -304,11 +323,11 @@ defmodule Mobilizon.Actors do
|
|||||||
Repo.preload(actor, :organized_events)
|
Repo.preload(actor, :organized_events)
|
||||||
end
|
end
|
||||||
|
|
||||||
def get_or_fetch_by_url(url) do
|
def get_or_fetch_by_url(url, preload \\ false) do
|
||||||
if actor = get_actor_by_url(url) do
|
if {:ok, actor} = get_actor_by_url(url, preload) do
|
||||||
{:ok, actor}
|
{:ok, actor}
|
||||||
else
|
else
|
||||||
case ActivityPub.make_actor_from_url(url) do
|
case ActivityPub.make_actor_from_url(url, preload) do
|
||||||
{:ok, actor} ->
|
{:ok, actor} ->
|
||||||
{:ok, actor}
|
{:ok, actor}
|
||||||
|
|
||||||
|
@ -26,7 +26,10 @@ defmodule Mobilizon.Events.Comment do
|
|||||||
|
|
||||||
@doc false
|
@doc false
|
||||||
def changeset(comment, attrs) do
|
def changeset(comment, attrs) do
|
||||||
uuid = Ecto.UUID.generate()
|
uuid =
|
||||||
|
if Map.has_key?(attrs, "uuid"),
|
||||||
|
do: attrs["uuid"],
|
||||||
|
else: Ecto.UUID.generate()
|
||||||
|
|
||||||
# TODO : really change me right away
|
# TODO : really change me right away
|
||||||
url =
|
url =
|
||||||
@ -35,7 +38,15 @@ defmodule Mobilizon.Events.Comment do
|
|||||||
else: "#{MobilizonWeb.Endpoint.url()}/comments/#{uuid}"
|
else: "#{MobilizonWeb.Endpoint.url()}/comments/#{uuid}"
|
||||||
|
|
||||||
comment
|
comment
|
||||||
|> cast(attrs, [:url, :text, :actor_id, :event_id, :in_reply_to_comment_id, :origin_comment_id, :attributed_to_id])
|
|> cast(attrs, [
|
||||||
|
:url,
|
||||||
|
:text,
|
||||||
|
:actor_id,
|
||||||
|
:event_id,
|
||||||
|
:in_reply_to_comment_id,
|
||||||
|
:origin_comment_id,
|
||||||
|
:attributed_to_id
|
||||||
|
])
|
||||||
|> put_change(:uuid, uuid)
|
|> put_change(:uuid, uuid)
|
||||||
|> put_change(:url, url)
|
|> put_change(:url, url)
|
||||||
|> validate_required([:text, :actor_id, :url])
|
|> validate_required([:text, :actor_id, :url])
|
||||||
|
@ -885,7 +885,15 @@ defmodule Mobilizon.Events do
|
|||||||
"""
|
"""
|
||||||
def get_comment!(id), do: Repo.get!(Comment, id)
|
def get_comment!(id), do: Repo.get!(Comment, id)
|
||||||
|
|
||||||
def get_comment_with_uuid!(uuid), do: Repo.get_by!(Comment, uuid: uuid)
|
def get_comment_from_uuid(uuid), do: Repo.get_by(Comment, uuid: uuid)
|
||||||
|
|
||||||
|
def get_comment_from_uuid!(uuid), do: Repo.get_by!(Comment, uuid: uuid)
|
||||||
|
|
||||||
|
def get_comment_full_from_uuid(uuid) do
|
||||||
|
with %Comment{} = comment <- Repo.get_by!(Comment, uuid: uuid) do
|
||||||
|
Repo.preload(comment, [:actor, :attributed_to])
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
def get_comment_from_url(url), do: Repo.get_by(Comment, url: url)
|
def get_comment_from_url(url), do: Repo.get_by(Comment, url: url)
|
||||||
|
|
||||||
|
@ -1,6 +1,7 @@
|
|||||||
defmodule MobilizonWeb.ActivityPubController do
|
defmodule MobilizonWeb.ActivityPubController do
|
||||||
use MobilizonWeb, :controller
|
use MobilizonWeb, :controller
|
||||||
alias Mobilizon.{Actors, Actors.Actor, Events, Events.Event}
|
alias Mobilizon.{Actors, Actors.Actor, Events}
|
||||||
|
alias Mobilizon.Events.{Event, Comment}
|
||||||
alias MobilizonWeb.ActivityPub.{ObjectView, ActorView}
|
alias MobilizonWeb.ActivityPub.{ObjectView, ActorView}
|
||||||
alias Mobilizon.Service.ActivityPub
|
alias Mobilizon.Service.ActivityPub
|
||||||
alias Mobilizon.Service.Federator
|
alias Mobilizon.Service.Federator
|
||||||
@ -9,16 +10,18 @@ defmodule MobilizonWeb.ActivityPubController do
|
|||||||
|
|
||||||
action_fallback(:errors)
|
action_fallback(:errors)
|
||||||
|
|
||||||
|
@activity_pub_headers [
|
||||||
|
"application/activity+json",
|
||||||
|
"application/activity+json, application/ld+json"
|
||||||
|
]
|
||||||
|
|
||||||
def actor(conn, %{"name" => name}) do
|
def actor(conn, %{"name" => name}) do
|
||||||
with %Actor{} = actor <- Actors.get_local_actor_by_name(name) do
|
with %Actor{} = actor <- Actors.get_local_actor_by_name(name) do
|
||||||
case get_req_header(conn, "accept") do
|
cond do
|
||||||
["application/activity+json"] ->
|
conn |> get_req_header("accept") |> is_ap_header() ->
|
||||||
conn |> render_ap_actor(actor)
|
conn |> render_ap_actor(actor)
|
||||||
|
|
||||||
["application/activity+json, application/ld+json"] ->
|
true ->
|
||||||
conn |> render_ap_actor(actor)
|
|
||||||
|
|
||||||
_ ->
|
|
||||||
conn
|
conn
|
||||||
|> put_resp_content_type("text/html")
|
|> put_resp_content_type("text/html")
|
||||||
|> send_file(200, "priv/static/index.html")
|
|> send_file(200, "priv/static/index.html")
|
||||||
@ -28,6 +31,10 @@ defmodule MobilizonWeb.ActivityPubController do
|
|||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
defp is_ap_header(ap_headers) do
|
||||||
|
length(@activity_pub_headers -- ap_headers) < 2
|
||||||
|
end
|
||||||
|
|
||||||
defp render_ap_actor(conn, %Actor{} = actor) do
|
defp render_ap_actor(conn, %Actor{} = actor) do
|
||||||
conn
|
conn
|
||||||
|> put_resp_header("content-type", "application/activity+json")
|
|> put_resp_header("content-type", "application/activity+json")
|
||||||
@ -41,7 +48,21 @@ defmodule MobilizonWeb.ActivityPubController do
|
|||||||
|> put_resp_header("content-type", "application/activity+json")
|
|> put_resp_header("content-type", "application/activity+json")
|
||||||
|> json(ObjectView.render("event.json", %{event: event}))
|
|> json(ObjectView.render("event.json", %{event: event}))
|
||||||
else
|
else
|
||||||
false ->
|
_ ->
|
||||||
|
{:error, :not_found}
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
def comment(conn, %{"uuid" => uuid}) do
|
||||||
|
with %Comment{} = comment <- Events.get_comment_full_from_uuid(uuid) do
|
||||||
|
# Comments are always public for now
|
||||||
|
# TODO : Make comments maybe restricted
|
||||||
|
# true <- comment.public do
|
||||||
|
conn
|
||||||
|
|> put_resp_header("content-type", "application/activity+json")
|
||||||
|
|> json(ObjectView.render("comment.json", %{comment: comment}))
|
||||||
|
else
|
||||||
|
_ ->
|
||||||
{:error, :not_found}
|
{:error, :not_found}
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
@ -14,7 +14,7 @@ defmodule MobilizonWeb.Router do
|
|||||||
end
|
end
|
||||||
|
|
||||||
pipeline :activity_pub do
|
pipeline :activity_pub do
|
||||||
plug(:accepts, ["activity-json", "text/html"])
|
plug(:accepts, ["activity-json", "html"])
|
||||||
plug(MobilizonWeb.HTTPSignaturePlug)
|
plug(MobilizonWeb.HTTPSignaturePlug)
|
||||||
end
|
end
|
||||||
|
|
||||||
@ -55,7 +55,7 @@ defmodule MobilizonWeb.Router do
|
|||||||
get("/@:name/following", ActivityPubController, :following)
|
get("/@:name/following", ActivityPubController, :following)
|
||||||
get("/@:name/followers", ActivityPubController, :followers)
|
get("/@:name/followers", ActivityPubController, :followers)
|
||||||
get("/events/:uuid", ActivityPubController, :event)
|
get("/events/:uuid", ActivityPubController, :event)
|
||||||
get("/comments/:uuid", ActivityPubController, :event)
|
get("/comments/:uuid", ActivityPubController, :comment)
|
||||||
post("/@:name/inbox", ActivityPubController, :inbox)
|
post("/@:name/inbox", ActivityPubController, :inbox)
|
||||||
post("/inbox", ActivityPubController, :inbox)
|
post("/inbox", ActivityPubController, :inbox)
|
||||||
end
|
end
|
||||||
|
@ -32,6 +32,8 @@ defmodule MobilizonWeb.ActivityPub.ActorView do
|
|||||||
"owner" => actor.url,
|
"owner" => actor.url,
|
||||||
"publicKeyPem" => public_key
|
"publicKeyPem" => public_key
|
||||||
},
|
},
|
||||||
|
# TODO : Make have actors have an uuid
|
||||||
|
# "uuid" => actor.uuid
|
||||||
"endpoints" => %{
|
"endpoints" => %{
|
||||||
"sharedInbox" => actor.shared_inbox_url
|
"sharedInbox" => actor.shared_inbox_url
|
||||||
}
|
}
|
||||||
@ -135,6 +137,7 @@ defmodule MobilizonWeb.ActivityPub.ActorView do
|
|||||||
"Announce"
|
"Announce"
|
||||||
end,
|
end,
|
||||||
"actor" => activity.actor,
|
"actor" => activity.actor,
|
||||||
|
# Not sure if needed since this is used into outbox
|
||||||
"published" => Timex.now(),
|
"published" => Timex.now(),
|
||||||
"to" => ["https://www.w3.org/ns/activitystreams#Public"],
|
"to" => ["https://www.w3.org/ns/activitystreams#Public"],
|
||||||
"object" =>
|
"object" =>
|
||||||
@ -143,9 +146,10 @@ defmodule MobilizonWeb.ActivityPub.ActorView do
|
|||||||
render_one(activity.data, ObjectView, "event.json", as: :event)
|
render_one(activity.data, ObjectView, "event.json", as: :event)
|
||||||
|
|
||||||
:Comment ->
|
:Comment ->
|
||||||
render_one(activity.data, ObjectView, "note.json", as: :note)
|
render_one(activity.data, ObjectView, "comment.json", as: :comment)
|
||||||
end
|
end
|
||||||
}
|
}
|
||||||
|
|> Map.merge(Utils.make_json_ld_header())
|
||||||
end
|
end
|
||||||
|
|
||||||
def collection(collection, iri, page, total \\ nil) do
|
def collection(collection, iri, page, total \\ nil) do
|
||||||
|
@ -2,20 +2,7 @@ defmodule MobilizonWeb.ActivityPub.ObjectView do
|
|||||||
use MobilizonWeb, :view
|
use MobilizonWeb, :view
|
||||||
alias MobilizonWeb.ActivityPub.ObjectView
|
alias MobilizonWeb.ActivityPub.ObjectView
|
||||||
alias Mobilizon.Service.ActivityPub.Transmogrifier
|
alias Mobilizon.Service.ActivityPub.Transmogrifier
|
||||||
|
alias Mobilizon.Service.ActivityPub.Utils
|
||||||
@base %{
|
|
||||||
"@context" => [
|
|
||||||
"https://www.w3.org/ns/activitystreams",
|
|
||||||
"https://w3id.org/security/v1",
|
|
||||||
%{
|
|
||||||
"manuallyApprovesFollowers" => "as:manuallyApprovesFollowers",
|
|
||||||
"sensitive" => "as:sensitive",
|
|
||||||
"Hashtag" => "as:Hashtag",
|
|
||||||
"toot" => "http://joinmastodon.org/ns#",
|
|
||||||
"Emoji" => "toot:Emoji"
|
|
||||||
}
|
|
||||||
]
|
|
||||||
}
|
|
||||||
|
|
||||||
def render("event.json", %{event: event}) do
|
def render("event.json", %{event: event}) do
|
||||||
event = %{
|
event = %{
|
||||||
@ -24,29 +11,36 @@ defmodule MobilizonWeb.ActivityPub.ObjectView do
|
|||||||
"name" => event.title,
|
"name" => event.title,
|
||||||
"category" => render_one(event.category, ObjectView, "category.json", as: :category),
|
"category" => render_one(event.category, ObjectView, "category.json", as: :category),
|
||||||
"content" => event.description,
|
"content" => event.description,
|
||||||
"mediaType" => "text/markdown",
|
"mediaType" => "text/html",
|
||||||
"published" => Timex.format!(event.inserted_at, "{ISO:Extended}"),
|
"published" => Timex.format!(event.inserted_at, "{ISO:Extended}"),
|
||||||
"updated" => Timex.format!(event.updated_at, "{ISO:Extended}")
|
"updated" => Timex.format!(event.updated_at, "{ISO:Extended}")
|
||||||
}
|
}
|
||||||
|
|
||||||
Map.merge(event, @base)
|
Map.merge(event, Utils.make_json_ld_header())
|
||||||
end
|
end
|
||||||
|
|
||||||
def render("note.json", %{note: note}) do
|
def render("comment.json", %{comment: comment}) do
|
||||||
event = %{
|
comment = %{
|
||||||
|
"actor" => comment.actor.url,
|
||||||
|
"uuid" => comment.uuid,
|
||||||
|
# The activity should have attributedTo, not the comment itself
|
||||||
|
# "attributedTo" => comment.attributed_to,
|
||||||
"type" => "Note",
|
"type" => "Note",
|
||||||
"id" => note.url,
|
"id" => comment.url,
|
||||||
"content" => note.text,
|
"content" => comment.text,
|
||||||
"mediaType" => "text/markdown",
|
"mediaType" => "text/html",
|
||||||
"published" => Timex.format!(note.inserted_at, "{ISO:Extended}"),
|
"published" => Timex.format!(comment.inserted_at, "{ISO:Extended}"),
|
||||||
"updated" => Timex.format!(note.updated_at, "{ISO:Extended}")
|
"updated" => Timex.format!(comment.updated_at, "{ISO:Extended}")
|
||||||
}
|
}
|
||||||
|
|
||||||
Map.merge(event, @base)
|
Map.merge(comment, Utils.make_json_ld_header())
|
||||||
end
|
end
|
||||||
|
|
||||||
def render("category.json", %{category: category}) do
|
def render("category.json", %{category: category}) do
|
||||||
%{"title" => category.title}
|
%{
|
||||||
|
"identifier" => category.id,
|
||||||
|
"name" => category.title
|
||||||
|
}
|
||||||
end
|
end
|
||||||
|
|
||||||
def render("category.json", %{category: nil}) do
|
def render("category.json", %{category: nil}) do
|
||||||
|
@ -16,7 +16,7 @@ defmodule Mobilizon.Service.ActivityPub do
|
|||||||
|
|
||||||
alias Mobilizon.Service.Federator
|
alias Mobilizon.Service.Federator
|
||||||
|
|
||||||
import Logger
|
require Logger
|
||||||
import Mobilizon.Service.ActivityPub.Utils
|
import Mobilizon.Service.ActivityPub.Utils
|
||||||
|
|
||||||
def get_recipients(data) do
|
def get_recipients(data) do
|
||||||
@ -24,9 +24,21 @@ defmodule Mobilizon.Service.ActivityPub do
|
|||||||
end
|
end
|
||||||
|
|
||||||
def insert(map, local \\ true) when is_map(map) do
|
def insert(map, local \\ true) when is_map(map) do
|
||||||
|
Logger.debug("preparing an activity")
|
||||||
|
Logger.debug(inspect(map))
|
||||||
|
|
||||||
with map <- lazy_put_activity_defaults(map),
|
with map <- lazy_put_activity_defaults(map),
|
||||||
:ok <- insert_full_object(map) do
|
:ok <- insert_full_object(map, local) do
|
||||||
map = Map.put(map, "id", Ecto.UUID.generate())
|
object_id =
|
||||||
|
cond do
|
||||||
|
is_map(map["object"]) ->
|
||||||
|
map["object"]["id"]
|
||||||
|
|
||||||
|
is_binary(map["object"]) ->
|
||||||
|
map["id"]
|
||||||
|
end
|
||||||
|
|
||||||
|
map = Map.put(map, "id", "#{object_id}/activity")
|
||||||
|
|
||||||
activity = %Activity{
|
activity = %Activity{
|
||||||
data: map,
|
data: map,
|
||||||
@ -106,7 +118,8 @@ defmodule Mobilizon.Service.ActivityPub do
|
|||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
def create(%{to: to, actor: actor, context: context, object: object} = params) do
|
def create(%{to: to, actor: actor, object: object} = params) do
|
||||||
|
Logger.debug("creating an activity")
|
||||||
additional = params[:additional] || %{}
|
additional = params[:additional] || %{}
|
||||||
# only accept false as false value
|
# only accept false as false value
|
||||||
local = !(params[:local] == false)
|
local = !(params[:local] == false)
|
||||||
@ -114,7 +127,7 @@ defmodule Mobilizon.Service.ActivityPub do
|
|||||||
|
|
||||||
with create_data <-
|
with create_data <-
|
||||||
make_create_data(
|
make_create_data(
|
||||||
%{to: to, actor: actor, published: published, context: context, object: object},
|
%{to: to, actor: actor, published: published, object: object},
|
||||||
additional
|
additional
|
||||||
),
|
),
|
||||||
{:ok, activity} <- insert(create_data, local),
|
{:ok, activity} <- insert(create_data, local),
|
||||||
@ -157,10 +170,14 @@ defmodule Mobilizon.Service.ActivityPub do
|
|||||||
end
|
end
|
||||||
|
|
||||||
def follow(%Actor{} = follower, %Actor{} = followed, activity_id \\ nil, local \\ true) do
|
def follow(%Actor{} = follower, %Actor{} = followed, activity_id \\ nil, local \\ true) do
|
||||||
with data <- make_follow_data(follower, followed, activity_id),
|
with {:ok, follow} <- Actor.follow(follower, followed, true),
|
||||||
|
data <- make_follow_data(follower, followed, follow.id),
|
||||||
{:ok, activity} <- insert(data, local),
|
{:ok, activity} <- insert(data, local),
|
||||||
:ok <- maybe_federate(activity) do
|
:ok <- maybe_federate(activity) do
|
||||||
{:ok, activity}
|
{:ok, activity}
|
||||||
|
else
|
||||||
|
{err, _} when err in [:already_following, :suspended] ->
|
||||||
|
{:error, err}
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
@ -199,9 +216,9 @@ defmodule Mobilizon.Service.ActivityPub do
|
|||||||
def create_public_activities(%Actor{} = actor) do
|
def create_public_activities(%Actor{} = actor) do
|
||||||
end
|
end
|
||||||
|
|
||||||
def make_actor_from_url(url) do
|
def make_actor_from_url(url, preload \\ false) do
|
||||||
with {:ok, data} <- fetch_and_prepare_user_from_url(url) do
|
with {:ok, data} <- fetch_and_prepare_actor_from_url(url) do
|
||||||
Actors.insert_or_update_actor(data)
|
Actors.insert_or_update_actor(data, preload)
|
||||||
else
|
else
|
||||||
# Request returned 410
|
# Request returned 410
|
||||||
{:error, :actor_deleted} ->
|
{:error, :actor_deleted} ->
|
||||||
@ -243,12 +260,13 @@ defmodule Mobilizon.Service.ActivityPub do
|
|||||||
end
|
end
|
||||||
|
|
||||||
remote_inboxes =
|
remote_inboxes =
|
||||||
followers
|
(remote_actors(activity) ++ followers)
|
||||||
|> Enum.map(fn follower -> follower.shared_inbox_url end)
|
|> Enum.map(fn follower -> follower.shared_inbox_url end)
|
||||||
|> Enum.uniq()
|
|> Enum.uniq()
|
||||||
|
|
||||||
{:ok, data} = Transmogrifier.prepare_outgoing(activity.data)
|
{:ok, data} = Transmogrifier.prepare_outgoing(activity.data)
|
||||||
json = Jason.encode!(data)
|
json = Jason.encode!(data)
|
||||||
|
Logger.debug("Remote inboxes are : #{inspect(remote_inboxes)}")
|
||||||
|
|
||||||
Enum.each(remote_inboxes, fn inbox ->
|
Enum.each(remote_inboxes, fn inbox ->
|
||||||
Federator.enqueue(:publish_single_ap, %{
|
Federator.enqueue(:publish_single_ap, %{
|
||||||
@ -273,6 +291,9 @@ defmodule Mobilizon.Service.ActivityPub do
|
|||||||
Logger.debug("signature")
|
Logger.debug("signature")
|
||||||
Logger.debug(inspect(signature))
|
Logger.debug(inspect(signature))
|
||||||
|
|
||||||
|
Logger.debug("body json")
|
||||||
|
Logger.debug(inspect(json))
|
||||||
|
|
||||||
{:ok, response} =
|
{:ok, response} =
|
||||||
HTTPoison.post(
|
HTTPoison.post(
|
||||||
inbox,
|
inbox,
|
||||||
@ -284,8 +305,8 @@ defmodule Mobilizon.Service.ActivityPub do
|
|||||||
Logger.debug(inspect(response))
|
Logger.debug(inspect(response))
|
||||||
end
|
end
|
||||||
|
|
||||||
def fetch_and_prepare_user_from_url(url) do
|
def fetch_and_prepare_actor_from_url(url) do
|
||||||
Logger.debug("Fetching and preparing user from url")
|
Logger.debug("Fetching and preparing actor from url")
|
||||||
|
|
||||||
with {:ok, %HTTPoison.Response{status_code: 200, body: body}} <-
|
with {:ok, %HTTPoison.Response{status_code: 200, body: body}} <-
|
||||||
HTTPoison.get(url, [Accept: "application/activity+json"], follow_redirect: true),
|
HTTPoison.get(url, [Accept: "application/activity+json"], follow_redirect: true),
|
||||||
@ -297,7 +318,7 @@ defmodule Mobilizon.Service.ActivityPub do
|
|||||||
{:error, :actor_deleted}
|
{:error, :actor_deleted}
|
||||||
|
|
||||||
e ->
|
e ->
|
||||||
Logger.error("Could not decode user at fetch #{url}, #{inspect(e)}")
|
Logger.error("Could not decode actor at fetch #{url}, #{inspect(e)}")
|
||||||
e
|
e
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
@ -18,7 +18,6 @@ defmodule Mobilizon.Service.ActivityPub.Transmogrifier do
|
|||||||
object
|
object
|
||||||
|> Map.put("actor", object["attributedTo"])
|
|> Map.put("actor", object["attributedTo"])
|
||||||
|> fix_attachments
|
|> fix_attachments
|
||||||
|> fix_context
|
|
||||||
# |> fix_in_reply_to
|
# |> fix_in_reply_to
|
||||||
|> fix_tag
|
|> fix_tag
|
||||||
end
|
end
|
||||||
@ -31,10 +30,6 @@ defmodule Mobilizon.Service.ActivityPub.Transmogrifier do
|
|||||||
#
|
#
|
||||||
# object
|
# object
|
||||||
# |> Map.put("inReplyTo", replied_object.data["id"])
|
# |> Map.put("inReplyTo", replied_object.data["id"])
|
||||||
# |> Map.put("inReplyToAtomUri", object["inReplyToAtomUri"] || in_reply_to_id)
|
|
||||||
# |> Map.put("inReplyToStatusId", activity.id)
|
|
||||||
# |> Map.put("conversation", replied_object.data["context"] || object["conversation"])
|
|
||||||
# |> Map.put("context", replied_object.data["context"] || object["conversation"])
|
|
||||||
#
|
#
|
||||||
# e ->
|
# e ->
|
||||||
# Logger.error("Couldn't fetch #{object["inReplyTo"]} #{inspect(e)}")
|
# Logger.error("Couldn't fetch #{object["inReplyTo"]} #{inspect(e)}")
|
||||||
@ -44,11 +39,6 @@ defmodule Mobilizon.Service.ActivityPub.Transmogrifier do
|
|||||||
|
|
||||||
def fix_in_reply_to(object), do: object
|
def fix_in_reply_to(object), do: object
|
||||||
|
|
||||||
def fix_context(object) do
|
|
||||||
object
|
|
||||||
|> Map.put("context", object["conversation"])
|
|
||||||
end
|
|
||||||
|
|
||||||
def fix_attachments(object) do
|
def fix_attachments(object) do
|
||||||
attachments =
|
attachments =
|
||||||
(object["attachment"] || [])
|
(object["attachment"] || [])
|
||||||
@ -87,7 +77,6 @@ defmodule Mobilizon.Service.ActivityPub.Transmogrifier do
|
|||||||
to: data["to"],
|
to: data["to"],
|
||||||
object: object,
|
object: object,
|
||||||
actor: actor,
|
actor: actor,
|
||||||
context: object["conversation"],
|
|
||||||
local: false,
|
local: false,
|
||||||
published: data["published"],
|
published: data["published"],
|
||||||
additional:
|
additional:
|
||||||
@ -104,12 +93,11 @@ defmodule Mobilizon.Service.ActivityPub.Transmogrifier do
|
|||||||
def handle_incoming(
|
def handle_incoming(
|
||||||
%{"type" => "Follow", "object" => followed, "actor" => follower, "id" => id} = data
|
%{"type" => "Follow", "object" => followed, "actor" => follower, "id" => id} = data
|
||||||
) do
|
) do
|
||||||
with {:ok, %Actor{} = followed} <- Actors.get_or_fetch_by_url(followed),
|
with {:ok, %Actor{} = followed} <- Actors.get_or_fetch_by_url(followed, true),
|
||||||
{:ok, %Actor{} = follower} <- Actors.get_or_fetch_by_url(follower),
|
{:ok, %Actor{} = follower} <- Actors.get_or_fetch_by_url(follower),
|
||||||
{:ok, activity} <- ActivityPub.follow(follower, followed, id, false) do
|
{:ok, activity} <- ActivityPub.follow(follower, followed, id, false) do
|
||||||
ActivityPub.accept(%{to: [follower.url], actor: followed.url, object: data, local: true})
|
ActivityPub.accept(%{to: [follower.url], actor: followed.url, object: data, local: true})
|
||||||
|
|
||||||
# Actors.follow(follower, followed)
|
|
||||||
{:ok, activity}
|
{:ok, activity}
|
||||||
else
|
else
|
||||||
e ->
|
e ->
|
||||||
@ -225,11 +213,10 @@ defmodule Mobilizon.Service.ActivityPub.Transmogrifier do
|
|||||||
object
|
object
|
||||||
# |> set_sensitive
|
# |> set_sensitive
|
||||||
# |> add_hashtags
|
# |> add_hashtags
|
||||||
# |> add_mention_tags
|
|> add_mention_tags
|
||||||
# |> add_emoji_tags
|
# |> add_emoji_tags
|
||||||
|> add_attributed_to
|
|> add_attributed_to
|
||||||
# |> prepare_attachments
|
# |> prepare_attachments
|
||||||
|> set_conversation
|
|
||||||
|> set_reply_to_uri
|
|> set_reply_to_uri
|
||||||
end
|
end
|
||||||
|
|
||||||
@ -239,6 +226,8 @@ defmodule Mobilizon.Service.ActivityPub.Transmogrifier do
|
|||||||
"""
|
"""
|
||||||
|
|
||||||
def prepare_outgoing(%{"type" => "Create", "object" => %{"type" => "Note"} = object} = data) do
|
def prepare_outgoing(%{"type" => "Create", "object" => %{"type" => "Note"} = object} = data) do
|
||||||
|
Logger.debug("Prepare outgoing for a note creation")
|
||||||
|
|
||||||
object =
|
object =
|
||||||
object
|
object
|
||||||
|> prepare_object
|
|> prepare_object
|
||||||
@ -248,6 +237,8 @@ defmodule Mobilizon.Service.ActivityPub.Transmogrifier do
|
|||||||
|> Map.put("object", object)
|
|> Map.put("object", object)
|
||||||
|> Map.put("@context", "https://www.w3.org/ns/activitystreams")
|
|> Map.put("@context", "https://www.w3.org/ns/activitystreams")
|
||||||
|
|
||||||
|
Logger.debug("Finished prepare outgoing for a note creation")
|
||||||
|
|
||||||
{:ok, data}
|
{:ok, data}
|
||||||
end
|
end
|
||||||
|
|
||||||
@ -304,22 +295,23 @@ defmodule Mobilizon.Service.ActivityPub.Transmogrifier do
|
|||||||
# end
|
# end
|
||||||
# end
|
# end
|
||||||
#
|
#
|
||||||
# def add_mention_tags(object) do
|
def add_mention_tags(object) do
|
||||||
# recipients = object["to"] ++ (object["cc"] || [])
|
recipients = object["to"] ++ (object["cc"] || [])
|
||||||
#
|
|
||||||
# mentions =
|
mentions =
|
||||||
# recipients
|
recipients
|
||||||
# |> Enum.map(fn ap_id -> User.get_cached_by_ap_id(ap_id) end)
|
|> Enum.map(fn url -> Actors.get_actor_by_url!(url) end)
|
||||||
# |> Enum.filter(& &1)
|
|> Enum.filter(& &1)
|
||||||
# |> Enum.map(fn user ->
|
|> Enum.map(fn actor ->
|
||||||
# %{"type" => "Mention", "href" => user.ap_id, "name" => "@#{user.nickname}"}
|
%{"type" => "Mention", "href" => actor.url, "name" => "@#{actor.preferred_username}"}
|
||||||
# end)
|
end)
|
||||||
#
|
|
||||||
# tags = object["tag"] || []
|
tags = object["tag"] || []
|
||||||
#
|
|
||||||
# object
|
object
|
||||||
# |> Map.put("tag", tags ++ mentions)
|
|> Map.put("tag", tags ++ mentions)
|
||||||
# end
|
end
|
||||||
|
|
||||||
#
|
#
|
||||||
# # TODO: we should probably send mtime instead of unix epoch time for updated
|
# # TODO: we should probably send mtime instead of unix epoch time for updated
|
||||||
# def add_emoji_tags(object) do
|
# def add_emoji_tags(object) do
|
||||||
@ -342,9 +334,6 @@ defmodule Mobilizon.Service.ActivityPub.Transmogrifier do
|
|||||||
# |> Map.put("tag", tags ++ out)
|
# |> Map.put("tag", tags ++ out)
|
||||||
# end
|
# end
|
||||||
#
|
#
|
||||||
def set_conversation(object) do
|
|
||||||
Map.put(object, "conversation", object["context"])
|
|
||||||
end
|
|
||||||
|
|
||||||
#
|
#
|
||||||
# def set_sensitive(object) do
|
# def set_sensitive(object) do
|
||||||
@ -370,84 +359,4 @@ defmodule Mobilizon.Service.ActivityPub.Transmogrifier do
|
|||||||
# object
|
# object
|
||||||
# |> Map.put("attachment", attachments)
|
# |> Map.put("attachment", attachments)
|
||||||
# end
|
# end
|
||||||
#
|
|
||||||
# defp user_upgrade_task(user) do
|
|
||||||
# old_follower_address = User.ap_followers(user)
|
|
||||||
#
|
|
||||||
# q =
|
|
||||||
# from(
|
|
||||||
# u in User,
|
|
||||||
# where: ^old_follower_address in u.following,
|
|
||||||
# update: [
|
|
||||||
# set: [
|
|
||||||
# following:
|
|
||||||
# fragment(
|
|
||||||
# "array_replace(?,?,?)",
|
|
||||||
# u.following,
|
|
||||||
# ^old_follower_address,
|
|
||||||
# ^user.follower_address
|
|
||||||
# )
|
|
||||||
# ]
|
|
||||||
# ]
|
|
||||||
# )
|
|
||||||
#
|
|
||||||
# Repo.update_all(q, [])
|
|
||||||
#
|
|
||||||
# maybe_retire_websub(user.ap_id)
|
|
||||||
#
|
|
||||||
# # Only do this for recent activties, don't go through the whole db.
|
|
||||||
# # Only look at the last 1000 activities.
|
|
||||||
# since = (Repo.aggregate(Activity, :max, :id) || 0) - 1_000
|
|
||||||
#
|
|
||||||
# q =
|
|
||||||
# from(
|
|
||||||
# a in Activity,
|
|
||||||
# where: ^old_follower_address in a.recipients,
|
|
||||||
# where: a.id > ^since,
|
|
||||||
# update: [
|
|
||||||
# set: [
|
|
||||||
# recipients:
|
|
||||||
# fragment(
|
|
||||||
# "array_replace(?,?,?)",
|
|
||||||
# a.recipients,
|
|
||||||
# ^old_follower_address,
|
|
||||||
# ^user.follower_address
|
|
||||||
# )
|
|
||||||
# ]
|
|
||||||
# ]
|
|
||||||
# )
|
|
||||||
#
|
|
||||||
# Repo.update_all(q, [])
|
|
||||||
# end
|
|
||||||
#
|
|
||||||
# def upgrade_user_from_ap_id(ap_id, async \\ true) do
|
|
||||||
# with %User{local: false} = user <- User.get_by_ap_id(ap_id),
|
|
||||||
# {:ok, data} <- ActivityPub.fetch_and_prepare_user_from_ap_id(ap_id) do
|
|
||||||
# data =
|
|
||||||
# data
|
|
||||||
# |> Map.put(:info, Map.merge(user.info, data[:info]))
|
|
||||||
#
|
|
||||||
# already_ap = User.ap_enabled?(user)
|
|
||||||
#
|
|
||||||
# {:ok, user} =
|
|
||||||
# User.upgrade_changeset(user, data)
|
|
||||||
# |> Repo.update()
|
|
||||||
#
|
|
||||||
# if !already_ap do
|
|
||||||
# # This could potentially take a long time, do it in the background
|
|
||||||
# if async do
|
|
||||||
# Task.start(fn ->
|
|
||||||
# user_upgrade_task(user)
|
|
||||||
# end)
|
|
||||||
# else
|
|
||||||
# user_upgrade_task(user)
|
|
||||||
# end
|
|
||||||
# end
|
|
||||||
#
|
|
||||||
# {:ok, user}
|
|
||||||
# else
|
|
||||||
# e -> e
|
|
||||||
# end
|
|
||||||
# end
|
|
||||||
#
|
|
||||||
end
|
end
|
||||||
|
@ -20,16 +20,19 @@ defmodule Mobilizon.Service.ActivityPub.Utils do
|
|||||||
import Ecto.Query
|
import Ecto.Query
|
||||||
require Logger
|
require Logger
|
||||||
|
|
||||||
|
def make_context(%Activity{data: %{"context" => context}}), do: context
|
||||||
|
def make_context(_), do: generate_context_id()
|
||||||
|
|
||||||
def make_json_ld_header do
|
def make_json_ld_header do
|
||||||
%{
|
%{
|
||||||
"@context" => [
|
"@context" => [
|
||||||
"https://www.w3.org/ns/activitystreams",
|
"https://www.w3.org/ns/activitystreams",
|
||||||
|
"https://litepub.github.io/litepub/context.jsonld",
|
||||||
%{
|
%{
|
||||||
"manuallyApprovesFollowers" => "as:manuallyApprovesFollowers",
|
"sc" => "http://schema.org#",
|
||||||
"sensitive" => "as:sensitive",
|
|
||||||
"Hashtag" => "as:Hashtag",
|
"Hashtag" => "as:Hashtag",
|
||||||
"toot" => "http://joinmastodon.org/ns#",
|
"category" => "sc:category",
|
||||||
"Emoji" => "toot:Emoji"
|
"uuid" => "sc:identifier"
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
@ -74,6 +77,8 @@ defmodule Mobilizon.Service.ActivityPub.Utils do
|
|||||||
Enqueues an activity for federation if it's local
|
Enqueues an activity for federation if it's local
|
||||||
"""
|
"""
|
||||||
def maybe_federate(%Activity{local: true} = activity) do
|
def maybe_federate(%Activity{local: true} = activity) do
|
||||||
|
Logger.debug("Maybe federate an activity")
|
||||||
|
|
||||||
priority =
|
priority =
|
||||||
case activity.data["type"] do
|
case activity.data["type"] do
|
||||||
"Delete" -> 10
|
"Delete" -> 10
|
||||||
@ -87,6 +92,14 @@ defmodule Mobilizon.Service.ActivityPub.Utils do
|
|||||||
|
|
||||||
def maybe_federate(_), do: :ok
|
def maybe_federate(_), do: :ok
|
||||||
|
|
||||||
|
def remote_actors(%{data: %{"to" => to} = data}) do
|
||||||
|
to = to ++ (data["cc"] || [])
|
||||||
|
|
||||||
|
to
|
||||||
|
|> Enum.map(fn url -> Actors.get_actor_by_url!(url) end)
|
||||||
|
|> Enum.filter(fn actor -> actor && !is_nil(actor.domain) end)
|
||||||
|
end
|
||||||
|
|
||||||
@doc """
|
@doc """
|
||||||
Adds an id and a published data if they aren't there,
|
Adds an id and a published data if they aren't there,
|
||||||
also adds it to an included object
|
also adds it to an included object
|
||||||
@ -123,7 +136,12 @@ defmodule Mobilizon.Service.ActivityPub.Utils do
|
|||||||
@doc """
|
@doc """
|
||||||
Inserts a full object if it is contained in an activity.
|
Inserts a full object if it is contained in an activity.
|
||||||
"""
|
"""
|
||||||
def insert_full_object(%{"object" => %{"type" => type} = object_data})
|
def insert_full_object(object_data, local \\ false)
|
||||||
|
|
||||||
|
@doc """
|
||||||
|
Inserts a full object if it is contained in an activity.
|
||||||
|
"""
|
||||||
|
def insert_full_object(%{"object" => %{"type" => type} = object_data}, _local)
|
||||||
when is_map(object_data) and type == "Event" do
|
when is_map(object_data) and type == "Event" do
|
||||||
with {:ok, _} <- Events.create_event(object_data) do
|
with {:ok, _} <- Events.create_event(object_data) do
|
||||||
:ok
|
:ok
|
||||||
@ -133,7 +151,7 @@ defmodule Mobilizon.Service.ActivityPub.Utils do
|
|||||||
@doc """
|
@doc """
|
||||||
Inserts a full object if it is contained in an activity.
|
Inserts a full object if it is contained in an activity.
|
||||||
"""
|
"""
|
||||||
def insert_full_object(%{"object" => %{"type" => type} = object_data})
|
def insert_full_object(%{"object" => %{"type" => type} = object_data}, local)
|
||||||
when is_map(object_data) and type == "Note" do
|
when is_map(object_data) and type == "Note" do
|
||||||
with {:ok, %Actor{id: actor_id}} <- Actors.get_or_fetch_by_url(object_data["actor"]) do
|
with {:ok, %Actor{id: actor_id}} <- Actors.get_or_fetch_by_url(object_data["actor"]) do
|
||||||
data = %{
|
data = %{
|
||||||
@ -142,8 +160,9 @@ defmodule Mobilizon.Service.ActivityPub.Utils do
|
|||||||
"actor_id" => actor_id,
|
"actor_id" => actor_id,
|
||||||
"in_reply_to_comment_id" => nil,
|
"in_reply_to_comment_id" => nil,
|
||||||
"event_id" => nil,
|
"event_id" => nil,
|
||||||
|
"uuid" => object_data["uuid"],
|
||||||
# probably
|
# probably
|
||||||
"local" => false
|
"local" => local
|
||||||
}
|
}
|
||||||
|
|
||||||
# We fetch the parent object
|
# We fetch the parent object
|
||||||
@ -193,7 +212,7 @@ defmodule Mobilizon.Service.ActivityPub.Utils do
|
|||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
def insert_full_object(_), do: :ok
|
def insert_full_object(_, _), do: :ok
|
||||||
|
|
||||||
# def update_object_in_activities(%{data: %{"id" => id}} = object) do
|
# def update_object_in_activities(%{data: %{"id" => id}} = object) do
|
||||||
# # TODO
|
# # TODO
|
||||||
@ -236,30 +255,31 @@ defmodule Mobilizon.Service.ActivityPub.Utils do
|
|||||||
def make_comment_data(
|
def make_comment_data(
|
||||||
actor,
|
actor,
|
||||||
to,
|
to,
|
||||||
context,
|
|
||||||
content_html,
|
content_html,
|
||||||
attachments,
|
# attachments,
|
||||||
inReplyTo,
|
inReplyTo \\ nil,
|
||||||
tags,
|
# tags,
|
||||||
cw \\ nil,
|
cw \\ nil,
|
||||||
cc \\ []
|
cc \\ []
|
||||||
) do
|
) do
|
||||||
|
Logger.debug("Making comment data")
|
||||||
|
uuid = Ecto.UUID.generate()
|
||||||
|
|
||||||
object = %{
|
object = %{
|
||||||
"type" => "Note",
|
"type" => "Note",
|
||||||
"to" => to,
|
"to" => to,
|
||||||
"cc" => cc,
|
# "cc" => cc,
|
||||||
"content" => content_html,
|
"content" => content_html,
|
||||||
"summary" => cw,
|
# "summary" => cw,
|
||||||
"context" => context,
|
# "attachment" => attachments,
|
||||||
"attachment" => attachments,
|
|
||||||
"actor" => actor,
|
"actor" => actor,
|
||||||
"tag" => tags |> Enum.map(fn {_, tag} -> tag end) |> Enum.uniq()
|
"id" => "#{MobilizonWeb.Endpoint.url()}/comments/#{uuid}"
|
||||||
|
# "tag" => tags |> Enum.map(fn {_, tag} -> tag end) |> Enum.uniq()
|
||||||
}
|
}
|
||||||
|
|
||||||
if inReplyTo do
|
if inReplyTo do
|
||||||
object
|
object
|
||||||
|> Map.put("inReplyTo", inReplyTo.data["object"]["id"])
|
|> Map.put("inReplyTo", inReplyTo)
|
||||||
|> Map.put("inReplyToStatusId", inReplyTo.id)
|
|
||||||
else
|
else
|
||||||
object
|
object
|
||||||
end
|
end
|
||||||
@ -311,6 +331,8 @@ defmodule Mobilizon.Service.ActivityPub.Utils do
|
|||||||
Makes a follow activity data for the given follower and followed
|
Makes a follow activity data for the given follower and followed
|
||||||
"""
|
"""
|
||||||
def make_follow_data(%Actor{url: follower_id}, %Actor{url: followed_id}, activity_id) do
|
def make_follow_data(%Actor{url: follower_id}, %Actor{url: followed_id}, activity_id) do
|
||||||
|
Logger.debug("Make follow data")
|
||||||
|
|
||||||
data = %{
|
data = %{
|
||||||
"type" => "Follow",
|
"type" => "Follow",
|
||||||
"actor" => follower_id,
|
"actor" => follower_id,
|
||||||
@ -319,7 +341,11 @@ defmodule Mobilizon.Service.ActivityPub.Utils do
|
|||||||
"object" => followed_id
|
"object" => followed_id
|
||||||
}
|
}
|
||||||
|
|
||||||
if activity_id, do: Map.put(data, "id", activity_id), else: data
|
Logger.debug(inspect(data))
|
||||||
|
|
||||||
|
if activity_id,
|
||||||
|
do: Map.put(data, "id", "#{MobilizonWeb.Endpoint.url()}/follow/#{activity_id}/activity"),
|
||||||
|
else: data
|
||||||
end
|
end
|
||||||
|
|
||||||
# def fetch_latest_follow(%Actor{url: follower_id}, %Actor{url: followed_id}) do
|
# def fetch_latest_follow(%Actor{url: follower_id}, %Actor{url: followed_id}) do
|
||||||
@ -388,8 +414,7 @@ defmodule Mobilizon.Service.ActivityPub.Utils do
|
|||||||
"to" => params.to |> Enum.uniq(),
|
"to" => params.to |> Enum.uniq(),
|
||||||
"actor" => params.actor.url,
|
"actor" => params.actor.url,
|
||||||
"object" => params.object,
|
"object" => params.object,
|
||||||
"published" => published,
|
"published" => published
|
||||||
"context" => params.context
|
|
||||||
}
|
}
|
||||||
|> Map.merge(additional)
|
|> Map.merge(additional)
|
||||||
end
|
end
|
||||||
|
@ -36,7 +36,7 @@ defmodule Mobilizon.Service.Federator do
|
|||||||
Logger.debug(inspect(activity))
|
Logger.debug(inspect(activity))
|
||||||
Logger.debug(fn -> "Running publish for #{activity.data["id"]}" end)
|
Logger.debug(fn -> "Running publish for #{activity.data["id"]}" end)
|
||||||
|
|
||||||
with actor when not is_nil(actor) <- Actors.get_actor_by_url(activity.data["actor"]) do
|
with actor when not is_nil(actor) <- Actors.get_actor_by_url!(activity.data["actor"]) do
|
||||||
Logger.info(fn -> "Sending #{activity.data["id"]} out via AP" end)
|
Logger.info(fn -> "Sending #{activity.data["id"]} out via AP" end)
|
||||||
ActivityPub.publish(actor, activity)
|
ActivityPub.publish(actor, activity)
|
||||||
end
|
end
|
||||||
|
@ -90,17 +90,21 @@ defmodule Mobilizon.Service.HTTPSignatures do
|
|||||||
end
|
end
|
||||||
|
|
||||||
def sign(%Actor{} = actor, headers) do
|
def sign(%Actor{} = actor, headers) do
|
||||||
sigstring = build_signing_string(headers, Map.keys(headers))
|
with sigstring <- build_signing_string(headers, Map.keys(headers)),
|
||||||
|
{:ok, key} <- actor.keys |> prepare_public_key(),
|
||||||
signature = sigstring |> :public_key.sign(:sha256, actor.keys) |> Base.encode64()
|
signature <- sigstring |> :public_key.sign(:sha256, key) |> Base.encode64() do
|
||||||
|
[
|
||||||
[
|
keyId: actor.url <> "#main-key",
|
||||||
keyId: actor.url <> "#main-key",
|
algorithm: "rsa-sha256",
|
||||||
algorithm: "rsa-sha256",
|
headers: headers |> Map.keys() |> Enum.join(" "),
|
||||||
headers: headers |> Map.keys() |> Enum.join(" "),
|
signature: signature
|
||||||
signature: signature
|
]
|
||||||
]
|
|> Enum.map(fn {k, v} -> "#{k}=\"#{v}\"" end)
|
||||||
|> Enum.map(fn {k, v} -> "#{k}=\"#{v}\"" end)
|
|> Enum.join(",")
|
||||||
|> Enum.join(",")
|
else
|
||||||
|
err ->
|
||||||
|
Logger.error("Unable to sign headers")
|
||||||
|
Logger.error(inspect(err))
|
||||||
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
@ -38,7 +38,7 @@ defmodule Mobilizon.Service.WebFinger do
|
|||||||
{:ok, represent_user(user, "JSON")}
|
{:ok, represent_user(user, "JSON")}
|
||||||
else
|
else
|
||||||
_e ->
|
_e ->
|
||||||
with user when not is_nil(user) <- Actors.get_actor_by_url(resource) do
|
with user when not is_nil(user) <- Actors.get_actor_by_url!(resource) do
|
||||||
{:ok, represent_user(user, "JSON")}
|
{:ok, represent_user(user, "JSON")}
|
||||||
else
|
else
|
||||||
_e ->
|
_e ->
|
||||||
|
Loading…
Reference in New Issue
Block a user