# Pleroma: A lightweight social networking server # Copyright © 2017-2023 Pleroma Authors # SPDX-License-Identifier: AGPL-3.0-only defmodule Pleroma.Repo.Migrations.FixBlockedFollows do use Ecto.Migration import Ecto.Query alias Pleroma.Config alias Pleroma.Repo def up do unfollow_blocked = Config.get([:activitypub, :unfollow_blocked]) if unfollow_blocked do "activities" |> where([activity], fragment("? ->> 'type' = 'Block'", activity.data)) |> distinct([activity], [ activity.actor, fragment( "coalesce((?)->'object'->>'id', (?)->>'object')", activity.data, activity.data ) ]) |> order_by([activity], [fragment("? desc nulls last", activity.id)]) |> select([activity], %{ blocker: activity.actor, blocked: fragment("coalesce((?)->'object'->>'id', (?)->>'object')", activity.data, activity.data), created_at: activity.id }) |> Repo.stream() |> Enum.map(&unfollow_if_blocked/1) |> Enum.uniq() |> Enum.each(&update_follower_count/1) end end def down do end def unfollow_if_blocked(%{blocker: blocker_id, blocked: blocked_id, created_at: blocked_at}) do query = from( activity in "activities", where: fragment("? ->> 'type' = 'Follow'", activity.data), where: activity.actor == ^blocked_id, # this is to use the index where: fragment( "coalesce((?)->'object'->>'id', (?)->>'object') = ?", activity.data, activity.data, ^blocker_id ), where: activity.id > ^blocked_at, where: fragment("(?)->>'state' = 'accept'", activity.data), order_by: [fragment("? desc nulls last", activity.id)] ) unless Repo.exists?(query) do blocker = "users" |> select([:id, :local]) |> Repo.get_by(ap_id: blocker_id) blocked = "users" |> select([:id]) |> Repo.get_by(ap_id: blocked_id) if !is_nil(blocker) && !is_nil(blocked) do unfollow(blocked, blocker) end end end def unfollow(%{id: follower_id}, %{id: followed_id} = followed) do following_relationship = "following_relationships" |> where(follower_id: ^follower_id, following_id: ^followed_id, state: "accept") |> select([:id]) |> Repo.one() case following_relationship do nil -> {:ok, nil} %{id: following_relationship_id} -> "following_relationships" |> where(id: ^following_relationship_id) |> Repo.delete_all() followed end end def update_follower_count(%{id: user_id} = user) do if user.local or !Pleroma.Config.get([:instance, :external_user_synchronization]) do follower_count_query = "users" |> where([u], u.id != ^user_id) |> where([u], u.deactivated != ^true) |> join(:inner, [u], r in "following_relationships", as: :relationships, on: r.following_id == ^user_id and r.follower_id == u.id ) |> where([relationships: r], r.state == "accept") |> select([u], %{count: count(u.id)}) "users" |> where(id: ^user_id) |> join(:inner, [u], s in subquery(follower_count_query)) |> update([u, s], set: [follower_count: s.count] ) |> Repo.update_all([]) end end def update_follower_count(_), do: :noop end