wsapi.go 10 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423
  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. Guilds []Guild
  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 string `json:"last_message_id"`
  41. Id string `json:"id"`
  42. }
  43. type TypingStart struct {
  44. UserId string `json:"user_id"`
  45. ChannelId string `json:"channel_id"`
  46. Timestamp int `json:"timestamp"`
  47. }
  48. type PresenceUpdate struct {
  49. User User `json:"user"`
  50. Status string `json:"status"`
  51. Roles []string `json:"roles"`
  52. GuildId string `json:"guild_id"`
  53. GameId int `json:"game_id"`
  54. }
  55. type MessageAck struct {
  56. MessageId string `json:"message_id"`
  57. ChannelId string `json:"channel_id"`
  58. }
  59. type MessageDelete struct {
  60. Id string `json:"id"`
  61. ChannelId string `json:"channel_id"`
  62. } // so much like MessageAck..
  63. type GuildIntegrationsUpdate struct {
  64. GuildId string `json:"guild_id"`
  65. }
  66. type GuildRoleUpdate struct {
  67. Role Role `json:"role"`
  68. GuildId int `json:"guild_id,int"`
  69. }
  70. // Open a websocket connection to Discord
  71. func (s *Session) Open() (err error) {
  72. // Get the gateway to use for the Websocket connection
  73. g, err := s.Gateway()
  74. // TODO: See if there's a use for the http response.
  75. // conn, response, err := websocket.DefaultDialer.Dial(session.Gateway, nil)
  76. s.wsConn, _, err = websocket.DefaultDialer.Dial(g, nil)
  77. return
  78. }
  79. // maybe this is SendOrigin? not sure the right name here
  80. // also bson.M vs string interface map? Read about
  81. // how to send JSON the right way.
  82. func (s *Session) Handshake() (err error) {
  83. err = s.wsConn.WriteJSON(map[string]interface{}{
  84. "op": 2,
  85. "d": map[string]interface{}{
  86. "v": 3,
  87. "token": s.Token,
  88. "properties": map[string]string{
  89. "$os": runtime.GOOS,
  90. "$browser": "Discordgo",
  91. "$device": "Discordgo",
  92. "$referer": "",
  93. "$referring_domain": "",
  94. },
  95. },
  96. })
  97. return
  98. }
  99. func (s *Session) UpdateStatus(idleSince, gameId string) (err error) {
  100. err = s.wsConn.WriteJSON(map[string]interface{}{
  101. "op": 2,
  102. "d": map[string]interface{}{
  103. "idle_since": idleSince,
  104. "game_id": gameId,
  105. },
  106. })
  107. return
  108. }
  109. // TODO: need a channel or something to communicate
  110. // to this so I can tell it to stop listening
  111. func (s *Session) Listen() (err error) {
  112. if s.wsConn == nil {
  113. fmt.Println("No websocket connection exists.")
  114. return // need to return an error.
  115. }
  116. for { // s.wsConn != nil { // need a cleaner way to exit? this doesn't acheive anything.
  117. messageType, message, err := s.wsConn.ReadMessage()
  118. if err != nil {
  119. fmt.Println(err)
  120. break
  121. }
  122. go s.event(messageType, message)
  123. }
  124. return
  125. }
  126. // Not sure how needed this is and where it would be best to call it.
  127. // somewhere.
  128. func (s *Session) Close() {
  129. s.wsConn.Close()
  130. }
  131. // Front line handler for all Websocket Events. Determines the
  132. // event type and passes the message along to the next handler.
  133. func (s *Session) event(messageType int, message []byte) (err error) {
  134. if s.Debug {
  135. printJSON(message)
  136. }
  137. var e Event
  138. if err := json.Unmarshal(message, &e); err != nil {
  139. return err
  140. }
  141. switch e.Type {
  142. case "READY":
  143. if s.OnReady != nil {
  144. var st Ready
  145. if err := json.Unmarshal(e.RawData, &st); err != nil {
  146. fmt.Println(e.Type, err)
  147. printJSON(e.RawData) // TODO: Better error logging
  148. return err
  149. }
  150. s.OnReady(s, st)
  151. return
  152. }
  153. case "VOICE_STATE_UPDATE":
  154. if s.OnVoiceStateUpdate != nil {
  155. var st VoiceState
  156. if err := json.Unmarshal(e.RawData, &st); err != nil {
  157. fmt.Println(e.Type, err)
  158. printJSON(e.RawData) // TODO: Better error logging
  159. return err
  160. }
  161. s.OnVoiceStateUpdate(s, st)
  162. return
  163. }
  164. case "PRESENCE_UPDATE":
  165. if s.OnPresenceUpdate != nil {
  166. var st PresenceUpdate
  167. if err := json.Unmarshal(e.RawData, &st); err != nil {
  168. fmt.Println(e.Type, err)
  169. printJSON(e.RawData) // TODO: Better error logging
  170. return err
  171. }
  172. s.OnPresenceUpdate(s, st)
  173. return
  174. }
  175. case "TYPING_START":
  176. if s.OnTypingStart != nil {
  177. var st TypingStart
  178. if err := json.Unmarshal(e.RawData, &st); err != nil {
  179. fmt.Println(e.Type, err)
  180. printJSON(e.RawData) // TODO: Better error logging
  181. return err
  182. }
  183. s.OnTypingStart(s, st)
  184. return
  185. }
  186. case "MESSAGE_CREATE":
  187. if s.OnMessageCreate != nil {
  188. var st Message
  189. if err := json.Unmarshal(e.RawData, &st); err != nil {
  190. fmt.Println(e.Type, err)
  191. printJSON(e.RawData) // TODO: Better error logging
  192. return err
  193. }
  194. s.OnMessageCreate(s, st)
  195. return
  196. }
  197. case "MESSAGE_UPDATE":
  198. if s.OnMessageUpdate != nil {
  199. var st Message
  200. if err := json.Unmarshal(e.RawData, &st); err != nil {
  201. fmt.Println(e.Type, err)
  202. printJSON(e.RawData) // TODO: Better error logging
  203. return err
  204. }
  205. s.OnMessageUpdate(s, st)
  206. return
  207. }
  208. case "MESSAGE_DELETE":
  209. if s.OnMessageDelete != nil {
  210. var st MessageDelete
  211. if err := json.Unmarshal(e.RawData, &st); err != nil {
  212. fmt.Println(e.Type, err)
  213. printJSON(e.RawData) // TODO: Better error logging
  214. return err
  215. }
  216. s.OnMessageDelete(s, st)
  217. return
  218. }
  219. case "MESSAGE_ACK":
  220. if s.OnMessageAck != nil {
  221. var st MessageAck
  222. if err := json.Unmarshal(e.RawData, &st); err != nil {
  223. fmt.Println(e.Type, err)
  224. printJSON(e.RawData) // TODO: Better error logging
  225. return err
  226. }
  227. s.OnMessageAck(s, st)
  228. return
  229. }
  230. case "CHANNEL_CREATE":
  231. if s.OnChannelCreate != nil {
  232. var st Channel
  233. if err := json.Unmarshal(e.RawData, &st); err != nil {
  234. fmt.Println(e.Type, err)
  235. printJSON(e.RawData) // TODO: Better error logginEventg
  236. return err
  237. }
  238. s.OnChannelCreate(s, st)
  239. return
  240. }
  241. case "CHANNEL_UPDATE":
  242. if s.OnChannelUpdate != nil {
  243. var st Channel
  244. if err := json.Unmarshal(e.RawData, &st); err != nil {
  245. fmt.Println(e.Type, err)
  246. printJSON(e.RawData) // TODO: Better error logginEventg
  247. return err
  248. }
  249. s.OnChannelUpdate(s, st)
  250. return
  251. }
  252. case "CHANNEL_DELETE":
  253. if s.OnChannelDelete != nil {
  254. var st Channel
  255. if err := json.Unmarshal(e.RawData, &st); err != nil {
  256. fmt.Println(e.Type, err)
  257. printJSON(e.RawData) // TODO: Better error logginEventg
  258. return err
  259. }
  260. s.OnChannelDelete(s, st)
  261. return
  262. }
  263. case "GUILD_CREATE":
  264. if s.OnGuildCreate != nil {
  265. var st Guild
  266. if err := json.Unmarshal(e.RawData, &st); err != nil {
  267. fmt.Println(e.Type, err)
  268. printJSON(e.RawData) // TODO: Better error logginEventg
  269. return err
  270. }
  271. s.OnGuildCreate(s, st)
  272. return
  273. }
  274. case "GUILD_UPDATE":
  275. if s.OnGuildCreate != nil {
  276. var st Guild
  277. if err := json.Unmarshal(e.RawData, &st); err != nil {
  278. fmt.Println(e.Type, err)
  279. printJSON(e.RawData) // TODO: Better error logginEventg
  280. return err
  281. }
  282. s.OnGuildUpdate(s, st)
  283. return
  284. }
  285. case "GUILD_DELETE":
  286. if s.OnGuildDelete != nil {
  287. var st Guild
  288. if err := json.Unmarshal(e.RawData, &st); err != nil {
  289. fmt.Println(e.Type, err)
  290. printJSON(e.RawData) // TODO: Better error logginEventg
  291. return err
  292. }
  293. s.OnGuildDelete(s, st)
  294. return
  295. }
  296. case "GUILD_MEMBER_ADD":
  297. if s.OnGuildMemberAdd != nil {
  298. var st Member
  299. if err := json.Unmarshal(e.RawData, &st); err != nil {
  300. fmt.Println(e.Type, err)
  301. printJSON(e.RawData) // TODO: Better error logginEventg
  302. return err
  303. }
  304. s.OnGuildMemberAdd(s, st)
  305. return
  306. }
  307. case "GUILD_MEMBER_REMOVE":
  308. if s.OnGuildMemberRemove != nil {
  309. var st Member
  310. if err := json.Unmarshal(e.RawData, &st); err != nil {
  311. fmt.Println(e.Type, err)
  312. printJSON(e.RawData) // TODO: Better error logginEventg
  313. return err
  314. }
  315. s.OnGuildMemberRemove(s, st)
  316. return
  317. }
  318. case "GUILD_MEMBER_UPDATE":
  319. if s.OnGuildMemberUpdate != nil {
  320. var st Member
  321. if err := json.Unmarshal(e.RawData, &st); err != nil {
  322. fmt.Println(e.Type, err)
  323. printJSON(e.RawData) // TODO: Better error logginEventg
  324. return err
  325. }
  326. s.OnGuildMemberUpdate(s, st)
  327. return
  328. }
  329. case "GUILD_ROLE_CREATE":
  330. if s.OnGuildRoleUpdate != nil {
  331. var st GuildRoleUpdate
  332. if err := json.Unmarshal(e.RawData, &st); err != nil {
  333. fmt.Println(e.Type, err)
  334. printJSON(e.RawData) // TODO: Better error logginEventg
  335. return err
  336. }
  337. s.OnGuildRoleUpdate(s, st)
  338. return
  339. }
  340. /*
  341. case "GUILD_ROLE_DELETE":
  342. if s.OnGuildRoleDelete != nil {
  343. }
  344. */
  345. case "GUILD_INTEGRATIONS_UPDATE":
  346. if s.OnGuildIntegrationsUpdate != nil {
  347. var st GuildIntegrationsUpdate
  348. if err := json.Unmarshal(e.RawData, &st); err != nil {
  349. fmt.Println(e.Type, err)
  350. printJSON(e.RawData) // TODO: Better error logginEventg
  351. return err
  352. }
  353. s.OnGuildIntegrationsUpdate(s, st)
  354. return
  355. }
  356. default:
  357. fmt.Println("UNKNOWN EVENT: ", e.Type)
  358. // learn the log package
  359. // log.print type and JSON data
  360. }
  361. // if still here, send to generic OnEvent
  362. if s.OnEvent != nil {
  363. s.OnEvent(s, e)
  364. }
  365. return
  366. }
  367. // This heartbeat is sent to keep the Websocket conenction
  368. // to Discord alive. If not sent, Discord will close the
  369. // connection.
  370. func (s *Session) Heartbeat(i time.Duration) {
  371. if s.wsConn == nil {
  372. fmt.Println("No websocket connection exists.")
  373. return // need to return an error.
  374. }
  375. ticker := time.NewTicker(i * time.Millisecond)
  376. for range ticker.C {
  377. timestamp := int(time.Now().Unix())
  378. err := s.wsConn.WriteJSON(map[string]int{
  379. "op": 1,
  380. "d": timestamp,
  381. })
  382. if err != nil {
  383. return // log error?
  384. }
  385. }
  386. }