From 57e9aacd3a5ea0be5840461a964177df478f6563 Mon Sep 17 00:00:00 2001 From: Kevin Thompson Date: Sun, 10 Nov 2024 18:10:04 -0600 Subject: [PATCH] Complete rewrite to support new API. --- config/config.go | 12 ++-- internal/bot/bot.go | 56 +++++++++---------- internal/clients/cfb.go | 121 +++++++++++++++++++++++----------------- 3 files changed, 105 insertions(+), 84 deletions(-) diff --git a/config/config.go b/config/config.go index 0e7a0ce..2c6c6dd 100644 --- a/config/config.go +++ b/config/config.go @@ -1,9 +1,13 @@ package config -var ( - BotToken = "MTMwMDU3NjEzNTkzMDk3MDI2NA.GuRb2j.1C-wvihlNPCPWz1SXeEmeZcONJNUIirkxgTWW8" - CFBDAPIKey = "ay2L+7JSttYQP9e10qxpAkluht7NHcyEnSqbBUsqWrwVjBcl02QH2XjOaVDnPRBy" -) +var BotToken = "" + +var CustomTeamNames = map[string]string{ + "UGA": "Georgia", + "Booger Eaters": "Auburn", + "Rape Enablers": "LSU", + "Checkerboard Clowns": "Tennessee", +} func LoadConfig() { } diff --git a/internal/bot/bot.go b/internal/bot/bot.go index 00dc02a..218ed84 100644 --- a/internal/bot/bot.go +++ b/internal/bot/bot.go @@ -1,41 +1,41 @@ package bot import ( - "fmt" - "strings" - - "discord-cfb-bot/internal/clients/cfb" // Adjust this import path as needed - "github.com/bwmarrin/discordgo" + "discord-cfb-bot/config" + "discord-cfb-bot/internal/clients" + "github.com/bwmarrin/discordgo" + "fmt" + "strings" ) -// MessageCreate handles incoming messages and triggers the appropriate response -func MessageCreate(s *discordgo.Session, m *discordgo.MessageCreate) { - // Ignore messages from the bot itself - if m.Author.ID == s.State.User.ID { - return - } +var Bot *discordgo.Session - // Command for fetching college football scores - if strings.HasPrefix(m.Content, "!cfb") { - // Extract any arguments (e.g., week number) if needed - weekNumber := 10 // Replace this with dynamic parsing if necessary - - games, err := cfb.FetchScoreboard(weekNumber) +func Start() error { + var err error + Bot, err = discordgo.New("Bot " + config.BotToken) if err != nil { - s.ChannelMessageSend(m.ChannelID, fmt.Sprintf("Error fetching games: %s", err)) - return + return fmt.Errorf("error creating a Discord session: %w", err) } - if len(games) == 0 { - s.ChannelMessageSend(m.ChannelID, "No games found for this week.") - return - } + fmt.Println("Discord session created successfully") - // Send formatted game summaries - for _, game := range games { - gameSummary := cfb.FormatGameOutput(game) - s.ChannelMessageSend(m.ChannelID, gameSummary) + Bot.AddHandler(commandHandler) + err = Bot.Open() + if err != nil { + return fmt.Errorf("Error opening connection: %w", err) } - } + fmt.Println("Bot is now running.") + return nil } +func commandHandler(s *discordgo.Session, m *discordgo.MessageCreate) { + if m.Author.Bot { + return + } + + if strings.HasPrefix(m.Content, "!cfb ") { + teamName := strings.TrimSpace(strings.TrimPrefix(m.Content, "!cfb ")) + response := cfb.GetGameInfo(teamName) + s.ChannelMessageSend(m.ChannelID, response) + } +} diff --git a/internal/clients/cfb.go b/internal/clients/cfb.go index 8d1a928..a6e787f 100644 --- a/internal/clients/cfb.go +++ b/internal/clients/cfb.go @@ -1,81 +1,98 @@ -package cfb +ipackage cfb import ( "encoding/json" - "errors" "fmt" "io/ioutil" "net/http" - "os" - "time" + "strings" + + "discord-cfb-bot/config" ) -// Game represents the structure of a single game type Game struct { - AwayTeam string `json:"away_team"` - AwayScore string `json:"away_score"` - HomeTeam string `json:"home_team"` - HomeScore string `json:"home_score"` - GameState string `json:"game_state"` - CurrentPeriod string `json:"current_period"` - StartTime string `json:"start_time"` - ContestClock string `json:"contest_clock"` - StartTimeEpoch int64 `json:"start_time_epoch"` + Game struct { + GameID string `json:"gameID"` + StartDate string `json:"startDate"` + StartTime string `json:"startTime"` + GameState string `json:"gameState"` + CurrentPeriod string `json:"currentPeriod"` + ContestClock string `json:"contestClock"` + Home Team `json:"home"` + Away Team `json:"away"` + FinalMessage string `json:"finalMessage"` + } `json:"game"` } -// FetchScoreboard retrieves and parses the scoreboard data from the specified API URL -func FetchScoreboard(weekNumber int) ([]Game, error) { - apiKey := os.Getenv("CFBD_API_KEY") - if apiKey == "" { - return nil, errors.New("missing API key") +type Team struct { + Score string `json:"score"` + Names struct { + Short string `json:"short"` + Full string `json:"full"` + } `json:"names"` +} + +func GetGameInfo(teamName string) string { + // Make the teamName input lowercase + teamNameLower := strings.ToLower(teamName) + + // Check if the lowercase teamName matches a custom abbreviation + for key, value := range config.CustomTeamNames { + if strings.ToLower(key) == teamNameLower { + teamNameLower = strings.ToLower(value) + break + } } - url := fmt.Sprintf("https://ncaa.ewnix.net/scoreboard/football/fbs/2024/%d/all-conf", weekNumber) - req, _ := http.NewRequest("GET", url, nil) - req.Header.Set("Authorization", fmt.Sprintf("Bearer %s", apiKey)) - - client := &http.Client{} - resp, err := client.Do(req) + apiURL := "https://ncaa.ewnix.net/scoreboard/football/fbs/2024/all-conf" + resp, err := http.Get(apiURL) if err != nil { - return nil, err + return fmt.Sprintf("Failed to reach the scoreboard API: %v", err) } defer resp.Body.Close() if resp.StatusCode != http.StatusOK { - return nil, fmt.Errorf("failed to fetch scoreboard, status code: %d", resp.StatusCode) + return fmt.Sprintf("Failed to reach the scoreboard API. Status code: %d", resp.StatusCode) } body, _ := ioutil.ReadAll(resp.Body) - var data struct { - Games []struct { - Game Game `json:"game"` - } `json:"games"` + + var apiResponse struct { + Games []Game `json:"games"` + } + if err := json.Unmarshal(body, &apiResponse); err != nil { + return "Error parsing the API response." } - if err := json.Unmarshal(body, &data); err != nil { - return nil, fmt.Errorf("error parsing scoreboard: %w", err) - } + var results []string - games := make([]Game, len(data.Games)) - for i, g := range data.Games { - // Adjust start time by subtracting 5 hours for upcoming games - if g.Game.GameState == "upcoming" { - epochTime := time.Unix(g.Game.StartTimeEpoch, 0).Add(-5 * time.Hour) - g.Game.StartTime = epochTime.Format("03:04PM ET") + for _, game := range apiResponse.Games { + // Filter games by team name (either home or away) + if strings.ToLower(game.Game.Home.Names.Short) == teamNameLower || + strings.ToLower(game.Game.Away.Names.Short) == teamNameLower { + + var gameInfo string + if game.Game.GameState == "in_progress" { + gameInfo = fmt.Sprintf("%s: **%s** %s: **%s** | Quarter: %s | Time Remaining: %s", + game.Game.Away.Names.Short, game.Game.Away.Score, + game.Game.Home.Names.Short, game.Game.Home.Score, + game.Game.CurrentPeriod, game.Game.ContestClock) + } else if game.Game.GameState == "scheduled" { + gameInfo = fmt.Sprintf("Upcoming: %s @ %s on %s at %s ET", + game.Game.Away.Names.Short, game.Game.Home.Names.Short, game.Game.StartDate, game.Game.StartTime) + } else if game.Game.GameState == "final" { + gameInfo = fmt.Sprintf("Final: %s: %s %s: %s", + game.Game.Away.Names.Short, game.Game.Away.Score, + game.Game.Home.Names.Short, game.Game.Home.Score) + } + + results = append(results, gameInfo) } - games[i] = g.Game } - return games, nil -} - -// FormatGameOutput formats the game data into a string -func FormatGameOutput(game Game) string { - if game.GameState == "live" { - return fmt.Sprintf("%s: *%s* %s: *%s* Quarter: %s Time Remaining: %s", - game.AwayTeam, game.AwayScore, game.HomeTeam, game.HomeScore, game.CurrentPeriod, game.ContestClock) + if len(results) > 0 { + return strings.Join(results, "\n") } - return fmt.Sprintf("%s: %s %s: %s Final", - game.AwayTeam, game.AwayScore, game.HomeTeam, game.HomeScore) -} + return "No game found for the specified team." +}