Add post format selection
[bloat] / mastodon / status.go
1 package mastodon
2
3 import (
4         "context"
5         "fmt"
6         "io"
7         "mime/multipart"
8         "net/http"
9         "net/url"
10         "time"
11 )
12
13 type StatusPleroma struct {
14         InReplyToAccountAcct string `json:"in_reply_to_account_acct"`
15 }
16
17 type ReplyInfo struct {
18         ID     string `json:"id"`
19         Number int    `json:"number"`
20 }
21
22 // Status is struct to hold status.
23 type Status struct {
24         ID                 string       `json:"id"`
25         URI                string       `json:"uri"`
26         URL                string       `json:"url"`
27         Account            Account      `json:"account"`
28         InReplyToID        interface{}  `json:"in_reply_to_id"`
29         InReplyToAccountID interface{}  `json:"in_reply_to_account_id"`
30         Reblog             *Status      `json:"reblog"`
31         Content            string       `json:"content"`
32         CreatedAt          time.Time    `json:"created_at"`
33         Emojis             []Emoji      `json:"emojis"`
34         RepliesCount       int64        `json:"replies_count"`
35         ReblogsCount       int64        `json:"reblogs_count"`
36         FavouritesCount    int64        `json:"favourites_count"`
37         Reblogged          interface{}  `json:"reblogged"`
38         Favourited         interface{}  `json:"favourited"`
39         Muted              interface{}  `json:"muted"`
40         Sensitive          bool         `json:"sensitive"`
41         SpoilerText        string       `json:"spoiler_text"`
42         Visibility         string       `json:"visibility"`
43         MediaAttachments   []Attachment `json:"media_attachments"`
44         Mentions           []Mention    `json:"mentions"`
45         Tags               []Tag        `json:"tags"`
46         Card               *Card        `json:"card"`
47         Application        Application  `json:"application"`
48         Language           string       `json:"language"`
49         Pinned             interface{}  `json:"pinned"`
50
51         // Custom fields
52         Pleroma         StatusPleroma          `json:"pleroma"`
53         HideAccountInfo bool                   `json:"hide_account_info"`
54         ShowReplies     bool                   `json:"show_replies"`
55         ReplyMap        map[string][]ReplyInfo `json:"reply_map"`
56         ReplyNumber     int                    `json:"reply_number"`
57 }
58
59 // Context hold information for mastodon context.
60 type Context struct {
61         Ancestors   []*Status `json:"ancestors"`
62         Descendants []*Status `json:"descendants"`
63 }
64
65 // Card hold information for mastodon card.
66 type Card struct {
67         URL          string `json:"url"`
68         Title        string `json:"title"`
69         Description  string `json:"description"`
70         Image        string `json:"image"`
71         Type         string `json:"type"`
72         AuthorName   string `json:"author_name"`
73         AuthorURL    string `json:"author_url"`
74         ProviderName string `json:"provider_name"`
75         ProviderURL  string `json:"provider_url"`
76         HTML         string `json:"html"`
77         Width        int64  `json:"width"`
78         Height       int64  `json:"height"`
79 }
80
81 // GetFavourites return the favorite list of the current user.
82 func (c *Client) GetFavourites(ctx context.Context, pg *Pagination) ([]*Status, error) {
83         var statuses []*Status
84         err := c.doAPI(ctx, http.MethodGet, "/api/v1/favourites", nil, &statuses, pg)
85         if err != nil {
86                 return nil, err
87         }
88         return statuses, nil
89 }
90
91 // GetStatus return status specified by id.
92 func (c *Client) GetStatus(ctx context.Context, id string) (*Status, error) {
93         var status Status
94         err := c.doAPI(ctx, http.MethodGet, fmt.Sprintf("/api/v1/statuses/%s", id), nil, &status, nil)
95         if err != nil {
96                 return nil, err
97         }
98         return &status, nil
99 }
100
101 // GetStatusContext return status specified by id.
102 func (c *Client) GetStatusContext(ctx context.Context, id string) (*Context, error) {
103         var context Context
104         err := c.doAPI(ctx, http.MethodGet, fmt.Sprintf("/api/v1/statuses/%s/context", id), nil, &context, nil)
105         if err != nil {
106                 return nil, err
107         }
108         return &context, nil
109 }
110
111 // GetStatusCard return status specified by id.
112 func (c *Client) GetStatusCard(ctx context.Context, id string) (*Card, error) {
113         var card Card
114         err := c.doAPI(ctx, http.MethodGet, fmt.Sprintf("/api/v1/statuses/%s/card", id), nil, &card, nil)
115         if err != nil {
116                 return nil, err
117         }
118         return &card, nil
119 }
120
121 // GetRebloggedBy returns the account list of the user who reblogged the toot of id.
122 func (c *Client) GetRebloggedBy(ctx context.Context, id string, pg *Pagination) ([]*Account, error) {
123         var accounts []*Account
124         err := c.doAPI(ctx, http.MethodGet, fmt.Sprintf("/api/v1/statuses/%s/reblogged_by", id), nil, &accounts, pg)
125         if err != nil {
126                 return nil, err
127         }
128         return accounts, nil
129 }
130
131 // GetFavouritedBy returns the account list of the user who liked the toot of id.
132 func (c *Client) GetFavouritedBy(ctx context.Context, id string, pg *Pagination) ([]*Account, error) {
133         var accounts []*Account
134         err := c.doAPI(ctx, http.MethodGet, fmt.Sprintf("/api/v1/statuses/%s/favourited_by", id), nil, &accounts, pg)
135         if err != nil {
136                 return nil, err
137         }
138         return accounts, nil
139 }
140
141 // Reblog is reblog the toot of id and return status of reblog.
142 func (c *Client) Reblog(ctx context.Context, id string) (*Status, error) {
143         var status Status
144         err := c.doAPI(ctx, http.MethodPost, fmt.Sprintf("/api/v1/statuses/%s/reblog", id), nil, &status, nil)
145         if err != nil {
146                 return nil, err
147         }
148         return &status, nil
149 }
150
151 // Unreblog is unreblog the toot of id and return status of the original toot.
152 func (c *Client) Unreblog(ctx context.Context, id string) (*Status, error) {
153         var status Status
154         err := c.doAPI(ctx, http.MethodPost, fmt.Sprintf("/api/v1/statuses/%s/unreblog", id), nil, &status, nil)
155         if err != nil {
156                 return nil, err
157         }
158         return &status, nil
159 }
160
161 // Favourite is favourite the toot of id and return status of the favourite toot.
162 func (c *Client) Favourite(ctx context.Context, id string) (*Status, error) {
163         var status Status
164         err := c.doAPI(ctx, http.MethodPost, fmt.Sprintf("/api/v1/statuses/%s/favourite", id), nil, &status, nil)
165         if err != nil {
166                 return nil, err
167         }
168         return &status, nil
169 }
170
171 // Unfavourite is unfavourite the toot of id and return status of the unfavourite toot.
172 func (c *Client) Unfavourite(ctx context.Context, id string) (*Status, error) {
173         var status Status
174         err := c.doAPI(ctx, http.MethodPost, fmt.Sprintf("/api/v1/statuses/%s/unfavourite", id), nil, &status, nil)
175         if err != nil {
176                 return nil, err
177         }
178         return &status, nil
179 }
180
181 // GetTimelineHome return statuses from home timeline.
182 func (c *Client) GetTimelineHome(ctx context.Context, pg *Pagination) ([]*Status, error) {
183         var statuses []*Status
184         err := c.doAPI(ctx, http.MethodGet, "/api/v1/timelines/home", nil, &statuses, pg)
185         if err != nil {
186                 return nil, err
187         }
188         return statuses, nil
189 }
190
191 // GetTimelinePublic return statuses from public timeline.
192 func (c *Client) GetTimelinePublic(ctx context.Context, isLocal bool, pg *Pagination) ([]*Status, error) {
193         params := url.Values{}
194         if isLocal {
195                 params.Set("local", "true")
196         }
197
198         var statuses []*Status
199         err := c.doAPI(ctx, http.MethodGet, "/api/v1/timelines/public", params, &statuses, pg)
200         if err != nil {
201                 return nil, err
202         }
203         return statuses, nil
204 }
205
206 // GetTimelineHashtag return statuses from tagged timeline.
207 func (c *Client) GetTimelineHashtag(ctx context.Context, tag string, isLocal bool, pg *Pagination) ([]*Status, error) {
208         params := url.Values{}
209         if isLocal {
210                 params.Set("local", "t")
211         }
212
213         var statuses []*Status
214         err := c.doAPI(ctx, http.MethodGet, fmt.Sprintf("/api/v1/timelines/tag/%s", url.PathEscape(tag)), params, &statuses, pg)
215         if err != nil {
216                 return nil, err
217         }
218         return statuses, nil
219 }
220
221 // GetTimelineList return statuses from a list timeline.
222 func (c *Client) GetTimelineList(ctx context.Context, id string, pg *Pagination) ([]*Status, error) {
223         var statuses []*Status
224         err := c.doAPI(ctx, http.MethodGet, fmt.Sprintf("/api/v1/timelines/list/%s", url.PathEscape(string(id))), nil, &statuses, pg)
225         if err != nil {
226                 return nil, err
227         }
228         return statuses, nil
229 }
230
231 // GetTimelineMedia return statuses from media timeline.
232 // NOTE: This is an experimental feature of pawoo.net.
233 func (c *Client) GetTimelineMedia(ctx context.Context, isLocal bool, pg *Pagination) ([]*Status, error) {
234         params := url.Values{}
235         params.Set("media", "t")
236         if isLocal {
237                 params.Set("local", "t")
238         }
239
240         var statuses []*Status
241         err := c.doAPI(ctx, http.MethodGet, "/api/v1/timelines/public", params, &statuses, pg)
242         if err != nil {
243                 return nil, err
244         }
245         return statuses, nil
246 }
247
248 // PostStatus post the toot.
249 func (c *Client) PostStatus(ctx context.Context, toot *Toot) (*Status, error) {
250         params := url.Values{}
251         params.Set("status", toot.Status)
252         if toot.InReplyToID != "" {
253                 params.Set("in_reply_to_id", string(toot.InReplyToID))
254         }
255         if toot.MediaIDs != nil {
256                 for _, media := range toot.MediaIDs {
257                         params.Add("media_ids[]", string(media))
258                 }
259         }
260         if toot.Visibility != "" {
261                 params.Set("visibility", fmt.Sprint(toot.Visibility))
262         }
263         if toot.Sensitive {
264                 params.Set("sensitive", "true")
265         }
266         if toot.SpoilerText != "" {
267                 params.Set("spoiler_text", toot.SpoilerText)
268         }
269         if toot.ContentType != "" {
270                 params.Set("content_type", toot.ContentType)
271         }
272
273         var status Status
274         err := c.doAPI(ctx, http.MethodPost, "/api/v1/statuses", params, &status, nil)
275         if err != nil {
276                 return nil, err
277         }
278         return &status, nil
279 }
280
281 // DeleteStatus delete the toot.
282 func (c *Client) DeleteStatus(ctx context.Context, id string) error {
283         return c.doAPI(ctx, http.MethodDelete, fmt.Sprintf("/api/v1/statuses/%s", id), nil, nil, nil)
284 }
285
286 // Search search content with query.
287 func (c *Client) Search(ctx context.Context, q string, resolve bool) (*Results, error) {
288         params := url.Values{}
289         params.Set("q", q)
290         params.Set("resolve", fmt.Sprint(resolve))
291         var results Results
292         err := c.doAPI(ctx, http.MethodGet, "/api/v1/search", params, &results, nil)
293         if err != nil {
294                 return nil, err
295         }
296         return &results, nil
297 }
298
299 // UploadMedia upload a media attachment from a file.
300 func (c *Client) UploadMedia(ctx context.Context, file string) (*Attachment, error) {
301         var attachment Attachment
302         err := c.doAPI(ctx, http.MethodPost, "/api/v1/media", file, &attachment, nil)
303         if err != nil {
304                 return nil, err
305         }
306         return &attachment, nil
307 }
308
309 // UploadMediaFromReader uploads a media attachment from a io.Reader.
310 func (c *Client) UploadMediaFromReader(ctx context.Context, reader io.Reader) (*Attachment, error) {
311         var attachment Attachment
312         err := c.doAPI(ctx, http.MethodPost, "/api/v1/media", reader, &attachment, nil)
313         if err != nil {
314                 return nil, err
315         }
316         return &attachment, nil
317 }
318
319 // UploadMediaFromReader uploads a media attachment from a io.Reader.
320 func (c *Client) UploadMediaFromMultipartFileHeader(ctx context.Context, fh *multipart.FileHeader) (*Attachment, error) {
321         var attachment Attachment
322         err := c.doAPI(ctx, http.MethodPost, "/api/v1/media", fh, &attachment, nil)
323         if err != nil {
324                 return nil, err
325         }
326         return &attachment, nil
327 }