diff options
Diffstat (limited to 'lib')
| -rw-r--r-- | lib/pleroma/bbs/handler.ex | 4 | ||||
| -rw-r--r-- | lib/pleroma/scheduled_activity.ex | 2 | ||||
| -rw-r--r-- | lib/pleroma/user/welcome_message.ex | 4 | ||||
| -rw-r--r-- | lib/pleroma/web/admin_api/admin_api_controller.ex | 9 | ||||
| -rw-r--r-- | lib/pleroma/web/api_spec/operations/status_operation.ex | 502 | ||||
| -rw-r--r-- | lib/pleroma/web/api_spec/schemas/status.ex | 198 | ||||
| -rw-r--r-- | lib/pleroma/web/api_spec/schemas/visibility_scope.ex | 2 | ||||
| -rw-r--r-- | lib/pleroma/web/common_api/activity_draft.ex | 22 | ||||
| -rw-r--r-- | lib/pleroma/web/common_api/common_api.ex | 29 | ||||
| -rw-r--r-- | lib/pleroma/web/common_api/utils.ex | 18 | ||||
| -rw-r--r-- | lib/pleroma/web/mastodon_api/controllers/status_controller.ex | 75 | ||||
| -rw-r--r-- | lib/pleroma/web/mastodon_api/views/account_view.ex | 6 | ||||
| -rw-r--r-- | lib/pleroma/workers/scheduled_activity_worker.ex | 2 | 
13 files changed, 757 insertions, 116 deletions
diff --git a/lib/pleroma/bbs/handler.ex b/lib/pleroma/bbs/handler.ex index c7bc8ef6c..12d64c2fe 100644 --- a/lib/pleroma/bbs/handler.ex +++ b/lib/pleroma/bbs/handler.ex @@ -66,7 +66,7 @@ defmodule Pleroma.BBS.Handler do      with %Activity{} <- Activity.get_by_id(activity_id),           {:ok, _activity} <- -           CommonAPI.post(user, %{"status" => rest, "in_reply_to_status_id" => activity_id}) do +           CommonAPI.post(user, %{status: rest, in_reply_to_status_id: activity_id}) do        IO.puts("Replied!")      else        _e -> IO.puts("Could not reply...") @@ -78,7 +78,7 @@ defmodule Pleroma.BBS.Handler do    def handle_command(%{user: user} = state, "p " <> text) do      text = String.trim(text) -    with {:ok, _activity} <- CommonAPI.post(user, %{"status" => text}) do +    with {:ok, _activity} <- CommonAPI.post(user, %{status: text}) do        IO.puts("Posted!")      else        _e -> IO.puts("Could not post...") diff --git a/lib/pleroma/scheduled_activity.ex b/lib/pleroma/scheduled_activity.ex index 8ff06a462..0937cb7db 100644 --- a/lib/pleroma/scheduled_activity.ex +++ b/lib/pleroma/scheduled_activity.ex @@ -40,7 +40,7 @@ defmodule Pleroma.ScheduledActivity do           %{changes: %{params: %{"media_ids" => media_ids} = params}} = changeset         )         when is_list(media_ids) do -    media_attachments = Utils.attachments_from_ids(%{"media_ids" => media_ids}) +    media_attachments = Utils.attachments_from_ids(%{media_ids: media_ids})      params =        params diff --git a/lib/pleroma/user/welcome_message.ex b/lib/pleroma/user/welcome_message.ex index f0ac8ebae..f8f520285 100644 --- a/lib/pleroma/user/welcome_message.ex +++ b/lib/pleroma/user/welcome_message.ex @@ -10,8 +10,8 @@ defmodule Pleroma.User.WelcomeMessage do      with %User{} = sender_user <- welcome_user(),           message when is_binary(message) <- welcome_message() do        CommonAPI.post(sender_user, %{ -        "visibility" => "direct", -        "status" => "@#{user.nickname}\n#{message}" +        visibility: "direct", +        status: "@#{user.nickname}\n#{message}"        })      else        _ -> {:ok, nil} diff --git a/lib/pleroma/web/admin_api/admin_api_controller.ex b/lib/pleroma/web/admin_api/admin_api_controller.ex index 9f1fd3aeb..9821173d0 100644 --- a/lib/pleroma/web/admin_api/admin_api_controller.ex +++ b/lib/pleroma/web/admin_api/admin_api_controller.ex @@ -844,15 +844,20 @@ defmodule Pleroma.Web.AdminAPI.AdminAPIController do    end    def status_update(%{assigns: %{user: admin}} = conn, %{"id" => id} = params) do +    params = +      params +      |> Map.take(["sensitive", "visibility"]) +      |> Map.new(fn {key, value} -> {String.to_existing_atom(key), value} end) +      with {:ok, activity} <- CommonAPI.update_activity_scope(id, params) do -      {:ok, sensitive} = Ecto.Type.cast(:boolean, params["sensitive"]) +      {:ok, sensitive} = Ecto.Type.cast(:boolean, params[:sensitive])        ModerationLog.insert_log(%{          action: "status_update",          actor: admin,          subject: activity,          sensitive: sensitive, -        visibility: params["visibility"] +        visibility: params[:visibility]        })        conn diff --git a/lib/pleroma/web/api_spec/operations/status_operation.ex b/lib/pleroma/web/api_spec/operations/status_operation.ex new file mode 100644 index 000000000..2c28b23aa --- /dev/null +++ b/lib/pleroma/web/api_spec/operations/status_operation.ex @@ -0,0 +1,502 @@ +# Pleroma: A lightweight social networking server +# Copyright © 2017-2020 Pleroma Authors <https://pleroma.social/> +# SPDX-License-Identifier: AGPL-3.0-only + +defmodule Pleroma.Web.ApiSpec.StatusOperation do +  alias OpenApiSpex.Operation +  alias OpenApiSpex.Schema +  alias Pleroma.Web.ApiSpec.AccountOperation +  alias Pleroma.Web.ApiSpec.Schemas.ApiError +  alias Pleroma.Web.ApiSpec.Schemas.BooleanLike +  alias Pleroma.Web.ApiSpec.Schemas.FlakeID +  alias Pleroma.Web.ApiSpec.Schemas.ScheduledStatus +  alias Pleroma.Web.ApiSpec.Schemas.Status +  alias Pleroma.Web.ApiSpec.Schemas.VisibilityScope + +  import Pleroma.Web.ApiSpec.Helpers + +  def open_api_operation(action) do +    operation = String.to_existing_atom("#{action}_operation") +    apply(__MODULE__, operation, []) +  end + +  def index_operation do +    %Operation{ +      tags: ["Statuses"], +      summary: "Get multiple statuses by IDs", +      security: [%{"oAuth" => ["read:statuses"]}], +      parameters: [ +        Operation.parameter( +          :ids, +          :query, +          %Schema{type: :array, items: FlakeID}, +          "Array of account IDs" +        ) +      ], +      operationId: "StatusController.index", +      responses: %{ +        200 => Operation.response("Array of Status", "application/json", array_of_statuses()) +      } +    } +  end + +  def create_operation do +    %Operation{ +      tags: ["Statuses"], +      summary: "Publish new status", +      security: [%{"oAuth" => ["write:statuses"]}], +      description: "Post a new status", +      operationId: "StatusController.create", +      requestBody: request_body("Parameters", create_request(), required: true), +      responses: %{ +        200 => +          Operation.response( +            "Status. When `scheduled_at` is present, ScheduledStatus is returned instead", +            "application/json", +            %Schema{oneOf: [Status, ScheduledStatus]} +          ), +        422 => Operation.response("Bad Request", "application/json", ApiError) +      } +    } +  end + +  def show_operation do +    %Operation{ +      tags: ["Statuses"], +      summary: "View specific status", +      description: "View information about a status", +      operationId: "StatusController.show", +      security: [%{"oAuth" => ["read:statuses"]}], +      parameters: [id_param()], +      responses: %{ +        200 => status_response(), +        404 => Operation.response("Not Found", "application/json", ApiError) +      } +    } +  end + +  def delete_operation do +    %Operation{ +      tags: ["Statuses"], +      summary: "Delete status", +      security: [%{"oAuth" => ["write:statuses"]}], +      description: "Delete one of your own statuses", +      operationId: "StatusController.delete", +      parameters: [id_param()], +      responses: %{ +        200 => empty_object_response(), +        403 => Operation.response("Forbidden", "application/json", ApiError), +        404 => Operation.response("Not Found", "application/json", ApiError) +      } +    } +  end + +  def reblog_operation do +    %Operation{ +      tags: ["Statuses"], +      summary: "Boost", +      security: [%{"oAuth" => ["write:statuses"]}], +      description: "Reshare a status", +      operationId: "StatusController.reblog", +      parameters: [id_param()], +      requestBody: +        request_body("Parameters", %Schema{ +          type: :object, +          properties: %{ +            visibility: %Schema{allOf: [VisibilityScope], default: "public"} +          } +        }), +      responses: %{ +        200 => status_response(), +        404 => Operation.response("Not Found", "application/json", ApiError) +      } +    } +  end + +  def unreblog_operation do +    %Operation{ +      tags: ["Statuses"], +      summary: "Undo boost", +      security: [%{"oAuth" => ["write:statuses"]}], +      description: "Undo a reshare of a status", +      operationId: "StatusController.unreblog", +      parameters: [id_param()], +      responses: %{ +        200 => status_response(), +        404 => Operation.response("Not Found", "application/json", ApiError) +      } +    } +  end + +  def favourite_operation do +    %Operation{ +      tags: ["Statuses"], +      summary: "Favourite", +      security: [%{"oAuth" => ["write:favourites"]}], +      description: "Add a status to your favourites list", +      operationId: "StatusController.favourite", +      parameters: [id_param()], +      responses: %{ +        200 => status_response(), +        404 => Operation.response("Not Found", "application/json", ApiError) +      } +    } +  end + +  def unfavourite_operation do +    %Operation{ +      tags: ["Statuses"], +      summary: "Undo favourite", +      security: [%{"oAuth" => ["write:favourites"]}], +      description: "Remove a status from your favourites list", +      operationId: "StatusController.unfavourite", +      parameters: [id_param()], +      responses: %{ +        200 => status_response(), +        404 => Operation.response("Not Found", "application/json", ApiError) +      } +    } +  end + +  def pin_operation do +    %Operation{ +      tags: ["Statuses"], +      summary: "Pin to profile", +      security: [%{"oAuth" => ["write:accounts"]}], +      description: "Feature one of your own public statuses at the top of your profile", +      operationId: "StatusController.pin", +      parameters: [id_param()], +      responses: %{ +        200 => status_response(), +        400 => Operation.response("Error", "application/json", ApiError) +      } +    } +  end + +  def unpin_operation do +    %Operation{ +      tags: ["Statuses"], +      summary: "Unpin to profile", +      security: [%{"oAuth" => ["write:accounts"]}], +      description: "Unfeature a status from the top of your profile", +      operationId: "StatusController.unpin", +      parameters: [id_param()], +      responses: %{ +        200 => status_response(), +        400 => Operation.response("Error", "application/json", ApiError) +      } +    } +  end + +  def bookmark_operation do +    %Operation{ +      tags: ["Statuses"], +      summary: "Bookmark", +      security: [%{"oAuth" => ["write:bookmarks"]}], +      description: "Privately bookmark a status", +      operationId: "StatusController.bookmark", +      parameters: [id_param()], +      responses: %{ +        200 => status_response() +      } +    } +  end + +  def unbookmark_operation do +    %Operation{ +      tags: ["Statuses"], +      summary: "Undo bookmark", +      security: [%{"oAuth" => ["write:bookmarks"]}], +      description: "Remove a status from your private bookmarks", +      operationId: "StatusController.unbookmark", +      parameters: [id_param()], +      responses: %{ +        200 => status_response() +      } +    } +  end + +  def mute_conversation_operation do +    %Operation{ +      tags: ["Statuses"], +      summary: "Mute conversation", +      security: [%{"oAuth" => ["write:mutes"]}], +      description: +        "Do not receive notifications for the thread that this status is part of. Must be a thread in which you are a participant.", +      operationId: "StatusController.mute_conversation", +      parameters: [id_param()], +      responses: %{ +        200 => status_response(), +        400 => Operation.response("Error", "application/json", ApiError) +      } +    } +  end + +  def unmute_conversation_operation do +    %Operation{ +      tags: ["Statuses"], +      summary: "Unmute conversation", +      security: [%{"oAuth" => ["write:mutes"]}], +      description: +        "Start receiving notifications again for the thread that this status is part of", +      operationId: "StatusController.unmute_conversation", +      parameters: [id_param()], +      responses: %{ +        200 => status_response(), +        400 => Operation.response("Error", "application/json", ApiError) +      } +    } +  end + +  def card_operation do +    %Operation{ +      tags: ["Statuses"], +      deprecated: true, +      summary: "Preview card", +      description: "Deprecated in favor of card property inlined on Status entity", +      operationId: "StatusController.card", +      parameters: [id_param()], +      security: [%{"oAuth" => ["read:statuses"]}], +      responses: %{ +        200 => +          Operation.response("Card", "application/json", %Schema{ +            type: :object, +            nullable: true, +            properties: %{ +              type: %Schema{type: :string, enum: ["link", "photo", "video", "rich"]}, +              provider_name: %Schema{type: :string, nullable: true}, +              provider_url: %Schema{type: :string, format: :uri}, +              url: %Schema{type: :string, format: :uri}, +              image: %Schema{type: :string, nullable: true, format: :uri}, +              title: %Schema{type: :string}, +              description: %Schema{type: :string} +            } +          }) +      } +    } +  end + +  def favourited_by_operation do +    %Operation{ +      tags: ["Statuses"], +      summary: "Favourited by", +      description: "View who favourited a given status", +      operationId: "StatusController.favourited_by", +      security: [%{"oAuth" => ["read:accounts"]}], +      parameters: [id_param()], +      responses: %{ +        200 => +          Operation.response( +            "Array of Accounts", +            "application/json", +            AccountOperation.array_of_accounts() +          ), +        404 => Operation.response("Not Found", "application/json", ApiError) +      } +    } +  end + +  def reblogged_by_operation do +    %Operation{ +      tags: ["Statuses"], +      summary: "Boosted by", +      description: "View who boosted a given status", +      operationId: "StatusController.reblogged_by", +      security: [%{"oAuth" => ["read:accounts"]}], +      parameters: [id_param()], +      responses: %{ +        200 => +          Operation.response( +            "Array of Accounts", +            "application/json", +            AccountOperation.array_of_accounts() +          ), +        404 => Operation.response("Not Found", "application/json", ApiError) +      } +    } +  end + +  def context_operation do +    %Operation{ +      tags: ["Statuses"], +      summary: "Parent and child statuses", +      description: "View statuses above and below this status in the thread", +      operationId: "StatusController.context", +      security: [%{"oAuth" => ["read:statuses"]}], +      parameters: [id_param()], +      responses: %{ +        200 => Operation.response("Context", "application/json", context()) +      } +    } +  end + +  def favourites_operation do +    %Operation{ +      tags: ["Statuses"], +      summary: "Favourited statuses", +      description: "Statuses the user has favourited", +      operationId: "StatusController.favourites", +      parameters: pagination_params(), +      security: [%{"oAuth" => ["read:favourites"]}], +      responses: %{ +        200 => Operation.response("Array of Statuses", "application/json", array_of_statuses()) +      } +    } +  end + +  def bookmarks_operation do +    %Operation{ +      tags: ["Statuses"], +      summary: "Bookmarked statuses", +      description: "Statuses the user has bookmarked", +      operationId: "StatusController.bookmarks", +      parameters: [ +        Operation.parameter(:with_relationships, :query, BooleanLike, "Include relationships") +        | pagination_params() +      ], +      security: [%{"oAuth" => ["read:bookmarks"]}], +      responses: %{ +        200 => Operation.response("Array of Statuses", "application/json", array_of_statuses()) +      } +    } +  end + +  defp array_of_statuses do +    %Schema{type: :array, items: Status, example: [Status.schema().example]} +  end + +  defp create_request do +    %Schema{ +      title: "StatusCreateRequest", +      type: :object, +      properties: %{ +        status: %Schema{ +          type: :string, +          description: +            "Text content of the status. If `media_ids` is provided, this becomes optional. Attaching a `poll` is optional while `status` is provided." +        }, +        media_ids: %Schema{ +          type: :array, +          items: %Schema{type: :string}, +          description: +            "Array of Attachment ids to be attached as media. If provided, `status` becomes optional, and `poll` cannot be used." +        }, +        poll: %Schema{ +          type: :object, +          required: [:options], +          properties: %{ +            options: %Schema{ +              type: :array, +              items: %Schema{type: :string}, +              description: +                "Array of possible answers. If provided, `media_ids` cannot be used, and `poll[expires_in]` must be provided." +            }, +            expires_in: %Schema{ +              type: :integer, +              description: +                "Duration the poll should be open, in seconds. If provided, `media_ids` cannot be used, and `poll[options]` must be provided." +            }, +            multiple: %Schema{type: :boolean, description: "Allow multiple choices?"}, +            hide_totals: %Schema{ +              type: :boolean, +              description: "Hide vote counts until the poll ends?" +            } +          } +        }, +        in_reply_to_id: %Schema{ +          allOf: [FlakeID], +          description: "ID of the status being replied to, if status is a reply" +        }, +        sensitive: %Schema{ +          type: :boolean, +          description: "Mark status and attached media as sensitive?" +        }, +        spoiler_text: %Schema{ +          type: :string, +          description: +            "Text to be shown as a warning or subject before the actual content. Statuses are generally collapsed behind this field." +        }, +        scheduled_at: %Schema{ +          type: :string, +          format: :"date-time", +          nullable: true, +          description: +            "ISO 8601 Datetime at which to schedule a status. Providing this paramter will cause ScheduledStatus to be returned instead of Status. Must be at least 5 minutes in the future." +        }, +        language: %Schema{type: :string, description: "ISO 639 language code for this status."}, +        # Pleroma-specific properties: +        preview: %Schema{ +          type: :boolean, +          description: +            "If set to `true` the post won't be actually posted, but the status entitiy would still be rendered back. This could be useful for previewing rich text/custom emoji, for example" +        }, +        content_type: %Schema{ +          type: :string, +          description: +            "The MIME type of the status, it is transformed into HTML by the backend. You can get the list of the supported MIME types with the nodeinfo endpoint." +        }, +        to: %Schema{ +          type: :array, +          items: %Schema{type: :string}, +          description: +            "A list of nicknames (like `lain@soykaf.club` or `lain` on the local server) that will be used to determine who is going to be addressed by this post. Using this will disable the implicit addressing by mentioned names in the `status` body, only the people in the `to` list will be addressed. The normal rules for for post visibility are not affected by this and will still apply" +        }, +        visibility: %Schema{ +          anyOf: [ +            VisibilityScope, +            %Schema{type: :string, description: "`list:LIST_ID`", example: "LIST:123"} +          ], +          description: +            "Visibility of the posted status. Besides standard MastoAPI values (`direct`, `private`, `unlisted` or `public`) it can be used to address a List by setting it to `list:LIST_ID`" +        }, +        expires_in: %Schema{ +          type: :integer, +          description: +            "The number of seconds the posted activity should expire in. When a posted activity expires it will be deleted from the server, and a delete request for it will be federated. This needs to be longer than an hour." +        }, +        in_reply_to_conversation_id: %Schema{ +          type: :string, +          description: +            "Will reply to a given conversation, addressing only the people who are part of the recipient set of that conversation. Sets the visibility to `direct`." +        } +      }, +      example: %{ +        "status" => "What time is it?", +        "sensitive" => "false", +        "poll" => %{ +          "options" => ["Cofe", "Adventure"], +          "expires_in" => 420 +        } +      } +    } +  end + +  defp id_param do +    Operation.parameter(:id, :path, FlakeID, "Status ID", +      example: "9umDrYheeY451cQnEe", +      required: true +    ) +  end + +  defp status_response do +    Operation.response("Status", "application/json", Status) +  end + +  defp context do +    %Schema{ +      title: "StatusContext", +      description: +        "Represents the tree around a given status. Used for reconstructing threads of statuses.", +      type: :object, +      required: [:ancestors, :descendants], +      properties: %{ +        ancestors: array_of_statuses(), +        descendants: array_of_statuses() +      }, +      example: %{ +        "ancestors" => [Status.schema().example], +        "descendants" => [Status.schema().example] +      } +    } +  end +end diff --git a/lib/pleroma/web/api_spec/schemas/status.ex b/lib/pleroma/web/api_spec/schemas/status.ex index 2572c9641..8b87cb25b 100644 --- a/lib/pleroma/web/api_spec/schemas/status.ex +++ b/lib/pleroma/web/api_spec/schemas/status.ex @@ -19,60 +19,127 @@ defmodule Pleroma.Web.ApiSpec.Schemas.Status do      description: "Response schema for a status",      type: :object,      properties: %{ -      account: Account, +      account: %Schema{allOf: [Account], description: "The account that authored this status"},        application: %Schema{ +        description: "The application used to post this status",          type: :object,          properties: %{            name: %Schema{type: :string},            website: %Schema{type: :string, nullable: true, format: :uri}          }        }, -      bookmarked: %Schema{type: :boolean}, +      bookmarked: %Schema{type: :boolean, description: "Have you bookmarked this status?"},        card: %Schema{          type: :object,          nullable: true, +        description: "Preview card for links included within status content", +        required: [:url, :title, :description, :type],          properties: %{ -          type: %Schema{type: :string, enum: ["link", "photo", "video", "rich"]}, -          provider_name: %Schema{type: :string, nullable: true}, -          provider_url: %Schema{type: :string, format: :uri}, -          url: %Schema{type: :string, format: :uri}, -          image: %Schema{type: :string, nullable: true, format: :uri}, -          title: %Schema{type: :string}, -          description: %Schema{type: :string} +          type: %Schema{ +            type: :string, +            enum: ["link", "photo", "video", "rich"], +            description: "The type of the preview card" +          }, +          provider_name: %Schema{ +            type: :string, +            nullable: true, +            description: "The provider of the original resource" +          }, +          provider_url: %Schema{ +            type: :string, +            format: :uri, +            description: "A link to the provider of the original resource" +          }, +          url: %Schema{type: :string, format: :uri, description: "Location of linked resource"}, +          image: %Schema{ +            type: :string, +            nullable: true, +            format: :uri, +            description: "Preview thumbnail" +          }, +          title: %Schema{type: :string, description: "Title of linked resource"}, +          description: %Schema{type: :string, description: "Description of preview"}          }        }, -      content: %Schema{type: :string, format: :html}, -      created_at: %Schema{type: :string, format: "date-time"}, -      emojis: %Schema{type: :array, items: Emoji}, -      favourited: %Schema{type: :boolean}, -      favourites_count: %Schema{type: :integer}, +      content: %Schema{type: :string, format: :html, description: "HTML-encoded status content"}, +      created_at: %Schema{ +        type: :string, +        format: "date-time", +        description: "The date when this status was created" +      }, +      emojis: %Schema{ +        type: :array, +        items: Emoji, +        description: "Custom emoji to be used when rendering status content" +      }, +      favourited: %Schema{type: :boolean, description: "Have you favourited this status?"}, +      favourites_count: %Schema{ +        type: :integer, +        description: "How many favourites this status has received" +      },        id: FlakeID, -      in_reply_to_account_id: %Schema{type: :string, nullable: true}, -      in_reply_to_id: %Schema{type: :string, nullable: true}, -      language: %Schema{type: :string, nullable: true}, +      in_reply_to_account_id: %Schema{ +        allOf: [FlakeID], +        nullable: true, +        description: "ID of the account being replied to" +      }, +      in_reply_to_id: %Schema{ +        allOf: [FlakeID], +        nullable: true, +        description: "ID of the status being replied" +      }, +      language: %Schema{ +        type: :string, +        nullable: true, +        description: "Primary language of this status" +      },        media_attachments: %Schema{          type: :array, -        items: Attachment +        items: Attachment, +        description: "Media that is attached to this status"        },        mentions: %Schema{          type: :array, +        description: "Mentions of users within the status content",          items: %Schema{            type: :object,            properties: %{ -            id: %Schema{type: :string}, -            acct: %Schema{type: :string}, -            username: %Schema{type: :string}, -            url: %Schema{type: :string, format: :uri} +            id: %Schema{allOf: [FlakeID], description: "The account id of the mentioned user"}, +            acct: %Schema{ +              type: :string, +              description: +                "The webfinger acct: URI of the mentioned user. Equivalent to `username` for local users, or `username@domain` for remote users." +            }, +            username: %Schema{type: :string, description: "The username of the mentioned user"}, +            url: %Schema{ +              type: :string, +              format: :uri, +              description: "The location of the mentioned user's profile" +            }            }          }        }, -      muted: %Schema{type: :boolean}, -      pinned: %Schema{type: :boolean}, +      muted: %Schema{ +        type: :boolean, +        description: "Have you muted notifications for this status's conversation?" +      }, +      pinned: %Schema{ +        type: :boolean, +        description: "Have you pinned this status? Only appears if the status is pinnable." +      },        pleroma: %Schema{          type: :object,          properties: %{ -          content: %Schema{type: :object, additionalProperties: %Schema{type: :string}}, -          conversation_id: %Schema{type: :integer}, +          content: %Schema{ +            type: :object, +            additionalProperties: %Schema{type: :string}, +            description: +              "A map consisting of alternate representations of the `content` property with the key being it's mimetype. Currently the only alternate representation supported is `text/plain`" +          }, +          conversation_id: %Schema{ +            type: :integer, +            description: "The ID of the AP context the status is associated with (if any)" +          },            direct_conversation_id: %Schema{              type: :integer,              nullable: true, @@ -81,6 +148,8 @@ defmodule Pleroma.Web.ApiSpec.Schemas.Status do            },            emoji_reactions: %Schema{              type: :array, +            description: +              "A list with emoji / reaction maps. Contains no information about the reacting users, for that use the /statuses/:id/reactions endpoint.",              items: %Schema{                type: :object,                properties: %{ @@ -90,27 +159,74 @@ defmodule Pleroma.Web.ApiSpec.Schemas.Status do                }              }            }, -          expires_at: %Schema{type: :string, format: "date-time", nullable: true}, -          in_reply_to_account_acct: %Schema{type: :string, nullable: true}, -          local: %Schema{type: :boolean}, -          spoiler_text: %Schema{type: :object, additionalProperties: %Schema{type: :string}}, -          thread_muted: %Schema{type: :boolean} +          expires_at: %Schema{ +            type: :string, +            format: "date-time", +            nullable: true, +            description: +              "A datetime (ISO 8601) that states when the post will expire (be deleted automatically), or empty if the post won't expire" +          }, +          in_reply_to_account_acct: %Schema{ +            type: :string, +            nullable: true, +            description: "The `acct` property of User entity for replied user (if any)" +          }, +          local: %Schema{ +            type: :boolean, +            description: "`true` if the post was made on the local instance" +          }, +          spoiler_text: %Schema{ +            type: :object, +            additionalProperties: %Schema{type: :string}, +            description: +              "A map consisting of alternate representations of the `spoiler_text` property with the key being it's mimetype. Currently the only alternate representation supported is `text/plain`." +          }, +          thread_muted: %Schema{ +            type: :boolean, +            description: "`true` if the thread the post belongs to is muted" +          }          }        }, -      poll: %Schema{type: Poll, nullable: true}, +      poll: %Schema{allOf: [Poll], nullable: true, description: "The poll attached to the status"},        reblog: %Schema{          allOf: [%OpenApiSpex.Reference{"$ref": "#/components/schemas/Status"}], -        nullable: true +        nullable: true, +        description: "The status being reblogged" +      }, +      reblogged: %Schema{type: :boolean, description: "Have you boosted this status?"}, +      reblogs_count: %Schema{ +        type: :integer, +        description: "How many boosts this status has received" +      }, +      replies_count: %Schema{ +        type: :integer, +        description: "How many replies this status has received" +      }, +      sensitive: %Schema{ +        type: :boolean, +        description: "Is this status marked as sensitive content?" +      }, +      spoiler_text: %Schema{ +        type: :string, +        description: +          "Subject or summary line, below which status content is collapsed until expanded"        }, -      reblogged: %Schema{type: :boolean}, -      reblogs_count: %Schema{type: :integer}, -      replies_count: %Schema{type: :integer}, -      sensitive: %Schema{type: :boolean}, -      spoiler_text: %Schema{type: :string},        tags: %Schema{type: :array, items: Tag}, -      uri: %Schema{type: :string, format: :uri}, -      url: %Schema{type: :string, nullable: true, format: :uri}, -      visibility: VisibilityScope +      uri: %Schema{ +        type: :string, +        format: :uri, +        description: "URI of the status used for federation" +      }, +      url: %Schema{ +        type: :string, +        nullable: true, +        format: :uri, +        description: "A link to the status's HTML representation" +      }, +      visibility: %Schema{ +        allOf: [VisibilityScope], +        description: "Visibility of this status" +      }      },      example: %{        "account" => %{ diff --git a/lib/pleroma/web/api_spec/schemas/visibility_scope.ex b/lib/pleroma/web/api_spec/schemas/visibility_scope.ex index 8c81a4d73..831734e27 100644 --- a/lib/pleroma/web/api_spec/schemas/visibility_scope.ex +++ b/lib/pleroma/web/api_spec/schemas/visibility_scope.ex @@ -9,6 +9,6 @@ defmodule Pleroma.Web.ApiSpec.Schemas.VisibilityScope do      title: "VisibilityScope",      description: "Status visibility",      type: :string, -    enum: ["public", "unlisted", "private", "direct"] +    enum: ["public", "unlisted", "private", "direct", "list"]    })  end diff --git a/lib/pleroma/web/common_api/activity_draft.ex b/lib/pleroma/web/common_api/activity_draft.ex index 244cf2be5..3f1a50b96 100644 --- a/lib/pleroma/web/common_api/activity_draft.ex +++ b/lib/pleroma/web/common_api/activity_draft.ex @@ -58,16 +58,16 @@ defmodule Pleroma.Web.CommonAPI.ActivityDraft do    end    defp put_params(draft, params) do -    params = Map.put_new(params, "in_reply_to_status_id", params["in_reply_to_id"]) +    params = Map.put_new(params, :in_reply_to_status_id, params[:in_reply_to_id])      %__MODULE__{draft | params: params}    end -  defp status(%{params: %{"status" => status}} = draft) do +  defp status(%{params: %{status: status}} = draft) do      %__MODULE__{draft | status: String.trim(status)}    end    defp summary(%{params: params} = draft) do -    %__MODULE__{draft | summary: Map.get(params, "spoiler_text", "")} +    %__MODULE__{draft | summary: Map.get(params, :spoiler_text, "")}    end    defp full_payload(%{status: status, summary: summary} = draft) do @@ -84,20 +84,20 @@ defmodule Pleroma.Web.CommonAPI.ActivityDraft do      %__MODULE__{draft | attachments: attachments}    end -  defp in_reply_to(%{params: %{"in_reply_to_status_id" => ""}} = draft), do: draft +  defp in_reply_to(%{params: %{in_reply_to_status_id: ""}} = draft), do: draft -  defp in_reply_to(%{params: %{"in_reply_to_status_id" => id}} = draft) when is_binary(id) do +  defp in_reply_to(%{params: %{in_reply_to_status_id: id}} = draft) when is_binary(id) do      %__MODULE__{draft | in_reply_to: Activity.get_by_id(id)}    end -  defp in_reply_to(%{params: %{"in_reply_to_status_id" => %Activity{} = in_reply_to}} = draft) do +  defp in_reply_to(%{params: %{in_reply_to_status_id: %Activity{} = in_reply_to}} = draft) do      %__MODULE__{draft | in_reply_to: in_reply_to}    end    defp in_reply_to(draft), do: draft    defp in_reply_to_conversation(draft) do -    in_reply_to_conversation = Participation.get(draft.params["in_reply_to_conversation_id"]) +    in_reply_to_conversation = Participation.get(draft.params[:in_reply_to_conversation_id])      %__MODULE__{draft | in_reply_to_conversation: in_reply_to_conversation}    end @@ -112,7 +112,7 @@ defmodule Pleroma.Web.CommonAPI.ActivityDraft do    end    defp expires_at(draft) do -    case CommonAPI.check_expiry_date(draft.params["expires_in"]) do +    case CommonAPI.check_expiry_date(draft.params[:expires_in]) do        {:ok, expires_at} -> %__MODULE__{draft | expires_at: expires_at}        {:error, message} -> add_error(draft, message)      end @@ -144,7 +144,7 @@ defmodule Pleroma.Web.CommonAPI.ActivityDraft do      addressed_users =        draft.mentions        |> Enum.map(fn {_, mentioned_user} -> mentioned_user.ap_id end) -      |> Utils.get_addressed_users(draft.params["to"]) +      |> Utils.get_addressed_users(draft.params[:to])      {to, cc} =        Utils.get_to_and_cc( @@ -164,7 +164,7 @@ defmodule Pleroma.Web.CommonAPI.ActivityDraft do    end    defp sensitive(draft) do -    sensitive = draft.params["sensitive"] || Enum.member?(draft.tags, {"#nsfw", "nsfw"}) +    sensitive = draft.params[:sensitive] || Enum.member?(draft.tags, {"#nsfw", "nsfw"})      %__MODULE__{draft | sensitive: sensitive}    end @@ -191,7 +191,7 @@ defmodule Pleroma.Web.CommonAPI.ActivityDraft do    end    defp preview?(draft) do -    preview? = Pleroma.Web.ControllerHelper.truthy_param?(draft.params["preview"]) +    preview? = Pleroma.Web.ControllerHelper.truthy_param?(draft.params[:preview])      %__MODULE__{draft | preview?: preview?}    end diff --git a/lib/pleroma/web/common_api/common_api.ex b/lib/pleroma/web/common_api/common_api.ex index fbef05e83..601caeb46 100644 --- a/lib/pleroma/web/common_api/common_api.ex +++ b/lib/pleroma/web/common_api/common_api.ex @@ -116,19 +116,18 @@ defmodule Pleroma.Web.CommonAPI do    end    def repeat(id, user, params \\ %{}) do -    with {_, %Activity{data: %{"type" => "Create"}} = activity} <- -           {:find_activity, Activity.get_by_id(id)}, -         object <- Object.normalize(activity), -         announce_activity <- Utils.get_existing_announce(user.ap_id, object), -         public <- public_announce?(object, params) do +    with %Activity{data: %{"type" => "Create"}} = activity <- Activity.get_by_id(id) do +      object = Object.normalize(activity) +      announce_activity = Utils.get_existing_announce(user.ap_id, object) +      public = public_announce?(object, params) +        if announce_activity do          {:ok, announce_activity, object}        else          ActivityPub.announce(user, object, nil, true, public)        end      else -      {:find_activity, _} -> {:error, :not_found} -      _ -> {:error, dgettext("errors", "Could not repeat")} +      _ -> {:error, :not_found}      end    end @@ -286,7 +285,7 @@ defmodule Pleroma.Web.CommonAPI do      end    end -  def public_announce?(_, %{"visibility" => visibility}) +  def public_announce?(_, %{visibility: visibility})        when visibility in ~w{public unlisted private direct},        do: visibility in ~w(public unlisted) @@ -296,11 +295,11 @@ defmodule Pleroma.Web.CommonAPI do    def get_visibility(_, _, %Participation{}), do: {"direct", "direct"} -  def get_visibility(%{"visibility" => visibility}, in_reply_to, _) +  def get_visibility(%{visibility: visibility}, in_reply_to, _)        when visibility in ~w{public unlisted private direct},        do: {visibility, get_replied_to_visibility(in_reply_to)} -  def get_visibility(%{"visibility" => "list:" <> list_id}, in_reply_to, _) do +  def get_visibility(%{visibility: "list:" <> list_id}, in_reply_to, _) do      visibility = {:list, String.to_integer(list_id)}      {visibility, get_replied_to_visibility(in_reply_to)}    end @@ -358,7 +357,7 @@ defmodule Pleroma.Web.CommonAPI do      end    end -  def post(user, %{"status" => _} = data) do +  def post(user, %{status: _} = data) do      with {:ok, draft} <- Pleroma.Web.CommonAPI.ActivityDraft.create(user, data) do        draft.changes        |> ActivityPub.create(draft.preview?) @@ -467,11 +466,11 @@ defmodule Pleroma.Web.CommonAPI do      end    end -  defp toggle_sensitive(activity, %{"sensitive" => sensitive}) when sensitive in ~w(true false) do -    toggle_sensitive(activity, %{"sensitive" => String.to_existing_atom(sensitive)}) +  defp toggle_sensitive(activity, %{sensitive: sensitive}) when sensitive in ~w(true false) do +    toggle_sensitive(activity, %{sensitive: String.to_existing_atom(sensitive)})    end -  defp toggle_sensitive(%Activity{object: object} = activity, %{"sensitive" => sensitive}) +  defp toggle_sensitive(%Activity{object: object} = activity, %{sensitive: sensitive})         when is_boolean(sensitive) do      new_data = Map.put(object.data, "sensitive", sensitive) @@ -485,7 +484,7 @@ defmodule Pleroma.Web.CommonAPI do    defp toggle_sensitive(activity, _), do: {:ok, activity} -  defp set_visibility(activity, %{"visibility" => visibility}) do +  defp set_visibility(activity, %{visibility: visibility}) do      Utils.update_activity_visibility(activity, visibility)    end diff --git a/lib/pleroma/web/common_api/utils.ex b/lib/pleroma/web/common_api/utils.ex index 793f2e7f8..e8deee223 100644 --- a/lib/pleroma/web/common_api/utils.ex +++ b/lib/pleroma/web/common_api/utils.ex @@ -22,11 +22,11 @@ defmodule Pleroma.Web.CommonAPI.Utils do    require Logger    require Pleroma.Constants -  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)    end -  def attachments_from_ids(%{"media_ids" => ids} = _) do +  def attachments_from_ids(%{media_ids: ids}) do      attachments_from_ids_no_descs(ids)    end @@ -37,11 +37,11 @@ defmodule Pleroma.Web.CommonAPI.Utils do    def attachments_from_ids_no_descs(ids) do      Enum.map(ids, fn media_id ->        case Repo.get(Object, media_id) do -        %Object{data: data} = _ -> data +        %Object{data: data} -> data          _ -> nil        end      end) -    |> Enum.filter(& &1) +    |> Enum.reject(&is_nil/1)    end    def attachments_from_ids_descs([], _), do: [] @@ -51,14 +51,14 @@ defmodule Pleroma.Web.CommonAPI.Utils do      Enum.map(ids, fn media_id ->        case Repo.get(Object, media_id) do -        %Object{data: data} = _ -> +        %Object{data: data} ->            Map.put(data, "name", descs[media_id])          _ ->            nil        end      end) -    |> Enum.filter(& &1) +    |> Enum.reject(&is_nil/1)    end    @spec get_to_and_cc( @@ -140,7 +140,7 @@ defmodule Pleroma.Web.CommonAPI.Utils do      |> make_poll_data()    end -  def make_poll_data(%{"poll" => %{"options" => options, "expires_in" => expires_in}} = data) +  def make_poll_data(%{poll: %{options: options, expires_in: expires_in}} = data)        when is_list(options) do      limits = Pleroma.Config.get([:instance, :poll_limits]) @@ -163,7 +163,7 @@ defmodule Pleroma.Web.CommonAPI.Utils do          |> DateTime.add(expires_in)          |> DateTime.to_iso8601() -      key = if truthy_param?(data["poll"]["multiple"]), do: "anyOf", else: "oneOf" +      key = if truthy_param?(data.poll[:multiple]), do: "anyOf", else: "oneOf"        poll = %{"type" => "Question", key => option_notes, "closed" => end_time}        {:ok, {poll, emoji}} @@ -213,7 +213,7 @@ defmodule Pleroma.Web.CommonAPI.Utils do        |> Map.get("attachment_links", Config.get([:instance, :attachment_links]))        |> truthy_param?() -    content_type = get_content_type(data["content_type"]) +    content_type = get_content_type(data[:content_type])      options =        if visibility == "direct" && Config.get([:instance, :safe_dm_mentions]) do diff --git a/lib/pleroma/web/mastodon_api/controllers/status_controller.ex b/lib/pleroma/web/mastodon_api/controllers/status_controller.ex index 12e3ba15e..25e499a77 100644 --- a/lib/pleroma/web/mastodon_api/controllers/status_controller.ex +++ b/lib/pleroma/web/mastodon_api/controllers/status_controller.ex @@ -24,6 +24,7 @@ defmodule Pleroma.Web.MastodonAPI.StatusController do    alias Pleroma.Web.MastodonAPI.AccountView    alias Pleroma.Web.MastodonAPI.ScheduledActivityView +  plug(Pleroma.Web.ApiSpec.CastAndValidate)    plug(:skip_plug, Pleroma.Plugs.EnsurePublicOrAuthenticatedPlug when action in [:index, :show])    @unauthenticated_access %{fallback: :proceed_unauthenticated, scopes: []} @@ -97,12 +98,14 @@ defmodule Pleroma.Web.MastodonAPI.StatusController do    action_fallback(Pleroma.Web.MastodonAPI.FallbackController) +  defdelegate open_api_operation(action), to: Pleroma.Web.ApiSpec.StatusOperation +    @doc """    GET `/api/v1/statuses?ids[]=1&ids[]=2`    `ids` query param is required    """ -  def index(%{assigns: %{user: user}} = conn, %{"ids" => ids} = params) do +  def index(%{assigns: %{user: user}} = conn, %{ids: ids} = params) do      limit = 100      activities = @@ -125,21 +128,29 @@ defmodule Pleroma.Web.MastodonAPI.StatusController do    Creates a scheduled status when `scheduled_at` param is present and it's far enough    """    def create( -        %{assigns: %{user: user}} = conn, -        %{"status" => _, "scheduled_at" => scheduled_at} = params +        %{ +          assigns: %{user: user}, +          body_params: %{status: _, scheduled_at: scheduled_at} = params +        } = conn, +        _        )        when not is_nil(scheduled_at) do -    params = Map.put(params, "in_reply_to_status_id", params["in_reply_to_id"]) +    params = Map.put(params, :in_reply_to_status_id, params[:in_reply_to_id]) + +    attrs = %{ +      params: Map.new(params, fn {key, value} -> {to_string(key), value} end), +      scheduled_at: scheduled_at +    }      with {:far_enough, true} <- {:far_enough, ScheduledActivity.far_enough?(scheduled_at)}, -         attrs <- %{"params" => params, "scheduled_at" => scheduled_at},           {:ok, scheduled_activity} <- ScheduledActivity.create(user, attrs) do        conn        |> put_view(ScheduledActivityView)        |> render("show.json", scheduled_activity: scheduled_activity)      else        {:far_enough, _} -> -        create(conn, Map.drop(params, ["scheduled_at"])) +        params = Map.drop(params, [:scheduled_at]) +        create(%Plug.Conn{conn | body_params: params}, %{})        error ->          error @@ -151,8 +162,8 @@ defmodule Pleroma.Web.MastodonAPI.StatusController do    Creates a regular status    """ -  def create(%{assigns: %{user: user}} = conn, %{"status" => _} = params) do -    params = Map.put(params, "in_reply_to_status_id", params["in_reply_to_id"]) +  def create(%{assigns: %{user: user}, body_params: %{status: _} = params} = conn, _) do +    params = Map.put(params, :in_reply_to_status_id, params[:in_reply_to_id])      with {:ok, activity} <- CommonAPI.post(user, params) do        try_render(conn, "show.json", @@ -169,12 +180,13 @@ defmodule Pleroma.Web.MastodonAPI.StatusController do      end    end -  def create(%{assigns: %{user: _user}} = conn, %{"media_ids" => _} = params) do -    create(conn, Map.put(params, "status", "")) +  def create(%{assigns: %{user: _user}, body_params: %{media_ids: _} = params} = conn, _) do +    params = Map.put(params, :status, "") +    create(%Plug.Conn{conn | body_params: params}, %{})    end    @doc "GET /api/v1/statuses/:id" -  def show(%{assigns: %{user: user}} = conn, %{"id" => id}) do +  def show(%{assigns: %{user: user}} = conn, %{id: id}) do      with %Activity{} = activity <- Activity.get_by_id_with_object(id),           true <- Visibility.visible_for_user?(activity, user) do        try_render(conn, "show.json", @@ -188,7 +200,7 @@ defmodule Pleroma.Web.MastodonAPI.StatusController do    end    @doc "DELETE /api/v1/statuses/:id" -  def delete(%{assigns: %{user: user}} = conn, %{"id" => id}) do +  def delete(%{assigns: %{user: user}} = conn, %{id: id}) do      with {:ok, %Activity{}} <- CommonAPI.delete(id, user) do        json(conn, %{})      else @@ -198,7 +210,7 @@ defmodule Pleroma.Web.MastodonAPI.StatusController do    end    @doc "POST /api/v1/statuses/:id/reblog" -  def reblog(%{assigns: %{user: user}} = conn, %{"id" => ap_id_or_id} = params) do +  def reblog(%{assigns: %{user: user}, body_params: params} = conn, %{id: ap_id_or_id}) do      with {:ok, announce, _activity} <- CommonAPI.repeat(ap_id_or_id, user, params),           %Activity{} = announce <- Activity.normalize(announce.data) do        try_render(conn, "show.json", %{activity: announce, for: user, as: :activity}) @@ -206,7 +218,7 @@ defmodule Pleroma.Web.MastodonAPI.StatusController do    end    @doc "POST /api/v1/statuses/:id/unreblog" -  def unreblog(%{assigns: %{user: user}} = conn, %{"id" => activity_id}) do +  def unreblog(%{assigns: %{user: user}} = conn, %{id: activity_id}) do      with {:ok, _unannounce} <- CommonAPI.unrepeat(activity_id, user),           %Activity{} = activity <- Activity.get_by_id(activity_id) do        try_render(conn, "show.json", %{activity: activity, for: user, as: :activity}) @@ -214,7 +226,7 @@ defmodule Pleroma.Web.MastodonAPI.StatusController do    end    @doc "POST /api/v1/statuses/:id/favourite" -  def favourite(%{assigns: %{user: user}} = conn, %{"id" => activity_id}) do +  def favourite(%{assigns: %{user: user}} = conn, %{id: activity_id}) do      with {:ok, _fav} <- CommonAPI.favorite(user, activity_id),           %Activity{} = activity <- Activity.get_by_id(activity_id) do        try_render(conn, "show.json", activity: activity, for: user, as: :activity) @@ -222,7 +234,7 @@ defmodule Pleroma.Web.MastodonAPI.StatusController do    end    @doc "POST /api/v1/statuses/:id/unfavourite" -  def unfavourite(%{assigns: %{user: user}} = conn, %{"id" => activity_id}) do +  def unfavourite(%{assigns: %{user: user}} = conn, %{id: activity_id}) do      with {:ok, _unfav} <- CommonAPI.unfavorite(activity_id, user),           %Activity{} = activity <- Activity.get_by_id(activity_id) do        try_render(conn, "show.json", activity: activity, for: user, as: :activity) @@ -230,21 +242,21 @@ defmodule Pleroma.Web.MastodonAPI.StatusController do    end    @doc "POST /api/v1/statuses/:id/pin" -  def pin(%{assigns: %{user: user}} = conn, %{"id" => ap_id_or_id}) do +  def pin(%{assigns: %{user: user}} = conn, %{id: ap_id_or_id}) do      with {:ok, activity} <- CommonAPI.pin(ap_id_or_id, user) do        try_render(conn, "show.json", activity: activity, for: user, as: :activity)      end    end    @doc "POST /api/v1/statuses/:id/unpin" -  def unpin(%{assigns: %{user: user}} = conn, %{"id" => ap_id_or_id}) do +  def unpin(%{assigns: %{user: user}} = conn, %{id: ap_id_or_id}) do      with {:ok, activity} <- CommonAPI.unpin(ap_id_or_id, user) do        try_render(conn, "show.json", activity: activity, for: user, as: :activity)      end    end    @doc "POST /api/v1/statuses/:id/bookmark" -  def bookmark(%{assigns: %{user: user}} = conn, %{"id" => id}) do +  def bookmark(%{assigns: %{user: user}} = conn, %{id: id}) do      with %Activity{} = activity <- Activity.get_by_id_with_object(id),           %User{} = user <- User.get_cached_by_nickname(user.nickname),           true <- Visibility.visible_for_user?(activity, user), @@ -254,7 +266,7 @@ defmodule Pleroma.Web.MastodonAPI.StatusController do    end    @doc "POST /api/v1/statuses/:id/unbookmark" -  def unbookmark(%{assigns: %{user: user}} = conn, %{"id" => id}) do +  def unbookmark(%{assigns: %{user: user}} = conn, %{id: id}) do      with %Activity{} = activity <- Activity.get_by_id_with_object(id),           %User{} = user <- User.get_cached_by_nickname(user.nickname),           true <- Visibility.visible_for_user?(activity, user), @@ -264,7 +276,7 @@ defmodule Pleroma.Web.MastodonAPI.StatusController do    end    @doc "POST /api/v1/statuses/:id/mute" -  def mute_conversation(%{assigns: %{user: user}} = conn, %{"id" => id}) do +  def mute_conversation(%{assigns: %{user: user}} = conn, %{id: id}) do      with %Activity{} = activity <- Activity.get_by_id(id),           {:ok, activity} <- CommonAPI.add_mute(user, activity) do        try_render(conn, "show.json", activity: activity, for: user, as: :activity) @@ -272,7 +284,7 @@ defmodule Pleroma.Web.MastodonAPI.StatusController do    end    @doc "POST /api/v1/statuses/:id/unmute" -  def unmute_conversation(%{assigns: %{user: user}} = conn, %{"id" => id}) do +  def unmute_conversation(%{assigns: %{user: user}} = conn, %{id: id}) do      with %Activity{} = activity <- Activity.get_by_id(id),           {:ok, activity} <- CommonAPI.remove_mute(user, activity) do        try_render(conn, "show.json", activity: activity, for: user, as: :activity) @@ -281,7 +293,7 @@ defmodule Pleroma.Web.MastodonAPI.StatusController do    @doc "GET /api/v1/statuses/:id/card"    @deprecated "https://github.com/tootsuite/mastodon/pull/11213" -  def card(%{assigns: %{user: user}} = conn, %{"id" => status_id}) do +  def card(%{assigns: %{user: user}} = conn, %{id: status_id}) do      with %Activity{} = activity <- Activity.get_by_id(status_id),           true <- Visibility.visible_for_user?(activity, user) do        data = Pleroma.Web.RichMedia.Helpers.fetch_data_for_activity(activity) @@ -292,7 +304,7 @@ defmodule Pleroma.Web.MastodonAPI.StatusController do    end    @doc "GET /api/v1/statuses/:id/favourited_by" -  def favourited_by(%{assigns: %{user: user}} = conn, %{"id" => id}) do +  def favourited_by(%{assigns: %{user: user}} = conn, %{id: id}) do      with %Activity{} = activity <- Activity.get_by_id_with_object(id),           {:visible, true} <- {:visible, Visibility.visible_for_user?(activity, user)},           %Object{data: %{"likes" => likes}} <- Object.normalize(activity) do @@ -312,7 +324,7 @@ defmodule Pleroma.Web.MastodonAPI.StatusController do    end    @doc "GET /api/v1/statuses/:id/reblogged_by" -  def reblogged_by(%{assigns: %{user: user}} = conn, %{"id" => id}) do +  def reblogged_by(%{assigns: %{user: user}} = conn, %{id: id}) do      with %Activity{} = activity <- Activity.get_by_id_with_object(id),           {:visible, true} <- {:visible, Visibility.visible_for_user?(activity, user)},           %Object{data: %{"announcements" => announces, "id" => ap_id}} <- @@ -344,7 +356,7 @@ defmodule Pleroma.Web.MastodonAPI.StatusController do    end    @doc "GET /api/v1/statuses/:id/context" -  def context(%{assigns: %{user: user}} = conn, %{"id" => id}) do +  def context(%{assigns: %{user: user}} = conn, %{id: id}) do      with %Activity{} = activity <- Activity.get_by_id(id) do        activities =          ActivityPub.fetch_activities_for_context(activity.data["context"], %{ @@ -359,11 +371,12 @@ defmodule Pleroma.Web.MastodonAPI.StatusController do    @doc "GET /api/v1/favourites"    def favourites(%{assigns: %{user: %User{} = user}} = conn, params) do -    activities = -      ActivityPub.fetch_favourites( -        user, -        Map.take(params, Pleroma.Pagination.page_keys()) -      ) +    params = +      params +      |> Map.new(fn {key, value} -> {to_string(key), value} end) +      |> Map.take(Pleroma.Pagination.page_keys()) + +    activities = ActivityPub.fetch_favourites(user, params)      conn      |> add_link_headers(activities) diff --git a/lib/pleroma/web/mastodon_api/views/account_view.ex b/lib/pleroma/web/mastodon_api/views/account_view.ex index b7cdb52b1..835dfe9f4 100644 --- a/lib/pleroma/web/mastodon_api/views/account_view.ex +++ b/lib/pleroma/web/mastodon_api/views/account_view.ex @@ -337,7 +337,11 @@ defmodule Pleroma.Web.MastodonAPI.AccountView do    defp maybe_put_role(data, _, _), do: data    defp maybe_put_notification_settings(data, %User{id: user_id} = user, %User{id: user_id}) do -    Kernel.put_in(data, [:pleroma, :notification_settings], user.notification_settings) +    Kernel.put_in( +      data, +      [:pleroma, :notification_settings], +      Map.from_struct(user.notification_settings) +    )    end    defp maybe_put_notification_settings(data, _, _), do: data diff --git a/lib/pleroma/workers/scheduled_activity_worker.ex b/lib/pleroma/workers/scheduled_activity_worker.ex index 8905f4ad0..97d1efbfb 100644 --- a/lib/pleroma/workers/scheduled_activity_worker.ex +++ b/lib/pleroma/workers/scheduled_activity_worker.ex @@ -30,6 +30,8 @@ defmodule Pleroma.Workers.ScheduledActivityWorker do    end    defp post_activity(%ScheduledActivity{user_id: user_id, params: params} = scheduled_activity) do +    params = Map.new(params, fn {key, value} -> {String.to_existing_atom(key), value} end) +      with {:delete, {:ok, _}} <- {:delete, ScheduledActivity.delete(scheduled_activity)},           {:user, %User{} = user} <- {:user, User.get_cached_by_id(user_id)},           {:post, {:ok, _}} <- {:post, CommonAPI.post(user, params)} do  | 
