Inital implementation for callback funcs handling Websocket events
This commit is contained in:
parent
a5d3e9378e
commit
e8b77083df
2 changed files with 132 additions and 76 deletions
40
session.go
40
session.go
|
@ -7,14 +7,42 @@ package discordgo
|
||||||
import "github.com/gorilla/websocket"
|
import "github.com/gorilla/websocket"
|
||||||
|
|
||||||
// A Session represents a connection to the Discord REST API.
|
// A Session represents a connection to the Discord REST API.
|
||||||
// Token : The authentication token returned from Discord
|
// token : The authentication token returned from Discord
|
||||||
// Debug : If set to ture debug logging will be displayed.
|
// Debug : If set to ture debug logging will be displayed.
|
||||||
type Session struct {
|
type Session struct {
|
||||||
Token string // Authentication token for this session
|
Token string // Authentication token for this session
|
||||||
Gateway string // Websocket Gateway for this session
|
Gateway string // Websocket Gateway for this session
|
||||||
Debug bool // Debug for printing JSON request/responses
|
Debug bool // Debug for printing JSON request/responses
|
||||||
Cache int // number in X to cache some responses
|
Cache int // number in X to cache some responses
|
||||||
Websocket *websocket.Conn // TODO: use this
|
|
||||||
|
// Settable Callback functions for Websocket Events
|
||||||
|
OnEvent func(*Session, Event) // should Event be *Event?
|
||||||
|
OnReady func(*Session, Ready)
|
||||||
|
OnMessageCreate func(*Session, Message)
|
||||||
|
OnTypingStart func(*Session, Event)
|
||||||
|
OnMessageAck func(*Session, Event)
|
||||||
|
OnMessageUpdate func(*Session, Event)
|
||||||
|
OnMessageDelete func(*Session, Event)
|
||||||
|
OnPresenceUpdate func(*Session, Event)
|
||||||
|
OnChannelCreate func(*Session, Event)
|
||||||
|
OnChannelUpdate func(*Session, Event)
|
||||||
|
OnChannelDelete func(*Session, Event)
|
||||||
|
OnGuildCreate func(*Session, Event)
|
||||||
|
OnGuildDelete func(*Session, Event)
|
||||||
|
OnGuildMemberAdd func(*Session, Event)
|
||||||
|
OnGuildMemberRemove func(*Session, Event)
|
||||||
|
OnGuildMemberDelete func(*Session, Event) // which is it?
|
||||||
|
OnGuildMemberUpdate func(*Session, Event)
|
||||||
|
OnGuildRoleCreate func(*Session, Event)
|
||||||
|
OnGuildRoleDelete func(*Session, Event)
|
||||||
|
OnGuildIntegrationsUpdate func(*Session, Event)
|
||||||
|
|
||||||
|
// OnMessageCreate func(Session, Event, Message)
|
||||||
|
// ^^ Any value to passing session, event, message?
|
||||||
|
// probably just the Message is all one would need.
|
||||||
|
// but having the sessin could be handy?
|
||||||
|
|
||||||
|
wsConn *websocket.Conn
|
||||||
//TODO, add bools for like.
|
//TODO, add bools for like.
|
||||||
// are we connnected to websocket?
|
// are we connnected to websocket?
|
||||||
// have we authenticated to login?
|
// have we authenticated to login?
|
||||||
|
|
168
wsapi.go
168
wsapi.go
|
@ -11,6 +11,7 @@ package discordgo
|
||||||
import (
|
import (
|
||||||
"encoding/json"
|
"encoding/json"
|
||||||
"fmt"
|
"fmt"
|
||||||
|
"runtime"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
"github.com/gorilla/websocket"
|
"github.com/gorilla/websocket"
|
||||||
|
@ -25,6 +26,7 @@ type Event struct {
|
||||||
//Direction of command, 0-received, 1-sent -- thanks Xackery/discord
|
//Direction of command, 0-received, 1-sent -- thanks Xackery/discord
|
||||||
|
|
||||||
RawData json.RawMessage `json:"d"`
|
RawData json.RawMessage `json:"d"`
|
||||||
|
Session Session
|
||||||
}
|
}
|
||||||
|
|
||||||
// The Ready Event given after initial connection
|
// The Ready Event given after initial connection
|
||||||
|
@ -47,30 +49,26 @@ type ReadState struct {
|
||||||
}
|
}
|
||||||
|
|
||||||
// Open a websocket connection to Discord
|
// Open a websocket connection to Discord
|
||||||
func Open(session *Session) (conn *websocket.Conn, err error) {
|
func Open(s *Session) (err error) {
|
||||||
|
|
||||||
// TODO: See if there's a use for the http response.
|
// TODO: See if there's a use for the http response.
|
||||||
//conn, response, err := websocket.DefaultDialer.Dial(session.Gateway, nil)
|
// conn, response, err := websocket.DefaultDialer.Dial(session.Gateway, nil)
|
||||||
conn, _, err = websocket.DefaultDialer.Dial(session.Gateway, nil)
|
s.wsConn, _, err = websocket.DefaultDialer.Dial(s.Gateway, nil)
|
||||||
if err != nil {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
// maybe this is SendOrigin? not sure the right name here
|
// maybe this is SendOrigin? not sure the right name here
|
||||||
// also bson.M vs string interface map? Read about
|
// also bson.M vs string interface map? Read about
|
||||||
// how to send JSON the right way.
|
// how to send JSON the right way.
|
||||||
func Handshake(conn *websocket.Conn, token string) (err error) {
|
func Handshake(s *Session) (err error) {
|
||||||
|
|
||||||
err = conn.WriteJSON(map[string]interface{}{
|
err = s.wsConn.WriteJSON(map[string]interface{}{
|
||||||
"op": 2,
|
"op": 2,
|
||||||
"d": map[string]interface{}{
|
"d": map[string]interface{}{
|
||||||
"v": 3,
|
"v": 3,
|
||||||
"token": token,
|
"token": s.Token,
|
||||||
"properties": map[string]string{
|
"properties": map[string]string{
|
||||||
"$os": "linux", // get from os package
|
"$os": runtime.GOOS,
|
||||||
"$browser": "Discordgo",
|
"$browser": "Discordgo",
|
||||||
"$device": "Discordgo",
|
"$device": "Discordgo",
|
||||||
"$referer": "",
|
"$referer": "",
|
||||||
|
@ -82,9 +80,9 @@ func Handshake(conn *websocket.Conn, token string) (err error) {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
func UpdateStatus(conn *websocket.Conn, idleSince, gameId string) (err error) {
|
func UpdateStatus(s *Session, idleSince, gameId string) (err error) {
|
||||||
|
|
||||||
err = conn.WriteJSON(map[string]interface{}{
|
err = s.wsConn.WriteJSON(map[string]interface{}{
|
||||||
"op": 2,
|
"op": 2,
|
||||||
"d": map[string]interface{}{
|
"d": map[string]interface{}{
|
||||||
"idle_since": idleSince,
|
"idle_since": idleSince,
|
||||||
|
@ -97,14 +95,20 @@ func UpdateStatus(conn *websocket.Conn, idleSince, gameId string) (err error) {
|
||||||
|
|
||||||
// TODO: need a channel or something to communicate
|
// TODO: need a channel or something to communicate
|
||||||
// to this so I can tell it to stop listening
|
// to this so I can tell it to stop listening
|
||||||
func Listen(conn *websocket.Conn) (err error) {
|
func Listen(s *Session) (err error) {
|
||||||
for {
|
|
||||||
messageType, message, err := conn.ReadMessage()
|
if s.wsConn == nil {
|
||||||
|
fmt.Println("No websocket connection exists.")
|
||||||
|
return // need to return an error.
|
||||||
|
}
|
||||||
|
|
||||||
|
for { // s.wsConn != nil { // need a cleaner way to exit? this doesn't acheive anything.
|
||||||
|
messageType, message, err := s.wsConn.ReadMessage()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
fmt.Println(err)
|
fmt.Println(err)
|
||||||
break
|
break
|
||||||
}
|
}
|
||||||
go event(conn, messageType, message)
|
go event(s, messageType, message)
|
||||||
}
|
}
|
||||||
|
|
||||||
return
|
return
|
||||||
|
@ -112,103 +116,127 @@ func Listen(conn *websocket.Conn) (err error) {
|
||||||
|
|
||||||
// Not sure how needed this is and where it would be best to call it.
|
// Not sure how needed this is and where it would be best to call it.
|
||||||
// somewhere.
|
// somewhere.
|
||||||
func Close(conn *websocket.Conn) {
|
func Close(s *Session) {
|
||||||
conn.Close()
|
s.wsConn.Close()
|
||||||
}
|
}
|
||||||
|
|
||||||
// Front line handler for all Websocket Events. Determines the
|
// Front line handler for all Websocket Events. Determines the
|
||||||
// event type and passes the message along to the next handler.
|
// event type and passes the message along to the next handler.
|
||||||
func event(conn *websocket.Conn, messageType int, message []byte) {
|
func event(s *Session, messageType int, message []byte) (err error) {
|
||||||
|
|
||||||
//printJSON(message) // TODO: wrap in debug if statement
|
if s.Debug {
|
||||||
|
printJSON(message)
|
||||||
var event Event
|
|
||||||
err := json.Unmarshal(message, &event)
|
|
||||||
if err != nil {
|
|
||||||
fmt.Println(err)
|
|
||||||
return
|
|
||||||
}
|
}
|
||||||
|
|
||||||
switch event.Type {
|
var e Event
|
||||||
|
if err := json.Unmarshal(message, &e); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
switch e.Type {
|
||||||
|
|
||||||
case "READY":
|
case "READY":
|
||||||
ready(conn, &event)
|
if s.OnReady != nil {
|
||||||
|
var st Ready
|
||||||
|
if err := json.Unmarshal(e.RawData, &st); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
s.OnReady(s, st)
|
||||||
|
return
|
||||||
|
}
|
||||||
case "TYPING_START":
|
case "TYPING_START":
|
||||||
// do stuff
|
if s.OnTypingStart != nil {
|
||||||
|
}
|
||||||
case "MESSAGE_CREATE":
|
case "MESSAGE_CREATE":
|
||||||
// do stuff
|
if s.OnMessageCreate != nil {
|
||||||
|
var st Message
|
||||||
|
if err := json.Unmarshal(e.RawData, &st); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
s.OnMessageCreate(s, st)
|
||||||
|
return
|
||||||
|
}
|
||||||
case "MESSAGE_ACK":
|
case "MESSAGE_ACK":
|
||||||
// do stuff
|
if s.OnMessageAck != nil {
|
||||||
|
}
|
||||||
case "MESSAGE_UPDATE":
|
case "MESSAGE_UPDATE":
|
||||||
// do stuff
|
if s.OnMessageUpdate != nil {
|
||||||
|
}
|
||||||
case "MESSAGE_DELETE":
|
case "MESSAGE_DELETE":
|
||||||
// do stuff
|
if s.OnMessageDelete != nil {
|
||||||
|
}
|
||||||
case "PRESENCE_UPDATE":
|
case "PRESENCE_UPDATE":
|
||||||
// do stuff
|
if s.OnPresenceUpdate != nil {
|
||||||
|
}
|
||||||
case "CHANNEL_CREATE":
|
case "CHANNEL_CREATE":
|
||||||
// do stuff
|
if s.OnChannelCreate != nil {
|
||||||
|
}
|
||||||
case "CHANNEL_UPDATE":
|
case "CHANNEL_UPDATE":
|
||||||
// do stuff
|
if s.OnChannelUpdate != nil {
|
||||||
|
}
|
||||||
case "CHANNEL_DELETE":
|
case "CHANNEL_DELETE":
|
||||||
// do stuff
|
if s.OnChannelDelete != nil {
|
||||||
|
}
|
||||||
case "GUILD_CREATE":
|
case "GUILD_CREATE":
|
||||||
// do stuff
|
if s.OnGuildCreate != nil {
|
||||||
|
}
|
||||||
case "GUILD_DELETE":
|
case "GUILD_DELETE":
|
||||||
// do stuff
|
if s.OnGuildDelete != nil {
|
||||||
|
}
|
||||||
case "GUILD_MEMBER_ADD":
|
case "GUILD_MEMBER_ADD":
|
||||||
// do stuff
|
if s.OnGuildMemberAdd != nil {
|
||||||
|
}
|
||||||
case "GUILD_MEMBER_REMOVE": // which is it.
|
case "GUILD_MEMBER_REMOVE": // which is it.
|
||||||
// do stuff
|
if s.OnGuildMemberRemove != nil {
|
||||||
|
}
|
||||||
case "GUILD_MEMBER_DELETE":
|
case "GUILD_MEMBER_DELETE":
|
||||||
// do stuff
|
if s.OnGuildMemberDelete != nil {
|
||||||
|
}
|
||||||
case "GUILD_MEMBER_UPDATE":
|
case "GUILD_MEMBER_UPDATE":
|
||||||
// do stuff
|
if s.OnGuildMemberUpdate != nil {
|
||||||
|
}
|
||||||
case "GUILD_ROLE_CREATE":
|
case "GUILD_ROLE_CREATE":
|
||||||
// do stuff
|
if s.OnGuildRoleCreate != nil {
|
||||||
|
}
|
||||||
case "GUILD_ROLE_DELETE":
|
case "GUILD_ROLE_DELETE":
|
||||||
// do stuff
|
if s.OnGuildRoleDelete != nil {
|
||||||
|
}
|
||||||
case "GUILD_INTEGRATIONS_UPDATE":
|
case "GUILD_INTEGRATIONS_UPDATE":
|
||||||
// do stuff
|
if s.OnGuildIntegrationsUpdate != nil {
|
||||||
|
}
|
||||||
default:
|
default:
|
||||||
fmt.Println("UNKNOWN EVENT: ", event.Type)
|
fmt.Println("UNKNOWN EVENT: ", e.Type)
|
||||||
// learn the log package
|
// learn the log package
|
||||||
// log.print type and JSON data
|
// log.print type and JSON data
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
// if still here, send to generic OnEvent
|
||||||
|
if s.OnEvent != nil {
|
||||||
// handles the READY Websocket Event from Discord
|
s.OnEvent(s, e)
|
||||||
// this is the motherload of detail provided at
|
|
||||||
// initial connection to the Websocket.
|
|
||||||
func ready(conn *websocket.Conn, event *Event) {
|
|
||||||
|
|
||||||
var ready Ready
|
|
||||||
err := json.Unmarshal(event.RawData, &ready)
|
|
||||||
if err != nil {
|
|
||||||
fmt.Println(err)
|
|
||||||
return
|
|
||||||
}
|
}
|
||||||
|
|
||||||
fmt.Println(ready)
|
return
|
||||||
|
|
||||||
go heartbeat(conn, ready.HeartbeatInterval)
|
|
||||||
|
|
||||||
// Start KeepAlive based on .
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// This heartbeat is sent to keep the Websocket conenction
|
// This heartbeat is sent to keep the Websocket conenction
|
||||||
// to Discord alive. If not sent, Discord will close the
|
// to Discord alive. If not sent, Discord will close the
|
||||||
// connection.
|
// connection.
|
||||||
func heartbeat(conn *websocket.Conn, interval time.Duration) {
|
func Heartbeat(s *Session, i time.Duration) {
|
||||||
|
|
||||||
ticker := time.NewTicker(interval * time.Millisecond)
|
if s.wsConn == nil {
|
||||||
|
fmt.Println("No websocket connection exists.")
|
||||||
|
return // need to return an error.
|
||||||
|
}
|
||||||
|
|
||||||
|
ticker := time.NewTicker(i * time.Millisecond)
|
||||||
for range ticker.C {
|
for range ticker.C {
|
||||||
timestamp := int(time.Now().Unix())
|
timestamp := int(time.Now().Unix())
|
||||||
conn.WriteJSON(map[string]int{
|
err := s.wsConn.WriteJSON(map[string]int{
|
||||||
"op": 1,
|
"op": 1,
|
||||||
"d": timestamp,
|
"d": timestamp,
|
||||||
})
|
})
|
||||||
|
if err != nil {
|
||||||
|
return // log error?
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in a new issue