real time push basics
This commit is contained in:
parent
6d42a230d1
commit
784cfe93c1
81
examples/real_time_updates/main.go
Normal file
81
examples/real_time_updates/main.go
Normal file
@ -0,0 +1,81 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
|
||||
"github.com/densestvoid/groupme"
|
||||
)
|
||||
|
||||
// This is not a real token. Please find yours by logging
|
||||
// into the GroupMe development website: https://dev.groupme.com/
|
||||
var authorizationToken = "ABCD"
|
||||
|
||||
// A short program that gets the gets the first 5 groups
|
||||
// the user is part of, and then the first 10 messages of
|
||||
// the first group in that list
|
||||
func main() {
|
||||
// Create a new client with your auth token
|
||||
client := groupme.NewClient(authorizationToken)
|
||||
|
||||
// Get the groups your user is part of
|
||||
groups, err := client.IndexGroups(
|
||||
context.Background(),
|
||||
&groupme.GroupsQuery{
|
||||
Page: 0,
|
||||
PerPage: 5,
|
||||
Omit: "memberships",
|
||||
},
|
||||
)
|
||||
|
||||
if err != nil {
|
||||
fmt.Println(err)
|
||||
return
|
||||
}
|
||||
|
||||
// fmt.Println(groups)
|
||||
|
||||
// Get first 10 messages of the first group
|
||||
if len(groups) == 0 {
|
||||
fmt.Println("No groups")
|
||||
}
|
||||
|
||||
// messages, err := client.IndexMessages(context.Background(), groups[0].ID, &groupme.IndexMessagesQuery{
|
||||
// Limit: 10,
|
||||
// })
|
||||
|
||||
// if err != nil {
|
||||
// fmt.Println(err)
|
||||
// }
|
||||
|
||||
// fmt.Println(messages)
|
||||
p := client.NewPushSubscription(context.Background())
|
||||
go p.Listen(context.Background())
|
||||
|
||||
client = groupme.NewClient(authorizationToken)
|
||||
|
||||
a, _ := client.MyUser(context.Background())
|
||||
|
||||
p.SubscribeToUser(context.Background(), a.ID, authorizationToken)
|
||||
|
||||
authorizationToken = "BCDF"
|
||||
client = groupme.NewClient(authorizationToken)
|
||||
a, _ = client.MyUser(context.Background())
|
||||
|
||||
p.SubscribeToUser(context.Background(), a.ID, authorizationToken)
|
||||
|
||||
for {
|
||||
select {
|
||||
case msg := <-p.MessageChannel:
|
||||
println(msg.Text)
|
||||
break
|
||||
|
||||
case like := <-p.LikeChannel:
|
||||
println("Liked")
|
||||
println(like.Message.ID.String())
|
||||
println(like.Message.Text)
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
}
|
3
go.mod
3
go.mod
@ -2,8 +2,11 @@ module github.com/densestvoid/groupme
|
||||
|
||||
go 1.15
|
||||
|
||||
replace github.com/karmanyaahm/wray => ../wray
|
||||
|
||||
require (
|
||||
github.com/google/uuid v1.2.0
|
||||
github.com/gorilla/mux v1.8.0
|
||||
github.com/karmanyaahm/wray v0.0.0-20160519030252-f36984f6648c
|
||||
github.com/stretchr/testify v1.7.0
|
||||
)
|
||||
|
14
go.sum
14
go.sum
@ -1,15 +1,29 @@
|
||||
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/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
||||
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/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/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/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
|
||||
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
|
||||
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 h1:4G4v2dO3VZwixGIRoQ5Lfboy6nUhCyYzaqnIAPPhYs4=
|
||||
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/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/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
|
||||
gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c h1:dUUwHk2QECo/6vqA44rthZ8ie2QXMNeKRTHCNY2nXvo=
|
||||
|
180
real_time.go
Normal file
180
real_time.go
Normal file
@ -0,0 +1,180 @@
|
||||
package groupme
|
||||
|
||||
import (
|
||||
"context"
|
||||
"encoding/json"
|
||||
"errors"
|
||||
"log"
|
||||
"sync"
|
||||
"time"
|
||||
|
||||
"github.com/karmanyaahm/wray"
|
||||
)
|
||||
|
||||
const (
|
||||
pushServer = "https://push.groupme.com/faye"
|
||||
userChannel = "/user/"
|
||||
groupChannel = "/group/"
|
||||
handshakeChannel = "/meta/handshake"
|
||||
connectChannel = "/meta/connect"
|
||||
subscribeChannel = "/meta/subscribe"
|
||||
)
|
||||
|
||||
var concur = sync.Mutex{}
|
||||
var token string
|
||||
|
||||
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...)
|
||||
}
|
||||
|
||||
func init() {
|
||||
wray.RegisterTransports([]wray.Transport{&wray.HTTPTransport{}})
|
||||
}
|
||||
|
||||
//LikeEvent returns events as they happen from GroupMe
|
||||
type LikeEvent struct {
|
||||
Message Message
|
||||
}
|
||||
|
||||
//PushSubscription manages real time subscription
|
||||
type PushSubscription struct {
|
||||
channel chan wray.Message
|
||||
//Channel to return things
|
||||
MessageChannel chan Message
|
||||
LikeChannel chan LikeEvent
|
||||
fayeClient *wray.FayeClient
|
||||
}
|
||||
|
||||
//NewPushSubscription creates and returns a push subscription object
|
||||
func NewPushSubscription(context context.Context) PushSubscription {
|
||||
|
||||
r := PushSubscription{
|
||||
channel: make(chan wray.Message),
|
||||
MessageChannel: make(chan Message),
|
||||
LikeChannel: make(chan LikeEvent),
|
||||
}
|
||||
|
||||
return r
|
||||
}
|
||||
|
||||
//Listen connects to GroupMe. Runs in Goroutine.
|
||||
func (r *PushSubscription) Listen(context context.Context) {
|
||||
r.fayeClient = wray.NewFayeClient(pushServer)
|
||||
|
||||
r.fayeClient.SetLogger(fayeLogger{})
|
||||
|
||||
r.fayeClient.AddExtension(&authExtension{})
|
||||
//r.fayeClient.AddExtension(r.fayeClient) //verbose output
|
||||
|
||||
go r.fayeClient.Listen()
|
||||
|
||||
for {
|
||||
msg := <-r.channel
|
||||
data := msg.Data()
|
||||
content, _ := data["subject"]
|
||||
contentType := data["type"].(string)
|
||||
|
||||
switch contentType {
|
||||
case "line.create":
|
||||
b, _ := json.Marshal(content)
|
||||
|
||||
out := Message{}
|
||||
json.Unmarshal(b, &out)
|
||||
//fmt.Printf("%+v\n", out) //TODO
|
||||
|
||||
r.MessageChannel <- out
|
||||
break
|
||||
case "like.create":
|
||||
b, _ := json.Marshal(content.(map[string]interface{})["line"])
|
||||
|
||||
out := Message{}
|
||||
//log.Println(string(b))
|
||||
err := json.Unmarshal(b, &out)
|
||||
if err != nil {
|
||||
log.Println(err)
|
||||
}
|
||||
outt := LikeEvent{Message: out}
|
||||
//fmt.Printf("Like on %+v \n", outt.Message)
|
||||
|
||||
r.LikeChannel <- outt
|
||||
break
|
||||
case "ping":
|
||||
break
|
||||
default: //TODO: see if any other types are returned
|
||||
if len(contentType) == 0 || content == nil {
|
||||
break
|
||||
}
|
||||
log.Println(contentType)
|
||||
log.Fatalln(data)
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
//SubscribeToUser to users
|
||||
func (r *PushSubscription) SubscribeToUser(context context.Context, userID ID, authToken string) error {
|
||||
concur.Lock()
|
||||
defer concur.Unlock()
|
||||
|
||||
if r.fayeClient == nil {
|
||||
return errors.New("Not Listening") //TODO: Proper error
|
||||
}
|
||||
|
||||
token = authToken
|
||||
r.fayeClient.WaitSubscribe(userChannel+userID.String(), r.channel)
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
//SubscribeToGroup to groups for typing notification
|
||||
func (r *PushSubscription) SubscribeToGroup(context context.Context, groupID ID, authToken string) error {
|
||||
concur.Lock()
|
||||
defer concur.Unlock()
|
||||
if r.fayeClient == nil {
|
||||
return errors.New("Not Listening") //TODO: Proper error
|
||||
}
|
||||
|
||||
token = authToken
|
||||
r.fayeClient.WaitSubscribe(groupChannel+groupID.String(), r.channel)
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// Stop listening to GroupMe after completing all other actions scheduled first
|
||||
func (r *PushSubscription) Stop(context context.Context) {
|
||||
concur.Lock()
|
||||
defer concur.Unlock()
|
||||
|
||||
//TODO: stop listening
|
||||
}
|
||||
|
||||
type authExtension struct {
|
||||
}
|
||||
|
||||
// In does nothing in this extension, but is needed to satisy the interface
|
||||
func (e *authExtension) In(msg wray.Message) {
|
||||
if len(msg.Error()) > 0 {
|
||||
log.Fatalln(msg.Error())
|
||||
}
|
||||
}
|
||||
|
||||
// Out adds the authentication token to the messages ext field
|
||||
func (e *authExtension) Out(msg wray.Message) {
|
||||
if msg.Channel() == subscribeChannel {
|
||||
ext := msg.Ext()
|
||||
ext["access_token"] = token
|
||||
ext["timestamp"] = time.Now().Unix()
|
||||
}
|
||||
}
|
Loading…
Reference in New Issue
Block a user