Compare commits

..

10 Commits

Author SHA1 Message Date
d8bbab483f Remove API key 2025-05-09 05:43:18 +00:00
2524404d33 Add nicknames 2025-05-03 22:05:42 +00:00
36f03c2281 Convert team names to lower to be case-insensitive 2024-11-12 11:34:57 -06:00
385bd4c7bf Change to local time to avoid UTC confusion if no date is present 2024-11-11 19:24:15 -05:00
c1a20533da The shit I do for w00t4me 2024-11-11 13:04:55 -05:00
551f077158 Change time zone to central and remove double timezone listing 2024-11-11 12:25:19 -05:00
fa7185ec01 Oops 2024-11-11 11:50:24 -05:00
8876f134b1 Readd modfiles 2024-11-10 21:28:24 -05:00
5503df6270 remove modfiles 2024-11-10 21:26:57 -05:00
56d4158d8e Rename 2024-11-10 21:22:50 -05:00
8 changed files with 136 additions and 95 deletions

28
Dockerfile Normal file
View File

@@ -0,0 +1,28 @@
# Use the official Go 1.23 image for building the application
FROM golang:1.23-alpine AS builder
# Set the working directory inside the container
WORKDIR /app
# Copy the Go modules manifest and download dependencies
COPY go.mod go.sum ./
RUN go mod download
# Copy the rest of the application source code
COPY . .
# Build the Go application from the appropriate directory inside 'cmd'
RUN go build -o discord-scores-bot ./cmd/main.go
# Create a minimal runtime image
FROM alpine:latest
RUN apk --no-cache add ca-certificates
# Set the working directory
WORKDIR /root/
# Copy the built Go binaries from the builder stage
COPY --from=builder /app/discord-scores-bot .
# Run the bot (use separate Dockerfiles if needed for distinct deployments)
CMD ["./discord-scores-bot"]

120
README.md
View File

@@ -1,105 +1,71 @@
# Discord College Football Bot
A Discord bot written in Go that provides up-to-date college football scores and schedules. It responds to commands to show recent or upcoming games for specific teams using the [College Football Data API](https://collegefootballdata.com/).
# Discord Scores Bot
**Discord Scores Bot** is a powerful, user-friendly bot designed to bring real-time college sports scores and updates directly to your Discord server. Whether you follow college football or basketball, this bot keeps you up-to-date with live game information, final scores, and upcoming match schedules.
## Features
- Retrieves college football scores by team, including live scores (for Patreon members)
- Provides upcoming game schedules if a game hasnt started
- Designed to run on a Discord server, responds to commands in real-time
- **Live Score Updates**: Get real-time scores for ongoing games.
- **Upcoming Matches**: Check when your favorite teams are playing next.
- **Final Scores**: View results from completed games.
- **Team Search**: Search for scores using team names.
- **Date & Week Specification**: Specify dates (e.g., `MM/DD`) for basketball and weeks for football to view related games.
## Commands
## Supported Commands
- **!s `<team_name>`** — Retrieves the score for the most recent game or the upcoming game schedule if the game hasnt started.
Example:
```
!s Alabama
```
- `!cfb [team] [week]`: Retrieves college football game details for the specified team and week (current week if not specified).
- `!cbb [team] [MM/DD]`: Retrieves college basketball game details for the specified team and date (today's date if not specified).
## Setup
## Installation
### Prerequisites
- **Go** (1.19 or later)
- **College Football Data API key**: Sign up at [College Football Data](https://collegefootballdata.com/) and obtain an API key.
- **Discord Bot Token**: Set up a bot on the [Discord Developer Portal](https://discord.com/developers/applications) to obtain a token.
### Installation
1. **Clone the repository**:
1. Clone this repository:
```bash
git clone https://github.com/yourusername/discord-cfb-bot.git
cd discord-cfb-bot
git clone https://your-repo-url/discord-scores-bot.git
cd discord-scores-bot
```
2. **Configure API keys**:
- In `config/config.go`, set the `CFBDAPIKey` with your College Football Data API key and your Discord bot token.
3. **Run the bot**:
- To test locally:
```bash
go run main.go
```
- To run in the background:
- Use `nohup`, `screen`, `tmux`, or create a `systemd` service (see below for options).
### Running the Bot in the Background
You can keep the bot running in the background by using tools like `nohup`, `screen`, or `tmux`. For production servers, consider creating a `systemd` service.
Example with `nohup`:
```bash
nohup go run main.go > bot.log 2>&1 &
```
### Deployment with `systemd` (Linux)
1. Create a `systemd` service file at `/etc/systemd/system/discord-cfb-bot.service`:
```ini
[Unit]
Description=Discord College Football Bot
After=network.target
[Service]
ExecStart=/usr/local/go/bin/go run /path/to/main.go
WorkingDirectory=/path/to/discord-cfb-bot
StandardOutput=append:/path/to/bot.log
StandardError=append:/path/to/bot.log
Restart=always
User=yourusername
[Install]
WantedBy=multi-user.target
2. Install dependencies:
```bash
go mod tidy
```
2. **Enable and start the service**:
3. Create a `.env` file and add your Discord bot token:
```env
BOT_TOKEN=your-discord-bot-token
```
4. Run the bot:
```bash
sudo systemctl enable discord-cfb-bot
sudo systemctl start discord-cfb-bot
go run cmd/main.go
```
## Usage
Invite the bot to your server and type commands in any text channel where the bot has permissions. For example:
```
!s Texas A&M
```
- Invite the bot to your Discord server and type `!cfb [team] [week]` or `!cbb [team] [MM/DD]` to get game information.
- If a date or week is not specified, the bot defaults to today's date or the current week.
The bot will reply with the score or the upcoming game schedule for the team.
## Examples
## Project Structure
- `!cfb Alabama`: Get details of Alabama's football games for the current week.
- `!cbb Duke 11/10`: Get details of Duke's basketball game on November 10th.
- `main.go`: Entry point of the bot
- `config/config.go`: Configuration file for the API keys and other settings
- `clients/cfbd_client.go`: Client for interacting with the College Football Data API
- `bot/bot.go`: Discord bot setup and command handling
## Configuration
## Contributions
The bot is configured to use pre-defined API endpoints for fetching scores:
- College football: `https://ncaa.ewnix.net/football/fbs`
- College basketball: `https://ncaa.ewnix.net/scoreboard/basketball-men/d1`
Contributions are welcome! Feel free to submit a pull request to improve the bot or add new features.
Ensure that your bot has the necessary permissions to read and send messages in the channels where it's active.
## Contribution
Contributions are welcome! Feel free to open an issue or submit a pull request to improve the bot or add new features.
## License
This project is licensed under the MIT License. See the [LICENSE](LICENSE) file for details.
---
Stay connected with your favorite college sports and enhance your Discord experience with **Discord Scores Bot**!

View File

@@ -1,8 +1,8 @@
package main
import (
"discord-cfb-bot/internal/bot"
"discord-cfb-bot/config"
"discord-scores-bot/internal/bot"
"discord-scores-bot/config"
"fmt"
)

View File

@@ -1,12 +1,11 @@
package config
var BotToken = " "
var BotToken = ""
var CustomTeamNames = map[string]string{
"UGA": "Georgia",
"Booger Eaters": "Auburn",
"Rape Enablers": "LSU",
"Checkerboard Clowns": "Tennessee",
"bama": "Alabama",
"uga": "Georgia",
"buttchuggers": "Tennessee",
}
func LoadConfig() {

2
go.mod
View File

@@ -1,4 +1,4 @@
module discord-cfb-bot
module discord-scores-bot
go 1.19

View File

@@ -1,9 +1,9 @@
package bot
import (
"discord-cfb-bot/config"
cfbClient "discord-cfb-bot/internal/clients/cfb"
cbbClient "discord-cfb-bot/internal/clients/cbb"
"discord-scores-bot/config"
cfbClient "discord-scores-bot/internal/clients/cfb"
cbbClient "discord-scores-bot/internal/clients/cbb"
"github.com/bwmarrin/discordgo"
"fmt"
"strings"

View File

@@ -35,12 +35,20 @@ type Team struct {
func GetGameInfo(teamName, date string) string {
// If no date is provided, use today's date
if date == "" {
date = time.Now().Format("2006/01/02") // Format as YYYY/MM/DD
// Load Central Time location
loc, err := time.LoadLocation("America/Chicago")
if err != nil {
loc = time.FixedZone("CST", -6*60*60) // Fallback to a fixed offset if loading fails
}
// Get current time in Central Time
currentTime := time.Now().In(loc)
date = currentTime.Format("2006/01/02") // Format as YYYY/MM/DD in Central Time
} else {
// Check if the last 5 characters are in MM/DD format for date input
if len(date) == 5 && unicode.IsDigit(rune(date[0])) && unicode.IsDigit(rune(date[1])) && date[2] == '/' &&
unicode.IsDigit(rune(date[3])) && unicode.IsDigit(rune(date[4])) {
date = fmt.Sprintf("2024/%s", strings.ReplaceAll(date, "/", "/")) // Add the year and reformat
date = fmt.Sprintf("2024/%s", date) // Add the year and format
} else {
return "Invalid date format. Please use MM/DD."
}
@@ -89,8 +97,16 @@ func fetchAndParseGames(apiURL, teamName string) string {
game.Game.Home.Names.Short, game.Game.Home.Score,
currentPeriod, game.Game.ContestClock)
} else if game.Game.GameState == "pre" {
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)
startTime, err := time.Parse("03:04PM ET", game.Game.StartTime)
if err == nil {
startTime = startTime.Add(-1 * time.Hour) // Subtract 1 hour for Central Time
formattedTime := startTime.Format("03:04 PM CT")
gameInfo = fmt.Sprintf("Upcoming: %s @ %s on %s at %s",
game.Game.Away.Names.Short, game.Game.Home.Names.Short, game.Game.StartDate, formattedTime)
} else {
gameInfo = fmt.Sprintf("Upcoming: %s @ %s on %s at %s",
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,

View File

@@ -6,8 +6,11 @@ import (
"io/ioutil"
"log"
"net/http"
"strconv"
"strings"
"time"
"unicode"
"discord-scores-bot/config"
)
type Game struct {
@@ -15,6 +18,7 @@ type Game struct {
GameID string `json:"gameID"`
StartDate string `json:"startDate"`
StartTime string `json:"startTime"`
StartTimeEpoch string `json:"startTimeEpoch"`
GameState string `json:"gameState"`
CurrentPeriod string `json:"currentPeriod"`
ContestClock string `json:"contestClock"`
@@ -33,8 +37,14 @@ type Team struct {
}
func GetGameInfo(teamName string) string {
teamName = strings.TrimSpace(teamName)
teamName = strings.TrimSpace(strings.ToLower(teamName))
week := ""
// Check if the teamName matches a custom name and replace if found
if customName, exists := config.CustomTeamNames[teamName]; exists {
log.Printf("Custom team name detected: %s -> %s", teamName, customName)
teamName = customName
}
// Check if the last two characters of the input are integers for the week
if len(teamName) >= 2 && unicode.IsDigit(rune(teamName[len(teamName)-1])) && unicode.IsDigit(rune(teamName[len(teamName)-2])) {
@@ -90,8 +100,13 @@ func GetGameInfo(teamName string) string {
game.Game.Home.Names.Short, game.Game.Home.Score,
period, game.Game.ContestClock)
} else if game.Game.GameState == "pre" {
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)
centralTime, err := convertEpochToCentralTime(game.Game.StartTimeEpoch)
if err != nil {
log.Printf("Time conversion error: %v", err)
centralTime = game.Game.StartTime // Fall back to original time if conversion fails
}
gameInfo = fmt.Sprintf("Upcoming: %s @ %s on %s at %s CT",
game.Game.Away.Names.Short, game.Game.Home.Names.Short, game.Game.StartDate, centralTime)
} else if game.Game.GameState == "final" {
gameInfo = fmt.Sprintf("Final: %s: **%s** %s: **%s**",
game.Game.Away.Names.Short, game.Game.Away.Score,
@@ -109,3 +124,20 @@ func GetGameInfo(teamName string) string {
return "No game found for the specified team."
}
func convertEpochToCentralTime(epochStr string) (string, error) {
epoch, err := strconv.ParseInt(epochStr, 10, 64)
if err != nil {
return "", fmt.Errorf("error parsing epoch: %w", err)
}
// Convert the epoch time to time.Time and set it to Central Time
t := time.Unix(epoch, 0).UTC()
locCT, err := time.LoadLocation("America/Chicago")
if err != nil {
return "", fmt.Errorf("error loading Central Time location: %w", err)
}
centralTime := t.In(locCT)
return centralTime.Format("03:04 PM"), nil
}