support for idna domains
[akkoma] / lib / pleroma / user / synchronization.ex
1 # Pleroma: A lightweight social networking server
2 # Copyright © 2017-2018 Pleroma Authors <https://pleroma.social/>
3 # SPDX-License-Identifier: AGPL-3.0-only
4
5 defmodule Pleroma.User.Synchronization do
6 alias Pleroma.HTTP
7 alias Pleroma.User
8
9 @spec call([User.t()], map(), keyword()) :: {User.t(), map()}
10 def call(users, errors, opts \\ []) do
11 do_call(users, errors, opts)
12 end
13
14 defp do_call([user | []], errors, opts) do
15 updated = fetch_counters(user, errors, opts)
16 {user, updated}
17 end
18
19 defp do_call([user | others], errors, opts) do
20 updated = fetch_counters(user, errors, opts)
21 do_call(others, updated, opts)
22 end
23
24 defp fetch_counters(user, errors, opts) do
25 %{host: host} = URI.parse(user.ap_id)
26
27 info = %{}
28 {following, errors} = fetch_counter(user.ap_id <> "/following", host, errors, opts)
29 info = if following, do: Map.put(info, :following_count, following), else: info
30
31 {followers, errors} = fetch_counter(user.ap_id <> "/followers", host, errors, opts)
32 info = if followers, do: Map.put(info, :follower_count, followers), else: info
33
34 User.set_info_cache(user, info)
35 errors
36 end
37
38 defp available_domain?(domain, errors, opts) do
39 max_retries = Keyword.get(opts, :max_retries, 3)
40 not (Map.has_key?(errors, domain) && errors[domain] >= max_retries)
41 end
42
43 defp fetch_counter(url, host, errors, opts) do
44 with true <- available_domain?(host, errors, opts),
45 {:ok, %{body: body, status: code}} when code in 200..299 <-
46 HTTP.get(
47 url,
48 [{:Accept, "application/activity+json"}]
49 ),
50 {:ok, data} <- Jason.decode(body) do
51 {data["totalItems"], errors}
52 else
53 false ->
54 {nil, errors}
55
56 _ ->
57 {nil, Map.update(errors, host, 1, &(&1 + 1))}
58 end
59 end
60 end