Skip to content

Commit

Permalink
Ad permission debugger
Browse files Browse the repository at this point in the history
  • Loading branch information
DRSchlaubi committed Oct 25, 2024
1 parent 00132c0 commit 8a3c3e7
Show file tree
Hide file tree
Showing 7 changed files with 101 additions and 13 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -37,7 +37,7 @@ suspend fun SettingsModule.addGuildSettingsCommand() = ephemeralSlashCommand {
action {
val channel = arguments.channel.asChannelOfOrNull<TopGuildMessageChannel>()
?: discordError(translate("commands.create.invalid_channel"))
checkPermissions(channel)
if (!checkPermissions(channel)) return@action

val guildSettings =
VoteBotDatabase.guildSettings.findOneByGuild(guild!!.id) ?: GuildSettings(newId(), guild!!.id, null)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -49,7 +49,7 @@ suspend fun <A : Arguments> SlashCommandContext<*, A, *>.createVote(
val channel = when {
isGuildInstall -> {
val guildChannel = (guildVoteChannel ?: settings.channel ?: this.channel).asChannelOf<GuildMessageChannel>()
checkPermissions(guildChannel)
if(!checkPermissions(guildChannel)) return null
guildChannel.toVoteParentChannel()
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,7 @@ suspend fun VoteBotModule.quickCommand() = ephemeralSlashCommand(::CreateOptions
voteCommandContext()

action {
createVote(interactionResponse)
createVote(interactionResponse) ?: return@action
respond {
content = translate("commands.create.success", arrayOf(arguments.title))
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,7 @@ suspend fun VoteBotModule.yesNowCommand() = publicSlashCommand(::YesNoArguments)
voteCommandContext()

action {
createVote(interactionResponse)
createVote(interactionResponse) ?: return@action
interactionResponse.createEphemeralFollowup {
content = translate("commands.yes_no.success")
}
Expand Down
82 changes: 75 additions & 7 deletions plugin/src/main/kotlin/space/votebot/util/PermissionUtil.kt
Original file line number Diff line number Diff line change
@@ -1,31 +1,99 @@
package space.votebot.util

import com.kotlindiscord.kord.extensions.commands.Arguments
import com.kotlindiscord.kord.extensions.commands.application.slash.EphemeralSlashCommandContext
import com.kotlindiscord.kord.extensions.commands.application.slash.PublicSlashCommandContext
import com.kotlindiscord.kord.extensions.commands.application.slash.SlashCommandContext
import com.kotlindiscord.kord.extensions.components.components
import com.kotlindiscord.kord.extensions.components.ephemeralButton
import com.kotlindiscord.kord.extensions.utils.translate
import dev.kord.common.entity.ButtonStyle
import dev.kord.common.entity.Permission
import dev.kord.common.entity.Permissions
import dev.kord.common.entity.Snowflake
import dev.kord.core.entity.channel.GuildMessageChannel
import dev.kord.core.entity.channel.TopGuildMessageChannel
import dev.kord.core.entity.channel.thread.ThreadChannel
import dev.schlaubi.mikbot.plugin.api.util.discordError
import dev.kord.core.entity.interaction.followup.FollowupMessage
import dev.kord.rest.builder.message.create.MessageCreateBuilder
import kotlin.time.Duration.Companion.minutes

suspend fun <A : Arguments> SlashCommandContext<*, A, *>.checkPermissions(channel: GuildMessageChannel) {
private val requiredPermissions =
Permissions(Permission.SendMessages, Permission.EmbedLinks, Permission.AttachFiles, Permission.ViewChannel)

suspend fun <A : Arguments> SlashCommandContext<*, A, *>.checkPermissions(channel: GuildMessageChannel): Boolean {
val selfPermissions = channel.getEffectivePermissions(channel.kord.selfId)
val requiredPermissions =
Permissions(Permission.SendMessages, Permission.EmbedLinks, Permission.AttachFiles, Permission.ViewChannel)
if (requiredPermissions !in selfPermissions) {
discordError(translate("vote.create.missing_permissions.bot", arrayOf(channel.mention)))
sendMissingPermissions("vote.create.missing_permissions.bot", channel, channel.kord.selfId, selfPermissions)
return false
}

val userPermissions = channel.getEffectivePermissions(user.id)
if ((requiredPermissions - Permission.ViewChannel) !in userPermissions) {
discordError(translate("vote.create.missing_permissions.user", arrayOf(channel.mention)))
if ((requiredPermissions - Permission.ViewChannel) !in userPermissions) {
sendMissingPermissions("vote.create.missing_permissions.user", channel, user.id, userPermissions)
return false
}

return true
}

private suspend fun SlashCommandContext<*, *, *>.sendMissingPermissions(
translation: String,
channel: GuildMessageChannel,
user: Snowflake,
permissions: Permissions
) {
suspend fun Boolean.translate() = translate(if (this) "common.yes" else "common.no")

respond {
content = translate(translation, arrayOf(channel.mention))

components(5.minutes) {
ephemeralButton {
bundle = command.resolvedBundle
style = ButtonStyle.Secondary
label = translate("vote.create.missing_permissions.explainer.label")

action {
val serverPermissions = channel.guild.getMember(user).getPermissions()
val isAdministrator =
if (Permission.Administrator in serverPermissions) "common.yes" else "common.no"

val missingPermissions = (requiredPermissions - permissions).values.map {
translate(
"vote.create.missing_permissions.explainer.permission",
arrayOf(
it.translate(this@sendMissingPermissions),
(it in serverPermissions).translate(),
(it in permissions).translate(),
)
)
}.joinToString("\n")


respond {
content = translate(
"vote.create.missing_permissions.explainer",
arrayOf(translate(isAdministrator), missingPermissions)
)
}
}
}
}
}
}


private suspend fun GuildMessageChannel.getEffectivePermissions(user: Snowflake) = when (this) {
is TopGuildMessageChannel -> getEffectivePermissions(user)
is ThreadChannel -> parent.asChannel().getEffectivePermissions(user)
else -> error("Could not determine permissions for channel type ${this::class.simpleName}")
}

suspend fun SlashCommandContext<*, *, *>.respond(build: suspend MessageCreateBuilder.() -> Unit): FollowupMessage {
return when (this) {
is EphemeralSlashCommandContext<*, *> -> respond(build)
is PublicSlashCommandContext<*, *> -> respond(build)
else -> error("Unsupported slash command context: $this")
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -134,4 +134,14 @@ vote.create.invalid_duration.dm=Aufgrund einer technischen Einschränkung von Di
eine Abstimmung zu erstellen, die länger als 10 Minuten dauert. Sie können jedoch keine Dauer angeben und \
die Abstimmung manuell schließen
commands.context.close.name=Umfrage schließen
vote.create.too_many_options.user_mode=Eine Umfrage kann im Benutzermodus nur 20 Optionen haben.
vote.create.too_many_options.user_mode=Eine Umfrage kann im Benutzermodus nur 20 Optionen haben.
vote.create.missing_permissions.explainer.label=Erfahre warum
vote.create.missing_permissions.explainer=\
**Berechtigungsinformationen** \n\
Ist Administrator: {0} \n\
### Fehlende Berechtigungen: \n\
{1}
vote.create.missing_permissions.explainer.permission=\
**{0}**: Auf Server Level gestattet: {1}; Auf Kanal Level gestattet: {2}
common.yes=Ja
common.no=Nein
Original file line number Diff line number Diff line change
Expand Up @@ -147,4 +147,14 @@ vote.create.invalid_duration.dm=Due to a technical limitation from Discord it is
a vote which lasts longer than 10-minutes in group chats, you can however specify no duration and \
close the vote manually
commands.context.close.name=Close poll
vote.create.too_many_options.user_mode=A poll can only have 20 options in user-mode
vote.create.too_many_options.user_mode=A poll can only have 20 options in user-mode
vote.create.missing_permissions.explainer.label=Find out why
vote.create.missing_permissions.explainer=\
**Permission debug information** \n\
Is Administrator: {0} \n\
### Missing Permissions: \n\
{1}
vote.create.missing_permissions.explainer.permission=\
**{0}**: Granted on server level: {1}; Granted on channel level: {2}
common.yes=Yes
common.no=No

0 comments on commit 8a3c3e7

Please sign in to comment.