diff --git a/README.md b/README.md index f5efed3..b6d110d 100644 --- a/README.md +++ b/README.md @@ -7,10 +7,12 @@ BirbMC Discord generi-bot. ### Public * `register ` - Register to the Discord -* `links` - Get a list of dynamic links - * `` - Get a specific link +* `echoes` - Get a list of dynamic echo messages + * `` - Get a specific echo message * `fired` - Check how many times Carolyn has been fired * `inspire` - Get a random "inspirational" message from [InspiroBot](https://inspirobot.me) +* `dad` - Get a random dad joke from [icanhazdadjoke](https://icanhazdadjoke.com) +* `insult` - The fan favorite returns ### Moderation diff --git a/config/config.go b/config/config.go index 6b59878..6fa1b23 100644 --- a/config/config.go +++ b/config/config.go @@ -22,6 +22,13 @@ type Config struct { RegisterRole string `toml:"register_role"` RegisteredChannel string `toml:"registered_channel"` FiredRole string `toml:"fired_role"` + MemeRate string `toml:"meme_rate"` + Insult struct { + Targets []string `toml:"target"` + Comparisons []string `toml:"comparison"` + Adjectives []string `toml:"adjectives"` + Nouns []string `toml:"nouns"` + } `toml:"insult"` } type MessageRole struct { diff --git a/discord/dad.go b/discord/dad.go index 84a6fb3..10d999e 100644 --- a/discord/dad.go +++ b/discord/dad.go @@ -23,6 +23,10 @@ func init() { return true }, run: func(cmd commandInit) (string, error) { + if !memeRateLimit.Try() { + return "Woah, slow down!", nil + } + req, err := http.NewRequest(http.MethodGet, dadJokeAPI, nil) if err != nil { return "", err diff --git a/discord/discord.go b/discord/discord.go index 8e4fed6..d3e1e35 100644 --- a/discord/discord.go +++ b/discord/discord.go @@ -2,6 +2,7 @@ package discord import ( "strings" + "time" "go.etztech.xyz/sedbot/config" "go.etztech.xyz/sedbot/database" @@ -14,6 +15,8 @@ import ( var ( commands = make(map[string]command) messageRoleMap = make(map[string]map[string]string) + + memeRateLimit *rateLimit ) type commandInit struct { @@ -50,6 +53,13 @@ func Bot(cfg *config.Config, db *database.Database) (*discordgo.Session, error) bot.AddHandler(reactionAddHandler()) bot.AddHandler(reactionRemoveHandler()) + // Rate limits + d, err := time.ParseDuration(cfg.MemeRate) + if err != nil { + return nil, err + } + memeRateLimit = NewRateLimit(d) + return bot, nil } diff --git a/discord/discord_test.go b/discord/discord_test.go new file mode 100644 index 0000000..498ef79 --- /dev/null +++ b/discord/discord_test.go @@ -0,0 +1,10 @@ +package discord + +import ( + "os" + "testing" +) + +func TestMain(m *testing.M) { + os.Exit(m.Run()) +} diff --git a/discord/echo.go b/discord/echo.go index f249772..b450a02 100644 --- a/discord/echo.go +++ b/discord/echo.go @@ -33,7 +33,7 @@ func Echo(cfg *config.Config) { } } combined := append([]string{echo.Name}, echo.Aliases...) - echoes = append(echoes, fmt.Sprintf("%s: %s", strings.Join(combined, ", "), echo.Help)) + echoes = append(echoes, fmt.Sprintf("**%s**: %s", strings.Join(combined, ", "), echo.Help)) } commands["echoes"] = command{ diff --git a/discord/inspire.go b/discord/inspire.go index 0772bcd..d412128 100644 --- a/discord/inspire.go +++ b/discord/inspire.go @@ -8,6 +8,10 @@ func init() { return true }, run: func(cmd commandInit) (string, error) { + if !memeRateLimit.Try() { + return "Woah, slow down!", nil + } + sendTyping(cmd.session, cmd.message.ChannelID) img, err := inspiro.Generate() diff --git a/discord/insult.go b/discord/insult.go new file mode 100644 index 0000000..eaeb839 --- /dev/null +++ b/discord/insult.go @@ -0,0 +1,41 @@ +package discord + +import ( + "fmt" + r "math/rand" + "time" +) + +func init() { + commands["insult"] = command{ + validate: func(cmd commandInit) bool { + return true + }, + run: func(cmd commandInit) (string, error) { + if !memeRateLimit.Try() { + return "Woah, slow down!", nil + } + + args, err := cmd.message.ContentWithMoreMentionsReplaced(cmd.session) + if err != nil { + return "", err + } + insult := fmt.Sprintf("%s, your %s looks like %s, you %s %s.", + args, + random(cmd.config.Insult.Targets), + random(cmd.config.Insult.Comparisons), + random(cmd.config.Insult.Adjectives), + random(cmd.config.Insult.Nouns), + ) + return insult, nil + }, + help: "Insult someone!", + } +} + +var rand = r.New(r.NewSource(time.Now().Unix())) + +func random(list []string) string { + idx := rand.Intn(len(list) - 1) + return list[idx] +} diff --git a/discord/utils.go b/discord/utils.go new file mode 100644 index 0000000..4fcab16 --- /dev/null +++ b/discord/utils.go @@ -0,0 +1,24 @@ +package discord + +import "time" + +type rateLimit struct { + rate time.Duration + next time.Time +} + +func NewRateLimit(rate time.Duration) *rateLimit { + return &rateLimit{ + rate: rate, + next: time.Now(), + } +} + +func (r *rateLimit) Try() bool { + now := time.Now() + if now.After(r.next) { + r.next = now.Add(r.rate) + return true + } + return false +} diff --git a/discord/utils_test.go b/discord/utils_test.go new file mode 100644 index 0000000..f0d2e04 --- /dev/null +++ b/discord/utils_test.go @@ -0,0 +1,43 @@ +package discord + +import ( + "testing" + "time" +) + +func TestRateLimit(t *testing.T) { + now := func() { + t.Logf("Time: %s", time.Now().Format("15:04:05")) + } + + now() + limit := NewRateLimit(time.Second * 2) + + if ok := limit.Try(); !ok { + now() + t.Log("First try: Rate limit should pass.") + t.FailNow() + } + + if ok := limit.Try(); ok { + now() + t.Log("Second try: Rate limit should fail.") + t.FailNow() + } + + time.Sleep(time.Second) + + if ok := limit.Try(); ok { + now() + t.Log("Third try: Rate limit should fail.") + t.FailNow() + } + + time.Sleep(time.Second) + + if ok := limit.Try(); !ok { + now() + t.Log("Fourth try: Rate limit should pass.") + t.FailNow() + } +} diff --git a/sedbot.example.toml b/sedbot.example.toml index cc02ab7..29e3758 100644 --- a/sedbot.example.toml +++ b/sedbot.example.toml @@ -25,6 +25,17 @@ registered_channel = "0" # staff_roles are for staff commands staff_roles = [] +# meme_rate is the rate limit for memes +meme_rate = "3s" + +# insults +# , your looks like , you +[insult] +targets = [] +comparisons = [] +adjectives = [] +nouns = [] + # echoes are any basic command -> message [[echoes]] name = "discord"