154 lines
3.9 KiB
Go
154 lines
3.9 KiB
Go
package activitypub
|
|
|
|
import (
|
|
"context"
|
|
"encoding/json"
|
|
"fmt"
|
|
"io"
|
|
"net/http"
|
|
"time"
|
|
)
|
|
|
|
type APClient struct {
|
|
client *http.Client
|
|
host string
|
|
bearerToken string
|
|
}
|
|
|
|
func NewAPClient(ctx context.Context, host, apikey string) (*APClient, error) {
|
|
if host == "" || apikey == "" {
|
|
return nil, fmt.Errorf("host and apikey must not be empty")
|
|
}
|
|
return &APClient{
|
|
client: http.DefaultClient,
|
|
host: host,
|
|
bearerToken: fmt.Sprintf("Bearer %s", apikey),
|
|
}, nil
|
|
}
|
|
|
|
func (ap *APClient) GetPost(ctx context.Context, postID string) (*Post, error) {
|
|
url := fmt.Sprintf("%s/api/v1/statuses/%s", ap.host, postID)
|
|
req, err := http.NewRequestWithContext(ctx, http.MethodGet, url, nil)
|
|
if err != nil {
|
|
return nil, fmt.Errorf("failed to create request: %w", err)
|
|
}
|
|
req.Header.Set("Authorization", ap.bearerToken)
|
|
resp, err := ap.client.Do(req)
|
|
if err != nil {
|
|
return nil, fmt.Errorf("failed to get post: %w", err)
|
|
}
|
|
defer resp.Body.Close()
|
|
if resp.StatusCode != http.StatusOK {
|
|
return nil, fmt.Errorf("failed to get post: %s", resp.Status)
|
|
}
|
|
body, err := io.ReadAll(resp.Body)
|
|
if err != nil {
|
|
return nil, fmt.Errorf("failed to read response body: %w", err)
|
|
}
|
|
var internal = &internalPost{}
|
|
if err = json.Unmarshal(body, internal); err != nil {
|
|
return nil, fmt.Errorf("failed to unmarshal json response: %w", err)
|
|
}
|
|
return internal.toPost(), nil
|
|
}
|
|
|
|
func (ap *APClient) GetLatestNPosts(ctx context.Context, actor string, limit int) ([]*Post, error) {
|
|
url := fmt.Sprintf("%s/api/v1/accounts/%s/statuses?limit=%d", ap.host, actor, limit)
|
|
req, err := http.NewRequestWithContext(ctx, http.MethodGet, url, nil)
|
|
if err != nil {
|
|
return nil, fmt.Errorf("failed to create request: %w", err)
|
|
}
|
|
req.Header.Set("Authorization", ap.bearerToken)
|
|
resp, err := ap.client.Do(req)
|
|
if err != nil {
|
|
return nil, fmt.Errorf("failed to get latest posts: %w", err)
|
|
}
|
|
defer resp.Body.Close()
|
|
if resp.StatusCode != http.StatusOK {
|
|
return nil, fmt.Errorf("failed to get latest posts: %s", resp.Status)
|
|
}
|
|
body, err := io.ReadAll(resp.Body)
|
|
if err != nil {
|
|
return nil, fmt.Errorf("failed to read response body: %w", err)
|
|
}
|
|
var internal []*internalPost
|
|
if err = json.Unmarshal(body, &internal); err != nil {
|
|
return nil, fmt.Errorf("failed to unmarshal json response: %w", err)
|
|
}
|
|
posts := make([]*Post, len(internal))
|
|
for i, p := range internal {
|
|
posts[i] = p.toPost()
|
|
}
|
|
return posts, nil
|
|
}
|
|
|
|
func (ap *APClient) GetPostsInTimeframe(ctx context.Context, actor string, start, end time.Time, limit int) ([]Post, error) {
|
|
var posts []Post
|
|
var maxID string
|
|
|
|
for {
|
|
if ctx.Err() != nil {
|
|
return nil, ctx.Err()
|
|
}
|
|
if len(posts) >= limit {
|
|
break
|
|
}
|
|
|
|
url := fmt.Sprintf("%s/api/v1/accounts/%s/statuses?limit=50", ap.host, actor)
|
|
if maxID != "" {
|
|
url += fmt.Sprintf("&max_id=%s", maxID)
|
|
}
|
|
|
|
req, err := http.NewRequestWithContext(ctx, http.MethodGet, url, nil)
|
|
if err != nil {
|
|
return nil, fmt.Errorf("failed to create request: %w", err)
|
|
}
|
|
req.Header.Set("Authorization", ap.bearerToken)
|
|
|
|
resp, err := ap.client.Do(req)
|
|
if err != nil {
|
|
return nil, fmt.Errorf("failed to get posts: %w", err)
|
|
}
|
|
// !!IMPORTANT!! Manually close body instead of deferring, due to loop
|
|
|
|
if resp.StatusCode != http.StatusOK {
|
|
resp.Body.Close()
|
|
return nil, fmt.Errorf("failed to get posts: %s", resp.Status)
|
|
}
|
|
|
|
body, err := io.ReadAll(resp.Body)
|
|
resp.Body.Close()
|
|
if err != nil {
|
|
return nil, fmt.Errorf("failed to read response body: %w", err)
|
|
}
|
|
|
|
var internal []*internalPost
|
|
if err := json.Unmarshal(body, &internal); err != nil {
|
|
return nil, fmt.Errorf("failed to unmarshal response body: %w", err)
|
|
}
|
|
|
|
if len(internal) == 0 {
|
|
break
|
|
}
|
|
|
|
for _, i := range internal {
|
|
if i.CreatedAt.After(end) {
|
|
continue
|
|
}
|
|
|
|
if i.CreatedAt.Before(start) {
|
|
return posts, nil
|
|
}
|
|
|
|
posts = append(posts, *i.toPost())
|
|
|
|
if len(posts) >= limit {
|
|
return posts, nil
|
|
}
|
|
}
|
|
|
|
maxID = internal[len(internal)-1].ID
|
|
}
|
|
|
|
return posts, nil
|
|
}
|