2.8 KiB
Discord code and reflection
Most interesting plugins patch Discord internals. You need to find the right class and method, then sometimes reach private members.
Upstream:
docs/documentation/plugin-dev/6_finding_discord_stuff.mddocs/documentation/plugin-dev/5_reflection.md
Decompile Discord
Pull the APK Aliucord uses (legacy Discord 126.21) and run JADX:
jadx --export-gradle --show-bad-code --fs-case-sensitive \
--respect-bytecode-access-modifiers \
--no-inline-methods --no-inline-anonymous --no-inline-kotlin-lambda \
--output-dir decompiled base.apk
With Aliucord's JADX fork you can also pass --no-generate-kotlin-metadata to drop noisy @Metadata annotations.
A public decompile dump exists at Juby210/discord-jadx. It can be outdated. Prefer a fresh decompile of 126.21 when a symbol is missing.
For live UI ids while Aliucord is running, Developer Assistant helps.
Search strategy that works
- Reproduce the UI action in Discord.
- Guess a class name (
Widget*,Store*,Model*) and search the decompile. - Confirm with nearby strings, resource ids, or call stacks from other plugins.
- Copy how a similar feature is patched in
examples/.
Examples of good "find a peer and copy the target" plugins:
| Goal | Look at |
|---|---|
| Chat list / message UI | Template message embed patch; Vendicated MessageLinkEmbeds, ShowBlockedMessages |
| Stores / pending reply | Juby210 NoAutoReplyMention |
| User model fields | Template CoreUser.getUsername patches |
| Typing | Template StoreUserTyping instead-hook |
Prefer access$ methods over reflection
Kotlin/Java compilers emit access$... bridges for private members. Call those first. They are faster and stabler than raw reflection.
WidgetMedia.`access$handlePlayerEvent`(widgetMedia, event)
Reflection when you must
val field = ClassName::class.java.getDeclaredField("fieldName").apply {
isAccessible = true
}
val value = field.get(instance) // null instance for static
val method = ClassName::class.java.getDeclaredMethod("methodName", Arg::class.java).apply {
isAccessible = true
}
val result = method.invoke(instance, arg)
Same idea in Java with setAccessible(true).
Cache reflected Field/Method objects. Looking them up on every call is slow.
Obfuscation helpers
Some Discord getters are mangled. Aliucord ships wrappers/extensions for common ones (e.g. MessageEmbedWrapper.title used in the template). Check com.aliucord.wrappers before writing your own reflective accessors.
Next: Build, deploy, publish