Compare commits

..

10 commits

Author SHA1 Message Date
tyler f35ec00a16 Migrated from github 2024-06-14 13:29:24 -04:00
tyler 6bcc437bc0 Updated chat info scraper to match rumble website update 2024-05-31 11:26:07 -04:00
tyler a4125e8de8 Added raid and sub fields to chat view 2024-05-27 16:07:44 -04:00
tyler 3818644bb0 LoggedIn returns response 2024-05-24 11:41:51 -04:00
tyler ea9955c164 Added fields to logged-in response 2024-05-24 11:36:04 -04:00
tyler 563d9f9413 Added badge constants 2024-05-21 15:43:04 -04:00
tyler 9c32f33d9f Added rumble chat type constants 2024-05-18 13:08:42 -04:00
tyler ba26735378 Added message time to chat view 2024-05-18 12:41:13 -04:00
tyler a779afcaba Trimmed quotes from RumbleChat args 2024-05-16 17:03:28 -04:00
tyler 5b3593ec1e Trimmed whitespace in RumbleChat arguments 2024-05-16 16:56:35 -04:00
7 changed files with 76 additions and 43 deletions

87
chat.go
View file

@ -13,9 +13,23 @@ import (
"time" "time"
"github.com/r3labs/sse/v2" "github.com/r3labs/sse/v2"
"github.com/tylertravisty/go-utils/random"
"golang.org/x/net/html" "golang.org/x/net/html"
"gopkg.in/cenkalti/backoff.v1" "gopkg.in/cenkalti/backoff.v1"
"travisty.io/tyler/go-utils/random"
)
const (
ChatBadgeRecurringSubscription = "recurring_subscription"
ChatBadgeLocalsSupporter = "locals_supporter"
ChatTypeInit = "init"
ChatTypeMessages = "messages"
ChatTypeMuteUsers = "mute_users"
ChatTypeDeleteMessages = "delete_messages"
ChatTypeSubscriber = "locals_supporter"
ChatTypeRaiding = "raid_confirmed"
ChatTypePinMessage = "pin_message"
ChatTypeUnpinMessage = "unpin_message"
) )
type ChatInfo struct { type ChatInfo struct {
@ -65,24 +79,6 @@ func (c *Client) getChatInfo() (*ChatInfo, error) {
lineS, err := r.ReadString('\n') lineS, err := r.ReadString('\n')
for err == nil { for err == nil {
if strings.Contains(lineS, "RumbleChat(") { if strings.Contains(lineS, "RumbleChat(") {
//start := strings.Index(lineS, "RumbleChat(") + len("RumbleChat(")
//if start == -1 {
// return nil, fmt.Errorf("error finding chat function in webpage")
//}
//end := strings.Index(lineS[start:], ");")
//if end == -1 {
// return nil, fmt.Errorf("error finding end of chat function in webpage")
//}
//argsS := strings.ReplaceAll(lineS[start:start+end], ", ", ",")
//argsS = strings.Replace(argsS, "[", "\"[", 1)
//n := strings.LastIndex(argsS, "]")
//argsS = argsS[:n] + "]\"" + argsS[n+1:]
//c := csv.NewReader(strings.NewReader(argsS))
//args, err := c.ReadAll()
//if err != nil {
// return nil, fmt.Errorf("error parsing csv: %v", err)
//}
//info := args[0]
start := strings.Index(lineS, "RumbleChat(") + len("RumbleChat(") start := strings.Index(lineS, "RumbleChat(") + len("RumbleChat(")
if start == -1 { if start == -1 {
return nil, fmt.Errorf("error finding chat function in webpage") return nil, fmt.Errorf("error finding chat function in webpage")
@ -92,11 +88,11 @@ func (c *Client) getChatInfo() (*ChatInfo, error) {
return nil, fmt.Errorf("error finding end of chat function in webpage") return nil, fmt.Errorf("error finding end of chat function in webpage")
} }
args := parseRumbleChatArgs(lineS[start : start+end]) args := parseRumbleChatArgs(lineS[start : start+end])
channelID, err := strconv.Atoi(args[5]) channelID, err := strconv.Atoi(args[6])
if err != nil { if err != nil {
return nil, fmt.Errorf("error converting channel ID argument string to int: %v", err) 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") { } else if strings.Contains(lineS, "media-by--a") && strings.Contains(lineS, "author") {
r := strings.NewReader(lineS) r := strings.NewReader(lineS)
node, err := html.Parse(r) node, err := html.Parse(r)
@ -132,7 +128,7 @@ func parseRumbleChatArgs(argsS string) []string {
arg := []rune{} arg := []rune{}
for _, c := range argsS { for _, c := range argsS {
if c == ',' && open == 0 { if c == ',' && open == 0 {
args = append(args, string(arg)) args = append(args, trimRumbleChatArg(string(arg)))
arg = []rune{} arg = []rune{}
} else { } else {
if c == '[' { if c == '[' {
@ -146,12 +142,16 @@ func parseRumbleChatArgs(argsS string) []string {
} }
} }
if len(arg) > 0 { if len(arg) > 0 {
args = append(args, string(arg)) args = append(args, trimRumbleChatArg(string(arg)))
} }
return args return args
} }
func trimRumbleChatArg(arg string) string {
return strings.Trim(strings.TrimSpace(arg), "\"")
}
type ChatMessage struct { type ChatMessage struct {
Text string `json:"text"` Text string `json:"text"`
} }
@ -258,6 +258,15 @@ type ChatEventBlock struct {
Type string `json:"type"` Type string `json:"type"`
} }
type ChatEventNotification struct {
Badge string `json:"badge"`
Text string `json:"text"`
}
type ChatEventRaidNotification struct {
StartTs int64 `json:"start_ts"`
}
type ChatEventRant struct { type ChatEventRant struct {
Duration int `json:"duration"` Duration int `json:"duration"`
ExpiresOn string `json:"expires_on"` ExpiresOn string `json:"expires_on"`
@ -265,13 +274,15 @@ type ChatEventRant struct {
} }
type ChatEventMessage struct { type ChatEventMessage struct {
Blocks []ChatEventBlock `json:"blocks"` Blocks []ChatEventBlock `json:"blocks"`
ChannelID *int64 `json:"channel_id"` ChannelID *int64 `json:"channel_id"`
ID string `json:"id"` ID string `json:"id"`
Rant *ChatEventRant `json:"rant"` Notification *ChatEventNotification `json:"notification"`
Text string `json:"text"` RaidNotification *ChatEventRaidNotification `json:"raid_notification"`
Time string `json:"time"` Rant *ChatEventRant `json:"rant"`
UserID string `json:"user_id"` Text string `json:"text"`
Time string `json:"time"`
UserID string `json:"user_id"`
} }
type ChatEventUser struct { type ChatEventUser struct {
@ -392,8 +403,11 @@ type ChatView struct {
ImageUrl string ImageUrl string
Init bool Init bool
IsFollower bool IsFollower bool
Raid bool
Rant int Rant int
Sub bool
Text string Text string
Time time.Time
Type string Type string
Username string Username string
} }
@ -455,10 +469,23 @@ func parseMessages(eventType string, messages []ChatEventMessage, users map[stri
view.Color = user.Color view.Color = user.Color
view.ImageUrl = user.Image1 view.ImageUrl = user.Image1
view.IsFollower = user.IsFollower view.IsFollower = user.IsFollower
if message.RaidNotification != nil {
view.Raid = true
}
if message.Rant != nil { if message.Rant != nil {
view.Rant = message.Rant.PriceCents view.Rant = message.Rant.PriceCents
} }
if message.Notification != nil {
if message.Notification.Badge == ChatBadgeRecurringSubscription {
view.Sub = true
}
}
view.Text = message.Text view.Text = message.Text
t, err := time.Parse(time.RFC3339, message.Time)
if err != nil {
return nil, fmt.Errorf("error parsing message time: %v", err)
}
view.Time = t
view.Type = eventType view.Type = eventType
view.Username = user.Username view.Username = user.Username

View file

@ -273,31 +273,37 @@ func (c *Client) userLogout() error {
return nil return nil
} }
type LoggedInResponseData struct {
Username string `json:"username"`
}
type LoggedInResponseUser struct { type LoggedInResponseUser struct {
LoggedIn bool `json:"logged_in"` ID string `json:"id"`
LoggedIn bool `json:"logged_in"`
} }
type LoggedInResponse struct { type LoggedInResponse struct {
Data LoggedInResponseData `json:"data"`
User LoggedInResponseUser `json:"user"` User LoggedInResponseUser `json:"user"`
} }
func (c *Client) LoggedIn() (bool, error) { func (c *Client) LoggedIn() (*LoggedInResponse, error) {
resp, err := c.httpClient.Get(urlUserLogin) resp, err := c.httpClient.Get(urlUserLogin)
if err != nil { if err != nil {
return false, pkgErr("error getting login service", err) return nil, pkgErr("error getting login service", err)
} }
defer resp.Body.Close() defer resp.Body.Close()
bodyB, err := io.ReadAll(resp.Body) bodyB, err := io.ReadAll(resp.Body)
if err != nil { if err != nil {
return false, pkgErr("error reading body bytes", err) return nil, pkgErr("error reading body bytes", err)
} }
var lir LoggedInResponse var lir LoggedInResponse
err = json.NewDecoder(strings.NewReader(string(bodyB))).Decode(&lir) err = json.NewDecoder(strings.NewReader(string(bodyB))).Decode(&lir)
if err != nil { if err != nil {
return false, pkgErr("error un-marshaling response body", err) return nil, pkgErr("error un-marshaling response body", err)
} }
return lir.User.LoggedIn, nil return &lir, nil
} }

4
go.mod
View file

@ -1,13 +1,13 @@
module github.com/tylertravisty/rumble-livestream-lib-go module travisty.io/tyler/rumble-livestream-lib-go
go 1.19 go 1.19
require ( require (
github.com/r3labs/sse/v2 v2.10.0 github.com/r3labs/sse/v2 v2.10.0
github.com/robertkrimen/otto v0.2.1 github.com/robertkrimen/otto v0.2.1
github.com/tylertravisty/go-utils v0.0.0-20230524204414-6893ae548909
golang.org/x/net v0.24.0 golang.org/x/net v0.24.0
gopkg.in/cenkalti/backoff.v1 v1.1.0 gopkg.in/cenkalti/backoff.v1 v1.1.0
travisty.io/tyler/go-utils v0.0.0-20240614172648-00b0b1a557b4
) )
require ( require (

4
go.sum
View file

@ -9,8 +9,6 @@ github.com/robertkrimen/otto v0.2.1/go.mod h1:UPwtJ1Xu7JrLcZjNWN8orJaM5n5YEtqL//
github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
github.com/stretchr/testify v1.8.1 h1:w7B6lhMri9wdJUVmEZPGGhZzrYTPvgJArz7wNPgYKsk= github.com/stretchr/testify v1.8.1 h1:w7B6lhMri9wdJUVmEZPGGhZzrYTPvgJArz7wNPgYKsk=
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=
golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
golang.org/x/net v0.0.0-20191116160921-f9c825593386/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= golang.org/x/net v0.0.0-20191116160921-f9c825593386/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
golang.org/x/net v0.24.0 h1:1PcaxkF854Fu3+lvBIx5SYn9wRlBzzcnHZSiaFFAb0w= golang.org/x/net v0.24.0 h1:1PcaxkF854Fu3+lvBIx5SYn9wRlBzzcnHZSiaFFAb0w=
@ -26,3 +24,5 @@ gopkg.in/sourcemap.v1 v1.0.5 h1:inv58fC9f9J3TK2Y2R1NPntXEn3/wjWHkonhIUODNTI=
gopkg.in/sourcemap.v1 v1.0.5/go.mod h1:2RlvNNSMglmRrcvhfuzp4hQHwOtjxlbjX7UPY/GXb78= gopkg.in/sourcemap.v1 v1.0.5/go.mod h1:2RlvNNSMglmRrcvhfuzp4hQHwOtjxlbjX7UPY/GXb78=
gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA= gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA=
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=

6
vendor/modules.txt vendored
View file

@ -10,9 +10,6 @@ github.com/robertkrimen/otto/file
github.com/robertkrimen/otto/parser github.com/robertkrimen/otto/parser
github.com/robertkrimen/otto/registry github.com/robertkrimen/otto/registry
github.com/robertkrimen/otto/token github.com/robertkrimen/otto/token
# github.com/tylertravisty/go-utils v0.0.0-20230524204414-6893ae548909
## explicit; go 1.16
github.com/tylertravisty/go-utils/random
# golang.org/x/net v0.24.0 # golang.org/x/net v0.24.0
## explicit; go 1.18 ## explicit; go 1.18
golang.org/x/net/context golang.org/x/net/context
@ -40,3 +37,6 @@ gopkg.in/cenkalti/backoff.v1
## explicit ## explicit
gopkg.in/sourcemap.v1 gopkg.in/sourcemap.v1
gopkg.in/sourcemap.v1/base64vlq 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