Skip to content
This repository was archived by the owner on Sep 24, 2022. It is now read-only.

🔐 Lock commands #241

Open
wants to merge 19 commits into
base: dev
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from 11 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
@@ -0,0 +1,69 @@
package org.cascadebot.cascadebot.commands.moderation

import net.dv8tion.jda.api.Permission
import net.dv8tion.jda.api.entities.ISnowflake
import net.dv8tion.jda.api.entities.Member
import net.dv8tion.jda.api.entities.Role
import net.dv8tion.jda.api.entities.TextChannel
import net.dv8tion.jda.api.exceptions.PermissionException
import org.cascadebot.cascadebot.commandmeta.CommandContext
import org.cascadebot.cascadebot.commandmeta.MainCommand
import org.cascadebot.cascadebot.commandmeta.Module
import org.cascadebot.cascadebot.data.managers.LockManager
import org.cascadebot.cascadebot.permissions.CascadePermission
import org.cascadebot.cascadebot.utils.DiscordUtils

class LockCommand : MainCommand() {
override fun onCommand(sender: Member, context: CommandContext) {
var channel: TextChannel = context.channel
if (context.args.isNotEmpty()) {
channel = DiscordUtils.getTextChannel(context.guild, context.getArg(0))
?: return context.typedMessaging.replyDanger(context.i18n("responses.cannot_find_channel_matching", context.getArg(0)))

}

val target: ISnowflake = if (context.args.size == 2) {
DiscordUtils.getRole(context.getArg(1), context.guild)
?: DiscordUtils.getMember(context.guild, context.getArg(1))
?: DiscordUtils.getTextChannel(context.guild, context.getArg(1))
?: return context.typedMessaging.replyDanger(context.i18n("commands.lock.invalid_argument", context.getArg(0)))
} else {
context.guild.publicRole
}

if (target is TextChannel) channel = target;

val name: String = try {
when (target) {
is Role -> {
LockManager.lock(channel, target)
"%s %s".format(context.i18n("arguments.role"), target.asMention)
}
is Member -> {
LockManager.lock(channel, target)
"%s %s".format(context.i18n("arguments.member"), target.asMention)
}
else -> ""
}
} catch (e: PermissionException) {
context.uiMessaging.sendBotDiscordPermError(e.permission)
return
}

context.typedMessaging.replySuccess((context.i18n("commands.lock.success", channel.name, name)))
}


override fun command(): String {
return "lock"
}

override fun permission(): CascadePermission? {
return CascadePermission.of("lock", false, Permission.MANAGE_CHANNEL)
}

override fun module(): Module {
return Module.MODERATION
}

}
Original file line number Diff line number Diff line change
@@ -0,0 +1,113 @@
package org.cascadebot.cascadebot.commands.moderation

import net.dv8tion.jda.api.Permission
import net.dv8tion.jda.api.entities.ISnowflake
import net.dv8tion.jda.api.entities.Member
import net.dv8tion.jda.api.entities.Role
import net.dv8tion.jda.api.entities.TextChannel
import net.dv8tion.jda.api.exceptions.PermissionException
import org.cascadebot.cascadebot.commandmeta.CommandContext
import org.cascadebot.cascadebot.commandmeta.MainCommand
import org.cascadebot.cascadebot.commandmeta.Module
import org.cascadebot.cascadebot.data.managers.LockManager
import org.cascadebot.cascadebot.data.managers.Status
import org.cascadebot.cascadebot.data.managers.ScheduledActionManager
import org.cascadebot.cascadebot.permissions.CascadePermission
import org.cascadebot.cascadebot.scheduler.ActionType
import org.cascadebot.cascadebot.scheduler.ScheduledAction
import org.cascadebot.cascadebot.utils.DiscordUtils
import org.cascadebot.cascadebot.utils.FormatUtils
import org.cascadebot.cascadebot.utils.ParserUtils
import java.time.Instant
import java.time.OffsetDateTime
import java.time.temporal.ChronoUnit

class TempLockCommand : MainCommand() {
override fun onCommand(sender: Member, context: CommandContext) {
if (context.args.isEmpty()) {
context.uiMessaging.replyUsage()
return
}

val longDuration = ParserUtils.parseTextTime(context.getArg(0), false)
if (longDuration <= 0) {
context.typedMessaging.replyDanger(context.i18n("responses.invalid_duration"))
return
}

var channel: TextChannel = context.channel
if (context.args.size == 2) {
val tempChannel = DiscordUtils.getTextChannel(context.guild, context.getArg(1))
if (tempChannel != null) {
channel = tempChannel
} else {
context.typedMessaging.replyDanger(context.i18n("responses.cannot_find_channel_matching", context.getArg(1)))
return
}
}

val target: ISnowflake = if (context.args.size == 3) {
DiscordUtils.getRole(context.getArg(1), context.guild)
?: DiscordUtils.getMember(context.guild, context.getArg(2))
?: return context.typedMessaging.replyDanger(context.i18n("commands.templock.invalid_argument", context.getArg(2)))
} else {
context.channel
}

val toAction = ScheduledAction.LockActionData(channel.idLong, Status.NEUTRAL, 0, 0)

var name: String? = null
try {
when (target) {
is Role -> {
toAction.oldPermission = LockManager.getPerm(channel, target).left

LockManager.lock(channel, target)

toAction.targetRoleID = target.idLong
name = "%s %s".format(context.i18n("arguments.role"), target.asMention)
}
is Member -> {
toAction.oldPermission = LockManager.getPerm(channel, target).left

LockManager.lock(channel, target)

toAction.targetMemberID = target.idLong
name = "%s %s".format(context.i18n("arguments.member"), target.user.asMention)
}
}
} catch (e: PermissionException) {
context.uiMessaging.sendBotDiscordPermError(e.permission)
return
}

ScheduledActionManager.registerScheduledAction(ScheduledAction(
ActionType.UNLOCK,
toAction,
context.guild.idLong,
context.channel.idLong,
context.member.idLong,
Instant.now(),
longDuration
))


val textDuration = FormatUtils.formatTime(longDuration, context.locale, true).replace("(0[hm])".toRegex(), "") +
" (" + context.i18n("words.until") + " " + FormatUtils.formatDateTime(OffsetDateTime.now().plus(longDuration, ChronoUnit.MILLIS), context.locale) + ")"
context.typedMessaging.replySuccess(if (target is TextChannel) context.i18n("commands.templock.text_success", target.name, textDuration) else name?.let { context.i18n("commands.templock.success", channel.name, it, textDuration) })
}


override fun command(): String {
return "templock"
}

override fun permission(): CascadePermission? {
return CascadePermission.of("templock", false, Permission.MANAGE_CHANNEL)
}

override fun module(): Module {
return Module.MODERATION
}

}
Original file line number Diff line number Diff line change
@@ -0,0 +1,73 @@
package org.cascadebot.cascadebot.commands.moderation

import javassist.NotFoundException
import net.dv8tion.jda.api.Permission
import net.dv8tion.jda.api.entities.ISnowflake
import net.dv8tion.jda.api.entities.Member
import net.dv8tion.jda.api.entities.Role
import net.dv8tion.jda.api.entities.TextChannel
import net.dv8tion.jda.api.exceptions.PermissionException
import org.cascadebot.cascadebot.commandmeta.CommandContext
import org.cascadebot.cascadebot.commandmeta.MainCommand
import org.cascadebot.cascadebot.commandmeta.Module
import org.cascadebot.cascadebot.data.managers.LockManager
import org.cascadebot.cascadebot.permissions.CascadePermission
import org.cascadebot.cascadebot.utils.DiscordUtils

class UnlockCommand : MainCommand() {
override fun onCommand(sender: Member, context: CommandContext) {
var channel: TextChannel = context.channel
if (context.args.isNotEmpty()) {
channel = DiscordUtils.getTextChannel(context.guild, context.getArg(0))
?: return context.typedMessaging.replyDanger(context.i18n("responses.cannot_find_channel_matching", context.getArg(0)))

}

val target: ISnowflake = if (context.args.size == 2) {
DiscordUtils.getRole(context.getArg(1), context.guild)
?: DiscordUtils.getMember(context.guild, context.getArg(1))
?: return context.typedMessaging.replyDanger(context.i18n("commands.unlock.invalid_argument", context.getArg(1)))
} else {
context.guild.publicRole
}

var name = ""
val completed = try {
when (target) {
is Role -> {
name = target.asMention
LockManager.unlock(context.guild, channel, target)
}
is Member -> {
name = "%s %s".format(context.i18n("arguments.member"), target.asMention)
LockManager.unlock(context.guild, channel, target)
}
else -> false
}
} catch (e: PermissionException) {
context.uiMessaging.sendBotDiscordPermError(e.permission)
return
} catch (e: NotFoundException) {
context.typedMessaging.replyWarning(context.i18n("commands.unlock.fail", if (target is TextChannel) context.guild.publicRole.asMention else name!!))
return
}
if (completed) {
context.typedMessaging.replySuccess(context.i18n("commands.unlock.success", channel.name, name))
} else {
context.typedMessaging.replyDanger(context.i18n("commands.unlock.failure", channel.name, name))
}
}

override fun command(): String {
return "unlock"
}

override fun permission(): CascadePermission? {
return CascadePermission.of("unlock", false, Permission.MANAGE_CHANNEL)
}

override fun module(): Module {
return Module.MODERATION
}

}
Original file line number Diff line number Diff line change
@@ -0,0 +1,91 @@
package org.cascadebot.cascadebot.data.managers


import net.dv8tion.jda.api.Permission
import net.dv8tion.jda.api.entities.Guild
import net.dv8tion.jda.api.entities.IPermissionHolder
import net.dv8tion.jda.api.entities.TextChannel
import net.dv8tion.jda.api.requests.restaction.PermissionOverrideAction
import net.dv8tion.jda.internal.utils.tuple.MutablePair
import java.util.EnumSet

enum class Status {
ALLOW,
DENY,
NEUTRAL;

fun apply(action: PermissionOverrideAction, perm: EnumSet<Permission>) {
when (this) {
ALLOW -> action.grant(perm)
DENY -> action.deny(perm)
NEUTRAL -> action.clear(perm)
}
}

}

object LockManager {

fun getPerm(channel: TextChannel, target: IPermissionHolder): MutablePair<Status, Status> {
// [target, selfMember]
val perm = MutablePair<Status, Status>(Status.NEUTRAL, Status.NEUTRAL)

val targetOverride = channel.getPermissionOverride(target)
if (targetOverride != null) {
if (targetOverride.allowed.contains(Permission.MESSAGE_WRITE)) perm.setLeft(Status.ALLOW)
if (targetOverride.denied.contains(Permission.MESSAGE_WRITE)) perm.setLeft(Status.DENY)
}

val selfMemberOverride = channel.getPermissionOverride(channel.guild.selfMember)
if (selfMemberOverride != null) {
if (selfMemberOverride.allowed.contains(Permission.MESSAGE_WRITE)) perm.setRight(Status.ALLOW)
if (selfMemberOverride.denied.contains(Permission.MESSAGE_WRITE)) perm.setRight(Status.DENY)
}
return perm
}

private fun storePermissions(channel: TextChannel, target: IPermissionHolder) {
val lockedChannels = GuildDataManager.getGuildData(channel.guild.idLong).lockedChannels
val mutableMap = lockedChannels[channel.id]
if (mutableMap == null) {
lockedChannels[channel.id] = mutableMapOf(Pair(target.id, getPerm(channel, target)))
} else {
mutableMap[target.id] = getPerm(channel, target)
}
}


fun lock(channel: TextChannel, target: IPermissionHolder) {
storePermissions(channel, target)
channel.upsertPermissionOverride(channel.guild.selfMember).grant(Permission.MESSAGE_WRITE).queue()
channel.upsertPermissionOverride(target).deny(Permission.MESSAGE_WRITE).queue()
}

fun unlock(guild: Guild, channel: TextChannel, target: IPermissionHolder) : Boolean {
val state = GuildDataManager.getGuildData(channel.guild.idLong).lockedChannels[channel.id]?.get(target.id)
// If there is no state to restore, we can't do anything!
?: return false
val perm = EnumSet.of(Permission.MESSAGE_WRITE)

var changed = false;

val selfPermissionAction = channel.getPermissionOverride(guild.selfMember)?.manager
if (selfPermissionAction != null) {
state.right.apply(selfPermissionAction, perm)
selfPermissionAction.queue { if (it.allowedRaw == 0L && it.deniedRaw == 0L) it.delete().queue() }
changed = true;
}

val targetPermissionAction = channel.getPermissionOverride(target)?.manager
if (targetPermissionAction != null) {
state.left.apply(targetPermissionAction, perm)
targetPermissionAction.queue {
GuildDataManager.getGuildData(guild.idLong).lockedChannels[channel.idLong.toString()]?.remove(target.idLong.toString())
if (it.allowedRaw == 0L && it.deniedRaw == 0L) it.delete().queue()
}
changed = true
}
return changed
}

}
Original file line number Diff line number Diff line change
Expand Up @@ -5,8 +5,10 @@ import de.bild.codec.annotations.Id
import de.bild.codec.annotations.Transient
import net.dv8tion.jda.api.entities.Message
import net.dv8tion.jda.api.entities.MessageChannel
import net.dv8tion.jda.internal.utils.tuple.MutablePair
import org.cascadebot.cascadebot.CascadeBot
import org.cascadebot.cascadebot.data.language.Locale
import org.cascadebot.cascadebot.data.managers.Status
import org.cascadebot.cascadebot.music.CascadeLavalinkPlayer
import org.cascadebot.cascadebot.utils.buttons.ButtonGroup
import org.cascadebot.cascadebot.utils.buttons.ButtonsCache
Expand Down Expand Up @@ -42,6 +44,9 @@ class GuildData(@field:Id val guildId: Long) {
val music = GuildSettingsMusic()
//endregion

// <Channel ID, <Target ID, >>
var lockedChannels: MutableMap<String, MutableMap<String, MutablePair<Status, Status>>> = mutableMapOf()

//region Transient fields
@Transient
val buttonsCache = ButtonsCache(5)
Expand Down
10 changes: 8 additions & 2 deletions src/main/kotlin/org/cascadebot/cascadebot/messaging/Messaging.kt
Original file line number Diff line number Diff line change
Expand Up @@ -39,7 +39,10 @@ object Messaging {
return if (embed) {
channel.sendMessage(MessagingObjects.getMessageTypeEmbedBuilder(type).setDescription(message).build()).submit()
} else {
channel.sendMessage(MessagingObjects.getMessageTypeMessageBuilder(type).append(message).build()).submit()
channel.sendMessage(MessagingObjects.getMessageTypeMessageBuilder(type)
.append(message)
.denyMentions(Message.MentionType.EVERYONE, Message.MentionType.HERE, Message.MentionType.ROLE).build())
.submit()
}
}

Expand All @@ -49,7 +52,10 @@ object Messaging {
return if (embed) {
channel.sendMessage(builder.setColor(type.color).build()).submit()
} else {
channel.sendMessage(type.emoji + " " + FormatUtils.formatEmbed(builder.build())).submit()
channel.sendMessage(MessageBuilder()
.denyMentions(Message.MentionType.EVERYONE, Message.MentionType.HERE, Message.MentionType.ROLE)
.setContent(type.emoji + " " + FormatUtils.formatEmbed(builder.build()))
.build()).submit()
}
}

Expand Down
Loading