Skip to content

Commit

Permalink
feat(voice)!: add support for node.js (#931)
Browse files Browse the repository at this point in the history
* chore: configure gradle for js voice

* fix(samples/voice): support js

* fix(voice/non-jvm): use correct nonce length

* feat(voice): support js

* chore(voice): api dump

* chore: requested changes

* fix(gradle): use correct kotlin-node version

* feat(voice): configurable dispatcher

* chore: api dump
  • Loading branch information
viztea authored Apr 12, 2024
1 parent b8021fe commit 35227fc
Show file tree
Hide file tree
Showing 30 changed files with 330 additions and 177 deletions.
20 changes: 11 additions & 9 deletions buildSrc/src/main/kotlin/Targets.kt
Original file line number Diff line number Diff line change
Expand Up @@ -15,19 +15,21 @@ fun KotlinMultiplatformExtension.targets() {
jvm()

if (project.name != "voice" && project.name != "core-voice") {
// https://youtrack.jetbrains.com/issue/KTOR-4080
mingwX64()
js {
nodejs {
testTask {
useMocha {
// disable timeouts, some tests are too slow for default 2-second timeout:
// https://mochajs.org/#-timeout-ms-t-ms
timeout = "0"
}
}

js {
nodejs {
testTask {
useMocha {
// disable timeouts, some tests are too slow for default 2-second timeout:
// https://mochajs.org/#-timeout-ms-t-ms
timeout = "0"
}
}
useCommonJs()
}
useCommonJs()
}

macosArm64()
Expand Down
5 changes: 5 additions & 0 deletions buildSrc/src/main/kotlin/kord-multiplatform-module.gradle.kts
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,11 @@ kotlin {
withNative()
withJs()
}

group("nonJs") {
withNative()
withJvm()
}
}
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,9 @@ import dev.kord.core.entity.channel.VoiceChannel
import dev.kord.core.exception.GatewayNotFoundException
import dev.kord.voice.VoiceConnection
import dev.kord.voice.VoiceConnectionBuilder
import kotlinx.coroutines.SupervisorJob
import kotlinx.coroutines.job
import kotlinx.coroutines.plus
import kotlin.jvm.JvmName

/**
Expand All @@ -24,8 +27,10 @@ public suspend fun BaseVoiceChannelBehavior.connect(builder: VoiceConnectionBuil
kord.selfId,
id,
guildId,
builder
)
) {
scope { guild.kord + SupervisorJob(guild.kord.coroutineContext.job) }
builder()
}

voiceConnection.connect()

Expand Down
2 changes: 1 addition & 1 deletion gradle/libs.versions.toml
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ kord-cache = "0.5.0-20240130.022209-2" # https://github.com/kordlib/cache
# implementation dependencies
kotlin-logging = "6.0.3" # https://github.com/oshai/kotlin-logging
slf4j = "2.0.12" # https://www.slf4j.org
kotlin-node = "20.11.5-pre.706" # https://github.com/JetBrains/kotlin-wrappers
kotlin-node = "20.11.30-pre.723" # https://github.com/JetBrains/kotlin-wrappers
bignum = "0.3.9" # https://github.com/ionspin/kotlin-multiplatform-bignum
stately = "2.0.6" # https://github.com/touchlab/Stately
fastZlib = "2.0.1" # https://github.com/timotejroiko/fast-zlib
Expand Down
12 changes: 12 additions & 0 deletions kotlin-js-store/yarn.lock
Original file line number Diff line number Diff line change
Expand Up @@ -310,6 +310,18 @@ [email protected]:
dependencies:
argparse "^2.0.1"

libsodium-sumo@^0.7.13:
version "0.7.13"
resolved "https://registry.yarnpkg.com/libsodium-sumo/-/libsodium-sumo-0.7.13.tgz#533b97d2be44b1277e59c1f9f60805978ac5542d"
integrity sha512-zTGdLu4b9zSNLfovImpBCbdAA4xkpkZbMnSQjP8HShyOutnGjRHmSOKlsylh1okao6QhLiz7nG98EGn+04cZjQ==

[email protected]:
version "0.7.13"
resolved "https://registry.yarnpkg.com/libsodium-wrappers-sumo/-/libsodium-wrappers-sumo-0.7.13.tgz#a33aea845a0bb56db067548f04feba28c730ab8e"
integrity sha512-lz4YdplzDRh6AhnLGF2Dj2IUj94xRN6Bh8T0HLNwzYGwPehQJX6c7iYVrFUPZ3QqxE0bqC+K0IIqqZJYWumwSQ==
dependencies:
libsodium-sumo "^0.7.13"

locate-path@^6.0.0:
version "6.0.0"
resolved "https://registry.yarnpkg.com/locate-path/-/locate-path-6.0.0.tgz#55321eb309febbc59c4801d931a72452a681d286"
Expand Down
6 changes: 6 additions & 0 deletions samples/build.gradle.kts
Original file line number Diff line number Diff line change
Expand Up @@ -14,11 +14,17 @@ kotlin {
withLinux()
withMacos()
withJvm()
withJs()
}
}
}

targets()

js {
binaries.executable()
}

targets.withType<KotlinNativeTarget> {
// Voice does not target windows, so we disable it
if (konanTarget.family != Family.MINGW) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@ suspend fun main(args: Array<String>) {
gateway.events.filterIsInstance<MessageCreate>().onEach {
val words = it.message.content.split(' ')
when (words.firstOrNull()) {
"!close" -> gateway.stop()
"!close" -> gateway.stop()
"!detach" -> gateway.detach()
"!status" -> when (words.getOrNull(1)) {
"playing" -> gateway.editPresence {
Expand All @@ -29,7 +29,7 @@ suspend fun main(args: Array<String>) {
}
}

"!ping" -> gateway.editPresence {
"!ping" -> gateway.editPresence {
status = PresenceStatus.Online
afk = false
listening("a ${gateway.ping.value?.inWholeMilliseconds} ms ping")
Expand Down
3 changes: 3 additions & 0 deletions samples/src/commonMain/kotlin/runMain.kt
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
import kotlinx.coroutines.CoroutineScope

expect fun runMain(block: suspend CoroutineScope.() -> Unit)
7 changes: 7 additions & 0 deletions samples/src/jsMain/kotlin/runMain.js.kt
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.GlobalScope
import kotlinx.coroutines.launch

actual fun runMain(block: suspend CoroutineScope.() -> Unit) {
GlobalScope.launch { block() }
}
8 changes: 8 additions & 0 deletions samples/src/jvmMain/kotlin/runMain.jvm.kt
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.GlobalScope
import kotlinx.coroutines.launch
import kotlinx.coroutines.runBlocking

actual fun runMain(block: suspend CoroutineScope.() -> Unit) {
runBlocking { block() }
}
6 changes: 6 additions & 0 deletions samples/src/nativeMain/kotlin/runMain.native.kt
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.runBlocking

actual fun runMain(block: suspend CoroutineScope.() -> Unit) {
runBlocking { block() }
}
10 changes: 6 additions & 4 deletions samples/src/voiceMain/kotlin/VoiceBot.kt
Original file line number Diff line number Diff line change
Expand Up @@ -11,10 +11,11 @@ import dev.kord.core.event.interaction.GuildChatInputCommandInteractionCreateEve
import dev.kord.core.on
import dev.kord.voice.AudioFrame
import kotlinx.coroutines.launch
import kotlinx.coroutines.runBlocking
import runMain

fun main(args: Array<String>) = runBlocking {
val kord = Kord(args.firstOrNull() ?: error("Missing token"))
fun main(args: Array<String>) = runMain {
val kord =
Kord(args.firstOrNull() ?: error("Missing token"))

kord.createGlobalApplicationCommands {
input("join", "Test command") {
Expand All @@ -35,12 +36,13 @@ fun main(args: Array<String>) = runBlocking {
kord.login()
}

@OptIn(KordVoice::class)
private suspend fun BaseVoiceChannelBehavior.connectEcho() {
val buffer = mutableListOf(AudioFrame.SILENCE, AudioFrame.SILENCE, AudioFrame.SILENCE, AudioFrame.SILENCE)
val connection = connect {
receiveVoice = true
audioProvider {
buffer.removeLastOrNull() ?: AudioFrame.SILENCE
buffer.removeFirstOrNull() ?: AudioFrame.SILENCE
}
}
connection.scope.launch {
Expand Down
39 changes: 20 additions & 19 deletions voice/api/voice.api
Original file line number Diff line number Diff line change
Expand Up @@ -317,7 +317,7 @@ public final class dev/kord/voice/SpeakingFlags$Companion {
}

public final class dev/kord/voice/VoiceConnection {
public synthetic fun <init> (Ldev/kord/voice/VoiceConnectionData;Ldev/kord/gateway/Gateway;Ldev/kord/voice/gateway/VoiceGateway;Ldev/kord/voice/udp/VoiceUdpSocket;Ldev/kord/voice/gateway/VoiceGatewayConfiguration;Ldev/kord/voice/streams/Streams;Ldev/kord/voice/AudioProvider;Ldev/kord/voice/FrameInterceptor;Ldev/kord/voice/udp/AudioFrameSender;Ldev/kord/voice/encryption/strategies/NonceStrategy;JLkotlin/jvm/internal/DefaultConstructorMarker;)V
public synthetic fun <init> (Lkotlinx/coroutines/CoroutineScope;Ldev/kord/voice/VoiceConnectionData;Ldev/kord/gateway/Gateway;Ldev/kord/voice/gateway/VoiceGateway;Ldev/kord/voice/udp/VoiceUdpSocket;Ldev/kord/voice/gateway/VoiceGatewayConfiguration;Ldev/kord/voice/streams/Streams;Ldev/kord/voice/AudioProvider;Ldev/kord/voice/FrameInterceptor;Ldev/kord/voice/udp/AudioFrameSender;Ldev/kord/voice/encryption/strategies/NonceStrategy;JLkotlin/jvm/internal/DefaultConstructorMarker;)V
public final fun connect (Lkotlinx/coroutines/CoroutineScope;Lkotlin/coroutines/Continuation;)Ljava/lang/Object;
public static synthetic fun connect$default (Ldev/kord/voice/VoiceConnection;Lkotlinx/coroutines/CoroutineScope;Lkotlin/coroutines/Continuation;ILjava/lang/Object;)Ljava/lang/Object;
public final fun disconnect (Lkotlin/coroutines/Continuation;)Ljava/lang/Object;
Expand Down Expand Up @@ -348,6 +348,7 @@ public final class dev/kord/voice/VoiceConnectionBuilder {
public final fun getAudioSender ()Ldev/kord/voice/udp/AudioFrameSender;
public final fun getChannelId ()Ldev/kord/common/entity/Snowflake;
public final fun getConnectionDetachDuration-UwyO8pc ()J
public final fun getDispatcher ()Lkotlinx/coroutines/CoroutineDispatcher;
public final fun getFrameInterceptor ()Ldev/kord/voice/FrameInterceptor;
public final fun getGateway ()Ldev/kord/gateway/Gateway;
public final fun getGuildId ()Ldev/kord/common/entity/Snowflake;
Expand All @@ -363,6 +364,7 @@ public final class dev/kord/voice/VoiceConnectionBuilder {
public final fun setAudioSender (Ldev/kord/voice/udp/AudioFrameSender;)V
public final fun setChannelId (Ldev/kord/common/entity/Snowflake;)V
public final fun setConnectionDetachDuration-LRDsOJo (J)V
public final fun setDispatcher (Lkotlinx/coroutines/CoroutineDispatcher;)V
public final fun setFrameInterceptor (Ldev/kord/voice/FrameInterceptor;)V
public final fun setGateway (Ldev/kord/gateway/Gateway;)V
public final fun setGuildId (Ldev/kord/common/entity/Snowflake;)V
Expand Down Expand Up @@ -1068,7 +1070,7 @@ public final class dev/kord/voice/streams/DefaultStreams : dev/kord/voice/stream
public synthetic fun getIncomingUserStreams ()Lkotlinx/coroutines/flow/Flow;
public fun getIncomingUserStreams ()Lkotlinx/coroutines/flow/SharedFlow;
public fun getSsrcToUser ()Ljava/util/Map;
public fun listen ([BLio/ktor/network/sockets/SocketAddress;Lkotlin/coroutines/Continuation;)Ljava/lang/Object;
public fun listen ([BLio/ktor/network/sockets/InetSocketAddress;Lkotlin/coroutines/Continuation;)Ljava/lang/Object;
}

public final class dev/kord/voice/streams/NOPStreams : dev/kord/voice/streams/Streams {
Expand All @@ -1077,33 +1079,33 @@ public final class dev/kord/voice/streams/NOPStreams : dev/kord/voice/streams/St
public fun getIncomingAudioPackets ()Lkotlinx/coroutines/flow/Flow;
public fun getIncomingUserStreams ()Lkotlinx/coroutines/flow/Flow;
public fun getSsrcToUser ()Ljava/util/Map;
public fun listen ([BLio/ktor/network/sockets/SocketAddress;Lkotlin/coroutines/Continuation;)Ljava/lang/Object;
public fun listen ([BLio/ktor/network/sockets/InetSocketAddress;Lkotlin/coroutines/Continuation;)Ljava/lang/Object;
}

public abstract interface class dev/kord/voice/streams/Streams {
public abstract fun getIncomingAudioFrames ()Lkotlinx/coroutines/flow/Flow;
public abstract fun getIncomingAudioPackets ()Lkotlinx/coroutines/flow/Flow;
public abstract fun getIncomingUserStreams ()Lkotlinx/coroutines/flow/Flow;
public abstract fun getSsrcToUser ()Ljava/util/Map;
public abstract fun listen ([BLio/ktor/network/sockets/SocketAddress;Lkotlin/coroutines/Continuation;)Ljava/lang/Object;
public abstract fun listen ([BLio/ktor/network/sockets/InetSocketAddress;Lkotlin/coroutines/Continuation;)Ljava/lang/Object;
}

public abstract interface class dev/kord/voice/udp/AudioFrameSender {
public abstract fun start (Ldev/kord/voice/udp/AudioFrameSenderConfiguration;Lkotlin/coroutines/Continuation;)Ljava/lang/Object;
}

public final class dev/kord/voice/udp/AudioFrameSenderConfiguration {
public synthetic fun <init> (Lio/ktor/network/sockets/SocketAddress;I[BLdev/kord/voice/FrameInterceptorConfiguration;Lkotlin/jvm/internal/DefaultConstructorMarker;)V
public final fun component1 ()Lio/ktor/network/sockets/SocketAddress;
public synthetic fun <init> (Lio/ktor/network/sockets/InetSocketAddress;I[BLdev/kord/voice/FrameInterceptorConfiguration;Lkotlin/jvm/internal/DefaultConstructorMarker;)V
public final fun component1 ()Lio/ktor/network/sockets/InetSocketAddress;
public final fun component2-pVg5ArA ()I
public final fun component3 ()[B
public final fun component4 ()Ldev/kord/voice/FrameInterceptorConfiguration;
public final fun copy-Yuhug_o (Lio/ktor/network/sockets/SocketAddress;I[BLdev/kord/voice/FrameInterceptorConfiguration;)Ldev/kord/voice/udp/AudioFrameSenderConfiguration;
public static synthetic fun copy-Yuhug_o$default (Ldev/kord/voice/udp/AudioFrameSenderConfiguration;Lio/ktor/network/sockets/SocketAddress;I[BLdev/kord/voice/FrameInterceptorConfiguration;ILjava/lang/Object;)Ldev/kord/voice/udp/AudioFrameSenderConfiguration;
public final fun copy-Yuhug_o (Lio/ktor/network/sockets/InetSocketAddress;I[BLdev/kord/voice/FrameInterceptorConfiguration;)Ldev/kord/voice/udp/AudioFrameSenderConfiguration;
public static synthetic fun copy-Yuhug_o$default (Ldev/kord/voice/udp/AudioFrameSenderConfiguration;Lio/ktor/network/sockets/InetSocketAddress;I[BLdev/kord/voice/FrameInterceptorConfiguration;ILjava/lang/Object;)Ldev/kord/voice/udp/AudioFrameSenderConfiguration;
public fun equals (Ljava/lang/Object;)Z
public final fun getInterceptorConfiguration ()Ldev/kord/voice/FrameInterceptorConfiguration;
public final fun getKey ()[B
public final fun getServer ()Lio/ktor/network/sockets/SocketAddress;
public final fun getServer ()Lio/ktor/network/sockets/InetSocketAddress;
public final fun getSsrc-pVg5ArA ()I
public fun hashCode ()I
public fun toString ()Ljava/lang/String;
Expand Down Expand Up @@ -1148,12 +1150,8 @@ public final class dev/kord/voice/udp/DefaultJvmAudioPacketProvider : dev/kord/v
public fun provide-jfaDVJw (SII[B)Ldev/kord/voice/io/ByteArrayView;
}

public final class dev/kord/voice/udp/GlobalVoiceUdpSocket : dev/kord/voice/udp/VoiceUdpSocket {
public static final field INSTANCE Ldev/kord/voice/udp/GlobalVoiceUdpSocket;
public fun discoverIp (Lio/ktor/network/sockets/InetSocketAddress;ILkotlin/coroutines/Continuation;)Ljava/lang/Object;
public fun getIncoming ()Lkotlinx/coroutines/flow/SharedFlow;
public fun send (Lio/ktor/network/sockets/Datagram;Lkotlin/coroutines/Continuation;)Ljava/lang/Object;
public fun stop (Lkotlin/coroutines/Continuation;)Ljava/lang/Object;
public final class dev/kord/voice/udp/IpDiscoveryKt {
public static final fun discoverIP (Ldev/kord/voice/udp/VoiceUdpSocket;Lio/ktor/network/sockets/InetSocketAddress;ILkotlin/coroutines/Continuation;)Ljava/lang/Object;
}

public abstract class dev/kord/voice/udp/PayloadType {
Expand Down Expand Up @@ -1246,9 +1244,8 @@ public final class dev/kord/voice/udp/RTPPacketKt {

public abstract interface class dev/kord/voice/udp/VoiceUdpSocket {
public static final field Companion Ldev/kord/voice/udp/VoiceUdpSocket$Companion;
public abstract fun discoverIp (Lio/ktor/network/sockets/InetSocketAddress;ILkotlin/coroutines/Continuation;)Ljava/lang/Object;
public abstract fun getIncoming ()Lkotlinx/coroutines/flow/SharedFlow;
public abstract fun send (Lio/ktor/network/sockets/Datagram;Lkotlin/coroutines/Continuation;)Ljava/lang/Object;
public abstract fun all (Lio/ktor/network/sockets/InetSocketAddress;)Lkotlinx/coroutines/flow/Flow;
public abstract fun send (Lio/ktor/network/sockets/InetSocketAddress;Ldev/kord/voice/io/ByteArrayView;Lkotlin/coroutines/Continuation;)Ljava/lang/Object;
public abstract fun stop (Lkotlin/coroutines/Continuation;)Ljava/lang/Object;
}

Expand All @@ -1257,6 +1254,10 @@ public final class dev/kord/voice/udp/VoiceUdpSocket$Companion {
}

public final class dev/kord/voice/udp/VoiceUdpSocketKt {
public static final fun receiveFrom (Ldev/kord/voice/udp/VoiceUdpSocket;Lio/ktor/network/sockets/InetSocketAddress;Lkotlin/coroutines/Continuation;)Ljava/lang/Object;
public static final fun recv (Ldev/kord/voice/udp/VoiceUdpSocket;Lio/ktor/network/sockets/InetSocketAddress;Lkotlin/coroutines/Continuation;)Ljava/lang/Object;
}

public final class dev/kord/voice/udp/VoiceUdpSocket_nonJsKt {
public static final fun getGlobalVoiceUdpSocket ()Ldev/kord/voice/udp/VoiceUdpSocket;
}

36 changes: 19 additions & 17 deletions voice/build.gradle.kts
Original file line number Diff line number Diff line change
Expand Up @@ -5,31 +5,33 @@ plugins {

kotlin {
jvm {
withJava()
withJava()
}

sourceSets {
commonMain {
dependencies {
api(projects.common)
api(projects.gateway)
commonMain.dependencies {
api(projects.common)
api(projects.gateway)

api(libs.ktor.network)
implementation(libs.kotlin.logging)
implementation(libs.kotlin.logging)

compileOnly(projects.kspAnnotations)
}
compileOnly(projects.kspAnnotations)
}

nonJvmMain {
dependencies {
implementation(libs.libsodium)
}
nonJsMain.dependencies {
api(libs.ktor.network)
}
jvmMain {
dependencies {
implementation(libs.slf4j.api)
}

jsMain.dependencies {
implementation(libs.kotlin.node)
}

nonJvmMain.dependencies {
implementation(libs.libsodium)
}

jvmMain.dependencies {
implementation(libs.slf4j.api)
}
}
}
4 changes: 1 addition & 3 deletions voice/src/commonMain/kotlin/VoiceConnection.kt
Original file line number Diff line number Diff line change
Expand Up @@ -45,6 +45,7 @@ public data class VoiceConnectionData(
*/
@KordVoice
public class VoiceConnection(
public val scope: CoroutineScope,
public val data: VoiceConnectionData,
public val gateway: Gateway,
public val voiceGateway: VoiceGateway,
Expand All @@ -57,9 +58,6 @@ public class VoiceConnection(
public val nonceStrategy: NonceStrategy,
connectionDetachDuration: Duration
) {
public val scope: CoroutineScope =
CoroutineScope(SupervisorJob() + CoroutineName("kord-voice-connection[${data.guildId.value}]"))

init {
with(scope) {
launch { VoiceUpdateEventHandler(gateway.events, connectionDetachDuration, this@VoiceConnection).start() }
Expand Down
Loading

0 comments on commit 35227fc

Please sign in to comment.