diff --git a/lib/core/posts.ex b/lib/core/posts.ex index 92b8f88..0c8a00e 100644 --- a/lib/core/posts.ex +++ b/lib/core/posts.ex @@ -1,10 +1,22 @@ defmodule Core.Posts do @moduledoc false + alias Core.Syndication alias Core.Posts.Post def get!(id) do - Core.Repo.get!(Schema.Post, id) + Post.Query.base() + |> Core.Repo.get!(id) + end + + def get(id) do + Post.Query.base() + |> Core.Repo.get(id) + end + + def get_published_post(id) do + Post.Query.published() + |> Core.Repo.get(id) end def get_published_blog!(%Date{} = publish_date, slug) when is_binary(slug) do @@ -70,29 +82,28 @@ defmodule Core.Posts do attrs |> change_post() |> Core.Repo.insert() + |> do_syndication() end def update_post(%Schema.Post{} = post, attrs) do post |> change_post(attrs) |> Core.Repo.update() + |> do_syndication() end def create_or_update_post(%Schema.Post{} = post, attrs) do post |> change_post(attrs) |> Core.Repo.insert_or_update() + |> do_syndication() end def publish_post(%Schema.Post{} = post, published_at \\ DateTime.utc_now()) do - with {:ok, post} <- - post - |> Post.publish_changeset(published_at) - |> Core.Repo.update(), - {:ok, post} <- Core.Syndication.syndicate_to_mastodon(post), - {:ok, post} <- Core.Syndication.syndicate_to_bluesky(post) do - {:ok, post} - end + post + |> Post.publish_changeset(published_at) + |> Core.Repo.update() + |> do_syndication() end def delete_post(%Schema.Post{} = post, deleted_at \\ DateTime.utc_now()) do @@ -105,11 +116,29 @@ defmodule Core.Posts do post |> Post.unpublish_changeset() |> Core.Repo.update() + |> do_syndication() end def undelete_post(%Schema.Post{} = post) do post |> Post.undelete_changeset() |> Core.Repo.update() + |> do_syndication() end + + defp do_syndication(%Schema.Post{} = post) do + %{"post_id" => post.id} + |> Syndication.SyndicatePostWorker.new() + |> Oban.insert() + + post + end + + defp do_syndication({:ok, %Schema.Post{} = post}) do + do_syndication(post) + + {:ok, post} + end + + defp do_syndication(other), do: other end diff --git a/lib/core/syndication.ex b/lib/core/syndication.ex index d5f1099..b1a94f9 100644 --- a/lib/core/syndication.ex +++ b/lib/core/syndication.ex @@ -20,25 +20,19 @@ defmodule Core.Syndication do end def syndicate_to_mastodon(%Schema.Post{} = post) do - post = Core.Repo.preload(post, [:mastodon_post]) + conn = build_mastodon_client_conn() - if post.syndicate_to_mastodon and is_nil(post.mastodon_post) do - conn = build_mastodon_client_conn() + {:ok, resp} = MastodonClient.post(conn, "/api/v1/statuses", %{status: post.body}) - {:ok, resp} = MastodonClient.post(conn, "/api/v1/statuses", %{status: post.body}) + post + |> Ecto.build_assoc(:mastodon_post) + |> Syndication.MastodonPost.changeset(%{ + status_id: resp.body["id"], + url: resp.body["url"] + }) + |> Core.Repo.insert() - post - |> Ecto.build_assoc(:mastodon_post) - |> Syndication.MastodonPost.changeset(%{ - status_id: resp.body["id"], - url: resp.body["url"] - }) - |> Core.Repo.insert() - - {:ok, post} - else - {:ok, post} - end + {:ok, post} end defp get_mastodon_access_token! do @@ -94,20 +88,14 @@ defmodule Core.Syndication do end def syndicate_to_bluesky(%Schema.Post{} = post) do - post = Core.Repo.preload(post, [:bluesky_post]) + {:ok, bluesky_account} = get_bluesky_account!() |> refresh_bluesky_account() - if post.syndicate_to_bluesky && is_nil(post.bluesky_post) do - {:ok, bluesky_account} = get_bluesky_account!() |> refresh_bluesky_account() + with {:ok, resp} <- Syndication.BlueskyClient.post_status(bluesky_account, post.body) do + post + |> Ecto.build_assoc(:bluesky_post) + |> Syndication.BlueskyPost.changeset(resp.body) + |> Core.Repo.insert() - with {:ok, resp} <- Syndication.BlueskyClient.post_status(bluesky_account, post.body) do - post - |> Ecto.build_assoc(:bluesky_post) - |> Syndication.BlueskyPost.changeset(resp.body) - |> Core.Repo.insert() - - {:ok, post} - end - else {:ok, post} end end diff --git a/lib/core/syndication/bluesky_refresh_worker.ex b/lib/core/syndication/bluesky_refresh_worker.ex index 3b09544..754805a 100644 --- a/lib/core/syndication/bluesky_refresh_worker.ex +++ b/lib/core/syndication/bluesky_refresh_worker.ex @@ -1,5 +1,5 @@ defmodule Core.Syndication.BlueskyRefreshWorker do - use Oban.Worker + use Oban.Worker, unique: true, replace: [scheduled: [:scheduled_at]] @impl true def perform(%Oban.Job{args: %{"bluesky_account_id" => id}}) do diff --git a/lib/core/syndication/syndicate_post_worker.ex b/lib/core/syndication/syndicate_post_worker.ex new file mode 100644 index 0000000..0770bd0 --- /dev/null +++ b/lib/core/syndication/syndicate_post_worker.ex @@ -0,0 +1,36 @@ +defmodule Core.Syndication.SyndicatePostWorker do + use Oban.Worker, unique: true + + alias Core.Posts + alias Core.Syndication.SyndicateToBlueskyWorker + alias Core.Syndication.SyndicateToMastodonWorker + + def perform(%Oban.Job{args: %{"post_id" => post_id}}) do + with {:ok, %Schema.Post{} = post} <- get_post(post_id) do + syndicate_worker_args = %{"post_id" => post_id} + + subjobs = + [ + if(post.syndicate_to_bluesky, do: SyndicateToBlueskyWorker.new(syndicate_worker_args)), + if(post.syndicate_to_mastodon, do: SyndicateToMastodonWorker.new(syndicate_worker_args)) + ] + |> Enum.reject(&is_nil/1) + |> Oban.insert_all() + + case subjobs do + [] -> {:cancel, "post not set to syndicate"} + _ -> :ok + end + end + end + + defp get_post(post_id) do + case Posts.get_published_post(post_id) do + %Schema.Post{} = post -> + {:ok, post} + + _ -> + {:cancel, "post does not exist or is not published"} + end + end +end diff --git a/lib/core/syndication/syndicate_to_bluesky_worker.ex b/lib/core/syndication/syndicate_to_bluesky_worker.ex new file mode 100644 index 0000000..7c1e2c7 --- /dev/null +++ b/lib/core/syndication/syndicate_to_bluesky_worker.ex @@ -0,0 +1,34 @@ +defmodule Core.Syndication.SyndicateToBlueskyWorker do + alias Core.Syndication + use Oban.Worker, unique: true + + alias Core.Posts + + def perform(%Oban.Job{args: %{"post_id" => post_id}}) do + with {:ok, post} <- get_post(post_id), + {:ok, _bluesky_post} <- Syndication.syndicate_to_bluesky(post) do + :ok + end + end + + defp get_post(post_id) do + case Posts.get_published_post(post_id) do + %Schema.Post{} = post -> + post = Core.Repo.preload(post, [:bluesky_post]) + + cond do + not post.syndicate_to_bluesky -> + {:cancel, "post is not marked for syndication to bluesky"} + + not is_nil(post.bluesky_post) -> + {:cancel, "post already syndicated to bluesky"} + + true -> + {:ok, post} + end + + _ -> + {:cancel, "post does not exist or is not published"} + end + end +end diff --git a/lib/core/syndication/syndicate_to_mastodon_worker.ex b/lib/core/syndication/syndicate_to_mastodon_worker.ex new file mode 100644 index 0000000..0c80140 --- /dev/null +++ b/lib/core/syndication/syndicate_to_mastodon_worker.ex @@ -0,0 +1,28 @@ +defmodule Core.Syndication.SyndicateToMastodonWorker do + alias Core.Syndication + use Oban.Worker, unique: true + + alias Core.Posts + alias Core.Syndication + + def perform(%Oban.Job{args: %{"post_id" => post_id}}) do + with {:ok, post} <- get_post(post_id), + {:ok, _mastodon_post} <- Syndication.syndicate_to_mastodon(post) do + :ok + end + end + + defp get_post(post_id) do + case Posts.get(post_id) do + %Schema.Post{} = post -> + cond do + not post.syndicate_to_mastodon -> {:cancel, "post not set to syndicate to mastodon"} + not is_nil(post.mastodon_post) -> {:cancel, "post already syndicated to mastodon"} + true -> {:ok, post} + end + + _ -> + {:cancel, "post not found"} + end + end +end diff --git a/lib/schema/post.ex b/lib/schema/post.ex index 84b29ed..2d19329 100644 --- a/lib/schema/post.ex +++ b/lib/schema/post.ex @@ -28,8 +28,8 @@ defmodule Schema.Post do field :published_at, :utc_datetime_usec field :deleted_at, :utc_datetime_usec - field :syndicate_to_mastodon, :boolean - field :syndicate_to_bluesky, :boolean + field :syndicate_to_mastodon, :boolean, default: true + field :syndicate_to_bluesky, :boolean, default: true has_one :mastodon_post, Schema.MastodonPost has_one :bluesky_post, Schema.BlueskyPost