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
20 changed files with 317 additions and 582 deletions

View File

@ -1,4 +0,0 @@
.drone.yml
docker-ignore.yml
LICENCE
README.md

View File

@ -1,231 +0,0 @@
kind: pipeline
type: kubernetes
name: pepebot_build_amd64
platform:
os: linux
arch: amd64
steps:
- name: build-pepebot
image: golang:1.16.4
volumes:
- name: build
path: /drone/src/build
environment:
CGO_ENABLED: 0
GOOS: linux
commands:
- go build -a -installsuffix cgo -o ./build/app ./discord
- name: build-publish-image
image: plugins/docker
privileged: true
volumes:
- name: build
path: /drone/src/build
settings:
cache_from:
- "dutchellie/pepebot:latest"
username:
from_secret: docker_username
password:
from_secret: docker_password
dockerfile: Dockerfile
repo: dutchellie/pepebot
tags:
- latest-amd64
volumes:
- name: build
temp: {}
---
kind: pipeline
type: kubernetes
name: pepebot_service_build_amd64
platform:
os: linux
arch: amd64
steps:
- name: build-pepebot_service
image: golang:1.17
volumes:
- name: build
path: /drone/src/build
environment:
CGO_ENABLED: 0
GOOS: linux
commands:
- cd ./pepeservice
- go build -a -installsuffix cgo -o ../build/app .
- name: build-service
image: plugins/docker
privileged: true
volumes:
- name: build
path: /drone/src/build
settings:
cache_from:
- "dutchellie/pepebot_service:latest"
username:
from_secret: docker_username
password:
from_secret: docker_password
dockerfile: pepeservice/Dockerfile
repo: dutchellie/pepebot_service
tags:
- latest-amd64
volumes:
- name: build
temp: {}
---
kind: pipeline
type: docker
name: pepebot_build_arm64
platform:
os: linux
arch: arm64
steps:
- name: build-pepebot
image: golang:1.16.4
volumes:
- name: build
path: /drone/src/build
environment:
CGO_ENABLED: 0
GOOS: linux
commands:
- go build -a -installsuffix cgo -o ./build/app ./discord
- name: build-publish-image
image: plugins/docker
privileged: true
volumes:
- name: build
path: /drone/src/build
settings:
cache_from:
- "dutchellie/pepebot:latest"
username:
from_secret: docker_username
password:
from_secret: docker_password
dockerfile: Dockerfile
repo: dutchellie/pepebot
tags:
- latest-arm64
volumes:
- name: build
temp: {}
---
kind: pipeline
type: docker
name: pepebot_service_build_arm64
platform:
os: linux
arch: arm64
steps:
- name: build-pepebot_service
image: golang:1.17
volumes:
- name: build
path: /drone/src/build
environment:
CGO_ENABLED: 0
GOOS: linux
commands:
- cd ./pepeservice
- go build -a -installsuffix cgo -o ../build/app .
- name: build-service
image: plugins/docker
privileged: true
volumes:
- name: build
path: /drone/src/build
settings:
cache_from:
- "dutchellie/pepebot_service:latest"
username:
from_secret: docker_username
password:
from_secret: docker_password
dockerfile: pepeservice/Dockerfile
repo: dutchellie/pepebot_service
tags:
- latest-arm64
volumes:
- name: build
temp: {}
---
kind: pipeline
type: kubernetes
name: manifest
steps:
- name: manifest-main
image: plugins/manifest
settings:
platforms:
- linux/amd64
- linux/arm64
target: dutchellie/pepebot:latest
template: dutchellie/pepebot:latest-ARCH
username:
from_secret: docker_username
password:
from_secret: docker_password
- name: manifest-pepebotservice
image: plugins/manifest
settings:
platforms:
- linux/amd64
- linux/arm64
target: dutchellie/pepebot_service:latest
template: dutchellie/pepebot_service:latest-ARCH
username:
from_secret: docker_username
password:
from_secret: docker_password
depends_on:
- pepebot_build_arm64
- pepebot_build_amd64
- pepebot_service_build_arm64
- pepebot_service_build_amd64
---
kind: pipeline
type: kubernetes
name: deploy_on_pi
trigger:
branch:
- main
steps:
- name: deploy
image: appleboy/drone-ssh
settings:
host: home.dutchellie.nl
port: 223
username: ellie
key:
from_secret: ssh_key
script:
- cd /home/ellie/pepebot
- docker-compose pull app
- docker-compose pull pepe_service
- docker-compose up -d
depends_on:
- manifest

4
.gitignore vendored
View File

@ -1,3 +1 @@
discordtoken.txt discordtoken.txt
deploy_ed25519
deploy_ed25519.pub

View File

@ -1,13 +1,13 @@
# syntax=docker/dockerfile:1 # syntax=docker/dockerfile:1
#FROM golang:1.16.4-alpine AS builder FROM golang:1.16.4 AS builder
#WORKDIR /go/src/quenten.nl/pepebot/ WORKDIR /go/src/quenten.nl/pepebot/
#
#COPY . ./ COPY . ./
#RUN go mod download RUN go mod download
#RUN CGO_ENABLED=0 GOOS=linux go build -a -installsuffix cgo -o app ./discord RUN CGO_ENABLED=0 GOOS=linux go build -a -installsuffix cgo -o app ./discord
FROM alpine:latest FROM alpine:latest
RUN apk --no-cache add ca-certificates RUN apk --no-cache add ca-certificates
COPY ./build/app /root WORKDIR /root/
WORKDIR /root COPY --from=builder /go/src/quenten.nl/pepebot/app .
CMD ["./app"] CMD ["./app"]

View File

@ -1,10 +1,6 @@
package main package main
import ( import "github.com/bwmarrin/discordgo"
"net/http"
"github.com/bwmarrin/discordgo"
)
func (app *application) addWord(s *discordgo.Session, m *discordgo.MessageCreate, splitCommand []string) { func (app *application) addWord(s *discordgo.Session, m *discordgo.MessageCreate, splitCommand []string) {
/* Check if admin */ /* Check if admin */
@ -30,6 +26,8 @@ func (app *application) addWord(s *discordgo.Session, m *discordgo.MessageCreate
return return
} }
err = app.updateAllBadWords() err = app.updateAllBadWords()
if err != nil { if err != nil {
app.unknownError(err, s, true, m.ChannelID) app.unknownError(err, s, true, m.ChannelID)
@ -166,27 +164,4 @@ func (app *application) removeAdmin(s *discordgo.Session, m *discordgo.MessageCr
app.successMessage(s, m) app.successMessage(s, m)
} }
func (app *application) reloadPepeList(s *discordgo.Session, m *discordgo.MessageCreate) {
/* Check if admin */
r, err := app.checkIfAdmin(s, m)
if err != nil {
app.errorLog.Print(err)
return
}
if !r {
return
}
s.ChannelMessageSend(m.ChannelID, "Reloading list of pepes")
url := "http://" + app.pepeServer + "/reload"
_, err = http.Get(url)
if err != nil {
app.errorLog.Print(err)
s.ChannelMessageSend(m.ChannelID, "An error occured!")
return
}
app.successMessage(s, m)
}

View File

@ -1,9 +1,6 @@
package main package main
import ( import (
"strings"
"time"
"github.com/bwmarrin/discordgo" "github.com/bwmarrin/discordgo"
) )
@ -11,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)
@ -26,55 +31,40 @@ 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
} } */
/*
Temporary checking for Ben's birthday
*/
if time.Now().Month() == time.May && time.Now().Day() == 19 && m.Author.ID == "287892688919986176" {
s.ChannelMessageSend(m.ChannelID, "Happy birthday ben!! https://www.youtube.com/watch?v=z21HOwUk5oM")
}
/* 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":
app.sendNigelGif(s, m) app.sendNigelGif(s, m)
case "monday":
app.sendMonday(s, m)
case "tuesday": case "tuesday":
app.sendTuesday(s, m) app.sendTuesday(s, m)
case "wednesday": case "wednesday":
app.sendWednesday(s, m) app.sendWednesday(s, m)
case "friday":
app.sendFriday(s, m)
case "github", "source": case "github", "source":
app.sendGithub(s, m) app.sendGithub(s, m)
case "peski", "rotterdam": /* --- Bot commands for words --- *
app.sendPeski(s, m)
case "proper", "based":
app.sendProper(s, m)
/* --- 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":
@ -83,14 +73,12 @@ func (app *application) messageCreate(s *discordgo.Session, m *discordgo.Message
app.addAdmin(s, m, splitCommand) app.addAdmin(s, m, splitCommand)
case "removeadmin": case "removeadmin":
app.removeAdmin(s, m, splitCommand) app.removeAdmin(s, m, splitCommand)
case "reload":
app.reloadPepeList(s, m)
} }
} }
} 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"
) )
@ -24,11 +25,11 @@ type application struct {
adminroles *mysql.AdminRolesModel adminroles *mysql.AdminRolesModel
trigger string trigger string
allBadWords map[string][]string allBadWords map[string][]string
pepeServer string
limiter *limiter.Limiter limiter *limiter.Limiter
commandMux *mux.CommandMux
active bool active bool
stop bool stop bool
} }
func main() { func main() {
@ -37,7 +38,6 @@ func main() {
discordToken := os.Getenv("DISCORD_TOKEN") discordToken := os.Getenv("DISCORD_TOKEN")
rateLimit := os.Getenv("RATE_LIMIT") rateLimit := os.Getenv("RATE_LIMIT")
timeLimit := os.Getenv("TIME_LIMIT") timeLimit := os.Getenv("TIME_LIMIT")
pepeServer := os.Getenv("PEPE_SERVER")
dsn := fmt.Sprintf("%s:%s@tcp(db:3306)/badwords?parseTime=true", dbUser, dbPass) dsn := fmt.Sprintf("%s:%s@tcp(db:3306)/badwords?parseTime=true", dbUser, dbPass)
infoLog := log.New(os.Stdout, "INFO\t", log.Ldate|log.Ltime) infoLog := log.New(os.Stdout, "INFO\t", log.Ldate|log.Ltime)
errorLog := log.New(os.Stderr, "ERROR\t", log.Ldate|log.Ltime|log.Lshortfile) errorLog := log.New(os.Stderr, "ERROR\t", log.Ldate|log.Ltime|log.Lshortfile)
@ -63,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,
@ -70,7 +73,7 @@ func main() {
adminroles: &mysql.AdminRolesModel{DB: db}, adminroles: &mysql.AdminRolesModel{DB: db},
trigger: "!pepe", trigger: "!pepe",
limiter: limiter, limiter: limiter,
pepeServer: pepeServer, commandMux: server,
} }
app.allBadWords, err = app.badwords.AllWords() app.allBadWords, err = app.badwords.AllWords()
@ -78,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,9 +3,10 @@ package main
import ( import (
"errors" "errors"
"fmt" "fmt"
"io" "io/ioutil"
"math/rand" "math/rand"
"net/http" "net/http"
"regexp"
"strconv" "strconv"
"strings" "strings"
"time" "time"
@ -14,31 +15,30 @@ import (
) )
func (app *application) getPepeLink() (string, error) { func (app *application) getPepeLink() (string, error) {
url := "http://" + app.pepeServer + "/pepe" resp, err := http.Get("http://bbwroller.com/random")
resp, err := http.Get(url)
if err != nil { if err != nil {
return "", err return "", err
} }
body, err := ioutil.ReadAll(resp.Body)
body, err := io.ReadAll(resp.Body)
if err != nil { if err != nil {
return "", err return "", err
} }
rep, err := regexp.Compile("/static.*\\.jpg")
if err != nil {
return "", err
}
pepes := rep.FindAllString(string(body), 200)
if pepes == nil {
return "", err
}
randomIndex := rand.Intn(35)
url := "https://bbwroller.com"
url += pepes[randomIndex]
return string(body), nil return url, nil
} }
func (app *application) sendPepe(s *discordgo.Session, m *discordgo.MessageCreate) { func (app *application) sendPepe(s *discordgo.Session, m *discordgo.MessageCreate) {
ch, err := s.Channel(m.ChannelID)
if err != nil {
app.errorLog.Print(err)
return
}
if !strings.Contains(ch.Topic, "pepebot allowed") {
app.errorLog.Print("Can't send pepe within this channel")
return
}
url, err := app.getPepeLink() url, err := app.getPepeLink()
if err != nil { if err != nil {
app.errorLog.Print(err) app.errorLog.Print(err)
@ -69,22 +69,6 @@ func (app *application) sendNigelGif(s *discordgo.Session, m *discordgo.MessageC
} }
} }
func (app *application) sendMonday(s *discordgo.Session, m *discordgo.MessageCreate) {
if time.Now().Weekday().String() != "Monday" {
_, err := s.ChannelMessageSend(m.ChannelID, "This command only works on mondays")
if err != nil {
app.errorLog.Print(err)
return
}
return
}
_, err := s.ChannelMessageSend(m.ChannelID, "https://www.youtube.com/watch?v=EkALyaMjoXw")
if err != nil {
app.errorLog.Print(err)
return
}
}
func (app *application) sendTuesday(s *discordgo.Session, m *discordgo.MessageCreate) { func (app *application) sendTuesday(s *discordgo.Session, m *discordgo.MessageCreate) {
if time.Now().Weekday().String() != "Tuesday" { if time.Now().Weekday().String() != "Tuesday" {
_, err := s.ChannelMessageSend(m.ChannelID, "This command only works on tuesdays") _, err := s.ChannelMessageSend(m.ChannelID, "This command only works on tuesdays")
@ -126,46 +110,8 @@ func (app *application) sendWednesday(s *discordgo.Session, m *discordgo.Message
} }
} }
func (app *application) sendFriday(s *discordgo.Session, m *discordgo.MessageCreate) {
if time.Now().Weekday().String() != "Friday" {
_, err := s.ChannelMessageSend(m.ChannelID, "This command only works on fridays")
if err != nil {
app.errorLog.Print(err)
return
}
return
}
_, err := s.ChannelMessageSend(m.ChannelID, "https://www.youtube.com/watch?v=kfVsfOSbJY0")
if err != nil {
app.errorLog.Print(err)
return
}
}
func (app *application) sendGithub(s *discordgo.Session, m *discordgo.MessageCreate) { func (app *application) sendGithub(s *discordgo.Session, m *discordgo.MessageCreate) {
_, err := s.ChannelMessageSend(m.ChannelID, "My code is hosted publicly over at https://dutchellie.nl/DutchEllie/pepebot") _, err := s.ChannelMessageSend(m.ChannelID, "My code is hosted publicly over at https://github.com/DutchEllie/pepebot")
if err != nil {
app.errorLog.Print(err)
return
}
}
func (app *application) sendPeski(s *discordgo.Session, m *discordgo.MessageCreate) {
_, err := s.ChannelMessageSend(m.ChannelID, "https://www.youtube.com/watch?v=P0jHTCJYm44")
if err != nil {
app.errorLog.Print(err)
return
}
}
func (app *application) sendProper(s *discordgo.Session, m *discordgo.MessageCreate) {
responseList := []string{"That's proper", "I use Gentoo", "So based", "That's best practice", "Emacs is the best", "LISP is so fucking based", "My website is here https://forestofunix.xyz\nIt's written in Lisp. It's so based!", "Python is LISP based"}
source := rand.NewSource(time.Now().UnixNano())
rng := rand.New(source)
index := rng.Intn(len(responseList))
_, err := s.ChannelMessageSend(m.ChannelID, fmt.Sprintf("\"%s\"\n-Sebastiaan", responseList[index]))
if err != nil { if err != nil {
app.errorLog.Print(err) app.errorLog.Print(err)
return return
@ -173,16 +119,6 @@ func (app *application) sendProper(s *discordgo.Session, m *discordgo.MessageCre
} }
func (app *application) sendManyPepes(s *discordgo.Session, m *discordgo.MessageCreate, splitCommand []string) { func (app *application) sendManyPepes(s *discordgo.Session, m *discordgo.MessageCreate, splitCommand []string) {
ch, err := s.Channel(m.ChannelID)
if err != nil {
app.errorLog.Print(err)
return
}
if !strings.Contains(ch.Topic, "pepebot allowed") {
app.errorLog.Print("Can't send pepe within this channel")
return
}
override := false override := false
/* [0] is !pepe, [1] is spam, [2] is amount, [3] is override*/ /* [0] is !pepe, [1] is spam, [2] is amount, [3] is override*/
@ -205,7 +141,7 @@ func (app *application) sendManyPepes(s *discordgo.Session, m *discordgo.Message
//s.ChannelMessageSend(m.ChannelID, "You have to be admin to override, not overriding") //s.ChannelMessageSend(m.ChannelID, "You have to be admin to override, not overriding")
override = true override = true
} }
} }
} }
@ -237,12 +173,12 @@ func (app *application) sendManyPepes(s *discordgo.Session, m *discordgo.Message
app.errorLog.Print(err) app.errorLog.Print(err)
return return
} }
if len(msg+link) > 512 { if len(msg + link) > 1950 {
s.ChannelMessageSend(m.ChannelID, msg) s.ChannelMessageSend(m.ChannelID, msg)
msg = "" msg = ""
time.Sleep(time.Millisecond * 500) time.Sleep(time.Millisecond * 500)
} }
msg += link msg += link
msg += "\n" msg += "\n"
@ -261,7 +197,7 @@ func (app *application) stopRequest(s *discordgo.Session, m *discordgo.MessageCr
app.stop = false app.stop = false
s.ChannelMessageSend(m.ChannelID, "But I wasn't doing anything!") s.ChannelMessageSend(m.ChannelID, "But I wasn't doing anything!")
} }
} }
func (app *application) findTrigger(s *discordgo.Session, m *discordgo.MessageCreate) { func (app *application) findTrigger(s *discordgo.Session, m *discordgo.MessageCreate) {
@ -269,7 +205,7 @@ func (app *application) findTrigger(s *discordgo.Session, m *discordgo.MessageCr
Check if the message contains that word Check if the message contains that word
if it doesn't continue, if it doesn't continue,
if it does then get the word from the database, update the new time, format a message and send it */ if it does then get the word from the database, update the new time, format a message and send it */
for i := 0; i < len(app.allBadWords[m.GuildID]); i++ { for i := 0; i < len(app.allBadWords[m.GuildID]); i++{
if strings.Contains(strings.ToLower(m.Content), strings.ToLower(app.allBadWords[m.GuildID][i])) { if strings.Contains(strings.ToLower(m.Content), strings.ToLower(app.allBadWords[m.GuildID][i])) {
/* Found the bad word */ /* Found the bad word */
word, err := app.badwords.GetWord(strings.ToLower(app.allBadWords[m.GuildID][i]), m.GuildID) word, err := app.badwords.GetWord(strings.ToLower(app.allBadWords[m.GuildID][i]), m.GuildID)
@ -286,7 +222,7 @@ func (app *application) findTrigger(s *discordgo.Session, m *discordgo.MessageCr
user := m.Author.Mention() user := m.Author.Mention()
eyesEmoji := ":eyes:" eyesEmoji := ":eyes:"
message := fmt.Sprintf("%s mentioned the forbidden word '%s'. They broke a streak of %s...\nYou better watch out, I am always watching %s", user, word.Word, format, eyesEmoji) message := fmt.Sprintf("%s mentioned the forbidden word '%s'. They broke a streak of %s...\nYou better watch out, I am always watching %s", user, word.Word, format, eyesEmoji)
_, err = s.ChannelMessageSend(m.ChannelID, message) _ ,err = s.ChannelMessageSend(m.ChannelID, message)
if err != nil { if err != nil {
app.errorLog.Print(err) app.errorLog.Print(err)
return return
@ -296,84 +232,84 @@ func (app *application) findTrigger(s *discordgo.Session, m *discordgo.MessageCr
} }
} }
func formatTimeCheck(last time.Time) string { func formatTimeCheck(last time.Time) string{
now := time.Now() now := time.Now()
sinceLast := now.Sub(last) sinceLast := now.Sub(last)
var realSeconds uint64 = uint64(sinceLast.Seconds()) var realSeconds uint64 = uint64(sinceLast.Seconds())
var seconds, minutes, hours, days uint64 var seconds, minutes, hours, days uint64
realBackup := realSeconds realBackup := realSeconds
days = realSeconds / (24 * 3600) days = realSeconds / ( 24 * 3600 )
realSeconds -= days * (24 * 3600) realSeconds -= days * ( 24 * 3600 )
hours = realSeconds / 3600 hours = realSeconds / 3600
realSeconds -= hours * 3600 realSeconds -= hours * 3600
minutes = realSeconds / 60 minutes = realSeconds / 60
realSeconds -= minutes * 60 realSeconds -= minutes * 60
seconds = realSeconds seconds = realSeconds
if realBackup < 60 { if realBackup < 60{
if seconds == 1 { if seconds == 1{
return fmt.Sprintf("%d second", seconds) return fmt.Sprintf("%d second", seconds)
} }
return fmt.Sprintf("%d seconds", seconds) return fmt.Sprintf("%d seconds", seconds)
} else if realBackup > 60 && realBackup < 3600 { }else if realBackup > 60 && realBackup < 3600 {
if seconds == 1 && minutes == 1 { if seconds == 1 && minutes == 1{
return fmt.Sprintf("%d minute and %d second", minutes, seconds) return fmt.Sprintf("%d minute and %d second", minutes, seconds)
} else if minutes == 1 && seconds != 1 { }else if minutes == 1 && seconds != 1{
return fmt.Sprintf("%d minute and %d seconds", minutes, seconds) return fmt.Sprintf("%d minute and %d seconds", minutes, seconds)
} else if minutes != 1 && seconds == 1 { }else if minutes != 1 && seconds == 1{
return fmt.Sprintf("%d minutes and %d second", minutes, seconds) return fmt.Sprintf("%d minutes and %d second", minutes, seconds)
} }
return fmt.Sprintf("%d minutes and %d seconds", minutes, seconds) return fmt.Sprintf("%d minutes and %d seconds", minutes, seconds)
} else if realBackup > 60 && realBackup < (24*3600) { }else if realBackup > 60 && realBackup < ( 24 * 3600 ){
if hours == 1 && minutes == 1 && seconds == 1 { if hours == 1 && minutes == 1 && seconds == 1{
return fmt.Sprintf("%d hour, %d minute and %d second", hours, minutes, seconds) return fmt.Sprintf("%d hour, %d minute and %d second", hours, minutes, seconds)
} else if hours == 1 && minutes == 1 && seconds != 1 { }else if hours == 1 && minutes == 1 && seconds != 1 {
return fmt.Sprintf("%d hour, %d minute and %d seconds", hours, minutes, seconds) return fmt.Sprintf("%d hour, %d minute and %d seconds", hours, minutes, seconds)
} else if hours == 1 && minutes != 1 && seconds == 1 { }else if hours == 1 && minutes != 1 && seconds == 1{
return fmt.Sprintf("%d hour, %d minutes and %d second", hours, minutes, seconds) return fmt.Sprintf("%d hour, %d minutes and %d second", hours, minutes, seconds)
} else if hours == 1 && minutes != 1 && seconds != 1 { }else if hours == 1 && minutes != 1 && seconds != 1{
return fmt.Sprintf("%d hour, %d minutes and %d seconds", hours, minutes, seconds) return fmt.Sprintf("%d hour, %d minutes and %d seconds", hours, minutes, seconds)
} else if hours != 1 && minutes == 1 && seconds == 1 { }else if hours != 1 && minutes == 1 && seconds == 1{
return fmt.Sprintf("%d hours, %d minute and %d second", hours, minutes, seconds) return fmt.Sprintf("%d hours, %d minute and %d second", hours, minutes, seconds)
} else if hours != 1 && minutes == 1 && seconds != 1 { }else if hours != 1 && minutes == 1 && seconds != 1{
return fmt.Sprintf("%d hours, %d minute and %d seconds", hours, minutes, seconds) return fmt.Sprintf("%d hours, %d minute and %d seconds", hours, minutes, seconds)
} else if hours != 1 && minutes != 1 && seconds == 1 { }else if hours != 1 && minutes != 1 && seconds == 1{
return fmt.Sprintf("%d hours, %d minutes and %d second", hours, minutes, seconds) return fmt.Sprintf("%d hours, %d minutes and %d second", hours, minutes, seconds)
} else if hours != 1 && minutes != 1 && seconds != 1 { }else if hours != 1 && minutes != 1 && seconds != 1{
return fmt.Sprintf("%d hours, %d minutes and %d seconds", hours, minutes, seconds) return fmt.Sprintf("%d hours, %d minutes and %d seconds", hours, minutes, seconds)
} }
return fmt.Sprintf("%d hours, %d minutes and %d seconds", hours, minutes, seconds) return fmt.Sprintf("%d hours, %d minutes and %d seconds", hours, minutes, seconds)
} else if realBackup > (24 * 3600) { }else if realBackup > ( 24 * 3600 ){
if days != 1 && hours != 1 && minutes != 1 && seconds != 1 { if days != 1 && hours != 1 && minutes != 1 && seconds != 1{
return fmt.Sprintf("%d days, %d hours, %d minutes and %d seconds", days, hours, minutes, seconds) return fmt.Sprintf("%d days, %d hours, %d minutes and %d seconds", days, hours, minutes, seconds)
} else if days != 1 && hours != 1 && minutes != 1 && seconds == 1 { }else if days != 1 && hours != 1 && minutes != 1 && seconds == 1{
return fmt.Sprintf("%d days, %d hours, %d minutes and %d second", days, hours, minutes, seconds) return fmt.Sprintf("%d days, %d hours, %d minutes and %d second", days, hours, minutes, seconds)
} else if days != 1 && hours != 1 && minutes == 1 && seconds != 1 { }else if days != 1 && hours != 1 && minutes == 1 && seconds != 1{
return fmt.Sprintf("%d days, %d hours, %d minute and %d seconds", days, hours, minutes, seconds) return fmt.Sprintf("%d days, %d hours, %d minute and %d seconds", days, hours, minutes, seconds)
} else if days != 1 && hours != 1 && minutes == 1 && seconds == 1 { }else if days != 1 && hours != 1 && minutes == 1 && seconds == 1{
return fmt.Sprintf("%d days, %d hours, %d minute and %d second", days, hours, minutes, seconds) return fmt.Sprintf("%d days, %d hours, %d minute and %d second", days, hours, minutes, seconds)
} else if days != 1 && hours == 1 && minutes != 1 && seconds != 1 { }else if days != 1 && hours == 1 && minutes != 1 && seconds != 1{
return fmt.Sprintf("%d days, %d hour, %d minutes and %d seconds", days, hours, minutes, seconds) return fmt.Sprintf("%d days, %d hour, %d minutes and %d seconds", days, hours, minutes, seconds)
} else if days != 1 && hours == 1 && minutes != 1 && seconds == 1 { }else if days != 1 && hours == 1 && minutes != 1 && seconds == 1{
return fmt.Sprintf("%d days, %d hour, %d minutes and %d second", days, hours, minutes, seconds) return fmt.Sprintf("%d days, %d hour, %d minutes and %d second", days, hours, minutes, seconds)
} else if days != 1 && hours == 1 && minutes == 1 && seconds != 1 { }else if days != 1 && hours == 1 && minutes == 1 && seconds != 1{
return fmt.Sprintf("%d days, %d hour, %d minute and %d seconds", days, hours, minutes, seconds) return fmt.Sprintf("%d days, %d hour, %d minute and %d seconds", days, hours, minutes, seconds)
} else if days != 1 && hours == 1 && minutes == 1 && seconds == 1 { }else if days != 1 && hours == 1 && minutes == 1 && seconds == 1{
return fmt.Sprintf("%d days, %d hour, %d minute and %d second", days, hours, minutes, seconds) return fmt.Sprintf("%d days, %d hour, %d minute and %d second", days, hours, minutes, seconds)
} else if days == 1 && hours != 1 && minutes != 1 && seconds != 1 { }else if days == 1 && hours != 1 && minutes != 1 && seconds != 1{
return fmt.Sprintf("%d day, %d hours, %d minutes and %d seconds", days, hours, minutes, seconds) return fmt.Sprintf("%d day, %d hours, %d minutes and %d seconds", days, hours, minutes, seconds)
} else if days == 1 && hours != 1 && minutes != 1 && seconds == 1 { }else if days == 1 && hours != 1 && minutes != 1 && seconds == 1{
return fmt.Sprintf("%d day, %d hours, %d minutes and %d second", days, hours, minutes, seconds) return fmt.Sprintf("%d day, %d hours, %d minutes and %d second", days, hours, minutes, seconds)
} else if days == 1 && hours != 1 && minutes == 1 && seconds != 1 { }else if days == 1 && hours != 1 && minutes == 1 && seconds != 1{
return fmt.Sprintf("%d day, %d hours, %d minute and %d seconds", days, hours, minutes, seconds) return fmt.Sprintf("%d day, %d hours, %d minute and %d seconds", days, hours, minutes, seconds)
} else if days == 1 && hours != 1 && minutes == 1 && seconds == 1 { }else if days == 1 && hours != 1 && minutes == 1 && seconds == 1{
return fmt.Sprintf("%d day, %d hours, %d minute and %d second", days, hours, minutes, seconds) return fmt.Sprintf("%d day, %d hours, %d minute and %d second", days, hours, minutes, seconds)
} else if days == 1 && hours == 1 && minutes != 1 && seconds != 1 { }else if days == 1 && hours == 1 && minutes != 1 && seconds != 1{
return fmt.Sprintf("%d day, %d hour, %d minutes and %d seconds", days, hours, minutes, seconds) return fmt.Sprintf("%d day, %d hour, %d minutes and %d seconds", days, hours, minutes, seconds)
} else if days == 1 && hours == 1 && minutes != 1 && seconds == 1 { }else if days == 1 && hours == 1 && minutes != 1 && seconds == 1{
return fmt.Sprintf("%d day, %d hour, %d minutes and %d second", days, hours, minutes, seconds) return fmt.Sprintf("%d day, %d hour, %d minutes and %d second", days, hours, minutes, seconds)
} else if days == 1 && hours == 1 && minutes == 1 && seconds != 1 { }else if days == 1 && hours == 1 && minutes == 1 && seconds != 1{
return fmt.Sprintf("%d day, %d hour, %d minute and %d seconds", days, hours, minutes, seconds) return fmt.Sprintf("%d day, %d hour, %d minute and %d seconds", days, hours, minutes, seconds)
} else if days == 1 && hours == 1 && minutes == 1 && seconds == 1 { }else if days == 1 && hours == 1 && minutes == 1 && seconds == 1{
return fmt.Sprintf("%d day, %d hour, %d minute and %d second", days, hours, minutes, seconds) return fmt.Sprintf("%d day, %d hour, %d minute and %d second", days, hours, minutes, seconds)
} }
return fmt.Sprintf("%d days, %d hours, %d minutes and %d seconds", days, hours, minutes, seconds) return fmt.Sprintf("%d days, %d hours, %d minutes and %d seconds", days, hours, minutes, seconds)
@ -409,7 +345,7 @@ func (app *application) checkIfAdmin(s *discordgo.Session, m *discordgo.MessageC
return false, nil return false, nil
} }
func (app *application) contextLength(splitCommand []string) error { func (app *application) contextLength(splitCommand []string) (error) {
if !(len(splitCommand) > 2) { if !(len(splitCommand) > 2) {
app.errorLog.Printf("The command's context was not enough.\n") app.errorLog.Printf("The command's context was not enough.\n")
return errors.New("not enough context") return errors.New("not enough context")
@ -423,4 +359,4 @@ func (app *application) successMessage(s *discordgo.Session, m *discordgo.Messag
app.unknownError(err, s, true, m.ChannelID) app.unknownError(err, s, true, m.ChannelID)
return return
} }
} }

View File

@ -1,38 +1,30 @@
version: "3.7" version: "3.7"
services: services:
app: app:
container_name: pepebot_server container_name: pepebot_server
image: dutchellie/pepebot image: dutchellie/pepebot
build: . build: .
restart: always restart: always
depends_on: depends_on:
- db - db
environment: environment:
- DB_USER=changemeusername - DB_USER=
- DB_PASS=changemepassword - DB_PASS=
- DISCORD_TOKEN=<discord token> - DISCORD_TOKEN=
- RATE_LIMIT=2 # 2 Actions per TIME_LIMIT - RATE_LIMIT=
- TIME_LIMIT=5 # Per second - TIME_LIMIT=
- PEPE_SERVER=pepe_service:4000 db:
db: container_name: pepebot_database
container_name: pepebot_database image: mysql:8.0
image: mysql:8.0 restart: always
restart: always environment:
environment: MYSQL_ROOT_PASSWORD:
MYSQL_ROOT_PASSWORD: changeme MYSQL_DATABASE:
MYSQL_DATABASE: pepebot MYSQL_USER:
MYSQL_USER: changemeusername MYSQL_PASSWORD:
MYSQL_PASSWORD: changemepassword volumes:
volumes: - pepe_db_data:/var/lib/mysql
- pepe_db_data:/var/lib/mysql
pepe_service:
container_name: pepebot_service
image: dutchellie/pepebot_service:latest
build: pepeservice
restart: always
environment:
- PEPE_DIR=/pepe/1.00
volumes:
- /your/pepe/folder:/pepe #edit this
volumes: volumes:
pepe_db_data: {} pepe_db_data: {}

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
}

View File

@ -43,7 +43,7 @@ func (l *Limiter) CheckAllowed(userid string) error {
l.removeAction(userid, expiredEntries[i]) l.removeAction(userid, expiredEntries[i])
} }
if counter > l.RateLimit { if counter >= l.RateLimit {
return errors.New("rate limit exceeded") return errors.New("rate limit exceeded")
} else { } else {
return nil return nil

View File

@ -1,4 +0,0 @@
.drone.yml
docker-ignore.yml
LICENCE
README.md

View File

@ -1,14 +0,0 @@
# syntax=docker/dockerfile:1
#FROM golang:1.17-alpine AS builder
#WORKDIR /go/src/quenten.nl/pepeservice
#
#COPY . ./
#WORKDIR /go/src/quenten.nl/pepeservice/pepeservice
#RUN go mod download
#RUN CGO_ENABLED=0 GOOS=linux go build -a -installsuffix cgo -o app ./
FROM alpine:latest
RUN apk --no-cache add ca-certificates
COPY ./build/app /root
WORKDIR /root
CMD ["./app"]

View File

@ -1,3 +0,0 @@
module quenten.nl/pepeservice
go 1.17

View File

@ -1,84 +0,0 @@
package main
import (
"log"
"math/rand"
"net/http"
"os"
"time"
)
type application struct {
errorLog *log.Logger
infoLog *log.Logger
pepe_list []string
pepe_dir string
}
func main() {
pepe_dir := os.Getenv("PEPE_DIR")
infoLog := log.New(os.Stdout, "INFO\t", log.Ldate|log.Ltime)
errorLog := log.New(os.Stderr, "ERROR\t", log.Ldate|log.Ltime|log.Lshortfile)
file, err := os.Open(pepe_dir)
if err != nil {
errorLog.Printf("Error opening pepe directory\n")
return
}
pepe_list, err := file.Readdirnames(0)
if err != nil {
errorLog.Printf("Error reading pepe directory file names\n")
return
}
file.Close()
app := &application{
infoLog: infoLog,
errorLog: errorLog,
pepe_dir: pepe_dir,
pepe_list: pepe_list,
}
mux := http.NewServeMux()
mux.HandleFunc("/pepe", app.sendPepe)
mux.HandleFunc("/reload", app.reloadList)
app.infoLog.Printf("Starting server at :4000\n")
err = http.ListenAndServe(":4000", mux)
log.Fatal(err)
}
func (app *application) sendPepe(w http.ResponseWriter, r *http.Request) {
// Random number generator
s := rand.NewSource(time.Now().UnixMicro())
rd := rand.New(s) // Init pseudorandom generator
number := rd.Intn(len(app.pepe_list))
baseURL := "https://cdn.nicecock.eu/pepe/1.00/"
URL := baseURL + app.pepe_list[number]
w.Write([]byte(URL))
}
func (app *application) reloadList(w http.ResponseWriter, r *http.Request) {
file, err := os.Open(app.pepe_dir)
if err != nil {
app.errorLog.Printf("Error opening pepe directory\n")
return
}
defer file.Close()
pepe_list, err := file.Readdirnames(0)
if err != nil {
app.errorLog.Printf("Error reading pepe directory file names\n")
return
}
app.pepe_list = pepe_list
w.WriteHeader(http.StatusOK)
w.Write([]byte("200 - Reloaded the list of pepes"))
}