- `/api/v1/search`
- `/api/v1/statuses/{id}/card`
- LDAP authenticator
+- Chats, they were half-baked. Just use PMs.
## 2022.07
sender_nickname: nil,
message: nil
],
- chat_message: [
- enabled: false,
- sender_nickname: nil,
- message: nil
- ],
email: [
enabled: false,
sender: nil,
"mastodon-fe" => %{
"name" => "mastodon-fe",
"git" => "https://akkoma.dev/AkkomaGang/masto-fe",
- "build_url" => "https://akkoma-updates.s3-website.fr-par.scw.cloud/frontend/${ref}/masto-fe.zip",
+ "build_url" =>
+ "https://akkoma-updates.s3-website.fr-par.scw.cloud/frontend/${ref}/masto-fe.zip",
"build_dir" => "distribution",
"ref" => "akkoma"
},
key: :privileged_staff,
type: :boolean,
description:
- "Let moderators access sensitive data (e.g. updating user credentials, get password reset token, delete users, index and read private statuses and chats)"
+ "Let moderators access sensitive data (e.g. updating user credentials, get password reset token, delete users, index and read private statuses)"
}
]
},
}
]
},
- %{
- key: :chat_message,
- type: :keyword,
- descpiption: "Chat message settings",
- children: [
- %{
- key: :enabled,
- type: :boolean,
- description: "Enables sending a chat message to newly registered users"
- },
- %{
- key: :message,
- type: :string,
- description:
- "A message that will be sent to newly registered users as a chat message",
- suggestions: [
- "Hello, welcome on board!"
- ]
- },
- %{
- key: :sender_nickname,
- type: :string,
- description: "The nickname of the local user that sends a welcome chat message",
- suggestions: [
- "lain"
- ]
- }
- ]
- },
%{
key: :email,
type: :keyword,
}
]
},
- %{
- 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,
key: :http,
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://akkoma.dev/AkkomaGang/akkoma/src/branch/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
* `name`: The instance’s name.
* `email`: Email used to reach an Administrator/Moderator of the instance.
* `enabled`: Enables the send a direct message to a newly registered user. Defaults to `false`.
* `sender_nickname`: The nickname of the local user that sends the welcome message.
* `message`: A message that will be send to a newly registered users as a direct message.
-* `chat_message`: - welcome message sent as a chat message.
- * `enabled`: Enables the send a chat message to a newly registered user. Defaults to `false`.
- * `sender_nickname`: The nickname of the local user that sends the welcome message.
- * `message`: A message that will be send to a newly registered users as a chat message.
* `email`: - welcome message sent as a email.
* `enabled`: Enables the send a welcome email to a newly registered user. Defaults to `false`.
* `sender`: The email address or tuple with `{nickname, email}` that will use as sender to the welcome email.
- `:hackney_pools`
- `:connections_pool`
- `:pools`
- - `:chat`
- partially settings inside these keys:
- `:seconds_valid` in `Pleroma.Captcha`
- `:proxy_remote` in `Pleroma.Upload`
```
-## GET /api/v1/pleroma/admin/users/:nickname/chats
-
-### List a user's chats
-
-- Params: None
-
-- Response:
-
-```json
-[
- {
- "sender": {
- "id": "someflakeid",
- "username": "somenick",
- ...
- },
- "receiver": {
- "id": "someflakeid",
- "username": "somenick",
- ...
- },
- "id" : "1",
- "unread" : 2,
- "last_message" : {...}, // The last message in that chat
- "updated_at": "2020-04-21T15:11:46.000Z"
- }
-]
-```
-
-## GET /api/v1/pleroma/admin/chats/:chat_id
-
-### View a single chat
-
-- Params: None
-
-- Response:
-
-```json
-{
- "sender": {
- "id": "someflakeid",
- "username": "somenick",
- ...
- },
- "receiver": {
- "id": "someflakeid",
- "username": "somenick",
- ...
- },
- "id" : "1",
- "unread" : 2,
- "last_message" : {...}, // The last message in that chat
- "updated_at": "2020-04-21T15:11:46.000Z"
-}
-```
-
-## GET /api/v1/pleroma/admin/chats/:chat_id/messages
-
-### List the messages in a chat
-
-- Params: `max_id`, `min_id`
-
-- Response:
-
-```json
-[
- {
- "account_id": "someflakeid",
- "chat_id": "1",
- "content": "Check this out :firefox:",
- "created_at": "2020-04-21T15:11:46.000Z",
- "emojis": [
- {
- "shortcode": "firefox",
- "static_url": "https://dontbulling.me/emoji/Firefox.gif",
- "url": "https://dontbulling.me/emoji/Firefox.gif",
- "visible_in_picker": false
- }
- ],
- "id": "13",
- "unread": true
- },
- {
- "account_id": "someflakeid",
- "chat_id": "1",
- "content": "Whats' up?",
- "created_at": "2020-04-21T15:06:45.000Z",
- "emojis": [],
- "id": "12",
- "unread": false
- }
-]
-```
-
-## DELETE /api/v1/pleroma/admin/chats/:chat_id/messages/:message_id
-
-### Delete a single message
-
-- Params: None
-
-- Response:
-
-```json
-{
- "account_id": "someflakeid",
- "chat_id": "1",
- "content": "Check this out :firefox:",
- "created_at": "2020-04-21T15:11:46.000Z",
- "emojis": [
- {
- "shortcode": "firefox",
- "static_url": "https://dontbulling.me/emoji/Firefox.gif",
- "url": "https://dontbulling.me/emoji/Firefox.gif",
- "visible_in_picker": false
- }
- ],
- "id": "13",
- "unread": false
-}
-```
-
## `GET /api/v1/pleroma/admin/instance_document/:document_name`
### Get an instance document
+++ /dev/null
-# Chats
-
-Chats are a way to represent an IM-style conversation between two actors. They are not the same as direct messages and they are not `Status`es, even though they have a lot in common.
-
-## Why Chats?
-
-There are no 'visibility levels' in ActivityPub, their definition is purely a Mastodon convention. Direct Messaging between users on the fediverse has mostly been modeled by using ActivityPub addressing following Mastodon conventions on normal `Note` objects. In this case, a 'direct message' would be a message that has no followers addressed and also does not address the special public actor, but just the recipients in the `to` field. It would still be a `Note` and is presented with other `Note`s as a `Status` in the API.
-
-This is an awkward setup for a few reasons:
-
-- As DMs generally still follow the usual `Status` conventions, it is easy to accidentally pull somebody into a DM thread by mentioning them. (e.g. "I hate @badguy so much")
-- It is possible to go from a publicly addressed `Status` to a DM reply, back to public, then to a 'followers only' reply, and so on. This can be become very confusing, as it is unclear which user can see which part of the conversation.
-- The standard `Status` format of implicit addressing also leads to rather ugly results if you try to display the messages as a chat, because all the recipients are always mentioned by name in the message.
-- As direct messages are posted with the same api call (and usually same frontend component) as public messages, accidentally making a public message private or vice versa can happen easily. Client bugs can also lead to this, accidentally making private messages public.
-
-As a measure to improve this situation, the `Conversation` concept and related Akkoma extensions were introduced. While it made it possible to work around a few of the issues, many of the problems remained and it didn't see much adoption because it was too complicated to use correctly.
-
-## Chats explained
-For this reasons, Chats are a new and different entity, both in the API as well as in ActivityPub. A quick overview:
-
-- Chats are meant to represent an instant message conversation between two actors. For now these are only 1-on-1 conversations, but the other actor can be a group in the future.
-- Chat messages have the ActivityPub type `ChatMessage`. They are not `Note`s. Servers that don't understand them will just drop them.
-- The only addressing allowed in `ChatMessage`s is one single ActivityPub actor in the `to` field.
-- There's always only one Chat between two actors. If you start chatting with someone and later start a 'new' Chat, the old Chat will be continued.
-- `ChatMessage`s are posted with a different api, making it very hard to accidentally send a message to the wrong person.
-- `ChatMessage`s don't show up in the existing timelines.
-- Chats can never go from private to public. They are always private between the two actors.
-
-## Caveats
-
-- Chats are NOT E2E encrypted (yet). Security is still the same as email.
-
-## API
-
-In general, the way to send a `ChatMessage` is to first create a `Chat`, then post a message to that `Chat`. `Group`s will later be supported by making them a sub-type of `Account`.
-
-This is the overview of using the API. The API is also documented via OpenAPI, so you can view it and play with it by pointing SwaggerUI or a similar OpenAPI tool to `https://yourinstance.tld/api/openapi`.
-
-### Creating or getting a chat.
-
-To create or get an existing Chat for a certain recipient (identified by Account ID)
-you can call:
-
-`POST /api/v1/pleroma/chats/by-account-id/:account_id`
-
-The account id is the normal FlakeId of the user
-```
-POST /api/v1/pleroma/chats/by-account-id/someflakeid
-```
-
-If you already have the id of a chat, you can also use
-
-```
-GET /api/v1/pleroma/chats/:id
-```
-
-There will only ever be ONE Chat for you and a given recipient, so this call
-will return the same Chat if you already have one with that user.
-
-Returned data:
-
-```json
-{
- "account": {
- "id": "someflakeid",
- "username": "somenick",
- ...
- },
- "id" : "1",
- "unread" : 2,
- "last_message" : {...}, // The last message in that chat
- "updated_at": "2020-04-21T15:11:46.000Z"
-}
-```
-
-### Marking a chat as read
-
-To mark a number of messages in a chat up to a certain message as read, you can use
-
-`POST /api/v1/pleroma/chats/:id/read`
-
-
-Parameters:
-- last_read_id: Given this id, all chat messages until this one will be marked as read. Required.
-
-
-Returned data:
-
-```json
-{
- "account": {
- "id": "someflakeid",
- "username": "somenick",
- ...
- },
- "id" : "1",
- "unread" : 0,
- "updated_at": "2020-04-21T15:11:46.000Z"
-}
-```
-
-### Marking a single chat message as read
-
-To set the `unread` property of a message to `false`
-
-`POST /api/v1/pleroma/chats/:id/messages/:message_id/read`
-
-Returned data:
-
-The modified chat message
-
-### Getting a list of Chats
-
-`GET /api/v1/pleroma/chats`
-
-This will return a list of chats that you have been involved in, sorted by their
-last update (so new chats will be at the top).
-
-Parameters:
-
-- with_muted: Include chats from muted users (boolean).
-
-Returned data:
-
-```json
-[
- {
- "account": {
- "id": "someflakeid",
- "username": "somenick",
- ...
- },
- "id" : "1",
- "unread" : 2,
- "last_message" : {...}, // The last message in that chat
- "updated_at": "2020-04-21T15:11:46.000Z"
- }
-]
-```
-
-The recipient of messages that are sent to this chat is given by their AP ID.
-No pagination is implemented for now.
-
-### Getting the messages for a Chat
-
-For a given Chat id, you can get the associated messages with
-
-`GET /api/v1/pleroma/chats/:id/messages`
-
-This will return all messages, sorted by most recent to least recent. The usual
-pagination options are implemented.
-
-Returned data:
-
-```json
-[
- {
- "account_id": "someflakeid",
- "chat_id": "1",
- "content": "Check this out :firefox:",
- "created_at": "2020-04-21T15:11:46.000Z",
- "emojis": [
- {
- "shortcode": "firefox",
- "static_url": "https://dontbulling.me/emoji/Firefox.gif",
- "url": "https://dontbulling.me/emoji/Firefox.gif",
- "visible_in_picker": false
- }
- ],
- "id": "13",
- "unread": true
- },
- {
- "account_id": "someflakeid",
- "chat_id": "1",
- "content": "Whats' up?",
- "created_at": "2020-04-21T15:06:45.000Z",
- "emojis": [],
- "id": "12",
- "unread": false,
- "idempotency_key": "75442486-0874-440c-9db1-a7006c25a31f"
- }
-]
-```
-
-- idempotency_key: The copy of the `idempotency-key` HTTP request header that can be used for optimistic message sending. Included only during the first few minutes after the message creation.
-
-### Posting a chat message
-
-Posting a chat message for given Chat id works like this:
-
-`POST /api/v1/pleroma/chats/:id/messages`
-
-Parameters:
-- content: The text content of the message. Optional if media is attached.
-- media_id: The id of an upload that will be attached to the message.
-
-Currently, no formatting beyond basic escaping and emoji is implemented.
-
-Returned data:
-
-```json
-{
- "account_id": "someflakeid",
- "chat_id": "1",
- "content": "Check this out :firefox:",
- "created_at": "2020-04-21T15:11:46.000Z",
- "emojis": [
- {
- "shortcode": "firefox",
- "static_url": "https://dontbulling.me/emoji/Firefox.gif",
- "url": "https://dontbulling.me/emoji/Firefox.gif",
- "visible_in_picker": false
- }
- ],
- "id": "13",
- "unread": false
-}
-```
-
-### Deleting a chat message
-
-Deleting a chat message for given Chat id works like this:
-
-`DELETE /api/v1/pleroma/chats/:chat_id/messages/:message_id`
-
-Returned data is the deleted message.
-
-### Notifications
-
-There's a new `pleroma:chat_mention` notification, which has this form. It is not given out in the notifications endpoint by default, you need to explicitly request it with `include_types[]=pleroma:chat_mention`:
-
-```json
-{
- "id": "someid",
- "type": "pleroma:chat_mention",
- "account": { ... } // User account of the sender,
- "chat_message": {
- "chat_id": "1",
- "id": "10",
- "content": "Hello",
- "account_id": "someflakeid",
- "unread": false
- },
- "created_at": "somedate"
-}
-```
-
-### Streaming
-
-There is an additional `user:pleroma_chat` stream. Incoming chat messages will make the current chat be sent to this `user` stream. The `event` of an incoming chat message is `pleroma:chat_update`. The payload is the updated chat with the incoming chat message in the `last_message` field.
-
-### Web Push
-
-If you want to receive push messages for this type, you'll need to add the `pleroma:chat_mention` type to your alerts in the push subscription.
- `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
- `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 Akkoma shoutbox. Only returned in `/api/v1/accounts/verify_credentials`
- `deactivated`: boolean, true when the user is deactivated
- `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_notifications_count`: The count of unread notifications. Only returned to the account owner.
- `notification_settings`: object, can be absent. See `/api/v1/pleroma/notification_settings` for the parameters/keys returned.
-- `accepts_chat_messages`: boolean, but can be null if we don't have that information about a user
- `favicon`: nullable URL string, Favicon image of the user's instance
### Source
- `account`: The account of the user who reacted
- `status`: The status that was reacted on
-### ChatMention Notification (not default)
-
-This notification has to be requested explicitly.
-
-The `type` value is `pleroma:chat_mention`
-
-- `account`: The account who sent the message
-- `chat_message`: The chat message
-
### Report Notification (not default)
This notification has to be requested explicitly.
Accepts additional parameters:
- `exclude_visibilities`: will exclude the notifications for activities with the given visibilities. The parameter accepts an array of visibility types (`public`, `unlisted`, `private`, `direct`). Usage example: `GET /api/v1/notifications?exclude_visibilities[]=direct&exclude_visibilities[]=private`.
-- `include_types`: will include the notifications for activities with the given types. The parameter accepts an array of types (`mention`, `follow`, `reblog`, `favourite`, `move`, `pleroma:emoji_reaction`, `pleroma:chat_mention`, `pleroma:report`). Usage example: `GET /api/v1/notifications?include_types[]=mention&include_types[]=reblog`.
+- `include_types`: will include the notifications for activities with the given types. The parameter accepts an array of types (`mention`, `follow`, `reblog`, `favourite`, `move`, `pleroma:emoji_reaction`, `pleroma:report`). Usage example: `GET /api/v1/notifications?include_types[]=mention&include_types[]=reblog`.
## DELETE `/api/v1/notifications/destroy_multiple`
- `pleroma_background_image` - sets the background image of the user. Can be set to "" (an empty string) to reset.
- `discoverable` - if true, external services (search bots) etc. are allowed to index / list the account (regardless of this setting, user will still appear in regular search results).
- `actor_type` - the type of this account.
-- `accepts_chat_messages` - if false, this account will reject all chat messages.
- `language` - user's preferred language for receiving emails (digest, confirmation, etc.)
All images (avatar, banner and background) can be reset to the default by sending an empty string ("") instead of a file.
`GET /api/v1/instance` has additional fields
- `max_toot_chars`: The maximum characters per post
-- `chat_limit`: The maximum characters per chat message
- `description_limit`: The maximum characters per image description
- `poll_limits`: The limits of polls
- `upload_limit`: The maximum upload file size
Permits these additional alert types:
-- pleroma:chat_mention
- pleroma:emoji_reaction
## Markers
## Streaming
-### Chats
-
-There is an additional `user:pleroma_chat` stream. Incoming chat messages will make the current chat be sent to this `user` stream. The `event` of an incoming chat message is `pleroma:chat_update`. The payload is the updated chat with the incoming chat message in the `last_message` field.
-
### Remote timelines
For viewing remote server timelines, there are `public:remote` and `public:remote:media` streams. Each of these accept a parameter like `?instance=lain.com`.
"shareable_emoji_packs",
"multifetch",
"pleroma:api/v1/notifications:include_types_filter",
- "chat",
- "shout",
"relay",
- "pleroma_emoji_reactions",
- "pleroma_chat_messages"
+ "pleroma_emoji_reactions"
],
"federation":{
"enabled":true,
"shareable_emoji_packs",
"multifetch",
"pleroma:api/v1/notifications:include_types_filter",
- "chat",
- "shout",
"relay",
- "pleroma_emoji_reactions",
- "pleroma_chat_messages"
+ "pleroma_emoji_reactions"
],
"federation":{
"enabled":true,
The object given in the reponse should then be inserted into an Object's `attachment` field.
-## ChatMessages
-
-`ChatMessage`s are the messages sent in 1-on-1 chats. They are similar to
-`Note`s, but the addresing is done by having a single AP actor in the `to`
-field. Addressing multiple actors is not allowed. These messages are always
-private, there is no public version of them. They are created with a `Create`
-activity.
-
-They are part of the `litepub` namespace as `http://litepub.social/ns#ChatMessage`.
-
-Example:
-
-```json
-{
- "actor": "http://2hu.gensokyo/users/raymoo",
- "id": "http://2hu.gensokyo/objects/1",
- "object": {
- "attributedTo": "http://2hu.gensokyo/users/raymoo",
- "content": "You expected a cute girl? Too bad.",
- "id": "http://2hu.gensokyo/objects/2",
- "published": "2020-02-12T14:08:20Z",
- "to": [
- "http://2hu.gensokyo/users/marisa"
- ],
- "type": "ChatMessage"
- },
- "published": "2018-02-12T14:08:20Z",
- "to": [
- "http://2hu.gensokyo/users/marisa"
- ],
- "type": "Create"
-}
-```
-
-This setup does not prevent multi-user chats, but these will have to go through
-a `Group`, which will be the recipient of the messages and then `Announce` them
-to the users in the `Group`.
] ++
elasticsearch_children() ++
task_children(@mix_env) ++
- dont_run_in_test(@mix_env) ++
- shout_child(shout_enabled?())
+ dont_run_in_test(@mix_env)
# See http://elixir-lang.org/docs/stable/elixir/Supervisor.html
# for other strategies and supported options
end
opts = [strategy: :one_for_one, name: Pleroma.Supervisor, max_restarts: max_restarts]
- result = Supervisor.start_link(children, opts)
- set_postgres_server_version()
-
- result
+ with {:ok, data} <- Supervisor.start_link(children, opts) do
+ set_postgres_server_version()
+ {:ok, data}
+ else
+ e ->
+ Logger.error("Failed to start!")
+ Logger.error("#{inspect(e)}")
+ e
+ end
end
defp set_postgres_server_version 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("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(),
- limit: 500_000
- )
+ build_cachex("banned_urls", default_ttl: :timer.hours(24 * 30), limit: 5_000)
]
end
defp idempotency_expiration,
do: expiration(default: :timer.seconds(6 * 60 * 60), interval: :timer.seconds(60))
- defp chat_message_id_idempotency_key_expiration,
- do: expiration(default: :timer.minutes(2), interval: :timer.seconds(60))
-
defp seconds_valid_interval,
do: :timer.seconds(Config.get!([Pleroma.Captcha, :seconds_valid]))
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(_) do
]
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
[
%{
+++ /dev/null
-# Pleroma: A lightweight social networking server
-# Copyright © 2017-2021 Pleroma Authors <https://pleroma.social/>
-# SPDX-License-Identifier: AGPL-3.0-only
-
-defmodule Pleroma.Chat do
- use Ecto.Schema
-
- import Ecto.Changeset
- import Ecto.Query
-
- alias Pleroma.Chat
- alias Pleroma.Repo
- alias Pleroma.User
-
- @moduledoc """
- Chat keeps a reference to ChatMessage conversations between a user and an recipient. The recipient can be a user (for now) or a group (not implemented yet).
-
- It is a helper only, to make it easy to display a list of chats with other people, ordered by last bump. The actual messages are retrieved by querying the recipients of the ChatMessages.
- """
-
- @type t :: %__MODULE__{}
- @primary_key {:id, FlakeId.Ecto.CompatType, autogenerate: true}
-
- schema "chats" do
- belongs_to(:user, User, type: FlakeId.Ecto.CompatType)
- field(:recipient, :string)
-
- timestamps()
- end
-
- def changeset(struct, params) do
- struct
- |> cast(params, [:user_id, :recipient])
- |> validate_change(:recipient, fn
- :recipient, recipient ->
- case User.get_cached_by_ap_id(recipient) do
- nil -> [recipient: "must be an existing user"]
- _ -> []
- end
- end)
- |> validate_required([:user_id, :recipient])
- |> unique_constraint(:user_id, name: :chats_user_id_recipient_index)
- end
-
- @spec get_by_user_and_id(User.t(), FlakeId.Ecto.CompatType.t()) ::
- {:ok, t()} | {:error, :not_found}
- def get_by_user_and_id(%User{id: user_id}, id) do
- from(c in __MODULE__,
- where: c.id == ^id,
- where: c.user_id == ^user_id
- )
- |> Repo.find_resource()
- end
-
- @spec get_by_id(FlakeId.Ecto.CompatType.t()) :: t() | nil
- def get_by_id(id) do
- Repo.get(__MODULE__, id)
- end
-
- @spec get(FlakeId.Ecto.CompatType.t(), String.t()) :: t() | nil
- def get(user_id, recipient) do
- Repo.get_by(__MODULE__, user_id: user_id, recipient: recipient)
- end
-
- @spec get_or_create(FlakeId.Ecto.CompatType.t(), String.t()) ::
- {:ok, t()} | {:error, Ecto.Changeset.t()}
- def get_or_create(user_id, recipient) do
- %__MODULE__{}
- |> changeset(%{user_id: user_id, recipient: recipient})
- |> Repo.insert(
- # Need to set something, otherwise we get nothing back at all
- on_conflict: [set: [recipient: recipient]],
- returning: true,
- conflict_target: [:user_id, :recipient]
- )
- end
-
- @spec bump_or_create(FlakeId.Ecto.CompatType.t(), String.t()) ::
- {:ok, t()} | {:error, Ecto.Changeset.t()}
- def bump_or_create(user_id, recipient) do
- %__MODULE__{}
- |> changeset(%{user_id: user_id, recipient: recipient})
- |> Repo.insert(
- on_conflict: [set: [updated_at: NaiveDateTime.utc_now()]],
- returning: true,
- conflict_target: [:user_id, :recipient]
- )
- end
-
- @spec for_user_query(FlakeId.Ecto.CompatType.t()) :: Ecto.Query.t()
- def for_user_query(user_id) do
- from(c in Chat,
- where: c.user_id == ^user_id,
- order_by: [desc: c.updated_at]
- )
- end
-end
+++ /dev/null
-# Pleroma: A lightweight social networking server
-# Copyright © 2017-2021 Pleroma Authors <https://pleroma.social/>
-# SPDX-License-Identifier: AGPL-3.0-only
-
-defmodule Pleroma.Chat.MessageReference do
- @moduledoc """
- A reference that builds a relation between an AP chat message that a user can see and whether it has been seen
- by them, or should be displayed to them. Used to build the chat view that is presented to the user.
- """
-
- use Ecto.Schema
-
- alias Pleroma.Chat
- alias Pleroma.Object
- alias Pleroma.Repo
-
- import Ecto.Changeset
- import Ecto.Query
-
- @primary_key {:id, FlakeId.Ecto.Type, autogenerate: true}
-
- schema "chat_message_references" do
- belongs_to(:object, Object)
- belongs_to(:chat, Chat, type: FlakeId.Ecto.CompatType)
-
- field(:unread, :boolean, default: true)
-
- timestamps()
- end
-
- def changeset(struct, params) do
- struct
- |> cast(params, [:object_id, :chat_id, :unread])
- |> validate_required([:object_id, :chat_id, :unread])
- end
-
- def get_by_id(id) do
- __MODULE__
- |> Repo.get(id)
- |> Repo.preload(:object)
- end
-
- def delete(cm_ref) do
- cm_ref
- |> Repo.delete()
- end
-
- def delete_for_object(%{id: object_id}) do
- from(cr in __MODULE__,
- where: cr.object_id == ^object_id
- )
- |> Repo.delete_all()
- end
-
- def for_chat_and_object(%{id: chat_id}, %{id: object_id}) do
- __MODULE__
- |> Repo.get_by(chat_id: chat_id, object_id: object_id)
- |> Repo.preload(:object)
- end
-
- def for_chat_query(chat) do
- from(cr in __MODULE__,
- where: cr.chat_id == ^chat.id,
- order_by: [desc: :id],
- preload: [:object]
- )
- end
-
- def last_message_for_chat(chat) do
- chat
- |> for_chat_query()
- |> limit(1)
- |> Repo.one()
- end
-
- def create(chat, object, unread) do
- params = %{
- chat_id: chat.id,
- object_id: object.id,
- unread: unread
- }
-
- %__MODULE__{}
- |> changeset(params)
- |> Repo.insert()
- end
-
- def unread_count_for_chat(chat) do
- chat
- |> for_chat_query()
- |> where([cmr], cmr.unread == true)
- |> Repo.aggregate(:count)
- end
-
- def mark_as_read(cm_ref) do
- cm_ref
- |> changeset(%{unread: false})
- |> Repo.update()
- end
-
- def set_all_seen_for_chat(chat, last_read_id \\ nil) do
- query =
- chat
- |> for_chat_query()
- |> exclude(:order_by)
- |> exclude(:preload)
- |> where([cmr], cmr.unread == true)
-
- if last_read_id do
- query
- |> where([cmr], cmr.id <= ^last_read_id)
- else
- query
- end
- |> Repo.update_all(set: [unread: false])
- end
-end
check_activity_expiration_config(),
check_remote_ip_plug_name(),
check_uploders_s3_public_endpoint(),
- check_old_chat_shoutbox(),
check_quarantined_instances_tuples(),
check_transparency_exclusions_tuples(),
check_simple_policy_tuples()
:ok
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
defp reboot_time_keys,
do: [
- {:pleroma, :shout},
{:pleroma, Oban},
{:pleroma, :rate_limit},
{:pleroma, :markup},
# SPDX-License-Identifier: AGPL-3.0-only
defmodule Pleroma.MigrationHelper.NotificationBackfill do
- alias Pleroma.Object
alias Pleroma.Repo
alias Pleroma.User
end
end
- defp type_from_activity_object(%{data: %{"type" => "Create", "object" => %{}}}), do: "mention"
-
- defp type_from_activity_object(%{data: %{"type" => "Create"}} = activity) do
- object = Object.get_by_ap_id(activity.data["object"])
-
- case object && object.data["type"] do
- "ChatMessage" -> "pleroma:chat_mention"
- _ -> "mention"
- end
- end
+ defp type_from_activity_object(%{data: %{"type" => "Create"}}), do: "mention"
end
insert_log_entry_with_message(%ModerationLog{data: data})
end
- def insert_log(%{actor: %User{} = actor, action: "chat_message_delete", subject_id: subject_id}) do
- %ModerationLog{
- data: %{
- "actor" => %{"nickname" => actor.nickname},
- "action" => "chat_message_delete",
- "subject_id" => subject_id
- }
- }
- |> insert_log_entry_with_message()
- end
-
@spec insert_log_entry_with_message(ModerationLog) :: {:ok, ModerationLog} | {:error, any}
defp insert_log_entry_with_message(entry) do
entry.data["message"]
"@#{actor_nickname} updated users: #{users_to_nicknames_string(subjects)}"
end
- def get_log_entry_message(%ModerationLog{
- data: %{
- "actor" => %{"nickname" => actor_nickname},
- "action" => "chat_message_delete",
- "subject_id" => subject_id
- }
- }) do
- "@#{actor_nickname} deleted chat message ##{subject_id}"
- end
-
def get_log_entry_message(%ModerationLog{
data: %{
"actor" => %{"nickname" => actor_nickname},
follow_request
mention
move
- pleroma:chat_mention
pleroma:emoji_reaction
pleroma:report
reblog
end
end
- defp type_from_activity_object(%{data: %{"type" => "Create", "object" => %{}}}), do: "mention"
-
- defp type_from_activity_object(%{data: %{"type" => "Create"}} = activity) do
- object = Object.get_by_ap_id(activity.data["object"])
-
- case object && object.data["type"] do
- "ChatMessage" -> "pleroma:chat_mention"
- _ -> "mention"
- end
- end
+ defp type_from_activity_object(%{data: %{"type" => "Create"}}), do: "mention"
# TODO move to sql, too.
def create_notification(%Activity{} = activity, %User{} = user, opts \\ []) do
field(:also_known_as, {:array, ObjectValidators.ObjectID}, default: [])
field(:inbox, :string)
field(:shared_inbox, :string)
- field(:accepts_chat_messages, :boolean, default: nil)
field(:last_active_at, :naive_datetime)
field(:disclose_client, :boolean, default: true)
field(:pinned_objects, :map, default: %{})
:invisible,
:actor_type,
:also_known_as,
- :accepts_chat_messages,
:pinned_objects
]
)
:pleroma_settings_store,
:is_discoverable,
:actor_type,
- :accepts_chat_messages,
:disclose_client
]
)
bio_limit = Config.get([:instance, :user_bio_length], 5000)
name_limit = Config.get([:instance, :user_name_length], 100)
reason_limit = Config.get([:instance, :registration_reason_length], 500)
- params = Map.put_new(params, :accepts_chat_messages, true)
confirmed? =
if is_nil(opts[:confirmed]) do
:password,
:password_confirmation,
:emoji,
- :accepts_chat_messages,
:registration_reason,
:language
])
{:ok, user} <- set_cache(user),
{:ok, _} <- maybe_send_registration_email(user),
{:ok, _} <- maybe_send_welcome_email(user),
- {:ok, _} <- maybe_send_welcome_message(user),
- {:ok, _} <- maybe_send_welcome_chat_message(user) do
+ {:ok, _} <- maybe_send_welcome_message(user) do
{:ok, user}
end
end
end
end
- defp maybe_send_welcome_chat_message(user) do
- if User.WelcomeChatMessage.enabled?() do
- User.WelcomeChatMessage.post_message(user)
- {:ok, :enqueued}
- else
- {:ok, :noop}
- end
- end
-
defp maybe_send_welcome_email(%User{email: email} = user) when is_binary(email) do
if User.WelcomeEmail.enabled?() do
User.WelcomeEmail.send_email(user)
+++ /dev/null
-# Pleroma: A lightweight social networking server
-# Copyright © 2017-2021 Pleroma Authors <https://pleroma.social/>
-# SPDX-License-Identifier: AGPL-3.0-only
-
-defmodule Pleroma.User.WelcomeChatMessage do
- alias Pleroma.Config
- alias Pleroma.User
- alias Pleroma.Web.CommonAPI
-
- @spec enabled?() :: boolean()
- def enabled?, do: Config.get([:welcome, :chat_message, :enabled], false)
-
- @spec post_message(User.t()) :: {:ok, Pleroma.Activity.t() | nil}
- def post_message(user) do
- [:welcome, :chat_message, :sender_nickname]
- |> Config.get(nil)
- |> fetch_sender()
- |> do_post(user, welcome_message())
- end
-
- defp do_post(%User{} = sender, recipient, message)
- when is_binary(message) do
- CommonAPI.post_chat_message(
- sender,
- recipient,
- message
- )
- end
-
- defp do_post(_sender, _recipient, _message), do: {:ok, nil}
-
- defp fetch_sender(nickname) when is_binary(nickname) do
- with %User{local: true} = user <- User.get_cached_by_nickname(nickname) do
- user
- else
- _ -> nil
- end
- end
-
- defp fetch_sender(_), do: nil
-
- defp welcome_message do
- Config.get([:welcome, :chat_message, :message], nil)
- end
-end
defp increase_replies_count_if_reply(_create_data), do: :noop
- @object_types ~w[ChatMessage Question Answer Audio Video Event Article Note Page]
+ @object_types ~w[Question Answer Audio Video Event Article Note Page]
@impl true
def persist(%{"type" => type} = object, meta) when type in @object_types do
with {:ok, object} <- Object.create(object) do
end
end
- defp exclude_chat_messages(query, %{include_chat_messages: true}), do: query
-
- defp exclude_chat_messages(query, _) do
- if has_named_binding?(query, :object) do
- from([activity, object: o] in query,
- where: fragment("not(?->>'type' = ?)", o.data, "ChatMessage")
- )
- else
- query
- end
- end
-
defp exclude_invisible_actors(query, %{invisible_actors: true}), do: query
defp exclude_invisible_actors(query, _opts) do
|> restrict_filtered(opts)
|> Activity.restrict_deactivated_users()
|> exclude_poll_votes(opts)
- |> exclude_chat_messages(opts)
|> exclude_invisible_actors(opts)
|> exclude_visibility(opts)
end)
is_locked = data["manuallyApprovesFollowers"] || false
- capabilities = data["capabilities"] || %{}
- accepts_chat_messages = capabilities["acceptsChatMessages"]
data = Transmogrifier.maybe_fix_user_object(data)
is_discoverable = data["discoverable"] || false
invisible = data["invisible"] || false
public_key: public_key,
inbox: data["inbox"],
shared_inbox: shared_inbox,
- accepts_chat_messages: accepts_chat_messages,
pinned_objects: pinned_objects
}
end
end
- def chat_message(actor, recipient, content, opts \\ []) do
- basic = %{
- "id" => Utils.generate_object_id(),
- "actor" => actor.ap_id,
- "type" => "ChatMessage",
- "to" => [recipient],
- "content" => content,
- "published" => DateTime.utc_now() |> DateTime.to_iso8601(),
- "emoji" => Emoji.Formatter.get_emoji_map(content)
- }
-
- case opts[:attachment] do
- %Object{data: attachment_data} ->
- {
- :ok,
- Map.put(basic, "attachment", attachment_data),
- []
- }
-
- _ ->
- {:ok, basic, []}
- end
- end
-
def answer(user, object, name) do
{:ok,
%{
alias Pleroma.Web.ActivityPub.ObjectValidators.ArticleNotePageValidator
alias Pleroma.Web.ActivityPub.ObjectValidators.AudioVideoValidator
alias Pleroma.Web.ActivityPub.ObjectValidators.BlockValidator
- alias Pleroma.Web.ActivityPub.ObjectValidators.ChatMessageValidator
- alias Pleroma.Web.ActivityPub.ObjectValidators.CreateChatMessageValidator
alias Pleroma.Web.ActivityPub.ObjectValidators.CreateGenericValidator
alias Pleroma.Web.ActivityPub.ObjectValidators.DeleteValidator
alias Pleroma.Web.ActivityPub.ObjectValidators.EmojiReactValidator
end
end
- def validate(
- %{"type" => "Create", "object" => %{"type" => "ChatMessage"} = object} = create_activity,
- meta
- ) do
- with {:ok, object_data} <- cast_and_apply(object),
- meta = Keyword.put(meta, :object_data, object_data |> stringify_keys),
- {:ok, create_activity} <-
- create_activity
- |> CreateChatMessageValidator.cast_and_validate(meta)
- |> Ecto.Changeset.apply_action(:insert) do
- create_activity = stringify_keys(create_activity)
- {:ok, create_activity, meta}
- end
- end
-
def validate(
%{"type" => "Create", "object" => %{"type" => objtype} = object} = create_activity,
meta
def validate(%{"type" => type} = object, meta)
when type in ~w[Accept Reject Follow Update Like EmojiReact Announce
- ChatMessage Answer] do
+ Answer] do
validator =
case type do
"Accept" -> AcceptRejectValidator
"Like" -> LikeValidator
"EmojiReact" -> EmojiReactValidator
"Announce" -> AnnounceValidator
- "ChatMessage" -> ChatMessageValidator
"Answer" -> AnswerValidator
end
def validate(o, m), do: {:error, {:validator_not_set, {o, m}}}
- def cast_and_apply(%{"type" => "ChatMessage"} = object) do
- ChatMessageValidator.cast_and_apply(object)
- end
-
def cast_and_apply(%{"type" => "Question"} = object) do
QuestionValidator.cast_and_apply(object)
end
+++ /dev/null
-# Pleroma: A lightweight social networking server
-# Copyright © 2017-2021 Pleroma Authors <https://pleroma.social/>
-# SPDX-License-Identifier: AGPL-3.0-only
-
-defmodule Pleroma.Web.ActivityPub.ObjectValidators.ChatMessageValidator do
- use Ecto.Schema
-
- alias Pleroma.EctoType.ActivityPub.ObjectValidators
- alias Pleroma.User
- alias Pleroma.Web.ActivityPub.ObjectValidators.AttachmentValidator
-
- import Ecto.Changeset
- import Pleroma.Web.ActivityPub.Transmogrifier, only: [fix_emoji: 1]
-
- @primary_key false
- @derive Jason.Encoder
-
- embedded_schema do
- field(:id, ObjectValidators.ObjectID, primary_key: true)
- field(:to, ObjectValidators.Recipients, default: [])
- field(:type, :string)
- field(:content, ObjectValidators.SafeText)
- field(:actor, ObjectValidators.ObjectID)
- field(:published, ObjectValidators.DateTime)
- field(:emoji, ObjectValidators.Emoji, default: %{})
-
- embeds_one(:attachment, AttachmentValidator)
- end
-
- def cast_and_apply(data) do
- data
- |> cast_data
- |> apply_action(:insert)
- end
-
- def cast_and_validate(data) do
- data
- |> cast_data()
- |> validate_data()
- end
-
- def cast_data(data) do
- %__MODULE__{}
- |> changeset(data)
- end
-
- def fix(data) do
- data
- |> fix_emoji()
- |> fix_attachment()
- |> Map.put_new("actor", data["attributedTo"])
- end
-
- # Throws everything but the first one away
- def fix_attachment(%{"attachment" => [attachment | _]} = data) do
- data
- |> Map.put("attachment", attachment)
- end
-
- def fix_attachment(data), do: data
-
- def changeset(struct, data) do
- data = fix(data)
-
- struct
- |> cast(data, List.delete(__schema__(:fields), :attachment))
- |> cast_embed(:attachment)
- end
-
- defp validate_data(data_cng) do
- data_cng
- |> validate_inclusion(:type, ["ChatMessage"])
- |> validate_required([:id, :actor, :to, :type, :published])
- |> validate_content_or_attachment()
- |> validate_length(:to, is: 1)
- |> validate_length(:content, max: Pleroma.Config.get([:instance, :remote_limit]))
- |> validate_local_concern()
- end
-
- def validate_content_or_attachment(cng) do
- attachment = get_field(cng, :attachment)
-
- if attachment do
- cng
- else
- cng
- |> validate_required([:content])
- end
- end
-
- @doc """
- Validates the following
- - If both users are in our system
- - If at least one of the users in this ChatMessage is a local user
- - If the recipient is not blocking the actor
- - If the recipient is explicitly not accepting chat messages
- """
- def validate_local_concern(cng) do
- with actor_ap <- get_field(cng, :actor),
- {_, %User{} = actor} <- {:find_actor, User.get_cached_by_ap_id(actor_ap)},
- {_, %User{} = recipient} <-
- {:find_recipient, User.get_cached_by_ap_id(get_field(cng, :to) |> hd())},
- {_, false} <- {:not_accepting_chats?, recipient.accepts_chat_messages == false},
- {_, false} <- {:blocking_actor?, User.blocks?(recipient, actor)},
- {_, true} <- {:local?, Enum.any?([actor, recipient], & &1.local)} do
- cng
- else
- {:blocking_actor?, true} ->
- cng
- |> add_error(:actor, "actor is blocked by recipient")
-
- {:not_accepting_chats?, true} ->
- cng
- |> add_error(:to, "recipient does not accept chat messages")
-
- {:local?, false} ->
- cng
- |> add_error(:actor, "actor and recipient are both remote")
-
- {:find_actor, _} ->
- cng
- |> add_error(:actor, "can't find user")
-
- {:find_recipient, _} ->
- cng
- |> add_error(:to, "can't find user")
- end
- end
-end
alias Pleroma.Web.ActivityPub.ObjectValidators.AttachmentValidator
alias Pleroma.Web.ActivityPub.ObjectValidators.TagValidator
- # Activities and Objects, except (Create)ChatMessage
+ # Activities and Objects
defmacro message_fields do
quote bind_quoted: binding() do
field(:type, :string)
end
end
- # Basically objects that aren't ChatMessage and Answer
+ # Basically objects that aren't Answer
defmacro status_object_fields do
quote bind_quoted: binding() do
# TODO: Remove actor on objects
+++ /dev/null
-# Pleroma: A lightweight social networking server
-# Copyright © 2017-2021 Pleroma Authors <https://pleroma.social/>
-# SPDX-License-Identifier: AGPL-3.0-only
-
-# NOTES
-# - Can probably be a generic create validator
-# - doesn't embed, will only get the object id
-defmodule Pleroma.Web.ActivityPub.ObjectValidators.CreateChatMessageValidator do
- use Ecto.Schema
- alias Pleroma.EctoType.ActivityPub.ObjectValidators
-
- alias Pleroma.Object
-
- 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
- activity_fields()
- end
- end
-
- field(:id, ObjectValidators.ObjectID, primary_key: true)
- field(:type, :string)
- field(:to, ObjectValidators.Recipients, default: [])
- end
-
- def cast_and_apply(data) do
- data
- |> cast_data
- |> apply_action(:insert)
- end
-
- def cast_data(data) do
- cast(%__MODULE__{}, data, __schema__(:fields))
- end
-
- def cast_and_validate(data, meta \\ []) do
- cast_data(data)
- |> validate_data(meta)
- end
-
- defp validate_data(cng, meta) do
- cng
- |> validate_required([:id, :actor, :to, :type, :object])
- |> validate_inclusion(:type, ["Create"])
- |> validate_actor_presence()
- |> validate_recipients_match(meta)
- |> validate_actors_match(meta)
- |> validate_object_nonexistence()
- end
-
- def validate_object_nonexistence(cng) do
- cng
- |> validate_change(:object, fn :object, object_id ->
- if Object.get_cached_by_ap_id(object_id) do
- [{:object, "The object to create already exists"}]
- else
- []
- end
- end)
- end
-
- def validate_actors_match(cng, meta) do
- object_actor = meta[:object_data]["actor"]
-
- cng
- |> validate_change(:actor, fn :actor, actor ->
- if actor == object_actor do
- []
- else
- [{:actor, "Actor doesn't match with object actor"}]
- end
- end)
- end
-
- def validate_recipients_match(cng, meta) do
- object_recipients = meta[:object_data]["to"] || []
-
- cng
- |> validate_change(:to, fn :to, recipients ->
- activity_set = MapSet.new(recipients)
- object_set = MapSet.new(object_recipients)
-
- if MapSet.equal?(activity_set, object_set) do
- []
- else
- [{:to, "Recipients don't match with object recipients"}]
- end
- end)
- end
-end
Answer
Article
Audio
- ChatMessage
Event
Note
Page
collection, and so on.
"""
alias Pleroma.Activity
- alias Pleroma.Chat
- alias Pleroma.Chat.MessageReference
alias Pleroma.FollowingRelationship
alias Pleroma.Notification
alias Pleroma.Object
require Logger
- @cachex Pleroma.Config.get([:cachex, :provider], Cachex)
@logger Pleroma.Config.get([:side_effects, :logger], Logger)
@behaviour Pleroma.Web.ActivityPub.SideEffects.Handling
Object.decrease_replies_count(in_reply_to)
end
- MessageReference.delete_for_object(deleted_object)
-
ap_streamer().stream_out(object)
ap_streamer().stream_out_participations(deleted_object, user)
:ok
{:ok, object, meta}
end
- def handle_object_creation(%{"type" => "ChatMessage"} = object, _activity, meta) do
- with {:ok, object, meta} <- Pipeline.common_pipeline(object, meta) do
- actor = User.get_cached_by_ap_id(object.data["actor"])
- recipient = User.get_cached_by_ap_id(hd(object.data["to"]))
-
- streamables =
- [[actor, recipient], [recipient, actor]]
- |> Enum.uniq()
- |> Enum.map(fn [user, other_user] ->
- if user.local do
- {:ok, chat} = Chat.bump_or_create(user.id, other_user.ap_id)
- {:ok, cm_ref} = MessageReference.create(chat, object, user.ap_id != actor.ap_id)
-
- @cachex.put(
- :chat_message_id_idempotency_key_cache,
- cm_ref.id,
- meta[:idempotency_key]
- )
-
- {
- ["user", "user:pleroma_chat"],
- {user, %{cm_ref | chat: chat, object: object}}
- }
- end
- end)
- |> Enum.filter(& &1)
-
- meta =
- meta
- |> add_streamables(streamables)
-
- {:ok, object, meta}
- end
- end
-
def handle_object_creation(%{"type" => "Question"} = object, activity, meta) do
with {:ok, object, meta} <- Pipeline.common_pipeline(object, meta) do
PollWorker.schedule_poll_end(activity)
meta
end
- defp add_streamables(meta, streamables) do
- existing = Keyword.get(meta, :streamables, [])
-
- meta
- |> Keyword.put(:streamables, streamables ++ existing)
- end
-
defp add_notifications(meta, notifications) do
existing = Keyword.get(meta, :notifications, [])
%{"type" => "Create", "object" => %{"type" => objtype, "id" => obj_id}} = data,
options
)
- when objtype in ~w{Question Answer ChatMessage Audio Video Event Article Note Page} do
+ when objtype in ~w{Question Answer Audio Video Event Article Note Page} do
fetch_options = Keyword.put(options, :depth, (options[:depth] || 0) + 1)
object =
Map.put(object, "attributedTo", attributed_to)
end
- # TODO: Revisit this
- def prepare_attachments(%{"type" => "ChatMessage"} = object), do: object
-
def prepare_attachments(object) do
attachments =
object
fields = Enum.map(user.fields, &Map.put(&1, "type", "PropertyValue"))
- capabilities =
- if is_boolean(user.accepts_chat_messages) do
- %{
- "acceptsChatMessages" => user.accepts_chat_messages
- }
- else
- %{}
- end
+ capabilities = %{}
%{
"id" => user.ap_id,
when action in [:list_user_statuses]
)
- plug(
- OAuthScopesPlug,
- %{scopes: ["admin:read:chats"]}
- when action in [:list_user_chats]
- )
-
plug(
OAuthScopesPlug,
%{scopes: ["admin:read"]}
end
end
- def list_user_chats(%{assigns: %{user: admin}} = conn, %{"nickname" => nickname} = _params) do
- with %User{id: user_id} <- User.get_cached_by_nickname_or_id(nickname, for: admin) do
- chats =
- Pleroma.Chat.for_user_query(user_id)
- |> Pleroma.Repo.all()
-
- conn
- |> put_view(AdminAPI.ChatView)
- |> render("index.json", chats: chats)
- else
- _ -> {:error, :not_found}
- end
- end
-
def tag_users(%{assigns: %{user: admin}} = conn, %{"nicknames" => nicknames, "tags" => tags}) do
with {:ok, _} <- User.tag(nicknames, tags) do
ModerationLog.insert_log(%{
+++ /dev/null
-# Pleroma: A lightweight social networking server
-# Copyright © 2017-2021 Pleroma Authors <https://pleroma.social/>
-# SPDX-License-Identifier: AGPL-3.0-only
-
-defmodule Pleroma.Web.AdminAPI.ChatController do
- use Pleroma.Web, :controller
-
- alias Pleroma.Activity
- alias Pleroma.Chat
- alias Pleroma.Chat.MessageReference
- alias Pleroma.ModerationLog
- alias Pleroma.Pagination
- alias Pleroma.Web.AdminAPI
- alias Pleroma.Web.CommonAPI
- alias Pleroma.Web.PleromaAPI.Chat.MessageReferenceView
- alias Pleroma.Web.Plugs.OAuthScopesPlug
-
- require Logger
-
- plug(Pleroma.Web.ApiSpec.CastAndValidate)
-
- plug(
- OAuthScopesPlug,
- %{scopes: ["admin:read:chats"]} when action in [:show, :messages]
- )
-
- plug(
- OAuthScopesPlug,
- %{scopes: ["admin:write:chats"]} when action in [:delete_message]
- )
-
- action_fallback(Pleroma.Web.AdminAPI.FallbackController)
-
- defdelegate open_api_operation(action), to: Pleroma.Web.ApiSpec.Admin.ChatOperation
-
- def delete_message(%{assigns: %{user: user}} = conn, %{
- message_id: message_id,
- id: chat_id
- }) do
- with %MessageReference{object: %{data: %{"id" => object_ap_id}}} = cm_ref <-
- MessageReference.get_by_id(message_id),
- ^chat_id <- to_string(cm_ref.chat_id),
- %Activity{id: activity_id} <- Activity.get_create_by_object_ap_id(object_ap_id),
- {:ok, _} <- CommonAPI.delete(activity_id, user) do
- ModerationLog.insert_log(%{
- action: "chat_message_delete",
- actor: user,
- subject_id: message_id
- })
-
- conn
- |> put_view(MessageReferenceView)
- |> render("show.json", chat_message_reference: cm_ref)
- else
- _e ->
- {:error, :could_not_delete}
- end
- end
-
- def messages(conn, %{id: id} = params) do
- with %Chat{} = chat <- Chat.get_by_id(id) do
- cm_refs =
- chat
- |> MessageReference.for_chat_query()
- |> Pagination.fetch_paginated(params)
-
- conn
- |> put_view(MessageReferenceView)
- |> render("index.json", chat_message_references: cm_refs)
- else
- _ ->
- conn
- |> put_status(:not_found)
- |> json(%{error: "not found"})
- end
- end
-
- def show(conn, %{id: id}) do
- with %Chat{} = chat <- Chat.get_by_id(id) do
- conn
- |> put_view(AdminAPI.ChatView)
- |> render("show.json", chat: chat)
- end
- end
-end
+++ /dev/null
-# Pleroma: A lightweight social networking server
-# Copyright © 2017-2021 Pleroma Authors <https://pleroma.social/>
-# SPDX-License-Identifier: AGPL-3.0-only
-
-defmodule Pleroma.Web.AdminAPI.ChatView do
- use Pleroma.Web, :view
-
- alias Pleroma.Chat
- alias Pleroma.User
- alias Pleroma.Web.MastodonAPI
- alias Pleroma.Web.PleromaAPI
-
- def render("index.json", %{chats: chats} = opts) do
- render_many(chats, __MODULE__, "show.json", Map.delete(opts, :chats))
- end
-
- def render("show.json", %{chat: %Chat{user_id: user_id}} = opts) do
- user = User.get_by_id(user_id)
- sender = MastodonAPI.AccountView.render("show.json", user: user, skip_visibility_check: true)
-
- serialized_chat = PleromaAPI.ChatView.render("show.json", opts)
-
- serialized_chat
- |> Map.put(:sender, sender)
- |> Map.put(:receiver, serialized_chat[:account])
- |> Map.delete(:account)
- end
-
- def render(view, opts), do: PleromaAPI.ChatView.render(view, opts)
-end
%{
"name" => "Administration",
"tags" => [
- "Chat administration",
"Emoji pack administration",
"Frontend managment",
"Instance configuration",
]
},
%{"name" => "Instance", "tags" => ["Custom emojis"]},
- %{"name" => "Messaging", "tags" => ["Chats", "Conversations"]},
%{
"name" => "Statuses",
"tags" => [
nullable: true,
description: "Whether manual approval of follow requests is required."
},
- accepts_chat_messages: %Schema{
- allOf: [BooleanLike],
- nullable: true,
- description: "Whether the user accepts receiving chat messages."
- },
fields_attributes: %Schema{
nullable: true,
oneOf: [
+++ /dev/null
-# Pleroma: A lightweight social networking server
-# Copyright © 2017-2021 Pleroma Authors <https://pleroma.social/>
-# SPDX-License-Identifier: AGPL-3.0-only
-
-defmodule Pleroma.Web.ApiSpec.Admin.ChatOperation do
- alias OpenApiSpex.Operation
- alias Pleroma.Web.ApiSpec.Schemas.Chat
- alias Pleroma.Web.ApiSpec.Schemas.ChatMessage
-
- import Pleroma.Web.ApiSpec.Helpers
-
- def open_api_operation(action) do
- operation = String.to_existing_atom("#{action}_operation")
- apply(__MODULE__, operation, [])
- end
-
- def delete_message_operation do
- %Operation{
- tags: ["Chat administration"],
- summary: "Delete an individual chat message",
- operationId: "AdminAPI.ChatController.delete_message",
- parameters: [
- Operation.parameter(:id, :path, :string, "The ID of the Chat"),
- Operation.parameter(:message_id, :path, :string, "The ID of the message")
- ],
- responses: %{
- 200 =>
- Operation.response(
- "The deleted ChatMessage",
- "application/json",
- ChatMessage
- )
- },
- security: [
- %{
- "oAuth" => ["admin:write:chats"]
- }
- ]
- }
- end
-
- def messages_operation do
- %Operation{
- tags: ["Chat administration"],
- summary: "Get chat's messages",
- operationId: "AdminAPI.ChatController.messages",
- parameters:
- [Operation.parameter(:id, :path, :string, "The ID of the Chat")] ++
- pagination_params(),
- responses: %{
- 200 =>
- Operation.response(
- "The messages in the chat",
- "application/json",
- Pleroma.Web.ApiSpec.ChatOperation.chat_messages_response()
- )
- },
- security: [
- %{
- "oAuth" => ["admin:read:chats"]
- }
- ]
- }
- end
-
- def show_operation do
- %Operation{
- tags: ["Chat administration"],
- summary: "Create a chat",
- operationId: "AdminAPI.ChatController.show",
- parameters: [
- Operation.parameter(
- :id,
- :path,
- :string,
- "The id of the chat",
- required: true,
- example: "1234"
- )
- ],
- responses: %{
- 200 =>
- Operation.response(
- "The existing chat",
- "application/json",
- Chat
- )
- },
- security: [
- %{
- "oAuth" => ["admin:read"]
- }
- ]
- }
- end
-end
+++ /dev/null
-# Pleroma: A lightweight social networking server
-# Copyright © 2017-2021 Pleroma Authors <https://pleroma.social/>
-# SPDX-License-Identifier: AGPL-3.0-only
-
-defmodule Pleroma.Web.ApiSpec.ChatOperation do
- alias OpenApiSpex.Operation
- alias OpenApiSpex.Schema
- alias Pleroma.Web.ApiSpec.Schemas.ApiError
- alias Pleroma.Web.ApiSpec.Schemas.BooleanLike
- alias Pleroma.Web.ApiSpec.Schemas.Chat
- alias Pleroma.Web.ApiSpec.Schemas.ChatMessage
-
- import Pleroma.Web.ApiSpec.Helpers
-
- @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 mark_as_read_operation do
- %Operation{
- tags: ["Chats"],
- summary: "Mark all messages in the chat as read",
- operationId: "ChatController.mark_as_read",
- parameters: [Operation.parameter(:id, :path, :string, "The ID of the Chat")],
- requestBody: request_body("Parameters", mark_as_read()),
- responses: %{
- 200 =>
- Operation.response(
- "The updated chat",
- "application/json",
- Chat
- )
- },
- security: [
- %{
- "oAuth" => ["write:chats"]
- }
- ]
- }
- end
-
- def mark_message_as_read_operation do
- %Operation{
- tags: ["Chats"],
- summary: "Mark a message as read",
- operationId: "ChatController.mark_message_as_read",
- parameters: [
- Operation.parameter(:id, :path, :string, "The ID of the Chat"),
- Operation.parameter(:message_id, :path, :string, "The ID of the message")
- ],
- responses: %{
- 200 =>
- Operation.response(
- "The read ChatMessage",
- "application/json",
- ChatMessage
- )
- },
- security: [
- %{
- "oAuth" => ["write:chats"]
- }
- ]
- }
- end
-
- def show_operation do
- %Operation{
- tags: ["Chats"],
- summary: "Retrieve a chat",
- operationId: "ChatController.show",
- parameters: [
- Operation.parameter(
- :id,
- :path,
- :string,
- "The id of the chat",
- required: true,
- example: "1234"
- )
- ],
- responses: %{
- 200 =>
- Operation.response(
- "The existing chat",
- "application/json",
- Chat
- )
- },
- security: [
- %{
- "oAuth" => ["read"]
- }
- ]
- }
- end
-
- def create_operation do
- %Operation{
- tags: ["Chats"],
- summary: "Create a chat",
- operationId: "ChatController.create",
- parameters: [
- Operation.parameter(
- :id,
- :path,
- :string,
- "The account id of the recipient of this chat",
- required: true,
- example: "someflakeid"
- )
- ],
- responses: %{
- 200 =>
- Operation.response(
- "The created or existing chat",
- "application/json",
- Chat
- )
- },
- security: [
- %{
- "oAuth" => ["write:chats"]
- }
- ]
- }
- end
-
- def index2_operation do
- %Operation{
- tags: ["Chats"],
- summary: "Retrieve list of chats",
- operationId: "ChatController.index2",
- parameters: [
- Operation.parameter(:with_muted, :query, BooleanLike, "Include chats from muted users")
- | pagination_params()
- ],
- responses: %{
- 200 => Operation.response("The chats of the user", "application/json", chats_response())
- },
- security: [
- %{
- "oAuth" => ["read:chats"]
- }
- ]
- }
- end
-
- def messages_operation do
- %Operation{
- tags: ["Chats"],
- summary: "Retrieve chat's messages",
- operationId: "ChatController.messages",
- parameters:
- [Operation.parameter(:id, :path, :string, "The ID of the Chat")] ++
- pagination_params(),
- responses: %{
- 200 =>
- Operation.response(
- "The messages in the chat",
- "application/json",
- chat_messages_response()
- ),
- 404 => Operation.response("Not Found", "application/json", ApiError)
- },
- security: [
- %{
- "oAuth" => ["read:chats"]
- }
- ]
- }
- end
-
- def post_chat_message_operation do
- %Operation{
- tags: ["Chats"],
- summary: "Post a message to the chat",
- operationId: "ChatController.post_chat_message",
- parameters: [
- Operation.parameter(:id, :path, :string, "The ID of the Chat")
- ],
- requestBody: request_body("Parameters", chat_message_create()),
- responses: %{
- 200 =>
- Operation.response(
- "The newly created ChatMessage",
- "application/json",
- ChatMessage
- ),
- 400 => Operation.response("Bad Request", "application/json", ApiError),
- 422 => Operation.response("MRF Rejection", "application/json", ApiError)
- },
- security: [
- %{
- "oAuth" => ["write:chats"]
- }
- ]
- }
- end
-
- def delete_message_operation do
- %Operation{
- tags: ["Chats"],
- summary: "Delete message",
- operationId: "ChatController.delete_message",
- parameters: [
- Operation.parameter(:id, :path, :string, "The ID of the Chat"),
- Operation.parameter(:message_id, :path, :string, "The ID of the message")
- ],
- responses: %{
- 200 =>
- Operation.response(
- "The deleted ChatMessage",
- "application/json",
- ChatMessage
- )
- },
- security: [
- %{
- "oAuth" => ["write:chats"]
- }
- ]
- }
- end
-
- def chats_response do
- %Schema{
- title: "ChatsResponse",
- description: "Response schema for multiple Chats",
- type: :array,
- items: Chat,
- example: [
- %{
- "account" => %{
- "pleroma" => %{
- "is_admin" => false,
- "is_confirmed" => true,
- "hide_followers_count" => false,
- "is_moderator" => false,
- "hide_favorites" => true,
- "ap_id" => "https://dontbulling.me/users/lain",
- "hide_follows_count" => false,
- "hide_follows" => false,
- "background_image" => nil,
- "skip_thread_containment" => false,
- "hide_followers" => false,
- "relationship" => %{},
- "tags" => []
- },
- "avatar" =>
- "https://dontbulling.me/media/065a4dd3c6740dab13ff9c71ec7d240bb9f8be9205c9e7467fb2202117da1e32.jpg",
- "following_count" => 0,
- "header_static" => "https://originalpatchou.li/images/banner.png",
- "source" => %{
- "sensitive" => false,
- "note" => "lain",
- "pleroma" => %{
- "discoverable" => false,
- "actor_type" => "Person"
- },
- "fields" => []
- },
- "statuses_count" => 1,
- "locked" => false,
- "created_at" => "2020-04-16T13:40:15.000Z",
- "display_name" => "lain",
- "fields" => [],
- "acct" => "lain@dontbulling.me",
- "id" => "9u6Qw6TAZANpqokMkK",
- "emojis" => [],
- "avatar_static" =>
- "https://dontbulling.me/media/065a4dd3c6740dab13ff9c71ec7d240bb9f8be9205c9e7467fb2202117da1e32.jpg",
- "username" => "lain",
- "followers_count" => 0,
- "header" => "https://originalpatchou.li/images/banner.png",
- "bot" => false,
- "note" => "lain",
- "url" => "https://dontbulling.me/users/lain"
- },
- "id" => "1",
- "unread" => 2
- }
- ]
- }
- end
-
- def chat_messages_response do
- %Schema{
- title: "ChatMessagesResponse",
- description: "Response schema for multiple ChatMessages",
- type: :array,
- items: ChatMessage,
- example: [
- %{
- "emojis" => [
- %{
- "static_url" => "https://dontbulling.me/emoji/Firefox.gif",
- "visible_in_picker" => false,
- "shortcode" => "firefox",
- "url" => "https://dontbulling.me/emoji/Firefox.gif"
- }
- ],
- "created_at" => "2020-04-21T15:11:46.000Z",
- "content" => "Check this out :firefox:",
- "id" => "13",
- "chat_id" => "1",
- "account_id" => "someflakeid",
- "unread" => false
- },
- %{
- "account_id" => "someflakeid",
- "content" => "Whats' up?",
- "id" => "12",
- "chat_id" => "1",
- "emojis" => [],
- "created_at" => "2020-04-21T15:06:45.000Z",
- "unread" => false
- }
- ]
- }
- end
-
- def chat_message_create do
- %Schema{
- title: "ChatMessageCreateRequest",
- description: "POST body for creating an chat message",
- type: :object,
- properties: %{
- content: %Schema{
- type: :string,
- description: "The content of your message. Optional if media_id is present"
- },
- media_id: %Schema{type: :string, description: "The id of an upload"}
- },
- example: %{
- "content" => "Hey wanna buy feet pics?",
- "media_id" => "134234"
- }
- }
- end
-
- def mark_as_read do
- %Schema{
- title: "MarkAsReadRequest",
- description: "POST body for marking a number of chat messages as read",
- type: :object,
- required: [:last_read_id],
- properties: %{
- last_read_id: %Schema{
- type: :string,
- description: "The content of your message."
- }
- },
- example: %{
- "last_read_id" => "abcdef12456"
- }
- }
- end
-end
"reblog",
"mention",
"pleroma:emoji_reaction",
- "pleroma:chat_mention",
"pleroma:report",
"move",
"follow_request",
- `poll` - A poll you have voted in or created has ended
- `move` - Someone moved their account
- `pleroma:emoji_reaction` - Someone reacted with emoji to your status
- - `pleroma:chat_mention` - Someone mentioned you in a chat message
- `pleroma:report` - Someone was reported
"""
}
nullable: true,
description: "Receive poll notifications?"
},
- "pleroma:chat_mention": %Schema{
- allOf: [BooleanLike],
- nullable: true,
- description: "Receive chat notifications?"
- },
"pleroma:emoji_reaction": %Schema{
allOf: [BooleanLike],
nullable: true,
nullable: true,
description: "Receive poll notifications?"
},
- "pleroma:chat_mention": %Schema{
- allOf: [BooleanLike],
- nullable: true,
- description: "Receive chat notifications?"
- },
"pleroma:emoji_reaction": %Schema{
allOf: [BooleanLike],
nullable: true,
tags: ["Timelines"],
summary: "Direct timeline",
description:
- "View statuses with a “direct” scope addressed to the account. Using this endpoint is discouraged, please use [conversations](#tag/Conversations) or [chats](#tag/Chats).",
+ "View statuses with a “direct” scope addressed to the account. Using this endpoint is discouraged, please use [conversations](#tag/Conversations).",
parameters: [with_muted_param() | pagination_params()],
security: [%{"oAuth" => ["read:statuses"]}],
operationId: "TimelineController.direct",
description: "whether the user allows automatically follow moved following accounts"
},
background_image: %Schema{type: :string, nullable: true, format: :uri},
- chat_token: %Schema{type: :string},
is_confirmed: %Schema{
type: :boolean,
description:
description:
"A generic map of settings for frontends. Opaque to the backend. Only returned in `verify_credentials` and `update_credentials`"
},
- accepts_chat_messages: %Schema{type: :boolean, nullable: true},
favicon: %Schema{
type: :string,
format: :uri,
"is_admin" => false,
"is_moderator" => false,
"skip_thread_containment" => false,
- "accepts_chat_messages" => true,
- "chat_token" =>
- "SFMyNTY.g3QAAAACZAAEZGF0YW0AAAASOXRLaTNlc2JHN09RZ1oyOTIwZAAGc2lnbmVkbgYARNplS3EB.Mb_Iaqew2bN1I1o79B_iP7encmVCpTKC4OtHZRxdjKc",
"unread_conversation_count" => 0,
"tags" => [],
"notification_settings" => %{
+++ /dev/null
-# Pleroma: A lightweight social networking server
-# Copyright © 2017-2021 Pleroma Authors <https://pleroma.social/>
-# SPDX-License-Identifier: AGPL-3.0-only
-
-defmodule Pleroma.Web.ApiSpec.Schemas.Chat do
- alias OpenApiSpex.Schema
- alias Pleroma.Web.ApiSpec.Schemas.ChatMessage
-
- require OpenApiSpex
-
- OpenApiSpex.schema(%{
- title: "Chat",
- description: "Response schema for a Chat",
- type: :object,
- properties: %{
- id: %Schema{type: :string},
- account: %Schema{type: :object},
- unread: %Schema{type: :integer},
- last_message: ChatMessage,
- updated_at: %Schema{type: :string, format: :"date-time"}
- },
- example: %{
- "account" => %{
- "pleroma" => %{
- "is_admin" => false,
- "is_confirmed" => true,
- "hide_followers_count" => false,
- "is_moderator" => false,
- "hide_favorites" => true,
- "ap_id" => "https://dontbulling.me/users/lain",
- "hide_follows_count" => false,
- "hide_follows" => false,
- "background_image" => nil,
- "skip_thread_containment" => false,
- "hide_followers" => false,
- "relationship" => %{},
- "tags" => []
- },
- "avatar" =>
- "https://dontbulling.me/media/065a4dd3c6740dab13ff9c71ec7d240bb9f8be9205c9e7467fb2202117da1e32.jpg",
- "following_count" => 0,
- "header_static" => "https://originalpatchou.li/images/banner.png",
- "source" => %{
- "sensitive" => false,
- "note" => "lain",
- "pleroma" => %{
- "discoverable" => false,
- "actor_type" => "Person"
- },
- "fields" => []
- },
- "statuses_count" => 1,
- "is_locked" => false,
- "created_at" => "2020-04-16T13:40:15.000Z",
- "display_name" => "lain",
- "fields" => [],
- "acct" => "lain@dontbulling.me",
- "id" => "9u6Qw6TAZANpqokMkK",
- "emojis" => [],
- "avatar_static" =>
- "https://dontbulling.me/media/065a4dd3c6740dab13ff9c71ec7d240bb9f8be9205c9e7467fb2202117da1e32.jpg",
- "username" => "lain",
- "followers_count" => 0,
- "header" => "https://originalpatchou.li/images/banner.png",
- "bot" => false,
- "note" => "lain",
- "url" => "https://dontbulling.me/users/lain"
- },
- "id" => "1",
- "unread" => 2,
- "last_message" => ChatMessage.schema().example(),
- "updated_at" => "2020-04-21T15:06:45.000Z"
- }
- })
-end
+++ /dev/null
-# Pleroma: A lightweight social networking server
-# Copyright © 2017-2021 Pleroma Authors <https://pleroma.social/>
-# SPDX-License-Identifier: AGPL-3.0-only
-
-defmodule Pleroma.Web.ApiSpec.Schemas.ChatMessage do
- alias OpenApiSpex.Schema
- alias Pleroma.Web.ApiSpec.Schemas.Emoji
-
- require OpenApiSpex
-
- OpenApiSpex.schema(%{
- title: "ChatMessage",
- description: "Response schema for a ChatMessage",
- nullable: true,
- type: :object,
- properties: %{
- id: %Schema{type: :string},
- account_id: %Schema{type: :string, description: "The Mastodon API id of the actor"},
- chat_id: %Schema{type: :string},
- content: %Schema{type: :string, nullable: true},
- created_at: %Schema{type: :string, format: :"date-time"},
- emojis: %Schema{type: :array, items: Emoji},
- attachment: %Schema{type: :object, nullable: true},
- 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"],
- 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"}
- }
- },
- unread: %Schema{type: :boolean, description: "Whether a message has been marked as read."}
- },
- example: %{
- "account_id" => "someflakeid",
- "chat_id" => "1",
- "content" => "hey you again",
- "created_at" => "2020-04-21T15:06:45.000Z",
- "card" => nil,
- "emojis" => [
- %{
- "static_url" => "https://dontbulling.me/emoji/Firefox.gif",
- "visible_in_picker" => false,
- "shortcode" => "firefox",
- "url" => "https://dontbulling.me/emoji/Firefox.gif"
- }
- ],
- "id" => "14",
- "attachment" => nil,
- "unread" => false
- }
- })
-end
+++ /dev/null
-# Pleroma: A lightweight social networking server
-# Copyright © 2017-2021 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
defmodule Pleroma.Web.CommonAPI do
alias Pleroma.Activity
alias Pleroma.Conversation.Participation
- alias Pleroma.Formatter
alias Pleroma.Object
alias Pleroma.ThreadMute
alias Pleroma.User
end
end
- def post_chat_message(%User{} = user, %User{} = recipient, content, opts \\ []) do
- with maybe_attachment <- opts[:media_id] && Object.get_by_id(opts[:media_id]),
- :ok <- validate_chat_content_length(content, !!maybe_attachment),
- {_, {:ok, chat_message_data, _meta}} <-
- {:build_object,
- Builder.chat_message(
- user,
- recipient.ap_id,
- content |> format_chat_content,
- attachment: maybe_attachment
- )},
- {_, {:ok, create_activity_data, _meta}} <-
- {:build_create_activity, Builder.create(user, chat_message_data, [recipient.ap_id])},
- {_, {:ok, %Activity{} = activity, _meta}} <-
- {:common_pipeline,
- Pipeline.common_pipeline(create_activity_data,
- local: true,
- idempotency_key: opts[:idempotency_key]
- )} do
- {:ok, activity}
- else
- {:common_pipeline, {:reject, _} = e} -> e
- e -> e
- end
- end
-
- defp format_chat_content(nil), do: nil
-
- defp format_chat_content(content) do
- {text, _, _} =
- content
- |> Formatter.html_escape("text/plain")
- |> Formatter.linkify()
- |> (fn {text, mentions, tags} ->
- {String.replace(text, ~r/\r?\n/, "<br>"), mentions, tags}
- end).()
-
- text
- end
-
- defp validate_chat_content_length(_, true), do: :ok
- defp validate_chat_content_length(nil, false), do: {:error, :no_content}
-
- defp validate_chat_content_length(content, _) do
- if String.length(content) <= Pleroma.Config.get([:instance, :chat_limit]) do
- :ok
- else
- {:error, :content_too_long}
- end
- end
-
def unblock(blocker, blocked) do
with {_, %Activity{} = block} <- {:fetch_block, Utils.fetch_latest_block(blocker, blocked)},
{:ok, unblock_data, _} <- Builder.undo(blocker, block),
alias Pleroma.Config
- socket("/socket", Pleroma.Web.UserSocket)
socket("/live", Phoenix.LiveView.Socket)
plug(Pleroma.Web.Plugs.SetLocalePlug)
@doc "GET /api/v1/accounts/verify_credentials"
def verify_credentials(%{assigns: %{user: user}} = conn, _) do
- chat_token = Phoenix.Token.sign(conn, "user socket", user.id)
-
render(conn, "show.json",
user: user,
for: user,
- with_pleroma_settings: true,
- with_chat_token: chat_token
+ with_pleroma_settings: true
)
end
:show_role,
:skip_thread_containment,
:allow_following_move,
- :also_known_as,
- :accepts_chat_messages
+ :also_known_as
]
|> Enum.reduce(%{}, fn key, acc ->
Maps.put_if_present(acc, key, params[key], &{:ok, Params.truthy_param?(&1)})
relationship: relationship,
skip_thread_containment: user.skip_thread_containment,
background_image: image_url(user.background) |> MediaProxy.url(),
- accepts_chat_messages: user.accepts_chat_messages,
favicon: favicon
}
}
|> maybe_put_settings(user, opts[:for], opts)
|> maybe_put_notification_settings(user, opts[:for])
|> 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_follow_requests_count(user, opts[:for])
|> maybe_put_allow_following_move(user, opts[:for])
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
data
|> Kernel.put_in([:pleroma, :is_admin], user.is_admin)
background_upload_limit: Keyword.get(instance, :background_upload_limit),
banner_upload_limit: Keyword.get(instance, :banner_upload_limit),
background_image: Pleroma.Web.Endpoint.url() <> Keyword.get(instance, :background_image),
- shout_limit: Config.get([:shout, :limit]),
description_limit: Keyword.get(instance, :description_limit),
pleroma: %{
metadata: %{
use Pleroma.Web, :view
alias Pleroma.Activity
- alias Pleroma.Chat.MessageReference
alias Pleroma.Notification
- alias Pleroma.Object
alias Pleroma.User
alias Pleroma.UserRelationship
alias Pleroma.Web.AdminAPI.Report
alias Pleroma.Web.MastodonAPI.AccountView
alias Pleroma.Web.MastodonAPI.NotificationView
alias Pleroma.Web.MastodonAPI.StatusView
- alias Pleroma.Web.PleromaAPI.Chat.MessageReferenceView
@parent_types ~w{Like Announce EmojiReact}
|> put_status(parent_activity_fn.(), reading_user, status_render_opts)
|> put_emoji(activity)
- "pleroma:chat_mention" ->
- put_chat_message(response, activity, reading_user, status_render_opts)
-
"pleroma:report" ->
put_report(response, activity)
|> Map.put(:emoji_url, MediaProxy.url(Pleroma.Emoji.emoji_url(activity.data)))
end
- defp put_chat_message(response, activity, reading_user, opts) do
- object = Object.normalize(activity, fetch: false)
- author = User.get_cached_by_ap_id(object.data["actor"])
- chat = Pleroma.Chat.get(reading_user.id, author.ap_id)
- cm_ref = MessageReference.for_chat_and_object(chat, object)
- render_opts = Map.merge(opts, %{for: reading_user, chat_message_reference: cm_ref})
- chat_message_render = MessageReferenceView.render("show.json", render_opts)
-
- Map.put(response, :chat_message, chat_message_render)
- end
-
defp put_status(response, activity, reading_user, opts) do
status_render_opts = Map.merge(opts, %{activity: activity, for: reading_user})
status_render = StatusView.render("show.json", status_render_opts)
+++ /dev/null
-# Pleroma: A lightweight social networking server
-# Copyright © 2017-2021 Pleroma Authors <https://pleroma.social/>
-# SPDX-License-Identifier: AGPL-3.0-only
-defmodule Pleroma.Web.PleromaAPI.ChatController do
- use Pleroma.Web, :controller
-
- import Pleroma.Web.ControllerHelper, only: [add_link_headers: 2]
-
- alias Pleroma.Activity
- alias Pleroma.Chat
- alias Pleroma.Chat.MessageReference
- alias Pleroma.Object
- alias Pleroma.Pagination
- alias Pleroma.Repo
- alias Pleroma.User
- alias Pleroma.Web.CommonAPI
- alias Pleroma.Web.PleromaAPI.Chat.MessageReferenceView
- alias Pleroma.Web.Plugs.OAuthScopesPlug
-
- import Ecto.Query
-
- action_fallback(Pleroma.Web.MastodonAPI.FallbackController)
-
- plug(
- OAuthScopesPlug,
- %{scopes: ["write:chats"]}
- when action in [
- :post_chat_message,
- :create,
- :mark_as_read,
- :mark_message_as_read,
- :delete_message
- ]
- )
-
- plug(
- OAuthScopesPlug,
- %{scopes: ["read:chats"]} when action in [:messages, :index, :index2, :show]
- )
-
- plug(Pleroma.Web.ApiSpec.CastAndValidate)
-
- defdelegate open_api_operation(action), to: Pleroma.Web.ApiSpec.ChatOperation
-
- def delete_message(%{assigns: %{user: %{id: user_id} = user}} = conn, %{
- message_id: message_id,
- id: chat_id
- }) do
- with %MessageReference{} = cm_ref <-
- MessageReference.get_by_id(message_id),
- ^chat_id <- to_string(cm_ref.chat_id),
- %Chat{user_id: ^user_id} <- Chat.get_by_id(chat_id),
- {:ok, _} <- remove_or_delete(cm_ref, user) do
- conn
- |> put_view(MessageReferenceView)
- |> render("show.json", chat_message_reference: cm_ref)
- else
- _e ->
- {:error, :could_not_delete}
- end
- end
-
- defp remove_or_delete(
- %{object: %{data: %{"actor" => actor, "id" => id}}},
- %{ap_id: actor} = user
- ) do
- with %Activity{} = activity <- Activity.get_create_by_object_ap_id(id) do
- CommonAPI.delete(activity.id, user)
- end
- end
-
- defp remove_or_delete(cm_ref, _), do: MessageReference.delete(cm_ref)
-
- def post_chat_message(
- %{body_params: params, assigns: %{user: user}} = conn,
- %{id: id}
- ) do
- with {:ok, chat} <- Chat.get_by_user_and_id(user, id),
- %User{} = recipient <- User.get_cached_by_ap_id(chat.recipient),
- {:ok, activity} <-
- CommonAPI.post_chat_message(user, recipient, params[:content],
- media_id: params[:media_id],
- idempotency_key: idempotency_key(conn)
- ),
- message <- Object.normalize(activity, fetch: false),
- cm_ref <- MessageReference.for_chat_and_object(chat, message) do
- conn
- |> put_view(MessageReferenceView)
- |> render("show.json", chat_message_reference: cm_ref)
- else
- {:reject, message} ->
- conn
- |> put_status(:unprocessable_entity)
- |> json(%{error: message})
-
- {:error, message} ->
- conn
- |> put_status(:bad_request)
- |> json(%{error: message})
- end
- end
-
- def mark_message_as_read(
- %{assigns: %{user: %{id: user_id}}} = conn,
- %{id: chat_id, message_id: message_id}
- ) do
- with %MessageReference{} = cm_ref <- MessageReference.get_by_id(message_id),
- ^chat_id <- to_string(cm_ref.chat_id),
- %Chat{user_id: ^user_id} <- Chat.get_by_id(chat_id),
- {:ok, cm_ref} <- MessageReference.mark_as_read(cm_ref) do
- conn
- |> put_view(MessageReferenceView)
- |> render("show.json", chat_message_reference: cm_ref)
- end
- end
-
- def mark_as_read(
- %{body_params: %{last_read_id: last_read_id}, assigns: %{user: user}} = conn,
- %{id: id}
- ) do
- with {:ok, chat} <- Chat.get_by_user_and_id(user, id),
- {_n, _} <- MessageReference.set_all_seen_for_chat(chat, last_read_id) do
- render(conn, "show.json", chat: chat)
- end
- end
-
- def messages(%{assigns: %{user: user}} = conn, %{id: id} = params) do
- with {:ok, chat} <- Chat.get_by_user_and_id(user, id) do
- chat_message_refs =
- chat
- |> MessageReference.for_chat_query()
- |> Pagination.fetch_paginated(params)
-
- conn
- |> add_link_headers(chat_message_refs)
- |> put_view(MessageReferenceView)
- |> render("index.json", chat_message_references: chat_message_refs)
- end
- end
-
- def index(%{assigns: %{user: user}} = conn, params) do
- chats =
- index_query(user, params)
- |> Repo.all()
-
- render(conn, "index.json", chats: chats)
- end
-
- def index2(%{assigns: %{user: user}} = conn, params) do
- chats =
- index_query(user, params)
- |> Pagination.fetch_paginated(params)
-
- conn
- |> add_link_headers(chats)
- |> render("index.json", chats: chats)
- end
-
- defp index_query(%{id: user_id} = user, params) do
- exclude_users =
- User.cached_blocked_users_ap_ids(user) ++
- if params[:with_muted], do: [], else: User.cached_muted_users_ap_ids(user)
-
- user_id
- |> Chat.for_user_query()
- |> where([c], c.recipient not in ^exclude_users)
- end
-
- def create(%{assigns: %{user: user}} = conn, %{id: id}) do
- with %User{ap_id: recipient} <- User.get_cached_by_id(id),
- {:ok, %Chat{} = chat} <- Chat.get_or_create(user.id, recipient) do
- render(conn, "show.json", chat: chat)
- end
- end
-
- def show(%{assigns: %{user: user}} = conn, %{id: id}) do
- with {:ok, chat} <- Chat.get_by_user_and_id(user, id) do
- render(conn, "show.json", chat: chat)
- end
- end
-
- defp idempotency_key(conn) do
- case get_req_header(conn, "idempotency-key") do
- [key] -> key
- _ -> nil
- end
- end
-end
+++ /dev/null
-# Pleroma: A lightweight social networking server
-# Copyright © 2017-2021 Pleroma Authors <https://pleroma.social/>
-# SPDX-License-Identifier: AGPL-3.0-only
-
-defmodule Pleroma.Web.PleromaAPI.Chat.MessageReferenceView do
- use Pleroma.Web, :view
-
- alias Pleroma.Maps
- alias Pleroma.User
- alias Pleroma.Web.CommonAPI.Utils
- alias Pleroma.Web.MastodonAPI.StatusView
-
- @cachex Pleroma.Config.get([:cachex, :provider], Cachex)
-
- def render(
- "show.json",
- %{
- chat_message_reference: %{
- id: id,
- object: %{data: chat_message} = object,
- chat_id: chat_id,
- unread: unread
- }
- }
- ) do
- %{
- id: id |> to_string(),
- content: chat_message["content"],
- chat_id: chat_id |> to_string(),
- account_id: User.get_cached_by_ap_id(chat_message["actor"]).id,
- created_at: Utils.to_masto_date(chat_message["published"]),
- emojis: StatusView.build_emojis(chat_message["emoji"]),
- attachment:
- chat_message["attachment"] &&
- StatusView.render("attachment.json", attachment: chat_message["attachment"]),
- unread: unread,
- card:
- StatusView.render(
- "card.json",
- Pleroma.Web.RichMedia.Helpers.fetch_data_for_object(object)
- )
- }
- |> put_idempotency_key()
- end
-
- def render("index.json", opts) do
- render_many(
- opts[:chat_message_references],
- __MODULE__,
- "show.json",
- Map.put(opts, :as, :chat_message_reference)
- )
- end
-
- defp put_idempotency_key(data) do
- with {:ok, idempotency_key} <- @cachex.get(:chat_message_id_idempotency_key_cache, data.id) do
- data
- |> Maps.put_if_present(:idempotency_key, idempotency_key)
- else
- _ -> data
- end
- end
-end
+++ /dev/null
-# Pleroma: A lightweight social networking server
-# Copyright © 2017-2021 Pleroma Authors <https://pleroma.social/>
-# SPDX-License-Identifier: AGPL-3.0-only
-
-defmodule Pleroma.Web.PleromaAPI.ChatView do
- use Pleroma.Web, :view
-
- alias Pleroma.Chat
- alias Pleroma.Chat.MessageReference
- alias Pleroma.User
- alias Pleroma.Web.CommonAPI.Utils
- alias Pleroma.Web.MastodonAPI.AccountView
- alias Pleroma.Web.PleromaAPI.Chat.MessageReferenceView
-
- def render("show.json", %{chat: %Chat{} = chat} = opts) do
- recipient = User.get_cached_by_ap_id(chat.recipient)
- last_message = opts[:last_message] || MessageReference.last_message_for_chat(chat)
- account_view_opts = account_view_opts(opts, recipient)
-
- %{
- id: chat.id |> to_string(),
- account: AccountView.render("show.json", account_view_opts),
- unread: MessageReference.unread_count_for_chat(chat),
- last_message:
- last_message &&
- MessageReferenceView.render("show.json", chat_message_reference: last_message),
- updated_at: Utils.to_masto_date(chat.updated_at)
- }
- end
-
- def render("index.json", %{chats: chats} = opts) do
- render_many(chats, __MODULE__, "show.json", Map.delete(opts, :chats))
- end
-
- defp account_view_opts(opts, recipient) do
- account_view_opts = Map.put(opts, :user, recipient)
-
- if Map.has_key?(account_view_opts, :for) do
- account_view_opts
- else
- Map.put(account_view_opts, :skip_visibility_check, true)
- end
- end
-end
def format_body(activity, actor, object, mastodon_type \\ nil)
- def format_body(_activity, actor, %{data: %{"type" => "ChatMessage"} = data}, _) do
- case data["content"] do
- nil -> "@#{actor.nickname}: (Attachment)"
- content -> "@#{actor.nickname}: #{Utils.scrub_html_and_truncate(content, 80)}"
- end
- end
-
def format_body(
%{activity: %{data: %{"type" => "Create"}}},
actor,
"follow_request" -> "New Follow Request"
"reblog" -> "New Repeat"
"favourite" -> "New Favorite"
- "pleroma:chat_mention" -> "New Chat Message"
"pleroma:emoji_reaction" -> "New Reaction"
type -> "New #{String.capitalize(type || "event")}"
end
end
# credo:disable-for-next-line Credo.Check.Readability.MaxLineLength
- @supported_alert_types ~w[follow favourite mention reblog poll pleroma:chat_mention pleroma:emoji_reaction]a
+ @supported_alert_types ~w[follow favourite mention reblog poll pleroma:emoji_reaction]a
defp alerts(%{data: %{alerts: alerts}}) do
alerts = Map.take(alerts, @supported_alert_types)
patch("/users/:nickname/credentials", AdminAPIController, :update_user_credentials)
get("/users/:nickname/statuses", AdminAPIController, :list_user_statuses)
- get("/users/:nickname/chats", AdminAPIController, :list_user_chats)
get("/statuses", StatusController, :index)
-
- get("/chats/:id", ChatController, :show)
- get("/chats/:id/messages", ChatController, :messages)
end
# AdminAPI: admins and mods (staff) can perform these actions
post("/reload_emoji", AdminAPIController, :reload_emoji)
get("/stats", AdminAPIController, :stats)
-
- delete("/chats/:id/messages/:message_id", ChatController, :delete_message)
end
scope "/api/v1/pleroma/emoji", Pleroma.Web.PleromaAPI do
scope [] do
pipe_through(:authenticated_api)
- post("/chats/by-account-id/:id", ChatController, :create)
- get("/chats/:id", ChatController, :show)
- get("/chats/:id/messages", ChatController, :messages)
- post("/chats/:id/messages", ChatController, :post_chat_message)
- delete("/chats/:id/messages/:message_id", ChatController, :delete_message)
- post("/chats/:id/read", ChatController, :mark_as_read)
- post("/chats/:id/messages/:message_id/read", ChatController, :mark_message_as_read)
-
get("/conversations/:id/statuses", ConversationController, :statuses)
get("/conversations/:id", ConversationController, :show)
post("/conversations/read", ConversationController, :mark_as_read)
get("/federation_status", InstancesController, :show)
end
- scope "/api/v2/pleroma", Pleroma.Web.PleromaAPI do
- scope [] do
- pipe_through(:authenticated_api)
- get("/chats", ChatController, :index2)
- end
- end
-
scope "/api/v1", Pleroma.Web.MastodonAPI do
pipe_through(:authenticated_api)
+++ /dev/null
-# Pleroma: A lightweight social networking server
-# Copyright © 2017-2021 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
require Logger
alias Pleroma.Activity
- alias Pleroma.Chat.MessageReference
alias Pleroma.Config
alias Pleroma.Conversation.Participation
alias Pleroma.Notification
def registry, do: @registry
@public_streams ["public", "public:local", "public:media", "public:local:media"]
- @user_streams ["user", "user:notification", "direct", "user:pleroma_chat"]
+ @user_streams ["user", "user:notification", "direct"]
@doc "Expands and authorizes a stream, and registers the process for streaming."
@spec get_topic_and_add_socket(
end)
end
- defp do_stream(topic, {user, %MessageReference{} = cm_ref})
- when topic in ["user", "user:pleroma_chat"] do
- topic = "#{topic}:#{user.id}"
-
- text = StreamerView.render("chat_update.json", %{chat_message_reference: cm_ref})
-
- Registry.dispatch(@registry, topic, fn list ->
- Enum.each(list, fn {pid, _auth} ->
- send(pid, {:text, text})
- end)
- end)
- end
-
defp do_stream("user", item) do
Logger.debug("Trying to push to users")
|> Jason.encode!()
end
- def render("chat_update.json", %{chat_message_reference: cm_ref}) do
- # Explicitly giving the cmr for the object here, so we don't accidentally
- # send a later 'last_message' that was inserted between inserting this and
- # streaming it out
- #
- # It also contains the chat with a cache of the correct unread count
- Logger.debug("Trying to stream out #{inspect(cm_ref)}")
-
- representation =
- Pleroma.Web.PleromaAPI.ChatView.render(
- "show.json",
- %{last_message: cm_ref, chat: cm_ref.chat}
- )
-
- %{
- event: "pleroma:chat_update",
- payload:
- representation
- |> Jason.encode!()
- }
- |> Jason.encode!()
- end
-
def render("follow_relationships_update.json", item) do
%{
event: "pleroma:follow_relationships_update",
],
"attachment" : [],
"capabilities" : {
- "acceptsChatMessages" : true
},
"discoverable" : false,
"endpoints" : {
notify_email: "noreply@example.com",
description: "A Pleroma instance, an alternative fediverse server",
limit: 5_000,
- chat_limit: 5_000,
remote_limit: 100_000,
upload_limit: 16_000_000,
avatar_upload_limit: 2_000_000,
{:ok, file} = File.read(temp_file)
assert file ==
- "import Config\n\nconfig :pleroma, :instance,\n name: \"Pleroma\",\n email: \"example@example.com\",\n notify_email: \"noreply@example.com\",\n description: \"A Pleroma instance, an alternative fediverse server\",\n limit: 5000,\n chat_limit: 5000,\n remote_limit: 100_000,\n upload_limit: 16_000_000,\n avatar_upload_limit: 2_000_000,\n background_upload_limit: 4_000_000,\n banner_upload_limit: 4_000_000,\n poll_limits: %{\n max_expiration: 31_536_000,\n max_option_chars: 200,\n max_options: 20,\n min_expiration: 0\n },\n registrations_open: true,\n federating: true,\n federation_incoming_replies_max_depth: 100,\n federation_reachability_timeout_days: 7,\n federation_publisher_modules: [Pleroma.Web.ActivityPub.Publisher],\n allow_relay: true,\n public: true,\n quarantined_instances: [],\n managed_config: true,\n static_dir: \"instance/static/\",\n allowed_post_formats: [\"text/plain\", \"text/html\", \"text/markdown\", \"text/bbcode\"],\n autofollowed_nicknames: [],\n max_pinned_statuses: 1,\n attachment_links: false,\n max_report_comment_size: 1000,\n safe_dm_mentions: false,\n healthcheck: false,\n remote_post_retention_days: 90,\n skip_thread_containment: true,\n limit_to_local_content: :unauthenticated,\n user_bio_length: 5000,\n user_name_length: 100,\n max_account_fields: 10,\n max_remote_account_fields: 20,\n account_field_name_length: 512,\n account_field_value_length: 2048,\n external_user_synchronization: true,\n extended_nickname_format: true,\n multi_factor_authentication: [\n totp: [digits: 6, period: 30],\n backup_codes: [number: 2, length: 6]\n ]\n"
+ "import Config\n\nconfig :pleroma, :instance,\n name: \"Pleroma\",\n email: \"example@example.com\",\n notify_email: \"noreply@example.com\",\n description: \"A Pleroma instance, an alternative fediverse server\",\n limit: 5000,\n remote_limit: 100_000,\n upload_limit: 16_000_000,\n avatar_upload_limit: 2_000_000,\n background_upload_limit: 4_000_000,\n banner_upload_limit: 4_000_000,\n poll_limits: %{\n max_expiration: 31_536_000,\n max_option_chars: 200,\n max_options: 20,\n min_expiration: 0\n },\n registrations_open: true,\n federating: true,\n federation_incoming_replies_max_depth: 100,\n federation_reachability_timeout_days: 7,\n federation_publisher_modules: [Pleroma.Web.ActivityPub.Publisher],\n allow_relay: true,\n public: true,\n quarantined_instances: [],\n managed_config: true,\n static_dir: \"instance/static/\",\n allowed_post_formats: [\"text/plain\", \"text/html\", \"text/markdown\", \"text/bbcode\"],\n autofollowed_nicknames: [],\n max_pinned_statuses: 1,\n attachment_links: false,\n max_report_comment_size: 1000,\n safe_dm_mentions: false,\n healthcheck: false,\n remote_post_retention_days: 90,\n skip_thread_containment: true,\n limit_to_local_content: :unauthenticated,\n user_bio_length: 5000,\n user_name_length: 100,\n max_account_fields: 10,\n max_remote_account_fields: 20,\n account_field_name_length: 512,\n account_field_value_length: 2048,\n external_user_synchronization: true,\n extended_nickname_format: true,\n multi_factor_authentication: [\n totp: [digits: 6, period: 30],\n backup_codes: [number: 2, length: 6]\n ]\n"
end
end
+++ /dev/null
-# Pleroma: A lightweight social networking server
-# Copyright © 2017-2021 Pleroma Authors <https://pleroma.social/>
-# SPDX-License-Identifier: AGPL-3.0-only
-
-defmodule Pleroma.Chat.MessageReferenceTest do
- use Pleroma.DataCase, async: true
-
- alias Pleroma.Chat
- alias Pleroma.Chat.MessageReference
- alias Pleroma.Web.CommonAPI
-
- import Pleroma.Factory
-
- describe "messages" do
- test "it returns the last message in a chat" do
- user = insert(:user)
- recipient = insert(:user)
-
- {:ok, _message_1} = CommonAPI.post_chat_message(user, recipient, "hey")
- {:ok, _message_2} = CommonAPI.post_chat_message(recipient, user, "ho")
-
- {:ok, chat} = Chat.get_or_create(user.id, recipient.ap_id)
-
- message = MessageReference.last_message_for_chat(chat)
-
- assert message.object.data["content"] == "ho"
- end
- end
-end
+++ /dev/null
-# Pleroma: A lightweight social networking server
-# Copyright © 2017-2021 Pleroma Authors <https://pleroma.social/>
-# SPDX-License-Identifier: AGPL-3.0-only
-
-defmodule Pleroma.ChatTest do
- use Pleroma.DataCase, async: true
-
- alias Pleroma.Chat
-
- import Pleroma.Factory
-
- describe "creation and getting" do
- test "it only works if the recipient is a valid user (for now)" do
- user = insert(:user)
-
- assert {:error, _chat} = Chat.bump_or_create(user.id, "http://some/nonexisting/account")
- assert {:error, _chat} = Chat.get_or_create(user.id, "http://some/nonexisting/account")
- end
-
- test "it creates a chat for a user and recipient" do
- user = insert(:user)
- other_user = insert(:user)
-
- {:ok, chat} = Chat.bump_or_create(user.id, other_user.ap_id)
-
- assert chat.id
- end
-
- test "deleting the user deletes the chat" do
- user = insert(:user)
- other_user = insert(:user)
-
- {:ok, chat} = Chat.bump_or_create(user.id, other_user.ap_id)
-
- Repo.delete(user)
-
- refute Chat.get_by_id(chat.id)
- end
-
- test "deleting the recipient deletes the chat" do
- user = insert(:user)
- other_user = insert(:user)
-
- {:ok, chat} = Chat.bump_or_create(user.id, other_user.ap_id)
-
- Repo.delete(other_user)
-
- refute Chat.get_by_id(chat.id)
- end
-
- test "it returns and bumps a chat for a user and recipient if it already exists" do
- user = insert(:user)
- other_user = insert(:user)
-
- {:ok, chat} = Chat.bump_or_create(user.id, other_user.ap_id)
- {:ok, chat_two} = Chat.bump_or_create(user.id, other_user.ap_id)
-
- assert chat.id == chat_two.id
- end
-
- test "it returns a chat for a user and recipient if it already exists" do
- user = insert(:user)
- other_user = insert(:user)
-
- {:ok, chat} = Chat.get_or_create(user.id, other_user.ap_id)
- {:ok, chat_two} = Chat.get_or_create(user.id, other_user.ap_id)
-
- assert chat.id == chat_two.id
- end
-
- test "a returning chat will have an updated `update_at` field" do
- user = insert(:user)
- other_user = insert(:user)
-
- {:ok, chat} = Chat.bump_or_create(user.id, other_user.ap_id)
- {:ok, chat} = time_travel(chat, -2)
-
- {:ok, chat_two} = Chat.bump_or_create(user.id, other_user.ap_id)
-
- assert chat.id == chat_two.id
- assert chat.updated_at != chat_two.updated_at
- end
- end
-end
end) =~
"Your config is using the old setting for controlling the URL of media uploaded to your S3 bucket."
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
@tag :erratic
test "on reboot time key" do
- clear_config(:shout)
- insert(:config, key: :shout, value: [enabled: false])
+ clear_config([:pleroma, :rate_limit])
+ insert(:config, key: {:pleroma, :rate_limit}, value: [enabled: false])
assert capture_log(fn -> TransferTask.start_link([]) end) =~ "pleroma restarted"
end
@tag :erratic
test "don't restart pleroma on reboot time key and subkey if there is false flag" do
- clear_config(:shout)
+ clear_config([:pleroma, :rate_limit])
clear_config(Pleroma.Captcha)
- insert(:config, key: :shout, value: [enabled: false])
+ insert(:config, key: {:pleroma, :rate_limit}, value: [enabled: false])
insert(:config, key: Pleroma.Captcha, value: [seconds_valid: 60])
refute String.contains?(
other_user = insert(:user)
{:ok, post} = CommonAPI.post(user, %{status: "yeah, @#{other_user.nickname}"})
- {:ok, chat} = CommonAPI.post_chat_message(user, other_user, "yo")
{:ok, react} = CommonAPI.react_with_emoji(post.id, other_user, "☕")
{:ok, like} = CommonAPI.favorite(other_user, post.id)
{:ok, react_2} = CommonAPI.react_with_emoji(post.id, other_user, "☕")
|> Activity.change(%{data: data})
|> Repo.update()
- assert {5, nil} = Repo.update_all(Notification, set: [type: nil])
+ assert {4, nil} = Repo.update_all(Notification, set: [type: nil])
NotificationBackfill.fill_in_notification_types()
assert %{type: "pleroma:emoji_reaction"} =
Repo.get_by(Notification, user_id: user.id, activity_id: react_2.id)
-
- assert %{type: "pleroma:chat_mention"} =
- Repo.get_by(Notification, user_id: other_user.id, activity_id: chat.id)
end
end
end
+++ /dev/null
-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
+++ /dev/null
-# Pleroma: A lightweight social networking server
-# Copyright © 2017-2021 Pleroma Authors <https://pleroma.social/>
-# SPDX-License-Identifier: AGPL-3.0-only
-
-defmodule Pleroma.User.WelcomeChatMessageTest do
- use Pleroma.DataCase
-
- alias Pleroma.User.WelcomeChatMessage
-
- import Pleroma.Factory
-
- setup do: clear_config([:welcome])
-
- describe "post_message/1" do
- test "send a chat welcome message" do
- welcome_user = insert(:user, name: "mewmew")
- user = insert(:user)
-
- clear_config([:welcome, :chat_message, :enabled], true)
- clear_config([:welcome, :chat_message, :sender_nickname], welcome_user.nickname)
-
- clear_config(
- [:welcome, :chat_message, :message],
- "Hello, welcome to Blob/Cat!"
- )
-
- {:ok, %Pleroma.Activity{} = activity} = WelcomeChatMessage.post_message(user)
-
- assert user.ap_id in activity.recipients
- assert Pleroma.Object.normalize(activity, fetch: false).data["type"] == "ChatMessage"
-
- assert Pleroma.Object.normalize(activity, fetch: false).data["content"] ==
- "Hello, welcome to Blob/Cat!"
- end
- end
-end
assert activity.actor == welcome_user.ap_id
end
- test "it sends a welcome chat message if it is set" do
- welcome_user = insert(:user)
- clear_config([:welcome, :chat_message, :enabled], true)
- clear_config([:welcome, :chat_message, :sender_nickname], welcome_user.nickname)
- clear_config([:welcome, :chat_message, :message], "Hello, this is a chat message")
-
- cng = User.register_changeset(%User{}, @full_user_data)
- {:ok, registered_user} = User.register(cng)
- ObanHelpers.perform_all()
-
- activity = Repo.one(Pleroma.Activity)
- assert registered_user.ap_id in activity.recipients
- assert Object.normalize(activity, fetch: false).data["content"] =~ "chat message"
- assert activity.actor == welcome_user.ap_id
- end
-
setup do:
clear_config(:mrf_simple,
media_removal: [],
]
)
- test "it sends a welcome chat message when Simple policy applied to local instance" do
- clear_config([:mrf_simple, :media_nsfw], [{"localhost", ""}])
-
- welcome_user = insert(:user)
- clear_config([:welcome, :chat_message, :enabled], true)
- clear_config([:welcome, :chat_message, :sender_nickname], welcome_user.nickname)
- clear_config([:welcome, :chat_message, :message], "Hello, this is a chat message")
-
- cng = User.register_changeset(%User{}, @full_user_data)
- {:ok, registered_user} = User.register(cng)
- ObanHelpers.perform_all()
-
- activity = Repo.one(Pleroma.Activity)
- assert registered_user.ap_id in activity.recipients
- assert Object.normalize(activity, fetch: false).data["content"] =~ "chat message"
- assert activity.actor == welcome_user.ap_id
- end
-
test "it sends a welcome email message if it is set" do
welcome_user = insert(:user)
clear_config([:welcome, :email, :enabled], true)
assert changeset.changes.follower_address == "#{changeset.changes.ap_id}/followers"
end
- test "it sets the 'accepts_chat_messages' set to true" do
- changeset = User.register_changeset(%User{}, @full_user_data)
- assert changeset.valid?
-
- {:ok, user} = Repo.insert(changeset)
-
- assert user.accepts_chat_messages
- end
-
test "it creates a confirmed user" do
changeset = User.register_changeset(%User{}, @full_user_data)
assert changeset.valid?
assert User.invisible?(user)
end
- test "it returns a user that accepts chat messages" do
- user_id = "http://mastodon.example.org/users/admin"
- {:ok, user} = ActivityPub.make_user_from_ap_id(user_id)
-
- assert user.accepts_chat_messages
- end
-
test "works for guppe actors" do
user_id = "https://gup.pe/u/bernie2020"
import Pleroma.Web.ActivityPub.MRF.HellthreadPolicy
- alias Pleroma.Web.CommonAPI
-
setup do
user = insert(:user)
setup do: clear_config(:mrf_hellthread)
- test "doesn't die on chat messages" do
- clear_config([:mrf_hellthread], %{delist_threshold: 2, reject_threshold: 0})
-
- user = insert(:user)
- other_user = insert(:user)
-
- {:ok, activity} = CommonAPI.post_chat_message(user, other_user, "moin")
-
- assert {:ok, _} = filter(activity.data)
- end
-
describe "reject" do
test "rejects the message if the recipient count is above reject_threshold", %{
message: message
+++ /dev/null
-# Pleroma: A lightweight social networking server
-# Copyright © 2017-2021 Pleroma Authors <https://pleroma.social/>
-# SPDX-License-Identifier: AGPL-3.0-only
-
-defmodule Pleroma.Web.ActivityPub.ObjectValidators.ChatValidationTest do
- use Pleroma.DataCase
- alias Pleroma.Object
- alias Pleroma.Web.ActivityPub.ActivityPub
- alias Pleroma.Web.ActivityPub.Builder
- alias Pleroma.Web.ActivityPub.ObjectValidator
- alias Pleroma.Web.CommonAPI
-
- import Pleroma.Factory
-
- describe "chat message create activities" do
- test "it is invalid if the object already exists" do
- user = insert(:user)
- recipient = insert(:user)
- {:ok, activity} = CommonAPI.post_chat_message(user, recipient, "hey")
- object = Object.normalize(activity, fetch: false)
-
- {:ok, create_data, _} = Builder.create(user, object.data, [recipient.ap_id])
-
- {:error, cng} = ObjectValidator.validate(create_data, [])
-
- assert {:object, {"The object to create already exists", []}} in cng.errors
- end
-
- test "it is invalid if the object data has a different `to` or `actor` field" do
- user = insert(:user)
- recipient = insert(:user)
- {:ok, object_data, _} = Builder.chat_message(recipient, user.ap_id, "Hey")
-
- {:ok, create_data, _} = Builder.create(user, object_data, [recipient.ap_id])
-
- {:error, cng} = ObjectValidator.validate(create_data, [])
-
- assert {:to, {"Recipients don't match with object recipients", []}} in cng.errors
- assert {:actor, {"Actor doesn't match with object actor", []}} in cng.errors
- end
- end
-
- describe "chat messages" do
- setup do
- clear_config([:instance, :remote_limit])
- user = insert(:user)
- recipient = insert(:user, local: false)
-
- {:ok, valid_chat_message, _} = Builder.chat_message(user, recipient.ap_id, "hey :firefox:")
-
- %{user: user, recipient: recipient, valid_chat_message: valid_chat_message}
- end
-
- test "let's through some basic html", %{user: user, recipient: recipient} do
- {:ok, valid_chat_message, _} =
- Builder.chat_message(
- user,
- recipient.ap_id,
- "hey <a href='https://example.org'>example</a> <script>alert('uguu')</script>"
- )
-
- assert {:ok, object, _meta} = ObjectValidator.validate(valid_chat_message, [])
-
- assert object["content"] ==
- "hey <a href=\"https://example.org\">example</a> alert('uguu')"
- end
-
- test "validates for a basic object we build", %{valid_chat_message: valid_chat_message} do
- assert {:ok, object, _meta} = ObjectValidator.validate(valid_chat_message, [])
-
- assert valid_chat_message == object
- assert match?(%{"firefox" => _}, object["emoji"])
- end
-
- test "validates for a basic object with an attachment", %{
- valid_chat_message: valid_chat_message,
- user: user
- } do
- file = %Plug.Upload{
- content_type: "image/jpeg",
- path: Path.absname("test/fixtures/image.jpg"),
- filename: "an_image.jpg"
- }
-
- {:ok, attachment} = ActivityPub.upload(file, actor: user.ap_id)
-
- valid_chat_message =
- valid_chat_message
- |> Map.put("attachment", attachment.data)
-
- assert {:ok, object, _meta} = ObjectValidator.validate(valid_chat_message, [])
-
- assert object["attachment"]
- end
-
- test "validates for a basic object with an attachment in an array", %{
- valid_chat_message: valid_chat_message,
- user: user
- } do
- file = %Plug.Upload{
- content_type: "image/jpeg",
- path: Path.absname("test/fixtures/image.jpg"),
- filename: "an_image.jpg"
- }
-
- {:ok, attachment} = ActivityPub.upload(file, actor: user.ap_id)
-
- valid_chat_message =
- valid_chat_message
- |> Map.put("attachment", [attachment.data])
-
- assert {:ok, object, _meta} = ObjectValidator.validate(valid_chat_message, [])
-
- assert object["attachment"]
- end
-
- test "validates for a basic object with an attachment but without content", %{
- valid_chat_message: valid_chat_message,
- user: user
- } do
- file = %Plug.Upload{
- content_type: "image/jpeg",
- path: Path.absname("test/fixtures/image.jpg"),
- filename: "an_image.jpg"
- }
-
- {:ok, attachment} = ActivityPub.upload(file, actor: user.ap_id)
-
- valid_chat_message =
- valid_chat_message
- |> Map.put("attachment", attachment.data)
- |> Map.delete("content")
-
- assert {:ok, object, _meta} = ObjectValidator.validate(valid_chat_message, [])
-
- assert object["attachment"]
- end
-
- test "does not validate if the message has no content", %{
- valid_chat_message: valid_chat_message
- } do
- contentless =
- valid_chat_message
- |> Map.delete("content")
-
- refute match?({:ok, _object, _meta}, ObjectValidator.validate(contentless, []))
- end
-
- test "does not validate if the message is longer than the remote_limit", %{
- valid_chat_message: valid_chat_message
- } do
- clear_config([:instance, :remote_limit], 2)
- refute match?({:ok, _object, _meta}, ObjectValidator.validate(valid_chat_message, []))
- end
-
- test "does not validate if the recipient is blocking the actor", %{
- valid_chat_message: valid_chat_message,
- user: user,
- recipient: recipient
- } do
- Pleroma.User.block(recipient, user)
- refute match?({:ok, _object, _meta}, ObjectValidator.validate(valid_chat_message, []))
- end
-
- test "does not validate if the recipient is not accepting chat messages", %{
- valid_chat_message: valid_chat_message,
- recipient: recipient
- } do
- recipient
- |> Ecto.Changeset.change(%{accepts_chat_messages: false})
- |> Pleroma.Repo.update!()
-
- refute match?({:ok, _object, _meta}, ObjectValidator.validate(valid_chat_message, []))
- end
-
- test "does not validate if the actor or the recipient is not in our system", %{
- valid_chat_message: valid_chat_message
- } do
- chat_message =
- valid_chat_message
- |> Map.put("actor", "https://raymoo.com/raymoo")
-
- {:error, _} = ObjectValidator.validate(chat_message, [])
-
- chat_message =
- valid_chat_message
- |> Map.put("to", ["https://raymoo.com/raymoo"])
-
- {:error, _} = ObjectValidator.validate(chat_message, [])
- end
-
- test "does not validate for a message with multiple recipients", %{
- valid_chat_message: valid_chat_message,
- user: user,
- recipient: recipient
- } do
- chat_message =
- valid_chat_message
- |> Map.put("to", [user.ap_id, recipient.ap_id])
-
- assert {:error, _} = ObjectValidator.validate(chat_message, [])
- end
-
- test "does not validate if it doesn't concern local users" do
- user = insert(:user, local: false)
- recipient = insert(:user, local: false)
-
- {:ok, valid_chat_message, _} = Builder.chat_message(user, recipient.ap_id, "hey")
- assert {:error, _} = ObjectValidator.validate(valid_chat_message, [])
- end
- end
-end
use Pleroma.DataCase
alias Pleroma.Activity
- alias Pleroma.Chat
- alias Pleroma.Chat.MessageReference
alias Pleroma.Notification
alias Pleroma.Object
alias Pleroma.Repo
alias Pleroma.Web.ActivityPub.ActivityPub
alias Pleroma.Web.ActivityPub.Builder
alias Pleroma.Web.ActivityPub.SideEffects
+ alias Pleroma.Web.ActivityPub.Utils
alias Pleroma.Web.CommonAPI
import Mock
author = insert(:user, local: true)
recipient = insert(:user, local: true)
- {:ok, chat_message_data, _meta} = Builder.chat_message(author, recipient.ap_id, "hey")
+ {:ok, note_data, _meta} =
+ Builder.note(%Pleroma.Web.CommonAPI.ActivityDraft{
+ user: author,
+ to: [recipient.ap_id],
+ mentions: [recipient],
+ content_html: "hey",
+ extra: %{"id" => Utils.generate_object_id()}
+ })
{:ok, create_activity_data, _meta} =
- Builder.create(author, chat_message_data["id"], [recipient.ap_id])
+ Builder.create(author, note_data["id"], [recipient.ap_id])
{:ok, create_activity, _meta} = ActivityPub.persist(create_activity_data, local: false)
{:ok, _create_activity, meta} =
- SideEffects.handle(create_activity, local: false, object_data: chat_message_data)
+ SideEffects.handle(create_activity, local: false, object_data: note_data)
assert [notification] = meta[:notifications]
SideEffects.handle_after_transaction(meta)
assert called(Pleroma.Web.Streamer.stream(["user", "user:notification"], notification))
- assert called(Pleroma.Web.Streamer.stream(["user", "user:pleroma_chat"], :_))
assert called(Pleroma.Web.Push.send(notification))
end
end
end
end
- describe "creation of ChatMessages" do
- test "notifies the recipient" do
- author = insert(:user, local: false)
- recipient = insert(:user, local: true)
-
- {:ok, chat_message_data, _meta} = Builder.chat_message(author, recipient.ap_id, "hey")
-
- {:ok, create_activity_data, _meta} =
- Builder.create(author, chat_message_data["id"], [recipient.ap_id])
-
- {:ok, create_activity, _meta} = ActivityPub.persist(create_activity_data, local: false)
-
- {:ok, _create_activity, _meta} =
- SideEffects.handle(create_activity, local: false, object_data: chat_message_data)
-
- assert Repo.get_by(Notification, user_id: recipient.id, activity_id: create_activity.id)
- end
-
- test "it streams the created ChatMessage" do
- author = insert(:user, local: true)
- recipient = insert(:user, local: true)
-
- {:ok, chat_message_data, _meta} = Builder.chat_message(author, recipient.ap_id, "hey")
-
- {:ok, create_activity_data, _meta} =
- Builder.create(author, chat_message_data["id"], [recipient.ap_id])
-
- {:ok, create_activity, _meta} = ActivityPub.persist(create_activity_data, local: false)
-
- {:ok, _create_activity, meta} =
- SideEffects.handle(create_activity, local: false, object_data: chat_message_data)
-
- assert [_, _] = meta[:streamables]
- end
-
- test "it creates a Chat and MessageReferences for the local users and bumps the unread count, except for the author" do
- author = insert(:user, local: true)
- recipient = insert(:user, local: true)
-
- {:ok, chat_message_data, _meta} = Builder.chat_message(author, recipient.ap_id, "hey")
-
- {:ok, create_activity_data, _meta} =
- Builder.create(author, chat_message_data["id"], [recipient.ap_id])
-
- {:ok, create_activity, _meta} = ActivityPub.persist(create_activity_data, local: false)
-
- with_mocks([
- {
- Pleroma.Web.Streamer,
- [],
- [
- stream: fn _, _ -> nil end
- ]
- },
- {
- Pleroma.Web.Push,
- [],
- [
- send: fn _ -> nil end
- ]
- }
- ]) do
- {:ok, _create_activity, meta} =
- SideEffects.handle(create_activity, local: false, object_data: chat_message_data)
-
- # The notification gets created
- assert [notification] = meta[:notifications]
- assert notification.activity_id == create_activity.id
-
- # But it is not sent out
- refute called(Pleroma.Web.Streamer.stream(["user", "user:notification"], notification))
- refute called(Pleroma.Web.Push.send(notification))
-
- # Same for the user chat stream
- assert [{topics, _}, _] = meta[:streamables]
- assert topics == ["user", "user:pleroma_chat"]
- refute called(Pleroma.Web.Streamer.stream(["user", "user:pleroma_chat"], :_))
-
- chat = Chat.get(author.id, recipient.ap_id)
-
- [cm_ref] = MessageReference.for_chat_query(chat) |> Repo.all()
-
- assert cm_ref.object.data["content"] == "hey"
- assert cm_ref.unread == false
-
- chat = Chat.get(recipient.id, author.ap_id)
-
- [cm_ref] = MessageReference.for_chat_query(chat) |> Repo.all()
-
- assert cm_ref.object.data["content"] == "hey"
- assert cm_ref.unread == true
- end
- end
-
- test "it creates a Chat for the local users and bumps the unread count" do
- author = insert(:user, local: false)
- recipient = insert(:user, local: true)
-
- {:ok, chat_message_data, _meta} = Builder.chat_message(author, recipient.ap_id, "hey")
-
- {:ok, create_activity_data, _meta} =
- Builder.create(author, chat_message_data["id"], [recipient.ap_id])
-
- {:ok, create_activity, _meta} = ActivityPub.persist(create_activity_data, local: false)
-
- {:ok, _create_activity, _meta} =
- SideEffects.handle(create_activity, local: false, object_data: chat_message_data)
-
- # An object is created
- assert Object.get_by_ap_id(chat_message_data["id"])
-
- # The remote user won't get a chat
- chat = Chat.get(author.id, recipient.ap_id)
- refute chat
-
- # The local user will get a chat
- chat = Chat.get(recipient.id, author.ap_id)
- assert chat
-
- author = insert(:user, local: true)
- recipient = insert(:user, local: true)
-
- {:ok, chat_message_data, _meta} = Builder.chat_message(author, recipient.ap_id, "hey")
-
- {:ok, create_activity_data, _meta} =
- Builder.create(author, chat_message_data["id"], [recipient.ap_id])
-
- {:ok, create_activity, _meta} = ActivityPub.persist(create_activity_data, local: false)
-
- {:ok, _create_activity, _meta} =
- SideEffects.handle(create_activity, local: false, object_data: chat_message_data)
-
- # Both users are local and get the chat
- chat = Chat.get(author.id, recipient.ap_id)
- assert chat
-
- chat = Chat.get(recipient.id, author.ap_id)
- assert chat
- end
- end
-
describe "announce objects" do
setup do
poster = insert(:user)
+++ /dev/null
-# Pleroma: A lightweight social networking server
-# Copyright © 2017-2021 Pleroma Authors <https://pleroma.social/>
-# SPDX-License-Identifier: AGPL-3.0-only
-
-defmodule Pleroma.Web.ActivityPub.Transmogrifier.ChatMessageTest do
- use Pleroma.DataCase
-
- import Pleroma.Factory
-
- alias Pleroma.Activity
- alias Pleroma.Chat
- alias Pleroma.Object
- alias Pleroma.Web.ActivityPub.Transmogrifier
-
- describe "handle_incoming" do
- test "handles chonks with attachment" do
- data = %{
- "@context" => "https://www.w3.org/ns/activitystreams",
- "actor" => "https://honk.tedunangst.com/u/tedu",
- "id" => "https://honk.tedunangst.com/u/tedu/honk/x6gt8X8PcyGkQcXxzg1T",
- "object" => %{
- "attachment" => [
- %{
- "mediaType" => "image/jpeg",
- "name" => "298p3RG7j27tfsZ9RQ.jpg",
- "summary" => "298p3RG7j27tfsZ9RQ.jpg",
- "type" => "Document",
- "url" => "https://honk.tedunangst.com/d/298p3RG7j27tfsZ9RQ.jpg"
- }
- ],
- "attributedTo" => "https://honk.tedunangst.com/u/tedu",
- "content" => "",
- "id" => "https://honk.tedunangst.com/u/tedu/chonk/26L4wl5yCbn4dr4y1b",
- "published" => "2020-05-18T01:13:03Z",
- "to" => [
- "https://dontbulling.me/users/lain"
- ],
- "type" => "ChatMessage"
- },
- "published" => "2020-05-18T01:13:03Z",
- "to" => [
- "https://dontbulling.me/users/lain"
- ],
- "type" => "Create"
- }
-
- _user = insert(:user, ap_id: data["actor"])
- _user = insert(:user, ap_id: hd(data["to"]))
-
- assert {:ok, _activity} = Transmogrifier.handle_incoming(data)
- end
-
- test "it rejects messages that don't contain content" do
- data =
- File.read!("test/fixtures/create-chat-message.json")
- |> Jason.decode!()
-
- object =
- data["object"]
- |> Map.delete("content")
-
- data =
- data
- |> Map.put("object", object)
-
- _author =
- insert(:user, ap_id: data["actor"], local: false, last_refreshed_at: DateTime.utc_now())
-
- _recipient =
- insert(:user,
- ap_id: List.first(data["to"]),
- local: true,
- last_refreshed_at: DateTime.utc_now()
- )
-
- {:error, _} = Transmogrifier.handle_incoming(data)
- end
-
- test "it rejects messages that don't concern local users" do
- data =
- File.read!("test/fixtures/create-chat-message.json")
- |> Jason.decode!()
-
- _author =
- insert(:user, ap_id: data["actor"], local: false, last_refreshed_at: DateTime.utc_now())
-
- _recipient =
- insert(:user,
- ap_id: List.first(data["to"]),
- local: false,
- last_refreshed_at: DateTime.utc_now()
- )
-
- {:error, _} = Transmogrifier.handle_incoming(data)
- end
-
- test "it rejects messages where the `to` field of activity and object don't match" do
- data =
- File.read!("test/fixtures/create-chat-message.json")
- |> Jason.decode!()
-
- author = insert(:user, ap_id: data["actor"])
- _recipient = insert(:user, ap_id: List.first(data["to"]))
-
- data =
- data
- |> Map.put("to", author.ap_id)
-
- assert match?({:error, _}, Transmogrifier.handle_incoming(data))
- refute Object.get_by_ap_id(data["object"]["id"])
- end
-
- test "it fetches the actor if they aren't in our system" do
- Tesla.Mock.mock(fn env -> apply(HttpRequestMock, :request, [env]) end)
-
- data =
- File.read!("test/fixtures/create-chat-message.json")
- |> Jason.decode!()
- |> Map.put("actor", "http://mastodon.example.org/users/admin")
- |> put_in(["object", "actor"], "http://mastodon.example.org/users/admin")
-
- _recipient = insert(:user, ap_id: List.first(data["to"]), local: true)
-
- {:ok, %Activity{} = _activity} = Transmogrifier.handle_incoming(data)
- end
-
- test "it doesn't work for deactivated users" do
- data =
- File.read!("test/fixtures/create-chat-message.json")
- |> Jason.decode!()
-
- _author =
- insert(:user,
- ap_id: data["actor"],
- local: false,
- last_refreshed_at: DateTime.utc_now(),
- is_active: false
- )
-
- _recipient = insert(:user, ap_id: List.first(data["to"]), local: true)
-
- assert {:error, _} = Transmogrifier.handle_incoming(data)
- end
-
- test "it inserts it and creates a chat" do
- data =
- File.read!("test/fixtures/create-chat-message.json")
- |> Jason.decode!()
-
- author =
- insert(:user, ap_id: data["actor"], local: false, last_refreshed_at: DateTime.utc_now())
-
- recipient = insert(:user, ap_id: List.first(data["to"]), local: true)
-
- {:ok, %Activity{} = activity} = Transmogrifier.handle_incoming(data)
- assert activity.local == false
-
- assert activity.actor == author.ap_id
- assert activity.recipients == [recipient.ap_id, author.ap_id]
-
- %Object{} = object = Object.get_by_ap_id(activity.data["object"])
-
- assert object
- assert object.data["content"] == "You expected a cute girl? Too bad. alert('XSS')"
- assert match?(%{"firefox" => _}, object.data["emoji"])
-
- refute Chat.get(author.id, recipient.ap_id)
- assert Chat.get(recipient.id, author.ap_id)
- end
- end
-end
assert %{"totalItems" => 1} = UserView.render("following.json", %{user: user})
end
end
-
- describe "acceptsChatMessages" do
- test "it returns this value if it is set" do
- true_user = insert(:user, accepts_chat_messages: true)
- false_user = insert(:user, accepts_chat_messages: false)
- nil_user = insert(:user, accepts_chat_messages: nil)
-
- assert %{"capabilities" => %{"acceptsChatMessages" => true}} =
- UserView.render("user.json", user: true_user)
-
- assert %{"capabilities" => %{"acceptsChatMessages" => false}} =
- UserView.render("user.json", user: false_user)
-
- refute Map.has_key?(
- UserView.render("user.json", user: nil_user)["capabilities"],
- "acceptsChatMessages"
- )
- end
- end
end
end
end
- describe "GET /api/pleroma/admin/users/:nickname/chats" do
- setup do
- user = insert(:user)
- recipients = insert_list(3, :user)
-
- Enum.each(recipients, fn recipient ->
- CommonAPI.post_chat_message(user, recipient, "yo")
- end)
-
- %{user: user}
- end
-
- test "renders user's chats", %{conn: conn, user: user} do
- conn = get(conn, "/api/pleroma/admin/users/#{user.nickname}/chats")
-
- assert json_response(conn, 200) |> length() == 3
- end
- end
-
- describe "GET /api/pleroma/admin/users/:nickname/chats unauthorized" do
- setup do
- user = insert(:user)
- recipient = insert(:user)
- CommonAPI.post_chat_message(user, recipient, "yo")
- %{conn: conn} = oauth_access(["read:chats"])
- %{conn: conn, user: user}
- end
-
- test "returns 403", %{conn: conn, user: user} do
- conn
- |> get("/api/pleroma/admin/users/#{user.nickname}/chats")
- |> json_response(403)
- end
- end
-
- describe "GET /api/pleroma/admin/users/:nickname/chats unauthenticated" do
- setup do
- user = insert(:user)
- recipient = insert(:user)
- CommonAPI.post_chat_message(user, recipient, "yo")
- %{conn: build_conn(), user: user}
- end
-
- test "returns 403", %{conn: conn, user: user} do
- conn
- |> get("/api/pleroma/admin/users/#{user.nickname}/chats")
- |> json_response(403)
- end
- end
-
describe "GET /api/pleroma/admin/moderation_log" do
setup do
moderator = insert(:user, is_moderator: true)
+++ /dev/null
-# Pleroma: A lightweight social networking server
-# Copyright © 2017-2021 Pleroma Authors <https://pleroma.social/>
-# SPDX-License-Identifier: AGPL-3.0-only
-
-defmodule Pleroma.Web.AdminAPI.ChatControllerTest do
- use Pleroma.Web.ConnCase, async: true
-
- import Pleroma.Factory
-
- alias Pleroma.Chat
- alias Pleroma.Chat.MessageReference
- alias Pleroma.ModerationLog
- alias Pleroma.Object
- alias Pleroma.Repo
- alias Pleroma.Web.CommonAPI
-
- defp admin_setup do
- admin = insert(:user, is_admin: true)
- token = insert(:oauth_admin_token, user: admin)
-
- conn =
- build_conn()
- |> assign(:user, admin)
- |> assign(:token, token)
-
- {:ok, %{admin: admin, token: token, conn: conn}}
- end
-
- describe "DELETE /api/pleroma/admin/chats/:id/messages/:message_id" do
- setup do: admin_setup()
-
- test "it deletes a message from the chat", %{conn: conn, admin: admin} do
- user = insert(:user)
- recipient = insert(:user)
-
- {:ok, message} =
- CommonAPI.post_chat_message(user, recipient, "Hello darkness my old friend")
-
- object = Object.normalize(message, fetch: false)
-
- chat = Chat.get(user.id, recipient.ap_id)
- recipient_chat = Chat.get(recipient.id, user.ap_id)
-
- cm_ref = MessageReference.for_chat_and_object(chat, object)
- recipient_cm_ref = MessageReference.for_chat_and_object(recipient_chat, object)
-
- result =
- conn
- |> put_req_header("content-type", "application/json")
- |> delete("/api/pleroma/admin/chats/#{chat.id}/messages/#{cm_ref.id}")
- |> json_response_and_validate_schema(200)
-
- log_entry = Repo.one(ModerationLog)
-
- assert ModerationLog.get_log_entry_message(log_entry) ==
- "@#{admin.nickname} deleted chat message ##{cm_ref.id}"
-
- assert result["id"] == cm_ref.id
- refute MessageReference.get_by_id(cm_ref.id)
- refute MessageReference.get_by_id(recipient_cm_ref.id)
- assert %{data: %{"type" => "Tombstone"}} = Object.get_by_id(object.id)
- end
- end
-
- describe "GET /api/pleroma/admin/chats/:id/messages" do
- setup do: admin_setup()
-
- test "it paginates", %{conn: conn} do
- user = insert(:user)
- recipient = insert(:user)
-
- Enum.each(1..30, fn _ ->
- {:ok, _} = CommonAPI.post_chat_message(user, recipient, "hey")
- end)
-
- chat = Chat.get(user.id, recipient.ap_id)
-
- result =
- conn
- |> get("/api/pleroma/admin/chats/#{chat.id}/messages")
- |> json_response_and_validate_schema(200)
-
- assert length(result) == 20
-
- result =
- conn
- |> get("/api/pleroma/admin/chats/#{chat.id}/messages?max_id=#{List.last(result)["id"]}")
- |> json_response_and_validate_schema(200)
-
- assert length(result) == 10
- end
-
- test "it returns the messages for a given chat", %{conn: conn} do
- user = insert(:user)
- other_user = insert(:user)
- third_user = insert(:user)
-
- {:ok, _} = CommonAPI.post_chat_message(user, other_user, "hey")
- {:ok, _} = CommonAPI.post_chat_message(user, third_user, "hey")
- {:ok, _} = CommonAPI.post_chat_message(user, other_user, "how are you?")
- {:ok, _} = CommonAPI.post_chat_message(other_user, user, "fine, how about you?")
-
- chat = Chat.get(user.id, other_user.ap_id)
-
- result =
- conn
- |> get("/api/pleroma/admin/chats/#{chat.id}/messages")
- |> json_response_and_validate_schema(200)
-
- result
- |> Enum.each(fn message ->
- assert message["chat_id"] == chat.id |> to_string()
- end)
-
- assert length(result) == 3
- end
- end
-
- describe "GET /api/pleroma/admin/chats/:id" do
- setup do: admin_setup()
-
- test "it returns a chat", %{conn: conn} do
- user = insert(:user)
- other_user = insert(:user)
-
- {:ok, chat} = Chat.get_or_create(user.id, other_user.ap_id)
-
- result =
- conn
- |> get("/api/pleroma/admin/chats/#{chat.id}")
- |> json_response_and_validate_schema(200)
-
- assert result["id"] == to_string(chat.id)
- assert %{} = result["sender"]
- assert %{} = result["receiver"]
- refute result["account"]
- end
- end
-
- describe "unauthorized chat moderation" do
- setup do
- user = insert(:user)
- recipient = insert(:user)
-
- {:ok, message} = CommonAPI.post_chat_message(user, recipient, "Yo")
- object = Object.normalize(message, fetch: false)
- chat = Chat.get(user.id, recipient.ap_id)
- cm_ref = MessageReference.for_chat_and_object(chat, object)
-
- %{conn: conn} = oauth_access(["read:chats", "write:chats"])
- %{conn: conn, chat: chat, cm_ref: cm_ref}
- end
-
- test "DELETE /api/pleroma/admin/chats/:id/messages/:message_id", %{
- conn: conn,
- chat: chat,
- cm_ref: cm_ref
- } do
- conn
- |> put_req_header("content-type", "application/json")
- |> delete("/api/pleroma/admin/chats/#{chat.id}/messages/#{cm_ref.id}")
- |> json_response(403)
-
- assert MessageReference.get_by_id(cm_ref.id) == cm_ref
- end
-
- test "GET /api/pleroma/admin/chats/:id/messages", %{conn: conn, chat: chat} do
- conn
- |> get("/api/pleroma/admin/chats/#{chat.id}/messages")
- |> json_response(403)
- end
-
- test "GET /api/pleroma/admin/chats/:id", %{conn: conn, chat: chat} do
- conn
- |> get("/api/pleroma/admin/chats/#{chat.id}")
- |> json_response(403)
- end
- end
-
- describe "unauthenticated chat moderation" do
- setup do
- user = insert(:user)
- recipient = insert(:user)
-
- {:ok, message} = CommonAPI.post_chat_message(user, recipient, "Yo")
- object = Object.normalize(message, fetch: false)
- chat = Chat.get(user.id, recipient.ap_id)
- cm_ref = MessageReference.for_chat_and_object(chat, object)
-
- %{conn: build_conn(), chat: chat, cm_ref: cm_ref}
- end
-
- test "DELETE /api/pleroma/admin/chats/:id/messages/:message_id", %{
- conn: conn,
- chat: chat,
- cm_ref: cm_ref
- } do
- conn
- |> put_req_header("content-type", "application/json")
- |> delete("/api/pleroma/admin/chats/#{chat.id}/messages/#{cm_ref.id}")
- |> json_response(403)
-
- assert MessageReference.get_by_id(cm_ref.id) == cm_ref
- end
-
- test "GET /api/pleroma/admin/chats/:id/messages", %{conn: conn, chat: chat} do
- conn
- |> get("/api/pleroma/admin/chats/#{chat.id}/messages")
- |> json_response(403)
- end
-
- test "GET /api/pleroma/admin/chats/:id", %{conn: conn, chat: chat} do
- conn
- |> get("/api/pleroma/admin/chats/#{chat.id}")
- |> json_response(403)
- end
- end
-end
+++ /dev/null
-# Pleroma: A lightweight social networking server
-# Copyright © 2017-2021 Pleroma Authors <https://pleroma.social/>
-# SPDX-License-Identifier: AGPL-3.0-only
-
-defmodule Pleroma.Web.AdminAPI.ConfigControllerTest do
- use Pleroma.Web.ConnCase
-
- import ExUnit.CaptureLog
- import Pleroma.Factory
-
- alias Pleroma.ConfigDB
-
- setup do
- admin = insert(:user, is_admin: true)
- token = insert(:oauth_admin_token, user: admin)
-
- conn =
- build_conn()
- |> assign(:user, admin)
- |> assign(:token, token)
-
- {:ok, %{admin: admin, token: token, conn: conn}}
- end
-
- describe "GET /api/pleroma/admin/config" do
- setup do: clear_config(:configurable_from_database, true)
-
- test "when configuration from database is off", %{conn: conn} do
- clear_config(:configurable_from_database, false)
- conn = get(conn, "/api/pleroma/admin/config")
-
- assert json_response_and_validate_schema(conn, 400) ==
- %{
- "error" => "You must enable configurable_from_database in your config file."
- }
- end
-
- test "with settings only in db", %{conn: conn} do
- config1 = insert(:config)
- config2 = insert(:config)
-
- conn = get(conn, "/api/pleroma/admin/config?only_db=true")
-
- %{
- "configs" => [
- %{
- "group" => ":pleroma",
- "key" => key1,
- "value" => _
- },
- %{
- "group" => ":pleroma",
- "key" => key2,
- "value" => _
- }
- ]
- } = json_response_and_validate_schema(conn, 200)
-
- assert key1 == inspect(config1.key)
- assert key2 == inspect(config2.key)
- end
-
- test "db is added to settings that are in db", %{conn: conn} do
- _config = insert(:config, key: ":instance", value: [name: "Some name"])
-
- %{"configs" => configs} =
- conn
- |> get("/api/pleroma/admin/config")
- |> json_response_and_validate_schema(200)
-
- [instance_config] =
- Enum.filter(configs, fn %{"group" => group, "key" => key} ->
- group == ":pleroma" and key == ":instance"
- end)
-
- assert instance_config["db"] == [":name"]
- end
-
- test "merged default setting with db settings", %{conn: conn} do
- config1 = insert(:config)
- config2 = insert(:config)
-
- config3 =
- insert(:config,
- value: [k1: :v1, k2: :v2]
- )
-
- %{"configs" => configs} =
- conn
- |> get("/api/pleroma/admin/config")
- |> json_response_and_validate_schema(200)
-
- assert length(configs) > 3
-
- saved_configs = [config1, config2, config3]
- keys = Enum.map(saved_configs, &inspect(&1.key))
-
- received_configs =
- Enum.filter(configs, fn %{"group" => group, "key" => key} ->
- group == ":pleroma" and key in keys
- end)
-
- assert length(received_configs) == 3
-
- db_keys =
- config3.value
- |> Keyword.keys()
- |> ConfigDB.to_json_types()
-
- keys = Enum.map(saved_configs -- [config3], &inspect(&1.key))
-
- values = Enum.map(saved_configs, &ConfigDB.to_json_types(&1.value))
-
- mapset_keys = MapSet.new(keys ++ db_keys)
-
- Enum.each(received_configs, fn %{"value" => value, "db" => db} ->
- db = MapSet.new(db)
- assert MapSet.subset?(db, mapset_keys)
-
- assert value in values
- end)
- end
-
- test "subkeys with full update right merge", %{conn: conn} do
- insert(:config,
- key: ":emoji",
- value: [groups: [a: 1, b: 2], key: [a: 1]]
- )
-
- insert(:config,
- key: ":assets",
- value: [mascots: [a: 1, b: 2], key: [a: 1]]
- )
-
- %{"configs" => configs} =
- conn
- |> get("/api/pleroma/admin/config")
- |> json_response_and_validate_schema(200)
-
- vals =
- Enum.filter(configs, fn %{"group" => group, "key" => key} ->
- group == ":pleroma" and key in [":emoji", ":assets"]
- end)
-
- emoji = Enum.find(vals, fn %{"key" => key} -> key == ":emoji" end)
- assets = Enum.find(vals, fn %{"key" => key} -> key == ":assets" end)
-
- emoji_val = ConfigDB.to_elixir_types(emoji["value"])
- assets_val = ConfigDB.to_elixir_types(assets["value"])
-
- assert emoji_val[:groups] == [a: 1, b: 2]
- assert assets_val[:mascots] == [a: 1, b: 2]
- end
-
- test "with valid `admin_token` query parameter, skips OAuth scopes check" do
- clear_config([:admin_token], "password123")
-
- build_conn()
- |> get("/api/pleroma/admin/config?admin_token=password123")
- |> json_response_and_validate_schema(200)
- end
- end
-
- test "POST /api/pleroma/admin/config with configdb disabled", %{conn: conn} do
- clear_config(:configurable_from_database, false)
-
- conn =
- conn
- |> put_req_header("content-type", "application/json")
- |> post("/api/pleroma/admin/config", %{"configs" => []})
-
- assert json_response_and_validate_schema(conn, 400) ==
- %{"error" => "You must enable configurable_from_database in your config file."}
- end
-
- describe "POST /api/pleroma/admin/config" do
- setup do
- http = Application.get_env(:pleroma, :http)
-
- on_exit(fn ->
- Application.delete_env(:pleroma, :key1)
- Application.delete_env(:pleroma, :key2)
- Application.delete_env(:pleroma, :key3)
- Application.delete_env(:pleroma, :key4)
- Application.delete_env(:pleroma, :keyaa1)
- Application.delete_env(:pleroma, :keyaa2)
- Application.delete_env(:pleroma, Pleroma.Web.Endpoint.NotReal)
- Application.delete_env(:pleroma, Pleroma.Captcha.NotReal)
- Application.put_env(:pleroma, :http, http)
- Application.put_env(:tesla, :adapter, Tesla.Mock)
- Restarter.Pleroma.refresh()
- end)
- end
-
- setup do: clear_config(:configurable_from_database, true)
-
- @tag capture_log: true
- test "create new config setting in db", %{conn: conn} do
- ueberauth = Application.get_env(:ueberauth, Ueberauth)
- on_exit(fn -> Application.put_env(:ueberauth, Ueberauth, ueberauth) end)
-
- conn =
- conn
- |> put_req_header("content-type", "application/json")
- |> post("/api/pleroma/admin/config", %{
- configs: [
- %{group: ":pleroma", key: ":key1", value: "value1"},
- %{
- group: ":ueberauth",
- key: "Ueberauth",
- value: [%{"tuple" => [":consumer_secret", "aaaa"]}]
- },
- %{
- group: ":pleroma",
- key: ":key2",
- value: %{
- ":nested_1" => "nested_value1",
- ":nested_2" => [
- %{":nested_22" => "nested_value222"},
- %{":nested_33" => %{":nested_44" => "nested_444"}}
- ]
- }
- },
- %{
- group: ":pleroma",
- key: ":key3",
- value: [
- %{"nested_3" => ":nested_3", "nested_33" => "nested_33"},
- %{"nested_4" => true}
- ]
- },
- %{
- group: ":pleroma",
- key: ":key4",
- value: %{":nested_5" => ":upload", "endpoint" => "https://example.com"}
- },
- %{
- group: ":idna",
- key: ":key5",
- value: %{"tuple" => ["string", "Pleroma.Captcha.NotReal", []]}
- }
- ]
- })
-
- assert json_response_and_validate_schema(conn, 200) == %{
- "configs" => [
- %{
- "group" => ":pleroma",
- "key" => ":key1",
- "value" => "value1",
- "db" => [":key1"]
- },
- %{
- "group" => ":ueberauth",
- "key" => "Ueberauth",
- "value" => [%{"tuple" => [":consumer_secret", "aaaa"]}],
- "db" => [":consumer_secret"]
- },
- %{
- "group" => ":pleroma",
- "key" => ":key2",
- "value" => %{
- ":nested_1" => "nested_value1",
- ":nested_2" => [
- %{":nested_22" => "nested_value222"},
- %{":nested_33" => %{":nested_44" => "nested_444"}}
- ]
- },
- "db" => [":key2"]
- },
- %{
- "group" => ":pleroma",
- "key" => ":key3",
- "value" => [
- %{"nested_3" => ":nested_3", "nested_33" => "nested_33"},
- %{"nested_4" => true}
- ],
- "db" => [":key3"]
- },
- %{
- "group" => ":pleroma",
- "key" => ":key4",
- "value" => %{"endpoint" => "https://example.com", ":nested_5" => ":upload"},
- "db" => [":key4"]
- },
- %{
- "group" => ":idna",
- "key" => ":key5",
- "value" => %{"tuple" => ["string", "Pleroma.Captcha.NotReal", []]},
- "db" => [":key5"]
- }
- ],
- "need_reboot" => false
- }
-
- assert Application.get_env(:pleroma, :key1) == "value1"
-
- assert Application.get_env(:pleroma, :key2) == %{
- nested_1: "nested_value1",
- nested_2: [
- %{nested_22: "nested_value222"},
- %{nested_33: %{nested_44: "nested_444"}}
- ]
- }
-
- assert Application.get_env(:pleroma, :key3) == [
- %{"nested_3" => :nested_3, "nested_33" => "nested_33"},
- %{"nested_4" => true}
- ]
-
- assert Application.get_env(:pleroma, :key4) == %{
- "endpoint" => "https://example.com",
- nested_5: :upload
- }
-
- assert Application.get_env(:idna, :key5) == {"string", Pleroma.Captcha.NotReal, []}
- end
-
- test "save configs setting without explicit key", %{conn: conn} do
- level = Application.get_env(:quack, :level)
- meta = Application.get_env(:quack, :meta)
- webhook_url = Application.get_env(:quack, :webhook_url)
-
- on_exit(fn ->
- Application.put_env(:quack, :level, level)
- Application.put_env(:quack, :meta, meta)
- Application.put_env(:quack, :webhook_url, webhook_url)
- end)
-
- conn =
- conn
- |> put_req_header("content-type", "application/json")
- |> post("/api/pleroma/admin/config", %{
- configs: [
- %{
- group: ":quack",
- key: ":level",
- value: ":info"
- },
- %{
- group: ":quack",
- key: ":meta",
- value: [":none"]
- },
- %{
- group: ":quack",
- key: ":webhook_url",
- value: "https://hooks.slack.com/services/KEY"
- }
- ]
- })
-
- assert json_response_and_validate_schema(conn, 200) == %{
- "configs" => [
- %{
- "group" => ":quack",
- "key" => ":level",
- "value" => ":info",
- "db" => [":level"]
- },
- %{
- "group" => ":quack",
- "key" => ":meta",
- "value" => [":none"],
- "db" => [":meta"]
- },
- %{
- "group" => ":quack",
- "key" => ":webhook_url",
- "value" => "https://hooks.slack.com/services/KEY",
- "db" => [":webhook_url"]
- }
- ],
- "need_reboot" => false
- }
-
- assert Application.get_env(:quack, :level) == :info
- assert Application.get_env(:quack, :meta) == [:none]
- assert Application.get_env(:quack, :webhook_url) == "https://hooks.slack.com/services/KEY"
- end
-
- test "saving config with partial update", %{conn: conn} do
- insert(:config, key: ":key1", value: :erlang.term_to_binary(key1: 1, key2: 2))
-
- conn =
- conn
- |> put_req_header("content-type", "application/json")
- |> post("/api/pleroma/admin/config", %{
- configs: [
- %{group: ":pleroma", key: ":key1", value: [%{"tuple" => [":key3", 3]}]}
- ]
- })
-
- assert json_response_and_validate_schema(conn, 200) == %{
- "configs" => [
- %{
- "group" => ":pleroma",
- "key" => ":key1",
- "value" => [
- %{"tuple" => [":key1", 1]},
- %{"tuple" => [":key2", 2]},
- %{"tuple" => [":key3", 3]}
- ],
- "db" => [":key1", ":key2", ":key3"]
- }
- ],
- "need_reboot" => false
- }
- end
-
- test "saving config which need pleroma reboot", %{conn: conn} do
- clear_config([:shout, :enabled], true)
-
- assert conn
- |> put_req_header("content-type", "application/json")
- |> post(
- "/api/pleroma/admin/config",
- %{
- configs: [
- %{group: ":pleroma", key: ":shout", value: [%{"tuple" => [":enabled", true]}]}
- ]
- }
- )
- |> json_response_and_validate_schema(200) == %{
- "configs" => [
- %{
- "db" => [":enabled"],
- "group" => ":pleroma",
- "key" => ":shout",
- "value" => [%{"tuple" => [":enabled", true]}]
- }
- ],
- "need_reboot" => true
- }
-
- configs =
- conn
- |> get("/api/pleroma/admin/config")
- |> json_response_and_validate_schema(200)
-
- assert configs["need_reboot"]
-
- capture_log(fn ->
- assert conn |> get("/api/pleroma/admin/restart") |> json_response(200) ==
- %{}
- end) =~ "pleroma restarted"
-
- configs =
- conn
- |> get("/api/pleroma/admin/config")
- |> json_response_and_validate_schema(200)
-
- assert configs["need_reboot"] == false
- end
-
- test "update setting which need reboot, don't change reboot flag until reboot", %{conn: conn} do
- clear_config([:shout, :enabled], true)
-
- assert conn
- |> put_req_header("content-type", "application/json")
- |> post(
- "/api/pleroma/admin/config",
- %{
- configs: [
- %{group: ":pleroma", key: ":shout", value: [%{"tuple" => [":enabled", true]}]}
- ]
- }
- )
- |> json_response_and_validate_schema(200) == %{
- "configs" => [
- %{
- "db" => [":enabled"],
- "group" => ":pleroma",
- "key" => ":shout",
- "value" => [%{"tuple" => [":enabled", true]}]
- }
- ],
- "need_reboot" => true
- }
-
- assert conn
- |> put_req_header("content-type", "application/json")
- |> post("/api/pleroma/admin/config", %{
- configs: [
- %{group: ":pleroma", key: ":key1", value: [%{"tuple" => [":key3", 3]}]}
- ]
- })
- |> json_response_and_validate_schema(200) == %{
- "configs" => [
- %{
- "group" => ":pleroma",
- "key" => ":key1",
- "value" => [
- %{"tuple" => [":key3", 3]}
- ],
- "db" => [":key3"]
- }
- ],
- "need_reboot" => true
- }
-
- capture_log(fn ->
- assert conn |> get("/api/pleroma/admin/restart") |> json_response(200) ==
- %{}
- end) =~ "pleroma restarted"
-
- configs =
- conn
- |> get("/api/pleroma/admin/config")
- |> json_response_and_validate_schema(200)
-
- assert configs["need_reboot"] == false
- end
-
- test "saving config with nested merge", %{conn: conn} do
- insert(:config, key: :key1, value: [key1: 1, key2: [k1: 1, k2: 2]])
-
- conn =
- conn
- |> put_req_header("content-type", "application/json")
- |> post("/api/pleroma/admin/config", %{
- configs: [
- %{
- group: ":pleroma",
- key: ":key1",
- value: [
- %{"tuple" => [":key3", 3]},
- %{
- "tuple" => [
- ":key2",
- [
- %{"tuple" => [":k2", 1]},
- %{"tuple" => [":k3", 3]}
- ]
- ]
- }
- ]
- }
- ]
- })
-
- assert json_response_and_validate_schema(conn, 200) == %{
- "configs" => [
- %{
- "group" => ":pleroma",
- "key" => ":key1",
- "value" => [
- %{"tuple" => [":key1", 1]},
- %{"tuple" => [":key3", 3]},
- %{
- "tuple" => [
- ":key2",
- [
- %{"tuple" => [":k1", 1]},
- %{"tuple" => [":k2", 1]},
- %{"tuple" => [":k3", 3]}
- ]
- ]
- }
- ],
- "db" => [":key1", ":key3", ":key2"]
- }
- ],
- "need_reboot" => false
- }
- end
-
- test "saving special atoms", %{conn: conn} do
- conn =
- conn
- |> put_req_header("content-type", "application/json")
- |> post("/api/pleroma/admin/config", %{
- "configs" => [
- %{
- "group" => ":pleroma",
- "key" => ":key1",
- "value" => [
- %{
- "tuple" => [
- ":ssl_options",
- [%{"tuple" => [":versions", [":tlsv1", ":tlsv1.1", ":tlsv1.2"]]}]
- ]
- }
- ]
- }
- ]
- })
-
- assert json_response_and_validate_schema(conn, 200) == %{
- "configs" => [
- %{
- "group" => ":pleroma",
- "key" => ":key1",
- "value" => [
- %{
- "tuple" => [
- ":ssl_options",
- [%{"tuple" => [":versions", [":tlsv1", ":tlsv1.1", ":tlsv1.2"]]}]
- ]
- }
- ],
- "db" => [":ssl_options"]
- }
- ],
- "need_reboot" => false
- }
-
- assert Application.get_env(:pleroma, :key1) == [
- ssl_options: [versions: [:tlsv1, :"tlsv1.1", :"tlsv1.2"]]
- ]
- end
-
- test "saving full setting if value is in full_key_update list", %{conn: conn} do
- backends = Application.get_env(:logger, :backends)
- on_exit(fn -> Application.put_env(:logger, :backends, backends) end)
-
- insert(:config,
- group: :logger,
- key: :backends,
- value: []
- )
-
- Pleroma.Config.TransferTask.load_and_update_env([], false)
-
- assert Application.get_env(:logger, :backends) == []
-
- conn =
- conn
- |> put_req_header("content-type", "application/json")
- |> post("/api/pleroma/admin/config", %{
- configs: [
- %{
- group: ":logger",
- key: ":backends",
- value: [":console"]
- }
- ]
- })
-
- assert json_response_and_validate_schema(conn, 200) == %{
- "configs" => [
- %{
- "group" => ":logger",
- "key" => ":backends",
- "value" => [
- ":console"
- ],
- "db" => [":backends"]
- }
- ],
- "need_reboot" => false
- }
-
- assert Application.get_env(:logger, :backends) == [
- :console
- ]
- end
-
- test "saving full setting if value is not keyword", %{conn: conn} do
- insert(:config,
- group: :tesla,
- key: :adapter,
- value: Tesla.Adapter.Hackey
- )
-
- conn =
- conn
- |> put_req_header("content-type", "application/json")
- |> post("/api/pleroma/admin/config", %{
- configs: [
- %{group: ":tesla", key: ":adapter", value: "Tesla.Adapter.Httpc"}
- ]
- })
-
- assert json_response_and_validate_schema(conn, 200) == %{
- "configs" => [
- %{
- "group" => ":tesla",
- "key" => ":adapter",
- "value" => "Tesla.Adapter.Httpc",
- "db" => [":adapter"]
- }
- ],
- "need_reboot" => false
- }
- end
-
- test "update config setting & delete with fallback to default value", %{
- conn: conn,
- admin: admin,
- token: token
- } do
- ueberauth = Application.get_env(:ueberauth, Ueberauth)
- insert(:config, key: :keyaa1)
- insert(:config, key: :keyaa2)
-
- config3 =
- insert(:config,
- group: :ueberauth,
- key: Ueberauth
- )
-
- conn =
- conn
- |> put_req_header("content-type", "application/json")
- |> post("/api/pleroma/admin/config", %{
- configs: [
- %{group: ":pleroma", key: ":keyaa1", value: "another_value"},
- %{group: ":pleroma", key: ":keyaa2", value: "another_value"}
- ]
- })
-
- assert json_response_and_validate_schema(conn, 200) == %{
- "configs" => [
- %{
- "group" => ":pleroma",
- "key" => ":keyaa1",
- "value" => "another_value",
- "db" => [":keyaa1"]
- },
- %{
- "group" => ":pleroma",
- "key" => ":keyaa2",
- "value" => "another_value",
- "db" => [":keyaa2"]
- }
- ],
- "need_reboot" => false
- }
-
- assert Application.get_env(:pleroma, :keyaa1) == "another_value"
- assert Application.get_env(:pleroma, :keyaa2) == "another_value"
- assert Application.get_env(:ueberauth, Ueberauth) == config3.value
-
- conn =
- build_conn()
- |> assign(:user, admin)
- |> assign(:token, token)
- |> put_req_header("content-type", "application/json")
- |> post("/api/pleroma/admin/config", %{
- configs: [
- %{group: ":pleroma", key: ":keyaa2", delete: true},
- %{
- group: ":ueberauth",
- key: "Ueberauth",
- delete: true
- }
- ]
- })
-
- assert json_response_and_validate_schema(conn, 200) == %{
- "configs" => [],
- "need_reboot" => false
- }
-
- assert Application.get_env(:ueberauth, Ueberauth) == ueberauth
- refute Keyword.has_key?(Application.get_all_env(:pleroma), :keyaa2)
- end
-
- test "common config example", %{conn: conn} do
- conn =
- conn
- |> put_req_header("content-type", "application/json")
- |> post("/api/pleroma/admin/config", %{
- configs: [
- %{
- "group" => ":pleroma",
- "key" => "Pleroma.Captcha.NotReal",
- "value" => [
- %{"tuple" => [":enabled", false]},
- %{"tuple" => [":method", "Pleroma.Captcha.Kocaptcha"]},
- %{"tuple" => [":seconds_valid", 60]},
- %{"tuple" => [":path", ""]},
- %{"tuple" => [":key1", nil]},
- %{"tuple" => [":partial_chain", "&:hackney_connect.partial_chain/1"]},
- %{"tuple" => [":regex1", "~r/https:\/\/example.com/"]},
- %{"tuple" => [":regex2", "~r/https:\/\/example.com/u"]},
- %{"tuple" => [":regex3", "~r/https:\/\/example.com/i"]},
- %{"tuple" => [":regex4", "~r/https:\/\/example.com/s"]},
- %{"tuple" => [":name", "Pleroma"]}
- ]
- }
- ]
- })
-
- assert Config.get([Pleroma.Captcha.NotReal, :name]) == "Pleroma"
-
- assert json_response_and_validate_schema(conn, 200) == %{
- "configs" => [
- %{
- "group" => ":pleroma",
- "key" => "Pleroma.Captcha.NotReal",
- "value" => [
- %{"tuple" => [":enabled", false]},
- %{"tuple" => [":method", "Pleroma.Captcha.Kocaptcha"]},
- %{"tuple" => [":seconds_valid", 60]},
- %{"tuple" => [":path", ""]},
- %{"tuple" => [":key1", nil]},
- %{"tuple" => [":partial_chain", "&:hackney_connect.partial_chain/1"]},
- %{"tuple" => [":regex1", "~r/https:\\/\\/example.com/"]},
- %{"tuple" => [":regex2", "~r/https:\\/\\/example.com/u"]},
- %{"tuple" => [":regex3", "~r/https:\\/\\/example.com/i"]},
- %{"tuple" => [":regex4", "~r/https:\\/\\/example.com/s"]},
- %{"tuple" => [":name", "Pleroma"]}
- ],
- "db" => [
- ":enabled",
- ":method",
- ":seconds_valid",
- ":path",
- ":key1",
- ":partial_chain",
- ":regex1",
- ":regex2",
- ":regex3",
- ":regex4",
- ":name"
- ]
- }
- ],
- "need_reboot" => false
- }
- end
-
- test "tuples with more than two values", %{conn: conn} do
- conn =
- conn
- |> put_req_header("content-type", "application/json")
- |> post("/api/pleroma/admin/config", %{
- configs: [
- %{
- "group" => ":pleroma",
- "key" => "Pleroma.Web.Endpoint.NotReal",
- "value" => [
- %{
- "tuple" => [
- ":http",
- [
- %{
- "tuple" => [
- ":key2",
- [
- %{
- "tuple" => [
- ":_",
- [
- %{
- "tuple" => [
- "/api/v1/streaming",
- "Pleroma.Web.MastodonAPI.WebsocketHandler",
- []
- ]
- },
- %{
- "tuple" => [
- "/websocket",
- "Phoenix.Endpoint.CowboyWebSocket",
- %{
- "tuple" => [
- "Phoenix.Transports.WebSocket",
- %{
- "tuple" => [
- "Pleroma.Web.Endpoint",
- "Pleroma.Web.UserSocket",
- []
- ]
- }
- ]
- }
- ]
- },
- %{
- "tuple" => [
- ":_",
- "Phoenix.Endpoint.Cowboy2Handler",
- %{"tuple" => ["Pleroma.Web.Endpoint", []]}
- ]
- }
- ]
- ]
- }
- ]
- ]
- }
- ]
- ]
- }
- ]
- }
- ]
- })
-
- assert json_response_and_validate_schema(conn, 200) == %{
- "configs" => [
- %{
- "group" => ":pleroma",
- "key" => "Pleroma.Web.Endpoint.NotReal",
- "value" => [
- %{
- "tuple" => [
- ":http",
- [
- %{
- "tuple" => [
- ":key2",
- [
- %{
- "tuple" => [
- ":_",
- [
- %{
- "tuple" => [
- "/api/v1/streaming",
- "Pleroma.Web.MastodonAPI.WebsocketHandler",
- []
- ]
- },
- %{
- "tuple" => [
- "/websocket",
- "Phoenix.Endpoint.CowboyWebSocket",
- %{
- "tuple" => [
- "Phoenix.Transports.WebSocket",
- %{
- "tuple" => [
- "Pleroma.Web.Endpoint",
- "Pleroma.Web.UserSocket",
- []
- ]
- }
- ]
- }
- ]
- },
- %{
- "tuple" => [
- ":_",
- "Phoenix.Endpoint.Cowboy2Handler",
- %{"tuple" => ["Pleroma.Web.Endpoint", []]}
- ]
- }
- ]
- ]
- }
- ]
- ]
- }
- ]
- ]
- }
- ],
- "db" => [":http"]
- }
- ],
- "need_reboot" => false
- }
- end
-
- test "settings with nesting map", %{conn: conn} do
- conn =
- conn
- |> put_req_header("content-type", "application/json")
- |> post("/api/pleroma/admin/config", %{
- configs: [
- %{
- "group" => ":pleroma",
- "key" => ":key1",
- "value" => [
- %{"tuple" => [":key2", "some_val"]},
- %{
- "tuple" => [
- ":key3",
- %{
- ":max_options" => 20,
- ":max_option_chars" => 200,
- ":min_expiration" => 0,
- ":max_expiration" => 31_536_000,
- "nested" => %{
- ":max_options" => 20,
- ":max_option_chars" => 200,
- ":min_expiration" => 0,
- ":max_expiration" => 31_536_000
- }
- }
- ]
- }
- ]
- }
- ]
- })
-
- assert json_response_and_validate_schema(conn, 200) ==
- %{
- "configs" => [
- %{
- "group" => ":pleroma",
- "key" => ":key1",
- "value" => [
- %{"tuple" => [":key2", "some_val"]},
- %{
- "tuple" => [
- ":key3",
- %{
- ":max_expiration" => 31_536_000,
- ":max_option_chars" => 200,
- ":max_options" => 20,
- ":min_expiration" => 0,
- "nested" => %{
- ":max_expiration" => 31_536_000,
- ":max_option_chars" => 200,
- ":max_options" => 20,
- ":min_expiration" => 0
- }
- }
- ]
- }
- ],
- "db" => [":key2", ":key3"]
- }
- ],
- "need_reboot" => false
- }
- end
-
- test "value as map", %{conn: conn} do
- conn =
- conn
- |> put_req_header("content-type", "application/json")
- |> post("/api/pleroma/admin/config", %{
- configs: [
- %{
- "group" => ":pleroma",
- "key" => ":key1",
- "value" => %{"key" => "some_val"}
- }
- ]
- })
-
- assert json_response_and_validate_schema(conn, 200) ==
- %{
- "configs" => [
- %{
- "group" => ":pleroma",
- "key" => ":key1",
- "value" => %{"key" => "some_val"},
- "db" => [":key1"]
- }
- ],
- "need_reboot" => false
- }
- end
-
- test "queues key as atom", %{conn: conn} do
- conn =
- conn
- |> put_req_header("content-type", "application/json")
- |> post("/api/pleroma/admin/config", %{
- configs: [
- %{
- "group" => ":oban",
- "key" => ":queues",
- "value" => [
- %{"tuple" => [":federator_incoming", 50]},
- %{"tuple" => [":federator_outgoing", 50]},
- %{"tuple" => [":web_push", 50]},
- %{"tuple" => [":mailer", 10]},
- %{"tuple" => [":transmogrifier", 20]},
- %{"tuple" => [":scheduled_activities", 10]},
- %{"tuple" => [":background", 5]}
- ]
- }
- ]
- })
-
- assert json_response_and_validate_schema(conn, 200) == %{
- "configs" => [
- %{
- "group" => ":oban",
- "key" => ":queues",
- "value" => [
- %{"tuple" => [":federator_incoming", 50]},
- %{"tuple" => [":federator_outgoing", 50]},
- %{"tuple" => [":web_push", 50]},
- %{"tuple" => [":mailer", 10]},
- %{"tuple" => [":transmogrifier", 20]},
- %{"tuple" => [":scheduled_activities", 10]},
- %{"tuple" => [":background", 5]}
- ],
- "db" => [
- ":federator_incoming",
- ":federator_outgoing",
- ":web_push",
- ":mailer",
- ":transmogrifier",
- ":scheduled_activities",
- ":background"
- ]
- }
- ],
- "need_reboot" => false
- }
- end
-
- test "delete part of settings by atom subkeys", %{conn: conn} do
- insert(:config,
- key: :keyaa1,
- value: [subkey1: "val1", subkey2: "val2", subkey3: "val3"]
- )
-
- conn =
- conn
- |> put_req_header("content-type", "application/json")
- |> post("/api/pleroma/admin/config", %{
- configs: [
- %{
- group: ":pleroma",
- key: ":keyaa1",
- subkeys: [":subkey1", ":subkey3"],
- delete: true
- }
- ]
- })
-
- assert json_response_and_validate_schema(conn, 200) == %{
- "configs" => [
- %{
- "group" => ":pleroma",
- "key" => ":keyaa1",
- "value" => [%{"tuple" => [":subkey2", "val2"]}],
- "db" => [":subkey2"]
- }
- ],
- "need_reboot" => false
- }
- end
-
- test "proxy tuple localhost", %{conn: conn} do
- conn =
- conn
- |> put_req_header("content-type", "application/json")
- |> post("/api/pleroma/admin/config", %{
- configs: [
- %{
- group: ":pleroma",
- key: ":http",
- value: [
- %{"tuple" => [":proxy_url", %{"tuple" => [":socks5", "localhost", 1234]}]}
- ]
- }
- ]
- })
-
- assert %{
- "configs" => [
- %{
- "group" => ":pleroma",
- "key" => ":http",
- "value" => value,
- "db" => db
- }
- ]
- } = json_response_and_validate_schema(conn, 200)
-
- assert %{"tuple" => [":proxy_url", %{"tuple" => [":socks5", "localhost", 1234]}]} in value
- assert ":proxy_url" in db
- end
-
- test "proxy tuple domain", %{conn: conn} do
- conn =
- conn
- |> put_req_header("content-type", "application/json")
- |> post("/api/pleroma/admin/config", %{
- configs: [
- %{
- group: ":pleroma",
- key: ":http",
- value: [
- %{"tuple" => [":proxy_url", %{"tuple" => [":socks5", "domain.com", 1234]}]}
- ]
- }
- ]
- })
-
- assert %{
- "configs" => [
- %{
- "group" => ":pleroma",
- "key" => ":http",
- "value" => value,
- "db" => db
- }
- ]
- } = json_response_and_validate_schema(conn, 200)
-
- assert %{"tuple" => [":proxy_url", %{"tuple" => [":socks5", "domain.com", 1234]}]} in value
- assert ":proxy_url" in db
- end
-
- test "proxy tuple ip", %{conn: conn} do
- conn =
- conn
- |> put_req_header("content-type", "application/json")
- |> post("/api/pleroma/admin/config", %{
- configs: [
- %{
- group: ":pleroma",
- key: ":http",
- value: [
- %{"tuple" => [":proxy_url", %{"tuple" => [":socks5", "127.0.0.1", 1234]}]}
- ]
- }
- ]
- })
-
- assert %{
- "configs" => [
- %{
- "group" => ":pleroma",
- "key" => ":http",
- "value" => value,
- "db" => db
- }
- ]
- } = json_response_and_validate_schema(conn, 200)
-
- assert %{"tuple" => [":proxy_url", %{"tuple" => [":socks5", "127.0.0.1", 1234]}]} in value
- assert ":proxy_url" in db
- end
-
- @tag capture_log: true
- test "doesn't set keys not in the whitelist", %{conn: conn} do
- clear_config(:database_config_whitelist, [
- {:pleroma, :key1},
- {:pleroma, :key2},
- {:pleroma, Pleroma.Captcha.NotReal},
- {:not_real}
- ])
-
- conn
- |> put_req_header("content-type", "application/json")
- |> post("/api/pleroma/admin/config", %{
- configs: [
- %{group: ":pleroma", key: ":key1", value: "value1"},
- %{group: ":pleroma", key: ":key2", value: "value2"},
- %{group: ":pleroma", key: ":key3", value: "value3"},
- %{group: ":pleroma", key: "Pleroma.Web.Endpoint.NotReal", value: "value4"},
- %{group: ":pleroma", key: "Pleroma.Captcha.NotReal", value: "value5"},
- %{group: ":not_real", key: ":anything", value: "value6"}
- ]
- })
-
- assert Application.get_env(:pleroma, :key1) == "value1"
- assert Application.get_env(:pleroma, :key2) == "value2"
- assert Application.get_env(:pleroma, :key3) == nil
- assert Application.get_env(:pleroma, Pleroma.Web.Endpoint.NotReal) == nil
- assert Application.get_env(:pleroma, Pleroma.Captcha.NotReal) == "value5"
- assert Application.get_env(:not_real, :anything) == "value6"
- end
-
- test "args for Pleroma.Upload.Filter.Mogrify with custom tuples", %{conn: conn} do
- clear_config(Pleroma.Upload.Filter.Mogrify)
-
- assert conn
- |> put_req_header("content-type", "application/json")
- |> post("/api/pleroma/admin/config", %{
- configs: [
- %{
- group: ":pleroma",
- key: "Pleroma.Upload.Filter.Mogrify",
- value: [
- %{"tuple" => [":args", ["auto-orient", "strip"]]}
- ]
- }
- ]
- })
- |> json_response_and_validate_schema(200) == %{
- "configs" => [
- %{
- "group" => ":pleroma",
- "key" => "Pleroma.Upload.Filter.Mogrify",
- "value" => [
- %{"tuple" => [":args", ["auto-orient", "strip"]]}
- ],
- "db" => [":args"]
- }
- ],
- "need_reboot" => false
- }
-
- assert Config.get(Pleroma.Upload.Filter.Mogrify) == [args: ["auto-orient", "strip"]]
-
- assert conn
- |> put_req_header("content-type", "application/json")
- |> post("/api/pleroma/admin/config", %{
- configs: [
- %{
- group: ":pleroma",
- key: "Pleroma.Upload.Filter.Mogrify",
- value: [
- %{
- "tuple" => [
- ":args",
- [
- "auto-orient",
- "strip",
- "{\"implode\", \"1\"}",
- "{\"resize\", \"3840x1080>\"}"
- ]
- ]
- }
- ]
- }
- ]
- })
- |> json_response(200) == %{
- "configs" => [
- %{
- "group" => ":pleroma",
- "key" => "Pleroma.Upload.Filter.Mogrify",
- "value" => [
- %{
- "tuple" => [
- ":args",
- [
- "auto-orient",
- "strip",
- "{\"implode\", \"1\"}",
- "{\"resize\", \"3840x1080>\"}"
- ]
- ]
- }
- ],
- "db" => [":args"]
- }
- ],
- "need_reboot" => false
- }
-
- assert Config.get(Pleroma.Upload.Filter.Mogrify) == [
- args: ["auto-orient", "strip", {"implode", "1"}, {"resize", "3840x1080>"}]
- ]
- end
-
- test "enables the welcome messages", %{conn: conn} do
- clear_config([:welcome])
-
- params = %{
- "group" => ":pleroma",
- "key" => ":welcome",
- "value" => [
- %{
- "tuple" => [
- ":direct_message",
- [
- %{"tuple" => [":enabled", true]},
- %{"tuple" => [":message", "Welcome to Pleroma!"]},
- %{"tuple" => [":sender_nickname", "pleroma"]}
- ]
- ]
- },
- %{
- "tuple" => [
- ":chat_message",
- [
- %{"tuple" => [":enabled", true]},
- %{"tuple" => [":message", "Welcome to Pleroma!"]},
- %{"tuple" => [":sender_nickname", "pleroma"]}
- ]
- ]
- },
- %{
- "tuple" => [
- ":email",
- [
- %{"tuple" => [":enabled", true]},
- %{"tuple" => [":sender", %{"tuple" => ["pleroma@dev.dev", "Pleroma"]}]},
- %{"tuple" => [":subject", "Welcome to <%= instance_name %>!"]},
- %{"tuple" => [":html", "Welcome to <%= instance_name %>!"]},
- %{"tuple" => [":text", "Welcome to <%= instance_name %>!"]}
- ]
- ]
- }
- ]
- }
-
- refute Pleroma.User.WelcomeEmail.enabled?()
- refute Pleroma.User.WelcomeMessage.enabled?()
- refute Pleroma.User.WelcomeChatMessage.enabled?()
-
- res =
- assert conn
- |> put_req_header("content-type", "application/json")
- |> post("/api/pleroma/admin/config", %{"configs" => [params]})
- |> json_response_and_validate_schema(200)
-
- assert Pleroma.User.WelcomeEmail.enabled?()
- assert Pleroma.User.WelcomeMessage.enabled?()
- assert Pleroma.User.WelcomeChatMessage.enabled?()
-
- assert res == %{
- "configs" => [
- %{
- "db" => [":direct_message", ":chat_message", ":email"],
- "group" => ":pleroma",
- "key" => ":welcome",
- "value" => params["value"]
- }
- ],
- "need_reboot" => false
- }
- end
-
- test "custom instance thumbnail", %{conn: conn} do
- clear_config([:instance])
-
- params = %{
- "group" => ":pleroma",
- "key" => ":instance",
- "value" => [
- %{
- "tuple" => [
- ":instance_thumbnail",
- "https://example.com/media/new_thumbnail.jpg"
- ]
- }
- ]
- }
-
- assert conn
- |> put_req_header("content-type", "application/json")
- |> post("/api/pleroma/admin/config", %{"configs" => [params]})
- |> json_response_and_validate_schema(200) ==
- %{
- "configs" => [
- %{
- "db" => [":instance_thumbnail"],
- "group" => ":pleroma",
- "key" => ":instance",
- "value" => params["value"]
- }
- ],
- "need_reboot" => false
- }
-
- assert conn
- |> get("/api/v1/instance")
- |> json_response_and_validate_schema(200)
- |> Map.take(["thumbnail"]) ==
- %{"thumbnail" => "https://example.com/media/new_thumbnail.jpg"}
- end
-
- test "Concurrent Limiter", %{conn: conn} do
- clear_config([ConcurrentLimiter])
-
- params = %{
- "group" => ":pleroma",
- "key" => "ConcurrentLimiter",
- "value" => [
- %{
- "tuple" => [
- "Pleroma.Web.RichMedia.Helpers",
- [
- %{"tuple" => [":max_running", 6]},
- %{"tuple" => [":max_waiting", 6]}
- ]
- ]
- },
- %{
- "tuple" => [
- "Pleroma.Web.ActivityPub.MRF.MediaProxyWarmingPolicy",
- [
- %{"tuple" => [":max_running", 7]},
- %{"tuple" => [":max_waiting", 7]}
- ]
- ]
- }
- ]
- }
-
- assert conn
- |> put_req_header("content-type", "application/json")
- |> post("/api/pleroma/admin/config", %{"configs" => [params]})
- |> json_response_and_validate_schema(200)
- end
- end
-
- describe "GET /api/pleroma/admin/config/descriptions" do
- test "structure", %{conn: conn} do
- conn = get(conn, "/api/pleroma/admin/config/descriptions")
-
- assert [child | _others] = json_response_and_validate_schema(conn, 200)
-
- assert child["children"]
- assert child["key"]
- assert String.starts_with?(child["group"], ":")
- assert child["description"]
- end
-
- test "filters by database configuration whitelist", %{conn: conn} do
- clear_config(:database_config_whitelist, [
- {:pleroma, :instance},
- {:pleroma, :activitypub},
- {:pleroma, Pleroma.Upload}
- ])
-
- conn = get(conn, "/api/pleroma/admin/config/descriptions")
-
- children = json_response_and_validate_schema(conn, 200)
-
- assert length(children) == 3
-
- assert Enum.count(children, fn c -> c["group"] == ":pleroma" end) == 3
-
- instance = Enum.find(children, fn c -> c["key"] == ":instance" end)
- assert instance["children"]
-
- activitypub = Enum.find(children, fn c -> c["key"] == ":activitypub" end)
- assert activitypub["children"]
-
- web_endpoint = Enum.find(children, fn c -> c["key"] == "Pleroma.Upload" end)
- assert web_endpoint["children"]
- end
- end
-end
use Pleroma.DataCase
alias Pleroma.Activity
- alias Pleroma.Chat
alias Pleroma.Conversation.Participation
alias Pleroma.Notification
alias Pleroma.Object
alias Pleroma.Repo
alias Pleroma.User
- alias Pleroma.Web.ActivityPub.ActivityPub
alias Pleroma.Web.ActivityPub.Transmogrifier
alias Pleroma.Web.ActivityPub.Visibility
alias Pleroma.Web.AdminAPI.AccountView
end
end
- describe "posting chat messages" do
- setup do: clear_config([:instance, :chat_limit])
-
- test "it posts a self-chat" do
- author = insert(:user)
- recipient = author
-
- {:ok, activity} =
- CommonAPI.post_chat_message(
- author,
- recipient,
- "remember to buy milk when milk truk arive"
- )
-
- assert activity.data["type"] == "Create"
- end
-
- test "it posts a chat message without content but with an attachment" do
- author = insert(:user)
- recipient = insert(:user)
-
- file = %Plug.Upload{
- content_type: "image/jpeg",
- path: Path.absname("test/fixtures/image.jpg"),
- filename: "an_image.jpg"
- }
-
- {:ok, upload} = ActivityPub.upload(file, actor: author.ap_id)
-
- with_mocks([
- {
- Pleroma.Web.Streamer,
- [],
- [
- stream: fn _, _ ->
- nil
- end
- ]
- },
- {
- Pleroma.Web.Push,
- [],
- [
- send: fn _ -> nil end
- ]
- }
- ]) do
- {:ok, activity} =
- CommonAPI.post_chat_message(
- author,
- recipient,
- nil,
- media_id: upload.id
- )
-
- notification =
- Notification.for_user_and_activity(recipient, activity)
- |> Repo.preload(:activity)
-
- assert called(Pleroma.Web.Push.send(notification))
- assert called(Pleroma.Web.Streamer.stream(["user", "user:notification"], notification))
- assert called(Pleroma.Web.Streamer.stream(["user", "user:pleroma_chat"], :_))
-
- assert activity
- end
- end
-
- test "it adds html newlines" do
- author = insert(:user)
- recipient = insert(:user)
-
- other_user = insert(:user)
-
- {:ok, activity} =
- CommonAPI.post_chat_message(
- author,
- recipient,
- "uguu\nuguuu"
- )
-
- assert other_user.ap_id not in activity.recipients
-
- object = Object.normalize(activity, fetch: false)
-
- assert object.data["content"] == "uguu<br/>uguuu"
- end
-
- test "it linkifies" do
- author = insert(:user)
- recipient = insert(:user)
-
- other_user = insert(:user)
-
- {:ok, activity} =
- CommonAPI.post_chat_message(
- author,
- recipient,
- "https://example.org is the site of @#{other_user.nickname} #2hu"
- )
-
- assert other_user.ap_id not in activity.recipients
-
- object = Object.normalize(activity, fetch: false)
-
- assert object.data["content"] ==
- "<a href=\"https://example.org\" rel=\"ugc\">https://example.org</a> is the site of <span class=\"h-card\"><a class=\"u-url mention\" data-user=\"#{other_user.id}\" href=\"#{other_user.ap_id}\" rel=\"ugc\">@<span>#{other_user.nickname}</span></a></span> <a class=\"hashtag\" data-tag=\"2hu\" href=\"http://localhost:4001/tag/2hu\">#2hu</a>"
- end
-
- test "it posts a chat message" do
- author = insert(:user)
- recipient = insert(:user)
-
- {:ok, activity} =
- CommonAPI.post_chat_message(
- author,
- recipient,
- "a test message <script>alert('uuu')</script> :firefox:"
- )
-
- assert activity.data["type"] == "Create"
- assert activity.local
- object = Object.normalize(activity, fetch: false)
-
- assert object.data["type"] == "ChatMessage"
- assert object.data["to"] == [recipient.ap_id]
-
- assert object.data["content"] ==
- "a test message <script>alert('uuu')</script> :firefox:"
-
- assert object.data["emoji"] == %{
- "firefox" => "http://localhost:4001/emoji/Firefox.gif"
- }
-
- assert Chat.get(author.id, recipient.ap_id)
- assert Chat.get(recipient.id, author.ap_id)
-
- assert :ok == Pleroma.Web.Federator.perform(:publish, activity)
- end
-
- test "it reject messages over the local limit" do
- clear_config([:instance, :chat_limit], 2)
-
- author = insert(:user)
- recipient = insert(:user)
-
- {:error, message} =
- CommonAPI.post_chat_message(
- author,
- recipient,
- "123"
- )
-
- assert message == :content_too_long
- end
-
- test "it reject messages via MRF" do
- clear_config([:mrf_keyword, :reject], ["GNO"])
- clear_config([:mrf, :policies], [Pleroma.Web.ActivityPub.MRF.KeywordPolicy])
-
- author = insert(:user)
- recipient = insert(:user)
-
- assert {:reject, "[KeywordPolicy] Matches with rejected keyword"} ==
- CommonAPI.post_chat_message(author, recipient, "GNO/Linux")
- end
- end
-
describe "unblocking" do
test "it works even without an existing block activity" do
blocked = insert(:user)
response = json_response_and_validate_schema(conn, 200)
assert %{"id" => id, "source" => %{"privacy" => "public"}} = response
- assert response["pleroma"]["chat_token"]
assert response["pleroma"]["unread_notifications_count"] == 6
assert id == to_string(user.id)
end
"background_upload_limit" => _,
"banner_upload_limit" => _,
"background_image" => from_config_background,
- "shout_limit" => _,
"description_limit" => _
} = result
assert response == expected_response
end
- test "by default, does not contain pleroma:chat_mention" do
- %{user: user, conn: conn} = oauth_access(["read:notifications"])
- other_user = insert(:user)
-
- {:ok, _activity} = CommonAPI.post_chat_message(other_user, user, "hey")
-
- result =
- conn
- |> get("/api/v1/notifications")
- |> json_response_and_validate_schema(200)
-
- assert [] == result
-
- result =
- conn
- |> get("/api/v1/notifications?include_types[]=pleroma:chat_mention")
- |> json_response_and_validate_schema(200)
-
- assert [_] = result
- end
-
test "by default, does not contain pleroma:report" do
%{user: user, conn: conn} = oauth_access(["read:notifications"])
other_user = insert(:user)
"favourite" => true,
"follow" => true,
"reblog" => true,
- "pleroma:chat_mention" => true,
"pleroma:emoji_reaction" => true
}
},
"favourite" => true,
"follow" => true,
"reblog" => true,
- "pleroma:chat_mention" => true,
"pleroma:emoji_reaction" => true
},
"endpoint" => subscription.endpoint,
"favourite" => true,
"follow" => true,
"reblog" => true,
- "pleroma:chat_mention" => true,
"pleroma:emoji_reaction" => true
}
}
"favourite" => false,
"follow" => false,
"reblog" => false,
- "pleroma:chat_mention" => false,
"pleroma:emoji_reaction" => false
}
}
"favourite" => false,
"follow" => false,
"reblog" => false,
- "pleroma:chat_mention" => false,
"pleroma:emoji_reaction" => false
},
"endpoint" => "https://example.com/example/1234",
assert user_data["locked"] == true
end
- test "updates the user's chat acceptance status", %{conn: conn} do
- conn = patch(conn, "/api/v1/accounts/update_credentials", %{accepts_chat_messages: "false"})
-
- assert user_data = json_response_and_validate_schema(conn, 200)
- assert user_data["pleroma"]["accepts_chat_messages"] == false
- end
-
test "updates the user's allow_following_move", %{user: user, conn: conn} do
assert user.allow_following_move == true
hide_followers_count: false,
hide_follows_count: false,
relationship: %{},
- skip_thread_containment: false,
- accepts_chat_messages: nil
+ skip_thread_containment: false
}
}
hide_followers_count: false,
hide_follows_count: false,
relationship: %{},
- skip_thread_containment: false,
- accepts_chat_messages: nil
+ skip_thread_containment: false
}
}
use Pleroma.DataCase
alias Pleroma.Activity
- alias Pleroma.Chat
- alias Pleroma.Chat.MessageReference
alias Pleroma.Notification
alias Pleroma.Object
alias Pleroma.Repo
alias Pleroma.Web.MastodonAPI.NotificationView
alias Pleroma.Web.MastodonAPI.StatusView
alias Pleroma.Web.MediaProxy
- alias Pleroma.Web.PleromaAPI.Chat.MessageReferenceView
import Pleroma.Factory
defp test_notifications_rendering(notifications, user, expected_result) do
assert expected_result == result
end
- test "ChatMessage notification" do
- user = insert(:user)
- recipient = insert(:user)
- {:ok, activity} = CommonAPI.post_chat_message(user, recipient, "what's up my dude")
-
- {:ok, [notification]} = Notification.create_notifications(activity)
-
- object = Object.normalize(activity, fetch: false)
- chat = Chat.get(recipient.id, user.ap_id)
-
- cm_ref = MessageReference.for_chat_and_object(chat, object)
-
- expected = %{
- id: to_string(notification.id),
- pleroma: %{is_seen: false, is_muted: false},
- type: "pleroma:chat_mention",
- account: AccountView.render("show.json", %{user: user, for: recipient}),
- chat_message: MessageReferenceView.render("show.json", %{chat_message_reference: cm_ref}),
- created_at: Utils.to_masto_date(notification.inserted_at)
- }
-
- test_notifications_rendering([notification], recipient, [expected])
- end
-
test "Mention notification" do
user = insert(:user)
mentioned_user = insert(:user)
+++ /dev/null
-# Pleroma: A lightweight social networking server
-# Copyright © 2017-2021 Pleroma Authors <https://pleroma.social/>
-# SPDX-License-Identifier: AGPL-3.0-only
-defmodule Pleroma.Web.PleromaAPI.ChatControllerTest do
- use Pleroma.Web.ConnCase
-
- alias Pleroma.Chat
- alias Pleroma.Chat.MessageReference
- alias Pleroma.Object
- alias Pleroma.User
- alias Pleroma.Web.ActivityPub.ActivityPub
- alias Pleroma.Web.CommonAPI
-
- import Pleroma.Factory
-
- describe "POST /api/v1/pleroma/chats/:id/messages/:message_id/read" do
- setup do: oauth_access(["write:chats"])
-
- test "it marks one message as read", %{conn: conn, user: user} do
- other_user = insert(:user)
-
- {:ok, create} = CommonAPI.post_chat_message(other_user, user, "sup")
- {:ok, _create} = CommonAPI.post_chat_message(other_user, user, "sup part 2")
- {:ok, chat} = Chat.get_or_create(user.id, other_user.ap_id)
- object = Object.normalize(create, fetch: false)
- cm_ref = MessageReference.for_chat_and_object(chat, object)
-
- assert cm_ref.unread == true
-
- result =
- conn
- |> post("/api/v1/pleroma/chats/#{chat.id}/messages/#{cm_ref.id}/read")
- |> json_response_and_validate_schema(200)
-
- assert result["unread"] == false
-
- cm_ref = MessageReference.for_chat_and_object(chat, object)
-
- assert cm_ref.unread == false
- end
- end
-
- describe "POST /api/v1/pleroma/chats/:id/read" do
- setup do: oauth_access(["write:chats"])
-
- test "given a `last_read_id`, it marks everything until then as read", %{
- conn: conn,
- user: user
- } do
- other_user = insert(:user)
-
- {:ok, create} = CommonAPI.post_chat_message(other_user, user, "sup")
- {:ok, _create} = CommonAPI.post_chat_message(other_user, user, "sup part 2")
- {:ok, chat} = Chat.get_or_create(user.id, other_user.ap_id)
- object = Object.normalize(create, fetch: false)
- cm_ref = MessageReference.for_chat_and_object(chat, object)
-
- assert cm_ref.unread == true
-
- result =
- conn
- |> put_req_header("content-type", "application/json")
- |> post("/api/v1/pleroma/chats/#{chat.id}/read", %{"last_read_id" => cm_ref.id})
- |> json_response_and_validate_schema(200)
-
- assert result["unread"] == 1
-
- cm_ref = MessageReference.for_chat_and_object(chat, object)
-
- assert cm_ref.unread == false
- end
- end
-
- describe "POST /api/v1/pleroma/chats/:id/messages" do
- setup do: oauth_access(["write:chats"])
-
- test "it posts a message to the chat", %{conn: conn, user: user} do
- other_user = insert(:user)
-
- {:ok, chat} = Chat.get_or_create(user.id, other_user.ap_id)
-
- result =
- conn
- |> put_req_header("content-type", "application/json")
- |> put_req_header("idempotency-key", "123")
- |> post("/api/v1/pleroma/chats/#{chat.id}/messages", %{"content" => "Hallo!!"})
- |> json_response_and_validate_schema(200)
-
- assert result["content"] == "Hallo!!"
- assert result["chat_id"] == chat.id |> to_string()
- assert result["idempotency_key"] == "123"
- end
-
- test "it fails if there is no content", %{conn: conn, user: user} do
- other_user = insert(:user)
-
- {:ok, chat} = Chat.get_or_create(user.id, other_user.ap_id)
-
- result =
- conn
- |> put_req_header("content-type", "application/json")
- |> post("/api/v1/pleroma/chats/#{chat.id}/messages")
- |> json_response_and_validate_schema(400)
-
- assert %{"error" => "no_content"} == result
- end
-
- test "it works with an attachment", %{conn: conn, user: user} do
- clear_config([Pleroma.Upload, :uploader], Pleroma.Uploaders.Local)
- clear_config([Pleroma.Uploaders.Local, :uploads], "uploads")
-
- file = %Plug.Upload{
- content_type: "image/jpeg",
- path: Path.absname("test/fixtures/image.jpg"),
- filename: "an_image.jpg"
- }
-
- {:ok, upload} = ActivityPub.upload(file, actor: user.ap_id)
-
- other_user = insert(:user)
-
- {:ok, chat} = Chat.get_or_create(user.id, other_user.ap_id)
-
- result =
- conn
- |> put_req_header("content-type", "application/json")
- |> post("/api/v1/pleroma/chats/#{chat.id}/messages", %{
- "media_id" => to_string(upload.id)
- })
- |> json_response_and_validate_schema(200)
-
- assert result["attachment"]
- end
-
- test "gets MRF reason when rejected", %{conn: conn, user: user} do
- clear_config([:mrf_keyword, :reject], ["GNO"])
- clear_config([:mrf, :policies], [Pleroma.Web.ActivityPub.MRF.KeywordPolicy])
-
- other_user = insert(:user)
-
- {:ok, chat} = Chat.get_or_create(user.id, other_user.ap_id)
-
- result =
- conn
- |> put_req_header("content-type", "application/json")
- |> post("/api/v1/pleroma/chats/#{chat.id}/messages", %{"content" => "GNO/Linux"})
- |> json_response_and_validate_schema(422)
-
- assert %{"error" => "[KeywordPolicy] Matches with rejected keyword"} == result
- end
- end
-
- describe "DELETE /api/v1/pleroma/chats/:id/messages/:message_id" do
- setup do: oauth_access(["write:chats"])
-
- test "it deletes a message from the chat", %{conn: conn, user: user} do
- recipient = insert(:user)
-
- {:ok, message} =
- CommonAPI.post_chat_message(user, recipient, "Hello darkness my old friend")
-
- {:ok, other_message} = CommonAPI.post_chat_message(recipient, user, "nico nico ni")
-
- object = Object.normalize(message, fetch: false)
-
- chat = Chat.get(user.id, recipient.ap_id)
-
- cm_ref = MessageReference.for_chat_and_object(chat, object)
-
- # Deleting your own message removes the message and the reference
- result =
- conn
- |> put_req_header("content-type", "application/json")
- |> delete("/api/v1/pleroma/chats/#{chat.id}/messages/#{cm_ref.id}")
- |> json_response_and_validate_schema(200)
-
- assert result["id"] == cm_ref.id
- refute MessageReference.get_by_id(cm_ref.id)
- assert %{data: %{"type" => "Tombstone"}} = Object.get_by_id(object.id)
-
- # Deleting other people's messages just removes the reference
- object = Object.normalize(other_message, fetch: false)
- cm_ref = MessageReference.for_chat_and_object(chat, object)
-
- result =
- conn
- |> put_req_header("content-type", "application/json")
- |> delete("/api/v1/pleroma/chats/#{chat.id}/messages/#{cm_ref.id}")
- |> json_response_and_validate_schema(200)
-
- assert result["id"] == cm_ref.id
- refute MessageReference.get_by_id(cm_ref.id)
- assert Object.get_by_id(object.id)
- end
- end
-
- describe "GET /api/v1/pleroma/chats/:id/messages" do
- setup do: oauth_access(["read:chats"])
-
- test "it paginates", %{conn: conn, user: user} do
- recipient = insert(:user)
-
- Enum.each(1..30, fn _ ->
- {:ok, _} = CommonAPI.post_chat_message(user, recipient, "hey")
- end)
-
- chat = Chat.get(user.id, recipient.ap_id)
-
- response = get(conn, "/api/v1/pleroma/chats/#{chat.id}/messages")
- result = json_response_and_validate_schema(response, 200)
-
- [next, prev] = get_resp_header(response, "link") |> hd() |> String.split(", ")
- api_endpoint = "/api/v1/pleroma/chats/"
-
- assert String.match?(
- next,
- ~r(#{api_endpoint}.*/messages\?limit=\d+&max_id=.*; rel=\"next\"$)
- )
-
- assert String.match?(
- prev,
- ~r(#{api_endpoint}.*/messages\?limit=\d+&min_id=.*; rel=\"prev\"$)
- )
-
- assert length(result) == 20
-
- response =
- get(conn, "/api/v1/pleroma/chats/#{chat.id}/messages?max_id=#{List.last(result)["id"]}")
-
- result = json_response_and_validate_schema(response, 200)
- [next, prev] = get_resp_header(response, "link") |> hd() |> String.split(", ")
-
- assert String.match?(
- next,
- ~r(#{api_endpoint}.*/messages\?limit=\d+&max_id=.*; rel=\"next\"$)
- )
-
- assert String.match?(
- prev,
- ~r(#{api_endpoint}.*/messages\?limit=\d+&max_id=.*&min_id=.*; rel=\"prev\"$)
- )
-
- assert length(result) == 10
- end
-
- test "it returns the messages for a given chat", %{conn: conn, user: user} do
- other_user = insert(:user)
- third_user = insert(:user)
-
- {:ok, _} = CommonAPI.post_chat_message(user, other_user, "hey")
- {:ok, _} = CommonAPI.post_chat_message(user, third_user, "hey")
- {:ok, _} = CommonAPI.post_chat_message(user, other_user, "how are you?")
- {:ok, _} = CommonAPI.post_chat_message(other_user, user, "fine, how about you?")
-
- chat = Chat.get(user.id, other_user.ap_id)
-
- result =
- conn
- |> get("/api/v1/pleroma/chats/#{chat.id}/messages")
- |> json_response_and_validate_schema(200)
-
- result
- |> Enum.each(fn message ->
- assert message["chat_id"] == chat.id |> to_string()
- end)
-
- assert length(result) == 3
-
- # Trying to get the chat of a different user
- other_user_chat = Chat.get(other_user.id, user.ap_id)
-
- conn
- |> get("/api/v1/pleroma/chats/#{other_user_chat.id}/messages")
- |> json_response_and_validate_schema(404)
- end
- end
-
- describe "POST /api/v1/pleroma/chats/by-account-id/:id" do
- setup do: oauth_access(["write:chats"])
-
- test "it creates or returns a chat", %{conn: conn} do
- other_user = insert(:user)
-
- result =
- conn
- |> post("/api/v1/pleroma/chats/by-account-id/#{other_user.id}")
- |> json_response_and_validate_schema(200)
-
- assert result["id"]
- end
- end
-
- describe "GET /api/v1/pleroma/chats/:id" do
- setup do: oauth_access(["read:chats"])
-
- test "it returns a chat", %{conn: conn, user: user} do
- other_user = insert(:user)
-
- {:ok, chat} = Chat.get_or_create(user.id, other_user.ap_id)
-
- result =
- conn
- |> get("/api/v1/pleroma/chats/#{chat.id}")
- |> json_response_and_validate_schema(200)
-
- assert result["id"] == to_string(chat.id)
- end
- end
-
- describe "GET /api/v2/pleroma/chats" do
- setup do: oauth_access(["read:chats"])
-
- test "it does not return chats with deleted users", %{conn: conn, user: user} do
- recipient = insert(:user)
- {:ok, _} = Chat.get_or_create(user.id, recipient.ap_id)
-
- Pleroma.Repo.delete(recipient)
- User.invalidate_cache(recipient)
-
- result =
- conn
- |> get("/api/v2/pleroma/chats")
- |> json_response_and_validate_schema(200)
-
- assert length(result) == 0
- end
-
- test "it does not return chats with users you blocked", %{conn: conn, user: user} do
- recipient = insert(:user)
-
- {:ok, _} = Chat.get_or_create(user.id, recipient.ap_id)
-
- result =
- conn
- |> get("/api/v2/pleroma/chats")
- |> json_response_and_validate_schema(200)
-
- assert length(result) == 1
-
- User.block(user, recipient)
-
- result =
- conn
- |> get("/api/v2/pleroma/chats")
- |> json_response_and_validate_schema(200)
-
- assert length(result) == 0
- end
-
- test "it does not return chats with users you muted", %{conn: conn, user: user} do
- recipient = insert(:user)
-
- {:ok, _} = Chat.get_or_create(user.id, recipient.ap_id)
-
- result =
- conn
- |> get("/api/v2/pleroma/chats")
- |> json_response_and_validate_schema(200)
-
- assert length(result) == 1
-
- User.mute(user, recipient)
-
- result =
- conn
- |> get("/api/v2/pleroma/chats")
- |> json_response_and_validate_schema(200)
-
- assert length(result) == 0
-
- result =
- conn
- |> get("/api/v2/pleroma/chats?with_muted=true")
- |> json_response_and_validate_schema(200)
-
- assert length(result) == 1
- end
-
- test "it paginates chats", %{conn: conn, user: user} do
- Enum.each(1..30, fn _ ->
- recipient = insert(:user)
- {:ok, _} = Chat.get_or_create(user.id, recipient.ap_id)
- end)
-
- result =
- conn
- |> get("/api/v2/pleroma/chats")
- |> json_response_and_validate_schema(200)
-
- assert length(result) == 20
- last_id = List.last(result)["id"]
-
- result =
- conn
- |> get("/api/v2/pleroma/chats?max_id=#{last_id}")
- |> json_response_and_validate_schema(200)
-
- assert length(result) == 10
- end
-
- test "it return a list of chats the current user is participating in, in descending order of updates",
- %{conn: conn, user: user} do
- har = insert(:user)
- jafnhar = insert(:user)
- tridi = insert(:user)
-
- {:ok, chat_1} = Chat.get_or_create(user.id, har.ap_id)
- {:ok, chat_1} = time_travel(chat_1, -3)
- {:ok, chat_2} = Chat.get_or_create(user.id, jafnhar.ap_id)
- {:ok, _chat_2} = time_travel(chat_2, -2)
- {:ok, chat_3} = Chat.get_or_create(user.id, tridi.ap_id)
- {:ok, chat_3} = time_travel(chat_3, -1)
-
- # bump the second one
- {:ok, chat_2} = Chat.bump_or_create(user.id, jafnhar.ap_id)
-
- result =
- conn
- |> get("/api/v2/pleroma/chats")
- |> json_response_and_validate_schema(200)
-
- ids = Enum.map(result, & &1["id"])
-
- assert ids == [
- chat_2.id |> to_string(),
- chat_3.id |> to_string(),
- chat_1.id |> to_string()
- ]
- end
-
- test "it is not affected by :restrict_unauthenticated setting (issue #1973)", %{
- conn: conn,
- user: user
- } do
- clear_config([:restrict_unauthenticated, :profiles, :local], true)
- clear_config([:restrict_unauthenticated, :profiles, :remote], true)
-
- user2 = insert(:user)
- user3 = insert(:user, local: false)
-
- {:ok, _chat_12} = Chat.get_or_create(user.id, user2.ap_id)
- {:ok, _chat_13} = Chat.get_or_create(user.id, user3.ap_id)
-
- result =
- conn
- |> get("/api/v2/pleroma/chats")
- |> json_response_and_validate_schema(200)
-
- account_ids = Enum.map(result, &get_in(&1, ["account", "id"]))
- assert Enum.sort(account_ids) == Enum.sort([user2.id, user3.id])
- end
- end
-end
+++ /dev/null
-# Pleroma: A lightweight social networking server
-# Copyright © 2017-2021 Pleroma Authors <https://pleroma.social/>
-# SPDX-License-Identifier: AGPL-3.0-only
-
-defmodule Pleroma.Web.PleromaAPI.ChatMessageReferenceViewTest do
- use Pleroma.DataCase
-
- alias Pleroma.Chat
- alias Pleroma.Chat.MessageReference
- alias Pleroma.Object
- alias Pleroma.Web.ActivityPub.ActivityPub
- alias Pleroma.Web.CommonAPI
- alias Pleroma.Web.PleromaAPI.Chat.MessageReferenceView
-
- import Pleroma.Factory
-
- test "it displays a chat message" do
- user = insert(:user)
- recipient = insert(:user)
-
- file = %Plug.Upload{
- content_type: "image/jpeg",
- path: Path.absname("test/fixtures/image.jpg"),
- filename: "an_image.jpg"
- }
-
- {:ok, upload} = ActivityPub.upload(file, actor: user.ap_id)
-
- {:ok, activity} =
- CommonAPI.post_chat_message(user, recipient, "kippis :firefox:", idempotency_key: "123")
-
- chat = Chat.get(user.id, recipient.ap_id)
-
- object = Object.normalize(activity, fetch: false)
-
- cm_ref = MessageReference.for_chat_and_object(chat, object)
-
- chat_message = MessageReferenceView.render("show.json", chat_message_reference: cm_ref)
-
- assert chat_message[:id] == cm_ref.id
- assert chat_message[:content] == "kippis :firefox:"
- assert chat_message[:account_id] == user.id
- assert chat_message[:chat_id]
- assert chat_message[:created_at]
- assert chat_message[:unread] == false
- assert match?([%{shortcode: "firefox"}], chat_message[:emojis])
- assert chat_message[:idempotency_key] == "123"
-
- clear_config([:rich_media, :enabled], true)
-
- Tesla.Mock.mock_global(fn
- %{url: "https://example.com/ogp"} ->
- %Tesla.Env{status: 200, body: File.read!("test/fixtures/rich_media/ogp.html")}
- end)
-
- {:ok, activity} =
- CommonAPI.post_chat_message(recipient, user, "gkgkgk https://example.com/ogp",
- media_id: upload.id
- )
-
- object = Object.normalize(activity, fetch: false)
-
- cm_ref = MessageReference.for_chat_and_object(chat, object)
-
- chat_message_two = MessageReferenceView.render("show.json", chat_message_reference: cm_ref)
-
- assert chat_message_two[:id] == cm_ref.id
- assert chat_message_two[:content] == object.data["content"]
- assert chat_message_two[:account_id] == recipient.id
- assert chat_message_two[:chat_id] == chat_message[:chat_id]
- assert chat_message_two[:attachment]
- assert chat_message_two[:unread] == true
- assert chat_message_two[:card]
- end
-end
+++ /dev/null
-# Pleroma: A lightweight social networking server
-# Copyright © 2017-2021 Pleroma Authors <https://pleroma.social/>
-# SPDX-License-Identifier: AGPL-3.0-only
-
-defmodule Pleroma.Web.PleromaAPI.ChatViewTest do
- use Pleroma.DataCase, async: true
-
- alias Pleroma.Chat
- alias Pleroma.Chat.MessageReference
- alias Pleroma.Object
- alias Pleroma.Web.CommonAPI
- alias Pleroma.Web.CommonAPI.Utils
- alias Pleroma.Web.MastodonAPI.AccountView
- alias Pleroma.Web.PleromaAPI.Chat.MessageReferenceView
- alias Pleroma.Web.PleromaAPI.ChatView
-
- import Pleroma.Factory
-
- test "it represents a chat" do
- user = insert(:user)
- recipient = insert(:user)
-
- {:ok, chat} = Chat.get_or_create(user.id, recipient.ap_id)
-
- represented_chat = ChatView.render("show.json", chat: chat)
-
- assert represented_chat == %{
- id: "#{chat.id}",
- account:
- AccountView.render("show.json", user: recipient, skip_visibility_check: true),
- unread: 0,
- last_message: nil,
- updated_at: Utils.to_masto_date(chat.updated_at)
- }
-
- {:ok, chat_message_creation} = CommonAPI.post_chat_message(user, recipient, "hello")
-
- chat_message = Object.normalize(chat_message_creation, fetch: false)
-
- {:ok, chat} = Chat.get_or_create(user.id, recipient.ap_id)
-
- represented_chat = ChatView.render("show.json", chat: chat)
-
- cm_ref = MessageReference.for_chat_and_object(chat, chat_message)
-
- assert represented_chat[:last_message] ==
- MessageReferenceView.render("show.json", chat_message_reference: cm_ref)
- end
-end
import Pleroma.Factory
- alias Pleroma.Notification
alias Pleroma.Object
alias Pleroma.User
- alias Pleroma.Web.ActivityPub.ActivityPub
alias Pleroma.Web.CommonAPI
alias Pleroma.Web.Push.Impl
alias Pleroma.Web.Push.Subscription
end
describe "build_content/3" do
- test "builds content for chat messages" do
- user = insert(:user)
- recipient = insert(:user)
-
- {:ok, chat} = CommonAPI.post_chat_message(user, recipient, "hey")
- object = Object.normalize(chat, fetch: false)
- [notification] = Notification.for_user(recipient)
-
- res = Impl.build_content(notification, user, object)
-
- assert res == %{
- body: "@#{user.nickname}: hey",
- title: "New Chat Message"
- }
- end
-
- test "builds content for chat messages with no content" do
- user = insert(:user)
- recipient = insert(:user)
-
- file = %Plug.Upload{
- content_type: "image/jpeg",
- path: Path.absname("test/fixtures/image.jpg"),
- filename: "an_image.jpg"
- }
-
- {:ok, upload} = ActivityPub.upload(file, actor: user.ap_id)
-
- {:ok, chat} = CommonAPI.post_chat_message(user, recipient, nil, media_id: upload.id)
- object = Object.normalize(chat, fetch: false)
- [notification] = Notification.for_user(recipient)
-
- res = Impl.build_content(notification, user, object)
-
- assert res == %{
- body: "@#{user.nickname}: (Attachment)",
- title: "New Chat Message"
- }
- end
-
test "hides contents of notifications when option enabled" do
user = insert(:user, nickname: "Bob")
+++ /dev/null
-# Pleroma: A lightweight social networking server
-# Copyright © 2017-2021 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
import Pleroma.Factory
- alias Pleroma.Chat
- alias Pleroma.Chat.MessageReference
alias Pleroma.Conversation.Participation
alias Pleroma.List
- alias Pleroma.Object
alias Pleroma.User
alias Pleroma.Web.CommonAPI
alias Pleroma.Web.Streamer
- alias Pleroma.Web.StreamerView
@moduletag needs_streamer: true, capture_log: true
expected_user_topic = "user:#{user.id}"
expected_notification_topic = "user:notification:#{user.id}"
expected_direct_topic = "direct:#{user.id}"
- expected_pleroma_chat_topic = "user:pleroma_chat:#{user.id}"
for valid_user_token <- [read_oauth_token, read_statuses_token] do
assert {:ok, ^expected_user_topic} = Streamer.get_topic("user", user, valid_user_token)
assert {:ok, ^expected_direct_topic} =
Streamer.get_topic("direct", user, valid_user_token)
-
- assert {:ok, ^expected_pleroma_chat_topic} =
- Streamer.get_topic("user:pleroma_chat", user, valid_user_token)
end
for invalid_user_token <- [read_notifications_token, badly_scoped_token],
- user_topic <- ["user", "direct", "user:pleroma_chat"] do
+ user_topic <- ["user", "direct"] do
assert {:error, :unauthorized} = Streamer.get_topic(user_topic, user, invalid_user_token)
end
refute Streamer.filtered_by_user?(user, notify)
end
- test "it sends chat messages to the 'user:pleroma_chat' stream", %{
- user: user,
- token: oauth_token
- } do
- other_user = insert(:user)
-
- {:ok, create_activity} =
- CommonAPI.post_chat_message(other_user, user, "hey cirno", idempotency_key: "123")
-
- object = Object.normalize(create_activity, fetch: false)
- chat = Chat.get(user.id, other_user.ap_id)
- cm_ref = MessageReference.for_chat_and_object(chat, object)
- cm_ref = %{cm_ref | chat: chat, object: object}
-
- Streamer.get_topic_and_add_socket("user:pleroma_chat", user, oauth_token)
- Streamer.stream("user:pleroma_chat", {user, cm_ref})
-
- text = StreamerView.render("chat_update.json", %{chat_message_reference: cm_ref})
-
- assert text =~ "hey cirno"
- assert_receive {:text, ^text}
- end
-
- test "it sends chat messages to the 'user' stream", %{user: user, token: oauth_token} do
- other_user = insert(:user)
-
- {:ok, create_activity} = CommonAPI.post_chat_message(other_user, user, "hey cirno")
- object = Object.normalize(create_activity, fetch: false)
- chat = Chat.get(user.id, other_user.ap_id)
- cm_ref = MessageReference.for_chat_and_object(chat, object)
- cm_ref = %{cm_ref | chat: chat, object: object}
-
- Streamer.get_topic_and_add_socket("user", user, oauth_token)
- Streamer.stream("user", {user, cm_ref})
-
- text = StreamerView.render("chat_update.json", %{chat_message_reference: cm_ref})
-
- assert text =~ "hey cirno"
- assert_receive {:text, ^text}
- end
-
- test "it sends chat message notifications to the 'user:notification' stream", %{
- user: user,
- token: oauth_token
- } do
- other_user = insert(:user)
-
- {:ok, create_activity} = CommonAPI.post_chat_message(other_user, user, "hey")
-
- notify =
- Repo.get_by(Pleroma.Notification, user_id: user.id, activity_id: create_activity.id)
- |> Repo.preload(:activity)
-
- Streamer.get_topic_and_add_socket("user:notification", user, oauth_token)
- Streamer.stream("user:notification", notify)
-
- assert_receive {:render_with_user, _, _, ^notify}
- refute Streamer.filtered_by_user?(user, notify)
- end
-
test "it doesn't send notify to the 'user:notification' stream when a user is blocked", %{
user: user,
token: oauth_token