Compare commits

...

13 Commits

Author SHA1 Message Date
DutchEllie
780fe972cd fix apparent mistake 2021-06-04 16:21:36 +02:00
b64a4096e9 implement? 2021-06-04 16:08:44 +02:00
661ce08764 log 2021-06-04 16:07:19 +02:00
062e9276f2 packaged up server, added mutex 2021-06-04 09:54:28 +02:00
a8128b120a changed 2021-06-03 17:07:33 +02:00
eff943a715 fixed error message 2021-06-03 14:07:11 +02:00
8555a686a6 this might break it all 2021-06-03 13:52:53 +02:00
ebbbc3680d added middleware 2021-06-03 13:45:07 +02:00
DutchEllie
9f1d13d3aa added build 2021-06-03 13:28:47 +02:00
e56686ee3c added newsendpepe 2021-06-03 12:54:03 +02:00
0829c2a39d ready for testing?? 2021-06-03 12:50:42 +02:00
6e65f9165b renamed general 2021-06-03 11:29:43 +02:00
91c9230e05 basic idea 2021-06-03 11:29:34 +02:00
9 changed files with 218 additions and 20 deletions

View File

@ -1,8 +1,6 @@
package main package main
import ( import (
"strings"
"github.com/bwmarrin/discordgo" "github.com/bwmarrin/discordgo"
) )
@ -10,12 +8,20 @@ func (app *application) messageCreate(s *discordgo.Session, m *discordgo.Message
if m.Author.Bot { if m.Author.Bot {
return return
} }
app.limiter.LogInteraction(m.Author.ID, "messagecreate")
app.LogInteraction(app.LogToConsole(app.commandMux)).Execute(s, m)
/* if strings.HasPrefix(m.Content, "!newpepe") {
app.LogToConsole(app.commandMux).Execute(s, m)
return
} */
//app.limiter.LogInteraction(m.Author.ID, "messagecreate")
/* Check if the user is even allowed by the rate limiter */ /* Check if the user is even allowed by the rate limiter */
err := app.limiter.CheckAllowed(m.Author.ID) /* err := app.limiter.CheckAllowed(m.Author.ID)
if err != nil { if err != nil {
/* normally don't send, but now do for debug purposes. This is the admin bot channel */ /* normally don't send, but now do for debug purposes. This is the admin bot channel *
//app.unknownError(err, s, true, "815952128106430514") //app.unknownError(err, s, true, "815952128106430514")
app.infoLog.Printf("Rate limit exceeded by used %s", m.Author.Username) app.infoLog.Printf("Rate limit exceeded by used %s", m.Author.Username)
channel, err := s.UserChannelCreate(m.Author.ID) channel, err := s.UserChannelCreate(m.Author.ID)
@ -25,24 +31,24 @@ func (app *application) messageCreate(s *discordgo.Session, m *discordgo.Message
} }
s.ChannelMessageSend(channel.ID, "You exceeded the rate limit for the server, please stop spamming") s.ChannelMessageSend(channel.ID, "You exceeded the rate limit for the server, please stop spamming")
return return
} } */
/* Checking if the message starts with the trigger specified in application struct /* Checking if the message starts with the trigger specified in application struct
if it does then we start the switch statement to trigger the appropriate function if it does then we start the switch statement to trigger the appropriate function
if it does not then we check if it contains a triggerword from the database */ if it does not then we check if it contains a triggerword from the database */
if strings.HasPrefix(m.Content, app.trigger) { /* if strings.HasPrefix(m.Content, app.trigger) {
splitCommand := strings.Split(strings.TrimSpace(m.Content), " ") splitCommand := strings.Split(strings.TrimSpace(m.Content), " ")
/* If the whole message on it's own is just "!pepe" aka the triggerword, then send a pepe and stop */ /* If the whole message on it's own is just "!pepe" aka the triggerword, then send a pepe and stop *
if strings.TrimSpace(m.Content) == app.trigger { if strings.TrimSpace(m.Content) == app.trigger {
app.sendPepe(s, m) app.sendPepe(s, m)
return return
} }
/* This switches on the first word in the message /* This switches on the first word in the message
it could be anything starting with !pepe */ it could be anything starting with !pepe *
if len(splitCommand) > 1 { if len(splitCommand) > 1 {
switch splitCommand[1] { switch splitCommand[1] {
/* --- Funny commands --- */ /* --- Funny commands --- *
case "cringe": case "cringe":
app.sendCringe(s, m) app.sendCringe(s, m)
case "gif": case "gif":
@ -53,12 +59,12 @@ func (app *application) messageCreate(s *discordgo.Session, m *discordgo.Message
app.sendWednesday(s, m) app.sendWednesday(s, m)
case "github", "source": case "github", "source":
app.sendGithub(s, m) app.sendGithub(s, m)
/* --- Bot commands for words --- */ /* --- Bot commands for words --- *
case "spam": case "spam":
app.sendManyPepes(s, m, splitCommand) app.sendManyPepes(s, m, splitCommand)
case "stop": case "stop":
app.stopRequest(s, m) app.stopRequest(s, m)
/* --- Bot commands, but only admins --- */ /* --- Bot commands, but only admins --- *
case "addword": case "addword":
app.addWord(s, m, splitCommand) app.addWord(s, m, splitCommand)
case "removeword": case "removeword":
@ -71,8 +77,8 @@ func (app *application) messageCreate(s *discordgo.Session, m *discordgo.Message
} }
} else { } else {
/* If the trigger wasn't the prefix of the message, we need to check all the words for a trigger */ /* If the trigger wasn't the prefix of the message, we need to check all the words for a trigger
app.findTrigger(s, m) app.findTrigger(s, m)
} } */
} }

View File

@ -1 +0,0 @@
package main

7
discord/handlers.go Normal file
View File

@ -0,0 +1,7 @@
package main
import "github.com/bwmarrin/discordgo"
func newCringe(s *discordgo.Session, m *discordgo.MessageCreate) {
s.ChannelMessageSend(m.ChannelID, "this is a test message right from the new command system!")
}

View File

@ -13,7 +13,7 @@ import (
/* -------- DB Helper functions -------- */ /* -------- DB Helper functions -------- */
func openDB(dsn string) (*sql.DB, error){ func openDB(dsn string) (*sql.DB, error) {
db, err := sql.Open("mysql", dsn) db, err := sql.Open("mysql", dsn)
if err != nil { if err != nil {
return nil, err return nil, err
@ -25,7 +25,7 @@ func openDB(dsn string) (*sql.DB, error){
return db, nil return db, nil
} }
func (app *application) updateAllBadWords() (error) { func (app *application) updateAllBadWords() error {
var err error var err error
app.allBadWords, err = app.badwords.AllWords() app.allBadWords, err = app.badwords.AllWords()
if err != nil { if err != nil {
@ -39,7 +39,7 @@ func (app *application) updateAllBadWords() (error) {
func (app *application) unknownError(err error, s *discordgo.Session, notifyDiscord bool, channelID string) { func (app *application) unknownError(err error, s *discordgo.Session, notifyDiscord bool, channelID string) {
trace := fmt.Sprintf("%s\n%s", err.Error(), debug.Stack()) trace := fmt.Sprintf("%s\n%s", err.Error(), debug.Stack())
app.errorLog.Output(2, trace) app.errorLog.Output(2, trace)
if notifyDiscord { if notifyDiscord {
msg := fmt.Sprintf("An unknown error occured, error message attached below. Stack trace is in the server logs.\n%s", err.Error()) msg := fmt.Sprintf("An unknown error occured, error message attached below. Stack trace is in the server logs.\n%s", err.Error())
@ -61,4 +61,4 @@ func (app *application) readAuthToken() (string, error) {
} }
return string(token), nil return string(token), nil
} }

View File

@ -10,6 +10,7 @@ import (
"time" "time"
"github.com/bwmarrin/discordgo" "github.com/bwmarrin/discordgo"
"quenten.nl/pepebot/discord/mux"
"quenten.nl/pepebot/limiter" "quenten.nl/pepebot/limiter"
"quenten.nl/pepebot/models/mysql" "quenten.nl/pepebot/models/mysql"
) )
@ -25,9 +26,10 @@ type application struct {
trigger string trigger string
allBadWords map[string][]string allBadWords map[string][]string
limiter *limiter.Limiter limiter *limiter.Limiter
commandMux *mux.CommandMux
active bool active bool
stop bool stop bool
} }
func main() { func main() {
@ -61,6 +63,9 @@ func main() {
Logs: make(map[string][]*limiter.Action), Logs: make(map[string][]*limiter.Action),
} }
server := mux.NewCommandMux()
server.Prefix = "!pepe"
app := &application{ app := &application{
infoLog: infoLog, infoLog: infoLog,
errorLog: errorLog, errorLog: errorLog,
@ -68,6 +73,7 @@ func main() {
adminroles: &mysql.AdminRolesModel{DB: db}, adminroles: &mysql.AdminRolesModel{DB: db},
trigger: "!pepe", trigger: "!pepe",
limiter: limiter, limiter: limiter,
commandMux: server,
} }
app.allBadWords, err = app.badwords.AllWords() app.allBadWords, err = app.badwords.AllWords()
@ -75,6 +81,23 @@ func main() {
app.errorLog.Fatal(err) app.errorLog.Fatal(err)
} }
server.HandleFunc("cringe", app.sendCringe)
server.HandleFunc("gif", app.sendNigelGif)
server.HandleFunc("tuesday", app.sendTuesday)
server.HandleFunc("wednesday", app.sendWednesday)
server.HandleFunc("github", app.sendGithub)
server.HandleFunc("source", app.sendGithub)
/* The admin commands are left out for now.
They have specialised functions and don't work yet.
Their code is left unworking and nonfunctional to be fixed
sometime in the future... sometime
Another thing left out is the bad words feature.
It goes underused and has had it's joke.
Oh and no one must be sad to see the death of the spam command...*/
server.HandleFunc(server.Prefix, app.sendPepe)
/* token, err := app.readAuthToken() /* token, err := app.readAuthToken()
if err != nil { if err != nil {
app.errorLog.Fatal(err) app.errorLog.Fatal(err)

39
discord/middleware.go Normal file
View File

@ -0,0 +1,39 @@
package main
import (
"github.com/bwmarrin/discordgo"
"quenten.nl/pepebot/discord/mux"
"quenten.nl/pepebot/limiter"
)
/*
Middleware chain
Logtoconsole -> loginteraction -> mux -> command
*/
func (app *application) LogToConsole(next mux.Command) mux.Command {
fn := func(s *discordgo.Session, m *discordgo.MessageCreate) {
app.infoLog.Printf("%s \tsaid: %s\n", m.Author.Username, m.Content)
next.Execute(s, m)
}
return mux.HandlerFunc(fn)
}
func (app *application) LogInteraction(next mux.Command) mux.Command {
fn := func(s *discordgo.Session, m *discordgo.MessageCreate) {
// Logging interaction
a := limiter.NewAction("Any message")
app.limiter.Logs[m.Author.ID] = append(app.limiter.Logs[m.Author.ID], a)
// Checking if rate limit exceeded
err := app.limiter.CheckAllowed(m.Author.ID)
if err != nil {
mux.NotFound(s, m)
} else {
next.Execute(s, m)
}
}
return mux.HandlerFunc(fn)
}

114
discord/mux/server.go Normal file
View File

@ -0,0 +1,114 @@
package mux
import (
"strings"
"sync"
"github.com/bwmarrin/discordgo"
)
type Command interface {
Execute(s *discordgo.Session, m *discordgo.MessageCreate)
}
func NotFound(s *discordgo.Session, m *discordgo.MessageCreate) {
return
}
func NotFoundHandler() Command { return HandlerFunc(NotFound) }
type HandlerFunc func(s *discordgo.Session, m *discordgo.MessageCreate)
func (f HandlerFunc) Execute(s *discordgo.Session, m *discordgo.MessageCreate) {
f(s, m)
}
/* The CommandMux struct is a type of mux for Discord commands. It's modelled after the net/http ServeMux */
type CommandMux struct {
mu sync.RWMutex
m map[string]muxEntry
Prefix string
}
func NewCommandMux() *CommandMux { return new(CommandMux) }
func (c *CommandMux) removeFirst(command string) string {
split := strings.SplitN(strings.TrimSpace(command), " ", 2)
if len(split) > 1 {
return split[1]
}
return ""
}
func (c *CommandMux) firstCommand(command string) string {
split := strings.SplitN(strings.TrimSpace(command), " ", 2)
if len(split) > 0 {
return split[0]
}
return ""
}
func (c *CommandMux) Handler(m *discordgo.MessageCreate) (cmd Command, pattern string) {
c.mu.RLock()
defer c.mu.RUnlock()
if strings.HasPrefix(m.Content, c.Prefix) {
/* Special case for this bot alone. It has a command that is only it's prefix
So we check if the whole message is only the prefix before proceding.
So please don't forget to add the command, since it's totally hardcoded here. */
if strings.TrimSpace(m.Content) == c.Prefix {
return c.m[c.Prefix].h, c.m[c.Prefix].pattern
}
m := c.removeFirst(m.Content) /* Here the prefix is removed, so we're left with only the first keyword */
cmd, ok := c.m[c.firstCommand(m)]
if ok {
return cmd.h, cmd.pattern
}
}
/* Here is where I might add the whole checking for bad words part */
return nil, ""
}
func (c *CommandMux) Execute(s *discordgo.Session, m *discordgo.MessageCreate) {
h, _ := c.Handler(m)
if h == nil {
//log.Printf("There exists no handler for %s\n", m.Content)
return
}
h.Execute(s, m)
}
func (c *CommandMux) Handle(pattern string, handler Command) {
c.mu.Lock()
defer c.mu.Unlock()
if pattern == "" {
panic("commandmux: invalid pattern")
}
if handler == nil {
panic("commandmux: nil handler")
}
if _, exist := c.m[pattern]; exist {
panic("commandmux: multiple registrations for " + pattern)
}
if c.m == nil {
c.m = make(map[string]muxEntry)
}
e := muxEntry{h: handler, pattern: pattern}
c.m[pattern] = e
}
func (c *CommandMux) HandleFunc(pattern string, handler func(s *discordgo.Session, m *discordgo.MessageCreate)) {
if handler == nil {
panic("commandmux: nil handler")
}
c.Handle(pattern, HandlerFunc(handler))
}
/* The muxEntry struct contains the actual Command implementation as well as the pattern (discord command)
it will be matched against */
type muxEntry struct {
h Command
pattern string
}

View File

@ -3,6 +3,7 @@ version: "3.7"
services: services:
app: app:
container_name: pepebot_server container_name: pepebot_server
image: dutchellie/pepebot
build: . build: .
restart: always restart: always
depends_on: depends_on:
@ -11,6 +12,8 @@ services:
- DB_USER= - DB_USER=
- DB_PASS= - DB_PASS=
- DISCORD_TOKEN= - DISCORD_TOKEN=
- RATE_LIMIT=
- TIME_LIMIT=
db: db:
container_name: pepebot_database container_name: pepebot_database
image: mysql:8.0 image: mysql:8.0

View File

@ -6,3 +6,10 @@ type Action struct {
Type string Type string
Timestamp time.Time Timestamp time.Time
} }
func NewAction(t string) *Action {
a := new(Action)
a.Timestamp = time.Now()
a.Type = t
return a
}