Inital addition of Websocket handling. Lots of moving things around.
This commit is contained in:
parent
14dc3f3e79
commit
adac11495a
4 changed files with 263 additions and 0 deletions
17
users.go
Normal file
17
users.go
Normal file
|
@ -0,0 +1,17 @@
|
|||
package discordgo
|
||||
|
||||
type User struct {
|
||||
Id int `json:"id,string"`
|
||||
Email string `json:"email"`
|
||||
Username string `json:"username"`
|
||||
Avatar string `json:"Avatar"`
|
||||
Verified bool `json:"verified"`
|
||||
Discriminator string `json:"discriminator"`
|
||||
}
|
||||
|
||||
type PrivateChannel struct {
|
||||
Id int `json:"id,string"`
|
||||
IsPrivate bool `json:"is_private"`
|
||||
LastMessageId int `json:"last_message_id,string"`
|
||||
Recipient User `json:"recipient"`
|
||||
}
|
16
util.go
Normal file
16
util.go
Normal file
|
@ -0,0 +1,16 @@
|
|||
package discordgo
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
)
|
||||
|
||||
func printJSON(body []byte) {
|
||||
var prettyJSON bytes.Buffer
|
||||
error := json.Indent(&prettyJSON, body, "", "\t")
|
||||
if error != nil {
|
||||
fmt.Print("JSON parse error: ", error)
|
||||
}
|
||||
fmt.Println("RESPONSE ::\n" + string(prettyJSON.Bytes()))
|
||||
}
|
230
wsapi.go
Normal file
230
wsapi.go
Normal file
|
@ -0,0 +1,230 @@
|
|||
/******************************************************************************
|
||||
* Discordgo by Bruce Marriner <bruce@sqls.net>
|
||||
* A Discord API for Golang.
|
||||
* See discord.go for more information.
|
||||
*
|
||||
* This file contains functions low level functions for interacting
|
||||
* with the Discord Websocket interface.
|
||||
*/
|
||||
|
||||
package discordgo
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"time"
|
||||
|
||||
"github.com/gorilla/websocket"
|
||||
)
|
||||
|
||||
// Basic struct for all Websocket Event messages
|
||||
type Event struct {
|
||||
Type string `json:"t"`
|
||||
State int `json:"s"`
|
||||
Operation int `json:"o"`
|
||||
Direction int `json:"dir"`
|
||||
//Direction of command, 0-received, 1-sent -- thanks Xackery/discord
|
||||
|
||||
RawData json.RawMessage `json:"d"`
|
||||
}
|
||||
|
||||
// The Ready Event given after initial connection
|
||||
type Ready struct {
|
||||
Version int `json:"v"`
|
||||
SessionID string `json:"session_id"`
|
||||
HeartbeatInterval time.Duration `json:"heartbeat_interval"`
|
||||
User User `json:"user"`
|
||||
ReadState []ReadState
|
||||
PrivateChannels []PrivateChannel
|
||||
Servers []Server
|
||||
}
|
||||
|
||||
// ReadState might need to move? Gives me the read status
|
||||
// of all my channels when first connecting. I think :)
|
||||
type ReadState struct {
|
||||
MentionCount int
|
||||
LastMessageID int `json:"last_message_id,string"`
|
||||
ID int `json:"id,string"`
|
||||
}
|
||||
|
||||
// Returns the a websocket Gateway address
|
||||
// session : An active session connection to Discord
|
||||
// put this here instead of restapi because it is used soley
|
||||
// for the websocket stuff - but maybe I should move it back
|
||||
// because it's part of the restapi...
|
||||
func Gateway(session *Session) (gateway string, err error) {
|
||||
|
||||
response, err := Request(session, "GET", fmt.Sprintf("%s/gateway", discordApi), ``)
|
||||
|
||||
var temp map[string]interface{}
|
||||
err = json.Unmarshal(response, &temp)
|
||||
gateway = temp["url"].(string)
|
||||
return
|
||||
}
|
||||
|
||||
// Open a websocket connection to Discord
|
||||
func Open(session *Session) (conn *websocket.Conn, err error) {
|
||||
|
||||
// TODO: See if there's a use for the http response.
|
||||
//conn, response, err := websocket.DefaultDialer.Dial(session.Gateway, nil)
|
||||
conn, _, err = websocket.DefaultDialer.Dial(session.Gateway, nil)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
// maybe this is SendOrigin? not sure the right name here
|
||||
// also bson.M vs string interface map? Read about
|
||||
// how to send JSON the right way.
|
||||
func Handshake(conn *websocket.Conn, token string) (err error) {
|
||||
|
||||
err = conn.WriteJSON(map[string]interface{}{
|
||||
"op": 2,
|
||||
"d": map[string]interface{}{
|
||||
"v": 3,
|
||||
"token": token,
|
||||
"properties": map[string]string{
|
||||
"$os": "linux", // get from os package
|
||||
"$browser": "Discordgo",
|
||||
"$device": "Discordgo",
|
||||
"$referer": "",
|
||||
"$referring_domain": "",
|
||||
},
|
||||
},
|
||||
})
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
func UpdateStatus(conn *websocket.Conn, idleSince, gameId string) (err error) {
|
||||
|
||||
err = conn.WriteJSON(map[string]interface{}{
|
||||
"op": 2,
|
||||
"d": map[string]interface{}{
|
||||
"idle_since": idleSince,
|
||||
"game_id": gameId,
|
||||
},
|
||||
})
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
// TODO: need a channel or something to communicate
|
||||
// to this so I can tell it to stop listening
|
||||
func Listen(conn *websocket.Conn) (err error) {
|
||||
for {
|
||||
messageType, message, err := conn.ReadMessage()
|
||||
if err != nil {
|
||||
fmt.Println(err)
|
||||
break
|
||||
}
|
||||
go event(conn, messageType, message)
|
||||
}
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
// Not sure how needed this is and where it would be best to call it.
|
||||
// somewhere.
|
||||
func Close(conn *websocket.Conn) {
|
||||
conn.Close()
|
||||
}
|
||||
|
||||
// Front line handler for all Websocket Events. Determines the
|
||||
// event type and passes the message along to the next handler.
|
||||
func event(conn *websocket.Conn, messageType int, message []byte) {
|
||||
|
||||
//printJSON(message) // TODO: wrap in debug if statement
|
||||
|
||||
var event Event
|
||||
err := json.Unmarshal(message, &event)
|
||||
if err != nil {
|
||||
fmt.Println(err)
|
||||
return
|
||||
}
|
||||
|
||||
switch event.Type {
|
||||
|
||||
case "READY":
|
||||
ready(conn, &event)
|
||||
case "TYPING_START":
|
||||
// do stuff
|
||||
case "MESSAGE_CREATE":
|
||||
// do stuff
|
||||
case "MESSAGE_ACK":
|
||||
// do stuff
|
||||
case "MESSAGE_UPDATE":
|
||||
// do stuff
|
||||
case "MESSAGE_DELETE":
|
||||
// do stuff
|
||||
case "PRESENCE_UPDATE":
|
||||
// do stuff
|
||||
case "CHANNEL_CREATE":
|
||||
// do stuff
|
||||
case "CHANNEL_UPDATE":
|
||||
// do stuff
|
||||
case "CHANNEL_DELETE":
|
||||
// do stuff
|
||||
case "GUILD_CREATE":
|
||||
// do stuff
|
||||
case "GUILD_DELETE":
|
||||
// do stuff
|
||||
case "GUILD_MEMBER_ADD":
|
||||
// do stuff
|
||||
case "GUILD_MEMBER_REMOVE": // which is it.
|
||||
// do stuff
|
||||
case "GUILD_MEMBER_DELETE":
|
||||
// do stuff
|
||||
case "GUILD_MEMBER_UPDATE":
|
||||
// do stuff
|
||||
case "GUILD_ROLE_CREATE":
|
||||
// do stuff
|
||||
case "GUILD_ROLE_DELETE":
|
||||
// do stuff
|
||||
case "GUILD_INTEGRATIONS_UPDATE":
|
||||
// do stuff
|
||||
|
||||
default:
|
||||
fmt.Println("UNKNOWN EVENT: ", event.Type)
|
||||
// learn the log package
|
||||
// log.print type and JSON data
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
// handles the READY Websocket Event from Discord
|
||||
// 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)
|
||||
|
||||
go heartbeat(conn, ready.HeartbeatInterval)
|
||||
|
||||
// Start KeepAlive based on .
|
||||
|
||||
}
|
||||
|
||||
// This heartbeat is sent to keep the Websocket conenction
|
||||
// to Discord alive. If not sent, Discord will close the
|
||||
// connection.
|
||||
func heartbeat(conn *websocket.Conn, interval time.Duration) {
|
||||
|
||||
ticker := time.NewTicker(interval * time.Millisecond)
|
||||
for range ticker.C {
|
||||
timestamp := int(time.Now().Unix())
|
||||
conn.WriteJSON(map[string]int{
|
||||
"op": 1,
|
||||
"d": timestamp,
|
||||
})
|
||||
}
|
||||
}
|
Loading…
Reference in a new issue