Add a command to refresh a single actor or all actors
Signed-off-by: Thomas Citharel <tcit@tcit.fr>
This commit is contained in:
parent
23a5ffd3a1
commit
c61a54d802
98
lib/mix/tasks/mobilizon/actors/refresh.ex
Normal file
98
lib/mix/tasks/mobilizon/actors/refresh.ex
Normal file
|
@ -0,0 +1,98 @@
|
||||||
|
defmodule Mix.Tasks.Mobilizon.Actors.Refresh do
|
||||||
|
@moduledoc """
|
||||||
|
Task to display an actor details
|
||||||
|
"""
|
||||||
|
use Mix.Task
|
||||||
|
alias Mobilizon.Actors.Actor
|
||||||
|
alias Mobilizon.Federation.ActivityPub
|
||||||
|
alias Mobilizon.Storage.Repo
|
||||||
|
import Ecto.Query
|
||||||
|
require Logger
|
||||||
|
|
||||||
|
@shortdoc "Refresh an actor or all actors"
|
||||||
|
|
||||||
|
@impl Mix.Task
|
||||||
|
def run(["--all" | options]) do
|
||||||
|
{options, [], []} =
|
||||||
|
OptionParser.parse(
|
||||||
|
options,
|
||||||
|
strict: [
|
||||||
|
verbose: :boolean
|
||||||
|
],
|
||||||
|
aliases: [
|
||||||
|
v: :verbose
|
||||||
|
]
|
||||||
|
)
|
||||||
|
|
||||||
|
verbose = Keyword.get(options, :verbose, false)
|
||||||
|
|
||||||
|
Mix.Task.run("app.start")
|
||||||
|
|
||||||
|
total = count_actors()
|
||||||
|
|
||||||
|
Mix.shell().info("""
|
||||||
|
#{total} actors to process
|
||||||
|
""")
|
||||||
|
|
||||||
|
query = from(a in Actor, where: not is_nil(a.domain))
|
||||||
|
|
||||||
|
{:ok, _res} =
|
||||||
|
Repo.transaction(
|
||||||
|
fn ->
|
||||||
|
query
|
||||||
|
|> Repo.stream(timeout: :infinity)
|
||||||
|
|> Stream.map(&"#{&1.preferred_username}@#{&1.domain}")
|
||||||
|
|> Stream.each(
|
||||||
|
if verbose,
|
||||||
|
do: &Logger.info("Processing #{inspect(&1)}"),
|
||||||
|
else: &Logger.debug("Processing #{inspect(&1)}")
|
||||||
|
)
|
||||||
|
|> Stream.map(fn username -> make_actor(username, verbose) end)
|
||||||
|
|> Stream.scan(0, fn _, acc -> acc + 1 end)
|
||||||
|
|> Stream.each(fn index ->
|
||||||
|
if verbose,
|
||||||
|
do: Logger.info("#{index}/#{total}"),
|
||||||
|
else: ProgressBar.render(index, total)
|
||||||
|
end)
|
||||||
|
|> Stream.run()
|
||||||
|
end,
|
||||||
|
timeout: :infinity
|
||||||
|
)
|
||||||
|
end
|
||||||
|
|
||||||
|
@impl Mix.Task
|
||||||
|
def run([preferred_username]) do
|
||||||
|
Mix.Task.run("app.start")
|
||||||
|
|
||||||
|
case ActivityPub.make_actor_from_nickname(preferred_username) do
|
||||||
|
{:ok, %Actor{}} ->
|
||||||
|
Mix.shell().info("""
|
||||||
|
Actor #{preferred_username} refreshed
|
||||||
|
""")
|
||||||
|
|
||||||
|
{:actor, nil} ->
|
||||||
|
Mix.raise("Error: No such actor")
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
@impl Mix.Task
|
||||||
|
def run(_) do
|
||||||
|
Mix.raise("mobilizon.actors.refresh requires an username as argument or --all as an option")
|
||||||
|
end
|
||||||
|
|
||||||
|
@spec make_actor(String.t(), boolean()) :: any()
|
||||||
|
defp make_actor(username, verbose) do
|
||||||
|
ActivityPub.make_actor_from_nickname(username)
|
||||||
|
rescue
|
||||||
|
_ ->
|
||||||
|
if verbose do
|
||||||
|
Logger.warn("Failed to refresh #{username}")
|
||||||
|
end
|
||||||
|
|
||||||
|
nil
|
||||||
|
end
|
||||||
|
|
||||||
|
defp count_actors do
|
||||||
|
Repo.aggregate(from(a in Actor, where: not is_nil(a.domain)), :count)
|
||||||
|
end
|
||||||
|
end
|
|
@ -210,12 +210,23 @@ defmodule Mobilizon.Actors do
|
||||||
Conflicts on actor's URL/AP ID, replaces keys, avatar and banner, name and summary.
|
Conflicts on actor's URL/AP ID, replaces keys, avatar and banner, name and summary.
|
||||||
"""
|
"""
|
||||||
@spec upsert_actor(map, boolean) :: {:ok, Actor.t()} | {:error, Ecto.Changeset.t()}
|
@spec upsert_actor(map, boolean) :: {:ok, Actor.t()} | {:error, Ecto.Changeset.t()}
|
||||||
def upsert_actor(%{keys: keys, name: name, summary: summary} = data, preload \\ false) do
|
def upsert_actor(
|
||||||
|
%{keys: keys, name: name, summary: summary, avatar: avatar, banner: banner} = data,
|
||||||
|
preload \\ false
|
||||||
|
) do
|
||||||
insert =
|
insert =
|
||||||
data
|
data
|
||||||
|> Actor.remote_actor_creation_changeset()
|
|> Actor.remote_actor_creation_changeset()
|
||||||
|> Repo.insert(
|
|> Repo.insert(
|
||||||
on_conflict: [set: [keys: keys, name: name, summary: summary]],
|
on_conflict: [
|
||||||
|
set: [
|
||||||
|
keys: keys,
|
||||||
|
name: name,
|
||||||
|
summary: summary,
|
||||||
|
avatar: transform_media_file(avatar),
|
||||||
|
banner: transform_media_file(banner)
|
||||||
|
]
|
||||||
|
],
|
||||||
conflict_target: [:url]
|
conflict_target: [:url]
|
||||||
)
|
)
|
||||||
|
|
||||||
|
@ -232,6 +243,18 @@ defmodule Mobilizon.Actors do
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
defp transform_media_file(nil), do: nil
|
||||||
|
|
||||||
|
defp transform_media_file(file) do
|
||||||
|
file = for({key, val} <- file, into: %{}, do: {String.to_atom(key), val})
|
||||||
|
|
||||||
|
if is_nil(file) do
|
||||||
|
nil
|
||||||
|
else
|
||||||
|
struct(Mobilizon.Media.File, file)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
def delete_actor(%Actor{} = actor) do
|
def delete_actor(%Actor{} = actor) do
|
||||||
Workers.Background.enqueue("delete_actor", %{"actor_id" => actor.id})
|
Workers.Background.enqueue("delete_actor", %{"actor_id" => actor.id})
|
||||||
end
|
end
|
||||||
|
|
Loading…
Reference in a new issue