338 líneas
9.3 KiB
Markdown
338 líneas
9.3 KiB
Markdown
# API Documentation
|
|
|
|
## Overview
|
|
|
|
This document describes the Mastodon API integration used in My ActivityPub app. The app communicates with Mastodon-compatible servers using their REST API v1.
|
|
|
|
## Base URL
|
|
|
|
Default: `https://mastodon.social/`
|
|
|
|
You can configure this to any Mastodon or ActivityPub-compatible instance.
|
|
|
|
## Authentication
|
|
|
|
Currently, the app accesses public endpoints that don't require authentication. Future versions will implement OAuth 2.0 for authenticated requests.
|
|
|
|
## Endpoints
|
|
|
|
### 1. Get Public Timeline
|
|
|
|
Retrieve statuses from the public timeline.
|
|
|
|
**Endpoint**: `GET /api/v1/timelines/public`
|
|
|
|
**Parameters**:
|
|
- `limit` (integer, optional): Maximum number of results. Default: 20
|
|
- `local` (boolean, optional): Show only local statuses. Default: false
|
|
- `max_id` (string, optional): Return results older than this ID
|
|
- `since_id` (string, optional): Return results newer than this ID
|
|
- `min_id` (string, optional): Return results immediately newer than this ID
|
|
|
|
**Example Request**:
|
|
```
|
|
GET https://mastodon.social/api/v1/timelines/public?limit=20&local=false
|
|
```
|
|
|
|
**Response**: Array of Status objects
|
|
|
|
**Example Response**:
|
|
```json
|
|
[
|
|
{
|
|
"id": "109382159165398564",
|
|
"created_at": "2022-11-23T07:49:01.940Z",
|
|
"content": "<p>Hello world!</p>",
|
|
"account": {
|
|
"id": "109382",
|
|
"username": "alice",
|
|
"display_name": "Alice",
|
|
"avatar": "https://...",
|
|
...
|
|
},
|
|
"media_attachments": [],
|
|
"favourites_count": 5,
|
|
"reblogs_count": 2,
|
|
"replies_count": 1,
|
|
...
|
|
}
|
|
]
|
|
```
|
|
|
|
### 2. Get Instance Information
|
|
|
|
Get information about the Mastodon instance.
|
|
|
|
**Endpoint**: `GET /api/v1/instance`
|
|
|
|
**Parameters**: None
|
|
|
|
**Example Request**:
|
|
```
|
|
GET https://mastodon.social/api/v1/instance
|
|
```
|
|
|
|
**Response**: Instance object
|
|
|
|
**Example Response**:
|
|
```json
|
|
{
|
|
"uri": "mastodon.social",
|
|
"title": "Mastodon",
|
|
"short_description": "The original server operated by the Mastodon gGmbH non-profit",
|
|
"description": "...",
|
|
"version": "4.0.0",
|
|
"languages": ["en"],
|
|
"thumbnail": "https://..."
|
|
}
|
|
```
|
|
|
|
### 3. Get Account
|
|
|
|
Get account information.
|
|
|
|
**Endpoint**: `GET /api/v1/accounts/:id`
|
|
|
|
**Parameters**:
|
|
- `id` (string, required): Account ID
|
|
|
|
**Example Request**:
|
|
```
|
|
GET https://mastodon.social/api/v1/accounts/109382
|
|
```
|
|
|
|
**Response**: Account object
|
|
|
|
**Example Response**:
|
|
```json
|
|
{
|
|
"id": "109382",
|
|
"username": "alice",
|
|
"acct": "alice",
|
|
"display_name": "Alice",
|
|
"locked": false,
|
|
"bot": false,
|
|
"created_at": "2022-11-23T07:49:01.940Z",
|
|
"note": "<p>Bio goes here</p>",
|
|
"url": "https://mastodon.social/@alice",
|
|
"avatar": "https://...",
|
|
"header": "https://...",
|
|
"followers_count": 100,
|
|
"following_count": 50,
|
|
"statuses_count": 500
|
|
}
|
|
```
|
|
|
|
### 4. Get Account Statuses
|
|
|
|
Get statuses posted by an account.
|
|
|
|
**Endpoint**: `GET /api/v1/accounts/:id/statuses`
|
|
|
|
**Parameters**:
|
|
- `id` (string, required): Account ID
|
|
- `limit` (integer, optional): Maximum number of results. Default: 20
|
|
- `max_id` (string, optional): Return results older than this ID
|
|
- `since_id` (string, optional): Return results newer than this ID
|
|
- `exclude_replies` (boolean, optional): Skip statuses that reply to other statuses
|
|
- `exclude_reblogs` (boolean, optional): Skip statuses that are reblogs of other statuses
|
|
- `only_media` (boolean, optional): Show only statuses with media attached
|
|
|
|
**Example Request**:
|
|
```
|
|
GET https://mastodon.social/api/v1/accounts/109382/statuses?limit=20
|
|
```
|
|
|
|
**Response**: Array of Status objects
|
|
|
|
## Data Models
|
|
|
|
### Status
|
|
|
|
Represents a post/toot on Mastodon.
|
|
|
|
**Properties**:
|
|
```typescript
|
|
{
|
|
id: string // Unique identifier
|
|
created_at: string // ISO 8601 datetime
|
|
content: string // HTML content
|
|
account: Account // Account that posted this status
|
|
media_attachments: Array // Media attachments
|
|
reblog: Status | null // If this is a reblog, the original status
|
|
favourites_count: number // Number of favorites
|
|
reblogs_count: number // Number of reblogs/boosts
|
|
replies_count: number // Number of replies
|
|
favourited: boolean // Has the current user favorited this?
|
|
reblogged: boolean // Has the current user reblogged this?
|
|
url: string | null // URL to the status
|
|
visibility: string // Visibility level (public, unlisted, private, direct)
|
|
}
|
|
```
|
|
|
|
### Account
|
|
|
|
Represents a user account.
|
|
|
|
**Properties**:
|
|
```typescript
|
|
{
|
|
id: string // Unique identifier
|
|
username: string // Username (without @domain)
|
|
acct: string // Full username (@username@domain)
|
|
display_name: string // Display name
|
|
avatar: string // URL to avatar image
|
|
header: string // URL to header image
|
|
note: string // Bio/description (HTML)
|
|
url: string | null // URL to profile page
|
|
followers_count: number // Number of followers
|
|
following_count: number // Number of accounts being followed
|
|
statuses_count: number // Number of statuses posted
|
|
bot: boolean // Is this a bot account?
|
|
locked: boolean // Does this account require follow requests?
|
|
}
|
|
```
|
|
|
|
### MediaAttachment
|
|
|
|
Represents media files attached to statuses.
|
|
|
|
**Properties**:
|
|
```typescript
|
|
{
|
|
id: string // Unique identifier
|
|
type: string // Type: image, video, gifv, audio, unknown
|
|
url: string // URL to the media file
|
|
preview_url: string | null // URL to the preview/thumbnail
|
|
remote_url: string | null // Remote URL if the media is from another server
|
|
description: string | null // Alt text description
|
|
}
|
|
```
|
|
|
|
### Instance
|
|
|
|
Represents a Mastodon instance.
|
|
|
|
**Properties**:
|
|
```typescript
|
|
{
|
|
uri: string // Domain name
|
|
title: string // Instance name
|
|
description: string // Long description (HTML)
|
|
short_description: string // Short description (plaintext)
|
|
version: string // Mastodon version
|
|
thumbnail: string | null // URL to thumbnail image
|
|
languages: Array<string> // ISO 639 Part 1-5 language codes
|
|
email: string | null // Contact email
|
|
contact_account: Account // Contact account
|
|
}
|
|
```
|
|
|
|
## Error Handling
|
|
|
|
The API returns standard HTTP status codes:
|
|
|
|
- `200 OK` - Request succeeded
|
|
- `400 Bad Request` - Invalid parameters
|
|
- `401 Unauthorized` - Authentication required
|
|
- `403 Forbidden` - Access denied
|
|
- `404 Not Found` - Resource not found
|
|
- `429 Too Many Requests` - Rate limit exceeded
|
|
- `500 Internal Server Error` - Server error
|
|
- `503 Service Unavailable` - Server temporarily unavailable
|
|
|
|
**Error Response Format**:
|
|
```json
|
|
{
|
|
"error": "Error message here"
|
|
}
|
|
```
|
|
|
|
## Rate Limiting
|
|
|
|
Mastodon implements rate limiting to prevent abuse. Limits vary by instance but typically:
|
|
|
|
- 300 requests per 5 minutes for authenticated requests
|
|
- Lower limits for unauthenticated requests
|
|
|
|
Rate limit information is returned in response headers:
|
|
- `X-RateLimit-Limit` - Maximum number of requests
|
|
- `X-RateLimit-Remaining` - Remaining requests in current window
|
|
- `X-RateLimit-Reset` - Time when the limit resets (ISO 8601)
|
|
|
|
## Best Practices
|
|
|
|
1. **Respect Rate Limits**: Implement exponential backoff when hitting rate limits
|
|
2. **Cache Responses**: Cache instance info and other static data
|
|
3. **Use Pagination**: Use `max_id` and `since_id` for efficient pagination
|
|
4. **Handle Errors**: Always handle network errors and API errors gracefully
|
|
5. **Validate Input**: Validate user input before making API calls
|
|
6. **Use HTTPS**: Always use HTTPS for API requests
|
|
7. **Set User-Agent**: Include a descriptive User-Agent header
|
|
|
|
## Implementation in the App
|
|
|
|
### Service Layer
|
|
|
|
`MastodonApiService.kt` defines the API interface using Retrofit annotations:
|
|
|
|
```kotlin
|
|
interface MastodonApiService {
|
|
@GET("api/v1/timelines/public")
|
|
suspend fun getPublicTimeline(
|
|
@Query("limit") limit: Int = 20,
|
|
@Query("local") local: Boolean = false
|
|
): Response<List<Status>>
|
|
|
|
// Other endpoints...
|
|
}
|
|
```
|
|
|
|
### Repository Layer
|
|
|
|
`MastodonRepository.kt` wraps API calls with error handling:
|
|
|
|
```kotlin
|
|
suspend fun getPublicTimeline(limit: Int = 20, local: Boolean = false): Result<List<Status>> {
|
|
return withContext(Dispatchers.IO) {
|
|
try {
|
|
val response = apiService.getPublicTimeline(limit, local)
|
|
if (response.isSuccessful) {
|
|
Result.success(response.body() ?: emptyList())
|
|
} else {
|
|
Result.failure(Exception("Error: ${response.code()}"))
|
|
}
|
|
} catch (e: Exception) {
|
|
Result.failure(e)
|
|
}
|
|
}
|
|
}
|
|
```
|
|
|
|
### ViewModel Layer
|
|
|
|
`TimelineViewModel.kt` manages UI state and calls repository methods:
|
|
|
|
```kotlin
|
|
fun loadTimeline() {
|
|
viewModelScope.launch {
|
|
_uiState.value = TimelineUiState.Loading
|
|
repository.getPublicTimeline().fold(
|
|
onSuccess = { statuses ->
|
|
_uiState.value = TimelineUiState.Success(statuses)
|
|
},
|
|
onFailure = { error ->
|
|
_uiState.value = TimelineUiState.Error(error.message ?: "Unknown error")
|
|
}
|
|
)
|
|
}
|
|
}
|
|
```
|
|
|
|
## Further Reading
|
|
|
|
- [Official Mastodon API Documentation](https://docs.joinmastodon.org/api/)
|
|
- [ActivityPub Specification](https://www.w3.org/TR/activitypub/)
|
|
- [Retrofit Documentation](https://square.github.io/retrofit/)
|
|
- [Kotlin Coroutines Guide](https://kotlinlang.org/docs/coroutines-guide.html)
|