Compare commits
15 Commits
Author | SHA1 | Date | |
---|---|---|---|
c2dbe7021e | |||
6eba33b3be | |||
7961d30a51 | |||
12931fb275 | |||
65299d9606 | |||
aa216ed4af | |||
28b2d85660 | |||
8dd5993481 | |||
042fb9a951 | |||
b6715c2375 | |||
c5d6c8a29c | |||
d6a8b0818f | |||
da7bc977a3 | |||
eeb2f88b97 | |||
784cfe93c1 |
20
chats_api.go
20
chats_api.go
@ -53,3 +53,23 @@ func (c *Client) IndexChats(ctx context.Context, req *IndexChatsQuery) ([]*Chat,
|
|||||||
|
|
||||||
return resp, nil
|
return resp, nil
|
||||||
}
|
}
|
||||||
|
func (c *Client) IndexRelations(ctx context.Context) ([]*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)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
return resp, nil
|
||||||
|
}
|
||||||
|
185
examples/real_time_updates/main.go
Normal file
185
examples/real_time_updates/main.go
Normal file
@ -0,0 +1,185 @@
|
|||||||
|
package main
|
||||||
|
|
||||||
|
import (
|
||||||
|
"context"
|
||||||
|
"fmt"
|
||||||
|
"log"
|
||||||
|
|
||||||
|
"github.com/densestvoid/groupme"
|
||||||
|
"github.com/karmanyaahm/wray"
|
||||||
|
)
|
||||||
|
|
||||||
|
// This is not a real token. Please find yours by logging
|
||||||
|
// into the GroupMe development website: https://dev.groupme.com/
|
||||||
|
|
||||||
|
var authorizationToken = "ABCD"
|
||||||
|
|
||||||
|
//This adapts your faye library to an interface compatible with this library
|
||||||
|
type FayeClient struct {
|
||||||
|
*wray.FayeClient
|
||||||
|
}
|
||||||
|
|
||||||
|
func (fc FayeClient) WaitSubscribe(channel string, msgChannel chan groupme.PushMessage) {
|
||||||
|
c_new := make(chan wray.Message)
|
||||||
|
fc.FayeClient.WaitSubscribe(channel, c_new)
|
||||||
|
//converting between types because channels don't support interfaces well
|
||||||
|
go func() {
|
||||||
|
for i := range c_new {
|
||||||
|
msgChannel <- i
|
||||||
|
}
|
||||||
|
}()
|
||||||
|
}
|
||||||
|
|
||||||
|
//for authentication, specific implementation will vary based on faye library
|
||||||
|
type AuthExt struct{}
|
||||||
|
|
||||||
|
func (a *AuthExt) In(wray.Message) {}
|
||||||
|
func (a *AuthExt) Out(m wray.Message) {
|
||||||
|
groupme.OutMsgProc(m)
|
||||||
|
}
|
||||||
|
|
||||||
|
//specific to faye library
|
||||||
|
type fayeLogger struct{}
|
||||||
|
|
||||||
|
func (l fayeLogger) Infof(f string, a ...interface{}) {
|
||||||
|
log.Printf("[INFO] : "+f, a...)
|
||||||
|
}
|
||||||
|
func (l fayeLogger) Errorf(f string, a ...interface{}) {
|
||||||
|
log.Printf("[ERROR] : "+f, a...)
|
||||||
|
}
|
||||||
|
func (l fayeLogger) Debugf(f string, a ...interface{}) {
|
||||||
|
log.Printf("[DEBUG] : "+f, a...)
|
||||||
|
}
|
||||||
|
func (l fayeLogger) Warnf(f string, a ...interface{}) {
|
||||||
|
log.Printf("[WARN] : "+f, a...)
|
||||||
|
}
|
||||||
|
|
||||||
|
// A short program that subscribes to 2 groups and 2 direct chats
|
||||||
|
// and prints out all recognized events in those
|
||||||
|
func main() {
|
||||||
|
|
||||||
|
//Create and initialize fayeclient
|
||||||
|
fc := FayeClient{wray.NewFayeClient(groupme.PushServer)}
|
||||||
|
fc.SetLogger(fayeLogger{})
|
||||||
|
fc.AddExtension(&AuthExt{})
|
||||||
|
//for additional logging uncomment the following line
|
||||||
|
//fc.AddExtension(fc.FayeClient)
|
||||||
|
|
||||||
|
//create push subscription and start listening
|
||||||
|
p := groupme.NewPushSubscription(context.Background())
|
||||||
|
go p.StartListening(context.TODO(), fc)
|
||||||
|
|
||||||
|
// Create a new client with your auth token
|
||||||
|
client := groupme.NewClient(authorizationToken)
|
||||||
|
User, _ := client.MyUser(context.Background())
|
||||||
|
//Subscribe to get messages and events for the specific user
|
||||||
|
err := p.SubscribeToUser(context.Background(), User.ID, authorizationToken)
|
||||||
|
if err != nil {
|
||||||
|
log.Fatal(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
//handles (in this case prints) all messages
|
||||||
|
p.AddFullHandler(Handler{User: User})
|
||||||
|
|
||||||
|
// Get the groups your user is part of
|
||||||
|
groups, err := client.IndexGroups(
|
||||||
|
context.Background(),
|
||||||
|
&groupme.GroupsQuery{
|
||||||
|
Page: 0,
|
||||||
|
PerPage: 2,
|
||||||
|
Omit: "memberships",
|
||||||
|
})
|
||||||
|
|
||||||
|
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)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
//get chats your user is part of
|
||||||
|
chats, err := client.IndexChats(context.Background(),
|
||||||
|
&groupme.IndexChatsQuery{
|
||||||
|
Page: 0,
|
||||||
|
PerPage: 2,
|
||||||
|
})
|
||||||
|
//subscribe to all those chats
|
||||||
|
for _, j := range chats {
|
||||||
|
err = p.SubscribeToDM(context.TODO(), j.LastMessage.ConversationID, authorizationToken)
|
||||||
|
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)
|
||||||
|
}
|
1
go.mod
1
go.mod
@ -5,5 +5,6 @@ go 1.15
|
|||||||
require (
|
require (
|
||||||
github.com/google/uuid v1.2.0
|
github.com/google/uuid v1.2.0
|
||||||
github.com/gorilla/mux v1.8.0
|
github.com/gorilla/mux v1.8.0
|
||||||
|
github.com/karmanyaahm/wray v0.0.0-20210303233435-756d58657c14
|
||||||
github.com/stretchr/testify v1.7.0
|
github.com/stretchr/testify v1.7.0
|
||||||
)
|
)
|
||||||
|
17
go.sum
17
go.sum
@ -1,15 +1,30 @@
|
|||||||
|
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/google/uuid v1.2.0 h1:qJYtXnJRWmpe7m/3XlyhrsLrEURqHRM2kxzoxXqyUDs=
|
github.com/google/uuid v1.2.0 h1:qJYtXnJRWmpe7m/3XlyhrsLrEURqHRM2kxzoxXqyUDs=
|
||||||
github.com/google/uuid v1.2.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
|
github.com/google/uuid v1.2.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
|
||||||
|
github.com/gopherjs/gopherjs v0.0.0-20181017120253-0766667cb4d1 h1:EGx4pi6eqNxGaHF6qqu48+N2wcFQ5qg5FXgOdqsJ5d8=
|
||||||
|
github.com/gopherjs/gopherjs v0.0.0-20181017120253-0766667cb4d1/go.mod h1:wJfORRmW1u3UXTncJ5qlYoELFm8eSnnEO6hX4iZ3EWY=
|
||||||
github.com/gorilla/mux v1.8.0 h1:i40aqfkR1h2SlN9hojwV5ZA91wcXFOvkdNIeFDP5koI=
|
github.com/gorilla/mux v1.8.0 h1:i40aqfkR1h2SlN9hojwV5ZA91wcXFOvkdNIeFDP5koI=
|
||||||
github.com/gorilla/mux v1.8.0/go.mod h1:DVbg23sWSpFRCP0SfiEN6jmj59UnW/n46BH5rLB71So=
|
github.com/gorilla/mux v1.8.0/go.mod h1:DVbg23sWSpFRCP0SfiEN6jmj59UnW/n46BH5rLB71So=
|
||||||
|
github.com/jtolds/gls v4.20.0+incompatible h1:xdiiI2gbIgH/gLH7ADydsJ1uDOEzR8yvV7C0MuV77Wo=
|
||||||
|
github.com/jtolds/gls v4.20.0+incompatible/go.mod h1:QJZ7F/aHp+rZTRtaJ1ow/lLfFfVYBRgL+9YlvaHOwJU=
|
||||||
|
github.com/karmanyaahm/wray v0.0.0-20210303233435-756d58657c14 h1:NrATjZKvkY+ojL8FXTWa3fQ+wihFrAxLNE6T+wOkIcY=
|
||||||
|
github.com/karmanyaahm/wray v0.0.0-20210303233435-756d58657c14/go.mod h1:ysD86MIEevmAkdfdg5s6Qt3I07RN6fvMAyna7jCGG2o=
|
||||||
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/stretchr/objx v0.1.0 h1:4G4v2dO3VZwixGIRoQ5Lfboy6nUhCyYzaqnIAPPhYs4=
|
github.com/smartystreets/assertions v0.0.0-20180927180507-b2de0cb4f26d h1:zE9ykElWQ6/NYmHa3jpm/yHnI4xSofP+UP6SpjHcSeM=
|
||||||
|
github.com/smartystreets/assertions v0.0.0-20180927180507-b2de0cb4f26d/go.mod h1:OnSkiWE9lh6wB0YB77sQom3nweQdgAjqCqsofrRNTgc=
|
||||||
|
github.com/smartystreets/goconvey v1.6.4 h1:fv0U8FUIMPNf1L9lnHLvLhgicrIVChEkdzIKYqbNC9s=
|
||||||
|
github.com/smartystreets/goconvey v1.6.4/go.mod h1:syvi0/a8iFYH4r/RixwvyeAJjdLS9QV7WQ/tjFTllLA=
|
||||||
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.7.0 h1:nwc3DEeHmmLAfoZucVR881uASk0Mfjw8xYJ99tb5CcY=
|
github.com/stretchr/testify v1.7.0 h1:nwc3DEeHmmLAfoZucVR881uASk0Mfjw8xYJ99tb5CcY=
|
||||||
github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
|
github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
|
||||||
|
golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
|
||||||
|
golang.org/x/net v0.0.0-20190311183353-d8887717615a/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
|
||||||
|
golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
||||||
|
golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
|
||||||
|
golang.org/x/tools v0.0.0-20190328211700-ab21143f2384/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs=
|
||||||
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405 h1:yhCVgyC4o1eVCa2tZl7eS0r+SDo693bJlVdllGtEeKM=
|
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405 h1:yhCVgyC4o1eVCa2tZl7eS0r+SDo693bJlVdllGtEeKM=
|
||||||
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.v3 v3.0.0-20200313102051-9f266ea9e77c h1:dUUwHk2QECo/6vqA44rthZ8ie2QXMNeKRTHCNY2nXvo=
|
gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c h1:dUUwHk2QECo/6vqA44rthZ8ie2QXMNeKRTHCNY2nXvo=
|
||||||
|
2
json.go
2
json.go
@ -111,6 +111,8 @@ type Message struct {
|
|||||||
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
|
||||||
|
208
real_time.go
Normal file
208
real_time.go
Normal file
@ -0,0 +1,208 @@
|
|||||||
|
package groupme
|
||||||
|
|
||||||
|
import (
|
||||||
|
"context"
|
||||||
|
"errors"
|
||||||
|
"log"
|
||||||
|
"strings"
|
||||||
|
"sync"
|
||||||
|
"time"
|
||||||
|
|
||||||
|
"github.com/karmanyaahm/wray"
|
||||||
|
)
|
||||||
|
|
||||||
|
const (
|
||||||
|
PushServer = "https://push.groupme.com/faye"
|
||||||
|
userChannel = "/user/"
|
||||||
|
groupChannel = "/group/"
|
||||||
|
dmChannel = "/direct_message/"
|
||||||
|
subscribeChannel = "/meta/subscribe"
|
||||||
|
)
|
||||||
|
|
||||||
|
var (
|
||||||
|
ErrHandlerNotFound = errors.New("Handler not found")
|
||||||
|
ErrListenerNotStarted = errors.New("GroupMe listener not started")
|
||||||
|
)
|
||||||
|
|
||||||
|
var concur = sync.Mutex{}
|
||||||
|
var token string
|
||||||
|
|
||||||
|
func init() {
|
||||||
|
wray.RegisterTransports([]wray.Transport{&wray.HTTPTransport{}})
|
||||||
|
}
|
||||||
|
|
||||||
|
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)
|
||||||
|
}
|
||||||
|
|
||||||
|
type PushMessage interface {
|
||||||
|
Channel() string
|
||||||
|
Data() map[string]interface{}
|
||||||
|
Ext() map[string]interface{}
|
||||||
|
Error() string
|
||||||
|
}
|
||||||
|
|
||||||
|
type FayeClient interface {
|
||||||
|
//Listen starts a blocking listen loop
|
||||||
|
Listen()
|
||||||
|
//WaitSubscribe is a blocking/synchronous subscribe method
|
||||||
|
WaitSubscribe(channel string, msgChannel chan PushMessage)
|
||||||
|
}
|
||||||
|
|
||||||
|
//PushSubscription manages real time subscription
|
||||||
|
type PushSubscription struct {
|
||||||
|
channel chan PushMessage
|
||||||
|
fayeClient FayeClient
|
||||||
|
handlers []Handler
|
||||||
|
LastConnected int64
|
||||||
|
}
|
||||||
|
|
||||||
|
//NewPushSubscription creates and returns a push subscription object
|
||||||
|
func NewPushSubscription(context context.Context) PushSubscription {
|
||||||
|
|
||||||
|
r := PushSubscription{
|
||||||
|
channel: make(chan PushMessage),
|
||||||
|
}
|
||||||
|
|
||||||
|
return r
|
||||||
|
}
|
||||||
|
|
||||||
|
func (r *PushSubscription) AddHandler(h Handler) {
|
||||||
|
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) {
|
||||||
|
r.handlers = append(r.handlers, h)
|
||||||
|
}
|
||||||
|
|
||||||
|
var RealTimeHandlers map[string]func(r *PushSubscription, channel string, data ...interface{})
|
||||||
|
var RealTimeSystemHandlers map[string]func(r *PushSubscription, channel string, id ID, rawData []byte)
|
||||||
|
|
||||||
|
//Listen connects to GroupMe. Runs in Goroutine.
|
||||||
|
func (r *PushSubscription) StartListening(context context.Context, client FayeClient) {
|
||||||
|
r.fayeClient = client
|
||||||
|
|
||||||
|
go r.fayeClient.Listen()
|
||||||
|
|
||||||
|
go func() {
|
||||||
|
for msg := range r.channel {
|
||||||
|
r.LastConnected = time.Now().Unix()
|
||||||
|
data := msg.Data()
|
||||||
|
content := data["subject"]
|
||||||
|
contentType := data["type"].(string)
|
||||||
|
channel := msg.Channel()
|
||||||
|
|
||||||
|
handler, ok := RealTimeHandlers[contentType]
|
||||||
|
if !ok {
|
||||||
|
if contentType == "ping" ||
|
||||||
|
len(contentType) == 0 ||
|
||||||
|
content == nil {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
log.Println("Unable to handle GroupMe message type", contentType)
|
||||||
|
}
|
||||||
|
|
||||||
|
handler(r, channel, content)
|
||||||
|
}
|
||||||
|
}()
|
||||||
|
}
|
||||||
|
|
||||||
|
//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.fayeClient == nil {
|
||||||
|
return ErrListenerNotStarted
|
||||||
|
}
|
||||||
|
|
||||||
|
token = authToken
|
||||||
|
r.fayeClient.WaitSubscribe(prefix+groupID.String(), r.channel)
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
//Connected check if connected
|
||||||
|
func (r *PushSubscription) Connected() bool {
|
||||||
|
return r.LastConnected+30 >= time.Now().Unix()
|
||||||
|
}
|
||||||
|
|
||||||
|
// Out adds the authentication token to the messages ext field
|
||||||
|
func OutMsgProc(msg PushMessage) {
|
||||||
|
if msg.Channel() == subscribeChannel {
|
||||||
|
ext := msg.Ext()
|
||||||
|
ext["access_token"] = token
|
||||||
|
ext["timestamp"] = time.Now().Unix()
|
||||||
|
}
|
||||||
|
}
|
239
real_time_handler.go
Normal file
239
real_time_handler.go
Normal file
@ -0,0 +1,239 @@
|
|||||||
|
package groupme
|
||||||
|
|
||||||
|
import (
|
||||||
|
"encoding/json"
|
||||||
|
"fmt"
|
||||||
|
"log"
|
||||||
|
"strconv"
|
||||||
|
)
|
||||||
|
|
||||||
|
func init() {
|
||||||
|
|
||||||
|
RealTimeHandlers = make(map[string]func(r *PushSubscription, channel string, data ...interface{}))
|
||||||
|
|
||||||
|
//Base Handlers on user channel
|
||||||
|
RealTimeHandlers["direct_message.create"] = func(r *PushSubscription, channel 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, rawData)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, h := range r.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, data ...interface{}) { //should be an associated chatEvent
|
||||||
|
}
|
||||||
|
|
||||||
|
RealTimeHandlers["membership.create"] = func(r *PushSubscription, channel string, data ...interface{}) {
|
||||||
|
c, _ := data[0].(map[string]interface{})
|
||||||
|
id, _ := c["id"].(string)
|
||||||
|
|
||||||
|
for _, h := range r.handlers {
|
||||||
|
if h, ok := h.(HandlerMembership); ok {
|
||||||
|
h.HandleJoin(ID(id))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
//following are for each chat
|
||||||
|
RealTimeHandlers["favorite"] = func(r *PushSubscription, channel 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)
|
||||||
|
for _, h := range r.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, rawData []byte))
|
||||||
|
|
||||||
|
RealTimeSystemHandlers["membership.nickname_changed"] = func(r *PushSubscription, channel string, id ID, rawData []byte) {
|
||||||
|
thing := struct {
|
||||||
|
Name string
|
||||||
|
User struct {
|
||||||
|
ID int
|
||||||
|
}
|
||||||
|
}{}
|
||||||
|
_ = json.Unmarshal(rawData, &thing)
|
||||||
|
|
||||||
|
for _, h := range r.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, rawData []byte) {
|
||||||
|
content := struct {
|
||||||
|
AvatarURL string `json:"avatar_url"`
|
||||||
|
User struct {
|
||||||
|
ID int
|
||||||
|
}
|
||||||
|
}{}
|
||||||
|
_ = json.Unmarshal(rawData, &content)
|
||||||
|
|
||||||
|
for _, h := range r.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, rawData []byte) {
|
||||||
|
data := struct {
|
||||||
|
Added []Member `json:"added_users"`
|
||||||
|
}{}
|
||||||
|
_ = json.Unmarshal(rawData, &data)
|
||||||
|
for _, h := range r.handlers {
|
||||||
|
if h, ok := h.(HandleMembers); ok {
|
||||||
|
h.HandleMembers(id, data.Added, true)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
RealTimeSystemHandlers["membership.notifications.removed"] = func(r *PushSubscription, channel string, id ID, rawData []byte) {
|
||||||
|
data := struct {
|
||||||
|
Added Member `json:"removed_user"`
|
||||||
|
}{}
|
||||||
|
_ = json.Unmarshal(rawData, &data)
|
||||||
|
for _, h := range r.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, rawData []byte) {
|
||||||
|
|
||||||
|
data := struct {
|
||||||
|
Name string
|
||||||
|
}{}
|
||||||
|
_ = json.Unmarshal(rawData, &data)
|
||||||
|
|
||||||
|
for _, h := range r.handlers {
|
||||||
|
if h, ok := h.(HandleGroupName); ok {
|
||||||
|
h.HandleGroupName(id, data.Name)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
RealTimeSystemHandlers["group.name_change"] = func(r *PushSubscription, channel string, id ID, rawData []byte) {
|
||||||
|
|
||||||
|
data := struct {
|
||||||
|
Name string
|
||||||
|
}{}
|
||||||
|
_ = json.Unmarshal(rawData, &data)
|
||||||
|
|
||||||
|
for _, h := range r.handlers {
|
||||||
|
if h, ok := h.(HandleGroupName); ok {
|
||||||
|
h.HandleGroupName(id, data.Name)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
RealTimeSystemHandlers["group.topic_change"] = func(r *PushSubscription, channel string, id ID, rawData []byte) {
|
||||||
|
|
||||||
|
data := struct {
|
||||||
|
Topic string
|
||||||
|
}{}
|
||||||
|
_ = json.Unmarshal(rawData, &data)
|
||||||
|
|
||||||
|
for _, h := range r.handlers {
|
||||||
|
if h, ok := h.(HandleGroupTopic); ok {
|
||||||
|
h.HandleGroupTopic(id, data.Topic)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
RealTimeSystemHandlers["group.avatar_change"] = func(r *PushSubscription, channel string, id ID, rawData []byte) {
|
||||||
|
data := struct {
|
||||||
|
AvatarURL string `json:"avatar_url"`
|
||||||
|
}{}
|
||||||
|
_ = json.Unmarshal(rawData, &data)
|
||||||
|
|
||||||
|
for _, h := range r.handlers {
|
||||||
|
if h, ok := h.(HandleGroupAvatar); ok {
|
||||||
|
h.HandleGroupAvatar(id, data.AvatarURL)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
RealTimeSystemHandlers["group.like_icon_set"] = func(r *PushSubscription, channel string, id ID, 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)
|
||||||
|
|
||||||
|
for _, h := range r.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, rawData []byte) {
|
||||||
|
for _, h := range r.handlers {
|
||||||
|
if h, ok := h.(HandleGroupLikeIcon); ok {
|
||||||
|
h.HandleLikeIcon(id, 0, 0, "")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
Reference in New Issue
Block a user