First commit
This commit is contained in:
commit
5d03c6aa09
105
README.md
Normal file
105
README.md
Normal file
@ -0,0 +1,105 @@
|
||||
# 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/).
|
||||
|
||||
## Features
|
||||
|
||||
- Retrieves college football scores by team, including live scores (for Patreon members)
|
||||
- Provides upcoming game schedules if a game hasn’t started
|
||||
- Designed to run on a Discord server, responds to commands in real-time
|
||||
|
||||
## Commands
|
||||
|
||||
- **!s `<team_name>`** — Retrieves the score for the most recent game or the upcoming game schedule if the game hasn’t started.
|
||||
|
||||
Example:
|
||||
```
|
||||
!s Alabama
|
||||
```
|
||||
|
||||
## Setup
|
||||
|
||||
### 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**:
|
||||
```bash
|
||||
git clone https://github.com/yourusername/discord-cfb-bot.git
|
||||
cd discord-cfb-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. **Enable and start the service**:
|
||||
```bash
|
||||
sudo systemctl enable discord-cfb-bot
|
||||
sudo systemctl start discord-cfb-bot
|
||||
```
|
||||
|
||||
## 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
|
||||
```
|
||||
|
||||
The bot will reply with the score or the upcoming game schedule for the team.
|
||||
|
||||
## Project Structure
|
||||
|
||||
- `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
|
||||
|
||||
## Contributions
|
||||
|
||||
Contributions are welcome! Feel free to 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.
|
||||
|
41
bot/bot.go
Normal file
41
bot/bot.go
Normal file
@ -0,0 +1,41 @@
|
||||
package bot
|
||||
|
||||
import (
|
||||
"discord-cfb-bot/config"
|
||||
"discord-cfb-bot/clients"
|
||||
"github.com/bwmarrin/discordgo"
|
||||
"fmt"
|
||||
"strings"
|
||||
)
|
||||
|
||||
var Bot *discordgo.Session
|
||||
|
||||
func Start() error {
|
||||
var err error
|
||||
Bot, err = discordgo.New("Bot " + config.BotToken)
|
||||
if err != nil {
|
||||
return fmt.Errorf("error creating a Discord session: %w", err)
|
||||
}
|
||||
|
||||
fmt.Println("Discord session created successfully")
|
||||
|
||||
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, "!s ") {
|
||||
teamName := strings.TrimSpace(strings.TrimPrefix(m.Content, "!s "))
|
||||
response := clients.GetGameInfo(teamName)
|
||||
s.ChannelMessageSend(m.ChannelID, response)
|
||||
}
|
||||
}
|
3
bot/handlers.go
Normal file
3
bot/handlers.go
Normal file
@ -0,0 +1,3 @@
|
||||
package bot
|
||||
|
||||
// I'll use this eventually
|
62
clients/cfbd_client.go
Normal file
62
clients/cfbd_client.go
Normal file
@ -0,0 +1,62 @@
|
||||
package clients
|
||||
|
||||
import (
|
||||
"discord-cfb-bot/config"
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"net/http"
|
||||
"net/url"
|
||||
"time"
|
||||
)
|
||||
|
||||
const seasonStartDate = "2024-08-28"
|
||||
|
||||
type Game struct {
|
||||
HomeTeam string `json:"home_team"`
|
||||
AwayTeam string `json:"away_team"`
|
||||
HomePoints *int `json:"home_points"`
|
||||
AwayPoints *int `json:"away_points"`
|
||||
StartDateTime string `json:"start_date"`
|
||||
}
|
||||
|
||||
func getCurrentWeek() int {
|
||||
startDate, _ := time.Parse("2006-01-02", seasonStartDate)
|
||||
weeks := int(time.Since(startDate).Hours() / (24 * 7))
|
||||
return weeks + 1 // +1 to adjust to the 1-based week number
|
||||
}
|
||||
|
||||
func GetGameInfo(teamName string) string {
|
||||
currentWeek := getCurrentWeek()
|
||||
|
||||
// Use url.QueryEscape to properly encode team names with special characters
|
||||
encodedTeamName := url.QueryEscape(teamName)
|
||||
url := fmt.Sprintf("https://api.collegefootballdata.com/games?year=%d&week=%d&team=%s", time.Now().Year(), currentWeek, encodedTeamName)
|
||||
|
||||
req, _ := http.NewRequest("GET", url, nil)
|
||||
req.Header.Set("Authorization", "Bearer "+config.CFBDAPIKey)
|
||||
|
||||
client := &http.Client{}
|
||||
resp, err := client.Do(req)
|
||||
if err != nil {
|
||||
fmt.Println("Error fetching data:", err)
|
||||
return "Error fetching game info."
|
||||
}
|
||||
defer resp.Body.Close()
|
||||
|
||||
var games []Game
|
||||
if err := json.NewDecoder(resp.Body).Decode(&games); err != nil {
|
||||
fmt.Println("Error decoding response:", err)
|
||||
return "Error parsing game data."
|
||||
}
|
||||
|
||||
for _, game := range games {
|
||||
startTime, _ := time.Parse(time.RFC3339, game.StartDateTime)
|
||||
if time.Now().After(startTime) && game.HomePoints != nil && game.AwayPoints != nil {
|
||||
return fmt.Sprintf("%s: %d %s: %d", game.AwayTeam, *game.AwayPoints, game.HomeTeam, *game.HomePoints)
|
||||
} else if time.Now().Before(startTime) {
|
||||
return fmt.Sprintf("%s @ %s %s Eastern", game.AwayTeam, game.HomeTeam, startTime.Format("03:04 PM"))
|
||||
}
|
||||
}
|
||||
|
||||
return "No recent or upcoming game data found."
|
||||
}
|
10
config/config.go
Normal file
10
config/config.go
Normal file
@ -0,0 +1,10 @@
|
||||
package config
|
||||
|
||||
var (
|
||||
BotToken = " " // Define your bot token here
|
||||
CFBDAPIKey = " " // Define your CFBD API Key here
|
||||
)
|
||||
|
||||
func LoadConfig() {
|
||||
// Just leaving this here in case we need to add stuff later.
|
||||
}
|
11
go.mod
Normal file
11
go.mod
Normal file
@ -0,0 +1,11 @@
|
||||
module discord-cfb-bot
|
||||
|
||||
go 1.19
|
||||
|
||||
require github.com/bwmarrin/discordgo v0.28.1
|
||||
|
||||
require (
|
||||
github.com/gorilla/websocket v1.4.2 // indirect
|
||||
golang.org/x/crypto v0.0.0-20210421170649-83a5a9bb288b // indirect
|
||||
golang.org/x/sys v0.0.0-20201119102817-f84b799fce68 // indirect
|
||||
)
|
12
go.sum
Normal file
12
go.sum
Normal file
@ -0,0 +1,12 @@
|
||||
github.com/bwmarrin/discordgo v0.28.1 h1:gXsuo2GBO7NbR6uqmrrBDplPUx2T3nzu775q/Rd1aG4=
|
||||
github.com/bwmarrin/discordgo v0.28.1/go.mod h1:NJZpH+1AfhIcyQsPeuBKsUtYrRnjkyu0kIVMCHkZtRY=
|
||||
github.com/gorilla/websocket v1.4.2 h1:+/TMaTYc4QFitKJxsQ7Yye35DkWvkdLcvGKqM+x0Ufc=
|
||||
github.com/gorilla/websocket v1.4.2/go.mod h1:YR8l580nyteQvAITg2hZ9XVh4b55+EU/adAjf1fMHhE=
|
||||
golang.org/x/crypto v0.0.0-20210421170649-83a5a9bb288b h1:7mWr3k41Qtv8XlltBkDkl8LoP3mpSgBW8BUoxtEdbXg=
|
||||
golang.org/x/crypto v0.0.0-20210421170649-83a5a9bb288b/go.mod h1:T9bdIzuCu7OtxOm1hfPfRQxPLYneinmdGuTeoZ9dtd4=
|
||||
golang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg=
|
||||
golang.org/x/sys v0.0.0-20201119102817-f84b799fce68 h1:nxC68pudNYkKU6jWhgrqdreuFiOQWj1Fs7T3VrH4Pjw=
|
||||
golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo=
|
||||
golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
|
||||
golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
|
18
main.go
Normal file
18
main.go
Normal file
@ -0,0 +1,18 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"discord-cfb-bot/bot"
|
||||
"discord-cfb-bot/config"
|
||||
"fmt"
|
||||
)
|
||||
|
||||
func main() {
|
||||
config.LoadConfig()
|
||||
err := bot.Start()
|
||||
if err != nil {
|
||||
fmt.Println("Error starting the bot:", err)
|
||||
return
|
||||
}
|
||||
|
||||
select {}
|
||||
}
|
9
utils/time.go
Normal file
9
utils/time.go
Normal file
@ -0,0 +1,9 @@
|
||||
package utils
|
||||
|
||||
import (
|
||||
"time"
|
||||
)
|
||||
|
||||
func ParseGameTime(gameTime string) (time.Time, error) {
|
||||
return time.Parse("03:04 PM", gameTime)
|
||||
}
|
Loading…
x
Reference in New Issue
Block a user