Compare commits

...

10 commits

24 changed files with 158 additions and 60 deletions

View file

@ -1,21 +1,60 @@
# Roadmap
Rum Goggles App:
- Chat bot rule triggers on stream events
- On: follow, subscribe, rant, raid
v1-beta:
- Chat polls
- Stream statistics
v1.N:
- Download subscriber data, track re-subs, trigger rules on re-subs
- Stream moderator bot
- OBS integration
Rum Goggles Service:
- Chat polls
- Monitor all live stream chats
- Channel points
- Spam/Troll tracking
# Bugs
Chat bot rule menu back button does not work in macOS
If connection to chat stream breaks, gracefully handle error
- try to reconnect
- let the chat rules know, etc.
- test with VPN
# Doing
Before v1-alpha release:
- stop all running rules when chat bot is deleted
- indicator in chatbot that producer is running
Add bypass to commands for:
- Host, admin, mod, etc.
Monitor how many handlers are listening to a producer.
- If producer.Stop is called, subtract from count.
- If count == 0, stop producer
Change API producer to monitor changes and only send new events, one at a time, to app, instead of the entire response; create datatype for single API event
- update chatbot and page handlers to use single API events
- page details should add to activity list one at a time
- store page list in Go, send entire list to frontend on updates
- list can be updated by any producer
Add timeouts to event triggers to prevent rate limit?
Don't stop rule if chat error is 429 Too Many Requests
Check if sender is logged in before running rule. If not, return rule error.
Add max rant amount for commands
Button to export log file -> user selects folder
Style scroll bars on Windows
- WebView2 issue
Indicator in chatbot that producer is running
- this can be in many different places as needed
Custom stream moderator rules
@ -82,3 +121,6 @@ User settings:
- API query timer (default: 2s)
# To Do
Currently relies on Rumble to manage account username case-sensitivity.
- Change database table to use UNIQUE COLLATE NOCASE on account username

View file

@ -12,12 +12,12 @@ import (
"sync"
"time"
"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"
"github.com/wailsapp/wails/v2/pkg/runtime"
"travisty.io/tyler/rum-goggles/v1/internal/chatbot"
"travisty.io/tyler/rum-goggles/v1/internal/config"
"travisty.io/tyler/rum-goggles/v1/internal/events"
"travisty.io/tyler/rum-goggles/v1/internal/models"
rumblelivestreamlib "travisty.io/tyler/rumble-livestream-lib-go"
_ "github.com/mattn/go-sqlite3"
)
@ -835,10 +835,6 @@ func (a *App) activatePage(pi PageInfo) error {
if name == nil {
return fmt.Errorf("page name is nil")
}
url := pi.KeyUrl()
if url == nil {
return fmt.Errorf("page key url is nil")
}
a.pagesMu.Lock()
page, exists := a.pages[*name]
@ -873,6 +869,11 @@ func (a *App) activatePage(pi PageInfo) error {
}
page.active = true
url := pi.KeyUrl()
if url == nil {
return fmt.Errorf("page key url is nil")
}
err := a.producers.ApiP.Start(*name, *url, 10*time.Second)
if err != nil {
return fmt.Errorf("error starting api: %v", err)
@ -888,6 +889,43 @@ func (a *App) activatePage(pi PageInfo) error {
return nil
}
func (a *App) deletePage(pi PageInfo) error {
name := pi.String()
if name == nil {
return fmt.Errorf("page name is nil")
}
a.pagesMu.Lock()
defer a.pagesMu.Unlock()
page, exists := a.pages[*name]
if !exists {
return nil
}
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)
}
page.displayingMu.Unlock()
page.active = false
}
delete(a.pages, *name)
return nil
}
func (a *App) DeleteAccount(id int64) error {
acct, err := a.services.AccountS.ByID(id)
if err != nil {
@ -913,13 +951,11 @@ func (a *App) DeleteAccount(id int64) error {
return fmt.Errorf("Error deleting account. Try again.")
}
if a.producers.ApiP.Active(*name) {
err := a.producers.ApiP.Stop(*name)
err = a.deletePage(acct)
if err != nil {
a.logError.Println("error stopping api:", err)
a.logError.Println("error deleting page:", err)
return fmt.Errorf("Error deleting account. Try again.")
}
}
err = a.services.AccountS.Delete(acct)
if err != nil {
@ -955,13 +991,11 @@ func (a *App) DeleteChannel(id int64) error {
return fmt.Errorf("Error deleting channel. Try again.")
}
if a.producers.ApiP.Active(*name) {
err := a.producers.ApiP.Stop(*name)
err = a.deletePage(channel)
if err != nil {
a.logError.Println("error stopping api:", err)
a.logError.Println("error deleting page:", err)
return fmt.Errorf("Error deleting channel. Try again.")
}
}
err = a.services.ChannelS.Delete(channel)
if err != nil {
@ -1275,12 +1309,13 @@ func (a *App) UpdateChatbot(chatbot *models.Chatbot) error {
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)
// 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, "ChatbotInfo", chatbot)
// runtime.EventsEmit(a.wails, "ChatbotList", list)
return nil
}

View file

@ -42,8 +42,19 @@ function ChatBot(props) {
const [openNewRule, setOpenNewRule] = useState(false);
const [chatbotRules, setChatbotRules] = useState([]);
const [chatbotSettings, setChatbotSettings] = useState(true);
const [refresh, setRefresh] = useState(false);
useEffect(() => {
EventsOff('ChatbotInfo');
EventsOn('ChatbotInfo', (event) => {
if (openChatbot !== null && openChatbot.id === event.id) {
openChatbot.name = event.name;
openChatbot.url = event.url;
setRefresh(!refresh);
}
});
EventsOff('ChatbotList');
EventsOn('ChatbotList', (event) => {
setChatbots(event);
if (openChatbot !== null) {
@ -54,7 +65,9 @@ function ChatBot(props) {
}
}
});
}, [openChatbot]);
useEffect(() => {
EventsOn('ChatbotRules', (event) => {
setChatbotRules(event);
});
@ -1133,6 +1146,10 @@ function ModalRuleTriggerEvent(props) {
let from_account = {};
switch (event) {
case 'Follow':
if (options.page === undefined || options.page === '') {
setValidOptions(false);
return;
}
from_account.name = options.page;
from_account.on_follow = {};
break;
@ -1158,6 +1175,10 @@ function ModalRuleTriggerEvent(props) {
let from_channel = {};
switch (event) {
case 'Follow':
if (options.page === undefined || options.page === '') {
setValidOptions(false);
return;
}
from_channel.name = options.page;
from_channel.on_follow = {};
break;
@ -1414,7 +1435,7 @@ function EventOptionsRant(props) {
setValidMaxAmount(true);
}
setMinAmount(event.target.value);
setMinAmount(amount);
props.setOptions({ min_amount: amount, max_amount: maxAmount });
};
const [maxAmount, setMaxAmount] = useState(

View file

@ -344,7 +344,7 @@ function PageDetails(props) {
onSubmit={() => setError('')}
/>
)}
{openDelete && (
{openDelete && details !== null && (
<Modal
backgroundClose={true}
cancelButton={'Cancel'}

View file

@ -1,4 +1,4 @@
module github.com/tylertravisty/rum-goggles/v1
module travisty.io/tyler/rum-goggles/v1
go 1.21
@ -6,8 +6,8 @@ toolchain go1.22.0
require (
github.com/mattn/go-sqlite3 v1.14.22
github.com/tylertravisty/rumble-livestream-lib-go v0.9.0
github.com/wailsapp/wails/v2 v2.8.1
travisty.io/tyler/rumble-livestream-lib-go v0.0.0-20240614172924-f35ec00a1690
)
require (
@ -31,7 +31,6 @@ require (
github.com/robertkrimen/otto v0.3.0 // indirect
github.com/samber/lo v1.38.1 // indirect
github.com/tkrajina/go-reflector v0.5.6 // indirect
github.com/tylertravisty/go-utils v0.0.0-20230524204414-6893ae548909 // indirect
github.com/valyala/bytebufferpool v1.0.0 // indirect
github.com/valyala/fasttemplate v1.2.2 // indirect
github.com/wailsapp/go-webview2 v1.0.10 // indirect
@ -43,6 +42,7 @@ require (
golang.org/x/text v0.14.0 // indirect
gopkg.in/cenkalti/backoff.v1 v1.1.0 // indirect
gopkg.in/sourcemap.v1 v1.0.5 // indirect
travisty.io/tyler/go-utils v0.0.0-20240614172648-00b0b1a557b4 // indirect
)
// replace github.com/wailsapp/wails/v2 v2.8.0 => /home/tyler/dev/go/pkg/mod

View file

@ -58,10 +58,6 @@ github.com/stretchr/testify v1.8.4 h1:CcVxjf3Q8PM0mHUKJCdn+eZZtm5yQwehR5yeSVQQcU
github.com/stretchr/testify v1.8.4/go.mod h1:sz/lmYIOXD/1dqDmKjjqLyZ2RngseejIcXlSw2iwfAo=
github.com/tkrajina/go-reflector v0.5.6 h1:hKQ0gyocG7vgMD2M3dRlYN6WBBOmdoOzJ6njQSepKdE=
github.com/tkrajina/go-reflector v0.5.6/go.mod h1:ECbqLgccecY5kPmPmXg1MrHW585yMcDkVl6IvJe64T4=
github.com/tylertravisty/go-utils v0.0.0-20230524204414-6893ae548909 h1:xrjIFqzGQXlCrCdMPpW6+SodGFSlrQ3ZNUCr3f5tF1g=
github.com/tylertravisty/go-utils v0.0.0-20230524204414-6893ae548909/go.mod h1:2W31Jhs9YSy7y500wsCOW0bcamGi9foQV1CKrfvfTxk=
github.com/tylertravisty/rumble-livestream-lib-go v0.9.0 h1:G1b/uac43dq7BG7NzcLeRLPOfOu8GyjViE9s48qhwhw=
github.com/tylertravisty/rumble-livestream-lib-go v0.9.0/go.mod h1:Odkqvsn+2eoWV3ePcj257Ga0bdOqV4JBTfOJcQ+Sqf8=
github.com/valyala/bytebufferpool v1.0.0 h1:GqA5TC/0021Y/b9FG4Oi9Mr3q7XYx6KllzawFIhcdPw=
github.com/valyala/bytebufferpool v1.0.0/go.mod h1:6bBcMArwyJ5K/AmCkWv1jt77kVWyCJ6HpOuEn7z0Csc=
github.com/valyala/fasttemplate v1.2.1/go.mod h1:KHLXt3tVN2HBp8eijSv/kGJopbvo7S+qRAEEKiv+SiQ=
@ -110,3 +106,7 @@ gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C
gopkg.in/yaml.v3 v3.0.0-20210107192922-496545a6307b/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA=
gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
travisty.io/tyler/go-utils v0.0.0-20240614172648-00b0b1a557b4 h1:/InkkmR3O6lh63ckLJe14k1DRB6RuOZJv5TpfxGVRKQ=
travisty.io/tyler/go-utils v0.0.0-20240614172648-00b0b1a557b4/go.mod h1:2inG89XtlVnppctG2WnKir4mWWN0zbF/PB9m1HXdTYI=
travisty.io/tyler/rumble-livestream-lib-go v0.0.0-20240614172924-f35ec00a1690 h1:j6qZCouGr8+eDKnK4T5qGfwqbwfq8G06gTv25zbKfgY=
travisty.io/tyler/rumble-livestream-lib-go v0.0.0-20240614172924-f35ec00a1690/go.mod h1:kawNZvHMCKVQ18Qt3jayiUGsHfH2tlovr65GXzVXVA0=

View file

@ -10,10 +10,10 @@ import (
"sync"
"time"
"github.com/tylertravisty/rum-goggles/v1/internal/events"
"github.com/tylertravisty/rum-goggles/v1/internal/models"
rumblelivestreamlib "github.com/tylertravisty/rumble-livestream-lib-go"
"github.com/wailsapp/wails/v2/pkg/runtime"
"travisty.io/tyler/rum-goggles/v1/internal/events"
"travisty.io/tyler/rum-goggles/v1/internal/models"
rumblelivestreamlib "travisty.io/tyler/rumble-livestream-lib-go"
)
type user struct {

View file

@ -13,7 +13,7 @@ import (
"strings"
"time"
"github.com/tylertravisty/rum-goggles/v1/internal/models"
"travisty.io/tyler/rum-goggles/v1/internal/models"
)
const (

View file

@ -8,9 +8,9 @@ import (
"sync"
"time"
"github.com/tylertravisty/rum-goggles/v1/internal/events"
rumblelivestreamlib "github.com/tylertravisty/rumble-livestream-lib-go"
"github.com/wailsapp/wails/v2/pkg/runtime"
"travisty.io/tyler/rum-goggles/v1/internal/events"
rumblelivestreamlib "travisty.io/tyler/rumble-livestream-lib-go"
)
type Runner struct {

View file

@ -7,7 +7,7 @@ import (
"sync"
"time"
rumblelivestreamlib "github.com/tylertravisty/rumble-livestream-lib-go"
rumblelivestreamlib "travisty.io/tyler/rumble-livestream-lib-go"
)
type Api struct {

View file

@ -7,7 +7,7 @@ import (
"sync"
"time"
rumblelivestreamlib "github.com/tylertravisty/rumble-livestream-lib-go"
rumblelivestreamlib "travisty.io/tyler/rumble-livestream-lib-go"
)
type Chat struct {

View file

@ -5,10 +5,10 @@ import (
"net/http"
"strings"
"github.com/tylertravisty/rum-goggles/v1/internal/config"
"github.com/wailsapp/wails/v2"
"github.com/wailsapp/wails/v2/pkg/options"
"github.com/wailsapp/wails/v2/pkg/options/assetserver"
"travisty.io/tyler/rum-goggles/v1/internal/config"
)
//go:embed all:frontend/dist

12
v1/vendor/modules.txt vendored
View file

@ -74,12 +74,6 @@ github.com/samber/lo
# github.com/tkrajina/go-reflector v0.5.6
## explicit; go 1.17
github.com/tkrajina/go-reflector/reflector
# github.com/tylertravisty/go-utils v0.0.0-20230524204414-6893ae548909
## explicit; go 1.16
github.com/tylertravisty/go-utils/random
# github.com/tylertravisty/rumble-livestream-lib-go v0.9.0
## explicit; go 1.19
github.com/tylertravisty/rumble-livestream-lib-go
# github.com/valyala/bytebufferpool v1.0.0
## explicit
github.com/valyala/bytebufferpool
@ -189,3 +183,9 @@ gopkg.in/cenkalti/backoff.v1
## explicit
gopkg.in/sourcemap.v1
gopkg.in/sourcemap.v1/base64vlq
# travisty.io/tyler/go-utils v0.0.0-20240614172648-00b0b1a557b4
## explicit; go 1.16
travisty.io/tyler/go-utils/random
# travisty.io/tyler/rumble-livestream-lib-go v0.0.0-20240614172924-f35ec00a1690
## explicit; go 1.19
travisty.io/tyler/rumble-livestream-lib-go

View file

@ -13,9 +13,9 @@ import (
"time"
"github.com/r3labs/sse/v2"
"github.com/tylertravisty/go-utils/random"
"golang.org/x/net/html"
"gopkg.in/cenkalti/backoff.v1"
"travisty.io/tyler/go-utils/random"
)
const (
@ -88,11 +88,11 @@ func (c *Client) getChatInfo() (*ChatInfo, error) {
return nil, fmt.Errorf("error finding end of chat function in webpage")
}
args := parseRumbleChatArgs(lineS[start : start+end])
channelID, err := strconv.Atoi(args[5])
channelID, err := strconv.Atoi(args[6])
if err != nil {
return nil, fmt.Errorf("error converting channel ID argument string to int: %v", err)
}
chatInfo = &ChatInfo{ChannelID: channelID, ChatID: args[1], UrlPrefix: args[0]}
chatInfo = &ChatInfo{ChannelID: channelID, ChatID: args[2], UrlPrefix: args[0]}
} else if strings.Contains(lineS, "media-by--a") && strings.Contains(lineS, "author") {
r := strings.NewReader(lineS)
node, err := html.Parse(r)

View file

@ -8,6 +8,6 @@
"frontend:dev:serverUrl": "auto",
"author": {
"name": "tyler",
"email": "tylertravisty@users.noreply.github.com"
"email": "tyler@noreply.travisty.io"
}
}