groupme-lib/groups_api.go

420 lines
9.8 KiB
Go
Executable File

// Package groupme defines a client capable of executing API commands for the GroupMe chat service
package groupme
import (
"bytes"
"context"
"encoding/json"
"errors"
"fmt"
"net/http"
"strconv"
)
// GroupMe documentation: https://dev.groupme.com/docs/v3#groups
/*//////// Endpoints ////////*/
const (
// Used to build other endpoints
groupsEndpointRoot = "/groups"
groupEndpointRoot = "/groups/%s"
// Actual Endpoints
indexGroupsEndpoint = groupsEndpointRoot // GET
formerGroupsEndpoint = groupsEndpointRoot + "/former" // GET
showGroupEndpoint = groupEndpointRoot // GET
createGroupEndpoint = groupsEndpointRoot // POST
updateGroupEndpoint = groupEndpointRoot + "/update" // POST
destroyGroupEndpoint = groupEndpointRoot + "/destroy" // POST
joinGroupEndpoint = groupEndpointRoot + "/join/%s" // POST
rejoinGroupEndpoint = groupsEndpointRoot + "/join" // POST
changeGroupOwnerEndpoint = groupsEndpointRoot + "/change_owners" // POST
)
/*//////// Common Request Parameters ////////*/
// GroupSettings is the settings for a group, used by CreateGroup and UpdateGroup
type GroupSettings struct {
// Required. Primary name of the group. Maximum 140 characters
Name string `json:"name"`
// A subheading for the group. Maximum 255 characters
Description string `json:"description"`
// GroupMe Image Service URL
ImageURL string `json:"image_url"`
// Defaults false. If true, disables notifications for all members.
// Documented for use only for UpdateGroup
OfficeMode bool `json:"office_mode"`
// Defaults false. If true, generates a share URL.
// Anyone with the URL can join the group
Share bool `json:"share"`
}
func (gss GroupSettings) String() string {
return marshal(&gss)
}
/*//////// API Requests ////////*/
/*/// Index ///*/
// GroupsQuery defines optional URL parameters for IndexGroups
type GroupsQuery struct {
// Fetch a particular page of results. Defaults to 1.
Page int `json:"page"`
// Define page size. Defaults to 10.
PerPage int `json:"per_page"`
// Comma separated list of data to omit from output.
// Currently supported value is only "memberships".
// If used then response will contain empty (null) members field.
Omit string `json:"omit"`
}
func (q GroupsQuery) String() string {
return marshal(&q)
}
/*
IndexGroups -
List the authenticated user's active groups.
The response is paginated, with a default of 10 groups per page.
Please consider using of omit=memberships parameter. Not including
member lists might significantly improve user experience of your
app for users who are participating in huge groups.
Parameters: See GroupsQuery
*/
func (c *Client) IndexGroups(ctx context.Context, req *GroupsQuery, authToken string) ([]*Group, error) {
httpReq, err := http.NewRequest("GET", c.endpointBase+indexGroupsEndpoint, nil)
if err != nil {
return nil, err
}
URL := httpReq.URL
query := URL.Query()
if req != nil {
if req.Page != 0 {
query.Set("page", strconv.Itoa(req.Page))
}
if req.PerPage != 0 {
query.Set("per_page", strconv.Itoa(req.PerPage))
}
if req.Omit != "" {
query.Set("omit", req.Omit)
}
}
URL.RawQuery = query.Encode()
var resp []*Group
err = c.doWithAuthToken(ctx, httpReq, &resp, authToken)
if err != nil {
return nil, err
}
return resp, nil
}
/*/// Former ///*/
/*
FormerGroups -
List they groups you have left but can rejoin.
*/
func (c *Client) FormerGroups(ctx context.Context, authToken string) ([]*Group, error) {
httpReq, err := http.NewRequest("GET", c.endpointBase+formerGroupsEndpoint, nil)
if err != nil {
return nil, err
}
var resp []*Group
err = c.doWithAuthToken(ctx, httpReq, &resp, authToken)
if err != nil {
return nil, err
}
return resp, nil
}
/*/// Show ///*/
/*
ShowGroup -
Loads a specific group.
Parameters:
groupID - required, ID(string)
*/
func (c *Client) ShowGroup(ctx context.Context, groupID ID, authToken string) (*Group, error) {
URL := fmt.Sprintf(c.endpointBase+showGroupEndpoint, groupID)
httpReq, err := http.NewRequest("GET", URL, nil)
if err != nil {
return nil, err
}
var resp Group
err = c.doWithAuthToken(ctx, httpReq, &resp, authToken)
if err != nil {
return nil, err
}
return &resp, nil
}
/*/// Create ///*/
/*
CreateGroup -
# Create a new group
Parameters: See GroupSettings
*/
func (c *Client) CreateGroup(ctx context.Context, gs GroupSettings, authToken string) (*Group, error) {
URL := fmt.Sprintf(c.endpointBase + createGroupEndpoint)
jsonBytes, err := json.Marshal(&gs)
if err != nil {
return nil, err
}
httpReq, err := http.NewRequest("POST", URL, bytes.NewBuffer(jsonBytes))
if err != nil {
return nil, err
}
var resp Group
err = c.doWithAuthToken(ctx, httpReq, &resp, authToken)
if err != nil {
return nil, err
}
return &resp, nil
}
/*/// Update ///*/
/*
UpdateGroup -
# Update a group after creation
Parameters:
groupID - required, ID(string)
See GroupSettings
*/
func (c *Client) UpdateGroup(ctx context.Context, groupID ID, gs GroupSettings, authToken string) (*Group, error) {
URL := fmt.Sprintf(c.endpointBase+updateGroupEndpoint, groupID)
jsonBytes, err := json.Marshal(&gs)
if err != nil {
return nil, err
}
httpReq, err := http.NewRequest("POST", URL, bytes.NewBuffer(jsonBytes))
if err != nil {
return nil, err
}
var resp Group
err = c.doWithAuthToken(ctx, httpReq, &resp, authToken)
if err != nil {
return nil, err
}
return &resp, nil
}
/*/// Destroy ///*/
/*
DestroyGroup -
# Disband a group
# This action is only available to the group creator
Parameters:
groupID - required, ID(string)
*/
func (c *Client) DestroyGroup(ctx context.Context, groupID ID, authToken string) error {
url := fmt.Sprintf(c.endpointBase+destroyGroupEndpoint, groupID)
httpReq, err := http.NewRequest("POST", url, nil)
if err != nil {
return err
}
return c.doWithAuthToken(ctx, httpReq, nil, authToken)
}
/*/// Join ///*/
/*
JoinGroup -
# Join a shared group
Parameters:
groupID - required, ID(string)
shareToken - required, string
*/
func (c *Client) JoinGroup(ctx context.Context, groupID ID, shareToken string, authToken string) (*Group, error) {
URL := fmt.Sprintf(c.endpointBase+joinGroupEndpoint, groupID, shareToken)
httpReq, err := http.NewRequest("POST", URL, nil)
if err != nil {
return nil, err
}
var resp Group
err = c.doWithAuthToken(ctx, httpReq, &resp, authToken)
if err != nil {
return nil, err
}
return &resp, nil
}
/*/// Rejoin ///*/
/*
RejoinGroup -
Rejoin a group. Only works if you previously removed yourself.
Parameters:
groupID - required, ID(string)
*/
func (c *Client) RejoinGroup(ctx context.Context, groupID ID, authToken string) (*Group, error) {
URL := fmt.Sprintf(c.endpointBase + rejoinGroupEndpoint)
var data = struct {
GroupID ID `json:"group_id"`
}{
groupID,
}
jsonBytes, err := json.Marshal(&data)
if err != nil {
return nil, err
}
httpReq, err := http.NewRequest("POST", URL, bytes.NewBuffer(jsonBytes))
if err != nil {
return nil, err
}
var resp Group
err = c.doWithAuthToken(ctx, httpReq, &resp, authToken)
if err != nil {
return nil, err
}
return &resp, nil
}
/*/// Change Owner ///*/
/*
ChangeGroupOwner - Change owner of requested groups.
This action is only available to the group creator.
Response is a result object which contain status field,
the result of change owner action for the request
Parameters: See ChangeOwnerRequest
*/
func (c *Client) ChangeGroupOwner(ctx context.Context, reqs ChangeOwnerRequest, authToken string) (ChangeOwnerResult, error) {
URL := fmt.Sprintf(c.endpointBase + changeGroupOwnerEndpoint)
var data = struct {
Requests []ChangeOwnerRequest `json:"requests"`
}{
[]ChangeOwnerRequest{reqs},
}
jsonBytes, err := json.Marshal(&data)
if err != nil {
return ChangeOwnerResult{}, err
}
httpReq, err := http.NewRequest("POST", URL, bytes.NewBuffer(jsonBytes))
if err != nil {
return ChangeOwnerResult{}, err
}
var resp struct {
Results []ChangeOwnerResult `json:"results"`
}
err = c.doWithAuthToken(ctx, httpReq, &resp, authToken)
if err != nil {
return ChangeOwnerResult{}, err
}
if len(resp.Results) < 1 {
return ChangeOwnerResult{}, errors.New("failed to parse results")
}
return resp.Results[0], nil
}
type changeOwnerStatusCode string
// Change owner Status Codes
const (
ChangeOwnerOk changeOwnerStatusCode = "200"
ChangeOwnerRequesterNewOwner changeOwnerStatusCode = "400"
ChangeOwnerNotOwner changeOwnerStatusCode = "403"
ChangeOwnerBadGroupOrOwner changeOwnerStatusCode = "404"
ChangeOwnerBadRequest changeOwnerStatusCode = "405"
)
// String returns the description of the status code according to GroupMe
func (c changeOwnerStatusCode) String() string {
return map[changeOwnerStatusCode]string{
ChangeOwnerOk: "success",
ChangeOwnerRequesterNewOwner: "requester is also a new owner",
ChangeOwnerNotOwner: "requester is not the owner of the group",
ChangeOwnerBadGroupOrOwner: "group or new owner not found or new owner is not member of the group",
ChangeOwnerBadRequest: "request object is missing required field or any of the required fields is not an ID",
}[c]
}
// ChangeOwnerRequest defines the new owner of a group
type ChangeOwnerRequest struct {
// Required
GroupID string `json:"group_id"`
// Required. UserId of the new owner of the group
// who must be an active member of the group
OwnerID string `json:"owner_id"`
}
func (r ChangeOwnerRequest) String() string {
return marshal(&r)
}
// ChangeOwnerResult holds the status of the group owner change
type ChangeOwnerResult struct {
GroupID string `json:"group_id"`
// UserId of the new owner of the group who is
// an active member of the group
OwnerID string `json:"owner_id"`
Status changeOwnerStatusCode `json:"status"`
}
func (r ChangeOwnerResult) String() string {
return marshal(&r)
}