| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143 |
- package login
- import (
- "errors"
- "log/slog"
- "github.com/ayn2op/discordo/internal/config"
- "github.com/ayn2op/discordo/internal/http"
- "github.com/ayn2op/discordo/internal/keyring"
- "github.com/ayn2op/discordo/internal/ui"
- "github.com/ayn2op/tview"
- "github.com/diamondburned/arikawa/v3/api"
- "golang.design/x/clipboard"
- )
- const (
- formPageName = "form"
- errorPageName = "error"
- qrPageName = "qr"
- )
- type DoneFn = func(token string)
- type Form struct {
- *tview.Pages
- app *tview.Application
- cfg *config.Config
- form *tview.Form
- done DoneFn
- }
- func NewForm(app *tview.Application, cfg *config.Config, done DoneFn) *Form {
- f := &Form{
- Pages: tview.NewPages(),
- app: app,
- cfg: cfg,
- form: tview.NewForm(),
- done: done,
- }
- f.form.
- AddInputField("Email", "", 0, nil).
- AddPasswordField("Password", "", 0, 0, nil).
- AddPasswordField("Code (optional)", "", 0, 0, nil).
- AddButton("Login", f.login).
- AddButton("Login with QR", f.loginWithQR)
- f.AddAndSwitchToPage(formPageName, f.form, true)
- return f
- }
- func (f *Form) login() {
- email := f.form.GetFormItem(0).(*tview.InputField).GetText()
- password := f.form.GetFormItem(1).(*tview.InputField).GetText()
- if email == "" || password == "" {
- return
- }
- // Create an API client without an authentication token.
- client := api.NewClient("")
- props := http.IdentifyProperties()
- if browserUserAgent, ok := props["browser_user_agent"]; ok {
- if val, ok := browserUserAgent.(string); ok {
- api.UserAgent = val
- }
- }
- resp, err := client.Login(email, password)
- if err != nil {
- f.onError(err)
- return
- }
- if resp.Token == "" && resp.MFA {
- code := f.form.GetFormItem(2).(*tview.InputField).GetText()
- if code == "" {
- f.onError(errors.New("code required"))
- return
- }
- // Attempt to login using the code.
- resp, err = client.TOTP(code, resp.Ticket)
- if err != nil {
- f.onError(err)
- return
- }
- }
- if resp.Token == "" {
- f.onError(errors.New("missing token"))
- return
- }
- go keyring.SetToken(resp.Token)
- if f.done != nil {
- f.done(resp.Token)
- }
- }
- func (f *Form) onError(err error) {
- slog.Error("failed to login", "err", err)
- message := err.Error()
- modal := tview.NewModal().
- SetText(message).
- AddButtons([]string{"Copy", "Close"}).
- SetDoneFunc(func(buttonIndex int, _ string) {
- if buttonIndex == 0 {
- go clipboard.Write(clipboard.FmtText, []byte(message))
- } else {
- f.
- RemovePage(errorPageName).
- SwitchToPage(formPageName)
- }
- })
- f.
- AddAndSwitchToPage(errorPageName, ui.Centered(modal, 0, 0), true).
- ShowPage(formPageName)
- }
- func (f *Form) loginWithQR() {
- qr := newQRLogin(f.app, f.cfg, func(token string, err error) {
- if err != nil {
- f.onError(err)
- return
- }
- if token == "" {
- f.RemovePage(qrPageName).SwitchToPage(formPageName)
- return
- }
- go keyring.SetToken(token)
- f.RemovePage(qrPageName)
- if f.done != nil {
- f.done(token)
- }
- })
- f.AddAndSwitchToPage(qrPageName, qr, true)
- qr.start()
- }
|