Compare commits

...

37 Commits

Author SHA1 Message Date
f9c1b24194 Bump fayec 2023-09-26 13:03:49 -05:00
a58e4100e1 Upgrading fayec 2023-09-25 15:29:44 -05:00
7b9e2a8d29 Fixing bug where nil handler was called 2023-09-24 15:36:17 -04:00
d81730e3e7 Handlers are now fired for individual users 2023-09-22 16:10:44 -05:00
08bfe83ba4 Updating API to re-use client for multiple users 2023-09-20 16:47:47 -05:00
c0f3da8060 Using updated fayec client 2023-09-19 16:28:22 -05:00
5727a20506 Updating package name 2023-09-18 21:37:41 -05:00
3e9851b2c0 Usinc fayec client instead of wray 2023-09-18 21:35:00 -05:00
Sumner Evans
8f23e04eea
namespace under beeper
Signed-off-by: Sumner Evans <sumner@beeper.com>
2022-10-21 15:59:45 -05:00
Karmanyaah Malhotra
c2dbe7021e fix 2021-05-08 16:01:24 -04:00
Karmanyaah Malhotra
6eba33b3be relations api 2021-05-08 15:54:12 -04:00
Karmanyaah Malhotra
7961d30a51 Dependencies 2021-05-08 15:53:38 -04:00
Karmanyaah Malhotra
12931fb275 ChatID name different over push 2021-05-08 15:53:38 -04:00
Karmanyaah Malhotra
65299d9606 Update example to use new real-time api 2021-05-08 15:53:38 -04:00
Karmanyaah Malhotra
aa216ed4af Reformat real_time 2021-05-08 15:53:38 -04:00
Karmanyaah Malhotra
28b2d85660 logging 2021-05-08 15:53:38 -04:00
Karmanyaah Malhotra
8dd5993481 Fix panic 2021-05-08 15:53:38 -04:00
Karmanyaah Malhotra
042fb9a951 fix golangcilint issues 2021-05-08 15:53:38 -04:00
Karmanyaah Malhotra
b6715c2375 added more conditions 2021-05-08 15:53:38 -04:00
Karmanyaah Malhotra
c5d6c8a29c replace not required in production version 2021-05-08 15:53:38 -04:00
Karmanyaah Malhotra
d6a8b0818f Add many types of handling
fix listen loop
improve example
2021-05-08 15:53:38 -04:00
Karmanyaah Malhotra
da7bc977a3 Add membership 2021-05-08 15:53:38 -04:00
Karmanyaah Malhotra
eeb2f88b97 Use interfaces and callbacks 2021-05-08 15:53:38 -04:00
Karmanyaah Malhotra
784cfe93c1 real time push basics 2021-05-08 15:53:38 -04:00
Karmanyaah Malhotra
6d42a230d1 Fix bugs with direct message api 2021-05-08 15:44:18 -04:00
Karmanyaah Malhotra
7166300503 Add more attachment values 2021-05-08 15:44:18 -04:00
densestvoid
f92a8a7a86 Introducing Contexts
Contexts have been added to all API requests, in addition to other linting fixes
2021-01-22 16:47:22 -05:00
Densest Void
646bec0e27
Update README.md 2021-01-22 14:43:05 -05:00
Densest Void
b9317e3e37
Update and rename go.yml to go-test.yml 2021-01-22 14:39:15 -05:00
Densest Void
05eb619c40
Update golangci-lint.yml 2021-01-22 14:36:24 -05:00
Densest Void
055fdc7126
Delete go-test.yml 2021-01-22 14:35:10 -05:00
Densest Void
3bac8c4f92
Update go.yml 2021-01-22 14:34:56 -05:00
Densest Void
8fca7782ec
Create go.yml 2021-01-22 14:30:44 -05:00
Densest Void
8628d37bef
Update README.md 2021-01-22 13:56:31 -05:00
Densest Void
6f453ff6cc
Merge pull request #10 from tekkamanendless/return-meta-on-error
Return Meta on error
2020-12-29 15:52:53 -05:00
Douglas Danger Manley
73586d4b4c Ensure that we return the correct error code even if we can't read the body 2020-12-20 13:35:13 -05:00
Douglas Danger Manley
7f8d829ff7 Return the Meta on error
Previously, this would never actually return the Meta data structure
since by the time that it got parsed, we already knew that the request
was good.  Now, we do a special parse when we know that it failed so
that we can return the structured data (in particular, we want to be
able to use the HTTP status code).
2020-12-20 13:28:12 -05:00
39 changed files with 1184 additions and 623 deletions

View File

@ -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

View File

@ -1,15 +1,27 @@
- name: Run golangci-lint name: golangci-lint
# You may pin to the exact commit or the version. on:
# uses: golangci/golangci-lint-action@04eca2038305127fb1e6683425b6864cd5612f2d push:
uses: golangci/golangci-lint-action@v1.2.1 pull_request:
jobs:
golangci:
name: lint
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v2
- name: golangci-lint
uses: golangci/golangci-lint-action@v2
with: with:
# version of golangci-lint to use in form of v1.2.3 # Required: the version of golangci-lint is required and must be specified without patch version: we always use the latest patch version.
version: version: v1.29
# golangci-lint command line arguments
args: # optional, default is # Optional: working directory, useful for monorepos
# golangci-lint working directory, default is project root # working-directory: somedir
working-directory: # optional
# the token is used for fetching patch of a pull request to show only new issues # Optional: golangci-lint command line arguments.
github-token: # default is ${{ github.token }} # args: --issues-exit-code=0
# if set to true and the action runs on a pull request - the action outputs only newly found issues
only-new-issues: # Optional: show only new issues if it's a pull request. The default value is `false`.
# only-new-issues: true
# Optional: if set to true then the action will use pre-installed Go
# skip-go-installation: true

View File

@ -6,7 +6,6 @@ I would like to add common helper functions/features inspired by the package use
# GroupMe API Wrapper # GroupMe API Wrapper
![GitHub tag (latest SemVer)](https://img.shields.io/github/v/tag/densestvoid/groupme?label=version&logo=version&sort=semver) ![GitHub tag (latest SemVer)](https://img.shields.io/github/v/tag/densestvoid/groupme?label=version&logo=version&sort=semver)
[![PkgGoDev](https://pkg.go.dev/badge/github.com/densestvoid/groupme)](https://pkg.go.dev/github.com/densestvoid/groupme) [![PkgGoDev](https://pkg.go.dev/badge/github.com/densestvoid/groupme)](https://pkg.go.dev/github.com/densestvoid/groupme)
[![codecov](https://codecov.io/gh/densestvoid/groupme/branch/master/graph/badge.svg)](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.

View File

@ -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
} }

View File

@ -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) {

View File

@ -1,7 +1,9 @@
// 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"
"fmt" "fmt"
"net/http" "net/http"
@ -9,7 +11,7 @@ import (
// 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,22 +23,11 @@ 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) {
CreateBot -
Create a bot. See the Bots Tutorial (https://dev.groupme.com/tutorials/bots)
for a full walkthrough.
Parameters:
See Bot
Name - required
GroupID - required
*/
func (c *Client) CreateBot(bot *Bot) (*Bot, error) {
URL := c.endpointBase + createBotEndpoint URL := c.endpointBase + createBotEndpoint
var data = struct { var data = struct {
@ -56,7 +47,7 @@ func (c *Client) CreateBot(bot *Bot) (*Bot, error) {
} }
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
} }
@ -64,21 +55,9 @@ 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)
var data = struct { var data = struct {
@ -101,24 +80,18 @@ func (c *Client) PostBotMessage(botID ID, text string, pictureURL *string) error
return err return err
} }
return c.do(httpReq, nil) return c.do(ctx, 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
} }
@ -126,17 +99,8 @@ 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)
var data = struct { var data = struct {
@ -155,5 +119,5 @@ func (c *Client) DestroyBot(botID ID) error {
return err return err
} }
return c.doWithAuthToken(httpReq, nil) return c.doWithAuthToken(ctx, httpReq, nil, authToken)
} }

View File

@ -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) {

View File

@ -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
} }

View File

@ -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,

View File

@ -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"
"fmt"
"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,16 +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{
// TODO: enable transport information passing in // TODO: enable transport information passing in
httpClient: &http.Client{}, httpClient: &http.Client{},
endpointBase: GroupMeAPIBase, endpointBase: GroupMeAPIBase,
authorizationToken: authToken,
} }
} }
@ -41,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"`
@ -59,7 +58,10 @@ 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" { if req.Method == "POST" {
req.Header.Set("Content-Type", "application/json") req.Header.Set("Content-Type", "application/json")
} }
@ -70,38 +72,49 @@ func (c Client) do(req *http.Request, i interface{}) error {
} }
defer getResp.Body.Close() defer getResp.Body.Close()
bytes, err := ioutil.ReadAll(getResp.Body) var readBytes []byte
// Check Status Code is 1XX or 2XX
if getResp.StatusCode >= errorStatusCodeMin {
readBytes, err = ioutil.ReadAll(getResp.Body)
if err != nil { if err != nil {
return err // We couldn't read the output. Oh well; generate the appropriate error type anyway.
return &Meta{
Code: HTTPStatusCode(getResp.StatusCode),
}
} }
// Check Status Code is 1XX or 2XX resp := newJSONResponse(nil)
if getResp.StatusCode/100 > 2 { if err = json.Unmarshal(readBytes, &resp); err != nil {
return fmt.Errorf("%s: %s", getResp.Status, string(bytes)) // 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)
} }

View File

@ -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"
@ -34,7 +36,7 @@ func (s *ClientSuite) TestClient_do_PostContentType() {
req, err := http.NewRequest("POST", "", nil) req, err := http.NewRequest("POST", "", 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{}{}))
s.Assert().EqualValues(req.Header.Get("Content-Type"), "application/json") s.Assert().EqualValues(req.Header.Get("Content-Type"), "application/json")
} }
@ -42,14 +44,14 @@ 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) {

View File

@ -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]
} }

View File

@ -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 (

View File

@ -1,7 +1,9 @@
// 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"
"fmt" "fmt"
"net/http" "net/http"
@ -11,7 +13,7 @@ import (
// 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"
@ -21,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"`
@ -63,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())
@ -82,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
} }
@ -92,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.
@ -109,18 +107,8 @@ 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)
m.SourceGUID = uuid.New().String() m.SourceGUID = uuid.New().String()
@ -141,9 +129,9 @@ func (c *Client) CreateDirectMessage(m *Message) (*Message, error) {
} }
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
} }

View File

@ -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()

View File

@ -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,
}) })

View File

@ -1,9 +1,8 @@
package main package main
import ( import (
"context"
"fmt" "fmt"
"github.com/densestvoid/groupme"
) )
// This is not a real Bot ID. Please find yours by logging // This is not a real Bot ID. Please find yours by logging
@ -16,5 +15,5 @@ const botID = "0123456789ABCDEF"
func main() { func main() {
// Create a new client with your auth token // Create a new client with your auth token
client := groupme.NewClient("") client := groupme.NewClient("")
fmt.Println(client.PostBotMessage(botID, "Your message here!", nil)) fmt.Println(client.PostBotMessage(context.Background(), botID, "Your message here!", nil))
} }

View 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
View File

@ -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
View File

@ -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=

View File

@ -1,7 +1,9 @@
// 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" "errors"
"fmt" "fmt"
@ -11,7 +13,7 @@ import (
// 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"
@ -29,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 {
@ -51,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 {
@ -84,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
@ -106,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
} }
@ -114,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
} }
@ -136,7 +138,7 @@ func (c *Client) FormerGroups() ([]*Group, error) {
return resp, nil return resp, nil
} }
///// Show ///// /*/// Show ///*/
/* /*
ShowGroup - ShowGroup -
@ -144,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)
@ -155,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
} }
@ -163,16 +166,16 @@ 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) {
URL := fmt.Sprintf(c.endpointBase + createGroupEndpoint) URL := fmt.Sprintf(c.endpointBase + createGroupEndpoint)
jsonBytes, err := json.Marshal(&gs) jsonBytes, err := json.Marshal(&gs)
@ -186,7 +189,7 @@ func (c *Client) CreateGroup(gs GroupSettings) (*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
} }
@ -194,18 +197,19 @@ 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)
jsonBytes, err := json.Marshal(&gs) jsonBytes, err := json.Marshal(&gs)
@ -219,7 +223,7 @@ func (c *Client) UpdateGroup(groupID ID, gs GroupSettings) (*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
} }
@ -227,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)
@ -247,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)
@ -270,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
} }
@ -278,7 +284,7 @@ func (c *Client) JoinGroup(groupID ID, shareToken string) (*Group, error) {
return &resp, nil return &resp, nil
} }
///// Rejoin ///// /*/// Rejoin ///*/
/* /*
RejoinGroup - RejoinGroup -
@ -286,9 +292,10 @@ 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) {
URL := fmt.Sprintf(c.endpointBase + rejoinGroupEndpoint) URL := fmt.Sprintf(c.endpointBase + rejoinGroupEndpoint)
var data = struct { var data = struct {
@ -308,7 +315,7 @@ func (c *Client) RejoinGroup(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
} }
@ -316,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.
@ -330,7 +335,7 @@ 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) {
URL := fmt.Sprintf(c.endpointBase + changeGroupOwnerEndpoint) URL := fmt.Sprintf(c.endpointBase + changeGroupOwnerEndpoint)
var data = struct { var data = struct {
@ -353,7 +358,7 @@ func (c *Client) ChangeGroupOwner(reqs ChangeOwnerRequest) (ChangeOwnerResult, e
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
} }
@ -365,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]
} }
@ -405,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 {

View File

@ -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,25 +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) 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",
@ -69,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",
@ -99,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()

62
json.go
View File

@ -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 (
@ -53,7 +54,7 @@ type MessagePreview struct {
// 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,8 +76,8 @@ 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
@ -93,8 +94,8 @@ type Member struct {
Email string `json:"email,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
@ -106,10 +107,12 @@ type Message struct {
UserID ID `json:"user_id,omitempty"` UserID ID `json:"user_id,omitempty"`
BotID ID `json:"bot_id,omitempty"` BotID ID `json:"bot_id,omitempty"`
SenderID ID `json:"sender_id,omitempty"` SenderID ID `json:"sender_id,omitempty"`
SenderType SenderType `json:"sender_type,omitempty"` SenderType senderType `json:"sender_type,omitempty"`
System bool `json:"system,omitempty"` System bool `json:"system,omitempty"`
Name string `json:"name,omitempty"` Name string `json:"name,omitempty"`
RecipientID ID `json:"recipient_id,omitempty"` RecipientID ID `json:"recipient_id,omitempty"`
//ChatID - over push ConversationID seems to be called ChatID
ChatID ID `json:"chat_id,omitempty"`
ConversationID ID `json:"conversation_id,omitempty"` ConversationID ID `json:"conversation_id,omitempty"`
AvatarURL string `json:"avatar_url,omitempty"` AvatarURL string `json:"avatar_url,omitempty"`
// Maximum length of 1000 characters // Maximum length of 1000 characters
@ -120,44 +123,47 @@ type Message struct {
Attachments []*Attachment `json:"attachments,omitempty"` 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,omitempty"` Type attachmentType `json:"type,omitempty"`
Loci [][]int `json:"loci,omitempty"` Loci [][]int `json:"loci,omitempty"`
UserIDs []ID `json:"user_ids,omitempty"` UserIDs []ID `json:"user_ids,omitempty"`
URL string `json:"url,omitempty"` URL string `json:"url,omitempty"`
FileID string `json:"file_id,omitempty"`
VideoPreviewURL string `json:"preview_url,omitempty"`
Name string `json:"name,omitempty"` Name string `json:"name,omitempty"`
Latitude string `json:"lat,omitempty"` Latitude string `json:"lat,omitempty"`
Longitude string `json:"lng,omitempty"` Longitude string `json:"lng,omitempty"`
Placeholder string `json:"placeholder,omitempty"` Placeholder string `json:"placeholder,omitempty"`
Charmap [][]int `json:"charmap,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
@ -173,8 +179,8 @@ type User struct {
SMS bool `json:"sms,omitempty"` 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,
@ -187,10 +193,11 @@ type Chat struct {
OtherUser User `json:"other_user,omitempty"` 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,omitempty"` BotID ID `json:"bot_id,omitempty"`
GroupID ID `json:"group_id,omitempty"` GroupID ID `json:"group_id,omitempty"`
@ -200,10 +207,11 @@ type Bot struct {
DMNotification bool `json:"dm_notification,omitempty"` 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,omitempty"` UserID ID `json:"user_id,omitempty"`
BlockedUserID ID `json:"blocked_user_id,omitempty"` BlockedUserID ID `json:"blocked_user_id,omitempty"`

View File

@ -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 (

View File

@ -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
} }

View File

@ -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()

View File

@ -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)
} }

View File

@ -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()

View File

@ -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())
} }

View File

@ -1,7 +1,9 @@
// 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"
"fmt" "fmt"
"net/http" "net/http"
@ -9,7 +11,7 @@ import (
// 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,7 +49,7 @@ 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)
var data = struct { var data = struct {
@ -69,7 +72,7 @@ func (c *Client) AddMembers(groupID ID, members ...*Member) (string, error) {
ResultsID string `json:"results_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
} }
@ -77,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 -
@ -92,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)
@ -107,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
} }
@ -115,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 -
@ -125,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)
@ -136,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 -
@ -147,7 +152,7 @@ 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)
type Nickname struct { type Nickname struct {
@ -171,7 +176,7 @@ func (c *Client) UpdateMember(groupID ID, nickname string) (*Member, error) {
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
} }

View File

@ -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)
} }

View File

@ -1,7 +1,9 @@
// 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"
"fmt" "fmt"
"net/http" "net/http"
@ -12,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"
@ -21,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
@ -39,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"`
@ -51,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 {
@ -111,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
} }
@ -119,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.
@ -137,19 +124,8 @@ 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)
m.SourceGUID = uuid.New().String() m.SourceGUID = uuid.New().String()
@ -172,7 +148,7 @@ func (c *Client) CreateMessage(groupID ID, m *Message) (*Message, error) {
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
} }

View File

@ -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
View 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
View 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, "")
}
}
}
}

View File

@ -1,7 +1,9 @@
// 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"
"fmt" "fmt"
"net/http" "net/http"
@ -9,7 +11,7 @@ import (
// 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,13 +31,14 @@ 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 {
URL := fmt.Sprintf(c.endpointBase + createSMSModeEndpoint) URL := fmt.Sprintf(c.endpointBase + createSMSModeEndpoint)
var data = struct { var data = struct {
@ -56,7 +59,7 @@ func (c *Client) CreateSMSMode(duration int, registrationID *ID) error {
return err return err
} }
err = c.doWithAuthToken(httpReq, nil) err = c.doWithAuthToken(ctx, httpReq, nil, authToken)
if err != nil { if err != nil {
return err return err
} }
@ -71,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)
@ -79,5 +82,5 @@ func (c *Client) DeleteSMSMode() error {
return err return err
} }
return c.doWithAuthToken(httpReq, nil) return c.doWithAuthToken(ctx, httpReq, nil, authToken)
} }

View File

@ -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()

View File

@ -1,7 +1,9 @@
// 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"
"fmt" "fmt"
"net/http" "net/http"
@ -9,7 +11,7 @@ import (
// 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"
@ -19,7 +21,7 @@ const (
updateMyUserEndpoint = usersEndpointRoot + "/update" // POST updateMyUserEndpoint = usersEndpointRoot + "/update" // POST
) )
////////// API Requests ////////// /*//////// API Requests ////////*/
// Me // Me
@ -29,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)
@ -40,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
} }
@ -48,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/....)
@ -64,11 +66,11 @@ 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)
jsonBytes, err := json.Marshal(&us) jsonBytes, err := json.Marshal(&us)
@ -82,7 +84,7 @@ func (c *Client) UpdateMyUser(us UserSettings) (*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
} }

View File

@ -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()