Skip to content
This repository was archived by the owner on Mar 7, 2024. It is now read-only.
Draft
Show file tree
Hide file tree
Changes from 5 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
13 changes: 10 additions & 3 deletions build.gradle.kts
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,16 @@ dependencies {
implementation(group = "com.fasterxml.jackson.core", name = "jackson-databind", version = "2.10.1")

implementation(group = "net.sf.trove4j", name = "trove4j", version = "3.0.3")

implementation(group = "com.discord4j", name = "discord4j-core", version = "3.1.6")

// TODO: remove after switch to d4j
implementation(group = "net.dv8tion", name = "JDA", version = "4.2.1_264") {
exclude(module = "opus-java")
}

// TODO: remove after switch to d4j
// TODO: custom oauth client?
// implementation(group = "com.jagrosh", name = "jda-utilities-oauth2", version = "3.0.5")
implementation(group = "com.github.JDA-Applications", name = "JDA-Utilities", version = "804d58a") {
// This is fine
Expand All @@ -43,9 +53,6 @@ dependencies {
exclude(module = "jda-utilities-command")
exclude(module = "jda-utilities-menu")
}
implementation(group = "net.dv8tion", name = "JDA", version = "4.2.1_264") {
exclude(module = "opus-java")
}

// Yes, this is JDA
// We're running this PR https://github.com/DV8FromTheWorld/JDA/pull/1178
Expand Down
4 changes: 2 additions & 2 deletions src/main/kotlin/com/dunctebot/dashboard/Container.kt
Original file line number Diff line number Diff line change
Expand Up @@ -2,11 +2,11 @@ package com.dunctebot.dashboard

import com.dunctebot.dashboard.websocket.WebsocketClient
import com.dunctebot.duncteapi.DuncteApi
import com.dunctebot.jda.JDARestClient
import com.dunctebot.discord.DiscordRestClient
import com.fasterxml.jackson.databind.json.JsonMapper
import okhttp3.OkHttpClient

val restJDA = JDARestClient(System.getenv("BOT_TOKEN"))
val discordClient = DiscordRestClient(System.getenv("BOT_TOKEN"))
val duncteApis = DuncteApi("Bot ${System.getenv("BOT_TOKEN")}")

val httpClient = OkHttpClient()
Expand Down
18 changes: 14 additions & 4 deletions src/main/kotlin/com/dunctebot/dashboard/WebHelpers.kt
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,8 @@ import com.dunctebot.dashboard.WebServer.Companion.USER_ID
import com.fasterxml.jackson.databind.JsonNode
import com.jagrosh.jdautilities.oauth2.OAuth2Client
import com.jagrosh.jdautilities.oauth2.session.Session
import discord4j.discordjson.json.GuildUpdateData
import discord4j.rest.entity.RestGuild
import net.dv8tion.jda.api.entities.Guild
import net.dv8tion.jda.internal.utils.IOUtil
import okhttp3.FormBody
Expand Down Expand Up @@ -39,11 +41,18 @@ val Request.userId: String
val Request.guildId: String?
get() = this.params(GUILD_ID)

fun Request.fetchGuild(): Guild? {
val guildId: String = this.guildId ?: return null
val Request.guild: RestGuild?
get() {
val guildId = this.guildId ?: return null

return discordClient.getGuild(guildId.toLong())
}

fun Request.fetchGuild(): GuildUpdateData? {
val guild: RestGuild = this.guild ?: return null

return try {
restJDA.retrieveGuildById(guildId).complete()
guild.data.block()
} catch (e: Exception) {
e.printStackTrace()
null
Expand Down Expand Up @@ -80,8 +89,9 @@ fun String?.toSafeLong(): Long {
}
}

@Throws(HaltException::class)
fun haltNotFound(request: Request, response: Response) {
Spark.halt(404, CustomErrorPages.getFor(404, request, response) as String)
throw Spark.halt(404, CustomErrorPages.getFor(404, request, response) as String)
}

fun verifyCaptcha(response: String): JsonNode {
Expand Down
31 changes: 25 additions & 6 deletions src/main/kotlin/com/dunctebot/dashboard/WebServer.kt
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,10 @@ import com.dunctebot.models.settings.WarnAction
import com.dunctebot.models.utils.Utils
import com.fasterxml.jackson.databind.JsonNode
import com.jagrosh.jdautilities.oauth2.OAuth2Client
import net.dv8tion.jda.api.entities.TextChannel
import discord4j.common.util.Snowflake
import discord4j.core.`object`.entity.channel.TextChannel
import discord4j.rest.util.Permission
import discord4j.rest.util.PermissionSet
import spark.ModelAndView
import spark.Spark.*

Expand Down Expand Up @@ -270,15 +273,31 @@ class WebServer {

private fun getWithGuildData(path: String, map: WebVariables, view: String) {
get(path) { request, _ ->
val guild = request.fetchGuild()
val guild = request.guild

if (guild != null) {
val guildId = guild.idLong
val guildId = guild.id.asLong()
val self = guild.selfMember.block()!!
val selfId = Snowflake.of(self.user().id())

val tcs = guild.channels
.ofType(TextChannel::class.java)
.filter {
it.getEffectivePermissions(selfId).map { p ->
p.containsAll(PermissionSet.of(
Permission.SEND_MESSAGES, Permission.VIEW_CHANNEL /* read messages */
))
}.block()!!
}

val goodRoles = guild.roles
.filter { !it.managed() }
.filter { it.name() != "@everyone" && it.name() != "@here" }
// TODO: check if can interact

val tcs = guild.textChannelCache.filter(TextChannel::canTalk).toList()
val goodRoles = guild.roleCache.filter {
/*val goodRoles_old = guild.roleCache.filter {
guild.selfMember.canInteract(it) && it.name != "@everyone" && it.name != "@here"
}.filter { !it.isManaged }.toList()
}.filter { !it.isManaged }.toList()*/

map.put("goodChannels", tcs)
map.put("goodRoles", goodRoles)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,9 +5,9 @@ import com.dunctebot.dashboard.WebServer.Companion.SESSION_ID
import com.dunctebot.dashboard.WebServer.Companion.USER_ID
import com.dunctebot.dashboard.fetchGuild
import com.dunctebot.dashboard.guildId
import com.dunctebot.dashboard.restJDA
import com.dunctebot.dashboard.discordClient
import com.dunctebot.dashboard.userId
import net.dv8tion.jda.api.Permission
import discord4j.rest.util.Permission
import spark.Request
import spark.Response
import spark.Spark
Expand All @@ -27,13 +27,18 @@ object DashboardController {
"&scope=bot&permissions=1609952470\" target=\"_blank\">invite it</a>?")

val member = try {
restJDA.retrieveMemberById(guild, request.userId).complete()
discordClient.retrieveMemberById(guild.id().asLong(), request.userId).block()!!
} catch (e: Exception) {
e.printStackTrace()
throw Spark.halt(200, "<h1>Either discord did a fucky wucky or you are not in the server that you are trying to edit</h1>")
Comment thread
duncte123 marked this conversation as resolved.
Outdated
}

if (!member.hasPermission(Permission.MANAGE_SERVER)) {
println("---------------------------------------")
println("Permissions")
println(member.permissions().get())
println("---------------------------------------")

if (!member.permissions().get().contains(Permission.MANAGE_GUILD.name)) {
Spark.halt(200, "<h1>You do not have permission to edit this server</h1>")
}
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,13 +4,11 @@ import com.dunctebot.dashboard.*
import com.dunctebot.dashboard.rendering.DbModelAndView
import com.dunctebot.dashboard.rendering.WebVariables
import com.github.benmanes.caffeine.cache.Caffeine
import net.dv8tion.jda.api.entities.Member
import net.dv8tion.jda.api.entities.Role
import net.dv8tion.jda.api.exceptions.ErrorResponseException
import discord4j.discordjson.json.MemberData
import discord4j.discordjson.json.RoleData
import spark.Request
import spark.Response
import java.util.concurrent.TimeUnit
import kotlin.streams.toList

object GuildController {
// some hash -> "$userId-$guildId"
Expand Down Expand Up @@ -88,32 +86,28 @@ object GuildController {
fun showGuildRoles(request: Request, response: Response): Any {
val hash = request.params("hash")
val guildId = guildHashes.getIfPresent(hash) ?: return haltNotFound(request, response)
val guild = try {
// TODO: do we want to do this?
// Maybe only cache for a short time as it will get outdated data
restJDA.fakeJDA.getGuildById(guildId) ?: restJDA.retrieveGuildById(guildId.toString()).complete()
} catch (e: ErrorResponseException) {
e.printStackTrace()
return haltNotFound(request, response)
}
val guild = discordClient.retrieveGuildData(guildId)

val roles = guildRoleCache.get(guild.idLong) {
val members = restJDA.retrieveAllMembers(guild).stream().toList()
val roles = guildRoleCache.get(guildId) {
val internalRoles = discordClient.retrieveGuildRoles(guildId)
val members = discordClient.retrieveGuildMembers(guildId).collectList().block()!!

guild.roles.map { CustomRole(it, members) }
internalRoles.map { CustomRole(it, members) }.collectList().block()
}!!

val guildName = guild.name()

return WebVariables()
.put("hide_menu", true)
.put("title", "Roles for ${guild.name}")
.put("guild_name", guild.name)
.put("title", "Roles for $guildName")
.put("guild_name", guildName)
.put("roles", roles)
.toModelAndView("guildRoles.vm")
}

class CustomRole(private val realRole: Role, allMembers: List<Member>) : Role by realRole {
class CustomRole(private val realRole: RoleData, allMembers: List<MemberData>) : RoleData by realRole {
// Accessed by our templating engine
@Suppress("unused")
val memberCount = allMembers.filter { it.roles.contains(realRole) }.size
val memberCount = allMembers.filter { it.roles().contains(realRole.id()) }
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -3,9 +3,10 @@ package com.dunctebot.dashboard.controllers.api
import com.dunctebot.dashboard.*
import com.dunctebot.dashboard.controllers.GuildController
import com.dunctebot.dashboard.utils.HashUtils
import com.dunctebot.discord.extensions.asTag
import com.fasterxml.jackson.databind.JsonNode
import com.fasterxml.jackson.databind.node.ObjectNode
import net.dv8tion.jda.api.entities.User
import discord4j.discordjson.json.UserData
import spark.Request
import spark.Response
import java.util.concurrent.CompletableFuture
Expand Down Expand Up @@ -45,8 +46,8 @@ object GuildApiController {
.put("code", response.status())
}

val user: User? = try {
restJDA.retrieveUserById(data["user_id"].asText()).complete()
val user: UserData? = try {
discordClient.retrieveD4JUserById(data["user_id"].asText()).data.block()
} catch (e: Exception) {
e.printStackTrace()
null
Expand Down Expand Up @@ -101,12 +102,13 @@ object GuildApiController {
.put("id", guildId)
.put("name", guild["name"].asText())

val userId = user.id().asString()
val userJson = jsonMapper.createObjectNode()
.put("id", user.id)
.put("name", user.name)
.put("id", userId)
.put("name", user.username())
.put("formatted", user.asTag)

val theKey = "${user.idLong}-${guildId}"
val theKey = "$userId-$guildId"
val theHash = HashUtils.sha1(theKey + System.currentTimeMillis())

GuildController.securityKeys[theHash] = theKey
Expand Down
Original file line number Diff line number Diff line change
@@ -1,14 +1,14 @@
package com.dunctebot.dashboard.websocket.handlers

import com.dunctebot.dashboard.restJDA
import com.dunctebot.dashboard.discordClient
import com.dunctebot.dashboard.websocket.handlers.base.SocketHandler
import com.fasterxml.jackson.databind.JsonNode

class DataUpdateHandler : SocketHandler() {
override fun handleInternally(data: JsonNode?) {
if (data!!.has("guilds")) {
data["guilds"]["invalidate"].forEach {
restJDA.invalidateGuild(it.asLong())
discordClient.invalidateGuild(it.asLong())
}
}
}
Expand Down
64 changes: 64 additions & 0 deletions src/main/kotlin/com/dunctebot/discord/DiscordRestClient.kt
Original file line number Diff line number Diff line change
@@ -0,0 +1,64 @@
package com.dunctebot.discord

import com.github.benmanes.caffeine.cache.Caffeine
import discord4j.common.util.Snowflake
import discord4j.core.DiscordClient
import discord4j.discordjson.json.*
import discord4j.rest.entity.RestGuild
import discord4j.rest.entity.RestUser
import net.dv8tion.jda.api.entities.Guild
import reactor.core.publisher.Flux
import reactor.core.publisher.Mono
import java.util.concurrent.TimeUnit


/**
* Custom jda rest client that allows for rest-only usage of JDA
*
* This class has been inspired by GivawayBot and all credit goes to them https://github.com/jagrosh/GiveawayBot
*/
class DiscordRestClient(token: String) {
// create a guild cache that keeps the guilds in cache for 30 minutes
// When we stop accessing the guild it will be removed from the cache
// TODO: use expiring map instead
private val guildCache = Caffeine.newBuilder()
.expireAfterAccess(30, TimeUnit.MINUTES)
.build<String, Guild>()

private val client = DiscordClient.create(token)

// TODO: still needed?
fun invalidateGuild(guildId: Long) {
this.guildCache.invalidate(guildId.toString())
}

// Note: instantly starts the request
fun retrieveD4JUserById(id: String): RestUser {
return this.client.getUserById(Snowflake.of(id))
}

// TODO: cache this
fun retrieveD4JSelfUser(): Mono<UserData> {
return this.client.self
}

fun retrieveGuildData(guildId: Long): GuildUpdateData {
return this.getGuild(guildId).data.block()!!
}

fun getGuild(guildId: Long): RestGuild {
return this.client.getGuildById(Snowflake.of(guildId))
}

fun retrieveGuildRoles(guildId: Long): Flux<RoleData> {
return this.getGuild(guildId).roles
}

fun retrieveGuildMembers(guildId: Long): Flux<MemberData> {
return this.getGuild(guildId).members
}

fun retrieveMemberById(guildId: Long, memberId: String): Mono<MemberData> {
return this.getGuild(guildId).getMember(Snowflake.of(memberId))
}
}
6 changes: 6 additions & 0 deletions src/main/kotlin/com/dunctebot/discord/extensions/UserData.kt
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
package com.dunctebot.discord.extensions

import discord4j.discordjson.json.UserData

val UserData.asTag: String
get() = "${this.username()}#${this.discriminator()}"
12 changes: 0 additions & 12 deletions src/main/kotlin/com/dunctebot/jda/FakeJDA.kt

This file was deleted.

31 changes: 0 additions & 31 deletions src/main/kotlin/com/dunctebot/jda/FakeSessionController.kt

This file was deleted.

Loading