From c9b4e114e034f1ab3f036a77abf63c9d9862aa32 Mon Sep 17 00:00:00 2001 From: ayntgl <90600421+ayntgl@users.noreply.github.com> Date: Sat, 15 Oct 2022 15:13:42 +0530 Subject: [PATCH] Add CLI flags for token, config, and log (#245) --- README.md | 12 +++---- config/config.go | 18 +++++++---- main.go | 59 ++++++++++++++++++++-------------- ui/login_view.go | 84 ++++++------------------------------------------ 4 files changed, 60 insertions(+), 113 deletions(-) diff --git a/README.md b/README.md index 9554c42..9890f08 100644 --- a/README.md +++ b/README.md @@ -65,17 +65,17 @@ sudo mv ./discordo /usr/local/bin 1. Run the `discordo` executable with no arguments. -2. A login view will be displayed on first start-up. You can choose to login using either your email and password or token; the views can be switched using `Ctrl+Space` keybinding. +> If you are logging in using an authentication token, provide the `token` command-line flag to the executable (eg: `--token "OTI2MDU5NTQxNDE2Nzc5ODA2.Yc2KKA.2iZ-5JxgxG-9Ub8GHzBSn-NJjNg"`). -3. Enter your credentials and click on the "Login" button to continue. +2. Enter your email and password and click on the "Login" button to continue. -- If you are logging in with a bot account, prefix the token with `Bot ` (eg: `Bot OTI2MDU5NTQxNDE2Nzc5ODA2.Yc2KKA.2iZ-5JxgxG-9Ub8GHzBSn-NJjNg`). - -- Most of the Discord third-party clients store the token in a configuration file unencrypted. Discordo securely stores the token in the default OS-specific keyring. +- Most of the Discord third-party clients store the token in a configuration file unencrypted. Discordo securely stores the token in the default OS-specific keyring. ### Configuration -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. +A default configuration file is created on first start-up at `$HOME/.config/discordo.yml` on Unix, `$HOME/Library/Application Support/discordo.yml` on Darwin, and `%AppData%/discordo.yml` on Windows. The configuration file path can be customized using the `config` command-line flag (eg: `--config ~/myconfigs/discordo.yml`). + +Similarly, a log file is created on first start-up at `$HOME/.cache/discordo.log` on Unix, `$HOME/Library/Caches/discordo.log` on Darwin, and `%LocalAppData%/discordo.yml` on Windows. The log file path can be customized using the `log` command-line flag. ## Disclaimer diff --git a/config/config.go b/config/config.go index e2a4aaf..ecfa9f7 100644 --- a/config/config.go +++ b/config/config.go @@ -80,9 +80,9 @@ func New() *Config { } } -func (cfg *Config) Load() error { +func (cfg *Config) Load(path string) error { // Open the existing configuration file with read-only flag. - f, err := os.OpenFile(cfg.configPath(), os.O_CREATE|os.O_RDWR, os.ModePerm) + f, err := os.OpenFile(path, os.O_CREATE|os.O_RDWR, os.ModePerm) if err != nil { return err } @@ -101,10 +101,14 @@ func (cfg *Config) Load() error { return yaml.NewDecoder(f).Decode(&cfg) } -func (cfg *Config) configPath() string { +// DefaultConfigPath returns the default configuration file path. +func DefaultConfigPath() string { path, _ := os.UserConfigDir() - path = filepath.Join(path, Name) - // Create the configuration directory if it does not exist already. - _ = os.MkdirAll(path, os.ModePerm) - return filepath.Join(path, "config.yml") + return filepath.Join(path, Name+".yml") +} + +// DefaultLogPath returns the default log file path. +func DefaultLogPath() string { + path, _ := os.UserCacheDir() + return filepath.Join(path, Name+".log") } diff --git a/main.go b/main.go index 85586ca..52c970e 100644 --- a/main.go +++ b/main.go @@ -1,10 +1,10 @@ package main import ( + "flag" "fmt" "log" "os" - "path/filepath" "runtime" "github.com/ayntgl/discordo/config" @@ -15,27 +15,13 @@ import ( "github.com/zalando/go-keyring" ) +var ( + flagToken string + flagConfig string + flagLog string +) + func init() { - path, err := os.UserCacheDir() - if err != nil { - log.Fatal(err) - } - - path = filepath.Join(path, config.Name) - err = os.MkdirAll(path, os.ModePerm) - if err != nil { - log.Fatal(err) - } - - path = filepath.Join(path, "out.log") - f, err := os.OpenFile(path, os.O_CREATE|os.O_WRONLY, os.ModePerm) - if err != nil { - log.Fatal(err) - } - - log.SetOutput(f) - log.SetFlags(log.LstdFlags | log.Lshortfile) - tview.Borders.TopLeftFocus = tview.Borders.TopLeft tview.Borders.TopRightFocus = tview.Borders.TopRight tview.Borders.BottomLeftFocus = tview.Borders.BottomLeft @@ -55,18 +41,41 @@ func init() { Browser: config.Name, Device: "", } + + flag.StringVar(&flagToken, "token", "", "The authentication token.") + flag.StringVar(&flagConfig, "config", config.DefaultConfigPath(), "The path to the configuration file.") + flag.StringVar(&flagLog, "log", config.DefaultLogPath(), "The path to the log file.") } func main() { + flag.Parse() + + if flagLog != "" { + // Set the standard logger output to the provided log file. + f, err := os.OpenFile(flagLog, os.O_CREATE|os.O_WRONLY, 0666) + if err != nil { + log.Fatal(err) + } + + log.SetOutput(f) + log.SetFlags(log.LstdFlags | log.Lshortfile) + } + cfg := config.New() - err := cfg.Load() + err := cfg.Load(flagConfig) if err != nil { log.Fatal(err) } - token, err := keyring.Get(config.Name, "token") - if err != nil { - log.Println(err) + var token string + if flagToken != "" { + token = flagToken + go keyring.Set(config.Name, "token", token) + } else { + token, err = keyring.Get(config.Name, "token") + if err != nil { + log.Println(err) + } } c := ui.NewCore(cfg) diff --git a/ui/login_view.go b/ui/login_view.go index 928cf0e..c4dc68f 100644 --- a/ui/login_view.go +++ b/ui/login_view.go @@ -6,56 +6,17 @@ import ( "github.com/ayntgl/discordo/config" "github.com/diamondburned/arikawa/v3/api" - "github.com/gdamore/tcell/v2" "github.com/rivo/tview" "github.com/zalando/go-keyring" ) -const ( - emailViewPageName = "email" - tokenViewPageName = "token" -) - -func NewLoginView(c *Core) *tview.Pages { - v := tview.NewPages() - - v.AddPage(emailViewPageName, newEmailView(c), true, true) - v.AddPage(tokenViewPageName, newTokenView(c), true, true) - // Since the recommended method to login is using the email and password, it is displayed on the screen first. - v.SwitchToPage(emailViewPageName) - - v.SetTitle("Login") - v.SetTitleAlign(tview.AlignLeft) - v.SetBorder(true) - v.SetBorderPadding(0, 0, 1, 1) - v.SetInputCapture(func(event *tcell.EventKey) *tcell.EventKey { - if event.Key() == tcell.KeyCtrlSpace { - name, _ := v.GetFrontPage() - - switch name { - case emailViewPageName: - name = tokenViewPageName - case tokenViewPageName: - name = emailViewPageName - } - - v.SwitchToPage(name) - return nil - } - - return event - }) - - return v -} - -type EmailView struct { +type LoginView struct { *tview.Form core *Core } -func newEmailView(c *Core) *EmailView { - v := &EmailView{ +func NewLoginView(c *Core) *LoginView { + v := &LoginView{ Form: tview.NewForm(), core: c, } @@ -65,10 +26,15 @@ func newEmailView(c *Core) *EmailView { v.AddPasswordField("Code (optional)", "", 0, 0, nil) v.AddButton("Login", v.onLoginButtonSelected) + v.SetTitle("Login") + v.SetTitleAlign(tview.AlignLeft) + v.SetBorder(true) + v.SetBorderPadding(1, 1, 1, 1) + return v } -func (v *EmailView) onLoginButtonSelected() { +func (v *LoginView) onLoginButtonSelected() { email := v.GetFormItem(0).(*tview.InputField).GetText() password := v.GetFormItem(1).(*tview.InputField).GetText() if email == "" || password == "" { @@ -105,35 +71,3 @@ func (v *EmailView) onLoginButtonSelected() { v.core.Draw() go keyring.Set(config.Name, "token", l.Token) } - -type TokenView struct { - *tview.Form - core *Core -} - -func newTokenView(c *Core) *TokenView { - v := &TokenView{ - Form: tview.NewForm(), - core: c, - } - - v.AddPasswordField("Token", "", 0, 0, nil) - v.AddButton("Login", v.onLoginButtonSelected) - - return v -} - -func (v *TokenView) onLoginButtonSelected() { - token := v.GetFormItem(0).(*tview.InputField).GetText() - if token == "" { - return - } - - err := v.core.Run(token) - if err != nil { - log.Fatal(err) - } - - v.core.Draw() - go keyring.Set(config.Name, "token", token) -}