Skip to content
This repository was archived by the owner on Mar 7, 2024. It is now read-only.

Replace JDA with Discord4J #4

Draft
wants to merge 17 commits into
base: develop
Choose a base branch
from
Draft
Show file tree
Hide file tree
Changes from all 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
22 changes: 11 additions & 11 deletions build.gradle.kts
Original file line number Diff line number Diff line change
Expand Up @@ -27,14 +27,24 @@ dependencies {

implementation(group = "ch.qos.logback", name = "logback-classic", version = "1.2.3")

implementation(group = "com.sparkjava", name = "spark-core", version = "2.9.2")
implementation(group = "com.sparkjava", name = "spark-core", version = "2.9.3")
implementation(group = "org.apache.velocity", name = "velocity-engine-core", version = "2.2")

implementation(group = "com.github.ben-manes.caffeine", name = "caffeine", version = "2.8.5")

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,16 +53,6 @@ dependencies {
exclude(module = "jda-utilities-command")
exclude(module = "jda-utilities-menu")
}
implementation(group = "net.dv8tion", name = "JDA", version = "4.3.0_298") {
exclude(module = "opus-java")
}

// Yes, this is JDA
// We're running this PR https://github.com/DV8FromTheWorld/JDA/pull/1178
// but it is broken atm
/*implementation(group = "com.github.dv8fromtheworld", name = "JDA", version = "68f4c4b") {
exclude(module = "opus-java")
}*/
}

configure<JavaPluginConvention> {
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
24 changes: 16 additions & 8 deletions src/main/kotlin/com/dunctebot/dashboard/WebHelpers.kt
Original file line number Diff line number Diff line change
Expand Up @@ -8,8 +8,8 @@ import com.dunctebot.dashboard.rendering.WebVariables
import com.fasterxml.jackson.databind.JsonNode
import com.jagrosh.jdautilities.oauth2.OAuth2Client
import com.jagrosh.jdautilities.oauth2.session.Session
import net.dv8tion.jda.api.entities.Guild
import net.dv8tion.jda.internal.utils.IOUtil
import discord4j.discordjson.json.GuildUpdateData
import discord4j.rest.entity.RestGuild
import okhttp3.FormBody
import spark.*
import java.net.URLDecoder
Expand Down Expand Up @@ -43,11 +43,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 @@ -127,8 +134,9 @@ fun verifyCaptcha(response: String): JsonNode {
.post(body)
.build()
).execute().use {
val readFully = IOUtil.readFully(IOUtil.getBody(it))

return jsonMapper.readTree(readFully)
it.body().use { body ->
// reads the entire body into memory
return jsonMapper.readTree(body!!.bytes())
}
}
}
47 changes: 32 additions & 15 deletions src/main/kotlin/com/dunctebot/dashboard/WebServer.kt
Original file line number Diff line number Diff line change
Expand Up @@ -9,17 +9,17 @@ import com.dunctebot.dashboard.controllers.api.DataController
import com.dunctebot.dashboard.controllers.api.GuildApiController
import com.dunctebot.dashboard.controllers.api.OtherAPi
import com.dunctebot.dashboard.controllers.errors.HttpErrorHandlers
import com.dunctebot.dashboard.rendering.VelocityRenderer
import com.dunctebot.dashboard.rendering.WebVariables
import com.dunctebot.dashboard.utils.fetchGuildPatronStatus
import com.dunctebot.dashboard.utils.getEffectivePermissions
import com.dunctebot.models.settings.GuildSetting
import com.dunctebot.models.settings.ProfanityFilterType
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 spark.ModelAndView
import discord4j.common.util.Snowflake
import discord4j.rest.util.Permission
import discord4j.rest.util.PermissionSet
import spark.Spark.*

// The socket server will be used to communicate with DuncteBot himself
Expand Down Expand Up @@ -202,11 +202,6 @@ class WebServer {
return@get OtherAPi.uptimeRobot()
}

// keep?
get("/commands.json") { _, _ ->
"TODO: setup websocket to bot"
}

post("/update-data") { request, _ ->
return@post DataController.updateData(request)
}
Expand Down Expand Up @@ -255,19 +250,41 @@ 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
.filter {
it.getEffectivePermissions(guild, self).map { p ->
println("Permissions $p")
p.containsAll(PermissionSet.of(
Permission.SEND_MESSAGES, Permission.VIEW_CHANNEL /* read messages */
))
}.block()!!
}
.collectList()
.block()!!

println("channels $tcs")

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

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)
map.put("guild", guild)
map.put("guild", discordClient.retrieveGuildData(guildId))

val settings = duncteApis.getGuildSetting(guildId)

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,8 @@ import com.dunctebot.dashboard.*
import com.dunctebot.dashboard.WebServer.Companion.OLD_PAGE
import com.dunctebot.dashboard.WebServer.Companion.SESSION_ID
import com.dunctebot.dashboard.WebServer.Companion.USER_ID
import net.dv8tion.jda.api.Permission
import com.dunctebot.discord.extensions.hasPermission
import discord4j.rest.util.Permission
import spark.Request
import spark.Response
import spark.Spark
Expand All @@ -21,15 +22,16 @@ object DashboardController {
}

val guild = request.fetchGuild() ?: throw haltDiscordError(DiscordError.NO_GUILD, request.guildId!!)
val guildId = guild.id().asLong()

val member = try {
restJDA.retrieveMemberById(guild, request.userId).complete()
discordClient.retrieveMemberById(guildId, request.userId).block()!!
} catch (e: Exception) {
e.printStackTrace()
throw haltDiscordError(DiscordError.WAT)
}

if (!member.hasPermission(Permission.MANAGE_SERVER)) {
if (!member.hasPermission(guildId, Permission.MANAGE_GUILD)) {
throw haltDiscordError(DiscordError.NO_PERMS)
}
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,23 +4,24 @@ 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 {
private const val DEFAULT_ROLE_COLOUR = 0x1FFFFFFF

// some hash -> "$userId-$guildId"
// TODO: convert to expiring map
val securityKeys = mutableMapOf<String, String>()
val guildHashes = Caffeine.newBuilder()
.expireAfterWrite(2, TimeUnit.HOURS)
.build<String, Long>()
val guildRoleCache = Caffeine.newBuilder()
.expireAfterWrite(1, TimeUnit.HOURS)
.build<Long, List<CustomRole>>()
.build<Long, Pair<String, List<CustomRole>>>()

fun handleOneGuildRegister(request: Request): Any {
val params = request.paramsMap
Expand Down Expand Up @@ -87,33 +88,35 @@ 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 guildId = guildHashes.getIfPresent(hash) ?: throw haltNotFound(request, response)
// val guildId = hash.toLong() // cheat :D

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

guild.roles.map { CustomRole(it, members) }
guild.name() to internalRoles.map { CustomRole(it, members) }
.sort { o1, o2 -> o2.position() - o1.position() }
.collectList().block()!!
}!!

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 {
// Accessed by our templating engine
@Suppress("unused")
val memberCount = allMembers.filter { it.roles.contains(realRole) }.size
// Accessed by our templating engine
@Suppress("unused")
class CustomRole(private val realRole: RoleData, allMembers: List<MemberData>) : RoleData by realRole {
val memberCount = allMembers.filter { it.roles().contains(realRole.id()) }.size

// overloads mimicking JDA names for easy access in the template
val idLong = realRole.id().asLong()
val name: String = realRole.name()
val colorRaw = if (realRole.color() == 0) DEFAULT_ROLE_COLOUR else realRole.color()
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,6 @@ import com.jagrosh.jdautilities.oauth2.OAuth2Client
import com.jagrosh.jdautilities.oauth2.OAuth2Client.DISCORD_REST_VERSION
import com.jagrosh.jdautilities.oauth2.Scope
import com.jagrosh.jdautilities.oauth2.exceptions.InvalidStateException
import net.dv8tion.jda.api.exceptions.HttpException
import org.slf4j.LoggerFactory
import spark.Request
import spark.Response
Expand Down
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.getUser(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
Loading