mirror of
https://github.com/lyx0/nourybot.git
synced 2024-11-13 19:49:55 +01:00
c4a05a2619
if running in the `prod` environment continue using the `()` prefix. otherwise in `dev` environment use !! as the prefix so we don't interfere with database calls to the production database because it was the same commands in both environments.
253 lines
6.4 KiB
Go
253 lines
6.4 KiB
Go
package main
|
|
|
|
import (
|
|
"context"
|
|
"database/sql"
|
|
"fmt"
|
|
"os"
|
|
"time"
|
|
|
|
"github.com/gempir/go-twitch-irc/v4"
|
|
"github.com/jakecoffman/cron"
|
|
"github.com/joho/godotenv"
|
|
_ "github.com/lib/pq"
|
|
"github.com/lyx0/nourybot/internal/common"
|
|
"github.com/lyx0/nourybot/internal/data"
|
|
"github.com/nicklaw5/helix/v2"
|
|
|
|
"go.uber.org/zap"
|
|
)
|
|
|
|
type config struct {
|
|
twitchUsername string
|
|
twitchOauth string
|
|
twitchClientId string
|
|
twitchClientSecret string
|
|
twitchID string
|
|
wolframAlphaAppID string
|
|
commandPrefix string
|
|
env string
|
|
db struct {
|
|
dsn string
|
|
maxOpenConns int
|
|
maxIdleConns int
|
|
maxIdleTime string
|
|
}
|
|
}
|
|
|
|
type application struct {
|
|
TwitchClient *twitch.Client
|
|
HelixClient *helix.Client
|
|
Log *zap.SugaredLogger
|
|
Db *sql.DB
|
|
Models data.Models
|
|
Scheduler *cron.Cron
|
|
Environment string
|
|
Config config
|
|
// Rdb *redis.Client
|
|
}
|
|
|
|
func main() {
|
|
var cfg config
|
|
// Initialize a new sugared logger that we'll pass on
|
|
// down through the application.
|
|
logger := zap.NewExample()
|
|
defer func() {
|
|
if err := logger.Sync(); err != nil {
|
|
logger.Sugar().Fatalw("error syncing logger",
|
|
"error", err,
|
|
)
|
|
}
|
|
}()
|
|
sugar := logger.Sugar()
|
|
|
|
common.StartTime()
|
|
|
|
err := godotenv.Load()
|
|
if err != nil {
|
|
sugar.Fatal("Error loading .env")
|
|
}
|
|
|
|
// Twitch config variables
|
|
cfg.twitchUsername = os.Getenv("TWITCH_USERNAME")
|
|
cfg.twitchOauth = os.Getenv("TWITCH_OAUTH")
|
|
cfg.twitchClientId = os.Getenv("TWITCH_CLIENT_ID")
|
|
cfg.twitchClientSecret = os.Getenv("TWITCH_CLIENT_SECRET")
|
|
cfg.wolframAlphaAppID = os.Getenv("WOLFRAMALPHA_APP_ID")
|
|
cfg.twitchID = os.Getenv("TWITCH_ID")
|
|
cfg.env = os.Getenv("ENV")
|
|
tc := twitch.NewClient(cfg.twitchUsername, cfg.twitchOauth)
|
|
|
|
switch cfg.env {
|
|
case "dev":
|
|
cfg.db.dsn = os.Getenv("DEV_DSN")
|
|
cfg.commandPrefix = "!!"
|
|
case "prod":
|
|
cfg.db.dsn = os.Getenv("PROD_DSN")
|
|
cfg.commandPrefix = "()"
|
|
}
|
|
// Database config variables
|
|
cfg.db.maxOpenConns = 25
|
|
cfg.db.maxIdleConns = 25
|
|
cfg.db.maxIdleTime = "15m"
|
|
|
|
// Initialize a new Helix Client, request a new AppAccessToken
|
|
// and set the token on the client.
|
|
helixClient, err := helix.NewClient(&helix.Options{
|
|
ClientID: cfg.twitchClientId,
|
|
ClientSecret: cfg.twitchClientSecret,
|
|
})
|
|
if err != nil {
|
|
sugar.Fatalw("Error creating new helix client",
|
|
"helixClient", helixClient,
|
|
"err", err,
|
|
)
|
|
}
|
|
|
|
helixResp, err := helixClient.RequestAppAccessToken([]string{"user:read:email"})
|
|
if err != nil {
|
|
sugar.Fatalw("Helix: Error getting new helix AppAcessToken",
|
|
"resp", helixResp,
|
|
"err", err,
|
|
)
|
|
}
|
|
|
|
// Set the access token on the client
|
|
helixClient.SetAppAccessToken(helixResp.Data.AccessToken)
|
|
|
|
// Establish database connection
|
|
db, err := openDB(cfg)
|
|
if err != nil {
|
|
sugar.Fatalw("could not establish database connection",
|
|
"err", err,
|
|
)
|
|
}
|
|
app := &application{
|
|
TwitchClient: tc,
|
|
HelixClient: helixClient,
|
|
Log: sugar,
|
|
Db: db,
|
|
Models: data.NewModels(db),
|
|
Scheduler: cron.New(),
|
|
Config: cfg,
|
|
Environment: cfg.env,
|
|
}
|
|
|
|
app.Log.Infow("db.Stats",
|
|
"db.Stats", db.Stats(),
|
|
)
|
|
|
|
// Received a PrivateMessage (normal chat message).
|
|
app.TwitchClient.OnPrivateMessage(func(message twitch.PrivateMessage) {
|
|
// sugar.Infow("New Twitch PrivateMessage",
|
|
// "message.Channel", message.Channel,
|
|
// "message.User.DisplayName", message.User.DisplayName,
|
|
// "message.User.ID", message.User.ID,
|
|
// "message.Message", message.Message,
|
|
// )
|
|
|
|
// roomId is the Twitch UserID of the channel the message originated from.
|
|
// If there is no roomId something went really wrong.
|
|
roomId := message.Tags["room-id"]
|
|
if roomId == "" {
|
|
return
|
|
}
|
|
|
|
// Message was shorter than our prefix is therefore it's irrelevant for us.
|
|
if len(message.Message) >= 2 {
|
|
// This bots prefix is "()" configured above at cfg.commandPrefix,
|
|
// Check if the first 2 characters of the mesage were our prefix.
|
|
// if they were forward the message to the command handler.
|
|
if message.Message[:2] == cfg.commandPrefix {
|
|
go app.handleCommand(message)
|
|
return
|
|
}
|
|
|
|
// Special rule for #pajlada.
|
|
if message.Message == "!nourybot" {
|
|
app.Send(message.Channel, "Lidl Twitch bot made by @nouryxd. Prefix: ()", message)
|
|
}
|
|
}
|
|
})
|
|
|
|
app.TwitchClient.OnClearChatMessage(func(message twitch.ClearChatMessage) {
|
|
if message.BanDuration == 0 && message.Channel == "forsen" {
|
|
app.TwitchClient.Say("nouryxd", fmt.Sprintf("MODS https://logs.ivr.fi/?channel=forsen&username=%v", message.TargetUsername))
|
|
}
|
|
|
|
if message.BanDuration >= 28700 && message.Channel == "forsen" {
|
|
app.TwitchClient.Say("nouryxd", fmt.Sprintf("monkaS -%v https://logs.ivr.fi/?channel=forsen&username=%v", message.BanDuration, message.TargetUsername))
|
|
}
|
|
|
|
})
|
|
|
|
app.InitialJoin()
|
|
// Load the initial timers from the database.
|
|
app.InitialTimers()
|
|
|
|
// Start the timers.
|
|
app.Scheduler.Start()
|
|
|
|
app.TwitchClient.OnConnect(func() {
|
|
app.TwitchClient.Say("nouryxd", "gopherDance")
|
|
app.TwitchClient.Say("nourybot", "gopherDance")
|
|
app.TwitchClient.Say("xnoury", "alienPls")
|
|
app.TwitchClient.Say("uudelleenkykeytynyt", "pajaDink")
|
|
|
|
// Successfully connected to Twitch
|
|
app.Log.Infow("Successfully connected to Twitch Servers",
|
|
"Bot username", cfg.twitchUsername,
|
|
"Environment", cfg.env,
|
|
"Database Open Conns", cfg.db.maxOpenConns,
|
|
"Database Idle Conns", cfg.db.maxIdleConns,
|
|
"Database Idle Time", cfg.db.maxIdleTime,
|
|
"Database", db.Stats(),
|
|
"Helix", helixResp,
|
|
)
|
|
|
|
})
|
|
|
|
// Start status page
|
|
go app.startRouter()
|
|
|
|
// Actually connect to chat.
|
|
err = app.TwitchClient.Connect()
|
|
if err != nil {
|
|
panic(err)
|
|
}
|
|
|
|
}
|
|
|
|
// openDB returns the sql.DB connection pool.
|
|
func openDB(cfg config) (*sql.DB, error) {
|
|
// sql.Open() creates an empty connection pool with the provided DSN
|
|
db, err := sql.Open("postgres", cfg.db.dsn)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
// Set database restraints.
|
|
db.SetMaxOpenConns(cfg.db.maxOpenConns)
|
|
db.SetMaxIdleConns(cfg.db.maxIdleConns)
|
|
|
|
// Parse the maxIdleTime string into an actual duration and set it.
|
|
duration, err := time.ParseDuration(cfg.db.maxIdleTime)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
db.SetConnMaxIdleTime(duration)
|
|
|
|
// Create a new context with a 5 second timeout.
|
|
ctx, cancel := context.WithTimeout(context.Background(), 5*time.Second)
|
|
defer cancel()
|
|
|
|
// db.PingContext() is needed to actually check if the
|
|
// connection to the database was successful.
|
|
err = db.PingContext(ctx)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
return db, nil
|
|
}
|