Compare commits

...

9 commits

Author SHA1 Message Date
Duponin de8750d559 Merge branch 'fix-463' into 'develop'
Add mix task to send a notifaction, with a web push

Closes #463

See merge request pleroma/pleroma!3805
2024-04-24 17:37:28 +00:00
lain 50af909c01 Merge branch 'pleroma-card-image-description' into 'develop'
Include image description in status media cards

See merge request pleroma/pleroma!4101
2024-04-19 07:39:05 +00:00
marcin mikołajczak 6f6bede900 Include image description in status media cards
Signed-off-by: marcin mikołajczak <git@mkljczk.pl>
2024-04-19 10:20:31 +04:00
lain 87b8ac3ce6 Merge branch 'receiverworker-error-handling' into 'develop'
ReceiverWorker: Make sure non-{:ok, _} is returned as {:error, …}

See merge request pleroma/pleroma!4100
2024-04-19 06:04:44 +00:00
Haelwenn 71a0373232 Merge branch 'ffmpeg-limiter' into 'develop'
Prevent Media Helper from respawning ffmpeg for bad media

See merge request pleroma/pleroma!4086
2024-04-17 05:47:54 +00:00
Haelwenn (lanodan) Monnier a299ddb10e
ReceiverWorker: Make sure non-{:ok, _} is returned as {:error, …}
Otherwise an error like `{:signature, {:error, {:error, :not_found}}}` ends up considered a success.
2024-04-17 07:43:47 +02:00
Mark Felder 741f22bfe0 MediaHelper: cache failed URLs for 15 minutes to prevent excessive retries 2024-03-19 12:14:03 -04:00
duponin 52fed681d4 Improve the message from the test notification
It makes it more clear it's a test from a Mix task
2022-12-17 18:35:40 +01:00
duponin a810cdee77 Add mix task to send a notifaction, with a web push
Sending only a Push notification can't be done, it needs a Notification.
Creating a notification needs an activity.
Creating an activity needs an object.
That's why it's running throughout the whole process.

It isn't really clean because it's forging a post, it might be
acceptable to perform it from an account like "admin" or similar.

That said, you can't use Pleroma's builtin users ('relay' and
'internal.fetch') because they are invisible and stripped out from
notification creation pipeline.
It would have been a better solution, to have real administrative users.
2022-12-13 21:27:28 +01:00
12 changed files with 157 additions and 20 deletions

View file

@ -0,0 +1 @@
Include image description in status media cards

View file

@ -0,0 +1 @@
Framegrabs with ffmpeg will execute with a 5 second timeout and cache the URLs of failures with a TTL of 15 minutes to prevent excessive retries.

View file

@ -0,0 +1 @@
ReceiverWorker: Make sure non-{:ok, _} is returned as {:error, …}

View file

@ -0,0 +1,91 @@
# Pleroma: A lightweight social networking server
# Copyright © 2017-2022 Pleroma Authors <https://pleroma.social/>
# SPDX-License-Identifier: AGPL-3.0-only
defmodule Mix.Tasks.Pleroma.NotificationTest do
@moduledoc """
Send a test notification under the form of a direct message.
The "From" user must exist and be different from the "To" user.
For technical reasons, you can't just run this task like an usual one, you need to start the task with a node name and connecting to the running Pleroma instance.
Firstly, make sure Pleroma is running with a node name; by default it is not.
To add a node name, you must add the following between `mix` and `phx.server` in whatever you use to start Pleroma: `--sname pleroma-main`.
Example: `mix --sname pleroma-main phx.server`
> Warning: While running with a name can be extremely useful for debug purpose, you should disable it as soon you have done you work, unless you are fluent with Elixir, Erlang and BEAM (and make sure the port `4369/TCP` is firewalled!).
Secondly, to have the mix task to connect to the running Pleroma instance, you also have to give it a node name, it looks almost the same as running Pleroma with a node name: `iex --sname pleroma-mix -S mix pleroma.notification_test ...`
Thirdly, for the `--node` argument, you have to provide the node name you gave to Pleroma (in the example, that's "pleroma-main"), suffixed by `@<your machine hostname>`.
If you don't know what your machine hostname is, just run the command `hostname` in a shell and you will get it.
Example:
```
$ hostname
wired
$ iex --sname pleroma-mix -S mix pleroma.notification_test --node="pleroma-main@wired"
```
Wrapping everything together:
```
mix pleroma.notification_test --from="lain" --to="masami" --node="pleroma-main@wired"
```
"""
use Mix.Task
import Mix.Pleroma
def run(args) do
start_pleroma()
{options, _, _} =
OptionParser.parse(
args,
strict: [
from: :string,
to: :string,
node: :string
]
)
if Node.self() == :nonode@nohost do
shell_error("It seems like you didn't start this mix task with a node name.")
shell_error("Don't worry, run 'mix help pleroma.notification_test' to get help.")
System.halt()
end
if not Keyword.has_key?(options, :from) do
shell_error("I need '--from' to be set!")
System.halt()
end
if not Keyword.has_key?(options, :to) do
shell_error("I need '--to' to be set!")
System.halt()
end
if not Keyword.has_key?(options, :node) do
shell_error("I need '--node' to be set!")
System.halt()
end
node = options |> Keyword.get(:node) |> String.to_atom()
Node.connect(node)
# A spawn_link would be much better, but I don't want to run an IEx shell
# after running this task (hence the `System.halt/1`). Anyway, the spawned
# function should not run forever, it's a oneshot function, if it fails,
# it won't last forever, that's not like a GenServer or anything, right?
Node.spawn(node, Pleroma.Notification, :test_notification, [
Keyword.get(options, :from),
Keyword.get(options, :to)
])
shell_info("Done")
System.halt()
end
end

View file

@ -156,6 +156,7 @@ defmodule Pleroma.Application do
build_cachex("web_resp", limit: 2500),
build_cachex("emoji_packs", expiration: emoji_packs_expiration(), limit: 10),
build_cachex("failed_proxy_url", limit: 2500),
build_cachex("failed_media_helper_url", default_ttl: :timer.minutes(15), limit: 2_500),
build_cachex("banned_urls", default_ttl: :timer.hours(24 * 30), limit: 5_000),
build_cachex("chat_message_id_idempotency_key",
expiration: chat_message_id_idempotency_key_expiration(),

View file

@ -12,6 +12,8 @@ defmodule Pleroma.Helpers.MediaHelper do
require Logger
@cachex Pleroma.Config.get([:cachex, :provider], Cachex)
def missing_dependencies do
Enum.reduce([ffmpeg: "ffmpeg"], [], fn {sym, executable}, acc ->
if Pleroma.Utils.command_available?(executable) do
@ -43,29 +45,40 @@ defmodule Pleroma.Helpers.MediaHelper do
@spec video_framegrab(String.t()) :: {:ok, binary()} | {:error, any()}
def video_framegrab(url) do
with executable when is_binary(executable) <- System.find_executable("ffmpeg"),
false <- @cachex.exists?(:failed_media_helper_cache, url),
{:ok, env} <- HTTP.get(url, [], pool: :media),
{:ok, pid} <- StringIO.open(env.body) do
body_stream = IO.binstream(pid, 1)
result =
Exile.stream!(
[
executable,
"-i",
"pipe:0",
"-vframes",
"1",
"-f",
"mjpeg",
"pipe:1"
],
input: body_stream,
ignore_epipe: true,
stderr: :disable
)
|> Enum.into(<<>>)
task =
Task.async(fn ->
Exile.stream!(
[
executable,
"-i",
"pipe:0",
"-vframes",
"1",
"-f",
"mjpeg",
"pipe:1"
],
input: body_stream,
ignore_epipe: true,
stderr: :disable
)
|> Enum.into(<<>>)
end)
{:ok, result}
case Task.yield(task, 5_000) do
nil ->
Task.shutdown(task)
@cachex.put(:failed_media_helper_cache, url, nil)
{:error, {:ffmpeg, :timeout}}
result ->
{:ok, result}
end
else
nil -> {:error, {:ffmpeg, :command_not_found}}
{:error, _} = error -> error

View file

@ -748,4 +748,24 @@ defmodule Pleroma.Notification do
)
|> Repo.update_all(set: [seen: true])
end
@doc """
This function should never be used for production, but only for debug purpose.
I would really prefer keeping it in the Mix.Task but these aren't available
during the runtime, but all my trials were unsuccessful.
"""
def test_notification(from, to) do
from = Pleroma.User.get_by_nickname(from)
to = Pleroma.User.get_by_nickname(to)
{:ok, activity} =
Pleroma.Web.CommonAPI.post(from, %{
:content_type => "text/plain",
:status => "This is a test notification made from a mix task.",
:visibility => "direct",
"source" => "The Wired"
})
Pleroma.Notification.create_notification(activity, to)
end
end

View file

@ -58,6 +58,10 @@ defmodule Pleroma.Web.ApiSpec.Schemas.Status do
format: :uri,
description: "Preview thumbnail"
},
image_description: %Schema{
type: :string,
description: "Alternate text that describes what is in the thumbnail"
},
title: %Schema{type: :string, description: "Title of linked resource"},
description: %Schema{type: :string, description: "Description of preview"}
}

View file

@ -589,6 +589,7 @@ defmodule Pleroma.Web.MastodonAPI.StatusView do
provider_url: page_url_data.scheme <> "://" <> page_url_data.host,
url: page_url,
image: image_url,
image_description: rich_media["image:alt"] || "",
title: rich_media["title"] || "",
description: rich_media["description"] || "",
pleroma: %{

View file

@ -52,7 +52,8 @@ defmodule Pleroma.Workers.ReceiverWorker do
{:error, {:reject, reason}} -> {:cancel, reason}
{:signature, false} -> {:cancel, :invalid_signature}
{:error, {:error, reason = "Object has been deleted"}} -> {:cancel, reason}
e -> e
{:error, _} = e -> e
e -> {:error, e}
end
end
end

View file

@ -1717,6 +1717,7 @@ defmodule Pleroma.Web.MastodonAPI.StatusControllerTest do
card_data = %{
"image" => "http://ia.media-imdb.com/images/rock.jpg",
"image_description" => "",
"provider_name" => "example.com",
"provider_url" => "https://example.com",
"title" => "The Rock",
@ -1770,6 +1771,7 @@ defmodule Pleroma.Web.MastodonAPI.StatusControllerTest do
"title" => "Pleroma",
"description" => "",
"image" => nil,
"image_description" => "",
"provider_name" => "example.com",
"provider_url" => "https://example.com",
"url" => "https://example.com/ogp-missing-data",

View file

@ -773,6 +773,7 @@ defmodule Pleroma.Web.MastodonAPI.StatusViewTest do
page_url = "http://example.com"
card = %{
"image:alt" => "Example image description",
url: page_url,
site_name: "Example site name",
title: "Example website",
@ -780,7 +781,7 @@ defmodule Pleroma.Web.MastodonAPI.StatusViewTest do
description: "Example description"
}
%{provider_name: "example.com"} =
%{provider_name: "example.com", image_description: "Example image description"} =
StatusView.render("card.json", %{page_url: page_url, rich_media: card})
end