337
docs/API.md
Archivo normal
337
docs/API.md
Archivo normal
@@ -0,0 +1,337 @@
|
||||
# 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)
|
||||
Referencia en una nueva incidencia
Block a user