Allow to bite users
Signed-off-by: marcin mikołajczak <git@mkljczk.pl>
This commit is contained in:
parent
639016bdee
commit
f3825b676a
21 changed files with 364 additions and 36 deletions
1
.gitattributes
vendored
1
.gitattributes
vendored
|
@ -2,6 +2,7 @@
|
|||
*.exs diff=elixir
|
||||
|
||||
priv/static/instance/static.css diff=css
|
||||
priv/static/schemas/litepub-0.1.jsonld diff
|
||||
|
||||
# Most of js/css files included in the repo are minified bundles,
|
||||
# and we don't want to search/diff those as text files.
|
||||
|
|
|
@ -74,6 +74,7 @@ def unread_notifications_count(%User{id: user_id}) do
|
|||
reblog
|
||||
poll
|
||||
status
|
||||
bite
|
||||
}
|
||||
|
||||
def changeset(%Notification{} = notification, attrs) do
|
||||
|
@ -367,7 +368,7 @@ def create_notifications(%Activity{data: %{"to" => _, "type" => "Create"}} = act
|
|||
end
|
||||
|
||||
def create_notifications(%Activity{data: %{"type" => type}} = activity)
|
||||
when type in ["Follow", "Like", "Announce", "Move", "EmojiReact", "Flag", "Update"] do
|
||||
when type in ["Follow", "Like", "Announce", "Move", "EmojiReact", "Flag", "Update", "Bite"] do
|
||||
do_create_notifications(activity)
|
||||
end
|
||||
|
||||
|
@ -425,6 +426,9 @@ defp type_from_activity(%{data: %{"type" => type}} = activity) do
|
|||
"Update" ->
|
||||
"update"
|
||||
|
||||
"Bite" ->
|
||||
"bite"
|
||||
|
||||
t ->
|
||||
raise "No notification type for activity type #{t}"
|
||||
end
|
||||
|
@ -501,7 +505,8 @@ def get_notified_from_activity(%Activity{data: %{"type" => type}} = activity, lo
|
|||
"Move",
|
||||
"EmojiReact",
|
||||
"Flag",
|
||||
"Update"
|
||||
"Update",
|
||||
"Bite"
|
||||
] do
|
||||
potential_receiver_ap_ids = get_potential_receiver_ap_ids(activity)
|
||||
|
||||
|
|
|
@ -431,4 +431,15 @@ def unpin(%User{} = user, object) do
|
|||
defp pinned_url(nickname) when is_binary(nickname) do
|
||||
Pleroma.Web.Router.Helpers.activity_pub_url(Pleroma.Web.Endpoint, :pinned, nickname)
|
||||
end
|
||||
|
||||
def bite(%User{} = biting, %User{} = bitten) do
|
||||
{:ok,
|
||||
%{
|
||||
"id" => Utils.generate_activity_id(),
|
||||
"target" => bitten.ap_id,
|
||||
"actor" => biting.ap_id,
|
||||
"type" => "Bite",
|
||||
"to" => [bitten.ap_id]
|
||||
}, []}
|
||||
end
|
||||
end
|
||||
|
|
|
@ -24,6 +24,7 @@ defmodule Pleroma.Web.ActivityPub.ObjectValidator do
|
|||
alias Pleroma.Web.ActivityPub.ObjectValidators.AnswerValidator
|
||||
alias Pleroma.Web.ActivityPub.ObjectValidators.ArticleNotePageValidator
|
||||
alias Pleroma.Web.ActivityPub.ObjectValidators.AudioImageVideoValidator
|
||||
alias Pleroma.Web.ActivityPub.ObjectValidators.BiteValidator
|
||||
alias Pleroma.Web.ActivityPub.ObjectValidators.BlockValidator
|
||||
alias Pleroma.Web.ActivityPub.ObjectValidators.ChatMessageValidator
|
||||
alias Pleroma.Web.ActivityPub.ObjectValidators.CreateChatMessageValidator
|
||||
|
@ -193,7 +194,7 @@ def validate(
|
|||
|
||||
def validate(%{"type" => type} = object, meta)
|
||||
when type in ~w[Accept Reject Follow Update Like EmojiReact Announce
|
||||
ChatMessage Answer] do
|
||||
ChatMessage Answer Bite] do
|
||||
validator =
|
||||
case type do
|
||||
"Accept" -> AcceptRejectValidator
|
||||
|
@ -205,6 +206,7 @@ def validate(%{"type" => type} = object, meta)
|
|||
"Announce" -> AnnounceValidator
|
||||
"ChatMessage" -> ChatMessageValidator
|
||||
"Answer" -> AnswerValidator
|
||||
"Bite" -> BiteValidator
|
||||
end
|
||||
|
||||
with {:ok, object} <-
|
||||
|
|
|
@ -32,7 +32,7 @@ defp validate_data(cng) do
|
|||
|> validate_required([:id, :type, :actor, :to, :object])
|
||||
|> validate_inclusion(:type, ["Accept", "Reject"])
|
||||
|> validate_actor_presence()
|
||||
|> validate_object_presence(allowed_types: ["Follow"])
|
||||
|> validate_object_presence(allowed_types: ["Follow", "Bite"])
|
||||
|> validate_accept_reject_rights()
|
||||
end
|
||||
|
||||
|
@ -44,8 +44,8 @@ def cast_and_validate(data) do
|
|||
|
||||
def validate_accept_reject_rights(cng) do
|
||||
with object_id when is_binary(object_id) <- get_field(cng, :object),
|
||||
%Activity{data: %{"object" => followed_actor}} <- Activity.get_by_ap_id(object_id),
|
||||
true <- followed_actor == get_field(cng, :actor) do
|
||||
%Activity{} = activity <- Activity.get_by_ap_id(object_id),
|
||||
true <- validate_actor(activity, get_field(cng, :actor)) do
|
||||
cng
|
||||
else
|
||||
_e ->
|
||||
|
@ -53,4 +53,12 @@ def validate_accept_reject_rights(cng) do
|
|||
|> add_error(:actor, "can't accept or reject the given activity")
|
||||
end
|
||||
end
|
||||
|
||||
defp validate_actor(%Activity{data: %{"type" => "Follow", "object" => followed_actor}}, actor) do
|
||||
followed_actor == actor
|
||||
end
|
||||
|
||||
defp validate_actor(%Activity{data: %{"type" => "Bite", "target" => biten_actor}}, actor) do
|
||||
biten_actor == actor
|
||||
end
|
||||
end
|
||||
|
|
|
@ -0,0 +1,49 @@
|
|||
# Pleroma: A lightweight social networking server
|
||||
# Copyright © 2017-2024 Pleroma Authors <https://pleroma.social/>
|
||||
# SPDX-License-Identifier: AGPL-3.0-only
|
||||
|
||||
defmodule Pleroma.Web.ActivityPub.ObjectValidators.BiteValidator do
|
||||
use Ecto.Schema
|
||||
|
||||
alias Pleroma.EctoType.ActivityPub.ObjectValidators
|
||||
|
||||
import Ecto.Changeset
|
||||
import Pleroma.Web.ActivityPub.ObjectValidators.CommonValidations
|
||||
|
||||
@primary_key false
|
||||
|
||||
embedded_schema do
|
||||
quote do
|
||||
unquote do
|
||||
import Elixir.Pleroma.Web.ActivityPub.ObjectValidators.CommonFields
|
||||
message_fields()
|
||||
activity_fields()
|
||||
end
|
||||
end
|
||||
|
||||
field(:target, ObjectValidators.ObjectID)
|
||||
end
|
||||
|
||||
def cast_data(data) do
|
||||
%__MODULE__{}
|
||||
|> cast(data |> fix_object(), __schema__(:fields))
|
||||
end
|
||||
|
||||
defp fix_object(data) do
|
||||
Map.put(data, "object", data["target"])
|
||||
end
|
||||
|
||||
defp validate_data(cng) do
|
||||
cng
|
||||
|> validate_required([:id, :type, :actor, :to, :target])
|
||||
|> validate_inclusion(:type, ["Bite"])
|
||||
|> validate_actor_presence()
|
||||
|> validate_actor_presence(field_name: :target)
|
||||
end
|
||||
|
||||
def cast_and_validate(data) do
|
||||
data
|
||||
|> cast_data
|
||||
|> validate_data
|
||||
end
|
||||
end
|
|
@ -42,23 +42,16 @@ def handle(object, meta \\ [])
|
|||
# - Sends a notification
|
||||
@impl true
|
||||
def handle(
|
||||
%{
|
||||
data: %{
|
||||
"actor" => actor,
|
||||
"type" => "Accept",
|
||||
"object" => follow_activity_id
|
||||
}
|
||||
} = object,
|
||||
%{data: %{"actor" => actor, "type" => "Accept", "object" => activity_id}} = object,
|
||||
meta
|
||||
) do
|
||||
with %Activity{actor: follower_id} = follow_activity <-
|
||||
Activity.get_by_ap_id(follow_activity_id),
|
||||
%User{} = followed <- User.get_cached_by_ap_id(actor),
|
||||
%User{} = follower <- User.get_cached_by_ap_id(follower_id),
|
||||
{:ok, follow_activity} <- Utils.update_follow_state_for_all(follow_activity, "accept"),
|
||||
{:ok, _follower, followed} <-
|
||||
FollowingRelationship.update(follower, followed, :follow_accept) do
|
||||
Notification.update_notification_type(followed, follow_activity)
|
||||
with %Activity{} = activity <-
|
||||
Activity.get_by_ap_id(activity_id) do
|
||||
handle_accepted(activity, actor)
|
||||
|
||||
if activity.data["type"] === "Join" do
|
||||
Notification.create_notifications(object)
|
||||
end
|
||||
end
|
||||
|
||||
{:ok, object, meta}
|
||||
|
@ -74,18 +67,14 @@ def handle(
|
|||
data: %{
|
||||
"actor" => actor,
|
||||
"type" => "Reject",
|
||||
"object" => follow_activity_id
|
||||
"object" => activity_id
|
||||
}
|
||||
} = object,
|
||||
meta
|
||||
) do
|
||||
with %Activity{actor: follower_id} = follow_activity <-
|
||||
Activity.get_by_ap_id(follow_activity_id),
|
||||
%User{} = followed <- User.get_cached_by_ap_id(actor),
|
||||
%User{} = follower <- User.get_cached_by_ap_id(follower_id),
|
||||
{:ok, _follow_activity} <- Utils.update_follow_state_for_all(follow_activity, "reject") do
|
||||
FollowingRelationship.update(follower, followed, :follow_reject)
|
||||
Notification.dismiss(follow_activity)
|
||||
with %Activity{} = activity <-
|
||||
Activity.get_by_ap_id(activity_id) do
|
||||
handle_rejected(activity, actor)
|
||||
end
|
||||
|
||||
{:ok, object, meta}
|
||||
|
@ -427,12 +416,93 @@ def handle(%{data: %{"type" => "Remove"} = data} = object, meta) do
|
|||
end
|
||||
end
|
||||
|
||||
# Task this handles
|
||||
# - Bites
|
||||
# - Sends a notification
|
||||
@impl true
|
||||
def handle(
|
||||
%{
|
||||
data: %{
|
||||
"id" => bite_id,
|
||||
"type" => "Bite",
|
||||
"target" => bitten_user,
|
||||
"actor" => biting_user
|
||||
}
|
||||
} = object,
|
||||
meta
|
||||
) do
|
||||
with %User{} = biting <- User.get_cached_by_ap_id(biting_user),
|
||||
%User{} = bitten <- User.get_cached_by_ap_id(bitten_user),
|
||||
{:previous_bite, previous_bite} <-
|
||||
{:previous_bite, Utils.fetch_latest_bite(biting, bitten, object)},
|
||||
{:reverse_bite, reverse_bite} <-
|
||||
{:reverse_bite, Utils.fetch_latest_bite(bitten, biting)},
|
||||
{:can_bite, true, _} <- {:can_bite, can_bite?(previous_bite, reverse_bite), bitten} do
|
||||
if bitten.local do
|
||||
{:ok, accept_data, _} = Builder.accept(bitten, object)
|
||||
{:ok, _activity, _} = Pipeline.common_pipeline(accept_data, local: true)
|
||||
end
|
||||
|
||||
if reverse_bite do
|
||||
Notification.dismiss(reverse_bite)
|
||||
end
|
||||
|
||||
{:ok, notifications} = Notification.create_notifications(object)
|
||||
|
||||
meta
|
||||
|> add_notifications(notifications)
|
||||
else
|
||||
{:can_bite, false, bitten} ->
|
||||
{:ok, reject_data, _} = Builder.reject(bitten, object)
|
||||
{:ok, _activity, _} = Pipeline.common_pipeline(reject_data, local: true)
|
||||
meta
|
||||
|
||||
_ ->
|
||||
meta
|
||||
end
|
||||
|
||||
updated_object = Activity.get_by_ap_id(bite_id)
|
||||
|
||||
{:ok, updated_object, meta}
|
||||
end
|
||||
|
||||
# Nothing to do
|
||||
@impl true
|
||||
def handle(object, meta) do
|
||||
{:ok, object, meta}
|
||||
end
|
||||
|
||||
defp handle_accepted(
|
||||
%Activity{actor: follower_id, data: %{"type" => "Follow"}} = follow_activity,
|
||||
actor
|
||||
) do
|
||||
with %User{} = followed <- User.get_cached_by_ap_id(actor),
|
||||
%User{} = follower <- User.get_cached_by_ap_id(follower_id),
|
||||
{:ok, follow_activity} <- Utils.update_follow_state_for_all(follow_activity, "accept"),
|
||||
{:ok, _follower, followed} <-
|
||||
FollowingRelationship.update(follower, followed, :follow_accept) do
|
||||
Notification.update_notification_type(followed, follow_activity)
|
||||
end
|
||||
end
|
||||
|
||||
defp handle_accepted(_, _), do: nil
|
||||
|
||||
defp handle_rejected(
|
||||
%Activity{actor: follower_id, data: %{"type" => "Follow"}} = follow_activity,
|
||||
actor
|
||||
) do
|
||||
with %User{} = followed <- User.get_cached_by_ap_id(actor),
|
||||
%User{} = follower <- User.get_cached_by_ap_id(follower_id),
|
||||
{:ok, _follow_activity} <- Utils.update_follow_state_for_all(follow_activity, "reject") do
|
||||
FollowingRelationship.update(follower, followed, :follow_reject)
|
||||
Notification.dismiss(follow_activity)
|
||||
end
|
||||
end
|
||||
|
||||
defp handle_rejected(%Activity{data: %{"type" => "Bite"}} = bite_activity, _actor) do
|
||||
Notification.dismiss(bite_activity)
|
||||
end
|
||||
|
||||
defp handle_update_user(
|
||||
%{data: %{"type" => "Update", "object" => updated_object}} = object,
|
||||
meta
|
||||
|
@ -632,4 +702,12 @@ def handle_after_transaction(meta) do
|
|||
|> stream_notifications()
|
||||
|> send_streamables()
|
||||
end
|
||||
|
||||
defp can_bite?(nil, _), do: true
|
||||
|
||||
defp can_bite?(_, nil), do: false
|
||||
|
||||
defp can_bite?(previous_bite, reverse_bite) do
|
||||
NaiveDateTime.diff(previous_bite.inserted_at, reverse_bite.inserted_at) < 0
|
||||
end
|
||||
end
|
||||
|
|
|
@ -488,7 +488,7 @@ def handle_incoming(
|
|||
end
|
||||
|
||||
def handle_incoming(%{"type" => type} = data, _options)
|
||||
when type in ~w{Like EmojiReact Announce Add Remove} do
|
||||
when type in ~w{Like EmojiReact Announce Add Remove Bite} do
|
||||
with :ok <- ObjectValidator.fetch_actor_and_object(data),
|
||||
{:ok, activity, _meta} <-
|
||||
Pipeline.common_pipeline(data, local: false) do
|
||||
|
@ -502,7 +502,7 @@ def handle_incoming(
|
|||
%{"type" => type} = data,
|
||||
_options
|
||||
)
|
||||
when type in ~w{Update Block Follow Accept Reject} do
|
||||
when type in ~w{Update Block Follow Accept Reject Bite} do
|
||||
with {:ok, %User{}} <- ObjectValidator.fetch_actor(data),
|
||||
{:ok, activity, _} <-
|
||||
Pipeline.common_pipeline(data, local: false) do
|
||||
|
|
|
@ -949,4 +949,36 @@ def maybe_handle_group_posts(activity) do
|
|||
|> Enum.reject(&User.blocks?(&1, poster))
|
||||
|> Enum.each(&Pleroma.Web.CommonAPI.repeat(activity.id, &1))
|
||||
end
|
||||
|
||||
def make_bite_data(biting, bitten, activity_id) do
|
||||
%{
|
||||
"type" => "Bite",
|
||||
"actor" => biting.ap_id,
|
||||
"to" => [bitten.ap_id],
|
||||
"target" => bitten.ap_id
|
||||
}
|
||||
|> Maps.put_if_present("id", activity_id)
|
||||
end
|
||||
|
||||
def fetch_latest_bite(
|
||||
%User{ap_id: biting_ap_id},
|
||||
%{ap_id: bitten_ap_id},
|
||||
exclude_activity \\ nil
|
||||
) do
|
||||
"Bite"
|
||||
|> Activity.Queries.by_type()
|
||||
|> where(actor: ^biting_ap_id)
|
||||
|> maybe_exclude_activity_id(exclude_activity)
|
||||
|> Activity.Queries.by_object_id(bitten_ap_id)
|
||||
|> order_by([activity], fragment("? desc nulls last", activity.id))
|
||||
|> limit(1)
|
||||
|> Repo.one()
|
||||
end
|
||||
|
||||
defp maybe_exclude_activity_id(query, nil), do: query
|
||||
|
||||
defp maybe_exclude_activity_id(query, %Activity{id: activity_id}) do
|
||||
query
|
||||
|> where([a], a.id != ^activity_id)
|
||||
end
|
||||
end
|
||||
|
|
|
@ -87,7 +87,7 @@ def spec(opts \\ []) do
|
|||
"x-tagGroups": [
|
||||
%{
|
||||
"name" => "Accounts",
|
||||
"tags" => ["Account actions", "Retrieve account information", "Scrobbles"]
|
||||
"tags" => ["Account actions", "Bites", "Retrieve account information", "Scrobbles"]
|
||||
},
|
||||
%{
|
||||
"name" => "Administration",
|
||||
|
|
33
lib/pleroma/web/api_spec/operations/bite_operation.ex
Normal file
33
lib/pleroma/web/api_spec/operations/bite_operation.ex
Normal file
|
@ -0,0 +1,33 @@
|
|||
# Pleroma: A lightweight social networking server
|
||||
# Copyright © 2017-2024 Pleroma Authors <https://pleroma.social/>
|
||||
# SPDX-License-Identifier: AGPL-3.0-only
|
||||
|
||||
defmodule Pleroma.Web.ApiSpec.BiteOperation do
|
||||
alias OpenApiSpex.Operation
|
||||
alias OpenApiSpex.Schema
|
||||
alias Pleroma.Web.ApiSpec.Schemas.ApiError
|
||||
|
||||
@spec open_api_operation(atom) :: Operation.t()
|
||||
def open_api_operation(action) do
|
||||
operation = String.to_existing_atom("#{action}_operation")
|
||||
apply(__MODULE__, operation, [])
|
||||
end
|
||||
|
||||
def bite_operation do
|
||||
%Operation{
|
||||
tags: ["Bites"],
|
||||
summary: "Bite",
|
||||
operationId: "BiteController.bite",
|
||||
security: [%{"oAuth" => ["write:bites"]}],
|
||||
description: "Bite the given account",
|
||||
parameters: [
|
||||
Operation.parameter(:id, :query, :string, "Bitten account ID")
|
||||
],
|
||||
responses: %{
|
||||
200 => Operation.response("Empty object", "application/json", %Schema{type: :object}),
|
||||
400 => Operation.response("Error", "application/json", ApiError),
|
||||
404 => Operation.response("Error", "application/json", ApiError)
|
||||
}
|
||||
}
|
||||
end
|
||||
end
|
|
@ -211,7 +211,8 @@ defp notification_type do
|
|||
"status",
|
||||
"update",
|
||||
"admin.sign_up",
|
||||
"admin.report"
|
||||
"admin.report",
|
||||
"bite"
|
||||
],
|
||||
description: """
|
||||
The type of event that resulted in the notification.
|
||||
|
@ -229,6 +230,7 @@ defp notification_type do
|
|||
- `update` - A status you boosted has been edited
|
||||
- `admin.sign_up` - Someone signed up (optionally sent to admins)
|
||||
- `admin.report` - A new report has been filed
|
||||
- `bite` - Someone bit you
|
||||
"""
|
||||
}
|
||||
end
|
||||
|
|
|
@ -729,4 +729,11 @@ defp maybe_cancel_jobs(%Activity{id: activity_id}) do
|
|||
end
|
||||
|
||||
defp maybe_cancel_jobs(_), do: {:ok, 0}
|
||||
|
||||
def bite(biting, bitten) do
|
||||
with {:ok, bite_data, _} <- Builder.bite(biting, bitten),
|
||||
{:ok, activity, _} <- Pipeline.common_pipeline(bite_data, local: true) do
|
||||
{:ok, biting, bitten, activity}
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
39
lib/pleroma/web/mastodon_api/controllers/bite_controller.ex
Normal file
39
lib/pleroma/web/mastodon_api/controllers/bite_controller.ex
Normal file
|
@ -0,0 +1,39 @@
|
|||
# Pleroma: A lightweight social networking server
|
||||
# Copyright © 2017-2024 Pleroma Authors <https://pleroma.social/>
|
||||
# SPDX-License-Identifier: AGPL-3.0-only
|
||||
|
||||
defmodule Pleroma.Web.MastodonAPI.BiteController do
|
||||
use Pleroma.Web, :controller
|
||||
|
||||
import Pleroma.Web.ControllerHelper, only: [assign_account_by_id: 2, json_response: 3]
|
||||
|
||||
alias Pleroma.Web.CommonAPI
|
||||
alias Pleroma.Web.Plugs.OAuthScopesPlug
|
||||
# alias Pleroma.Web.Plugs.RateLimiter
|
||||
|
||||
plug(Pleroma.Web.ApiSpec.CastAndValidate, replace_params: false)
|
||||
|
||||
plug(OAuthScopesPlug, %{scopes: ["write:bite"]} when action == :bite)
|
||||
|
||||
# plug(RateLimiter, [name: :relations_actions] when action in @relationship_actions)
|
||||
# plug(RateLimiter, [name: :app_account_creation] when action == :create)
|
||||
|
||||
plug(:assign_account_by_id)
|
||||
|
||||
action_fallback(Pleroma.Web.MastodonAPI.FallbackController)
|
||||
|
||||
defdelegate open_api_operation(action), to: Pleroma.Web.ApiSpec.BiteOperation
|
||||
|
||||
@doc "POST /api/v1/bite"
|
||||
def bite(%{assigns: %{user: %{id: id}, account: %{id: id}}}, _params) do
|
||||
{:error, "Can not bite yourself"}
|
||||
end
|
||||
|
||||
def bite(%{assigns: %{user: biting, account: bitten}} = conn, _) do
|
||||
with {:ok, _, _, _} <- CommonAPI.bite(biting, bitten) do
|
||||
json_response(conn, :ok, %{})
|
||||
else
|
||||
{:error, message} -> json_response(conn, :forbidden, %{error: message})
|
||||
end
|
||||
end
|
||||
end
|
|
@ -35,6 +35,7 @@ defmodule Pleroma.Web.MastodonAPI.NotificationController do
|
|||
poll
|
||||
update
|
||||
status
|
||||
bite
|
||||
}
|
||||
|
||||
# GET /api/v1/notifications
|
||||
|
|
|
@ -145,7 +145,8 @@ def features do
|
|||
end,
|
||||
"pleroma:get:main/ostatus",
|
||||
"pleroma:group_actors",
|
||||
"pleroma:bookmark_folders"
|
||||
"pleroma:bookmark_folders",
|
||||
"pleroma:bites"
|
||||
]
|
||||
|> Enum.filter(& &1)
|
||||
end
|
||||
|
|
|
@ -138,7 +138,7 @@ def render(
|
|||
"pleroma:report" ->
|
||||
put_report(response, activity)
|
||||
|
||||
type when type in ["follow", "follow_request"] ->
|
||||
type when type in ["follow", "follow_request", "bite"] ->
|
||||
response
|
||||
end
|
||||
end
|
||||
|
|
|
@ -74,6 +74,10 @@ def get_nodeinfo("2.0") do
|
|||
features: features,
|
||||
restrictedNicknames: Config.get([Pleroma.User, :restricted_nicknames]),
|
||||
skipThreadContainment: Config.get([:instance, :skip_thread_containment], false)
|
||||
},
|
||||
operations: %{
|
||||
"com.shinolabs.api.bite": ["1.0.0"],
|
||||
"jetzt.mia.ns.activitypub.accept.bite": ["1.0.0"]
|
||||
}
|
||||
}
|
||||
end
|
||||
|
|
|
@ -755,6 +755,8 @@ defmodule Pleroma.Web.Router do
|
|||
|
||||
get("/announcements", AnnouncementController, :index)
|
||||
post("/announcements/:id/dismiss", AnnouncementController, :mark_read)
|
||||
|
||||
post("/bite", BiteController, :bite)
|
||||
end
|
||||
|
||||
scope "/api/v1", Pleroma.Web.MastodonAPI do
|
||||
|
|
|
@ -0,0 +1,52 @@
|
|||
defmodule Pleroma.Repo.Migrations.AddBiteToNotificationsEnum do
|
||||
use Ecto.Migration
|
||||
|
||||
@disable_ddl_transaction true
|
||||
|
||||
def up do
|
||||
"""
|
||||
alter type notification_type add value 'bite'
|
||||
"""
|
||||
|> execute()
|
||||
end
|
||||
|
||||
# 20220605185734_add_update_to_notifications_enum.exs
|
||||
def down do
|
||||
alter table(:notifications) do
|
||||
modify(:type, :string)
|
||||
end
|
||||
|
||||
"""
|
||||
delete from notifications where type = 'bite'
|
||||
"""
|
||||
|> execute()
|
||||
|
||||
"""
|
||||
drop type if exists notification_type
|
||||
"""
|
||||
|> execute()
|
||||
|
||||
"""
|
||||
create type notification_type as enum (
|
||||
'follow',
|
||||
'follow_request',
|
||||
'mention',
|
||||
'move',
|
||||
'pleroma:emoji_reaction',
|
||||
'pleroma:chat_mention',
|
||||
'reblog',
|
||||
'favourite',
|
||||
'pleroma:report',
|
||||
'poll',
|
||||
'update'
|
||||
)
|
||||
"""
|
||||
|> execute()
|
||||
|
||||
"""
|
||||
alter table notifications
|
||||
alter column type type notification_type using (type::notification_type)
|
||||
"""
|
||||
|> execute()
|
||||
end
|
||||
end
|
|
@ -43,7 +43,8 @@
|
|||
"vcard": "http://www.w3.org/2006/vcard/ns#",
|
||||
"formerRepresentations": "litepub:formerRepresentations",
|
||||
"sm": "http://smithereen.software/ns#",
|
||||
"nonAnonymous": "sm:nonAnonymous"
|
||||
"nonAnonymous": "sm:nonAnonymous",
|
||||
"Bite": "https://ns.mia.jetzt/as#Bite"
|
||||
}
|
||||
]
|
||||
}
|
||||
|
|
Loading…
Reference in a new issue