commit: 557a7d736a873bf57c3e3e271669c0815fd6fe28
parent 03d4e7eeccc7fc69c731ada8a7bd049ad52a9d6f
Author: marcin mikołajczak <git@mkljczk.pl>
Date: Sun, 30 Oct 2022 18:47:41 +0100
WIP Translation backends support
Signed-off-by: marcin mikołajczak <git@mkljczk.pl>
Diffstat:
5 files changed, 99 insertions(+), 2 deletions(-)
diff --git a/lib/pleroma/application.ex b/lib/pleroma/application.ex
@@ -182,7 +182,8 @@ defmodule Pleroma.Application do
expiration: chat_message_id_idempotency_key_expiration(),
limit: 500_000
),
- build_cachex("rel_me", limit: 2500)
+ build_cachex("rel_me", limit: 2500),
+ build_cachex("translations", default_ttl: :timer.hours(24), limit: 5_000)
]
end
diff --git a/lib/pleroma/web/api_spec/operations/status_operation.ex b/lib/pleroma/web/api_spec/operations/status_operation.ex
@@ -409,6 +409,38 @@ defmodule Pleroma.Web.ApiSpec.StatusOperation do
}
end
+ def translate_operation do
+ %Operation{
+ tags: ["Retrieve status information"],
+ summary: "Translate status",
+ description: "Translate status with an external API",
+ operationId: "StatusController.translate",
+ security: [%{"oAuth" => ["read:statuses"]}],
+ parameters: [id_param()],
+ requestBody:
+ request_body(
+ "Parameters",
+ %Schema{
+ type: :object,
+ properties: %{
+ target_language: %Schema{
+ type: :string,
+ nullable: true,
+ description: "Translation target language."
+ }
+ }
+ },
+ required: false
+ ),
+ responses: %{
+ 200 => Operation.response("Translation", "application/json", translation())
+ 400 => Operation.response("Error", "application/json", ApiError)
+ 404 => Operation.response("Error", "application/json", ApiError)
+ 503 => Operation.response("Error", "application/json", ApiError)
+ }
+ }
+ end
+
def favourites_operation do
%Operation{
tags: ["Timelines"],
@@ -793,4 +825,32 @@ defmodule Pleroma.Web.ApiSpec.StatusOperation do
}
}
end
+
+ defp translation do
+ %Schema{
+ title: "StatusTranslation",
+ description: "Represents status translation with related information.",
+ type: :object,
+ required: [:content, :detected_source_language, :provider],
+ properties: %{
+ content: %Schema{
+ type: :string,
+ description: "Translated status content"
+ },
+ detected_source_language: %Schema{
+ type: :string,
+ description: "Detected source language"
+ },
+ provider: %Schema{
+ type: :string,
+ description: "Translation provider service name"
+ }
+ },
+ example: %{
+ "content" => "Software für die nächste Generation der sozialen Medien.",
+ "detected_source_language" => "en",
+ "provider" => "Deepl"
+ }
+ }
+ end
end
diff --git a/lib/pleroma/web/mastodon_api/controllers/status_controller.ex b/lib/pleroma/web/mastodon_api/controllers/status_controller.ex
@@ -15,6 +15,7 @@ defmodule Pleroma.Web.MastodonAPI.StatusController do
alias Pleroma.Object
alias Pleroma.Repo
alias Pleroma.ScheduledActivity
+ alias Pleroma.Translation
alias Pleroma.User
alias Pleroma.Web.ActivityPub.ActivityPub
alias Pleroma.Web.ActivityPub.Visibility
@@ -44,6 +45,8 @@ defmodule Pleroma.Web.MastodonAPI.StatusController do
]
)
+ plug(OAuthScopesPlug, %{scopes: ["read:statuses"]} when action == :translate)
+
plug(
OAuthScopesPlug,
%{scopes: ["write:statuses"]}
@@ -85,7 +88,7 @@ defmodule Pleroma.Web.MastodonAPI.StatusController do
%{scopes: ["write:bookmarks"]} when action in [:bookmark, :unbookmark]
)
- @rate_limited_status_actions ~w(reblog unreblog favourite unfavourite create delete)a
+ @rate_limited_status_actions ~w(reblog unreblog favourite unfavourite create delete translate)a
plug(
RateLimiter,
@@ -554,6 +557,30 @@ defmodule Pleroma.Web.MastodonAPI.StatusController do
end
end
+ @doc "POST /api/v1/statuses/:id/translate"
+ def translate(%{body_params: params, assigns: %{user: user}} = conn, %{id: status_id}) do
+ with %Activity{object: object} <- Activity.get_by_id_with_object(status_id),
+ {:language, language} when is_binary(language) <-
+ {:language, Map.get(params, :target_language) || user.language},
+ {:ok, result} <-
+ Translation.translate(
+ object.data["content"],
+ object.data["language"],
+ language
+ ) do
+ render(conn, "translation.json", result)
+ else
+ {:language, nil} ->
+ render_error(conn, :bad_request, "Language not specified")
+
+ {:error, :not_found} ->
+ render_error(conn, :not_found, "Translation service not configured")
+
+ {:error, error} when error in [:unexpected_response, :quota_exceeded, :too_many_requests] ->
+ render_error(conn, :service_unavailable, "Translation service not available")
+ end
+ end
+
@doc "GET /api/v1/favourites"
def favourites(
%{assigns: %{user: %User{} = user}, private: %{open_api_spex: %{params: params}}} = conn,
diff --git a/lib/pleroma/web/mastodon_api/views/status_view.ex b/lib/pleroma/web/mastodon_api/views/status_view.ex
@@ -656,6 +656,14 @@ defmodule Pleroma.Web.MastodonAPI.StatusView do
}
end
+ def render("translation.json", %{
+ content: content,
+ detected_source_language: detected_source_language,
+ provider: provider
+ }) do
+ %{content: content, detected_source_language: detected_source_language, provider: provider}
+ end
+
def get_reply_to(activity, %{replied_to_activities: replied_to_activities}) do
object = Object.normalize(activity, fetch: false)
diff --git a/lib/pleroma/web/router.ex b/lib/pleroma/web/router.ex
@@ -718,6 +718,7 @@ defmodule Pleroma.Web.Router do
post("/statuses/:id/unbookmark", StatusController, :unbookmark)
post("/statuses/:id/mute", StatusController, :mute_conversation)
post("/statuses/:id/unmute", StatusController, :unmute_conversation)
+ post("/statuses/:id/translate", StatusController, :translate)
post("/push/subscription", SubscriptionController, :create)
get("/push/subscription", SubscriptionController, :show)