Fork of Pleroma with site-specific changes and feature branches https://git.pleroma.social/pleroma/pleroma
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

1553 lines
48KB

  1. # Pleroma: A lightweight social networking server
  2. # Copyright © 2017-2020 Pleroma Authors <https://pleroma.social/>
  3. # SPDX-License-Identifier: AGPL-3.0-only
  4. defmodule Pleroma.Web.ActivityPub.ActivityPub do
  5. alias Pleroma.Activity
  6. alias Pleroma.Activity.Ir.Topics
  7. alias Pleroma.Config
  8. alias Pleroma.Constants
  9. alias Pleroma.Conversation
  10. alias Pleroma.Conversation.Participation
  11. alias Pleroma.Notification
  12. alias Pleroma.Object
  13. alias Pleroma.Object.Containment
  14. alias Pleroma.Object.Fetcher
  15. alias Pleroma.Pagination
  16. alias Pleroma.Repo
  17. alias Pleroma.Upload
  18. alias Pleroma.User
  19. alias Pleroma.Web.ActivityPub.MRF
  20. alias Pleroma.Web.ActivityPub.Transmogrifier
  21. alias Pleroma.Web.ActivityPub.Utils
  22. alias Pleroma.Web.Streamer
  23. alias Pleroma.Web.WebFinger
  24. alias Pleroma.Workers.BackgroundWorker
  25. import Ecto.Query
  26. import Pleroma.Web.ActivityPub.Utils
  27. import Pleroma.Web.ActivityPub.Visibility
  28. require Logger
  29. require Pleroma.Constants
  30. # For Announce activities, we filter the recipients based on following status for any actors
  31. # that match actual users. See issue #164 for more information about why this is necessary.
  32. defp get_recipients(%{"type" => "Announce"} = data) do
  33. to = Map.get(data, "to", [])
  34. cc = Map.get(data, "cc", [])
  35. bcc = Map.get(data, "bcc", [])
  36. actor = User.get_cached_by_ap_id(data["actor"])
  37. recipients =
  38. Enum.filter(Enum.concat([to, cc, bcc]), fn recipient ->
  39. case User.get_cached_by_ap_id(recipient) do
  40. nil -> true
  41. user -> User.following?(user, actor)
  42. end
  43. end)
  44. {recipients, to, cc}
  45. end
  46. defp get_recipients(%{"type" => "Create"} = data) do
  47. to = Map.get(data, "to", [])
  48. cc = Map.get(data, "cc", [])
  49. bcc = Map.get(data, "bcc", [])
  50. actor = Map.get(data, "actor", [])
  51. recipients = [to, cc, bcc, [actor]] |> Enum.concat() |> Enum.uniq()
  52. {recipients, to, cc}
  53. end
  54. defp get_recipients(data) do
  55. to = Map.get(data, "to", [])
  56. cc = Map.get(data, "cc", [])
  57. bcc = Map.get(data, "bcc", [])
  58. recipients = Enum.concat([to, cc, bcc])
  59. {recipients, to, cc}
  60. end
  61. defp check_actor_is_active(actor) do
  62. if not is_nil(actor) do
  63. with user <- User.get_cached_by_ap_id(actor),
  64. false <- user.deactivated do
  65. true
  66. else
  67. _e -> false
  68. end
  69. else
  70. true
  71. end
  72. end
  73. defp check_remote_limit(%{"object" => %{"content" => content}}) when not is_nil(content) do
  74. limit = Config.get([:instance, :remote_limit])
  75. String.length(content) <= limit
  76. end
  77. defp check_remote_limit(_), do: true
  78. def increase_note_count_if_public(actor, object) do
  79. if is_public?(object), do: User.increase_note_count(actor), else: {:ok, actor}
  80. end
  81. def decrease_note_count_if_public(actor, object) do
  82. if is_public?(object), do: User.decrease_note_count(actor), else: {:ok, actor}
  83. end
  84. def increase_replies_count_if_reply(%{
  85. "object" => %{"inReplyTo" => reply_ap_id} = object,
  86. "type" => "Create"
  87. }) do
  88. if is_public?(object) do
  89. Object.increase_replies_count(reply_ap_id)
  90. end
  91. end
  92. def increase_replies_count_if_reply(_create_data), do: :noop
  93. def decrease_replies_count_if_reply(%Object{
  94. data: %{"inReplyTo" => reply_ap_id} = object
  95. }) do
  96. if is_public?(object) do
  97. Object.decrease_replies_count(reply_ap_id)
  98. end
  99. end
  100. def decrease_replies_count_if_reply(_object), do: :noop
  101. def increase_poll_votes_if_vote(%{
  102. "object" => %{"inReplyTo" => reply_ap_id, "name" => name},
  103. "type" => "Create"
  104. }) do
  105. Object.increase_vote_count(reply_ap_id, name)
  106. end
  107. def increase_poll_votes_if_vote(_create_data), do: :noop
  108. @spec insert(map(), boolean(), boolean(), boolean()) :: {:ok, Activity.t()} | {:error, any()}
  109. def insert(map, local \\ true, fake \\ false, bypass_actor_check \\ false) when is_map(map) do
  110. with nil <- Activity.normalize(map),
  111. map <- lazy_put_activity_defaults(map, fake),
  112. true <- bypass_actor_check || check_actor_is_active(map["actor"]),
  113. {_, true} <- {:remote_limit_error, check_remote_limit(map)},
  114. {:ok, map} <- MRF.filter(map),
  115. {recipients, _, _} = get_recipients(map),
  116. {:fake, false, map, recipients} <- {:fake, fake, map, recipients},
  117. {:containment, :ok} <- {:containment, Containment.contain_child(map)},
  118. {:ok, map, object} <- insert_full_object(map) do
  119. {:ok, activity} =
  120. Repo.insert(%Activity{
  121. data: map,
  122. local: local,
  123. actor: map["actor"],
  124. recipients: recipients
  125. })
  126. # Splice in the child object if we have one.
  127. activity =
  128. if not is_nil(object) do
  129. Map.put(activity, :object, object)
  130. else
  131. activity
  132. end
  133. BackgroundWorker.enqueue("fetch_data_for_activity", %{"activity_id" => activity.id})
  134. Notification.create_notifications(activity)
  135. conversation = create_or_bump_conversation(activity, map["actor"])
  136. participations = get_participations(conversation)
  137. stream_out(activity)
  138. stream_out_participations(participations)
  139. {:ok, activity}
  140. else
  141. %Activity{} = activity ->
  142. {:ok, activity}
  143. {:fake, true, map, recipients} ->
  144. activity = %Activity{
  145. data: map,
  146. local: local,
  147. actor: map["actor"],
  148. recipients: recipients,
  149. id: "pleroma:fakeid"
  150. }
  151. Pleroma.Web.RichMedia.Helpers.fetch_data_for_activity(activity)
  152. {:ok, activity}
  153. error ->
  154. {:error, error}
  155. end
  156. end
  157. defp create_or_bump_conversation(activity, actor) do
  158. with {:ok, conversation} <- Conversation.create_or_bump_for(activity),
  159. %User{} = user <- User.get_cached_by_ap_id(actor),
  160. Participation.mark_as_read(user, conversation) do
  161. {:ok, conversation}
  162. end
  163. end
  164. defp get_participations({:ok, conversation}) do
  165. conversation
  166. |> Repo.preload(:participations, force: true)
  167. |> Map.get(:participations)
  168. end
  169. defp get_participations(_), do: []
  170. def stream_out_participations(participations) do
  171. participations =
  172. participations
  173. |> Repo.preload(:user)
  174. Streamer.stream("participation", participations)
  175. end
  176. def stream_out_participations(%Object{data: %{"context" => context}}, user) do
  177. with %Conversation{} = conversation <- Conversation.get_for_ap_id(context),
  178. conversation = Repo.preload(conversation, :participations),
  179. last_activity_id =
  180. fetch_latest_activity_id_for_context(conversation.ap_id, %{
  181. "user" => user,
  182. "blocking_user" => user
  183. }) do
  184. if last_activity_id do
  185. stream_out_participations(conversation.participations)
  186. end
  187. end
  188. end
  189. def stream_out_participations(_, _), do: :noop
  190. def stream_out(%Activity{data: %{"type" => data_type}} = activity)
  191. when data_type in ["Create", "Announce", "Delete"] do
  192. activity
  193. |> Topics.get_activity_topics()
  194. |> Streamer.stream(activity)
  195. end
  196. def stream_out(_activity) do
  197. :noop
  198. end
  199. @spec create(map(), boolean()) :: {:ok, Activity.t()} | {:error, any()}
  200. def create(params, fake \\ false) do
  201. with {:ok, result} <- Repo.transaction(fn -> do_create(params, fake) end) do
  202. result
  203. end
  204. end
  205. defp do_create(%{to: to, actor: actor, context: context, object: object} = params, fake) do
  206. additional = params[:additional] || %{}
  207. # only accept false as false value
  208. local = !(params[:local] == false)
  209. published = params[:published]
  210. quick_insert? = Config.get([:env]) == :benchmark
  211. with create_data <-
  212. make_create_data(
  213. %{to: to, actor: actor, published: published, context: context, object: object},
  214. additional
  215. ),
  216. {:ok, activity} <- insert(create_data, local, fake),
  217. {:fake, false, activity} <- {:fake, fake, activity},
  218. _ <- increase_replies_count_if_reply(create_data),
  219. _ <- increase_poll_votes_if_vote(create_data),
  220. {:quick_insert, false, activity} <- {:quick_insert, quick_insert?, activity},
  221. {:ok, _actor} <- increase_note_count_if_public(actor, activity),
  222. :ok <- maybe_federate(activity) do
  223. {:ok, activity}
  224. else
  225. {:quick_insert, true, activity} ->
  226. {:ok, activity}
  227. {:fake, true, activity} ->
  228. {:ok, activity}
  229. {:error, message} ->
  230. Repo.rollback(message)
  231. end
  232. end
  233. @spec listen(map()) :: {:ok, Activity.t()} | {:error, any()}
  234. def listen(%{to: to, actor: actor, context: context, object: object} = params) do
  235. additional = params[:additional] || %{}
  236. # only accept false as false value
  237. local = !(params[:local] == false)
  238. published = params[:published]
  239. with listen_data <-
  240. make_listen_data(
  241. %{to: to, actor: actor, published: published, context: context, object: object},
  242. additional
  243. ),
  244. {:ok, activity} <- insert(listen_data, local),
  245. :ok <- maybe_federate(activity) do
  246. {:ok, activity}
  247. end
  248. end
  249. @spec accept(map()) :: {:ok, Activity.t()} | {:error, any()}
  250. def accept(params) do
  251. accept_or_reject("Accept", params)
  252. end
  253. @spec reject(map()) :: {:ok, Activity.t()} | {:error, any()}
  254. def reject(params) do
  255. accept_or_reject("Reject", params)
  256. end
  257. @spec accept_or_reject(String.t(), map()) :: {:ok, Activity.t()} | {:error, any()}
  258. def accept_or_reject(type, %{to: to, actor: actor, object: object} = params) do
  259. local = Map.get(params, :local, true)
  260. activity_id = Map.get(params, :activity_id, nil)
  261. with data <-
  262. %{"to" => to, "type" => type, "actor" => actor.ap_id, "object" => object}
  263. |> Utils.maybe_put("id", activity_id),
  264. {:ok, activity} <- insert(data, local),
  265. :ok <- maybe_federate(activity) do
  266. {:ok, activity}
  267. end
  268. end
  269. @spec update(map()) :: {:ok, Activity.t()} | {:error, any()}
  270. def update(%{to: to, cc: cc, actor: actor, object: object} = params) do
  271. local = !(params[:local] == false)
  272. activity_id = params[:activity_id]
  273. with data <- %{
  274. "to" => to,
  275. "cc" => cc,
  276. "type" => "Update",
  277. "actor" => actor,
  278. "object" => object
  279. },
  280. data <- Utils.maybe_put(data, "id", activity_id),
  281. {:ok, activity} <- insert(data, local),
  282. :ok <- maybe_federate(activity) do
  283. {:ok, activity}
  284. end
  285. end
  286. @spec react_with_emoji(User.t(), Object.t(), String.t(), keyword()) ::
  287. {:ok, Activity.t(), Object.t()} | {:error, any()}
  288. def react_with_emoji(user, object, emoji, options \\ []) do
  289. with {:ok, result} <-
  290. Repo.transaction(fn -> do_react_with_emoji(user, object, emoji, options) end) do
  291. result
  292. end
  293. end
  294. defp do_react_with_emoji(user, object, emoji, options) do
  295. with local <- Keyword.get(options, :local, true),
  296. activity_id <- Keyword.get(options, :activity_id, nil),
  297. true <- Pleroma.Emoji.is_unicode_emoji?(emoji),
  298. reaction_data <- make_emoji_reaction_data(user, object, emoji, activity_id),
  299. {:ok, activity} <- insert(reaction_data, local),
  300. {:ok, object} <- add_emoji_reaction_to_object(activity, object),
  301. :ok <- maybe_federate(activity) do
  302. {:ok, activity, object}
  303. else
  304. false -> {:error, false}
  305. {:error, error} -> Repo.rollback(error)
  306. end
  307. end
  308. @spec unreact_with_emoji(User.t(), String.t(), keyword()) ::
  309. {:ok, Activity.t(), Object.t()} | {:error, any()}
  310. def unreact_with_emoji(user, reaction_id, options \\ []) do
  311. with {:ok, result} <-
  312. Repo.transaction(fn -> do_unreact_with_emoji(user, reaction_id, options) end) do
  313. result
  314. end
  315. end
  316. defp do_unreact_with_emoji(user, reaction_id, options) do
  317. with local <- Keyword.get(options, :local, true),
  318. activity_id <- Keyword.get(options, :activity_id, nil),
  319. user_ap_id <- user.ap_id,
  320. %Activity{actor: ^user_ap_id} = reaction_activity <- Activity.get_by_ap_id(reaction_id),
  321. object <- Object.normalize(reaction_activity),
  322. unreact_data <- make_undo_data(user, reaction_activity, activity_id),
  323. {:ok, activity} <- insert(unreact_data, local),
  324. {:ok, object} <- remove_emoji_reaction_from_object(reaction_activity, object),
  325. :ok <- maybe_federate(activity) do
  326. {:ok, activity, object}
  327. else
  328. {:error, error} -> Repo.rollback(error)
  329. end
  330. end
  331. # TODO: This is weird, maybe we shouldn't check here if we can make the activity.
  332. @spec like(User.t(), Object.t(), String.t() | nil, boolean()) ::
  333. {:ok, Activity.t(), Object.t()} | {:error, any()}
  334. def like(user, object, activity_id \\ nil, local \\ true) do
  335. with {:ok, result} <- Repo.transaction(fn -> do_like(user, object, activity_id, local) end) do
  336. result
  337. end
  338. end
  339. defp do_like(
  340. %User{ap_id: ap_id} = user,
  341. %Object{data: %{"id" => _}} = object,
  342. activity_id,
  343. local
  344. ) do
  345. with nil <- get_existing_like(ap_id, object),
  346. like_data <- make_like_data(user, object, activity_id),
  347. {:ok, activity} <- insert(like_data, local),
  348. {:ok, object} <- add_like_to_object(activity, object),
  349. :ok <- maybe_federate(activity) do
  350. {:ok, activity, object}
  351. else
  352. %Activity{} = activity ->
  353. {:ok, activity, object}
  354. {:error, error} ->
  355. Repo.rollback(error)
  356. end
  357. end
  358. @spec unlike(User.t(), Object.t(), String.t() | nil, boolean()) ::
  359. {:ok, Activity.t(), Activity.t(), Object.t()} | {:ok, Object.t()} | {:error, any()}
  360. def unlike(%User{} = actor, %Object{} = object, activity_id \\ nil, local \\ true) do
  361. with {:ok, result} <-
  362. Repo.transaction(fn -> do_unlike(actor, object, activity_id, local) end) do
  363. result
  364. end
  365. end
  366. defp do_unlike(actor, object, activity_id, local) do
  367. with %Activity{} = like_activity <- get_existing_like(actor.ap_id, object),
  368. unlike_data <- make_unlike_data(actor, like_activity, activity_id),
  369. {:ok, unlike_activity} <- insert(unlike_data, local),
  370. {:ok, _activity} <- Repo.delete(like_activity),
  371. {:ok, object} <- remove_like_from_object(like_activity, object),
  372. :ok <- maybe_federate(unlike_activity) do
  373. {:ok, unlike_activity, like_activity, object}
  374. else
  375. nil -> {:ok, object}
  376. {:error, error} -> Repo.rollback(error)
  377. end
  378. end
  379. @spec announce(User.t(), Object.t(), String.t() | nil, boolean(), boolean()) ::
  380. {:ok, Activity.t(), Object.t()} | {:error, any()}
  381. def announce(
  382. %User{ap_id: _} = user,
  383. %Object{data: %{"id" => _}} = object,
  384. activity_id \\ nil,
  385. local \\ true,
  386. public \\ true
  387. ) do
  388. with {:ok, result} <-
  389. Repo.transaction(fn -> do_announce(user, object, activity_id, local, public) end) do
  390. result
  391. end
  392. end
  393. defp do_announce(user, object, activity_id, local, public) do
  394. with true <- is_announceable?(object, user, public),
  395. announce_data <- make_announce_data(user, object, activity_id, public),
  396. {:ok, activity} <- insert(announce_data, local),
  397. {:ok, object} <- add_announce_to_object(activity, object),
  398. :ok <- maybe_federate(activity) do
  399. {:ok, activity, object}
  400. else
  401. false -> {:error, false}
  402. {:error, error} -> Repo.rollback(error)
  403. end
  404. end
  405. @spec unannounce(User.t(), Object.t(), String.t() | nil, boolean()) ::
  406. {:ok, Activity.t(), Object.t()} | {:ok, Object.t()} | {:error, any()}
  407. def unannounce(
  408. %User{} = actor,
  409. %Object{} = object,
  410. activity_id \\ nil,
  411. local \\ true
  412. ) do
  413. with {:ok, result} <-
  414. Repo.transaction(fn -> do_unannounce(actor, object, activity_id, local) end) do
  415. result
  416. end
  417. end
  418. defp do_unannounce(actor, object, activity_id, local) do
  419. with %Activity{} = announce_activity <- get_existing_announce(actor.ap_id, object),
  420. unannounce_data <- make_unannounce_data(actor, announce_activity, activity_id),
  421. {:ok, unannounce_activity} <- insert(unannounce_data, local),
  422. :ok <- maybe_federate(unannounce_activity),
  423. {:ok, _activity} <- Repo.delete(announce_activity),
  424. {:ok, object} <- remove_announce_from_object(announce_activity, object) do
  425. {:ok, unannounce_activity, object}
  426. else
  427. nil -> {:ok, object}
  428. {:error, error} -> Repo.rollback(error)
  429. end
  430. end
  431. @spec follow(User.t(), User.t(), String.t() | nil, boolean()) ::
  432. {:ok, Activity.t()} | {:error, any()}
  433. def follow(follower, followed, activity_id \\ nil, local \\ true) do
  434. with {:ok, result} <-
  435. Repo.transaction(fn -> do_follow(follower, followed, activity_id, local) end) do
  436. result
  437. end
  438. end
  439. defp do_follow(follower, followed, activity_id, local) do
  440. with data <- make_follow_data(follower, followed, activity_id),
  441. {:ok, activity} <- insert(data, local),
  442. :ok <- maybe_federate(activity),
  443. _ <- User.set_follow_state_cache(follower.ap_id, followed.ap_id, activity.data["state"]) do
  444. {:ok, activity}
  445. else
  446. {:error, error} -> Repo.rollback(error)
  447. end
  448. end
  449. @spec unfollow(User.t(), User.t(), String.t() | nil, boolean()) ::
  450. {:ok, Activity.t()} | nil | {:error, any()}
  451. def unfollow(follower, followed, activity_id \\ nil, local \\ true) do
  452. with {:ok, result} <-
  453. Repo.transaction(fn -> do_unfollow(follower, followed, activity_id, local) end) do
  454. result
  455. end
  456. end
  457. defp do_unfollow(follower, followed, activity_id, local) do
  458. with %Activity{} = follow_activity <- fetch_latest_follow(follower, followed),
  459. {:ok, follow_activity} <- update_follow_state(follow_activity, "cancelled"),
  460. unfollow_data <- make_unfollow_data(follower, followed, follow_activity, activity_id),
  461. {:ok, activity} <- insert(unfollow_data, local),
  462. :ok <- maybe_federate(activity) do
  463. {:ok, activity}
  464. else
  465. nil -> nil
  466. {:error, error} -> Repo.rollback(error)
  467. end
  468. end
  469. @spec delete(User.t() | Object.t(), keyword()) :: {:ok, User.t() | Object.t()} | {:error, any()}
  470. def delete(entity, options \\ []) do
  471. with {:ok, result} <- Repo.transaction(fn -> do_delete(entity, options) end) do
  472. result
  473. end
  474. end
  475. defp do_delete(%User{ap_id: ap_id, follower_address: follower_address} = user, _) do
  476. with data <- %{
  477. "to" => [follower_address],
  478. "type" => "Delete",
  479. "actor" => ap_id,
  480. "object" => %{"type" => "Person", "id" => ap_id}
  481. },
  482. {:ok, activity} <- insert(data, true, true, true),
  483. :ok <- maybe_federate(activity) do
  484. {:ok, user}
  485. end
  486. end
  487. defp do_delete(%Object{data: %{"id" => id, "actor" => actor}} = object, options) do
  488. local = Keyword.get(options, :local, true)
  489. activity_id = Keyword.get(options, :activity_id, nil)
  490. actor = Keyword.get(options, :actor, actor)
  491. user = User.get_cached_by_ap_id(actor)
  492. to = (object.data["to"] || []) ++ (object.data["cc"] || [])
  493. with create_activity <- Activity.get_create_by_object_ap_id(id),
  494. data <-
  495. %{
  496. "type" => "Delete",
  497. "actor" => actor,
  498. "object" => id,
  499. "to" => to,
  500. "deleted_activity_id" => create_activity && create_activity.id
  501. }
  502. |> maybe_put("id", activity_id),
  503. {:ok, activity} <- insert(data, local, false),
  504. {:ok, object, _create_activity} <- Object.delete(object),
  505. stream_out_participations(object, user),
  506. _ <- decrease_replies_count_if_reply(object),
  507. {:ok, _actor} <- decrease_note_count_if_public(user, object),
  508. :ok <- maybe_federate(activity) do
  509. {:ok, activity}
  510. else
  511. {:error, error} ->
  512. Repo.rollback(error)
  513. end
  514. end
  515. @spec block(User.t(), User.t(), String.t() | nil, boolean()) ::
  516. {:ok, Activity.t()} | {:error, any()}
  517. def block(blocker, blocked, activity_id \\ nil, local \\ true) do
  518. with {:ok, result} <-
  519. Repo.transaction(fn -> do_block(blocker, blocked, activity_id, local) end) do
  520. result
  521. end
  522. end
  523. defp do_block(blocker, blocked, activity_id, local) do
  524. outgoing_blocks = Config.get([:activitypub, :outgoing_blocks])
  525. unfollow_blocked = Config.get([:activitypub, :unfollow_blocked])
  526. if unfollow_blocked do
  527. follow_activity = fetch_latest_follow(blocker, blocked)
  528. if follow_activity, do: unfollow(blocker, blocked, nil, local)
  529. end
  530. with true <- outgoing_blocks,
  531. block_data <- make_block_data(blocker, blocked, activity_id),
  532. {:ok, activity} <- insert(block_data, local),
  533. :ok <- maybe_federate(activity) do
  534. {:ok, activity}
  535. else
  536. {:error, error} -> Repo.rollback(error)
  537. end
  538. end
  539. @spec unblock(User.t(), User.t(), String.t() | nil, boolean()) ::
  540. {:ok, Activity.t()} | {:error, any()} | nil
  541. def unblock(blocker, blocked, activity_id \\ nil, local \\ true) do
  542. with {:ok, result} <-
  543. Repo.transaction(fn -> do_unblock(blocker, blocked, activity_id, local) end) do
  544. result
  545. end
  546. end
  547. defp do_unblock(blocker, blocked, activity_id, local) do
  548. with %Activity{} = block_activity <- fetch_latest_block(blocker, blocked),
  549. unblock_data <- make_unblock_data(blocker, blocked, block_activity, activity_id),
  550. {:ok, activity} <- insert(unblock_data, local),
  551. :ok <- maybe_federate(activity) do
  552. {:ok, activity}
  553. else
  554. nil -> nil
  555. {:error, error} -> Repo.rollback(error)
  556. end
  557. end
  558. @spec flag(map()) :: {:ok, Activity.t()} | {:error, any()}
  559. def flag(
  560. %{
  561. actor: actor,
  562. context: _context,
  563. account: account,
  564. statuses: statuses,
  565. content: content
  566. } = params
  567. ) do
  568. # only accept false as false value
  569. local = !(params[:local] == false)
  570. forward = !(params[:forward] == false)
  571. additional = params[:additional] || %{}
  572. additional =
  573. if forward do
  574. Map.merge(additional, %{"to" => [], "cc" => [account.ap_id]})
  575. else
  576. Map.merge(additional, %{"to" => [], "cc" => []})
  577. end
  578. with flag_data <- make_flag_data(params, additional),
  579. {:ok, activity} <- insert(flag_data, local),
  580. {:ok, stripped_activity} <- strip_report_status_data(activity),
  581. :ok <- maybe_federate(stripped_activity) do
  582. User.all_superusers()
  583. |> Enum.filter(fn user -> not is_nil(user.email) end)
  584. |> Enum.each(fn superuser ->
  585. superuser
  586. |> Pleroma.Emails.AdminEmail.report(actor, account, statuses, content)
  587. |> Pleroma.Emails.Mailer.deliver_async()
  588. end)
  589. {:ok, activity}
  590. end
  591. end
  592. @spec move(User.t(), User.t(), boolean()) :: {:ok, Activity.t()} | {:error, any()}
  593. def move(%User{} = origin, %User{} = target, local \\ true) do
  594. params = %{
  595. "type" => "Move",
  596. "actor" => origin.ap_id,
  597. "object" => origin.ap_id,
  598. "target" => target.ap_id
  599. }
  600. with true <- origin.ap_id in target.also_known_as,
  601. {:ok, activity} <- insert(params, local) do
  602. maybe_federate(activity)
  603. BackgroundWorker.enqueue("move_following", %{
  604. "origin_id" => origin.id,
  605. "target_id" => target.id
  606. })
  607. {:ok, activity}
  608. else
  609. false -> {:error, "Target account must have the origin in `alsoKnownAs`"}
  610. err -> err
  611. end
  612. end
  613. defp fetch_activities_for_context_query(context, opts) do
  614. public = [Constants.as_public()]
  615. recipients =
  616. if opts["user"],
  617. do: [opts["user"].ap_id | User.following(opts["user"])] ++ public,
  618. else: public
  619. from(activity in Activity)
  620. |> maybe_preload_objects(opts)
  621. |> maybe_preload_bookmarks(opts)
  622. |> maybe_set_thread_muted_field(opts)
  623. |> restrict_blocked(opts)
  624. |> restrict_recipients(recipients, opts["user"])
  625. |> where(
  626. [activity],
  627. fragment(
  628. "?->>'type' = ? and ?->>'context' = ?",
  629. activity.data,
  630. "Create",
  631. activity.data,
  632. ^context
  633. )
  634. )
  635. |> exclude_poll_votes(opts)
  636. |> exclude_id(opts)
  637. |> order_by([activity], desc: activity.id)
  638. end
  639. @spec fetch_activities_for_context(String.t(), keyword() | map()) :: [Activity.t()]
  640. def fetch_activities_for_context(context, opts \\ %{}) do
  641. context
  642. |> fetch_activities_for_context_query(opts)
  643. |> Repo.all()
  644. end
  645. @spec fetch_latest_activity_id_for_context(String.t(), keyword() | map()) ::
  646. FlakeId.Ecto.CompatType.t() | nil
  647. def fetch_latest_activity_id_for_context(context, opts \\ %{}) do
  648. context
  649. |> fetch_activities_for_context_query(Map.merge(%{"skip_preload" => true}, opts))
  650. |> limit(1)
  651. |> select([a], a.id)
  652. |> Repo.one()
  653. end
  654. @spec fetch_public_activities(map(), Pagination.type()) :: [Activity.t()]
  655. def fetch_public_activities(opts \\ %{}, pagination \\ :keyset) do
  656. opts = Map.drop(opts, ["user"])
  657. [Constants.as_public()]
  658. |> fetch_activities_query(opts)
  659. |> restrict_unlisted()
  660. |> Pagination.fetch_paginated(opts, pagination)
  661. end
  662. @valid_visibilities ~w[direct unlisted public private]
  663. defp restrict_visibility(query, %{visibility: visibility})
  664. when is_list(visibility) do
  665. if Enum.all?(visibility, &(&1 in @valid_visibilities)) do
  666. query =
  667. from(
  668. a in query,
  669. where:
  670. fragment(
  671. "activity_visibility(?, ?, ?) = ANY (?)",
  672. a.actor,
  673. a.recipients,
  674. a.data,
  675. ^visibility
  676. )
  677. )
  678. query
  679. else
  680. Logger.error("Could not restrict visibility to #{visibility}")
  681. end
  682. end
  683. defp restrict_visibility(query, %{visibility: visibility})
  684. when visibility in @valid_visibilities do
  685. from(
  686. a in query,
  687. where:
  688. fragment("activity_visibility(?, ?, ?) = ?", a.actor, a.recipients, a.data, ^visibility)
  689. )
  690. end
  691. defp restrict_visibility(_query, %{visibility: visibility})
  692. when visibility not in @valid_visibilities do
  693. Logger.error("Could not restrict visibility to #{visibility}")
  694. end
  695. defp restrict_visibility(query, _visibility), do: query
  696. defp exclude_visibility(query, %{"exclude_visibilities" => visibility})
  697. when is_list(visibility) do
  698. if Enum.all?(visibility, &(&1 in @valid_visibilities)) do
  699. from(
  700. a in query,
  701. where:
  702. not fragment(
  703. "activity_visibility(?, ?, ?) = ANY (?)",
  704. a.actor,
  705. a.recipients,
  706. a.data,
  707. ^visibility
  708. )
  709. )
  710. else
  711. Logger.error("Could not exclude visibility to #{visibility}")
  712. query
  713. end
  714. end
  715. defp exclude_visibility(query, %{"exclude_visibilities" => visibility})
  716. when visibility in @valid_visibilities do
  717. from(
  718. a in query,
  719. where:
  720. not fragment(
  721. "activity_visibility(?, ?, ?) = ?",
  722. a.actor,
  723. a.recipients,
  724. a.data,
  725. ^visibility
  726. )
  727. )
  728. end
  729. defp exclude_visibility(query, %{"exclude_visibilities" => visibility})
  730. when visibility not in @valid_visibilities do
  731. Logger.error("Could not exclude visibility to #{visibility}")
  732. query
  733. end
  734. defp exclude_visibility(query, _visibility), do: query
  735. defp restrict_thread_visibility(query, _, %{skip_thread_containment: true} = _),
  736. do: query
  737. defp restrict_thread_visibility(
  738. query,
  739. %{"user" => %User{skip_thread_containment: true}},
  740. _
  741. ),
  742. do: query
  743. defp restrict_thread_visibility(query, %{"user" => %User{ap_id: ap_id}}, _) do
  744. from(
  745. a in query,
  746. where: fragment("thread_visibility(?, (?)->>'id') = true", ^ap_id, a.data)
  747. )
  748. end
  749. defp restrict_thread_visibility(query, _, _), do: query
  750. def fetch_user_abstract_activities(user, reading_user, params \\ %{}) do
  751. params =
  752. params
  753. |> Map.put("user", reading_user)
  754. |> Map.put("actor_id", user.ap_id)
  755. recipients =
  756. user_activities_recipients(%{
  757. "godmode" => params["godmode"],
  758. "reading_user" => reading_user
  759. })
  760. fetch_activities(recipients, params)
  761. |> Enum.reverse()
  762. end
  763. def fetch_user_activities(user, reading_user, params \\ %{}) do
  764. params =
  765. params
  766. |> Map.put("type", ["Create", "Announce"])
  767. |> Map.put("user", reading_user)
  768. |> Map.put("actor_id", user.ap_id)
  769. |> Map.put("pinned_activity_ids", user.pinned_activities)
  770. params =
  771. if User.blocks?(reading_user, user) do
  772. params
  773. else
  774. params
  775. |> Map.put("blocking_user", reading_user)
  776. |> Map.put("muting_user", reading_user)
  777. end
  778. recipients =
  779. user_activities_recipients(%{
  780. "godmode" => params["godmode"],
  781. "reading_user" => reading_user
  782. })
  783. fetch_activities(recipients, params)
  784. |> Enum.reverse()
  785. end
  786. def fetch_statuses(reading_user, params) do
  787. params =
  788. params
  789. |> Map.put("type", ["Create", "Announce"])
  790. recipients =
  791. user_activities_recipients(%{
  792. "godmode" => params["godmode"],
  793. "reading_user" => reading_user
  794. })
  795. fetch_activities(recipients, params, :offset)
  796. |> Enum.reverse()
  797. end
  798. defp user_activities_recipients(%{"godmode" => true}) do
  799. []
  800. end
  801. defp user_activities_recipients(%{"reading_user" => reading_user}) do
  802. if reading_user do
  803. [Constants.as_public()] ++ [reading_user.ap_id | User.following(reading_user)]
  804. else
  805. [Constants.as_public()]
  806. end
  807. end
  808. defp restrict_since(query, %{"since_id" => ""}), do: query
  809. defp restrict_since(query, %{"since_id" => since_id}) do
  810. from(activity in query, where: activity.id > ^since_id)
  811. end
  812. defp restrict_since(query, _), do: query
  813. defp restrict_tag_reject(_query, %{"tag_reject" => _tag_reject, "skip_preload" => true}) do
  814. raise "Can't use the child object without preloading!"
  815. end
  816. defp restrict_tag_reject(query, %{"tag_reject" => tag_reject})
  817. when is_list(tag_reject) and tag_reject != [] do
  818. from(
  819. [_activity, object] in query,
  820. where: fragment("not (?)->'tag' \\?| (?)", object.data, ^tag_reject)
  821. )
  822. end
  823. defp restrict_tag_reject(query, _), do: query
  824. defp restrict_tag_all(_query, %{"tag_all" => _tag_all, "skip_preload" => true}) do
  825. raise "Can't use the child object without preloading!"
  826. end
  827. defp restrict_tag_all(query, %{"tag_all" => tag_all})
  828. when is_list(tag_all) and tag_all != [] do
  829. from(
  830. [_activity, object] in query,
  831. where: fragment("(?)->'tag' \\?& (?)", object.data, ^tag_all)
  832. )
  833. end
  834. defp restrict_tag_all(query, _), do: query
  835. defp restrict_tag(_query, %{"tag" => _tag, "skip_preload" => true}) do
  836. raise "Can't use the child object without preloading!"
  837. end
  838. defp restrict_tag(query, %{"tag" => tag}) when is_list(tag) do
  839. from(
  840. [_activity, object] in query,
  841. where: fragment("(?)->'tag' \\?| (?)", object.data, ^tag)
  842. )
  843. end
  844. defp restrict_tag(query, %{"tag" => tag}) when is_binary(tag) do
  845. from(
  846. [_activity, object] in query,
  847. where: fragment("(?)->'tag' \\? (?)", object.data, ^tag)
  848. )
  849. end
  850. defp restrict_tag(query, _), do: query
  851. defp restrict_recipients(query, [], _user), do: query
  852. defp restrict_recipients(query, recipients, nil) do
  853. from(activity in query, where: fragment("? && ?", ^recipients, activity.recipients))
  854. end
  855. defp restrict_recipients(query, recipients, user) do
  856. from(
  857. activity in query,
  858. where: fragment("? && ?", ^recipients, activity.recipients),
  859. or_where: activity.actor == ^user.ap_id
  860. )
  861. end
  862. defp restrict_local(query, %{"local_only" => true}) do
  863. from(activity in query, where: activity.local == true)
  864. end
  865. defp restrict_local(query, _), do: query
  866. defp restrict_actor(query, %{"actor_id" => actor_id}) do
  867. from(activity in query, where: activity.actor == ^actor_id)
  868. end
  869. defp restrict_actor(query, _), do: query
  870. defp restrict_type(query, %{"type" => type}) when is_binary(type) do
  871. from(activity in query, where: fragment("?->>'type' = ?", activity.data, ^type))
  872. end
  873. defp restrict_type(query, %{"type" => type}) do
  874. from(activity in query, where: fragment("?->>'type' = ANY(?)", activity.data, ^type))
  875. end
  876. defp restrict_type(query, _), do: query
  877. defp restrict_state(query, %{"state" => state}) do
  878. from(activity in query, where: fragment("?->>'state' = ?", activity.data, ^state))
  879. end
  880. defp restrict_state(query, _), do: query
  881. defp restrict_favorited_by(query, %{"favorited_by" => ap_id}) do
  882. from(
  883. [_activity, object] in query,
  884. where: fragment("(?)->'likes' \\? (?)", object.data, ^ap_id)
  885. )
  886. end
  887. defp restrict_favorited_by(query, _), do: query
  888. defp restrict_media(_query, %{"only_media" => _val, "skip_preload" => true}) do
  889. raise "Can't use the child object without preloading!"
  890. end
  891. defp restrict_media(query, %{"only_media" => val}) when val == "true" or val == "1" do
  892. from(
  893. [_activity, object] in query,
  894. where: fragment("not (?)->'attachment' = (?)", object.data, ^[])
  895. )
  896. end
  897. defp restrict_media(query, _), do: query
  898. defp restrict_replies(query, %{"exclude_replies" => val}) when val == "true" or val == "1" do
  899. from(
  900. [_activity, object] in query,
  901. where: fragment("?->>'inReplyTo' is null", object.data)
  902. )
  903. end
  904. defp restrict_replies(query, _), do: query
  905. defp restrict_reblogs(query, %{"exclude_reblogs" => val}) when val == "true" or val == "1" do
  906. from(activity in query, where: fragment("?->>'type' != 'Announce'", activity.data))
  907. end
  908. defp restrict_reblogs(query, _), do: query
  909. defp restrict_muted(query, %{"with_muted" => val}) when val in [true, "true", "1"], do: query
  910. defp restrict_muted(query, %{"muting_user" => %User{} = user} = opts) do
  911. mutes = opts["muted_users_ap_ids"] || User.muted_users_ap_ids(user)
  912. query =
  913. from([activity] in query,
  914. where: fragment("not (? = ANY(?))", activity.actor, ^mutes),
  915. where: fragment("not (?->'to' \\?| ?)", activity.data, ^mutes)
  916. )
  917. unless opts["skip_preload"] do
  918. from([thread_mute: tm] in query, where: is_nil(tm.user_id))
  919. else
  920. query
  921. end
  922. end
  923. defp restrict_muted(query, _), do: query
  924. defp restrict_blocked(query, %{"blocking_user" => %User{} = user} = opts) do
  925. blocked_ap_ids = opts["blocked_users_ap_ids"] || User.blocked_users_ap_ids(user)
  926. domain_blocks = user.domain_blocks || []
  927. following_ap_ids = User.get_friends_ap_ids(user)
  928. query =
  929. if has_named_binding?(query, :object), do: query, else: Activity.with_joined_object(query)
  930. from(
  931. [activity, object: o] in query,
  932. where: fragment("not (? = ANY(?))", activity.actor, ^blocked_ap_ids),
  933. where: fragment("not (? && ?)", activity.recipients, ^blocked_ap_ids),
  934. where:
  935. fragment(
  936. "not (?->>'type' = 'Announce' and ?->'to' \\?| ?)",
  937. activity.data,
  938. activity.data,
  939. ^blocked_ap_ids
  940. ),
  941. where:
  942. fragment(
  943. "(not (split_part(?, '/', 3) = ANY(?))) or ? = ANY(?)",
  944. activity.actor,
  945. ^domain_blocks,
  946. activity.actor,
  947. ^following_ap_ids
  948. ),
  949. where:
  950. fragment(
  951. "(not (split_part(?->>'actor', '/', 3) = ANY(?))) or (?->>'actor') = ANY(?)",
  952. o.data,
  953. ^domain_blocks,
  954. o.data,
  955. ^following_ap_ids
  956. )
  957. )
  958. end
  959. defp restrict_blocked(query, _), do: query
  960. defp restrict_unlisted(query) do
  961. from(
  962. activity in query,
  963. where:
  964. fragment(
  965. "not (coalesce(?->'cc', '{}'::jsonb) \\?| ?)",
  966. activity.data,
  967. ^[Constants.as_public()]
  968. )
  969. )
  970. end
  971. defp restrict_pinned(query, %{"pinned" => "true", "pinned_activity_ids" => ids}) do
  972. from(activity in query, where: activity.id in ^ids)
  973. end
  974. defp restrict_pinned(query, _), do: query
  975. defp restrict_muted_reblogs(query, %{"muting_user" => %User{} = user} = opts) do
  976. muted_reblogs = opts["reblog_muted_users_ap_ids"] || User.reblog_muted_users_ap_ids(user)
  977. from(
  978. activity in query,
  979. where:
  980. fragment(
  981. "not ( ?->>'type' = 'Announce' and ? = ANY(?))",
  982. activity.data,
  983. activity.actor,
  984. ^muted_reblogs
  985. )
  986. )
  987. end
  988. defp restrict_muted_reblogs(query, _), do: query
  989. defp restrict_instance(query, %{"instance" => instance}) do
  990. users =
  991. from(
  992. u in User,
  993. select: u.ap_id,
  994. where: fragment("? LIKE ?", u.nickname, ^"%@#{instance}")
  995. )
  996. |> Repo.all()
  997. from(activity in query, where: activity.actor in ^users)
  998. end
  999. defp restrict_instance(query, _), do: query
  1000. defp exclude_poll_votes(query, %{"include_poll_votes" => true}), do: query
  1001. defp exclude_poll_votes(query, _) do
  1002. if has_named_binding?(query, :object) do
  1003. from([activity, object: o] in query,
  1004. where: fragment("not(?->>'type' = ?)", o.data, "Answer")
  1005. )
  1006. else
  1007. query
  1008. end
  1009. end
  1010. defp exclude_id(query, %{"exclude_id" => id}) when is_binary(id) do
  1011. from(activity in query, where: activity.id != ^id)
  1012. end
  1013. defp exclude_id(query, _), do: query
  1014. defp maybe_preload_objects(query, %{"skip_preload" => true}), do: query
  1015. defp maybe_preload_objects(query, _) do
  1016. query
  1017. |> Activity.with_preloaded_object()
  1018. end
  1019. defp maybe_preload_bookmarks(query, %{"skip_preload" => true}), do: query
  1020. defp maybe_preload_bookmarks(query, opts) do
  1021. query
  1022. |> Activity.with_preloaded_bookmark(opts["user"])
  1023. end
  1024. defp maybe_preload_report_notes(query, %{"preload_report_notes" => true}) do
  1025. query
  1026. |> Activity.with_preloaded_report_notes()
  1027. end
  1028. defp maybe_preload_report_notes(query, _), do: query
  1029. defp maybe_set_thread_muted_field(query, %{"skip_preload" => true}), do: query
  1030. defp maybe_set_thread_muted_field(query, opts) do
  1031. query
  1032. |> Activity.with_set_thread_muted_field(opts["muting_user"] || opts["user"])
  1033. end
  1034. defp maybe_order(query, %{order: :desc}) do
  1035. query
  1036. |> order_by(desc: :id)
  1037. end
  1038. defp maybe_order(query, %{order: :asc}) do
  1039. query
  1040. |> order_by(asc: :id)
  1041. end
  1042. defp maybe_order(query, _), do: query
  1043. defp fetch_activities_query_ap_ids_ops(opts) do
  1044. source_user = opts["muting_user"]
  1045. ap_id_relations = if source_user, do: [:mute, :reblog_mute], else: []
  1046. ap_id_relations =
  1047. ap_id_relations ++
  1048. if opts["blocking_user"] && opts["blocking_user"] == source_user do
  1049. [:block]
  1050. else
  1051. []
  1052. end
  1053. preloaded_ap_ids = User.outgoing_relations_ap_ids(source_user, ap_id_relations)
  1054. restrict_blocked_opts = Map.merge(%{"blocked_users_ap_ids" => preloaded_ap_ids[:block]}, opts)
  1055. restrict_muted_opts = Map.merge(%{"muted_users_ap_ids" => preloaded_ap_ids[:mute]}, opts)
  1056. restrict_muted_reblogs_opts =
  1057. Map.merge(%{"reblog_muted_users_ap_ids" => preloaded_ap_ids[:reblog_mute]}, opts)
  1058. {restrict_blocked_opts, restrict_muted_opts, restrict_muted_reblogs_opts}
  1059. end
  1060. def fetch_activities_query(recipients, opts \\ %{}) do
  1061. {restrict_blocked_opts, restrict_muted_opts, restrict_muted_reblogs_opts} =
  1062. fetch_activities_query_ap_ids_ops(opts)
  1063. config = %{
  1064. skip_thread_containment: Config.get([:instance, :skip_thread_containment])
  1065. }
  1066. Activity
  1067. |> maybe_preload_objects(opts)
  1068. |> maybe_preload_bookmarks(opts)
  1069. |> maybe_preload_report_notes(opts)
  1070. |> maybe_set_thread_muted_field(opts)
  1071. |> maybe_order(opts)
  1072. |> restrict_recipients(recipients, opts["user"])
  1073. |> restrict_tag(opts)
  1074. |> restrict_tag_reject(opts)
  1075. |> restrict_tag_all(opts)
  1076. |> restrict_since(opts)
  1077. |> restrict_local(opts)
  1078. |> restrict_actor(opts)
  1079. |> restrict_type(opts)
  1080. |> restrict_state(opts)
  1081. |> restrict_favorited_by(opts)
  1082. |> restrict_blocked(restrict_blocked_opts)
  1083. |> restrict_muted(restrict_muted_opts)
  1084. |> restrict_media(opts)
  1085. |> restrict_visibility(opts)
  1086. |> restrict_thread_visibility(opts, config)
  1087. |> restrict_replies(opts)
  1088. |> restrict_reblogs(opts)
  1089. |> restrict_pinned(opts)
  1090. |> restrict_muted_reblogs(restrict_muted_reblogs_opts)
  1091. |> restrict_instance(opts)
  1092. |> Activity.restrict_deactivated_users()
  1093. |> exclude_poll_votes(opts)
  1094. |> exclude_visibility(opts)
  1095. end
  1096. def fetch_activities(recipients, opts \\ %{}, pagination \\ :keyset) do
  1097. list_memberships = Pleroma.List.memberships(opts["user"])
  1098. fetch_activities_query(recipients ++ list_memberships, opts)
  1099. |> Pagination.fetch_paginated(opts, pagination)
  1100. |> Enum.reverse()
  1101. |> maybe_update_cc(list_memberships, opts["user"])
  1102. end
  1103. @doc """
  1104. Fetch favorites activities of user with order by sort adds to favorites
  1105. """
  1106. @spec fetch_favourites(User.t(), map(), Pagination.type()) :: list(Activity.t())
  1107. def fetch_favourites(user, params \\ %{}, pagination \\ :keyset) do
  1108. user.ap_id
  1109. |> Activity.Queries.by_actor()
  1110. |> Activity.Queries.by_type("Like")
  1111. |> Activity.with_joined_object()
  1112. |> Object.with_joined_activity()
  1113. |> select([_like, object, activity], %{activity | object: object})
  1114. |> order_by([like, _, _], desc: like.id)
  1115. |> Pagination.fetch_paginated(
  1116. Map.merge(params, %{"skip_order" => true}),
  1117. pagination,
  1118. :object_activity
  1119. )
  1120. end
  1121. defp maybe_update_cc(activities, list_memberships, %User{ap_id: user_ap_id})
  1122. when is_list(list_memberships) and length(list_memberships) > 0 do
  1123. Enum.map(activities, fn
  1124. %{data: %{"bcc" => bcc}} = activity when is_list(bcc) and length(bcc) > 0 ->
  1125. if Enum.any?(bcc, &(&1 in list_memberships)) do
  1126. update_in(activity.data["cc"], &[user_ap_id | &1])
  1127. else
  1128. activity
  1129. end
  1130. activity ->
  1131. activity
  1132. end)
  1133. end
  1134. defp maybe_update_cc(activities, _, _), do: activities
  1135. def fetch_activities_bounded_query(query, recipients, recipients_with_public) do
  1136. from(activity in query,
  1137. where:
  1138. fragment("? && ?", activity.recipients, ^recipients) or
  1139. (fragment("? && ?", activity.recipients, ^recipients_with_public) and
  1140. ^Constants.as_public() in activity.recipients)
  1141. )
  1142. end
  1143. def fetch_activities_bounded(
  1144. recipients,
  1145. recipients_with_public,
  1146. opts \\ %{},
  1147. pagination \\ :keyset
  1148. ) do
  1149. fetch_activities_query([], opts)
  1150. |> fetch_activities_bounded_query(recipients, recipients_with_public)
  1151. |> Pagination.fetch_paginated(opts, pagination)
  1152. |> Enum.reverse()
  1153. end
  1154. @spec upload(Upload.source(), keyword()) :: {:ok, Object.t()} | {:error, any()}
  1155. def upload(file, opts \\ []) do
  1156. with {:ok, data} <- Upload.store(file, opts) do
  1157. obj_data =
  1158. if opts[:actor] do
  1159. Map.put(data, "actor", opts[:actor])
  1160. else
  1161. data
  1162. end
  1163. Repo.insert(%Object{data: obj_data})
  1164. end
  1165. end
  1166. defp object_to_user_data(data) do
  1167. avatar =
  1168. data["icon"]["url"] &&
  1169. %{
  1170. "type" => "Image",
  1171. "url" => [%{"href" => data["icon"]["url"]}]
  1172. }
  1173. banner =
  1174. data["image"]["url"] &&
  1175. %{
  1176. "type" => "Image",
  1177. "url" => [%{"href" => data["image"]["url"]}]
  1178. }
  1179. fields =
  1180. data
  1181. |> Map.get("attachment", [])
  1182. |> Enum.filter(fn %{"type" => t} -> t == "PropertyValue" end)
  1183. |> Enum.map(fn fields -> Map.take(fields, ["name", "value"]) end)
  1184. locked = data["manuallyApprovesFollowers"] || false
  1185. data = Transmogrifier.maybe_fix_user_object(data)
  1186. discoverable = data["discoverable"] || false
  1187. invisible = data["invisible"] || false
  1188. actor_type = data["type"] || "Person"
  1189. user_data = %{
  1190. ap_id: data["id"],
  1191. ap_enabled: true,
  1192. source_data: data,
  1193. banner: banner,
  1194. fields: fields,
  1195. locked: locked,
  1196. discoverable: discoverable,
  1197. invisible: invisible,
  1198. avatar: avatar,
  1199. name: data["name"],
  1200. follower_address: data["followers"],
  1201. following_address: data["following"],
  1202. bio: data["summary"],
  1203. actor_type: actor_type,
  1204. also_known_as: Map.get(data, "alsoKnownAs", [])
  1205. }
  1206. # nickname can be nil because of virtual actors
  1207. user_data =
  1208. if data["preferredUsername"] do
  1209. Map.put(
  1210. user_data,
  1211. :nickname,
  1212. "#{data["preferredUsername"]}@#{URI.parse(data["id"]).host}"
  1213. )
  1214. else
  1215. Map.put(user_data, :nickname, nil)
  1216. end
  1217. {:ok, user_data}
  1218. end
  1219. def fetch_follow_information_for_user(user) do
  1220. with {:ok, following_data} <-
  1221. Fetcher.fetch_and_contain_remote_object_from_id(user.following_address),
  1222. {:ok, hide_follows} <- collection_private(following_data),
  1223. {:ok, followers_data} <-
  1224. Fetcher.fetch_and_contain_remote_object_from_id(user.follower_address),
  1225. {:ok, hide_followers} <- collection_private(followers_data) do
  1226. {:ok,
  1227. %{
  1228. hide_follows: hide_follows,
  1229. follower_count: normalize_counter(followers_data["totalItems"]),
  1230. following_count: normalize_counter(following_data["totalItems"]),
  1231. hide_followers: hide_followers
  1232. }}
  1233. else
  1234. {:error, _} = e -> e
  1235. e -> {:error, e}
  1236. end
  1237. end
  1238. defp normalize_counter(counter) when is_integer(counter), do: counter
  1239. defp normalize_counter(_), do: 0
  1240. defp maybe_update_follow_information(data) do
  1241. with {:enabled, true} <- {:enabled, Config.get([:instance, :external_user_synchronization])},
  1242. {:ok, info} <- fetch_follow_information_for_user(data) do
  1243. info = Map.merge(data[:info] || %{}, info)
  1244. Map.put(data, :info, info)
  1245. else
  1246. {:enabled, false} ->
  1247. data
  1248. e ->
  1249. Logger.error(
  1250. "Follower/Following counter update for #{data.ap_id} failed.\n" <> inspect(e)
  1251. )
  1252. data
  1253. end
  1254. end
  1255. defp collection_private(%{"first" => %{"type" => type}})
  1256. when type in ["CollectionPage", "OrderedCollectionPage"],
  1257. do: {:ok, false}
  1258. defp collection_private(%{"first" => first}) do
  1259. with {:ok, %{"type" => type}} when type in ["CollectionPage", "OrderedCollectionPage"] <-
  1260. Fetcher.fetch_and_contain_remote_object_from_id(first) do
  1261. {:ok, false}
  1262. else
  1263. {:error, {:ok, %{status: code}}} when code in [401, 403] -> {:ok, true}
  1264. {:error, _} = e -> e
  1265. e -> {:error, e}
  1266. end
  1267. end
  1268. defp collection_private(_data), do: {:ok, true}
  1269. def user_data_from_user_object(data) do
  1270. with {:ok, data} <- MRF.filter(data),
  1271. {:ok, data} <- object_to_user_data(data) do
  1272. {:ok, data}
  1273. else
  1274. e -> {:error, e}
  1275. end
  1276. end
  1277. def fetch_and_prepare_user_from_ap_id(ap_id) do
  1278. with {:ok, data} <- Fetcher.fetch_and_contain_remote_object_from_id(ap_id),
  1279. {:ok, data} <- user_data_from_user_object(data),
  1280. data <- maybe_update_follow_information(data) do
  1281. {:ok, data}
  1282. else
  1283. {:error, "Object has been deleted"} = e ->
  1284. Logger.debug("Could not decode user at fetch #{ap_id}, #{inspect(e)}")
  1285. {:error, e}
  1286. e ->
  1287. Logger.error("Could not decode user at fetch #{ap_id}, #{inspect(e)}")
  1288. {:error, e}
  1289. end
  1290. end
  1291. def make_user_from_ap_id(ap_id) do
  1292. if _user = User.get_cached_by_ap_id(ap_id) do
  1293. Transmogrifier.upgrade_user_from_ap_id(ap_id)
  1294. else
  1295. with {:ok, data} <- fetch_and_prepare_user_from_ap_id(ap_id) do
  1296. User.insert_or_update_user(data)
  1297. else
  1298. e -> {:error, e}
  1299. end
  1300. end
  1301. end
  1302. def make_user_from_nickname(nickname) do
  1303. with {:ok, %{"ap_id" => ap_id}} when not is_nil(ap_id) <- WebFinger.finger(nickname) do
  1304. make_user_from_ap_id(ap_id)
  1305. else
  1306. _e -> {:error, "No AP id in WebFinger"}
  1307. end
  1308. end
  1309. # filter out broken threads
  1310. def contain_broken_threads(%Activity{} = activity, %User{} = user) do
  1311. entire_thread_visible_for_user?(activity, user)
  1312. end
  1313. # do post-processing on a specific activity
  1314. def contain_activity(%Activity{} = activity, %User{} = user) do
  1315. contain_broken_threads(activity, user)
  1316. end
  1317. def fetch_direct_messages_query do
  1318. Activity
  1319. |> restrict_type(%{"type" => "Create"})
  1320. |> restrict_visibility(%{visibility: "direct"})
  1321. |> order_by([activity], asc: activity.id)
  1322. end
  1323. end