diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml new file mode 100644 index 0000000..ae00691 --- /dev/null +++ b/.github/workflows/build.yml @@ -0,0 +1,25 @@ +name: Build + +on: + push: + branches: [ master ] + pull_request: + branches: [ master ] + +jobs: + build: + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v2 + - name: Set up JDK + uses: actions/setup-java@v1 + with: + java-version: 11 + - name: Cache local Maven repository + uses: actions/cache@v2 + with: + path: ~/.m2/repository + key: maven-${{ hashFiles('build.gradle') }} + restore-keys: maven- + - name: Build + run: ./gradlew build \ No newline at end of file diff --git a/.github/workflows/docker.yml b/.github/workflows/docker.yml new file mode 100644 index 0000000..2c9e1bc --- /dev/null +++ b/.github/workflows/docker.yml @@ -0,0 +1,32 @@ +name: Docker + +on: + push: + branches: [ master ] + +jobs: + docker: + runs-on: ubuntu-latest + steps: + - + name: Checkout code + uses: actions/checkout@v2 + - + name: Set up Docker Buildx + uses: docker/setup-buildx-action@v1 + + - + name: Login to DockerHub + uses: docker/login-action@v1 + with: + username: ${{ secrets.DOCKERHUB_USERNAME }} + password: ${{ secrets.DOCKERHUB_ACCESS_TOKEN }} + + - + name: Build and push + uses: docker/build-push-action@v2 + with: + context: . + pull: true + push: true + tags: theprogrammershangout/judgebot:latest \ No newline at end of file diff --git a/src/main/kotlin/me/ddivad/judgebot/Main.kt b/src/main/kotlin/me/ddivad/judgebot/Main.kt index 55cd588..1412f52 100644 --- a/src/main/kotlin/me/ddivad/judgebot/Main.kt +++ b/src/main/kotlin/me/ddivad/judgebot/Main.kt @@ -6,6 +6,7 @@ import dev.kord.gateway.Intent import dev.kord.gateway.Intents import dev.kord.gateway.PrivilegedIntent import me.ddivad.judgebot.dataclasses.Configuration +import me.ddivad.judgebot.dataclasses.Permissions import me.ddivad.judgebot.services.* import me.ddivad.judgebot.services.infractions.BanService import me.ddivad.judgebot.services.infractions.MuteService @@ -31,6 +32,14 @@ suspend fun main(args: Array) { commandReaction = null theme = Color.MAGENTA entitySupplyStrategy = EntitySupplyStrategy.cacheWithCachingRestFallback + permissions(Permissions.NONE) + intents = Intents( + Intent.GuildMembers, + Intent.DirectMessages, + Intent.GuildBans, + Intent.Guilds, + Intent.GuildMessageReactions + ) } mentionEmbed { @@ -58,7 +67,7 @@ suspend fun main(args: Array) { field { name = "Build Info" value = "```" + - "Version: 2.3.0\n" + + "Version: 2.4.0\n" + "DiscordKt: ${versions.library}\n" + "Kotlin: $kotlinVersion" + "```" @@ -73,15 +82,6 @@ suspend fun main(args: Array) { } } - permissions { - val permissionsService = discord.getInjectionObjects(PermissionsService::class) - val permission = command.requiredPermissionLevel - if (guild != null) - permissionsService.hasClearance(guild!!, user, permission) - else - return@permissions command.requiredPermissionLevel == PermissionLevel.Everyone - } - onStart { val (muteService, banService, cacheService) = this.getInjectionObjects( MuteService::class, diff --git a/src/main/kotlin/me/ddivad/judgebot/arguments/LowerMemberArg.kt b/src/main/kotlin/me/ddivad/judgebot/arguments/LowerMemberArg.kt index 1d8d25f..da78ede 100644 --- a/src/main/kotlin/me/ddivad/judgebot/arguments/LowerMemberArg.kt +++ b/src/main/kotlin/me/ddivad/judgebot/arguments/LowerMemberArg.kt @@ -1,7 +1,6 @@ package me.ddivad.judgebot.arguments import dev.kord.core.entity.Member -import me.ddivad.judgebot.services.PermissionsService import me.jakejmattson.discordkt.api.arguments.* import me.jakejmattson.discordkt.api.dsl.CommandEvent import me.jakejmattson.discordkt.api.extensions.toSnowflakeOrNull @@ -14,20 +13,17 @@ open class LowerMemberArg(override val name: String = "LowerMemberArg") : Argume override suspend fun generateExamples(event: CommandEvent<*>) = mutableListOf("@User", "197780697866305536", "302134543639511050") override suspend fun convert(arg: String, args: List, event: CommandEvent<*>): ArgumentResult { - val permissionsService = event.discord.getInjectionObjects(PermissionsService::class) val guild = event.guild ?: return Error("No guild found") val member = arg.toSnowflakeOrNull()?.let { guild.getMemberOrNull(it) } ?: return Error("Not found") + val author = event.author.asMember(event.guild!!.id) return when { - event.author.asMember(event.guild!!.id).isHigherRankedThan(permissionsService, member) -> + event.discord.permissions.isHigherLevel(event.discord, member, author) -> Error("You don't have the permission to use this command on the target user.") else -> Success(member) } } override fun formatData(data: Member) = "@${data.tag}" -} - -suspend fun Member.isHigherRankedThan(permissions: PermissionsService, targetMember: Member) = - permissions.getPermissionRank(this) < permissions.getPermissionRank(targetMember) +} \ No newline at end of file diff --git a/src/main/kotlin/me/ddivad/judgebot/arguments/LowerUserArg.kt b/src/main/kotlin/me/ddivad/judgebot/arguments/LowerUserArg.kt index 2aa592c..3349fb1 100644 --- a/src/main/kotlin/me/ddivad/judgebot/arguments/LowerUserArg.kt +++ b/src/main/kotlin/me/ddivad/judgebot/arguments/LowerUserArg.kt @@ -1,7 +1,6 @@ package me.ddivad.judgebot.arguments import dev.kord.core.entity.User -import me.ddivad.judgebot.services.PermissionsService import me.jakejmattson.discordkt.api.arguments.* import me.jakejmattson.discordkt.api.dsl.CommandEvent import me.jakejmattson.discordkt.api.extensions.toSnowflakeOrNull @@ -14,15 +13,15 @@ open class LowerUserArg(override val name: String = "LowerUserArg") : ArgumentTy override suspend fun generateExamples(event: CommandEvent<*>) = mutableListOf("@User", "197780697866305536", "302134543639511050") override suspend fun convert(arg: String, args: List, event: CommandEvent<*>): ArgumentResult { - val permissionsService = event.discord.getInjectionObjects(PermissionsService::class) val guild = event.guild ?: return Error("No guild found") val user = arg.toSnowflakeOrNull()?.let { guild.kord.getUser(it) } ?: return Error("User Not Found") val member = guild.getMemberOrNull(user.id) ?: return Success(user) + val author = event.author.asMember(event.guild!!.id) return when { - event.author.asMember(event.guild!!.id).isHigherRankedThan(permissionsService, member) -> - Error("You don't have the permission to use this command on the target user") + event.discord.permissions.isHigherLevel(event.discord, member, author) -> + Error("You don't have the permission to use this command on the target user.") else -> Success(member.asUser()) } } diff --git a/src/main/kotlin/me/ddivad/judgebot/commands/GuildCommands.kt b/src/main/kotlin/me/ddivad/judgebot/commands/GuildCommands.kt index 8c4ac79..f7e1ba2 100644 --- a/src/main/kotlin/me/ddivad/judgebot/commands/GuildCommands.kt +++ b/src/main/kotlin/me/ddivad/judgebot/commands/GuildCommands.kt @@ -4,11 +4,10 @@ import me.ddivad.judgebot.arguments.GuildConfigArg import me.ddivad.judgebot.conversations.guild.GuildSetupConversation import me.ddivad.judgebot.conversations.guild.EditConfigConversation import me.ddivad.judgebot.dataclasses.Configuration +import me.ddivad.judgebot.dataclasses.Permissions import me.ddivad.judgebot.embeds.createActivePunishmentsEmbed import me.ddivad.judgebot.services.DatabaseService import me.ddivad.judgebot.services.infractions.MuteService -import me.ddivad.judgebot.services.PermissionLevel -import me.ddivad.judgebot.services.requiredPermissionLevel import me.jakejmattson.discordkt.api.dsl.commands fun guildConfigCommands(configuration: Configuration, @@ -16,7 +15,7 @@ fun guildConfigCommands(configuration: Configuration, muteService: MuteService) = commands("Guild") { guildCommand("setup") { description = "Configure a guild to use Judgebot." - requiredPermissionLevel = PermissionLevel.Administrator + requiredPermission = Permissions.ADMINISTRATOR execute { if (configuration.hasGuildConfig(guild.id.value)) { respond("Guild configuration exists. To modify it use the commands to set values.") @@ -32,7 +31,7 @@ fun guildConfigCommands(configuration: Configuration, guildCommand("configuration") { description = "Update configuration parameters for this guild (conversation)." - requiredPermissionLevel = PermissionLevel.Staff + requiredPermission = Permissions.STAFF execute(GuildConfigArg.optional("options")) { if (!configuration.hasGuildConfig(guild.id.value)) { respond("Please run the **setup** command to set this initially.") @@ -46,7 +45,7 @@ fun guildConfigCommands(configuration: Configuration, guildCommand("activePunishments") { description = "View active punishments for a guild." - requiredPermissionLevel = PermissionLevel.Staff + requiredPermission = Permissions.STAFF execute { val punishments = databaseService.guilds.getActivePunishments(guild) if (punishments.isEmpty()) { diff --git a/src/main/kotlin/me/ddivad/judgebot/commands/InfoCommands.kt b/src/main/kotlin/me/ddivad/judgebot/commands/InfoCommands.kt index a7f6db9..10a59e0 100644 --- a/src/main/kotlin/me/ddivad/judgebot/commands/InfoCommands.kt +++ b/src/main/kotlin/me/ddivad/judgebot/commands/InfoCommands.kt @@ -5,16 +5,12 @@ import dev.kord.x.emoji.Emojis import dev.kord.x.emoji.addReaction import me.ddivad.judgebot.arguments.LowerMemberArg import me.ddivad.judgebot.dataclasses.Info +import me.ddivad.judgebot.dataclasses.Permissions import me.ddivad.judgebot.embeds.createInformationEmbed import me.ddivad.judgebot.extensions.testDmStatus import me.ddivad.judgebot.services.DatabaseService -import me.ddivad.judgebot.services.HelpService -import me.ddivad.judgebot.services.PermissionLevel -import me.ddivad.judgebot.services.requiredPermissionLevel -import me.jakejmattson.discordkt.api.arguments.CommandArg import me.jakejmattson.discordkt.api.arguments.EveryArg import me.jakejmattson.discordkt.api.arguments.IntegerArg -import me.jakejmattson.discordkt.api.arguments.UnicodeEmojiArg import me.jakejmattson.discordkt.api.dsl.commands import me.jakejmattson.discordkt.api.extensions.sendPrivateMessage @@ -22,7 +18,7 @@ import me.jakejmattson.discordkt.api.extensions.sendPrivateMessage fun createInformationCommands(databaseService: DatabaseService) = commands("Information") { guildCommand("info") { description = "Send an information message to a guild member" - requiredPermissionLevel = PermissionLevel.Moderator + requiredPermission = Permissions.MODERATOR execute(LowerMemberArg, EveryArg("Info Content")) { val (target, content) = args try { @@ -45,7 +41,7 @@ fun createInformationCommands(databaseService: DatabaseService) = commands("Info guildCommand("removeInfo") { description = "Remove an information message from a member record." - requiredPermissionLevel = PermissionLevel.Staff + requiredPermission = Permissions.STAFF execute(LowerMemberArg, IntegerArg("Info ID")) { val (target, id) = args val user = databaseService.users.getOrCreateUser(target, guild) diff --git a/src/main/kotlin/me/ddivad/judgebot/commands/InfractionCommands.kt b/src/main/kotlin/me/ddivad/judgebot/commands/InfractionCommands.kt index a146fef..595bdbd 100644 --- a/src/main/kotlin/me/ddivad/judgebot/commands/InfractionCommands.kt +++ b/src/main/kotlin/me/ddivad/judgebot/commands/InfractionCommands.kt @@ -1,8 +1,8 @@ package me.ddivad.judgebot.commands -import dev.kord.common.entity.Permission import me.ddivad.judgebot.arguments.LowerUserArg import dev.kord.common.exception.RequestException +import dev.kord.core.behavior.reply import dev.kord.x.emoji.Emojis import dev.kord.x.emoji.addReaction import me.ddivad.judgebot.arguments.LowerMemberArg @@ -10,16 +10,16 @@ import me.ddivad.judgebot.conversations.InfractionConversation import me.ddivad.judgebot.dataclasses.Configuration import me.ddivad.judgebot.dataclasses.Infraction import me.ddivad.judgebot.dataclasses.InfractionType +import me.ddivad.judgebot.dataclasses.Permissions import me.ddivad.judgebot.extensions.testDmStatus import me.ddivad.judgebot.services.* -import me.ddivad.judgebot.extensions.* import me.ddivad.judgebot.services.infractions.BadPfpService import me.ddivad.judgebot.services.infractions.BadnameService import me.ddivad.judgebot.services.infractions.InfractionService import me.jakejmattson.discordkt.api.arguments.BooleanArg import me.jakejmattson.discordkt.api.arguments.EveryArg import me.jakejmattson.discordkt.api.arguments.IntegerArg -import me.jakejmattson.discordkt.api.arguments.UserArg +import me.jakejmattson.discordkt.api.conversations.ConversationResult import me.jakejmattson.discordkt.api.dsl.commands @Suppress("unused") @@ -30,7 +30,7 @@ fun createInfractionCommands(databaseService: DatabaseService, badnameService: BadnameService) = commands("Infraction") { guildCommand("strike", "s", "S") { description = "Strike a user." - requiredPermissionLevel = PermissionLevel.Staff + requiredPermission = Permissions.STAFF execute(LowerMemberArg, IntegerArg("Weight").optional(1), EveryArg("Reason")) { val (targetMember, weight, reason) = args val guildConfiguration = config[guild.id.value] ?: return@execute @@ -46,15 +46,20 @@ fun createInfractionCommands(databaseService: DatabaseService, this.message.addReaction(Emojis.x) respond("${targetMember.mention} has DMs disabled. No messages will be sent.") } - InfractionConversation(databaseService, config, infractionService) + val conversationResult = InfractionConversation(databaseService, config, infractionService) .createInfractionConversation(guild, targetMember, weight, reason, InfractionType.Strike) .startPublicly(discord, author, channel) + if (conversationResult == ConversationResult.HAS_CONVERSATION) { + message.reply { content = "You already have an active Strike conversation. Make sure you selected a rule." } + } else if (conversationResult == ConversationResult.EXITED) { + message.reply { content = "Infraction cancelled." } + } } } guildCommand("warn", "w", "W") { description = "Warn a user." - requiredPermissionLevel = PermissionLevel.Moderator + requiredPermission = Permissions.MODERATOR execute(LowerMemberArg, EveryArg("Reason")) { val (targetMember, reason) = args try { @@ -72,7 +77,7 @@ fun createInfractionCommands(databaseService: DatabaseService, guildCommand("badpfp") { description = "Notifies the user that they should change their profile pic and applies a 30 minute mute. Bans the user if they don't change picture." - requiredPermissionLevel = PermissionLevel.Staff + requiredPermission = Permissions.STAFF execute(BooleanArg("cancel", "apply", "cancel").optional(true), LowerMemberArg) { val (cancel, targetMember) = args try { @@ -103,7 +108,7 @@ fun createInfractionCommands(databaseService: DatabaseService, guildCommand("badname") { description = "Rename a guild member that has a bad name." - requiredPermissionLevel = PermissionLevel.Moderator + requiredPermission = Permissions.MODERATOR execute(LowerMemberArg) { badnameService.chooseRandomNickname(args.first) respond("User renamed to ${args.first.mention}") @@ -112,7 +117,7 @@ fun createInfractionCommands(databaseService: DatabaseService, guildCommand("cleanseInfractions") { description = "Use this to delete (permanently) as user's infractions." - requiredPermissionLevel = PermissionLevel.Administrator + requiredPermission = Permissions.ADMINISTRATOR execute(LowerUserArg) { val user = databaseService.users.getOrCreateUser(args.first, guild) if (user.getGuildInfo(guild.id.asString).infractions.isEmpty()) { @@ -126,7 +131,7 @@ fun createInfractionCommands(databaseService: DatabaseService, guildCommand("removeInfraction") { description = "Use this to delete (permanently) an infraction from a user." - requiredPermissionLevel = PermissionLevel.Administrator + requiredPermission = Permissions.ADMINISTRATOR execute(LowerUserArg, IntegerArg("Infraction ID")) { val user = databaseService.users.getOrCreateUser(args.first, guild) if (user.getGuildInfo(guild.id.asString).infractions.isEmpty()) { diff --git a/src/main/kotlin/me/ddivad/judgebot/commands/MuteCommands.kt b/src/main/kotlin/me/ddivad/judgebot/commands/MuteCommands.kt index 49de484..e8000fe 100644 --- a/src/main/kotlin/me/ddivad/judgebot/commands/MuteCommands.kt +++ b/src/main/kotlin/me/ddivad/judgebot/commands/MuteCommands.kt @@ -4,8 +4,8 @@ import dev.kord.common.exception.RequestException import dev.kord.x.emoji.Emojis import dev.kord.x.emoji.addReaction import me.ddivad.judgebot.arguments.LowerMemberArg +import me.ddivad.judgebot.dataclasses.Permissions import me.ddivad.judgebot.extensions.testDmStatus -import me.ddivad.judgebot.services.* import me.ddivad.judgebot.services.infractions.MuteService import me.ddivad.judgebot.services.infractions.RoleState import me.ddivad.judgebot.util.timeToString @@ -18,7 +18,7 @@ import kotlin.math.roundToLong fun createMuteCommands(muteService: MuteService) = commands("Mute") { guildCommand("mute") { description = "Mute a user for a specified time." - requiredPermissionLevel = PermissionLevel.Moderator + requiredPermission = Permissions.MODERATOR execute(LowerMemberArg, TimeArg("Time"), EveryArg("Reason")) { val (targetMember, length, reason) = args try { @@ -35,7 +35,7 @@ fun createMuteCommands(muteService: MuteService) = commands("Mute") { guildCommand("unmute") { description = "Unmute a user." - requiredPermissionLevel = PermissionLevel.Moderator + requiredPermission = Permissions.MODERATOR execute(LowerMemberArg) { val targetMember = args.first if (muteService.checkRoleState(guild, targetMember) == RoleState.None) { @@ -50,7 +50,7 @@ fun createMuteCommands(muteService: MuteService) = commands("Mute") { guildCommand("gag") { description = "Mute a user for 5 minutes while you deal with something" - requiredPermissionLevel = PermissionLevel.Moderator + requiredPermission = Permissions.MODERATOR execute(LowerMemberArg) { val targetMember = args.first if (muteService.checkRoleState(guild, targetMember) == RoleState.Tracked) { diff --git a/src/main/kotlin/me/ddivad/judgebot/commands/NoteCommands.kt b/src/main/kotlin/me/ddivad/judgebot/commands/NoteCommands.kt index fa69b0a..db1822e 100644 --- a/src/main/kotlin/me/ddivad/judgebot/commands/NoteCommands.kt +++ b/src/main/kotlin/me/ddivad/judgebot/commands/NoteCommands.kt @@ -1,9 +1,8 @@ package me.ddivad.judgebot.commands import me.ddivad.judgebot.arguments.LowerMemberArg +import me.ddivad.judgebot.dataclasses.Permissions import me.ddivad.judgebot.services.DatabaseService -import me.ddivad.judgebot.services.PermissionLevel -import me.ddivad.judgebot.services.requiredPermissionLevel import me.jakejmattson.discordkt.api.arguments.EveryArg import me.jakejmattson.discordkt.api.arguments.IntegerArg import me.jakejmattson.discordkt.api.arguments.UserArg @@ -13,7 +12,7 @@ import me.jakejmattson.discordkt.api.dsl.commands fun noteCommands(databaseService: DatabaseService) = commands("Note") { guildCommand("note") { description = "Use this to add a note to a user." - requiredPermissionLevel = PermissionLevel.Moderator + requiredPermission = Permissions.MODERATOR execute(UserArg, EveryArg("Note Content")) { val (target, note) = args val user = databaseService.users.getOrCreateUser(target, guild) @@ -24,7 +23,7 @@ fun noteCommands(databaseService: DatabaseService) = commands("Note") { guildCommand("editNote") { description = "Use this to edit a note." - requiredPermissionLevel = PermissionLevel.Moderator + requiredPermission = Permissions.MODERATOR execute(UserArg, IntegerArg("Note to edit"), EveryArg("Note Content")) { val (target, noteId, note) = args val user = databaseService.users.getOrCreateUser(target, guild) @@ -39,7 +38,7 @@ fun noteCommands(databaseService: DatabaseService) = commands("Note") { guildCommand("deleteNote") { description = "Use this to add a delete a note from a user." - requiredPermissionLevel = PermissionLevel.Staff + requiredPermission = Permissions.STAFF execute(LowerMemberArg, IntegerArg("Note ID")) { val (target, noteId) = args val user = databaseService.users.getOrCreateUser(target, guild) @@ -54,7 +53,7 @@ fun noteCommands(databaseService: DatabaseService) = commands("Note") { guildCommand("cleanseNotes") { description = "Use this to delete (permanently) as user's notes." - requiredPermissionLevel = PermissionLevel.Administrator + requiredPermission = Permissions.ADMINISTRATOR execute(LowerMemberArg) { val target = args.first val user = databaseService.users.getOrCreateUser(target, guild) diff --git a/src/main/kotlin/me/ddivad/judgebot/commands/RuleCommands.kt b/src/main/kotlin/me/ddivad/judgebot/commands/RuleCommands.kt index 839be9d..b97f9cd 100644 --- a/src/main/kotlin/me/ddivad/judgebot/commands/RuleCommands.kt +++ b/src/main/kotlin/me/ddivad/judgebot/commands/RuleCommands.kt @@ -6,12 +6,11 @@ import me.ddivad.judgebot.conversations.rules.AddRuleConversation import me.ddivad.judgebot.conversations.rules.ArchiveRuleConversation import me.ddivad.judgebot.conversations.rules.EditRuleConversation import me.ddivad.judgebot.dataclasses.Configuration +import me.ddivad.judgebot.dataclasses.Permissions import me.ddivad.judgebot.embeds.createRuleEmbed import me.ddivad.judgebot.embeds.createRulesEmbed import me.ddivad.judgebot.embeds.createRulesEmbedDetailed import me.ddivad.judgebot.services.DatabaseService -import me.ddivad.judgebot.services.PermissionLevel -import me.ddivad.judgebot.services.requiredPermissionLevel import me.jakejmattson.discordkt.api.arguments.MessageArg import me.jakejmattson.discordkt.api.dsl.commands import me.jakejmattson.discordkt.api.extensions.jumpLink @@ -22,7 +21,7 @@ fun ruleCommands(configuration: Configuration, guildCommand("addRule") { description = "Add a rule to this guild." - requiredPermissionLevel = PermissionLevel.Administrator + requiredPermission = Permissions.ADMINISTRATOR execute { AddRuleConversation(databaseService) .createAddRuleConversation(guild) @@ -32,7 +31,7 @@ fun ruleCommands(configuration: Configuration, guildCommand("editRule") { description = "Edit a rule in this guild." - requiredPermissionLevel = PermissionLevel.Administrator + requiredPermission = Permissions.ADMINISTRATOR execute { EditRuleConversation(databaseService) .createAddRuleConversation(guild) @@ -42,7 +41,7 @@ fun ruleCommands(configuration: Configuration, guildCommand("archiveRule") { description = "Archive a rule in this guild." - requiredPermissionLevel = PermissionLevel.Administrator + requiredPermission = Permissions.ADMINISTRATOR execute { ArchiveRuleConversation(databaseService) .createArchiveRuleConversation(guild) @@ -52,7 +51,7 @@ fun ruleCommands(configuration: Configuration, guildCommand("rules") { description = "List the rules of this guild. Pass a message ID to edit existing rules embed." - requiredPermissionLevel = PermissionLevel.Everyone + requiredPermission = Permissions.NONE execute(MessageArg.optionalNullable(null)) { val messageToEdit = args.first if (messageToEdit != null) { @@ -68,7 +67,7 @@ fun ruleCommands(configuration: Configuration, guildCommand("longRules") { description = "List the rules (with descriptions) of this guild. Pass a message ID to edit existing rules embed." - requiredPermissionLevel = PermissionLevel.Staff + requiredPermission = Permissions.STAFF execute(MessageArg.optionalNullable(null)) { val messageToEdit = args.first if (messageToEdit != null) { @@ -84,7 +83,7 @@ fun ruleCommands(configuration: Configuration, guildCommand("rule") { description = "List a rule from this guild." - requiredPermissionLevel = PermissionLevel.Everyone + requiredPermission = Permissions.NONE execute(RuleArg) { val rule = databaseService.guilds.getRule(guild, args.first.number)!! respond { diff --git a/src/main/kotlin/me/ddivad/judgebot/commands/UserCommands.kt b/src/main/kotlin/me/ddivad/judgebot/commands/UserCommands.kt index 5de7108..1d45566 100644 --- a/src/main/kotlin/me/ddivad/judgebot/commands/UserCommands.kt +++ b/src/main/kotlin/me/ddivad/judgebot/commands/UserCommands.kt @@ -5,24 +5,19 @@ import dev.kord.x.emoji.Emojis import dev.kord.x.emoji.addReaction import kotlinx.coroutines.flow.toList import me.ddivad.judgebot.arguments.LowerUserArg -import me.ddivad.judgebot.conversations.InfractionConversation import me.ddivad.judgebot.conversations.ResetUserConversation import me.ddivad.judgebot.conversations.guildChoiceConversation import me.ddivad.judgebot.dataclasses.* import me.ddivad.judgebot.embeds.createHistoryEmbed -import me.ddivad.judgebot.embeds.createCondensedHistoryEmbed import me.ddivad.judgebot.embeds.createLinkedAccountMenu import me.ddivad.judgebot.embeds.createSelfHistoryEmbed import me.ddivad.judgebot.services.DatabaseService import me.ddivad.judgebot.services.LoggingService -import me.ddivad.judgebot.services.PermissionLevel import me.ddivad.judgebot.services.infractions.BanService -import me.ddivad.judgebot.services.requiredPermissionLevel import me.jakejmattson.discordkt.api.arguments.* import me.jakejmattson.discordkt.api.dsl.commands import me.jakejmattson.discordkt.api.extensions.mutualGuilds import me.jakejmattson.discordkt.api.extensions.sendPrivateMessage -import me.jakejmattson.discordkt.api.extensions.toSnowflake import java.awt.Color @Suppress("unused") @@ -34,7 +29,7 @@ fun createUserCommands( ) = commands("User") { guildCommand("history", "h", "H") { description = "Use this to view a user's record." - requiredPermissionLevel = PermissionLevel.Moderator + requiredPermission = Permissions.MODERATOR execute(UserArg) { val user = databaseService.users.getOrCreateUser(args.first, guild) databaseService.users.incrementUserHistory(user, guild) @@ -47,7 +42,7 @@ fun createUserCommands( guildCommand("alts") { description = "Use this to view a user's alt accounts." - requiredPermissionLevel = PermissionLevel.Moderator + requiredPermission = Permissions.MODERATOR execute(UserArg) { val target = args.first val user = databaseService.users.getOrCreateUser(args.first, guild) @@ -67,7 +62,7 @@ fun createUserCommands( guildCommand("whatpfp") { description = "Perform a reverse image search of a User's profile picture" - requiredPermissionLevel = PermissionLevel.Moderator + requiredPermission = Permissions.MODERATOR execute(UserArg) { val user = args.first val reverseSearchUrl = "" @@ -82,7 +77,7 @@ fun createUserCommands( guildCommand("ban") { description = "Ban a member from this guild." - requiredPermissionLevel = PermissionLevel.Staff + requiredPermission = Permissions.STAFF execute(LowerUserArg, IntegerArg("Delete message days").optional(0), EveryArg) { val (target, deleteDays, reason) = args val ban = Punishment(target.id.asString, InfractionType.Ban, reason, author.id.asString) @@ -95,7 +90,7 @@ fun createUserCommands( guildCommand("unban") { description = "Unban a banned member from this guild." - requiredPermissionLevel = PermissionLevel.Staff + requiredPermission = Permissions.STAFF execute(UserArg) { val user = args.first guild.getBanOrNull(user.id)?.let { @@ -109,7 +104,7 @@ fun createUserCommands( guildCommand("setBanReason") { description = "Set a ban reason for a banned user" - requiredPermissionLevel = PermissionLevel.Staff + requiredPermission = Permissions.STAFF execute(UserArg, EveryArg("Reason")) { val (user, reason) = args val ban = Ban(user.id.asString, author.id.asString, reason) @@ -127,7 +122,7 @@ fun createUserCommands( guildCommand("getBanReason") { description = "Get a ban reason for a banned user" - requiredPermissionLevel = PermissionLevel.Staff + requiredPermission = Permissions.STAFF execute(UserArg) { val user = args.first guild.getBanOrNull(user.id)?.let { @@ -141,7 +136,7 @@ fun createUserCommands( command("selfHistory") { description = "View your infraction history (contents will be DM'd)" - requiredPermissionLevel = PermissionLevel.Everyone + requiredPermission = Permissions.NONE execute { val user = author val mutualGuilds = author.mutualGuilds.toList().filter { config[it.id.value] != null } @@ -162,7 +157,7 @@ fun createUserCommands( guildCommand("link") { description = "Link a user's alt account with their main" - requiredPermissionLevel = PermissionLevel.Staff + requiredPermission = Permissions.STAFF execute(UserArg("Main Account"), UserArg("Alt Account")) { val (main, alt) = args val mainRecord = databaseService.users.getOrCreateUser(main, guild) @@ -175,7 +170,7 @@ fun createUserCommands( guildCommand("unlink") { description = "Link a user's alt account with their main" - requiredPermissionLevel = PermissionLevel.Staff + requiredPermission = Permissions.STAFF execute(UserArg("Main Account"), UserArg("Alt Account")) { val (main, alt) = args val mainRecord = databaseService.users.getOrCreateUser(main, guild) @@ -188,7 +183,7 @@ fun createUserCommands( guildCommand("reset") { description = "Reset a user's record, and any linked accounts" - requiredPermissionLevel = PermissionLevel.Administrator + requiredPermission = Permissions.STAFF execute(LowerUserArg) { val target = args.first ResetUserConversation(databaseService, config) diff --git a/src/main/kotlin/me/ddivad/judgebot/commands/UtilityCommands.kt b/src/main/kotlin/me/ddivad/judgebot/commands/UtilityCommands.kt index 1b837f7..b853eb4 100644 --- a/src/main/kotlin/me/ddivad/judgebot/commands/UtilityCommands.kt +++ b/src/main/kotlin/me/ddivad/judgebot/commands/UtilityCommands.kt @@ -1,5 +1,6 @@ package me.ddivad.judgebot.commands +import me.ddivad.judgebot.dataclasses.Permissions import me.ddivad.judgebot.services.HelpService import me.jakejmattson.discordkt.api.arguments.AnyArg import me.jakejmattson.discordkt.api.arguments.CommandArg @@ -9,6 +10,7 @@ import me.jakejmattson.discordkt.api.dsl.commands fun createInformationCommands(helpService: HelpService) = commands("Utility") { command("help") { description = "Display help information." + requiredPermission = Permissions.NONE execute(AnyArg("Command").optional("")) { val input = args.first if (input == "") { diff --git a/src/main/kotlin/me/ddivad/judgebot/dataclasses/GuildMember.kt b/src/main/kotlin/me/ddivad/judgebot/dataclasses/GuildMember.kt index 909cfb1..086a246 100644 --- a/src/main/kotlin/me/ddivad/judgebot/dataclasses/GuildMember.kt +++ b/src/main/kotlin/me/ddivad/judgebot/dataclasses/GuildMember.kt @@ -99,6 +99,7 @@ data class GuildMember( infraction.id = nextId this.infractions.add(infraction) this.points += infraction.points + this.pointDecayTimer = DateTime().millis.plus(infraction.punishment?.duration ?: 0) this.lastInfraction = DateTime().millis } diff --git a/src/main/kotlin/me/ddivad/judgebot/dataclasses/Permissions.kt b/src/main/kotlin/me/ddivad/judgebot/dataclasses/Permissions.kt new file mode 100644 index 0000000..ea3d346 --- /dev/null +++ b/src/main/kotlin/me/ddivad/judgebot/dataclasses/Permissions.kt @@ -0,0 +1,48 @@ +package me.ddivad.judgebot.dataclasses + +import dev.kord.common.entity.Permission +import dev.kord.core.any +import me.jakejmattson.discordkt.api.dsl.PermissionContext +import me.jakejmattson.discordkt.api.dsl.PermissionSet + +enum class Permissions : PermissionSet { + BOT_OWNER { + override suspend fun hasPermission(context: PermissionContext) = + context.discord.getInjectionObjects().ownerId == context.user.id.asString + }, + GUILD_OWNER { + override suspend fun hasPermission(context: PermissionContext) = + context.getMember()?.isOwner() ?: false + + }, + ADMINISTRATOR { + override suspend fun hasPermission(context: PermissionContext): Boolean { + val guild = context.guild ?: return false + val member = context.user.asMember(guild.id) + val configuration = context.discord.getInjectionObjects() + return member.roles.any { configuration[guild.id.value]!!.adminRoles.contains(it.id.asString) } || member.getPermissions() + .contains( + Permission.Administrator + ) + } + }, + STAFF { + override suspend fun hasPermission(context: PermissionContext): Boolean { + val guild = context.guild ?: return false + val member = context.user.asMember(guild.id) + val configuration = context.discord.getInjectionObjects() + return member.roles.any { configuration[guild.id.value]!!.staffRoles.contains(it.id.asString) } + } + }, + MODERATOR { + override suspend fun hasPermission(context: PermissionContext): Boolean { + val guild = context.guild ?: return false + val member = context.user.asMember(guild.id) + val configuration = context.discord.getInjectionObjects() + return member.roles.any { configuration[guild.id.value]!!.moderatorRoles.contains(it.id.asString) } + } + }, + NONE { + override suspend fun hasPermission(context: PermissionContext) = true + } +} \ No newline at end of file diff --git a/src/main/kotlin/me/ddivad/judgebot/listeners/JoinLeaveListener.kt b/src/main/kotlin/me/ddivad/judgebot/listeners/JoinLeaveListener.kt index eb6def2..345baee 100644 --- a/src/main/kotlin/me/ddivad/judgebot/listeners/JoinLeaveListener.kt +++ b/src/main/kotlin/me/ddivad/judgebot/listeners/JoinLeaveListener.kt @@ -1,23 +1,15 @@ package me.ddivad.judgebot.listeners -import dev.kord.core.event.guild.GuildCreateEvent import dev.kord.core.event.guild.MemberJoinEvent import dev.kord.core.event.guild.MemberLeaveEvent -import dev.kord.gateway.PrivilegedIntent -import dev.kord.gateway.RequestGuildMembers import kotlinx.coroutines.GlobalScope import kotlinx.coroutines.delay import kotlinx.coroutines.launch import me.ddivad.judgebot.services.DatabaseService import me.jakejmattson.discordkt.api.dsl.listeners -@OptIn(PrivilegedIntent::class) @Suppress("unused") fun onGuildMemberLeave(databaseService: DatabaseService) = listeners { - on { - gateway.send(RequestGuildMembers(guildId = guild.id)) - } - on { databaseService.joinLeaves.addLeaveData(guildId.asString, user.id.asString) } diff --git a/src/main/kotlin/me/ddivad/judgebot/listeners/StaffReactionListeners.kt b/src/main/kotlin/me/ddivad/judgebot/listeners/StaffReactionListeners.kt index 369ed7b..7f342c4 100644 --- a/src/main/kotlin/me/ddivad/judgebot/listeners/StaffReactionListeners.kt +++ b/src/main/kotlin/me/ddivad/judgebot/listeners/StaffReactionListeners.kt @@ -4,14 +4,12 @@ import dev.kord.common.exception.RequestException import dev.kord.core.event.message.ReactionAddEvent import dev.kord.x.emoji.Emojis import dev.kord.x.emoji.addReaction -import me.ddivad.judgebot.arguments.isHigherRankedThan import me.ddivad.judgebot.dataclasses.Configuration +import me.ddivad.judgebot.dataclasses.Permissions import me.ddivad.judgebot.embeds.createMessageDeleteEmbed import me.ddivad.judgebot.embeds.createCondensedHistoryEmbed import me.ddivad.judgebot.services.DatabaseService import me.ddivad.judgebot.services.LoggingService -import me.ddivad.judgebot.services.PermissionLevel -import me.ddivad.judgebot.services.PermissionsService import me.ddivad.judgebot.services.infractions.MuteService import me.ddivad.judgebot.services.infractions.RoleState import me.jakejmattson.discordkt.api.dsl.listeners @@ -22,7 +20,6 @@ import me.jakejmattson.discordkt.api.extensions.sendPrivateMessage fun onStaffReactionAdd( muteService: MuteService, databaseService: DatabaseService, - permissionsService: PermissionsService, loggingService: LoggingService, configuration: Configuration ) = listeners { @@ -31,13 +28,10 @@ fun onStaffReactionAdd( val guildConfiguration = configuration[guild.asGuild().id.value] if (!guildConfiguration?.reactions!!.enabled) return@on val staffMember = user.asMemberOrNull(guild.id) ?: return@on - val messageAuthor = message.asMessage().author?.asMemberOrNull(guild.id) ?: return@on val msg = message.asMessage() - - if (permissionsService.hasPermission(staffMember, PermissionLevel.Moderator) && !staffMember.isHigherRankedThan( - permissionsService, - messageAuthor - ) + val messageAuthor = msg.author?.asMemberOrNull(guild.id) ?: return@on + if (discord.permissions.hasPermission(discord, staffMember, Permissions.MODERATOR) + && discord.permissions.isHigherLevel(discord, staffMember, messageAuthor) ) { when (this.emoji.name) { guildConfiguration.reactions.gagReaction -> { diff --git a/src/main/kotlin/me/ddivad/judgebot/services/HelpService.kt b/src/main/kotlin/me/ddivad/judgebot/services/HelpService.kt index 94914f1..1b85719 100644 --- a/src/main/kotlin/me/ddivad/judgebot/services/HelpService.kt +++ b/src/main/kotlin/me/ddivad/judgebot/services/HelpService.kt @@ -7,6 +7,7 @@ import dev.kord.core.entity.User import dev.kord.x.emoji.DiscordEmoji import dev.kord.x.emoji.Emojis import kotlinx.coroutines.runBlocking +import me.jakejmattson.discordkt.api.Discord import me.jakejmattson.discordkt.api.annotations.Service import me.jakejmattson.discordkt.api.arguments.ArgumentType import me.jakejmattson.discordkt.api.arguments.OptionalArg @@ -15,23 +16,20 @@ import me.jakejmattson.discordkt.api.dsl.CommandEvent import me.jakejmattson.discordkt.api.dsl.Execution @Service -class HelpService(private val permissionsService: PermissionsService) { - private suspend fun Command.isVisible(guild: Guild, user: User) = - permissionsService.isCommandVisible(guild, user, this) - +class HelpService(private val discord: Discord) { suspend fun buildHelpEmbed(event: CommandEvent<*>) = event.respondMenu { val container = event.discord.commands fun joinNames(value: List) = value.joinToString("\n") { it.names.first() } val groupedCommands = container - .filter { it.isVisible(event.guild!!, event.author) } + .filter { it.hasPermissionToRun(event) } .groupBy { it.category } .toList() .sortedByDescending { it.second.size } val categoryNames = container - .filter { it.isVisible(event.guild!!, event.author) } + .filter { it.hasPermissionToRun(event) } .groupBy { it.category } .toList() .sortedByDescending { it.second.size } @@ -47,7 +45,7 @@ class HelpService(private val permissionsService: PermissionsService) { description = """ You have **${commands.size}** commands available based on permissions. - Use `${event.prefix()}help` for more information + Use `${event.prefix()}help ` for more information """.trimIndent() color = event.discord.configuration.theme?.kColor @@ -56,6 +54,11 @@ class HelpService(private val permissionsService: PermissionsService) { value = "```css\n${joinNames(sorted)}\n```" inline = true } + + field { + name = "Don't see what you're looking for?" + value = "Try `search `. If the command exists in a bot, it will react with ${Emojis.whiteCheckMark}" + } } } diff --git a/src/main/kotlin/me/ddivad/judgebot/services/PermissionsService.kt b/src/main/kotlin/me/ddivad/judgebot/services/PermissionsService.kt deleted file mode 100644 index b223aae..0000000 --- a/src/main/kotlin/me/ddivad/judgebot/services/PermissionsService.kt +++ /dev/null @@ -1,61 +0,0 @@ -package me.ddivad.judgebot.services - -import dev.kord.core.any -import dev.kord.core.entity.Guild -import dev.kord.core.entity.Member -import dev.kord.core.entity.User -import me.ddivad.judgebot.dataclasses.Configuration -import me.jakejmattson.discordkt.api.annotations.Service -import me.jakejmattson.discordkt.api.dsl.Command - -enum class PermissionLevel { - Everyone, - Moderator, - Staff, - Administrator, - GuildOwner, - BotOwner -} - -val DEFAULT_REQUIRED_PERMISSION = PermissionLevel.Everyone -val commandPermissions: MutableMap = mutableMapOf() - -@Service -class PermissionsService(private val configuration: Configuration) { - suspend fun hasClearance(guild: Guild?, user: User, requiredPermissionLevel: PermissionLevel): Boolean { - val permissionLevel = guild?.getMember(user.id)?.let { getPermissionLevel(it) } - return if (permissionLevel == null) { - requiredPermissionLevel == PermissionLevel.Everyone || user.id.asString == configuration.ownerId - } else { - permissionLevel >= requiredPermissionLevel - } - } - - suspend fun hasPermission(member: Member, level: PermissionLevel) = getPermissionLevel(member) >= level - suspend fun getPermissionRank(member: Member) = getPermissionLevel(member).ordinal - - private suspend fun getPermissionLevel(member: Member) = - when { - member.isBotOwner() -> PermissionLevel.BotOwner - member.isGuildOwner() -> PermissionLevel.GuildOwner - member.isAdministrator() -> PermissionLevel.Administrator - member.isStaff() -> PermissionLevel.Staff - member.isModerator() -> PermissionLevel.Moderator - else -> PermissionLevel.Everyone - } - - private fun Member.isBotOwner() = id.asString == configuration.ownerId - private suspend fun Member.isGuildOwner() = isOwner() - private suspend fun Member.isAdministrator() = roles.any { configuration[guild.id.value]!!.adminRoles.contains(it.id.asString) } - private suspend fun Member.isStaff() = roles.any { configuration[guild.id.value]!!.staffRoles.contains(it.id.asString) } - private suspend fun Member.isModerator() = roles.any { configuration[guild.id.value]!!.moderatorRoles.contains(it.id.asString) } - - suspend fun isCommandVisible(guild: Guild, user: User, command: Command) = - hasClearance(guild, user, command.requiredPermissionLevel) -} - -var Command.requiredPermissionLevel: PermissionLevel - get() = commandPermissions[this] ?: DEFAULT_REQUIRED_PERMISSION - set(value) { - commandPermissions[this] = value - } \ No newline at end of file diff --git a/src/main/kotlin/me/ddivad/judgebot/services/database/UserOperations.kt b/src/main/kotlin/me/ddivad/judgebot/services/database/UserOperations.kt index 8881b33..bcf1b25 100644 --- a/src/main/kotlin/me/ddivad/judgebot/services/database/UserOperations.kt +++ b/src/main/kotlin/me/ddivad/judgebot/services/database/UserOperations.kt @@ -86,8 +86,8 @@ class UserOperations( } suspend fun addInfraction(guild: Guild, user: GuildMember, infraction: Infraction): Infraction { - user.addInfraction(infraction, guild) infraction.punishment = getPunishmentForPoints(guild, user) + user.addInfraction(infraction, guild) this.updateUser(user) return infraction }