diff --git a/cmd/nourybot/command.go b/cmd/nourybot/command.go index 58fbb7e..56900aa 100644 --- a/cmd/nourybot/command.go +++ b/cmd/nourybot/command.go @@ -3,6 +3,7 @@ package main import ( "fmt" "strconv" + "strings" "github.com/gempir/go-twitch-irc/v4" "github.com/google/uuid" @@ -17,7 +18,7 @@ func (app *application) AddCommand(name string, message twitch.PrivateMessage) { // trailing space = +1 // zero-based = +1 // = 15 - snipLength := 14 + snipLength := 15 // Split the twitch message at `snipLength` plus length of the name of the // command that we want to add. @@ -246,3 +247,51 @@ func (app *application) LogCommand(msg twitch.PrivateMessage, commandName string go app.Models.CommandsLogs.Insert(twitchLogin, twitchID, twitchChannel, twitchMessage, commandName, userLevel, identifier, rawMsg) } + +// InitialTimers is called on startup and queries the database for a list of +// timers and then adds each onto the scheduler. +func (app *application) ListCommands() string { + command, err := app.Models.Commands.GetAll() + if err != nil { + app.Log.Errorw("Error trying to retrieve all timers from database", err) + return "" + } + + // The slice of timers is only used to log them at + // the start so it looks a bit nicer. + var cs []string + + // Iterate over all timers and then add them onto the scheduler. + for i, v := range command { + // idk why this works but it does so no touchy touchy. + // https://github.com/robfig/cron/issues/420#issuecomment-940949195 + i, v := i, v + _ = i + + // cronName is the internal, unique tag/name for the timer. + // A timer named "sponsor" in channel "forsen" will be named "forsensponsor" + c := fmt.Sprintf( + "ID: \t\t%v\n"+ + "Name: \t\t%v\n"+ + "Text: \t\t%v\n"+ + "Category: \t%v\n"+ + "Level: \t\t%v\n"+ + "Help: \t\t%v\n"+ + "\n\n", + v.ID, v.Name, v.Text, v.Category, v.Level, v.Help, + ) + + // Add new value to the slice + cs = append(cs, c) + + //app.Scheduler.AddFunc(repeating, func() { app.newPrivateMessageTimer(v.Channel, v.Text) }, cronName) + } + + reply, err := app.uploadPaste(strings.Join(cs, "")) + if err != nil { + app.Log.Errorw("Error trying to retrieve all timers from database", err) + return "" + } + + return reply +} diff --git a/cmd/nourybot/commands.go b/cmd/nourybot/commands.go index 57c94e9..47c7a23 100644 --- a/cmd/nourybot/commands.go +++ b/cmd/nourybot/commands.go @@ -266,6 +266,10 @@ func (app *application) handleCommand(message twitch.PrivateMessage) { if userLevel >= 500 { app.DeleteCommand(cmdParams[2], message) } + case "list": + if userLevel >= 500 { + reply = app.ListCommands() + } case "edit": switch cmdParams[2] { case "level": diff --git a/internal/data/commands.go b/internal/data/commands.go index 11f8f07..67a4315 100644 --- a/internal/data/commands.go +++ b/internal/data/commands.go @@ -1,8 +1,10 @@ package data import ( + "context" "database/sql" "errors" + "time" ) type Command struct { @@ -203,3 +205,58 @@ func (c CommandModel) Delete(name string) error { return nil } + +// GetAll() returns a pointer to a slice of all channels (`[]*Channel`) in the database. +func (c CommandModel) GetAll() ([]*Command, error) { + query := ` + SELECT id, name, text, category, level, help + FROM commands + ORDER BY id` + + // Create a context with 3 seconds timeout. + ctx, cancel := context.WithTimeout(context.Background(), 3*time.Second) + defer cancel() + + // Use QueryContext() the context and query. This returns a + // sql.Rows resultset containing our channels. + rows, err := c.DB.QueryContext(ctx, query) + if err != nil { + return nil, err + } + + // Need to defer a call to rows.Close() to ensure the resultset + // is closed before GetAll() returns. + defer rows.Close() + + // Initialize an empty slice to hold the data. + commands := []*Command{} + + // Iterate over the resultset. + for rows.Next() { + // Initialize an empty Channel struct where we put on + // a single channel value. + var command Command + + // Scan the values onto the channel struct + err := rows.Scan( + &command.ID, + &command.Name, + &command.Text, + &command.Category, + &command.Level, + &command.Help, + ) + if err != nil { + return nil, err + } + // Add the single movie struct onto the slice. + commands = append(commands, &command) + } + + // When rows.Next() finishes call rows.Err() to retrieve any errors. + if err = rows.Err(); err != nil { + return nil, err + } + + return commands, nil +} diff --git a/internal/data/models.go b/internal/data/models.go index 73e882c..6e030f2 100644 --- a/internal/data/models.go +++ b/internal/data/models.go @@ -38,6 +38,7 @@ type Models struct { } Commands interface { Get(name string) (*Command, error) + GetAll() ([]*Command, error) Insert(command *Command) error Update(command *Command) error SetLevel(name string, level int) error