wsapi.go 10 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435
  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 int `json:"last_message_id,string"`
  41. ID int `json:"id,string"`
  42. }
  43. type TypingStart struct {
  44. UserId int `json:"user_id,string"`
  45. ChannelId int `json:"channel_id,string"`
  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"` // TODO: Should be ints, see below
  52. GuildId int `json:"guild_id,string"`
  53. GameId int `json:"game_id"`
  54. }
  55. //Roles []string `json:"roles"` // TODO: Should be ints, see below
  56. // Above "Roles" should be an array of ints
  57. // TODO: Figure out how to make it be one.
  58. /*
  59. {
  60. "roles": [
  61. "89544728336416768",
  62. "110429733396676608"
  63. ],
  64. }
  65. */
  66. type MessageAck struct {
  67. MessageId int `json:"message_id,string"`
  68. ChannelId int `json:"channel_id,string"`
  69. }
  70. type MessageDelete struct {
  71. Id int `json:"id,string"`
  72. ChannelId int `json:"channel_id,string"`
  73. } // so much like MessageAck..
  74. type GuildIntegrationsUpdate struct {
  75. GuildId int `json:"guild_id,string"`
  76. }
  77. type GuildRoleUpdate struct {
  78. Role Role `json:"role"`
  79. GuildId int `json:"guild_id,int"`
  80. }
  81. // Open a websocket connection to Discord
  82. func (s *Session) Open() (err error) {
  83. // Get the gateway to use for the Websocket connection
  84. g, err := s.Gateway()
  85. // TODO: See if there's a use for the http response.
  86. // conn, response, err := websocket.DefaultDialer.Dial(session.Gateway, nil)
  87. s.wsConn, _, err = websocket.DefaultDialer.Dial(g, nil)
  88. return
  89. }
  90. // maybe this is SendOrigin? not sure the right name here
  91. // also bson.M vs string interface map? Read about
  92. // how to send JSON the right way.
  93. func (s *Session) Handshake() (err error) {
  94. err = s.wsConn.WriteJSON(map[string]interface{}{
  95. "op": 2,
  96. "d": map[string]interface{}{
  97. "v": 3,
  98. "token": s.Token,
  99. "properties": map[string]string{
  100. "$os": runtime.GOOS,
  101. "$browser": "Discordgo",
  102. "$device": "Discordgo",
  103. "$referer": "",
  104. "$referring_domain": "",
  105. },
  106. },
  107. })
  108. return
  109. }
  110. func (s *Session) UpdateStatus(idleSince, gameId string) (err error) {
  111. err = s.wsConn.WriteJSON(map[string]interface{}{
  112. "op": 2,
  113. "d": map[string]interface{}{
  114. "idle_since": idleSince,
  115. "game_id": gameId,
  116. },
  117. })
  118. return
  119. }
  120. // TODO: need a channel or something to communicate
  121. // to this so I can tell it to stop listening
  122. func (s *Session) Listen() (err error) {
  123. if s.wsConn == nil {
  124. fmt.Println("No websocket connection exists.")
  125. return // need to return an error.
  126. }
  127. for { // s.wsConn != nil { // need a cleaner way to exit? this doesn't acheive anything.
  128. messageType, message, err := s.wsConn.ReadMessage()
  129. if err != nil {
  130. fmt.Println(err)
  131. break
  132. }
  133. go s.event(messageType, message)
  134. }
  135. return
  136. }
  137. // Not sure how needed this is and where it would be best to call it.
  138. // somewhere.
  139. func (s *Session) Close() {
  140. s.wsConn.Close()
  141. }
  142. // Front line handler for all Websocket Events. Determines the
  143. // event type and passes the message along to the next handler.
  144. func (s *Session) event(messageType int, message []byte) (err error) {
  145. if s.Debug {
  146. printJSON(message)
  147. }
  148. var e Event
  149. if err := json.Unmarshal(message, &e); err != nil {
  150. return err
  151. }
  152. switch e.Type {
  153. case "READY":
  154. if s.OnReady != nil {
  155. var st Ready
  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.OnReady(s, st)
  162. return
  163. }
  164. case "VOICE_STATE_UPDATE":
  165. if s.OnVoiceStateUpdate != nil {
  166. var st VoiceState
  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.OnVoiceStateUpdate(s, st)
  173. return
  174. }
  175. case "PRESENCE_UPDATE":
  176. if s.OnPresenceUpdate != nil {
  177. var st PresenceUpdate
  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.OnPresenceUpdate(s, st)
  184. return
  185. }
  186. case "TYPING_START":
  187. if s.OnTypingStart != nil {
  188. var st TypingStart
  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.OnTypingStart(s, st)
  195. return
  196. }
  197. case "MESSAGE_CREATE":
  198. if s.OnMessageCreate != 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.OnMessageCreate(s, st)
  206. return
  207. }
  208. case "MESSAGE_UPDATE":
  209. if s.OnMessageUpdate != nil {
  210. var st Message
  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.OnMessageUpdate(s, st)
  217. return
  218. }
  219. case "MESSAGE_DELETE":
  220. if s.OnMessageDelete != nil {
  221. var st MessageDelete
  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.OnMessageDelete(s, st)
  228. return
  229. }
  230. case "MESSAGE_ACK":
  231. if s.OnMessageAck != nil {
  232. var st MessageAck
  233. if err := json.Unmarshal(e.RawData, &st); err != nil {
  234. fmt.Println(e.Type, err)
  235. printJSON(e.RawData) // TODO: Better error logging
  236. return err
  237. }
  238. s.OnMessageAck(s, st)
  239. return
  240. }
  241. case "CHANNEL_CREATE":
  242. if s.OnChannelCreate != 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.OnChannelCreate(s, st)
  250. return
  251. }
  252. case "CHANNEL_UPDATE":
  253. if s.OnChannelUpdate != 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.OnChannelUpdate(s, st)
  261. return
  262. }
  263. case "CHANNEL_DELETE":
  264. if s.OnChannelDelete != nil {
  265. var st Channel
  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.OnChannelDelete(s, st)
  272. return
  273. }
  274. case "GUILD_CREATE":
  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.OnGuildCreate(s, st)
  283. return
  284. }
  285. case "GUILD_UPDATE":
  286. if s.OnGuildCreate != 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.OnGuildUpdate(s, st)
  294. return
  295. }
  296. case "GUILD_DELETE":
  297. if s.OnGuildDelete != nil {
  298. var st Guild
  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.OnGuildDelete(s, st)
  305. return
  306. }
  307. case "GUILD_MEMBER_ADD":
  308. if s.OnGuildMemberAdd != 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.OnGuildMemberAdd(s, st)
  316. return
  317. }
  318. case "GUILD_MEMBER_REMOVE":
  319. if s.OnGuildMemberRemove != 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.OnGuildMemberRemove(s, st)
  327. return
  328. }
  329. case "GUILD_MEMBER_UPDATE":
  330. if s.OnGuildMemberUpdate != nil {
  331. var st Member
  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.OnGuildMemberUpdate(s, st)
  338. return
  339. }
  340. case "GUILD_ROLE_CREATE":
  341. if s.OnGuildRoleUpdate != nil {
  342. var st GuildRoleUpdate
  343. if err := json.Unmarshal(e.RawData, &st); err != nil {
  344. fmt.Println(e.Type, err)
  345. printJSON(e.RawData) // TODO: Better error logginEventg
  346. return err
  347. }
  348. s.OnGuildRoleUpdate(s, st)
  349. return
  350. }
  351. /*
  352. case "GUILD_ROLE_DELETE":
  353. if s.OnGuildRoleDelete != nil {
  354. }
  355. */
  356. case "GUILD_INTEGRATIONS_UPDATE":
  357. if s.OnGuildIntegrationsUpdate != nil {
  358. var st GuildIntegrationsUpdate
  359. if err := json.Unmarshal(e.RawData, &st); err != nil {
  360. fmt.Println(e.Type, err)
  361. printJSON(e.RawData) // TODO: Better error logginEventg
  362. return err
  363. }
  364. s.OnGuildIntegrationsUpdate(s, st)
  365. return
  366. }
  367. default:
  368. fmt.Println("UNKNOWN EVENT: ", e.Type)
  369. // learn the log package
  370. // log.print type and JSON data
  371. }
  372. // if still here, send to generic OnEvent
  373. if s.OnEvent != nil {
  374. s.OnEvent(s, e)
  375. }
  376. return
  377. }
  378. // This heartbeat is sent to keep the Websocket conenction
  379. // to Discord alive. If not sent, Discord will close the
  380. // connection.
  381. func (s *Session) Heartbeat(i time.Duration) {
  382. if s.wsConn == nil {
  383. fmt.Println("No websocket connection exists.")
  384. return // need to return an error.
  385. }
  386. ticker := time.NewTicker(i * time.Millisecond)
  387. for range ticker.C {
  388. timestamp := int(time.Now().Unix())
  389. err := s.wsConn.WriteJSON(map[string]int{
  390. "op": 1,
  391. "d": timestamp,
  392. })
  393. if err != nil {
  394. return // log error?
  395. }
  396. }
  397. }