alias Comeonin.Pbkdf2
alias Pleroma.Activity
+ alias Pleroma.Bookmark
alias Pleroma.Formatter
alias Pleroma.Notification
alias Pleroma.Object
field(:search_rank, :float, virtual: true)
field(:search_type, :integer, virtual: true)
field(:tags, {:array, :string}, default: [])
- field(:bookmarks, {:array, :string}, default: [])
field(:last_refreshed_at, :naive_datetime_usec)
+ field(:last_digest_emailed_at, :naive_datetime)
+ has_many(:bookmarks, Bookmark)
has_many(:notifications, Notification)
has_many(:registrations, Registration)
embeds_one(:info, Pleroma.User.Info)
def register(%Ecto.Changeset{} = changeset) do
with {:ok, user} <- Repo.insert(changeset),
{:ok, user} <- autofollow_users(user),
+ {:ok, user} <- set_cache(user),
{:ok, _} <- Pleroma.User.WelcomeMessage.post_welcome_message_to_user(user),
{:ok, _} <- try_send_confirmation_email(user) do
{:ok, user}
user
|> Pleroma.Emails.UserEmail.account_confirmation_email()
|> Pleroma.Emails.Mailer.deliver_async()
+
+ {:ok, :enqueued}
else
{:ok, :noop}
end
name = List.last(String.split(ap_id, "/"))
nickname = "#{name}@#{domain}"
- get_by_nickname(nickname)
+ get_cached_by_nickname(nickname)
end
- def set_cache(user) do
+ def set_cache({:ok, user}), do: set_cache(user)
+ def set_cache({:error, err}), do: {:error, err}
+
+ def set_cache(%User{} = user) do
Cachex.put(:user_cache, "ap_id:#{user.ap_id}", user)
Cachex.put(:user_cache, "nickname:#{user.nickname}", user)
Cachex.put(:user_cache, "user_info:#{user.id}", user_info(user))
with [_nick, _domain] <- String.split(nickname, "@"),
{:ok, user} <- fetch_by_nickname(nickname) do
if Pleroma.Config.get([:fetch_initial_posts, :enabled]) do
+ # TODO turn into job
{:ok, _} = Task.start(__MODULE__, :fetch_initial_posts, [user])
end
# helper to handle the block given only an actor's AP id
def block(blocker, %{ap_id: ap_id}) do
- block(blocker, User.get_by_ap_id(ap_id))
+ block(blocker, get_cached_by_ap_id(ap_id))
end
def unblock(blocker, %{ap_id: ap_id}) do
end
def subscribed_to?(user, %{ap_id: ap_id}) do
- with %User{} = target <- User.get_by_ap_id(ap_id) do
+ with %User{} = target <- get_cached_by_ap_id(ap_id) do
Enum.member?(target.info.subscribers, user.ap_id)
end
end
end
def get_or_fetch_by_ap_id(ap_id) do
- user = get_by_ap_id(ap_id)
+ user = get_cached_by_ap_id(ap_id)
if !is_nil(user) and !User.needs_update?(user) do
user
def get_or_create_instance_user do
relay_uri = "#{Pleroma.Web.Endpoint.url()}/relay"
- if user = get_by_ap_id(relay_uri) do
+ if user = get_cached_by_ap_id(relay_uri) do
user
else
changes =
defp blank?(n), do: n
def insert_or_update_user(data) do
- data =
- data
- |> Map.put(:name, blank?(data[:name]) || data[:nickname])
-
- cs = User.remote_user_creation(data)
-
- Repo.insert(cs, on_conflict: :replace_all, conflict_target: :nickname)
+ data
+ |> Map.put(:name, blank?(data[:name]) || data[:nickname])
+ |> remote_user_creation()
+ |> Repo.insert(on_conflict: :replace_all, conflict_target: :nickname)
+ |> set_cache()
end
def ap_enabled?(%User{local: true}), do: true
# this is because we have synchronous follow APIs and need to simulate them
# with an async handshake
def wait_and_refresh(_, %User{local: true} = a, %User{local: true} = b) do
- with %User{} = a <- User.get_by_id(a.id),
- %User{} = b <- User.get_by_id(b.id) do
+ with %User{} = a <- User.get_cached_by_id(a.id),
+ %User{} = b <- User.get_cached_by_id(b.id) do
{:ok, a, b}
else
_e ->
def wait_and_refresh(timeout, %User{} = a, %User{} = b) do
with :ok <- :timer.sleep(timeout),
- %User{} = a <- User.get_by_id(a.id),
- %User{} = b <- User.get_by_id(b.id) do
+ %User{} = a <- User.get_cached_by_id(a.id),
+ %User{} = b <- User.get_cached_by_id(b.id) do
{:ok, a, b}
else
_e ->
end
def tag(nickname, tags) when is_binary(nickname),
- do: tag(User.get_by_nickname(nickname), tags)
+ do: tag(get_by_nickname(nickname), tags)
def tag(%User{} = user, tags),
do: update_tags(user, Enum.uniq((user.tags || []) ++ normalize_tags(tags)))
end
def untag(nickname, tags) when is_binary(nickname),
- do: untag(User.get_by_nickname(nickname), tags)
+ do: untag(get_by_nickname(nickname), tags)
def untag(%User{} = user, tags),
do: update_tags(user, (user.tags || []) -- normalize_tags(tags))
updated_user
end
- def bookmark(%User{} = user, status_id) do
- bookmarks = Enum.uniq(user.bookmarks ++ [status_id])
- update_bookmarks(user, bookmarks)
- end
-
- def unbookmark(%User{} = user, status_id) do
- bookmarks = Enum.uniq(user.bookmarks -- [status_id])
- update_bookmarks(user, bookmarks)
- end
-
- def update_bookmarks(%User{} = user, bookmarks) do
- user
- |> change(%{bookmarks: bookmarks})
- |> update_and_set_cache
- end
-
defp normalize_tags(tags) do
[tags]
|> List.flatten()
def showing_reblogs?(%User{} = user, %User{} = target) do
target.ap_id not in user.info.muted_reblogs
end
+
+ @doc """
+ The function returns a query to get users with no activity for given interval of days.
+ Inactive users are those who didn't read any notification, or had any activity where
+ the user is the activity's actor, during `inactivity_threshold` days.
+ Deactivated users will not appear in this list.
+
+ ## Examples
+
+ iex> Pleroma.User.list_inactive_users()
+ %Ecto.Query{}
+ """
+ @spec list_inactive_users_query(integer()) :: Ecto.Query.t()
+ def list_inactive_users_query(inactivity_threshold \\ 7) do
+ negative_inactivity_threshold = -inactivity_threshold
+ now = NaiveDateTime.truncate(NaiveDateTime.utc_now(), :second)
+ # Subqueries are not supported in `where` clauses, join gets too complicated.
+ has_read_notifications =
+ from(n in Pleroma.Notification,
+ where: n.seen == true,
+ group_by: n.id,
+ having: max(n.updated_at) > datetime_add(^now, ^negative_inactivity_threshold, "day"),
+ select: n.user_id
+ )
+ |> Pleroma.Repo.all()
+
+ from(u in Pleroma.User,
+ left_join: a in Pleroma.Activity,
+ on: u.ap_id == a.actor,
+ where: not is_nil(u.nickname),
+ where: fragment("not (?->'deactivated' @> 'true')", u.info),
+ where: u.id not in ^has_read_notifications,
+ group_by: u.id,
+ having:
+ max(a.inserted_at) < datetime_add(^now, ^negative_inactivity_threshold, "day") or
+ is_nil(max(a.inserted_at))
+ )
+ end
+
+ @doc """
+ Enable or disable email notifications for user
+
+ ## Examples
+
+ iex> Pleroma.User.switch_email_notifications(Pleroma.User{info: %{email_notifications: %{"digest" => false}}}, "digest", true)
+ Pleroma.User{info: %{email_notifications: %{"digest" => true}}}
+
+ iex> Pleroma.User.switch_email_notifications(Pleroma.User{info: %{email_notifications: %{"digest" => true}}}, "digest", false)
+ Pleroma.User{info: %{email_notifications: %{"digest" => false}}}
+ """
+ @spec switch_email_notifications(t(), String.t(), boolean()) ::
+ {:ok, t()} | {:error, Ecto.Changeset.t()}
+ def switch_email_notifications(user, type, status) do
+ info = Pleroma.User.Info.update_email_notifications(user.info, %{type => status})
+
+ change(user)
+ |> put_embed(:info, info)
+ |> update_and_set_cache()
+ end
+
+ @doc """
+ Set `last_digest_emailed_at` value for the user to current time
+ """
+ @spec touch_last_digest_emailed_at(t()) :: t()
+ def touch_last_digest_emailed_at(user) do
+ now = NaiveDateTime.truncate(NaiveDateTime.utc_now(), :second)
+
+ {:ok, updated_user} =
+ user
+ |> change(%{last_digest_emailed_at: now})
+ |> update_and_set_cache()
+
+ updated_user
+ end
end