diff --git a/.github/workflows/deployment-ci.yml b/.github/workflows/deployment-ci.yml deleted file mode 100644 index 904edc4dacf..00000000000 --- a/.github/workflows/deployment-ci.yml +++ /dev/null @@ -1,63 +0,0 @@ -# This workflow will build a package using Gradle and then publish it to maven - -name: Kotlin CI - -on: - push: - branches: - - '**' # We want to run this on all branch pushes - tags-ignore: - - '**' # We don't want this to run on tags pushes - pull_request: - release: - types: [ published ] - -jobs: - build: - name: Build Kord - runs-on: ubuntu-latest - if: | - !contains(github.event.head_commit.message, '[ci skip]') && !contains(github.event.pull_request.title, '[ci skip]') - && !(github.event_name == 'pull_request' && github.event.pull_request.head.repo.full_name == 'kordlib/kord') - env: - KORD_TEST_TOKEN: ${{ secrets.KORD_TEST_TOKEN }} - steps: - - uses: actions/checkout@v4 - - name: Set up JDK - uses: actions/setup-java@v4 - with: - distribution: temurin - java-version: 21 - - name: Set up Gradle - uses: gradle/actions/setup-gradle@v4 - - name: Build with Gradle - run: ./gradlew --stacktrace --info build - release: - name: Publish artifacts - runs-on: ubuntu-latest - needs: build - if: | - !contains(github.event.head_commit.message, '[publish skip]') && github.event_name != 'pull_request' && github.ref != 'refs/heads/master' - env: - KORD_TEST_TOKEN: ${{ secrets.KORD_TEST_TOKEN }} - NEXUS_USER: ${{ secrets.NEXUS_USER }} - NEXUS_PASSWORD: ${{ secrets.NEXUS_PASSWORD }} - KORD_REPO_USER: ${{ secrets.KORD_REPO_USER }} - KORD_REPO_PASSWORD: ${{ secrets.KORD_REPO_PASSWORD }} - SIGNING_KEY: ${{ secrets.signingKey }} - SIGNING_PASSWORD: ${{ secrets.signingPassword }} - steps: - - uses: actions/checkout@v4 - with: - fetch-depth: 0 - - name: Set up JDK - uses: actions/setup-java@v4 - with: - distribution: temurin - java-version: 21 - - name: Set up Gradle - uses: gradle/actions/setup-gradle@v4 - - name: Build with Gradle - run: ./gradlew --stacktrace --info build - - name: Publish with Gradle - run: ./gradlew --no-parallel -x test publish diff --git a/.github/workflows/docs-ci.yml b/.github/workflows/docs-ci.yml index 82f8b56600d..9c754879ba5 100644 --- a/.github/workflows/docs-ci.yml +++ b/.github/workflows/docs-ci.yml @@ -1,38 +1,12 @@ -# This workflow will build and deploy docs to GitHub Pages whenever something is pushed to the main branch - -name: Docs - +name: Documentation CI on: push: - branches: - - main + pull_request: permissions: - contents: write + id-token: write + pages: write jobs: docs: - name: Build and deploy docs - runs-on: ubuntu-latest - concurrency: # Allow one concurrent deployment - group: pages - cancel-in-progress: true - steps: - - uses: actions/checkout@v4 - - name: Set up JDK - uses: actions/setup-java@v4 - with: - distribution: temurin - java-version: 21 - - name: Set up Gradle - uses: gradle/actions/setup-gradle@v4 - - name: Build docs with Gradle - run: ./gradlew --stacktrace --info :dokkaGeneratePublicationHtml - - name: Deploy docs to GitHub Pages - uses: JamesIves/github-pages-deploy-action@v4 - with: - folder: build/dokka/html - branch: gh-pages - git-config-name: GitHub Actions - git-config-email: actions@github.com - commit-message: Update docs + uses: kordlib/.teamcity/.github/workflows/documentation.yml@main diff --git a/README.md b/README.md index e19a99d4636..f20a05979d1 100644 --- a/README.md +++ b/README.md @@ -37,18 +37,17 @@ mapping of the Voice API on Kotlin/JVM ## Modules -| Module | Docs | Artifact | JVM | JS | Native² | -|--------------------------|---------------------------------------------------------|-------------------|-----|----|---------| -| [common](common) | [common](https://kordlib.github.io/kord/common) | `kord-common`¹ | ✅ | ✅ | ❌ | -| [rest](rest) | [rest](https://kordlib.github.io/kord/rest) | `kord-rest`¹ | ✅ | ✅ | ❌ | -| [gateway](gateway) | [gateway](https://kordlib.github.io/kord/gateway) | `kord-gateway`¹ | ✅ | ✅ | ❌ | -| [core](core) | [core](https://kordlib.github.io/kord/core) | `kord-core`¹ | ✅ | ✅ | ❌ | -| [voice](voice) | [voice](https://kordlib.github.io/kord/voice) | `kord-voice` | ✅ | ❌³ | ❌ | -| [core-voice](core-voice) | [core-voice](https://kordlib.github.io/kord/core-voice) | `kord-core-voice` | ✅ | ❌ | ❌ | +| Module | Docs | Artifact | JVM | JS (NodeJS) | Native | +|--------------------------|---------------------------------------------------------|--------------------|-----|-------------|--------| +| [common](common) | [common](https://kordlib.github.io/kord/common) | `kord-common`¹ | ✅ | ✅ | ✅ | +| [rest](rest) | [rest](https://kordlib.github.io/kord/rest) | `kord-rest`¹ | ✅ | ✅ | ✅ | +| [gateway](gateway) | [gateway](https://kordlib.github.io/kord/gateway) | `kord-gateway`¹ | ✅ | ✅ | ✅ | +| [core](core) | [core](https://kordlib.github.io/kord/core) | `kord-core`¹ | ✅ | ✅ | ✅ | +| [voice](voice) | [voice](https://kordlib.github.io/kord/voice) | `kord-voice`¹ | ✅ | ✅ | ✅² | +| [core-voice](core-voice) | [core-voice](https://kordlib.github.io/kord/core-voice) | `kord-core-voice`¹ | ✅ | ✅ | ✅² | ¹ These artifacts only supports Gradle Version 5.3 or higher, for older Gradle versions and Maven please append `-jvm` -² For Native Support please see #69 -³ For Voice JS please see #69 +² Currently not on Windows(MinGW) targets, see #69 ## Installation diff --git a/bom/build.gradle.kts b/bom/build.gradle.kts index 534d252b314..fb32d631c76 100644 --- a/bom/build.gradle.kts +++ b/bom/build.gradle.kts @@ -1,6 +1,6 @@ plugins { `java-platform` - `maven-publish` + `kord-publishing` } val me = project @@ -31,5 +31,3 @@ publishing { from(components["javaPlatform"]) } } - -apply(plugin = "kord-publishing") diff --git a/build.gradle.kts b/build.gradle.kts index 52f11df7127..5636678b687 100644 --- a/build.gradle.kts +++ b/build.gradle.kts @@ -2,12 +2,16 @@ plugins { org.jetbrains.dokka // for dokkaGeneratePublicationHtml task } -repositories { - mavenCentral() +allprojects { + repositories { + // TODO: Remove wants https://github.com/ktorio/ktor/pull/3950 lands + maven("https://europe-west3-maven.pkg.dev/mik-music/kord") + mavenCentral() + maven("https://oss.sonatype.org/content/repositories/snapshots/") + } } group = Library.group -version = libraryVersion dependencies { dokka(projects.common) diff --git a/buildSrc/build.gradle.kts b/buildSrc/build.gradle.kts index 260920e5258..d6abfed304d 100644 --- a/buildSrc/build.gradle.kts +++ b/buildSrc/build.gradle.kts @@ -3,7 +3,10 @@ plugins { } repositories { + gradlePluginPortal() mavenCentral() + // Repo providing the Kord Gradle plugin + maven("https://europe-west3-maven.pkg.dev/mik-music/kord") } kotlin { diff --git a/buildSrc/src/main/kotlin/Compiler.kt b/buildSrc/src/main/kotlin/Compiler.kt index 6caa6484fc3..f767ee0cbca 100644 --- a/buildSrc/src/main/kotlin/Compiler.kt +++ b/buildSrc/src/main/kotlin/Compiler.kt @@ -14,13 +14,13 @@ val kordOptIns = listOf( "dev.kord.common.annotation.KordVoice", ) -internal fun KotlinCommonCompilerOptions.applyKordCommonCompilerOptions() { +fun KotlinCommonCompilerOptions.applyKordCommonCompilerOptions() { allWarningsAsErrors = true progressiveMode = true freeCompilerArgs.add("-Xexpect-actual-classes") } -internal const val KORD_JVM_TARGET = 8 +const val KORD_JVM_TARGET = 8 internal fun KotlinJvmCompilerOptions.applyKordJvmCompilerOptions() { applyKordCommonCompilerOptions() diff --git a/buildSrc/src/main/kotlin/Documentation.kt b/buildSrc/src/main/kotlin/Documentation.kt index 6b3a1801d7e..b0adaa00097 100644 --- a/buildSrc/src/main/kotlin/Documentation.kt +++ b/buildSrc/src/main/kotlin/Documentation.kt @@ -1,6 +1,7 @@ +import dev.kord.gradle.tools.util.commitHash import org.gradle.api.Project import org.gradle.kotlin.dsl.assign -import org.gradle.kotlin.dsl.invoke +import java.net.URI import org.jetbrains.dokka.gradle.DokkaExtension import org.jetbrains.dokka.gradle.workers.ProcessIsolation @@ -27,7 +28,7 @@ internal fun DokkaExtension.applyKordDokkaOptions(project: Project) { remoteLineSuffix = "#L" } - externalDocumentationLinks { + externalDocumentationLinks.apply { register("kotlinx.coroutines") { url("https://kotlinlang.org/api/kotlinx.coroutines/") } diff --git a/buildSrc/src/main/kotlin/Git.kt b/buildSrc/src/main/kotlin/Git.kt deleted file mode 100644 index 321fe3fbbc1..00000000000 --- a/buildSrc/src/main/kotlin/Git.kt +++ /dev/null @@ -1,13 +0,0 @@ -import org.gradle.api.Project -import java.io.ByteArrayOutputStream - -internal fun Project.git(vararg command: String): String { - val output = ByteArrayOutputStream() - exec { - commandLine("git", *command) - standardOutput = output - errorOutput = output - workingDir = rootDir - }.rethrowFailure().assertNormalExitValue() - return output.toString().trim() -} diff --git a/buildSrc/src/main/kotlin/Projects.kt b/buildSrc/src/main/kotlin/Projects.kt index be8c41f12ee..3ffba637fce 100644 --- a/buildSrc/src/main/kotlin/Projects.kt +++ b/buildSrc/src/main/kotlin/Projects.kt @@ -1,33 +1,6 @@ -import org.gradle.api.Project - object Library { const val name = "kord" const val group = "dev.kord" const val description = "Idiomatic Kotlin Wrapper for The Discord API" const val projectUrl = "https://github.com/kordlib/kord" } - -private val Project.tag - get() = git("tag", "--no-column", "--points-at", "HEAD") - .takeIf { it.isNotBlank() } - ?.lines() - ?.single() - -val Project.libraryVersion - get() = tag ?: run { - val snapshotPrefix = when (val branch = git("branch", "--show-current")) { - "main" -> providers.gradleProperty("nextPlannedVersion").get() - else -> branch.replace('/', '-') - } - "$snapshotPrefix-SNAPSHOT" - } - -val Project.commitHash get() = git("rev-parse", "--verify", "HEAD") -val Project.shortCommitHash get() = git("rev-parse", "--short", "HEAD") - -val Project.isRelease get() = tag != null - -object Repo { - const val releasesUrl = "https://oss.sonatype.org/service/local/staging/deploy/maven2/" - const val snapshotsUrl = "https://oss.sonatype.org/content/repositories/snapshots/" -} diff --git a/buildSrc/src/main/kotlin/Targets.kt b/buildSrc/src/main/kotlin/Targets.kt new file mode 100644 index 00000000000..f804f3e7acf --- /dev/null +++ b/buildSrc/src/main/kotlin/Targets.kt @@ -0,0 +1,58 @@ +import org.gradle.api.tasks.TaskContainer +import org.jetbrains.kotlin.gradle.ExperimentalKotlinGradlePluginApi +import org.jetbrains.kotlin.gradle.dsl.KotlinMultiplatformExtension +import org.jetbrains.kotlin.konan.target.HostManager + +@OptIn(ExperimentalKotlinGradlePluginApi::class) +fun KotlinMultiplatformExtension.targets() { + jvm { + compilerOptions { + applyKordJvmCompilerOptions() + } + } + + 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() + } + + linuxX64() + linuxArm64() + + mingwX64() + + macosArm64() + macosX64() + + iosArm64() + iosX64() + iosSimulatorArm64() + + watchosArm64() + watchosSimulatorArm64() + + tvosX64() + tvosArm64() + tvosSimulatorArm64() +} + +// There are issues with linking the linux variant on windows. +// Please use WSL if you need to work on the linux port. +fun TaskContainer.disableLinuxLinkTestTasksOnWindows() { + if (HostManager.hostIsMingw) { + val linuxLinkTestTasks = listOf("linkDebugTestLinuxX64", "linkDebugTestLinuxArm64") + for (task in linuxLinkTestTasks) { + named(task) { + enabled = false + } + } + } +} diff --git a/buildSrc/src/main/kotlin/kord-internal-module.gradle.kts b/buildSrc/src/main/kotlin/kord-internal-module.gradle.kts index ad9b04bb4d1..30df3969765 100644 --- a/buildSrc/src/main/kotlin/kord-internal-module.gradle.kts +++ b/buildSrc/src/main/kotlin/kord-internal-module.gradle.kts @@ -1,5 +1,6 @@ plugins { org.jetbrains.kotlin.jvm + dev.kord.`gradle-tools` } repositories { diff --git a/buildSrc/src/main/kotlin/kord-internal-multiplatform-module.gradle.kts b/buildSrc/src/main/kotlin/kord-internal-multiplatform-module.gradle.kts deleted file mode 100644 index 076b387f80e..00000000000 --- a/buildSrc/src/main/kotlin/kord-internal-multiplatform-module.gradle.kts +++ /dev/null @@ -1,32 +0,0 @@ -import org.jetbrains.kotlin.gradle.ExperimentalKotlinGradlePluginApi - -plugins { - org.jetbrains.kotlin.multiplatform -} - -repositories { - mavenCentral() -} - -@OptIn(ExperimentalKotlinGradlePluginApi::class) -kotlin { - compilerOptions { - applyKordCommonCompilerOptions() - } - - jvm { - compilerOptions { - applyKordJvmCompilerOptions() - } - } - js { - nodejs() - useCommonJs() - } -} - -tasks { - withType().configureEach { - useJUnitPlatform() - } -} diff --git a/buildSrc/src/main/kotlin/kord-module.gradle.kts b/buildSrc/src/main/kotlin/kord-module.gradle.kts deleted file mode 100644 index 37397688ec0..00000000000 --- a/buildSrc/src/main/kotlin/kord-module.gradle.kts +++ /dev/null @@ -1,54 +0,0 @@ -plugins { - org.jetbrains.kotlin.jvm - org.jetbrains.kotlin.plugin.serialization - org.jetbrains.dokka - org.jetbrains.kotlinx.atomicfu - org.jetbrains.kotlinx.`binary-compatibility-validator` - com.google.devtools.ksp - `maven-publish` -} - -repositories { - mavenCentral() -} - -dependencies { - ksp(project(":ksp-processors")) -} - -apiValidation { - applyKordBCVOptions() -} - -kotlin { - explicitApi() - compilerOptions { - applyKordJvmCompilerOptions() - optIn.addAll(kordOptIns) - } - - sourceSets { - applyKordTestOptIns() - } -} - -dokka { - applyKordDokkaOptions(project) -} - -tasks { - withType().configureEach { - useJUnitPlatform() - } - - withType().configureEach { - options.release = KORD_JVM_TARGET - } -} - -publishing { - publications.register(Library.name) { - from(components["java"]) - artifact(tasks.kotlinSourcesJar) - } -} diff --git a/buildSrc/src/main/kotlin/kord-multiplatform-module.gradle.kts b/buildSrc/src/main/kotlin/kord-multiplatform-module.gradle.kts index db9f9064e8f..a3414422aa8 100644 --- a/buildSrc/src/main/kotlin/kord-multiplatform-module.gradle.kts +++ b/buildSrc/src/main/kotlin/kord-multiplatform-module.gradle.kts @@ -1,5 +1,7 @@ +import org.jetbrains.dokka.gradle.AbstractDokkaLeafTask import org.jetbrains.kotlin.gradle.ExperimentalKotlinGradlePluginApi import org.jetbrains.kotlin.gradle.targets.js.testing.KotlinJsTest +import org.jetbrains.kotlin.gradle.targets.native.tasks.KotlinNativeTest plugins { org.jetbrains.kotlin.multiplatform @@ -24,31 +26,26 @@ apiValidation { @OptIn(ExperimentalKotlinGradlePluginApi::class) kotlin { - explicitApi() - compilerOptions { - applyKordCommonCompilerOptions() - optIn.addAll(kordOptIns) - } + applyDefaultHierarchyTemplate { + common { + group("nonJvm") { + withNative() + withJs() + } - jvm { - compilerOptions { - applyKordJvmCompilerOptions() - } - } - 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" - } + group("nonJs") { + withNative() + withJvm() } } - useCommonJs() } - applyDefaultHierarchyTemplate() + targets() + explicitApi() + compilerOptions { + optIn.addAll(kordOptIns) + applyKordCommonCompilerOptions() + } sourceSets { applyKordTestOptIns() @@ -61,12 +58,6 @@ kotlin { implementation(project(":test-kit")) } } - val nonJvmMain by creating { - dependsOn(commonMain.get()) - } - jsMain { - dependsOn(nonJvmMain) - } } } @@ -83,14 +74,22 @@ tasks { environment("PROJECT_ROOT", rootProject.projectDir.absolutePath) } + withType().configureEach { + environment("PROJECT_ROOT", rootProject.projectDir.absolutePath) + } + + withType().configureEach { + options.release = KORD_JVM_TARGET + } + + val compilationTasks = kotlin.targets.flatMap { + listOf("compileKotlin${it.name.replaceFirstChar(Char::titlecase)}", "${it.name}SourcesJar") + } + for (task in listOf( - "compileKotlinJvm", - "compileKotlinJs", - "jvmSourcesJar", - "jsSourcesJar", "dokkaGenerateModuleHtml", "dokkaGeneratePublicationHtml", - )) { + ) + compilationTasks) { named(task) { dependsOn("kspCommonMainKotlinMetadata") } @@ -101,4 +100,6 @@ tasks { dependsOn("kspCommonMainKotlinMetadata") } } + + disableLinuxLinkTestTasksOnWindows() } diff --git a/buildSrc/src/main/kotlin/kord-publishing.gradle.kts b/buildSrc/src/main/kotlin/kord-publishing.gradle.kts index 0d3904bd282..7b10ed4302e 100644 --- a/buildSrc/src/main/kotlin/kord-publishing.gradle.kts +++ b/buildSrc/src/main/kotlin/kord-publishing.gradle.kts @@ -1,9 +1,10 @@ +import org.jetbrains.kotlin.konan.target.Family import java.lang.System.getenv import java.util.Base64 plugins { - `maven-publish` - signing + id("com.vanniktech.maven.publish.base") + dev.kord.`gradle-tools` } fun MavenPublication.registerDokkaJar() = @@ -13,63 +14,57 @@ fun MavenPublication.registerDokkaJar() = from(tasks.named("dokkaGeneratePublicationHtml")) } -publishing { - publications { - withType().configureEach { - if (project.name != "bom") artifact(registerDokkaJar()) - - groupId = Library.group - artifactId = "kord-$artifactId" - version = libraryVersion +kord { + publicationName = "mavenCentral" + metadataHost = Family.OSX +} - pom { - name = Library.name - description = Library.description - url = Library.projectUrl +mavenPublishing { + coordinates(Library.group, "kord-${project.name}") + publishToMavenCentral() + signAllPublications() - organization { - name = "Kord" - url = "https://github.com/kordlib" - } - - developers { - developer { - name = "The Kord Team" - } - } + if (plugins.hasPlugin("org.jetbrains.kotlin.multiplatform")) { +// configure(KotlinMultipla(javadocJar = JavadocJar.Dokka("dokkaHtml"))) + } - issueManagement { - system = "GitHub" - url = "https://github.com/kordlib/kord/issues" - } + pom { + name = Library.name + description = Library.description + url = Library.projectUrl - licenses { - license { - name = "MIT" - url = "https://opensource.org/licenses/MIT" - } - } + organization { + name = "Kord" + url = "https://github.com/kordlib" + } - scm { - connection = "scm:git:ssh://github.com/kordlib/kord.git" - developerConnection = "scm:git:ssh://git@github.com:kordlib/kord.git" - url = Library.projectUrl - } + developers { + developer { + name = "The Kord Team" } } - } - repositories { - maven { - url = uri(if (isRelease) Repo.releasesUrl else Repo.snapshotsUrl) + issueManagement { + system = "GitHub" + url = "https://github.com/kordlib/kord/issues" + } - credentials { - username = getenv("NEXUS_USER") - password = getenv("NEXUS_PASSWORD") + licenses { + license { + name = "MIT" + url = "https://opensource.org/licenses/MIT" } } - if (!isRelease) { + scm { + connection = "scm:git:ssh://github.com/kordlib/kord.git" + developerConnection = "scm:git:ssh://git@github.com:kordlib/kord.git" + url = Library.projectUrl + } + } + + repositories { + if (true) { maven { name = "kordSnapshots" url = uri("https://repo.kord.dev/snapshots") @@ -81,10 +76,3 @@ publishing { } } } - -signing { - val secretKey = getenv("SIGNING_KEY")?.let { String(Base64.getDecoder().decode(it)) } - val password = getenv("SIGNING_PASSWORD") - useInMemoryPgpKeys(secretKey, password) - sign(publishing.publications) -} diff --git a/common/api/common.klib.api b/common/api/common.klib.api index add796aee52..43ec91271f9 100644 --- a/common/api/common.klib.api +++ b/common/api/common.klib.api @@ -1,5 +1,5 @@ // Klib ABI Dump -// Targets: [js] +// Targets: [iosArm64, iosSimulatorArm64, iosX64, js, linuxArm64, linuxX64, macosArm64, macosX64, mingwX64, tvosArm64, tvosSimulatorArm64, tvosX64, watchosArm64, watchosSimulatorArm64] // Rendering settings: // - Signature version: 2 // - Show manifest properties: true diff --git a/common/build.gradle.kts b/common/build.gradle.kts index 5098d607e20..c46971ecef3 100644 --- a/common/build.gradle.kts +++ b/common/build.gradle.kts @@ -1,3 +1,6 @@ +import dev.kord.gradle.tools.util.commitHash +import dev.kord.gradle.tools.util.shortCommitHash + plugins { `kord-multiplatform-module` `kord-publishing` @@ -37,6 +40,27 @@ kotlin { implementation(projects.kspAnnotations) } } + nativeMain { + dependencies { + // Native does not have compileOnly + implementation(projects.kspAnnotations) + } + } + mingwMain { + dependencies { + api(libs.ktor.client.winhttp) + } + } + appleMain { + dependencies { + api(libs.ktor.client.darwin) + } + } + linuxMain { + dependencies { + api(libs.ktor.client.curl) + } + } jvmTest { dependencies { implementation(libs.kbson) @@ -63,7 +87,7 @@ buildConfig { internalVisibility = true } - buildConfigField("BUILD_CONFIG_GENERATED_LIBRARY_VERSION", libraryVersion) + buildConfigField("BUILD_CONFIG_GENERATED_LIBRARY_VERSION", provider { project.version.toString() }) buildConfigField("BUILD_CONFIG_GENERATED_COMMIT_HASH", commitHash) buildConfigField("BUILD_CONFIG_GENERATED_SHORT_COMMIT_HASH", shortCommitHash) } diff --git a/common/src/appleMain/kotlin/HttpEngine.kt b/common/src/appleMain/kotlin/HttpEngine.kt new file mode 100644 index 00000000000..945d0d5fb1c --- /dev/null +++ b/common/src/appleMain/kotlin/HttpEngine.kt @@ -0,0 +1,9 @@ +package dev.kord.common.http + +import dev.kord.common.annotation.KordInternal +import io.ktor.client.engine.* +import io.ktor.client.engine.darwin.* + +/** @suppress */ +@KordInternal +public actual fun httpEngine(): HttpClientEngineFactory = Darwin diff --git a/common/src/commonTest/kotlin/json/ChannelTest.kt b/common/src/commonTest/kotlin/json/ChannelTest.kt index 9851007c24d..5a0da21c1b1 100644 --- a/common/src/commonTest/kotlin/json/ChannelTest.kt +++ b/common/src/commonTest/kotlin/json/ChannelTest.kt @@ -3,6 +3,7 @@ package dev.kord.common.json import dev.kord.common.entity.DiscordChannel import dev.kord.common.entity.optional.value import dev.kord.common.readFile +import dev.kord.test.IgnoreOnSimulatorPlatforms import kotlinx.coroutines.test.runTest import kotlinx.serialization.json.Json import kotlin.js.JsName @@ -11,6 +12,7 @@ import kotlin.test.Test private suspend fun file(name: String): String = readFile("channel", name) +@IgnoreOnSimulatorPlatforms class ChannelTest { @Test diff --git a/common/src/commonTest/kotlin/json/EmojiTest.kt b/common/src/commonTest/kotlin/json/EmojiTest.kt index 7af78e1c6ef..3d6b6cd649a 100644 --- a/common/src/commonTest/kotlin/json/EmojiTest.kt +++ b/common/src/commonTest/kotlin/json/EmojiTest.kt @@ -3,6 +3,7 @@ package dev.kord.common.json import dev.kord.common.entity.DiscordEmoji import dev.kord.common.entity.Snowflake import dev.kord.common.readFile +import dev.kord.test.IgnoreOnSimulatorPlatforms import kotlinx.coroutines.test.runTest import kotlinx.serialization.json.Json import kotlin.js.JsName @@ -10,6 +11,7 @@ import kotlin.test.Test private suspend fun file(name: String): String = readFile("emoji", name) +@IgnoreOnSimulatorPlatforms class EmojiTest { @Test diff --git a/common/src/commonTest/kotlin/json/GuildTest.kt b/common/src/commonTest/kotlin/json/GuildTest.kt index 93549a01ca0..136c9e5a4d0 100644 --- a/common/src/commonTest/kotlin/json/GuildTest.kt +++ b/common/src/commonTest/kotlin/json/GuildTest.kt @@ -3,6 +3,7 @@ package dev.kord.common.json import dev.kord.common.entity.* import dev.kord.common.entity.Permission.* import dev.kord.common.readFile +import dev.kord.test.IgnoreOnSimulatorPlatforms import kotlinx.coroutines.test.runTest import kotlinx.datetime.Instant import kotlinx.serialization.json.Json @@ -12,6 +13,7 @@ import kotlin.time.Duration.Companion.seconds private suspend fun file(name: String): String = readFile("guild", name) +@IgnoreOnSimulatorPlatforms class GuildTest { @Test diff --git a/common/src/commonTest/kotlin/json/InteractionTest.kt b/common/src/commonTest/kotlin/json/InteractionTest.kt index eaf6bb6aa79..02b10983bb0 100644 --- a/common/src/commonTest/kotlin/json/InteractionTest.kt +++ b/common/src/commonTest/kotlin/json/InteractionTest.kt @@ -4,6 +4,7 @@ import dev.kord.common.entity.* import dev.kord.common.entity.Permission.* import dev.kord.common.entity.optional.orEmpty import dev.kord.common.readFile +import dev.kord.test.IgnoreOnSimulatorPlatforms import kotlinx.coroutines.test.runTest import kotlinx.serialization.json.Json import kotlin.js.JsName @@ -30,6 +31,7 @@ private val testEntitlements = listOf( ), ) +@IgnoreOnSimulatorPlatforms class InteractionTest { val json = Json { diff --git a/common/src/commonTest/kotlin/json/MessageTest.kt b/common/src/commonTest/kotlin/json/MessageTest.kt index 2b4332166de..bef454cc9e8 100644 --- a/common/src/commonTest/kotlin/json/MessageTest.kt +++ b/common/src/commonTest/kotlin/json/MessageTest.kt @@ -2,6 +2,7 @@ package dev.kord.common.json import dev.kord.common.entity.* import dev.kord.common.readFile +import dev.kord.test.IgnoreOnSimulatorPlatforms import kotlinx.coroutines.test.runTest import kotlinx.datetime.Instant import kotlinx.serialization.json.Json @@ -10,6 +11,7 @@ import kotlin.test.Test private suspend fun file(name: String): String = readFile("message", name) +@IgnoreOnSimulatorPlatforms class MessageTest { @Test diff --git a/common/src/commonTest/kotlin/json/PermissionsTest.kt b/common/src/commonTest/kotlin/json/PermissionsTest.kt index 291985a2aec..98b1b066ecc 100644 --- a/common/src/commonTest/kotlin/json/PermissionsTest.kt +++ b/common/src/commonTest/kotlin/json/PermissionsTest.kt @@ -3,6 +3,7 @@ package dev.kord.common.json import dev.kord.common.entity.DiscordRole import dev.kord.common.entity.Permission import dev.kord.common.entity.Permissions +import dev.kord.test.IgnoreOnSimulatorPlatforms import kotlinx.serialization.json.Json import kotlinx.serialization.json.buildJsonObject import kotlinx.serialization.json.put @@ -10,6 +11,7 @@ import kotlin.js.JsName import kotlin.test.Test import kotlin.test.assertEquals +@IgnoreOnSimulatorPlatforms class PermissionsTest { @Test diff --git a/common/src/commonTest/kotlin/json/UserTest.kt b/common/src/commonTest/kotlin/json/UserTest.kt index 4b1fc18877a..071cbdcaec9 100644 --- a/common/src/commonTest/kotlin/json/UserTest.kt +++ b/common/src/commonTest/kotlin/json/UserTest.kt @@ -4,6 +4,7 @@ import dev.kord.common.entity.DiscordUser import dev.kord.common.entity.UserFlag.HouseBravery import dev.kord.common.entity.UserFlags import dev.kord.common.readFile +import dev.kord.test.IgnoreOnSimulatorPlatforms import kotlinx.coroutines.test.runTest import kotlinx.serialization.json.Json import kotlin.js.JsName @@ -11,6 +12,7 @@ import kotlin.test.Test private suspend fun file(name: String): String = readFile("user", name) +@IgnoreOnSimulatorPlatforms class UserTest { @Test diff --git a/common/src/commonTest/kotlin/json/VoiceStateTest.kt b/common/src/commonTest/kotlin/json/VoiceStateTest.kt index 1d73de02faf..d3988d802a0 100644 --- a/common/src/commonTest/kotlin/json/VoiceStateTest.kt +++ b/common/src/commonTest/kotlin/json/VoiceStateTest.kt @@ -2,6 +2,7 @@ package dev.kord.common.json import dev.kord.common.entity.DiscordVoiceState import dev.kord.common.readFile +import dev.kord.test.IgnoreOnSimulatorPlatforms import kotlinx.coroutines.test.runTest import kotlinx.datetime.Instant import kotlinx.serialization.json.Json @@ -10,6 +11,7 @@ import kotlin.test.Test private suspend fun file(name: String): String = readFile("voice", name) +@IgnoreOnSimulatorPlatforms class VoiceStateTest { @Test diff --git a/common/src/linuxMain/kotlin/HttpEngine.kt b/common/src/linuxMain/kotlin/HttpEngine.kt new file mode 100644 index 00000000000..f0cdf5c7018 --- /dev/null +++ b/common/src/linuxMain/kotlin/HttpEngine.kt @@ -0,0 +1,9 @@ +package dev.kord.common.http + +import dev.kord.common.annotation.KordInternal +import io.ktor.client.engine.* +import io.ktor.client.engine.curl.* + +/** @suppress */ +@KordInternal +public actual fun httpEngine(): HttpClientEngineFactory = Curl diff --git a/common/src/mingwMain/kotlin/HttpEngine.kt b/common/src/mingwMain/kotlin/HttpEngine.kt new file mode 100644 index 00000000000..9c3eb3dc13c --- /dev/null +++ b/common/src/mingwMain/kotlin/HttpEngine.kt @@ -0,0 +1,9 @@ +package dev.kord.common.http + +import dev.kord.common.annotation.KordInternal +import io.ktor.client.engine.* +import io.ktor.client.engine.winhttp.* + +/** @suppress */ +@KordInternal +public actual fun httpEngine(): HttpClientEngineFactory = WinHttp diff --git a/common/src/nonJvmMain/kotlin/DiscordBitSet.kt b/common/src/nonJvmMain/kotlin/DiscordBitSet.kt index f1764a24a20..72ff1462dd6 100644 --- a/common/src/nonJvmMain/kotlin/DiscordBitSet.kt +++ b/common/src/nonJvmMain/kotlin/DiscordBitSet.kt @@ -2,6 +2,7 @@ package dev.kord.common import com.ionspin.kotlin.bignum.integer.BigInteger import com.ionspin.kotlin.bignum.integer.Sign +import io.ktor.utils.io.core.* internal actual fun formatIntegerFromLittleEndianLongArray(data: LongArray): String { // need to convert from little-endian data to big-endian expected by BigInteger diff --git a/core-voice/api/core-voice.klib.api b/core-voice/api/core-voice.klib.api new file mode 100644 index 00000000000..4caf54409c4 --- /dev/null +++ b/core-voice/api/core-voice.klib.api @@ -0,0 +1,9 @@ +// Klib ABI Dump +// Targets: [iosArm64, iosSimulatorArm64, iosX64, js, linuxArm64, linuxX64, macosArm64, macosX64, mingwX64, tvosArm64, tvosSimulatorArm64, tvosX64, watchosArm64, watchosSimulatorArm64] +// Rendering settings: +// - Signature version: 2 +// - Show manifest properties: true +// - Show declarations: true + +// Library unique name: +final suspend fun (dev.kord.core.behavior.channel/BaseVoiceChannelBehavior).dev.kord.core.behavior.channel/connect(kotlin/Function1): dev.kord.voice/VoiceConnection // dev.kord.core.behavior.channel/connect|connect@dev.kord.core.behavior.channel.BaseVoiceChannelBehavior(kotlin.Function1){}[0] diff --git a/core-voice/build.gradle.kts b/core-voice/build.gradle.kts index 356fca4c82b..e0aa14087fa 100644 --- a/core-voice/build.gradle.kts +++ b/core-voice/build.gradle.kts @@ -1,9 +1,15 @@ plugins { - `kord-module` + `kord-multiplatform-module` `kord-publishing` } -dependencies { - api(projects.core) - api(projects.voice) +kotlin { + sourceSets { + commonMain { + dependencies { + api(projects.core) + api(projects.voice) + } + } + } } diff --git a/core-voice/src/main/kotlin/BaseVoiceChannelBehaviorExtensions.kt b/core-voice/src/commonMain/kotlin/BaseVoiceChannelBehaviorExtensions.kt similarity index 83% rename from core-voice/src/main/kotlin/BaseVoiceChannelBehaviorExtensions.kt rename to core-voice/src/commonMain/kotlin/BaseVoiceChannelBehaviorExtensions.kt index c6da181516d..77e02a166bb 100644 --- a/core-voice/src/main/kotlin/BaseVoiceChannelBehaviorExtensions.kt +++ b/core-voice/src/commonMain/kotlin/BaseVoiceChannelBehaviorExtensions.kt @@ -7,6 +7,10 @@ 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 /** * Connect to this [VoiceChannel] and create a [VoiceConnection] for this voice session. @@ -23,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() diff --git a/core/api/core.api b/core/api/core.api index 3b4173c5603..a038d4920bb 100644 --- a/core/api/core.api +++ b/core/api/core.api @@ -104,7 +104,6 @@ public final class dev/kord/core/Kord$Companion { public final class dev/kord/core/KordKt { public static final fun Kord (Ljava/lang/String;Lkotlin/jvm/functions/Function1;Lkotlin/coroutines/Continuation;)Ljava/lang/Object; public static synthetic fun Kord$default (Ljava/lang/String;Lkotlin/jvm/functions/Function1;Lkotlin/coroutines/Continuation;ILjava/lang/Object;)Ljava/lang/Object; - public static final synthetic fun getKordLogger ()Lmu/KLogger; public static final fun logCaughtThrowable (Ljava/lang/Throwable;)V } diff --git a/core/api/core.klib.api b/core/api/core.klib.api index fea2d3745fa..ddb3c5a267e 100644 --- a/core/api/core.klib.api +++ b/core/api/core.klib.api @@ -1,5 +1,5 @@ // Klib ABI Dump -// Targets: [js] +// Targets: [iosArm64, iosSimulatorArm64, iosX64, js, linuxArm64, linuxX64, macosArm64, macosX64, mingwX64, tvosArm64, tvosSimulatorArm64, tvosX64, watchosArm64, watchosSimulatorArm64] // Rendering settings: // - Signature version: 2 // - Show manifest properties: true @@ -12330,8 +12330,6 @@ final val dev.kord.core.cache.data/id // dev.kord.core.cache.data/id|@dev.kord.c final fun (dev.kord.core.cache.data/VoiceStateData).(): kotlin/String // dev.kord.core.cache.data/id.|@dev.kord.core.cache.data.VoiceStateData(){}[0] final val dev.kord.core.entity/effectiveName // dev.kord.core.entity/effectiveName|@dev.kord.core.entity.User{}effectiveName[0] final fun (dev.kord.core.entity/User).(): kotlin/String // dev.kord.core.entity/effectiveName.|@dev.kord.core.entity.User(){}[0] -final val dev.kord.core/kordLogger // dev.kord.core/kordLogger|{}kordLogger[0] - final fun (): mu/KLogger // dev.kord.core/kordLogger.|(){}[0] final fun (dev.kord.cache.api/DataCache).dev.kord.core.cache/createView(): dev.kord.core.cache/DataCacheView // dev.kord.core.cache/createView|createView@dev.kord.cache.api.DataCache(){}[0] final fun (dev.kord.common.entity/DiscordChannel).dev.kord.core.cache.data/toData(): dev.kord.core.cache.data/ChannelData // dev.kord.core.cache.data/toData|toData@dev.kord.common.entity.DiscordChannel(){}[0] diff --git a/core/build.gradle.kts b/core/build.gradle.kts index 6a9478a6873..fa0218a2db1 100644 --- a/core/build.gradle.kts +++ b/core/build.gradle.kts @@ -15,9 +15,11 @@ kotlin { api(libs.kord.cache.map) implementation(libs.kotlin.logging) - - // TODO remove when kordLogger is removed - implementation(libs.kotlin.logging.old) + } + } + nonJvmMain { + dependencies { + implementation(libs.stately.collections) } } jvmMain { diff --git a/core/live-tests/build.gradle.kts b/core/live-tests/build.gradle.kts index 51862817d9f..4f50a55a000 100644 --- a/core/live-tests/build.gradle.kts +++ b/core/live-tests/build.gradle.kts @@ -1,7 +1,7 @@ import org.jetbrains.kotlin.gradle.ExperimentalKotlinGradlePluginApi plugins { - `kord-internal-multiplatform-module` + org.jetbrains.kotlin.multiplatform } kotlin { @@ -9,6 +9,7 @@ kotlin { compilerOptions { optIn.addAll(kordOptIns) } + targets() sourceSets { commonTest { dependencies { @@ -23,4 +24,8 @@ tasks { withType().configureEach { enabled = !System.getenv("KORD_TEST_TOKEN").isNullOrBlank() } + withType().configureEach { + useJUnitPlatform() + } + disableLinuxLinkTestTasksOnWindows() } diff --git a/core/src/commonMain/kotlin/Kord.kt b/core/src/commonMain/kotlin/Kord.kt index 0b3bc844361..71d4c3d6741 100644 --- a/core/src/commonMain/kotlin/Kord.kt +++ b/core/src/commonMain/kotlin/Kord.kt @@ -43,9 +43,6 @@ import kotlin.coroutines.CoroutineContext import kotlin.jvm.JvmName import kotlinx.coroutines.channels.Channel as CoroutineChannel -@Deprecated("Use your own logger instead. This declaration will be removed in 0.16.0.", level = DeprecationLevel.HIDDEN) -public val kordLogger: mu.KLogger = mu.KotlinLogging.logger { } - private val logger = KotlinLogging.logger { } @PublishedApi diff --git a/core/src/commonTest/kotlin/performance/KordEventDropTest.kt b/core/src/commonTest/kotlin/performance/KordEventDropTest.kt index 637a0fbe10d..7b7c18d25f3 100644 --- a/core/src/commonTest/kotlin/performance/KordEventDropTest.kt +++ b/core/src/commonTest/kotlin/performance/KordEventDropTest.kt @@ -12,6 +12,7 @@ import dev.kord.gateway.* import dev.kord.gateway.builder.Shards import dev.kord.rest.request.KtorRequestHandler import dev.kord.rest.service.RestClient + import dev.kord.test.IgnoreOnNative import io.ktor.client.* import kotlinx.atomicfu.atomic import kotlinx.coroutines.CompletableDeferred @@ -63,6 +64,8 @@ class KordEventDropTest { ) @Test + // This test seems to timeout sometimes on native + @IgnoreOnNative @JsName("test1") fun `hammering the gateway does not drop core events`() = runTest { val amount = 1_000 diff --git a/core/src/jsMain/kotlin/KordBuilder.kt b/core/src/nonJvmMain/kotlin/KordBuilder.kt similarity index 100% rename from core/src/jsMain/kotlin/KordBuilder.kt rename to core/src/nonJvmMain/kotlin/KordBuilder.kt diff --git a/gateway/api/gateway.api b/gateway/api/gateway.api index 949870c870f..b73047ebda7 100644 --- a/gateway/api/gateway.api +++ b/gateway/api/gateway.api @@ -707,7 +707,6 @@ public final class dev/kord/gateway/GatewayConfigurationBuilder { public final class dev/kord/gateway/GatewayKt { public static final fun editPresence (Ldev/kord/gateway/Gateway;Lkotlin/jvm/functions/Function1;Lkotlin/coroutines/Continuation;)Ljava/lang/Object; - public static final synthetic fun getGatewayOnLogger ()Lmu/KLogger; public static final fun logCaughtThrowable (Ljava/lang/Throwable;)V public static final fun requestGuildMembers (Ldev/kord/gateway/Gateway;Ldev/kord/common/entity/Snowflake;Lkotlin/jvm/functions/Function1;)Lkotlinx/coroutines/flow/Flow; public static final fun requestGuildMembers (Ldev/kord/gateway/Gateway;Ldev/kord/gateway/RequestGuildMembers;)Lkotlinx/coroutines/flow/Flow; @@ -1958,10 +1957,6 @@ public final class dev/kord/gateway/UserUpdate : dev/kord/gateway/DispatchEvent public fun toString ()Ljava/lang/String; } -public final class dev/kord/gateway/UtilsKt { - public static final synthetic fun error (Lmu/KLogger;Ljava/lang/Throwable;)V -} - public final class dev/kord/gateway/VoiceServerUpdate : dev/kord/gateway/DispatchEvent { public fun (Ldev/kord/common/entity/DiscordVoiceServerUpdateData;Ljava/lang/Integer;)V public final fun component1 ()Ldev/kord/common/entity/DiscordVoiceServerUpdateData; diff --git a/gateway/api/gateway.klib.api b/gateway/api/gateway.klib.api index b272e94d237..60f0a1dda61 100644 --- a/gateway/api/gateway.klib.api +++ b/gateway/api/gateway.klib.api @@ -1,5 +1,5 @@ // Klib ABI Dump -// Targets: [js] +// Targets: [iosArm64, iosSimulatorArm64, iosX64, js, linuxArm64, linuxX64, macosArm64, macosX64, mingwX64, tvosArm64, tvosSimulatorArm64, tvosX64, watchosArm64, watchosSimulatorArm64] // Rendering settings: // - Signature version: 2 // - Show manifest properties: true @@ -2350,12 +2350,9 @@ final val dev.kord.gateway/NON_PRIVILEGED // dev.kord.gateway/NON_PRIVILEGED|@de final fun (dev.kord.gateway/Intents.Companion).(): dev.kord.gateway/Intents // dev.kord.gateway/NON_PRIVILEGED.|@dev.kord.gateway.Intents.Companion(){}[0] final val dev.kord.gateway/PRIVILEGED // dev.kord.gateway/PRIVILEGED|@dev.kord.gateway.Intents.Companion{}PRIVILEGED[0] final fun (dev.kord.gateway/Intents.Companion).(): dev.kord.gateway/Intents // dev.kord.gateway/PRIVILEGED.|@dev.kord.gateway.Intents.Companion(){}[0] -final val dev.kord.gateway/gatewayOnLogger // dev.kord.gateway/gatewayOnLogger|{}gatewayOnLogger[0] - final fun (): mu/KLogger // dev.kord.gateway/gatewayOnLogger.|(){}[0] final fun (dev.kord.gateway/Gateway).dev.kord.gateway/requestGuildMembers(dev.kord.common.entity/Snowflake, kotlin/Function1 = ...): kotlinx.coroutines.flow/Flow // dev.kord.gateway/requestGuildMembers|requestGuildMembers@dev.kord.gateway.Gateway(dev.kord.common.entity.Snowflake;kotlin.Function1){}[0] final fun (dev.kord.gateway/Gateway).dev.kord.gateway/requestGuildMembers(dev.kord.gateway/RequestGuildMembers): kotlinx.coroutines.flow/Flow // dev.kord.gateway/requestGuildMembers|requestGuildMembers@dev.kord.gateway.Gateway(dev.kord.gateway.RequestGuildMembers){}[0] -final fun (mu/KLogger).dev.kord.gateway/error(kotlin/Throwable) // dev.kord.gateway/error|error@mu.KLogger(kotlin.Throwable){}[0] final fun dev.kord.gateway.ratelimit/IdentifyRateLimiter(kotlin/Int, kotlinx.coroutines/CoroutineDispatcher = ...): dev.kord.gateway.ratelimit/IdentifyRateLimiter // dev.kord.gateway.ratelimit/IdentifyRateLimiter|IdentifyRateLimiter(kotlin.Int;kotlinx.coroutines.CoroutineDispatcher){}[0] final fun dev.kord.gateway/Intents(kotlin.collections/Iterable): dev.kord.gateway/Intents // dev.kord.gateway/Intents|Intents(kotlin.collections.Iterable){}[0] final fun dev.kord.gateway/Intents(kotlin.collections/Iterable): dev.kord.gateway/Intents // dev.kord.gateway/Intents|Intents(kotlin.collections.Iterable){}[0] diff --git a/gateway/build.gradle.kts b/gateway/build.gradle.kts index 076873ffa47..2b646a41100 100644 --- a/gateway/build.gradle.kts +++ b/gateway/build.gradle.kts @@ -13,9 +13,6 @@ kotlin { implementation(libs.kotlin.logging) - // TODO remove when gatewayOnLogger and mu.KLogger.error() are removed - implementation(libs.kotlin.logging.old) - compileOnly(projects.kspAnnotations) } } diff --git a/gateway/src/commonMain/kotlin/DefaultGateway.kt b/gateway/src/commonMain/kotlin/DefaultGateway.kt index 8a5f673ffc0..669f456760f 100644 --- a/gateway/src/commonMain/kotlin/DefaultGateway.kt +++ b/gateway/src/commonMain/kotlin/DefaultGateway.kt @@ -168,30 +168,28 @@ public class DefaultGateway(private val data: DefaultGatewayData) : Gateway { private suspend fun readSocket() { - socket.incoming.asFlow().buffer(Channel.UNLIMITED).collect { - when (it) { - is Frame.Binary, is Frame.Text -> read(it) - else -> { /*ignore*/ + val frames = socket.incoming.asFlow() + .buffer(Channel.UNLIMITED) + .onEach { frame -> defaultGatewayLogger.trace { "Received raw frame: $frame" } } + val eventsJson = if (compression) { + frames.decompressFrames(inflater) + } else { + frames.mapNotNull { frame -> + when (frame) { + is Frame.Binary, is Frame.Text -> frame.data.decodeToString() + else -> null // ignore other frame types } } } - } - - private suspend fun read(frame: Frame) { - defaultGatewayLogger.trace { "Received raw frame: $frame" } - val json = when { - compression -> with(inflater) { frame.inflateData() } - else -> frame.data.decodeToString() - } - - try { - defaultGatewayLogger.trace { "Gateway <<< $json" } - val event = jsonParser.decodeFromString(Event.DeserializationStrategy, json) - data.eventFlow.emit(event) - } catch (exception: Exception) { - defaultGatewayLogger.error(exception) { "" } + eventsJson.collect { json -> + try { + defaultGatewayLogger.trace { "Gateway <<< $json" } + val event = jsonParser.decodeFromString(Event.DeserializationStrategy, json) + data.eventFlow.emit(event) + } catch (exception: Exception) { + defaultGatewayLogger.error(exception) { "" } + } } - } private suspend fun handleClose() { diff --git a/gateway/src/commonMain/kotlin/DefaultGatewayBuilder.kt b/gateway/src/commonMain/kotlin/DefaultGatewayBuilder.kt index 7dd8febc9cc..ac88ec36ced 100644 --- a/gateway/src/commonMain/kotlin/DefaultGatewayBuilder.kt +++ b/gateway/src/commonMain/kotlin/DefaultGatewayBuilder.kt @@ -8,9 +8,7 @@ import dev.kord.gateway.ratelimit.IdentifyRateLimiter import dev.kord.gateway.retry.LinearRetry import dev.kord.gateway.retry.Retry import io.ktor.client.* -import io.ktor.client.plugins.contentnegotiation.* import io.ktor.client.plugins.websocket.* -import io.ktor.serialization.kotlinx.json.* import kotlinx.coroutines.CoroutineDispatcher import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.flow.MutableSharedFlow @@ -29,9 +27,6 @@ public class DefaultGatewayBuilder { public fun build(): DefaultGateway { val client = client ?: HttpClient(httpEngine()) { install(WebSockets) - install(ContentNegotiation) { - json() - } } val retry = reconnectRetry ?: LinearRetry(2.seconds, 20.seconds, 10) val sendRateLimiter = sendRateLimiter ?: IntervalRateLimiter(limit = 120, interval = 60.seconds) diff --git a/gateway/src/commonMain/kotlin/Gateway.kt b/gateway/src/commonMain/kotlin/Gateway.kt index 093791d17e6..2948b89b5c9 100644 --- a/gateway/src/commonMain/kotlin/Gateway.kt +++ b/gateway/src/commonMain/kotlin/Gateway.kt @@ -139,14 +139,6 @@ public suspend inline fun Gateway.start(token: String, config: GatewayConfigurat start(builder.build()) } -@Suppress("unused") -@Deprecated( - "Kept for binary compatibility, this declaration will be removed in 0.16.0.", - level = DeprecationLevel.HIDDEN, -) -@PublishedApi -internal val gatewayOnLogger: mu.KLogger = mu.KotlinLogging.logger("Gateway.on") - /** * Logger used to report [Throwable]s caught in [Gateway.on]. */ diff --git a/gateway/src/commonMain/kotlin/Inflater.kt b/gateway/src/commonMain/kotlin/Inflater.kt index d8078372c4c..30a2fae6d5d 100644 --- a/gateway/src/commonMain/kotlin/Inflater.kt +++ b/gateway/src/commonMain/kotlin/Inflater.kt @@ -1,9 +1,48 @@ package dev.kord.gateway import io.ktor.websocket.* +import kotlinx.coroutines.flow.Flow +import kotlinx.coroutines.flow.transform internal interface Inflater : AutoCloseable { - fun Frame.inflateData(): String + /** Decompresses [compressedLen] bytes from [compressed] and decodes them to a [String]. */ + fun inflate(compressed: ByteArray, compressedLen: Int): String } internal expect fun Inflater(): Inflater + +// check if the last four bytes are equal to Z_SYNC_FLUSH suffix (00 00 ff ff), +// see https://discord.com/developers/docs/topics/gateway#transport-compression +private fun ByteArray.endsWithZlibSuffix(len: Int) = len >= 4 + && this[len - 4] == 0x00.toByte() + && this[len - 3] == 0x00.toByte() + && this[len - 2] == 0xff.toByte() + && this[len - 1] == 0xff.toByte() + +internal fun Flow.decompressFrames(inflater: Inflater): Flow { + var buffer = ByteArray(0) + var bufferLen = 0 + return transform { frame -> + when (frame) { + is Frame.Text, is Frame.Binary -> { + val data = frame.data + val dataLen = data.size + // skip copying into buffer if buffer is empty and data has suffix + if (bufferLen == 0 && data.endsWithZlibSuffix(dataLen)) { + emit(inflater.inflate(data, dataLen)) + } else { + if (buffer.size - bufferLen < dataLen) { + buffer = buffer.copyOf(bufferLen + dataLen) + } + data.copyInto(buffer, destinationOffset = bufferLen) + bufferLen += dataLen + if (buffer.endsWithZlibSuffix(bufferLen)) { + emit(inflater.inflate(buffer, bufferLen)) + bufferLen = 0 + } + } + } + else -> {} // ignore other frame types + } + } +} diff --git a/gateway/src/commonMain/kotlin/Utils.kt b/gateway/src/commonMain/kotlin/Utils.kt deleted file mode 100644 index 31ef79fd596..00000000000 --- a/gateway/src/commonMain/kotlin/Utils.kt +++ /dev/null @@ -1,10 +0,0 @@ -package dev.kord.gateway - -import mu.KLogger - -@Deprecated( - "Kept for binary compatibility, this declaration will be removed in 0.16.0.", - level = DeprecationLevel.HIDDEN, -) -@PublishedApi -internal fun KLogger.error(throwable: Throwable): Unit = error(throwable) { "" } diff --git a/gateway/src/commonTest/kotlin/json/CommandTest.kt b/gateway/src/commonTest/kotlin/json/CommandTest.kt index ffb1174f0c7..24114b5a5bb 100644 --- a/gateway/src/commonTest/kotlin/json/CommandTest.kt +++ b/gateway/src/commonTest/kotlin/json/CommandTest.kt @@ -9,6 +9,7 @@ import dev.kord.common.entity.optional.coerceToMissing import dev.kord.common.entity.optional.optional import dev.kord.common.entity.optional.optionalInt import dev.kord.gateway.* +import dev.kord.test.IgnoreOnSimulatorPlatforms import kotlinx.datetime.Instant import kotlinx.serialization.json.* import kotlin.js.JsName @@ -17,6 +18,7 @@ import kotlin.test.assertEquals private val json = Json { encodeDefaults = false } +@IgnoreOnSimulatorPlatforms class CommandTest { @Test @JsName("test1") diff --git a/gateway/src/commonTest/kotlin/json/RegressionTests.kt b/gateway/src/commonTest/kotlin/json/RegressionTests.kt index 831aade5e82..c174bc42dae 100644 --- a/gateway/src/commonTest/kotlin/json/RegressionTests.kt +++ b/gateway/src/commonTest/kotlin/json/RegressionTests.kt @@ -2,6 +2,7 @@ package dev.kord.gateway.json import dev.kord.gateway.Event import dev.kord.gateway.Reconnect +import dev.kord.test.IgnoreOnSimulatorPlatforms import kotlinx.coroutines.test.runTest import kotlinx.serialization.json.Json import kotlin.js.JsName @@ -9,6 +10,7 @@ import kotlin.test.Test private suspend fun file(name: String): String = readFile("regression", name) +@IgnoreOnSimulatorPlatforms class RegressionTests { @Test @JsName("test1") diff --git a/gateway/src/commonTest/kotlin/json/SerializationTest.kt b/gateway/src/commonTest/kotlin/json/SerializationTest.kt index 23d0712136b..1d172c6b7fb 100644 --- a/gateway/src/commonTest/kotlin/json/SerializationTest.kt +++ b/gateway/src/commonTest/kotlin/json/SerializationTest.kt @@ -6,6 +6,7 @@ import dev.kord.common.entity.UserPremium import dev.kord.common.entity.optional.Optional import dev.kord.common.entity.optional.value import dev.kord.gateway.* +import dev.kord.test.IgnoreOnSimulatorPlatforms import kotlinx.coroutines.test.runTest import kotlinx.datetime.Instant import kotlinx.serialization.ExperimentalSerializationApi @@ -21,6 +22,7 @@ import kotlin.time.Duration.Companion.seconds private suspend fun file(name: String): String = readFile("event", name) +@IgnoreOnSimulatorPlatforms class SerializationTest { @Test diff --git a/gateway/src/commonTest/kotlin/json/SnowflakeTest.kt b/gateway/src/commonTest/kotlin/json/SnowflakeTest.kt index d3db76c8ccd..9945d395105 100644 --- a/gateway/src/commonTest/kotlin/json/SnowflakeTest.kt +++ b/gateway/src/commonTest/kotlin/json/SnowflakeTest.kt @@ -1,6 +1,7 @@ package dev.kord.gateway.json import dev.kord.common.entity.Snowflake +import dev.kord.test.IgnoreOnSimulatorPlatforms import kotlinx.serialization.ExperimentalSerializationApi import kotlinx.serialization.Serializable import kotlinx.serialization.encodeToString @@ -9,6 +10,7 @@ import kotlin.js.JsName import kotlin.test.Test import kotlin.test.assertEquals +@IgnoreOnSimulatorPlatforms class SnowflakeTest { @Serializable diff --git a/gateway/src/jsMain/kotlin/Inflater.kt b/gateway/src/jsMain/kotlin/Inflater.kt index 418c269d094..53d725b00f3 100644 --- a/gateway/src/jsMain/kotlin/Inflater.kt +++ b/gateway/src/jsMain/kotlin/Inflater.kt @@ -1,15 +1,14 @@ package dev.kord.gateway import dev.kord.gateway.internal.Inflate -import io.ktor.websocket.* import node.buffer.Buffer import node.buffer.BufferEncoding internal actual fun Inflater() = object : Inflater { private val inflate = Inflate() - override fun Frame.inflateData(): String { - val buffer = Buffer.from(data) + override fun inflate(compressed: ByteArray, compressedLen: Int): String { + val buffer = Buffer.from(compressed, byteOffset = 0, length = compressedLen) return inflate.process(buffer).toString(BufferEncoding.utf8) } diff --git a/gateway/src/jvmMain/kotlin/Inflater.kt b/gateway/src/jvmMain/kotlin/Inflater.kt deleted file mode 100644 index 348ba9ae389..00000000000 --- a/gateway/src/jvmMain/kotlin/Inflater.kt +++ /dev/null @@ -1,20 +0,0 @@ -package dev.kord.gateway - -import io.ktor.websocket.* -import java.io.ByteArrayOutputStream -import java.util.zip.InflaterOutputStream - -internal actual fun Inflater() = object : Inflater { - private val delegate = java.util.zip.Inflater() - - override fun Frame.inflateData(): String { - val outputStream = ByteArrayOutputStream() - InflaterOutputStream(outputStream, delegate).use { - it.write(data) - } - - return outputStream.use { it.toByteArray().decodeToString() } - } - - override fun close() = delegate.end() -} diff --git a/gateway/src/jvmMain/kotlin/InflaterJvm.kt b/gateway/src/jvmMain/kotlin/InflaterJvm.kt new file mode 100644 index 00000000000..ccfc286488c --- /dev/null +++ b/gateway/src/jvmMain/kotlin/InflaterJvm.kt @@ -0,0 +1,19 @@ +package dev.kord.gateway + +import java.io.ByteArrayOutputStream +import java.util.zip.InflaterOutputStream + +internal actual fun Inflater() = object : Inflater { + private val delegate = java.util.zip.Inflater() + private val buffer = ByteArrayOutputStream() + + override fun inflate(compressed: ByteArray, compressedLen: Int): String { + buffer.reset() + InflaterOutputStream(buffer, delegate).use { + it.write(compressed, /* off = */ 0, /* len = */ compressedLen) + } + return buffer.toString("UTF-8") + } + + override fun close() = delegate.end() +} diff --git a/gateway/src/nativeMain/kotlin/DefaultGateway.kt b/gateway/src/nativeMain/kotlin/DefaultGateway.kt new file mode 100644 index 00000000000..2224a4ebe0c --- /dev/null +++ b/gateway/src/nativeMain/kotlin/DefaultGateway.kt @@ -0,0 +1,11 @@ +package dev.kord.gateway + +import dev.kord.common.annotation.KordInternal +import io.ktor.util.network.* +import kotlin.experimental.ExperimentalNativeApi + +@KordInternal +public actual fun Throwable.isTimeout(): Boolean = this is UnresolvedAddressException + +@OptIn(ExperimentalNativeApi::class) +internal actual val os: String get() = Platform.osFamily.name diff --git a/gateway/src/nativeMain/kotlin/Inflater.kt b/gateway/src/nativeMain/kotlin/Inflater.kt new file mode 100644 index 00000000000..dc0e9cc72e4 --- /dev/null +++ b/gateway/src/nativeMain/kotlin/Inflater.kt @@ -0,0 +1,95 @@ +package dev.kord.gateway + +import kotlinx.cinterop.* +import platform.zlib.* + +@ExperimentalForeignApi +private class ZlibException(msg: CPointer?, ret: Int) : IllegalStateException( + message = msg?.toKString() + ?: zError(ret)?.toKString()?.ifEmpty { null } // zError returns empty string for unknown codes + ?: "unexpected return code: $ret" +) + +@OptIn(ExperimentalForeignApi::class) +internal actual fun Inflater(): Inflater = object : Inflater { + // see https://zlib.net/manual.html + + // buffer for decompressed data, only grows, reused for every zlib inflate call + private var decompressed = UByteArray(1024) + private var closed = false + private val zStream = nativeHeap.alloc() + + init { + try { + // next_in, avail_in, zalloc, zfree and opaque must be initialized before calling inflateInit + zStream.next_in = null + zStream.avail_in = 0u + zStream.zalloc = null + zStream.zfree = null + zStream.opaque = null + // initialize msg to null in case inflateInit doesn't, we use it for throwing exceptions + zStream.msg = null + val ret = inflateInit(zStream.ptr) + if (ret != Z_OK) throw ZlibException(zStream.msg, ret) + } catch (e: Throwable) { + try { + nativeHeap.free(zStream) + } catch (freeException: Throwable) { + e.addSuppressed(freeException) + } + throw e + } + } + + override fun inflate(compressed: ByteArray, compressedLen: Int): String { + check(!closed) { "Inflater has already been closed." } + compressed.asUByteArray().usePinned { compressedPinned -> + zStream.next_in = compressedPinned.addressOf(0) + zStream.avail_in = compressedLen.convert() + var decompressedLen = 0 + while (true) { + val ret = decompressed.usePinned { decompressedPinned -> + zStream.next_out = decompressedPinned.addressOf(decompressedLen) + zStream.avail_out = (decompressed.size - decompressedLen).convert() + inflate(zStream.ptr, Z_SYNC_FLUSH) + } + when { + ret == Z_OK && zStream.avail_out == 0u -> { + // grow decompressed buffer and call inflate again, there might be more output pending + decompressedLen = decompressed.size + decompressed = decompressed.copyOf(decompressed.size * 2) + } + // Z_BUF_ERROR is no real error after the first inflate call: + // It means the previous inflate call did exactly fill the decompressed buffer (avail_out == 0). + // Because of that, we grew the buffer and called inflate again. However, it couldn't make any + // progress this time (everything is already decompressed), so it returns Z_BUF_ERROR. + ret == Z_OK || ret == Z_STREAM_END || (decompressedLen != 0 && ret == Z_BUF_ERROR) -> { + check(zStream.avail_in == 0u) { "Inflater did not decompress all available data." } + break + } + else -> throw ZlibException(zStream.msg, ret) + } + } + } + return decompressed + .asByteArray() + .decodeToString(endIndex = decompressed.size - zStream.avail_out.convert()) + } + + override fun close() { + if (closed) return + closed = true + try { + val ret = inflateEnd(zStream.ptr) + if (ret != Z_OK) throw ZlibException(zStream.msg, ret) + } catch (e: Throwable) { + try { + nativeHeap.free(zStream) + } catch (freeException: Throwable) { + e.addSuppressed(freeException) + } + throw e + } + nativeHeap.free(zStream) + } +} diff --git a/gradle.properties b/gradle.properties index 599b3de433f..b278d7bba58 100644 --- a/gradle.properties +++ b/gradle.properties @@ -8,6 +8,7 @@ kotlin.code.style=official # the Kotlin daemon runs out of memory with the default maximum heap size kotlin.daemon.jvmargs=-Xmx2g +org.gradle.jvmargs=-Xmx1g # https://kotlinlang.org/docs/dokka-migration.html#set-the-opt-in-flag org.jetbrains.dokka.experimental.gradle.pluginMode=V2Enabled @@ -21,3 +22,6 @@ kotlin.suppressGradlePluginWarnings=IncorrectCompileOnlyDependencyWarning kotlinx.atomicfu.enableJvmIrTransformation=true kotlinx.atomicfu.enableJsIrTransformation=true kotlinx.atomicfu.enableNativeIrTransformation=true + +# We are aware of these issues and their symptoms don't affect us +kotlin.native.ignoreDisabledTargets=true diff --git a/gradle/libs.versions.toml b/gradle/libs.versions.toml index ab485a8dfde..1a2804826b8 100644 --- a/gradle/libs.versions.toml +++ b/gradle/libs.versions.toml @@ -9,13 +9,14 @@ kotlinx-datetime = "0.6.1" # https://github.com/Kotlin/kotlinx-datetime kord-cache = "0.5.4" # https://github.com/kordlib/cache # implementation dependencies +kotlinx-io = "0.5.4" # https://github.com/Kotlin/kotlinx-io kotlin-logging = "7.0.0" # https://github.com/oshai/kotlin-logging -kotlin-logging-old = "3.0.5" # TODO remove after dependency is removed in rest, gateway, voice and core slf4j = "2.0.16" # https://www.slf4j.org kotlin-node = "22.5.4-pre.818" # https://github.com/JetBrains/kotlin-wrappers bignum = "0.3.10" # https://github.com/ionspin/kotlin-multiplatform-bignum stately = "2.1.0" # https://github.com/touchlab/Stately fastZlib = "2.0.1" # https://github.com/timotejroiko/fast-zlib +sodium = "0.9.2" # https://github.com/ionspin/kotlin-multiplatform-libsodium # code generation ksp = "2.0.21-1.0.25" # https://github.com/google/ksp @@ -32,6 +33,8 @@ dokka = "2.0.0-Beta" # https://github.com/Kotlin/dokka kotlinx-atomicfu = "0.25.0" # https://github.com/Kotlin/kotlinx-atomicfu binary-compatibility-validator = "0.16.3" # https://github.com/Kotlin/binary-compatibility-validator buildconfig = "5.5.0" # https://github.com/gmazzo/gradle-buildconfig-plugin +kord-gradle-tools = "1.6.3" # https://github.com/kordlib/gradle-tools/blob/main/build.gradle.kts#L10 +maven-publish-plugin = "0.30.0" # https://github.com/vanniktech/gradle-maven-publish-plugin [libraries] @@ -46,6 +49,9 @@ ktor-serialization-kotlinx-json = { module = "io.ktor:ktor-serialization-kotlinx ktor-client-core = { module = "io.ktor:ktor-client-core", version.ref = "ktor" } ktor-client-js = { module = "io.ktor:ktor-client-js", version.ref = "ktor" } ktor-client-okhttp = { module = "io.ktor:ktor-client-okhttp", version.ref = "ktor" } +ktor-client-winhttp = { module = "io.ktor:ktor-client-winhttp", version.ref = "ktor" } +ktor-client-darwin = { module = "io.ktor:ktor-client-darwin", version.ref = "ktor" } +ktor-client-curl = { module = "io.ktor:ktor-client-curl", version.ref = "ktor" } ktor-client-mock = { module = "io.ktor:ktor-client-mock", version.ref = "ktor" } ktor-network = { module = "io.ktor:ktor-network", version.ref = "ktor" } ktor-io = { module = "io.ktor:ktor-io", version.ref = "ktor" } @@ -58,13 +64,13 @@ kotlinx-datetime = { module = "org.jetbrains.kotlinx:kotlinx-datetime", version. # other kotlin-logging = { module = "io.github.oshai:kotlin-logging", version.ref = "kotlin-logging" } -kotlin-logging-old = { module = "io.github.microutils:kotlin-logging", version.ref = "kotlin-logging-old" } slf4j-api = { module = "org.slf4j:slf4j-api", version.ref = "slf4j" } kotlin-node = { module = "org.jetbrains.kotlin-wrappers:kotlin-node", version.ref = "kotlin-node" } # JDK replacements bignum = { module = "com.ionspin.kotlin:bignum", version.ref = "bignum" } stately-collections = { module = "co.touchlab:stately-concurrent-collections", version.ref = "stately" } +libsodium = { module = "com.ionspin.kotlin:multiplatform-crypto-libsodium-bindings", version.ref = "sodium" } # code generation ksp-api = { module = "com.google.devtools.ksp:symbol-processing-api", version.ref = "ksp" } @@ -82,6 +88,7 @@ junit-platform-launcher = { module = "org.junit.platform:junit-platform-launcher mockk = { module = "io.mockk:mockk", version.ref = "mockk" } slf4j-simple = { module = "org.slf4j:slf4j-simple", version.ref = "slf4j" } kbson = { module = "org.mongodb.kbson:kbson", version.ref = "kbson" } +kotlinx-io = { module = "org.jetbrains.kotlinx:kotlinx-io-core", version.ref = "kotlinx-io" } # actually plugins, not libraries, but used is 'buildSrc/build.gradle.kts' as implementation dependencies: # https://docs.gradle.org/current/userguide/implementing_gradle_plugins_precompiled.html#sec:applying_external_plugins @@ -91,6 +98,8 @@ dokka-plugin = { module = "org.jetbrains.dokka:dokka-gradle-plugin", version.ref atomicfu-plugin = { module = "org.jetbrains.kotlinx:atomicfu-gradle-plugin", version.ref = "kotlinx-atomicfu" } binary-compatibility-validator-plugin = { module = "org.jetbrains.kotlinx:binary-compatibility-validator", version.ref = "binary-compatibility-validator" } ksp-plugin = { module = "com.google.devtools.ksp:symbol-processing-gradle-plugin", version.ref = "ksp" } +kord-gradle-plugin = { module = "dev.kord:gradle-tools", version.ref = "kord-gradle-tools" } +maven-publish-plugin = { module = "com.vanniktech:gradle-maven-publish-plugin", version.ref = "maven-publish-plugin" } [bundles] @@ -109,6 +118,8 @@ pluginsForBuildSrc = [ "atomicfu-plugin", "binary-compatibility-validator-plugin", "ksp-plugin", + "kord-gradle-plugin", + "maven-publish-plugin", ] diff --git a/kotlin-js-store/yarn.lock b/kotlin-js-store/yarn.lock index 493392acc4e..b76cb2178b9 100644 --- a/kotlin-js-store/yarn.lock +++ b/kotlin-js-store/yarn.lock @@ -296,6 +296,18 @@ js-yaml@^4.1.0: 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== + +libsodium-wrappers-sumo@0.7.13: + 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" diff --git a/ksp-annotations/build.gradle.kts b/ksp-annotations/build.gradle.kts index 6f3e2f7d6c7..cf2fffe8b74 100644 --- a/ksp-annotations/build.gradle.kts +++ b/ksp-annotations/build.gradle.kts @@ -1,5 +1,7 @@ +import org.jetbrains.dokka.gradle.AbstractDokkaLeafTask + plugins { - `kord-internal-multiplatform-module` + org.jetbrains.kotlin.multiplatform // workaround for https://youtrack.jetbrains.com/issue/KT-43500 / // https://youtrack.jetbrains.com/issue/KT-64109#focus=Comments-27-10064206.0-0 / @@ -8,8 +10,18 @@ plugins { `kord-publishing` } +kotlin { + targets() +} + dokka { dokkaSourceSets.configureEach { suppress = true } } + +tasks { + withType().configureEach { + options.release = KORD_JVM_TARGET + } +} diff --git a/rest/api/rest.klib.api b/rest/api/rest.klib.api index b9cb1a9ef81..573573a43a8 100644 --- a/rest/api/rest.klib.api +++ b/rest/api/rest.klib.api @@ -1,5 +1,5 @@ // Klib ABI Dump -// Targets: [js] +// Targets: [iosArm64, iosSimulatorArm64, iosX64, js, linuxArm64, linuxX64, macosArm64, macosX64, mingwX64, tvosArm64, tvosSimulatorArm64, tvosX64, watchosArm64, watchosSimulatorArm64] // Rendering settings: // - Signature version: 2 // - Show manifest properties: true diff --git a/rest/src/commonTest/kotlin/json/AuditLogResponseTest.kt b/rest/src/commonTest/kotlin/json/AuditLogResponseTest.kt index c53cb4a83cf..b87d7724f40 100644 --- a/rest/src/commonTest/kotlin/json/AuditLogResponseTest.kt +++ b/rest/src/commonTest/kotlin/json/AuditLogResponseTest.kt @@ -1,11 +1,13 @@ package dev.kord.rest.json import dev.kord.common.entity.DiscordAuditLog +import dev.kord.test.IgnoreOnSimulatorPlatforms import kotlinx.coroutines.test.runTest import kotlinx.serialization.json.Json import kotlin.js.JsName import kotlin.test.Test +@IgnoreOnSimulatorPlatforms class AuditLogResponseTest { @Test diff --git a/rest/src/commonTest/kotlin/json/ErrorTest.kt b/rest/src/commonTest/kotlin/json/ErrorTest.kt index 7b641c3b218..8155bb8cb31 100644 --- a/rest/src/commonTest/kotlin/json/ErrorTest.kt +++ b/rest/src/commonTest/kotlin/json/ErrorTest.kt @@ -1,12 +1,14 @@ package dev.kord.rest.json import dev.kord.rest.json.response.DiscordErrorResponse +import dev.kord.test.IgnoreOnSimulatorPlatforms import kotlinx.coroutines.test.runTest import kotlinx.serialization.json.Json import kotlin.js.JsName import kotlin.test.Test import kotlin.test.assertEquals +@IgnoreOnSimulatorPlatforms class ErrorTest { private val parser = Json { encodeDefaults = false diff --git a/rest/src/commonTest/kotlin/json/Util.kt b/rest/src/commonTest/kotlin/json/Util.kt index dd6f1420ef5..5f47781fe40 100644 --- a/rest/src/commonTest/kotlin/json/Util.kt +++ b/rest/src/commonTest/kotlin/json/Util.kt @@ -5,4 +5,4 @@ import dev.kord.test.file as platformFile import dev.kord.test.readFile as platformReadFile internal suspend fun file(name: String): String = platformFile("rest", "json/$name.json") -internal suspend fun readFile(name: String): ByteReadChannel = platformReadFile("rest", name) +internal suspend fun readFile(name: String): CountedByteReadChannel = platformReadFile("rest", name) diff --git a/rest/src/commonTest/kotlin/request/MessageRequests.kt b/rest/src/commonTest/kotlin/request/MessageRequests.kt index 7c728d37cbd..cea4d72b5d0 100644 --- a/rest/src/commonTest/kotlin/request/MessageRequests.kt +++ b/rest/src/commonTest/kotlin/request/MessageRequests.kt @@ -8,10 +8,12 @@ import dev.kord.common.entity.Snowflake import dev.kord.common.entity.optional.Optional import dev.kord.rest.json.readFile import dev.kord.rest.service.ChannelService +import dev.kord.test.IgnoreOnSimulatorPlatforms +import dev.kord.test.Platform import io.ktor.client.* import io.ktor.client.engine.mock.* import io.ktor.client.request.forms.* -import io.ktor.utils.io.* +import io.ktor.utils.io.counted import kotlinx.coroutines.test.runTest import kotlinx.datetime.Clock import kotlinx.serialization.encodeToString @@ -52,6 +54,7 @@ private val mockMessage = DiscordMessage( class MessageRequests { @Test @JsName("test1") + @IgnoreOnSimulatorPlatforms fun `attachment channel is read and closed lazily`() = runTest { val mockEngine = MockEngine { request -> diff --git a/rest/src/nativeMain/kotlin/RecoveredStackTrace.kt b/rest/src/nativeMain/kotlin/RecoveredStackTrace.kt new file mode 100644 index 00000000000..091fca9aadd --- /dev/null +++ b/rest/src/nativeMain/kotlin/RecoveredStackTrace.kt @@ -0,0 +1,4 @@ +package dev.kord.rest.request + +// You cannot really modify stack traces in Native :( +internal actual fun RecoveredStackTrace.sanitizeStackTrace() = Unit diff --git a/rest/src/nativeTest/kotlin/dev/kord/rest/request/StackTrace.kt b/rest/src/nativeTest/kotlin/dev/kord/rest/request/StackTrace.kt new file mode 100644 index 00000000000..8c9d00a4697 --- /dev/null +++ b/rest/src/nativeTest/kotlin/dev/kord/rest/request/StackTrace.kt @@ -0,0 +1,23 @@ +package dev.kord.rest.request + +import kotlin.test.assertEquals + +actual typealias StackTraceElement = String + +//kotlin.Exception +//at 0 ??? 7ff68473da75 kfun:kotlin.Throwable#(kotlin.String?){} + 117 +//at 1 ??? 7ff684f9ee89 kfun:dev.kord.rest.request.RecoveredStackTrace#(){} + 89 +//at 2 ??? 7ff684f9e939 kfun:dev.kord.rest.request.StackTraceRecoveringKtorRequestHandler.$handleCOROUTINE$23#invokeSuspend(kotlin.Result){}kotlin.Any? + 681 +//at 3 ??? 7ff684f9ed3c kfun:dev.kord.rest.request.StackTraceRecoveringKtorRequestHandler#handle(dev.kord.rest.request.Request<0:0,0:1>){0§;1§}0:1 + 300 +//at 4 ??? 7ff684fbd4c4 kfun:dev.kord.rest.request.StackTraceRecoveryTest.$test stack trace recovery$lambda$1COROUTINE$15.invokeSuspend#internal + 2740 +//-->at 5 ??? 7ff684fbdeca kfun:dev.kord.rest.request.StackTraceRecoveryTest.$test stack trace<-- +actual fun currentThreadStackTrace(): StackTraceElement = + Exception().stackTraceToString().lineSequence().filterNot(String::isBlank).drop(5).first().trim() + .substringAfter("kfun:") + +internal actual fun RecoveredStackTrace.validate(expected: StackTraceElement) { + // The first few lines are artifacts from coroutines which are not present in expected + val actual = stackTraceToString().lineSequence().drop(6).first().trim() + .substringAfter("kfun:") // index is off at call site + assertEquals(expected, actual) +} diff --git a/samples/build.gradle.kts b/samples/build.gradle.kts index 2e16a83d74d..6b612393dfd 100644 --- a/samples/build.gradle.kts +++ b/samples/build.gradle.kts @@ -1,16 +1,45 @@ +import org.jetbrains.kotlin.gradle.ExperimentalKotlinGradlePluginApi +import org.jetbrains.kotlin.gradle.plugin.mpp.KotlinNativeTarget +import org.jetbrains.kotlin.konan.target.Family +import org.jetbrains.kotlin.konan.target.HostManager + plugins { - `kord-internal-multiplatform-module` + org.jetbrains.kotlin.multiplatform } +@OptIn(ExperimentalKotlinGradlePluginApi::class) kotlin { + applyDefaultHierarchyTemplate { + common { + group("voice") { + withLinux() + withMacos() + withJvm() + withJs() + } + } + } + + targets() + js { binaries.executable() } + targets.withType { + // Voice does not target windows, so we disable it + if (konanTarget.family != Family.MINGW) { + binaries.executable { + entryPoint = "dev.kord.voice.test.main" + } + } + } + sourceSets { commonMain { dependencies { implementation(projects.core) + implementation(libs.kotlin.logging) } } jvmMain { @@ -18,5 +47,35 @@ kotlin { runtimeOnly(libs.slf4j.simple) } } + linuxMain { + dependencies { + implementation(libs.ktor.client.curl) + } + } + + named("voiceMain") { + dependencies { + implementation(projects.coreVoice) + } + } + } +} + +tasks { + // There are issues with linking the linux variant on windows. + // Please use WSL if you need to work on the linux port. + /** see [disableLinuxLinkTestTasksOnWindows] */ + if (HostManager.hostIsMingw) { + val linuxLinkExecutableTasks = listOf( + "linkDebugExecutableLinuxX64", + "linkDebugExecutableLinuxArm64", + "linkReleaseExecutableLinuxX64", + "linkReleaseExecutableLinuxArm64", + ) + for (task in linuxLinkExecutableTasks) { + named(task) { + enabled = false + } + } } } diff --git a/samples/src/commonMain/kotlin/runMain.kt b/samples/src/commonMain/kotlin/runMain.kt new file mode 100644 index 00000000000..6d358d86576 --- /dev/null +++ b/samples/src/commonMain/kotlin/runMain.kt @@ -0,0 +1,3 @@ +import kotlinx.coroutines.CoroutineScope + +expect fun runMain(block: suspend CoroutineScope.() -> Unit) diff --git a/samples/src/jsMain/kotlin/runMain.js.kt b/samples/src/jsMain/kotlin/runMain.js.kt new file mode 100644 index 00000000000..a937c82a759 --- /dev/null +++ b/samples/src/jsMain/kotlin/runMain.js.kt @@ -0,0 +1,9 @@ +import kotlinx.coroutines.CoroutineScope +import kotlinx.coroutines.DelicateCoroutinesApi +import kotlinx.coroutines.GlobalScope +import kotlinx.coroutines.launch + +@OptIn(DelicateCoroutinesApi::class) +actual fun runMain(block: suspend CoroutineScope.() -> Unit) { + GlobalScope.launch { block() } +} diff --git a/samples/src/jvmMain/kotlin/runMain.jvm.kt b/samples/src/jvmMain/kotlin/runMain.jvm.kt new file mode 100644 index 00000000000..48392f5b700 --- /dev/null +++ b/samples/src/jvmMain/kotlin/runMain.jvm.kt @@ -0,0 +1,6 @@ +import kotlinx.coroutines.CoroutineScope +import kotlinx.coroutines.runBlocking + +actual fun runMain(block: suspend CoroutineScope.() -> Unit) { + runBlocking { block() } +} diff --git a/samples/src/jvmMain/resources/simplelogger.properties b/samples/src/jvmMain/resources/simplelogger.properties new file mode 100644 index 00000000000..ba270bfe08c --- /dev/null +++ b/samples/src/jvmMain/resources/simplelogger.properties @@ -0,0 +1 @@ +org.slf4j.simpleLogger.defaultLogLevel=trace \ No newline at end of file diff --git a/samples/src/nativeMain/kotlin/runMain.native.kt b/samples/src/nativeMain/kotlin/runMain.native.kt new file mode 100644 index 00000000000..ca2234a0986 --- /dev/null +++ b/samples/src/nativeMain/kotlin/runMain.native.kt @@ -0,0 +1,6 @@ +import kotlinx.coroutines.CoroutineScope +import kotlinx.coroutines.runBlocking + +actual fun runMain(block: suspend CoroutineScope.() -> Unit) { + runBlocking { block() } +} \ No newline at end of file diff --git a/samples/src/voiceMain/kotlin/VoiceBot.kt b/samples/src/voiceMain/kotlin/VoiceBot.kt new file mode 100644 index 00000000000..a933224986a --- /dev/null +++ b/samples/src/voiceMain/kotlin/VoiceBot.kt @@ -0,0 +1,54 @@ +@file:OptIn(KordVoice::class) + +package dev.kord.voice.test + +import dev.kord.common.annotation.KordVoice +import dev.kord.core.Kord +import dev.kord.core.behavior.channel.BaseVoiceChannelBehavior +import dev.kord.core.behavior.channel.connect +import dev.kord.core.behavior.interaction.respondPublic +import dev.kord.core.event.interaction.GuildChatInputCommandInteractionCreateEvent +import dev.kord.core.on +import dev.kord.voice.AudioFrame +import kotlinx.coroutines.launch +import runMain + +fun main(args: Array) = runMain { + val kord = + Kord(args.firstOrNull() ?: error("Missing token")) + + kord.createGlobalApplicationCommands { + input("join", "Test command") { + dmPermission = false + } + } + + kord.on { + val channel = interaction.user.asMember(interaction.guildId).getVoiceState().getChannelOrNull() + if (channel == null) { + interaction.respondPublic { content = "not in channel" } + return@on + } + interaction.respondPublic { content = "success" } + channel.connectEcho() + } + + 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.removeFirstOrNull() ?: AudioFrame.SILENCE + } + } + connection.scope.launch { + connection.streams.incomingAudioFrames.collect { (userId, frame) -> + println("Received frame from:${userId}") + buffer.add(frame) + } + } +} diff --git a/test-kit/build.gradle.kts b/test-kit/build.gradle.kts index a5c13ff37e4..060654d3766 100644 --- a/test-kit/build.gradle.kts +++ b/test-kit/build.gradle.kts @@ -1,8 +1,22 @@ +import org.jetbrains.kotlin.gradle.ExperimentalKotlinGradlePluginApi + plugins { - `kord-internal-multiplatform-module` + org.jetbrains.kotlin.multiplatform + dev.kord.`gradle-tools` } +@OptIn(ExperimentalKotlinGradlePluginApi::class) kotlin { + applyDefaultHierarchyTemplate { + common { + group("simulator") { + withIos() + withTvos() + withWatchos() + } + } + } + targets() sourceSets { commonMain { dependencies { @@ -21,5 +35,20 @@ kotlin { runtimeOnly(libs.bundles.test.jvm.runtime) } } + nativeMain { + dependencies { + api(libs.kotlinx.io) + } + } + } + + compilerOptions { + applyKordCommonCompilerOptions() + } +} + +tasks { + withType().configureEach { + options.release = KORD_JVM_TARGET } } diff --git a/test-kit/src/commonMain/kotlin/Annotations.kt b/test-kit/src/commonMain/kotlin/Annotations.kt index bcc93e2653d..1da1d84342a 100644 --- a/test-kit/src/commonMain/kotlin/Annotations.kt +++ b/test-kit/src/commonMain/kotlin/Annotations.kt @@ -14,3 +14,13 @@ expect annotation class IgnoreOnJs() @Target(CLASS, FUNCTION) @OptionalExpectation expect annotation class IgnoreOnJvm() + +/** Ignores this test on Native platforms. */ +@Target(CLASS, FUNCTION) +@OptionalExpectation +expect annotation class IgnoreOnNative() + +/** Ignores this test on simulator platforms. */ +@Target(CLASS, FUNCTION) +@OptionalExpectation +expect annotation class IgnoreOnSimulatorPlatforms() diff --git a/test-kit/src/commonMain/kotlin/Platform.kt b/test-kit/src/commonMain/kotlin/Platform.kt index fe9f5179e53..6fbe0600f97 100644 --- a/test-kit/src/commonMain/kotlin/Platform.kt +++ b/test-kit/src/commonMain/kotlin/Platform.kt @@ -5,8 +5,13 @@ import io.ktor.utils.io.* expect object Platform { val IS_JVM: Boolean val IS_NODE: Boolean + val IS_BROWSER: Boolean + val IS_MINGW: Boolean + val IS_LINUX: Boolean + val IS_DARWIN: Boolean } expect fun getEnv(name: String): String? expect suspend fun file(project: String, path: String): String -expect suspend fun readFile(project: String, path: String): ByteReadChannel +suspend fun readFile(project: String, path: String) = readFile0(project, path).counted() +expect suspend fun readFile0(project: String, path: String): ByteReadChannel diff --git a/test-kit/src/jsMain/kotlin/Platform.kt b/test-kit/src/jsMain/kotlin/Platform.kt index bf7bcae63bb..5d5094b8275 100644 --- a/test-kit/src/jsMain/kotlin/Platform.kt +++ b/test-kit/src/jsMain/kotlin/Platform.kt @@ -9,6 +9,13 @@ actual object Platform { get() = js( "typeof process !== 'undefined' && process.versions != null && process.versions.node != null" ) as Boolean + actual val IS_BROWSER: Boolean + get() = js( + "typeof window !== 'undefined' && typeof window.document !== 'undefined' || typeof self !== 'undefined' && typeof self.location !== 'undefined'" + ) as Boolean + actual const val IS_MINGW: Boolean = false + actual const val IS_LINUX: Boolean = false + actual const val IS_DARWIN: Boolean = false } actual fun getEnv(name: String) = process.env[name] @@ -16,5 +23,5 @@ actual fun getEnv(name: String) = process.env[name] actual suspend fun file(project: String, path: String): String = if (Platform.IS_NODE) nodeFile(project, path) else TODO("Browser JS is not supported yet") -actual suspend fun readFile(project: String, path: String): ByteReadChannel = +actual suspend fun readFile0(project: String, path: String): ByteReadChannel = if (Platform.IS_NODE) nodeReadFile(project, path) else TODO("Browser JS is not supported yet") diff --git a/test-kit/src/jvmMain/kotlin/Platform.kt b/test-kit/src/jvmMain/kotlin/Platform.kt index 85e5784737c..616692b9c31 100644 --- a/test-kit/src/jvmMain/kotlin/Platform.kt +++ b/test-kit/src/jvmMain/kotlin/Platform.kt @@ -1,3 +1,4 @@ +@file:JvmName("PlatformJvm") package dev.kord.test import io.ktor.utils.io.* @@ -6,9 +7,13 @@ import io.ktor.utils.io.jvm.javaio.* actual object Platform { actual const val IS_JVM: Boolean = true actual const val IS_NODE: Boolean = false + actual val IS_BROWSER: Boolean = false + actual val IS_MINGW: Boolean = false + actual val IS_LINUX: Boolean = false + actual val IS_DARWIN: Boolean = false } actual fun getEnv(name: String): String? = System.getenv(name) actual suspend fun file(project: String, path: String): String = ClassLoader.getSystemResource(path).readText() -actual suspend fun readFile(project: String, path: String): ByteReadChannel = +actual suspend fun readFile0(project: String, path: String): ByteReadChannel = ClassLoader.getSystemResourceAsStream(path)!!.toByteReadChannel() diff --git a/test-kit/src/nativeMain/kotlin/IgnoreOnNative.kt b/test-kit/src/nativeMain/kotlin/IgnoreOnNative.kt new file mode 100644 index 00000000000..db53b213dce --- /dev/null +++ b/test-kit/src/nativeMain/kotlin/IgnoreOnNative.kt @@ -0,0 +1,5 @@ +package dev.kord.test + +import kotlin.test.Ignore + +actual typealias IgnoreOnNative = Ignore diff --git a/test-kit/src/nativeMain/kotlin/Platform.kt b/test-kit/src/nativeMain/kotlin/Platform.kt new file mode 100644 index 00000000000..d15f5d0dbf7 --- /dev/null +++ b/test-kit/src/nativeMain/kotlin/Platform.kt @@ -0,0 +1,37 @@ +@file:OptIn(ExperimentalNativeApi::class) + +package dev.kord.test + +import io.ktor.utils.io.* +import kotlinx.cinterop.ExperimentalForeignApi +import kotlinx.cinterop.toKString +import kotlinx.io.* +import kotlinx.io.files.Path +import kotlinx.io.files.SystemFileSystem +import platform.posix.getenv +import kotlin.experimental.ExperimentalNativeApi +import kotlin.native.Platform + +private val darwinFamilies = listOf(OsFamily.WATCHOS, OsFamily.IOS, OsFamily.TVOS, OsFamily.MACOSX) + +actual object Platform { + actual val IS_JVM: Boolean = false + actual val IS_NODE: Boolean = false + actual val IS_BROWSER: Boolean = false + actual val IS_MINGW: Boolean = Platform.osFamily == OsFamily.WINDOWS + actual val IS_LINUX: Boolean = Platform.osFamily == OsFamily.LINUX + actual val IS_DARWIN: Boolean = Platform.osFamily in darwinFamilies +} + +@OptIn(ExperimentalForeignApi::class) +actual fun getEnv(name: String) = getenv(name)?.toKString() + +actual suspend fun file(project: String, path: String): String = read(project, path, Source::readString) + +actual suspend fun readFile0(project: String, path: String): ByteReadChannel = + read(project, path) { ByteReadChannel(readByteArray()) } + +private inline fun read(project: String, path: String, readerAction: Source.() -> T): T { + val actualPath = Path("${getEnv("PROJECT_ROOT")}/$project/src/commonTest/resources/$path") + return SystemFileSystem.source(actualPath).buffered().readerAction() +} diff --git a/test-kit/src/simulatorMain/kotlin/IgnoreOnSimulatorPlatforms.kt b/test-kit/src/simulatorMain/kotlin/IgnoreOnSimulatorPlatforms.kt new file mode 100644 index 00000000000..f0b4f1a3624 --- /dev/null +++ b/test-kit/src/simulatorMain/kotlin/IgnoreOnSimulatorPlatforms.kt @@ -0,0 +1,5 @@ +package dev.kord.test + +import kotlin.test.Ignore + +actual typealias IgnoreOnSimulatorPlatforms = Ignore diff --git a/voice/api/voice.api b/voice/api/voice.api index 6680afe9661..59f0ab8da34 100644 --- a/voice/api/voice.api +++ b/voice/api/voice.api @@ -317,7 +317,7 @@ public final class dev/kord/voice/SpeakingFlags$Companion { } public final class dev/kord/voice/VoiceConnection { - public synthetic fun (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 (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; @@ -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; @@ -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 @@ -988,7 +990,6 @@ public final class dev/kord/voice/gateway/VoiceGatewayConfiguration { } public final class dev/kord/voice/gateway/VoiceGatewayKt { - public static final synthetic fun getVoiceGatewayOnLogger ()Lmu/KLogger; public static final fun logCaughtThrowable (Ljava/lang/Throwable;)V } @@ -1069,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 { @@ -1078,7 +1079,7 @@ 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 { @@ -1086,7 +1087,7 @@ public abstract interface class dev/kord/voice/streams/Streams { 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 { @@ -1094,17 +1095,17 @@ public abstract interface class dev/kord/voice/udp/AudioFrameSender { } public final class dev/kord/voice/udp/AudioFrameSenderConfiguration { - public synthetic fun (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 (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; @@ -1140,17 +1141,17 @@ public final class dev/kord/voice/udp/DefaultAudioFrameSenderData { public fun toString ()Ljava/lang/String; } -public final class dev/kord/voice/udp/DefaultAudioPacketProvider : dev/kord/voice/udp/AudioPacketProvider { +public final class dev/kord/voice/udp/DefaultAudioPacketProviderKt { + public static final fun DefaultAudioPacketProvider ([BLdev/kord/voice/encryption/strategies/NonceStrategy;)Ldev/kord/voice/udp/AudioPacketProvider; +} + +public final class dev/kord/voice/udp/DefaultJvmAudioPacketProvider : dev/kord/voice/udp/AudioPacketProvider { public fun ([BLdev/kord/voice/encryption/strategies/NonceStrategy;)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 { @@ -1173,6 +1174,7 @@ public final class dev/kord/voice/udp/PayloadType$Companion { public final class dev/kord/voice/udp/PayloadType$Unknown : dev/kord/voice/udp/PayloadType { public fun (B)V + public fun toString ()Ljava/lang/String; } public final class dev/kord/voice/udp/RTPPacket { @@ -1242,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; } @@ -1253,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_ktorKt { + public static final fun getGlobalVoiceUdpSocket ()Ldev/kord/voice/udp/VoiceUdpSocket; } diff --git a/voice/api/voice.klib.api b/voice/api/voice.klib.api new file mode 100644 index 00000000000..f6b8cca3386 --- /dev/null +++ b/voice/api/voice.klib.api @@ -0,0 +1,1257 @@ +// Klib ABI Dump +// Targets: [iosArm64, iosSimulatorArm64, iosX64, js, linuxArm64, linuxX64, macosArm64, macosX64, mingwX64, tvosArm64, tvosSimulatorArm64, tvosX64, watchosArm64, watchosSimulatorArm64] +// Alias: native => [iosArm64, iosSimulatorArm64, iosX64, linuxArm64, linuxX64, macosArm64, macosX64, mingwX64, tvosArm64, tvosSimulatorArm64, tvosX64, watchosArm64, watchosSimulatorArm64] +// Alias: apple => [iosArm64, iosSimulatorArm64, iosX64, macosArm64, macosX64, tvosArm64, tvosSimulatorArm64, tvosX64, watchosArm64, watchosSimulatorArm64] +// Rendering settings: +// - Signature version: 2 +// - Show manifest properties: true +// - Show declarations: true + +// Library unique name: +final enum class dev.kord.voice.gateway/OpCode : kotlin/Enum { // dev.kord.voice.gateway/OpCode|null[0] + enum entry ClientDisconnect // dev.kord.voice.gateway/OpCode.ClientDisconnect|null[0] + enum entry Heartbeat // dev.kord.voice.gateway/OpCode.Heartbeat|null[0] + enum entry HeartbeatAck // dev.kord.voice.gateway/OpCode.HeartbeatAck|null[0] + enum entry Hello // dev.kord.voice.gateway/OpCode.Hello|null[0] + enum entry Identify // dev.kord.voice.gateway/OpCode.Identify|null[0] + enum entry Ready // dev.kord.voice.gateway/OpCode.Ready|null[0] + enum entry Resume // dev.kord.voice.gateway/OpCode.Resume|null[0] + enum entry Resumed // dev.kord.voice.gateway/OpCode.Resumed|null[0] + enum entry SelectProtocol // dev.kord.voice.gateway/OpCode.SelectProtocol|null[0] + enum entry SessionDescription // dev.kord.voice.gateway/OpCode.SessionDescription|null[0] + enum entry Speaking // dev.kord.voice.gateway/OpCode.Speaking|null[0] + enum entry Unknown // dev.kord.voice.gateway/OpCode.Unknown|null[0] + + final val code // dev.kord.voice.gateway/OpCode.code|{}code[0] + final fun (): kotlin/Int // dev.kord.voice.gateway/OpCode.code.|(){}[0] + final val entries // dev.kord.voice.gateway/OpCode.entries|#static{}entries[0] + final fun (): kotlin.enums/EnumEntries // dev.kord.voice.gateway/OpCode.entries.|#static(){}[0] + + final fun valueOf(kotlin/String): dev.kord.voice.gateway/OpCode // dev.kord.voice.gateway/OpCode.valueOf|valueOf#static(kotlin.String){}[0] + final fun values(): kotlin/Array // dev.kord.voice.gateway/OpCode.values|values#static(){}[0] + + final object Companion : kotlinx.serialization.internal/SerializerFactory { // dev.kord.voice.gateway/OpCode.Companion|null[0] + final fun serializer(): kotlinx.serialization/KSerializer // dev.kord.voice.gateway/OpCode.Companion.serializer|serializer(){}[0] + final fun serializer(kotlin/Array>...): kotlinx.serialization/KSerializer<*> // dev.kord.voice.gateway/OpCode.Companion.serializer|serializer(kotlin.Array>...){}[0] + } +} + +abstract fun interface dev.kord.voice/AudioProvider { // dev.kord.voice/AudioProvider|null[0] + abstract suspend fun provide(): dev.kord.voice/AudioFrame? // dev.kord.voice/AudioProvider.provide|provide(){}[0] + open suspend fun (kotlinx.coroutines/CoroutineScope).provideFrames(kotlinx.coroutines.channels/SendChannel) // dev.kord.voice/AudioProvider.provideFrames|provideFrames@kotlinx.coroutines.CoroutineScope(kotlinx.coroutines.channels.SendChannel){}[0] +} + +abstract fun interface dev.kord.voice/FrameInterceptor { // dev.kord.voice/FrameInterceptor|null[0] + abstract fun (kotlinx.coroutines.flow/Flow).intercept(dev.kord.voice/FrameInterceptorConfiguration): kotlinx.coroutines.flow/Flow // dev.kord.voice/FrameInterceptor.intercept|intercept@kotlinx.coroutines.flow.Flow(dev.kord.voice.FrameInterceptorConfiguration){}[0] +} + +abstract interface dev.kord.voice.gateway/VoiceGateway { // dev.kord.voice.gateway/VoiceGateway|null[0] + abstract val events // dev.kord.voice.gateway/VoiceGateway.events|{}events[0] + abstract fun (): kotlinx.coroutines.flow/SharedFlow // dev.kord.voice.gateway/VoiceGateway.events.|(){}[0] + abstract val ping // dev.kord.voice.gateway/VoiceGateway.ping|{}ping[0] + abstract fun (): kotlinx.coroutines.flow/StateFlow // dev.kord.voice.gateway/VoiceGateway.ping.|(){}[0] + abstract val scope // dev.kord.voice.gateway/VoiceGateway.scope|{}scope[0] + abstract fun (): kotlinx.coroutines/CoroutineScope // dev.kord.voice.gateway/VoiceGateway.scope.|(){}[0] + + abstract suspend fun detach() // dev.kord.voice.gateway/VoiceGateway.detach|detach(){}[0] + abstract suspend fun send(dev.kord.voice.gateway/Command) // dev.kord.voice.gateway/VoiceGateway.send|send(dev.kord.voice.gateway.Command){}[0] + abstract suspend fun start(dev.kord.voice.gateway/VoiceGatewayConfiguration) // dev.kord.voice.gateway/VoiceGateway.start|start(dev.kord.voice.gateway.VoiceGatewayConfiguration){}[0] + abstract suspend fun stop() // dev.kord.voice.gateway/VoiceGateway.stop|stop(){}[0] + + final object Companion { // dev.kord.voice.gateway/VoiceGateway.Companion|null[0] + final fun none(): dev.kord.voice.gateway/VoiceGateway // dev.kord.voice.gateway/VoiceGateway.Companion.none|none(){}[0] + } +} + +abstract interface dev.kord.voice.streams/Streams { // dev.kord.voice.streams/Streams|null[0] + abstract val incomingAudioFrames // dev.kord.voice.streams/Streams.incomingAudioFrames|{}incomingAudioFrames[0] + abstract fun (): kotlinx.coroutines.flow/Flow> // dev.kord.voice.streams/Streams.incomingAudioFrames.|(){}[0] + abstract val incomingAudioPackets // dev.kord.voice.streams/Streams.incomingAudioPackets|{}incomingAudioPackets[0] + abstract fun (): kotlinx.coroutines.flow/Flow // dev.kord.voice.streams/Streams.incomingAudioPackets.|(){}[0] + abstract val incomingUserStreams // dev.kord.voice.streams/Streams.incomingUserStreams|{}incomingUserStreams[0] + abstract fun (): kotlinx.coroutines.flow/Flow> // dev.kord.voice.streams/Streams.incomingUserStreams.|(){}[0] + abstract val ssrcToUser // dev.kord.voice.streams/Streams.ssrcToUser|{}ssrcToUser[0] + abstract fun (): kotlin.collections/Map // dev.kord.voice.streams/Streams.ssrcToUser.|(){}[0] + + // Targets: [native] + abstract suspend fun listen(kotlin/ByteArray, io.ktor.network.sockets/InetSocketAddress) // dev.kord.voice.streams/Streams.listen|listen(kotlin.ByteArray;io.ktor.network.sockets.InetSocketAddress){}[0] + + // Targets: [js] + abstract suspend fun listen(kotlin/ByteArray, dev.kord.voice.udp/SocketAddress) // dev.kord.voice.streams/Streams.listen|listen(kotlin.ByteArray;dev.kord.voice.udp.SocketAddress){}[0] +} + +abstract interface dev.kord.voice.udp/AudioFrameSender { // dev.kord.voice.udp/AudioFrameSender|null[0] + abstract suspend fun start(dev.kord.voice.udp/AudioFrameSenderConfiguration) // dev.kord.voice.udp/AudioFrameSender.start|start(dev.kord.voice.udp.AudioFrameSenderConfiguration){}[0] +} + +abstract interface dev.kord.voice.udp/VoiceUdpSocket { // dev.kord.voice.udp/VoiceUdpSocket|null[0] + abstract suspend fun stop() // dev.kord.voice.udp/VoiceUdpSocket.stop|stop(){}[0] + + final object Companion { // dev.kord.voice.udp/VoiceUdpSocket.Companion|null[0] + final fun none(): dev.kord.voice.udp/VoiceUdpSocket // dev.kord.voice.udp/VoiceUdpSocket.Companion.none|none(){}[0] + } + + // Targets: [native] + abstract fun all(io.ktor.network.sockets/InetSocketAddress): kotlinx.coroutines.flow/Flow // dev.kord.voice.udp/VoiceUdpSocket.all|all(io.ktor.network.sockets.InetSocketAddress){}[0] + + // Targets: [native] + abstract suspend fun send(io.ktor.network.sockets/InetSocketAddress, dev.kord.voice.io/ByteArrayView) // dev.kord.voice.udp/VoiceUdpSocket.send|send(io.ktor.network.sockets.InetSocketAddress;dev.kord.voice.io.ByteArrayView){}[0] + + // Targets: [apple] + abstract fun all(io.ktor.network.sockets/InetSocketAddress): kotlinx.coroutines.flow/Flow // dev.kord.voice.udp/VoiceUdpSocket.all|all(io.ktor.network.sockets.InetSocketAddress){}[0] + + // Targets: [js] + abstract fun all(dev.kord.voice.udp/SocketAddress): kotlinx.coroutines.flow/Flow // dev.kord.voice.udp/VoiceUdpSocket.all|all(dev.kord.voice.udp.SocketAddress){}[0] + + // Targets: [js] + abstract suspend fun send(dev.kord.voice.udp/SocketAddress, dev.kord.voice.io/ByteArrayView) // dev.kord.voice.udp/VoiceUdpSocket.send|send(dev.kord.voice.udp.SocketAddress;dev.kord.voice.io.ByteArrayView){}[0] +} + +sealed interface dev.kord.voice.encryption.strategies/NonceStrategy { // dev.kord.voice.encryption.strategies/NonceStrategy|null[0] + abstract val nonceLength // dev.kord.voice.encryption.strategies/NonceStrategy.nonceLength|{}nonceLength[0] + abstract fun (): kotlin/Int // dev.kord.voice.encryption.strategies/NonceStrategy.nonceLength.|(){}[0] + + abstract fun append(dev.kord.voice.io/ByteArrayView, dev.kord.voice.io/MutableByteArrayCursor) // dev.kord.voice.encryption.strategies/NonceStrategy.append|append(dev.kord.voice.io.ByteArrayView;dev.kord.voice.io.MutableByteArrayCursor){}[0] + abstract fun generate(kotlin/Function0): dev.kord.voice.io/ByteArrayView // dev.kord.voice.encryption.strategies/NonceStrategy.generate|generate(kotlin.Function0){}[0] + abstract fun strip(dev.kord.voice.udp/RTPPacket): dev.kord.voice.io/ByteArrayView // dev.kord.voice.encryption.strategies/NonceStrategy.strip|strip(dev.kord.voice.udp.RTPPacket){}[0] +} + +abstract class dev.kord.voice.udp/AudioPacketProvider { // dev.kord.voice.udp/AudioPacketProvider|null[0] + constructor (kotlin/ByteArray, dev.kord.voice.encryption.strategies/NonceStrategy) // dev.kord.voice.udp/AudioPacketProvider.|(kotlin.ByteArray;dev.kord.voice.encryption.strategies.NonceStrategy){}[0] + + final val key // dev.kord.voice.udp/AudioPacketProvider.key|{}key[0] + final fun (): kotlin/ByteArray // dev.kord.voice.udp/AudioPacketProvider.key.|(){}[0] + final val nonceStrategy // dev.kord.voice.udp/AudioPacketProvider.nonceStrategy|{}nonceStrategy[0] + final fun (): dev.kord.voice.encryption.strategies/NonceStrategy // dev.kord.voice.udp/AudioPacketProvider.nonceStrategy.|(){}[0] + + abstract fun provide(kotlin/UShort, kotlin/UInt, kotlin/UInt, kotlin/ByteArray): dev.kord.voice.io/ByteArrayView // dev.kord.voice.udp/AudioPacketProvider.provide|provide(kotlin.UShort;kotlin.UInt;kotlin.UInt;kotlin.ByteArray){}[0] +} + +final class dev.kord.voice.encryption.strategies/LiteNonceStrategy : dev.kord.voice.encryption.strategies/NonceStrategy { // dev.kord.voice.encryption.strategies/LiteNonceStrategy|null[0] + constructor () // dev.kord.voice.encryption.strategies/LiteNonceStrategy.|(){}[0] + + final val nonceLength // dev.kord.voice.encryption.strategies/LiteNonceStrategy.nonceLength|{}nonceLength[0] + final fun (): kotlin/Int // dev.kord.voice.encryption.strategies/LiteNonceStrategy.nonceLength.|(){}[0] + + final fun append(dev.kord.voice.io/ByteArrayView, dev.kord.voice.io/MutableByteArrayCursor) // dev.kord.voice.encryption.strategies/LiteNonceStrategy.append|append(dev.kord.voice.io.ByteArrayView;dev.kord.voice.io.MutableByteArrayCursor){}[0] + final fun generate(kotlin/Function0): dev.kord.voice.io/ByteArrayView // dev.kord.voice.encryption.strategies/LiteNonceStrategy.generate|generate(kotlin.Function0){}[0] + final fun strip(dev.kord.voice.udp/RTPPacket): dev.kord.voice.io/ByteArrayView // dev.kord.voice.encryption.strategies/LiteNonceStrategy.strip|strip(dev.kord.voice.udp.RTPPacket){}[0] +} + +final class dev.kord.voice.encryption.strategies/NormalNonceStrategy : dev.kord.voice.encryption.strategies/NonceStrategy { // dev.kord.voice.encryption.strategies/NormalNonceStrategy|null[0] + constructor () // dev.kord.voice.encryption.strategies/NormalNonceStrategy.|(){}[0] + + final val nonceLength // dev.kord.voice.encryption.strategies/NormalNonceStrategy.nonceLength|{}nonceLength[0] + final fun (): kotlin/Int // dev.kord.voice.encryption.strategies/NormalNonceStrategy.nonceLength.|(){}[0] + + final fun append(dev.kord.voice.io/ByteArrayView, dev.kord.voice.io/MutableByteArrayCursor) // dev.kord.voice.encryption.strategies/NormalNonceStrategy.append|append(dev.kord.voice.io.ByteArrayView;dev.kord.voice.io.MutableByteArrayCursor){}[0] + final fun generate(kotlin/Function0): dev.kord.voice.io/ByteArrayView // dev.kord.voice.encryption.strategies/NormalNonceStrategy.generate|generate(kotlin.Function0){}[0] + final fun strip(dev.kord.voice.udp/RTPPacket): dev.kord.voice.io/ByteArrayView // dev.kord.voice.encryption.strategies/NormalNonceStrategy.strip|strip(dev.kord.voice.udp.RTPPacket){}[0] +} + +final class dev.kord.voice.encryption.strategies/SuffixNonceStrategy : dev.kord.voice.encryption.strategies/NonceStrategy { // dev.kord.voice.encryption.strategies/SuffixNonceStrategy|null[0] + constructor () // dev.kord.voice.encryption.strategies/SuffixNonceStrategy.|(){}[0] + + final val nonceLength // dev.kord.voice.encryption.strategies/SuffixNonceStrategy.nonceLength|{}nonceLength[0] + final fun (): kotlin/Int // dev.kord.voice.encryption.strategies/SuffixNonceStrategy.nonceLength.|(){}[0] + + final fun append(dev.kord.voice.io/ByteArrayView, dev.kord.voice.io/MutableByteArrayCursor) // dev.kord.voice.encryption.strategies/SuffixNonceStrategy.append|append(dev.kord.voice.io.ByteArrayView;dev.kord.voice.io.MutableByteArrayCursor){}[0] + final fun generate(kotlin/Function0): dev.kord.voice.io/ByteArrayView // dev.kord.voice.encryption.strategies/SuffixNonceStrategy.generate|generate(kotlin.Function0){}[0] + final fun strip(dev.kord.voice.udp/RTPPacket): dev.kord.voice.io/ByteArrayView // dev.kord.voice.encryption.strategies/SuffixNonceStrategy.strip|strip(dev.kord.voice.udp.RTPPacket){}[0] +} + +final class dev.kord.voice.exception/VoiceConnectionInitializationException : kotlin/Exception { // dev.kord.voice.exception/VoiceConnectionInitializationException|null[0] + constructor (kotlin/String) // dev.kord.voice.exception/VoiceConnectionInitializationException.|(kotlin.String){}[0] + constructor (kotlin/String, kotlin/Throwable) // dev.kord.voice.exception/VoiceConnectionInitializationException.|(kotlin.String;kotlin.Throwable){}[0] + constructor (kotlin/Throwable) // dev.kord.voice.exception/VoiceConnectionInitializationException.|(kotlin.Throwable){}[0] +} + +final class dev.kord.voice.gateway/DefaultVoiceGateway : dev.kord.voice.gateway/VoiceGateway { // dev.kord.voice.gateway/DefaultVoiceGateway|null[0] + constructor (dev.kord.voice.gateway/DefaultVoiceGatewayData) // dev.kord.voice.gateway/DefaultVoiceGateway.|(dev.kord.voice.gateway.DefaultVoiceGatewayData){}[0] + + final val events // dev.kord.voice.gateway/DefaultVoiceGateway.events|{}events[0] + final fun (): kotlinx.coroutines.flow/SharedFlow // dev.kord.voice.gateway/DefaultVoiceGateway.events.|(){}[0] + final val ping // dev.kord.voice.gateway/DefaultVoiceGateway.ping|{}ping[0] + final fun (): kotlinx.coroutines.flow/StateFlow // dev.kord.voice.gateway/DefaultVoiceGateway.ping.|(){}[0] + final val scope // dev.kord.voice.gateway/DefaultVoiceGateway.scope|{}scope[0] + final fun (): kotlinx.coroutines/CoroutineScope // dev.kord.voice.gateway/DefaultVoiceGateway.scope.|(){}[0] + + final suspend fun detach() // dev.kord.voice.gateway/DefaultVoiceGateway.detach|detach(){}[0] + final suspend fun send(dev.kord.voice.gateway/Command) // dev.kord.voice.gateway/DefaultVoiceGateway.send|send(dev.kord.voice.gateway.Command){}[0] + final suspend fun start(dev.kord.voice.gateway/VoiceGatewayConfiguration) // dev.kord.voice.gateway/DefaultVoiceGateway.start|start(dev.kord.voice.gateway.VoiceGatewayConfiguration){}[0] + final suspend fun stop() // dev.kord.voice.gateway/DefaultVoiceGateway.stop|stop(){}[0] +} + +final class dev.kord.voice.gateway/DefaultVoiceGatewayBuilder { // dev.kord.voice.gateway/DefaultVoiceGatewayBuilder|null[0] + constructor (dev.kord.common.entity/Snowflake, dev.kord.common.entity/Snowflake, kotlin/String) // dev.kord.voice.gateway/DefaultVoiceGatewayBuilder.|(dev.kord.common.entity.Snowflake;dev.kord.common.entity.Snowflake;kotlin.String){}[0] + + final val guildId // dev.kord.voice.gateway/DefaultVoiceGatewayBuilder.guildId|{}guildId[0] + final fun (): dev.kord.common.entity/Snowflake // dev.kord.voice.gateway/DefaultVoiceGatewayBuilder.guildId.|(){}[0] + final val selfId // dev.kord.voice.gateway/DefaultVoiceGatewayBuilder.selfId|{}selfId[0] + final fun (): dev.kord.common.entity/Snowflake // dev.kord.voice.gateway/DefaultVoiceGatewayBuilder.selfId.|(){}[0] + final val sessionId // dev.kord.voice.gateway/DefaultVoiceGatewayBuilder.sessionId|{}sessionId[0] + final fun (): kotlin/String // dev.kord.voice.gateway/DefaultVoiceGatewayBuilder.sessionId.|(){}[0] + + final var client // dev.kord.voice.gateway/DefaultVoiceGatewayBuilder.client|{}client[0] + final fun (): io.ktor.client/HttpClient? // dev.kord.voice.gateway/DefaultVoiceGatewayBuilder.client.|(){}[0] + final fun (io.ktor.client/HttpClient?) // dev.kord.voice.gateway/DefaultVoiceGatewayBuilder.client.|(io.ktor.client.HttpClient?){}[0] + final var eventFlow // dev.kord.voice.gateway/DefaultVoiceGatewayBuilder.eventFlow|{}eventFlow[0] + final fun (): kotlinx.coroutines.flow/MutableSharedFlow // dev.kord.voice.gateway/DefaultVoiceGatewayBuilder.eventFlow.|(){}[0] + final fun (kotlinx.coroutines.flow/MutableSharedFlow) // dev.kord.voice.gateway/DefaultVoiceGatewayBuilder.eventFlow.|(kotlinx.coroutines.flow.MutableSharedFlow){}[0] + final var reconnectRetry // dev.kord.voice.gateway/DefaultVoiceGatewayBuilder.reconnectRetry|{}reconnectRetry[0] + final fun (): dev.kord.gateway.retry/Retry? // dev.kord.voice.gateway/DefaultVoiceGatewayBuilder.reconnectRetry.|(){}[0] + final fun (dev.kord.gateway.retry/Retry?) // dev.kord.voice.gateway/DefaultVoiceGatewayBuilder.reconnectRetry.|(dev.kord.gateway.retry.Retry?){}[0] + + final fun build(): dev.kord.voice.gateway/DefaultVoiceGateway // dev.kord.voice.gateway/DefaultVoiceGatewayBuilder.build|build(){}[0] +} + +final class dev.kord.voice.gateway/DefaultVoiceGatewayData { // dev.kord.voice.gateway/DefaultVoiceGatewayData|null[0] + constructor (dev.kord.common.entity/Snowflake, dev.kord.common.entity/Snowflake, kotlin/String, io.ktor.client/HttpClient, dev.kord.gateway.retry/Retry, kotlinx.coroutines.flow/MutableSharedFlow) // dev.kord.voice.gateway/DefaultVoiceGatewayData.|(dev.kord.common.entity.Snowflake;dev.kord.common.entity.Snowflake;kotlin.String;io.ktor.client.HttpClient;dev.kord.gateway.retry.Retry;kotlinx.coroutines.flow.MutableSharedFlow){}[0] + + final val client // dev.kord.voice.gateway/DefaultVoiceGatewayData.client|{}client[0] + final fun (): io.ktor.client/HttpClient // dev.kord.voice.gateway/DefaultVoiceGatewayData.client.|(){}[0] + final val eventFlow // dev.kord.voice.gateway/DefaultVoiceGatewayData.eventFlow|{}eventFlow[0] + final fun (): kotlinx.coroutines.flow/MutableSharedFlow // dev.kord.voice.gateway/DefaultVoiceGatewayData.eventFlow.|(){}[0] + final val guildId // dev.kord.voice.gateway/DefaultVoiceGatewayData.guildId|{}guildId[0] + final fun (): dev.kord.common.entity/Snowflake // dev.kord.voice.gateway/DefaultVoiceGatewayData.guildId.|(){}[0] + final val reconnectRetry // dev.kord.voice.gateway/DefaultVoiceGatewayData.reconnectRetry|{}reconnectRetry[0] + final fun (): dev.kord.gateway.retry/Retry // dev.kord.voice.gateway/DefaultVoiceGatewayData.reconnectRetry.|(){}[0] + final val selfId // dev.kord.voice.gateway/DefaultVoiceGatewayData.selfId|{}selfId[0] + final fun (): dev.kord.common.entity/Snowflake // dev.kord.voice.gateway/DefaultVoiceGatewayData.selfId.|(){}[0] + final val sessionId // dev.kord.voice.gateway/DefaultVoiceGatewayData.sessionId|{}sessionId[0] + final fun (): kotlin/String // dev.kord.voice.gateway/DefaultVoiceGatewayData.sessionId.|(){}[0] + + final fun component1(): dev.kord.common.entity/Snowflake // dev.kord.voice.gateway/DefaultVoiceGatewayData.component1|component1(){}[0] + final fun component2(): dev.kord.common.entity/Snowflake // dev.kord.voice.gateway/DefaultVoiceGatewayData.component2|component2(){}[0] + final fun component3(): kotlin/String // dev.kord.voice.gateway/DefaultVoiceGatewayData.component3|component3(){}[0] + final fun component4(): io.ktor.client/HttpClient // dev.kord.voice.gateway/DefaultVoiceGatewayData.component4|component4(){}[0] + final fun component5(): dev.kord.gateway.retry/Retry // dev.kord.voice.gateway/DefaultVoiceGatewayData.component5|component5(){}[0] + final fun component6(): kotlinx.coroutines.flow/MutableSharedFlow // dev.kord.voice.gateway/DefaultVoiceGatewayData.component6|component6(){}[0] + final fun copy(dev.kord.common.entity/Snowflake = ..., dev.kord.common.entity/Snowflake = ..., kotlin/String = ..., io.ktor.client/HttpClient = ..., dev.kord.gateway.retry/Retry = ..., kotlinx.coroutines.flow/MutableSharedFlow = ...): dev.kord.voice.gateway/DefaultVoiceGatewayData // dev.kord.voice.gateway/DefaultVoiceGatewayData.copy|copy(dev.kord.common.entity.Snowflake;dev.kord.common.entity.Snowflake;kotlin.String;io.ktor.client.HttpClient;dev.kord.gateway.retry.Retry;kotlinx.coroutines.flow.MutableSharedFlow){}[0] + final fun equals(kotlin/Any?): kotlin/Boolean // dev.kord.voice.gateway/DefaultVoiceGatewayData.equals|equals(kotlin.Any?){}[0] + final fun hashCode(): kotlin/Int // dev.kord.voice.gateway/DefaultVoiceGatewayData.hashCode|hashCode(){}[0] + final fun toString(): kotlin/String // dev.kord.voice.gateway/DefaultVoiceGatewayData.toString|toString(){}[0] +} + +final class dev.kord.voice.gateway/Heartbeat : dev.kord.voice.gateway/Command { // dev.kord.voice.gateway/Heartbeat|null[0] + constructor (kotlin/Long) // dev.kord.voice.gateway/Heartbeat.|(kotlin.Long){}[0] + + final val nonce // dev.kord.voice.gateway/Heartbeat.nonce|{}nonce[0] + final fun (): kotlin/Long // dev.kord.voice.gateway/Heartbeat.nonce.|(){}[0] + + final fun component1(): kotlin/Long // dev.kord.voice.gateway/Heartbeat.component1|component1(){}[0] + final fun copy(kotlin/Long = ...): dev.kord.voice.gateway/Heartbeat // dev.kord.voice.gateway/Heartbeat.copy|copy(kotlin.Long){}[0] + final fun equals(kotlin/Any?): kotlin/Boolean // dev.kord.voice.gateway/Heartbeat.equals|equals(kotlin.Any?){}[0] + final fun hashCode(): kotlin/Int // dev.kord.voice.gateway/Heartbeat.hashCode|hashCode(){}[0] + final fun toString(): kotlin/String // dev.kord.voice.gateway/Heartbeat.toString|toString(){}[0] + + final object $serializer : kotlinx.serialization.internal/GeneratedSerializer { // dev.kord.voice.gateway/Heartbeat.$serializer|null[0] + final val descriptor // dev.kord.voice.gateway/Heartbeat.$serializer.descriptor|{}descriptor[0] + final fun (): kotlinx.serialization.descriptors/SerialDescriptor // dev.kord.voice.gateway/Heartbeat.$serializer.descriptor.|(){}[0] + + final fun childSerializers(): kotlin/Array> // dev.kord.voice.gateway/Heartbeat.$serializer.childSerializers|childSerializers(){}[0] + final fun deserialize(kotlinx.serialization.encoding/Decoder): dev.kord.voice.gateway/Heartbeat // dev.kord.voice.gateway/Heartbeat.$serializer.deserialize|deserialize(kotlinx.serialization.encoding.Decoder){}[0] + final fun serialize(kotlinx.serialization.encoding/Encoder, dev.kord.voice.gateway/Heartbeat) // dev.kord.voice.gateway/Heartbeat.$serializer.serialize|serialize(kotlinx.serialization.encoding.Encoder;dev.kord.voice.gateway.Heartbeat){}[0] + } + + final object Companion { // dev.kord.voice.gateway/Heartbeat.Companion|null[0] + final fun serializer(): kotlinx.serialization/KSerializer // dev.kord.voice.gateway/Heartbeat.Companion.serializer|serializer(){}[0] + } +} + +final class dev.kord.voice.gateway/HeartbeatAck : dev.kord.voice.gateway/VoiceEvent { // dev.kord.voice.gateway/HeartbeatAck|null[0] + constructor (kotlin/Long) // dev.kord.voice.gateway/HeartbeatAck.|(kotlin.Long){}[0] + + final val nonce // dev.kord.voice.gateway/HeartbeatAck.nonce|{}nonce[0] + final fun (): kotlin/Long // dev.kord.voice.gateway/HeartbeatAck.nonce.|(){}[0] + + final fun component1(): kotlin/Long // dev.kord.voice.gateway/HeartbeatAck.component1|component1(){}[0] + final fun copy(kotlin/Long = ...): dev.kord.voice.gateway/HeartbeatAck // dev.kord.voice.gateway/HeartbeatAck.copy|copy(kotlin.Long){}[0] + final fun equals(kotlin/Any?): kotlin/Boolean // dev.kord.voice.gateway/HeartbeatAck.equals|equals(kotlin.Any?){}[0] + final fun hashCode(): kotlin/Int // dev.kord.voice.gateway/HeartbeatAck.hashCode|hashCode(){}[0] + final fun toString(): kotlin/String // dev.kord.voice.gateway/HeartbeatAck.toString|toString(){}[0] + + final object Companion { // dev.kord.voice.gateway/HeartbeatAck.Companion|null[0] + final fun serializer(): kotlinx.serialization/KSerializer // dev.kord.voice.gateway/HeartbeatAck.Companion.serializer|serializer(){}[0] + } +} + +final class dev.kord.voice.gateway/Hello : dev.kord.voice.gateway/VoiceEvent { // dev.kord.voice.gateway/Hello|null[0] + constructor (kotlin/Short, kotlin/Double) // dev.kord.voice.gateway/Hello.|(kotlin.Short;kotlin.Double){}[0] + + final val heartbeatInterval // dev.kord.voice.gateway/Hello.heartbeatInterval|{}heartbeatInterval[0] + final fun (): kotlin/Double // dev.kord.voice.gateway/Hello.heartbeatInterval.|(){}[0] + final val version // dev.kord.voice.gateway/Hello.version|{}version[0] + final fun (): kotlin/Short // dev.kord.voice.gateway/Hello.version.|(){}[0] + + final fun component1(): kotlin/Short // dev.kord.voice.gateway/Hello.component1|component1(){}[0] + final fun component2(): kotlin/Double // dev.kord.voice.gateway/Hello.component2|component2(){}[0] + final fun copy(kotlin/Short = ..., kotlin/Double = ...): dev.kord.voice.gateway/Hello // dev.kord.voice.gateway/Hello.copy|copy(kotlin.Short;kotlin.Double){}[0] + final fun equals(kotlin/Any?): kotlin/Boolean // dev.kord.voice.gateway/Hello.equals|equals(kotlin.Any?){}[0] + final fun hashCode(): kotlin/Int // dev.kord.voice.gateway/Hello.hashCode|hashCode(){}[0] + final fun toString(): kotlin/String // dev.kord.voice.gateway/Hello.toString|toString(){}[0] + + final object $serializer : kotlinx.serialization.internal/GeneratedSerializer { // dev.kord.voice.gateway/Hello.$serializer|null[0] + final val descriptor // dev.kord.voice.gateway/Hello.$serializer.descriptor|{}descriptor[0] + final fun (): kotlinx.serialization.descriptors/SerialDescriptor // dev.kord.voice.gateway/Hello.$serializer.descriptor.|(){}[0] + + final fun childSerializers(): kotlin/Array> // dev.kord.voice.gateway/Hello.$serializer.childSerializers|childSerializers(){}[0] + final fun deserialize(kotlinx.serialization.encoding/Decoder): dev.kord.voice.gateway/Hello // dev.kord.voice.gateway/Hello.$serializer.deserialize|deserialize(kotlinx.serialization.encoding.Decoder){}[0] + final fun serialize(kotlinx.serialization.encoding/Encoder, dev.kord.voice.gateway/Hello) // dev.kord.voice.gateway/Hello.$serializer.serialize|serialize(kotlinx.serialization.encoding.Encoder;dev.kord.voice.gateway.Hello){}[0] + } + + final object Companion { // dev.kord.voice.gateway/Hello.Companion|null[0] + final fun serializer(): kotlinx.serialization/KSerializer // dev.kord.voice.gateway/Hello.Companion.serializer|serializer(){}[0] + } +} + +final class dev.kord.voice.gateway/Identify : dev.kord.voice.gateway/Command { // dev.kord.voice.gateway/Identify|null[0] + constructor (dev.kord.common.entity/Snowflake, dev.kord.common.entity/Snowflake, kotlin/String, kotlin/String) // dev.kord.voice.gateway/Identify.|(dev.kord.common.entity.Snowflake;dev.kord.common.entity.Snowflake;kotlin.String;kotlin.String){}[0] + + final val serverId // dev.kord.voice.gateway/Identify.serverId|{}serverId[0] + final fun (): dev.kord.common.entity/Snowflake // dev.kord.voice.gateway/Identify.serverId.|(){}[0] + final val sessionId // dev.kord.voice.gateway/Identify.sessionId|{}sessionId[0] + final fun (): kotlin/String // dev.kord.voice.gateway/Identify.sessionId.|(){}[0] + final val token // dev.kord.voice.gateway/Identify.token|{}token[0] + final fun (): kotlin/String // dev.kord.voice.gateway/Identify.token.|(){}[0] + final val userId // dev.kord.voice.gateway/Identify.userId|{}userId[0] + final fun (): dev.kord.common.entity/Snowflake // dev.kord.voice.gateway/Identify.userId.|(){}[0] + + final fun component1(): dev.kord.common.entity/Snowflake // dev.kord.voice.gateway/Identify.component1|component1(){}[0] + final fun component2(): dev.kord.common.entity/Snowflake // dev.kord.voice.gateway/Identify.component2|component2(){}[0] + final fun component3(): kotlin/String // dev.kord.voice.gateway/Identify.component3|component3(){}[0] + final fun component4(): kotlin/String // dev.kord.voice.gateway/Identify.component4|component4(){}[0] + final fun copy(dev.kord.common.entity/Snowflake = ..., dev.kord.common.entity/Snowflake = ..., kotlin/String = ..., kotlin/String = ...): dev.kord.voice.gateway/Identify // dev.kord.voice.gateway/Identify.copy|copy(dev.kord.common.entity.Snowflake;dev.kord.common.entity.Snowflake;kotlin.String;kotlin.String){}[0] + final fun equals(kotlin/Any?): kotlin/Boolean // dev.kord.voice.gateway/Identify.equals|equals(kotlin.Any?){}[0] + final fun hashCode(): kotlin/Int // dev.kord.voice.gateway/Identify.hashCode|hashCode(){}[0] + final fun toString(): kotlin/String // dev.kord.voice.gateway/Identify.toString|toString(){}[0] + + final object $serializer : kotlinx.serialization.internal/GeneratedSerializer { // dev.kord.voice.gateway/Identify.$serializer|null[0] + final val descriptor // dev.kord.voice.gateway/Identify.$serializer.descriptor|{}descriptor[0] + final fun (): kotlinx.serialization.descriptors/SerialDescriptor // dev.kord.voice.gateway/Identify.$serializer.descriptor.|(){}[0] + + final fun childSerializers(): kotlin/Array> // dev.kord.voice.gateway/Identify.$serializer.childSerializers|childSerializers(){}[0] + final fun deserialize(kotlinx.serialization.encoding/Decoder): dev.kord.voice.gateway/Identify // dev.kord.voice.gateway/Identify.$serializer.deserialize|deserialize(kotlinx.serialization.encoding.Decoder){}[0] + final fun serialize(kotlinx.serialization.encoding/Encoder, dev.kord.voice.gateway/Identify) // dev.kord.voice.gateway/Identify.$serializer.serialize|serialize(kotlinx.serialization.encoding.Encoder;dev.kord.voice.gateway.Identify){}[0] + } + + final object Companion { // dev.kord.voice.gateway/Identify.Companion|null[0] + final fun serializer(): kotlinx.serialization/KSerializer // dev.kord.voice.gateway/Identify.Companion.serializer|serializer(){}[0] + } +} + +final class dev.kord.voice.gateway/Ready : dev.kord.voice.gateway/VoiceEvent { // dev.kord.voice.gateway/Ready|null[0] + constructor (kotlin/UInt, kotlin/String, kotlin/Int, kotlin.collections/List) // dev.kord.voice.gateway/Ready.|(kotlin.UInt;kotlin.String;kotlin.Int;kotlin.collections.List){}[0] + + final val ip // dev.kord.voice.gateway/Ready.ip|{}ip[0] + final fun (): kotlin/String // dev.kord.voice.gateway/Ready.ip.|(){}[0] + final val modes // dev.kord.voice.gateway/Ready.modes|{}modes[0] + final fun (): kotlin.collections/List // dev.kord.voice.gateway/Ready.modes.|(){}[0] + final val port // dev.kord.voice.gateway/Ready.port|{}port[0] + final fun (): kotlin/Int // dev.kord.voice.gateway/Ready.port.|(){}[0] + final val ssrc // dev.kord.voice.gateway/Ready.ssrc|{}ssrc[0] + final fun (): kotlin/UInt // dev.kord.voice.gateway/Ready.ssrc.|(){}[0] + + final fun component1(): kotlin/UInt // dev.kord.voice.gateway/Ready.component1|component1(){}[0] + final fun component2(): kotlin/String // dev.kord.voice.gateway/Ready.component2|component2(){}[0] + final fun component3(): kotlin/Int // dev.kord.voice.gateway/Ready.component3|component3(){}[0] + final fun component4(): kotlin.collections/List // dev.kord.voice.gateway/Ready.component4|component4(){}[0] + final fun copy(kotlin/UInt = ..., kotlin/String = ..., kotlin/Int = ..., kotlin.collections/List = ...): dev.kord.voice.gateway/Ready // dev.kord.voice.gateway/Ready.copy|copy(kotlin.UInt;kotlin.String;kotlin.Int;kotlin.collections.List){}[0] + final fun equals(kotlin/Any?): kotlin/Boolean // dev.kord.voice.gateway/Ready.equals|equals(kotlin.Any?){}[0] + final fun hashCode(): kotlin/Int // dev.kord.voice.gateway/Ready.hashCode|hashCode(){}[0] + final fun toString(): kotlin/String // dev.kord.voice.gateway/Ready.toString|toString(){}[0] + + final object $serializer : kotlinx.serialization.internal/GeneratedSerializer { // dev.kord.voice.gateway/Ready.$serializer|null[0] + final val descriptor // dev.kord.voice.gateway/Ready.$serializer.descriptor|{}descriptor[0] + final fun (): kotlinx.serialization.descriptors/SerialDescriptor // dev.kord.voice.gateway/Ready.$serializer.descriptor.|(){}[0] + + final fun childSerializers(): kotlin/Array> // dev.kord.voice.gateway/Ready.$serializer.childSerializers|childSerializers(){}[0] + final fun deserialize(kotlinx.serialization.encoding/Decoder): dev.kord.voice.gateway/Ready // dev.kord.voice.gateway/Ready.$serializer.deserialize|deserialize(kotlinx.serialization.encoding.Decoder){}[0] + final fun serialize(kotlinx.serialization.encoding/Encoder, dev.kord.voice.gateway/Ready) // dev.kord.voice.gateway/Ready.$serializer.serialize|serialize(kotlinx.serialization.encoding.Encoder;dev.kord.voice.gateway.Ready){}[0] + } + + final object Companion { // dev.kord.voice.gateway/Ready.Companion|null[0] + final val $childSerializers // dev.kord.voice.gateway/Ready.Companion.$childSerializers|{}$childSerializers[0] + + final fun serializer(): kotlinx.serialization/KSerializer // dev.kord.voice.gateway/Ready.Companion.serializer|serializer(){}[0] + } +} + +final class dev.kord.voice.gateway/Resume : dev.kord.voice.gateway/Command { // dev.kord.voice.gateway/Resume|null[0] + constructor (dev.kord.common.entity/Snowflake, kotlin/String, kotlin/String) // dev.kord.voice.gateway/Resume.|(dev.kord.common.entity.Snowflake;kotlin.String;kotlin.String){}[0] + + final val serverId // dev.kord.voice.gateway/Resume.serverId|{}serverId[0] + final fun (): dev.kord.common.entity/Snowflake // dev.kord.voice.gateway/Resume.serverId.|(){}[0] + final val sessionId // dev.kord.voice.gateway/Resume.sessionId|{}sessionId[0] + final fun (): kotlin/String // dev.kord.voice.gateway/Resume.sessionId.|(){}[0] + final val token // dev.kord.voice.gateway/Resume.token|{}token[0] + final fun (): kotlin/String // dev.kord.voice.gateway/Resume.token.|(){}[0] + + final fun component1(): dev.kord.common.entity/Snowflake // dev.kord.voice.gateway/Resume.component1|component1(){}[0] + final fun component2(): kotlin/String // dev.kord.voice.gateway/Resume.component2|component2(){}[0] + final fun component3(): kotlin/String // dev.kord.voice.gateway/Resume.component3|component3(){}[0] + final fun copy(dev.kord.common.entity/Snowflake = ..., kotlin/String = ..., kotlin/String = ...): dev.kord.voice.gateway/Resume // dev.kord.voice.gateway/Resume.copy|copy(dev.kord.common.entity.Snowflake;kotlin.String;kotlin.String){}[0] + final fun equals(kotlin/Any?): kotlin/Boolean // dev.kord.voice.gateway/Resume.equals|equals(kotlin.Any?){}[0] + final fun hashCode(): kotlin/Int // dev.kord.voice.gateway/Resume.hashCode|hashCode(){}[0] + final fun toString(): kotlin/String // dev.kord.voice.gateway/Resume.toString|toString(){}[0] + + final object $serializer : kotlinx.serialization.internal/GeneratedSerializer { // dev.kord.voice.gateway/Resume.$serializer|null[0] + final val descriptor // dev.kord.voice.gateway/Resume.$serializer.descriptor|{}descriptor[0] + final fun (): kotlinx.serialization.descriptors/SerialDescriptor // dev.kord.voice.gateway/Resume.$serializer.descriptor.|(){}[0] + + final fun childSerializers(): kotlin/Array> // dev.kord.voice.gateway/Resume.$serializer.childSerializers|childSerializers(){}[0] + final fun deserialize(kotlinx.serialization.encoding/Decoder): dev.kord.voice.gateway/Resume // dev.kord.voice.gateway/Resume.$serializer.deserialize|deserialize(kotlinx.serialization.encoding.Decoder){}[0] + final fun serialize(kotlinx.serialization.encoding/Encoder, dev.kord.voice.gateway/Resume) // dev.kord.voice.gateway/Resume.$serializer.serialize|serialize(kotlinx.serialization.encoding.Encoder;dev.kord.voice.gateway.Resume){}[0] + } + + final object Companion { // dev.kord.voice.gateway/Resume.Companion|null[0] + final fun serializer(): kotlinx.serialization/KSerializer // dev.kord.voice.gateway/Resume.Companion.serializer|serializer(){}[0] + } +} + +final class dev.kord.voice.gateway/SelectProtocol : dev.kord.voice.gateway/Command { // dev.kord.voice.gateway/SelectProtocol|null[0] + constructor (kotlin/String, dev.kord.voice.gateway/SelectProtocol.Data) // dev.kord.voice.gateway/SelectProtocol.|(kotlin.String;dev.kord.voice.gateway.SelectProtocol.Data){}[0] + + final val data // dev.kord.voice.gateway/SelectProtocol.data|{}data[0] + final fun (): dev.kord.voice.gateway/SelectProtocol.Data // dev.kord.voice.gateway/SelectProtocol.data.|(){}[0] + final val protocol // dev.kord.voice.gateway/SelectProtocol.protocol|{}protocol[0] + final fun (): kotlin/String // dev.kord.voice.gateway/SelectProtocol.protocol.|(){}[0] + + final fun component1(): kotlin/String // dev.kord.voice.gateway/SelectProtocol.component1|component1(){}[0] + final fun component2(): dev.kord.voice.gateway/SelectProtocol.Data // dev.kord.voice.gateway/SelectProtocol.component2|component2(){}[0] + final fun copy(kotlin/String = ..., dev.kord.voice.gateway/SelectProtocol.Data = ...): dev.kord.voice.gateway/SelectProtocol // dev.kord.voice.gateway/SelectProtocol.copy|copy(kotlin.String;dev.kord.voice.gateway.SelectProtocol.Data){}[0] + final fun equals(kotlin/Any?): kotlin/Boolean // dev.kord.voice.gateway/SelectProtocol.equals|equals(kotlin.Any?){}[0] + final fun hashCode(): kotlin/Int // dev.kord.voice.gateway/SelectProtocol.hashCode|hashCode(){}[0] + final fun toString(): kotlin/String // dev.kord.voice.gateway/SelectProtocol.toString|toString(){}[0] + + final class Data { // dev.kord.voice.gateway/SelectProtocol.Data|null[0] + constructor (kotlin/String, kotlin/Int, dev.kord.voice/EncryptionMode) // dev.kord.voice.gateway/SelectProtocol.Data.|(kotlin.String;kotlin.Int;dev.kord.voice.EncryptionMode){}[0] + + final val address // dev.kord.voice.gateway/SelectProtocol.Data.address|{}address[0] + final fun (): kotlin/String // dev.kord.voice.gateway/SelectProtocol.Data.address.|(){}[0] + final val mode // dev.kord.voice.gateway/SelectProtocol.Data.mode|{}mode[0] + final fun (): dev.kord.voice/EncryptionMode // dev.kord.voice.gateway/SelectProtocol.Data.mode.|(){}[0] + final val port // dev.kord.voice.gateway/SelectProtocol.Data.port|{}port[0] + final fun (): kotlin/Int // dev.kord.voice.gateway/SelectProtocol.Data.port.|(){}[0] + + final fun component1(): kotlin/String // dev.kord.voice.gateway/SelectProtocol.Data.component1|component1(){}[0] + final fun component2(): kotlin/Int // dev.kord.voice.gateway/SelectProtocol.Data.component2|component2(){}[0] + final fun component3(): dev.kord.voice/EncryptionMode // dev.kord.voice.gateway/SelectProtocol.Data.component3|component3(){}[0] + final fun copy(kotlin/String = ..., kotlin/Int = ..., dev.kord.voice/EncryptionMode = ...): dev.kord.voice.gateway/SelectProtocol.Data // dev.kord.voice.gateway/SelectProtocol.Data.copy|copy(kotlin.String;kotlin.Int;dev.kord.voice.EncryptionMode){}[0] + final fun equals(kotlin/Any?): kotlin/Boolean // dev.kord.voice.gateway/SelectProtocol.Data.equals|equals(kotlin.Any?){}[0] + final fun hashCode(): kotlin/Int // dev.kord.voice.gateway/SelectProtocol.Data.hashCode|hashCode(){}[0] + final fun toString(): kotlin/String // dev.kord.voice.gateway/SelectProtocol.Data.toString|toString(){}[0] + + final object $serializer : kotlinx.serialization.internal/GeneratedSerializer { // dev.kord.voice.gateway/SelectProtocol.Data.$serializer|null[0] + final val descriptor // dev.kord.voice.gateway/SelectProtocol.Data.$serializer.descriptor|{}descriptor[0] + final fun (): kotlinx.serialization.descriptors/SerialDescriptor // dev.kord.voice.gateway/SelectProtocol.Data.$serializer.descriptor.|(){}[0] + + final fun childSerializers(): kotlin/Array> // dev.kord.voice.gateway/SelectProtocol.Data.$serializer.childSerializers|childSerializers(){}[0] + final fun deserialize(kotlinx.serialization.encoding/Decoder): dev.kord.voice.gateway/SelectProtocol.Data // dev.kord.voice.gateway/SelectProtocol.Data.$serializer.deserialize|deserialize(kotlinx.serialization.encoding.Decoder){}[0] + final fun serialize(kotlinx.serialization.encoding/Encoder, dev.kord.voice.gateway/SelectProtocol.Data) // dev.kord.voice.gateway/SelectProtocol.Data.$serializer.serialize|serialize(kotlinx.serialization.encoding.Encoder;dev.kord.voice.gateway.SelectProtocol.Data){}[0] + } + + final object Companion { // dev.kord.voice.gateway/SelectProtocol.Data.Companion|null[0] + final fun serializer(): kotlinx.serialization/KSerializer // dev.kord.voice.gateway/SelectProtocol.Data.Companion.serializer|serializer(){}[0] + } + } + + final object $serializer : kotlinx.serialization.internal/GeneratedSerializer { // dev.kord.voice.gateway/SelectProtocol.$serializer|null[0] + final val descriptor // dev.kord.voice.gateway/SelectProtocol.$serializer.descriptor|{}descriptor[0] + final fun (): kotlinx.serialization.descriptors/SerialDescriptor // dev.kord.voice.gateway/SelectProtocol.$serializer.descriptor.|(){}[0] + + final fun childSerializers(): kotlin/Array> // dev.kord.voice.gateway/SelectProtocol.$serializer.childSerializers|childSerializers(){}[0] + final fun deserialize(kotlinx.serialization.encoding/Decoder): dev.kord.voice.gateway/SelectProtocol // dev.kord.voice.gateway/SelectProtocol.$serializer.deserialize|deserialize(kotlinx.serialization.encoding.Decoder){}[0] + final fun serialize(kotlinx.serialization.encoding/Encoder, dev.kord.voice.gateway/SelectProtocol) // dev.kord.voice.gateway/SelectProtocol.$serializer.serialize|serialize(kotlinx.serialization.encoding.Encoder;dev.kord.voice.gateway.SelectProtocol){}[0] + } + + final object Companion { // dev.kord.voice.gateway/SelectProtocol.Companion|null[0] + final fun serializer(): kotlinx.serialization/KSerializer // dev.kord.voice.gateway/SelectProtocol.Companion.serializer|serializer(){}[0] + } +} + +final class dev.kord.voice.gateway/SendSpeaking : dev.kord.voice.gateway/Command { // dev.kord.voice.gateway/SendSpeaking|null[0] + constructor (dev.kord.voice/SpeakingFlags, kotlin/Int, kotlin/UInt) // dev.kord.voice.gateway/SendSpeaking.|(dev.kord.voice.SpeakingFlags;kotlin.Int;kotlin.UInt){}[0] + + final val delay // dev.kord.voice.gateway/SendSpeaking.delay|{}delay[0] + final fun (): kotlin/Int // dev.kord.voice.gateway/SendSpeaking.delay.|(){}[0] + final val speaking // dev.kord.voice.gateway/SendSpeaking.speaking|{}speaking[0] + final fun (): dev.kord.voice/SpeakingFlags // dev.kord.voice.gateway/SendSpeaking.speaking.|(){}[0] + final val ssrc // dev.kord.voice.gateway/SendSpeaking.ssrc|{}ssrc[0] + final fun (): kotlin/UInt // dev.kord.voice.gateway/SendSpeaking.ssrc.|(){}[0] + + final fun component1(): dev.kord.voice/SpeakingFlags // dev.kord.voice.gateway/SendSpeaking.component1|component1(){}[0] + final fun component2(): kotlin/Int // dev.kord.voice.gateway/SendSpeaking.component2|component2(){}[0] + final fun component3(): kotlin/UInt // dev.kord.voice.gateway/SendSpeaking.component3|component3(){}[0] + final fun copy(dev.kord.voice/SpeakingFlags = ..., kotlin/Int = ..., kotlin/UInt = ...): dev.kord.voice.gateway/SendSpeaking // dev.kord.voice.gateway/SendSpeaking.copy|copy(dev.kord.voice.SpeakingFlags;kotlin.Int;kotlin.UInt){}[0] + final fun equals(kotlin/Any?): kotlin/Boolean // dev.kord.voice.gateway/SendSpeaking.equals|equals(kotlin.Any?){}[0] + final fun hashCode(): kotlin/Int // dev.kord.voice.gateway/SendSpeaking.hashCode|hashCode(){}[0] + final fun toString(): kotlin/String // dev.kord.voice.gateway/SendSpeaking.toString|toString(){}[0] + + final object $serializer : kotlinx.serialization.internal/GeneratedSerializer { // dev.kord.voice.gateway/SendSpeaking.$serializer|null[0] + final val descriptor // dev.kord.voice.gateway/SendSpeaking.$serializer.descriptor|{}descriptor[0] + final fun (): kotlinx.serialization.descriptors/SerialDescriptor // dev.kord.voice.gateway/SendSpeaking.$serializer.descriptor.|(){}[0] + + final fun childSerializers(): kotlin/Array> // dev.kord.voice.gateway/SendSpeaking.$serializer.childSerializers|childSerializers(){}[0] + final fun deserialize(kotlinx.serialization.encoding/Decoder): dev.kord.voice.gateway/SendSpeaking // dev.kord.voice.gateway/SendSpeaking.$serializer.deserialize|deserialize(kotlinx.serialization.encoding.Decoder){}[0] + final fun serialize(kotlinx.serialization.encoding/Encoder, dev.kord.voice.gateway/SendSpeaking) // dev.kord.voice.gateway/SendSpeaking.$serializer.serialize|serialize(kotlinx.serialization.encoding.Encoder;dev.kord.voice.gateway.SendSpeaking){}[0] + } + + final object Companion { // dev.kord.voice.gateway/SendSpeaking.Companion|null[0] + final fun serializer(): kotlinx.serialization/KSerializer // dev.kord.voice.gateway/SendSpeaking.Companion.serializer|serializer(){}[0] + } +} + +final class dev.kord.voice.gateway/SessionDescription : dev.kord.voice.gateway/VoiceEvent { // dev.kord.voice.gateway/SessionDescription|null[0] + constructor (dev.kord.voice/EncryptionMode, kotlin.collections/List) // dev.kord.voice.gateway/SessionDescription.|(dev.kord.voice.EncryptionMode;kotlin.collections.List){}[0] + + final val mode // dev.kord.voice.gateway/SessionDescription.mode|{}mode[0] + final fun (): dev.kord.voice/EncryptionMode // dev.kord.voice.gateway/SessionDescription.mode.|(){}[0] + final val secretKey // dev.kord.voice.gateway/SessionDescription.secretKey|{}secretKey[0] + final fun (): kotlin.collections/List // dev.kord.voice.gateway/SessionDescription.secretKey.|(){}[0] + + final fun component1(): dev.kord.voice/EncryptionMode // dev.kord.voice.gateway/SessionDescription.component1|component1(){}[0] + final fun component2(): kotlin.collections/List // dev.kord.voice.gateway/SessionDescription.component2|component2(){}[0] + final fun copy(dev.kord.voice/EncryptionMode = ..., kotlin.collections/List = ...): dev.kord.voice.gateway/SessionDescription // dev.kord.voice.gateway/SessionDescription.copy|copy(dev.kord.voice.EncryptionMode;kotlin.collections.List){}[0] + final fun equals(kotlin/Any?): kotlin/Boolean // dev.kord.voice.gateway/SessionDescription.equals|equals(kotlin.Any?){}[0] + final fun hashCode(): kotlin/Int // dev.kord.voice.gateway/SessionDescription.hashCode|hashCode(){}[0] + final fun toString(): kotlin/String // dev.kord.voice.gateway/SessionDescription.toString|toString(){}[0] + + final object $serializer : kotlinx.serialization.internal/GeneratedSerializer { // dev.kord.voice.gateway/SessionDescription.$serializer|null[0] + final val descriptor // dev.kord.voice.gateway/SessionDescription.$serializer.descriptor|{}descriptor[0] + final fun (): kotlinx.serialization.descriptors/SerialDescriptor // dev.kord.voice.gateway/SessionDescription.$serializer.descriptor.|(){}[0] + + final fun childSerializers(): kotlin/Array> // dev.kord.voice.gateway/SessionDescription.$serializer.childSerializers|childSerializers(){}[0] + final fun deserialize(kotlinx.serialization.encoding/Decoder): dev.kord.voice.gateway/SessionDescription // dev.kord.voice.gateway/SessionDescription.$serializer.deserialize|deserialize(kotlinx.serialization.encoding.Decoder){}[0] + final fun serialize(kotlinx.serialization.encoding/Encoder, dev.kord.voice.gateway/SessionDescription) // dev.kord.voice.gateway/SessionDescription.$serializer.serialize|serialize(kotlinx.serialization.encoding.Encoder;dev.kord.voice.gateway.SessionDescription){}[0] + } + + final object Companion { // dev.kord.voice.gateway/SessionDescription.Companion|null[0] + final val $childSerializers // dev.kord.voice.gateway/SessionDescription.Companion.$childSerializers|{}$childSerializers[0] + + final fun serializer(): kotlinx.serialization/KSerializer // dev.kord.voice.gateway/SessionDescription.Companion.serializer|serializer(){}[0] + } +} + +final class dev.kord.voice.gateway/Speaking : dev.kord.voice.gateway/VoiceEvent { // dev.kord.voice.gateway/Speaking|null[0] + constructor (dev.kord.common.entity/Snowflake, kotlin/UInt, dev.kord.voice/SpeakingFlags) // dev.kord.voice.gateway/Speaking.|(dev.kord.common.entity.Snowflake;kotlin.UInt;dev.kord.voice.SpeakingFlags){}[0] + + final val speaking // dev.kord.voice.gateway/Speaking.speaking|{}speaking[0] + final fun (): dev.kord.voice/SpeakingFlags // dev.kord.voice.gateway/Speaking.speaking.|(){}[0] + final val ssrc // dev.kord.voice.gateway/Speaking.ssrc|{}ssrc[0] + final fun (): kotlin/UInt // dev.kord.voice.gateway/Speaking.ssrc.|(){}[0] + final val userId // dev.kord.voice.gateway/Speaking.userId|{}userId[0] + final fun (): dev.kord.common.entity/Snowflake // dev.kord.voice.gateway/Speaking.userId.|(){}[0] + + final fun component1(): dev.kord.common.entity/Snowflake // dev.kord.voice.gateway/Speaking.component1|component1(){}[0] + final fun component2(): kotlin/UInt // dev.kord.voice.gateway/Speaking.component2|component2(){}[0] + final fun component3(): dev.kord.voice/SpeakingFlags // dev.kord.voice.gateway/Speaking.component3|component3(){}[0] + final fun copy(dev.kord.common.entity/Snowflake = ..., kotlin/UInt = ..., dev.kord.voice/SpeakingFlags = ...): dev.kord.voice.gateway/Speaking // dev.kord.voice.gateway/Speaking.copy|copy(dev.kord.common.entity.Snowflake;kotlin.UInt;dev.kord.voice.SpeakingFlags){}[0] + final fun equals(kotlin/Any?): kotlin/Boolean // dev.kord.voice.gateway/Speaking.equals|equals(kotlin.Any?){}[0] + final fun hashCode(): kotlin/Int // dev.kord.voice.gateway/Speaking.hashCode|hashCode(){}[0] + final fun toString(): kotlin/String // dev.kord.voice.gateway/Speaking.toString|toString(){}[0] + + final object $serializer : kotlinx.serialization.internal/GeneratedSerializer { // dev.kord.voice.gateway/Speaking.$serializer|null[0] + final val descriptor // dev.kord.voice.gateway/Speaking.$serializer.descriptor|{}descriptor[0] + final fun (): kotlinx.serialization.descriptors/SerialDescriptor // dev.kord.voice.gateway/Speaking.$serializer.descriptor.|(){}[0] + + final fun childSerializers(): kotlin/Array> // dev.kord.voice.gateway/Speaking.$serializer.childSerializers|childSerializers(){}[0] + final fun deserialize(kotlinx.serialization.encoding/Decoder): dev.kord.voice.gateway/Speaking // dev.kord.voice.gateway/Speaking.$serializer.deserialize|deserialize(kotlinx.serialization.encoding.Decoder){}[0] + final fun serialize(kotlinx.serialization.encoding/Encoder, dev.kord.voice.gateway/Speaking) // dev.kord.voice.gateway/Speaking.$serializer.serialize|serialize(kotlinx.serialization.encoding.Encoder;dev.kord.voice.gateway.Speaking){}[0] + } + + final object Companion { // dev.kord.voice.gateway/Speaking.Companion|null[0] + final fun serializer(): kotlinx.serialization/KSerializer // dev.kord.voice.gateway/Speaking.Companion.serializer|serializer(){}[0] + } +} + +final class dev.kord.voice.gateway/VoiceGatewayConfiguration { // dev.kord.voice.gateway/VoiceGatewayConfiguration|null[0] + constructor (kotlin/String, kotlin/String) // dev.kord.voice.gateway/VoiceGatewayConfiguration.|(kotlin.String;kotlin.String){}[0] + + final val endpoint // dev.kord.voice.gateway/VoiceGatewayConfiguration.endpoint|{}endpoint[0] + final fun (): kotlin/String // dev.kord.voice.gateway/VoiceGatewayConfiguration.endpoint.|(){}[0] + final val token // dev.kord.voice.gateway/VoiceGatewayConfiguration.token|{}token[0] + final fun (): kotlin/String // dev.kord.voice.gateway/VoiceGatewayConfiguration.token.|(){}[0] + + final fun component1(): kotlin/String // dev.kord.voice.gateway/VoiceGatewayConfiguration.component1|component1(){}[0] + final fun component2(): kotlin/String // dev.kord.voice.gateway/VoiceGatewayConfiguration.component2|component2(){}[0] + final fun copy(kotlin/String = ..., kotlin/String = ...): dev.kord.voice.gateway/VoiceGatewayConfiguration // dev.kord.voice.gateway/VoiceGatewayConfiguration.copy|copy(kotlin.String;kotlin.String){}[0] + final fun equals(kotlin/Any?): kotlin/Boolean // dev.kord.voice.gateway/VoiceGatewayConfiguration.equals|equals(kotlin.Any?){}[0] + final fun hashCode(): kotlin/Int // dev.kord.voice.gateway/VoiceGatewayConfiguration.hashCode|hashCode(){}[0] + final fun toString(): kotlin/String // dev.kord.voice.gateway/VoiceGatewayConfiguration.toString|toString(){}[0] +} + +final class dev.kord.voice.io/ByteArrayView : kotlin.collections/Iterable { // dev.kord.voice.io/ByteArrayView|null[0] + final val data // dev.kord.voice.io/ByteArrayView.data|{}data[0] + final fun (): kotlin/ByteArray // dev.kord.voice.io/ByteArrayView.data.|(){}[0] + final val viewSize // dev.kord.voice.io/ByteArrayView.viewSize|{}viewSize[0] + final fun (): kotlin/Int // dev.kord.voice.io/ByteArrayView.viewSize.|(){}[0] + + final var dataEnd // dev.kord.voice.io/ByteArrayView.dataEnd|{}dataEnd[0] + final fun (): kotlin/Int // dev.kord.voice.io/ByteArrayView.dataEnd.|(){}[0] + final var dataStart // dev.kord.voice.io/ByteArrayView.dataStart|{}dataStart[0] + final fun (): kotlin/Int // dev.kord.voice.io/ByteArrayView.dataStart.|(){}[0] + + final fun asInt(): kotlin/Int // dev.kord.voice.io/ByteArrayView.asInt|asInt(){}[0] + final fun asShort(): kotlin/Short // dev.kord.voice.io/ByteArrayView.asShort|asShort(){}[0] + final fun clone(): dev.kord.voice.io/ByteArrayView // dev.kord.voice.io/ByteArrayView.clone|clone(){}[0] + final fun get(kotlin/Int): kotlin/Byte // dev.kord.voice.io/ByteArrayView.get|get(kotlin.Int){}[0] + final fun iterator(): kotlin.collections/Iterator // dev.kord.voice.io/ByteArrayView.iterator|iterator(){}[0] + final fun resize(kotlin/Int = ..., kotlin/Int = ...): kotlin/Boolean // dev.kord.voice.io/ByteArrayView.resize|resize(kotlin.Int;kotlin.Int){}[0] + final fun toByteArray(): kotlin/ByteArray // dev.kord.voice.io/ByteArrayView.toByteArray|toByteArray(){}[0] + final fun view(kotlin/Int = ..., kotlin/Int = ...): dev.kord.voice.io/ByteArrayView? // dev.kord.voice.io/ByteArrayView.view|view(kotlin.Int;kotlin.Int){}[0] + + final object Companion { // dev.kord.voice.io/ByteArrayView.Companion|null[0] + final fun from(kotlin/ByteArray, kotlin/Int, kotlin/Int): dev.kord.voice.io/ByteArrayView? // dev.kord.voice.io/ByteArrayView.Companion.from|from(kotlin.ByteArray;kotlin.Int;kotlin.Int){}[0] + } +} + +final class dev.kord.voice.io/MutableByteArrayCursor { // dev.kord.voice.io/MutableByteArrayCursor|null[0] + constructor (kotlin/ByteArray) // dev.kord.voice.io/MutableByteArrayCursor.|(kotlin.ByteArray){}[0] + + final val isExhausted // dev.kord.voice.io/MutableByteArrayCursor.isExhausted|{}isExhausted[0] + final fun (): kotlin/Boolean // dev.kord.voice.io/MutableByteArrayCursor.isExhausted.|(){}[0] + final val remaining // dev.kord.voice.io/MutableByteArrayCursor.remaining|{}remaining[0] + final fun (): kotlin/Int // dev.kord.voice.io/MutableByteArrayCursor.remaining.|(){}[0] + + final var cursor // dev.kord.voice.io/MutableByteArrayCursor.cursor|{}cursor[0] + final fun (): kotlin/Int // dev.kord.voice.io/MutableByteArrayCursor.cursor.|(){}[0] + final fun (kotlin/Int) // dev.kord.voice.io/MutableByteArrayCursor.cursor.|(kotlin.Int){}[0] + final var data // dev.kord.voice.io/MutableByteArrayCursor.data|{}data[0] + final fun (): kotlin/ByteArray // dev.kord.voice.io/MutableByteArrayCursor.data.|(){}[0] + + final fun reset() // dev.kord.voice.io/MutableByteArrayCursor.reset|reset(){}[0] + final fun resize(kotlin/Int, kotlin/Boolean = ...): kotlin/Boolean // dev.kord.voice.io/MutableByteArrayCursor.resize|resize(kotlin.Int;kotlin.Boolean){}[0] + final fun writeByte(kotlin/Byte) // dev.kord.voice.io/MutableByteArrayCursor.writeByte|writeByte(kotlin.Byte){}[0] + final fun writeByteArray(kotlin/ByteArray, kotlin/Int = ..., kotlin/Int = ...) // dev.kord.voice.io/MutableByteArrayCursor.writeByteArray|writeByteArray(kotlin.ByteArray;kotlin.Int;kotlin.Int){}[0] + final fun writeByteView(dev.kord.voice.io/ByteArrayView) // dev.kord.voice.io/MutableByteArrayCursor.writeByteView|writeByteView(dev.kord.voice.io.ByteArrayView){}[0] + final fun writeInt(kotlin/Int) // dev.kord.voice.io/MutableByteArrayCursor.writeInt|writeInt(kotlin.Int){}[0] + final fun writeShort(kotlin/Short) // dev.kord.voice.io/MutableByteArrayCursor.writeShort|writeShort(kotlin.Short){}[0] +} + +final class dev.kord.voice.io/ReadableByteArrayCursor { // dev.kord.voice.io/ReadableByteArrayCursor|null[0] + constructor (dev.kord.voice.io/ByteArrayView) // dev.kord.voice.io/ReadableByteArrayCursor.|(dev.kord.voice.io.ByteArrayView){}[0] + + final val remaining // dev.kord.voice.io/ReadableByteArrayCursor.remaining|{}remaining[0] + final fun (): kotlin/Int // dev.kord.voice.io/ReadableByteArrayCursor.remaining.|(){}[0] + final val view // dev.kord.voice.io/ReadableByteArrayCursor.view|{}view[0] + final fun (): dev.kord.voice.io/ByteArrayView // dev.kord.voice.io/ReadableByteArrayCursor.view.|(){}[0] + + final var cursor // dev.kord.voice.io/ReadableByteArrayCursor.cursor|{}cursor[0] + final fun (): kotlin/Int // dev.kord.voice.io/ReadableByteArrayCursor.cursor.|(){}[0] + final fun (kotlin/Int) // dev.kord.voice.io/ReadableByteArrayCursor.cursor.|(kotlin.Int){}[0] + + final fun consume(kotlin/Int) // dev.kord.voice.io/ReadableByteArrayCursor.consume|consume(kotlin.Int){}[0] + final fun readByte(): kotlin/Byte // dev.kord.voice.io/ReadableByteArrayCursor.readByte|readByte(){}[0] + final fun readBytes(kotlin/Int): dev.kord.voice.io/ByteArrayView // dev.kord.voice.io/ReadableByteArrayCursor.readBytes|readBytes(kotlin.Int){}[0] + final fun readInt(): kotlin/Int // dev.kord.voice.io/ReadableByteArrayCursor.readInt|readInt(){}[0] + final fun readRemaining(): dev.kord.voice.io/ByteArrayView // dev.kord.voice.io/ReadableByteArrayCursor.readRemaining|readRemaining(){}[0] + final fun readShort(): kotlin/Short // dev.kord.voice.io/ReadableByteArrayCursor.readShort|readShort(){}[0] +} + +final class dev.kord.voice.streams/DefaultStreams : dev.kord.voice.streams/Streams { // dev.kord.voice.streams/DefaultStreams|null[0] + constructor (dev.kord.voice.gateway/VoiceGateway, dev.kord.voice.udp/VoiceUdpSocket, dev.kord.voice.encryption.strategies/NonceStrategy) // dev.kord.voice.streams/DefaultStreams.|(dev.kord.voice.gateway.VoiceGateway;dev.kord.voice.udp.VoiceUdpSocket;dev.kord.voice.encryption.strategies.NonceStrategy){}[0] + + final val incomingAudioFrames // dev.kord.voice.streams/DefaultStreams.incomingAudioFrames|{}incomingAudioFrames[0] + final fun (): kotlinx.coroutines.flow/Flow> // dev.kord.voice.streams/DefaultStreams.incomingAudioFrames.|(){}[0] + final val incomingAudioPackets // dev.kord.voice.streams/DefaultStreams.incomingAudioPackets|{}incomingAudioPackets[0] + final fun (): kotlinx.coroutines.flow/SharedFlow // dev.kord.voice.streams/DefaultStreams.incomingAudioPackets.|(){}[0] + final val incomingUserStreams // dev.kord.voice.streams/DefaultStreams.incomingUserStreams|{}incomingUserStreams[0] + final fun (): kotlinx.coroutines.flow/SharedFlow> // dev.kord.voice.streams/DefaultStreams.incomingUserStreams.|(){}[0] + final val ssrcToUser // dev.kord.voice.streams/DefaultStreams.ssrcToUser|{}ssrcToUser[0] + final fun (): kotlin.collections/Map // dev.kord.voice.streams/DefaultStreams.ssrcToUser.|(){}[0] + + // Targets: [native] + final suspend fun listen(kotlin/ByteArray, io.ktor.network.sockets/InetSocketAddress) // dev.kord.voice.streams/DefaultStreams.listen|listen(kotlin.ByteArray;io.ktor.network.sockets.InetSocketAddress){}[0] + + // Targets: [js] + final suspend fun listen(kotlin/ByteArray, dev.kord.voice.udp/SocketAddress) // dev.kord.voice.streams/DefaultStreams.listen|listen(kotlin.ByteArray;dev.kord.voice.udp.SocketAddress){}[0] +} + +final class dev.kord.voice.udp/AudioFrameSenderConfiguration { // dev.kord.voice.udp/AudioFrameSenderConfiguration|null[0] + final val interceptorConfiguration // dev.kord.voice.udp/AudioFrameSenderConfiguration.interceptorConfiguration|{}interceptorConfiguration[0] + final fun (): dev.kord.voice/FrameInterceptorConfiguration // dev.kord.voice.udp/AudioFrameSenderConfiguration.interceptorConfiguration.|(){}[0] + final val key // dev.kord.voice.udp/AudioFrameSenderConfiguration.key|{}key[0] + final fun (): kotlin/ByteArray // dev.kord.voice.udp/AudioFrameSenderConfiguration.key.|(){}[0] + final val server // dev.kord.voice.udp/AudioFrameSenderConfiguration.server|{}server[0] + // Targets: [native] + final fun (): io.ktor.network.sockets/InetSocketAddress // dev.kord.voice.udp/AudioFrameSenderConfiguration.server.|(){}[0] + + // Targets: [js] + final fun (): dev.kord.voice.udp/SocketAddress // dev.kord.voice.udp/AudioFrameSenderConfiguration.server.|(){}[0] + final val ssrc // dev.kord.voice.udp/AudioFrameSenderConfiguration.ssrc|{}ssrc[0] + final fun (): kotlin/UInt // dev.kord.voice.udp/AudioFrameSenderConfiguration.ssrc.|(){}[0] + + final fun component2(): kotlin/UInt // dev.kord.voice.udp/AudioFrameSenderConfiguration.component2|component2(){}[0] + final fun component3(): kotlin/ByteArray // dev.kord.voice.udp/AudioFrameSenderConfiguration.component3|component3(){}[0] + final fun component4(): dev.kord.voice/FrameInterceptorConfiguration // dev.kord.voice.udp/AudioFrameSenderConfiguration.component4|component4(){}[0] + final fun equals(kotlin/Any?): kotlin/Boolean // dev.kord.voice.udp/AudioFrameSenderConfiguration.equals|equals(kotlin.Any?){}[0] + final fun hashCode(): kotlin/Int // dev.kord.voice.udp/AudioFrameSenderConfiguration.hashCode|hashCode(){}[0] + final fun toString(): kotlin/String // dev.kord.voice.udp/AudioFrameSenderConfiguration.toString|toString(){}[0] + + // Targets: [native] + constructor (io.ktor.network.sockets/InetSocketAddress, kotlin/UInt, kotlin/ByteArray, dev.kord.voice/FrameInterceptorConfiguration) // dev.kord.voice.udp/AudioFrameSenderConfiguration.|(io.ktor.network.sockets.InetSocketAddress;kotlin.UInt;kotlin.ByteArray;dev.kord.voice.FrameInterceptorConfiguration){}[0] + + // Targets: [native] + final fun component1(): io.ktor.network.sockets/InetSocketAddress // dev.kord.voice.udp/AudioFrameSenderConfiguration.component1|component1(){}[0] + + // Targets: [native] + final fun copy(io.ktor.network.sockets/InetSocketAddress = ..., kotlin/UInt = ..., kotlin/ByteArray = ..., dev.kord.voice/FrameInterceptorConfiguration = ...): dev.kord.voice.udp/AudioFrameSenderConfiguration // dev.kord.voice.udp/AudioFrameSenderConfiguration.copy|copy(io.ktor.network.sockets.InetSocketAddress;kotlin.UInt;kotlin.ByteArray;dev.kord.voice.FrameInterceptorConfiguration){}[0] + + // Targets: [apple] + final fun copy(io.ktor.network.sockets/InetSocketAddress =..., kotlin/UInt =..., kotlin/ByteArray =..., dev.kord.voice/FrameInterceptorConfiguration =...): dev.kord.voice.udp/AudioFrameSenderConfiguration // dev.kord.voice.udp/AudioFrameSenderConfiguration.copy|copy(io.ktor.network.sockets.InetSocketAddress;kotlin.UInt;kotlin.ByteArray;dev.kord.voice.FrameInterceptorConfiguration){}[0] + + // Targets: [js] + constructor (dev.kord.voice.udp/SocketAddress, kotlin/UInt, kotlin/ByteArray, dev.kord.voice/FrameInterceptorConfiguration) // dev.kord.voice.udp/AudioFrameSenderConfiguration.|(dev.kord.voice.udp.SocketAddress;kotlin.UInt;kotlin.ByteArray;dev.kord.voice.FrameInterceptorConfiguration){}[0] + + // Targets: [js] + final fun component1(): dev.kord.voice.udp/SocketAddress // dev.kord.voice.udp/AudioFrameSenderConfiguration.component1|component1(){}[0] + + // Targets: [js] + final fun copy(dev.kord.voice.udp/SocketAddress = ..., kotlin/UInt = ..., kotlin/ByteArray = ..., dev.kord.voice/FrameInterceptorConfiguration = ...): dev.kord.voice.udp/AudioFrameSenderConfiguration // dev.kord.voice.udp/AudioFrameSenderConfiguration.copy|copy(dev.kord.voice.udp.SocketAddress;kotlin.UInt;kotlin.ByteArray;dev.kord.voice.FrameInterceptorConfiguration){}[0] +} + +final class dev.kord.voice.udp/DefaultAudioFrameSender : dev.kord.voice.udp/AudioFrameSender { // dev.kord.voice.udp/DefaultAudioFrameSender|null[0] + constructor (dev.kord.voice.udp/DefaultAudioFrameSenderData) // dev.kord.voice.udp/DefaultAudioFrameSender.|(dev.kord.voice.udp.DefaultAudioFrameSenderData){}[0] + + final val data // dev.kord.voice.udp/DefaultAudioFrameSender.data|{}data[0] + final fun (): dev.kord.voice.udp/DefaultAudioFrameSenderData // dev.kord.voice.udp/DefaultAudioFrameSender.data.|(){}[0] + + final suspend fun start(dev.kord.voice.udp/AudioFrameSenderConfiguration) // dev.kord.voice.udp/DefaultAudioFrameSender.start|start(dev.kord.voice.udp.AudioFrameSenderConfiguration){}[0] +} + +final class dev.kord.voice.udp/DefaultAudioFrameSenderData { // dev.kord.voice.udp/DefaultAudioFrameSenderData|null[0] + constructor (dev.kord.voice.udp/VoiceUdpSocket, dev.kord.voice/FrameInterceptor, dev.kord.voice/AudioProvider, dev.kord.voice.encryption.strategies/NonceStrategy) // dev.kord.voice.udp/DefaultAudioFrameSenderData.|(dev.kord.voice.udp.VoiceUdpSocket;dev.kord.voice.FrameInterceptor;dev.kord.voice.AudioProvider;dev.kord.voice.encryption.strategies.NonceStrategy){}[0] + + final val interceptor // dev.kord.voice.udp/DefaultAudioFrameSenderData.interceptor|{}interceptor[0] + final fun (): dev.kord.voice/FrameInterceptor // dev.kord.voice.udp/DefaultAudioFrameSenderData.interceptor.|(){}[0] + final val nonceStrategy // dev.kord.voice.udp/DefaultAudioFrameSenderData.nonceStrategy|{}nonceStrategy[0] + final fun (): dev.kord.voice.encryption.strategies/NonceStrategy // dev.kord.voice.udp/DefaultAudioFrameSenderData.nonceStrategy.|(){}[0] + final val provider // dev.kord.voice.udp/DefaultAudioFrameSenderData.provider|{}provider[0] + final fun (): dev.kord.voice/AudioProvider // dev.kord.voice.udp/DefaultAudioFrameSenderData.provider.|(){}[0] + final val udp // dev.kord.voice.udp/DefaultAudioFrameSenderData.udp|{}udp[0] + final fun (): dev.kord.voice.udp/VoiceUdpSocket // dev.kord.voice.udp/DefaultAudioFrameSenderData.udp.|(){}[0] + + final fun component1(): dev.kord.voice.udp/VoiceUdpSocket // dev.kord.voice.udp/DefaultAudioFrameSenderData.component1|component1(){}[0] + final fun component2(): dev.kord.voice/FrameInterceptor // dev.kord.voice.udp/DefaultAudioFrameSenderData.component2|component2(){}[0] + final fun component3(): dev.kord.voice/AudioProvider // dev.kord.voice.udp/DefaultAudioFrameSenderData.component3|component3(){}[0] + final fun component4(): dev.kord.voice.encryption.strategies/NonceStrategy // dev.kord.voice.udp/DefaultAudioFrameSenderData.component4|component4(){}[0] + final fun copy(dev.kord.voice.udp/VoiceUdpSocket = ..., dev.kord.voice/FrameInterceptor = ..., dev.kord.voice/AudioProvider = ..., dev.kord.voice.encryption.strategies/NonceStrategy = ...): dev.kord.voice.udp/DefaultAudioFrameSenderData // dev.kord.voice.udp/DefaultAudioFrameSenderData.copy|copy(dev.kord.voice.udp.VoiceUdpSocket;dev.kord.voice.FrameInterceptor;dev.kord.voice.AudioProvider;dev.kord.voice.encryption.strategies.NonceStrategy){}[0] + final fun equals(kotlin/Any?): kotlin/Boolean // dev.kord.voice.udp/DefaultAudioFrameSenderData.equals|equals(kotlin.Any?){}[0] + final fun hashCode(): kotlin/Int // dev.kord.voice.udp/DefaultAudioFrameSenderData.hashCode|hashCode(){}[0] + final fun toString(): kotlin/String // dev.kord.voice.udp/DefaultAudioFrameSenderData.toString|toString(){}[0] +} + +final class dev.kord.voice.udp/DefaultNativeAudioPacketProvider : dev.kord.voice.udp/AudioPacketProvider { // dev.kord.voice.udp/DefaultNativeAudioPacketProvider|null[0] + constructor (kotlin/ByteArray, dev.kord.voice.encryption.strategies/NonceStrategy) // dev.kord.voice.udp/DefaultNativeAudioPacketProvider.|(kotlin.ByteArray;dev.kord.voice.encryption.strategies.NonceStrategy){}[0] + + final fun provide(kotlin/UShort, kotlin/UInt, kotlin/UInt, kotlin/ByteArray): dev.kord.voice.io/ByteArrayView // dev.kord.voice.udp/DefaultNativeAudioPacketProvider.provide|provide(kotlin.UShort;kotlin.UInt;kotlin.UInt;kotlin.ByteArray){}[0] +} + +final class dev.kord.voice.udp/RTPPacket { // dev.kord.voice.udp/RTPPacket|null[0] + constructor (kotlin/UByte, kotlin/Byte, kotlin/UShort, kotlin/UInt, kotlin/UInt, kotlin/UIntArray, kotlin/Boolean, kotlin/Boolean, dev.kord.voice.io/ByteArrayView) // dev.kord.voice.udp/RTPPacket.|(kotlin.UByte;kotlin.Byte;kotlin.UShort;kotlin.UInt;kotlin.UInt;kotlin.UIntArray;kotlin.Boolean;kotlin.Boolean;dev.kord.voice.io.ByteArrayView){}[0] + + final val csrcIdentifiers // dev.kord.voice.udp/RTPPacket.csrcIdentifiers|{}csrcIdentifiers[0] + final fun (): kotlin/UIntArray // dev.kord.voice.udp/RTPPacket.csrcIdentifiers.|(){}[0] + final val hasExtension // dev.kord.voice.udp/RTPPacket.hasExtension|{}hasExtension[0] + final fun (): kotlin/Boolean // dev.kord.voice.udp/RTPPacket.hasExtension.|(){}[0] + final val hasMarker // dev.kord.voice.udp/RTPPacket.hasMarker|{}hasMarker[0] + final fun (): kotlin/Boolean // dev.kord.voice.udp/RTPPacket.hasMarker.|(){}[0] + final val paddingBytes // dev.kord.voice.udp/RTPPacket.paddingBytes|{}paddingBytes[0] + final fun (): kotlin/UByte // dev.kord.voice.udp/RTPPacket.paddingBytes.|(){}[0] + final val payload // dev.kord.voice.udp/RTPPacket.payload|{}payload[0] + final fun (): dev.kord.voice.io/ByteArrayView // dev.kord.voice.udp/RTPPacket.payload.|(){}[0] + final val payloadType // dev.kord.voice.udp/RTPPacket.payloadType|{}payloadType[0] + final fun (): kotlin/Byte // dev.kord.voice.udp/RTPPacket.payloadType.|(){}[0] + final val sequence // dev.kord.voice.udp/RTPPacket.sequence|{}sequence[0] + final fun (): kotlin/UShort // dev.kord.voice.udp/RTPPacket.sequence.|(){}[0] + final val ssrc // dev.kord.voice.udp/RTPPacket.ssrc|{}ssrc[0] + final fun (): kotlin/UInt // dev.kord.voice.udp/RTPPacket.ssrc.|(){}[0] + final val timestamp // dev.kord.voice.udp/RTPPacket.timestamp|{}timestamp[0] + final fun (): kotlin/UInt // dev.kord.voice.udp/RTPPacket.timestamp.|(){}[0] + + final fun asByteArray(): kotlin/ByteArray // dev.kord.voice.udp/RTPPacket.asByteArray|asByteArray(){}[0] + final fun asByteArrayView(dev.kord.voice.io/MutableByteArrayCursor): dev.kord.voice.io/ByteArrayView // dev.kord.voice.udp/RTPPacket.asByteArrayView|asByteArrayView(dev.kord.voice.io.MutableByteArrayCursor){}[0] + final fun clone(): dev.kord.voice.udp/RTPPacket // dev.kord.voice.udp/RTPPacket.clone|clone(){}[0] + final fun component1(): kotlin/UByte // dev.kord.voice.udp/RTPPacket.component1|component1(){}[0] + final fun component2(): kotlin/Byte // dev.kord.voice.udp/RTPPacket.component2|component2(){}[0] + final fun component3(): kotlin/UShort // dev.kord.voice.udp/RTPPacket.component3|component3(){}[0] + final fun component4(): kotlin/UInt // dev.kord.voice.udp/RTPPacket.component4|component4(){}[0] + final fun component5(): kotlin/UInt // dev.kord.voice.udp/RTPPacket.component5|component5(){}[0] + final fun component6(): kotlin/UIntArray // dev.kord.voice.udp/RTPPacket.component6|component6(){}[0] + final fun component7(): kotlin/Boolean // dev.kord.voice.udp/RTPPacket.component7|component7(){}[0] + final fun component8(): kotlin/Boolean // dev.kord.voice.udp/RTPPacket.component8|component8(){}[0] + final fun component9(): dev.kord.voice.io/ByteArrayView // dev.kord.voice.udp/RTPPacket.component9|component9(){}[0] + final fun copy(kotlin/UByte = ..., kotlin/Byte = ..., kotlin/UShort = ..., kotlin/UInt = ..., kotlin/UInt = ..., kotlin/UIntArray = ..., kotlin/Boolean = ..., kotlin/Boolean = ..., dev.kord.voice.io/ByteArrayView = ...): dev.kord.voice.udp/RTPPacket // dev.kord.voice.udp/RTPPacket.copy|copy(kotlin.UByte;kotlin.Byte;kotlin.UShort;kotlin.UInt;kotlin.UInt;kotlin.UIntArray;kotlin.Boolean;kotlin.Boolean;dev.kord.voice.io.ByteArrayView){}[0] + final fun equals(kotlin/Any?): kotlin/Boolean // dev.kord.voice.udp/RTPPacket.equals|equals(kotlin.Any?){}[0] + final fun hashCode(): kotlin/Int // dev.kord.voice.udp/RTPPacket.hashCode|hashCode(){}[0] + final fun toString(): kotlin/String // dev.kord.voice.udp/RTPPacket.toString|toString(){}[0] + final fun writeHeader(): kotlin/ByteArray // dev.kord.voice.udp/RTPPacket.writeHeader|writeHeader(){}[0] + final fun writeHeader(dev.kord.voice.io/MutableByteArrayCursor) // dev.kord.voice.udp/RTPPacket.writeHeader|writeHeader(dev.kord.voice.io.MutableByteArrayCursor){}[0] + + final class Builder { // dev.kord.voice.udp/RTPPacket.Builder|null[0] + constructor (kotlin/UInt, kotlin/UInt, kotlin/UShort, kotlin/Byte, kotlin/ByteArray) // dev.kord.voice.udp/RTPPacket.Builder.|(kotlin.UInt;kotlin.UInt;kotlin.UShort;kotlin.Byte;kotlin.ByteArray){}[0] + + final var csrcIdentifiers // dev.kord.voice.udp/RTPPacket.Builder.csrcIdentifiers|{}csrcIdentifiers[0] + final fun (): kotlin/UIntArray // dev.kord.voice.udp/RTPPacket.Builder.csrcIdentifiers.|(){}[0] + final fun (kotlin/UIntArray) // dev.kord.voice.udp/RTPPacket.Builder.csrcIdentifiers.|(kotlin.UIntArray){}[0] + final var hasExtension // dev.kord.voice.udp/RTPPacket.Builder.hasExtension|{}hasExtension[0] + final fun (): kotlin/Boolean // dev.kord.voice.udp/RTPPacket.Builder.hasExtension.|(){}[0] + final fun (kotlin/Boolean) // dev.kord.voice.udp/RTPPacket.Builder.hasExtension.|(kotlin.Boolean){}[0] + final var marker // dev.kord.voice.udp/RTPPacket.Builder.marker|{}marker[0] + final fun (): kotlin/Boolean // dev.kord.voice.udp/RTPPacket.Builder.marker.|(){}[0] + final fun (kotlin/Boolean) // dev.kord.voice.udp/RTPPacket.Builder.marker.|(kotlin.Boolean){}[0] + final var paddingBytes // dev.kord.voice.udp/RTPPacket.Builder.paddingBytes|{}paddingBytes[0] + final fun (): kotlin/UByte // dev.kord.voice.udp/RTPPacket.Builder.paddingBytes.|(){}[0] + final fun (kotlin/UByte) // dev.kord.voice.udp/RTPPacket.Builder.paddingBytes.|(kotlin.UByte){}[0] + final var payload // dev.kord.voice.udp/RTPPacket.Builder.payload|{}payload[0] + final fun (): kotlin/ByteArray // dev.kord.voice.udp/RTPPacket.Builder.payload.|(){}[0] + final fun (kotlin/ByteArray) // dev.kord.voice.udp/RTPPacket.Builder.payload.|(kotlin.ByteArray){}[0] + final var payloadType // dev.kord.voice.udp/RTPPacket.Builder.payloadType|{}payloadType[0] + final fun (): kotlin/Byte // dev.kord.voice.udp/RTPPacket.Builder.payloadType.|(){}[0] + final fun (kotlin/Byte) // dev.kord.voice.udp/RTPPacket.Builder.payloadType.|(kotlin.Byte){}[0] + final var sequence // dev.kord.voice.udp/RTPPacket.Builder.sequence|{}sequence[0] + final fun (): kotlin/UShort // dev.kord.voice.udp/RTPPacket.Builder.sequence.|(){}[0] + final fun (kotlin/UShort) // dev.kord.voice.udp/RTPPacket.Builder.sequence.|(kotlin.UShort){}[0] + final var ssrc // dev.kord.voice.udp/RTPPacket.Builder.ssrc|{}ssrc[0] + final fun (): kotlin/UInt // dev.kord.voice.udp/RTPPacket.Builder.ssrc.|(){}[0] + final fun (kotlin/UInt) // dev.kord.voice.udp/RTPPacket.Builder.ssrc.|(kotlin.UInt){}[0] + final var timestamp // dev.kord.voice.udp/RTPPacket.Builder.timestamp|{}timestamp[0] + final fun (): kotlin/UInt // dev.kord.voice.udp/RTPPacket.Builder.timestamp.|(){}[0] + final fun (kotlin/UInt) // dev.kord.voice.udp/RTPPacket.Builder.timestamp.|(kotlin.UInt){}[0] + + final fun build(): dev.kord.voice.udp/RTPPacket // dev.kord.voice.udp/RTPPacket.Builder.build|build(){}[0] + } + + final object Companion { // dev.kord.voice.udp/RTPPacket.Companion|null[0] + final fun fromPacket(kotlinx.io/Source): dev.kord.voice.udp/RTPPacket? // dev.kord.voice.udp/RTPPacket.Companion.fromPacket|fromPacket(kotlinx.io.Source){}[0] + } +} + +final class dev.kord.voice/DefaultFrameInterceptor : dev.kord.voice/FrameInterceptor { // dev.kord.voice/DefaultFrameInterceptor|null[0] + constructor (dev.kord.voice/DefaultFrameInterceptorData = ...) // dev.kord.voice/DefaultFrameInterceptor.|(dev.kord.voice.DefaultFrameInterceptorData){}[0] + + final fun (kotlinx.coroutines.flow/Flow).intercept(dev.kord.voice/FrameInterceptorConfiguration): kotlinx.coroutines.flow/Flow // dev.kord.voice/DefaultFrameInterceptor.intercept|intercept@kotlinx.coroutines.flow.Flow(dev.kord.voice.FrameInterceptorConfiguration){}[0] +} + +final class dev.kord.voice/DefaultFrameInterceptorData { // dev.kord.voice/DefaultFrameInterceptorData|null[0] + constructor (dev.kord.voice/SpeakingFlags = ...) // dev.kord.voice/DefaultFrameInterceptorData.|(dev.kord.voice.SpeakingFlags){}[0] + + final val speakingState // dev.kord.voice/DefaultFrameInterceptorData.speakingState|{}speakingState[0] + final fun (): dev.kord.voice/SpeakingFlags // dev.kord.voice/DefaultFrameInterceptorData.speakingState.|(){}[0] + + final fun component1(): dev.kord.voice/SpeakingFlags // dev.kord.voice/DefaultFrameInterceptorData.component1|component1(){}[0] + final fun copy(dev.kord.voice/SpeakingFlags = ...): dev.kord.voice/DefaultFrameInterceptorData // dev.kord.voice/DefaultFrameInterceptorData.copy|copy(dev.kord.voice.SpeakingFlags){}[0] + final fun equals(kotlin/Any?): kotlin/Boolean // dev.kord.voice/DefaultFrameInterceptorData.equals|equals(kotlin.Any?){}[0] + final fun hashCode(): kotlin/Int // dev.kord.voice/DefaultFrameInterceptorData.hashCode|hashCode(){}[0] + final fun toString(): kotlin/String // dev.kord.voice/DefaultFrameInterceptorData.toString|toString(){}[0] +} + +final class dev.kord.voice/FrameInterceptorConfiguration { // dev.kord.voice/FrameInterceptorConfiguration|null[0] + constructor (dev.kord.gateway/Gateway, dev.kord.voice.gateway/VoiceGateway, kotlin/UInt) // dev.kord.voice/FrameInterceptorConfiguration.|(dev.kord.gateway.Gateway;dev.kord.voice.gateway.VoiceGateway;kotlin.UInt){}[0] + + final val gateway // dev.kord.voice/FrameInterceptorConfiguration.gateway|{}gateway[0] + final fun (): dev.kord.gateway/Gateway // dev.kord.voice/FrameInterceptorConfiguration.gateway.|(){}[0] + final val ssrc // dev.kord.voice/FrameInterceptorConfiguration.ssrc|{}ssrc[0] + final fun (): kotlin/UInt // dev.kord.voice/FrameInterceptorConfiguration.ssrc.|(){}[0] + final val voiceGateway // dev.kord.voice/FrameInterceptorConfiguration.voiceGateway|{}voiceGateway[0] + final fun (): dev.kord.voice.gateway/VoiceGateway // dev.kord.voice/FrameInterceptorConfiguration.voiceGateway.|(){}[0] + + final fun component1(): dev.kord.gateway/Gateway // dev.kord.voice/FrameInterceptorConfiguration.component1|component1(){}[0] + final fun component2(): dev.kord.voice.gateway/VoiceGateway // dev.kord.voice/FrameInterceptorConfiguration.component2|component2(){}[0] + final fun component3(): kotlin/UInt // dev.kord.voice/FrameInterceptorConfiguration.component3|component3(){}[0] + final fun copy(dev.kord.gateway/Gateway = ..., dev.kord.voice.gateway/VoiceGateway = ..., kotlin/UInt = ...): dev.kord.voice/FrameInterceptorConfiguration // dev.kord.voice/FrameInterceptorConfiguration.copy|copy(dev.kord.gateway.Gateway;dev.kord.voice.gateway.VoiceGateway;kotlin.UInt){}[0] + final fun equals(kotlin/Any?): kotlin/Boolean // dev.kord.voice/FrameInterceptorConfiguration.equals|equals(kotlin.Any?){}[0] + final fun hashCode(): kotlin/Int // dev.kord.voice/FrameInterceptorConfiguration.hashCode|hashCode(){}[0] + final fun toString(): kotlin/String // dev.kord.voice/FrameInterceptorConfiguration.toString|toString(){}[0] +} + +final class dev.kord.voice/SpeakingFlags { // dev.kord.voice/SpeakingFlags|null[0] + final val code // dev.kord.voice/SpeakingFlags.code|{}code[0] + final fun (): kotlin/Int // dev.kord.voice/SpeakingFlags.code.|(){}[0] + final val values // dev.kord.voice/SpeakingFlags.values|{}values[0] + final fun (): kotlin.collections/Set // dev.kord.voice/SpeakingFlags.values.|(){}[0] + + final fun contains(dev.kord.voice/SpeakingFlag): kotlin/Boolean // dev.kord.voice/SpeakingFlags.contains|contains(dev.kord.voice.SpeakingFlag){}[0] + final fun contains(dev.kord.voice/SpeakingFlags): kotlin/Boolean // dev.kord.voice/SpeakingFlags.contains|contains(dev.kord.voice.SpeakingFlags){}[0] + final fun equals(kotlin/Any?): kotlin/Boolean // dev.kord.voice/SpeakingFlags.equals|equals(kotlin.Any?){}[0] + final fun hashCode(): kotlin/Int // dev.kord.voice/SpeakingFlags.hashCode|hashCode(){}[0] + final fun minus(dev.kord.voice/SpeakingFlag): dev.kord.voice/SpeakingFlags // dev.kord.voice/SpeakingFlags.minus|minus(dev.kord.voice.SpeakingFlag){}[0] + final fun minus(dev.kord.voice/SpeakingFlags): dev.kord.voice/SpeakingFlags // dev.kord.voice/SpeakingFlags.minus|minus(dev.kord.voice.SpeakingFlags){}[0] + final fun plus(dev.kord.voice/SpeakingFlag): dev.kord.voice/SpeakingFlags // dev.kord.voice/SpeakingFlags.plus|plus(dev.kord.voice.SpeakingFlag){}[0] + final fun plus(dev.kord.voice/SpeakingFlags): dev.kord.voice/SpeakingFlags // dev.kord.voice/SpeakingFlags.plus|plus(dev.kord.voice.SpeakingFlags){}[0] + final fun toString(): kotlin/String // dev.kord.voice/SpeakingFlags.toString|toString(){}[0] + final inline fun copy(kotlin/Function1): dev.kord.voice/SpeakingFlags // dev.kord.voice/SpeakingFlags.copy|copy(kotlin.Function1){}[0] + + final class Builder { // dev.kord.voice/SpeakingFlags.Builder|null[0] + constructor (kotlin/Int = ...) // dev.kord.voice/SpeakingFlags.Builder.|(kotlin.Int){}[0] + + final fun (dev.kord.voice/SpeakingFlag).unaryMinus() // dev.kord.voice/SpeakingFlags.Builder.unaryMinus|unaryMinus@dev.kord.voice.SpeakingFlag(){}[0] + final fun (dev.kord.voice/SpeakingFlag).unaryPlus() // dev.kord.voice/SpeakingFlags.Builder.unaryPlus|unaryPlus@dev.kord.voice.SpeakingFlag(){}[0] + final fun (dev.kord.voice/SpeakingFlags).unaryMinus() // dev.kord.voice/SpeakingFlags.Builder.unaryMinus|unaryMinus@dev.kord.voice.SpeakingFlags(){}[0] + final fun (dev.kord.voice/SpeakingFlags).unaryPlus() // dev.kord.voice/SpeakingFlags.Builder.unaryPlus|unaryPlus@dev.kord.voice.SpeakingFlags(){}[0] + final fun build(): dev.kord.voice/SpeakingFlags // dev.kord.voice/SpeakingFlags.Builder.build|build(){}[0] + } + + final object Companion { // dev.kord.voice/SpeakingFlags.Companion|null[0] + final fun serializer(): kotlinx.serialization/KSerializer // dev.kord.voice/SpeakingFlags.Companion.serializer|serializer(){}[0] + } +} + +final class dev.kord.voice/VoiceConnection { // dev.kord.voice/VoiceConnection|null[0] + constructor (kotlinx.coroutines/CoroutineScope, dev.kord.voice/VoiceConnectionData, dev.kord.gateway/Gateway, dev.kord.voice.gateway/VoiceGateway, dev.kord.voice.udp/VoiceUdpSocket, dev.kord.voice.gateway/VoiceGatewayConfiguration, dev.kord.voice.streams/Streams, dev.kord.voice/AudioProvider, dev.kord.voice/FrameInterceptor, dev.kord.voice.udp/AudioFrameSender, dev.kord.voice.encryption.strategies/NonceStrategy, kotlin.time/Duration) // dev.kord.voice/VoiceConnection.|(kotlinx.coroutines.CoroutineScope;dev.kord.voice.VoiceConnectionData;dev.kord.gateway.Gateway;dev.kord.voice.gateway.VoiceGateway;dev.kord.voice.udp.VoiceUdpSocket;dev.kord.voice.gateway.VoiceGatewayConfiguration;dev.kord.voice.streams.Streams;dev.kord.voice.AudioProvider;dev.kord.voice.FrameInterceptor;dev.kord.voice.udp.AudioFrameSender;dev.kord.voice.encryption.strategies.NonceStrategy;kotlin.time.Duration){}[0] + + final val audioProvider // dev.kord.voice/VoiceConnection.audioProvider|{}audioProvider[0] + final fun (): dev.kord.voice/AudioProvider // dev.kord.voice/VoiceConnection.audioProvider.|(){}[0] + final val data // dev.kord.voice/VoiceConnection.data|{}data[0] + final fun (): dev.kord.voice/VoiceConnectionData // dev.kord.voice/VoiceConnection.data.|(){}[0] + final val frameInterceptor // dev.kord.voice/VoiceConnection.frameInterceptor|{}frameInterceptor[0] + final fun (): dev.kord.voice/FrameInterceptor // dev.kord.voice/VoiceConnection.frameInterceptor.|(){}[0] + final val frameSender // dev.kord.voice/VoiceConnection.frameSender|{}frameSender[0] + final fun (): dev.kord.voice.udp/AudioFrameSender // dev.kord.voice/VoiceConnection.frameSender.|(){}[0] + final val gateway // dev.kord.voice/VoiceConnection.gateway|{}gateway[0] + final fun (): dev.kord.gateway/Gateway // dev.kord.voice/VoiceConnection.gateway.|(){}[0] + final val nonceStrategy // dev.kord.voice/VoiceConnection.nonceStrategy|{}nonceStrategy[0] + final fun (): dev.kord.voice.encryption.strategies/NonceStrategy // dev.kord.voice/VoiceConnection.nonceStrategy.|(){}[0] + final val scope // dev.kord.voice/VoiceConnection.scope|{}scope[0] + final fun (): kotlinx.coroutines/CoroutineScope // dev.kord.voice/VoiceConnection.scope.|(){}[0] + final val socket // dev.kord.voice/VoiceConnection.socket|{}socket[0] + final fun (): dev.kord.voice.udp/VoiceUdpSocket // dev.kord.voice/VoiceConnection.socket.|(){}[0] + final val streams // dev.kord.voice/VoiceConnection.streams|{}streams[0] + final fun (): dev.kord.voice.streams/Streams // dev.kord.voice/VoiceConnection.streams.|(){}[0] + final val voiceGateway // dev.kord.voice/VoiceConnection.voiceGateway|{}voiceGateway[0] + final fun (): dev.kord.voice.gateway/VoiceGateway // dev.kord.voice/VoiceConnection.voiceGateway.|(){}[0] + + final var voiceGatewayConfiguration // dev.kord.voice/VoiceConnection.voiceGatewayConfiguration|{}voiceGatewayConfiguration[0] + final fun (): dev.kord.voice.gateway/VoiceGatewayConfiguration // dev.kord.voice/VoiceConnection.voiceGatewayConfiguration.|(){}[0] + final fun (dev.kord.voice.gateway/VoiceGatewayConfiguration) // dev.kord.voice/VoiceConnection.voiceGatewayConfiguration.|(dev.kord.voice.gateway.VoiceGatewayConfiguration){}[0] + + final suspend fun connect(kotlinx.coroutines/CoroutineScope = ...) // dev.kord.voice/VoiceConnection.connect|connect(kotlinx.coroutines.CoroutineScope){}[0] + final suspend fun disconnect() // dev.kord.voice/VoiceConnection.disconnect|disconnect(){}[0] + final suspend fun leave() // dev.kord.voice/VoiceConnection.leave|leave(){}[0] + final suspend fun move(dev.kord.common.entity/Snowflake, kotlin/Boolean = ..., kotlin/Boolean = ...) // dev.kord.voice/VoiceConnection.move|move(dev.kord.common.entity.Snowflake;kotlin.Boolean;kotlin.Boolean){}[0] + final suspend fun shutdown() // dev.kord.voice/VoiceConnection.shutdown|shutdown(){}[0] +} + +final class dev.kord.voice/VoiceConnectionBuilder { // dev.kord.voice/VoiceConnectionBuilder|null[0] + constructor (dev.kord.gateway/Gateway, dev.kord.common.entity/Snowflake, dev.kord.common.entity/Snowflake, dev.kord.common.entity/Snowflake) // dev.kord.voice/VoiceConnectionBuilder.|(dev.kord.gateway.Gateway;dev.kord.common.entity.Snowflake;dev.kord.common.entity.Snowflake;dev.kord.common.entity.Snowflake){}[0] + + final var audioProvider // dev.kord.voice/VoiceConnectionBuilder.audioProvider|{}audioProvider[0] + final fun (): dev.kord.voice/AudioProvider? // dev.kord.voice/VoiceConnectionBuilder.audioProvider.|(){}[0] + final fun (dev.kord.voice/AudioProvider?) // dev.kord.voice/VoiceConnectionBuilder.audioProvider.|(dev.kord.voice.AudioProvider?){}[0] + final var audioSender // dev.kord.voice/VoiceConnectionBuilder.audioSender|{}audioSender[0] + final fun (): dev.kord.voice.udp/AudioFrameSender? // dev.kord.voice/VoiceConnectionBuilder.audioSender.|(){}[0] + final fun (dev.kord.voice.udp/AudioFrameSender?) // dev.kord.voice/VoiceConnectionBuilder.audioSender.|(dev.kord.voice.udp.AudioFrameSender?){}[0] + final var channelId // dev.kord.voice/VoiceConnectionBuilder.channelId|{}channelId[0] + final fun (): dev.kord.common.entity/Snowflake // dev.kord.voice/VoiceConnectionBuilder.channelId.|(){}[0] + final fun (dev.kord.common.entity/Snowflake) // dev.kord.voice/VoiceConnectionBuilder.channelId.|(dev.kord.common.entity.Snowflake){}[0] + final var connectionDetachDuration // dev.kord.voice/VoiceConnectionBuilder.connectionDetachDuration|{}connectionDetachDuration[0] + final fun (): kotlin.time/Duration // dev.kord.voice/VoiceConnectionBuilder.connectionDetachDuration.|(){}[0] + final fun (kotlin.time/Duration) // dev.kord.voice/VoiceConnectionBuilder.connectionDetachDuration.|(kotlin.time.Duration){}[0] + final var dispatcher // dev.kord.voice/VoiceConnectionBuilder.dispatcher|{}dispatcher[0] + final fun (): kotlinx.coroutines/CoroutineDispatcher // dev.kord.voice/VoiceConnectionBuilder.dispatcher.|(){}[0] + final fun (kotlinx.coroutines/CoroutineDispatcher) // dev.kord.voice/VoiceConnectionBuilder.dispatcher.|(kotlinx.coroutines.CoroutineDispatcher){}[0] + final var frameInterceptor // dev.kord.voice/VoiceConnectionBuilder.frameInterceptor|{}frameInterceptor[0] + final fun (): dev.kord.voice/FrameInterceptor? // dev.kord.voice/VoiceConnectionBuilder.frameInterceptor.|(){}[0] + final fun (dev.kord.voice/FrameInterceptor?) // dev.kord.voice/VoiceConnectionBuilder.frameInterceptor.|(dev.kord.voice.FrameInterceptor?){}[0] + final var gateway // dev.kord.voice/VoiceConnectionBuilder.gateway|{}gateway[0] + final fun (): dev.kord.gateway/Gateway // dev.kord.voice/VoiceConnectionBuilder.gateway.|(){}[0] + final fun (dev.kord.gateway/Gateway) // dev.kord.voice/VoiceConnectionBuilder.gateway.|(dev.kord.gateway.Gateway){}[0] + final var guildId // dev.kord.voice/VoiceConnectionBuilder.guildId|{}guildId[0] + final fun (): dev.kord.common.entity/Snowflake // dev.kord.voice/VoiceConnectionBuilder.guildId.|(){}[0] + final fun (dev.kord.common.entity/Snowflake) // dev.kord.voice/VoiceConnectionBuilder.guildId.|(dev.kord.common.entity.Snowflake){}[0] + final var nonceStrategy // dev.kord.voice/VoiceConnectionBuilder.nonceStrategy|{}nonceStrategy[0] + final fun (): dev.kord.voice.encryption.strategies/NonceStrategy? // dev.kord.voice/VoiceConnectionBuilder.nonceStrategy.|(){}[0] + final fun (dev.kord.voice.encryption.strategies/NonceStrategy?) // dev.kord.voice/VoiceConnectionBuilder.nonceStrategy.|(dev.kord.voice.encryption.strategies.NonceStrategy?){}[0] + final var receiveVoice // dev.kord.voice/VoiceConnectionBuilder.receiveVoice|{}receiveVoice[0] + final fun (): kotlin/Boolean // dev.kord.voice/VoiceConnectionBuilder.receiveVoice.|(){}[0] + final fun (kotlin/Boolean) // dev.kord.voice/VoiceConnectionBuilder.receiveVoice.|(kotlin.Boolean){}[0] + final var selfDeaf // dev.kord.voice/VoiceConnectionBuilder.selfDeaf|{}selfDeaf[0] + final fun (): kotlin/Boolean // dev.kord.voice/VoiceConnectionBuilder.selfDeaf.|(){}[0] + final fun (kotlin/Boolean) // dev.kord.voice/VoiceConnectionBuilder.selfDeaf.|(kotlin.Boolean){}[0] + final var selfId // dev.kord.voice/VoiceConnectionBuilder.selfId|{}selfId[0] + final fun (): dev.kord.common.entity/Snowflake // dev.kord.voice/VoiceConnectionBuilder.selfId.|(){}[0] + final fun (dev.kord.common.entity/Snowflake) // dev.kord.voice/VoiceConnectionBuilder.selfId.|(dev.kord.common.entity.Snowflake){}[0] + final var selfMute // dev.kord.voice/VoiceConnectionBuilder.selfMute|{}selfMute[0] + final fun (): kotlin/Boolean // dev.kord.voice/VoiceConnectionBuilder.selfMute.|(){}[0] + final fun (kotlin/Boolean) // dev.kord.voice/VoiceConnectionBuilder.selfMute.|(kotlin.Boolean){}[0] + final var streams // dev.kord.voice/VoiceConnectionBuilder.streams|{}streams[0] + final fun (): dev.kord.voice.streams/Streams? // dev.kord.voice/VoiceConnectionBuilder.streams.|(){}[0] + final fun (dev.kord.voice.streams/Streams?) // dev.kord.voice/VoiceConnectionBuilder.streams.|(dev.kord.voice.streams.Streams?){}[0] + final var timeout // dev.kord.voice/VoiceConnectionBuilder.timeout|{}timeout[0] + final fun (): kotlin/Long // dev.kord.voice/VoiceConnectionBuilder.timeout.|(){}[0] + final fun (kotlin/Long) // dev.kord.voice/VoiceConnectionBuilder.timeout.|(kotlin.Long){}[0] + final var udpSocket // dev.kord.voice/VoiceConnectionBuilder.udpSocket|{}udpSocket[0] + final fun (): dev.kord.voice.udp/VoiceUdpSocket? // dev.kord.voice/VoiceConnectionBuilder.udpSocket.|(){}[0] + final fun (dev.kord.voice.udp/VoiceUdpSocket?) // dev.kord.voice/VoiceConnectionBuilder.udpSocket.|(dev.kord.voice.udp.VoiceUdpSocket?){}[0] + + final fun audioProvider(dev.kord.voice/AudioProvider) // dev.kord.voice/VoiceConnectionBuilder.audioProvider|audioProvider(dev.kord.voice.AudioProvider){}[0] + final fun frameInterceptor(dev.kord.voice/FrameInterceptor) // dev.kord.voice/VoiceConnectionBuilder.frameInterceptor|frameInterceptor(dev.kord.voice.FrameInterceptor){}[0] + final fun voiceGateway(kotlin/Function1) // dev.kord.voice/VoiceConnectionBuilder.voiceGateway|voiceGateway(kotlin.Function1){}[0] + final suspend fun build(): dev.kord.voice/VoiceConnection // dev.kord.voice/VoiceConnectionBuilder.build|build(){}[0] +} + +final class dev.kord.voice/VoiceConnectionData { // dev.kord.voice/VoiceConnectionData|null[0] + constructor (dev.kord.common.entity/Snowflake, dev.kord.common.entity/Snowflake, kotlin/String) // dev.kord.voice/VoiceConnectionData.|(dev.kord.common.entity.Snowflake;dev.kord.common.entity.Snowflake;kotlin.String){}[0] + + final val guildId // dev.kord.voice/VoiceConnectionData.guildId|{}guildId[0] + final fun (): dev.kord.common.entity/Snowflake // dev.kord.voice/VoiceConnectionData.guildId.|(){}[0] + final val selfId // dev.kord.voice/VoiceConnectionData.selfId|{}selfId[0] + final fun (): dev.kord.common.entity/Snowflake // dev.kord.voice/VoiceConnectionData.selfId.|(){}[0] + final val sessionId // dev.kord.voice/VoiceConnectionData.sessionId|{}sessionId[0] + final fun (): kotlin/String // dev.kord.voice/VoiceConnectionData.sessionId.|(){}[0] + + final fun component1(): dev.kord.common.entity/Snowflake // dev.kord.voice/VoiceConnectionData.component1|component1(){}[0] + final fun component2(): dev.kord.common.entity/Snowflake // dev.kord.voice/VoiceConnectionData.component2|component2(){}[0] + final fun component3(): kotlin/String // dev.kord.voice/VoiceConnectionData.component3|component3(){}[0] + final fun copy(dev.kord.common.entity/Snowflake = ..., dev.kord.common.entity/Snowflake = ..., kotlin/String = ...): dev.kord.voice/VoiceConnectionData // dev.kord.voice/VoiceConnectionData.copy|copy(dev.kord.common.entity.Snowflake;dev.kord.common.entity.Snowflake;kotlin.String){}[0] + final fun equals(kotlin/Any?): kotlin/Boolean // dev.kord.voice/VoiceConnectionData.equals|equals(kotlin.Any?){}[0] + final fun hashCode(): kotlin/Int // dev.kord.voice/VoiceConnectionData.hashCode|hashCode(){}[0] + final fun toString(): kotlin/String // dev.kord.voice/VoiceConnectionData.toString|toString(){}[0] +} + +final value class dev.kord.voice/AudioFrame { // dev.kord.voice/AudioFrame|null[0] + constructor (kotlin/ByteArray) // dev.kord.voice/AudioFrame.|(kotlin.ByteArray){}[0] + + final val data // dev.kord.voice/AudioFrame.data|{}data[0] + final fun (): kotlin/ByteArray // dev.kord.voice/AudioFrame.data.|(){}[0] + + final fun equals(kotlin/Any?): kotlin/Boolean // dev.kord.voice/AudioFrame.equals|equals(kotlin.Any?){}[0] + final fun hashCode(): kotlin/Int // dev.kord.voice/AudioFrame.hashCode|hashCode(){}[0] + final fun toString(): kotlin/String // dev.kord.voice/AudioFrame.toString|toString(){}[0] + + final object Companion { // dev.kord.voice/AudioFrame.Companion|null[0] + final val SILENCE // dev.kord.voice/AudioFrame.Companion.SILENCE|{}SILENCE[0] + final fun (): dev.kord.voice/AudioFrame // dev.kord.voice/AudioFrame.Companion.SILENCE.|(){}[0] + + final fun fromData(kotlin/ByteArray?): dev.kord.voice/AudioFrame? // dev.kord.voice/AudioFrame.Companion.fromData|fromData(kotlin.ByteArray?){}[0] + } +} + +sealed class dev.kord.voice.gateway/Close : dev.kord.voice.gateway/VoiceEvent { // dev.kord.voice.gateway/Close|null[0] + final class DiscordClose : dev.kord.voice.gateway/Close { // dev.kord.voice.gateway/Close.DiscordClose|null[0] + constructor (dev.kord.voice.gateway/VoiceGatewayCloseCode, kotlin/Boolean) // dev.kord.voice.gateway/Close.DiscordClose.|(dev.kord.voice.gateway.VoiceGatewayCloseCode;kotlin.Boolean){}[0] + + final val closeCode // dev.kord.voice.gateway/Close.DiscordClose.closeCode|{}closeCode[0] + final fun (): dev.kord.voice.gateway/VoiceGatewayCloseCode // dev.kord.voice.gateway/Close.DiscordClose.closeCode.|(){}[0] + final val recoverable // dev.kord.voice.gateway/Close.DiscordClose.recoverable|{}recoverable[0] + final fun (): kotlin/Boolean // dev.kord.voice.gateway/Close.DiscordClose.recoverable.|(){}[0] + + final fun component1(): dev.kord.voice.gateway/VoiceGatewayCloseCode // dev.kord.voice.gateway/Close.DiscordClose.component1|component1(){}[0] + final fun component2(): kotlin/Boolean // dev.kord.voice.gateway/Close.DiscordClose.component2|component2(){}[0] + final fun copy(dev.kord.voice.gateway/VoiceGatewayCloseCode = ..., kotlin/Boolean = ...): dev.kord.voice.gateway/Close.DiscordClose // dev.kord.voice.gateway/Close.DiscordClose.copy|copy(dev.kord.voice.gateway.VoiceGatewayCloseCode;kotlin.Boolean){}[0] + final fun equals(kotlin/Any?): kotlin/Boolean // dev.kord.voice.gateway/Close.DiscordClose.equals|equals(kotlin.Any?){}[0] + final fun hashCode(): kotlin/Int // dev.kord.voice.gateway/Close.DiscordClose.hashCode|hashCode(){}[0] + final fun toString(): kotlin/String // dev.kord.voice.gateway/Close.DiscordClose.toString|toString(){}[0] + } + + final object Reconnecting : dev.kord.voice.gateway/Close // dev.kord.voice.gateway/Close.Reconnecting|null[0] + + final object RetryLimitReached : dev.kord.voice.gateway/Close // dev.kord.voice.gateway/Close.RetryLimitReached|null[0] + + final object Timeout : dev.kord.voice.gateway/Close // dev.kord.voice.gateway/Close.Timeout|null[0] + + final object UserClose : dev.kord.voice.gateway/Close // dev.kord.voice.gateway/Close.UserClose|null[0] +} + +sealed class dev.kord.voice.gateway/Command { // dev.kord.voice.gateway/Command|null[0] + final object SerializationStrategy : kotlinx.serialization/SerializationStrategy { // dev.kord.voice.gateway/Command.SerializationStrategy|null[0] + final val descriptor // dev.kord.voice.gateway/Command.SerializationStrategy.descriptor|{}descriptor[0] + final fun (): kotlinx.serialization.descriptors/SerialDescriptor // dev.kord.voice.gateway/Command.SerializationStrategy.descriptor.|(){}[0] + + final fun serialize(kotlinx.serialization.encoding/Encoder, dev.kord.voice.gateway/Command) // dev.kord.voice.gateway/Command.SerializationStrategy.serialize|serialize(kotlinx.serialization.encoding.Encoder;dev.kord.voice.gateway.Command){}[0] + } +} + +sealed class dev.kord.voice.gateway/VoiceEvent { // dev.kord.voice.gateway/VoiceEvent|null[0] + final object DeserializationStrategy : kotlinx.serialization/DeserializationStrategy { // dev.kord.voice.gateway/VoiceEvent.DeserializationStrategy|null[0] + final val descriptor // dev.kord.voice.gateway/VoiceEvent.DeserializationStrategy.descriptor|{}descriptor[0] + final fun (): kotlinx.serialization.descriptors/SerialDescriptor // dev.kord.voice.gateway/VoiceEvent.DeserializationStrategy.descriptor.|(){}[0] + + final fun deserialize(kotlinx.serialization.encoding/Decoder): dev.kord.voice.gateway/VoiceEvent? // dev.kord.voice.gateway/VoiceEvent.DeserializationStrategy.deserialize|deserialize(kotlinx.serialization.encoding.Decoder){}[0] + } +} + +sealed class dev.kord.voice.gateway/VoiceGatewayCloseCode { // dev.kord.voice.gateway/VoiceGatewayCloseCode|null[0] + final val code // dev.kord.voice.gateway/VoiceGatewayCloseCode.code|{}code[0] + final fun (): kotlin/Int // dev.kord.voice.gateway/VoiceGatewayCloseCode.code.|(){}[0] + + final class Unknown : dev.kord.voice.gateway/VoiceGatewayCloseCode { // dev.kord.voice.gateway/VoiceGatewayCloseCode.Unknown|null[0] + constructor (kotlin/Int) // dev.kord.voice.gateway/VoiceGatewayCloseCode.Unknown.|(kotlin.Int){}[0] + } + + final object AlreadyAuthenticated : dev.kord.voice.gateway/VoiceGatewayCloseCode // dev.kord.voice.gateway/VoiceGatewayCloseCode.AlreadyAuthenticated|null[0] + + final object AuthenticationFailed : dev.kord.voice.gateway/VoiceGatewayCloseCode // dev.kord.voice.gateway/VoiceGatewayCloseCode.AuthenticationFailed|null[0] + + final object Companion { // dev.kord.voice.gateway/VoiceGatewayCloseCode.Companion|null[0] + final fun of(kotlin/Int): dev.kord.voice.gateway/VoiceGatewayCloseCode // dev.kord.voice.gateway/VoiceGatewayCloseCode.Companion.of|of(kotlin.Int){}[0] + } + + final object Disconnect : dev.kord.voice.gateway/VoiceGatewayCloseCode // dev.kord.voice.gateway/VoiceGatewayCloseCode.Disconnect|null[0] + + final object FailedToDecodePayload : dev.kord.voice.gateway/VoiceGatewayCloseCode // dev.kord.voice.gateway/VoiceGatewayCloseCode.FailedToDecodePayload|null[0] + + final object NotAuthenticated : dev.kord.voice.gateway/VoiceGatewayCloseCode // dev.kord.voice.gateway/VoiceGatewayCloseCode.NotAuthenticated|null[0] + + final object ServerNotFound : dev.kord.voice.gateway/VoiceGatewayCloseCode // dev.kord.voice.gateway/VoiceGatewayCloseCode.ServerNotFound|null[0] + + final object SessionNoLongerValid : dev.kord.voice.gateway/VoiceGatewayCloseCode // dev.kord.voice.gateway/VoiceGatewayCloseCode.SessionNoLongerValid|null[0] + + final object SessionTimeout : dev.kord.voice.gateway/VoiceGatewayCloseCode // dev.kord.voice.gateway/VoiceGatewayCloseCode.SessionTimeout|null[0] + + final object UnknownEncryptionMode : dev.kord.voice.gateway/VoiceGatewayCloseCode // dev.kord.voice.gateway/VoiceGatewayCloseCode.UnknownEncryptionMode|null[0] + + final object UnknownOpcode : dev.kord.voice.gateway/VoiceGatewayCloseCode // dev.kord.voice.gateway/VoiceGatewayCloseCode.UnknownOpcode|null[0] + + final object UnknownProtocol : dev.kord.voice.gateway/VoiceGatewayCloseCode // dev.kord.voice.gateway/VoiceGatewayCloseCode.UnknownProtocol|null[0] + + final object VoiceServerCrashed : dev.kord.voice.gateway/VoiceGatewayCloseCode // dev.kord.voice.gateway/VoiceGatewayCloseCode.VoiceServerCrashed|null[0] +} + +sealed class dev.kord.voice.udp/PayloadType { // dev.kord.voice.udp/PayloadType|null[0] + final val raw // dev.kord.voice.udp/PayloadType.raw|{}raw[0] + final fun (): kotlin/Byte // dev.kord.voice.udp/PayloadType.raw.|(){}[0] + + final class Unknown : dev.kord.voice.udp/PayloadType { // dev.kord.voice.udp/PayloadType.Unknown|null[0] + constructor (kotlin/Byte) // dev.kord.voice.udp/PayloadType.Unknown.|(kotlin.Byte){}[0] + + final fun toString(): kotlin/String // dev.kord.voice.udp/PayloadType.Unknown.toString|toString(){}[0] + } + + final object Alive : dev.kord.voice.udp/PayloadType // dev.kord.voice.udp/PayloadType.Alive|null[0] + + final object Audio : dev.kord.voice.udp/PayloadType // dev.kord.voice.udp/PayloadType.Audio|null[0] + + final object Companion { // dev.kord.voice.udp/PayloadType.Companion|null[0] + final fun from(kotlin/Byte): dev.kord.voice.udp/PayloadType // dev.kord.voice.udp/PayloadType.Companion.from|from(kotlin.Byte){}[0] + } +} + +sealed class dev.kord.voice/EncryptionMode { // dev.kord.voice/EncryptionMode|null[0] + final val value // dev.kord.voice/EncryptionMode.value|{}value[0] + final fun (): kotlin/String // dev.kord.voice/EncryptionMode.value.|(){}[0] + + final fun equals(kotlin/Any?): kotlin/Boolean // dev.kord.voice/EncryptionMode.equals|equals(kotlin.Any?){}[0] + final fun hashCode(): kotlin/Int // dev.kord.voice/EncryptionMode.hashCode|hashCode(){}[0] + final fun toString(): kotlin/String // dev.kord.voice/EncryptionMode.toString|toString(){}[0] + + final class Unknown : dev.kord.voice/EncryptionMode // dev.kord.voice/EncryptionMode.Unknown|null[0] + + final object Companion { // dev.kord.voice/EncryptionMode.Companion|null[0] + final val entries // dev.kord.voice/EncryptionMode.Companion.entries|{}entries[0] + final fun (): kotlin.collections/List // dev.kord.voice/EncryptionMode.Companion.entries.|(){}[0] + + final fun from(kotlin/String): dev.kord.voice/EncryptionMode // dev.kord.voice/EncryptionMode.Companion.from|from(kotlin.String){}[0] + final fun serializer(): kotlinx.serialization/KSerializer // dev.kord.voice/EncryptionMode.Companion.serializer|serializer(){}[0] + } + + final object XSalsa20Poly1305 : dev.kord.voice/EncryptionMode // dev.kord.voice/EncryptionMode.XSalsa20Poly1305|null[0] + + final object XSalsa20Poly1305Lite : dev.kord.voice/EncryptionMode // dev.kord.voice/EncryptionMode.XSalsa20Poly1305Lite|null[0] + + final object XSalsa20Poly1305Suffix : dev.kord.voice/EncryptionMode // dev.kord.voice/EncryptionMode.XSalsa20Poly1305Suffix|null[0] +} + +sealed class dev.kord.voice/SpeakingFlag { // dev.kord.voice/SpeakingFlag|null[0] + final val code // dev.kord.voice/SpeakingFlag.code|{}code[0] + final fun (): kotlin/Int // dev.kord.voice/SpeakingFlag.code.|(){}[0] + final val shift // dev.kord.voice/SpeakingFlag.shift|{}shift[0] + final fun (): kotlin/Int // dev.kord.voice/SpeakingFlag.shift.|(){}[0] + + final fun equals(kotlin/Any?): kotlin/Boolean // dev.kord.voice/SpeakingFlag.equals|equals(kotlin.Any?){}[0] + final fun hashCode(): kotlin/Int // dev.kord.voice/SpeakingFlag.hashCode|hashCode(){}[0] + final fun plus(dev.kord.voice/SpeakingFlag): dev.kord.voice/SpeakingFlags // dev.kord.voice/SpeakingFlag.plus|plus(dev.kord.voice.SpeakingFlag){}[0] + final fun plus(dev.kord.voice/SpeakingFlags): dev.kord.voice/SpeakingFlags // dev.kord.voice/SpeakingFlag.plus|plus(dev.kord.voice.SpeakingFlags){}[0] + final fun toString(): kotlin/String // dev.kord.voice/SpeakingFlag.toString|toString(){}[0] + + final class Unknown : dev.kord.voice/SpeakingFlag // dev.kord.voice/SpeakingFlag.Unknown|null[0] + + final object Companion { // dev.kord.voice/SpeakingFlag.Companion|null[0] + final val entries // dev.kord.voice/SpeakingFlag.Companion.entries|{}entries[0] + final fun (): kotlin.collections/List // dev.kord.voice/SpeakingFlag.Companion.entries.|(){}[0] + + final fun fromShift(kotlin/Int): dev.kord.voice/SpeakingFlag // dev.kord.voice/SpeakingFlag.Companion.fromShift|fromShift(kotlin.Int){}[0] + } + + final object Microphone : dev.kord.voice/SpeakingFlag // dev.kord.voice/SpeakingFlag.Microphone|null[0] + + final object Priority : dev.kord.voice/SpeakingFlag // dev.kord.voice/SpeakingFlag.Priority|null[0] + + final object Soundshare : dev.kord.voice/SpeakingFlag // dev.kord.voice/SpeakingFlag.Soundshare|null[0] +} + +final object dev.kord.voice.gateway/Resumed : dev.kord.voice.gateway/VoiceEvent { // dev.kord.voice.gateway/Resumed|null[0] + final fun serializer(): kotlinx.serialization/KSerializer // dev.kord.voice.gateway/Resumed.serializer|serializer(){}[0] +} + +final object dev.kord.voice.streams/NOPStreams : dev.kord.voice.streams/Streams { // dev.kord.voice.streams/NOPStreams|null[0] + final val incomingAudioFrames // dev.kord.voice.streams/NOPStreams.incomingAudioFrames|{}incomingAudioFrames[0] + final fun (): kotlinx.coroutines.flow/Flow> // dev.kord.voice.streams/NOPStreams.incomingAudioFrames.|(){}[0] + final val incomingAudioPackets // dev.kord.voice.streams/NOPStreams.incomingAudioPackets|{}incomingAudioPackets[0] + final fun (): kotlinx.coroutines.flow/Flow // dev.kord.voice.streams/NOPStreams.incomingAudioPackets.|(){}[0] + final val incomingUserStreams // dev.kord.voice.streams/NOPStreams.incomingUserStreams|{}incomingUserStreams[0] + final fun (): kotlinx.coroutines.flow/Flow> // dev.kord.voice.streams/NOPStreams.incomingUserStreams.|(){}[0] + final val ssrcToUser // dev.kord.voice.streams/NOPStreams.ssrcToUser|{}ssrcToUser[0] + final fun (): kotlin.collections/Map // dev.kord.voice.streams/NOPStreams.ssrcToUser.|(){}[0] + + // Targets: [native] + final suspend fun listen(kotlin/ByteArray, io.ktor.network.sockets/InetSocketAddress) // dev.kord.voice.streams/NOPStreams.listen|listen(kotlin.ByteArray;io.ktor.network.sockets.InetSocketAddress){}[0] + + // Targets: [js] + final suspend fun listen(kotlin/ByteArray, dev.kord.voice.udp/SocketAddress) // dev.kord.voice.streams/NOPStreams.listen|listen(kotlin.ByteArray;dev.kord.voice.udp.SocketAddress){}[0] +} + +final val dev.kord.voice.udp/GlobalVoiceUdpSocket // dev.kord.voice.udp/GlobalVoiceUdpSocket|{}GlobalVoiceUdpSocket[0] + final fun (): dev.kord.voice.udp/VoiceUdpSocket // dev.kord.voice.udp/GlobalVoiceUdpSocket.|(){}[0] + +final fun (dev.kord.voice.io/ByteArrayView).dev.kord.voice.io/mutableCursor(): dev.kord.voice.io/MutableByteArrayCursor // dev.kord.voice.io/mutableCursor|mutableCursor@dev.kord.voice.io.ByteArrayView(){}[0] +final fun (dev.kord.voice.io/ByteArrayView).dev.kord.voice.io/readableCursor(): dev.kord.voice.io/ReadableByteArrayCursor // dev.kord.voice.io/readableCursor|readableCursor@dev.kord.voice.io.ByteArrayView(){}[0] +final fun (dev.kord.voice.io/MutableByteArrayCursor).dev.kord.voice.io/writeByteArrayOrResize(kotlin/ByteArray) // dev.kord.voice.io/writeByteArrayOrResize|writeByteArrayOrResize@dev.kord.voice.io.MutableByteArrayCursor(kotlin.ByteArray){}[0] +final fun (dev.kord.voice.io/MutableByteArrayCursor).dev.kord.voice.io/writeByteViewOrResize(dev.kord.voice.io/ByteArrayView) // dev.kord.voice.io/writeByteViewOrResize|writeByteViewOrResize@dev.kord.voice.io.MutableByteArrayCursor(dev.kord.voice.io.ByteArrayView){}[0] +final fun (kotlin/ByteArray).dev.kord.voice.io/mutableCursor(): dev.kord.voice.io/MutableByteArrayCursor // dev.kord.voice.io/mutableCursor|mutableCursor@kotlin.ByteArray(){}[0] +final fun (kotlin/ByteArray).dev.kord.voice.io/readableCursor(): dev.kord.voice.io/ReadableByteArrayCursor // dev.kord.voice.io/readableCursor|readableCursor@kotlin.ByteArray(){}[0] +final fun (kotlin/ByteArray).dev.kord.voice.io/view(): dev.kord.voice.io/ByteArrayView // dev.kord.voice.io/view|view@kotlin.ByteArray(){}[0] +final fun (kotlin/ByteArray).dev.kord.voice.io/view(kotlin/Int, kotlin/Int): dev.kord.voice.io/ByteArrayView? // dev.kord.voice.io/view|view@kotlin.ByteArray(kotlin.Int;kotlin.Int){}[0] +final fun dev.kord.voice.gateway/logCaughtThrowable(kotlin/Throwable) // dev.kord.voice.gateway/logCaughtThrowable|logCaughtThrowable(kotlin.Throwable){}[0] +final fun dev.kord.voice.udp/DefaultAudioPacketProvider(kotlin/ByteArray, dev.kord.voice.encryption.strategies/NonceStrategy): dev.kord.voice.udp/AudioPacketProvider // dev.kord.voice.udp/DefaultAudioPacketProvider|DefaultAudioPacketProvider(kotlin.ByteArray;dev.kord.voice.encryption.strategies.NonceStrategy){}[0] +final fun dev.kord.voice.udp/RTPPacket(kotlin/UInt, kotlin/UInt, kotlin/UShort, kotlin/Byte, kotlin/ByteArray, kotlin/Function1 = ...): dev.kord.voice.udp/RTPPacket // dev.kord.voice.udp/RTPPacket|RTPPacket(kotlin.UInt;kotlin.UInt;kotlin.UShort;kotlin.Byte;kotlin.ByteArray;kotlin.Function1){}[0] +final fun dev.kord.voice/SpeakingFlags(kotlin.collections/Iterable): dev.kord.voice/SpeakingFlags // dev.kord.voice/SpeakingFlags|SpeakingFlags(kotlin.collections.Iterable){}[0] +final fun dev.kord.voice/SpeakingFlags(kotlin.collections/Iterable): dev.kord.voice/SpeakingFlags // dev.kord.voice/SpeakingFlags|SpeakingFlags(kotlin.collections.Iterable){}[0] +final fun dev.kord.voice/SpeakingFlags(kotlin/Array...): dev.kord.voice/SpeakingFlags // dev.kord.voice/SpeakingFlags|SpeakingFlags(kotlin.Array...){}[0] +final fun dev.kord.voice/SpeakingFlags(kotlin/Array...): dev.kord.voice/SpeakingFlags // dev.kord.voice/SpeakingFlags|SpeakingFlags(kotlin.Array...){}[0] +final inline fun <#A: reified dev.kord.voice.gateway/VoiceEvent> (dev.kord.voice.gateway/VoiceGateway).dev.kord.voice.gateway/on(kotlinx.coroutines/CoroutineScope = ..., crossinline kotlin.coroutines/SuspendFunction1<#A, kotlin/Unit>): kotlinx.coroutines/Job // dev.kord.voice.gateway/on|on@dev.kord.voice.gateway.VoiceGateway(kotlinx.coroutines.CoroutineScope;kotlin.coroutines.SuspendFunction1<0:0,kotlin.Unit>){0§}[0] +final inline fun dev.kord.voice/SpeakingFlags(kotlin/Function1 = ...): dev.kord.voice/SpeakingFlags // dev.kord.voice/SpeakingFlags|SpeakingFlags(kotlin.Function1){}[0] +final suspend inline fun dev.kord.voice/VoiceConnection(dev.kord.gateway/Gateway, dev.kord.common.entity/Snowflake, dev.kord.common.entity/Snowflake, dev.kord.common.entity/Snowflake, kotlin/Function1 = ...): dev.kord.voice/VoiceConnection // dev.kord.voice/VoiceConnection|VoiceConnection(dev.kord.gateway.Gateway;dev.kord.common.entity.Snowflake;dev.kord.common.entity.Snowflake;dev.kord.common.entity.Snowflake;kotlin.Function1){}[0] + +// Targets: [native] +final suspend fun (dev.kord.voice.udp/VoiceUdpSocket).dev.kord.voice.udp/discoverIP(io.ktor.network.sockets/InetSocketAddress, kotlin/Int): io.ktor.network.sockets/InetSocketAddress // dev.kord.voice.udp/discoverIP|discoverIP@dev.kord.voice.udp.VoiceUdpSocket(io.ktor.network.sockets.InetSocketAddress;kotlin.Int){}[0] + +// Targets: [native] +final suspend fun (dev.kord.voice.udp/VoiceUdpSocket).dev.kord.voice.udp/recv(io.ktor.network.sockets/InetSocketAddress): kotlinx.io/Source // dev.kord.voice.udp/recv|recv@dev.kord.voice.udp.VoiceUdpSocket(io.ktor.network.sockets.InetSocketAddress){}[0] + +// Targets: [apple] +final suspend fun (dev.kord.voice.udp/VoiceUdpSocket).dev.kord.voice.udp/recv(io.ktor.network.sockets/InetSocketAddress): io.ktor.utils.io.core/ByteReadPacket // dev.kord.voice.udp/recv|recv@dev.kord.voice.udp.VoiceUdpSocket(io.ktor.network.sockets.InetSocketAddress){}[0] + +// Targets: [js] +final class dev.kord.voice.udp/SocketAddress { // dev.kord.voice.udp/SocketAddress|null[0] + constructor (kotlin/String, kotlin/Int) // dev.kord.voice.udp/SocketAddress.|(kotlin.String;kotlin.Int){}[0] + + final val hostname // dev.kord.voice.udp/SocketAddress.hostname|{}hostname[0] + final fun (): kotlin/String // dev.kord.voice.udp/SocketAddress.hostname.|(){}[0] + final val port // dev.kord.voice.udp/SocketAddress.port|{}port[0] + final fun (): kotlin/Int // dev.kord.voice.udp/SocketAddress.port.|(){}[0] + + final fun component1(): kotlin/String // dev.kord.voice.udp/SocketAddress.component1|component1(){}[0] + final fun component2(): kotlin/Int // dev.kord.voice.udp/SocketAddress.component2|component2(){}[0] + final fun copy(kotlin/String = ..., kotlin/Int = ...): dev.kord.voice.udp/SocketAddress // dev.kord.voice.udp/SocketAddress.copy|copy(kotlin.String;kotlin.Int){}[0] + final fun equals(kotlin/Any?): kotlin/Boolean // dev.kord.voice.udp/SocketAddress.equals|equals(kotlin.Any?){}[0] + final fun hashCode(): kotlin/Int // dev.kord.voice.udp/SocketAddress.hashCode|hashCode(){}[0] + final fun toString(): kotlin/String // dev.kord.voice.udp/SocketAddress.toString|toString(){}[0] +} + +// Targets: [js] +final suspend fun (dev.kord.voice.udp/VoiceUdpSocket).dev.kord.voice.udp/discoverIP(dev.kord.voice.udp/SocketAddress, kotlin/Int): dev.kord.voice.udp/SocketAddress // dev.kord.voice.udp/discoverIP|discoverIP@dev.kord.voice.udp.VoiceUdpSocket(dev.kord.voice.udp.SocketAddress;kotlin.Int){}[0] + +// Targets: [js] +final suspend fun (dev.kord.voice.udp/VoiceUdpSocket).dev.kord.voice.udp/recv(dev.kord.voice.udp/SocketAddress): kotlinx.io/Source // dev.kord.voice.udp/recv|recv@dev.kord.voice.udp.VoiceUdpSocket(dev.kord.voice.udp.SocketAddress){}[0] diff --git a/voice/build.gradle.kts b/voice/build.gradle.kts index fbf4f4eaaad..153c6d93f68 100644 --- a/voice/build.gradle.kts +++ b/voice/build.gradle.kts @@ -1,21 +1,54 @@ +import org.jetbrains.kotlin.gradle.ExperimentalKotlinGradlePluginApi + plugins { - java // for TweetNaclFast - `kord-module` - `kord-sampled-module` + `kord-multiplatform-module` `kord-publishing` } -dependencies { - api(projects.common) - api(projects.gateway) +@OptIn(ExperimentalKotlinGradlePluginApi::class) +kotlin { + applyDefaultHierarchyTemplate { + common { + group("ktor") { + withJvm() + withApple() + withLinux() + withMingw() + } + + group("nonKtor") { + withJs() + } + } + } + jvm { + withJava() + } + + sourceSets { + commonMain.dependencies { + api(projects.common) + api(projects.gateway) + + implementation(libs.kotlin.logging) + + compileOnly(projects.kspAnnotations) + } - implementation(libs.kotlin.logging) - implementation(libs.slf4j.api) + named("ktorMain").dependencies { + api(libs.ktor.network) + } - // TODO remove when voiceGatewayOnLogger is removed - implementation(libs.kotlin.logging.old) + jsMain.dependencies { + implementation(libs.kotlin.node) + } - compileOnly(projects.kspAnnotations) + nonJvmMain.dependencies { + implementation(libs.libsodium) + } - api(libs.ktor.network) + jvmMain.dependencies { + implementation(libs.slf4j.api) + } + } } diff --git a/voice/build/generated/ksp/main/kotlin/dev/kord/voice/EncryptionMode.kt b/voice/build/generated/ksp/metadata/commonMain/kotlin/dev/kord/voice/EncryptionMode.kt similarity index 100% rename from voice/build/generated/ksp/main/kotlin/dev/kord/voice/EncryptionMode.kt rename to voice/build/generated/ksp/metadata/commonMain/kotlin/dev/kord/voice/EncryptionMode.kt diff --git a/voice/build/generated/ksp/main/kotlin/dev/kord/voice/SpeakingFlag.kt b/voice/build/generated/ksp/metadata/commonMain/kotlin/dev/kord/voice/SpeakingFlag.kt similarity index 100% rename from voice/build/generated/ksp/main/kotlin/dev/kord/voice/SpeakingFlag.kt rename to voice/build/generated/ksp/metadata/commonMain/kotlin/dev/kord/voice/SpeakingFlag.kt diff --git a/voice/src/main/kotlin/AudioFrame.kt b/voice/src/commonMain/kotlin/AudioFrame.kt similarity index 94% rename from voice/src/main/kotlin/AudioFrame.kt rename to voice/src/commonMain/kotlin/AudioFrame.kt index dbb3d1041d7..0ec8957a22e 100644 --- a/voice/src/main/kotlin/AudioFrame.kt +++ b/voice/src/commonMain/kotlin/AudioFrame.kt @@ -1,6 +1,7 @@ package dev.kord.voice import dev.kord.common.annotation.KordVoice +import kotlin.jvm.JvmInline /** * A frame of 20ms Opus-encoded 48k stereo audio data. diff --git a/voice/src/main/kotlin/AudioProvider.kt b/voice/src/commonMain/kotlin/AudioProvider.kt similarity index 100% rename from voice/src/main/kotlin/AudioProvider.kt rename to voice/src/commonMain/kotlin/AudioProvider.kt diff --git a/voice/src/main/kotlin/DefaultFrameInterceptor.kt b/voice/src/commonMain/kotlin/DefaultFrameInterceptor.kt similarity index 100% rename from voice/src/main/kotlin/DefaultFrameInterceptor.kt rename to voice/src/commonMain/kotlin/DefaultFrameInterceptor.kt diff --git a/voice/src/main/kotlin/EncryptionMode.kt b/voice/src/commonMain/kotlin/EncryptionMode.kt similarity index 100% rename from voice/src/main/kotlin/EncryptionMode.kt rename to voice/src/commonMain/kotlin/EncryptionMode.kt diff --git a/voice/src/main/kotlin/FrameInterceptor.kt b/voice/src/commonMain/kotlin/FrameInterceptor.kt similarity index 100% rename from voice/src/main/kotlin/FrameInterceptor.kt rename to voice/src/commonMain/kotlin/FrameInterceptor.kt diff --git a/voice/src/main/kotlin/SpeakingFlag.kt b/voice/src/commonMain/kotlin/SpeakingFlag.kt similarity index 100% rename from voice/src/main/kotlin/SpeakingFlag.kt rename to voice/src/commonMain/kotlin/SpeakingFlag.kt diff --git a/voice/src/main/kotlin/VoiceConnection.kt b/voice/src/commonMain/kotlin/VoiceConnection.kt similarity index 97% rename from voice/src/main/kotlin/VoiceConnection.kt rename to voice/src/commonMain/kotlin/VoiceConnection.kt index c034484c90b..b62969cc263 100644 --- a/voice/src/main/kotlin/VoiceConnection.kt +++ b/voice/src/commonMain/kotlin/VoiceConnection.kt @@ -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, @@ -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() } diff --git a/voice/src/main/kotlin/VoiceConnectionBuilder.kt b/voice/src/commonMain/kotlin/VoiceConnectionBuilder.kt similarity index 91% rename from voice/src/main/kotlin/VoiceConnectionBuilder.kt rename to voice/src/commonMain/kotlin/VoiceConnectionBuilder.kt index 62765a73701..f9aa30990d7 100644 --- a/voice/src/main/kotlin/VoiceConnectionBuilder.kt +++ b/voice/src/commonMain/kotlin/VoiceConnectionBuilder.kt @@ -1,6 +1,7 @@ package dev.kord.voice import dev.kord.common.KordConfiguration +import dev.kord.common.annotation.KordInternal import dev.kord.common.annotation.KordVoice import dev.kord.common.entity.Snowflake import dev.kord.gateway.Gateway @@ -17,12 +18,10 @@ import dev.kord.voice.streams.DefaultStreams import dev.kord.voice.streams.NOPStreams import dev.kord.voice.streams.Streams import dev.kord.voice.udp.* -import kotlinx.coroutines.async -import kotlinx.coroutines.coroutineScope +import kotlinx.coroutines.* import kotlinx.coroutines.flow.filter import kotlinx.coroutines.flow.filterIsInstance import kotlinx.coroutines.flow.first -import kotlinx.coroutines.withTimeoutOrNull import kotlin.time.Duration import kotlin.time.Duration.Companion.milliseconds @@ -38,6 +37,22 @@ public class VoiceConnectionBuilder( */ public var timeout: Long = 5000 + /** + * + */ + public var dispatcher: CoroutineDispatcher = Dispatchers.Default + + /** + * The [CoroutineScope] to use. By default, the scope will be created using the given [dispatcher] when [build] is called. + */ + @KordInternal + public var scopeFactory: () -> CoroutineScope = { CoroutineScope(dispatcher + SupervisorJob()) } + + @KordInternal + public fun scope(factory: () -> CoroutineScope) { + this.scopeFactory = factory + } + /** * The [AudioProvider] for this [VoiceConnection]. No audio will be provided when one is not set. */ @@ -181,6 +196,7 @@ public class VoiceConnectionBuilder( streams ?: if (receiveVoice) DefaultStreams(voiceGateway, udpSocket, nonceStrategy) else NOPStreams return VoiceConnection( + scopeFactory() + CoroutineName("kord-voice-connection[${guildId.value}]"), voiceConnectionData, gateway, voiceGateway, diff --git a/voice/src/main/kotlin/encryption/strategies/LiteNonceStrategy.kt b/voice/src/commonMain/kotlin/encryption/strategies/LiteNonceStrategy.kt similarity index 100% rename from voice/src/main/kotlin/encryption/strategies/LiteNonceStrategy.kt rename to voice/src/commonMain/kotlin/encryption/strategies/LiteNonceStrategy.kt diff --git a/voice/src/main/kotlin/encryption/strategies/NonceStrategy.kt b/voice/src/commonMain/kotlin/encryption/strategies/NonceStrategy.kt similarity index 100% rename from voice/src/main/kotlin/encryption/strategies/NonceStrategy.kt rename to voice/src/commonMain/kotlin/encryption/strategies/NonceStrategy.kt diff --git a/voice/src/main/kotlin/encryption/strategies/NormalNonceStrategy.kt b/voice/src/commonMain/kotlin/encryption/strategies/NormalNonceStrategy.kt similarity index 100% rename from voice/src/main/kotlin/encryption/strategies/NormalNonceStrategy.kt rename to voice/src/commonMain/kotlin/encryption/strategies/NormalNonceStrategy.kt diff --git a/voice/src/main/kotlin/encryption/strategies/SuffixNonceStrategy.kt b/voice/src/commonMain/kotlin/encryption/strategies/SuffixNonceStrategy.kt similarity index 100% rename from voice/src/main/kotlin/encryption/strategies/SuffixNonceStrategy.kt rename to voice/src/commonMain/kotlin/encryption/strategies/SuffixNonceStrategy.kt diff --git a/voice/src/main/kotlin/exception/VoiceConnectionInitializationException.kt b/voice/src/commonMain/kotlin/exception/VoiceConnectionInitializationException.kt similarity index 100% rename from voice/src/main/kotlin/exception/VoiceConnectionInitializationException.kt rename to voice/src/commonMain/kotlin/exception/VoiceConnectionInitializationException.kt diff --git a/voice/src/main/kotlin/gateway/Command.kt b/voice/src/commonMain/kotlin/gateway/Command.kt similarity index 100% rename from voice/src/main/kotlin/gateway/Command.kt rename to voice/src/commonMain/kotlin/gateway/Command.kt diff --git a/voice/src/main/kotlin/gateway/DefaultVoiceGateway.kt b/voice/src/commonMain/kotlin/gateway/DefaultVoiceGateway.kt similarity index 98% rename from voice/src/main/kotlin/gateway/DefaultVoiceGateway.kt rename to voice/src/commonMain/kotlin/gateway/DefaultVoiceGateway.kt index 8cf5b33844c..b9b5bc7a5c0 100644 --- a/voice/src/main/kotlin/gateway/DefaultVoiceGateway.kt +++ b/voice/src/commonMain/kotlin/gateway/DefaultVoiceGateway.kt @@ -9,6 +9,7 @@ import io.github.oshai.kotlinlogging.KotlinLogging import io.ktor.client.* import io.ktor.client.plugins.websocket.* import io.ktor.client.request.* +import io.ktor.util.network.* import io.ktor.websocket.* import kotlinx.atomicfu.AtomicRef import kotlinx.atomicfu.atomic @@ -88,7 +89,7 @@ public class DefaultVoiceGateway( if (exception is CancellationException) break defaultVoiceGatewayLogger.error(exception) { "" } - if (exception is java.nio.channels.UnresolvedAddressException) { + if (exception is UnresolvedAddressException) { data.eventFlow.emit(Close.Timeout) } @@ -139,7 +140,7 @@ public class DefaultVoiceGateway( } private suspend fun read(frame: Frame) { - val json = String(frame.data, Charsets.UTF_8) + val json = frame.data.decodeToString() try { val event = jsonParser.decodeFromString(VoiceEvent.DeserializationStrategy, json) diff --git a/voice/src/main/kotlin/gateway/DefaultVoiceGatewayBuilder.kt b/voice/src/commonMain/kotlin/gateway/DefaultVoiceGatewayBuilder.kt similarity index 80% rename from voice/src/main/kotlin/gateway/DefaultVoiceGatewayBuilder.kt rename to voice/src/commonMain/kotlin/gateway/DefaultVoiceGatewayBuilder.kt index 0ac5659bdd7..541a1b5c0d9 100644 --- a/voice/src/main/kotlin/gateway/DefaultVoiceGatewayBuilder.kt +++ b/voice/src/commonMain/kotlin/gateway/DefaultVoiceGatewayBuilder.kt @@ -2,13 +2,11 @@ package dev.kord.voice.gateway import dev.kord.common.annotation.KordVoice import dev.kord.common.entity.Snowflake +import dev.kord.common.http.httpEngine import dev.kord.gateway.retry.LinearRetry import dev.kord.gateway.retry.Retry import io.ktor.client.* -import io.ktor.client.engine.okhttp.OkHttp -import io.ktor.client.plugins.contentnegotiation.* import io.ktor.client.plugins.websocket.* -import io.ktor.serialization.kotlinx.json.* import kotlinx.coroutines.flow.MutableSharedFlow import kotlin.time.Duration.Companion.seconds @@ -23,11 +21,8 @@ public class DefaultVoiceGatewayBuilder( public var eventFlow: MutableSharedFlow = MutableSharedFlow(extraBufferCapacity = Int.MAX_VALUE) public fun build(): DefaultVoiceGateway { - val client = client ?: HttpClient(OkHttp) { + val client = client ?: HttpClient(httpEngine()) { install(WebSockets) - install(ContentNegotiation) { - json() - } } val retry = reconnectRetry ?: LinearRetry(2.seconds, 20.seconds, 10) diff --git a/voice/src/main/kotlin/gateway/OpCode.kt b/voice/src/commonMain/kotlin/gateway/OpCode.kt similarity index 100% rename from voice/src/main/kotlin/gateway/OpCode.kt rename to voice/src/commonMain/kotlin/gateway/OpCode.kt diff --git a/voice/src/main/kotlin/gateway/Ticker.kt b/voice/src/commonMain/kotlin/gateway/Ticker.kt similarity index 100% rename from voice/src/main/kotlin/gateway/Ticker.kt rename to voice/src/commonMain/kotlin/gateway/Ticker.kt diff --git a/voice/src/main/kotlin/gateway/VoiceEvent.kt b/voice/src/commonMain/kotlin/gateway/VoiceEvent.kt similarity index 100% rename from voice/src/main/kotlin/gateway/VoiceEvent.kt rename to voice/src/commonMain/kotlin/gateway/VoiceEvent.kt diff --git a/voice/src/main/kotlin/gateway/VoiceGateway.kt b/voice/src/commonMain/kotlin/gateway/VoiceGateway.kt similarity index 95% rename from voice/src/main/kotlin/gateway/VoiceGateway.kt rename to voice/src/commonMain/kotlin/gateway/VoiceGateway.kt index 9e738a4e1eb..27b9d0aead7 100644 --- a/voice/src/main/kotlin/gateway/VoiceGateway.kt +++ b/voice/src/commonMain/kotlin/gateway/VoiceGateway.kt @@ -85,15 +85,6 @@ public interface VoiceGateway { public suspend fun detach() } - -@Suppress("unused") -@Deprecated( - "Kept for binary compatibility, this declaration will be removed in 0.16.0.", - level = DeprecationLevel.HIDDEN, -) -@PublishedApi -internal val voiceGatewayOnLogger: mu.KLogger = mu.KotlinLogging.logger("Gateway.on") - /** * Logger used to report [Throwable]s caught in [VoiceGateway.on]. */ diff --git a/voice/src/main/kotlin/gateway/VoiceGatewayConfiguration.kt b/voice/src/commonMain/kotlin/gateway/VoiceGatewayConfiguration.kt similarity index 100% rename from voice/src/main/kotlin/gateway/VoiceGatewayConfiguration.kt rename to voice/src/commonMain/kotlin/gateway/VoiceGatewayConfiguration.kt diff --git a/voice/src/main/kotlin/gateway/handler/GatewayEventHandler.kt b/voice/src/commonMain/kotlin/gateway/handler/GatewayEventHandler.kt similarity index 100% rename from voice/src/main/kotlin/gateway/handler/GatewayEventHandler.kt rename to voice/src/commonMain/kotlin/gateway/handler/GatewayEventHandler.kt diff --git a/voice/src/main/kotlin/gateway/handler/HandshakeHandler.kt b/voice/src/commonMain/kotlin/gateway/handler/HandshakeHandler.kt similarity index 100% rename from voice/src/main/kotlin/gateway/handler/HandshakeHandler.kt rename to voice/src/commonMain/kotlin/gateway/handler/HandshakeHandler.kt diff --git a/voice/src/main/kotlin/gateway/handler/HeartbeatHandler.kt b/voice/src/commonMain/kotlin/gateway/handler/HeartbeatHandler.kt similarity index 100% rename from voice/src/main/kotlin/gateway/handler/HeartbeatHandler.kt rename to voice/src/commonMain/kotlin/gateway/handler/HeartbeatHandler.kt diff --git a/voice/src/main/kotlin/handlers/ConnectionEventHandler.kt b/voice/src/commonMain/kotlin/handlers/ConnectionEventHandler.kt similarity index 100% rename from voice/src/main/kotlin/handlers/ConnectionEventHandler.kt rename to voice/src/commonMain/kotlin/handlers/ConnectionEventHandler.kt diff --git a/voice/src/main/kotlin/handlers/StreamsHandler.kt b/voice/src/commonMain/kotlin/handlers/StreamsHandler.kt similarity index 92% rename from voice/src/main/kotlin/handlers/StreamsHandler.kt rename to voice/src/commonMain/kotlin/handlers/StreamsHandler.kt index 61efad03e5f..319e2c50beb 100644 --- a/voice/src/main/kotlin/handlers/StreamsHandler.kt +++ b/voice/src/commonMain/kotlin/handlers/StreamsHandler.kt @@ -6,7 +6,7 @@ import dev.kord.voice.gateway.SessionDescription import dev.kord.voice.gateway.VoiceEvent import dev.kord.voice.gateway.handler.GatewayEventHandler import dev.kord.voice.streams.Streams -import io.ktor.network.sockets.* +import dev.kord.voice.udp.SocketAddress import kotlinx.atomicfu.AtomicRef import kotlinx.atomicfu.atomic import kotlinx.coroutines.Job @@ -25,7 +25,7 @@ internal class StreamsHandler( @OptIn(ExperimentalUnsignedTypes::class) override suspend fun start() = coroutineScope { on { - server.value = InetSocketAddress(it.ip, it.port) + server.value = SocketAddress(it.ip, it.port) } on { diff --git a/voice/src/main/kotlin/handlers/UdpLifeCycleHandler.kt b/voice/src/commonMain/kotlin/handlers/UdpLifeCycleHandler.kt similarity index 82% rename from voice/src/main/kotlin/handlers/UdpLifeCycleHandler.kt rename to voice/src/commonMain/kotlin/handlers/UdpLifeCycleHandler.kt index 7d9004417a6..fc37e221618 100644 --- a/voice/src/main/kotlin/handlers/UdpLifeCycleHandler.kt +++ b/voice/src/commonMain/kotlin/handlers/UdpLifeCycleHandler.kt @@ -8,8 +8,9 @@ import dev.kord.voice.encryption.strategies.NormalNonceStrategy import dev.kord.voice.encryption.strategies.SuffixNonceStrategy import dev.kord.voice.gateway.* import dev.kord.voice.udp.AudioFrameSenderConfiguration +import dev.kord.voice.udp.SocketAddress +import dev.kord.voice.udp.discoverIP import io.github.oshai.kotlinlogging.KotlinLogging -import io.ktor.network.sockets.* import kotlinx.atomicfu.atomic import kotlinx.coroutines.Job import kotlinx.coroutines.coroutineScope @@ -22,18 +23,18 @@ internal class UdpLifeCycleHandler( flow: Flow, private val connection: VoiceConnection ) : ConnectionEventHandler(flow, "UdpInterceptor") { - private var ssrc: UInt? by atomic(null) - private var server: InetSocketAddress? by atomic(null) + private val ssrc = atomic(null) + private val server = atomic(null) private var audioSenderJob: Job? by atomic(null) @OptIn(ExperimentalUnsignedTypes::class) override suspend fun start() = coroutineScope { on { - ssrc = it.ssrc - server = InetSocketAddress(it.ip, it.port) + ssrc.value = it.ssrc + server.value = SocketAddress(it.ip, it.port) - val ip: InetSocketAddress = connection.socket.discoverIp(server!!, ssrc!!.toInt()) + val ip: SocketAddress = connection.socket.discoverIP(server.value!!, ssrc.value!!.toInt()) udpLifeCycleLogger.trace { "ip discovered for voice successfully" } @@ -58,10 +59,10 @@ internal class UdpLifeCycleHandler( on { with(connection) { val config = AudioFrameSenderConfiguration( - ssrc = ssrc!!, + ssrc = ssrc.value!!, key = it.secretKey.toUByteArray().toByteArray(), - server = server!!, - interceptorConfiguration = FrameInterceptorConfiguration(gateway, voiceGateway, ssrc!!) + server = server.value!!, + interceptorConfiguration = FrameInterceptorConfiguration(gateway, voiceGateway, ssrc.value!!) ) audioSenderJob?.cancel() diff --git a/voice/src/main/kotlin/handlers/VoiceUpdateEventHandler.kt b/voice/src/commonMain/kotlin/handlers/VoiceUpdateEventHandler.kt similarity index 100% rename from voice/src/main/kotlin/handlers/VoiceUpdateEventHandler.kt rename to voice/src/commonMain/kotlin/handlers/VoiceUpdateEventHandler.kt diff --git a/voice/src/main/kotlin/io/ByteArrayCursors.kt b/voice/src/commonMain/kotlin/io/ByteArrayCursors.kt similarity index 100% rename from voice/src/main/kotlin/io/ByteArrayCursors.kt rename to voice/src/commonMain/kotlin/io/ByteArrayCursors.kt diff --git a/voice/src/main/kotlin/io/ByteArrayView.kt b/voice/src/commonMain/kotlin/io/ByteArrayView.kt similarity index 95% rename from voice/src/main/kotlin/io/ByteArrayView.kt rename to voice/src/commonMain/kotlin/io/ByteArrayView.kt index fdc44913b85..ff99239bc8e 100644 --- a/voice/src/main/kotlin/io/ByteArrayView.kt +++ b/voice/src/commonMain/kotlin/io/ByteArrayView.kt @@ -24,7 +24,7 @@ public class ByteArrayView private constructor(public val data: ByteArray, start public operator fun get(index: Int): Byte { if (dataStart + index > dataEnd) { - throw ArrayIndexOutOfBoundsException(index) + throw IndexOutOfBoundsException("Index is out of bounds: $index") } return data[dataStart + index] @@ -37,7 +37,7 @@ public class ByteArrayView private constructor(public val data: ByteArray, start fun nextByte(): Byte = try { view[index++] - } catch (e: ArrayIndexOutOfBoundsException) { + } catch (e: IndexOutOfBoundsException) { index -= 1 throw NoSuchElementException(e.message) } diff --git a/voice/src/main/kotlin/streams/DefaultStreams.kt b/voice/src/commonMain/kotlin/streams/DefaultStreams.kt similarity index 68% rename from voice/src/main/kotlin/streams/DefaultStreams.kt rename to voice/src/commonMain/kotlin/streams/DefaultStreams.kt index 0907f1ff895..da72c18f7ea 100644 --- a/voice/src/main/kotlin/streams/DefaultStreams.kt +++ b/voice/src/commonMain/kotlin/streams/DefaultStreams.kt @@ -1,19 +1,18 @@ package dev.kord.voice.streams -import com.iwebpp.crypto.TweetNaclFast import dev.kord.common.annotation.KordVoice import dev.kord.common.entity.Snowflake import dev.kord.voice.AudioFrame -import dev.kord.voice.encryption.XSalsa20Poly1305Codec import dev.kord.voice.encryption.strategies.NonceStrategy import dev.kord.voice.gateway.Speaking import dev.kord.voice.gateway.VoiceGateway -import dev.kord.voice.io.* +import dev.kord.voice.io.ByteArrayView +import dev.kord.voice.io.readableCursor +import dev.kord.voice.udp.SocketAddress import dev.kord.voice.udp.PayloadType import dev.kord.voice.udp.RTPPacket import dev.kord.voice.udp.VoiceUdpSocket import io.github.oshai.kotlinlogging.KotlinLogging -import io.ktor.network.sockets.* import kotlinx.atomicfu.AtomicRef import kotlinx.atomicfu.atomic import kotlinx.atomicfu.update @@ -22,7 +21,7 @@ import kotlinx.coroutines.channels.Channel import kotlinx.coroutines.coroutineScope import kotlinx.coroutines.flow.* -private val defaultStreamsLogger = KotlinLogging.logger { } +internal val defaultStreamsLogger = KotlinLogging.logger { } @KordVoice public class DefaultStreams( @@ -31,9 +30,8 @@ public class DefaultStreams( private val nonceStrategy: NonceStrategy ) : Streams { private fun CoroutineScope.listenForIncoming(key: ByteArray, server: SocketAddress) { - udp.incoming - .filter { it.address == server } - .mapNotNull { RTPPacket.fromPacket(it.packet) } + udp.all(server) + .mapNotNull { RTPPacket.fromPacket(it) } .filter { it.payloadType == PayloadType.Audio.raw } .decrypt(nonceStrategy, key) .clean() @@ -47,7 +45,7 @@ public class DefaultStreams( .buffer(Channel.UNLIMITED) .onEach { speaking -> _ssrcToUser.update { - it.computeIfAbsent(speaking.ssrc) { + it.getOrPut(speaking.ssrc) { incomingAudioFrames .filter { (ssrc, _) -> speaking.ssrc == ssrc } .map { (_, frame) -> speaking.userId to frame } @@ -86,38 +84,7 @@ public class DefaultStreams( override val ssrcToUser: Map get() = _ssrcToUser.value } -private fun Flow.decrypt(nonceStrategy: NonceStrategy, key: ByteArray): Flow { - val codec = XSalsa20Poly1305Codec(key) - val nonceBuffer = ByteArray(TweetNaclFast.SecretBox.nonceLength).mutableCursor() - - val decryptedBuffer = ByteArray(512) - val decryptedCursor = decryptedBuffer.mutableCursor() - val decryptedView = decryptedBuffer.view() - - return mapNotNull { - nonceBuffer.reset() - decryptedCursor.reset() - - nonceBuffer.writeByteView(nonceStrategy.strip(it)) - - val decrypted = with(it.payload) { - codec.decrypt(data, dataStart, viewSize, nonceBuffer.data, decryptedCursor) - } - - if (!decrypted) { - defaultStreamsLogger.trace { "failed to decrypt the packet with data ${it.payload.data.contentToString()} at offset ${it.payload.dataStart} and length ${it.payload.viewSize - 4}" } - return@mapNotNull null - } - - decryptedView.resize(0, decryptedCursor.cursor) - - // mutate the payload data and update the view - it.payload.data.mutableCursor().writeByteViewOrResize(decryptedView) - it.payload.resize(0, decryptedView.viewSize) - - it - } -} +internal expect fun Flow.decrypt(nonceStrategy: NonceStrategy, key: ByteArray): Flow private fun Flow.clean(): Flow { fun processExtensionHeader(payload: ByteArrayView) = with(payload.readableCursor()) { diff --git a/voice/src/main/kotlin/streams/NOPStreams.kt b/voice/src/commonMain/kotlin/streams/NOPStreams.kt similarity index 94% rename from voice/src/main/kotlin/streams/NOPStreams.kt rename to voice/src/commonMain/kotlin/streams/NOPStreams.kt index 3b914f6095a..d2f2db1a942 100644 --- a/voice/src/main/kotlin/streams/NOPStreams.kt +++ b/voice/src/commonMain/kotlin/streams/NOPStreams.kt @@ -3,8 +3,8 @@ package dev.kord.voice.streams import dev.kord.common.annotation.KordVoice import dev.kord.common.entity.Snowflake import dev.kord.voice.AudioFrame +import dev.kord.voice.udp.SocketAddress import dev.kord.voice.udp.RTPPacket -import io.ktor.network.sockets.* import kotlinx.coroutines.flow.Flow import kotlinx.coroutines.flow.flow diff --git a/voice/src/main/kotlin/streams/Streams.kt b/voice/src/commonMain/kotlin/streams/Streams.kt similarity index 96% rename from voice/src/main/kotlin/streams/Streams.kt rename to voice/src/commonMain/kotlin/streams/Streams.kt index 12ac383f11a..e7e917caa9f 100644 --- a/voice/src/main/kotlin/streams/Streams.kt +++ b/voice/src/commonMain/kotlin/streams/Streams.kt @@ -4,7 +4,7 @@ import dev.kord.common.annotation.KordVoice import dev.kord.common.entity.Snowflake import dev.kord.voice.AudioFrame import dev.kord.voice.udp.RTPPacket -import io.ktor.network.sockets.* +import dev.kord.voice.udp.SocketAddress import kotlinx.coroutines.flow.Flow /** diff --git a/voice/src/main/kotlin/udp/AudioFrameSender.kt b/voice/src/commonMain/kotlin/udp/AudioFrameSender.kt similarity index 95% rename from voice/src/main/kotlin/udp/AudioFrameSender.kt rename to voice/src/commonMain/kotlin/udp/AudioFrameSender.kt index ebb5e79be68..53e924e0ca1 100644 --- a/voice/src/main/kotlin/udp/AudioFrameSender.kt +++ b/voice/src/commonMain/kotlin/udp/AudioFrameSender.kt @@ -4,7 +4,6 @@ package dev.kord.voice.udp import dev.kord.common.annotation.KordVoice import dev.kord.voice.FrameInterceptorConfiguration -import io.ktor.network.sockets.* @KordVoice public data class AudioFrameSenderConfiguration( diff --git a/voice/src/commonMain/kotlin/udp/AudioPacketProvider.kt b/voice/src/commonMain/kotlin/udp/AudioPacketProvider.kt new file mode 100644 index 00000000000..ababbc0392f --- /dev/null +++ b/voice/src/commonMain/kotlin/udp/AudioPacketProvider.kt @@ -0,0 +1,23 @@ +package dev.kord.voice.udp + +import dev.kord.voice.encryption.strategies.NonceStrategy +import dev.kord.voice.io.ByteArrayView +import dev.kord.voice.io.MutableByteArrayCursor + +public abstract class AudioPacketProvider(public val key: ByteArray, public val nonceStrategy: NonceStrategy) { + public abstract fun provide(sequence: UShort, timestamp: UInt, ssrc: UInt, data: ByteArray): ByteArrayView +} + +internal class CouldNotEncryptDataException(data: ByteArray, cause: Throwable? = null) : + RuntimeException("Couldn't encrypt the following data: [${data.joinToString(", ")}]", cause) + +public expect fun DefaultAudioPacketProvider(key: ByteArray, nonceStrategy: NonceStrategy) : AudioPacketProvider + + +internal fun MutableByteArrayCursor.writeHeader(sequence: Short, timestamp: Int, ssrc: Int) { + writeByte(((2 shl 6) or (0x0) or (0x0)).toByte()) // first 2 bytes are version. the rest + writeByte(PayloadType.Audio.raw) + writeShort(sequence) + writeInt(timestamp) + writeInt(ssrc) +} diff --git a/voice/src/main/kotlin/udp/DefaultAudioFrameSender.kt b/voice/src/commonMain/kotlin/udp/DefaultAudioFrameSender.kt similarity index 89% rename from voice/src/main/kotlin/udp/DefaultAudioFrameSender.kt rename to voice/src/commonMain/kotlin/udp/DefaultAudioFrameSender.kt index 3edeb832c77..f0316424563 100644 --- a/voice/src/main/kotlin/udp/DefaultAudioFrameSender.kt +++ b/voice/src/commonMain/kotlin/udp/DefaultAudioFrameSender.kt @@ -6,8 +6,6 @@ import dev.kord.voice.AudioProvider import dev.kord.voice.FrameInterceptor import dev.kord.voice.encryption.strategies.NonceStrategy import io.github.oshai.kotlinlogging.KotlinLogging -import io.ktor.network.sockets.* -import io.ktor.utils.io.core.* import kotlinx.coroutines.channels.Channel import kotlinx.coroutines.coroutineScope import kotlinx.coroutines.flow.* @@ -44,8 +42,7 @@ public class DefaultAudioFrameSender( .intercept(configuration.interceptorConfiguration) .filterNotNull() .map { packetProvider.provide(sequence, sequence * 960u, configuration.ssrc, it.data) } - .map { Datagram(ByteReadPacket(it.data, it.dataStart, it.viewSize), configuration.server) } - .onEach(data.udp::send) + .map { data.udp.send(configuration.server, it) } .onEach { sequence++ } .collect() } diff --git a/voice/src/main/kotlin/udp/PayloadType.kt b/voice/src/commonMain/kotlin/udp/PayloadType.kt similarity index 75% rename from voice/src/main/kotlin/udp/PayloadType.kt rename to voice/src/commonMain/kotlin/udp/PayloadType.kt index 45fa0fb8564..bf229d81d85 100644 --- a/voice/src/main/kotlin/udp/PayloadType.kt +++ b/voice/src/commonMain/kotlin/udp/PayloadType.kt @@ -6,7 +6,9 @@ package dev.kord.voice.udp public sealed class PayloadType(public val raw: Byte) { public object Alive : PayloadType(0x37.toByte()) public object Audio : PayloadType(0x78.toByte()) - public class Unknown(value: Byte) : PayloadType(value) + public class Unknown(value: Byte) : PayloadType(value) { + override fun toString(): String = "PayloadType.Unknown(0x${raw.toString(16)})" + } public companion object { public fun from(value: Byte): PayloadType = when (value) { diff --git a/voice/src/main/kotlin/udp/RTPPacket.kt b/voice/src/commonMain/kotlin/udp/RTPPacket.kt similarity index 99% rename from voice/src/main/kotlin/udp/RTPPacket.kt rename to voice/src/commonMain/kotlin/udp/RTPPacket.kt index 56a89078e20..052dc0f640a 100644 --- a/voice/src/main/kotlin/udp/RTPPacket.kt +++ b/voice/src/commonMain/kotlin/udp/RTPPacket.kt @@ -4,6 +4,7 @@ import dev.kord.voice.io.ByteArrayView import dev.kord.voice.io.MutableByteArrayCursor import dev.kord.voice.io.mutableCursor import dev.kord.voice.io.view +import io.ktor.utils.io.core.* import kotlinx.io.Source import kotlinx.io.readByteArray import kotlinx.io.readUInt diff --git a/voice/src/commonMain/kotlin/udp/VoiceUdpSocket.kt b/voice/src/commonMain/kotlin/udp/VoiceUdpSocket.kt new file mode 100644 index 00000000000..15103c1f262 --- /dev/null +++ b/voice/src/commonMain/kotlin/udp/VoiceUdpSocket.kt @@ -0,0 +1,44 @@ +package dev.kord.voice.udp + +import dev.kord.common.annotation.KordVoice +import dev.kord.voice.io.ByteArrayView +import io.ktor.utils.io.core.* +import kotlinx.coroutines.flow.* +import kotlinx.io.Source + +@KordVoice +public expect class SocketAddress(hostname: String, port: Int) { + public val hostname: String + + public val port: Int +} + +/** + * A global [VoiceUdpSocket] for all [dev.kord.voice.VoiceConnection]s, unless specified otherwise. + * Initiated once and kept open for the lifetime of this process. + */ +@KordVoice +public expect val GlobalVoiceUdpSocket: VoiceUdpSocket + +@KordVoice +public interface VoiceUdpSocket { + public fun all(address: SocketAddress): Flow + + public suspend fun send(address: SocketAddress, packet: ByteArrayView): Unit + + public suspend fun stop() + + public companion object { + private object None : VoiceUdpSocket { + override fun all(address: SocketAddress): Flow = emptyFlow() + + override suspend fun send(address: SocketAddress, packet: ByteArrayView) {} + + override suspend fun stop() {} + } + + public fun none(): VoiceUdpSocket = None + } +} + +public suspend fun VoiceUdpSocket.recv(address: SocketAddress): Source = all(address).first() diff --git a/voice/src/commonMain/kotlin/udp/ipDiscovery.kt b/voice/src/commonMain/kotlin/udp/ipDiscovery.kt new file mode 100644 index 00000000000..abfdf16536a --- /dev/null +++ b/voice/src/commonMain/kotlin/udp/ipDiscovery.kt @@ -0,0 +1,41 @@ +package dev.kord.voice.udp + +import dev.kord.voice.io.mutableCursor +import dev.kord.voice.io.view +import io.github.oshai.kotlinlogging.KotlinLogging +import kotlinx.io.readByteArray +import kotlinx.io.readUShort + +private val ipDiscoveryLogger = KotlinLogging.logger { } + +private const val MESSAGE_LENGTH: Short = 70 +private const val DISCOVERY_HEADER_SIZE = 8 +private const val DISCOVERY_DATA_SIZE: Int = 66 +private const val DISCOVERY_MESSAGE_SIZE = DISCOVERY_HEADER_SIZE + DISCOVERY_DATA_SIZE + +private const val REQUEST: Short = 0x01 +private const val RESPONSE: Short = 0x02 + +public suspend fun VoiceUdpSocket.discoverIP(address: SocketAddress, ssrc: Int): SocketAddress { + ipDiscoveryLogger.trace { "discovering ip" } + + val data = ByteArray(DISCOVERY_MESSAGE_SIZE ) + with (data.mutableCursor()) { + writeShort(REQUEST) + writeShort(MESSAGE_LENGTH) + writeInt(ssrc) + } + + send(address, data.view()) + + return with(recv(address)) { + require(readShort() == RESPONSE) { "did not receive a response." } + require(readShort() == MESSAGE_LENGTH) { "expected $MESSAGE_LENGTH bytes of data."} + skip(4) // ssrc + + val ip = readByteArray(64).decodeToString().trimEnd(0.toChar()) + val port = readUShort().toInt() + + SocketAddress(ip, port) + } +} diff --git a/voice/src/jsMain/kotlin/VoiceUdpSocket.js.kt b/voice/src/jsMain/kotlin/VoiceUdpSocket.js.kt new file mode 100644 index 00000000000..08d4e7b382b --- /dev/null +++ b/voice/src/jsMain/kotlin/VoiceUdpSocket.js.kt @@ -0,0 +1,60 @@ +package dev.kord.voice.udp + +import dev.kord.voice.io.ByteArrayView +import io.ktor.utils.io.core.* +import js.typedarrays.toUint8Array +import kotlinx.coroutines.* +import kotlinx.coroutines.flow.Flow +import kotlinx.coroutines.flow.MutableSharedFlow +import kotlinx.coroutines.flow.filter +import kotlinx.coroutines.flow.map +import kotlinx.io.Source +import node.dgram.SocketEvent +import node.dgram.SocketType +import node.dgram.createSocket +import kotlin.coroutines.resume +import kotlin.coroutines.resumeWithException +import kotlin.coroutines.suspendCoroutine + +public actual val GlobalVoiceUdpSocket: VoiceUdpSocket = object : VoiceUdpSocket { + private val socketScope = + CoroutineScope(Dispatchers.Default + SupervisorJob() + CoroutineName("kord-voice-global-socket")) + + private val incoming = MutableSharedFlow>() + + private val socket = createSocket(SocketType.udp4) + + init { + socket.on(SocketEvent.MESSAGE) { message, info -> + // + socketScope.launch { + incoming.emit(SocketAddress(info.address, info.port.toInt()) to message.toByteArray()) + } + } + } + + override fun all(address: SocketAddress): Flow = incoming + .filter { it.first == address } + .map { ByteReadPacket(it.second) } + + override suspend fun send(address: SocketAddress, packet: ByteArrayView) { + suspendCoroutine { cont -> + socket.send( + packet.data.toUint8Array(), + packet.dataStart, + packet.viewSize, + address.port, + address.hostname + ) { error, _ -> + if (error != null) { + cont.resumeWithException(error) + } else { + cont.resume(Unit) + } + } + } + } + + override suspend fun stop() { + } +} diff --git a/voice/src/main/java/com/iwebpp/crypto/TweetNaclFast.java b/voice/src/jvmMain/java/com/iwebpp/crypto/TweetNaclFast.java similarity index 100% rename from voice/src/main/java/com/iwebpp/crypto/TweetNaclFast.java rename to voice/src/jvmMain/java/com/iwebpp/crypto/TweetNaclFast.java diff --git a/voice/src/main/kotlin/encryption/XSalsa20Poly1305Codec.kt b/voice/src/jvmMain/kotlin/dev/kord/voice/encryption/XSalsa20Poly1305Codec.kt similarity index 99% rename from voice/src/main/kotlin/encryption/XSalsa20Poly1305Codec.kt rename to voice/src/jvmMain/kotlin/dev/kord/voice/encryption/XSalsa20Poly1305Codec.kt index 5e1083e2c29..d17057ca8de 100644 --- a/voice/src/main/kotlin/encryption/XSalsa20Poly1305Codec.kt +++ b/voice/src/jvmMain/kotlin/dev/kord/voice/encryption/XSalsa20Poly1305Codec.kt @@ -46,4 +46,4 @@ public fun XSalsa20Poly1305Codec.decrypt( val buffer = ByteArray(boxLength - TweetNaclFast.SecretBox.boxzerobytesLength) if (!decrypt(box, boxOffset, boxLength, nonce, buffer.mutableCursor())) return null return buffer -} \ No newline at end of file +} diff --git a/voice/src/main/kotlin/encryption/XSalsa20Poly1305Encryption.kt b/voice/src/jvmMain/kotlin/dev/kord/voice/encryption/XSalsa20Poly1305Encryption.kt similarity index 100% rename from voice/src/main/kotlin/encryption/XSalsa20Poly1305Encryption.kt rename to voice/src/jvmMain/kotlin/dev/kord/voice/encryption/XSalsa20Poly1305Encryption.kt diff --git a/voice/src/jvmMain/kotlin/dev/kord/voice/streams/DefaultStreams.kt b/voice/src/jvmMain/kotlin/dev/kord/voice/streams/DefaultStreams.kt new file mode 100644 index 00000000000..28b0ab5f817 --- /dev/null +++ b/voice/src/jvmMain/kotlin/dev/kord/voice/streams/DefaultStreams.kt @@ -0,0 +1,46 @@ +@file:JvmName("DefaultStreamsJvm") + +package dev.kord.voice.streams + +import com.iwebpp.crypto.TweetNaclFast +import dev.kord.voice.encryption.XSalsa20Poly1305Codec +import dev.kord.voice.encryption.strategies.NonceStrategy +import dev.kord.voice.io.mutableCursor +import dev.kord.voice.io.view +import dev.kord.voice.io.writeByteViewOrResize +import dev.kord.voice.udp.RTPPacket +import kotlinx.coroutines.flow.Flow +import kotlinx.coroutines.flow.mapNotNull + +internal actual fun Flow.decrypt(nonceStrategy: NonceStrategy, key: ByteArray): Flow { + val codec = XSalsa20Poly1305Codec(key) + val nonceBuffer = ByteArray(TweetNaclFast.SecretBox.nonceLength).mutableCursor() + + val decryptedBuffer = ByteArray(512) + val decryptedCursor = decryptedBuffer.mutableCursor() + val decryptedView = decryptedBuffer.view() + + return mapNotNull { + nonceBuffer.reset() + decryptedCursor.reset() + + nonceBuffer.writeByteView(nonceStrategy.strip(it)) + + val decrypted = with(it.payload) { + codec.decrypt(data, dataStart, viewSize, nonceBuffer.data, decryptedCursor) + } + + if (!decrypted) { + defaultStreamsLogger.trace { "failed to decrypt the packet with data ${it.payload.data.contentToString()} at offset ${it.payload.dataStart} and length ${it.payload.viewSize - 4}" } + return@mapNotNull null + } + + decryptedView.resize(0, decryptedCursor.cursor) + + // mutate the payload data and update the view + it.payload.data.mutableCursor().writeByteViewOrResize(decryptedView) + it.payload.resize(0, decryptedView.viewSize) + + it + } +} diff --git a/voice/src/main/kotlin/udp/AudioPacketProvider.kt b/voice/src/jvmMain/kotlin/dev/kord/voice/udp/DefaultAudioPacketProvider.kt similarity index 72% rename from voice/src/main/kotlin/udp/AudioPacketProvider.kt rename to voice/src/jvmMain/kotlin/dev/kord/voice/udp/DefaultAudioPacketProvider.kt index 05f2e522d4c..367d042b3fc 100644 --- a/voice/src/main/kotlin/udp/AudioPacketProvider.kt +++ b/voice/src/jvmMain/kotlin/dev/kord/voice/udp/DefaultAudioPacketProvider.kt @@ -8,14 +8,12 @@ import dev.kord.voice.io.MutableByteArrayCursor import dev.kord.voice.io.mutableCursor import dev.kord.voice.io.view -public abstract class AudioPacketProvider(public val key: ByteArray, public val nonceStrategy: NonceStrategy) { - public abstract fun provide(sequence: UShort, timestamp: UInt, ssrc: UInt, data: ByteArray): ByteArrayView -} +@Suppress("FunctionName") +public actual fun DefaultAudioPacketProvider(key: ByteArray, nonceStrategy: NonceStrategy) : AudioPacketProvider = + DefaultJvmAudioPacketProvider(key, nonceStrategy) -private class CouldNotEncryptDataException(data: ByteArray) : - RuntimeException("Couldn't encrypt the following data: [${data.joinToString(", ")}]") -public class DefaultAudioPacketProvider(key: ByteArray, nonceStrategy: NonceStrategy) : +public class DefaultJvmAudioPacketProvider(key: ByteArray, nonceStrategy: NonceStrategy) : AudioPacketProvider(key, nonceStrategy) { private val codec = XSalsa20Poly1305Codec(key) @@ -29,14 +27,6 @@ public class DefaultAudioPacketProvider(key: ByteArray, nonceStrategy: NonceStra private val lock: Any = Any() - private fun MutableByteArrayCursor.writeHeader(sequence: Short, timestamp: Int, ssrc: Int) { - writeByte(((2 shl 6) or (0x0) or (0x0)).toByte()) // first 2 bytes are version. the rest - writeByte(PayloadType.Audio.raw) - writeShort(sequence) - writeInt(timestamp) - writeInt(ssrc) - } - override fun provide(sequence: UShort, timestamp: UInt, ssrc: UInt, data: ByteArray): ByteArrayView = synchronized(lock) { with(packetBufferCursor) { diff --git a/voice/src/ktorMain/kotlin/VoiceUdpSocket.ktor.kt b/voice/src/ktorMain/kotlin/VoiceUdpSocket.ktor.kt new file mode 100644 index 00000000000..bb233d0f99f --- /dev/null +++ b/voice/src/ktorMain/kotlin/VoiceUdpSocket.ktor.kt @@ -0,0 +1,46 @@ +package dev.kord.voice.udp + +import dev.kord.common.annotation.KordVoice +import dev.kord.voice.io.ByteArrayView +import io.ktor.network.sockets.* +import io.ktor.network.selector.* +import io.ktor.network.sockets.Datagram +import io.ktor.utils.io.core.* +import kotlinx.coroutines.* +import kotlinx.coroutines.flow.* +import kotlinx.io.Source +import io.ktor.network.sockets.Datagram as KtorDatagram + +@KordVoice +public actual typealias SocketAddress = InetSocketAddress + +@KordVoice +public actual val GlobalVoiceUdpSocket: VoiceUdpSocket = object : VoiceUdpSocket { + private val socketScope = + CoroutineScope(Dispatchers.Default + SupervisorJob() + CoroutineName("kord-voice-global-socket")) + + private val socket = socketScope.async { + aSocket(SelectorManager(socketScope.coroutineContext)).udp().bind() + } + + private val incoming: MutableSharedFlow = MutableSharedFlow() + + init { + socketScope.launch { incoming.emitAll(socket.await().incoming) } + } + + override fun all(address: SocketAddress): Flow { + return incoming + .filter { it.address == address } + .map { it.packet } + } + + override suspend fun send(address: SocketAddress, packet: ByteArrayView) { + val brp = ByteReadPacket(packet.data, packet.dataStart, packet.viewSize) + socket.await().send(KtorDatagram(brp, address)) + } + + override suspend fun stop() { + } +} + diff --git a/voice/src/main/kotlin/udp/GlobalVoiceUdpSocket.kt b/voice/src/main/kotlin/udp/GlobalVoiceUdpSocket.kt deleted file mode 100644 index 4221bbf50d0..00000000000 --- a/voice/src/main/kotlin/udp/GlobalVoiceUdpSocket.kt +++ /dev/null @@ -1,77 +0,0 @@ -package dev.kord.voice.udp - -import dev.kord.common.annotation.KordVoice -import io.github.oshai.kotlinlogging.KotlinLogging -import io.ktor.network.selector.* -import io.ktor.network.sockets.* -import io.ktor.utils.io.core.* -import kotlinx.coroutines.* -import kotlinx.coroutines.flow.MutableSharedFlow -import kotlinx.coroutines.flow.SharedFlow -import kotlinx.coroutines.flow.emitAll -import kotlinx.io.Sink -import kotlinx.io.readString -import kotlinx.io.readUShort - -private val globalVoiceSocketLogger = KotlinLogging.logger { } - -private const val MESSAGE_LENGTH: Short = 70 -private const val DISCOVERY_DATA_SIZE: Int = 66 - -private const val REQUEST: Short = 0x01 -private const val RESPONSE: Short = 0x02 - -/** - * A global [VoiceUdpSocket] for all [dev.kord.voice.VoiceConnection]s, unless specified otherwise. - * Initiated once and kept open for the lifetime of this process. - */ -@KordVoice -public object GlobalVoiceUdpSocket : VoiceUdpSocket { - private val socketScope = - CoroutineScope(Dispatchers.Default + SupervisorJob() + CoroutineName("kord-voice-global-socket")) - - private val _incoming: MutableSharedFlow = MutableSharedFlow() - override val incoming: SharedFlow = _incoming - - private val socket = socketScope.async { - aSocket(ActorSelectorManager(socketScope.coroutineContext)).udp().bind() - } - - private val EMPTY_DATA = ByteArray(DISCOVERY_DATA_SIZE) - - init { - socketScope.launch { _incoming.emitAll(socket.await().incoming) } - } - - override suspend fun discoverIp(address: InetSocketAddress, ssrc: Int): InetSocketAddress { - globalVoiceSocketLogger.trace { "discovering ip" } - - send(packet(address) { - writeShort(REQUEST) - writeShort(MESSAGE_LENGTH) - writeInt(ssrc) - write(EMPTY_DATA) - }) - - return with(receiveFrom(address).packet) { - require(readShort() == RESPONSE) { "did not receive a response." } - require(readShort() == MESSAGE_LENGTH) { "expected $MESSAGE_LENGTH bytes of data." } - skip(byteCount = 4) // ssrc - - val ip = readString(byteCount = 64).trimEnd(0.toChar()) - val port = readUShort().toInt() - - InetSocketAddress(ip, port) - } - } - - override suspend fun send(packet: Datagram) { - socket.await().send(packet) - } - - override suspend fun stop() { /* this doesn't stop until the end of the process */ } - - private fun packet(address: SocketAddress, builder: Sink.() -> Unit): Datagram { - return Datagram(buildPacket(block = builder), address) - } -} diff --git a/voice/src/main/kotlin/udp/VoiceUdpSocket.kt b/voice/src/main/kotlin/udp/VoiceUdpSocket.kt deleted file mode 100644 index 609e5941aa9..00000000000 --- a/voice/src/main/kotlin/udp/VoiceUdpSocket.kt +++ /dev/null @@ -1,39 +0,0 @@ -package dev.kord.voice.udp - -import dev.kord.common.annotation.KordVoice -import io.ktor.network.sockets.* -import kotlinx.coroutines.flow.MutableSharedFlow -import kotlinx.coroutines.flow.SharedFlow -import kotlinx.coroutines.flow.filter -import kotlinx.coroutines.flow.first - -@KordVoice -public interface VoiceUdpSocket { - public val incoming: SharedFlow - - public suspend fun discoverIp(address: InetSocketAddress, ssrc: Int): InetSocketAddress - - public suspend fun send(packet: Datagram) - - public suspend fun stop() - - public companion object { - private object None : VoiceUdpSocket { - override val incoming: SharedFlow = MutableSharedFlow() - - override suspend fun discoverIp(address: InetSocketAddress, ssrc: Int): InetSocketAddress { - return address - } - - override suspend fun send(packet: Datagram) {} - - override suspend fun stop() {} - } - - public fun none(): VoiceUdpSocket = None - } -} - -@KordVoice -public suspend fun VoiceUdpSocket.receiveFrom(address: InetSocketAddress): Datagram = - incoming.filter { it.address == address }.first() diff --git a/voice/src/nonJvmMain/kotlin/dev/kord/voice/streams/DefaultStreams.kt b/voice/src/nonJvmMain/kotlin/dev/kord/voice/streams/DefaultStreams.kt new file mode 100644 index 00000000000..51df4c37d3e --- /dev/null +++ b/voice/src/nonJvmMain/kotlin/dev/kord/voice/streams/DefaultStreams.kt @@ -0,0 +1,29 @@ +package dev.kord.voice.streams + +import com.ionspin.kotlin.crypto.secretbox.SecretBox +import com.ionspin.kotlin.crypto.secretbox.crypto_secretbox_NONCEBYTES +import dev.kord.voice.encryption.strategies.NonceStrategy +import dev.kord.voice.io.mutableCursor +import dev.kord.voice.io.view +import dev.kord.voice.udp.RTPPacket +import kotlinx.coroutines.flow.Flow +import kotlinx.coroutines.flow.mapNotNull + +@OptIn(ExperimentalUnsignedTypes::class) +internal actual fun Flow.decrypt(nonceStrategy: NonceStrategy, key: ByteArray): Flow { + val nonceBuffer = ByteArray(crypto_secretbox_NONCEBYTES).mutableCursor() + val uKey = key.asUByteArray() + + return mapNotNull { + nonceBuffer.reset() + nonceBuffer.writeByteView(nonceStrategy.strip(it)) + + val decrypted = SecretBox.openEasy( + it.payload.toByteArray().asUByteArray(), + nonceBuffer.data.asUByteArray(), + uKey + ) + + it.copy(payload = decrypted.asByteArray().view()) + } +} diff --git a/voice/src/nonJvmMain/kotlin/dev/kord/voice/udp/DefaultAudioPacketProvider.kt b/voice/src/nonJvmMain/kotlin/dev/kord/voice/udp/DefaultAudioPacketProvider.kt new file mode 100644 index 00000000000..2b5766b912e --- /dev/null +++ b/voice/src/nonJvmMain/kotlin/dev/kord/voice/udp/DefaultAudioPacketProvider.kt @@ -0,0 +1,67 @@ +package dev.kord.voice.udp + +import com.ionspin.kotlin.crypto.secretbox.SecretBox +import com.ionspin.kotlin.crypto.secretbox.crypto_secretbox_NONCEBYTES +import dev.kord.voice.encryption.strategies.NonceStrategy +import dev.kord.voice.io.ByteArrayView +import dev.kord.voice.io.MutableByteArrayCursor +import dev.kord.voice.io.mutableCursor +import dev.kord.voice.io.view +import kotlinx.atomicfu.locks.SynchronizedObject +import kotlinx.atomicfu.locks.synchronized + +@Suppress("FunctionName") +public actual fun DefaultAudioPacketProvider(key: ByteArray, nonceStrategy: NonceStrategy): AudioPacketProvider = + DefaultNativeAudioPacketProvider(key, nonceStrategy) + +public class DefaultNativeAudioPacketProvider(key: ByteArray, nonceStrategy: NonceStrategy) : + AudioPacketProvider(key, nonceStrategy) { + + private val packetBuffer = ByteArray(2048) + private val packetBufferCursor: MutableByteArrayCursor = packetBuffer.mutableCursor() + private val packetBufferView: ByteArrayView = packetBuffer.view() + + private val rtpHeaderView: ByteArrayView = packetBuffer.view(0, RTP_HEADER_LENGTH)!! + + private val nonceBuffer: MutableByteArrayCursor = ByteArray(crypto_secretbox_NONCEBYTES).mutableCursor() + + private val lock = SynchronizedObject() + + @OptIn(ExperimentalUnsignedTypes::class) + override fun provide(sequence: UShort, timestamp: UInt, ssrc: UInt, data: ByteArray): ByteArrayView = + synchronized(lock) { + with(packetBufferCursor) { + this.reset() + nonceBuffer.reset() + + // make sure we enough room in this buffer + resize(RTP_HEADER_LENGTH + (data.size + 16) + nonceStrategy.nonceLength) + + // write header and generate nonce + writeHeader(sequence.toShort(), timestamp.toInt(), ssrc.toInt()) + + val rawNonce = nonceStrategy.generate { rtpHeaderView } + nonceBuffer.writeByteView(rawNonce) + + // encrypt data and write into our buffer + try { + writeByteArray( + SecretBox.easy( + data.toUByteArray(), + nonceBuffer.data.toUByteArray(), + key.toUByteArray() + ).toByteArray() + ) + } catch (e: Throwable) { + throw CouldNotEncryptDataException(data, e) + } + + nonceStrategy.append(rawNonce, this) + + // let's make sure we have the correct view of the packet + if (!packetBufferView.resize(0, cursor)) error("couldn't resize packet buffer view?!") + + packetBufferView + } + } +} diff --git a/voice/src/nonKtorMain/kotlin/VoiceUdpSocket.js.kt b/voice/src/nonKtorMain/kotlin/VoiceUdpSocket.js.kt new file mode 100644 index 00000000000..3c5972f6720 --- /dev/null +++ b/voice/src/nonKtorMain/kotlin/VoiceUdpSocket.js.kt @@ -0,0 +1,9 @@ +package dev.kord.voice.udp + +import dev.kord.common.annotation.KordVoice + +@KordVoice +public actual data class SocketAddress actual constructor( + public actual val hostname: String, + public actual val port: Int, +)