TTW_Bot_GO/internal/gui/window.go

177 lines
4.8 KiB
Go
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

package gui
import (
"os/exec"
"runtime"
"stream-bot/internal/logger"
"time"
"github.com/lxn/walk"
. "github.com/lxn/walk/declarative"
)
const appVersion = "11.0.43"
func Run(webURL string,
getChatStatus func() bool,
getEventSubStatus func() (connected bool, subscriptions []string)) {
if runtime.GOOS != "windows" {
logger.Warn("GUI only supported on Windows, running without window")
return
}
var urlLE *walk.LineEdit
var openBtn *walk.PushButton
var exitBtn *walk.PushButton
var chatStatusLabel, eventSubLabel *walk.Label
var mw *walk.MainWindow
exitWithoutMinimize := false // флаг для полного закрытия
err := MainWindow{
AssignTo: &mw,
Title: "TTW_Bot v" + appVersion,
MinSize: Size{Width: 450, Height: 280},
Size: Size{Width: 500, Height: 300},
Layout: VBox{},
Children: []Widget{
Label{Text: "Веб-интерфейс бота доступен по адресу:"},
LineEdit{
AssignTo: &urlLE,
Text: webURL,
ReadOnly: true,
},
PushButton{
AssignTo: &openBtn,
Text: "🌐 Открыть в браузере",
OnClicked: func() {
openBrowser(webURL)
},
},
Label{Text: "─────────────────────────────────"},
Label{Text: "Статус Twitch чата:"},
Label{AssignTo: &chatStatusLabel, Text: "загрузка..."},
Label{Text: "Статус EventSub:"},
Label{AssignTo: &eventSubLabel, Text: "загрузка..."},
Label{Text: "─────────────────────────────────"},
PushButton{
AssignTo: &exitBtn,
Text: "❌ Завершить работу бота",
OnClicked: func() {
exitWithoutMinimize = true // запоминаем, что хотим выйти
_ = mw.Close()
},
},
},
}.Create()
if err != nil {
msg := "Failed to create GUI window: " + err.Error()
logger.Error(msg)
walk.MsgBox(nil, "Ошибка", msg, walk.MsgBoxIconError)
return
}
// При закрытии окна либо сворачиваем в трей, либо завершаем программу
mw.Closing().Attach(func(cancel *bool, reason walk.CloseReason) {
if exitWithoutMinimize {
// полное завершение не отменяем закрытие
return
}
// иначе сворачиваем в трей
*cancel = true
mw.Hide()
})
// Создаём иконку в трее
ni, err := walk.NewNotifyIcon(mw)
if err != nil {
logger.Error("Failed to create notify icon: %v", err)
} else {
_ = ni.SetToolTip("TTW_Bot")
// Устанавливаем иконку (системная иконка информации)
if err := ni.SetIcon(walk.IconInformation()); err != nil {
logger.Warn("Failed to set icon: %v", err)
}
showAction := walk.NewAction()
_ = showAction.SetText("Показать окно")
showAction.Triggered().Attach(func() {
mw.Show()
mw.SetVisible(true)
})
exitAction := walk.NewAction()
_ = exitAction.SetText("Выход")
exitAction.Triggered().Attach(func() {
exitWithoutMinimize = true
_ = mw.Close()
})
menu := ni.ContextMenu()
_ = menu.Actions().Add(showAction)
_ = menu.Actions().Add(exitAction)
ni.MouseDown().Attach(func(x, y int, button walk.MouseButton) {
if button == walk.LeftButton {
mw.Show()
mw.SetVisible(true)
}
})
if err := ni.SetVisible(true); err != nil {
logger.Error("Failed to set tray icon visible: %v", err)
}
}
// Запускаем периодическое обновление статусов
ticker := time.NewTicker(5 * time.Second)
go func() {
for range ticker.C {
mw.Synchronize(func() {
chatConnected := getChatStatus()
if chatConnected {
_ = chatStatusLabel.SetText("✅ Подключен")
} else {
_ = chatStatusLabel.SetText("❌ Отключен")
}
esConnected, subs := getEventSubStatus()
if esConnected {
_ = eventSubLabel.SetText("✅ Подключен (" + itoa(len(subs)) + " подписок)")
} else {
_ = eventSubLabel.SetText("❌ Отключен")
}
})
}
}()
mw.Run()
ticker.Stop()
}
func openBrowser(url string) {
var cmd *exec.Cmd
switch runtime.GOOS {
case "windows":
cmd = exec.Command("cmd", "/c", "start", url)
case "darwin":
cmd = exec.Command("open", url)
default:
cmd = exec.Command("xdg-open", url)
}
if err := cmd.Start(); err != nil {
logger.Error("Failed to open browser: %v", err)
walk.MsgBox(nil, "Ошибка", "Не удалось открыть браузер: "+err.Error(), walk.MsgBoxIconError)
}
}
func itoa(i int) string {
if i == 0 {
return "0"
}
digits := ""
for i > 0 {
digits = string(rune('0'+i%10)) + digits
i /= 10
}
return digits
}