diff --git a/exts/guild.go b/exts/guild.go index b5da4bc..a75990c 100644 --- a/exts/guild.go +++ b/exts/guild.go @@ -1,6 +1,7 @@ package exts import ( + "database/sql" "fmt" "strings" @@ -288,3 +289,88 @@ func getPuzzleChannel(ctx disgoman.Context, _ []string) { _, _ = 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 index 94d70ee..dfcda31 100644 --- a/exts/init.go +++ b/exts/init.go @@ -204,6 +204,24 @@ func AddCommandHandlers(h *disgoman.CommandManager) { 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"}, @@ -231,4 +249,40 @@ func AddCommandHandlers(h *disgoman.CommandManager) { 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/exts/roles.go b/exts/roles.go new file mode 100644 index 0000000..35237e1 --- /dev/null +++ b/exts/roles.go @@ -0,0 +1,216 @@ +package exts + +import ( + "fmt" + "strconv" + "strings" + + "github.com/bwmarrin/discordgo" + "github.com/dustinpianalto/disgoman" + "github.com/dustinpianalto/goff/utils" +) + +func makeRoleSelfAssignable(ctx disgoman.Context, args []string) { + var roleString string + var roleID string + if len(args) > 0 { + roleString = strings.Join(args, " ") + if strings.HasPrefix(roleString, "<@&") && strings.HasSuffix(roleString, ">") { + roleID = roleString[3 : len(roleString)-1] + } else if _, err := strconv.Atoi(roleString); err == nil { + roleID = roleString + } else { + for _, role := range ctx.Guild.Roles { + if roleString == role.Name { + roleID = role.ID + } + } + } + } + fmt.Println(roleID) + var role *discordgo.Role + var err error + if role, err = ctx.Session.State.Role(ctx.Guild.ID, roleID); 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, 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{ + Context: ctx, + Message: "Error Updating Database", + Error: err, + } + return + } + _, _ = ctx.Send(fmt.Sprintf("%s is now self assignable", role.Name)) +} + +func removeSelfAssignableRole(ctx disgoman.Context, args []string) { + var roleString string + var roleID string + if len(args) > 0 { + roleString = strings.Join(args, " ") + if strings.HasPrefix(roleString, "<@&") && strings.HasSuffix(roleString, ">") { + roleID = roleString[3 : len(roleString)-1] + } else if _, err := strconv.Atoi(roleString); err == nil { + roleID = roleString + } else { + for _, role := range ctx.Guild.Roles { + if roleString == role.Name { + roleID = role.ID + } + } + } + } + fmt.Println(roleID) + var err error + var role *discordgo.Role + if role, err = ctx.Session.State.Role(ctx.Guild.ID, roleID); 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, 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{ + Context: ctx, + Message: "Error Updating Database", + Error: err, + } + return + } + _, _ = ctx.Send(fmt.Sprintf("%s's self assignability has been removed.", role.Name)) +} + +func selfAssignRole(ctx disgoman.Context, args []string) { + var roleString string + var roleID string + if len(args) > 0 { + roleString = strings.Join(args, " ") + if strings.HasPrefix(roleString, "<@&") && strings.HasSuffix(roleString, ">") { + roleID = roleString[3 : len(roleString)-1] + } else if _, err := strconv.Atoi(roleString); err == nil { + roleID = roleString + } else { + for _, role := range ctx.Guild.Roles { + if roleString == role.Name { + roleID = role.ID + } + } + } + } + fmt.Println(roleID) + var role *discordgo.Role + var err error + if role, err = ctx.Session.State.Role(ctx.Guild.ID, roleID); err != nil { + ctx.ErrorChannel <- disgoman.CommandError{ + Context: ctx, + Message: "Can't find that Role.", + Error: err, + } + return + } + if memberHasRole(ctx.Member, role.ID) { + _, _ = ctx.Send(fmt.Sprintf("You already have the %s role silly...", role.Name)) + return + } + var selfAssignable bool + err = utils.Database.QueryRow("SELECT self_assignable FROM roles where id=$1", role.ID).Scan(&selfAssignable) + if err != nil { + ctx.ErrorChannel <- disgoman.CommandError{ + Context: ctx, + Message: "Error Updating Database", + Error: err, + } + return + } + if !selfAssignable { + _, _ = ctx.Send(fmt.Sprintf("You aren't allowed to assign yourself the %s role", role.Name)) + return + } + err = ctx.Session.GuildMemberRoleAdd(ctx.Guild.ID, ctx.User.ID, role.ID) + if err != nil { + ctx.ErrorChannel <- disgoman.CommandError{ + Context: ctx, + Message: "There was a problem adding that role to you.", + Error: err, + } + return + } + _, _ = 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 roleString string + var roleID string + if len(args) > 0 { + roleString = strings.Join(args, " ") + if strings.HasPrefix(roleString, "<@&") && strings.HasSuffix(roleString, ">") { + roleID = roleString[3 : len(roleString)-1] + } else if _, err := strconv.Atoi(roleString); err == nil { + roleID = roleString + } else { + for _, role := range ctx.Guild.Roles { + if roleString == role.Name { + roleID = role.ID + } + } + } + } + fmt.Println(roleID) + var role *discordgo.Role + var err error + if role, err = ctx.Session.State.Role(ctx.Guild.ID, roleID); err != nil { + ctx.ErrorChannel <- disgoman.CommandError{ + Context: ctx, + Message: "Can't find that Role.", + Error: err, + } + return + } + if !memberHasRole(ctx.Member, role.ID) { + _, _ = ctx.Send(fmt.Sprintf("I can't remove the %s role from you because you don't have it...", role.Name)) + return + } + var selfAssignable bool + err = utils.Database.QueryRow("SELECT self_assignable FROM roles where id=$1", role.ID).Scan(&selfAssignable) + if err != nil { + ctx.ErrorChannel <- disgoman.CommandError{ + Context: ctx, + Message: "Error Updating Database", + Error: err, + } + return + } + if !selfAssignable { + _, _ = ctx.Send(fmt.Sprintf("You aren't allowed to remove the %s role from yourself", role.Name)) + return + } + err = ctx.Session.GuildMemberRoleRemove(ctx.Guild.ID, ctx.User.ID, role.ID) + if err != nil { + ctx.ErrorChannel <- disgoman.CommandError{ + Context: ctx, + Message: "There was a problem removing that role from you.", + Error: err, + } + return + } + _, _ = ctx.Send(fmt.Sprintf("Sad to see you go... but the %s role has been removed.", role.Name)) +} + +func memberHasRole(m *discordgo.Member, id string) bool { + for _, r := range m.Roles { + if r == id { + return true + } + } + return false +} diff --git a/go.mod b/go.mod index 8368764..6d9586c 100644 --- a/go.mod +++ b/go.mod @@ -3,7 +3,7 @@ module github.com/dustinpianalto/goff go 1.14 require ( - github.com/bwmarrin/discordgo v0.20.3-0.20200525154655-ca64123b05de + github.com/bwmarrin/discordgo v0.22.0 github.com/dustinpianalto/disgoman v0.0.12 github.com/dustinpianalto/rpnparse v1.0.1 github.com/emersion/go-imap v1.0.5 diff --git a/go.sum b/go.sum index 1b163dc..cf05f55 100644 --- a/go.sum +++ b/go.sum @@ -4,11 +4,17 @@ github.com/bwmarrin/discordgo v0.20.2 h1:nA7jiTtqUA9lT93WL2jPjUp8ZTEInRujBdx1C9g github.com/bwmarrin/discordgo v0.20.2/go.mod h1:O9S4p+ofTFwB02em7jkpkV8M3R0/PUVOwN61zSZ0r4Q= github.com/bwmarrin/discordgo v0.20.3-0.20200525154655-ca64123b05de h1:0TOVVwGrmv0PA+/vuekQIRY9jJ9rcHYnicIaph3/4S4= github.com/bwmarrin/discordgo v0.20.3-0.20200525154655-ca64123b05de/go.mod h1:c1WtWUGN6nREDmzIpyTp/iD3VYt4Fpx+bVyfBG7JE+M= +github.com/bwmarrin/discordgo v0.22.0 h1:uBxY1HmlVCsW1IuaPjpCGT6A2DBwRn0nvOguQIxDdFM= +github.com/bwmarrin/discordgo v0.22.0/go.mod h1:c1WtWUGN6nREDmzIpyTp/iD3VYt4Fpx+bVyfBG7JE+M= github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c= github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/dustinpianalto/disgoman v0.0.10 h1:UzmvMpOi4peF59tXGaNfVU+ePHs1hILa6gQbjxjWQ9g= 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/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= github.com/emersion/go-imap v1.0.5/go.mod h1:yKASt+C3ZiDAiCSssxg9caIckWF/JG7ZQTO7GAmvicU= github.com/emersion/go-message v0.11.1/go.mod h1:C4jnca5HOTo4bGN9YdqNQM9sITuT3Y0K6bSUw9RklvY= diff --git a/goff.go b/goff.go index 08a4124..fc2a384 100644 --- a/goff.go +++ b/goff.go @@ -34,6 +34,11 @@ func main() { 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() diff --git a/utils/postfixes.go b/utils/postfixes.go index 659bd03..0e324af 100644 --- a/utils/postfixes.go +++ b/utils/postfixes.go @@ -20,6 +20,14 @@ var postfixes = []postfix{ Name: "1_Update_Tags_Content_Length", Invoke: updateTagsContentLength, }, + postfix{ + Name: "2_Add_Table_Roles", + Invoke: addTableRoles, + }, + postfix{ + Name: "3_Update_Guild_Add_Puzzle_Role", + Invoke: updateGuildsAddPuzzleRole, + }, } func RunPostfixes() { @@ -96,3 +104,41 @@ func updateTagsContentLength(revert bool) error { } return nil } + +func addTableRoles(revert bool) error { + var queryString string + if !revert { + queryString = `CREATE TABLE roles( + id varchar(30) primary key, + guild_id varchar(30) not null references guilds(id), + self_assignable bool not null default false, + admin bool not null default false, + moderator bool not null default false + )` + } else { + queryString = `DROP TABLE roles` + } + _, err := Database.Exec(queryString) + if err != nil { + log.Println(err) + return err + } + return nil +} + +func updateGuildsAddPuzzleRole(revert bool) error { + var queryString string + if !revert { + queryString = `ALTER TABLE guilds + ADD COLUMN puzzle_role varchar(30) references roles(id)` + } else { + queryString = `ALTER TABLE guilds + DROP COLUMN puzzle_role` + } + _, err := Database.Exec(queryString) + if err != nil { + log.Println(err) + return err + } + return nil +} diff --git a/utils/puzzles.go b/utils/puzzles.go index 6edd5ad..e27ba5b 100644 --- a/utils/puzzles.go +++ b/utils/puzzles.go @@ -49,14 +49,14 @@ func ProcessPuzzleEmail(mr *mail.Reader, dg *discordgo.Session) { }, } var guilds []Guild - queryString := `SELECT id, puzzle_channel from guilds` + queryString := `SELECT id, puzzle_channel, puzzle_role from guilds` rows, err := Database.Query(queryString) if err != nil { log.Println(err) } for rows.Next() { var guild Guild - err := rows.Scan(&guild.ID, &guild.PuzzleChannel) + err := rows.Scan(&guild.ID, &guild.PuzzleChannel, &guild.PuzzleRole) if err != nil { log.Println(err) continue @@ -74,8 +74,17 @@ func ProcessPuzzleEmail(mr *mail.Reader, dg *discordgo.Session) { if g.PuzzleChannel == "" { continue } - msg := discordgo.MessageSend{ - Embed: &e, + var msg discordgo.MessageSend + role, err := dg.State.Role(g.ID, g.PuzzleRole.String) + if err != nil { + msg = discordgo.MessageSend{ + Embed: &e, + } + } else { + msg = discordgo.MessageSend{ + Content: role.Mention(), + Embed: &e, + } } m, err := dg.ChannelMessageSendComplex(g.PuzzleChannel, &msg) if err != nil { diff --git a/utils/types.go b/utils/types.go index 0fd92b9..06bc2d1 100644 --- a/utils/types.go +++ b/utils/types.go @@ -1,5 +1,7 @@ package utils +import "database/sql" + type Guild struct { ID string WelcomeMessage string @@ -7,4 +9,5 @@ type Guild struct { LoggingChannel string WelcomeChannel string PuzzleChannel string + PuzzleRole sql.NullString }