remove Salmon module
This commit is contained in:
parent
beb9861f9d
commit
835ad52378
@ -39,9 +39,6 @@ defmodule Pleroma.User.Info do
|
|||||||
field(:settings, :map, default: nil)
|
field(:settings, :map, default: nil)
|
||||||
field(:magic_key, :string, default: nil)
|
field(:magic_key, :string, default: nil)
|
||||||
field(:uri, :string, default: nil)
|
field(:uri, :string, default: nil)
|
||||||
field(:topic, :string, default: nil)
|
|
||||||
field(:hub, :string, default: nil)
|
|
||||||
field(:salmon, :string, default: nil)
|
|
||||||
field(:hide_followers_count, :boolean, default: false)
|
field(:hide_followers_count, :boolean, default: false)
|
||||||
field(:hide_follows_count, :boolean, default: false)
|
field(:hide_follows_count, :boolean, default: false)
|
||||||
field(:hide_followers, :boolean, default: false)
|
field(:hide_followers, :boolean, default: false)
|
||||||
@ -262,9 +259,6 @@ def remote_user_creation(info, params) do
|
|||||||
:locked,
|
:locked,
|
||||||
:magic_key,
|
:magic_key,
|
||||||
:uri,
|
:uri,
|
||||||
:hub,
|
|
||||||
:topic,
|
|
||||||
:salmon,
|
|
||||||
:hide_followers,
|
:hide_followers,
|
||||||
:hide_follows,
|
:hide_follows,
|
||||||
:hide_followers_count,
|
:hide_followers_count,
|
||||||
|
@ -40,8 +40,6 @@ def to_simple_form(user, activities, _users) do
|
|||||||
{:title, ['#{user.nickname}\'s timeline']},
|
{:title, ['#{user.nickname}\'s timeline']},
|
||||||
{:updated, h.(most_recent_update)},
|
{:updated, h.(most_recent_update)},
|
||||||
{:logo, [to_charlist(User.avatar_url(user) |> MediaProxy.url())]},
|
{:logo, [to_charlist(User.avatar_url(user) |> MediaProxy.url())]},
|
||||||
{:link, [rel: 'hub', href: h.(OStatus.pubsub_path(user))], []},
|
|
||||||
{:link, [rel: 'salmon', href: h.(OStatus.salmon_path(user))], []},
|
|
||||||
{:link, [rel: 'self', href: h.(OStatus.feed_path(user)), type: 'application/atom+xml'],
|
{:link, [rel: 'self', href: h.(OStatus.feed_path(user)), type: 'application/atom+xml'],
|
||||||
[]},
|
[]},
|
||||||
{:author, UserRepresenter.to_simple_form(user)}
|
{:author, UserRepresenter.to_simple_form(user)}
|
||||||
|
@ -37,10 +37,6 @@ def is_representable?(%Activity{} = activity) do
|
|||||||
|
|
||||||
def feed_path(user), do: "#{user.ap_id}/feed.atom"
|
def feed_path(user), do: "#{user.ap_id}/feed.atom"
|
||||||
|
|
||||||
def pubsub_path(user), do: "#{Web.base_url()}/push/hub/#{user.nickname}"
|
|
||||||
|
|
||||||
def salmon_path(user), do: "#{user.ap_id}/salmon"
|
|
||||||
|
|
||||||
def remote_follow_path, do: "#{Web.base_url()}/ostatus_subscribe?acct={uri}"
|
def remote_follow_path, do: "#{Web.base_url()}/ostatus_subscribe?acct={uri}"
|
||||||
|
|
||||||
def handle_incoming(xml_string, options \\ []) do
|
def handle_incoming(xml_string, options \\ []) do
|
||||||
|
@ -24,8 +24,6 @@ defmodule Pleroma.Web.OStatus.OStatusController do
|
|||||||
{:ap_routes, params: ["uuid"]} when action in [:object, :activity]
|
{:ap_routes, params: ["uuid"]} when action in [:object, :activity]
|
||||||
)
|
)
|
||||||
|
|
||||||
plug(Pleroma.Web.FederatingPlug when action in [:salmon_incoming])
|
|
||||||
|
|
||||||
plug(
|
plug(
|
||||||
Pleroma.Plugs.SetFormatPlug
|
Pleroma.Plugs.SetFormatPlug
|
||||||
when action in [:object, :activity, :notice]
|
when action in [:object, :activity, :notice]
|
||||||
@ -33,32 +31,6 @@ defmodule Pleroma.Web.OStatus.OStatusController do
|
|||||||
|
|
||||||
action_fallback(:errors)
|
action_fallback(:errors)
|
||||||
|
|
||||||
defp decode_or_retry(body) do
|
|
||||||
with {:ok, magic_key} <- Pleroma.Web.Salmon.fetch_magic_key(body),
|
|
||||||
{:ok, doc} <- Pleroma.Web.Salmon.decode_and_validate(magic_key, body) do
|
|
||||||
{:ok, doc}
|
|
||||||
else
|
|
||||||
_e ->
|
|
||||||
with [decoded | _] <- Pleroma.Web.Salmon.decode(body),
|
|
||||||
doc <- XML.parse_document(decoded),
|
|
||||||
uri when not is_nil(uri) <- XML.string_from_xpath("/entry/author[1]/uri", doc),
|
|
||||||
{:ok, _} <- Pleroma.Web.OStatus.make_user(uri, true),
|
|
||||||
{:ok, magic_key} <- Pleroma.Web.Salmon.fetch_magic_key(body),
|
|
||||||
{:ok, doc} <- Pleroma.Web.Salmon.decode_and_validate(magic_key, body) do
|
|
||||||
{:ok, doc}
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
def salmon_incoming(conn, _) do
|
|
||||||
{:ok, body, _conn} = read_body(conn)
|
|
||||||
{:ok, doc} = decode_or_retry(body)
|
|
||||||
|
|
||||||
Federator.incoming_doc(doc)
|
|
||||||
|
|
||||||
send_resp(conn, 200, "")
|
|
||||||
end
|
|
||||||
|
|
||||||
def object(%{assigns: %{format: format}} = conn, %{"uuid" => _uuid})
|
def object(%{assigns: %{format: format}} = conn, %{"uuid" => _uuid})
|
||||||
when format in ["json", "activity+json"] do
|
when format in ["json", "activity+json"] do
|
||||||
ActivityPubController.call(conn, :object)
|
ActivityPubController.call(conn, :object)
|
||||||
|
@ -1,254 +0,0 @@
|
|||||||
# Pleroma: A lightweight social networking server
|
|
||||||
# Copyright © 2017-2019 Pleroma Authors <https://pleroma.social/>
|
|
||||||
# SPDX-License-Identifier: AGPL-3.0-only
|
|
||||||
|
|
||||||
defmodule Pleroma.Web.Salmon do
|
|
||||||
@behaviour Pleroma.Web.Federator.Publisher
|
|
||||||
|
|
||||||
use Bitwise
|
|
||||||
|
|
||||||
alias Pleroma.Activity
|
|
||||||
alias Pleroma.HTTP
|
|
||||||
alias Pleroma.Instances
|
|
||||||
alias Pleroma.Keys
|
|
||||||
alias Pleroma.User
|
|
||||||
alias Pleroma.Web.ActivityPub.Visibility
|
|
||||||
alias Pleroma.Web.Federator.Publisher
|
|
||||||
alias Pleroma.Web.OStatus
|
|
||||||
alias Pleroma.Web.OStatus.ActivityRepresenter
|
|
||||||
alias Pleroma.Web.XML
|
|
||||||
|
|
||||||
require Logger
|
|
||||||
|
|
||||||
def decode(salmon) do
|
|
||||||
doc = XML.parse_document(salmon)
|
|
||||||
|
|
||||||
{:xmlObj, :string, data} = :xmerl_xpath.string('string(//me:data[1])', doc)
|
|
||||||
{:xmlObj, :string, sig} = :xmerl_xpath.string('string(//me:sig[1])', doc)
|
|
||||||
{:xmlObj, :string, alg} = :xmerl_xpath.string('string(//me:alg[1])', doc)
|
|
||||||
{:xmlObj, :string, encoding} = :xmerl_xpath.string('string(//me:encoding[1])', doc)
|
|
||||||
{:xmlObj, :string, type} = :xmerl_xpath.string('string(//me:data[1]/@type)', doc)
|
|
||||||
|
|
||||||
{:ok, data} = Base.url_decode64(to_string(data), ignore: :whitespace)
|
|
||||||
{:ok, sig} = Base.url_decode64(to_string(sig), ignore: :whitespace)
|
|
||||||
alg = to_string(alg)
|
|
||||||
encoding = to_string(encoding)
|
|
||||||
type = to_string(type)
|
|
||||||
|
|
||||||
[data, type, encoding, alg, sig]
|
|
||||||
end
|
|
||||||
|
|
||||||
def fetch_magic_key(salmon) do
|
|
||||||
with [data, _, _, _, _] <- decode(salmon),
|
|
||||||
doc <- XML.parse_document(data),
|
|
||||||
uri when not is_nil(uri) <- XML.string_from_xpath("/entry/author[1]/uri", doc),
|
|
||||||
{:ok, public_key} <- User.get_public_key_for_ap_id(uri),
|
|
||||||
magic_key <- encode_key(public_key) do
|
|
||||||
{:ok, magic_key}
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
def decode_and_validate(magickey, salmon) do
|
|
||||||
[data, type, encoding, alg, sig] = decode(salmon)
|
|
||||||
|
|
||||||
signed_text =
|
|
||||||
[data, type, encoding, alg]
|
|
||||||
|> Enum.map(&Base.url_encode64/1)
|
|
||||||
|> Enum.join(".")
|
|
||||||
|
|
||||||
key = decode_key(magickey)
|
|
||||||
|
|
||||||
verify = :public_key.verify(signed_text, :sha256, sig, key)
|
|
||||||
|
|
||||||
if verify do
|
|
||||||
{:ok, data}
|
|
||||||
else
|
|
||||||
:error
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
def decode_key("RSA." <> magickey) do
|
|
||||||
make_integer = fn bin ->
|
|
||||||
list = :erlang.binary_to_list(bin)
|
|
||||||
Enum.reduce(list, 0, fn el, acc -> acc <<< 8 ||| el end)
|
|
||||||
end
|
|
||||||
|
|
||||||
[modulus, exponent] =
|
|
||||||
magickey
|
|
||||||
|> String.split(".")
|
|
||||||
|> Enum.map(fn n -> Base.url_decode64!(n, padding: false) end)
|
|
||||||
|> Enum.map(make_integer)
|
|
||||||
|
|
||||||
{:RSAPublicKey, modulus, exponent}
|
|
||||||
end
|
|
||||||
|
|
||||||
def encode_key({:RSAPublicKey, modulus, exponent}) do
|
|
||||||
modulus_enc = :binary.encode_unsigned(modulus) |> Base.url_encode64()
|
|
||||||
exponent_enc = :binary.encode_unsigned(exponent) |> Base.url_encode64()
|
|
||||||
|
|
||||||
"RSA.#{modulus_enc}.#{exponent_enc}"
|
|
||||||
end
|
|
||||||
|
|
||||||
def encode(private_key, doc) do
|
|
||||||
type = "application/atom+xml"
|
|
||||||
encoding = "base64url"
|
|
||||||
alg = "RSA-SHA256"
|
|
||||||
|
|
||||||
signed_text =
|
|
||||||
[doc, type, encoding, alg]
|
|
||||||
|> Enum.map(&Base.url_encode64/1)
|
|
||||||
|> Enum.join(".")
|
|
||||||
|
|
||||||
signature =
|
|
||||||
signed_text
|
|
||||||
|> :public_key.sign(:sha256, private_key)
|
|
||||||
|> to_string
|
|
||||||
|> Base.url_encode64()
|
|
||||||
|
|
||||||
doc_base64 =
|
|
||||||
doc
|
|
||||||
|> Base.url_encode64()
|
|
||||||
|
|
||||||
# Don't need proper xml building, these strings are safe to leave unescaped
|
|
||||||
salmon = """
|
|
||||||
<?xml version="1.0" encoding="UTF-8"?>
|
|
||||||
<me:env xmlns:me="http://salmon-protocol.org/ns/magic-env">
|
|
||||||
<me:data type="application/atom+xml">#{doc_base64}</me:data>
|
|
||||||
<me:encoding>#{encoding}</me:encoding>
|
|
||||||
<me:alg>#{alg}</me:alg>
|
|
||||||
<me:sig>#{signature}</me:sig>
|
|
||||||
</me:env>
|
|
||||||
"""
|
|
||||||
|
|
||||||
{:ok, salmon}
|
|
||||||
end
|
|
||||||
|
|
||||||
def remote_users(%User{id: user_id}, %{data: %{"to" => to} = data}) do
|
|
||||||
cc = Map.get(data, "cc", [])
|
|
||||||
|
|
||||||
bcc =
|
|
||||||
data
|
|
||||||
|> Map.get("bcc", [])
|
|
||||||
|> Enum.reduce([], fn ap_id, bcc ->
|
|
||||||
case Pleroma.List.get_by_ap_id(ap_id) do
|
|
||||||
%Pleroma.List{user_id: ^user_id} = list ->
|
|
||||||
{:ok, following} = Pleroma.List.get_following(list)
|
|
||||||
bcc ++ Enum.map(following, & &1.ap_id)
|
|
||||||
|
|
||||||
_ ->
|
|
||||||
bcc
|
|
||||||
end
|
|
||||||
end)
|
|
||||||
|
|
||||||
[to, cc, bcc]
|
|
||||||
|> Enum.concat()
|
|
||||||
|> Enum.map(&User.get_cached_by_ap_id/1)
|
|
||||||
|> Enum.filter(fn user -> user && !user.local end)
|
|
||||||
end
|
|
||||||
|
|
||||||
@doc "Pushes an activity to remote account."
|
|
||||||
def publish_one(%{recipient: %{info: %{salmon: salmon}}} = params),
|
|
||||||
do: publish_one(Map.put(params, :recipient, salmon))
|
|
||||||
|
|
||||||
def publish_one(%{recipient: url, feed: feed} = params) when is_binary(url) do
|
|
||||||
with {:ok, %{status: code}} when code in 200..299 <-
|
|
||||||
HTTP.post(
|
|
||||||
url,
|
|
||||||
feed,
|
|
||||||
[{"Content-Type", "application/magic-envelope+xml"}]
|
|
||||||
) do
|
|
||||||
if !Map.has_key?(params, :unreachable_since) || params[:unreachable_since],
|
|
||||||
do: Instances.set_reachable(url)
|
|
||||||
|
|
||||||
Logger.debug(fn -> "Pushed to #{url}, code #{code}" end)
|
|
||||||
{:ok, code}
|
|
||||||
else
|
|
||||||
e ->
|
|
||||||
unless params[:unreachable_since], do: Instances.set_reachable(url)
|
|
||||||
Logger.debug(fn -> "Pushing Salmon to #{url} failed, #{inspect(e)}" end)
|
|
||||||
{:error, "Unreachable instance"}
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
def publish_one(%{recipient_id: recipient_id} = params) do
|
|
||||||
recipient = User.get_cached_by_id(recipient_id)
|
|
||||||
|
|
||||||
params
|
|
||||||
|> Map.delete(:recipient_id)
|
|
||||||
|> Map.put(:recipient, recipient)
|
|
||||||
|> publish_one()
|
|
||||||
end
|
|
||||||
|
|
||||||
def publish_one(_), do: :noop
|
|
||||||
|
|
||||||
@supported_activities [
|
|
||||||
"Create",
|
|
||||||
"Follow",
|
|
||||||
"Like",
|
|
||||||
"Announce",
|
|
||||||
"Undo",
|
|
||||||
"Delete"
|
|
||||||
]
|
|
||||||
|
|
||||||
def is_representable?(%Activity{data: %{"type" => type}} = activity)
|
|
||||||
when type in @supported_activities,
|
|
||||||
do: Visibility.is_public?(activity)
|
|
||||||
|
|
||||||
def is_representable?(_), do: false
|
|
||||||
|
|
||||||
@doc """
|
|
||||||
Publishes an activity to remote accounts
|
|
||||||
"""
|
|
||||||
@spec publish(User.t(), Pleroma.Activity.t()) :: none
|
|
||||||
def publish(user, activity)
|
|
||||||
|
|
||||||
def publish(%{keys: keys} = user, %{data: %{"type" => type}} = activity)
|
|
||||||
when type in @supported_activities do
|
|
||||||
feed = ActivityRepresenter.to_simple_form(activity, user, true)
|
|
||||||
|
|
||||||
if feed do
|
|
||||||
feed =
|
|
||||||
ActivityRepresenter.wrap_with_entry(feed)
|
|
||||||
|> :xmerl.export_simple(:xmerl_xml)
|
|
||||||
|> to_string
|
|
||||||
|
|
||||||
{:ok, private, _} = Keys.keys_from_pem(keys)
|
|
||||||
{:ok, feed} = encode(private, feed)
|
|
||||||
|
|
||||||
remote_users = remote_users(user, activity)
|
|
||||||
|
|
||||||
salmon_urls = Enum.map(remote_users, & &1.info.salmon)
|
|
||||||
reachable_urls_metadata = Instances.filter_reachable(salmon_urls)
|
|
||||||
reachable_urls = Map.keys(reachable_urls_metadata)
|
|
||||||
|
|
||||||
remote_users
|
|
||||||
|> Enum.filter(&(&1.info.salmon in reachable_urls))
|
|
||||||
|> Enum.each(fn remote_user ->
|
|
||||||
Logger.debug(fn -> "Sending Salmon to #{remote_user.ap_id}" end)
|
|
||||||
|
|
||||||
Publisher.enqueue_one(__MODULE__, %{
|
|
||||||
recipient_id: remote_user.id,
|
|
||||||
feed: feed,
|
|
||||||
unreachable_since: reachable_urls_metadata[remote_user.info.salmon]
|
|
||||||
})
|
|
||||||
end)
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
def publish(%{id: id}, _), do: Logger.debug(fn -> "Keys missing for user #{id}" end)
|
|
||||||
|
|
||||||
def gather_webfinger_links(%User{} = user) do
|
|
||||||
{:ok, _private, public} = Keys.keys_from_pem(user.keys)
|
|
||||||
magic_key = encode_key(public)
|
|
||||||
|
|
||||||
[
|
|
||||||
%{"rel" => "salmon", "href" => OStatus.salmon_path(user)},
|
|
||||||
%{
|
|
||||||
"rel" => "magic-public-key",
|
|
||||||
"href" => "data:application/magic-public-key,#{magic_key}"
|
|
||||||
}
|
|
||||||
]
|
|
||||||
end
|
|
||||||
|
|
||||||
def gather_nodeinfo_protocol_names, do: []
|
|
||||||
end
|
|
@ -1,101 +0,0 @@
|
|||||||
# Pleroma: A lightweight social networking server
|
|
||||||
# Copyright © 2017-2019 Pleroma Authors <https://pleroma.social/>
|
|
||||||
# SPDX-License-Identifier: AGPL-3.0-only
|
|
||||||
|
|
||||||
defmodule Pleroma.Web.Salmon.SalmonTest do
|
|
||||||
use Pleroma.DataCase
|
|
||||||
alias Pleroma.Activity
|
|
||||||
alias Pleroma.Keys
|
|
||||||
alias Pleroma.Repo
|
|
||||||
alias Pleroma.User
|
|
||||||
alias Pleroma.Web.Federator.Publisher
|
|
||||||
alias Pleroma.Web.Salmon
|
|
||||||
import Mock
|
|
||||||
import Pleroma.Factory
|
|
||||||
|
|
||||||
@magickey "RSA.pu0s-halox4tu7wmES1FVSx6u-4wc0YrUFXcqWXZG4-27UmbCOpMQftRCldNRfyA-qLbz-eqiwQhh-1EwUvjsD4cYbAHNGHwTvDOyx5AKthQUP44ykPv7kjKGh3DWKySJvcs9tlUG87hlo7AvnMo9pwRS_Zz2CacQ-MKaXyDepk=.AQAB"
|
|
||||||
|
|
||||||
@wrong_magickey "RSA.pu0s-halox4tu7wmES1FVSx6u-4wc0YrUFXcqWXZG4-27UmbCOpMQftRCldNRfyA-qLbz-eqiwQhh-1EwUvjsD4cYbAHNGHwTvDOyx5AKthQUP44ykPv7kjKGh3DWKySJvcs9tlUG87hlo7AvnMo9pwRS_Zz2CacQ-MKaXyDepk=.AQAA"
|
|
||||||
|
|
||||||
@magickey_friendica "RSA.AMwa8FUs2fWEjX0xN7yRQgegQffhBpuKNC6fa5VNSVorFjGZhRrlPMn7TQOeihlc9lBz2OsHlIedbYn2uJ7yCs0.AQAB"
|
|
||||||
|
|
||||||
setup_all do
|
|
||||||
Tesla.Mock.mock_global(fn env -> apply(HttpRequestMock, :request, [env]) end)
|
|
||||||
:ok
|
|
||||||
end
|
|
||||||
|
|
||||||
test "decodes a salmon" do
|
|
||||||
{:ok, salmon} = File.read("test/fixtures/salmon.xml")
|
|
||||||
{:ok, doc} = Salmon.decode_and_validate(@magickey, salmon)
|
|
||||||
assert Regex.match?(~r/xml/, doc)
|
|
||||||
end
|
|
||||||
|
|
||||||
test "errors on wrong magic key" do
|
|
||||||
{:ok, salmon} = File.read("test/fixtures/salmon.xml")
|
|
||||||
assert Salmon.decode_and_validate(@wrong_magickey, salmon) == :error
|
|
||||||
end
|
|
||||||
|
|
||||||
test "it encodes a magic key from a public key" do
|
|
||||||
key = Salmon.decode_key(@magickey)
|
|
||||||
magic_key = Salmon.encode_key(key)
|
|
||||||
|
|
||||||
assert @magickey == magic_key
|
|
||||||
end
|
|
||||||
|
|
||||||
test "it decodes a friendica public key" do
|
|
||||||
_key = Salmon.decode_key(@magickey_friendica)
|
|
||||||
end
|
|
||||||
|
|
||||||
test "encodes an xml payload with a private key" do
|
|
||||||
doc = File.read!("test/fixtures/incoming_note_activity.xml")
|
|
||||||
pem = File.read!("test/fixtures/private_key.pem")
|
|
||||||
{:ok, private, public} = Keys.keys_from_pem(pem)
|
|
||||||
|
|
||||||
# Let's try a roundtrip.
|
|
||||||
{:ok, salmon} = Salmon.encode(private, doc)
|
|
||||||
{:ok, decoded_doc} = Salmon.decode_and_validate(Salmon.encode_key(public), salmon)
|
|
||||||
|
|
||||||
assert doc == decoded_doc
|
|
||||||
end
|
|
||||||
|
|
||||||
test "it gets a magic key" do
|
|
||||||
salmon = File.read!("test/fixtures/salmon2.xml")
|
|
||||||
{:ok, key} = Salmon.fetch_magic_key(salmon)
|
|
||||||
|
|
||||||
assert key ==
|
|
||||||
"RSA.uzg6r1peZU0vXGADWxGJ0PE34WvmhjUmydbX5YYdOiXfODVLwCMi1umGoqUDm-mRu4vNEdFBVJU1CpFA7dKzWgIsqsa501i2XqElmEveXRLvNRWFB6nG03Q5OUY2as8eE54BJm0p20GkMfIJGwP6TSFb-ICp3QjzbatuSPJ6xCE=.AQAB"
|
|
||||||
end
|
|
||||||
|
|
||||||
test_with_mock "it pushes an activity to remote accounts it's addressed to",
|
|
||||||
Publisher,
|
|
||||||
[:passthrough],
|
|
||||||
[] do
|
|
||||||
user_data = %{
|
|
||||||
info: %{
|
|
||||||
salmon: "http://test-example.org/salmon"
|
|
||||||
},
|
|
||||||
local: false
|
|
||||||
}
|
|
||||||
|
|
||||||
mentioned_user = insert(:user, user_data)
|
|
||||||
note = insert(:note)
|
|
||||||
|
|
||||||
activity_data = %{
|
|
||||||
"id" => Pleroma.Web.ActivityPub.Utils.generate_activity_id(),
|
|
||||||
"type" => "Create",
|
|
||||||
"actor" => note.data["actor"],
|
|
||||||
"to" => note.data["to"] ++ [mentioned_user.ap_id],
|
|
||||||
"object" => note.data,
|
|
||||||
"published_at" => DateTime.utc_now() |> DateTime.to_iso8601(),
|
|
||||||
"context" => note.data["context"]
|
|
||||||
}
|
|
||||||
|
|
||||||
{:ok, activity} = Repo.insert(%Activity{data: activity_data, recipients: activity_data["to"]})
|
|
||||||
user = User.get_cached_by_ap_id(activity.data["actor"])
|
|
||||||
{:ok, user} = User.ensure_keys_present(user)
|
|
||||||
|
|
||||||
Salmon.publish(user, activity)
|
|
||||||
|
|
||||||
assert called(Publisher.enqueue_one(Salmon, %{recipient_id: mentioned_user.id}))
|
|
||||||
end
|
|
||||||
end
|
|
Loading…
Reference in New Issue
Block a user