/* Copyright 2017 Andrew Dawson * * This file is a part of Tusky. * * This program is free software; you can redistribute it and/or modify it under the terms of the * GNU General Public License as published by the Free Software Foundation; either version 3 of the * License, or (at your option) any later version. * * Tusky is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even * the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General * Public License for more details. * * You should have received a copy of the GNU General Public License along with Tusky; if not, * see . */ package com.keylesspalace.tusky.network import com.keylesspalace.tusky.entity.* import io.reactivex.Completable import io.reactivex.Single import okhttp3.MultipartBody import okhttp3.RequestBody import okhttp3.ResponseBody import retrofit2.Call import retrofit2.Response import retrofit2.http.* import retrofit2.http.Field /** * for documentation of the Mastodon REST API see https://docs.joinmastodon.org/api/ */ @JvmSuppressWildcards interface MastodonApi { companion object { const val ENDPOINT_AUTHORIZE = "/oauth/authorize" const val DOMAIN_HEADER = "domain" const val PLACEHOLDER_DOMAIN = "dummy.placeholder" } @GET("/api/v1/lists") fun getLists(): Single> @GET("/api/v1/custom_emojis") fun getCustomEmojis(): Single> @GET("api/v1/instance") fun getInstance(): Single @GET("api/v1/filters") fun getFilters(): Call> @GET("api/v1/timelines/home") fun homeTimeline( @Query("max_id") maxId: String?, @Query("since_id") sinceId: String?, @Query("limit") limit: Int? ): Call> @GET("api/v1/timelines/home") fun homeTimelineSingle( @Query("max_id") maxId: String?, @Query("since_id") sinceId: String?, @Query("limit") limit: Int? ): Single> @GET("api/v1/timelines/public") fun publicTimeline( @Query("local") local: Boolean?, @Query("max_id") maxId: String?, @Query("since_id") sinceId: String?, @Query("limit") limit: Int? ): Call> @GET("api/v1/timelines/tag/{hashtag}") fun hashtagTimeline( @Path("hashtag") hashtag: String, @Query("local") local: Boolean?, @Query("max_id") maxId: String?, @Query("since_id") sinceId: String?, @Query("limit") limit: Int? ): Call> @GET("api/v1/timelines/list/{listId}") fun listTimeline( @Path("listId") listId: String, @Query("max_id") maxId: String?, @Query("since_id") sinceId: String?, @Query("limit") limit: Int? ): Call> @GET("api/v1/notifications") fun notifications( @Query("max_id") maxId: String?, @Query("since_id") sinceId: String?, @Query("limit") limit: Int?, @Query("exclude_types[]") excludes: Set? ): Call> @GET("api/v1/notifications") fun notificationsWithAuth( @Header("Authorization") auth: String, @Header(DOMAIN_HEADER) domain: String ): Call> @POST("api/v1/notifications/clear") fun clearNotifications(): Call @GET("api/v1/notifications/{id}") fun notification( @Path("id") notificationId: String ): Call @Multipart @POST("api/v1/media") fun uploadMedia( @Part file: MultipartBody.Part ): Single @FormUrlEncoded @PUT("api/v1/media/{mediaId}") fun updateMedia( @Path("mediaId") mediaId: String, @Field("description") description: String ): Single @POST("api/v1/statuses") fun createStatus( @Header("Authorization") auth: String, @Header(DOMAIN_HEADER) domain: String, @Header("Idempotency-Key") idempotencyKey: String, @Body status: NewStatus ): Call @GET("api/v1/statuses/{id}") fun status( @Path("id") statusId: String ): Call @GET("api/v1/statuses/{id}/context") fun statusContext( @Path("id") statusId: String ): Call @GET("api/v1/statuses/{id}/reblogged_by") fun statusRebloggedBy( @Path("id") statusId: String, @Query("max_id") maxId: String? ): Single>> @GET("api/v1/statuses/{id}/favourited_by") fun statusFavouritedBy( @Path("id") statusId: String, @Query("max_id") maxId: String? ): Single>> @DELETE("api/v1/statuses/{id}") fun deleteStatus( @Path("id") statusId: String ): Single @POST("api/v1/statuses/{id}/reblog") fun reblogStatus( @Path("id") statusId: String ): Single @POST("api/v1/statuses/{id}/unreblog") fun unreblogStatus( @Path("id") statusId: String ): Single @POST("api/v1/statuses/{id}/favourite") fun favouriteStatus( @Path("id") statusId: String ): Single @POST("api/v1/statuses/{id}/unfavourite") fun unfavouriteStatus( @Path("id") statusId: String ): Single @POST("api/v1/statuses/{id}/bookmark") fun bookmarkStatus( @Path("id") statusId: String ): Single @POST("api/v1/statuses/{id}/unbookmark") fun unbookmarkStatus( @Path("id") statusId: String ): Single @POST("api/v1/statuses/{id}/pin") fun pinStatus( @Path("id") statusId: String ): Single @POST("api/v1/statuses/{id}/unpin") fun unpinStatus( @Path("id") statusId: String ): Single @POST("api/v1/statuses/{id}/mute") fun muteConversation( @Path("id") statusId: String ): Single @POST("api/v1/statuses/{id}/unmute") fun unmuteConversation( @Path("id") statusId: String ): Single @GET("api/v1/scheduled_statuses") fun scheduledStatuses( @Query("limit") limit: Int? = null, @Query("max_id") maxId: String? = null ): Single> @DELETE("api/v1/scheduled_statuses/{id}") fun deleteScheduledStatus( @Path("id") scheduledStatusId: String ): Single @GET("api/v1/accounts/verify_credentials") fun accountVerifyCredentials(): Single @FormUrlEncoded @PATCH("api/v1/accounts/update_credentials") fun accountUpdateSource( @Field("source[privacy]") privacy: String?, @Field("source[sensitive]") sensitive: Boolean? ): Call @Multipart @PATCH("api/v1/accounts/update_credentials") fun accountUpdateCredentials( @Part(value = "display_name") displayName: RequestBody?, @Part(value = "note") note: RequestBody?, @Part(value = "locked") locked: RequestBody?, @Part avatar: MultipartBody.Part?, @Part header: MultipartBody.Part?, @Part(value = "fields_attributes[0][name]") fieldName0: RequestBody?, @Part(value = "fields_attributes[0][value]") fieldValue0: RequestBody?, @Part(value = "fields_attributes[1][name]") fieldName1: RequestBody?, @Part(value = "fields_attributes[1][value]") fieldValue1: RequestBody?, @Part(value = "fields_attributes[2][name]") fieldName2: RequestBody?, @Part(value = "fields_attributes[2][value]") fieldValue2: RequestBody?, @Part(value = "fields_attributes[3][name]") fieldName3: RequestBody?, @Part(value = "fields_attributes[3][value]") fieldValue3: RequestBody? ): Call @GET("api/v1/accounts/search") fun searchAccounts( @Query("q") query: String, @Query("resolve") resolve: Boolean? = null, @Query("limit") limit: Int? = null, @Query("following") following: Boolean? = null ): Single> @GET("api/v1/accounts/{id}") fun account( @Path("id") accountId: String ): Call /** * Method to fetch statuses for the specified account. * @param accountId ID for account for which statuses will be requested * @param maxId Only statuses with ID less than maxID will be returned * @param sinceId Only statuses with ID bigger than sinceID will be returned * @param limit Limit returned statuses (current API limits: default - 20, max - 40) * @param excludeReplies only return statuses that are no replies * @param onlyMedia only return statuses that have media attached */ @GET("api/v1/accounts/{id}/statuses") fun accountStatuses( @Path("id") accountId: String, @Query("max_id") maxId: String?, @Query("since_id") sinceId: String?, @Query("limit") limit: Int?, @Query("exclude_replies") excludeReplies: Boolean?, @Query("only_media") onlyMedia: Boolean?, @Query("pinned") pinned: Boolean? ): Call> @GET("api/v1/accounts/{id}/followers") fun accountFollowers( @Path("id") accountId: String, @Query("max_id") maxId: String? ): Single>> @GET("api/v1/accounts/{id}/following") fun accountFollowing( @Path("id") accountId: String, @Query("max_id") maxId: String? ): Single>> @FormUrlEncoded @POST("api/v1/accounts/{id}/follow") fun followAccount( @Path("id") accountId: String, @Field("reblogs") showReblogs: Boolean ): Call @POST("api/v1/accounts/{id}/unfollow") fun unfollowAccount( @Path("id") accountId: String ): Call @POST("api/v1/accounts/{id}/block") fun blockAccount( @Path("id") accountId: String ): Call @POST("api/v1/accounts/{id}/unblock") fun unblockAccount( @Path("id") accountId: String ): Call @POST("api/v1/accounts/{id}/mute") fun muteAccount( @Path("id") accountId: String ): Call @POST("api/v1/accounts/{id}/unmute") fun unmuteAccount( @Path("id") accountId: String ): Call @GET("api/v1/accounts/relationships") fun relationships( @Query("id[]") accountIds: List ): Call> @GET("api/v1/accounts/{id}/identity_proofs") fun identityProofs( @Path("id") accountId: String ): Call> @GET("api/v1/blocks") fun blocks( @Query("max_id") maxId: String? ): Single>> @GET("api/v1/mutes") fun mutes( @Query("max_id") maxId: String? ): Single>> @GET("api/v1/domain_blocks") fun domainBlocks( @Query("max_id") maxId: String? = null, @Query("since_id") sinceId: String? = null, @Query("limit") limit: Int? = null ): Single>> @FormUrlEncoded @POST("api/v1/domain_blocks") fun blockDomain( @Field("domain") domain: String ): Call @FormUrlEncoded // @DELETE doesn't support fields @HTTP(method = "DELETE", path = "api/v1/domain_blocks", hasBody = true) fun unblockDomain(@Field("domain") domain: String): Call @GET("api/v1/favourites") fun favourites( @Query("max_id") maxId: String?, @Query("since_id") sinceId: String?, @Query("limit") limit: Int? ): Call> @GET("api/v1/bookmarks") fun bookmarks( @Query("max_id") maxId: String?, @Query("since_id") sinceId: String?, @Query("limit") limit: Int? ): Call> @GET("api/v1/follow_requests") fun followRequests( @Query("max_id") maxId: String? ): Single>> @POST("api/v1/follow_requests/{id}/authorize") fun authorizeFollowRequest( @Path("id") accountId: String ): Call @POST("api/v1/follow_requests/{id}/reject") fun rejectFollowRequest( @Path("id") accountId: String ): Call @POST("api/v1/follow_requests/{id}/authorize") fun authorizeFollowRequestObservable( @Path("id") accountId: String ): Single @POST("api/v1/follow_requests/{id}/reject") fun rejectFollowRequestObservable( @Path("id") accountId: String ): Single @FormUrlEncoded @POST("api/v1/apps") fun authenticateApp( @Header(DOMAIN_HEADER) domain: String, @Field("client_name") clientName: String, @Field("redirect_uris") redirectUris: String, @Field("scopes") scopes: String, @Field("website") website: String ): Call @FormUrlEncoded @POST("oauth/token") fun fetchOAuthToken( @Header(DOMAIN_HEADER) domain: String, @Field("client_id") clientId: String, @Field("client_secret") clientSecret: String, @Field("redirect_uri") redirectUri: String, @Field("code") code: String, @Field("grant_type") grantType: String ): Call @FormUrlEncoded @POST("api/v1/lists") fun createList( @Field("title") title: String ): Single @FormUrlEncoded @PUT("api/v1/lists/{listId}") fun updateList( @Path("listId") listId: String, @Field("title") title: String ): Single @DELETE("api/v1/lists/{listId}") fun deleteList( @Path("listId") listId: String ): Completable @GET("api/v1/lists/{listId}/accounts") fun getAccountsInList( @Path("listId") listId: String, @Query("limit") limit: Int ): Single> @FormUrlEncoded // @DELETE doesn't support fields @HTTP(method = "DELETE", path = "api/v1/lists/{listId}/accounts", hasBody = true) fun deleteAccountFromList( @Path("listId") listId: String, @Field("account_ids[]") accountIds: List ): Completable @FormUrlEncoded @POST("api/v1/lists/{listId}/accounts") fun addCountToList( @Path("listId") listId: String, @Field("account_ids[]") accountIds: List ): Completable @GET("/api/v1/conversations") fun getConversations( @Query("max_id") maxId: String? = null, @Query("limit") limit: Int ): Call> @FormUrlEncoded @POST("api/v1/filters") fun createFilter( @Field("phrase") phrase: String, @Field("context[]") context: List, @Field("irreversible") irreversible: Boolean?, @Field("whole_word") wholeWord: Boolean?, @Field("expires_in") expiresIn: String? ): Call @FormUrlEncoded @PUT("api/v1/filters/{id}") fun updateFilter( @Path("id") id: String, @Field("phrase") phrase: String, @Field("context[]") context: List, @Field("irreversible") irreversible: Boolean?, @Field("whole_word") wholeWord: Boolean?, @Field("expires_in") expiresIn: String? ): Call @DELETE("api/v1/filters/{id}") fun deleteFilter( @Path("id") id: String ): Call @FormUrlEncoded @POST("api/v1/polls/{id}/votes") fun voteInPoll( @Path("id") id: String, @Field("choices[]") choices: List ): Single @GET("api/v1/announcements") fun listAnnouncements(): Single> @POST("api/v1/accounts/{id}/block") fun blockAccountObservable( @Path("id") accountId: String ): Single @POST("api/v1/accounts/{id}/unblock") fun unblockAccountObservable( @Path("id") accountId: String ): Single @POST("api/v1/accounts/{id}/mute") fun muteAccountObservable( @Path("id") accountId: String ): Single @POST("api/v1/accounts/{id}/unmute") fun unmuteAccountObservable( @Path("id") accountId: String ): Single @GET("api/v1/accounts/relationships") fun relationshipsObservable( @Query("id[]") accountIds: List ): Single> @FormUrlEncoded @POST("api/v1/reports") fun reportObservable( @Field("account_id") accountId: String, @Field("status_ids[]") statusIds: List, @Field("comment") comment: String, @Field("forward") isNotifyRemote: Boolean? ): Single @GET("api/v1/accounts/{id}/statuses") fun accountStatusesObservable( @Path("id") accountId: String, @Query("max_id") maxId: String?, @Query("since_id") sinceId: String?, @Query("limit") limit: Int?, @Query("exclude_reblogs") excludeReblogs: Boolean? ): Single> @GET("api/v1/statuses/{id}") fun statusObservable( @Path("id") statusId: String ): Single @GET("api/v2/search") fun searchObservable( @Query("q") query: String?, @Query("type") type: String? = null, @Query("resolve") resolve: Boolean? = null, @Query("limit") limit: Int? = null, @Query("offset") offset: Int? = null, @Query("following") following: Boolean? = null ): Single }