First commit

This commit is contained in:
DutchEllie 2021-05-18 12:11:30 +02:00
commit 26097ea9d1
15 changed files with 890 additions and 0 deletions

1
.gitignore vendored Normal file
View File

@ -0,0 +1 @@
discordtoken.txt

13
Dockerfile Normal file
View File

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

1
README.md Normal file
View File

@ -0,0 +1 @@
Substitute the environment variables in the docker-compose.yml file for your own

149
discord/admin.go Normal file
View File

@ -0,0 +1,149 @@
package main
import "github.com/bwmarrin/discordgo"
func (app *application) addWord(s *discordgo.Session, m *discordgo.MessageCreate, splitCommand []string) {
/* Check if admin */
r, err := app.checkIfAdmin(s, m)
if err != nil {
app.errorLog.Print(err)
return
}
if !r {
return
}
/* [0] = trigger, [1] is addword, [2] is the word! */
err = app.contextLength(splitCommand)
if err != nil {
s.ChannelMessageSend(m.ChannelID, "Please provide a word to add")
return
}
_, err = app.badwords.InsertNewWord(splitCommand[2], m.GuildID)
if err != nil {
app.errorLog.Print(err)
s.ChannelMessageSend(m.ChannelID, err.Error())
return
}
app.updateAllBadWords()
}
func (app *application) removeWord(s *discordgo.Session, m *discordgo.MessageCreate, splitCommand []string) {
/* Check if admin */
r, err := app.checkIfAdmin(s, m)
if err != nil {
app.errorLog.Print(err)
return
}
if !r {
return
}
/* [0] = trigger, [1] is removeword, [2] is the word! */
err = app.contextLength(splitCommand)
if err != nil {
s.ChannelMessageSend(m.ChannelID, "Please provide a word to remove")
return
}
err = app.badwords.RemoveWord(splitCommand[2], m.GuildID)
if err != nil {
app.errorLog.Print(err)
s.ChannelMessageSend(m.ChannelID, err.Error())
return
}
app.updateAllBadWords()
}
func (app *application) addAdmin(s *discordgo.Session, m *discordgo.MessageCreate, splitCommand []string) {
/* Check if admin */
r, err := app.checkIfAdmin(s, m)
if err != nil {
app.errorLog.Print(err)
return
}
if !r {
return
}
/* [0] = trigger, [1] is addadmin, [2] is the id! */
err = app.contextLength(splitCommand)
if err != nil {
s.ChannelMessageSend(m.ChannelID, "Please provide a role id")
return
}
allRoles, err := s.GuildRoles(m.GuildID)
if err != nil {
app.unknownError(err, s, true, m.ChannelID)
return
}
var found bool = false
var counter int = 0
for i := 0; i < len(allRoles); i++ {
if allRoles[i].ID == splitCommand[2] {
found = true
counter = i
break
}
}
if !found {
s.ChannelMessageSend(m.ChannelID, "This role id does not exist")
return
}
_, err = app.adminroles.AddAdminRole(allRoles[counter].Name, allRoles[counter].ID, m.GuildID)
if err != nil {
app.unknownError(err, s, true, m.ChannelID)
return
}
}
func (app *application) removeAdmin(s *discordgo.Session, m *discordgo.MessageCreate, splitCommand []string) {
/* Check if admin */
r, err := app.checkIfAdmin(s, m)
if err != nil {
app.errorLog.Print(err)
return
}
if !r {
return
}
/* [0] = trigger, [1] is removeadmin, [2] is the id! */
err = app.contextLength(splitCommand)
if err != nil {
s.ChannelMessageSend(m.ChannelID, "Please provide a role id")
return
}
allRoles, err := s.GuildRoles(m.GuildID)
if err != nil {
app.unknownError(err, s, true, m.ChannelID)
return
}
var found bool = false
var counter int = 0
for i := 0; i < len(allRoles); i++ {
if allRoles[i].ID == splitCommand[2] {
found = true
counter = i
break
}
}
if !found {
s.ChannelMessageSend(m.ChannelID, "This role id does not exist")
return
}
err = app.adminroles.RemoveAdminRole(allRoles[counter].Name, allRoles[counter].ID, m.GuildID)
if err != nil {
app.unknownError(err, s, true, m.ChannelID)
return
}
}

55
discord/discord.go Normal file
View File

@ -0,0 +1,55 @@
package main
import (
"strings"
"github.com/bwmarrin/discordgo"
)
func (app *application) messageCreate(s *discordgo.Session, m *discordgo.MessageCreate) {
if m.Author.Bot {
return
}
/* 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 not then we check if it contains a triggerword from the database */
if strings.HasPrefix(m.Content, app.trigger) {
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 strings.TrimSpace(m.Content) == app.trigger {
app.sendPepe(s, m)
return
}
/* This switches on the first word in the message
it could be anything starting with !pepe */
if len(splitCommand) > 1 {
switch splitCommand[1] {
/* --- Funny commands --- */
case "cringe":
app.sendCringe(s, m)
case "gif":
app.sendNigelGif(s, m)
/* --- Bot commands for words --- */
/* --- Bot commands, but only admins --- */
case "addword":
app.addWord(s, m, splitCommand)
case "removeword":
app.removeWord(s, m, splitCommand)
case "addadmin":
app.addAdmin(s, m, splitCommand)
case "removeadmin":
app.removeAdmin(s, m, splitCommand)
}
}
} else {
/* If the trigger wasn't the prefix of the message, we need to check all the words for a trigger */
app.findTrigger(s, m)
}
}

1
discord/general.go Normal file
View File

@ -0,0 +1 @@
package main

64
discord/helper.go Normal file
View File

@ -0,0 +1,64 @@
package main
import (
"database/sql"
"fmt"
"io"
"os"
"runtime/debug"
"github.com/bwmarrin/discordgo"
_ "github.com/go-sql-driver/mysql"
)
/* -------- DB Helper functions -------- */
func openDB(dsn string) (*sql.DB, error){
db, err := sql.Open("mysql", dsn)
if err != nil {
return nil, err
}
if orr := db.Ping(); orr != nil {
return nil, orr
}
return db, nil
}
func (app *application) updateAllBadWords() (error) {
var err error
app.allBadWords, err = app.badwords.AllWords()
if err != nil {
return err
}
return nil
}
/* -------- Error Helper functions -------- */
func (app *application) unknownError(err error, s *discordgo.Session, notifyDiscord bool, channelID string) {
trace := fmt.Sprintf("%s\n%s", err.Error(), debug.Stack())
app.errorLog.Output(2, trace)
if notifyDiscord {
msg := fmt.Sprintf("An unknown error occured, error message attached below. Stack trace is in the server logs.\n%s", err.Error())
s.ChannelMessageSend(channelID, msg)
}
}
/* -------- Discord Helper functions -------- */
func (app *application) readAuthToken() (string, error) {
file, err := os.Open("./discordtoken.txt")
if err != nil {
return "", err
}
token, err := io.ReadAll(file)
if err != nil {
return "", err
}
return string(token), nil
}

79
discord/main.go Normal file
View File

@ -0,0 +1,79 @@
package main
import (
"fmt"
"log"
"os"
"os/signal"
"syscall"
"github.com/bwmarrin/discordgo"
"quenten.nl/pepebot/models/mysql"
)
/* Application struct contains the logging objects.
It also has many methods for the different functions of the bot.
These methods are mostly located in discord.go */
type application struct {
errorLog *log.Logger
infoLog *log.Logger
badwords *mysql.BadwordModel
adminroles *mysql.AdminRolesModel
trigger string
allBadWords map[string][]string
}
func main() {
dbUser := os.Getenv("DB_USER")
dbPass := os.Getenv("DB_PASS")
discordToken := os.Getenv("DISCORD_TOKEN")
dsn := fmt.Sprintf("%s:%s@tcp(db:3306)/badwords?parseTime=true", dbUser, dbPass)
infoLog := log.New(os.Stdout, "INFO\t", log.Ldate|log.Ltime)
errorLog := log.New(os.Stderr, "ERROR\t", log.Ldate|log.Ltime|log.Lshortfile)
db, err := openDB(dsn)
if err != nil {
errorLog.Fatal(err)
}
app := &application{
infoLog: infoLog,
errorLog: errorLog,
badwords: &mysql.BadwordModel{DB: db},
adminroles: &mysql.AdminRolesModel{DB: db},
trigger: "!pepe",
}
app.allBadWords, err = app.badwords.AllWords()
if err != nil {
app.errorLog.Fatal(err)
}
/* token, err := app.readAuthToken()
if err != nil {
app.errorLog.Fatal(err)
} */
discord, err := discordgo.New("Bot " + discordToken)
if err != nil {
app.errorLog.Fatal(err)
}
discord.AddHandler(app.messageCreate)
discord.Identify.Intents = discordgo.IntentsGuildMessages
err = discord.Open()
if err != nil {
app.errorLog.Fatal(err)
}
defer discord.Close()
fmt.Println("Bot is now running. Press CTRL-C to exit.")
sc := make(chan os.Signal, 1)
signal.Notify(sc, syscall.SIGINT, syscall.SIGTERM, os.Interrupt, os.Kill)
<-sc
// Cleanly close down the Discord session.
discord.Close()
}

215
discord/simple.go Normal file
View File

@ -0,0 +1,215 @@
package main
import (
"errors"
"fmt"
"io/ioutil"
"math/rand"
"net/http"
"regexp"
"strings"
"time"
"github.com/bwmarrin/discordgo"
)
func (app *application) sendPepe(s *discordgo.Session, m *discordgo.MessageCreate) {
resp, err := http.Get("http://bbwroller.com/random")
if err != nil {
app.errorLog.Print(err)
return
}
body, err := ioutil.ReadAll(resp.Body)
if err != nil {
app.errorLog.Print(err)
return
}
rep, err := regexp.Compile("/static.*\\.jpg")
if err != nil {
app.errorLog.Print(err)
return
}
pepes := rep.FindAllString(string(body), 200)
if pepes == nil {
app.errorLog.Printf("No pepes were found\n")
return
}
randomIndex := rand.Intn(35)
url := "https://bbwroller.com"
url += pepes[randomIndex]
_, err = s.ChannelMessageSend(m.ChannelID, url)
if err != nil {
app.errorLog.Print(err)
}
}
func (app *application) sendCringe(s *discordgo.Session, m *discordgo.MessageCreate) {
_, err := s.ChannelMessageSend(m.ChannelID, "https://cdn.nicecock.eu/cringe.webm")
if err != nil {
app.errorLog.Print(err)
return
}
}
func (app *application) sendNigelGif(s *discordgo.Session, m *discordgo.MessageCreate) {
msg := "<@77516941199159296> kun je die gif verwijderen van die pickup truck die naar de camera rijdt want bij mij zorg ie voor dat discord opnieuw opstart. ik weet niet of iemand anders dit heeft maar als iemand weet hoe dit komt en een andere oplossing weet hoor ik het graag."
_, err := s.ChannelMessageSend(m.ChannelID, msg)
if err != nil {
app.errorLog.Print(err)
return
}
}
func (app *application) findTrigger(s *discordgo.Session, m *discordgo.MessageCreate) {
/* Finding for every word in the allBadWords map of string slices
Check if the message contains that word
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 */
for i := 0; i < len(app.allBadWords[m.GuildID]); i++{
if strings.Contains(strings.ToLower(m.Content), strings.ToLower(app.allBadWords[m.GuildID][i])) {
/* Found the bad word */
word, err := app.badwords.GetWord(strings.ToLower(app.allBadWords[m.GuildID][i]), m.GuildID)
if err != nil {
app.errorLog.Print(err)
s.ChannelMessageSend(m.ChannelID, err.Error())
}
format := formatTimeCheck(word.LastSaid)
_, err = app.badwords.UpdateLastSaid(word.Word, word.ServerID)
if err != nil {
app.errorLog.Print(err)
return
}
user := m.Author.Mention()
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)
_ ,err = s.ChannelMessageSend(m.ChannelID, message)
if err != nil {
app.errorLog.Print(err)
return
}
break
}
}
}
func formatTimeCheck(last time.Time) string{
now := time.Now()
sinceLast := now.Sub(last)
var realSeconds uint64 = uint64(sinceLast.Seconds())
var seconds, minutes, hours, days uint64
realBackup := realSeconds
days = realSeconds / ( 24 * 3600 )
realSeconds -= days * ( 24 * 3600 )
hours = realSeconds / 3600
realSeconds -= hours * 3600
minutes = realSeconds / 60
realSeconds -= minutes * 60
seconds = realSeconds
if realBackup < 60{
if seconds == 1{
return fmt.Sprintf("%d second", seconds)
}
return fmt.Sprintf("%d seconds", seconds)
}else if realBackup > 60 && realBackup < 3600 {
if seconds == 1 && minutes == 1{
return fmt.Sprintf("%d minute and %d second", minutes, seconds)
}else if minutes == 1 && seconds != 1{
return fmt.Sprintf("%d minute and %d seconds", minutes, seconds)
}else if minutes != 1 && seconds == 1{
return fmt.Sprintf("%d minutes and %d second", minutes, seconds)
}
return fmt.Sprintf("%d minutes and %d seconds", minutes, seconds)
}else if realBackup > 60 && realBackup < ( 24 * 3600 ){
if hours == 1 && minutes == 1 && seconds == 1{
return fmt.Sprintf("%d hour, %d minute and %d second", hours, minutes, seconds)
}else if hours == 1 && minutes == 1 && seconds != 1 {
return fmt.Sprintf("%d hour, %d minute and %d seconds", hours, minutes, seconds)
}else if hours == 1 && minutes != 1 && seconds == 1{
return fmt.Sprintf("%d hour, %d minutes and %d second", hours, minutes, seconds)
}else if hours == 1 && minutes != 1 && seconds != 1{
return fmt.Sprintf("%d hour, %d minutes and %d seconds", hours, minutes, seconds)
}else if hours != 1 && minutes == 1 && seconds == 1{
return fmt.Sprintf("%d hours, %d minute and %d second", hours, minutes, seconds)
}else if hours != 1 && minutes == 1 && seconds != 1{
return fmt.Sprintf("%d hours, %d minute and %d seconds", hours, minutes, seconds)
}else if hours != 1 && minutes != 1 && seconds == 1{
return fmt.Sprintf("%d hours, %d minutes and %d second", hours, minutes, seconds)
}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)
}else if realBackup > ( 24 * 3600 ){
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)
}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)
}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)
}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)
}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)
}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)
}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)
}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)
}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)
}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)
}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)
}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)
}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)
}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)
}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)
}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 days, %d hours, %d minutes and %d seconds", days, hours, minutes, seconds)
}
return "error"
}
func (app *application) checkIfAdmin(s *discordgo.Session, m *discordgo.MessageCreate) (bool, error) {
authorMemberInfo, err := s.GuildMember(m.GuildID, m.Author.ID)
if err != nil {
return false, err
}
roleIDs, err := app.adminroles.GetAdminRoleIDs()
if err != nil {
return false, err
}
for i := 0; i < len(authorMemberInfo.Roles); i++ {
for j := 0; j < len(roleIDs); j++ {
if authorMemberInfo.Roles[i] == roleIDs[j] {
return true, nil
}
}
}
app.infoLog.Printf("The user %s tried to perform an admin command without an admin role, purge them", m.Author)
_, err = s.ChannelMessageSend(m.ChannelID, "You aren't authorized to perform this function, this incident has been reported.")
if err != nil {
return false, err
}
return false, nil
}
func (app *application) contextLength(splitCommand []string) (error) {
if !(len(splitCommand) > 2) {
app.errorLog.Printf("The command's context was not enough.\n")
return errors.New("not enough context")
}
return nil
}

27
docker-compose.yml Normal file
View File

@ -0,0 +1,27 @@
version: "3.7"
services:
app:
container_name: pepebot_server
build: .
restart: always
depends_on:
- db
environment:
- DB_USER=
- DB_PASS=
- DISCORD_TOKEN=
db:
container_name: pepebot_database
image: mysql:8.0
restart: always
environment:
MYSQL_ROOT_PASSWORD:
MYSQL_DATABASE:
MYSQL_USER:
MYSQL_PASSWORD:
volumes:
- pepe_db_data:/var/lib/mysql
volumes:
pepe_db_data: {}

8
go.mod Normal file
View File

@ -0,0 +1,8 @@
module quenten.nl/pepebot
go 1.16
require (
github.com/bwmarrin/discordgo v0.23.2
github.com/go-sql-driver/mysql v1.6.0
)

8
go.sum Normal file
View File

@ -0,0 +1,8 @@
github.com/bwmarrin/discordgo v0.23.2 h1:BzrtTktixGHIu9Tt7dEE6diysEF9HWnXeHuoJEt2fH4=
github.com/bwmarrin/discordgo v0.23.2/go.mod h1:c1WtWUGN6nREDmzIpyTp/iD3VYt4Fpx+bVyfBG7JE+M=
github.com/go-sql-driver/mysql v1.6.0 h1:BCTh4TKNUYmOmMUcQ3IipzF5prigylS7XXjEkfCHuOE=
github.com/go-sql-driver/mysql v1.6.0/go.mod h1:DCzpHaOWr8IXmIStZouvnhqoel9Qv2LBy8hT2VhHyBg=
github.com/gorilla/websocket v1.4.0 h1:WDFjx/TMzVgy9VdMMQi2K2Emtwi2QcUQsztZ/zLaH/Q=
github.com/gorilla/websocket v1.4.0/go.mod h1:E7qHFY5m1UJ88s3WnNqhKjPHQ0heANvMoAMk2YaljkQ=
golang.org/x/crypto v0.0.0-20181030102418-4d3f4d9ffa16 h1:y6ce7gCWtnH+m3dCjzQ1PCuwl28DDIc3VNnvY29DlIA=
golang.org/x/crypto v0.0.0-20181030102418-4d3f4d9ffa16/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4=

23
models/models.go Normal file
View File

@ -0,0 +1,23 @@
package models
import (
"errors"
"time"
)
var ErrNoRecord = errors.New("models: no matching record found")
type Badword struct {
ID int
Word string
ServerID string
LastSaid time.Time
}
type AdminRoles struct {
ID int
RoleName string
RoleID string
GuildID string
}

View File

@ -0,0 +1,99 @@
package mysql
import (
"database/sql"
"errors"
"quenten.nl/pepebot/models"
)
type AdminRolesModel struct {
DB *sql.DB
}
func (m *AdminRolesModel) GetAdmins() ([]*models.AdminRoles, error) {
stmt := `SELECT id, rolename, roleid, guildid FROM adminroles`
rows, err := m.DB.Query(stmt)
if err != nil {
return nil, err
}
defer rows.Close()
tmp := []*models.AdminRoles{}
for rows.Next() {
t := &models.AdminRoles{}
err = rows.Scan(&t.ID, &t.RoleName, &t.RoleID, &t.GuildID)
if err != nil {
return nil, err
}
tmp = append(tmp, t)
}
if err = rows.Err(); err != nil{
return nil, err
}
return tmp, nil
}
func (m *AdminRolesModel) GetAdminRoleIDs() ([]string, error) {
admins, err := m.GetAdmins()
if err != nil {
return nil, err
}
roleIDs := make([]string, 0)
for i := 0; i < len(admins); i++ {
roleIDs = append(roleIDs, admins[i].RoleID)
}
return roleIDs, nil
}
func (m *AdminRolesModel) AddAdminRole(roleName string, roleID string, guildID string) (int, error) {
stmt := `INSERT INTO adminroles (rolename, roleid, guildid) VALUES (?, ?, ?)`
result, err := m.DB.Exec(stmt, roleName, roleID, guildID)
if err != nil {
return 0, err
}
id, err := result.LastInsertId()
if err != nil {
return 0, err
}
return int(id), nil
}
func (m *AdminRolesModel) RemoveAdminRole(roleName string, roleID string, guildID string) (error) {
stmt := `SELECT id FROM adminroles WHERE rolename = ? AND roleid = ? AND guildid = ?`
row := m.DB.QueryRow(stmt, roleName, roleID, guildID)
var id int
err := row.Scan(&id)
if err != nil {
if errors.Is(err, sql.ErrNoRows){
return models.ErrNoRecord
}else{
return err
}
}
stmt = `DELETE FROM adminroles WHERE id = ?`
result, err := m.DB.Exec(stmt, id)
if err != nil {
return err
}
if r, _ := result.RowsAffected(); r == 0 || r > 1 {
return errors.New("either zero or more than one rows were affected")
}
return nil
}

147
models/mysql/badword.go Normal file
View File

@ -0,0 +1,147 @@
package mysql
import (
"database/sql"
"errors"
"strconv"
"quenten.nl/pepebot/models"
)
type BadwordModel struct {
DB *sql.DB
}
func (m *BadwordModel) GetWord(word string, serverID string) (*models.Badword, error) {
stmt := `SELECT id, word, serverid, lastsaid FROM badwords
WHERE word = ? AND serverid = ?`
row := m.DB.QueryRow(stmt, word, serverID)
bw := &models.Badword{}
err := row.Scan(&bw.ID, &bw.Word, &bw.ServerID, &bw.LastSaid)
if err != nil {
if errors.Is(err, sql.ErrNoRows){
return nil, models.ErrNoRecord
}else{
return nil, err
}
}
return bw, nil
}
func (m *BadwordModel) AllWords() (map[string][]string, error) {
stmt := `SELECT word, serverid FROM badwords`
rows, err := m.DB.Query(stmt)
if err != nil {
return nil, err
}
defer rows.Close()
type tmp struct{
word string
serverid string
}
tmp2 := []*tmp{}
for rows.Next() {
t := &tmp{}
err = rows.Scan(&t.word, &t.serverid)
if err != nil {
return nil, err
}
tmp2 = append(tmp2, t)
}
if err = rows.Err(); err != nil{
return nil, err
}
finaltmp := make(map[string][]string)
for i := 0; i < len(tmp2); i++ {
finaltmp[tmp2[i].serverid] = append(finaltmp[tmp2[i].serverid], tmp2[i].word)
}
return finaltmp, nil
}
func (m *BadwordModel) InsertNewWord(word string, serverid string) (int, error) {
stmt := `INSERT INTO badwords (word, serverid, lastsaid)
VALUES (?, ?, UTC_TIMESTAMP())`
id1, err := strconv.Atoi(serverid)
if err != nil {
return 0, err
}
result, err := m.DB.Exec(stmt, word, id1)
if err != nil {
return 0, err
}
id, err := result.LastInsertId()
if err != nil {
return 0, err
}
return int(id), nil
}
func (m *BadwordModel) RemoveWord(word string, serverid string) (error) {
allwords, err := m.AllWords()
if err != nil {
return err
}
found := false
for i := 0; i < len(allwords[serverid]); i++ {
if allwords[serverid][i] == word {
found = true
break
}
}
if !found {
return errors.New("that word doesn't exist")
}
stmt := `DELETE FROM badwords WHERE word = ? AND serverid = ?`
result, err := m.DB.Exec(stmt, word, serverid)
if err != nil {
return err
}
if r, _ := result.RowsAffected(); r == 0 || r > 1 {
return errors.New("an unknown error occured")
}
return nil
}
func (m *BadwordModel) UpdateLastSaid(word string, serverid string) (int, error) {
stmt := `SELECT id FROM badwords WHERE
word = ? AND serverid = ?`
row := m.DB.QueryRow(stmt, word, serverid)
id := 0
row.Scan(&id)
stmt = `UPDATE badwords SET lastsaid = UTC_TIMESTAMP() WHERE id = ?`
result, err := m.DB.Exec(stmt, id)
if err != nil {
return 0, err
}
id2, err := result.LastInsertId()
if err != nil {
return 0, nil
}
return int(id2), nil
}