139 lines
4.1 KiB
Elixir
139 lines
4.1 KiB
Elixir
defmodule Core.Syndication do
|
|
alias __MODULE__
|
|
|
|
## mastodon
|
|
|
|
@mastodon_instance Application.compile_env!(:sloanely_but_surely, [
|
|
Core.Syndication,
|
|
:mastodon_instance
|
|
])
|
|
|
|
def get_mastodon_account(user) do
|
|
Core.Repo.get_by(Schema.MastodonAccount, user_id: user.id)
|
|
end
|
|
|
|
def save_mastodon_account(user, attrs) do
|
|
user
|
|
|> Ecto.build_assoc(:mastodon_account)
|
|
|> Syndication.MastodonAccount.changeset(attrs)
|
|
|> Core.Repo.insert()
|
|
end
|
|
|
|
def syndicate_to_mastodon(%Schema.Post{} = post) do
|
|
conn = build_mastodon_client_conn()
|
|
|
|
{: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()
|
|
|
|
{:ok, post}
|
|
end
|
|
|
|
defp get_mastodon_access_token! do
|
|
mastodon_account = Core.Repo.one!(Schema.MastodonAccount)
|
|
mastodon_account.access_token
|
|
end
|
|
|
|
defp build_mastodon_client_conn do
|
|
%MastodonClient.Conn{
|
|
instance: @mastodon_instance,
|
|
access_token: get_mastodon_access_token!()
|
|
}
|
|
end
|
|
|
|
## bluesky
|
|
|
|
def get_bluesky_account(%Schema.User{} = user) do
|
|
Core.Repo.get_by(Schema.BlueskyAccount, user_id: user.id)
|
|
end
|
|
|
|
def save_bluesky_account(user, identifier, password) do
|
|
with {:ok, session_resp} <-
|
|
Syndication.BlueskyClient.create_session(identifier, password),
|
|
{:ok, attrs} <- parse_bluesky_token_resp(session_resp) do
|
|
user
|
|
|> Ecto.build_assoc(:bluesky_account)
|
|
|> Syndication.BlueskyAccount.create_changeset(attrs)
|
|
|> Core.Repo.insert(
|
|
conflict_target: [:user_id],
|
|
on_conflict: {:replace_all_except, [:id, :inserted_at]}
|
|
)
|
|
end
|
|
end
|
|
|
|
def refresh_bluesky_account(%Schema.BlueskyAccount{} = bluesky_account) do
|
|
if DateTime.compare(DateTime.utc_now(), bluesky_account.access_jwt_exp) == :lt do
|
|
{:ok, bluesky_account}
|
|
else
|
|
with {:ok, refresh_resp} <-
|
|
Syndication.BlueskyClient.refresh_session(bluesky_account),
|
|
{:ok, attrs} <- parse_bluesky_token_resp(refresh_resp) do
|
|
%{"bluesky_account_id" => bluesky_account.id}
|
|
|> Core.Syndication.BlueskyRefreshWorker.new(
|
|
scheduled_at: DateTime.add(bluesky_account.refresh_jwt_exp, -1, :day)
|
|
)
|
|
|> Oban.insert()
|
|
|
|
bluesky_account
|
|
|> Syndication.BlueskyAccount.refresh_changeset(attrs)
|
|
|> Core.Repo.update()
|
|
end
|
|
end
|
|
end
|
|
|
|
def syndicate_to_bluesky(%Schema.Post{} = 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()
|
|
|
|
{:ok, post}
|
|
end
|
|
end
|
|
|
|
def bsky_app_url(%Schema.BlueskyPost{uri: uri}) do
|
|
[did, id] = Regex.run(~r|at://(did:.*)/app.bsky.feed.post/(.*)|, uri, capture: :all_but_first)
|
|
|
|
"https://bsky.app/profile/#{did}/post/#{id}"
|
|
end
|
|
|
|
defp parse_bluesky_token_resp(resp) do
|
|
with {:ok, %{iat: access_jwt_iat, exp: access_jwt_exp}} <-
|
|
parse_bluesky_jwt(resp.body["accessJwt"]),
|
|
{:ok, %{iat: refresh_jwt_iat, exp: refresh_jwt_exp}} <-
|
|
parse_bluesky_jwt(resp.body["refreshJwt"]) do
|
|
{:ok,
|
|
%{
|
|
handle: resp.body["handle"],
|
|
did: resp.body["did"],
|
|
access_jwt: resp.body["accessJwt"],
|
|
access_jwt_iat: access_jwt_iat,
|
|
access_jwt_exp: access_jwt_exp,
|
|
refresh_jwt: resp.body["refreshJwt"],
|
|
refresh_jwt_iat: refresh_jwt_iat,
|
|
refresh_jwt_exp: refresh_jwt_exp
|
|
}}
|
|
end
|
|
end
|
|
|
|
defp parse_bluesky_jwt(jwt) do
|
|
with {:ok, claims} <- Joken.peek_claims(jwt),
|
|
{:ok, iat} <- DateTime.from_unix(claims["iat"]),
|
|
{:ok, exp} <- DateTime.from_unix(claims["exp"]) do
|
|
{:ok, %{iat: iat, exp: exp}}
|
|
end
|
|
end
|
|
|
|
defp get_bluesky_account! do
|
|
Core.Repo.one!(Schema.BlueskyAccount)
|
|
end
|
|
end
|