twitchapon

[Twi]tch [Cha]nnel [Po]ints Rewards Redemption Listener
git clone git://bsandro.tech/twitchapon
Log | Files | Refs | README | LICENSE

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:
M.gitignore | 1-
Dapp.go | 45---------------------------------------------
Aapp/app.go | 110+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Dauth.go | 53-----------------------------------------------------
Dclient.go | 77-----------------------------------------------------------------------------
Acmd/twitchapon/main.go | 49+++++++++++++++++++++++++++++++++++++++++++++++++
Acmd/twitchapon/twitchapon | 0
Dconfig.go | 65-----------------------------------------------------------------
Aconfig/config.go | 65+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Ainterfaces/iapp.go | 17+++++++++++++++++
Dlisten.go | 39---------------------------------------
Dlocal_server.go | 46----------------------------------------------
Alocalserver/localserver.go | 42++++++++++++++++++++++++++++++++++++++++++
Dmessage.go | 63---------------------------------------------------------------
Aobs/client.go | 6++++++
Dobs_client.go | 6------
Dping.go | 26--------------------------
Atwitch/auth.go | 54++++++++++++++++++++++++++++++++++++++++++++++++++++++
Atwitch/listen.go | 51+++++++++++++++++++++++++++++++++++++++++++++++++++
Atwitch/message.go | 63+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Atwitch/ping.go | 11+++++++++++
Atwitch/user.go | 73+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Duser.go | 67-------------------------------------------------------------------
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 -}