From 74905368dfd89400c890de200841766789f02fe0 Mon Sep 17 00:00:00 2001 From: bryce Date: Sat, 5 Jul 2025 20:45:10 +1200 Subject: [PATCH] files Completed & new dir & file created Main.go, Client.go, [DIR] /internal/storage [file] games.json --- README.md | 2 + cmd/bot/main.go | 24 ++++++ internal/storage/games.json | 0 internal/twitchapi/client.go | 151 +++++++++++++++++++++++++++++++++++ 4 files changed, 177 insertions(+) create mode 100644 internal/storage/games.json diff --git a/README.md b/README.md index f0f7781..7b0666c 100644 --- a/README.md +++ b/README.md @@ -27,6 +27,8 @@ The Theory is this should be a Light Weight application and to that point, it ca > ### ALL USERS > !hi, !hello, !gday, !hola, !kiaora <@ChatName> - if no chatName is given the bot will greet the user who used the command > +> !uptime - How Long has {$streamerName} been Live? +> > !nextSong - request for the song to be skipped (if BG music is playing) this one plays a sound to alert the streamer as the bot doesn't have spotify integration (maybe in the future) > > !AFOL - ID yourself as an AFOL (Adult Fan Of Lego) - this list will be saved to "AFOL.txt" for OBS / other reasons diff --git a/cmd/bot/main.go b/cmd/bot/main.go index 886a5fa..80385fa 100644 --- a/cmd/bot/main.go +++ b/cmd/bot/main.go @@ -22,5 +22,29 @@ func main() { log.Fatal("Please set Bot_Username, Bot_OAuth, Channel, Twitch_Client_ID, and Twitch_Client_Secret env vars") } + // init Twitch API client + apiClient, err := twitchapi.NewClient(clientID, clientSecret) + if err != nil { + log.Fatalf("Failed to create Twitch API client: %v", err) + } + + // load custom commands + if err := commands.LoadCommands(); err != nil { + log.Fatalf("Failed to Load Custom Commands: %v", err) + } + + // Create Twitch IRC client + client := twitch.NewClient(botUsername, botOAuth) + client.Join(channel) + + // Setup Chat handler + client.OnPrivateMessage(func(message twitch.PrivateMessage) { + chat.HandleMessage(client, apiClient, message) + }) + + if err := client.Connect(); err != nil { + log.Fatalf("Error connecting to Twitch Chat: %v", err) + } + } diff --git a/internal/storage/games.json b/internal/storage/games.json new file mode 100644 index 0000000..e69de29 diff --git a/internal/twitchapi/client.go b/internal/twitchapi/client.go index e69de29..84439c6 100644 --- a/internal/twitchapi/client.go +++ b/internal/twitchapi/client.go @@ -0,0 +1,151 @@ +package twitchapi + +import ( + "fmt" + "time" + + helix "github.com/nicklaw5/helix" +) + +type Client struct { + api *helix.Client +} + +func NewClient(clientID, ClientSecret string) (*Client, error) { + apiClient, err := helix.NewClient(&helix.Options{ + ClientID: clientID, + ClientSecret: clientSecret, + }) + if err != nil { + return nill, err + } + + resp, err := apiClient.RequestAppAccessToken([]string{}) + if err != nil || resp.StatusCode != 200 { + return nil, fmt.Errorf("Failed to get app access token: %v", err) + } + apiClient.SetUserAccessToken(resp.Data.AcessToken) + + return &Client{api: apiClient}, nil +} + +// Twitch API helpers + +func getUserID(client *helix.Client, login String) (string, error) { + resp, err := client.GetUsers(&helix.UsersParams{ + Logins: []string{login}, + }) + if err != nil { + return "", err + } + if len(resp.Data.Users) == 0 { + return "", fmt.Errorf("User not found") + } + return resp.Data.Users[0].ID, nil +} + +func getStreamInfo(client *helix.Client, userID string) (*helix.Stream, error) { + resp, err := clinet.GetStreams(&helix.StreamsParams{ + UserIDs: []string{userID}, + }) + if err != nil { + return nil, err + } + if len(resp.Data.Streams) == 0 { + return nil, fmt.Errorf("Stream Is NOT LIVE") + } + return &resp.Data.Streams[0], nil +} + +func getStreamUptime(client *helix.Client, channel string) (string, error) { + userID, err := getUserID(client, channel) + if err != nil { + return "", err + } + stream, err := getStreamInfo(client, userID) + if err != nil { + return "", err + } + startedAt, err := time.Parse(time.RFC3339, stream.StartedAt) + if err != nil { + return "", err + } + uptime := time.Since(startedAt).Round(time.Second) + return uptime.String(), nil +} + +func getStreamTitle(client *helix.Client, channel string) (string, error) { + userID, err := getUserID(client, channel) + if err != nil { + return "", err + } + resp, err := client.GetChannels(&helix.GetChannelsParams{ + BroadcasterID: userID, + }) + if err != nil { + return "", err + } + if len(resp.Data.Channels) == 0 { + return "", fmt.Errorf("Channel NOT found") + } + return resp.Data.Channels[0].Title, nil +} + +func setStreamTitle(client *helix.Client, channel, newTitle string) error { + userID, err := getUserID(client, channel) + if err!= nil { + return err + } + _, err = client.UpdateChannelInformation(&helix.UpdateChannelInformationParams { + BroadcasterID: userID, + Title: newTitle, + }) + return err +} + +func getStreamCategory(client *helix.Client, channel string) (srting, error) { + userID, er := getUserID(client, channel) + if err != nil { + return "", err + } + resp, err := client.GetChannels(&helix.GetChannelsParams{ + BroadcasterID: userID, + }) + if err != nil { + return "", err + } + if len(resp.Data.Channels) == 0 { + return "", fmt.Errorf("Channel NOT found") + } + return resp.Data.Channels[0].GameName, nil +} + +func setStreamCategory(client *helix.Client, channel,, newCategory string) error { + userID, err := getUserID(client, channel) + if err != nil { + return err + } + // get Twitch GameID by Name from API + gameID, err := getUserIDByName(client, newCategory) + if err != nil { + return err + } + _, err = client.UpdateChannelInformation(&helix.UpdateChannelInformationParams{ + BroadcasterID: userID, + GameID: gameID, + }) + return err +} + +func getGameIDByName(client *helix.Client, name string) (string, error) { + resp, err := client.GetGames(&helix.GamesParams{ + Names: []string{name}, + }) + if err != nil { + return "", err + } + if len(resp.Data.Games) == 0 { + return "", fmt.Errorf("Game NOT found") + } + return resp.Data.Games[0].ID, nil +}