diff --git a/README.md b/README.md index c48cce5..c635a17 100644 --- a/README.md +++ b/README.md @@ -73,7 +73,7 @@ sudo mv ./discordo /usr/local/bin ### Configuration -A default configuration file is created on first start-up at `$HOME/.config/discordo/config.lua` on Unix, `$HOME/Library/Application Support/discordo/config.lua` on Darwin, and `%AppData%/discordo/config.lua` on Windows. You can configure the default configuration directory path using the `config` command-line flag (eg: `discordo --config $HOME/my-custom-dir). +A default configuration file is created on first start-up at `$HOME/.config/discordo/config.yml` on Unix, `$HOME/Library/Application Support/discordo/config.yml` on Darwin, and `%AppData%/discordo/config.yml` on Windows. You can configure the default configuration directory path using the `config` command-line flag (eg: `discordo --config $HOME/my-custom-dir). ## Disclaimer diff --git a/config/config.go b/config/config.go index 72ea15a..904220f 100644 --- a/config/config.go +++ b/config/config.go @@ -2,78 +2,114 @@ package config import ( _ "embed" - "io" "os" "path/filepath" + "time" - lua "github.com/yuin/gopher-lua" + "gopkg.in/yaml.v3" ) const Name = "discordo" -//go:embed config.lua -var LuaConfig []byte +type MessagesPanelKeysConfig struct { + OpenActionsList string `yaml:"open_actions_list"` -// Config initializes a new Lua state, loads a configuration file, and defines essential micellaneous fields. -type Config struct { - // Path is the path of the configuration file. Its value is the configuration directory until Load() is called. - Path string - State *lua.LState + SelectPreviousMessage string `yaml:"select_previous_message"` + SelectNextMessage string `yaml:"select_next_message"` + SelectFirstMessage string `yaml:"select_first_message"` + SelectLastMessage string `yaml:"select_last_message"` } -func New(path string) *Config { +type MessageInputKeysConfig struct { + OpenExternalEditor string `yaml:"open_external_editor"` + PasteClipboard string `yaml:"paste_clipboard"` +} + +type KeysConfig struct { + MessagesPanel MessagesPanelKeysConfig `yaml:"messages_panel"` + MessageInput MessageInputKeysConfig `yaml:"message_input"` +} + +type ThemeConfig struct { + Background string `yaml:"background"` + Border string `yaml:"border"` + Title string `yaml:"title"` +} + +type Config struct { + // Whether the mouse is usable or not. + Mouse bool `yaml:"mouse"` + // The maximum number of messages to fetch and display on the messages panel. Its value must not be lesser than 1 and greater than 100. + MessagesLimit uint `yaml:"messages_limit"` + // Whether to display the timestamps of the messages beside the displayed message or not. + Timestamps bool `yaml:"timestamps"` + // The timezone of the timestamps. Learn more: https://pkg.go.dev/time#LoadLocation + Timezone string `yaml:"timezone"` + // A textual representation of the time value formatted according to the layout defined by its value. Learn more: https://pkg.go.dev/time#Layout + TimeFormat string `yaml:"time_format"` + // Keybindings + Keys KeysConfig `yaml:"keys"` + // Theme + Theme ThemeConfig `yaml:"theme"` +} + +func New() *Config { return &Config{ - Path: path, - State: lua.NewState(), + Mouse: true, + MessagesLimit: 50, + + Timestamps: false, + Timezone: "Local", + TimeFormat: time.Kitchen, + + Keys: KeysConfig{ + MessagesPanel: MessagesPanelKeysConfig{ + OpenActionsList: "Rune[a]", + + SelectPreviousMessage: "Up", + SelectNextMessage: "Down", + SelectFirstMessage: "Home", + SelectLastMessage: "End", + }, + }, + Theme: ThemeConfig{ + Background: "default", + Border: "white", + Title: "white", + }, } } func (c *Config) Load() error { - // Create directories that do not exist and are mentioned in the path recursively. - err := os.MkdirAll(c.Path, os.ModePerm) + configPath, err := os.UserConfigDir() if err != nil { return err } - c.Path = filepath.Join(c.Path, "config.lua") - // Open the existing configuration file with read-only flag. - f, err := os.Open(c.Path) - // If the configuration file does not exist, create a new configuration file with the read-write flag. - if os.IsNotExist(err) { - f, err = os.Create(c.Path) - if err != nil { - return err - } - defer f.Close() - - _, err = f.Write(LuaConfig) - if err != nil { - return err - } - - return f.Sync() + configPath = filepath.Join(configPath, Name) + // Create directories that do not exist and are mentioned in the path recursively. + err = os.MkdirAll(configPath, os.ModePerm) + if err != nil { + return err } + configPath = filepath.Join(configPath, "config.yml") + // Open the existing configuration file with read-only flag. + f, err := os.OpenFile(configPath, os.O_CREATE|os.O_RDWR, os.ModePerm) if err != nil { return err } defer f.Close() - b, err := io.ReadAll(f) + fi, err := f.Stat() if err != nil { return err } - LuaConfig = b - return nil -} + // If the configuration file is empty (the size of the file is zero; a new configuration file was created), write the default configuration to the file. + if fi.Size() == 0 { + return yaml.NewEncoder(f).Encode(c) + } -func (c *Config) KeyLua(s *lua.LState) int { - keyTable := s.NewTable() - keyTable.RawSetString("name", s.Get(1)) - keyTable.RawSetString("description", s.Get(2)) - keyTable.RawSetString("action", s.Get(3)) - - s.Push(keyTable) // Push the result - return 1 // Number of results + return yaml.NewDecoder(f).Decode(&c) } diff --git a/config/config.lua b/config/config.lua deleted file mode 100644 index 1cdfbfd..0000000 --- a/config/config.lua +++ /dev/null @@ -1,107 +0,0 @@ -local string = require "string" - --- Whether the mouse is usable or not. -mouse = true - --- The maximum number of messages to fetch and display on the messages panel. --- Its value must not be lesser than 1 and greater than 100. -messagesLimit = 50 - --- Whether to display the timestamp of the message beside the displayed message or not. -timestamps = false - --- The timezone of the timestamps. --- Learn more: https://pkg.go.dev/time#LoadLocation -timezone = "Local" - --- A textual representation of the time value formatted according to the layout defined by its value. --- Learn more: https://pkg.go.dev/time#Layout -timeFormat = "3:04PM" - -browser = "Chrome" -browserVersion = "104.0.5112.102" -oss = "Linux" - --- Identify properties are connection properties that are dispatched in the IDENTIFY gateway event to trigger the initial handshake with the gateway. --- Learn more: https://discord.com/developers/docs/topics/gateway#identify -identifyProperties = { - userAgent = string.format( - "Mozilla/5.0 (X11; %s x86_64) AppleWebKit/537.36 (KHTML, like Gecko) %s/%s Safari/537.36", - oss, - browser, - browserVersion - ), - browser = browser, - browserVersion = browserVersion, - os = oss -} - --- Keybindings -keys = { - -- application = { - -- key( - -- "Ctrl+R", - -- "Refresh the screen.", - -- function(core, event) - -- core.Application:Sync() - -- return nil - -- end - -- ) - -- }, - messagesPanel = { - key( - "Rune[a]", - "Open the message actions list widget.", - function(core, event) - return openMessageActionsList() - end - ), - key( - "Up", - "Select the previous message.", - function(core, event) - return selectPreviousMessage() - end - ), - key( - "Down", - "Select the next message.", - function(core, event) - return selectNextMessage() - end - ), - key( - "Home", - "Select the first message.", - function(core, event) - return selectFirstMessage() - end - ), - key( - "End", - "Select the last message.", - function(core, event) - return selectLastMessage() - end - ) - }, - messageInput = { - key( - "Ctrl+E", - "Open the external editor.", - function() - return openExternalEditor() - end - ), - key( - "Ctrl+V", - "Paste the clipboard content.", - function() - return pasteClipboardContent() - end - ) - } -} - --- Theme -theme = {background = "default", border = "white", title = "white"} diff --git a/go.mod b/go.mod index dac8d72..c08f00e 100644 --- a/go.mod +++ b/go.mod @@ -8,9 +8,8 @@ require ( github.com/gdamore/tcell/v2 v2.5.3 github.com/rivo/tview v0.0.0-20220906194528-4664d8bf22d9 github.com/skratchdot/open-golang v0.0.0-20200116055534-eef842397966 - github.com/yuin/gopher-lua v0.0.0-20220504180219-658193537a64 github.com/zalando/go-keyring v0.2.1 - layeh.com/gopher-luar v1.0.10 + gopkg.in/yaml.v3 v3.0.1 ) require ( diff --git a/go.sum b/go.sum index b588f99..24e9249 100644 --- a/go.sum +++ b/go.sum @@ -2,9 +2,6 @@ github.com/alessio/shellescape v1.4.1 h1:V7yhSDDn8LP4lc4jS8pFkt0zCnzVJlG5JXy9BVK github.com/alessio/shellescape v1.4.1/go.mod h1:PZAiSCk0LJaZkiCSkPv8qIobYglO3FPpyFjDCtHLS30= github.com/atotto/clipboard v0.1.4 h1:EH0zSVneZPSuFR11BlR9YppQTVDbh5+16AmcJi4g1z4= github.com/atotto/clipboard v0.1.4/go.mod h1:ZY9tmq7sm5xIbd9bOK4onWV4S6X0u6GY7Vn0Yu86PYI= -github.com/chzyer/logex v1.1.10/go.mod h1:+Ywpsq7O8HXn0nuIou7OrIPyXbp3wmkHB+jjWRnGsAI= -github.com/chzyer/readline v0.0.0-20180603132655-2972be24d48e/go.mod h1:nSuG5e5PlCu98SY8svDHJxuZscDgtXS6KTTbou5AhLI= -github.com/chzyer/test v0.0.0-20180213035817-a1ea475d72b1/go.mod h1:Q3SI9o4m/ZMnBNeIyt5eFwwo7qiLfzFZmjNmxjkiQlU= github.com/danieljoos/wincred v1.1.0/go.mod h1:XYlo+eRTsVA9aHGp7NGjFkPla4m+DCL7hqDjlFjiygg= github.com/danieljoos/wincred v1.1.2 h1:QLdCxFs1/Yl4zduvBdcHB8goaYk9RARS2SgLLRuAyr0= github.com/danieljoos/wincred v1.1.2/go.mod h1:GijpziifJoIBfYh+S7BbkdUTU4LfM+QnGqR5Vl2tAx0= @@ -46,14 +43,10 @@ github.com/stretchr/testify v1.5.1/go.mod h1:5W2xD1RspED5o8YsWQXVCued0rvSQ+mT+I5 github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= github.com/stretchr/testify v1.7.2 h1:4jaiDzPyXQvSd7D0EjG45355tLlV3VOECpq10pLC+8s= github.com/stretchr/testify v1.7.2/go.mod h1:R6va5+xMeoiuVRoj+gSkQ7d3FALtqAAGI1FQKckRals= -github.com/yuin/gopher-lua v0.0.0-20190206043414-8bfc7677f583/go.mod h1:gqRgreBUhTSL0GeU64rtZ3Uq3wtjOa/TB2YfrtkCbVQ= -github.com/yuin/gopher-lua v0.0.0-20220504180219-658193537a64 h1:5mLPGnFdSsevFRFc9q3yYbBkB6tsm4aCwwQV/j1JQAQ= -github.com/yuin/gopher-lua v0.0.0-20220504180219-658193537a64/go.mod h1:GBR0iDaNXjAgGg9zfCvksxSRnQx76gclCIb7kdAd1Pw= github.com/zalando/go-keyring v0.2.1 h1:MBRN/Z8H4U5wEKXiD67YbDAr5cj/DOStmSga70/2qKc= github.com/zalando/go-keyring v0.2.1/go.mod h1:g63M2PPn0w5vjmEbwAX3ib5I+41zdm4esSETOn9Y6Dw= golang.org/x/crypto v0.0.0-20210921155107-089bfa567519/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc= golang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg= -golang.org/x/sys v0.0.0-20190204203706-41f3e6584952/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20210615035016-665e8c7367d1/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20210819135213-f52c844e1c1c/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= @@ -73,11 +66,10 @@ golang.org/x/time v0.0.0-20210723032227-1f47c861a9ac/go.mod h1:tRJNPiyCQ0inRvYxb golang.org/x/time v0.0.0-20220722155302-e5dcc9cfc0b9 h1:ftMN5LMiBFjbzleLqtoBZk7KdJwhuybIU+FckUHgoyQ= golang.org/x/time v0.0.0-20220722155302-e5dcc9cfc0b9/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= +gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405 h1:yhCVgyC4o1eVCa2tZl7eS0r+SDo693bJlVdllGtEeKM= gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= gopkg.in/yaml.v2 v2.4.0/go.mod h1:RDklbk79AGWmwhnvt/jBztapEOGDOx6ZbXqjP6csGnQ= gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA= gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= -layeh.com/gopher-luar v1.0.10 h1:55b0mpBhN9XSshEd2Nz6WsbYXctyBT35azk4POQNSXo= -layeh.com/gopher-luar v1.0.10/go.mod h1:TPnIVCZ2RJBndm7ohXyaqfhzjlZ+OA2SZR/YwL8tECk= diff --git a/main.go b/main.go index 3e8ee64..84be89a 100644 --- a/main.go +++ b/main.go @@ -3,8 +3,6 @@ package main import ( "flag" "log" - "os" - "path/filepath" "github.com/ayntgl/discordo/config" "github.com/ayntgl/discordo/ui" @@ -12,38 +10,24 @@ import ( "github.com/zalando/go-keyring" ) -const ( - name = "discordo" -) - -var ( - token string - configPath string -) +var token string func init() { flag.StringVar(&token, "token", "", "The client authentication token.") // If the token is provided via a command-line flag, store it in the default keyring. if token != "" { - go keyring.Set(name, "token", token) + go keyring.Set(config.Name, "token", token) } if token == "" { - token, _ = keyring.Get(name, "token") + token, _ = keyring.Get(config.Name, "token") } - - configDirPath, err := os.UserConfigDir() - if err != nil { - log.Fatal(err) - } - - flag.StringVar(&configPath, "config", filepath.Join(configDirPath, name), "The path to the configuration directory.") } func main() { flag.Parse() - cfg := config.New(configPath) + cfg := config.New() err := cfg.Load() if err != nil { log.Fatal(err) @@ -80,7 +64,7 @@ func main() { log.Fatal(err) } - go keyring.Set(name, "token", tkn) + go keyring.Set(config.Name, "token", tkn) c.DrawMainFlex() c.Application.SetRoot(c.MainFlex, true) diff --git a/ui/builder.go b/ui/builder.go index dec176c..75f351e 100644 --- a/ui/builder.go +++ b/ui/builder.go @@ -6,7 +6,6 @@ import ( "time" "github.com/diamondburned/arikawa/v3/discord" - lua "github.com/yuin/gopher-lua" ) func buildMessage(c *Core, m discord.Message) []byte { @@ -23,19 +22,14 @@ func buildMessage(c *Core, m discord.Message) []byte { // Build the message associated with crosspost, channel follow add, pin, or a reply. buildReferencedMessage(&b, m.ReferencedMessage, c.State.Ready().User.ID) - timestamps := c.Config.State.GetGlobal("timestamps") - - if lua.LVAsBool(timestamps) { - timezone := c.Config.State.GetGlobal("timezone") - loc, err := time.LoadLocation(lua.LVAsString(timezone)) + if c.Config.Timestamps { + loc, err := time.LoadLocation(c.Config.Timezone) if err != nil { return nil } - timeFormat := c.Config.State.GetGlobal("timeFormat") - b.WriteString("[::d]") - b.WriteString(m.Timestamp.Time().In(loc).Format(lua.LVAsString(timeFormat))) + b.WriteString(m.Timestamp.Time().In(loc).Format(c.Config.TimeFormat)) b.WriteString("[::-]") b.WriteByte(' ') } diff --git a/ui/channels_tree.go b/ui/channels_tree.go index 414e292..4174bb7 100644 --- a/ui/channels_tree.go +++ b/ui/channels_tree.go @@ -5,7 +5,6 @@ import ( "github.com/diamondburned/arikawa/v3/discord" "github.com/rivo/tview" - lua "github.com/yuin/gopher-lua" ) type ChannelsTree struct { @@ -64,9 +63,8 @@ func (ct *ChannelsTree) onSelected(node *tview.TreeNode) { ct.core.MessagesPanel.SetTitle(title) go func() { - messagesLimit := ct.core.Config.State.GetGlobal("messagesLimit") // The returned slice will be sorted from latest to oldest. - ms, err := ct.core.State.Messages(c.ID, uint(lua.LVAsNumber(messagesLimit))) + ms, err := ct.core.State.Messages(c.ID, ct.core.Config.MessagesLimit) if err != nil { return } diff --git a/ui/core.go b/ui/core.go index a9d08ec..ffe99a0 100644 --- a/ui/core.go +++ b/ui/core.go @@ -1,15 +1,9 @@ package ui import ( - "strings" - "github.com/ayntgl/discordo/config" - "github.com/diamondburned/arikawa/v3/discord" - "github.com/diamondburned/arikawa/v3/gateway" "github.com/gdamore/tcell/v2" "github.com/rivo/tview" - lua "github.com/yuin/gopher-lua" - luar "layeh.com/gopher-luar" ) type focused int @@ -47,7 +41,14 @@ func NewCore(cfg *config.Config) *Core { Config: cfg, } + tview.Styles.PrimitiveBackgroundColor = tcell.GetColor(cfg.Theme.Background) + tview.Styles.BorderColor = tcell.GetColor(cfg.Theme.Border) + tview.Styles.TitleColor = tcell.GetColor(cfg.Theme.Title) + + c.Application.EnableMouse(c.Config.Mouse) c.Application.SetInputCapture(c.onInputCapture) + c.Application.SetBeforeDrawFunc(c.beforeDraw) + c.GuildsTree = NewGuildsTree(c) c.ChannelsTree = NewChannelsTree(c) c.MessagesPanel = NewMessagesPanel(c) @@ -56,65 +57,10 @@ func NewCore(cfg *config.Config) *Core { } func (c *Core) Run(token string) error { - c.register() - err := c.Config.State.DoString(string(config.LuaConfig)) - if err != nil { - return err - } - - themeTable, ok := c.Config.State.GetGlobal("theme").(*lua.LTable) - if !ok { - themeTable = c.Config.State.NewTable() - } - - backgroundColor := tcell.GetColor(lua.LVAsString(themeTable.RawGetString("background"))) - borderColor := tcell.GetColor(lua.LVAsString(themeTable.RawGetString("border"))) - titleColor := tcell.GetColor(lua.LVAsString(themeTable.RawGetString("title"))) - - c.GuildsTree.SetBackgroundColor(backgroundColor) - c.GuildsTree.SetBorderColor(borderColor) - c.GuildsTree.SetTitleColor(titleColor) - - c.ChannelsTree.SetBackgroundColor(backgroundColor) - c.ChannelsTree.SetBorderColor(borderColor) - c.ChannelsTree.SetTitleColor(titleColor) - - c.MessagesPanel.SetBackgroundColor(backgroundColor) - c.MessagesPanel.SetBorderColor(borderColor) - c.MessagesPanel.SetTitleColor(titleColor) - - c.MessageInput.SetBackgroundColor(backgroundColor) - c.MessageInput.SetBorderColor(borderColor) - c.MessageInput.SetTitleColor(titleColor) - c.MessageInput.SetPlaceholderStyle(tcell.StyleDefault.Background(backgroundColor)) - - c.Application.SetBeforeDrawFunc(func(s tcell.Screen) bool { - if backgroundColor == 0 { - s.Clear() - } - - return false - }) - - c.Application.EnableMouse(lua.LVAsBool(c.Config.State.GetGlobal("mouse"))) - c.State = NewState(token, c) return c.State.Run() } -func (c *Core) register() { - c.Config.State.SetGlobal("key", c.Config.State.NewFunction(c.Config.KeyLua)) - // Messages panel - c.Config.State.SetGlobal("openMessageActionsList", c.Config.State.NewFunction(c.MessagesPanel.openMessageActionsListLua)) - c.Config.State.SetGlobal("selectPreviousMessage", c.Config.State.NewFunction(c.MessagesPanel.selectPreviousMessageLua)) - c.Config.State.SetGlobal("selectNextMessage", c.Config.State.NewFunction(c.MessagesPanel.selectNextMessageLua)) - c.Config.State.SetGlobal("selectFirstMessage", c.Config.State.NewFunction(c.MessagesPanel.selectFirstMessageLua)) - c.Config.State.SetGlobal("selectLastMessage", c.Config.State.NewFunction(c.MessagesPanel.selectLastMessageLua)) - // Message input - c.Config.State.SetGlobal("openExternalEditor", c.Config.State.NewFunction(c.MessageInput.openExternalEditorLua)) - c.Config.State.SetGlobal("pasteClipboardContent", c.Config.State.NewFunction(c.MessageInput.pasteClipboardContentLua)) -} - func (c *Core) DrawMainFlex() { leftFlex := tview.NewFlex(). SetDirection(tview.FlexRow). @@ -129,52 +75,21 @@ func (c *Core) DrawMainFlex() { AddItem(rightFlex, 0, 4, false) } -func (c *Core) onInputCapture(e *tcell.EventKey) *tcell.EventKey { +func (c *Core) beforeDraw(screen tcell.Screen) bool { + if c.Config.Theme.Background == "default" { + screen.Clear() + } + + return false +} + +func (c *Core) onInputCapture(event *tcell.EventKey) *tcell.EventKey { // If the main flex is nil, that is, it is not initialized yet, then the login form is currently focused. if c.MainFlex == nil { - return e + return event } - keysTable, ok := c.Config.State.GetGlobal("keys").(*lua.LTable) - if !ok { - keysTable = c.Config.State.NewTable() - } - - applicationTable, ok := keysTable.RawGetString("application").(*lua.LTable) - if !ok { - applicationTable = c.Config.State.NewTable() - } - - var fn lua.LValue - applicationTable.ForEach(func(k, v lua.LValue) { - keyTable := v.(*lua.LTable) - if e.Name() == lua.LVAsString(keyTable.RawGetString("name")) { - fn = keyTable.RawGetString("action") - } - }) - - if fn != nil { - c.Config.State.CallByParam(lua.P{ - Fn: fn, - NRet: 1, - Protect: true, - }, luar.New(c.Config.State, c), luar.New(c.Config.State, e)) - // Returned value - ret, ok := c.Config.State.Get(-1).(*lua.LUserData) - if !ok { - return e - } - - // Remove returned value - c.Config.State.Pop(1) - ev, ok := ret.Value.(*tcell.EventKey) - if ok { - return ev - } - } - - // Default - switch e.Key() { + switch event.Key() { case tcell.KeyEsc: c.focused = 0 case tcell.KeyBacktab: @@ -197,7 +112,7 @@ func (c *Core) onInputCapture(e *tcell.EventKey) *tcell.EventKey { c.setFocus() } - return e + return event } func (c *Core) setFocus() { @@ -215,101 +130,3 @@ func (c *Core) setFocus() { c.Application.SetFocus(p) } - -func (c *Core) onStateReady(r *gateway.ReadyEvent) { - rootNode := c.GuildsTree.GetRoot() - for _, gf := range r.UserSettings.GuildFolders { - if gf.ID == 0 { - for _, gID := range gf.GuildIDs { - g, err := c.State.Cabinet.Guild(gID) - if err != nil { - return - } - - guildNode := tview.NewTreeNode(g.Name) - guildNode.SetReference(g.ID) - rootNode.AddChild(guildNode) - } - } else { - var b strings.Builder - - if gf.Color != discord.NullColor { - b.WriteByte('[') - b.WriteString(gf.Color.String()) - b.WriteByte(']') - } else { - b.WriteString("[#ED4245]") - } - - if gf.Name != "" { - b.WriteString(gf.Name) - } else { - b.WriteString("Folder") - } - - b.WriteString("[-]") - - folderNode := tview.NewTreeNode(b.String()) - rootNode.AddChild(folderNode) - - for _, gID := range gf.GuildIDs { - g, err := c.State.Cabinet.Guild(gID) - if err != nil { - return - } - - guildNode := tview.NewTreeNode(g.Name) - guildNode.SetReference(g.ID) - folderNode.AddChild(guildNode) - } - } - - } - - c.GuildsTree.SetCurrentNode(rootNode) - c.Application.SetFocus(c.GuildsTree) -} - -func (c *Core) onStateGuildCreate(g *gateway.GuildCreateEvent) { - guildNode := tview.NewTreeNode(g.Name) - guildNode.SetReference(g.ID) - - rootNode := c.GuildsTree.GetRoot() - rootNode.AddChild(guildNode) - - c.GuildsTree.SetCurrentNode(rootNode) - c.Application.SetFocus(c.GuildsTree) - c.Application.Draw() -} - -func (c *Core) onStateGuildDelete(g *gateway.GuildDeleteEvent) { - rootNode := c.GuildsTree.GetRoot() - var parentNode *tview.TreeNode - rootNode.Walk(func(node, _ *tview.TreeNode) bool { - if node.GetReference() == g.ID { - parentNode = node - return false - } - - return true - }) - - if parentNode != nil { - rootNode.RemoveChild(parentNode) - } - - c.Application.Draw() -} - -func (c *Core) onStateMessageCreate(m *gateway.MessageCreateEvent) { - if c.ChannelsTree.SelectedChannel != nil && c.ChannelsTree.SelectedChannel.ID == m.ChannelID { - _, err := c.MessagesPanel.Write(buildMessage(c, m.Message)) - if err != nil { - return - } - - if len(c.MessagesPanel.GetHighlights()) == 0 { - c.MessagesPanel.ScrollToEnd() - } - } -} diff --git a/ui/message_input.go b/ui/message_input.go index a7e680f..29c5f2e 100644 --- a/ui/message_input.go +++ b/ui/message_input.go @@ -12,8 +12,6 @@ import ( "github.com/diamondburned/arikawa/v3/utils/json/option" "github.com/gdamore/tcell/v2" "github.com/rivo/tview" - lua "github.com/yuin/gopher-lua" - luar "layeh.com/gopher-luar" ) type MessageInput struct { @@ -30,7 +28,7 @@ func NewMessageInput(c *Core) *MessageInput { mi.SetFieldBackgroundColor(tview.Styles.PrimitiveBackgroundColor) mi.SetPlaceholder("Message...") mi.SetPlaceholderStyle(tcell.StyleDefault.Background(tview.Styles.PrimitiveBackgroundColor)) - mi.SetInputCapture(mi.onInputCapture) + mi.SetInputCapture(mi.inputCapture) mi.SetTitleAlign(tview.AlignLeft) mi.SetBorder(true) @@ -39,50 +37,14 @@ func NewMessageInput(c *Core) *MessageInput { return mi } -func (mi *MessageInput) onInputCapture(e *tcell.EventKey) *tcell.EventKey { - keysTable, ok := mi.core.Config.State.GetGlobal("keys").(*lua.LTable) - if !ok { - keysTable = mi.core.Config.State.NewTable() - } - - messageInputTable, ok := keysTable.RawGetString("messageInput").(*lua.LTable) - if !ok { - messageInputTable = mi.core.Config.State.NewTable() - } - - var fn lua.LValue - messageInputTable.ForEach(func(k, v lua.LValue) { - keyTable := v.(*lua.LTable) - if e.Name() == lua.LVAsString(keyTable.RawGetString("name")) { - fn = keyTable.RawGetString("action") - } - }) - - if fn != nil { - mi.core.Config.State.CallByParam(lua.P{ - Fn: fn, - NRet: 1, - Protect: true, - }, luar.New(mi.core.Config.State, mi.core), luar.New(mi.core.Config.State, e)) - // Returned value - ret, ok := mi.core.Config.State.Get(-1).(*lua.LUserData) - if !ok { - return e - } - - // Remove returned value - mi.core.Config.State.Pop(1) - - ev, ok := ret.Value.(*tcell.EventKey) - if ok { - return ev - } - } - - // Defaults - switch e.Name() { +func (mi *MessageInput) inputCapture(event *tcell.EventKey) *tcell.EventKey { + switch event.Name() { case "Enter": return mi.sendMessage() + case mi.core.Config.Keys.MessageInput.OpenExternalEditor: + return mi.openExternalEditor() + case mi.core.Config.Keys.MessageInput.PasteClipboard: + return mi.pasteClipboard() case "Esc": mi. SetText(""). @@ -92,7 +54,7 @@ func (mi *MessageInput) onInputCapture(e *tcell.EventKey) *tcell.EventKey { return nil } - return e + return event } func (mi *MessageInput) sendMessage() *tcell.EventKey { @@ -105,8 +67,7 @@ func (mi *MessageInput) sendMessage() *tcell.EventKey { return nil } - messagesLimit := mi.core.Config.State.GetGlobal("messagesLimit") - ms, err := mi.core.State.Messages(mi.core.ChannelsTree.SelectedChannel.ID, uint(lua.LVAsNumber(messagesLimit))) + ms, err := mi.core.State.Messages(mi.core.ChannelsTree.SelectedChannel.ID, mi.core.Config.MessagesLimit) if err != nil { return nil } @@ -143,22 +104,22 @@ func (mi *MessageInput) sendMessage() *tcell.EventKey { return nil } -func (mi *MessageInput) pasteClipboardContentLua(s *lua.LState) int { +func (mi *MessageInput) pasteClipboard() *tcell.EventKey { text, _ := clipboard.ReadAll() text = mi.GetText() + text mi.SetText(text) - return returnNilLua(s) + return nil } -func (mi *MessageInput) openExternalEditorLua(s *lua.LState) int { +func (mi *MessageInput) openExternalEditor() *tcell.EventKey { e := os.Getenv("EDITOR") if e == "" { - return returnNilLua(s) + return nil } f, err := os.CreateTemp(os.TempDir(), "discordo-*.md") if err != nil { - return returnNilLua(s) + return nil } defer os.Remove(f.Name()) @@ -175,9 +136,9 @@ func (mi *MessageInput) openExternalEditorLua(s *lua.LState) int { b, err := io.ReadAll(f) if err != nil { - return returnNilLua(s) + return nil } mi.SetText(string(b)) - return returnNilLua(s) + return nil } diff --git a/ui/messages_panel.go b/ui/messages_panel.go index 38e0f57..1253627 100644 --- a/ui/messages_panel.go +++ b/ui/messages_panel.go @@ -4,8 +4,6 @@ import ( "github.com/diamondburned/arikawa/v3/discord" "github.com/gdamore/tcell/v2" "github.com/rivo/tview" - lua "github.com/yuin/gopher-lua" - luar "layeh.com/gopher-luar" ) type MessagesPanel struct { @@ -51,47 +49,19 @@ func (mp *MessagesPanel) onInputCapture(e *tcell.EventKey) *tcell.EventKey { return nil } - keysTable, ok := mp.core.Config.State.GetGlobal("keys").(*lua.LTable) - if !ok { - keysTable = mp.core.Config.State.NewTable() - } - - messagesPanel, ok := keysTable.RawGetString("messagesPanel").(*lua.LTable) - if !ok { - messagesPanel = mp.core.Config.State.NewTable() - } - - var fn lua.LValue - messagesPanel.ForEach(func(k, v lua.LValue) { - keyTable := v.(*lua.LTable) - if e.Name() == lua.LVAsString(keyTable.RawGetString("name")) { - fn = keyTable.RawGetString("action") - } - }) - - if fn != nil { - mp.core.Config.State.CallByParam(lua.P{ - Fn: fn, - NRet: 1, - Protect: true, - }, luar.New(mp.core.Config.State, mp.core), luar.New(mp.core.Config.State, e)) - // Returned value - ret, ok := mp.core.Config.State.Get(-1).(*lua.LUserData) - if !ok { - return e - } - - // Remove returned value - mp.core.Config.State.Pop(1) - - ev, ok := ret.Value.(*tcell.EventKey) - if ok { - return ev - } - } - // Defaults switch e.Name() { + case mp.core.Config.Keys.MessagesPanel.OpenActionsList: + return mp.openMessageActionsList(ms) + + case mp.core.Config.Keys.MessagesPanel.SelectPreviousMessage: + return mp.selectPreviousMessage(ms) + case mp.core.Config.Keys.MessagesPanel.SelectNextMessage: + return mp.selectNextMessage(ms) + case mp.core.Config.Keys.MessagesPanel.SelectFirstMessage: + return mp.selectFirstMessage(ms) + case mp.core.Config.Keys.MessagesPanel.SelectLastMessage: + return mp.selectLastMessage(ms) case "Esc": mp.SelectedMessage = -1 mp.core.Application.SetFocus(mp.core.MainFlex) @@ -105,13 +75,7 @@ func (mp *MessagesPanel) onInputCapture(e *tcell.EventKey) *tcell.EventKey { return e } -func (mp *MessagesPanel) selectPreviousMessageLua(s *lua.LState) int { - // Messages should return messages ordered from latest to earliest. - ms, err := mp.core.State.Cabinet.Messages(mp.core.ChannelsTree.SelectedChannel.ID) - if err != nil || len(ms) == 0 { - return returnNilLua(s) - } - +func (mp *MessagesPanel) selectPreviousMessage(ms []discord.Message) *tcell.EventKey { // If there are no highlighted regions, select the latest (last) message in the messages panel. if len(mp.GetHighlights()) == 0 { mp.SelectedMessage = 0 @@ -126,16 +90,10 @@ func (mp *MessagesPanel) selectPreviousMessageLua(s *lua.LState) int { mp.Highlight(ms[mp.SelectedMessage].ID.String()) mp.ScrollToHighlight() - return returnNilLua(s) + return nil } -func (mp *MessagesPanel) selectNextMessageLua(s *lua.LState) int { - // Messages should return messages ordered from latest to earliest. - ms, err := mp.core.State.Cabinet.Messages(mp.core.ChannelsTree.SelectedChannel.ID) - if err != nil || len(ms) == 0 { - return returnNilLua(s) - } - +func (mp *MessagesPanel) selectNextMessage(ms []discord.Message) *tcell.EventKey { // If there are no highlighted regions, select the latest (last) message in the messages panel. if len(mp.GetHighlights()) == 0 { mp.SelectedMessage = 0 @@ -151,60 +109,42 @@ func (mp *MessagesPanel) selectNextMessageLua(s *lua.LState) int { mp. Highlight(ms[mp.SelectedMessage].ID.String()). ScrollToHighlight() - return returnNilLua(s) + return nil } -func (mp *MessagesPanel) selectFirstMessageLua(s *lua.LState) int { - // Messages should return messages ordered from latest to earliest. - ms, err := mp.core.State.Cabinet.Messages(mp.core.ChannelsTree.SelectedChannel.ID) - if err != nil || len(ms) == 0 { - return returnNilLua(s) - } - +func (mp *MessagesPanel) selectFirstMessage(ms []discord.Message) *tcell.EventKey { mp.SelectedMessage = len(ms) - 1 mp. Highlight(ms[mp.SelectedMessage].ID.String()). ScrollToHighlight() - return returnNilLua(s) + return nil } -func (mp *MessagesPanel) selectLastMessageLua(s *lua.LState) int { - // Messages should return messages ordered from latest to earliest. - ms, err := mp.core.State.Cabinet.Messages(mp.core.ChannelsTree.SelectedChannel.ID) - if err != nil || len(ms) == 0 { - return returnNilLua(s) - } - +func (mp *MessagesPanel) selectLastMessage(ms []discord.Message) *tcell.EventKey { mp.SelectedMessage = 0 mp. Highlight(ms[mp.SelectedMessage].ID.String()). ScrollToHighlight() - return returnNilLua(s) + return nil } -func (mp *MessagesPanel) openMessageActionsListLua(s *lua.LState) int { - // Messages should return messages ordered from latest to earliest. - ms, err := mp.core.State.Cabinet.Messages(mp.core.ChannelsTree.SelectedChannel.ID) - if err != nil || len(ms) == 0 { - return returnNilLua(s) - } - +func (mp *MessagesPanel) openMessageActionsList(ms []discord.Message) *tcell.EventKey { hs := mp.GetHighlights() if len(hs) == 0 { - return returnNilLua(s) + return nil } mID, err := discord.ParseSnowflake(hs[0]) if err != nil { - return returnNilLua(s) + return nil } _, m := findMessageByID(ms, discord.MessageID(mID)) if m == nil { - return returnNilLua(s) + return nil } actionsList := NewMessageActionsList(mp.core, m) mp.core.Application.SetRoot(actionsList, true) - return returnNilLua(s) + return nil } diff --git a/ui/util.go b/ui/util.go index 606530d..fef2124 100644 --- a/ui/util.go +++ b/ui/util.go @@ -5,7 +5,6 @@ import ( "strings" "github.com/diamondburned/arikawa/v3/discord" - lua "github.com/yuin/gopher-lua" ) var ( @@ -66,8 +65,3 @@ func hasPermission(s *State, cID discord.ChannelID, p discord.Permissions) bool return perm&p == p } - -func returnNilLua(s *lua.LState) int { - s.Push(lua.LNil) // Push the result - return 1 // Number of results -}