vendor/github.com/McKael/madon/v2/status.go
changeset 265 05c40b36d3b2
parent 264 8f478162d991
child 266 80973a656b81
equal deleted inserted replaced
264:8f478162d991 265:05c40b36d3b2
     1 /*
       
     2 Copyright 2017-2018 Mikael Berthe
       
     3 
       
     4 Licensed under the MIT license.  Please see the LICENSE file is this directory.
       
     5 */
       
     6 
       
     7 package madon
       
     8 
       
     9 import (
       
    10 	"fmt"
       
    11 	"strconv"
       
    12 
       
    13 	"github.com/pkg/errors"
       
    14 	"github.com/sendgrid/rest"
       
    15 )
       
    16 
       
    17 // PostStatusParams contains option fields for the PostStatus command
       
    18 type PostStatusParams struct {
       
    19 	Text        string
       
    20 	InReplyTo   int64
       
    21 	MediaIDs    []int64
       
    22 	Sensitive   bool
       
    23 	SpoilerText string
       
    24 	Visibility  string
       
    25 }
       
    26 
       
    27 // updateStatusOptions contains option fields for POST and DELETE API calls
       
    28 type updateStatusOptions struct {
       
    29 	// The ID is used for most commands
       
    30 	ID int64
       
    31 
       
    32 	// The following fields are used for posting a new status
       
    33 	Status      string
       
    34 	InReplyToID int64
       
    35 	MediaIDs    []int64
       
    36 	Sensitive   bool
       
    37 	SpoilerText string
       
    38 	Visibility  string // "direct", "private", "unlisted" or "public"
       
    39 }
       
    40 
       
    41 // getMultipleStatuses returns a list of status entities
       
    42 // If lopt.All is true, several requests will be made until the API server
       
    43 // has nothing to return.
       
    44 func (mc *Client) getMultipleStatuses(endPoint string, params apiCallParams, lopt *LimitParams) ([]Status, error) {
       
    45 	var statuses []Status
       
    46 	var links apiLinks
       
    47 	if err := mc.apiCall("v1/"+endPoint, rest.Get, params, lopt, &links, &statuses); err != nil {
       
    48 		return nil, err
       
    49 	}
       
    50 	if lopt != nil { // Fetch more pages to reach our limit
       
    51 		var statusSlice []Status
       
    52 		for (lopt.All || lopt.Limit > len(statuses)) && links.next != nil {
       
    53 			newlopt := links.next
       
    54 			links = apiLinks{}
       
    55 			if err := mc.apiCall("v1/"+endPoint, rest.Get, params, newlopt, &links, &statusSlice); err != nil {
       
    56 				return nil, err
       
    57 			}
       
    58 			statuses = append(statuses, statusSlice...)
       
    59 			statusSlice = statusSlice[:0] // Clear struct
       
    60 		}
       
    61 	}
       
    62 	return statuses, nil
       
    63 }
       
    64 
       
    65 // queryStatusData queries the statuses API
       
    66 // The operation 'op' can be empty or "status" (the status itself), "context",
       
    67 // "card", "reblogged_by", "favourited_by".
       
    68 // The data argument will receive the object(s) returned by the API server.
       
    69 func (mc *Client) queryStatusData(statusID int64, op string, data interface{}) error {
       
    70 	if statusID < 1 {
       
    71 		return ErrInvalidID
       
    72 	}
       
    73 
       
    74 	endPoint := "statuses/" + strconv.FormatInt(statusID, 10)
       
    75 
       
    76 	if op != "" && op != "status" {
       
    77 		switch op {
       
    78 		case "context", "card", "reblogged_by", "favourited_by":
       
    79 		default:
       
    80 			return ErrInvalidParameter
       
    81 		}
       
    82 
       
    83 		endPoint += "/" + op
       
    84 	}
       
    85 
       
    86 	return mc.apiCall("v1/"+endPoint, rest.Get, nil, nil, nil, data)
       
    87 }
       
    88 
       
    89 // updateStatusData updates the statuses
       
    90 // The operation 'op' can be empty or "status" (to post a status), "delete"
       
    91 // (for deleting a status), "reblog"/"unreblog", "favourite"/"unfavourite",
       
    92 // "mute"/"unmute" (for conversations) or "pin"/"unpin".
       
    93 // The data argument will receive the object(s) returned by the API server.
       
    94 func (mc *Client) updateStatusData(op string, opts updateStatusOptions, data interface{}) error {
       
    95 	method := rest.Post
       
    96 	endPoint := "statuses"
       
    97 	params := make(apiCallParams)
       
    98 
       
    99 	switch op {
       
   100 	case "", "status":
       
   101 		op = "status"
       
   102 		if opts.Status == "" {
       
   103 			return ErrInvalidParameter
       
   104 		}
       
   105 		switch opts.Visibility {
       
   106 		case "", "direct", "private", "unlisted", "public":
       
   107 			// Okay
       
   108 		default:
       
   109 			return ErrInvalidParameter
       
   110 		}
       
   111 		if len(opts.MediaIDs) > 4 {
       
   112 			return errors.New("too many (>4) media IDs")
       
   113 		}
       
   114 	case "delete":
       
   115 		method = rest.Delete
       
   116 		if opts.ID < 1 {
       
   117 			return ErrInvalidID
       
   118 		}
       
   119 		endPoint += "/" + strconv.FormatInt(opts.ID, 10)
       
   120 	case "reblog", "unreblog", "favourite", "unfavourite":
       
   121 		if opts.ID < 1 {
       
   122 			return ErrInvalidID
       
   123 		}
       
   124 		endPoint += "/" + strconv.FormatInt(opts.ID, 10) + "/" + op
       
   125 	case "mute", "unmute", "pin", "unpin":
       
   126 		if opts.ID < 1 {
       
   127 			return ErrInvalidID
       
   128 		}
       
   129 		endPoint += "/" + strconv.FormatInt(opts.ID, 10) + "/" + op
       
   130 	default:
       
   131 		return ErrInvalidParameter
       
   132 	}
       
   133 
       
   134 	// Form items for a new toot
       
   135 	if op == "status" {
       
   136 		params["status"] = opts.Status
       
   137 		if opts.InReplyToID > 0 {
       
   138 			params["in_reply_to_id"] = strconv.FormatInt(opts.InReplyToID, 10)
       
   139 		}
       
   140 		for i, id := range opts.MediaIDs {
       
   141 			if id < 1 {
       
   142 				return ErrInvalidID
       
   143 			}
       
   144 			qID := fmt.Sprintf("[%d]media_ids", i)
       
   145 			params[qID] = strconv.FormatInt(id, 10)
       
   146 		}
       
   147 		if opts.Sensitive {
       
   148 			params["sensitive"] = "true"
       
   149 		}
       
   150 		if opts.SpoilerText != "" {
       
   151 			params["spoiler_text"] = opts.SpoilerText
       
   152 		}
       
   153 		if opts.Visibility != "" {
       
   154 			params["visibility"] = opts.Visibility
       
   155 		}
       
   156 	}
       
   157 
       
   158 	return mc.apiCall("v1/"+endPoint, method, params, nil, nil, data)
       
   159 }
       
   160 
       
   161 // GetStatus returns a status
       
   162 // The returned status can be nil if there is an error or if the
       
   163 // requested ID does not exist.
       
   164 func (mc *Client) GetStatus(statusID int64) (*Status, error) {
       
   165 	var status Status
       
   166 
       
   167 	if err := mc.queryStatusData(statusID, "status", &status); err != nil {
       
   168 		return nil, err
       
   169 	}
       
   170 	if status.ID == 0 {
       
   171 		return nil, ErrEntityNotFound
       
   172 	}
       
   173 	return &status, nil
       
   174 }
       
   175 
       
   176 // GetStatusContext returns a status context
       
   177 func (mc *Client) GetStatusContext(statusID int64) (*Context, error) {
       
   178 	var context Context
       
   179 	if err := mc.queryStatusData(statusID, "context", &context); err != nil {
       
   180 		return nil, err
       
   181 	}
       
   182 	return &context, nil
       
   183 }
       
   184 
       
   185 // GetStatusCard returns a status card
       
   186 func (mc *Client) GetStatusCard(statusID int64) (*Card, error) {
       
   187 	var card Card
       
   188 	if err := mc.queryStatusData(statusID, "card", &card); err != nil {
       
   189 		return nil, err
       
   190 	}
       
   191 	return &card, nil
       
   192 }
       
   193 
       
   194 // GetStatusRebloggedBy returns a list of the accounts who reblogged a status
       
   195 func (mc *Client) GetStatusRebloggedBy(statusID int64, lopt *LimitParams) ([]Account, error) {
       
   196 	o := &getAccountsOptions{ID: statusID, Limit: lopt}
       
   197 	return mc.getMultipleAccountsHelper("reblogged_by", o)
       
   198 }
       
   199 
       
   200 // GetStatusFavouritedBy returns a list of the accounts who favourited a status
       
   201 func (mc *Client) GetStatusFavouritedBy(statusID int64, lopt *LimitParams) ([]Account, error) {
       
   202 	o := &getAccountsOptions{ID: statusID, Limit: lopt}
       
   203 	return mc.getMultipleAccountsHelper("favourited_by", o)
       
   204 }
       
   205 
       
   206 // PostStatus posts a new "toot"
       
   207 // All parameters but "text" can be empty.
       
   208 // Visibility must be empty, or one of "direct", "private", "unlisted" and "public".
       
   209 func (mc *Client) PostStatus(cmdParams PostStatusParams) (*Status, error) {
       
   210 	var status Status
       
   211 	o := updateStatusOptions{
       
   212 		Status:      cmdParams.Text,
       
   213 		InReplyToID: cmdParams.InReplyTo,
       
   214 		MediaIDs:    cmdParams.MediaIDs,
       
   215 		Sensitive:   cmdParams.Sensitive,
       
   216 		SpoilerText: cmdParams.SpoilerText,
       
   217 		Visibility:  cmdParams.Visibility,
       
   218 	}
       
   219 
       
   220 	err := mc.updateStatusData("status", o, &status)
       
   221 	if err != nil {
       
   222 		return nil, err
       
   223 	}
       
   224 	if status.ID == 0 {
       
   225 		return nil, ErrEntityNotFound // TODO Change error message
       
   226 	}
       
   227 	return &status, err
       
   228 }
       
   229 
       
   230 // DeleteStatus deletes a status
       
   231 func (mc *Client) DeleteStatus(statusID int64) error {
       
   232 	var status Status
       
   233 	o := updateStatusOptions{ID: statusID}
       
   234 	err := mc.updateStatusData("delete", o, &status)
       
   235 	return err
       
   236 }
       
   237 
       
   238 // ReblogStatus reblogs a status
       
   239 func (mc *Client) ReblogStatus(statusID int64) error {
       
   240 	var status Status
       
   241 	o := updateStatusOptions{ID: statusID}
       
   242 	err := mc.updateStatusData("reblog", o, &status)
       
   243 	return err
       
   244 }
       
   245 
       
   246 // UnreblogStatus unreblogs a status
       
   247 func (mc *Client) UnreblogStatus(statusID int64) error {
       
   248 	var status Status
       
   249 	o := updateStatusOptions{ID: statusID}
       
   250 	err := mc.updateStatusData("unreblog", o, &status)
       
   251 	return err
       
   252 }
       
   253 
       
   254 // FavouriteStatus favourites a status
       
   255 func (mc *Client) FavouriteStatus(statusID int64) error {
       
   256 	var status Status
       
   257 	o := updateStatusOptions{ID: statusID}
       
   258 	err := mc.updateStatusData("favourite", o, &status)
       
   259 	return err
       
   260 }
       
   261 
       
   262 // UnfavouriteStatus unfavourites a status
       
   263 func (mc *Client) UnfavouriteStatus(statusID int64) error {
       
   264 	var status Status
       
   265 	o := updateStatusOptions{ID: statusID}
       
   266 	err := mc.updateStatusData("unfavourite", o, &status)
       
   267 	return err
       
   268 }
       
   269 
       
   270 // PinStatus pins a status
       
   271 func (mc *Client) PinStatus(statusID int64) error {
       
   272 	var status Status
       
   273 	o := updateStatusOptions{ID: statusID}
       
   274 	err := mc.updateStatusData("pin", o, &status)
       
   275 	return err
       
   276 }
       
   277 
       
   278 // UnpinStatus unpins a status
       
   279 func (mc *Client) UnpinStatus(statusID int64) error {
       
   280 	var status Status
       
   281 	o := updateStatusOptions{ID: statusID}
       
   282 	err := mc.updateStatusData("unpin", o, &status)
       
   283 	return err
       
   284 }
       
   285 
       
   286 // MuteConversation mutes the conversation containing a status
       
   287 func (mc *Client) MuteConversation(statusID int64) (*Status, error) {
       
   288 	var status Status
       
   289 	o := updateStatusOptions{ID: statusID}
       
   290 	err := mc.updateStatusData("mute", o, &status)
       
   291 	return &status, err
       
   292 }
       
   293 
       
   294 // UnmuteConversation unmutes the conversation containing a status
       
   295 func (mc *Client) UnmuteConversation(statusID int64) (*Status, error) {
       
   296 	var status Status
       
   297 	o := updateStatusOptions{ID: statusID}
       
   298 	err := mc.updateStatusData("unmute", o, &status)
       
   299 	return &status, err
       
   300 }
       
   301 
       
   302 // GetFavourites returns the list of the user's favourites
       
   303 // If lopt.All is true, several requests will be made until the API server
       
   304 // has nothing to return.
       
   305 // If lopt.Limit is set (and not All), several queries can be made until the
       
   306 // limit is reached.
       
   307 func (mc *Client) GetFavourites(lopt *LimitParams) ([]Status, error) {
       
   308 	return mc.getMultipleStatuses("favourites", nil, lopt)
       
   309 }