diff --git a/Dockerfile b/Dockerfile index e5c5618..37eec23 100644 --- a/Dockerfile +++ b/Dockerfile @@ -7,9 +7,9 @@ COPY ./go.sum . RUN go mod download COPY . . -RUN go install github.com/dustinpianalto/goff +RUN go install github.com/dustinpianalto/goff/... -CMD [ "go", "run", "goff.go"] +CMD [ "go", "run", "cmd/goff/main.go"] from alpine diff --git a/README.md b/README.md new file mode 100644 index 0000000..5386765 --- /dev/null +++ b/README.md @@ -0,0 +1,3 @@ +# Goff + +A discord bot for fun diff --git a/cmd/goff/main b/cmd/goff/main new file mode 100755 index 0000000..cad5eac Binary files /dev/null and b/cmd/goff/main differ diff --git a/cmd/goff/main.go b/cmd/goff/main.go new file mode 100644 index 0000000..7881321 --- /dev/null +++ b/cmd/goff/main.go @@ -0,0 +1,148 @@ +package main + +import ( + "fmt" + "log" + + "github.com/dustinpianalto/disgoman" + "github.com/dustinpianalto/goff/internal/exts" + "github.com/dustinpianalto/goff/internal/exts/guild_management" + "github.com/dustinpianalto/goff/internal/exts/logging" + "github.com/dustinpianalto/goff/internal/exts/tasks" + "github.com/dustinpianalto/goff/internal/exts/user_management" + "github.com/dustinpianalto/goff/internal/postgres" + "github.com/dustinpianalto/goff/internal/services" + "github.com/dustinpianalto/goff/pkg/email" + + //"github.com/MikeModder/anpan" + "os" + "os/signal" + "syscall" + + "github.com/bwmarrin/discordgo" +) + +var ( + Token string +) + +//func init() { +// flag.StringVar(&Token, "t", "", "Bot Token") +// flag.Parse() +//} + +func main() { + Token = os.Getenv("DISCORDGO_TOKEN") + dg, err := discordgo.New("Bot " + Token) + if err != nil { + fmt.Println("There was an error when creating the Discord Session, ", err) + return + } + dg.State.MaxMessageCount = 100 + dg.StateEnabled = true + + dg.Identify = discordgo.Identify{ + Intents: discordgo.MakeIntent(discordgo.IntentsAll), + } + + postgres.ConnectDatabase(os.Getenv("DATABASE_URL")) + postgres.InitializeDatabase() + //utils.LoadTestData() + + us := &postgres.UserService{DB: postgres.DB} + gs := &postgres.GuildService{DB: postgres.DB} + + //prefixes := []string{ + // "Go.", + //} + owners := []string{ + "351794468870946827", + } + + manager := disgoman.CommandManager{ + Prefixes: getPrefixes, + Owners: owners, + StatusManager: disgoman.GetDefaultStatusManager(), + ErrorChannel: make(chan disgoman.CommandError, 10), + Commands: make(map[string]*disgoman.Command), + IgnoreBots: true, + CheckPermissions: false, + } + + // Add Command Handlers + exts.AddCommandHandlers(&manager) + services.InitalizeServices(us, gs) + + //if _, ok := handler.Commands["help"]; !ok { + // handler.AddDefaultHelpCommand() + //} + + dg.AddHandler(manager.OnMessage) + dg.AddHandler(manager.StatusManager.OnReady) + dg.AddHandler(guild_management.OnMessageUpdate) + dg.AddHandler(guild_management.OnMessageDelete) + dg.AddHandler(user_management.OnGuildMemberAddLogging) + dg.AddHandler(user_management.OnGuildMemberRemoveLogging) + + err = dg.Open() + if err != nil { + fmt.Println("There was an error opening the connection, ", err) + return + } + + // Start the Error handler in a goroutine + go ErrorHandler(manager.ErrorChannel) + + // Start the Logging handler in a goroutine + go logging.LoggingHandler(logging.LoggingChannel) + + // Start the task handler in a goroutine + go tasks.ProcessTasks(dg, 1) + + go email.RecieveEmail(dg) + + fmt.Println("The Bot is now running.") + sc := make(chan os.Signal, 1) + signal.Notify(sc, syscall.SIGINT, syscall.SIGTERM, os.Interrupt, os.Kill) + <-sc + + fmt.Println("Shutting Down...") + err = dg.Close() + if err != nil { + fmt.Println(err) + } +} + +func getPrefixes(guildID string) []string { + queryString := "Select prefix from prefixes p, x_guilds_prefixes xgp where xgp.guild_id = $1 and xgp.prefix_id = p.id" + rows, err := postgres.DB.Query(queryString, guildID) + if err != nil { + log.Println(err) + return []string{"Go.", "go."} + } + var prefixes []string + for rows.Next() { + var prefix string + err = rows.Scan(&prefix) + if err != nil { + log.Println(err) + return []string{"Go.", "go."} + } + prefixes = append(prefixes, prefix) + } + if len(prefixes) == 0 { + prefixes = append(prefixes, "Go.", "go.") + } + return prefixes +} + +func ErrorHandler(ErrorChan chan disgoman.CommandError) { + for ce := range ErrorChan { + msg := ce.Message + if msg == "" { + msg = ce.Error.Error() + } + _, _ = ce.Context.Send(msg) + fmt.Println(ce.Error) + } +} diff --git a/docker-compose.yml b/docker-compose.yml deleted file mode 100644 index 361ca62..0000000 --- a/docker-compose.yml +++ /dev/null @@ -1,32 +0,0 @@ -version: "3" - -services: -# goff-db: -# image: postgres -# ports: -# - "5432:5432" -# volumes: -# - "${PWD}/postgres.conf:/etc/postgresql/postgresql.conf" -# - "goff-db:/var/lib/postgresql/data:rw" -# env_file: ${PWD}/.env - - goff: - build: - context: . - dockerfile: "${PWD}/Dockerfile" - env_file: ${PWD}/.env - logging: - driver: awslogs - options: - awslogs-region: us-east-1 - awslogs-group: "/docker/goff/production" -# depends_on: -# - goff-db - environment: - - DATABASE_URL=postgres://${POSTGRES_USER}:${POSTGRES_PASSWORD}@${POSTGRES_HOST}:${POSTGRES_PORT}/${POSTGRES_DB}?sslmode=disable -# links: -# - goff-db:goff.db - -#volumes: -# goff-db: -# external: true \ No newline at end of file diff --git a/docker-dev.yml b/docker-dev.yml deleted file mode 100644 index 15da14d..0000000 --- a/docker-dev.yml +++ /dev/null @@ -1,32 +0,0 @@ -version: "3" - -services: -# goff-db: -# image: postgres -# ports: -# - "5432:5432" -# volumes: -# - "${PWD}/postgres.conf:/etc/postgresql/postgresql.conf" -# - "goff-db:/var/lib/postgresql/data:rw" -# env_file: ${PWD}/.env - - goff: - build: - context: . - dockerfile: "${PWD}/Dockerfile" - env_file: ${PWD}/.env -# logging: -# driver: awslogs -# options: -# awslogs-region: us-east-1 -# awslogs-group: "/docker/goff/production" -# depends_on: -# - goff-db - environment: - - DATABASE_URL=postgres://${POSTGRES_USER}:${POSTGRES_PASSWORD}@${POSTGRES_HOST}:${POSTGRES_PORT}/${POSTGRES_DB}?sslmode=disable -# links: -# - goff-db:goff.db - -#volumes: -# goff-db: -# external: true diff --git a/exts/fun.go b/exts/fun.go deleted file mode 100644 index 5084128..0000000 --- a/exts/fun.go +++ /dev/null @@ -1,84 +0,0 @@ -package exts - -import ( - "fmt" - "strconv" - "strings" - - "github.com/dustinpianalto/disgoman" - "github.com/dustinpianalto/rpnparse" -) - -func interleave(ctx disgoman.Context, args []string) { - if len(args) == 2 { - x, err := strconv.ParseInt(args[0], 10, 64) - if err != nil { - return - } - y, err := strconv.ParseInt(args[1], 10, 64) - if err != nil { - return - } - var z = int64(0) - for i := 0; i < 64; i++ { - x_masked_i := x & (1 << i) - y_masked_i := y & (1 << i) - - z |= x_masked_i << i - z |= y_masked_i << (i + 1) - } - ctx.Send(fmt.Sprintf("%v", z)) - } -} - -func deinterleave(ctx disgoman.Context, args []string) { - if len(args) == 1 { - z, err := strconv.ParseInt(args[0], 10, 64) - if err != nil { - return - } - var x = int64(0) - var y = int64(0) - i := 0 - for z > 0 { - x |= (z & 1) << i - z >>= 1 - y |= (z & 1) << i - z >>= 1 - i++ - } - ctx.Send(fmt.Sprintf("(%v, %v)", x, y)) - } -} - -func generateRPNCommand(ctx disgoman.Context, args []string) { - rpn, err := rpnparse.GenerateRPN(args) - if err != nil { - ctx.Send(err.Error()) - return - } - ctx.Send(rpn) -} - -func parseRPNCommand(ctx disgoman.Context, args []string) { - res, err := rpnparse.ParseRPN(args) - if err != nil { - ctx.Send(err.Error()) - return - } - ctx.Send(fmt.Sprintf("The result is: %v", res)) -} - -func solveCommand(ctx disgoman.Context, args []string) { - rpn, err := rpnparse.GenerateRPN(args) - if err != nil { - ctx.Send(err.Error()) - return - } - res, err := rpnparse.ParseRPN(strings.Split(rpn, " ")) - if err != nil { - ctx.Send(err.Error()) - return - } - ctx.Send(fmt.Sprintf("The result is: %v", res)) -} diff --git a/exts/guild.go b/exts/guild.go deleted file mode 100644 index a75990c..0000000 --- a/exts/guild.go +++ /dev/null @@ -1,376 +0,0 @@ -package exts - -import ( - "database/sql" - "fmt" - "strings" - - "github.com/dustinpianalto/disgoman" - "github.com/dustinpianalto/goff/utils" -) - -// Guild management commands - -func loggingChannel(ctx disgoman.Context, args []string) { - var idString string - if len(args) > 0 { - idString = args[0] - if strings.HasPrefix(idString, "<#") && strings.HasSuffix(idString, ">") { - idString = idString[2 : len(idString)-1] - } - } else { - idString = "" - } - fmt.Println(idString) - if idString == "" { - _, err := utils.Database.Exec("UPDATE guilds SET logging_channel='' WHERE id=$1;", ctx.Guild.ID) - if err != nil { - ctx.ErrorChannel <- disgoman.CommandError{ - Context: ctx, - Message: "Error Updating Database", - Error: err, - } - return - } - _, _ = ctx.Send("Logging Channel Updated.") - return - } - channel, err := ctx.Session.State.Channel(idString) - if err != nil { - ctx.ErrorChannel <- disgoman.CommandError{ - Context: ctx, - Message: "Can't find that channel.", - Error: err, - } - return - } - if channel.GuildID != ctx.Guild.ID { - ctx.ErrorChannel <- disgoman.CommandError{ - Context: ctx, - Message: "The channel passed is not in this guild.", - Error: err, - } - return - } - _, err = utils.Database.Exec("UPDATE guilds SET logging_channel=$1 WHERE id=$2;", idString, ctx.Guild.ID) - if err != nil { - ctx.ErrorChannel <- disgoman.CommandError{ - Context: ctx, - Message: "Error Updating Database", - Error: err, - } - return - } - _, _ = ctx.Send("Logging Channel Updated.") -} - -func getLoggingChannel(ctx disgoman.Context, _ []string) { - var channelID string - row := utils.Database.QueryRow("SELECT logging_channel FROM guilds where id=$1", ctx.Guild.ID) - err := row.Scan(&channelID) - if err != nil { - fmt.Println(err) - ctx.ErrorChannel <- disgoman.CommandError{ - Context: ctx, - Message: "Error getting data from the database.", - Error: err, - } - return - } - if channelID == "" { - _, _ = ctx.Send("The logging channel is not set.") - return - } - channel, err := ctx.Session.State.GuildChannel(ctx.Guild.ID, channelID) - if err != nil { - fmt.Println(err) - ctx.ErrorChannel <- disgoman.CommandError{ - Context: ctx, - Message: "I got the channel ID but it does not appear to be a valid channel in this guild.", - Error: err, - } - return - } - _, _ = ctx.Send(fmt.Sprintf("The logging channel is currently %s", channel.Mention())) - return -} - -func welcomeChannel(ctx disgoman.Context, args []string) { - var idString string - if len(args) > 0 { - idString = args[0] - if strings.HasPrefix(idString, "<#") && strings.HasSuffix(idString, ">") { - idString = idString[2 : len(idString)-1] - } - } else { - idString = "" - } - fmt.Println(idString) - if idString == "" { - _, err := utils.Database.Exec("UPDATE guilds SET welcome_channel='' WHERE id=$1;", ctx.Guild.ID) - if err != nil { - ctx.ErrorChannel <- disgoman.CommandError{ - Context: ctx, - Message: "Error Updating Database", - Error: err, - } - return - } - _, _ = ctx.Send("Welcomer Disabled.") - return - } - channel, err := ctx.Session.State.Channel(idString) - if err != nil { - ctx.ErrorChannel <- disgoman.CommandError{ - Context: ctx, - Message: "Can't find that channel.", - Error: err, - } - return - } - if channel.GuildID != ctx.Guild.ID { - ctx.ErrorChannel <- disgoman.CommandError{ - Context: ctx, - Message: "The channel passed is not in this guild.", - Error: err, - } - return - } - _, err = utils.Database.Exec("UPDATE guilds SET welcome_channel=$1 WHERE id=$2;", idString, ctx.Guild.ID) - if err != nil { - ctx.ErrorChannel <- disgoman.CommandError{ - Context: ctx, - Message: "Error Updating Database", - Error: err, - } - return - } - _, _ = ctx.Send("Welcome Channel Updated.") - return -} - -func getWelcomeChannel(ctx disgoman.Context, _ []string) { - var channelID string - row := utils.Database.QueryRow("SELECT welcome_channel FROM guilds where id=$1", ctx.Guild.ID) - err := row.Scan(&channelID) - if err != nil { - fmt.Println(err) - ctx.ErrorChannel <- disgoman.CommandError{ - Context: ctx, - Message: "Error getting data from the database.", - Error: err, - } - return - } - if channelID == "" { - _, _ = ctx.Send("The welcomer is disabled.") - return - } - channel, err := ctx.Session.State.GuildChannel(ctx.Guild.ID, channelID) - if err != nil { - fmt.Println(err) - ctx.ErrorChannel <- disgoman.CommandError{ - Context: ctx, - Message: "I got the channel ID but it does not appear to be a valid channel in this guild.", - Error: err, - } - return - } - _, _ = ctx.Send(fmt.Sprintf("The welcome channel is currently %s", channel.Mention())) -} - -func addGuildCommand(ctx disgoman.Context, args []string) { - var guildID string - row := utils.Database.QueryRow("SELECT id FROM guilds where id=$1", ctx.Guild.ID) - err := row.Scan(&guildID) - if err == nil { - ctx.ErrorChannel <- disgoman.CommandError{ - Context: ctx, - Message: "This guild is already in my database", - Error: err, - } - return - } - - _, err = utils.Database.Query("INSERT INTO guilds (id) VALUES ($1)", ctx.Guild.ID) - if err != nil { - fmt.Println(err) - ctx.ErrorChannel <- disgoman.CommandError{ - Context: ctx, - Message: "There was a problem inserting this guild into the database", - Error: err, - } - return - } - _, _ = ctx.Send("This guild has been added.") - -} - -func puzzleChannel(ctx disgoman.Context, args []string) { - var idString string - if len(args) > 0 { - idString = args[0] - if strings.HasPrefix(idString, "<#") && strings.HasSuffix(idString, ">") { - idString = idString[2 : len(idString)-1] - } - } else { - idString = "" - } - fmt.Println(idString) - if idString == "" { - _, err := utils.Database.Exec("UPDATE guilds SET puzzle_channel='' WHERE id=$1;", ctx.Guild.ID) - if err != nil { - ctx.ErrorChannel <- disgoman.CommandError{ - Context: ctx, - Message: "Error Updating Database", - Error: err, - } - return - } - _, _ = ctx.Send("Puzzle Channel Updated.") - return - } - channel, err := ctx.Session.State.Channel(idString) - if err != nil { - ctx.ErrorChannel <- disgoman.CommandError{ - Context: ctx, - Message: "Can't find that channel.", - Error: err, - } - return - } - if channel.GuildID != ctx.Guild.ID { - ctx.ErrorChannel <- disgoman.CommandError{ - Context: ctx, - Message: "The channel passed is not in this guild.", - Error: err, - } - return - } - _, err = utils.Database.Exec("UPDATE guilds SET puzzle_channel=$1 WHERE id=$2;", idString, ctx.Guild.ID) - if err != nil { - ctx.ErrorChannel <- disgoman.CommandError{ - Context: ctx, - Message: "Error Updating Database", - Error: err, - } - return - } - _, _ = ctx.Send("Puzzle Channel Updated.") -} - -func getPuzzleChannel(ctx disgoman.Context, _ []string) { - var channelID string - row := utils.Database.QueryRow("SELECT puzzle_channel FROM guilds where id=$1", ctx.Guild.ID) - err := row.Scan(&channelID) - if err != nil { - fmt.Println(err) - ctx.ErrorChannel <- disgoman.CommandError{ - Context: ctx, - Message: "Error getting data from the database.", - Error: err, - } - return - } - if channelID == "" { - _, _ = ctx.Send("The puzzle channel is not set.") - return - } - channel, err := ctx.Session.State.GuildChannel(ctx.Guild.ID, channelID) - if err != nil { - fmt.Println(err) - ctx.ErrorChannel <- disgoman.CommandError{ - Context: ctx, - Message: "I got the channel ID but it does not appear to be a valid channel in this guild.", - Error: err, - } - return - } - _, _ = ctx.Send(fmt.Sprintf("The puzzle channel is currently %s", channel.Mention())) - return -} - -func puzzleRole(ctx disgoman.Context, args []string) { - var idString string - if len(args) > 0 { - idString = args[0] - if strings.HasPrefix(idString, "<@&") && strings.HasSuffix(idString, ">") { - idString = idString[3 : len(idString)-1] - } - } else { - idString = "" - } - fmt.Println(idString) - if idString == "" { - _, err := utils.Database.Exec("UPDATE guilds SET puzzle_role=NULL WHERE id=$1;", ctx.Guild.ID) - if err != nil { - ctx.ErrorChannel <- disgoman.CommandError{ - Context: ctx, - Message: "Error Updating Database", - Error: err, - } - return - } - _, _ = ctx.Send("Puzzle Role Cleared.") - return - } - role, err := ctx.Session.State.Role(ctx.Guild.ID, idString) - if err != nil { - ctx.ErrorChannel <- disgoman.CommandError{ - Context: ctx, - Message: "Can't find that Role.", - Error: err, - } - return - } - _, err = utils.Database.Exec("INSERT INTO roles (id, guild_id) VALUES ($1, $2) ON CONFLICT DO NOTHING", role.ID, ctx.Guild.ID) - if err != nil { - ctx.ErrorChannel <- disgoman.CommandError{ - Context: ctx, - Message: "Error Updating Database", - Error: err, - } - return - } - _, err = utils.Database.Exec("UPDATE guilds SET puzzle_role=$1 WHERE id=$2;", role.ID, ctx.Guild.ID) - if err != nil { - ctx.ErrorChannel <- disgoman.CommandError{ - Context: ctx, - Message: "Error Updating Database", - Error: err, - } - return - } - _, _ = ctx.Send("Puzzle Role Updated.") -} - -func getPuzzleRole(ctx disgoman.Context, _ []string) { - var roleID sql.NullString - row := utils.Database.QueryRow("SELECT puzzle_role FROM guilds where id=$1", ctx.Guild.ID) - err := row.Scan(&roleID) - if err != nil { - fmt.Println(err) - ctx.ErrorChannel <- disgoman.CommandError{ - Context: ctx, - Message: "Error getting data from the database.", - Error: err, - } - return - } - if !roleID.Valid { - _, _ = ctx.Send("The puzzle role is not set.") - return - } - role, err := ctx.Session.State.Role(ctx.Guild.ID, roleID.String) - if err != nil { - fmt.Println(err) - ctx.ErrorChannel <- disgoman.CommandError{ - Context: ctx, - Message: "I got the role ID but it does not appear to be a valid role in this guild.", - Error: err, - } - return - } - _, _ = ctx.Send(fmt.Sprintf("The puzzle role is currently %s", role.Mention())) - return -} diff --git a/exts/init.go b/exts/init.go deleted file mode 100644 index dfcda31..0000000 --- a/exts/init.go +++ /dev/null @@ -1,288 +0,0 @@ -package exts - -import ( - "github.com/dustinpianalto/disgoman" -) - -func AddCommandHandlers(h *disgoman.CommandManager) { - // Arguments: - // name - command name - string - // desc - command description - string - // owneronly - only allow owners to run - bool - // hidden - hide command from non-owners - bool - // perms - permissisions required - anpan.Permission (int) - // type - command type, sets where the command is available - // run - function to run - func(anpan.Context, []string) / CommandRunFunc - _ = h.AddCommand(&disgoman.Command{ - Name: "ping", - Aliases: nil, - Description: "Check the bot's ping", - OwnerOnly: false, - Hidden: false, - RequiredPermissions: 0, - Invoke: pingCommand, - }) - _ = h.AddCommand(&disgoman.Command{ - Name: "say", - Aliases: nil, - Description: "Repeat a message", - OwnerOnly: false, - Hidden: false, - RequiredPermissions: 0, - SanitizeEveryone: true, - Invoke: sayCommand, - }) - _ = h.AddCommand(&disgoman.Command{ - Name: "user", - Aliases: nil, - Description: "Get user info", - OwnerOnly: false, - Hidden: false, - RequiredPermissions: 0, - Invoke: userCommand, - }) - _ = h.AddCommand(&disgoman.Command{ - Name: "git", - Aliases: nil, - Description: "Show my github link", - OwnerOnly: false, - Hidden: false, - RequiredPermissions: 0, - Invoke: gitCommand, - }) - _ = h.AddCommand(&disgoman.Command{ - Name: "tag", - Aliases: nil, - Description: "Get a tag", - OwnerOnly: false, - Hidden: false, - RequiredPermissions: 0, - Invoke: tagCommand, - }) - _ = h.AddCommand(&disgoman.Command{ - Name: "addtag", - Aliases: nil, - Description: "Add a tag", - OwnerOnly: false, - Hidden: false, - RequiredPermissions: 0, - SanitizeEveryone: true, - Invoke: addTagCommand, - }) - _ = h.AddCommand(&disgoman.Command{ - Name: "invite", - Aliases: nil, - Description: "Get the invite link for this bot or others", - OwnerOnly: false, - Hidden: false, - RequiredPermissions: 0, - Invoke: inviteCommand, - }) - _ = h.AddCommand(&disgoman.Command{ - Name: "P", - Aliases: nil, - Description: "Interpret a P\" program and return the results", - OwnerOnly: false, - Hidden: false, - RequiredPermissions: 0, - Invoke: pCommand, - }) - _ = h.AddCommand(&disgoman.Command{ - Name: "set-logging-channel", - Aliases: []string{"slc"}, - Description: "Set the channel logging messages will be sent to.", - OwnerOnly: false, - Hidden: false, - RequiredPermissions: disgoman.PermissionManageServer, - Invoke: loggingChannel, - }) - _ = h.AddCommand(&disgoman.Command{ - Name: "get-logging-channel", - Aliases: []string{"glc"}, - Description: "Gets the channel logging messages will be sent to.", - OwnerOnly: false, - Hidden: false, - RequiredPermissions: disgoman.PermissionManageServer, - Invoke: getLoggingChannel, - }) - _ = h.AddCommand(&disgoman.Command{ - Name: "set-welcome-channel", - Aliases: []string{"swc"}, - Description: "Set the channel welcome messages will be sent to.", - OwnerOnly: false, - Hidden: false, - RequiredPermissions: disgoman.PermissionManageServer, - Invoke: welcomeChannel, - }) - _ = h.AddCommand(&disgoman.Command{ - Name: "get-welcome-channel", - Aliases: []string{"gwc"}, - Description: "Gets the channel welcome messages will be sent to.", - OwnerOnly: false, - Hidden: false, - RequiredPermissions: disgoman.PermissionManageServer, - Invoke: getWelcomeChannel, - }) - _ = h.AddCommand(&disgoman.Command{ - Name: "kick", - Aliases: nil, - Description: "Kicks the given user with the given reason", - OwnerOnly: false, - Hidden: false, - RequiredPermissions: disgoman.PermissionKickMembers, - Invoke: kickUserCommand, - }) - _ = h.AddCommand(&disgoman.Command{ - Name: "addGuild", - Aliases: nil, - Description: "Adds the current guild to the database", - OwnerOnly: true, - Hidden: false, - RequiredPermissions: disgoman.PermissionManageServer, - Invoke: addGuildCommand, - }) - _ = h.AddCommand(&disgoman.Command{ - Name: "ban", - Aliases: []string{"ban-no-delete"}, - Description: "Bans the given user with the given reason", - OwnerOnly: false, - Hidden: false, - RequiredPermissions: disgoman.PermissionBanMembers, - Invoke: banUserCommand, - }) - _ = h.AddCommand(&disgoman.Command{ - Name: "unban", - Aliases: nil, - Description: "Unbans the given user", - OwnerOnly: false, - Hidden: false, - RequiredPermissions: disgoman.PermissionBanMembers, - Invoke: unbanUserCommand, - }) - _ = h.AddCommand(&disgoman.Command{ - Name: "remind", - Aliases: nil, - Description: "Remind me at a later time", - OwnerOnly: false, - Hidden: false, - RequiredPermissions: 0, - Invoke: addReminderCommand, - }) - _ = h.AddCommand(&disgoman.Command{ - Name: "encode", - Aliases: []string{"e"}, - Description: "Encode 2 numbers", - OwnerOnly: false, - Hidden: false, - RequiredPermissions: 0, - Invoke: interleave, - }) - _ = h.AddCommand(&disgoman.Command{ - Name: "decode", - Aliases: []string{"d"}, - Description: "Decode 1 number into 2", - OwnerOnly: false, - Hidden: false, - RequiredPermissions: 0, - Invoke: deinterleave, - }) - _ = h.AddCommand(&disgoman.Command{ - Name: "set-puzzle-channel", - Aliases: []string{"spc"}, - Description: "Set the channel puzzle messages will be sent to.", - OwnerOnly: false, - Hidden: false, - RequiredPermissions: disgoman.PermissionManageServer, - Invoke: puzzleChannel, - }) - _ = h.AddCommand(&disgoman.Command{ - Name: "get-puzzle-channel", - Aliases: []string{"gpc"}, - Description: "Gets the channel puzzle messages will be sent to.", - OwnerOnly: false, - Hidden: false, - RequiredPermissions: disgoman.PermissionManageServer, - Invoke: getPuzzleChannel, - }) - _ = h.AddCommand(&disgoman.Command{ - Name: "set-puzzle-role", - Aliases: []string{"spr"}, - Description: "Set the role to be pinged when there is a new puzzle", - OwnerOnly: false, - Hidden: false, - RequiredPermissions: disgoman.PermissionManageServer, - Invoke: puzzleRole, - }) - _ = h.AddCommand(&disgoman.Command{ - Name: "get-puzzle-role", - Aliases: []string{"gpr"}, - Description: "Get the role that will be pinged when there is a new puzzle", - OwnerOnly: false, - Hidden: false, - RequiredPermissions: disgoman.PermissionManageServer, - Invoke: getPuzzleRole, - }) - _ = h.AddCommand(&disgoman.Command{ - Name: "RPN", - Aliases: []string{"rpn"}, - Description: "Convert infix to rpn", - OwnerOnly: false, - Hidden: false, - RequiredPermissions: 0, - Invoke: generateRPNCommand, - }) - _ = h.AddCommand(&disgoman.Command{ - Name: "ParseRPN", - Aliases: []string{"PRPN", "prpn"}, - Description: "Parse RPN string and return the result", - OwnerOnly: false, - Hidden: false, - RequiredPermissions: 0, - Invoke: parseRPNCommand, - }) - _ = h.AddCommand(&disgoman.Command{ - Name: "solve", - Aliases: []string{"math", "infix"}, - Description: "Solve infix equation and return the result", - OwnerOnly: false, - Hidden: false, - RequiredPermissions: 0, - Invoke: solveCommand, - }) - _ = h.AddCommand(&disgoman.Command{ - Name: "make-role-self-assignable", - Aliases: []string{"makesar"}, - Description: "Makes the passed in role self assignable by anyone", - OwnerOnly: false, - Hidden: false, - RequiredPermissions: disgoman.PermissionManageServer, - Invoke: makeRoleSelfAssignable, - }) - _ = h.AddCommand(&disgoman.Command{ - Name: "remove-self-assignable-role", - Aliases: []string{"removesar"}, - Description: "Makes a role that was previously self assignable not so", - OwnerOnly: false, - Hidden: false, - RequiredPermissions: disgoman.PermissionManageServer, - Invoke: removeSelfAssignableRole, - }) - _ = h.AddCommand(&disgoman.Command{ - Name: "giverole", - Aliases: []string{"iwant", "givetome"}, - Description: "Assigns a person the passed in role if it is self assignable", - OwnerOnly: false, - Hidden: false, - RequiredPermissions: 0, - Invoke: selfAssignRole, - }) - _ = h.AddCommand(&disgoman.Command{ - Name: "removerole", - Aliases: []string{"idon'twant"}, - Description: "Removes a role from a person if the role is self assignable", - OwnerOnly: false, - Hidden: false, - RequiredPermissions: 0, - Invoke: unAssignRole, - }) -} diff --git a/go.mod b/go.mod index 6d9586c..f4ae69b 100644 --- a/go.mod +++ b/go.mod @@ -4,7 +4,7 @@ go 1.14 require ( github.com/bwmarrin/discordgo v0.22.0 - github.com/dustinpianalto/disgoman v0.0.12 + github.com/dustinpianalto/disgoman v0.0.15 github.com/dustinpianalto/rpnparse v1.0.1 github.com/emersion/go-imap v1.0.5 github.com/emersion/go-message v0.12.0 diff --git a/go.sum b/go.sum index cf05f55..f7ae336 100644 --- a/go.sum +++ b/go.sum @@ -13,6 +13,10 @@ github.com/dustinpianalto/disgoman v0.0.10 h1:UzmvMpOi4peF59tXGaNfVU+ePHs1hILa6g github.com/dustinpianalto/disgoman v0.0.10/go.mod h1:v3FM6n+4dH9XlvO+IDx6MN3DUnGq6YVDBvy1A1k202g= github.com/dustinpianalto/disgoman v0.0.12 h1:dLptU2ZTUZJaLBOKeE6qjuL8gqdAr6ehHSOtfHmUpL8= github.com/dustinpianalto/disgoman v0.0.12/go.mod h1:v3FM6n+4dH9XlvO+IDx6MN3DUnGq6YVDBvy1A1k202g= +github.com/dustinpianalto/disgoman v0.0.14 h1:hsk56JATBE5eH5GPCDGeiTdYxok4m+edSYiYz5Uj8uo= +github.com/dustinpianalto/disgoman v0.0.14/go.mod h1:v3FM6n+4dH9XlvO+IDx6MN3DUnGq6YVDBvy1A1k202g= +github.com/dustinpianalto/disgoman v0.0.15 h1:kdIw6jhC82WBut7+4BarqxBw06dozU+Hu47LQzkkoGM= +github.com/dustinpianalto/disgoman v0.0.15/go.mod h1:v3FM6n+4dH9XlvO+IDx6MN3DUnGq6YVDBvy1A1k202g= github.com/dustinpianalto/rpnparse v1.0.1 h1:ZvH1/RIe5hh3RGSAXOgtngEDHNPTF+DMh88XFWpQjzY= github.com/dustinpianalto/rpnparse v1.0.1/go.mod h1:SzFbQb+Eed5gYCtDu/SYEXXwdPtWkDg9oaL1xQtN1BY= github.com/emersion/go-imap v1.0.5 h1:8xg/d2wo2BBP3AEP5AOaM/6i8887RGyVW2st/IVHWUw= diff --git a/goff.go b/goff.go index fc2a384..bf84c15 100644 --- a/goff.go +++ b/goff.go @@ -1,141 +1,9 @@ -package main +package goff -import ( - "fmt" - "log" +import "github.com/dustinpianalto/disgoman" - "github.com/dustinpianalto/disgoman" - "github.com/dustinpianalto/goff/events" - "github.com/dustinpianalto/goff/exts" - "github.com/dustinpianalto/goff/utils" - - //"github.com/MikeModder/anpan" - "os" - "os/signal" - "syscall" - - "github.com/bwmarrin/discordgo" -) - -var ( - Token string -) - -//func init() { -// flag.StringVar(&Token, "t", "", "Bot Token") -// flag.Parse() -//} - -func main() { - Token = os.Getenv("DISCORDGO_TOKEN") - dg, err := discordgo.New("Bot " + Token) - if err != nil { - fmt.Println("There was an error when creating the Discord Session, ", err) - return - } - dg.State.MaxMessageCount = 100 - dg.StateEnabled = true - - dg.Identify = discordgo.Identify{ - Intents: discordgo.MakeIntent(discordgo.IntentsAll), - } - - utils.ConnectDatabase(os.Getenv("DATABASE_URL")) - utils.InitializeDatabase() - //utils.LoadTestData() - - //prefixes := []string{ - // "Go.", - //} - owners := []string{ - "351794468870946827", - } - - // Arguments are: - // prefixes - []string - // owner ids - []string - // ignore bots - bool - // check perms - bool - handler := disgoman.CommandManager{ - Prefixes: getPrefixes, - Owners: owners, - StatusManager: disgoman.GetDefaultStatusManager(), - ErrorChannel: make(chan disgoman.CommandError, 10), - Commands: make(map[string]*disgoman.Command), - IgnoreBots: true, - CheckPermissions: false, - } - - // Add Command Handlers - exts.AddCommandHandlers(&handler) - - //if _, ok := handler.Commands["help"]; !ok { - // handler.AddDefaultHelpCommand() - //} - - dg.AddHandler(handler.OnMessage) - dg.AddHandler(handler.StatusManager.OnReady) - dg.AddHandler(events.OnMessageUpdate) - dg.AddHandler(events.OnMessageDelete) - dg.AddHandler(events.OnGuildMemberAddLogging) - dg.AddHandler(events.OnGuildMemberRemoveLogging) - - err = dg.Open() - if err != nil { - fmt.Println("There was an error opening the connection, ", err) - return - } - - // Start the Error handler in a goroutine - go ErrorHandler(handler.ErrorChannel) - - // Start the Logging handler in a goroutine - go utils.LoggingHandler(utils.LoggingChannel) - - // Start the task handler in a goroutine - go utils.ProcessTasks(dg, 1) - - go utils.RecieveEmail(dg) - - fmt.Println("The Bot is now running.") - sc := make(chan os.Signal, 1) - signal.Notify(sc, syscall.SIGINT, syscall.SIGTERM, os.Interrupt, os.Kill) - <-sc - - fmt.Println("Shutting Down...") - err = dg.Close() - if err != nil { - fmt.Println(err) - } -} - -func getPrefixes(guildID string) []string { - queryString := "Select prefix from prefixes p, x_guilds_prefixes xgp where xgp.guild_id = $1 and xgp.prefix_id = p.id" - rows, err := utils.Database.Query(queryString, guildID) - if err != nil { - log.Println(err) - return []string{"Go.", "go."} - } - var prefixes []string - for rows.Next() { - var prefix string - err = rows.Scan(&prefix) - if err != nil { - log.Println(err) - return []string{"Go.", "go."} - } - prefixes = append(prefixes, prefix) - } - return prefixes -} - -func ErrorHandler(ErrorChan chan disgoman.CommandError) { - for ce := range ErrorChan { - msg := ce.Message - if msg == "" { - msg = ce.Error.Error() - } - _, _ = ce.Context.Send(msg) - fmt.Println(ce.Error) - } +type CommandManager struct { + UserService UserService + GuildService GuildService + disgoman.CommandManager } diff --git a/guild.go b/guild.go new file mode 100644 index 0000000..f99eaaf --- /dev/null +++ b/guild.go @@ -0,0 +1,22 @@ +package goff + +import "database/sql" + +type Guild struct { + ID string + WelcomeMessage string + GoodbyeMessage string + LoggingChannel string + WelcomeChannel string + PuzzleChannel string + PuzzleRole sql.NullString + Prefixes []string +} + +type GuildService interface { + Guild(id string) (*Guild, error) + CreateGuild(g *Guild) error + DeleteGuild(g *Guild) error + GuildUsers(g *Guild) ([]*User, error) + UpdateGuild(g *Guild) error +} diff --git a/utils/date_strings.go b/internal/discord_utils/date_strings.go similarity index 98% rename from utils/date_strings.go rename to internal/discord_utils/date_strings.go index 510288d..f55d808 100644 --- a/utils/date_strings.go +++ b/internal/discord_utils/date_strings.go @@ -1,4 +1,4 @@ -package utils +package discord_utils import ( "fmt" diff --git a/utils/snowflake.go b/internal/discord_utils/snowflake.go similarity index 97% rename from utils/snowflake.go rename to internal/discord_utils/snowflake.go index f26978c..d521086 100644 --- a/utils/snowflake.go +++ b/internal/discord_utils/snowflake.go @@ -1,4 +1,4 @@ -package utils +package discord_utils import "time" diff --git a/internal/exts/fun/fun.go b/internal/exts/fun/fun.go new file mode 100644 index 0000000..85f3b54 --- /dev/null +++ b/internal/exts/fun/fun.go @@ -0,0 +1,134 @@ +package fun + +import ( + "fmt" + "strconv" + "strings" + + "github.com/dustinpianalto/disgoman" + "github.com/dustinpianalto/rpnparse" +) + +var InterleaveCommand = &disgoman.Command{ + Name: "encode", + Aliases: []string{"e"}, + Description: "Encode 2 numbers", + OwnerOnly: false, + Hidden: false, + RequiredPermissions: 0, + Invoke: interleaveFunc, +} + +func interleaveFunc(ctx disgoman.Context, args []string) { + if len(args) == 2 { + x, err := strconv.ParseInt(args[0], 10, 64) + if err != nil { + return + } + y, err := strconv.ParseInt(args[1], 10, 64) + if err != nil { + return + } + var z = int64(0) + for i := 0; i < 64; i++ { + x_masked_i := x & (1 << i) + y_masked_i := y & (1 << i) + + z |= x_masked_i << i + z |= y_masked_i << (i + 1) + } + ctx.Send(fmt.Sprintf("%v", z)) + } +} + +var DeinterleaveCommand = &disgoman.Command{ + Name: "decode", + Aliases: []string{"d"}, + Description: "Decode 1 number into 2", + OwnerOnly: false, + Hidden: false, + RequiredPermissions: 0, + Invoke: deinterleaveFunc, +} + +func deinterleaveFunc(ctx disgoman.Context, args []string) { + if len(args) == 1 { + z, err := strconv.ParseInt(args[0], 10, 64) + if err != nil { + return + } + var x = int64(0) + var y = int64(0) + i := 0 + for z > 0 { + x |= (z & 1) << i + z >>= 1 + y |= (z & 1) << i + z >>= 1 + i++ + } + ctx.Send(fmt.Sprintf("(%v, %v)", x, y)) + } +} + +var GenerateRPNCommand = &disgoman.Command{ + Name: "RPN", + Aliases: []string{"rpn"}, + Description: "Convert infix to rpn", + OwnerOnly: false, + Hidden: false, + RequiredPermissions: 0, + Invoke: generateRPNFunc, +} + +func generateRPNFunc(ctx disgoman.Context, args []string) { + rpn, err := rpnparse.GenerateRPN(args) + if err != nil { + ctx.Send(err.Error()) + return + } + ctx.Send(rpn) +} + +var ParseRPNCommand = &disgoman.Command{ + Name: "ParseRPN", + Aliases: []string{"PRPN", "prpn"}, + Description: "Parse RPN string and return the result", + OwnerOnly: false, + Hidden: false, + RequiredPermissions: 0, + Invoke: parseRPNFunc, +} + +func parseRPNFunc(ctx disgoman.Context, args []string) { + res, err := rpnparse.ParseRPN(args) + if err != nil { + ctx.Send(err.Error()) + return + } + ctx.Send(fmt.Sprintf("The result is: %v", res)) +} + +var SolveCommand = &disgoman.Command{ + Name: "solve", + Aliases: []string{"math", "infix"}, + Description: "Solve infix equation and return the result", + OwnerOnly: false, + Hidden: false, + RequiredPermissions: 0, + Invoke: solveFunc, +} + +func solveFunc(ctx disgoman.Context, args []string) { + rpn, err := rpnparse.GenerateRPN(args) + if err != nil { + ctx.Send(err.Error()) + return + } + res, err := rpnparse.ParseRPN(strings.Split(rpn, " ")) + if err != nil { + ctx.Send(err.Error()) + return + } + ctx.Send(fmt.Sprintf("The result is: %v", res)) +} diff --git a/internal/exts/guild_management/guild.go b/internal/exts/guild_management/guild.go new file mode 100644 index 0000000..e60386c --- /dev/null +++ b/internal/exts/guild_management/guild.go @@ -0,0 +1,476 @@ +package guild_management + +import ( + "database/sql" + "fmt" + "strings" + + "github.com/dustinpianalto/disgoman" + "github.com/dustinpianalto/goff/internal/postgres" + "github.com/dustinpianalto/goff/internal/services" +) + +// Guild management commands +var SetLoggingChannelCommand = &disgoman.Command{ + Name: "set-logging-channel", + Aliases: []string{"slc"}, + Description: "Set the channel logging messages will be sent to.", + OwnerOnly: false, + Hidden: false, + RequiredPermissions: disgoman.PermissionManageServer, + Invoke: setLoggingChannelFunc, +} + +func setLoggingChannelFunc(ctx disgoman.Context, args []string) { + var idString string + if len(args) > 0 { + idString = args[0] + if strings.HasPrefix(idString, "<#") && strings.HasSuffix(idString, ">") { + idString = idString[2 : len(idString)-1] + } + } else { + idString = "" + } + guild, err := services.GuildService.Guild(ctx.Guild.ID) + if err != nil { + ctx.CommandManager.ErrorChannel <- disgoman.CommandError{ + Context: ctx, + Message: "Error Updating Database", + Error: err, + } + return + } + if idString == "" { + guild.LoggingChannel = idString + err = services.GuildService.UpdateGuild(guild) + if err != nil { + ctx.CommandManager.ErrorChannel <- disgoman.CommandError{ + Context: ctx, + Message: "Error Updating Database", + Error: err, + } + return + } + _, _ = ctx.Send("Logging Channel Updated.") + return + } + channel, err := ctx.Session.State.Channel(idString) + if err != nil { + ctx.CommandManager.ErrorChannel <- disgoman.CommandError{ + Context: ctx, + Message: "Can't find that channel.", + Error: err, + } + return + } + if channel.GuildID != ctx.Guild.ID { + ctx.CommandManager.ErrorChannel <- disgoman.CommandError{ + Context: ctx, + Message: "The channel passed is not in this guild.", + Error: err, + } + return + } + guild.LoggingChannel = channel.ID + err = services.GuildService.UpdateGuild(guild) + if err != nil { + ctx.CommandManager.ErrorChannel <- disgoman.CommandError{ + Context: ctx, + Message: "Error Updating Database", + Error: err, + } + return + } + _, _ = ctx.Send("Logging Channel Updated.") +} + +var GetLoggingChannelCommand = &disgoman.Command{ + Name: "get-logging-channel", + Aliases: []string{"glc"}, + Description: "Gets the channel logging messages will be sent to.", + OwnerOnly: false, + Hidden: false, + RequiredPermissions: disgoman.PermissionManageServer, + Invoke: getLoggingChannelFunc, +} + +func getLoggingChannelFunc(ctx disgoman.Context, _ []string) { + var channelID string + row := postgres.DB.QueryRow("SELECT logging_channel FROM guilds where id=$1", ctx.Guild.ID) + err := row.Scan(&channelID) + if err != nil { + fmt.Println(err) + ctx.CommandManager.ErrorChannel <- disgoman.CommandError{ + Context: ctx, + Message: "Error getting data from the database.", + Error: err, + } + return + } + if channelID == "" { + _, _ = ctx.Send("The logging channel is not set.") + return + } + channel, err := ctx.Session.State.GuildChannel(ctx.Guild.ID, channelID) + if err != nil { + fmt.Println(err) + ctx.CommandManager.ErrorChannel <- disgoman.CommandError{ + Context: ctx, + Message: "I got the channel ID but it does not appear to be a valid channel in this guild.", + Error: err, + } + return + } + _, _ = ctx.Send(fmt.Sprintf("The logging channel is currently %s", channel.Mention())) + return +} + +var SetWelcomeChannelCommand = &disgoman.Command{ + Name: "set-welcome-channel", + Aliases: []string{"swc"}, + Description: "Set the channel welcome messages will be sent to.", + OwnerOnly: false, + Hidden: false, + RequiredPermissions: disgoman.PermissionManageServer, + Invoke: setWelcomeChannelFunc, +} + +func setWelcomeChannelFunc(ctx disgoman.Context, args []string) { + var idString string + if len(args) > 0 { + idString = args[0] + if strings.HasPrefix(idString, "<#") && strings.HasSuffix(idString, ">") { + idString = idString[2 : len(idString)-1] + } + } else { + idString = "" + } + fmt.Println(idString) + if idString == "" { + _, err := postgres.DB.Exec("UPDATE guilds SET welcome_channel='' WHERE id=$1;", ctx.Guild.ID) + if err != nil { + ctx.CommandManager.ErrorChannel <- disgoman.CommandError{ + Context: ctx, + Message: "Error Updating Database", + Error: err, + } + return + } + _, _ = ctx.Send("Welcomer Disabled.") + return + } + channel, err := ctx.Session.State.Channel(idString) + if err != nil { + ctx.CommandManager.ErrorChannel <- disgoman.CommandError{ + Context: ctx, + Message: "Can't find that channel.", + Error: err, + } + return + } + if channel.GuildID != ctx.Guild.ID { + ctx.CommandManager.ErrorChannel <- disgoman.CommandError{ + Context: ctx, + Message: "The channel passed is not in this guild.", + Error: err, + } + return + } + _, err = postgres.DB.Exec("UPDATE guilds SET welcome_channel=$1 WHERE id=$2;", idString, ctx.Guild.ID) + if err != nil { + ctx.CommandManager.ErrorChannel <- disgoman.CommandError{ + Context: ctx, + Message: "Error Updating Database", + Error: err, + } + return + } + _, _ = ctx.Send("Welcome Channel Updated.") + return +} + +var GetWelcomeChannelCommand = &disgoman.Command{ + Name: "get-welcome-channel", + Aliases: []string{"gwc"}, + Description: "Gets the channel welcome messages will be sent to.", + OwnerOnly: false, + Hidden: false, + RequiredPermissions: disgoman.PermissionManageServer, + Invoke: getWelcomeChannelFunc, +} + +func getWelcomeChannelFunc(ctx disgoman.Context, _ []string) { + var channelID string + row := postgres.DB.QueryRow("SELECT welcome_channel FROM guilds where id=$1", ctx.Guild.ID) + err := row.Scan(&channelID) + if err != nil { + fmt.Println(err) + ctx.CommandManager.ErrorChannel <- disgoman.CommandError{ + Context: ctx, + Message: "Error getting data from the database.", + Error: err, + } + return + } + if channelID == "" { + _, _ = ctx.Send("The welcomer is disabled.") + return + } + channel, err := ctx.Session.State.GuildChannel(ctx.Guild.ID, channelID) + if err != nil { + fmt.Println(err) + ctx.CommandManager.ErrorChannel <- disgoman.CommandError{ + Context: ctx, + Message: "I got the channel ID but it does not appear to be a valid channel in this guild.", + Error: err, + } + return + } + _, _ = ctx.Send(fmt.Sprintf("The welcome channel is currently %s", channel.Mention())) +} + +var AddGuildCommand = &disgoman.Command{ + Name: "addGuild", + Aliases: nil, + Description: "Adds the current guild to the database", + OwnerOnly: true, + Hidden: false, + RequiredPermissions: 0, + Invoke: addGuildCommandFunc, +} + +func addGuildCommandFunc(ctx disgoman.Context, args []string) { + var guildID string + row := postgres.DB.QueryRow("SELECT id FROM guilds where id=$1", ctx.Guild.ID) + err := row.Scan(&guildID) + if err == nil { + ctx.CommandManager.ErrorChannel <- disgoman.CommandError{ + Context: ctx, + Message: "This guild is already in my database", + Error: err, + } + return + } + + _, err = postgres.DB.Query("INSERT INTO guilds (id) VALUES ($1)", ctx.Guild.ID) + if err != nil { + fmt.Println(err) + ctx.CommandManager.ErrorChannel <- disgoman.CommandError{ + Context: ctx, + Message: "There was a problem inserting this guild into the database", + Error: err, + } + return + } + _, _ = ctx.Send("This guild has been added.") + +} + +var SetPuzzleChannelCommand = &disgoman.Command{ + Name: "set-puzzle-channel", + Aliases: []string{"spc"}, + Description: "Set the channel puzzle messages will be sent to.", + OwnerOnly: false, + Hidden: false, + RequiredPermissions: disgoman.PermissionManageServer, + Invoke: setPuzzleChannelFunc, +} + +func setPuzzleChannelFunc(ctx disgoman.Context, args []string) { + var idString string + if len(args) > 0 { + idString = args[0] + if strings.HasPrefix(idString, "<#") && strings.HasSuffix(idString, ">") { + idString = idString[2 : len(idString)-1] + } + } else { + idString = "" + } + fmt.Println(idString) + if idString == "" { + _, err := postgres.DB.Exec("UPDATE guilds SET puzzle_channel='' WHERE id=$1;", ctx.Guild.ID) + if err != nil { + ctx.CommandManager.ErrorChannel <- disgoman.CommandError{ + Context: ctx, + Message: "Error Updating Database", + Error: err, + } + return + } + _, _ = ctx.Send("Puzzle Channel Updated.") + return + } + channel, err := ctx.Session.State.Channel(idString) + if err != nil { + ctx.CommandManager.ErrorChannel <- disgoman.CommandError{ + Context: ctx, + Message: "Can't find that channel.", + Error: err, + } + return + } + if channel.GuildID != ctx.Guild.ID { + ctx.CommandManager.ErrorChannel <- disgoman.CommandError{ + Context: ctx, + Message: "The channel passed is not in this guild.", + Error: err, + } + return + } + _, err = postgres.DB.Exec("UPDATE guilds SET puzzle_channel=$1 WHERE id=$2;", idString, ctx.Guild.ID) + if err != nil { + ctx.CommandManager.ErrorChannel <- disgoman.CommandError{ + Context: ctx, + Message: "Error Updating Database", + Error: err, + } + return + } + _, _ = ctx.Send("Puzzle Channel Updated.") +} + +var GetPuzzleChannelCommand = &disgoman.Command{ + Name: "get-puzzle-channel", + Aliases: []string{"gpc"}, + Description: "Gets the channel puzzle messages will be sent to.", + OwnerOnly: false, + Hidden: false, + RequiredPermissions: disgoman.PermissionManageServer, + Invoke: getPuzzleChannelFunc, +} + +func getPuzzleChannelFunc(ctx disgoman.Context, _ []string) { + var channelID string + row := postgres.DB.QueryRow("SELECT puzzle_channel FROM guilds where id=$1", ctx.Guild.ID) + err := row.Scan(&channelID) + if err != nil { + fmt.Println(err) + ctx.CommandManager.ErrorChannel <- disgoman.CommandError{ + Context: ctx, + Message: "Error getting data from the database.", + Error: err, + } + return + } + if channelID == "" { + _, _ = ctx.Send("The puzzle channel is not set.") + return + } + channel, err := ctx.Session.State.GuildChannel(ctx.Guild.ID, channelID) + if err != nil { + fmt.Println(err) + ctx.CommandManager.ErrorChannel <- disgoman.CommandError{ + Context: ctx, + Message: "I got the channel ID but it does not appear to be a valid channel in this guild.", + Error: err, + } + return + } + _, _ = ctx.Send(fmt.Sprintf("The puzzle channel is currently %s", channel.Mention())) + return +} + +var SetPuzzleRoleCommand = &disgoman.Command{ + Name: "set-puzzle-role", + Aliases: []string{"spr"}, + Description: "Set the role to be pinged when there is a new puzzle", + OwnerOnly: false, + Hidden: false, + RequiredPermissions: disgoman.PermissionManageServer, + Invoke: setPuzzleRoleFunc, +} + +func setPuzzleRoleFunc(ctx disgoman.Context, args []string) { + var idString string + if len(args) > 0 { + idString = args[0] + if strings.HasPrefix(idString, "<@&") && strings.HasSuffix(idString, ">") { + idString = idString[3 : len(idString)-1] + } + } else { + idString = "" + } + fmt.Println(idString) + if idString == "" { + _, err := postgres.DB.Exec("UPDATE guilds SET puzzle_role=NULL WHERE id=$1;", ctx.Guild.ID) + if err != nil { + ctx.CommandManager.ErrorChannel <- disgoman.CommandError{ + Context: ctx, + Message: "Error Updating Database", + Error: err, + } + return + } + _, _ = ctx.Send("Puzzle Role Cleared.") + return + } + role, err := ctx.Session.State.Role(ctx.Guild.ID, idString) + if err != nil { + ctx.CommandManager.ErrorChannel <- disgoman.CommandError{ + Context: ctx, + Message: "Can't find that Role.", + Error: err, + } + return + } + _, err = postgres.DB.Exec("INSERT INTO roles (id, guild_id) VALUES ($1, $2) ON CONFLICT DO NOTHING", role.ID, ctx.Guild.ID) + if err != nil { + ctx.CommandManager.ErrorChannel <- disgoman.CommandError{ + Context: ctx, + Message: "Error Updating Database", + Error: err, + } + return + } + _, err = postgres.DB.Exec("UPDATE guilds SET puzzle_role=$1 WHERE id=$2;", role.ID, ctx.Guild.ID) + if err != nil { + ctx.CommandManager.ErrorChannel <- disgoman.CommandError{ + Context: ctx, + Message: "Error Updating Database", + Error: err, + } + return + } + _, _ = ctx.Send("Puzzle Role Updated.") +} + +var GetPuzzleRoleCommand = &disgoman.Command{ + Name: "get-puzzle-role", + Aliases: []string{"gpr"}, + Description: "Get the role that will be pinged when there is a new puzzle", + OwnerOnly: false, + Hidden: false, + RequiredPermissions: disgoman.PermissionManageServer, + Invoke: getPuzzleRoleFunc, +} + +func getPuzzleRoleFunc(ctx disgoman.Context, _ []string) { + var roleID sql.NullString + row := postgres.DB.QueryRow("SELECT puzzle_role FROM guilds where id=$1", ctx.Guild.ID) + err := row.Scan(&roleID) + if err != nil { + fmt.Println(err) + ctx.CommandManager.ErrorChannel <- disgoman.CommandError{ + Context: ctx, + Message: "Error getting data from the database.", + Error: err, + } + return + } + if !roleID.Valid { + _, _ = ctx.Send("The puzzle role is not set.") + return + } + role, err := ctx.Session.State.Role(ctx.Guild.ID, roleID.String) + if err != nil { + fmt.Println(err) + ctx.CommandManager.ErrorChannel <- disgoman.CommandError{ + Context: ctx, + Message: "I got the role ID but it does not appear to be a valid role in this guild.", + Error: err, + } + return + } + _, _ = ctx.Send(fmt.Sprintf("The puzzle role is currently %s", role.Mention())) + return +} diff --git a/events/message_events.go b/internal/exts/guild_management/message_events.go similarity index 90% rename from events/message_events.go rename to internal/exts/guild_management/message_events.go index 5167a82..933b2d6 100644 --- a/events/message_events.go +++ b/internal/exts/guild_management/message_events.go @@ -1,11 +1,11 @@ -package events +package guild_management import ( "fmt" "log" "github.com/bwmarrin/discordgo" - "github.com/dustinpianalto/goff/utils" + "github.com/dustinpianalto/goff/internal/postgres" ) func OnMessageUpdate(session *discordgo.Session, m *discordgo.MessageUpdate) { @@ -19,7 +19,7 @@ func OnMessageUpdate(session *discordgo.Session, m *discordgo.MessageUpdate) { return } var channelID string - row := utils.Database.QueryRow("SELECT logging_channel FROM guilds where id=$1", msg.GuildID) + row := postgres.DB.QueryRow("SELECT logging_channel FROM guilds where id=$1", msg.GuildID) err := row.Scan(&channelID) if err != nil || channelID == "" { return @@ -56,7 +56,7 @@ func OnMessageDelete(session *discordgo.Session, m *discordgo.MessageDelete) { return } var channelID string - row := utils.Database.QueryRow("SELECT logging_channel FROM guilds where id=$1", msg.GuildID) + row := postgres.DB.QueryRow("SELECT logging_channel FROM guilds where id=$1", msg.GuildID) err := row.Scan(&channelID) if err != nil || channelID == "" { return diff --git a/internal/exts/init.go b/internal/exts/init.go new file mode 100644 index 0000000..081f07a --- /dev/null +++ b/internal/exts/init.go @@ -0,0 +1,56 @@ +package exts + +import ( + "github.com/dustinpianalto/disgoman" + "github.com/dustinpianalto/goff/internal/exts/fun" + "github.com/dustinpianalto/goff/internal/exts/guild_management" + "github.com/dustinpianalto/goff/internal/exts/roles" + "github.com/dustinpianalto/goff/internal/exts/tags" + "github.com/dustinpianalto/goff/internal/exts/tasks" + "github.com/dustinpianalto/goff/internal/exts/user_management" + "github.com/dustinpianalto/goff/internal/exts/utils" + + "github.com/dustinpianalto/goff/internal/exts/p_interpreter" +) + +func AddCommandHandlers(h *disgoman.CommandManager) { + // Arguments: + // name - command name - string + // desc - command description - string + // owneronly - only allow owners to run - bool + // hidden - hide command from non-owners - bool + // perms - permissisions required - anpan.Permission (int) + // type - command type, sets where the command is available + // run - function to run - func(anpan.Context, []string) / CommandRunFunc + _ = h.AddCommand(utils.UserCommand) + _ = h.AddCommand(utils.SayCommand) + _ = h.AddCommand(utils.GitCommand) + _ = h.AddCommand(utils.InviteCommand) + _ = h.AddCommand(utils.PingCommand) + _ = h.AddCommand(tasks.AddReminderCommand) + _ = h.AddCommand(tags.AddTagCommand) + _ = h.AddCommand(tags.TagCommand) + _ = h.AddCommand(roles.MakeRoleSelfAssignableCommand) + _ = h.AddCommand(roles.RemoveSelfAssignableCommand) + _ = h.AddCommand(roles.SelfAssignRoleCommand) + _ = h.AddCommand(roles.UnAssignRoleCommand) + _ = h.AddCommand(p_interpreter.PCommand) + _ = h.AddCommand(fun.InterleaveCommand) + _ = h.AddCommand(fun.DeinterleaveCommand) + _ = h.AddCommand(fun.GenerateRPNCommand) + _ = h.AddCommand(fun.ParseRPNCommand) + _ = h.AddCommand(fun.SolveCommand) + _ = h.AddCommand(user_management.KickUserCommand) + _ = h.AddCommand(user_management.BanUserCommand) + _ = h.AddCommand(user_management.UnbanUserCommand) + _ = h.AddCommand(guild_management.SetLoggingChannelCommand) + _ = h.AddCommand(guild_management.GetLoggingChannelCommand) + _ = h.AddCommand(guild_management.SetWelcomeChannelCommand) + _ = h.AddCommand(guild_management.GetWelcomeChannelCommand) + _ = h.AddCommand(guild_management.AddGuildCommand) + _ = h.AddCommand(guild_management.SetPuzzleChannelCommand) + _ = h.AddCommand(guild_management.GetPuzzleChannelCommand) + _ = h.AddCommand(guild_management.SetPuzzleRoleCommand) + _ = h.AddCommand(guild_management.GetPuzzleRoleCommand) + +} diff --git a/utils/logging.go b/internal/exts/logging/logging.go similarity index 78% rename from utils/logging.go rename to internal/exts/logging/logging.go index c3415c4..5e65c72 100644 --- a/utils/logging.go +++ b/internal/exts/logging/logging.go @@ -1,8 +1,10 @@ -package utils +package logging import ( "fmt" + "github.com/bwmarrin/discordgo" + "github.com/dustinpianalto/goff/internal/postgres" ) var LoggingChannel = make(chan *LogEvent, 10) @@ -19,7 +21,7 @@ type LogEvent struct { func LoggingHandler(lc chan *LogEvent) { for event := range lc { var channelID string - row := Database.QueryRow("SELECT logging_channel FROM guilds where id=$1", event.GuildID) + row := postgres.DB.QueryRow("SELECT logging_channel FROM guilds where id=$1", event.GuildID) err := row.Scan(&channelID) if err != nil { fmt.Println(err) diff --git a/exts/P_interpreter.go b/internal/exts/p_interpreter/P_interpreter.go similarity index 73% rename from exts/P_interpreter.go rename to internal/exts/p_interpreter/P_interpreter.go index 57f4b77..b6a83f5 100644 --- a/exts/P_interpreter.go +++ b/internal/exts/p_interpreter/P_interpreter.go @@ -1,13 +1,24 @@ -package exts +package p_interpreter import ( "errors" "fmt" - "github.com/dustinpianalto/disgoman" "strings" + + "github.com/dustinpianalto/disgoman" ) -func pCommand(ctx disgoman.Context, args []string) { +var PCommand = &disgoman.Command{ + Name: "P", + Aliases: nil, + Description: "Interpret a P\" program and return the results", + OwnerOnly: false, + Hidden: false, + RequiredPermissions: 0, + Invoke: pCommandFunc, +} + +func pCommandFunc(ctx disgoman.Context, args []string) { input := strings.Join(args, "") const LENGTH = 1999 var mem [LENGTH]byte @@ -57,7 +68,7 @@ func pCommand(ctx disgoman.Context, args []string) { } } } else { - ctx.ErrorChannel <- disgoman.CommandError{ + ctx.CommandManager.ErrorChannel <- disgoman.CommandError{ Context: ctx, Message: fmt.Sprintf("Invalid Character: %v", input[i]), Error: errors.New("invalid character"), @@ -73,7 +84,7 @@ func pCommand(ctx disgoman.Context, args []string) { } _, err := ctx.Send(string(out)) if err != nil { - ctx.ErrorChannel <- disgoman.CommandError{ + ctx.CommandManager.ErrorChannel <- disgoman.CommandError{ Context: ctx, Message: "Couldn't send results", Error: err, diff --git a/exts/roles.go b/internal/exts/roles/roles.go similarity index 60% rename from exts/roles.go rename to internal/exts/roles/roles.go index 35237e1..e7f0a01 100644 --- a/exts/roles.go +++ b/internal/exts/roles/roles.go @@ -1,4 +1,4 @@ -package exts +package roles import ( "fmt" @@ -7,10 +7,20 @@ import ( "github.com/bwmarrin/discordgo" "github.com/dustinpianalto/disgoman" - "github.com/dustinpianalto/goff/utils" + "github.com/dustinpianalto/goff/internal/postgres" ) -func makeRoleSelfAssignable(ctx disgoman.Context, args []string) { +var MakeRoleSelfAssignableCommand = &disgoman.Command{ + Name: "make-role-self-assignable", + Aliases: []string{"makesar"}, + Description: "Makes the passed in role self assignable by anyone", + OwnerOnly: false, + Hidden: false, + RequiredPermissions: disgoman.PermissionManageServer, + Invoke: makeRoleSelfAssignableCommandFunc, +} + +func makeRoleSelfAssignableCommandFunc(ctx disgoman.Context, args []string) { var roleString string var roleID string if len(args) > 0 { @@ -31,16 +41,16 @@ func makeRoleSelfAssignable(ctx disgoman.Context, args []string) { var role *discordgo.Role var err error if role, err = ctx.Session.State.Role(ctx.Guild.ID, roleID); err != nil { - ctx.ErrorChannel <- disgoman.CommandError{ + ctx.CommandManager.ErrorChannel <- disgoman.CommandError{ Context: ctx, Message: "Can't find that Role.", Error: err, } return } - _, err = utils.Database.Exec("INSERT INTO roles (id, guild_id, self_assignable) VALUES ($1, $2, true) ON CONFLICT (id) DO UPDATE SET self_assignable=true", role.ID, ctx.Guild.ID) + _, err = postgres.DB.Exec("INSERT INTO roles (id, guild_id, self_assignable) VALUES ($1, $2, true) ON CONFLICT (id) DO UPDATE SET self_assignable=true", role.ID, ctx.Guild.ID) if err != nil { - ctx.ErrorChannel <- disgoman.CommandError{ + ctx.CommandManager.ErrorChannel <- disgoman.CommandError{ Context: ctx, Message: "Error Updating Database", Error: err, @@ -50,7 +60,17 @@ func makeRoleSelfAssignable(ctx disgoman.Context, args []string) { _, _ = ctx.Send(fmt.Sprintf("%s is now self assignable", role.Name)) } -func removeSelfAssignableRole(ctx disgoman.Context, args []string) { +var RemoveSelfAssignableCommand = &disgoman.Command{ + Name: "remove-self-assignable-role", + Aliases: []string{"removesar"}, + Description: "Makes a role that was previously self assignable not so", + OwnerOnly: false, + Hidden: false, + RequiredPermissions: disgoman.PermissionManageServer, + Invoke: removeSelfAssignableRoleCommandFunc, +} + +func removeSelfAssignableRoleCommandFunc(ctx disgoman.Context, args []string) { var roleString string var roleID string if len(args) > 0 { @@ -71,16 +91,16 @@ func removeSelfAssignableRole(ctx disgoman.Context, args []string) { var err error var role *discordgo.Role if role, err = ctx.Session.State.Role(ctx.Guild.ID, roleID); err != nil { - ctx.ErrorChannel <- disgoman.CommandError{ + ctx.CommandManager.ErrorChannel <- disgoman.CommandError{ Context: ctx, Message: "Can't find that Role.", Error: err, } return } - _, err = utils.Database.Exec("INSERT INTO roles (id, guild_id, self_assignable) VALUES ($1, $2, false) ON CONFLICT (id) DO UPDATE SET self_assignable=false", role.ID, ctx.Guild.ID) + _, err = postgres.DB.Exec("INSERT INTO roles (id, guild_id, self_assignable) VALUES ($1, $2, false) ON CONFLICT (id) DO UPDATE SET self_assignable=false", role.ID, ctx.Guild.ID) if err != nil { - ctx.ErrorChannel <- disgoman.CommandError{ + ctx.CommandManager.ErrorChannel <- disgoman.CommandError{ Context: ctx, Message: "Error Updating Database", Error: err, @@ -90,7 +110,17 @@ func removeSelfAssignableRole(ctx disgoman.Context, args []string) { _, _ = ctx.Send(fmt.Sprintf("%s's self assignability has been removed.", role.Name)) } -func selfAssignRole(ctx disgoman.Context, args []string) { +var SelfAssignRoleCommand = &disgoman.Command{ + Name: "giverole", + Aliases: []string{"iwant", "givetome"}, + Description: "Assigns a person the passed in role if it is self assignable", + OwnerOnly: false, + Hidden: false, + RequiredPermissions: 0, + Invoke: selfAssignRoleCommandFunc, +} + +func selfAssignRoleCommandFunc(ctx disgoman.Context, args []string) { var roleString string var roleID string if len(args) > 0 { @@ -111,7 +141,7 @@ func selfAssignRole(ctx disgoman.Context, args []string) { var role *discordgo.Role var err error if role, err = ctx.Session.State.Role(ctx.Guild.ID, roleID); err != nil { - ctx.ErrorChannel <- disgoman.CommandError{ + ctx.CommandManager.ErrorChannel <- disgoman.CommandError{ Context: ctx, Message: "Can't find that Role.", Error: err, @@ -123,9 +153,9 @@ func selfAssignRole(ctx disgoman.Context, args []string) { return } var selfAssignable bool - err = utils.Database.QueryRow("SELECT self_assignable FROM roles where id=$1", role.ID).Scan(&selfAssignable) + err = postgres.DB.QueryRow("SELECT self_assignable FROM roles where id=$1", role.ID).Scan(&selfAssignable) if err != nil { - ctx.ErrorChannel <- disgoman.CommandError{ + ctx.CommandManager.ErrorChannel <- disgoman.CommandError{ Context: ctx, Message: "Error Updating Database", Error: err, @@ -138,7 +168,7 @@ func selfAssignRole(ctx disgoman.Context, args []string) { } err = ctx.Session.GuildMemberRoleAdd(ctx.Guild.ID, ctx.User.ID, role.ID) if err != nil { - ctx.ErrorChannel <- disgoman.CommandError{ + ctx.CommandManager.ErrorChannel <- disgoman.CommandError{ Context: ctx, Message: "There was a problem adding that role to you.", Error: err, @@ -148,7 +178,17 @@ func selfAssignRole(ctx disgoman.Context, args []string) { _, _ = ctx.Send(fmt.Sprintf("Congratulations! The %s role has been added to your... Ummm... Thing.", role.Name)) } -func unAssignRole(ctx disgoman.Context, args []string) { +var UnAssignRoleCommand = &disgoman.Command{ + Name: "removerole", + Aliases: []string{"idon'twant"}, + Description: "Removes a role from a person if the role is self assignable", + OwnerOnly: false, + Hidden: false, + RequiredPermissions: 0, + Invoke: unAssignRoleCommandFunc, +} + +func unAssignRoleCommandFunc(ctx disgoman.Context, args []string) { var roleString string var roleID string if len(args) > 0 { @@ -169,7 +209,7 @@ func unAssignRole(ctx disgoman.Context, args []string) { var role *discordgo.Role var err error if role, err = ctx.Session.State.Role(ctx.Guild.ID, roleID); err != nil { - ctx.ErrorChannel <- disgoman.CommandError{ + ctx.CommandManager.ErrorChannel <- disgoman.CommandError{ Context: ctx, Message: "Can't find that Role.", Error: err, @@ -181,9 +221,9 @@ func unAssignRole(ctx disgoman.Context, args []string) { return } var selfAssignable bool - err = utils.Database.QueryRow("SELECT self_assignable FROM roles where id=$1", role.ID).Scan(&selfAssignable) + err = postgres.DB.QueryRow("SELECT self_assignable FROM roles where id=$1", role.ID).Scan(&selfAssignable) if err != nil { - ctx.ErrorChannel <- disgoman.CommandError{ + ctx.CommandManager.ErrorChannel <- disgoman.CommandError{ Context: ctx, Message: "Error Updating Database", Error: err, @@ -196,7 +236,7 @@ func unAssignRole(ctx disgoman.Context, args []string) { } err = ctx.Session.GuildMemberRoleRemove(ctx.Guild.ID, ctx.User.ID, role.ID) if err != nil { - ctx.ErrorChannel <- disgoman.CommandError{ + ctx.CommandManager.ErrorChannel <- disgoman.CommandError{ Context: ctx, Message: "There was a problem removing that role from you.", Error: err, diff --git a/exts/tags.go b/internal/exts/tags/tags.go similarity index 57% rename from exts/tags.go rename to internal/exts/tags/tags.go index 0d0ef91..98b0458 100644 --- a/exts/tags.go +++ b/internal/exts/tags/tags.go @@ -1,4 +1,4 @@ -package exts +package tags import ( "errors" @@ -7,20 +7,31 @@ import ( "strings" "github.com/dustinpianalto/disgoman" - "github.com/dustinpianalto/goff/utils" + "github.com/dustinpianalto/goff/internal/postgres" ) -func addTagCommand(ctx disgoman.Context, input []string) { +var AddTagCommand = &disgoman.Command{ + Name: "addtag", + Aliases: nil, + Description: "Add a tag", + OwnerOnly: false, + Hidden: false, + RequiredPermissions: 0, + SanitizeEveryone: true, + Invoke: addTagCommandFunc, +} + +func addTagCommandFunc(ctx disgoman.Context, input []string) { if len(input) >= 1 { queryString := `SELECT tags.id, tags.tag, tags.content from tags WHERE tags.guild_id = $1 AND tags.tag = $2;` - row := utils.Database.QueryRow(queryString, ctx.Guild.ID, input[0]) + row := postgres.DB.QueryRow(queryString, ctx.Guild.ID, input[0]) var dest string if err := row.Scan(&dest); err != nil { tag := input[0] if tag == "" { - ctx.ErrorChannel <- disgoman.CommandError{ + ctx.CommandManager.ErrorChannel <- disgoman.CommandError{ Context: ctx, Message: "That is not a valid tag name", Error: err, @@ -28,7 +39,7 @@ func addTagCommand(ctx disgoman.Context, input []string) { return } if len(input) <= 1 { - ctx.ErrorChannel <- disgoman.CommandError{ + ctx.CommandManager.ErrorChannel <- disgoman.CommandError{ Context: ctx, Message: "I got a name but no value", Error: err, @@ -37,7 +48,7 @@ func addTagCommand(ctx disgoman.Context, input []string) { } value := strings.Join(input[1:], " ") if value == "" { - ctx.ErrorChannel <- disgoman.CommandError{ + ctx.CommandManager.ErrorChannel <- disgoman.CommandError{ Context: ctx, Message: "You have to include a content for the tag", Error: err, @@ -45,9 +56,9 @@ func addTagCommand(ctx disgoman.Context, input []string) { return } queryString = `INSERT INTO tags (tag, content, creator, guild_id) VALUES ($1, $2, $3, $4);` - _, err := utils.Database.Exec(queryString, tag, value, ctx.Message.Author.ID, ctx.Guild.ID) + _, err := postgres.DB.Exec(queryString, tag, value, ctx.Message.Author.ID, ctx.Guild.ID) if err != nil { - ctx.ErrorChannel <- disgoman.CommandError{ + ctx.CommandManager.ErrorChannel <- disgoman.CommandError{ Context: ctx, Message: "", Error: err, @@ -57,7 +68,7 @@ func addTagCommand(ctx disgoman.Context, input []string) { ctx.Send(fmt.Sprintf("Tag %v added successfully.", tag)) return } else { - ctx.ErrorChannel <- disgoman.CommandError{ + ctx.CommandManager.ErrorChannel <- disgoman.CommandError{ Context: ctx, Message: "That tag already exists", Error: err, @@ -66,7 +77,7 @@ func addTagCommand(ctx disgoman.Context, input []string) { } } else { ctx.Send("You need to tell me what tag you want to add...") - ctx.ErrorChannel <- disgoman.CommandError{ + ctx.CommandManager.ErrorChannel <- disgoman.CommandError{ Context: ctx, Message: "You need to tell me what tag you want to add...", Error: errors.New("nothing to do"), @@ -75,14 +86,24 @@ func addTagCommand(ctx disgoman.Context, input []string) { } } -func tagCommand(ctx disgoman.Context, args []string) { +var TagCommand = &disgoman.Command{ + Name: "tag", + Aliases: nil, + Description: "Get a tag", + OwnerOnly: false, + Hidden: false, + RequiredPermissions: 0, + Invoke: tagCommandFunc, +} + +func tagCommandFunc(ctx disgoman.Context, args []string) { if len(args) >= 1 { tagString := strings.Join(args, " ") queryString := `SELECT tags.id, tags.tag, tags.content from tags WHERE tags.guild_id = $1;` - rows, err := utils.Database.Query(queryString, ctx.Guild.ID) + rows, err := postgres.DB.Query(queryString, ctx.Guild.ID) if err != nil { - ctx.ErrorChannel <- disgoman.CommandError{ + ctx.CommandManager.ErrorChannel <- disgoman.CommandError{ Context: ctx, Message: "", Error: err, @@ -96,14 +117,14 @@ func tagCommand(ctx disgoman.Context, args []string) { content string ) if err := rows.Scan(&id, &tag, &content); err != nil { - log.Fatal(err) + log.Println(err) } if tagString == tag { ctx.Send(content) return } } - ctx.ErrorChannel <- disgoman.CommandError{ + ctx.CommandManager.ErrorChannel <- disgoman.CommandError{ Context: ctx, Message: fmt.Sprintf("Tag %v not found", args[0]), Error: err, @@ -112,7 +133,7 @@ func tagCommand(ctx disgoman.Context, args []string) { } } else { ctx.Send("I need a tag to fetch...") - ctx.ErrorChannel <- disgoman.CommandError{ + ctx.CommandManager.ErrorChannel <- disgoman.CommandError{ Context: ctx, Message: "I need a tag to fetch...", Error: errors.New("nothing to do"), diff --git a/utils/tasks.go b/internal/exts/tasks/reminders.go similarity index 90% rename from utils/tasks.go rename to internal/exts/tasks/reminders.go index 3af8494..a295cc4 100644 --- a/utils/tasks.go +++ b/internal/exts/tasks/reminders.go @@ -1,10 +1,12 @@ -package utils +package tasks import ( "fmt" - "github.com/bwmarrin/discordgo" "log" "time" + + "github.com/bwmarrin/discordgo" + "github.com/dustinpianalto/goff/internal/postgres" ) type Task struct { @@ -20,7 +22,7 @@ type Task struct { func processTask(task *Task, s *discordgo.Session) { query := "SELECT completed, processing from tasks where id = $1" - res, err := Database.Query(query, task.ID) + res, err := postgres.DB.Query(query, task.ID) if err != nil { log.Println(err) return @@ -38,8 +40,8 @@ func processTask(task *Task, s *discordgo.Session) { } closeQuery := "Update tasks set completed = true where id = $1" processQuery := "UPDATE tasks SET processing = true WHERE id = $1" - defer Database.Exec(closeQuery, task.ID) - _, err = Database.Exec(processQuery, task.ID) + defer postgres.DB.Exec(closeQuery, task.ID) + _, err = postgres.DB.Exec(processQuery, task.ID) if err != nil { log.Println(err) return @@ -85,7 +87,7 @@ func processTask(task *Task, s *discordgo.Session) { } } processQuery = "UPDATE tasks SET processing = false WHERE id = $1" - _, err = Database.Exec(processQuery, task.ID) + _, err = postgres.DB.Exec(processQuery, task.ID) if err != nil { log.Println(err) } @@ -95,7 +97,7 @@ func processTask(task *Task, s *discordgo.Session) { func getTasksToRun() []Task { query := "SELECT id, type, content, guild_id, channel_id, user_id, creation_time, trigger_time " + "from tasks where completed is false and processing is false and trigger_time < $1" - res, err := Database.Query(query, time.Now()) + res, err := postgres.DB.Query(query, time.Now()) if err != nil { log.Println(err) } diff --git a/exts/tasks.go b/internal/exts/tasks/tasks.go similarity index 61% rename from exts/tasks.go rename to internal/exts/tasks/tasks.go index fa54327..9be283a 100644 --- a/exts/tasks.go +++ b/internal/exts/tasks/tasks.go @@ -1,4 +1,4 @@ -package exts +package tasks import ( "errors" @@ -7,13 +7,23 @@ import ( "time" "github.com/dustinpianalto/disgoman" - "github.com/dustinpianalto/goff/utils" + "github.com/dustinpianalto/goff/internal/postgres" "github.com/olebedev/when" "github.com/olebedev/when/rules/common" "github.com/olebedev/when/rules/en" ) -func addReminderCommand(ctx disgoman.Context, args []string) { +var AddReminderCommand = &disgoman.Command{ + Name: "remind", + Aliases: nil, + Description: "Remind me at a later time", + OwnerOnly: false, + Hidden: false, + RequiredPermissions: 0, + Invoke: addReminderFunc, +} + +func addReminderFunc(ctx disgoman.Context, args []string) { w := when.New(nil) w.Add(en.All...) w.Add(common.All...) @@ -21,7 +31,7 @@ func addReminderCommand(ctx disgoman.Context, args []string) { text := strings.Join(args, " ") r, err := w.Parse(text, time.Now()) if err != nil { - ctx.ErrorChannel <- disgoman.CommandError{ + ctx.CommandManager.ErrorChannel <- disgoman.CommandError{ Context: ctx, Message: "Error parsing time", Error: err, @@ -29,7 +39,7 @@ func addReminderCommand(ctx disgoman.Context, args []string) { return } if r == nil { - ctx.ErrorChannel <- disgoman.CommandError{ + ctx.CommandManager.ErrorChannel <- disgoman.CommandError{ Context: ctx, Message: "You need to include a valid time", Error: errors.New("no time found"), @@ -39,9 +49,9 @@ func addReminderCommand(ctx disgoman.Context, args []string) { content := strings.Replace(text, r.Text+" ", "", 1) query := "INSERT INTO tasks (type, content, guild_id, channel_id, user_id, trigger_time) " + "VALUES ('Reminder', $1, $2, $3, $4, $5)" - _, err = utils.Database.Exec(query, content, ctx.Guild.ID, ctx.Channel.ID, ctx.User.ID, r.Time) + _, err = postgres.DB.Exec(query, content, ctx.Guild.ID, ctx.Channel.ID, ctx.User.ID, r.Time) if err != nil { - ctx.ErrorChannel <- disgoman.CommandError{ + ctx.CommandManager.ErrorChannel <- disgoman.CommandError{ Context: ctx, Message: "Error adding task to database", Error: err, diff --git a/internal/exts/user_management/member_join.go b/internal/exts/user_management/member_join.go new file mode 100644 index 0000000..b884efa --- /dev/null +++ b/internal/exts/user_management/member_join.go @@ -0,0 +1,73 @@ +package user_management + +import ( + "log" + + "github.com/bwmarrin/discordgo" + "github.com/dustinpianalto/goff" + "github.com/dustinpianalto/goff/internal/services" +) + +func OnGuildMemberAdd(s *discordgo.Session, member *discordgo.GuildMemberAdd) { + defer func() { + if r := recover(); r != nil { + log.Println("Recovered from panic in OnGuildMemberAdd", r) + } + }() + user, err := services.UserService.User(member.User.ID) + if err != nil { + log.Println("Error getting user from database: ", err) + user = &goff.User{ + ID: member.User.ID, + Banned: false, + Logging: true, + IsActive: true, + IsStaff: false, + IsAdmin: false, + } + err := services.UserService.CreateUser(user) + if err != nil { + log.Println("Error adding user to database: ", err) + } + } + if !user.IsActive { + user.IsActive = true + err = services.UserService.UpdateUser(user) + if err != nil { + log.Println("Error marking user as active: ", err) + } + } + err = services.UserService.AddUserToGuild(user, &goff.Guild{ID: member.GuildID}) + if err != nil { + log.Println("Error adding user to guild: ", err) + } +} + +func OnGuildMemberRemove(s *discordgo.Session, member *discordgo.GuildMemberRemove) { + defer func() { + if r := recover(); r != nil { + log.Println("Recovered from panic in OnGuildMemberRemove: ", r) + } + }() + user, err := services.UserService.User(member.User.ID) + if err != nil { + log.Println("Error getting user from database: ", err) + return + } + err = services.UserService.RemoveUserFromGuild(user, &goff.Guild{ID: member.GuildID}) + if err != nil { + log.Println("Error removing user from guild: ", err) + } + for i, g := range user.Guilds { + if g == member.GuildID { + user.Guilds[len(user.Guilds)-1], user.Guilds[i] = user.Guilds[i], user.Guilds[len(user.Guilds)-1] + user.Guilds = user.Guilds[:len(user.Guilds)-1] + } + } + if len(user.Guilds) == 0 { + err = services.UserService.MarkUserInactive(user) + if err != nil { + log.Println("Error marking user as inactive: ", err) + } + } +} diff --git a/events/member_events.go b/internal/exts/user_management/member_logging.go similarity index 67% rename from events/member_events.go rename to internal/exts/user_management/member_logging.go index ebc0755..ad24625 100644 --- a/events/member_events.go +++ b/internal/exts/user_management/member_logging.go @@ -1,4 +1,4 @@ -package events +package user_management import ( "fmt" @@ -7,7 +7,8 @@ import ( "time" "github.com/bwmarrin/discordgo" - "github.com/dustinpianalto/goff/utils" + "github.com/dustinpianalto/goff/internal/discord_utils" + "github.com/dustinpianalto/goff/internal/postgres" ) func OnGuildMemberAddLogging(s *discordgo.Session, member *discordgo.GuildMemberAdd) { @@ -17,7 +18,7 @@ func OnGuildMemberAddLogging(s *discordgo.Session, member *discordgo.GuildMember } }() var channelID string - row := utils.Database.QueryRow("SELECT logging_channel FROM guilds where id=$1", member.GuildID) + row := postgres.DB.QueryRow("SELECT logging_channel FROM guilds where id=$1", member.GuildID) err := row.Scan(&channelID) if err != nil || channelID == "" { return @@ -40,11 +41,11 @@ func OnGuildMemberAddLogging(s *discordgo.Session, member *discordgo.GuildMember } int64ID, _ := strconv.ParseInt(member.User.ID, 10, 64) - snow := utils.ParseSnowflake(int64ID) + snow := discord_utils.ParseSnowflake(int64ID) field := &discordgo.MessageEmbedField{ Name: "User was created:", - Value: utils.ParseDateString(snow.CreationTime), + Value: discord_utils.ParseDateString(snow.CreationTime), Inline: false, } @@ -73,7 +74,7 @@ func OnGuildMemberRemoveLogging(s *discordgo.Session, member *discordgo.GuildMem }() timeNow := time.Now() var channelID string - row := utils.Database.QueryRow("SELECT logging_channel FROM guilds where id=$1", member.GuildID) + row := postgres.DB.QueryRow("SELECT logging_channel FROM guilds where id=$1", member.GuildID) err := row.Scan(&channelID) if err != nil || channelID == "" { return @@ -103,7 +104,7 @@ func OnGuildMemberRemoveLogging(s *discordgo.Session, member *discordgo.GuildMem for _, log := range al.AuditLogEntries { if log.TargetID == member.User.ID { int64ID, _ := strconv.ParseInt(log.ID, 10, 64) - logSnow := utils.ParseSnowflake(int64ID) + logSnow := discord_utils.ParseSnowflake(int64ID) if timeNow.Sub(logSnow.CreationTime).Seconds() <= 10 { user, err := s.User(log.UserID) if err == nil { @@ -133,3 +134,34 @@ func OnGuildMemberRemoveLogging(s *discordgo.Session, member *discordgo.GuildMem } s.ChannelMessageSendEmbed(channelID, embed) } + +func AddMemberToDatabase(s *discordgo.Session, m *discordgo.GuildMemberAdd) { + defer func() { + if r := recover(); r != nil { + log.Println("Recovered from panic in AddMemberToDatabase", r) + } + }() + queryString := `INSERT INTO users (id, banned, logging, is_active, is_staff, is_admin) + VALUES ($1, false, false, true, false, false)` + _, err := postgres.DB.Exec(queryString, m.User.ID) + if err != nil { + log.Println(fmt.Errorf("error inserting %s into database: %w", m.User.ID, err)) + return + } + log.Printf("New User: %s\n", m.User.ID) +} + +func MarkMemberInactive(s *discordgo.Session, m *discordgo.GuildMemberRemove) { + defer func() { + if r := recover(); r != nil { + log.Println("Recovered from panic in MarkMemberInactive") + } + }() + queryString := `UPDATE users SET is_active = false WHERE id = $1` + _, err := postgres.DB.Exec(queryString, m.User.ID) + if err != nil { + log.Println(fmt.Errorf("error marking %s as inactive: %w", m.User.ID, err)) + return + } + log.Println("User left: %s") +} diff --git a/exts/user_management.go b/internal/exts/user_management/user_management.go similarity index 73% rename from exts/user_management.go rename to internal/exts/user_management/user_management.go index 08c5bc3..01b88da 100644 --- a/exts/user_management.go +++ b/internal/exts/user_management/user_management.go @@ -1,4 +1,4 @@ -package exts +package user_management import ( "errors" @@ -8,10 +8,20 @@ import ( "github.com/bwmarrin/discordgo" "github.com/dustinpianalto/disgoman" - "github.com/dustinpianalto/goff/utils" + "github.com/dustinpianalto/goff/internal/exts/logging" ) -func kickUserCommand(ctx disgoman.Context, args []string) { +var KickUserCommand = &disgoman.Command{ + Name: "kick", + Aliases: nil, + Description: "Kicks the given user with the given reason", + OwnerOnly: false, + Hidden: false, + RequiredPermissions: disgoman.PermissionKickMembers, + Invoke: kickUserFunc, +} + +func kickUserFunc(ctx disgoman.Context, args []string) { var member *discordgo.Member var err error if len(ctx.Message.Mentions) > 0 { @@ -26,7 +36,7 @@ func kickUserCommand(ctx disgoman.Context, args []string) { err = errors.New("that is not a valid id") } if err != nil { - ctx.ErrorChannel <- disgoman.CommandError{ + ctx.CommandManager.ErrorChannel <- disgoman.CommandError{ Context: ctx, Message: "Couldn't get that member", Error: err, @@ -35,7 +45,7 @@ func kickUserCommand(ctx disgoman.Context, args []string) { } if higher, _ := disgoman.HasHigherRole(ctx.Session, ctx.Guild.ID, ctx.Message.Author.ID, member.User.ID); !higher { - ctx.ErrorChannel <- disgoman.CommandError{ + ctx.CommandManager.ErrorChannel <- disgoman.CommandError{ Context: ctx, Message: "You must have a higher role than the person you are trying to kick", Error: errors.New("need higher role"), @@ -44,7 +54,7 @@ func kickUserCommand(ctx disgoman.Context, args []string) { } if higher, _ := disgoman.HasHigherRole(ctx.Session, ctx.Guild.ID, ctx.Session.State.User.ID, member.User.ID); !higher { - ctx.ErrorChannel <- disgoman.CommandError{ + ctx.CommandManager.ErrorChannel <- disgoman.CommandError{ Context: ctx, Message: "I don't have a high enough role to kick that person", Error: errors.New("need higher role"), @@ -62,7 +72,7 @@ func kickUserCommand(ctx disgoman.Context, args []string) { err = ctx.Session.GuildMemberDeleteWithReason(ctx.Guild.ID, member.User.ID, auditReason) if err != nil { - ctx.ErrorChannel <- disgoman.CommandError{ + ctx.CommandManager.ErrorChannel <- disgoman.CommandError{ Context: ctx, Message: fmt.Sprintf("Something went wrong kicking %v", member.User.Username), Error: err, @@ -70,7 +80,7 @@ func kickUserCommand(ctx disgoman.Context, args []string) { return } - event := &utils.LogEvent{ + event := &logging.LogEvent{ Embed: discordgo.MessageEmbed{ Title: "User Kicked", Description: fmt.Sprintf( @@ -85,11 +95,21 @@ func kickUserCommand(ctx disgoman.Context, args []string) { GuildID: ctx.Guild.ID, Session: ctx.Session, } - utils.LoggingChannel <- event + logging.LoggingChannel <- event _, _ = ctx.Send(fmt.Sprintf("User %v#%v has been kicked.", member.User.Username, member.User.Discriminator)) } -func banUserCommand(ctx disgoman.Context, args []string) { +var BanUserCommand = &disgoman.Command{ + Name: "ban", + Aliases: []string{"ban-no-delete"}, + Description: "Bans the given user with the given reason", + OwnerOnly: false, + Hidden: false, + RequiredPermissions: disgoman.PermissionBanMembers, + Invoke: banUserFunc, +} + +func banUserFunc(ctx disgoman.Context, args []string) { var user *discordgo.User var err error if len(ctx.Message.Mentions) > 0 { @@ -104,7 +124,7 @@ func banUserCommand(ctx disgoman.Context, args []string) { err = errors.New("that is not a valid id") } if err != nil { - ctx.ErrorChannel <- disgoman.CommandError{ + ctx.CommandManager.ErrorChannel <- disgoman.CommandError{ Context: ctx, Message: "Couldn't get that user", Error: err, @@ -114,7 +134,7 @@ func banUserCommand(ctx disgoman.Context, args []string) { if higher, err := disgoman.HasHigherRole(ctx.Session, ctx.Guild.ID, ctx.Message.Author.ID, user.ID); err != nil { if err.Error() == "can't find caller member" { - ctx.ErrorChannel <- disgoman.CommandError{ + ctx.CommandManager.ErrorChannel <- disgoman.CommandError{ Context: ctx, Message: "Who are you?", Error: err, @@ -122,7 +142,7 @@ func banUserCommand(ctx disgoman.Context, args []string) { return } } else if !higher { - ctx.ErrorChannel <- disgoman.CommandError{ + ctx.CommandManager.ErrorChannel <- disgoman.CommandError{ Context: ctx, Message: "You must have a higher role than the person you are trying to ban", Error: errors.New("need higher role"), @@ -132,7 +152,7 @@ func banUserCommand(ctx disgoman.Context, args []string) { if higher, err := disgoman.HasHigherRole(ctx.Session, ctx.Guild.ID, ctx.Session.State.User.ID, user.ID); err != nil { if err.Error() == "can't find caller member" { - ctx.ErrorChannel <- disgoman.CommandError{ + ctx.CommandManager.ErrorChannel <- disgoman.CommandError{ Context: ctx, Message: "Who am I?", Error: err, @@ -140,7 +160,7 @@ func banUserCommand(ctx disgoman.Context, args []string) { return } } else if !higher { - ctx.ErrorChannel <- disgoman.CommandError{ + ctx.CommandManager.ErrorChannel <- disgoman.CommandError{ Context: ctx, Message: "I don't have a high enough role to ban that person", Error: errors.New("need higher role"), @@ -162,7 +182,7 @@ func banUserCommand(ctx disgoman.Context, args []string) { err = ctx.Session.GuildBanCreateWithReason(ctx.Guild.ID, user.ID, auditReason, days) if err != nil { - ctx.ErrorChannel <- disgoman.CommandError{ + ctx.CommandManager.ErrorChannel <- disgoman.CommandError{ Context: ctx, Message: fmt.Sprintf("Something went wrong banning %v", user.Username), Error: err, @@ -170,7 +190,7 @@ func banUserCommand(ctx disgoman.Context, args []string) { return } - event := &utils.LogEvent{ + event := &logging.LogEvent{ Embed: discordgo.MessageEmbed{ Title: "User Banned", Description: fmt.Sprintf( @@ -185,11 +205,21 @@ func banUserCommand(ctx disgoman.Context, args []string) { GuildID: ctx.Guild.ID, Session: ctx.Session, } - utils.LoggingChannel <- event + logging.LoggingChannel <- event _, _ = ctx.Send(fmt.Sprintf("User %v#%v has been banned.", user.Username, user.Discriminator)) } -func unbanUserCommand(ctx disgoman.Context, args []string) { +var UnbanUserCommand = &disgoman.Command{ + Name: "unban", + Aliases: nil, + Description: "Unbans the given user", + OwnerOnly: false, + Hidden: false, + RequiredPermissions: disgoman.PermissionBanMembers, + Invoke: unbanUserFunc, +} + +func unbanUserFunc(ctx disgoman.Context, args []string) { var user *discordgo.User var err error if len(ctx.Message.Mentions) > 0 { @@ -204,7 +234,7 @@ func unbanUserCommand(ctx disgoman.Context, args []string) { err = errors.New("that is not a valid id") } if err != nil { - ctx.ErrorChannel <- disgoman.CommandError{ + ctx.CommandManager.ErrorChannel <- disgoman.CommandError{ Context: ctx, Message: "Couldn't get that user", Error: err, @@ -214,7 +244,7 @@ func unbanUserCommand(ctx disgoman.Context, args []string) { bans, err := ctx.Session.GuildBans(ctx.Guild.ID) if err != nil { - ctx.ErrorChannel <- disgoman.CommandError{ + ctx.CommandManager.ErrorChannel <- disgoman.CommandError{ Context: ctx, Message: "Error processing current bans", Error: err, @@ -225,14 +255,14 @@ func unbanUserCommand(ctx disgoman.Context, args []string) { if ban.User.ID == user.ID { err = ctx.Session.GuildBanDelete(ctx.Guild.ID, user.ID) if err != nil { - ctx.ErrorChannel <- disgoman.CommandError{ + ctx.CommandManager.ErrorChannel <- disgoman.CommandError{ Context: ctx, Message: fmt.Sprintf("Something went wrong unbanning %v", user.Username), Error: err, } return } - event := &utils.LogEvent{ + event := &logging.LogEvent{ Embed: discordgo.MessageEmbed{ Title: "User Banned", Description: fmt.Sprintf( @@ -247,7 +277,7 @@ func unbanUserCommand(ctx disgoman.Context, args []string) { GuildID: ctx.Guild.ID, Session: ctx.Session, } - utils.LoggingChannel <- event + logging.LoggingChannel <- event _, _ = ctx.Send(fmt.Sprintf("User %v#%v has been unbanned.", user.Username, user.Discriminator)) return } diff --git a/exts/utils.go b/internal/exts/utils/utils.go similarity index 61% rename from exts/utils.go rename to internal/exts/utils/utils.go index 4a0e3f6..461e92c 100644 --- a/exts/utils.go +++ b/internal/exts/utils/utils.go @@ -1,4 +1,4 @@ -package exts +package utils import ( "fmt" @@ -9,16 +9,26 @@ import ( "github.com/bwmarrin/discordgo" "github.com/dustinpianalto/disgoman" - "github.com/dustinpianalto/goff/utils" + "github.com/dustinpianalto/goff/internal/discord_utils" ) -func pingCommand(ctx disgoman.Context, _ []string) { +var PingCommand = &disgoman.Command{ + Name: "ping", + Aliases: []string{" "}, + Description: "Check the bot's ping", + OwnerOnly: false, + Hidden: false, + RequiredPermissions: 0, + Invoke: pingCommandFunc, +} + +func pingCommandFunc(ctx disgoman.Context, _ []string) { timeBefore := time.Now() msg, _ := ctx.Send("Pong!") took := time.Now().Sub(timeBefore) _, err := ctx.Session.ChannelMessageEdit(ctx.Message.ChannelID, msg.ID, fmt.Sprintf("Pong!\nPing Took **%s**", took.String())) if err != nil { - ctx.ErrorChannel <- disgoman.CommandError{ + ctx.CommandManager.ErrorChannel <- disgoman.CommandError{ Context: ctx, Message: "Ping Failed", Error: err, @@ -26,7 +36,17 @@ func pingCommand(ctx disgoman.Context, _ []string) { } } -func inviteCommand(ctx disgoman.Context, args []string) { +var InviteCommand = &disgoman.Command{ + Name: "invite", + Aliases: nil, + Description: "Get the invite link for this bot or others", + OwnerOnly: false, + Hidden: false, + RequiredPermissions: 0, + Invoke: inviteCommandFunc, +} + +func inviteCommandFunc(ctx disgoman.Context, args []string) { var ids []string if len(args) == 0 { ids = []string{ctx.Session.State.User.ID} @@ -39,7 +59,7 @@ func inviteCommand(ctx disgoman.Context, args []string) { url := fmt.Sprintf("", id) _, err := ctx.Send(url) if err != nil { - ctx.ErrorChannel <- disgoman.CommandError{ + ctx.CommandManager.ErrorChannel <- disgoman.CommandError{ Context: ctx, Message: "Couldn't send the invite link.", Error: err, @@ -48,7 +68,17 @@ func inviteCommand(ctx disgoman.Context, args []string) { } } -func gitCommand(ctx disgoman.Context, _ []string) { +var GitCommand = &disgoman.Command{ + Name: "git", + Aliases: nil, + Description: "Show my github link", + OwnerOnly: false, + Hidden: false, + RequiredPermissions: 0, + Invoke: gitCommandFunc, +} + +func gitCommandFunc(ctx disgoman.Context, _ []string) { embed := &discordgo.MessageEmbed{ Title: "Hi there, My code is on Github", Color: 0, @@ -56,7 +86,7 @@ func gitCommand(ctx disgoman.Context, _ []string) { } _, err := ctx.Session.ChannelMessageSendEmbed(ctx.Channel.ID, embed) if err != nil { - ctx.ErrorChannel <- disgoman.CommandError{ + ctx.CommandManager.ErrorChannel <- disgoman.CommandError{ Context: ctx, Message: "Git failed", Error: err, @@ -64,13 +94,24 @@ func gitCommand(ctx disgoman.Context, _ []string) { } } -func sayCommand(ctx disgoman.Context, args []string) { +var SayCommand = &disgoman.Command{ + Name: "say", + Aliases: nil, + Description: "Repeat a message", + OwnerOnly: false, + Hidden: false, + RequiredPermissions: 0, + SanitizeEveryone: true, + Invoke: sayCommandFunc, +} + +func sayCommandFunc(ctx disgoman.Context, args []string) { resp := strings.Join(args, " ") resp = strings.ReplaceAll(resp, "@everyone", "@\ufff0everyone") resp = strings.ReplaceAll(resp, "@here", "@\ufff0here") _, err := ctx.Session.ChannelMessageSend(ctx.Message.ChannelID, resp) if err != nil { - ctx.ErrorChannel <- disgoman.CommandError{ + ctx.CommandManager.ErrorChannel <- disgoman.CommandError{ Context: ctx, Message: "Say Failed", Error: err, @@ -78,7 +119,17 @@ func sayCommand(ctx disgoman.Context, args []string) { } } -func userCommand(ctx disgoman.Context, args []string) { +var UserCommand = &disgoman.Command{ + Name: "user", + Aliases: nil, + Description: "Get user info", + OwnerOnly: false, + Hidden: false, + RequiredPermissions: 0, + Invoke: userCommandFunc, +} + +func userCommandFunc(ctx disgoman.Context, args []string) { var member *discordgo.Member if len(args) == 0 { member, _ = ctx.Session.GuildMember(ctx.Guild.ID, ctx.Message.Author.ID) @@ -90,7 +141,7 @@ func userCommand(ctx disgoman.Context, args []string) { member, err = ctx.Session.GuildMember(ctx.Guild.ID, args[0]) } if err != nil { - ctx.ErrorChannel <- disgoman.CommandError{ + ctx.CommandManager.ErrorChannel <- disgoman.CommandError{ Context: ctx, Message: "Couldn't get that member", Error: err, @@ -135,15 +186,15 @@ func userCommand(ctx disgoman.Context, args []string) { guildJoinTime, _ := member.JoinedAt.Parse() guildJoinedField := &discordgo.MessageEmbedField{ Name: "Joined Guild:", - Value: utils.ParseDateString(guildJoinTime), + Value: discord_utils.ParseDateString(guildJoinTime), Inline: false, } int64ID, _ := strconv.ParseInt(member.User.ID, 10, 64) - s := utils.ParseSnowflake(int64ID) + s := discord_utils.ParseSnowflake(int64ID) discordJoinedField := &discordgo.MessageEmbedField{ Name: "Joined Discord:", - Value: utils.ParseDateString(s.CreationTime), + Value: discord_utils.ParseDateString(s.CreationTime), Inline: false, } @@ -160,7 +211,7 @@ func userCommand(ctx disgoman.Context, args []string) { } _, err := ctx.Session.ChannelMessageSendEmbed(ctx.Channel.ID, embed) if err != nil { - ctx.ErrorChannel <- disgoman.CommandError{ + ctx.CommandManager.ErrorChannel <- disgoman.CommandError{ Context: ctx, Message: "Couldn't send the user embed", Error: err, diff --git a/utils/database.go b/internal/postgres/database.go similarity index 73% rename from utils/database.go rename to internal/postgres/database.go index 88d0aed..39f37f5 100644 --- a/utils/database.go +++ b/internal/postgres/database.go @@ -1,4 +1,4 @@ -package utils +package postgres import ( "database/sql" @@ -9,7 +9,7 @@ import ( ) var ( - Database *sql.DB + DB *sql.DB ) func ConnectDatabase(dbConnString string) { @@ -19,11 +19,14 @@ func ConnectDatabase(dbConnString string) { } else { fmt.Println("Database Connected.") } - Database = db + db.SetMaxOpenConns(75) // The RDS instance has a max of 75 open connections + db.SetMaxIdleConns(5) + db.SetConnMaxLifetime(300) + DB = db } func InitializeDatabase() { - _, err := Database.Query("CREATE TABLE IF NOT EXISTS users(" + + _, err := DB.Query("CREATE TABLE IF NOT EXISTS users(" + "id varchar(30) primary key," + "banned bool not null default false," + "logging bool not null default true," + @@ -35,7 +38,7 @@ func InitializeDatabase() { if err != nil { fmt.Println(err) } - _, err = Database.Query("CREATE TABLE IF NOT EXISTS guilds(" + + _, err = DB.Query("CREATE TABLE IF NOT EXISTS guilds(" + "id varchar(30) primary key," + "welcome_message varchar(1000) NOT NULL DEFAULT ''," + "goodbye_message varchar(1000) NOT NULL DEFAULT ''," + @@ -45,14 +48,14 @@ func InitializeDatabase() { if err != nil { fmt.Println(err) } - _, err = Database.Query("CREATE TABLE IF NOT EXISTS prefixes(" + + _, err = DB.Query("CREATE TABLE IF NOT EXISTS prefixes(" + "id serial primary key," + "prefix varchar(10) not null unique default 'Go.'" + ")") if err != nil { fmt.Println(err) } - _, err = Database.Query("CREATE TABLE IF NOT EXISTS tags(" + + _, err = DB.Query("CREATE TABLE IF NOT EXISTS tags(" + "id serial primary key," + "tag varchar(100) not null unique," + "content varchar(1000) not null," + @@ -63,21 +66,21 @@ func InitializeDatabase() { if err != nil { fmt.Println(err) } - _, err = Database.Query("CREATE TABLE IF NOT EXISTS x_users_guilds(" + + _, err = DB.Query("CREATE TABLE IF NOT EXISTS x_users_guilds(" + "guild_id varchar(30) not null references guilds(id)," + "user_id varchar(30) not null references users(id)" + ")") if err != nil { fmt.Println(err) } - _, err = Database.Query("CREATE TABLE IF NOT EXISTS x_guilds_prefixes(" + + _, err = DB.Query("CREATE TABLE IF NOT EXISTS x_guilds_prefixes(" + "guild_id varchar(30) not null references guilds(id)," + "prefix_id int not null references prefixes(id)" + ")") if err != nil { fmt.Println(err) } - _, err = Database.Query("CREATE TABLE IF NOT EXISTS tasks(" + + _, err = DB.Query("CREATE TABLE IF NOT EXISTS tasks(" + "id serial primary key," + "type varchar(10) not null," + "content text not null," + @@ -91,14 +94,14 @@ func InitializeDatabase() { if err != nil { fmt.Println(err) } - _, err = Database.Query(`CREATE TABLE IF NOT EXISTS postfixes( + _, err = DB.Query(`CREATE TABLE IF NOT EXISTS postfixes( id serial primary key, name varchar(100) not null, time timestamp not null default NOW())`) if err != nil { log.Println(err) } - _, err = Database.Exec(`CREATE TABLE IF NOT EXISTS puzzles( + _, err = DB.Exec(`CREATE TABLE IF NOT EXISTS puzzles( id serial primary key, text text not null, time timestamp not null @@ -106,7 +109,7 @@ func InitializeDatabase() { if err != nil { log.Println(err) } - _, err = Database.Exec(`CREATE TABLE IF NOT EXISTS x_guilds_puzzles( + _, err = DB.Exec(`CREATE TABLE IF NOT EXISTS x_guilds_puzzles( id serial primary key, guild_id varchar(30) not null references guilds(id), puzzle_id int not null references puzzles(id), @@ -116,37 +119,37 @@ func InitializeDatabase() { } func LoadTestData() { - _, err := Database.Query("INSERT INTO users (id, banned, logging, steam_id, is_active, is_staff, is_admin) values " + + _, err := DB.Query("INSERT INTO users (id, banned, logging, steam_id, is_active, is_staff, is_admin) values " + "('351794468870946827', false, true, '76561198024193239', true, true, true)," + "('692908139506434065', false, true, '', true, false, false)," + "('396588996706304010', false, true, '', true, true, false)") if err != nil { fmt.Println(err) } - _, err = Database.Query("INSERT INTO guilds (id, welcome_message, goodbye_message) VALUES " + + _, err = DB.Query("INSERT INTO guilds (id, welcome_message, goodbye_message) VALUES " + "('265828729970753537', 'Hey there is someone new here.', 'Well fine then... Just leave without saying goodbye')") if err != nil { fmt.Println(err) } - _, err = Database.Query("INSERT INTO prefixes (prefix) VALUES ('Godev.'), ('godev.'), ('godev,')") + _, err = DB.Query("INSERT INTO prefixes (prefix) VALUES ('Godev.'), ('godev.'), ('godev,')") if err != nil { fmt.Println(err) } - _, err = Database.Query("INSERT INTO x_users_guilds (guild_id, user_id) VALUES " + + _, err = DB.Query("INSERT INTO x_users_guilds (guild_id, user_id) VALUES " + "('265828729970753537', '351794468870946827')," + "('265828729970753537', '692908139506434065')," + "('265828729970753537', '396588996706304010')") if err != nil { fmt.Println(err) } - _, err = Database.Query("INSERT INTO x_guilds_prefixes (guild_id, prefix_id) VALUES " + + _, err = DB.Query("INSERT INTO x_guilds_prefixes (guild_id, prefix_id) VALUES " + "('265828729970753537', 1)," + "('265828729970753537', 2)," + "('265828729970753537', 3)") if err != nil { fmt.Println(err) } - _, err = Database.Query("INSERT INTO tags (tag, content, creator, guild_id) VALUES " + + _, err = DB.Query("INSERT INTO tags (tag, content, creator, guild_id) VALUES " + "('test', 'This is a test of the tag system', '351794468870946827', '265828729970753537')") if err != nil { fmt.Println(err) diff --git a/internal/postgres/guild.go b/internal/postgres/guild.go new file mode 100644 index 0000000..4c7db8c --- /dev/null +++ b/internal/postgres/guild.go @@ -0,0 +1,127 @@ +package postgres + +import ( + "database/sql" + "log" + + "github.com/dustinpianalto/goff" +) + +type GuildService struct { + DB *sql.DB +} + +func (s *GuildService) Guild(id string) (*goff.Guild, error) { + var g goff.Guild + queryString := `SELECT id, welcome_message, goodbye_message, + logging_channel, welcome_channel, puzzle_channel, puzzle_role + FROM guilds + WHERE id = $1` + row := s.DB.QueryRow(queryString, id) + err := row.Scan( + &g.ID, + &g.WelcomeMessage, + &g.GoodbyeMessage, + &g.LoggingChannel, + &g.WelcomeChannel, + &g.PuzzleChannel, + &g.PuzzleRole, + ) + if err != nil { + return nil, err + } + var prefixes []string + queryString = `SELECT p.prefix + FROM prefixes p, x_guilds_prefixes xgp + WHERE p.id = xgp.prefix_id AND xgp.guild_id = $1` + rows, err := s.DB.Query(queryString, id) + if err == nil { + for rows.Next() { + var prefix string + err = rows.Scan(&prefix) + if err != nil { + continue + } + prefixes = append(prefixes, prefix) + } + } + g.Prefixes = prefixes + return &g, nil +} + +func (s *GuildService) CreateGuild(g *goff.Guild) error { + queryString := `INSERT INTO guilds (id, + welcome_message, + goodbye_message, + logging_channel, + welcome_channel, + puzzle_channel, + puzzle_role) + VALUES ($1, $2, $3, $4, $5, $6, $7)` + _, err := s.DB.Exec(queryString, + g.ID, + g.WelcomeMessage, + g.GoodbyeMessage, + g.LoggingChannel, + g.WelcomeChannel, + g.PuzzleChannel, + g.PuzzleRole, + ) + return err +} + +func (s *GuildService) DeleteGuild(g *goff.Guild) error { + queryString := `DELETE CASCADE FROM guilds WHERE id = $1` + _, err := s.DB.Exec(queryString, g.ID) + return err +} + +func (s *GuildService) GuildUsers(g *goff.Guild) ([]*goff.User, error) { + var users []*goff.User + queryString := `SELECT u.id, u.banned, u.logging, u.steam_id, u.is_active, u.is_staff, u.is_admin + FROM users u, x_users_guilds xug + WHERE xug.guild_id = $1 + AND xug.user_id = u.id` + rows, err := s.DB.Query(queryString, g.ID) + if err != nil { + return nil, err + } + for rows.Next() { + var user goff.User + err := rows.Scan(&user.ID, + &user.Banned, + &user.Logging, + &user.SteamID, + &user.IsActive, + &user.IsStaff, + &user.IsAdmin, + ) + if err != nil { + log.Println(err) + continue + } + users = append(users, &user) + } + return users, nil +} + +func (s *GuildService) UpdateGuild(g *goff.Guild) error { + queryString := `UPDATE guilds SET + welcome_message = $1, + goodbye_message = $2, + logging_channel = $3, + welcome_channel = $4, + puzzle_channel = $5, + puzzle_role = $6 + WHERE id = $7` + _, err := s.DB.Exec(queryString, + g.WelcomeMessage, + g.GoodbyeMessage, + g.LoggingChannel, + g.WelcomeChannel, + g.PuzzleChannel, + g.PuzzleRole, + g.ID, + ) + return err +} diff --git a/utils/postfixes.go b/internal/postgres/postfixes.go similarity index 76% rename from utils/postfixes.go rename to internal/postgres/postfixes.go index 0e324af..07224d9 100644 --- a/utils/postfixes.go +++ b/internal/postgres/postfixes.go @@ -1,4 +1,4 @@ -package utils +package postgres import "log" @@ -28,12 +28,16 @@ var postfixes = []postfix{ Name: "3_Update_Guild_Add_Puzzle_Role", Invoke: updateGuildsAddPuzzleRole, }, + postfix{ + Name: "4_Add_Multi_Column_Unique_XUsersGuilds", + Invoke: addMultiColumnUniqueXUsersGuilds, + }, } func RunPostfixes() { for _, postfix := range postfixes { queryString := "SELECT * from postfixes where name = $1" - rows, err := Database.Query(queryString, postfix.Name) + rows, err := DB.Query(queryString, postfix.Name) if err != nil { log.Println(err) continue @@ -45,7 +49,7 @@ func RunPostfixes() { if err != nil { continue } - _, err = Database.Exec("INSERT INTO postfixes (name) VALUES ($1)", postfix.Name) + _, err = DB.Exec("INSERT INTO postfixes (name) VALUES ($1)", postfix.Name) if err != nil { log.Println(err) continue @@ -63,7 +67,7 @@ func updateGuildForPuzzle(revert bool) error { queryString = `ALTER TABLE guilds DROP COLUMN puzzleChat` } - _, err := Database.Exec(queryString) + _, err := DB.Exec(queryString) if err != nil { log.Println(err) return err @@ -80,7 +84,7 @@ func updateXGuildPrefixesToAddID(revert bool) error { queryString = `ALTER TABLE x_guilds_prefixes DROP COLUMN id` } - _, err := Database.Exec(queryString) + _, err := DB.Exec(queryString) if err != nil { log.Println(err) return err @@ -97,7 +101,7 @@ func updateTagsContentLength(revert bool) error { queryString = `ALTER TABLE tags ALTER COLUMN content TYPE varchar(1000)` } - _, err := Database.Exec(queryString) + _, err := DB.Exec(queryString) if err != nil { log.Println(err) return err @@ -118,7 +122,7 @@ func addTableRoles(revert bool) error { } else { queryString = `DROP TABLE roles` } - _, err := Database.Exec(queryString) + _, err := DB.Exec(queryString) if err != nil { log.Println(err) return err @@ -135,7 +139,23 @@ func updateGuildsAddPuzzleRole(revert bool) error { queryString = `ALTER TABLE guilds DROP COLUMN puzzle_role` } - _, err := Database.Exec(queryString) + _, err := DB.Exec(queryString) + if err != nil { + log.Println(err) + return err + } + return nil +} + +func addMultiColumnUniqueXUsersGuilds(revert bool) error { + var queryString string + if !revert { + queryString = `ALTER TABLE x_users_guilds + ADD CONSTRAINT u_user_guild UNIQUE(user_id, guild_id)` + } else { + queryString = `ALTER TABLE DROP CONSTRAINT u_user_guild IF EXISTS` + } + _, err := DB.Exec(queryString) if err != nil { log.Println(err) return err diff --git a/internal/postgres/user.go b/internal/postgres/user.go new file mode 100644 index 0000000..40ae64c --- /dev/null +++ b/internal/postgres/user.go @@ -0,0 +1,84 @@ +package postgres + +import ( + "database/sql" + + "github.com/dustinpianalto/goff" +) + +type UserService struct { + DB *sql.DB +} + +func (s *UserService) User(id string) (*goff.User, error) { + var u goff.User + queryString := `SELECT id, banned, logging, steam_id, is_active, is_staff, is_admin + FROM users WHERE id=$1` + row := s.DB.QueryRow(queryString, id) + if err := row.Scan(&u.ID, &u.Banned, &u.Logging, &u.SteamID, &u.IsActive, &u.IsStaff, &u.IsAdmin); err != nil { + return nil, err + } + var guilds []string + queryString = `SELECT guild_id from x_users_guilds WHERE user_id=$1` + rows, err := s.DB.Query(queryString, id) + if err != nil { + return nil, err + } + for rows.Next() { + var guildID string + err = rows.Scan(&guildID) + if err != nil { + return nil, err + } + guilds = append(guilds, guildID) + } + u.Guilds = guilds + return &u, nil +} + +func (s *UserService) CreateUser(u *goff.User) error { + queryString := `INSERT INTO users (id, banned, logging, steam_id, is_active, is_staff, is_admin) + VALUES ($1, $2, $3, $4, $5, $6, $7)` + _, err := s.DB.Exec(queryString, u.ID, u.Banned, u.Logging, u.SteamID, u.IsActive, u.IsStaff, u.IsAdmin) + return err +} + +func (s *UserService) DeleteUser(u *goff.User) error { + queryString := `DELETE CASCADE FROM x_users_guilds WHERE user_id = $1; DELETE FROM users WHERE id = $1` + _, err := s.DB.Exec(queryString, u.ID) + return err +} + +func (s *UserService) MarkUserInactive(u *goff.User) error { + queryString := `UPDATE users SET is_active = false WHERE id = $1` + _, err := s.DB.Exec(queryString, u.ID) + if err == nil { + u.IsActive = false + } + return err +} + +func (s *UserService) AddUserToGuild(u *goff.User, g *goff.Guild) error { + queryString := `INSERT INTO x_users_guilds (user_id, guild_id) VALUES ($1, $2)` + _, err := s.DB.Exec(queryString, u.ID, g.ID) + return err +} + +func (s *UserService) RemoveUserFromGuild(u *goff.User, g *goff.Guild) error { + queryString := `DELETE FROM x_users_guilds WHERE user_id = $1 AND guild_id = $2` + _, err := s.DB.Exec(queryString, u.ID, g.ID) + return err +} + +func (s *UserService) UpdateUser(u *goff.User) error { + queryString := `UPDATE users SET + banned = $1, + logging = $2, + steam_id = $3, + is_active = $4, + is_staff = $5, + is_admin = $6 + WHERE id = $7` + _, err := s.DB.Exec(queryString, u.Banned, u.Logging, u.SteamID, u.IsActive, u.IsStaff, u.IsAdmin) + return err +} diff --git a/internal/services/services.go b/internal/services/services.go new file mode 100644 index 0000000..d498bd5 --- /dev/null +++ b/internal/services/services.go @@ -0,0 +1,11 @@ +package services + +import "github.com/dustinpianalto/goff" + +var UserService goff.UserService +var GuildService goff.GuildService + +func InitalizeServices(us goff.UserService, gs goff.GuildService) { + UserService = us + GuildService = gs +} diff --git a/utils/email.go b/pkg/email/email.go similarity index 96% rename from utils/email.go rename to pkg/email/email.go index 305ff6a..6b0cd8b 100644 --- a/utils/email.go +++ b/pkg/email/email.go @@ -1,4 +1,4 @@ -package utils +package email import ( "io" @@ -9,6 +9,7 @@ import ( "time" "github.com/bwmarrin/discordgo" + "github.com/dustinpianalto/goff/pkg/puzzles" imap "github.com/emersion/go-imap" "github.com/emersion/go-imap/client" "github.com/emersion/go-message/mail" @@ -117,7 +118,7 @@ func processEmail(r io.Reader, dg *discordgo.Session, wg *sync.WaitGroup) { if addressIn(from, puzzleAddress) && strings.Contains(subject, "Daily Coding Problem:") { log.Println("Processing Puzzle") - ProcessPuzzleEmail(mr, dg) + puzzles.ProcessPuzzleEmail(mr, dg) } } diff --git a/utils/puzzles.go b/pkg/puzzles/puzzles.go similarity index 86% rename from utils/puzzles.go rename to pkg/puzzles/puzzles.go index e27ba5b..f4afedf 100644 --- a/utils/puzzles.go +++ b/pkg/puzzles/puzzles.go @@ -1,4 +1,4 @@ -package utils +package puzzles import ( "io" @@ -8,6 +8,8 @@ import ( "time" "github.com/bwmarrin/discordgo" + "github.com/dustinpianalto/goff" + "github.com/dustinpianalto/goff/internal/postgres" "github.com/emersion/go-message/mail" ) @@ -48,14 +50,14 @@ func ProcessPuzzleEmail(mr *mail.Reader, dg *discordgo.Session) { Text: "Daily Coding Problem", }, } - var guilds []Guild + var guilds []goff.Guild queryString := `SELECT id, puzzle_channel, puzzle_role from guilds` - rows, err := Database.Query(queryString) + rows, err := postgres.DB.Query(queryString) if err != nil { log.Println(err) } for rows.Next() { - var guild Guild + var guild goff.Guild err := rows.Scan(&guild.ID, &guild.PuzzleChannel, &guild.PuzzleRole) if err != nil { log.Println(err) @@ -65,7 +67,7 @@ func ProcessPuzzleEmail(mr *mail.Reader, dg *discordgo.Session) { } var puzzleID int64 queryString = "INSERT INTO puzzles (text, time) VALUES ($1, $2) RETURNING id" - err = Database.QueryRow(queryString, puzzle, date).Scan(&puzzleID) + err = postgres.DB.QueryRow(queryString, puzzle, date).Scan(&puzzleID) if err != nil { log.Println(err) return @@ -91,7 +93,7 @@ func ProcessPuzzleEmail(mr *mail.Reader, dg *discordgo.Session) { log.Println(err) } queryString = "INSERT INTO x_guilds_puzzles (guild_id, puzzle_id, message_id) VALUES ($1, $2, $3)" - _, err = Database.Exec(queryString, g.ID, puzzleID, m.ID) + _, err = postgres.DB.Exec(queryString, g.ID, puzzleID, m.ID) if err != nil { log.Println(err) continue diff --git a/user-data.sh b/user-data.sh deleted file mode 100644 index ec26ade..0000000 --- a/user-data.sh +++ /dev/null @@ -1,4 +0,0 @@ -#!/bin/bash - -/usr/local/bin/docker-compose build || exit -/usr/local/bin/docker-compose up -d \ No newline at end of file diff --git a/user.go b/user.go new file mode 100644 index 0000000..0724236 --- /dev/null +++ b/user.go @@ -0,0 +1,22 @@ +package goff + +type User struct { + ID string + Banned bool + Logging bool + SteamID string + IsActive bool + IsStaff bool + IsAdmin bool + Guilds []string +} + +type UserService interface { + User(id string) (*User, error) + CreateUser(u *User) error + DeleteUser(u *User) error + MarkUserInactive(u *User) error + AddUserToGuild(u *User, g *Guild) error + RemoveUserFromGuild(u *User, g *Guild) error + UpdateUser(u *User) error +} diff --git a/utils/rpn.go b/utils/rpn.go deleted file mode 100644 index db39f5d..0000000 --- a/utils/rpn.go +++ /dev/null @@ -1,156 +0,0 @@ -package utils - -import ( - "fmt" - "strconv" - "strings" -) - -type Operator struct { - Token string - Precedence int - Association string -} - -func (o Operator) HasHigherPrecedence(t Operator) bool { - return o.Precedence < t.Precedence // lower number is higher precedence -} - -func (o Operator) HasEqualPrecedence(t Operator) bool { - return o.Precedence == t.Precedence -} - -func (o Operator) IsLeftAssociative() bool { - return o.Association == "left" -} - -var operators = map[string]Operator{ - "+": Operator{ - Token: "+", - Precedence: 4, - Association: "left", - }, - "-": Operator{ - Token: "-", - Precedence: 4, - Association: "left", - }, - "*": Operator{ - Token: "*", - Precedence: 3, - Association: "left", - }, - "/": Operator{ - Token: "/", - Precedence: 3, - Association: "left", - }, - "%": Operator{ - Token: "%", - Precedence: 3, - Association: "left", - }, - "(": Operator{ - Token: "(", - Precedence: 1, - Association: "left", - }, - ")": Operator{ - Token: ")", - Precedence: 1, - Association: "left", - }, -} - -type Stack []Operator - -func (s *Stack) IsEmpty() bool { - return len(*s) == 0 -} - -func (s *Stack) Push(op Operator) { - *s = append(*s, op) -} - -func (s *Stack) Pop() (Operator, bool) { - if s.IsEmpty() { - return Operator{}, false - } - index := len(*s) - 1 - element := (*s)[index] - *s = (*s)[:index] - return element, true -} - -func (s *Stack) Top() Operator { - if s.IsEmpty() { - return Operator{} - } - return (*s)[len(*s)-1] -} - -func GenerateRPN(tokens []string) (string, error) { - output := "" - s := Stack{} - for _, token := range tokens { - err := processToken(token, &s, &output) - if err != nil { - return "", err - } - } - for !s.IsEmpty() { - ele, _ := s.Pop() - output += " " + ele.Token - } - - return strings.TrimSpace(output), nil -} - -func processToken(t string, s *Stack, o *string) error { - if _, err := strconv.Atoi(t); err == nil { - *o += " " + t - return nil - } else if op, ok := operators[t]; ok { - if op.Token == "(" { - s.Push(op) - } else if op.Token == ")" { - if s.IsEmpty() { - return fmt.Errorf("mismatched parentheses") - } - for s.Top().Token != "(" { - if ele, ok := s.Pop(); ok { - *o += " " + ele.Token - } else { - return fmt.Errorf("mismatched parentheses") - } - if s.IsEmpty() { - break - } - } - s.Pop() // Pop and discard the ( - } else if !s.IsEmpty() { - for { - if (s.Top().HasHigherPrecedence(op) || - (s.Top().HasEqualPrecedence(op) && - op.IsLeftAssociative())) && - s.Top().Token != "(" { - if ele, ok := s.Pop(); ok { - *o += " " + ele.Token - if s.IsEmpty() { - break - } - continue - } else { - break - } - } - break - } - s.Push(op) - } else { - s.Push(op) - } - return nil - } - return fmt.Errorf("invalid character %s", t) -} diff --git a/utils/rpnParser.go b/utils/rpnParser.go deleted file mode 100644 index 7f9dff9..0000000 --- a/utils/rpnParser.go +++ /dev/null @@ -1,95 +0,0 @@ -package utils - -import ( - "errors" - "fmt" - "math" - "strconv" -) - -type FStack []float64 - -func (s *FStack) IsEmpty() bool { - return len(*s) == 0 -} - -func (s *FStack) Push(op float64) { - *s = append(*s, op) -} - -func (s *FStack) Pop() (float64, bool) { - if s.IsEmpty() { - return 0, false - } - index := len(*s) - 1 - element := (*s)[index] - *s = (*s)[:index] - return element, true -} - -func (s *FStack) PopTwo() (float64, float64, bool) { - if s.IsEmpty() || len(*s) < 2 { - return 0, 0, false - } - index := len(*s) - 1 - b := (*s)[index] - a := (*s)[index-1] - *s = (*s)[:index-1] - return a, b, true - -} - -func (s *FStack) Top() float64 { - if s.IsEmpty() { - return 0 - } - return (*s)[len(*s)-1] -} - -func ParseRPN(args []string) (float64, error) { - s := FStack{} - for _, token := range args { - switch token { - case "+": - if a, b, ok := s.PopTwo(); ok { - s.Push(a + b) - } else { - return 0, fmt.Errorf("not enough operands on stack for +: %v", s) - } - case "-": - if a, b, ok := s.PopTwo(); ok { - s.Push(a - b) - } else { - return 0, fmt.Errorf("not enough operands on stack for -: %v", s) - } - case "*": - if a, b, ok := s.PopTwo(); ok { - s.Push(a * b) - } else { - return 0, fmt.Errorf("not enough operands on stack for *: %v", s) - } - case "/": - if a, b, ok := s.PopTwo(); ok { - s.Push(a / b) - } else { - return 0, fmt.Errorf("not enough operands on stack for /: %v", s) - } - case "%": - if a, b, ok := s.PopTwo(); ok { - s.Push(math.Mod(a, b)) - } else { - return 0, fmt.Errorf("not enough operands on stack for %: %v", s) - } - default: - f, err := strconv.ParseFloat(token, 64) - if err != nil { - return 0, err - } - s.Push(f) - } - } - if res, ok := s.Pop(); ok { - return res, nil - } - return 0, errors.New("no result") -} diff --git a/utils/types.go b/utils/types.go deleted file mode 100644 index 06bc2d1..0000000 --- a/utils/types.go +++ /dev/null @@ -1,13 +0,0 @@ -package utils - -import "database/sql" - -type Guild struct { - ID string - WelcomeMessage string - GoodbyeMessage string - LoggingChannel string - WelcomeChannel string - PuzzleChannel string - PuzzleRole sql.NullString -}