Merge remote-tracking branch 'soapbox/develop' into pleroma-events
Signed-off-by: marcin mikołajczak <git@mkljczk.pl>
This commit is contained in:
commit
8661703c16
65 changed files with 1343 additions and 526 deletions
|
@ -30,7 +30,7 @@ LABEL maintainer="hello@soapbox.pub" \
|
||||||
org.opencontainers.image.description="Rebased" \
|
org.opencontainers.image.description="Rebased" \
|
||||||
org.opencontainers.image.authors="hello@soapbox.pub" \
|
org.opencontainers.image.authors="hello@soapbox.pub" \
|
||||||
org.opencontainers.image.vendor="soapbox.pub" \
|
org.opencontainers.image.vendor="soapbox.pub" \
|
||||||
org.opencontainers.image.documentation="https://gitlab.com/soapbox-pub/soapbox-be" \
|
org.opencontainers.image.documentation="https://gitlab.com/soapbox-pub/rebased" \
|
||||||
org.opencontainers.image.licenses="AGPL-3.0" \
|
org.opencontainers.image.licenses="AGPL-3.0" \
|
||||||
org.opencontainers.image.url="https://soapbox.pub" \
|
org.opencontainers.image.url="https://soapbox.pub" \
|
||||||
org.opencontainers.image.revision=$VCS_REF \
|
org.opencontainers.image.revision=$VCS_REF \
|
||||||
|
@ -40,13 +40,16 @@ ARG HOME=/opt/pleroma
|
||||||
ARG DATA=/var/lib/pleroma
|
ARG DATA=/var/lib/pleroma
|
||||||
|
|
||||||
RUN apt-get update &&\
|
RUN apt-get update &&\
|
||||||
apt-get install -y --no-install-recommends imagemagick libmagic-dev ffmpeg libimage-exiftool-perl libncurses5 postgresql-client &&\
|
apt-get install -y --no-install-recommends curl ca-certificates imagemagick libmagic-dev ffmpeg libimage-exiftool-perl libncurses5 postgresql-client fasttext &&\
|
||||||
adduser --system --shell /bin/false --home ${HOME} pleroma &&\
|
adduser --system --shell /bin/false --home ${HOME} pleroma &&\
|
||||||
mkdir -p ${DATA}/uploads &&\
|
mkdir -p ${DATA}/uploads &&\
|
||||||
mkdir -p ${DATA}/static &&\
|
mkdir -p ${DATA}/static &&\
|
||||||
chown -R pleroma ${DATA} &&\
|
chown -R pleroma ${DATA} &&\
|
||||||
mkdir -p /etc/pleroma &&\
|
mkdir -p /etc/pleroma &&\
|
||||||
chown -R pleroma /etc/pleroma
|
chown -R pleroma /etc/pleroma &&\
|
||||||
|
mkdir -p /usr/share/fasttext &&\
|
||||||
|
curl -L https://dl.fbaipublicfiles.com/fasttext/supervised-models/lid.176.ftz -o /usr/share/fasttext/lid.176.ftz &&\
|
||||||
|
chmod 0644 /usr/share/fasttext/lid.176.ftz
|
||||||
|
|
||||||
USER pleroma
|
USER pleroma
|
||||||
|
|
||||||
|
|
|
@ -110,17 +110,6 @@
|
||||||
"xmpp"
|
"xmpp"
|
||||||
]
|
]
|
||||||
|
|
||||||
websocket_config = [
|
|
||||||
path: "/websocket",
|
|
||||||
serializer: [
|
|
||||||
{Phoenix.Socket.V1.JSONSerializer, "~> 1.0.0"},
|
|
||||||
{Phoenix.Socket.V2.JSONSerializer, "~> 2.0.0"}
|
|
||||||
],
|
|
||||||
timeout: 60_000,
|
|
||||||
transport_log: false,
|
|
||||||
compress: false
|
|
||||||
]
|
|
||||||
|
|
||||||
# Configures the endpoint
|
# Configures the endpoint
|
||||||
config :pleroma, Pleroma.Web.Endpoint,
|
config :pleroma, Pleroma.Web.Endpoint,
|
||||||
url: [host: "localhost"],
|
url: [host: "localhost"],
|
||||||
|
@ -130,9 +119,6 @@
|
||||||
{:_,
|
{:_,
|
||||||
[
|
[
|
||||||
{"/api/v1/streaming", Pleroma.Web.MastodonAPI.WebsocketHandler, []},
|
{"/api/v1/streaming", Pleroma.Web.MastodonAPI.WebsocketHandler, []},
|
||||||
{"/websocket", Phoenix.Endpoint.CowboyWebSocket,
|
|
||||||
{Phoenix.Transports.WebSocket,
|
|
||||||
{Pleroma.Web.Endpoint, Pleroma.Web.UserSocket, websocket_config}}},
|
|
||||||
{:_, Phoenix.Endpoint.Cowboy2Handler, {Pleroma.Web.Endpoint, []}}
|
{:_, Phoenix.Endpoint.Cowboy2Handler, {Pleroma.Web.Endpoint, []}}
|
||||||
]}
|
]}
|
||||||
]
|
]
|
||||||
|
@ -428,6 +414,11 @@
|
||||||
|
|
||||||
config :pleroma, :mrf_inline_quote, prefix: "RT"
|
config :pleroma, :mrf_inline_quote, prefix: "RT"
|
||||||
|
|
||||||
|
config :pleroma, :mrf_remote_report,
|
||||||
|
reject_all: false,
|
||||||
|
reject_anonymous: true,
|
||||||
|
reject_empty_message: true
|
||||||
|
|
||||||
config :pleroma, :rich_media,
|
config :pleroma, :rich_media,
|
||||||
enabled: true,
|
enabled: true,
|
||||||
ignore_hosts: [],
|
ignore_hosts: [],
|
||||||
|
@ -475,10 +466,6 @@
|
||||||
image_quality: 85,
|
image_quality: 85,
|
||||||
min_content_length: 100 * 1024
|
min_content_length: 100 * 1024
|
||||||
|
|
||||||
config :pleroma, :shout,
|
|
||||||
enabled: true,
|
|
||||||
limit: 5_000
|
|
||||||
|
|
||||||
config :phoenix, :format_encoders, json: Jason, "activity+json": Jason, ics: ICalendar
|
config :phoenix, :format_encoders, json: Jason, "activity+json": Jason, ics: ICalendar
|
||||||
|
|
||||||
config :phoenix, :json_library, Jason
|
config :phoenix, :json_library, Jason
|
||||||
|
|
|
@ -566,6 +566,12 @@
|
||||||
"Cool instance"
|
"Cool instance"
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
|
%{
|
||||||
|
key: :contact_username,
|
||||||
|
type: :string,
|
||||||
|
description: "Instance owner username",
|
||||||
|
suggestions: ["admin"]
|
||||||
|
},
|
||||||
%{
|
%{
|
||||||
key: :limit,
|
key: :limit,
|
||||||
type: :integer,
|
type: :integer,
|
||||||
|
@ -2742,27 +2748,6 @@
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
%{
|
|
||||||
group: :pleroma,
|
|
||||||
key: :shout,
|
|
||||||
type: :group,
|
|
||||||
description: "Pleroma shout settings",
|
|
||||||
children: [
|
|
||||||
%{
|
|
||||||
key: :enabled,
|
|
||||||
type: :boolean,
|
|
||||||
description: "Enables the backend Shoutbox chat feature."
|
|
||||||
},
|
|
||||||
%{
|
|
||||||
key: :limit,
|
|
||||||
type: :integer,
|
|
||||||
description: "Shout message character limit.",
|
|
||||||
suggestions: [
|
|
||||||
5_000
|
|
||||||
]
|
|
||||||
}
|
|
||||||
]
|
|
||||||
},
|
|
||||||
%{
|
%{
|
||||||
group: :pleroma,
|
group: :pleroma,
|
||||||
key: :http,
|
key: :http,
|
||||||
|
@ -3550,5 +3535,71 @@
|
||||||
description: "Update nickname according to host-meta, when refetching the user"
|
description: "Update nickname according to host-meta, when refetching the user"
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
|
},
|
||||||
|
%{
|
||||||
|
group: :pleroma,
|
||||||
|
key: Pleroma.Language.Translation,
|
||||||
|
type: :group,
|
||||||
|
description: "Translation providers",
|
||||||
|
children: [
|
||||||
|
%{
|
||||||
|
key: :provider,
|
||||||
|
type: :module,
|
||||||
|
suggestions: [
|
||||||
|
Pleroma.Language.Translation.Deepl,
|
||||||
|
Pleroma.Language.Translation.Libretranslate
|
||||||
|
]
|
||||||
|
},
|
||||||
|
%{
|
||||||
|
group: {:subgroup, Pleroma.Language.Translation.Deepl},
|
||||||
|
key: :base_url,
|
||||||
|
label: "DeepL base URL",
|
||||||
|
type: :string,
|
||||||
|
suggestions: ["https://api-free.deepl.com", "https://api.deepl.com"]
|
||||||
|
},
|
||||||
|
%{
|
||||||
|
group: {:subgroup, Pleroma.Language.Translation.Deepl},
|
||||||
|
key: :api_key,
|
||||||
|
label: "DeepL API Key",
|
||||||
|
type: :string,
|
||||||
|
suggestions: ["YOUR_API_KEY"]
|
||||||
|
},
|
||||||
|
%{
|
||||||
|
group: {:subgroup, Pleroma.Language.Translation.Libretranslate},
|
||||||
|
key: :base_url,
|
||||||
|
label: "LibreTranslate instance URL",
|
||||||
|
type: :string,
|
||||||
|
suggestions: ["https://libretranslate.com"]
|
||||||
|
},
|
||||||
|
%{
|
||||||
|
group: {:subgroup, Pleroma.Language.Translation.Libretranslate},
|
||||||
|
key: :api_key,
|
||||||
|
label: "LibreTranslate API Key",
|
||||||
|
type: :string,
|
||||||
|
suggestions: ["YOUR_API_KEY"]
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
%{
|
||||||
|
group: :pleroma,
|
||||||
|
key: Pleroma.Language.LanguageDetector,
|
||||||
|
type: :group,
|
||||||
|
description: "Language detection providers",
|
||||||
|
children: [
|
||||||
|
%{
|
||||||
|
key: :provider,
|
||||||
|
type: :module,
|
||||||
|
suggestions: [
|
||||||
|
Pleroma.Language.LanguageDetector.Fasttext
|
||||||
|
]
|
||||||
|
},
|
||||||
|
%{
|
||||||
|
group: {:subgroup, Pleroma.Language.LanguageDetector.Fasttext},
|
||||||
|
key: :model,
|
||||||
|
label: "fastText language detection model",
|
||||||
|
type: :string,
|
||||||
|
suggestions: ["/usr/share/fasttext/lid.176.bin"]
|
||||||
|
}
|
||||||
|
]
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
|
|
|
@ -32,6 +32,12 @@
|
||||||
config :pleroma, :instance, static_dir: "/var/lib/pleroma/static"
|
config :pleroma, :instance, static_dir: "/var/lib/pleroma/static"
|
||||||
config :pleroma, Pleroma.Uploaders.Local, uploads: "/var/lib/pleroma/uploads"
|
config :pleroma, Pleroma.Uploaders.Local, uploads: "/var/lib/pleroma/uploads"
|
||||||
|
|
||||||
|
config :pleroma, Pleroma.Language.LanguageDetector,
|
||||||
|
provider: Pleroma.Language.LanguageDetector.Fasttext
|
||||||
|
|
||||||
|
config :pleroma, Pleroma.Language.LanguageDetector.Fasttext,
|
||||||
|
model: "/usr/share/fasttext/lid.176.ftz"
|
||||||
|
|
||||||
# We can't store the secrets in this file, since this is baked into the docker image
|
# We can't store the secrets in this file, since this is baked into the docker image
|
||||||
if not File.exists?("/var/lib/pleroma/secret.exs") do
|
if not File.exists?("/var/lib/pleroma/secret.exs") do
|
||||||
secret = :crypto.strong_rand_bytes(64) |> Base.encode64() |> binary_part(0, 64)
|
secret = :crypto.strong_rand_bytes(64) |> Base.encode64() |> binary_part(0, 64)
|
||||||
|
|
|
@ -8,11 +8,6 @@ For from source installations Pleroma configuration works by first importing the
|
||||||
|
|
||||||
To add configuration to your config file, you can copy it from the base config. The latest version of it can be viewed [here](https://git.pleroma.social/pleroma/pleroma/blob/develop/config/config.exs). You can also use this file if you don't know how an option is supposed to be formatted.
|
To add configuration to your config file, you can copy it from the base config. The latest version of it can be viewed [here](https://git.pleroma.social/pleroma/pleroma/blob/develop/config/config.exs). You can also use this file if you don't know how an option is supposed to be formatted.
|
||||||
|
|
||||||
## :shout
|
|
||||||
|
|
||||||
* `enabled` - Enables the backend Shoutbox chat feature. Defaults to `true`.
|
|
||||||
* `limit` - Shout character limit. Defaults to `5_000`
|
|
||||||
|
|
||||||
## :instance
|
## :instance
|
||||||
* `name`: The instance’s name.
|
* `name`: The instance’s name.
|
||||||
* `email`: Email used to reach an Administrator/Moderator of the instance.
|
* `email`: Email used to reach an Administrator/Moderator of the instance.
|
||||||
|
|
|
@ -103,7 +103,6 @@ Has these additional fields under the `pleroma` object:
|
||||||
- `hide_followers_count`: boolean, true when the user has follower stat hiding enabled
|
- `hide_followers_count`: boolean, true when the user has follower stat hiding enabled
|
||||||
- `hide_follows_count`: boolean, true when the user has follow stat hiding enabled
|
- `hide_follows_count`: boolean, true when the user has follow stat hiding enabled
|
||||||
- `settings_store`: A generic map of settings for frontends. Opaque to the backend. Only returned in `/api/v1/accounts/verify_credentials` and `/api/v1/accounts/update_credentials`
|
- `settings_store`: A generic map of settings for frontends. Opaque to the backend. Only returned in `/api/v1/accounts/verify_credentials` and `/api/v1/accounts/update_credentials`
|
||||||
- `chat_token`: The token needed for Pleroma shoutbox. Only returned in `/api/v1/accounts/verify_credentials`
|
|
||||||
- `deactivated`: boolean, true when the user is deactivated
|
- `deactivated`: boolean, true when the user is deactivated
|
||||||
- `allow_following_move`: boolean, true when the user allows automatically follow moved following accounts
|
- `allow_following_move`: boolean, true when the user allows automatically follow moved following accounts
|
||||||
- `unread_conversation_count`: The count of unread conversations. Only returned to the account owner.
|
- `unread_conversation_count`: The count of unread conversations. Only returned to the account owner.
|
||||||
|
|
|
@ -45,7 +45,6 @@ See also [the Nodeinfo standard](https://nodeinfo.diaspora.software/).
|
||||||
"multifetch",
|
"multifetch",
|
||||||
"pleroma:api/v1/notifications:include_types_filter",
|
"pleroma:api/v1/notifications:include_types_filter",
|
||||||
"chat",
|
"chat",
|
||||||
"shout",
|
|
||||||
"relay",
|
"relay",
|
||||||
"pleroma_emoji_reactions",
|
"pleroma_emoji_reactions",
|
||||||
"pleroma_chat_messages"
|
"pleroma_chat_messages"
|
||||||
|
@ -205,7 +204,6 @@ See also [the Nodeinfo standard](https://nodeinfo.diaspora.software/).
|
||||||
"multifetch",
|
"multifetch",
|
||||||
"pleroma:api/v1/notifications:include_types_filter",
|
"pleroma:api/v1/notifications:include_types_filter",
|
||||||
"chat",
|
"chat",
|
||||||
"shout",
|
|
||||||
"relay",
|
"relay",
|
||||||
"pleroma_emoji_reactions",
|
"pleroma_emoji_reactions",
|
||||||
"pleroma_chat_messages"
|
"pleroma_chat_messages"
|
||||||
|
|
|
@ -113,7 +113,6 @@ def start(_type, _args) do
|
||||||
] ++
|
] ++
|
||||||
task_children(@mix_env) ++
|
task_children(@mix_env) ++
|
||||||
dont_run_in_test(@mix_env) ++
|
dont_run_in_test(@mix_env) ++
|
||||||
shout_child(shout_enabled?()) ++
|
|
||||||
[Pleroma.Gopher.Server]
|
[Pleroma.Gopher.Server]
|
||||||
|
|
||||||
# See http://elixir-lang.org/docs/stable/elixir/Supervisor.html
|
# See http://elixir-lang.org/docs/stable/elixir/Supervisor.html
|
||||||
|
@ -214,7 +213,8 @@ defp cachex_children do
|
||||||
build_cachex("chat_message_id_idempotency_key",
|
build_cachex("chat_message_id_idempotency_key",
|
||||||
expiration: chat_message_id_idempotency_key_expiration(),
|
expiration: chat_message_id_idempotency_key_expiration(),
|
||||||
limit: 500_000
|
limit: 500_000
|
||||||
)
|
),
|
||||||
|
build_cachex("translations", default_ttl: :timer.hours(24), limit: 5_000)
|
||||||
]
|
]
|
||||||
end
|
end
|
||||||
|
|
||||||
|
@ -238,8 +238,6 @@ def build_cachex(type, opts),
|
||||||
type: :worker
|
type: :worker
|
||||||
}
|
}
|
||||||
|
|
||||||
defp shout_enabled?, do: Config.get([:shout, :enabled])
|
|
||||||
|
|
||||||
defp dont_run_in_test(env) when env in [:test, :benchmark], do: []
|
defp dont_run_in_test(env) when env in [:test, :benchmark], do: []
|
||||||
|
|
||||||
defp dont_run_in_test(_) do
|
defp dont_run_in_test(_) do
|
||||||
|
@ -260,15 +258,6 @@ defp background_migrators do
|
||||||
]
|
]
|
||||||
end
|
end
|
||||||
|
|
||||||
defp shout_child(true) do
|
|
||||||
[
|
|
||||||
Pleroma.Web.ShoutChannel.ShoutChannelState,
|
|
||||||
{Phoenix.PubSub, [name: Pleroma.PubSub, adapter: Phoenix.PubSub.PG2]}
|
|
||||||
]
|
|
||||||
end
|
|
||||||
|
|
||||||
defp shout_child(_), do: []
|
|
||||||
|
|
||||||
defp task_children(:test) do
|
defp task_children(:test) do
|
||||||
[
|
[
|
||||||
%{
|
%{
|
||||||
|
|
|
@ -187,7 +187,27 @@ defp check_system_commands!(:ok) do
|
||||||
false
|
false
|
||||||
end
|
end
|
||||||
|
|
||||||
if Enum.all?([preview_proxy_commands_status | filter_commands_statuses], & &1) do
|
language_detector_commands_status =
|
||||||
|
if Pleroma.Language.LanguageDetector.missing_dependencies() == [] do
|
||||||
|
true
|
||||||
|
else
|
||||||
|
Logger.error(
|
||||||
|
"The following dependencies required by the currently enabled " <>
|
||||||
|
"language detection provider are not installed: " <>
|
||||||
|
inspect(Pleroma.Language.LanguageDetector.missing_dependencies())
|
||||||
|
)
|
||||||
|
|
||||||
|
false
|
||||||
|
end
|
||||||
|
|
||||||
|
if Enum.all?(
|
||||||
|
[
|
||||||
|
preview_proxy_commands_status,
|
||||||
|
language_detector_commands_status
|
||||||
|
| filter_commands_statuses
|
||||||
|
],
|
||||||
|
& &1
|
||||||
|
) do
|
||||||
:ok
|
:ok
|
||||||
else
|
else
|
||||||
{:error,
|
{:error,
|
||||||
|
|
|
@ -214,7 +214,6 @@ def warn do
|
||||||
check_activity_expiration_config(),
|
check_activity_expiration_config(),
|
||||||
check_remote_ip_plug_name(),
|
check_remote_ip_plug_name(),
|
||||||
check_uploders_s3_public_endpoint(),
|
check_uploders_s3_public_endpoint(),
|
||||||
check_old_chat_shoutbox(),
|
|
||||||
check_quarantined_instances_tuples(),
|
check_quarantined_instances_tuples(),
|
||||||
check_transparency_exclusions_tuples(),
|
check_transparency_exclusions_tuples(),
|
||||||
check_simple_policy_tuples(),
|
check_simple_policy_tuples(),
|
||||||
|
@ -392,27 +391,4 @@ def check_uploders_s3_public_endpoint do
|
||||||
:ok
|
:ok
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
@spec check_old_chat_shoutbox() :: :ok | nil
|
|
||||||
def check_old_chat_shoutbox do
|
|
||||||
instance_config = Pleroma.Config.get([:instance])
|
|
||||||
chat_config = Pleroma.Config.get([:chat]) || []
|
|
||||||
|
|
||||||
use_old_config =
|
|
||||||
Keyword.has_key?(instance_config, :chat_limit) or
|
|
||||||
Keyword.has_key?(chat_config, :enabled)
|
|
||||||
|
|
||||||
if use_old_config do
|
|
||||||
Logger.error("""
|
|
||||||
!!!DEPRECATION WARNING!!!
|
|
||||||
Your config is using the old namespace for the Shoutbox configuration. You need to convert to the new namespace. e.g.,
|
|
||||||
\n* `config :pleroma, :chat, enabled` and `config :pleroma, :instance, chat_limit` are now equal to:
|
|
||||||
\n* `config :pleroma, :shout, enabled` and `config :pleroma, :shout, limit`
|
|
||||||
""")
|
|
||||||
|
|
||||||
:error
|
|
||||||
else
|
|
||||||
:ok
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end
|
end
|
||||||
|
|
|
@ -16,7 +16,6 @@ defmodule Pleroma.Config.TransferTask do
|
||||||
defp reboot_time_keys,
|
defp reboot_time_keys,
|
||||||
do: [
|
do: [
|
||||||
{:pleroma, :hackney_pools},
|
{:pleroma, :hackney_pools},
|
||||||
{:pleroma, :shout},
|
|
||||||
{:pleroma, Oban},
|
{:pleroma, Oban},
|
||||||
{:pleroma, :rate_limit},
|
{:pleroma, :rate_limit},
|
||||||
{:pleroma, :markup},
|
{:pleroma, :markup},
|
||||||
|
|
|
@ -23,6 +23,7 @@ defmodule Pleroma.Constants do
|
||||||
"assigned_account",
|
"assigned_account",
|
||||||
"rules",
|
"rules",
|
||||||
"content_type",
|
"content_type",
|
||||||
|
"language",
|
||||||
"participations",
|
"participations",
|
||||||
"participation_count",
|
"participation_count",
|
||||||
"participation_request_count",
|
"participation_request_count",
|
||||||
|
@ -46,6 +47,7 @@ defmodule Pleroma.Constants do
|
||||||
"sensitive",
|
"sensitive",
|
||||||
"attachment",
|
"attachment",
|
||||||
"generator",
|
"generator",
|
||||||
|
"language",
|
||||||
"startTime",
|
"startTime",
|
||||||
"endTime",
|
"endTime",
|
||||||
"location",
|
"location",
|
||||||
|
|
44
lib/pleroma/language/language_detector.ex
Normal file
44
lib/pleroma/language/language_detector.ex
Normal file
|
@ -0,0 +1,44 @@
|
||||||
|
# Pleroma: A lightweight social networking server
|
||||||
|
# Copyright © 2017-2022 Pleroma Authors <https://pleroma.social/>
|
||||||
|
# SPDX-License-Identifier: AGPL-3.0-only
|
||||||
|
|
||||||
|
defmodule Pleroma.Language.LanguageDetector do
|
||||||
|
@words_threshold 4
|
||||||
|
|
||||||
|
def missing_dependencies do
|
||||||
|
provider = get_provider()
|
||||||
|
|
||||||
|
if provider do
|
||||||
|
provider.missing_dependencies()
|
||||||
|
else
|
||||||
|
[]
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
# Strip tags from text, etc.
|
||||||
|
defp prepare_text(text) do
|
||||||
|
text
|
||||||
|
|> Floki.parse_fragment!()
|
||||||
|
|> Floki.filter_out(
|
||||||
|
".h-card, .mention, .hashtag, .u-url, .quote-inline, .recipients-inline, code, pre"
|
||||||
|
)
|
||||||
|
|> Floki.text()
|
||||||
|
end
|
||||||
|
|
||||||
|
def detect(text) do
|
||||||
|
provider = get_provider()
|
||||||
|
|
||||||
|
text = prepare_text(text)
|
||||||
|
word_count = text |> String.split(~r/\s+/) |> Enum.count()
|
||||||
|
|
||||||
|
if word_count < @words_threshold or !provider or !provider.configured? do
|
||||||
|
nil
|
||||||
|
else
|
||||||
|
provider.detect(text)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
defp get_provider do
|
||||||
|
Pleroma.Config.get([__MODULE__, :provider])
|
||||||
|
end
|
||||||
|
end
|
47
lib/pleroma/language/language_detector/fasttext.ex
Normal file
47
lib/pleroma/language/language_detector/fasttext.ex
Normal file
|
@ -0,0 +1,47 @@
|
||||||
|
# Pleroma: A lightweight social networking server
|
||||||
|
# Copyright © 2017-2022 Pleroma Authors <https://pleroma.social/>
|
||||||
|
# SPDX-License-Identifier: AGPL-3.0-only
|
||||||
|
|
||||||
|
defmodule Pleroma.Language.LanguageDetector.Fasttext do
|
||||||
|
import Pleroma.Web.Utils.Guards, only: [not_empty_string: 1]
|
||||||
|
|
||||||
|
alias Pleroma.Language.LanguageDetector.Provider
|
||||||
|
|
||||||
|
@behaviour Provider
|
||||||
|
|
||||||
|
@impl Provider
|
||||||
|
def missing_dependencies do
|
||||||
|
if Pleroma.Utils.command_available?("fasttext") do
|
||||||
|
[]
|
||||||
|
else
|
||||||
|
["fasttext"]
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
@impl Provider
|
||||||
|
def configured?, do: not_empty_string(get_model())
|
||||||
|
|
||||||
|
@impl Provider
|
||||||
|
def detect(text) do
|
||||||
|
text_path = Path.join(System.tmp_dir!(), "fasttext-#{Ecto.UUID.generate()}")
|
||||||
|
|
||||||
|
File.write(text_path, text |> String.replace(~r/\s+/, " "))
|
||||||
|
|
||||||
|
detected_language =
|
||||||
|
case System.cmd("fasttext", ["predict", get_model(), text_path]) do
|
||||||
|
{"__label__" <> language, _} ->
|
||||||
|
language |> String.trim()
|
||||||
|
|
||||||
|
_ ->
|
||||||
|
nil
|
||||||
|
end
|
||||||
|
|
||||||
|
File.rm(text_path)
|
||||||
|
|
||||||
|
detected_language
|
||||||
|
end
|
||||||
|
|
||||||
|
defp get_model do
|
||||||
|
Pleroma.Config.get([__MODULE__, :model])
|
||||||
|
end
|
||||||
|
end
|
11
lib/pleroma/language/language_detector/provider.ex
Normal file
11
lib/pleroma/language/language_detector/provider.ex
Normal file
|
@ -0,0 +1,11 @@
|
||||||
|
# Pleroma: A lightweight social networking server
|
||||||
|
# Copyright © 2017-2022 Pleroma Authors <https://pleroma.social/>
|
||||||
|
# SPDX-License-Identifier: AGPL-3.0-only
|
||||||
|
|
||||||
|
defmodule Pleroma.Language.LanguageDetector.Provider do
|
||||||
|
@callback missing_dependencies() :: [String.t()]
|
||||||
|
|
||||||
|
@callback configured?() :: boolean()
|
||||||
|
|
||||||
|
@callback detect(text :: String.t()) :: String.t() | nil
|
||||||
|
end
|
53
lib/pleroma/language/translation.ex
Normal file
53
lib/pleroma/language/translation.ex
Normal file
|
@ -0,0 +1,53 @@
|
||||||
|
# Pleroma: A lightweight social networking server
|
||||||
|
# Copyright © 2017-2022 Pleroma Authors <https://pleroma.social/>
|
||||||
|
# SPDX-License-Identifier: AGPL-3.0-only
|
||||||
|
|
||||||
|
defmodule Pleroma.Language.Translation do
|
||||||
|
@cachex Pleroma.Config.get([:cachex, :provider], Cachex)
|
||||||
|
|
||||||
|
def configured? do
|
||||||
|
provider = get_provider()
|
||||||
|
|
||||||
|
!!provider and provider.configured?
|
||||||
|
end
|
||||||
|
|
||||||
|
def translate(text, source_language, target_language) do
|
||||||
|
cache_key = get_cache_key(text, source_language, target_language)
|
||||||
|
|
||||||
|
case @cachex.get(:translations_cache, cache_key) do
|
||||||
|
{:ok, nil} ->
|
||||||
|
provider = get_provider()
|
||||||
|
|
||||||
|
result =
|
||||||
|
if !configured?() do
|
||||||
|
{:error, :not_found}
|
||||||
|
else
|
||||||
|
provider.translate(text, source_language, target_language)
|
||||||
|
end
|
||||||
|
|
||||||
|
store_result(result, cache_key)
|
||||||
|
|
||||||
|
result
|
||||||
|
|
||||||
|
{:ok, result} ->
|
||||||
|
{:ok, result}
|
||||||
|
|
||||||
|
{:error, error} ->
|
||||||
|
{:error, error}
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
defp get_provider, do: Pleroma.Config.get([__MODULE__, :provider])
|
||||||
|
|
||||||
|
defp get_cache_key(text, source_language, target_language) do
|
||||||
|
"#{source_language}/#{target_language}/#{content_hash(text)}"
|
||||||
|
end
|
||||||
|
|
||||||
|
defp store_result({:ok, result}, cache_key) do
|
||||||
|
@cachex.put(:translations_cache, cache_key, result)
|
||||||
|
end
|
||||||
|
|
||||||
|
defp store_result(_, _), do: nil
|
||||||
|
|
||||||
|
defp content_hash(text), do: :crypto.hash(:sha256, text) |> Base.encode64()
|
||||||
|
end
|
74
lib/pleroma/language/translation/deepl.ex
Normal file
74
lib/pleroma/language/translation/deepl.ex
Normal file
|
@ -0,0 +1,74 @@
|
||||||
|
# Pleroma: A lightweight social networking server
|
||||||
|
# Copyright © 2017-2022 Pleroma Authors <https://pleroma.social/>
|
||||||
|
# SPDX-License-Identifier: AGPL-3.0-only
|
||||||
|
|
||||||
|
defmodule Pleroma.Language.Translation.Deepl do
|
||||||
|
import Pleroma.Web.Utils.Guards, only: [not_empty_string: 1]
|
||||||
|
|
||||||
|
alias Pleroma.Language.Translation.Provider
|
||||||
|
|
||||||
|
@behaviour Provider
|
||||||
|
|
||||||
|
@impl Provider
|
||||||
|
def configured? do
|
||||||
|
not_empty_string(get_base_url()) and not_empty_string(get_api_key())
|
||||||
|
end
|
||||||
|
|
||||||
|
@impl Provider
|
||||||
|
def translate(content, source_language, target_language) do
|
||||||
|
endpoint = get_endpoint()
|
||||||
|
|
||||||
|
case Pleroma.HTTP.post(
|
||||||
|
endpoint <>
|
||||||
|
"?" <>
|
||||||
|
URI.encode_query(%{
|
||||||
|
text: content,
|
||||||
|
source_lang: source_language |> String.upcase(),
|
||||||
|
target_lang: target_language,
|
||||||
|
tag_handling: "html"
|
||||||
|
}),
|
||||||
|
"",
|
||||||
|
[
|
||||||
|
{"Content-Type", "application/x-www-form-urlencoded"},
|
||||||
|
{"Authorization", "DeepL-Auth-Key #{get_api_key()}"}
|
||||||
|
]
|
||||||
|
) do
|
||||||
|
{:ok, %{status: 429}} ->
|
||||||
|
{:error, :too_many_requests}
|
||||||
|
|
||||||
|
{:ok, %{status: 456}} ->
|
||||||
|
{:error, :quota_exceeded}
|
||||||
|
|
||||||
|
{:ok, %{status: 200} = res} ->
|
||||||
|
%{
|
||||||
|
"translations" => [
|
||||||
|
%{"text" => content, "detected_source_language" => detected_source_language}
|
||||||
|
]
|
||||||
|
} = Jason.decode!(res.body)
|
||||||
|
|
||||||
|
{:ok,
|
||||||
|
%{
|
||||||
|
content: content,
|
||||||
|
detected_source_language: detected_source_language,
|
||||||
|
provider: "DeepL"
|
||||||
|
}}
|
||||||
|
|
||||||
|
_ ->
|
||||||
|
{:error, :internal_server_error}
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
defp get_endpoint do
|
||||||
|
get_base_url()
|
||||||
|
|> URI.merge("/v2/translate")
|
||||||
|
|> URI.to_string()
|
||||||
|
end
|
||||||
|
|
||||||
|
defp get_base_url do
|
||||||
|
Pleroma.Config.get([__MODULE__, :base_url])
|
||||||
|
end
|
||||||
|
|
||||||
|
defp get_api_key do
|
||||||
|
Pleroma.Config.get([__MODULE__, :api_key])
|
||||||
|
end
|
||||||
|
end
|
66
lib/pleroma/language/translation/libretranslate.ex
Normal file
66
lib/pleroma/language/translation/libretranslate.ex
Normal file
|
@ -0,0 +1,66 @@
|
||||||
|
# Pleroma: A lightweight social networking server
|
||||||
|
# Copyright © 2017-2022 Pleroma Authors <https://pleroma.social/>
|
||||||
|
# SPDX-License-Identifier: AGPL-3.0-only
|
||||||
|
|
||||||
|
defmodule Pleroma.Language.Translation.Libretranslate do
|
||||||
|
import Pleroma.Web.Utils.Guards, only: [not_empty_string: 1]
|
||||||
|
|
||||||
|
alias Pleroma.Language.Translation.Provider
|
||||||
|
|
||||||
|
@behaviour Provider
|
||||||
|
|
||||||
|
@impl Provider
|
||||||
|
def configured?, do: not_empty_string(get_base_url())
|
||||||
|
|
||||||
|
@impl Provider
|
||||||
|
def translate(content, source_language, target_language) do
|
||||||
|
endpoint = endpoint_url()
|
||||||
|
|
||||||
|
case Pleroma.HTTP.post(
|
||||||
|
endpoint,
|
||||||
|
Jason.encode!(%{
|
||||||
|
q: content,
|
||||||
|
source: source_language |> String.upcase(),
|
||||||
|
target: target_language,
|
||||||
|
format: "html",
|
||||||
|
api_key: get_api_key()
|
||||||
|
}),
|
||||||
|
[
|
||||||
|
{"Content-Type", "application/json"}
|
||||||
|
]
|
||||||
|
) do
|
||||||
|
{:ok, %{status: 429}} ->
|
||||||
|
{:error, :too_many_requests}
|
||||||
|
|
||||||
|
{:ok, %{status: 403}} ->
|
||||||
|
{:error, :quota_exceeded}
|
||||||
|
|
||||||
|
{:ok, %{status: 200} = res} ->
|
||||||
|
%{
|
||||||
|
"translatedText" => content
|
||||||
|
} = Jason.decode!(res.body)
|
||||||
|
|
||||||
|
{:ok,
|
||||||
|
%{
|
||||||
|
content: content,
|
||||||
|
detected_source_language: source_language,
|
||||||
|
provider: "LibreTranslate"
|
||||||
|
}}
|
||||||
|
|
||||||
|
_ ->
|
||||||
|
{:error, :internal_server_error}
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
defp endpoint_url do
|
||||||
|
get_base_url() <> "/translate"
|
||||||
|
end
|
||||||
|
|
||||||
|
defp get_base_url do
|
||||||
|
Pleroma.Config.get([__MODULE__, :base_url])
|
||||||
|
end
|
||||||
|
|
||||||
|
defp get_api_key do
|
||||||
|
Pleroma.Config.get([__MODULE__, :api_key], "")
|
||||||
|
end
|
||||||
|
end
|
20
lib/pleroma/language/translation/provider.ex
Normal file
20
lib/pleroma/language/translation/provider.ex
Normal file
|
@ -0,0 +1,20 @@
|
||||||
|
# Pleroma: A lightweight social networking server
|
||||||
|
# Copyright © 2017-2022 Pleroma Authors <https://pleroma.social/>
|
||||||
|
# SPDX-License-Identifier: AGPL-3.0-only
|
||||||
|
|
||||||
|
defmodule Pleroma.Language.Translation.Provider do
|
||||||
|
@callback configured?() :: boolean()
|
||||||
|
|
||||||
|
@callback translate(
|
||||||
|
content :: String.t(),
|
||||||
|
source_language :: String.t(),
|
||||||
|
target_language :: String.t()
|
||||||
|
) ::
|
||||||
|
{:ok,
|
||||||
|
%{
|
||||||
|
content: String.t(),
|
||||||
|
detected_source_language: String.t(),
|
||||||
|
provider: String.t()
|
||||||
|
}}
|
||||||
|
| {:error, atom()}
|
||||||
|
end
|
|
@ -246,7 +246,7 @@ def update(actor, object) do
|
||||||
{to, cc, bcc} =
|
{to, cc, bcc} =
|
||||||
if object["type"] in Pleroma.Constants.actor_types() do
|
if object["type"] in Pleroma.Constants.actor_types() do
|
||||||
# User updates, always public
|
# User updates, always public
|
||||||
{[Pleroma.Constants.as_public(), actor.follower_address], []}
|
{[Pleroma.Constants.as_public(), actor.follower_address], [], []}
|
||||||
else
|
else
|
||||||
# Status updates, follow the recipients in the object
|
# Status updates, follow the recipients in the object
|
||||||
{object["to"] || [], object["cc"] || [], object["participations"] || []}
|
{object["to"] || [], object["cc"] || [], object["participations"] || []}
|
||||||
|
|
95
lib/pleroma/web/activity_pub/mrf/remote_report_policy.ex
Normal file
95
lib/pleroma/web/activity_pub/mrf/remote_report_policy.ex
Normal file
|
@ -0,0 +1,95 @@
|
||||||
|
defmodule Pleroma.Web.ActivityPub.MRF.RemoteReportPolicy do
|
||||||
|
@moduledoc "Drop remote reports if they don't contain enough information."
|
||||||
|
@behaviour Pleroma.Web.ActivityPub.MRF.Policy
|
||||||
|
|
||||||
|
alias Pleroma.Config
|
||||||
|
|
||||||
|
@impl true
|
||||||
|
def filter(%{"type" => "Flag"} = object) do
|
||||||
|
with {_, false} <- {:local, local?(object)},
|
||||||
|
{:ok, _} <- maybe_reject_all(object),
|
||||||
|
{:ok, _} <- maybe_reject_anonymous(object),
|
||||||
|
{:ok, _} <- maybe_reject_empty_message(object) do
|
||||||
|
{:ok, object}
|
||||||
|
else
|
||||||
|
{:local, true} -> {:ok, object}
|
||||||
|
{:reject, message} -> {:reject, message}
|
||||||
|
error -> {:reject, error}
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
def filter(object), do: {:ok, object}
|
||||||
|
|
||||||
|
defp maybe_reject_all(object) do
|
||||||
|
if Config.get([:mrf_remote_report, :reject_all]) do
|
||||||
|
{:reject, "[RemoteReportPolicy] Remote report"}
|
||||||
|
else
|
||||||
|
{:ok, object}
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
defp maybe_reject_anonymous(%{"actor" => actor} = object) do
|
||||||
|
with true <- Config.get([:mrf_remote_report, :reject_anonymous]),
|
||||||
|
%URI{path: "/actor"} <- URI.parse(actor) do
|
||||||
|
{:reject, "[RemoteReportPolicy] Anonymous: #{actor}"}
|
||||||
|
else
|
||||||
|
_ -> {:ok, object}
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
defp maybe_reject_empty_message(%{"content" => content} = object)
|
||||||
|
when is_binary(content) and content != "" do
|
||||||
|
{:ok, object}
|
||||||
|
end
|
||||||
|
|
||||||
|
defp maybe_reject_empty_message(object) do
|
||||||
|
if Config.get([:mrf_remote_report, :reject_empty_message]) do
|
||||||
|
{:reject, ["RemoteReportPolicy] No content"]}
|
||||||
|
else
|
||||||
|
{:ok, object}
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
defp local?(%{"actor" => actor}) do
|
||||||
|
String.starts_with?(actor, Pleroma.Web.Endpoint.url())
|
||||||
|
end
|
||||||
|
|
||||||
|
@impl true
|
||||||
|
def describe do
|
||||||
|
mrf_remote_report =
|
||||||
|
Config.get(:mrf_remote_report)
|
||||||
|
|> Enum.into(%{})
|
||||||
|
|
||||||
|
{:ok, %{mrf_remote_report: mrf_remote_report}}
|
||||||
|
end
|
||||||
|
|
||||||
|
@impl true
|
||||||
|
def config_description do
|
||||||
|
%{
|
||||||
|
key: :mrf_remote_report,
|
||||||
|
related_policy: "Pleroma.Web.ActivityPub.MRF.RemoteReportPolicy",
|
||||||
|
label: "MRF Remote Report",
|
||||||
|
description: "Drop remote reports if they don't contain enough information.",
|
||||||
|
children: [
|
||||||
|
%{
|
||||||
|
key: :reject_all,
|
||||||
|
type: :boolean,
|
||||||
|
description: "Reject all remote reports? (this option takes precedence)",
|
||||||
|
suggestions: [false]
|
||||||
|
},
|
||||||
|
%{
|
||||||
|
key: :reject_anonymous,
|
||||||
|
type: :boolean,
|
||||||
|
description: "Reject anonymous remote reports?",
|
||||||
|
suggestions: [true]
|
||||||
|
},
|
||||||
|
%{
|
||||||
|
key: :reject_empty_message,
|
||||||
|
type: :boolean,
|
||||||
|
description: "Reject remote reports with no message?",
|
||||||
|
suggestions: [true]
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
end
|
||||||
|
end
|
|
@ -108,6 +108,7 @@ defp fix(data) do
|
||||||
|> fix_attachments()
|
|> fix_attachments()
|
||||||
|> Transmogrifier.fix_emoji()
|
|> Transmogrifier.fix_emoji()
|
||||||
|> Transmogrifier.fix_content_map()
|
|> Transmogrifier.fix_content_map()
|
||||||
|
|> Transmogrifier.maybe_add_language()
|
||||||
end
|
end
|
||||||
|
|
||||||
def changeset(struct, data) do
|
def changeset(struct, data) do
|
||||||
|
|
|
@ -59,6 +59,7 @@ defmacro status_object_fields do
|
||||||
field(:like_count, :integer, default: 0)
|
field(:like_count, :integer, default: 0)
|
||||||
field(:announcement_count, :integer, default: 0)
|
field(:announcement_count, :integer, default: 0)
|
||||||
field(:quotes_count, :integer, default: 0)
|
field(:quotes_count, :integer, default: 0)
|
||||||
|
field(:language, :string)
|
||||||
field(:inReplyTo, ObjectValidators.ObjectID)
|
field(:inReplyTo, ObjectValidators.ObjectID)
|
||||||
field(:quoteUrl, ObjectValidators.ObjectID)
|
field(:quoteUrl, ObjectValidators.ObjectID)
|
||||||
field(:url, ObjectValidators.Uri)
|
field(:url, ObjectValidators.Uri)
|
||||||
|
|
|
@ -8,6 +8,7 @@ defmodule Pleroma.Web.ActivityPub.Transmogrifier do
|
||||||
"""
|
"""
|
||||||
alias Pleroma.Activity
|
alias Pleroma.Activity
|
||||||
alias Pleroma.EctoType.ActivityPub.ObjectValidators
|
alias Pleroma.EctoType.ActivityPub.ObjectValidators
|
||||||
|
alias Pleroma.Language.LanguageDetector
|
||||||
alias Pleroma.Maps
|
alias Pleroma.Maps
|
||||||
alias Pleroma.Object
|
alias Pleroma.Object
|
||||||
alias Pleroma.Object.Containment
|
alias Pleroma.Object.Containment
|
||||||
|
@ -23,6 +24,8 @@ defmodule Pleroma.Web.ActivityPub.Transmogrifier do
|
||||||
alias Pleroma.Workers.TransmogrifierWorker
|
alias Pleroma.Workers.TransmogrifierWorker
|
||||||
|
|
||||||
import Ecto.Query
|
import Ecto.Query
|
||||||
|
import Pleroma.Web.CommonAPI.Utils, only: [get_valid_language: 1]
|
||||||
|
import Pleroma.Web.Utils.Guards, only: [not_empty_string: 1]
|
||||||
|
|
||||||
require Logger
|
require Logger
|
||||||
require Pleroma.Constants
|
require Pleroma.Constants
|
||||||
|
@ -43,6 +46,7 @@ def fix_object(object, options \\ []) do
|
||||||
|> fix_content_map()
|
|> fix_content_map()
|
||||||
|> fix_addressing()
|
|> fix_addressing()
|
||||||
|> fix_summary()
|
|> fix_summary()
|
||||||
|
|> maybe_add_language()
|
||||||
end
|
end
|
||||||
|
|
||||||
def fix_summary(%{"summary" => nil} = object) do
|
def fix_summary(%{"summary" => nil} = object) do
|
||||||
|
@ -389,6 +393,8 @@ def fix_tag(%{"tag" => %{} = tag} = object) do
|
||||||
|
|
||||||
def fix_tag(object), do: object
|
def fix_tag(object), do: object
|
||||||
|
|
||||||
|
def fix_content_map(%{"content" => content} = object) when not_empty_string(content), do: object
|
||||||
|
|
||||||
# content map usually only has one language so this will do for now.
|
# content map usually only has one language so this will do for now.
|
||||||
def fix_content_map(%{"contentMap" => content_map} = object) do
|
def fix_content_map(%{"contentMap" => content_map} = object) do
|
||||||
content_groups = Map.to_list(content_map)
|
content_groups = Map.to_list(content_map)
|
||||||
|
@ -526,6 +532,7 @@ def handle_incoming(
|
||||||
|> fix_type(fetch_options)
|
|> fix_type(fetch_options)
|
||||||
|> fix_in_reply_to(fetch_options)
|
|> fix_in_reply_to(fetch_options)
|
||||||
|> fix_quote_url(fetch_options)
|
|> fix_quote_url(fetch_options)
|
||||||
|
|> maybe_add_language_from_activity(data)
|
||||||
|
|
||||||
data = Map.put(data, "object", object)
|
data = Map.put(data, "object", object)
|
||||||
options = Keyword.put(options, :local, false)
|
options = Keyword.put(options, :local, false)
|
||||||
|
@ -769,6 +776,7 @@ def prepare_object(object) do
|
||||||
|> add_mention_tags
|
|> add_mention_tags
|
||||||
|> add_emoji_tags
|
|> add_emoji_tags
|
||||||
|> add_attributed_to
|
|> add_attributed_to
|
||||||
|
|> maybe_add_content_map
|
||||||
|> prepare_attachments
|
|> prepare_attachments
|
||||||
|> set_conversation
|
|> set_conversation
|
||||||
|> set_reply_to_uri
|
|> set_reply_to_uri
|
||||||
|
@ -814,7 +822,7 @@ def prepare_outgoing(%{"type" => activity_type, "object" => object_id} = data)
|
||||||
data =
|
data =
|
||||||
data
|
data
|
||||||
|> Map.put("object", object)
|
|> Map.put("object", object)
|
||||||
|> Map.merge(Utils.make_json_ld_header())
|
|> Map.merge(Utils.make_json_ld_header(data))
|
||||||
|> Map.delete("bcc")
|
|> Map.delete("bcc")
|
||||||
|
|
||||||
{:ok, data}
|
{:ok, data}
|
||||||
|
@ -829,7 +837,7 @@ def prepare_outgoing(%{"type" => "Update", "object" => %{"type" => objtype} = ob
|
||||||
data =
|
data =
|
||||||
data
|
data
|
||||||
|> Map.put("object", object)
|
|> Map.put("object", object)
|
||||||
|> Map.merge(Utils.make_json_ld_header())
|
|> Map.merge(Utils.make_json_ld_header(data))
|
||||||
|> Map.delete("bcc")
|
|> Map.delete("bcc")
|
||||||
|
|
||||||
{:ok, data}
|
{:ok, data}
|
||||||
|
@ -850,7 +858,7 @@ def prepare_outgoing(%{"type" => "Announce", "actor" => ap_id, "object" => objec
|
||||||
data =
|
data =
|
||||||
data
|
data
|
||||||
|> strip_internal_fields
|
|> strip_internal_fields
|
||||||
|> Map.merge(Utils.make_json_ld_header())
|
|> Map.merge(Utils.make_json_ld_header(data))
|
||||||
|> Map.delete("bcc")
|
|> Map.delete("bcc")
|
||||||
|
|
||||||
{:ok, data}
|
{:ok, data}
|
||||||
|
@ -870,7 +878,7 @@ def prepare_outgoing(%{"type" => "Accept"} = data) do
|
||||||
data =
|
data =
|
||||||
data
|
data
|
||||||
|> Map.put("object", object)
|
|> Map.put("object", object)
|
||||||
|> Map.merge(Utils.make_json_ld_header())
|
|> Map.merge(Utils.make_json_ld_header(data))
|
||||||
|
|
||||||
{:ok, data}
|
{:ok, data}
|
||||||
end
|
end
|
||||||
|
@ -888,7 +896,7 @@ def prepare_outgoing(%{"type" => "Reject"} = data) do
|
||||||
data =
|
data =
|
||||||
data
|
data
|
||||||
|> Map.put("object", object)
|
|> Map.put("object", object)
|
||||||
|> Map.merge(Utils.make_json_ld_header())
|
|> Map.merge(Utils.make_json_ld_header(data))
|
||||||
|
|
||||||
{:ok, data}
|
{:ok, data}
|
||||||
end
|
end
|
||||||
|
@ -899,7 +907,7 @@ def prepare_outgoing(%{"type" => _type} = data) do
|
||||||
data
|
data
|
||||||
|> strip_internal_fields
|
|> strip_internal_fields
|
||||||
|> maybe_fix_object_url
|
|> maybe_fix_object_url
|
||||||
|> Map.merge(Utils.make_json_ld_header())
|
|> Map.merge(Utils.make_json_ld_header(data))
|
||||||
|
|
||||||
{:ok, data}
|
{:ok, data}
|
||||||
end
|
end
|
||||||
|
@ -1085,4 +1093,64 @@ def maybe_fix_user_url(%{"url" => url} = data) when is_map(url) do
|
||||||
def maybe_fix_user_url(data), do: data
|
def maybe_fix_user_url(data), do: data
|
||||||
|
|
||||||
def maybe_fix_user_object(data), do: maybe_fix_user_url(data)
|
def maybe_fix_user_object(data), do: maybe_fix_user_url(data)
|
||||||
|
|
||||||
|
defp maybe_add_content_map(%{"language" => language, "content" => content} = object)
|
||||||
|
when not_empty_string(language) do
|
||||||
|
Map.put(object, "contentMap", Map.put(%{}, language, content))
|
||||||
|
end
|
||||||
|
|
||||||
|
defp maybe_add_content_map(object), do: object
|
||||||
|
|
||||||
|
def maybe_add_language(object) do
|
||||||
|
language =
|
||||||
|
get_language_from_context(object) |> get_valid_language() ||
|
||||||
|
get_language_from_content_map(object) |> get_valid_language() ||
|
||||||
|
get_language_from_content(object) |> get_valid_language()
|
||||||
|
|
||||||
|
if language do
|
||||||
|
Map.put(object, "language", language)
|
||||||
|
else
|
||||||
|
object
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
def maybe_add_language_from_activity(object, activity) do
|
||||||
|
language = get_language_from_context(activity) |> get_valid_language()
|
||||||
|
|
||||||
|
if language do
|
||||||
|
Map.put(object, "language", language)
|
||||||
|
else
|
||||||
|
object
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
defp get_language_from_context(%{"@context" => context}) when is_list(context) do
|
||||||
|
case context
|
||||||
|
|> Enum.find(fn
|
||||||
|
%{"@language" => language} -> language != "und"
|
||||||
|
_ -> nil
|
||||||
|
end) do
|
||||||
|
%{"@language" => language} -> language
|
||||||
|
_ -> nil
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
defp get_language_from_context(_), do: nil
|
||||||
|
|
||||||
|
defp get_language_from_content_map(%{"contentMap" => content_map, "content" => source_content}) do
|
||||||
|
content_groups = Map.to_list(content_map)
|
||||||
|
|
||||||
|
case Enum.find(content_groups, fn {_, content} -> content == source_content end) do
|
||||||
|
{language, _} -> language
|
||||||
|
_ -> nil
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
defp get_language_from_content_map(_), do: nil
|
||||||
|
|
||||||
|
defp get_language_from_content(%{"content" => content}) do
|
||||||
|
LanguageDetector.detect(content)
|
||||||
|
end
|
||||||
|
|
||||||
|
defp get_language_from_content(_), do: nil
|
||||||
end
|
end
|
||||||
|
|
|
@ -19,6 +19,7 @@ defmodule Pleroma.Web.ActivityPub.Utils do
|
||||||
alias Pleroma.Web.Router.Helpers
|
alias Pleroma.Web.Router.Helpers
|
||||||
|
|
||||||
import Ecto.Query
|
import Ecto.Query
|
||||||
|
import Pleroma.Web.Utils.Guards, only: [not_empty_string: 1]
|
||||||
|
|
||||||
require Logger
|
require Logger
|
||||||
require Pleroma.Constants
|
require Pleroma.Constants
|
||||||
|
@ -107,18 +108,24 @@ def maybe_splice_recipient(ap_id, params) do
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
def make_json_ld_header do
|
def make_json_ld_header(data \\ %{}) do
|
||||||
%{
|
%{
|
||||||
"@context" => [
|
"@context" => [
|
||||||
"https://www.w3.org/ns/activitystreams",
|
"https://www.w3.org/ns/activitystreams",
|
||||||
"#{Endpoint.url()}/schemas/litepub-0.1.jsonld",
|
"#{Endpoint.url()}/schemas/litepub-0.1.jsonld",
|
||||||
%{
|
%{
|
||||||
"@language" => "und"
|
"@language" => get_language(data)
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
end
|
end
|
||||||
|
|
||||||
|
defp get_language(%{"language" => language}) when not_empty_string(language) do
|
||||||
|
language
|
||||||
|
end
|
||||||
|
|
||||||
|
defp get_language(_), do: "und"
|
||||||
|
|
||||||
def make_date do
|
def make_date do
|
||||||
DateTime.utc_now() |> DateTime.to_iso8601()
|
DateTime.utc_now() |> DateTime.to_iso8601()
|
||||||
end
|
end
|
||||||
|
|
|
@ -9,7 +9,7 @@ defmodule Pleroma.Web.ActivityPub.ObjectView do
|
||||||
alias Pleroma.Web.ActivityPub.Transmogrifier
|
alias Pleroma.Web.ActivityPub.Transmogrifier
|
||||||
|
|
||||||
def render("object.json", %{object: %Object{} = object}) do
|
def render("object.json", %{object: %Object{} = object}) do
|
||||||
base = Pleroma.Web.ActivityPub.Utils.make_json_ld_header()
|
base = Pleroma.Web.ActivityPub.Utils.make_json_ld_header(object.data)
|
||||||
|
|
||||||
additional = Transmogrifier.prepare_object(object.data)
|
additional = Transmogrifier.prepare_object(object.data)
|
||||||
Map.merge(base, additional)
|
Map.merge(base, additional)
|
||||||
|
@ -17,7 +17,7 @@ def render("object.json", %{object: %Object{} = object}) do
|
||||||
|
|
||||||
def render("object.json", %{object: %Activity{data: %{"type" => activity_type}} = activity})
|
def render("object.json", %{object: %Activity{data: %{"type" => activity_type}} = activity})
|
||||||
when activity_type in ["Create", "Listen"] do
|
when activity_type in ["Create", "Listen"] do
|
||||||
base = Pleroma.Web.ActivityPub.Utils.make_json_ld_header()
|
base = Pleroma.Web.ActivityPub.Utils.make_json_ld_header(activity.data)
|
||||||
object = Object.normalize(activity, fetch: false)
|
object = Object.normalize(activity, fetch: false)
|
||||||
|
|
||||||
additional =
|
additional =
|
||||||
|
@ -28,7 +28,7 @@ def render("object.json", %{object: %Activity{data: %{"type" => activity_type}}
|
||||||
end
|
end
|
||||||
|
|
||||||
def render("object.json", %{object: %Activity{} = activity}) do
|
def render("object.json", %{object: %Activity{} = activity}) do
|
||||||
base = Pleroma.Web.ActivityPub.Utils.make_json_ld_header()
|
base = Pleroma.Web.ActivityPub.Utils.make_json_ld_header(activity.data)
|
||||||
object_id = Object.normalize(activity, id_only: true)
|
object_id = Object.normalize(activity, id_only: true)
|
||||||
|
|
||||||
additional =
|
additional =
|
||||||
|
|
|
@ -410,6 +410,38 @@ def context_operation do
|
||||||
}
|
}
|
||||||
end
|
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
|
def favourites_operation do
|
||||||
%Operation{
|
%Operation{
|
||||||
tags: ["Timelines"],
|
tags: ["Timelines"],
|
||||||
|
@ -800,4 +832,32 @@ defp context do
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
end
|
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
|
end
|
||||||
|
|
|
@ -49,7 +49,6 @@ defmodule Pleroma.Web.ApiSpec.Schemas.Account do
|
||||||
},
|
},
|
||||||
background_image: %Schema{type: :string, nullable: true, format: :uri},
|
background_image: %Schema{type: :string, nullable: true, format: :uri},
|
||||||
birthday: %Schema{type: :string, nullable: true, format: :date},
|
birthday: %Schema{type: :string, nullable: true, format: :date},
|
||||||
chat_token: %Schema{type: :string},
|
|
||||||
is_confirmed: %Schema{
|
is_confirmed: %Schema{
|
||||||
type: :boolean,
|
type: :boolean,
|
||||||
description:
|
description:
|
||||||
|
@ -180,8 +179,6 @@ defmodule Pleroma.Web.ApiSpec.Schemas.Account do
|
||||||
"is_moderator" => false,
|
"is_moderator" => false,
|
||||||
"skip_thread_containment" => false,
|
"skip_thread_containment" => false,
|
||||||
"accepts_chat_messages" => true,
|
"accepts_chat_messages" => true,
|
||||||
"chat_token" =>
|
|
||||||
"SFMyNTY.g3QAAAACZAAEZGF0YW0AAAASOXRLaTNlc2JHN09RZ1oyOTIwZAAGc2lnbmVkbgYARNplS3EB.Mb_Iaqew2bN1I1o79B_iP7encmVCpTKC4OtHZRxdjKc",
|
|
||||||
"unread_conversation_count" => 0,
|
"unread_conversation_count" => 0,
|
||||||
"tags" => [],
|
"tags" => [],
|
||||||
"notification_settings" => %{
|
"notification_settings" => %{
|
||||||
|
|
|
@ -1,45 +0,0 @@
|
||||||
# Pleroma: A lightweight social networking server
|
|
||||||
# Copyright © 2017-2022 Pleroma Authors <https://pleroma.social/>
|
|
||||||
# SPDX-License-Identifier: AGPL-3.0-only
|
|
||||||
|
|
||||||
defmodule Pleroma.Web.UserSocket do
|
|
||||||
use Phoenix.Socket
|
|
||||||
alias Pleroma.User
|
|
||||||
|
|
||||||
## Channels
|
|
||||||
# channel "room:*", Pleroma.Web.RoomChannel
|
|
||||||
channel("chat:*", Pleroma.Web.ShoutChannel)
|
|
||||||
|
|
||||||
# Socket params are passed from the client and can
|
|
||||||
# be used to verify and authenticate a user. After
|
|
||||||
# verification, you can put default assigns into
|
|
||||||
# the socket that will be set for all channels, ie
|
|
||||||
#
|
|
||||||
# {:ok, assign(socket, :user_id, verified_user_id)}
|
|
||||||
#
|
|
||||||
# To deny connection, return `:error`.
|
|
||||||
#
|
|
||||||
# See `Phoenix.Token` documentation for examples in
|
|
||||||
# performing token verification on connect.
|
|
||||||
def connect(%{"token" => token}, socket) do
|
|
||||||
with true <- Pleroma.Config.get([:shout, :enabled]),
|
|
||||||
{:ok, user_id} <- Phoenix.Token.verify(socket, "user socket", token, max_age: 84_600),
|
|
||||||
%User{} = user <- Pleroma.User.get_cached_by_id(user_id) do
|
|
||||||
{:ok, assign(socket, :user_name, user.nickname)}
|
|
||||||
else
|
|
||||||
_e -> :error
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
# Socket id's are topics that allow you to identify all sockets for a given user:
|
|
||||||
#
|
|
||||||
# def id(socket), do: "user_socket:#{socket.assigns.user_id}"
|
|
||||||
#
|
|
||||||
# Would allow you to broadcast a "disconnect" event and terminate
|
|
||||||
# all active sockets and channels for a given user:
|
|
||||||
#
|
|
||||||
# Pleroma.Web.Endpoint.broadcast("user_socket:#{user.id}", "disconnect", %{})
|
|
||||||
#
|
|
||||||
# Returning `nil` makes this socket anonymous.
|
|
||||||
def id(_socket), do: nil
|
|
||||||
end
|
|
|
@ -5,6 +5,7 @@
|
||||||
defmodule Pleroma.Web.CommonAPI.ActivityDraft do
|
defmodule Pleroma.Web.CommonAPI.ActivityDraft do
|
||||||
alias Pleroma.Activity
|
alias Pleroma.Activity
|
||||||
alias Pleroma.Conversation.Participation
|
alias Pleroma.Conversation.Participation
|
||||||
|
alias Pleroma.Language.LanguageDetector
|
||||||
alias Pleroma.Object
|
alias Pleroma.Object
|
||||||
alias Pleroma.Repo
|
alias Pleroma.Repo
|
||||||
alias Pleroma.Web.ActivityPub.Builder
|
alias Pleroma.Web.ActivityPub.Builder
|
||||||
|
@ -37,6 +38,7 @@ defmodule Pleroma.Web.CommonAPI.ActivityDraft do
|
||||||
cc: [],
|
cc: [],
|
||||||
context: nil,
|
context: nil,
|
||||||
sensitive: false,
|
sensitive: false,
|
||||||
|
language: nil,
|
||||||
object: nil,
|
object: nil,
|
||||||
preview?: false,
|
preview?: false,
|
||||||
changes: %{},
|
changes: %{},
|
||||||
|
@ -68,6 +70,7 @@ def create(user, params) do
|
||||||
|> content()
|
|> content()
|
||||||
|> with_valid(&to_and_cc/1)
|
|> with_valid(&to_and_cc/1)
|
||||||
|> with_valid(&context/1)
|
|> with_valid(&context/1)
|
||||||
|
|> with_valid(&language/1)
|
||||||
|> sensitive()
|
|> sensitive()
|
||||||
|> with_valid(&object/1)
|
|> with_valid(&object/1)
|
||||||
|> preview?()
|
|> preview?()
|
||||||
|
@ -284,6 +287,14 @@ defp sensitive(draft) do
|
||||||
%__MODULE__{draft | sensitive: sensitive}
|
%__MODULE__{draft | sensitive: sensitive}
|
||||||
end
|
end
|
||||||
|
|
||||||
|
defp language(draft) do
|
||||||
|
language =
|
||||||
|
Utils.get_valid_language(draft.params[:language]) ||
|
||||||
|
LanguageDetector.detect(draft.content_html <> " " <> draft.summary)
|
||||||
|
|
||||||
|
%__MODULE__{draft | language: language}
|
||||||
|
end
|
||||||
|
|
||||||
defp object(draft) do
|
defp object(draft) do
|
||||||
emoji = Map.merge(Pleroma.Emoji.Formatter.get_emoji_map(draft.full_payload), draft.emoji)
|
emoji = Map.merge(Pleroma.Emoji.Formatter.get_emoji_map(draft.full_payload), draft.emoji)
|
||||||
|
|
||||||
|
@ -324,6 +335,7 @@ defp object(draft) do
|
||||||
})
|
})
|
||||||
|> Map.put("generator", draft.params[:generator])
|
|> Map.put("generator", draft.params[:generator])
|
||||||
|> Map.put("content_type", draft.params[:content_type])
|
|> Map.put("content_type", draft.params[:content_type])
|
||||||
|
|> Map.put("language", draft.language)
|
||||||
|
|
||||||
%__MODULE__{draft | object: object}
|
%__MODULE__{draft | object: object}
|
||||||
end
|
end
|
||||||
|
|
|
@ -23,6 +23,15 @@ defmodule Pleroma.Web.CommonAPI.Utils do
|
||||||
require Logger
|
require Logger
|
||||||
require Pleroma.Constants
|
require Pleroma.Constants
|
||||||
|
|
||||||
|
@supported_locales ~w(
|
||||||
|
aa ab ae af ak am an ar as av ay az ba be bg bh bi bm bn bo br bs ca ce ch co cr cs cu cv cy da
|
||||||
|
de dv dz ee el en eo es et eu fa ff fi fj fo fr fy ga gd gl gn gu gv ha he hi ho hr ht hu hy hz
|
||||||
|
ia id ie ig ii ik io is it iu ja jv ka kg ki kj kk kl km kn ko kr ks ku kv kw ky la lb lg li ln
|
||||||
|
lo lt lu lv mg mh mi mk ml mn mr ms mt my na nb nd ne ng nl nn no nr nv ny oc oj om or os pa pi
|
||||||
|
pl ps pt qu rm rn ro ru rw sa sc sd se sg si sk sl sm sn so sq sr ss st su sv sw ta te tg th ti
|
||||||
|
tk tl tn to tr ts tt tw ty ug uk ur uz ve vi vo wa wo xh yi yo za zh zu ast ckb kab kmr zgh
|
||||||
|
)
|
||||||
|
|
||||||
def attachments_from_ids(%{media_ids: ids, descriptions: desc}) do
|
def attachments_from_ids(%{media_ids: ids, descriptions: desc}) do
|
||||||
attachments_from_ids_descs(ids, desc)
|
attachments_from_ids_descs(ids, desc)
|
||||||
end
|
end
|
||||||
|
@ -497,4 +506,13 @@ def validate_attachments_count(attachments) do
|
||||||
{:error, dgettext("errors", "Too many attachments")}
|
{:error, dgettext("errors", "Too many attachments")}
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
def get_valid_language(language) when is_binary(language) do
|
||||||
|
case language |> String.split("_") |> Enum.at(0) do
|
||||||
|
locale when locale in @supported_locales -> locale
|
||||||
|
_ -> nil
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
def get_valid_language(_), do: nil
|
||||||
end
|
end
|
||||||
|
|
|
@ -9,7 +9,6 @@ defmodule Pleroma.Web.Endpoint do
|
||||||
|
|
||||||
alias Pleroma.Config
|
alias Pleroma.Config
|
||||||
|
|
||||||
socket("/socket", Pleroma.Web.UserSocket)
|
|
||||||
socket("/live", Phoenix.LiveView.Socket)
|
socket("/live", Phoenix.LiveView.Socket)
|
||||||
|
|
||||||
plug(Unplug,
|
plug(Unplug,
|
||||||
|
|
|
@ -157,13 +157,10 @@ defp validate_email_param(_) do
|
||||||
|
|
||||||
@doc "GET /api/v1/accounts/verify_credentials"
|
@doc "GET /api/v1/accounts/verify_credentials"
|
||||||
def verify_credentials(%{assigns: %{user: user}} = conn, _) do
|
def verify_credentials(%{assigns: %{user: user}} = conn, _) do
|
||||||
chat_token = Phoenix.Token.sign(conn, "user socket", user.id)
|
|
||||||
|
|
||||||
render(conn, "show.json",
|
render(conn, "show.json",
|
||||||
user: user,
|
user: user,
|
||||||
for: user,
|
for: user,
|
||||||
with_pleroma_settings: true,
|
with_pleroma_settings: true
|
||||||
with_chat_token: chat_token
|
|
||||||
)
|
)
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|
|
@ -12,6 +12,7 @@ defmodule Pleroma.Web.MastodonAPI.StatusController do
|
||||||
|
|
||||||
alias Pleroma.Activity
|
alias Pleroma.Activity
|
||||||
alias Pleroma.Bookmark
|
alias Pleroma.Bookmark
|
||||||
|
alias Pleroma.Language.Translation
|
||||||
alias Pleroma.Object
|
alias Pleroma.Object
|
||||||
alias Pleroma.Repo
|
alias Pleroma.Repo
|
||||||
alias Pleroma.ScheduledActivity
|
alias Pleroma.ScheduledActivity
|
||||||
|
@ -43,6 +44,8 @@ defmodule Pleroma.Web.MastodonAPI.StatusController do
|
||||||
]
|
]
|
||||||
)
|
)
|
||||||
|
|
||||||
|
plug(OAuthScopesPlug, %{scopes: ["read:statuses"]} when action == :translate)
|
||||||
|
|
||||||
plug(
|
plug(
|
||||||
OAuthScopesPlug,
|
OAuthScopesPlug,
|
||||||
%{scopes: ["write:statuses"]}
|
%{scopes: ["write:statuses"]}
|
||||||
|
@ -84,7 +87,7 @@ defmodule Pleroma.Web.MastodonAPI.StatusController do
|
||||||
%{scopes: ["write:bookmarks"]} when action in [:bookmark, :unbookmark]
|
%{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(
|
plug(
|
||||||
RateLimiter,
|
RateLimiter,
|
||||||
|
@ -455,6 +458,35 @@ def context(%{assigns: %{user: user}} = conn, %{id: id}) do
|
||||||
end
|
end
|
||||||
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),
|
||||||
|
{:visibility, visibility} when visibility in ["public", "unlisted"] <-
|
||||||
|
{:visibility, Visibility.get_visibility(object)},
|
||||||
|
{: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")
|
||||||
|
|
||||||
|
{:visibility, _} ->
|
||||||
|
render_error(conn, :not_found, "Record not found")
|
||||||
|
|
||||||
|
{: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"
|
@doc "GET /api/v1/favourites"
|
||||||
def favourites(%{assigns: %{user: %User{} = user}} = conn, params) do
|
def favourites(%{assigns: %{user: %User{} = user}} = conn, params) do
|
||||||
activities = ActivityPub.fetch_favourites(user, params)
|
activities = ActivityPub.fetch_favourites(user, params)
|
||||||
|
|
|
@ -305,7 +305,6 @@ defp do_render("show.json", %{user: user} = opts) do
|
||||||
|> maybe_put_settings(user, opts[:for], opts)
|
|> maybe_put_settings(user, opts[:for], opts)
|
||||||
|> maybe_put_notification_settings(user, opts[:for])
|
|> maybe_put_notification_settings(user, opts[:for])
|
||||||
|> maybe_put_settings_store(user, opts[:for], opts)
|
|> maybe_put_settings_store(user, opts[:for], opts)
|
||||||
|> maybe_put_chat_token(user, opts[:for], opts)
|
|
||||||
|> maybe_put_activation_status(user, opts[:for])
|
|> maybe_put_activation_status(user, opts[:for])
|
||||||
|> maybe_put_follow_requests_count(user, opts[:for])
|
|> maybe_put_follow_requests_count(user, opts[:for])
|
||||||
|> maybe_put_allow_following_move(user, opts[:for])
|
|> maybe_put_allow_following_move(user, opts[:for])
|
||||||
|
@ -362,15 +361,6 @@ defp maybe_put_settings_store(data, %User{} = user, %User{}, %{
|
||||||
|
|
||||||
defp maybe_put_settings_store(data, _, _, _), do: data
|
defp maybe_put_settings_store(data, _, _, _), do: data
|
||||||
|
|
||||||
defp maybe_put_chat_token(data, %User{id: id}, %User{id: id}, %{
|
|
||||||
with_chat_token: token
|
|
||||||
}) do
|
|
||||||
data
|
|
||||||
|> Kernel.put_in([:pleroma, :chat_token], token)
|
|
||||||
end
|
|
||||||
|
|
||||||
defp maybe_put_chat_token(data, _, _, _), do: data
|
|
||||||
|
|
||||||
defp maybe_put_role(data, %User{show_role: true} = user, _) do
|
defp maybe_put_role(data, %User{show_role: true} = user, _) do
|
||||||
data
|
data
|
||||||
|> Kernel.put_in([:pleroma, :is_admin], user.is_admin)
|
|> Kernel.put_in([:pleroma, :is_admin], user.is_admin)
|
||||||
|
|
|
@ -6,7 +6,9 @@ defmodule Pleroma.Web.MastodonAPI.InstanceView do
|
||||||
use Pleroma.Web, :view
|
use Pleroma.Web, :view
|
||||||
|
|
||||||
alias Pleroma.Config
|
alias Pleroma.Config
|
||||||
|
alias Pleroma.User
|
||||||
alias Pleroma.Web.ActivityPub.MRF
|
alias Pleroma.Web.ActivityPub.MRF
|
||||||
|
alias Pleroma.Web.MastodonAPI
|
||||||
|
|
||||||
@mastodon_api_level "2.7.2"
|
@mastodon_api_level "2.7.2"
|
||||||
|
|
||||||
|
@ -31,6 +33,7 @@ def render("show.json", _) do
|
||||||
registrations: Keyword.get(instance, :registrations_open),
|
registrations: Keyword.get(instance, :registrations_open),
|
||||||
approval_required: Keyword.get(instance, :account_approval_required),
|
approval_required: Keyword.get(instance, :account_approval_required),
|
||||||
configuration: configuration(),
|
configuration: configuration(),
|
||||||
|
contact_account: contact_account(Keyword.get(instance, :contact_username)),
|
||||||
rules: render(__MODULE__, "rules.json"),
|
rules: render(__MODULE__, "rules.json"),
|
||||||
# Extra (not present in Mastodon):
|
# Extra (not present in Mastodon):
|
||||||
max_toot_chars: Keyword.get(instance, :limit),
|
max_toot_chars: Keyword.get(instance, :limit),
|
||||||
|
@ -41,7 +44,6 @@ def render("show.json", _) do
|
||||||
background_upload_limit: Keyword.get(instance, :background_upload_limit),
|
background_upload_limit: Keyword.get(instance, :background_upload_limit),
|
||||||
banner_upload_limit: Keyword.get(instance, :banner_upload_limit),
|
banner_upload_limit: Keyword.get(instance, :banner_upload_limit),
|
||||||
background_image: Pleroma.Web.Endpoint.url() <> Keyword.get(instance, :background_image),
|
background_image: Pleroma.Web.Endpoint.url() <> Keyword.get(instance, :background_image),
|
||||||
shout_limit: Config.get([:shout, :limit]),
|
|
||||||
description_limit: Keyword.get(instance, :description_limit),
|
description_limit: Keyword.get(instance, :description_limit),
|
||||||
pleroma: pleroma_configuration(instance),
|
pleroma: pleroma_configuration(instance),
|
||||||
soapbox: %{
|
soapbox: %{
|
||||||
|
@ -74,7 +76,7 @@ def render("show2.json", _) do
|
||||||
},
|
},
|
||||||
contact: %{
|
contact: %{
|
||||||
email: Keyword.get(instance, :email),
|
email: Keyword.get(instance, :email),
|
||||||
account: nil
|
account: contact_account(Keyword.get(instance, :contact_username))
|
||||||
},
|
},
|
||||||
rules: render(__MODULE__, "rules.json"),
|
rules: render(__MODULE__, "rules.json"),
|
||||||
# Extra (not present in Mastodon):
|
# Extra (not present in Mastodon):
|
||||||
|
@ -120,13 +122,6 @@ def features do
|
||||||
if Config.get([:gopher, :enabled]) do
|
if Config.get([:gopher, :enabled]) do
|
||||||
"gopher"
|
"gopher"
|
||||||
end,
|
end,
|
||||||
# backwards compat
|
|
||||||
if Config.get([:shout, :enabled]) do
|
|
||||||
"chat"
|
|
||||||
end,
|
|
||||||
if Config.get([:shout, :enabled]) do
|
|
||||||
"shout"
|
|
||||||
end,
|
|
||||||
if Config.get([:instance, :allow_relay]) do
|
if Config.get([:instance, :allow_relay]) do
|
||||||
"relay"
|
"relay"
|
||||||
end,
|
end,
|
||||||
|
@ -142,7 +137,10 @@ def features do
|
||||||
if Config.get([:instance, :profile_directory]) do
|
if Config.get([:instance, :profile_directory]) do
|
||||||
"profile_directory"
|
"profile_directory"
|
||||||
end,
|
end,
|
||||||
"pleroma:get:main/ostatus"
|
"pleroma:get:main/ostatus",
|
||||||
|
if Pleroma.Language.Translation.configured?() do
|
||||||
|
"translation"
|
||||||
|
end
|
||||||
]
|
]
|
||||||
|> Enum.filter(& &1)
|
|> Enum.filter(& &1)
|
||||||
end
|
end
|
||||||
|
@ -206,7 +204,7 @@ def configuration2 do
|
||||||
configuration()
|
configuration()
|
||||||
|> Map.merge(%{
|
|> Map.merge(%{
|
||||||
urls: %{streaming: Pleroma.Web.Endpoint.websocket_url()},
|
urls: %{streaming: Pleroma.Web.Endpoint.websocket_url()},
|
||||||
translation: %{enabled: false}
|
translation: %{enabled: Pleroma.Language.Translation.configured?()}
|
||||||
})
|
})
|
||||||
end
|
end
|
||||||
|
|
||||||
|
@ -245,9 +243,24 @@ defp pleroma_configuration2(instance) do
|
||||||
banner_upload_limit: Keyword.get(instance, :banner_upload_limit),
|
banner_upload_limit: Keyword.get(instance, :banner_upload_limit),
|
||||||
background_image:
|
background_image:
|
||||||
Pleroma.Web.Endpoint.url() <> Keyword.get(instance, :background_image),
|
Pleroma.Web.Endpoint.url() <> Keyword.get(instance, :background_image),
|
||||||
shout_limit: Config.get([:shout, :limit]),
|
|
||||||
description_limit: Keyword.get(instance, :description_limit)
|
description_limit: Keyword.get(instance, :description_limit)
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
end
|
end
|
||||||
|
|
||||||
|
defp contact_account(nil), do: nil
|
||||||
|
|
||||||
|
defp contact_account("@" <> username) do
|
||||||
|
contact_account(username)
|
||||||
|
end
|
||||||
|
|
||||||
|
defp contact_account(username) do
|
||||||
|
user = User.get_cached_by_nickname(username)
|
||||||
|
|
||||||
|
if user do
|
||||||
|
MastodonAPI.AccountView.render("show.json", %{user: user, for: nil})
|
||||||
|
else
|
||||||
|
nil
|
||||||
|
end
|
||||||
|
end
|
||||||
end
|
end
|
||||||
|
|
|
@ -225,7 +225,7 @@ def render(
|
||||||
mentions: mentions,
|
mentions: mentions,
|
||||||
tags: reblogged[:tags] || [],
|
tags: reblogged[:tags] || [],
|
||||||
application: build_application(object.data["generator"]),
|
application: build_application(object.data["generator"]),
|
||||||
language: nil,
|
language: object.data["language"],
|
||||||
emojis: [],
|
emojis: [],
|
||||||
pleroma: %{
|
pleroma: %{
|
||||||
local: activity.local,
|
local: activity.local,
|
||||||
|
@ -428,7 +428,7 @@ def render("show.json", %{activity: %{data: %{"object" => _object}} = activity}
|
||||||
mentions: mentions,
|
mentions: mentions,
|
||||||
tags: build_tags(tags),
|
tags: build_tags(tags),
|
||||||
application: build_application(object.data["generator"]),
|
application: build_application(object.data["generator"]),
|
||||||
language: nil,
|
language: object.data["language"],
|
||||||
emojis: build_emojis(object.data["emoji"]),
|
emojis: build_emojis(object.data["emoji"]),
|
||||||
pleroma: %{
|
pleroma: %{
|
||||||
local: activity.local,
|
local: activity.local,
|
||||||
|
@ -667,6 +667,14 @@ def render("context.json", %{activity: activity, activities: activities, user: u
|
||||||
}
|
}
|
||||||
end
|
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
|
def get_reply_to(activity, %{replied_to_activities: replied_to_activities}) do
|
||||||
object = Object.normalize(activity, fetch: false)
|
object = Object.normalize(activity, fetch: false)
|
||||||
|
|
||||||
|
|
|
@ -127,7 +127,7 @@ def decode_url(encoded) do
|
||||||
end
|
end
|
||||||
|
|
||||||
defp signed_url(url) do
|
defp signed_url(url) do
|
||||||
:crypto.mac(:hmac, :sha, Config.get([Endpoint, :secret_key_base]), url)
|
:crypto.mac(:hmac, :sha256, Config.get([Endpoint, :secret_key_base]), url)
|
||||||
end
|
end
|
||||||
|
|
||||||
def filename(url_or_path) do
|
def filename(url_or_path) do
|
||||||
|
|
|
@ -641,6 +641,7 @@ defmodule Pleroma.Web.Router do
|
||||||
post("/statuses/:id/unbookmark", StatusController, :unbookmark)
|
post("/statuses/:id/unbookmark", StatusController, :unbookmark)
|
||||||
post("/statuses/:id/mute", StatusController, :mute_conversation)
|
post("/statuses/:id/mute", StatusController, :mute_conversation)
|
||||||
post("/statuses/:id/unmute", StatusController, :unmute_conversation)
|
post("/statuses/:id/unmute", StatusController, :unmute_conversation)
|
||||||
|
post("/statuses/:id/translate", StatusController, :translate)
|
||||||
|
|
||||||
post("/push/subscription", SubscriptionController, :create)
|
post("/push/subscription", SubscriptionController, :create)
|
||||||
get("/push/subscription", SubscriptionController, :show)
|
get("/push/subscription", SubscriptionController, :show)
|
||||||
|
|
|
@ -1,59 +0,0 @@
|
||||||
# Pleroma: A lightweight social networking server
|
|
||||||
# Copyright © 2017-2022 Pleroma Authors <https://pleroma.social/>
|
|
||||||
# SPDX-License-Identifier: AGPL-3.0-only
|
|
||||||
|
|
||||||
defmodule Pleroma.Web.ShoutChannel do
|
|
||||||
use Phoenix.Channel
|
|
||||||
|
|
||||||
alias Pleroma.User
|
|
||||||
alias Pleroma.Web.MastodonAPI.AccountView
|
|
||||||
alias Pleroma.Web.ShoutChannel.ShoutChannelState
|
|
||||||
|
|
||||||
def join("chat:public", _message, socket) do
|
|
||||||
send(self(), :after_join)
|
|
||||||
{:ok, socket}
|
|
||||||
end
|
|
||||||
|
|
||||||
def handle_info(:after_join, socket) do
|
|
||||||
push(socket, "messages", %{messages: ShoutChannelState.messages()})
|
|
||||||
{:noreply, socket}
|
|
||||||
end
|
|
||||||
|
|
||||||
def handle_in("new_msg", %{"text" => text}, %{assigns: %{user_name: user_name}} = socket) do
|
|
||||||
text = String.trim(text)
|
|
||||||
|
|
||||||
if String.length(text) in 1..Pleroma.Config.get([:shout, :limit]) do
|
|
||||||
author = User.get_cached_by_nickname(user_name)
|
|
||||||
author_json = AccountView.render("show.json", user: author, skip_visibility_check: true)
|
|
||||||
|
|
||||||
message = ShoutChannelState.add_message(%{text: text, author: author_json})
|
|
||||||
|
|
||||||
broadcast!(socket, "new_msg", message)
|
|
||||||
end
|
|
||||||
|
|
||||||
{:noreply, socket}
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
defmodule Pleroma.Web.ShoutChannel.ShoutChannelState do
|
|
||||||
use Agent
|
|
||||||
|
|
||||||
@max_messages 20
|
|
||||||
|
|
||||||
def start_link(_) do
|
|
||||||
Agent.start_link(fn -> %{max_id: 1, messages: []} end, name: __MODULE__)
|
|
||||||
end
|
|
||||||
|
|
||||||
def add_message(message) do
|
|
||||||
Agent.get_and_update(__MODULE__, fn state ->
|
|
||||||
id = state[:max_id] + 1
|
|
||||||
message = Map.put(message, "id", id)
|
|
||||||
messages = [message | state[:messages]] |> Enum.take(@max_messages)
|
|
||||||
{message, %{max_id: id, messages: messages}}
|
|
||||||
end)
|
|
||||||
end
|
|
||||||
|
|
||||||
def messages do
|
|
||||||
Agent.get(__MODULE__, fn state -> state[:messages] |> Enum.reverse() end)
|
|
||||||
end
|
|
||||||
end
|
|
|
@ -7,75 +7,6 @@ defmodule Pleroma.Repo.Migrations.RenameInstanceChat do
|
||||||
|
|
||||||
alias Pleroma.ConfigDB
|
alias Pleroma.ConfigDB
|
||||||
|
|
||||||
@instance_params %{group: :pleroma, key: :instance}
|
def up, do: :noop
|
||||||
@shout_params %{group: :pleroma, key: :shout}
|
def down, do: :noop
|
||||||
@chat_params %{group: :pleroma, key: :chat}
|
|
||||||
|
|
||||||
def up do
|
|
||||||
instance_updated? = maybe_update_instance_key(:up) != :noop
|
|
||||||
chat_updated? = maybe_update_chat_key(:up) != :noop
|
|
||||||
|
|
||||||
case Enum.any?([instance_updated?, chat_updated?]) do
|
|
||||||
true -> :ok
|
|
||||||
false -> :noop
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
def down do
|
|
||||||
instance_updated? = maybe_update_instance_key(:down) != :noop
|
|
||||||
chat_updated? = maybe_update_chat_key(:down) != :noop
|
|
||||||
|
|
||||||
case Enum.any?([instance_updated?, chat_updated?]) do
|
|
||||||
true -> :ok
|
|
||||||
false -> :noop
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
# pleroma.instance.chat_limit -> pleroma.shout.limit
|
|
||||||
defp maybe_update_instance_key(:up) do
|
|
||||||
with %ConfigDB{value: values} <- ConfigDB.get_by_params(@instance_params),
|
|
||||||
limit when is_integer(limit) <- values[:chat_limit] do
|
|
||||||
@shout_params |> Map.put(:value, limit: limit) |> ConfigDB.update_or_create()
|
|
||||||
@instance_params |> Map.put(:subkeys, [":chat_limit"]) |> ConfigDB.delete()
|
|
||||||
else
|
|
||||||
_ ->
|
|
||||||
:noop
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
# pleroma.shout.limit -> pleroma.instance.chat_limit
|
|
||||||
defp maybe_update_instance_key(:down) do
|
|
||||||
with %ConfigDB{value: values} <- ConfigDB.get_by_params(@shout_params),
|
|
||||||
limit when is_integer(limit) <- values[:limit] do
|
|
||||||
@instance_params |> Map.put(:value, chat_limit: limit) |> ConfigDB.update_or_create()
|
|
||||||
@shout_params |> Map.put(:subkeys, [":limit"]) |> ConfigDB.delete()
|
|
||||||
else
|
|
||||||
_ ->
|
|
||||||
:noop
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
# pleroma.chat.enabled -> pleroma.shout.enabled
|
|
||||||
defp maybe_update_chat_key(:up) do
|
|
||||||
with %ConfigDB{value: values} <- ConfigDB.get_by_params(@chat_params),
|
|
||||||
enabled? when is_boolean(enabled?) <- values[:enabled] do
|
|
||||||
@shout_params |> Map.put(:value, enabled: enabled?) |> ConfigDB.update_or_create()
|
|
||||||
@chat_params |> Map.put(:subkeys, [":enabled"]) |> ConfigDB.delete()
|
|
||||||
else
|
|
||||||
_ ->
|
|
||||||
:noop
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
# pleroma.shout.enabled -> pleroma.chat.enabled
|
|
||||||
defp maybe_update_chat_key(:down) do
|
|
||||||
with %ConfigDB{value: values} <- ConfigDB.get_by_params(@shout_params),
|
|
||||||
enabled? when is_boolean(enabled?) <- values[:enabled] do
|
|
||||||
@chat_params |> Map.put(:value, enabled: enabled?) |> ConfigDB.update_or_create()
|
|
||||||
@shout_params |> Map.put(:subkeys, [":enabled"]) |> ConfigDB.delete()
|
|
||||||
else
|
|
||||||
_ ->
|
|
||||||
:noop
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end
|
end
|
||||||
|
|
1
test/fixtures/tesla_mock/deepl-translation.json
vendored
Normal file
1
test/fixtures/tesla_mock/deepl-translation.json
vendored
Normal file
|
@ -0,0 +1 @@
|
||||||
|
{"translations":[{"detected_source_language":"PL","text":"REMOVE THE FOLLOWER!Paste this on your follower. If we get 70% of nk users...they will remove the follower!!!"}]}
|
|
@ -1,80 +1,80 @@
|
||||||
%{
|
{
|
||||||
"@context" => [
|
"@context": [
|
||||||
"https://www.w3.org/ns/activitystreams",
|
"https://www.w3.org/ns/activitystreams",
|
||||||
"https://w3id.org/security/v1",
|
"https://w3id.org/security/v1",
|
||||||
%{
|
{
|
||||||
"addressRegion" => "sc:addressRegion",
|
"addressRegion": "sc:addressRegion",
|
||||||
"timezone" => %{"@id" => "mz:timezone", "@type" => "sc:Text"},
|
"timezone": {"@id": "mz:timezone", "@type": "sc:Text"},
|
||||||
"isOnline" => %{"@id" => "mz:isOnline", "@type" => "sc:Boolean"},
|
"isOnline": {"@id": "mz:isOnline", "@type": "sc:Boolean"},
|
||||||
"pt" => "https://joinpeertube.org/ns#",
|
"pt": "https://joinpeertube.org/ns#",
|
||||||
"manuallyApprovesFollowers" => "as:manuallyApprovesFollowers",
|
"manuallyApprovesFollowers": "as:manuallyApprovesFollowers",
|
||||||
"inLanguage" => "sc:inLanguage",
|
"inLanguage": "sc:inLanguage",
|
||||||
"address" => %{"@id" => "sc:address", "@type" => "sc:PostalAddress"},
|
"address": {"@id": "sc:address", "@type": "sc:PostalAddress"},
|
||||||
"discoverable" => "toot:discoverable",
|
"discoverable": "toot:discoverable",
|
||||||
"repliesModerationOption" => %{
|
"repliesModerationOption": {
|
||||||
"@id" => "mz:repliesModerationOption",
|
"@id": "mz:repliesModerationOption",
|
||||||
"@type" => "mz:repliesModerationOptionType"
|
"@type": "mz:repliesModerationOptionType"
|
||||||
},
|
},
|
||||||
"sc" => "http://schema.org#",
|
"sc": "http://schema.org#",
|
||||||
"mz" => "https://joinmobilizon.org/ns#",
|
"mz": "https://joinmobilizon.org/ns#",
|
||||||
"category" => "sc:category",
|
"category": "sc:category",
|
||||||
"joinModeType" => %{"@id" => "mz:joinModeType", "@type" => "rdfs:Class"},
|
"joinModeType": {"@id": "mz:joinModeType", "@type": "rdfs:Class"},
|
||||||
"Hashtag" => "as:Hashtag",
|
"Hashtag": "as:Hashtag",
|
||||||
"propertyID" => "sc:propertyID",
|
"propertyID": "sc:propertyID",
|
||||||
"PostalAddress" => "sc:PostalAddress",
|
"PostalAddress": "sc:PostalAddress",
|
||||||
"discussions" => %{"@id" => "mz:discussions", "@type" => "@id"},
|
"discussions": {"@id": "mz:discussions", "@type": "@id"},
|
||||||
"remainingAttendeeCapacity" => "sc:remainingAttendeeCapacity",
|
"remainingAttendeeCapacity": "sc:remainingAttendeeCapacity",
|
||||||
"streetAddress" => "sc:streetAddress",
|
"streetAddress": "sc:streetAddress",
|
||||||
"anonymousParticipationEnabled" => %{
|
"anonymousParticipationEnabled": {
|
||||||
"@id" => "mz:anonymousParticipationEnabled",
|
"@id": "mz:anonymousParticipationEnabled",
|
||||||
"@type" => "sc:Boolean"
|
"@type": "sc:Boolean"
|
||||||
},
|
},
|
||||||
"addressLocality" => "sc:addressLocality",
|
"addressLocality": "sc:addressLocality",
|
||||||
"joinMode" => %{"@id" => "mz:joinMode", "@type" => "mz:joinModeType"},
|
"joinMode": {"@id": "mz:joinMode", "@type": "mz:joinModeType"},
|
||||||
"location" => %{"@id" => "sc:location", "@type" => "sc:Place"},
|
"location": {"@id": "sc:location", "@type": "sc:Place"},
|
||||||
"toot" => "http://joinmastodon.org/ns#",
|
"toot": "http://joinmastodon.org/ns#",
|
||||||
"participantCount" => %{
|
"participantCount": {
|
||||||
"@id" => "mz:participantCount",
|
"@id": "mz:participantCount",
|
||||||
"@type" => "sc:Integer"
|
"@type": "sc:Integer"
|
||||||
},
|
},
|
||||||
"uuid" => "sc:identifier",
|
"uuid": "sc:identifier",
|
||||||
"maximumAttendeeCapacity" => "sc:maximumAttendeeCapacity",
|
"maximumAttendeeCapacity": "sc:maximumAttendeeCapacity",
|
||||||
"participationMessage" => %{
|
"participationMessage": {
|
||||||
"@id" => "mz:participationMessage",
|
"@id": "mz:participationMessage",
|
||||||
"@type" => "sc:Text"
|
"@type": "sc:Text"
|
||||||
},
|
},
|
||||||
"openness" => %{"@id" => "mz:openness", "@type" => "@id"},
|
"openness": {"@id": "mz:openness", "@type": "@id"},
|
||||||
"members" => %{"@id" => "mz:members", "@type" => "@id"},
|
"members": {"@id": "mz:members", "@type": "@id"},
|
||||||
"events" => %{"@id" => "mz:events", "@type" => "@id"},
|
"events": {"@id": "mz:events", "@type": "@id"},
|
||||||
"resources" => %{"@id" => "mz:resources", "@type" => "@id"},
|
"resources": {"@id": "mz:resources", "@type": "@id"},
|
||||||
"addressCountry" => "sc:addressCountry",
|
"addressCountry": "sc:addressCountry",
|
||||||
"posts" => %{"@id" => "mz:posts", "@type" => "@id"},
|
"posts": {"@id": "mz:posts", "@type": "@id"},
|
||||||
"commentsEnabled" => %{
|
"commentsEnabled": {
|
||||||
"@id" => "pt:commentsEnabled",
|
"@id": "pt:commentsEnabled",
|
||||||
"@type" => "sc:Boolean"
|
"@type": "sc:Boolean"
|
||||||
},
|
},
|
||||||
"value" => "sc:value",
|
"value": "sc:value",
|
||||||
"PropertyValue" => "sc:PropertyValue",
|
"PropertyValue": "sc:PropertyValue",
|
||||||
"repliesModerationOptionType" => %{
|
"repliesModerationOptionType": {
|
||||||
"@id" => "mz:repliesModerationOptionType",
|
"@id": "mz:repliesModerationOptionType",
|
||||||
"@type" => "rdfs:Class"
|
"@type": "rdfs:Class"
|
||||||
},
|
},
|
||||||
"todos" => %{"@id" => "mz:todos", "@type" => "@id"},
|
"todos": {"@id": "mz:todos", "@type": "@id"},
|
||||||
"ical" => "http://www.w3.org/2002/12/cal/ical#",
|
"ical": "http://www.w3.org/2002/12/cal/ical#",
|
||||||
"postalCode" => "sc:postalCode",
|
"postalCode": "sc:postalCode",
|
||||||
"memberCount" => %{"@id" => "mz:memberCount", "@type" => "sc:Integer"},
|
"memberCount": {"@id": "mz:memberCount", "@type": "sc:Integer"},
|
||||||
"@language" => "und"
|
"@language": "und"
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
"actor" => "https://mobilizon.org/@tcit",
|
"actor": "https://mobilizon.org/@tcit",
|
||||||
"id" => "https://mobilizon.mkljczk.pl/accept/join/fef2a925-cce5-4b8e-b12f-20afe01e5a0f",
|
"id": "https://mobilizon.mkljczk.pl/accept/join/fef2a925-cce5-4b8e-b12f-20afe01e5a0f",
|
||||||
"object" => %{
|
"object": {
|
||||||
"actor" => "https://pleroma.mkljczk.pl/users/mkljczk",
|
"actor": "https://pleroma.mkljczk.pl/users/mkljczk",
|
||||||
"id" => "https://pleroma.mkljczk.pl/activities/7d1f3986-8b2c-48c2-b89e-d27ba8459777",
|
"id": "https://pleroma.mkljczk.pl/activities/7d1f3986-8b2c-48c2-b89e-d27ba8459777",
|
||||||
"object" => "https://mobilizon.mkljczk.pl/events/d9d08e46-81af-4ee9-91e5-1298f49beea9",
|
"object": "https://mobilizon.mkljczk.pl/events/d9d08e46-81af-4ee9-91e5-1298f49beea9",
|
||||||
"participationMessage" => nil,
|
"participationMessage": null,
|
||||||
"published" => "2022-10-07T18:53:53Z",
|
"published": "2022-10-07T18:53:53Z",
|
||||||
"type" => "Join"
|
"type": "Join"
|
||||||
},
|
},
|
||||||
"type" => "Accept"
|
"type": "Accept"
|
||||||
}
|
}
|
|
@ -379,14 +379,4 @@ test "pool timeout" do
|
||||||
"Your config is using old setting name `timeout` instead of `recv_timeout` in pool settings"
|
"Your config is using old setting name `timeout` instead of `recv_timeout` in pool settings"
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
test "check_old_chat_shoutbox/0" do
|
|
||||||
clear_config([:instance, :chat_limit], 1_000)
|
|
||||||
clear_config([:chat, :enabled], true)
|
|
||||||
|
|
||||||
assert capture_log(fn ->
|
|
||||||
DeprecationWarnings.check_old_chat_shoutbox()
|
|
||||||
end) =~
|
|
||||||
"Your config is using the old namespace for the Shoutbox configuration."
|
|
||||||
end
|
|
||||||
end
|
end
|
||||||
|
|
|
@ -110,8 +110,8 @@ test "don't restart if no reboot time settings were changed" do
|
||||||
end
|
end
|
||||||
|
|
||||||
test "on reboot time key" do
|
test "on reboot time key" do
|
||||||
clear_config(:shout)
|
clear_config([:rate_limit, :enabled], true)
|
||||||
insert(:config, key: :shout, value: [enabled: false])
|
insert(:config, key: :rate_limit, value: [enabled: false])
|
||||||
|
|
||||||
# Note that we don't actually restart Pleroma.
|
# Note that we don't actually restart Pleroma.
|
||||||
# See module Restarter.Pleroma
|
# See module Restarter.Pleroma
|
||||||
|
@ -144,10 +144,10 @@ test "on reboot time subkey" do
|
||||||
end
|
end
|
||||||
|
|
||||||
test "don't restart pleroma on reboot time key and subkey if there is false flag" do
|
test "don't restart pleroma on reboot time key and subkey if there is false flag" do
|
||||||
clear_config(:shout)
|
clear_config([:rate_limit, :enabled], true)
|
||||||
clear_config(Pleroma.Captcha)
|
clear_config(Pleroma.Captcha)
|
||||||
|
|
||||||
insert(:config, key: :shout, value: [enabled: false])
|
insert(:config, key: :rate_limit, value: [enabled: false])
|
||||||
insert(:config, key: Pleroma.Captcha, value: [seconds_valid: 60])
|
insert(:config, key: Pleroma.Captcha, value: [seconds_valid: 60])
|
||||||
|
|
||||||
refute String.contains?(
|
refute String.contains?(
|
||||||
|
|
31
test/pleroma/language/language_detector_test.ex
Normal file
31
test/pleroma/language/language_detector_test.ex
Normal file
|
@ -0,0 +1,31 @@
|
||||||
|
# Pleroma: A lightweight social networking server
|
||||||
|
# Copyright © 2017-2022 Pleroma Authors <https://pleroma.social/>
|
||||||
|
# SPDX-License-Identifier: AGPL-3.0-only
|
||||||
|
|
||||||
|
defmodule Pleroma.Language.LanguageDetectorTest do
|
||||||
|
use Pleroma.Web.ConnCase
|
||||||
|
|
||||||
|
alias Pleroma.Language.LanguageDetector
|
||||||
|
|
||||||
|
setup do: clear_config([Pleroma.Language.LanguageDetector, :provider], LanguageDetectorMock)
|
||||||
|
|
||||||
|
test "it detects text language" do
|
||||||
|
detected_language = LanguageDetector.detect("Je viens d'atterrir en Tchéquie.")
|
||||||
|
|
||||||
|
assert detected_language == "fr"
|
||||||
|
end
|
||||||
|
|
||||||
|
test "it returns nil if text is not long enough" do
|
||||||
|
detected_language = LanguageDetector.detect("it returns nil")
|
||||||
|
|
||||||
|
assert detected_language == nil
|
||||||
|
end
|
||||||
|
|
||||||
|
test "it returns nil if no provider specified" do
|
||||||
|
clear_config([Pleroma.Language.LanguageDetector, :provider], nil)
|
||||||
|
|
||||||
|
detected_language = LanguageDetector.detect("this should also return nil")
|
||||||
|
|
||||||
|
assert detected_language == nil
|
||||||
|
end
|
||||||
|
end
|
27
test/pleroma/language/translation/deepl_test.ex
Normal file
27
test/pleroma/language/translation/deepl_test.ex
Normal file
|
@ -0,0 +1,27 @@
|
||||||
|
# Pleroma: A lightweight social networking server
|
||||||
|
# Copyright © 2017-2022 Pleroma Authors <https://pleroma.social/>
|
||||||
|
# SPDX-License-Identifier: AGPL-3.0-only
|
||||||
|
|
||||||
|
defmodule Pleroma.Language.Translation.DeeplTest do
|
||||||
|
use Pleroma.Web.ConnCase
|
||||||
|
|
||||||
|
alias Pleroma.Language.Translation.Deepl
|
||||||
|
|
||||||
|
test "it translates text" do
|
||||||
|
Tesla.Mock.mock_global(fn env -> apply(HttpRequestMock, :request, [env]) end)
|
||||||
|
clear_config([Pleroma.Language.Translation.Deepl, :base_url], "https://api-free.deepl.com")
|
||||||
|
clear_config([Pleroma.Language.Translation.Deepl, :api_key], "API_KEY")
|
||||||
|
|
||||||
|
{:ok, res} =
|
||||||
|
Deepl.translate(
|
||||||
|
"USUNĄĆ ŚLEDZIKA!Wklej to na swojego śledzika. Jeżeli uzbieramy 70% użytkowników nk...to usuną śledzika!!!",
|
||||||
|
"pl",
|
||||||
|
"en"
|
||||||
|
)
|
||||||
|
|
||||||
|
assert %{
|
||||||
|
detected_source_language: "PL",
|
||||||
|
provider: "DeepL"
|
||||||
|
} = res
|
||||||
|
end
|
||||||
|
end
|
32
test/pleroma/language/translation_test.ex
Normal file
32
test/pleroma/language/translation_test.ex
Normal file
|
@ -0,0 +1,32 @@
|
||||||
|
# Pleroma: A lightweight social networking server
|
||||||
|
# Copyright © 2017-2022 Pleroma Authors <https://pleroma.social/>
|
||||||
|
# SPDX-License-Identifier: AGPL-3.0-only
|
||||||
|
|
||||||
|
defmodule Pleroma.Language.TranslationTest do
|
||||||
|
use Pleroma.Web.ConnCase
|
||||||
|
|
||||||
|
alias Pleroma.Language.Translation
|
||||||
|
|
||||||
|
setup do: clear_config([Pleroma.Language.Translation, :provider], TranslationMock)
|
||||||
|
|
||||||
|
test "it translates text" do
|
||||||
|
assert {:ok,
|
||||||
|
%{
|
||||||
|
content: "txet emos",
|
||||||
|
detected_source_language: _,
|
||||||
|
provider: _
|
||||||
|
}} = Translation.translate("some text", "en", "uk")
|
||||||
|
end
|
||||||
|
|
||||||
|
test "it stores translation result in cache" do
|
||||||
|
Translation.translate("some text", "en", "uk")
|
||||||
|
|
||||||
|
assert {:ok, result} =
|
||||||
|
Cachex.get(
|
||||||
|
:translations_cache,
|
||||||
|
"en/uk/#{:crypto.hash(:sha256, "some text") |> Base.encode64()}"
|
||||||
|
)
|
||||||
|
|
||||||
|
assert result.content == "txet emos"
|
||||||
|
end
|
||||||
|
end
|
|
@ -1,56 +0,0 @@
|
||||||
# Pleroma: A lightweight social networking server
|
|
||||||
# Copyright © 2017-2022 Pleroma Authors <https://pleroma.social/>
|
|
||||||
# SPDX-License-Identifier: AGPL-3.0-only
|
|
||||||
|
|
||||||
defmodule Pleroma.Repo.Migrations.RenameInstanceChatTest do
|
|
||||||
use Pleroma.DataCase
|
|
||||||
import Pleroma.Factory
|
|
||||||
import Pleroma.Tests.Helpers
|
|
||||||
alias Pleroma.ConfigDB
|
|
||||||
|
|
||||||
setup do: clear_config([:instance])
|
|
||||||
setup do: clear_config([:chat])
|
|
||||||
setup_all do: require_migration("20200806175913_rename_instance_chat")
|
|
||||||
|
|
||||||
describe "up/0" do
|
|
||||||
test "migrates chat settings to shout", %{migration: migration} do
|
|
||||||
insert(:config, group: :pleroma, key: :instance, value: [chat_limit: 6000])
|
|
||||||
insert(:config, group: :pleroma, key: :chat, value: [enabled: true])
|
|
||||||
|
|
||||||
assert migration.up() == :ok
|
|
||||||
|
|
||||||
assert ConfigDB.get_by_params(%{group: :pleroma, key: :chat}) == nil
|
|
||||||
assert ConfigDB.get_by_params(%{group: :pleroma, key: :instance}) == nil
|
|
||||||
|
|
||||||
assert ConfigDB.get_by_params(%{group: :pleroma, key: :shout}).value == [
|
|
||||||
limit: 6000,
|
|
||||||
enabled: true
|
|
||||||
]
|
|
||||||
end
|
|
||||||
|
|
||||||
test "does nothing when chat settings are not set", %{migration: migration} do
|
|
||||||
assert migration.up() == :noop
|
|
||||||
assert ConfigDB.get_by_params(%{group: :pleroma, key: :chat}) == nil
|
|
||||||
assert ConfigDB.get_by_params(%{group: :pleroma, key: :shout}) == nil
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
describe "down/0" do
|
|
||||||
test "migrates shout settings back to instance and chat", %{migration: migration} do
|
|
||||||
insert(:config, group: :pleroma, key: :shout, value: [limit: 42, enabled: true])
|
|
||||||
|
|
||||||
assert migration.down() == :ok
|
|
||||||
|
|
||||||
assert ConfigDB.get_by_params(%{group: :pleroma, key: :chat}).value == [enabled: true]
|
|
||||||
assert ConfigDB.get_by_params(%{group: :pleroma, key: :instance}).value == [chat_limit: 42]
|
|
||||||
assert ConfigDB.get_by_params(%{group: :pleroma, key: :shout}) == nil
|
|
||||||
end
|
|
||||||
|
|
||||||
test "does nothing when shout settings are not set", %{migration: migration} do
|
|
||||||
assert migration.down() == :noop
|
|
||||||
assert ConfigDB.get_by_params(%{group: :pleroma, key: :chat}) == nil
|
|
||||||
assert ConfigDB.get_by_params(%{group: :pleroma, key: :instance}) == nil
|
|
||||||
assert ConfigDB.get_by_params(%{group: :pleroma, key: :shout}) == nil
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end
|
|
108
test/pleroma/web/activity_pub/mrf/remote_report_policy_test.exs
Normal file
108
test/pleroma/web/activity_pub/mrf/remote_report_policy_test.exs
Normal file
|
@ -0,0 +1,108 @@
|
||||||
|
defmodule Pleroma.Web.ActivityPub.MRF.RemoteReportPolicyTest do
|
||||||
|
use Pleroma.DataCase, async: true
|
||||||
|
|
||||||
|
alias Pleroma.Web.ActivityPub.MRF.RemoteReportPolicy
|
||||||
|
|
||||||
|
setup do
|
||||||
|
clear_config([:mrf_remote_report, :reject_all], false)
|
||||||
|
end
|
||||||
|
|
||||||
|
test "doesn't impact local report" do
|
||||||
|
clear_config([:mrf_remote_report, :reject_anonymous], true)
|
||||||
|
clear_config([:mrf_remote_report, :reject_empty_message], true)
|
||||||
|
|
||||||
|
activity = %{
|
||||||
|
"type" => "Flag",
|
||||||
|
"actor" => "http://localhost:4001/actor"
|
||||||
|
}
|
||||||
|
|
||||||
|
assert {:ok, _} = RemoteReportPolicy.filter(activity)
|
||||||
|
end
|
||||||
|
|
||||||
|
test "rejects anonymous report if `reject_anonymous: true`" do
|
||||||
|
clear_config([:mrf_remote_report, :reject_anonymous], true)
|
||||||
|
clear_config([:mrf_remote_report, :reject_empty_message], true)
|
||||||
|
|
||||||
|
activity = %{
|
||||||
|
"type" => "Flag",
|
||||||
|
"actor" => "https://mastodon.social/actor"
|
||||||
|
}
|
||||||
|
|
||||||
|
assert {:reject, _} = RemoteReportPolicy.filter(activity)
|
||||||
|
end
|
||||||
|
|
||||||
|
test "preserves anonymous report if `reject_anonymous: false`" do
|
||||||
|
clear_config([:mrf_remote_report, :reject_anonymous], false)
|
||||||
|
clear_config([:mrf_remote_report, :reject_empty_message], false)
|
||||||
|
|
||||||
|
activity = %{
|
||||||
|
"type" => "Flag",
|
||||||
|
"actor" => "https://mastodon.social/actor"
|
||||||
|
}
|
||||||
|
|
||||||
|
assert {:ok, _} = RemoteReportPolicy.filter(activity)
|
||||||
|
end
|
||||||
|
|
||||||
|
test "rejects empty message report if `reject_empty_message: true`" do
|
||||||
|
clear_config([:mrf_remote_report, :reject_anonymous], false)
|
||||||
|
clear_config([:mrf_remote_report, :reject_empty_message], true)
|
||||||
|
|
||||||
|
activity = %{
|
||||||
|
"type" => "Flag",
|
||||||
|
"actor" => "https://mastodon.social/users/Gargron"
|
||||||
|
}
|
||||||
|
|
||||||
|
assert {:reject, _} = RemoteReportPolicy.filter(activity)
|
||||||
|
end
|
||||||
|
|
||||||
|
test "rejects empty message report (\"\") if `reject_empty_message: true`" do
|
||||||
|
clear_config([:mrf_remote_report, :reject_anonymous], false)
|
||||||
|
clear_config([:mrf_remote_report, :reject_empty_message], true)
|
||||||
|
|
||||||
|
activity = %{
|
||||||
|
"type" => "Flag",
|
||||||
|
"actor" => "https://mastodon.social/users/Gargron",
|
||||||
|
"content" => ""
|
||||||
|
}
|
||||||
|
|
||||||
|
assert {:reject, _} = RemoteReportPolicy.filter(activity)
|
||||||
|
end
|
||||||
|
|
||||||
|
test "preserves empty message report if `reject_empty_message: false`" do
|
||||||
|
clear_config([:mrf_remote_report, :reject_anonymous], false)
|
||||||
|
clear_config([:mrf_remote_report, :reject_empty_message], false)
|
||||||
|
|
||||||
|
activity = %{
|
||||||
|
"type" => "Flag",
|
||||||
|
"actor" => "https://mastodon.social/users/Gargron"
|
||||||
|
}
|
||||||
|
|
||||||
|
assert {:ok, _} = RemoteReportPolicy.filter(activity)
|
||||||
|
end
|
||||||
|
|
||||||
|
test "preserves anonymous, empty message report with all settings disabled" do
|
||||||
|
clear_config([:mrf_remote_report, :reject_anonymous], false)
|
||||||
|
clear_config([:mrf_remote_report, :reject_empty_message], false)
|
||||||
|
|
||||||
|
activity = %{
|
||||||
|
"type" => "Flag",
|
||||||
|
"actor" => "https://mastodon.social/actor"
|
||||||
|
}
|
||||||
|
|
||||||
|
assert {:ok, _} = RemoteReportPolicy.filter(activity)
|
||||||
|
end
|
||||||
|
|
||||||
|
test "reject remote report if `reject_all: true`" do
|
||||||
|
clear_config([:mrf_remote_report, :reject_all], true)
|
||||||
|
clear_config([:mrf_remote_report, :reject_anonymous], false)
|
||||||
|
clear_config([:mrf_remote_report, :reject_empty_message], false)
|
||||||
|
|
||||||
|
activity = %{
|
||||||
|
"type" => "Flag",
|
||||||
|
"actor" => "https://mastodon.social/users/Gargron",
|
||||||
|
"content" => "Transphobia"
|
||||||
|
}
|
||||||
|
|
||||||
|
assert {:reject, _} = RemoteReportPolicy.filter(activity)
|
||||||
|
end
|
||||||
|
end
|
|
@ -95,7 +95,7 @@ test "it works for incoming Mobilizon join accepts" do
|
||||||
event_author = insert(:user)
|
event_author = insert(:user)
|
||||||
participant = insert(:user)
|
participant = insert(:user)
|
||||||
|
|
||||||
event = insert(:event, %{data: %{"joinMode" => "restricted"}})
|
event = insert(:event, %{user: event_author, data: %{"joinMode" => "restricted"}})
|
||||||
event_activity = insert(:event_activity, event: event)
|
event_activity = insert(:event_activity, event: event)
|
||||||
|
|
||||||
{:ok, join_activity} = CommonAPI.join(participant, event_activity.id)
|
{:ok, join_activity} = CommonAPI.join(participant, event_activity.id)
|
||||||
|
|
|
@ -9,13 +9,10 @@ defmodule Pleroma.Web.ActivityPub.Transmogrifier.JoinHandlingTest do
|
||||||
alias Pleroma.Notification
|
alias Pleroma.Notification
|
||||||
alias Pleroma.Object
|
alias Pleroma.Object
|
||||||
alias Pleroma.Repo
|
alias Pleroma.Repo
|
||||||
alias Pleroma.User
|
|
||||||
alias Pleroma.Web.ActivityPub.Transmogrifier
|
alias Pleroma.Web.ActivityPub.Transmogrifier
|
||||||
alias Pleroma.Web.ActivityPub.Utils
|
|
||||||
|
|
||||||
import Pleroma.Factory
|
import Pleroma.Factory
|
||||||
import Ecto.Query
|
import Ecto.Query
|
||||||
import Mock
|
|
||||||
|
|
||||||
setup_all do
|
setup_all do
|
||||||
Tesla.Mock.mock_global(fn env -> apply(HttpRequestMock, :request, [env]) end)
|
Tesla.Mock.mock_global(fn env -> apply(HttpRequestMock, :request, [env]) end)
|
||||||
|
@ -34,8 +31,7 @@ test "it works for incoming Mobilizon joins" do
|
||||||
|> Map.put("actor", user.ap_id)
|
|> Map.put("actor", user.ap_id)
|
||||||
|> Map.put("object", event.data["id"])
|
|> Map.put("object", event.data["id"])
|
||||||
|
|
||||||
{:ok, %Activity{data: data, local: false} = activity} =
|
{:ok, %Activity{local: false} = activity} = Transmogrifier.handle_incoming(join_data)
|
||||||
Transmogrifier.handle_incoming(join_data)
|
|
||||||
|
|
||||||
event = Object.get_by_id(event.id)
|
event = Object.get_by_id(event.id)
|
||||||
|
|
||||||
|
@ -46,18 +42,17 @@ test "it works for incoming Mobilizon joins" do
|
||||||
end
|
end
|
||||||
|
|
||||||
test "with restricted events, it does create a Join, but not an Accept" do
|
test "with restricted events, it does create a Join, but not an Accept" do
|
||||||
user = insert(:user)
|
[participant, event_author] = insert_pair(:user)
|
||||||
|
|
||||||
event = insert(:event, %{data: %{"joinMode" => "restricted"}})
|
event = insert(:event, %{user: event_author, data: %{"joinMode" => "restricted"}})
|
||||||
|
|
||||||
join_data =
|
join_data =
|
||||||
File.read!("test/fixtures/tesla_mock/mobilizon-event-join.json")
|
File.read!("test/fixtures/tesla_mock/mobilizon-event-join.json")
|
||||||
|> Jason.decode!()
|
|> Jason.decode!()
|
||||||
|> Map.put("actor", user.ap_id)
|
|> Map.put("actor", participant.ap_id)
|
||||||
|> Map.put("object", event.data["id"])
|
|> Map.put("object", event.data["id"])
|
||||||
|
|
||||||
{:ok, %Activity{data: data, local: false} = activity} =
|
{:ok, %Activity{data: data, local: false}} = Transmogrifier.handle_incoming(join_data)
|
||||||
Transmogrifier.handle_incoming(join_data)
|
|
||||||
|
|
||||||
event = Object.get_by_id(event.id)
|
event = Object.get_by_id(event.id)
|
||||||
|
|
||||||
|
@ -74,7 +69,7 @@ test "with restricted events, it does create a Join, but not an Accept" do
|
||||||
|
|
||||||
assert Enum.empty?(accepts)
|
assert Enum.empty?(accepts)
|
||||||
|
|
||||||
[notification] = Notification.for_user(user)
|
[notification] = Notification.for_user(event_author)
|
||||||
assert notification.type == "pleroma:participation_request"
|
assert notification.type == "pleroma:participation_request"
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
|
@ -220,6 +220,36 @@ test "it works for incoming notices with contentMap" do
|
||||||
"<p><span class=\"h-card\"><a href=\"http://localtesting.pleroma.lol/users/lain\" class=\"u-url mention\">@<span>lain</span></a></span></p>"
|
"<p><span class=\"h-card\"><a href=\"http://localtesting.pleroma.lol/users/lain\" class=\"u-url mention\">@<span>lain</span></a></span></p>"
|
||||||
end
|
end
|
||||||
|
|
||||||
|
test "it only uses contentMap if content is not present" do
|
||||||
|
user = insert(:user)
|
||||||
|
|
||||||
|
message = %{
|
||||||
|
"@context" => "https://www.w3.org/ns/activitystreams",
|
||||||
|
"to" => ["https://www.w3.org/ns/activitystreams#Public"],
|
||||||
|
"cc" => [],
|
||||||
|
"type" => "Create",
|
||||||
|
"object" => %{
|
||||||
|
"to" => ["https://www.w3.org/ns/activitystreams#Public"],
|
||||||
|
"cc" => [],
|
||||||
|
"id" => Utils.generate_object_id(),
|
||||||
|
"type" => "Note",
|
||||||
|
"content" => "Hi",
|
||||||
|
"contentMap" => %{
|
||||||
|
"de" => "Hallo",
|
||||||
|
"uk" => "Привіт"
|
||||||
|
},
|
||||||
|
"inReplyTo" => nil,
|
||||||
|
"attributedTo" => user.ap_id
|
||||||
|
},
|
||||||
|
"actor" => user.ap_id
|
||||||
|
}
|
||||||
|
|
||||||
|
{:ok, %Activity{data: data, local: false}} = Transmogrifier.handle_incoming(message)
|
||||||
|
object = Object.normalize(data["object"], fetch: false)
|
||||||
|
|
||||||
|
assert object.data["content"] == "Hi"
|
||||||
|
end
|
||||||
|
|
||||||
test "it works for incoming notices with to/cc not being an array (kroeg)" do
|
test "it works for incoming notices with to/cc not being an array (kroeg)" do
|
||||||
data = File.read!("test/fixtures/kroeg-post-activity.json") |> Jason.decode!()
|
data = File.read!("test/fixtures/kroeg-post-activity.json") |> Jason.decode!()
|
||||||
|
|
||||||
|
@ -355,6 +385,87 @@ test "it correctly processes messages with weirdness in address fields" do
|
||||||
assert ["http://mastodon.example.org/users/admin/followers"] == activity.data["cc"]
|
assert ["http://mastodon.example.org/users/admin/followers"] == activity.data["cc"]
|
||||||
assert ["https://www.w3.org/ns/activitystreams#Public"] == activity.data["to"]
|
assert ["https://www.w3.org/ns/activitystreams#Public"] == activity.data["to"]
|
||||||
end
|
end
|
||||||
|
|
||||||
|
test "it detects language from context" do
|
||||||
|
user = insert(:user)
|
||||||
|
|
||||||
|
message = %{
|
||||||
|
"@context" => ["https://www.w3.org/ns/activitystreams", %{"@language" => "pl"}],
|
||||||
|
"to" => ["https://www.w3.org/ns/activitystreams#Public"],
|
||||||
|
"cc" => [],
|
||||||
|
"type" => "Create",
|
||||||
|
"object" => %{
|
||||||
|
"to" => ["https://www.w3.org/ns/activitystreams#Public"],
|
||||||
|
"cc" => [],
|
||||||
|
"id" => Utils.generate_object_id(),
|
||||||
|
"type" => "Note",
|
||||||
|
"content" => "Szczęść Boże",
|
||||||
|
"attributedTo" => user.ap_id
|
||||||
|
},
|
||||||
|
"actor" => user.ap_id
|
||||||
|
}
|
||||||
|
|
||||||
|
{:ok, %Activity{data: data, local: false}} = Transmogrifier.handle_incoming(message)
|
||||||
|
object = Object.normalize(data["object"], fetch: false)
|
||||||
|
|
||||||
|
assert object.data["language"] == "pl"
|
||||||
|
end
|
||||||
|
|
||||||
|
test "it detects language from contentMap" do
|
||||||
|
user = insert(:user)
|
||||||
|
|
||||||
|
message = %{
|
||||||
|
"@context" => "https://www.w3.org/ns/activitystreams",
|
||||||
|
"to" => ["https://www.w3.org/ns/activitystreams#Public"],
|
||||||
|
"cc" => [],
|
||||||
|
"type" => "Create",
|
||||||
|
"object" => %{
|
||||||
|
"to" => ["https://www.w3.org/ns/activitystreams#Public"],
|
||||||
|
"cc" => [],
|
||||||
|
"id" => Utils.generate_object_id(),
|
||||||
|
"type" => "Note",
|
||||||
|
"content" => "Szczęść Boże",
|
||||||
|
"contentMap" => %{
|
||||||
|
"de" => "Gott segne",
|
||||||
|
"pl" => "Szczęść Boże"
|
||||||
|
},
|
||||||
|
"attributedTo" => user.ap_id
|
||||||
|
},
|
||||||
|
"actor" => user.ap_id
|
||||||
|
}
|
||||||
|
|
||||||
|
{:ok, %Activity{data: data, local: false}} = Transmogrifier.handle_incoming(message)
|
||||||
|
object = Object.normalize(data["object"], fetch: false)
|
||||||
|
|
||||||
|
assert object.data["language"] == "pl"
|
||||||
|
end
|
||||||
|
|
||||||
|
test "it detects language from content" do
|
||||||
|
clear_config([Pleroma.Language.LanguageDetector, :provider], LanguageDetectorMock)
|
||||||
|
|
||||||
|
user = insert(:user)
|
||||||
|
|
||||||
|
message = %{
|
||||||
|
"@context" => ["https://www.w3.org/ns/activitystreams"],
|
||||||
|
"to" => ["https://www.w3.org/ns/activitystreams#Public"],
|
||||||
|
"cc" => [],
|
||||||
|
"type" => "Create",
|
||||||
|
"object" => %{
|
||||||
|
"to" => ["https://www.w3.org/ns/activitystreams#Public"],
|
||||||
|
"cc" => [],
|
||||||
|
"id" => Utils.generate_object_id(),
|
||||||
|
"type" => "Note",
|
||||||
|
"content" => "Dieu vous bénisse, Fédivers.",
|
||||||
|
"attributedTo" => user.ap_id
|
||||||
|
},
|
||||||
|
"actor" => user.ap_id
|
||||||
|
}
|
||||||
|
|
||||||
|
{:ok, %Activity{data: data, local: false}} = Transmogrifier.handle_incoming(message)
|
||||||
|
object = Object.normalize(data["object"], fetch: false)
|
||||||
|
|
||||||
|
assert object.data["language"] == "fr"
|
||||||
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
describe "`handle_incoming/2`, Mastodon format `replies` handling" do
|
describe "`handle_incoming/2`, Mastodon format `replies` handling" do
|
||||||
|
|
|
@ -392,6 +392,18 @@ test "it prepares a quote post" do
|
||||||
assert modified["object"]["quoteUrl"] == quote_id
|
assert modified["object"]["quoteUrl"] == quote_id
|
||||||
assert modified["object"]["quoteUri"] == quote_id
|
assert modified["object"]["quoteUri"] == quote_id
|
||||||
end
|
end
|
||||||
|
|
||||||
|
test "it adds contentMap if language is specified" do
|
||||||
|
user = insert(:user)
|
||||||
|
|
||||||
|
{:ok, activity} = CommonAPI.post(user, %{status: "тест", language: "uk"})
|
||||||
|
|
||||||
|
{:ok, prepared} = Transmogrifier.prepare_outgoing(activity.data)
|
||||||
|
|
||||||
|
assert prepared["object"]["contentMap"] == %{
|
||||||
|
"uk" => "тест"
|
||||||
|
}
|
||||||
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
describe "user upgrade" do
|
describe "user upgrade" do
|
||||||
|
|
|
@ -138,7 +138,8 @@ test "does not adress actor's follower address if the activity is not public", %
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
test "make_json_ld_header/0" do
|
describe "make_json_ld_header/1" do
|
||||||
|
test "makes jsonld header" do
|
||||||
assert Utils.make_json_ld_header() == %{
|
assert Utils.make_json_ld_header() == %{
|
||||||
"@context" => [
|
"@context" => [
|
||||||
"https://www.w3.org/ns/activitystreams",
|
"https://www.w3.org/ns/activitystreams",
|
||||||
|
@ -150,6 +151,19 @@ test "make_json_ld_header/0" do
|
||||||
}
|
}
|
||||||
end
|
end
|
||||||
|
|
||||||
|
test "includes language if specified" do
|
||||||
|
assert Utils.make_json_ld_header(%{"language" => "pl"}) == %{
|
||||||
|
"@context" => [
|
||||||
|
"https://www.w3.org/ns/activitystreams",
|
||||||
|
"http://localhost:4001/schemas/litepub-0.1.jsonld",
|
||||||
|
%{
|
||||||
|
"@language" => "pl"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
describe "get_existing_votes" do
|
describe "get_existing_votes" do
|
||||||
test "fetches existing votes" do
|
test "fetches existing votes" do
|
||||||
user = insert(:user)
|
user = insert(:user)
|
||||||
|
|
|
@ -409,7 +409,7 @@ test "saving config with partial update", %{conn: conn} do
|
||||||
end
|
end
|
||||||
|
|
||||||
test "saving config which need pleroma reboot", %{conn: conn} do
|
test "saving config which need pleroma reboot", %{conn: conn} do
|
||||||
clear_config([:shout, :enabled], true)
|
clear_config([:streamer, :workers], 3)
|
||||||
|
|
||||||
assert conn
|
assert conn
|
||||||
|> put_req_header("content-type", "application/json")
|
|> put_req_header("content-type", "application/json")
|
||||||
|
@ -417,17 +417,17 @@ test "saving config which need pleroma reboot", %{conn: conn} do
|
||||||
"/api/pleroma/admin/config",
|
"/api/pleroma/admin/config",
|
||||||
%{
|
%{
|
||||||
configs: [
|
configs: [
|
||||||
%{group: ":pleroma", key: ":shout", value: [%{"tuple" => [":enabled", true]}]}
|
%{group: ":pleroma", key: ":streamer", value: [%{"tuple" => [":workers", 5]}]}
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
|> json_response_and_validate_schema(200) == %{
|
|> json_response_and_validate_schema(200) == %{
|
||||||
"configs" => [
|
"configs" => [
|
||||||
%{
|
%{
|
||||||
"db" => [":enabled"],
|
"db" => [":workers"],
|
||||||
"group" => ":pleroma",
|
"group" => ":pleroma",
|
||||||
"key" => ":shout",
|
"key" => ":streamer",
|
||||||
"value" => [%{"tuple" => [":enabled", true]}]
|
"value" => [%{"tuple" => [":workers", 5]}]
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
"need_reboot" => true
|
"need_reboot" => true
|
||||||
|
@ -454,7 +454,7 @@ test "saving config which need pleroma reboot", %{conn: conn} do
|
||||||
end
|
end
|
||||||
|
|
||||||
test "update setting which need reboot, don't change reboot flag until reboot", %{conn: conn} do
|
test "update setting which need reboot, don't change reboot flag until reboot", %{conn: conn} do
|
||||||
clear_config([:shout, :enabled], true)
|
clear_config([:streamer, :workers], 3)
|
||||||
|
|
||||||
assert conn
|
assert conn
|
||||||
|> put_req_header("content-type", "application/json")
|
|> put_req_header("content-type", "application/json")
|
||||||
|
@ -462,17 +462,17 @@ test "update setting which need reboot, don't change reboot flag until reboot",
|
||||||
"/api/pleroma/admin/config",
|
"/api/pleroma/admin/config",
|
||||||
%{
|
%{
|
||||||
configs: [
|
configs: [
|
||||||
%{group: ":pleroma", key: ":shout", value: [%{"tuple" => [":enabled", true]}]}
|
%{group: ":pleroma", key: ":streamer", value: [%{"tuple" => [":workers", 5]}]}
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
|> json_response_and_validate_schema(200) == %{
|
|> json_response_and_validate_schema(200) == %{
|
||||||
"configs" => [
|
"configs" => [
|
||||||
%{
|
%{
|
||||||
"db" => [":enabled"],
|
"db" => [":workers"],
|
||||||
"group" => ":pleroma",
|
"group" => ":pleroma",
|
||||||
"key" => ":shout",
|
"key" => ":streamer",
|
||||||
"value" => [%{"tuple" => [":enabled", true]}]
|
"value" => [%{"tuple" => [":workers", 5]}]
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
"need_reboot" => true
|
"need_reboot" => true
|
||||||
|
|
|
@ -1823,7 +1823,6 @@ test "verify_credentials" do
|
||||||
response = json_response_and_validate_schema(conn, 200)
|
response = json_response_and_validate_schema(conn, 200)
|
||||||
|
|
||||||
assert %{"id" => id, "source" => %{"privacy" => "public"}} = response
|
assert %{"id" => id, "source" => %{"privacy" => "public"}} = response
|
||||||
assert response["pleroma"]["chat_token"]
|
|
||||||
assert response["pleroma"]["unread_notifications_count"] == 6
|
assert response["pleroma"]["unread_notifications_count"] == 6
|
||||||
assert id == to_string(user.id)
|
assert id == to_string(user.id)
|
||||||
end
|
end
|
||||||
|
|
|
@ -43,7 +43,6 @@ test "get instance information", %{conn: conn} do
|
||||||
"background_upload_limit" => _,
|
"background_upload_limit" => _,
|
||||||
"banner_upload_limit" => _,
|
"banner_upload_limit" => _,
|
||||||
"background_image" => from_config_background,
|
"background_image" => from_config_background,
|
||||||
"shout_limit" => _,
|
|
||||||
"description_limit" => _,
|
"description_limit" => _,
|
||||||
"rules" => _,
|
"rules" => _,
|
||||||
"pleroma" => %{
|
"pleroma" => %{
|
||||||
|
@ -139,9 +138,19 @@ test "get oauth_consumer_strategies", %{conn: conn} do
|
||||||
assert result["pleroma"]["oauth_consumer_strategies"] == ["keycloak"]
|
assert result["pleroma"]["oauth_consumer_strategies"] == ["keycloak"]
|
||||||
end
|
end
|
||||||
|
|
||||||
test "get instance information v2", %{conn: conn} do
|
test "get instance contact information", %{conn: conn} do
|
||||||
clear_config([:auth, :oauth_consumer_strategies], [])
|
user = insert(:user, %{local: true})
|
||||||
|
|
||||||
|
clear_config([:instance, :contact_username], user.nickname)
|
||||||
|
|
||||||
|
conn = get(conn, "/api/v1/instance")
|
||||||
|
|
||||||
|
assert result = json_response_and_validate_schema(conn, 200)
|
||||||
|
|
||||||
|
assert result["contact_account"]["id"] == user.id
|
||||||
|
end
|
||||||
|
|
||||||
|
test "get instance information v2", %{conn: conn} do
|
||||||
assert get(conn, "/api/v2/instance")
|
assert get(conn, "/api/v2/instance")
|
||||||
|> json_response_and_validate_schema(200)
|
|> json_response_and_validate_schema(200)
|
||||||
end
|
end
|
||||||
|
|
|
@ -2229,4 +2229,64 @@ test "it returns 404 if the user cannot see the post", %{conn: conn} do
|
||||||
|> json_response_and_validate_schema(:not_found)
|
|> json_response_and_validate_schema(:not_found)
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
describe "translating statuses" do
|
||||||
|
setup do: clear_config([Pleroma.Language.Translation, :provider], TranslationMock)
|
||||||
|
|
||||||
|
test "it translates a status to user language" do
|
||||||
|
user = insert(:user, language: "fr")
|
||||||
|
%{conn: conn, user: user} = oauth_access(["read:statuses"], user: user)
|
||||||
|
another_user = insert(:user)
|
||||||
|
|
||||||
|
{:ok, activity} =
|
||||||
|
CommonAPI.post(another_user, %{
|
||||||
|
status: "Cześć!",
|
||||||
|
visibility: "public",
|
||||||
|
language: "pl"
|
||||||
|
})
|
||||||
|
|
||||||
|
response =
|
||||||
|
conn
|
||||||
|
|> post("/api/v1/statuses/#{activity.id}/translate")
|
||||||
|
|> json_response_and_validate_schema(200)
|
||||||
|
|
||||||
|
assert response == %{
|
||||||
|
"content" => "!ćśezC",
|
||||||
|
"detected_source_language" => "pl",
|
||||||
|
"provider" => "TranslationMock"
|
||||||
|
}
|
||||||
|
end
|
||||||
|
|
||||||
|
test "it returns an error if no target language provided" do
|
||||||
|
%{conn: conn, user: user} = oauth_access(["read:statuses"])
|
||||||
|
another_user = insert(:user)
|
||||||
|
|
||||||
|
{:ok, activity} =
|
||||||
|
CommonAPI.post(another_user, %{
|
||||||
|
status: "Cześć!",
|
||||||
|
language: "pl"
|
||||||
|
})
|
||||||
|
|
||||||
|
response =
|
||||||
|
conn
|
||||||
|
|> post("/api/v1/statuses/#{activity.id}/translate")
|
||||||
|
|> json_response_and_validate_schema(400)
|
||||||
|
end
|
||||||
|
|
||||||
|
test "it doesn't translate non-public statuses" do
|
||||||
|
%{conn: conn, user: user} = oauth_access(["read:statuses"])
|
||||||
|
|
||||||
|
{:ok, activity} =
|
||||||
|
CommonAPI.post(user, %{
|
||||||
|
status: "Cześć!",
|
||||||
|
visibility: "private",
|
||||||
|
language: "pl"
|
||||||
|
})
|
||||||
|
|
||||||
|
response =
|
||||||
|
conn
|
||||||
|
|> post("/api/v1/statuses/#{activity.id}/translate")
|
||||||
|
|> json_response_and_validate_schema(404)
|
||||||
|
end
|
||||||
|
end
|
||||||
end
|
end
|
||||||
|
|
|
@ -819,6 +819,16 @@ test "it shows edited_at" do
|
||||||
assert status.edited_at
|
assert status.edited_at
|
||||||
end
|
end
|
||||||
|
|
||||||
|
test "it shows post language" do
|
||||||
|
user = insert(:user)
|
||||||
|
|
||||||
|
{:ok, post} = CommonAPI.post(user, %{status: "Szczęść Boże", language: "pl"})
|
||||||
|
|
||||||
|
status = StatusView.render("show.json", activity: post)
|
||||||
|
|
||||||
|
assert status.language == "pl"
|
||||||
|
end
|
||||||
|
|
||||||
test "with a source object" do
|
test "with a source object" do
|
||||||
note =
|
note =
|
||||||
insert(:note,
|
insert(:note,
|
||||||
|
|
|
@ -144,7 +144,7 @@ test "video attachments have image thumbnail with WxH metadata with Preview Prox
|
||||||
[
|
[
|
||||||
property: "og:image",
|
property: "og:image",
|
||||||
content:
|
content:
|
||||||
"http://localhost:4001/proxy/preview/LzAnlke-l5oZbNzWsrHfprX1rGw/aHR0cHM6Ly9wbGVyb21hLmdvdi9hYm91dC9qdWNoZS53ZWJt/juche.webm"
|
"http://localhost:4001/proxy/preview/YyXfYXyaEAGVHVfKF-HRc4GrFMY03Smi7effZ8WKow8/aHR0cHM6Ly9wbGVyb21hLmdvdi9hYm91dC9qdWNoZS53ZWJt/juche.webm"
|
||||||
], []} in result
|
], []} in result
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|
|
@ -1,41 +0,0 @@
|
||||||
# Pleroma: A lightweight social networking server
|
|
||||||
# Copyright © 2017-2022 Pleroma Authors <https://pleroma.social/>
|
|
||||||
# SPDX-License-Identifier: AGPL-3.0-only
|
|
||||||
|
|
||||||
defmodule Pleroma.Web.ShoutChannelTest do
|
|
||||||
use Pleroma.Web.ChannelCase
|
|
||||||
alias Pleroma.Web.ShoutChannel
|
|
||||||
alias Pleroma.Web.UserSocket
|
|
||||||
|
|
||||||
import Pleroma.Factory
|
|
||||||
|
|
||||||
setup do
|
|
||||||
user = insert(:user)
|
|
||||||
|
|
||||||
{:ok, _, socket} =
|
|
||||||
socket(UserSocket, "", %{user_name: user.nickname})
|
|
||||||
|> subscribe_and_join(ShoutChannel, "chat:public")
|
|
||||||
|
|
||||||
{:ok, socket: socket}
|
|
||||||
end
|
|
||||||
|
|
||||||
test "it broadcasts a message", %{socket: socket} do
|
|
||||||
push(socket, "new_msg", %{"text" => "why is tenshi eating a corndog so cute?"})
|
|
||||||
assert_broadcast("new_msg", %{text: "why is tenshi eating a corndog so cute?"})
|
|
||||||
end
|
|
||||||
|
|
||||||
describe "message lengths" do
|
|
||||||
setup do: clear_config([:shout, :limit])
|
|
||||||
|
|
||||||
test "it ignores messages of length zero", %{socket: socket} do
|
|
||||||
push(socket, "new_msg", %{"text" => ""})
|
|
||||||
refute_broadcast("new_msg", %{text: ""})
|
|
||||||
end
|
|
||||||
|
|
||||||
test "it ignores messages above a certain length", %{socket: socket} do
|
|
||||||
clear_config([:shout, :limit], 2)
|
|
||||||
push(socket, "new_msg", %{"text" => "123"})
|
|
||||||
refute_broadcast("new_msg", %{text: "123"})
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end
|
|
|
@ -1588,6 +1588,15 @@ def post("http://404.site" <> _, _, _, _) do
|
||||||
}}
|
}}
|
||||||
end
|
end
|
||||||
|
|
||||||
|
def post("https://api-free.deepl.com/v2/translate" <> _, _, _, _) do
|
||||||
|
{:ok,
|
||||||
|
%Tesla.Env{
|
||||||
|
status: 200,
|
||||||
|
body: File.read!("test/fixtures/tesla_mock/deepl-translation.json"),
|
||||||
|
headers: [{"content-type", "application/json"}]
|
||||||
|
}}
|
||||||
|
end
|
||||||
|
|
||||||
def post(url, query, body, headers) do
|
def post(url, query, body, headers) do
|
||||||
{:error,
|
{:error,
|
||||||
"Mock response not implemented for POST #{inspect(url)}, #{query}, #{inspect(body)}, #{inspect(headers)}"}
|
"Mock response not implemented for POST #{inspect(url)}, #{query}, #{inspect(body)}, #{inspect(headers)}"}
|
||||||
|
|
18
test/support/language_detector_mock.ex
Normal file
18
test/support/language_detector_mock.ex
Normal file
|
@ -0,0 +1,18 @@
|
||||||
|
# Pleroma: A lightweight social networking server
|
||||||
|
# Copyright © 2017-2022 Pleroma Authors <https://pleroma.social/>
|
||||||
|
# SPDX-License-Identifier: AGPL-3.0-only
|
||||||
|
|
||||||
|
defmodule LanguageDetectorMock do
|
||||||
|
alias Pleroma.Language.LanguageDetector.Provider
|
||||||
|
|
||||||
|
@behaviour Provider
|
||||||
|
|
||||||
|
@impl Provider
|
||||||
|
def missing_dependencies, do: []
|
||||||
|
|
||||||
|
@impl Provider
|
||||||
|
def configured?, do: true
|
||||||
|
|
||||||
|
@impl Provider
|
||||||
|
def detect(_text), do: "fr"
|
||||||
|
end
|
22
test/support/translation_mock.ex
Normal file
22
test/support/translation_mock.ex
Normal file
|
@ -0,0 +1,22 @@
|
||||||
|
# Pleroma: A lightweight social networking server
|
||||||
|
# Copyright © 2017-2022 Pleroma Authors <https://pleroma.social/>
|
||||||
|
# SPDX-License-Identifier: AGPL-3.0-only
|
||||||
|
|
||||||
|
defmodule TranslationMock do
|
||||||
|
alias Pleroma.Language.Translation.Provider
|
||||||
|
|
||||||
|
@behaviour Provider
|
||||||
|
|
||||||
|
@impl Provider
|
||||||
|
def configured?, do: true
|
||||||
|
|
||||||
|
@impl Provider
|
||||||
|
def translate(content, source_language, _target_language) do
|
||||||
|
{:ok,
|
||||||
|
%{
|
||||||
|
content: content |> String.reverse(),
|
||||||
|
detected_source_language: source_language,
|
||||||
|
provider: "TranslationMock"
|
||||||
|
}}
|
||||||
|
end
|
||||||
|
end
|
Loading…
Reference in a new issue