account.go
author Mikael Berthe <mikael@lilotux.net>
Thu, 13 Apr 2017 20:20:50 +0200
changeset 111 fc7cd6c90b2e
parent 110 0ee4bfc17cc8
child 114 0a1f493588ee
permissions -rw-r--r--
Add FollowRemoteAccount() and GetAccountRelationships()

package gondole

import (
	"encoding/json"
	"fmt"
	"strconv"

	"github.com/sendgrid/rest"
)

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

	// The following fields are used when searching for accounts
	Q     string
	Limit int
}

// getSingleAccount returns an account entity
// The target can be "account", "verify_credentials", "follow", "unfollow",
// "block", "unblock", "mute" or "unmute".
// The id is optional and depends on the target.
func (g *Client) getSingleAccount(target string, id int) (*Account, error) {
	var endPoint string
	method := rest.Get
	strID := strconv.Itoa(id)

	switch target {
	case "account":
		endPoint = "accounts/" + strID
	case "verify_credentials":
		endPoint = "accounts/verify_credentials"
	case "follow", "unfollow", "block", "unblock", "mute", "unmute":
		endPoint = "accounts/" + strID + "/" + target
		method = rest.Post
	default:
		return nil, ErrInvalidParameter
	}

	req := g.prepareRequest(endPoint)
	req.Method = method
	r, err := rest.API(req)
	if err != nil {
		return nil, fmt.Errorf("getAccount (%s): %s", target, err.Error())
	}

	// Check for error reply
	var errorResult Error
	if err := json.Unmarshal([]byte(r.Body), &errorResult); err == nil {
		// The empty object is not an error
		if errorResult.Text != "" {
			return nil, fmt.Errorf("%s", errorResult.Text)
		}
	}

	var account Account
	// Not an error reply; let's unmarshal the data
	err = json.Unmarshal([]byte(r.Body), &account)
	if err != nil {
		return nil, fmt.Errorf("getAccount (%s) API: %s", target, err.Error())
	}
	return &account, nil
}

// getMultipleAccounts returns a list of account entities
// The target can be "followers", "following", "search", "blocks", "mutes",
// "follow_requests".
// The id is optional and depends on the target.
func (g *Client) getMultipleAccounts(target string, opts *getAccountsOptions) ([]Account, error) {
	var endPoint string
	switch target {
	case "followers", "following":
		if opts == nil || opts.ID < 1 {
			return []Account{}, ErrInvalidID
		}
		endPoint = "accounts/" + strconv.Itoa(opts.ID) + "/" + target
	case "follow_requests", "blocks", "mutes":
		endPoint = target
	case "search":
		if opts == nil || opts.Q == "" {
			return []Account{}, ErrInvalidParameter
		}
		endPoint = "accounts/" + target
	default:
		return nil, ErrInvalidParameter
	}

	req := g.prepareRequest(endPoint)
	if target == "search" {
		req.QueryParams["q"] = opts.Q
		if opts.Limit > 0 {
			req.QueryParams["limit"] = strconv.Itoa(opts.Limit)
		}
	}
	r, err := rest.API(req)
	if err != nil {
		return nil, fmt.Errorf("getAccount (%s): %s", target, err.Error())
	}

	// Check for error reply
	var errorResult Error
	if err := json.Unmarshal([]byte(r.Body), &errorResult); err == nil {
		// The empty object is not an error
		if errorResult.Text != "" {
			return nil, fmt.Errorf("%s", errorResult.Text)
		}
	}

	var accounts []Account
	// Not an error reply; let's unmarshal the data
	err = json.Unmarshal([]byte(r.Body), &accounts)
	if err != nil {
		return nil, fmt.Errorf("getAccount (%s) API: %s", target, err.Error())
	}
	return accounts, nil
}

// GetAccount returns an account entity
// The returned value can be nil if there is an error or if the
// requested ID does not exist.
func (g *Client) GetAccount(id int) (*Account, error) {
	account, err := g.getSingleAccount("account", id)
	if err != nil {
		return nil, err
	}
	if account != nil && account.ID == 0 {
		return nil, ErrEntityNotFound
	}
	return account, nil
}

// GetCurrentAccount returns the current user account
func (g *Client) GetCurrentAccount() (*Account, error) {
	account, err := g.getSingleAccount("verify_credentials", 0)
	if err != nil {
		return nil, err
	}
	if account != nil && account.ID == 0 {
		return nil, ErrEntityNotFound
	}
	return account, nil
}

// GetAccountFollowers returns the list of accounts following a given account
func (g *Client) GetAccountFollowers(accountID int) ([]Account, error) {
	o := &getAccountsOptions{ID: accountID}
	return g.getMultipleAccounts("followers", o)
}

// GetAccountFollowing returns the list of accounts a given account is following
func (g *Client) GetAccountFollowing(accountID int) ([]Account, error) {
	o := &getAccountsOptions{ID: accountID}
	return g.getMultipleAccounts("following", o)
}

// FollowAccount follows an account
func (g *Client) FollowAccount(id int) error {
	account, err := g.getSingleAccount("follow", id)
	if err != nil {
		return err
	}
	if account != nil && account.ID != id {
		return ErrEntityNotFound
	}
	return nil
}

// UnfollowAccount unfollows an account
func (g *Client) UnfollowAccount(id int) error {
	account, err := g.getSingleAccount("unfollow", id)
	if err != nil {
		return err
	}
	if account != nil && account.ID != id {
		return ErrEntityNotFound
	}
	return nil
}

// FollowRemoteAccount follows a remote account
// The parameter 'id' is a URI (username@domain).
func (g *Client) FollowRemoteAccount(id string) (*Account, error) {
	if id == "" {
		return nil, ErrInvalidID
	}

	req := g.prepareRequest("follows")
	req.Method = rest.Post
	req.QueryParams["uri"] = id
	r, err := rest.API(req)
	if err != nil {
		return nil, fmt.Errorf("FollowRemoteAccount: %s", err.Error())
	}

	// Check for error reply
	var errorResult Error
	if err := json.Unmarshal([]byte(r.Body), &errorResult); err == nil {
		// The empty object is not an error
		if errorResult.Text != "" {
			return nil, fmt.Errorf("%s", errorResult.Text)
		}
	}

	var account Account
	// Not an error reply; let's unmarshal the data
	err = json.Unmarshal([]byte(r.Body), &account)
	if err != nil {
		return nil, fmt.Errorf("FollowRemoteAccount API: %s", err.Error())
	}
	if account.ID == 0 {
		return nil, ErrEntityNotFound
	}
	return &account, nil
}

// BlockAccount blocks an account
func (g *Client) BlockAccount(id int) error {
	account, err := g.getSingleAccount("block", id)
	if err != nil {
		return err
	}
	if account != nil && account.ID != id {
		return ErrEntityNotFound
	}
	return nil
}

// UnblockAccount unblocks an account
func (g *Client) UnblockAccount(id int) error {
	account, err := g.getSingleAccount("unblock", id)
	if err != nil {
		return err
	}
	if account != nil && account.ID != id {
		return ErrEntityNotFound
	}
	return nil
}

// MuteAccount mutes an account
func (g *Client) MuteAccount(id int) error {
	account, err := g.getSingleAccount("mute", id)
	if err != nil {
		return err
	}
	if account != nil && account.ID != id {
		return ErrEntityNotFound
	}
	return nil
}

// UnmuteAccount unmutes an account
func (g *Client) UnmuteAccount(id int) error {
	account, err := g.getSingleAccount("unmute", id)
	if err != nil {
		return err
	}
	if account != nil && account.ID != id {
		return ErrEntityNotFound
	}
	return nil
}

// SearchAccounts returns a list of accounts matching the query string
// The limit parameter is optional (can be 0).
func (g *Client) SearchAccounts(query string, limit int) ([]Account, error) {
	o := &getAccountsOptions{Q: query, Limit: limit}
	return g.getMultipleAccounts("search", o)
}

// GetBlockedAccounts returns the list of blocked accounts
func (g *Client) GetBlockedAccounts() ([]Account, error) {
	return g.getMultipleAccounts("blocks", nil)
}

// GetMutedAccounts returns the list of muted accounts
func (g *Client) GetMutedAccounts() ([]Account, error) {
	return g.getMultipleAccounts("mutes", nil)
}

// GetAccountFollowRequests returns the list of follow requests accounts
func (g *Client) GetAccountFollowRequests() ([]Account, error) {
	return g.getMultipleAccounts("follow_requests", nil)
}

// GetAccountRelationships returns a list of relationship entities for the given accounts
// NOTE: Currently it doesn't seem to work with several items.
func (g *Client) GetAccountRelationships(accountIDs []int) ([]Relationship, error) {
	var rl []Relationship

	if len(accountIDs) < 1 {
		return rl, ErrInvalidID
	}

	req := g.prepareRequest("accounts/relationships")

	if len(accountIDs) > 1 { // XXX
		return rl, fmt.Errorf("accounts/relationships currently does not work with more than 1 ID")
	}
	req.QueryParams["id"] = strconv.Itoa(accountIDs[0])
	/*
		for i, id := range accountIDList {
			qID := fmt.Sprintf("id[%d]", i+1)
			req.QueryParams[qID] = strconv.Itoa(id)
		}
	*/

	r, err := rest.API(req)
	if err != nil {
		return rl, fmt.Errorf("GetAccountRelationships: %s", err.Error())
	}

	// Check for error reply
	var errorResult Error
	if err := json.Unmarshal([]byte(r.Body), &errorResult); err == nil {
		// The empty object is not an error
		if errorResult.Text != "" {
			return nil, fmt.Errorf("%s", errorResult.Text)
		}
	}

	// Not an error reply; let's unmarshal the data
	err = json.Unmarshal([]byte(r.Body), &rl)
	if err != nil {
		return nil, fmt.Errorf("accounts/relationships API: %s", err.Error())
	}
	return rl, nil
}