diff --git a/cmd/nourybot/timer.go b/cmd/nourybot/timer.go index fbc3b39..f1678c1 100644 --- a/cmd/nourybot/timer.go +++ b/cmd/nourybot/timer.go @@ -6,6 +6,7 @@ import ( "strings" "github.com/gempir/go-twitch-irc/v4" + "github.com/google/uuid" "github.com/lyx0/nourybot/internal/data" ) @@ -42,12 +43,13 @@ func (app *application) AddTimer(name, repeat string, message twitch.PrivateMess ) return } - + id := uuid.NewString() timer := &data.Timer{ - Name: name, - Text: text, - Channel: message.Channel, - Repeat: repeat, + Name: name, + Text: text, + Identifier: id, + Channel: message.Channel, + Repeat: repeat, } // Check if the time string we got is valid, this is important @@ -55,10 +57,11 @@ func (app *application) AddTimer(name, repeat string, message twitch.PrivateMess // time format string is supplied. if validateTimeFormat { timer := &data.Timer{ - Name: name, - Text: text, - Channel: message.Channel, - Repeat: repeat, + Name: name, + Text: text, + Identifier: id, + Channel: message.Channel, + Repeat: repeat, } err = app.Models.Timers.Insert(timer) @@ -74,9 +77,8 @@ func (app *application) AddTimer(name, repeat string, message twitch.PrivateMess } 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) - app.Scheduler.AddFunc(fmt.Sprintf("@every %s", repeat), func() { app.newPrivateMessageTimer(message.Channel, text) }, cronName) + app.Scheduler.AddFunc(fmt.Sprintf("@every %s", repeat), func() { app.newPrivateMessageTimer(message.Channel, text) }, id) app.Log.Infow("Added new timer", "timer", timer, ) @@ -116,14 +118,14 @@ func (app *application) EditTimer(name, repeat string, message twitch.PrivateMes // ----------------------- // Delete the old timer // ----------------------- - cronName := fmt.Sprintf("%s%s", message.Channel, name) - app.Scheduler.RemoveJob(cronName) + identifier := old.Identifier + app.Scheduler.RemoveJob(identifier) err = app.Models.Timers.Delete(name) if err != nil { app.Log.Errorw("Error deleting timer from database", "name", name, - "cronName", cronName, + "identifier", identifier, "error", err, ) @@ -165,11 +167,13 @@ func (app *application) EditTimer(name, repeat string, message twitch.PrivateMes return } + id := uuid.NewString() timer := &data.Timer{ - Name: name, - Text: text, - Channel: message.Channel, - Repeat: repeat, + Name: name, + Text: text, + Identifier: id, + Channel: message.Channel, + Repeat: repeat, } // Check if the time string we got is valid, this is important @@ -177,10 +181,11 @@ func (app *application) EditTimer(name, repeat string, message twitch.PrivateMes // time format string is supplied. if validateTimeFormat { timer := &data.Timer{ - Name: name, - Text: text, - Channel: message.Channel, - Repeat: repeat, + Name: name, + Text: text, + Identifier: id, + Channel: message.Channel, + Repeat: repeat, } err = app.Models.Timers.Insert(timer) @@ -196,9 +201,7 @@ func (app *application) EditTimer(name, repeat string, message twitch.PrivateMes } 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) - - app.Scheduler.AddFunc(fmt.Sprintf("@every %s", repeat), func() { app.newPrivateMessageTimer(message.Channel, text) }, cronName) + app.Scheduler.AddFunc(fmt.Sprintf("@every %s", repeat), func() { app.newPrivateMessageTimer(message.Channel, text) }, id) app.Log.Infow("Updated a timer", "Name", name, @@ -247,12 +250,12 @@ func (app *application) ListTimers() string { t := fmt.Sprintf( "ID: \t\t%v\n"+ "Name: \t\t%v\n"+ - "CronName: \t\t%v\n"+ + "Identifier: \t%v\n"+ "Text: \t\t%v\n"+ "Channel: \t%v\n"+ "Repeat: \t%v\n"+ "\n\n", - v.ID, v.Name, v.CronName, v.Text, v.Channel, v.Repeat, + v.ID, v.Name, v.Identifier, v.Text, v.Channel, v.Repeat, ) // Add new value to the slice @@ -292,7 +295,6 @@ 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) // Repeating is at what times the timer should repeat. // 2 minute timer is @every 2m @@ -301,7 +303,7 @@ func (app *application) InitialTimers() { // Add new value to the slice ts = append(ts, v) - app.Scheduler.AddFunc(repeating, func() { app.newPrivateMessageTimer(v.Channel, v.Text) }, cronName) + app.Scheduler.AddFunc(repeating, func() { app.newPrivateMessageTimer(v.Channel, v.Text) }, v.Identifier) } app.Log.Infow("Initial timers", @@ -317,20 +319,29 @@ 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) - app.Scheduler.RemoveJob(cronName) + + identifier, err := app.Models.Timers.GetIdentifier(name) + if err != nil { + app.Log.Errorw("Error retrieving identifier rom database", + "name", name, + "identifier", identifier, + "error", err, + ) + } + + app.Scheduler.RemoveJob(identifier) app.Log.Infow("Deleting timer", "name", name, + "identifier", identifier, "message.Channel", message.Channel, - "cronName", cronName, ) - err := app.Models.Timers.Delete(name) + err = app.Models.Timers.Delete(identifier) if err != nil { app.Log.Errorw("Error deleting timer from database", "name", name, - "cronName", cronName, + "identifier", identifier, "error", err, ) diff --git a/internal/data/models.go b/internal/data/models.go index f1a6065..b48f0c7 100644 --- a/internal/data/models.go +++ b/internal/data/models.go @@ -47,6 +47,7 @@ type Models struct { } Timers interface { Get(name string) (*Timer, error) + GetIdentifier(name string) (string, error) Insert(timer *Timer) error Update(timer *Timer) error GetAll() ([]*Timer, error) diff --git a/internal/data/timers.go b/internal/data/timers.go index a13a51b..63830ff 100644 --- a/internal/data/timers.go +++ b/internal/data/timers.go @@ -8,12 +8,12 @@ import ( ) type Timer struct { - 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"` + ID int `json:"id" redis:"timer-id"` + Name string `json:"name" redis:"timer-name"` + Identifier string `json:"identifier"` + Text string `json:"text" redis:"timer-text"` + Channel string `json:"channel" redis:"timer-channel"` + Repeat string `json:"repeat" redis:"timer-repeat"` } type TimerModel struct { @@ -22,7 +22,7 @@ type TimerModel struct { func (t TimerModel) Get(name string) (*Timer, error) { query := ` - SELECT id, name, text, channel, repeat + SELECT id, name, identifier, text, channel, repeat FROM timers WHERE name = $1 ` @@ -32,6 +32,7 @@ func (t TimerModel) Get(name string) (*Timer, error) { err := t.DB.QueryRow(query, name).Scan( &timer.ID, &timer.Name, + &timer.Identifier, &timer.Text, &timer.Channel, &timer.Repeat, @@ -48,15 +49,44 @@ func (t TimerModel) Get(name string) (*Timer, error) { return &timer, nil } +func (t TimerModel) GetIdentifier(name string) (string, error) { + query := ` + SELECT id, name, identifier, text, channel, repeat + FROM timers + WHERE name = $1 + ` + + var timer Timer + + err := t.DB.QueryRow(query, name).Scan( + &timer.ID, + &timer.Name, + &timer.Identifier, + &timer.Text, + &timer.Channel, + &timer.Repeat, + ) + if err != nil { + switch { + case errors.Is(err, sql.ErrNoRows): + return "", ErrRecordNotFound + default: + return "", err + } + } + + return timer.Identifier, nil +} + // Insert adds a command into the database. func (t TimerModel) Insert(timer *Timer) error { query := ` - INSERT into timers(name, text, channel, repeat) - VALUES ($1, $2, $3, $4) + INSERT into timers(name, identifier, text, channel, repeat) + VALUES ($1, $2, $3, $4, $5) RETURNING id; ` - args := []interface{}{timer.Name, timer.Text, timer.Channel, timer.Repeat} + args := []interface{}{timer.Name, timer.Identifier, timer.Text, timer.Channel, timer.Repeat} result, err := t.DB.Exec(query, args...) if err != nil { @@ -78,7 +108,7 @@ func (t TimerModel) Insert(timer *Timer) error { // GetAll() returns a pointer to a slice of all channels (`[]*Channel`) in the database. func (t TimerModel) GetAll() ([]*Timer, error) { query := ` - SELECT id, name, text, channel, repeat + SELECT id, name, identifier, text, channel, repeat FROM timers ORDER BY id` @@ -110,6 +140,7 @@ func (t TimerModel) GetAll() ([]*Timer, error) { err := rows.Scan( &timer.ID, &timer.Name, + &timer.Identifier, &timer.Text, &timer.Channel, &timer.Repeat, @@ -158,14 +189,14 @@ func (t TimerModel) Update(timer *Timer) error { // Delete takes in a command name and queries the database for an entry with // the same name and tries to delete that entry. -func (t TimerModel) Delete(name string) error { +func (t TimerModel) Delete(identifier string) error { // Prepare the statement. query := ` DELETE FROM timers - WHERE name = $1` + WHERE identifier = $1` // Execute the query returning the number of affected rows. - result, err := t.DB.Exec(query, name) + result, err := t.DB.Exec(query, identifier) if err != nil { return err } diff --git a/migrations/000004_create_timers_table.up.sql b/migrations/000004_create_timers_table.up.sql index 06e23e7..20ad2d2 100644 --- a/migrations/000004_create_timers_table.up.sql +++ b/migrations/000004_create_timers_table.up.sql @@ -1,17 +1,16 @@ CREATE TABLE IF NOT EXISTS timers ( id bigserial PRIMARY KEY, name text NOT NULL, + identifier text NOT NULL, text text NOT NULL, channel text NOT NULL, repeat text NOT NULL ); -INSERT INTO timers (name,"text",channel,repeat) VALUES - ('nourylul-60m','timer every 60 minutes :)','nourylul','60m'), - ('nourylul-2m','timer every 2 minutes :)','nourylul','2m'), - ('nourybot-60m','timer every 60 minutes :)','nourybot','60m'), - ('nourybot-1h','timer every 1 hour :)','nourybot','1h'), - ('xnoury-60m','timer every 420 minutes :)','xnoury','420m'), - ('xnoury-1h','timer every 1 hour :)','xnoury','1h'), - ('xnoury-15m','180 minutes timer :)','xnoury','180m'); +INSERT INTO timers (name,identifier,"text",channel,repeat) VALUES + ('nourylul-60m','678efbe2-fa2f-4849-8dbc-9ec32e6ffd3b','timer every 60 minutes :)','nourylul','60m'), + ('nourylul-2m','63142f10-1672-4353-8b03-e72f5a4dd566','timer every 2 minutes :)','nourylul','2m'), + ('nourybot-60m','2ad01f96-05d3-444e-9dd6-524d397caa96','timer every 60 minutes :)','nourybot','60m'), + ('nourybot-1h','2353fd22-fef9-4cbd-b01e-bc8804992f4c', 'timer every 1 hour :)','nourybot','1h'), + ('xnoury-15m','6e178e14-36c2-45e1-af59-b5dea4903fee','180 minutes timer :)','xnoury','180m');