billstclair / elm-mastodon / Mastodon.Request

Types to represent the Mastodon REST API.

Funcion to generate a request and parse the return.

Documentation starts at https://docs.joinmastodon.org/api/rest/accounts

Basic Types


type alias ServerInfo =
{ server : String
, token : Maybe String 
}

Used to create the HTTP URL and fill in its authentication token.

It's the host name for the URL.

Example server: "mastodon.social".

A few requests do not require a token. Most do, and will error if you don't include one.


type Request
    = AccountsRequest AccountsReq
    | AppsRequest AppsReq
    | BlocksRequest BlocksReq
    | CustomEmojisRequest CustomEmojisReq
    | DomainBlocksRequest DomainBlocksReq
    | EndorsementsRequest EndorsementsReq
    | FavouritesRequest FavouritesReq
    | FiltersRequest FiltersReq
    | FollowRequestsRequest FollowRequestsReq
    | FollowSuggestionsRequest FollowSuggestionsReq
    | GroupsRequest GroupsReq
    | InstanceRequest InstanceReq
    | ListsRequest ListsReq
    | MediaAttachmentsRequest MediaAttachmentsReq
    | MutesRequest MutesReq
    | NotificationsRequest NotificationsReq
    | PollsRequest PollsReq
    | ReportsRequest ReportsReq
    | ScheduledStatusesRequest ScheduledStatusesReq
    | SearchRequest SearchReq
    | StatusesRequest StatusesReq
    | TimelinesRequest TimelinesReq
    | TrendsRequest TrendsReq

An API request.

Broken down as in the documentation.

Result entity types are documented with the various "xxxReq" types.


type alias Response =
{ request : Request
, rawRequest : RawRequest
, metadata : Http.Metadata
, entity : Mastodon.Entity.Entity 
}

A response from an API request.

The request is a copy of the Request that was sent over the wire, for cases where that isn't obvious from the Entity tag.

The metadata is Http.Metadata for a successful request, mostly so you can get to the headers, if you need them.


type Error
    = BadUrl String
    | Timeout
    | NetworkError
    | BadStatus Http.Metadata String
    | BadBody Http.Metadata Json.Decode.Error String

Encodes an error from the server request.

Same as Http.Error, but includes Http.Metadata when it's available.

The String in a BadStatus is the Http error message, usually HTML.

The Error in BadBody is a Json.Decode.Error. The String in BadBody is the JSON string returned by the Http request.

Creating an HTTP request

serverRequest : (id -> Result Error Response -> msg) -> List Http.Header -> ServerInfo -> id -> Request -> Platform.Cmd.Cmd msg

Create an HTTP request for the server.

The id is whatever you need, besides the Request, to identify the returned Error or Response.

You will often pass [] for headers, but including a "User-Agent" header is usually a good idea. For example, https://mammudeck.com uses:

[ Mastodon.Request.userAgentHeader "Mammudeck" ]

Request details


type alias PostedStatus =
{ status : Maybe String
, in_reply_to_id : Maybe String
, group_id : Maybe String
, quote_of_id : Maybe String
, media_ids : List String
, poll : Maybe Mastodon.Entity.PollDefinition
, sensitive : Basics.Bool
, spoiler_text : Maybe String
, visibility : Maybe Mastodon.Entity.Visibility
, content_type : Maybe String
, scheduled_at : Maybe Mastodon.Entity.Datetime
, language : Maybe Mastodon.Entity.ISO6391
, idempotencyKey : Maybe String 
}

Fields for a new Status


type alias EditedStatus =
{ status : Maybe String
, in_reply_to_id : Maybe String
, quote_of_id : Maybe String
, media_ids : Maybe (List String)
, sensitive : Basics.Bool
, spoiler_text : Maybe String
, visibility : Maybe Mastodon.Entity.Visibility
, content_type : Maybe String
, poll : Maybe Mastodon.Entity.PollDefinition
, scheduled_at : Maybe Mastodon.Entity.Datetime 
}

Fields for an edited Status.

Parameter to Request.PutStatus.

content_type is "text/plain" or "text/markdown"

Sent over the wire to a Rebased server, from Soapbox:

{"status":"A status to edit with Mammudeck..."
,"in_reply_to_id":null
,"quote_id":null
,"media_ids":[]
,"sensitive":false
,"spoiler_text":""
,"visibility":"public"
,"content_type":"text/markdown"
,"poll":null
,"scheduled_at":null
,"to":[]
}


type StatusesReq
    = GetStatus ({ id : String })
    | GetStatusSource ({ id : String })
    | GetStatusContext ({ id : String })
    | GetStatusPartialContext ({ which : PartialContext, id : String, offset : Maybe Basics.Int })
    | GetStatusCard ({ id : String })
    | GetStatusRebloggedBy ({ id : String, limit : Maybe Basics.Int })
    | GetStatusFavouritedBy ({ id : String, limit : Maybe Basics.Int })
    | GetStatusHistory ({ id : String })
    | PostStatus PostedStatus
    | PutStatus ({ id : String, status : EditedStatus })
    | DeleteStatus ({ id : String })
    | PostReblogStatus ({ id : String })
    | PostUnreblogStatus ({ id : String })
    | PostPinStatus ({ id : String })
    | PostUnpinStatus ({ id : String })
    | PostTranslate ({ id : String, target_language : String })

GET/POST /api/v1/statuses

GetStatus, PostStatus, PutStatus, PostReblogStatus, PostUnreblogStatus, PostPinStatus, and PostUnpinStatus result in a StatusEntity.

GetStatusSource results in a StatusSourceEntity

GetStatusHistory results in a HistoryStatusListEntity

GetStatusContext results in a ContextEntity.

GetStatusPartialContext results in a StatusListEntity. It is supported only by TruthSocial.com. The offset parameter is for DescendantsContext only. It is used to page through responses.

GetStatusCard results in a CardEntity.

GetStatusRebloggedBy and GetStatusFavouritedBy result in an AccountListEntity.

DeleteStatus results in NoEntity.

The GetXxx requests require no authentication token.


type TimelinesReq
    = GetHomeTimeline ({ paging : Maybe Paging })
    | GetConversations ({ paging : Maybe Paging })
    | GetPublicTimeline ({ local : Basics.Bool, only_media : Basics.Bool, paging : Maybe Paging })
    | GetProTimeline ({ only_media : Basics.Bool, paging : Maybe Paging })
    | GetTagTimeline ({ hashtag : String, local : Basics.Bool, only_media : Basics.Bool, paging : Maybe Paging })
    | GetListTimeline ({ list_id : String, paging : Maybe Paging })
    | GetGroupTimeline ({ group_id : String, paging : Maybe Paging })

GET/POST /api/v1/timelines

GetHomeTimeline, GetPublicTimeline, GetProTimeline, GetTagTimeline, and GetListTimeline result in a StatusListEntity.

GetConversations results in a ConversationListEntity.

GetPublicTimeline and GetTagTimeline do not require an authentication token.


type AccountsReq
    = GetVerifyCredentials
    | GetAccountByUsername ({ username : String })
    | GetAccount ({ id : String })
    | PatchUpdateCredentials ({ display_name : Maybe String, note : Maybe String, avatar : Maybe File, header : Maybe File, locked : Maybe Basics.Bool, source : Maybe SourceUpdate, fields_attributes : Maybe (List FieldUpdate) })
    | GetFollowers ({ id : String, limit : Maybe Basics.Int })
    | GetFollowing ({ id : String, limit : Maybe Basics.Int })
    | GetStatuses ({ id : String, only_media : Basics.Bool, pinned : Basics.Bool, exclude_replies : Basics.Bool, paging : Maybe Paging, exclude_reblogs : Basics.Bool })
    | PostFollow ({ id : String, reblogs : Basics.Bool })
    | PostUnfollow ({ id : String })
    | GetRelationships ({ ids : List String })
    | GetSearchAccounts ({ q : String, limit : Maybe Basics.Int, resolve : Basics.Bool, following : Basics.Bool })

GET/POST/PATCH /api/v1/accounts

(and GET /api/v1/account_by_username)

GetAccountByUsername and GetAccount do not require an authentication token.

GetAccountByUsername, GetAccount, GetVerifyCredentials, and PatchUpdateCredentials result in an AccountEntity.

GetFollowers, GetFollowing, and GetSearchAccounts result in an AccountListEntity.

GetStatuses results in a StatusListEntity.

PostFollow and PostUnfollow result in a RelationshipEntity.

GetRelationships results in a RelationshipListEntity.

GetAccountByUsername (GET account_by_username) is not documented, and may be Gab-only.

The fields_attributes list in PatchUpdateCredentials will be silently shortened to four elements if it's longer than that. If it's shorter than four elements, then the fields past those specified will be cleared.


type AppsReq
    = PostApp ({ client_name : String, redirect_uris : String, scopes : List String, website : Maybe String })
    | GetVerifyAppCredentials

GET/POST /api/v1/apps

PostApp results in an AppEntity.

These are not associated with your account on Mastodon servers, but they need to be deleted when you're done with them on Pleroma servers (if you use one to get a token, and you wouldn't bother to make it except for that). I don't know yet how to delete them, except in the Pleroma server's web API (the "Security" tab at https://<pleroma-server.com>/user-settings).

You will rarely use PostApp directly, instead allowing the functions in the Mastodon.Login module to do that for you.


type BlocksReq
    = GetBlocks ({ limit : Maybe Basics.Int })
    | PostBlock ({ id : String })
    | PostUnblock ({ id : String })

GET/POST /api/v1/blocks

GetBlocks results in an AccountListEntity.

PostBlock and PostUnblock result in a RelationshipEntity.


type CustomEmojisReq
    = GetCustomEmojis

GET /api/v8/custom_emojis

GetCustomEmojis results in an EmojiListEntity.


type DomainBlocksReq
    = GetDomainBlocks ({ limit : Maybe Basics.Int })
    | PostDomainBlock ({ domain : String })
    | DeleteDomainBlock ({ domain : String })

GET/POST /api/v8/domain_blocks

GetDomainBlocks results in a StringListEntity, a list of domain names.

PostDomainBlock and DeleteDomainBlock result in NoEntity.


type EndorsementsReq
    = GetEndorsements
    | PostPinAccount ({ id : String })
    | PostUnpinAccount ({ id : String })

GET/POST /api/v1/endorsements

GetEndorsements results in an AccountListEntity.

PostPinAccount and PostUnpinAccount result in a RelationshipEntity.


type FavouritesReq
    = GetFavourites ({ limit : Maybe Basics.Int })
    | PostFavourite ({ id : String })
    | PostUnfavourite ({ id : String })

GET/POST /api/v1/favourites

GetFavourites results in a StatusListEntity.

PostFavourite and PostUnfavorite result in a StatusEntity.


type FiltersReq
    = GetFilters
    | PostFilter ({ phrase : String, context : List Mastodon.Entity.FilterContext, irreversible : Basics.Bool, whole_word : Basics.Bool, expires_in : Maybe Basics.Int })
    | GetFilter ({ id : String })
    | PutFilter ({ id : String, phrase : String, context : List Mastodon.Entity.FilterContext, irreversible : Basics.Bool, whole_word : Basics.Bool, expires_in : Maybe Basics.Int })
    | DeleteFilter ({ id : String })

GET/POST/PUT /api/v1/filters

GetFilters results in a FilterListEntity.

PostFilter, GetFilter, and PutFilter result in a FilterEntity.

DeleteFilter results in NoEntity.


type FollowRequestsReq
    = GetFollowRequests ({ limit : Maybe Basics.Int })
    | PostAuthorizeFollow ({ id : String })
    | PostRejectFollow ({ id : String })

GET/POST /api/v1/follow_requests

GetFollowRequests results in an AccountListEntity.

PostAuthorizeFollow and PostRejectFollow result in NoEntity.


type FollowSuggestionsReq
    = GetFollowSuggestions
    | DeleteFollowSuggestions ({ account_id : String })

GET/DELETE /api/v1/suggestions

GetFollowSuggestions results in an AccountListEntity.

DeleteFollowSuggestions results in NoEntity.


type GroupsReq
    = GetGroups ({ tab : WhichGroups })
    | GetGroup ({ id : String })
    | GetGroupAccounts ({ id : String })
    | PostGroup ({ title : String, description : String, cover_image : Maybe File })
    | PutGroup ({ id : String, title : Maybe String, description : Maybe String, cover_image : Maybe File })
    | GetGroupRelationships ({ ids : List String })
    | PostGroupJoin ({ id : String })
    | DeleteGroupJoin ({ id : String })
    | DeleteGroupStatus ({ id : String, status_id : String })
    | GetGroupRemovedAccounts ({ id : String })
    | PostGroupRemovedAccounts ({ id : String, account_id : String })
    | DeleteGroupRemovedAccounts ({ id : String, account_id : String })
    | PatchGroupAddAdministrator ({ id : String, account_id : String })

GET /api/v1/groups

The groups API is Gab-only.

GetGroups results in a GroupListEntity.

GetGroup, PostGroup, and PutGroup result in a GroupEntity

GetGroupRelationships results in a GroupRelationshipListEntity.

GetGroupAccounts and GetGroupRemovedAccounts result in an AccountListEntity.

PostGroupJoin results in a GroupRelationshipEntity.

DeleteGroupJoin, DeleteGroupStatus, PostGroupRemovedAccounts, DeleteGroupRemovedAccounts, and PatchGroupAddAdministrator result in NoEntity.

Descriptions

GetGroups fetches the list of all groups of which the logged-in account is a member.

GetGroup fetches one group.

GetGroupAccounts returns the members of a group.

GetGroupRelationships gets the logged-in account relationships for one or more groups.

PostGroup creates a new group.

PutGroup updates the group profile information.

PostGroupJoin joins a group from the logged-in account.

DeletGroupJoin leaves a group from the logged-in account.

DeleteGroupStatus removes a status from the group.

GetGroupRemovedAccounts returns the list of removed accounts for a group.

PostGroupRemovedAccounts revokes group membership for an account.

DeleteGroupRemovedAccounts removes a previously revoked membership from the list of deleted accounts.

PatchGroupAddAdministrator adds an administrator to a group. There is currently no way to remove an administrator, except to remove the account from the group.


type InstanceReq
    = GetInstance
    | GetActivity
    | GetPeers

GET /api/v1/instance


type ListsReq
    = GetLists
    | GetAccountLists ({ id : String })
    | GetListAccounts ({ id : String, limit : Maybe Basics.Int })
    | GetList ({ id : String })
    | PostList ({ title : String })
    | PutList ({ id : String, title : String })
    | DeleteList ({ id : String })
    | PostListAccounts ({ id : String, account_ids : List String })
    | DeleteListAccounts ({ id : String, account_ids : List String })

GET/POST/PUT/DELETE /api/v1/lists

GetLists and GetAccountLists result in a ListEntityListEntity.

GetListAccounts results in an AccountListEntity.

GetList, PostList, and PutList result in a ListEntity.

PostListAccounts and DeleteListAccounts result in NoEntity.


type MediaAttachmentsReq
    = PostMedia ({ file : File, description : Maybe String, focus : Maybe Mastodon.Entity.Focus })
    | PutMedia ({ id : String, description : Maybe String, focus : Maybe Mastodon.Entity.Focus })

GET/POST /api/v1/media

PostMedia and PutMedia result in an AttachmentEntity.


type MutesReq
    = GetAccountMutes ({ limit : Maybe Basics.Int })
    | PostAccountMute ({ id : String, notifications : Basics.Bool })
    | PostAccountUnmute ({ id : String })
    | PostStatusMute ({ id : String })
    | PostStatusUnmute ({ id : String })

GET/POST /api/v1/mutes

GetAccountMutes results in an AccountListEntity.

PostAccountMute and PostAccountUnmute result in a RelationshipEntity.

PostStatusMute and PostStatusUnmute result in a StatusEntity.


type NotificationsReq
    = GetNotifications ({ paging : Maybe Paging, exclude_types : List Mastodon.Entity.NotificationType, account_id : Maybe String })
    | GetNotification ({ id : String })
    | PostClearNotifications
    | PostDismissNotification ({ id : String })

GET/POST /api/v1/notifications

GetNotifications results in a NotificationListEntity.

GetNotification results in NotificationEntity.

PostClearNotifications and PostDismissNotifications result in NoEntity.

This doesn't yet define requests for "POST /api/v1/push/subscription", "GET /api/v1/push/subscription", "PUT /api/v1/push/subscription", or "DELETE /api/v1/push/subscription".


type PollsReq
    = GetPoll ({ id : String })
    | PostVotes ({ id : String, choices : List Basics.Int })

GET/POST /api/v1/polls

GetPoll and PostVotes result in a PollEntity.

GetPoll does not require an authentication token.


type ReportsReq
    = PostReports ({ account_id : String, status_ids : List String, comment : Maybe String, forward : Basics.Bool })

POST /api/v1/reports

PostReports results in NoInstance.


type ScheduledStatusesReq
    = GetScheduledStatuses
    | GetScheduledStatus ({ id : String })
    | PutScheduledStatus ({ id : String, scheduled_at : Maybe Mastodon.Entity.UnixTimestamp })
    | DeleteScheduledStatus ({ id : String })

GET/PUT /api/v1/scheduled_statuses

GetScheduledStatuses results in a ScheduledStatusListInstance.

GetScheduledStatus and PutScheduledStatus result in a ScheduledStatusInstance.

DeleteScheduledStatus results in NoInstance.


type SearchReq
    = GetSearch ({ q : String, resolve : Basics.Bool, limit : Maybe Basics.Int, offset : Maybe Basics.Int, following : Basics.Bool })

GET/POST /api/v1/search

GetSearch results in a ResultsInstance.


type TrendsReq
    = GetTrends

GET /api/v1/trends

GetTrends results in a TagListEntity.

Non-atomic data in requests


type alias Paging =
{ max_id : Maybe String
, since_id : Maybe String
, min_id : Maybe String
, limit : Maybe Basics.Int 
}

Parameters to control paging for requests that return lists.


type alias SourceUpdate =
{ privacy : Maybe Mastodon.Entity.Privacy
, sensitive : Maybe Basics.Bool
, language : Maybe (Maybe Mastodon.Entity.ISO6391) 
}

Updated account Source information


type alias FieldUpdate =
{ name : String
, value : Mastodon.Entity.HtmlString 
}

Updated account Field information.


type WhichGroups
    = MemberGroups
    | FeaturedGroups
    | AdminGroups

Which groups to return from GetGroups { tab : WhichGroups }


type PartialContext
    = AncestorsContext
    | DescendantsContext

Which context should GetStatusPartialContext return?

Utility

userAgentHeader : String -> Http.Header

Only required by GitHub that I know of, but can't hurt.

Pass whatever string describes your user agent. Or fake a common one.

idempotencyKeyHeader : String -> Http.Header

Create an "Idempotency-Key" header for use with PostStatus.

Usually, you will create this header by passing Just key for the idempotencyKey property of the parameter to PostStatus, but if you need to create it in another context, use this.

emptyPaging : Paging

The default Paging instance, with no restrictions.

simplePostStatus : String -> Maybe String -> Maybe String -> Request

Create a PostStatus request using only the most common non-blank fields.

Parameters are:

simplePostStatus status in_reply_to_id spoiler_text

Low-level request creation


type alias RawRequest =
{ method : String
, token : Maybe String
, url : String
, headers : List Http.Header
, body : Http.Body
, request : Request
, jsonBody : Maybe Json.Encode.Value
, decoder : Json.Decode.Decoder Mastodon.Entity.Entity 
}

Represent an HTTP request.

Usually, you will let serverRequest create one of these internally.

Sometimes, however, you need to create one yourself, or call requestToRawRequest to make one, make changes to it, and then call rawRequestToCmd.

requestToRawRequest : List Http.Header -> ServerInfo -> Request -> RawRequest

Convert a Request into a RawRequest.

You will usually not call this yourself, but let serverRequest do it internally.

Sometimes, however, you need to call this to create a RawRequest, modify it, and then call rawRequestToCmd to turn it into a Cmd.

rawRequestToCmd : (Result Error Response -> msg) -> RawRequest -> Platform.Cmd.Cmd msg

Convert a RawRequest into a Cmd.

You will usually not call this yourself, but let serverRequest do it internally.

Sometimes, however, you need to create a RawRequest, by hand or by calling requestToRawRequest, then pass it here to turn it into a Cmd.

rawRequestToTask : RawRequest -> Task Error Response

Same as rawRequestToCmd, but returns a Task.

rawRequestToCmd could be defined as:

rawRequestToCmd : (Result Error Response -> msg) -> RawRequest -> Cmd msg
rawRequestToCmd tagger rawRequest =
    Task.attempt tagger <|
        rawRequestToTask rawRequest

Testing

emptyRawRequest : RawRequest

An empty raw request.

Exposed only for testing in elm repl.

emptyServerInfo : ServerInfo

For testing in elm repl.