message_actions_list.go 5.1 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219
  1. package ui
  2. import (
  3. "io"
  4. "net/http"
  5. "os"
  6. "path/filepath"
  7. "regexp"
  8. "github.com/atotto/clipboard"
  9. "github.com/diamondburned/arikawa/v3/discord"
  10. "github.com/rivo/tview"
  11. "github.com/skratchdot/open-golang/open"
  12. )
  13. var linkRegex = regexp.MustCompile("https?://.+")
  14. type MessageActionsList struct {
  15. *tview.List
  16. app *App
  17. message *discord.Message
  18. }
  19. func NewMessageActionsList(app *App, m *discord.Message) *MessageActionsList {
  20. mal := &MessageActionsList{
  21. List: tview.NewList(),
  22. app: app,
  23. message: m,
  24. }
  25. mal.ShowSecondaryText(false)
  26. mal.SetDoneFunc(func() {
  27. app.
  28. SetRoot(app.MainFlex, true).
  29. SetFocus(app.MessagesPanel)
  30. })
  31. // If the client user has the `SEND_MESSAGES` permission, add "Reply" and "Mention Reply" actions.
  32. if hasPermission(app.State, app.ChannelsTree.SelectedChannel.ID, discord.PermissionSendMessages) {
  33. mal.AddItem("Reply", "", 'r', mal.replyAction)
  34. mal.AddItem("Mention Reply", "", 'R', mal.mentionReplyAction)
  35. }
  36. // If the referenced message exists, add a new action to select the reply.
  37. if m.ReferencedMessage != nil {
  38. mal.AddItem("Select Reply", "", 'm', mal.selectReplyAction)
  39. }
  40. // If the content of the message contains link(s), add the appropriate actions to the list.
  41. links := linkRegex.FindAllString(m.Content, -1)
  42. if len(links) != 0 {
  43. mal.AddItem("Open Link", "", 'l', func() {
  44. for _, l := range links {
  45. go open.Run(l)
  46. }
  47. app.SetRoot(app.MainFlex, true)
  48. app.SetFocus(app.MessagesPanel)
  49. })
  50. }
  51. // If the message contains attachments, add the appropriate actions to the actions list.
  52. if len(m.Attachments) != 0 {
  53. mal.AddItem("Open Attachment", "", 'o', mal.openAttachmentAction)
  54. mal.AddItem("Download Attachment", "", 'd', mal.downloadAttachmentAction)
  55. }
  56. // If the client user has the `MANAGE_MESSAGES` permission, add a new action to delete the message.
  57. if hasPermission(app.State, app.ChannelsTree.SelectedChannel.ID, discord.PermissionManageMessages) {
  58. mal.AddItem("Delete", "", 'd', mal.deleteAction)
  59. }
  60. mal.AddItem("Copy Content", "", 'c', mal.copyContentAction)
  61. mal.AddItem("Copy ID", "", 'i', mal.copyIDAction)
  62. mal.SetTitle("Press the Escape key to close")
  63. mal.SetTitleAlign(tview.AlignLeft)
  64. mal.SetBorder(true)
  65. mal.SetBorderPadding(0, 0, 1, 1)
  66. return mal
  67. }
  68. func (mal *MessageActionsList) replyAction() {
  69. mal.app.MessageInputField.SetTitle("Replying to " + mal.message.Author.Tag())
  70. mal.app.
  71. SetRoot(mal.app.MainFlex, true).
  72. SetFocus(mal.app.MessageInputField)
  73. }
  74. func (mal *MessageActionsList) mentionReplyAction() {
  75. mal.app.MessageInputField.SetTitle("[@] Replying to " + mal.message.Author.Tag())
  76. mal.app.
  77. SetRoot(mal.app.MainFlex, true).
  78. SetFocus(mal.app.MessageInputField)
  79. }
  80. func (mal *MessageActionsList) selectReplyAction() {
  81. ms, err := mal.app.State.Cabinet.Messages(mal.message.ChannelID)
  82. if err != nil {
  83. return
  84. }
  85. mal.app.MessagesPanel.SelectedMessage, _ = findMessageByID(ms, mal.message.ReferencedMessage.ID)
  86. mal.app.MessagesPanel.
  87. Highlight(mal.message.ReferencedMessage.ID.String()).
  88. ScrollToHighlight()
  89. mal.app.
  90. SetRoot(mal.app.MainFlex, true).
  91. SetFocus(mal.app.MessagesPanel)
  92. }
  93. func (mal *MessageActionsList) openAttachmentAction() {
  94. for _, a := range mal.message.Attachments {
  95. cacheDirPath, _ := os.UserCacheDir()
  96. f, err := os.Create(filepath.Join(cacheDirPath, a.Filename))
  97. if err != nil {
  98. return
  99. }
  100. defer f.Close()
  101. resp, err := http.Get(a.URL)
  102. if err != nil {
  103. return
  104. }
  105. d, err := io.ReadAll(resp.Body)
  106. if err != nil {
  107. return
  108. }
  109. f.Write(d)
  110. go open.Run(f.Name())
  111. }
  112. mal.app.
  113. SetRoot(mal.app.MainFlex, true).
  114. SetFocus(mal.app.MessagesPanel)
  115. }
  116. func (mal *MessageActionsList) downloadAttachmentAction() {
  117. for _, a := range mal.message.Attachments {
  118. f, err := os.Create(filepath.Join(mal.app.Config.AttachmentDownloadsDir, a.Filename))
  119. if err != nil {
  120. return
  121. }
  122. defer f.Close()
  123. resp, err := http.Get(a.URL)
  124. if err != nil {
  125. return
  126. }
  127. d, err := io.ReadAll(resp.Body)
  128. if err != nil {
  129. return
  130. }
  131. f.Write(d)
  132. }
  133. mal.app.
  134. SetRoot(mal.app.MainFlex, true).
  135. SetFocus(mal.app.MessagesPanel)
  136. }
  137. func (mal *MessageActionsList) deleteAction() {
  138. mal.app.MessagesPanel.Clear()
  139. err := mal.app.State.MessageRemove(mal.message.ChannelID, mal.message.ID)
  140. if err != nil {
  141. return
  142. }
  143. err = mal.app.State.DeleteMessage(mal.message.ChannelID, mal.message.ID, "Unknown")
  144. if err != nil {
  145. return
  146. }
  147. // The returned slice will be sorted from latest to oldest.
  148. ms, err := mal.app.State.Cabinet.Messages(mal.message.ChannelID)
  149. if err != nil {
  150. return
  151. }
  152. for i := len(ms) - 1; i >= 0; i-- {
  153. _, err = mal.app.MessagesPanel.Write(buildMessage(mal.app, ms[i]))
  154. if err != nil {
  155. return
  156. }
  157. }
  158. mal.app.
  159. SetRoot(mal.app.MainFlex, true).
  160. SetFocus(mal.app.MessagesPanel)
  161. }
  162. func (mal *MessageActionsList) copyContentAction() {
  163. err := clipboard.WriteAll(mal.message.Content)
  164. if err != nil {
  165. return
  166. }
  167. mal.app.SetRoot(mal.app.MainFlex, true)
  168. mal.app.SetFocus(mal.app.MessagesPanel)
  169. }
  170. func (mal *MessageActionsList) copyIDAction() {
  171. err := clipboard.WriteAll(mal.message.ID.String())
  172. if err != nil {
  173. return
  174. }
  175. mal.app.SetRoot(mal.app.MainFlex, true)
  176. mal.app.SetFocus(mal.app.MessagesPanel)
  177. }