2023-09-08 00:18:09 +02:00
|
|
|
package main
|
|
|
|
|
|
|
|
import (
|
|
|
|
"fmt"
|
|
|
|
"strconv"
|
2023-12-03 17:24:42 +01:00
|
|
|
"strings"
|
2023-09-08 00:18:09 +02:00
|
|
|
|
|
|
|
"github.com/gempir/go-twitch-irc/v4"
|
2023-10-10 16:07:35 +02:00
|
|
|
"github.com/google/uuid"
|
2023-09-08 00:18:09 +02:00
|
|
|
"github.com/lyx0/nourybot/internal/data"
|
|
|
|
)
|
|
|
|
|
2024-02-28 11:53:39 +01:00
|
|
|
// AddCommand splits a message into two parts and passes on the name and text
|
|
|
|
// to the database handler.
|
2023-09-08 00:18:09 +02:00
|
|
|
func (app *application) AddCommand(name string, message twitch.PrivateMessage) {
|
|
|
|
// snipLength is the length we need to "snip" off of the start of `message`.
|
|
|
|
// `()add command` = +12
|
|
|
|
// trailing space = +1
|
|
|
|
// zero-based = +1
|
|
|
|
// = 15
|
2023-12-03 17:24:42 +01:00
|
|
|
snipLength := 15
|
2023-09-08 00:18:09 +02:00
|
|
|
|
|
|
|
// Split the twitch message at `snipLength` plus length of the name of the
|
2024-02-28 11:53:39 +01:00
|
|
|
// command that we want to add. The part of the message we are left over with
|
|
|
|
// is then passed on to the database handlers as the `text` part of the command.
|
2023-09-08 00:18:09 +02:00
|
|
|
//
|
2024-02-28 11:53:39 +01:00
|
|
|
// e.g. ()addcommand CoolSponsors Check out CoolSponsor.com they are the coolest sponsors!
|
2023-09-08 00:18:09 +02:00
|
|
|
// | <- snipLength + name -> | <--- command text with however many characters ---> |
|
2024-02-28 11:53:39 +01:00
|
|
|
// | <----- 15 + 12 ------> |
|
2023-09-08 00:18:09 +02:00
|
|
|
text := message.Message[snipLength+len(name) : len(message.Message)]
|
|
|
|
command := &data.Command{
|
2023-12-18 00:04:27 +01:00
|
|
|
Name: name,
|
|
|
|
Channel: message.Channel,
|
|
|
|
Text: text,
|
|
|
|
Level: 0,
|
|
|
|
Description: "",
|
2023-09-08 00:18:09 +02:00
|
|
|
}
|
|
|
|
app.Log.Info(command)
|
|
|
|
err := app.Models.Commands.Insert(command)
|
|
|
|
|
|
|
|
if err != nil {
|
|
|
|
reply := fmt.Sprintf("Something went wrong FeelsBadMan %s", err)
|
2023-10-10 17:51:12 +02:00
|
|
|
app.Send(message.Channel, reply, message)
|
2023-09-08 00:18:09 +02:00
|
|
|
return
|
|
|
|
} else {
|
|
|
|
reply := fmt.Sprintf("Successfully added command: %s", name)
|
2023-10-10 17:51:12 +02:00
|
|
|
app.Send(message.Channel, reply, message)
|
2023-09-08 00:18:09 +02:00
|
|
|
return
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2024-02-28 11:53:39 +01:00
|
|
|
// GetCommand queries the database for a command with the provided name. If an entry exists
|
|
|
|
// it checks if the Command.Level is 0, if it is the command.Text value is returned.
|
2023-09-08 00:18:09 +02:00
|
|
|
//
|
2024-02-28 11:53:39 +01:00
|
|
|
// If the Command.Level is not 0 it queries the database for the level of the user who sent
|
|
|
|
// the message. If the users level is equal or higher the command.Text field is returned.
|
2023-10-10 11:58:42 +02:00
|
|
|
func (app *application) GetCommand(target, commandName string, userLevel int) (string, error) {
|
2023-12-10 23:50:59 +01:00
|
|
|
app.Log.Infow("command",
|
|
|
|
"target", target,
|
|
|
|
"commandname", commandName,
|
|
|
|
)
|
2023-09-08 00:18:09 +02:00
|
|
|
// Fetch the command from the database if it exists.
|
2023-12-10 23:50:59 +01:00
|
|
|
command, err := app.Models.Commands.Get(commandName, target)
|
2023-09-08 00:18:09 +02:00
|
|
|
if err != nil {
|
|
|
|
// It probably did not exist
|
|
|
|
return "", err
|
|
|
|
}
|
|
|
|
|
2023-12-10 23:50:59 +01:00
|
|
|
app.Log.Info("command", command)
|
|
|
|
|
2023-09-08 00:18:09 +02:00
|
|
|
if command.Level == 0 {
|
|
|
|
return command.Text, nil
|
2023-10-10 11:58:42 +02:00
|
|
|
} else if userLevel >= command.Level {
|
2023-12-18 00:04:27 +01:00
|
|
|
// Userlevel is sufficient so return the command.Text
|
|
|
|
return command.Text, nil
|
2023-10-10 11:58:42 +02:00
|
|
|
}
|
2023-09-08 00:18:09 +02:00
|
|
|
// Userlevel was not enough so return an empty string and error.
|
|
|
|
return "", ErrUserInsufficientLevel
|
|
|
|
}
|
|
|
|
|
2024-02-28 11:53:39 +01:00
|
|
|
// GetCommandDescription queries the database for a command with the provided name in the channel.
|
|
|
|
// If a command exist it then checks if the Command.Level is 0, if it is the command.Text value is returned.
|
2023-09-08 00:18:09 +02:00
|
|
|
//
|
2024-02-28 11:53:39 +01:00
|
|
|
// If the Command.Level is not 0 it queries the database for the level of the user who sent
|
|
|
|
// the message. If the users level is equal or higher the command.Text field is returned.
|
2023-12-18 00:04:27 +01:00
|
|
|
func (app *application) GetCommandDescription(name, channel, username string) (string, error) {
|
2023-12-10 23:50:59 +01:00
|
|
|
command, err := app.Models.Commands.Get(name, channel)
|
2023-09-08 00:18:09 +02:00
|
|
|
if err != nil {
|
|
|
|
// It probably did not exist
|
|
|
|
return "", err
|
|
|
|
}
|
|
|
|
|
|
|
|
// If the command has no level set just return the text.
|
|
|
|
// Otherwise check if the level is high enough.
|
|
|
|
if command.Level == 0 {
|
2023-12-18 00:04:27 +01:00
|
|
|
return command.Description, nil
|
2023-09-08 00:18:09 +02:00
|
|
|
} else {
|
2024-02-28 11:53:39 +01:00
|
|
|
// Get the user from the database to check if the userlevel is sufficient.
|
2023-09-08 00:18:09 +02:00
|
|
|
user, err := app.Models.Users.Get(username)
|
|
|
|
if err != nil {
|
|
|
|
return "", err
|
|
|
|
}
|
|
|
|
if user.Level >= command.Level {
|
|
|
|
// Userlevel is sufficient so return the command.Text
|
2023-12-18 00:04:27 +01:00
|
|
|
return command.Description, nil
|
2023-09-08 00:18:09 +02:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// Userlevel was not enough so return an empty string and error.
|
|
|
|
return "", ErrUserInsufficientLevel
|
|
|
|
}
|
|
|
|
|
2024-02-28 11:53:39 +01:00
|
|
|
// EditCommandLevel checks if a command with the provided name exists in the database. If it does it
|
|
|
|
// changes the level of the command with the supplied value.
|
2023-09-08 00:18:09 +02:00
|
|
|
func (app *application) EditCommandLevel(name, lvl string, message twitch.PrivateMessage) {
|
|
|
|
level, err := strconv.Atoi(lvl)
|
|
|
|
if err != nil {
|
|
|
|
app.Log.Error(err)
|
2023-10-10 17:51:12 +02:00
|
|
|
app.Send(message.Channel, fmt.Sprintf("Something went wrong FeelsBadMan %s", ErrCommandLevelNotInteger), message)
|
2023-09-08 00:18:09 +02:00
|
|
|
return
|
|
|
|
}
|
|
|
|
|
2023-12-10 23:50:59 +01:00
|
|
|
err = app.Models.Commands.SetLevel(name, message.Channel, level)
|
2023-09-08 00:18:09 +02:00
|
|
|
|
|
|
|
if err != nil {
|
2023-10-10 17:51:12 +02:00
|
|
|
app.Send(message.Channel, fmt.Sprintf("Something went wrong FeelsBadMan %s", ErrRecordNotFound), message)
|
2023-09-08 00:18:09 +02:00
|
|
|
app.Log.Error(err)
|
|
|
|
return
|
|
|
|
} else {
|
|
|
|
reply := fmt.Sprintf("Updated command %s to level %v", name, level)
|
2023-10-10 17:51:12 +02:00
|
|
|
app.Send(message.Channel, reply, message)
|
2023-09-08 00:18:09 +02:00
|
|
|
return
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2024-02-28 11:53:39 +01:00
|
|
|
// DebugCommand checks if a command with the provided name exists in the database and if it
|
|
|
|
// does it creates a new paste with the commands information.
|
2023-09-08 00:18:09 +02:00
|
|
|
func (app *application) DebugCommand(name string, message twitch.PrivateMessage) {
|
2023-12-10 23:50:59 +01:00
|
|
|
cmd, err := app.Models.Commands.Get(name, message.Channel)
|
2023-09-08 00:18:09 +02:00
|
|
|
if err != nil {
|
|
|
|
reply := fmt.Sprintf("Something went wrong FeelsBadMan %s", err)
|
2023-10-10 17:51:12 +02:00
|
|
|
app.Send(message.Channel, reply, message)
|
2023-09-08 00:18:09 +02:00
|
|
|
return
|
|
|
|
} else {
|
2023-12-18 00:04:27 +01:00
|
|
|
reply := fmt.Sprintf("id=%v\nname=%v\nchannel=%v\nlevel=%v\ntext=%v\ndescription=%v\n",
|
2023-09-08 00:18:09 +02:00
|
|
|
cmd.ID,
|
|
|
|
cmd.Name,
|
2023-12-10 23:50:59 +01:00
|
|
|
cmd.Channel,
|
2023-09-08 00:18:09 +02:00
|
|
|
cmd.Level,
|
|
|
|
cmd.Text,
|
2023-12-18 00:04:27 +01:00
|
|
|
cmd.Description,
|
2023-09-08 00:18:09 +02:00
|
|
|
)
|
|
|
|
|
2023-09-16 17:18:14 +02:00
|
|
|
resp, err := app.uploadPaste(reply)
|
|
|
|
if err != nil {
|
|
|
|
app.Log.Errorln("Could not upload paste:", err)
|
2023-10-10 17:51:12 +02:00
|
|
|
app.Send(message.Channel, fmt.Sprintf("Something went wrong FeelsBadMan %v", ErrDuringPasteUpload), message)
|
2023-09-16 17:18:14 +02:00
|
|
|
return
|
|
|
|
}
|
2023-10-10 17:51:12 +02:00
|
|
|
app.Send(message.Channel, resp, message)
|
2023-09-08 00:18:09 +02:00
|
|
|
return
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// SetCommandHelp updates the `help` column of a given commands name in the
|
|
|
|
// database to the provided new help text.
|
|
|
|
func (app *application) EditCommandHelp(name string, message twitch.PrivateMessage) {
|
|
|
|
// snipLength is the length we need to "snip" off of the start of `message`.
|
|
|
|
// `()edit command` = +13
|
|
|
|
// trailing space = +1
|
|
|
|
// zero-based = +1
|
|
|
|
// `help` = +4
|
|
|
|
// = 19
|
|
|
|
snipLength := 19
|
|
|
|
|
|
|
|
// Split the twitch message at `snipLength` plus length of the name of the
|
|
|
|
// command that we want to set the help text for so that we get the
|
|
|
|
// actual help message left over and then assign this string to `text`.
|
|
|
|
//
|
|
|
|
// e.g. `()editcommand help FeelsDankMan Returns a FeelsDankMan ascii art. Requires user level 500.`
|
|
|
|
// | <---- snipLength + name ----> | <------ help text with however many characters. ----> |
|
|
|
|
// | <--------- 19 + 12 --------> |
|
|
|
|
text := message.Message[snipLength+len(name) : len(message.Message)]
|
2023-12-18 00:04:27 +01:00
|
|
|
err := app.Models.Commands.SetDescription(name, message.Channel, text)
|
2023-09-08 00:18:09 +02:00
|
|
|
|
|
|
|
if err != nil {
|
2023-10-10 17:51:12 +02:00
|
|
|
app.Send(message.Channel, fmt.Sprintf("Something went wrong FeelsBadMan %s", ErrRecordNotFound), message)
|
2023-09-08 00:18:09 +02:00
|
|
|
app.Log.Error(err)
|
|
|
|
return
|
|
|
|
} else {
|
|
|
|
reply := fmt.Sprintf("Updated help text for command %s to: %v", name, text)
|
2023-10-10 17:51:12 +02:00
|
|
|
app.Send(message.Channel, reply, message)
|
2023-09-08 00:18:09 +02:00
|
|
|
return
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2024-02-28 11:53:39 +01:00
|
|
|
// DeleteCommand takes in a name of a command and deletes the command from the database if it exists.
|
2023-09-08 00:18:09 +02:00
|
|
|
func (app *application) DeleteCommand(name string, message twitch.PrivateMessage) {
|
2023-12-10 23:50:59 +01:00
|
|
|
err := app.Models.Commands.Delete(name, message.Channel)
|
2023-09-08 00:18:09 +02:00
|
|
|
if err != nil {
|
2023-10-10 17:51:12 +02:00
|
|
|
app.Send(message.Channel, "Something went wrong FeelsBadMan", message)
|
2023-09-08 00:18:09 +02:00
|
|
|
app.Log.Error(err)
|
|
|
|
return
|
|
|
|
}
|
|
|
|
|
|
|
|
reply := fmt.Sprintf("Deleted command %s", name)
|
2023-10-10 17:51:12 +02:00
|
|
|
app.Send(message.Channel, reply, message)
|
2023-09-08 00:18:09 +02:00
|
|
|
}
|
2023-10-10 16:07:35 +02:00
|
|
|
|
2024-02-28 11:53:39 +01:00
|
|
|
// LogCommand is called when a command is called and logs information about it in the database.
|
2023-10-10 16:07:35 +02:00
|
|
|
func (app *application) LogCommand(msg twitch.PrivateMessage, commandName string, userLevel int) {
|
|
|
|
twitchLogin := msg.User.Name
|
|
|
|
twitchID := msg.User.ID
|
|
|
|
twitchMessage := msg.Message
|
|
|
|
twitchChannel := msg.Channel
|
|
|
|
identifier := uuid.NewString()
|
|
|
|
rawMsg := msg.Raw
|
|
|
|
|
|
|
|
go app.Models.CommandsLogs.Insert(twitchLogin, twitchID, twitchChannel, twitchMessage, commandName, userLevel, identifier, rawMsg)
|
|
|
|
}
|
2023-12-03 17:24:42 +01:00
|
|
|
|
2024-02-28 11:53:39 +01:00
|
|
|
// ListCommands queries the databse for all commands, and then creates a new paste with them.
|
2023-12-03 17:24:42 +01:00
|
|
|
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"+
|
2023-12-10 23:50:59 +01:00
|
|
|
"Channel: \t%v\n"+
|
2023-12-03 17:24:42 +01:00
|
|
|
"Text: \t\t%v\n"+
|
|
|
|
"Level: \t\t%v\n"+
|
2023-12-18 00:04:27 +01:00
|
|
|
"Description: %v\n"+
|
2023-12-03 17:24:42 +01:00
|
|
|
"\n\n",
|
2023-12-18 00:04:27 +01:00
|
|
|
v.ID, v.Name, v.Channel, v.Text, v.Level, v.Description,
|
2023-12-03 17:24:42 +01:00
|
|
|
)
|
|
|
|
|
|
|
|
// 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
|
|
|
|
}
|
2023-12-17 18:48:46 +01:00
|
|
|
|
2024-02-28 11:53:39 +01:00
|
|
|
// ListChannelCommands queries the databse for all commands in a specified twitch channel
|
|
|
|
// and then creates a new paste with them.
|
2023-12-17 18:48:46 +01:00
|
|
|
func (app *application) ListChannelCommands(channel string) string {
|
2024-01-08 16:25:54 +01:00
|
|
|
channelUrl := fmt.Sprintf("https://bot.noury.li/commands/%s", channel)
|
|
|
|
commandUrl := "https://bot.noury.li/commands"
|
2023-12-17 18:48:46 +01:00
|
|
|
command, err := app.Models.Commands.GetAllChannel(channel)
|
|
|
|
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
|
|
|
|
|
2023-12-17 21:01:14 +01:00
|
|
|
allHelpText := app.GetAllHelpText()
|
|
|
|
app.Log.Info(allHelpText)
|
|
|
|
cs = append(cs, fmt.Sprintf("General commands: \n\n%s\nChannel commands:\n\n", allHelpText))
|
|
|
|
|
2023-12-17 18:48:46 +01:00
|
|
|
// 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
|
|
|
|
var c string
|
|
|
|
|
2023-12-18 00:04:27 +01:00
|
|
|
c = fmt.Sprintf(
|
|
|
|
"Name: \t%v\n"+
|
|
|
|
"Description: %v\n"+
|
|
|
|
"Level: \t%v\n"+
|
|
|
|
"Text: \t%v\n"+
|
|
|
|
"\n",
|
|
|
|
v.Name, v.Description, v.Level, v.Text,
|
|
|
|
)
|
2023-12-17 18:48:46 +01:00
|
|
|
|
|
|
|
// Add new value to the slice
|
|
|
|
cs = append(cs, c)
|
|
|
|
|
|
|
|
}
|
|
|
|
|
2023-12-18 00:07:10 +01:00
|
|
|
reply := fmt.Sprintf("Channel commands: %s | General commands: %s", channelUrl, commandUrl)
|
2023-12-17 18:48:46 +01:00
|
|
|
return reply
|
|
|
|
}
|