2023-08-08 02:37:37 +02:00
package main
import (
"strings"
"github.com/gempir/go-twitch-irc/v4"
"github.com/lyx0/nourybot/internal/commands"
"github.com/lyx0/nourybot/internal/common"
2023-10-11 18:52:46 +02:00
"github.com/lyx0/nourybot/pkg/ivr"
"github.com/lyx0/nourybot/pkg/lastfm"
"github.com/lyx0/nourybot/pkg/owm"
2023-08-08 02:37:37 +02:00
)
// 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 ) {
2023-09-07 20:12:09 +02:00
var reply string
2023-08-08 02:37:37 +02:00
2023-12-03 16:35:34 +01:00
if message . Channel == "forsen" {
return
}
2023-08-08 02:37:37 +02:00
// Increments the counter how many commands have been used, called in the ping command.
2023-10-10 12:49:06 +02:00
go common . CommandUsed ( )
go app . InitUser ( message . User . Name , message . User . ID )
2023-08-08 02:37:37 +02:00
// 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 ) )
2023-10-11 20:46:02 +02:00
userLevel := app . GetUserLevel ( message )
2023-10-11 18:01:32 +02:00
2023-08-08 02:37:37 +02:00
// target is the channelname the message originated from and
// where the TwitchClient should send the response
target := message . Channel
app . Log . Infow ( "Command received" ,
// "message", message, // Pretty taxing
"message.Message" , message . Message ,
"message.Channel" , target ,
"commandName" , commandName ,
"cmdParams" , cmdParams ,
"msgLen" , msgLen ,
2023-09-19 14:04:58 +02:00
"userLevel" , userLevel ,
2023-08-08 02:37:37 +02:00
)
2023-10-10 16:07:35 +02:00
go app . LogCommand ( message , commandName , userLevel )
2023-08-08 02:37:37 +02:00
// 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 {
2023-09-07 20:12:09 +02:00
reply = "xd"
2023-08-08 02:37:37 +02:00
}
2023-10-11 18:01:32 +02:00
// --------------------------------
// pleb commands
// --------------------------------
2023-09-07 20:43:16 +02:00
case "bttv" :
if msgLen < 2 {
reply = "Not enough arguments provided. Usage: ()bttv <emote name>"
} else {
reply = commands . Bttv ( cmdParams [ 1 ] )
}
// Coinflip
case "coin" :
reply = commands . Coinflip ( )
case "coinflip" :
reply = commands . Coinflip ( )
case "cf" :
reply = commands . Coinflip ( )
2023-09-07 21:09:33 +02:00
// ()currency <amount> <input currency> to <output currency>
case "currency" :
if msgLen < 4 {
reply = "Not enough arguments provided. Usage: ()currency 10 USD to EUR"
} else {
reply , _ = commands . Currency ( cmdParams [ 1 ] , cmdParams [ 2 ] , cmdParams [ 4 ] )
}
2023-10-10 14:28:43 +02:00
case "osrs" :
reply = commands . OSRS ( message . Message [ 7 : len ( message . Message ) ] )
case "preview" :
reply = commands . Preview ( cmdParams [ 1 ] )
case "thumbnail" :
reply = commands . Preview ( cmdParams [ 1 ] )
case "ffz" :
reply = commands . Ffz ( cmdParams [ 1 ] )
case "ddg" :
reply = commands . DuckDuckGo ( message . Message [ 6 : len ( message . Message ) ] )
case "youtube" :
reply = commands . Youtube ( message . Message [ 10 : len ( message . Message ) ] )
case "godocs" :
reply = commands . Godocs ( message . Message [ 9 : len ( message . Message ) ] )
case "google" :
reply = commands . Google ( message . Message [ 9 : len ( message . Message ) ] )
case "duckduckgo" :
reply = commands . DuckDuckGo ( message . Message [ 13 : len ( message . Message ) ] )
case "seventv" :
reply = commands . SevenTV ( cmdParams [ 1 ] )
case "7tv" :
reply = commands . SevenTV ( cmdParams [ 1 ] )
2023-09-08 01:36:41 +02:00
case "lastfm" :
if msgLen == 1 {
reply = app . UserCheckLastFM ( message )
} else {
// Default to first argument supplied being the name
// of the user to look up recently played.
2023-10-11 18:52:46 +02:00
reply = lastfm . LastFmUserRecent ( target , cmdParams [ 1 ] )
2023-09-08 01:36:41 +02:00
}
2023-10-10 13:16:30 +02:00
case "help" :
if msgLen > 1 {
2023-10-10 17:51:12 +02:00
app . commandHelp ( target , cmdParams [ 1 ] , message . User . Name , message )
2023-10-10 13:16:30 +02:00
}
2023-08-08 02:37:37 +02:00
case "nourybot" :
2023-10-23 13:48:27 +02:00
reply = "Lidl Twitch bot made by @nouryxd. Prefix: ()"
2023-08-08 02:37:37 +02:00
2023-09-07 21:09:33 +02:00
case "phonetic" :
if msgLen == 1 {
reply = "Not enough arguments provided. Usage: ()phonetic <text to translate>"
} else {
reply , _ = commands . Phonetic ( message . Message [ 11 : len ( message . Message ) ] )
}
2023-08-08 02:37:37 +02:00
case "ping" :
2023-09-07 20:12:09 +02:00
reply = commands . Ping ( )
2023-08-08 02:37:37 +02:00
// ()bttv <emote name>
2023-09-07 20:43:16 +02:00
// ()weather <location>
case "weather" :
if msgLen == 1 {
2023-09-08 01:36:41 +02:00
app . UserCheckWeather ( message )
2023-09-07 20:43:16 +02:00
} else if msgLen < 2 {
reply = "Not enough arguments provided."
} else {
2023-10-11 18:52:46 +02:00
reply , _ = owm . Weather ( message . Message [ 10 : len ( message . Message ) ] )
2023-09-07 20:43:16 +02:00
}
2023-09-07 21:09:33 +02:00
case "rxkcd" :
reply , _ = commands . RandomXkcd ( )
case "randomxkcd" :
reply , _ = commands . RandomXkcd ( )
// Latest Xkcd
case "xkcd" :
reply , _ = commands . Xkcd ( )
2023-10-11 18:01:32 +02:00
case "uid" :
reply = ivr . IDByUsername ( cmdParams [ 1 ] )
case "set" :
2023-09-07 22:34:53 +02:00
switch cmdParams [ 1 ] {
2023-10-11 18:01:32 +02:00
case "lastfm" :
app . SetUserLastFM ( cmdParams [ 2 ] , message )
case "location" :
app . SetUserLocation ( message )
2023-09-07 22:34:53 +02:00
}
2023-10-11 18:01:32 +02:00
// --------------------------------
2023-10-11 21:35:47 +02:00
// 100 user level
// trusted
// vip
2023-10-11 18:01:32 +02:00
// --------------------------------
2023-09-08 03:03:57 +02:00
case "debug" :
switch cmdParams [ 1 ] {
case "user" :
2023-10-11 21:35:47 +02:00
if userLevel >= 100 {
2023-09-19 14:04:58 +02:00
app . DebugUser ( cmdParams [ 2 ] , message )
}
2023-09-14 17:40:35 +02:00
case "command" :
2023-10-11 21:35:47 +02:00
if userLevel >= 100 {
2023-09-19 14:04:58 +02:00
app . DebugCommand ( cmdParams [ 2 ] , message )
}
2023-09-08 03:03:57 +02:00
}
2023-10-11 21:35:47 +02:00
// --------------------------------
// 250 user level
// twitch mod/broadcaster
// --------------------------------
// empty for now
// --------------------------------
// 420 user level
// dank
// --------------------------------
case "catbox" :
if userLevel >= 420 {
go app . NewDownload ( "catbox" , target , cmdParams [ 1 ] , message )
}
case "kappa" :
if userLevel >= 420 {
go app . NewDownload ( "kappa" , target , cmdParams [ 1 ] , message )
}
case "yaf" :
if userLevel >= 420 {
go app . NewDownload ( "yaf" , target , cmdParams [ 1 ] , message )
}
case "gofile" :
if userLevel >= 420 {
go app . NewDownload ( "gofile" , target , cmdParams [ 1 ] , message )
}
//----------------------------------
// 500 User Level
// trusted
//---------------------------------
2023-10-11 18:01:32 +02:00
case "timer" :
switch cmdParams [ 1 ] {
case "add" :
2023-10-11 21:35:47 +02:00
if userLevel >= 500 {
2023-10-11 18:01:32 +02:00
app . AddTimer ( cmdParams [ 2 ] , cmdParams [ 3 ] , message )
}
case "edit" :
2023-10-11 21:35:47 +02:00
if userLevel >= 500 {
2023-10-11 18:01:32 +02:00
app . EditTimer ( cmdParams [ 2 ] , cmdParams [ 3 ] , message )
}
case "delete" :
2023-10-11 21:35:47 +02:00
if userLevel >= 500 {
2023-10-11 18:01:32 +02:00
app . DeleteTimer ( cmdParams [ 2 ] , message )
}
case "list" :
2023-10-11 21:35:47 +02:00
if userLevel >= 500 {
2023-10-11 18:01:32 +02:00
reply = app . ListTimers ( )
}
}
2023-09-08 00:18:09 +02:00
case "command" :
switch cmdParams [ 1 ] {
case "add" :
2023-10-11 21:35:47 +02:00
if userLevel >= 500 {
2023-10-11 18:01:32 +02:00
app . AddCommand ( cmdParams [ 2 ] , message )
}
2023-09-08 00:18:09 +02:00
case "delete" :
2023-10-11 21:35:47 +02:00
if userLevel >= 500 {
2023-10-11 18:01:32 +02:00
app . DeleteCommand ( cmdParams [ 2 ] , message )
}
2023-12-03 17:24:42 +01:00
case "list" :
if userLevel >= 500 {
reply = app . ListCommands ( )
}
2023-09-08 00:18:09 +02:00
case "edit" :
switch cmdParams [ 2 ] {
case "level" :
2023-10-11 21:35:47 +02:00
if userLevel >= 500 {
2023-10-11 18:01:32 +02:00
app . EditCommandLevel ( cmdParams [ 3 ] , cmdParams [ 4 ] , message )
}
2023-09-08 00:18:09 +02:00
case "category" :
2023-10-11 21:35:47 +02:00
if userLevel >= 500 {
2023-10-11 18:01:32 +02:00
app . EditCommandCategory ( cmdParams [ 3 ] , cmdParams [ 4 ] , message )
}
2023-09-08 00:18:09 +02:00
}
}
2023-10-11 18:01:32 +02:00
//------------------------------------
// 1000 User Level
2023-10-11 21:35:47 +02:00
// Admin
2023-10-11 18:01:32 +02:00
//------------------------------------
case "join" :
if userLevel >= 1000 {
go app . AddChannel ( cmdParams [ 1 ] , message )
}
case "part" :
if userLevel >= 1000 {
go app . DeleteChannel ( cmdParams [ 1 ] , message )
2023-09-08 01:36:41 +02:00
}
case "user" :
switch cmdParams [ 1 ] {
case "edit" :
switch cmdParams [ 2 ] {
case "level" :
2023-10-11 18:01:32 +02:00
if userLevel >= 1000 {
app . EditUserLevel ( cmdParams [ 3 ] , cmdParams [ 4 ] , message )
}
}
case "set" :
switch cmdParams [ 2 ] {
case "level" :
if userLevel >= 1000 {
app . EditUserLevel ( cmdParams [ 3 ] , cmdParams [ 4 ] , message )
}
2023-09-08 01:36:41 +02:00
}
}
2023-10-10 11:58:42 +02:00
default :
r , err := app . GetCommand ( target , commandName , userLevel )
if err != nil {
return
}
reply = r
2023-08-08 02:37:37 +02:00
}
2023-09-07 20:12:09 +02:00
if reply != "" {
2023-10-10 17:51:12 +02:00
go app . Send ( target , reply , message )
2023-09-07 20:12:09 +02:00
return
}
2023-08-08 02:37:37 +02:00
}
2023-10-10 13:16:30 +02:00
// 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 <emote name>" ,
"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 <emote name>" ,
"followage" : "Returns how long a given user has been following a channel. Example usage: ()followage <channel> <username>" ,
"firstline" : "Returns the first message a user has sent in a given channel. Aliases: firstline, fl. Example usage: ()firstline <channel> <username>" ,
"fl" : "Returns the first message a user has sent in a given channel. Aliases: firstline, fl. Example usage: ()fl <channel> <username>" ,
"help" : "Returns more information about a command and its usage. 4Head Example usage: ()help <command name>" ,
"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 <channel>" ,
"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 <channel>" ,
"tweet" : "Returns the latest tweet for a provided user. Example usage: ()tweet <username>" ,
"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" ,
}
// Help checks if a help text for a given command exists and replies with it.
2023-10-10 17:51:12 +02:00
func ( app * application ) commandHelp ( target , name , username string , message twitch . PrivateMessage ) {
2023-10-10 13:16:30 +02:00
// 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 . Log . Infow ( "commandHelp: no such command found" ,
"err" , err )
return
}
2023-10-10 17:51:12 +02:00
app . Send ( target , c , message )
2023-10-10 13:16:30 +02:00
return
}
2023-10-10 17:51:12 +02:00
app . Send ( target , i , message )
2023-10-10 13:16:30 +02:00
}