status.go
author Mikael Berthe <mikael@lilotux.net>
Sat, 15 Apr 2017 21:08:34 +0200
changeset 125 2bbb72b9ebf6
parent 120 579912e9d0ef
child 130 c450bb73f59a
permissions -rw-r--r--
Rework the API wrappers to handle arrays of parameters This make some API calls work better (reports with several statuses, statuses with several attachments, relationships for multiple accounts...).

package gondole

import (
	"fmt"
	"strconv"

	"github.com/sendgrid/rest"
)

// updateStatusOptions contains option fields for POST and DELETE API calls
type updateStatusOptions struct {
	// The ID is used for most commands
	ID int

	// The following fields are used for posting a new status
	Status      string
	InReplyToID int
	MediaIDs    []int
	Sensitive   bool
	SpoilerText string
	Visibility  string // "direct", "private", "unlisted" or "public"
}

// queryStatusData queries the statuses API
// The operation 'op' can be empty or "status" (the status itself), "context",
// "card", "reblogged_by", "favourited_by".
// The data argument will receive the object(s) returned by the API server.
func (g *Client) queryStatusData(statusID int, op string, data interface{}) error {
	if statusID < 1 {
		return ErrInvalidID
	}

	endPoint := "statuses/" + strconv.Itoa(statusID)

	if op != "" && op != "status" {
		switch op {
		case "context", "card", "reblogged_by", "favourited_by":
		default:
			return ErrInvalidParameter
		}

		endPoint += "/" + op
	}

	return g.apiCall(endPoint, rest.Get, nil, data)
}

// updateStatusData updates the statuses
// The operation 'op' can be empty or "status" (to post a status), "delete"
// (for deleting a status), "reblog", "unreblog", "favourite", "unfavourite".
// The data argument will receive the object(s) returned by the API server.
func (g *Client) updateStatusData(op string, opts updateStatusOptions, data interface{}) error {
	method := rest.Post
	endPoint := "statuses"
	params := make(apiCallParams)

	switch op {
	case "", "status":
		op = "status"
		if opts.Status == "" {
			return ErrInvalidParameter
		}
		switch opts.Visibility {
		case "", "direct", "private", "unlisted", "public":
			// Okay
		default:
			return ErrInvalidParameter
		}
		if len(opts.MediaIDs) > 4 {
			return fmt.Errorf("too many (>4) media IDs")
		}
	case "delete":
		method = rest.Delete
		if opts.ID < 1 {
			return ErrInvalidID
		}
		endPoint += "/" + strconv.Itoa(opts.ID)
	case "reblog", "unreblog", "favourite", "unfavourite":
		if opts.ID < 1 {
			return ErrInvalidID
		}
		endPoint += "/" + strconv.Itoa(opts.ID) + "/" + op
	default:
		return ErrInvalidParameter
	}

	// Form items for a new toot
	if op == "status" {
		params["status"] = opts.Status
		if opts.InReplyToID > 0 {
			params["in_reply_to_id"] = strconv.Itoa(opts.InReplyToID)
		}
		for i, id := range opts.MediaIDs {
			qID := fmt.Sprintf("media_ids[%d]", i+1)
			params[qID] = strconv.Itoa(id)
		}
		if opts.Sensitive {
			params["sensitive"] = "true"
		}
		if opts.SpoilerText != "" {
			params["spoiler_text"] = opts.SpoilerText
		}
		if opts.Visibility != "" {
			params["visibility"] = opts.Visibility
		}
	}

	return g.apiCall(endPoint, method, params, data)
}

// GetStatus returns a status
// The returned status can be nil if there is an error or if the
// requested ID does not exist.
func (g *Client) GetStatus(id int) (*Status, error) {
	var status Status

	if err := g.queryStatusData(id, "status", &status); err != nil {
		return nil, err
	}
	if status.ID == 0 {
		return nil, ErrEntityNotFound
	}
	return &status, nil
}

// GetStatusContext returns a status context
func (g *Client) GetStatusContext(id int) (*Context, error) {
	var context Context
	if err := g.queryStatusData(id, "context", &context); err != nil {
		return nil, err
	}
	return &context, nil
}

// GetStatusCard returns a status card
func (g *Client) GetStatusCard(id int) (*Card, error) {
	var card Card
	if err := g.queryStatusData(id, "card", &card); err != nil {
		return nil, err
	}
	return &card, nil
}

// GetStatusRebloggedBy returns a list of the accounts who reblogged a status
func (g *Client) GetStatusRebloggedBy(id int) ([]Account, error) {
	var accounts []Account
	err := g.queryStatusData(id, "reblogged_by", &accounts)
	return accounts, err
}

// GetStatusFavouritedBy returns a list of the accounts who favourited a status
func (g *Client) GetStatusFavouritedBy(id int) ([]Account, error) {
	var accounts []Account
	err := g.queryStatusData(id, "favourited_by", &accounts)
	return accounts, err
}

// PostStatus posts a new "toot"
// All parameters but "text" can be empty.
// Visibility must be empty, or one of "direct", "private", "unlisted" and "public".
func (g *Client) PostStatus(text string, inReplyTo int, mediaIDs []int, sensitive bool, spoilerText string, visibility string) (*Status, error) {
	var status Status
	o := updateStatusOptions{
		Status:      text,
		InReplyToID: inReplyTo,
		MediaIDs:    mediaIDs,
		Sensitive:   sensitive,
		SpoilerText: spoilerText,
		Visibility:  visibility,
	}

	err := g.updateStatusData("status", o, &status)
	if err != nil {
		return nil, err
	}
	if status.ID == 0 {
		return nil, ErrEntityNotFound // TODO Change error message
	}
	return &status, err
}

// DeleteStatus deletes a status
func (g *Client) DeleteStatus(id int) error {
	var status Status
	o := updateStatusOptions{ID: id}
	err := g.updateStatusData("delete", o, &status)
	return err
}

// ReblogStatus reblogs a status
func (g *Client) ReblogStatus(id int) error {
	var status Status
	o := updateStatusOptions{ID: id}
	err := g.updateStatusData("reblog", o, &status)
	return err
}

// UnreblogStatus unreblogs a status
func (g *Client) UnreblogStatus(id int) error {
	var status Status
	o := updateStatusOptions{ID: id}
	err := g.updateStatusData("unreblog", o, &status)
	return err
}

// FavouriteStatus favourites a status
func (g *Client) FavouriteStatus(id int) error {
	var status Status
	o := updateStatusOptions{ID: id}
	err := g.updateStatusData("favourite", o, &status)
	return err
}

// UnfavouriteStatus unfavourites a status
func (g *Client) UnfavouriteStatus(id int) error {
	var status Status
	o := updateStatusOptions{ID: id}
	err := g.updateStatusData("unfavourite", o, &status)
	return err
}