Эх сурвалжийг харах

refactor: move renderer functionality to discord.go

ayntgl 4 жил өмнө
parent
commit
083eda48bf
4 өөрчлөгдсөн 181 нэмэгдсэн , 207 устгасан
  1. 127 0
      discord.go
  2. 37 5
      main.go
  3. 0 132
      renderer.go
  4. 17 70
      ui.go

+ 127 - 0
discord.go

@@ -3,7 +3,9 @@ package main
 import (
 	"encoding/json"
 	"fmt"
+	"regexp"
 	"sort"
+	"strings"
 
 	"github.com/ayntgl/discordgo"
 	"github.com/gen2brain/beeep"
@@ -15,6 +17,12 @@ var (
 	selectedChannel *discordgo.Channel
 	selectedMessage *discordgo.Message
 )
+var (
+	boldRegex          = regexp.MustCompile(`(?m)\*\*(.*?)\*\*`)
+	italicRegex        = regexp.MustCompile(`(?m)\*(.*?)\*`)
+	underlineRegex     = regexp.MustCompile(`(?m)__(.*?)__`)
+	strikeThroughRegex = regexp.MustCompile(`(?m)~~(.*?)~~`)
+)
 
 func newSession() *discordgo.Session {
 	s, err := discordgo.New()
@@ -194,3 +202,122 @@ func totp(code, ticket string) (*loginResponse, error) {
 
 	return &lr, nil
 }
+
+func renderMessage(m *discordgo.Message) {
+	var b strings.Builder
+
+	switch m.Type {
+	case discordgo.MessageTypeDefault, discordgo.MessageTypeReply:
+		// Define a new region and assign message ID as the region ID.
+		// Learn more:
+		// https://pkg.go.dev/github.com/rivo/tview#hdr-Regions_and_Highlights
+		b.WriteString("[\"")
+		b.WriteString(m.ID)
+		b.WriteString("\"]")
+		// Render the message associated with crosspost, channel follow add,
+		// pin, or a reply.
+		if rm := m.ReferencedMessage; rm != nil {
+			b.WriteString(" ╭ ")
+			b.WriteString("[::d]")
+			parseAuthor(&b, rm.Author)
+
+			if rm.Content != "" {
+				rm.Content = parseMentions(rm.Content, rm.Mentions)
+				b.WriteString(parseMarkdown(rm.Content))
+			}
+
+			b.WriteString("[::-]")
+			b.WriteByte('\n')
+		}
+
+		// Render the author of the message.
+		parseAuthor(&b, m.Author)
+		// If the message content is not empty, parse the message mentions
+		// (users mentioned in the message) and render the message content.
+		if m.Content != "" {
+			m.Content = parseMentions(m.Content, m.Mentions)
+			b.WriteString(parseMarkdown(m.Content))
+		}
+		// If the edited timestamp of the message is not empty; it implies that
+		// the message has been edited, hence render the message with edited
+		// label for distinction
+		if m.EditedTimestamp != "" {
+			b.WriteString(" [::d](edited)[::-]")
+		}
+		// TODO: render message embeds
+		for range m.Embeds {
+			b.WriteString("\n<EMBED>")
+		}
+		// Render the message attachments (attached files to the message).
+		for _, a := range m.Attachments {
+			b.WriteString("\n[")
+			b.WriteString(a.Filename)
+			b.WriteString("]: ")
+			b.WriteString(a.URL)
+		}
+		// Tags with no region ID ([""]) do not start new regions. They can
+		// therefore be used to mark the end of a region.
+		b.WriteString("[\"\"]")
+		b.WriteByte('\n')
+	case discordgo.MessageTypeGuildMemberJoin:
+		b.WriteString("[#5865F2]")
+		b.WriteString(m.Author.Username)
+		b.WriteString("[-] joined the server")
+		b.WriteByte('\n')
+	}
+
+	if str := b.String(); str != "" {
+		b := make([]byte, len(str)+1)
+		copy(b, str)
+
+		messagesView.Write(b)
+	}
+}
+
+func parseMentions(content string, mentions []*discordgo.User) string {
+	for _, mUser := range mentions {
+		var color string
+		if mUser.ID == session.State.User.ID {
+			color = "[:#5865F2]"
+		} else {
+			color = "[#EB459E]"
+		}
+
+		content = strings.NewReplacer(
+			// <@USER_ID>
+			"<@"+mUser.ID+">",
+			color+"@"+mUser.Username+"[-:-]",
+			// <@!USER_ID>
+			"<@!"+mUser.ID+">",
+			color+"@"+mUser.Username+"[-:-]",
+		).Replace(content)
+	}
+
+	return content
+}
+
+func parseAuthor(b *strings.Builder, u *discordgo.User) {
+	if u.ID == session.State.User.ID {
+		b.WriteString("[#57F287]")
+	} else {
+		b.WriteString("[#ED4245]")
+	}
+
+	b.WriteString(u.Username)
+	b.WriteString("[-] ")
+	// If the message author is a bot account, render the message with bot label
+	// for distinction.
+	if u.Bot {
+		b.WriteString("[#EB459E]BOT[-] ")
+	}
+}
+
+func parseMarkdown(md string) string {
+	var res string
+	res = boldRegex.ReplaceAllString(md, "[::b]$1[::-]")
+	res = italicRegex.ReplaceAllString(res, "[::i]$1[::-]")
+	res = underlineRegex.ReplaceAllString(res, "[::u]$1[::-]")
+	res = strikeThroughRegex.ReplaceAllString(res, "[::s]$1[::-]")
+
+	return res
+}

+ 37 - 5
main.go

@@ -23,14 +23,46 @@ func main() {
 	tview.Styles.InverseTextColor = tcell.GetColor(conf.Theme.Text.Inverse)
 	tview.Styles.ContrastSecondaryTextColor = tcell.GetColor(conf.Theme.Text.ContrastSecondary)
 
-	app = newApplication()
-	channelsTree = newChannelsTree()
-	messagesTextView = newMessagesTextView()
-	messageInputField = newMessageInputField()
+	app = tview.NewApplication()
+	app.
+		EnableMouse(conf.Mouse).
+		SetInputCapture(onAppInputCapture)
+
+	channelsTree = tview.NewTreeView()
+	channelsTree.
+		SetSelectedFunc(onChannelsTreeSelected).
+		SetTopLevel(1).
+		SetRoot(tview.NewTreeNode("")).
+		SetBorder(true).
+		SetBorderPadding(0, 0, 1, 0)
+
+	messagesView = tview.NewTextView()
+	messagesView.
+		SetRegions(true).
+		SetDynamicColors(true).
+		SetWordWrap(true).
+		ScrollToEnd().
+		SetChangedFunc(func() {
+			app.Draw()
+		}).
+		SetInputCapture(onMessagesTextViewInputCapture).
+		SetBorder(true).
+		SetBorderPadding(0, 0, 1, 0).
+		SetTitleAlign(tview.AlignLeft)
+
+	messageInputField = tview.NewInputField()
+	messageInputField.
+		SetPlaceholder("Message...").
+		SetPlaceholderTextColor(tcell.ColorWhite).
+		SetFieldBackgroundColor(tview.Styles.PrimitiveBackgroundColor).
+		SetInputCapture(onMessageInputFieldInputCapture).
+		SetBorder(true).
+		SetBorderPadding(0, 0, 1, 0).
+		SetTitleAlign(tview.AlignLeft)
 
 	rightFlex := tview.NewFlex().
 		SetDirection(tview.FlexRow).
-		AddItem(messagesTextView, 0, 1, false).
+		AddItem(messagesView, 0, 1, false).
 		AddItem(messageInputField, 3, 1, false)
 	mainFlex = tview.NewFlex().
 		AddItem(channelsTree, 0, 1, false).

+ 0 - 132
renderer.go

@@ -1,132 +0,0 @@
-package main
-
-import (
-	"regexp"
-	"strings"
-
-	"github.com/ayntgl/discordgo"
-)
-
-var boldRegex = regexp.MustCompile(`(?m)\*\*(.*?)\*\*`)
-var italicRegex = regexp.MustCompile(`(?m)\*(.*?)\*`)
-var underlineRegex = regexp.MustCompile(`(?m)__(.*?)__`)
-var strikeThroughRegex = regexp.MustCompile(`(?m)~~(.*?)~~`)
-
-func renderMessage(m *discordgo.Message) {
-	var b strings.Builder
-
-	switch m.Type {
-	case discordgo.MessageTypeDefault, discordgo.MessageTypeReply:
-		// Define a new region and assign message ID as the region ID.
-		// Learn more:
-		// https://pkg.go.dev/github.com/rivo/tview#hdr-Regions_and_Highlights
-		b.WriteString("[\"")
-		b.WriteString(m.ID)
-		b.WriteString("\"]")
-		// Render the message associated with crosspost, channel follow add,
-		// pin, or a reply.
-		if rm := m.ReferencedMessage; rm != nil {
-			b.WriteString(" ╭ ")
-			b.WriteString("[::d]")
-			parseAuthor(&b, rm.Author)
-
-			if rm.Content != "" {
-				rm.Content = parseMentions(rm.Content, rm.Mentions)
-				b.WriteString(parseMarkdown(rm.Content))
-			}
-
-			b.WriteString("[::-]")
-			b.WriteByte('\n')
-		}
-
-		// Render the author of the message.
-		parseAuthor(&b, m.Author)
-		// If the message content is not empty, parse the message mentions
-		// (users mentioned in the message) and render the message content.
-		if m.Content != "" {
-			m.Content = parseMentions(m.Content, m.Mentions)
-			b.WriteString(parseMarkdown(m.Content))
-		}
-		// If the edited timestamp of the message is not empty; it implies that
-		// the message has been edited, hence render the message with edited
-		// label for distinction
-		if m.EditedTimestamp != "" {
-			b.WriteString(" [::d](edited)[::-]")
-		}
-		// TODO: render message embeds
-		for range m.Embeds {
-			b.WriteString("\n<EMBED>")
-		}
-		// Render the message attachments (attached files to the message).
-		for _, a := range m.Attachments {
-			b.WriteString("\n[")
-			b.WriteString(a.Filename)
-			b.WriteString("]: ")
-			b.WriteString(a.URL)
-		}
-		// Tags with no region ID ([""]) do not start new regions. They can
-		// therefore be used to mark the end of a region.
-		b.WriteString("[\"\"]")
-		b.WriteByte('\n')
-	case discordgo.MessageTypeGuildMemberJoin:
-		b.WriteString("[#5865F2]")
-		b.WriteString(m.Author.Username)
-		b.WriteString("[-] joined the server")
-		b.WriteByte('\n')
-	}
-
-	if str := b.String(); str != "" {
-		b := make([]byte, len(str)+1)
-		copy(b, str)
-
-		messagesTextView.Write(b)
-	}
-}
-
-func parseMentions(content string, mentions []*discordgo.User) string {
-	for _, mUser := range mentions {
-		var color string
-		if mUser.ID == session.State.User.ID {
-			color = "[:#5865F2]"
-		} else {
-			color = "[#EB459E]"
-		}
-
-		content = strings.NewReplacer(
-			// <@USER_ID>
-			"<@"+mUser.ID+">",
-			color+"@"+mUser.Username+"[-:-]",
-			// <@!USER_ID>
-			"<@!"+mUser.ID+">",
-			color+"@"+mUser.Username+"[-:-]",
-		).Replace(content)
-	}
-
-	return content
-}
-
-func parseAuthor(b *strings.Builder, u *discordgo.User) {
-	if u.ID == session.State.User.ID {
-		b.WriteString("[#57F287]")
-	} else {
-		b.WriteString("[#ED4245]")
-	}
-
-	b.WriteString(u.Username)
-	b.WriteString("[-] ")
-	// If the message author is a bot account, render the message with bot label
-	// for distinction.
-	if u.Bot {
-		b.WriteString("[#EB459E]BOT[-] ")
-	}
-}
-
-func parseMarkdown(md string) string {
-	var res string
-	res = boldRegex.ReplaceAllString(md, "[::b]$1[::-]")
-	res = italicRegex.ReplaceAllString(res, "[::i]$1[::-]")
-	res = underlineRegex.ReplaceAllString(res, "[::u]$1[::-]")
-	res = strikeThroughRegex.ReplaceAllString(res, "[::s]$1[::-]")
-
-	return res
-}

+ 17 - 70
ui.go

@@ -13,26 +13,17 @@ var (
 	app               *tview.Application
 	loginForm         *tview.Form
 	channelsTree      *tview.TreeView
-	messagesTextView  *tview.TextView
+	messagesView      *tview.TextView
 	messageInputField *tview.InputField
 	mainFlex          *tview.Flex
 )
 
-func newApplication() *tview.Application {
-	a := tview.NewApplication()
-	a.
-		EnableMouse(conf.Mouse).
-		SetInputCapture(onAppInputCapture)
-
-	return a
-}
-
 func onAppInputCapture(e *tcell.EventKey) *tcell.EventKey {
 	switch e.Name() {
 	case conf.Keybindings.ChannelsTree.Focus:
 		app.SetFocus(channelsTree)
 	case conf.Keybindings.MessagesTextView.Focus:
-		app.SetFocus(messagesTextView)
+		app.SetFocus(messagesView)
 	case conf.Keybindings.MessageInputField.Focus:
 		app.SetFocus(messageInputField)
 	}
@@ -40,27 +31,15 @@ func onAppInputCapture(e *tcell.EventKey) *tcell.EventKey {
 	return e
 }
 
-func newChannelsTree() *tview.TreeView {
-	channelsTree := tview.NewTreeView()
-	channelsTree.
-		SetSelectedFunc(onChannelsTreeSelected).
-		SetTopLevel(1).
-		SetRoot(tview.NewTreeNode("")).
-		SetBorder(true).
-		SetBorderPadding(0, 0, 1, 0)
-
-	return channelsTree
-}
-
 func onChannelsTreeSelected(n *tview.TreeNode) {
 	selectedChannel = nil
 	selectedMessage = nil
-	messagesTextView.
+	messagesView.
 		Clear().
 		SetTitle("")
 	messageInputField.SetText("")
 	// Unhighlight the already-highlighted regions.
-	messagesTextView.Highlight()
+	messagesView.Highlight()
 
 	if len(n.GetChildren()) != 0 || n.GetText() == "Direct Messages" {
 		n.SetExpanded(!n.IsExpanded())
@@ -81,16 +60,16 @@ func onChannelsTreeSelected(n *tview.TreeNode) {
 				title += " - " + c.Topic
 			}
 
-			messagesTextView.SetTitle(title)
+			messagesView.SetTitle(title)
 		case discordgo.ChannelTypeDM, discordgo.ChannelTypeGroupDM:
-			messagesTextView.SetTitle(generateChannelRepr(c))
+			messagesView.SetTitle(generateChannelRepr(c))
 		}
 
 		if strings.HasPrefix(n.GetText(), "[::b]") {
 			n.SetText("[::d]" + generateChannelRepr(c) + "[::-]")
 		}
 
-		messagesTextView.Clear()
+		messagesView.Clear()
 
 		go func() {
 			ms, err := session.ChannelMessages(cID, conf.GetMessagesLimit, "", "", "")
@@ -204,24 +183,6 @@ func getTreeNodeByReference(r interface{}) (mn *tview.TreeNode) {
 	return
 }
 
-func newMessagesTextView() *tview.TextView {
-	w := tview.NewTextView()
-	w.
-		SetRegions(true).
-		SetDynamicColors(true).
-		SetWordWrap(true).
-		ScrollToEnd().
-		SetChangedFunc(func() {
-			app.Draw()
-		}).
-		SetInputCapture(onMessagesTextViewInputCapture).
-		SetBorder(true).
-		SetBorderPadding(0, 0, 1, 0).
-		SetTitleAlign(tview.AlignLeft)
-
-	return w
-}
-
 func onMessagesTextViewInputCapture(e *tcell.EventKey) *tcell.EventKey {
 	if selectedChannel == nil {
 		return nil
@@ -234,9 +195,9 @@ func onMessagesTextViewInputCapture(e *tcell.EventKey) *tcell.EventKey {
 			return nil
 		}
 
-		hs := messagesTextView.GetHighlights()
+		hs := messagesView.GetHighlights()
 		if len(hs) == 0 {
-			messagesTextView.
+			messagesView.
 				Highlight(ms[len(ms)-1].ID).
 				ScrollToHighlight()
 		} else {
@@ -245,7 +206,7 @@ func onMessagesTextViewInputCapture(e *tcell.EventKey) *tcell.EventKey {
 				return nil
 			}
 
-			messagesTextView.
+			messagesView.
 				Highlight(ms[idx-1].ID).
 				ScrollToHighlight()
 		}
@@ -257,9 +218,9 @@ func onMessagesTextViewInputCapture(e *tcell.EventKey) *tcell.EventKey {
 			return nil
 		}
 
-		hs := messagesTextView.GetHighlights()
+		hs := messagesView.GetHighlights()
 		if len(hs) == 0 {
-			messagesTextView.
+			messagesView.
 				Highlight(ms[len(ms)-1].ID).
 				ScrollToHighlight()
 		} else {
@@ -268,7 +229,7 @@ func onMessagesTextViewInputCapture(e *tcell.EventKey) *tcell.EventKey {
 				return nil
 			}
 
-			messagesTextView.
+			messagesView.
 				Highlight(ms[idx+1].ID).
 				ScrollToHighlight()
 		}
@@ -280,7 +241,7 @@ func onMessagesTextViewInputCapture(e *tcell.EventKey) *tcell.EventKey {
 			return nil
 		}
 
-		messagesTextView.
+		messagesView.
 			Highlight(ms[0].ID).
 			ScrollToHighlight()
 	case conf.Keybindings.MessagesTextView.SelectLast:
@@ -289,7 +250,7 @@ func onMessagesTextViewInputCapture(e *tcell.EventKey) *tcell.EventKey {
 			return nil
 		}
 
-		messagesTextView.
+		messagesView.
 			Highlight(ms[len(ms)-1].ID).
 			ScrollToHighlight()
 	case conf.Keybindings.MessagesTextView.Reply:
@@ -298,7 +259,7 @@ func onMessagesTextViewInputCapture(e *tcell.EventKey) *tcell.EventKey {
 			return nil
 		}
 
-		hs := messagesTextView.GetHighlights()
+		hs := messagesView.GetHighlights()
 		if len(hs) == 0 {
 			return nil
 		}
@@ -314,7 +275,7 @@ func onMessagesTextViewInputCapture(e *tcell.EventKey) *tcell.EventKey {
 			return nil
 		}
 
-		hs := messagesTextView.GetHighlights()
+		hs := messagesView.GetHighlights()
 		if len(hs) == 0 {
 			return nil
 		}
@@ -327,20 +288,6 @@ func onMessagesTextViewInputCapture(e *tcell.EventKey) *tcell.EventKey {
 	return e
 }
 
-func newMessageInputField() *tview.InputField {
-	w := tview.NewInputField()
-	w.
-		SetPlaceholder("Message...").
-		SetPlaceholderTextColor(tcell.ColorWhite).
-		SetFieldBackgroundColor(tview.Styles.PrimitiveBackgroundColor).
-		SetInputCapture(onMessageInputFieldInputCapture).
-		SetBorder(true).
-		SetBorderPadding(0, 0, 1, 0).
-		SetTitleAlign(tview.AlignLeft)
-
-	return w
-}
-
 func onMessageInputFieldInputCapture(e *tcell.EventKey) *tcell.EventKey {
 	// If the "Alt" modifier key is pressed, do not handle the event.
 	if e.Modifiers() == tcell.ModAlt {