discord.go 3.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172
  1. package main
  2. import (
  3. "encoding/json"
  4. "fmt"
  5. "sort"
  6. "github.com/ayntgl/discordgo"
  7. "github.com/gen2brain/beeep"
  8. "github.com/rivo/tview"
  9. )
  10. var (
  11. session *discordgo.Session
  12. selectedChannel *discordgo.Channel
  13. selectedMessage *discordgo.Message
  14. )
  15. func newSession() *discordgo.Session {
  16. s, err := discordgo.New()
  17. if err != nil {
  18. panic(err)
  19. }
  20. s.UserAgent = conf.UserAgent
  21. s.Identify.Compress = false
  22. s.Identify.Intents = 0
  23. s.Identify.LargeThreshold = 0
  24. s.Identify.Properties.Device = ""
  25. s.Identify.Properties.Browser = "Chrome"
  26. s.Identify.Properties.OS = "Linux"
  27. s.AddHandlerOnce(onSessionReady)
  28. s.AddHandler(onSessionMessageCreate)
  29. return s
  30. }
  31. func onSessionReady(_ *discordgo.Session, r *discordgo.Ready) {
  32. dmNode := tview.NewTreeNode("Direct Messages").
  33. Collapse()
  34. n := channelsTree.GetRoot()
  35. n.AddChild(dmNode)
  36. sort.Slice(r.PrivateChannels, func(i, j int) bool {
  37. return r.PrivateChannels[i].LastMessageID > r.PrivateChannels[j].LastMessageID
  38. })
  39. for _, c := range r.PrivateChannels {
  40. cn := tview.NewTreeNode(generateChannelRepr(c)).SetReference(c.ID)
  41. dmNode.AddChild(cn)
  42. }
  43. sort.Slice(r.Guilds, func(a, b int) bool {
  44. found := false
  45. for _, gID := range r.Settings.GuildPositions {
  46. if found {
  47. if gID == r.Guilds[b].ID {
  48. return true
  49. }
  50. } else {
  51. if gID == r.Guilds[a].ID {
  52. found = true
  53. }
  54. }
  55. }
  56. return false
  57. })
  58. for _, g := range r.Guilds {
  59. gn := tview.NewTreeNode(g.Name).Collapse()
  60. n.AddChild(gn)
  61. cs := g.Channels
  62. sort.Slice(cs, func(i, j int) bool {
  63. return cs[i].Position < cs[j].Position
  64. })
  65. // Top-level channels
  66. createTopLevelChannelsTreeNodes(gn, cs)
  67. // Category channels
  68. createCategoryChannelsTreeNodes(gn, cs)
  69. // Second-level channels
  70. createSecondLevelChannelsTreeNodes(cs)
  71. }
  72. channelsTree.SetCurrentNode(n)
  73. }
  74. func onSessionMessageCreate(_ *discordgo.Session, m *discordgo.MessageCreate) {
  75. if selectedChannel == nil {
  76. selectedChannel = &discordgo.Channel{ID: ""}
  77. }
  78. if selectedChannel.ID != m.ChannelID {
  79. if conf.Notifications {
  80. for _, u := range m.Mentions {
  81. if u.ID == session.State.User.ID {
  82. g, err := session.State.Guild(m.GuildID)
  83. if err != nil {
  84. return
  85. }
  86. c, err := session.State.Channel(m.ChannelID)
  87. if err != nil {
  88. return
  89. }
  90. go beeep.Alert(fmt.Sprintf("%s (#%s)", g.Name, c.Name), m.ContentWithMentionsReplaced(), "")
  91. return
  92. }
  93. }
  94. }
  95. return
  96. }
  97. selectedChannel.Messages = append(selectedChannel.Messages, m.Message)
  98. renderMessage(m.Message)
  99. }
  100. type loginResponse struct {
  101. MFA bool `json:"mfa"`
  102. SMS bool `json:"sms"`
  103. Ticket string `json:"ticket"`
  104. Token string `json:"token"`
  105. }
  106. func login(email, password string) (*loginResponse, error) {
  107. data := struct {
  108. Email string `json:"email"`
  109. Password string `json:"password"`
  110. }{email, password}
  111. resp, err := session.RequestWithBucketID(
  112. "POST",
  113. discordgo.EndpointLogin,
  114. data,
  115. discordgo.EndpointLogin,
  116. )
  117. if err != nil {
  118. return nil, err
  119. }
  120. var lr loginResponse
  121. err = json.Unmarshal(resp, &lr)
  122. if err != nil {
  123. return nil, err
  124. }
  125. return &lr, nil
  126. }
  127. func totp(code, ticket string) (*loginResponse, error) {
  128. data := struct {
  129. Code string `json:"code"`
  130. Ticket string `json:"ticket"`
  131. }{code, ticket}
  132. e := discordgo.EndpointAuth + "mfa/totp"
  133. resp, err := session.RequestWithBucketID("POST", e, data, e)
  134. if err != nil {
  135. return nil, err
  136. }
  137. var lr loginResponse
  138. err = json.Unmarshal(resp, &lr)
  139. if err != nil {
  140. return nil, err
  141. }
  142. return &lr, nil
  143. }