From 39288bfd1c7968e346df49e2c51cacdd45394f53 Mon Sep 17 00:00:00 2001 From: lyx0 <66651385+lyx0@users.noreply.github.com> Date: Thu, 13 Apr 2023 03:26:20 +0200 Subject: [PATCH] add redis storage for timers --- Makefile | 3 ++ cmd/bot/main.go | 22 +++++++++++++ cmd/bot/timer.go | 68 ++++++++++++++++++++++++++++++++++++----- go.mod | 3 ++ go.sum | 6 ++++ internal/data/timers.go | 11 ++++--- 6 files changed, 100 insertions(+), 13 deletions(-) diff --git a/Makefile b/Makefile index 41ca60c..748bedd 100644 --- a/Makefile +++ b/Makefile @@ -1,6 +1,9 @@ BINARY_NAME=Nourybot.out BINARY_NAME_API=NourybotApi.out +cup: + sudo docker compose up + xd: cd cmd/bot && go build -o ${BINARY_NAME} && ./${BINARY_NAME} -env="dev" diff --git a/cmd/bot/main.go b/cmd/bot/main.go index 5109edb..15e881c 100644 --- a/cmd/bot/main.go +++ b/cmd/bot/main.go @@ -15,6 +15,7 @@ import ( "github.com/lyx0/nourybot/internal/common" "github.com/lyx0/nourybot/internal/data" "github.com/nicklaw5/helix" + "github.com/redis/go-redis/v9" "go.uber.org/zap" ) @@ -39,9 +40,11 @@ type Application struct { Db *sql.DB Models data.Models Scheduler *cron.Cron + Rdb *redis.Client } var envFlag string +var ctx = context.Background() func init() { flag.StringVar(&envFlag, "env", "dev", "database connection to use: (dev/prod)") @@ -115,6 +118,24 @@ func main() { sugar.Fatal(err) } + rdb := redis.NewClient(&redis.Options{ + Addr: "127.0.0.1:6379", + Password: "", + DB: 0, + }) + + err = rdb.Set(ctx, "key", "value", 0).Err() + if err != nil { + sugar.Panic(err) + } + val, err := rdb.Get(ctx, "key").Result() + if err != nil { + sugar.Panic(err) + } + sugar.Infow("Redis initialization key", + "key", val, + ) + // Initialize Application with the new values app := &Application{ TwitchClient: tc, @@ -123,6 +144,7 @@ func main() { Db: db, Models: data.NewModels(db), Scheduler: cron.New(), + Rdb: rdb, } // Received a PrivateMessage (normal chat message). diff --git a/cmd/bot/timer.go b/cmd/bot/timer.go index f61d5be..409569d 100644 --- a/cmd/bot/timer.go +++ b/cmd/bot/timer.go @@ -8,6 +8,7 @@ import ( "github.com/gempir/go-twitch-irc/v3" "github.com/lyx0/nourybot/internal/common" "github.com/lyx0/nourybot/internal/data" + "github.com/redis/go-redis/v9" ) // AddTimer slices the message into relevant parts, adding the values onto a @@ -66,13 +67,28 @@ func (app *Application) AddTimer(name string, message twitch.PrivateMessage) { } else { // cronName is the internal, unique tag/name for the timer. // A timer named "sponsor" in channel "forsen" will be named "forsensponsor" - cronName := fmt.Sprintf("%s%s", message.Channel, name) + cronName := fmt.Sprintf("%s-%s", message.Channel, name) app.Scheduler.AddFunc(fmt.Sprintf("@every %s", repeat), func() { app.newPrivateMessageTimer(message.Channel, text) }, cronName) app.Logger.Infow("Added new timer", "timer", timer, ) + if _, err := app.Rdb.Pipelined(ctx, func(rdb redis.Pipeliner) error { + rdb.HSet(ctx, cronName, "timer-name", name) + rdb.HSet(ctx, cronName, "timer-cronname", cronName) + rdb.HSet(ctx, cronName, "timer-text", text) + rdb.HSet(ctx, cronName, "timer-channel", message.Channel) + rdb.HSet(ctx, cronName, "timer-repeat", repeat) + return nil + }); err != nil { + app.Logger.Panic(err) + } + app.Logger.Infow("Loaded timer into redis:", + "key", cronName, + "value", app.Rdb.HGetAll(ctx, cronName), + ) + reply := fmt.Sprintf("Successfully added timer %s repeating every %s", name, repeat) common.Send(message.Channel, reply, app.TwitchClient) return @@ -104,9 +120,10 @@ func (app *Application) EditTimer(name string, message twitch.PrivateMessage) { } // Delete the old timer - cronName := fmt.Sprintf("%s%s", message.Channel, name) + cronName := fmt.Sprintf("%s-%s", message.Channel, name) app.Scheduler.RemoveJob(cronName) + _ = app.Rdb.Del(ctx, cronName) err = app.Models.Timers.Delete(name) if err != nil { app.Logger.Errorw("Error deleting timer from database", @@ -174,10 +191,9 @@ func (app *Application) EditTimer(name string, message twitch.PrivateMessage) { } else { // this is a bit scuffed. The else here is the end of a successful call. // cronName is the internal, unique tag/name for the timer. // A timer named "sponsor" in channel "forsen" will be named "forsensponsor" - cronName := fmt.Sprintf("%s%s", message.Channel, name) + cronName := fmt.Sprintf("%s-%s", message.Channel, name) app.Scheduler.AddFunc(fmt.Sprintf("@every %s", repeat), func() { app.newPrivateMessageTimer(message.Channel, text) }, cronName) - app.Logger.Infow("Updated a timer", "Name", name, "Channel", message.Channel, @@ -185,6 +201,21 @@ func (app *Application) EditTimer(name string, message twitch.PrivateMessage) { "New timer", timer, ) + if _, err := app.Rdb.Pipelined(ctx, func(rdb redis.Pipeliner) error { + rdb.HSet(ctx, cronName, "timer-name", name) + rdb.HSet(ctx, cronName, "timer-cronname", cronName) + rdb.HSet(ctx, cronName, "timer-text", text) + rdb.HSet(ctx, cronName, "timer-channel", message.Channel) + rdb.HSet(ctx, cronName, "timer-repeat", repeat) + return nil + }); err != nil { + app.Logger.Panic(err) + } + app.Logger.Infow("Loaded timer into redis:", + "key", cronName, + "value", app.Rdb.HGetAll(ctx, cronName), + ) + reply := fmt.Sprintf("Successfully updated timer %s", name) common.Send(message.Channel, reply, app.TwitchClient) return @@ -222,18 +253,38 @@ func (app *Application) InitialTimers() { // cronName is the internal, unique tag/name for the timer. // A timer named "sponsor" in channel "forsen" will be named "forsensponsor" - cronName := fmt.Sprintf("%s%s", v.Channel, v.Name) + cronName := fmt.Sprintf("%s-%s", v.Channel, v.Name) // Repeating is at what times the timer should repeat. // 2 minute timer is @every 2m repeating := fmt.Sprintf("@every %s", v.Repeat) + if _, err := app.Rdb.Pipelined(ctx, func(rdb redis.Pipeliner) error { + rdb.HSet(ctx, cronName, "timer-id", v.ID) + rdb.HSet(ctx, cronName, "timer-name", v.Name) + rdb.HSet(ctx, cronName, "timer-cronname", cronName) + rdb.HSet(ctx, cronName, "timer-text", v.Text) + rdb.HSet(ctx, cronName, "timer-channel", v.Channel) + rdb.HSet(ctx, cronName, "timer-repeat", v.Repeat) + return nil + }); err != nil { + app.Logger.Panic(err) + } + app.Logger.Infow("Loaded timer into redis:", + "key", cronName, + "value", app.Rdb.HGetAll(ctx, v.Channel), + ) + app.Scheduler.AddFunc(repeating, func() { app.newPrivateMessageTimer(v.Channel, v.Text) }, cronName) + // Add new value to the slice ts = append(ts, v) - - app.Scheduler.AddFunc(repeating, func() { app.newPrivateMessageTimer(v.Channel, v.Text) }, cronName) } + // var model1 rdbVal + // if err := app.Rdb.HGetAll(ctx, cronName).Scan(&model1); err != nil { + // app.Logger.Panic(err) + // } + app.Logger.Infow("Initial timers", "timer", ts, ) @@ -247,7 +298,7 @@ func (app *Application) newPrivateMessageTimer(channel, text string) { // DeleteTimer takes in the name of a timer and tries to delete the timer from the database. func (app *Application) DeleteTimer(name string, message twitch.PrivateMessage) { - cronName := fmt.Sprintf("%s%s", message.Channel, name) + cronName := fmt.Sprintf("%s-%s", message.Channel, name) app.Scheduler.RemoveJob(cronName) app.Logger.Infow("Deleting timer", @@ -256,6 +307,7 @@ func (app *Application) DeleteTimer(name string, message twitch.PrivateMessage) "cronName", cronName, ) + _ = app.Rdb.Del(ctx, cronName) err := app.Models.Timers.Delete(name) if err != nil { app.Logger.Errorw("Error deleting timer from database", diff --git a/go.mod b/go.mod index 5fbc8e3..65de3e3 100644 --- a/go.mod +++ b/go.mod @@ -15,7 +15,10 @@ require ( ) require ( + github.com/cespare/xxhash/v2 v2.2.0 // indirect + github.com/dgryski/go-rendezvous v0.0.0-20200823014737-9f7001d12a5f // indirect github.com/golang-jwt/jwt v3.2.1+incompatible // indirect + github.com/redis/go-redis/v9 v9.0.3 // indirect github.com/shkh/lastfm-go v0.0.0-20191215035245-89a801c244e0 // indirect go.uber.org/atomic v1.9.0 // indirect go.uber.org/multierr v1.8.0 // indirect diff --git a/go.sum b/go.sum index 55a91ec..b421469 100644 --- a/go.sum +++ b/go.sum @@ -2,9 +2,13 @@ github.com/benbjohnson/clock v1.1.0 h1:Q92kusRqC1XV2MjkWETPvjJVqKetz1OzxZB7mHJLj github.com/benbjohnson/clock v1.1.0/go.mod h1:J11/hYXuz8f4ySSvYwY0FKfm+ezbsZBKZxNJlLklBHA= github.com/briandowns/openweathermap v0.18.0 h1:JYTtJ4bKjXZRmDTe7huJ5+dZ7CsjPUw10GUzMASkNV8= github.com/briandowns/openweathermap v0.18.0/go.mod h1:0GLnknqicWxXnGi1IqoOaZIw+kIe5hkt+YM5WY3j8+0= +github.com/cespare/xxhash/v2 v2.2.0 h1:DC2CZ1Ep5Y4k3ZQ899DldepgrayRUGE6BBZ/cd9Cj44= +github.com/cespare/xxhash/v2 v2.2.0/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs= github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c= github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= +github.com/dgryski/go-rendezvous v0.0.0-20200823014737-9f7001d12a5f h1:lO4WD4F/rVNCu3HqELle0jiPLLBs70cWOduZpkS1E78= +github.com/dgryski/go-rendezvous v0.0.0-20200823014737-9f7001d12a5f/go.mod h1:cuUVRXasLTGF7a8hSLbxyZXjz+1KgoB3wDUb6vlszIc= github.com/dustin/go-humanize v1.0.0 h1:VSnTsYCnlFHaM2/igO1h6X3HA71jcobQuxemgkq4zYo= github.com/dustin/go-humanize v1.0.0/go.mod h1:HtrtbFcZ19U5GC7JDqmcUSB87Iq5E25KnS6fMYU6eOk= github.com/gempir/go-twitch-irc/v3 v3.2.0 h1:ENhsa7RgBE1GMmDqe0iMkvcSYfgw6ZsXilt+sAg32/U= @@ -28,6 +32,8 @@ github.com/pkg/errors v0.8.1 h1:iURUrRGxPUNPdy5/HRSm+Yj6okJ6UtLINN0Q9M4+h3I= github.com/pkg/errors v0.8.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= +github.com/redis/go-redis/v9 v9.0.3 h1:+7mmR26M0IvyLxGZUHxu4GiBkJkVDid0Un+j4ScYu4k= +github.com/redis/go-redis/v9 v9.0.3/go.mod h1:WqMKv5vnQbRuZstUwxQI195wHy+t4PuXDOjzMvcuQHk= github.com/shkh/lastfm-go v0.0.0-20191215035245-89a801c244e0 h1:cgqwZtnR+IQfUYDLJ3Kiy4aE+O/wExTzEIg8xwC4Qfs= github.com/shkh/lastfm-go v0.0.0-20191215035245-89a801c244e0/go.mod h1:n3nudMl178cEvD44PaopxH9jhJaQzthSxUzLO5iKMy4= github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= diff --git a/internal/data/timers.go b/internal/data/timers.go index bc40056..a13a51b 100644 --- a/internal/data/timers.go +++ b/internal/data/timers.go @@ -8,11 +8,12 @@ import ( ) type Timer struct { - ID int `json:"id"` - Name string `json:"name"` - Text string `json:"text"` - Channel string `json:"channel"` - Repeat string `json:"repeat"` + ID int `json:"id" redis:"timer-id"` + Name string `json:"name" redis:"timer-name"` + CronName string `redis:"timer-cronname"` + Text string `json:"text" redis:"timer-text"` + Channel string `json:"channel" redis:"timer-channel"` + Repeat string `json:"repeat" redis:"timer-repeat"` } type TimerModel struct {