util.go 5.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234
  1. package ui
  2. import (
  3. "fmt"
  4. "strings"
  5. "github.com/ayntgl/discordgo"
  6. "github.com/ayntgl/discordo/discord"
  7. )
  8. func channelToString(c *discordgo.Channel) string {
  9. var repr string
  10. if c.Name != "" {
  11. repr = "#" + c.Name
  12. } else if len(c.Recipients) == 1 {
  13. rp := c.Recipients[0]
  14. repr = rp.Username + "#" + rp.Discriminator
  15. } else {
  16. rps := make([]string, len(c.Recipients))
  17. for i, r := range c.Recipients {
  18. rps[i] = r.Username + "#" + r.Discriminator
  19. }
  20. repr = strings.Join(rps, ", ")
  21. }
  22. return repr
  23. }
  24. func hasKeybinding(ks []string, k string) bool {
  25. for _, repr := range ks {
  26. if repr == k {
  27. return true
  28. }
  29. }
  30. return false
  31. }
  32. func buildMessage(app *App, m *discordgo.Message) []byte {
  33. var b strings.Builder
  34. switch m.Type {
  35. case discordgo.MessageTypeDefault, discordgo.MessageTypeReply:
  36. // Define a new region and assign message ID as the region ID.
  37. // Learn more:
  38. // https://pkg.go.dev/github.com/rivo/tview#hdr-Regions_and_Highlights
  39. b.WriteString("[\"")
  40. b.WriteString(m.ID)
  41. b.WriteString("\"]")
  42. // Build the message associated with crosspost, channel follow add, pin, or a reply.
  43. buildReferencedMessage(&b, m.ReferencedMessage, app.Session.State.User.ID)
  44. // Build the author of this message.
  45. buildAuthor(&b, m.Author, app.Session.State.User.ID)
  46. // Build the contents of the message.
  47. buildContent(&b, m, app.Session.State.User.ID)
  48. if m.EditedTimestamp != "" {
  49. b.WriteString(" [::d](edited)[::-]")
  50. }
  51. // Build the embeds associated with the message.
  52. buildEmbeds(&b, m.Embeds)
  53. // Build the message attachments (attached files to the message).
  54. buildAttachments(&b, m.Attachments)
  55. // Tags with no region ID ([""]) do not start new regions. They can
  56. // therefore be used to mark the end of a region.
  57. b.WriteString("[\"\"]")
  58. b.WriteByte('\n')
  59. case discordgo.MessageTypeGuildMemberJoin:
  60. b.WriteString("[#5865F2]")
  61. b.WriteString(m.Author.Username)
  62. b.WriteString("[-] joined the server.")
  63. b.WriteByte('\n')
  64. case discordgo.MessageTypeCall:
  65. b.WriteString("[#5865F2]")
  66. b.WriteString(m.Author.Username)
  67. b.WriteString("[-] started a call.")
  68. b.WriteByte('\n')
  69. case discordgo.MessageTypeChannelPinnedMessage:
  70. b.WriteString("[#5865F2]")
  71. b.WriteString(m.Author.Username)
  72. b.WriteString("[-] pinned a message.")
  73. b.WriteByte('\n')
  74. }
  75. if str := b.String(); str != "" {
  76. b := make([]byte, len(str)+1)
  77. copy(b, str)
  78. return b
  79. }
  80. return nil
  81. }
  82. func buildReferencedMessage(b *strings.Builder, rm *discordgo.Message, clientID string) {
  83. if rm != nil {
  84. b.WriteString(" ╭ ")
  85. b.WriteString("[::d]")
  86. buildAuthor(b, rm.Author, clientID)
  87. if rm.Content != "" {
  88. rm.Content = buildMentions(rm.Content, rm.Mentions, clientID)
  89. b.WriteString(discord.ParseMarkdown(rm.Content))
  90. }
  91. b.WriteString("[::-]")
  92. b.WriteByte('\n')
  93. }
  94. }
  95. func buildContent(b *strings.Builder, m *discordgo.Message, clientID string) {
  96. if m.Content != "" {
  97. m.Content = buildMentions(m.Content, m.Mentions, clientID)
  98. b.WriteString(discord.ParseMarkdown(m.Content))
  99. }
  100. }
  101. func buildEmbeds(b *strings.Builder, es []*discordgo.MessageEmbed) {
  102. for _, e := range es {
  103. if e.Type != discordgo.EmbedTypeRich {
  104. continue
  105. }
  106. var embedBuilder strings.Builder
  107. var hasHeading bool
  108. prefix := fmt.Sprintf("[#%06X]▐[-] ", e.Color)
  109. b.WriteByte('\n')
  110. embedBuilder.WriteString(prefix)
  111. if e.Author != nil {
  112. hasHeading = true
  113. embedBuilder.WriteString("[::u]")
  114. embedBuilder.WriteString(e.Author.Name)
  115. embedBuilder.WriteString("[::-]")
  116. }
  117. if e.Title != "" {
  118. hasHeading = true
  119. embedBuilder.WriteString("[::b]")
  120. embedBuilder.WriteString(e.Title)
  121. embedBuilder.WriteString("[::-]")
  122. }
  123. if e.Description != "" {
  124. if hasHeading {
  125. embedBuilder.WriteString("\n\n")
  126. }
  127. embedBuilder.WriteString(discord.ParseMarkdown(e.Description))
  128. }
  129. if len(e.Fields) != 0 {
  130. if hasHeading || e.Description != "" {
  131. embedBuilder.WriteString("\n\n")
  132. }
  133. for i, ef := range e.Fields {
  134. embedBuilder.WriteString("[::b]")
  135. embedBuilder.WriteString(ef.Name)
  136. embedBuilder.WriteString("[::-]")
  137. embedBuilder.WriteByte('\n')
  138. embedBuilder.WriteString(discord.ParseMarkdown(ef.Value))
  139. if i != len(e.Fields)-1 {
  140. embedBuilder.WriteString("\n\n")
  141. }
  142. }
  143. }
  144. if e.Footer != nil {
  145. if hasHeading {
  146. embedBuilder.WriteString("\n\n")
  147. }
  148. embedBuilder.WriteString(e.Footer.Text)
  149. }
  150. b.WriteString(strings.Replace(embedBuilder.String(), "\n", "\n"+prefix, -1))
  151. }
  152. }
  153. func buildAttachments(b *strings.Builder, as []*discordgo.MessageAttachment) {
  154. for _, a := range as {
  155. b.WriteByte('\n')
  156. b.WriteByte('[')
  157. b.WriteString(a.Filename)
  158. b.WriteString("]: ")
  159. b.WriteString(a.URL)
  160. }
  161. }
  162. func buildMentions(content string, mentions []*discordgo.User, clientID string) string {
  163. for _, mUser := range mentions {
  164. var color string
  165. if mUser.ID == clientID {
  166. color = "[:#5865F2]"
  167. } else {
  168. color = "[#EB459E]"
  169. }
  170. content = strings.NewReplacer(
  171. // <@USER_ID>
  172. "<@"+mUser.ID+">",
  173. color+"@"+mUser.Username+"[-:-]",
  174. // <@!USER_ID>
  175. "<@!"+mUser.ID+">",
  176. color+"@"+mUser.Username+"[-:-]",
  177. ).Replace(content)
  178. }
  179. return content
  180. }
  181. func buildAuthor(b *strings.Builder, u *discordgo.User, clientID string) {
  182. if u.ID == clientID {
  183. b.WriteString("[#57F287]")
  184. } else {
  185. b.WriteString("[#ED4245]")
  186. }
  187. b.WriteString(u.Username)
  188. b.WriteString("[-] ")
  189. // If the message author is a bot account, render the message with bot label
  190. // for distinction.
  191. if u.Bot {
  192. b.WriteString("[#EB459E]BOT[-] ")
  193. }
  194. }