diff --git a/assets/preview.png b/assets/preview.png index d2089a0..6a8bac9 100644 Binary files a/assets/preview.png and b/assets/preview.png differ diff --git a/discordo.go b/discordo.go index d5b0e46..536f4c1 100644 --- a/discordo.go +++ b/discordo.go @@ -3,14 +3,14 @@ package main import ( "context" "sort" + "strings" "github.com/99designs/keyring" + "github.com/atotto/clipboard" "github.com/diamondburned/arikawa/v3/api" "github.com/diamondburned/arikawa/v3/discord" "github.com/diamondburned/arikawa/v3/gateway" "github.com/diamondburned/arikawa/v3/session" - "github.com/diamondburned/arikawa/v3/state" - "github.com/diamondburned/arikawa/v3/state/store/defaultstore" "github.com/gdamore/tcell/v2" "github.com/rigormorrtiss/discordo/ui" "github.com/rigormorrtiss/discordo/util" @@ -20,9 +20,8 @@ import ( var ( app *tview.Application loginForm *tview.Form - guildsDropDown *tview.DropDown - channelsTreeView *tview.TreeView - channelsTreeNode *tview.TreeNode + guildsTreeView *tview.TreeView + guildsTreeNode *tview.TreeNode messagesTextView *tview.TextView messageInputField *tview.InputField mainFlex *tview.Flex @@ -30,8 +29,8 @@ var ( kr keyring.Keyring config *util.Config discordSession *session.Session - discordState *state.State - currentGuild discord.Guild + clientID discord.UserID + currentGuild gateway.GuildCreateEvent currentChannel discord.Channel ) @@ -53,17 +52,17 @@ func main() { config = util.NewConfig() app = ui.NewApp(onAppInputCapture) - guildsDropDown = ui.NewGuildsDropDown(onGuildsDropDownSelected, config.Theme) - channelsTreeNode = tview.NewTreeNode("") - channelsTreeView = ui.NewChannelsTreeView(channelsTreeNode, onChannelsTreeViewSelected, config.Theme) + guildsTreeNode = tview.NewTreeNode("") + guildsTreeView = ui.NewGuildsTreeView(guildsTreeNode, onGuildsTreeViewSelected, config.Theme) messagesTextView = ui.NewMessagesTextView(app, config.Theme) - mainFlex = ui.NewMainFlex(guildsDropDown, channelsTreeView, messagesTextView) + messageInputField = ui.NewMessageInputField(onMessageInputFieldInputCapture, discordSession, currentChannel, config.Theme) + mainFlex = ui.NewMainFlex(guildsTreeView, messagesTextView, messageInputField) token := util.GetItem(kr, "token") if token != "" { app. SetRoot(mainFlex, true). - SetFocus(guildsDropDown) + SetFocus(guildsTreeView) discordSession = newSession("", "", token) } else { @@ -79,12 +78,10 @@ func main() { func onAppInputCapture(e *tcell.EventKey) *tcell.EventKey { switch e.Name() { case "Alt+Rune[1]": - app.SetFocus(guildsDropDown) + app.SetFocus(guildsTreeView) case "Alt+Rune[2]": - app.SetFocus(channelsTreeView) - case "Alt+Rune[3]": app.SetFocus(messagesTextView) - case "Alt+Rune[4]": + case "Alt+Rune[3]": if messageInputField != nil { app.SetFocus(messageInputField) } @@ -93,6 +90,25 @@ func onAppInputCapture(e *tcell.EventKey) *tcell.EventKey { return e } +func onMessageInputFieldInputCapture(event *tcell.EventKey) *tcell.EventKey { + switch event.Key() { + case tcell.KeyEnter: + t := strings.TrimSpace(messageInputField.GetText()) + if t == "" { + return nil + } + + discordSession.SendMessage(currentChannel.ID, t) + messageInputField.SetText("") + case tcell.KeyCtrlV: + text, _ := clipboard.ReadAll() + text = messageInputField.GetText() + text + messageInputField.SetText(text) + } + + return event +} + func newSession(email string, password string, token string) *session.Session { api.UserAgent = "" + "Mozilla/5.0 (X11; Linux x86_64) " + @@ -114,19 +130,10 @@ func newSession(email string, password string, token string) *session.Session { panic(err) } - discordState = state.NewFromSession(sess, defaultstore.New()) - - sess.AddHandler(func(r *gateway.ReadyEvent) { - for i := range r.Guilds { - guildsDropDown.AddOption(r.Guilds[i].Name, nil) - } - }) - sess.AddHandler(func(g *gateway.GuildCreateEvent) { - guildsDropDown.AddOption(g.Name, nil) - }) + sess.AddHandler(onSessionReady) sess.AddHandler(func(m *gateway.MessageCreateEvent) { if currentChannel.ID == m.ChannelID { - util.WriteMessage(messagesTextView, discordState, m.Message) + util.WriteMessage(messagesTextView, clientID, m.Message) } }) if err = sess.Open(context.Background()); err != nil { @@ -136,91 +143,86 @@ func newSession(email string, password string, token string) *session.Session { return sess } -func onGuildsDropDownSelected(_ string, i int) { - channelsTreeView.SetTitle("Channels") - channelsTreeNode.ClearChildren() - messagesTextView. - Clear(). - SetTitle("") - app.SetFocus(channelsTreeView) +func onSessionReady(r *gateway.ReadyEvent) { + clientID = r.User.ID - if messageInputField != nil { - mainFlex.RemoveItem(messageInputField) - messageInputField = nil - } + for i := range r.Guilds { + g := r.Guilds[i] + gNode := tview.NewTreeNode(g.Name). + SetReference(g). + Collapse() + guildsTreeNode.AddChild(gNode) - currentGuild = discordState.Ready().Guilds[i].Guild - channels, _ := discordState.Cabinet.Channels(currentGuild.ID) - sort.SliceStable(channels, func(i, j int) bool { - return channels[i].Position < channels[j].Position - }) + sort.Slice(g.Channels, func(i, j int) bool { + return g.Channels[i].Position < g.Channels[j].Position + }) - for i := range channels { - channel := channels[i] - channelNode := tview.NewTreeNode(channel.Name). - SetReference(channel) - switch channel.Type { - case discord.GuildCategory: - channelsTreeNode.AddChild(channelNode) - case discord.GuildText, discord.GuildNews: - if channel.CategoryID == 0 || channel.CategoryID == discord.NullChannelID { - channelNode.SetText("[::d]#" + channel.Name + "[-:-:-]") - channelsTreeNode.AddChild(channelNode) + for i := range g.Channels { + c := g.Channels[i] + switch c.Type { + case discord.GuildCategory: + cNode := tview.NewTreeNode(c.Name). + SetReference(c) + gNode.AddChild(cNode) + case discord.GuildText, discord.GuildNews: + if c.CategoryID == 0 || c.CategoryID == discord.NullChannelID { + cNode := tview.NewTreeNode("[::d]#" + c.Name + "[-:-:-]"). + SetReference(c) + gNode.AddChild(cNode) + } } } } } -func onChannelsTreeViewSelected(n *tview.TreeNode) { - currentChannel = n.GetReference().(discord.Channel) - switch currentChannel.Type { - case discord.GuildCategory: - if len(n.GetChildren()) == 0 { - channels, _ := discordState.Cabinet.Channels(currentGuild.ID) - sort.SliceStable(channels, func(i, j int) bool { - return channels[i].Position < channels[j].Position - }) +func onGuildsTreeViewSelected(n *tview.TreeNode) { + switch ref := n.GetReference().(type) { + case gateway.GuildCreateEvent: + currentGuild = ref - for i := range channels { - channel := channels[i] - if (channel.Type == discord.GuildText || channel.Type == discord.GuildNews) && channel.CategoryID == currentChannel.ID { - channelNode := tview.NewTreeNode("[::d]#" + channel.Name + "[-:-:-]"). - SetReference(channel) - n.AddChild(channelNode) + n.SetExpanded(!n.IsExpanded()) + case discord.Channel: + switch ref.Type { + case discord.GuildCategory: + if len(n.GetChildren()) == 0 { + for i := range currentGuild.Channels { + c := currentGuild.Channels[i] + if (c.Type == discord.GuildText || c.Type == discord.GuildNews) && c.CategoryID == ref.ID { + cNode := tview.NewTreeNode("[::d]#" + c.Name + "[-:-:-]"). + SetReference(c) + n.AddChild(cNode) + } } + } else { + n.SetExpanded(!n.IsExpanded()) } - } else { - n.SetExpanded(!n.IsExpanded()) - } - case discord.GuildText, discord.GuildNews: - if messageInputField == nil { - messageInputField = ui.NewMessageInputField(discordSession, currentChannel, config.Theme) - mainFlex.AddItem(messageInputField, 3, 1, false) - } - app.SetFocus(messageInputField) + case discord.GuildText, discord.GuildNews: + currentChannel = ref - messagesTextView.Clear() - messagesTextView.SetTitle(currentChannel.Name) + app.SetFocus(messageInputField) + messagesTextView.Clear() + messagesTextView.SetTitle(ref.Name) - go func() { - messages, _ := discordSession.Messages(currentChannel.ID, config.GetMessagesLimit) - for i := len(messages) - 1; i >= 0; i-- { - util.WriteMessage(messagesTextView, discordState, messages[i]) - } - }() + go func() { + messages, _ := discordSession.Messages(ref.ID, config.GetMessagesLimit) + for i := len(messages) - 1; i >= 0; i-- { + util.WriteMessage(messagesTextView, clientID, messages[i]) + } + }() + } } } func onLoginFormLoginButtonSelected() { - email := loginForm.GetFormItemByLabel("Email").(*tview.InputField).GetText() - password := loginForm.GetFormItemByLabel("Password").(*tview.InputField).GetText() + email := loginForm.GetFormItem(0).(*tview.InputField).GetText() + password := loginForm.GetFormItem(1).(*tview.InputField).GetText() if email == "" || password == "" { return } app. SetRoot(mainFlex, true). - SetFocus(guildsDropDown) + SetFocus(guildsTreeView) discordSession = newSession(email, password, "") diff --git a/ui/flex.go b/ui/flex.go index a7da247..c619049 100644 --- a/ui/flex.go +++ b/ui/flex.go @@ -4,14 +4,14 @@ import ( "github.com/rivo/tview" ) -func NewMainFlex(guildsDropDown *tview.DropDown, channelsTreeView *tview.TreeView, messagesTextView *tview.TextView) *tview.Flex { - midFlex := tview.NewFlex(). - AddItem(channelsTreeView, 20, 1, false). - AddItem(messagesTextView, 0, 3, false) - mainFlex := tview.NewFlex(). +func NewMainFlex(treeV *tview.TreeView, textV *tview.TextView, i *tview.InputField) *tview.Flex { + rightFlex := tview.NewFlex(). SetDirection(tview.FlexRow). - AddItem(guildsDropDown, 3, 1, false). - AddItem(midFlex, 0, 1, false) + AddItem(textV, 0, 1, false). + AddItem(i, 3, 1, false) + mainFlex := tview.NewFlex(). + AddItem(treeV, 25, 1, false). + AddItem(rightFlex, 0, 1, false) return mainFlex } diff --git a/ui/inputfields.go b/ui/inputfields.go index 92bfafe..8fdde73 100644 --- a/ui/inputfields.go +++ b/ui/inputfields.go @@ -1,9 +1,6 @@ package ui import ( - "strings" - - "github.com/atotto/clipboard" "github.com/diamondburned/arikawa/v3/discord" "github.com/diamondburned/arikawa/v3/session" "github.com/gdamore/tcell/v2" @@ -11,37 +8,16 @@ import ( "github.com/rivo/tview" ) -func NewMessageInputField(s *session.Session, c discord.Channel, theme *util.Theme) *tview.InputField { +func NewMessageInputField(onMessageInputFieldInputCapture func(event *tcell.EventKey) *tcell.EventKey, s *session.Session, c discord.Channel, theme *util.Theme) *tview.InputField { i := tview.NewInputField() i. SetPlaceholder("Message..."). - SetFieldWidth(0). + SetPlaceholderTextColor(tcell.ColorWhite). SetFieldBackgroundColor(tcell.GetColor(theme.InputFieldBackground)). SetBackgroundColor(tcell.GetColor(theme.InputFieldBackground)). SetBorder(true). SetBorderPadding(0, 0, 1, 1). - SetInputCapture(onMessageInputFieldInputCapture(i, s, c)) + SetInputCapture(onMessageInputFieldInputCapture) return i } - -func onMessageInputFieldInputCapture(i *tview.InputField, s *session.Session, c discord.Channel) func(event *tcell.EventKey) *tcell.EventKey { - return func(event *tcell.EventKey) *tcell.EventKey { - switch event.Key() { - case tcell.KeyEnter: - t := strings.TrimSpace(i.GetText()) - if t == "" { - return nil - } - - i.SetText("") - s.SendMessage(c.ID, t) - case tcell.KeyCtrlV: - text, _ := clipboard.ReadAll() - text = i.GetText() + text - i.SetText(text) - } - - return event - } -} diff --git a/ui/treeviews.go b/ui/treeviews.go index e0eadf6..ca15f66 100644 --- a/ui/treeviews.go +++ b/ui/treeviews.go @@ -6,17 +6,18 @@ import ( "github.com/rivo/tview" ) -func NewChannelsTreeView(channelsTreeNode *tview.TreeNode, onChannelsTreeViewSelected func(node *tview.TreeNode), theme *util.Theme) *tview.TreeView { - channelsTreeView := tview.NewTreeView() +func NewGuildsTreeView(guildsTreeNode *tview.TreeNode, onGuildsTreeViewSelected func(node *tview.TreeNode), theme *util.Theme) *tview.TreeView { + guildsTreeView := tview.NewTreeView() - channelsTreeView. + guildsTreeView. SetTopLevel(1). - SetRoot(channelsTreeNode). - SetCurrentNode(channelsTreeNode). - SetSelectedFunc(onChannelsTreeViewSelected). + SetRoot(guildsTreeNode). + SetCurrentNode(guildsTreeNode). + SetSelectedFunc(onGuildsTreeViewSelected). SetBackgroundColor(tcell.GetColor(theme.TreeViewBackground)). + SetTitle("Guilds"). SetBorder(true). SetBorderPadding(0, 0, 1, 1) - return channelsTreeView + return guildsTreeView } diff --git a/util/discord.go b/util/discord.go index e894de8..f981aeb 100644 --- a/util/discord.go +++ b/util/discord.go @@ -5,16 +5,15 @@ import ( "strings" "github.com/diamondburned/arikawa/v3/discord" - "github.com/diamondburned/arikawa/v3/state" "github.com/rivo/tview" ) -func WriteMessage(v *tview.TextView, s *state.State, m discord.Message) { +func WriteMessage(v *tview.TextView, clientID discord.UserID, m discord.Message) { var b strings.Builder // $ ╭ AUTHOR_USERNAME (BOT) MESSAGE_CONTENT*linebreak* - writeReferencedMessage(&b, s, m.ReferencedMessage) + writeReferencedMessage(&b, clientID, m.ReferencedMessage) // $ AUTHOR_USERNAME (BOT)*spacee* - writeAuthor(&b, s, m.Author) + writeAuthor(&b, clientID, m.Author) // $ MESSAGE_CONTENT writeContent(&b, m.Content) // $ *space*(edited) @@ -46,8 +45,8 @@ func writeAttachments(b *strings.Builder, attachments []discord.Attachment) { } } -func writeAuthor(b *strings.Builder, s *state.State, u discord.User) { - if s.Ready().User.ID == u.ID { +func writeAuthor(b *strings.Builder, clientID discord.UserID, u discord.User) { + if clientID == u.ID { b.WriteString("[#59E3E3]") } else { b.WriteString("[#E95678]") @@ -61,13 +60,13 @@ func writeAuthor(b *strings.Builder, s *state.State, u discord.User) { } } -func writeReferencedMessage(b *strings.Builder, s *state.State, rm *discord.Message) { +func writeReferencedMessage(b *strings.Builder, clientID discord.UserID, rm *discord.Message) { if rm != nil { b.WriteRune(' ') b.WriteRune('\u256D') b.WriteRune(' ') - if s.Ready().User.ID == rm.Author.ID { + if clientID == rm.Author.ID { b.WriteString("[#59E3E3::d]") } else { b.WriteString("[#E95678::d]")