Compare commits
45 Commits
Author | SHA1 | Date | |
---|---|---|---|
f9c1b24194 | |||
a58e4100e1 | |||
7b9e2a8d29 | |||
d81730e3e7 | |||
08bfe83ba4 | |||
c0f3da8060 | |||
5727a20506 | |||
3e9851b2c0 | |||
|
8f23e04eea | ||
|
c2dbe7021e | ||
|
6eba33b3be | ||
|
7961d30a51 | ||
|
12931fb275 | ||
|
65299d9606 | ||
|
aa216ed4af | ||
|
28b2d85660 | ||
|
8dd5993481 | ||
|
042fb9a951 | ||
|
b6715c2375 | ||
|
c5d6c8a29c | ||
|
d6a8b0818f | ||
|
da7bc977a3 | ||
|
eeb2f88b97 | ||
|
784cfe93c1 | ||
|
6d42a230d1 | ||
|
7166300503 | ||
|
f92a8a7a86 | ||
|
646bec0e27 | ||
|
b9317e3e37 | ||
|
05eb619c40 | ||
|
055fdc7126 | ||
|
3bac8c4f92 | ||
|
8fca7782ec | ||
|
8628d37bef | ||
|
6f453ff6cc | ||
|
73586d4b4c | ||
|
7f8d829ff7 | ||
|
d657643538 | ||
|
3e663f8615 | ||
|
eea12a3b50 | ||
|
e6f29c4e73 | ||
|
cd2551461d | ||
|
2ff9a03a8c | ||
|
f900b99dac | ||
|
d8cdcf4ef2 |
30
.github/workflows/go-test.yml
vendored
30
.github/workflows/go-test.yml
vendored
@ -1,29 +1,21 @@
|
|||||||
name: test
|
name: Go Test
|
||||||
|
|
||||||
on:
|
on:
|
||||||
push:
|
push:
|
||||||
tags:
|
branches: [ master ]
|
||||||
- v*
|
|
||||||
branches:
|
|
||||||
- master
|
|
||||||
pull_request:
|
pull_request:
|
||||||
|
branches: [ master ]
|
||||||
|
|
||||||
jobs:
|
jobs:
|
||||||
test:
|
test:
|
||||||
name: Test
|
|
||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
steps:
|
steps:
|
||||||
|
- uses: actions/checkout@v2
|
||||||
|
|
||||||
- name: Set up Go
|
- name: Set up Go
|
||||||
uses: actions/setup-go@v1
|
uses: actions/setup-go@v2
|
||||||
with:
|
with:
|
||||||
go-version: 1.14
|
go-version: 1.15
|
||||||
|
|
||||||
- name: Check out code
|
- name: Test
|
||||||
uses: actions/checkout@v1
|
run: go test -v ./...
|
||||||
|
|
||||||
- name: Run Unit tests.
|
|
||||||
run: go test -coverprofile cover.out -covermode=atomic ${PKG_LIST} && cat cover.out >> coverage.txt
|
|
||||||
|
|
||||||
- name: Upload Coverage report to CodeCov
|
|
||||||
uses: codecov/codecov-action@v1.0.0
|
|
||||||
with:
|
|
||||||
token: ${{secrets.CODECOV_TOKEN}}
|
|
||||||
file: ./coverage.txt
|
|
||||||
|
9
.github/workflows/golangci-lint.yml
vendored
9
.github/workflows/golangci-lint.yml
vendored
@ -1,10 +1,6 @@
|
|||||||
name: golangci-lint
|
name: golangci-lint
|
||||||
on:
|
on:
|
||||||
push:
|
push:
|
||||||
tags:
|
|
||||||
- v*
|
|
||||||
branches:
|
|
||||||
- master
|
|
||||||
pull_request:
|
pull_request:
|
||||||
jobs:
|
jobs:
|
||||||
golangci:
|
golangci:
|
||||||
@ -13,7 +9,7 @@ jobs:
|
|||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@v2
|
- uses: actions/checkout@v2
|
||||||
- name: golangci-lint
|
- name: golangci-lint
|
||||||
uses: golangci/golangci-lint-action@v1
|
uses: golangci/golangci-lint-action@v2
|
||||||
with:
|
with:
|
||||||
# Required: the version of golangci-lint is required and must be specified without patch version: we always use the latest patch version.
|
# Required: the version of golangci-lint is required and must be specified without patch version: we always use the latest patch version.
|
||||||
version: v1.29
|
version: v1.29
|
||||||
@ -26,3 +22,6 @@ jobs:
|
|||||||
|
|
||||||
# Optional: show only new issues if it's a pull request. The default value is `false`.
|
# Optional: show only new issues if it's a pull request. The default value is `false`.
|
||||||
# only-new-issues: true
|
# only-new-issues: true
|
||||||
|
|
||||||
|
# Optional: if set to true then the action will use pre-installed Go
|
||||||
|
# skip-go-installation: true
|
||||||
|
@ -6,7 +6,6 @@ I would like to add common helper functions/features inspired by the package use
|
|||||||
# GroupMe API Wrapper
|
# GroupMe API Wrapper
|
||||||

|

|
||||||
[](https://pkg.go.dev/github.com/densestvoid/groupme)
|
[](https://pkg.go.dev/github.com/densestvoid/groupme)
|
||||||
[](https://codecov.io/gh/densestvoid/groupme)
|
|
||||||
## Description
|
## Description
|
||||||
The design of this package is meant to be super simple. Wrap the exposed API endpoints [documented](https://dev.groupme.com/docs/v3#v3) by the GroupMe team. While you can achieve the core of this package with cURL, there are some small added features, coupled along with a modern language, that should simplify writing GroupMe [bots](https://dev.groupme.com/bots) and [applications](https://dev.groupme.com/applications).
|
The design of this package is meant to be super simple. Wrap the exposed API endpoints [documented](https://dev.groupme.com/docs/v3#v3) by the GroupMe team. While you can achieve the core of this package with cURL, there are some small added features, coupled along with a modern language, that should simplify writing GroupMe [bots](https://dev.groupme.com/bots) and [applications](https://dev.groupme.com/applications).
|
||||||
|
|
||||||
@ -74,6 +73,9 @@ func main() {
|
|||||||
|
|
||||||
### [*FUTURE*] CLI
|
### [*FUTURE*] CLI
|
||||||
|
|
||||||
|
## Support
|
||||||
|
You can join the [GroupMe support group](https://groupme.com/join_group/65686806/il1737tE) (you will need to provide a reason for joining), or the [Discord server](https://discord.gg/raAdxWuKTU).
|
||||||
|
|
||||||
## Contribute
|
## Contribute
|
||||||
I find the hours I can spend developing personal projects decreasing every year, so I welcome any help I can get. Feel free to tackle any open issues, or if a feature request catches your eye, feel free to reach out to me and we can discuss adding it to the package. However, once version 1.0 is released, I don't foresee much work happening on this project unless the GroupMe API is updated.
|
I find the hours I can spend developing personal projects decreasing every year, so I welcome any help I can get. Feel free to tackle any open issues, or if a feature request catches your eye, feel free to reach out to me and we can discuss adding it to the package. However, once version 1.0 is released, I don't foresee much work happening on this project unless the GroupMe API is updated.
|
||||||
|
|
||||||
|
@ -1,12 +1,14 @@
|
|||||||
|
// Package groupme defines a client capable of executing API commands for the GroupMe chat service
|
||||||
package groupme
|
package groupme
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"context"
|
||||||
"net/http"
|
"net/http"
|
||||||
)
|
)
|
||||||
|
|
||||||
// GroupMe documentation: https://dev.groupme.com/docs/v3#blocks
|
// GroupMe documentation: https://dev.groupme.com/docs/v3#blocks
|
||||||
|
|
||||||
////////// Endpoints //////////
|
/*//////// Endpoints ////////*/
|
||||||
const (
|
const (
|
||||||
// Used to build other endpoints
|
// Used to build other endpoints
|
||||||
blocksEndpointRoot = "/blocks"
|
blocksEndpointRoot = "/blocks"
|
||||||
@ -18,19 +20,10 @@ const (
|
|||||||
unblockEndpoint = blocksEndpointRoot // DELETE
|
unblockEndpoint = blocksEndpointRoot // DELETE
|
||||||
)
|
)
|
||||||
|
|
||||||
////////// API Requests //////////
|
/*//////// API Requests ////////*/
|
||||||
|
|
||||||
// Index
|
// IndexBlock - A list of contacts you have blocked. These people cannot DM you
|
||||||
|
func (c *Client) IndexBlock(ctx context.Context, userID string, authToken string) ([]*Block, error) {
|
||||||
/*
|
|
||||||
IndexBlock -
|
|
||||||
|
|
||||||
A list of contacts you have blocked. These people cannot DM you
|
|
||||||
|
|
||||||
Parameters:
|
|
||||||
userID - required, ID(string)
|
|
||||||
*/
|
|
||||||
func (c *Client) IndexBlock(userID ID) ([]*Block, error) {
|
|
||||||
httpReq, err := http.NewRequest("GET", c.endpointBase+indexBlocksEndpoint, nil)
|
httpReq, err := http.NewRequest("GET", c.endpointBase+indexBlocksEndpoint, nil)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
@ -38,13 +31,13 @@ func (c *Client) IndexBlock(userID ID) ([]*Block, error) {
|
|||||||
|
|
||||||
URL := httpReq.URL
|
URL := httpReq.URL
|
||||||
query := URL.Query()
|
query := URL.Query()
|
||||||
query.Set("user", userID.String())
|
query.Set("user", userID)
|
||||||
URL.RawQuery = query.Encode()
|
URL.RawQuery = query.Encode()
|
||||||
|
|
||||||
var resp struct {
|
var resp struct {
|
||||||
Blocks []*Block `json:"blocks"`
|
Blocks []*Block `json:"blocks"`
|
||||||
}
|
}
|
||||||
err = c.doWithAuthToken(httpReq, &resp)
|
err = c.doWithAuthToken(ctx, httpReq, &resp, authToken)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
@ -52,17 +45,8 @@ func (c *Client) IndexBlock(userID ID) ([]*Block, error) {
|
|||||||
return resp.Blocks, nil
|
return resp.Blocks, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// Between
|
// BlockBetween - Asks if a block exists between you and another user id
|
||||||
|
func (c *Client) BlockBetween(ctx context.Context, userID, otherUserID string, authToken string) (bool, error) {
|
||||||
/*
|
|
||||||
BlockBetween -
|
|
||||||
|
|
||||||
Asks if a block exists between you and another user id
|
|
||||||
|
|
||||||
Parameters:
|
|
||||||
otherUserID - required, ID(string)
|
|
||||||
*/
|
|
||||||
func (c *Client) BlockBetween(userID, otherUserID ID) (bool, error) {
|
|
||||||
httpReq, err := http.NewRequest("GET", c.endpointBase+blockBetweenEndpoint, nil)
|
httpReq, err := http.NewRequest("GET", c.endpointBase+blockBetweenEndpoint, nil)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return false, err
|
return false, err
|
||||||
@ -70,14 +54,14 @@ func (c *Client) BlockBetween(userID, otherUserID ID) (bool, error) {
|
|||||||
|
|
||||||
URL := httpReq.URL
|
URL := httpReq.URL
|
||||||
query := URL.Query()
|
query := URL.Query()
|
||||||
query.Set("user", userID.String())
|
query.Set("user", userID)
|
||||||
query.Set("otherUser", otherUserID.String())
|
query.Set("otherUser", otherUserID)
|
||||||
URL.RawQuery = query.Encode()
|
URL.RawQuery = query.Encode()
|
||||||
|
|
||||||
var resp struct {
|
var resp struct {
|
||||||
Between bool `json:"between"`
|
Between bool `json:"between"`
|
||||||
}
|
}
|
||||||
err = c.doWithAuthToken(httpReq, &resp)
|
err = c.doWithAuthToken(ctx, httpReq, &resp, authToken)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return false, err
|
return false, err
|
||||||
}
|
}
|
||||||
@ -85,18 +69,8 @@ func (c *Client) BlockBetween(userID, otherUserID ID) (bool, error) {
|
|||||||
return resp.Between, nil
|
return resp.Between, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// Create
|
// CreateBlock - Creates a block between you and the contact
|
||||||
|
func (c *Client) CreateBlock(ctx context.Context, userID, otherUserID string, authToken string) (*Block, error) {
|
||||||
/*
|
|
||||||
CreateBlock -
|
|
||||||
|
|
||||||
Creates a block between you and the contact
|
|
||||||
|
|
||||||
Parameters:
|
|
||||||
userID - required, ID(string)
|
|
||||||
otherUserID - required, ID(string)
|
|
||||||
*/
|
|
||||||
func (c *Client) CreateBlock(userID, otherUserID ID) (*Block, error) {
|
|
||||||
httpReq, err := http.NewRequest("POST", c.endpointBase+createBlockEndpoint, nil)
|
httpReq, err := http.NewRequest("POST", c.endpointBase+createBlockEndpoint, nil)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
@ -104,14 +78,14 @@ func (c *Client) CreateBlock(userID, otherUserID ID) (*Block, error) {
|
|||||||
|
|
||||||
URL := httpReq.URL
|
URL := httpReq.URL
|
||||||
query := URL.Query()
|
query := URL.Query()
|
||||||
query.Set("user", userID.String())
|
query.Set("user", userID)
|
||||||
query.Set("otherUser", otherUserID.String())
|
query.Set("otherUser", otherUserID)
|
||||||
URL.RawQuery = query.Encode()
|
URL.RawQuery = query.Encode()
|
||||||
|
|
||||||
var resp struct {
|
var resp struct {
|
||||||
Block *Block `json:"block"`
|
Block *Block `json:"block"`
|
||||||
}
|
}
|
||||||
err = c.doWithAuthToken(httpReq, &resp)
|
err = c.doWithAuthToken(ctx, httpReq, &resp, authToken)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
@ -119,18 +93,8 @@ func (c *Client) CreateBlock(userID, otherUserID ID) (*Block, error) {
|
|||||||
return resp.Block, nil
|
return resp.Block, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// Unblock
|
// Unblock - Removes block between you and other user
|
||||||
|
func (c *Client) Unblock(ctx context.Context, userID, otherUserID string, authToken string) error {
|
||||||
/*
|
|
||||||
Unblock -
|
|
||||||
|
|
||||||
Removes block between you and other user
|
|
||||||
|
|
||||||
Parameters:
|
|
||||||
userID - required, ID(string)
|
|
||||||
otherUserID - required, ID(string)
|
|
||||||
*/
|
|
||||||
func (c *Client) Unblock(userID, otherUserID ID) error {
|
|
||||||
httpReq, err := http.NewRequest("DELETE", c.endpointBase+unblockEndpoint, nil)
|
httpReq, err := http.NewRequest("DELETE", c.endpointBase+unblockEndpoint, nil)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
@ -138,11 +102,11 @@ func (c *Client) Unblock(userID, otherUserID ID) error {
|
|||||||
|
|
||||||
URL := httpReq.URL
|
URL := httpReq.URL
|
||||||
query := URL.Query()
|
query := URL.Query()
|
||||||
query.Set("user", userID.String())
|
query.Set("user", userID)
|
||||||
query.Set("otherUser", otherUserID.String())
|
query.Set("otherUser", otherUserID)
|
||||||
URL.RawQuery = query.Encode()
|
URL.RawQuery = query.Encode()
|
||||||
|
|
||||||
err = c.doWithAuthToken(httpReq, nil)
|
err = c.doWithAuthToken(ctx, httpReq, nil, authToken)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
@ -1,6 +1,8 @@
|
|||||||
|
// Package groupme defines a client capable of executing API commands for the GroupMe chat service
|
||||||
package groupme
|
package groupme
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"context"
|
||||||
"fmt"
|
"fmt"
|
||||||
"net/http"
|
"net/http"
|
||||||
"testing"
|
"testing"
|
||||||
@ -17,7 +19,7 @@ func (s *BlocksAPISuite) SetupSuite() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (s *BlocksAPISuite) TestBlocksIndex() {
|
func (s *BlocksAPISuite) TestBlocksIndex() {
|
||||||
blocks, err := s.client.IndexBlock("1")
|
blocks, err := s.client.IndexBlock(context.Background(), "1")
|
||||||
s.Require().NoError(err)
|
s.Require().NoError(err)
|
||||||
s.Require().NotZero(blocks)
|
s.Require().NotZero(blocks)
|
||||||
for _, block := range blocks {
|
for _, block := range blocks {
|
||||||
@ -26,19 +28,19 @@ func (s *BlocksAPISuite) TestBlocksIndex() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (s *BlocksAPISuite) TestBlocksBetween() {
|
func (s *BlocksAPISuite) TestBlocksBetween() {
|
||||||
between, err := s.client.BlockBetween("1", "2")
|
between, err := s.client.BlockBetween(context.Background(), "1", "2")
|
||||||
s.Require().NoError(err)
|
s.Require().NoError(err)
|
||||||
s.Assert().True(between)
|
s.Assert().True(between)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *BlocksAPISuite) TestBlocksCreate() {
|
func (s *BlocksAPISuite) TestBlocksCreate() {
|
||||||
block, err := s.client.CreateBlock("1", "2")
|
block, err := s.client.CreateBlock(context.Background(), "1", "2")
|
||||||
s.Require().NoError(err)
|
s.Require().NoError(err)
|
||||||
s.Assert().NotZero(block)
|
s.Assert().NotZero(block)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *BlocksAPISuite) TestBlocksUnblock() {
|
func (s *BlocksAPISuite) TestBlocksUnblock() {
|
||||||
s.Assert().NoError(s.client.Unblock("1", "2"))
|
s.Assert().NoError(s.client.Unblock(context.Background(), "1", "2"))
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestBlocksAPISuite(t *testing.T) {
|
func TestBlocksAPISuite(t *testing.T) {
|
||||||
|
128
bots_api.go
128
bots_api.go
@ -1,15 +1,17 @@
|
|||||||
|
// Package groupme defines a client capable of executing API commands for the GroupMe chat service
|
||||||
package groupme
|
package groupme
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"errors"
|
"bytes"
|
||||||
|
"context"
|
||||||
|
"encoding/json"
|
||||||
"fmt"
|
"fmt"
|
||||||
"net/http"
|
"net/http"
|
||||||
"net/url"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
// GroupMe documentation: https://dev.groupme.com/docs/v3#bots
|
// GroupMe documentation: https://dev.groupme.com/docs/v3#bots
|
||||||
|
|
||||||
////////// Endpoints //////////
|
/*//////// Endpoints ////////*/
|
||||||
const (
|
const (
|
||||||
// Used to build other endpoints
|
// Used to build other endpoints
|
||||||
botsEndpointRoot = "/bots"
|
botsEndpointRoot = "/bots"
|
||||||
@ -21,38 +23,31 @@ const (
|
|||||||
destroyBotEndpoint = botsEndpointRoot + "/destroy" // POST
|
destroyBotEndpoint = botsEndpointRoot + "/destroy" // POST
|
||||||
)
|
)
|
||||||
|
|
||||||
////////// API Requests //////////
|
/*//////// API Requests ////////*/
|
||||||
|
|
||||||
// Create
|
// CreateBot - Create a bot. See the Bots Tutorial (https://dev.groupme.com/tutorials/bots)
|
||||||
|
// for a full walkthrough.
|
||||||
|
func (c *Client) CreateBot(ctx context.Context, bot *Bot, authToken string) (*Bot, error) {
|
||||||
|
URL := c.endpointBase + createBotEndpoint
|
||||||
|
|
||||||
/*
|
var data = struct {
|
||||||
CreateBot -
|
Bot *Bot `json:"bot,omitempty"`
|
||||||
|
}{
|
||||||
|
bot,
|
||||||
|
}
|
||||||
|
|
||||||
Create a bot. See the Bots Tutorial (https://dev.groupme.com/tutorials/bots)
|
jsonBytes, err := json.Marshal(&data)
|
||||||
for a full walkthrough.
|
|
||||||
|
|
||||||
Parameters:
|
|
||||||
See Bot
|
|
||||||
Name - required
|
|
||||||
GroupID - required
|
|
||||||
*/
|
|
||||||
func (c *Client) CreateBot(bot *Bot) (*Bot, error) {
|
|
||||||
httpReq, err := http.NewRequest("POST", c.endpointBase+createBotEndpoint, nil)
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
if bot == nil {
|
httpReq, err := http.NewRequest("POST", URL, bytes.NewBuffer(jsonBytes))
|
||||||
return nil, errors.New("bot cannot be nil")
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
data := url.Values{}
|
|
||||||
data.Add("bot", bot.String())
|
|
||||||
|
|
||||||
httpReq.PostForm = data
|
|
||||||
|
|
||||||
var resp Bot
|
var resp Bot
|
||||||
err = c.doWithAuthToken(httpReq, &resp)
|
err = c.doWithAuthToken(ctx, httpReq, &resp, authToken)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
@ -60,55 +55,43 @@ func (c *Client) CreateBot(bot *Bot) (*Bot, error) {
|
|||||||
return &resp, nil
|
return &resp, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// PostMessage
|
// PostBotMessage - Post a message from a bot
|
||||||
|
|
||||||
/*
|
|
||||||
PostBotMessage -
|
|
||||||
|
|
||||||
Post a message from a bot
|
|
||||||
|
|
||||||
Parameters:
|
|
||||||
botID - required, ID(string)
|
|
||||||
text - required, string
|
|
||||||
pictureURL - string; image must be processed through image
|
|
||||||
service (https://dev.groupme.com/docs/image_service)
|
|
||||||
*/
|
|
||||||
// TODO: Move PostBotMessage to bot object, since it doesn't require access token
|
// TODO: Move PostBotMessage to bot object, since it doesn't require access token
|
||||||
func (c *Client) PostBotMessage(botID ID, text string, pictureURL *string) error {
|
func (c *Client) PostBotMessage(ctx context.Context, botID ID, text string, pictureURL *string) error {
|
||||||
URL := fmt.Sprintf(c.endpointBase + postBotMessageEndpoint)
|
URL := fmt.Sprintf(c.endpointBase + postBotMessageEndpoint)
|
||||||
|
|
||||||
httpReq, err := http.NewRequest("POST", URL, nil)
|
var data = struct {
|
||||||
|
BotID ID `json:"bot_id"`
|
||||||
|
Text string `json:"text"`
|
||||||
|
PictureURL *string `json:",omitempty"`
|
||||||
|
}{
|
||||||
|
botID,
|
||||||
|
text,
|
||||||
|
pictureURL,
|
||||||
|
}
|
||||||
|
|
||||||
|
jsonBytes, err := json.Marshal(&data)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
data := url.Values{}
|
httpReq, err := http.NewRequest("POST", URL, bytes.NewBuffer(jsonBytes))
|
||||||
data.Add("bot_id", string(botID))
|
if err != nil {
|
||||||
data.Add("text", text)
|
return err
|
||||||
if pictureURL != nil {
|
|
||||||
data.Add("picture_url", *pictureURL)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
httpReq.PostForm = data
|
return c.do(ctx, httpReq, nil)
|
||||||
|
|
||||||
return c.do(httpReq, nil)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Index
|
// IndexBots - list bots that you have created
|
||||||
|
func (c *Client) IndexBots(ctx context.Context, authToken string) ([]*Bot, error) {
|
||||||
/*
|
|
||||||
IndexBots -
|
|
||||||
|
|
||||||
List bots that you have created
|
|
||||||
*/
|
|
||||||
func (c *Client) IndexBots() ([]*Bot, error) {
|
|
||||||
httpReq, err := http.NewRequest("GET", c.endpointBase+indexBotsEndpoint, nil)
|
httpReq, err := http.NewRequest("GET", c.endpointBase+indexBotsEndpoint, nil)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
var resp []*Bot
|
var resp []*Bot
|
||||||
err = c.doWithAuthToken(httpReq, &resp)
|
err = c.doWithAuthToken(ctx, httpReq, &resp, authToken)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
@ -116,28 +99,25 @@ func (c *Client) IndexBots() ([]*Bot, error) {
|
|||||||
return resp, nil
|
return resp, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// Destroy
|
// DestroyBot - Remove a bot that you have created
|
||||||
|
func (c *Client) DestroyBot(ctx context.Context, botID ID, authToken string) error {
|
||||||
/*
|
|
||||||
DestroyBot -
|
|
||||||
|
|
||||||
Remove a bot that you have created
|
|
||||||
|
|
||||||
Parameters:
|
|
||||||
botID - required, ID(string)
|
|
||||||
*/
|
|
||||||
func (c *Client) DestroyBot(botID ID) error {
|
|
||||||
URL := fmt.Sprintf(c.endpointBase + destroyBotEndpoint)
|
URL := fmt.Sprintf(c.endpointBase + destroyBotEndpoint)
|
||||||
|
|
||||||
httpReq, err := http.NewRequest("POST", URL, nil)
|
var data = struct {
|
||||||
|
BotID ID `json:"bot_id"`
|
||||||
|
}{
|
||||||
|
botID,
|
||||||
|
}
|
||||||
|
|
||||||
|
jsonBytes, err := json.Marshal(&data)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
data := url.Values{}
|
httpReq, err := http.NewRequest("POST", URL, bytes.NewBuffer(jsonBytes))
|
||||||
data.Add("bot_id", string(botID))
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
httpReq.PostForm = data
|
return c.doWithAuthToken(ctx, httpReq, nil, authToken)
|
||||||
|
|
||||||
return c.doWithAuthToken(httpReq, nil)
|
|
||||||
}
|
}
|
||||||
|
@ -1,6 +1,8 @@
|
|||||||
|
// Package groupme defines a client capable of executing API commands for the GroupMe chat service
|
||||||
package groupme
|
package groupme
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"context"
|
||||||
"fmt"
|
"fmt"
|
||||||
"net/http"
|
"net/http"
|
||||||
"testing"
|
"testing"
|
||||||
@ -17,7 +19,7 @@ func (s *BotsAPISuite) SetupSuite() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (s *BotsAPISuite) TestBotsCreate() {
|
func (s *BotsAPISuite) TestBotsCreate() {
|
||||||
bot, err := s.client.CreateBot(&Bot{
|
bot, err := s.client.CreateBot(context.Background(), &Bot{
|
||||||
Name: "test",
|
Name: "test",
|
||||||
GroupID: "1",
|
GroupID: "1",
|
||||||
AvatarURL: "url.com",
|
AvatarURL: "url.com",
|
||||||
@ -29,12 +31,12 @@ func (s *BotsAPISuite) TestBotsCreate() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (s *BotsAPISuite) TestBotsPostMessage() {
|
func (s *BotsAPISuite) TestBotsPostMessage() {
|
||||||
err := s.client.PostBotMessage("1", "test message", nil)
|
err := s.client.PostBotMessage(context.Background(), "1", "test message", nil)
|
||||||
s.Require().NoError(err)
|
s.Require().NoError(err)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *BotsAPISuite) TestBotsIndex() {
|
func (s *BotsAPISuite) TestBotsIndex() {
|
||||||
bots, err := s.client.IndexBots()
|
bots, err := s.client.IndexBots(context.Background())
|
||||||
s.Require().NoError(err)
|
s.Require().NoError(err)
|
||||||
s.Require().NotZero(bots)
|
s.Require().NotZero(bots)
|
||||||
for _, bot := range bots {
|
for _, bot := range bots {
|
||||||
@ -43,7 +45,7 @@ func (s *BotsAPISuite) TestBotsIndex() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (s *BotsAPISuite) TestBotsDestroy() {
|
func (s *BotsAPISuite) TestBotsDestroy() {
|
||||||
s.Require().NoError(s.client.DestroyBot("1"))
|
s.Require().NoError(s.client.DestroyBot(context.Background(), "1"))
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestBotsAPISuite(t *testing.T) {
|
func TestBotsAPISuite(t *testing.T) {
|
||||||
|
42
chats_api.go
42
chats_api.go
@ -1,13 +1,15 @@
|
|||||||
|
// Package groupme defines a client capable of executing API commands for the GroupMe chat service
|
||||||
package groupme
|
package groupme
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"context"
|
||||||
"net/http"
|
"net/http"
|
||||||
"strconv"
|
"strconv"
|
||||||
)
|
)
|
||||||
|
|
||||||
// GroupMe documentation: https://dev.groupme.com/docs/v3#chats
|
// GroupMe documentation: https://dev.groupme.com/docs/v3#chats
|
||||||
|
|
||||||
////////// Endpoints //////////
|
/*//////// Endpoints ////////*/
|
||||||
const (
|
const (
|
||||||
// Used to build other endpoints
|
// Used to build other endpoints
|
||||||
chatsEndpointRoot = "/chats"
|
chatsEndpointRoot = "/chats"
|
||||||
@ -15,9 +17,7 @@ const (
|
|||||||
indexChatsEndpoint = chatsEndpointRoot // GET
|
indexChatsEndpoint = chatsEndpointRoot // GET
|
||||||
)
|
)
|
||||||
|
|
||||||
// Index
|
// IndexChatsQuery defines the optional URL parameters for IndexChats
|
||||||
|
|
||||||
// ChatsQuery defines the optional URL parameters for IndexChats
|
|
||||||
type IndexChatsQuery struct {
|
type IndexChatsQuery struct {
|
||||||
// Page Number
|
// Page Number
|
||||||
Page int `json:"page"`
|
Page int `json:"page"`
|
||||||
@ -25,15 +25,9 @@ type IndexChatsQuery struct {
|
|||||||
PerPage int `json:"per_page"`
|
PerPage int `json:"per_page"`
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
// IndexChats - Returns a paginated list of direct message chats, or
|
||||||
IndexChats -
|
// conversations, sorted by updated_at descending.
|
||||||
|
func (c *Client) IndexChats(ctx context.Context, req *IndexChatsQuery, authToken string) ([]*Chat, error) {
|
||||||
Returns a paginated list of direct message chats, or
|
|
||||||
conversations, sorted by updated_at descending.
|
|
||||||
|
|
||||||
Parameters: See ChatsQuery
|
|
||||||
*/
|
|
||||||
func (c *Client) IndexChats(req *IndexChatsQuery) ([]*Chat, error) {
|
|
||||||
httpReq, err := http.NewRequest("GET", c.endpointBase+indexChatsEndpoint, nil)
|
httpReq, err := http.NewRequest("GET", c.endpointBase+indexChatsEndpoint, nil)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
@ -52,7 +46,27 @@ func (c *Client) IndexChats(req *IndexChatsQuery) ([]*Chat, error) {
|
|||||||
URL.RawQuery = query.Encode()
|
URL.RawQuery = query.Encode()
|
||||||
|
|
||||||
var resp []*Chat
|
var resp []*Chat
|
||||||
err = c.doWithAuthToken(httpReq, &resp)
|
err = c.doWithAuthToken(ctx, httpReq, &resp, authToken)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
return resp, nil
|
||||||
|
}
|
||||||
|
func (c *Client) IndexRelations(ctx context.Context, authToken string) ([]*User, error) {
|
||||||
|
httpReq, err := http.NewRequest("GET", "https://api.groupme.com/v4"+"/relationships", nil)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
URL := httpReq.URL
|
||||||
|
query := URL.Query()
|
||||||
|
|
||||||
|
query.Set("include_blocked", "true")
|
||||||
|
URL.RawQuery = query.Encode()
|
||||||
|
|
||||||
|
var resp []*User
|
||||||
|
err = c.doWithAuthToken(ctx, httpReq, &resp, authToken)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
@ -1,6 +1,8 @@
|
|||||||
|
// Package groupme defines a client capable of executing API commands for the GroupMe chat service
|
||||||
package groupme
|
package groupme
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"context"
|
||||||
"fmt"
|
"fmt"
|
||||||
"net/http"
|
"net/http"
|
||||||
"testing"
|
"testing"
|
||||||
@ -18,6 +20,7 @@ func (s *ChatsAPISuite) SetupSuite() {
|
|||||||
|
|
||||||
func (s *ChatsAPISuite) TestChatsIndex() {
|
func (s *ChatsAPISuite) TestChatsIndex() {
|
||||||
chats, err := s.client.IndexChats(
|
chats, err := s.client.IndexChats(
|
||||||
|
context.Background(),
|
||||||
&IndexChatsQuery{
|
&IndexChatsQuery{
|
||||||
Page: 1,
|
Page: 1,
|
||||||
PerPage: 20,
|
PerPage: 20,
|
||||||
|
61
client.go
61
client.go
@ -1,14 +1,15 @@
|
|||||||
|
// Package groupme defines a client capable of executing API commands for the GroupMe chat service
|
||||||
package groupme
|
package groupme
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"bytes"
|
"bytes"
|
||||||
|
"context"
|
||||||
"encoding/json"
|
"encoding/json"
|
||||||
"errors"
|
|
||||||
"io/ioutil"
|
"io/ioutil"
|
||||||
"net/http"
|
"net/http"
|
||||||
)
|
)
|
||||||
|
|
||||||
// Endpoints are added on to the GroupMeAPIBase to get the full URI.
|
// GroupMeAPIBase - Endpoints are added on to this to get the full URI.
|
||||||
// Overridable for testing
|
// Overridable for testing
|
||||||
const GroupMeAPIBase = "https://api.groupme.com/v3"
|
const GroupMeAPIBase = "https://api.groupme.com/v3"
|
||||||
|
|
||||||
@ -17,15 +18,14 @@ const GroupMeAPIBase = "https://api.groupme.com/v3"
|
|||||||
type Client struct {
|
type Client struct {
|
||||||
httpClient *http.Client
|
httpClient *http.Client
|
||||||
endpointBase string
|
endpointBase string
|
||||||
authorizationToken string
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// NewClient creates a new GroupMe API Client
|
// NewClient creates a new GroupMe API Client
|
||||||
func NewClient(authToken string) *Client {
|
func NewClient() *Client {
|
||||||
return &Client{
|
return &Client{
|
||||||
httpClient: new(http.Client),
|
// TODO: enable transport information passing in
|
||||||
|
httpClient: &http.Client{},
|
||||||
endpointBase: GroupMeAPIBase,
|
endpointBase: GroupMeAPIBase,
|
||||||
authorizationToken: authToken,
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -40,7 +40,7 @@ func (c Client) String() string {
|
|||||||
return marshal(&c)
|
return marshal(&c)
|
||||||
}
|
}
|
||||||
|
|
||||||
///// Handle parsing of nested interface type response /////
|
/*/// Handle parsing of nested interface type response ///*/
|
||||||
type jsonResponse struct {
|
type jsonResponse struct {
|
||||||
Response response `json:"response"`
|
Response response `json:"response"`
|
||||||
Meta `json:"meta"`
|
Meta `json:"meta"`
|
||||||
@ -58,44 +58,63 @@ func (r response) UnmarshalJSON(bs []byte) error {
|
|||||||
return json.NewDecoder(bytes.NewBuffer(bs)).Decode(r.i)
|
return json.NewDecoder(bytes.NewBuffer(bs)).Decode(r.i)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (c Client) do(req *http.Request, i interface{}) error {
|
const errorStatusCodeMin = 300
|
||||||
|
|
||||||
|
func (c Client) do(ctx context.Context, req *http.Request, i interface{}) error {
|
||||||
|
req = req.WithContext(ctx)
|
||||||
|
if req.Method == "POST" {
|
||||||
|
req.Header.Set("Content-Type", "application/json")
|
||||||
|
}
|
||||||
|
|
||||||
getResp, err := c.httpClient.Do(req)
|
getResp, err := c.httpClient.Do(req)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
defer getResp.Body.Close()
|
||||||
|
|
||||||
|
var readBytes []byte
|
||||||
// Check Status Code is 1XX or 2XX
|
// Check Status Code is 1XX or 2XX
|
||||||
if getResp.StatusCode/100 > 2 {
|
if getResp.StatusCode >= errorStatusCodeMin {
|
||||||
return errors.New(getResp.Status)
|
readBytes, err = ioutil.ReadAll(getResp.Body)
|
||||||
|
if err != nil {
|
||||||
|
// We couldn't read the output. Oh well; generate the appropriate error type anyway.
|
||||||
|
return &Meta{
|
||||||
|
Code: HTTPStatusCode(getResp.StatusCode),
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
bytes, err := ioutil.ReadAll(getResp.Body)
|
resp := newJSONResponse(nil)
|
||||||
if err != nil {
|
if err = json.Unmarshal(readBytes, &resp); err != nil {
|
||||||
return err
|
// We couldn't parse the output. Oh well; generate the appropriate error type anyway.
|
||||||
|
return &Meta{
|
||||||
|
Code: HTTPStatusCode(getResp.StatusCode),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return &resp.Meta
|
||||||
}
|
}
|
||||||
|
|
||||||
if i == nil {
|
if i == nil {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
resp := newJSONResponse(i)
|
readBytes, err = ioutil.ReadAll(getResp.Body)
|
||||||
if err := json.Unmarshal(bytes, &resp); err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
// Check Status Code is 1XX or 2XX
|
resp := newJSONResponse(i)
|
||||||
if resp.Meta.Code/100 > 2 {
|
if err := json.Unmarshal(readBytes, &resp); err != nil {
|
||||||
return &resp.Meta
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (c Client) doWithAuthToken(req *http.Request, i interface{}) error {
|
func (c Client) doWithAuthToken(ctx context.Context, req *http.Request, i interface{}, authToken string) error {
|
||||||
URL := req.URL
|
URL := req.URL
|
||||||
query := URL.Query()
|
query := URL.Query()
|
||||||
query.Set("token", c.authorizationToken)
|
query.Set("token", authToken)
|
||||||
URL.RawQuery = query.Encode()
|
URL.RawQuery = query.Encode()
|
||||||
|
|
||||||
return c.do(req, i)
|
return c.do(ctx, req, i)
|
||||||
}
|
}
|
||||||
|
@ -1,6 +1,8 @@
|
|||||||
|
// Package groupme defines a client capable of executing API commands for the GroupMe chat service
|
||||||
package groupme
|
package groupme
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"context"
|
||||||
"net/http"
|
"net/http"
|
||||||
"testing"
|
"testing"
|
||||||
|
|
||||||
@ -30,18 +32,26 @@ func (s *ClientSuite) TestClient_Close() {
|
|||||||
s.Assert().NoError(s.client.Close())
|
s.Assert().NoError(s.client.Close())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (s *ClientSuite) TestClient_do_PostContentType() {
|
||||||
|
req, err := http.NewRequest("POST", "", nil)
|
||||||
|
s.Require().NoError(err)
|
||||||
|
|
||||||
|
s.Assert().Error(s.client.do(context.Background(), req, struct{}{}))
|
||||||
|
s.Assert().EqualValues(req.Header.Get("Content-Type"), "application/json")
|
||||||
|
}
|
||||||
|
|
||||||
func (s *ClientSuite) TestClient_do_DoError() {
|
func (s *ClientSuite) TestClient_do_DoError() {
|
||||||
req, err := http.NewRequest("", "", nil)
|
req, err := http.NewRequest("", "", nil)
|
||||||
s.Require().NoError(err)
|
s.Require().NoError(err)
|
||||||
|
|
||||||
s.Assert().Error(s.client.do(req, struct{}{}))
|
s.Assert().Error(s.client.do(context.Background(), req, struct{}{}))
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *ClientSuite) TestClient_do_UnmarshalError() {
|
func (s *ClientSuite) TestClient_do_UnmarshalError() {
|
||||||
req, err := http.NewRequest("GET", s.addr, nil)
|
req, err := http.NewRequest("GET", s.addr, nil)
|
||||||
s.Require().NoError(err)
|
s.Require().NoError(err)
|
||||||
|
|
||||||
s.Assert().Error(s.client.do(req, struct{}{}))
|
s.Assert().Error(s.client.do(context.Background(), req, struct{}{}))
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestClientSuite(t *testing.T) {
|
func TestClientSuite(t *testing.T) {
|
||||||
|
@ -1,3 +1,4 @@
|
|||||||
|
// Package groupme defines a client capable of executing API commands for the GroupMe chat service
|
||||||
package groupme
|
package groupme
|
||||||
|
|
||||||
import (
|
import (
|
||||||
@ -44,7 +45,7 @@ func (t Timestamp) String() string {
|
|||||||
type PhoneNumber string
|
type PhoneNumber string
|
||||||
|
|
||||||
// Treated as a constant
|
// Treated as a constant
|
||||||
var phoneNumberRegex = regexp.MustCompile(`^\+[0-9]+ [0-9]{10}$`)
|
var phoneNumberRegex = regexp.MustCompile(`^\+\d+ \d{10}$`)
|
||||||
|
|
||||||
// Valid checks if the ID string is alpha numeric
|
// Valid checks if the ID string is alpha numeric
|
||||||
func (pn PhoneNumber) Valid() bool {
|
func (pn PhoneNumber) Valid() bool {
|
||||||
@ -55,40 +56,40 @@ func (pn PhoneNumber) String() string {
|
|||||||
return string(pn)
|
return string(pn)
|
||||||
}
|
}
|
||||||
|
|
||||||
// StatusCodes are returned by HTTP requests in
|
// HTTPStatusCode are returned by HTTP requests in
|
||||||
// the header and the json "meta" field
|
// the header and the json "meta" field
|
||||||
type HTTPStatusCode int
|
type HTTPStatusCode int
|
||||||
|
|
||||||
// Text used as constant name
|
// Text used as constant name
|
||||||
const (
|
const (
|
||||||
HTTP_Ok HTTPStatusCode = 200
|
HTTPOk HTTPStatusCode = 200
|
||||||
HTTP_Created HTTPStatusCode = 201
|
HTTPCreated HTTPStatusCode = 201
|
||||||
HTTP_NoContent HTTPStatusCode = 204
|
HTTPNoContent HTTPStatusCode = 204
|
||||||
HTTP_NotModified HTTPStatusCode = 304
|
HTTPNotModified HTTPStatusCode = 304
|
||||||
HTTP_BadRequest HTTPStatusCode = 400
|
HTTPBadRequest HTTPStatusCode = 400
|
||||||
HTTP_Unauthorized HTTPStatusCode = 401
|
HTTPUnauthorized HTTPStatusCode = 401
|
||||||
HTTP_Forbidden HTTPStatusCode = 403
|
HTTPForbidden HTTPStatusCode = 403
|
||||||
HTTP_NotFound HTTPStatusCode = 404
|
HTTPNotFound HTTPStatusCode = 404
|
||||||
HTTP_EnhanceYourCalm HTTPStatusCode = 420
|
HTTPEnhanceYourCalm HTTPStatusCode = 420
|
||||||
HTTP_InternalServerError HTTPStatusCode = 500
|
HTTPInternalServerError HTTPStatusCode = 500
|
||||||
HTTP_BadGateway HTTPStatusCode = 502
|
HTTPBadGateway HTTPStatusCode = 502
|
||||||
HTTP_ServiceUnavailable HTTPStatusCode = 503
|
HTTPServiceUnavailable HTTPStatusCode = 503
|
||||||
)
|
)
|
||||||
|
|
||||||
// String returns the description of the status code according to GroupMe
|
// String returns the description of the status code according to GroupMe
|
||||||
func (c HTTPStatusCode) String() string {
|
func (c HTTPStatusCode) String() string {
|
||||||
return map[HTTPStatusCode]string{
|
return map[HTTPStatusCode]string{
|
||||||
HTTP_Ok: "success",
|
HTTPOk: "success",
|
||||||
HTTP_Created: "resource was created successfully",
|
HTTPCreated: "resource was created successfully",
|
||||||
HTTP_NoContent: "resource was deleted successfully",
|
HTTPNoContent: "resource was deleted successfully",
|
||||||
HTTP_NotModified: "no new data to return",
|
HTTPNotModified: "no new data to return",
|
||||||
HTTP_BadRequest: "invalid format or data specified in the request",
|
HTTPBadRequest: "invalid format or data specified in the request",
|
||||||
HTTP_Unauthorized: "authentication credentials missing or incorrect",
|
HTTPUnauthorized: "authentication credentials missing or incorrect",
|
||||||
HTTP_Forbidden: "request refused due to update limits",
|
HTTPForbidden: "request refused due to update limits",
|
||||||
HTTP_NotFound: "URI is invalid or resource does not exist",
|
HTTPNotFound: "URI is invalid or resource does not exist",
|
||||||
HTTP_EnhanceYourCalm: "application is being rate limited",
|
HTTPEnhanceYourCalm: "application is being rate limited",
|
||||||
HTTP_InternalServerError: "something unexpected occurred",
|
HTTPInternalServerError: "something unexpected occurred",
|
||||||
HTTP_BadGateway: "GroupMe is down or being upgraded",
|
HTTPBadGateway: "GroupMe is down or being upgraded",
|
||||||
HTTP_ServiceUnavailable: "servers are overloaded, try again later",
|
HTTPServiceUnavailable: "servers are overloaded, try again later",
|
||||||
}[c]
|
}[c]
|
||||||
}
|
}
|
||||||
|
@ -1,3 +1,4 @@
|
|||||||
|
// Package groupme defines a client capable of executing API commands for the GroupMe chat service
|
||||||
package groupme
|
package groupme
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
@ -1,16 +1,19 @@
|
|||||||
|
// Package groupme defines a client capable of executing API commands for the GroupMe chat service
|
||||||
package groupme
|
package groupme
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"bytes"
|
||||||
|
"context"
|
||||||
|
"encoding/json"
|
||||||
"fmt"
|
"fmt"
|
||||||
"net/http"
|
"net/http"
|
||||||
"net/url"
|
|
||||||
|
|
||||||
"github.com/google/uuid"
|
"github.com/google/uuid"
|
||||||
)
|
)
|
||||||
|
|
||||||
// GroupMe documentation: https://dev.groupme.com/docs/v3#direct_messages
|
// GroupMe documentation: https://dev.groupme.com/docs/v3#direct_messages
|
||||||
|
|
||||||
////////// Endpoints //////////
|
/*//////// Endpoints ////////*/
|
||||||
const (
|
const (
|
||||||
// Used to build other endpoints
|
// Used to build other endpoints
|
||||||
directMessagesEndpointRoot = "/direct_messages"
|
directMessagesEndpointRoot = "/direct_messages"
|
||||||
@ -20,11 +23,9 @@ const (
|
|||||||
createDirectMessageEndpoint = directMessagesEndpointRoot // POST
|
createDirectMessageEndpoint = directMessagesEndpointRoot // POST
|
||||||
)
|
)
|
||||||
|
|
||||||
////////// API Requests //////////
|
/*//////// API Requests ////////*/
|
||||||
|
|
||||||
// Index
|
// IndexDirectMessagesQuery defines the optional URL parameters for IndexDirectMessages
|
||||||
|
|
||||||
// MessagesQuery defines the optional URL parameters for IndexDirectMessages
|
|
||||||
type IndexDirectMessagesQuery struct {
|
type IndexDirectMessagesQuery struct {
|
||||||
// Returns 20 messages created before the given message ID
|
// Returns 20 messages created before the given message ID
|
||||||
BeforeID ID `json:"before_id"`
|
BeforeID ID `json:"before_id"`
|
||||||
@ -62,17 +63,18 @@ Note that for historical reasons, likes are returned as an array
|
|||||||
of user ids in the favorited_by key.
|
of user ids in the favorited_by key.
|
||||||
|
|
||||||
Parameters:
|
Parameters:
|
||||||
|
|
||||||
otherUserID - required, ID(string); the other participant in the conversation.
|
otherUserID - required, ID(string); the other participant in the conversation.
|
||||||
See IndexDirectMessagesQuery
|
See IndexDirectMessagesQuery
|
||||||
*/
|
*/
|
||||||
func (c *Client) IndexDirectMessages(otherUserID ID, req *IndexDirectMessagesQuery) (IndexDirectMessagesResponse, error) {
|
func (c *Client) IndexDirectMessages(ctx context.Context, otherUserID string, req *IndexDirectMessagesQuery, authToken string) (IndexDirectMessagesResponse, error) {
|
||||||
httpReq, err := http.NewRequest("GET", c.endpointBase+indexDirectMessagesEndpoint, nil)
|
httpReq, err := http.NewRequest("GET", c.endpointBase+indexDirectMessagesEndpoint, nil)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return IndexDirectMessagesResponse{}, err
|
return IndexDirectMessagesResponse{}, err
|
||||||
}
|
}
|
||||||
|
|
||||||
query := httpReq.URL.Query()
|
query := httpReq.URL.Query()
|
||||||
query.Set("other_user_id", otherUserID.String())
|
query.Set("other_user_id", otherUserID)
|
||||||
if req != nil {
|
if req != nil {
|
||||||
if req.BeforeID != "" {
|
if req.BeforeID != "" {
|
||||||
query.Add("before_ID", req.BeforeID.String())
|
query.Add("before_ID", req.BeforeID.String())
|
||||||
@ -81,9 +83,10 @@ func (c *Client) IndexDirectMessages(otherUserID ID, req *IndexDirectMessagesQue
|
|||||||
query.Add("since_id", req.SinceID.String())
|
query.Add("since_id", req.SinceID.String())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
httpReq.URL.RawQuery = query.Encode()
|
||||||
|
|
||||||
var resp IndexDirectMessagesResponse
|
var resp IndexDirectMessagesResponse
|
||||||
err = c.doWithAuthToken(httpReq, &resp)
|
err = c.doWithAuthToken(ctx, httpReq, &resp, authToken)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return IndexDirectMessagesResponse{}, err
|
return IndexDirectMessagesResponse{}, err
|
||||||
}
|
}
|
||||||
@ -91,12 +94,8 @@ func (c *Client) IndexDirectMessages(otherUserID ID, req *IndexDirectMessagesQue
|
|||||||
return resp, nil
|
return resp, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// Create
|
|
||||||
|
|
||||||
/*
|
/*
|
||||||
CreateDirectMessage -
|
CreateDirectMessage - Send a DM to another user
|
||||||
|
|
||||||
Send a DM to another user
|
|
||||||
|
|
||||||
If you want to attach an image, you must first process it
|
If you want to attach an image, you must first process it
|
||||||
through our image service.
|
through our image service.
|
||||||
@ -108,35 +107,31 @@ specify a replacement charmap to substitute emoji characters
|
|||||||
|
|
||||||
The character map is an array of arrays containing rune data
|
The character map is an array of arrays containing rune data
|
||||||
([[{pack_id,offset}],...]).
|
([[{pack_id,offset}],...]).
|
||||||
|
|
||||||
Parameters:
|
|
||||||
See Message.
|
|
||||||
recipientID - required, ID(string); The GroupMe user ID of the recipient of this message.
|
|
||||||
text - required, string. Can be ommitted if at least one
|
|
||||||
attachment is present
|
|
||||||
attachments - a polymorphic list of attachments (locations,
|
|
||||||
images, etc). You may have You may have more than
|
|
||||||
one of any type of attachment, provided clients can
|
|
||||||
display it.
|
|
||||||
*/
|
*/
|
||||||
func (c *Client) CreateDirectMessage(m *Message) (*Message, error) {
|
func (c *Client) CreateDirectMessage(ctx context.Context, m *Message, authToken string) (*Message, error) {
|
||||||
URL := fmt.Sprintf(c.endpointBase + createDirectMessageEndpoint)
|
URL := fmt.Sprintf(c.endpointBase + createDirectMessageEndpoint)
|
||||||
httpReq, err := http.NewRequest("POST", URL, nil)
|
|
||||||
|
m.SourceGUID = uuid.New().String()
|
||||||
|
var data = struct {
|
||||||
|
DirectMessage *Message `json:"direct_message,omitempty"`
|
||||||
|
}{
|
||||||
|
m,
|
||||||
|
}
|
||||||
|
|
||||||
|
jsonBytes, err := json.Marshal(&data)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
m.SourceGUID = uuid.New().String()
|
httpReq, err := http.NewRequest("POST", URL, bytes.NewBuffer(jsonBytes))
|
||||||
|
if err != nil {
|
||||||
data := url.Values{}
|
return nil, err
|
||||||
data.Set("direct_message", m.String())
|
}
|
||||||
|
|
||||||
httpReq.PostForm = data
|
|
||||||
|
|
||||||
var resp struct {
|
var resp struct {
|
||||||
*Message `json:"message"`
|
*Message `json:"direct_message"`
|
||||||
}
|
}
|
||||||
err = c.doWithAuthToken(httpReq, &resp)
|
err = c.doWithAuthToken(ctx, httpReq, &resp, authToken)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
@ -1,6 +1,8 @@
|
|||||||
|
// Package groupme defines a client capable of executing API commands for the GroupMe chat service
|
||||||
package groupme
|
package groupme
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"context"
|
||||||
"fmt"
|
"fmt"
|
||||||
"net/http"
|
"net/http"
|
||||||
"testing"
|
"testing"
|
||||||
@ -18,7 +20,8 @@ func (s *DirectMessagesAPISuite) SetupSuite() {
|
|||||||
|
|
||||||
func (s *DirectMessagesAPISuite) TestDirectMessagesIndex() {
|
func (s *DirectMessagesAPISuite) TestDirectMessagesIndex() {
|
||||||
resp, err := s.client.IndexDirectMessages(
|
resp, err := s.client.IndexDirectMessages(
|
||||||
ID("123"),
|
context.Background(),
|
||||||
|
"123",
|
||||||
&IndexDirectMessagesQuery{
|
&IndexDirectMessagesQuery{
|
||||||
BeforeID: "0123456789",
|
BeforeID: "0123456789",
|
||||||
SinceID: "9876543210",
|
SinceID: "9876543210",
|
||||||
@ -33,6 +36,7 @@ func (s *DirectMessagesAPISuite) TestDirectMessagesIndex() {
|
|||||||
|
|
||||||
func (s *DirectMessagesAPISuite) TestDirectMessagesCreate() {
|
func (s *DirectMessagesAPISuite) TestDirectMessagesCreate() {
|
||||||
message, err := s.client.CreateDirectMessage(
|
message, err := s.client.CreateDirectMessage(
|
||||||
|
context.Background(),
|
||||||
&Message{
|
&Message{
|
||||||
RecipientID: ID("123"),
|
RecipientID: ID("123"),
|
||||||
Text: "Test",
|
Text: "Test",
|
||||||
@ -47,6 +51,7 @@ func TestDirectMessagesAPISuite(t *testing.T) {
|
|||||||
suite.Run(t, new(DirectMessagesAPISuite))
|
suite.Run(t, new(DirectMessagesAPISuite))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// nolint // not duplicate code
|
||||||
func directMessagesTestRouter() *mux.Router {
|
func directMessagesTestRouter() *mux.Router {
|
||||||
router := mux.NewRouter().Queries("token", "").Subrouter()
|
router := mux.NewRouter().Queries("token", "").Subrouter()
|
||||||
|
|
||||||
|
@ -1,9 +1,8 @@
|
|||||||
package main
|
package main
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"context"
|
||||||
"fmt"
|
"fmt"
|
||||||
|
|
||||||
"github.com/densestvoid/groupme"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
// This is not a real token. Please find yours by logging
|
// This is not a real token. Please find yours by logging
|
||||||
@ -18,11 +17,14 @@ func main() {
|
|||||||
client := groupme.NewClient(authorizationToken)
|
client := groupme.NewClient(authorizationToken)
|
||||||
|
|
||||||
// Get the groups your user is part of
|
// Get the groups your user is part of
|
||||||
groups, err := client.IndexGroups(&groupme.GroupsQuery{
|
groups, err := client.IndexGroups(
|
||||||
|
context.Background(),
|
||||||
|
&groupme.GroupsQuery{
|
||||||
Page: 0,
|
Page: 0,
|
||||||
PerPage: 5,
|
PerPage: 5,
|
||||||
Omit: "memberships",
|
Omit: "memberships",
|
||||||
})
|
},
|
||||||
|
)
|
||||||
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
fmt.Println(err)
|
fmt.Println(err)
|
||||||
@ -32,11 +34,11 @@ func main() {
|
|||||||
fmt.Println(groups)
|
fmt.Println(groups)
|
||||||
|
|
||||||
// Get first 10 messages of the first group
|
// Get first 10 messages of the first group
|
||||||
if len(groups) <= 0 {
|
if len(groups) == 0 {
|
||||||
fmt.Println("No groups")
|
fmt.Println("No groups")
|
||||||
}
|
}
|
||||||
|
|
||||||
messages, err := client.IndexMessages(groups[0].ID, &groupme.IndexMessagesQuery{
|
messages, err := client.IndexMessages(context.Background(), groups[0].ID, &groupme.IndexMessagesQuery{
|
||||||
Limit: 10,
|
Limit: 10,
|
||||||
})
|
})
|
||||||
|
|
||||||
|
19
examples/post_bot_message/main.go
Normal file
19
examples/post_bot_message/main.go
Normal file
@ -0,0 +1,19 @@
|
|||||||
|
package main
|
||||||
|
|
||||||
|
import (
|
||||||
|
"context"
|
||||||
|
"fmt"
|
||||||
|
)
|
||||||
|
|
||||||
|
// This is not a real Bot ID. Please find yours by logging
|
||||||
|
// into the GroupMe development website: https://dev.groupme.com/bots
|
||||||
|
const botID = "0123456789ABCDEF"
|
||||||
|
|
||||||
|
// A short program that gets the gets the first 5 groups
|
||||||
|
// the user is part of, and then the first 10 messages of
|
||||||
|
// the first group in that list
|
||||||
|
func main() {
|
||||||
|
// Create a new client with your auth token
|
||||||
|
client := groupme.NewClient("")
|
||||||
|
fmt.Println(client.PostBotMessage(context.Background(), botID, "Your message here!", nil))
|
||||||
|
}
|
177
examples/real_time_updates/main.go
Normal file
177
examples/real_time_updates/main.go
Normal file
@ -0,0 +1,177 @@
|
|||||||
|
package main
|
||||||
|
|
||||||
|
import (
|
||||||
|
"context"
|
||||||
|
"fmt"
|
||||||
|
"log"
|
||||||
|
|
||||||
|
"gitea.watsonlabs.net/watsonb8/groupme-lib"
|
||||||
|
)
|
||||||
|
|
||||||
|
// This is not a real token. Please find yours by logging
|
||||||
|
// into the GroupMe development website: https://dev.groupme.com/
|
||||||
|
|
||||||
|
var authorizationToken = "ASD"
|
||||||
|
var authorizationToken2 = "ASDF"
|
||||||
|
|
||||||
|
// A short program that subscribes to 2 groups and 2 direct chats
|
||||||
|
// and prints out all recognized events in those
|
||||||
|
func main() {
|
||||||
|
//create push subscription and start listening
|
||||||
|
p := groupme.NewPushSubscription(context.Background())
|
||||||
|
err := p.Connect(context.TODO())
|
||||||
|
if err != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// Create a new client with your auth token
|
||||||
|
client := groupme.NewClient()
|
||||||
|
User, _ := client.MyUser(context.Background(), authorizationToken)
|
||||||
|
User2, _ := client.MyUser(context.Background(), authorizationToken2)
|
||||||
|
|
||||||
|
//handles (in this case prints) all messages
|
||||||
|
p.AddFullHandler(Handler{User: User}, authorizationToken)
|
||||||
|
p.AddHandler(Handler{User: User2}, authorizationToken2)
|
||||||
|
|
||||||
|
//Subscribe to get messages and events for the specific user
|
||||||
|
p.SubscribeToUser(context.Background(), User.ID, authorizationToken)
|
||||||
|
p.SubscribeToUser(context.Background(), User2.ID, authorizationToken2)
|
||||||
|
if err != nil {
|
||||||
|
log.Fatal(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Get the groups your user is part of
|
||||||
|
groups, err := client.IndexGroups(
|
||||||
|
context.Background(),
|
||||||
|
&groupme.GroupsQuery{
|
||||||
|
Page: 0,
|
||||||
|
PerPage: 2,
|
||||||
|
Omit: "memberships",
|
||||||
|
}, authorizationToken)
|
||||||
|
|
||||||
|
groups2, err := client.IndexGroups(
|
||||||
|
context.Background(),
|
||||||
|
&groupme.GroupsQuery{
|
||||||
|
Page: 0,
|
||||||
|
PerPage: 2,
|
||||||
|
Omit: "memberships",
|
||||||
|
}, authorizationToken2)
|
||||||
|
|
||||||
|
if err != nil {
|
||||||
|
fmt.Println(err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
//Subscribe to those groups
|
||||||
|
for _, j := range groups {
|
||||||
|
err = p.SubscribeToGroup(context.TODO(), j.ID, authorizationToken)
|
||||||
|
if err != nil {
|
||||||
|
log.Fatal(err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, j := range groups2 {
|
||||||
|
err = p.SubscribeToGroup(context.TODO(), j.ID, authorizationToken2)
|
||||||
|
if err != nil {
|
||||||
|
log.Fatal(err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
//get chats your user is part of
|
||||||
|
chats, err := client.IndexChats(context.Background(),
|
||||||
|
&groupme.IndexChatsQuery{
|
||||||
|
Page: 0,
|
||||||
|
PerPage: 2,
|
||||||
|
}, authorizationToken)
|
||||||
|
chats2, err := client.IndexChats(context.Background(),
|
||||||
|
&groupme.IndexChatsQuery{
|
||||||
|
Page: 0,
|
||||||
|
PerPage: 2,
|
||||||
|
}, authorizationToken2)
|
||||||
|
//subscribe to all those chats
|
||||||
|
for _, j := range chats {
|
||||||
|
go func() {
|
||||||
|
err := p.SubscribeToDM(context.TODO(), j.LastMessage.ConversationID, authorizationToken)
|
||||||
|
if err != nil {
|
||||||
|
log.Fatal(err)
|
||||||
|
}
|
||||||
|
}()
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, j := range chats2 {
|
||||||
|
go func() {
|
||||||
|
err := p.SubscribeToDM(context.TODO(), j.LastMessage.ConversationID, authorizationToken2)
|
||||||
|
if err != nil {
|
||||||
|
log.Fatal(err)
|
||||||
|
}
|
||||||
|
}()
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
//blocking
|
||||||
|
<-make(chan (struct{}))
|
||||||
|
}
|
||||||
|
|
||||||
|
// Following example handlers print out all data
|
||||||
|
type Handler struct {
|
||||||
|
User *groupme.User
|
||||||
|
}
|
||||||
|
|
||||||
|
func (h Handler) HandleError(e error) {
|
||||||
|
fmt.Println(e)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (h Handler) HandleTextMessage(msg groupme.Message) {
|
||||||
|
fmt.Println(msg.Text, msg.Name, msg.Attachments)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (h Handler) HandleJoin(group groupme.ID) {
|
||||||
|
fmt.Println("User joined group with id", group.String())
|
||||||
|
}
|
||||||
|
|
||||||
|
func (h Handler) HandleLike(msg groupme.Message) {
|
||||||
|
fmt.Println(msg.ID, "liked by", msg.FavoritedBy)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (h Handler) HandlerMembership(i groupme.ID) {
|
||||||
|
fmt.Println("Membership event on", i.String())
|
||||||
|
}
|
||||||
|
|
||||||
|
func (h Handler) HandleGroupTopic(group groupme.ID, newTopic string) {
|
||||||
|
fmt.Println(group.String(), "has new topic of", newTopic)
|
||||||
|
}
|
||||||
|
func (h Handler) HandleGroupName(group groupme.ID, newName string) {
|
||||||
|
fmt.Println(group.String(), "has new name of", newName)
|
||||||
|
}
|
||||||
|
func (h Handler) HandleGroupAvatar(group groupme.ID, newAvatar string) {
|
||||||
|
fmt.Println(group.String(), "has new avatar url of", newAvatar)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (h Handler) HandleLikeIcon(group groupme.ID, PackID, PackIndex int, Type string) {
|
||||||
|
//Not sure how to use without groupme icon packs
|
||||||
|
if len(Type) == 0 {
|
||||||
|
fmt.Println("Default like icon set")
|
||||||
|
return
|
||||||
|
}
|
||||||
|
fmt.Println(group.String(), "has new like icon of", PackID, PackIndex, Type)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (h Handler) HandleNewNickname(group groupme.ID, user groupme.ID, newName string) {
|
||||||
|
fmt.Printf("In group %s, user %s has new nickname %s\n", group.String(), user.String(), newName)
|
||||||
|
}
|
||||||
|
func (h Handler) HandleNewAvatarInGroup(group groupme.ID, user groupme.ID, avatarURL string) {
|
||||||
|
if avatarURL == "" {
|
||||||
|
//get default avatar
|
||||||
|
avatarURL = h.User.ImageURL
|
||||||
|
}
|
||||||
|
fmt.Printf("In group %s, user %s has new avatar with url %s\n", group.String(), user.String(), avatarURL)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (h Handler) HandleMembers(group groupme.ID, members []groupme.Member, added bool) {
|
||||||
|
action := "removed"
|
||||||
|
if added {
|
||||||
|
action = "added"
|
||||||
|
}
|
||||||
|
|
||||||
|
fmt.Printf("In group %s, users %v %s\n", group.String(), members, action)
|
||||||
|
}
|
27
go.mod
27
go.mod
@ -1,18 +1,17 @@
|
|||||||
module github.com/densestvoid/groupme
|
module gitea.watsonlabs.net/watsonb8/groupme-lib
|
||||||
|
|
||||||
go 1.14
|
go 1.21.0
|
||||||
|
|
||||||
require (
|
require (
|
||||||
github.com/go-critic/go-critic v0.5.0 // indirect
|
gitea.watsonlabs.net/watsonb8/fayec v0.0.5-0.20230926180210-b375ab3c8c11
|
||||||
github.com/go-toolsmith/astinfo v1.0.0 // indirect
|
github.com/google/uuid v1.2.0
|
||||||
github.com/google/go-cmp v0.5.1 // indirect
|
github.com/gorilla/mux v1.8.0
|
||||||
github.com/google/uuid v1.1.1
|
github.com/stretchr/testify v1.7.0
|
||||||
github.com/gorilla/mux v1.7.4
|
)
|
||||||
github.com/logrusorgru/aurora v2.0.3+incompatible // indirect
|
|
||||||
github.com/mattn/goveralls v0.0.6 // indirect
|
require (
|
||||||
github.com/quasilyte/go-consistent v0.0.0-20200404105227-766526bf1e96 // indirect
|
github.com/davecgh/go-spew v1.1.0 // indirect
|
||||||
github.com/quasilyte/go-ruleguard v0.1.3 // indirect
|
github.com/gorilla/websocket v1.5.0 // indirect
|
||||||
github.com/quasilyte/regex/syntax v0.0.0-20200419152657-af9db7f4a3ab // indirect
|
github.com/pmezard/go-difflib v1.0.0 // indirect
|
||||||
github.com/stretchr/testify v1.5.1
|
gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c // indirect
|
||||||
golang.org/x/tools v0.0.0-20200728190822-edd3c8e9e279 // indirect
|
|
||||||
)
|
)
|
||||||
|
135
go.sum
135
go.sum
@ -1,105 +1,50 @@
|
|||||||
|
gitea.watsonlabs.net/watsonb8/fayec v0.0.0-20230919020138-8f0db7048755 h1:FEhNSjSNvZ+nVg5Z3ds6X8ys3qjM+mmyLTSqKhCUHuQ=
|
||||||
|
gitea.watsonlabs.net/watsonb8/fayec v0.0.0-20230919020138-8f0db7048755/go.mod h1:gv8CWMq6dFJQhH30u8bO3u4k2irKlclZktLNYDebQ/0=
|
||||||
|
gitea.watsonlabs.net/watsonb8/fayec v0.0.0-20230919151904-5ca9ade6f946 h1:loc70tiaFs1U4sqn+lKMSBlo5OfvVfClYnWjfGLXaSg=
|
||||||
|
gitea.watsonlabs.net/watsonb8/fayec v0.0.0-20230919151904-5ca9ade6f946/go.mod h1:gv8CWMq6dFJQhH30u8bO3u4k2irKlclZktLNYDebQ/0=
|
||||||
|
gitea.watsonlabs.net/watsonb8/fayec v0.0.1 h1:MNFmTaTyyKKgrw04dGO9C5ojtm1jIvy8oHYqbj0ECeY=
|
||||||
|
gitea.watsonlabs.net/watsonb8/fayec v0.0.1/go.mod h1:gv8CWMq6dFJQhH30u8bO3u4k2irKlclZktLNYDebQ/0=
|
||||||
|
gitea.watsonlabs.net/watsonb8/fayec v0.0.2 h1:tqbgr1vRZ6Wq4W81xBg+FTOywSv3EJpK263SAVXXTco=
|
||||||
|
gitea.watsonlabs.net/watsonb8/fayec v0.0.2/go.mod h1:gv8CWMq6dFJQhH30u8bO3u4k2irKlclZktLNYDebQ/0=
|
||||||
|
gitea.watsonlabs.net/watsonb8/fayec v0.0.3 h1:YpaZBIee8Ix6uGm1UoEtBix1dEU1TURChAsJGJ3pVRo=
|
||||||
|
gitea.watsonlabs.net/watsonb8/fayec v0.0.3/go.mod h1:gv8CWMq6dFJQhH30u8bO3u4k2irKlclZktLNYDebQ/0=
|
||||||
|
gitea.watsonlabs.net/watsonb8/fayec v0.0.4 h1:SLvwip1DQy13QngVsEgoLtN7T6bS+X6348p6PQhUF2A=
|
||||||
|
gitea.watsonlabs.net/watsonb8/fayec v0.0.4/go.mod h1:gv8CWMq6dFJQhH30u8bO3u4k2irKlclZktLNYDebQ/0=
|
||||||
|
gitea.watsonlabs.net/watsonb8/fayec v0.0.5-0.20230926180210-b375ab3c8c11 h1:xJ9eSFyIrDA43UVpbxOD1QkA2jhg+vS+eezFKCDV3Dw=
|
||||||
|
gitea.watsonlabs.net/watsonb8/fayec v0.0.5-0.20230926180210-b375ab3c8c11/go.mod h1:gv8CWMq6dFJQhH30u8bO3u4k2irKlclZktLNYDebQ/0=
|
||||||
|
gitea.watsonlabs.net/watsonb8/fayec v0.0.5 h1:9+UHzUuEcLuZ5Gx5S/NTBxYshUhsiQ5M3vzUF8RAKxw=
|
||||||
|
gitea.watsonlabs.net/watsonb8/fayec v0.0.5/go.mod h1:gv8CWMq6dFJQhH30u8bO3u4k2irKlclZktLNYDebQ/0=
|
||||||
|
github.com/autogrowsystems/wray v0.0.0-20160519030252-f36984f6648c/go.mod h1:druJ8QMeBCUmwJ7ZSFowx77dWxEWF3SYlQlsqZaLZQg=
|
||||||
github.com/davecgh/go-spew v1.1.0 h1:ZDRjVQ15GmhC3fiQ8ni8+OwkZQO4DARzQgrnXU1Liz8=
|
github.com/davecgh/go-spew v1.1.0 h1:ZDRjVQ15GmhC3fiQ8ni8+OwkZQO4DARzQgrnXU1Liz8=
|
||||||
github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
||||||
github.com/go-critic/go-critic v0.5.0 h1:Ic2p5UCl5fX/2WX2w8nroPpPhxRNsNTMlJzsu/uqwnM=
|
github.com/google/uuid v1.2.0 h1:qJYtXnJRWmpe7m/3XlyhrsLrEURqHRM2kxzoxXqyUDs=
|
||||||
github.com/go-critic/go-critic v0.5.0/go.mod h1:4jeRh3ZAVnRYhuWdOEvwzVqLUpxMSoAT0xZ74JsTPlo=
|
github.com/google/uuid v1.2.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
|
||||||
github.com/go-lintpack/lintpack v0.5.2/go.mod h1:NwZuYi2nUHho8XEIZ6SIxihrnPoqBTDqfpXvXAN0sXM=
|
github.com/gopherjs/gopherjs v0.0.0-20181017120253-0766667cb4d1 h1:EGx4pi6eqNxGaHF6qqu48+N2wcFQ5qg5FXgOdqsJ5d8=
|
||||||
github.com/go-toolsmith/astcast v1.0.0 h1:JojxlmI6STnFVG9yOImLeGREv8W2ocNUM+iOhR6jE7g=
|
github.com/gopherjs/gopherjs v0.0.0-20181017120253-0766667cb4d1/go.mod h1:wJfORRmW1u3UXTncJ5qlYoELFm8eSnnEO6hX4iZ3EWY=
|
||||||
github.com/go-toolsmith/astcast v1.0.0/go.mod h1:mt2OdQTeAQcY4DQgPSArJjHCcOwlX+Wl/kwN+LbLGQ4=
|
github.com/gorilla/mux v1.8.0 h1:i40aqfkR1h2SlN9hojwV5ZA91wcXFOvkdNIeFDP5koI=
|
||||||
github.com/go-toolsmith/astcopy v1.0.0 h1:OMgl1b1MEpjFQ1m5ztEO06rz5CUd3oBv9RF7+DyvdG8=
|
github.com/gorilla/mux v1.8.0/go.mod h1:DVbg23sWSpFRCP0SfiEN6jmj59UnW/n46BH5rLB71So=
|
||||||
github.com/go-toolsmith/astcopy v1.0.0/go.mod h1:vrgyG+5Bxrnz4MZWPF+pI4R8h3qKRjjyvV/DSez4WVQ=
|
github.com/gorilla/websocket v1.5.0 h1:PPwGk2jz7EePpoHN/+ClbZu8SPxiqlu12wZP/3sWmnc=
|
||||||
github.com/go-toolsmith/astequal v0.0.0-20180903214952-dcb477bfacd6/go.mod h1:H+xSiq0+LtiDC11+h1G32h7Of5O3CYFJ99GVbS5lDKY=
|
github.com/gorilla/websocket v1.5.0/go.mod h1:YR8l580nyteQvAITg2hZ9XVh4b55+EU/adAjf1fMHhE=
|
||||||
github.com/go-toolsmith/astequal v1.0.0 h1:4zxD8j3JRFNyLN46lodQuqz3xdKSrur7U/sr0SDS/gQ=
|
github.com/jtolds/gls v4.20.0+incompatible h1:xdiiI2gbIgH/gLH7ADydsJ1uDOEzR8yvV7C0MuV77Wo=
|
||||||
github.com/go-toolsmith/astequal v1.0.0/go.mod h1:H+xSiq0+LtiDC11+h1G32h7Of5O3CYFJ99GVbS5lDKY=
|
github.com/jtolds/gls v4.20.0+incompatible/go.mod h1:QJZ7F/aHp+rZTRtaJ1ow/lLfFfVYBRgL+9YlvaHOwJU=
|
||||||
github.com/go-toolsmith/astfmt v0.0.0-20180903215011-8f8ee99c3086/go.mod h1:mP93XdblcopXwlyN4X4uodxXQhldPGZbcEJIimQHrkg=
|
github.com/karmanyaahm/wray v0.0.0-20210303233435-756d58657c14 h1:NrATjZKvkY+ojL8FXTWa3fQ+wihFrAxLNE6T+wOkIcY=
|
||||||
github.com/go-toolsmith/astfmt v1.0.0 h1:A0vDDXt+vsvLEdbMFJAUBI/uTbRw1ffOPnxsILnFL6k=
|
github.com/karmanyaahm/wray v0.0.0-20210303233435-756d58657c14/go.mod h1:ysD86MIEevmAkdfdg5s6Qt3I07RN6fvMAyna7jCGG2o=
|
||||||
github.com/go-toolsmith/astfmt v1.0.0/go.mod h1:cnWmsOAuq4jJY6Ct5YWlVLmcmLMn1JUPuQIHCY7CJDw=
|
github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
|
||||||
github.com/go-toolsmith/astinfo v0.0.0-20180906194353-9809ff7efb21 h1:wP6mXeB2V/d1P1K7bZ5vDUO3YqEzcvOREOxZPEu3gVI=
|
|
||||||
github.com/go-toolsmith/astinfo v0.0.0-20180906194353-9809ff7efb21/go.mod h1:dDStQCHtmZpYOmjRP/8gHHnCCch3Zz3oEgCdZVdtweU=
|
|
||||||
github.com/go-toolsmith/astinfo v1.0.0 h1:rNuhpyhsnsze/Pe1l/GUHwxo1rmN7Dyb6oAnFcrXh+w=
|
|
||||||
github.com/go-toolsmith/astinfo v1.0.0/go.mod h1:dDStQCHtmZpYOmjRP/8gHHnCCch3Zz3oEgCdZVdtweU=
|
|
||||||
github.com/go-toolsmith/astp v0.0.0-20180903215135-0af7e3c24f30/go.mod h1:SV2ur98SGypH1UjcPpCatrV5hPazG6+IfNHbkDXBRrk=
|
|
||||||
github.com/go-toolsmith/astp v1.0.0 h1:alXE75TXgcmupDsMK1fRAy0YUzLzqPVvBKoyWV+KPXg=
|
|
||||||
github.com/go-toolsmith/astp v1.0.0/go.mod h1:RSyrtpVlfTFGDYRbrjyWP1pYu//tSFcvdYrA8meBmLI=
|
|
||||||
github.com/go-toolsmith/pkgload v0.0.0-20181119091011-e9e65178eee8/go.mod h1:WoMrjiy4zvdS+Bg6z9jZH82QXwkcgCBX6nOfnmdaHks=
|
|
||||||
github.com/go-toolsmith/pkgload v1.0.0 h1:4DFWWMXVfbcN5So1sBNW9+yeiMqLFGl1wFLTL5R0Tgg=
|
|
||||||
github.com/go-toolsmith/pkgload v1.0.0/go.mod h1:5eFArkbO80v7Z0kdngIxsRXRMTaX4Ilcwuh3clNrQJc=
|
|
||||||
github.com/go-toolsmith/strparse v1.0.0 h1:Vcw78DnpCAKlM20kSbAyO4mPfJn/lyYA4BJUDxe2Jb4=
|
|
||||||
github.com/go-toolsmith/strparse v1.0.0/go.mod h1:YI2nUKP9YGZnL/L1/DLFBfixrcjslWct4wyljWhSRy8=
|
|
||||||
github.com/go-toolsmith/typep v1.0.0/go.mod h1:JSQCQMUPdRlMZFswiq3TGpNp1GMktqkR2Ns5AIQkATU=
|
|
||||||
github.com/go-toolsmith/typep v1.0.2 h1:8xdsa1+FSIH/RhEkgnD1j2CJOy5mNllW1Q9tRiYwvlk=
|
|
||||||
github.com/go-toolsmith/typep v1.0.2/go.mod h1:JSQCQMUPdRlMZFswiq3TGpNp1GMktqkR2Ns5AIQkATU=
|
|
||||||
github.com/google/go-cmp v0.2.0 h1:+dTQ8DZQJz0Mb/HjFlkptS1FeQ4cWSnN941F8aEG4SQ=
|
|
||||||
github.com/google/go-cmp v0.2.0/go.mod h1:oXzfMopK8JAjlY9xF4vHSVASa0yLyX7SntLO5aqRK0M=
|
|
||||||
github.com/google/go-cmp v0.5.1 h1:JFrFEBb2xKufg6XkJsJr+WbKb4FQlURi5RUcBveYu9k=
|
|
||||||
github.com/google/go-cmp v0.5.1/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
|
|
||||||
github.com/google/uuid v1.0.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
|
|
||||||
github.com/google/uuid v1.1.1 h1:Gkbcsh/GbpXz7lPftLA3P6TYMwjCLYm83jiFQZF/3gY=
|
|
||||||
github.com/google/uuid v1.1.1/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
|
|
||||||
github.com/gorilla/mux v1.7.4 h1:VuZ8uybHlWmqV03+zRzdwKL4tUnIp1MAQtp1mIFE1bc=
|
|
||||||
github.com/gorilla/mux v1.7.4/go.mod h1:DVbg23sWSpFRCP0SfiEN6jmj59UnW/n46BH5rLB71So=
|
|
||||||
github.com/kisielk/gotool v1.0.0 h1:AV2c/EiW3KqPNT9ZKl07ehoAGi4C5/01Cfbblndcapg=
|
|
||||||
github.com/kisielk/gotool v1.0.0/go.mod h1:XhKaO+MFFWcvkIS/tQcRk01m1F5IRFswLeQ+oQHNcck=
|
|
||||||
github.com/logrusorgru/aurora v0.0.0-20181002194514-a7b3b318ed4e h1:9MlwzLdW7QSDrhDjFlsEYmxpFyIoXmYRon3dt0io31k=
|
|
||||||
github.com/logrusorgru/aurora v0.0.0-20181002194514-a7b3b318ed4e/go.mod h1:7rIyQOR62GCctdiQpZ/zOJlFyk6y+94wXzv6RNZgaR4=
|
|
||||||
github.com/logrusorgru/aurora v2.0.3+incompatible h1:tOpm7WcpBTn4fjmVfgpQq0EfczGlG91VSDkswnjF5A8=
|
|
||||||
github.com/logrusorgru/aurora v2.0.3+incompatible/go.mod h1:7rIyQOR62GCctdiQpZ/zOJlFyk6y+94wXzv6RNZgaR4=
|
|
||||||
github.com/mattn/goveralls v0.0.2 h1:7eJB6EqsPhRVxvwEXGnqdO2sJI0PTsrWoTMXEk9/OQc=
|
|
||||||
github.com/mattn/goveralls v0.0.2/go.mod h1:8d1ZMHsd7fW6IRPKQh46F2WRpyib5/X4FOpevwGNQEw=
|
|
||||||
github.com/mattn/goveralls v0.0.6 h1:cr8Y0VMo/MnEZBjxNN/vh6G90SZ7IMb6lms1dzMoO+Y=
|
|
||||||
github.com/mattn/goveralls v0.0.6/go.mod h1:h8b4ow6FxSPMQHF6o2ve3qsclnffZjYTNEKmLesRwqw=
|
|
||||||
github.com/pborman/uuid v1.2.0/go.mod h1:X/NO0urCmaxf9VXbdlT7C2Yzkj2IKimNn4k+gtPdI/k=
|
|
||||||
github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
|
github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
|
||||||
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
|
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
|
||||||
github.com/quasilyte/go-consistent v0.0.0-20190521200055-c6f3937de18c h1:JoUA0uz9U0FVFq5p4LjEq4C0VgQ0El320s3Ms0V4eww=
|
github.com/smartystreets/assertions v0.0.0-20180927180507-b2de0cb4f26d h1:zE9ykElWQ6/NYmHa3jpm/yHnI4xSofP+UP6SpjHcSeM=
|
||||||
github.com/quasilyte/go-consistent v0.0.0-20190521200055-c6f3937de18c/go.mod h1:5STLWrekHfjyYwxBRVRXNOSewLJ3PWfDJd1VyTS21fI=
|
github.com/smartystreets/assertions v0.0.0-20180927180507-b2de0cb4f26d/go.mod h1:OnSkiWE9lh6wB0YB77sQom3nweQdgAjqCqsofrRNTgc=
|
||||||
github.com/quasilyte/go-consistent v0.0.0-20200404105227-766526bf1e96 h1:6VBkISnfYpPtRvpE9wsVoxX+i0cDQFBPQPYzw259xWY=
|
github.com/smartystreets/goconvey v1.6.4 h1:fv0U8FUIMPNf1L9lnHLvLhgicrIVChEkdzIKYqbNC9s=
|
||||||
github.com/quasilyte/go-consistent v0.0.0-20200404105227-766526bf1e96/go.mod h1:h5ob45vcE3sydtmo0lUDUmG3Y0HXudxMId1w+5G99VI=
|
github.com/smartystreets/goconvey v1.6.4/go.mod h1:syvi0/a8iFYH4r/RixwvyeAJjdLS9QV7WQ/tjFTllLA=
|
||||||
github.com/quasilyte/go-ruleguard v0.1.2-0.20200318202121-b00d7a75d3d8 h1:DvnesvLtRPQOvaUbfXfh0tpMHg29by0H7F2U+QIkSu8=
|
|
||||||
github.com/quasilyte/go-ruleguard v0.1.2-0.20200318202121-b00d7a75d3d8/go.mod h1:CGFX09Ci3pq9QZdj86B+VGIdNj4VyCo2iPOGS9esB/k=
|
|
||||||
github.com/quasilyte/go-ruleguard v0.1.3 h1:6BU9UaNiSbTRYriG0PTZACIMi1dLHzIWWseSpX4icNM=
|
|
||||||
github.com/quasilyte/go-ruleguard v0.1.3/go.mod h1:CGFX09Ci3pq9QZdj86B+VGIdNj4VyCo2iPOGS9esB/k=
|
|
||||||
github.com/quasilyte/regex/syntax v0.0.0-20200407221936-30656e2c4a95 h1:L8QM9bvf68pVdQ3bCFZMDmnt9yqcMBro1pC7F+IPYMY=
|
|
||||||
github.com/quasilyte/regex/syntax v0.0.0-20200407221936-30656e2c4a95/go.mod h1:rlzQ04UMyJXu/aOvhd8qT+hvDrFpiwqp8MRXDY9szc0=
|
|
||||||
github.com/quasilyte/regex/syntax v0.0.0-20200419152657-af9db7f4a3ab h1:rjBjlam2Bbr6Dwp0T8HY2paibXTjMsNQU7vUH8hB+C4=
|
|
||||||
github.com/quasilyte/regex/syntax v0.0.0-20200419152657-af9db7f4a3ab/go.mod h1:rlzQ04UMyJXu/aOvhd8qT+hvDrFpiwqp8MRXDY9szc0=
|
|
||||||
github.com/stretchr/objx v0.1.0 h1:4G4v2dO3VZwixGIRoQ5Lfboy6nUhCyYzaqnIAPPhYs4=
|
|
||||||
github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
|
github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
|
||||||
github.com/stretchr/testify v1.5.1 h1:nOGnQDM7FYENwehXlg/kFVnos3rEvtKTjRvOWSzb6H4=
|
github.com/stretchr/testify v1.7.0 h1:nwc3DEeHmmLAfoZucVR881uASk0Mfjw8xYJ99tb5CcY=
|
||||||
github.com/stretchr/testify v1.5.1/go.mod h1:5W2xD1RspED5o8YsWQXVCued0rvSQ+mT+I5cxcmMvtA=
|
github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
|
||||||
github.com/yuin/goldmark v1.1.25/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74=
|
|
||||||
github.com/yuin/goldmark v1.1.27/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74=
|
|
||||||
github.com/yuin/goldmark v1.1.32/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74=
|
|
||||||
golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
|
golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
|
||||||
golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
|
|
||||||
golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
|
|
||||||
golang.org/x/mod v0.2.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
|
|
||||||
golang.org/x/mod v0.3.0 h1:RM4zey1++hCTbCVQfnWeKs9/IEsaBLA8vTkd0WVtmH4=
|
|
||||||
golang.org/x/mod v0.3.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
|
|
||||||
golang.org/x/net v0.0.0-20190311183353-d8887717615a/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
|
golang.org/x/net v0.0.0-20190311183353-d8887717615a/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
|
||||||
golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
|
|
||||||
golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
|
|
||||||
golang.org/x/net v0.0.0-20200226121028-0de0cce0169b/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
|
|
||||||
golang.org/x/net v0.0.0-20200625001655-4c5254603344/go.mod h1:/O7V0waA8r7cgGh81Ro3o1hOxt32SMVPicZroKQ2sZA=
|
|
||||||
golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
|
||||||
golang.org/x/sync v0.0.0-20190911185100-cd5d95a43a6e/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
|
||||||
golang.org/x/sync v0.0.0-20200625203802-6e8e738ad208/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
|
||||||
golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
||||||
golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
|
||||||
golang.org/x/sys v0.0.0-20200323222414-85ca7c5b95cd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
|
||||||
golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
|
golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
|
||||||
golang.org/x/tools v0.0.0-20181117154741-2ddaf7f79a09/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
|
golang.org/x/tools v0.0.0-20190328211700-ab21143f2384/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs=
|
||||||
golang.org/x/tools v0.0.0-20190110163146-51295c7ec13a/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
|
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405 h1:yhCVgyC4o1eVCa2tZl7eS0r+SDo693bJlVdllGtEeKM=
|
||||||
golang.org/x/tools v0.0.0-20190521203540-521d6ed310dd h1:7E3PabyysDSEjnaANKBgums/hyvMI/HoHQ50qZEzTrg=
|
|
||||||
golang.org/x/tools v0.0.0-20190521203540-521d6ed310dd/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q=
|
|
||||||
golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
|
|
||||||
golang.org/x/tools v0.0.0-20200329025819-fd4102a86c65/go.mod h1:Sl4aGygMT6LrqrWclx+PTx3U+LnKx/seiNR+3G19Ar8=
|
|
||||||
golang.org/x/tools v0.0.0-20200522201501-cb1345f3a375/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE=
|
|
||||||
golang.org/x/tools v0.0.0-20200728190822-edd3c8e9e279 h1:VUQjqirfpXJk5i+LtIdDjCAqYrCqTarkUCmMVLqMmVQ=
|
|
||||||
golang.org/x/tools v0.0.0-20200728190822-edd3c8e9e279/go.mod h1:njjCfa9FT2d7l9Bc6FUM5FLjQPp3cFF28FI3qnDFljA=
|
|
||||||
golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
|
||||||
golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
|
||||||
golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543 h1:E7g+9GITq07hpfrRu66IVDexMakfv52eLZ2CXBWiKr4=
|
|
||||||
golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
|
||||||
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
|
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
|
||||||
gopkg.in/yaml.v2 v2.2.2 h1:ZCJp+EgiOT7lHqUV2J862kp8Qj64Jo6az82+3Td9dZw=
|
gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c h1:dUUwHk2QECo/6vqA44rthZ8ie2QXMNeKRTHCNY2nXvo=
|
||||||
gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
|
gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
|
||||||
|
190
groups_api.go
190
groups_api.go
@ -1,16 +1,19 @@
|
|||||||
|
// Package groupme defines a client capable of executing API commands for the GroupMe chat service
|
||||||
package groupme
|
package groupme
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"bytes"
|
||||||
|
"context"
|
||||||
|
"encoding/json"
|
||||||
"errors"
|
"errors"
|
||||||
"fmt"
|
"fmt"
|
||||||
"net/http"
|
"net/http"
|
||||||
"net/url"
|
|
||||||
"strconv"
|
"strconv"
|
||||||
)
|
)
|
||||||
|
|
||||||
// GroupMe documentation: https://dev.groupme.com/docs/v3#groups
|
// GroupMe documentation: https://dev.groupme.com/docs/v3#groups
|
||||||
|
|
||||||
////////// Endpoints //////////
|
/*//////// Endpoints ////////*/
|
||||||
const (
|
const (
|
||||||
// Used to build other endpoints
|
// Used to build other endpoints
|
||||||
groupsEndpointRoot = "/groups"
|
groupsEndpointRoot = "/groups"
|
||||||
@ -28,7 +31,7 @@ const (
|
|||||||
changeGroupOwnerEndpoint = groupsEndpointRoot + "/change_owners" // POST
|
changeGroupOwnerEndpoint = groupsEndpointRoot + "/change_owners" // POST
|
||||||
)
|
)
|
||||||
|
|
||||||
////////// Common Request Parameters //////////
|
/*//////// Common Request Parameters ////////*/
|
||||||
|
|
||||||
// GroupSettings is the settings for a group, used by CreateGroup and UpdateGroup
|
// GroupSettings is the settings for a group, used by CreateGroup and UpdateGroup
|
||||||
type GroupSettings struct {
|
type GroupSettings struct {
|
||||||
@ -50,9 +53,9 @@ func (gss GroupSettings) String() string {
|
|||||||
return marshal(&gss)
|
return marshal(&gss)
|
||||||
}
|
}
|
||||||
|
|
||||||
////////// API Requests //////////
|
/*//////// API Requests ////////*/
|
||||||
|
|
||||||
///// Index /////
|
/*/// Index ///*/
|
||||||
|
|
||||||
// GroupsQuery defines optional URL parameters for IndexGroups
|
// GroupsQuery defines optional URL parameters for IndexGroups
|
||||||
type GroupsQuery struct {
|
type GroupsQuery struct {
|
||||||
@ -83,7 +86,7 @@ app for users who are participating in huge groups.
|
|||||||
|
|
||||||
Parameters: See GroupsQuery
|
Parameters: See GroupsQuery
|
||||||
*/
|
*/
|
||||||
func (c *Client) IndexGroups(req *GroupsQuery) ([]*Group, error) {
|
func (c *Client) IndexGroups(ctx context.Context, req *GroupsQuery, authToken string) ([]*Group, error) {
|
||||||
httpReq, err := http.NewRequest("GET", c.endpointBase+indexGroupsEndpoint, nil)
|
httpReq, err := http.NewRequest("GET", c.endpointBase+indexGroupsEndpoint, nil)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
@ -105,7 +108,7 @@ func (c *Client) IndexGroups(req *GroupsQuery) ([]*Group, error) {
|
|||||||
URL.RawQuery = query.Encode()
|
URL.RawQuery = query.Encode()
|
||||||
|
|
||||||
var resp []*Group
|
var resp []*Group
|
||||||
err = c.doWithAuthToken(httpReq, &resp)
|
err = c.doWithAuthToken(ctx, httpReq, &resp, authToken)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
@ -113,21 +116,21 @@ func (c *Client) IndexGroups(req *GroupsQuery) ([]*Group, error) {
|
|||||||
return resp, nil
|
return resp, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
///// Former /////
|
/*/// Former ///*/
|
||||||
|
|
||||||
/*
|
/*
|
||||||
FormerGroups -
|
FormerGroups -
|
||||||
|
|
||||||
List they groups you have left but can rejoin.
|
List they groups you have left but can rejoin.
|
||||||
*/
|
*/
|
||||||
func (c *Client) FormerGroups() ([]*Group, error) {
|
func (c *Client) FormerGroups(ctx context.Context, authToken string) ([]*Group, error) {
|
||||||
httpReq, err := http.NewRequest("GET", c.endpointBase+formerGroupsEndpoint, nil)
|
httpReq, err := http.NewRequest("GET", c.endpointBase+formerGroupsEndpoint, nil)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
var resp []*Group
|
var resp []*Group
|
||||||
err = c.doWithAuthToken(httpReq, &resp)
|
err = c.doWithAuthToken(ctx, httpReq, &resp, authToken)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
@ -135,7 +138,7 @@ func (c *Client) FormerGroups() ([]*Group, error) {
|
|||||||
return resp, nil
|
return resp, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
///// Show /////
|
/*/// Show ///*/
|
||||||
|
|
||||||
/*
|
/*
|
||||||
ShowGroup -
|
ShowGroup -
|
||||||
@ -143,9 +146,10 @@ ShowGroup -
|
|||||||
Loads a specific group.
|
Loads a specific group.
|
||||||
|
|
||||||
Parameters:
|
Parameters:
|
||||||
|
|
||||||
groupID - required, ID(string)
|
groupID - required, ID(string)
|
||||||
*/
|
*/
|
||||||
func (c *Client) ShowGroup(groupID ID) (*Group, error) {
|
func (c *Client) ShowGroup(ctx context.Context, groupID ID, authToken string) (*Group, error) {
|
||||||
URL := fmt.Sprintf(c.endpointBase+showGroupEndpoint, groupID)
|
URL := fmt.Sprintf(c.endpointBase+showGroupEndpoint, groupID)
|
||||||
|
|
||||||
httpReq, err := http.NewRequest("GET", URL, nil)
|
httpReq, err := http.NewRequest("GET", URL, nil)
|
||||||
@ -154,7 +158,7 @@ func (c *Client) ShowGroup(groupID ID) (*Group, error) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
var resp Group
|
var resp Group
|
||||||
err = c.doWithAuthToken(httpReq, &resp)
|
err = c.doWithAuthToken(ctx, httpReq, &resp, authToken)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
@ -162,40 +166,30 @@ func (c *Client) ShowGroup(groupID ID) (*Group, error) {
|
|||||||
return &resp, nil
|
return &resp, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
///// Create /////
|
/*/// Create ///*/
|
||||||
|
|
||||||
/*
|
/*
|
||||||
CreateGroup -
|
CreateGroup -
|
||||||
|
|
||||||
Create a new group
|
# Create a new group
|
||||||
|
|
||||||
Parameters: See GroupSettings
|
Parameters: See GroupSettings
|
||||||
*/
|
*/
|
||||||
func (c *Client) CreateGroup(gs GroupSettings) (*Group, error) {
|
func (c *Client) CreateGroup(ctx context.Context, gs GroupSettings, authToken string) (*Group, error) {
|
||||||
httpReq, err := http.NewRequest("POST", c.endpointBase+createGroupEndpoint, nil)
|
URL := fmt.Sprintf(c.endpointBase + createGroupEndpoint)
|
||||||
|
|
||||||
|
jsonBytes, err := json.Marshal(&gs)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
data := url.Values{}
|
httpReq, err := http.NewRequest("POST", URL, bytes.NewBuffer(jsonBytes))
|
||||||
if gs.Name == "" {
|
if err != nil {
|
||||||
return nil, fmt.Errorf("GroupsCreateRequest Name field is required")
|
return nil, err
|
||||||
}
|
}
|
||||||
data.Set("name", gs.Name)
|
|
||||||
|
|
||||||
if gs.Description != "" {
|
|
||||||
data.Set("description", gs.Description)
|
|
||||||
}
|
|
||||||
if gs.ImageURL != "" {
|
|
||||||
data.Set("image_url", gs.ImageURL)
|
|
||||||
}
|
|
||||||
if gs.Share {
|
|
||||||
data.Set("share", "true")
|
|
||||||
}
|
|
||||||
httpReq.PostForm = data
|
|
||||||
|
|
||||||
var resp Group
|
var resp Group
|
||||||
err = c.doWithAuthToken(httpReq, &resp)
|
err = c.doWithAuthToken(ctx, httpReq, &resp, authToken)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
@ -203,45 +197,33 @@ func (c *Client) CreateGroup(gs GroupSettings) (*Group, error) {
|
|||||||
return &resp, nil
|
return &resp, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
///// Update /////
|
/*/// Update ///*/
|
||||||
|
|
||||||
/*
|
/*
|
||||||
UpdateGroup -
|
UpdateGroup -
|
||||||
|
|
||||||
Update a group after creation
|
# Update a group after creation
|
||||||
|
|
||||||
Parameters:
|
Parameters:
|
||||||
|
|
||||||
groupID - required, ID(string)
|
groupID - required, ID(string)
|
||||||
See GroupSettings
|
See GroupSettings
|
||||||
*/
|
*/
|
||||||
func (c *Client) UpdateGroup(groupID ID, gs GroupSettings) (*Group, error) {
|
func (c *Client) UpdateGroup(ctx context.Context, groupID ID, gs GroupSettings, authToken string) (*Group, error) {
|
||||||
URL := fmt.Sprintf(c.endpointBase+updateGroupEndpoint, groupID)
|
URL := fmt.Sprintf(c.endpointBase+updateGroupEndpoint, groupID)
|
||||||
|
|
||||||
httpReq, err := http.NewRequest("POST", URL, nil)
|
jsonBytes, err := json.Marshal(&gs)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
data := url.Values{}
|
httpReq, err := http.NewRequest("POST", URL, bytes.NewBuffer(jsonBytes))
|
||||||
if gs.Name != "" {
|
if err != nil {
|
||||||
data.Set("name", gs.Name)
|
return nil, err
|
||||||
}
|
}
|
||||||
if gs.Description != "" {
|
|
||||||
data.Set("description", gs.Description)
|
|
||||||
}
|
|
||||||
if gs.ImageURL != "" {
|
|
||||||
data.Set("image_url", gs.ImageURL)
|
|
||||||
}
|
|
||||||
if gs.OfficeMode {
|
|
||||||
data.Set("office_mode", "true")
|
|
||||||
}
|
|
||||||
if gs.Share {
|
|
||||||
data.Set("share", "true")
|
|
||||||
}
|
|
||||||
httpReq.PostForm = data
|
|
||||||
|
|
||||||
var resp Group
|
var resp Group
|
||||||
err = c.doWithAuthToken(httpReq, &resp)
|
err = c.doWithAuthToken(ctx, httpReq, &resp, authToken)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
@ -249,19 +231,20 @@ func (c *Client) UpdateGroup(groupID ID, gs GroupSettings) (*Group, error) {
|
|||||||
return &resp, nil
|
return &resp, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
///// Destroy /////
|
/*/// Destroy ///*/
|
||||||
|
|
||||||
/*
|
/*
|
||||||
DestroyGroup -
|
DestroyGroup -
|
||||||
|
|
||||||
Disband a group
|
# Disband a group
|
||||||
|
|
||||||
This action is only available to the group creator
|
# This action is only available to the group creator
|
||||||
|
|
||||||
Parameters:
|
Parameters:
|
||||||
|
|
||||||
groupID - required, ID(string)
|
groupID - required, ID(string)
|
||||||
*/
|
*/
|
||||||
func (c *Client) DestroyGroup(groupID ID) error {
|
func (c *Client) DestroyGroup(ctx context.Context, groupID ID, authToken string) error {
|
||||||
url := fmt.Sprintf(c.endpointBase+destroyGroupEndpoint, groupID)
|
url := fmt.Sprintf(c.endpointBase+destroyGroupEndpoint, groupID)
|
||||||
|
|
||||||
httpReq, err := http.NewRequest("POST", url, nil)
|
httpReq, err := http.NewRequest("POST", url, nil)
|
||||||
@ -269,21 +252,22 @@ func (c *Client) DestroyGroup(groupID ID) error {
|
|||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
return c.doWithAuthToken(httpReq, nil)
|
return c.doWithAuthToken(ctx, httpReq, nil, authToken)
|
||||||
}
|
}
|
||||||
|
|
||||||
///// Join /////
|
/*/// Join ///*/
|
||||||
|
|
||||||
/*
|
/*
|
||||||
JoinGroup -
|
JoinGroup -
|
||||||
|
|
||||||
Join a shared group
|
# Join a shared group
|
||||||
|
|
||||||
Parameters:
|
Parameters:
|
||||||
|
|
||||||
groupID - required, ID(string)
|
groupID - required, ID(string)
|
||||||
shareToken - required, string
|
shareToken - required, string
|
||||||
*/
|
*/
|
||||||
func (c *Client) JoinGroup(groupID ID, shareToken string) (*Group, error) {
|
func (c *Client) JoinGroup(ctx context.Context, groupID ID, shareToken string, authToken string) (*Group, error) {
|
||||||
URL := fmt.Sprintf(c.endpointBase+joinGroupEndpoint, groupID, shareToken)
|
URL := fmt.Sprintf(c.endpointBase+joinGroupEndpoint, groupID, shareToken)
|
||||||
|
|
||||||
httpReq, err := http.NewRequest("POST", URL, nil)
|
httpReq, err := http.NewRequest("POST", URL, nil)
|
||||||
@ -292,7 +276,7 @@ func (c *Client) JoinGroup(groupID ID, shareToken string) (*Group, error) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
var resp Group
|
var resp Group
|
||||||
err = c.doWithAuthToken(httpReq, &resp)
|
err = c.doWithAuthToken(ctx, httpReq, &resp, authToken)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
@ -300,7 +284,7 @@ func (c *Client) JoinGroup(groupID ID, shareToken string) (*Group, error) {
|
|||||||
return &resp, nil
|
return &resp, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
///// Rejoin /////
|
/*/// Rejoin ///*/
|
||||||
|
|
||||||
/*
|
/*
|
||||||
RejoinGroup -
|
RejoinGroup -
|
||||||
@ -308,20 +292,30 @@ RejoinGroup -
|
|||||||
Rejoin a group. Only works if you previously removed yourself.
|
Rejoin a group. Only works if you previously removed yourself.
|
||||||
|
|
||||||
Parameters:
|
Parameters:
|
||||||
|
|
||||||
groupID - required, ID(string)
|
groupID - required, ID(string)
|
||||||
*/
|
*/
|
||||||
func (c *Client) RejoinGroup(groupID ID) (*Group, error) {
|
func (c *Client) RejoinGroup(ctx context.Context, groupID ID, authToken string) (*Group, error) {
|
||||||
httpReq, err := http.NewRequest("POST", c.endpointBase+rejoinGroupEndpoint, nil)
|
URL := fmt.Sprintf(c.endpointBase + rejoinGroupEndpoint)
|
||||||
|
|
||||||
|
var data = struct {
|
||||||
|
GroupID ID `json:"group_id"`
|
||||||
|
}{
|
||||||
|
groupID,
|
||||||
|
}
|
||||||
|
|
||||||
|
jsonBytes, err := json.Marshal(&data)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
data := url.Values{}
|
httpReq, err := http.NewRequest("POST", URL, bytes.NewBuffer(jsonBytes))
|
||||||
data.Set("group_id", string(groupID))
|
if err != nil {
|
||||||
httpReq.PostForm = data
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
var resp Group
|
var resp Group
|
||||||
err = c.doWithAuthToken(httpReq, &resp)
|
err = c.doWithAuthToken(ctx, httpReq, &resp, authToken)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
@ -329,12 +323,10 @@ func (c *Client) RejoinGroup(groupID ID) (*Group, error) {
|
|||||||
return &resp, nil
|
return &resp, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
///// Change Owner /////
|
/*/// Change Owner ///*/
|
||||||
|
|
||||||
/*
|
/*
|
||||||
ChangeGroupOwner-
|
ChangeGroupOwner - Change owner of requested groups.
|
||||||
|
|
||||||
Change owner of requested groups.
|
|
||||||
|
|
||||||
This action is only available to the group creator.
|
This action is only available to the group creator.
|
||||||
|
|
||||||
@ -343,21 +335,30 @@ the result of change owner action for the request
|
|||||||
|
|
||||||
Parameters: See ChangeOwnerRequest
|
Parameters: See ChangeOwnerRequest
|
||||||
*/
|
*/
|
||||||
func (c *Client) ChangeGroupOwner(reqs ChangeOwnerRequest) (ChangeOwnerResult, error) {
|
func (c *Client) ChangeGroupOwner(ctx context.Context, reqs ChangeOwnerRequest, authToken string) (ChangeOwnerResult, error) {
|
||||||
httpReq, err := http.NewRequest("POST", c.endpointBase+changeGroupOwnerEndpoint, nil)
|
URL := fmt.Sprintf(c.endpointBase + changeGroupOwnerEndpoint)
|
||||||
|
|
||||||
|
var data = struct {
|
||||||
|
Requests []ChangeOwnerRequest `json:"requests"`
|
||||||
|
}{
|
||||||
|
[]ChangeOwnerRequest{reqs},
|
||||||
|
}
|
||||||
|
|
||||||
|
jsonBytes, err := json.Marshal(&data)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return ChangeOwnerResult{}, err
|
return ChangeOwnerResult{}, err
|
||||||
}
|
}
|
||||||
|
|
||||||
data := url.Values{}
|
httpReq, err := http.NewRequest("POST", URL, bytes.NewBuffer(jsonBytes))
|
||||||
data.Set("requests", marshal([]ChangeOwnerRequest{reqs}))
|
if err != nil {
|
||||||
httpReq.PostForm = data
|
return ChangeOwnerResult{}, err
|
||||||
|
}
|
||||||
|
|
||||||
var resp struct {
|
var resp struct {
|
||||||
Results []ChangeOwnerResult `json:"results"`
|
Results []ChangeOwnerResult `json:"results"`
|
||||||
}
|
}
|
||||||
|
|
||||||
err = c.doWithAuthToken(httpReq, &resp)
|
err = c.doWithAuthToken(ctx, httpReq, &resp, authToken)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return ChangeOwnerResult{}, err
|
return ChangeOwnerResult{}, err
|
||||||
}
|
}
|
||||||
@ -369,24 +370,25 @@ func (c *Client) ChangeGroupOwner(reqs ChangeOwnerRequest) (ChangeOwnerResult, e
|
|||||||
return resp.Results[0], nil
|
return resp.Results[0], nil
|
||||||
}
|
}
|
||||||
|
|
||||||
type ChangeOwnerStatusCode string
|
type changeOwnerStatusCode string
|
||||||
|
|
||||||
|
// Change owner Status Codes
|
||||||
const (
|
const (
|
||||||
ChangeOwner_Ok ChangeOwnerStatusCode = "200"
|
ChangeOwnerOk changeOwnerStatusCode = "200"
|
||||||
ChangeOwner_RequesterNewOwner ChangeOwnerStatusCode = "400"
|
ChangeOwnerRequesterNewOwner changeOwnerStatusCode = "400"
|
||||||
ChangeOwner_NotOwner ChangeOwnerStatusCode = "403"
|
ChangeOwnerNotOwner changeOwnerStatusCode = "403"
|
||||||
ChangeOwner_BadGroupOrOwner ChangeOwnerStatusCode = "404"
|
ChangeOwnerBadGroupOrOwner changeOwnerStatusCode = "404"
|
||||||
ChangeOwner_BadRequest ChangeOwnerStatusCode = "405"
|
ChangeOwnerBadRequest changeOwnerStatusCode = "405"
|
||||||
)
|
)
|
||||||
|
|
||||||
// String returns the description of the status code according to GroupMe
|
// String returns the description of the status code according to GroupMe
|
||||||
func (c ChangeOwnerStatusCode) String() string {
|
func (c changeOwnerStatusCode) String() string {
|
||||||
return map[ChangeOwnerStatusCode]string{
|
return map[changeOwnerStatusCode]string{
|
||||||
ChangeOwner_Ok: "success",
|
ChangeOwnerOk: "success",
|
||||||
ChangeOwner_RequesterNewOwner: "requester is also a new owner",
|
ChangeOwnerRequesterNewOwner: "requester is also a new owner",
|
||||||
ChangeOwner_NotOwner: "requester is not the owner of the group",
|
ChangeOwnerNotOwner: "requester is not the owner of the group",
|
||||||
ChangeOwner_BadGroupOrOwner: "group or new owner not found or new owner is not memeber of the group",
|
ChangeOwnerBadGroupOrOwner: "group or new owner not found or new owner is not member of the group",
|
||||||
ChangeOwner_BadRequest: "request object is missing required field or any of the required fields is not an ID",
|
ChangeOwnerBadRequest: "request object is missing required field or any of the required fields is not an ID",
|
||||||
}[c]
|
}[c]
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -409,7 +411,7 @@ type ChangeOwnerResult struct {
|
|||||||
// UserId of the new owner of the group who is
|
// UserId of the new owner of the group who is
|
||||||
// an active member of the group
|
// an active member of the group
|
||||||
OwnerID string `json:"owner_id"`
|
OwnerID string `json:"owner_id"`
|
||||||
Status ChangeOwnerStatusCode `json:"status"`
|
Status changeOwnerStatusCode `json:"status"`
|
||||||
}
|
}
|
||||||
|
|
||||||
func (r ChangeOwnerResult) String() string {
|
func (r ChangeOwnerResult) String() string {
|
||||||
|
@ -1,6 +1,8 @@
|
|||||||
|
// Package groupme defines a client capable of executing API commands for the GroupMe chat service
|
||||||
package groupme
|
package groupme
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"context"
|
||||||
"fmt"
|
"fmt"
|
||||||
"net/http"
|
"net/http"
|
||||||
"testing"
|
"testing"
|
||||||
@ -17,11 +19,14 @@ func (s *GroupsAPISuite) SetupSuite() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (s *GroupsAPISuite) TestGroupsIndex() {
|
func (s *GroupsAPISuite) TestGroupsIndex() {
|
||||||
groups, err := s.client.IndexGroups(&GroupsQuery{
|
groups, err := s.client.IndexGroups(
|
||||||
|
context.Background(),
|
||||||
|
&GroupsQuery{
|
||||||
Page: 5,
|
Page: 5,
|
||||||
PerPage: 20,
|
PerPage: 20,
|
||||||
Omit: "memberships",
|
Omit: "memberships",
|
||||||
})
|
},
|
||||||
|
)
|
||||||
s.Require().NoError(err)
|
s.Require().NoError(err)
|
||||||
s.Require().NotZero(groups)
|
s.Require().NotZero(groups)
|
||||||
for _, group := range groups {
|
for _, group := range groups {
|
||||||
@ -30,7 +35,7 @@ func (s *GroupsAPISuite) TestGroupsIndex() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (s *GroupsAPISuite) TestGroupsFormer() {
|
func (s *GroupsAPISuite) TestGroupsFormer() {
|
||||||
groups, err := s.client.FormerGroups()
|
groups, err := s.client.FormerGroups(context.Background())
|
||||||
s.Require().NoError(err)
|
s.Require().NoError(err)
|
||||||
s.Require().NotZero(groups)
|
s.Require().NotZero(groups)
|
||||||
for _, group := range groups {
|
for _, group := range groups {
|
||||||
@ -39,33 +44,28 @@ func (s *GroupsAPISuite) TestGroupsFormer() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (s *GroupsAPISuite) TestGroupsShow() {
|
func (s *GroupsAPISuite) TestGroupsShow() {
|
||||||
group, err := s.client.ShowGroup("1")
|
group, err := s.client.ShowGroup(context.Background(), "1")
|
||||||
s.Require().NoError(err)
|
s.Require().NoError(err)
|
||||||
s.Assert().NotZero(group)
|
s.Assert().NotZero(group)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *GroupsAPISuite) TestGroupsCreate() {
|
func (s *GroupsAPISuite) TestGroupsCreate() {
|
||||||
group, err := s.client.CreateGroup(GroupSettings{
|
group, err := s.client.CreateGroup(
|
||||||
|
context.Background(),
|
||||||
|
GroupSettings{
|
||||||
"Test",
|
"Test",
|
||||||
"This is a test group",
|
"This is a test group",
|
||||||
"www.blank.com/image",
|
"www.blank.com/image",
|
||||||
false,
|
false,
|
||||||
true,
|
true,
|
||||||
})
|
},
|
||||||
|
)
|
||||||
s.Require().NoError(err)
|
s.Require().NoError(err)
|
||||||
s.Assert().NotZero(group)
|
s.Assert().NotZero(group)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *GroupsAPISuite) TestGroupsCreate_EmptyName() {
|
|
||||||
group, err := s.client.CreateGroup(GroupSettings{
|
|
||||||
Name: "",
|
|
||||||
})
|
|
||||||
s.Require().Error(err)
|
|
||||||
s.Assert().Zero(group)
|
|
||||||
}
|
|
||||||
|
|
||||||
func (s *GroupsAPISuite) TestGroupsUpdate() {
|
func (s *GroupsAPISuite) TestGroupsUpdate() {
|
||||||
group, err := s.client.UpdateGroup("1", GroupSettings{
|
group, err := s.client.UpdateGroup(context.Background(), "1", GroupSettings{
|
||||||
"Test",
|
"Test",
|
||||||
"This is a test group",
|
"This is a test group",
|
||||||
"www.blank.com/image",
|
"www.blank.com/image",
|
||||||
@ -77,24 +77,25 @@ func (s *GroupsAPISuite) TestGroupsUpdate() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (s *GroupsAPISuite) TestGroupsDestroy() {
|
func (s *GroupsAPISuite) TestGroupsDestroy() {
|
||||||
err := s.client.DestroyGroup("1")
|
err := s.client.DestroyGroup(context.Background(), "1")
|
||||||
s.Require().NoError(err)
|
s.Require().NoError(err)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *GroupsAPISuite) TestGroupsJoin() {
|
func (s *GroupsAPISuite) TestGroupsJoin() {
|
||||||
group, err := s.client.JoinGroup("1", "please")
|
group, err := s.client.JoinGroup(context.Background(), "1", "please")
|
||||||
s.Require().NoError(err)
|
s.Require().NoError(err)
|
||||||
s.Assert().NotZero(group)
|
s.Assert().NotZero(group)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *GroupsAPISuite) TestGroupsRejoin() {
|
func (s *GroupsAPISuite) TestGroupsRejoin() {
|
||||||
group, err := s.client.RejoinGroup("1")
|
group, err := s.client.RejoinGroup(context.Background(), "1")
|
||||||
s.Require().NoError(err)
|
s.Require().NoError(err)
|
||||||
s.Assert().NotZero(group)
|
s.Assert().NotZero(group)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *GroupsAPISuite) TestGroupsChangeOwner() {
|
func (s *GroupsAPISuite) TestGroupsChangeOwner() {
|
||||||
result, err := s.client.ChangeGroupOwner(
|
result, err := s.client.ChangeGroupOwner(
|
||||||
|
context.Background(),
|
||||||
ChangeOwnerRequest{
|
ChangeOwnerRequest{
|
||||||
"1",
|
"1",
|
||||||
"123",
|
"123",
|
||||||
@ -107,8 +108,9 @@ func TestGroupsAPISuite(t *testing.T) {
|
|||||||
suite.Run(t, new(GroupsAPISuite))
|
suite.Run(t, new(GroupsAPISuite))
|
||||||
}
|
}
|
||||||
|
|
||||||
////////// Test Groups Router //////////
|
/*//////// Test Groups Router ////////*/
|
||||||
|
|
||||||
|
// nolint // not duplicate code
|
||||||
func groupsTestRouter() *mux.Router {
|
func groupsTestRouter() *mux.Router {
|
||||||
router := mux.NewRouter().Queries("token", "").Subrouter()
|
router := mux.NewRouter().Queries("token", "").Subrouter()
|
||||||
|
|
||||||
|
216
json.go
216
json.go
@ -1,3 +1,4 @@
|
|||||||
|
// Package groupme defines a client capable of executing API commands for the GroupMe chat service
|
||||||
package groupme
|
package groupme
|
||||||
|
|
||||||
import (
|
import (
|
||||||
@ -8,8 +9,8 @@ import (
|
|||||||
// Meta is the error type returned in the GroupMe response.
|
// Meta is the error type returned in the GroupMe response.
|
||||||
// Meant for clients that can't read HTTP status codes
|
// Meant for clients that can't read HTTP status codes
|
||||||
type Meta struct {
|
type Meta struct {
|
||||||
Code HTTPStatusCode `json:"code"`
|
Code HTTPStatusCode `json:"code,omitempty"`
|
||||||
Errors []string `json:"errors"`
|
Errors []string `json:"errors,omitempty"`
|
||||||
}
|
}
|
||||||
|
|
||||||
// Error returns the code and the error list as a string.
|
// Error returns the code and the error list as a string.
|
||||||
@ -20,40 +21,40 @@ func (m Meta) Error() string {
|
|||||||
|
|
||||||
// Group is a GroupMe group, returned in JSON API responses
|
// Group is a GroupMe group, returned in JSON API responses
|
||||||
type Group struct {
|
type Group struct {
|
||||||
ID ID `json:"id"`
|
ID ID `json:"id,omitempty"`
|
||||||
Name string `json:"name"`
|
Name string `json:"name,omitempty"`
|
||||||
// Type of group (private|public)
|
// Type of group (private|public)
|
||||||
Type string `json:"type"`
|
Type string `json:"type,omitempty"`
|
||||||
Description string `json:"description"`
|
Description string `json:"description,omitempty"`
|
||||||
ImageURL string `json:"image_url"`
|
ImageURL string `json:"image_url,omitempty"`
|
||||||
CreatorUserID ID `json:"creator_user_id"`
|
CreatorUserID ID `json:"creator_user_id,omitempty"`
|
||||||
CreatedAt Timestamp `json:"created_at"`
|
CreatedAt Timestamp `json:"created_at,omitempty"`
|
||||||
UpdatedAt Timestamp `json:"updated_at"`
|
UpdatedAt Timestamp `json:"updated_at,omitempty"`
|
||||||
Members []*Member `json:"members"`
|
Members []*Member `json:"members,omitempty"`
|
||||||
ShareURL string `json:"share_url"`
|
ShareURL string `json:"share_url,omitempty"`
|
||||||
Messages GroupMessages `json:"messages"`
|
Messages GroupMessages `json:"messages,omitempty"`
|
||||||
}
|
}
|
||||||
|
|
||||||
// GroupMessages is a Group field, only returned in Group JSON API responses
|
// GroupMessages is a Group field, only returned in Group JSON API responses
|
||||||
type GroupMessages struct {
|
type GroupMessages struct {
|
||||||
Count uint `json:"count"`
|
Count uint `json:"count,omitempty"`
|
||||||
LastMessageID ID `json:"last_message_id"`
|
LastMessageID ID `json:"last_message_id,omitempty"`
|
||||||
LastMessageCreatedAt Timestamp `json:"last_message_created_at"`
|
LastMessageCreatedAt Timestamp `json:"last_message_created_at,omitempty"`
|
||||||
Preview MessagePreview `json:"preview"`
|
Preview MessagePreview `json:"preview,omitempty"`
|
||||||
}
|
}
|
||||||
|
|
||||||
// MessagePreview is a GroupMessages field, only returned in Group JSON API responses.
|
// MessagePreview is a GroupMessages field, only returned in Group JSON API responses.
|
||||||
// Abbreviated form of Message type
|
// Abbreviated form of Message type
|
||||||
type MessagePreview struct {
|
type MessagePreview struct {
|
||||||
Nickname string `json:"nickname"`
|
Nickname string `json:"nickname,omitempty"`
|
||||||
Text string `json:"text"`
|
Text string `json:"text,omitempty"`
|
||||||
ImageURL string `json:"image_url"`
|
ImageURL string `json:"image_url,omitempty"`
|
||||||
Attachments []*Attachment `json:"attachments"`
|
Attachments []*Attachment `json:"attachments,omitempty"`
|
||||||
}
|
}
|
||||||
|
|
||||||
// GetMemberByUserID gets the group member by their UserID,
|
// GetMemberByUserID gets the group member by their UserID,
|
||||||
// nil if no member matches
|
// nil if no member matches
|
||||||
func (g Group) GetMemberByUserID(userID ID) *Member {
|
func (g *Group) GetMemberByUserID(userID ID) *Member {
|
||||||
for _, member := range g.Members {
|
for _, member := range g.Members {
|
||||||
if member.UserID == userID {
|
if member.UserID == userID {
|
||||||
return member
|
return member
|
||||||
@ -65,7 +66,7 @@ func (g Group) GetMemberByUserID(userID ID) *Member {
|
|||||||
|
|
||||||
// GetMemberByNickname gets the group member by their Nickname,
|
// GetMemberByNickname gets the group member by their Nickname,
|
||||||
// nil if no member matches
|
// nil if no member matches
|
||||||
func (g Group) GetMemberByNickname(nickname string) *Member {
|
func (g *Group) GetMemberByNickname(nickname string) *Member {
|
||||||
for _, member := range g.Members {
|
for _, member := range g.Members {
|
||||||
if member.Nickname == nickname {
|
if member.Nickname == nickname {
|
||||||
return member
|
return member
|
||||||
@ -75,137 +76,146 @@ func (g Group) GetMemberByNickname(nickname string) *Member {
|
|||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (g Group) String() string {
|
func (g *Group) String() string {
|
||||||
return marshal(&g)
|
return marshal(g)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Member is a GroupMe group member, returned in JSON API responses
|
// Member is a GroupMe group member, returned in JSON API responses
|
||||||
type Member struct {
|
type Member struct {
|
||||||
ID ID `json:"id"`
|
ID ID `json:"id,omitempty"`
|
||||||
UserID ID `json:"user_id"`
|
UserID ID `json:"user_id,omitempty"`
|
||||||
Nickname string `json:"nickname"`
|
Nickname string `json:"nickname,omitempty"`
|
||||||
Muted bool `json:"muted"`
|
Muted bool `json:"muted,omitempty"`
|
||||||
ImageURL string `json:"image_url"`
|
ImageURL string `json:"image_url,omitempty"`
|
||||||
AutoKicked bool `json:"autokicked"`
|
AutoKicked bool `json:"autokicked,omitempty"`
|
||||||
AppInstalled bool `json:"app_installed"`
|
AppInstalled bool `json:"app_installed,omitempty"`
|
||||||
GUID string `json:"guid"`
|
GUID string `json:"guid,omitempty"`
|
||||||
|
PhoneNumber string `json:"phone_number,omitempty"` // Only used when searching for the member to add to a group.
|
||||||
|
Email string `json:"email,omitempty"` // Only used when searching for the member to add to a group.
|
||||||
}
|
}
|
||||||
|
|
||||||
func (m Member) String() string {
|
func (m *Member) String() string {
|
||||||
return marshal(&m)
|
return marshal(m)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Message is a GroupMe group message, returned in JSON API responses
|
// Message is a GroupMe group message, returned in JSON API responses
|
||||||
type Message struct {
|
type Message struct {
|
||||||
ID ID `json:"id"`
|
ID ID `json:"id,omitempty"`
|
||||||
SourceGUID string `json:"source_guid"`
|
SourceGUID string `json:"source_guid,omitempty"`
|
||||||
CreatedAt Timestamp `json:"created_at"`
|
CreatedAt Timestamp `json:"created_at,omitempty"`
|
||||||
GroupID ID `json:"group_id"`
|
GroupID ID `json:"group_id,omitempty"`
|
||||||
UserID ID `json:"user_id"`
|
UserID ID `json:"user_id,omitempty"`
|
||||||
BotID ID `json:"bot_id"`
|
BotID ID `json:"bot_id,omitempty"`
|
||||||
SenderID ID `json:"sender_id"`
|
SenderID ID `json:"sender_id,omitempty"`
|
||||||
SenderType SenderType `json:"sender_type"`
|
SenderType senderType `json:"sender_type,omitempty"`
|
||||||
System bool `json:"system"`
|
System bool `json:"system,omitempty"`
|
||||||
Name string `json:"name"`
|
Name string `json:"name,omitempty"`
|
||||||
RecipientID ID `json:"recipient_id"`
|
RecipientID ID `json:"recipient_id,omitempty"`
|
||||||
ConversationID ID `json:"conversation_id"`
|
//ChatID - over push ConversationID seems to be called ChatID
|
||||||
AvatarURL string `json:"avatar_url"`
|
ChatID ID `json:"chat_id,omitempty"`
|
||||||
|
ConversationID ID `json:"conversation_id,omitempty"`
|
||||||
|
AvatarURL string `json:"avatar_url,omitempty"`
|
||||||
// Maximum length of 1000 characters
|
// Maximum length of 1000 characters
|
||||||
Text string `json:"text"`
|
Text string `json:"text,omitempty"`
|
||||||
// Must be an image service URL (i.groupme.com)
|
// Must be an image service URL (i.groupme.com)
|
||||||
ImageURL string `json:"image_url"`
|
ImageURL string `json:"image_url,omitempty"`
|
||||||
FavoritedBy []string `json:"favorited_by"`
|
FavoritedBy []string `json:"favorited_by,omitempty"`
|
||||||
Attachments []*Attachment `json:"attachments"`
|
Attachments []*Attachment `json:"attachments,omitempty"`
|
||||||
}
|
}
|
||||||
|
|
||||||
func (m Message) String() string {
|
func (m *Message) String() string {
|
||||||
return marshal(&m)
|
return marshal(m)
|
||||||
}
|
}
|
||||||
|
|
||||||
type SenderType string
|
type senderType string
|
||||||
|
|
||||||
// SenderType constants
|
// SenderType constants
|
||||||
const (
|
const (
|
||||||
SenderType_User SenderType = "user"
|
SenderTypeUser senderType = "user"
|
||||||
SenderType_Bot SenderType = "bot"
|
SenderTypeBot senderType = "bot"
|
||||||
SenderType_System SenderType = "system"
|
SenderTypeSystem senderType = "system"
|
||||||
)
|
)
|
||||||
|
|
||||||
type AttachmentType string
|
type attachmentType string
|
||||||
|
|
||||||
// AttachmentType constants
|
// AttachmentType constants
|
||||||
const (
|
const (
|
||||||
Mentions AttachmentType = "mentions"
|
Mentions attachmentType = "mentions"
|
||||||
Image AttachmentType = "image"
|
Image attachmentType = "image"
|
||||||
Location AttachmentType = "location"
|
Location attachmentType = "location"
|
||||||
Emoji AttachmentType = "emoji"
|
Emoji attachmentType = "emoji"
|
||||||
)
|
)
|
||||||
|
|
||||||
// Attachment is a GroupMe message attachment, returned in JSON API responses
|
// Attachment is a GroupMe message attachment, returned in JSON API responses
|
||||||
type Attachment struct {
|
type Attachment struct {
|
||||||
Type AttachmentType `json:"type"`
|
Type attachmentType `json:"type,omitempty"`
|
||||||
Loci [][]int `json:"loci"`
|
Loci [][]int `json:"loci,omitempty"`
|
||||||
UserIDs []ID `json:"user_ids"`
|
UserIDs []ID `json:"user_ids,omitempty"`
|
||||||
URL string `json:"url"`
|
URL string `json:"url,omitempty"`
|
||||||
Name string `json:"name"`
|
FileID string `json:"file_id,omitempty"`
|
||||||
Latitude string `json:"lat"`
|
VideoPreviewURL string `json:"preview_url,omitempty"`
|
||||||
Longitude string `json:"lng"`
|
Name string `json:"name,omitempty"`
|
||||||
Placeholder string `json:"placeholder"`
|
Latitude string `json:"lat,omitempty"`
|
||||||
Charmap [][]int `json:"charmap"`
|
Longitude string `json:"lng,omitempty"`
|
||||||
|
Placeholder string `json:"placeholder,omitempty"`
|
||||||
|
Charmap [][]int `json:"charmap,omitempty"`
|
||||||
|
ReplyID ID `json:"reply_id,omitempty"`
|
||||||
}
|
}
|
||||||
|
|
||||||
func (a Attachment) String() string {
|
func (a *Attachment) String() string {
|
||||||
return marshal(&a)
|
return marshal(a)
|
||||||
}
|
}
|
||||||
|
|
||||||
// User is a GroupMe user, returned in JSON API responses
|
// User is a GroupMe user, returned in JSON API responses
|
||||||
type User struct {
|
type User struct {
|
||||||
ID ID `json:"id"`
|
ID ID `json:"id,omitempty"`
|
||||||
PhoneNumber PhoneNumber `json:"phone_number"`
|
PhoneNumber PhoneNumber `json:"phone_number,omitempty"`
|
||||||
ImageURL string `json:"image_url"`
|
ImageURL string `json:"image_url,omitempty"`
|
||||||
Name string `json:"name"`
|
Name string `json:"name,omitempty"`
|
||||||
CreatedAt Timestamp `json:"created_at"`
|
CreatedAt Timestamp `json:"created_at,omitempty"`
|
||||||
UpdatedAt Timestamp `json:"updated_at"`
|
UpdatedAt Timestamp `json:"updated_at,omitempty"`
|
||||||
AvatarURL string `json:"avatar_url"`
|
AvatarURL string `json:"avatar_url,omitempty"`
|
||||||
Email string `json:"email"`
|
Email string `json:"email,omitempty"`
|
||||||
SMS bool `json:"sms"`
|
SMS bool `json:"sms,omitempty"`
|
||||||
}
|
}
|
||||||
|
|
||||||
func (u User) String() string {
|
func (u *User) String() string {
|
||||||
return marshal(&u)
|
return marshal(u)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Chat is a GroupMe direct message conversation between two users,
|
// Chat is a GroupMe direct message conversation between two users,
|
||||||
// returned in JSON API responses
|
// returned in JSON API responses
|
||||||
type Chat struct {
|
type Chat struct {
|
||||||
CreatedAt Timestamp `json:"created_at"`
|
CreatedAt Timestamp `json:"created_at,omitempty"`
|
||||||
UpdatedAt Timestamp `json:"updated_at"`
|
UpdatedAt Timestamp `json:"updated_at,omitempty"`
|
||||||
LastMessage *Message `json:"last_message"`
|
LastMessage *Message `json:"last_message,omitempty"`
|
||||||
MessagesCount int `json:"messages_count"`
|
MessagesCount int `json:"messages_count,omitempty"`
|
||||||
OtherUser User `json:"other_user"`
|
OtherUser User `json:"other_user,omitempty"`
|
||||||
}
|
}
|
||||||
|
|
||||||
func (c Chat) String() string {
|
func (c *Chat) String() string {
|
||||||
return marshal(&c)
|
return marshal(c)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Bot is a GroupMe bot, it is connected to a specific group which it can send messages to
|
||||||
type Bot struct {
|
type Bot struct {
|
||||||
BotID ID `json:"bot_id"`
|
BotID ID `json:"bot_id,omitempty"`
|
||||||
GroupID ID `json:"group_id"`
|
GroupID ID `json:"group_id,omitempty"`
|
||||||
Name string `json:"name"`
|
Name string `json:"name,omitempty"`
|
||||||
AvatarURL string `json:"avatar_url"`
|
AvatarURL string `json:"avatar_url,omitempty"`
|
||||||
CallbackURL string `json:"callback_url"`
|
CallbackURL string `json:"callback_url,omitempty"`
|
||||||
DMNotification bool `json:"dm_notification"`
|
DMNotification bool `json:"dm_notification,omitempty"`
|
||||||
}
|
}
|
||||||
|
|
||||||
func (b Bot) String() string {
|
func (b *Bot) String() string {
|
||||||
return marshal(&b)
|
return marshal(b)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Block is a GroupMe block between two users, direct messages are not allowed
|
||||||
type Block struct {
|
type Block struct {
|
||||||
UserID ID `json:"user_id"`
|
UserID ID `json:"user_id,omitempty"`
|
||||||
BlockedUserID ID `json:"blocked_user_id"`
|
BlockedUserID ID `json:"blocked_user_id,omitempty"`
|
||||||
CreatedAT Timestamp `json:"created_at"`
|
CreatedAT Timestamp `json:"created_at,omitempty"`
|
||||||
}
|
}
|
||||||
|
|
||||||
func (b Block) String() string {
|
func (b Block) String() string {
|
||||||
|
@ -1,3 +1,4 @@
|
|||||||
|
// Package groupme defines a client capable of executing API commands for the GroupMe chat service
|
||||||
package groupme
|
package groupme
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
@ -1,13 +1,15 @@
|
|||||||
|
// Package groupme defines a client capable of executing API commands for the GroupMe chat service
|
||||||
package groupme
|
package groupme
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"context"
|
||||||
"fmt"
|
"fmt"
|
||||||
"net/http"
|
"net/http"
|
||||||
)
|
)
|
||||||
|
|
||||||
// GroupMe documentation: https://dev.groupme.com/docs/v3#leaderboard
|
// GroupMe documentation: https://dev.groupme.com/docs/v3#leaderboard
|
||||||
|
|
||||||
////////// Endpoints //////////
|
/*//////// Endpoints ////////*/
|
||||||
const (
|
const (
|
||||||
// Used to build other endpoints
|
// Used to build other endpoints
|
||||||
leaderboardEndpointRoot = groupEndpointRoot + "/likes"
|
leaderboardEndpointRoot = groupEndpointRoot + "/likes"
|
||||||
@ -18,34 +20,22 @@ const (
|
|||||||
myHitsLeaderboardEndpoint = leaderboardEndpointRoot + "/for_me" // GET
|
myHitsLeaderboardEndpoint = leaderboardEndpointRoot + "/for_me" // GET
|
||||||
)
|
)
|
||||||
|
|
||||||
////////// API Requests //////////
|
/*//////// API Requests ////////*/
|
||||||
|
|
||||||
// Index
|
// Index
|
||||||
|
|
||||||
type period string
|
type period string
|
||||||
|
|
||||||
func (p period) String() string {
|
|
||||||
return string(p)
|
|
||||||
}
|
|
||||||
|
|
||||||
// Define acceptable period values
|
// Define acceptable period values
|
||||||
const (
|
const (
|
||||||
Period_Day = "day"
|
PeriodDay = "day"
|
||||||
Period_Week = "week"
|
PeriodWeek = "week"
|
||||||
Period_Month = "month"
|
PeriodMonth = "month"
|
||||||
)
|
)
|
||||||
|
|
||||||
/*
|
// IndexLeaderboard - A list of the liked messages in the group for a given period of
|
||||||
IndexLeaderboard -
|
// time. Messages are ranked in order of number of likes.
|
||||||
|
func (c *Client) IndexLeaderboard(ctx context.Context, groupID ID, p period, authToken string) ([]*Message, error) {
|
||||||
A list of the liked messages in the group for a given period of
|
|
||||||
time. Messages are ranked in order of number of likes.
|
|
||||||
|
|
||||||
Parameters:
|
|
||||||
groupID - required, ID(string)
|
|
||||||
p - required, period(string)
|
|
||||||
*/
|
|
||||||
func (c *Client) IndexLeaderboard(groupID ID, p period) ([]*Message, error) {
|
|
||||||
url := fmt.Sprintf(c.endpointBase+indexLeaderboardEndpoint, groupID)
|
url := fmt.Sprintf(c.endpointBase+indexLeaderboardEndpoint, groupID)
|
||||||
httpReq, err := http.NewRequest("GET", url, nil)
|
httpReq, err := http.NewRequest("GET", url, nil)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
@ -54,13 +44,13 @@ func (c *Client) IndexLeaderboard(groupID ID, p period) ([]*Message, error) {
|
|||||||
|
|
||||||
URL := httpReq.URL
|
URL := httpReq.URL
|
||||||
query := URL.Query()
|
query := URL.Query()
|
||||||
query.Set("period", p.String())
|
query.Set("period", string(p))
|
||||||
URL.RawQuery = query.Encode()
|
URL.RawQuery = query.Encode()
|
||||||
|
|
||||||
var resp struct {
|
var resp struct {
|
||||||
Messages []*Message `json:"messages"`
|
Messages []*Message `json:"messages"`
|
||||||
}
|
}
|
||||||
err = c.doWithAuthToken(httpReq, &resp)
|
err = c.doWithAuthToken(ctx, httpReq, &resp, authToken)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
@ -78,9 +68,10 @@ reverse chrono-order. Note that the payload includes a liked_at
|
|||||||
timestamp in ISO-8601 format.
|
timestamp in ISO-8601 format.
|
||||||
|
|
||||||
Parameters:
|
Parameters:
|
||||||
|
|
||||||
groupID - required, ID(string)
|
groupID - required, ID(string)
|
||||||
*/
|
*/
|
||||||
func (c *Client) MyLikesLeaderboard(groupID ID) ([]*Message, error) {
|
func (c *Client) MyLikesLeaderboard(ctx context.Context, groupID ID, authToken string) ([]*Message, error) {
|
||||||
url := fmt.Sprintf(c.endpointBase+myLikesLeaderboardEndpoint, groupID)
|
url := fmt.Sprintf(c.endpointBase+myLikesLeaderboardEndpoint, groupID)
|
||||||
httpReq, err := http.NewRequest("GET", url, nil)
|
httpReq, err := http.NewRequest("GET", url, nil)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
@ -90,7 +81,7 @@ func (c *Client) MyLikesLeaderboard(groupID ID) ([]*Message, error) {
|
|||||||
var resp struct {
|
var resp struct {
|
||||||
Messages []*Message `json:"messages"`
|
Messages []*Message `json:"messages"`
|
||||||
}
|
}
|
||||||
err = c.doWithAuthToken(httpReq, &resp)
|
err = c.doWithAuthToken(ctx, httpReq, &resp, authToken)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
@ -108,9 +99,10 @@ reverse chrono-order. Note that the payload includes a liked_at
|
|||||||
timestamp in ISO-8601 format.
|
timestamp in ISO-8601 format.
|
||||||
|
|
||||||
Parameters:
|
Parameters:
|
||||||
|
|
||||||
groupID - required, ID(string)
|
groupID - required, ID(string)
|
||||||
*/
|
*/
|
||||||
func (c *Client) MyHitsLeaderboard(groupID ID) ([]*Message, error) {
|
func (c *Client) MyHitsLeaderboard(ctx context.Context, groupID ID, authToken string) ([]*Message, error) {
|
||||||
url := fmt.Sprintf(c.endpointBase+myHitsLeaderboardEndpoint, groupID)
|
url := fmt.Sprintf(c.endpointBase+myHitsLeaderboardEndpoint, groupID)
|
||||||
httpReq, err := http.NewRequest("GET", url, nil)
|
httpReq, err := http.NewRequest("GET", url, nil)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
@ -120,7 +112,7 @@ func (c *Client) MyHitsLeaderboard(groupID ID) ([]*Message, error) {
|
|||||||
var resp struct {
|
var resp struct {
|
||||||
Messages []*Message `json:"messages"`
|
Messages []*Message `json:"messages"`
|
||||||
}
|
}
|
||||||
err = c.doWithAuthToken(httpReq, &resp)
|
err = c.doWithAuthToken(ctx, httpReq, &resp, authToken)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
@ -1,6 +1,8 @@
|
|||||||
|
// Package groupme defines a client capable of executing API commands for the GroupMe chat service
|
||||||
package groupme
|
package groupme
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"context"
|
||||||
"fmt"
|
"fmt"
|
||||||
"net/http"
|
"net/http"
|
||||||
"testing"
|
"testing"
|
||||||
@ -17,7 +19,7 @@ func (s *LeaderboardAPISuite) SetupSuite() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (s *LeaderboardAPISuite) TestLeaderboardIndex() {
|
func (s *LeaderboardAPISuite) TestLeaderboardIndex() {
|
||||||
messages, err := s.client.IndexLeaderboard("1", Period_Day)
|
messages, err := s.client.IndexLeaderboard(context.Background(), "1", PeriodDay)
|
||||||
s.Require().NoError(err)
|
s.Require().NoError(err)
|
||||||
s.Require().NotZero(messages)
|
s.Require().NotZero(messages)
|
||||||
for _, message := range messages {
|
for _, message := range messages {
|
||||||
@ -26,7 +28,7 @@ func (s *LeaderboardAPISuite) TestLeaderboardIndex() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (s *LeaderboardAPISuite) TestLeaderboardMyLikes() {
|
func (s *LeaderboardAPISuite) TestLeaderboardMyLikes() {
|
||||||
messages, err := s.client.MyLikesLeaderboard("1")
|
messages, err := s.client.MyLikesLeaderboard(context.Background(), "1")
|
||||||
s.Require().NoError(err)
|
s.Require().NoError(err)
|
||||||
s.Require().NotZero(messages)
|
s.Require().NotZero(messages)
|
||||||
for _, message := range messages {
|
for _, message := range messages {
|
||||||
@ -35,7 +37,7 @@ func (s *LeaderboardAPISuite) TestLeaderboardMyLikes() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (s *LeaderboardAPISuite) TestLeaderboardMyHits() {
|
func (s *LeaderboardAPISuite) TestLeaderboardMyHits() {
|
||||||
messages, err := s.client.MyHitsLeaderboard("1")
|
messages, err := s.client.MyHitsLeaderboard(context.Background(), "1")
|
||||||
s.Require().NoError(err)
|
s.Require().NoError(err)
|
||||||
s.Require().NotZero(messages)
|
s.Require().NotZero(messages)
|
||||||
for _, message := range messages {
|
for _, message := range messages {
|
||||||
@ -47,6 +49,7 @@ func TestLeaderboardAPISuite(t *testing.T) {
|
|||||||
suite.Run(t, new(LeaderboardAPISuite))
|
suite.Run(t, new(LeaderboardAPISuite))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// nolint // not duplicate code
|
||||||
func leaderboardTestRouter() *mux.Router {
|
func leaderboardTestRouter() *mux.Router {
|
||||||
router := mux.NewRouter().Queries("token", "").Subrouter()
|
router := mux.NewRouter().Queries("token", "").Subrouter()
|
||||||
|
|
||||||
|
36
likes_api.go
36
likes_api.go
@ -1,13 +1,15 @@
|
|||||||
|
// Package groupme defines a client capable of executing API commands for the GroupMe chat service
|
||||||
package groupme
|
package groupme
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"context"
|
||||||
"fmt"
|
"fmt"
|
||||||
"net/http"
|
"net/http"
|
||||||
)
|
)
|
||||||
|
|
||||||
// GroupMe documentation: https://dev.groupme.com/docs/v3#likes
|
// GroupMe documentation: https://dev.groupme.com/docs/v3#likes
|
||||||
|
|
||||||
////////// Endpoints //////////
|
/*//////// Endpoints ////////*/
|
||||||
const (
|
const (
|
||||||
// Used to build other endpoints
|
// Used to build other endpoints
|
||||||
likesEndpointRoot = "/messages/%s/%s"
|
likesEndpointRoot = "/messages/%s/%s"
|
||||||
@ -16,20 +18,12 @@ const (
|
|||||||
destroyLikeEndpoint = likesEndpointRoot + "/unlike" // POST
|
destroyLikeEndpoint = likesEndpointRoot + "/unlike" // POST
|
||||||
)
|
)
|
||||||
|
|
||||||
////////// API Requests /////////
|
/*//////// API Requests ////////*/
|
||||||
|
|
||||||
// Create
|
// Create
|
||||||
|
|
||||||
/*
|
// CreateLike - Like a message.
|
||||||
CreateLike -
|
func (c *Client) CreateLike(ctx context.Context, conversationID, messageID ID, authToken string) error {
|
||||||
|
|
||||||
Like a message.
|
|
||||||
|
|
||||||
Parameters:
|
|
||||||
conversationID - required, ID(string)
|
|
||||||
messageID - required, ID(string)
|
|
||||||
*/
|
|
||||||
func (c *Client) CreateLike(conversationID, messageID ID) error {
|
|
||||||
url := fmt.Sprintf(c.endpointBase+createLikeEndpoint, conversationID, messageID)
|
url := fmt.Sprintf(c.endpointBase+createLikeEndpoint, conversationID, messageID)
|
||||||
|
|
||||||
httpReq, err := http.NewRequest("POST", url, nil)
|
httpReq, err := http.NewRequest("POST", url, nil)
|
||||||
@ -37,21 +31,11 @@ func (c *Client) CreateLike(conversationID, messageID ID) error {
|
|||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
return c.doWithAuthToken(httpReq, nil)
|
return c.doWithAuthToken(ctx, httpReq, nil, authToken)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Destroy
|
// DestroyLike - Unlike a message.
|
||||||
|
func (c *Client) DestroyLike(ctx context.Context, conversationID, messageID ID, authToken string) error {
|
||||||
/*
|
|
||||||
DestroyLike -
|
|
||||||
|
|
||||||
Unlike a message.
|
|
||||||
|
|
||||||
Parameters:
|
|
||||||
conversationID - required, ID(string)
|
|
||||||
messageID - required, ID(string)
|
|
||||||
*/
|
|
||||||
func (c *Client) DestroyLike(conversationID, messageID ID) error {
|
|
||||||
url := fmt.Sprintf(c.endpointBase+destroyLikeEndpoint, conversationID, messageID)
|
url := fmt.Sprintf(c.endpointBase+destroyLikeEndpoint, conversationID, messageID)
|
||||||
|
|
||||||
httpReq, err := http.NewRequest("POST", url, nil)
|
httpReq, err := http.NewRequest("POST", url, nil)
|
||||||
@ -59,5 +43,5 @@ func (c *Client) DestroyLike(conversationID, messageID ID) error {
|
|||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
return c.doWithAuthToken(httpReq, nil)
|
return c.doWithAuthToken(ctx, httpReq, nil, authToken)
|
||||||
}
|
}
|
||||||
|
@ -1,6 +1,8 @@
|
|||||||
|
// Package groupme defines a client capable of executing API commands for the GroupMe chat service
|
||||||
package groupme
|
package groupme
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"context"
|
||||||
"net/http"
|
"net/http"
|
||||||
"testing"
|
"testing"
|
||||||
|
|
||||||
@ -16,18 +18,20 @@ func (s *LikesAPISuite) SetupSuite() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (s *LikesAPISuite) TestLikesCreate() {
|
func (s *LikesAPISuite) TestLikesCreate() {
|
||||||
err := s.client.CreateLike("1", "1")
|
err := s.client.CreateLike(context.Background(), "1", "1")
|
||||||
s.Require().NoError(err)
|
s.Require().NoError(err)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *LikesAPISuite) TestLikesDestroy() {
|
func (s *LikesAPISuite) TestLikesDestroy() {
|
||||||
err := s.client.DestroyLike("1", "1")
|
err := s.client.DestroyLike(context.Background(), "1", "1")
|
||||||
s.Require().NoError(err)
|
s.Require().NoError(err)
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestLikesAPISuite(t *testing.T) {
|
func TestLikesAPISuite(t *testing.T) {
|
||||||
suite.Run(t, new(LikesAPISuite))
|
suite.Run(t, new(LikesAPISuite))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// nolint // not duplicate code
|
||||||
func likesTestRouter() *mux.Router {
|
func likesTestRouter() *mux.Router {
|
||||||
router := mux.NewRouter().Queries("token", "").Subrouter()
|
router := mux.NewRouter().Queries("token", "").Subrouter()
|
||||||
|
|
||||||
|
13
main_test.go
13
main_test.go
@ -1,3 +1,4 @@
|
|||||||
|
// Package groupme defines a client capable of executing API commands for the GroupMe chat service
|
||||||
package groupme
|
package groupme
|
||||||
|
|
||||||
import (
|
import (
|
||||||
@ -14,7 +15,7 @@ import (
|
|||||||
"github.com/stretchr/testify/suite"
|
"github.com/stretchr/testify/suite"
|
||||||
)
|
)
|
||||||
|
|
||||||
////////// Base API Suite //////////
|
/*//////// Base API Suite ////////*/
|
||||||
type APISuite struct {
|
type APISuite struct {
|
||||||
// Base attributes
|
// Base attributes
|
||||||
suite.Suite
|
suite.Suite
|
||||||
@ -22,7 +23,7 @@ type APISuite struct {
|
|||||||
server *http.Server
|
server *http.Server
|
||||||
wg sync.WaitGroup
|
wg sync.WaitGroup
|
||||||
|
|
||||||
// Overriden by child Suite
|
// Overridden by child Suite
|
||||||
addr string
|
addr string
|
||||||
handler http.Handler
|
handler http.Handler
|
||||||
}
|
}
|
||||||
@ -42,7 +43,7 @@ func (s *APISuite) TearDownSuite() {
|
|||||||
s.wg.Wait()
|
s.wg.Wait()
|
||||||
}
|
}
|
||||||
|
|
||||||
///// Start Server /////
|
/*/// Start Server ///*/
|
||||||
func (s *APISuite) startServer(addr string, handler http.Handler) *http.Server {
|
func (s *APISuite) startServer(addr string, handler http.Handler) *http.Server {
|
||||||
server := &http.Server{
|
server := &http.Server{
|
||||||
Addr: addr,
|
Addr: addr,
|
||||||
@ -60,6 +61,7 @@ func (s *APISuite) startServer(addr string, handler http.Handler) *http.Server {
|
|||||||
|
|
||||||
// Wait until server has started listening
|
// Wait until server has started listening
|
||||||
url := fmt.Sprintf("http://%s", addr)
|
url := fmt.Sprintf("http://%s", addr)
|
||||||
|
// nolint // url is meant to be variable
|
||||||
for _, err := http.Get(url); err != nil; _, err = http.Get(url) {
|
for _, err := http.Get(url); err != nil; _, err = http.Get(url) {
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
@ -67,7 +69,7 @@ func (s *APISuite) startServer(addr string, handler http.Handler) *http.Server {
|
|||||||
return server
|
return server
|
||||||
}
|
}
|
||||||
|
|
||||||
///// Generate Ephemeral Port /////
|
/*/// Generate Ephemeral Port ///*/
|
||||||
const (
|
const (
|
||||||
portMin = 49152
|
portMin = 49152
|
||||||
portMax = 65535
|
portMax = 65535
|
||||||
@ -76,10 +78,11 @@ const (
|
|||||||
|
|
||||||
func (s *APISuite) generatePort() string {
|
func (s *APISuite) generatePort() string {
|
||||||
rand.Seed(time.Now().UnixNano())
|
rand.Seed(time.Now().UnixNano())
|
||||||
|
// nolint // weak random generator is ok for creating port number in a test
|
||||||
return strconv.Itoa((rand.Intn(portRange) + portMin))
|
return strconv.Itoa((rand.Intn(portRange) + portMin))
|
||||||
}
|
}
|
||||||
|
|
||||||
////////// Test Main //////////
|
/*//////// Test Main ////////*/
|
||||||
func TestMain(m *testing.M) {
|
func TestMain(m *testing.M) {
|
||||||
os.Exit(m.Run())
|
os.Exit(m.Run())
|
||||||
}
|
}
|
||||||
|
@ -1,15 +1,17 @@
|
|||||||
|
// Package groupme defines a client capable of executing API commands for the GroupMe chat service
|
||||||
package groupme
|
package groupme
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"bytes"
|
||||||
|
"context"
|
||||||
"encoding/json"
|
"encoding/json"
|
||||||
"fmt"
|
"fmt"
|
||||||
"net/http"
|
"net/http"
|
||||||
"net/url"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
// GroupMe documentation: https://dev.groupme.com/docs/v3#members
|
// GroupMe documentation: https://dev.groupme.com/docs/v3#members
|
||||||
|
|
||||||
////////// Endpoints //////////
|
/*//////// Endpoints ////////*/
|
||||||
const (
|
const (
|
||||||
// Used to build other endpoints
|
// Used to build other endpoints
|
||||||
membersEndpointRoot = groupEndpointRoot + "/members"
|
membersEndpointRoot = groupEndpointRoot + "/members"
|
||||||
@ -21,7 +23,7 @@ const (
|
|||||||
updateMemberEndpoint = groupEndpointRoot + "/memberships/update" // POST
|
updateMemberEndpoint = groupEndpointRoot + "/memberships/update" // POST
|
||||||
)
|
)
|
||||||
|
|
||||||
///// Add /////
|
/*/// Add ///*/
|
||||||
|
|
||||||
/*
|
/*
|
||||||
AddMembers -
|
AddMembers -
|
||||||
@ -38,6 +40,7 @@ GUIDs can be added to the members parameters. These GUIDs will
|
|||||||
be reflected in the membership JSON objects.
|
be reflected in the membership JSON objects.
|
||||||
|
|
||||||
Parameters:
|
Parameters:
|
||||||
|
|
||||||
groupID - required, ID(string)
|
groupID - required, ID(string)
|
||||||
See Member.
|
See Member.
|
||||||
Nickname - required
|
Nickname - required
|
||||||
@ -46,27 +49,30 @@ Parameters:
|
|||||||
PhoneNumber - PhoneNumber(string)
|
PhoneNumber - PhoneNumber(string)
|
||||||
Email - string
|
Email - string
|
||||||
*/
|
*/
|
||||||
func (c *Client) AddMembers(groupID ID, members ...*Member) (string, error) {
|
func (c *Client) AddMembers(ctx context.Context, groupID ID, authToken string, members ...*Member) (string, error) {
|
||||||
URL := fmt.Sprintf(c.endpointBase+addMembersEndpoint, groupID)
|
URL := fmt.Sprintf(c.endpointBase+addMembersEndpoint, groupID)
|
||||||
|
|
||||||
httpReq, err := http.NewRequest("POST", URL, nil)
|
var data = struct {
|
||||||
|
Members []*Member `json:"members"`
|
||||||
|
}{
|
||||||
|
members,
|
||||||
|
}
|
||||||
|
|
||||||
|
jsonBytes, err := json.Marshal(&data)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return "", err
|
return "", err
|
||||||
}
|
}
|
||||||
|
|
||||||
data := url.Values{}
|
httpReq, err := http.NewRequest("POST", URL, bytes.NewBuffer(jsonBytes))
|
||||||
bytes, err := json.Marshal(members)
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return "", err
|
return "", err
|
||||||
}
|
}
|
||||||
data.Set("members", string(bytes))
|
|
||||||
httpReq.PostForm = data
|
|
||||||
|
|
||||||
var resp struct {
|
var resp struct {
|
||||||
ResultsID string `json:"result_id"`
|
ResultsID string `json:"results_id"`
|
||||||
}
|
}
|
||||||
|
|
||||||
err = c.doWithAuthToken(httpReq, &resp)
|
err = c.doWithAuthToken(ctx, httpReq, &resp, authToken)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return "", err
|
return "", err
|
||||||
}
|
}
|
||||||
@ -74,7 +80,7 @@ func (c *Client) AddMembers(groupID ID, members ...*Member) (string, error) {
|
|||||||
return resp.ResultsID, nil
|
return resp.ResultsID, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
///// Results /////
|
/*/// Results ///*/
|
||||||
|
|
||||||
/*
|
/*
|
||||||
AddMembersResults -
|
AddMembersResults -
|
||||||
@ -89,10 +95,11 @@ Keep in mind that results are temporary -- they will only be
|
|||||||
available for 1 hour after the add request.
|
available for 1 hour after the add request.
|
||||||
|
|
||||||
Parameters:
|
Parameters:
|
||||||
|
|
||||||
groupID - required, ID(string)
|
groupID - required, ID(string)
|
||||||
resultID - required, string
|
resultID - required, string
|
||||||
*/
|
*/
|
||||||
func (c *Client) AddMembersResults(groupID ID, resultID string) ([]*Member, error) {
|
func (c *Client) AddMembersResults(ctx context.Context, groupID ID, resultID string, authToken string) ([]*Member, error) {
|
||||||
URL := fmt.Sprintf(c.endpointBase+addMembersResultsEndpoint, groupID, resultID)
|
URL := fmt.Sprintf(c.endpointBase+addMembersResultsEndpoint, groupID, resultID)
|
||||||
|
|
||||||
httpReq, err := http.NewRequest("GET", URL, nil)
|
httpReq, err := http.NewRequest("GET", URL, nil)
|
||||||
@ -104,7 +111,7 @@ func (c *Client) AddMembersResults(groupID ID, resultID string) ([]*Member, erro
|
|||||||
Members []*Member `json:"members"`
|
Members []*Member `json:"members"`
|
||||||
}
|
}
|
||||||
|
|
||||||
err = c.doWithAuthToken(httpReq, &resp)
|
err = c.doWithAuthToken(ctx, httpReq, &resp, authToken)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
@ -112,7 +119,7 @@ func (c *Client) AddMembersResults(groupID ID, resultID string) ([]*Member, erro
|
|||||||
return resp.Members, nil
|
return resp.Members, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
///// Remove /////
|
/*/// Remove ///*/
|
||||||
|
|
||||||
/*
|
/*
|
||||||
RemoveMember -
|
RemoveMember -
|
||||||
@ -122,10 +129,11 @@ Remove a member (or yourself) from a group.
|
|||||||
Note: The creator of the group cannot be removed or exit.
|
Note: The creator of the group cannot be removed or exit.
|
||||||
|
|
||||||
Parameters:
|
Parameters:
|
||||||
|
|
||||||
groupID - required, ID(string)
|
groupID - required, ID(string)
|
||||||
membershipID - required, ID(string). Not the same as userID
|
membershipID - required, ID(string). Not the same as userID
|
||||||
*/
|
*/
|
||||||
func (c *Client) RemoveMember(groupID, membershipID ID) error {
|
func (c *Client) RemoveMember(ctx context.Context, groupID, membershipID ID, authToken string) error {
|
||||||
URL := fmt.Sprintf(c.endpointBase+removeMemberEndpoint, groupID, membershipID)
|
URL := fmt.Sprintf(c.endpointBase+removeMemberEndpoint, groupID, membershipID)
|
||||||
|
|
||||||
httpReq, err := http.NewRequest("POST", URL, nil)
|
httpReq, err := http.NewRequest("POST", URL, nil)
|
||||||
@ -133,10 +141,10 @@ func (c *Client) RemoveMember(groupID, membershipID ID) error {
|
|||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
return c.doWithAuthToken(httpReq, nil)
|
return c.doWithAuthToken(ctx, httpReq, nil, authToken)
|
||||||
}
|
}
|
||||||
|
|
||||||
///// Update /////
|
/*/// Update ///*/
|
||||||
|
|
||||||
/*
|
/*
|
||||||
UpdateMember -
|
UpdateMember -
|
||||||
@ -144,28 +152,31 @@ UpdateMember -
|
|||||||
Update your nickname in a group. The nickname must be
|
Update your nickname in a group. The nickname must be
|
||||||
between 1 and 50 characters.
|
between 1 and 50 characters.
|
||||||
*/
|
*/
|
||||||
func (c *Client) UpdateMember(groupID ID, nickname string) (*Member, error) {
|
func (c *Client) UpdateMember(ctx context.Context, groupID ID, nickname string, authToken string) (*Member, error) {
|
||||||
URL := fmt.Sprintf(c.endpointBase+updateMemberEndpoint, groupID)
|
URL := fmt.Sprintf(c.endpointBase+updateMemberEndpoint, groupID)
|
||||||
|
|
||||||
httpReq, err := http.NewRequest("POST", URL, nil)
|
type Nickname struct {
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
|
|
||||||
type membership struct {
|
|
||||||
Nickname string `json:"nickname"`
|
Nickname string `json:"nickname"`
|
||||||
}
|
}
|
||||||
|
var data = struct {
|
||||||
|
Membership Nickname `json:"membership"`
|
||||||
|
}{
|
||||||
|
Nickname{nickname},
|
||||||
|
}
|
||||||
|
|
||||||
data := url.Values{}
|
jsonBytes, err := json.Marshal(&data)
|
||||||
bytes, err := json.Marshal(membership{nickname})
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
httpReq, err := http.NewRequest("POST", URL, bytes.NewBuffer(jsonBytes))
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
data.Add("membership", string(bytes))
|
|
||||||
|
|
||||||
var resp Member
|
var resp Member
|
||||||
|
|
||||||
err = c.doWithAuthToken(httpReq, &resp)
|
err = c.doWithAuthToken(ctx, httpReq, &resp, authToken)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
@ -1,6 +1,8 @@
|
|||||||
|
// Package groupme defines a client capable of executing API commands for the GroupMe chat service
|
||||||
package groupme
|
package groupme
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"context"
|
||||||
"fmt"
|
"fmt"
|
||||||
"net/http"
|
"net/http"
|
||||||
"testing"
|
"testing"
|
||||||
@ -18,6 +20,7 @@ func (s *MembersAPISuite) SetupSuite() {
|
|||||||
|
|
||||||
func (s *MembersAPISuite) TestMembersAdd() {
|
func (s *MembersAPISuite) TestMembersAdd() {
|
||||||
_, err := s.client.AddMembers(
|
_, err := s.client.AddMembers(
|
||||||
|
context.Background(),
|
||||||
"1",
|
"1",
|
||||||
&Member{Nickname: "test"},
|
&Member{Nickname: "test"},
|
||||||
)
|
)
|
||||||
@ -25,17 +28,17 @@ func (s *MembersAPISuite) TestMembersAdd() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (s *MembersAPISuite) TestMembersResults() {
|
func (s *MembersAPISuite) TestMembersResults() {
|
||||||
_, err := s.client.AddMembersResults("1", "123")
|
_, err := s.client.AddMembersResults(context.Background(), "1", "123")
|
||||||
s.Require().NoError(err)
|
s.Require().NoError(err)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *MembersAPISuite) TestMembersRemove() {
|
func (s *MembersAPISuite) TestMembersRemove() {
|
||||||
err := s.client.RemoveMember("1", "123")
|
err := s.client.RemoveMember(context.Background(), "1", "123")
|
||||||
s.Require().NoError(err)
|
s.Require().NoError(err)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *MembersAPISuite) TestMembersUpdate() {
|
func (s *MembersAPISuite) TestMembersUpdate() {
|
||||||
_, err := s.client.UpdateMember("1", "nickname")
|
_, err := s.client.UpdateMember(context.Background(), "1", "nickname")
|
||||||
s.Require().NoError(err)
|
s.Require().NoError(err)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1,9 +1,12 @@
|
|||||||
|
// Package groupme defines a client capable of executing API commands for the GroupMe chat service
|
||||||
package groupme
|
package groupme
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"bytes"
|
||||||
|
"context"
|
||||||
|
"encoding/json"
|
||||||
"fmt"
|
"fmt"
|
||||||
"net/http"
|
"net/http"
|
||||||
"net/url"
|
|
||||||
"strconv"
|
"strconv"
|
||||||
|
|
||||||
"github.com/google/uuid"
|
"github.com/google/uuid"
|
||||||
@ -11,7 +14,7 @@ import (
|
|||||||
|
|
||||||
// GroupMe documentation: https://dev.groupme.com/docs/v3#messages
|
// GroupMe documentation: https://dev.groupme.com/docs/v3#messages
|
||||||
|
|
||||||
////////// Endpoints //////////
|
/*//////// Endpoints ////////*/
|
||||||
const (
|
const (
|
||||||
// Used to build other endpoints
|
// Used to build other endpoints
|
||||||
messagesEndpointRoot = groupEndpointRoot + "/messages"
|
messagesEndpointRoot = groupEndpointRoot + "/messages"
|
||||||
@ -20,9 +23,7 @@ const (
|
|||||||
createMessagesEndpoint = messagesEndpointRoot // POST
|
createMessagesEndpoint = messagesEndpointRoot // POST
|
||||||
)
|
)
|
||||||
|
|
||||||
// Index
|
// IndexMessagesQuery defines the optional URL parameters for IndexMessages
|
||||||
|
|
||||||
// MessagesQuery defines the optional URL parameters for IndexMessages
|
|
||||||
type IndexMessagesQuery struct {
|
type IndexMessagesQuery struct {
|
||||||
// Returns messages created before the given message ID
|
// Returns messages created before the given message ID
|
||||||
BeforeID ID
|
BeforeID ID
|
||||||
@ -38,7 +39,7 @@ func (q IndexMessagesQuery) String() string {
|
|||||||
return marshal(&q)
|
return marshal(&q)
|
||||||
}
|
}
|
||||||
|
|
||||||
// MessagesIndexResponse contains the count and set of
|
// IndexMessagesResponse contains the count and set of
|
||||||
// messages returned by the IndexMessages API request
|
// messages returned by the IndexMessages API request
|
||||||
type IndexMessagesResponse struct {
|
type IndexMessagesResponse struct {
|
||||||
Count int `json:"count"`
|
Count int `json:"count"`
|
||||||
@ -50,41 +51,31 @@ func (r IndexMessagesResponse) String() string {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
IndexMessages -
|
IndexMessages - Retrieves messages for a group.
|
||||||
|
|
||||||
Retrieve messages for a group.
|
|
||||||
|
|
||||||
By default, messages are returned in groups of 20, ordered by
|
By default, messages are returned in groups of 20, ordered by
|
||||||
created_at descending. This can be raised or lowered by passing
|
created_at descending. This can be raised or lowered by passing
|
||||||
a limit parameter, up to a maximum of 100 messages.
|
a limit parameter, up to a maximum of 100 messages.
|
||||||
|
|
||||||
Messages can be scanned by providing a message ID as either the
|
Messages can be scanned by providing a message ID as either the
|
||||||
before_id, since_id, or after_id parameter. If before_id is
|
before_id, since_id, or after_id parameter. If before_id is
|
||||||
provided, then messages immediately preceding the given message
|
provided, then messages immediately preceding the given message
|
||||||
will be returned, in descending order. This can be used to
|
will be returned, in descending order. This can be used to
|
||||||
continually page back through a group's messages.
|
continually page back through a group's messages.
|
||||||
|
|
||||||
The after_id parameter will return messages that immediately
|
The after_id parameter will return messages that immediately
|
||||||
follow a given message, this time in ascending order (which
|
follow a given message, this time in ascending order (which
|
||||||
makes it easy to pick off the last result for continued
|
makes it easy to pick off the last result for continued
|
||||||
pagination).
|
pagination).
|
||||||
|
|
||||||
Finally, the since_id parameter also returns messages created
|
Finally, the since_id parameter also returns messages created
|
||||||
after the given message, but it retrieves the most recent
|
after the given message, but it retrieves the most recent
|
||||||
messages. For example, if more than twenty messages are created
|
messages. For example, if more than twenty messages are created
|
||||||
after the since_id message, using this parameter will omit the
|
after the since_id message, using this parameter will omit the
|
||||||
messages that immediately follow the given message. This is a
|
messages that immediately follow the given message. This is a
|
||||||
bit counterintuitive, so take care.
|
bit counterintuitive, so take care.
|
||||||
|
|
||||||
If no messages are found (e.g. when filtering with before_id)
|
If no messages are found (e.g. when filtering with before_id)
|
||||||
we return code 304.
|
we return code 304.
|
||||||
|
|
||||||
Note that for historical reasons, likes are returned as an
|
Note that for historical reasons, likes are returned as an
|
||||||
array of user ids in the favorited_by key.
|
array of user ids in the favorited_by key.
|
||||||
|
|
||||||
Parameters: See MessageQuery
|
|
||||||
*/
|
*/
|
||||||
func (c *Client) IndexMessages(groupID ID, req *IndexMessagesQuery) (IndexMessagesResponse, error) {
|
func (c *Client) IndexMessages(ctx context.Context, groupID ID, req *IndexMessagesQuery, authToken string) (IndexMessagesResponse, error) {
|
||||||
url := fmt.Sprintf(c.endpointBase+indexMessagesEndpoint, groupID)
|
url := fmt.Sprintf(c.endpointBase+indexMessagesEndpoint, groupID)
|
||||||
httpReq, err := http.NewRequest("GET", url, nil)
|
httpReq, err := http.NewRequest("GET", url, nil)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
@ -110,7 +101,7 @@ func (c *Client) IndexMessages(groupID ID, req *IndexMessagesQuery) (IndexMessag
|
|||||||
URL.RawQuery = query.Encode()
|
URL.RawQuery = query.Encode()
|
||||||
|
|
||||||
var resp IndexMessagesResponse
|
var resp IndexMessagesResponse
|
||||||
err = c.doWithAuthToken(httpReq, &resp)
|
err = c.doWithAuthToken(ctx, httpReq, &resp, authToken)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return IndexMessagesResponse{}, err
|
return IndexMessagesResponse{}, err
|
||||||
}
|
}
|
||||||
@ -118,11 +109,8 @@ func (c *Client) IndexMessages(groupID ID, req *IndexMessagesQuery) (IndexMessag
|
|||||||
return resp, nil
|
return resp, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// Create
|
|
||||||
|
|
||||||
/*
|
/*
|
||||||
CreateMessage -
|
CreateMessage - Send a message to a group
|
||||||
Send a message to a group
|
|
||||||
|
|
||||||
If you want to attach an image, you must first process it
|
If you want to attach an image, you must first process it
|
||||||
through our image service.
|
through our image service.
|
||||||
@ -136,36 +124,31 @@ The character map is an array of arrays containing rune data
|
|||||||
([[{pack_id,offset}],...]).
|
([[{pack_id,offset}],...]).
|
||||||
|
|
||||||
The placeholder should be a high-point/invisible UTF-8 character.
|
The placeholder should be a high-point/invisible UTF-8 character.
|
||||||
|
|
||||||
Parameters:
|
|
||||||
groupID - required, ID(String)
|
|
||||||
See Message.
|
|
||||||
text - required, string. Can be ommitted if at least one
|
|
||||||
attachment is present
|
|
||||||
attachments - a polymorphic list of attachments (locations,
|
|
||||||
images, etc). You may have You may have more than
|
|
||||||
one of any type of attachment, provided clients can
|
|
||||||
display it.
|
|
||||||
|
|
||||||
*/
|
*/
|
||||||
func (c *Client) CreateMessage(groupID ID, m *Message) (*Message, error) {
|
func (c *Client) CreateMessage(ctx context.Context, groupID ID, m *Message, authToken string) (*Message, error) {
|
||||||
URL := fmt.Sprintf(c.endpointBase+createMessagesEndpoint, groupID)
|
URL := fmt.Sprintf(c.endpointBase+createMessagesEndpoint, groupID)
|
||||||
httpReq, err := http.NewRequest("POST", URL, nil)
|
|
||||||
|
m.SourceGUID = uuid.New().String()
|
||||||
|
var data = struct {
|
||||||
|
Message *Message `json:"message"`
|
||||||
|
}{
|
||||||
|
m,
|
||||||
|
}
|
||||||
|
|
||||||
|
jsonBytes, err := json.Marshal(&data)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
m.SourceGUID = uuid.New().String()
|
httpReq, err := http.NewRequest("POST", URL, bytes.NewBuffer(jsonBytes))
|
||||||
|
if err != nil {
|
||||||
data := url.Values{}
|
return nil, err
|
||||||
data.Set("message", m.String())
|
}
|
||||||
|
|
||||||
httpReq.PostForm = data
|
|
||||||
|
|
||||||
var resp struct {
|
var resp struct {
|
||||||
*Message `json:"message"`
|
*Message `json:"message"`
|
||||||
}
|
}
|
||||||
err = c.doWithAuthToken(httpReq, &resp)
|
err = c.doWithAuthToken(ctx, httpReq, &resp, authToken)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
@ -1,6 +1,8 @@
|
|||||||
|
// Package groupme defines a client capable of executing API commands for the GroupMe chat service
|
||||||
package groupme
|
package groupme
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"context"
|
||||||
"fmt"
|
"fmt"
|
||||||
"net/http"
|
"net/http"
|
||||||
"testing"
|
"testing"
|
||||||
@ -18,6 +20,7 @@ func (s *MessagesAPISuite) SetupSuite() {
|
|||||||
|
|
||||||
func (s *MessagesAPISuite) TestMessagesIndex() {
|
func (s *MessagesAPISuite) TestMessagesIndex() {
|
||||||
resp, err := s.client.IndexMessages(
|
resp, err := s.client.IndexMessages(
|
||||||
|
context.Background(),
|
||||||
ID("123"),
|
ID("123"),
|
||||||
&IndexMessagesQuery{
|
&IndexMessagesQuery{
|
||||||
BeforeID: "0123456789",
|
BeforeID: "0123456789",
|
||||||
@ -35,6 +38,7 @@ func (s *MessagesAPISuite) TestMessagesIndex() {
|
|||||||
|
|
||||||
func (s *MessagesAPISuite) TestMessagesCreate() {
|
func (s *MessagesAPISuite) TestMessagesCreate() {
|
||||||
message, err := s.client.CreateMessage(
|
message, err := s.client.CreateMessage(
|
||||||
|
context.Background(),
|
||||||
ID("123"),
|
ID("123"),
|
||||||
&Message{
|
&Message{
|
||||||
Text: "Test",
|
Text: "Test",
|
||||||
@ -49,6 +53,7 @@ func TestMessagesAPISuite(t *testing.T) {
|
|||||||
suite.Run(t, new(MessagesAPISuite))
|
suite.Run(t, new(MessagesAPISuite))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// nolint // not duplicate code
|
||||||
func messagesTestRouter() *mux.Router {
|
func messagesTestRouter() *mux.Router {
|
||||||
router := mux.NewRouter().Queries("token", "").Subrouter()
|
router := mux.NewRouter().Queries("token", "").Subrouter()
|
||||||
|
|
||||||
|
196
real_time.go
Normal file
196
real_time.go
Normal file
@ -0,0 +1,196 @@
|
|||||||
|
package groupme
|
||||||
|
|
||||||
|
import (
|
||||||
|
"context"
|
||||||
|
"errors"
|
||||||
|
"gitea.watsonlabs.net/watsonb8/fayec"
|
||||||
|
"gitea.watsonlabs.net/watsonb8/fayec/message"
|
||||||
|
"gitea.watsonlabs.net/watsonb8/fayec/subscription"
|
||||||
|
"log"
|
||||||
|
"strings"
|
||||||
|
"sync"
|
||||||
|
"time"
|
||||||
|
)
|
||||||
|
|
||||||
|
const (
|
||||||
|
PushServer = "wss://push.groupme.com/faye"
|
||||||
|
userChannel = "/user/"
|
||||||
|
groupChannel = "/group/"
|
||||||
|
dmChannel = "/direct_message/"
|
||||||
|
)
|
||||||
|
|
||||||
|
var (
|
||||||
|
ErrHandlerNotFound = errors.New("Handler not found")
|
||||||
|
ErrListenerNotStarted = errors.New("GroupMe listener not started")
|
||||||
|
)
|
||||||
|
|
||||||
|
var concur = sync.Mutex{}
|
||||||
|
|
||||||
|
type HandlerAll interface {
|
||||||
|
Handler
|
||||||
|
|
||||||
|
//of self
|
||||||
|
HandlerText
|
||||||
|
HandlerLike
|
||||||
|
HandlerMembership
|
||||||
|
|
||||||
|
//of group
|
||||||
|
HandleGroupTopic
|
||||||
|
HandleGroupAvatar
|
||||||
|
HandleGroupName
|
||||||
|
HandleGroupLikeIcon
|
||||||
|
|
||||||
|
//of group members
|
||||||
|
HandleMemberNewNickname
|
||||||
|
HandleMemberNewAvatar
|
||||||
|
HandleMembers
|
||||||
|
}
|
||||||
|
type Handler interface {
|
||||||
|
HandleError(error)
|
||||||
|
}
|
||||||
|
type HandlerText interface {
|
||||||
|
HandleTextMessage(Message)
|
||||||
|
}
|
||||||
|
type HandlerLike interface {
|
||||||
|
HandleLike(Message)
|
||||||
|
}
|
||||||
|
type HandlerMembership interface {
|
||||||
|
HandleJoin(ID)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Group Handlers
|
||||||
|
type HandleGroupTopic interface {
|
||||||
|
HandleGroupTopic(group ID, newTopic string)
|
||||||
|
}
|
||||||
|
|
||||||
|
type HandleGroupName interface {
|
||||||
|
HandleGroupName(group ID, newName string)
|
||||||
|
}
|
||||||
|
type HandleGroupAvatar interface {
|
||||||
|
HandleGroupAvatar(group ID, newAvatar string)
|
||||||
|
}
|
||||||
|
type HandleGroupLikeIcon interface {
|
||||||
|
HandleLikeIcon(group ID, PackID, PackIndex int, Type string)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Group member handlers
|
||||||
|
type HandleMemberNewNickname interface {
|
||||||
|
HandleNewNickname(group ID, user ID, newName string)
|
||||||
|
}
|
||||||
|
|
||||||
|
type HandleMemberNewAvatar interface {
|
||||||
|
HandleNewAvatarInGroup(group ID, user ID, avatarURL string)
|
||||||
|
}
|
||||||
|
type HandleMembers interface {
|
||||||
|
//HandleNewMembers returns only partial member with id and nickname; added is false if removing
|
||||||
|
HandleMembers(group ID, members []Member, added bool)
|
||||||
|
}
|
||||||
|
|
||||||
|
// PushSubscription manages real time subscription
|
||||||
|
type PushSubscription struct {
|
||||||
|
channel chan message.Data
|
||||||
|
client *fayec.Client
|
||||||
|
handlers map[string][]Handler // key == token
|
||||||
|
LastConnected int64
|
||||||
|
}
|
||||||
|
|
||||||
|
// NewPushSubscription creates and returns a push subscription object
|
||||||
|
func NewPushSubscription(context context.Context) PushSubscription {
|
||||||
|
|
||||||
|
r := PushSubscription{
|
||||||
|
channel: make(chan message.Data),
|
||||||
|
handlers: make(map[string][]Handler),
|
||||||
|
}
|
||||||
|
|
||||||
|
return r
|
||||||
|
}
|
||||||
|
|
||||||
|
func (r *PushSubscription) AddHandler(h Handler, authToken string) {
|
||||||
|
if r.handlers[authToken] == nil {
|
||||||
|
r.handlers[authToken] = []Handler{h}
|
||||||
|
} else {
|
||||||
|
r.handlers[authToken] = append(r.handlers[authToken], h)
|
||||||
|
}
|
||||||
|
//r.handlers = append(r.handlers, h)
|
||||||
|
}
|
||||||
|
|
||||||
|
// AddFullHandler is the same as AddHandler except it ensures the interface implements everything
|
||||||
|
func (r *PushSubscription) AddFullHandler(h HandlerAll, authToken string) {
|
||||||
|
if r.handlers[authToken] == nil {
|
||||||
|
r.handlers[authToken] = []Handler{h}
|
||||||
|
} else {
|
||||||
|
r.handlers[authToken] = append(r.handlers[authToken], h)
|
||||||
|
}
|
||||||
|
|
||||||
|
//r.handlers = append(r.handlers, h)
|
||||||
|
}
|
||||||
|
|
||||||
|
var RealTimeHandlers map[string]func(r *PushSubscription, channel string, authToken string, data ...interface{})
|
||||||
|
var RealTimeSystemHandlers map[string]func(r *PushSubscription, channel string, id ID, authToken string, rawData []byte)
|
||||||
|
|
||||||
|
// Listen connects to GroupMe. Runs in Goroutine.
|
||||||
|
func (r *PushSubscription) Connect(context context.Context) error {
|
||||||
|
c, err := fayec.NewClient(PushServer)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
r.client = c
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// SubscribeToUser to users
|
||||||
|
func (r *PushSubscription) SubscribeToUser(context context.Context, id ID, authToken string) error {
|
||||||
|
return r.subscribeWithPrefix(userChannel, context, id, authToken)
|
||||||
|
}
|
||||||
|
|
||||||
|
// SubscribeToGroup to groups for typing notification
|
||||||
|
func (r *PushSubscription) SubscribeToGroup(context context.Context, id ID, authToken string) error {
|
||||||
|
return r.subscribeWithPrefix(groupChannel, context, id, authToken)
|
||||||
|
}
|
||||||
|
|
||||||
|
// SubscribeToDM to users
|
||||||
|
func (r *PushSubscription) SubscribeToDM(context context.Context, id ID, authToken string) error {
|
||||||
|
id = ID(strings.Replace(id.String(), "+", "_", 1))
|
||||||
|
return r.subscribeWithPrefix(dmChannel, context, id, authToken)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (r *PushSubscription) subscribeWithPrefix(prefix string, context context.Context, groupID ID, authToken string) error {
|
||||||
|
concur.Lock()
|
||||||
|
defer concur.Unlock()
|
||||||
|
if r.client == nil {
|
||||||
|
return ErrListenerNotStarted
|
||||||
|
}
|
||||||
|
|
||||||
|
var sub *subscription.Subscription
|
||||||
|
sub, err := r.client.Subscribe(prefix+groupID.String(), authToken)
|
||||||
|
if err != nil {
|
||||||
|
panic(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
err = sub.OnMessage(func(channel string, data message.Data) {
|
||||||
|
r.LastConnected = time.Now().Unix()
|
||||||
|
dataMap := data.(map[string]interface{})
|
||||||
|
content := dataMap["subject"]
|
||||||
|
contentType := dataMap["type"].(string)
|
||||||
|
|
||||||
|
handler, ok := RealTimeHandlers[contentType]
|
||||||
|
if !ok {
|
||||||
|
if contentType == "ping" ||
|
||||||
|
len(contentType) == 0 ||
|
||||||
|
content == "" {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
log.Println("Unable to handle GroupMe message type", contentType)
|
||||||
|
} else {
|
||||||
|
handler(r, channel, authToken, content)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// Connected check if connected
|
||||||
|
func (r *PushSubscription) Connected() bool {
|
||||||
|
return r.LastConnected+30 >= time.Now().Unix()
|
||||||
|
}
|
266
real_time_handler.go
Normal file
266
real_time_handler.go
Normal file
@ -0,0 +1,266 @@
|
|||||||
|
package groupme
|
||||||
|
|
||||||
|
import (
|
||||||
|
"encoding/json"
|
||||||
|
"fmt"
|
||||||
|
"log"
|
||||||
|
"strconv"
|
||||||
|
)
|
||||||
|
|
||||||
|
func init() {
|
||||||
|
|
||||||
|
RealTimeHandlers = make(map[string]func(r *PushSubscription, channel string, authToken string, data ...interface{}))
|
||||||
|
|
||||||
|
//Base Handlers on user channel
|
||||||
|
RealTimeHandlers["direct_message.create"] = func(r *PushSubscription, channel string, authToken string, data ...interface{}) {
|
||||||
|
b, _ := json.Marshal(data[0])
|
||||||
|
out := Message{}
|
||||||
|
_ = json.Unmarshal(b, &out)
|
||||||
|
|
||||||
|
//maybe something with API versioning
|
||||||
|
out.ConversationID = out.ChatID
|
||||||
|
|
||||||
|
if out.UserID.String() == "system" {
|
||||||
|
event := struct {
|
||||||
|
Event struct {
|
||||||
|
Kind string `json:"type"`
|
||||||
|
Data interface{}
|
||||||
|
}
|
||||||
|
}{}
|
||||||
|
|
||||||
|
err := json.Unmarshal(b, &event)
|
||||||
|
if err != nil {
|
||||||
|
fmt.Println(err)
|
||||||
|
}
|
||||||
|
rawData, _ := json.Marshal(event.Event.Data)
|
||||||
|
handler, ok := RealTimeSystemHandlers[event.Event.Kind]
|
||||||
|
if !ok {
|
||||||
|
log.Println("Unable to handle system message of type", event.Event.Kind)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
id := out.GroupID
|
||||||
|
if len(id) == 0 {
|
||||||
|
id = out.ConversationID
|
||||||
|
}
|
||||||
|
|
||||||
|
handler(r, channel, id, authToken, rawData)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
handlers := r.handlers[authToken]
|
||||||
|
|
||||||
|
for _, h := range handlers {
|
||||||
|
if h, ok := h.(HandlerText); ok {
|
||||||
|
h.HandleTextMessage(out)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
RealTimeHandlers["line.create"] = RealTimeHandlers["direct_message.create"]
|
||||||
|
|
||||||
|
RealTimeHandlers["like.create"] = func(r *PushSubscription, channel string, authToken string, data ...interface{}) { //should be an associated chatEvent
|
||||||
|
}
|
||||||
|
|
||||||
|
RealTimeHandlers["membership.create"] = func(r *PushSubscription, channel string, authToken string, data ...interface{}) {
|
||||||
|
c, _ := data[0].(map[string]interface{})
|
||||||
|
id, _ := c["id"].(string)
|
||||||
|
|
||||||
|
handlers := r.handlers[authToken]
|
||||||
|
|
||||||
|
for _, h := range handlers {
|
||||||
|
if h, ok := h.(HandlerMembership); ok {
|
||||||
|
h.HandleJoin(ID(id))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
//following are for each chat
|
||||||
|
RealTimeHandlers["favorite"] = func(r *PushSubscription, channel string, authToken string, data ...interface{}) {
|
||||||
|
c, ok := data[0].(map[string]interface{})
|
||||||
|
if !ok {
|
||||||
|
fmt.Println(data, "err")
|
||||||
|
return
|
||||||
|
}
|
||||||
|
e, ok := c["line"]
|
||||||
|
if !ok {
|
||||||
|
fmt.Println(data, "err")
|
||||||
|
return
|
||||||
|
}
|
||||||
|
d, _ := json.Marshal(e)
|
||||||
|
msg := Message{}
|
||||||
|
_ = json.Unmarshal(d, &msg)
|
||||||
|
|
||||||
|
handlers := r.handlers[authToken]
|
||||||
|
|
||||||
|
for _, h := range handlers {
|
||||||
|
if h, ok := h.(HandlerLike); ok {
|
||||||
|
h.HandleLike(msg)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
//following are for messages from system (administrative/settings changes)
|
||||||
|
RealTimeSystemHandlers = make(map[string]func(r *PushSubscription, channel string, id ID, authToken string, rawData []byte))
|
||||||
|
|
||||||
|
RealTimeSystemHandlers["membership.nickname_changed"] = func(r *PushSubscription, channel string, id ID, authToken string, rawData []byte) {
|
||||||
|
thing := struct {
|
||||||
|
Name string
|
||||||
|
User struct {
|
||||||
|
ID int
|
||||||
|
}
|
||||||
|
}{}
|
||||||
|
_ = json.Unmarshal(rawData, &thing)
|
||||||
|
|
||||||
|
handlers := r.handlers[authToken]
|
||||||
|
|
||||||
|
for _, h := range handlers {
|
||||||
|
if h, ok := h.(HandleMemberNewNickname); ok {
|
||||||
|
h.HandleNewNickname(id, ID(strconv.Itoa(thing.User.ID)), thing.Name)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
RealTimeSystemHandlers["membership.avatar_changed"] = func(r *PushSubscription, channel string, id ID, authToken string, rawData []byte) {
|
||||||
|
content := struct {
|
||||||
|
AvatarURL string `json:"avatar_url"`
|
||||||
|
User struct {
|
||||||
|
ID int
|
||||||
|
}
|
||||||
|
}{}
|
||||||
|
_ = json.Unmarshal(rawData, &content)
|
||||||
|
|
||||||
|
handlers := r.handlers[authToken]
|
||||||
|
|
||||||
|
for _, h := range handlers {
|
||||||
|
if h, ok := h.(HandleMemberNewAvatar); ok {
|
||||||
|
h.HandleNewAvatarInGroup(id, ID(strconv.Itoa(content.User.ID)), content.AvatarURL)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
RealTimeSystemHandlers["membership.announce.added"] = func(r *PushSubscription, channel string, id ID, authToken string, rawData []byte) {
|
||||||
|
data := struct {
|
||||||
|
Added []Member `json:"added_users"`
|
||||||
|
}{}
|
||||||
|
_ = json.Unmarshal(rawData, &data)
|
||||||
|
handlers := r.handlers[authToken]
|
||||||
|
|
||||||
|
for _, h := range handlers {
|
||||||
|
if h, ok := h.(HandleMembers); ok {
|
||||||
|
h.HandleMembers(id, data.Added, true)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
RealTimeSystemHandlers["membership.notifications.removed"] = func(r *PushSubscription, channel string, id ID, authToken string, rawData []byte) {
|
||||||
|
data := struct {
|
||||||
|
Added Member `json:"removed_user"`
|
||||||
|
}{}
|
||||||
|
_ = json.Unmarshal(rawData, &data)
|
||||||
|
handlers := r.handlers[authToken]
|
||||||
|
|
||||||
|
for _, h := range handlers {
|
||||||
|
if h, ok := h.(HandleMembers); ok {
|
||||||
|
h.HandleMembers(id, []Member{data.Added}, false)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
RealTimeSystemHandlers["membership.name_change"] = func(r *PushSubscription, channel string, id ID, authToken string, rawData []byte) {
|
||||||
|
|
||||||
|
data := struct {
|
||||||
|
Name string
|
||||||
|
}{}
|
||||||
|
_ = json.Unmarshal(rawData, &data)
|
||||||
|
|
||||||
|
handlers := r.handlers[authToken]
|
||||||
|
|
||||||
|
for _, h := range handlers {
|
||||||
|
if h, ok := h.(HandleGroupName); ok {
|
||||||
|
h.HandleGroupName(id, data.Name)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
RealTimeSystemHandlers["group.name_change"] = func(r *PushSubscription, channel string, id ID, authToken string, rawData []byte) {
|
||||||
|
|
||||||
|
data := struct {
|
||||||
|
Name string
|
||||||
|
}{}
|
||||||
|
_ = json.Unmarshal(rawData, &data)
|
||||||
|
|
||||||
|
handlers := r.handlers[authToken]
|
||||||
|
|
||||||
|
for _, h := range handlers {
|
||||||
|
if h, ok := h.(HandleGroupName); ok {
|
||||||
|
h.HandleGroupName(id, data.Name)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
RealTimeSystemHandlers["group.topic_change"] = func(r *PushSubscription, channel string, id ID, authToken string, rawData []byte) {
|
||||||
|
|
||||||
|
data := struct {
|
||||||
|
Topic string
|
||||||
|
}{}
|
||||||
|
_ = json.Unmarshal(rawData, &data)
|
||||||
|
|
||||||
|
handlers := r.handlers[authToken]
|
||||||
|
|
||||||
|
for _, h := range handlers {
|
||||||
|
if h, ok := h.(HandleGroupTopic); ok {
|
||||||
|
h.HandleGroupTopic(id, data.Topic)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
RealTimeSystemHandlers["group.avatar_change"] = func(r *PushSubscription, channel string, id ID, authToken string, rawData []byte) {
|
||||||
|
data := struct {
|
||||||
|
AvatarURL string `json:"avatar_url"`
|
||||||
|
}{}
|
||||||
|
_ = json.Unmarshal(rawData, &data)
|
||||||
|
|
||||||
|
handlers := r.handlers[authToken]
|
||||||
|
|
||||||
|
for _, h := range handlers {
|
||||||
|
if h, ok := h.(HandleGroupAvatar); ok {
|
||||||
|
h.HandleGroupAvatar(id, data.AvatarURL)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
RealTimeSystemHandlers["group.like_icon_set"] = func(r *PushSubscription, channel string, id ID, authToken string, rawData []byte) {
|
||||||
|
data := struct {
|
||||||
|
LikeIcon struct {
|
||||||
|
PackID int `json:"pack_id"`
|
||||||
|
PackIndex int `json:"pack_index"`
|
||||||
|
Type string
|
||||||
|
} `json:"like_icon"`
|
||||||
|
}{}
|
||||||
|
_ = json.Unmarshal(rawData, &data)
|
||||||
|
|
||||||
|
handlers := r.handlers[authToken]
|
||||||
|
|
||||||
|
for _, h := range handlers {
|
||||||
|
if h, ok := h.(HandleGroupLikeIcon); ok {
|
||||||
|
h.HandleLikeIcon(id, data.LikeIcon.PackID, data.LikeIcon.PackIndex, data.LikeIcon.Type)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
RealTimeSystemHandlers["group.like_icon_removed"] = func(r *PushSubscription, channel string, id ID, authToken string, rawData []byte) {
|
||||||
|
handlers := r.handlers[authToken]
|
||||||
|
|
||||||
|
for _, h := range handlers {
|
||||||
|
if h, ok := h.(HandleGroupLikeIcon); ok {
|
||||||
|
h.HandleLikeIcon(id, 0, 0, "")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
@ -1,15 +1,17 @@
|
|||||||
|
// Package groupme defines a client capable of executing API commands for the GroupMe chat service
|
||||||
package groupme
|
package groupme
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"bytes"
|
||||||
|
"context"
|
||||||
|
"encoding/json"
|
||||||
"fmt"
|
"fmt"
|
||||||
"net/http"
|
"net/http"
|
||||||
"net/url"
|
|
||||||
"strconv"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
// GroupMe documentation: https://dev.groupme.com/docs/v3#sms_mode
|
// GroupMe documentation: https://dev.groupme.com/docs/v3#sms_mode
|
||||||
|
|
||||||
////////// Endpoints //////////
|
/*//////// Endpoints ////////*/
|
||||||
const (
|
const (
|
||||||
// Used to build other endpoints
|
// Used to build other endpoints
|
||||||
smsModeEndpointRoot = usersEndpointRoot + "/sms_mode"
|
smsModeEndpointRoot = usersEndpointRoot + "/sms_mode"
|
||||||
@ -19,7 +21,7 @@ const (
|
|||||||
deleteSMSModeEndpoint = smsModeEndpointRoot + "/delete" // POST
|
deleteSMSModeEndpoint = smsModeEndpointRoot + "/delete" // POST
|
||||||
)
|
)
|
||||||
|
|
||||||
////////// API Requests //////////
|
/*//////// API Requests ////////*/
|
||||||
|
|
||||||
// Create
|
// Create
|
||||||
|
|
||||||
@ -29,28 +31,35 @@ Enables SMS mode for N hours, where N is at most 48. After N
|
|||||||
hours have elapsed, user will receive push notfications.
|
hours have elapsed, user will receive push notfications.
|
||||||
|
|
||||||
Parameters:
|
Parameters:
|
||||||
|
|
||||||
duration - required, integer
|
duration - required, integer
|
||||||
registration_id - string; The push notification ID/token
|
registration_id - string; The push notification ID/token
|
||||||
that should be suppressed during SMS mode. If this is
|
that should be suppressed during SMS mode. If this is
|
||||||
omitted, both SMS and push notifications will be
|
omitted, both SMS and push notifications will be
|
||||||
delivered to the device.
|
delivered to the device.
|
||||||
*/
|
*/
|
||||||
func (c *Client) CreateSMSMode(duration int, registrationID *ID) error {
|
func (c *Client) CreateSMSMode(ctx context.Context, duration int, registrationID *ID, authToken string) error {
|
||||||
httpReq, err := http.NewRequest("POST", c.endpointBase+createSMSModeEndpoint, nil)
|
URL := fmt.Sprintf(c.endpointBase + createSMSModeEndpoint)
|
||||||
|
|
||||||
|
var data = struct {
|
||||||
|
Duration int `json:"duration"`
|
||||||
|
RegistrationID *ID `json:"registration_id,omitempty"`
|
||||||
|
}{
|
||||||
|
duration,
|
||||||
|
registrationID,
|
||||||
|
}
|
||||||
|
|
||||||
|
jsonBytes, err := json.Marshal(&data)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
data := url.Values{}
|
httpReq, err := http.NewRequest("POST", URL, bytes.NewBuffer(jsonBytes))
|
||||||
data.Add("duration", strconv.Itoa(duration))
|
if err != nil {
|
||||||
|
return err
|
||||||
if registrationID != nil {
|
|
||||||
data.Add("registration_id", registrationID.String())
|
|
||||||
}
|
}
|
||||||
|
|
||||||
httpReq.PostForm = data
|
err = c.doWithAuthToken(ctx, httpReq, nil, authToken)
|
||||||
|
|
||||||
err = c.doWithAuthToken(httpReq, nil)
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
@ -65,7 +74,7 @@ DeleteSMSMode -
|
|||||||
|
|
||||||
Disables SMS mode
|
Disables SMS mode
|
||||||
*/
|
*/
|
||||||
func (c *Client) DeleteSMSMode() error {
|
func (c *Client) DeleteSMSMode(ctx context.Context, authToken string) error {
|
||||||
url := fmt.Sprintf(c.endpointBase + deleteSMSModeEndpoint)
|
url := fmt.Sprintf(c.endpointBase + deleteSMSModeEndpoint)
|
||||||
|
|
||||||
httpReq, err := http.NewRequest("POST", url, nil)
|
httpReq, err := http.NewRequest("POST", url, nil)
|
||||||
@ -73,5 +82,5 @@ func (c *Client) DeleteSMSMode() error {
|
|||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
return c.doWithAuthToken(httpReq, nil)
|
return c.doWithAuthToken(ctx, httpReq, nil, authToken)
|
||||||
}
|
}
|
||||||
|
@ -1,6 +1,8 @@
|
|||||||
|
// Package groupme defines a client capable of executing API commands for the GroupMe chat service
|
||||||
package groupme
|
package groupme
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"context"
|
||||||
"net/http"
|
"net/http"
|
||||||
"testing"
|
"testing"
|
||||||
|
|
||||||
@ -16,16 +18,17 @@ func (s *SMSModeAPISuite) SetupSuite() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (s *SMSModeAPISuite) TestSMSModeCreate() {
|
func (s *SMSModeAPISuite) TestSMSModeCreate() {
|
||||||
s.Assert().NoError(s.client.CreateSMSMode(10, nil))
|
s.Assert().NoError(s.client.CreateSMSMode(context.Background(), 10, nil))
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *SMSModeAPISuite) TestSMSModeDelete() {
|
func (s *SMSModeAPISuite) TestSMSModeDelete() {
|
||||||
s.Assert().NoError(s.client.DeleteSMSMode())
|
s.Assert().NoError(s.client.DeleteSMSMode(context.Background()))
|
||||||
}
|
}
|
||||||
func TestSMSModeAPISuite(t *testing.T) {
|
func TestSMSModeAPISuite(t *testing.T) {
|
||||||
suite.Run(t, new(SMSModeAPISuite))
|
suite.Run(t, new(SMSModeAPISuite))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// nolint // not duplicate code
|
||||||
func smsModeTestRouter() *mux.Router {
|
func smsModeTestRouter() *mux.Router {
|
||||||
router := mux.NewRouter().Queries("token", "").Subrouter()
|
router := mux.NewRouter().Queries("token", "").Subrouter()
|
||||||
|
|
||||||
|
46
users_api.go
46
users_api.go
@ -1,14 +1,17 @@
|
|||||||
|
// Package groupme defines a client capable of executing API commands for the GroupMe chat service
|
||||||
package groupme
|
package groupme
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"bytes"
|
||||||
|
"context"
|
||||||
|
"encoding/json"
|
||||||
"fmt"
|
"fmt"
|
||||||
"net/http"
|
"net/http"
|
||||||
"net/url"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
// GroupMe documentation: https://dev.groupme.com/docs/v3#users
|
// GroupMe documentation: https://dev.groupme.com/docs/v3#users
|
||||||
|
|
||||||
////////// Endpoints //////////
|
/*//////// Endpoints ////////*/
|
||||||
const (
|
const (
|
||||||
// Used to build other endpoints
|
// Used to build other endpoints
|
||||||
usersEndpointRoot = "/users"
|
usersEndpointRoot = "/users"
|
||||||
@ -18,7 +21,7 @@ const (
|
|||||||
updateMyUserEndpoint = usersEndpointRoot + "/update" // POST
|
updateMyUserEndpoint = usersEndpointRoot + "/update" // POST
|
||||||
)
|
)
|
||||||
|
|
||||||
////////// API Requests //////////
|
/*//////// API Requests ////////*/
|
||||||
|
|
||||||
// Me
|
// Me
|
||||||
|
|
||||||
@ -28,9 +31,10 @@ MyUser -
|
|||||||
Loads a specific group.
|
Loads a specific group.
|
||||||
|
|
||||||
Parameters:
|
Parameters:
|
||||||
|
|
||||||
groupID - required, ID(string)
|
groupID - required, ID(string)
|
||||||
*/
|
*/
|
||||||
func (c *Client) MyUser() (*User, error) {
|
func (c *Client) MyUser(ctx context.Context, authToken string) (*User, error) {
|
||||||
URL := fmt.Sprintf(c.endpointBase + myUserEndpoint)
|
URL := fmt.Sprintf(c.endpointBase + myUserEndpoint)
|
||||||
|
|
||||||
httpReq, err := http.NewRequest("GET", URL, nil)
|
httpReq, err := http.NewRequest("GET", URL, nil)
|
||||||
@ -39,7 +43,7 @@ func (c *Client) MyUser() (*User, error) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
var resp User
|
var resp User
|
||||||
err = c.doWithAuthToken(httpReq, &resp)
|
err = c.doWithAuthToken(ctx, httpReq, &resp, authToken)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
@ -47,8 +51,7 @@ func (c *Client) MyUser() (*User, error) {
|
|||||||
return &resp, nil
|
return &resp, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// Update
|
// UserSettings are the settings for a GroupMe user
|
||||||
|
|
||||||
type UserSettings struct {
|
type UserSettings struct {
|
||||||
// URL to valid JPG/PNG/GIF image. URL will be converted into
|
// URL to valid JPG/PNG/GIF image. URL will be converted into
|
||||||
// an image service link (https://i.groupme.com/....)
|
// an image service link (https://i.groupme.com/....)
|
||||||
@ -63,40 +66,25 @@ type UserSettings struct {
|
|||||||
/*
|
/*
|
||||||
UpdateMyUser -
|
UpdateMyUser -
|
||||||
|
|
||||||
Update attributes about your own account
|
# Update attributes about your own account
|
||||||
|
|
||||||
Parameters: See UserSettings
|
Parameters: See UserSettings
|
||||||
*/
|
*/
|
||||||
func (c *Client) UpdateMyUser(us UserSettings) (*User, error) {
|
func (c *Client) UpdateMyUser(ctx context.Context, us UserSettings, authToken string) (*User, error) {
|
||||||
URL := fmt.Sprintf(c.endpointBase + updateMyUserEndpoint)
|
URL := fmt.Sprintf(c.endpointBase + updateMyUserEndpoint)
|
||||||
|
|
||||||
httpReq, err := http.NewRequest("POST", URL, nil)
|
jsonBytes, err := json.Marshal(&us)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
data := url.Values{}
|
httpReq, err := http.NewRequest("POST", URL, bytes.NewBuffer(jsonBytes))
|
||||||
|
if err != nil {
|
||||||
if us.AvatarURL != "" {
|
return nil, err
|
||||||
data.Add("avatar_url", us.AvatarURL)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if us.Name != "" {
|
|
||||||
data.Add("name", us.Name)
|
|
||||||
}
|
|
||||||
|
|
||||||
if us.Email != "" {
|
|
||||||
data.Add("email", us.Email)
|
|
||||||
}
|
|
||||||
|
|
||||||
if us.ZipCode != "" {
|
|
||||||
data.Add("zip_code", us.ZipCode)
|
|
||||||
}
|
|
||||||
|
|
||||||
httpReq.PostForm = data
|
|
||||||
|
|
||||||
var resp User
|
var resp User
|
||||||
err = c.doWithAuthToken(httpReq, &resp)
|
err = c.doWithAuthToken(ctx, httpReq, &resp, authToken)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
@ -1,6 +1,8 @@
|
|||||||
|
// Package groupme defines a client capable of executing API commands for the GroupMe chat service
|
||||||
package groupme
|
package groupme
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"context"
|
||||||
"fmt"
|
"fmt"
|
||||||
"net/http"
|
"net/http"
|
||||||
"testing"
|
"testing"
|
||||||
@ -17,13 +19,13 @@ func (s *UsersAPISuite) SetupSuite() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (s *UsersAPISuite) TestUsersMe() {
|
func (s *UsersAPISuite) TestUsersMe() {
|
||||||
user, err := s.client.MyUser()
|
user, err := s.client.MyUser(context.Background())
|
||||||
s.Require().NoError(err)
|
s.Require().NoError(err)
|
||||||
s.Assert().NotZero(user)
|
s.Assert().NotZero(user)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *UsersAPISuite) TestUsersUpdate() {
|
func (s *UsersAPISuite) TestUsersUpdate() {
|
||||||
user, err := s.client.UpdateMyUser(UserSettings{})
|
user, err := s.client.UpdateMyUser(context.Background(), UserSettings{})
|
||||||
s.Require().NoError(err)
|
s.Require().NoError(err)
|
||||||
s.Assert().NotZero(user)
|
s.Assert().NotZero(user)
|
||||||
}
|
}
|
||||||
@ -31,6 +33,8 @@ func (s *UsersAPISuite) TestUsersUpdate() {
|
|||||||
func TestUsersAPISuite(t *testing.T) {
|
func TestUsersAPISuite(t *testing.T) {
|
||||||
suite.Run(t, new(UsersAPISuite))
|
suite.Run(t, new(UsersAPISuite))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// nolint // not duplicate code
|
||||||
func usersTestRouter() *mux.Router {
|
func usersTestRouter() *mux.Router {
|
||||||
router := mux.NewRouter().Queries("token", "").Subrouter()
|
router := mux.NewRouter().Queries("token", "").Subrouter()
|
||||||
|
|
||||||
|
Loading…
x
Reference in New Issue
Block a user