wsapi.go 4.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214
  1. /******************************************************************************
  2. * A Discord API for Golang.
  3. * See discord.go for more information.
  4. *
  5. * This file contains functions low level functions for interacting
  6. * with the Discord Websocket interface.
  7. */
  8. package discordgo
  9. import (
  10. "encoding/json"
  11. "fmt"
  12. "time"
  13. "github.com/gorilla/websocket"
  14. )
  15. // Basic struct for all Websocket Event messages
  16. type Event struct {
  17. Type string `json:"t"`
  18. State int `json:"s"`
  19. Operation int `json:"o"`
  20. Direction int `json:"dir"`
  21. //Direction of command, 0-received, 1-sent -- thanks Xackery/discord
  22. RawData json.RawMessage `json:"d"`
  23. }
  24. // The Ready Event given after initial connection
  25. type Ready struct {
  26. Version int `json:"v"`
  27. SessionID string `json:"session_id"`
  28. HeartbeatInterval time.Duration `json:"heartbeat_interval"`
  29. User User `json:"user"`
  30. ReadState []ReadState
  31. PrivateChannels []PrivateChannel
  32. Servers []Server
  33. }
  34. // ReadState might need to move? Gives me the read status
  35. // of all my channels when first connecting. I think :)
  36. type ReadState struct {
  37. MentionCount int
  38. LastMessageID int `json:"last_message_id,string"`
  39. ID int `json:"id,string"`
  40. }
  41. // Open a websocket connection to Discord
  42. func Open(session *Session) (conn *websocket.Conn, err error) {
  43. // TODO: See if there's a use for the http response.
  44. //conn, response, err := websocket.DefaultDialer.Dial(session.Gateway, nil)
  45. conn, _, err = websocket.DefaultDialer.Dial(session.Gateway, nil)
  46. if err != nil {
  47. return
  48. }
  49. return
  50. }
  51. // maybe this is SendOrigin? not sure the right name here
  52. // also bson.M vs string interface map? Read about
  53. // how to send JSON the right way.
  54. func Handshake(conn *websocket.Conn, token string) (err error) {
  55. err = conn.WriteJSON(map[string]interface{}{
  56. "op": 2,
  57. "d": map[string]interface{}{
  58. "v": 3,
  59. "token": token,
  60. "properties": map[string]string{
  61. "$os": "linux", // get from os package
  62. "$browser": "Discordgo",
  63. "$device": "Discordgo",
  64. "$referer": "",
  65. "$referring_domain": "",
  66. },
  67. },
  68. })
  69. return
  70. }
  71. func UpdateStatus(conn *websocket.Conn, idleSince, gameId string) (err error) {
  72. err = conn.WriteJSON(map[string]interface{}{
  73. "op": 2,
  74. "d": map[string]interface{}{
  75. "idle_since": idleSince,
  76. "game_id": gameId,
  77. },
  78. })
  79. return
  80. }
  81. // TODO: need a channel or something to communicate
  82. // to this so I can tell it to stop listening
  83. func Listen(conn *websocket.Conn) (err error) {
  84. for {
  85. messageType, message, err := conn.ReadMessage()
  86. if err != nil {
  87. fmt.Println(err)
  88. break
  89. }
  90. go event(conn, messageType, message)
  91. }
  92. return
  93. }
  94. // Not sure how needed this is and where it would be best to call it.
  95. // somewhere.
  96. func Close(conn *websocket.Conn) {
  97. conn.Close()
  98. }
  99. // Front line handler for all Websocket Events. Determines the
  100. // event type and passes the message along to the next handler.
  101. func event(conn *websocket.Conn, messageType int, message []byte) {
  102. //printJSON(message) // TODO: wrap in debug if statement
  103. var event Event
  104. err := json.Unmarshal(message, &event)
  105. if err != nil {
  106. fmt.Println(err)
  107. return
  108. }
  109. switch event.Type {
  110. case "READY":
  111. ready(conn, &event)
  112. case "TYPING_START":
  113. // do stuff
  114. case "MESSAGE_CREATE":
  115. // do stuff
  116. case "MESSAGE_ACK":
  117. // do stuff
  118. case "MESSAGE_UPDATE":
  119. // do stuff
  120. case "MESSAGE_DELETE":
  121. // do stuff
  122. case "PRESENCE_UPDATE":
  123. // do stuff
  124. case "CHANNEL_CREATE":
  125. // do stuff
  126. case "CHANNEL_UPDATE":
  127. // do stuff
  128. case "CHANNEL_DELETE":
  129. // do stuff
  130. case "GUILD_CREATE":
  131. // do stuff
  132. case "GUILD_DELETE":
  133. // do stuff
  134. case "GUILD_MEMBER_ADD":
  135. // do stuff
  136. case "GUILD_MEMBER_REMOVE": // which is it.
  137. // do stuff
  138. case "GUILD_MEMBER_DELETE":
  139. // do stuff
  140. case "GUILD_MEMBER_UPDATE":
  141. // do stuff
  142. case "GUILD_ROLE_CREATE":
  143. // do stuff
  144. case "GUILD_ROLE_DELETE":
  145. // do stuff
  146. case "GUILD_INTEGRATIONS_UPDATE":
  147. // do stuff
  148. default:
  149. fmt.Println("UNKNOWN EVENT: ", event.Type)
  150. // learn the log package
  151. // log.print type and JSON data
  152. }
  153. }
  154. // handles the READY Websocket Event from Discord
  155. // this is the motherload of detail provided at
  156. // initial connection to the Websocket.
  157. func ready(conn *websocket.Conn, event *Event) {
  158. var ready Ready
  159. err := json.Unmarshal(event.RawData, &ready)
  160. if err != nil {
  161. fmt.Println(err)
  162. return
  163. }
  164. fmt.Println(ready)
  165. go heartbeat(conn, ready.HeartbeatInterval)
  166. // Start KeepAlive based on .
  167. }
  168. // This heartbeat is sent to keep the Websocket conenction
  169. // to Discord alive. If not sent, Discord will close the
  170. // connection.
  171. func heartbeat(conn *websocket.Conn, interval time.Duration) {
  172. ticker := time.NewTicker(interval * time.Millisecond)
  173. for range ticker.C {
  174. timestamp := int(time.Now().Unix())
  175. conn.WriteJSON(map[string]int{
  176. "op": 1,
  177. "d": timestamp,
  178. })
  179. }
  180. }