Is your feature request related to a problem? Please describe.
Kord is effectively unmaintained — its DAVE (Discord E2EE voice) support lives only in a custom SNAPSHOT fork. Since Discord requires DAVE for all voice connections since March 2026, we're stuck maintaining a custom fork with 13 local JARs and manual transitive dependencies. This is fragile and unsustainable.
Describe the solution you'd like
Migrate to JDA v6.4.2 + jda-ktx (Kotlin coroutine extensions). Research done in #opencode confirms this is the most viable path:
- Active: JDA released v6.4.2 in June 2026 (180+ contributors)
- Coroutine-native: jda-ktx provides
CoroutineEventManager + .await() extensions — minimal paradigm shift from Kord
- DAVE ready: Supports the same
libdave-jvm library we already depend on
- Simpler deps: No local JARs, no manual transitive tracking — just Maven Central
We already use libdave-jvm (0.1.2/0.1.3) via JNI, which JDA also supports through its AudioModuleConfig API.
Describe alternatives you've considered
- Discord4J v3.3.2 — Also DAVE-ready and maintained, but uses Reactor (Mono/Flux), a reactive paradigm that would require rewriting every interaction handler. Bigger paradigm shift from our coroutine-heavy codebase.
- Staying on Kord — Requires maintaining the SNAPSHOT fork ourselves. Not sustainable long-term.
- Javacord — Less feature-rich, no DAVE support confirmed.
Migration scope (estimated ~17-19 days full-time)
| Layer |
Effort |
Notes |
| Dependencies |
1d |
Add JDA/jda-ktx, remove 13 local JARs, update Koin DI |
| Bot core |
2d |
Rewrite Bot.kt (client init, event wiring, presence) |
| Commands (13) |
4-5d |
Rewrite all command files + CommandHandlerService |
| Voice (GuildLavaPlayerService) |
3d |
Swap Kord's audioProvider lambda → JDA's AudioSendHandler + DAVE config |
| Components/Embeds |
2d |
Rewrite PlayerCommons, PaginatedEmbedUtils, component builders |
| Error handling |
1d |
Adapt ErrorHandlerService types |
| Tests (16 files) |
3-4d |
Rewrite DiscordMocks, adapt all Kord mocks to JDA |
| Cleanup |
1d |
Remove libs/ JARs, old deps, ktlint, verify |
Key changes per layer:
- Voice:
voiceChannel.connect { audioProvider { ... } } → guild.audioManager.sendingHandler = LavaPlayerSendHandler { ... } + AudioModuleConfig.withDaveSessionFactory(JDaveSessionFactory())
- Events:
bot.on<XEvent> { } → jda.listener<XEvent> { } (coroutine-friendly via jda-ktx)
- Commands:
GlobalMultiApplicationCommandBuilder → jda.updateCommands { slash("name", "desc") { } }
- Responses:
response.respond { content = "..." } → event.reply("...").await()
- Components:
ActionRowBuilder + interactionButton(...) → row(secondary(id) { }) (jda-ktx DSL)
- Embeds:
embed { ... } → Embed(title = "...") { field { } } (jda-ktx)
The business logic (Lavaplayer, queue management, TTS, radio, localization) stays largely intact.
Additional context
Full research and API mapping available in the opencode session logs. See also:
Is your feature request related to a problem? Please describe.
Kord is effectively unmaintained — its DAVE (Discord E2EE voice) support lives only in a custom SNAPSHOT fork. Since Discord requires DAVE for all voice connections since March 2026, we're stuck maintaining a custom fork with 13 local JARs and manual transitive dependencies. This is fragile and unsustainable.
Describe the solution you'd like
Migrate to JDA v6.4.2 + jda-ktx (Kotlin coroutine extensions). Research done in #opencode confirms this is the most viable path:
CoroutineEventManager+.await()extensions — minimal paradigm shift from Kordlibdave-jvmlibrary we already depend onWe already use
libdave-jvm(0.1.2/0.1.3) via JNI, which JDA also supports through itsAudioModuleConfigAPI.Describe alternatives you've considered
Migration scope (estimated ~17-19 days full-time)
Key changes per layer:
voiceChannel.connect { audioProvider { ... } }→guild.audioManager.sendingHandler = LavaPlayerSendHandler { ... }+AudioModuleConfig.withDaveSessionFactory(JDaveSessionFactory())bot.on<XEvent> { }→jda.listener<XEvent> { }(coroutine-friendly via jda-ktx)GlobalMultiApplicationCommandBuilder→jda.updateCommands { slash("name", "desc") { } }response.respond { content = "..." }→event.reply("...").await()ActionRowBuilder+interactionButton(...)→row(secondary(id) { })(jda-ktx DSL)embed { ... }→Embed(title = "...") { field { } }(jda-ktx)The business logic (Lavaplayer, queue management, TTS, radio, localization) stays largely intact.
Additional context
Full research and API mapping available in the opencode session logs. See also: