// package desktop -- консольный клиент для вар-танка package desktop import ( "fmt" "os" "time" "github.com/charmbracelet/bubbles/textinput" tea "github.com/charmbracelet/bubbletea" "wartank/desktop/client_serv" ) // Desktop -- консольный клиент для вар-танка type Desktop struct { TeaModel p *tea.Program // Объект приложения client *client_serv.ClientServ // Клиент подключния к бото-серверу isConnect bool // Признак подключения к бот-серверу isLogin bool // Признак логина на сервер isMakeAdmin bool // Признак создания админа } var desk *Desktop func NewDesktop() *Desktop { sf := &Desktop{ client: client_serv.NewClientServ(), } sf.TeaModel = NewModel() go sf.tick() sf.p = tea.NewProgram(sf, tea.WithAltScreen(), tea.WithANSICompressor()) desk = sf if _, err := sf.p.Run(); err != nil { fmt.Printf("Опа! здесь какая-то ошибочка: %v", err) os.Exit(1) } return sf } // AMode -- режим обработки клавиш type AMode int const ( ModeDefault AMode = iota ModeConfig // Режим конфигурирования ModeConfigServer // Режим конфигуррования сервера ModeInputHostServer // Режим ввода хоста сервера ModeInputLoginServer // Режим ввода логина на сервер ModeInputPassServer // Режим ввода пароля входа на сервер ModeConnectServer // Режим подключения к серверу ModeLoginServer // Режим логина на сервер ModeServerMakeAdmin // Создать админа для бот-сервера ) type TeaModel struct { choices []string // выбранный пункт в списке cursor int // позиция курсора в списке selected map[int]struct{} // что выбрано из списка mode AMode // Режим показа на экране textInput textinput.Model host string // Имя хоста сервера (с портом) login string // Логин для сервера pass string // Пароль для сервера } func NewModel() TeaModel { ti := textinput.New() ti.Placeholder = "localhost" ti.Focus() ti.CharLimit = 156 ti.Width = 20 m := TeaModel{ // Список дел choices: []string{"Купить картошку", "Оплатить мобилку", "Заплатить за кредит"}, selected: make(map[int]struct{}), textInput: ti, } return m } func (sf *Desktop) tick() { for { time.Sleep(time.Second * 1) msg := tea.MouseEvent{} // m.Update(msg) sf.p.Send(msg) } } // Run -- запускает в работу консольный клиент func (sf *Desktop) Run() error { return nil } func (sf Desktop) Init() tea.Cmd { // Просто вернуть `nil`, т.е. никаких команд return textinput.Blink } func (sf Desktop) Update(msg tea.Msg) (tea.Model, tea.Cmd) { var cmd tea.Cmd switch msg := msg.(type) { // Может клавиша нажата case tea.KeyMsg: switch msg.String() { // Да, нажато, а что именно? case "ctrl+q": // Надо выйти из программы return sf, tea.Quit } } switch desk.mode { case ModeDefault: switch msg := msg.(type) { // Может клавиша нажата case tea.KeyMsg: switch msg.String() { // Да, нажато, а что именно? case "up", "k": // Если "up" или "k" перемещение курсора вверх if desk.cursor > 0 { desk.cursor-- } case "down", "j": // Если "down" или "j" перемещение курсора вниз if desk.cursor < len(desk.choices)-1 { desk.cursor++ } case "enter", " ": // Нажатие "enter" или "пробел" переключает выбранный элемент списка _, ok := desk.selected[desk.cursor] if ok { delete(desk.selected, desk.cursor) } else { desk.selected[desk.cursor] = struct{}{} } case "w": // Настройки графического клиента desk.mode = ModeConfig } } case ModeConfig: switch msg := msg.(type) { // Может клавиша нажата case tea.KeyMsg: switch msg.String() { // Да, нажато, а что именно? case "esc": // Вернуться на шаг назад desk.mode = ModeDefault case "1": // Выбор один desk.mode = ModeConfigServer case "2": // Вход на сервер if !desk.isConnect { desk.mode = ModeConnectServer } case "3": // Логин на сервер if !desk.isLogin { desk.mode = ModeLoginServer } case "4": // Создать админа desk.mode = ModeServerMakeAdmin } } case ModeConfigServer: switch msg := msg.(type) { // Может клавиша нажата case tea.KeyMsg: switch msg.String() { // Да, нажато, а что именно? case "esc": // Вернуться на шаг назад desk.mode = ModeConfig case "1": // Выбор хоста сервера desk.mode = ModeInputHostServer desk.textInput.Reset() desk.textInput.Placeholder = "localhost:18050" case "2": // Выбор логина для входа desk.mode = ModeInputLoginServer desk.textInput.Reset() desk.textInput.Placeholder = "login" case "3": // Выбор пароля для входа desk.mode = ModeInputPassServer desk.textInput.Reset() desk.textInput.Placeholder = "pass" } } case ModeInputHostServer: switch msg := msg.(type) { // Может клавиша нажата case tea.KeyMsg: switch msg.String() { // Да, нажато, а что именно? case "esc": // Вернуться на шаг назад desk.mode = ModeConfigServer case "enter": // Нажатие "enter" или "пробел" переключает выбранный элемент списка desk.host = desk.textInput.Value() desk.mode = ModeConfigServer default: desk.textInput, cmd = desk.textInput.Update(msg) } } case ModeInputLoginServer: switch msg := msg.(type) { // Может клавиша нажата case tea.KeyMsg: switch msg.String() { // Да, нажато, а что именно? case "esc": // Вернуться на шаг назад desk.mode = ModeConfigServer case "enter": // Нажатие "enter" или "пробел" переключает выбранный элемент списка desk.login = desk.textInput.Value() desk.mode = ModeConfigServer default: desk.textInput, cmd = desk.textInput.Update(msg) } } case ModeInputPassServer: switch msg := msg.(type) { // Может клавиша нажата case tea.KeyMsg: switch msg.String() { // Да, нажато, а что именно? case "esc": // Вернуться на шаг назад desk.mode = ModeConfigServer case "enter": // Нажатие "enter" или "пробел" переключает выбранный элемент списка desk.pass = desk.textInput.Value() desk.mode = ModeConfigServer default: desk.textInput, cmd = desk.textInput.Update(msg) } } case ModeConnectServer: switch msg := msg.(type) { // Может клавиша нажата case tea.KeyMsg: switch msg.String() { // Да, нажато, а что именно? case "esc": // Нажатие "esc" прекращает подключение к серверу desk.mode = ModeConfigServer } } case ModeLoginServer: switch msg := msg.(type) { // Может клавиша нажата case tea.KeyMsg: switch msg.String() { // Да, нажато, а что именно? case "esc": // Нажатие "esc" прекращает логин на сервер desk.mode = ModeConfig } } case ModeServerMakeAdmin: switch msg := msg.(type) { // Может клавиша нажата case tea.KeyMsg: switch msg.String() { // Да, нажато, а что именно? case "esc": // Нажатие "esc" прекращает логин на сервер desk.mode = ModeConfig } } } // Возвращает обновлённую модель дляe Bubble Tea runtime для обработки. //Здесь команду возвращать надо. return desk, cmd } func (sf Desktop) View() string { s := time.Now().Local().Format("[ WarTank ] = 2006-01-02 15:04:05.000") s += fmt.Sprintf("\tХост=%q\tЛогин=%q\tПароль=%q\tПодключено=%v\tЛогин=%v\tЕстьАдмин=%v\n\n", desk.host, desk.login, desk.pass, desk.isConnect, desk.isLogin, desk.isMakeAdmin) switch desk.mode { case ModeDefault: // Исходное содержимое s += desk.winDefault() case ModeConfig: // Показать настройки клиента s += desk.winConfig() case ModeConfigServer: // Конфигурация сервера s += desk.winConfigServer() case ModeInputHostServer: // Ввод хоста для сервера s += desk.winHostServer() case ModeInputLoginServer: // Ввод логина для входа на сервер s += desk.winLoginServer() case ModeInputPassServer: // ввод пароля s += desk.winPassServer() case ModeConnectServer: // Режим подключения к серверу s += desk.winConnectServer() case ModeLoginServer: // Режим логина на сервер s += desk.winServerLogin() case ModeServerMakeAdmin: // Режим содания админа на бот-сервере s += desk.winServerMakeAdmin() } return s } func (sf *Desktop) winServerMakeAdmin() string { s := "=Создание админа на бот-сервере вар-танк=\n\n" if !desk.isMakeAdmin { err := desk.client.MakeAdmin(desk.pass) if err != nil { s += fmt.Sprintf("Desktop.winServerMakeAdmin(): err=\n\t%v", err) s += "\n\n[ctrl+q]-выход [esc]-отмена\n" return s } } s += "Desktop.winServerMakeAdmin(): ok\n" desk.isMakeAdmin = true s += "\n\n[ctrl+q]-выход [esc]-отмена\n" return s } func (sf *Desktop) winServerLogin() string { s := "=Логин на бот-сервер вар-танк=\n\n" err := desk.client.Login(desk.login, desk.pass) if err != nil { s += fmt.Sprintf("Desktop.winServerLogin(): err=\n\t%v", err) s += "\n\n[ctrl+q]-выход [esc]-отмена\n" return s } s += "Desktop.winServerLogin(): ok\n" desk.isLogin = true s += "\n\n[ctrl+q]-выход [esc]-отмена\n" return s } func (sf *Desktop) winConnectServer() string { s := "=Подключение к бот-серверу вар-танк=\n\n" if !desk.isConnect { err := desk.client.Connect(desk.host) if err != nil { s += fmt.Sprintf("Desktop.winConnectServer(): err=\n\t%v", err) s += "\n\n[ctrl+q]-выход [esc]-отмена\n" return s } } s += "Desktop.winConnectServer(): ok\n" desk.isConnect = true s += "\n\n[ctrl+q]-выход [esc]-отмена\n" return s } func (sf *Desktop) winPassServer() string { s := "=Ввод пароля для бот-сервера вар-танк=\n\n" s += fmt.Sprintf(">%v\n", desk.textInput.View()) s += "\n\n[ctrl+q]-выход [esc]-назад\n" return s } func (sf *Desktop) winLoginServer() string { s := "=Ввод логина для сервера вар-танк=\n\n" s += fmt.Sprintf(">%v\n", desk.textInput.View()) s += "\n\n[ctrl+q]-выход [esc]-назад\n" return s } func (sf *Desktop) winHostServer() string { s := "=Ввод хоста для сервера вар-танк=\n\n" s += fmt.Sprintf(">%v\n", desk.textInput.View()) s += "\n\n[ctrl+q]-выход [esc]-назад\n" return s } func (sf *Desktop) winConfigServer() string { s := "Настройки сервера вар-танк\n\n" s += "[1]-Настройки хоста сервера\n" s += "[2]-Логин для входа\n" s += "[3]-Пароль для входа\n" s += "\n\n[ctrl+q]-выход [esc]-назад\n" return s } func (sf *Desktop) winConfig() string { s := "Настройки клиента\n\n" s += "[1]-Настройки сервера\n" s += "[2]-Вход на сервер\n" s += "[3]-Логин на сервер\n" s += "[4]-Сделать админа на бот-сервере\n" s += "\n\n[ctrl+q]-выход [esc]-назад\n" return s } func (sf *Desktop) winDefault() string { // Заголовок s := "Что нам нужно сделать?\n\n" // Перебор по списку выбора for i, choice := range desk.choices { // Курсор указывает на этот выбор? cursor := " " // нет курсора if desk.cursor == i { cursor = ">" // есть курсор } // Этот элемент выбран? checked := " " // не выбран if _, ok := desk.selected[i]; ok { checked = "+" // выбран! } // отрендерить заново s += fmt.Sprintf("%s [%s] %s\n", cursor, checked, choice) } // Подвал s += "\n\n[ctrl+q]-выход [w]-настройки\n" // Вернуть строку экрана для отрисовки return s }