diff --git a/cmd/bot/commands.go b/cmd/bot/commands.go deleted file mode 100644 index 6c36e45..0000000 --- a/cmd/bot/commands.go +++ /dev/null @@ -1,561 +0,0 @@ -package main - -import ( - "fmt" - "strings" - - "github.com/gempir/go-twitch-irc/v4" - "github.com/lyx0/nourybot/internal/commands" - "github.com/lyx0/nourybot/internal/common" -) - -// handleCommand takes in a twitch.PrivateMessage and then routes the message to -// the function that is responsible for each command and knows how to deal with it accordingly. -func (app *Application) handleCommand(message twitch.PrivateMessage) { - - // Increments the counter how many commands have been used, called in the ping command. - common.CommandUsed() - - // commandName is the actual name of the command without the prefix. - // e.g. `()ping` would be `ping`. - commandName := strings.ToLower(strings.SplitN(message.Message, " ", 3)[0][2:]) - - // cmdParams are additional command parameters. - // e.g. `()weather san antonio` - // cmdParam[0] is `san` and cmdParam[1] = `antonio`. - // - // Since Twitch messages are at most 500 characters I use a - // maximum count of 500+10 just to be safe. - // https://discuss.dev.twitch.tv/t/missing-client-side-message-length-check/21316 - cmdParams := strings.SplitN(message.Message, " ", 500) - - // msgLen is the amount of words in a message without the prefix. - // Useful to check if enough cmdParams are provided. - msgLen := len(strings.SplitN(message.Message, " ", -2)) - - // target is the channelname the message originated from and - // where the TwitchClient should send the response - target := message.Channel - - // Userlevel is the level set for a user in the database. - // It is NOT same as twitch user/mod. - // 1000 = admin - // 500 = mod - // 250 = vip - // 100 = normal - // If the level returned is -1 then the user was not found in the database. - userLevel := app.GetUserLevel(message.User.ID) - - app.Logger.Infow("Command received", - // "message", message, // Pretty taxing - "message.Message", message.Message, - "message.Channel", target, - "userLevel", userLevel, - "commandName", commandName, - "cmdParams", cmdParams, - "msgLen", msgLen, - ) - - // A `commandName` is every message starting with `()`. - // Hardcoded commands have a priority over database commands. - // Switch over the commandName and see if there is a hardcoded case for it. - // If there was no switch case satisfied, query the database if there is - // a data.CommandModel.Name equal to the `commandName` - // If there is return the data.CommandModel.Text entry. - // Otherwise we ignore the message. - switch commandName { - case "": - if msgLen == 1 { - common.Send(target, "xd", app.TwitchClient) - return - } - - case "nourybot": - common.Send(target, "Lidl Twitch bot made by @nourylul. Prefix: ()", app.TwitchClient) - return - - // ()bttv - case "bttv": - if msgLen < 2 { - common.Send(target, "Not enough arguments provided. Usage: ()bttv ", app.TwitchClient) - return - } else { - commands.Bttv(target, cmdParams[1], app.TwitchClient) - return - } - - // Coinflip - case "coin": - commands.Coinflip(target, app.TwitchClient) - return - case "coinflip": - commands.Coinflip(target, app.TwitchClient) - return - case "cf": - commands.Coinflip(target, app.TwitchClient) - return - - // ()currency to - case "currency": - if msgLen < 4 { - common.Send(target, "Not enough arguments provided. Usage: ()currency 10 USD to EUR", app.TwitchClient) - return - } - commands.Currency(target, cmdParams[1], cmdParams[2], cmdParams[4], app.TwitchClient) - return - - // ()ffz - case "ffz": - if msgLen < 2 { - common.Send(target, "Not enough arguments provided. Usage: ()ffz ", app.TwitchClient) - return - } else { - commands.Ffz(target, cmdParams[1], app.TwitchClient) - return - } - - // ()followage - case "followage": - if msgLen == 1 { // ()followage - commands.Followage(target, target, message.User.Name, app.TwitchClient) - return - } else if msgLen == 2 { // ()followage forsen - commands.Followage(target, target, cmdParams[1], app.TwitchClient) - return - } else { // ()followage forsen pajlada - commands.Followage(target, cmdParams[1], cmdParams[2], app.TwitchClient) - return - } - - // First Line - // ()firstline - case "firstline": - if msgLen == 1 { - common.Send(target, "Usage: ()firstline ", app.TwitchClient) - return - } else if msgLen == 2 { - commands.FirstLine(target, target, cmdParams[1], app.TwitchClient) - return - } else { - commands.FirstLine(target, cmdParams[1], cmdParams[2], app.TwitchClient) - return - } - // ()fl - case "fl": - if msgLen == 1 { - common.Send(target, "Usage: ()firstline ", app.TwitchClient) - return - } else if msgLen == 2 { - commands.FirstLine(target, target, cmdParams[1], app.TwitchClient) - return - } else { - commands.FirstLine(target, cmdParams[1], cmdParams[2], app.TwitchClient) - return - } - case "lastfm": - if msgLen == 1 { - app.UserCheckLastFM(message) - return - } else if cmdParams[1] == "artist" && cmdParams[2] == "top" { - commands.LastFmArtistTop(target, message, app.TwitchClient) - return - } else { - // Default to first argument supplied being the name - // of the user to look up recently played. - commands.LastFmUserRecent(target, cmdParams[1], app.TwitchClient) - return - } - - case "help": - if msgLen == 1 { - common.Send(target, "Provides information for a given command. Usage: ()help ", app.TwitchClient) - return - } else { - app.commandHelp(target, cmdParams[1], message.User.Name) - return - } - - // ()ping - case "ping": - commands.Ping(target, app.TwitchClient) - return - - // Thumbnail - // ()preview - case "preview": - if msgLen < 2 { - common.Send(target, "Not enough arguments provided. Usage: ()preview ", app.TwitchClient) - return - } else { - commands.Preview(target, cmdParams[1], app.TwitchClient) - return - } - - case "set": - if msgLen < 3 { - common.Send(target, "Not enough arguments provided.", app.TwitchClient) - return - } else if cmdParams[1] == "lastfm" { - app.SetUserLastFM(cmdParams[2], message) - //app.SetLastFMUser(cmdParams[2], message) - return - } else if cmdParams[1] == "location" { - app.SetUserLocation(message) - return - } else { - return - } - - // SevenTV - // ()seventv - case "seventv": - if msgLen < 2 { - common.Send(target, "Not enough arguments provided. Usage: ()seventv ", app.TwitchClient) - return - } else { - commands.Seventv(target, cmdParams[1], app.TwitchClient) - return - } - - // ()thumbnail - case "thumbnail": - if msgLen < 2 { - common.Send(target, "Not enough arguments provided. Usage: ()thumbnail ", app.TwitchClient) - return - } else { - commands.Preview(target, cmdParams[1], app.TwitchClient) - return - } - // ()7tv - case "7tv": - if msgLen < 2 { - common.Send(target, "Not enough arguments provided. Usage: ()seventv ", app.TwitchClient) - return - } else { - commands.Seventv(target, cmdParams[1], app.TwitchClient) - return - } - - // ()tweet - case "tweet": - if msgLen < 2 { - common.Send(target, "Not enough arguments provided. Usage: ()tweet ", app.TwitchClient) - return - } else { - commands.Tweet(target, cmdParams[1], app.TwitchClient) - return - } - - case "phonetic": - if msgLen < 2 { - common.Send(target, "Not enough arguments provided. Usage: ()phonetic . ()help phonetic for more info", app.TwitchClient) - return - } else { - commands.Phonetic(target, message.Message[10:len(message.Message)], app.TwitchClient) - return - } - - case "ph": - if msgLen < 2 { - common.Send(target, "Not enough arguments provided. Usage: ()ph . ()help ph for more info", app.TwitchClient) - return - } else { - commands.Phonetic(target, message.Message[4:len(message.Message)], app.TwitchClient) - return - } - - // ()weather - case "weather": - if msgLen == 1 { - // Default to first argument supplied being the name - // of the user to look up recently played. - app.UserCheckWeather(message) - return - } else if msgLen < 2 { - common.Send(target, "Not enough arguments provided.", app.TwitchClient) - return - } else { - commands.Weather(target, message.Message[10:len(message.Message)], app.TwitchClient) - return - } - - // Xkcd - // Random Xkcd - case "rxkcd": - commands.RandomXkcd(target, app.TwitchClient) - return - case "randomxkcd": - commands.RandomXkcd(target, app.TwitchClient) - return - // Latest Xkcd - case "xkcd": - commands.Xkcd(target, app.TwitchClient) - return - - // Commands with permission level or database from here on - - //################# - // 250 - VIP only - //################# - // ()debug user - // ()debug command - case "debug": - if userLevel < 250 { - return - } else if msgLen < 3 { - common.Send(target, "Not enough arguments provided.", app.TwitchClient) - return - } else if cmdParams[1] == "user" { - app.DebugUser(cmdParams[2], message) - return - } else if cmdParams[1] == "command" { - app.DebugCommand(cmdParams[2], message) - return - } else { - return - } - - // ()echo - case "echo": - if userLevel < 250 { // Limit to myself for now. - return - } else if msgLen < 2 { - common.Send(target, "Not enough arguments provided.", app.TwitchClient) - return - } else { - commands.Echo(target, message.Message[7:len(message.Message)], app.TwitchClient) - return - } - - //################### - // 1000 - Admin only - //################### - - // ##### - // Add - // ##### - case "addchannel": - if userLevel < 1000 { - return - } else if msgLen < 2 { - common.Send(target, "Not enough arguments provided.", app.TwitchClient) - return - } else { - // ()addchannel noemience - app.AddChannel(cmdParams[1], message) - return - } - case "addcommand": - if userLevel < 1000 { - return - } else if msgLen < 3 { - common.Send(target, "Not enough arguments provided.", app.TwitchClient) - return - } else { - // ()addcommand dank FeelsDankMan xD - app.AddCommand(cmdParams[1], message) - return - } - case "addtimer": - if userLevel < 1000 { - return - } else if msgLen < 4 { - common.Send(target, "Not enough arguments provided.", app.TwitchClient) - return - } else { - // ()addtimer gfuel 5m sponsor XD xD - app.AddTimer(cmdParams[1], message) - return - } - - // ###### - // Edit - // ###### - case "edituser": - if userLevel < 1000 { // Limit to myself for now. - return - } else if msgLen < 4 { - common.Send(target, "Not enough arguments provided.", app.TwitchClient) - return - } else if cmdParams[1] == "level" { - // ()edituser level nourylul 1000 - app.EditUserLevel(cmdParams[2], cmdParams[3], message) - return - } else { - return - } - // ()edittimer testname 10m test text xd - case "edittimer": - if userLevel < 1000 { // Limit to myself for now. - return - } else if msgLen < 4 { - common.Send(target, "Not enough arguments provided.", app.TwitchClient) - return - } else { - // ()edituser level nourylul 1000 - app.EditTimer(cmdParams[1], message) - } - case "editcommand": // ()editcommand level dankwave 1000 - if userLevel < 1000 { - return - } else if msgLen < 4 { - common.Send(target, "Not enough arguments provided.", app.TwitchClient) - return - } else if cmdParams[1] == "level" { - app.EditCommandLevel(cmdParams[2], cmdParams[3], message) - return - } else if cmdParams[1] == "category" { - app.EditCommandCategory(cmdParams[2], cmdParams[3], message) - return - } else if cmdParams[1] == "help" { - app.EditCommandHelp(cmdParams[2], message) - return - } else { - return - } - - // ######## - // Delete - // ######## - case "deletechannel": - if userLevel < 1000 { // Limit to myself for now. - return - } else if msgLen < 2 { - common.Send(target, "Not enough arguments provided.", app.TwitchClient) - return - } else { - // ()deletechannel noemience - app.DeleteChannel(cmdParams[1], message) - return - } - case "deletecommand": - if userLevel < 1000 { // Limit to myself for now. - return - } else if msgLen < 2 { - common.Send(target, "Not enough arguments provided.", app.TwitchClient) - return - } else { - // ()deletecommand dank - app.DeleteCommand(cmdParams[1], message) - return - } - case "deleteuser": - if userLevel < 1000 { // Limit to myself for now. - return - } else if msgLen < 2 { - common.Send(target, "Not enough arguments provided.", app.TwitchClient) - return - } else { - // ()deleteuser noemience - app.DeleteUser(cmdParams[1], message) - return - } - case "deletetimer": - if userLevel < 1000 { // Limit to myself for now. - return - } else if msgLen < 2 { - common.Send(target, "Not enough arguments provided.", app.TwitchClient) - return - } else { - // ()deletetimer dank - app.DeleteTimer(cmdParams[1], message) - return - } - - case "asd": - app.Logger.Info(app.Scheduler.Entries()) - return - - case "bttvemotes": - if userLevel < 1000 { - commands.Bttvemotes(target, app.TwitchClient) - return - } else { - return - } - - case "ffzemotes": - if userLevel < 1000 { - commands.Ffzemotes(target, app.TwitchClient) - return - } else { - return - } - - // ################## - // Check if the commandName exists as the "name" of a command in the database. - // if it doesnt then ignore it. - // ################## - default: - reply, err := app.GetCommand(commandName, message.User.Name) - if err != nil { - return - } - common.SendNoLimit(message.Channel, reply, app.TwitchClient) - return - } -} - -// Map of known commands with their help texts. -var helpText = map[string]string{ - "bttv": "Returns the search URL for a given BTTV emote. Example usage: ()bttv ", - "coin": "Flips a coin! Aliases: coinflip, coin, cf", - "cf": "Flips a coin! Aliases: coinflip, coin, cf", - "coinflip": "Flips a coin! Aliases: coinflip, coin, cf", - "currency": "Returns the exchange rate for two currencies. Only three letter abbreviations are supported ( List of supported currencies: https://decapi.me/misc/currency?list ). Example usage: ()currency 10 USD to EUR", - "ffz": "Returns the search URL for a given FFZ emote. Example usage: ()ffz ", - "followage": "Returns how long a given user has been following a channel. Example usage: ()followage ", - "firstline": "Returns the first message a user has sent in a given channel. Aliases: firstline, fl. Example usage: ()firstline ", - "fl": "Returns the first message a user has sent in a given channel. Aliases: firstline, fl. Example usage: ()fl ", - "help": "Returns more information about a command and its usage. 4Head Example usage: ()help ", - "ping": "Hopefully returns a Pong! monkaS", - "preview": "Returns a link to an (almost) live screenshot of a live channel. Alias: preview, thumbnail. Example usage: ()preview ", - "phonetic": "Translates the input to the text equivalent on a phonetic russian keyboard layout. Layout and general functionality is the same as https://russian.typeit.org/", - "ph": "Translates the input to the text equivalent on a phonetic russian keyboard layout. Layout and general functionality is the same as https://russian.typeit.org/", - "thumbnail": "Returns a link to an (almost) live screenshot of a live channel. Alias: preview, thumbnail. Example usage: ()thumbnail ", - "tweet": "Returns the latest tweet for a provided user. Example usage: ()tweet ", - "seventv": "Returns the search URL for a given SevenTV emote. Aliases: seventv, 7tv. Example usage: ()seventv FeelsDankMan", - "7tv": "Returns the search URL for a given SevenTV emote. Aliases: seventv, 7tv. Example usage: ()7tv FeelsDankMan", - "weather": "Returns the weather for a given location. Example usage: ()weather Vilnius", - "randomxkcd": "Returns a link to a random xkcd comic. Alises: randomxkcd, rxkcd. Example usage: ()randomxkcd", - "rxkcd": "Returns a link to a random xkcd comic. Alises: randomxkcd, rxkcd. Example usage: ()rxkcd", - "xkcd": "Returns a link to the latest xkcd comic. Example usage: ()xkcd", -} - -func (app *Application) loadCommandHelp() { - for k, v := range helpText { - err := app.Rdb.HSet(ctx, "command-help", k, v).Err() - if err != nil { - app.Logger.Panic(err) - } - } - commandHelpText := app.Rdb.HGetAll(ctx, "command-help").Val() - app.Logger.Infow("Successfully loaded command help text into redis", - "commandHelpText", commandHelpText, - ) - -} - -// Help checks if a help text for a given command exists and replies with it. -func (app *Application) commandHelp(target, name, username string) { - // Check if the `helpText` map has an entry for `name`. If it does return it's value entry - // and send that as a reply. - i, ok := helpText[name] - if !ok { - // If it doesn't check the database for a command with that `name`. If there is one - // reply with that commands `help` entry. - c, err := app.GetCommandHelp(name, username) - if err != nil { - app.Logger.Infow("commandHelp: no such command found", - "err", err) - return - } - - reply := fmt.Sprint(c) - common.Send(target, reply, app.TwitchClient) - return - } - - reply := fmt.Sprint(i) - common.Send(target, reply, app.TwitchClient) -} diff --git a/cmd/bot/user.go b/cmd/bot/user.go deleted file mode 100644 index 9e620b0..0000000 --- a/cmd/bot/user.go +++ /dev/null @@ -1,213 +0,0 @@ -package main - -import ( - "fmt" - "strconv" - - "github.com/gempir/go-twitch-irc/v4" - "github.com/lyx0/nourybot/internal/commands" - "github.com/lyx0/nourybot/internal/common" - "go.uber.org/zap" -) - -// AddUser calls GetIdByLogin to get the twitch id of the login name and then adds -// the login name, twitch id and supplied level to the database. -func (app *Application) InitUser(login, twitchId string, message twitch.PrivateMessage) { - sugar := zap.NewExample().Sugar() - defer sugar.Sync() - - _, err := app.Models.Users.Check(twitchId) - app.Logger.Error(err) - if err != nil { - app.Logger.Infow("InitUser: Adding new user:", - "login: ", login, - "twitchId: ", twitchId, - ) - app.Models.Users.Insert(login, twitchId) - - return - } - - sugar.Infow("User Insert: User already registered: xd", - "login: ", login, - "twitchId: ", twitchId, - ) -} - -// DebugUser queries the database for a login name, if that name exists it returns the fields -// and outputs them to twitch chat and a twitch whisper. -func (app *Application) DebugUser(login string, message twitch.PrivateMessage) { - user, err := app.Models.Users.Get(login) - - if err != nil { - reply := fmt.Sprintf("Something went wrong FeelsBadMan %s", err) - common.Send(message.Channel, reply, app.TwitchClient) - return - } else { - reply := fmt.Sprintf("User %v: ID %v, Login: %s, TwitchID: %v, Level: %v", login, user.ID, user.Login, user.TwitchID, user.Level) - common.Send(message.Channel, reply, app.TwitchClient) - //app.TwitchClient.Whisper(message.User.Name, reply) - return - } -} - -// DeleteUser takes in a login string, queries the database for an entry with -// that login name and tries to delete that entry in the database. -func (app *Application) DeleteUser(login string, message twitch.PrivateMessage) { - err := app.Models.Users.Delete(login) - if err != nil { - common.Send(message.Channel, "Something went wrong FeelsBadMan", app.TwitchClient) - app.Logger.Error(err) - return - } - - reply := fmt.Sprintf("Deleted user %s", login) - common.Send(message.Channel, reply, app.TwitchClient) -} - -// EditUserLevel tries to update the database record for the supplied -// login name with the new level. -func (app *Application) EditUserLevel(login, lvl string, message twitch.PrivateMessage) { - // Convert the level string to an integer. This is an easy check to see if - // the level supplied was a number only. - level, err := strconv.Atoi(lvl) - if err != nil { - app.Logger.Error(err) - common.Send(message.Channel, fmt.Sprintf("Something went wrong FeelsBadMan %s", ErrUserLevelNotInteger), app.TwitchClient) - return - } - - err = app.Models.Users.SetLevel(login, level) - if err != nil { - common.Send(message.Channel, fmt.Sprintf("Something went wrong FeelsBadMan %s", ErrRecordNotFound), app.TwitchClient) - app.Logger.Error(err) - return - } else { - reply := fmt.Sprintf("Updated user %s to level %v", login, level) - common.Send(message.Channel, reply, app.TwitchClient) - return - } -} - -// SetUserLocation sets new location for the user -func (app *Application) SetUserLocation(message twitch.PrivateMessage) { - // snipLength is the length we need to "snip" off of the start of `message`. - // `()set location` = +13 - // trailing space = +1 - // zero-based = +1 - // = 16 - snipLength := 15 - - // Split the twitch message at `snipLength` plus length of the name of the - // The part of the message we are left over with is then passed on to the database - // handlers as the `location` part of the command. - location := message.Message[snipLength:len(message.Message)] - login := message.User.Name - twitchId := message.User.ID - - app.Logger.Infow("SetUserLocation", - "location", location, - "login", login, - "twitchId", message.User.ID, - ) - err := app.Models.Users.SetLocation(twitchId, location) - if err != nil { - common.Send(message.Channel, fmt.Sprintf("Something went wrong FeelsBadMan %s", ErrRecordNotFound), app.TwitchClient) - app.Logger.Error(err) - return - } else { - reply := fmt.Sprintf("Successfully set your location to %v", location) - common.Send(message.Channel, reply, app.TwitchClient) - return - } -} - -// SetUserLastFM tries to update the database record for the supplied -// login name with the new level. -func (app *Application) SetUserLastFM(lastfmUser string, message twitch.PrivateMessage) { - login := message.User.Name - - app.Logger.Infow("SetUserLastFM", - "lastfmUser", lastfmUser, - "login", login, - ) - err := app.Models.Users.SetLastFM(login, lastfmUser) - if err != nil { - common.Send(message.Channel, fmt.Sprintf("Something went wrong FeelsBadMan %s", ErrRecordNotFound), app.TwitchClient) - app.Logger.Error(err) - return - } else { - reply := fmt.Sprintf("Successfully set your lastfm username to %v", lastfmUser) - common.Send(message.Channel, reply, app.TwitchClient) - return - } -} - -// GetUserLevel takes in a login name and queries the database for an entry -// with such a name value. If there is one it returns the level value as an integer. -// Returns 0 on an error which is the level for unregistered users. -func (app *Application) GetUserLevel(twitchId string) int { - userLevel, err := app.Models.Users.GetLevel(twitchId) - if err != nil { - return 0 - } else { - return userLevel - } -} - -func (app *Application) UserCheckWeather(message twitch.PrivateMessage) { - sugar := zap.NewExample().Sugar() - defer sugar.Sync() - - twitchLogin := message.User.Name - twitchId := message.User.ID - sugar.Infow("UserCheckWeather: ", - "twitchLogin:", twitchLogin, - "twitchId:", twitchId, - ) - location, err := app.Models.Users.GetLocation(twitchId) - if err != nil { - sugar.Errorw("No location data registered for: ", - "twitchLogin:", twitchLogin, - "twitchId:", twitchId, - ) - reply := "No location for your account set in my database. Use ()set location to register. Otherwise use ()weather without registering." - common.Send(message.Channel, reply, app.TwitchClient) - return - } - - target := message.Channel - sugar.Infow("Twitchlogin: ", - "twitchLogin:", twitchLogin, - "location:", location, - ) - - commands.Weather(target, location, app.TwitchClient) -} - -func (app *Application) UserCheckLastFM(message twitch.PrivateMessage) { - sugar := zap.NewExample().Sugar() - defer sugar.Sync() - - twitchLogin := message.User.Name - sugar.Infow("Twitchlogin: ", - "twitchLogin:", twitchLogin, - ) - lastfmUser, err := app.Models.Users.GetLastFM(twitchLogin) - if err != nil { - sugar.Errorw("No LastFM account registered for: ", - "twitchLogin:", twitchLogin, - ) - reply := "No lastfm account registered in my database. Use ()register lastfm to register. (Not yet implemented) Otherwise use ()lastfm without registering." - common.Send(message.Channel, reply, app.TwitchClient) - return - } - - target := message.Channel - sugar.Infow("Twitchlogin: ", - "twitchLogin:", twitchLogin, - "user:", lastfmUser, - ) - - commands.LastFmUserRecent(target, lastfmUser, app.TwitchClient) -} diff --git a/go.mod b/go.mod index 5a33327..6d4e08e 100644 --- a/go.mod +++ b/go.mod @@ -3,23 +3,31 @@ module github.com/lyx0/nourybot go 1.20 require ( - github.com/briandowns/openweathermap v0.18.0 - github.com/dustin/go-humanize v1.0.0 github.com/gempir/go-twitch-irc/v4 v4.0.0 + github.com/google/uuid v1.3.1 github.com/jakecoffman/cron v0.0.0-20190106200828-7e2009c226a5 - github.com/joho/godotenv v1.4.0 - github.com/julienschmidt/httprouter v1.3.0 - github.com/lib/pq v1.10.6 - github.com/nicklaw5/helix v1.25.0 - github.com/redis/go-redis/v9 v9.0.3 + github.com/lib/pq v1.10.9 + github.com/rs/zerolog v1.29.1 github.com/shkh/lastfm-go v0.0.0-20191215035245-89a801c244e0 - go.uber.org/zap v1.21.0 + github.com/wader/goutubedl v0.0.0-20230924165737-427b7fa536e6 + go.uber.org/zap v1.24.0 + gopkg.in/gomail.v2 v2.0.0-20160411212932-81ebce5c23df ) 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 - go.uber.org/atomic v1.9.0 // indirect - go.uber.org/multierr v1.8.0 // indirect + github.com/golang-jwt/jwt/v4 v4.0.0 // indirect + github.com/nicklaw5/helix/v2 v2.25.1 // indirect + go.uber.org/atomic v1.7.0 // indirect + go.uber.org/multierr v1.6.0 // indirect + gopkg.in/alexcesaro/quotedprintable.v3 v3.0.0-20150716171945-2caba252f4dc // indirect +) + +require ( + github.com/briandowns/openweathermap v0.19.0 + github.com/dustin/go-humanize v1.0.1 + github.com/golang-jwt/jwt v3.2.1+incompatible // indirect + github.com/joho/godotenv v1.5.1 + github.com/mattn/go-colorable v0.1.12 // indirect + github.com/mattn/go-isatty v0.0.14 // indirect + golang.org/x/sys v0.8.0 // indirect ) diff --git a/go.sum b/go.sum index a5b0dad..cb05f7a 100644 --- a/go.sum +++ b/go.sum @@ -1,20 +1,17 @@ github.com/benbjohnson/clock v1.1.0 h1:Q92kusRqC1XV2MjkWETPvjJVqKetz1OzxZB7mHJLju8= -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/bsm/ginkgo/v2 v2.7.0 h1:ItPMPH90RbmZJt5GtkcNvIRuGEdwlBItdNVoyzaNQao= -github.com/bsm/gomega v1.26.0 h1:LhQm+AFcgV2M0WyKroMASzAzCAJVpAxQXv4SaI9a69Y= -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/briandowns/openweathermap v0.19.0 h1:nkopLMEtZLxbZI1th6dOG6xkajpszofqf53r5K8mT9k= +github.com/briandowns/openweathermap v0.19.0/go.mod h1:0GLnknqicWxXnGi1IqoOaZIw+kIe5hkt+YM5WY3j8+0= +github.com/coreos/go-systemd/v22 v22.5.0/go.mod h1:Y58oyj3AT4RCenI/lSvhwexgC+NSVTIJ3seZv2GcEnc= 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/dustin/go-humanize v1.0.1 h1:GzkhY7T5VNhEkwH0PVJgjz+fX1rhBrR7pRT3mDkpeCY= +github.com/dustin/go-humanize v1.0.1/go.mod h1:Mu1zIs6XwVuF/gI1OepvI0qD18qycQx+mFykh5fBlto= +github.com/fortytw2/leaktest v1.3.0 h1:u8491cBMTQ8ft8aeV+adlcytMZylmA5nnwwkRZjI8vw= +github.com/fortytw2/leaktest v1.3.0/go.mod h1:jDsjWgpAGjm2CA7WthBh/CdZYEPF31XHquHwclZch5g= github.com/gempir/go-twitch-irc/v4 v4.0.0 h1:sHVIvbWOv9nHXGEErilclxASv0AaQEr/r/f9C0B9aO8= github.com/gempir/go-twitch-irc/v4 v4.0.0/go.mod h1:QsOMMAk470uxQ7EYD9GJBGAVqM/jDrXBNbuePfTauzg= +github.com/godbus/dbus/v5 v5.0.4/go.mod h1:xhWf0FNVPg57R7Z0UbKHbJfkEywrmjJnf7w5xrFpKfA= github.com/golang-jwt/jwt v3.2.1+incompatible h1:73Z+4BJcrTC+KczS6WvTPvRGOp1WmfEP4Q1lOd9Z/+c= github.com/golang-jwt/jwt v3.2.1+incompatible/go.mod h1:8pz2t5EyA70fFQQSrl6XZXzqecmYZeUEB8OUGHkxJ+I= github.com/golang-jwt/jwt/v4 v4.0.0 h1:RAqyYixv1p7uEnocuy8P1nru5wprCh/MH2BIlW5z5/o= diff --git a/internal/commands/bttvemotes.go b/internal/commands/bttvemotes.go deleted file mode 100644 index 2180fff..0000000 --- a/internal/commands/bttvemotes.go +++ /dev/null @@ -1,20 +0,0 @@ -package commands - -import ( - "github.com/gempir/go-twitch-irc/v4" - "github.com/lyx0/nourybot/internal/commands/decapi" - "github.com/lyx0/nourybot/internal/common" - "go.uber.org/zap" -) - -func Bttvemotes(target string, tc *twitch.Client) { - sugar := zap.NewExample().Sugar() - defer sugar.Sync() - - resp, err := decapi.Bttvemotes(target) - if err != nil { - sugar.Error(err) - } - - common.Send(target, resp, tc) -} diff --git a/internal/commands/echo.go b/internal/commands/echo.go deleted file mode 100644 index 96090e2..0000000 --- a/internal/commands/echo.go +++ /dev/null @@ -1,10 +0,0 @@ -package commands - -import ( - "github.com/gempir/go-twitch-irc/v4" - "github.com/lyx0/nourybot/internal/common" -) - -func Echo(target, message string, tc *twitch.Client) { - common.Send(target, message, tc) -} diff --git a/internal/commands/ffz.go b/internal/commands/ffz.go index 011bd43..10435e1 100644 --- a/internal/commands/ffz.go +++ b/internal/commands/ffz.go @@ -2,9 +2,9 @@ package commands import "fmt" - "github.com/gempir/go-twitch-irc/v4" - "github.com/lyx0/nourybot/internal/common" -) +func Ffz(query string) string { + reply := fmt.Sprintf("https://www.frankerfacez.com/emoticons/?q=%s&sort=count-desc&days=0", query) return reply } + diff --git a/internal/commands/ffzemotes.go b/internal/commands/ffzemotes.go deleted file mode 100644 index 0ff5226..0000000 --- a/internal/commands/ffzemotes.go +++ /dev/null @@ -1,20 +0,0 @@ -package commands - -import ( - "github.com/gempir/go-twitch-irc/v4" - "github.com/lyx0/nourybot/internal/commands/decapi" - "github.com/lyx0/nourybot/internal/common" - "go.uber.org/zap" -) - -func Ffzemotes(target string, tc *twitch.Client) { - sugar := zap.NewExample().Sugar() - defer sugar.Sync() - - resp, err := decapi.Ffzemotes(target) - if err != nil { - sugar.Error(err) - } - - common.Send(target, resp, tc) -} diff --git a/internal/commands/firstline.go b/internal/commands/firstline.go deleted file mode 100644 index 679559e..0000000 --- a/internal/commands/firstline.go +++ /dev/null @@ -1,20 +0,0 @@ -package commands - -import ( - "fmt" - - "github.com/gempir/go-twitch-irc/v4" - "github.com/lyx0/nourybot/internal/commands/ivr" - "github.com/lyx0/nourybot/internal/common" -) - -func FirstLine(target, channel, username string, tc *twitch.Client) { - ivrResponse, err := ivr.FirstLine(channel, username) - - if err != nil { - common.Send(channel, fmt.Sprint(err), tc) - return - } - - common.Send(target, ivrResponse, tc) -} diff --git a/internal/commands/followage.go b/internal/commands/followage.go deleted file mode 100644 index 683a1e9..0000000 --- a/internal/commands/followage.go +++ /dev/null @@ -1,21 +0,0 @@ -package commands - -import ( - "github.com/gempir/go-twitch-irc/v4" - "github.com/lyx0/nourybot/internal/commands/decapi" - "github.com/lyx0/nourybot/internal/common" - "go.uber.org/zap" -) - -// ()currency 10 USD to EUR -func Followage(target, channel, username string, tc *twitch.Client) { - sugar := zap.NewExample().Sugar() - defer sugar.Sync() - - resp, err := decapi.Followage(channel, username) - if err != nil { - sugar.Error(err) - } - - common.Send(target, resp, tc) -} diff --git a/internal/commands/seventv.go b/internal/commands/seventv.go index 011bd43..1ea590d 100644 --- a/internal/commands/seventv.go +++ b/internal/commands/seventv.go @@ -2,9 +2,9 @@ package commands import "fmt" - "github.com/gempir/go-twitch-irc/v4" - "github.com/lyx0/nourybot/internal/common" -) +func SevenTV(query string) string { + reply := fmt.Sprintf("https://7tv.app/emotes?page=1&query=%s", query) return reply } + diff --git a/internal/commands/tweet.go b/internal/commands/tweet.go deleted file mode 100644 index 0fe75ce..0000000 --- a/internal/commands/tweet.go +++ /dev/null @@ -1,20 +0,0 @@ -package commands - -import ( - "github.com/gempir/go-twitch-irc/v4" - "github.com/lyx0/nourybot/internal/commands/decapi" - "github.com/lyx0/nourybot/internal/common" - "go.uber.org/zap" -) - -func Tweet(target, username string, tc *twitch.Client) { - sugar := zap.NewExample().Sugar() - defer sugar.Sync() - - resp, err := decapi.Tweet(username) - if err != nil { - sugar.Error(err) - } - - common.Send(target, resp, tc) -} diff --git a/internal/common/privs.go b/internal/common/privs.go deleted file mode 100644 index 0fc2aca..0000000 --- a/internal/common/privs.go +++ /dev/null @@ -1,26 +0,0 @@ -package common - -import "github.com/gempir/go-twitch-irc/v4" - -// ElevatedPrivsMessage is checking a given message twitch.PrivateMessage -// if it came from a moderator/vip/or broadcaster and returns a bool -func ElevatedPrivsMessage(message twitch.PrivateMessage) bool { - if message.User.Badges["moderator"] == 1 || - message.User.Badges["vip"] == 1 || - message.User.Badges["broadcaster"] == 1 { - return true - } else { - return false - } -} - -// ModPrivsMessage is checking a given message twitch.PrivateMessage -// if it came from a moderator or broadcaster and returns a bool -func ModPrivsMessage(message twitch.PrivateMessage) bool { - if message.User.Badges["moderator"] == 1 || - message.User.Badges["broadcaster"] == 1 { - return true - } else { - return false - } -} diff --git a/internal/common/send.go b/internal/common/send.go deleted file mode 100644 index 67e8a1e..0000000 --- a/internal/common/send.go +++ /dev/null @@ -1,180 +0,0 @@ -package common - -import ( - "bytes" - "encoding/json" - "fmt" - "io" - "log" - "net/http" - - "github.com/gempir/go-twitch-irc/v4" - "go.uber.org/zap" -) - -// banphraseResponse is the data we receive back from -// the banphrase API -type banphraseResponse struct { - Banned bool `json:"banned"` - InputMessage string `json:"input_message"` - BanphraseData banphraseData `json:"banphrase_data"` -} - -// banphraseData contains details about why a message -// was banphrased. -type banphraseData struct { - Id int `json:"id"` - Name string `json:"name"` - Phrase string `json:"phrase"` - Length int `json:"length"` - Permanent bool `json:"permanent"` -} - -var ( - banPhraseUrl = "https://pajlada.pajbot.com/api/v1/banphrases/test" -) - -// CheckMessage checks a given message against the banphrase api. -// returns false, "okay" if a message is allowed -// returns true and a string with the reason if it was banned. -// More information: -// https://gist.github.com/pajlada/57464e519ba8d195a97ddcd0755f9715 -func checkMessage(text string) (bool, string) { - sugar := zap.NewExample().Sugar() - defer sugar.Sync() - - // {"message": "AHAHAHAHA LUL"} - reqBody, err := json.Marshal(map[string]string{ - "message": text, - }) - if err != nil { - log.Fatal(err) - } - - resp, err := http.Post(banPhraseUrl, "application/json", bytes.NewBuffer(reqBody)) - if err != nil { - log.Panic(err) - } - - defer resp.Body.Close() - - body, err := io.ReadAll(resp.Body) - if err != nil { - log.Panic(err) - } - - var responseObject banphraseResponse - json.Unmarshal(body, &responseObject) - - // Bad Message - // - // {"phrase": "No gyazo allowed"} - reason := responseObject.BanphraseData.Name - if responseObject.Banned { - return true, fmt.Sprint(reason) - } else if !responseObject.Banned { - // Good message - return false, "okay" - } - - // Couldn't contact api so assume it was a bad message - return true, "Banphrase API couldn't be reached monkaS" -} - -// Send is used to send twitch replies and contains the necessary -// safeguards and logic for that. -func Send(target, message string, tc *twitch.Client) { - sugar := zap.NewExample().Sugar() - defer sugar.Sync() - - // Message we are trying to send is empty. - if len(message) == 0 { - return - } - - // Since messages starting with `.` or `/` are used for special actions - // (ban, whisper, timeout) and so on, we place an emote infront of it so - // the actions wouldn't execute. `!` and `$` are common bot prefixes so we - // don't allow them either. - if message[0] == '.' || message[0] == '/' || message[0] == '!' || message[0] == '$' { - message = ":tf: " + message - } - - // check the message for bad words before we say it - messageBanned, banReason := checkMessage(message) - if messageBanned { - // Bad message, replace message and log it. - tc.Say(target, "[BANPHRASED] monkaS") - sugar.Infow("banned message detected", - "target channel", target, - "message", message, - "ban reason", banReason, - ) - - return - } else { - // In case the message we are trying to send is longer than the - // maximum allowed message length on twitch we split the message in two parts. - // Twitch has a maximum length for messages of 510 characters so to be safe - // we split and check at 500 characters. - // https://discuss.dev.twitch.tv/t/missing-client-side-message-length-check/21316 - // TODO: Make it so it splits at a space instead and not in the middle of a word. - if len(message) > 500 { - firstMessage := message[0:499] - secondMessage := message[499:] - - tc.Say(target, firstMessage) - tc.Say(target, secondMessage) - - return - } - // Message was fine. - tc.Say(target, message) - return - } -} - -// SendNoLimit does not check for the maximum message size. -// Used in sending commands from the database since the command has to have -// been gotten in there somehow. So it fits. Still checks for banphrases. -func SendNoLimit(target, message string, tc *twitch.Client) { - sugar := zap.NewExample().Sugar() - defer sugar.Sync() - - // Message we are trying to send is empty. - if len(message) == 0 { - return - } - - // Since messages starting with `.` or `/` are used for special actions - // (ban, whisper, timeout) and so on, we place an emote infront of it so - // the actions wouldn't execute. `!` and `$` are common bot prefixes so we - // don't allow them either. - if message[0] == '.' || message[0] == '/' || message[0] == '!' || message[0] == '$' { - message = ":tf: " + message - } - - // check the message for bad words before we say it - messageBanned, banReason := checkMessage(message) - if messageBanned { - // Bad message, replace message and log it. - tc.Say(target, "[BANPHRASED] monkaS") - sugar.Infow("banned message detected", - "target channel", target, - "message", message, - "ban reason", banReason, - ) - - return - } else { - // In case the message we are trying to send is longer than the - // maximum allowed message length on twitch we split the message in two parts. - // Twitch has a maximum length for messages of 510 characters so to be safe - // we split and check at 500 characters. - // https://discuss.dev.twitch.tv/t/missing-client-side-message-length-check/21316 - // TODO: Make it so it splits at a space instead and not in the middle of a word. - // Message was fine. - tc.Say(target, message) - return - } -}