Skip to content

Commit

Permalink
Add conversations API (#235)
Browse files Browse the repository at this point in the history
  • Loading branch information
andregasser authored Jul 18, 2023
1 parent 78d11a7 commit e2e396f
Show file tree
Hide file tree
Showing 10 changed files with 458 additions and 0 deletions.
Original file line number Diff line number Diff line change
@@ -0,0 +1,53 @@
package social.bigbone.rx

import io.reactivex.rxjava3.core.Completable
import io.reactivex.rxjava3.core.Single
import social.bigbone.MastodonClient
import social.bigbone.api.Pageable
import social.bigbone.api.Range
import social.bigbone.api.entity.Conversation
import social.bigbone.api.method.ConversationMethods
import social.bigbone.rx.extensions.onErrorIfNotDisposed

/**
* Reactive implementation of [ConversationMethods].
* Direct conversations with other participants. (Currently, just threads containing a post with "direct" visibility.)
* @see <a href="https://docs.joinmastodon.org/methods/conversations/">Mastodon conversations API methods</a>
*/
class RxConversationMethods(client: MastodonClient) {
private val conversationMethods = ConversationMethods(client)

@JvmOverloads
fun getConversations(range: Range = Range()): Single<Pageable<Conversation>> {
return Single.create {
try {
val conversations = conversationMethods.getConversations(range)
it.onSuccess(conversations.execute())
} catch (throwable: Throwable) {
it.onErrorIfNotDisposed(throwable)
}
}
}

fun deleteConversation(conversationId: String): Completable {
return Completable.create {
try {
conversationMethods.deleteConversation(conversationId)
it.onComplete()
} catch (throwable: Throwable) {
it.onErrorIfNotDisposed(throwable)
}
}
}

fun markConversationAsRead(conversationId: String): Single<Conversation> {
return Single.create {
try {
val conversation = conversationMethods.markConversationAsRead(conversationId)
it.onSuccess(conversation.execute())
} catch (throwable: Throwable) {
it.onError(throwable)
}
}
}
}
8 changes: 8 additions & 0 deletions bigbone/src/main/kotlin/social/bigbone/MastodonClient.kt
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@ import social.bigbone.api.exception.BigBoneRequestException
import social.bigbone.api.method.AccountMethods
import social.bigbone.api.method.AppMethods
import social.bigbone.api.method.BlockMethods
import social.bigbone.api.method.ConversationMethods
import social.bigbone.api.method.DirectoryMethods
import social.bigbone.api.method.FavouriteMethods
import social.bigbone.api.method.FilterMethods
Expand Down Expand Up @@ -79,6 +80,13 @@ private constructor(
@get:JvmName("blocks")
val blocks: BlockMethods by lazy { BlockMethods(this) }

/**
* Access API methods under "api/vX/conversations" endpoint.
*/
@Suppress("unused") // public API
@get:JvmName("conversations")
val conversations: ConversationMethods by lazy { ConversationMethods(this) }

/**
* Access API methods under "api/vX/directory" endpoint.
*/
Expand Down
33 changes: 33 additions & 0 deletions bigbone/src/main/kotlin/social/bigbone/api/entity/Conversation.kt
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
package social.bigbone.api.entity

import com.google.gson.annotations.SerializedName

/**
* Represents a conversation with "direct message" visibility.
* @see <a href="https://docs.joinmastodon.org/entities/Conversation/">Mastodon API Conversation</a>
*/
data class Conversation(
/**
* The ID of the conversation in the database.
*/
@SerializedName("id")
val id: String = "0",

/**
* Is the conversation currently marked as unread?
*/
@SerializedName("unread")
val unread: Boolean = true,

/**
* Participants in the conversation.
*/
@SerializedName("accounts")
val accounts: List<Account>? = emptyList(),

/**
* The last status in the conversation.
*/
@SerializedName("last_status")
val lastStatus: Status? = null,
)
Original file line number Diff line number Diff line change
@@ -0,0 +1,51 @@
package social.bigbone.api.method

import social.bigbone.MastodonClient
import social.bigbone.MastodonRequest
import social.bigbone.api.Pageable
import social.bigbone.api.Range
import social.bigbone.api.entity.Conversation

/**
* Direct conversations with other participants. (Currently, just threads containing a post with "direct" visibility.)
* @see <a href="https://docs.joinmastodon.org/methods/conversations/">Mastodon conversations API methods</a>
*/
class ConversationMethods(private val client: MastodonClient) {
/**
* View your direct conversations with other participants.
* @param range optional Range for the pageable return value
* @see <a href="https://docs.joinmastodon.org/methods/conversations/#get">Mastodon API documentation: methods/conversations/#get</a>
*/
@JvmOverloads
fun getConversations(range: Range = Range()): MastodonRequest<Pageable<Conversation>> {
return client.getPageableMastodonRequest(
endpoint = "api/v1/conversations",
method = MastodonClient.Method.GET,
parameters = range.toParameters()
)
}

/**
* Removes a conversation from your list of conversations.
* @param conversationId ID of the conversation.
* @see <a href="https://docs.joinmastodon.org/methods/conversations/#delete">Mastodon API documentation: methods/conversations/#delete</a>
*/
fun deleteConversation(conversationId: String) {
client.performAction(
endpoint = "api/v1/conversations/$conversationId",
method = MastodonClient.Method.DELETE
)
}

/**
* Marks a conversation as read.
* @param conversationId ID of the conversation.
* @see <a href="https://docs.joinmastodon.org/methods/conversations/#read">Mastodon API documentation: methods/conversations/#read</a>
*/
fun markConversationAsRead(conversationId: String): MastodonRequest<Conversation> {
return client.getMastodonRequest(
endpoint = "api/v1/conversations/$conversationId/read",
method = MastodonClient.Method.POST
)
}
}
140 changes: 140 additions & 0 deletions bigbone/src/test/assets/conversations.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,140 @@
[
{
"id": "418450",
"unread": true,
"accounts": [
{
"id": 11111,
"username": "test",
"acct": "[email protected]",
"display_name": "test",
"locked": false,
"created_at": "2017-04-27T11:12:20.039Z",
"followers_count": 371,
"following_count": 14547,
"statuses_count": 195,
"note": "<p>test</p>",
"url": "https://test.com/test",
"avatar": "test",
"avatar_static": "test",
"header": "test",
"header_static": "test",
"emojis": [
{
"shortcode": "test",
"url": "https://test.jp/images/custom_emojis/images/000/001/576/original/test.png",
"static_url": "https://test.jp/images/custom_emojis/images/000/001/576/static/test.png",
"visible_in_picker": true
}
]
}
],
"last_status": {
"account": {
"acct": "test",
"avatar": "test",
"avatar_static": "test",
"created_at": "2017-04-13T13:09:20.869Z",
"display_name": "test",
"followers_count": 1,
"following_count": 0,
"header": "https://mstdn.jp/headers/original/missing.png",
"header_static": "https://mstdn.jp/headers/original/missing.png",
"id": "14476",
"locked": false,
"note": "test",
"statuses_count": 10,
"url": "https://mstdn.jp/test",
"username": "test"
},
"application": null,
"content": "Test Status",
"created_at": "2017-04-14T06:11:41.893Z",
"favourited": null,
"favourites_count": 0,
"id": "11111",
"in_reply_to_account_id": null,
"in_reply_to_id": null,
"media_attachments": [],
"mentions": [],
"reblog": null,
"reblogged": null,
"reblogs_count": 0,
"sensitive": false,
"spoiler_text": "",
"tags": [],
"uri": "tag:mstdn.jp,2017-04-14:objectId=172429:objectType=Status",
"url": "https://mstdn.jp/test",
"visibility": "public"
}
},
{
"id": "418374",
"unread": false,
"accounts": [
{
"id": 11111,
"username": "test",
"acct": "[email protected]",
"display_name": "test",
"locked": false,
"created_at": "2017-04-27T11:12:20.039Z",
"followers_count": 371,
"following_count": 14547,
"statuses_count": 195,
"note": "<p>test</p>",
"url": "https://test.com/test",
"avatar": "test",
"avatar_static": "test",
"header": "test",
"header_static": "test",
"emojis": [
{
"shortcode": "test",
"url": "https://test.jp/images/custom_emojis/images/000/001/576/original/test.png",
"static_url": "https://test.jp/images/custom_emojis/images/000/001/576/static/test.png",
"visible_in_picker": true
}
]
}
],
"last_status": {
"account": {
"acct": "test",
"avatar": "test",
"avatar_static": "test",
"created_at": "2017-04-13T13:09:20.869Z",
"display_name": "test",
"followers_count": 1,
"following_count": 0,
"header": "https://mstdn.jp/headers/original/missing.png",
"header_static": "https://mstdn.jp/headers/original/missing.png",
"id": "14476",
"locked": false,
"note": "test",
"statuses_count": 10,
"url": "https://mstdn.jp/test",
"username": "test"
},
"application": null,
"content": "Test Status",
"created_at": "2017-04-14T06:11:41.893Z",
"favourited": null,
"favourites_count": 0,
"id": "11111",
"in_reply_to_account_id": null,
"in_reply_to_id": null,
"media_attachments": [],
"mentions": [],
"reblog": null,
"reblogged": null,
"reblogs_count": 0,
"sensitive": false,
"spoiler_text": "",
"tags": [],
"uri": "tag:mstdn.jp,2017-04-14:objectId=172429:objectType=Status",
"url": "https://mstdn.jp/test",
"visibility": "public"
}
}
]
1 change: 1 addition & 0 deletions bigbone/src/test/assets/conversations_after_deleting.json
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
{}
69 changes: 69 additions & 0 deletions bigbone/src/test/assets/conversations_after_edit.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,69 @@
{
"id": "418450",
"unread": false,
"accounts": [
{
"id": 11111,
"username": "test",
"acct": "[email protected]",
"display_name": "test",
"locked": false,
"created_at": "2017-04-27T11:12:20.039Z",
"followers_count": 371,
"following_count": 14547,
"statuses_count": 195,
"note": "<p>test</p>",
"url": "https://test.com/test",
"avatar": "test",
"avatar_static": "test",
"header": "test",
"header_static": "test",
"emojis": [
{
"shortcode": "test",
"url": "https://test.jp/images/custom_emojis/images/000/001/576/original/test.png",
"static_url": "https://test.jp/images/custom_emojis/images/000/001/576/static/test.png",
"visible_in_picker": true
}
]
}
],
"last_status": {
"account": {
"acct": "test",
"avatar": "test",
"avatar_static": "test",
"created_at": "2017-04-13T13:09:20.869Z",
"display_name": "test",
"followers_count": 1,
"following_count": 0,
"header": "https://mstdn.jp/headers/original/missing.png",
"header_static": "https://mstdn.jp/headers/original/missing.png",
"id": "14476",
"locked": false,
"note": "test",
"statuses_count": 10,
"url": "https://mstdn.jp/test",
"username": "test"
},
"application": null,
"content": "Test Status",
"created_at": "2017-04-14T06:11:41.893Z",
"favourited": null,
"favourites_count": 0,
"id": "11111",
"in_reply_to_account_id": null,
"in_reply_to_id": null,
"media_attachments": [],
"mentions": [],
"reblog": null,
"reblogged": null,
"reblogs_count": 0,
"sensitive": false,
"spoiler_text": "",
"tags": [],
"uri": "tag:mstdn.jp,2017-04-14:objectId=172429:objectType=Status",
"url": "https://mstdn.jp/test",
"visibility": "public"
}
}
Loading

0 comments on commit e2e396f

Please sign in to comment.