commit 365fb8a8022852461647990e950b86347f2dcfdc
parent db176a4acd5fde3bf46a9ed934b08d07d554c57f
Author: bsandro <brian.drosan@gmail.com>
Date: Thu, 12 Aug 2021 02:15:53 +0300
Reworked application structure, did some basic decoupling, split everything into separate modules.
Diffstat:
23 files changed, 541 insertions(+), 488 deletions(-)
diff --git a/.gitignore b/.gitignore
@@ -1,4 +1,3 @@
.DS_Store
*.swp
config.json
-twitchapon*
diff --git a/app.go b/app.go
@@ -1,45 +0,0 @@
-package main
-
-import (
- "golang.org/x/net/websocket"
- "sync"
-)
-
-type TApp struct {
- Connection *websocket.Conn
- Obs *websocket.Conn
- WaitGroup sync.WaitGroup
- Config TConfig
- User *TUser
- Auth *TAuth
-}
-
-func (app *TApp) InitConfig() error {
- if app.Config.IsInited() {
- return nil
- } else {
- return app.Config.Init()
- }
-}
-
-func (app *TApp) InitAuth(auth_code string) error {
- if app.Auth == nil {
- app.Auth = new(TAuth)
- }
- if app.Auth.IsInited() {
- return nil
- } else {
- return app.Auth.Init(auth_code)
- }
-}
-
-func (app *TApp) InitUser() error {
- if app.User == nil {
- app.User = new(TUser)
- }
- if app.User.IsInited() {
- return nil
- } else {
- return app.User.Init()
- }
-}
diff --git a/app/app.go b/app/app.go
@@ -0,0 +1,110 @@
+package app
+
+import (
+ "bsandro.tech/git/twitchapon/config"
+ "bsandro.tech/git/twitchapon/localserver"
+ "bsandro.tech/git/twitchapon/twitch"
+ "golang.org/x/net/websocket"
+ "sync"
+ "time"
+ "log"
+ "net/http"
+)
+
+type TApp struct {
+ Connection *websocket.Conn
+ Obs *websocket.Conn
+ WaitGroup sync.WaitGroup
+ Config config.TConfig
+ User *twitch.TUser
+ Auth *twitch.TAuth
+}
+
+func NewApp() *TApp {
+ app := new(TApp)
+ return app
+}
+
+func (app *TApp) GetAccessToken() string {
+ return app.Auth.AccessToken
+}
+
+func (app *TApp) GetUserId() string {
+ return app.User.Id
+}
+
+func (app *TApp) GetConnection() *websocket.Conn {
+ return app.Connection
+}
+
+func (app *TApp) InitConfig() error {
+ if app.Config.IsInited() {
+ return nil
+ } else {
+ return app.Config.Init()
+ }
+}
+
+func (app *TApp) InitAuth(auth_code string) error {
+ if app.Auth == nil {
+ app.Auth = new(twitch.TAuth)
+ }
+ if app.Auth.IsInited() {
+ return nil
+ } else {
+ return app.Auth.Init(auth_code, &app.Config)
+ }
+}
+
+func (app *TApp) InitUser() error {
+ info := twitch.TAuthInfo{
+ UserName: app.Config.Twitch.UserName,
+ ClientId: app.Config.Twitch.ClientId,
+ AccessToken: app.Auth.AccessToken,
+ }
+ if app.User == nil {
+ app.User = new(twitch.TUser)
+ }
+ if app.User.IsInited() {
+ return nil
+ } else {
+ return app.User.Init(info)
+ }
+}
+
+func (app *TApp) PingJob() {
+ for app.Connection != nil {
+ msg, _ := twitch.OnPing()
+ websocket.JSON.Send(app.Connection, msg)
+ time.Sleep(4 * time.Minute)
+ }
+
+ app.WaitGroup.Done()
+}
+
+func (app *TApp) ListenJob() {
+ for app.Connection != nil {
+ var msg twitch.TMessage
+ if err := websocket.JSON.Receive(app.Connection, &msg); err != nil {
+ log.Fatal(err)
+ }
+ // ignore PING and RESPONSE for now
+ if msg.Type == "RESPONSE" {
+ twitch.ProcessResponse(&msg)
+ } else if msg.Type == "MESSAGE" {
+ twitch.ProcessMessage(&msg)
+ }
+ }
+
+ app.WaitGroup.Done()
+}
+
+func (app *TApp) LocalServerJob() {
+ handler := &localserver.HttpRequestHandler{App: app}
+ http.HandleFunc("/", handler.Handle)
+ err := http.ListenAndServe(app.Config.LocalServer, nil)
+ if err != nil {
+ log.Fatal(err)
+ }
+ app.WaitGroup.Done()
+}
diff --git a/auth.go b/auth.go
@@ -1,53 +0,0 @@
-package main
-
-import (
- "encoding/json"
- "errors"
- "fmt"
- "io"
- "net/http"
-)
-
-type TAuth struct {
- AccessToken string `json:"access_token"`
- RefreshToken string `json:"refresh_token"`
- ExpiresIn uint64 `json:"expires_in"`
- Scope []string `json:"scope"`
- TokenType string `json:"token_type"`
- Message string `json:"message"`
-}
-
-func (auth *TAuth) Init(auth_code string) error {
- const auth_request_url string = "https://id.twitch.tv/oauth2/token?client_id=%s&client_secret=%s&grant_type=authorization_code&code=%s&redirect_uri=http://%s"
- response, err := http.Post(fmt.Sprintf(
- auth_request_url,
- App.Config.Twitch.ClientId,
- App.Config.Twitch.ClientSecret,
- auth_code,
- App.Config.LocalServer), "", nil)
-
- if err != nil {
- return err
- }
- defer response.Body.Close()
- body, err := io.ReadAll(response.Body)
- if err != nil {
- return err
- }
- if err := json.Unmarshal(body, auth); err != nil {
- return err
- }
-
- if len(auth.AccessToken) == 0 {
- if len(auth.Message) > 0 {
- return errors.New(auth.Message)
- } else {
- return errors.New("Auth error")
- }
- }
- return nil
-}
-
-func (auth *TAuth) IsInited() bool {
- return auth != nil && len(auth.AccessToken) > 0
-}
diff --git a/client.go b/client.go
@@ -1,77 +0,0 @@
-package main
-
-import (
- "fmt"
- "golang.org/x/net/websocket"
- "log"
-)
-
-// generic msg struct, fits MESSAGE, RESPONSE and PING
-type TMessage struct {
- Type string `json:"type"`
- Nonce string `json:"nonce"`
- Error string `json:"error"`
- Data struct {
- Topic string `json:"topic"`
- Message string `json:"message"`
- } `json:"data"`
-}
-
-// global, shame on me
-var App TApp
-
-func ListenJob() {
- for App.Connection != nil {
- var msg TMessage
- if err := websocket.JSON.Receive(App.Connection, &msg); err != nil {
- log.Fatal(err)
- }
- // ignore PING and RESPONSE for now
- if msg.Type == "RESPONSE" {
- ProcessResponse(&msg)
- } else if msg.Type == "MESSAGE" {
- ProcessMessage(&msg)
- }
- }
-
- App.WaitGroup.Done()
-}
-
-func CloseConnections() {
- if App.Connection != nil {
- App.Connection.Close()
- App.Connection = nil
- }
- if App.Obs != nil {
- App.Obs.Close()
- App.Obs = nil
- }
-}
-
-func main() {
- const code_request_url string = "https://id.twitch.tv/oauth2/authorize?client_id=%s&redirect_uri=http%%3A%%2F%%2F%s&response_type=code&scope=channel:read:redemptions\n"
-
- err := App.InitConfig()
- if err != nil {
- log.Fatal(err)
- }
-
- fmt.Printf(code_request_url, App.Config.Twitch.ClientId, App.Config.LocalServer)
-
- App.Connection, err = websocket.Dial("wss://pubsub-edge.twitch.tv:443/", "", "http://localhost")
- if err != nil {
- log.Fatal(err)
- }
- App.Obs, err = websocket.Dial(fmt.Sprintf("wss://%s:%s/", App.Config.Obs.Host, App.Config.Obs.Port), "", "http://localhost")
- if err != nil {
- log.Fatal(err)
- }
-
- App.WaitGroup.Add(3)
- go ListenJob()
- go PingJob()
- go LocalServerJob()
-
- App.WaitGroup.Wait()
- CloseConnections()
-}
diff --git a/cmd/twitchapon/main.go b/cmd/twitchapon/main.go
@@ -0,0 +1,49 @@
+package main
+
+import (
+ App "bsandro.tech/git/twitchapon/app"
+ "fmt"
+ "golang.org/x/net/websocket"
+ "log"
+)
+
+func CloseConnections(app *App.TApp) {
+ if app.Connection != nil {
+ app.Connection.Close()
+ app.Connection = nil
+ }
+ if app.Obs != nil {
+ app.Obs.Close()
+ app.Obs = nil
+ }
+}
+
+func main() {
+ const code_request_url string = "https://id.twitch.tv/oauth2/authorize?client_id=%s&redirect_uri=http%%3A%%2F%%2F%s&response_type=code&scope=channel:read:redemptions\n"
+
+ app := App.NewApp()
+ err := app.InitConfig()
+ if err != nil {
+ log.Fatal(err)
+ }
+
+ fmt.Printf(code_request_url, app.Config.Twitch.ClientId, app.Config.LocalServer)
+
+ app.Connection, err = websocket.Dial("wss://pubsub-edge.twitch.tv:443/", "", "http://localhost")
+ if err != nil {
+ log.Fatal(err)
+ }
+ app.Obs, err = websocket.Dial(fmt.Sprintf("wss://%s:%s/", app.Config.Obs.Host, app.Config.Obs.Port), "", "http://localhost")
+ if err != nil {
+ log.Fatal(err)
+ }
+
+ app.WaitGroup.Add(3)
+
+ go app.ListenJob()
+ go app.PingJob()
+ go app.LocalServerJob()
+
+ app.WaitGroup.Wait()
+ CloseConnections(app)
+}
diff --git a/cmd/twitchapon/twitchapon b/cmd/twitchapon/twitchapon
Binary files differ.
diff --git a/config.go b/config.go
@@ -1,65 +0,0 @@
-package main
-
-import (
- "encoding/json"
- "fmt"
- "os"
-)
-
-type TConfig struct {
- LocalServer string `json:"local_server"`
- Twitch struct {
- UserName string `json:"user_name"`
- ClientId string `json:"client_id"`
- ClientSecret string `json:"client_secret"`
- } `json:"twitch"`
- Obs struct {
- Host string `json:"host"`
- Port string `json:"port"`
- Password string `json:"password"`
- } `json:"obs"`
-}
-
-func (conf *TConfig) Init() error {
- config_name := "config.json"
- if len(os.Args) > 1 {
- config_name = os.Args[1]
- }
- config_data, err := os.ReadFile(config_name)
- if err != nil {
- if os.IsNotExist(err) {
- fmt.Println("Config file does not exist! Sample:")
- conf.InitSample()
- conf.Print()
- }
-
- return err
- }
- err = json.Unmarshal(config_data, conf)
- if err != nil {
- return err
- }
-
- return nil
-}
-
-func (conf *TConfig) IsInited() bool {
- return conf != nil && len(conf.LocalServer) > 0
-}
-
-func (conf *TConfig) Print() {
- fmt.Println("------------------------8<----------------------")
- json, _ := json.MarshalIndent(conf, "", "\t")
- fmt.Println(string(json))
- fmt.Println("------------------------>8----------------------")
-}
-
-func (conf *TConfig) InitSample() {
- conf.LocalServer = "localhost:8081"
- conf.Twitch.UserName = "twitch-username"
- conf.Twitch.ClientId = "xxxxxxxxxxxxxxx"
- conf.Twitch.ClientSecret = "xxxxxxxxxxx"
- conf.Obs.Host = "127.0.0.1"
- conf.Obs.Port = "4444"
- conf.Obs.Password = "xxxxxxxxx"
-}
diff --git a/config/config.go b/config/config.go
@@ -0,0 +1,65 @@
+package config
+
+import (
+ "encoding/json"
+ "fmt"
+ "os"
+)
+
+type TConfig struct {
+ LocalServer string `json:"local_server"`
+ Twitch struct {
+ UserName string `json:"user_name"`
+ ClientId string `json:"client_id"`
+ ClientSecret string `json:"client_secret"`
+ } `json:"twitch"`
+ Obs struct {
+ Host string `json:"host"`
+ Port string `json:"port"`
+ Password string `json:"password"`
+ } `json:"obs"`
+}
+
+func (conf *TConfig) Init() error {
+ config_name := "config.json"
+ if len(os.Args) > 1 {
+ config_name = os.Args[1]
+ }
+ config_data, err := os.ReadFile(config_name)
+ if err != nil {
+ if os.IsNotExist(err) {
+ fmt.Println("Config file does not exist! Sample:")
+ conf.InitSample()
+ conf.Print()
+ }
+
+ return err
+ }
+ err = json.Unmarshal(config_data, conf)
+ if err != nil {
+ return err
+ }
+
+ return nil
+}
+
+func (conf *TConfig) IsInited() bool {
+ return conf != nil && len(conf.LocalServer) > 0
+}
+
+func (conf *TConfig) Print() {
+ fmt.Println("------------------------8<----------------------")
+ json, _ := json.MarshalIndent(conf, "", "\t")
+ fmt.Println(string(json))
+ fmt.Println("------------------------>8----------------------")
+}
+
+func (conf *TConfig) InitSample() {
+ conf.LocalServer = "localhost:8081"
+ conf.Twitch.UserName = "twitch-username"
+ conf.Twitch.ClientId = "xxxxxxxxxxxxxxx"
+ conf.Twitch.ClientSecret = "xxxxxxxxxxx"
+ conf.Obs.Host = "127.0.0.1"
+ conf.Obs.Port = "4444"
+ conf.Obs.Password = "xxxxxxxxx"
+}
diff --git a/interfaces/iapp.go b/interfaces/iapp.go
@@ -0,0 +1,17 @@
+package twitchapon
+import (
+ "golang.org/x/net/websocket"
+)
+
+type IApp interface {
+ InitConfig() error
+ InitAuth(string) error
+ InitUser() error
+ PingJob()
+ ListenJob()
+ LocalServerJob()
+
+ GetAccessToken() string
+ GetUserId() string
+ GetConnection() *websocket.Conn
+}
diff --git a/listen.go b/listen.go
@@ -1,39 +0,0 @@
-package main
-
-import (
- "fmt"
- "golang.org/x/net/websocket"
-)
-
-type TListenRequest struct {
- MsgType string `json:"type"`
- Nonce string `json:"nonce"`
- Data struct {
- Topics []string `json:"topics"`
- AuthToken string `json:"auth_token"`
- } `json:"data"`
-}
-
-func OnListen(auth_token string, user_id string) interface{} {
- var req TListenRequest
- req.MsgType = "LISTEN"
- req.Data.AuthToken = auth_token
- req.Nonce = "listen-channel-points"
- req.Data.Topics = append(req.Data.Topics, fmt.Sprintf("channel-points-channel-v1.%s", user_id))
- return &req
-}
-
-func RequestListen() error {
- msg := OnListen(App.Auth.AccessToken, App.User.Id)
- websocket.JSON.Send(App.Connection, msg)
- fmt.Println("Sending channel points events subscription...")
- return nil
-}
-
-func ProcessResponse(msg *TMessage) {
- if msg.Error == "" {
- fmt.Printf("Received valid response for request '%'\n", msg.Nonce)
- } else {
- fmt.Printf("Received response for request '%s' with the following error: %s\n", msg.Nonce, msg.Error)
- }
-}
diff --git a/local_server.go b/local_server.go
@@ -1,46 +0,0 @@
-package main
-
-import (
- "io"
- "log"
- "net/http"
-)
-
-func LocalServerJob() {
- http.HandleFunc("/", HandleHttpRequest)
- err := http.ListenAndServe(App.Config.LocalServer, nil)
- if err != nil {
- log.Fatal(err)
- }
- App.WaitGroup.Done()
-}
-
-func HandleHttpRequest(w http.ResponseWriter, req *http.Request) {
- var resp string
- defer func() {
- io.WriteString(w, resp)
- }()
-
- codes, exists := req.URL.Query()["code"]
- if exists && len(codes) > 0 {
- auth_code := codes[0]
-
- err := App.InitAuth(auth_code)
- if err != nil {
- resp = err.Error()
- return
- }
-
- err = App.InitUser()
- if err != nil {
- resp = err.Error()
- return
- }
-
- RequestListen()
- resp = "OK"
-
- } else {
- resp = "Error: no code provided"
- }
-}
diff --git a/localserver/localserver.go b/localserver/localserver.go
@@ -0,0 +1,42 @@
+package localserver
+
+import (
+ "bsandro.tech/git/twitchapon/twitch"
+ iface "bsandro.tech/git/twitchapon/interfaces"
+ "io"
+ "net/http"
+)
+
+type HttpRequestHandler struct {
+ App iface.IApp
+}
+
+func (self *HttpRequestHandler) Handle(w http.ResponseWriter, req *http.Request) {
+ var resp string
+ defer func() {
+ io.WriteString(w, resp)
+ }()
+
+ codes, exists := req.URL.Query()["code"]
+ if exists && len(codes) > 0 {
+ auth_code := codes[0]
+
+ err := self.App.InitAuth(auth_code)
+ if err != nil {
+ resp = err.Error()
+ return
+ }
+
+ err = self.App.InitUser()
+ if err != nil {
+ resp = err.Error()
+ return
+ }
+
+ twitch.RequestListen(self.App)
+ resp = "OK"
+
+ } else {
+ resp = "Error: no code provided"
+ }
+}
diff --git a/message.go b/message.go
@@ -1,63 +0,0 @@
-package main
-
-import (
- "encoding/json"
- "fmt"
-)
-
-type TChannelPointsMessage struct {
- MsgType string `json:"type"`
- Data struct {
- Timestamp string `json:"timestamp"`
- Redemption struct {
- Id string `json:"id"`
- User struct {
- Id string `json:"id"`
- Login string `json:"login"`
- DisplayName string `json:"display_name"`
- } `json:"user"`
- ChannelId string `json:"channel_id"`
- RedeemedAt string `json:"redeemed_at"`
- Reward struct {
- Id string `json:"id"`
- ChannelId string `json:"channel_id"`
- Title string `json:"title"`
- Prompt string `json:"prompt"`
- Cost float64 `json:"cost"`
- IsUserInputRequired bool `json:"is_user_input_required"`
- IsSubOnly bool `json:"is_sub_only"`
- Image struct {
- Url1x string `json:"url_1x"`
- Url2x string `json:"url_2x"`
- Url4x string `json:"url_4x"`
- } `json:"image"`
- DefaultImage struct {
- Url1x string `json:"url_1x"`
- Url2x string `json:"url_2x"`
- Url4x string `json:"url_4x"`
- } `json:"default_image"`
- BackgroundColor string `json:"background_color"`
- IsEnabled bool `json:"is_enabled"`
- IsPaused bool `json:"is_paused"`
- IsInStock bool `json:"is_in_stock"`
- MaxPerStream struct {
- IsEnabled bool `json:"is_enabled"`
- MaxPerStream float64 `json:"max_per_stream"`
- } `json:"max_per_stream"`
- ShouldRedemptionSkipRequestQueue bool `json:"should_redemption_skip_request_queue"`
- } `json:"reward"`
- UserInput string `json:"user_input"`
- Status string `json:"status"`
- } `json:"redemption"`
- } `json:"data"`
-}
-
-func ProcessMessage(msg *TMessage) {
- fmt.Println("Got valid message:", msg.Data.Message)
- var chanPointsMsg TChannelPointsMessage
- if err := json.Unmarshal([]byte(msg.Data.Message), &chanPointsMsg); err != nil {
- fmt.Println("Json parse error", err)
- } else {
- fmt.Println("Json parse ok")
- }
-}
diff --git a/obs/client.go b/obs/client.go
@@ -0,0 +1,6 @@
+package twitchapon
+
+import (
+ _ "fmt"
+ _ "golang.org/x/net/websocket"
+)
diff --git a/obs_client.go b/obs_client.go
@@ -1,6 +0,0 @@
-package main
-
-import (
- _ "fmt"
- _ "golang.org/x/net/websocket"
-)
diff --git a/ping.go b/ping.go
@@ -1,26 +0,0 @@
-package main
-
-import (
- "golang.org/x/net/websocket"
- "time"
-)
-
-type TPingMsg struct {
- MsgType string `json:"type"`
-}
-
-func OnPing() (interface{}, error) {
- var res TPingMsg
- res.MsgType = "PING"
- return res, nil
-}
-
-func PingJob() {
- for App.Connection != nil {
- msg, _ := OnPing()
- websocket.JSON.Send(App.Connection, msg)
- time.Sleep(4 * time.Minute)
- }
-
- App.WaitGroup.Done()
-}
diff --git a/twitch/auth.go b/twitch/auth.go
@@ -0,0 +1,54 @@
+package twitch
+
+import (
+ "encoding/json"
+ "errors"
+ "fmt"
+ "io"
+ "net/http"
+ conf "bsandro.tech/git/twitchapon/config"
+)
+
+type TAuth struct {
+ AccessToken string `json:"access_token"`
+ RefreshToken string `json:"refresh_token"`
+ ExpiresIn uint64 `json:"expires_in"`
+ Scope []string `json:"scope"`
+ TokenType string `json:"token_type"`
+ Message string `json:"message"`
+}
+
+func (auth *TAuth) Init(auth_code string, config *conf.TConfig) error {
+ const auth_request_url string = "https://id.twitch.tv/oauth2/token?client_id=%s&client_secret=%s&grant_type=authorization_code&code=%s&redirect_uri=http://%s"
+ response, err := http.Post(fmt.Sprintf(
+ auth_request_url,
+ config.Twitch.ClientId,
+ config.Twitch.ClientSecret,
+ auth_code,
+ config.LocalServer), "", nil)
+
+ if err != nil {
+ return err
+ }
+ defer response.Body.Close()
+ body, err := io.ReadAll(response.Body)
+ if err != nil {
+ return err
+ }
+ if err := json.Unmarshal(body, auth); err != nil {
+ return err
+ }
+
+ if len(auth.AccessToken) == 0 {
+ if len(auth.Message) > 0 {
+ return errors.New(auth.Message)
+ } else {
+ return errors.New("Auth error")
+ }
+ }
+ return nil
+}
+
+func (auth *TAuth) IsInited() bool {
+ return auth != nil && len(auth.AccessToken) > 0
+}
diff --git a/twitch/listen.go b/twitch/listen.go
@@ -0,0 +1,51 @@
+package twitch
+
+import (
+ "fmt"
+ "golang.org/x/net/websocket"
+ iface "bsandro.tech/git/twitchapon/interfaces"
+)
+
+type TListenRequest struct {
+ MsgType string `json:"type"`
+ Nonce string `json:"nonce"`
+ Data struct {
+ Topics []string `json:"topics"`
+ AuthToken string `json:"auth_token"`
+ } `json:"data"`
+}
+
+// generic msg struct, fits MESSAGE, RESPONSE and PING
+type TMessage struct {
+ Type string `json:"type"`
+ Nonce string `json:"nonce"`
+ Error string `json:"error"`
+ Data struct {
+ Topic string `json:"topic"`
+ Message string `json:"message"`
+ } `json:"data"`
+}
+
+func createListen(auth_token string, user_id string) interface{} {
+ var req TListenRequest
+ req.MsgType = "LISTEN"
+ req.Data.AuthToken = auth_token
+ req.Nonce = "listen-channel-points"
+ req.Data.Topics = append(req.Data.Topics, fmt.Sprintf("channel-points-channel-v1.%s", user_id))
+ return &req
+}
+
+func RequestListen(app iface.IApp) error {
+ msg := createListen(app.GetAccessToken(), app.GetUserId())
+ websocket.JSON.Send(app.GetConnection(), msg)
+ fmt.Println("Sending channel points events subscription...")
+ return nil
+}
+
+func ProcessResponse(msg *TMessage) {
+ if msg.Error == "" {
+ fmt.Printf("Received valid response for request '%'\n", msg.Nonce)
+ } else {
+ fmt.Printf("Received response for request '%s' with the following error: %s\n", msg.Nonce, msg.Error)
+ }
+}
diff --git a/twitch/message.go b/twitch/message.go
@@ -0,0 +1,63 @@
+package twitch
+
+import (
+ "encoding/json"
+ "fmt"
+)
+
+type TChannelPointsMessage struct {
+ MsgType string `json:"type"`
+ Data struct {
+ Timestamp string `json:"timestamp"`
+ Redemption struct {
+ Id string `json:"id"`
+ User struct {
+ Id string `json:"id"`
+ Login string `json:"login"`
+ DisplayName string `json:"display_name"`
+ } `json:"user"`
+ ChannelId string `json:"channel_id"`
+ RedeemedAt string `json:"redeemed_at"`
+ Reward struct {
+ Id string `json:"id"`
+ ChannelId string `json:"channel_id"`
+ Title string `json:"title"`
+ Prompt string `json:"prompt"`
+ Cost float64 `json:"cost"`
+ IsUserInputRequired bool `json:"is_user_input_required"`
+ IsSubOnly bool `json:"is_sub_only"`
+ Image struct {
+ Url1x string `json:"url_1x"`
+ Url2x string `json:"url_2x"`
+ Url4x string `json:"url_4x"`
+ } `json:"image"`
+ DefaultImage struct {
+ Url1x string `json:"url_1x"`
+ Url2x string `json:"url_2x"`
+ Url4x string `json:"url_4x"`
+ } `json:"default_image"`
+ BackgroundColor string `json:"background_color"`
+ IsEnabled bool `json:"is_enabled"`
+ IsPaused bool `json:"is_paused"`
+ IsInStock bool `json:"is_in_stock"`
+ MaxPerStream struct {
+ IsEnabled bool `json:"is_enabled"`
+ MaxPerStream float64 `json:"max_per_stream"`
+ } `json:"max_per_stream"`
+ ShouldRedemptionSkipRequestQueue bool `json:"should_redemption_skip_request_queue"`
+ } `json:"reward"`
+ UserInput string `json:"user_input"`
+ Status string `json:"status"`
+ } `json:"redemption"`
+ } `json:"data"`
+}
+
+func ProcessMessage(msg *TMessage) {
+ fmt.Println("Got valid message:", msg.Data.Message)
+ var chanPointsMsg TChannelPointsMessage
+ if err := json.Unmarshal([]byte(msg.Data.Message), &chanPointsMsg); err != nil {
+ fmt.Println("Json parse error", err)
+ } else {
+ fmt.Println("Json parse ok")
+ }
+}
diff --git a/twitch/ping.go b/twitch/ping.go
@@ -0,0 +1,11 @@
+package twitch
+
+type TPingMsg struct {
+ MsgType string `json:"type"`
+}
+
+func OnPing() (interface{}, error) {
+ var res TPingMsg
+ res.MsgType = "PING"
+ return res, nil
+}
diff --git a/twitch/user.go b/twitch/user.go
@@ -0,0 +1,73 @@
+package twitch
+
+import (
+ "encoding/json"
+ "errors"
+ "fmt"
+ "io"
+ "net/http"
+)
+
+const users_request_url string = "https://api.twitch.tv/helix/users?login=%s"
+
+type TUser struct {
+ Id string `json:"id"`
+ BroadcasterType string `json:"broadcaster_type"`
+ Name string `json:"display_name"`
+ Login string `json:"login"`
+ Description string `json:"description"`
+ OfflineImageUrl string `json:"offline_image_url"`
+ ProfileIMageUrl string `json:"profile_image_url"`
+ UserType string `json:"type"`
+ ViewCount int `json:"view_count"`
+ CreatedAt string `json:"created_at"`
+}
+
+type TUsersResponse struct {
+ Data []TUser `json:"data"`
+}
+
+type TAuthInfo struct {
+ UserName string
+ ClientId string
+ AccessToken string
+}
+
+func getUser(info TAuthInfo, user *TUser) error {
+ client := &http.Client{}
+ request, err := http.NewRequest("GET", fmt.Sprintf(users_request_url, info.UserName), nil)
+ if err != nil {
+ return err
+ }
+ request.Header.Add("Client-Id", info.ClientId)
+ request.Header.Add("Authorization", fmt.Sprintf("Bearer %s", info.AccessToken))
+ response, err := client.Do(request)
+ if err != nil {
+ return err
+ }
+ defer response.Body.Close()
+ body, err := io.ReadAll(response.Body)
+ if err != nil {
+ return err
+ }
+
+ var users TUsersResponse
+ if err := json.Unmarshal(body, &users); err != nil {
+ return err
+ }
+ if len(users.Data) == 0 {
+ return errors.New("getUser(): no users returned")
+ }
+ *user = users.Data[0]
+
+ return nil
+}
+
+func (user *TUser) Init(info TAuthInfo) error {
+ err := getUser(info, user)
+ return err
+}
+
+func (user *TUser) IsInited() bool {
+ return user != nil && len(user.Id) > 0
+}
diff --git a/user.go b/user.go
@@ -1,67 +0,0 @@
-package main
-
-import (
- "encoding/json"
- "errors"
- "fmt"
- "io"
- "net/http"
-)
-
-const users_request_url string = "https://api.twitch.tv/helix/users?login=%s"
-
-type TUser struct {
- Id string `json:"id"`
- BroadcasterType string `json:"broadcaster_type"`
- Name string `json:"display_name"`
- Login string `json:"login"`
- Description string `json:"description"`
- OfflineImageUrl string `json:"offline_image_url"`
- ProfileIMageUrl string `json:"profile_image_url"`
- UserType string `json:"type"`
- ViewCount int `json:"view_count"`
- CreatedAt string `json:"created_at"`
-}
-
-type TUsersResponse struct {
- Data []TUser `json:"data"`
-}
-
-func getUser(user_name string, user *TUser) error {
- client := &http.Client{}
- request, err := http.NewRequest("GET", fmt.Sprintf(users_request_url, user_name), nil)
- if err != nil {
- return err
- }
- request.Header.Add("Client-Id", App.Config.Twitch.ClientId)
- request.Header.Add("Authorization", fmt.Sprintf("Bearer %s", App.Auth.AccessToken))
- response, err := client.Do(request)
- if err != nil {
- return err
- }
- defer response.Body.Close()
- body, err := io.ReadAll(response.Body)
- if err != nil {
- return err
- }
-
- var users TUsersResponse
- if err := json.Unmarshal(body, &users); err != nil {
- return err
- }
- if len(users.Data) == 0 {
- return errors.New("getUser(): no users returned")
- }
- *user = users.Data[0]
-
- return nil
-}
-
-func (user *TUser) Init() error {
- err := getUser(App.Config.Twitch.UserName, user)
- return err
-}
-
-func (user *TUser) IsInited() bool {
- return user != nil && len(user.Id) > 0
-}