forked from AkkomaGang/akkoma
Merge pull request 'purge chat and shout endpoints' (#97) from purge-chat into develop
Reviewed-on: AkkomaGang/akkoma#97
This commit is contained in:
commit
d109bbf71c
91 changed files with 52 additions and 6142 deletions
|
@ -19,6 +19,7 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/).
|
||||||
- `/api/v1/search`
|
- `/api/v1/search`
|
||||||
- `/api/v1/statuses/{id}/card`
|
- `/api/v1/statuses/{id}/card`
|
||||||
- LDAP authenticator
|
- LDAP authenticator
|
||||||
|
- Chats, they were half-baked. Just use PMs.
|
||||||
|
|
||||||
## 2022.07
|
## 2022.07
|
||||||
|
|
||||||
|
|
|
@ -267,11 +267,6 @@
|
||||||
sender_nickname: nil,
|
sender_nickname: nil,
|
||||||
message: nil
|
message: nil
|
||||||
],
|
],
|
||||||
chat_message: [
|
|
||||||
enabled: false,
|
|
||||||
sender_nickname: nil,
|
|
||||||
message: nil
|
|
||||||
],
|
|
||||||
email: [
|
email: [
|
||||||
enabled: false,
|
enabled: false,
|
||||||
sender: nil,
|
sender: nil,
|
||||||
|
@ -729,7 +724,8 @@
|
||||||
"mastodon-fe" => %{
|
"mastodon-fe" => %{
|
||||||
"name" => "mastodon-fe",
|
"name" => "mastodon-fe",
|
||||||
"git" => "https://akkoma.dev/AkkomaGang/masto-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",
|
"build_dir" => "distribution",
|
||||||
"ref" => "akkoma"
|
"ref" => "akkoma"
|
||||||
},
|
},
|
||||||
|
|
|
@ -946,7 +946,7 @@
|
||||||
key: :privileged_staff,
|
key: :privileged_staff,
|
||||||
type: :boolean,
|
type: :boolean,
|
||||||
description:
|
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)"
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
|
@ -984,35 +984,6 @@
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
%{
|
|
||||||
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,
|
key: :email,
|
||||||
type: :keyword,
|
type: :keyword,
|
||||||
|
@ -2507,27 +2478,6 @@
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
%{
|
|
||||||
group: :pleroma,
|
|
||||||
key: :shout,
|
|
||||||
type: :group,
|
|
||||||
description: "Pleroma shout settings",
|
|
||||||
children: [
|
|
||||||
%{
|
|
||||||
key: :enabled,
|
|
||||||
type: :boolean,
|
|
||||||
description: "Enables the backend Shoutbox chat feature."
|
|
||||||
},
|
|
||||||
%{
|
|
||||||
key: :limit,
|
|
||||||
type: :integer,
|
|
||||||
description: "Shout message character limit.",
|
|
||||||
suggestions: [
|
|
||||||
5_000
|
|
||||||
]
|
|
||||||
}
|
|
||||||
]
|
|
||||||
},
|
|
||||||
%{
|
%{
|
||||||
group: :pleroma,
|
group: :pleroma,
|
||||||
key: :http,
|
key: :http,
|
||||||
|
|
|
@ -8,11 +8,6 @@ For from source installations Akkoma configuration works by first importing the
|
||||||
|
|
||||||
To add configuration to your config file, you can copy it from the base config. The latest version of it can be viewed [here](https://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.
|
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
|
## :instance
|
||||||
* `name`: The instance’s name.
|
* `name`: The instance’s name.
|
||||||
* `email`: Email used to reach an Administrator/Moderator of the instance.
|
* `email`: Email used to reach an Administrator/Moderator of the instance.
|
||||||
|
@ -77,10 +72,6 @@ To add configuration to your config file, you can copy it from the base config.
|
||||||
* `enabled`: Enables the send a direct message to a newly registered user. Defaults to `false`.
|
* `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.
|
* `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.
|
* `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.
|
* `email`: - welcome message sent as a email.
|
||||||
* `enabled`: Enables the send a welcome email to a newly registered user. Defaults to `false`.
|
* `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.
|
* `sender`: The email address or tuple with `{nickname, email}` that will use as sender to the welcome email.
|
||||||
|
|
|
@ -1031,7 +1031,6 @@ Most of the settings will be applied in `runtime`, this means that you don't nee
|
||||||
- `:hackney_pools`
|
- `:hackney_pools`
|
||||||
- `:connections_pool`
|
- `:connections_pool`
|
||||||
- `:pools`
|
- `:pools`
|
||||||
- `:chat`
|
|
||||||
- partially settings inside these keys:
|
- partially settings inside these keys:
|
||||||
- `:seconds_valid` in `Pleroma.Captcha`
|
- `:seconds_valid` in `Pleroma.Captcha`
|
||||||
- `:proxy_remote` in `Pleroma.Upload`
|
- `:proxy_remote` in `Pleroma.Upload`
|
||||||
|
@ -1411,127 +1410,6 @@ Loads json generated from `config/descriptions.exs`.
|
||||||
|
|
||||||
```
|
```
|
||||||
|
|
||||||
## 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 /api/v1/pleroma/admin/instance_document/:document_name`
|
||||||
|
|
||||||
### Get an instance document
|
### Get an instance document
|
||||||
|
|
|
@ -1,255 +0,0 @@
|
||||||
# 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.
|
|
|
@ -99,13 +99,11 @@ Has these additional fields under the `pleroma` object:
|
||||||
- `hide_followers_count`: boolean, true when the user has follower stat hiding enabled
|
- `hide_followers_count`: boolean, true when the user has follower stat hiding enabled
|
||||||
- `hide_follows_count`: boolean, true when the user has follow stat hiding enabled
|
- `hide_follows_count`: boolean, true when the user has follow stat hiding enabled
|
||||||
- `settings_store`: A generic map of settings for frontends. Opaque to the backend. Only returned in `/api/v1/accounts/verify_credentials` and `/api/v1/accounts/update_credentials`
|
- `settings_store`: A generic map of settings for frontends. Opaque to the backend. Only returned in `/api/v1/accounts/verify_credentials` and `/api/v1/accounts/update_credentials`
|
||||||
- `chat_token`: The token needed for Akkoma shoutbox. Only returned in `/api/v1/accounts/verify_credentials`
|
|
||||||
- `deactivated`: boolean, true when the user is deactivated
|
- `deactivated`: boolean, true when the user is deactivated
|
||||||
- `allow_following_move`: boolean, true when the user allows automatically follow moved following accounts
|
- `allow_following_move`: boolean, true when the user allows automatically follow moved following accounts
|
||||||
- `unread_conversation_count`: The count of unread conversations. Only returned to the account owner.
|
- `unread_conversation_count`: The count of unread conversations. Only returned to the account owner.
|
||||||
- `unread_notifications_count`: The count of unread notifications. 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.
|
- `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
|
- `favicon`: nullable URL string, Favicon image of the user's instance
|
||||||
|
|
||||||
### Source
|
### Source
|
||||||
|
@ -159,15 +157,6 @@ The `type` value is `pleroma:emoji_reaction`. Has these fields:
|
||||||
- `account`: The account of the user who reacted
|
- `account`: The account of the user who reacted
|
||||||
- `status`: The status that was reacted on
|
- `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)
|
### Report Notification (not default)
|
||||||
|
|
||||||
This notification has to be requested explicitly.
|
This notification has to be requested explicitly.
|
||||||
|
@ -182,7 +171,7 @@ The `type` value is `pleroma:report`
|
||||||
Accepts additional parameters:
|
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`.
|
- `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`
|
## DELETE `/api/v1/notifications/destroy_multiple`
|
||||||
|
|
||||||
|
@ -240,7 +229,6 @@ Additional parameters can be added to the JSON body/Form data:
|
||||||
- `pleroma_background_image` - sets the background image of the user. Can be set to "" (an empty string) to reset.
|
- `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).
|
- `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.
|
- `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.)
|
- `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.
|
All images (avatar, banner and background) can be reset to the default by sending an empty string ("") instead of a file.
|
||||||
|
@ -300,7 +288,6 @@ Has these additional parameters (which are the same as in Akkoma-API):
|
||||||
`GET /api/v1/instance` has additional fields
|
`GET /api/v1/instance` has additional fields
|
||||||
|
|
||||||
- `max_toot_chars`: The maximum characters per post
|
- `max_toot_chars`: The maximum characters per post
|
||||||
- `chat_limit`: The maximum characters per chat message
|
|
||||||
- `description_limit`: The maximum characters per image description
|
- `description_limit`: The maximum characters per image description
|
||||||
- `poll_limits`: The limits of polls
|
- `poll_limits`: The limits of polls
|
||||||
- `upload_limit`: The maximum upload file size
|
- `upload_limit`: The maximum upload file size
|
||||||
|
@ -321,7 +308,6 @@ Has these additional parameters (which are the same as in Akkoma-API):
|
||||||
|
|
||||||
Permits these additional alert types:
|
Permits these additional alert types:
|
||||||
|
|
||||||
- pleroma:chat_mention
|
|
||||||
- pleroma:emoji_reaction
|
- pleroma:emoji_reaction
|
||||||
|
|
||||||
## Markers
|
## Markers
|
||||||
|
@ -332,10 +318,6 @@ Has these additional fields under the `pleroma` object:
|
||||||
|
|
||||||
## Streaming
|
## 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
|
### 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`.
|
For viewing remote server timelines, there are `public:remote` and `public:remote:media` streams. Each of these accept a parameter like `?instance=lain.com`.
|
||||||
|
|
|
@ -44,11 +44,8 @@ See also [the Nodeinfo standard](https://nodeinfo.diaspora.software/).
|
||||||
"shareable_emoji_packs",
|
"shareable_emoji_packs",
|
||||||
"multifetch",
|
"multifetch",
|
||||||
"pleroma:api/v1/notifications:include_types_filter",
|
"pleroma:api/v1/notifications:include_types_filter",
|
||||||
"chat",
|
|
||||||
"shout",
|
|
||||||
"relay",
|
"relay",
|
||||||
"pleroma_emoji_reactions",
|
"pleroma_emoji_reactions"
|
||||||
"pleroma_chat_messages"
|
|
||||||
],
|
],
|
||||||
"federation":{
|
"federation":{
|
||||||
"enabled":true,
|
"enabled":true,
|
||||||
|
@ -204,11 +201,8 @@ See also [the Nodeinfo standard](https://nodeinfo.diaspora.software/).
|
||||||
"shareable_emoji_packs",
|
"shareable_emoji_packs",
|
||||||
"multifetch",
|
"multifetch",
|
||||||
"pleroma:api/v1/notifications:include_types_filter",
|
"pleroma:api/v1/notifications:include_types_filter",
|
||||||
"chat",
|
|
||||||
"shout",
|
|
||||||
"relay",
|
"relay",
|
||||||
"pleroma_emoji_reactions",
|
"pleroma_emoji_reactions"
|
||||||
"pleroma_chat_messages"
|
|
||||||
],
|
],
|
||||||
"federation":{
|
"federation":{
|
||||||
"enabled":true,
|
"enabled":true,
|
||||||
|
|
|
@ -26,40 +26,3 @@ Response: HTTP 201 Created with the object into the body, no `Location` header p
|
||||||
|
|
||||||
The object given in the reponse should then be inserted into an Object's `attachment` field.
|
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`.
|
|
||||||
|
|
|
@ -77,8 +77,7 @@ def start(_type, _args) do
|
||||||
] ++
|
] ++
|
||||||
elasticsearch_children() ++
|
elasticsearch_children() ++
|
||||||
task_children(@mix_env) ++
|
task_children(@mix_env) ++
|
||||||
dont_run_in_test(@mix_env) ++
|
dont_run_in_test(@mix_env)
|
||||||
shout_child(shout_enabled?())
|
|
||||||
|
|
||||||
# See http://elixir-lang.org/docs/stable/elixir/Supervisor.html
|
# See http://elixir-lang.org/docs/stable/elixir/Supervisor.html
|
||||||
# for other strategies and supported options
|
# for other strategies and supported options
|
||||||
|
@ -93,11 +92,16 @@ def start(_type, _args) do
|
||||||
end
|
end
|
||||||
|
|
||||||
opts = [strategy: :one_for_one, name: Pleroma.Supervisor, max_restarts: max_restarts]
|
opts = [strategy: :one_for_one, name: Pleroma.Supervisor, max_restarts: max_restarts]
|
||||||
result = Supervisor.start_link(children, opts)
|
|
||||||
|
|
||||||
set_postgres_server_version()
|
with {:ok, data} <- Supervisor.start_link(children, opts) do
|
||||||
|
set_postgres_server_version()
|
||||||
result
|
{:ok, data}
|
||||||
|
else
|
||||||
|
e ->
|
||||||
|
Logger.error("Failed to start!")
|
||||||
|
Logger.error("#{inspect(e)}")
|
||||||
|
e
|
||||||
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
defp set_postgres_server_version do
|
defp set_postgres_server_version do
|
||||||
|
@ -173,11 +177,7 @@ defp cachex_children do
|
||||||
build_cachex("web_resp", limit: 2500),
|
build_cachex("web_resp", limit: 2500),
|
||||||
build_cachex("emoji_packs", expiration: emoji_packs_expiration(), limit: 10),
|
build_cachex("emoji_packs", expiration: emoji_packs_expiration(), limit: 10),
|
||||||
build_cachex("failed_proxy_url", limit: 2500),
|
build_cachex("failed_proxy_url", limit: 2500),
|
||||||
build_cachex("banned_urls", default_ttl: :timer.hours(24 * 30), limit: 5_000),
|
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
|
|
||||||
)
|
|
||||||
]
|
]
|
||||||
end
|
end
|
||||||
|
|
||||||
|
@ -187,9 +187,6 @@ defp emoji_packs_expiration,
|
||||||
defp idempotency_expiration,
|
defp idempotency_expiration,
|
||||||
do: expiration(default: :timer.seconds(6 * 60 * 60), interval: :timer.seconds(60))
|
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,
|
defp seconds_valid_interval,
|
||||||
do: :timer.seconds(Config.get!([Pleroma.Captcha, :seconds_valid]))
|
do: :timer.seconds(Config.get!([Pleroma.Captcha, :seconds_valid]))
|
||||||
|
|
||||||
|
@ -201,8 +198,6 @@ def build_cachex(type, opts),
|
||||||
type: :worker
|
type: :worker
|
||||||
}
|
}
|
||||||
|
|
||||||
defp shout_enabled?, do: Config.get([:shout, :enabled])
|
|
||||||
|
|
||||||
defp dont_run_in_test(env) when env in [:test, :benchmark], do: []
|
defp dont_run_in_test(env) when env in [:test, :benchmark], do: []
|
||||||
|
|
||||||
defp dont_run_in_test(_) do
|
defp dont_run_in_test(_) do
|
||||||
|
@ -222,15 +217,6 @@ defp background_migrators do
|
||||||
]
|
]
|
||||||
end
|
end
|
||||||
|
|
||||||
defp shout_child(true) do
|
|
||||||
[
|
|
||||||
Pleroma.Web.ShoutChannel.ShoutChannelState,
|
|
||||||
{Phoenix.PubSub, [name: Pleroma.PubSub, adapter: Phoenix.PubSub.PG2]}
|
|
||||||
]
|
|
||||||
end
|
|
||||||
|
|
||||||
defp shout_child(_), do: []
|
|
||||||
|
|
||||||
defp task_children(:test) do
|
defp task_children(:test) do
|
||||||
[
|
[
|
||||||
%{
|
%{
|
||||||
|
|
|
@ -1,97 +0,0 @@
|
||||||
# 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
|
|
|
@ -1,117 +0,0 @@
|
||||||
# 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
|
|
|
@ -176,7 +176,6 @@ def warn do
|
||||||
check_activity_expiration_config(),
|
check_activity_expiration_config(),
|
||||||
check_remote_ip_plug_name(),
|
check_remote_ip_plug_name(),
|
||||||
check_uploders_s3_public_endpoint(),
|
check_uploders_s3_public_endpoint(),
|
||||||
check_old_chat_shoutbox(),
|
|
||||||
check_quarantined_instances_tuples(),
|
check_quarantined_instances_tuples(),
|
||||||
check_transparency_exclusions_tuples(),
|
check_transparency_exclusions_tuples(),
|
||||||
check_simple_policy_tuples()
|
check_simple_policy_tuples()
|
||||||
|
@ -308,27 +307,4 @@ def check_uploders_s3_public_endpoint do
|
||||||
:ok
|
:ok
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
@spec check_old_chat_shoutbox() :: :ok | nil
|
|
||||||
def check_old_chat_shoutbox do
|
|
||||||
instance_config = Pleroma.Config.get([:instance])
|
|
||||||
chat_config = Pleroma.Config.get([:chat]) || []
|
|
||||||
|
|
||||||
use_old_config =
|
|
||||||
Keyword.has_key?(instance_config, :chat_limit) or
|
|
||||||
Keyword.has_key?(chat_config, :enabled)
|
|
||||||
|
|
||||||
if use_old_config do
|
|
||||||
Logger.error("""
|
|
||||||
!!!DEPRECATION WARNING!!!
|
|
||||||
Your config is using the old namespace for the Shoutbox configuration. You need to convert to the new namespace. e.g.,
|
|
||||||
\n* `config :pleroma, :chat, enabled` and `config :pleroma, :instance, chat_limit` are now equal to:
|
|
||||||
\n* `config :pleroma, :shout, enabled` and `config :pleroma, :shout, limit`
|
|
||||||
""")
|
|
||||||
|
|
||||||
:error
|
|
||||||
else
|
|
||||||
:ok
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end
|
end
|
||||||
|
|
|
@ -15,7 +15,6 @@ defmodule Pleroma.Config.TransferTask do
|
||||||
|
|
||||||
defp reboot_time_keys,
|
defp reboot_time_keys,
|
||||||
do: [
|
do: [
|
||||||
{:pleroma, :shout},
|
|
||||||
{:pleroma, Oban},
|
{:pleroma, Oban},
|
||||||
{:pleroma, :rate_limit},
|
{:pleroma, :rate_limit},
|
||||||
{:pleroma, :markup},
|
{:pleroma, :markup},
|
||||||
|
|
|
@ -3,7 +3,6 @@
|
||||||
# SPDX-License-Identifier: AGPL-3.0-only
|
# SPDX-License-Identifier: AGPL-3.0-only
|
||||||
|
|
||||||
defmodule Pleroma.MigrationHelper.NotificationBackfill do
|
defmodule Pleroma.MigrationHelper.NotificationBackfill do
|
||||||
alias Pleroma.Object
|
|
||||||
alias Pleroma.Repo
|
alias Pleroma.Repo
|
||||||
alias Pleroma.User
|
alias Pleroma.User
|
||||||
|
|
||||||
|
@ -79,14 +78,5 @@ defp type_from_activity(%{data: %{"type" => type}} = activity) do
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
defp type_from_activity_object(%{data: %{"type" => "Create", "object" => %{}}}), do: "mention"
|
defp type_from_activity_object(%{data: %{"type" => "Create"}}), 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
|
|
||||||
end
|
end
|
||||||
|
|
|
@ -237,17 +237,6 @@ def insert_log(%{actor: %User{}, action: action, target: target} = attrs)
|
||||||
insert_log_entry_with_message(%ModerationLog{data: data})
|
insert_log_entry_with_message(%ModerationLog{data: data})
|
||||||
end
|
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}
|
@spec insert_log_entry_with_message(ModerationLog) :: {:ok, ModerationLog} | {:error, any}
|
||||||
defp insert_log_entry_with_message(entry) do
|
defp insert_log_entry_with_message(entry) do
|
||||||
entry.data["message"]
|
entry.data["message"]
|
||||||
|
@ -554,16 +543,6 @@ def get_log_entry_message(%ModerationLog{
|
||||||
"@#{actor_nickname} updated users: #{users_to_nicknames_string(subjects)}"
|
"@#{actor_nickname} updated users: #{users_to_nicknames_string(subjects)}"
|
||||||
end
|
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{
|
def get_log_entry_message(%ModerationLog{
|
||||||
data: %{
|
data: %{
|
||||||
"actor" => %{"nickname" => actor_nickname},
|
"actor" => %{"nickname" => actor_nickname},
|
||||||
|
|
|
@ -68,7 +68,6 @@ def unread_notifications_count(%User{id: user_id}) do
|
||||||
follow_request
|
follow_request
|
||||||
mention
|
mention
|
||||||
move
|
move
|
||||||
pleroma:chat_mention
|
|
||||||
pleroma:emoji_reaction
|
pleroma:emoji_reaction
|
||||||
pleroma:report
|
pleroma:report
|
||||||
reblog
|
reblog
|
||||||
|
@ -444,16 +443,7 @@ defp type_from_activity(%{data: %{"type" => type}} = activity) do
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
defp type_from_activity_object(%{data: %{"type" => "Create", "object" => %{}}}), do: "mention"
|
defp type_from_activity_object(%{data: %{"type" => "Create"}}), 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
|
|
||||||
|
|
||||||
# TODO move to sql, too.
|
# TODO move to sql, too.
|
||||||
def create_notification(%Activity{} = activity, %User{} = user, opts \\ []) do
|
def create_notification(%Activity{} = activity, %User{} = user, opts \\ []) do
|
||||||
|
|
|
@ -145,7 +145,6 @@ defmodule Pleroma.User do
|
||||||
field(:also_known_as, {:array, ObjectValidators.ObjectID}, default: [])
|
field(:also_known_as, {:array, ObjectValidators.ObjectID}, default: [])
|
||||||
field(:inbox, :string)
|
field(:inbox, :string)
|
||||||
field(:shared_inbox, :string)
|
field(:shared_inbox, :string)
|
||||||
field(:accepts_chat_messages, :boolean, default: nil)
|
|
||||||
field(:last_active_at, :naive_datetime)
|
field(:last_active_at, :naive_datetime)
|
||||||
field(:disclose_client, :boolean, default: true)
|
field(:disclose_client, :boolean, default: true)
|
||||||
field(:pinned_objects, :map, default: %{})
|
field(:pinned_objects, :map, default: %{})
|
||||||
|
@ -455,7 +454,6 @@ def remote_user_changeset(struct \\ %User{local: false}, params) do
|
||||||
:invisible,
|
:invisible,
|
||||||
:actor_type,
|
:actor_type,
|
||||||
:also_known_as,
|
:also_known_as,
|
||||||
:accepts_chat_messages,
|
|
||||||
:pinned_objects
|
:pinned_objects
|
||||||
]
|
]
|
||||||
)
|
)
|
||||||
|
@ -516,7 +514,6 @@ def update_changeset(struct, params \\ %{}) do
|
||||||
:pleroma_settings_store,
|
:pleroma_settings_store,
|
||||||
:is_discoverable,
|
:is_discoverable,
|
||||||
:actor_type,
|
:actor_type,
|
||||||
:accepts_chat_messages,
|
|
||||||
:disclose_client
|
:disclose_client
|
||||||
]
|
]
|
||||||
)
|
)
|
||||||
|
@ -667,7 +664,6 @@ def register_changeset(struct, params \\ %{}, opts \\ []) do
|
||||||
bio_limit = Config.get([:instance, :user_bio_length], 5000)
|
bio_limit = Config.get([:instance, :user_bio_length], 5000)
|
||||||
name_limit = Config.get([:instance, :user_name_length], 100)
|
name_limit = Config.get([:instance, :user_name_length], 100)
|
||||||
reason_limit = Config.get([:instance, :registration_reason_length], 500)
|
reason_limit = Config.get([:instance, :registration_reason_length], 500)
|
||||||
params = Map.put_new(params, :accepts_chat_messages, true)
|
|
||||||
|
|
||||||
confirmed? =
|
confirmed? =
|
||||||
if is_nil(opts[:confirmed]) do
|
if is_nil(opts[:confirmed]) do
|
||||||
|
@ -695,7 +691,6 @@ def register_changeset(struct, params \\ %{}, opts \\ []) do
|
||||||
:password,
|
:password,
|
||||||
:password_confirmation,
|
:password_confirmation,
|
||||||
:emoji,
|
:emoji,
|
||||||
:accepts_chat_messages,
|
|
||||||
:registration_reason,
|
:registration_reason,
|
||||||
:language
|
:language
|
||||||
])
|
])
|
||||||
|
@ -798,8 +793,7 @@ def post_register_action(%User{is_approved: true, is_confirmed: true} = user) do
|
||||||
{:ok, user} <- set_cache(user),
|
{:ok, user} <- set_cache(user),
|
||||||
{:ok, _} <- maybe_send_registration_email(user),
|
{:ok, _} <- maybe_send_registration_email(user),
|
||||||
{:ok, _} <- maybe_send_welcome_email(user),
|
{:ok, _} <- maybe_send_welcome_email(user),
|
||||||
{:ok, _} <- maybe_send_welcome_message(user),
|
{:ok, _} <- maybe_send_welcome_message(user) do
|
||||||
{:ok, _} <- maybe_send_welcome_chat_message(user) do
|
|
||||||
{:ok, user}
|
{:ok, user}
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
@ -833,15 +827,6 @@ defp maybe_send_welcome_message(user) do
|
||||||
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
|
defp maybe_send_welcome_email(%User{email: email} = user) when is_binary(email) do
|
||||||
if User.WelcomeEmail.enabled?() do
|
if User.WelcomeEmail.enabled?() do
|
||||||
User.WelcomeEmail.send_email(user)
|
User.WelcomeEmail.send_email(user)
|
||||||
|
|
|
@ -1,45 +0,0 @@
|
||||||
# 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
|
|
|
@ -97,7 +97,7 @@ defp increase_replies_count_if_reply(%{
|
||||||
|
|
||||||
defp increase_replies_count_if_reply(_create_data), do: :noop
|
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
|
@impl true
|
||||||
def persist(%{"type" => type} = object, meta) when type in @object_types do
|
def persist(%{"type" => type} = object, meta) when type in @object_types do
|
||||||
with {:ok, object} <- Object.create(object) do
|
with {:ok, object} <- Object.create(object) do
|
||||||
|
@ -1188,18 +1188,6 @@ defp exclude_poll_votes(query, _) do
|
||||||
end
|
end
|
||||||
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, %{invisible_actors: true}), do: query
|
||||||
|
|
||||||
defp exclude_invisible_actors(query, _opts) do
|
defp exclude_invisible_actors(query, _opts) do
|
||||||
|
@ -1340,7 +1328,6 @@ def fetch_activities_query(recipients, opts \\ %{}) do
|
||||||
|> restrict_filtered(opts)
|
|> restrict_filtered(opts)
|
||||||
|> Activity.restrict_deactivated_users()
|
|> Activity.restrict_deactivated_users()
|
||||||
|> exclude_poll_votes(opts)
|
|> exclude_poll_votes(opts)
|
||||||
|> exclude_chat_messages(opts)
|
|
||||||
|> exclude_invisible_actors(opts)
|
|> exclude_invisible_actors(opts)
|
||||||
|> exclude_visibility(opts)
|
|> exclude_visibility(opts)
|
||||||
|
|
||||||
|
@ -1462,8 +1449,6 @@ defp object_to_user_data(data) do
|
||||||
end)
|
end)
|
||||||
|
|
||||||
is_locked = data["manuallyApprovesFollowers"] || false
|
is_locked = data["manuallyApprovesFollowers"] || false
|
||||||
capabilities = data["capabilities"] || %{}
|
|
||||||
accepts_chat_messages = capabilities["acceptsChatMessages"]
|
|
||||||
data = Transmogrifier.maybe_fix_user_object(data)
|
data = Transmogrifier.maybe_fix_user_object(data)
|
||||||
is_discoverable = data["discoverable"] || false
|
is_discoverable = data["discoverable"] || false
|
||||||
invisible = data["invisible"] || false
|
invisible = data["invisible"] || false
|
||||||
|
@ -1507,7 +1492,6 @@ defp object_to_user_data(data) do
|
||||||
public_key: public_key,
|
public_key: public_key,
|
||||||
inbox: data["inbox"],
|
inbox: data["inbox"],
|
||||||
shared_inbox: shared_inbox,
|
shared_inbox: shared_inbox,
|
||||||
accepts_chat_messages: accepts_chat_messages,
|
|
||||||
pinned_objects: pinned_objects
|
pinned_objects: pinned_objects
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -183,30 +183,6 @@ defp add_in_reply_to(object, in_reply_to) do
|
||||||
end
|
end
|
||||||
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
|
def answer(user, object, name) do
|
||||||
{:ok,
|
{:ok,
|
||||||
%{
|
%{
|
||||||
|
|
|
@ -23,8 +23,6 @@ defmodule Pleroma.Web.ActivityPub.ObjectValidator do
|
||||||
alias Pleroma.Web.ActivityPub.ObjectValidators.ArticleNotePageValidator
|
alias Pleroma.Web.ActivityPub.ObjectValidators.ArticleNotePageValidator
|
||||||
alias Pleroma.Web.ActivityPub.ObjectValidators.AudioVideoValidator
|
alias Pleroma.Web.ActivityPub.ObjectValidators.AudioVideoValidator
|
||||||
alias Pleroma.Web.ActivityPub.ObjectValidators.BlockValidator
|
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.CreateGenericValidator
|
||||||
alias Pleroma.Web.ActivityPub.ObjectValidators.DeleteValidator
|
alias Pleroma.Web.ActivityPub.ObjectValidators.DeleteValidator
|
||||||
alias Pleroma.Web.ActivityPub.ObjectValidators.EmojiReactValidator
|
alias Pleroma.Web.ActivityPub.ObjectValidators.EmojiReactValidator
|
||||||
|
@ -83,21 +81,6 @@ def validate(%{"type" => "Delete"} = object, meta) do
|
||||||
end
|
end
|
||||||
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(
|
def validate(
|
||||||
%{"type" => "Create", "object" => %{"type" => objtype} = object} = create_activity,
|
%{"type" => "Create", "object" => %{"type" => objtype} = object} = create_activity,
|
||||||
meta
|
meta
|
||||||
|
@ -143,7 +126,7 @@ def validate(%{"type" => type} = object, meta)
|
||||||
|
|
||||||
def validate(%{"type" => type} = object, meta)
|
def validate(%{"type" => type} = object, meta)
|
||||||
when type in ~w[Accept Reject Follow Update Like EmojiReact Announce
|
when type in ~w[Accept Reject Follow Update Like EmojiReact Announce
|
||||||
ChatMessage Answer] do
|
Answer] do
|
||||||
validator =
|
validator =
|
||||||
case type do
|
case type do
|
||||||
"Accept" -> AcceptRejectValidator
|
"Accept" -> AcceptRejectValidator
|
||||||
|
@ -153,7 +136,6 @@ def validate(%{"type" => type} = object, meta)
|
||||||
"Like" -> LikeValidator
|
"Like" -> LikeValidator
|
||||||
"EmojiReact" -> EmojiReactValidator
|
"EmojiReact" -> EmojiReactValidator
|
||||||
"Announce" -> AnnounceValidator
|
"Announce" -> AnnounceValidator
|
||||||
"ChatMessage" -> ChatMessageValidator
|
|
||||||
"Answer" -> AnswerValidator
|
"Answer" -> AnswerValidator
|
||||||
end
|
end
|
||||||
|
|
||||||
|
@ -178,10 +160,6 @@ def validate(%{"type" => type} = object, meta) when type in ~w(Add Remove) do
|
||||||
|
|
||||||
def validate(o, m), do: {:error, {:validator_not_set, {o, m}}}
|
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
|
def cast_and_apply(%{"type" => "Question"} = object) do
|
||||||
QuestionValidator.cast_and_apply(object)
|
QuestionValidator.cast_and_apply(object)
|
||||||
end
|
end
|
||||||
|
|
|
@ -1,129 +0,0 @@
|
||||||
# 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
|
|
|
@ -7,7 +7,7 @@ defmodule Pleroma.Web.ActivityPub.ObjectValidators.CommonFields do
|
||||||
alias Pleroma.Web.ActivityPub.ObjectValidators.AttachmentValidator
|
alias Pleroma.Web.ActivityPub.ObjectValidators.AttachmentValidator
|
||||||
alias Pleroma.Web.ActivityPub.ObjectValidators.TagValidator
|
alias Pleroma.Web.ActivityPub.ObjectValidators.TagValidator
|
||||||
|
|
||||||
# Activities and Objects, except (Create)ChatMessage
|
# Activities and Objects
|
||||||
defmacro message_fields do
|
defmacro message_fields do
|
||||||
quote bind_quoted: binding() do
|
quote bind_quoted: binding() do
|
||||||
field(:type, :string)
|
field(:type, :string)
|
||||||
|
@ -38,7 +38,7 @@ defmacro object_fields do
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
# Basically objects that aren't ChatMessage and Answer
|
# Basically objects that aren't Answer
|
||||||
defmacro status_object_fields do
|
defmacro status_object_fields do
|
||||||
quote bind_quoted: binding() do
|
quote bind_quoted: binding() do
|
||||||
# TODO: Remove actor on objects
|
# TODO: Remove actor on objects
|
||||||
|
|
|
@ -1,96 +0,0 @@
|
||||||
# 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
|
|
|
@ -48,7 +48,6 @@ def add_deleted_activity_id(cng) do
|
||||||
Answer
|
Answer
|
||||||
Article
|
Article
|
||||||
Audio
|
Audio
|
||||||
ChatMessage
|
|
||||||
Event
|
Event
|
||||||
Note
|
Note
|
||||||
Page
|
Page
|
||||||
|
|
|
@ -10,8 +10,6 @@ defmodule Pleroma.Web.ActivityPub.SideEffects do
|
||||||
collection, and so on.
|
collection, and so on.
|
||||||
"""
|
"""
|
||||||
alias Pleroma.Activity
|
alias Pleroma.Activity
|
||||||
alias Pleroma.Chat
|
|
||||||
alias Pleroma.Chat.MessageReference
|
|
||||||
alias Pleroma.FollowingRelationship
|
alias Pleroma.FollowingRelationship
|
||||||
alias Pleroma.Notification
|
alias Pleroma.Notification
|
||||||
alias Pleroma.Object
|
alias Pleroma.Object
|
||||||
|
@ -27,7 +25,6 @@ defmodule Pleroma.Web.ActivityPub.SideEffects do
|
||||||
|
|
||||||
require Logger
|
require Logger
|
||||||
|
|
||||||
@cachex Pleroma.Config.get([:cachex, :provider], Cachex)
|
|
||||||
@logger Pleroma.Config.get([:side_effects, :logger], Logger)
|
@logger Pleroma.Config.get([:side_effects, :logger], Logger)
|
||||||
|
|
||||||
@behaviour Pleroma.Web.ActivityPub.SideEffects.Handling
|
@behaviour Pleroma.Web.ActivityPub.SideEffects.Handling
|
||||||
|
@ -306,8 +303,6 @@ def handle(%{data: %{"type" => "Delete", "object" => deleted_object}} = object,
|
||||||
Object.decrease_replies_count(in_reply_to)
|
Object.decrease_replies_count(in_reply_to)
|
||||||
end
|
end
|
||||||
|
|
||||||
MessageReference.delete_for_object(deleted_object)
|
|
||||||
|
|
||||||
ap_streamer().stream_out(object)
|
ap_streamer().stream_out(object)
|
||||||
ap_streamer().stream_out_participations(deleted_object, user)
|
ap_streamer().stream_out_participations(deleted_object, user)
|
||||||
:ok
|
:ok
|
||||||
|
@ -400,41 +395,6 @@ def handle(object, meta) do
|
||||||
{:ok, object, meta}
|
{:ok, object, meta}
|
||||||
end
|
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
|
def handle_object_creation(%{"type" => "Question"} = object, activity, meta) do
|
||||||
with {:ok, object, meta} <- Pipeline.common_pipeline(object, meta) do
|
with {:ok, object, meta} <- Pipeline.common_pipeline(object, meta) do
|
||||||
PollWorker.schedule_poll_end(activity)
|
PollWorker.schedule_poll_end(activity)
|
||||||
|
@ -533,13 +493,6 @@ defp send_streamables(meta) do
|
||||||
meta
|
meta
|
||||||
end
|
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
|
defp add_notifications(meta, notifications) do
|
||||||
existing = Keyword.get(meta, :notifications, [])
|
existing = Keyword.get(meta, :notifications, [])
|
||||||
|
|
||||||
|
|
|
@ -416,7 +416,7 @@ def handle_incoming(
|
||||||
%{"type" => "Create", "object" => %{"type" => objtype, "id" => obj_id}} = data,
|
%{"type" => "Create", "object" => %{"type" => objtype, "id" => obj_id}} = data,
|
||||||
options
|
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)
|
fetch_options = Keyword.put(options, :depth, (options[:depth] || 0) + 1)
|
||||||
|
|
||||||
object =
|
object =
|
||||||
|
@ -846,9 +846,6 @@ def add_attributed_to(object) do
|
||||||
Map.put(object, "attributedTo", attributed_to)
|
Map.put(object, "attributedTo", attributed_to)
|
||||||
end
|
end
|
||||||
|
|
||||||
# TODO: Revisit this
|
|
||||||
def prepare_attachments(%{"type" => "ChatMessage"} = object), do: object
|
|
||||||
|
|
||||||
def prepare_attachments(object) do
|
def prepare_attachments(object) do
|
||||||
attachments =
|
attachments =
|
||||||
object
|
object
|
||||||
|
|
|
@ -83,14 +83,7 @@ def render("user.json", %{user: user}) do
|
||||||
|
|
||||||
fields = Enum.map(user.fields, &Map.put(&1, "type", "PropertyValue"))
|
fields = Enum.map(user.fields, &Map.put(&1, "type", "PropertyValue"))
|
||||||
|
|
||||||
capabilities =
|
capabilities = %{}
|
||||||
if is_boolean(user.accepts_chat_messages) do
|
|
||||||
%{
|
|
||||||
"acceptsChatMessages" => user.accepts_chat_messages
|
|
||||||
}
|
|
||||||
else
|
|
||||||
%{}
|
|
||||||
end
|
|
||||||
|
|
||||||
%{
|
%{
|
||||||
"id" => user.ap_id,
|
"id" => user.ap_id,
|
||||||
|
|
|
@ -52,12 +52,6 @@ defmodule Pleroma.Web.AdminAPI.AdminAPIController do
|
||||||
when action in [:list_user_statuses]
|
when action in [:list_user_statuses]
|
||||||
)
|
)
|
||||||
|
|
||||||
plug(
|
|
||||||
OAuthScopesPlug,
|
|
||||||
%{scopes: ["admin:read:chats"]}
|
|
||||||
when action in [:list_user_chats]
|
|
||||||
)
|
|
||||||
|
|
||||||
plug(
|
plug(
|
||||||
OAuthScopesPlug,
|
OAuthScopesPlug,
|
||||||
%{scopes: ["admin:read"]}
|
%{scopes: ["admin:read"]}
|
||||||
|
@ -106,20 +100,6 @@ def list_user_statuses(%{assigns: %{user: admin}} = conn, %{"nickname" => nickna
|
||||||
end
|
end
|
||||||
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
|
def tag_users(%{assigns: %{user: admin}} = conn, %{"nicknames" => nicknames, "tags" => tags}) do
|
||||||
with {:ok, _} <- User.tag(nicknames, tags) do
|
with {:ok, _} <- User.tag(nicknames, tags) do
|
||||||
ModerationLog.insert_log(%{
|
ModerationLog.insert_log(%{
|
||||||
|
|
|
@ -1,85 +0,0 @@
|
||||||
# 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
|
|
|
@ -1,30 +0,0 @@
|
||||||
# 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
|
|
|
@ -84,7 +84,6 @@ def spec(opts \\ []) do
|
||||||
%{
|
%{
|
||||||
"name" => "Administration",
|
"name" => "Administration",
|
||||||
"tags" => [
|
"tags" => [
|
||||||
"Chat administration",
|
|
||||||
"Emoji pack administration",
|
"Emoji pack administration",
|
||||||
"Frontend managment",
|
"Frontend managment",
|
||||||
"Instance configuration",
|
"Instance configuration",
|
||||||
|
@ -114,7 +113,6 @@ def spec(opts \\ []) do
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
%{"name" => "Instance", "tags" => ["Custom emojis"]},
|
%{"name" => "Instance", "tags" => ["Custom emojis"]},
|
||||||
%{"name" => "Messaging", "tags" => ["Chats", "Conversations"]},
|
|
||||||
%{
|
%{
|
||||||
"name" => "Statuses",
|
"name" => "Statuses",
|
||||||
"tags" => [
|
"tags" => [
|
||||||
|
|
|
@ -595,11 +595,6 @@ defp update_credentials_request do
|
||||||
nullable: true,
|
nullable: true,
|
||||||
description: "Whether manual approval of follow requests is required."
|
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{
|
fields_attributes: %Schema{
|
||||||
nullable: true,
|
nullable: true,
|
||||||
oneOf: [
|
oneOf: [
|
||||||
|
|
|
@ -1,96 +0,0 @@
|
||||||
# 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
|
|
|
@ -1,361 +0,0 @@
|
||||||
# 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
|
|
|
@ -174,7 +174,6 @@ defp notification_type do
|
||||||
"reblog",
|
"reblog",
|
||||||
"mention",
|
"mention",
|
||||||
"pleroma:emoji_reaction",
|
"pleroma:emoji_reaction",
|
||||||
"pleroma:chat_mention",
|
|
||||||
"pleroma:report",
|
"pleroma:report",
|
||||||
"move",
|
"move",
|
||||||
"follow_request",
|
"follow_request",
|
||||||
|
@ -190,7 +189,6 @@ defp notification_type do
|
||||||
- `poll` - A poll you have voted in or created has ended
|
- `poll` - A poll you have voted in or created has ended
|
||||||
- `move` - Someone moved their account
|
- `move` - Someone moved their account
|
||||||
- `pleroma:emoji_reaction` - Someone reacted with emoji to your status
|
- `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
|
- `pleroma:report` - Someone was reported
|
||||||
"""
|
"""
|
||||||
}
|
}
|
||||||
|
|
|
@ -142,11 +142,6 @@ defp create_request do
|
||||||
nullable: true,
|
nullable: true,
|
||||||
description: "Receive poll notifications?"
|
description: "Receive poll notifications?"
|
||||||
},
|
},
|
||||||
"pleroma:chat_mention": %Schema{
|
|
||||||
allOf: [BooleanLike],
|
|
||||||
nullable: true,
|
|
||||||
description: "Receive chat notifications?"
|
|
||||||
},
|
|
||||||
"pleroma:emoji_reaction": %Schema{
|
"pleroma:emoji_reaction": %Schema{
|
||||||
allOf: [BooleanLike],
|
allOf: [BooleanLike],
|
||||||
nullable: true,
|
nullable: true,
|
||||||
|
@ -216,11 +211,6 @@ defp update_request do
|
||||||
nullable: true,
|
nullable: true,
|
||||||
description: "Receive poll notifications?"
|
description: "Receive poll notifications?"
|
||||||
},
|
},
|
||||||
"pleroma:chat_mention": %Schema{
|
|
||||||
allOf: [BooleanLike],
|
|
||||||
nullable: true,
|
|
||||||
description: "Receive chat notifications?"
|
|
||||||
},
|
|
||||||
"pleroma:emoji_reaction": %Schema{
|
"pleroma:emoji_reaction": %Schema{
|
||||||
allOf: [BooleanLike],
|
allOf: [BooleanLike],
|
||||||
nullable: true,
|
nullable: true,
|
||||||
|
|
|
@ -43,7 +43,7 @@ def direct_operation do
|
||||||
tags: ["Timelines"],
|
tags: ["Timelines"],
|
||||||
summary: "Direct timeline",
|
summary: "Direct timeline",
|
||||||
description:
|
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()],
|
parameters: [with_muted_param() | pagination_params()],
|
||||||
security: [%{"oAuth" => ["read:statuses"]}],
|
security: [%{"oAuth" => ["read:statuses"]}],
|
||||||
operationId: "TimelineController.direct",
|
operationId: "TimelineController.direct",
|
||||||
|
|
|
@ -47,7 +47,6 @@ defmodule Pleroma.Web.ApiSpec.Schemas.Account do
|
||||||
description: "whether the user allows automatically follow moved following accounts"
|
description: "whether the user allows automatically follow moved following accounts"
|
||||||
},
|
},
|
||||||
background_image: %Schema{type: :string, nullable: true, format: :uri},
|
background_image: %Schema{type: :string, nullable: true, format: :uri},
|
||||||
chat_token: %Schema{type: :string},
|
|
||||||
is_confirmed: %Schema{
|
is_confirmed: %Schema{
|
||||||
type: :boolean,
|
type: :boolean,
|
||||||
description:
|
description:
|
||||||
|
@ -102,7 +101,6 @@ defmodule Pleroma.Web.ApiSpec.Schemas.Account do
|
||||||
description:
|
description:
|
||||||
"A generic map of settings for frontends. Opaque to the backend. Only returned in `verify_credentials` and `update_credentials`"
|
"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{
|
favicon: %Schema{
|
||||||
type: :string,
|
type: :string,
|
||||||
format: :uri,
|
format: :uri,
|
||||||
|
@ -175,9 +173,6 @@ defmodule Pleroma.Web.ApiSpec.Schemas.Account do
|
||||||
"is_admin" => false,
|
"is_admin" => false,
|
||||||
"is_moderator" => false,
|
"is_moderator" => false,
|
||||||
"skip_thread_containment" => false,
|
"skip_thread_containment" => false,
|
||||||
"accepts_chat_messages" => true,
|
|
||||||
"chat_token" =>
|
|
||||||
"SFMyNTY.g3QAAAACZAAEZGF0YW0AAAASOXRLaTNlc2JHN09RZ1oyOTIwZAAGc2lnbmVkbgYARNplS3EB.Mb_Iaqew2bN1I1o79B_iP7encmVCpTKC4OtHZRxdjKc",
|
|
||||||
"unread_conversation_count" => 0,
|
"unread_conversation_count" => 0,
|
||||||
"tags" => [],
|
"tags" => [],
|
||||||
"notification_settings" => %{
|
"notification_settings" => %{
|
||||||
|
|
|
@ -1,75 +0,0 @@
|
||||||
# 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
|
|
|
@ -1,77 +0,0 @@
|
||||||
# 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
|
|
|
@ -1,45 +0,0 @@
|
||||||
# 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
|
|
|
@ -5,7 +5,6 @@
|
||||||
defmodule Pleroma.Web.CommonAPI do
|
defmodule Pleroma.Web.CommonAPI do
|
||||||
alias Pleroma.Activity
|
alias Pleroma.Activity
|
||||||
alias Pleroma.Conversation.Participation
|
alias Pleroma.Conversation.Participation
|
||||||
alias Pleroma.Formatter
|
|
||||||
alias Pleroma.Object
|
alias Pleroma.Object
|
||||||
alias Pleroma.ThreadMute
|
alias Pleroma.ThreadMute
|
||||||
alias Pleroma.User
|
alias Pleroma.User
|
||||||
|
@ -30,57 +29,6 @@ def block(blocker, blocked) do
|
||||||
end
|
end
|
||||||
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
|
def unblock(blocker, blocked) do
|
||||||
with {_, %Activity{} = block} <- {:fetch_block, Utils.fetch_latest_block(blocker, blocked)},
|
with {_, %Activity{} = block} <- {:fetch_block, Utils.fetch_latest_block(blocker, blocked)},
|
||||||
{:ok, unblock_data, _} <- Builder.undo(blocker, block),
|
{:ok, unblock_data, _} <- Builder.undo(blocker, block),
|
||||||
|
|
|
@ -9,7 +9,6 @@ defmodule Pleroma.Web.Endpoint do
|
||||||
|
|
||||||
alias Pleroma.Config
|
alias Pleroma.Config
|
||||||
|
|
||||||
socket("/socket", Pleroma.Web.UserSocket)
|
|
||||||
socket("/live", Phoenix.LiveView.Socket)
|
socket("/live", Phoenix.LiveView.Socket)
|
||||||
|
|
||||||
plug(Pleroma.Web.Plugs.SetLocalePlug)
|
plug(Pleroma.Web.Plugs.SetLocalePlug)
|
||||||
|
|
|
@ -153,13 +153,10 @@ defp validate_email_param(_) do
|
||||||
|
|
||||||
@doc "GET /api/v1/accounts/verify_credentials"
|
@doc "GET /api/v1/accounts/verify_credentials"
|
||||||
def verify_credentials(%{assigns: %{user: user}} = conn, _) do
|
def verify_credentials(%{assigns: %{user: user}} = conn, _) do
|
||||||
chat_token = Phoenix.Token.sign(conn, "user socket", user.id)
|
|
||||||
|
|
||||||
render(conn, "show.json",
|
render(conn, "show.json",
|
||||||
user: user,
|
user: user,
|
||||||
for: user,
|
for: user,
|
||||||
with_pleroma_settings: true,
|
with_pleroma_settings: true
|
||||||
with_chat_token: chat_token
|
|
||||||
)
|
)
|
||||||
end
|
end
|
||||||
|
|
||||||
|
@ -188,8 +185,7 @@ def update_credentials(%{assigns: %{user: user}, body_params: params} = conn, _p
|
||||||
:show_role,
|
:show_role,
|
||||||
:skip_thread_containment,
|
:skip_thread_containment,
|
||||||
:allow_following_move,
|
:allow_following_move,
|
||||||
:also_known_as,
|
:also_known_as
|
||||||
:accepts_chat_messages
|
|
||||||
]
|
]
|
||||||
|> Enum.reduce(%{}, fn key, acc ->
|
|> Enum.reduce(%{}, fn key, acc ->
|
||||||
Maps.put_if_present(acc, key, params[key], &{:ok, Params.truthy_param?(&1)})
|
Maps.put_if_present(acc, key, params[key], &{:ok, Params.truthy_param?(&1)})
|
||||||
|
|
|
@ -289,7 +289,6 @@ defp do_render("show.json", %{user: user} = opts) do
|
||||||
relationship: relationship,
|
relationship: relationship,
|
||||||
skip_thread_containment: user.skip_thread_containment,
|
skip_thread_containment: user.skip_thread_containment,
|
||||||
background_image: image_url(user.background) |> MediaProxy.url(),
|
background_image: image_url(user.background) |> MediaProxy.url(),
|
||||||
accepts_chat_messages: user.accepts_chat_messages,
|
|
||||||
favicon: favicon
|
favicon: favicon
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -297,7 +296,6 @@ defp do_render("show.json", %{user: user} = opts) do
|
||||||
|> maybe_put_settings(user, opts[:for], opts)
|
|> maybe_put_settings(user, opts[:for], opts)
|
||||||
|> maybe_put_notification_settings(user, opts[:for])
|
|> maybe_put_notification_settings(user, opts[:for])
|
||||||
|> maybe_put_settings_store(user, opts[:for], opts)
|
|> maybe_put_settings_store(user, opts[:for], opts)
|
||||||
|> maybe_put_chat_token(user, opts[:for], opts)
|
|
||||||
|> maybe_put_activation_status(user, opts[:for])
|
|> maybe_put_activation_status(user, opts[:for])
|
||||||
|> maybe_put_follow_requests_count(user, opts[:for])
|
|> maybe_put_follow_requests_count(user, opts[:for])
|
||||||
|> maybe_put_allow_following_move(user, opts[:for])
|
|> maybe_put_allow_following_move(user, opts[:for])
|
||||||
|
@ -350,15 +348,6 @@ defp maybe_put_settings_store(data, %User{} = user, %User{}, %{
|
||||||
|
|
||||||
defp maybe_put_settings_store(data, _, _, _), do: data
|
defp maybe_put_settings_store(data, _, _, _), do: data
|
||||||
|
|
||||||
defp maybe_put_chat_token(data, %User{id: id}, %User{id: id}, %{
|
|
||||||
with_chat_token: token
|
|
||||||
}) do
|
|
||||||
data
|
|
||||||
|> Kernel.put_in([:pleroma, :chat_token], token)
|
|
||||||
end
|
|
||||||
|
|
||||||
defp maybe_put_chat_token(data, _, _, _), do: data
|
|
||||||
|
|
||||||
defp maybe_put_role(data, %User{show_role: true} = user, _) do
|
defp maybe_put_role(data, %User{show_role: true} = user, _) do
|
||||||
data
|
data
|
||||||
|> Kernel.put_in([:pleroma, :is_admin], user.is_admin)
|
|> Kernel.put_in([:pleroma, :is_admin], user.is_admin)
|
||||||
|
|
|
@ -37,7 +37,6 @@ def render("show.json", _) do
|
||||||
background_upload_limit: Keyword.get(instance, :background_upload_limit),
|
background_upload_limit: Keyword.get(instance, :background_upload_limit),
|
||||||
banner_upload_limit: Keyword.get(instance, :banner_upload_limit),
|
banner_upload_limit: Keyword.get(instance, :banner_upload_limit),
|
||||||
background_image: Pleroma.Web.Endpoint.url() <> Keyword.get(instance, :background_image),
|
background_image: Pleroma.Web.Endpoint.url() <> Keyword.get(instance, :background_image),
|
||||||
shout_limit: Config.get([:shout, :limit]),
|
|
||||||
description_limit: Keyword.get(instance, :description_limit),
|
description_limit: Keyword.get(instance, :description_limit),
|
||||||
pleroma: %{
|
pleroma: %{
|
||||||
metadata: %{
|
metadata: %{
|
||||||
|
|
|
@ -6,9 +6,7 @@ defmodule Pleroma.Web.MastodonAPI.NotificationView do
|
||||||
use Pleroma.Web, :view
|
use Pleroma.Web, :view
|
||||||
|
|
||||||
alias Pleroma.Activity
|
alias Pleroma.Activity
|
||||||
alias Pleroma.Chat.MessageReference
|
|
||||||
alias Pleroma.Notification
|
alias Pleroma.Notification
|
||||||
alias Pleroma.Object
|
|
||||||
alias Pleroma.User
|
alias Pleroma.User
|
||||||
alias Pleroma.UserRelationship
|
alias Pleroma.UserRelationship
|
||||||
alias Pleroma.Web.AdminAPI.Report
|
alias Pleroma.Web.AdminAPI.Report
|
||||||
|
@ -18,7 +16,6 @@ defmodule Pleroma.Web.MastodonAPI.NotificationView do
|
||||||
alias Pleroma.Web.MastodonAPI.AccountView
|
alias Pleroma.Web.MastodonAPI.AccountView
|
||||||
alias Pleroma.Web.MastodonAPI.NotificationView
|
alias Pleroma.Web.MastodonAPI.NotificationView
|
||||||
alias Pleroma.Web.MastodonAPI.StatusView
|
alias Pleroma.Web.MastodonAPI.StatusView
|
||||||
alias Pleroma.Web.PleromaAPI.Chat.MessageReferenceView
|
|
||||||
|
|
||||||
@parent_types ~w{Like Announce EmojiReact}
|
@parent_types ~w{Like Announce EmojiReact}
|
||||||
|
|
||||||
|
@ -121,9 +118,6 @@ def render(
|
||||||
|> put_status(parent_activity_fn.(), reading_user, status_render_opts)
|
|> put_status(parent_activity_fn.(), reading_user, status_render_opts)
|
||||||
|> put_emoji(activity)
|
|> put_emoji(activity)
|
||||||
|
|
||||||
"pleroma:chat_mention" ->
|
|
||||||
put_chat_message(response, activity, reading_user, status_render_opts)
|
|
||||||
|
|
||||||
"pleroma:report" ->
|
"pleroma:report" ->
|
||||||
put_report(response, activity)
|
put_report(response, activity)
|
||||||
|
|
||||||
|
@ -144,17 +138,6 @@ defp put_emoji(response, activity) do
|
||||||
|> Map.put(:emoji_url, MediaProxy.url(Pleroma.Emoji.emoji_url(activity.data)))
|
|> Map.put(:emoji_url, MediaProxy.url(Pleroma.Emoji.emoji_url(activity.data)))
|
||||||
end
|
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
|
defp put_status(response, activity, reading_user, opts) do
|
||||||
status_render_opts = Map.merge(opts, %{activity: activity, for: reading_user})
|
status_render_opts = Map.merge(opts, %{activity: activity, for: reading_user})
|
||||||
status_render = StatusView.render("show.json", status_render_opts)
|
status_render = StatusView.render("show.json", status_render_opts)
|
||||||
|
|
|
@ -1,188 +0,0 @@
|
||||||
# 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
|
|
|
@ -1,63 +0,0 @@
|
||||||
# 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
|
|
|
@ -1,44 +0,0 @@
|
||||||
# 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
|
|
|
@ -124,13 +124,6 @@ def build_content(notification, actor, object, mastodon_type) do
|
||||||
|
|
||||||
def format_body(activity, actor, object, mastodon_type \\ nil)
|
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(
|
def format_body(
|
||||||
%{activity: %{data: %{"type" => "Create"}}},
|
%{activity: %{data: %{"type" => "Create"}}},
|
||||||
actor,
|
actor,
|
||||||
|
@ -187,7 +180,6 @@ def format_title(%{type: type}, mastodon_type) do
|
||||||
"follow_request" -> "New Follow Request"
|
"follow_request" -> "New Follow Request"
|
||||||
"reblog" -> "New Repeat"
|
"reblog" -> "New Repeat"
|
||||||
"favourite" -> "New Favorite"
|
"favourite" -> "New Favorite"
|
||||||
"pleroma:chat_mention" -> "New Chat Message"
|
|
||||||
"pleroma:emoji_reaction" -> "New Reaction"
|
"pleroma:emoji_reaction" -> "New Reaction"
|
||||||
type -> "New #{String.capitalize(type || "event")}"
|
type -> "New #{String.capitalize(type || "event")}"
|
||||||
end
|
end
|
||||||
|
|
|
@ -26,7 +26,7 @@ defmodule Pleroma.Web.Push.Subscription do
|
||||||
end
|
end
|
||||||
|
|
||||||
# credo:disable-for-next-line Credo.Check.Readability.MaxLineLength
|
# 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
|
defp alerts(%{data: %{alerts: alerts}}) do
|
||||||
alerts = Map.take(alerts, @supported_alert_types)
|
alerts = Map.take(alerts, @supported_alert_types)
|
||||||
|
|
|
@ -253,12 +253,8 @@ defmodule Pleroma.Web.Router do
|
||||||
patch("/users/:nickname/credentials", AdminAPIController, :update_user_credentials)
|
patch("/users/:nickname/credentials", AdminAPIController, :update_user_credentials)
|
||||||
|
|
||||||
get("/users/:nickname/statuses", AdminAPIController, :list_user_statuses)
|
get("/users/:nickname/statuses", AdminAPIController, :list_user_statuses)
|
||||||
get("/users/:nickname/chats", AdminAPIController, :list_user_chats)
|
|
||||||
|
|
||||||
get("/statuses", StatusController, :index)
|
get("/statuses", StatusController, :index)
|
||||||
|
|
||||||
get("/chats/:id", ChatController, :show)
|
|
||||||
get("/chats/:id/messages", ChatController, :messages)
|
|
||||||
end
|
end
|
||||||
|
|
||||||
# AdminAPI: admins and mods (staff) can perform these actions
|
# AdminAPI: admins and mods (staff) can perform these actions
|
||||||
|
@ -298,8 +294,6 @@ defmodule Pleroma.Web.Router do
|
||||||
|
|
||||||
post("/reload_emoji", AdminAPIController, :reload_emoji)
|
post("/reload_emoji", AdminAPIController, :reload_emoji)
|
||||||
get("/stats", AdminAPIController, :stats)
|
get("/stats", AdminAPIController, :stats)
|
||||||
|
|
||||||
delete("/chats/:id/messages/:message_id", ChatController, :delete_message)
|
|
||||||
end
|
end
|
||||||
|
|
||||||
scope "/api/v1/pleroma/emoji", Pleroma.Web.PleromaAPI do
|
scope "/api/v1/pleroma/emoji", Pleroma.Web.PleromaAPI do
|
||||||
|
@ -427,14 +421,6 @@ defmodule Pleroma.Web.Router do
|
||||||
scope [] do
|
scope [] do
|
||||||
pipe_through(:authenticated_api)
|
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/statuses", ConversationController, :statuses)
|
||||||
get("/conversations/:id", ConversationController, :show)
|
get("/conversations/:id", ConversationController, :show)
|
||||||
post("/conversations/read", ConversationController, :mark_as_read)
|
post("/conversations/read", ConversationController, :mark_as_read)
|
||||||
|
@ -471,13 +457,6 @@ defmodule Pleroma.Web.Router do
|
||||||
get("/federation_status", InstancesController, :show)
|
get("/federation_status", InstancesController, :show)
|
||||||
end
|
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
|
scope "/api/v1", Pleroma.Web.MastodonAPI do
|
||||||
pipe_through(:authenticated_api)
|
pipe_through(:authenticated_api)
|
||||||
|
|
||||||
|
|
|
@ -1,59 +0,0 @@
|
||||||
# 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
|
|
|
@ -6,7 +6,6 @@ defmodule Pleroma.Web.Streamer do
|
||||||
require Logger
|
require Logger
|
||||||
|
|
||||||
alias Pleroma.Activity
|
alias Pleroma.Activity
|
||||||
alias Pleroma.Chat.MessageReference
|
|
||||||
alias Pleroma.Config
|
alias Pleroma.Config
|
||||||
alias Pleroma.Conversation.Participation
|
alias Pleroma.Conversation.Participation
|
||||||
alias Pleroma.Notification
|
alias Pleroma.Notification
|
||||||
|
@ -25,7 +24,7 @@ defmodule Pleroma.Web.Streamer do
|
||||||
def registry, do: @registry
|
def registry, do: @registry
|
||||||
|
|
||||||
@public_streams ["public", "public:local", "public:media", "public:local:media"]
|
@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."
|
@doc "Expands and authorizes a stream, and registers the process for streaming."
|
||||||
@spec get_topic_and_add_socket(
|
@spec get_topic_and_add_socket(
|
||||||
|
@ -241,19 +240,6 @@ defp do_stream(topic, %Notification{} = item)
|
||||||
end)
|
end)
|
||||||
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
|
defp do_stream("user", item) do
|
||||||
Logger.debug("Trying to push to users")
|
Logger.debug("Trying to push to users")
|
||||||
|
|
||||||
|
|
|
@ -51,29 +51,6 @@ def render("update.json", %Activity{} = activity) do
|
||||||
|> Jason.encode!()
|
|> Jason.encode!()
|
||||||
end
|
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
|
def render("follow_relationships_update.json", item) do
|
||||||
%{
|
%{
|
||||||
event: "pleroma:follow_relationships_update",
|
event: "pleroma:follow_relationships_update",
|
||||||
|
|
1
test/fixtures/mewmew_no_name.json
vendored
1
test/fixtures/mewmew_no_name.json
vendored
|
@ -8,7 +8,6 @@
|
||||||
],
|
],
|
||||||
"attachment" : [],
|
"attachment" : [],
|
||||||
"capabilities" : {
|
"capabilities" : {
|
||||||
"acceptsChatMessages" : true
|
|
||||||
},
|
},
|
||||||
"discoverable" : false,
|
"discoverable" : false,
|
||||||
"endpoints" : {
|
"endpoints" : {
|
||||||
|
|
|
@ -129,7 +129,6 @@ test "load a settings with large values and pass to file", %{temp_file: temp_fil
|
||||||
notify_email: "noreply@example.com",
|
notify_email: "noreply@example.com",
|
||||||
description: "A Pleroma instance, an alternative fediverse server",
|
description: "A Pleroma instance, an alternative fediverse server",
|
||||||
limit: 5_000,
|
limit: 5_000,
|
||||||
chat_limit: 5_000,
|
|
||||||
remote_limit: 100_000,
|
remote_limit: 100_000,
|
||||||
upload_limit: 16_000_000,
|
upload_limit: 16_000_000,
|
||||||
avatar_upload_limit: 2_000_000,
|
avatar_upload_limit: 2_000_000,
|
||||||
|
@ -189,7 +188,7 @@ test "load a settings with large values and pass to file", %{temp_file: temp_fil
|
||||||
{:ok, file} = File.read(temp_file)
|
{:ok, file} = File.read(temp_file)
|
||||||
|
|
||||||
assert 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
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|
|
@ -1,29 +0,0 @@
|
||||||
# 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
|
|
|
@ -1,84 +0,0 @@
|
||||||
# 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
|
|
|
@ -279,14 +279,4 @@ test "check_uploders_s3_public_endpoint/0" do
|
||||||
end) =~
|
end) =~
|
||||||
"Your config is using the old setting for controlling the URL of media uploaded to your S3 bucket."
|
"Your config is using the old setting for controlling the URL of media uploaded to your S3 bucket."
|
||||||
end
|
end
|
||||||
|
|
||||||
test "check_old_chat_shoutbox/0" do
|
|
||||||
clear_config([:instance, :chat_limit], 1_000)
|
|
||||||
clear_config([:chat, :enabled], true)
|
|
||||||
|
|
||||||
assert capture_log(fn ->
|
|
||||||
DeprecationWarnings.check_old_chat_shoutbox()
|
|
||||||
end) =~
|
|
||||||
"Your config is using the old namespace for the Shoutbox configuration."
|
|
||||||
end
|
|
||||||
end
|
end
|
||||||
|
|
|
@ -95,8 +95,8 @@ test "don't restart if no reboot time settings were changed" do
|
||||||
|
|
||||||
@tag :erratic
|
@tag :erratic
|
||||||
test "on reboot time key" do
|
test "on reboot time key" do
|
||||||
clear_config(:shout)
|
clear_config([:pleroma, :rate_limit])
|
||||||
insert(:config, key: :shout, value: [enabled: false])
|
insert(:config, key: {:pleroma, :rate_limit}, value: [enabled: false])
|
||||||
assert capture_log(fn -> TransferTask.start_link([]) end) =~ "pleroma restarted"
|
assert capture_log(fn -> TransferTask.start_link([]) end) =~ "pleroma restarted"
|
||||||
end
|
end
|
||||||
|
|
||||||
|
@ -109,10 +109,10 @@ test "on reboot time subkey" do
|
||||||
|
|
||||||
@tag :erratic
|
@tag :erratic
|
||||||
test "don't restart pleroma on reboot time key and subkey if there is false flag" do
|
test "don't restart pleroma on reboot time key and subkey if there is false flag" do
|
||||||
clear_config(:shout)
|
clear_config([:pleroma, :rate_limit])
|
||||||
clear_config(Pleroma.Captcha)
|
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])
|
insert(:config, key: Pleroma.Captcha, value: [seconds_valid: 60])
|
||||||
|
|
||||||
refute String.contains?(
|
refute String.contains?(
|
||||||
|
|
|
@ -19,7 +19,6 @@ test "it fills in missing notification types" do
|
||||||
other_user = insert(:user)
|
other_user = insert(:user)
|
||||||
|
|
||||||
{:ok, post} = CommonAPI.post(user, %{status: "yeah, @#{other_user.nickname}"})
|
{: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, react} = CommonAPI.react_with_emoji(post.id, other_user, "☕")
|
||||||
{:ok, like} = CommonAPI.favorite(other_user, post.id)
|
{:ok, like} = CommonAPI.favorite(other_user, post.id)
|
||||||
{:ok, react_2} = CommonAPI.react_with_emoji(post.id, other_user, "☕")
|
{:ok, react_2} = CommonAPI.react_with_emoji(post.id, other_user, "☕")
|
||||||
|
@ -33,7 +32,7 @@ test "it fills in missing notification types" do
|
||||||
|> Activity.change(%{data: data})
|
|> Activity.change(%{data: data})
|
||||||
|> Repo.update()
|
|> 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()
|
NotificationBackfill.fill_in_notification_types()
|
||||||
|
|
||||||
|
@ -48,9 +47,6 @@ test "it fills in missing notification types" do
|
||||||
|
|
||||||
assert %{type: "pleroma:emoji_reaction"} =
|
assert %{type: "pleroma:emoji_reaction"} =
|
||||||
Repo.get_by(Notification, user_id: user.id, activity_id: react_2.id)
|
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
|
end
|
||||||
end
|
end
|
||||||
|
|
|
@ -1,52 +0,0 @@
|
||||||
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
|
|
|
@ -1,36 +0,0 @@
|
||||||
# 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
|
|
|
@ -443,22 +443,6 @@ test "it sends a welcome message if it is set" do
|
||||||
assert activity.actor == welcome_user.ap_id
|
assert activity.actor == welcome_user.ap_id
|
||||||
end
|
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:
|
setup do:
|
||||||
clear_config(:mrf_simple,
|
clear_config(:mrf_simple,
|
||||||
media_removal: [],
|
media_removal: [],
|
||||||
|
@ -480,24 +464,6 @@ test "it sends a welcome chat message if it is set" do
|
||||||
]
|
]
|
||||||
)
|
)
|
||||||
|
|
||||||
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
|
test "it sends a welcome email message if it is set" do
|
||||||
welcome_user = insert(:user)
|
welcome_user = insert(:user)
|
||||||
clear_config([:welcome, :email, :enabled], true)
|
clear_config([:welcome, :email, :enabled], true)
|
||||||
|
@ -665,15 +631,6 @@ test "it sets the password_hash and ap_id" do
|
||||||
assert changeset.changes.follower_address == "#{changeset.changes.ap_id}/followers"
|
assert changeset.changes.follower_address == "#{changeset.changes.ap_id}/followers"
|
||||||
end
|
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
|
test "it creates a confirmed user" do
|
||||||
changeset = User.register_changeset(%User{}, @full_user_data)
|
changeset = User.register_changeset(%User{}, @full_user_data)
|
||||||
assert changeset.valid?
|
assert changeset.valid?
|
||||||
|
|
|
@ -186,13 +186,6 @@ test "it returns a user that is invisible" do
|
||||||
assert User.invisible?(user)
|
assert User.invisible?(user)
|
||||||
end
|
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
|
test "works for guppe actors" do
|
||||||
user_id = "https://gup.pe/u/bernie2020"
|
user_id = "https://gup.pe/u/bernie2020"
|
||||||
|
|
||||||
|
|
|
@ -8,8 +8,6 @@ defmodule Pleroma.Web.ActivityPub.MRF.HellthreadPolicyTest do
|
||||||
|
|
||||||
import Pleroma.Web.ActivityPub.MRF.HellthreadPolicy
|
import Pleroma.Web.ActivityPub.MRF.HellthreadPolicy
|
||||||
|
|
||||||
alias Pleroma.Web.CommonAPI
|
|
||||||
|
|
||||||
setup do
|
setup do
|
||||||
user = insert(:user)
|
user = insert(:user)
|
||||||
|
|
||||||
|
@ -33,17 +31,6 @@ defmodule Pleroma.Web.ActivityPub.MRF.HellthreadPolicyTest do
|
||||||
|
|
||||||
setup do: clear_config(:mrf_hellthread)
|
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
|
describe "reject" do
|
||||||
test "rejects the message if the recipient count is above reject_threshold", %{
|
test "rejects the message if the recipient count is above reject_threshold", %{
|
||||||
message: message
|
message: message
|
||||||
|
|
|
@ -1,212 +0,0 @@
|
||||||
# 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
|
|
|
@ -7,8 +7,6 @@ defmodule Pleroma.Web.ActivityPub.SideEffectsTest do
|
||||||
use Pleroma.DataCase
|
use Pleroma.DataCase
|
||||||
|
|
||||||
alias Pleroma.Activity
|
alias Pleroma.Activity
|
||||||
alias Pleroma.Chat
|
|
||||||
alias Pleroma.Chat.MessageReference
|
|
||||||
alias Pleroma.Notification
|
alias Pleroma.Notification
|
||||||
alias Pleroma.Object
|
alias Pleroma.Object
|
||||||
alias Pleroma.Repo
|
alias Pleroma.Repo
|
||||||
|
@ -17,6 +15,7 @@ defmodule Pleroma.Web.ActivityPub.SideEffectsTest do
|
||||||
alias Pleroma.Web.ActivityPub.ActivityPub
|
alias Pleroma.Web.ActivityPub.ActivityPub
|
||||||
alias Pleroma.Web.ActivityPub.Builder
|
alias Pleroma.Web.ActivityPub.Builder
|
||||||
alias Pleroma.Web.ActivityPub.SideEffects
|
alias Pleroma.Web.ActivityPub.SideEffects
|
||||||
|
alias Pleroma.Web.ActivityPub.Utils
|
||||||
alias Pleroma.Web.CommonAPI
|
alias Pleroma.Web.CommonAPI
|
||||||
|
|
||||||
import Mock
|
import Mock
|
||||||
|
@ -27,15 +26,22 @@ test "it streams out notifications and streams" do
|
||||||
author = insert(:user, local: true)
|
author = insert(:user, local: true)
|
||||||
recipient = 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} =
|
{: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} = ActivityPub.persist(create_activity_data, local: false)
|
||||||
|
|
||||||
{:ok, _create_activity, meta} =
|
{: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]
|
assert [notification] = meta[:notifications]
|
||||||
|
|
||||||
|
@ -58,7 +64,6 @@ test "it streams out notifications and streams" do
|
||||||
SideEffects.handle_after_transaction(meta)
|
SideEffects.handle_after_transaction(meta)
|
||||||
|
|
||||||
assert called(Pleroma.Web.Streamer.stream(["user", "user:notification"], notification))
|
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))
|
assert called(Pleroma.Web.Push.send(notification))
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
@ -336,147 +341,6 @@ test "creates a notification", %{like: like, poster: poster} do
|
||||||
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
|
describe "announce objects" do
|
||||||
setup do
|
setup do
|
||||||
poster = insert(:user)
|
poster = insert(:user)
|
||||||
|
|
|
@ -1,171 +0,0 @@
|
||||||
# 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
|
|
|
@ -164,23 +164,4 @@ test "sets correct totalItems when follows are hidden but the follow counter is
|
||||||
assert %{"totalItems" => 1} = UserView.render("following.json", %{user: user})
|
assert %{"totalItems" => 1} = UserView.render("following.json", %{user: user})
|
||||||
end
|
end
|
||||||
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
|
||||||
|
|
|
@ -419,56 +419,6 @@ test "excludes reblogs by default", %{conn: conn, user: user} do
|
||||||
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
|
describe "GET /api/pleroma/admin/moderation_log" do
|
||||||
setup do
|
setup do
|
||||||
moderator = insert(:user, is_moderator: true)
|
moderator = insert(:user, is_moderator: true)
|
||||||
|
|
|
@ -1,218 +0,0 @@
|
||||||
# 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
|
|
File diff suppressed because it is too large
Load diff
|
@ -7,13 +7,11 @@ defmodule Pleroma.Web.CommonAPITest do
|
||||||
use Pleroma.DataCase
|
use Pleroma.DataCase
|
||||||
|
|
||||||
alias Pleroma.Activity
|
alias Pleroma.Activity
|
||||||
alias Pleroma.Chat
|
|
||||||
alias Pleroma.Conversation.Participation
|
alias Pleroma.Conversation.Participation
|
||||||
alias Pleroma.Notification
|
alias Pleroma.Notification
|
||||||
alias Pleroma.Object
|
alias Pleroma.Object
|
||||||
alias Pleroma.Repo
|
alias Pleroma.Repo
|
||||||
alias Pleroma.User
|
alias Pleroma.User
|
||||||
alias Pleroma.Web.ActivityPub.ActivityPub
|
|
||||||
alias Pleroma.Web.ActivityPub.Transmogrifier
|
alias Pleroma.Web.ActivityPub.Transmogrifier
|
||||||
alias Pleroma.Web.ActivityPub.Visibility
|
alias Pleroma.Web.ActivityPub.Visibility
|
||||||
alias Pleroma.Web.AdminAPI.AccountView
|
alias Pleroma.Web.AdminAPI.AccountView
|
||||||
|
@ -106,173 +104,6 @@ test "it blocks and does not federate if outgoing blocks are disabled", %{
|
||||||
end
|
end
|
||||||
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
|
describe "unblocking" do
|
||||||
test "it works even without an existing block activity" do
|
test "it works even without an existing block activity" do
|
||||||
blocked = insert(:user)
|
blocked = insert(:user)
|
||||||
|
|
|
@ -1683,7 +1683,6 @@ test "verify_credentials" do
|
||||||
response = json_response_and_validate_schema(conn, 200)
|
response = json_response_and_validate_schema(conn, 200)
|
||||||
|
|
||||||
assert %{"id" => id, "source" => %{"privacy" => "public"}} = response
|
assert %{"id" => id, "source" => %{"privacy" => "public"}} = response
|
||||||
assert response["pleroma"]["chat_token"]
|
|
||||||
assert response["pleroma"]["unread_notifications_count"] == 6
|
assert response["pleroma"]["unread_notifications_count"] == 6
|
||||||
assert id == to_string(user.id)
|
assert id == to_string(user.id)
|
||||||
end
|
end
|
||||||
|
|
|
@ -38,7 +38,6 @@ test "get instance information", %{conn: conn} do
|
||||||
"background_upload_limit" => _,
|
"background_upload_limit" => _,
|
||||||
"banner_upload_limit" => _,
|
"banner_upload_limit" => _,
|
||||||
"background_image" => from_config_background,
|
"background_image" => from_config_background,
|
||||||
"shout_limit" => _,
|
|
||||||
"description_limit" => _
|
"description_limit" => _
|
||||||
} = result
|
} = result
|
||||||
|
|
||||||
|
|
|
@ -52,27 +52,6 @@ test "list of notifications" do
|
||||||
assert response == expected_response
|
assert response == expected_response
|
||||||
end
|
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
|
test "by default, does not contain pleroma:report" do
|
||||||
%{user: user, conn: conn} = oauth_access(["read:notifications"])
|
%{user: user, conn: conn} = oauth_access(["read:notifications"])
|
||||||
other_user = insert(:user)
|
other_user = insert(:user)
|
||||||
|
|
|
@ -113,7 +113,6 @@ test "successful creation", %{conn: conn} do
|
||||||
"favourite" => true,
|
"favourite" => true,
|
||||||
"follow" => true,
|
"follow" => true,
|
||||||
"reblog" => true,
|
"reblog" => true,
|
||||||
"pleroma:chat_mention" => true,
|
|
||||||
"pleroma:emoji_reaction" => true
|
"pleroma:emoji_reaction" => true
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
@ -129,7 +128,6 @@ test "successful creation", %{conn: conn} do
|
||||||
"favourite" => true,
|
"favourite" => true,
|
||||||
"follow" => true,
|
"follow" => true,
|
||||||
"reblog" => true,
|
"reblog" => true,
|
||||||
"pleroma:chat_mention" => true,
|
|
||||||
"pleroma:emoji_reaction" => true
|
"pleroma:emoji_reaction" => true
|
||||||
},
|
},
|
||||||
"endpoint" => subscription.endpoint,
|
"endpoint" => subscription.endpoint,
|
||||||
|
@ -185,7 +183,6 @@ test "returns a user subsciption", %{conn: conn, user: user, token: token} do
|
||||||
"favourite" => true,
|
"favourite" => true,
|
||||||
"follow" => true,
|
"follow" => true,
|
||||||
"reblog" => true,
|
"reblog" => true,
|
||||||
"pleroma:chat_mention" => true,
|
|
||||||
"pleroma:emoji_reaction" => true
|
"pleroma:emoji_reaction" => true
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -204,7 +201,6 @@ test "returns updated subsciption", %{conn: conn, subscription: subscription} do
|
||||||
"favourite" => false,
|
"favourite" => false,
|
||||||
"follow" => false,
|
"follow" => false,
|
||||||
"reblog" => false,
|
"reblog" => false,
|
||||||
"pleroma:chat_mention" => false,
|
|
||||||
"pleroma:emoji_reaction" => false
|
"pleroma:emoji_reaction" => false
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -217,7 +213,6 @@ test "returns updated subsciption", %{conn: conn, subscription: subscription} do
|
||||||
"favourite" => false,
|
"favourite" => false,
|
||||||
"follow" => false,
|
"follow" => false,
|
||||||
"reblog" => false,
|
"reblog" => false,
|
||||||
"pleroma:chat_mention" => false,
|
|
||||||
"pleroma:emoji_reaction" => false
|
"pleroma:emoji_reaction" => false
|
||||||
},
|
},
|
||||||
"endpoint" => "https://example.com/example/1234",
|
"endpoint" => "https://example.com/example/1234",
|
||||||
|
|
|
@ -104,13 +104,6 @@ test "updates the user's locking status", %{conn: conn} do
|
||||||
assert user_data["locked"] == true
|
assert user_data["locked"] == true
|
||||||
end
|
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
|
test "updates the user's allow_following_move", %{user: user, conn: conn} do
|
||||||
assert user.allow_following_move == true
|
assert user.allow_following_move == true
|
||||||
|
|
||||||
|
|
|
@ -91,8 +91,7 @@ test "Represent a user account" do
|
||||||
hide_followers_count: false,
|
hide_followers_count: false,
|
||||||
hide_follows_count: false,
|
hide_follows_count: false,
|
||||||
relationship: %{},
|
relationship: %{},
|
||||||
skip_thread_containment: false,
|
skip_thread_containment: false
|
||||||
accepts_chat_messages: nil
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -193,8 +192,7 @@ test "Represent a Service(bot) account" do
|
||||||
hide_followers_count: false,
|
hide_followers_count: false,
|
||||||
hide_follows_count: false,
|
hide_follows_count: false,
|
||||||
relationship: %{},
|
relationship: %{},
|
||||||
skip_thread_containment: false,
|
skip_thread_containment: false
|
||||||
accepts_chat_messages: nil
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -6,8 +6,6 @@ defmodule Pleroma.Web.MastodonAPI.NotificationViewTest do
|
||||||
use Pleroma.DataCase
|
use Pleroma.DataCase
|
||||||
|
|
||||||
alias Pleroma.Activity
|
alias Pleroma.Activity
|
||||||
alias Pleroma.Chat
|
|
||||||
alias Pleroma.Chat.MessageReference
|
|
||||||
alias Pleroma.Notification
|
alias Pleroma.Notification
|
||||||
alias Pleroma.Object
|
alias Pleroma.Object
|
||||||
alias Pleroma.Repo
|
alias Pleroma.Repo
|
||||||
|
@ -22,7 +20,6 @@ defmodule Pleroma.Web.MastodonAPI.NotificationViewTest do
|
||||||
alias Pleroma.Web.MastodonAPI.NotificationView
|
alias Pleroma.Web.MastodonAPI.NotificationView
|
||||||
alias Pleroma.Web.MastodonAPI.StatusView
|
alias Pleroma.Web.MastodonAPI.StatusView
|
||||||
alias Pleroma.Web.MediaProxy
|
alias Pleroma.Web.MediaProxy
|
||||||
alias Pleroma.Web.PleromaAPI.Chat.MessageReferenceView
|
|
||||||
import Pleroma.Factory
|
import Pleroma.Factory
|
||||||
|
|
||||||
defp test_notifications_rendering(notifications, user, expected_result) do
|
defp test_notifications_rendering(notifications, user, expected_result) do
|
||||||
|
@ -40,30 +37,6 @@ defp test_notifications_rendering(notifications, user, expected_result) do
|
||||||
assert expected_result == result
|
assert expected_result == result
|
||||||
end
|
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
|
test "Mention notification" do
|
||||||
user = insert(:user)
|
user = insert(:user)
|
||||||
mentioned_user = insert(:user)
|
mentioned_user = insert(:user)
|
||||||
|
|
|
@ -1,453 +0,0 @@
|
||||||
# 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
|
|
|
@ -1,75 +0,0 @@
|
||||||
# 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
|
|
|
@ -1,49 +0,0 @@
|
||||||
# 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
|
|
|
@ -7,10 +7,8 @@ defmodule Pleroma.Web.Push.ImplTest do
|
||||||
|
|
||||||
import Pleroma.Factory
|
import Pleroma.Factory
|
||||||
|
|
||||||
alias Pleroma.Notification
|
|
||||||
alias Pleroma.Object
|
alias Pleroma.Object
|
||||||
alias Pleroma.User
|
alias Pleroma.User
|
||||||
alias Pleroma.Web.ActivityPub.ActivityPub
|
|
||||||
alias Pleroma.Web.CommonAPI
|
alias Pleroma.Web.CommonAPI
|
||||||
alias Pleroma.Web.Push.Impl
|
alias Pleroma.Web.Push.Impl
|
||||||
alias Pleroma.Web.Push.Subscription
|
alias Pleroma.Web.Push.Subscription
|
||||||
|
@ -216,46 +214,6 @@ test "renders title for create activity with direct visibility" do
|
||||||
end
|
end
|
||||||
|
|
||||||
describe "build_content/3" do
|
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
|
test "hides contents of notifications when option enabled" do
|
||||||
user = insert(:user, nickname: "Bob")
|
user = insert(:user, nickname: "Bob")
|
||||||
|
|
||||||
|
|
|
@ -1,41 +0,0 @@
|
||||||
# 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
|
|
|
@ -7,15 +7,11 @@ defmodule Pleroma.Web.StreamerTest do
|
||||||
|
|
||||||
import Pleroma.Factory
|
import Pleroma.Factory
|
||||||
|
|
||||||
alias Pleroma.Chat
|
|
||||||
alias Pleroma.Chat.MessageReference
|
|
||||||
alias Pleroma.Conversation.Participation
|
alias Pleroma.Conversation.Participation
|
||||||
alias Pleroma.List
|
alias Pleroma.List
|
||||||
alias Pleroma.Object
|
|
||||||
alias Pleroma.User
|
alias Pleroma.User
|
||||||
alias Pleroma.Web.CommonAPI
|
alias Pleroma.Web.CommonAPI
|
||||||
alias Pleroma.Web.Streamer
|
alias Pleroma.Web.Streamer
|
||||||
alias Pleroma.Web.StreamerView
|
|
||||||
|
|
||||||
@moduletag needs_streamer: true, capture_log: true
|
@moduletag needs_streamer: true, capture_log: true
|
||||||
|
|
||||||
|
@ -80,20 +76,16 @@ test "allows user streams (with proper OAuth token scopes)", %{
|
||||||
expected_user_topic = "user:#{user.id}"
|
expected_user_topic = "user:#{user.id}"
|
||||||
expected_notification_topic = "user:notification:#{user.id}"
|
expected_notification_topic = "user:notification:#{user.id}"
|
||||||
expected_direct_topic = "direct:#{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
|
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_user_topic} = Streamer.get_topic("user", user, valid_user_token)
|
||||||
|
|
||||||
assert {:ok, ^expected_direct_topic} =
|
assert {:ok, ^expected_direct_topic} =
|
||||||
Streamer.get_topic("direct", user, valid_user_token)
|
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
|
end
|
||||||
|
|
||||||
for invalid_user_token <- [read_notifications_token, badly_scoped_token],
|
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)
|
assert {:error, :unauthorized} = Streamer.get_topic(user_topic, user, invalid_user_token)
|
||||||
end
|
end
|
||||||
|
|
||||||
|
@ -257,66 +249,6 @@ test "it sends notify to in the 'user:notification' stream", %{
|
||||||
refute Streamer.filtered_by_user?(user, notify)
|
refute Streamer.filtered_by_user?(user, notify)
|
||||||
end
|
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", %{
|
test "it doesn't send notify to the 'user:notification' stream when a user is blocked", %{
|
||||||
user: user,
|
user: user,
|
||||||
token: oauth_token
|
token: oauth_token
|
||||||
|
|
Loading…
Reference in a new issue