Skip to content

Revenge Next Native Kotlin Development

Native plugins execute pre-compiled DEX bytecode inside Discord\'s ClassLoader before Hermes JS initializes.


1. Plugin Structure

A native plugin contains a Kotlin source tree under src/main/kotlin and a manifest.json:

{
  "format": 1,
  "id": "dev.everestmcarthur.my-plugin",
  "name": "My Plugin",
  "description": "High performance native Android hooks.",
  "author": "Rosie",
  "icon": "ic_cog_24px",
  "version": "1.0.0",
  "dependencies": {
    "revenge.api": {
      "version": ">=1 <2"
    },
    "discord": {
      "version": "*"
    }
  },
  "dist": {
    "script": "index.js",
    "android": {
      "path": "plugin.jar",
      "class": "dev.everestmcarthur.myplugin.MyPlugin"
    }
  }
}

2. Kotlin DSL Entrypoint (MyPlugin.kt)

Annotate the file with @file:JvmName(...) matching dist.android.class:

@file:JvmName("MyPlugin")

package dev.everestmcarthur.myplugin

import android.widget.TextView
import de.robv.android.xposed.XC_MethodHook
import de.robv.android.xposed.XposedBridge
import de.robv.android.xposed.XposedHelpers
import io.github.revenge.plugins.plugin
import io.github.revenge.xposed.api.registerMethod

val myPlugin = plugin {
    start {
        registerMethod("${manifest.id}.setNativeConfig") { args ->
            val jsonConfig = args.optString(0)
            "ACK"
        }

        try {
            val messageViewClass = XposedHelpers.findClass(
                "com.discord.chat.presentation.message.view.MessageContentView",
                hostClassLoader
            )

            XposedBridge.hookAllMethods(
                messageViewClass,
                "setLineSpacing",
                object : XC_MethodHook() {
                    override fun beforeHookedMethod(param: MethodHookParam) {
                        param.args[0] = 0.0f
                        param.args[1] = 1.0f
                    }
                }
            )
        } catch (e: Throwable) {
        }
    }

    stop {
    }
}

3. Compilation & Packaging

Compile all native plugins into build/outputs/plugin/plugin.jar using Gradle and D8 desugaring:

./gradlew packageAllPlugins -PjsTool=bun