Added chat functionality
This commit is contained in:
parent
cbd3c200e9
commit
aa3a0594bd
125
chat.go
Normal file
125
chat.go
Normal file
|
@ -0,0 +1,125 @@
|
|||
package rumblelivestreamlib
|
||||
|
||||
import (
|
||||
"bufio"
|
||||
"bytes"
|
||||
"encoding/csv"
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"net/http"
|
||||
"strconv"
|
||||
"strings"
|
||||
|
||||
"github.com/tylertravisty/random"
|
||||
)
|
||||
|
||||
type ChatInfo struct {
|
||||
UrlPrefix string
|
||||
ChatID string
|
||||
ChannelID int
|
||||
}
|
||||
|
||||
func (ci *ChatInfo) Url() string {
|
||||
return fmt.Sprintf("%s/chat/%s/message", ci.UrlPrefix, ci.ChatID)
|
||||
}
|
||||
|
||||
func (c *Client) streamChatInfo() (*ChatInfo, error) {
|
||||
if c.StreamUrl == "" {
|
||||
return nil, fmt.Errorf("stream url is empty")
|
||||
}
|
||||
|
||||
resp, err := c.getWebpage(c.StreamUrl)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("error getting stream webpage: %v", err)
|
||||
}
|
||||
defer resp.Body.Close()
|
||||
|
||||
r := bufio.NewReader(resp.Body)
|
||||
line, _, err := r.ReadLine()
|
||||
var lineS string
|
||||
for err == nil {
|
||||
lineS = string(line)
|
||||
if strings.Contains(lineS, "RumbleChat(") {
|
||||
start := strings.Index(lineS, "RumbleChat(") + len("RumbleChat(")
|
||||
end := strings.Index(lineS[start:], ");")
|
||||
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]
|
||||
channelID, err := strconv.Atoi(info[5])
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("error converting channel ID argument string to int: %v", err)
|
||||
}
|
||||
return &ChatInfo{info[0], info[1], channelID}, nil
|
||||
}
|
||||
line, _, err = r.ReadLine()
|
||||
}
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("error reading line from stream webpage: %v", err)
|
||||
}
|
||||
|
||||
return nil, fmt.Errorf("did not find RumbleChat function call")
|
||||
}
|
||||
|
||||
type ChatMessage struct {
|
||||
Text string `json:"text"`
|
||||
}
|
||||
|
||||
type ChatData struct {
|
||||
RequestID string `json:"request_id"`
|
||||
Message ChatMessage `json:"message"`
|
||||
Rant *string `json:"rant"`
|
||||
ChannelID *int `json:"channel_id"`
|
||||
}
|
||||
|
||||
type ChatRequest struct {
|
||||
Data ChatData `json:"data"`
|
||||
}
|
||||
|
||||
func (c *Client) Chat(message string) error {
|
||||
if c.httpClient == nil {
|
||||
return pkgErr("", fmt.Errorf("http client is nil"))
|
||||
}
|
||||
|
||||
chatInfo, err := c.streamChatInfo()
|
||||
if err != nil {
|
||||
return pkgErr("error getting stream chat info", err)
|
||||
}
|
||||
|
||||
requestID, err := random.String(32)
|
||||
if err != nil {
|
||||
return pkgErr("error generating request ID", err)
|
||||
}
|
||||
body := ChatRequest{
|
||||
Data: ChatData{
|
||||
RequestID: requestID,
|
||||
Message: ChatMessage{
|
||||
Text: message,
|
||||
},
|
||||
Rant: nil,
|
||||
ChannelID: &chatInfo.ChannelID,
|
||||
},
|
||||
}
|
||||
bodyB, err := json.Marshal(body)
|
||||
if err != nil {
|
||||
return pkgErr("error marshaling request body into json", err)
|
||||
}
|
||||
|
||||
resp, err := c.httpClient.Post(chatInfo.Url(), "application/json", bytes.NewReader(bodyB))
|
||||
if err != nil {
|
||||
return pkgErr("http Post request returned error", err)
|
||||
}
|
||||
defer resp.Body.Close()
|
||||
|
||||
if resp.StatusCode != http.StatusOK {
|
||||
return fmt.Errorf("http Post response status not %s: %s", http.StatusText(http.StatusOK), resp.Status)
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
39
client.go
39
client.go
|
@ -4,7 +4,6 @@ import (
|
|||
"encoding/json"
|
||||
"fmt"
|
||||
"io"
|
||||
"log"
|
||||
"net/http"
|
||||
"net/http/cookiejar"
|
||||
"net/url"
|
||||
|
@ -14,7 +13,8 @@ import (
|
|||
)
|
||||
|
||||
const (
|
||||
urlWeb = "https://rumble.com"
|
||||
domain = "rumble.com"
|
||||
urlWeb = "https://" + domain
|
||||
urlGetSalts = urlWeb + "/service.php?name=user.get_salts"
|
||||
urlUserLogin = urlWeb + "/service.php?name=user.login"
|
||||
urlUserLogout = urlWeb + "/service.php?name=user.logout"
|
||||
|
@ -26,15 +26,25 @@ type Client struct {
|
|||
StreamUrl string
|
||||
}
|
||||
|
||||
func (c *Client) printCookies() {
|
||||
func (c *Client) cookies() ([]*http.Cookie, error) {
|
||||
u, err := url.Parse(urlWeb)
|
||||
if err != nil {
|
||||
log.Fatal("url.Parse err=", err)
|
||||
return nil, fmt.Errorf("error parsing domain: %v", err)
|
||||
}
|
||||
fmt.Println("Cookies:")
|
||||
for _, cookie := range c.httpClient.Jar.Cookies(u) {
|
||||
fmt.Println(cookie)
|
||||
return c.httpClient.Jar.Cookies(u), nil
|
||||
}
|
||||
|
||||
func (c *Client) PrintCookies() error {
|
||||
cookies, err := c.cookies()
|
||||
if err != nil {
|
||||
return pkgErr("error getting cookies", err)
|
||||
}
|
||||
fmt.Println("Cookies:", len(cookies))
|
||||
for _, cookie := range cookies {
|
||||
fmt.Println(cookie.String())
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func NewClient(streamKey string, streamUrl string) (*Client, error) {
|
||||
|
@ -81,17 +91,17 @@ func (c *Client) Login(username string, password string) error {
|
|||
return nil
|
||||
}
|
||||
|
||||
func (c *Client) getWeb() error {
|
||||
resp, err := c.httpClient.Get(urlWeb)
|
||||
func (c *Client) getWebpage(url string) (*http.Response, error) {
|
||||
resp, err := c.httpClient.Get(url)
|
||||
if err != nil {
|
||||
return fmt.Errorf("http Get request returned error: %v", err)
|
||||
return nil, fmt.Errorf("http Get request returned error: %v", err)
|
||||
}
|
||||
defer resp.Body.Close()
|
||||
if resp.StatusCode != http.StatusOK {
|
||||
return fmt.Errorf("http Get response status not %s: %s", http.StatusText(http.StatusOK), resp.Status)
|
||||
resp.Body.Close()
|
||||
return nil, fmt.Errorf("http Get response status not %s: %s", http.StatusText(http.StatusOK), resp.Status)
|
||||
}
|
||||
|
||||
return nil
|
||||
return resp, nil
|
||||
}
|
||||
|
||||
func (c *Client) getSalts(username string) ([]string, error) {
|
||||
|
@ -112,7 +122,6 @@ func (c *Client) getSalts(username string) ([]string, error) {
|
|||
if err != nil {
|
||||
return nil, fmt.Errorf("error reading body bytes: %v", err)
|
||||
}
|
||||
fmt.Println("BodyB:", string(bodyB))
|
||||
|
||||
var gsr GetSaltsResponse
|
||||
err = json.NewDecoder(strings.NewReader(string(bodyB))).Decode(&gsr)
|
||||
|
@ -142,8 +151,6 @@ func (c *Client) userLogin(username string, password string, salts []string) err
|
|||
if resp.StatusCode != http.StatusOK {
|
||||
return fmt.Errorf("http Post response status not %s: %s", http.StatusText(http.StatusOK), resp.Status)
|
||||
}
|
||||
bodyB, _ := io.ReadAll(resp.Body)
|
||||
fmt.Println(string(bodyB))
|
||||
|
||||
return nil
|
||||
}
|
||||
|
|
1
go.mod
1
go.mod
|
@ -4,6 +4,7 @@ go 1.19
|
|||
|
||||
require (
|
||||
github.com/robertkrimen/otto v0.2.1 // indirect
|
||||
github.com/tylertravisty/random v0.0.0-20210907021019-42b7944f3ad5 // indirect
|
||||
golang.org/x/text v0.4.0 // indirect
|
||||
gopkg.in/sourcemap.v1 v1.0.5 // indirect
|
||||
)
|
||||
|
|
2
go.sum
2
go.sum
|
@ -1,5 +1,7 @@
|
|||
github.com/robertkrimen/otto v0.2.1 h1:FVP0PJ0AHIjC+N4pKCG9yCDz6LHNPCwi/GKID5pGGF0=
|
||||
github.com/robertkrimen/otto v0.2.1/go.mod h1:UPwtJ1Xu7JrLcZjNWN8orJaM5n5YEtqL//farB5FlRY=
|
||||
github.com/tylertravisty/random v0.0.0-20210907021019-42b7944f3ad5 h1:13ClHNZRQ+3Ktg6Q6MVjcGZS9xBiFpmgvUg0jkQ/TEY=
|
||||
github.com/tylertravisty/random v0.0.0-20210907021019-42b7944f3ad5/go.mod h1:4mNgC4SPDnTcDJ5U2JZW1hCrVZdUQNWoLlp+t57gOLU=
|
||||
golang.org/x/text v0.4.0 h1:BrVqGRd7+k1DiOgtnFvAkoQEWQvBc25ouMJM6429SFg=
|
||||
golang.org/x/text v0.4.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8=
|
||||
gopkg.in/sourcemap.v1 v1.0.5 h1:inv58fC9f9J3TK2Y2R1NPntXEn3/wjWHkonhIUODNTI=
|
||||
|
|
Loading…
Reference in a new issue