wsapi.go 5.4 KB

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