rum-goggles/v1/app.go

1520 lines
38 KiB
Go
Raw Normal View History

2024-02-23 17:10:39 +00:00
package main
import (
"context"
"encoding/json"
"fmt"
"log"
2024-04-05 15:05:17 +00:00
"net/http"
"os"
2024-05-22 16:51:46 +00:00
"path/filepath"
"sync"
2024-04-04 14:46:14 +00:00
"time"
2024-05-22 16:51:46 +00:00
"github.com/tylertravisty/rum-goggles/v1/internal/chatbot"
"github.com/tylertravisty/rum-goggles/v1/internal/config"
"github.com/tylertravisty/rum-goggles/v1/internal/events"
"github.com/tylertravisty/rum-goggles/v1/internal/models"
rumblelivestreamlib "github.com/tylertravisty/rumble-livestream-lib-go"
2024-04-04 14:46:14 +00:00
"github.com/wailsapp/wails/v2/pkg/runtime"
_ "github.com/mattn/go-sqlite3"
2024-02-23 17:10:39 +00:00
)
2024-04-04 14:46:14 +00:00
const (
AccountType = "Account"
ChannelType = "Channel"
)
type ApiState struct {
active bool
activeMu sync.Mutex
resp *rumblelivestreamlib.LivestreamResponse
respMu sync.Mutex
}
type Page struct {
active bool
activeMu sync.Mutex
apiSt *ApiState
displaying bool
displayingMu sync.Mutex
name string
}
2024-05-02 19:30:25 +00:00
func (p *Page) staticLiveStreamUrl() string {
return fmt.Sprintf("https://rumble.com%s/live", p.name)
}
2024-02-23 17:10:39 +00:00
// App struct
type App struct {
2024-05-02 19:30:25 +00:00
cancelProc context.CancelFunc
2024-05-22 16:51:46 +00:00
chatbot *chatbot.Chatbot
clients map[string]*rumblelivestreamlib.Client
clientsMu sync.Mutex
displaying string
displayingMu sync.Mutex
logError *log.Logger
logFile *os.File
logFileMu sync.Mutex
logInfo *log.Logger
pages map[string]*Page
pagesMu sync.Mutex
producers *events.Producers
services *models.Services
wails context.Context
2024-02-23 17:10:39 +00:00
}
// NewApp creates a new App application struct
func NewApp() *App {
app := &App{
clients: map[string]*rumblelivestreamlib.Client{},
pages: map[string]*Page{},
}
err := app.log()
if err != nil {
log.Fatal("error initializing log: ", err)
}
return app
}
func (a *App) log() error {
a.logFileMu.Lock()
defer a.logFileMu.Unlock()
f, err := config.Log()
if err != nil {
return fmt.Errorf("error getting log file: %v", err)
}
a.logFile = f
a.logInfo = log.New(f, "[info]", log.LstdFlags|log.Lshortfile)
a.logError = log.New(f, "[error]", log.LstdFlags|log.Lshortfile)
return nil
2024-02-23 17:10:39 +00:00
}
// startup is called when the app starts. The context is saved
// so we can call the runtime methods
func (a *App) startup(wails context.Context) {
a.wails = wails
2024-04-05 15:05:17 +00:00
}
2024-05-02 19:30:25 +00:00
func (a *App) process(ctx context.Context) {
for {
select {
case apiE := <-a.producers.ApiP.Ch:
2024-05-22 16:51:46 +00:00
a.processApi(apiE)
2024-05-02 19:30:25 +00:00
case chatE := <-a.producers.ChatP.Ch:
2024-05-22 16:51:46 +00:00
a.processChat(chatE)
case <-ctx.Done():
return
2024-04-05 15:05:17 +00:00
}
}
}
2024-05-22 16:51:46 +00:00
type apiProcessor func(event events.Api)
func (a *App) runApiProcessors(event events.Api, procs ...apiProcessor) {
for _, proc := range procs {
proc(event)
}
}
func (a *App) processApi(event events.Api) {
a.runApiProcessors(
event,
a.pageApiProcessor,
a.chatbotApiProcessor,
)
}
func (a *App) pageApiProcessor(event events.Api) {
if event.Name == "" {
2024-05-22 16:51:46 +00:00
a.logError.Println("page cannot process API: event name is empty")
}
a.pagesMu.Lock()
defer a.pagesMu.Unlock()
page, exists := a.pages[event.Name]
if !exists {
page = &Page{
apiSt: &ApiState{},
name: event.Name,
}
a.pages[event.Name] = page
}
page.apiSt.activeMu.Lock()
2024-05-02 19:30:25 +00:00
page.apiSt.active = !event.Stop
page.apiSt.activeMu.Unlock()
if event.Stop {
runtime.EventsEmit(a.wails, "ApiActive-"+page.name, false)
2024-05-22 16:51:46 +00:00
return
}
runtime.EventsEmit(a.wails, "ApiActive-"+page.name, true)
page.displayingMu.Lock()
if page.displaying {
runtime.EventsEmit(a.wails, "PageActive", true)
}
page.displayingMu.Unlock()
page.apiSt.respMu.Lock()
page.apiSt.resp = event.Resp
page.apiSt.respMu.Unlock()
a.updatePage(page)
2024-05-22 16:51:46 +00:00
}
2024-05-22 16:51:46 +00:00
type chatProcessor func(event events.Chat)
func (a *App) runChatProcessors(event events.Chat, procs ...chatProcessor) {
for _, proc := range procs {
proc(event)
}
}
2024-05-22 16:51:46 +00:00
func (a *App) processChat(event events.Chat) {
if event.Stop {
runtime.EventsEmit(a.wails, "ChatStreamActive-"+event.Url, false)
return
}
a.runChatProcessors(
event,
a.chatbotChatProcessor,
)
}
// TODO: implement this
func (a *App) chatbotApiProcessor(event events.Api) {
return
}
func (a *App) chatbotChatProcessor(event events.Chat) {
if event.Message.Type == rumblelivestreamlib.ChatTypeInit {
return
}
a.chatbot.HandleChat(event)
2024-05-02 19:30:25 +00:00
}
func (a *App) shutdown(ctx context.Context) {
err := a.producers.Shutdown()
if err != nil {
a.logError.Println("error closing event producers:", err)
}
2024-05-02 19:30:25 +00:00
a.cancelProc()
2024-04-05 15:05:17 +00:00
if a.services != nil {
err := a.services.Close()
if err != nil {
a.logError.Println("error closing services:", err)
}
}
a.logFileMu.Lock()
if a.logFile != nil {
err := a.logFile.Close()
if err != nil {
log.Println("error closing log file:", err)
}
}
a.logFileMu.Unlock()
}
func (a *App) Start() (bool, error) {
runtime.EventsEmit(a.wails, "StartupMessage", "Initializing database...")
2024-04-05 15:05:17 +00:00
err := a.initServices()
if err != nil {
a.logError.Println("error initializing services:", err)
return false, fmt.Errorf("Error starting Rum Goggles. Try restarting.")
}
runtime.EventsEmit(a.wails, "StartupMessage", "Initializing database complete.")
2024-04-05 15:05:17 +00:00
runtime.EventsEmit(a.wails, "StartupMessage", "Verifying account sessions...")
2024-04-05 15:05:17 +00:00
count, err := a.verifyAccounts()
if err != nil {
a.logError.Println("error verifying accounts:", err)
return false, fmt.Errorf("Error starting Rum Goggles. Try restarting.")
}
runtime.EventsEmit(a.wails, "StartupMessage", "Verifying account sessions complete.")
runtime.EventsEmit(a.wails, "StartupMessage", "Initializing event producers...")
err = a.initProducers()
if err != nil {
a.logError.Println("error initializing producers:", err)
return false, fmt.Errorf("Error starting Rum Goggles. Try restarting.")
}
runtime.EventsEmit(a.wails, "StartupMessage", "Initializing event producers complete.")
2024-04-05 15:05:17 +00:00
2024-05-22 16:51:46 +00:00
runtime.EventsEmit(a.wails, "StartupMessage", "Initializing chat bot...")
err = a.initChatbot()
if err != nil {
a.logError.Println("error initializing chat bot:", err)
return false, fmt.Errorf("Error starting Rum Goggles. Try restarting.")
}
runtime.EventsEmit(a.wails, "StartupMessage", "Initializing chat bot complete.")
2024-04-05 15:05:17 +00:00
// TODO: check for update - if available, pop up window
// runtime.EventsEmit(a.ctx, "StartupMessage", "Checking for updates...")
// update, err = a.checkForUpdate()
// runtime.EventsEmit(a.ctx, "StartupMessage", "Checking for updates complete.")
ctx, cancel := context.WithCancel(context.Background())
2024-05-02 19:30:25 +00:00
a.cancelProc = cancel
go a.process(ctx)
2024-04-05 15:05:17 +00:00
signin := true
if count > 0 {
signin = false
}
return signin, nil
}
2024-05-22 16:51:46 +00:00
func (a *App) initChatbot() error {
cb := chatbot.New(a.services.AccountS, a.services.ChatbotS, a.logError, a.wails)
a.chatbot = cb
return nil
}
func (a *App) initProducers() error {
producers, err := events.NewProducers(
events.WithLoggers(a.logError, a.logInfo),
events.WithApiProducer(),
2024-05-02 19:30:25 +00:00
events.WithChatProducer(),
)
if err != nil {
return fmt.Errorf("error initializing producers: %v", err)
}
err = producers.Startup()
if err != nil {
return fmt.Errorf("error starting producers: %v", err)
}
a.producers = producers
return nil
}
2024-04-05 15:05:17 +00:00
func (a *App) initServices() error {
db, err := config.Database()
if err != nil {
2024-04-05 15:05:17 +00:00
return fmt.Errorf("error getting database config: %v", err)
}
services, err := models.NewServices(
models.WithDatabase(db),
models.WithAccountService(),
models.WithChannelService(),
models.WithAccountChannelService(),
2024-05-02 19:30:25 +00:00
models.WithChatbotService(),
2024-05-22 16:51:46 +00:00
models.WithChatbotRuleService(),
)
if err != nil {
2024-04-05 15:05:17 +00:00
return fmt.Errorf("error initializing services: %v", err)
}
err = services.AutoMigrate()
if err != nil {
2024-04-05 15:05:17 +00:00
return fmt.Errorf("error auto-migrating services: %v", err)
}
a.services = services
2024-04-05 15:05:17 +00:00
return nil
}
2024-04-05 15:05:17 +00:00
func (a *App) verifyAccounts() (int, error) {
accounts, err := a.services.AccountS.All()
if err != nil {
return -1, fmt.Errorf("error querying all accounts: %v", err)
2024-04-04 14:46:14 +00:00
}
2024-04-05 15:05:17 +00:00
a.clientsMu.Lock()
defer a.clientsMu.Unlock()
for _, account := range accounts {
if account.Cookies != nil {
var cookies []*http.Cookie
err = json.Unmarshal([]byte(*account.Cookies), &cookies)
if err != nil {
return -1, fmt.Errorf("error un-marshaling cookie string: %v", err)
}
client, err := rumblelivestreamlib.NewClient(rumblelivestreamlib.NewClientOptions{Cookies: cookies})
if err != nil {
return -1, fmt.Errorf("error creating new client: %v", err)
}
if account.Username == nil {
return -1, fmt.Errorf("account username is nil")
}
loggedIn, err := client.LoggedIn()
if err != nil {
return -1, fmt.Errorf("error check if account is logged in: %v", err)
}
if loggedIn {
a.clients[*account.Username] = client
} else {
account.Cookies = nil
err = a.services.AccountS.Update(&account)
2024-05-02 19:30:25 +00:00
if err != nil {
return -1, fmt.Errorf("error updating account: %v", err)
}
2024-04-05 15:05:17 +00:00
}
}
}
2024-04-05 15:05:17 +00:00
return len(accounts), nil
}
2024-04-04 14:46:14 +00:00
func (a *App) AddPage(apiKey string) error {
2024-05-02 19:30:25 +00:00
client := rumblelivestreamlib.Client{ApiKey: apiKey}
resp, err := client.Request()
if err != nil {
a.logError.Println("error executing api request:", err)
return fmt.Errorf("Error querying API. Verify key and try again.")
}
userKey := apiKey
channelKey := ""
if resp.Type == "channel" {
userKey = ""
channelKey = apiKey
}
err = a.addAccountNotExist(resp.UserID, resp.Username, userKey)
if err != nil {
a.logError.Println("error adding account if not exist:", err)
return fmt.Errorf("Error adding channel. Try again.")
}
if resp.Type == "channel" {
err = a.addChannelNotExist(resp.Username, fmt.Sprint(resp.ChannelID), resp.ChannelName, channelKey)
if err != nil {
a.logError.Println("error adding channel if not exist:", err)
return fmt.Errorf("Error adding channel. Try again.")
}
}
2024-05-02 19:30:25 +00:00
list, err := a.accountList()
if err != nil {
a.logError.Println("error getting account list:", err)
return fmt.Errorf("Error logging in. Try again.")
}
runtime.EventsEmit(a.wails, "PageSideBarAccounts", list)
return nil
}
func (a *App) addAccountNotExist(uid string, username string, apiKey string) error {
acct, err := a.services.AccountS.ByUsername(username)
if err != nil {
return fmt.Errorf("error querying account by username: %v", err)
}
if acct == nil {
2024-04-04 14:46:14 +00:00
_, err = a.services.AccountS.Create(&models.Account{
UID: &uid,
Username: &username,
ApiKey: &apiKey,
})
if err != nil {
return fmt.Errorf("error creating account: %v", err)
}
}
return nil
}
func (a *App) addChannelNotExist(username string, cid string, name string, apiKey string) error {
channel, err := a.services.ChannelS.ByName(name)
if err != nil {
return fmt.Errorf("error querying channel by name: %v", err)
}
if channel == nil {
acct, err := a.services.AccountS.ByUsername(username)
if err != nil {
return fmt.Errorf("error querying account by username: %v", err)
}
if acct == nil {
return fmt.Errorf("account does not exist with username: %s", username)
}
err = a.services.ChannelS.Create(&models.Channel{
AccountID: acct.ID,
CID: &cid,
Name: &name,
ApiKey: &apiKey,
})
if err != nil {
return fmt.Errorf("error creating channel: %v", err)
}
}
return nil
}
func (a *App) Login(username string, password string) error {
var err error
2024-04-04 14:46:14 +00:00
a.clientsMu.Lock()
defer a.clientsMu.Unlock()
client, exists := a.clients[username]
if exists && client != nil {
err = client.Logout()
if err != nil {
a.logError.Println("error logging out:", err)
return fmt.Errorf("Error logging in. Try again.")
}
} else {
client, err = rumblelivestreamlib.NewClient(rumblelivestreamlib.NewClientOptions{})
}
cookies, err := client.Login(username, password)
if err != nil {
a.logError.Println("error logging in:", err)
return fmt.Errorf("Error logging in. Try again.")
}
a.clients[username] = client
cookiesB, err := json.Marshal(cookies)
if err != nil {
a.logError.Println("error marshaling cookies into json:", err)
return fmt.Errorf("Error logging in. Try again.")
}
cookiesS := string(cookiesB)
2024-04-04 14:46:14 +00:00
acct, err := a.services.AccountS.ByUsername(username)
if err != nil {
a.logError.Println("error getting account by username:", err)
return fmt.Errorf("Error logging in. Try again.")
}
2024-04-04 14:46:14 +00:00
if acct == nil {
acct = &models.Account{nil, nil, &username, &cookiesS, nil, nil}
id, err := a.services.AccountS.Create(acct)
if err != nil {
a.logError.Println("error creating account:", err)
return fmt.Errorf("Error logging in. Try again.")
}
2024-04-04 14:46:14 +00:00
acct.ID = &id
} else {
2024-04-04 14:46:14 +00:00
acct.Cookies = &cookiesS
err = a.services.AccountS.Update(acct)
if err != nil {
a.logError.Println("error updating account:", err)
return fmt.Errorf("Error logging in. Try again.")
}
}
2024-04-04 14:46:14 +00:00
name := acct.String()
if name == nil {
a.logError.Println("account name is nil")
return fmt.Errorf("Error logging in. Try again.")
}
runtime.EventsEmit(a.wails, "LoggedIn-"+*name, true)
2024-04-04 14:46:14 +00:00
list, err := a.accountList()
if err != nil {
a.logError.Println("error getting account list:", err)
return fmt.Errorf("Error logging in. Try again.")
}
runtime.EventsEmit(a.wails, "PageSideBarAccounts", list)
2024-04-04 14:46:14 +00:00
err = a.openDetails(acct)
if err != nil {
a.logError.Println("error opening account details:", err)
return fmt.Errorf("Error logging in. Try again.")
}
return nil
}
func (a *App) Logout(id int64) error {
acct, err := a.services.AccountS.ByID(id)
if err != nil {
a.logError.Println("error querying account by ID:", err)
return fmt.Errorf("Error logging out. Try again.")
}
if acct == nil {
return fmt.Errorf("Did not find account. Try again.")
}
if acct.Username == nil {
a.logError.Println("account username is nil")
return fmt.Errorf("Error logging out. Try again.")
}
a.clientsMu.Lock()
defer a.clientsMu.Unlock()
client, exists := a.clients[*acct.Username]
if exists {
err = client.Logout()
if err != nil {
a.logError.Println("error logging out:", err)
return fmt.Errorf("Error logging out. Try again.")
}
delete(a.clients, *acct.Username)
}
if acct.Cookies != nil {
2024-04-05 15:05:17 +00:00
if !exists {
var cookies []*http.Cookie
err = json.Unmarshal([]byte(*acct.Cookies), &cookies)
if err != nil {
a.logError.Println("error un-marshaling cookie string:", err)
return fmt.Errorf("Error logging out. Try again.")
}
client, err = rumblelivestreamlib.NewClient(rumblelivestreamlib.NewClientOptions{Cookies: cookies})
err = client.Logout()
if err != nil {
a.logError.Println("error logging out:", err)
return fmt.Errorf("Error logging out. Try again.")
}
}
2024-04-04 14:46:14 +00:00
acct.Cookies = nil
err = a.services.AccountS.Update(acct)
if err != nil {
a.logError.Println("error updating account:", err)
return fmt.Errorf("Error logging out. Try again.")
}
}
name := acct.String()
if name == nil {
a.logError.Println("account name is nil")
return fmt.Errorf("Error logging out. Try again.")
}
runtime.EventsEmit(a.wails, "LoggedIn-"+*name, false)
2024-04-04 14:46:14 +00:00
list, err := a.accountList()
if err != nil {
a.logError.Println("error getting account list:", err)
return fmt.Errorf("Error logging out. Try again.")
}
runtime.EventsEmit(a.wails, "PageSideBarAccounts", list)
2024-04-04 14:46:14 +00:00
err = a.openDetails(acct)
if err != nil {
a.logError.Println("error opening account details:", err)
return fmt.Errorf("Error logging out. Try again.")
}
return nil
2024-02-23 17:10:39 +00:00
}
func (a *App) SignedIn() (bool, error) {
accounts, err := a.services.AccountS.All()
if err != nil {
a.logError.Println("error getting all accounts:", err)
return false, fmt.Errorf("Error retrieving accounts. Try restarting.")
}
return len(accounts) > 0, nil
}
type Account struct {
Account models.Account `json:"account"`
Channels []models.Channel `json:"channels"`
}
func (a *App) AccountList() (map[string]*Account, error) {
2024-04-04 14:46:14 +00:00
list, err := a.accountList()
if err != nil {
a.logError.Println("error getting account list:", err)
return nil, fmt.Errorf("Error retrieving accounts and channels. Try restarting.")
}
return list, nil
}
func (a *App) accountList() (map[string]*Account, error) {
list := map[string]*Account{}
accountChannels, err := a.services.AccountChannelS.All()
if err != nil {
2024-04-04 14:46:14 +00:00
return nil, fmt.Errorf("error querying all account channels: %v", err)
}
for _, ac := range accountChannels {
if ac.Account.Username == nil {
2024-04-04 14:46:14 +00:00
return nil, fmt.Errorf("account-channel contains nil account username")
}
act, exists := list[*ac.Account.Username]
if !exists || act == nil {
act = &Account{ac.Account, []models.Channel{}}
list[*ac.Account.Username] = act
}
if ac.Channel.AccountID != nil {
act.Channels = append(act.Channels, ac.Channel)
}
}
return list, nil
}
2024-04-04 14:46:14 +00:00
func (a *App) OpenAccount(id int64) error {
acct, err := a.services.AccountS.ByID(id)
if err != nil {
a.logError.Println("error querying account by ID:", err)
return fmt.Errorf("Error opening account. Try again.")
}
if acct == nil {
return fmt.Errorf("Did not find account. Try again.")
}
err = a.openDetails(acct)
if err != nil {
a.logError.Println("error opening account details:", err)
return fmt.Errorf("Error opening account. Try again.")
}
return nil
}
func (a *App) OpenChannel(id int64) error {
channel, err := a.services.ChannelS.ByID(id)
if err != nil {
a.logError.Println("error querying channel by ID:", err)
return fmt.Errorf("Error opening channel. Try again.")
}
if channel == nil {
return fmt.Errorf("Did not find channel. Try again.")
}
err = a.openDetails(channel)
if err != nil {
a.logError.Println("error opening channel details:", err)
return fmt.Errorf("Error opening channel. Try again.")
}
return nil
}
type PageInfo interface {
2024-04-04 14:46:14 +00:00
Id() *int64
KeyUrl() *string
LoggedIn() bool
String() *string
Title() *string
Type() string
}
type PageDetails struct {
ID int64 `json:"id"`
HasApi bool `json:"has_api"`
LoggedIn bool `json:"logged_in"`
Title string `json:"title"`
Type string `json:"type"`
}
func (a *App) openDetails(pi PageInfo) error {
id := pi.Id()
2024-04-04 14:46:14 +00:00
if id == nil {
return fmt.Errorf("page id is nil")
}
hasApi := true
key := pi.KeyUrl()
2024-04-04 14:46:14 +00:00
if key == nil || *key == "" {
hasApi = false
}
name := pi.String()
2024-04-04 14:46:14 +00:00
if name == nil {
return fmt.Errorf("page name is nil")
}
title := pi.Title()
2024-04-04 14:46:14 +00:00
if title == nil {
return fmt.Errorf("page title is nil")
}
runtime.EventsEmit(a.wails, "PageDetails", PageDetails{
2024-04-04 14:46:14 +00:00
ID: *id,
HasApi: hasApi,
LoggedIn: pi.LoggedIn(),
2024-04-04 14:46:14 +00:00
Title: *title,
Type: pi.Type(),
2024-04-04 14:46:14 +00:00
})
err := a.display(*name)
2024-04-04 14:46:14 +00:00
if err != nil {
return fmt.Errorf("error displaying page: %v", err)
2024-04-04 14:46:14 +00:00
}
return nil
}
func (a *App) ActivateAccount(id int64) error {
acct, err := a.services.AccountS.ByID(id)
if err != nil {
a.logError.Println("error querying account by ID:", err)
return fmt.Errorf("Error activating account. Try again.")
}
if acct == nil {
return fmt.Errorf("Did not find account. Try again.")
}
err = a.activatePage(acct)
if err != nil {
a.logError.Println("error activating account:", err)
return fmt.Errorf("Error activating account. Try again.")
}
return nil
}
// ActivateChannel activates an inactivate page and deactivates an active page.
func (a *App) ActivateChannel(id int64) error {
channel, err := a.services.ChannelS.ByID(id)
if err != nil {
a.logError.Println("error querying channel by ID:", err)
return fmt.Errorf("Error activating channel. Try again.")
}
if channel == nil {
return fmt.Errorf("Did not find channel. Try again.")
}
err = a.activatePage(channel)
if err != nil {
a.logError.Println("error activating channel:", err)
return fmt.Errorf("Error activating channel. Try again.")
}
return nil
}
// If page is inactivate, activate.
// If page is active, deactivate.
func (a *App) activatePage(pi PageInfo) error {
name := pi.String()
2024-04-04 14:46:14 +00:00
if name == nil {
return fmt.Errorf("page name is nil")
}
url := pi.KeyUrl()
2024-04-04 14:46:14 +00:00
if url == nil {
return fmt.Errorf("page key url is nil")
}
a.pagesMu.Lock()
page, exists := a.pages[*name]
if !exists {
page = &Page{
active: false,
apiSt: &ApiState{},
name: *name,
}
a.pages[*name] = page
}
a.pagesMu.Unlock()
page.activeMu.Lock()
defer page.activeMu.Unlock()
if page.active {
if a.producers.ApiP.Active(*name) {
err := a.producers.ApiP.Stop(*name)
if err != nil {
return fmt.Errorf("error stopping api: %v", err)
}
}
page.displayingMu.Lock()
if page.displaying {
runtime.EventsEmit(a.wails, "PageActive", false)
2024-04-04 14:46:14 +00:00
}
page.displayingMu.Unlock()
page.active = false
2024-04-04 14:46:14 +00:00
return nil
}
page.active = true
2024-04-04 14:46:14 +00:00
err := a.producers.ApiP.Start(*name, *url, 10*time.Second)
2024-04-04 14:46:14 +00:00
if err != nil {
return fmt.Errorf("error starting api: %v", err)
}
runtime.EventsEmit(a.wails, "ApiActive-"+*name, true)
err = a.display(*name)
2024-04-04 14:46:14 +00:00
if err != nil {
return fmt.Errorf("error displaying page: %v", err)
2024-04-04 14:46:14 +00:00
}
runtime.EventsEmit(a.wails, "PageActive", true)
2024-04-04 14:46:14 +00:00
return nil
}
func (a *App) DeleteAccount(id int64) error {
acct, err := a.services.AccountS.ByID(id)
if err != nil {
a.logError.Println("error querying account by ID:", err)
return fmt.Errorf("Error deleting account. Try again.")
}
if acct == nil {
return fmt.Errorf("Did not find account. Try again.")
}
channels, err := a.services.ChannelS.ByAccount(acct)
if err != nil {
a.logError.Println("error querying channels by account:", err)
return fmt.Errorf("Error deleting account. Try again.")
}
if len(channels) != 0 {
return fmt.Errorf("You must delete all channels associated with the account before it can be deleted.")
}
name := acct.String()
if name == nil {
a.logError.Println("account name is nil")
return fmt.Errorf("Error deleting account. Try again.")
}
if a.producers.ApiP.Active(*name) {
err := a.producers.ApiP.Stop(*name)
2024-04-04 14:46:14 +00:00
if err != nil {
a.logError.Println("error stopping api:", err)
return fmt.Errorf("Error deleting account. Try again.")
}
}
err = a.services.AccountS.Delete(acct)
if err != nil {
a.logError.Println("error deleting account:", err)
return fmt.Errorf("Error deleting account. Try again.")
}
runtime.EventsEmit(a.wails, "PageDetails", nil)
2024-04-04 14:46:14 +00:00
list, err := a.accountList()
if err != nil {
a.logError.Println("error getting account list:", err)
return fmt.Errorf("Error deleting account. Try again.")
}
runtime.EventsEmit(a.wails, "PageSideBarAccounts", list)
2024-04-04 14:46:14 +00:00
return nil
}
func (a *App) DeleteChannel(id int64) error {
channel, err := a.services.ChannelS.ByID(id)
if err != nil {
a.logError.Println("error querying channel by ID:", err)
return fmt.Errorf("Error deleting channel. Try again.")
}
if channel == nil {
return fmt.Errorf("Did not find channel. Try again.")
}
name := channel.String()
if name == nil {
a.logError.Println("channel name is nil")
return fmt.Errorf("Error deleting channel. Try again.")
}
if a.producers.ApiP.Active(*name) {
err := a.producers.ApiP.Stop(*name)
2024-04-04 14:46:14 +00:00
if err != nil {
a.logError.Println("error stopping api:", err)
return fmt.Errorf("Error deleting channel. Try again.")
}
}
err = a.services.ChannelS.Delete(channel)
if err != nil {
a.logError.Println("error deleting channel:", err)
return fmt.Errorf("Error deleting channel. Try again.")
}
runtime.EventsEmit(a.wails, "PageDetails", nil)
2024-04-04 14:46:14 +00:00
list, err := a.accountList()
if err != nil {
a.logError.Println("error getting account list:", err)
return fmt.Errorf("Error deleting channel. Try again.")
}
runtime.EventsEmit(a.wails, "PageSideBarAccounts", list)
return nil
}
func (a *App) display(name string) error {
a.displayingMu.Lock()
defer a.displayingMu.Unlock()
if name == a.displaying {
return nil
}
a.pagesMu.Lock()
defer a.pagesMu.Unlock()
if a.displaying != "" {
displaying, exists := a.pages[a.displaying]
a.displaying = ""
if !exists {
return fmt.Errorf("displaying page does not exist: %s", a.display)
}
displaying.displayingMu.Lock()
displaying.displaying = false
displaying.displayingMu.Unlock()
}
page, exists := a.pages[name]
if !exists {
page = &Page{
active: false,
apiSt: &ApiState{},
name: name,
}
a.pages[name] = page
}
page.displayingMu.Lock()
page.displaying = true
page.displayingMu.Unlock()
a.displaying = name
err := a.updatePage(page)
if err != nil {
return fmt.Errorf("error updating page: %v", err)
}
return nil
}
func (a *App) updatePage(p *Page) error {
if p == nil {
return fmt.Errorf("page is nil")
}
//TODO check p.api == nil
p.apiSt.respMu.Lock()
defer p.apiSt.respMu.Unlock()
p.displayingMu.Lock()
if p.displaying {
runtime.EventsEmit(a.wails, "PageActivity", p.apiSt.resp)
p.activeMu.Lock()
runtime.EventsEmit(a.wails, "PageActive", p.active)
p.activeMu.Unlock()
}
p.displayingMu.Unlock()
if p.apiSt.resp != nil {
isLive := len(p.apiSt.resp.Livestreams) > 0
runtime.EventsEmit(a.wails, "PageLive-"+p.name, isLive)
}
2024-04-04 14:46:14 +00:00
return nil
}
func (a *App) PageStatus(name string) {
active := false
isLive := false
a.pagesMu.Lock()
defer a.pagesMu.Unlock()
page, exists := a.pages[name]
if exists && page.apiSt != nil {
page.apiSt.activeMu.Lock()
active = page.apiSt.active
page.apiSt.activeMu.Unlock()
page.apiSt.respMu.Lock()
if page.apiSt.resp != nil {
isLive = len(page.apiSt.resp.Livestreams) > 0
}
page.apiSt.respMu.Unlock()
2024-04-04 14:46:14 +00:00
}
runtime.EventsEmit(a.wails, "ApiActive-"+name, active)
runtime.EventsEmit(a.wails, "PageLive-"+name, isLive)
2024-04-04 14:46:14 +00:00
}
func (a *App) UpdateAccountApi(id int64, apiKey string) error {
acct, err := a.services.AccountS.ByID(id)
if err != nil {
a.logError.Println("error querying account by ID:", err)
return fmt.Errorf("Error updating account. Try again.")
}
if acct == nil {
return fmt.Errorf("Did not find account. Try again.")
}
name := acct.String()
if name == nil {
a.logError.Println("account name is nil")
return fmt.Errorf("Error updating account. Try again.")
}
if a.producers.ApiP.Active(*name) {
err := a.producers.ApiP.Stop(*name)
2024-04-04 14:46:14 +00:00
if err != nil {
a.logError.Println("error stopping api:", err)
return fmt.Errorf("Error updating account. Try again.")
}
}
2024-05-02 19:30:25 +00:00
client := rumblelivestreamlib.Client{ApiKey: apiKey}
2024-04-04 14:46:14 +00:00
resp, err := client.Request()
if err != nil {
a.logError.Println("error executing api request:", err)
return fmt.Errorf("Error querying API. Verify key and try again.")
}
if resp.ChannelName != "" || resp.Username != *acct.Username {
return fmt.Errorf("API key does not belong to account. Verify key and try again.")
}
acct.ApiKey = &apiKey
err = a.services.AccountS.Update(acct)
if err != nil {
a.logError.Println("error updating account:", err)
return fmt.Errorf("Error updating account. Try again.")
}
return nil
}
func (a *App) UpdateChannelApi(id int64, apiKey string) error {
channel, err := a.services.ChannelS.ByID(id)
if err != nil {
a.logError.Println("error querying channel by ID:", err)
return fmt.Errorf("Error updating channel. Try again.")
}
if channel == nil {
return fmt.Errorf("Did not find channel. Try again.")
}
name := channel.String()
if name == nil {
a.logError.Println("channel name is nil")
return fmt.Errorf("Error updating channel. Try again.")
}
if a.producers.ApiP.Active(*name) {
err := a.producers.ApiP.Stop(*name)
2024-04-04 14:46:14 +00:00
if err != nil {
a.logError.Println("error stopping api:", err)
return fmt.Errorf("Error updating channel. Try again.")
}
}
2024-05-02 19:30:25 +00:00
client := rumblelivestreamlib.Client{ApiKey: apiKey}
2024-04-04 14:46:14 +00:00
resp, err := client.Request()
if err != nil {
a.logError.Println("error executing api request:", err)
return fmt.Errorf("Error querying API. Verify key and try again.")
}
if resp.ChannelName != *channel.Name {
return fmt.Errorf("API key does not belong to channel. Verify key and try again.")
}
*channel.ApiKey = apiKey
err = a.services.ChannelS.Update(channel)
if err != nil {
a.logError.Println("error updating channel:", err)
return fmt.Errorf("Error updating channel. Try again.")
}
return nil
}
2024-05-02 19:30:25 +00:00
func (a *App) DeleteChatbot(chatbot *models.Chatbot) error {
if chatbot == nil || chatbot.ID == nil {
return fmt.Errorf("Invalid chatbot. Try again.")
}
2024-05-22 16:51:46 +00:00
err := a.StopChatbotRules(chatbot.ID)
if err != nil {
a.logError.Println("error stopping chatbot rules before deleting chatbot")
return fmt.Errorf("Error deleting chatbot. Could not stop running rules. Try Again.")
}
2024-05-02 19:30:25 +00:00
cb, err := a.services.ChatbotS.ByID(*chatbot.ID)
if err != nil {
a.logError.Println("error getting chatbot by ID:", err)
return fmt.Errorf("Error deleting chatbot. Try again.")
}
if cb == nil {
return fmt.Errorf("Chatbot does not exist.")
}
2024-05-22 16:51:46 +00:00
rules, err := a.services.ChatbotRuleS.ByChatbotID(*chatbot.ID)
if err != nil {
a.logError.Println("error getting chatbot rules by chatbot ID:", err)
return fmt.Errorf("Error deleting chatbot. Try again.")
}
for _, rule := range rules {
err = a.services.ChatbotRuleS.Delete(&rule)
if err != nil {
a.logError.Println("error deleting chatbot rule:", err)
return fmt.Errorf("Error deleting chatbot. Try again.")
}
}
2024-05-02 19:30:25 +00:00
err = a.services.ChatbotS.Delete(chatbot)
if err != nil {
a.logError.Println("error deleting chatbot:", err)
return fmt.Errorf("Error deleting chatbot. Try again.")
}
list, err := a.chatbotList()
if err != nil {
a.logError.Println("error getting chatbot list:", err)
return fmt.Errorf("Error deleting chatbot. Try again.")
}
runtime.EventsEmit(a.wails, "ChatbotList", list)
return nil
}
func (a *App) NewChatbot(chatbot *models.Chatbot) error {
if chatbot == nil || chatbot.Name == nil {
return fmt.Errorf("Invalid chatbot. Try again.")
}
cb, err := a.services.ChatbotS.ByName(*chatbot.Name)
if err != nil {
a.logError.Println("error getting chatbot by name:", err)
return fmt.Errorf("Error creating chatbot. Try again.")
}
if cb != nil {
return fmt.Errorf("Chatbot name already exists.")
}
_, err = a.services.ChatbotS.Create(chatbot)
if err != nil {
a.logError.Println("error creating chatbot:", err)
return fmt.Errorf("Error creating chatbot. Try again.")
}
list, err := a.chatbotList()
if err != nil {
a.logError.Println("error getting chatbot list:", err)
return fmt.Errorf("Error creating chatbot. Try again.")
}
runtime.EventsEmit(a.wails, "ChatbotList", list)
return nil
}
func (a *App) UpdateChatbot(chatbot *models.Chatbot) error {
if chatbot == nil || chatbot.ID == nil || chatbot.Name == nil {
return fmt.Errorf("Invalid chatbot. Try again.")
}
cb, err := a.services.ChatbotS.ByID(*chatbot.ID)
if err != nil {
a.logError.Println("error getting chatbot by ID:", err)
return fmt.Errorf("Error updating chatbot. Try again.")
}
if cb == nil {
return fmt.Errorf("Chatbot does not exist.")
}
cbByName, err := a.services.ChatbotS.ByName(*chatbot.Name)
if err != nil {
a.logError.Println("error getting chatbot by Name:", err)
return fmt.Errorf("Error updating chatbot. Try again.")
}
if cbByName != nil && *cbByName.ID != *cb.ID {
return fmt.Errorf("Chatbot name already exists.")
}
err = a.services.ChatbotS.Update(chatbot)
if err != nil {
a.logError.Println("error updating chatbot:", err)
return fmt.Errorf("Error updating chatbot. Try again.")
}
list, err := a.chatbotList()
if err != nil {
a.logError.Println("error getting chatbot list:", err)
return fmt.Errorf("Error updating chatbot. Try again.")
}
runtime.EventsEmit(a.wails, "ChatbotList", list)
return nil
}
func (a *App) ChatbotList() ([]models.Chatbot, error) {
list, err := a.chatbotList()
if err != nil {
a.logError.Println("error getting chatbot list:", err)
return nil, fmt.Errorf("Error retrieving chatbots. Try restarting.")
}
return list, nil
}
func (a *App) chatbotList() ([]models.Chatbot, error) {
list, err := a.services.ChatbotS.All()
if err != nil {
return nil, fmt.Errorf("error querying all chatbots: %v", err)
}
return list, err
}
2024-05-22 16:51:46 +00:00
func (a *App) ChatbotRules(chatbot *models.Chatbot) ([]chatbot.Rule, error) {
if chatbot == nil || chatbot.ID == nil {
return nil, fmt.Errorf("Invalid chatbot. Try again.")
}
rules, err := a.chatbotRules(*chatbot.ID)
if err != nil {
a.logError.Println("error getting chatbot rules:", err)
return nil, fmt.Errorf("Error getting chatbot rules. Try again.")
}
return rules, nil
2024-05-02 19:30:25 +00:00
}
2024-05-22 16:51:46 +00:00
func (a *App) chatbotRules(chatbotID int64) ([]chatbot.Rule, error) {
modelsRules, err := a.services.ChatbotRuleS.ByChatbotID(chatbotID)
if err != nil {
return nil, fmt.Errorf("error querying chatbot rules: %v", err)
}
rules := []chatbot.Rule{}
for _, modelsRule := range modelsRules {
rule := chatbot.Rule{
ID: modelsRule.ID,
ChatbotID: modelsRule.ChatbotID,
}
if modelsRule.Parameters != nil {
var params chatbot.RuleParameters
err = json.Unmarshal([]byte(*modelsRule.Parameters), &params)
if err != nil {
return nil, fmt.Errorf("error un-marshaling chatbot rule parameters from json: %v", err)
}
rule.Parameters = &params
}
rule.Running = a.chatbot.Running(*rule.ChatbotID, *rule.ID)
rule.Display = rule.Parameters.Message.FromText
if rule.Parameters.Message.FromFile != nil {
rule.Display = filepath.Base(rule.Parameters.Message.FromFile.Filepath)
}
rules = append(rules, rule)
}
chatbot.SortRules(rules)
return rules, err
2024-05-02 19:30:25 +00:00
}
2024-05-22 16:51:46 +00:00
func (a *App) DeleteChatbotRule(rule *chatbot.Rule) error {
if rule == nil || rule.ID == nil || rule.ChatbotID == nil {
return fmt.Errorf("Invalid chatbot rule. Try again.")
}
mRule, err := rule.ToModelsChatbotRule()
if err != nil {
a.logError.Println("error converting chatbot.Rule into models.ChatbotRule:", err)
return fmt.Errorf("Error deleting chatbot rule. Try again.")
}
err = a.chatbot.Stop(rule)
if err != nil {
a.logError.Println("error stopping chatbot rule:", err)
return fmt.Errorf("Error deleting chatbot rule. Try again.")
}
err = a.services.ChatbotRuleS.Delete(mRule)
if err != nil {
a.logError.Println("error deleting chatbot rule:", err)
return fmt.Errorf("Error deleting chatbot rule. Try again.")
}
rules, err := a.chatbotRules(*rule.ChatbotID)
if err != nil {
a.logError.Println("error getting chatbot rules:", err)
return fmt.Errorf("Error deleting chatbot rule. Try again.")
}
runtime.EventsEmit(a.wails, "ChatbotRules", rules)
return nil
2024-05-02 19:30:25 +00:00
}
2024-05-22 16:51:46 +00:00
func (a *App) NewChatbotRule(rule *chatbot.Rule) error {
if rule == nil || rule.ChatbotID == nil || rule.Parameters == nil {
return fmt.Errorf("Invalid chatbot rule. Try again.")
}
mRule, err := rule.ToModelsChatbotRule()
if err != nil {
a.logError.Println("error converting chatbot.Rule into models.ChatbotRule:", err)
return fmt.Errorf("Error creating chatbot rule. Try again.")
}
_, err = a.services.ChatbotRuleS.Create(mRule)
if err != nil {
a.logError.Println("error creating chatbot rule:", err)
return fmt.Errorf("Error creating chatbot rule. Try again.")
}
rules, err := a.chatbotRules(*rule.ChatbotID)
if err != nil {
a.logError.Println("error getting chatbot rules:", err)
return fmt.Errorf("Error creating chatbot rule. Try again.")
}
runtime.EventsEmit(a.wails, "ChatbotRules", rules)
return nil
2024-05-02 19:30:25 +00:00
}
2024-05-22 16:51:46 +00:00
func (a *App) RunChatbotRule(rule *chatbot.Rule) error {
if rule == nil || rule.ChatbotID == nil {
return fmt.Errorf("Invalid chatbot rule. Try again.")
}
mChatbot, err := a.services.ChatbotS.ByID(*rule.ChatbotID)
if err != nil {
a.logError.Println("error getting chatbot by ID:", err)
return fmt.Errorf("Error running chatbot rule. Try again.")
}
if mChatbot == nil {
return fmt.Errorf("Chatbot does not exist. Try again.")
}
if mChatbot.Url == nil {
a.logError.Println("chatbot url is nil")
return fmt.Errorf("Chatbot url is not set. Update url and try again.")
}
_, err = a.producers.ChatP.Start(*mChatbot.Url)
if err != nil {
a.logError.Println("error starting chat producer:", err)
// TODO: send error to UI that chatbot URL could not be started
//runtime.EventsEmit("Ch")
}
err = a.chatbot.Run(rule, *mChatbot.Url)
if err != nil {
a.logError.Println("error running chat bot rule:", err)
return fmt.Errorf("Error running chatbot rule. Try again.")
}
return nil
2024-05-02 19:30:25 +00:00
}
2024-05-22 16:51:46 +00:00
func (a *App) StopChatbotRule(rule *chatbot.Rule) error {
err := a.chatbot.Stop(rule)
if err != nil {
a.logError.Println("error stopping chat bot rule:", err)
return fmt.Errorf("Error stopping chatbot rule. Try again.")
}
return nil
2024-05-02 19:30:25 +00:00
}
2024-05-22 16:51:46 +00:00
func (a *App) UpdateChatbotRule(rule *chatbot.Rule) error {
if rule == nil || rule.ID == nil || rule.ChatbotID == nil {
return fmt.Errorf("Invalid chatbot rule. Try again.")
}
mRule, err := rule.ToModelsChatbotRule()
if err != nil {
a.logError.Println("error converting chatbot.Rule into models.ChatbotRule:", err)
return fmt.Errorf("Error updating chatbot rule. Try again.")
}
err = a.chatbot.Stop(rule)
if err != nil {
a.logError.Println("error stopping chatbot rule:", err)
return fmt.Errorf("Error updating chatbot rule. Try again.")
}
err = a.services.ChatbotRuleS.Update(mRule)
if err != nil {
a.logError.Println("error updating chatbot rule:", err)
return fmt.Errorf("Error updating chatbot rule. Try again.")
}
rules, err := a.chatbotRules(*rule.ChatbotID)
if err != nil {
a.logError.Println("error getting chatbot rules:", err)
return fmt.Errorf("Error updating chatbot rule. Try again.")
}
runtime.EventsEmit(a.wails, "ChatbotRules", rules)
return nil
2024-05-02 19:30:25 +00:00
}
2024-05-22 16:51:46 +00:00
func (a *App) RunChatbotRules(chatbotID *int64) error {
if chatbotID == nil {
return fmt.Errorf("Invalid chatbot. Try again.")
}
rules, err := a.chatbotRules(*chatbotID)
if err != nil {
a.logError.Println("error getting chatbot rules:", err)
return fmt.Errorf("Error running chatbot rules. Try again.")
}
var errored bool
for _, rule := range rules {
if err = a.RunChatbotRule(&rule); err != nil {
errored = true
}
}
if errored {
return fmt.Errorf("An error occurred while running rules. Check error log for details.")
}
return nil
2024-05-02 19:30:25 +00:00
}
2024-05-22 16:51:46 +00:00
func (a *App) StopChatbotRules(chatbotID *int64) error {
if chatbotID == nil {
return fmt.Errorf("Invalid chatbot. Try again.")
}
rules, err := a.chatbotRules(*chatbotID)
if err != nil {
a.logError.Println("error getting chatbot rules:", err)
return fmt.Errorf("Error stopping chatbot rules. Try again.")
}
var errored bool
for _, rule := range rules {
if err = a.StopChatbotRule(&rule); err != nil {
errored = true
}
}
if errored {
return fmt.Errorf("An error occurred while stopping rules. Check error log for details.")
}
return nil
2024-05-02 19:30:25 +00:00
}
func (a *App) OpenFileDialog() (string, error) {
home, err := os.UserHomeDir()
if err != nil {
a.logError.Println("error getting home directory:", err)
return "", fmt.Errorf("Error opening file explorer. Try again.")
}
filepath, err := runtime.OpenFileDialog(a.wails, runtime.OpenDialogOptions{DefaultDirectory: home})
if err != nil {
a.logError.Println("error opening file dialog:", err)
return "", fmt.Errorf("Error opening file explorer. Try again.")
}
return filepath, err
}