diff --git a/buildSrc/src/main/kotlin/HmppConfigure.kt b/buildSrc/src/main/kotlin/HmppConfigure.kt index 7469cbcdb..5e851695f 100644 --- a/buildSrc/src/main/kotlin/HmppConfigure.kt +++ b/buildSrc/src/main/kotlin/HmppConfigure.kt @@ -16,15 +16,10 @@ import org.jetbrains.kotlin.gradle.plugin.KotlinCompilation.Companion.MAIN_COMPI import org.jetbrains.kotlin.gradle.plugin.KotlinCompilation.Companion.TEST_COMPILATION_NAME import org.jetbrains.kotlin.gradle.plugin.KotlinPlatformType import org.jetbrains.kotlin.gradle.plugin.KotlinSourceSet - -/* - * Copyright 2019-2022 Mamoe Technologies and contributors. - * - * 此源代码的使用受 GNU AFFERO GENERAL PUBLIC LICENSE version 3 许可证的约束, 可以在以下链接找到该许可证. - * Use of this source code is governed by the GNU AGPLv3 license that can be found through the following link. - * - * https://github.com/mamoe/mirai/blob/dev/LICENSE - */ +import org.jetbrains.kotlin.gradle.plugin.mpp.KotlinNativeCompilation +import org.jetbrains.kotlin.gradle.plugin.mpp.KotlinNativeTarget +import org.jetbrains.kotlin.gradle.plugin.mpp.NativeBuildType +import java.io.File private val miraiPlatform = Attribute.of( "net.mamoe.mirai.platform", @@ -35,6 +30,9 @@ private val miraiPlatform = Attribute.of( fun Project.configureHMPPJvm() { extensions.getByType(KotlinMultiplatformExtension::class.java).apply { jvm("jvmBase") { + compilations.all { + this.compileKotlinTask.enabled = false // IDE complain + } attributes.attribute(KotlinPlatformType.attribute, KotlinPlatformType.common) // avoid resolving by others // attributes.attribute(miraiPlatform, "jvmBase") } @@ -57,16 +55,18 @@ fun Project.configureHMPPJvm() { val nativeMainSets = mutableListOf() val nativeTestSets = mutableListOf() + val nativeTargets = mutableListOf() if (ideaActive) { - when { + val target = when { Os.isFamily(Os.FAMILY_MAC) -> if (Os.isArch("aarch64")) macosArm64("native") else macosX64("native") Os.isFamily(Os.FAMILY_WINDOWS) -> mingwX64("native") else -> linuxX64("native") } + nativeTargets.add(target) } else { // 1.6.0 - val nativeTargets: List = arrayOf( + val nativeTargetNames: List = arrayOf( // serialization doesn't support those commented targets // "androidNativeArm32, androidNativeArm64, androidNativeX86, androidNativeX64", "iosArm32, iosArm64, iosX64, iosSimulatorArm64", @@ -77,11 +77,12 @@ fun Project.configureHMPPJvm() { "mingwX64", // "wasm32" // linuxArm32Hfp, mingwX86 ).flatMap { it.split(",") }.map { it.trim() } - presets.filter { it.name in nativeTargets } + presets.filter { it.name in nativeTargetNames } .forEach { preset -> - val target = targetFromPreset(preset, preset.name) + val target = targetFromPreset(preset, preset.name) as KotlinNativeTarget nativeMainSets.add(target.compilations[MAIN_COMPILATION_NAME].kotlinSourceSets.first()) nativeTestSets.add(target.compilations[TEST_COMPILATION_NAME].kotlinSourceSets.first()) + nativeTargets.add(target) } if (!ideaActive) { @@ -95,31 +96,8 @@ fun Project.configureHMPPJvm() { } } -// nativeTarget.apply { -// val myrust by compilations.getByName("main").cinterops.creating { -// headers(project.projectDir.resolve("untitled/myrust.h")) -// } -// -// binaries { -// sharedLib { -// linkerOpts("-v") -// linkerOpts("-L${project.projectDir.resolve("untitled/target/debug/").absolutePath}") -//// linkerOpts("-lmyrust") -// linkerOpts("-Wl,-undefined,dynamic_lookup") // resolve symbols in runtime -// baseName = "mykotlin" -// } -// -// executable { -// -// linkerOpts("-v") -// linkerOpts("-L${project.projectDir.resolve("untitled/target/debug/").absolutePath}") -//// linkerOpts("-lmyrust") -// linkerOpts("-Wl,-undefined,dynamic_lookup") // resolve symbols in runtime -// baseName = "KotlinExecutable" -// entryPoint = "main.main" -// } -// } -// } + configureNativeInterop("main", projectDir.resolve("src/nativeMainInterop"), nativeTargets) + configureNativeInterop("test", projectDir.resolve("src/nativeTestInterop"), nativeTargets) val sourceSets = kotlinSourceSets.orEmpty() @@ -143,9 +121,181 @@ fun Project.configureHMPPJvm() { androidMain.dependsOn(jvmBaseMain) jvmTest.dependsOn(jvmBaseTest) - androidTest.dependsOn(commonTest) + androidTest.dependsOn(jvmBaseTest) nativeMain.dependsOn(commonMain) nativeTest.dependsOn(commonTest) } +} + +private fun Project.linkerDirs(): List { + return listOf( + ":mirai-core", + ":mirai-core-api", + ":mirai-core-utils", + ).map { + rootProject.project(it).projectDir.resolve("src/nativeMainInterop/target/debug/").absolutePath + } +} + +private fun Project.includeDirs(): List { + return listOf( + ":mirai-core", + ":mirai-core-api", + ":mirai-core-utils", + ).map { + rootProject.project(it).projectDir.resolve("src/nativeMainInterop/").absolutePath + } +} + +private fun Project.configureNativeInterop( + compilationName: String, + nativeInteropDir: File, + nativeTargets: MutableList +) { + val crateName = project.name.replace("-", "_") + "_i" + + configure(nativeTargets) { + binaries { + for (buildType in NativeBuildType.values()) { + findTest(buildType)?.apply { + linkerOpts("-v") + linkerOpts(*linkerDirs().map { "-L$it" }.toTypedArray()) + linkerOpts("-undefined", "dynamic_lookup") // resolve symbol in runtime + } + } + } + } + if (nativeInteropDir.exists() && nativeInteropDir.isDirectory && nativeInteropDir.resolve("build.rs").exists()) { + val kotlinDylibName = project.name.replace("-", "_") + "_i" + val kotlinDylibName = project.name.replace("-", "_") + + val headerName = "$crateName.h" + val rustLibDir = nativeInteropDir.resolve("target/debug/") + + var interopTaskName = "" + + configure(nativeTargets) { + interopTaskName = compilations.getByName(compilationName).cinterops.create(compilationName) { + defFile(nativeInteropDir.resolve("interop.def")) + val headerFile = nativeInteropDir.resolve(headerName) + if (headerFile.exists()) headers(headerFile) + }.interopProcessingTaskName + + binaries { + sharedLib { + linkerOpts("-v") + linkerOpts("-L${rustLibDir.absolutePath.replace("\\", "/")}") +// linkerOpts("-lmirai_core_utils_i") + linkerOpts("-undefined", "dynamic_lookup") + baseName = project.name + } + getTest(NativeBuildType.DEBUG).apply { + linkerOpts("-v") + linkerOpts("-L${rustLibDir.absolutePath.replace("\\", "/")}") + linkerOpts("-lmirai_core_utils_i") +// linkerOpts("-undefined", "dynamic_lookup") + } + } + } + + val cbindgen = tasks.register("cbindgen${compilationName.titlecase()}") { + group = "mirai" + description = "Generate C Headers from Rust" + inputs.files( + project.objects.fileTree().from(nativeInteropDir.resolve("src")) + .filterNot { it.name == "bindings.rs" } + ) + outputs.file(nativeInteropDir.resolve(headerName)) + doLast { + exec { + workingDir(nativeInteropDir) + commandLine( + "cbindgen", + "--config", "cbindgen.toml", + "--crate", crateName, + "--output", headerName + ) + } + } + } + + val generateRustBindings = tasks.register("generateRustBindings${compilationName.titlecase()}") { + group = "mirai" + description = "Generates Rust bindings for Kotlin" + dependsOn(cbindgen) + } + + afterEvaluate { + val cinteropTask = tasks.getByName(interopTaskName) + cinteropTask.mustRunAfter(cbindgen) + generateRustBindings.get().dependsOn(cinteropTask) + } + + val bindgen = tasks.register("bindgen${compilationName.titlecase()}") { + group = "mirai" + val bindingsPath = nativeInteropDir.resolve("src/bindings.rs") + val headerFile = buildDir.resolve("bin/native/debugShared/lib${kotlinDylibName}_api.h") + inputs.files(headerFile) + outputs.file(bindingsPath) + mustRunAfter(tasks.findByName("linkDebugSharedNative")) + doLast { + exec { + workingDir(nativeInteropDir) + // bindgen input.h -o bindings.rs + commandLine( + "bindgen", + headerFile, + "-o", bindingsPath, + ) + } + } + } + + val generateKotlinBindings = tasks.register("generateKotlinBindings${compilationName.titlecase()}") { + group = "mirai" + description = "Generates Kotlin bindings for Rust" + dependsOn(bindgen) + dependsOn(tasks.findByName("linkDebugSharedNative")) + } + + var targetCompilation: KotlinNativeCompilation? = null + configure(nativeTargets) { + val compilations = compilations.filter { nativeInteropDir.name.contains(it.name, ignoreCase = true) } + check(compilations.isNotEmpty()) { "Should be at lease one corresponding native compilation, but found 0" } + targetCompilation = compilations.single() +// targetCompilation!!.compileKotlinTask.dependsOn(cbindgen) +// tasks.getByName("cinteropNative$name").dependsOn(cbindgen) + } + targetCompilation!! + + val compileRust = tasks.register("compileRust${compilationName.titlecase()}") { + group = "mirai" + inputs.files(nativeInteropDir.resolve("src")) + outputs.file(rustLibDir.resolve("lib$crateName.dylib")) +// dependsOn(targetCompilation!!.compileKotlinTask) + dependsOn(bindgen) + dependsOn(tasks.findByName("linkDebugSharedNative")) // dylib to link + doLast { + exec { + workingDir(nativeInteropDir) + commandLine( + "cargo", + "build", + "--color", "always", + "--all", +// "--", "--color", "always", "2>&1" + ) + } + } + } + + tasks.getByName("assemble").dependsOn(compileRust) + } +} + +fun String.titlecase(): String { + if (this.isEmpty()) return this + val c = get(0) + return replaceFirst(c, Character.toTitleCase(c)) } \ No newline at end of file diff --git a/buildSrc/src/main/kotlin/ProjectConfigure.kt b/buildSrc/src/main/kotlin/ProjectConfigure.kt index 0a2d280dd..7827c1b4d 100644 --- a/buildSrc/src/main/kotlin/ProjectConfigure.kt +++ b/buildSrc/src/main/kotlin/ProjectConfigure.kt @@ -21,6 +21,7 @@ import org.jetbrains.kotlin.gradle.plugin.KotlinPlatformType import org.jetbrains.kotlin.gradle.plugin.KotlinSourceSet import org.jetbrains.kotlin.gradle.plugin.KotlinTarget import org.jetbrains.kotlin.gradle.targets.jvm.KotlinJvmTarget +import org.jetbrains.kotlin.gradle.tasks.KotlinJvmCompile fun Project.useIr() { @@ -41,7 +42,6 @@ fun Project.preConfigureJvmTarget() { val defaultVer = jvmVersion() tasks.withType(KotlinJvmCompile::class.java) { - kotlinOptions.languageVersion = "1.6" kotlinOptions.jvmTarget = defaultVer.toString() kotlinOptions.freeCompilerArgs += "-Xjvm-default=all" @@ -61,8 +61,8 @@ fun Project.preConfigureJvmTarget() { fun Project.configureJvmTarget() { val defaultVer = jvmVersion() - tasks.withType(KotlinJvmCompile::class) - .filter { it.name.startsWith("compileTestKotlin") } + tasks.withType(KotlinCompile::class) + .filter { it.name.contains("test", ignoreCase = true) } .forEach { task -> task.kotlinOptions.freeCompilerArgs += "-Xopt-in=net.mamoe.mirai.utils.TestOnly" } diff --git a/buildSrc/src/main/kotlin/Versions.kt b/buildSrc/src/main/kotlin/Versions.kt index 694fddfd6..4e564e2ed 100644 --- a/buildSrc/src/main/kotlin/Versions.kt +++ b/buildSrc/src/main/kotlin/Versions.kt @@ -22,11 +22,11 @@ object Versions { val consoleIntellij = "221-$project-162-1" // idea-mirai-kotlin-patch val consoleTerminal = project - const val kotlinCompiler = "1.6.21" + const val kotlinCompiler = "1.7.0-RC" const val kotlinStdlib = kotlinCompiler - const val dokka = "1.6.20" + const val dokka = "1.6.21" - const val kotlinCompilerForIdeaPlugin = "1.6.20" + const val kotlinCompilerForIdeaPlugin = "1.7.0-RC" const val coroutines = "1.6.1" const val atomicFU = "0.17.2" @@ -38,8 +38,8 @@ object Versions { const val io = "0.1.16" const val coroutinesIo = "0.1.16" - const val blockingBridge = "2.1.0-162.1" - const val dynamicDelegation = "0.3.0-162.4" + const val blockingBridge = "2.1.0-170.1" + const val dynamicDelegation = "0.3.0-170.1" const val androidGradlePlugin = "4.1.1" const val android = "4.1.1.4" @@ -106,13 +106,13 @@ val `kotlinx-coroutines-io` = kotlinx("coroutines-io", Versions.coroutinesIo) val `ktor-serialization` = ktor("serialization", Versions.ktor) -val `ktor-client-core` = ktor("client-core-jvm", Versions.ktor) -val `ktor-client-cio` = ktor("client-cio-jvm", Versions.ktor) +val `ktor-client-core` = ktor("client-core", Versions.ktor) +val `ktor-client-cio` = ktor("client-cio", Versions.ktor) val `ktor-client-okhttp` = ktor("client-okhttp", Versions.ktor) val `ktor-client-android` = ktor("client-android", Versions.ktor) -val `ktor-client-logging` = ktor("client-logging-jvm", Versions.ktor) +val `ktor-client-logging` = ktor("client-logging", Versions.ktor) val `ktor-network` = ktor("network-jvm", Versions.ktor) -val `ktor-client-serialization` = ktor("client-serialization-jvm", Versions.ktor) +val `ktor-client-serialization` = ktor("client-serialization", Versions.ktor) const val `logback-classic` = "ch.qos.logback:logback-classic:" + Versions.logback diff --git a/gradle.properties b/gradle.properties index ab45b776f..4fb2fcd07 100644 --- a/gradle.properties +++ b/gradle.properties @@ -13,6 +13,7 @@ org.gradle.jvmargs=-Xmx4096m -Dfile.encoding=UTF-8 --illegal-access=permit -Dkot org.gradle.parallel=true org.gradle.vfs.watch=true kotlin.mpp.enableGranularSourceSetsMetadata=true +kotlin.native.binary.memoryModel=experimental kotlin.native.enableDependencyPropagation=false #kotlin.mpp.enableCompatibilityMetadataVariant=true #kotlin.mpp.enableGranularSourceSetsMetadata=true @@ -21,4 +22,5 @@ gnsp.disableApplyOnlyOnRootProjectEnforcement=true # We may target 15 with Kotlin 1.5 IR mirai.android.target.api.level=24 # Enable if you want to use mavenLocal for both Gradle plugin and project dependencies resolutions. -systemProp.use.maven.local=false \ No newline at end of file +systemProp.use.maven.local=false +org.gradle.caching=true \ No newline at end of file diff --git a/install.sh b/install.sh new file mode 100644 index 000000000..17edd0362 --- /dev/null +++ b/install.sh @@ -0,0 +1,11 @@ +# +# Copyright 2019-2022 Mamoe Technologies and contributors. +# +# 此源代码的使用受 GNU AFFERO GENERAL PUBLIC LICENSE version 3 许可证的约束, 可以在以下链接找到该许可证. +# Use of this source code is governed by the GNU AGPLv3 license that can be found through the following link. +# +# https://github.com/mamoe/mirai/blob/dev/LICENSE +# + +cargo install --force cbindgen +cargo install bindgen \ No newline at end of file diff --git a/mirai-console/backend/integration-test/test/MiraiConsoleIntegrationTestBootstrap.kt b/mirai-console/backend/integration-test/test/MiraiConsoleIntegrationTestBootstrap.kt index f0fac32e2..cbbce61b4 100644 --- a/mirai-console/backend/integration-test/test/MiraiConsoleIntegrationTestBootstrap.kt +++ b/mirai-console/backend/integration-test/test/MiraiConsoleIntegrationTestBootstrap.kt @@ -10,7 +10,6 @@ package net.mamoe.console.integrationtest import net.mamoe.console.integrationtest.testpoints.MCITBSelfAssertions -import org.junit.jupiter.api.Test import org.objectweb.asm.ClassReader import java.io.File import java.lang.management.ManagementFactory @@ -22,6 +21,7 @@ import kotlin.io.path.inputStream import kotlin.io.path.isDirectory import kotlin.io.path.name import kotlin.reflect.KClass +import kotlin.test.Test import kotlin.test.assertTrue diff --git a/mirai-console/backend/integration-test/test/testpoints/plugin/PluginDataRenameToIdTest.kt b/mirai-console/backend/integration-test/test/testpoints/plugin/PluginDataRenameToIdTest.kt index d161a6121..99d8913da 100644 --- a/mirai-console/backend/integration-test/test/testpoints/plugin/PluginDataRenameToIdTest.kt +++ b/mirai-console/backend/integration-test/test/testpoints/plugin/PluginDataRenameToIdTest.kt @@ -1,5 +1,5 @@ /* - * Copyright 2019-2021 Mamoe Technologies and contributors. + * Copyright 2019-2022 Mamoe Technologies and contributors. * * 此源代码的使用受 GNU AFFERO GENERAL PUBLIC LICENSE version 3 许可证的约束, 可以在以下链接找到该许可证. * Use of this source code is governed by the GNU AGPLv3 license that can be found through the following link. @@ -17,7 +17,6 @@ import net.mamoe.mirai.console.data.value import net.mamoe.mirai.console.extension.PluginComponentStorage import net.mamoe.mirai.console.plugin.jvm.JvmPluginDescription import net.mamoe.mirai.console.plugin.jvm.KotlinPlugin -import net.mamoe.mirai.utils.touch import java.io.File import kotlin.test.assertEquals @@ -40,13 +39,14 @@ internal object PluginDataRenameToIdTest : AbstractTestPointAsPlugin() { } override fun beforeConsoleStartup() { - File("config/PluginDataRenameToIdTest/test.txt").touch() + File("config/PluginDataRenameToIdTest").mkdirs() + File("config/PluginDataRenameToIdTest/test.txt").createNewFile() File("config/PluginDataRenameToIdTest/testconf.yml").writeText( """ test: a """.trimIndent() ) - File("data/PluginDataRenameToIdTest/test.txt").touch() + File("data/PluginDataRenameToIdTest/test.txt").createNewFile() File("data/PluginDataRenameToIdTest/testdata.yml").writeText( """ test: a diff --git a/mirai-console/backend/mirai-console/test/command/InstanceTestCommand.kt b/mirai-console/backend/mirai-console/test/command/InstanceTestCommand.kt index 71dccf4d2..a6eca0a09 100644 --- a/mirai-console/backend/mirai-console/test/command/InstanceTestCommand.kt +++ b/mirai-console/backend/mirai-console/test/command/InstanceTestCommand.kt @@ -28,9 +28,6 @@ import net.mamoe.mirai.console.internal.command.flattenCommandComponents import net.mamoe.mirai.console.permission.PermissionService.Companion.permit import net.mamoe.mirai.console.testFramework.AbstractConsoleInstanceTest import net.mamoe.mirai.message.data.* -import org.junit.jupiter.api.BeforeEach -import org.junit.jupiter.api.Test -import org.junit.jupiter.api.TestInstance import java.time.* import java.time.temporal.TemporalAccessor import kotlin.reflect.KClass @@ -158,7 +155,6 @@ class TestTemporalArgCommand : CompositeCommand(owner, "testtemporal") { private val sender get() = ConsoleCommandSender private val owner get() = ConsoleCommandOwner -@TestInstance(TestInstance.Lifecycle.PER_METHOD) @OptIn(ExperimentalCommandDescriptors::class) internal class InstanceTestCommand : AbstractConsoleInstanceTest() { private val manager by lazy { MiraiConsoleImplementation.getBridge().commandManager as CommandManagerImpl } @@ -167,7 +163,7 @@ internal class InstanceTestCommand : AbstractConsoleInstanceTest() { private val rawCommand by lazy { TestRawCommand() } private val compositeCommand by lazy { TestCompositeCommand() } - @BeforeEach + @BeforeTest fun grantPermission() { ConsoleCommandSender.permit(simpleCommand.permission) ConsoleCommandSender.permit(compositeCommand.permission) diff --git a/mirai-console/backend/mirai-console/test/command/LoginCommandTest.kt b/mirai-console/backend/mirai-console/test/command/LoginCommandTest.kt index 5598789b0..c1da959fa 100644 --- a/mirai-console/backend/mirai-console/test/command/LoginCommandTest.kt +++ b/mirai-console/backend/mirai-console/test/command/LoginCommandTest.kt @@ -12,6 +12,7 @@ package net.mamoe.mirai.console.command import kotlinx.coroutines.CompletableDeferred +import kotlinx.coroutines.runBlocking import me.him188.kotlin.jvm.blocking.bridge.JvmBlockingBridge import net.mamoe.mirai.Bot import net.mamoe.mirai.console.command.CommandManager.INSTANCE.register @@ -23,7 +24,7 @@ import net.mamoe.mirai.console.internal.data.builtins.AutoLoginConfig.Account.Pa import net.mamoe.mirai.internal.QQAndroidBot import net.mamoe.mirai.utils.md5 import net.mamoe.mirai.utils.toUHexString -import org.junit.jupiter.api.Test +import kotlin.test.Test import kotlin.test.assertContentEquals import kotlin.test.assertEquals @@ -32,7 +33,7 @@ import kotlin.test.assertEquals internal class LoginCommandTest : AbstractCommandTest() { @Test - suspend fun `login with provided password`() { + fun `login with provided password`() = runBlocking { val myId = 123L val myPwd = "password001" @@ -52,7 +53,7 @@ internal class LoginCommandTest : AbstractCommandTest() { } @Test - suspend fun `login with saved plain password`() { + fun `login with saved plain password`() = runBlocking { val myId = 123L val myPwd = "password001" @@ -81,7 +82,7 @@ internal class LoginCommandTest : AbstractCommandTest() { } @Test - suspend fun `login with saved md5 password`() { + fun `login with saved md5 password`() = runBlocking { val myId = 123L val myPwd = "password001" diff --git a/mirai-console/backend/mirai-console/test/configuration/AutoLoginTest.kt b/mirai-console/backend/mirai-console/test/configuration/AutoLoginTest.kt index 3c408b5f6..d1884b7e1 100644 --- a/mirai-console/backend/mirai-console/test/configuration/AutoLoginTest.kt +++ b/mirai-console/backend/mirai-console/test/configuration/AutoLoginTest.kt @@ -17,7 +17,7 @@ import net.mamoe.mirai.event.events.BotOnlineEvent import net.mamoe.mirai.event.globalEventChannel import net.mamoe.mirai.utils.BotConfiguration import org.junit.jupiter.api.Disabled -import org.junit.jupiter.api.Test +import kotlin.test.Test import kotlin.test.assertEquals class AutoLoginTest : AbstractConsoleInstanceTest() { diff --git a/mirai-console/backend/mirai-console/test/data/JAutoSavePluginDataTest.kt b/mirai-console/backend/mirai-console/test/data/JAutoSavePluginDataTest.kt index ed46bfdf3..de7fcbf39 100644 --- a/mirai-console/backend/mirai-console/test/data/JAutoSavePluginDataTest.kt +++ b/mirai-console/backend/mirai-console/test/data/JAutoSavePluginDataTest.kt @@ -13,7 +13,7 @@ import net.mamoe.mirai.console.data.java.JavaAutoSavePluginData import net.mamoe.mirai.console.plugin.jvm.reloadPluginData import net.mamoe.mirai.console.testFramework.AbstractConsoleInstanceTest import net.mamoe.mirai.console.util.JavaFriendlyApi -import org.junit.jupiter.api.Test +import kotlin.test.Test import kotlin.test.assertEquals diff --git a/mirai-console/backend/mirai-console/test/data/PluginDataTest.kt b/mirai-console/backend/mirai-console/test/data/PluginDataTest.kt index cc0ef7eca..7cf4abc0c 100644 --- a/mirai-console/backend/mirai-console/test/data/PluginDataTest.kt +++ b/mirai-console/backend/mirai-console/test/data/PluginDataTest.kt @@ -19,9 +19,9 @@ import net.mamoe.mirai.message.data.PlainText import net.mamoe.mirai.message.data.SingleMessage import net.mamoe.mirai.message.data.messageChainOf import net.mamoe.mirai.utils.mapPrimitive -import org.junit.jupiter.api.Test import org.junit.jupiter.api.io.TempDir import java.nio.file.Path +import kotlin.test.Test import kotlin.test.assertEquals import kotlin.test.assertSame diff --git a/mirai-console/backend/mirai-console/test/data/PluginMovingTests.kt b/mirai-console/backend/mirai-console/test/data/PluginMovingTests.kt index 8878ef214..4ba670022 100644 --- a/mirai-console/backend/mirai-console/test/data/PluginMovingTests.kt +++ b/mirai-console/backend/mirai-console/test/data/PluginMovingTests.kt @@ -18,7 +18,7 @@ import net.mamoe.mirai.console.plugin.jvm.JvmPluginDescription import net.mamoe.mirai.console.plugin.jvm.KotlinPlugin import net.mamoe.mirai.console.plugin.name import net.mamoe.mirai.console.testFramework.AbstractConsoleInstanceTest -import org.junit.jupiter.api.Test +import kotlin.test.Test class PluginMovingTests : AbstractConsoleInstanceTest() { private val mockPluginWithName = object : KotlinPlugin(JvmPluginDescription("org.test1.test1", "1.0.0", "test1")) {} diff --git a/mirai-console/backend/mirai-console/test/extension/GlobalComponentStorageTest.kt b/mirai-console/backend/mirai-console/test/extension/GlobalComponentStorageTest.kt index 450385faf..997a79429 100644 --- a/mirai-console/backend/mirai-console/test/extension/GlobalComponentStorageTest.kt +++ b/mirai-console/backend/mirai-console/test/extension/GlobalComponentStorageTest.kt @@ -12,7 +12,7 @@ package net.mamoe.mirai.console.extension import net.mamoe.mirai.console.internal.extension.GlobalComponentStorage import net.mamoe.mirai.console.internal.extension.GlobalComponentStorageImpl import net.mamoe.mirai.console.testFramework.AbstractConsoleInstanceTest -import org.junit.jupiter.api.Test +import kotlin.test.Test import kotlin.test.assertEquals internal class GlobalComponentStorageTest : AbstractConsoleInstanceTest() { diff --git a/mirai-console/backend/mirai-console/test/logging/TestALC_PathBased.kt b/mirai-console/backend/mirai-console/test/logging/TestALC_PathBased.kt index df3a1476c..17cebc650 100644 --- a/mirai-console/backend/mirai-console/test/logging/TestALC_PathBased.kt +++ b/mirai-console/backend/mirai-console/test/logging/TestALC_PathBased.kt @@ -1,5 +1,5 @@ /* - * Copyright 2019-2021 Mamoe Technologies and contributors. + * Copyright 2019-2022 Mamoe Technologies and contributors. * * 此源代码的使用受 GNU AFFERO GENERAL PUBLIC LICENSE version 3 许可证的约束, 可以在以下链接找到该许可证. * Use of this source code is governed by the GNU AGPLv3 license that can be found through the following link. @@ -9,7 +9,7 @@ package net.mamoe.mirai.console.logging -import org.junit.jupiter.api.Test +import kotlin.test.Test @Suppress("ClassName") internal class TestALC_PathBased { diff --git a/mirai-console/backend/mirai-console/test/permission/PermissionServiceTest.kt b/mirai-console/backend/mirai-console/test/permission/PermissionServiceTest.kt index 6c3659c5f..7134eb1f9 100644 --- a/mirai-console/backend/mirai-console/test/permission/PermissionServiceTest.kt +++ b/mirai-console/backend/mirai-console/test/permission/PermissionServiceTest.kt @@ -11,7 +11,6 @@ package net.mamoe.mirai.console.permission import net.mamoe.mirai.console.internal.permission.BuiltInPermissionService import net.mamoe.mirai.console.internal.permission.PermissionImpl -import org.junit.jupiter.api.Test import kotlin.test.* internal class PermissionServiceTest { diff --git a/mirai-console/backend/mirai-console/test/permission/PermissionsBasicsTest.kt b/mirai-console/backend/mirai-console/test/permission/PermissionsBasicsTest.kt index a07ce6c77..7dcc58f2e 100644 --- a/mirai-console/backend/mirai-console/test/permission/PermissionsBasicsTest.kt +++ b/mirai-console/backend/mirai-console/test/permission/PermissionsBasicsTest.kt @@ -1,5 +1,5 @@ /* - * Copyright 2019-2021 Mamoe Technologies and contributors. + * Copyright 2019-2022 Mamoe Technologies and contributors. * * 此源代码的使用受 GNU AFFERO GENERAL PUBLIC LICENSE version 3 许可证的约束, 可以在以下链接找到该许可证. * Use of this source code is governed by the GNU AGPLv3 license that can be found through the following link. @@ -9,7 +9,7 @@ package net.mamoe.mirai.console.permission -import org.junit.jupiter.api.Test +import kotlin.test.Test import kotlin.test.assertFails internal class PermissionsBasicsTest { diff --git a/mirai-console/backend/mirai-console/test/testFramework/AbstractConsoleInstanceTest.kt b/mirai-console/backend/mirai-console/test/testFramework/AbstractConsoleInstanceTest.kt index 73570ae6c..a6af33268 100644 --- a/mirai-console/backend/mirai-console/test/testFramework/AbstractConsoleInstanceTest.kt +++ b/mirai-console/backend/mirai-console/test/testFramework/AbstractConsoleInstanceTest.kt @@ -19,14 +19,14 @@ import net.mamoe.mirai.console.command.CommandManager import net.mamoe.mirai.console.plugin.jvm.JvmPluginDescription import net.mamoe.mirai.console.plugin.jvm.KotlinPlugin import org.junit.jupiter.api.AfterEach -import org.junit.jupiter.api.BeforeEach +import kotlin.test.BeforeTest abstract class AbstractConsoleInstanceTest { val mockPlugin by lazy { mockKotlinPlugin() } private lateinit var implementation: MiraiConsoleImplementation val consoleImplementation: MiraiConsoleImplementation by ::implementation - @BeforeEach + @BeforeTest protected open fun initializeConsole() { this.implementation = MockConsoleImplementation().apply { start() } CommandManager diff --git a/mirai-console/backend/mirai-console/test/testFramework/test/FrameworkInstanceTest.kt b/mirai-console/backend/mirai-console/test/testFramework/test/FrameworkInstanceTest.kt index 93501f666..20424bcbe 100644 --- a/mirai-console/backend/mirai-console/test/testFramework/test/FrameworkInstanceTest.kt +++ b/mirai-console/backend/mirai-console/test/testFramework/test/FrameworkInstanceTest.kt @@ -1,5 +1,5 @@ /* - * Copyright 2019-2021 Mamoe Technologies and contributors. + * Copyright 2019-2022 Mamoe Technologies and contributors. * * 此源代码的使用受 GNU AFFERO GENERAL PUBLIC LICENSE version 3 许可证的约束, 可以在以下链接找到该许可证. * Use of this source code is governed by the GNU AGPLv3 license that can be found through the following link. @@ -9,9 +9,9 @@ package net.mamoe.mirai.console.testFramework.test -import net.mamoe.mirai.console.testFramework.AbstractConsoleInstanceTest import net.mamoe.mirai.console.plugin.PluginManager -import org.junit.jupiter.api.Test +import net.mamoe.mirai.console.testFramework.AbstractConsoleInstanceTest +import kotlin.test.Test import kotlin.test.assertEquals class FrameworkInstanceTest : AbstractConsoleInstanceTest() { diff --git a/mirai-console/backend/mirai-console/test/util/TestCoroutineUtils.kt b/mirai-console/backend/mirai-console/test/util/TestCoroutineUtils.kt index da37c1e92..11c6b4de4 100644 --- a/mirai-console/backend/mirai-console/test/util/TestCoroutineUtils.kt +++ b/mirai-console/backend/mirai-console/test/util/TestCoroutineUtils.kt @@ -1,17 +1,17 @@ /* - * Copyright 2019-2021 Mamoe Technologies and contributors. + * Copyright 2019-2022 Mamoe Technologies and contributors. * - * 此源代码的使用受 GNU AFFERO GENERAL PUBLIC LICENSE version 3 许可证的约束, 可以在以下链接找到该许可证. - * Use of this source code is governed by the GNU AGPLv3 license that can be found through the following link. + * 此源代码的使用受 GNU AFFERO GENERAL PUBLIC LICENSE version 3 许可证的约束, 可以在以下链接找到该许可证. + * Use of this source code is governed by the GNU AGPLv3 license that can be found through the following link. * - * https://github.com/mamoe/mirai/blob/master/LICENSE + * https://github.com/mamoe/mirai/blob/dev/LICENSE */ package net.mamoe.mirai.console.util //import kotlinx.coroutines.* -//import org.junit.jupiter.api.Test +//import kotlin.test.Test //import java.util.concurrent.atomic.AtomicInteger //import kotlin.coroutines.resume //import kotlin.test.assertEquals diff --git a/mirai-console/backend/mirai-console/test/util/TestSemVersion.kt b/mirai-console/backend/mirai-console/test/util/TestSemVersion.kt index 7183b615b..07d130453 100644 --- a/mirai-console/backend/mirai-console/test/util/TestSemVersion.kt +++ b/mirai-console/backend/mirai-console/test/util/TestSemVersion.kt @@ -1,5 +1,5 @@ /* - * Copyright 2019-2021 Mamoe Technologies and contributors. + * Copyright 2019-2022 Mamoe Technologies and contributors. * * 此源代码的使用受 GNU AFFERO GENERAL PUBLIC LICENSE version 3 许可证的约束, 可以在以下链接找到该许可证. * Use of this source code is governed by the GNU AGPLv3 license that can be found through the following link. @@ -14,7 +14,7 @@ package net.mamoe.mirai.console.util import net.mamoe.mirai.console.util.SemVersion.Companion.test -import org.junit.jupiter.api.Test +import kotlin.test.Test import kotlin.test.assertFails internal class TestSemVersion { diff --git a/mirai-console/tools/compiler-annotations/build.gradle.kts b/mirai-console/tools/compiler-annotations/build.gradle.kts index 538ab056f..059a1ec0f 100644 --- a/mirai-console/tools/compiler-annotations/build.gradle.kts +++ b/mirai-console/tools/compiler-annotations/build.gradle.kts @@ -20,7 +20,7 @@ description = "Mirai Console compiler annotations" kotlin { explicitApi() - configureHMPPJvm() + configureHMPP() } configureMppPublishing() \ No newline at end of file diff --git a/mirai-console/tools/gradle-plugin/src/integTest/kotlin/AbstractTest.kt b/mirai-console/tools/gradle-plugin/src/integTest/kotlin/AbstractTest.kt index 56f7b2c8d..3ebece2db 100644 --- a/mirai-console/tools/gradle-plugin/src/integTest/kotlin/AbstractTest.kt +++ b/mirai-console/tools/gradle-plugin/src/integTest/kotlin/AbstractTest.kt @@ -1,5 +1,5 @@ /* - * Copyright 2019-2021 Mamoe Technologies and contributors. + * Copyright 2019-2022 Mamoe Technologies and contributors. * * 此源代码的使用受 GNU AFFERO GENERAL PUBLIC LICENSE version 3 许可证的约束, 可以在以下链接找到该许可证. * Use of this source code is governed by the GNU AGPLv3 license that can be found through the following link. @@ -58,10 +58,12 @@ abstract class AbstractTest { File(tempDir, "gradle.properties").apply { delete() - writeText(""" + writeText( + """ org.gradle.daemon=false org.gradle.jvmargs=-Xmx4096m -Dfile.encoding=UTF-8 - """.trimIndent()) + """.trimIndent() + ) } buildFile = File(tempDir, "build.gradle") diff --git a/mirai-console/tools/gradle-plugin/src/integTest/kotlin/KotlinTransitiveDependenciesIntegrationTest.kt b/mirai-console/tools/gradle-plugin/src/integTest/kotlin/KotlinTransitiveDependenciesIntegrationTest.kt index eb17c7f1a..a97701e76 100644 --- a/mirai-console/tools/gradle-plugin/src/integTest/kotlin/KotlinTransitiveDependenciesIntegrationTest.kt +++ b/mirai-console/tools/gradle-plugin/src/integTest/kotlin/KotlinTransitiveDependenciesIntegrationTest.kt @@ -1,5 +1,5 @@ /* - * Copyright 2019-2021 Mamoe Technologies and contributors. + * Copyright 2019-2022 Mamoe Technologies and contributors. * * 此源代码的使用受 GNU AFFERO GENERAL PUBLIC LICENSE version 3 许可证的约束, 可以在以下链接找到该许可证. * Use of this source code is governed by the GNU AGPLv3 license that can be found through the following link. @@ -11,11 +11,11 @@ package net.mamoe.mirai.console.gradle import org.gradle.testkit.runner.GradleRunner import org.junit.jupiter.api.Assertions -import org.junit.jupiter.api.Test import org.junit.jupiter.api.io.TempDir import java.io.ByteArrayOutputStream import java.io.File import java.io.PrintWriter +import kotlin.test.Test class KotlinTransitiveDependenciesIntegrationTest { @Test diff --git a/mirai-console/tools/gradle-plugin/src/integTest/kotlin/TestBuildPlugin.kt b/mirai-console/tools/gradle-plugin/src/integTest/kotlin/TestBuildPlugin.kt index 0a8518eab..c2a5d3e56 100644 --- a/mirai-console/tools/gradle-plugin/src/integTest/kotlin/TestBuildPlugin.kt +++ b/mirai-console/tools/gradle-plugin/src/integTest/kotlin/TestBuildPlugin.kt @@ -12,9 +12,9 @@ package net.mamoe.mirai.console.gradle import org.junit.jupiter.api.DisplayName -import org.junit.jupiter.api.Test import java.io.File import java.util.zip.ZipFile +import kotlin.test.Test import kotlin.test.assertFalse import kotlin.test.assertNotNull import kotlin.test.assertNull diff --git a/mirai-console/tools/gradle-plugin/src/integTest/kotlin/TestPluginApply.kt b/mirai-console/tools/gradle-plugin/src/integTest/kotlin/TestPluginApply.kt index 6c9e41cb5..bb0939757 100644 --- a/mirai-console/tools/gradle-plugin/src/integTest/kotlin/TestPluginApply.kt +++ b/mirai-console/tools/gradle-plugin/src/integTest/kotlin/TestPluginApply.kt @@ -1,5 +1,5 @@ /* - * Copyright 2019-2021 Mamoe Technologies and contributors. + * Copyright 2019-2022 Mamoe Technologies and contributors. * * 此源代码的使用受 GNU AFFERO GENERAL PUBLIC LICENSE version 3 许可证的约束, 可以在以下链接找到该许可证. * Use of this source code is governed by the GNU AGPLv3 license that can be found through the following link. @@ -9,7 +9,7 @@ package net.mamoe.mirai.console.gradle -import org.junit.jupiter.api.Test +import kotlin.test.Test class TestPluginApply : AbstractTest() { diff --git a/mirai-console/tools/gradle-plugin/src/main/kotlin/MiraiConsoleGradlePlugin.kt b/mirai-console/tools/gradle-plugin/src/main/kotlin/MiraiConsoleGradlePlugin.kt index 2b180e4df..9a3a75fbe 100644 --- a/mirai-console/tools/gradle-plugin/src/main/kotlin/MiraiConsoleGradlePlugin.kt +++ b/mirai-console/tools/gradle-plugin/src/main/kotlin/MiraiConsoleGradlePlugin.kt @@ -45,8 +45,10 @@ public class MiraiConsoleGradlePlugin : Plugin { try { languageSettings.optIn("kotlin.RequiresOptIn") } catch (e: NoSuchMethodError) { - @Suppress("DEPRECATION") - languageSettings.useExperimentalAnnotation("kotlin.RequiresOptIn") + // User is using < 1.6 + target.compilations.forEach { compilation -> + compilation.kotlinOptions.freeCompilerArgs += "-Xopt-in=kotlin.RequiresOptIn" + } } dependencies { configureDependencies(project, this@configureSourceSet, target) } } diff --git a/mirai-console/tools/intellij-plugin/test/creator/MiraiVersionKindTest.kt b/mirai-console/tools/intellij-plugin/test/creator/MiraiVersionKindTest.kt index d99d71b8c..1c6bcf3f4 100644 --- a/mirai-console/tools/intellij-plugin/test/creator/MiraiVersionKindTest.kt +++ b/mirai-console/tools/intellij-plugin/test/creator/MiraiVersionKindTest.kt @@ -9,7 +9,7 @@ package creator import net.mamoe.mirai.console.intellij.wizard.sortVersionsDescending -import org.junit.jupiter.api.Test +import kotlin.test.Test import kotlin.test.assertEquals class MiraiVersionKindTest { diff --git a/mirai-console/tools/intellij-plugin/test/creator/tasks/TaskUtilsKtTest.kt b/mirai-console/tools/intellij-plugin/test/creator/tasks/TaskUtilsKtTest.kt index 10ac31d03..8d133b945 100644 --- a/mirai-console/tools/intellij-plugin/test/creator/tasks/TaskUtilsKtTest.kt +++ b/mirai-console/tools/intellij-plugin/test/creator/tasks/TaskUtilsKtTest.kt @@ -13,7 +13,7 @@ import net.mamoe.mirai.console.intellij.diagnostics.adjustToClassName import net.mamoe.mirai.console.intellij.diagnostics.isValidPackageName import net.mamoe.mirai.console.intellij.diagnostics.isValidQualifiedClassName import net.mamoe.mirai.console.intellij.diagnostics.isValidSimpleClassName -import org.junit.jupiter.api.Test +import kotlin.test.Test import kotlin.test.assertEquals import kotlin.test.assertTrue diff --git a/mirai-core-api/build.gradle.kts b/mirai-core-api/build.gradle.kts index 601d50c2d..342e886aa 100644 --- a/mirai-core-api/build.gradle.kts +++ b/mirai-core-api/build.gradle.kts @@ -26,7 +26,7 @@ description = "Mirai API module" kotlin { explicitApi() - configureHMPPJvm() + configureHMPP() sourceSets { @@ -36,6 +36,7 @@ kotlin { api(`kotlinx-serialization-core`) api(`kotlinx-serialization-json`) api(`kotlinx-coroutines-core`) // don't remove it, otherwise IDE will complain + implementation(`ktor-client-core`) implementation(project(":mirai-core-utils")) implementation(project(":mirai-console-compiler-annotations")) diff --git a/mirai-core-api/src/commonMain/kotlin/BotFactory.kt b/mirai-core-api/src/commonMain/kotlin/BotFactory.kt index 0c7004c07..01ea1d7e7 100644 --- a/mirai-core-api/src/commonMain/kotlin/BotFactory.kt +++ b/mirai-core-api/src/commonMain/kotlin/BotFactory.kt @@ -1,10 +1,10 @@ /* - * Copyright 2019-2021 Mamoe Technologies and contributors. + * Copyright 2019-2022 Mamoe Technologies and contributors. * - * 此源代码的使用受 GNU AFFERO GENERAL PUBLIC LICENSE version 3 许可证的约束, 可以在以下链接找到该许可证. - * Use of this source code is governed by the GNU AGPLv3 license that can be found through the following link. + * 此源代码的使用受 GNU AFFERO GENERAL PUBLIC LICENSE version 3 许可证的约束, 可以在以下链接找到该许可证. + * Use of this source code is governed by the GNU AGPLv3 license that can be found through the following link. * - * https://github.com/mamoe/mirai/blob/master/LICENSE + * https://github.com/mamoe/mirai/blob/dev/LICENSE */ @file:Suppress("unused", "NOTHING_TO_INLINE") @@ -12,6 +12,7 @@ package net.mamoe.mirai import net.mamoe.mirai.utils.BotConfiguration +import kotlin.jvm.JvmSynthetic /** * 构造 [Bot] 的工厂. 这是 [Bot] 唯一的构造方式. diff --git a/mirai-core-api/src/commonMain/kotlin/IMirai.kt b/mirai-core-api/src/commonMain/kotlin/IMirai.kt index b51e503d3..908cd03e2 100644 --- a/mirai-core-api/src/commonMain/kotlin/IMirai.kt +++ b/mirai-core-api/src/commonMain/kotlin/IMirai.kt @@ -15,7 +15,6 @@ package net.mamoe.mirai import io.ktor.client.* -import io.ktor.client.engine.okhttp.* import me.him188.kotlin.jvm.blocking.bridge.JvmBlockingBridge import net.mamoe.mirai.contact.* import net.mamoe.mirai.data.UserProfile @@ -30,8 +29,9 @@ import net.mamoe.mirai.message.data.* import net.mamoe.mirai.message.data.Image.Key.queryUrl import net.mamoe.mirai.message.data.MessageSource.Key.recall import net.mamoe.mirai.utils.* -import java.util.ServiceLoader -import kotlin.reflect.full.companionObjectInstance +import kotlin.jvm.JvmName +import kotlin.jvm.JvmStatic +import kotlin.jvm.JvmSynthetic /** * [IMirai] 实例. @@ -336,11 +336,10 @@ public suspend inline fun IMirai.recallMessage(bot: Bot, message: MessageChain): @PublishedApi // for tests and potential public uses. @Suppress("ClassName") internal object _MiraiInstance { - private var instance: IMirai? = null @JvmStatic fun set(instance: IMirai) { - this.instance = instance + miraiInstance = instance } /** @@ -348,15 +347,14 @@ internal object _MiraiInstance { */ @JvmStatic fun get(): IMirai { - return instance ?: findMiraiInstance().also { instance = it } + return miraiInstance ?: findMiraiInstance().also { miraiInstance = it } } } +// to overcome native gc issue +private var miraiInstance: IMirai? = null + @JvmSynthetic internal fun findMiraiInstance(): IMirai { - ServiceLoader.load(IMirai::class.java).firstOrNull()?.let { return it } - - val implClass = Class.forName("net.mamoe.mirai.internal.MiraiImpl") - (implClass.kotlin.companionObjectInstance as? IMirai)?.let { return it } - return implClass.asSubclass(IMirai::class.java).getConstructor().newInstance() + return loadService(IMirai::class, "net.mamoe.mirai.internal.MiraiImpl") } \ No newline at end of file diff --git a/mirai-core-api/src/commonMain/kotlin/LowLevelApiAccessor.kt b/mirai-core-api/src/commonMain/kotlin/LowLevelApiAccessor.kt index 36f5983fb..1c5f4be75 100644 --- a/mirai-core-api/src/commonMain/kotlin/LowLevelApiAccessor.kt +++ b/mirai-core-api/src/commonMain/kotlin/LowLevelApiAccessor.kt @@ -1,5 +1,5 @@ /* - * Copyright 2019-2021 Mamoe Technologies and contributors. + * Copyright 2019-2022 Mamoe Technologies and contributors. * * 此源代码的使用受 GNU AFFERO GENERAL PUBLIC LICENSE version 3 许可证的约束, 可以在以下链接找到该许可证. * Use of this source code is governed by the GNU AGPLv3 license that can be found through the following link. @@ -17,7 +17,6 @@ import net.mamoe.mirai.contact.* import net.mamoe.mirai.data.* import net.mamoe.mirai.utils.MiraiExperimentalApi import net.mamoe.mirai.utils.NotStableForInheritance -import net.mamoe.mirai.utils.WeakRef import kotlin.annotation.AnnotationTarget.* /** @@ -52,7 +51,7 @@ public interface LowLevelApiAccessor { public suspend fun refreshKeys(bot: Bot) /** - * 构造一个 [Friend] 对象. 它持有对 [Bot] 的弱引用([WeakRef]). + * 构造一个 [Friend] 对象. * * [Bot] 无法管理这个对象, 但这个对象会以 [Bot] 的 [Job] 作为父 Job. * 因此, 当 [Bot] 被关闭后, 这个对象也会被关闭. @@ -61,7 +60,7 @@ public interface LowLevelApiAccessor { public fun newFriend(bot: Bot, friendInfo: FriendInfo): Friend /** - * 构造一个 [Stranger] 对象. 它持有对 [Bot] 的弱引用([WeakRef]). + * 构造一个 [Stranger] 对象. * * [Bot] 无法管理这个对象, 但这个对象会以 [Bot] 的 [Job] 作为父 Job. * 因此, 当 [Bot] 被关闭后, 这个对象也会被关闭. diff --git a/mirai-core-api/src/commonMain/kotlin/contact/Contact.kt b/mirai-core-api/src/commonMain/kotlin/contact/Contact.kt index a28a863b7..fe1704a18 100644 --- a/mirai-core-api/src/commonMain/kotlin/contact/Contact.kt +++ b/mirai-core-api/src/commonMain/kotlin/contact/Contact.kt @@ -1,5 +1,5 @@ /* - * Copyright 2019-2021 Mamoe Technologies and contributors. + * Copyright 2019-2022 Mamoe Technologies and contributors. * * 此源代码的使用受 GNU AFFERO GENERAL PUBLIC LICENSE version 3 许可证的约束, 可以在以下链接找到该许可证. * Use of this source code is governed by the GNU AGPLv3 license that can be found through the following link. @@ -23,16 +23,16 @@ import net.mamoe.mirai.message.MessageReceipt import net.mamoe.mirai.message.data.* import net.mamoe.mirai.recallMessage import net.mamoe.mirai.utils.* -import net.mamoe.mirai.utils.ExternalResource.Companion.sendAsImageTo import net.mamoe.mirai.utils.ExternalResource.Companion.uploadAsImage -import java.io.File -import java.io.InputStream +import kotlin.coroutines.cancellation.CancellationException +import kotlin.jvm.JvmStatic +import kotlin.jvm.JvmSynthetic /** * 联系对象, 即可以与 [Bot] 互动的对象. 包含 [用户][User], 和 [群][Group]. */ @NotStableForInheritance -public interface Contact : ContactOrBot, CoroutineScope { +public expect interface Contact : ContactOrBot, CoroutineScope { /** * 这个联系对象所属 [Bot]. */ @@ -67,7 +67,7 @@ public interface Contact : ContactOrBot, CoroutineScope { * 发送纯文本消息 * @see sendMessage */ - public suspend fun sendMessage(message: String): MessageReceipt = this.sendMessage(message.toPlainText()) + public open suspend fun sendMessage(message: String): MessageReceipt /** * 上传一个 [资源][ExternalResource] 作为图片以备发送. @@ -90,81 +90,23 @@ public interface Contact : ContactOrBot, CoroutineScope { @JvmBlockingBridge public companion object { - /** - * 读取 [InputStream] 到临时文件并将其作为图片发送到指定联系人 - * - * 注意:此函数不会关闭 [imageStream] - * - * @param formatName 查看 [ExternalResource.formatName] - * @throws OverFileSizeMaxException - * @see FileCacheStrategy - */ - @JvmStatic - @JvmOverloads - public suspend fun C.sendImage( - imageStream: InputStream, - formatName: String? = null - ): MessageReceipt = imageStream.sendAsImageTo(this, formatName) - - /** - * 将文件作为图片发送到指定联系人 - * @param formatName 查看 [ExternalResource.formatName] - * @throws OverFileSizeMaxException - * @see FileCacheStrategy - */ - @JvmStatic - @JvmOverloads - public suspend fun C.sendImage( - file: File, - formatName: String? = null - ): MessageReceipt = file.sendAsImageTo(this, formatName) - /** * 将资源作为单独的图片消息发送给 [this] * * @see Contact.sendMessage 最终调用, 发送消息. */ @JvmStatic - public suspend fun C.sendImage(resource: ExternalResource): MessageReceipt = - resource.sendAsImageTo(this) - - - /** - * 读取 [InputStream] 到临时文件并将其作为图片上传, 但不发送 - * - * 注意:本函数不会关闭流 - * - * @param formatName 查看 [ExternalResource.formatName] - * @throws OverFileSizeMaxException - */ - @JvmStatic - @JvmOverloads - public suspend fun Contact.uploadImage( - imageStream: InputStream, - formatName: String? = null - ): Image = imageStream.uploadAsImage(this@uploadImage, formatName) - - /** - * 将文件作为图片上传, 但不发送 - * @param formatName 查看 [ExternalResource.formatName] - * @throws OverFileSizeMaxException - */ - @JvmStatic - @JvmOverloads - public suspend fun Contact.uploadImage( - file: File, - formatName: String? = null - ): Image = file.uploadAsImage(this, formatName) + public suspend fun C.sendImage(resource: ExternalResource): MessageReceipt /** * 将文件作为图片上传, 但不发送 * @throws OverFileSizeMaxException */ - @Throws(OverFileSizeMaxException::class) + @Throws(OverFileSizeMaxException::class, CancellationException::class) @JvmStatic @Suppress("INVISIBLE_MEMBER", "INVISIBLE_REFERENCE", "EXTENSION_SHADOWED_BY_MEMBER") @kotlin.internal.LowPriorityInOverloadResolution // for better Java API - public suspend fun Contact.uploadImage(resource: ExternalResource): Image = this.uploadImage(resource) + public suspend fun Contact.uploadImage(resource: ExternalResource): Image } } diff --git a/mirai-core-api/src/commonMain/kotlin/contact/ContactList.kt b/mirai-core-api/src/commonMain/kotlin/contact/ContactList.kt index 9d93684fa..7fe3915fd 100644 --- a/mirai-core-api/src/commonMain/kotlin/contact/ContactList.kt +++ b/mirai-core-api/src/commonMain/kotlin/contact/ContactList.kt @@ -1,18 +1,19 @@ /* - * Copyright 2019-2021 Mamoe Technologies and contributors. + * Copyright 2019-2022 Mamoe Technologies and contributors. * - * 此源代码的使用受 GNU AFFERO GENERAL PUBLIC LICENSE version 3 许可证的约束, 可以在以下链接找到该许可证. - * Use of this source code is governed by the GNU AGPLv3 license that can be found through the following link. + * 此源代码的使用受 GNU AFFERO GENERAL PUBLIC LICENSE version 3 许可证的约束, 可以在以下链接找到该许可证. + * Use of this source code is governed by the GNU AGPLv3 license that can be found through the following link. * - * https://github.com/mamoe/mirai/blob/master/LICENSE + * https://github.com/mamoe/mirai/blob/dev/LICENSE */ @file:Suppress("EXPERIMENTAL_API_USAGE", "unused") package net.mamoe.mirai.contact +import net.mamoe.mirai.utils.ConcurrentLinkedDeque import net.mamoe.mirai.utils.MiraiInternalApi -import java.util.concurrent.ConcurrentLinkedQueue +import kotlin.jvm.JvmField /** @@ -26,7 +27,7 @@ public class ContactList Collection by delegate { @MiraiInternalApi - public constructor() : this(ConcurrentLinkedQueue()) + public constructor() : this(ConcurrentLinkedDeque()) /** * 获取一个 [Contact.id] 为 [id] 的元素. 在不存在时返回 `null`. diff --git a/mirai-core-api/src/commonMain/kotlin/contact/ContactOrBot.kt b/mirai-core-api/src/commonMain/kotlin/contact/ContactOrBot.kt index 2fbb05a85..01ba36b13 100644 --- a/mirai-core-api/src/commonMain/kotlin/contact/ContactOrBot.kt +++ b/mirai-core-api/src/commonMain/kotlin/contact/ContactOrBot.kt @@ -12,6 +12,7 @@ package net.mamoe.mirai.contact import kotlinx.coroutines.CoroutineScope import net.mamoe.mirai.Bot import net.mamoe.mirai.utils.NotStableForInheritance +import kotlin.jvm.JvmName /** * 拥有 [id] 的对象. diff --git a/mirai-core-api/src/commonMain/kotlin/contact/Group.kt b/mirai-core-api/src/commonMain/kotlin/contact/Group.kt index 58ef8f5c3..8d3bf096a 100644 --- a/mirai-core-api/src/commonMain/kotlin/contact/Group.kt +++ b/mirai-core-api/src/commonMain/kotlin/contact/Group.kt @@ -23,6 +23,8 @@ import net.mamoe.mirai.utils.DeprecatedSinceMirai import net.mamoe.mirai.utils.ExternalResource import net.mamoe.mirai.utils.MiraiExperimentalApi import net.mamoe.mirai.utils.NotStableForInheritance +import kotlin.jvm.JvmStatic +import kotlin.jvm.JvmSynthetic /** * 群. diff --git a/mirai-core-api/src/commonMain/kotlin/contact/NormalMember.kt b/mirai-core-api/src/commonMain/kotlin/contact/NormalMember.kt index 92cb26972..63e09d3dc 100644 --- a/mirai-core-api/src/commonMain/kotlin/contact/NormalMember.kt +++ b/mirai-core-api/src/commonMain/kotlin/contact/NormalMember.kt @@ -1,5 +1,5 @@ /* - * Copyright 2019-2021 Mamoe Technologies and contributors. + * Copyright 2019-2022 Mamoe Technologies and contributors. * * 此源代码的使用受 GNU AFFERO GENERAL PUBLIC LICENSE version 3 许可证的约束, 可以在以下链接找到该许可证. * Use of this source code is governed by the GNU AGPLv3 license that can be found through the following link. @@ -20,8 +20,8 @@ import net.mamoe.mirai.message.action.Nudge import net.mamoe.mirai.message.data.Message import net.mamoe.mirai.message.data.isContentEmpty import net.mamoe.mirai.message.data.toPlainText -import net.mamoe.mirai.utils.DeprecatedSinceMirai import net.mamoe.mirai.utils.NotStableForInheritance +import kotlin.jvm.JvmName import kotlin.time.Duration import kotlin.time.DurationUnit import kotlin.time.ExperimentalTime diff --git a/mirai-core-api/src/commonMain/kotlin/contact/announcement/Announcement.kt b/mirai-core-api/src/commonMain/kotlin/contact/announcement/Announcement.kt index e8cab2df5..23a95c7e8 100644 --- a/mirai-core-api/src/commonMain/kotlin/contact/announcement/Announcement.kt +++ b/mirai-core-api/src/commonMain/kotlin/contact/announcement/Announcement.kt @@ -1,5 +1,5 @@ /* - * Copyright 2019-2021 Mamoe Technologies and contributors. + * Copyright 2019-2022 Mamoe Technologies and contributors. * * 此源代码的使用受 GNU AFFERO GENERAL PUBLIC LICENSE version 3 许可证的约束, 可以在以下链接找到该许可证. * Use of this source code is governed by the GNU AGPLv3 license that can be found through the following link. @@ -16,6 +16,9 @@ import me.him188.kotlin.jvm.blocking.bridge.JvmBlockingBridge import net.mamoe.mirai.contact.Group import net.mamoe.mirai.contact.PermissionDeniedException import net.mamoe.mirai.contact.announcement.Announcement.Companion.publishAnnouncement +import kotlin.jvm.JvmOverloads +import kotlin.jvm.JvmStatic +import kotlin.jvm.JvmSynthetic /** diff --git a/mirai-core-api/src/commonMain/kotlin/contact/announcement/AnnouncementImage.kt b/mirai-core-api/src/commonMain/kotlin/contact/announcement/AnnouncementImage.kt index e0154c7aa..85dcf7289 100644 --- a/mirai-core-api/src/commonMain/kotlin/contact/announcement/AnnouncementImage.kt +++ b/mirai-core-api/src/commonMain/kotlin/contact/announcement/AnnouncementImage.kt @@ -1,5 +1,5 @@ /* - * Copyright 2019-2021 Mamoe Technologies and contributors. + * Copyright 2019-2022 Mamoe Technologies and contributors. * * 此源代码的使用受 GNU AFFERO GENERAL PUBLIC LICENSE version 3 许可证的约束, 可以在以下链接找到该许可证. * Use of this source code is governed by the GNU AGPLv3 license that can be found through the following link. @@ -11,6 +11,8 @@ package net.mamoe.mirai.contact.announcement import kotlinx.serialization.SerialName import kotlinx.serialization.Serializable +import net.mamoe.mirai.utils.isSameClass +import kotlin.jvm.JvmStatic /** @@ -47,9 +49,7 @@ public class AnnouncementImage private constructor( override fun equals(other: Any?): Boolean { if (this === other) return true - if (javaClass != other?.javaClass) return false - - other as AnnouncementImage + if (other !is AnnouncementImage || !isSameClass(this, other)) return false if (id != other.id) return false if (height != other.height) return false diff --git a/mirai-core-api/src/commonMain/kotlin/contact/announcement/AnnouncementParameters.kt b/mirai-core-api/src/commonMain/kotlin/contact/announcement/AnnouncementParameters.kt index c5bf5be43..838faae50 100644 --- a/mirai-core-api/src/commonMain/kotlin/contact/announcement/AnnouncementParameters.kt +++ b/mirai-core-api/src/commonMain/kotlin/contact/announcement/AnnouncementParameters.kt @@ -1,5 +1,5 @@ /* - * Copyright 2019-2021 Mamoe Technologies and contributors. + * Copyright 2019-2022 Mamoe Technologies and contributors. * * 此源代码的使用受 GNU AFFERO GENERAL PUBLIC LICENSE version 3 许可证的约束, 可以在以下链接找到该许可证. * Use of this source code is governed by the GNU AGPLv3 license that can be found through the following link. @@ -12,6 +12,9 @@ package net.mamoe.mirai.contact.announcement import kotlinx.serialization.SerialName import kotlinx.serialization.Serializable import net.mamoe.mirai.contact.announcement.AnnouncementParameters.Companion.DEFAULT +import net.mamoe.mirai.utils.isSameClass +import kotlin.jvm.JvmName +import kotlin.jvm.JvmStatic /** * 群公告的附加参数. @@ -65,9 +68,7 @@ public class AnnouncementParameters internal constructor( override fun equals(other: Any?): Boolean { if (this === other) return true - if (javaClass != other?.javaClass) return false - - other as AnnouncementParameters + if (other !is AnnouncementParameters || !isSameClass(this, other)) return false if (image != other.image) return false if (sendToNewMember != other.sendToNewMember) return false diff --git a/mirai-core-api/src/commonMain/kotlin/contact/announcement/AnnouncementParametersBuilder.kt b/mirai-core-api/src/commonMain/kotlin/contact/announcement/AnnouncementParametersBuilder.kt index be1cd6c00..bdf879dc2 100644 --- a/mirai-core-api/src/commonMain/kotlin/contact/announcement/AnnouncementParametersBuilder.kt +++ b/mirai-core-api/src/commonMain/kotlin/contact/announcement/AnnouncementParametersBuilder.kt @@ -1,5 +1,5 @@ /* - * Copyright 2019-2021 Mamoe Technologies and contributors. + * Copyright 2019-2022 Mamoe Technologies and contributors. * * 此源代码的使用受 GNU AFFERO GENERAL PUBLIC LICENSE version 3 许可证的约束, 可以在以下链接找到该许可证. * Use of this source code is governed by the GNU AGPLv3 license that can be found through the following link. @@ -13,6 +13,9 @@ package net.mamoe.mirai.contact.announcement import kotlin.contracts.InvocationKind import kotlin.contracts.contract +import kotlin.jvm.JvmName +import kotlin.jvm.JvmOverloads +import kotlin.jvm.JvmSynthetic /** diff --git a/mirai-core-api/src/commonMain/kotlin/contact/announcement/OfflineAnnouncement.kt b/mirai-core-api/src/commonMain/kotlin/contact/announcement/OfflineAnnouncement.kt index 5fc6f9f04..2fd87a494 100644 --- a/mirai-core-api/src/commonMain/kotlin/contact/announcement/OfflineAnnouncement.kt +++ b/mirai-core-api/src/commonMain/kotlin/contact/announcement/OfflineAnnouncement.kt @@ -21,6 +21,9 @@ import net.mamoe.mirai.utils.map import net.mamoe.mirai.utils.safeCast import kotlin.contracts.InvocationKind import kotlin.contracts.contract +import kotlin.jvm.JvmOverloads +import kotlin.jvm.JvmStatic +import kotlin.jvm.JvmSynthetic /** * 表示在本地构建的 [Announcement]. diff --git a/mirai-core-api/src/commonMain/kotlin/contact/announcement/OnlineAnnouncement.kt b/mirai-core-api/src/commonMain/kotlin/contact/announcement/OnlineAnnouncement.kt index 0df92e350..66daa849d 100644 --- a/mirai-core-api/src/commonMain/kotlin/contact/announcement/OnlineAnnouncement.kt +++ b/mirai-core-api/src/commonMain/kotlin/contact/announcement/OnlineAnnouncement.kt @@ -1,5 +1,5 @@ /* - * Copyright 2019-2021 Mamoe Technologies and contributors. + * Copyright 2019-2022 Mamoe Technologies and contributors. * * 此源代码的使用受 GNU AFFERO GENERAL PUBLIC LICENSE version 3 许可证的约束, 可以在以下链接找到该许可证. * Use of this source code is governed by the GNU AGPLv3 license that can be found through the following link. @@ -18,7 +18,6 @@ import net.mamoe.mirai.contact.Group import net.mamoe.mirai.contact.NormalMember import net.mamoe.mirai.contact.PermissionDeniedException import net.mamoe.mirai.utils.NotStableForInheritance -import java.time.Instant /** @@ -65,7 +64,7 @@ public interface OnlineAnnouncement : Announcement { /** * 公告发出的时间,为 EpochSecond (自 1970-01-01T00:00:00Z 的秒数) * - * @see Instant.ofEpochSecond + * @see java.time.Instant.ofEpochSecond */ public val publicationTime: Long diff --git a/mirai-core-api/src/commonMain/kotlin/contact/file/AbsoluteFileFolder.kt b/mirai-core-api/src/commonMain/kotlin/contact/file/AbsoluteFileFolder.kt index 5d22c130d..70ecbb713 100644 --- a/mirai-core-api/src/commonMain/kotlin/contact/file/AbsoluteFileFolder.kt +++ b/mirai-core-api/src/commonMain/kotlin/contact/file/AbsoluteFileFolder.kt @@ -17,7 +17,7 @@ import me.him188.kotlin.jvm.blocking.bridge.JvmBlockingBridge import net.mamoe.mirai.contact.FileSupported import net.mamoe.mirai.contact.PermissionDeniedException import net.mamoe.mirai.utils.NotStableForInheritance -import java.io.File +import kotlin.jvm.JvmStatic /** * 绝对文件或目录标识. 精确表示一个远程文件. 不会受同名文件或目录的影响. @@ -174,7 +174,7 @@ public sealed interface AbsoluteFileFolder { * * 不会包含 `:*?"<>|/\` 任一字符. * - * @see File.extension + * @see java.io.File.extension */ @get:JvmStatic public val AbsoluteFileFolder.extension: String diff --git a/mirai-core-api/src/commonMain/kotlin/contact/file/AbsoluteFolder.kt b/mirai-core-api/src/commonMain/kotlin/contact/file/AbsoluteFolder.kt index e05ab314e..e3868ffb8 100644 --- a/mirai-core-api/src/commonMain/kotlin/contact/file/AbsoluteFolder.kt +++ b/mirai-core-api/src/commonMain/kotlin/contact/file/AbsoluteFolder.kt @@ -1,5 +1,5 @@ /* - * Copyright 2019-2021 Mamoe Technologies and contributors. + * Copyright 2019-2022 Mamoe Technologies and contributors. * * 此源代码的使用受 GNU AFFERO GENERAL PUBLIC LICENSE version 3 许可证的约束, 可以在以下链接找到该许可证. * Use of this source code is governed by the GNU AGPLv3 license that can be found through the following link. @@ -16,10 +16,9 @@ import kotlinx.coroutines.flow.Flow import me.him188.kotlin.jvm.blocking.bridge.JvmBlockingBridge import net.mamoe.mirai.contact.PermissionDeniedException import net.mamoe.mirai.utils.ExternalResource -import net.mamoe.mirai.utils.JavaFriendlyAPI import net.mamoe.mirai.utils.NotStableForInheritance import net.mamoe.mirai.utils.ProgressionCallback -import java.util.stream.Stream +import kotlin.jvm.JvmOverloads /** * 绝对目录标识. 精确表示一个远程目录. 不会受同名文件或目录的影响. @@ -30,7 +29,7 @@ import java.util.stream.Stream * @see AbsoluteFileFolder */ @NotStableForInheritance -public interface AbsoluteFolder : AbsoluteFileFolder { +public expect interface AbsoluteFolder : AbsoluteFileFolder { /** * 当前快照中文件数量, 当有文件更新时(上传/删除文件) 该属性不会更新. * @@ -43,7 +42,7 @@ public interface AbsoluteFolder : AbsoluteFileFolder { /** * 当该目录为空时返回 `true`. */ - public fun isEmpty(): Boolean = contentsCount == 0 + public open fun isEmpty(): Boolean /** * 返回更新了文件或目录信息 ([lastModifiedTime] 等) 的, 指向相同文件的 [AbsoluteFileFolder]. @@ -64,42 +63,18 @@ public interface AbsoluteFolder : AbsoluteFileFolder { */ public suspend fun folders(): Flow - /** - * 获取该目录下所有子目录列表. - * - * 实现细节: 为了适合 Java 调用, 实现类似为阻塞式的 [folders], 因此不建议在 Kotlin 使用. 在 Kotlin 请使用 [folders]. - */ - @JavaFriendlyAPI - public suspend fun foldersStream(): Stream - /** * 获取该目录下所有文件列表. */ public suspend fun files(): Flow - /** - * 获取该目录下所有文件列表. - * - * 实现细节: 为了适合 Java 调用, 实现类似为阻塞式的 [files], 因此不建议在 Kotlin 使用. 在 Kotlin 请使用 [files]. - */ - @JavaFriendlyAPI - public suspend fun filesStream(): Stream - /** * 获取该目录下所有文件和子目录列表. */ public suspend fun children(): Flow - /** - * 获取该目录下所有文件和子目录列表. - * - * 实现细节: 为了适合 Java 调用, 实现类似为阻塞式的 [children], 因此不建议在 Kotlin 使用. 在 Kotlin 请使用 [children]. - */ - @JavaFriendlyAPI - public suspend fun childrenStream(): Stream - /////////////////////////////////////////////////////////////////////////// // resolve and upload /////////////////////////////////////////////////////////////////////////// @@ -144,16 +119,6 @@ public interface AbsoluteFolder : AbsoluteFileFolder { path: String ): Flow - /** - * 根据路径获取指向的所有路径为 [path] 的文件列表. 同时支持相对路径和绝对路径. 支持获取子目录内的文件. - * - * 实现细节: 为了适合 Java 调用, 实现类似为阻塞式的 [resolveFiles], 因此不建议在 Kotlin 使用. 在 Kotlin 请使用 [resolveFiles]. - */ - @JavaFriendlyAPI - public suspend fun resolveFilesStream( - path: String - ): Stream - /** * 根据路径获取指向的所有路径为 [path] 的文件和目录列表. 同时支持相对路径和绝对路径. 支持获取子目录内的文件和目录. */ @@ -161,16 +126,6 @@ public interface AbsoluteFolder : AbsoluteFileFolder { path: String ): Flow - /** - * 根据路径获取指向的所有路径为 [path] 的文件和目录列表. 同时支持相对路径和绝对路径. 支持获取子目录内的文件和目录. - * - * 实现细节: 为了适合 Java 调用, 实现类似为阻塞式的 [resolveAll], 因此不建议在 Kotlin 使用. 在 Kotlin 请使用 [resolveAll]. - */ - @JavaFriendlyAPI - public suspend fun resolveAllStream( - path: String - ): Stream - /** * 上传一个文件到该目录, 返回上传成功的文件标识. * @@ -200,6 +155,7 @@ public interface AbsoluteFolder : AbsoluteFileFolder { * 根目录 folder ID. * @see id */ - public const val ROOT_FOLDER_ID: String = "/" + @Suppress("CONST_VAL_WITHOUT_INITIALIZER") // compiler bug + public const val ROOT_FOLDER_ID: String } } \ No newline at end of file diff --git a/mirai-core-api/src/commonMain/kotlin/contact/file/RemoteFiles.kt b/mirai-core-api/src/commonMain/kotlin/contact/file/RemoteFiles.kt index f1215b27c..3b526e676 100644 --- a/mirai-core-api/src/commonMain/kotlin/contact/file/RemoteFiles.kt +++ b/mirai-core-api/src/commonMain/kotlin/contact/file/RemoteFiles.kt @@ -1,5 +1,5 @@ /* - * Copyright 2019-2021 Mamoe Technologies and contributors. + * Copyright 2019-2022 Mamoe Technologies and contributors. * * 此源代码的使用受 GNU AFFERO GENERAL PUBLIC LICENSE version 3 许可证的约束, 可以在以下链接找到该许可证. * Use of this source code is governed by the GNU AGPLv3 license that can be found through the following link. @@ -19,8 +19,7 @@ import net.mamoe.mirai.contact.PermissionDeniedException import net.mamoe.mirai.utils.ExternalResource import net.mamoe.mirai.utils.NotStableForInheritance import net.mamoe.mirai.utils.ProgressionCallback -import java.io.File -import java.util.stream.Stream +import kotlin.jvm.JvmOverloads /** * 表示远程文件列表 (管理器). @@ -46,7 +45,7 @@ import java.util.stream.Stream * * # 绝对路径与相对路径 * - * mirai 文件系统的绝对路径与相对路径与 Java [File] 实现的相同. + * mirai 文件系统的绝对路径与相对路径与 Java [java.io.File] 实现的相同. * * 以 `/` 起始的路径表示绝对路径, 基于根目录 [root] 处理. 其他路径均表示相对路径. * @@ -64,11 +63,11 @@ import java.util.stream.Stream * * 一个目录 ([AbsoluteFolder]) 可以包含多个子文件, 根目录还可以包含多个子目录 (详见下文 '目录结构限制'). * - * 使用 [AbsoluteFolder.children] 可以获得其内子目录和文件列表 [Flow]. [AbsoluteFolder.childrenStream] 提供适合 Java 的 [Stream] 实现. + * 使用 [AbsoluteFolder.children] 可以获得其内子目录和文件列表 [Flow]. [AbsoluteFolder.childrenStream] 提供适合 Java 的 [java.util.stream.Stream] 实现. * 使用 [AbsoluteFolder.folders] 或 [AbsoluteFolder.files] 可以特定地只获取子目录或文件列表. 这些函数也有其 `*Stream` 实现. * * 若要根据确定的文件或目录名称获取其 [AbsoluteFileFolder] 实例, 可使用 [AbsoluteFolder.resolveFiles] 或 [AbsoluteFolder.resolveFiles]. - * 注意 [AbsoluteFolder.resolveFiles] 返回 [Flow] (其 Stream 版返回 [Stream]), 因为服务器允许多个文件有相同名称. (详见下文 '允许重名'). + * 注意 [AbsoluteFolder.resolveFiles] 返回 [Flow] (其 Stream 版返回 [java.util.stream.Stream]), 因为服务器允许多个文件有相同名称. (详见下文 '允许重名'). * * 若已知文件 [AbsoluteFile.id], 可通过 [AbsoluteFolder.resolveFileById] 获得该文件. * diff --git a/mirai-core-api/src/commonMain/kotlin/contact/roaming/RoamingMessageFilter.kt b/mirai-core-api/src/commonMain/kotlin/contact/roaming/RoamingMessageFilter.kt index 3b9a6d344..7438c104a 100644 --- a/mirai-core-api/src/commonMain/kotlin/contact/roaming/RoamingMessageFilter.kt +++ b/mirai-core-api/src/commonMain/kotlin/contact/roaming/RoamingMessageFilter.kt @@ -1,5 +1,5 @@ /* - * Copyright 2019-2021 Mamoe Technologies and contributors. + * Copyright 2019-2022 Mamoe Technologies and contributors. * * 此源代码的使用受 GNU AFFERO GENERAL PUBLIC LICENSE version 3 许可证的约束, 可以在以下链接找到该许可证. * Use of this source code is governed by the GNU AGPLv3 license that can be found through the following link. @@ -12,6 +12,7 @@ package net.mamoe.mirai.contact.roaming import net.mamoe.mirai.Bot import net.mamoe.mirai.contact.Contact import net.mamoe.mirai.message.data.MessageSource +import kotlin.jvm.JvmField /** * @since 2.8 diff --git a/mirai-core-api/src/commonMain/kotlin/contact/roaming/RoamingMessages.kt b/mirai-core-api/src/commonMain/kotlin/contact/roaming/RoamingMessages.kt index 4a2e14097..dd5d235be 100644 --- a/mirai-core-api/src/commonMain/kotlin/contact/roaming/RoamingMessages.kt +++ b/mirai-core-api/src/commonMain/kotlin/contact/roaming/RoamingMessages.kt @@ -16,8 +16,6 @@ import me.him188.kotlin.jvm.blocking.bridge.JvmBlockingBridge import net.mamoe.mirai.contact.Friend import net.mamoe.mirai.message.data.MessageChain import net.mamoe.mirai.message.data.MessageSource -import net.mamoe.mirai.utils.JavaFriendlyAPI -import java.util.stream.Stream /** * 漫游消息记录管理器. 可通过 [RoamingSupported.roamingMessages] 获得. 目前仅 [Friend] 实现 [RoamingSupported]. @@ -25,13 +23,13 @@ import java.util.stream.Stream * @since 2.8 * @see RoamingSupported */ -public interface RoamingMessages { +public expect interface RoamingMessages { /////////////////////////////////////////////////////////////////////////// // Get list /////////////////////////////////////////////////////////////////////////// /** - * 查询指定时间段内的漫游消息记录. Java Stream 方法查看 [getMessagesStream]. + * 查询指定时间段内的漫游消息记录. * * 返回查询到的漫游消息记录, 顺序为由新到旧. 这些 [MessageChain] 与从事件中收到的消息链相似, 属于在线消息. * 可从 [MessageChain] 获取 [MessageSource] 来确定发送人等相关信息, 也可以进行引用回复或撤回. @@ -55,7 +53,7 @@ public interface RoamingMessages { ): Flow /** - * 查询所有漫游消息记录. Java Stream 方法查看 [getAllMessagesStream]. + * 查询所有漫游消息记录. * * 返回查询到的漫游消息记录, 顺序为由新到旧. 这些 [MessageChain] 与从事件中收到的消息链相似, 属于在线消息. * 可从 [MessageChain] 获取 [MessageSource] 来确定发送人等相关信息, 也可以进行引用回复或撤回. @@ -70,57 +68,7 @@ public interface RoamingMessages { * * @param filter 过滤器. */ - public suspend fun getAllMessages( + public open suspend fun getAllMessages( filter: RoamingMessageFilter? = null - ): Flow = getMessagesIn(0, Long.MAX_VALUE, filter) - - /** - * 查询指定时间段内的漫游消息记录. Kotlin Flow 版本查看 [getMessagesIn]. - * - * 返回查询到的漫游消息记录, 顺序为由新到旧. 这些 [MessageChain] 与从事件中收到的消息链相似, 属于在线消息. - * 可从 [MessageChain] 获取 [MessageSource] 来确定发送人等相关信息, 也可以进行引用回复或撤回. - * - * 注意, 返回的消息记录既包含机器人发送给目标用户的消息, 也包含目标用户发送给机器人的消息. - * 可通过 [MessageChain] 获取 [MessageSource] (用法为 `messageChain.get(MessageSource.Key)`), 判断 [MessageSource.fromId] (发送人). - * 消息的其他*元数据*信息也要通过 [MessageSource] 获取 (如 [MessageSource.time] 获取时间). - * - * 若只需要获取单向消息 (机器人发送给目标用户的消息或反之), 可使用 [RoamingMessageFilter.SENT] 或 [RoamingMessageFilter.RECEIVED] 作为 [filter] 参数传递. - * - * 性能提示: 请在 [filter] 执行筛选, 若 [filter] 返回 `false` 则不会解析消息链, 这对本函数的处理速度有决定性影响. - * - * @param timeStart 起始时间, UTC+8 时间戳, 单位为秒. 可以为 `0`, 即表示从可以获取的最早的消息起. 负数将会被看是 `0`. - * @param timeEnd 结束时间, UTC+8 时间戳, 单位为秒. 可以为 [Long.MAX_VALUE], 即表示到可以获取的最晚的消息为止. 低于 [timeStart] 的值将会被看作是 [timeStart] 的值. - * @param filter 过滤器. - */ - @Suppress("OVERLOADS_INTERFACE") - @JvmOverloads - @JavaFriendlyAPI - public suspend fun getMessagesStream( - timeStart: Long, - timeEnd: Long, - filter: RoamingMessageFilter? = null - ): Stream - - /** - * 查询所有漫游消息记录. Kotlin Flow 版本查看 [getAllMessages]. - * - * 返回查询到的漫游消息记录, 顺序为由新到旧. 这些 [MessageChain] 与从事件中收到的消息链相似, 属于在线消息. - * 可从 [MessageChain] 获取 [MessageSource] 来确定发送人等相关信息, 也可以进行引用回复或撤回. - * - * 注意, 返回的消息记录既包含机器人发送给目标用户的消息, 也包含目标用户发送给机器人的消息. - * 可通过 [MessageChain] 获取 [MessageSource] (用法为 `messageChain.get(MessageSource.Key)`), 判断 [MessageSource.fromId] (发送人). - * 消息的其他*元数据*信息也要通过 [MessageSource] 获取 (如 [MessageSource.time] 获取时间). - * - * 若只需要获取单向消息 (机器人发送给目标用户的消息或反之), 可使用 [RoamingMessageFilter.SENT] 或 [RoamingMessageFilter.RECEIVED] 作为 [filter] 参数传递. - * - * 性能提示: 请在 [filter] 执行筛选, 若 [filter] 返回 `false` 则不会解析消息链, 这对本函数的处理速度有决定性影响. - * - * @param filter 过滤器. - */ - @Suppress("OVERLOADS_INTERFACE") - @JvmOverloads - @JavaFriendlyAPI - public suspend fun getAllMessagesStream( - filter: RoamingMessageFilter? = null - ): Stream = getMessagesStream(0, Long.MAX_VALUE, filter) + ): Flow } \ No newline at end of file diff --git a/mirai-core-api/src/commonMain/kotlin/data/GroupHonorListData.kt b/mirai-core-api/src/commonMain/kotlin/data/GroupHonorListData.kt index 098c61525..8f222a680 100644 --- a/mirai-core-api/src/commonMain/kotlin/data/GroupHonorListData.kt +++ b/mirai-core-api/src/commonMain/kotlin/data/GroupHonorListData.kt @@ -1,5 +1,5 @@ /* - * Copyright 2019-2021 Mamoe Technologies and contributors. + * Copyright 2019-2022 Mamoe Technologies and contributors. * * 此源代码的使用受 GNU AFFERO GENERAL PUBLIC LICENSE version 3 许可证的约束, 可以在以下链接找到该许可证. * Use of this source code is governed by the GNU AGPLv3 license that can be found through the following link. @@ -19,6 +19,7 @@ import kotlinx.serialization.encoding.Decoder import kotlinx.serialization.encoding.Encoder import net.mamoe.mirai.utils.DeprecatedSinceMirai import net.mamoe.mirai.utils.MiraiExperimentalApi +import kotlin.jvm.JvmStatic /** * 群荣誉信息 diff --git a/mirai-core-api/src/commonMain/kotlin/data/RequestEventData.kt b/mirai-core-api/src/commonMain/kotlin/data/RequestEventData.kt index 03136a132..2763f7eec 100644 --- a/mirai-core-api/src/commonMain/kotlin/data/RequestEventData.kt +++ b/mirai-core-api/src/commonMain/kotlin/data/RequestEventData.kt @@ -1,5 +1,5 @@ /* - * Copyright 2019-2021 Mamoe Technologies and contributors. + * Copyright 2019-2022 Mamoe Technologies and contributors. * * 此源代码的使用受 GNU AFFERO GENERAL PUBLIC LICENSE version 3 许可证的约束, 可以在以下链接找到该许可证. * Use of this source code is governed by the GNU AGPLv3 license that can be found through the following link. @@ -20,6 +20,9 @@ import net.mamoe.mirai.event.events.BotInvitedJoinGroupRequestEvent import net.mamoe.mirai.event.events.MemberJoinRequestEvent import net.mamoe.mirai.event.events.NewFriendRequestEvent import net.mamoe.mirai.utils.MiraiExperimentalApi +import kotlin.jvm.JvmName +import kotlin.jvm.JvmOverloads +import kotlin.jvm.JvmStatic @Serializable @SerialName("RequestEventData") diff --git a/mirai-core-api/src/commonMain/kotlin/event/Event.kt b/mirai-core-api/src/commonMain/kotlin/event/Event.kt index fff8c176c..ef2b46031 100644 --- a/mirai-core-api/src/commonMain/kotlin/event/Event.kt +++ b/mirai-core-api/src/commonMain/kotlin/event/Event.kt @@ -18,6 +18,8 @@ import net.mamoe.mirai.Mirai import net.mamoe.mirai.utils.DeprecatedSinceMirai import net.mamoe.mirai.utils.MiraiExperimentalApi import net.mamoe.mirai.utils.MiraiInternalApi +import kotlin.jvm.JvmField +import kotlin.jvm.Volatile /** * 表示一个事件. @@ -152,6 +154,7 @@ public interface CancellableEvent : Event { * [EventChannel.filter] 和 [Listener.onEvent] 时产生的异常只会由监听方处理. */ @JvmBlockingBridge +@Suppress("TOP_LEVEL_FUNCTIONS_NOT_SUPPORTED") // compiler bug public suspend fun E.broadcast(): E { Mirai.broadcastEvent(this) return this diff --git a/mirai-core-api/src/commonMain/kotlin/event/EventChannel.kt b/mirai-core-api/src/commonMain/kotlin/event/EventChannel.kt index 5d52db850..6ad63949a 100644 --- a/mirai-core-api/src/commonMain/kotlin/event/EventChannel.kt +++ b/mirai-core-api/src/commonMain/kotlin/event/EventChannel.kt @@ -25,12 +25,14 @@ import net.mamoe.mirai.IMirai import net.mamoe.mirai.event.ConcurrencyKind.CONCURRENT import net.mamoe.mirai.event.ConcurrencyKind.LOCKED import net.mamoe.mirai.event.events.BotEvent -import net.mamoe.mirai.internal.event.registerEventHandler -import net.mamoe.mirai.utils.* -import org.jetbrains.annotations.Contract -import java.util.function.Consumer +import net.mamoe.mirai.utils.MiraiInternalApi +import net.mamoe.mirai.utils.NotStableForInheritance +import net.mamoe.mirai.utils.context import kotlin.coroutines.CoroutineContext import kotlin.coroutines.EmptyCoroutineContext +import kotlin.jvm.JvmMultifileClass +import kotlin.jvm.JvmName +import kotlin.jvm.JvmSynthetic import kotlin.reflect.KClass /** @@ -54,7 +56,7 @@ import kotlin.reflect.KClass * * ### 对通道的操作 * - 过滤通道: 通过 [EventChannel.filter]. 例如 `filter { it is BotEvent }` 得到一个只能监听到 [BotEvent] 的事件通道. - * - 转换为 Kotlin 协程 [Channel]: [EventChannel.asChannel] + * - 转换为 Kotlin 协程 [Channel]: [EventChannel.forwardToChannel] * - 添加 [CoroutineContext]: [context], [parentJob], [parentScope], [exceptionHandler] * * ### 创建事件监听 @@ -80,38 +82,17 @@ import kotlin.reflect.KClass * 使用 [EventChannel.forwardToChannel] 可将事件转发到指定 [SendChannel]. */ @NotStableForInheritance // since 2.12, before it was `final class`. -public abstract class EventChannel @MiraiInternalApi public constructor( - public val baseEventClass: KClass, +public expect abstract class EventChannel @MiraiInternalApi public constructor( + baseEventClass: KClass, + + defaultCoroutineContext: CoroutineContext, +) { /** * 此事件通道的默认 [CoroutineScope.coroutineContext]. 将会被添加给所有注册的事件监听器. */ - public val defaultCoroutineContext: CoroutineContext, -) { - /** - * 创建事件监听并将监听结果发送在 [Channel]. 将返回值 [Channel] [关闭][Channel.close] 时将会同时关闭事件监听. - * - * @param capacity Channel 容量. 详见 [Channel] 构造. - * - * @see subscribeAlways - * @see Channel - */ - @Deprecated( - "Please use forwardToChannel instead.", - replaceWith = ReplaceWith( - "Channel(capacity).apply { forwardToChannel(this, coroutineContext, priority) }", - "kotlinx.coroutines.channels.Channel" - ), - level = DeprecationLevel.ERROR, - ) - @DeprecatedSinceMirai(warningSince = "2.10", errorSince = "2.12") - @MiraiExperimentalApi - public fun asChannel( - capacity: Int = Channel.RENDEZVOUS, - coroutineContext: CoroutineContext = EmptyCoroutineContext, - @Suppress("UNUSED_PARAMETER") concurrency: ConcurrencyKind = CONCURRENT, - priority: EventPriority = EventPriority.NORMAL, - ): Channel = - Channel(capacity).apply { forwardToChannel(this, coroutineContext, priority) } + public val defaultCoroutineContext: CoroutineContext + + public val baseEventClass: KClass /** * 创建事件监听并将监听结果转发到 [channel]. 当 [Channel.send] 抛出 [ClosedSendChannelException] 时停止 [Listener] 监听和转发. @@ -139,16 +120,7 @@ public abstract class EventChannel @MiraiInternalApi publ channel: SendChannel<@UnsafeVariance BaseEvent>, coroutineContext: CoroutineContext = EmptyCoroutineContext, priority: EventPriority = EventPriority.MONITOR, - ): Listener<@UnsafeVariance BaseEvent> { - return subscribe(baseEventClass, coroutineContext, priority = priority) { - try { - channel.send(it) - ListeningStatus.LISTENING - } catch (_: ClosedSendChannelException) { - ListeningStatus.STOPPED - } - } - } + ): Listener<@UnsafeVariance BaseEvent> /** * 通过 [Flow] 接收此通道内的所有事件. @@ -216,9 +188,7 @@ public abstract class EventChannel @MiraiInternalApi publ * @see filterIsInstance 过滤指定类型的事件 */ @JvmSynthetic - public fun filter(filter: suspend (event: BaseEvent) -> Boolean): EventChannel { - return FilterEventChannel(this, filter) - } + public fun filter(filter: suspend (event: BaseEvent) -> Boolean): EventChannel /** * [EventChannel.filter] 的 Java 版本. @@ -258,32 +228,20 @@ public abstract class EventChannel @MiraiInternalApi publ */ @Suppress("INVISIBLE_MEMBER", "INVISIBLE_REFERENCE") @kotlin.internal.LowPriorityInOverloadResolution - public fun filter(filter: (event: BaseEvent) -> Boolean): EventChannel { - return filter { runBIO { filter(it) } } - } + public fun filter(filter: (event: BaseEvent) -> Boolean): EventChannel /** * 过滤事件的类型. 返回一个只包含 [E] 类型事件的 [EventChannel] * @see filter 获取更多信息 */ @JvmSynthetic - public inline fun filterIsInstance(): EventChannel = - filterIsInstance(E::class) + public inline fun filterIsInstance(): EventChannel /** * 过滤事件的类型. 返回一个只包含 [E] 类型事件的 [EventChannel] * @see filter 获取更多信息 */ - public fun filterIsInstance(kClass: KClass): EventChannel { - return filter { kClass.isInstance(it) }.cast() - } - - /** - * 过滤事件的类型. 返回一个只包含 [E] 类型事件的 [EventChannel] - * @see filter 获取更多信息 - */ - public fun filterIsInstance(clazz: Class): EventChannel = - filterIsInstance(clazz.kotlin) + public fun filterIsInstance(kClass: KClass): EventChannel /** @@ -300,19 +258,13 @@ public abstract class EventChannel @MiraiInternalApi publ */ @Suppress("INVISIBLE_MEMBER", "INVISIBLE_REFERENCE") @kotlin.internal.LowPriorityInOverloadResolution - public fun exceptionHandler(coroutineExceptionHandler: CoroutineExceptionHandler): EventChannel { - return context(coroutineExceptionHandler) - } + public fun exceptionHandler(coroutineExceptionHandler: CoroutineExceptionHandler): EventChannel /** * 创建一个新的 [EventChannel], 该 [EventChannel] 包含 [`this.coroutineContext`][defaultCoroutineContext] 和添加的 [coroutineExceptionHandler] * @see context */ - public fun exceptionHandler(coroutineExceptionHandler: (exception: Throwable) -> Unit): EventChannel { - return context(CoroutineExceptionHandler { _, throwable -> - coroutineExceptionHandler(throwable) - }) - } + public fun exceptionHandler(coroutineExceptionHandler: (exception: Throwable) -> Unit): EventChannel /** * 创建一个新的 [EventChannel], 该 [EventChannel] 包含 [`this.coroutineContext`][defaultCoroutineContext] 和添加的 [coroutineExceptionHandler] @@ -337,9 +289,7 @@ public abstract class EventChannel @MiraiInternalApi publ * * @see CoroutineScope.globalEventChannel `GlobalEventChannel.parentScope()` 的扩展 */ - public fun parentScope(coroutineScope: CoroutineScope): EventChannel { - return context(coroutineScope.coroutineContext) - } + public fun parentScope(coroutineScope: CoroutineScope): EventChannel /** * 指定协程父 [Job]. 之后在此 [EventChannel] 下创建的事件监听器都会成为 [job] 的子任务, 当 [job] 被取消时, 所有的事件监听器都会被取消. @@ -349,9 +299,7 @@ public abstract class EventChannel @MiraiInternalApi publ * @see parentScope * @see context */ - public fun parentJob(job: Job): EventChannel { - return context(job) - } + public fun parentJob(job: Job): EventChannel // endregion @@ -452,7 +400,7 @@ public abstract class EventChannel @MiraiInternalApi publ concurrency: ConcurrencyKind = LOCKED, priority: EventPriority = EventPriority.NORMAL, noinline handler: suspend E.(E) -> ListeningStatus, - ): Listener = subscribe(E::class, coroutineContext, concurrency, priority, handler) + ): Listener /** * 与 [subscribe] 的区别是接受 [eventClass] 参数, 而不使用 `reified` 泛型. 通常推荐使用具体化类型参数. @@ -467,10 +415,7 @@ public abstract class EventChannel @MiraiInternalApi publ concurrency: ConcurrencyKind = LOCKED, priority: EventPriority = EventPriority.NORMAL, handler: suspend E.(E) -> ListeningStatus, - ): Listener = subscribeInternal( - eventClass, - createListener(coroutineContext, concurrency, priority) { it.handler(it); } - ) + ): Listener /** * 创建一个事件监听器, 监听事件通道中所有 [E] 及其子类事件. @@ -492,7 +437,7 @@ public abstract class EventChannel @MiraiInternalApi publ concurrency: ConcurrencyKind = CONCURRENT, priority: EventPriority = EventPriority.NORMAL, noinline handler: suspend E.(E) -> Unit, - ): Listener = subscribeAlways(E::class, coroutineContext, concurrency, priority, handler) + ): Listener /** @@ -506,10 +451,7 @@ public abstract class EventChannel @MiraiInternalApi publ concurrency: ConcurrencyKind = CONCURRENT, priority: EventPriority = EventPriority.NORMAL, handler: suspend E.(E) -> Unit, - ): Listener = subscribeInternal( - eventClass, - createListener(coroutineContext, concurrency, priority) { it.handler(it); ListeningStatus.LISTENING } - ) + ): Listener /** * 创建一个事件监听器, 监听事件通道中所有 [E] 及其子类事件, 只监听一次. @@ -527,7 +469,7 @@ public abstract class EventChannel @MiraiInternalApi publ coroutineContext: CoroutineContext = EmptyCoroutineContext, priority: EventPriority = EventPriority.NORMAL, noinline handler: suspend E.(E) -> Unit, - ): Listener = subscribeOnce(E::class, coroutineContext, priority, handler) + ): Listener /** * @see subscribeOnce @@ -537,155 +479,7 @@ public abstract class EventChannel @MiraiInternalApi publ coroutineContext: CoroutineContext = EmptyCoroutineContext, priority: EventPriority = EventPriority.NORMAL, handler: suspend E.(E) -> Unit, - ): Listener = subscribeInternal( - eventClass, - createListener(coroutineContext, LOCKED, priority) { it.handler(it); ListeningStatus.STOPPED } - ) - - // endregion - - /** - * 注册 [ListenerHost] 中的所有 [EventHandler] 标注的方法到这个 [EventChannel]. 查看 [EventHandler]. - * - * @param coroutineContext 在 [defaultCoroutineContext] 的基础上, 给事件监听协程的额外的 [CoroutineContext] - * - * @see subscribe - * @see EventHandler - * @see ListenerHost - */ - @JvmOverloads - public fun registerListenerHost( - host: ListenerHost, - coroutineContext: CoroutineContext = EmptyCoroutineContext, - ) { - val jobOfListenerHost: Job? - val coroutineContext0 = if (host is SimpleListenerHost) { - val listenerCoroutineContext = host.coroutineContext - val listenerJob = listenerCoroutineContext[Job] - - val rsp = listenerCoroutineContext.minusKey(Job) + - coroutineContext + - (listenerCoroutineContext[CoroutineExceptionHandler] ?: EmptyCoroutineContext) - - val registerCancelHook = when { - listenerJob === null -> false - - // Registering cancellation hook is needless - // if [Job] of [EventChannel] is same as [Job] of [SimpleListenerHost] - (rsp[Job] ?: this.defaultCoroutineContext[Job]) === listenerJob -> false - - else -> true - } - - jobOfListenerHost = if (registerCancelHook) { - listenerCoroutineContext[Job] - } else { - null - } - rsp - } else { - jobOfListenerHost = null - coroutineContext - } - for (method in host.javaClass.declaredMethods) { - method.getAnnotation(EventHandler::class.java)?.let { - val listener = method.registerEventHandler(host, this, it, coroutineContext0) - // For [SimpleListenerHost.cancelAll] - jobOfListenerHost?.invokeOnCompletion { exception -> - listener.cancel( - when (exception) { - is CancellationException -> exception - is Throwable -> CancellationException(null, exception) - else -> null - } - ) - } - } - } - } - - // region Java API - - /** - * Java API. 查看 [subscribeAlways] 获取更多信息. - * - * ```java - * eventChannel.subscribeAlways(GroupMessageEvent.class, (event) -> { }); - * ``` - * - * @see subscribe - * @see subscribeAlways - */ - @JvmOverloads - @Suppress("INVISIBLE_MEMBER", "INVISIBLE_REFERENCE") - @kotlin.internal.LowPriorityInOverloadResolution - public fun subscribeAlways( - eventClass: Class, - coroutineContext: CoroutineContext = EmptyCoroutineContext, - concurrency: ConcurrencyKind = CONCURRENT, - priority: EventPriority = EventPriority.NORMAL, - handler: Consumer, - ): Listener = subscribeInternal( - eventClass.kotlin, - createListener(coroutineContext, concurrency, priority) { event -> - runInterruptible(Dispatchers.IO) { handler.accept(event) } - ListeningStatus.LISTENING - } - ) - - /** - * Java API. 查看 [subscribe] 获取更多信息. - * - * ```java - * eventChannel.subscribe(GroupMessageEvent.class, (event) -> { - * return ListeningStatus.LISTENING; - * }); - * ``` - * - * @see subscribe - */ - @JvmOverloads - @Suppress("INVISIBLE_MEMBER", "INVISIBLE_REFERENCE") - @kotlin.internal.LowPriorityInOverloadResolution - public fun subscribe( - eventClass: Class, - coroutineContext: CoroutineContext = EmptyCoroutineContext, - concurrency: ConcurrencyKind = CONCURRENT, - priority: EventPriority = EventPriority.NORMAL, - handler: java.util.function.Function, - ): Listener = subscribeInternal( - eventClass.kotlin, - createListener(coroutineContext, concurrency, priority) { event -> - runInterruptible(Dispatchers.IO) { handler.apply(event) } - } - ) - - /** - * Java API. 查看 [subscribeOnce] 获取更多信息. - * - * ```java - * eventChannel.subscribeOnce(GroupMessageEvent.class, (event) -> { }); - * ``` - * - * @see subscribe - * @see subscribeOnce - */ - @JvmOverloads - @Suppress("INVISIBLE_MEMBER", "INVISIBLE_REFERENCE") - @kotlin.internal.LowPriorityInOverloadResolution - public fun subscribeOnce( - eventClass: Class, - coroutineContext: CoroutineContext = EmptyCoroutineContext, - concurrency: ConcurrencyKind = CONCURRENT, - priority: EventPriority = EventPriority.NORMAL, - handler: Consumer, - ): Listener = subscribeInternal( - eventClass.kotlin, - createListener(coroutineContext, concurrency, priority) { event -> - runInterruptible(Dispatchers.IO) { handler.accept(event) } - ListeningStatus.STOPPED - } - ) + ): Listener // endregion @@ -697,19 +491,12 @@ public abstract class EventChannel @MiraiInternalApi publ protected abstract fun registerListener(eventClass: KClass, listener: Listener) // to overcome visibility issue - internal fun registerListener0(eventClass: KClass, listener: Listener) { - return registerListener(eventClass, listener) - } - - private fun , E : Event> subscribeInternal(eventClass: KClass, listener: L): L { - registerListener(eventClass, listener) - return listener - } + internal fun registerListener0(eventClass: KClass, listener: Listener) /** * Creates [Listener] instance using the [listenerBlock] action. */ - @Contract("_ -> new") // always creates new instance +// @Contract("_ -> new") // always creates new instance @MiraiInternalApi protected abstract fun createListener( coroutineContext: CoroutineContext, @@ -724,7 +511,7 @@ public abstract class EventChannel @MiraiInternalApi publ concurrencyKind: ConcurrencyKind, priority: EventPriority, listenerBlock: suspend (E) -> ListeningStatus, - ): Listener = createListener(coroutineContext, concurrencyKind, priority, listenerBlock) + ): Listener // endregion } diff --git a/mirai-core-api/src/commonMain/kotlin/event/EventChannelKotlinExtensions.kt b/mirai-core-api/src/commonMain/kotlin/event/EventChannelKotlinExtensions.kt index 9dea50ccb..e0744693c 100644 --- a/mirai-core-api/src/commonMain/kotlin/event/EventChannelKotlinExtensions.kt +++ b/mirai-core-api/src/commonMain/kotlin/event/EventChannelKotlinExtensions.kt @@ -1,10 +1,10 @@ /* - * Copyright 2019-2021 Mamoe Technologies and contributors. + * Copyright 2019-2022 Mamoe Technologies and contributors. * - * 此源代码的使用受 GNU AFFERO GENERAL PUBLIC LICENSE version 3 许可证的约束, 可以在以下链接找到该许可证. - * Use of this source code is governed by the GNU AGPLv3 license that can be found through the following link. + * 此源代码的使用受 GNU AFFERO GENERAL PUBLIC LICENSE version 3 许可证的约束, 可以在以下链接找到该许可证. + * Use of this source code is governed by the GNU AGPLv3 license that can be found through the following link. * - * https://github.com/mamoe/mirai/blob/master/LICENSE + * https://github.com/mamoe/mirai/blob/dev/LICENSE */ @file:Suppress("INVISIBLE_MEMBER", "INVISIBLE_REFERENCE") @@ -14,7 +14,8 @@ package net.mamoe.mirai.event import kotlin.coroutines.CoroutineContext import kotlin.coroutines.EmptyCoroutineContext -import kotlin.internal.LowPriorityInOverloadResolution +import kotlin.jvm.JvmName +import kotlin.jvm.JvmSynthetic /** diff --git a/mirai-core-api/src/commonMain/kotlin/event/GlobalEventChannel.kt b/mirai-core-api/src/commonMain/kotlin/event/GlobalEventChannel.kt index bc871600e..417a00b5d 100644 --- a/mirai-core-api/src/commonMain/kotlin/event/GlobalEventChannel.kt +++ b/mirai-core-api/src/commonMain/kotlin/event/GlobalEventChannel.kt @@ -19,6 +19,9 @@ import net.mamoe.mirai.utils.MiraiInternalApi import net.mamoe.mirai.utils.loadService import kotlin.coroutines.CoroutineContext import kotlin.coroutines.EmptyCoroutineContext +import kotlin.jvm.JvmMultifileClass +import kotlin.jvm.JvmName +import kotlin.jvm.JvmSynthetic import kotlin.reflect.KClass /** diff --git a/mirai-core-api/src/commonMain/kotlin/event/Listener.kt b/mirai-core-api/src/commonMain/kotlin/event/Listener.kt index 2a7e57f8f..fd0fd6925 100644 --- a/mirai-core-api/src/commonMain/kotlin/event/Listener.kt +++ b/mirai-core-api/src/commonMain/kotlin/event/Listener.kt @@ -18,6 +18,8 @@ import kotlinx.coroutines.sync.Mutex import net.mamoe.mirai.event.EventPriority.* import net.mamoe.mirai.utils.NotStableForInheritance import kotlin.coroutines.CoroutineContext +import kotlin.jvm.JvmMultifileClass +import kotlin.jvm.JvmName /** * 订阅者的状态 diff --git a/mirai-core-api/src/commonMain/kotlin/event/MessageSubscribersBuilder.kt b/mirai-core-api/src/commonMain/kotlin/event/MessageSubscribersBuilder.kt index 27e6e1563..73dc5fe04 100644 --- a/mirai-core-api/src/commonMain/kotlin/event/MessageSubscribersBuilder.kt +++ b/mirai-core-api/src/commonMain/kotlin/event/MessageSubscribersBuilder.kt @@ -1,10 +1,10 @@ /* - * Copyright 2019-2021 Mamoe Technologies and contributors. + * Copyright 2019-2022 Mamoe Technologies and contributors. * - * 此源代码的使用受 GNU AFFERO GENERAL PUBLIC LICENSE version 3 许可证的约束, 可以在以下链接找到该许可证. - * Use of this source code is governed by the GNU AGPLv3 license that can be found through the following link. + * 此源代码的使用受 GNU AFFERO GENERAL PUBLIC LICENSE version 3 许可证的约束, 可以在以下链接找到该许可证. + * Use of this source code is governed by the GNU AGPLv3 license that can be found through the following link. * - * https://github.com/mamoe/mirai/blob/master/LICENSE + * https://github.com/mamoe/mirai/blob/dev/LICENSE */ @file:Suppress( @@ -22,6 +22,9 @@ import net.mamoe.mirai.message.data.* import net.mamoe.mirai.message.data.MessageSource.Key.quote import net.mamoe.mirai.utils.DeprecatedSinceMirai import kotlin.annotation.AnnotationTarget.CONSTRUCTOR +import kotlin.jvm.JvmName +import kotlin.jvm.JvmOverloads +import kotlin.jvm.JvmSynthetic /** diff --git a/mirai-core-api/src/commonMain/kotlin/event/deprecated.nextEvent.kt b/mirai-core-api/src/commonMain/kotlin/event/deprecated.nextEvent.kt index 11b21decc..fc28c5e13 100644 --- a/mirai-core-api/src/commonMain/kotlin/event/deprecated.nextEvent.kt +++ b/mirai-core-api/src/commonMain/kotlin/event/deprecated.nextEvent.kt @@ -17,6 +17,8 @@ import net.mamoe.mirai.Bot import net.mamoe.mirai.event.events.BotEvent import net.mamoe.mirai.utils.DeprecatedSinceMirai import kotlin.coroutines.resume +import kotlin.jvm.JvmName +import kotlin.jvm.JvmSynthetic import kotlin.reflect.KClass diff --git a/mirai-core-api/src/commonMain/kotlin/event/deprecated.nextEventAsync.kt b/mirai-core-api/src/commonMain/kotlin/event/deprecated.nextEventAsync.kt index afb522632..53af8dbc4 100644 --- a/mirai-core-api/src/commonMain/kotlin/event/deprecated.nextEventAsync.kt +++ b/mirai-core-api/src/commonMain/kotlin/event/deprecated.nextEventAsync.kt @@ -17,6 +17,8 @@ import net.mamoe.mirai.utils.DeprecatedSinceMirai import net.mamoe.mirai.utils.MiraiExperimentalApi import kotlin.coroutines.CoroutineContext import kotlin.coroutines.EmptyCoroutineContext +import kotlin.jvm.JvmName +import kotlin.jvm.JvmSynthetic /** diff --git a/mirai-core-api/src/commonMain/kotlin/event/deprecated.syncFromEvent.kt b/mirai-core-api/src/commonMain/kotlin/event/deprecated.syncFromEvent.kt index dcbd18927..a3b39a731 100644 --- a/mirai-core-api/src/commonMain/kotlin/event/deprecated.syncFromEvent.kt +++ b/mirai-core-api/src/commonMain/kotlin/event/deprecated.syncFromEvent.kt @@ -16,6 +16,8 @@ import kotlinx.coroutines.* import net.mamoe.mirai.utils.DeprecatedSinceMirai import kotlin.coroutines.CoroutineContext import kotlin.coroutines.EmptyCoroutineContext +import kotlin.jvm.JvmName +import kotlin.jvm.JvmSynthetic import kotlin.reflect.KClass /** diff --git a/mirai-core-api/src/commonMain/kotlin/event/events/ImageUploadEvent.kt b/mirai-core-api/src/commonMain/kotlin/event/events/ImageUploadEvent.kt index f72ef7b83..3a9cae991 100644 --- a/mirai-core-api/src/commonMain/kotlin/event/events/ImageUploadEvent.kt +++ b/mirai-core-api/src/commonMain/kotlin/event/events/ImageUploadEvent.kt @@ -23,6 +23,8 @@ import net.mamoe.mirai.internal.event.VerboseEvent import net.mamoe.mirai.message.data.Image import net.mamoe.mirai.utils.ExternalResource import net.mamoe.mirai.utils.MiraiInternalApi +import kotlin.jvm.JvmMultifileClass +import kotlin.jvm.JvmName /** diff --git a/mirai-core-api/src/commonMain/kotlin/event/events/MessageEvent.kt b/mirai-core-api/src/commonMain/kotlin/event/events/MessageEvent.kt index edf91ccfa..0fe869a38 100644 --- a/mirai-core-api/src/commonMain/kotlin/event/events/MessageEvent.kt +++ b/mirai-core-api/src/commonMain/kotlin/event/events/MessageEvent.kt @@ -24,6 +24,8 @@ import net.mamoe.mirai.message.data.source import net.mamoe.mirai.message.isContextIdenticalWith import net.mamoe.mirai.utils.DeprecatedSinceMirai import net.mamoe.mirai.utils.MiraiInternalApi +import kotlin.jvm.JvmMultifileClass +import kotlin.jvm.JvmName /** diff --git a/mirai-core-api/src/commonMain/kotlin/event/events/MessagePostSendEvent.kt b/mirai-core-api/src/commonMain/kotlin/event/events/MessagePostSendEvent.kt index 825c20640..78fd091a8 100644 --- a/mirai-core-api/src/commonMain/kotlin/event/events/MessagePostSendEvent.kt +++ b/mirai-core-api/src/commonMain/kotlin/event/events/MessagePostSendEvent.kt @@ -22,6 +22,9 @@ import net.mamoe.mirai.message.data.MessageChain import net.mamoe.mirai.message.data.MessageSource import net.mamoe.mirai.utils.DeprecatedSinceMirai import net.mamoe.mirai.utils.MiraiInternalApi +import kotlin.jvm.JvmMultifileClass +import kotlin.jvm.JvmName +import kotlin.jvm.JvmSynthetic /** diff --git a/mirai-core-api/src/commonMain/kotlin/event/events/MessagePreSendEvent.kt b/mirai-core-api/src/commonMain/kotlin/event/events/MessagePreSendEvent.kt index 4a2e7bf34..535a47ce8 100644 --- a/mirai-core-api/src/commonMain/kotlin/event/events/MessagePreSendEvent.kt +++ b/mirai-core-api/src/commonMain/kotlin/event/events/MessagePreSendEvent.kt @@ -20,6 +20,8 @@ import net.mamoe.mirai.internal.event.VerboseEvent import net.mamoe.mirai.message.data.Message import net.mamoe.mirai.utils.DeprecatedSinceMirai import net.mamoe.mirai.utils.MiraiInternalApi +import kotlin.jvm.JvmMultifileClass +import kotlin.jvm.JvmName /** @@ -105,7 +107,7 @@ public data class GroupTempMessagePreSendEvent @MiraiInternalApi constructor( public override val target: NormalMember, /** 待发送的消息. 修改后将会同时应用于发送. */ public override var message: Message -) : @kotlin.Suppress("DEPRECATION_ERROR") TempMessagePreSendEvent(target, message) { +) : @Suppress("DEPRECATION_ERROR") TempMessagePreSendEvent(target, message) { public override val group: Group get() = target.group } diff --git a/mirai-core-api/src/commonMain/kotlin/event/events/MessageRecallEvent.kt b/mirai-core-api/src/commonMain/kotlin/event/events/MessageRecallEvent.kt index 067673721..0cf005c99 100644 --- a/mirai-core-api/src/commonMain/kotlin/event/events/MessageRecallEvent.kt +++ b/mirai-core-api/src/commonMain/kotlin/event/events/MessageRecallEvent.kt @@ -18,6 +18,9 @@ import net.mamoe.mirai.event.AbstractEvent import net.mamoe.mirai.internal.network.Packet import net.mamoe.mirai.message.data.MessageSource import net.mamoe.mirai.utils.MiraiInternalApi +import net.mamoe.mirai.utils.isSameClass +import kotlin.jvm.JvmMultifileClass +import kotlin.jvm.JvmName /** @@ -78,9 +81,7 @@ public sealed class MessageRecallEvent : BotEvent, AbstractEvent() { @Suppress("DuplicatedCode") override fun equals(other: Any?): Boolean { if (this === other) return true - if (javaClass != other?.javaClass) return false - - other as FriendRecall + if (other !is FriendRecall || !isSameClass(this, other)) return false if (bot != other.bot) return false if (!messageIds.contentEquals(other.messageIds)) return false @@ -123,9 +124,7 @@ public sealed class MessageRecallEvent : BotEvent, AbstractEvent() { @Suppress("DuplicatedCode") override fun equals(other: Any?): Boolean { if (this === other) return true - if (javaClass != other?.javaClass) return false - - other as GroupRecall + if (other !is GroupRecall || !isSameClass(this, other)) return false if (bot != other.bot) return false if (authorId != other.authorId) return false diff --git a/mirai-core-api/src/commonMain/kotlin/event/events/MessageSyncEvent.kt b/mirai-core-api/src/commonMain/kotlin/event/events/MessageSyncEvent.kt index 2c149f8d5..0422179e3 100644 --- a/mirai-core-api/src/commonMain/kotlin/event/events/MessageSyncEvent.kt +++ b/mirai-core-api/src/commonMain/kotlin/event/events/MessageSyncEvent.kt @@ -18,6 +18,8 @@ import net.mamoe.mirai.message.data.MessageChain import net.mamoe.mirai.message.data.MessageSource import net.mamoe.mirai.message.data.OnlineMessageSource import net.mamoe.mirai.message.data.source +import kotlin.jvm.JvmMultifileClass +import kotlin.jvm.JvmName /** diff --git a/mirai-core-api/src/commonMain/kotlin/event/events/NudgeEvent.kt b/mirai-core-api/src/commonMain/kotlin/event/events/NudgeEvent.kt index 58fd232c5..da119a170 100644 --- a/mirai-core-api/src/commonMain/kotlin/event/events/NudgeEvent.kt +++ b/mirai-core-api/src/commonMain/kotlin/event/events/NudgeEvent.kt @@ -17,6 +17,8 @@ import net.mamoe.mirai.contact.* import net.mamoe.mirai.event.AbstractEvent import net.mamoe.mirai.internal.network.Packet import net.mamoe.mirai.utils.MiraiInternalApi +import kotlin.jvm.JvmMultifileClass +import kotlin.jvm.JvmName /** diff --git a/mirai-core-api/src/commonMain/kotlin/event/events/bot.kt b/mirai-core-api/src/commonMain/kotlin/event/events/bot.kt index 0d6d4355d..c81a4baa3 100644 --- a/mirai-core-api/src/commonMain/kotlin/event/events/bot.kt +++ b/mirai-core-api/src/commonMain/kotlin/event/events/bot.kt @@ -1,10 +1,10 @@ /* - * Copyright 2019-2021 Mamoe Technologies and contributors. + * Copyright 2019-2022 Mamoe Technologies and contributors. * - * 此源代码的使用受 GNU AFFERO GENERAL PUBLIC LICENSE version 3 许可证的约束, 可以在以下链接找到该许可证. - * Use of this source code is governed by the GNU AGPLv3 license that can be found through the following link. + * 此源代码的使用受 GNU AFFERO GENERAL PUBLIC LICENSE version 3 许可证的约束, 可以在以下链接找到该许可证. + * Use of this source code is governed by the GNU AGPLv3 license that can be found through the following link. * - * https://github.com/mamoe/mirai/blob/master/LICENSE + * https://github.com/mamoe/mirai/blob/dev/LICENSE */ @file:Suppress("unused", "FunctionName") @@ -18,6 +18,8 @@ import net.mamoe.mirai.event.AbstractEvent import net.mamoe.mirai.internal.network.Packet import net.mamoe.mirai.utils.MiraiExperimentalApi import net.mamoe.mirai.utils.MiraiInternalApi +import kotlin.jvm.JvmMultifileClass +import kotlin.jvm.JvmName // note: 若你使用 IntelliJ IDEA, 按 alt + 7 可打开结构 diff --git a/mirai-core-api/src/commonMain/kotlin/event/events/friend.kt b/mirai-core-api/src/commonMain/kotlin/event/events/friend.kt index 83642967d..daf03073a 100644 --- a/mirai-core-api/src/commonMain/kotlin/event/events/friend.kt +++ b/mirai-core-api/src/commonMain/kotlin/event/events/friend.kt @@ -1,10 +1,10 @@ /* - * Copyright 2019-2021 Mamoe Technologies and contributors. + * Copyright 2019-2022 Mamoe Technologies and contributors. * - * 此源代码的使用受 GNU AFFERO GENERAL PUBLIC LICENSE version 3 许可证的约束, 可以在以下链接找到该许可证. - * Use of this source code is governed by the GNU AGPLv3 license that can be found through the following link. + * 此源代码的使用受 GNU AFFERO GENERAL PUBLIC LICENSE version 3 许可证的约束, 可以在以下链接找到该许可证. + * Use of this source code is governed by the GNU AGPLv3 license that can be found through the following link. * - * https://github.com/mamoe/mirai/blob/master/LICENSE + * https://github.com/mamoe/mirai/blob/dev/LICENSE */ @file:JvmMultifileClass @@ -13,6 +13,8 @@ package net.mamoe.mirai.event.events +import kotlinx.atomicfu.AtomicBoolean +import kotlinx.atomicfu.atomic import me.him188.kotlin.jvm.blocking.bridge.JvmBlockingBridge import net.mamoe.mirai.Bot import net.mamoe.mirai.Mirai @@ -23,7 +25,9 @@ import net.mamoe.mirai.event.AbstractEvent import net.mamoe.mirai.internal.event.VerboseEvent import net.mamoe.mirai.internal.network.Packet import net.mamoe.mirai.utils.MiraiInternalApi -import java.util.concurrent.atomic.AtomicBoolean +import kotlin.jvm.JvmField +import kotlin.jvm.JvmMultifileClass +import kotlin.jvm.JvmName /** @@ -80,7 +84,7 @@ public data class NewFriendRequestEvent @MiraiInternalApi public constructor( public val fromNick: String, ) : BotEvent, Packet, AbstractEvent(), FriendInfoChangeEvent { @JvmField - internal val responded: AtomicBoolean = AtomicBoolean(false) + internal val responded: AtomicBoolean = atomic(false) /** * @return 申请人来自的群. 当申请人来自其他途径申请时为 `null` diff --git a/mirai-core-api/src/commonMain/kotlin/event/events/group.kt b/mirai-core-api/src/commonMain/kotlin/event/events/group.kt index e4af141c8..f4dfebfea 100644 --- a/mirai-core-api/src/commonMain/kotlin/event/events/group.kt +++ b/mirai-core-api/src/commonMain/kotlin/event/events/group.kt @@ -16,6 +16,8 @@ package net.mamoe.mirai.event.events +import kotlinx.atomicfu.AtomicBoolean +import kotlinx.atomicfu.atomic import me.him188.kotlin.jvm.blocking.bridge.JvmBlockingBridge import net.mamoe.mirai.Bot import net.mamoe.mirai.Mirai @@ -27,7 +29,7 @@ import net.mamoe.mirai.internal.network.Packet import net.mamoe.mirai.utils.DeprecatedSinceMirai import net.mamoe.mirai.utils.MiraiExperimentalApi import net.mamoe.mirai.utils.MiraiInternalApi -import java.util.concurrent.atomic.AtomicBoolean +import kotlin.jvm.* /** * 机器人被踢出群或在其他客户端主动退出一个群. 在事件广播前 [Bot.groups] 就已删除这个群. @@ -353,7 +355,7 @@ public data class BotInvitedJoinGroupRequestEvent @MiraiInternalApi constructor( public val invitor: Friend? get() = this.bot.getFriend(invitorId) @JvmField - internal val responded: AtomicBoolean = AtomicBoolean(false) + internal val responded: AtomicBoolean = atomic(false) @JvmBlockingBridge public suspend fun accept(): Unit = Mirai.acceptInvitedJoinGroupRequest(this) @@ -403,7 +405,7 @@ public data class MemberJoinRequestEvent @MiraiInternalApi constructor( @JvmField @PublishedApi - internal val responded: AtomicBoolean = AtomicBoolean(false) + internal val responded: AtomicBoolean = atomic(false) /** * 同意这个请求 @@ -445,7 +447,7 @@ public data class MemberJoinRequestEvent @MiraiInternalApi constructor( @Deprecated("For binary compatibility", level = DeprecationLevel.HIDDEN) @JvmStatic @JvmName("copy\$default") // avoid being mangled - fun `copy$default`( + fun copy_default( var0: MemberJoinRequestEvent, var1: Bot, var2: Long, var4: String, var5: Long, var7: Long, var9: String, var10: String, var11: Int, @Suppress("UNUSED_PARAMETER") var12: Any ): MemberJoinRequestEvent { diff --git a/mirai-core-api/src/commonMain/kotlin/event/events/types.kt b/mirai-core-api/src/commonMain/kotlin/event/events/types.kt index aa2b25f4b..87eb8fa14 100644 --- a/mirai-core-api/src/commonMain/kotlin/event/events/types.kt +++ b/mirai-core-api/src/commonMain/kotlin/event/events/types.kt @@ -1,10 +1,10 @@ /* - * Copyright 2019-2021 Mamoe Technologies and contributors. + * Copyright 2019-2022 Mamoe Technologies and contributors. * - * 此源代码的使用受 GNU AFFERO GENERAL PUBLIC LICENSE version 3 许可证的约束, 可以在以下链接找到该许可证. - * Use of this source code is governed by the GNU AGPLv3 license that can be found through the following link. + * 此源代码的使用受 GNU AFFERO GENERAL PUBLIC LICENSE version 3 许可证的约束, 可以在以下链接找到该许可证. + * Use of this source code is governed by the GNU AGPLv3 license that can be found through the following link. * - * https://github.com/mamoe/mirai/blob/master/LICENSE + * https://github.com/mamoe/mirai/blob/dev/LICENSE */ @file:JvmMultifileClass @@ -17,6 +17,9 @@ import net.mamoe.mirai.contact.* import net.mamoe.mirai.event.Event import net.mamoe.mirai.internal.network.Packet import net.mamoe.mirai.utils.MiraiInternalApi +import kotlin.jvm.JvmMultifileClass +import kotlin.jvm.JvmName +import kotlin.jvm.JvmSynthetic /** * 有关一个 [Bot] 的事件 diff --git a/mirai-core-api/src/commonMain/kotlin/event/select.kt b/mirai-core-api/src/commonMain/kotlin/event/select.kt index bf385fc75..ce8c7b56e 100644 --- a/mirai-core-api/src/commonMain/kotlin/event/select.kt +++ b/mirai-core-api/src/commonMain/kotlin/event/select.kt @@ -1,10 +1,10 @@ /* - * Copyright 2019-2021 Mamoe Technologies and contributors. + * Copyright 2019-2022 Mamoe Technologies and contributors. * - * 此源代码的使用受 GNU AFFERO GENERAL PUBLIC LICENSE version 3 许可证的约束, 可以在以下链接找到该许可证. - * Use of this source code is governed by the GNU AGPLv3 license that can be found through the following link. + * 此源代码的使用受 GNU AFFERO GENERAL PUBLIC LICENSE version 3 许可证的约束, 可以在以下链接找到该许可证. + * Use of this source code is governed by the GNU AGPLv3 license that can be found through the following link. * - * https://github.com/mamoe/mirai/blob/master/LICENSE + * https://github.com/mamoe/mirai/blob/dev/LICENSE */ @file:Suppress("DuplicatedCode") @@ -19,6 +19,9 @@ import net.mamoe.mirai.message.data.PlainText import net.mamoe.mirai.message.isContextIdenticalWith import net.mamoe.mirai.message.nextMessage import net.mamoe.mirai.utils.MiraiExperimentalApi +import kotlin.jvm.JvmInline +import kotlin.jvm.JvmName +import kotlin.jvm.JvmSynthetic /** @@ -404,7 +407,7 @@ public abstract class MessageSelectBuilderUnit @PublishedAp @Suppress("unused") @JvmName("invoke-RNyhSv4") @Deprecated("For binary compatibility", level = DeprecationLevel.HIDDEN) - public fun MessageSelectionTimeoutChecker.invoke000(block: suspend () -> R): Void? { + public fun MessageSelectionTimeoutChecker.invoke000(block: suspend () -> R): Nothing? { invoke(block) return null } @@ -417,7 +420,7 @@ public abstract class MessageSelectBuilderUnit @PublishedAp @JvmName("reply-RNyhSv4") @Deprecated("For binary compatibility", level = DeprecationLevel.HIDDEN) - public infix fun MessageSelectionTimeoutChecker.reply000(block: suspend () -> Any?): Void? { + public infix fun MessageSelectionTimeoutChecker.reply000(block: suspend () -> Any?): Nothing? { reply(block) return null } @@ -430,7 +433,7 @@ public abstract class MessageSelectBuilderUnit @PublishedAp @JvmName("reply-sCZ5gAI") @Deprecated("For binary compatibility", level = DeprecationLevel.HIDDEN) - public infix fun MessageSelectionTimeoutChecker.reply000(message: String): Void? { + public infix fun MessageSelectionTimeoutChecker.reply000(message: String): Nothing? { reply(message) return null } @@ -443,7 +446,7 @@ public abstract class MessageSelectBuilderUnit @PublishedAp @JvmName("reply-AVDwu3U") @Deprecated("For binary compatibility", level = DeprecationLevel.HIDDEN) - public infix fun MessageSelectionTimeoutChecker.reply000(message: Message): Void? { + public infix fun MessageSelectionTimeoutChecker.reply000(message: Message): Nothing? { reply(message) return null } @@ -457,7 +460,7 @@ public abstract class MessageSelectBuilderUnit @PublishedAp @JvmName("quoteReply-RNyhSv4") @Deprecated("For binary compatibility", level = DeprecationLevel.HIDDEN) - public infix fun MessageSelectionTimeoutChecker.quoteReply000(block: suspend () -> Any?): Void? { + public infix fun MessageSelectionTimeoutChecker.quoteReply000(block: suspend () -> Any?): Nothing? { reply(block) return null } @@ -470,7 +473,7 @@ public abstract class MessageSelectBuilderUnit @PublishedAp @JvmName("quoteReply-sCZ5gAI") @Deprecated("For binary compatibility", level = DeprecationLevel.HIDDEN) - public infix fun MessageSelectionTimeoutChecker.quoteReply000(message: String): Void? { + public infix fun MessageSelectionTimeoutChecker.quoteReply000(message: String): Nothing? { reply(message) return null } @@ -483,7 +486,7 @@ public abstract class MessageSelectBuilderUnit @PublishedAp @JvmName("quoteReply-AVDwu3U") @Deprecated("For binary compatibility", level = DeprecationLevel.HIDDEN) - public infix fun MessageSelectionTimeoutChecker.quoteReply000(message: Message): Void? { + public infix fun MessageSelectionTimeoutChecker.quoteReply000(message: Message): Nothing? { reply(message) return null } diff --git a/mirai-core-api/src/commonMain/kotlin/event/subscribeMessages.kt b/mirai-core-api/src/commonMain/kotlin/event/subscribeMessages.kt index 9dbff28e8..19357af48 100644 --- a/mirai-core-api/src/commonMain/kotlin/event/subscribeMessages.kt +++ b/mirai-core-api/src/commonMain/kotlin/event/subscribeMessages.kt @@ -1,10 +1,10 @@ /* - * Copyright 2019-2021 Mamoe Technologies and contributors. + * Copyright 2019-2022 Mamoe Technologies and contributors. * - * 此源代码的使用受 GNU AFFERO GENERAL PUBLIC LICENSE version 3 许可证的约束, 可以在以下链接找到该许可证. - * Use of this source code is governed by the GNU AGPLv3 license that can be found through the following link. + * 此源代码的使用受 GNU AFFERO GENERAL PUBLIC LICENSE version 3 许可证的约束, 可以在以下链接找到该许可证. + * Use of this source code is governed by the GNU AGPLv3 license that can be found through the following link. * - * https://github.com/mamoe/mirai/blob/master/LICENSE + * https://github.com/mamoe/mirai/blob/dev/LICENSE */ @file:JvmMultifileClass @@ -25,6 +25,8 @@ import kotlin.contracts.InvocationKind import kotlin.contracts.contract import kotlin.coroutines.CoroutineContext import kotlin.coroutines.EmptyCoroutineContext +import kotlin.jvm.JvmMultifileClass +import kotlin.jvm.JvmName public typealias MessageEventSubscribersBuilder = MessageSubscribersBuilder, Unit, Unit> diff --git a/mirai-core-api/src/commonMain/kotlin/internal/message/MessageSerializersImpl.kt b/mirai-core-api/src/commonMain/kotlin/internal/message/MessageSerializersImpl.kt index e32844664..0aaaf0365 100644 --- a/mirai-core-api/src/commonMain/kotlin/internal/message/MessageSerializersImpl.kt +++ b/mirai-core-api/src/commonMain/kotlin/internal/message/MessageSerializersImpl.kt @@ -13,7 +13,10 @@ import kotlinx.serialization.KSerializer import kotlinx.serialization.SerialName import kotlinx.serialization.Serializable import kotlinx.serialization.descriptors.buildClassSerialDescriptor -import kotlinx.serialization.modules.* +import kotlinx.serialization.modules.PolymorphicModuleBuilder +import kotlinx.serialization.modules.SerializersModule +import kotlinx.serialization.modules.overwriteWith +import kotlinx.serialization.modules.polymorphic import net.mamoe.mirai.Mirai import net.mamoe.mirai.message.MessageSerializers import net.mamoe.mirai.message.data.* @@ -21,9 +24,8 @@ import net.mamoe.mirai.utils.MiraiInternalApi import net.mamoe.mirai.utils.lateinitMutableProperty import net.mamoe.mirai.utils.map import net.mamoe.mirai.utils.takeElementsFrom +import kotlin.jvm.Synchronized import kotlin.reflect.KClass -import kotlin.reflect.full.allSuperclasses -import kotlin.reflect.full.isSubclassOf @MiraiInternalApi public open class MessageSourceSerializerImpl(serialName: String) : @@ -208,37 +210,25 @@ internal object MessageSerializersImpl : MessageSerializers { } } -internal fun SerializersModule.overwritePolymorphicWith( +internal expect fun SerializersModule.overwritePolymorphicWith( type: KClass, serializer: KSerializer -): SerializersModule { - return overwriteWith(SerializersModule { - // contextual(type, serializer) - for (superclass in type.allSuperclasses) { - if (superclass.isFinal) continue - if (!superclass.isSubclassOf(SingleMessage::class)) continue - @Suppress("UNCHECKED_CAST") - polymorphic(superclass as KClass) { - subclass(type, serializer) - } - } - }) -} +): SerializersModule -private inline fun SerializersModuleBuilder.hierarchicallyPolymorphic(serializer: KSerializer) = - hierarchicallyPolymorphic(M::class, serializer) - -private fun SerializersModuleBuilder.hierarchicallyPolymorphic( - type: KClass, - serializer: KSerializer -) { - // contextual(type, serializer) - for (superclass in type.allSuperclasses) { - if (superclass.isFinal) continue - if (!superclass.isSubclassOf(SingleMessage::class)) continue - @Suppress("UNCHECKED_CAST") - polymorphic(superclass as KClass) { - subclass(type, serializer) - } - } -} \ No newline at end of file +//private inline fun SerializersModuleBuilder.hierarchicallyPolymorphic(serializer: KSerializer) = +// hierarchicallyPolymorphic(M::class, serializer) +// +//private fun SerializersModuleBuilder.hierarchicallyPolymorphic( +// type: KClass, +// serializer: KSerializer +//) { +// // contextual(type, serializer) +// for (superclass in type.allSuperclasses) { +// if (superclass.isFinal) continue +// if (!superclass.isSubclassOf(SingleMessage::class)) continue +// @Suppress("UNCHECKED_CAST") +// polymorphic(superclass as KClass) { +// subclass(type, serializer) +// } +// } +//} \ No newline at end of file diff --git a/mirai-core-api/src/commonMain/kotlin/internal/utils/MarkedMiraiLogger.kt b/mirai-core-api/src/commonMain/kotlin/internal/utils/MarkedMiraiLogger.kt index 0fd7059d1..4f19ad044 100644 --- a/mirai-core-api/src/commonMain/kotlin/internal/utils/MarkedMiraiLogger.kt +++ b/mirai-core-api/src/commonMain/kotlin/internal/utils/MarkedMiraiLogger.kt @@ -1,5 +1,5 @@ /* - * Copyright 2019-2021 Mamoe Technologies and contributors. + * Copyright 2019-2022 Mamoe Technologies and contributors. * * 此源代码的使用受 GNU AFFERO GENERAL PUBLIC LICENSE version 3 许可证的约束, 可以在以下链接找到该许可证. * Use of this source code is governed by the GNU AGPLv3 license that can be found through the following link. @@ -10,8 +10,6 @@ package net.mamoe.mirai.internal.utils import net.mamoe.mirai.utils.MiraiLogger -import org.apache.logging.log4j.Marker -import org.apache.logging.log4j.MarkerManager /** * 内部添加 [Marker] 支持, 并兼容旧 [MiraiLogger] API. diff --git a/mirai-core-api/src/commonMain/kotlin/internal/utils/Marker.kt b/mirai-core-api/src/commonMain/kotlin/internal/utils/Marker.kt new file mode 100644 index 000000000..bf8908f2f --- /dev/null +++ b/mirai-core-api/src/commonMain/kotlin/internal/utils/Marker.kt @@ -0,0 +1,15 @@ +/* + * Copyright 2019-2022 Mamoe Technologies and contributors. + * + * 此源代码的使用受 GNU AFFERO GENERAL PUBLIC LICENSE version 3 许可证的约束, 可以在以下链接找到该许可证. + * Use of this source code is governed by the GNU AGPLv3 license that can be found through the following link. + * + * https://github.com/mamoe/mirai/blob/dev/LICENSE + */ + +package net.mamoe.mirai.internal.utils + +internal expect interface Marker { + fun addParents(vararg parent: Marker) +} + diff --git a/mirai-core-api/src/commonMain/kotlin/internal/utils/MarkerManager.kt b/mirai-core-api/src/commonMain/kotlin/internal/utils/MarkerManager.kt new file mode 100644 index 000000000..241cee025 --- /dev/null +++ b/mirai-core-api/src/commonMain/kotlin/internal/utils/MarkerManager.kt @@ -0,0 +1,14 @@ +/* + * Copyright 2019-2022 Mamoe Technologies and contributors. + * + * 此源代码的使用受 GNU AFFERO GENERAL PUBLIC LICENSE version 3 许可证的约束, 可以在以下链接找到该许可证. + * Use of this source code is governed by the GNU AGPLv3 license that can be found through the following link. + * + * https://github.com/mamoe/mirai/blob/dev/LICENSE + */ + +package net.mamoe.mirai.internal.utils + +internal expect object MarkerManager { + fun getMarker(name: String): Marker +} \ No newline at end of file diff --git a/mirai-core-api/src/commonMain/kotlin/internal/utils/StdoutLogger.kt b/mirai-core-api/src/commonMain/kotlin/internal/utils/StdoutLogger.kt index 1b3eb4624..e3801e756 100644 --- a/mirai-core-api/src/commonMain/kotlin/internal/utils/StdoutLogger.kt +++ b/mirai-core-api/src/commonMain/kotlin/internal/utils/StdoutLogger.kt @@ -10,8 +10,6 @@ package net.mamoe.mirai.internal.utils import net.mamoe.mirai.utils.* -import java.text.SimpleDateFormat -import java.util.* /** @@ -101,11 +99,8 @@ internal open class StdoutLogger constructor( else debug(message.toString()) } - protected open val timeFormat: SimpleDateFormat by threadLocal { - SimpleDateFormat("yyyy-MM-dd HH:mm:ss", Locale.getDefault()) - } - private val currentTimeFormatted get() = timeFormat.format(Date()) + private val currentTimeFormatted get() = currentTimeFormatted(null) @MiraiExperimentalApi("This is subject to change.") protected enum class Color(private val format: String) { diff --git a/mirai-core-api/src/commonMain/kotlin/message/action/AsyncRecallResult.kt b/mirai-core-api/src/commonMain/kotlin/message/action/AsyncRecallResult.kt index 1a44c0fd9..a9f321e7c 100644 --- a/mirai-core-api/src/commonMain/kotlin/message/action/AsyncRecallResult.kt +++ b/mirai-core-api/src/commonMain/kotlin/message/action/AsyncRecallResult.kt @@ -8,85 +8,41 @@ */ @file:Suppress("MemberVisibilityCanBePrivate", "unused") +@file:JvmBlockingBridge package net.mamoe.mirai.message.action -import kotlinx.coroutines.* +import kotlinx.coroutines.Deferred import me.him188.kotlin.jvm.blocking.bridge.JvmBlockingBridge import net.mamoe.mirai.message.data.MessageSource import net.mamoe.mirai.message.data.MessageSource.Key.recallIn -import java.util.concurrent.CompletableFuture /** * [MessageSource.recallIn] 的结果. * * @see MessageSource.recallIn */ -public class AsyncRecallResult internal constructor( +public expect class AsyncRecallResult internal constructor( /** - * 撤回时产生的异常. Kotlin [Deferred] API. + * 撤回时产生的异常. */ - public val exception: Deferred, + exception: Deferred, ) { - /** - * 撤回时产生的异常. Java [CompletableFuture] API. - */ - public val exceptionFuture: CompletableFuture by lazy { exception.asCompletableFuture() } + public val exception: Deferred /** - * 撤回是否成功. Kotlin [Deferred] API. + * 撤回是否成功. */ - public val isSuccess: Deferred by lazy { - CompletableDeferred().apply { - exception.invokeOnCompletion { - complete(it == null) - } - } - } - - /** - * 撤回是否成功. Java [CompletableFuture] API. - */ - public val isSuccessFuture: CompletableFuture by lazy { isSuccess.asCompletableFuture() } + public val isSuccess: Deferred /** * 等待撤回完成, 返回撤回时产生的异常. */ - @JvmBlockingBridge - public suspend fun awaitException(): Throwable? { - return exception.await() - } + public suspend fun awaitException(): Throwable? /** * 等待撤回完成, 返回撤回的结果. */ - @JvmBlockingBridge - public suspend fun awaitIsSuccess(): Boolean { - return isSuccess.await() - } + public suspend fun awaitIsSuccess(): Boolean } - -// copied from kotlinx-coroutines-jdk8 -private fun Deferred.asCompletableFuture(): CompletableFuture { - val future = CompletableFuture() - setupCancellation(future) - invokeOnCompletion { - @OptIn(ExperimentalCoroutinesApi::class) - try { - future.complete(getCompleted()) - } catch (t: Throwable) { - future.completeExceptionally(t) - } - } - return future -} - -// copied from kotlinx-coroutines-jdk8 -private fun Job.setupCancellation(future: CompletableFuture<*>) { - future.whenComplete { _, exception -> - cancel(exception?.let { - it as? CancellationException ?: CancellationException("CompletableFuture was completed exceptionally", it) - }) - } -} diff --git a/mirai-core-api/src/commonMain/kotlin/message/action/Nudge.kt b/mirai-core-api/src/commonMain/kotlin/message/action/Nudge.kt index e7b3c833b..c44aa50b2 100644 --- a/mirai-core-api/src/commonMain/kotlin/message/action/Nudge.kt +++ b/mirai-core-api/src/commonMain/kotlin/message/action/Nudge.kt @@ -1,10 +1,10 @@ /* - * Copyright 2019-2021 Mamoe Technologies and contributors. + * Copyright 2019-2022 Mamoe Technologies and contributors. * - * 此源代码的使用受 GNU AFFERO GENERAL PUBLIC LICENSE version 3 许可证的约束, 可以在以下链接找到该许可证. - * Use of this source code is governed by the GNU AGPLv3 license that can be found through the following link. + * 此源代码的使用受 GNU AFFERO GENERAL PUBLIC LICENSE version 3 许可证的约束, 可以在以下链接找到该许可证. + * Use of this source code is governed by the GNU AGPLv3 license that can be found through the following link. * - * https://github.com/mamoe/mirai/blob/master/LICENSE + * https://github.com/mamoe/mirai/blob/dev/LICENSE */ package net.mamoe.mirai.message.action @@ -16,6 +16,8 @@ import net.mamoe.mirai.event.events.NudgeEvent import net.mamoe.mirai.message.data.PokeMessage import net.mamoe.mirai.utils.BotConfiguration import net.mamoe.mirai.utils.BotConfiguration.MiraiProtocol +import kotlin.jvm.JvmStatic +import kotlin.jvm.JvmSynthetic /** * 一个 "戳一戳" 动作. diff --git a/mirai-core-api/src/commonMain/kotlin/message/code/MiraiCode.kt b/mirai-core-api/src/commonMain/kotlin/message/code/MiraiCode.kt index 64d7535c4..0749f89fc 100644 --- a/mirai-core-api/src/commonMain/kotlin/message/code/MiraiCode.kt +++ b/mirai-core-api/src/commonMain/kotlin/message/code/MiraiCode.kt @@ -1,10 +1,10 @@ /* - * Copyright 2019-2021 Mamoe Technologies and contributors. + * Copyright 2019-2022 Mamoe Technologies and contributors. * - * 此源代码的使用受 GNU AFFERO GENERAL PUBLIC LICENSE version 3 许可证的约束, 可以在以下链接找到该许可证. - * Use of this source code is governed by the GNU AGPLv3 license that can be found through the following link. + * 此源代码的使用受 GNU AFFERO GENERAL PUBLIC LICENSE version 3 许可证的约束, 可以在以下链接找到该许可证. + * Use of this source code is governed by the GNU AGPLv3 license that can be found through the following link. * - * https://github.com/mamoe/mirai/blob/master/LICENSE + * https://github.com/mamoe/mirai/blob/dev/LICENSE */ @file:Suppress("unused", "NOTHING_TO_INLINE") @@ -17,6 +17,10 @@ import net.mamoe.mirai.message.data.Message import net.mamoe.mirai.message.data.MessageChain import net.mamoe.mirai.message.data.MessageChain.Companion.deserializeFromMiraiCode import net.mamoe.mirai.utils.safeCast +import kotlin.jvm.JvmName +import kotlin.jvm.JvmOverloads +import kotlin.jvm.JvmStatic +import kotlin.jvm.JvmSynthetic /** * Mirai 码相关操作. diff --git a/mirai-core-api/src/commonMain/kotlin/message/data/At.kt b/mirai-core-api/src/commonMain/kotlin/message/data/At.kt index 31e4c7a3d..491c618a2 100644 --- a/mirai-core-api/src/commonMain/kotlin/message/data/At.kt +++ b/mirai-core-api/src/commonMain/kotlin/message/data/At.kt @@ -24,6 +24,9 @@ import net.mamoe.mirai.message.code.CodableMessage import net.mamoe.mirai.message.data.visitor.MessageVisitor import net.mamoe.mirai.utils.MiraiExperimentalApi import net.mamoe.mirai.utils.MiraiInternalApi +import kotlin.jvm.JvmMultifileClass +import kotlin.jvm.JvmName +import kotlin.jvm.JvmSynthetic /** diff --git a/mirai-core-api/src/commonMain/kotlin/message/data/AtAll.kt b/mirai-core-api/src/commonMain/kotlin/message/data/AtAll.kt index 369d7da04..5f0537159 100644 --- a/mirai-core-api/src/commonMain/kotlin/message/data/AtAll.kt +++ b/mirai-core-api/src/commonMain/kotlin/message/data/AtAll.kt @@ -19,6 +19,9 @@ import net.mamoe.mirai.message.code.CodableMessage import net.mamoe.mirai.message.data.visitor.MessageVisitor import net.mamoe.mirai.utils.MiraiExperimentalApi import net.mamoe.mirai.utils.MiraiInternalApi +import kotlin.jvm.JvmMultifileClass +import kotlin.jvm.JvmName +import kotlin.jvm.JvmSynthetic /** * "@全体成员". diff --git a/mirai-core-api/src/commonMain/kotlin/message/data/Audio.kt b/mirai-core-api/src/commonMain/kotlin/message/data/Audio.kt index d597368c2..0a86e1541 100644 --- a/mirai-core-api/src/commonMain/kotlin/message/data/Audio.kt +++ b/mirai-core-api/src/commonMain/kotlin/message/data/Audio.kt @@ -22,6 +22,10 @@ import net.mamoe.mirai.message.MessageSerializers import net.mamoe.mirai.message.data.MessageChain.Companion.serializeToJsonString import net.mamoe.mirai.message.data.visitor.MessageVisitor import net.mamoe.mirai.utils.* +import kotlin.jvm.JvmMultifileClass +import kotlin.jvm.JvmName +import kotlin.jvm.JvmStatic +import kotlin.jvm.JvmSynthetic import kotlin.time.Duration import kotlin.time.Duration.Companion.seconds diff --git a/mirai-core-api/src/commonMain/kotlin/message/data/ConstrainSingle.kt b/mirai-core-api/src/commonMain/kotlin/message/data/ConstrainSingle.kt index 8de0e4798..3dfe141a8 100644 --- a/mirai-core-api/src/commonMain/kotlin/message/data/ConstrainSingle.kt +++ b/mirai-core-api/src/commonMain/kotlin/message/data/ConstrainSingle.kt @@ -1,22 +1,20 @@ /* - * Copyright 2019-2021 Mamoe Technologies and contributors. + * Copyright 2019-2022 Mamoe Technologies and contributors. * - * 此源代码的使用受 GNU AFFERO GENERAL PUBLIC LICENSE version 3 许可证的约束, 可以在以下链接找到该许可证. - * Use of this source code is governed by the GNU AGPLv3 license that can be found through the following link. + * 此源代码的使用受 GNU AFFERO GENERAL PUBLIC LICENSE version 3 许可证的约束, 可以在以下链接找到该许可证. + * Use of this source code is governed by the GNU AGPLv3 license that can be found through the following link. * - * https://github.com/mamoe/mirai/blob/master/LICENSE + * https://github.com/mamoe/mirai/blob/dev/LICENSE */ -@file:Suppress( - "MemberVisibilityCanBePrivate", "unused", "EXPERIMENTAL_API_USAGE", - "NOTHING_TO_INLINE", "INVISIBLE_MEMBER", "INVISIBLE_REFERENCE", - "INAPPLICABLE_JVM_NAME" -) @file:JvmMultifileClass @file:JvmName("MessageUtils") package net.mamoe.mirai.message.data +import kotlin.jvm.JvmMultifileClass +import kotlin.jvm.JvmName + /** * 约束一个 [MessageChain] 中只存在这一种类型的元素. 新元素将会替换旧元素, 保持原顺序. * diff --git a/mirai-core-api/src/commonMain/kotlin/message/data/CustomMessage.kt b/mirai-core-api/src/commonMain/kotlin/message/data/CustomMessage.kt index c25b2259a..3b245ecdd 100644 --- a/mirai-core-api/src/commonMain/kotlin/message/data/CustomMessage.kt +++ b/mirai-core-api/src/commonMain/kotlin/message/data/CustomMessage.kt @@ -7,8 +7,6 @@ * https://github.com/mamoe/mirai/blob/dev/LICENSE */ -@file:OptIn(MiraiInternalApi::class) - package net.mamoe.mirai.message.data import io.ktor.utils.io.core.* @@ -19,9 +17,9 @@ import kotlinx.serialization.protobuf.ProtoBuf import kotlinx.serialization.protobuf.ProtoNumber import net.mamoe.mirai.message.MessageSerializers import net.mamoe.mirai.message.data.visitor.MessageVisitor +import net.mamoe.mirai.utils.ConcurrentLinkedDeque import net.mamoe.mirai.utils.MiraiExperimentalApi import net.mamoe.mirai.utils.MiraiInternalApi -import java.util.concurrent.ConcurrentLinkedQueue /** * 自定义消息 @@ -117,7 +115,7 @@ public sealed class CustomMessage : SingleMessage { } public companion object { - private val factories: ConcurrentLinkedQueue> = ConcurrentLinkedQueue() + private val factories: MutableCollection> = ConcurrentLinkedDeque() internal fun register(factory: Factory) { factories.removeAll { it::class == factory::class } diff --git a/mirai-core-api/src/commonMain/kotlin/message/data/Deprecated.kt b/mirai-core-api/src/commonMain/kotlin/message/data/Deprecated.kt index e1fba3b6f..d0358d6a7 100644 --- a/mirai-core-api/src/commonMain/kotlin/message/data/Deprecated.kt +++ b/mirai-core-api/src/commonMain/kotlin/message/data/Deprecated.kt @@ -1,10 +1,10 @@ /* - * Copyright 2019-2021 Mamoe Technologies and contributors. + * Copyright 2019-2022 Mamoe Technologies and contributors. * - * 此源代码的使用受 GNU AFFERO GENERAL PUBLIC LICENSE version 3 许可证的约束, 可以在以下链接找到该许可证. - * Use of this source code is governed by the GNU AGPLv3 license that can be found through the following link. + * 此源代码的使用受 GNU AFFERO GENERAL PUBLIC LICENSE version 3 许可证的约束, 可以在以下链接找到该许可证. + * Use of this source code is governed by the GNU AGPLv3 license that can be found through the following link. * - * https://github.com/mamoe/mirai/blob/master/LICENSE + * https://github.com/mamoe/mirai/blob/dev/LICENSE */ @@ -19,7 +19,10 @@ import kotlinx.serialization.SerialName import kotlinx.serialization.Serializable import net.mamoe.mirai.IMirai import net.mamoe.mirai.utils.DeprecatedSinceMirai +import net.mamoe.mirai.utils.isSameClass import net.mamoe.mirai.utils.safeCast +import kotlin.jvm.JvmMultifileClass +import kotlin.jvm.JvmName /** @@ -81,10 +84,7 @@ constructor( override fun equals(other: Any?): Boolean { if (this === other) return true - if (javaClass != other?.javaClass) return false - - @Suppress("DEPRECATION_ERROR") - other as RichMessageOrigin + if (other !is RichMessageOrigin || !isSameClass(this, other)) return false if (origin != other.origin) return false if (resourceId != other.resourceId) return false diff --git a/mirai-core-api/src/commonMain/kotlin/message/data/Dice.kt b/mirai-core-api/src/commonMain/kotlin/message/data/Dice.kt index 2fc054da8..5d9d739d9 100644 --- a/mirai-core-api/src/commonMain/kotlin/message/data/Dice.kt +++ b/mirai-core-api/src/commonMain/kotlin/message/data/Dice.kt @@ -19,8 +19,11 @@ import net.mamoe.mirai.message.code.CodableMessage import net.mamoe.mirai.message.data.visitor.MessageVisitor import net.mamoe.mirai.utils.MiraiExperimentalApi import net.mamoe.mirai.utils.MiraiInternalApi +import net.mamoe.mirai.utils.annotations.Range import net.mamoe.mirai.utils.safeCast -import org.jetbrains.annotations.Range +import kotlin.jvm.JvmMultifileClass +import kotlin.jvm.JvmName +import kotlin.jvm.JvmStatic import kotlin.random.Random import kotlin.random.nextInt diff --git a/mirai-core-api/src/commonMain/kotlin/message/data/Face.kt b/mirai-core-api/src/commonMain/kotlin/message/data/Face.kt index f07a53c7a..ee0e34de9 100644 --- a/mirai-core-api/src/commonMain/kotlin/message/data/Face.kt +++ b/mirai-core-api/src/commonMain/kotlin/message/data/Face.kt @@ -19,6 +19,9 @@ import net.mamoe.mirai.message.code.CodableMessage import net.mamoe.mirai.message.data.visitor.MessageVisitor import net.mamoe.mirai.utils.MiraiExperimentalApi import net.mamoe.mirai.utils.MiraiInternalApi +import kotlin.jvm.JvmField +import kotlin.jvm.JvmMultifileClass +import kotlin.jvm.JvmName /** * QQ 自带表情 diff --git a/mirai-core-api/src/commonMain/kotlin/message/data/FileMessage.kt b/mirai-core-api/src/commonMain/kotlin/message/data/FileMessage.kt index a00b84777..8e30fa56b 100644 --- a/mirai-core-api/src/commonMain/kotlin/message/data/FileMessage.kt +++ b/mirai-core-api/src/commonMain/kotlin/message/data/FileMessage.kt @@ -25,6 +25,10 @@ import net.mamoe.mirai.message.code.CodableMessage import net.mamoe.mirai.message.code.internal.appendStringAsMiraiCode import net.mamoe.mirai.message.data.visitor.MessageVisitor import net.mamoe.mirai.utils.* +import kotlin.jvm.JvmMultifileClass +import kotlin.jvm.JvmName +import kotlin.jvm.JvmStatic +import kotlin.jvm.JvmSynthetic /** * 文件消息. diff --git a/mirai-core-api/src/commonMain/kotlin/message/data/FlashImage.kt b/mirai-core-api/src/commonMain/kotlin/message/data/FlashImage.kt index cba5303fb..7b06a9938 100644 --- a/mirai-core-api/src/commonMain/kotlin/message/data/FlashImage.kt +++ b/mirai-core-api/src/commonMain/kotlin/message/data/FlashImage.kt @@ -18,6 +18,8 @@ import net.mamoe.mirai.message.data.visitor.MessageVisitor import net.mamoe.mirai.utils.MiraiExperimentalApi import net.mamoe.mirai.utils.MiraiInternalApi import net.mamoe.mirai.utils.safeCast +import kotlin.jvm.JvmStatic +import kotlin.jvm.JvmSynthetic /** * 闪照. 闪照的内容取决于 [image] 代表的图片. diff --git a/mirai-core-api/src/commonMain/kotlin/message/data/ForwardMessage.kt b/mirai-core-api/src/commonMain/kotlin/message/data/ForwardMessage.kt index 74808af1a..6595e738c 100644 --- a/mirai-core-api/src/commonMain/kotlin/message/data/ForwardMessage.kt +++ b/mirai-core-api/src/commonMain/kotlin/message/data/ForwardMessage.kt @@ -19,7 +19,8 @@ import net.mamoe.mirai.event.events.MessageEvent import net.mamoe.mirai.message.data.ForwardMessage.DisplayStrategy import net.mamoe.mirai.message.data.visitor.MessageVisitor import net.mamoe.mirai.utils.* - +import kotlin.jvm.JvmOverloads +import kotlin.jvm.JvmSynthetic /** * 未通过 [DisplayStrategy] 渲染的合并转发消息. [RawForwardMessage] 仅作为一个中间件, 用于 [ForwardMessageBuilder]. diff --git a/mirai-core-api/src/commonMain/kotlin/message/data/HummerMessage.kt b/mirai-core-api/src/commonMain/kotlin/message/data/HummerMessage.kt index 7b1d24579..5c56fe61a 100644 --- a/mirai-core-api/src/commonMain/kotlin/message/data/HummerMessage.kt +++ b/mirai-core-api/src/commonMain/kotlin/message/data/HummerMessage.kt @@ -18,6 +18,8 @@ import net.mamoe.mirai.utils.MiraiExperimentalApi import net.mamoe.mirai.utils.MiraiInternalApi import net.mamoe.mirai.utils.NotStableForInheritance import net.mamoe.mirai.utils.castOrNull +import kotlin.jvm.JvmMultifileClass +import kotlin.jvm.JvmName /** * 一些特殊的消息 diff --git a/mirai-core-api/src/commonMain/kotlin/message/data/Message.kt b/mirai-core-api/src/commonMain/kotlin/message/data/Message.kt index c8d4ed6af..d222b51ef 100644 --- a/mirai-core-api/src/commonMain/kotlin/message/data/Message.kt +++ b/mirai-core-api/src/commonMain/kotlin/message/data/Message.kt @@ -7,13 +7,9 @@ * https://github.com/mamoe/mirai/blob/dev/LICENSE */ -@file:Suppress( - "MemberVisibilityCanBePrivate", "unused", "EXPERIMENTAL_API_USAGE", - "NOTHING_TO_INLINE", "INVISIBLE_MEMBER", "INVISIBLE_REFERENCE", - "INAPPLICABLE_JVM_NAME" -) @file:JvmMultifileClass @file:JvmName("MessageUtils") +@file:Suppress("NOTHING_TO_INLINE") package net.mamoe.mirai.message.data @@ -27,7 +23,9 @@ import net.mamoe.mirai.message.code.MiraiCode.serializeToMiraiCode import net.mamoe.mirai.message.data.MessageChain.Companion.serializeToJsonString import net.mamoe.mirai.message.data.visitor.MessageVisitor import net.mamoe.mirai.utils.MiraiInternalApi -import kotlin.internal.LowPriorityInOverloadResolution +import kotlin.jvm.JvmMultifileClass +import kotlin.jvm.JvmName +import kotlin.jvm.JvmSynthetic /** * 可发送的或从服务器接收的消息. @@ -200,7 +198,8 @@ public interface Message { * * @param ignoreCase 为 `true` 时忽略大小写 */ - @LowPriorityInOverloadResolution + @kotlin.internal.LowPriorityInOverloadResolution + @Suppress("INVISIBLE_MEMBER", "INVISIBLE_REFERENCE") public fun contentEquals(another: Message, ignoreCase: Boolean = false): Boolean = contentEquals(another, ignoreCase, false) @@ -281,6 +280,7 @@ public interface Message { /** 将 [another] 按顺序连接到这个消息的尾部. */ @JvmName("plusIterableString") + @Suppress("INAPPLICABLE_JVM_NAME") public operator fun plus(another: Iterable): MessageChain = another.fold(this, Message::plus).toMessageChain() @@ -360,7 +360,7 @@ public inline fun Message.repeat(count: Int): MessageChain { return this.toMessageChain() } return buildMessageChain(count) { - repeat(count) { + repeat(count) l@{ add(this@repeat) } } diff --git a/mirai-core-api/src/commonMain/kotlin/message/data/MessageChain.kt b/mirai-core-api/src/commonMain/kotlin/message/data/MessageChain.kt index 9add258f8..85a5d82e3 100644 --- a/mirai-core-api/src/commonMain/kotlin/message/data/MessageChain.kt +++ b/mirai-core-api/src/commonMain/kotlin/message/data/MessageChain.kt @@ -34,9 +34,8 @@ import net.mamoe.mirai.message.data.MessageSource.Key.recall import net.mamoe.mirai.message.data.MessageSource.Key.recallIn import net.mamoe.mirai.message.data.visitor.MessageVisitor import net.mamoe.mirai.utils.* -import java.util.stream.Stream +import kotlin.jvm.* import kotlin.reflect.KProperty -import kotlin.streams.asSequence import net.mamoe.mirai.console.compiler.common.ResolveContext.Kind.RESTRICTED_ABSTRACT_MESSAGE_KEYS as RAMK /** @@ -492,12 +491,6 @@ public inline fun messageChainOf(vararg messages: Message): MessageChain = messa public fun Sequence.toMessageChain(): MessageChain = LinearMessageChainImpl.create(ConstrainSingleHelper.constrainSingleMessages(this)) -/** - * 扁平化 [this] 并创建一个 [MessageChain]. - */ -@JvmName("newChain") -public fun Stream.toMessageChain(): MessageChain = this.asSequence().toMessageChain() - /** * 扁平化 [this] 并创建一个 [MessageChain]. */ diff --git a/mirai-core-api/src/commonMain/kotlin/message/data/MessageChainBuilder.kt b/mirai-core-api/src/commonMain/kotlin/message/data/MessageChainBuilder.kt index e7c14d420..2b89798fe 100644 --- a/mirai-core-api/src/commonMain/kotlin/message/data/MessageChainBuilder.kt +++ b/mirai-core-api/src/commonMain/kotlin/message/data/MessageChainBuilder.kt @@ -15,6 +15,9 @@ package net.mamoe.mirai.message.data import kotlin.contracts.InvocationKind.EXACTLY_ONCE import kotlin.contracts.contract +import kotlin.jvm.JvmMultifileClass +import kotlin.jvm.JvmName +import kotlin.jvm.JvmSynthetic /** * 构建一个 [MessageChain]. 用法查看 [MessageChainBuilder]. diff --git a/mirai-core-api/src/commonMain/kotlin/message/data/MessageKey.kt b/mirai-core-api/src/commonMain/kotlin/message/data/MessageKey.kt index 580f56770..915a46bb5 100644 --- a/mirai-core-api/src/commonMain/kotlin/message/data/MessageKey.kt +++ b/mirai-core-api/src/commonMain/kotlin/message/data/MessageKey.kt @@ -1,14 +1,16 @@ /* - * Copyright 2019-2021 Mamoe Technologies and contributors. + * Copyright 2019-2022 Mamoe Technologies and contributors. * - * 此源代码的使用受 GNU AFFERO GENERAL PUBLIC LICENSE version 3 许可证的约束, 可以在以下链接找到该许可证. - * Use of this source code is governed by the GNU AGPLv3 license that can be found through the following link. + * 此源代码的使用受 GNU AFFERO GENERAL PUBLIC LICENSE version 3 许可证的约束, 可以在以下链接找到该许可证. + * Use of this source code is governed by the GNU AGPLv3 license that can be found through the following link. * - * https://github.com/mamoe/mirai/blob/master/LICENSE + * https://github.com/mamoe/mirai/blob/dev/LICENSE */ package net.mamoe.mirai.message.data +import kotlin.jvm.JvmField + /** * 类型 Key. 由伴生对象实现, 表示一个 [Message] 对象的类型. * diff --git a/mirai-core-api/src/commonMain/kotlin/message/data/MessageOrigin.kt b/mirai-core-api/src/commonMain/kotlin/message/data/MessageOrigin.kt index 1ebd0533f..a8868ff17 100644 --- a/mirai-core-api/src/commonMain/kotlin/message/data/MessageOrigin.kt +++ b/mirai-core-api/src/commonMain/kotlin/message/data/MessageOrigin.kt @@ -18,6 +18,7 @@ import net.mamoe.mirai.IMirai import net.mamoe.mirai.message.data.visitor.MessageVisitor import net.mamoe.mirai.utils.MiraiExperimentalApi import net.mamoe.mirai.utils.MiraiInternalApi +import net.mamoe.mirai.utils.isSameClass import net.mamoe.mirai.utils.safeCast /** @@ -71,9 +72,7 @@ public class MessageOrigin( override fun equals(other: Any?): Boolean { if (this === other) return true - if (javaClass != other?.javaClass) return false - - other as MessageOrigin + if (other !is MessageOrigin || !isSameClass(this, other)) return false if (origin != other.origin) return false if (resourceId != other.resourceId) return false diff --git a/mirai-core-api/src/commonMain/kotlin/message/data/MessageSource.kt b/mirai-core-api/src/commonMain/kotlin/message/data/MessageSource.kt index 7b83d3380..6fd70f969 100644 --- a/mirai-core-api/src/commonMain/kotlin/message/data/MessageSource.kt +++ b/mirai-core-api/src/commonMain/kotlin/message/data/MessageSource.kt @@ -32,6 +32,10 @@ import net.mamoe.mirai.message.data.visitor.MessageVisitor import net.mamoe.mirai.utils.MiraiInternalApi import net.mamoe.mirai.utils.NotStableForInheritance import net.mamoe.mirai.utils.safeCast +import kotlin.jvm.JvmMultifileClass +import kotlin.jvm.JvmName +import kotlin.jvm.JvmStatic +import kotlin.jvm.JvmSynthetic /** * 消息源. 消息源存在于 [MessageChain] 中, 用于表示这个消息的来源, 也可以用来分辨 [MessageChain]. diff --git a/mirai-core-api/src/commonMain/kotlin/message/data/MessageSourceBuilder.kt b/mirai-core-api/src/commonMain/kotlin/message/data/MessageSourceBuilder.kt index c62a72a70..8a0fdd3fc 100644 --- a/mirai-core-api/src/commonMain/kotlin/message/data/MessageSourceBuilder.kt +++ b/mirai-core-api/src/commonMain/kotlin/message/data/MessageSourceBuilder.kt @@ -1,10 +1,10 @@ /* - * Copyright 2019-2021 Mamoe Technologies and contributors. + * Copyright 2019-2022 Mamoe Technologies and contributors. * - * 此源代码的使用受 GNU AFFERO GENERAL PUBLIC LICENSE version 3 许可证的约束, 可以在以下链接找到该许可证. - * Use of this source code is governed by the GNU AGPLv3 license that can be found through the following link. + * 此源代码的使用受 GNU AFFERO GENERAL PUBLIC LICENSE version 3 许可证的约束, 可以在以下链接找到该许可证. + * Use of this source code is governed by the GNU AGPLv3 license that can be found through the following link. * - * https://github.com/mamoe/mirai/blob/master/LICENSE + * https://github.com/mamoe/mirai/blob/dev/LICENSE */ @file:JvmMultifileClass @@ -20,6 +20,9 @@ import net.mamoe.mirai.contact.ContactOrBot import net.mamoe.mirai.message.data.MessageSource.Key.quote import net.mamoe.mirai.message.data.MessageSource.Key.recall import net.mamoe.mirai.utils.currentTimeSeconds +import kotlin.jvm.JvmMultifileClass +import kotlin.jvm.JvmName +import kotlin.jvm.JvmSynthetic /** * 将在线消息源转换为离线消息源. diff --git a/mirai-core-api/src/commonMain/kotlin/message/data/PlainText.kt b/mirai-core-api/src/commonMain/kotlin/message/data/PlainText.kt index de68a1aeb..06891d496 100644 --- a/mirai-core-api/src/commonMain/kotlin/message/data/PlainText.kt +++ b/mirai-core-api/src/commonMain/kotlin/message/data/PlainText.kt @@ -20,6 +20,9 @@ import net.mamoe.mirai.message.code.internal.appendStringAsMiraiCode import net.mamoe.mirai.message.data.visitor.MessageVisitor import net.mamoe.mirai.utils.MiraiExperimentalApi import net.mamoe.mirai.utils.MiraiInternalApi +import kotlin.jvm.JvmMultifileClass +import kotlin.jvm.JvmName +import kotlin.jvm.JvmSynthetic /** * 纯文本. diff --git a/mirai-core-api/src/commonMain/kotlin/message/data/PokeMessage.kt b/mirai-core-api/src/commonMain/kotlin/message/data/PokeMessage.kt index dca31fdfd..3dc2482ce 100644 --- a/mirai-core-api/src/commonMain/kotlin/message/data/PokeMessage.kt +++ b/mirai-core-api/src/commonMain/kotlin/message/data/PokeMessage.kt @@ -18,6 +18,7 @@ import net.mamoe.mirai.message.data.visitor.MessageVisitor import net.mamoe.mirai.utils.MiraiExperimentalApi import net.mamoe.mirai.utils.MiraiInternalApi import net.mamoe.mirai.utils.castOrNull +import kotlin.jvm.JvmField /** * 戳一戳. 可以发送给好友或群. diff --git a/mirai-core-api/src/commonMain/kotlin/message/data/QuoteReply.kt b/mirai-core-api/src/commonMain/kotlin/message/data/QuoteReply.kt index 22aaa73ff..82fb97681 100644 --- a/mirai-core-api/src/commonMain/kotlin/message/data/QuoteReply.kt +++ b/mirai-core-api/src/commonMain/kotlin/message/data/QuoteReply.kt @@ -20,6 +20,9 @@ import net.mamoe.mirai.message.data.MessageSource.Key.recall import net.mamoe.mirai.message.data.visitor.MessageVisitor import net.mamoe.mirai.utils.MiraiInternalApi import net.mamoe.mirai.utils.safeCast +import kotlin.jvm.JvmMultifileClass +import kotlin.jvm.JvmName +import kotlin.jvm.JvmSynthetic /** diff --git a/mirai-core-api/src/commonMain/kotlin/message/data/RichMessage.kt b/mirai-core-api/src/commonMain/kotlin/message/data/RichMessage.kt index 63012a6f9..f9dedbb3a 100644 --- a/mirai-core-api/src/commonMain/kotlin/message/data/RichMessage.kt +++ b/mirai-core-api/src/commonMain/kotlin/message/data/RichMessage.kt @@ -23,6 +23,7 @@ import net.mamoe.mirai.utils.MiraiInternalApi import net.mamoe.mirai.utils.NotStableForInheritance import net.mamoe.mirai.utils.safeCast import kotlin.annotation.AnnotationTarget.* +import kotlin.jvm.* /** * XML, JSON 消息等富文本消息. diff --git a/mirai-core-api/src/commonMain/kotlin/message/data/SingleMessage.kt b/mirai-core-api/src/commonMain/kotlin/message/data/SingleMessage.kt index da96631d1..564d4d693 100644 --- a/mirai-core-api/src/commonMain/kotlin/message/data/SingleMessage.kt +++ b/mirai-core-api/src/commonMain/kotlin/message/data/SingleMessage.kt @@ -7,11 +7,6 @@ * https://github.com/mamoe/mirai/blob/dev/LICENSE */ -@file:Suppress( - "MemberVisibilityCanBePrivate", "unused", "EXPERIMENTAL_API_USAGE", - "NOTHING_TO_INLINE", "INVISIBLE_MEMBER", "INVISIBLE_REFERENCE", - "INAPPLICABLE_JVM_NAME" -) @file:JvmMultifileClass @file:JvmName("MessageUtils") @@ -23,6 +18,8 @@ import net.mamoe.mirai.message.data.visitor.MessageVisitor import net.mamoe.mirai.utils.DeprecatedSinceMirai import net.mamoe.mirai.utils.MiraiInternalApi import net.mamoe.mirai.utils.safeCast +import kotlin.jvm.JvmMultifileClass +import kotlin.jvm.JvmName /** * 单个消息元素. 与之相对的是 [MessageChain], 是多个 [SingleMessage] 的集合. diff --git a/mirai-core-api/src/commonMain/kotlin/message/data/UnsupportedMessage.kt b/mirai-core-api/src/commonMain/kotlin/message/data/UnsupportedMessage.kt index ac9ada537..9f82234fb 100644 --- a/mirai-core-api/src/commonMain/kotlin/message/data/UnsupportedMessage.kt +++ b/mirai-core-api/src/commonMain/kotlin/message/data/UnsupportedMessage.kt @@ -21,6 +21,10 @@ import net.mamoe.mirai.IMirai import net.mamoe.mirai.Mirai import net.mamoe.mirai.message.data.visitor.MessageVisitor import net.mamoe.mirai.utils.* +import kotlin.jvm.JvmMultifileClass +import kotlin.jvm.JvmName +import kotlin.jvm.JvmStatic +import kotlin.jvm.JvmSynthetic /** * mirai 尚未支持的消息类型. diff --git a/mirai-core-api/src/commonMain/kotlin/message/data/VipFace.kt b/mirai-core-api/src/commonMain/kotlin/message/data/VipFace.kt index b07d2b357..2d3c774bb 100644 --- a/mirai-core-api/src/commonMain/kotlin/message/data/VipFace.kt +++ b/mirai-core-api/src/commonMain/kotlin/message/data/VipFace.kt @@ -19,6 +19,7 @@ import net.mamoe.mirai.message.data.visitor.MessageVisitor import net.mamoe.mirai.utils.MiraiExperimentalApi import net.mamoe.mirai.utils.MiraiInternalApi import net.mamoe.mirai.utils.safeCast +import kotlin.jvm.JvmField /** * VIP 表情. diff --git a/mirai-core-api/src/commonMain/kotlin/message/data/Voice.kt b/mirai-core-api/src/commonMain/kotlin/message/data/Voice.kt index 40182bf93..f3db81a6a 100644 --- a/mirai-core-api/src/commonMain/kotlin/message/data/Voice.kt +++ b/mirai-core-api/src/commonMain/kotlin/message/data/Voice.kt @@ -19,6 +19,10 @@ import kotlinx.serialization.Transient import net.mamoe.mirai.contact.Group import net.mamoe.mirai.message.data.visitor.MessageVisitor import net.mamoe.mirai.utils.* +import kotlin.jvm.JvmMultifileClass +import kotlin.jvm.JvmName +import kotlin.jvm.JvmStatic +import kotlin.jvm.JvmSynthetic /** diff --git a/mirai-core-api/src/commonMain/kotlin/message/data/impl.kt b/mirai-core-api/src/commonMain/kotlin/message/data/impl.kt index fe9da6069..226ef6fe4 100644 --- a/mirai-core-api/src/commonMain/kotlin/message/data/impl.kt +++ b/mirai-core-api/src/commonMain/kotlin/message/data/impl.kt @@ -19,8 +19,13 @@ import net.mamoe.mirai.message.data.Image.Key.IMAGE_RESOURCE_ID_REGEX_1 import net.mamoe.mirai.message.data.Image.Key.IMAGE_RESOURCE_ID_REGEX_2 import net.mamoe.mirai.message.data.visitor.MessageVisitor import net.mamoe.mirai.utils.MiraiInternalApi +import net.mamoe.mirai.utils.asImmutable import net.mamoe.mirai.utils.castOrNull import net.mamoe.mirai.utils.replaceAllKotlin +import kotlin.jvm.JvmField +import kotlin.jvm.JvmMultifileClass +import kotlin.jvm.JvmName +import kotlin.jvm.JvmSynthetic // region image @@ -111,7 +116,7 @@ internal object ConstrainSingleHelper { } } - return ConstrainSingleData(list.filterNotNull().asImmutable(), hasConstrainSingle) + return ConstrainSingleData(list.filterNotNull(), hasConstrainSingle) } } @@ -126,6 +131,9 @@ internal annotation class MessageChainConstructor @Suppress("SERIALIZER_TYPE_INCOMPATIBLE") @Serializable(MessageChain.Serializer::class) internal class LinearMessageChainImpl @MessageChainConstructor private constructor( + /** + * Must be guaranteed to be immutable + */ @JvmField internal val delegate: List, override val hasConstrainSingle: Boolean @@ -207,7 +215,7 @@ internal class LinearMessageChainImpl @MessageChainConstructor private construct return if (delegate.isEmpty()) { emptyMessageChain() } else { - LinearMessageChainImpl(delegate, hasConstrainSingle) + LinearMessageChainImpl(delegate.asImmutable(), hasConstrainSingle) } } diff --git a/mirai-core-api/src/commonMain/kotlin/message/utils.kt b/mirai-core-api/src/commonMain/kotlin/message/utils.kt index a772d3775..a84db391b 100644 --- a/mirai-core-api/src/commonMain/kotlin/message/utils.kt +++ b/mirai-core-api/src/commonMain/kotlin/message/utils.kt @@ -1,10 +1,10 @@ /* - * Copyright 2019-2021 Mamoe Technologies and contributors. + * Copyright 2019-2022 Mamoe Technologies and contributors. * - * 此源代码的使用受 GNU AFFERO GENERAL PUBLIC LICENSE version 3 许可证的约束, 可以在以下链接找到该许可证. - * Use of this source code is governed by the GNU AGPLv3 license that can be found through the following link. + * 此源代码的使用受 GNU AFFERO GENERAL PUBLIC LICENSE version 3 许可证的约束, 可以在以下链接找到该许可证. + * Use of this source code is governed by the GNU AGPLv3 license that can be found through the following link. * - * https://github.com/mamoe/mirai/blob/master/LICENSE + * https://github.com/mamoe/mirai/blob/dev/LICENSE */ @file:JvmMultifileClass @@ -25,6 +25,9 @@ import net.mamoe.mirai.event.syncFromEventOrNull import net.mamoe.mirai.message.data.MessageChain import kotlin.coroutines.CoroutineContext import kotlin.coroutines.EmptyCoroutineContext +import kotlin.jvm.JvmMultifileClass +import kotlin.jvm.JvmName +import kotlin.jvm.JvmSynthetic /** diff --git a/mirai-core-api/src/commonMain/kotlin/network/LoginFailedException.kt b/mirai-core-api/src/commonMain/kotlin/network/LoginFailedException.kt index f513aca65..ab08c87e7 100644 --- a/mirai-core-api/src/commonMain/kotlin/network/LoginFailedException.kt +++ b/mirai-core-api/src/commonMain/kotlin/network/LoginFailedException.kt @@ -1,5 +1,5 @@ /* - * Copyright 2019-2021 Mamoe Technologies and contributors. + * Copyright 2019-2022 Mamoe Technologies and contributors. * * 此源代码的使用受 GNU AFFERO GENERAL PUBLIC LICENSE version 3 许可证的约束, 可以在以下链接找到该许可证. * Use of this source code is governed by the GNU AGPLv3 license that can be found through the following link. @@ -45,7 +45,7 @@ public class NoServerAvailableException @MiraiInternalApi constructor( /** * 服务器要求稍后重试 */ -public class RetryLaterException @MiraiInternalApi constructor() : +public class RetryLaterException @MiraiInternalApi constructor(override val cause: Throwable? = null) : LoginFailedException(false, "server requests retrial later") /** diff --git a/mirai-core-api/src/commonMain/kotlin/utils/BotConfiguration.kt b/mirai-core-api/src/commonMain/kotlin/utils/BotConfiguration.kt index 44038813e..8164a2778 100644 --- a/mirai-core-api/src/commonMain/kotlin/utils/BotConfiguration.kt +++ b/mirai-core-api/src/commonMain/kotlin/utils/BotConfiguration.kt @@ -139,7 +139,7 @@ public expect open class BotConfiguration() { // open for Java * 心跳策略. * @since 2.6.3 */ - public enum class HeartbeatStrategy { + public enum class HeartbeatStrategy { // IN ACTUAL DECLARATION DO NOT ADD EXTRA ELEMENTS. /** * 使用 2.6.0 增加的*状态心跳* (Stat Heartbeat). 通常推荐这个模式. * diff --git a/mirai-core-api/src/commonMain/kotlin/utils/DeviceInfo.kt b/mirai-core-api/src/commonMain/kotlin/utils/DeviceInfo.kt index 8d62278a2..18eab00b0 100644 --- a/mirai-core-api/src/commonMain/kotlin/utils/DeviceInfo.kt +++ b/mirai-core-api/src/commonMain/kotlin/utils/DeviceInfo.kt @@ -181,9 +181,7 @@ internal object DeviceInfoCommonImpl { @Suppress("DuplicatedCode") fun equalsImpl(deviceInfo: DeviceInfo, other: Any?): Boolean = deviceInfo.run { if (deviceInfo === other) return true - if (other !is DeviceInfo) return false - - other as DeviceInfo + if (!isSameType(this, other)) return false if (!display.contentEquals(other.display)) return false if (!product.contentEquals(other.product)) return false diff --git a/mirai-core-api/src/commonMain/kotlin/utils/ExternalResource.kt b/mirai-core-api/src/commonMain/kotlin/utils/ExternalResource.kt index 36cdc4a52..8c3b14e94 100644 --- a/mirai-core-api/src/commonMain/kotlin/utils/ExternalResource.kt +++ b/mirai-core-api/src/commonMain/kotlin/utils/ExternalResource.kt @@ -216,7 +216,8 @@ public expect interface ExternalResource : Closeable { * * @see ExternalResource.formatName */ - public val DEFAULT_FORMAT_NAME: String + @Suppress("CONST_VAL_WITHOUT_INITIALIZER") // compile bug + public const val DEFAULT_FORMAT_NAME: String /////////////////////////////////////////////////////////////////////////// // region toExternalResource diff --git a/mirai-core-api/src/commonMain/kotlin/utils/FileCacheStrategy.kt b/mirai-core-api/src/commonMain/kotlin/utils/FileCacheStrategy.kt index 825a76aa9..355a9dc71 100644 --- a/mirai-core-api/src/commonMain/kotlin/utils/FileCacheStrategy.kt +++ b/mirai-core-api/src/commonMain/kotlin/utils/FileCacheStrategy.kt @@ -12,17 +12,12 @@ package net.mamoe.mirai.utils import io.ktor.utils.io.errors.* -import kotlinx.coroutines.Dispatchers import net.mamoe.mirai.Bot import net.mamoe.mirai.IMirai import net.mamoe.mirai.utils.ExternalResource.Companion.sendAsImageTo import net.mamoe.mirai.utils.ExternalResource.Companion.toExternalResource import net.mamoe.mirai.utils.ExternalResource.Companion.uploadAsImage -import net.mamoe.mirai.utils.FileCacheStrategy.MemoryCache -import net.mamoe.mirai.utils.FileCacheStrategy.TempCache -import java.io.File -import java.io.InputStream -import kotlin.jvm.JvmOverloads +import kotlin.jvm.JvmStatic /** * 资源缓存策略. @@ -53,66 +48,7 @@ import kotlin.jvm.JvmOverloads * * @see ExternalResource */ -public interface FileCacheStrategy { - /** - * 立即读取 [input] 所有内容并缓存为 [ExternalResource]. - * - * 注意: - * - 此函数不会关闭输入 - * - 此函数可能会阻塞线程读取 [input] 内容, 若在 Kotlin 协程使用请确保在允许阻塞的环境 ([Dispatchers.IO]). - * - * @param formatName 文件类型. 此参数通常只会影响官方客户端接收到的文件的文件后缀. 若为 `null` 则会自动根据文件头识别. 识别失败时将使用 "mirai" - */ - @Throws(IOException::class) - public fun newCache(input: InputStream, formatName: String? = null): ExternalResource - - /** - * 立即读取 [input] 所有内容并缓存为 [ExternalResource]. 自动根据文件头识别文件类型. 识别失败时将使用 "mirai". - * - * 注意: - * - 此函数不会关闭输入 - * - 此函数可能会阻塞线程读取 [input] 内容, 若在 Kotlin 协程使用请确保在允许阻塞的环境 ([Dispatchers.IO]). - */ - @Throws(IOException::class) - public fun newCache(input: InputStream): ExternalResource = newCache(input, null) - - /** - * 使用内存直接存储所有图片文件. 由 JVM 执行 GC. - */ - public object MemoryCache : FileCacheStrategy { - @Throws(IOException::class) - override fun newCache(input: InputStream, formatName: String?): ExternalResource { - return input.readBytes().toExternalResource(formatName) - } - } - - /** - * 使用系统临时文件夹缓存图片文件. 在图片使用完毕后或 JVM 正常结束时删除临时文件. - */ - public class TempCache @JvmOverloads public constructor( - /** - * 缓存图片存放位置. 为 `null` 时使用主机系统的临时文件夹: `File.createTempFile("tmp", null, directory)` - */ - public val directory: File? = null, - ) : FileCacheStrategy { - private fun createTempFile(): File { - return File.createTempFile("tmp", null, directory) - } - - @Throws(IOException::class) - override fun newCache(input: InputStream, formatName: String?): ExternalResource { - val file = createTempFile() - return file.apply { - deleteOnExit() - outputStream().use { out -> input.copyTo(out) } - }.toExternalResource(formatName).apply { - closed.invokeOnCompletion { - kotlin.runCatching { file.delete() } - } - } - } - } - +public expect interface FileCacheStrategy { public companion object { /** * 当前平台下默认的缓存策略. 注意, 这可能不是 Mirai 全局默认使用的, Mirai 从 [IMirai.FileCacheStrategy] 获取. @@ -121,6 +57,6 @@ public interface FileCacheStrategy { */ @MiraiExperimentalApi @JvmStatic - public val PlatformDefault: FileCacheStrategy = TempCache(null) + public val PlatformDefault: FileCacheStrategy } } diff --git a/mirai-core-api/src/commonMain/kotlin/utils/MiraiLogger.kt b/mirai-core-api/src/commonMain/kotlin/utils/MiraiLogger.kt index b234db795..99919ccc9 100644 --- a/mirai-core-api/src/commonMain/kotlin/utils/MiraiLogger.kt +++ b/mirai-core-api/src/commonMain/kotlin/utils/MiraiLogger.kt @@ -15,7 +15,6 @@ package net.mamoe.mirai.utils import kotlin.jvm.JvmMultifileClass import kotlin.jvm.JvmName import kotlin.jvm.JvmOverloads -import kotlin.jvm.JvmStatic import kotlin.reflect.KClass /** @@ -31,12 +30,6 @@ public fun MiraiLogger.withSwitch(default: Boolean = true): MiraiLoggerWithSwitc * * Mirai 内建简单的日志系统, 即 [MiraiLogger]. [MiraiLogger] 的实现有 [SimpleLogger], [PlatformLogger], [SilentLogger]. * - * [MiraiLogger] 仅能处理简单的日志任务, 通常推荐使用 [SLF4J][org.slf4j.Logger], [LOG4J][org.apache.logging.log4j.Logger] 等日志库. - * - * ## 使用第三方日志库接管 Mirai 日志系统 - * - * 使用 [LoggerAdapters], 将第三方日志 `Logger` 转为 [MiraiLogger]. 然后通过 [MiraiLogger.setDefaultLoggerCreator] 全局覆盖日志. - * * ## 实现或使用 [MiraiLogger] * * 不建议实现或使用 [MiraiLogger]. 请优先考虑使用上述第三方框架. [MiraiLogger] 仅应用于兼容旧版本代码. @@ -44,7 +37,6 @@ public fun MiraiLogger.withSwitch(default: Boolean = true): MiraiLoggerWithSwitc * @see SimpleLogger 简易 logger, 它将所有的日志记录操作都转移给 lambda `(String?, Throwable?) -> Unit` * @see PlatformLogger 各个平台下的默认日志记录实现. * @see SilentLogger 忽略任何日志记录操作的 logger 实例. - * @see LoggerAdapters * * @see MiraiLoggerPlatformBase 平台通用基础实现. 若 Mirai 自带的日志系统无法满足需求, 请继承这个类并实现其抽象函数. */ @@ -74,44 +66,7 @@ public expect interface MiraiLogger { public companion object INSTANCE : Factory } - public companion object { - /** - * 顶层日志, 仅供 Mirai 内部使用. - */ - @MiraiInternalApi - @MiraiExperimentalApi - @Deprecated("Deprecated.", level = DeprecationLevel.HIDDEN) // deprecated since 2.7 - @DeprecatedSinceMirai(warningSince = "2.7", errorSince = "2.10", hiddenSince = "2.11") - public val TopLevel: MiraiLogger - - /** - * 已弃用, 请实现 service [net.mamoe.mirai.utils.MiraiLogger.Factory] 并以 [ServiceLoader] 支持的方式提供. - */ - @Suppress("DeprecatedCallableAddReplaceWith") - @Deprecated( - "Please set factory by providing an service of type net.mamoe.mirai.utils.MiraiLogger.Factory", - level = DeprecationLevel.ERROR - ) // deprecated since 2.7 - @JvmStatic - @DeprecatedSinceMirai(warningSince = "2.7", errorSince = "2.10") // left ERROR intentionally, for internal uses. - public fun setDefaultLoggerCreator(creator: (identity: String?) -> MiraiLogger) - - - /** - * 旧版本用于创建 [MiraiLogger]. 已弃用. 请使用 [MiraiLogger.Factory.INSTANCE.create]. - * - * @see setDefaultLoggerCreator - */ - @Deprecated( - "Please use MiraiLogger.Factory.create", ReplaceWith( - "MiraiLogger.Factory.create(YourClass::class, identity)", - "net.mamoe.mirai.utils.MiraiLogger" - ), level = DeprecationLevel.HIDDEN - ) // deprecated since 2.7 - @JvmStatic - @DeprecatedSinceMirai(warningSince = "2.7", errorSince = "2.10", hiddenSince = "2.11") - public fun create(identity: String?): MiraiLogger - } + public companion object; /** * 日志的标记. 在 Mirai 中, identity 可为 @@ -134,7 +89,6 @@ public expect interface MiraiLogger { * 当 VERBOSE 级别的日志启用时返回 `true`. * * 若 [isEnabled] 为 `false`, 返回 `false`. - * 在使用 [SLF4J][org.slf4j.Logger], [LOG4J][org.apache.logging.log4j.Logger] 或 [JUL][java.util.logging.Logger] 时返回真实配置值. * 其他情况下返回 [isEnabled] 的值. * * @since 2.7 @@ -145,7 +99,6 @@ public expect interface MiraiLogger { * 当 DEBUG 级别的日志启用时返回 `true` * * 若 [isEnabled] 为 `false`, 返回 `false`. - * 在使用 [SLF4J][org.slf4j.Logger], [LOG4J][org.apache.logging.log4j.Logger] 或 [JUL][java.util.logging.Logger] 时返回真实配置值. * 其他情况下返回 [isEnabled] 的值. * * @since 2.7 @@ -156,7 +109,6 @@ public expect interface MiraiLogger { * 当 INFO 级别的日志启用时返回 `true` * * 若 [isEnabled] 为 `false`, 返回 `false`. - * 在使用 [SLF4J][org.slf4j.Logger], [LOG4J][org.apache.logging.log4j.Logger] 或 [JUL][java.util.logging.Logger] 时返回真实配置值. * 其他情况下返回 [isEnabled] 的值. * * @since 2.7 @@ -167,7 +119,6 @@ public expect interface MiraiLogger { * 当 WARNING 级别的日志启用时返回 `true` * * 若 [isEnabled] 为 `false`, 返回 `false`. - * 在使用 [SLF4J][org.slf4j.Logger], [LOG4J][org.apache.logging.log4j.Logger] 或 [JUL][java.util.logging.Logger] 时返回真实配置值. * 其他情况下返回 [isEnabled] 的值. * * @since 2.7 @@ -178,35 +129,19 @@ public expect interface MiraiLogger { * 当 ERROR 级别的日志启用时返回 `true` * * 若 [isEnabled] 为 `false`, 返回 `false`. - * 在使用 [SLF4J][org.slf4j.Logger], [LOG4J][org.apache.logging.log4j.Logger] 或 [JUL][java.util.logging.Logger] 时返回真实配置值. * 其他情况下返回 [isEnabled] 的值. * * @since 2.7 */ public open val isErrorEnabled: Boolean - /** - * 随从. 在 this 中调用所有方法后都应继续往 [follower] 传递调用. - * [follower] 的存在可以让一次日志被多个日志记录器记录. - * - * 一般不建议直接修改这个属性. 请通过 [plus] 来连接两个日志记录器. - * 如: `val logger = bot.logger + MyLogger()` - * 当调用 `logger.info()` 时, `bot.logger` 会首先记录, `MyLogger` 会随后记录. - * - * 当然, 多个 logger 也可以加在一起: `val logger = bot.logger + MynLogger() + MyLogger2()` - */ - @Suppress("UNUSED_PARAMETER") - @Deprecated("follower 设计不佳, 请避免使用", level = DeprecationLevel.HIDDEN) // deprecated since 2.7 - @DeprecatedSinceMirai(warningSince = "2.7", errorSince = "2.10", hiddenSince = "2.11") - public open var follower: MiraiLogger? - /** * 记录一个 `verbose` 级别的日志. * 无关紧要的, 经常大量输出的日志应使用它. */ public fun verbose(message: String?) - public open fun verbose(e: Throwable?): Unit + public open fun verbose(e: Throwable?) public fun verbose(message: String?, e: Throwable?) /** @@ -214,7 +149,7 @@ public expect interface MiraiLogger { */ public fun debug(message: String?) - public open fun debug(e: Throwable?): Unit + public open fun debug(e: Throwable?) public fun debug(message: String?, e: Throwable?) @@ -223,7 +158,7 @@ public expect interface MiraiLogger { */ public fun info(message: String?) - public open fun info(e: Throwable?): Unit + public open fun info(e: Throwable?) public fun info(message: String?, e: Throwable?) @@ -232,7 +167,7 @@ public expect interface MiraiLogger { */ public fun warning(message: String?) - public open fun warning(e: Throwable?): Unit + public open fun warning(e: Throwable?) public fun warning(message: String?, e: Throwable?) @@ -241,28 +176,11 @@ public expect interface MiraiLogger { */ public fun error(message: String?) - public open fun error(e: Throwable?): Unit + public open fun error(e: Throwable?) public fun error(message: String?, e: Throwable?) /** 根据优先级调用对应函数 */ - public open fun call(priority: SimpleLogger.LogPriority, message: String? = null, e: Throwable? = null): Unit - - /** - * 添加一个 [follower], 返回 [follower] - * 它只会把 `this` 的属性 [MiraiLogger.follower] 修改为这个函数的参数 [follower], 然后返回这个参数. - * 若 [MiraiLogger.follower] 已经有值, 则会替换掉这个值. - * ``` - * +------+ +----------+ +----------+ +----------+ - * | base | <-- | follower | <-- | follower | <-- | follower | - * +------+ +----------+ +----------+ +----------+ - * ``` - * - * @return [follower] - */ - @Suppress("DeprecatedCallableAddReplaceWith") - @Deprecated("plus 设计不佳, 请避免使用.", level = DeprecationLevel.HIDDEN) // deprecated since 2.7 - @DeprecatedSinceMirai(warningSince = "2.7", errorSince = "2.10", hiddenSince = "2.11") - public open operator fun plus(follower: T): T + public open fun call(priority: SimpleLogger.LogPriority, message: String? = null, e: Throwable? = null) } @@ -327,7 +245,7 @@ public inline fun MiraiLogger.error(message: () -> String?, e: Throwable?) { * * 严重程度为 V, I, W, E. 分别对应 verbose, info, warning, error * - * @see MiraiLogger.create + * @see MiraiLogger.Factory.create */ @MiraiInternalApi public expect open class PlatformLogger @JvmOverloads constructor( @@ -455,7 +373,7 @@ public class MiraiLoggerWithSwitch internal constructor(private val delegate: Mi } /** - * 日志基类. 实现了 [follower] 的调用传递. + * 日志基类. * 若 Mirai 自带的日志系统无法满足需求, 请继承这个类或 [PlatformLogger] 并实现其抽象函数. * * 这个类不应该被用作变量的类型定义. 只应被作为继承对象. @@ -468,11 +386,6 @@ public class MiraiLoggerWithSwitch internal constructor(private val delegate: Mi public abstract class MiraiLoggerPlatformBase : MiraiLogger { public override val isEnabled: Boolean get() = true - @Suppress("OverridingDeprecatedMember") - @Deprecated("follower 设计不佳, 请避免使用", level = DeprecationLevel.HIDDEN) // deprecated since 2.7 - @DeprecatedSinceMirai(warningSince = "2.7", errorSince = "2.10", hiddenSince = "2.11") - public final override var follower: MiraiLogger? = null - public final override fun verbose(message: String?) { if (!isEnabled) return verbose0(message) @@ -533,11 +446,4 @@ public abstract class MiraiLoggerPlatformBase : MiraiLogger { protected abstract fun warning0(message: String?, e: Throwable?) protected open fun error0(message: String?): Unit = error0(message, null) protected abstract fun error0(message: String?, e: Throwable?) - - @Suppress("OverridingDeprecatedMember") - @Deprecated("plus 设计不佳, 请避免使用.", level = DeprecationLevel.HIDDEN) // deprecated since 2.7 - @DeprecatedSinceMirai(warningSince = "2.7", errorSince = "2.10", hiddenSince = "2.11") - public override operator fun plus(follower: T): T { - return follower - } } \ No newline at end of file diff --git a/mirai-core-api/src/commonMain/kotlin/utils/RemoteFile.kt b/mirai-core-api/src/commonMain/kotlin/utils/RemoteFile.kt index a2f164a29..44924444b 100644 --- a/mirai-core-api/src/commonMain/kotlin/utils/RemoteFile.kt +++ b/mirai-core-api/src/commonMain/kotlin/utils/RemoteFile.kt @@ -532,7 +532,8 @@ public expect interface RemoteFile { * 根目录路径 * @see RemoteFile.path */ - public val ROOT_PATH: String + @Suppress("CONST_VAL_WITHOUT_INITIALIZER") // compiler bug + public const val ROOT_PATH: String /** * 上传文件并获取文件消息, 但不发送. diff --git a/mirai-core-api/src/commonTest/kotlin/message.data/MessageKeyTest.kt b/mirai-core-api/src/commonTest/kotlin/message.data/MessageKeyTest.kt index b90a284d9..45aa79ce3 100644 --- a/mirai-core-api/src/commonTest/kotlin/message.data/MessageKeyTest.kt +++ b/mirai-core-api/src/commonTest/kotlin/message.data/MessageKeyTest.kt @@ -1,16 +1,16 @@ /* - * Copyright 2019-2021 Mamoe Technologies and contributors. + * Copyright 2019-2022 Mamoe Technologies and contributors. * - * 此源代码的使用受 GNU AFFERO GENERAL PUBLIC LICENSE version 3 许可证的约束, 可以在以下链接找到该许可证. - * Use of this source code is governed by the GNU AGPLv3 license that can be found through the following link. + * 此源代码的使用受 GNU AFFERO GENERAL PUBLIC LICENSE version 3 许可证的约束, 可以在以下链接找到该许可证. + * Use of this source code is governed by the GNU AGPLv3 license that can be found through the following link. * - * https://github.com/mamoe/mirai/blob/master/LICENSE + * https://github.com/mamoe/mirai/blob/dev/LICENSE */ package net.mamoe.mirai.message.data import net.mamoe.mirai.utils.safeCast -import org.junit.jupiter.api.Test +import kotlin.test.Test import kotlin.test.assertEquals import kotlin.test.assertSame diff --git a/mirai-core-api/src/commonTest/kotlin/utils/DeviceInfoTest.kt b/mirai-core-api/src/commonTest/kotlin/utils/DeviceInfoTest.kt index af4c9bca6..af3c36c2c 100644 --- a/mirai-core-api/src/commonTest/kotlin/utils/DeviceInfoTest.kt +++ b/mirai-core-api/src/commonTest/kotlin/utils/DeviceInfoTest.kt @@ -1,5 +1,5 @@ /* - * Copyright 2019-2021 Mamoe Technologies and contributors. + * Copyright 2019-2022 Mamoe Technologies and contributors. * * 此源代码的使用受 GNU AFFERO GENERAL PUBLIC LICENSE version 3 许可证的约束, 可以在以下链接找到该许可证. * Use of this source code is governed by the GNU AGPLv3 license that can be found through the following link. @@ -12,22 +12,20 @@ package net.mamoe.mirai.utils import kotlinx.serialization.json.Json import kotlinx.serialization.json.jsonObject import kotlinx.serialization.json.jsonPrimitive -import net.mamoe.mirai.utils.DeviceInfo.Companion.loadAsDeviceInfo -import org.junit.jupiter.api.io.TempDir -import java.io.File import kotlin.random.Random import kotlin.test.Test import kotlin.test.assertContentEquals import kotlin.test.assertEquals import kotlin.test.assertTrue -class DeviceInfoTest { +class CommonDeviceInfoTest { @Test fun `DeviceInfo_random with custom Random is stable`() { - val time = System.currentTimeMillis() + val time = currentTimeMillis() assertEquals(DeviceInfo.random(Random(time)), DeviceInfo.random(Random(time))) } + class HexStringTest { @Test fun `can serialize as String`() { @@ -43,24 +41,12 @@ class DeviceInfoTest { } } - @TempDir - lateinit var dir: File - @Test fun `can serialize and deserialize v2`() { val device = DeviceInfo.random() assertEquals(device, DeviceInfoManager.deserialize(DeviceInfoManager.serialize(device))) } - @Test - fun `can write and read v2`() { - val device = DeviceInfo.random() - val file = dir.resolve("device.json") - - file.writeText(DeviceInfoManager.serialize(device)) - assertEquals(device, file.loadAsDeviceInfo()) - } - @Test fun `current version pretty print preview`() { val device = DeviceInfo.random() @@ -111,13 +97,4 @@ class DeviceInfoTest { ) assertTrue { imsiMd5 matches Regex("""[a-z0-9]+""") } } - - @Test - fun `can read legacy v1`() { - val device = DeviceInfo.random() - val file = dir.resolve("device.json") - - file.writeText(Json.encodeToString(DeviceInfo.serializer(), device)) - assertEquals(device, file.loadAsDeviceInfo()) - } } \ No newline at end of file diff --git a/mirai-core-api/src/jvmBaseMain/kotlin/contact/Contact.kt b/mirai-core-api/src/jvmBaseMain/kotlin/contact/Contact.kt new file mode 100644 index 000000000..dcd2bad0a --- /dev/null +++ b/mirai-core-api/src/jvmBaseMain/kotlin/contact/Contact.kt @@ -0,0 +1,170 @@ +/* + * Copyright 2019-2022 Mamoe Technologies and contributors. + * + * 此源代码的使用受 GNU AFFERO GENERAL PUBLIC LICENSE version 3 许可证的约束, 可以在以下链接找到该许可证. + * Use of this source code is governed by the GNU AGPLv3 license that can be found through the following link. + * + * https://github.com/mamoe/mirai/blob/dev/LICENSE + */ + +@file:JvmBlockingBridge + +package net.mamoe.mirai.contact + +import kotlinx.coroutines.CoroutineScope +import me.him188.kotlin.jvm.blocking.bridge.JvmBlockingBridge +import net.mamoe.mirai.Bot +import net.mamoe.mirai.event.events.* +import net.mamoe.mirai.message.MessageReceipt +import net.mamoe.mirai.message.data.Image +import net.mamoe.mirai.message.data.Message +import net.mamoe.mirai.message.data.isContentEmpty +import net.mamoe.mirai.message.data.toPlainText +import net.mamoe.mirai.utils.ExternalResource +import net.mamoe.mirai.utils.ExternalResource.Companion.sendAsImageTo +import net.mamoe.mirai.utils.ExternalResource.Companion.uploadAsImage +import net.mamoe.mirai.utils.FileCacheStrategy +import net.mamoe.mirai.utils.NotStableForInheritance +import net.mamoe.mirai.utils.OverFileSizeMaxException +import java.io.File +import java.io.InputStream + +/** + * 联系对象, 即可以与 [Bot] 互动的对象. 包含 [用户][User], 和 [群][Group]. + */ +@NotStableForInheritance +public actual interface Contact : ContactOrBot, CoroutineScope { + /** + * 这个联系对象所属 [Bot]. + */ + public actual override val bot: Bot + + /** + * 可以是 QQ 号码或者群号码. + * + * @see User.id + * @see Group.id + */ + public actual override val id: Long + + /** + * 向这个对象发送消息. + * + * 单条消息最大可发送 4500 字符或 50 张图片. + * + * @see MessagePreSendEvent 发送消息前事件 + * @see MessagePostSendEvent 发送消息后事件 + * + * @throws EventCancelledException 当发送消息事件被取消时抛出 + * @throws BotIsBeingMutedException 发送群消息时若 [Bot] 被禁言抛出 + * @throws MessageTooLargeException 当消息过长时抛出 + * @throws IllegalArgumentException 当消息内容为空时抛出 (详见 [Message.isContentEmpty]) + * + * @return 消息回执. 可 [引用][MessageReceipt.quote] 或 [撤回][MessageReceipt.recall] 这条消息. + */ + public actual suspend fun sendMessage(message: Message): MessageReceipt + + /** + * 发送纯文本消息 + * @see sendMessage + */ + public actual suspend fun sendMessage(message: String): MessageReceipt = + this.sendMessage(message.toPlainText()) + + /** + * 上传一个 [资源][ExternalResource] 作为图片以备发送. + * + * **无论上传是否成功都不会关闭 [resource]. 需要调用方手动关闭资源** + * + * 也可以使用其他扩展: [ExternalResource.uploadAsImage] 使用 [File], [InputStream] 等上传. + * + * @see Image 查看有关图片的更多信息, 如上传图片 + * + * @see BeforeImageUploadEvent 图片发送前事件, 可拦截. + * @see ImageUploadEvent 图片发送完成事件, 不可拦截. + * + * @see ExternalResource + * + * @throws EventCancelledException 当发送消息事件被取消时抛出 + * @throws OverFileSizeMaxException 当图片文件过大而被服务器拒绝上传时抛出. (最大大小约为 20 MB, 但 mirai 限制的大小为 30 MB) + */ + public actual suspend fun uploadImage(resource: ExternalResource): Image + + public actual companion object { + /** + * 读取 [InputStream] 到临时文件并将其作为图片发送到指定联系人 + * + * 注意:此函数不会关闭 [imageStream] + * + * @param formatName 查看 [ExternalResource.formatName] + * @throws OverFileSizeMaxException + * @see FileCacheStrategy + */ + @JvmStatic + @JvmOverloads + public suspend fun C.sendImage( + imageStream: InputStream, + formatName: String? = null + ): MessageReceipt = imageStream.sendAsImageTo(this, formatName) + + /** + * 将文件作为图片发送到指定联系人 + * @param formatName 查看 [ExternalResource.formatName] + * @throws OverFileSizeMaxException + * @see FileCacheStrategy + */ + @JvmStatic + @JvmOverloads + public suspend fun C.sendImage( + file: File, + formatName: String? = null + ): MessageReceipt = file.sendAsImageTo(this, formatName) + + /** + * 将资源作为单独的图片消息发送给 [this] + * + * @see Contact.sendMessage 最终调用, 发送消息. + */ + @JvmStatic + public actual suspend fun C.sendImage(resource: ExternalResource): MessageReceipt = + resource.sendAsImageTo(this) + + + /** + * 读取 [InputStream] 到临时文件并将其作为图片上传, 但不发送 + * + * 注意:本函数不会关闭流 + * + * @param formatName 查看 [ExternalResource.formatName] + * @throws OverFileSizeMaxException + */ + @JvmStatic + @JvmOverloads + public suspend fun Contact.uploadImage( + imageStream: InputStream, + formatName: String? = null + ): Image = imageStream.uploadAsImage(this@uploadImage, formatName) + + /** + * 将文件作为图片上传, 但不发送 + * @param formatName 查看 [ExternalResource.formatName] + * @throws OverFileSizeMaxException + */ + @JvmStatic + @JvmOverloads + public suspend fun Contact.uploadImage( + file: File, + formatName: String? = null + ): Image = file.uploadAsImage(this, formatName) + + /** + * 将文件作为图片上传, 但不发送 + * @throws OverFileSizeMaxException + */ + @Throws(OverFileSizeMaxException::class) + @JvmStatic + @Suppress("INVISIBLE_MEMBER", "INVISIBLE_REFERENCE", "EXTENSION_SHADOWED_BY_MEMBER") + @kotlin.internal.LowPriorityInOverloadResolution // for better Java API + public actual suspend fun Contact.uploadImage(resource: ExternalResource): Image = this.uploadImage(resource) + } +} diff --git a/mirai-core-api/src/jvmBaseMain/kotlin/contact/announcement/Announcements.kt b/mirai-core-api/src/jvmBaseMain/kotlin/contact/announcement/Announcements.kt index 7bc45c798..b4f935298 100644 --- a/mirai-core-api/src/jvmBaseMain/kotlin/contact/announcement/Announcements.kt +++ b/mirai-core-api/src/jvmBaseMain/kotlin/contact/announcement/Announcements.kt @@ -43,7 +43,6 @@ import java.util.stream.Stream * * @since 2.7 */ -@JvmBlockingBridge @NotStableForInheritance public actual interface Announcements { /** diff --git a/mirai-core-api/src/jvmBaseMain/kotlin/contact/file/AbsoluteFolder.kt b/mirai-core-api/src/jvmBaseMain/kotlin/contact/file/AbsoluteFolder.kt new file mode 100644 index 000000000..7f159258b --- /dev/null +++ b/mirai-core-api/src/jvmBaseMain/kotlin/contact/file/AbsoluteFolder.kt @@ -0,0 +1,203 @@ +/* + * Copyright 2019-2022 Mamoe Technologies and contributors. + * + * 此源代码的使用受 GNU AFFERO GENERAL PUBLIC LICENSE version 3 许可证的约束, 可以在以下链接找到该许可证. + * Use of this source code is governed by the GNU AGPLv3 license that can be found through the following link. + * + * https://github.com/mamoe/mirai/blob/dev/LICENSE + */ + +@file:JvmBlockingBridge + +package net.mamoe.mirai.contact.file + +import kotlinx.coroutines.flow.Flow +import me.him188.kotlin.jvm.blocking.bridge.JvmBlockingBridge +import net.mamoe.mirai.contact.PermissionDeniedException +import net.mamoe.mirai.utils.ExternalResource +import net.mamoe.mirai.utils.JavaFriendlyAPI +import net.mamoe.mirai.utils.NotStableForInheritance +import net.mamoe.mirai.utils.ProgressionCallback +import java.util.stream.Stream + +/** + * 绝对目录标识. 精确表示一个远程目录. 不会受同名文件或目录的影响. + * + * @since 2.8 + * @see RemoteFiles + * @see AbsoluteFile + * @see AbsoluteFileFolder + */ +@Suppress("SEALED_INHERITOR_IN_DIFFERENT_MODULE") +@NotStableForInheritance +public actual interface AbsoluteFolder : AbsoluteFileFolder { + /** + * 当前快照中文件数量, 当有文件更新时(上传/删除文件) 该属性不会更新. + * + * 只可能通过 [refresh] 手动刷新 + * + * 特别的, 若该目录表示根目录, [contentsCount] 返回 `0`. (无法快速获取) + */ + public actual val contentsCount: Int + + /** + * 当该目录为空时返回 `true`. + */ + public actual fun isEmpty(): Boolean = contentsCount == 0 + + /** + * 返回更新了文件或目录信息 ([lastModifiedTime] 等) 的, 指向相同文件的 [AbsoluteFileFolder]. + * 不会更新当前 [AbsoluteFileFolder] 对象. + * + * 当远程文件或目录不存在时返回 `null`. + * + * 该函数会遍历上级目录的所有文件并匹配当前文件, 因此可能会非常慢, 请不要频繁使用. + */ + actual override suspend fun refreshed(): AbsoluteFolder? + + /////////////////////////////////////////////////////////////////////////// + // list children + /////////////////////////////////////////////////////////////////////////// + + /** + * 获取该目录下所有子目录列表. + */ + public actual suspend fun folders(): Flow + + /** + * 获取该目录下所有子目录列表. + * + * 实现细节: 为了适合 Java 调用, 实现类似为阻塞式的 [folders], 因此不建议在 Kotlin 使用. 在 Kotlin 请使用 [folders]. + */ + @JavaFriendlyAPI + public suspend fun foldersStream(): Stream + + + /** + * 获取该目录下所有文件列表. + */ + public actual suspend fun files(): Flow + + /** + * 获取该目录下所有文件列表. + * + * 实现细节: 为了适合 Java 调用, 实现类似为阻塞式的 [files], 因此不建议在 Kotlin 使用. 在 Kotlin 请使用 [files]. + */ + @JavaFriendlyAPI + public suspend fun filesStream(): Stream + + + /** + * 获取该目录下所有文件和子目录列表. + */ + public actual suspend fun children(): Flow + + /** + * 获取该目录下所有文件和子目录列表. + * + * 实现细节: 为了适合 Java 调用, 实现类似为阻塞式的 [children], 因此不建议在 Kotlin 使用. 在 Kotlin 请使用 [children]. + */ + @JavaFriendlyAPI + public suspend fun childrenStream(): Stream + + /////////////////////////////////////////////////////////////////////////// + // resolve and upload + /////////////////////////////////////////////////////////////////////////// + + /** + * 创建一个名称为 [name] 的子目录. 返回成功创建的或已有的子目录. 当目标目录已经存在时则直接返回该目录. + * + * @throws IllegalArgumentException 当 [name] 为空或包含非法字符 (`:*?"<>|`) 时抛出 + * @throws PermissionDeniedException 当权限不足时抛出 + */ + public actual suspend fun createFolder(name: String): AbsoluteFolder + + /** + * 获取一个已存在的名称为 [name] 的子目录. 当该名称的子目录不存在时返回 `null`. + * + * @throws IllegalArgumentException 当 [name] 为空或包含非法字符 (`:*?"<>|`) 时抛出 + */ + public actual suspend fun resolveFolder(name: String): AbsoluteFolder? + + /** + * 获取一个已存在的 [AbsoluteFileFolder.id] 为 [id] 的子目录. 当该名称的子目录不存在时返回 `null`. + * + * @throws IllegalArgumentException 当 [id] 为空或无效时抛出 + * + * @since 2.9.0 + */ + public actual suspend fun resolveFolderById(id: String): AbsoluteFolder? + + /** + * 精确获取 [AbsoluteFile.id] 为 [id] 的文件. 在目标文件不存在时返回 `null`. 当 [deep] 为 `true` 时还会深入子目录查找. + */ + public actual suspend fun resolveFileById( + id: String, + deep: Boolean + ): AbsoluteFile? + + /** + * 根据路径获取指向的所有路径为 [path] 的文件列表. 同时支持相对路径和绝对路径. 支持获取子目录内的文件. + */ + public actual suspend fun resolveFiles( + path: String + ): Flow + + /** + * 根据路径获取指向的所有路径为 [path] 的文件列表. 同时支持相对路径和绝对路径. 支持获取子目录内的文件. + * + * 实现细节: 为了适合 Java 调用, 实现类似为阻塞式的 [resolveFiles], 因此不建议在 Kotlin 使用. 在 Kotlin 请使用 [resolveFiles]. + */ + @JavaFriendlyAPI + public suspend fun resolveFilesStream( + path: String + ): Stream + + /** + * 根据路径获取指向的所有路径为 [path] 的文件和目录列表. 同时支持相对路径和绝对路径. 支持获取子目录内的文件和目录. + */ + public actual suspend fun resolveAll( + path: String + ): Flow + + /** + * 根据路径获取指向的所有路径为 [path] 的文件和目录列表. 同时支持相对路径和绝对路径. 支持获取子目录内的文件和目录. + * + * 实现细节: 为了适合 Java 调用, 实现类似为阻塞式的 [resolveAll], 因此不建议在 Kotlin 使用. 在 Kotlin 请使用 [resolveAll]. + */ + @JavaFriendlyAPI + public suspend fun resolveAllStream( + path: String + ): Stream + + /** + * 上传一个文件到该目录, 返回上传成功的文件标识. + * + * 会在必要时尝试创建远程目录. + * + * ### [filepath] + * + * - 可以是 `foo.txt` 表示该目录下的文件 "foo.txt" + * - 也可以是 `sub/foo.txt` 表示该目录的子目录 "sub" 下的文件 "foo.txt". + * - 或是绝对路径 `/sub/foo.txt` 表示根目录的 "sub" 目录下的文件 "foo.txt" + * + * @param filepath 目标文件名 + * @param content 文件内容 + * @param callback 下载进度回调, 传递的 `progression` 是已下载字节数. + * + * @throws PermissionDeniedException 当无管理员权限时抛出 (若群仅允许管理员上传) + */ + public actual suspend fun uploadNewFile( + filepath: String, + content: ExternalResource, + callback: ProgressionCallback?, + ): AbsoluteFile + + public actual companion object { + /** + * 根目录 folder ID. + * @see id + */ + public actual const val ROOT_FOLDER_ID: String = "/" + } +} \ No newline at end of file diff --git a/mirai-core-api/src/jvmBaseMain/kotlin/contact/roaming/RoamingMessages.kt b/mirai-core-api/src/jvmBaseMain/kotlin/contact/roaming/RoamingMessages.kt new file mode 100644 index 000000000..36e205f97 --- /dev/null +++ b/mirai-core-api/src/jvmBaseMain/kotlin/contact/roaming/RoamingMessages.kt @@ -0,0 +1,124 @@ +/* + * Copyright 2019-2022 Mamoe Technologies and contributors. + * + * 此源代码的使用受 GNU AFFERO GENERAL PUBLIC LICENSE version 3 许可证的约束, 可以在以下链接找到该许可证. + * Use of this source code is governed by the GNU AGPLv3 license that can be found through the following link. + * + * https://github.com/mamoe/mirai/blob/dev/LICENSE + */ + +package net.mamoe.mirai.contact.roaming + +import kotlinx.coroutines.flow.Flow +import net.mamoe.mirai.contact.Friend +import net.mamoe.mirai.message.data.MessageChain +import net.mamoe.mirai.message.data.MessageSource +import net.mamoe.mirai.utils.JavaFriendlyAPI +import java.util.stream.Stream + + +/** + * 漫游消息记录管理器. 可通过 [RoamingSupported.roamingMessages] 获得. 目前仅 [Friend] 实现 [RoamingSupported]. + * + * @since 2.8 + * @see RoamingSupported + */ +public actual interface RoamingMessages { + /////////////////////////////////////////////////////////////////////////// + // Get list + /////////////////////////////////////////////////////////////////////////// + + /** + * 查询指定时间段内的漫游消息记录. Java Stream 方法查看 [getMessagesStream]. + * + * 返回查询到的漫游消息记录, 顺序为由新到旧. 这些 [MessageChain] 与从事件中收到的消息链相似, 属于在线消息. + * 可从 [MessageChain] 获取 [MessageSource] 来确定发送人等相关信息, 也可以进行引用回复或撤回. + * + * 注意, 返回的消息记录既包含机器人发送给目标用户的消息, 也包含目标用户发送给机器人的消息. + * 可通过 [MessageChain] 获取 [MessageSource] (用法为 `messageChain.source`), 判断 [MessageSource.fromId] (发送人). + * 消息的其他*元数据*信息也要通过 [MessageSource] 获取 (如 [MessageSource.time] 获取时间). + * + * 若只需要获取单向消息 (机器人发送给目标用户的消息或反之), 可使用 [RoamingMessageFilter.SENT] 或 [RoamingMessageFilter.RECEIVED] 作为 [filter] 参数传递. + * + * 性能提示: 请在 [filter] 执行筛选, 若 [filter] 返回 `false` 则不会解析消息链, 这对本函数的处理速度有决定性影响. + * + * @param timeStart 起始时间, UTC+8 时间戳, 单位为秒. 可以为 `0`, 即表示从可以获取的最早的消息起. 负数将会被看是 `0`. + * @param timeEnd 结束时间, UTC+8 时间戳, 单位为秒. 可以为 [Long.MAX_VALUE], 即表示到可以获取的最晚的消息为止. 低于 [timeStart] 的值将会被看作是 [timeStart] 的值. + * @param filter 过滤器. + */ + public actual suspend fun getMessagesIn( + timeStart: Long, + timeEnd: Long, + filter: RoamingMessageFilter? + ): Flow + + /** + * 查询所有漫游消息记录. Java Stream 方法查看 [getAllMessagesStream]. + * + * 返回查询到的漫游消息记录, 顺序为由新到旧. 这些 [MessageChain] 与从事件中收到的消息链相似, 属于在线消息. + * 可从 [MessageChain] 获取 [MessageSource] 来确定发送人等相关信息, 也可以进行引用回复或撤回. + * + * 注意, 返回的消息记录既包含机器人发送给目标用户的消息, 也包含目标用户发送给机器人的消息. + * 可通过 [MessageChain] 获取 [MessageSource] (用法为 `messageChain.source`), 判断 [MessageSource.fromId] (发送人). + * 消息的其他*元数据*信息也要通过 [MessageSource] 获取 (如 [MessageSource.time] 获取时间). + * + * 若只需要获取单向消息 (机器人发送给目标用户的消息或反之), 可使用 [RoamingMessageFilter.SENT] 或 [RoamingMessageFilter.RECEIVED] 作为 [filter] 参数传递. + * + * 性能提示: 请在 [filter] 执行筛选, 若 [filter] 返回 `false` 则不会解析消息链, 这对本函数的处理速度有决定性影响. + * + * @param filter 过滤器. + */ + public actual suspend fun getAllMessages( + filter: RoamingMessageFilter? + ): Flow = getMessagesIn(0, Long.MAX_VALUE, filter) + + /** + * 查询指定时间段内的漫游消息记录. Kotlin Flow 版本查看 [getMessagesIn]. + * + * 返回查询到的漫游消息记录, 顺序为由新到旧. 这些 [MessageChain] 与从事件中收到的消息链相似, 属于在线消息. + * 可从 [MessageChain] 获取 [MessageSource] 来确定发送人等相关信息, 也可以进行引用回复或撤回. + * + * 注意, 返回的消息记录既包含机器人发送给目标用户的消息, 也包含目标用户发送给机器人的消息. + * 可通过 [MessageChain] 获取 [MessageSource] (用法为 `messageChain.get(MessageSource.Key)`), 判断 [MessageSource.fromId] (发送人). + * 消息的其他*元数据*信息也要通过 [MessageSource] 获取 (如 [MessageSource.time] 获取时间). + * + * 若只需要获取单向消息 (机器人发送给目标用户的消息或反之), 可使用 [RoamingMessageFilter.SENT] 或 [RoamingMessageFilter.RECEIVED] 作为 [filter] 参数传递. + * + * 性能提示: 请在 [filter] 执行筛选, 若 [filter] 返回 `false` 则不会解析消息链, 这对本函数的处理速度有决定性影响. + * + * @param timeStart 起始时间, UTC+8 时间戳, 单位为秒. 可以为 `0`, 即表示从可以获取的最早的消息起. 负数将会被看是 `0`. + * @param timeEnd 结束时间, UTC+8 时间戳, 单位为秒. 可以为 [Long.MAX_VALUE], 即表示到可以获取的最晚的消息为止. 低于 [timeStart] 的值将会被看作是 [timeStart] 的值. + * @param filter 过滤器. + */ + @Suppress("OVERLOADS_INTERFACE") + @JvmOverloads + @JavaFriendlyAPI + public suspend fun getMessagesStream( + timeStart: Long, + timeEnd: Long, + filter: RoamingMessageFilter? = null + ): Stream + + /** + * 查询所有漫游消息记录. Kotlin Flow 版本查看 [getAllMessages]. + * + * 返回查询到的漫游消息记录, 顺序为由新到旧. 这些 [MessageChain] 与从事件中收到的消息链相似, 属于在线消息. + * 可从 [MessageChain] 获取 [MessageSource] 来确定发送人等相关信息, 也可以进行引用回复或撤回. + * + * 注意, 返回的消息记录既包含机器人发送给目标用户的消息, 也包含目标用户发送给机器人的消息. + * 可通过 [MessageChain] 获取 [MessageSource] (用法为 `messageChain.get(MessageSource.Key)`), 判断 [MessageSource.fromId] (发送人). + * 消息的其他*元数据*信息也要通过 [MessageSource] 获取 (如 [MessageSource.time] 获取时间). + * + * 若只需要获取单向消息 (机器人发送给目标用户的消息或反之), 可使用 [RoamingMessageFilter.SENT] 或 [RoamingMessageFilter.RECEIVED] 作为 [filter] 参数传递. + * + * 性能提示: 请在 [filter] 执行筛选, 若 [filter] 返回 `false` 则不会解析消息链, 这对本函数的处理速度有决定性影响. + * + * @param filter 过滤器. + */ + @Suppress("OVERLOADS_INTERFACE") + @JvmOverloads + @JavaFriendlyAPI + public suspend fun getAllMessagesStream( + filter: RoamingMessageFilter? = null + ): Stream = getMessagesStream(0, Long.MAX_VALUE, filter) +} \ No newline at end of file diff --git a/mirai-core-api/src/jvmBaseMain/kotlin/event/EventChannel.kt b/mirai-core-api/src/jvmBaseMain/kotlin/event/EventChannel.kt new file mode 100644 index 000000000..c987de739 --- /dev/null +++ b/mirai-core-api/src/jvmBaseMain/kotlin/event/EventChannel.kt @@ -0,0 +1,714 @@ +/* + * Copyright 2019-2022 Mamoe Technologies and contributors. + * + * 此源代码的使用受 GNU AFFERO GENERAL PUBLIC LICENSE version 3 许可证的约束, 可以在以下链接找到该许可证. + * Use of this source code is governed by the GNU AGPLv3 license that can be found through the following link. + * + * https://github.com/mamoe/mirai/blob/dev/LICENSE + */ + +package net.mamoe.mirai.event + +import kotlinx.coroutines.* +import kotlinx.coroutines.channels.Channel +import kotlinx.coroutines.channels.ClosedSendChannelException +import kotlinx.coroutines.channels.SendChannel +import kotlinx.coroutines.flow.Flow +import kotlinx.coroutines.flow.SharedFlow +import kotlinx.coroutines.flow.filter +import kotlinx.coroutines.sync.Mutex +import net.mamoe.mirai.Bot +import net.mamoe.mirai.IMirai +import net.mamoe.mirai.event.ConcurrencyKind.CONCURRENT +import net.mamoe.mirai.event.events.BotEvent +import net.mamoe.mirai.internal.event.registerEventHandler +import net.mamoe.mirai.utils.* +import java.util.function.Consumer +import kotlin.coroutines.CoroutineContext +import kotlin.coroutines.EmptyCoroutineContext +import kotlin.reflect.KClass + +/** + * 事件通道. + * + * 事件通道是监听事件的入口, 但不负责广播事件. 要广播事件, 使用 [Event.broadcast] 或 [IMirai.broadcastEvent]. + * + * ## 获取事件通道 + * + * [EventChannel] 不可自行构造, 只能通过 [GlobalEventChannel], [BotEvent], 或基于一个通道的过滤等操作获得. + * + * ### 全局事件通道 + * + * [GlobalEventChannel] 是单例对象, 表示全局事件通道, 可以获取到在其中广播的所有事件. + * + * ### [BotEvent] 事件通道 + * + * 若只需要监听某个 [Bot] 的事件, 可通过 [Bot.eventChannel] 获取到这样的 [EventChannel]. + * + * ## 通道操作 + * + * ### 对通道的操作 + * - 过滤通道: 通过 [EventChannel.filter]. 例如 `filter { it is BotEvent }` 得到一个只能监听到 [BotEvent] 的事件通道. + * - 转换为 Kotlin 协程 [Channel]: [EventChannel.forwardToChannel] + * - 添加 [CoroutineContext]: [context], [parentJob], [parentScope], [exceptionHandler] + * + * ### 创建事件监听 + * - [EventChannel.subscribe] 创建带条件的一个事件监听器. + * - [EventChannel.subscribeAlways] 创建一个总是监听事件的事件监听器. + * - [EventChannel.subscribeOnce] 创建一个只监听单次的事件监听器. + * + * ### 监听器生命周期 + * + * 阅读 [EventChannel.subscribe] 以获取监听器生命周期相关信息. + * + * ## 与 kotlinx-coroutines 交互 + * + * mirai [EventChannel] 设计比 kotlinx-coroutines 的 [Flow] 稳定版更早. + * [EventChannel] 的功能与 [Flow] 类似, 不过 [EventChannel] 在 [subscribe] (类似 [Flow.collect]) 时有优先级判定, 也允许[拦截][Event.intercept]. + * + * ### 通过 [Flow] 接收事件 + * + * 使用 [EventChannel.asFlow] 获得 [Flow], 然后可使用 [Flow.collect] 等操作. + * + * ### 转发事件到 [SendChannel] + * + * 使用 [EventChannel.forwardToChannel] 可将事件转发到指定 [SendChannel]. + */ +@NotStableForInheritance // since 2.12, before it was `final class`. +public actual abstract class EventChannel @MiraiInternalApi public actual constructor( + public actual val baseEventClass: KClass, + /** + * 此事件通道的默认 [CoroutineScope.coroutineContext]. 将会被添加给所有注册的事件监听器. + */ + public actual val defaultCoroutineContext: CoroutineContext, +) { + /** + * 创建事件监听并将监听结果发送在 [Channel]. 将返回值 [Channel] [关闭][Channel.close] 时将会同时关闭事件监听. + * + * @param capacity Channel 容量. 详见 [Channel] 构造. + * + * @see subscribeAlways + * @see Channel + */ + @Deprecated( + "Please use forwardToChannel instead.", + replaceWith = ReplaceWith( + "Channel(capacity).apply { forwardToChannel(this, coroutineContext, priority) }", + "kotlinx.coroutines.channels.Channel" + ), + level = DeprecationLevel.WARNING, + ) + @DeprecatedSinceMirai(warningSince = "2.10") + @MiraiExperimentalApi + public fun asChannel( + capacity: Int = Channel.RENDEZVOUS, + coroutineContext: CoroutineContext = EmptyCoroutineContext, + @Suppress("UNUSED_PARAMETER") concurrency: ConcurrencyKind = CONCURRENT, + priority: EventPriority = EventPriority.NORMAL, + ): Channel = + Channel(capacity).apply { forwardToChannel(this, coroutineContext, priority) } + + /** + * 创建事件监听并将监听结果转发到 [channel]. 当 [Channel.send] 抛出 [ClosedSendChannelException] 时停止 [Listener] 监听和转发. + * + * 返回创建的会转发监听到的所有事件到 [channel] 的[事件监听器][Listener]. [停止][Listener.complete] 该监听器会停止转发, 不会影响目标 [channel]. + * + * 若 [Channel.send] 挂起, 则监听器也会挂起, 也就可能会导致事件广播过程挂起. + * + * 示例: + * + * ``` + * val eventChannel: EventChannel = ... + * val channel = Channel() // kotlinx.coroutines.channels.Channel + * eventChannel.forwardToChannel(channel, priority = ...) + * + * // 其他地方 + * val event: BotEvent = channel.receive() // 挂起并接收一个事件 + * ``` + * + * @see subscribeAlways + * @see Channel + * @since 2.10 + */ + public actual fun forwardToChannel( + channel: SendChannel<@UnsafeVariance BaseEvent>, + coroutineContext: CoroutineContext, + priority: EventPriority, + ): Listener<@UnsafeVariance BaseEvent> { + return subscribe(baseEventClass, coroutineContext, priority = priority) { + try { + channel.send(it) + ListeningStatus.LISTENING + } catch (_: ClosedSendChannelException) { + ListeningStatus.STOPPED + } + } + } + + /** + * 通过 [Flow] 接收此通道内的所有事件. + * + * ``` + * val eventChannel: EventChannel = ... + * val flow: Flow = eventChannel.asFlow() + * + * flow.collect { // it + * // + * } + * + * flow.filterIsInstance.collect { // it: GroupMessageEvent + * // 处理事件 ... + * } + * + * flow.filterIsInstance.collect { // it: FriendMessageEvent + * // 处理事件 ... + * } + * ``` + * + * 类似于 [SharedFlow], [EventChannel.asFlow] 返回的 [Flow] 永远都不会停止. 因此上述示例 [Flow.collect] 永远都不会正常 (以抛出异常之外的) 结束. + * + * 通过 [asFlow] 接收事件相当于通过 [subscribeAlways] 以 [EventPriority.MONITOR] 监听事件. + * + * **注意**: [context], [parentJob] 等控制 [EventChannel.defaultCoroutineContext] 的操作对 [asFlow] 无效. 因为 [asFlow] 并不创建协程. + * + * @see Flow + * @since 2.12 + */ + public actual abstract fun asFlow(): Flow + + // region transforming operations + + /** + * 添加一个过滤器. 过滤器将在收到任何事件之后, 传递给通过 [EventChannel.subscribe] 注册的监听器之前调用. + * + * 若 [filter] 返回 `true`, 该事件将会被传给监听器. 否则将会被忽略, **监听器继续监听**. + * + * ## 线性顺序 + * 多个 [filter] 的处理是线性且有顺序的. 若一个 [filter] 已经返回了 `false` (代表忽略这个事件), 则会立即忽略, 而不会传递给后续过滤器. + * + * 示例: + * ``` + * GlobalEventChannel // GlobalEventChannel 会收到全局所有事件, 事件类型是 Event + * .filterIsInstance() // 过滤, 只接受 BotEvent + * .filter { event: BotEvent -> + * // 此时的 event 一定是 BotEvent + * event.bot.id == 123456 // 再过滤 event 的 bot.id + * } + * .subscribeAlways { event: BotEvent -> + * // 现在 event 是 BotEvent, 且 bot.id == 123456 + * } + * ``` + * + * ## 过滤器挂起 + * [filter] 允许挂起协程. **过滤器的挂起将被认为是事件监听器的挂起**. + * + * 过滤器挂起是否会影响事件处理, + * 取决于 [subscribe] 时的 [ConcurrencyKind] 和 [EventPriority]. + * + * ## 过滤器异常处理 + * 若 [filter] 抛出异常, 将被包装为 [ExceptionInEventChannelFilterException] 并重新抛出. + * + * @see filterIsInstance 过滤指定类型的事件 + */ + @JvmSynthetic + public actual fun filter(filter: suspend (event: BaseEvent) -> Boolean): EventChannel { + return FilterEventChannel(this, filter) + } + + /** + * [EventChannel.filter] 的 Java 版本. + * + * 添加一个过滤器. 过滤器将在收到任何事件之后, 传递给通过 [EventChannel.subscribe] 注册的监听器之前调用. + * + * 若 [filter] 返回 `true`, 该事件将会被传给监听器. 否则将会被忽略, **监听器继续监听**. + * + * ## 线性顺序 + * 多个 [filter] 的处理是线性且有顺序的. 若一个 [filter] 已经返回了 `false` (代表忽略这个事件), 则会立即忽略, 而不会传递给后续过滤器. + * + * 示例: + * ``` + * GlobalEventChannel // GlobalEventChannel 会收到全局所有事件, 事件类型是 Event + * .filterIsInstance(BotEvent.class) // 过滤, 只接受 BotEvent + * .filter(event -> + * // 此时的 event 一定是 BotEvent + * event.bot.id == 123456 // 再过滤 event 的 bot.id + * ) + * .subscribeAlways(event -> { + * // 现在 event 是 BotEvent, 且 bot.id == 123456 + * }) + * ``` + * + * ## 过滤器阻塞 + * [filter] 允许阻塞线程. **过滤器的阻塞将被认为是事件监听器的阻塞**. + * + * 过滤器阻塞是否会影响事件处理, + * 取决于 [subscribe] 时的 [ConcurrencyKind] 和 [EventPriority]. + * + * ## 过滤器异常处理 + * 若 [filter] 抛出异常, 将被包装为 [ExceptionInEventChannelFilterException] 并重新抛出. + * + * @see filterIsInstance 过滤指定类型的事件 + * + * @since 2.2 + */ + @Suppress("INVISIBLE_MEMBER", "INVISIBLE_REFERENCE") + @kotlin.internal.LowPriorityInOverloadResolution + public actual fun filter(filter: (event: BaseEvent) -> Boolean): EventChannel { + return filter { runBIO { filter(it) } } + } + + /** + * 过滤事件的类型. 返回一个只包含 [E] 类型事件的 [EventChannel] + * @see filter 获取更多信息 + */ + @JvmSynthetic + public actual inline fun filterIsInstance(): EventChannel = + filterIsInstance(E::class) + + /** + * 过滤事件的类型. 返回一个只包含 [E] 类型事件的 [EventChannel] + * @see filter 获取更多信息 + */ + public actual fun filterIsInstance(kClass: KClass): EventChannel { + return filter { kClass.isInstance(it) }.cast() + } + + /** + * 过滤事件的类型. 返回一个只包含 [E] 类型事件的 [EventChannel] + * @see filter 获取更多信息 + */ + public fun filterIsInstance(clazz: Class): EventChannel = + filterIsInstance(clazz.kotlin) + + + /** + * 创建一个新的 [EventChannel], 该 [EventChannel] 包含 [`this.coroutineContext`][defaultCoroutineContext] 和添加的 [coroutineContexts]. + * [coroutineContexts] 会覆盖 [defaultCoroutineContext] 中的重复元素. + * + * 此操作不会修改 [`this.coroutineContext`][defaultCoroutineContext], 只会创建一个新的 [EventChannel]. + */ + public actual abstract fun context(vararg coroutineContexts: CoroutineContext): EventChannel + + /** + * 创建一个新的 [EventChannel], 该 [EventChannel] 包含 [this.coroutineContext][defaultCoroutineContext] 和添加的 [coroutineExceptionHandler] + * @see context + */ + @Suppress("INVISIBLE_MEMBER", "INVISIBLE_REFERENCE") + @kotlin.internal.LowPriorityInOverloadResolution + public actual fun exceptionHandler(coroutineExceptionHandler: CoroutineExceptionHandler): EventChannel { + return context(coroutineExceptionHandler) + } + + /** + * 创建一个新的 [EventChannel], 该 [EventChannel] 包含 [`this.coroutineContext`][defaultCoroutineContext] 和添加的 [coroutineExceptionHandler] + * @see context + */ + public actual fun exceptionHandler(coroutineExceptionHandler: (exception: Throwable) -> Unit): EventChannel { + return context(CoroutineExceptionHandler { _, throwable -> + coroutineExceptionHandler(throwable) + }) + } + + /** + * 将 [coroutineScope] 作为这个 [EventChannel] 的父作用域. + * + * 实际作用为创建一个新的 [EventChannel], + * 该 [EventChannel] 包含 [`this.coroutineContext`][defaultCoroutineContext] 和添加的 [CoroutineScope.coroutineContext], + * 并以 [CoroutineScope] 中 [Job] (如果有) [作为父 Job][parentJob] + * + * @see parentJob + * @see context + * + * @see CoroutineScope.globalEventChannel `GlobalEventChannel.parentScope()` 的扩展 + */ + public actual fun parentScope(coroutineScope: CoroutineScope): EventChannel { + return context(coroutineScope.coroutineContext) + } + + /** + * 指定协程父 [Job]. 之后在此 [EventChannel] 下创建的事件监听器都会成为 [job] 的子任务, 当 [job] 被取消时, 所有的事件监听器都会被取消. + * + * 注意: 监听器不会失败 ([Job.cancel]). 监听器处理过程的异常都会被捕获然后交由 [CoroutineExceptionHandler] 处理, 因此 [job] 不会因为子任务监听器的失败而被取消. + * + * @see parentScope + * @see context + */ + public actual fun parentJob(job: Job): EventChannel { + return context(job) + } + + // endregion + + // region subscribe + + /** + * 创建一个事件监听器, 监听事件通道中所有 [E] 及其子类事件. + * + * 每当 [事件广播][Event.broadcast] 时, [handler] 都会被执行. + * + * + * ## 创建监听 + * 调用本函数: + * ``` + * eventChannel.subscribe { /* 会收到此通道中的所有是 E 的事件 */ } + * ``` + * + * ## 生命周期 + * + * ### 通过协程作用域管理监听器 + * 本函数将会创建一个 [Job], 成为 [parentJob] 中的子任务. 可创建一个 [CoroutineScope] 来管理所有的监听器: + * ``` + * val scope = CoroutineScope(SupervisorJob()) + * + * val scopedChannel = eventChannel.parentScope(scope) // 将协程作用域 scope 附加到这个 EventChannel + * + * scopedChannel.subscribeAlways { /* ... */ } // 启动监听, 监听器协程会作为 scope 的子任务 + * scopedChannel.subscribeAlways { /* ... */ } // 启动监听, 监听器协程会作为 scope 的子任务 + * + * scope.cancel() // 停止了协程作用域, 也就取消了两个监听器 + * ``` + * + * 这个函数返回 [Listener], 它是一个 [CompletableJob]. 它会成为 [parentJob] 或 [parentScope] 的一个 [子任务][Job] + * + * ### 停止监听 + * 如果 [handler] 返回 [ListeningStatus.STOPPED] 监听器将被停止. + * + * 也可以通过 [subscribe] 返回值 [Listener] 的 [Listener.complete] + * + * ## 监听器调度 + * 监听器会被创建一个协程任务, 语义上在 [parentScope] 下运行. + * 通过 Kotlin [默认协程调度器][Dispatchers.Default] 在固定的全局共享线程池里执行, 除非有 [coroutineContext] 指定. + * + * 默认在 [handler] 中不能处理阻塞任务. 阻塞任务将会阻塞一个 Kotlin 全局协程调度线程并可能导致严重问题. + * 请通过 `withContext(Dispatchers.IO) { }` 等方法执行阻塞工作. + * + * ## 异常处理 + * + * **监听过程抛出的异常是需要尽可能避免的, 因为这将产生不确定性.** + * + * 当参数 [handler] 处理事件抛出异常时, 只会从监听方协程上下文 ([CoroutineContext]) 寻找 [CoroutineExceptionHandler] 处理异常, 即如下顺序: + * 1. 本函数参数 [coroutineContext] + * 2. [EventChannel.defaultCoroutineContext] + * 3. 若以上步骤无法获取 [CoroutineExceptionHandler], 则只会在日志记录异常. + * 因此建议先指定 [CoroutineExceptionHandler] (可通过 [EventChannel.exceptionHandler]) 再监听事件, 或者在监听事件中捕获异常. + * + * 因此, 广播方 ([Event.broadcast]) 不会知晓监听方产生的异常, 其 [Event.broadcast] 过程也不会因监听方产生异常而提前结束. + * + * ***备注***: 在 2.11 以前, 发生上述异常时还会从广播方和有关 [Bot] 协程域获取 [CoroutineExceptionHandler]. 因此行为不稳定而在 2.11 变更为上述过程. + * + * 事件处理时抛出异常不会停止监听器. + * + * 建议在事件处理中 (即 [handler] 里) 处理异常, + * 或在参数 [coroutineContext] 中添加 [CoroutineExceptionHandler], 或通过 [EventChannel.exceptionHandler]. + * + * ## 并发安全性 + * 基于 [concurrency] 参数, 事件监听器可以被允许并行执行. + * + * - 若 [concurrency] 为 [ConcurrencyKind.CONCURRENT], [handler] 可能被并行调用, 需要保证并发安全. + * - 若 [concurrency] 为 [ConcurrencyKind.LOCKED], [handler] 会被 [Mutex] 限制, 串行异步执行. + * + * ## 衍生监听方法 + * + * 这些方法仅 Kotlin 可用. + * + * - [syncFromEvent]: 挂起当前协程, 监听一个事件, 并尝试从这个事件中**获取**一个值 + * - [nextEvent]: 挂起当前协程, 直到监听到特定类型事件的广播并通过过滤器, 返回这个事件实例. + * + * @param coroutineContext 在 [defaultCoroutineContext] 的基础上, 给事件监听协程的额外的 [CoroutineContext]. + * @param concurrency 并发类型. 查看 [ConcurrencyKind] + * @param priority 监听优先级,优先级越高越先执行 + * @param handler 事件处理器. 在接收到事件时会调用这个处理器. 其返回值意义参考 [ListeningStatus]. 其异常处理参考上文 + * + * @return 监听器实例. 此监听器已经注册到指定事件上, 在事件广播时将会调用 [handler] + * + * + * @see selectMessages 以 `when` 的语法 '选择' 即将到来的一条消息. + * @see whileSelectMessages 以 `when` 的语法 '选择' 即将到来的所有消息, 直到不满足筛选结果. + * + * @see subscribeAlways 一直监听 + * @see subscribeOnce 只监听一次 + * + * @see subscribeMessages 监听消息 DSL + */ + @JvmSynthetic + public actual inline fun subscribe( + coroutineContext: CoroutineContext, + concurrency: ConcurrencyKind, + priority: EventPriority, + noinline handler: suspend E.(E) -> ListeningStatus, + ): Listener = subscribe(E::class, coroutineContext, concurrency, priority, handler) + + /** + * 与 [subscribe] 的区别是接受 [eventClass] 参数, 而不使用 `reified` 泛型. 通常推荐使用具体化类型参数. + * + * @return 监听器实例. 此监听器已经注册到指定事件上, 在事件广播时将会调用 [handler] + * @see subscribe + */ + @JvmSynthetic + public actual fun subscribe( + eventClass: KClass, + coroutineContext: CoroutineContext, + concurrency: ConcurrencyKind, + priority: EventPriority, + handler: suspend E.(E) -> ListeningStatus, + ): Listener = subscribeInternal( + eventClass, + createListener(coroutineContext, concurrency, priority) { it.handler(it); } + ) + + /** + * 创建一个事件监听器, 监听事件通道中所有 [E] 及其子类事件. + * 每当 [事件广播][Event.broadcast] 时, [handler] 都会被执行. + * + * 可在任意时候通过 [Listener.complete] 来主动停止监听. + * + * @param concurrency 并发类型默认为 [CONCURRENT] + * @param coroutineContext 在 [defaultCoroutineContext] 的基础上, 给事件监听协程的额外的 [CoroutineContext] + * @param priority 处理优先级, 优先级高的先执行 + * + * @return 监听器实例. 此监听器已经注册到指定事件上, 在事件广播时将会调用 [handler] + * + * @see subscribe 获取更多说明 + */ + @JvmSynthetic + public actual inline fun subscribeAlways( + coroutineContext: CoroutineContext, + concurrency: ConcurrencyKind, + priority: EventPriority, + noinline handler: suspend E.(E) -> Unit, + ): Listener = subscribeAlways(E::class, coroutineContext, concurrency, priority, handler) + + + /** + * @see subscribe + * @see subscribeAlways + */ + @JvmSynthetic + public actual fun subscribeAlways( + eventClass: KClass, + coroutineContext: CoroutineContext, + concurrency: ConcurrencyKind, + priority: EventPriority, + handler: suspend E.(E) -> Unit, + ): Listener = subscribeInternal( + eventClass, + createListener(coroutineContext, concurrency, priority) { it.handler(it); ListeningStatus.LISTENING } + ) + + /** + * 创建一个事件监听器, 监听事件通道中所有 [E] 及其子类事件, 只监听一次. + * 当 [事件广播][Event.broadcast] 时, [handler] 会被执行. + * + * 可在任意时候通过 [Listener.complete] 来主动停止监听. + * + * @param coroutineContext 在 [defaultCoroutineContext] 的基础上, 给事件监听协程的额外的 [CoroutineContext] + * @param priority 处理优先级, 优先级高的先执行 + * + * @see subscribe 获取更多说明 + */ + @JvmSynthetic + public actual inline fun subscribeOnce( + coroutineContext: CoroutineContext, + priority: EventPriority, + noinline handler: suspend E.(E) -> Unit, + ): Listener = subscribeOnce(E::class, coroutineContext, priority, handler) + + /** + * @see subscribeOnce + */ + public actual fun subscribeOnce( + eventClass: KClass, + coroutineContext: CoroutineContext, + priority: EventPriority, + handler: suspend E.(E) -> Unit, + ): Listener = subscribeInternal( + eventClass, + createListener(coroutineContext, ConcurrencyKind.LOCKED, priority) { it.handler(it); ListeningStatus.STOPPED } + ) + + // endregion + + /** + * 注册 [ListenerHost] 中的所有 [EventHandler] 标注的方法到这个 [EventChannel]. 查看 [EventHandler]. + * + * @param coroutineContext 在 [defaultCoroutineContext] 的基础上, 给事件监听协程的额外的 [CoroutineContext] + * + * @see subscribe + * @see EventHandler + * @see ListenerHost + */ + @JvmOverloads + public fun registerListenerHost( + host: ListenerHost, + coroutineContext: CoroutineContext = EmptyCoroutineContext, + ) { + val jobOfListenerHost: Job? + val coroutineContext0 = if (host is SimpleListenerHost) { + val listenerCoroutineContext = host.coroutineContext + val listenerJob = listenerCoroutineContext[Job] + + val rsp = listenerCoroutineContext.minusKey(Job) + + coroutineContext + + (listenerCoroutineContext[CoroutineExceptionHandler] ?: EmptyCoroutineContext) + + val registerCancelHook = when { + listenerJob === null -> false + + // Registering cancellation hook is needless + // if [Job] of [EventChannel] is same as [Job] of [SimpleListenerHost] + (rsp[Job] ?: this.defaultCoroutineContext[Job]) === listenerJob -> false + + else -> true + } + + jobOfListenerHost = if (registerCancelHook) { + listenerCoroutineContext[Job] + } else { + null + } + rsp + } else { + jobOfListenerHost = null + coroutineContext + } + for (method in host.javaClass.declaredMethods) { + method.getAnnotation(EventHandler::class.java)?.let { + val listener = method.registerEventHandler(host, this, it, coroutineContext0) + // For [SimpleListenerHost.cancelAll] + jobOfListenerHost?.invokeOnCompletion { exception -> + listener.cancel( + when (exception) { + is CancellationException -> exception + is Throwable -> CancellationException(null, exception) + else -> null + } + ) + } + } + } + } + + // region Java API + + /** + * Java API. 查看 [subscribeAlways] 获取更多信息. + * + * ```java + * eventChannel.subscribeAlways(GroupMessageEvent.class, (event) -> { }); + * ``` + * + * @see subscribe + * @see subscribeAlways + */ + @JvmOverloads + @Suppress("INVISIBLE_MEMBER", "INVISIBLE_REFERENCE") + @kotlin.internal.LowPriorityInOverloadResolution + public fun subscribeAlways( + eventClass: Class, + coroutineContext: CoroutineContext = EmptyCoroutineContext, + concurrency: ConcurrencyKind = CONCURRENT, + priority: EventPriority = EventPriority.NORMAL, + handler: Consumer, + ): Listener = subscribeInternal( + eventClass.kotlin, + createListener(coroutineContext, concurrency, priority) { event -> + runInterruptible(Dispatchers.IO) { handler.accept(event) } + ListeningStatus.LISTENING + } + ) + + /** + * Java API. 查看 [subscribe] 获取更多信息. + * + * ```java + * eventChannel.subscribe(GroupMessageEvent.class, (event) -> { + * return ListeningStatus.LISTENING; + * }); + * ``` + * + * @see subscribe + */ + @JvmOverloads + @Suppress("INVISIBLE_MEMBER", "INVISIBLE_REFERENCE") + @kotlin.internal.LowPriorityInOverloadResolution + public fun subscribe( + eventClass: Class, + coroutineContext: CoroutineContext = EmptyCoroutineContext, + concurrency: ConcurrencyKind = CONCURRENT, + priority: EventPriority = EventPriority.NORMAL, + handler: java.util.function.Function, + ): Listener = subscribeInternal( + eventClass.kotlin, + createListener(coroutineContext, concurrency, priority) { event -> + runInterruptible(Dispatchers.IO) { handler.apply(event) } + } + ) + + /** + * Java API. 查看 [subscribeOnce] 获取更多信息. + * + * ```java + * eventChannel.subscribeOnce(GroupMessageEvent.class, (event) -> { }); + * ``` + * + * @see subscribe + * @see subscribeOnce + */ + @JvmOverloads + @Suppress("INVISIBLE_MEMBER", "INVISIBLE_REFERENCE") + @kotlin.internal.LowPriorityInOverloadResolution + public fun subscribeOnce( + eventClass: Class, + coroutineContext: CoroutineContext = EmptyCoroutineContext, + concurrency: ConcurrencyKind = CONCURRENT, + priority: EventPriority = EventPriority.NORMAL, + handler: Consumer, + ): Listener = subscribeInternal( + eventClass.kotlin, + createListener(coroutineContext, concurrency, priority) { event -> + runInterruptible(Dispatchers.IO) { handler.accept(event) } + ListeningStatus.STOPPED + } + ) + + // endregion + + // region impl + + + // protected, to hide from users + @MiraiInternalApi + protected actual abstract fun registerListener(eventClass: KClass, listener: Listener) + + // to overcome visibility issue + internal actual fun registerListener0(eventClass: KClass, listener: Listener) { + return registerListener(eventClass, listener) + } + + private fun , E : Event> subscribeInternal(eventClass: KClass, listener: L): L { + registerListener(eventClass, listener) + return listener + } + + /** + * Creates [Listener] instance using the [listenerBlock] action. + */ +// @Contract("_ -> new") // always creates new instance + @MiraiInternalApi + protected actual abstract fun createListener( + coroutineContext: CoroutineContext, + concurrencyKind: ConcurrencyKind, + priority: EventPriority, + listenerBlock: suspend (E) -> ListeningStatus, + ): Listener + + // to overcome visibility issue + internal actual fun createListener0( + coroutineContext: CoroutineContext, + concurrencyKind: ConcurrencyKind, + priority: EventPriority, + listenerBlock: suspend (E) -> ListeningStatus, + ): Listener = createListener(coroutineContext, concurrencyKind, priority, listenerBlock) + + // endregion +} diff --git a/mirai-core-api/src/commonMain/kotlin/event/JvmMethodListeners.kt b/mirai-core-api/src/jvmBaseMain/kotlin/event/JvmMethodListeners.kt similarity index 95% rename from mirai-core-api/src/commonMain/kotlin/event/JvmMethodListeners.kt rename to mirai-core-api/src/jvmBaseMain/kotlin/event/JvmMethodListeners.kt index a76c2bdea..e5416c6e1 100644 --- a/mirai-core-api/src/commonMain/kotlin/event/JvmMethodListeners.kt +++ b/mirai-core-api/src/jvmBaseMain/kotlin/event/JvmMethodListeners.kt @@ -1,10 +1,10 @@ /* - * Copyright 2019-2021 Mamoe Technologies and contributors. + * Copyright 2019-2022 Mamoe Technologies and contributors. * - * 此源代码的使用受 GNU AFFERO GENERAL PUBLIC LICENSE version 3 许可证的约束, 可以在以下链接找到该许可证. - * Use of this source code is governed by the GNU AGPLv3 license that can be found through the following link. + * 此源代码的使用受 GNU AFFERO GENERAL PUBLIC LICENSE version 3 许可证的约束, 可以在以下链接找到该许可证. + * Use of this source code is governed by the GNU AGPLv3 license that can be found through the following link. * - * https://github.com/mamoe/mirai/blob/master/LICENSE + * https://github.com/mamoe/mirai/blob/dev/LICENSE */ @file:JvmMultifileClass diff --git a/mirai-core-api/src/commonMain/kotlin/internal/event/JvmMethodListenersInternal.kt b/mirai-core-api/src/jvmBaseMain/kotlin/internal/event/JvmMethodListenersInternal.kt similarity index 95% rename from mirai-core-api/src/commonMain/kotlin/internal/event/JvmMethodListenersInternal.kt rename to mirai-core-api/src/jvmBaseMain/kotlin/internal/event/JvmMethodListenersInternal.kt index 1a2c37a42..62f92ba9d 100644 --- a/mirai-core-api/src/commonMain/kotlin/internal/event/JvmMethodListenersInternal.kt +++ b/mirai-core-api/src/jvmBaseMain/kotlin/internal/event/JvmMethodListenersInternal.kt @@ -1,10 +1,10 @@ /* - * Copyright 2019-2021 Mamoe Technologies and contributors. + * Copyright 2019-2022 Mamoe Technologies and contributors. * - * 此源代码的使用受 GNU AFFERO GENERAL PUBLIC LICENSE version 3 许可证的约束, 可以在以下链接找到该许可证. - * Use of this source code is governed by the GNU AGPLv3 license that can be found through the following link. + * 此源代码的使用受 GNU AFFERO GENERAL PUBLIC LICENSE version 3 许可证的约束, 可以在以下链接找到该许可证. + * Use of this source code is governed by the GNU AGPLv3 license that can be found through the following link. * - * https://github.com/mamoe/mirai/blob/master/LICENSE + * https://github.com/mamoe/mirai/blob/dev/LICENSE */ package net.mamoe.mirai.internal.event diff --git a/mirai-core-api/src/jvmBaseMain/kotlin/internal/message/overwritePolymorphicWith.kt b/mirai-core-api/src/jvmBaseMain/kotlin/internal/message/overwritePolymorphicWith.kt new file mode 100644 index 000000000..12cff2a08 --- /dev/null +++ b/mirai-core-api/src/jvmBaseMain/kotlin/internal/message/overwritePolymorphicWith.kt @@ -0,0 +1,36 @@ +/* + * Copyright 2019-2022 Mamoe Technologies and contributors. + * + * 此源代码的使用受 GNU AFFERO GENERAL PUBLIC LICENSE version 3 许可证的约束, 可以在以下链接找到该许可证. + * Use of this source code is governed by the GNU AGPLv3 license that can be found through the following link. + * + * https://github.com/mamoe/mirai/blob/dev/LICENSE + */ + +package net.mamoe.mirai.internal.message + +import kotlinx.serialization.KSerializer +import kotlinx.serialization.modules.SerializersModule +import kotlinx.serialization.modules.overwriteWith +import kotlinx.serialization.modules.polymorphic +import net.mamoe.mirai.message.data.SingleMessage +import kotlin.reflect.KClass +import kotlin.reflect.full.allSuperclasses +import kotlin.reflect.full.isSubclassOf + +internal actual fun SerializersModule.overwritePolymorphicWith( + type: KClass, + serializer: KSerializer +): SerializersModule { + return overwriteWith(SerializersModule { + // contextual(type, serializer) + for (superclass in type.allSuperclasses) { + if (superclass.isFinal) continue + if (!superclass.isSubclassOf(SingleMessage::class)) continue + @Suppress("UNCHECKED_CAST") + polymorphic(superclass as KClass) { + subclass(type, serializer) + } + } + }) +} diff --git a/mirai-core-api/src/commonMain/kotlin/internal/utils/ExternalResourceLeakObserver.kt b/mirai-core-api/src/jvmBaseMain/kotlin/internal/utils/ExternalResourceLeakObserver.kt similarity index 98% rename from mirai-core-api/src/commonMain/kotlin/internal/utils/ExternalResourceLeakObserver.kt rename to mirai-core-api/src/jvmBaseMain/kotlin/internal/utils/ExternalResourceLeakObserver.kt index 8ffb70525..f656e52fd 100644 --- a/mirai-core-api/src/commonMain/kotlin/internal/utils/ExternalResourceLeakObserver.kt +++ b/mirai-core-api/src/jvmBaseMain/kotlin/internal/utils/ExternalResourceLeakObserver.kt @@ -1,5 +1,5 @@ /* - * Copyright 2019-2021 Mamoe Technologies and contributors. + * Copyright 2019-2022 Mamoe Technologies and contributors. * * 此源代码的使用受 GNU AFFERO GENERAL PUBLIC LICENSE version 3 许可证的约束, 可以在以下链接找到该许可证. * Use of this source code is governed by the GNU AGPLv3 license that can be found through the following link. diff --git a/mirai-core-api/src/commonMain/kotlin/internal/utils/LoggerAdapterImpls.kt b/mirai-core-api/src/jvmBaseMain/kotlin/internal/utils/LoggerAdapterImpls.kt similarity index 100% rename from mirai-core-api/src/commonMain/kotlin/internal/utils/LoggerAdapterImpls.kt rename to mirai-core-api/src/jvmBaseMain/kotlin/internal/utils/LoggerAdapterImpls.kt diff --git a/mirai-core-api/src/jvmBaseMain/kotlin/internal/utils/Marker.kt b/mirai-core-api/src/jvmBaseMain/kotlin/internal/utils/Marker.kt new file mode 100644 index 000000000..ea83b7c34 --- /dev/null +++ b/mirai-core-api/src/jvmBaseMain/kotlin/internal/utils/Marker.kt @@ -0,0 +1,21 @@ +/* + * Copyright 2019-2022 Mamoe Technologies and contributors. + * + * 此源代码的使用受 GNU AFFERO GENERAL PUBLIC LICENSE version 3 许可证的约束, 可以在以下链接找到该许可证. + * Use of this source code is governed by the GNU AGPLv3 license that can be found through the following link. + * + * https://github.com/mamoe/mirai/blob/dev/LICENSE + */ + +package net.mamoe.mirai.internal.utils + +import org.apache.logging.log4j.MarkerManager + +@Suppress("ACTUAL_WITHOUT_EXPECT") // visibility +internal actual typealias Marker = org.apache.logging.log4j.Marker + +internal actual object MarkerManager { + actual fun getMarker(name: String): Marker { + return MarkerManager.getMarker(name) + } +} \ No newline at end of file diff --git a/mirai-core-api/src/jvmBaseMain/kotlin/message/action/AsyncRecallResult.kt b/mirai-core-api/src/jvmBaseMain/kotlin/message/action/AsyncRecallResult.kt new file mode 100644 index 000000000..9895a22f2 --- /dev/null +++ b/mirai-core-api/src/jvmBaseMain/kotlin/message/action/AsyncRecallResult.kt @@ -0,0 +1,67 @@ +/* + * Copyright 2019-2022 Mamoe Technologies and contributors. + * + * 此源代码的使用受 GNU AFFERO GENERAL PUBLIC LICENSE version 3 许可证的约束, 可以在以下链接找到该许可证. + * Use of this source code is governed by the GNU AGPLv3 license that can be found through the following link. + * + * https://github.com/mamoe/mirai/blob/dev/LICENSE + */ + +@file:JvmBlockingBridge + +package net.mamoe.mirai.message.action + +import kotlinx.coroutines.CompletableDeferred +import kotlinx.coroutines.Deferred +import kotlinx.coroutines.future.asCompletableFuture +import me.him188.kotlin.jvm.blocking.bridge.JvmBlockingBridge +import net.mamoe.mirai.message.data.MessageSource +import net.mamoe.mirai.message.data.MessageSource.Key.recallIn +import java.util.concurrent.CompletableFuture + +/** + * [MessageSource.recallIn] 的结果. + * + * @see MessageSource.recallIn + */ +public actual class AsyncRecallResult internal actual constructor( + /** + * 撤回时产生的异常. Kotlin [Deferred] API. + */ + public actual val exception: Deferred, +) { + /** + * 撤回时产生的异常. Java [CompletableFuture] API. + */ + public val exceptionFuture: CompletableFuture by lazy { exception.asCompletableFuture() } + + /** + * 撤回是否成功. Kotlin [Deferred] API. + */ + public actual val isSuccess: Deferred by lazy { + CompletableDeferred().apply { + exception.invokeOnCompletion { + complete(it == null) + } + } + } + + /** + * 撤回是否成功. Java [CompletableFuture] API. + */ + public val isSuccessFuture: CompletableFuture by lazy { isSuccess.asCompletableFuture() } + + /** + * 等待撤回完成, 返回撤回时产生的异常. + */ + public actual suspend fun awaitException(): Throwable? { + return exception.await() + } + + /** + * 等待撤回完成, 返回撤回的结果. + */ + public actual suspend fun awaitIsSuccess(): Boolean { + return isSuccess.await() + } +} diff --git a/mirai-core-api/src/jvmBaseMain/kotlin/message/data/MessageChainJvm.kt b/mirai-core-api/src/jvmBaseMain/kotlin/message/data/MessageChainJvm.kt new file mode 100644 index 000000000..0334fb146 --- /dev/null +++ b/mirai-core-api/src/jvmBaseMain/kotlin/message/data/MessageChainJvm.kt @@ -0,0 +1,25 @@ +/* + * Copyright 2019-2022 Mamoe Technologies and contributors. + * + * 此源代码的使用受 GNU AFFERO GENERAL PUBLIC LICENSE version 3 许可证的约束, 可以在以下链接找到该许可证. + * Use of this source code is governed by the GNU AGPLv3 license that can be found through the following link. + * + * https://github.com/mamoe/mirai/blob/dev/LICENSE + */ + +@file:JvmMultifileClass +@file:JvmName("MessageUtils") + +package net.mamoe.mirai.message.data + +import java.util.stream.Stream +import kotlin.streams.asSequence + + +/** + * 扁平化 [this] 并创建一个 [MessageChain]. + */ +@JvmName("newChain") +public fun Stream.toMessageChain(): MessageChain = this.asSequence().toMessageChain() + + diff --git a/mirai-core-api/src/jvmBaseMain/kotlin/utils/BotConfiguration.kt b/mirai-core-api/src/jvmBaseMain/kotlin/utils/BotConfiguration.kt index 0838b497d..73559091c 100644 --- a/mirai-core-api/src/jvmBaseMain/kotlin/utils/BotConfiguration.kt +++ b/mirai-core-api/src/jvmBaseMain/kotlin/utils/BotConfiguration.kt @@ -645,12 +645,12 @@ public actual open class BotConfiguration { // open for Java /** 默认的配置实例. 可以进行修改 */ @JvmStatic public actual val Default: BotConfiguration = BotConfiguration() - } -} -internal fun BotConfiguration.getFileBasedDeviceInfoSupplier(file: () -> File): (Bot) -> DeviceInfo { - return { - @Suppress("DEPRECATION_ERROR") - file().loadAsDeviceInfo(json) + internal fun BotConfiguration.getFileBasedDeviceInfoSupplier(file: () -> File): (Bot) -> DeviceInfo { + return { + @Suppress("DEPRECATION_ERROR") + file().loadAsDeviceInfo(json) + } + } } } \ No newline at end of file diff --git a/mirai-core-api/src/jvmBaseMain/kotlin/utils/DeviceInfo.kt b/mirai-core-api/src/jvmBaseMain/kotlin/utils/DeviceInfo.kt index d3bbb4645..5c56ccb8e 100644 --- a/mirai-core-api/src/jvmBaseMain/kotlin/utils/DeviceInfo.kt +++ b/mirai-core-api/src/jvmBaseMain/kotlin/utils/DeviceInfo.kt @@ -9,7 +9,6 @@ package net.mamoe.mirai.utils -import io.ktor.utils.io.core.* import kotlinx.serialization.Serializable import kotlinx.serialization.Transient import kotlinx.serialization.json.Json @@ -50,13 +49,12 @@ public actual class DeviceInfo actual constructor( @MiraiInternalApi public actual val guid: ByteArray = generateGuid(androidId, macAddress) - @Suppress("ACTUAL_FUNCTION_WITH_DEFAULT_ARGUMENTS") // serializable @Serializable public actual class Version actual constructor( - public actual val incremental: ByteArray = "5891938".toByteArray(), - public actual val release: ByteArray = "10".toByteArray(), - public actual val codename: ByteArray = "REL".toByteArray(), - public actual val sdk: Int = 29 + public actual val incremental: ByteArray, + public actual val release: ByteArray, + public actual val codename: ByteArray, + public actual val sdk: Int ) { /** * @since 2.9 diff --git a/mirai-core-api/src/jvmBaseMain/kotlin/utils/ExternalResource.kt b/mirai-core-api/src/jvmBaseMain/kotlin/utils/ExternalResource.kt index 37cca19a5..9ebdf1cb8 100644 --- a/mirai-core-api/src/jvmBaseMain/kotlin/utils/ExternalResource.kt +++ b/mirai-core-api/src/jvmBaseMain/kotlin/utils/ExternalResource.kt @@ -22,7 +22,6 @@ import net.mamoe.mirai.contact.FileSupported import net.mamoe.mirai.contact.Group import net.mamoe.mirai.internal.utils.ExternalResourceImplByByteArray import net.mamoe.mirai.internal.utils.ExternalResourceImplByFile -import net.mamoe.mirai.internal.utils.inputStream import net.mamoe.mirai.message.MessageReceipt import net.mamoe.mirai.message.data.FileMessage import net.mamoe.mirai.message.data.Image @@ -31,10 +30,10 @@ import net.mamoe.mirai.message.data.toVoice import net.mamoe.mirai.utils.ExternalResource.Companion.sendAsImageTo import net.mamoe.mirai.utils.ExternalResource.Companion.toExternalResource import net.mamoe.mirai.utils.ExternalResource.Companion.uploadAsImage +import java.io.Closeable import java.io.File import java.io.InputStream import java.io.RandomAccessFile -import kotlin.io.inputStream import kotlin.io.use /** diff --git a/mirai-core-api/src/jvmBaseMain/kotlin/utils/FileCacheStrategy.kt b/mirai-core-api/src/jvmBaseMain/kotlin/utils/FileCacheStrategy.kt new file mode 100644 index 000000000..fdbd3bb21 --- /dev/null +++ b/mirai-core-api/src/jvmBaseMain/kotlin/utils/FileCacheStrategy.kt @@ -0,0 +1,123 @@ +/* + * Copyright 2019-2022 Mamoe Technologies and contributors. + * + * 此源代码的使用受 GNU AFFERO GENERAL PUBLIC LICENSE version 3 许可证的约束, 可以在以下链接找到该许可证. + * Use of this source code is governed by the GNU AGPLv3 license that can be found through the following link. + * + * https://github.com/mamoe/mirai/blob/dev/LICENSE + */ + +package net.mamoe.mirai.utils + +import io.ktor.utils.io.errors.* +import kotlinx.coroutines.Dispatchers +import net.mamoe.mirai.Bot +import net.mamoe.mirai.IMirai +import net.mamoe.mirai.utils.ExternalResource.Companion.sendAsImageTo +import net.mamoe.mirai.utils.ExternalResource.Companion.toExternalResource +import net.mamoe.mirai.utils.ExternalResource.Companion.uploadAsImage +import net.mamoe.mirai.utils.FileCacheStrategy.MemoryCache +import net.mamoe.mirai.utils.FileCacheStrategy.TempCache +import java.io.File +import java.io.InputStream + +/** + * 资源缓存策略. + * + * 由于上传资源时服务器要求提前给出 MD5 和文件大小等数据, 一些资源如 [InputStream] 需要首先缓存才能使用. + * + * 资源的缓存都是将 [InputStream] 缓存未 [ExternalResource]. 根据 [FileCacheStrategy] 实现不同, 可以以临时文件存储, 也可以在数据库或是内存按需存储. + * Mirai 内置的实现有 [内存存储][MemoryCache] 和 [临时文件存储][TempCache]. + * 操作 [ExternalResource.toExternalResource] 时将会使用 [IMirai.FileCacheStrategy]. 可以覆盖, 示例: + * ``` + * // Kotlin + * Mirai.FileCacheStrategy = FileCacheStrategy.TempCache() // 使用系统默认缓存路径, 也是默认的行为 + * Mirai.FileCacheStrategy = FileCacheStrategy.TempCache(File("C:/cache")) // 使用自定义缓存路径 + * + * // Java + * Mirai.getInstance().setFileCacheStrategy(new FileCacheStrategy.TempCache()); // 使用系统默认缓存路径, 也是默认的行为 + * Mirai.getInstance().setFileCacheStrategy(new FileCacheStrategy.TempCache(new File("C:/cache"))); // 使用自定义的缓存路径 + * ``` + * + * 此接口的实现和使用都是稳定的. 自行实现的 [FileCacheStrategy] 也可以被 Mirai 使用. + * + * 注意, 此接口目前仅缓存 [InputStream] 等一次性数据. 好友列表等数据由每个 [Bot] 的 [BotConfiguration.cacheDir] 缓存. + * + * ### 使用 [FileCacheStrategy] 的操作 + * - [ExternalResource.toExternalResource] + * - [ExternalResource.uploadAsImage] + * - [ExternalResource.sendAsImageTo] + * + * @see ExternalResource + */ +public actual interface FileCacheStrategy { + /** + * 立即读取 [input] 所有内容并缓存为 [ExternalResource]. + * + * 注意: + * - 此函数不会关闭输入 + * - 此函数可能会阻塞线程读取 [input] 内容, 若在 Kotlin 协程使用请确保在允许阻塞的环境 ([Dispatchers.IO]). + * + * @param formatName 文件类型. 此参数通常只会影响官方客户端接收到的文件的文件后缀. 若为 `null` 则会自动根据文件头识别. 识别失败时将使用 "mirai" + */ + @Throws(IOException::class) + public fun newCache(input: InputStream, formatName: String? = null): ExternalResource + + /** + * 立即读取 [input] 所有内容并缓存为 [ExternalResource]. 自动根据文件头识别文件类型. 识别失败时将使用 "mirai". + * + * 注意: + * - 此函数不会关闭输入 + * - 此函数可能会阻塞线程读取 [input] 内容, 若在 Kotlin 协程使用请确保在允许阻塞的环境 ([Dispatchers.IO]). + */ + @Throws(IOException::class) + public fun newCache(input: InputStream): ExternalResource = newCache(input, null) + + /** + * 使用内存直接存储所有图片文件. 由 JVM 执行 GC. + */ + public object MemoryCache : FileCacheStrategy { + @Throws(IOException::class) + override fun newCache(input: InputStream, formatName: String?): ExternalResource { + return input.readBytes().toExternalResource(formatName) + } + } + + /** + * 使用系统临时文件夹缓存图片文件. 在图片使用完毕后或 JVM 正常结束时删除临时文件. + */ + public class TempCache @JvmOverloads public constructor( + /** + * 缓存图片存放位置. 为 `null` 时使用主机系统的临时文件夹: `File.createTempFile("tmp", null, directory)` + */ + public val directory: File? = null, + ) : FileCacheStrategy { + private fun createTempFile(): File { + return File.createTempFile("tmp", null, directory) + } + + @Throws(IOException::class) + override fun newCache(input: InputStream, formatName: String?): ExternalResource { + val file = createTempFile() + return file.apply { + deleteOnExit() + outputStream().use { out -> input.copyTo(out) } + }.toExternalResource(formatName).apply { + closed.invokeOnCompletion { + kotlin.runCatching { file.delete() } + } + } + } + } + + public actual companion object { + /** + * 当前平台下默认的缓存策略. 注意, 这可能不是 Mirai 全局默认使用的, Mirai 从 [IMirai.FileCacheStrategy] 获取. + * + * @see IMirai.FileCacheStrategy + */ + @MiraiExperimentalApi + @JvmStatic + public actual val PlatformDefault: FileCacheStrategy = TempCache(null) + } +} diff --git a/mirai-core-api/src/jvmBaseMain/kotlin/utils/MiraiLogger.kt b/mirai-core-api/src/jvmBaseMain/kotlin/utils/MiraiLogger.kt index d11334b24..53ac41f7e 100644 --- a/mirai-core-api/src/jvmBaseMain/kotlin/utils/MiraiLogger.kt +++ b/mirai-core-api/src/jvmBaseMain/kotlin/utils/MiraiLogger.kt @@ -13,6 +13,7 @@ package net.mamoe.mirai.utils import net.mamoe.mirai.utils.* +import java.util.* import kotlin.reflect.KClass /** @@ -89,7 +90,7 @@ public actual interface MiraiLogger { @MiraiExperimentalApi @Deprecated("Deprecated.", level = DeprecationLevel.HIDDEN) // deprecated since 2.7 @DeprecatedSinceMirai(warningSince = "2.7", errorSince = "2.10", hiddenSince = "2.11") - public actual val TopLevel: MiraiLogger by lazy { Factory.create(MiraiLogger::class, "Mirai") } + public val TopLevel: MiraiLogger by lazy { Factory.create(MiraiLogger::class, "Mirai") } /** * 已弃用, 请实现 service [net.mamoe.mirai.utils.MiraiLogger.Factory] 并以 [ServiceLoader] 支持的方式提供. @@ -101,7 +102,7 @@ public actual interface MiraiLogger { ) // deprecated since 2.7 @JvmStatic @DeprecatedSinceMirai(warningSince = "2.7", errorSince = "2.10") // left ERROR intentionally, for internal uses. - public actual fun setDefaultLoggerCreator(creator: (identity: String?) -> MiraiLogger) { + public fun setDefaultLoggerCreator(creator: (identity: String?) -> MiraiLogger) { DefaultFactoryOverrides.override { _, identity -> creator(identity) } } @@ -118,7 +119,7 @@ public actual interface MiraiLogger { ) // deprecated since 2.7 @JvmStatic @DeprecatedSinceMirai(warningSince = "2.7", errorSince = "2.10", hiddenSince = "2.11") - public actual fun create(identity: String?): MiraiLogger = Factory.create(MiraiLogger::class, identity) + public fun create(identity: String?): MiraiLogger = Factory.create(MiraiLogger::class, identity) } /** @@ -193,20 +194,10 @@ public actual interface MiraiLogger { */ public actual val isErrorEnabled: Boolean get() = isEnabled - /** - * 随从. 在 this 中调用所有方法后都应继续往 [follower] 传递调用. - * [follower] 的存在可以让一次日志被多个日志记录器记录. - * - * 一般不建议直接修改这个属性. 请通过 [plus] 来连接两个日志记录器. - * 如: `val logger = bot.logger + MyLogger()` - * 当调用 `logger.info()` 时, `bot.logger` 会首先记录, `MyLogger` 会随后记录. - * - * 当然, 多个 logger 也可以加在一起: `val logger = bot.logger + MynLogger() + MyLogger2()` - */ @Suppress("UNUSED_PARAMETER") @Deprecated("follower 设计不佳, 请避免使用", level = DeprecationLevel.HIDDEN) // deprecated since 2.7 @DeprecatedSinceMirai(warningSince = "2.7", errorSince = "2.10", hiddenSince = "2.11") - public actual var follower: MiraiLogger? + public var follower: MiraiLogger? get() = null set(value) {} @@ -258,22 +249,10 @@ public actual interface MiraiLogger { public actual fun call(priority: SimpleLogger.LogPriority, message: String?, e: Throwable?): Unit = priority.correspondingFunction(this, message, e) - /** - * 添加一个 [follower], 返回 [follower] - * 它只会把 `this` 的属性 [MiraiLogger.follower] 修改为这个函数的参数 [follower], 然后返回这个参数. - * 若 [MiraiLogger.follower] 已经有值, 则会替换掉这个值. - * ``` - * +------+ +----------+ +----------+ +----------+ - * | base | <-- | follower | <-- | follower | <-- | follower | - * +------+ +----------+ +----------+ +----------+ - * ``` - * - * @return [follower] - */ @Suppress("DeprecatedCallableAddReplaceWith") @Deprecated("plus 设计不佳, 请避免使用.", level = DeprecationLevel.HIDDEN) // deprecated since 2.7 @DeprecatedSinceMirai(warningSince = "2.7", errorSince = "2.10", hiddenSince = "2.11") - public actual operator fun plus(follower: T): T = follower + public operator fun plus(follower: T): T = follower } diff --git a/mirai-core-api/src/commonTest/kotlin/logging/Log4j2LoggingTest.kt b/mirai-core-api/src/jvmBaseTest/kotlin/logging/Log4j2LoggingTest.kt similarity index 96% rename from mirai-core-api/src/commonTest/kotlin/logging/Log4j2LoggingTest.kt rename to mirai-core-api/src/jvmBaseTest/kotlin/logging/Log4j2LoggingTest.kt index 16ba3fb80..9d9311eef 100644 --- a/mirai-core-api/src/commonTest/kotlin/logging/Log4j2LoggingTest.kt +++ b/mirai-core-api/src/jvmBaseTest/kotlin/logging/Log4j2LoggingTest.kt @@ -1,5 +1,5 @@ /* - * Copyright 2019-2021 Mamoe Technologies and contributors. + * Copyright 2019-2022 Mamoe Technologies and contributors. * * 此源代码的使用受 GNU AFFERO GENERAL PUBLIC LICENSE version 3 许可证的约束, 可以在以下链接找到该许可证. * Use of this source code is governed by the GNU AGPLv3 license that can be found through the following link. @@ -17,13 +17,14 @@ import net.mamoe.mirai.utils.MiraiLogger import org.apache.logging.log4j.LogManager import org.junit.jupiter.api.AfterEach import org.junit.jupiter.api.BeforeEach +import kotlin.test.BeforeTest import kotlin.test.Test import kotlin.test.assertEquals import kotlin.test.assertIs import kotlin.test.assertSame internal class Log4j2LoggingTest { - @BeforeEach + @BeforeTest fun init() { DefaultFactoryOverrides.override { requester, identity -> LogManager.getLogger(requester).asMiraiLogger(Marker(identity ?: requester.simpleName, MARKER_MIRAI)) diff --git a/mirai-core-api/src/commonTest/kotlin/logging/LoggingCompatibilityTest.kt b/mirai-core-api/src/jvmBaseTest/kotlin/logging/LoggingCompatibilityTest.kt similarity index 94% rename from mirai-core-api/src/commonTest/kotlin/logging/LoggingCompatibilityTest.kt rename to mirai-core-api/src/jvmBaseTest/kotlin/logging/LoggingCompatibilityTest.kt index 6bb29d182..d8b252f69 100644 --- a/mirai-core-api/src/commonTest/kotlin/logging/LoggingCompatibilityTest.kt +++ b/mirai-core-api/src/jvmBaseTest/kotlin/logging/LoggingCompatibilityTest.kt @@ -1,5 +1,5 @@ /* - * Copyright 2019-2021 Mamoe Technologies and contributors. + * Copyright 2019-2022 Mamoe Technologies and contributors. * * 此源代码的使用受 GNU AFFERO GENERAL PUBLIC LICENSE version 3 许可证的约束, 可以在以下链接找到该许可证. * Use of this source code is governed by the GNU AGPLv3 license that can be found through the following link. diff --git a/mirai-core-api/src/commonTest/kotlin/message.data/MessageChainImmutableTest.kt b/mirai-core-api/src/jvmBaseTest/kotlin/message/data/MessageChainImmutableTest.kt similarity index 65% rename from mirai-core-api/src/commonTest/kotlin/message.data/MessageChainImmutableTest.kt rename to mirai-core-api/src/jvmBaseTest/kotlin/message/data/MessageChainImmutableTest.kt index e7da476fd..f1f86f53d 100644 --- a/mirai-core-api/src/commonTest/kotlin/message.data/MessageChainImmutableTest.kt +++ b/mirai-core-api/src/jvmBaseTest/kotlin/message/data/MessageChainImmutableTest.kt @@ -1,5 +1,5 @@ /* - * Copyright 2019-2021 Mamoe Technologies and contributors. + * Copyright 2019-2022 Mamoe Technologies and contributors. * * 此源代码的使用受 GNU AFFERO GENERAL PUBLIC LICENSE version 3 许可证的约束, 可以在以下链接找到该许可证. * Use of this source code is governed by the GNU AGPLv3 license that can be found through the following link. @@ -11,32 +11,32 @@ package net.mamoe.mirai.message.data -import org.junit.jupiter.api.Test +import kotlin.test.Test import kotlin.test.assertFails -import java.util.List as JdkList internal open class MessageChainImmutableTest { - fun msg0(): MessageChain = messageChainOf( - AtAll, PlainText("Hello!"), At(114514), - ) - fun msgAsJdk(): JdkList { - return msg0() as java.util.List + @Test + fun `LinearMessageChainImpl is immutable`() { + runCheck( + messageChainOf( + AtAll, PlainText("Hello!"), At(114514), + ) as java.util.List + ) } @Test - fun `direct access`() { - val chain = msgAsJdk() + fun `CombinedMessage is immutable`() { + runCheck( + (AtAll + PlainText("Hello!")) as java.util.List, + ) + } + private fun runCheck(chain: java.util.List) { assertFails { chain.set(0, AtAll) } assertFails { chain.remove(0) } assertFails { chain.clear() } assertFails { chain.add(PlainText("Hey Hey!")) } - } - - @Test - fun `iterator access`() { - val chain = msgAsJdk() assertFails { chain.iterator().remove() } assertFails { chain.iterator().also { it.next() }.remove() } assertFails { chain.listIterator().remove() } diff --git a/mirai-core-api/src/jvmBaseTest/kotlin/package.kt b/mirai-core-api/src/jvmBaseTest/kotlin/package.kt new file mode 100644 index 000000000..8bb3a229f --- /dev/null +++ b/mirai-core-api/src/jvmBaseTest/kotlin/package.kt @@ -0,0 +1,9 @@ +/* + * Copyright 2019-2022 Mamoe Technologies and contributors. + * + * 此源代码的使用受 GNU AFFERO GENERAL PUBLIC LICENSE version 3 许可证的约束, 可以在以下链接找到该许可证. + * Use of this source code is governed by the GNU AGPLv3 license that can be found through the following link. + * + * https://github.com/mamoe/mirai/blob/dev/LICENSE + */ +package net.mamoe.mirai \ No newline at end of file diff --git a/mirai-core-api/src/jvmBaseTest/kotlin/utils/JvmDeviceInfoTest.kt b/mirai-core-api/src/jvmBaseTest/kotlin/utils/JvmDeviceInfoTest.kt new file mode 100644 index 000000000..0272b6d58 --- /dev/null +++ b/mirai-core-api/src/jvmBaseTest/kotlin/utils/JvmDeviceInfoTest.kt @@ -0,0 +1,41 @@ +/* + * Copyright 2019-2022 Mamoe Technologies and contributors. + * + * 此源代码的使用受 GNU AFFERO GENERAL PUBLIC LICENSE version 3 许可证的约束, 可以在以下链接找到该许可证. + * Use of this source code is governed by the GNU AGPLv3 license that can be found through the following link. + * + * https://github.com/mamoe/mirai/blob/dev/LICENSE + */ + +package net.mamoe.mirai.utils + +import kotlinx.serialization.json.Json +import net.mamoe.mirai.utils.DeviceInfo.Companion.loadAsDeviceInfo +import kotlin.test.Test +import org.junit.jupiter.api.io.TempDir +import java.io.File +import kotlin.test.assertEquals + +class JvmDeviceInfoTest { + + @TempDir + lateinit var dir: File + + @Test + fun `can write and read v2`() { + val device = DeviceInfo.random() + val file = dir.resolve("device.json") + + file.writeText(DeviceInfoManager.serialize(device)) + assertEquals(device, file.loadAsDeviceInfo()) + } + + @Test + fun `can read legacy v1`() { + val device = DeviceInfo.random() + val file = dir.resolve("device.json") + + file.writeText(Json.encodeToString(DeviceInfo.serializer(), device)) + assertEquals(device, file.loadAsDeviceInfo()) + } +} \ No newline at end of file diff --git a/mirai-core-api/src/jvmTest/kotlin/message/data/MessageChainImmutableTest_JDK8.kt b/mirai-core-api/src/jvmTest/kotlin/message/data/MessageChainImmutableTestJdk8.kt similarity index 73% rename from mirai-core-api/src/jvmTest/kotlin/message/data/MessageChainImmutableTest_JDK8.kt rename to mirai-core-api/src/jvmTest/kotlin/message/data/MessageChainImmutableTestJdk8.kt index 213c3cd80..00808a26f 100644 --- a/mirai-core-api/src/jvmTest/kotlin/message/data/MessageChainImmutableTest_JDK8.kt +++ b/mirai-core-api/src/jvmTest/kotlin/message/data/MessageChainImmutableTestJdk8.kt @@ -1,5 +1,5 @@ /* - * Copyright 2019-2021 Mamoe Technologies and contributors. + * Copyright 2019-2022 Mamoe Technologies and contributors. * * 此源代码的使用受 GNU AFFERO GENERAL PUBLIC LICENSE version 3 许可证的约束, 可以在以下链接找到该许可证. * Use of this source code is governed by the GNU AGPLv3 license that can be found through the following link. @@ -11,13 +11,13 @@ package net.mamoe.mirai.message.data -import org.junit.jupiter.api.Test +import kotlin.test.Test import kotlin.test.assertFails -internal class MessageChainImmutableTest_JDK8 : MessageChainImmutableTest() { +internal class MessageChainImmutableTestJdk8 : MessageChainImmutableTest() { @Test fun `access with JDK8 lambda`() { - val chain = msgAsJdk() + val chain = messageChainOf(AtAll, PlainText("Hello!"), At(114514)) as java.util.List assertFails { chain.removeIf { true } } assertFails { chain.replaceAll { AtAll } } assertFails { chain.sort { o1, o2 -> o1.javaClass.name.compareTo(o2.javaClass.name) } } diff --git a/mirai-core-api/src/nativeMain/kotlin/contact/Contact.kt b/mirai-core-api/src/nativeMain/kotlin/contact/Contact.kt new file mode 100644 index 000000000..acb15735e --- /dev/null +++ b/mirai-core-api/src/nativeMain/kotlin/contact/Contact.kt @@ -0,0 +1,107 @@ +/* + * Copyright 2019-2022 Mamoe Technologies and contributors. + * + * 此源代码的使用受 GNU AFFERO GENERAL PUBLIC LICENSE version 3 许可证的约束, 可以在以下链接找到该许可证. + * Use of this source code is governed by the GNU AGPLv3 license that can be found through the following link. + * + * https://github.com/mamoe/mirai/blob/dev/LICENSE + */ + +package net.mamoe.mirai.contact + +import io.ktor.utils.io.core.* +import kotlinx.coroutines.CoroutineScope +import net.mamoe.mirai.Bot +import net.mamoe.mirai.event.events.* +import net.mamoe.mirai.message.MessageReceipt +import net.mamoe.mirai.message.data.* +import net.mamoe.mirai.utils.ExternalResource +import net.mamoe.mirai.utils.ExternalResource.Companion.uploadAsImage +import net.mamoe.mirai.utils.NotStableForInheritance +import net.mamoe.mirai.utils.OverFileSizeMaxException +import kotlin.coroutines.cancellation.CancellationException + +/** + * 联系对象, 即可以与 [Bot] 互动的对象. 包含 [用户][User], 和 [群][Group]. + */ +@NotStableForInheritance +public actual interface Contact : ContactOrBot, CoroutineScope { + /** + * 这个联系对象所属 [Bot]. + */ + public actual override val bot: Bot + + /** + * 可以是 QQ 号码或者群号码. + * + * @see User.id + * @see Group.id + */ + public actual override val id: Long + + /** + * 向这个对象发送消息. + * + * 单条消息最大可发送 4500 字符或 50 张图片. + * + * @see MessagePreSendEvent 发送消息前事件 + * @see MessagePostSendEvent 发送消息后事件 + * + * @throws EventCancelledException 当发送消息事件被取消时抛出 + * @throws BotIsBeingMutedException 发送群消息时若 [Bot] 被禁言抛出 + * @throws MessageTooLargeException 当消息过长时抛出 + * @throws IllegalArgumentException 当消息内容为空时抛出 (详见 [Message.isContentEmpty]) + * + * @return 消息回执. 可 [引用][MessageReceipt.quote] 或 [撤回][MessageReceipt.recall] 这条消息. + */ + public actual suspend fun sendMessage(message: Message): MessageReceipt + + /** + * 发送纯文本消息 + * @see sendMessage + */ + public actual suspend fun sendMessage(message: String): MessageReceipt = sendMessage(message.toPlainText()) + + /** + * 上传一个 [资源][ExternalResource] 作为图片以备发送. + * + * **无论上传是否成功都不会关闭 [resource]. 需要调用方手动关闭资源** + * + * 也可以使用其他扩展: [ExternalResource.uploadAsImage] 使用 [Input] 等上传. + * + * @see Image 查看有关图片的更多信息, 如上传图片 + * + * @see BeforeImageUploadEvent 图片发送前事件, 可拦截. + * @see ImageUploadEvent 图片发送完成事件, 不可拦截. + * + * @see ExternalResource + * + * @throws EventCancelledException 当发送消息事件被取消时抛出 + * @throws OverFileSizeMaxException 当图片文件过大而被服务器拒绝上传时抛出. (最大大小约为 20 MB, 但 mirai 限制的大小为 30 MB) + */ + public actual suspend fun uploadImage(resource: ExternalResource): Image + + public actual companion object { + /** + * 将资源作为单独的图片消息发送给 [this] + * + * @see Contact.sendMessage 最终调用, 发送消息. + */ + public actual suspend fun C.sendImage(resource: ExternalResource): MessageReceipt { + return this.uploadImage(resource).sendTo(this) + } + + /** + * 将文件作为图片上传, 但不发送 + * @throws OverFileSizeMaxException + */ + @kotlin.internal.LowPriorityInOverloadResolution + @Suppress("INVISIBLE_MEMBER", "INVISIBLE_REFERENCE", "EXTENSION_SHADOWED_BY_MEMBER") + @Throws(OverFileSizeMaxException::class, CancellationException::class) + public actual suspend fun Contact.uploadImage(resource: ExternalResource): Image { + return uploadImage(resource) + } + + } + +} \ No newline at end of file diff --git a/mirai-core-api/src/nativeMain/kotlin/contact/file/AbsoluteFolder.kt b/mirai-core-api/src/nativeMain/kotlin/contact/file/AbsoluteFolder.kt new file mode 100644 index 000000000..05981121a --- /dev/null +++ b/mirai-core-api/src/nativeMain/kotlin/contact/file/AbsoluteFolder.kt @@ -0,0 +1,153 @@ +/* + * Copyright 2019-2022 Mamoe Technologies and contributors. + * + * 此源代码的使用受 GNU AFFERO GENERAL PUBLIC LICENSE version 3 许可证的约束, 可以在以下链接找到该许可证. + * Use of this source code is governed by the GNU AGPLv3 license that can be found through the following link. + * + * https://github.com/mamoe/mirai/blob/dev/LICENSE + */ + +package net.mamoe.mirai.contact.file + +import kotlinx.coroutines.flow.Flow +import net.mamoe.mirai.contact.PermissionDeniedException +import net.mamoe.mirai.utils.ExternalResource +import net.mamoe.mirai.utils.NotStableForInheritance +import net.mamoe.mirai.utils.ProgressionCallback + +/** + * 绝对目录标识. 精确表示一个远程目录. 不会受同名文件或目录的影响. + * + * @since 2.8 + * @see RemoteFiles + * @see AbsoluteFile + * @see AbsoluteFileFolder + */ +@Suppress("SEALED_INHERITOR_IN_DIFFERENT_MODULE") +@NotStableForInheritance +public actual interface AbsoluteFolder : AbsoluteFileFolder { + /** + * 当前快照中文件数量, 当有文件更新时(上传/删除文件) 该属性不会更新. + * + * 只可能通过 [refresh] 手动刷新 + * + * 特别的, 若该目录表示根目录, [contentsCount] 返回 `0`. (无法快速获取) + */ + public actual val contentsCount: Int + + /** + * 当该目录为空时返回 `true`. + */ + public actual fun isEmpty(): Boolean = contentsCount == 0 + + /** + * 返回更新了文件或目录信息 ([lastModifiedTime] 等) 的, 指向相同文件的 [AbsoluteFileFolder]. + * 不会更新当前 [AbsoluteFileFolder] 对象. + * + * 当远程文件或目录不存在时返回 `null`. + * + * 该函数会遍历上级目录的所有文件并匹配当前文件, 因此可能会非常慢, 请不要频繁使用. + */ + actual override suspend fun refreshed(): AbsoluteFolder? + + /////////////////////////////////////////////////////////////////////////// + // list children + /////////////////////////////////////////////////////////////////////////// + + /** + * 获取该目录下所有子目录列表. + */ + public actual suspend fun folders(): Flow + + /** + * 获取该目录下所有文件列表. + */ + public actual suspend fun files(): Flow + + + /** + * 获取该目录下所有文件和子目录列表. + */ + public actual suspend fun children(): Flow + + /////////////////////////////////////////////////////////////////////////// + // resolve and upload + /////////////////////////////////////////////////////////////////////////// + + /** + * 创建一个名称为 [name] 的子目录. 返回成功创建的或已有的子目录. 当目标目录已经存在时则直接返回该目录. + * + * @throws IllegalArgumentException 当 [name] 为空或包含非法字符 (`:*?"<>|`) 时抛出 + * @throws PermissionDeniedException 当权限不足时抛出 + */ + public actual suspend fun createFolder(name: String): AbsoluteFolder + + /** + * 获取一个已存在的名称为 [name] 的子目录. 当该名称的子目录不存在时返回 `null`. + * + * @throws IllegalArgumentException 当 [name] 为空或包含非法字符 (`:*?"<>|`) 时抛出 + */ + public actual suspend fun resolveFolder(name: String): AbsoluteFolder? + + /** + * 获取一个已存在的 [AbsoluteFileFolder.id] 为 [id] 的子目录. 当该名称的子目录不存在时返回 `null`. + * + * @throws IllegalArgumentException 当 [id] 为空或无效时抛出 + * + * @since 2.9.0 + */ + public actual suspend fun resolveFolderById(id: String): AbsoluteFolder? + + /** + * 精确获取 [AbsoluteFile.id] 为 [id] 的文件. 在目标文件不存在时返回 `null`. 当 [deep] 为 `true` 时还会深入子目录查找. + */ + public actual suspend fun resolveFileById( + id: String, + deep: Boolean + ): AbsoluteFile? + + /** + * 根据路径获取指向的所有路径为 [path] 的文件列表. 同时支持相对路径和绝对路径. 支持获取子目录内的文件. + */ + public actual suspend fun resolveFiles( + path: String + ): Flow + + /** + * 根据路径获取指向的所有路径为 [path] 的文件和目录列表. 同时支持相对路径和绝对路径. 支持获取子目录内的文件和目录. + */ + public actual suspend fun resolveAll( + path: String + ): Flow + + /** + * 上传一个文件到该目录, 返回上传成功的文件标识. + * + * 会在必要时尝试创建远程目录. + * + * ### [filepath] + * + * - 可以是 `foo.txt` 表示该目录下的文件 "foo.txt" + * - 也可以是 `sub/foo.txt` 表示该目录的子目录 "sub" 下的文件 "foo.txt". + * - 或是绝对路径 `/sub/foo.txt` 表示根目录的 "sub" 目录下的文件 "foo.txt" + * + * @param filepath 目标文件名 + * @param content 文件内容 + * @param callback 下载进度回调, 传递的 `progression` 是已下载字节数. + * + * @throws PermissionDeniedException 当无管理员权限时抛出 (若群仅允许管理员上传) + */ + public actual suspend fun uploadNewFile( + filepath: String, + content: ExternalResource, + callback: ProgressionCallback?, + ): AbsoluteFile + + public actual companion object { + /** + * 根目录 folder ID. + * @see id + */ + public actual const val ROOT_FOLDER_ID: String = "/" + } +} \ No newline at end of file diff --git a/mirai-core-api/src/nativeMain/kotlin/contact/roaming/RoamingMessages.kt b/mirai-core-api/src/nativeMain/kotlin/contact/roaming/RoamingMessages.kt new file mode 100644 index 000000000..473bc3417 --- /dev/null +++ b/mirai-core-api/src/nativeMain/kotlin/contact/roaming/RoamingMessages.kt @@ -0,0 +1,72 @@ +/* + * Copyright 2019-2022 Mamoe Technologies and contributors. + * + * 此源代码的使用受 GNU AFFERO GENERAL PUBLIC LICENSE version 3 许可证的约束, 可以在以下链接找到该许可证. + * Use of this source code is governed by the GNU AGPLv3 license that can be found through the following link. + * + * https://github.com/mamoe/mirai/blob/dev/LICENSE + */ + +package net.mamoe.mirai.contact.roaming + +import kotlinx.coroutines.flow.Flow +import net.mamoe.mirai.contact.Friend +import net.mamoe.mirai.message.data.MessageChain +import net.mamoe.mirai.message.data.MessageSource + + +/** + * 漫游消息记录管理器. 可通过 [RoamingSupported.roamingMessages] 获得. 目前仅 [Friend] 实现 [RoamingSupported]. + * + * @since 2.8 + * @see RoamingSupported + */ +public actual interface RoamingMessages { + /////////////////////////////////////////////////////////////////////////// + // Get list + /////////////////////////////////////////////////////////////////////////// + + /** + * 查询指定时间段内的漫游消息记录. + * + * 返回查询到的漫游消息记录, 顺序为由新到旧. 这些 [MessageChain] 与从事件中收到的消息链相似, 属于在线消息. + * 可从 [MessageChain] 获取 [MessageSource] 来确定发送人等相关信息, 也可以进行引用回复或撤回. + * + * 注意, 返回的消息记录既包含机器人发送给目标用户的消息, 也包含目标用户发送给机器人的消息. + * 可通过 [MessageChain] 获取 [MessageSource] (用法为 `messageChain.source`), 判断 [MessageSource.fromId] (发送人). + * 消息的其他*元数据*信息也要通过 [MessageSource] 获取 (如 [MessageSource.time] 获取时间). + * + * 若只需要获取单向消息 (机器人发送给目标用户的消息或反之), 可使用 [RoamingMessageFilter.SENT] 或 [RoamingMessageFilter.RECEIVED] 作为 [filter] 参数传递. + * + * 性能提示: 请在 [filter] 执行筛选, 若 [filter] 返回 `false` 则不会解析消息链, 这对本函数的处理速度有决定性影响. + * + * @param timeStart 起始时间, UTC+8 时间戳, 单位为秒. 可以为 `0`, 即表示从可以获取的最早的消息起. 负数将会被看是 `0`. + * @param timeEnd 结束时间, UTC+8 时间戳, 单位为秒. 可以为 [Long.MAX_VALUE], 即表示到可以获取的最晚的消息为止. 低于 [timeStart] 的值将会被看作是 [timeStart] 的值. + * @param filter 过滤器. + */ + public actual suspend fun getMessagesIn( + timeStart: Long, + timeEnd: Long, + filter: RoamingMessageFilter? + ): Flow + + /** + * 查询所有漫游消息记录. + * + * 返回查询到的漫游消息记录, 顺序为由新到旧. 这些 [MessageChain] 与从事件中收到的消息链相似, 属于在线消息. + * 可从 [MessageChain] 获取 [MessageSource] 来确定发送人等相关信息, 也可以进行引用回复或撤回. + * + * 注意, 返回的消息记录既包含机器人发送给目标用户的消息, 也包含目标用户发送给机器人的消息. + * 可通过 [MessageChain] 获取 [MessageSource] (用法为 `messageChain.source`), 判断 [MessageSource.fromId] (发送人). + * 消息的其他*元数据*信息也要通过 [MessageSource] 获取 (如 [MessageSource.time] 获取时间). + * + * 若只需要获取单向消息 (机器人发送给目标用户的消息或反之), 可使用 [RoamingMessageFilter.SENT] 或 [RoamingMessageFilter.RECEIVED] 作为 [filter] 参数传递. + * + * 性能提示: 请在 [filter] 执行筛选, 若 [filter] 返回 `false` 则不会解析消息链, 这对本函数的处理速度有决定性影响. + * + * @param filter 过滤器. + */ + public actual suspend fun getAllMessages( + filter: RoamingMessageFilter? + ): Flow = getMessagesIn(0, Long.MAX_VALUE, filter) +} \ No newline at end of file diff --git a/mirai-core-api/src/nativeMain/kotlin/event/EventChannel.kt b/mirai-core-api/src/nativeMain/kotlin/event/EventChannel.kt new file mode 100644 index 000000000..890bd10b1 --- /dev/null +++ b/mirai-core-api/src/nativeMain/kotlin/event/EventChannel.kt @@ -0,0 +1,528 @@ +/* + * Copyright 2019-2022 Mamoe Technologies and contributors. + * + * 此源代码的使用受 GNU AFFERO GENERAL PUBLIC LICENSE version 3 许可证的约束, 可以在以下链接找到该许可证. + * Use of this source code is governed by the GNU AGPLv3 license that can be found through the following link. + * + * https://github.com/mamoe/mirai/blob/dev/LICENSE + */ + +package net.mamoe.mirai.event + +import kotlinx.coroutines.* +import kotlinx.coroutines.channels.Channel +import kotlinx.coroutines.channels.ClosedSendChannelException +import kotlinx.coroutines.channels.SendChannel +import kotlinx.coroutines.flow.Flow +import kotlinx.coroutines.flow.SharedFlow +import kotlinx.coroutines.flow.filter +import kotlinx.coroutines.sync.Mutex +import net.mamoe.mirai.Bot +import net.mamoe.mirai.IMirai +import net.mamoe.mirai.event.ConcurrencyKind.CONCURRENT +import net.mamoe.mirai.event.ConcurrencyKind.LOCKED +import net.mamoe.mirai.event.events.BotEvent +import net.mamoe.mirai.utils.* +import kotlin.coroutines.CoroutineContext +import kotlin.reflect.KClass + +/** + * 事件通道. + * + * 事件通道是监听事件的入口, 但不负责广播事件. 要广播事件, 使用 [Event.broadcast] 或 [IMirai.broadcastEvent]. + * + * ## 获取事件通道 + * + * [EventChannel] 不可自行构造, 只能通过 [GlobalEventChannel], [BotEvent], 或基于一个通道的过滤等操作获得. + * + * ### 全局事件通道 + * + * [GlobalEventChannel] 是单例对象, 表示全局事件通道, 可以获取到在其中广播的所有事件. + * + * ### [BotEvent] 事件通道 + * + * 若只需要监听某个 [Bot] 的事件, 可通过 [Bot.eventChannel] 获取到这样的 [EventChannel]. + * + * ## 通道操作 + * + * ### 对通道的操作 + * - 过滤通道: 通过 [EventChannel.filter]. 例如 `filter { it is BotEvent }` 得到一个只能监听到 [BotEvent] 的事件通道. + * - 转换为 Kotlin 协程 [Channel]: [EventChannel.forwardToChannel] + * - 添加 [CoroutineContext]: [context], [parentJob], [parentScope], [exceptionHandler] + * + * ### 创建事件监听 + * - [EventChannel.subscribe] 创建带条件的一个事件监听器. + * - [EventChannel.subscribeAlways] 创建一个总是监听事件的事件监听器. + * - [EventChannel.subscribeOnce] 创建一个只监听单次的事件监听器. + * + * ### 监听器生命周期 + * + * 阅读 [EventChannel.subscribe] 以获取监听器生命周期相关信息. + * + * ## 与 kotlinx-coroutines 交互 + * + * mirai [EventChannel] 设计比 kotlinx-coroutines 的 [Flow] 稳定版更早. + * [EventChannel] 的功能与 [Flow] 类似, 不过 [EventChannel] 在 [subscribe] (类似 [Flow.collect]) 时有优先级判定, 也允许[拦截][Event.intercept]. + * + * ### 通过 [Flow] 接收事件 + * + * 使用 [EventChannel.asFlow] 获得 [Flow], 然后可使用 [Flow.collect] 等操作. + * + * ### 转发事件到 [SendChannel] + * + * 使用 [EventChannel.forwardToChannel] 可将事件转发到指定 [SendChannel]. + */ +@NotStableForInheritance // since 2.12, before it was `final class`. +public actual abstract class EventChannel @MiraiInternalApi public actual constructor( + public actual val baseEventClass: KClass, + /** + * 此事件通道的默认 [CoroutineScope.coroutineContext]. 将会被添加给所有注册的事件监听器. + */ + public actual val defaultCoroutineContext: CoroutineContext, +) { + + /** + * 创建事件监听并将监听结果转发到 [channel]. 当 [Channel.send] 抛出 [ClosedSendChannelException] 时停止 [Listener] 监听和转发. + * + * 返回创建的会转发监听到的所有事件到 [channel] 的[事件监听器][Listener]. [停止][Listener.complete] 该监听器会停止转发, 不会影响目标 [channel]. + * + * 若 [Channel.send] 挂起, 则监听器也会挂起, 也就可能会导致事件广播过程挂起. + * + * 示例: + * + * ``` + * val eventChannel: EventChannel = ... + * val channel = Channel() // kotlinx.coroutines.channels.Channel + * eventChannel.forwardToChannel(channel, priority = ...) + * + * // 其他地方 + * val event: BotEvent = channel.receive() // 挂起并接收一个事件 + * ``` + * + * @see subscribeAlways + * @see Channel + * @since 2.10 + */ + public actual fun forwardToChannel( + channel: SendChannel<@UnsafeVariance BaseEvent>, + coroutineContext: CoroutineContext, + priority: EventPriority, + ): Listener<@UnsafeVariance BaseEvent> { + // keep this LOCKED, otherwise compiler will choose the 'inline subscribe' which takes no KClass arg. + return subscribe(baseEventClass, coroutineContext, LOCKED, priority) { + try { + channel.send(it) + ListeningStatus.LISTENING + } catch (_: ClosedSendChannelException) { + ListeningStatus.STOPPED + } + } + } + + /** + * 通过 [Flow] 接收此通道内的所有事件. + * + * ``` + * val eventChannel: EventChannel = ... + * val flow: Flow = eventChannel.asFlow() + * + * flow.collect { // it + * // + * } + * + * flow.filterIsInstance.collect { // it: GroupMessageEvent + * // 处理事件 ... + * } + * + * flow.filterIsInstance.collect { // it: FriendMessageEvent + * // 处理事件 ... + * } + * ``` + * + * 类似于 [SharedFlow], [EventChannel.asFlow] 返回的 [Flow] 永远都不会停止. 因此上述示例 [Flow.collect] 永远都不会正常 (以抛出异常之外的) 结束. + * + * 通过 [asFlow] 接收事件相当于通过 [subscribeAlways] 以 [EventPriority.MONITOR] 监听事件. + * + * **注意**: [context], [parentJob] 等控制 [EventChannel.defaultCoroutineContext] 的操作对 [asFlow] 无效. 因为 [asFlow] 并不创建协程. + * + * @see Flow + * @since 2.12 + */ + public actual abstract fun asFlow(): Flow + + // region transforming operations + + /** + * 添加一个过滤器. 过滤器将在收到任何事件之后, 传递给通过 [EventChannel.subscribe] 注册的监听器之前调用. + * + * 若 [filter] 返回 `true`, 该事件将会被传给监听器. 否则将会被忽略, **监听器继续监听**. + * + * ## 线性顺序 + * 多个 [filter] 的处理是线性且有顺序的. 若一个 [filter] 已经返回了 `false` (代表忽略这个事件), 则会立即忽略, 而不会传递给后续过滤器. + * + * 示例: + * ``` + * GlobalEventChannel // GlobalEventChannel 会收到全局所有事件, 事件类型是 Event + * .filterIsInstance() // 过滤, 只接受 BotEvent + * .filter { event: BotEvent -> + * // 此时的 event 一定是 BotEvent + * event.bot.id == 123456 // 再过滤 event 的 bot.id + * } + * .subscribeAlways { event: BotEvent -> + * // 现在 event 是 BotEvent, 且 bot.id == 123456 + * } + * ``` + * + * ## 过滤器挂起 + * [filter] 允许挂起协程. **过滤器的挂起将被认为是事件监听器的挂起**. + * + * 过滤器挂起是否会影响事件处理, + * 取决于 [subscribe] 时的 [ConcurrencyKind] 和 [EventPriority]. + * + * ## 过滤器异常处理 + * 若 [filter] 抛出异常, 将被包装为 [ExceptionInEventChannelFilterException] 并重新抛出. + * + * @see filterIsInstance 过滤指定类型的事件 + */ + public actual fun filter(filter: suspend (event: BaseEvent) -> Boolean): EventChannel { + return FilterEventChannel(this, filter) + } + + /** + * [EventChannel.filter] 的 Java 版本. + * + * 添加一个过滤器. 过滤器将在收到任何事件之后, 传递给通过 [EventChannel.subscribe] 注册的监听器之前调用. + * + * 若 [filter] 返回 `true`, 该事件将会被传给监听器. 否则将会被忽略, **监听器继续监听**. + * + * ## 线性顺序 + * 多个 [filter] 的处理是线性且有顺序的. 若一个 [filter] 已经返回了 `false` (代表忽略这个事件), 则会立即忽略, 而不会传递给后续过滤器. + * + * 示例: + * ``` + * GlobalEventChannel // GlobalEventChannel 会收到全局所有事件, 事件类型是 Event + * .filterIsInstance(BotEvent.class) // 过滤, 只接受 BotEvent + * .filter(event -> + * // 此时的 event 一定是 BotEvent + * event.bot.id == 123456 // 再过滤 event 的 bot.id + * ) + * .subscribeAlways(event -> { + * // 现在 event 是 BotEvent, 且 bot.id == 123456 + * }) + * ``` + * + * ## 过滤器阻塞 + * [filter] 允许阻塞线程. **过滤器的阻塞将被认为是事件监听器的阻塞**. + * + * 过滤器阻塞是否会影响事件处理, + * 取决于 [subscribe] 时的 [ConcurrencyKind] 和 [EventPriority]. + * + * ## 过滤器异常处理 + * 若 [filter] 抛出异常, 将被包装为 [ExceptionInEventChannelFilterException] 并重新抛出. + * + * @see filterIsInstance 过滤指定类型的事件 + * + * @since 2.2 + */ + @Suppress("INVISIBLE_MEMBER", "INVISIBLE_REFERENCE") + @kotlin.internal.LowPriorityInOverloadResolution + public actual fun filter(filter: (event: BaseEvent) -> Boolean): EventChannel { + return filter { runBIO { filter(it) } } + } + + /** + * 过滤事件的类型. 返回一个只包含 [E] 类型事件的 [EventChannel] + * @see filter 获取更多信息 + */ + public actual inline fun filterIsInstance(): EventChannel = + filterIsInstance(E::class) + + /** + * 过滤事件的类型. 返回一个只包含 [E] 类型事件的 [EventChannel] + * @see filter 获取更多信息 + */ + public actual fun filterIsInstance(kClass: KClass): EventChannel { + return filter { kClass.isInstance(it) }.cast() + } + + /** + * 创建一个新的 [EventChannel], 该 [EventChannel] 包含 [`this.coroutineContext`][defaultCoroutineContext] 和添加的 [coroutineContexts]. + * [coroutineContexts] 会覆盖 [defaultCoroutineContext] 中的重复元素. + * + * 此操作不会修改 [`this.coroutineContext`][defaultCoroutineContext], 只会创建一个新的 [EventChannel]. + */ + public actual abstract fun context(vararg coroutineContexts: CoroutineContext): EventChannel + + /** + * 创建一个新的 [EventChannel], 该 [EventChannel] 包含 [this.coroutineContext][defaultCoroutineContext] 和添加的 [coroutineExceptionHandler] + * @see context + */ + @Suppress("INVISIBLE_MEMBER", "INVISIBLE_REFERENCE") + @kotlin.internal.LowPriorityInOverloadResolution + public actual fun exceptionHandler(coroutineExceptionHandler: CoroutineExceptionHandler): EventChannel { + return context(coroutineExceptionHandler) + } + + /** + * 创建一个新的 [EventChannel], 该 [EventChannel] 包含 [`this.coroutineContext`][defaultCoroutineContext] 和添加的 [coroutineExceptionHandler] + * @see context + */ + public actual fun exceptionHandler(coroutineExceptionHandler: (exception: Throwable) -> Unit): EventChannel { + return context(CoroutineExceptionHandler { _, throwable -> + coroutineExceptionHandler(throwable) + }) + } + + /** + * 将 [coroutineScope] 作为这个 [EventChannel] 的父作用域. + * + * 实际作用为创建一个新的 [EventChannel], + * 该 [EventChannel] 包含 [`this.coroutineContext`][defaultCoroutineContext] 和添加的 [CoroutineScope.coroutineContext], + * 并以 [CoroutineScope] 中 [Job] (如果有) [作为父 Job][parentJob] + * + * @see parentJob + * @see context + * + * @see CoroutineScope.globalEventChannel `GlobalEventChannel.parentScope()` 的扩展 + */ + public actual fun parentScope(coroutineScope: CoroutineScope): EventChannel { + return context(coroutineScope.coroutineContext) + } + + /** + * 指定协程父 [Job]. 之后在此 [EventChannel] 下创建的事件监听器都会成为 [job] 的子任务, 当 [job] 被取消时, 所有的事件监听器都会被取消. + * + * 注意: 监听器不会失败 ([Job.cancel]). 监听器处理过程的异常都会被捕获然后交由 [CoroutineExceptionHandler] 处理, 因此 [job] 不会因为子任务监听器的失败而被取消. + * + * @see parentScope + * @see context + */ + public actual fun parentJob(job: Job): EventChannel { + return context(job) + } + + // endregion + + // region subscribe + + /** + * 创建一个事件监听器, 监听事件通道中所有 [E] 及其子类事件. + * + * 每当 [事件广播][Event.broadcast] 时, [handler] 都会被执行. + * + * + * ## 创建监听 + * 调用本函数: + * ``` + * eventChannel.subscribe { /* 会收到此通道中的所有是 E 的事件 */ } + * ``` + * + * ## 生命周期 + * + * ### 通过协程作用域管理监听器 + * 本函数将会创建一个 [Job], 成为 [parentJob] 中的子任务. 可创建一个 [CoroutineScope] 来管理所有的监听器: + * ``` + * val scope = CoroutineScope(SupervisorJob()) + * + * val scopedChannel = eventChannel.parentScope(scope) // 将协程作用域 scope 附加到这个 EventChannel + * + * scopedChannel.subscribeAlways { /* ... */ } // 启动监听, 监听器协程会作为 scope 的子任务 + * scopedChannel.subscribeAlways { /* ... */ } // 启动监听, 监听器协程会作为 scope 的子任务 + * + * scope.cancel() // 停止了协程作用域, 也就取消了两个监听器 + * ``` + * + * 这个函数返回 [Listener], 它是一个 [CompletableJob]. 它会成为 [parentJob] 或 [parentScope] 的一个 [子任务][Job] + * + * ### 停止监听 + * 如果 [handler] 返回 [ListeningStatus.STOPPED] 监听器将被停止. + * + * 也可以通过 [subscribe] 返回值 [Listener] 的 [Listener.complete] + * + * ## 监听器调度 + * 监听器会被创建一个协程任务, 语义上在 [parentScope] 下运行. + * 通过 Kotlin [默认协程调度器][Dispatchers.Default] 在固定的全局共享线程池里执行, 除非有 [coroutineContext] 指定. + * + * 默认在 [handler] 中不能处理阻塞任务. 阻塞任务将会阻塞一个 Kotlin 全局协程调度线程并可能导致严重问题. + * 请通过 `withContext(Dispatchers.IO) { }` 等方法执行阻塞工作. + * + * ## 异常处理 + * + * **监听过程抛出的异常是需要尽可能避免的, 因为这将产生不确定性.** + * + * 当参数 [handler] 处理事件抛出异常时, 只会从监听方协程上下文 ([CoroutineContext]) 寻找 [CoroutineExceptionHandler] 处理异常, 即如下顺序: + * 1. 本函数参数 [coroutineContext] + * 2. [EventChannel.defaultCoroutineContext] + * 3. 若以上步骤无法获取 [CoroutineExceptionHandler], 则只会在日志记录异常. + * 因此建议先指定 [CoroutineExceptionHandler] (可通过 [EventChannel.exceptionHandler]) 再监听事件, 或者在监听事件中捕获异常. + * + * 因此, 广播方 ([Event.broadcast]) 不会知晓监听方产生的异常, 其 [Event.broadcast] 过程也不会因监听方产生异常而提前结束. + * + * ***备注***: 在 2.11 以前, 发生上述异常时还会从广播方和有关 [Bot] 协程域获取 [CoroutineExceptionHandler]. 因此行为不稳定而在 2.11 变更为上述过程. + * + * 事件处理时抛出异常不会停止监听器. + * + * 建议在事件处理中 (即 [handler] 里) 处理异常, + * 或在参数 [coroutineContext] 中添加 [CoroutineExceptionHandler], 或通过 [EventChannel.exceptionHandler]. + * + * ## 并发安全性 + * 基于 [concurrency] 参数, 事件监听器可以被允许并行执行. + * + * - 若 [concurrency] 为 [ConcurrencyKind.CONCURRENT], [handler] 可能被并行调用, 需要保证并发安全. + * - 若 [concurrency] 为 [ConcurrencyKind.LOCKED], [handler] 会被 [Mutex] 限制, 串行异步执行. + * + * ## 衍生监听方法 + * + * 这些方法仅 Kotlin 可用. + * + * - [syncFromEvent]: 挂起当前协程, 监听一个事件, 并尝试从这个事件中**获取**一个值 + * - [nextEvent]: 挂起当前协程, 直到监听到特定类型事件的广播并通过过滤器, 返回这个事件实例. + * + * @param coroutineContext 在 [defaultCoroutineContext] 的基础上, 给事件监听协程的额外的 [CoroutineContext]. + * @param concurrency 并发类型. 查看 [ConcurrencyKind] + * @param priority 监听优先级,优先级越高越先执行 + * @param handler 事件处理器. 在接收到事件时会调用这个处理器. 其返回值意义参考 [ListeningStatus]. 其异常处理参考上文 + * + * @return 监听器实例. 此监听器已经注册到指定事件上, 在事件广播时将会调用 [handler] + * + * + * @see selectMessages 以 `when` 的语法 '选择' 即将到来的一条消息. + * @see whileSelectMessages 以 `when` 的语法 '选择' 即将到来的所有消息, 直到不满足筛选结果. + * + * @see subscribeAlways 一直监听 + * @see subscribeOnce 只监听一次 + * + * @see subscribeMessages 监听消息 DSL + */ + public actual inline fun subscribe( + coroutineContext: CoroutineContext, + concurrency: ConcurrencyKind, + priority: EventPriority, + noinline handler: suspend E.(E) -> ListeningStatus, + ): Listener = subscribe(E::class, coroutineContext, concurrency, priority, handler) + + /** + * 与 [subscribe] 的区别是接受 [eventClass] 参数, 而不使用 `reified` 泛型. 通常推荐使用具体化类型参数. + * + * @return 监听器实例. 此监听器已经注册到指定事件上, 在事件广播时将会调用 [handler] + * @see subscribe + */ + public actual fun subscribe( + eventClass: KClass, + coroutineContext: CoroutineContext, + concurrency: ConcurrencyKind, + priority: EventPriority, + handler: suspend E.(E) -> ListeningStatus, + ): Listener = subscribeInternal( + eventClass, + createListener(coroutineContext, concurrency, priority) { it.handler(it); } + ) + + /** + * 创建一个事件监听器, 监听事件通道中所有 [E] 及其子类事件. + * 每当 [事件广播][Event.broadcast] 时, [handler] 都会被执行. + * + * 可在任意时候通过 [Listener.complete] 来主动停止监听. + * + * @param concurrency 并发类型默认为 [CONCURRENT] + * @param coroutineContext 在 [defaultCoroutineContext] 的基础上, 给事件监听协程的额外的 [CoroutineContext] + * @param priority 处理优先级, 优先级高的先执行 + * + * @return 监听器实例. 此监听器已经注册到指定事件上, 在事件广播时将会调用 [handler] + * + * @see subscribe 获取更多说明 + */ + public actual inline fun subscribeAlways( + coroutineContext: CoroutineContext, + concurrency: ConcurrencyKind, + priority: EventPriority, + noinline handler: suspend E.(E) -> Unit, + ): Listener = subscribeAlways(E::class, coroutineContext, concurrency, priority, handler) + + + /** + * @see subscribe + * @see subscribeAlways + */ + public actual fun subscribeAlways( + eventClass: KClass, + coroutineContext: CoroutineContext, + concurrency: ConcurrencyKind, + priority: EventPriority, + handler: suspend E.(E) -> Unit, + ): Listener = subscribeInternal( + eventClass, + createListener(coroutineContext, concurrency, priority) { it.handler(it); ListeningStatus.LISTENING } + ) + + /** + * 创建一个事件监听器, 监听事件通道中所有 [E] 及其子类事件, 只监听一次. + * 当 [事件广播][Event.broadcast] 时, [handler] 会被执行. + * + * 可在任意时候通过 [Listener.complete] 来主动停止监听. + * + * @param coroutineContext 在 [defaultCoroutineContext] 的基础上, 给事件监听协程的额外的 [CoroutineContext] + * @param priority 处理优先级, 优先级高的先执行 + * + * @see subscribe 获取更多说明 + */ + public actual inline fun subscribeOnce( + coroutineContext: CoroutineContext, + priority: EventPriority, + noinline handler: suspend E.(E) -> Unit, + ): Listener = subscribeOnce(E::class, coroutineContext, priority, handler) + + /** + * @see subscribeOnce + */ + public actual fun subscribeOnce( + eventClass: KClass, + coroutineContext: CoroutineContext, + priority: EventPriority, + handler: suspend E.(E) -> Unit, + ): Listener = subscribeInternal( + eventClass, + createListener(coroutineContext, ConcurrencyKind.LOCKED, priority) { it.handler(it); ListeningStatus.STOPPED } + ) + + // endregion + + // region impl + + + // protected, to hide from users + @MiraiInternalApi + protected actual abstract fun registerListener(eventClass: KClass, listener: Listener) + + // to overcome visibility issue + internal actual fun registerListener0(eventClass: KClass, listener: Listener) { + return registerListener(eventClass, listener) + } + + private fun , E : Event> subscribeInternal(eventClass: KClass, listener: L): L { + registerListener(eventClass, listener) + return listener + } + + /** + * Creates [Listener] instance using the [listenerBlock] action. + */ +// @Contract("_ -> new") // always creates new instance + @MiraiInternalApi + protected actual abstract fun createListener( + coroutineContext: CoroutineContext, + concurrencyKind: ConcurrencyKind, + priority: EventPriority, + listenerBlock: suspend (E) -> ListeningStatus, + ): Listener + + // to overcome visibility issue + internal actual fun createListener0( + coroutineContext: CoroutineContext, + concurrencyKind: ConcurrencyKind, + priority: EventPriority, + listenerBlock: suspend (E) -> ListeningStatus, + ): Listener = createListener(coroutineContext, concurrencyKind, priority, listenerBlock) + + // endregion +} diff --git a/mirai-core-utils/src/nativeMain/kotlin/loadServiceOrNull.kt b/mirai-core-api/src/nativeMain/kotlin/internal/message/overwritePolymorphicWith.kt similarity index 59% rename from mirai-core-utils/src/nativeMain/kotlin/loadServiceOrNull.kt rename to mirai-core-api/src/nativeMain/kotlin/internal/message/overwritePolymorphicWith.kt index 76601472a..867753bdb 100644 --- a/mirai-core-utils/src/nativeMain/kotlin/loadServiceOrNull.kt +++ b/mirai-core-api/src/nativeMain/kotlin/internal/message/overwritePolymorphicWith.kt @@ -7,20 +7,15 @@ * https://github.com/mamoe/mirai/blob/dev/LICENSE */ -package net.mamoe.mirai.utils +package net.mamoe.mirai.internal.message +import kotlinx.serialization.KSerializer +import kotlinx.serialization.modules.SerializersModule import kotlin.reflect.KClass -public actual fun loadServiceOrNull( - clazz: KClass, - fallbackImplementation: String? -): T? { - TODO("Not yet implemented") -} - -public actual fun loadService( - clazz: KClass, - fallbackImplementation: String? -): T { +internal actual fun SerializersModule.overwritePolymorphicWith( + type: KClass, + serializer: KSerializer +): SerializersModule { TODO("Not yet implemented") } \ No newline at end of file diff --git a/mirai-core-api/src/nativeMain/kotlin/internal/utils/ExternalResourceImplByByteArray.kt b/mirai-core-api/src/nativeMain/kotlin/internal/utils/ExternalResourceImplByByteArray.kt new file mode 100644 index 000000000..a06a3d874 --- /dev/null +++ b/mirai-core-api/src/nativeMain/kotlin/internal/utils/ExternalResourceImplByByteArray.kt @@ -0,0 +1,36 @@ +/* + * Copyright 2019-2022 Mamoe Technologies and contributors. + * + * 此源代码的使用受 GNU AFFERO GENERAL PUBLIC LICENSE version 3 许可证的约束, 可以在以下链接找到该许可证. + * Use of this source code is governed by the GNU AGPLv3 license that can be found through the following link. + * + * https://github.com/mamoe/mirai/blob/dev/LICENSE + */ + +package net.mamoe.mirai.internal.utils + +import io.ktor.utils.io.core.* +import kotlinx.coroutines.CompletableDeferred +import net.mamoe.mirai.utils.* + +internal class ExternalResourceImplByByteArray( + private val data: ByteArray, + formatName: String? +) : ExternalResource { + override val size: Long = data.size.toLong() + override val md5: ByteArray by lazy { data.md5() } + override val sha1: ByteArray by lazy { data.sha1() } + override val formatName: String by lazy { + formatName ?: getFileType(data.copyOf(COUNT_BYTES_USED_FOR_DETECTING_FILE_TYPE)) + ?: ExternalResource.DEFAULT_FORMAT_NAME + } + override val closed: CompletableDeferred = CompletableDeferred() + override val origin: Any + get() = data//.clone() + + override fun input(): Input = ByteReadPacket(data) + + override fun close() { + kotlin.runCatching { closed.complete(Unit) } + } +} diff --git a/mirai-core-api/src/nativeMain/kotlin/internal/utils/Marker.kt b/mirai-core-api/src/nativeMain/kotlin/internal/utils/Marker.kt new file mode 100644 index 000000000..801bc1440 --- /dev/null +++ b/mirai-core-api/src/nativeMain/kotlin/internal/utils/Marker.kt @@ -0,0 +1,21 @@ +/* + * Copyright 2019-2022 Mamoe Technologies and contributors. + * + * 此源代码的使用受 GNU AFFERO GENERAL PUBLIC LICENSE version 3 许可证的约束, 可以在以下链接找到该许可证. + * Use of this source code is governed by the GNU AGPLv3 license that can be found through the following link. + * + * https://github.com/mamoe/mirai/blob/dev/LICENSE + */ + +package net.mamoe.mirai.internal.utils + +internal actual interface Marker { + actual fun addParents(vararg parent: Marker) +} + +internal class MarkerImpl( + private val name: String +) : Marker { + override fun addParents(vararg parent: Marker) { + } +} \ No newline at end of file diff --git a/mirai-core-api/src/nativeMain/kotlin/internal/utils/MarkerManager.kt b/mirai-core-api/src/nativeMain/kotlin/internal/utils/MarkerManager.kt new file mode 100644 index 000000000..352122dd6 --- /dev/null +++ b/mirai-core-api/src/nativeMain/kotlin/internal/utils/MarkerManager.kt @@ -0,0 +1,16 @@ +/* + * Copyright 2019-2022 Mamoe Technologies and contributors. + * + * 此源代码的使用受 GNU AFFERO GENERAL PUBLIC LICENSE version 3 许可证的约束, 可以在以下链接找到该许可证. + * Use of this source code is governed by the GNU AGPLv3 license that can be found through the following link. + * + * https://github.com/mamoe/mirai/blob/dev/LICENSE + */ + +package net.mamoe.mirai.internal.utils + +internal actual object MarkerManager { + actual fun getMarker(name: String): Marker { + return MarkerImpl(name) + } +} \ No newline at end of file diff --git a/mirai-core-api/src/nativeMain/kotlin/message/action/AsyncRecallResult.kt b/mirai-core-api/src/nativeMain/kotlin/message/action/AsyncRecallResult.kt new file mode 100644 index 000000000..0f9025d35 --- /dev/null +++ b/mirai-core-api/src/nativeMain/kotlin/message/action/AsyncRecallResult.kt @@ -0,0 +1,50 @@ +/* + * Copyright 2019-2022 Mamoe Technologies and contributors. + * + * 此源代码的使用受 GNU AFFERO GENERAL PUBLIC LICENSE version 3 许可证的约束, 可以在以下链接找到该许可证. + * Use of this source code is governed by the GNU AGPLv3 license that can be found through the following link. + * + * https://github.com/mamoe/mirai/blob/dev/LICENSE + */ + +package net.mamoe.mirai.message.action + +import kotlinx.coroutines.CompletableDeferred +import kotlinx.coroutines.Deferred + +/** + * [MessageSource.recallIn] 的结果. + * + * @see MessageSource.recallIn + */ +public actual class AsyncRecallResult internal actual constructor( + /** + * 撤回时产生的异常. + */ + public actual val exception: Deferred, +) { + /** + * 撤回是否成功. + */ + public actual val isSuccess: Deferred by lazy { + CompletableDeferred().apply { + exception.invokeOnCompletion { + complete(it == null) + } + } + } + + /** + * 等待撤回完成, 返回撤回时产生的异常. + */ + public actual suspend fun awaitException(): Throwable? { + return exception.await() + } + + /** + * 等待撤回完成, 返回撤回的结果. + */ + public actual suspend fun awaitIsSuccess(): Boolean { + return isSuccess.await() + } +} diff --git a/mirai-core-api/src/nativeMain/kotlin/spi/SPIServiceLoader.kt b/mirai-core-api/src/nativeMain/kotlin/spi/SPIServiceLoader.kt index 0f6b5c2df..856713847 100644 --- a/mirai-core-api/src/nativeMain/kotlin/spi/SPIServiceLoader.kt +++ b/mirai-core-api/src/nativeMain/kotlin/spi/SPIServiceLoader.kt @@ -10,6 +10,7 @@ package net.mamoe.mirai.spi import net.mamoe.mirai.utils.MiraiLogger +import net.mamoe.mirai.utils.loadService import kotlin.reflect.KClass internal actual class SPIServiceLoader actual constructor( @@ -19,7 +20,7 @@ internal actual class SPIServiceLoader actual constructor( actual var service: T = defaultService actual fun reload() { - TODO("native") + service = loadService(serviceType) { service } } init { diff --git a/mirai-core-api/src/nativeMain/kotlin/utils/BotConfiguration.kt b/mirai-core-api/src/nativeMain/kotlin/utils/BotConfiguration.kt index 55ab15d4d..ade77c7e1 100644 --- a/mirai-core-api/src/nativeMain/kotlin/utils/BotConfiguration.kt +++ b/mirai-core-api/src/nativeMain/kotlin/utils/BotConfiguration.kt @@ -7,6 +7,8 @@ * https://github.com/mamoe/mirai/blob/dev/LICENSE */ +@file:Suppress("RedundantVisibilityModifier") + package net.mamoe.mirai.utils import io.ktor.utils.io.core.* @@ -325,7 +327,10 @@ public actual open class BotConfiguration { // open for Java */ @ConfigurationDsl public actual fun fileBasedDeviceInfo(filepath: String) { - deviceInfo = TODO("native") + deviceInfo = { + val file = MiraiFile.create(workingDir).resolve(filepath) + Json.decodeFromString(DeviceInfo.serializer(), file.readText()) + } } /////////////////////////////////////////////////////////////////////////// @@ -406,7 +411,7 @@ public actual open class BotConfiguration { // open for Java * * @since 2.4 */ - public var cacheDir: String = workingDir + "/cache" + public var cacheDir: String = "$workingDir/cache" /** * 联系人信息缓存配置. 将会保存在 [cacheDir] 中 `contacts` 目录 diff --git a/mirai-core-api/src/nativeMain/kotlin/utils/DeviceInfo.kt b/mirai-core-api/src/nativeMain/kotlin/utils/DeviceInfo.kt index 6909a5c96..58dfaecda 100644 --- a/mirai-core-api/src/nativeMain/kotlin/utils/DeviceInfo.kt +++ b/mirai-core-api/src/nativeMain/kotlin/utils/DeviceInfo.kt @@ -9,7 +9,6 @@ package net.mamoe.mirai.utils -import io.ktor.utils.io.core.* import kotlinx.serialization.Serializable import kotlinx.serialization.Transient import kotlin.random.Random @@ -48,13 +47,12 @@ public actual class DeviceInfo actual constructor( @MiraiInternalApi public actual val guid: ByteArray = generateGuid(androidId, macAddress) - @Suppress("ACTUAL_FUNCTION_WITH_DEFAULT_ARGUMENTS") // serializable @Serializable public actual class Version actual constructor( - public actual val incremental: ByteArray = "5891938".toByteArray(), - public actual val release: ByteArray = "10".toByteArray(), - public actual val codename: ByteArray = "REL".toByteArray(), - public actual val sdk: Int = 29 + public actual val incremental: ByteArray, + public actual val release: ByteArray, + public actual val codename: ByteArray, + public actual val sdk: Int ) { /** * @since 2.9 diff --git a/mirai-core-api/src/nativeMain/kotlin/utils/FileCacheStrategy.kt b/mirai-core-api/src/nativeMain/kotlin/utils/FileCacheStrategy.kt new file mode 100644 index 000000000..a94ae52f0 --- /dev/null +++ b/mirai-core-api/src/nativeMain/kotlin/utils/FileCacheStrategy.kt @@ -0,0 +1,53 @@ +/* + * Copyright 2019-2022 Mamoe Technologies and contributors. + * + * 此源代码的使用受 GNU AFFERO GENERAL PUBLIC LICENSE version 3 许可证的约束, 可以在以下链接找到该许可证. + * Use of this source code is governed by the GNU AGPLv3 license that can be found through the following link. + * + * https://github.com/mamoe/mirai/blob/dev/LICENSE + */ + +package net.mamoe.mirai.utils + +/** + * 资源缓存策略. + * + * 注意: 本接口只用于 JVM 平台. 在 native 平台没有作用. + * + * 由于上传资源时服务器要求提前给出 MD5 和文件大小等数据, 一些资源如 [InputStream] 需要首先缓存才能使用. + * + * 资源的缓存都是将 [InputStream] 缓存未 [ExternalResource]. 根据 [FileCacheStrategy] 实现不同, 可以以临时文件存储, 也可以在数据库或是内存按需存储. + * Mirai 内置的实现有 [内存存储][MemoryCache] 和 [临时文件存储][TempCache]. + * 操作 [ExternalResource.toExternalResource] 时将会使用 [IMirai.FileCacheStrategy]. 可以覆盖, 示例: + * ``` + * // Kotlin + * Mirai.FileCacheStrategy = FileCacheStrategy.TempCache() // 使用系统默认缓存路径, 也是默认的行为 + * Mirai.FileCacheStrategy = FileCacheStrategy.TempCache(File("C:/cache")) // 使用自定义缓存路径 + * + * // Java + * Mirai.getInstance().setFileCacheStrategy(new FileCacheStrategy.TempCache()); // 使用系统默认缓存路径, 也是默认的行为 + * Mirai.getInstance().setFileCacheStrategy(new FileCacheStrategy.TempCache(new File("C:/cache"))); // 使用自定义的缓存路径 + * ``` + * + * 此接口的实现和使用都是稳定的. 自行实现的 [FileCacheStrategy] 也可以被 Mirai 使用. + * + * 注意, 此接口目前仅缓存 [InputStream] 等一次性数据. 好友列表等数据由每个 [Bot] 的 [BotConfiguration.cacheDir] 缓存. + * + * ### 使用 [FileCacheStrategy] 的操作 + * - [ExternalResource.toExternalResource] + * - [ExternalResource.uploadAsImage] + * - [ExternalResource.sendAsImageTo] + * + * @see ExternalResource + */ +public actual interface FileCacheStrategy { + public actual companion object { + /** + * 当前平台下默认的缓存策略. 注意, 这可能不是 Mirai 全局默认使用的, Mirai 从 [IMirai.FileCacheStrategy] 获取. + * + * @see IMirai.FileCacheStrategy + */ + @MiraiExperimentalApi + public actual val PlatformDefault: FileCacheStrategy = object : FileCacheStrategy {} + } +} \ No newline at end of file diff --git a/mirai-core-api/src/nativeMain/kotlin/utils/MiraiLogger.kt b/mirai-core-api/src/nativeMain/kotlin/utils/MiraiLogger.kt index 5aab324f0..17300135e 100644 --- a/mirai-core-api/src/nativeMain/kotlin/utils/MiraiLogger.kt +++ b/mirai-core-api/src/nativeMain/kotlin/utils/MiraiLogger.kt @@ -18,12 +18,6 @@ import kotlin.reflect.KClass * * Mirai 内建简单的日志系统, 即 [MiraiLogger]. [MiraiLogger] 的实现有 [SimpleLogger], [PlatformLogger], [SilentLogger]. * - * [MiraiLogger] 仅能处理简单的日志任务, 通常推荐使用 [SLF4J][org.slf4j.Logger], [LOG4J][org.apache.logging.log4j.Logger] 等日志库. - * - * ## 使用第三方日志库接管 Mirai 日志系统 - * - * 使用 [LoggerAdapters], 将第三方日志 `Logger` 转为 [MiraiLogger]. 然后通过 [MiraiLogger.setDefaultLoggerCreator] 全局覆盖日志. - * * ## 实现或使用 [MiraiLogger] * * 不建议实现或使用 [MiraiLogger]. 请优先考虑使用上述第三方框架. [MiraiLogger] 仅应用于兼容旧版本代码. @@ -31,7 +25,6 @@ import kotlin.reflect.KClass * @see SimpleLogger 简易 logger, 它将所有的日志记录操作都转移给 lambda `(String?, Throwable?) -> Unit` * @see PlatformLogger 各个平台下的默认日志记录实现. * @see SilentLogger 忽略任何日志记录操作的 logger 实例. - * @see LoggerAdapters * * @see MiraiLoggerPlatformBase 平台通用基础实现. 若 Mirai 自带的日志系统无法满足需求, 请继承这个类并实现其抽象函数. */ @@ -48,56 +41,34 @@ public actual interface MiraiLogger { * @param requester 请求创建 [MiraiLogger] 的对象的 class * @param identity 对象标记 (备注) */ - public actual fun create(requester: KClass<*>, identity: String?): MiraiLogger = TODO("native") + public actual fun create(requester: KClass<*>, identity: String?): MiraiLogger { + return create(requester) + } /** * 创建 [MiraiLogger] 实例. * * @param requester 请求创建 [MiraiLogger] 的对象 */ - public actual fun create(requester: KClass<*>): MiraiLogger = TODO("native") - - public actual companion object INSTANCE : Factory by TODO("native") - } - - public actual companion object { - /** - * 顶层日志, 仅供 Mirai 内部使用. - */ - @MiraiInternalApi - @MiraiExperimentalApi - @Deprecated("Deprecated.", level = DeprecationLevel.HIDDEN) // deprecated since 2.7 - @DeprecatedSinceMirai(warningSince = "2.7", errorSince = "2.10", hiddenSince = "2.11") - public actual val TopLevel: MiraiLogger by lazy { Factory.create(MiraiLogger::class, "Mirai") } - - /** - * 已弃用, 请实现 service [net.mamoe.mirai.utils.MiraiLogger.Factory] 并以 [ServiceLoader] 支持的方式提供. - */ - @Suppress("DeprecatedCallableAddReplaceWith") - @Deprecated( - "Please set factory by providing an service of type net.mamoe.mirai.utils.MiraiLogger.Factory", - level = DeprecationLevel.ERROR - ) // deprecated since 2.7 - @DeprecatedSinceMirai(warningSince = "2.7", errorSince = "2.10") // left ERROR intentionally, for internal uses. - public actual fun setDefaultLoggerCreator(creator: (identity: String?) -> MiraiLogger) { - throw UnsupportedOperationException() + public actual fun create(requester: KClass<*>): MiraiLogger { + throw UnsupportedOperationException() // Cannot be abstract since on JVM it is open } - /** - * 旧版本用于创建 [MiraiLogger]. 已弃用. 请使用 [MiraiLogger.Factory.INSTANCE.create]. - * - * @see setDefaultLoggerCreator - */ - @Deprecated( - "Please use MiraiLogger.Factory.create", ReplaceWith( - "MiraiLogger.Factory.create(YourClass::class, identity)", - "net.mamoe.mirai.utils.MiraiLogger" - ), level = DeprecationLevel.HIDDEN - ) // deprecated since 2.7 - @DeprecatedSinceMirai(warningSince = "2.7", errorSince = "2.10", hiddenSince = "2.11") - public actual fun create(identity: String?): MiraiLogger = Factory.create(MiraiLogger::class, identity) + public actual companion object INSTANCE : Factory by loadService(Factory::class, fallbackImplementation = { + object : Factory { + override fun create(requester: KClass<*>): MiraiLogger { + return PlatformLogger(requester.qualifiedName ?: requester.simpleName) + } + + override fun create(requester: KClass<*>, identity: String?): MiraiLogger { + return PlatformLogger(identity) + } + } + }) } + public actual companion object; + /** * 日志的标记. 在 Mirai 中, identity 可为 * - "Bot" @@ -119,7 +90,6 @@ public actual interface MiraiLogger { * 当 VERBOSE 级别的日志启用时返回 `true`. * * 若 [isEnabled] 为 `false`, 返回 `false`. - * 在使用 [SLF4J][org.slf4j.Logger], [LOG4J][org.apache.logging.log4j.Logger] 或 [JUL][java.util.logging.Logger] 时返回真实配置值. * 其他情况下返回 [isEnabled] 的值. * * @since 2.7 @@ -130,7 +100,6 @@ public actual interface MiraiLogger { * 当 DEBUG 级别的日志启用时返回 `true` * * 若 [isEnabled] 为 `false`, 返回 `false`. - * 在使用 [SLF4J][org.slf4j.Logger], [LOG4J][org.apache.logging.log4j.Logger] 或 [JUL][java.util.logging.Logger] 时返回真实配置值. * 其他情况下返回 [isEnabled] 的值. * * @since 2.7 @@ -141,7 +110,6 @@ public actual interface MiraiLogger { * 当 INFO 级别的日志启用时返回 `true` * * 若 [isEnabled] 为 `false`, 返回 `false`. - * 在使用 [SLF4J][org.slf4j.Logger], [LOG4J][org.apache.logging.log4j.Logger] 或 [JUL][java.util.logging.Logger] 时返回真实配置值. * 其他情况下返回 [isEnabled] 的值. * * @since 2.7 @@ -152,7 +120,6 @@ public actual interface MiraiLogger { * 当 WARNING 级别的日志启用时返回 `true` * * 若 [isEnabled] 为 `false`, 返回 `false`. - * 在使用 [SLF4J][org.slf4j.Logger], [LOG4J][org.apache.logging.log4j.Logger] 或 [JUL][java.util.logging.Logger] 时返回真实配置值. * 其他情况下返回 [isEnabled] 的值. * * @since 2.7 @@ -163,30 +130,12 @@ public actual interface MiraiLogger { * 当 ERROR 级别的日志启用时返回 `true` * * 若 [isEnabled] 为 `false`, 返回 `false`. - * 在使用 [SLF4J][org.slf4j.Logger], [LOG4J][org.apache.logging.log4j.Logger] 或 [JUL][java.util.logging.Logger] 时返回真实配置值. * 其他情况下返回 [isEnabled] 的值. * * @since 2.7 */ public actual val isErrorEnabled: Boolean get() = isEnabled - /** - * 随从. 在 this 中调用所有方法后都应继续往 [follower] 传递调用. - * [follower] 的存在可以让一次日志被多个日志记录器记录. - * - * 一般不建议直接修改这个属性. 请通过 [plus] 来连接两个日志记录器. - * 如: `val logger = bot.logger + MyLogger()` - * 当调用 `logger.info()` 时, `bot.logger` 会首先记录, `MyLogger` 会随后记录. - * - * 当然, 多个 logger 也可以加在一起: `val logger = bot.logger + MynLogger() + MyLogger2()` - */ - @Suppress("UNUSED_PARAMETER") - @Deprecated("follower 设计不佳, 请避免使用", level = DeprecationLevel.HIDDEN) // deprecated since 2.7 - @DeprecatedSinceMirai(warningSince = "2.7", errorSince = "2.10", hiddenSince = "2.11") - public actual var follower: MiraiLogger? - get() = null - set(value) {} - /** * 记录一个 `verbose` 级别的日志. * 无关紧要的, 经常大量输出的日志应使用它. @@ -234,21 +183,4 @@ public actual interface MiraiLogger { /** 根据优先级调用对应函数 */ public actual fun call(priority: SimpleLogger.LogPriority, message: String?, e: Throwable?): Unit = priority.correspondingFunction(this, message, e) - - /** - * 添加一个 [follower], 返回 [follower] - * 它只会把 `this` 的属性 [MiraiLogger.follower] 修改为这个函数的参数 [follower], 然后返回这个参数. - * 若 [MiraiLogger.follower] 已经有值, 则会替换掉这个值. - * ``` - * +------+ +----------+ +----------+ +----------+ - * | base | <-- | follower | <-- | follower | <-- | follower | - * +------+ +----------+ +----------+ +----------+ - * ``` - * - * @return [follower] - */ - @Suppress("DeprecatedCallableAddReplaceWith") - @Deprecated("plus 设计不佳, 请避免使用.", level = DeprecationLevel.HIDDEN) // deprecated since 2.7 - @DeprecatedSinceMirai(warningSince = "2.7", errorSince = "2.10", hiddenSince = "2.11") - public actual operator fun plus(follower: T): T = follower } diff --git a/mirai-core-api/src/nativeMain/kotlin/utils/PlatformLogger.kt b/mirai-core-api/src/nativeMain/kotlin/utils/PlatformLogger.kt index fa512078f..d950a33e4 100644 --- a/mirai-core-api/src/nativeMain/kotlin/utils/PlatformLogger.kt +++ b/mirai-core-api/src/nativeMain/kotlin/utils/PlatformLogger.kt @@ -9,6 +9,8 @@ package net.mamoe.mirai.utils +import net.mamoe.mirai.internal.utils.StdoutLogger + /** * 当前平台的默认的日志记录器. * - 在 _JVM 控制台_ 端的实现为 [println] @@ -34,27 +36,35 @@ package net.mamoe.mirai.utils */ @MiraiInternalApi public actual open class PlatformLogger actual constructor(identity: String?) : - MiraiLoggerPlatformBase() { - override val identity: String? - get() = TODO("Not yet implemented") + MiraiLoggerPlatformBase(), MiraiLogger { + + private val delegate = StdoutLogger(identity) + + override val identity: String? get() = delegate.identity + override val isEnabled: Boolean get() = delegate.isEnabled + override val isVerboseEnabled: Boolean get() = delegate.isVerboseEnabled + override val isDebugEnabled: Boolean get() = delegate.isDebugEnabled + override val isInfoEnabled: Boolean get() = delegate.isInfoEnabled + override val isWarningEnabled: Boolean get() = delegate.isWarningEnabled + override val isErrorEnabled: Boolean get() = delegate.isErrorEnabled override fun verbose0(message: String?, e: Throwable?) { - TODO("Not yet implemented") + delegate.verbose0(message, e) } override fun debug0(message: String?, e: Throwable?) { - TODO("Not yet implemented") + delegate.debug(message, e) } override fun info0(message: String?, e: Throwable?) { - TODO("Not yet implemented") + delegate.info0(message, e) } override fun warning0(message: String?, e: Throwable?) { - TODO("Not yet implemented") + delegate.warning(message, e) } override fun error0(message: String?, e: Throwable?) { - TODO("Not yet implemented") + delegate.error0(message, e) } } \ No newline at end of file diff --git a/mirai-core-utils/build.gradle.kts b/mirai-core-utils/build.gradle.kts index 685b90e2d..ea012015d 100644 --- a/mirai-core-utils/build.gradle.kts +++ b/mirai-core-utils/build.gradle.kts @@ -23,7 +23,7 @@ description = "mirai-core utilities" kotlin { explicitApi() - configureHMPPJvm() + configureHMPP() sourceSets { val commonMain by getting { @@ -47,6 +47,7 @@ kotlin { val jvmBaseMain by getting { dependencies { + implementation(`jetbrains-annotations`) } } @@ -70,6 +71,17 @@ kotlin { val nativeMain by getting { dependencies { +// implementation("com.soywiz.korlibs.krypto:krypto:2.4.12") // ':mirai-core-utils:compileNativeMainKotlinMetadata' fails because compiler cannot find reference + } + } + + val mingwMain by getting { + dependencies { + } + } + + val unixMain by getting { + dependencies { } } } diff --git a/mirai-core-utils/src/androidMain/kotlin/Actuals.kt b/mirai-core-utils/src/androidMain/kotlin/Actuals.kt index 8645198cf..0d61475cc 100644 --- a/mirai-core-utils/src/androidMain/kotlin/Actuals.kt +++ b/mirai-core-utils/src/androidMain/kotlin/Actuals.kt @@ -1,10 +1,10 @@ /* - * Copyright 2019-2021 Mamoe Technologies and contributors. + * Copyright 2019-2022 Mamoe Technologies and contributors. * - * 此源代码的使用受 GNU AFFERO GENERAL PUBLIC LICENSE version 3 许可证的约束, 可以在以下链接找到该许可证. - * Use of this source code is governed by the GNU AGPLv3 license that can be found through the following link. + * 此源代码的使用受 GNU AFFERO GENERAL PUBLIC LICENSE version 3 许可证的约束, 可以在以下链接找到该许可证. + * Use of this source code is governed by the GNU AGPLv3 license that can be found through the following link. * - * https://github.com/mamoe/mirai/blob/master/LICENSE + * https://github.com/mamoe/mirai/blob/dev/LICENSE */ @file:JvmMultifileClass @@ -13,9 +13,6 @@ package net.mamoe.mirai.utils import android.util.Base64 -import java.util.* -import kotlin.reflect.KClass -import kotlin.reflect.full.createInstance public actual fun ByteArray.encodeBase64(): String { @@ -43,23 +40,3 @@ public actual inline fun Throwable.unwrap(): Throwable { ?.also { it.addSuppressed(e) } ?: this } - -public actual fun loadService(clazz: KClass, fallbackImplementation: String?): T { - var suppressed: Throwable? = null - return ServiceLoader.load(clazz.java).firstOrNull() - ?: (if (fallbackImplementation == null) null - else runCatching { findCreateInstance(fallbackImplementation) }.onFailure { suppressed = it }.getOrNull()) - ?: throw NoSuchElementException("Could not find an implementation for service class ${clazz.qualifiedName}").apply { - if (suppressed != null) addSuppressed(suppressed) - } -} - -private fun findCreateInstance(fallbackImplementation: String): T { - return Class.forName(fallbackImplementation).cast>().kotlin.run { objectInstance ?: createInstance() } -} - -public actual fun loadServiceOrNull(clazz: KClass, fallbackImplementation: String?): T? { - return ServiceLoader.load(clazz.java).firstOrNull() - ?: if (fallbackImplementation == null) return null - else runCatching { findCreateInstance(fallbackImplementation) }.getOrNull() -} \ No newline at end of file diff --git a/mirai-core-utils/src/commonMain/kotlin/Annotations.kt b/mirai-core-utils/src/commonMain/kotlin/Annotations.kt index c3aac5f5d..13d2a5371 100644 --- a/mirai-core-utils/src/commonMain/kotlin/Annotations.kt +++ b/mirai-core-utils/src/commonMain/kotlin/Annotations.kt @@ -14,7 +14,7 @@ import kotlin.annotation.AnnotationTarget.* @RequiresOptIn("This can only be used in tests.", level = ERROR) -@Target(CLASS, FUNCTION, PROPERTY, CLASS, CONSTRUCTOR, FUNCTION) +@Target(CLASS, FUNCTION, PROPERTY, CLASS, CONSTRUCTOR, FUNCTION, PROPERTY_GETTER) public annotation class TestOnly /** diff --git a/mirai-core-utils/src/commonMain/kotlin/Arrays.kt b/mirai-core-utils/src/commonMain/kotlin/Arrays.kt index ebebf7258..a418c1e90 100644 --- a/mirai-core-utils/src/commonMain/kotlin/Arrays.kt +++ b/mirai-core-utils/src/commonMain/kotlin/Arrays.kt @@ -36,5 +36,13 @@ public inline fun Collection.mapToIntArray(block: (element: A) -> Int): I this.forEachIndexed { index, element -> result[index] = block(element) } - return result.cast() + return result +} + +public inline fun Collection.mapToByteArray(block: (element: A) -> Byte): ByteArray { + val result = ByteArray(size) + this.forEachIndexed { index, element -> + result[index] = block(element) + } + return result } \ No newline at end of file diff --git a/mirai-core-utils/src/commonMain/kotlin/MiraiPlatformUtils.kt b/mirai-core-utils/src/commonMain/kotlin/ByteArrayOp.kt similarity index 51% rename from mirai-core-utils/src/commonMain/kotlin/MiraiPlatformUtils.kt rename to mirai-core-utils/src/commonMain/kotlin/ByteArrayOp.kt index a30105971..5c7fc7aaa 100644 --- a/mirai-core-utils/src/commonMain/kotlin/MiraiPlatformUtils.kt +++ b/mirai-core-utils/src/commonMain/kotlin/ByteArrayOp.kt @@ -7,28 +7,15 @@ * https://github.com/mamoe/mirai/blob/dev/LICENSE */ -@file:JvmMultifileClass -@file:JvmName("MiraiUtils") +@file:JvmName("ByteArrayOpKt_common") package net.mamoe.mirai.utils import io.ktor.utils.io.core.* -import io.ktor.utils.io.core.Closeable -import kotlin.contracts.InvocationKind -import kotlin.contracts.contract -import kotlin.jvm.JvmMultifileClass import kotlin.jvm.JvmName public expect val DEFAULT_BUFFER_SIZE: Int -public expect fun ByteArray.unzip(offset: Int = 0, length: Int = size - offset): ByteArray - - -/** - * Localhost 解析 - */ -public expect fun localIpAddress(): String - public fun String.md5(): ByteArray = toByteArray().md5() public expect fun ByteArray.md5(offset: Int = 0, length: Int = size - offset): ByteArray @@ -37,24 +24,8 @@ public fun String.sha1(): ByteArray = toByteArray().sha1() public expect fun ByteArray.sha1(offset: Int = 0, length: Int = size - offset): ByteArray +public expect fun ByteArray.gzip(offset: Int = 0, length: Int = size - offset): ByteArray public expect fun ByteArray.ungzip(offset: Int = 0, length: Int = size - offset): ByteArray -public expect fun ByteArray.gzip(offset: Int = 0, length: Int = size - offset): ByteArray - -public expect fun ByteArray.zip(offset: Int = 0, length: Int = size - offset): ByteArray - -public expect fun availableProcessors(): Int - -public inline fun C.withUse(block: C.() -> R): R { - contract { - callsInPlace(block, InvocationKind.EXACTLY_ONCE) - } - return use(block) -} - -public inline fun I.withOut(output: O, block: I.(output: O) -> R): R { - contract { - callsInPlace(block, InvocationKind.EXACTLY_ONCE) - } - return use { output.use { block(this, output) } } -} +public expect fun ByteArray.inflate(offset: Int = 0, length: Int = size - offset): ByteArray +public expect fun ByteArray.deflate(offset: Int = 0, length: Int = size - offset): ByteArray diff --git a/mirai-core-utils/src/commonMain/kotlin/Bytes.kt b/mirai-core-utils/src/commonMain/kotlin/Bytes.kt index d247ade85..ee46bee34 100644 --- a/mirai-core-utils/src/commonMain/kotlin/Bytes.kt +++ b/mirai-core-utils/src/commonMain/kotlin/Bytes.kt @@ -128,5 +128,5 @@ public inline fun ByteArray.read(t: ByteReadPacket.() -> R): R { contract { callsInPlace(t, InvocationKind.EXACTLY_ONCE) } - return this.toReadPacket().withUse(t) + return this.toReadPacket().use(t) } \ No newline at end of file diff --git a/mirai-core-utils/src/commonMain/kotlin/Clock.kt b/mirai-core-utils/src/commonMain/kotlin/Clock.kt index 6ad23b720..068a70cd3 100644 --- a/mirai-core-utils/src/commonMain/kotlin/Clock.kt +++ b/mirai-core-utils/src/commonMain/kotlin/Clock.kt @@ -7,8 +7,12 @@ * https://github.com/mamoe/mirai/blob/dev/LICENSE */ +@file:JvmName("ClockKt_common") + package net.mamoe.mirai.utils +import kotlin.jvm.JvmName + public interface Clock { public fun currentTimeMillis(): Long public fun currentTimeSeconds(): Long = currentTimeMillis() / 1000 @@ -27,4 +31,6 @@ public class AdjustedClock( ) : Clock { override fun currentTimeMillis(): Long = clock.currentTimeMillis() + diffMillis override fun currentTimeSeconds(): Long = (clock.currentTimeMillis() + diffMillis) / 1000 -} \ No newline at end of file +} + +public expect inline fun measureTimeMillis(block: () -> Unit): Long \ No newline at end of file diff --git a/mirai-core-utils/src/commonMain/kotlin/Collections.kt b/mirai-core-utils/src/commonMain/kotlin/Collections.kt index c8a49287b..b44c3fb95 100644 --- a/mirai-core-utils/src/commonMain/kotlin/Collections.kt +++ b/mirai-core-utils/src/commonMain/kotlin/Collections.kt @@ -7,7 +7,74 @@ * https://github.com/mamoe/mirai/blob/dev/LICENSE */ +@file:JvmName("CollectionsKt_common") + package net.mamoe.mirai.utils +import kotlin.jvm.JvmName +import kotlin.reflect.KClass + @Suppress("FunctionName") -public expect fun ConcurrentHashMap(): MutableMap \ No newline at end of file +public expect fun ConcurrentHashMap(): MutableMap + +@Suppress("FunctionName") +public expect fun ConcurrentLinkedDeque(): MutableDeque + +@Suppress("FunctionName") +public fun ConcurrentLinkedQueue(): MutableQueue = ConcurrentLinkedDeque() + +public expect class LinkedList constructor() : MutableList { + public fun addLast(element: E) +} + +public expect interface MutableQueue : MutableCollection { + /** + * Adds the specified element to the collection. + * + * @return `true` if the element has been added, `false` if the collection does not support duplicates + * and the element is already contained in the collection. + * @throws IllegalStateException if the queue is full. + */ + public override fun add(element: E): Boolean + + /** + * Removes and returns the head of the queue, `null` otherwise. + */ + public fun poll(): E? + + + /** + * Adds an element into the queue. + * @return `true` if the element has been added, `false` if queue is full. + */ + public fun offer(element: E): Boolean +} + +public expect interface MutableDeque : MutableQueue { + public fun addFirst(element: E) +} + +@Suppress("FunctionName") +public expect fun , V> EnumMap(clazz: KClass): MutableMap + +@Suppress("FunctionName") +public expect fun ConcurrentSet(): MutableSet + +@Deprecated("", ReplaceWith("getOrElse(key) { default }")) +public fun Map.getOrDefault(key: K, default: R): R = getOrElse(key) { default } + +@Suppress("EXTENSION_SHADOWED_BY_MEMBER") // JDK 1.8 +@Deprecated("", ReplaceWith("getOrPut(key) { value }")) +public fun MutableMap.putIfAbsent(key: K, value: V): V = getOrPut(key) { value } + +/** + * Returns a [List] that cannot be cast to [MutableList] to modify it. + */ +public expect fun List.asImmutable(): List + +/** + * Returns a [Collection] that cannot be cast to [MutableCollection] to modify it. + */ +public expect fun Collection.asImmutable(): Collection + +public expect fun Set.asImmutable(): Set \ No newline at end of file diff --git a/mirai-core-utils/src/commonMain/kotlin/ComputeOnNullMutableProperty.kt b/mirai-core-utils/src/commonMain/kotlin/ComputeOnNullMutableProperty.kt deleted file mode 100644 index 89e90a5ab..000000000 --- a/mirai-core-utils/src/commonMain/kotlin/ComputeOnNullMutableProperty.kt +++ /dev/null @@ -1,52 +0,0 @@ -/* - * Copyright 2019-2022 Mamoe Technologies and contributors. - * - * 此源代码的使用受 GNU AFFERO GENERAL PUBLIC LICENSE version 3 许可证的约束, 可以在以下链接找到该许可证. - * Use of this source code is governed by the GNU AGPLv3 license that can be found through the following link. - * - * https://github.com/mamoe/mirai/blob/dev/LICENSE - */ - -package net.mamoe.mirai.utils - -import kotlinx.atomicfu.atomic -import kotlinx.atomicfu.locks.SynchronizedObject -import kotlinx.atomicfu.locks.synchronized -import kotlin.reflect.KProperty - -public fun computeOnNullMutableProperty(initializer: () -> T): ComputeOnNullMutableProperty = - ComputeOnNullMutablePropertyImpl(initializer) - -public interface ComputeOnNullMutableProperty { - public fun get(): V - public fun set(value: V?) - - public operator fun getValue(thisRef: Any?, property: KProperty<*>): V = get() - public operator fun setValue(thisRef: Any?, property: KProperty<*>, value: V?): Unit = set(value) -} - - -private class ComputeOnNullMutablePropertyImpl( - private val initializer: () -> T -) : ComputeOnNullMutableProperty { - private val value = atomic(null) - private val lock = SynchronizedObject() - - override tailrec fun get(): T { - return when (val v = this.value.value) { - null -> synchronized(lock) { - if (this.value.value === null) { - val value = this.initializer() - // compiler inserts - this.value.compareAndSet(null, value) // setValue prevails - return get() - } else this.value.value as T - } - else -> v - } - } - - override fun set(value: T?) { - this.value.value = value - } -} \ No newline at end of file diff --git a/mirai-core-utils/src/commonMain/kotlin/CoroutineUtils.kt b/mirai-core-utils/src/commonMain/kotlin/CoroutineUtils.kt index eb40bee74..f6bd1b4a2 100644 --- a/mirai-core-utils/src/commonMain/kotlin/CoroutineUtils.kt +++ b/mirai-core-utils/src/commonMain/kotlin/CoroutineUtils.kt @@ -8,7 +8,7 @@ */ -@file:JvmName("CoroutineUtils_common") +@file:JvmName("CoroutineUtilsKt_common") package net.mamoe.mirai.utils diff --git a/mirai-core-utils/src/commonMain/kotlin/ExceptionCollector.kt b/mirai-core-utils/src/commonMain/kotlin/ExceptionCollector.kt index a21288e46..644525e99 100644 --- a/mirai-core-utils/src/commonMain/kotlin/ExceptionCollector.kt +++ b/mirai-core-utils/src/commonMain/kotlin/ExceptionCollector.kt @@ -7,10 +7,13 @@ * https://github.com/mamoe/mirai/blob/dev/LICENSE */ +@file:JvmName("ExceptionCollectorKt_common") + package net.mamoe.mirai.utils import kotlin.contracts.InvocationKind import kotlin.contracts.contract +import kotlin.jvm.JvmName import kotlin.jvm.Synchronized import kotlin.jvm.Volatile diff --git a/mirai-core-utils/src/commonMain/kotlin/File.kt b/mirai-core-utils/src/commonMain/kotlin/File.kt new file mode 100644 index 000000000..74775a115 --- /dev/null +++ b/mirai-core-utils/src/commonMain/kotlin/File.kt @@ -0,0 +1,106 @@ +/* + * Copyright 2019-2022 Mamoe Technologies and contributors. + * + * 此源代码的使用受 GNU AFFERO GENERAL PUBLIC LICENSE version 3 许可证的约束, 可以在以下链接找到该许可证. + * Use of this source code is governed by the GNU AGPLv3 license that can be found through the following link. + * + * https://github.com/mamoe/mirai/blob/dev/LICENSE + */ + +package net.mamoe.mirai.utils + +import io.ktor.utils.io.core.* + +/** + * Multiplatform implementation of file operations. + */ +public expect interface MiraiFile { + /** + * Name of this file or directory. Can be '.' and '..' if created by + */ + public val name: String + + /** + * Parent of this file or directory. + */ + public val parent: MiraiFile? + + /** + * Input path from [create]. + */ + public val path: String + + /** + * Normalized absolute [path]. + */ + public val absolutePath: String + + public val length: Long + + public val isFile: Boolean + public val isDirectory: Boolean + + public fun exists(): Boolean + + /** + * Resolves a [MiraiFile] representing the [path] based on this [MiraiFile]. Result path is not guaranteed to be normalized. + */ + public fun resolve(path: String): MiraiFile + public fun resolve(file: MiraiFile): MiraiFile + + public fun createNewFile(): Boolean + public fun delete(): Boolean + + public fun mkdir(): Boolean + public fun mkdirs(): Boolean + + public fun input(): Input + public fun output(): Output + + public companion object { + public fun create(path: String): MiraiFile + + public fun getWorkingDir(): MiraiFile + } + +} + +public expect fun MiraiFile.deleteRecursively(): Boolean + +public fun MiraiFile.writeBytes(data: ByteArray) { + return output().use { it.writeFully(data) } +} + +public fun MiraiFile.writeText(text: String) { + return output().use { it.writeText(text) } +} + +public fun MiraiFile.readText(): String { + return input().use { it.readText() } +} + +public fun MiraiFile.readBytes(): ByteArray { + return input().use { it.readBytes() } +} + + +public fun MiraiFile.createFileIfNotExists() { + if (!this.exists()) { + this.parent?.mkdirs() + this.createNewFile() + } +} + +public fun MiraiFile.resolveCreateFile(relative: String): MiraiFile = + this.resolve(relative).apply { createFileIfNotExists() } + +public fun MiraiFile.resolveCreateFile(relative: MiraiFile): MiraiFile = + this.resolve(relative).apply { createFileIfNotExists() } + +public fun MiraiFile.resolveMkdir(relative: String): MiraiFile = this.resolve(relative).apply { mkdirs() } +public fun MiraiFile.resolveMkdir(relative: MiraiFile): MiraiFile = this.resolve(relative).apply { mkdirs() } + +public fun MiraiFile.touch(): MiraiFile = apply { + parent?.mkdirs() + createNewFile() +} diff --git a/mirai-core-utils/src/commonMain/kotlin/IO.kt b/mirai-core-utils/src/commonMain/kotlin/IO.kt index d5f403e45..39838bfc6 100644 --- a/mirai-core-utils/src/commonMain/kotlin/IO.kt +++ b/mirai-core-utils/src/commonMain/kotlin/IO.kt @@ -14,8 +14,12 @@ package net.mamoe.mirai.utils +import io.ktor.utils.io.* import io.ktor.utils.io.charsets.* import io.ktor.utils.io.core.* +import io.ktor.utils.io.core.internal.* +import kotlin.contracts.InvocationKind +import kotlin.contracts.contract import kotlin.jvm.JvmMultifileClass import kotlin.jvm.JvmName import kotlin.jvm.JvmSynthetic @@ -25,6 +29,20 @@ public val EMPTY_BYTE_ARRAY: ByteArray = ByteArray(0) public val DECRYPTER_16_ZERO: ByteArray = ByteArray(16) public val KEY_16_ZEROS: ByteArray = ByteArray(16) +public inline fun C.withUse(block: C.() -> R): R { + contract { + callsInPlace(block, InvocationKind.EXACTLY_ONCE) + } + return use(block) +} + +public inline fun I.withOut(output: O, block: I.(output: O) -> R): R { + contract { + callsInPlace(block, InvocationKind.EXACTLY_ONCE) + } + return use { output.use { block(this, output) } } +} + @Suppress("INVISIBLE_MEMBER", "INVISIBLE_REFERENCE") public inline fun ByteReadPacket.useBytes( n: Int = remaining.toInt(),//not that safe but adequate @@ -133,3 +151,22 @@ public inline fun Input.readString(length: Byte, charset: Charset = Charsets.UTF public fun Input.readUShortLVString(): String = String(this.readUShortLVByteArray()) public fun Input.readUShortLVByteArray(): ByteArray = this.readBytes(this.readUShort().toInt()) + +public suspend fun Input.copyTo(output: ByteWriteChannel): Long { + val buffer = ChunkBuffer.Pool.borrow() + var copied = 0L + + try { + do { + buffer.resetForWrite() + val rc = readAvailable(buffer) + if (rc == -1) break + copied += rc + output.writeFully(buffer) + } while (true) + + return copied + } finally { + buffer.release(ChunkBuffer.Pool) + } +} diff --git a/mirai-core-utils/src/commonMain/kotlin/Numbers.kt b/mirai-core-utils/src/commonMain/kotlin/Numbers.kt index 8b507cb02..8120e136e 100644 --- a/mirai-core-utils/src/commonMain/kotlin/Numbers.kt +++ b/mirai-core-utils/src/commonMain/kotlin/Numbers.kt @@ -16,6 +16,7 @@ import kotlin.jvm.JvmMultifileClass import kotlin.jvm.JvmName public fun Int.toLongUnsigned(): Long = this.toLong().and(0xFFFF_FFFF) +public fun Long.toLongUnsigned(): Long = this // for native unstable types public fun Short.toIntUnsigned(): Int = this.toUShort().toInt() public fun Byte.toIntUnsigned(): Int = toInt() and 0xFF public fun Int.concatAsLong(i2: Int): Long = this.toLongUnsigned().shl(Int.SIZE_BITS) or i2.toLongUnsigned() diff --git a/mirai-core-utils/src/commonMain/kotlin/Serialization.kt b/mirai-core-utils/src/commonMain/kotlin/Serialization.kt index ccf376fe4..c7f7cd64a 100644 --- a/mirai-core-utils/src/commonMain/kotlin/Serialization.kt +++ b/mirai-core-utils/src/commonMain/kotlin/Serialization.kt @@ -7,12 +7,18 @@ * https://github.com/mamoe/mirai/blob/master/LICENSE */ +@file:JvmName("SerializationKt_common") + package net.mamoe.mirai.utils +import kotlinx.serialization.BinaryFormat +import kotlinx.serialization.DeserializationStrategy import kotlinx.serialization.KSerializer +import kotlinx.serialization.StringFormat import kotlinx.serialization.descriptors.* import kotlinx.serialization.encoding.Decoder import kotlinx.serialization.encoding.Encoder +import kotlin.jvm.JvmName public fun SerialDescriptor.copy(newName: String): SerialDescriptor = buildClassSerialDescriptor(newName) { takeElementsFrom(this@copy) } @@ -58,3 +64,25 @@ public inline fun KSerializer.mapPrimitive( this@mapPrimitive.serialize(encoder, value.let { serialize(it, it) }) } } + + +public fun MiraiFile.loadNotBlankAs( + serializer: DeserializationStrategy, + stringFormat: StringFormat, +): T? { + if (!this.exists() || this.length == 0L) { + return null + } + return stringFormat.decodeFromString(serializer, this.readText()) +} + +public fun MiraiFile.loadNotBlankAs( + serializer: DeserializationStrategy, + binaryFormat: BinaryFormat, +): T? { + if (!this.exists() || this.length == 0L) { + return null + } + return binaryFormat.decodeFromByteArray(serializer, this.readBytes()) +} + diff --git a/mirai-core-utils/src/commonMain/kotlin/Services.kt b/mirai-core-utils/src/commonMain/kotlin/Services.kt index 1da85f04a..9b4a1d065 100644 --- a/mirai-core-utils/src/commonMain/kotlin/Services.kt +++ b/mirai-core-utils/src/commonMain/kotlin/Services.kt @@ -1,5 +1,5 @@ /* - * Copyright 2019-2021 Mamoe Technologies and contributors. + * Copyright 2019-2022 Mamoe Technologies and contributors. * * 此源代码的使用受 GNU AFFERO GENERAL PUBLIC LICENSE version 3 许可证的约束, 可以在以下链接找到该许可证. * Use of this source code is governed by the GNU AGPLv3 license that can be found through the following link. @@ -7,12 +7,16 @@ * https://github.com/mamoe/mirai/blob/dev/LICENSE */ +@file:JvmName("ServicesKt_common") + package net.mamoe.mirai.utils +import kotlin.jvm.JvmName import kotlin.reflect.KClass public expect fun loadServiceOrNull(clazz: KClass, fallbackImplementation: String? = null): T? public expect fun loadService(clazz: KClass, fallbackImplementation: String? = null): T +public expect fun loadServices(clazz: KClass): Sequence public inline fun loadService(fallbackImplementation: String? = null): T = loadService(T::class, fallbackImplementation) diff --git a/mirai-core-utils/src/commonMain/kotlin/StandardUtils.kt b/mirai-core-utils/src/commonMain/kotlin/StandardUtils.kt index 9672f8a20..d2e17363e 100644 --- a/mirai-core-utils/src/commonMain/kotlin/StandardUtils.kt +++ b/mirai-core-utils/src/commonMain/kotlin/StandardUtils.kt @@ -161,9 +161,17 @@ internal expect fun isSameClassPlatform(object1: Any, object2: Any): Boolean public inline fun isSameType(thisObject: T, other: Any?): Boolean { contract { - returns() implies (other is T) + returns(true) implies (other is T) } if (other == null) return false if (other !is T) return false return isSameClass(thisObject, other) -} \ No newline at end of file +} + +public expect fun availableProcessors(): Int + + +/** + * Localhost 解析 + */ +public expect fun localIpAddress(): String diff --git a/mirai-core-utils/src/commonMain/kotlin/TimeUtils.kt b/mirai-core-utils/src/commonMain/kotlin/TimeUtils.kt index a3ffab138..599392ce1 100644 --- a/mirai-core-utils/src/commonMain/kotlin/TimeUtils.kt +++ b/mirai-core-utils/src/commonMain/kotlin/TimeUtils.kt @@ -7,12 +7,10 @@ * https://github.com/mamoe/mirai/blob/dev/LICENSE */ -@file:JvmMultifileClass -@file:JvmName("MiraiUtils") +@file:JvmName("TimeUtilsKt_common") package net.mamoe.mirai.utils -import kotlin.jvm.JvmMultifileClass import kotlin.jvm.JvmName import kotlin.jvm.JvmSynthetic import kotlin.math.floor @@ -27,6 +25,8 @@ public expect fun currentTimeMillis(): Long */ public fun currentTimeSeconds(): Long = currentTimeMillis() / 1000 +public expect fun currentTimeFormatted(format: String? = null): String + // 临时使用, 待 Kotlin Duration 稳定后使用 Duration. // 内联属性, 则将来删除这些 API 将不会导致二进制不兼容. diff --git a/mirai-core-utils/src/commonMain/kotlin/TypeSafeMap.kt b/mirai-core-utils/src/commonMain/kotlin/TypeSafeMap.kt index ea1f0bdf4..bf14b4b9b 100644 --- a/mirai-core-utils/src/commonMain/kotlin/TypeSafeMap.kt +++ b/mirai-core-utils/src/commonMain/kotlin/TypeSafeMap.kt @@ -14,10 +14,10 @@ package net.mamoe.mirai.utils import kotlinx.serialization.Serializable import kotlin.contracts.InvocationKind import kotlin.contracts.contract +import kotlin.jvm.JvmInline import kotlin.properties.ReadOnlyProperty import kotlin.properties.ReadWriteProperty import kotlin.reflect.KProperty -import kotlin.jvm.JvmInline @Serializable @JvmInline @@ -175,13 +175,13 @@ internal class MutableTypeSafeMapImpl( } } -public fun TypeSafeMap.toMutableTypeSafeMap(): MutableTypeSafeMap = MutableTypeSafeMap(this.toMap()) +public fun TypeSafeMap.toMutableTypeSafeMap(): MutableTypeSafeMap = createMutableTypeSafeMap(this.toMap()) -public inline fun MutableTypeSafeMap(): MutableTypeSafeMap = MutableTypeSafeMapImpl() -public inline fun MutableTypeSafeMap(map: Map): MutableTypeSafeMap = +public inline fun createMutableTypeSafeMap(): MutableTypeSafeMap = MutableTypeSafeMapImpl() +public inline fun createMutableTypeSafeMap(map: Map): MutableTypeSafeMap = MutableTypeSafeMapImpl().also { it.map.putAll(map) } -public inline fun TypeSafeMap(): TypeSafeMap = TypeSafeMap.EMPTY +public inline fun createTypeSafeMap(): TypeSafeMap = TypeSafeMap.EMPTY public inline fun buildTypeSafeMap(block: MutableTypeSafeMap.() -> Unit): MutableTypeSafeMap { contract { callsInPlace(block, InvocationKind.EXACTLY_ONCE) } diff --git a/mirai-core-utils/src/commonMain/kotlin/annotations/Range.kt b/mirai-core-utils/src/commonMain/kotlin/annotations/Range.kt new file mode 100644 index 000000000..061653720 --- /dev/null +++ b/mirai-core-utils/src/commonMain/kotlin/annotations/Range.kt @@ -0,0 +1,16 @@ +/* + * Copyright 2019-2022 Mamoe Technologies and contributors. + * + * 此源代码的使用受 GNU AFFERO GENERAL PUBLIC LICENSE version 3 许可证的约束, 可以在以下链接找到该许可证. + * Use of this source code is governed by the GNU AGPLv3 license that can be found through the following link. + * + * https://github.com/mamoe/mirai/blob/dev/LICENSE + */ + +package net.mamoe.mirai.utils.annotations + +@OptIn(ExperimentalMultiplatform::class) +@Target(AnnotationTarget.VALUE_PARAMETER, AnnotationTarget.PROPERTY, AnnotationTarget.TYPE) +@Retention(AnnotationRetention.BINARY) +@OptionalExpectation +public expect annotation class Range(val from: Long, val to: Long) \ No newline at end of file diff --git a/mirai-core-utils/src/commonMain/kotlin/systemProp.kt b/mirai-core-utils/src/commonMain/kotlin/systemProp.kt index 59aa4a4a0..2019b67c9 100644 --- a/mirai-core-utils/src/commonMain/kotlin/systemProp.kt +++ b/mirai-core-utils/src/commonMain/kotlin/systemProp.kt @@ -18,6 +18,10 @@ import kotlin.jvm.JvmName internal expect fun getProperty(name: String, default: String): String? +internal expect fun setProperty(name: String, value: String) + +public fun setSystemProp(name: String, value: String): Unit = setProperty(name, value) + public fun systemProp(name: String, default: String): String = getProperty(name, default) ?: default diff --git a/mirai-core-utils/src/commonTest/kotlin/net/mamoe/mirai/utils/ComputeOnNullMutablePropertyTest.kt b/mirai-core-utils/src/commonTest/kotlin/net/mamoe/mirai/utils/ComputeOnNullMutablePropertyTest.kt deleted file mode 100644 index 64fd43400..000000000 --- a/mirai-core-utils/src/commonTest/kotlin/net/mamoe/mirai/utils/ComputeOnNullMutablePropertyTest.kt +++ /dev/null @@ -1,51 +0,0 @@ -/* - * Copyright 2019-2021 Mamoe Technologies and contributors. - * - * 此源代码的使用受 GNU AFFERO GENERAL PUBLIC LICENSE version 3 许可证的约束, 可以在以下链接找到该许可证. - * Use of this source code is governed by the GNU AGPLv3 license that can be found through the following link. - * - * https://github.com/mamoe/mirai/blob/dev/LICENSE - */ - -package net.mamoe.mirai.utils - -import org.junit.jupiter.api.Test -import java.util.concurrent.atomic.AtomicBoolean -import kotlin.test.assertEquals -import kotlin.test.assertFalse -import kotlin.test.assertTrue - -internal class ComputeOnNullMutablePropertyTest { - @Test - fun `can initialize`() { - val prop = computeOnNullMutableProperty { "ok" } - assertEquals("ok", prop.get()) - } - - @Test - fun `can override`() { - val called = AtomicBoolean(false) - val prop = computeOnNullMutableProperty { "not ok".also { called.set(true) } } - prop.set("ok") - assertEquals("ok", prop.get()) - assertFalse { called.get() } - } - - @Test - fun `can reinitialize 1`() { - val called = AtomicBoolean(false) - val prop = computeOnNullMutableProperty { "ok".also { called.set(true) } } - prop.set("not ok 2") - prop.set(null) - assertEquals("ok", prop.get()) - assertTrue { called.get() } - } - - @Test - fun `can reinitialize 2`() { - val prop = computeOnNullMutableProperty { "ok" } - prop.get() - prop.set(null) - assertEquals("ok", prop.get()) - } -} \ No newline at end of file diff --git a/mirai-core-utils/src/commonTest/kotlin/net/mamoe/mirai/utils/EitherTest.kt b/mirai-core-utils/src/commonTest/kotlin/net/mamoe/mirai/utils/EitherTest.kt index dce031eaf..8e04a5e4f 100644 --- a/mirai-core-utils/src/commonTest/kotlin/net/mamoe/mirai/utils/EitherTest.kt +++ b/mirai-core-utils/src/commonTest/kotlin/net/mamoe/mirai/utils/EitherTest.kt @@ -23,10 +23,9 @@ import net.mamoe.mirai.utils.Either.Companion.onLeft import net.mamoe.mirai.utils.Either.Companion.onRight import net.mamoe.mirai.utils.Either.Companion.right import net.mamoe.mirai.utils.Either.Companion.rightOrNull -import org.junit.jupiter.api.Test -import org.junit.jupiter.api.assertThrows import kotlin.reflect.KType import kotlin.reflect.typeOf +import kotlin.test.Test import kotlin.test.assertEquals import kotlin.test.assertFailsWith import kotlin.test.assertIs @@ -38,8 +37,8 @@ internal class EitherTest { Either("") Either(1) - assertThrows { Either.invoke("") } - assertThrows { Either.invoke("") } + assertFailsWith { Either.invoke("") } + assertFailsWith { Either.invoke("") } } @Test diff --git a/mirai-core-utils/src/commonTest/kotlin/net/mamoe/mirai/utils/ExceptionCollectorTest.kt b/mirai-core-utils/src/commonTest/kotlin/net/mamoe/mirai/utils/ExceptionCollectorTest.kt index 918d1312f..72f9ee414 100644 --- a/mirai-core-utils/src/commonTest/kotlin/net/mamoe/mirai/utils/ExceptionCollectorTest.kt +++ b/mirai-core-utils/src/commonTest/kotlin/net/mamoe/mirai/utils/ExceptionCollectorTest.kt @@ -1,19 +1,15 @@ /* - * Copyright 2019-2021 Mamoe Technologies and contributors. + * Copyright 2019-2022 Mamoe Technologies and contributors. * - * 此源代码的使用受 GNU AFFERO GENERAL PUBLIC LICENSE version 3 许可证的约束, 可以在以下链接找到该许可证. - * Use of this source code is governed by the GNU AGPLv3 license that can be found through the following link. + * 此源代码的使用受 GNU AFFERO GENERAL PUBLIC LICENSE version 3 许可证的约束, 可以在以下链接找到该许可证. + * Use of this source code is governed by the GNU AGPLv3 license that can be found through the following link. * - * https://github.com/mamoe/mirai/blob/master/LICENSE + * https://github.com/mamoe/mirai/blob/dev/LICENSE */ package net.mamoe.mirai.utils -import org.junit.jupiter.api.Test -import kotlin.test.assertEquals -import kotlin.test.assertIs -import kotlin.test.assertSame -import kotlin.test.assertTrue +import kotlin.test.* @OptIn(TestOnly::class) internal class ExceptionCollectorTest { @@ -36,7 +32,7 @@ internal class ExceptionCollectorTest { collector.collect(IllegalStateException()) assertIs(collector.getLast()) - assertTrue { collector.getLast()!!.suppressed.single() is IllegalArgumentException } + assertTrue { collector.getLast()!!.suppressedExceptions.single() is IllegalArgumentException } assertEquals(2, collector.asSequence().count()) } @@ -44,13 +40,13 @@ internal class ExceptionCollectorTest { fun `can collect suppressed nested`() { val collector = ExceptionCollector() - collector.collect(StackOverflowError()) + collector.collect(IndexOutOfBoundsException()) collector.collect(IllegalArgumentException()) collector.collect(IllegalStateException()) assertIs(collector.getLast()) - assertTrue { collector.getLast()!!.suppressed.single() is IllegalArgumentException } - assertTrue { collector.getLast()!!.suppressed.single()!!.suppressed.single() is StackOverflowError } + assertTrue { collector.getLast()!!.suppressedExceptions.single() is IllegalArgumentException } + assertTrue { collector.getLast()!!.suppressedExceptions.single().suppressedExceptions.single() is IndexOutOfBoundsException } assertEquals(3, collector.asSequence().count()) } @@ -65,7 +61,7 @@ internal class ExceptionCollectorTest { collector.collect(exception) assertSame(exception, collector.asSequence().last()) - assertEquals(0, collector.getLast()!!.suppressed.size) + assertEquals(0, collector.getLast()!!.suppressedExceptions.size) assertEquals(1, collector.asSequence().count()) } @@ -83,7 +79,7 @@ internal class ExceptionCollectorTest { collector.collect(exception) } - assertEquals(0, collector.getLast()!!.suppressed.size) + assertEquals(0, collector.getLast()!!.suppressedExceptions.size) assertEquals(1, collector.asSequence().count()) assertSame(exceptions.first(), collector.getLast()) assertEquals("#0", collector.getLast()!!.message) diff --git a/mirai-core-utils/src/commonTest/kotlin/net/mamoe/mirai/utils/ImageIdConversionTest.kt b/mirai-core-utils/src/commonTest/kotlin/net/mamoe/mirai/utils/ImageIdConversionTest.kt index ffe4cb0de..9ae75317c 100644 --- a/mirai-core-utils/src/commonTest/kotlin/net/mamoe/mirai/utils/ImageIdConversionTest.kt +++ b/mirai-core-utils/src/commonTest/kotlin/net/mamoe/mirai/utils/ImageIdConversionTest.kt @@ -1,15 +1,15 @@ /* - * Copyright 2019-2021 Mamoe Technologies and contributors. + * Copyright 2019-2022 Mamoe Technologies and contributors. * - * 此源代码的使用受 GNU AFFERO GENERAL PUBLIC LICENSE version 3 许可证的约束, 可以在以下链接找到该许可证. - * Use of this source code is governed by the GNU AGPLv3 license that can be found through the following link. + * 此源代码的使用受 GNU AFFERO GENERAL PUBLIC LICENSE version 3 许可证的约束, 可以在以下链接找到该许可证. + * Use of this source code is governed by the GNU AGPLv3 license that can be found through the following link. * - * https://github.com/mamoe/mirai/blob/master/LICENSE + * https://github.com/mamoe/mirai/blob/dev/LICENSE */ package net.mamoe.mirai.utils -import org.junit.jupiter.api.Test +import kotlin.test.Test import kotlin.test.assertEquals internal class ImageIdConversionTest { diff --git a/mirai-core-utils/src/commonTest/kotlin/net/mamoe/mirai/utils/LateinitMutablePropertyTest.kt b/mirai-core-utils/src/commonTest/kotlin/net/mamoe/mirai/utils/LateinitMutablePropertyTest.kt index 1fe8c92c1..fdce2772f 100644 --- a/mirai-core-utils/src/commonTest/kotlin/net/mamoe/mirai/utils/LateinitMutablePropertyTest.kt +++ b/mirai-core-utils/src/commonTest/kotlin/net/mamoe/mirai/utils/LateinitMutablePropertyTest.kt @@ -1,10 +1,10 @@ /* - * Copyright 2019-2021 Mamoe Technologies and contributors. + * Copyright 2019-2022 Mamoe Technologies and contributors. * - * 此源代码的使用受 GNU AFFERO GENERAL PUBLIC LICENSE version 3 许可证的约束, 可以在以下链接找到该许可证. - * Use of this source code is governed by the GNU AGPLv3 license that can be found through the following link. + * 此源代码的使用受 GNU AFFERO GENERAL PUBLIC LICENSE version 3 许可证的约束, 可以在以下链接找到该许可证. + * Use of this source code is governed by the GNU AGPLv3 license that can be found through the following link. * - * https://github.com/mamoe/mirai/blob/master/LICENSE + * https://github.com/mamoe/mirai/blob/dev/LICENSE */ package net.mamoe.mirai.utils @@ -12,9 +12,7 @@ package net.mamoe.mirai.utils import kotlinx.coroutines.CompletableDeferred import kotlinx.coroutines.launch import kotlinx.coroutines.runBlocking -import java.util.concurrent.CompletableFuture -import java.util.concurrent.atomic.AtomicInteger -import kotlin.concurrent.thread +import kotlinx.coroutines.yield import kotlin.test.Test import kotlin.test.assertEquals import kotlin.test.assertSame @@ -40,61 +38,29 @@ internal class LateinitMutablePropertyTest { @Test fun initializerCalledOnce() { val value = Symbol("expected") - val counter = AtomicInteger(0) + var counter = 0 val prop by lateinitMutableProperty { - counter.incrementAndGet() + counter++ value } assertSame(value, prop) assertSame(value, prop) - assertEquals(1, counter.get()) + assertEquals(1, counter) } @Test - fun initializerCalledOnceConcurrent() = runBlocking { - val value = Symbol("expected") - val counter = AtomicInteger(0) - - val verySlowInitializer = CompletableFuture() - - - val prop by lateinitMutableProperty { - counter.incrementAndGet() - verySlowInitializer.join() // do not use coroutine: coroutines run in same thread so `synchronized` doesnt work. - value - } - - - val lock = CompletableDeferred() - repeat(10) { - launch { - lock.join() - @Suppress("UNUSED_EXPRESSION") - prop - } - } - lock.complete(Unit) // resume callers - - - verySlowInitializer.complete(Unit) - - assertSame(value, prop) - assertEquals(1, counter.get()) - } - - @Test - fun setValuePrevailsOnCompetitionWithInitializer() { - val verySlowInitializer = CompletableFuture() + fun setValuePrevailsOnCompetitionWithInitializer() = runBlocking { + val verySlowInitializer = CompletableDeferred() val override = Symbol("override") val initializer = Symbol("initializer") var prop by lateinitMutableProperty { - verySlowInitializer.join() + runBlocking { yield(); verySlowInitializer.await() } initializer } - thread { println(prop) } + launch { println(prop) } prop = override verySlowInitializer.complete(Unit) diff --git a/mirai-core-utils/src/commonTest/kotlin/net/mamoe/mirai/utils/ResourceAccessLockTest.kt b/mirai-core-utils/src/commonTest/kotlin/net/mamoe/mirai/utils/ResourceAccessLockTest.kt index 74e7f3053..102e22f8e 100644 --- a/mirai-core-utils/src/commonTest/kotlin/net/mamoe/mirai/utils/ResourceAccessLockTest.kt +++ b/mirai-core-utils/src/commonTest/kotlin/net/mamoe/mirai/utils/ResourceAccessLockTest.kt @@ -1,5 +1,5 @@ /* - * Copyright 2019-2021 Mamoe Technologies and contributors. + * Copyright 2019-2022 Mamoe Technologies and contributors. * * 此源代码的使用受 GNU AFFERO GENERAL PUBLIC LICENSE version 3 许可证的约束, 可以在以下链接找到该许可证. * Use of this source code is governed by the GNU AGPLv3 license that can be found through the following link. @@ -9,11 +9,7 @@ package net.mamoe.mirai.utils -import org.junit.jupiter.api.Test -import kotlin.test.assertEquals -import kotlin.test.assertFails -import kotlin.test.assertFalse -import kotlin.test.assertTrue +import kotlin.test.* internal class ResourceAccessLockTest { @Test diff --git a/mirai-core-utils/src/commonTest/kotlin/net/mamoe/mirai/utils/SizedCacheTest.kt b/mirai-core-utils/src/commonTest/kotlin/net/mamoe/mirai/utils/SizedCacheTest.kt index eb3b368d6..e7477ee8c 100644 --- a/mirai-core-utils/src/commonTest/kotlin/net/mamoe/mirai/utils/SizedCacheTest.kt +++ b/mirai-core-utils/src/commonTest/kotlin/net/mamoe/mirai/utils/SizedCacheTest.kt @@ -1,5 +1,5 @@ /* - * Copyright 2019-2021 Mamoe Technologies and contributors. + * Copyright 2019-2022 Mamoe Technologies and contributors. * * 此源代码的使用受 GNU AFFERO GENERAL PUBLIC LICENSE version 3 许可证的约束, 可以在以下链接找到该许可证. * Use of this source code is governed by the GNU AGPLv3 license that can be found through the following link. @@ -9,7 +9,7 @@ package net.mamoe.mirai.utils -import org.junit.jupiter.api.Test +import kotlin.test.Test import kotlin.test.assertEquals internal class SizedCacheTest { diff --git a/mirai-core-utils/src/commonTest/kotlin/net/mamoe/mirai/utils/TrySafelyTest.kt b/mirai-core-utils/src/commonTest/kotlin/net/mamoe/mirai/utils/TrySafelyTest.kt index 921a750aa..b57b256fc 100644 --- a/mirai-core-utils/src/commonTest/kotlin/net/mamoe/mirai/utils/TrySafelyTest.kt +++ b/mirai-core-utils/src/commonTest/kotlin/net/mamoe/mirai/utils/TrySafelyTest.kt @@ -1,5 +1,5 @@ /* - * Copyright 2019-2021 Mamoe Technologies and contributors. + * Copyright 2019-2022 Mamoe Technologies and contributors. * * 此源代码的使用受 GNU AFFERO GENERAL PUBLIC LICENSE version 3 许可证的约束, 可以在以下链接找到该许可证. * Use of this source code is governed by the GNU AGPLv3 license that can be found through the following link. @@ -9,10 +9,10 @@ package net.mamoe.mirai.utils -import org.junit.jupiter.api.Test -import org.junit.jupiter.api.assertThrows -import java.io.IOException +import io.ktor.utils.io.errors.* +import kotlin.test.Test import kotlin.test.assertEquals +import kotlin.test.assertFailsWith import kotlin.test.assertIs internal class TrySafelyTest { @@ -40,7 +40,7 @@ internal class TrySafelyTest { @Test fun `can run finally when exception in block`() { var x = 0 - assertThrows { + assertFailsWith { trySafely( block = { throw Exception() }, finally = { x = 1 } @@ -51,13 +51,13 @@ internal class TrySafelyTest { @Test fun `can run finally catching`() { - assertThrows { + assertFailsWith { trySafely( block = { throw NoSuchElementException() }, - finally = { throw IOException() } + finally = { throw IOException("") } ) }.let { e -> - assertIs(e.suppressed.single()) + assertIs(e.suppressedExceptions.single()) } } } \ No newline at end of file diff --git a/mirai-core-utils/src/commonTest/kotlin/net/mamoe/mirai/utils/TypeSafeMapTest.kt b/mirai-core-utils/src/commonTest/kotlin/net/mamoe/mirai/utils/TypeSafeMapTest.kt index bc8f8ddce..049ee39de 100644 --- a/mirai-core-utils/src/commonTest/kotlin/net/mamoe/mirai/utils/TypeSafeMapTest.kt +++ b/mirai-core-utils/src/commonTest/kotlin/net/mamoe/mirai/utils/TypeSafeMapTest.kt @@ -23,7 +23,7 @@ internal class TypeSafeMapTest { @Test fun `can set get`() { - val map = MutableTypeSafeMap() + val map = createMutableTypeSafeMap() map[myKey] = "str" map[myKey2] = "str2" assertEquals(2, map.size) @@ -34,7 +34,7 @@ internal class TypeSafeMapTest { @Test fun `test nulls`() { - val map = MutableTypeSafeMap() + val map = createMutableTypeSafeMap() map[myNullableKey] = null map[myNullableKey2] = "str2" assertEquals(2, map.size) @@ -44,7 +44,7 @@ internal class TypeSafeMapTest { @Test fun `key is inlined`() { - val map = MutableTypeSafeMap() + val map = createMutableTypeSafeMap() map[TypeKey("test")] = "str" map[TypeKey("test")] = "str2" assertEquals(1, map.size) @@ -53,7 +53,7 @@ internal class TypeSafeMapTest { @Test fun `can toMap`() { - val map = MutableTypeSafeMap() + val map = createMutableTypeSafeMap() map[myKey] = "str" map[myKey2] = "str2" assertEquals(2, map.size) @@ -67,7 +67,7 @@ internal class TypeSafeMapTest { @Test fun `test serialization`() { - val map = MutableTypeSafeMap() + val map = createMutableTypeSafeMap() map[myKey] = "str" map[myKey2] = "str2" assertEquals(2, map.size) @@ -86,7 +86,7 @@ internal class TypeSafeMapTest { val string = yaml.encodeToString(map1) println(string) // { "test2": "str2" ,"test": "str" } - val result = MutableTypeSafeMap(Yaml.decodeMapFromString(string).cast()) + val result = createMutableTypeSafeMap(Yaml.decodeMapFromString(string).cast()) assertEquals(map, result) } } \ No newline at end of file diff --git a/mirai-core-utils/src/commonMain/kotlin/Closeable.kt b/mirai-core-utils/src/jvmBaseMain/kotlin/Clock.kt similarity index 76% rename from mirai-core-utils/src/commonMain/kotlin/Closeable.kt rename to mirai-core-utils/src/jvmBaseMain/kotlin/Clock.kt index 8deebf9f0..a12644c07 100644 --- a/mirai-core-utils/src/commonMain/kotlin/Closeable.kt +++ b/mirai-core-utils/src/jvmBaseMain/kotlin/Clock.kt @@ -9,9 +9,4 @@ package net.mamoe.mirai.utils -import io.ktor.utils.io.errors.* - -public expect interface Closeable { - @Throws(IOException::class) - public fun close() -} \ No newline at end of file +public actual inline fun measureTimeMillis(block: () -> Unit): Long = kotlin.system.measureTimeMillis(block) \ No newline at end of file diff --git a/mirai-core-utils/src/jvmBaseMain/kotlin/Collections.kt b/mirai-core-utils/src/jvmBaseMain/kotlin/Collections.kt index 4da4ae987..9be23ec12 100644 --- a/mirai-core-utils/src/jvmBaseMain/kotlin/Collections.kt +++ b/mirai-core-utils/src/jvmBaseMain/kotlin/Collections.kt @@ -10,28 +10,21 @@ package net.mamoe.mirai.utils import java.util.* +import java.util.concurrent.CopyOnWriteArraySet +import kotlin.reflect.KClass -public fun Collection.asImmutable(): Collection { - return when (this) { - is List -> asImmutable() - is Set -> asImmutable() - else -> Collections.unmodifiableCollection(this) - } -} - -@Suppress("NOTHING_TO_INLINE") -public inline fun Collection.asImmutableStrict(): Collection { +public actual fun Collection.asImmutable(): Collection { return Collections.unmodifiableCollection(this) } @Suppress("NOTHING_TO_INLINE") -public inline fun List.asImmutable(): List { +public actual inline fun List.asImmutable(): List { return Collections.unmodifiableList(this) } @Suppress("NOTHING_TO_INLINE") -public inline fun Set.asImmutable(): Set { +public actual inline fun Set.asImmutable(): Set { return Collections.unmodifiableSet(this) } @@ -43,4 +36,22 @@ public inline fun Map.asImmutable(): Map { @Suppress("FunctionName") public actual fun ConcurrentHashMap(): MutableMap { return java.util.concurrent.ConcurrentHashMap() +} + +public actual typealias LinkedList = java.util.LinkedList + + +public actual typealias MutableDeque = java.util.Deque + +public actual typealias MutableQueue = java.util.Queue + + +@Suppress("FunctionName") +public actual fun , V> EnumMap(clazz: KClass): MutableMap { + return EnumMap(clazz.java) +} + +@Suppress("FunctionName") +public actual fun ConcurrentSet(): MutableSet { + return CopyOnWriteArraySet() } \ No newline at end of file diff --git a/mirai-core-utils/src/nativeMain/kotlin/ConcurrentHashMap.kt b/mirai-core-utils/src/jvmBaseMain/kotlin/ConcurrentLinkedQueue.kt similarity index 77% rename from mirai-core-utils/src/nativeMain/kotlin/ConcurrentHashMap.kt rename to mirai-core-utils/src/jvmBaseMain/kotlin/ConcurrentLinkedQueue.kt index f5e873c59..8f5bb43e1 100644 --- a/mirai-core-utils/src/nativeMain/kotlin/ConcurrentHashMap.kt +++ b/mirai-core-utils/src/jvmBaseMain/kotlin/ConcurrentLinkedQueue.kt @@ -9,7 +9,8 @@ package net.mamoe.mirai.utils + @Suppress("FunctionName") -public actual fun ConcurrentHashMap(): MutableMap { - TODO("Not yet implemented") +public actual fun ConcurrentLinkedDeque(): MutableDeque { + return java.util.concurrent.ConcurrentLinkedDeque() } \ No newline at end of file diff --git a/mirai-core-utils/src/jvmBaseMain/kotlin/Crypto.kt b/mirai-core-utils/src/jvmBaseMain/kotlin/Crypto.kt index 6d726bbb4..c248c3b7b 100644 --- a/mirai-core-utils/src/jvmBaseMain/kotlin/Crypto.kt +++ b/mirai-core-utils/src/jvmBaseMain/kotlin/Crypto.kt @@ -12,11 +12,9 @@ package net.mamoe.mirai.utils -import io.ktor.utils.io.core.* import java.io.ByteArrayOutputStream import java.io.InputStream import java.io.OutputStream -import java.net.Inet4Address import java.security.MessageDigest import java.util.zip.Deflater import java.util.zip.GZIPInputStream @@ -25,7 +23,7 @@ import java.util.zip.Inflater public actual val DEFAULT_BUFFER_SIZE: Int get() = kotlin.io.DEFAULT_BUFFER_SIZE -public actual fun ByteArray.unzip(offset: Int, length: Int): ByteArray { +public actual fun ByteArray.inflate(offset: Int, length: Int): ByteArray { checkOffsetAndLength(offset, length) if (length == 0) return ByteArray(0) @@ -44,10 +42,6 @@ public actual fun ByteArray.unzip(offset: Int, length: Int): ByteArray { } } -public actual fun localIpAddress(): String = runCatching { - Inet4Address.getLocalHost().hostAddress -}.getOrElse { "192.168.1.123" } - public fun InputStream.md5(): ByteArray { return digest("md5") } @@ -104,7 +98,7 @@ public actual fun ByteArray.gzip(offset: Int, length: Int): ByteArray { } @JvmOverloads -public actual fun ByteArray.zip(offset: Int, length: Int): ByteArray { +public actual fun ByteArray.deflate(offset: Int, length: Int): ByteArray { checkOffsetAndLength(offset, length) if (length == 0) return ByteArray(0) @@ -117,4 +111,3 @@ public actual fun ByteArray.zip(offset: Int, length: Int): ByteArray { } } -public actual fun availableProcessors(): Int = Runtime.getRuntime().availableProcessors() \ No newline at end of file diff --git a/mirai-core-utils/src/jvmBaseMain/kotlin/File.kt b/mirai-core-utils/src/jvmBaseMain/kotlin/Files.kt similarity index 92% rename from mirai-core-utils/src/jvmBaseMain/kotlin/File.kt rename to mirai-core-utils/src/jvmBaseMain/kotlin/Files.kt index f8be1eefb..1434dc1bd 100644 --- a/mirai-core-utils/src/jvmBaseMain/kotlin/File.kt +++ b/mirai-core-utils/src/jvmBaseMain/kotlin/Files.kt @@ -7,9 +7,6 @@ * https://github.com/mamoe/mirai/blob/dev/LICENSE */ -@file:JvmMultifileClass -@file:JvmName("MiraiUtils") - package net.mamoe.mirai.utils import java.io.File @@ -17,7 +14,7 @@ import java.io.File public fun File.createFileIfNotExists() { if (!this.exists()) { - this.parentFile.mkdirs() + this.parentFile?.mkdirs() this.createNewFile() } } diff --git a/mirai-core-utils/src/jvmBaseMain/kotlin/MiraiFile.kt b/mirai-core-utils/src/jvmBaseMain/kotlin/MiraiFile.kt new file mode 100644 index 000000000..73f8a3b67 --- /dev/null +++ b/mirai-core-utils/src/jvmBaseMain/kotlin/MiraiFile.kt @@ -0,0 +1,104 @@ +/* + * Copyright 2019-2022 Mamoe Technologies and contributors. + * + * 此源代码的使用受 GNU AFFERO GENERAL PUBLIC LICENSE version 3 许可证的约束, 可以在以下链接找到该许可证. + * Use of this source code is governed by the GNU AGPLv3 license that can be found through the following link. + * + * https://github.com/mamoe/mirai/blob/dev/LICENSE + */ + +package net.mamoe.mirai.utils + +import io.ktor.utils.io.core.* +import io.ktor.utils.io.streams.* +import java.io.File + +public actual interface MiraiFile { + /** + * Name of this file or directory. Can be '.' and '..' if created by + */ + public actual val name: String + + /** + * Parent of this file or directory. + */ + public actual val parent: MiraiFile? + + /** + * Input path from [create]. + */ + public actual val path: String + + /** + * Normalized absolute [path]. + */ + public actual val absolutePath: String + + public actual val length: Long + + public actual val isFile: Boolean + public actual val isDirectory: Boolean + + public actual fun exists(): Boolean + + /** + * Resolves a [MiraiFile] representing the [path] based on this [MiraiFile]. Result path is not guaranteed to be normalized. + */ + public actual fun resolve(path: String): MiraiFile + public actual fun resolve(file: MiraiFile): MiraiFile + + public actual fun createNewFile(): Boolean + public actual fun delete(): Boolean + + public actual fun mkdir(): Boolean + public actual fun mkdirs(): Boolean + + public actual fun input(): Input + public actual fun output(): Output + + public actual companion object { + public actual fun create(path: String): MiraiFile { + return File(path).asMiraiFile() + } + + public actual fun getWorkingDir(): MiraiFile { + return create(System.getProperty("user.dir")) + } + } +} + +public actual fun MiraiFile.deleteRecursively(): Boolean { + return this.toJvmFile().deleteRecursively() +} + +public fun File.asMiraiFile(): MiraiFile { + return JvmFileAsMiraiFile(this) +} + +public fun MiraiFile.toJvmFile(): File { + if (this is JvmFileAsMiraiFile) { + return jvmFile + } + return File(absolutePath) +} + +internal class JvmFileAsMiraiFile( + internal val jvmFile: File +) : MiraiFile { + override val name: String get() = jvmFile.name + override val parent: MiraiFile? get() = jvmFile.parentFile?.asMiraiFile() + override val path: String get() = jvmFile.path + override val absolutePath: String get() = jvmFile.absolutePath + override val length: Long get() = jvmFile.length() + override val isFile: Boolean get() = jvmFile.isFile + override val isDirectory: Boolean get() = jvmFile.isDirectory + override fun exists(): Boolean = jvmFile.exists() + override fun resolve(path: String): MiraiFile = jvmFile.resolve(path).asMiraiFile() + override fun resolve(file: MiraiFile): MiraiFile = jvmFile.resolve(file.absolutePath).asMiraiFile() + override fun createNewFile(): Boolean = jvmFile.createNewFile() + override fun delete(): Boolean = jvmFile.delete() + override fun mkdir(): Boolean = jvmFile.mkdir() + override fun mkdirs(): Boolean = jvmFile.mkdirs() + override fun input(): Input = jvmFile.inputStream().asInput() + override fun output(): Output = jvmFile.outputStream().asOutput() +} diff --git a/mirai-core-utils/src/jvmBaseMain/kotlin/Serialization.kt b/mirai-core-utils/src/jvmBaseMain/kotlin/Serialization.kt index 0696576ce..3bbeda1db 100644 --- a/mirai-core-utils/src/jvmBaseMain/kotlin/Serialization.kt +++ b/mirai-core-utils/src/jvmBaseMain/kotlin/Serialization.kt @@ -14,24 +14,3 @@ import kotlinx.serialization.DeserializationStrategy import kotlinx.serialization.StringFormat import java.io.File - -public fun File.loadNotBlankAs( - serializer: DeserializationStrategy, - stringFormat: StringFormat, -): T? { - if (!this.exists() || this.length() == 0L) { - return null - } - return stringFormat.decodeFromString(serializer, this.readText()) -} - -public fun File.loadNotBlankAs( - serializer: DeserializationStrategy, - binaryFormat: BinaryFormat, -): T? { - if (!this.exists() || this.length() == 0L) { - return null - } - return binaryFormat.decodeFromByteArray(serializer, this.readBytes()) -} - diff --git a/mirai-core-utils/src/jvmBaseMain/kotlin/Services.kt b/mirai-core-utils/src/jvmBaseMain/kotlin/Services.kt new file mode 100644 index 000000000..979c380e8 --- /dev/null +++ b/mirai-core-utils/src/jvmBaseMain/kotlin/Services.kt @@ -0,0 +1,38 @@ +/* + * Copyright 2019-2022 Mamoe Technologies and contributors. + * + * 此源代码的使用受 GNU AFFERO GENERAL PUBLIC LICENSE version 3 许可证的约束, 可以在以下链接找到该许可证. + * Use of this source code is governed by the GNU AGPLv3 license that can be found through the following link. + * + * https://github.com/mamoe/mirai/blob/dev/LICENSE + */ + +package net.mamoe.mirai.utils + +import java.util.* +import kotlin.reflect.KClass +import kotlin.reflect.full.createInstance + +public actual fun loadService(clazz: KClass, fallbackImplementation: String?): T { + var suppressed: Throwable? = null + return ServiceLoader.load(clazz.java).firstOrNull() + ?: (if (fallbackImplementation == null) null + else runCatching { findCreateInstance(fallbackImplementation) }.onFailure { suppressed = it }.getOrNull()) + ?: throw NoSuchElementException("Could not find an implementation for service class ${clazz.qualifiedName}").apply { + if (suppressed != null) addSuppressed(suppressed) + } +} + +private fun findCreateInstance(fallbackImplementation: String): T { + return Class.forName(fallbackImplementation).cast>().kotlin.run { objectInstance ?: createInstance() } +} + +public actual fun loadServiceOrNull(clazz: KClass, fallbackImplementation: String?): T? { + return ServiceLoader.load(clazz.java).firstOrNull() + ?: if (fallbackImplementation == null) return null + else runCatching { findCreateInstance(fallbackImplementation) }.getOrNull() +} + +public actual fun loadServices(clazz: KClass): Sequence { + return ServiceLoader.load(clazz.java).asSequence() +} \ No newline at end of file diff --git a/mirai-core-utils/src/jvmBaseMain/kotlin/StandardUtils.kt b/mirai-core-utils/src/jvmBaseMain/kotlin/StandardUtils.kt new file mode 100644 index 000000000..b2941a4ec --- /dev/null +++ b/mirai-core-utils/src/jvmBaseMain/kotlin/StandardUtils.kt @@ -0,0 +1,22 @@ +/* + * Copyright 2019-2022 Mamoe Technologies and contributors. + * + * 此源代码的使用受 GNU AFFERO GENERAL PUBLIC LICENSE version 3 许可证的约束, 可以在以下链接找到该许可证. + * Use of this source code is governed by the GNU AGPLv3 license that can be found through the following link. + * + * https://github.com/mamoe/mirai/blob/dev/LICENSE + */ + +package net.mamoe.mirai.utils + +import java.net.Inet4Address + +internal actual fun isSameClassPlatform(object1: Any, object2: Any): Boolean { + return object1.javaClass == object2.javaClass +} + +public actual fun localIpAddress(): String = runCatching { + Inet4Address.getLocalHost().hostAddress +}.getOrElse { "192.168.1.123" } + +public actual fun availableProcessors(): Int = Runtime.getRuntime().availableProcessors() \ No newline at end of file diff --git a/mirai-core-utils/src/jvmBaseMain/kotlin/TimeUtils.kt b/mirai-core-utils/src/jvmBaseMain/kotlin/TimeUtils.kt index 93016adf5..e487e9df1 100644 --- a/mirai-core-utils/src/jvmBaseMain/kotlin/TimeUtils.kt +++ b/mirai-core-utils/src/jvmBaseMain/kotlin/TimeUtils.kt @@ -9,4 +9,20 @@ package net.mamoe.mirai.utils -public actual fun currentTimeMillis(): Long = System.currentTimeMillis() \ No newline at end of file +import java.text.SimpleDateFormat +import java.util.* + +public actual fun currentTimeMillis(): Long = System.currentTimeMillis() + + +private val timeFormat: SimpleDateFormat by threadLocal { + SimpleDateFormat("yyyy-MM-dd HH:mm:ss", Locale.getDefault()) +} + +public actual fun currentTimeFormatted(format: String?): String { + return if (format == null) { + timeFormat.format(Date()) + } else { + SimpleDateFormat(format, Locale.getDefault()).format(Date()) + } +} \ No newline at end of file diff --git a/mirai-core-utils/src/jvmBaseMain/kotlin/annotations/Range.kt b/mirai-core-utils/src/jvmBaseMain/kotlin/annotations/Range.kt new file mode 100644 index 000000000..93d0d859b --- /dev/null +++ b/mirai-core-utils/src/jvmBaseMain/kotlin/annotations/Range.kt @@ -0,0 +1,13 @@ +/* + * Copyright 2019-2022 Mamoe Technologies and contributors. + * + * 此源代码的使用受 GNU AFFERO GENERAL PUBLIC LICENSE version 3 许可证的约束, 可以在以下链接找到该许可证. + * Use of this source code is governed by the GNU AGPLv3 license that can be found through the following link. + * + * https://github.com/mamoe/mirai/blob/dev/LICENSE + */ + +package net.mamoe.mirai.utils.annotations + + +public actual typealias Range = org.jetbrains.annotations.Range \ No newline at end of file diff --git a/mirai-core-utils/src/jvmBaseMain/kotlin/systemProp.kt b/mirai-core-utils/src/jvmBaseMain/kotlin/systemProp.kt index 7dcf2a861..3f5a0e9cf 100644 --- a/mirai-core-utils/src/jvmBaseMain/kotlin/systemProp.kt +++ b/mirai-core-utils/src/jvmBaseMain/kotlin/systemProp.kt @@ -10,3 +10,8 @@ package net.mamoe.mirai.utils internal actual fun getProperty(name: String, default: String): String? = System.getProperty(name, default) + +internal actual fun setProperty(name: String, value: String) { + System.setProperty(name, value) +} + diff --git a/mirai-core-utils/src/jvmBaseTest/kotlin/LateinitMutablePropertyTestJvm.kt b/mirai-core-utils/src/jvmBaseTest/kotlin/LateinitMutablePropertyTestJvm.kt new file mode 100644 index 000000000..45e11bdc2 --- /dev/null +++ b/mirai-core-utils/src/jvmBaseTest/kotlin/LateinitMutablePropertyTestJvm.kt @@ -0,0 +1,55 @@ +/* + * Copyright 2019-2022 Mamoe Technologies and contributors. + * + * 此源代码的使用受 GNU AFFERO GENERAL PUBLIC LICENSE version 3 许可证的约束, 可以在以下链接找到该许可证. + * Use of this source code is governed by the GNU AGPLv3 license that can be found through the following link. + * + * https://github.com/mamoe/mirai/blob/dev/LICENSE + */ + +package net.mamoe.mirai.utils + +import kotlinx.coroutines.CompletableDeferred +import kotlinx.coroutines.runBlocking +import kotlinx.coroutines.yield +import org.junit.jupiter.api.Test +import java.util.concurrent.CompletableFuture +import kotlin.concurrent.thread +import kotlin.test.assertEquals +import kotlin.test.assertSame + +internal class LateinitMutablePropertyTestJvm { + @Test + fun `initializer called once if requested by multiple threads`() = runBlocking { + val value = Symbol("expected") + var counter = 0 + + val verySlowInitializer = CompletableDeferred() + + + val prop by lateinitMutableProperty { + counter++ + runBlocking { yield(); verySlowInitializer.await() } + value + } + + + // requested by 10 threads + val lock = CompletableFuture() + repeat(10) { + thread { + lock.join() + @Suppress("UNUSED_EXPRESSION") + prop + } + } + lock.complete(Unit) // resume callers + + + verySlowInitializer.complete(Unit) + + assertSame(value, prop) + assertEquals(1, counter) + } + +} \ No newline at end of file diff --git a/mirai-core-utils/src/jvmMain/kotlin/Actuals.kt b/mirai-core-utils/src/jvmMain/kotlin/Actuals.kt index 417c334f5..9b100f3f1 100644 --- a/mirai-core-utils/src/jvmMain/kotlin/Actuals.kt +++ b/mirai-core-utils/src/jvmMain/kotlin/Actuals.kt @@ -1,10 +1,10 @@ /* - * Copyright 2019-2021 Mamoe Technologies and contributors. + * Copyright 2019-2022 Mamoe Technologies and contributors. * - * 此源代码的使用受 GNU AFFERO GENERAL PUBLIC LICENSE version 3 许可证的约束, 可以在以下链接找到该许可证. - * Use of this source code is governed by the GNU AGPLv3 license that can be found through the following link. + * 此源代码的使用受 GNU AFFERO GENERAL PUBLIC LICENSE version 3 许可证的约束, 可以在以下链接找到该许可证. + * Use of this source code is governed by the GNU AGPLv3 license that can be found through the following link. * - * https://github.com/mamoe/mirai/blob/master/LICENSE + * https://github.com/mamoe/mirai/blob/dev/LICENSE */ @file:JvmMultifileClass @@ -13,8 +13,6 @@ package net.mamoe.mirai.utils import java.util.* -import kotlin.reflect.KClass -import kotlin.reflect.full.createInstance public actual fun ByteArray.encodeBase64(): String { @@ -30,24 +28,4 @@ public actual inline fun Throwable.unwrap(): Throwable { return this.findCause { it !is E } ?.also { it.addSuppressed(this) } ?: this -} - -public actual fun loadService(clazz: KClass, fallbackImplementation: String?): T { - var suppressed: Throwable? = null - return ServiceLoader.load(clazz.java).firstOrNull() - ?: (if (fallbackImplementation == null) null - else runCatching { findCreateInstance(fallbackImplementation) }.onFailure { suppressed = it }.getOrNull()) - ?: throw NoSuchElementException("Could not find an implementation for service class ${clazz.qualifiedName}").apply { - if (suppressed != null) addSuppressed(suppressed) - } -} - -private fun findCreateInstance(fallbackImplementation: String): T { - return Class.forName(fallbackImplementation).cast>().kotlin.run { objectInstance ?: createInstance() } -} - -public actual fun loadServiceOrNull(clazz: KClass, fallbackImplementation: String?): T? { - return ServiceLoader.load(clazz.java).firstOrNull() - ?: if (fallbackImplementation == null) return null - else runCatching { findCreateInstance(fallbackImplementation) }.getOrNull() } \ No newline at end of file diff --git a/mirai-core-utils/src/jvmTest/kotlin/SecretsProtectionTest.kt b/mirai-core-utils/src/jvmTest/kotlin/SecretsProtectionTest.kt index 53617e6ff..750f30021 100644 --- a/mirai-core-utils/src/jvmTest/kotlin/SecretsProtectionTest.kt +++ b/mirai-core-utils/src/jvmTest/kotlin/SecretsProtectionTest.kt @@ -1,5 +1,5 @@ /* - * Copyright 2019-2021 Mamoe Technologies and contributors. + * Copyright 2019-2022 Mamoe Technologies and contributors. * * 此源代码的使用受 GNU AFFERO GENERAL PUBLIC LICENSE version 3 许可证的约束, 可以在以下链接找到该许可证. * Use of this source code is governed by the GNU AGPLv3 license that can be found through the following link. @@ -12,7 +12,7 @@ package net.mamoe.mirai.utils import kotlinx.coroutines.delay import kotlinx.coroutines.launch import kotlinx.coroutines.runBlocking -import org.junit.jupiter.api.Test +import kotlin.test.Test import kotlin.test.assertContentEquals import kotlin.test.assertTrue diff --git a/mirai-core-utils/src/mingwMain/kotlin/MiraiFileImpl.kt b/mirai-core-utils/src/mingwMain/kotlin/MiraiFileImpl.kt new file mode 100644 index 000000000..1c09c4a82 --- /dev/null +++ b/mirai-core-utils/src/mingwMain/kotlin/MiraiFileImpl.kt @@ -0,0 +1,174 @@ +/* + * Copyright 2019-2022 Mamoe Technologies and contributors. + * + * 此源代码的使用受 GNU AFFERO GENERAL PUBLIC LICENSE version 3 许可证的约束, 可以在以下链接找到该许可证. + * Use of this source code is governed by the GNU AGPLv3 license that can be found through the following link. + * + * https://github.com/mamoe/mirai/blob/dev/LICENSE + */ + +package net.mamoe.mirai.utils + +import io.ktor.utils.io.core.* +import kotlinx.cinterop.* +import platform.posix.PATH_MAX +import platform.posix.fopen +import platform.posix.getcwd +import platform.windows.* + + +internal actual class MiraiFileImpl actual constructor( + // canonical + path: String +) : MiraiFile { + override val path = path.replace("/", "\\") + + actual companion object { + private val ROOT_REGEX = Regex("""^([a-zA-z]+:[/\\])""") + private const val SEPARATOR = '\\' + + @Suppress("UnnecessaryOptInAnnotation") + @OptIn(UnsafeNumber::class) + actual fun getWorkingDir(): MiraiFile { + val path = memScoped { + ByteArray(PATH_MAX).usePinned { + getcwd(it.addressOf(0), it.get().size.convert()) + it.get().toKString() + } + } + return MiraiFile.create(path) + } + } + + override val absolutePath: String = kotlin.run { + val result = ROOT_REGEX.matchEntire(path) ?: return@run path.dropLastWhile { it.isSeparator() } + return@run result.groups.first()!!.value + } + + private fun Char.isSeparator() = this == '/' || this == '\\' + + override val parent: MiraiFile? by lazy { + val absolute = absolutePath + val p = absolute.substringBeforeLast(SEPARATOR, "") + if (p.isEmpty()) { + return@lazy null + } + if (p.lastOrNull() == ':') { + if (absolute.lastIndexOf(SEPARATOR) == p.lastIndex) { + // file is C:/ + return@lazy null + } else { + return@lazy MiraiFileImpl("$p/") // file is C:/xxx + } + } + MiraiFileImpl(p) + } + + override val name: String + get() = if (absolutePath.matches(ROOT_REGEX)) absolutePath + else absolutePath.substringAfterLast('/') + + init { + checkName(absolutePath.substringAfterLast('/')) // do not check drive letter + } + + private fun checkName(name: String) { + name.substringAfterLast('/').forEach { c -> + if (c in """\/:?*"><|""") { + throw IllegalArgumentException("'${name}' contains illegal character '$c'.") + } + } + + memScoped { + val b = alloc() + CheckNameLegalDOS8Dot3A(absolutePath, nullPtr(), 0, nullPtr(), b.ptr) + if (b.value != 1) { + throw IllegalArgumentException("'${name}' contains illegal character.") + } + } + } + + override val length: Long + get() = useStat { it.st_size.convert() } ?: 0 + + + override val isFile: Boolean + get() = getFileAttributes() flag FILE_ATTRIBUTE_NORMAL + + override val isDirectory: Boolean + get() = getFileAttributes() flag FILE_ATTRIBUTE_DIRECTORY + + override fun exists(): Boolean = getFileAttributes() != INVALID_FILE_ATTRIBUTES + + private fun getFileAttributes(): DWORD = memScoped { GetFileAttributesA(absolutePath) } + + override fun resolve(path: String): MiraiFile { + when (path) { + "." -> return this + ".." -> return parent ?: this // root + } + + if (ROOT_REGEX.find(path) != null) { // absolute + return MiraiFileImpl(path) + } + + return MiraiFileImpl(this.absolutePath + SEPARATOR + path) // assuming path is 'appendable' + } + + override fun resolve(file: MiraiFile): MiraiFile { + val parent = file.parent ?: return resolve(file.name) + return resolve(parent).resolve(file.name) + } + + override fun createNewFile(): Boolean { + memScoped { + // https://docs.microsoft.com/en-us/windows/win32/api/fileapi/nf-fileapi-createfilea + val handle = CreateFileA( + absolutePath, + GENERIC_READ, + FILE_SHARE_WRITE, + nullPtr(), + CREATE_NEW, + FILE_ATTRIBUTE_NORMAL, + nullPtr() + ) + if (handle == NULL) return false + CloseHandle(handle) + return true + } + } + + override fun delete(): Boolean { + return if (isFile) { + DeleteFileA(absolutePath) == 0 + } else { + RemoveDirectoryA(absolutePath) == 0 + } + } + + override fun mkdir(): Boolean { + memScoped { + val v = alloc<_SECURITY_ATTRIBUTES>() + return CreateDirectoryA(absolutePath, v.ptr) == 0 + } + } + + override fun mkdirs(): Boolean { + if (this.parent?.mkdirs() == false) { + return false + } + return mkdir() + } + + override fun input(): Input { + val handle = fopen(absolutePath, "r") + if (handle == NULL) throw IllegalStateException("Failed to open file '$absolutePath'") + return PosixInputForFile(handle!!) + } + + override fun output(): Output { + val handle = fopen(absolutePath, "w") + if (handle == NULL) throw IllegalStateException("Failed to open file '$absolutePath'") + return PosixFileInstanceOutput(handle!!) + } +} \ No newline at end of file diff --git a/mirai-core-utils/src/nativeMain/kotlin/Closeable.kt b/mirai-core-utils/src/mingwMain/kotlin/StandardUtils.kt similarity index 75% rename from mirai-core-utils/src/nativeMain/kotlin/Closeable.kt rename to mirai-core-utils/src/mingwMain/kotlin/StandardUtils.kt index e66e2f71f..0d77f28fd 100644 --- a/mirai-core-utils/src/nativeMain/kotlin/Closeable.kt +++ b/mirai-core-utils/src/mingwMain/kotlin/StandardUtils.kt @@ -9,9 +9,6 @@ package net.mamoe.mirai.utils -import io.ktor.utils.io.errors.* +import platform.windows.GetCurrentProcessorNumber -public actual interface Closeable { - @Throws(IOException::class) - public actual fun close() -} \ No newline at end of file +public actual fun availableProcessors(): Int = GetCurrentProcessorNumber().toInt() \ No newline at end of file diff --git a/mirai-core-utils/src/mingwTest/kotlin/MiraiFileImplTest.kt b/mirai-core-utils/src/mingwTest/kotlin/MiraiFileImplTest.kt new file mode 100644 index 000000000..f8bfbee5d --- /dev/null +++ b/mirai-core-utils/src/mingwTest/kotlin/MiraiFileImplTest.kt @@ -0,0 +1,35 @@ +/* + * Copyright 2019-2022 Mamoe Technologies and contributors. + * + * 此源代码的使用受 GNU AFFERO GENERAL PUBLIC LICENSE version 3 许可证的约束, 可以在以下链接找到该许可证. + * Use of this source code is governed by the GNU AGPLv3 license that can be found through the following link. + * + * https://github.com/mamoe/mirai/blob/dev/LICENSE + */ + +package net.mamoe.mirai.utils + +import kotlin.math.absoluteValue +import kotlin.random.Random +import kotlin.test.Test +import kotlin.test.assertEquals + +internal class WindowsMiraiFileImplTest : AbstractNativeMiraiFileImplTest() { + private val rand = Random.nextInt().absoluteValue + override val baseTempDir: MiraiFile = MiraiFile.create("mirai_unit_tests") + override val tempPath = "mirai_unit_tests/temp$rand" + + @Test + override fun parent() { + assertEquals("C:/Users/Shared/mirai_test", tempDir.parent!!.absolutePath) + super.parent() + } + + @Test + override fun `resolve absolute`() { + MiraiFile.create("$tempPath/").resolve("C:/Users").let { + assertEquals("C:/Users", it.path) + assertEquals("C:/Users", it.absolutePath) + } + } +} \ No newline at end of file diff --git a/mirai-core-utils/src/nativeMain/kotlin/Base64.kt b/mirai-core-utils/src/nativeMain/kotlin/Base64.kt index bc4571e8c..2d9ba0af8 100644 --- a/mirai-core-utils/src/nativeMain/kotlin/Base64.kt +++ b/mirai-core-utils/src/nativeMain/kotlin/Base64.kt @@ -7,12 +7,110 @@ * https://github.com/mamoe/mirai/blob/dev/LICENSE */ +@file:Suppress("RedundantVisibilityModifier") + package net.mamoe.mirai.utils +import io.ktor.utils.io.core.* + public actual fun String.decodeBase64(): ByteArray { - TODO("Not yet implemented") + return Base64Impl.decode(this) } public actual fun ByteArray.encodeBase64(): String { - TODO("Not yet implemented") + return Base64Impl.encode(this) +} + +/** + * From . + * @author EmilHernvall + */ +private object Base64Impl { + fun encode(data: ByteArray): String { + val tbl = charArrayOf( + 'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I', 'J', 'K', 'L', 'M', 'N', 'O', 'P', + 'Q', 'R', 'S', 'T', 'U', 'V', 'W', 'X', 'Y', 'Z', 'a', 'b', 'c', 'd', 'e', 'f', + 'g', 'h', 'i', 'j', 'k', 'l', 'm', 'n', 'o', 'p', 'q', 'r', 's', 't', 'u', 'v', + 'w', 'x', 'y', 'z', '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', '+', '/' + ) + val buffer = StringBuilder() + var pad = 0 + var i = 0 + while (i < data.size) { + var b = data[i].toInt() and 0xFF shl 16 and 0xFFFFFF + if (i + 1 < data.size) { + b = b or (data[i + 1].toInt() and 0xFF shl 8) + } else { + pad++ + } + if (i + 2 < data.size) { + b = b or (data[i + 2].toInt() and 0xFF) + } else { + pad++ + } + for (j in 0 until 4 - pad) { + val c = b and 0xFC0000 shr 18 + buffer.append(tbl[c]) + b = b shl 6 + } + i += 3 + } + for (j in 0 until pad) { + buffer.append("=") + } + return buffer.toString() + } + + fun decode(data: String): ByteArray { + val tbl = intArrayOf( + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, 62, -1, -1, -1, 63, 52, 53, 54, + 55, 56, 57, 58, 59, 60, 61, -1, -1, -1, -1, -1, -1, -1, 0, 1, 2, + 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, + 20, 21, 22, 23, 24, 25, -1, -1, -1, -1, -1, -1, 26, 27, 28, 29, 30, + 31, 32, 33, 34, 35, 36, 37, 38, 39, 40, 41, 42, 43, 44, 45, 46, 47, + 48, 49, 50, 51, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1 + ) + val bytes: ByteArray = data.toByteArray() + return buildPacket { + var i = 0 + while (i < bytes.size) { + var b: Int + b = if (tbl[bytes[i].toInt()] != -1) { + tbl[bytes[i].toInt()] and 0xFF shl 18 + } else { + i++ + continue + } + var num = 0 + if (i + 1 < bytes.size && tbl[bytes[i + 1].toInt()] != -1) { + b = b or (tbl[bytes[i + 1].toInt()] and 0xFF shl 12) + num++ + } + if (i + 2 < bytes.size && tbl[bytes[i + 2].toInt()] != -1) { + b = b or (tbl[bytes[i + 2].toInt()] and 0xFF shl 6) + num++ + } + if (i + 3 < bytes.size && tbl[bytes[i + 3].toInt()] != -1) { + b = b or (tbl[bytes[i + 3].toInt()] and 0xFF) + num++ + } + while (num > 0) { + val c = b and 0xFF0000 shr 16 + writeByte(c.toByte()) + b = b shl 8 + num-- + } + i += 4 + } + }.readBytes() + } } \ No newline at end of file diff --git a/mirai-core-utils/src/nativeMain/kotlin/ByteArrayOp.kt b/mirai-core-utils/src/nativeMain/kotlin/ByteArrayOp.kt new file mode 100644 index 000000000..e4c9c616b --- /dev/null +++ b/mirai-core-utils/src/nativeMain/kotlin/ByteArrayOp.kt @@ -0,0 +1,141 @@ +/* + * Copyright 2019-2022 Mamoe Technologies and contributors. + * + * 此源代码的使用受 GNU AFFERO GENERAL PUBLIC LICENSE version 3 许可证的约束, 可以在以下链接找到该许可证. + * Use of this source code is governed by the GNU AGPLv3 license that can be found through the following link. + * + * https://github.com/mamoe/mirai/blob/dev/LICENSE + */ + +@file:Suppress("RedundantVisibilityModifier") + +package net.mamoe.mirai.utils + +import kotlinx.cinterop.* +import platform.zlib.* + + +public actual val DEFAULT_BUFFER_SIZE: Int get() = 8192 + +public actual fun ByteArray.md5(offset: Int, length: Int): ByteArray { + MD5.create().run { + update(this@md5, offset, length) + return digest().bytes + } +} + +public actual fun ByteArray.sha1(offset: Int, length: Int): ByteArray = SHA1.create().run { + update(this@sha1, offset, length) + return digest().bytes +} + +public actual fun ByteArray.gzip(offset: Int, length: Int): ByteArray { + val output = ByteArray(length * 5) + output.usePinned { out -> + usePinned { pin -> + memScoped { + val z = alloc() + z.avail_in = size.toUInt() + z.next_in = pin.addressOf(0).reinterpret() + z.avail_out = output.size.toUInt() + val initialOutAddress = out.addressOf(0) + z.next_out = initialOutAddress.reinterpret() + deflateInit2(z.ptr, Z_DEFAULT_COMPRESSION, Z_DEFLATED, 15 or 16, 8, Z_DEFAULT_STRATEGY) + deflate(z.ptr, Z_FINISH) // TODO: 2022/5/28 buf + deflateEnd(z.ptr) + + val resultSize = z.next_out.toLong() - initialOutAddress.toLong() + return output.copyOf(resultSize.toInt()) + } + } + } +} + +// TODO: 2022/5/28 optimize length + +public actual fun ByteArray.ungzip(offset: Int, length: Int): ByteArray { + val output = ByteArray(length) + output.usePinned { out -> + usePinned { pin -> + memScoped { + val z = alloc() + z.avail_in = size.toUInt() + z.next_in = pin.addressOf(0).reinterpret() + z.avail_out = output.size.toUInt() + val initialOutAddress = out.addressOf(0) + z.next_out = initialOutAddress.reinterpret() + inflateInit2(z.ptr, 15 or 16) + inflate(z.ptr, Z_FINISH) + inflateEnd(z.ptr) + + val resultSize = z.next_out.toLong() - initialOutAddress.toLong() + return output.copyOf(resultSize.toInt()) + } + } + } +} + +public actual fun ByteArray.deflate(offset: Int, length: Int): ByteArray { + val output = ByteArray(length * 2) + output.usePinned { out -> + usePinned { pin -> + memScoped { + val z = alloc() + z.avail_in = size.toUInt() + z.next_in = pin.addressOf(0).reinterpret() + z.avail_out = output.size.toUInt() + val initialOutAddress = out.addressOf(0) + z.next_out = initialOutAddress.reinterpret() + deflateInit(z.ptr, Z_DEFAULT_COMPRESSION) + deflate(z.ptr, Z_FINISH) + deflateEnd(z.ptr) + + val resultSize = z.next_out.toLong() - initialOutAddress.toLong() + return output.copyOf(resultSize.toInt()) + } + } + } +} + +public actual fun ByteArray.inflate(offset: Int, length: Int): ByteArray { + val output = ByteArray(length) + output.usePinned { out -> + usePinned { pin -> + memScoped { + val z = alloc() + z.avail_in = size.toUInt() + z.next_in = pin.addressOf(0).reinterpret() + z.avail_out = output.size.toUInt() + val initialOutAddress = out.addressOf(0) + z.next_out = initialOutAddress.reinterpret() + inflateInit(z.ptr) + inflate(z.ptr, Z_FINISH) + inflateEnd(z.ptr) + + val resultSize = z.next_out.toLong() - initialOutAddress.toLong() + return output.copyOf(resultSize.toInt()) + } + } + } +} + + +//private fun ByteArray.callImpl( +// fn: (CValuesRef, UInt, CValuesRef) -> Boolean, +// offset: Int, +// length: Int +//): ByteArray { +// checkOffsetAndLength(offset, length) +// +// memScoped { +// val r = alloc() +// if (!fn(toCValues().ptr.reinterpret().plus(offset)!!, length.toUInt(), r.ptr)) { +// throw IllegalStateException("Failed platform implementation call") +// } +// try { +// return r.arr?.readBytes(r.size.toInt())!! +// } finally { +// free(r.arr) +// } +// } +//} \ No newline at end of file diff --git a/mirai-core-utils/src/nativeMain/kotlin/currentTimeMillis.kt b/mirai-core-utils/src/nativeMain/kotlin/Clock.kt similarity index 70% rename from mirai-core-utils/src/nativeMain/kotlin/currentTimeMillis.kt rename to mirai-core-utils/src/nativeMain/kotlin/Clock.kt index b36541ab6..42def5877 100644 --- a/mirai-core-utils/src/nativeMain/kotlin/currentTimeMillis.kt +++ b/mirai-core-utils/src/nativeMain/kotlin/Clock.kt @@ -7,13 +7,10 @@ * https://github.com/mamoe/mirai/blob/dev/LICENSE */ +@file:Suppress("RedundantVisibilityModifier") + package net.mamoe.mirai.utils -/** - * 时间戳 - * - * @see System.currentTimeMillis - */ -public actual fun currentTimeMillis(): Long { - TODO("Not yet implemented") +public actual inline fun measureTimeMillis(block: () -> Unit): Long { + return kotlin.system.measureTimeMillis(block) } \ No newline at end of file diff --git a/mirai-core-utils/src/nativeMain/kotlin/Collections.kt b/mirai-core-utils/src/nativeMain/kotlin/Collections.kt new file mode 100644 index 000000000..1c2b70e2d --- /dev/null +++ b/mirai-core-utils/src/nativeMain/kotlin/Collections.kt @@ -0,0 +1,260 @@ +/* + * Copyright 2019-2022 Mamoe Technologies and contributors. + * + * 此源代码的使用受 GNU AFFERO GENERAL PUBLIC LICENSE version 3 许可证的约束, 可以在以下链接找到该许可证. + * Use of this source code is governed by the GNU AGPLv3 license that can be found through the following link. + * + * https://github.com/mamoe/mirai/blob/dev/LICENSE + */ + +@file:Suppress("RedundantVisibilityModifier") + +package net.mamoe.mirai.utils + +import kotlinx.atomicfu.locks.ReentrantLock +import kotlinx.atomicfu.locks.reentrantLock +import kotlinx.atomicfu.locks.withLock +import kotlin.reflect.KClass + +@Suppress("FunctionName") +public actual fun ConcurrentHashMap(): MutableMap { + return LockedConcurrentHashMap(reentrantLock()) +} + +private class LockedConcurrentHashSet( + private val lock: ReentrantLock, + private val delegate: MutableSet = mutableSetOf() +) : MutableSet { + override fun add(element: E): Boolean = lock.withLock { + return delegate.add(element) + } + + override fun addAll(elements: Collection): Boolean = lock.withLock { + return delegate.addAll(elements) + } + + override val size: Int get() = lock.withLock { delegate.size } + + override fun clear() = lock.withLock { delegate.clear() } + + override fun isEmpty(): Boolean = lock.withLock { delegate.isEmpty() } + override fun containsAll(elements: Collection): Boolean = lock.withLock { delegate.containsAll(elements) } + override fun contains(element: E): Boolean = lock.withLock { delegate.contains(element) } + + override fun iterator(): MutableIterator = delegate.iterator() // no effect for locking + + @Suppress("ConvertArgumentToSet") + override fun retainAll(elements: Collection): Boolean = lock.withLock { delegate.retainAll(elements) } + + @Suppress("ConvertArgumentToSet") + override fun removeAll(elements: Collection): Boolean = lock.withLock { delegate.removeAll(elements) } + + override fun remove(element: E): Boolean = lock.withLock { delegate.remove(element) } + + + override fun equals(other: Any?): Boolean = delegate == other + override fun hashCode(): Int = delegate.hashCode() + override fun toString(): String = delegate.toString() +} + +private class LockedConcurrentCollection( + private val lock: ReentrantLock, + private val delegate: MutableCollection +) : MutableCollection { + override fun add(element: E): Boolean = lock.withLock { + return delegate.add(element) + } + + override fun addAll(elements: Collection): Boolean = lock.withLock { + return delegate.addAll(elements) + } + + override val size: Int get() = lock.withLock { delegate.size } + + override fun clear() = lock.withLock { delegate.clear() } + + override fun isEmpty(): Boolean = lock.withLock { delegate.isEmpty() } + override fun containsAll(elements: Collection): Boolean = lock.withLock { delegate.containsAll(elements) } + override fun contains(element: E): Boolean = lock.withLock { delegate.contains(element) } + + override fun iterator(): MutableIterator = delegate.iterator() // no effect for locking + + @Suppress("ConvertArgumentToSet") + override fun retainAll(elements: Collection): Boolean = lock.withLock { delegate.retainAll(elements) } + + @Suppress("ConvertArgumentToSet") + override fun removeAll(elements: Collection): Boolean = lock.withLock { delegate.removeAll(elements) } + + override fun remove(element: E): Boolean = lock.withLock { delegate.remove(element) } + + override fun equals(other: Any?): Boolean = delegate == other + override fun hashCode(): Int = delegate.hashCode() + override fun toString(): String = delegate.toString() +} + +private class LockedConcurrentHashMap( + private val lock: ReentrantLock, + private val delegate: MutableMap = mutableMapOf() +) : MutableMap { + override val entries: MutableSet> + get() = lock.withLock { LockedConcurrentHashSet(lock, delegate.entries) } + + override val keys: MutableSet get() = lock.withLock { LockedConcurrentHashSet(lock, delegate.keys) } + override val size: Int get() = lock.withLock { delegate.size } + override val values: MutableCollection + get() = lock.withLock { LockedConcurrentCollection(lock, delegate.values) } + + override fun clear() = lock.withLock { delegate.clear() } + override fun isEmpty(): Boolean = lock.withLock { delegate.isEmpty() } + override fun remove(key: K): V? = lock.withLock { delegate.remove(key) } + override fun putAll(from: Map) = lock.withLock { delegate.putAll(from) } + override fun put(key: K, value: V): V? = lock.withLock { delegate.put(key, value) } + override fun get(key: K): V? = lock.withLock { delegate.get(key) } + override fun containsValue(value: V): Boolean = lock.withLock { delegate.containsValue(value) } + override fun containsKey(key: K): Boolean = lock.withLock { delegate.containsKey(key) } + + override fun equals(other: Any?): Boolean = delegate == other + override fun hashCode(): Int = delegate.hashCode() + override fun toString(): String = delegate.toString() +} + +@Suppress("FunctionName") +public actual fun ConcurrentLinkedDeque(): MutableDeque { + return LockedConcurrentArrayDeque(reentrantLock()) +} + +private class LockedConcurrentArrayDeque( + private val lock: ReentrantLock, + private val delegate: ArrayDeque = ArrayDeque() +) : MutableDeque { + override fun addFirst(element: E) = lock.withLock { delegate.addFirst(element) } + + override fun add(element: E): Boolean { + lock.withLock { delegate.add(element) } + return true + } + + override fun poll(): E? = lock.withLock { delegate.removeFirstOrNull() } + + override fun offer(element: E): Boolean { + lock.withLock { delegate.addLast(element) } + return true + } + + override val size: Int + get() = lock.withLock { delegate.size } + + override fun clear() = lock.withLock { delegate.clear() } + override fun addAll(elements: Collection): Boolean = lock.withLock { delegate.addAll(elements) } + override fun isEmpty(): Boolean = lock.withLock { delegate.isEmpty() } + override fun iterator(): MutableIterator = delegate.iterator() + override fun retainAll(elements: Collection): Boolean = lock.withLock { delegate.retainAll(elements) } + override fun removeAll(elements: Collection): Boolean = lock.withLock { delegate.removeAll(elements) } + override fun remove(element: E): Boolean = lock.withLock { delegate.remove(element) } + override fun containsAll(elements: Collection): Boolean = lock.withLock { delegate.containsAll(elements) } + override fun contains(element: E): Boolean = lock.withLock { delegate.contains(element) } + + override fun equals(other: Any?): Boolean = delegate == other + override fun hashCode(): Int = delegate.hashCode() + override fun toString(): String = delegate.toString() +} + +internal class ArrayDequeAsMutableDeque( + private val delegate: ArrayDeque = ArrayDeque() +) : MutableDeque { + override fun addFirst(element: E) = delegate.addFirst(element) + + override fun add(element: E): Boolean { + delegate.add(element) + return true + } + + override fun poll(): E? = delegate.removeFirstOrNull() + + override fun offer(element: E): Boolean { + delegate.addLast(element) + return true + } + + override val size: Int + get() = delegate.size + + override fun clear() = delegate.clear() + override fun addAll(elements: Collection): Boolean = delegate.addAll(elements) + override fun isEmpty(): Boolean = delegate.isEmpty() + override fun iterator(): MutableIterator = delegate.iterator() + override fun retainAll(elements: Collection): Boolean = delegate.retainAll(elements) + override fun removeAll(elements: Collection): Boolean = delegate.removeAll(elements) + override fun remove(element: E): Boolean = delegate.remove(element) + override fun containsAll(elements: Collection): Boolean = delegate.containsAll(elements) + override fun contains(element: E): Boolean = delegate.contains(element) + + override fun equals(other: Any?): Boolean = delegate == other + override fun hashCode(): Int = delegate.hashCode() + override fun toString(): String = delegate.toString() +} + +@Suppress("FunctionName") +public actual fun , V> EnumMap(clazz: KClass): MutableMap = mutableMapOf() + +@Suppress("FunctionName") +public actual fun ConcurrentSet(): MutableSet { + return LockedConcurrentHashSet(reentrantLock()) +} + +public actual class LinkedList( + private val delegate: ArrayDeque +) : MutableList by delegate { + public actual constructor() : this(ArrayDeque()) + + public actual fun addLast(element: E) { + return delegate.addLast(element) + } +} + +public actual interface MutableDeque : MutableQueue { + public actual fun addFirst(element: E) +} + +public actual interface MutableQueue : MutableCollection { + /** + * Adds the specified element to the collection. + * + * @return `true` if the element has been added, `false` if the collection does not support duplicates + * and the element is already contained in the collection. + * @throws IllegalStateException if the queue is full. + */ + public actual override fun add(element: E): Boolean + + /** + * Removes and returns the head of the queue, `null` otherwise. + */ + public actual fun poll(): E? + + /** + * Adds an element into the queue. + * @return `true` if the element has been added, `false` if queue is full. + */ + public actual fun offer(element: E): Boolean + + +} + +/** + * Returns a [List] that cannot be cast to [MutableList] to modify it. + */ +public actual fun List.asImmutable(): List = ImmutableList(this) +public actual fun Collection.asImmutable(): Collection = ImmutableCollection(this) +public actual fun Set.asImmutable(): Set = ImmutableSet(this) + +internal class ImmutableList( + private val delegate: List +) : List by delegate + +internal class ImmutableCollection( + private val delegate: Collection +) : Collection by delegate + +internal class ImmutableSet( + private val delegate: Set +) : Set by delegate diff --git a/mirai-core-utils/src/nativeMain/kotlin/CommonDigest.kt b/mirai-core-utils/src/nativeMain/kotlin/CommonDigest.kt new file mode 100644 index 000000000..ea53c3d3a --- /dev/null +++ b/mirai-core-utils/src/nativeMain/kotlin/CommonDigest.kt @@ -0,0 +1,274 @@ +/* + * Copyright 2019-2022 Mamoe Technologies and contributors. + * + * 此源代码的使用受 GNU AFFERO GENERAL PUBLIC LICENSE version 3 许可证的约束, 可以在以下链接找到该许可证. + * Use of this source code is governed by the GNU AGPLv3 license that can be found through the following link. + * + * https://github.com/mamoe/mirai/blob/dev/LICENSE + */ + +package net.mamoe.mirai.utils + +import kotlin.math.abs +import kotlin.math.min +import kotlin.math.sin + +/* + * Note: All the declarations in this file are copied from 'com.soywiz.korlibs.krypto'. + * + * The license is attached: + +MIT License + +Copyright (c) 2017 Carlos Ballesteros Velasco + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. + +---------------------------------- + +/** + * Based on CryptoJS v3.1.2 + * code.google.com/p/crypto-js + * (c) 2009-2013 by Jeff Mott. All rights reserved. + * https://github.com/brix/crypto-js/blob/develop/LICENSE + */ + */ + +internal inline fun Int.ext8(offset: Int) = (this ushr offset) and 0xFF + +internal fun Int.rotateRight(amount: Int): Int = (this ushr amount) or (this shl (32 - amount)) +internal fun Int.rotateLeft(bits: Int): Int = ((this shl bits) or (this ushr (32 - bits))) + +internal fun arraycopy(src: ByteArray, srcPos: Int, dst: ByteArray, dstPos: Int, count: Int) = + src.copyInto(dst, dstPos, srcPos, srcPos + count) + +internal fun arraycopy(src: IntArray, srcPos: Int, dst: IntArray, dstPos: Int, count: Int) = + src.copyInto(dst, dstPos, srcPos, srcPos + count) + +internal fun ByteArray.readU8(o: Int): Int = this[o].toInt() and 0xFF +internal fun ByteArray.readS32_be(o: Int): Int = + (readU8(o + 3) shl 0) or (readU8(o + 2) shl 8) or (readU8(o + 1) shl 16) or (readU8(o + 0) shl 24) + +internal abstract class Hasher(val chunkSize: Int, val digestSize: Int) { + private val chunk = ByteArray(chunkSize) + private var writtenInChunk = 0 + private var totalWritten = 0L + + fun reset(): Hasher { + coreReset() + writtenInChunk = 0 + totalWritten = 0L + return this + } + + fun update(data: ByteArray, offset: Int, count: Int): Hasher { + var curr = offset + var left = count + while (left > 0) { + val remainingInChunk = chunkSize - writtenInChunk + val toRead = min(remainingInChunk, left) + arraycopy(data, curr, chunk, writtenInChunk, toRead) + left -= toRead + curr += toRead + writtenInChunk += toRead + if (writtenInChunk >= chunkSize) { + writtenInChunk -= chunkSize + coreUpdate(chunk) + } + } + totalWritten += count + return this + } + + fun digestOut(out: ByteArray) { + val pad = corePadding(totalWritten) + var padPos = 0 + while (padPos < pad.size) { + val padSize = chunkSize - writtenInChunk + arraycopy(pad, padPos, chunk, writtenInChunk, padSize) + coreUpdate(chunk) + writtenInChunk = 0 + padPos += padSize + } + + coreDigest(out) + coreReset() + } + + protected abstract fun coreReset() + protected abstract fun corePadding(totalWritten: Long): ByteArray + protected abstract fun coreUpdate(chunk: ByteArray) + protected abstract fun coreDigest(out: ByteArray) + + fun update(data: ByteArray) = update(data, 0, data.size) + fun digest(): Hash = Hash(ByteArray(digestSize).also { digestOut(it) }) +} + +internal value class Hash(val bytes: ByteArray) + +internal open class HasherFactory(val create: () -> Hasher) { + fun digest(data: ByteArray) = create().also { it.update(data, 0, data.size) }.digest() + + inline fun digest(temp: ByteArray = ByteArray(0x1000), readBytes: (data: ByteArray) -> Int): Hash = + this.create().also { + while (true) { + val count = readBytes(temp) + if (count <= 0) break + it.update(temp, 0, count) + } + }.digest() +} + +internal class MD5 : Hasher(chunkSize = 64, digestSize = 16) { + companion object : HasherFactory({ MD5() }) { + private val S = intArrayOf(7, 12, 17, 22, 5, 9, 14, 20, 4, 11, 16, 23, 6, 10, 15, 21) + private val T = IntArray(64) { ((1L shl 32) * abs(sin(1.0 + it))).toLong().toInt() } + } + + private val r = IntArray(4) + private val o = IntArray(4) + private val b = IntArray(16) + + init { + coreReset() + } + + override fun coreReset() { + r[0] = 0x67452301 + r[1] = 0xEFCDAB89.toInt() + r[2] = 0x98BADCFE.toInt() + r[3] = 0x10325476 + } + + override fun coreUpdate(chunk: ByteArray) { + for (j in 0 until 64) b[j ushr 2] = (chunk[j].toInt() shl 24) or (b[j ushr 2] ushr 8) + for (j in 0 until 4) o[j] = r[j] + for (j in 0 until 64) { + val d16 = j / 16 + val f = when (d16) { + 0 -> (r[1] and r[2]) or (r[1].inv() and r[3]) + 1 -> (r[1] and r[3]) or (r[2] and r[3].inv()) + 2 -> r[1] xor r[2] xor r[3] + 3 -> r[2] xor (r[1] or r[3].inv()) + else -> 0 + } + val bi = when (d16) { + 0 -> j + 1 -> (j * 5 + 1) and 0x0F + 2 -> (j * 3 + 5) and 0x0F + 3 -> (j * 7) and 0x0F + else -> 0 + } + val temp = r[1] + (r[0] + f + b[bi] + T[j]).rotateLeft(S[(d16 shl 2) or (j and 3)]) + r[0] = r[3] + r[3] = r[2] + r[2] = r[1] + r[1] = temp + } + for (j in 0 until 4) r[j] += o[j] + } + + override fun corePadding(totalWritten: Long): ByteArray { + val numberOfBlocks = ((totalWritten + 8) / chunkSize) + 1 + val totalWrittenBits = totalWritten * 8 + return ByteArray(((numberOfBlocks * chunkSize) - totalWritten).toInt()).apply { + this[0] = 0x80.toByte() + for (i in 0 until 8) this[this.size - 8 + i] = (totalWrittenBits ushr (8 * i)).toByte() + } + } + + override fun coreDigest(out: ByteArray) { + for (it in 0 until 16) out[it] = (r[it / 4] ushr ((it % 4) * 8)).toByte() + } +} + +internal abstract class SHA(chunkSize: Int, digestSize: Int) : Hasher(chunkSize, digestSize) { + override fun corePadding(totalWritten: Long): ByteArray { + val tail = totalWritten % 64 + val padding = (if (64 - tail >= 9) 64 - tail else 128 - tail) + val pad = ByteArray(padding.toInt()).apply { this[0] = 0x80.toByte() } + val bits = (totalWritten * 8) + for (i in 0 until 8) pad[pad.size - 1 - i] = ((bits ushr (8 * i)) and 0xFF).toByte() + return pad + } +} + +internal class SHA1 : SHA(chunkSize = 64, digestSize = 20) { + companion object : HasherFactory({ SHA1() }) { + private val H = intArrayOf( + 0x67452301L.toInt(), + 0xEFCDAB89L.toInt(), + 0x98BADCFEL.toInt(), + 0x10325476L.toInt(), + 0xC3D2E1F0L.toInt() + ) + + private const val K0020: Int = 0x5A827999L.toInt() + private const val K2040: Int = 0x6ED9EBA1L.toInt() + private const val K4060: Int = 0x8F1BBCDCL.toInt() + private const val K6080: Int = 0xCA62C1D6L.toInt() + } + + private val w = IntArray(80) + private val h = IntArray(5) + + override fun coreReset(): Unit { + arraycopy(H, 0, h, 0, 5) + } + + init { + coreReset() + } + + override fun coreUpdate(chunk: ByteArray) { + for (j in 0 until 16) w[j] = chunk.readS32_be(j * 4) + for (j in 16 until 80) w[j] = (w[j - 3] xor w[j - 8] xor w[j - 14] xor w[j - 16]).rotateLeft(1) + + var a = h[0] + var b = h[1] + var c = h[2] + var d = h[3] + var e = h[4] + + for (j in 0 until 80) { + val temp = a.rotateLeft(5) + e + w[j] + when (j / 20) { + 0 -> ((b and c) or ((b.inv()) and d)) + K0020 + 1 -> (b xor c xor d) + K2040 + 2 -> ((b and c) xor (b and d) xor (c and d)) + K4060 + else -> (b xor c xor d) + K6080 + } + + e = d + d = c + c = b.rotateLeft(30) + b = a + a = temp + } + + h[0] += a + h[1] += b + h[2] += c + h[3] += d + h[4] += e + } + + override fun coreDigest(out: ByteArray) { + for (n in out.indices) out[n] = (h[n / 4] ushr (24 - 8 * (n % 4))).toByte() + } +} diff --git a/mirai-core-utils/src/nativeMain/kotlin/CoroutineUtils.kt b/mirai-core-utils/src/nativeMain/kotlin/CoroutineUtils.kt index 72a18d9dd..e3df32ca5 100644 --- a/mirai-core-utils/src/nativeMain/kotlin/CoroutineUtils.kt +++ b/mirai-core-utils/src/nativeMain/kotlin/CoroutineUtils.kt @@ -7,6 +7,8 @@ * https://github.com/mamoe/mirai/blob/dev/LICENSE */ +@file:Suppress("RedundantVisibilityModifier") + package net.mamoe.mirai.utils public actual suspend inline fun runBIO(noinline block: () -> R): R { @@ -14,7 +16,7 @@ public actual suspend inline fun runBIO(noinline block: () -> R): R { } public actual suspend inline fun T.runBIO(crossinline block: T.() -> R): R { - TODO("Not yet implemented") + return block() } /** diff --git a/mirai-core-utils/src/nativeMain/kotlin/Crypto.kt b/mirai-core-utils/src/nativeMain/kotlin/Crypto.kt deleted file mode 100644 index b1cfcf1a4..000000000 --- a/mirai-core-utils/src/nativeMain/kotlin/Crypto.kt +++ /dev/null @@ -1,44 +0,0 @@ -/* - * Copyright 2019-2022 Mamoe Technologies and contributors. - * - * 此源代码的使用受 GNU AFFERO GENERAL PUBLIC LICENSE version 3 许可证的约束, 可以在以下链接找到该许可证. - * Use of this source code is governed by the GNU AGPLv3 license that can be found through the following link. - * - * https://github.com/mamoe/mirai/blob/dev/LICENSE - */ - -package net.mamoe.mirai.utils - - -public actual fun ByteArray.unzip(offset: Int, length: Int): ByteArray { - TODO("Not yet implemented") -} - -public actual fun localIpAddress(): String = "192.168.1.123" - - -public actual fun ByteArray.md5(offset: Int, length: Int): ByteArray { - TODO("Not yet implemented") -} - -public actual val DEFAULT_BUFFER_SIZE: Int get() = 8192 - -public actual fun ByteArray.sha1(offset: Int, length: Int): ByteArray { - TODO("Not yet implemented") -} - -public actual fun ByteArray.ungzip(offset: Int, length: Int): ByteArray { - TODO("Not yet implemented") -} - -public actual fun ByteArray.gzip(offset: Int, length: Int): ByteArray { - TODO("Not yet implemented") -} - -public actual fun ByteArray.zip(offset: Int, length: Int): ByteArray { - TODO("Not yet implemented") -} - -public actual fun availableProcessors(): Int { - TODO("Not yet implemented") -} \ No newline at end of file diff --git a/mirai-core-utils/src/nativeMain/kotlin/ExceptionCollector.kt b/mirai-core-utils/src/nativeMain/kotlin/ExceptionCollector.kt index 46f05c8fd..076f1f9c7 100644 --- a/mirai-core-utils/src/nativeMain/kotlin/ExceptionCollector.kt +++ b/mirai-core-utils/src/nativeMain/kotlin/ExceptionCollector.kt @@ -11,5 +11,9 @@ package net.mamoe.mirai.utils internal actual fun hash(e: Throwable): Long { // Stacktrace analysis not available - return e.hashCode().toLongUnsigned() + var hashCode = 1L + for (stackTraceAddress in e.getStackTraceAddresses()) { + hashCode = (hashCode xor stackTraceAddress).shl(1) + } + return hashCode } \ No newline at end of file diff --git a/mirai-core-utils/src/nativeMain/kotlin/MiraiFile.kt b/mirai-core-utils/src/nativeMain/kotlin/MiraiFile.kt new file mode 100644 index 000000000..f652d61cd --- /dev/null +++ b/mirai-core-utils/src/nativeMain/kotlin/MiraiFile.kt @@ -0,0 +1,219 @@ +/* + * Copyright 2019-2022 Mamoe Technologies and contributors. + * + * 此源代码的使用受 GNU AFFERO GENERAL PUBLIC LICENSE version 3 许可证的约束, 可以在以下链接找到该许可证. + * Use of this source code is governed by the GNU AGPLv3 license that can be found through the following link. + * + * https://github.com/mamoe/mirai/blob/dev/LICENSE + */ + +@file:Suppress("RedundantVisibilityModifier") + +package net.mamoe.mirai.utils + +import io.ktor.utils.io.bits.* +import io.ktor.utils.io.core.* +import io.ktor.utils.io.errors.* +import io.ktor.utils.io.streams.* +import kotlinx.cinterop.* +import platform.posix.* + +/** + * Multiplatform implementation of file operations. + */ +public actual interface MiraiFile { + /** + * Name of this file or directory. Can be '.' and '..' if created by + */ + public actual val name: String + + /** + * Parent of this file or directory. + */ + public actual val parent: MiraiFile? + + /** + * Input path from [create]. + */ + public actual val path: String + + /** + * Normalized absolute [path]. + */ + public actual val absolutePath: String + public actual val length: Long + public actual val isFile: Boolean + public actual val isDirectory: Boolean + public actual fun exists(): Boolean + + /** + * Resolves a [MiraiFile] representing the [path] based on this [MiraiFile]. Result path is not guaranteed to be normalized. + */ + public actual fun resolve(path: String): MiraiFile + public actual fun resolve(file: MiraiFile): MiraiFile + public actual fun createNewFile(): Boolean + public actual fun delete(): Boolean + public actual fun mkdir(): Boolean + public actual fun mkdirs(): Boolean + public actual fun input(): Input + public actual fun output(): Output + + public actual companion object { + public actual fun create(path: String): MiraiFile = MiraiFileImpl(path) + public actual fun getWorkingDir(): MiraiFile = MiraiFileImpl.getWorkingDir() + } +} + + +private val deleteFile = + staticCFunction>?, CPointer?, Int, CPointer?, Int> { pathPtr, _, _, _ -> + val path = pathPtr!!.toKString() + if (remove(path) < 0) { + -1 + } else { + 0 + } + } + +public actual fun MiraiFile.deleteRecursively(): Boolean { + return nftw(absolutePath, deleteFile, 10, FTW_DEPTH or FTW_MOUNT or FTW_PHYS) >= 0 +} + +internal expect class MiraiFileImpl(path: String) : MiraiFile { + companion object { + public fun getWorkingDir(): MiraiFile + } +} + + +/* + Data from https://man7.org/linux/man-pages/man2/lstat.2.html + + + st_dev This field describes the device on which this file + resides. (The major(3) and minor(3) macros may be useful + to decompose the device ID in this field.) + + st_ino This field contains the file's inode number. + + st_mode + This field contains the file type and mode. See inode(7) + for further information. + + S_IFMT 0170000 bit mask for the file type bit field + + S_IFSOCK 0140000 socket + S_IFLNK 0120000 symbolic link + S_IFREG 0100000 regular file + S_IFBLK 0060000 block device + S_IFDIR 0040000 directory + S_IFCHR 0020000 character device + S_IFIFO 0010000 FIFO + + + st_nlink + This field contains the number of hard links to the file. + + st_uid This field contains the user ID of the owner of the file. + + st_gid This field contains the ID of the group owner of the file. + + st_rdev + This field describes the device that this file (inode) + represents. + + st_size + This field gives the size of the file (if it is a regular + file or a symbolic link) in bytes. The size of a symbolic + link is the length of the pathname it contains, without a + terminating null byte. + + st_blksize + This field gives the "preferred" block size for efficient + filesystem I/O. + + st_blocks + This field indicates the number of blocks allocated to the + file, in 512-byte units. (This may be smaller than + st_size/512 when the file has holes.) + + st_atime + This is the time of the last access of file data. + + st_mtime + This is the time of last modification of file data. + + st_ctime + This is the file's last status change timestamp (time of + last change to the inode). + + */ +internal inline fun MiraiFileImpl.useStat(block: (stat) -> R): R? { + memScoped { + val stat = alloc() + val ret = stat(absolutePath, stat.ptr) + if (ret != 0) return null + return block(stat) + } +} + +internal class FileNotFoundException(message: String, cause: Throwable? = null) : + IOException(message, cause) + + +@Suppress("DEPRECATION") +@OptIn(ExperimentalIoApi::class) +internal class PosixFileInstanceOutput(val file: CPointer) : AbstractOutput() { + private var closed = false + + override fun flush(source: Memory, offset: Int, length: Int) { + val end = offset + length + var currentOffset = offset + + while (currentOffset < end) { + val result = fwrite(source, currentOffset, end - currentOffset, file.cast()) + if (result == 0) { + throw PosixException.forErrno(posixFunctionName = "fwrite()").wrapIO() + } + currentOffset += result + } + } + + override fun closeDestination() { + if (closed) return + closed = true + + if (fclose(file) != 0) { + throw PosixException.forErrno(posixFunctionName = "fclose").wrapIO() + } + } +} + +@Suppress("DEPRECATION") +@OptIn(ExperimentalIoApi::class) +internal class PosixInputForFile(val file: CPointer) : AbstractInput() { + private var closed = false + + override fun fill(destination: Memory, offset: Int, length: Int): Int { + val size = fread(destination, offset, length, file.cast()) + if (size == 0) { + if (feof(file) != 0) return 0 + throw PosixException.forErrno(posixFunctionName = "read()").wrapIO() + } + + return size + } + + override fun closeSource() { + if (closed) return + closed = true + + if (fclose(file) != 0) { + throw PosixException.forErrno(posixFunctionName = "fclose()").wrapIO() + } + } +} + +@OptIn(ExperimentalIoApi::class) +internal fun PosixException.wrapIO(): IOException = + IOException("I/O operation failed due to posix error code $errno", this) diff --git a/mirai-core-utils/src/nativeMain/kotlin/NativePlatformSupport.kt b/mirai-core-utils/src/nativeMain/kotlin/NativePlatformSupport.kt new file mode 100644 index 000000000..9c930c234 --- /dev/null +++ b/mirai-core-utils/src/nativeMain/kotlin/NativePlatformSupport.kt @@ -0,0 +1,21 @@ +/* + * Copyright 2019-2022 Mamoe Technologies and contributors. + * + * 此源代码的使用受 GNU AFFERO GENERAL PUBLIC LICENSE version 3 许可证的约束, 可以在以下链接找到该许可证. + * Use of this source code is governed by the GNU AGPLv3 license that can be found through the following link. + * + * https://github.com/mamoe/mirai/blob/dev/LICENSE + */ + +package net.mamoe.mirai.utils + +import kotlinx.cinterop.* + +public inline infix fun UShort.flag(flag: UShort): Boolean = this and flag != 0u.toUShort() +public inline infix fun UInt.flag(flag: UInt): Boolean = this and flag != 0u +public inline infix fun UInt.flag(flag: Int): Boolean = this and flag.toUInt() != 0u +public inline infix fun Int.flag(flag: UInt): Boolean = this.toUInt() and flag != 0u +public inline infix fun ULong.flag(flag: ULong): Boolean = this and flag != 0uL + +public val NULL_PTR: COpaquePointerVar = nativeHeap.alloc() +public inline fun nullPtr(): T = NULL_PTR.reinterpret() \ No newline at end of file diff --git a/mirai-core-utils/src/nativeMain/kotlin/Service.kt b/mirai-core-utils/src/nativeMain/kotlin/Service.kt new file mode 100644 index 000000000..9530e4144 --- /dev/null +++ b/mirai-core-utils/src/nativeMain/kotlin/Service.kt @@ -0,0 +1,66 @@ +/* + * Copyright 2019-2022 Mamoe Technologies and contributors. + * + * 此源代码的使用受 GNU AFFERO GENERAL PUBLIC LICENSE version 3 许可证的约束, 可以在以下链接找到该许可证. + * Use of this source code is governed by the GNU AGPLv3 license that can be found through the following link. + * + * https://github.com/mamoe/mirai/blob/dev/LICENSE + */ + +@file:Suppress("RedundantVisibilityModifier") + +package net.mamoe.mirai.utils + +import kotlinx.atomicfu.locks.reentrantLock +import kotlinx.atomicfu.locks.withLock +import kotlin.reflect.KClass + +public object Services { + private val lock = reentrantLock() + + private class Implementation( + val implementationClass: String, + val instance: Lazy + ) + + private val registered: MutableMap> = mutableMapOf() + + public fun register(baseClass: String, implementationClass: String, implementation: () -> Any) { + lock.withLock { + registered.getOrPut(baseClass, ::mutableListOf) + .add(Implementation(implementationClass, lazy(implementation))) + } + } + + public fun firstImplementationOrNull(baseClass: String): Any? { + lock.withLock { + return registered[baseClass]?.firstOrNull()?.instance?.value + } + } + + public fun implementations(baseClass: String): List? { + lock.withLock { + return registered[baseClass]?.map { it.instance } + } + + } +} + +@Suppress("UNCHECKED_CAST") +public actual fun loadServiceOrNull( + clazz: KClass, + fallbackImplementation: String? +): T? = + Services.firstImplementationOrNull(qualifiedNameOrFail(clazz)) as T? + +public actual fun loadService( + clazz: KClass, + fallbackImplementation: String? +): T = loadServiceOrNull(clazz, fallbackImplementation) + ?: error("Could not load service '${clazz.qualifiedName ?: clazz}'") + +public actual fun loadServices(clazz: KClass): Sequence = + Services.implementations(qualifiedNameOrFail(clazz))?.asSequence().orEmpty().castUp() + +private fun qualifiedNameOrFail(clazz: KClass) = + clazz.qualifiedName ?: error("Could not find qualifiedName for $clazz") \ No newline at end of file diff --git a/mirai-core-utils/src/nativeMain/kotlin/StandardUtils.kt b/mirai-core-utils/src/nativeMain/kotlin/StandardUtils.kt new file mode 100644 index 000000000..a8ab13b3d --- /dev/null +++ b/mirai-core-utils/src/nativeMain/kotlin/StandardUtils.kt @@ -0,0 +1,17 @@ +/* + * Copyright 2019-2022 Mamoe Technologies and contributors. + * + * 此源代码的使用受 GNU AFFERO GENERAL PUBLIC LICENSE version 3 许可证的约束, 可以在以下链接找到该许可证. + * Use of this source code is governed by the GNU AGPLv3 license that can be found through the following link. + * + * https://github.com/mamoe/mirai/blob/dev/LICENSE + */ + +package net.mamoe.mirai.utils + + +public actual fun localIpAddress(): String = "192.168.1.123" + +internal actual fun isSameClassPlatform(object1: Any, object2: Any): Boolean { + return object1::class == object2::class +} \ No newline at end of file diff --git a/mirai-core-utils/src/nativeMain/kotlin/TimeUtils.kt b/mirai-core-utils/src/nativeMain/kotlin/TimeUtils.kt new file mode 100644 index 000000000..f9c06e238 --- /dev/null +++ b/mirai-core-utils/src/nativeMain/kotlin/TimeUtils.kt @@ -0,0 +1,42 @@ +/* + * Copyright 2019-2022 Mamoe Technologies and contributors. + * + * 此源代码的使用受 GNU AFFERO GENERAL PUBLIC LICENSE version 3 许可证的约束, 可以在以下链接找到该许可证. + * Use of this source code is governed by the GNU AGPLv3 license that can be found through the following link. + * + * https://github.com/mamoe/mirai/blob/dev/LICENSE + */ + +@file:Suppress("RedundantVisibilityModifier") + +package net.mamoe.mirai.utils + +import kotlinx.cinterop.* +import platform.posix.* + +/** + * 时间戳 + */ +public actual fun currentTimeMillis(): Long { + // Do not use getTimeMillis from stdlib, it doesn't support iosSimulatorArm64 + memScoped { + val timeT = alloc() + time(timeT.ptr) + return timeT.value.toLongUnsigned() + } +} + +public actual fun currentTimeFormatted(format: String?): String { + memScoped { + val timeT = alloc() + time(timeT.ptr) + val tm = localtime(timeT.ptr) + try { + val bb = allocArray(40) + strftime(bb, 40, "%Y-%M-%d %H:%M:%S", tm); + return bb.toKString() + } finally { + free(tm) + } + } +} \ No newline at end of file diff --git a/mirai-core-utils/src/nativeMain/kotlin/cipher.rs b/mirai-core-utils/src/nativeMain/kotlin/cipher.rs new file mode 100644 index 000000000..e69de29bb diff --git a/mirai-core-utils/src/nativeMain/kotlin/getProperty.kt b/mirai-core-utils/src/nativeMain/kotlin/getProperty.kt index 4c8d94959..43a7583b1 100644 --- a/mirai-core-utils/src/nativeMain/kotlin/getProperty.kt +++ b/mirai-core-utils/src/nativeMain/kotlin/getProperty.kt @@ -11,4 +11,8 @@ package net.mamoe.mirai.utils internal actual fun getProperty(name: String, default: String): String? { TODO("Not yet implemented") +} + +internal actual fun setProperty(name: String, value: String) { + TODO("Not yet implemented") } \ No newline at end of file diff --git a/mirai-core-utils/src/nativeMainInterop/.gitignore b/mirai-core-utils/src/nativeMainInterop/.gitignore new file mode 100644 index 000000000..683119ebf --- /dev/null +++ b/mirai-core-utils/src/nativeMainInterop/.gitignore @@ -0,0 +1,8 @@ +/target +Cargo.lock +myrust.h + +*.iml + +src/bindings.rs +/*.h \ No newline at end of file diff --git a/mirai-core-utils/src/nativeMainInterop/Cargo.toml b/mirai-core-utils/src/nativeMainInterop/Cargo.toml new file mode 100644 index 000000000..4e35d2e0a --- /dev/null +++ b/mirai-core-utils/src/nativeMainInterop/Cargo.toml @@ -0,0 +1,19 @@ +[package] +name = "mirai_core_utils_i" +version = "0.1.0" + +[dependencies] +md5 = "0.7.0" +sha1 = "0.10.1" +flate2 = "1.0.23" +libc = "0.2.126" +#chashmap = "2.2.2" + +[lib] +name = "mirai_core_utils_i" +crate-type = ["cdylib"] # Creates dynamic lib +# crate-type = ["staticlib"] # Creates static lib + +[build-dependencies] +bindgen = "0.53.1" +cbindgen = "0.20.0" diff --git a/mirai-core-utils/src/nativeMainInterop/build.rs b/mirai-core-utils/src/nativeMainInterop/build.rs new file mode 100644 index 000000000..669efe88f --- /dev/null +++ b/mirai-core-utils/src/nativeMainInterop/build.rs @@ -0,0 +1,32 @@ +/* + * Copyright 2019-2022 Mamoe Technologies and contributors. + * + * 此源代码的使用受 GNU AFFERO GENERAL PUBLIC LICENSE version 3 许可证的约束, 可以在以下链接找到该许可证. + * Use of this source code is governed by the GNU AGPLv3 license that can be found through the following link. + * + * https://github.com/mamoe/mirai/blob/dev/LICENSE + */ + +extern crate bindgen; +extern crate cbindgen; + +use std::env; +use std::path::PathBuf; + +use cbindgen::Config; +use cbindgen::Language::C; + +fn main() { + // let crate_dir = env::var("CARGO_MANIFEST_DIR").unwrap(); + + // cbindgen::Builder::new() + // .with_crate(crate_dir) + // .with_language(C) + // .generate() + // .expect("Unable to generate bindings") + // .write_to_file("nativeInterop.h"); + + + println!("cargo:rustc-link-search=../../build/bin/native/debugShared"); + println!("cargo:rustc-link-lib=mirai_core_utils"); +} \ No newline at end of file diff --git a/mirai-core-utils/src/nativeMainInterop/cbindgen.toml b/mirai-core-utils/src/nativeMainInterop/cbindgen.toml new file mode 100644 index 000000000..4aa4b4903 --- /dev/null +++ b/mirai-core-utils/src/nativeMainInterop/cbindgen.toml @@ -0,0 +1,10 @@ +# This is a template cbindgen.toml file with all of the default values. +# Some values are commented out because their absence is the real default. +# +# See https://github.com/eqrion/cbindgen/blob/master/docs.md#cbindgentoml +# for detailed documentation of every option here. + + + +language = "C" + diff --git a/mirai-core-utils/src/nativeMainInterop/interop.def b/mirai-core-utils/src/nativeMainInterop/interop.def new file mode 100644 index 000000000..e69de29bb diff --git a/mirai-core-utils/src/nativeMainInterop/src/chmap.rs b/mirai-core-utils/src/nativeMainInterop/src/chmap.rs new file mode 100644 index 000000000..8293ac703 --- /dev/null +++ b/mirai-core-utils/src/nativeMainInterop/src/chmap.rs @@ -0,0 +1,27 @@ +/* + * Copyright 2019-2022 Mamoe Technologies and contributors. + * + * 此源代码的使用受 GNU AFFERO GENERAL PUBLIC LICENSE version 3 许可证的约束, 可以在以下链接找到该许可证. + * Use of this source code is governed by the GNU AGPLv3 license that can be found through the following link. + * + * https://github.com/mamoe/mirai/blob/dev/LICENSE + */ + +// use std::ops::DerefMut; +// use std::ptr::{null, null_mut}; +// +// use chashmap::CHashMap; +// use libc::c_void; +// // +// #[no_mangle] +// pub extern "C" fn mirai_chmap_create() -> *mut c_void { +// let map = CHashMap::<*mut c_void, *mut c_void>::new(); +// // Box::into_raw(Box::new(map)) +// return Box::into_raw(Box::new(map)) as *mut c_void; +// } +// +// #[no_mangle] +// pub unsafe extern "C" fn mirai_chmap_put(map: *const c_void, key: *const c_void, value: *const c_void) -> *const c_void { +// let chmap = Box::from_raw(map as *mut CHashMap::<*const c_void, *const c_void>); +// return chmap.insert(key, value).unwrap_or(null()); +// } diff --git a/mirai-core-utils/src/nativeMainInterop/src/crypto.rs b/mirai-core-utils/src/nativeMainInterop/src/crypto.rs new file mode 100644 index 000000000..6efd52b6e --- /dev/null +++ b/mirai-core-utils/src/nativeMainInterop/src/crypto.rs @@ -0,0 +1,125 @@ +/* + * Copyright 2019-2022 Mamoe Technologies and contributors. + * + * 此源代码的使用受 GNU AFFERO GENERAL PUBLIC LICENSE version 3 许可证的约束, 可以在以下链接找到该许可证. + * Use of this source code is governed by the GNU AGPLv3 license that can be found through the following link. + * + * https://github.com/mamoe/mirai/blob/dev/LICENSE + */ + +use std::io::{BufReader, Read, Write}; + +use flate2::Compression; +use flate2::write::{DeflateDecoder, DeflateEncoder, GzDecoder, GzEncoder, ZlibDecoder, ZlibEncoder}; +use libc::{malloc, read, size_t}; +use sha1::{Digest, Sha1}; +use sha1::digest::{Output, OutputSizeUser}; +use sha1::digest::generic_array::GenericArray; + +#[no_mangle] +#[repr(C)] +pub struct SizedByteArray { + arr: *mut u8, + size: u32, +} + +#[no_mangle] +pub unsafe extern "C" fn mirai_crypto_md5(data: *const u8, len: u32, ret: &mut SizedByteArray) -> bool { + let data = unsafe { std::slice::from_raw_parts(data, len as usize) }; + let result = md5::compute(data); + let size = 16; + let mut memory = malloc(size).cast(); + memory.copy_from(result.as_ptr(), size); + + ret.arr = memory; + ret.size = size as u32; + return true; +} + +#[no_mangle] +pub unsafe extern "C" fn mirai_crypto_sha1(data: *const u8, len: u32, ret: &mut SizedByteArray) -> bool { + let data = unsafe { std::slice::from_raw_parts(data, len as usize) }; + let mut hasher = Sha1::new(); + hasher.update(data); + let result = hasher.finalize(); + let size = 16; + let mut memory = malloc(size).cast(); + memory.copy_from(result.as_ptr(), size); + + ret.arr = memory; + ret.size = size as u32; + return true; +} + + +#[no_mangle] +pub unsafe extern "C" fn mirai_crypto_gzip(data: *const u8, len: u32, ret: &mut SizedByteArray) -> bool { + let data = unsafe { std::slice::from_raw_parts(data, len as usize) }; + let mut encoder = GzEncoder::new(Vec::new(), Compression::default()); + + let result = encoder.write_all(data).and_then(|_| { encoder.finish() }); + if result.is_err() { return false; } + let result = result.unwrap(); + + let size = result.len(); + let mut memory = malloc(size).cast(); + memory.copy_from(result.as_ptr(), size); + + ret.arr = memory; + ret.size = size as u32; + return true; +} + +#[no_mangle] +pub unsafe extern "C" fn mirai_crypto_ungzip(data: *const u8, len: u32, ret: &mut SizedByteArray) -> bool { + let data = unsafe { std::slice::from_raw_parts(data, len as usize) }; + let mut encoder = GzDecoder::new(Vec::new()); + + let result = encoder.write_all(data).and_then(|_| { encoder.finish() }); + if result.is_err() { return false; } + let result = result.unwrap(); + + let size = result.len(); + let mut memory = malloc(size).cast(); + memory.copy_from(result.as_ptr(), size); + + ret.arr = memory; + ret.size = size as u32; + return true; +} + +#[no_mangle] +pub unsafe extern "C" fn mirai_crypto_deflate(data: *const u8, len: u32, ret: &mut SizedByteArray) -> bool { + let data = unsafe { std::slice::from_raw_parts(data, len as usize) }; + let mut encoder = ZlibEncoder::new(Vec::new(), Compression::default()); + + let result = encoder.write_all(data).and_then(|_| { encoder.finish() }); + if result.is_err() { return false; } + let result = result.unwrap(); + + let size = result.len(); + let mut memory = malloc(size).cast(); + memory.copy_from(result.as_ptr(), size); + + ret.arr = memory; + ret.size = size as u32; + return true; +} + +#[no_mangle] +pub unsafe extern "C" fn mirai_crypto_inflate(data: *const u8, len: u32, ret: &mut SizedByteArray) -> bool { + let data = unsafe { std::slice::from_raw_parts(data, len as usize) }; + let mut encoder = ZlibDecoder::new(Vec::new()); + + let result = encoder.write_all(data).and_then(|_| { encoder.finish() }); + if result.is_err() { return false; } + let result = result.unwrap(); + + let size = result.len(); + let mut memory = malloc(size).cast(); + memory.copy_from(result.as_ptr(), size); + + ret.arr = memory; + ret.size = size as u32; + return true; +} diff --git a/mirai-core-utils/src/nativeMainInterop/src/lib.rs b/mirai-core-utils/src/nativeMainInterop/src/lib.rs new file mode 100644 index 000000000..592b7953c --- /dev/null +++ b/mirai-core-utils/src/nativeMainInterop/src/lib.rs @@ -0,0 +1,20 @@ +/* + * Copyright 2019-2022 Mamoe Technologies and contributors. + * + * 此源代码的使用受 GNU AFFERO GENERAL PUBLIC LICENSE version 3 许可证的约束, 可以在以下链接找到该许可证. + * Use of this source code is governed by the GNU AGPLv3 license that can be found through the following link. + * + * https://github.com/mamoe/mirai/blob/dev/LICENSE + */ + +// extern crate chashmap; +extern crate core; +extern crate flate2; +extern crate libc; +extern crate sha1; + +/// cbindgen:ignore +mod bindings; +mod crypto; +mod chmap; + diff --git a/mirai-core-utils/src/nativeTest/kotlin/AbstractNativeMiraiFileImplTest.kt b/mirai-core-utils/src/nativeTest/kotlin/AbstractNativeMiraiFileImplTest.kt new file mode 100644 index 000000000..88515a7e2 --- /dev/null +++ b/mirai-core-utils/src/nativeTest/kotlin/AbstractNativeMiraiFileImplTest.kt @@ -0,0 +1,146 @@ +/* + * Copyright 2019-2022 Mamoe Technologies and contributors. + * + * 此源代码的使用受 GNU AFFERO GENERAL PUBLIC LICENSE version 3 许可证的约束, 可以在以下链接找到该许可证. + * Use of this source code is governed by the GNU AGPLv3 license that can be found through the following link. + * + * https://github.com/mamoe/mirai/blob/dev/LICENSE + */ + +package net.mamoe.mirai.utils + +import io.ktor.utils.io.errors.* +import kotlin.test.* + +internal abstract class AbstractNativeMiraiFileImplTest { + protected abstract val baseTempDir: MiraiFile // MiraiFile.create("/Users/Shared/mirai_test") + protected abstract val tempPath: String + protected val tempDir by lazy { + MiraiFile.create(tempPath).apply { + assertTrue("Failed to make temp directory: ${this.absolutePath}") { mkdirs() } + } + } + + @AfterTest + fun afterTest() { + println("Cleaning up...") + baseTempDir.deleteRecursively() + } + + @BeforeTest + fun init() { + println("Test start") + assertTrue { tempDir.exists() } + } + + @Test + fun `canonical paths for canonical input`() { + assertEquals(tempPath, tempDir.path) + assertEquals(tempPath, tempDir.absolutePath) + } + + @Test + protected open fun parent() { + assertEquals(tempDir, tempDir.resolve("s").parent) + assertEquals(tempDir.parent, tempDir.resolve("..")) + } + + @Test + fun `canonical paths for non-canonical input`() { + // extra / + MiraiFile.create("$tempPath/").resolve("test").let { + assertEquals("${tempPath}/test", it.path) + assertEquals("${tempPath}/test", it.absolutePath) + } + // extra // + MiraiFile.create("$tempPath//").resolve("test").let { + assertEquals("${tempPath}/test", it.path) + assertEquals("${tempPath}/test", it.absolutePath) + } + // extra /. + MiraiFile.create("$tempPath/.").resolve("test").let { + assertEquals("${tempPath}/test", it.path) + assertEquals("${tempPath}/test", it.absolutePath) + } + // extra /./. + MiraiFile.create("$tempPath/./.").resolve("test").let { + assertEquals("${tempPath}/test", it.path) + assertEquals("${tempPath}/test", it.absolutePath) + } + // extra /sss/.. + MiraiFile.create("$tempPath/sss/..").resolve("test").let { + assertEquals("${tempPath}/sss/../test", it.path) // because file is not found + assertEquals("${tempPath}/sss/../test", it.absolutePath) + } + } + + @Test + abstract fun `resolve absolute`() + + @Test + fun `exits createNewFile mkdir length`() { + assertTrue { tempDir.exists() } + + assertFalse { tempDir.resolve("not_existing_file.txt").exists() } + assertEquals(0L, tempDir.resolve("not_existing_file.txt").length) + assertTrue { tempDir.resolve("not_existing_file.txt").createNewFile() } + assertEquals(0L, tempDir.resolve("not_existing_file.txt").length) + assertTrue { tempDir.resolve("not_existing_file.txt").exists() } + + assertFalse { tempDir.resolve("not_existing_dir").exists() } + assertEquals(0L, tempDir.resolve("not_existing_dir").length) + assertTrue { tempDir.resolve("not_existing_dir").mkdir() } + assertNotEquals(0L, tempDir.resolve("not_existing_dir").length) + assertTrue { tempDir.resolve("not_existing_dir").exists() } + } + + @Test + fun `isFile isDirectory`() { + assertTrue { tempDir.exists() } + + assertFalse { tempDir.resolve("not_existing_file.txt").exists() } + assertEquals(false, tempDir.resolve("not_existing_file.txt").isFile) + assertEquals(false, tempDir.resolve("not_existing_file.txt").isDirectory) + assertTrue { tempDir.resolve("not_existing_file.txt").createNewFile() } + assertEquals(true, tempDir.resolve("not_existing_file.txt").isFile) + assertEquals(false, tempDir.resolve("not_existing_file.txt").isDirectory) + assertTrue { tempDir.resolve("not_existing_file.txt").exists() } + + assertFalse { tempDir.resolve("not_existing_dir").exists() } + assertEquals(false, tempDir.resolve("not_existing_dir").isFile) + assertEquals(false, tempDir.resolve("not_existing_dir").isDirectory) + assertTrue { tempDir.resolve("not_existing_dir").mkdir() } + assertEquals(false, tempDir.resolve("not_existing_dir").isFile) + assertEquals(true, tempDir.resolve("not_existing_dir").isDirectory) + assertTrue { tempDir.resolve("not_existing_dir").exists() } + } + + @Test + fun writeText() { + // new file + tempDir.resolve("writeText1.txt").let { file -> + val text = "some text" + file.writeText(text) + assertEquals(text.length, file.length.toInt()) + } + + // override + tempDir.resolve("writeText1.txt").let { file -> + val text = "some other text" + file.writeText(text) + assertEquals(text.length, file.length.toInt()) + } + } + + @Test + fun readText() { + tempDir.resolve("readText1.txt").let { file -> + assertTrue { !file.exists() } + assertFailsWith { file.readText() } + + val text = "some text" + file.writeText(text) + assertEquals(text, file.readText()) + } + } +} \ No newline at end of file diff --git a/mirai-core-utils/src/nativeTest/kotlin/ByteArrayOpTest.kt b/mirai-core-utils/src/nativeTest/kotlin/ByteArrayOpTest.kt new file mode 100644 index 000000000..2b9a85d42 --- /dev/null +++ b/mirai-core-utils/src/nativeTest/kotlin/ByteArrayOpTest.kt @@ -0,0 +1,110 @@ +/* + * Copyright 2019-2022 Mamoe Technologies and contributors. + * + * 此源代码的使用受 GNU AFFERO GENERAL PUBLIC LICENSE version 3 许可证的约束, 可以在以下链接找到该许可证. + * Use of this source code is governed by the GNU AGPLv3 license that can be found through the following link. + * + * https://github.com/mamoe/mirai/blob/dev/LICENSE + */ + +package net.mamoe.mirai.utils + +import io.ktor.utils.io.core.* +import kotlin.random.Random +import kotlin.test.Test +import kotlin.test.assertContentEquals +import kotlin.test.assertEquals +import kotlin.test.assertTrue + +class ByteArrayOpTest { + + @Test + fun testAvailableProcessors() { + val processors = availableProcessors() + assertTrue(processors.toString()) { processors > 0 } + } + + @Test + fun testMd5() { + val str = getRandomString(10, Random(1)) + println(str) + val hash = str.md5() + assertContentEquals( + "30 3B 36 B3 42 00 39 E2 EC 18 22 79 10 32 05 48".hexToBytes(), + hash, + message = hash.toUHexString() + ) + } + + @Test + fun testMd5WithOffset() { + val str = getRandomString(10, Random(1)) + println(str) + val hash = (byteArrayOf(1) + str.toByteArray()).md5(1) + assertContentEquals( + "30 3B 36 B3 42 00 39 E2 EC 18 22 79 10 32 05 48".hexToBytes(), + hash, + message = hash.toUHexString() + ) + } + + @Test + fun testSha1() { + val str = getRandomString(10, Random(1)) + println(str) + val hash = str.sha1() + assertContentEquals( + "54 98 CD 62 6C DE E3 9B 96 D4 34 5E 13 51 48 BB FC 32 1C 48".hexToBytes(), + hash, + message = hash.toUHexString() + ) + } + + @Test + fun testDeflate() { + val str = "qGnJ1RrFC9" + println(str) + val hash = str.toByteArray().deflate() + assertContentEquals( + "78 9C 2B 74 CF F3 32 0C 2A 72 73 B6 04 00 12 82 03 28".hexToBytes(), + hash, + message = hash.toUHexString() + ) + } + + @Test + fun testInflate() { + val result = + "78 9C 2B 74 CF F3 32 0C 2A 72 73 B6 04 00 12 82 03 28".hexToBytes() + .inflate().decodeToString() + assertEquals( + "qGnJ1RrFC9", + result, + message = result + ) + } + + @Test + fun testGzip() { + val str = "qGnJ1RrFC9" + println(str) + val hash = str.toByteArray().gzip() + assertContentEquals( + "1F 8B 08 00 00 00 00 00 00 13 2B 74 CF F3 32 0C 2A 72 73 B6 04 00 A8 35 6D D9 0A 00 00 00".hexToBytes(), + hash, + message = hash.toUHexString() + ) + } + + @Test + fun testUnGzip() { + val result = + "1F 8B 08 00 00 00 00 00 00 FF 2B 74 CF F3 32 0C 2A 72 73 B6 04 00 A8 35 6D D9 0A 00 00 00".hexToBytes() + .ungzip().decodeToString() + assertEquals( + "qGnJ1RrFC9", + result, + message = result + ) + } +} \ No newline at end of file diff --git a/mirai-core-utils/src/nativeTest/kotlin/CollectionsTest.kt b/mirai-core-utils/src/nativeTest/kotlin/CollectionsTest.kt new file mode 100644 index 000000000..f31e36291 --- /dev/null +++ b/mirai-core-utils/src/nativeTest/kotlin/CollectionsTest.kt @@ -0,0 +1,41 @@ +/* + * Copyright 2019-2022 Mamoe Technologies and contributors. + * + * 此源代码的使用受 GNU AFFERO GENERAL PUBLIC LICENSE version 3 许可证的约束, 可以在以下链接找到该许可证. + * Use of this source code is governed by the GNU AGPLv3 license that can be found through the following link. + * + * https://github.com/mamoe/mirai/blob/dev/LICENSE + */ + +package net.mamoe.mirai.utils + +import kotlin.test.Test +import kotlin.test.assertEquals +import kotlin.test.assertFails +import kotlin.test.assertFalse + +internal sealed class MapTest( + private val map: MutableMap +) { + class ConcurrentMapTest : MapTest(ConcurrentHashMap()) + + @Test + fun `initial state test`() { + assertEquals(0, map.size) + assertEquals(null, map[1]) + assertFalse(map.iterator().hasNext()) + assertFails { map.iterator().next() } + } + + @Test + fun `get set size`() { + assertEquals(0, map.size) + assertEquals(null, map[1]) + map[1] = 2 + assertEquals(2, map[1]) + assertEquals(1, map.size) + map[2] = 2 + assertEquals(2, map[2]) + assertEquals(2, map.size) + } +} \ No newline at end of file diff --git a/mirai-core-utils/src/nativeTest/kotlin/TimeUtilsTest.kt b/mirai-core-utils/src/nativeTest/kotlin/TimeUtilsTest.kt new file mode 100644 index 000000000..df528605f --- /dev/null +++ b/mirai-core-utils/src/nativeTest/kotlin/TimeUtilsTest.kt @@ -0,0 +1,28 @@ +/* + * Copyright 2019-2022 Mamoe Technologies and contributors. + * + * 此源代码的使用受 GNU AFFERO GENERAL PUBLIC LICENSE version 3 许可证的约束, 可以在以下链接找到该许可证. + * Use of this source code is governed by the GNU AGPLv3 license that can be found through the following link. + * + * https://github.com/mamoe/mirai/blob/dev/LICENSE + */ + +package net.mamoe.mirai.utils + +import kotlin.test.Test +import kotlin.test.assertTrue + +internal class TimeUtilsTest { + + @Test + fun `can get currentTimeMillis`() { + val time = currentTimeMillis() + assertTrue(time.toString()) { time > 1642549113 } + } + + @Test + fun `can get currentTimeFormatted`() { + // 2022-28-26 18:28:28 + assertTrue { currentTimeFormatted().matches(Regex("""\d{4}-\d{2}-\d{2} \d{2}:\d{2}:\d{2}""")) } + } +} \ No newline at end of file diff --git a/mirai-core-utils/src/jvmBaseMain/kotlin/Closeable.kt b/mirai-core-utils/src/nativeTest/kotlin/package.kt similarity index 81% rename from mirai-core-utils/src/jvmBaseMain/kotlin/Closeable.kt rename to mirai-core-utils/src/nativeTest/kotlin/package.kt index 71812b77c..f699501a9 100644 --- a/mirai-core-utils/src/jvmBaseMain/kotlin/Closeable.kt +++ b/mirai-core-utils/src/nativeTest/kotlin/package.kt @@ -7,6 +7,4 @@ * https://github.com/mamoe/mirai/blob/dev/LICENSE */ -package net.mamoe.mirai.utils - -public actual typealias Closeable = java.io.Closeable \ No newline at end of file +package net.mamoe.mirai.utils \ No newline at end of file diff --git a/mirai-core-utils/src/unixMain/kotlin/MiraiFileImpl.kt b/mirai-core-utils/src/unixMain/kotlin/MiraiFileImpl.kt new file mode 100644 index 000000000..4e7c0e659 --- /dev/null +++ b/mirai-core-utils/src/unixMain/kotlin/MiraiFileImpl.kt @@ -0,0 +1,191 @@ +/* + * Copyright 2019-2022 Mamoe Technologies and contributors. + * + * 此源代码的使用受 GNU AFFERO GENERAL PUBLIC LICENSE version 3 许可证的约束, 可以在以下链接找到该许可证. + * Use of this source code is governed by the GNU AGPLv3 license that can be found through the following link. + * + * https://github.com/mamoe/mirai/blob/dev/LICENSE + */ + +package net.mamoe.mirai.utils + +import io.ktor.utils.io.core.* +import io.ktor.utils.io.errors.* +import kotlinx.cinterop.* +import platform.posix.* + +@OptIn(ExperimentalIoApi::class) +private fun readlink(path: String): String = memScoped { + val len = realpath(path, null) + if (len != null) { + try { + return len.toKString() + } finally { + free(len) + } + } else { + when (val errno = errno) { + ENOTDIR -> return@memScoped path + EACCES -> return@memScoped path // permission denied + ENOENT -> return@memScoped path // no such file + else -> throw IllegalArgumentException( + "Invalid path($errno): $path", + cause = PosixException.forErrno(posixFunctionName = "realpath()") + ) + } + } +} + +internal actual class MiraiFileImpl actual constructor( + override val path: String, +) : MiraiFile { + actual companion object { + private const val SEPARATOR = '/' + private val ROOT by lazy { MiraiFileImpl("/") } + + @Suppress("UnnecessaryOptInAnnotation") + @OptIn(UnsafeNumber::class) + actual fun getWorkingDir(): MiraiFile { + val path = memScoped { + ByteArray(PATH_MAX).usePinned { + getcwd(it.addressOf(0), it.get().size.convert()) + it.get().toKString() + } + } + return MiraiFile.create(path) + } + } + + override val absolutePath: String by lazy { kotlin.run { readlink(path) } } + + override val parent: MiraiFile? by lazy { + val absolutePath = absolutePath + val p = absolutePath.substringBeforeLast(SEPARATOR, "") + if (p.isEmpty()) { + if (absolutePath.singleOrNull() == SEPARATOR) return@lazy null // root + else return@lazy ROOT + } + MiraiFileImpl(p) + } + + override val name: String + get() = absolutePath.substringAfterLast('/', "").ifEmpty { absolutePath } + + init { + absolutePath.split('/').forEach { checkName(it) } + } + + private fun checkName(name: String) { + name.forEach { c -> + if (c in """\/:?*"><|""") { + throw IllegalArgumentException("'${name}' contains illegal character '$c'.") + } + } + } + + override val length: Long + get() = useStat { it.st_size.convert() } ?: 0 + + @OptIn(UnsafeNumber::class) + override val isFile: Boolean + get() = useStat { it.st_mode.convert() flag S_IFREG } ?: false + + @OptIn(UnsafeNumber::class) + override val isDirectory: Boolean + get() = useStat { it.st_mode.convert() flag S_IFDIR } ?: false + + override fun exists(): Boolean = useStat { true } ?: false + + override fun resolve(path: String): MiraiFile { + when (path) { + "." -> return this + ".." -> return parent ?: this // root + } + + if (path.startsWith(SEPARATOR)) { + return MiraiFileImpl(path) + } + + return MiraiFileImpl("$absolutePath/$path") + } + + override fun resolve(file: MiraiFile): MiraiFile { + val parent = file.parent ?: return resolve(file.name) + return resolve(parent).resolve(file.name) + } + + @OptIn(UnsafeNumber::class) + override fun createNewFile(): Boolean { + memScoped { + val fp = fopen(absolutePath, "w") + fwrite(fp, 0, 0, fp) + fclose(fp) + return true + } + } + + override fun delete(): Boolean { + return if (isFile) { + remove(absolutePath) == 0 + } else { + rmdir(absolutePath) == 0 + } + } + + override fun mkdir(): Boolean { + @Suppress("UnnecessaryOptInAnnotation") // bug + @OptIn(UnsafeNumber::class) + return (mkdir("$absolutePath/", "755".toUShort(8).convert()).convert() == 0) + } + + @OptIn(UnsafeNumber::class) + override fun mkdirs(): Boolean { + val flags = useStat { it.st_mode.convert() } + return when { + flags == null -> { + this.parent?.mkdirs() + mkdir() + } + flags flag S_IFDIR -> { + false // already exists + } + else -> { + mkdir() + } + } + } + + @OptIn(ExperimentalIoApi::class) + override fun input(): Input { + val handle = fopen(absolutePath, "r") + ?: throw IOException( + "Failed to open file '$absolutePath'", + PosixException.forErrno(posixFunctionName = "fopen()") + ) + return PosixInputForFile(handle) + } + + @OptIn(ExperimentalIoApi::class) + override fun output(): Output { + val handle = fopen(absolutePath, "w") + ?: throw IOException( + "Failed to open file '$absolutePath'", + PosixException.forErrno(posixFunctionName = "fopen()") + ) + return PosixFileInstanceOutput(handle) + } + + override fun hashCode(): Int { + return this.path.hashCode() + } + + override fun equals(other: Any?): Boolean { + if (other == null) return false + if (!isSameType(this, other)) return false + return this.path == other.path + } + + override fun toString(): String { + return "MiraiFileImpl($path)" + } +} \ No newline at end of file diff --git a/mirai-core-utils/src/unixMain/kotlin/StandardUtils.kt b/mirai-core-utils/src/unixMain/kotlin/StandardUtils.kt new file mode 100644 index 000000000..56bf10284 --- /dev/null +++ b/mirai-core-utils/src/unixMain/kotlin/StandardUtils.kt @@ -0,0 +1,18 @@ +/* + * Copyright 2019-2022 Mamoe Technologies and contributors. + * + * 此源代码的使用受 GNU AFFERO GENERAL PUBLIC LICENSE version 3 许可证的约束, 可以在以下链接找到该许可证. + * Use of this source code is governed by the GNU AGPLv3 license that can be found through the following link. + * + * https://github.com/mamoe/mirai/blob/dev/LICENSE + */ + +package net.mamoe.mirai.utils + +import kotlinx.cinterop.UnsafeNumber +import kotlinx.cinterop.convert +import platform.posix._SC_NPROCESSORS_ONLN +import platform.posix.sysconf + +@OptIn(UnsafeNumber::class) +public actual fun availableProcessors(): Int = sysconf(_SC_NPROCESSORS_ONLN).convert() diff --git a/mirai-core-utils/src/unixTest/kotlin/UnixMiraiFileImplTest.kt b/mirai-core-utils/src/unixTest/kotlin/UnixMiraiFileImplTest.kt new file mode 100644 index 000000000..91d7751c8 --- /dev/null +++ b/mirai-core-utils/src/unixTest/kotlin/UnixMiraiFileImplTest.kt @@ -0,0 +1,38 @@ +/* + * Copyright 2019-2022 Mamoe Technologies and contributors. + * + * 此源代码的使用受 GNU AFFERO GENERAL PUBLIC LICENSE version 3 许可证的约束, 可以在以下链接找到该许可证. + * Use of this source code is governed by the GNU AGPLv3 license that can be found through the following link. + * + * https://github.com/mamoe/mirai/blob/dev/LICENSE + */ + +package net.mamoe.mirai.utils + +import kotlin.math.absoluteValue +import kotlin.random.Random +import kotlin.test.Test +import kotlin.test.assertEquals + +internal class UnixMiraiFileImplTest : AbstractNativeMiraiFileImplTest() { + private val rand = Random.nextInt().absoluteValue + override val baseTempDir: MiraiFile by lazy { MiraiFile.create(MiraiFile.getWorkingDir().absolutePath + "/mirai_unit_tests") } + override val tempPath by lazy { "${baseTempDir.absolutePath}/temp$rand" } + + @Test + override fun parent() { + assertEquals(baseTempDir.absolutePath, tempDir.parent!!.absolutePath) + assertEquals(null, MiraiFile.create("/").parent) + assertEquals("/", MiraiFile.create("/dev").parent?.path) + assertEquals("/", MiraiFile.create("/dev").parent?.absolutePath) + super.parent() + } + + @Test + override fun `resolve absolute`() { + MiraiFile.create("$tempPath/").resolve("/Users").let { + assertEquals("/Users", it.path) + assertEquals("/Users", it.absolutePath) + } + } +} \ No newline at end of file diff --git a/mirai-core/build.gradle.kts b/mirai-core/build.gradle.kts index 20b9470b2..08cfc5a49 100644 --- a/mirai-core/build.gradle.kts +++ b/mirai-core/build.gradle.kts @@ -25,7 +25,7 @@ description = "Mirai Protocol implementation for QQ Android" kotlin { explicitApi() - configureHMPPJvm() + configureHMPP() sourceSets.apply { @@ -40,6 +40,7 @@ kotlin { implementation(`kotlinx-serialization-protobuf`) implementation(`kotlinx-atomicfu`) implementation(`ktor-io`) + implementation(`ktor-client-core`) } } @@ -58,6 +59,12 @@ kotlin { } } + val jvmBaseTest by getting { + dependencies { + implementation(`kotlinx-coroutines-debug`) + } + } + if (isAndroidSDKAvailable) { val androidMain by getting { dependsOn(commonMain) diff --git a/mirai-core/src/androidMain/kotlin/utils/crypto/ECDHAndroid.kt b/mirai-core/src/androidMain/kotlin/utils/crypto/ECDHAndroid.kt index 3ec9e1fcb..b3558bd5d 100644 --- a/mirai-core/src/androidMain/kotlin/utils/crypto/ECDHAndroid.kt +++ b/mirai-core/src/androidMain/kotlin/utils/crypto/ECDHAndroid.kt @@ -1,10 +1,10 @@ /* - * Copyright 2019-2021 Mamoe Technologies and contributors. + * Copyright 2019-2022 Mamoe Technologies and contributors. * - * 此源代码的使用受 GNU AFFERO GENERAL PUBLIC LICENSE version 3 许可证的约束, 可以在以下链接找到该许可证. - * Use of this source code is governed by the GNU AGPLv3 license that can be found through the following link. + * 此源代码的使用受 GNU AFFERO GENERAL PUBLIC LICENSE version 3 许可证的约束, 可以在以下链接找到该许可证. + * Use of this source code is governed by the GNU AGPLv3 license that can be found through the following link. * - * https://github.com/mamoe/mirai/blob/master/LICENSE + * https://github.com/mamoe/mirai/blob/dev/LICENSE */ package net.mamoe.mirai.internal.utils.crypto @@ -12,27 +12,14 @@ package net.mamoe.mirai.internal.utils.crypto import net.mamoe.mirai.utils.decodeBase64 import net.mamoe.mirai.utils.md5 import net.mamoe.mirai.utils.recoverCatchingSuppressed -import java.security.* +import java.security.KeyFactory +import java.security.KeyPairGenerator +import java.security.Provider +import java.security.Signature import java.security.spec.ECGenParameterSpec import java.security.spec.X509EncodedKeySpec import javax.crypto.KeyAgreement - -@Suppress("ACTUAL_WITHOUT_EXPECT") -internal actual typealias ECDHPrivateKey = PrivateKey -@Suppress("ACTUAL_WITHOUT_EXPECT") -internal actual typealias ECDHPublicKey = PublicKey - -internal actual class ECDHKeyPairImpl( - private val delegate: KeyPair, - initialPublicKey: ECDHPublicKey = defaultInitialPublicKey.key -) : ECDHKeyPair { - override val privateKey: ECDHPrivateKey get() = delegate.private - override val publicKey: ECDHPublicKey get() = delegate.public - override val maskedPublicKey: ByteArray by lazy { publicKey.encoded.copyOfRange(26, 91) } - override val maskedShareKey: ByteArray by lazy { ECDH.calculateShareKey(privateKey, initialPublicKey) } -} - /** * 绕过在Android P之后的版本无法使用EC的限制 * https://cs.android.com/android/platform/superproject/+/master:libcore/ojluni/src/main/java/sun/security/jca/Providers.java;l=371;bpv=1;bpt=1 diff --git a/mirai-core/src/androidTest/kotlin/test/initPlatform.android.kt b/mirai-core/src/androidTest/kotlin/test/initPlatform.android.kt index 5311888ce..331d597ea 100644 --- a/mirai-core/src/androidTest/kotlin/test/initPlatform.android.kt +++ b/mirai-core/src/androidTest/kotlin/test/initPlatform.android.kt @@ -1,18 +1,18 @@ /* - * Copyright 2019-2021 Mamoe Technologies and contributors. + * Copyright 2019-2022 Mamoe Technologies and contributors. * - * 此源代码的使用受 GNU AFFERO GENERAL PUBLIC LICENSE version 3 许可证的约束, 可以在以下链接找到该许可证. - * Use of this source code is governed by the GNU AGPLv3 license that can be found through the following link. + * 此源代码的使用受 GNU AFFERO GENERAL PUBLIC LICENSE version 3 许可证的约束, 可以在以下链接找到该许可证. + * Use of this source code is governed by the GNU AGPLv3 license that can be found through the following link. * - * https://github.com/mamoe/mirai/blob/master/LICENSE + * https://github.com/mamoe/mirai/blob/dev/LICENSE */ package net.mamoe.mirai.internal.test import net.mamoe.mirai.utils.MiraiLogger import org.bouncycastle.jce.provider.BouncyCastleProvider -import org.junit.jupiter.api.Test import java.security.Security +import kotlin.test.Test import kotlin.test.assertTrue internal actual fun initPlatform() { diff --git a/mirai-core/src/commonMain/kotlin/AbstractBot.kt b/mirai-core/src/commonMain/kotlin/AbstractBot.kt index 6c594f9b6..e70d3be1e 100644 --- a/mirai-core/src/commonMain/kotlin/AbstractBot.kt +++ b/mirai-core/src/commonMain/kotlin/AbstractBot.kt @@ -1,10 +1,10 @@ /* - * Copyright 2019-2021 Mamoe Technologies and contributors. + * Copyright 2019-2022 Mamoe Technologies and contributors. * - * 此源代码的使用受 GNU AFFERO GENERAL PUBLIC LICENSE version 3 许可证的约束, 可以在以下链接找到该许可证. - * Use of this source code is governed by the GNU AGPLv3 license that can be found through the following link. + * 此源代码的使用受 GNU AFFERO GENERAL PUBLIC LICENSE version 3 许可证的约束, 可以在以下链接找到该许可证. + * Use of this source code is governed by the GNU AGPLv3 license that can be found through the following link. * - * https://github.com/mamoe/mirai/blob/master/LICENSE + * https://github.com/mamoe/mirai/blob/dev/LICENSE */ @@ -24,13 +24,14 @@ import net.mamoe.mirai.internal.network.component.ComponentStorage import net.mamoe.mirai.internal.network.components.SsoProcessor import net.mamoe.mirai.internal.network.handler.NetworkHandler import net.mamoe.mirai.internal.network.handler.NetworkHandler.State +import net.mamoe.mirai.internal.network.handler.asCoroutineExceptionHandler import net.mamoe.mirai.internal.network.handler.selector.NetworkException -import net.mamoe.mirai.internal.network.impl.netty.asCoroutineExceptionHandler import net.mamoe.mirai.network.LoginFailedException import net.mamoe.mirai.supervisorJob import net.mamoe.mirai.utils.* import kotlin.collections.set import kotlin.coroutines.CoroutineContext +import kotlin.jvm.Volatile /** * Protocol-irrelevant implementations diff --git a/mirai-core/src/commonMain/kotlin/BotAccount.kt b/mirai-core/src/commonMain/kotlin/BotAccount.kt index f218c30ca..611acb7ad 100644 --- a/mirai-core/src/commonMain/kotlin/BotAccount.kt +++ b/mirai-core/src/commonMain/kotlin/BotAccount.kt @@ -1,5 +1,5 @@ /* - * Copyright 2019-2021 Mamoe Technologies and contributors. + * Copyright 2019-2022 Mamoe Technologies and contributors. * * 此源代码的使用受 GNU AFFERO GENERAL PUBLIC LICENSE version 3 许可证的约束, 可以在以下链接找到该许可证. * Use of this source code is governed by the GNU AGPLv3 license that can be found through the following link. @@ -10,60 +10,16 @@ package net.mamoe.mirai.internal -import net.mamoe.mirai.utils.* -import java.nio.ByteBuffer -internal data class BotAccount( - @JvmSynthetic - internal val id: Long, +internal expect class BotAccount { + internal val id: Long + val phoneNumber: String - @JvmSynthetic - @MiraiExperimentalApi - val passwordMd5Buffer: ByteBuffer, // md5 + constructor(id: Long, passwordMd5: ByteArray, phoneNumber: String = "") + constructor(id: Long, passwordPlainText: String, phoneNumber: String = "") - val phoneNumber: String = "" -) { - init { - check(passwordMd5Buffer.remaining == 16) { - "Invalid passwordMd5: size must be 16 but got ${passwordMd5Buffer.remaining}. passwordMd5=${passwordMd5.toUHexString()}" - } - } - - constructor(id: Long, passwordMd5: ByteArray, phoneNumber: String = "") : this( - id, SecretsProtection.escape(passwordMd5), phoneNumber - ) - - constructor(id: Long, passwordPlainText: String, phoneNumber: String = "") : this( - id, - passwordPlainText.md5(), - phoneNumber - ) { - require(passwordPlainText.length <= 16) { "Password length must be at most 16." } - } - - @get:JvmSynthetic - @MiraiExperimentalApi val passwordMd5: ByteArray - get() { - return passwordMd5Buffer.duplicate().readBytes() - } - override fun equals(other: Any?): Boolean { - if (this === other) return true - if (other == null || this::class != other::class) return false - - other as BotAccount - - if (id != other.id) return false - if (passwordMd5Buffer != other.passwordMd5Buffer) return false - - return true - } - - - override fun hashCode(): Int { - var result = id.hashCode() - result = 31 * result + passwordMd5Buffer.hashCode() - return result - } + override fun equals(other: Any?): Boolean + override fun hashCode(): Int } \ No newline at end of file diff --git a/mirai-core/src/commonMain/kotlin/MiraiImpl.kt b/mirai-core/src/commonMain/kotlin/MiraiImpl.kt index 088af7f1b..5b68ae763 100644 --- a/mirai-core/src/commonMain/kotlin/MiraiImpl.kt +++ b/mirai-core/src/commonMain/kotlin/MiraiImpl.kt @@ -10,11 +10,10 @@ package net.mamoe.mirai.internal import io.ktor.client.* -import io.ktor.client.engine.okhttp.* import io.ktor.client.features.* import io.ktor.client.request.* import io.ktor.client.request.forms.* -import io.ktor.utils.io.core.readBytes +import io.ktor.utils.io.core.* import kotlinx.serialization.json.Json import kotlinx.serialization.json.JsonObject import kotlinx.serialization.json.jsonPrimitive @@ -157,7 +156,7 @@ internal open class MiraiImpl : IMirai, LowLevelApiAccessor { override var FileCacheStrategy: FileCacheStrategy = net.mamoe.mirai.utils.FileCacheStrategy.PlatformDefault @Deprecated("Mirai is not going to use ktor. This is deprecated for removal.", level = DeprecationLevel.WARNING) - override var Http: HttpClient = HttpClient(OkHttp) { + override var Http: HttpClient = HttpClient() { install(HttpTimeout) { this.requestTimeoutMillis = 30_0000 this.connectTimeoutMillis = 30_0000 @@ -467,7 +466,7 @@ internal open class MiraiImpl : IMirai, LowLevelApiAccessor { source.ensureSequenceIdAvailable() @Suppress("BooleanLiteralArgument", "INVISIBLE_REFERENCE", "INVISIBLE_MEMBER") // false positive - check(!source.isRecalledOrPlanned.get() && source.isRecalledOrPlanned.compareAndSet(false, true)) { + check(!source.isRecalledOrPlanned.value && source.isRecalledOrPlanned.compareAndSet(false, true)) { "$source had already been recalled." } diff --git a/mirai-core/src/commonMain/kotlin/QQAndroidBot.kt b/mirai-core/src/commonMain/kotlin/QQAndroidBot.kt index 82a71a524..0058d733d 100644 --- a/mirai-core/src/commonMain/kotlin/QQAndroidBot.kt +++ b/mirai-core/src/commonMain/kotlin/QQAndroidBot.kt @@ -25,6 +25,7 @@ import net.mamoe.mirai.internal.network.components.* import net.mamoe.mirai.internal.network.handler.NetworkHandler import net.mamoe.mirai.internal.network.handler.NetworkHandler.State import net.mamoe.mirai.internal.network.handler.NetworkHandlerContextImpl +import net.mamoe.mirai.internal.network.handler.NetworkHandlerFactory import net.mamoe.mirai.internal.network.handler.NetworkHandlerSupport import net.mamoe.mirai.internal.network.handler.NetworkHandlerSupport.BaseStateImpl import net.mamoe.mirai.internal.network.handler.selector.KeepAliveNetworkHandlerSelector @@ -32,8 +33,7 @@ import net.mamoe.mirai.internal.network.handler.selector.NetworkException import net.mamoe.mirai.internal.network.handler.selector.SelectorNetworkHandler import net.mamoe.mirai.internal.network.handler.state.* import net.mamoe.mirai.internal.network.handler.state.CombinedStateObserver.Companion.plus -import net.mamoe.mirai.internal.network.impl.netty.ForceOfflineException -import net.mamoe.mirai.internal.network.impl.netty.NettyNetworkHandlerFactory +import net.mamoe.mirai.internal.network.impl.ForceOfflineException import net.mamoe.mirai.internal.network.notice.TraceLoggingNoticeProcessor import net.mamoe.mirai.internal.network.notice.UnconsumedNoticesAlerter import net.mamoe.mirai.internal.network.notice.decoders.GroupNotificationDecoder @@ -275,7 +275,7 @@ internal open class QQAndroidBot constructor( networkLogger, createNetworkLevelComponents(), ) - NettyNetworkHandlerFactory.create( + NetworkHandlerFactory.getPlatformDefault().create( context, context[ServerList].pollAny().toSocketAddress(), ) diff --git a/mirai-core/src/commonMain/kotlin/contact/FriendImpl.kt b/mirai-core/src/commonMain/kotlin/contact/FriendImpl.kt index dbe4f9cd1..4f8ab87d7 100644 --- a/mirai-core/src/commonMain/kotlin/contact/FriendImpl.kt +++ b/mirai-core/src/commonMain/kotlin/contact/FriendImpl.kt @@ -14,6 +14,7 @@ package net.mamoe.mirai.internal.contact +import io.ktor.utils.io.core.* import net.mamoe.mirai.LowLevelApi import net.mamoe.mirai.Mirai import net.mamoe.mirai.contact.Friend diff --git a/mirai-core/src/commonMain/kotlin/contact/GroupImpl.kt b/mirai-core/src/commonMain/kotlin/contact/GroupImpl.kt index e12640055..44321f4ad 100644 --- a/mirai-core/src/commonMain/kotlin/contact/GroupImpl.kt +++ b/mirai-core/src/commonMain/kotlin/contact/GroupImpl.kt @@ -59,7 +59,6 @@ import net.mamoe.mirai.message.MessageReceipt import net.mamoe.mirai.message.data.* import net.mamoe.mirai.spi.AudioToSilkService import net.mamoe.mirai.utils.* -import java.util.concurrent.ConcurrentLinkedQueue import kotlin.contracts.contract import kotlin.coroutines.CoroutineContext @@ -81,7 +80,7 @@ internal fun GroupImpl( groupInfo: GroupInfo, members: Sequence, ): GroupImpl { - return GroupImpl(bot, parentCoroutineContext, id, groupInfo, ContactList(ConcurrentLinkedQueue())).apply Group@{ + return GroupImpl(bot, parentCoroutineContext, id, groupInfo, ContactList(ConcurrentLinkedDeque())).apply Group@{ members.forEach { info -> if (info.uin == bot.id) { botAsMember = newNormalMember(info) @@ -112,7 +111,7 @@ internal fun GroupImpl( } private val logger by lazy { - MiraiLogger.Factory.create(GroupImpl::class.java, "Group") + MiraiLogger.Factory.create(GroupImpl::class, "Group") } internal fun Bot.nickIn(context: Contact): String = diff --git a/mirai-core/src/commonMain/kotlin/contact/GroupSendMessageImpl.kt b/mirai-core/src/commonMain/kotlin/contact/GroupSendMessageImpl.kt index 7142dff17..b3c534546 100644 --- a/mirai-core/src/commonMain/kotlin/contact/GroupSendMessageImpl.kt +++ b/mirai-core/src/commonMain/kotlin/contact/GroupSendMessageImpl.kt @@ -31,14 +31,14 @@ internal suspend fun C.broadcastMessagePreSendEvent( var eventName: String? = null return kotlin.runCatching { eventConstructor(this, message).also { - eventName = it.javaClass.simpleName + eventName = it::class.simpleName }.broadcast() }.onSuccess { check(!it.isCancelled) { throw EventCancelledException("cancelled by $eventName") } }.getOrElse { - eventName = eventName ?: (this@broadcastMessagePreSendEvent.javaClass.simpleName + "MessagePreSendEvent") + eventName = eventName ?: (this@broadcastMessagePreSendEvent::class.simpleName + "MessagePreSendEvent") throw EventCancelledException("exception thrown when broadcasting $eventName", it) }.message.toMessageChain() } diff --git a/mirai-core/src/commonMain/kotlin/contact/NormalMemberImpl.kt b/mirai-core/src/commonMain/kotlin/contact/NormalMemberImpl.kt index a6155aa18..e49b1bb84 100644 --- a/mirai-core/src/commonMain/kotlin/contact/NormalMemberImpl.kt +++ b/mirai-core/src/commonMain/kotlin/contact/NormalMemberImpl.kt @@ -186,8 +186,8 @@ internal class NormalMemberImpl constructor( if (response.ret == 255) error("Operation too fast") // https://github.com/mamoe/mirai/issues/1503 check(response.success) { "kick failed: ${response.ret}" } - @Suppress("INVISIBLE_MEMBER", "INVISIBLE_REFERENCE") - group.members.delegate.removeIf { it.id == this@NormalMemberImpl.id } + + group.members.delegate.removeAll { it.id == this@NormalMemberImpl.id } this@NormalMemberImpl.cancel(CancellationException("Kicked by bot")) MemberLeaveEvent.Kick(this@NormalMemberImpl, null).broadcast() } diff --git a/mirai-core/src/commonMain/kotlin/contact/announcement/AnnouncementsImpl.kt b/mirai-core/src/commonMain/kotlin/contact/announcement/AnnouncementsImpl.kt index c25a6d1ba..f9d9221c0 100644 --- a/mirai-core/src/commonMain/kotlin/contact/announcement/AnnouncementsImpl.kt +++ b/mirai-core/src/commonMain/kotlin/contact/announcement/AnnouncementsImpl.kt @@ -1,5 +1,5 @@ /* - * Copyright 2019-2021 Mamoe Technologies and contributors. + * Copyright 2019-2022 Mamoe Technologies and contributors. * * 此源代码的使用受 GNU AFFERO GENERAL PUBLIC LICENSE version 3 许可证的约束, 可以在以下链接找到该许可证. * Use of this source code is governed by the GNU AGPLv3 license that can be found through the following link. @@ -16,7 +16,6 @@ import io.ktor.client.request.forms.* import io.ktor.http.* import io.ktor.util.* import kotlinx.coroutines.flow.* -import kotlinx.coroutines.runBlocking import kotlinx.serialization.SerialName import kotlinx.serialization.Serializable import net.mamoe.mirai.Bot @@ -44,15 +43,19 @@ import net.mamoe.mirai.internal.utils.io.writeResource import net.mamoe.mirai.utils.* import net.mamoe.mirai.utils.Either.Companion.onLeft import net.mamoe.mirai.utils.Either.Companion.rightOrNull -import java.util.stream.Stream -internal class AnnouncementsImpl( - private val group: GroupImpl, - private val logger: MiraiLogger, +internal expect class AnnouncementsImpl( + group: GroupImpl, + logger: MiraiLogger, +) : CommonAnnouncementsImpl + +internal abstract class CommonAnnouncementsImpl( + protected val group: GroupImpl, + protected val logger: MiraiLogger, ) : Announcements { inline val bot get() = group.bot - private suspend fun getGroupAnnouncementList(i: Int): GroupAnnouncementList? { + protected suspend fun getGroupAnnouncementList(i: Int): GroupAnnouncementList? { return bot.getRawGroupAnnouncements(group.id, i).onLeft { if (logger.isEnabled) { // createException logger.warning( @@ -77,19 +80,6 @@ internal class AnnouncementsImpl( }.map { it.toAnnouncement(group) } } - override fun asStream(): Stream { - return stream { - var i = 1 - while (true) { - val result = runBlocking { getGroupAnnouncementList(i++) } ?: break - - if (result.inst.isNullOrEmpty() && result.feeds.isNullOrEmpty()) break - - result.inst?.let { yieldAll(it) } - result.feeds?.let { yieldAll(it) } - } - }.map { it.toAnnouncement(group) } - } override suspend fun delete(fid: String): Boolean { group.checkBotPermission(MemberPermission.ADMINISTRATOR) { "Only administrator have permission to delete group announcement" } diff --git a/mirai-core/src/commonMain/kotlin/contact/file/AbsoluteFileImpl.kt b/mirai-core/src/commonMain/kotlin/contact/file/AbsoluteFileImpl.kt index 8d5474ff1..0fa7e98b1 100644 --- a/mirai-core/src/commonMain/kotlin/contact/file/AbsoluteFileImpl.kt +++ b/mirai-core/src/commonMain/kotlin/contact/file/AbsoluteFileImpl.kt @@ -9,6 +9,7 @@ package net.mamoe.mirai.internal.contact.file +import io.ktor.utils.io.core.* import net.mamoe.mirai.contact.FileSupported import net.mamoe.mirai.contact.file.AbsoluteFile import net.mamoe.mirai.contact.file.AbsoluteFolder @@ -16,6 +17,7 @@ import net.mamoe.mirai.internal.message.data.FileMessageImpl import net.mamoe.mirai.internal.network.protocol.packet.chat.FileManagement import net.mamoe.mirai.internal.network.protocol.packet.chat.toResult import net.mamoe.mirai.message.data.FileMessage +import net.mamoe.mirai.utils.isSameClass import net.mamoe.mirai.utils.toUHexString internal class AbsoluteFileImpl( @@ -158,11 +160,9 @@ internal class AbsoluteFileImpl( override fun equals(other: Any?): Boolean { if (this === other) return true - if (javaClass != other?.javaClass) return false + if (other !is AbsoluteFileImpl || !isSameClass(this, other)) return false if (!super.equals(other)) return false - other as AbsoluteFileImpl - if (expiryTime != other.expiryTime) return false if (size != other.size) return false if (!sha1.contentEquals(other.sha1)) return false diff --git a/mirai-core/src/commonMain/kotlin/contact/file/AbsoluteFolderImpl.kt b/mirai-core/src/commonMain/kotlin/contact/file/AbsoluteFolderImpl.kt index 125df5f85..f8cbc4226 100644 --- a/mirai-core/src/commonMain/kotlin/contact/file/AbsoluteFolderImpl.kt +++ b/mirai-core/src/commonMain/kotlin/contact/file/AbsoluteFolderImpl.kt @@ -9,8 +9,8 @@ package net.mamoe.mirai.internal.contact.file +import io.ktor.utils.io.core.* import kotlinx.coroutines.flow.* -import kotlinx.coroutines.runBlocking import net.mamoe.mirai.contact.FileSupported import net.mamoe.mirai.contact.file.AbsoluteFile import net.mamoe.mirai.contact.file.AbsoluteFileFolder @@ -30,10 +30,8 @@ import net.mamoe.mirai.internal.network.protocol.packet.chat.toResult import net.mamoe.mirai.internal.utils.FileSystem import net.mamoe.mirai.internal.utils.io.serialization.toByteArray import net.mamoe.mirai.utils.* -import java.util.stream.Stream -import kotlin.streams.asStream -internal fun Oidb0x6d8.GetFileListRspBody.Item.resolved(parent: AbsoluteFolderImpl): AbsoluteFileFolder? { +internal fun Oidb0x6d8.GetFileListRspBody.Item.resolved(parent: CommonAbsoluteFolderImpl): AbsoluteFileFolder? { val item = this return when { item.fileInfo != null -> { @@ -46,7 +44,7 @@ internal fun Oidb0x6d8.GetFileListRspBody.Item.resolved(parent: AbsoluteFolderIm } } -internal fun AbsoluteFolderImpl.createChildFolder( +internal fun CommonAbsoluteFolderImpl.createChildFolder( folderInfo: GroupFileCommon.FolderInfo ): AbsoluteFolderImpl = AbsoluteFolderImpl( contact = contact, @@ -59,7 +57,7 @@ internal fun AbsoluteFolderImpl.createChildFolder( contentsCount = folderInfo.totalFileCount ) -internal fun AbsoluteFolderImpl.createChildFile( +internal fun CommonAbsoluteFolderImpl.createChildFile( info: GroupFileCommon.FileInfo ): AbsoluteFileImpl = AbsoluteFileImpl( contact = contact, @@ -76,7 +74,13 @@ internal fun AbsoluteFolderImpl.createChildFile( busId = info.busId ) -internal class AbsoluteFolderImpl( +internal expect class AbsoluteFolderImpl( + contact: FileSupported, parent: AbsoluteFolder?, id: String, name: String, + uploadTime: Long, uploaderId: Long, lastModifiedTime: Long, + contentsCount: Int, +) : CommonAbsoluteFolderImpl + +internal abstract class CommonAbsoluteFolderImpl( contact: FileSupported, parent: AbsoluteFolder?, id: String, name: String, uploadTime: Long, uploaderId: Long, lastModifiedTime: Long, override var contentsCount: Int, @@ -253,60 +257,21 @@ internal class AbsoluteFolderImpl( suspend fun getItemsFlow(): Flow = Companion.getItemsFlow(client, contact, id) - @JavaFriendlyAPI - private suspend fun getItemsSequence(): Sequence { - return sequence { - var index = 0 - while (true) { - val list = runBlocking { - bot.network.sendAndExpect( - FileManagement.GetFileList( - client, - groupCode = contact.id, - folderId = id, - startIndex = index - ) - ) - }.toResult("AbsoluteFolderImpl.getFilesFlow").getOrThrow() - index += list.itemList.size - - if (list.int32RetCode != 0) return@sequence - if (list.itemList.isEmpty()) return@sequence - - yieldAll(list.itemList) - } - } - } - - private fun Oidb0x6d8.GetFileListRspBody.Item.resolve(): AbsoluteFileFolder? = resolved(this@AbsoluteFolderImpl) + protected fun Oidb0x6d8.GetFileListRspBody.Item.resolve(): AbsoluteFileFolder? = + resolved(this@CommonAbsoluteFolderImpl) override suspend fun folders(): Flow { return getItemsFlow().filter { it.folderInfo != null }.map { it.resolve() as AbsoluteFolder } } - @JavaFriendlyAPI - override suspend fun foldersStream(): Stream { - return getItemsSequence().filter { it.folderInfo != null }.map { it.resolve() as AbsoluteFolder }.asStream() - } - override suspend fun files(): Flow { return getItemsFlow().filter { it.fileInfo != null }.map { it.resolve() as AbsoluteFile } } - @JavaFriendlyAPI - override suspend fun filesStream(): Stream { - return getItemsSequence().filter { it.fileInfo != null }.map { it.resolve() as AbsoluteFile }.asStream() - } - override suspend fun children(): Flow { return getItemsFlow().mapNotNull { it.resolve() } } - @JavaFriendlyAPI - override suspend fun childrenStream(): Stream { - return getItemsSequence().mapNotNull { it.resolve() }.asStream() - } - override suspend fun createFolder(name: String): AbsoluteFolder { if (name.isBlank()) throw IllegalArgumentException("folder name cannot be blank.") checkPermission("createFolder") @@ -414,25 +379,6 @@ internal class AbsoluteFolderImpl( return resolveFolder(path.substringBefore('/'))?.resolveFiles(path.substringAfter('/')) ?: emptyFlow() } - @OptIn(JavaFriendlyAPI::class) - override suspend fun resolveFilesStream(path: String): Stream { - if (path.isBlank()) throw IllegalArgumentException("path cannot be blank.") - if (!FileSystem.isLegal(path)) return Stream.empty() - - if (path[0] == '/') { - return root.resolveFilesStream(path.substring(1)) - } - - if (!path.contains('/')) { - return getItemsSequence() - .filter { it.fileInfo?.fileName == path } - .map { it.resolve() as AbsoluteFile } - .asStream() - } - - return resolveFolder(path.substringBefore('/'))?.resolveFilesStream(path.substringAfter('/')) ?: Stream.empty() - } - override suspend fun resolveAll(path: String): Flow { if (path.isBlank()) throw IllegalArgumentException("path cannot be blank.") if (!FileSystem.isLegal(path)) return emptyFlow() @@ -446,20 +392,6 @@ internal class AbsoluteFolderImpl( return resolveFolder(path.substringBefore('/'))?.resolveAll(path.substringAfter('/')) ?: emptyFlow() } - @JavaFriendlyAPI - override suspend fun resolveAllStream(path: String): Stream { - if (path.isBlank()) throw IllegalArgumentException("path cannot be blank.") - if (!FileSystem.isLegal(path)) return Stream.empty() - if (path[0] == '/') { - return root.resolveAllStream(path.substring(1)) - } - if (!path.contains('/')) { - return getItemsSequence().mapNotNull { it.resolve() }.asStream() - } - - return resolveFolder(path.substringBefore('/'))?.resolveAllStream(path.substringAfter('/')) ?: Stream.empty() - } - override suspend fun uploadNewFile( filepath: String, content: ExternalResource, @@ -487,11 +419,9 @@ internal class AbsoluteFolderImpl( override fun equals(other: Any?): Boolean { if (this === other) return true - if (javaClass != other?.javaClass) return false + if (!isSameType(this, other)) return false if (!super.equals(other)) return false - other as AbsoluteFolderImpl - if (contentsCount != other.contentsCount) return false return true diff --git a/mirai-core/src/commonMain/kotlin/contact/file/AbstractAbsoluteFileFolder.kt b/mirai-core/src/commonMain/kotlin/contact/file/AbstractAbsoluteFileFolder.kt index 328e01615..6c1d9a4c8 100644 --- a/mirai-core/src/commonMain/kotlin/contact/file/AbstractAbsoluteFileFolder.kt +++ b/mirai-core/src/commonMain/kotlin/contact/file/AbstractAbsoluteFileFolder.kt @@ -21,6 +21,7 @@ import net.mamoe.mirai.internal.network.protocol.packet.chat.FileManagement import net.mamoe.mirai.internal.network.protocol.packet.chat.toResult import net.mamoe.mirai.internal.utils.FileSystem import net.mamoe.mirai.utils.cast +import net.mamoe.mirai.utils.isSameType internal fun AbstractAbsoluteFileFolder.api(): AbsoluteFileFolder = this.cast() internal fun AbsoluteFileFolder.impl(): AbstractAbsoluteFileFolder = this.cast() @@ -115,9 +116,7 @@ internal abstract class AbstractAbsoluteFileFolder( @Suppress("DuplicatedCode") override fun equals(other: Any?): Boolean { if (this === other) return true - if (javaClass != other?.javaClass) return false - - other as AbstractAbsoluteFileFolder + if (!isSameType(this, other)) return false if (contact != other.contact) return false if (parent != other.parent) return false diff --git a/mirai-core/src/commonMain/kotlin/contact/roaming/RoamingMessagesImpl.kt b/mirai-core/src/commonMain/kotlin/contact/roaming/RoamingMessagesImpl.kt index e85faf5a4..15e180c08 100644 --- a/mirai-core/src/commonMain/kotlin/contact/roaming/RoamingMessagesImpl.kt +++ b/mirai-core/src/commonMain/kotlin/contact/roaming/RoamingMessagesImpl.kt @@ -15,7 +15,6 @@ import kotlinx.coroutines.currentCoroutineContext import kotlinx.coroutines.flow.Flow import kotlinx.coroutines.flow.flow import kotlinx.coroutines.isActive -import kotlinx.coroutines.runBlocking import net.mamoe.mirai.contact.Contact import net.mamoe.mirai.contact.roaming.RoamingMessage import net.mamoe.mirai.contact.roaming.RoamingMessageFilter @@ -26,10 +25,11 @@ import net.mamoe.mirai.internal.message.toMessageChainOnline import net.mamoe.mirai.internal.network.protocol.data.proto.MsgComm import net.mamoe.mirai.internal.network.protocol.packet.chat.receive.MessageSvcPbGetRoamMsgReq import net.mamoe.mirai.message.data.MessageChain -import net.mamoe.mirai.utils.* -import java.util.stream.Stream +import net.mamoe.mirai.utils.check +import net.mamoe.mirai.utils.mapToIntArray +import net.mamoe.mirai.utils.toLongUnsigned -internal sealed class RoamingMessagesImpl : RoamingMessages { +internal abstract class CommonRoamingMessagesImpl : RoamingMessages { abstract val contact: AbstractContact override suspend fun getMessagesIn( @@ -60,11 +60,11 @@ internal sealed class RoamingMessagesImpl : RoamingMessages { } } - private fun createRoamingMessage( + protected fun createRoamingMessage( message: MsgComm.Msg, messages: List ) = object : RoamingMessage { - override val contact: Contact get() = this@RoamingMessagesImpl.contact + override val contact: Contact get() = this@CommonRoamingMessagesImpl.contact override val sender: Long get() = message.msgHead.fromUin override val target: Long get() = message.msgHead.groupInfo?.groupCode ?: message.msgHead.toUin @@ -75,38 +75,6 @@ internal sealed class RoamingMessagesImpl : RoamingMessages { } } - - @JavaFriendlyAPI - override suspend fun getMessagesStream( - timeStart: Long, - timeEnd: Long, - filter: RoamingMessageFilter?, - ): Stream { - return stream { - var lastMessageTime = timeEnd - var random = 0L - while (true) { - val resp = runBlocking { - requestRoamMsg(timeStart, lastMessageTime, random) - } - - val messages = resp.messages ?: break - if (filter == null || filter === RoamingMessageFilter.ANY) { - messages.forEach { yield(runBlocking { it.toMessageChainOnline(contact.bot) }) } - } else { - for (message in messages) { - if (filter.invoke(createRoamingMessage(message, messages))) { - yield(runBlocking { message.toMessageChainOnline(contact.bot) }) - } - } - } - - lastMessageTime = resp.lastMessageTime - random = resp.random - } - } - } - abstract suspend fun requestRoamMsg( timeStart: Long, lastMessageTime: Long, @@ -114,6 +82,9 @@ internal sealed class RoamingMessagesImpl : RoamingMessages { ): MessageSvcPbGetRoamMsgReq.Response } + +internal expect sealed class RoamingMessagesImpl() : CommonRoamingMessagesImpl + internal class RoamingMessagesImplFriend( override val contact: FriendImpl ) : RoamingMessagesImpl() { diff --git a/mirai-core/src/commonMain/kotlin/event/EventListeners.kt b/mirai-core/src/commonMain/kotlin/event/EventListeners.kt index 366cc061d..390b66081 100644 --- a/mirai-core/src/commonMain/kotlin/event/EventListeners.kt +++ b/mirai-core/src/commonMain/kotlin/event/EventListeners.kt @@ -15,12 +15,7 @@ import kotlinx.coroutines.supervisorScope import kotlinx.coroutines.sync.withLock import net.mamoe.mirai.event.* import net.mamoe.mirai.internal.network.components.EVENT_LAUNCH_UNDISPATCHED -import net.mamoe.mirai.utils.MiraiLogger -import net.mamoe.mirai.utils.info -import net.mamoe.mirai.utils.systemProp -import net.mamoe.mirai.utils.withSwitch -import java.util.* -import java.util.concurrent.ConcurrentLinkedQueue +import net.mamoe.mirai.utils.* import kotlin.reflect.KClass @@ -37,13 +32,13 @@ internal class EventListeners { } } - private val map: Map> + private val map: Map> init { val map = - EnumMap>(EventPriority::class.java) + EnumMap>(EventPriority::class) EventPriority.values().forEach { - map[it] = ConcurrentLinkedQueue() + map[it] = ConcurrentLinkedDeque() } this.map = map } diff --git a/mirai-core/src/commonMain/kotlin/message/data/FileMessageImpl.kt b/mirai-core/src/commonMain/kotlin/message/data/FileMessageImpl.kt index 98f0ef29f..ee1ed9115 100644 --- a/mirai-core/src/commonMain/kotlin/message/data/FileMessageImpl.kt +++ b/mirai-core/src/commonMain/kotlin/message/data/FileMessageImpl.kt @@ -21,10 +21,7 @@ import net.mamoe.mirai.contact.file.AbsoluteFile import net.mamoe.mirai.contact.file.AbsoluteFolder import net.mamoe.mirai.internal.QQAndroidBot import net.mamoe.mirai.internal.asQQAndroidBot -import net.mamoe.mirai.internal.contact.file.AbsoluteFolderImpl -import net.mamoe.mirai.internal.contact.file.createChildFile -import net.mamoe.mirai.internal.contact.file.impl -import net.mamoe.mirai.internal.contact.file.resolved +import net.mamoe.mirai.internal.contact.file.* import net.mamoe.mirai.internal.network.protocol.data.proto.Oidb0x6d8.GetFileListRspBody import net.mamoe.mirai.internal.network.protocol.packet.chat.FileManagement import net.mamoe.mirai.internal.network.protocol.packet.chat.toResult @@ -71,7 +68,7 @@ internal data class FileMessageImpl( ?.resolved(root) as AbsoluteFolderImpl? ?: kotlin.run { for (folder in folders) { - AbsoluteFolderImpl.getItemsFlow( + CommonAbsoluteFolderImpl.getItemsFlow( (contact.bot as QQAndroidBot).client, contact, folder.folderInfo!!.folderId diff --git a/mirai-core/src/commonMain/kotlin/message/data/MultiMsgUploader.kt b/mirai-core/src/commonMain/kotlin/message/data/MultiMsgUploader.kt index 9fb3ccdae..b21dc3772 100644 --- a/mirai-core/src/commonMain/kotlin/message/data/MultiMsgUploader.kt +++ b/mirai-core/src/commonMain/kotlin/message/data/MultiMsgUploader.kt @@ -125,7 +125,7 @@ internal open class MultiMsgUploader( open suspend fun emit(id: String, msgs: Collection) { val nds = mutableListOf().let { tmp -> - nestedMsgs.putIfAbsent(id, tmp) ?: tmp + nestedMsgs.getOrPut(id) { tmp } } val existsIds = mutableSetOf() diff --git a/mirai-core/src/commonMain/kotlin/message/data/audio.kt b/mirai-core/src/commonMain/kotlin/message/data/audio.kt index 4a64f7a82..a2029599e 100644 --- a/mirai-core/src/commonMain/kotlin/message/data/audio.kt +++ b/mirai-core/src/commonMain/kotlin/message/data/audio.kt @@ -20,6 +20,7 @@ import net.mamoe.mirai.internal.utils.io.serialization.loadAs import net.mamoe.mirai.internal.utils.io.serialization.toByteArray import net.mamoe.mirai.message.data.* import net.mamoe.mirai.utils.copy +import net.mamoe.mirai.utils.isSameType import net.mamoe.mirai.utils.map @@ -123,9 +124,7 @@ internal class OnlineAudioImpl( @Suppress("DuplicatedCode") override fun equals(other: Any?): Boolean { if (this === other) return true - if (javaClass != other?.javaClass) return false - - other as OnlineAudioImpl + if (!isSameType(this, other)) return false if (filename != other.filename) return false if (!fileMd5.contentEquals(other.fileMd5)) return false @@ -232,9 +231,7 @@ internal class OfflineAudioImpl( override fun equals(other: Any?): Boolean { if (this === other) return true - if (javaClass != other?.javaClass) return false - - other as OfflineAudioImpl + if (!isSameType(this, other)) return false if (filename != other.filename) return false if (!fileMd5.contentEquals(other.fileMd5)) return false diff --git a/mirai-core/src/commonMain/kotlin/message/image/ImageDecoder.kt b/mirai-core/src/commonMain/kotlin/message/image/ImageDecoder.kt index 08552fe11..ecd35effd 100644 --- a/mirai-core/src/commonMain/kotlin/message/image/ImageDecoder.kt +++ b/mirai-core/src/commonMain/kotlin/message/image/ImageDecoder.kt @@ -10,10 +10,9 @@ package net.mamoe.mirai.internal.message.image import io.ktor.utils.io.core.* -import io.ktor.utils.io.streams.asInput +import io.ktor.utils.io.errors.* import net.mamoe.mirai.message.data.ImageType import net.mamoe.mirai.utils.* -import java.io.IOException //SOF0-SOF3 SOF5-SOF7 SOF9-SOF11 SOF13-SOF15 Segment // (0xC4, 0xC8 and 0xCC not included due to is not an SOF) @@ -150,7 +149,7 @@ private fun Input.getGIFImageInfo(): ImageInfo { internal fun ExternalResource.calculateImageInfo(): ImageInfo { //Preload val imageType = ImageType.match(formatName) - return inputStream().asInput().withUse { + return input().withUse { when (imageType) { ImageType.JPG -> getJPGImageInfo() ImageType.BMP -> getBMPImageInfo() diff --git a/mirai-core/src/commonMain/kotlin/message/image/OfflineImage.kt b/mirai-core/src/commonMain/kotlin/message/image/OfflineImage.kt index 1f7c83d62..5d2bd17dc 100644 --- a/mirai-core/src/commonMain/kotlin/message/image/OfflineImage.kt +++ b/mirai-core/src/commonMain/kotlin/message/image/OfflineImage.kt @@ -10,6 +10,7 @@ package net.mamoe.mirai.internal.message.image import kotlinx.serialization.Serializable +import kotlinx.serialization.Transient import net.mamoe.mirai.Bot import net.mamoe.mirai.message.data.Image import net.mamoe.mirai.message.data.ImageType diff --git a/mirai-core/src/commonMain/kotlin/message/protocol/MessageProtocol.kt b/mirai-core/src/commonMain/kotlin/message/protocol/MessageProtocol.kt index 26c5a5408..bb79eaa70 100644 --- a/mirai-core/src/commonMain/kotlin/message/protocol/MessageProtocol.kt +++ b/mirai-core/src/commonMain/kotlin/message/protocol/MessageProtocol.kt @@ -44,7 +44,7 @@ internal abstract class MessageProtocol( } object PriorityComparator : Comparator { - override fun compare(o1: MessageProtocol, o2: MessageProtocol): Int { + override fun compare(a: MessageProtocol, b: MessageProtocol): Int { // Do not use o1.compareTo // > Task :mirai-core:checkAndroidApiLevel @@ -53,7 +53,7 @@ internal abstract class MessageProtocol( // > Invoke method java/lang/Integer.compareUnsigned(II)I // Couldn't access java/lang/Integer.compareUnsigned(II)I: java/lang/Integer.compareUnsigned(II)I since api level 26 - return uintCompare(o1.priority.toInt(), o2.priority.toInt()) + return uintCompare(a.priority.toInt(), b.priority.toInt()) } private fun uintCompare(v1: Int, v2: Int): Int = (v1 xor Int.MIN_VALUE).compareTo(v2 xor Int.MIN_VALUE) diff --git a/mirai-core/src/commonMain/kotlin/message/protocol/MessageProtocolFacade.kt b/mirai-core/src/commonMain/kotlin/message/protocol/MessageProtocolFacade.kt index 040a68880..4ffa003e0 100644 --- a/mirai-core/src/commonMain/kotlin/message/protocol/MessageProtocolFacade.kt +++ b/mirai-core/src/commonMain/kotlin/message/protocol/MessageProtocolFacade.kt @@ -42,7 +42,6 @@ import net.mamoe.mirai.message.data.* import net.mamoe.mirai.message.data.visitor.RecursiveMessageVisitor import net.mamoe.mirai.message.data.visitor.accept import net.mamoe.mirai.utils.* -import java.util.* import kotlin.reflect.KClass internal interface MessageProtocolFacade { @@ -161,7 +160,7 @@ internal suspend fun MessageProtocolFacade.decodeAndRefineDeep( internal class MessageProtocolFacadeImpl( - private val protocols: Iterable = ServiceLoader.load(MessageProtocol::class.java), + private val protocols: Iterable = loadServices(MessageProtocol::class).asIterable(), override val remark: String = "MessageProtocolFacade" ) : MessageProtocolFacade { override val encoderPipeline: MessageEncoderPipeline = MessageEncoderPipelineImpl() @@ -170,8 +169,8 @@ internal class MessageProtocolFacadeImpl( override val outgoingPipeline: OutgoingMessagePipeline = OutgoingMessagePipelineImpl() override val loaded: List = kotlin.run { - val instances: PriorityQueue = protocols - .toCollection(PriorityQueue(MessageProtocol.PriorityComparator.reversed())) + val instances = protocols + .sortedWith(MessageProtocol.PriorityComparator.reversed()) for (instance in instances) { instance.collectProcessors(object : ProcessorCollector() { override fun add(encoder: MessageEncoder, elementType: KClass) { diff --git a/mirai-core/src/commonMain/kotlin/message/protocol/impl/RichMessageProtocol.kt b/mirai-core/src/commonMain/kotlin/message/protocol/impl/RichMessageProtocol.kt index 756e64800..6e46357e3 100644 --- a/mirai-core/src/commonMain/kotlin/message/protocol/impl/RichMessageProtocol.kt +++ b/mirai-core/src/commonMain/kotlin/message/protocol/impl/RichMessageProtocol.kt @@ -23,10 +23,10 @@ import net.mamoe.mirai.internal.message.protocol.encode.MessageEncoderContext.Co import net.mamoe.mirai.internal.message.runWithBugReport import net.mamoe.mirai.internal.network.protocol.data.proto.ImMsgBody import net.mamoe.mirai.message.data.* +import net.mamoe.mirai.utils.deflate import net.mamoe.mirai.utils.hexToBytes +import net.mamoe.mirai.utils.inflate import net.mamoe.mirai.utils.toUHexString -import net.mamoe.mirai.utils.unzip -import net.mamoe.mirai.utils.zip /** * Handles: @@ -50,7 +50,7 @@ internal class RichMessageProtocol : MessageProtocol() { private class Encoder : MessageEncoder { override suspend fun MessageEncoderContext.process(data: RichMessage) { markAsConsumed() - val content = data.content.toByteArray().zip() + val content = data.content.toByteArray().deflate() var longTextResId: String? = null when (data) { is ForwardMessageInternal -> { @@ -127,7 +127,7 @@ internal class RichMessageProtocol : MessageProtocol() { { "resId=" + lightApp.msgResid + "data=" + lightApp.data.toUHexString() }) { when (lightApp.data[0].toInt()) { 0 -> lightApp.data.decodeToString(startIndex = 1) - 1 -> lightApp.data.unzip(1).decodeToString() + 1 -> lightApp.data.inflate(1).decodeToString() else -> error("unknown compression flag=${lightApp.data[0]}") } } @@ -146,7 +146,7 @@ internal class RichMessageProtocol : MessageProtocol() { val content = runWithBugReport("解析 richMsg", { richMsg.template1.toUHexString() }) { when (richMsg.template1[0].toInt()) { 0 -> richMsg.template1.decodeToString(startIndex = 1) - 1 -> richMsg.template1.unzip(1).decodeToString() + 1 -> richMsg.template1.inflate(1).decodeToString() else -> error("unknown compression flag=${richMsg.template1[0]}") } } diff --git a/mirai-core/src/commonMain/kotlin/message/protocol/impl/TextProtocol.kt b/mirai-core/src/commonMain/kotlin/message/protocol/impl/TextProtocol.kt index b7bf17810..20abf8ee2 100644 --- a/mirai-core/src/commonMain/kotlin/message/protocol/impl/TextProtocol.kt +++ b/mirai-core/src/commonMain/kotlin/message/protocol/impl/TextProtocol.kt @@ -7,6 +7,8 @@ * https://github.com/mamoe/mirai/blob/dev/LICENSE */ +@file:JvmName("TextProtocol_common") + package net.mamoe.mirai.internal.message.protocol.impl import io.ktor.utils.io.core.* @@ -25,7 +27,7 @@ import net.mamoe.mirai.internal.network.protocol.data.proto.ImMsgBody import net.mamoe.mirai.message.data.* import net.mamoe.mirai.utils.read import net.mamoe.mirai.utils.safeCast -import net.mamoe.mirai.utils.withUse +import kotlin.jvm.JvmName /** * For [PlainText] and [At] @@ -135,10 +137,7 @@ internal class TextProtocol : MessageProtocol() { @Suppress("RegExpSingleCharAlternation", "RegExpRedundantEscape") private val EMOJI_PATTERN: Regex? = runCatching { - val resource = - AtEncoder::class.java.classLoader.getResourceAsStream("emoji-pattern.regex") - ?.withUse { readBytes().decodeToString() } - ?: return@runCatching null + val resource = getEmojiPatternResourceOrNull() ?: return@runCatching null Regex(resource) }.getOrNull() // May some java runtime unsupported @@ -176,4 +175,6 @@ internal class TextProtocol : MessageProtocol() { } } } -} \ No newline at end of file +} + +internal expect fun getEmojiPatternResourceOrNull(): String? \ No newline at end of file diff --git a/mirai-core/src/commonMain/kotlin/message/source/MessageSourceInternal.kt b/mirai-core/src/commonMain/kotlin/message/source/MessageSourceInternal.kt index d0070d89a..81ba4f74e 100644 --- a/mirai-core/src/commonMain/kotlin/message/source/MessageSourceInternal.kt +++ b/mirai-core/src/commonMain/kotlin/message/source/MessageSourceInternal.kt @@ -9,6 +9,7 @@ package net.mamoe.mirai.internal.message.source +import kotlinx.atomicfu.AtomicBoolean import kotlinx.serialization.Transient import net.mamoe.mirai.contact.Contact import net.mamoe.mirai.internal.message.LightMessageRefiner.dropMiraiInternalFlags @@ -19,7 +20,6 @@ import net.mamoe.mirai.message.MessageReceipt import net.mamoe.mirai.message.data.* import net.mamoe.mirai.message.data.visitor.MessageVisitor import net.mamoe.mirai.utils.cast -import java.util.concurrent.atomic.AtomicBoolean /** diff --git a/mirai-core/src/commonMain/kotlin/message/source/incomingSourceImpl.kt b/mirai-core/src/commonMain/kotlin/message/source/incomingSourceImpl.kt index a2f70481d..a34115f3b 100644 --- a/mirai-core/src/commonMain/kotlin/message/source/incomingSourceImpl.kt +++ b/mirai-core/src/commonMain/kotlin/message/source/incomingSourceImpl.kt @@ -11,6 +11,8 @@ package net.mamoe.mirai.internal.message.source +import kotlinx.atomicfu.AtomicBoolean +import kotlinx.atomicfu.atomic import kotlinx.serialization.Serializable import kotlinx.serialization.Transient import net.mamoe.mirai.Bot @@ -36,7 +38,6 @@ import net.mamoe.mirai.message.data.visitor.MessageVisitor import net.mamoe.mirai.utils.EMPTY_BYTE_ARRAY import net.mamoe.mirai.utils.encodeBase64 import net.mamoe.mirai.utils.mapToIntArray -import java.util.concurrent.atomic.AtomicBoolean @Suppress("SERIALIZER_TYPE_INCOMPATIBLE") @Serializable(OnlineMessageSourceFromFriendImpl.Serializer::class) @@ -47,7 +48,7 @@ internal class OnlineMessageSourceFromFriendImpl( object Serializer : MessageSourceSerializerImpl("OnlineMessageSourceFromFriend") override val sequenceIds: IntArray = msg.mapToIntArray { it.msgHead.msgSeq } - override var isRecalledOrPlanned: AtomicBoolean = AtomicBoolean(false) + override var isRecalledOrPlanned: AtomicBoolean = atomic(false) override val ids: IntArray get() = sequenceIds // msg.msgBody.richText.attr!!.random override val internalIds: IntArray = msg.mapToIntArray { it.msgBody.richText.attr?.random ?: 0 @@ -80,7 +81,7 @@ internal class OnlineMessageSourceFromStrangerImpl( object Serializer : MessageSourceSerializerImpl("OnlineMessageSourceFromStranger") override val sequenceIds: IntArray = msg.mapToIntArray { it.msgHead.msgSeq } - override var isRecalledOrPlanned: AtomicBoolean = AtomicBoolean(false) + override var isRecalledOrPlanned: AtomicBoolean = atomic(false) override val ids: IntArray get() = sequenceIds // msg.msgBody.richText.attr!!.random override val internalIds: IntArray = msg.mapToIntArray { it.msgBody.richText.attr?.random ?: 0 @@ -153,7 +154,7 @@ internal class OnlineMessageSourceFromTempImpl( override val sequenceIds: IntArray = msg.mapToIntArray { it.msgHead.msgSeq } override val internalIds: IntArray = msg.mapToIntArray { it.msgBody.richText.attr!!.random } - override var isRecalledOrPlanned: AtomicBoolean = AtomicBoolean(false) + override var isRecalledOrPlanned: AtomicBoolean = atomic(false) override val ids: IntArray get() = sequenceIds // override val time: Int = msg.first().msgHead.msgTime override var originalMessageLazy = lazy { @@ -189,7 +190,7 @@ internal class OnlineMessageSourceFromGroupImpl( object Serializer : MessageSourceSerializerImpl("OnlineMessageSourceFromGroupImpl") @Transient - override var isRecalledOrPlanned: AtomicBoolean = AtomicBoolean(false) + override var isRecalledOrPlanned: AtomicBoolean = atomic(false) override val sequenceIds: IntArray = msg.mapToIntArray { it.msgHead.msgSeq } override val internalIds: IntArray = msg.mapToIntArray { it.msgBody.richText.attr!!.random } override val ids: IntArray get() = sequenceIds diff --git a/mirai-core/src/commonMain/kotlin/message/source/offlineSourceImpl.kt b/mirai-core/src/commonMain/kotlin/message/source/offlineSourceImpl.kt index bb83f2fc5..8bd35d4b0 100644 --- a/mirai-core/src/commonMain/kotlin/message/source/offlineSourceImpl.kt +++ b/mirai-core/src/commonMain/kotlin/message/source/offlineSourceImpl.kt @@ -10,6 +10,8 @@ package net.mamoe.mirai.internal.message.source +import kotlinx.atomicfu.AtomicBoolean +import kotlinx.atomicfu.atomic import kotlinx.serialization.Serializable import kotlinx.serialization.Transient import net.mamoe.mirai.Bot @@ -25,8 +27,8 @@ import net.mamoe.mirai.message.data.MessageSourceKind import net.mamoe.mirai.message.data.OfflineMessageSource import net.mamoe.mirai.message.data.visitor.MessageVisitor import net.mamoe.mirai.utils.EMPTY_BYTE_ARRAY +import net.mamoe.mirai.utils.isSameType import net.mamoe.mirai.utils.mapToIntArray -import java.util.concurrent.atomic.AtomicBoolean @Suppress("SERIALIZER_TYPE_INCOMPATIBLE") @Serializable(OfflineMessageSourceImplData.Serializer::class) @@ -58,7 +60,7 @@ internal class OfflineMessageSourceImplData( @Transient @Suppress("INVISIBLE_REFERENCE", "INVISIBLE_MEMBER") - override var isRecalledOrPlanned: AtomicBoolean = AtomicBoolean(false) + override var isRecalledOrPlanned: AtomicBoolean = atomic(false) override fun toJceData(): ImMsgBody.SourceMsg { return jceData ?: ImMsgBody.SourceMsg( @@ -79,9 +81,7 @@ internal class OfflineMessageSourceImplData( override fun equals(other: Any?): Boolean { if (this === other) return true - if (javaClass != other?.javaClass) return false - - other as OfflineMessageSourceImplData + if (!isSameType(this, other)) return false val originElems = originElems if (originElems != null) { diff --git a/mirai-core/src/commonMain/kotlin/message/source/outgoingSourceImpl.kt b/mirai-core/src/commonMain/kotlin/message/source/outgoingSourceImpl.kt index 9a0579b12..59054ca8c 100644 --- a/mirai-core/src/commonMain/kotlin/message/source/outgoingSourceImpl.kt +++ b/mirai-core/src/commonMain/kotlin/message/source/outgoingSourceImpl.kt @@ -11,6 +11,8 @@ package net.mamoe.mirai.internal.message.source +import kotlinx.atomicfu.AtomicBoolean +import kotlinx.atomicfu.atomic import kotlinx.coroutines.* import kotlinx.serialization.Serializable import net.mamoe.mirai.Bot @@ -32,7 +34,6 @@ import net.mamoe.mirai.message.data.OnlineMessageSource import net.mamoe.mirai.message.data.visitor.MessageVisitor import net.mamoe.mirai.utils.loadService import net.mamoe.mirai.utils.toLongUnsigned -import java.util.concurrent.atomic.AtomicBoolean private fun T.toJceDataImpl(subject: ContactOrBot?): ImMsgBody.SourceMsg @@ -96,7 +97,7 @@ internal class OnlineMessageSourceToFriendImpl( get() = sender override val ids: IntArray get() = sequenceIds - override var isRecalledOrPlanned: AtomicBoolean = AtomicBoolean(false) + override var isRecalledOrPlanned: AtomicBoolean = atomic(false) private val jceData: ImMsgBody.SourceMsg by lazy { toJceDataImpl(subject) } override fun toJceData(): ImMsgBody.SourceMsg = jceData @@ -129,7 +130,7 @@ internal class OnlineMessageSourceToStrangerImpl( get() = sender override val ids: IntArray get() = sequenceIds - override var isRecalledOrPlanned: AtomicBoolean = AtomicBoolean(false) + override var isRecalledOrPlanned: AtomicBoolean = atomic(false) private val jceData: ImMsgBody.SourceMsg by lazy { toJceDataImpl(subject) } override fun toJceData(): ImMsgBody.SourceMsg = jceData @@ -162,7 +163,7 @@ internal class OnlineMessageSourceToTempImpl( get() = sender override val ids: IntArray get() = sequenceIds - override var isRecalledOrPlanned: AtomicBoolean = AtomicBoolean(false) + override var isRecalledOrPlanned: AtomicBoolean = atomic(false) private val jceData: ImMsgBody.SourceMsg by lazy { toJceDataImpl(subject) } override fun toJceData(): ImMsgBody.SourceMsg = jceData @@ -191,7 +192,7 @@ internal class OnlineMessageSourceToGroupImpl( get() = sequenceIds override val bot: Bot get() = sender - override var isRecalledOrPlanned: AtomicBoolean = AtomicBoolean(false) + override var isRecalledOrPlanned: AtomicBoolean = atomic(false) /** * Note that in tests result of this Deferred is always `null`. See TestMessageSourceSequenceIdAwaiter. diff --git a/mirai-core/src/commonMain/kotlin/network/ContactListCache.kt b/mirai-core/src/commonMain/kotlin/network/ContactListCache.kt index 935c27e69..266a74063 100644 --- a/mirai-core/src/commonMain/kotlin/network/ContactListCache.kt +++ b/mirai-core/src/commonMain/kotlin/network/ContactListCache.kt @@ -1,10 +1,10 @@ /* - * Copyright 2019-2021 Mamoe Technologies and contributors. + * Copyright 2019-2022 Mamoe Technologies and contributors. * - * 此源代码的使用受 GNU AFFERO GENERAL PUBLIC LICENSE version 3 许可证的约束, 可以在以下链接找到该许可证. - * Use of this source code is governed by the GNU AGPLv3 license that can be found through the following link. + * 此源代码的使用受 GNU AFFERO GENERAL PUBLIC LICENSE version 3 许可证的约束, 可以在以下链接找到该许可证. + * Use of this source code is governed by the GNU AGPLv3 license that can be found through the following link. * - * https://github.com/mamoe/mirai/blob/master/LICENSE + * https://github.com/mamoe/mirai/blob/dev/LICENSE */ package net.mamoe.mirai.internal.network @@ -18,13 +18,7 @@ import net.mamoe.mirai.internal.contact.info.MemberInfoImpl import net.mamoe.mirai.internal.network.protocol.data.jce.StTroopNum import net.mamoe.mirai.internal.utils.ScheduledJob import net.mamoe.mirai.internal.utils.groupCacheDir -import net.mamoe.mirai.utils.MiraiLogger -import net.mamoe.mirai.utils.createFileIfNotExists -import net.mamoe.mirai.utils.info -import net.mamoe.mirai.utils.runBIO -import java.io.File -import java.util.concurrent.ConcurrentHashMap -import java.util.concurrent.ConcurrentLinkedQueue +import net.mamoe.mirai.utils.* internal val JsonForCache = Json { encodeDefaults = true @@ -70,7 +64,7 @@ internal class GroupMemberListCaches( } - private val changedGroups: MutableCollection = ConcurrentLinkedQueue() + private val changedGroups: MutableCollection = ConcurrentLinkedDeque() private val groupListSaver: ScheduledJob by lazy { ScheduledJob(bot.coroutineContext, bot.configuration.contactListCache.saveIntervalMillis) { runBIO { saveGroupCaches() } @@ -84,16 +78,16 @@ internal class GroupMemberListCaches( private fun takeCurrentChangedGroups(): Map { val ret = HashMap() - changedGroups.removeIf { + changedGroups.removeAll { ret[it] = get(it) true } return ret } - private val cacheDir: File by lazy { bot.configuration.groupCacheDir() } + private val cacheDir: MiraiFile by lazy { bot.configuration.groupCacheDir() } - private fun resolveCacheFile(groupCode: Long): File { + private fun resolveCacheFile(groupCode: Long): MiraiFile { cacheDir.mkdirs() return cacheDir.resolve("$groupCode.json") } @@ -103,7 +97,7 @@ internal class GroupMemberListCaches( if (currentChanged.isNotEmpty()) { for ((id, cache) in currentChanged) { val file = resolveCacheFile(id) - file.createFileIfNotExists() + file.createNewFile() file.writeText(JsonForCache.encodeToString(GroupMemberListCache.serializer(), cache)) } logger.info { "Saved ${currentChanged.size} groups to local cache." } diff --git a/mirai-core/src/commonMain/kotlin/network/QQAndroidClient.kt b/mirai-core/src/commonMain/kotlin/network/QQAndroidClient.kt index bd1406cc6..dfaeeb888 100644 --- a/mirai-core/src/commonMain/kotlin/network/QQAndroidClient.kt +++ b/mirai-core/src/commonMain/kotlin/network/QQAndroidClient.kt @@ -25,6 +25,8 @@ import net.mamoe.mirai.internal.utils.AtomicIntSeq import net.mamoe.mirai.internal.utils.MiraiProtocolInternal import net.mamoe.mirai.internal.utils.NetworkType import net.mamoe.mirai.utils.* +import kotlin.jvm.Synchronized +import kotlin.jvm.Volatile import kotlin.random.Random diff --git a/mirai-core/src/commonMain/kotlin/network/component/ComponentKey.kt b/mirai-core/src/commonMain/kotlin/network/component/ComponentKey.kt index 92ebcc076..5aa134782 100644 --- a/mirai-core/src/commonMain/kotlin/network/component/ComponentKey.kt +++ b/mirai-core/src/commonMain/kotlin/network/component/ComponentKey.kt @@ -7,11 +7,13 @@ * https://github.com/mamoe/mirai/blob/dev/LICENSE */ +@file:JvmName("ComponentKeyKt_common") + package net.mamoe.mirai.internal.network.component import net.mamoe.mirai.internal.message.protocol.MessageProtocolFacade +import kotlin.jvm.JvmName import kotlin.reflect.* -import kotlin.reflect.full.allSupertypes /** * A key for specific component [T]. Components are not polymorphic. @@ -80,12 +82,7 @@ internal interface ComponentKey { else -> upperBounds.joinToString(" & ") { it.renderType(fullName) } } } - - private fun ComponentKey<*>.getComponentTypeArgument(): KTypeProjection? { - val thisType = this::class.allSupertypes.find { it.classifier == COMPONENT_KEY_K_CLASS } - return thisType?.arguments?.firstOrNull() - } - - private val COMPONENT_KEY_K_CLASS = ComponentKey::class } } + +internal expect fun ComponentKey<*>.getComponentTypeArgument(): KTypeProjection? \ No newline at end of file diff --git a/mirai-core/src/commonMain/kotlin/network/component/ComponentStorage.kt b/mirai-core/src/commonMain/kotlin/network/component/ComponentStorage.kt index 6440d81a8..dff26c541 100644 --- a/mirai-core/src/commonMain/kotlin/network/component/ComponentStorage.kt +++ b/mirai-core/src/commonMain/kotlin/network/component/ComponentStorage.kt @@ -9,7 +9,7 @@ package net.mamoe.mirai.internal.network.component -import org.jetbrains.annotations.TestOnly +import net.mamoe.mirai.utils.TestOnly import kotlin.contracts.InvocationKind import kotlin.contracts.contract @@ -23,7 +23,7 @@ import kotlin.contracts.contract * @see withFallback */ internal interface ComponentStorage { - @get:TestOnly + @TestOnly val size: Int @Throws(NoSuchComponentException::class) @@ -57,6 +57,7 @@ private class CombinedComponentStorage( val fallback: ComponentStorage, ) : ComponentStorage { override val keys: Set> get() = main.keys + fallback.keys + @TestOnly override val size: Int get() = main.size + fallback.size override fun get(key: ComponentKey): T { diff --git a/mirai-core/src/commonMain/kotlin/network/component/ComponentStorageDelegate.kt b/mirai-core/src/commonMain/kotlin/network/component/ComponentStorageDelegate.kt index 8617a13b6..1ff8bd94b 100644 --- a/mirai-core/src/commonMain/kotlin/network/component/ComponentStorageDelegate.kt +++ b/mirai-core/src/commonMain/kotlin/network/component/ComponentStorageDelegate.kt @@ -1,17 +1,20 @@ /* - * Copyright 2019-2021 Mamoe Technologies and contributors. + * Copyright 2019-2022 Mamoe Technologies and contributors. * - * 此源代码的使用受 GNU AFFERO GENERAL PUBLIC LICENSE version 3 许可证的约束, 可以在以下链接找到该许可证. - * Use of this source code is governed by the GNU AGPLv3 license that can be found through the following link. + * 此源代码的使用受 GNU AFFERO GENERAL PUBLIC LICENSE version 3 许可证的约束, 可以在以下链接找到该许可证. + * Use of this source code is governed by the GNU AGPLv3 license that can be found through the following link. * - * https://github.com/mamoe/mirai/blob/master/LICENSE + * https://github.com/mamoe/mirai/blob/dev/LICENSE */ package net.mamoe.mirai.internal.network.component +import net.mamoe.mirai.utils.TestOnly + internal class ComponentStorageDelegate( private val instance: () -> ComponentStorage ) : ComponentStorage { + @TestOnly override val size: Int get() = instance().size override fun get(key: ComponentKey): T = instance()[key] override fun getOrNull(key: ComponentKey): T? = instance().getOrNull(key) diff --git a/mirai-core/src/commonMain/kotlin/network/component/ConcurrentComponentStorage.kt b/mirai-core/src/commonMain/kotlin/network/component/ConcurrentComponentStorage.kt index 2101755e0..e677a8a42 100644 --- a/mirai-core/src/commonMain/kotlin/network/component/ConcurrentComponentStorage.kt +++ b/mirai-core/src/commonMain/kotlin/network/component/ConcurrentComponentStorage.kt @@ -1,17 +1,18 @@ /* - * Copyright 2019-2021 Mamoe Technologies and contributors. + * Copyright 2019-2022 Mamoe Technologies and contributors. * - * 此源代码的使用受 GNU AFFERO GENERAL PUBLIC LICENSE version 3 许可证的约束, 可以在以下链接找到该许可证. - * Use of this source code is governed by the GNU AGPLv3 license that can be found through the following link. + * 此源代码的使用受 GNU AFFERO GENERAL PUBLIC LICENSE version 3 许可证的约束, 可以在以下链接找到该许可证. + * Use of this source code is governed by the GNU AGPLv3 license that can be found through the following link. * - * https://github.com/mamoe/mirai/blob/master/LICENSE + * https://github.com/mamoe/mirai/blob/dev/LICENSE */ package net.mamoe.mirai.internal.network.component import net.mamoe.mirai.internal.network.component.ComponentKey.Companion.componentName +import net.mamoe.mirai.utils.ConcurrentHashMap +import net.mamoe.mirai.utils.TestOnly import net.mamoe.mirai.utils.systemProp -import java.util.concurrent.ConcurrentHashMap import kotlin.LazyThreadSafetyMode.NONE /** @@ -25,11 +26,12 @@ internal class ConcurrentComponentStorage( private val map = ConcurrentHashMap, Any?>() override val keys: Set> get() = map.keys + @TestOnly override val size: Int get() = map.size override operator fun get(key: ComponentKey): T { return getOrNull(key) - ?: throw NoSuchComponentException(key, this).apply { creationStacktrace?.let(this::initCause) } + ?: throw NoSuchComponentException(key, this, creationStacktrace) } override fun getOrNull(key: ComponentKey): T? { diff --git a/mirai-core/src/commonMain/kotlin/network/component/NoSuchComponentException.kt b/mirai-core/src/commonMain/kotlin/network/component/NoSuchComponentException.kt index 67d41feea..7a52bdd71 100644 --- a/mirai-core/src/commonMain/kotlin/network/component/NoSuchComponentException.kt +++ b/mirai-core/src/commonMain/kotlin/network/component/NoSuchComponentException.kt @@ -1,10 +1,10 @@ /* - * Copyright 2019-2021 Mamoe Technologies and contributors. + * Copyright 2019-2022 Mamoe Technologies and contributors. * - * 此源代码的使用受 GNU AFFERO GENERAL PUBLIC LICENSE version 3 许可证的约束, 可以在以下链接找到该许可证. - * Use of this source code is governed by the GNU AGPLv3 license that can be found through the following link. + * 此源代码的使用受 GNU AFFERO GENERAL PUBLIC LICENSE version 3 许可证的约束, 可以在以下链接找到该许可证. + * Use of this source code is governed by the GNU AGPLv3 license that can be found through the following link. * - * https://github.com/mamoe/mirai/blob/master/LICENSE + * https://github.com/mamoe/mirai/blob/dev/LICENSE */ package net.mamoe.mirai.internal.network.component @@ -14,7 +14,8 @@ package net.mamoe.mirai.internal.network.component */ internal data class NoSuchComponentException( val key: ComponentKey<*>, - val storage: ComponentStorage + val storage: ComponentStorage, + override val cause: Throwable? = null ) : NoSuchElementException() { override val message: String by lazy { "No such component '$key' in storage: \n$storage" diff --git a/mirai-core/src/commonMain/kotlin/network/components/AccountSecretsManager.kt b/mirai-core/src/commonMain/kotlin/network/components/AccountSecretsManager.kt index 81cc18ba3..5bec626dc 100644 --- a/mirai-core/src/commonMain/kotlin/network/components/AccountSecretsManager.kt +++ b/mirai-core/src/commonMain/kotlin/network/components/AccountSecretsManager.kt @@ -26,8 +26,8 @@ import net.mamoe.mirai.internal.utils.io.ProtoBuf import net.mamoe.mirai.internal.utils.io.serialization.loadAs import net.mamoe.mirai.internal.utils.io.serialization.toByteArray import net.mamoe.mirai.utils.* -import java.io.File -import java.util.concurrent.CopyOnWriteArraySet +import kotlin.jvm.Synchronized +import kotlin.jvm.Volatile /** * For a [Bot]. @@ -91,9 +91,7 @@ internal data class AccountSecretsImpl( ) : AccountSecrets, ProtoBuf { override fun equals(other: Any?): Boolean { if (this === other) return true - if (javaClass != other?.javaClass) return false - - other as AccountSecretsImpl + if (!isSameType(this, other)) return false if (loginExtraData != other.loginExtraData) return false if (wLoginSigInfoField != other.wLoginSigInfoField) return false @@ -135,7 +133,7 @@ internal fun AccountSecretsImpl( device: DeviceInfo, account: BotAccount, ): AccountSecretsImpl { return AccountSecretsImpl( - loginExtraData = CopyOnWriteArraySet(), + loginExtraData = ConcurrentSet(), wLoginSigInfoField = null, G = device.guid, dpwd = get_mpasswd().toByteArray(), @@ -177,7 +175,7 @@ internal class MemoryAccountSecretsManager : AccountSecretsManager { internal class FileCacheAccountSecretsManager( - val file: File, + val file: MiraiFile, val logger: MiraiLogger, ) : AccountSecretsManager { @Synchronized @@ -216,7 +214,7 @@ internal class FileCacheAccountSecretsManager( } companion object { - fun saveSecretsToFile(file: File, account: BotAccount, secrets: AccountSecrets) { + fun saveSecretsToFile(file: MiraiFile, account: BotAccount, secrets: AccountSecrets) { file.writeBytes( TEA.encrypt( AccountSecretsImpl(secrets).toByteArray(AccountSecretsImpl.serializer()), diff --git a/mirai-core/src/commonMain/kotlin/network/components/BdhSessionSyncer.kt b/mirai-core/src/commonMain/kotlin/network/components/BdhSessionSyncer.kt index 7bd266bd1..531bd1cce 100644 --- a/mirai-core/src/commonMain/kotlin/network/components/BdhSessionSyncer.kt +++ b/mirai-core/src/commonMain/kotlin/network/components/BdhSessionSyncer.kt @@ -1,10 +1,10 @@ /* - * Copyright 2019-2021 Mamoe Technologies and contributors. + * Copyright 2019-2022 Mamoe Technologies and contributors. * - * 此源代码的使用受 GNU AFFERO GENERAL PUBLIC LICENSE version 3 许可证的约束, 可以在以下链接找到该许可证. - * Use of this source code is governed by the GNU AGPLv3 license that can be found through the following link. + * 此源代码的使用受 GNU AFFERO GENERAL PUBLIC LICENSE version 3 许可证的约束, 可以在以下链接找到该许可证. + * Use of this source code is governed by the GNU AGPLv3 license that can be found through the following link. * - * https://github.com/mamoe/mirai/blob/master/LICENSE + * https://github.com/mamoe/mirai/blob/dev/LICENSE */ package net.mamoe.mirai.internal.network.components @@ -19,10 +19,8 @@ import net.mamoe.mirai.internal.network.ProtoBufForCache import net.mamoe.mirai.internal.network.component.ComponentKey import net.mamoe.mirai.internal.network.component.ComponentStorage import net.mamoe.mirai.internal.utils.actualCacheDir -import net.mamoe.mirai.utils.BotConfiguration -import net.mamoe.mirai.utils.MiraiLogger -import java.io.File -import java.util.concurrent.CopyOnWriteArraySet +import net.mamoe.mirai.utils.* +import kotlin.jvm.Volatile internal interface BdhSessionSyncer { val bdhSession: CompletableDeferred @@ -45,8 +43,8 @@ internal interface BdhSessionSyncer { internal class BdhSession( val sigSession: ByteArray, val sessionKey: ByteArray, - var ssoAddresses: MutableSet> = CopyOnWriteArraySet(), - var otherAddresses: MutableSet> = CopyOnWriteArraySet(), + var ssoAddresses: MutableSet> = ConcurrentSet(), + var otherAddresses: MutableSet> = ConcurrentSet(), ) private val ServerListSerializer: KSerializer> = @@ -74,9 +72,9 @@ internal class BdhSessionSyncerImpl( } } - private val sessionCacheFile: File + private val sessionCacheFile: MiraiFile get() = configuration.actualCacheDir().resolve("session.bin") - private val serverListCacheFile: File + private val serverListCacheFile: MiraiFile get() = configuration.actualCacheDir().resolve("servers.json") override fun loadServerListFromCache() { @@ -114,7 +112,7 @@ internal class BdhSessionSyncerImpl( override fun saveServerListToCache() { val serverListCacheFile = this.serverListCacheFile - serverListCacheFile.parentFile?.mkdirs() + serverListCacheFile.parent?.mkdirs() logger.verbose("Saving server list to cache") kotlin.runCatching { @@ -131,7 +129,7 @@ internal class BdhSessionSyncerImpl( override fun saveToCache() { val sessionCacheFile = this.sessionCacheFile - sessionCacheFile.parentFile?.mkdirs() + sessionCacheFile.parent?.mkdirs() if (bdhSession.isCompleted) { logger.verbose("Saving bdh session to cache") kotlin.runCatching { diff --git a/mirai-core/src/commonMain/kotlin/network/components/BotOfflineEventMonitor.kt b/mirai-core/src/commonMain/kotlin/network/components/BotOfflineEventMonitor.kt index c140876d0..21fadc0d6 100644 --- a/mirai-core/src/commonMain/kotlin/network/components/BotOfflineEventMonitor.kt +++ b/mirai-core/src/commonMain/kotlin/network/components/BotOfflineEventMonitor.kt @@ -20,11 +20,7 @@ import net.mamoe.mirai.internal.network.component.ComponentKey import net.mamoe.mirai.internal.network.handler.NetworkHandler import net.mamoe.mirai.internal.network.handler.NetworkHandler.State import net.mamoe.mirai.internal.network.handler.selector.NetworkException -import net.mamoe.mirai.utils.castOrNull -import net.mamoe.mirai.utils.info -import net.mamoe.mirai.utils.millisToHumanReadableString -import net.mamoe.mirai.utils.warning -import kotlin.system.measureTimeMillis +import net.mamoe.mirai.utils.* /** * Handles [BotOfflineEvent] @@ -96,7 +92,7 @@ internal class BotOfflineEventMonitorImpl : BotOfflineEventMonitor { // Run this coroutine in EventDispatcher, so joinBroadcast will work. // EventDispatcher is in Bot's components level so won't be closed by network. bot.components[EventDispatcher].broadcastAsync { - val success: Boolean + var success = false val time = measureTimeMillis { success = kotlin.runCatching { bot.network.resumeConnection() diff --git a/mirai-core/src/commonMain/kotlin/network/components/ContactUpdater.kt b/mirai-core/src/commonMain/kotlin/network/components/ContactUpdater.kt index 47514a260..4dae96871 100644 --- a/mirai-core/src/commonMain/kotlin/network/components/ContactUpdater.kt +++ b/mirai-core/src/commonMain/kotlin/network/components/ContactUpdater.kt @@ -42,6 +42,8 @@ import net.mamoe.mirai.utils.MiraiLogger import net.mamoe.mirai.utils.info import net.mamoe.mirai.utils.retryCatching import net.mamoe.mirai.utils.verbose +import kotlin.jvm.Synchronized +import kotlin.jvm.Volatile /** * Manager of caches for [Contact]s. diff --git a/mirai-core/src/commonMain/kotlin/network/components/EventDispatcher.kt b/mirai-core/src/commonMain/kotlin/network/components/EventDispatcher.kt index e48c91185..532e3bd86 100644 --- a/mirai-core/src/commonMain/kotlin/network/components/EventDispatcher.kt +++ b/mirai-core/src/commonMain/kotlin/network/components/EventDispatcher.kt @@ -19,6 +19,7 @@ import net.mamoe.mirai.internal.network.component.ComponentKey import net.mamoe.mirai.utils.* import kotlin.coroutines.CoroutineContext import kotlin.coroutines.EmptyCoroutineContext +import kotlin.jvm.JvmInline /** * All events will be caught and forwarded to [EventDispatcher]. Invocation of [Event.broadcast] and [EventDispatcher.broadcast] are effectively equal. @@ -128,7 +129,7 @@ internal open class EventDispatcherImpl( } protected fun optimizeEventToString(event: Event): String { - val qualified = event::class.java.canonicalName ?: return event.toString() + val qualified = event::class.qualifiedName ?: return event.toString() return qualified.substringAfter("net.mamoe.mirai.event.events.", "").ifEmpty { event.toString() } } diff --git a/mirai-core/src/commonMain/kotlin/network/components/HeartbeatScheduler.kt b/mirai-core/src/commonMain/kotlin/network/components/HeartbeatScheduler.kt index 8cc5398a5..c1d68e185 100644 --- a/mirai-core/src/commonMain/kotlin/network/components/HeartbeatScheduler.kt +++ b/mirai-core/src/commonMain/kotlin/network/components/HeartbeatScheduler.kt @@ -52,7 +52,7 @@ internal class TimeBasedHeartbeatSchedulerImpl( val timeout = configuration.heartbeatTimeoutMillis val list = mutableListOf() - when (context[SsoProcessorContext].configuration.heartbeatStrategy) { + when (val hb = context[SsoProcessorContext].configuration.heartbeatStrategy) { STAT_HB -> { list += launchHeartbeatJobAsync( scope = scope, @@ -75,6 +75,7 @@ internal class TimeBasedHeartbeatSchedulerImpl( } NONE -> { } + else -> throw IllegalStateException("Unexpected HeartbeatStrategy: $hb") } list += launchHeartbeatJobAsync( diff --git a/mirai-core/src/commonMain/kotlin/network/components/KeyRefreshProcessor.kt b/mirai-core/src/commonMain/kotlin/network/components/KeyRefreshProcessor.kt index 09166176f..ed9b5eee2 100644 --- a/mirai-core/src/commonMain/kotlin/network/components/KeyRefreshProcessor.kt +++ b/mirai-core/src/commonMain/kotlin/network/components/KeyRefreshProcessor.kt @@ -18,11 +18,12 @@ import net.mamoe.mirai.utils.MiraiLogger import net.mamoe.mirai.utils.info import net.mamoe.mirai.utils.millisToHumanReadableString import net.mamoe.mirai.utils.minutesToMillis +import kotlin.coroutines.cancellation.CancellationException internal interface KeyRefreshProcessor { suspend fun keyRefreshLoop(handler: NetworkHandler) - @Throws(LoginFailedException::class) + @Throws(LoginFailedException::class, CancellationException::class) suspend fun refreshKeysNow(handler: NetworkHandler) companion object : ComponentKey diff --git a/mirai-core/src/commonMain/kotlin/network/components/MessageSvcSyncer.kt b/mirai-core/src/commonMain/kotlin/network/components/MessageSvcSyncer.kt index c9094c051..9c3487bd8 100644 --- a/mirai-core/src/commonMain/kotlin/network/components/MessageSvcSyncer.kt +++ b/mirai-core/src/commonMain/kotlin/network/components/MessageSvcSyncer.kt @@ -23,6 +23,7 @@ import net.mamoe.mirai.utils.addNameHierarchically import net.mamoe.mirai.utils.childScope import net.mamoe.mirai.utils.info import kotlin.coroutines.CoroutineContext +import kotlin.jvm.Volatile internal interface MessageSvcSyncer { suspend fun startSync() diff --git a/mirai-core/src/commonMain/kotlin/network/components/NoticeProcessorPipeline.kt b/mirai-core/src/commonMain/kotlin/network/components/NoticeProcessorPipeline.kt index 4bc9ec14e..0c13bf1be 100644 --- a/mirai-core/src/commonMain/kotlin/network/components/NoticeProcessorPipeline.kt +++ b/mirai-core/src/commonMain/kotlin/network/components/NoticeProcessorPipeline.kt @@ -31,6 +31,7 @@ import net.mamoe.mirai.internal.network.toPacket import net.mamoe.mirai.internal.pipeline.* import net.mamoe.mirai.internal.utils.io.ProtocolStruct import net.mamoe.mirai.utils.* +import kotlin.jvm.JvmStatic import kotlin.reflect.KClass /** diff --git a/mirai-core/src/commonMain/kotlin/network/components/PacketCodec.kt b/mirai-core/src/commonMain/kotlin/network/components/PacketCodec.kt index fecb34bed..58c3807f0 100644 --- a/mirai-core/src/commonMain/kotlin/network/components/PacketCodec.kt +++ b/mirai-core/src/commonMain/kotlin/network/components/PacketCodec.kt @@ -88,9 +88,10 @@ internal class PacketCodecException( OTHER, } - override fun getStackTrace(): Array { - return targetException.stackTrace - } + // not available in native +// override fun getStackTrace(): Array { +// return targetException.stackTrace +// } } internal class PacketCodecImpl : PacketCodec { @@ -222,7 +223,7 @@ internal class PacketCodecImpl : PacketCodec { 1 -> { input.discardExact(4) input.useBytes { data, length -> - data.unzip(0, length).let { + data.inflate(0, length).let { val size = it.toInt() if (size == it.size || size == it.size + 4) { it.toReadPacket(offset = 4) diff --git a/mirai-core/src/commonMain/kotlin/network/components/PacketLoggingStrategy.kt b/mirai-core/src/commonMain/kotlin/network/components/PacketLoggingStrategy.kt index 0bfc637ad..b8625d5c3 100644 --- a/mirai-core/src/commonMain/kotlin/network/components/PacketLoggingStrategy.kt +++ b/mirai-core/src/commonMain/kotlin/network/components/PacketLoggingStrategy.kt @@ -1,10 +1,10 @@ /* - * Copyright 2019-2021 Mamoe Technologies and contributors. + * Copyright 2019-2022 Mamoe Technologies and contributors. * - * 此源代码的使用受 GNU AFFERO GENERAL PUBLIC LICENSE version 3 许可证的约束, 可以在以下链接找到该许可证. - * Use of this source code is governed by the GNU AGPLv3 license that can be found through the following link. + * 此源代码的使用受 GNU AFFERO GENERAL PUBLIC LICENSE version 3 许可证的约束, 可以在以下链接找到该许可证. + * Use of this source code is governed by the GNU AGPLv3 license that can be found through the following link. * - * https://github.com/mamoe/mirai/blob/master/LICENSE + * https://github.com/mamoe/mirai/blob/dev/LICENSE */ package net.mamoe.mirai.internal.network.components @@ -24,6 +24,7 @@ import net.mamoe.mirai.utils.MiraiLogger import net.mamoe.mirai.utils.systemProp import net.mamoe.mirai.utils.verbose import kotlin.coroutines.cancellation.CancellationException +import kotlin.native.concurrent.SharedImmutable /** * Implementation must be fast and non-blocking, throwing no exception. @@ -112,7 +113,9 @@ internal class PacketLoggingStrategyImpl( ) } - @JvmField - var SHOW_PACKET_DETAILS = systemProp("mirai.network.show.packet.details", false) } } + + +@SharedImmutable +private val SHOW_PACKET_DETAILS = systemProp("mirai.network.show.packet.details", false) \ No newline at end of file diff --git a/mirai-core/src/commonMain/kotlin/network/components/ServerList.kt b/mirai-core/src/commonMain/kotlin/network/components/ServerList.kt index 12cf56a58..0d41f8260 100644 --- a/mirai-core/src/commonMain/kotlin/network/components/ServerList.kt +++ b/mirai-core/src/commonMain/kotlin/network/components/ServerList.kt @@ -1,10 +1,10 @@ /* - * Copyright 2019-2021 Mamoe Technologies and contributors. + * Copyright 2019-2022 Mamoe Technologies and contributors. * - * 此源代码的使用受 GNU AFFERO GENERAL PUBLIC LICENSE version 3 许可证的约束, 可以在以下链接找到该许可证. - * Use of this source code is governed by the GNU AGPLv3 license that can be found through the following link. + * 此源代码的使用受 GNU AFFERO GENERAL PUBLIC LICENSE version 3 许可证的约束, 可以在以下链接找到该许可证. + * Use of this source code is governed by the GNU AGPLv3 license that can be found through the following link. * - * https://github.com/mamoe/mirai/blob/master/LICENSE + * https://github.com/mamoe/mirai/blob/dev/LICENSE */ package net.mamoe.mirai.internal.network.components @@ -12,11 +12,12 @@ package net.mamoe.mirai.internal.network.components import kotlinx.serialization.Serializable import net.mamoe.mirai.internal.network.component.ComponentKey import net.mamoe.mirai.internal.network.components.ServerList.Companion.DEFAULT_SERVER_LIST +import net.mamoe.mirai.internal.network.handler.SocketAddress import net.mamoe.mirai.utils.MiraiLogger import net.mamoe.mirai.utils.TestOnly import net.mamoe.mirai.utils.info -import java.net.InetSocketAddress -import java.util.* +import kotlin.jvm.Synchronized +import kotlin.jvm.Volatile @Serializable internal data class ServerAddress( @@ -32,7 +33,7 @@ internal data class ServerAddress( return "$host:$port" } - fun toSocketAddress(): InetSocketAddress = InetSocketAddress.createUnresolved(host, port) + fun toSocketAddress(): SocketAddress = SocketAddress(host, port) } /** @@ -111,7 +112,7 @@ internal class ServerListImpl( private var preferred: Set = DEFAULT_SERVER_LIST @Volatile - private var current: Queue = ArrayDeque(initial) + private var current: ArrayDeque = ArrayDeque(initial) @Volatile private var lastPolledAddress: ServerAddress? = null @@ -147,7 +148,7 @@ internal class ServerListImpl( */ @Synchronized override fun pollCurrent(): ServerAddress? { - return current.poll()?.also { address -> + return current.removeFirstOrNull()?.also { address -> lastPolledAddress = address } } @@ -158,7 +159,7 @@ internal class ServerListImpl( @Synchronized override fun pollAny(): ServerAddress { if (current.isEmpty()) refresh() - return current.remove().also { address -> + return current.removeFirst().also { address -> lastPolledAddress = address } } diff --git a/mirai-core/src/commonMain/kotlin/network/components/SsoProcessor.kt b/mirai-core/src/commonMain/kotlin/network/components/SsoProcessor.kt index 9ae6b18f1..047184cdd 100644 --- a/mirai-core/src/commonMain/kotlin/network/components/SsoProcessor.kt +++ b/mirai-core/src/commonMain/kotlin/network/components/SsoProcessor.kt @@ -17,7 +17,6 @@ import net.mamoe.mirai.internal.network.QQAndroidClient import net.mamoe.mirai.internal.network.WLoginSigInfo import net.mamoe.mirai.internal.network.component.ComponentKey import net.mamoe.mirai.internal.network.handler.NetworkHandler -import net.mamoe.mirai.internal.network.impl.netty.NettyNetworkHandler import net.mamoe.mirai.internal.network.protocol.packet.OutgoingPacketWithRespType import net.mamoe.mirai.internal.network.protocol.packet.login.StatSvc import net.mamoe.mirai.internal.network.protocol.packet.login.WtLogin.Login.LoginPacketResponse @@ -31,6 +30,8 @@ import net.mamoe.mirai.utils.BotConfiguration.MiraiProtocol import net.mamoe.mirai.utils.LoginSolver import net.mamoe.mirai.utils.info import net.mamoe.mirai.utils.withExceptionCollector +import kotlin.coroutines.cancellation.CancellationException +import kotlin.jvm.Volatile /** * Handles login, and acts also as a mediator of [BotInitProcessor] @@ -46,7 +47,7 @@ internal interface SsoProcessor { /** * Do login. Throws [LoginFailedException] if failed */ - @Throws(LoginFailedException::class) + @Throws(LoginFailedException::class, CancellationException::class) suspend fun login(handler: NetworkHandler) suspend fun logout(handler: NetworkHandler) @@ -88,7 +89,7 @@ internal interface SsoSession { * * And allows to retire the [session][ssoSession] after success. * - * Used by [NettyNetworkHandler.StateConnecting]. + * Used by `NettyNetworkHandler.StateConnecting`. */ internal class SsoProcessorImpl( val ssoContext: SsoProcessorContext, @@ -115,7 +116,6 @@ internal class SsoProcessorImpl( /** * Do login. Throws [LoginFailedException] if failed */ - @Throws(LoginFailedException::class) override suspend fun login(handler: NetworkHandler) = withExceptionCollector { components[BdhSessionSyncer].loadServerListFromCache() try { @@ -278,7 +278,7 @@ internal class SsoProcessorImpl( is LoginPacketResponse.Error -> { if (response.message.contains("0x9a")) { //Error(title=登录失败, message=请你稍后重试。(0x9a), errorInfo=) - collectThrow(RetryLaterException().initCause(IllegalStateException("Login failed: $response"))) + collectThrow(RetryLaterException(IllegalStateException("Login failed: $response"))) } val msg = response.toString() collectThrow(WrongPasswordException(buildString(capacity = msg.length) { diff --git a/mirai-core/src/commonMain/kotlin/network/components/SyncController.kt b/mirai-core/src/commonMain/kotlin/network/components/SyncController.kt index 25581d2a5..fb1e3c2c1 100644 --- a/mirai-core/src/commonMain/kotlin/network/components/SyncController.kt +++ b/mirai-core/src/commonMain/kotlin/network/components/SyncController.kt @@ -1,10 +1,10 @@ /* - * Copyright 2019-2021 Mamoe Technologies and contributors. + * Copyright 2019-2022 Mamoe Technologies and contributors. * - * 此源代码的使用受 GNU AFFERO GENERAL PUBLIC LICENSE version 3 许可证的约束, 可以在以下链接找到该许可证. - * Use of this source code is governed by the GNU AGPLv3 license that can be found through the following link. + * 此源代码的使用受 GNU AFFERO GENERAL PUBLIC LICENSE version 3 许可证的约束, 可以在以下链接找到该许可证. + * Use of this source code is governed by the GNU AGPLv3 license that can be found through the following link. * - * https://github.com/mamoe/mirai/blob/master/LICENSE + * https://github.com/mamoe/mirai/blob/dev/LICENSE */ package net.mamoe.mirai.internal.network.components @@ -20,6 +20,7 @@ import net.mamoe.mirai.internal.network.protocol.data.proto.MsgComm import net.mamoe.mirai.internal.network.protocol.data.proto.OnlinePushTrans import net.mamoe.mirai.utils.EMPTY_BYTE_ARRAY import net.mamoe.mirai.utils.currentTimeSeconds +import kotlin.jvm.Volatile internal interface SyncController { val firstNotify: AtomicBoolean diff --git a/mirai-core/src/commonMain/kotlin/network/handler/CommonNetworkHandler.kt b/mirai-core/src/commonMain/kotlin/network/handler/CommonNetworkHandler.kt new file mode 100644 index 000000000..444a16d20 --- /dev/null +++ b/mirai-core/src/commonMain/kotlin/network/handler/CommonNetworkHandler.kt @@ -0,0 +1,333 @@ +/* + * Copyright 2019-2022 Mamoe Technologies and contributors. + * + * 此源代码的使用受 GNU AFFERO GENERAL PUBLIC LICENSE version 3 许可证的约束, 可以在以下链接找到该许可证. + * Use of this source code is governed by the GNU AGPLv3 license that can be found through the following link. + * + * https://github.com/mamoe/mirai/blob/dev/LICENSE + */ + +package net.mamoe.mirai.internal.network.handler + +import io.ktor.utils.io.core.* +import kotlinx.coroutines.* +import net.mamoe.mirai.internal.network.components.* +import net.mamoe.mirai.internal.network.handler.selector.NetworkException +import net.mamoe.mirai.internal.network.handler.selector.NetworkHandlerSelector +import net.mamoe.mirai.internal.network.handler.state.StateObserver +import net.mamoe.mirai.internal.network.impl.HeartbeatFailedException +import net.mamoe.mirai.internal.network.protocol.packet.OutgoingPacket +import net.mamoe.mirai.utils.* +import kotlin.coroutines.CoroutineContext + +/** + * Implements protocol-specific logic based on [NetworkHandlerSupport]. This can be shared between tests and + */ +internal abstract class CommonNetworkHandler( + context: NetworkHandlerContext, + protected val address: SocketAddress, +) : NetworkHandlerSupport(context) { + final override tailrec suspend fun sendPacketImpl(packet: OutgoingPacket) { + val state = _state as CommonNetworkHandler<*>.CommonState + if (state.sendPacketImpl(packet)) return + + // now the state it not yet ready for sending packet ... + stateChannel.receive() // [SUSPENSION POINT] so we wait for next state ... + return sendPacketImpl(packet) // and try again. + } + + override fun toString(): String { + return "CommonNetworkHandler(context=$context, address=$address)" + } + + + /////////////////////////////////////////////////////////////////////////// + // exception handling + /////////////////////////////////////////////////////////////////////////// + + protected open fun handleExceptionInDecoding(error: Throwable) { + fun passToExceptionHandler() { + // Typically, just log the exception + coroutineContext[CoroutineExceptionHandler]!!.handleException( + coroutineContext, + ExceptionInPacketCodecException(error.unwrap()) + ) + } + + if (error is PacketCodecException) { + if (error.targetException is EOFException) return + when (error.kind) { + PacketCodecException.Kind.SESSION_EXPIRED -> { + setState { StateClosed(error) } + return + } + PacketCodecException.Kind.PROTOCOL_UPDATED -> passToExceptionHandler() + PacketCodecException.Kind.OTHER -> passToExceptionHandler() + } + } + + passToExceptionHandler() + } + + /////////////////////////////////////////////////////////////////////////// + // conn. + /////////////////////////////////////////////////////////////////////////// + + /** + * Creates a connection + */ + protected abstract suspend fun createConnection(): Conn + + /** + * Writes and flushes the packet asynchronously. + */ + protected abstract fun Conn.writeAndFlushOrCloseAsync(packet: OutgoingPacket) + + @Suppress("EXTENSION_SHADOWED_BY_MEMBER") + protected abstract fun Conn.close() + + internal inner class PacketDecodePipeline(parentContext: CoroutineContext) : + CoroutineScope by parentContext.childScope() { + private val packetCodec: PacketCodec by lazy { context[PacketCodec] } + + fun send(raw: RawIncomingPacket) { + launch { + packetLogger.debug { "Packet Handling Processor: receive packet ${raw.commandName}" } + val result = packetCodec.processBody(context.bot, raw) + if (result == null) { + collectUnknownPacket(raw) + } else collectReceived(result) + } + } + } + + + /////////////////////////////////////////////////////////////////////////// + // states + /////////////////////////////////////////////////////////////////////////// + + override fun close(cause: Throwable?) { + if (state == NetworkHandler.State.CLOSED) return // quick check if already closed + if (setState { StateClosed(cause) } == null) return // atomic check + super.close(cause) // cancel coroutine scope + } + + init { + coroutineContext.job.invokeOnCompletion { e -> + close(e?.unwrapCancellationException()) + } + } + + /** + * When state is initialized, it must be set to [_state]. (inside [setState]) + * + * For what jobs each state will do, it is not solely decided by the state itself. [StateObserver]s may also launch jobs into the scope. + * + * @see StateObserver + */ + protected abstract inner class CommonState( + correspondingState: NetworkHandler.State, + ) : NetworkHandlerSupport.BaseStateImpl(correspondingState) { + /** + * @return `true` if packet has been sent, `false` if state is not ready for send. + * @throws IllegalStateException if is [StateClosed]. + */ + abstract suspend fun sendPacketImpl(packet: OutgoingPacket): Boolean + } + + protected inner class StateInitialized : CommonState(NetworkHandler.State.INITIALIZED) { + override suspend fun sendPacketImpl(packet: OutgoingPacket): Boolean { + // error("Cannot send packet when connection is not set. (resumeConnection not called.)") + return false + } + + override suspend fun resumeConnection0() { + this.setState { StateConnecting(ExceptionCollector()) } + ?.resumeConnection() + ?: this@CommonNetworkHandler.resumeConnection() // concurrently closed by other thread. + + println("INITIALIZED RETURN") + } + + override fun toString(): String = "StateInitialized" + } + + /** + * 1. Connect to server. + * 2. Perform SSO login with [SsoProcessor] + * + * If failure, set state to [StateClosed] + * If success, set state to [StateOK] + */ + protected inner class StateConnecting( + /** + * Collected (suppressed) exceptions that have led this state. + * + * Dropped when state becomes [StateOK]. + */ + private val collectiveExceptions: ExceptionCollector, + ) : CommonState(NetworkHandler.State.CONNECTING) { + private lateinit var connection: Deferred + + @Suppress("JoinDeclarationAndAssignment") + private lateinit var connectResult: Deferred + + override fun startState() { + connection = async { + createConnection() + } + + connectResult = async { + connection.join() + context[SsoProcessor].login(this@CommonNetworkHandler) + } + connectResult.invokeOnCompletion { error -> + if (error == null) { + this@CommonNetworkHandler.launch { resumeConnection() } + } else { + // failed in SSO stage + context[SsoProcessor].firstLoginResult.compareAndSet(null, FirstLoginResult.OTHER_FAILURE) + + if (error is StateSwitchingException && error.new is CommonNetworkHandler<*>.StateConnecting) { + return@invokeOnCompletion // state already switched, so do not do it again. + } + setState { + // logon failure closes the network handler. + StateClosed(collectiveExceptions.collectGet(error)) + // The exception will be ignored unless all further attempts recovering connection have failed. + // This is to reduce useless logs for the user----there is nothing to worry about if we can recover the connection. + } + } + } + + } + + override fun getCause(): Throwable? = collectiveExceptions.getLast() + + override suspend fun sendPacketImpl(packet: OutgoingPacket): Boolean = runUnwrapCancellationException { + connection.await() // split line number + .writeAndFlushOrCloseAsync(packet) + return true + } + + override suspend fun resumeConnection0() = runUnwrapCancellationException { + connectResult.await() // propagates exceptions + val connection = connection.await() + this.setState { StateLoading(connection) } + .also { + println(" this.setState { StateLoading(connection) }: " + it) + } + ?.resumeConnection() + ?: this@CommonNetworkHandler.resumeConnection() // concurrently closed by other thread. + } + + override fun toString(): String = "StateConnecting" + } + + /** + * @see BotInitProcessor + * @see StateObserver + */ + protected inner class StateLoading( + private val connection: Conn, + ) : CommonState(NetworkHandler.State.LOADING) { + + override fun startState() { + coroutineContext.job.invokeOnCompletion { + if (it != null) { + connection.close() + } + } + } + + override suspend fun sendPacketImpl(packet: OutgoingPacket): Boolean { + connection.writeAndFlushOrCloseAsync(packet) + return true + } + + private val configPush = this@CommonNetworkHandler.launch(CoroutineName("ConfigPush sync")) { + context[ConfigPushProcessor].syncConfigPush(this@CommonNetworkHandler) + } + + override suspend fun resumeConnection0(): Unit = runUnwrapCancellationException { + (coroutineContext.job as CompletableJob).run { + complete() + join() + } + joinCompleted(configPush) // throw exception + setState { StateOK(connection, configPush) } + } // noop + + override fun toString(): String = "StateLoading" + } + + protected inner class StateOK( + private val connection: Conn, + private val configPush: Job, + ) : CommonState(NetworkHandler.State.OK) { + override fun startState() { + coroutineContext.job.invokeOnCompletion { err -> + if (err is StateSwitchingException) { + if (err.new.correspondingState == NetworkHandler.State.CLOSED) { + return@invokeOnCompletion + } + } + connection.close() + } + } + + private val heartbeatJobs = + context[HeartbeatScheduler].launchJobsIn(this@CommonNetworkHandler, this) { name, e -> + setState { StateClosed(HeartbeatFailedException(name, e)) } + } + + // we can also move them as observers if needed. + + private val keyRefresh = launch(CoroutineName("Key refresh")) { + context[KeyRefreshProcessor].keyRefreshLoop(this@CommonNetworkHandler) + } + + override suspend fun sendPacketImpl(packet: OutgoingPacket): Boolean { + connection.writeAndFlushOrCloseAsync(packet) + return true + } + + override suspend fun resumeConnection0(): Unit = runUnwrapCancellationException { + joinCompleted(coroutineContext.job) + for (job in heartbeatJobs) joinCompleted(job) + joinCompleted(configPush) + joinCompleted(keyRefresh) + } // noop + + override fun toString(): String = "StateOK" + } + + /** + * 这会永久关闭这个 [NetworkHandler], 但通常 bot 会使用 [NetworkHandlerSelector], selector 会创建新的 [NetworkHandler] 来恢复连接. + * + * 备注: selector 会恢复连接, 当且仅当 [exception] 类型是 [NetworkException] 且 [NetworkException.recoverable] 为 `true`. + */ + protected inner class StateClosed( + val exception: Throwable?, + ) : CommonState(NetworkHandler.State.CLOSED) { + + override fun afterUpdated() { + close(exception) + } + + override fun getCause(): Throwable? = exception + override suspend fun sendPacketImpl(packet: OutgoingPacket) = error("NetworkHandler is already closed.") + override suspend fun resumeConnection0() { + exception?.let { throw it } + } // noop + + override fun toString(): String = "StateClosed" + } + + override fun initialState(): NetworkHandlerSupport.BaseStateImpl = StateInitialized() + +} + +internal suspend inline fun joinCompleted(job: Job) { + if (job.isCompleted) job.join() +} diff --git a/mirai-core/src/commonMain/kotlin/network/handler/NetworkHandler.kt b/mirai-core/src/commonMain/kotlin/network/handler/NetworkHandler.kt index 590dbf2fb..e1e347e92 100644 --- a/mirai-core/src/commonMain/kotlin/network/handler/NetworkHandler.kt +++ b/mirai-core/src/commonMain/kotlin/network/handler/NetworkHandler.kt @@ -26,6 +26,8 @@ import net.mamoe.mirai.internal.network.handler.state.StateObserver import net.mamoe.mirai.internal.network.protocol.packet.OutgoingPacket import net.mamoe.mirai.internal.network.protocol.packet.OutgoingPacketWithRespType import net.mamoe.mirai.utils.MiraiLogger +import net.mamoe.mirai.utils.uncheckedCast +import kotlin.jvm.JvmName /** * Coroutine-based network framework. Usually wrapped with [SelectorNetworkHandler] to enable retrying. diff --git a/mirai-core/src/commonMain/kotlin/network/handler/NetworkHandlerContext.kt b/mirai-core/src/commonMain/kotlin/network/handler/NetworkHandlerContext.kt index 3e49e4729..90e34d888 100644 --- a/mirai-core/src/commonMain/kotlin/network/handler/NetworkHandlerContext.kt +++ b/mirai-core/src/commonMain/kotlin/network/handler/NetworkHandlerContext.kt @@ -1,17 +1,20 @@ /* - * Copyright 2019-2021 Mamoe Technologies and contributors. + * Copyright 2019-2022 Mamoe Technologies and contributors. * - * 此源代码的使用受 GNU AFFERO GENERAL PUBLIC LICENSE version 3 许可证的约束, 可以在以下链接找到该许可证. - * Use of this source code is governed by the GNU AGPLv3 license that can be found through the following link. + * 此源代码的使用受 GNU AFFERO GENERAL PUBLIC LICENSE version 3 许可证的约束, 可以在以下链接找到该许可证. + * Use of this source code is governed by the GNU AGPLv3 license that can be found through the following link. * - * https://github.com/mamoe/mirai/blob/master/LICENSE + * https://github.com/mamoe/mirai/blob/dev/LICENSE */ package net.mamoe.mirai.internal.network.handler +import kotlinx.coroutines.CoroutineExceptionHandler +import kotlinx.coroutines.CoroutineName import net.mamoe.mirai.internal.QQAndroidBot import net.mamoe.mirai.internal.network.component.ComponentStorage import net.mamoe.mirai.utils.MiraiLogger +import net.mamoe.mirai.utils.SimpleLogger /** * Immutable context for [NetworkHandler] @@ -37,3 +40,15 @@ internal class NetworkHandlerContextImpl( return "NetworkHandlerContextImpl(bot=${bot.id}, storage=$storage)" } } + +internal fun MiraiLogger.asCoroutineExceptionHandler( + priority: SimpleLogger.LogPriority = SimpleLogger.LogPriority.ERROR, +): CoroutineExceptionHandler { + return CoroutineExceptionHandler { context, e -> + call( + priority, + context[CoroutineName]?.let { "Exception in coroutine '${it.name}'." } ?: "Exception in unnamed coroutine.", + e + ) + } +} diff --git a/mirai-core/src/commonMain/kotlin/network/handler/NetworkHandlerFactory.kt b/mirai-core/src/commonMain/kotlin/network/handler/NetworkHandlerFactory.kt index 09142f90a..9fa66d317 100644 --- a/mirai-core/src/commonMain/kotlin/network/handler/NetworkHandlerFactory.kt +++ b/mirai-core/src/commonMain/kotlin/network/handler/NetworkHandlerFactory.kt @@ -1,31 +1,35 @@ /* - * Copyright 2019-2021 Mamoe Technologies and contributors. + * Copyright 2019-2022 Mamoe Technologies and contributors. * - * 此源代码的使用受 GNU AFFERO GENERAL PUBLIC LICENSE version 3 许可证的约束, 可以在以下链接找到该许可证. - * Use of this source code is governed by the GNU AGPLv3 license that can be found through the following link. + * 此源代码的使用受 GNU AFFERO GENERAL PUBLIC LICENSE version 3 许可证的约束, 可以在以下链接找到该许可证. + * Use of this source code is governed by the GNU AGPLv3 license that can be found through the following link. * - * https://github.com/mamoe/mirai/blob/master/LICENSE + * https://github.com/mamoe/mirai/blob/dev/LICENSE */ package net.mamoe.mirai.internal.network.handler import net.mamoe.mirai.internal.network.handler.NetworkHandler.State -import java.net.InetAddress -import java.net.InetSocketAddress -import java.net.SocketAddress /** * Factory for a specific [NetworkHandler] implementation. */ -internal fun interface NetworkHandlerFactory { - fun create(context: NetworkHandlerContext, host: String, port: Int): H = - create(context, InetSocketAddress.createUnresolved(host, port)) - - fun create(context: NetworkHandlerContext, host: InetAddress, port: Int): H = - create(context, InetSocketAddress(host, port)) +internal expect fun interface NetworkHandlerFactory { + open fun create(context: NetworkHandlerContext, host: String, port: Int): H /** * Create an instance of [H]. The returning [H] has [NetworkHandler.state] of [State.INITIALIZED] */ fun create(context: NetworkHandlerContext, address: SocketAddress): H + + companion object { + fun getPlatformDefault(): NetworkHandlerFactory<*> + } } + +internal expect abstract class SocketAddress { + val host: String + val port: Int +} + +internal expect fun SocketAddress(host: String, port: Int): SocketAddress diff --git a/mirai-core/src/commonMain/kotlin/network/handler/NetworkHandlerSupport.kt b/mirai-core/src/commonMain/kotlin/network/handler/NetworkHandlerSupport.kt index 9fe6d6cdf..92eca0bb7 100644 --- a/mirai-core/src/commonMain/kotlin/network/handler/NetworkHandlerSupport.kt +++ b/mirai-core/src/commonMain/kotlin/network/handler/NetworkHandlerSupport.kt @@ -9,6 +9,8 @@ package net.mamoe.mirai.internal.network.handler +import kotlinx.atomicfu.locks.SynchronizedObject +import kotlinx.atomicfu.locks.synchronized import kotlinx.coroutines.* import kotlinx.coroutines.channels.Channel import kotlinx.coroutines.channels.ReceiveChannel @@ -27,7 +29,6 @@ import net.mamoe.mirai.internal.utils.fromMiraiLogger import net.mamoe.mirai.internal.utils.subLogger import net.mamoe.mirai.utils.* import net.mamoe.mirai.utils.Either.Companion.fold -import java.util.concurrent.ConcurrentLinkedQueue import kotlin.coroutines.CoroutineContext import kotlin.coroutines.EmptyCoroutineContext import kotlin.reflect.KClass @@ -148,7 +149,7 @@ internal abstract class NetworkHandlerSupport( this.commandName == packet.commandName && this.sequenceId == packet.sequenceId } - private val packetListeners = ConcurrentLinkedQueue() + private val packetListeners = ConcurrentLinkedDeque() /////////////////////////////////////////////////////////////////////////// // state impl @@ -267,7 +268,7 @@ internal abstract class NetworkHandlerSupport( } private val lock = SingleEntrantLock() - private val lockForSetStateWithOldInstance = Any() + private val lockForSetStateWithOldInstance = SynchronizedObject() /** * This can only be called by [setState] or in tests. diff --git a/mirai-core/src/commonMain/kotlin/network/handler/selector/AbstractKeepAliveNetworkHandlerSelector.kt b/mirai-core/src/commonMain/kotlin/network/handler/selector/AbstractKeepAliveNetworkHandlerSelector.kt index 46698240a..9c12fddd1 100644 --- a/mirai-core/src/commonMain/kotlin/network/handler/selector/AbstractKeepAliveNetworkHandlerSelector.kt +++ b/mirai-core/src/commonMain/kotlin/network/handler/selector/AbstractKeepAliveNetworkHandlerSelector.kt @@ -10,6 +10,8 @@ package net.mamoe.mirai.internal.network.handler.selector import kotlinx.atomicfu.atomic +import kotlinx.atomicfu.locks.SynchronizedObject +import kotlinx.atomicfu.locks.synchronized import kotlinx.coroutines.currentCoroutineContext import kotlinx.coroutines.delay import kotlinx.coroutines.isActive @@ -21,6 +23,8 @@ import net.mamoe.mirai.internal.network.handler.logger import net.mamoe.mirai.network.LoginFailedException import net.mamoe.mirai.network.RetryLaterException import net.mamoe.mirai.utils.* +import kotlin.jvm.JvmField +import kotlin.native.concurrent.ThreadLocal /** * A lazy stateful implementation of [NetworkHandlerSelector]. @@ -102,7 +106,8 @@ internal abstract class AbstractKeepAliveNetworkHandlerSelector( .addNameHierarchically("SelectorNetworkHandler") .childScope() } + private val lock = SynchronizedObject() protected suspend inline fun instance(): H { if (!scope.isActive) { @@ -87,7 +91,7 @@ internal open class SelectorNetworkHandler( selector.getCurrentInstanceOrNull()?.close(cause) return } - synchronized(scope) { + synchronized(lock) { if (scope.isActive) { lastCancellationCause = cause scope.cancel() diff --git a/mirai-core/src/commonMain/kotlin/network/highway/ChunkedFlowSession.kt b/mirai-core/src/commonMain/kotlin/network/highway/ChunkedFlowSession.kt index cc180671f..a35239413 100644 --- a/mirai-core/src/commonMain/kotlin/network/highway/ChunkedFlowSession.kt +++ b/mirai-core/src/commonMain/kotlin/network/highway/ChunkedFlowSession.kt @@ -10,18 +10,17 @@ package net.mamoe.mirai.internal.network.highway import io.ktor.utils.io.core.* +import kotlinx.atomicfu.atomic import kotlinx.coroutines.flow.Flow import kotlinx.coroutines.flow.flow import net.mamoe.mirai.utils.runBIO import net.mamoe.mirai.utils.toLongUnsigned import net.mamoe.mirai.utils.withUse -import java.io.InputStream -import java.util.concurrent.atomic.AtomicLong import kotlin.contracts.InvocationKind import kotlin.contracts.contract internal class ChunkedFlowSession( - private val input: InputStream, + private val input: Input, private val buffer: ByteArray, private val callback: Highway.ProgressionCallback? = null, private val mapper: (buffer: ByteArray, size: Int, offset: Long) -> T, @@ -30,16 +29,16 @@ internal class ChunkedFlowSession( input.close() } - private var offset = AtomicLong(0L) + private var offset = atomic(0L) internal suspend inline fun useAll(crossinline block: suspend (T) -> Unit) { contract { callsInPlace(block, InvocationKind.UNKNOWN) } withUse { while (true) { - val size = runBIO { input.read(buffer) } + val size = runBIO { input.readAvailable(buffer) } if (size == -1) return block(mapper(buffer, size, offset.getAndAdd(size.toLongUnsigned()))) - callback?.onProgression(offset.get()) + callback?.onProgression(offset.value) } } } diff --git a/mirai-core/src/commonMain/kotlin/network/highway/Highway.kt b/mirai-core/src/commonMain/kotlin/network/highway/Highway.kt index 58f3400fd..63140cd62 100644 --- a/mirai-core/src/commonMain/kotlin/network/highway/Highway.kt +++ b/mirai-core/src/commonMain/kotlin/network/highway/Highway.kt @@ -9,9 +9,8 @@ package net.mamoe.mirai.internal.network.highway -import io.ktor.utils.io.core.ByteReadPacket -import io.ktor.utils.io.core.buildPacket -import io.ktor.utils.io.core.writeFully +import io.ktor.utils.io.core.* +import kotlinx.atomicfu.atomic import kotlinx.coroutines.* import kotlinx.coroutines.channels.Channel import kotlinx.coroutines.channels.ReceiveChannel @@ -32,11 +31,10 @@ import net.mamoe.mirai.internal.utils.io.serialization.toByteArray import net.mamoe.mirai.internal.utils.retryWithServers import net.mamoe.mirai.internal.utils.sizeToString import net.mamoe.mirai.utils.* -import java.util.concurrent.atomic.AtomicReference import kotlin.contracts.InvocationKind import kotlin.contracts.contract +import kotlin.jvm.Volatile import kotlin.math.roundToInt -import kotlin.system.measureTimeMillis internal object Highway { @@ -383,9 +381,9 @@ internal fun highwayPacketSession( ByteArrayPool.checkBufferSize(sizePerPacket) // require(ticket.size == 128) { "bad uKey. Required size=128, got ${ticket.size}" } - val ticket = AtomicReference(initialTicket) + val ticket = atomic(initialTicket) - return ChunkedFlowSession(data.inputStream(), ByteArray(sizePerPacket), callback) { buffer, size, offset -> + return ChunkedFlowSession(data.input(), ByteArray(sizePerPacket), callback) { buffer, size, offset -> val head = CSDataHighwayHead.ReqDataHighwayHead( msgBasehead = CSDataHighwayHead.DataHighwayHead( version = 1, @@ -403,7 +401,7 @@ internal fun highwayPacketSession( datalength = size, dataoffset = offset, filesize = data.size, - serviceticket = ticket.get(), + serviceticket = ticket.value, md5 = buffer.md5(0, size), fileMd5 = fileMd5, flag = 0, diff --git a/mirai-core/src/commonMain/kotlin/network/highway/Http.kt b/mirai-core/src/commonMain/kotlin/network/highway/Http.kt index a0e2f2915..3e9f99bf1 100644 --- a/mirai-core/src/commonMain/kotlin/network/highway/Http.kt +++ b/mirai-core/src/commonMain/kotlin/network/highway/Http.kt @@ -15,12 +15,12 @@ import io.ktor.http.* import io.ktor.http.content.* import io.ktor.util.* import io.ktor.utils.io.* -import io.ktor.utils.io.jvm.javaio.* +import io.ktor.utils.io.core.* import net.mamoe.mirai.internal.network.protocol.packet.chat.voice.voiceCodec import net.mamoe.mirai.utils.ExternalResource +import net.mamoe.mirai.utils.copyTo import net.mamoe.mirai.utils.toUHexString import net.mamoe.mirai.utils.withUse -import java.io.InputStream /** @@ -32,7 +32,7 @@ internal fun ExternalResource.consumeAsWriteChannelContent(contentType: ContentT override val contentLength: Long = size override suspend fun writeTo(channel: ByteWriteChannel) { - inputStream().withUse { copyTo(channel) } + input().withUse { copyTo(channel) } } } } diff --git a/mirai-core/src/commonMain/kotlin/network/notice/decoders/MsgInfoDecoder.kt b/mirai-core/src/commonMain/kotlin/network/notice/decoders/MsgInfoDecoder.kt index e067fa88f..bc82b1c71 100644 --- a/mirai-core/src/commonMain/kotlin/network/notice/decoders/MsgInfoDecoder.kt +++ b/mirai-core/src/commonMain/kotlin/network/notice/decoders/MsgInfoDecoder.kt @@ -24,10 +24,7 @@ import net.mamoe.mirai.internal.network.protocol.data.jce.MsgType0x210 import net.mamoe.mirai.internal.network.protocol.data.jce.OnlinePushPack.SvcReqPushMsg import net.mamoe.mirai.internal.utils.io.ProtocolStruct import net.mamoe.mirai.internal.utils.io.serialization.loadAs -import net.mamoe.mirai.utils.MiraiLogger -import net.mamoe.mirai.utils.debug -import net.mamoe.mirai.utils.read -import net.mamoe.mirai.utils.toUHexString +import net.mamoe.mirai.utils.* /** * Decodes [SvcReqPushMsg] to [MsgInfo] then re-fire [MsgType0x210] or [MsgType0x2DC] @@ -87,9 +84,7 @@ internal data class MsgType0x2DC( ) : ProtocolStruct, BaseMsgType0x2DC { override fun equals(other: Any?): Boolean { if (this === other) return true - if (javaClass != other?.javaClass) return false - - other as MsgType0x2DC + if (!isSameType(this, other)) return false if (kind != other.kind) return false if (group != other.group) return false diff --git a/mirai-core/src/commonMain/kotlin/network/notice/group/GroupNotificationProcessor.kt b/mirai-core/src/commonMain/kotlin/network/notice/group/GroupNotificationProcessor.kt index ec0015c62..acb617b3f 100644 --- a/mirai-core/src/commonMain/kotlin/network/notice/group/GroupNotificationProcessor.kt +++ b/mirai-core/src/commonMain/kotlin/network/notice/group/GroupNotificationProcessor.kt @@ -31,6 +31,7 @@ import net.mamoe.mirai.internal.utils.io.serialization.loadAs import net.mamoe.mirai.internal.utils.parseToMessageDataList import net.mamoe.mirai.internal.utils.structureToString import net.mamoe.mirai.utils.* +import kotlin.jvm.JvmName internal class GroupNotificationProcessor( private val logger: MiraiLogger, diff --git a/mirai-core/src/commonMain/kotlin/network/protocol/LoginType.kt b/mirai-core/src/commonMain/kotlin/network/protocol/LoginType.kt index f94a6dc29..acb032b09 100644 --- a/mirai-core/src/commonMain/kotlin/network/protocol/LoginType.kt +++ b/mirai-core/src/commonMain/kotlin/network/protocol/LoginType.kt @@ -1,14 +1,16 @@ /* - * Copyright 2019-2021 Mamoe Technologies and contributors. + * Copyright 2019-2022 Mamoe Technologies and contributors. * - * 此源代码的使用受 GNU AFFERO GENERAL PUBLIC LICENSE version 3 许可证的约束, 可以在以下链接找到该许可证. - * Use of this source code is governed by the GNU AGPLv3 license that can be found through the following link. + * 此源代码的使用受 GNU AFFERO GENERAL PUBLIC LICENSE version 3 许可证的约束, 可以在以下链接找到该许可证. + * Use of this source code is governed by the GNU AGPLv3 license that can be found through the following link. * - * https://github.com/mamoe/mirai/blob/master/LICENSE + * https://github.com/mamoe/mirai/blob/dev/LICENSE */ package net.mamoe.mirai.internal.network.protocol +import kotlin.jvm.JvmInline + @JvmInline internal value class LoginType( val value: Int diff --git a/mirai-core/src/commonMain/kotlin/network/protocol/SyncingCacheList.kt b/mirai-core/src/commonMain/kotlin/network/protocol/SyncingCacheList.kt index e85076079..be49110fa 100644 --- a/mirai-core/src/commonMain/kotlin/network/protocol/SyncingCacheList.kt +++ b/mirai-core/src/commonMain/kotlin/network/protocol/SyncingCacheList.kt @@ -1,5 +1,5 @@ /* - * Copyright 2019-2021 Mamoe Technologies and contributors. + * Copyright 2019-2022 Mamoe Technologies and contributors. * * 此源代码的使用受 GNU AFFERO GENERAL PUBLIC LICENSE version 3 许可证的约束, 可以在以下链接找到该许可证. * Use of this source code is governed by the GNU AGPLv3 license that can be found through the following link. @@ -8,7 +8,8 @@ */ package net.mamoe.mirai.internal.network.protocol -import java.util.* +import net.mamoe.mirai.utils.LinkedList +import kotlin.jvm.Synchronized internal class SyncingCacheList(private val size: Int = 50) { private val packetIdList = LinkedList() @@ -16,7 +17,7 @@ internal class SyncingCacheList(private val size: Int = 50) { @Synchronized // faster than suspending Mutex fun addCache(element: E): Boolean { if (packetIdList.contains(element)) return false // duplicate - packetIdList.addLast(element) + packetIdList.add(element) if (packetIdList.size >= size) packetIdList.removeFirst() return true } diff --git a/mirai-core/src/commonMain/kotlin/network/protocol/data/jce/ConfigPush.kt b/mirai-core/src/commonMain/kotlin/network/protocol/data/jce/ConfigPush.kt index 01d04a26d..7f2bbc175 100644 --- a/mirai-core/src/commonMain/kotlin/network/protocol/data/jce/ConfigPush.kt +++ b/mirai-core/src/commonMain/kotlin/network/protocol/data/jce/ConfigPush.kt @@ -1,10 +1,10 @@ /* - * Copyright 2019-2021 Mamoe Technologies and contributors. + * Copyright 2019-2022 Mamoe Technologies and contributors. * - * 此源代码的使用受 GNU AFFERO GENERAL PUBLIC LICENSE version 3 许可证的约束, 可以在以下链接找到该许可证. - * Use of this source code is governed by the GNU AGPLv3 license that can be found through the following link. + * 此源代码的使用受 GNU AFFERO GENERAL PUBLIC LICENSE version 3 许可证的约束, 可以在以下链接找到该许可证. + * Use of this source code is governed by the GNU AGPLv3 license that can be found through the following link. * - * https://github.com/mamoe/mirai/blob/master/LICENSE + * https://github.com/mamoe/mirai/blob/dev/LICENSE */ package net.mamoe.mirai.internal.network.protocol.data.jce @@ -13,6 +13,7 @@ import kotlinx.serialization.Serializable import net.mamoe.mirai.internal.network.Packet import net.mamoe.mirai.internal.utils.io.JceStruct import net.mamoe.mirai.internal.utils.io.serialization.tars.TarsId +import kotlin.jvm.JvmField @Serializable internal class BigDataChannel( diff --git a/mirai-core/src/commonMain/kotlin/network/protocol/data/jce/DeviceItemDes.kt b/mirai-core/src/commonMain/kotlin/network/protocol/data/jce/DeviceItemDes.kt index d5953efcd..cc2469e6d 100644 --- a/mirai-core/src/commonMain/kotlin/network/protocol/data/jce/DeviceItemDes.kt +++ b/mirai-core/src/commonMain/kotlin/network/protocol/data/jce/DeviceItemDes.kt @@ -1,5 +1,5 @@ /* - * Copyright 2019-2021 Mamoe Technologies and contributors. + * Copyright 2019-2022 Mamoe Technologies and contributors. * * 此源代码的使用受 GNU AFFERO GENERAL PUBLIC LICENSE version 3 许可证的约束, 可以在以下链接找到该许可证. * Use of this source code is governed by the GNU AGPLv3 license that can be found through the following link. @@ -11,6 +11,7 @@ package net.mamoe.mirai.internal.network.protocol.data.jce import kotlinx.serialization.Serializable import net.mamoe.mirai.internal.utils.io.JceStruct import net.mamoe.mirai.internal.utils.io.serialization.tars.TarsId +import kotlin.jvm.JvmField @Serializable internal class DeviceItemDes( diff --git a/mirai-core/src/commonMain/kotlin/network/protocol/data/jce/FriendList.kt b/mirai-core/src/commonMain/kotlin/network/protocol/data/jce/FriendList.kt index 915ae848c..dc0538eef 100644 --- a/mirai-core/src/commonMain/kotlin/network/protocol/data/jce/FriendList.kt +++ b/mirai-core/src/commonMain/kotlin/network/protocol/data/jce/FriendList.kt @@ -1,5 +1,5 @@ /* - * Copyright 2019-2021 Mamoe Technologies and contributors. + * Copyright 2019-2022 Mamoe Technologies and contributors. * * 此源代码的使用受 GNU AFFERO GENERAL PUBLIC LICENSE version 3 许可证的约束, 可以在以下链接找到该许可证. * Use of this source code is governed by the GNU AGPLv3 license that can be found through the following link. @@ -12,6 +12,7 @@ package net.mamoe.mirai.internal.network.protocol.data.jce import kotlinx.serialization.Serializable import net.mamoe.mirai.internal.utils.io.JceStruct import net.mamoe.mirai.internal.utils.io.serialization.tars.TarsId +import kotlin.jvm.JvmField @Serializable diff --git a/mirai-core/src/commonMain/kotlin/network/protocol/data/jce/GroupMngReq.kt b/mirai-core/src/commonMain/kotlin/network/protocol/data/jce/GroupMngReq.kt index f8240c5ad..c30411249 100644 --- a/mirai-core/src/commonMain/kotlin/network/protocol/data/jce/GroupMngReq.kt +++ b/mirai-core/src/commonMain/kotlin/network/protocol/data/jce/GroupMngReq.kt @@ -1,5 +1,5 @@ /* - * Copyright 2019-2021 Mamoe Technologies and contributors. + * Copyright 2019-2022 Mamoe Technologies and contributors. * * 此源代码的使用受 GNU AFFERO GENERAL PUBLIC LICENSE version 3 许可证的约束, 可以在以下链接找到该许可证. * Use of this source code is governed by the GNU AGPLv3 license that can be found through the following link. @@ -12,6 +12,7 @@ package net.mamoe.mirai.internal.network.protocol.data.jce import kotlinx.serialization.Serializable import net.mamoe.mirai.internal.utils.io.JceStruct import net.mamoe.mirai.internal.utils.io.serialization.tars.TarsId +import kotlin.jvm.JvmField @Serializable internal class GroupMngReqJce( diff --git a/mirai-core/src/commonMain/kotlin/network/protocol/data/jce/InstanceInfo.kt b/mirai-core/src/commonMain/kotlin/network/protocol/data/jce/InstanceInfo.kt index a4403113f..dc3ee87d4 100644 --- a/mirai-core/src/commonMain/kotlin/network/protocol/data/jce/InstanceInfo.kt +++ b/mirai-core/src/commonMain/kotlin/network/protocol/data/jce/InstanceInfo.kt @@ -1,5 +1,5 @@ /* - * Copyright 2019-2021 Mamoe Technologies and contributors. + * Copyright 2019-2022 Mamoe Technologies and contributors. * * 此源代码的使用受 GNU AFFERO GENERAL PUBLIC LICENSE version 3 许可证的约束, 可以在以下链接找到该许可证. * Use of this source code is governed by the GNU AGPLv3 license that can be found through the following link. @@ -13,6 +13,7 @@ import kotlinx.serialization.Serializable import net.mamoe.mirai.contact.ClientKind import net.mamoe.mirai.internal.utils.io.JceStruct import net.mamoe.mirai.internal.utils.io.serialization.tars.TarsId +import kotlin.jvm.JvmField @Serializable internal data class InstanceInfo( diff --git a/mirai-core/src/commonMain/kotlin/network/protocol/data/jce/MsgType0x210.kt b/mirai-core/src/commonMain/kotlin/network/protocol/data/jce/MsgType0x210.kt index d076ecfc0..24bf2bcc2 100644 --- a/mirai-core/src/commonMain/kotlin/network/protocol/data/jce/MsgType0x210.kt +++ b/mirai-core/src/commonMain/kotlin/network/protocol/data/jce/MsgType0x210.kt @@ -1,10 +1,10 @@ /* - * Copyright 2019-2021 Mamoe Technologies and contributors. + * Copyright 2019-2022 Mamoe Technologies and contributors. * - * 此源代码的使用受 GNU AFFERO GENERAL PUBLIC LICENSE version 3 许可证的约束, 可以在以下链接找到该许可证. - * Use of this source code is governed by the GNU AGPLv3 license that can be found through the following link. + * 此源代码的使用受 GNU AFFERO GENERAL PUBLIC LICENSE version 3 许可证的约束, 可以在以下链接找到该许可证. + * Use of this source code is governed by the GNU AGPLv3 license that can be found through the following link. * - * https://github.com/mamoe/mirai/blob/master/LICENSE + * https://github.com/mamoe/mirai/blob/dev/LICENSE */ package net.mamoe.mirai.internal.network.protocol.data.jce @@ -18,6 +18,7 @@ import net.mamoe.mirai.internal.utils.io.ProtocolStruct import net.mamoe.mirai.internal.utils.io.serialization.loadAs import net.mamoe.mirai.internal.utils.io.serialization.tars.TarsId import net.mamoe.mirai.utils.EMPTY_BYTE_ARRAY +import kotlin.jvm.JvmField @Serializable internal class AddGroup( diff --git a/mirai-core/src/commonMain/kotlin/network/protocol/data/jce/OnlinePushPack.kt b/mirai-core/src/commonMain/kotlin/network/protocol/data/jce/OnlinePushPack.kt index 6518cf206..3b037b351 100644 --- a/mirai-core/src/commonMain/kotlin/network/protocol/data/jce/OnlinePushPack.kt +++ b/mirai-core/src/commonMain/kotlin/network/protocol/data/jce/OnlinePushPack.kt @@ -1,5 +1,5 @@ /* - * Copyright 2019-2021 Mamoe Technologies and contributors. + * Copyright 2019-2022 Mamoe Technologies and contributors. * * 此源代码的使用受 GNU AFFERO GENERAL PUBLIC LICENSE version 3 许可证的约束, 可以在以下链接找到该许可证. * Use of this source code is governed by the GNU AGPLv3 license that can be found through the following link. @@ -12,6 +12,7 @@ package net.mamoe.mirai.internal.network.protocol.data.jce import kotlinx.serialization.Serializable import net.mamoe.mirai.internal.utils.io.JceStruct import net.mamoe.mirai.internal.utils.io.serialization.tars.TarsId +import kotlin.jvm.JvmField internal class OnlinePushPack { @Serializable diff --git a/mirai-core/src/commonMain/kotlin/network/protocol/data/jce/PushNotifyPack.kt b/mirai-core/src/commonMain/kotlin/network/protocol/data/jce/PushNotifyPack.kt index 3d45060cd..6a84bff21 100644 --- a/mirai-core/src/commonMain/kotlin/network/protocol/data/jce/PushNotifyPack.kt +++ b/mirai-core/src/commonMain/kotlin/network/protocol/data/jce/PushNotifyPack.kt @@ -1,10 +1,10 @@ /* - * Copyright 2019-2021 Mamoe Technologies and contributors. + * Copyright 2019-2022 Mamoe Technologies and contributors. * - * 此源代码的使用受 GNU AFFERO GENERAL PUBLIC LICENSE version 3 许可证的约束, 可以在以下链接找到该许可证. - * Use of this source code is governed by the GNU AGPLv3 license that can be found through the following link. + * 此源代码的使用受 GNU AFFERO GENERAL PUBLIC LICENSE version 3 许可证的约束, 可以在以下链接找到该许可证. + * Use of this source code is governed by the GNU AGPLv3 license that can be found through the following link. * - * https://github.com/mamoe/mirai/blob/master/LICENSE + * https://github.com/mamoe/mirai/blob/dev/LICENSE */ package net.mamoe.mirai.internal.network.protocol.data.jce @@ -18,6 +18,7 @@ import net.mamoe.mirai.internal.utils.io.ProtocolStruct import net.mamoe.mirai.internal.utils.io.serialization.loadAs import net.mamoe.mirai.internal.utils.io.serialization.tars.TarsId import net.mamoe.mirai.utils.EMPTY_BYTE_ARRAY +import kotlin.jvm.JvmField @Suppress("ArrayInDataClass") @Serializable diff --git a/mirai-core/src/commonMain/kotlin/network/protocol/data/jce/ReqPushStatus.kt b/mirai-core/src/commonMain/kotlin/network/protocol/data/jce/ReqPushStatus.kt index 145cefb7d..17ac3718a 100644 --- a/mirai-core/src/commonMain/kotlin/network/protocol/data/jce/ReqPushStatus.kt +++ b/mirai-core/src/commonMain/kotlin/network/protocol/data/jce/ReqPushStatus.kt @@ -1,10 +1,10 @@ /* - * Copyright 2020-2021 Mamoe Technologies and contributors. + * Copyright 2019-2022 Mamoe Technologies and contributors. * - * 此源代码的使用受 GNU AFFERO GENERAL PUBLIC LICENSE version 3 许可证的约束, 可以在以下链接找到该许可证. - * Use of this source code is governed by the GNU AGPLv3 license that can be found through the following link. + * 此源代码的使用受 GNU AFFERO GENERAL PUBLIC LICENSE version 3 许可证的约束, 可以在以下链接找到该许可证. + * Use of this source code is governed by the GNU AGPLv3 license that can be found through the following link. * - * https://github.com/mamoe/mirai/blob/master/LICENSE + * https://github.com/mamoe/mirai/blob/dev/LICENSE */ package net.mamoe.mirai.internal.network.protocol.data.jce @@ -13,6 +13,7 @@ import kotlinx.serialization.Serializable import net.mamoe.mirai.internal.network.Packet import net.mamoe.mirai.internal.utils.io.JceStruct import net.mamoe.mirai.internal.utils.io.serialization.tars.TarsId +import kotlin.jvm.JvmField @Serializable diff --git a/mirai-core/src/commonMain/kotlin/network/protocol/data/jce/ReqSummaryCard.kt b/mirai-core/src/commonMain/kotlin/network/protocol/data/jce/ReqSummaryCard.kt index b83b9982b..3df4b3a0f 100644 --- a/mirai-core/src/commonMain/kotlin/network/protocol/data/jce/ReqSummaryCard.kt +++ b/mirai-core/src/commonMain/kotlin/network/protocol/data/jce/ReqSummaryCard.kt @@ -1,10 +1,10 @@ /* - * Copyright 2019-2021 Mamoe Technologies and contributors. + * Copyright 2019-2022 Mamoe Technologies and contributors. * - * 此源代码的使用受 GNU AFFERO GENERAL PUBLIC LICENSE version 3 许可证的约束, 可以在以下链接找到该许可证. - * Use of this source code is governed by the GNU AGPLv3 license that can be found through the following link. + * 此源代码的使用受 GNU AFFERO GENERAL PUBLIC LICENSE version 3 许可证的约束, 可以在以下链接找到该许可证. + * Use of this source code is governed by the GNU AGPLv3 license that can be found through the following link. * - * https://github.com/mamoe/mirai/blob/master/LICENSE + * https://github.com/mamoe/mirai/blob/dev/LICENSE */ package net.mamoe.mirai.internal.network.protocol.data.jce @@ -12,6 +12,7 @@ package net.mamoe.mirai.internal.network.protocol.data.jce import kotlinx.serialization.Serializable import net.mamoe.mirai.internal.utils.io.JceStruct import net.mamoe.mirai.internal.utils.io.serialization.tars.TarsId +import kotlin.jvm.JvmField @Serializable diff --git a/mirai-core/src/commonMain/kotlin/network/protocol/data/jce/RequestMSFForceOffline.kt b/mirai-core/src/commonMain/kotlin/network/protocol/data/jce/RequestMSFForceOffline.kt index 5939691bd..d442c87ca 100644 --- a/mirai-core/src/commonMain/kotlin/network/protocol/data/jce/RequestMSFForceOffline.kt +++ b/mirai-core/src/commonMain/kotlin/network/protocol/data/jce/RequestMSFForceOffline.kt @@ -1,5 +1,5 @@ /* - * Copyright 2019-2021 Mamoe Technologies and contributors. + * Copyright 2019-2022 Mamoe Technologies and contributors. * * 此源代码的使用受 GNU AFFERO GENERAL PUBLIC LICENSE version 3 许可证的约束, 可以在以下链接找到该许可证. * Use of this source code is governed by the GNU AGPLv3 license that can be found through the following link. @@ -12,6 +12,7 @@ package net.mamoe.mirai.internal.network.protocol.data.jce import kotlinx.serialization.Serializable import net.mamoe.mirai.internal.utils.io.JceStruct import net.mamoe.mirai.internal.utils.io.serialization.tars.TarsId +import kotlin.jvm.JvmField @Serializable internal class RequestMSFForceOffline( diff --git a/mirai-core/src/commonMain/kotlin/network/protocol/data/jce/RequestPacket.kt b/mirai-core/src/commonMain/kotlin/network/protocol/data/jce/RequestPacket.kt index eceb93c07..46a14a565 100644 --- a/mirai-core/src/commonMain/kotlin/network/protocol/data/jce/RequestPacket.kt +++ b/mirai-core/src/commonMain/kotlin/network/protocol/data/jce/RequestPacket.kt @@ -1,10 +1,10 @@ /* - * Copyright 2019-2021 Mamoe Technologies and contributors. + * Copyright 2019-2022 Mamoe Technologies and contributors. * - * 此源代码的使用受 GNU AFFERO GENERAL PUBLIC LICENSE version 3 许可证的约束, 可以在以下链接找到该许可证. - * Use of this source code is governed by the GNU AGPLv3 license that can be found through the following link. + * 此源代码的使用受 GNU AFFERO GENERAL PUBLIC LICENSE version 3 许可证的约束, 可以在以下链接找到该许可证. + * Use of this source code is governed by the GNU AGPLv3 license that can be found through the following link. * - * https://github.com/mamoe/mirai/blob/master/LICENSE + * https://github.com/mamoe/mirai/blob/dev/LICENSE */ package net.mamoe.mirai.internal.network.protocol.data.jce @@ -13,6 +13,7 @@ import kotlinx.serialization.Serializable import net.mamoe.mirai.internal.utils.io.JceStruct import net.mamoe.mirai.internal.utils.io.serialization.tars.TarsId import net.mamoe.mirai.utils.EMPTY_BYTE_ARRAY +import kotlin.jvm.JvmField private val EMPTY_MAP = mapOf() diff --git a/mirai-core/src/commonMain/kotlin/network/protocol/data/jce/RequestPushForceOffline.kt b/mirai-core/src/commonMain/kotlin/network/protocol/data/jce/RequestPushForceOffline.kt index 6268553ee..fcb279f15 100644 --- a/mirai-core/src/commonMain/kotlin/network/protocol/data/jce/RequestPushForceOffline.kt +++ b/mirai-core/src/commonMain/kotlin/network/protocol/data/jce/RequestPushForceOffline.kt @@ -1,10 +1,10 @@ /* - * Copyright 2019-2021 Mamoe Technologies and contributors. + * Copyright 2019-2022 Mamoe Technologies and contributors. * - * 此源代码的使用受 GNU AFFERO GENERAL PUBLIC LICENSE version 3 许可证的约束, 可以在以下链接找到该许可证. - * Use of this source code is governed by the GNU AGPLv3 license that can be found through the following link. + * 此源代码的使用受 GNU AFFERO GENERAL PUBLIC LICENSE version 3 许可证的约束, 可以在以下链接找到该许可证. + * Use of this source code is governed by the GNU AGPLv3 license that can be found through the following link. * - * https://github.com/mamoe/mirai/blob/master/LICENSE + * https://github.com/mamoe/mirai/blob/dev/LICENSE */ package net.mamoe.mirai.internal.network.protocol.data.jce @@ -13,6 +13,7 @@ import kotlinx.serialization.Serializable import net.mamoe.mirai.internal.network.Packet import net.mamoe.mirai.internal.utils.io.JceStruct import net.mamoe.mirai.internal.utils.io.serialization.tars.TarsId +import kotlin.jvm.JvmField @Serializable internal class RequestPushForceOffline( diff --git a/mirai-core/src/commonMain/kotlin/network/protocol/data/jce/SvcDevLoginInfo.kt b/mirai-core/src/commonMain/kotlin/network/protocol/data/jce/SvcDevLoginInfo.kt index 476ad0ee9..4729603be 100644 --- a/mirai-core/src/commonMain/kotlin/network/protocol/data/jce/SvcDevLoginInfo.kt +++ b/mirai-core/src/commonMain/kotlin/network/protocol/data/jce/SvcDevLoginInfo.kt @@ -1,5 +1,5 @@ /* - * Copyright 2019-2021 Mamoe Technologies and contributors. + * Copyright 2019-2022 Mamoe Technologies and contributors. * * 此源代码的使用受 GNU AFFERO GENERAL PUBLIC LICENSE version 3 许可证的约束, 可以在以下链接找到该许可证. * Use of this source code is governed by the GNU AGPLv3 license that can be found through the following link. @@ -12,6 +12,7 @@ package net.mamoe.mirai.internal.network.protocol.data.jce import kotlinx.serialization.Serializable import net.mamoe.mirai.internal.utils.io.JceStruct import net.mamoe.mirai.internal.utils.io.serialization.tars.TarsId +import kotlin.jvm.JvmField @Serializable internal data class SvcDevLoginInfo( diff --git a/mirai-core/src/commonMain/kotlin/network/protocol/data/jce/SvcReqMSFLoginNotifyData.kt b/mirai-core/src/commonMain/kotlin/network/protocol/data/jce/SvcReqMSFLoginNotifyData.kt index b9f594f1a..1454d104a 100644 --- a/mirai-core/src/commonMain/kotlin/network/protocol/data/jce/SvcReqMSFLoginNotifyData.kt +++ b/mirai-core/src/commonMain/kotlin/network/protocol/data/jce/SvcReqMSFLoginNotifyData.kt @@ -1,5 +1,5 @@ /* - * Copyright 2019-2021 Mamoe Technologies and contributors. + * Copyright 2019-2022 Mamoe Technologies and contributors. * * 此源代码的使用受 GNU AFFERO GENERAL PUBLIC LICENSE version 3 许可证的约束, 可以在以下链接找到该许可证. * Use of this source code is governed by the GNU AGPLv3 license that can be found through the following link. @@ -12,6 +12,7 @@ package net.mamoe.mirai.internal.network.protocol.data.jce import kotlinx.serialization.Serializable import net.mamoe.mirai.internal.utils.io.JceStruct import net.mamoe.mirai.internal.utils.io.serialization.tars.TarsId +import kotlin.jvm.JvmField // ANDROID PHONE QQ diff --git a/mirai-core/src/commonMain/kotlin/network/protocol/data/jce/SvcReqRegister.kt b/mirai-core/src/commonMain/kotlin/network/protocol/data/jce/SvcReqRegister.kt index 402a5da8d..d5a7ebb60 100644 --- a/mirai-core/src/commonMain/kotlin/network/protocol/data/jce/SvcReqRegister.kt +++ b/mirai-core/src/commonMain/kotlin/network/protocol/data/jce/SvcReqRegister.kt @@ -1,5 +1,5 @@ /* - * Copyright 2019-2021 Mamoe Technologies and contributors. + * Copyright 2019-2022 Mamoe Technologies and contributors. * * 此源代码的使用受 GNU AFFERO GENERAL PUBLIC LICENSE version 3 许可证的约束, 可以在以下链接找到该许可证. * Use of this source code is governed by the GNU AGPLv3 license that can be found through the following link. @@ -12,6 +12,7 @@ package net.mamoe.mirai.internal.network.protocol.data.jce import kotlinx.serialization.Serializable import net.mamoe.mirai.internal.utils.io.JceStruct import net.mamoe.mirai.internal.utils.io.serialization.tars.TarsId +import kotlin.jvm.JvmField @Serializable internal class SvcReqRegister( diff --git a/mirai-core/src/commonMain/kotlin/network/protocol/data/jce/SvcRequestPushReadedNotify.kt b/mirai-core/src/commonMain/kotlin/network/protocol/data/jce/SvcRequestPushReadedNotify.kt index 992993583..5eed4d322 100644 --- a/mirai-core/src/commonMain/kotlin/network/protocol/data/jce/SvcRequestPushReadedNotify.kt +++ b/mirai-core/src/commonMain/kotlin/network/protocol/data/jce/SvcRequestPushReadedNotify.kt @@ -1,5 +1,5 @@ /* - * Copyright 2019-2021 Mamoe Technologies and contributors. + * Copyright 2019-2022 Mamoe Technologies and contributors. * * 此源代码的使用受 GNU AFFERO GENERAL PUBLIC LICENSE version 3 许可证的约束, 可以在以下链接找到该许可证. * Use of this source code is governed by the GNU AGPLv3 license that can be found through the following link. @@ -14,6 +14,7 @@ package net.mamoe.mirai.internal.network.protocol.data.jce import kotlinx.serialization.Serializable import net.mamoe.mirai.internal.utils.io.JceStruct import net.mamoe.mirai.internal.utils.io.serialization.tars.TarsId +import kotlin.jvm.JvmField @Serializable internal class SvcRequestPushReadedNotify( diff --git a/mirai-core/src/commonMain/kotlin/network/protocol/data/jce/SvcRespRegister.kt b/mirai-core/src/commonMain/kotlin/network/protocol/data/jce/SvcRespRegister.kt index eadae1fc1..0d529a0b8 100644 --- a/mirai-core/src/commonMain/kotlin/network/protocol/data/jce/SvcRespRegister.kt +++ b/mirai-core/src/commonMain/kotlin/network/protocol/data/jce/SvcRespRegister.kt @@ -1,5 +1,5 @@ /* - * Copyright 2019-2021 Mamoe Technologies and contributors. + * Copyright 2019-2022 Mamoe Technologies and contributors. * * 此源代码的使用受 GNU AFFERO GENERAL PUBLIC LICENSE version 3 许可证的约束, 可以在以下链接找到该许可证. * Use of this source code is governed by the GNU AGPLv3 license that can be found through the following link. @@ -13,6 +13,7 @@ import kotlinx.serialization.Serializable import net.mamoe.mirai.internal.network.FriendListCache import net.mamoe.mirai.internal.utils.io.JceStruct import net.mamoe.mirai.internal.utils.io.serialization.tars.TarsId +import kotlin.jvm.JvmField @Serializable diff --git a/mirai-core/src/commonMain/kotlin/network/protocol/data/jce/SvcRspGetDevLoginInfo.kt b/mirai-core/src/commonMain/kotlin/network/protocol/data/jce/SvcRspGetDevLoginInfo.kt index ceba08a57..bb7deb8e5 100644 --- a/mirai-core/src/commonMain/kotlin/network/protocol/data/jce/SvcRspGetDevLoginInfo.kt +++ b/mirai-core/src/commonMain/kotlin/network/protocol/data/jce/SvcRspGetDevLoginInfo.kt @@ -1,5 +1,5 @@ /* - * Copyright 2019-2021 Mamoe Technologies and contributors. + * Copyright 2019-2022 Mamoe Technologies and contributors. * * 此源代码的使用受 GNU AFFERO GENERAL PUBLIC LICENSE version 3 许可证的约束, 可以在以下链接找到该许可证. * Use of this source code is governed by the GNU AGPLv3 license that can be found through the following link. @@ -12,6 +12,7 @@ package net.mamoe.mirai.internal.network.protocol.data.jce import kotlinx.serialization.Serializable import net.mamoe.mirai.internal.utils.io.JceStruct import net.mamoe.mirai.internal.utils.io.serialization.tars.TarsId +import kotlin.jvm.JvmField @Serializable internal class SvcRspGetDevLoginInfo( diff --git a/mirai-core/src/commonMain/kotlin/network/protocol/data/jce/TroopList.kt b/mirai-core/src/commonMain/kotlin/network/protocol/data/jce/TroopList.kt index 5aefda270..aeabf8da9 100644 --- a/mirai-core/src/commonMain/kotlin/network/protocol/data/jce/TroopList.kt +++ b/mirai-core/src/commonMain/kotlin/network/protocol/data/jce/TroopList.kt @@ -1,5 +1,5 @@ /* - * Copyright 2019-2021 Mamoe Technologies and contributors. + * Copyright 2019-2022 Mamoe Technologies and contributors. * * 此源代码的使用受 GNU AFFERO GENERAL PUBLIC LICENSE version 3 许可证的约束, 可以在以下链接找到该许可证. * Use of this source code is governed by the GNU AGPLv3 license that can be found through the following link. @@ -12,6 +12,7 @@ package net.mamoe.mirai.internal.network.protocol.data.jce import kotlinx.serialization.Serializable import net.mamoe.mirai.internal.utils.io.JceStruct import net.mamoe.mirai.internal.utils.io.serialization.tars.TarsId +import kotlin.jvm.JvmField @Serializable internal class GetTroopListReqV2Simplify( diff --git a/mirai-core/src/commonMain/kotlin/network/protocol/data/proto/Cmd0x346.kt b/mirai-core/src/commonMain/kotlin/network/protocol/data/proto/Cmd0x346.kt index 6f9d19f7e..2282dd2bd 100644 --- a/mirai-core/src/commonMain/kotlin/network/protocol/data/proto/Cmd0x346.kt +++ b/mirai-core/src/commonMain/kotlin/network/protocol/data/proto/Cmd0x346.kt @@ -1,10 +1,10 @@ /* - * Copyright 2019-2021 Mamoe Technologies and contributors. + * Copyright 2019-2022 Mamoe Technologies and contributors. * - * 此源代码的使用受 GNU AFFERO GENERAL PUBLIC LICENSE version 3 许可证的约束, 可以在以下链接找到该许可证. - * Use of this source code is governed by the GNU AGPLv3 license that can be found through the following link. + * 此源代码的使用受 GNU AFFERO GENERAL PUBLIC LICENSE version 3 许可证的约束, 可以在以下链接找到该许可证. + * Use of this source code is governed by the GNU AGPLv3 license that can be found through the following link. * - * https://github.com/mamoe/mirai/blob/master/LICENSE + * https://github.com/mamoe/mirai/blob/dev/LICENSE */ @file:Suppress("unused", "SpellCheckingInspection") @@ -14,6 +14,7 @@ import kotlinx.serialization.Serializable import kotlinx.serialization.protobuf.ProtoNumber import net.mamoe.mirai.internal.utils.io.ProtoBuf import net.mamoe.mirai.utils.EMPTY_BYTE_ARRAY +import kotlin.jvm.JvmField @Serializable internal class Cmd0x346 : ProtoBuf { diff --git a/mirai-core/src/commonMain/kotlin/network/protocol/data/proto/Cmd0x352.kt b/mirai-core/src/commonMain/kotlin/network/protocol/data/proto/Cmd0x352.kt index a80ff28f1..882bc3068 100644 --- a/mirai-core/src/commonMain/kotlin/network/protocol/data/proto/Cmd0x352.kt +++ b/mirai-core/src/commonMain/kotlin/network/protocol/data/proto/Cmd0x352.kt @@ -1,10 +1,10 @@ /* - * Copyright 2019-2021 Mamoe Technologies and contributors. + * Copyright 2019-2022 Mamoe Technologies and contributors. * - * 此源代码的使用受 GNU AFFERO GENERAL PUBLIC LICENSE version 3 许可证的约束, 可以在以下链接找到该许可证. - * Use of this source code is governed by the GNU AGPLv3 license that can be found through the following link. + * 此源代码的使用受 GNU AFFERO GENERAL PUBLIC LICENSE version 3 许可证的约束, 可以在以下链接找到该许可证. + * Use of this source code is governed by the GNU AGPLv3 license that can be found through the following link. * - * https://github.com/mamoe/mirai/blob/master/LICENSE + * https://github.com/mamoe/mirai/blob/dev/LICENSE */ package net.mamoe.mirai.internal.network.protocol.data.proto @@ -13,6 +13,7 @@ import kotlinx.serialization.Serializable import kotlinx.serialization.protobuf.ProtoNumber import net.mamoe.mirai.internal.utils.io.ProtoBuf import net.mamoe.mirai.utils.EMPTY_BYTE_ARRAY +import kotlin.jvm.JvmField @Serializable internal class Cmd0x352 : ProtoBuf { diff --git a/mirai-core/src/commonMain/kotlin/network/protocol/data/proto/Cmd0x388.kt b/mirai-core/src/commonMain/kotlin/network/protocol/data/proto/Cmd0x388.kt index a55553bdc..0f4af3340 100644 --- a/mirai-core/src/commonMain/kotlin/network/protocol/data/proto/Cmd0x388.kt +++ b/mirai-core/src/commonMain/kotlin/network/protocol/data/proto/Cmd0x388.kt @@ -1,10 +1,10 @@ /* - * Copyright 2019-2021 Mamoe Technologies and contributors. + * Copyright 2019-2022 Mamoe Technologies and contributors. * - * 此源代码的使用受 GNU AFFERO GENERAL PUBLIC LICENSE version 3 许可证的约束, 可以在以下链接找到该许可证. - * Use of this source code is governed by the GNU AGPLv3 license that can be found through the following link. + * 此源代码的使用受 GNU AFFERO GENERAL PUBLIC LICENSE version 3 许可证的约束, 可以在以下链接找到该许可证. + * Use of this source code is governed by the GNU AGPLv3 license that can be found through the following link. * - * https://github.com/mamoe/mirai/blob/master/LICENSE + * https://github.com/mamoe/mirai/blob/dev/LICENSE */ package net.mamoe.mirai.internal.network.protocol.data.proto @@ -13,6 +13,7 @@ import kotlinx.serialization.Serializable import kotlinx.serialization.protobuf.ProtoNumber import net.mamoe.mirai.internal.utils.io.ProtoBuf import net.mamoe.mirai.utils.EMPTY_BYTE_ARRAY +import kotlin.jvm.JvmField @Serializable internal class Cmd0x388 : ProtoBuf { diff --git a/mirai-core/src/commonMain/kotlin/network/protocol/data/proto/Cmd0x857.kt b/mirai-core/src/commonMain/kotlin/network/protocol/data/proto/Cmd0x857.kt index ab66fb3cd..5b88661ae 100644 --- a/mirai-core/src/commonMain/kotlin/network/protocol/data/proto/Cmd0x857.kt +++ b/mirai-core/src/commonMain/kotlin/network/protocol/data/proto/Cmd0x857.kt @@ -1,10 +1,10 @@ /* - * Copyright 2019-2021 Mamoe Technologies and contributors. + * Copyright 2019-2022 Mamoe Technologies and contributors. * - * 此源代码的使用受 GNU AFFERO GENERAL PUBLIC LICENSE version 3 许可证的约束, 可以在以下链接找到该许可证. - * Use of this source code is governed by the GNU AGPLv3 license that can be found through the following link. + * 此源代码的使用受 GNU AFFERO GENERAL PUBLIC LICENSE version 3 许可证的约束, 可以在以下链接找到该许可证. + * Use of this source code is governed by the GNU AGPLv3 license that can be found through the following link. * - * https://github.com/mamoe/mirai/blob/master/LICENSE + * https://github.com/mamoe/mirai/blob/dev/LICENSE */ @file:Suppress("unused", "SpellCheckingInspection") @@ -17,6 +17,7 @@ import kotlinx.serialization.protobuf.ProtoNumber import kotlinx.serialization.protobuf.ProtoType import net.mamoe.mirai.internal.utils.io.ProtoBuf import net.mamoe.mirai.utils.EMPTY_BYTE_ARRAY +import kotlin.jvm.JvmField @Serializable internal class GroupOpenSysMsg : ProtoBuf { diff --git a/mirai-core/src/commonMain/kotlin/network/protocol/data/proto/Cmd0x858.kt b/mirai-core/src/commonMain/kotlin/network/protocol/data/proto/Cmd0x858.kt index 1af907bf5..cfc15b107 100644 --- a/mirai-core/src/commonMain/kotlin/network/protocol/data/proto/Cmd0x858.kt +++ b/mirai-core/src/commonMain/kotlin/network/protocol/data/proto/Cmd0x858.kt @@ -1,10 +1,10 @@ /* - * Copyright 2019-2021 Mamoe Technologies and contributors. + * Copyright 2019-2022 Mamoe Technologies and contributors. * - * 此源代码的使用受 GNU AFFERO GENERAL PUBLIC LICENSE version 3 许可证的约束, 可以在以下链接找到该许可证. - * Use of this source code is governed by the GNU AGPLv3 license that can be found through the following link. + * 此源代码的使用受 GNU AFFERO GENERAL PUBLIC LICENSE version 3 许可证的约束, 可以在以下链接找到该许可证. + * Use of this source code is governed by the GNU AGPLv3 license that can be found through the following link. * - * https://github.com/mamoe/mirai/blob/master/LICENSE + * https://github.com/mamoe/mirai/blob/dev/LICENSE */ @file:Suppress("SpellCheckingInspection") @@ -17,6 +17,7 @@ import kotlinx.serialization.protobuf.ProtoNumber import kotlinx.serialization.protobuf.ProtoType import net.mamoe.mirai.internal.utils.io.ProtoBuf import net.mamoe.mirai.utils.EMPTY_BYTE_ARRAY +import kotlin.jvm.JvmField @Serializable internal class Oidb0x858 : ProtoBuf { diff --git a/mirai-core/src/commonMain/kotlin/network/protocol/data/proto/Define.kt b/mirai-core/src/commonMain/kotlin/network/protocol/data/proto/Define.kt index f02971b92..24dff4e28 100644 --- a/mirai-core/src/commonMain/kotlin/network/protocol/data/proto/Define.kt +++ b/mirai-core/src/commonMain/kotlin/network/protocol/data/proto/Define.kt @@ -1,10 +1,10 @@ /* - * Copyright 2019-2021 Mamoe Technologies and contributors. + * Copyright 2019-2022 Mamoe Technologies and contributors. * - * 此源代码的使用受 GNU AFFERO GENERAL PUBLIC LICENSE version 3 许可证的约束, 可以在以下链接找到该许可证. - * Use of this source code is governed by the GNU AGPLv3 license that can be found through the following link. + * 此源代码的使用受 GNU AFFERO GENERAL PUBLIC LICENSE version 3 许可证的约束, 可以在以下链接找到该许可证. + * Use of this source code is governed by the GNU AGPLv3 license that can be found through the following link. * - * https://github.com/mamoe/mirai/blob/master/LICENSE + * https://github.com/mamoe/mirai/blob/dev/LICENSE */ package net.mamoe.mirai.internal.network.protocol.data.proto @@ -13,6 +13,7 @@ import kotlinx.serialization.Serializable import kotlinx.serialization.protobuf.ProtoNumber import net.mamoe.mirai.internal.utils.io.ProtoBuf import net.mamoe.mirai.utils.EMPTY_BYTE_ARRAY +import kotlin.jvm.JvmField internal class Common : ProtoBuf { @Serializable diff --git a/mirai-core/src/commonMain/kotlin/network/protocol/data/proto/Exciting.kt b/mirai-core/src/commonMain/kotlin/network/protocol/data/proto/Exciting.kt index 001c4cbb5..694e218ea 100644 --- a/mirai-core/src/commonMain/kotlin/network/protocol/data/proto/Exciting.kt +++ b/mirai-core/src/commonMain/kotlin/network/protocol/data/proto/Exciting.kt @@ -1,10 +1,10 @@ /* - * Copyright 2019-2021 Mamoe Technologies and contributors. + * Copyright 2019-2022 Mamoe Technologies and contributors. * - * 此源代码的使用受 GNU AFFERO GENERAL PUBLIC LICENSE version 3 许可证的约束, 可以在以下链接找到该许可证. - * Use of this source code is governed by the GNU AGPLv3 license that can be found through the following link. + * 此源代码的使用受 GNU AFFERO GENERAL PUBLIC LICENSE version 3 许可证的约束, 可以在以下链接找到该许可证. + * Use of this source code is governed by the GNU AGPLv3 license that can be found through the following link. * - * https://github.com/mamoe/mirai/blob/master/LICENSE + * https://github.com/mamoe/mirai/blob/dev/LICENSE */ @@ -13,6 +13,7 @@ package net.mamoe.mirai.internal.network.protocol.data.proto import kotlinx.serialization.Serializable import kotlinx.serialization.protobuf.ProtoNumber import net.mamoe.mirai.internal.utils.io.ProtoBuf +import kotlin.jvm.JvmField @Serializable internal class GroupFileUploadExt( diff --git a/mirai-core/src/commonMain/kotlin/network/protocol/data/proto/FrdSysMsg.kt b/mirai-core/src/commonMain/kotlin/network/protocol/data/proto/FrdSysMsg.kt index 55eb083fe..4ebe6c518 100644 --- a/mirai-core/src/commonMain/kotlin/network/protocol/data/proto/FrdSysMsg.kt +++ b/mirai-core/src/commonMain/kotlin/network/protocol/data/proto/FrdSysMsg.kt @@ -1,5 +1,5 @@ /* - * Copyright 2019-2021 Mamoe Technologies and contributors. + * Copyright 2019-2022 Mamoe Technologies and contributors. * * 此源代码的使用受 GNU AFFERO GENERAL PUBLIC LICENSE version 3 许可证的约束, 可以在以下链接找到该许可证. * Use of this source code is governed by the GNU AGPLv3 license that can be found through the following link. @@ -14,6 +14,7 @@ package net.mamoe.mirai.internal.network.protocol.data.proto import kotlinx.serialization.Serializable import kotlinx.serialization.protobuf.ProtoNumber import net.mamoe.mirai.internal.utils.io.ProtoBuf +import kotlin.jvm.JvmField internal class FrdSysMsg { @Serializable diff --git a/mirai-core/src/commonMain/kotlin/network/protocol/data/proto/FriendListCommon.kt b/mirai-core/src/commonMain/kotlin/network/protocol/data/proto/FriendListCommon.kt index 7be2d3fa8..cffdccae7 100644 --- a/mirai-core/src/commonMain/kotlin/network/protocol/data/proto/FriendListCommon.kt +++ b/mirai-core/src/commonMain/kotlin/network/protocol/data/proto/FriendListCommon.kt @@ -1,10 +1,10 @@ /* - * Copyright 2019-2021 Mamoe Technologies and contributors. + * Copyright 2019-2022 Mamoe Technologies and contributors. * - * 此源代码的使用受 GNU AFFERO GENERAL PUBLIC LICENSE version 3 许可证的约束, 可以在以下链接找到该许可证. - * Use of this source code is governed by the GNU AGPLv3 license that can be found through the following link. + * 此源代码的使用受 GNU AFFERO GENERAL PUBLIC LICENSE version 3 许可证的约束, 可以在以下链接找到该许可证. + * Use of this source code is governed by the GNU AGPLv3 license that can be found through the following link. * - * https://github.com/mamoe/mirai/blob/master/LICENSE + * https://github.com/mamoe/mirai/blob/dev/LICENSE */ @file:Suppress("SpellCheckingInspection") @@ -15,6 +15,7 @@ import kotlinx.serialization.Serializable import kotlinx.serialization.protobuf.ProtoNumber import net.mamoe.mirai.internal.utils.io.ProtoBuf import net.mamoe.mirai.utils.EMPTY_BYTE_ARRAY +import kotlin.jvm.JvmField @Serializable internal class Vec0xd50 : ProtoBuf { diff --git a/mirai-core/src/commonMain/kotlin/network/protocol/data/proto/Group.kt b/mirai-core/src/commonMain/kotlin/network/protocol/data/proto/Group.kt index 5e427c536..c8fcb8ae7 100644 --- a/mirai-core/src/commonMain/kotlin/network/protocol/data/proto/Group.kt +++ b/mirai-core/src/commonMain/kotlin/network/protocol/data/proto/Group.kt @@ -1,10 +1,10 @@ /* - * Copyright 2019-2021 Mamoe Technologies and contributors. + * Copyright 2019-2022 Mamoe Technologies and contributors. * - * 此源代码的使用受 GNU AFFERO GENERAL PUBLIC LICENSE version 3 许可证的约束, 可以在以下链接找到该许可证. - * Use of this source code is governed by the GNU AGPLv3 license that can be found through the following link. + * 此源代码的使用受 GNU AFFERO GENERAL PUBLIC LICENSE version 3 许可证的约束, 可以在以下链接找到该许可证. + * Use of this source code is governed by the GNU AGPLv3 license that can be found through the following link. * - * https://github.com/mamoe/mirai/blob/master/LICENSE + * https://github.com/mamoe/mirai/blob/dev/LICENSE */ package net.mamoe.mirai.internal.network.protocol.data.proto @@ -13,6 +13,7 @@ import kotlinx.serialization.Serializable import kotlinx.serialization.protobuf.ProtoNumber import net.mamoe.mirai.internal.utils.io.ProtoBuf import net.mamoe.mirai.utils.EMPTY_BYTE_ARRAY +import kotlin.jvm.JvmField @Serializable internal class GroupLabel : ProtoBuf { diff --git a/mirai-core/src/commonMain/kotlin/network/protocol/data/proto/GroupFileCommon.kt b/mirai-core/src/commonMain/kotlin/network/protocol/data/proto/GroupFileCommon.kt index 63a54b15c..492148841 100644 --- a/mirai-core/src/commonMain/kotlin/network/protocol/data/proto/GroupFileCommon.kt +++ b/mirai-core/src/commonMain/kotlin/network/protocol/data/proto/GroupFileCommon.kt @@ -1,10 +1,10 @@ /* - * Copyright 2019-2021 Mamoe Technologies and contributors. + * Copyright 2019-2022 Mamoe Technologies and contributors. * - * 此源代码的使用受 GNU AFFERO GENERAL PUBLIC LICENSE version 3 许可证的约束, 可以在以下链接找到该许可证. - * Use of this source code is governed by the GNU AGPLv3 license that can be found through the following link. + * 此源代码的使用受 GNU AFFERO GENERAL PUBLIC LICENSE version 3 许可证的约束, 可以在以下链接找到该许可证. + * Use of this source code is governed by the GNU AGPLv3 license that can be found through the following link. * - * https://github.com/mamoe/mirai/blob/master/LICENSE + * https://github.com/mamoe/mirai/blob/dev/LICENSE */ @file:Suppress("unused", "SpellCheckingInspection") @@ -15,6 +15,7 @@ import kotlinx.serialization.Serializable import kotlinx.serialization.protobuf.ProtoNumber import net.mamoe.mirai.internal.utils.io.ProtoBuf import net.mamoe.mirai.utils.EMPTY_BYTE_ARRAY +import kotlin.jvm.JvmField internal class GroupFileCommon : ProtoBuf { @Serializable diff --git a/mirai-core/src/commonMain/kotlin/network/protocol/data/proto/Highway.kt b/mirai-core/src/commonMain/kotlin/network/protocol/data/proto/Highway.kt index e19db05dc..58b59047d 100644 --- a/mirai-core/src/commonMain/kotlin/network/protocol/data/proto/Highway.kt +++ b/mirai-core/src/commonMain/kotlin/network/protocol/data/proto/Highway.kt @@ -1,10 +1,10 @@ /* - * Copyright 2019-2021 Mamoe Technologies and contributors. + * Copyright 2019-2022 Mamoe Technologies and contributors. * - * 此源代码的使用受 GNU AFFERO GENERAL PUBLIC LICENSE version 3 许可证的约束, 可以在以下链接找到该许可证. - * Use of this source code is governed by the GNU AGPLv3 license that can be found through the following link. + * 此源代码的使用受 GNU AFFERO GENERAL PUBLIC LICENSE version 3 许可证的约束, 可以在以下链接找到该许可证. + * Use of this source code is governed by the GNU AGPLv3 license that can be found through the following link. * - * https://github.com/mamoe/mirai/blob/master/LICENSE + * https://github.com/mamoe/mirai/blob/dev/LICENSE */ @file:Suppress("unused", "SpellCheckingInspection") @@ -17,6 +17,7 @@ import kotlinx.serialization.protobuf.ProtoNumber import kotlinx.serialization.protobuf.ProtoType import net.mamoe.mirai.internal.utils.io.ProtoBuf import net.mamoe.mirai.utils.EMPTY_BYTE_ARRAY +import kotlin.jvm.JvmField /** diff --git a/mirai-core/src/commonMain/kotlin/network/protocol/data/proto/HummerCommelem.kt b/mirai-core/src/commonMain/kotlin/network/protocol/data/proto/HummerCommelem.kt index aa94da815..24495ad86 100644 --- a/mirai-core/src/commonMain/kotlin/network/protocol/data/proto/HummerCommelem.kt +++ b/mirai-core/src/commonMain/kotlin/network/protocol/data/proto/HummerCommelem.kt @@ -1,10 +1,10 @@ /* - * Copyright 2019-2021 Mamoe Technologies and contributors. + * Copyright 2019-2022 Mamoe Technologies and contributors. * - * 此源代码的使用受 GNU AFFERO GENERAL PUBLIC LICENSE version 3 许可证的约束, 可以在以下链接找到该许可证. - * Use of this source code is governed by the GNU AGPLv3 license that can be found through the following link. + * 此源代码的使用受 GNU AFFERO GENERAL PUBLIC LICENSE version 3 许可证的约束, 可以在以下链接找到该许可证. + * Use of this source code is governed by the GNU AGPLv3 license that can be found through the following link. * - * https://github.com/mamoe/mirai/blob/master/LICENSE + * https://github.com/mamoe/mirai/blob/dev/LICENSE */ package net.mamoe.mirai.internal.network.protocol.data.proto @@ -13,6 +13,7 @@ import kotlinx.serialization.Serializable import kotlinx.serialization.protobuf.ProtoNumber import net.mamoe.mirai.internal.utils.io.ProtoBuf import net.mamoe.mirai.utils.EMPTY_BYTE_ARRAY +import kotlin.jvm.JvmField internal class HummerCommelem : ProtoBuf { @Serializable diff --git a/mirai-core/src/commonMain/kotlin/network/protocol/data/proto/HummerResv12.kt b/mirai-core/src/commonMain/kotlin/network/protocol/data/proto/HummerResv12.kt index e27ea839b..a7c2075ca 100644 --- a/mirai-core/src/commonMain/kotlin/network/protocol/data/proto/HummerResv12.kt +++ b/mirai-core/src/commonMain/kotlin/network/protocol/data/proto/HummerResv12.kt @@ -1,5 +1,5 @@ /* - * Copyright 2019-2021 Mamoe Technologies and contributors. + * Copyright 2019-2022 Mamoe Technologies and contributors. * * 此源代码的使用受 GNU AFFERO GENERAL PUBLIC LICENSE version 3 许可证的约束, 可以在以下链接找到该许可证. * Use of this source code is governed by the GNU AGPLv3 license that can be found through the following link. @@ -14,6 +14,7 @@ package net.mamoe.mirai.internal.network.protocol.data.proto import kotlinx.serialization.Serializable import kotlinx.serialization.protobuf.ProtoNumber import net.mamoe.mirai.internal.utils.io.ProtoBuf +import kotlin.jvm.JvmField @Serializable internal class MarketFaceExtPb : ProtoBuf { diff --git a/mirai-core/src/commonMain/kotlin/network/protocol/data/proto/HummerResv21.kt b/mirai-core/src/commonMain/kotlin/network/protocol/data/proto/HummerResv21.kt index b5d9119c9..27481fe58 100644 --- a/mirai-core/src/commonMain/kotlin/network/protocol/data/proto/HummerResv21.kt +++ b/mirai-core/src/commonMain/kotlin/network/protocol/data/proto/HummerResv21.kt @@ -1,10 +1,10 @@ /* - * Copyright 2019-2021 Mamoe Technologies and contributors. + * Copyright 2019-2022 Mamoe Technologies and contributors. * - * 此源代码的使用受 GNU AFFERO GENERAL PUBLIC LICENSE version 3 许可证的约束, 可以在以下链接找到该许可证. - * Use of this source code is governed by the GNU AGPLv3 license that can be found through the following link. + * 此源代码的使用受 GNU AFFERO GENERAL PUBLIC LICENSE version 3 许可证的约束, 可以在以下链接找到该许可证. + * Use of this source code is governed by the GNU AGPLv3 license that can be found through the following link. * - * https://github.com/mamoe/mirai/blob/master/LICENSE + * https://github.com/mamoe/mirai/blob/dev/LICENSE */ @file:Suppress("unused", "SpellCheckingInspection") @@ -15,6 +15,7 @@ import kotlinx.serialization.Serializable import kotlinx.serialization.protobuf.ProtoNumber import net.mamoe.mirai.internal.utils.io.ProtoBuf import net.mamoe.mirai.utils.EMPTY_BYTE_ARRAY +import kotlin.jvm.JvmField internal class HummerResv21 : ProtoBuf { @Serializable diff --git a/mirai-core/src/commonMain/kotlin/network/protocol/data/proto/HummerResv3.kt b/mirai-core/src/commonMain/kotlin/network/protocol/data/proto/HummerResv3.kt index 23efb4c8b..4b5aa6f50 100644 --- a/mirai-core/src/commonMain/kotlin/network/protocol/data/proto/HummerResv3.kt +++ b/mirai-core/src/commonMain/kotlin/network/protocol/data/proto/HummerResv3.kt @@ -1,10 +1,10 @@ /* - * Copyright 2019-2021 Mamoe Technologies and contributors. + * Copyright 2019-2022 Mamoe Technologies and contributors. * - * 此源代码的使用受 GNU AFFERO GENERAL PUBLIC LICENSE version 3 许可证的约束, 可以在以下链接找到该许可证. - * Use of this source code is governed by the GNU AGPLv3 license that can be found through the following link. + * 此源代码的使用受 GNU AFFERO GENERAL PUBLIC LICENSE version 3 许可证的约束, 可以在以下链接找到该许可证. + * Use of this source code is governed by the GNU AGPLv3 license that can be found through the following link. * - * https://github.com/mamoe/mirai/blob/master/LICENSE + * https://github.com/mamoe/mirai/blob/dev/LICENSE */ @file:Suppress("unused", "SpellCheckingInspection") @@ -15,6 +15,7 @@ import kotlinx.serialization.Serializable import kotlinx.serialization.protobuf.ProtoNumber import net.mamoe.mirai.internal.utils.io.ProtoBuf import net.mamoe.mirai.utils.EMPTY_BYTE_ARRAY +import kotlin.jvm.JvmField /** * v8.5.5 diff --git a/mirai-core/src/commonMain/kotlin/network/protocol/data/proto/HummerResv6.kt b/mirai-core/src/commonMain/kotlin/network/protocol/data/proto/HummerResv6.kt index fa4403da7..7da94d01f 100644 --- a/mirai-core/src/commonMain/kotlin/network/protocol/data/proto/HummerResv6.kt +++ b/mirai-core/src/commonMain/kotlin/network/protocol/data/proto/HummerResv6.kt @@ -1,10 +1,10 @@ /* - * Copyright 2019-2021 Mamoe Technologies and contributors. + * Copyright 2019-2022 Mamoe Technologies and contributors. * - * 此源代码的使用受 GNU AFFERO GENERAL PUBLIC LICENSE version 3 许可证的约束, 可以在以下链接找到该许可证. - * Use of this source code is governed by the GNU AGPLv3 license that can be found through the following link. + * 此源代码的使用受 GNU AFFERO GENERAL PUBLIC LICENSE version 3 许可证的约束, 可以在以下链接找到该许可证. + * Use of this source code is governed by the GNU AGPLv3 license that can be found through the following link. * - * https://github.com/mamoe/mirai/blob/master/LICENSE + * https://github.com/mamoe/mirai/blob/dev/LICENSE */ @file:Suppress("unused", "SpellCheckingInspection") @@ -14,6 +14,7 @@ import kotlinx.serialization.Serializable import kotlinx.serialization.protobuf.ProtoNumber import net.mamoe.mirai.internal.utils.io.ProtoBuf import net.mamoe.mirai.utils.EMPTY_BYTE_ARRAY +import kotlin.jvm.JvmField @Serializable internal class NotOnlineImageExtPb : ProtoBuf { diff --git a/mirai-core/src/commonMain/kotlin/network/protocol/data/proto/ImageRequest.kt b/mirai-core/src/commonMain/kotlin/network/protocol/data/proto/ImageRequest.kt index 653efeac2..6016cd194 100644 --- a/mirai-core/src/commonMain/kotlin/network/protocol/data/proto/ImageRequest.kt +++ b/mirai-core/src/commonMain/kotlin/network/protocol/data/proto/ImageRequest.kt @@ -1,5 +1,5 @@ /* - * Copyright 2019-2021 Mamoe Technologies and contributors. + * Copyright 2019-2022 Mamoe Technologies and contributors. * * 此源代码的使用受 GNU AFFERO GENERAL PUBLIC LICENSE version 3 许可证的约束, 可以在以下链接找到该许可证. * Use of this source code is governed by the GNU AGPLv3 license that can be found through the following link. @@ -13,6 +13,7 @@ import kotlinx.serialization.Serializable import kotlinx.serialization.protobuf.ProtoNumber import net.mamoe.mirai.internal.utils.io.ProtoBuf import net.mamoe.mirai.utils.currentTimeSeconds +import kotlin.jvm.JvmField internal interface ImgReq : ProtoBuf diff --git a/mirai-core/src/commonMain/kotlin/network/protocol/data/proto/LongMsg.kt b/mirai-core/src/commonMain/kotlin/network/protocol/data/proto/LongMsg.kt index 6e8f80836..073392d97 100644 --- a/mirai-core/src/commonMain/kotlin/network/protocol/data/proto/LongMsg.kt +++ b/mirai-core/src/commonMain/kotlin/network/protocol/data/proto/LongMsg.kt @@ -1,10 +1,10 @@ /* - * Copyright 2019-2021 Mamoe Technologies and contributors. + * Copyright 2019-2022 Mamoe Technologies and contributors. * - * 此源代码的使用受 GNU AFFERO GENERAL PUBLIC LICENSE version 3 许可证的约束, 可以在以下链接找到该许可证. - * Use of this source code is governed by the GNU AGPLv3 license that can be found through the following link. + * 此源代码的使用受 GNU AFFERO GENERAL PUBLIC LICENSE version 3 许可证的约束, 可以在以下链接找到该许可证. + * Use of this source code is governed by the GNU AGPLv3 license that can be found through the following link. * - * https://github.com/mamoe/mirai/blob/master/LICENSE + * https://github.com/mamoe/mirai/blob/dev/LICENSE */ package net.mamoe.mirai.internal.network.protocol.data.proto @@ -13,6 +13,7 @@ import kotlinx.serialization.Serializable import kotlinx.serialization.protobuf.ProtoNumber import net.mamoe.mirai.internal.utils.io.ProtoBuf import net.mamoe.mirai.utils.EMPTY_BYTE_ARRAY +import kotlin.jvm.JvmField internal class LongMsg : ProtoBuf { @Serializable diff --git a/mirai-core/src/commonMain/kotlin/network/protocol/data/proto/Msg.kt b/mirai-core/src/commonMain/kotlin/network/protocol/data/proto/Msg.kt index 9b328f5fe..758ddb9ca 100644 --- a/mirai-core/src/commonMain/kotlin/network/protocol/data/proto/Msg.kt +++ b/mirai-core/src/commonMain/kotlin/network/protocol/data/proto/Msg.kt @@ -9,6 +9,7 @@ package net.mamoe.mirai.internal.network.protocol.data.proto +import io.ktor.utils.io.core.* import kotlinx.serialization.Serializable import kotlinx.serialization.protobuf.ProtoIntegerType import kotlinx.serialization.protobuf.ProtoNumber @@ -19,7 +20,9 @@ import net.mamoe.mirai.internal.utils.io.ProtoBuf import net.mamoe.mirai.internal.utils.io.serialization.loadAs import net.mamoe.mirai.internal.utils.structureToStringIfAvailable import net.mamoe.mirai.utils.EMPTY_BYTE_ARRAY -import net.mamoe.mirai.utils.unzip +import net.mamoe.mirai.utils.inflate +import net.mamoe.mirai.utils.isSameType +import kotlin.jvm.JvmField @Serializable internal class ImCommon : ProtoBuf { @@ -555,7 +558,7 @@ internal class ImMsgBody : ProtoBuf { return when (byteArray[0].toInt()) { 0 -> byteArrayOf(0) + byteArray.decodeToString(startIndex = 1).toByteArray() - 1 -> byteArrayOf(0) + byteArray.unzip(offset = 1).decodeToString().toByteArray() + 1 -> byteArrayOf(0) + byteArray.inflate(offset = 1).decodeToString().toByteArray() else -> error("unknown compression flag=${byteArray[0]}") } } @@ -605,9 +608,7 @@ internal class ImMsgBody : ProtoBuf { @Suppress("DuplicatedCode") override fun equals(other: Any?): Boolean { if (this === other) return true - if (javaClass != other?.javaClass) return false - - other as MarketFace + if (!isSameType(this, other)) return false if (!faceName.contentEquals(other.faceName)) return false if (itemType != other.itemType) return false @@ -793,9 +794,7 @@ internal class ImMsgBody : ProtoBuf { ) : ProtoBuf { override fun equals(other: Any?): Boolean { if (this === other) return true - if (javaClass != other?.javaClass) return false - - other as Ptt + if (!isSameType(this, other)) return false if (fileType != other.fileType) return false if (srcUin != other.srcUin) return false diff --git a/mirai-core/src/commonMain/kotlin/network/protocol/data/proto/MsgCommon.kt b/mirai-core/src/commonMain/kotlin/network/protocol/data/proto/MsgCommon.kt index 286692285..d00679434 100644 --- a/mirai-core/src/commonMain/kotlin/network/protocol/data/proto/MsgCommon.kt +++ b/mirai-core/src/commonMain/kotlin/network/protocol/data/proto/MsgCommon.kt @@ -1,10 +1,10 @@ /* - * Copyright 2019-2021 Mamoe Technologies and contributors. + * Copyright 2019-2022 Mamoe Technologies and contributors. * - * 此源代码的使用受 GNU AFFERO GENERAL PUBLIC LICENSE version 3 许可证的约束, 可以在以下链接找到该许可证. - * Use of this source code is governed by the GNU AGPLv3 license that can be found through the following link. + * 此源代码的使用受 GNU AFFERO GENERAL PUBLIC LICENSE version 3 许可证的约束, 可以在以下链接找到该许可证. + * Use of this source code is governed by the GNU AGPLv3 license that can be found through the following link. * - * https://github.com/mamoe/mirai/blob/master/LICENSE + * https://github.com/mamoe/mirai/blob/dev/LICENSE */ package net.mamoe.mirai.internal.network.protocol.data.proto @@ -13,6 +13,7 @@ import kotlinx.serialization.Serializable import kotlinx.serialization.protobuf.ProtoNumber import net.mamoe.mirai.internal.utils.io.ProtoBuf import net.mamoe.mirai.utils.EMPTY_BYTE_ARRAY +import kotlin.jvm.JvmField /** * msf.msgcomm.msg_comm diff --git a/mirai-core/src/commonMain/kotlin/network/protocol/data/proto/MsgRevokeUserDef.kt b/mirai-core/src/commonMain/kotlin/network/protocol/data/proto/MsgRevokeUserDef.kt index fafb3bf45..243f052e8 100644 --- a/mirai-core/src/commonMain/kotlin/network/protocol/data/proto/MsgRevokeUserDef.kt +++ b/mirai-core/src/commonMain/kotlin/network/protocol/data/proto/MsgRevokeUserDef.kt @@ -1,5 +1,5 @@ /* - * Copyright 2019-2021 Mamoe Technologies and contributors. + * Copyright 2019-2022 Mamoe Technologies and contributors. * * 此源代码的使用受 GNU AFFERO GENERAL PUBLIC LICENSE version 3 许可证的约束, 可以在以下链接找到该许可证. * Use of this source code is governed by the GNU AGPLv3 license that can be found through the following link. @@ -12,6 +12,7 @@ package net.mamoe.mirai.internal.network.protocol.data.proto import kotlinx.serialization.Serializable import kotlinx.serialization.protobuf.ProtoNumber import net.mamoe.mirai.internal.utils.io.ProtoBuf +import kotlin.jvm.JvmField internal class MsgRevokeUserDef : ProtoBuf { @Serializable diff --git a/mirai-core/src/commonMain/kotlin/network/protocol/data/proto/MsgSvc.kt b/mirai-core/src/commonMain/kotlin/network/protocol/data/proto/MsgSvc.kt index 628925acf..4118da716 100644 --- a/mirai-core/src/commonMain/kotlin/network/protocol/data/proto/MsgSvc.kt +++ b/mirai-core/src/commonMain/kotlin/network/protocol/data/proto/MsgSvc.kt @@ -1,10 +1,10 @@ /* - * Copyright 2019-2021 Mamoe Technologies and contributors. + * Copyright 2019-2022 Mamoe Technologies and contributors. * - * 此源代码的使用受 GNU AFFERO GENERAL PUBLIC LICENSE version 3 许可证的约束, 可以在以下链接找到该许可证. - * Use of this source code is governed by the GNU AGPLv3 license that can be found through the following link. + * 此源代码的使用受 GNU AFFERO GENERAL PUBLIC LICENSE version 3 许可证的约束, 可以在以下链接找到该许可证. + * Use of this source code is governed by the GNU AGPLv3 license that can be found through the following link. * - * https://github.com/mamoe/mirai/blob/master/LICENSE + * https://github.com/mamoe/mirai/blob/dev/LICENSE */ package net.mamoe.mirai.internal.network.protocol.data.proto @@ -15,6 +15,7 @@ import net.mamoe.mirai.internal.network.Packet import net.mamoe.mirai.internal.utils.io.ProtoBuf import net.mamoe.mirai.utils.CheckableResponseB import net.mamoe.mirai.utils.EMPTY_BYTE_ARRAY +import kotlin.jvm.JvmField @Serializable internal class MsgSvc : ProtoBuf { diff --git a/mirai-core/src/commonMain/kotlin/network/protocol/data/proto/MsgTransmit.kt b/mirai-core/src/commonMain/kotlin/network/protocol/data/proto/MsgTransmit.kt index 62c7c215e..96637f3fa 100644 --- a/mirai-core/src/commonMain/kotlin/network/protocol/data/proto/MsgTransmit.kt +++ b/mirai-core/src/commonMain/kotlin/network/protocol/data/proto/MsgTransmit.kt @@ -1,10 +1,10 @@ /* - * Copyright 2019-2021 Mamoe Technologies and contributors. + * Copyright 2019-2022 Mamoe Technologies and contributors. * - * 此源代码的使用受 GNU AFFERO GENERAL PUBLIC LICENSE version 3 许可证的约束, 可以在以下链接找到该许可证. - * Use of this source code is governed by the GNU AGPLv3 license that can be found through the following link. + * 此源代码的使用受 GNU AFFERO GENERAL PUBLIC LICENSE version 3 许可证的约束, 可以在以下链接找到该许可证. + * Use of this source code is governed by the GNU AGPLv3 license that can be found through the following link. * - * https://github.com/mamoe/mirai/blob/master/LICENSE + * https://github.com/mamoe/mirai/blob/dev/LICENSE */ package net.mamoe.mirai.internal.network.protocol.data.proto @@ -13,6 +13,7 @@ import kotlinx.serialization.Serializable import kotlinx.serialization.protobuf.ProtoNumber import net.mamoe.mirai.internal.utils.io.ProtoBuf import net.mamoe.mirai.utils.EMPTY_BYTE_ARRAY +import kotlin.jvm.JvmField internal class MsgTransmit : ProtoBuf { @Serializable diff --git a/mirai-core/src/commonMain/kotlin/network/protocol/data/proto/MultiMsg.kt b/mirai-core/src/commonMain/kotlin/network/protocol/data/proto/MultiMsg.kt index 135feac08..4996cad9a 100644 --- a/mirai-core/src/commonMain/kotlin/network/protocol/data/proto/MultiMsg.kt +++ b/mirai-core/src/commonMain/kotlin/network/protocol/data/proto/MultiMsg.kt @@ -1,10 +1,10 @@ /* - * Copyright 2019-2021 Mamoe Technologies and contributors. + * Copyright 2019-2022 Mamoe Technologies and contributors. * - * 此源代码的使用受 GNU AFFERO GENERAL PUBLIC LICENSE version 3 许可证的约束, 可以在以下链接找到该许可证. - * Use of this source code is governed by the GNU AGPLv3 license that can be found through the following link. + * 此源代码的使用受 GNU AFFERO GENERAL PUBLIC LICENSE version 3 许可证的约束, 可以在以下链接找到该许可证. + * Use of this source code is governed by the GNU AGPLv3 license that can be found through the following link. * - * https://github.com/mamoe/mirai/blob/master/LICENSE + * https://github.com/mamoe/mirai/blob/dev/LICENSE */ package net.mamoe.mirai.internal.network.protocol.data.proto @@ -13,6 +13,7 @@ import kotlinx.serialization.Serializable import kotlinx.serialization.protobuf.ProtoNumber import net.mamoe.mirai.internal.utils.io.ProtoBuf import net.mamoe.mirai.utils.EMPTY_BYTE_ARRAY +import kotlin.jvm.JvmField @Serializable internal class MultiMsg : ProtoBuf { diff --git a/mirai-core/src/commonMain/kotlin/network/protocol/data/proto/OIDB.kt b/mirai-core/src/commonMain/kotlin/network/protocol/data/proto/OIDB.kt index b5c0d241d..0e766efe7 100644 --- a/mirai-core/src/commonMain/kotlin/network/protocol/data/proto/OIDB.kt +++ b/mirai-core/src/commonMain/kotlin/network/protocol/data/proto/OIDB.kt @@ -1,10 +1,10 @@ /* - * Copyright 2019-2021 Mamoe Technologies and contributors. + * Copyright 2019-2022 Mamoe Technologies and contributors. * - * 此源代码的使用受 GNU AFFERO GENERAL PUBLIC LICENSE version 3 许可证的约束, 可以在以下链接找到该许可证. - * Use of this source code is governed by the GNU AGPLv3 license that can be found through the following link. + * 此源代码的使用受 GNU AFFERO GENERAL PUBLIC LICENSE version 3 许可证的约束, 可以在以下链接找到该许可证. + * Use of this source code is governed by the GNU AGPLv3 license that can be found through the following link. * - * https://github.com/mamoe/mirai/blob/master/LICENSE + * https://github.com/mamoe/mirai/blob/dev/LICENSE */ package net.mamoe.mirai.internal.network.protocol.data.proto @@ -15,6 +15,7 @@ import net.mamoe.mirai.internal.network.Packet import net.mamoe.mirai.internal.utils.io.ProtoBuf import net.mamoe.mirai.utils.EMPTY_BYTE_ARRAY import net.mamoe.mirai.utils.capitalize +import kotlin.jvm.JvmField internal class Oidb0x5d4 : ProtoBuf { @Serializable diff --git a/mirai-core/src/commonMain/kotlin/network/protocol/data/proto/Oidb0x6d6.kt b/mirai-core/src/commonMain/kotlin/network/protocol/data/proto/Oidb0x6d6.kt index edc5425bc..3bb9dc4aa 100644 --- a/mirai-core/src/commonMain/kotlin/network/protocol/data/proto/Oidb0x6d6.kt +++ b/mirai-core/src/commonMain/kotlin/network/protocol/data/proto/Oidb0x6d6.kt @@ -1,10 +1,10 @@ /* - * Copyright 2019-2021 Mamoe Technologies and contributors. + * Copyright 2019-2022 Mamoe Technologies and contributors. * - * 此源代码的使用受 GNU AFFERO GENERAL PUBLIC LICENSE version 3 许可证的约束, 可以在以下链接找到该许可证. - * Use of this source code is governed by the GNU AGPLv3 license that can be found through the following link. + * 此源代码的使用受 GNU AFFERO GENERAL PUBLIC LICENSE version 3 许可证的约束, 可以在以下链接找到该许可证. + * Use of this source code is governed by the GNU AGPLv3 license that can be found through the following link. * - * https://github.com/mamoe/mirai/blob/master/LICENSE + * https://github.com/mamoe/mirai/blob/dev/LICENSE */ @file:Suppress("unused", "SpellCheckingInspection") @@ -16,6 +16,7 @@ import kotlinx.serialization.protobuf.ProtoNumber import net.mamoe.mirai.internal.network.protocol.packet.chat.CheckableStruct import net.mamoe.mirai.internal.utils.io.ProtoBuf import net.mamoe.mirai.utils.EMPTY_BYTE_ARRAY +import kotlin.jvm.JvmField internal class Oidb0x6d6 : ProtoBuf { @Serializable diff --git a/mirai-core/src/commonMain/kotlin/network/protocol/data/proto/Oidb0x6d7.kt b/mirai-core/src/commonMain/kotlin/network/protocol/data/proto/Oidb0x6d7.kt index ddd215bc0..29bc1d559 100644 --- a/mirai-core/src/commonMain/kotlin/network/protocol/data/proto/Oidb0x6d7.kt +++ b/mirai-core/src/commonMain/kotlin/network/protocol/data/proto/Oidb0x6d7.kt @@ -1,10 +1,10 @@ /* - * Copyright 2019-2021 Mamoe Technologies and contributors. + * Copyright 2019-2022 Mamoe Technologies and contributors. * - * 此源代码的使用受 GNU AFFERO GENERAL PUBLIC LICENSE version 3 许可证的约束, 可以在以下链接找到该许可证. - * Use of this source code is governed by the GNU AGPLv3 license that can be found through the following link. + * 此源代码的使用受 GNU AFFERO GENERAL PUBLIC LICENSE version 3 许可证的约束, 可以在以下链接找到该许可证. + * Use of this source code is governed by the GNU AGPLv3 license that can be found through the following link. * - * https://github.com/mamoe/mirai/blob/master/LICENSE + * https://github.com/mamoe/mirai/blob/dev/LICENSE */ @file:Suppress("unused", "SpellCheckingInspection") @@ -15,6 +15,7 @@ import kotlinx.serialization.Serializable import kotlinx.serialization.protobuf.ProtoNumber import net.mamoe.mirai.internal.network.protocol.packet.chat.CheckableStruct import net.mamoe.mirai.internal.utils.io.ProtoBuf +import kotlin.jvm.JvmField internal class Oidb0x6d7 : ProtoBuf { @Serializable diff --git a/mirai-core/src/commonMain/kotlin/network/protocol/data/proto/Oidb0x6d8.kt b/mirai-core/src/commonMain/kotlin/network/protocol/data/proto/Oidb0x6d8.kt index 054c25df1..775ad90e6 100644 --- a/mirai-core/src/commonMain/kotlin/network/protocol/data/proto/Oidb0x6d8.kt +++ b/mirai-core/src/commonMain/kotlin/network/protocol/data/proto/Oidb0x6d8.kt @@ -1,10 +1,10 @@ /* - * Copyright 2019-2021 Mamoe Technologies and contributors. + * Copyright 2019-2022 Mamoe Technologies and contributors. * - * 此源代码的使用受 GNU AFFERO GENERAL PUBLIC LICENSE version 3 许可证的约束, 可以在以下链接找到该许可证. - * Use of this source code is governed by the GNU AGPLv3 license that can be found through the following link. + * 此源代码的使用受 GNU AFFERO GENERAL PUBLIC LICENSE version 3 许可证的约束, 可以在以下链接找到该许可证. + * Use of this source code is governed by the GNU AGPLv3 license that can be found through the following link. * - * https://github.com/mamoe/mirai/blob/master/LICENSE + * https://github.com/mamoe/mirai/blob/dev/LICENSE */ @file:Suppress("unused", "SpellCheckingInspection") @@ -15,6 +15,7 @@ import kotlinx.serialization.Serializable import kotlinx.serialization.protobuf.ProtoNumber import net.mamoe.mirai.internal.utils.io.ProtoBuf import net.mamoe.mirai.utils.EMPTY_BYTE_ARRAY +import kotlin.jvm.JvmField internal class Oidb0x6d8 : ProtoBuf { @Serializable diff --git a/mirai-core/src/commonMain/kotlin/network/protocol/data/proto/Oidb0x6d9.kt b/mirai-core/src/commonMain/kotlin/network/protocol/data/proto/Oidb0x6d9.kt index abce560bc..c9c9779bb 100644 --- a/mirai-core/src/commonMain/kotlin/network/protocol/data/proto/Oidb0x6d9.kt +++ b/mirai-core/src/commonMain/kotlin/network/protocol/data/proto/Oidb0x6d9.kt @@ -1,10 +1,10 @@ /* - * Copyright 2019-2021 Mamoe Technologies and contributors. + * Copyright 2019-2022 Mamoe Technologies and contributors. * - * 此源代码的使用受 GNU AFFERO GENERAL PUBLIC LICENSE version 3 许可证的约束, 可以在以下链接找到该许可证. - * Use of this source code is governed by the GNU AGPLv3 license that can be found through the following link. + * 此源代码的使用受 GNU AFFERO GENERAL PUBLIC LICENSE version 3 许可证的约束, 可以在以下链接找到该许可证. + * Use of this source code is governed by the GNU AGPLv3 license that can be found through the following link. * - * https://github.com/mamoe/mirai/blob/master/LICENSE + * https://github.com/mamoe/mirai/blob/dev/LICENSE */ @file:Suppress("unused", "SpellCheckingInspection") @@ -15,6 +15,7 @@ import kotlinx.serialization.Serializable import kotlinx.serialization.protobuf.ProtoNumber import net.mamoe.mirai.internal.utils.io.ProtoBuf import net.mamoe.mirai.utils.EMPTY_BYTE_ARRAY +import kotlin.jvm.JvmField internal class Oidb0x6d9 : ProtoBuf { @Serializable diff --git a/mirai-core/src/commonMain/kotlin/network/protocol/data/proto/Oidb0x769.kt b/mirai-core/src/commonMain/kotlin/network/protocol/data/proto/Oidb0x769.kt index f37becace..e30f2e814 100644 --- a/mirai-core/src/commonMain/kotlin/network/protocol/data/proto/Oidb0x769.kt +++ b/mirai-core/src/commonMain/kotlin/network/protocol/data/proto/Oidb0x769.kt @@ -1,10 +1,10 @@ /* - * Copyright 2019-2021 Mamoe Technologies and contributors. + * Copyright 2019-2022 Mamoe Technologies and contributors. * - * 此源代码的使用受 GNU AFFERO GENERAL PUBLIC LICENSE version 3 许可证的约束, 可以在以下链接找到该许可证. - * Use of this source code is governed by the GNU AGPLv3 license that can be found through the following link. + * 此源代码的使用受 GNU AFFERO GENERAL PUBLIC LICENSE version 3 许可证的约束, 可以在以下链接找到该许可证. + * Use of this source code is governed by the GNU AGPLv3 license that can be found through the following link. * - * https://github.com/mamoe/mirai/blob/master/LICENSE + * https://github.com/mamoe/mirai/blob/dev/LICENSE */ @file:Suppress("unused", "SpellCheckingInspection") @@ -15,6 +15,7 @@ import kotlinx.serialization.Serializable import kotlinx.serialization.protobuf.ProtoNumber import net.mamoe.mirai.internal.utils.io.ProtoBuf import net.mamoe.mirai.utils.EMPTY_BYTE_ARRAY +import kotlin.jvm.JvmField @Serializable internal class Oidb0x769 : ProtoBuf { diff --git a/mirai-core/src/commonMain/kotlin/network/protocol/data/proto/Oidb0xeac.kt b/mirai-core/src/commonMain/kotlin/network/protocol/data/proto/Oidb0xeac.kt index 116457273..c654b5cb9 100644 --- a/mirai-core/src/commonMain/kotlin/network/protocol/data/proto/Oidb0xeac.kt +++ b/mirai-core/src/commonMain/kotlin/network/protocol/data/proto/Oidb0xeac.kt @@ -1,10 +1,10 @@ /* - * Copyright 2019-2021 Mamoe Technologies and contributors. + * Copyright 2019-2022 Mamoe Technologies and contributors. * - * 此源代码的使用受 GNU AFFERO GENERAL PUBLIC LICENSE version 3 许可证的约束, 可以在以下链接找到该许可证. - * Use of this source code is governed by the GNU AGPLv3 license that can be found through the following link. + * 此源代码的使用受 GNU AFFERO GENERAL PUBLIC LICENSE version 3 许可证的约束, 可以在以下链接找到该许可证. + * Use of this source code is governed by the GNU AGPLv3 license that can be found through the following link. * - * https://github.com/mamoe/mirai/blob/master/LICENSE + * https://github.com/mamoe/mirai/blob/dev/LICENSE */ @file:Suppress("unused", "SpellCheckingInspection") @@ -15,6 +15,7 @@ import kotlinx.serialization.Serializable import kotlinx.serialization.protobuf.ProtoNumber import net.mamoe.mirai.internal.utils.io.ProtoBuf import net.mamoe.mirai.utils.EMPTY_BYTE_ARRAY +import kotlin.jvm.JvmField @Serializable internal class Oidb0xeac : ProtoBuf { diff --git a/mirai-core/src/commonMain/kotlin/network/protocol/data/proto/OidbCmd0xb77.kt b/mirai-core/src/commonMain/kotlin/network/protocol/data/proto/OidbCmd0xb77.kt index f63f7489a..7edc8804b 100644 --- a/mirai-core/src/commonMain/kotlin/network/protocol/data/proto/OidbCmd0xb77.kt +++ b/mirai-core/src/commonMain/kotlin/network/protocol/data/proto/OidbCmd0xb77.kt @@ -1,10 +1,10 @@ /* - * Copyright 2019-2021 Mamoe Technologies and contributors. + * Copyright 2019-2022 Mamoe Technologies and contributors. * - * 此源代码的使用受 GNU AFFERO GENERAL PUBLIC LICENSE version 3 许可证的约束, 可以在以下链接找到该许可证. - * Use of this source code is governed by the GNU AGPLv3 license that can be found through the following link. + * 此源代码的使用受 GNU AFFERO GENERAL PUBLIC LICENSE version 3 许可证的约束, 可以在以下链接找到该许可证. + * Use of this source code is governed by the GNU AGPLv3 license that can be found through the following link. * - * https://github.com/mamoe/mirai/blob/master/LICENSE + * https://github.com/mamoe/mirai/blob/dev/LICENSE */ @file:Suppress("unused", "SpellCheckingInspection") @@ -15,6 +15,7 @@ import kotlinx.serialization.Serializable import kotlinx.serialization.protobuf.ProtoNumber import net.mamoe.mirai.internal.network.Packet import net.mamoe.mirai.internal.utils.io.ProtoBuf +import kotlin.jvm.JvmField @Serializable internal class OidbCmd0xb77 : ProtoBuf { diff --git a/mirai-core/src/commonMain/kotlin/network/protocol/data/proto/OnlinePush.kt b/mirai-core/src/commonMain/kotlin/network/protocol/data/proto/OnlinePush.kt index 493fbafa1..233f4801f 100644 --- a/mirai-core/src/commonMain/kotlin/network/protocol/data/proto/OnlinePush.kt +++ b/mirai-core/src/commonMain/kotlin/network/protocol/data/proto/OnlinePush.kt @@ -1,10 +1,10 @@ /* - * Copyright 2019-2021 Mamoe Technologies and contributors. + * Copyright 2019-2022 Mamoe Technologies and contributors. * - * 此源代码的使用受 GNU AFFERO GENERAL PUBLIC LICENSE version 3 许可证的约束, 可以在以下链接找到该许可证. - * Use of this source code is governed by the GNU AGPLv3 license that can be found through the following link. + * 此源代码的使用受 GNU AFFERO GENERAL PUBLIC LICENSE version 3 许可证的约束, 可以在以下链接找到该许可证. + * Use of this source code is governed by the GNU AGPLv3 license that can be found through the following link. * - * https://github.com/mamoe/mirai/blob/master/LICENSE + * https://github.com/mamoe/mirai/blob/dev/LICENSE */ package net.mamoe.mirai.internal.network.protocol.data.proto @@ -13,6 +13,7 @@ import kotlinx.serialization.Serializable import kotlinx.serialization.protobuf.ProtoNumber import net.mamoe.mirai.internal.utils.io.ProtoBuf import net.mamoe.mirai.utils.EMPTY_BYTE_ARRAY +import kotlin.jvm.JvmField @Serializable internal class MsgOnlinePush { diff --git a/mirai-core/src/commonMain/kotlin/network/protocol/data/proto/PbReserve.kt b/mirai-core/src/commonMain/kotlin/network/protocol/data/proto/PbReserve.kt index 086854d01..02a45284d 100644 --- a/mirai-core/src/commonMain/kotlin/network/protocol/data/proto/PbReserve.kt +++ b/mirai-core/src/commonMain/kotlin/network/protocol/data/proto/PbReserve.kt @@ -1,10 +1,10 @@ /* - * Copyright 2019-2021 Mamoe Technologies and contributors. + * Copyright 2019-2022 Mamoe Technologies and contributors. * - * 此源代码的使用受 GNU AFFERO GENERAL PUBLIC LICENSE version 3 许可证的约束, 可以在以下链接找到该许可证. - * Use of this source code is governed by the GNU AGPLv3 license that can be found through the following link. + * 此源代码的使用受 GNU AFFERO GENERAL PUBLIC LICENSE version 3 许可证的约束, 可以在以下链接找到该许可证. + * Use of this source code is governed by the GNU AGPLv3 license that can be found through the following link. * - * https://github.com/mamoe/mirai/blob/master/LICENSE + * https://github.com/mamoe/mirai/blob/dev/LICENSE */ package net.mamoe.mirai.internal.network.protocol.data.proto @@ -13,6 +13,7 @@ import kotlinx.serialization.Serializable import kotlinx.serialization.protobuf.ProtoNumber import net.mamoe.mirai.internal.utils.io.ProtoBuf import net.mamoe.mirai.utils.EMPTY_BYTE_ARRAY +import kotlin.jvm.JvmField internal class NotOnlineImage { @Serializable diff --git a/mirai-core/src/commonMain/kotlin/network/protocol/data/proto/StatSvcGetOnline.kt b/mirai-core/src/commonMain/kotlin/network/protocol/data/proto/StatSvcGetOnline.kt index d168893de..efe41273f 100644 --- a/mirai-core/src/commonMain/kotlin/network/protocol/data/proto/StatSvcGetOnline.kt +++ b/mirai-core/src/commonMain/kotlin/network/protocol/data/proto/StatSvcGetOnline.kt @@ -1,5 +1,5 @@ /* - * Copyright 2019-2021 Mamoe Technologies and contributors. + * Copyright 2019-2022 Mamoe Technologies and contributors. * * 此源代码的使用受 GNU AFFERO GENERAL PUBLIC LICENSE version 3 许可证的约束, 可以在以下链接找到该许可证. * Use of this source code is governed by the GNU AGPLv3 license that can be found through the following link. @@ -12,6 +12,7 @@ package net.mamoe.mirai.internal.network.protocol.data.proto import kotlinx.serialization.Serializable import kotlinx.serialization.protobuf.ProtoNumber import net.mamoe.mirai.internal.utils.io.ProtoBuf +import kotlin.jvm.JvmField internal class StatSvcGetOnline { @Serializable diff --git a/mirai-core/src/commonMain/kotlin/network/protocol/data/proto/StatSvcSimpleGet.kt b/mirai-core/src/commonMain/kotlin/network/protocol/data/proto/StatSvcSimpleGet.kt index b39d7a5d3..8ce7a0f0e 100644 --- a/mirai-core/src/commonMain/kotlin/network/protocol/data/proto/StatSvcSimpleGet.kt +++ b/mirai-core/src/commonMain/kotlin/network/protocol/data/proto/StatSvcSimpleGet.kt @@ -1,10 +1,10 @@ /* - * Copyright 2019-2021 Mamoe Technologies and contributors. + * Copyright 2019-2022 Mamoe Technologies and contributors. * - * 此源代码的使用受 GNU AFFERO GENERAL PUBLIC LICENSE version 3 许可证的约束, 可以在以下链接找到该许可证. - * Use of this source code is governed by the GNU AGPLv3 license that can be found through the following link. + * 此源代码的使用受 GNU AFFERO GENERAL PUBLIC LICENSE version 3 许可证的约束, 可以在以下链接找到该许可证. + * Use of this source code is governed by the GNU AGPLv3 license that can be found through the following link. * - * https://github.com/mamoe/mirai/blob/master/LICENSE + * https://github.com/mamoe/mirai/blob/dev/LICENSE */ package net.mamoe.mirai.internal.network.protocol.data.proto @@ -12,6 +12,7 @@ package net.mamoe.mirai.internal.network.protocol.data.proto import kotlinx.serialization.Serializable import kotlinx.serialization.protobuf.ProtoNumber import net.mamoe.mirai.internal.utils.io.ProtoBuf +import kotlin.jvm.JvmField internal class StatSvcSimpleGet { diff --git a/mirai-core/src/commonMain/kotlin/network/protocol/data/proto/StructMsg.kt b/mirai-core/src/commonMain/kotlin/network/protocol/data/proto/StructMsg.kt index 61dda98db..6f4ea362f 100644 --- a/mirai-core/src/commonMain/kotlin/network/protocol/data/proto/StructMsg.kt +++ b/mirai-core/src/commonMain/kotlin/network/protocol/data/proto/StructMsg.kt @@ -1,10 +1,10 @@ /* - * Copyright 2019-2021 Mamoe Technologies and contributors. + * Copyright 2019-2022 Mamoe Technologies and contributors. * - * 此源代码的使用受 GNU AFFERO GENERAL PUBLIC LICENSE version 3 许可证的约束, 可以在以下链接找到该许可证. - * Use of this source code is governed by the GNU AGPLv3 license that can be found through the following link. + * 此源代码的使用受 GNU AFFERO GENERAL PUBLIC LICENSE version 3 许可证的约束, 可以在以下链接找到该许可证. + * Use of this source code is governed by the GNU AGPLv3 license that can be found through the following link. * - * https://github.com/mamoe/mirai/blob/master/LICENSE + * https://github.com/mamoe/mirai/blob/dev/LICENSE */ package net.mamoe.mirai.internal.network.protocol.data.proto @@ -13,6 +13,7 @@ import kotlinx.serialization.Serializable import kotlinx.serialization.protobuf.ProtoNumber import net.mamoe.mirai.internal.utils.io.ProtoBuf import net.mamoe.mirai.utils.EMPTY_BYTE_ARRAY +import kotlin.jvm.JvmField internal class QPayReminderMsg : ProtoBuf { @Serializable diff --git a/mirai-core/src/commonMain/kotlin/network/protocol/data/proto/SyncCookie.kt b/mirai-core/src/commonMain/kotlin/network/protocol/data/proto/SyncCookie.kt index 1f419f481..f8d77190d 100644 --- a/mirai-core/src/commonMain/kotlin/network/protocol/data/proto/SyncCookie.kt +++ b/mirai-core/src/commonMain/kotlin/network/protocol/data/proto/SyncCookie.kt @@ -1,5 +1,5 @@ /* - * Copyright 2019-2021 Mamoe Technologies and contributors. + * Copyright 2019-2022 Mamoe Technologies and contributors. * * 此源代码的使用受 GNU AFFERO GENERAL PUBLIC LICENSE version 3 许可证的约束, 可以在以下链接找到该许可证. * Use of this source code is governed by the GNU AGPLv3 license that can be found through the following link. @@ -12,6 +12,7 @@ package net.mamoe.mirai.internal.network.protocol.data.proto import kotlinx.serialization.Serializable import kotlinx.serialization.protobuf.ProtoNumber import net.mamoe.mirai.internal.utils.io.ProtoBuf +import kotlin.jvm.JvmField import kotlin.math.absoluteValue import kotlin.random.Random diff --git a/mirai-core/src/commonMain/kotlin/network/protocol/data/proto/msgType0x210.kt b/mirai-core/src/commonMain/kotlin/network/protocol/data/proto/msgType0x210.kt index 3243024e5..8dc8b0402 100644 --- a/mirai-core/src/commonMain/kotlin/network/protocol/data/proto/msgType0x210.kt +++ b/mirai-core/src/commonMain/kotlin/network/protocol/data/proto/msgType0x210.kt @@ -1,10 +1,10 @@ /* - * Copyright 2019-2021 Mamoe Technologies and contributors. + * Copyright 2019-2022 Mamoe Technologies and contributors. * - * 此源代码的使用受 GNU AFFERO GENERAL PUBLIC LICENSE version 3 许可证的约束, 可以在以下链接找到该许可证. - * Use of this source code is governed by the GNU AGPLv3 license that can be found through the following link. + * 此源代码的使用受 GNU AFFERO GENERAL PUBLIC LICENSE version 3 许可证的约束, 可以在以下链接找到该许可证. + * Use of this source code is governed by the GNU AGPLv3 license that can be found through the following link. * - * https://github.com/mamoe/mirai/blob/master/LICENSE + * https://github.com/mamoe/mirai/blob/dev/LICENSE */ @file:Suppress("unused", "SpellCheckingInspection") @@ -17,6 +17,7 @@ import kotlinx.serialization.protobuf.ProtoNumber import kotlinx.serialization.protobuf.ProtoType import net.mamoe.mirai.internal.utils.io.ProtoBuf import net.mamoe.mirai.utils.EMPTY_BYTE_ARRAY +import kotlin.jvm.JvmField @Serializable internal class SubMsgType0x43 : ProtoBuf { diff --git a/mirai-core/src/commonMain/kotlin/network/protocol/data/proto/msgType0x211.kt b/mirai-core/src/commonMain/kotlin/network/protocol/data/proto/msgType0x211.kt index 37974a3a3..fe9c4be35 100644 --- a/mirai-core/src/commonMain/kotlin/network/protocol/data/proto/msgType0x211.kt +++ b/mirai-core/src/commonMain/kotlin/network/protocol/data/proto/msgType0x211.kt @@ -1,10 +1,10 @@ /* - * Copyright 2019-2021 Mamoe Technologies and contributors. + * Copyright 2019-2022 Mamoe Technologies and contributors. * - * 此源代码的使用受 GNU AFFERO GENERAL PUBLIC LICENSE version 3 许可证的约束, 可以在以下链接找到该许可证. - * Use of this source code is governed by the GNU AGPLv3 license that can be found through the following link. + * 此源代码的使用受 GNU AFFERO GENERAL PUBLIC LICENSE version 3 许可证的约束, 可以在以下链接找到该许可证. + * Use of this source code is governed by the GNU AGPLv3 license that can be found through the following link. * - * https://github.com/mamoe/mirai/blob/master/LICENSE + * https://github.com/mamoe/mirai/blob/dev/LICENSE */ @file:Suppress("unused", "SpellCheckingInspection") @@ -17,6 +17,7 @@ import kotlinx.serialization.protobuf.ProtoNumber import kotlinx.serialization.protobuf.ProtoType import net.mamoe.mirai.internal.utils.io.ProtoBuf import net.mamoe.mirai.utils.EMPTY_BYTE_ARRAY +import kotlin.jvm.JvmField @Serializable diff --git a/mirai-core/src/commonMain/kotlin/network/protocol/data/richstatus/RichStatus.kt b/mirai-core/src/commonMain/kotlin/network/protocol/data/richstatus/RichStatus.kt index c0a75ac03..bebdfe03e 100644 --- a/mirai-core/src/commonMain/kotlin/network/protocol/data/richstatus/RichStatus.kt +++ b/mirai-core/src/commonMain/kotlin/network/protocol/data/richstatus/RichStatus.kt @@ -1,5 +1,5 @@ /* - * Copyright 2019-2021 Mamoe Technologies and contributors. + * Copyright 2019-2022 Mamoe Technologies and contributors. * * 此源代码的使用受 GNU AFFERO GENERAL PUBLIC LICENSE version 3 许可证的约束, 可以在以下链接找到该许可证. * Use of this source code is governed by the GNU AGPLv3 license that can be found through the following link. @@ -9,10 +9,7 @@ package net.mamoe.mirai.internal.network.protocol.data.richstatus -import net.mamoe.mirai.utils.pos -import net.mamoe.mirai.utils.toIntUnsigned -import java.nio.ByteBuffer -import java.nio.ByteOrder +import kotlin.jvm.JvmField internal class RichStatus( @JvmField var actId: Int = 0, @@ -59,127 +56,11 @@ internal class RichStatus( } companion object { - @Suppress("UsePropertyAccessSyntax") fun parseStatus(rawData: ByteArray?): RichStatus { - val rsp = RichStatus() - - if (rawData == null || rawData.size <= 2) return rsp - - val byteBuffer = ByteBuffer.wrap(rawData).order(ByteOrder.BIG_ENDIAN) - - var lastPosition = 0 - var lastStringData: String? = null - - while (byteBuffer.remaining() >= 2) { - val dataType = byteBuffer.get().toIntUnsigned() - val dataLength = byteBuffer.get().toIntUnsigned() - - if (byteBuffer.remaining() < dataLength) break - - val dataStartPosition = lastPosition + 2 - - // Origin: dataType > 0 && dataType < 128 - if (dataType in 1..127) { - val dataContent = String(rawData, dataStartPosition, dataLength) - lastPosition = dataStartPosition + dataLength - byteBuffer.pos = lastPosition - - when (dataType) { - 1 -> rsp.actionText = dataContent - 2 -> rsp.dataText = dataContent - 4 -> { - if (lastStringData != null) { - rsp.addPlainText(lastStringData) - lastStringData = null - } - if (rsp.plainText != null) { - rsp.locationPosition = rsp.plainText!!.size - } else { - rsp.locationPosition = 0 - } - rsp.locationText = dataContent - } - else -> { - if (lastStringData == null) { - lastStringData = dataContent - } else { - lastStringData += dataContent - } - } - } - } else { - kotlin.run theSwitch@{ - when (dataType) { - 129 -> { - if (byteBuffer.remaining() >= 8) { - rsp.actionId = byteBuffer.getInt() - rsp.dataId = byteBuffer.getInt() - } - } - 130 -> { - if (byteBuffer.remaining() >= 8) { - rsp.lontitude = byteBuffer.getInt() - rsp.latitude = byteBuffer.getInt() - } - } - 144 -> rsp.feedsId = String(rawData, dataStartPosition, dataLength) - 145 -> rsp.tplId = byteBuffer.getInt() - 146 -> rsp.tplType = byteBuffer.getInt() - 147 -> rsp.actId = byteBuffer.getInt() - 148 -> { - if (byteBuffer.remaining() >= 4) { - lastPosition = byteBuffer.getInt() - /* - if (var1 > 4) { - var19 = String(var0, var5+4, var1-4) - if (var19.isNotEmpty()) { - var9.topics.add(Pair(var2, var19)) - } - } - */ - } - } - 149 -> { - if (byteBuffer.remaining() >= 5) { - lastPosition = dataLength - while (true) { - if (lastPosition < 5) return@theSwitch - - byteBuffer.getInt() - byteBuffer.get().toIntUnsigned() - - // var9.topicsPos.add(new Pair(var6, var3)); - lastPosition -= 5 - } - } - } - 161 -> { - /* - val var11 = ByteArray(dataLength) - byteBuffer.get(var11) - */ - byteBuffer.pos += dataLength - // Parse richstatus_sticker$RichStatus_Sticker - } - 162 -> { - rsp.fontId = byteBuffer.getInt() - } - 163 -> { - rsp.fontType = byteBuffer.getInt() - } - - } - } - lastPosition = dataStartPosition + dataLength - byteBuffer.pos = lastPosition - } - } - - if (lastStringData != null) { - rsp.addPlainText(lastStringData) - } - - return rsp + return parseRichStatusImpl(rawData) } } } + + +internal expect fun parseRichStatusImpl(rawData: ByteArray?): RichStatus \ No newline at end of file diff --git a/mirai-core/src/commonMain/kotlin/network/protocol/packet/EncryptMethod.kt b/mirai-core/src/commonMain/kotlin/network/protocol/packet/EncryptMethod.kt index b29206a1a..967ca5942 100644 --- a/mirai-core/src/commonMain/kotlin/network/protocol/packet/EncryptMethod.kt +++ b/mirai-core/src/commonMain/kotlin/network/protocol/packet/EncryptMethod.kt @@ -9,10 +9,7 @@ package net.mamoe.mirai.internal.network.protocol.packet -import io.ktor.utils.io.core.BytePacketBuilder -import io.ktor.utils.io.core.ByteReadPacket -import io.ktor.utils.io.core.buildPacket -import io.ktor.utils.io.core.writeFully +import io.ktor.utils.io.core.* import net.mamoe.mirai.internal.network.QQAndroidClient import net.mamoe.mirai.internal.utils.crypto.ECDHKeyPair import net.mamoe.mirai.internal.utils.crypto.ECDHWithPublicKey diff --git a/mirai-core/src/commonMain/kotlin/network/protocol/packet/OutgoingPacket.kt b/mirai-core/src/commonMain/kotlin/network/protocol/packet/OutgoingPacket.kt index 0c789aea8..56ee915e9 100644 --- a/mirai-core/src/commonMain/kotlin/network/protocol/packet/OutgoingPacket.kt +++ b/mirai-core/src/commonMain/kotlin/network/protocol/packet/OutgoingPacket.kt @@ -94,7 +94,7 @@ internal inline fun OutgoingPacketFactory.buildOutgoingUniPacke writeByte(0) client.uin.toString().let { writeInt(it.length + 4) - writeStringUtf8(it) + writeText(it) } encryptAndWrite(key) { writeUniPacket(commandName, client.outgoingPacketSessionId, extraData) { @@ -125,7 +125,7 @@ internal inline fun IncomingPacketFactory.buildResponseUniPacke writeByte(0) client.uin.toString().let { writeInt(it.length + 4) - writeStringUtf8(it) + writeText(it) } encryptAndWrite(key) { writeUniPacket(commandName, client.outgoingPacketSessionId, extraData) { @@ -146,7 +146,7 @@ private inline fun BytePacketBuilder.writeUniPacket( writeIntLVPacket(lengthOffset = { it + 4 }) { commandName.let { writeInt(it.length + 4) - writeStringUtf8(it) + writeText(it) } writeInt(4 + 4) @@ -192,7 +192,7 @@ internal inline fun OutgoingPacketFactory.buildLoginOutgoingPac client.uin.toString().let { writeInt(it.length + 4) - writeStringUtf8(it) + writeText(it) } if (key === NO_ENCRYPT) { @@ -247,7 +247,7 @@ internal inline fun BytePacketBuilder.writeSsoPacket( } commandName.let { writeInt(it.length + 4) - writeStringUtf8(it) + writeText(it) } writeInt(4 + 4) @@ -255,7 +255,7 @@ internal inline fun BytePacketBuilder.writeSsoPacket( client.device.imei.let { writeInt(it.length + 4) - writeStringUtf8(it) + writeText(it) } writeInt(4) diff --git a/mirai-core/src/commonMain/kotlin/network/protocol/packet/PacketFactory.kt b/mirai-core/src/commonMain/kotlin/network/protocol/packet/PacketFactory.kt index 034ab47be..a4169ae12 100644 --- a/mirai-core/src/commonMain/kotlin/network/protocol/packet/PacketFactory.kt +++ b/mirai-core/src/commonMain/kotlin/network/protocol/packet/PacketFactory.kt @@ -29,6 +29,7 @@ import net.mamoe.mirai.internal.network.protocol.packet.login.WtLogin import net.mamoe.mirai.internal.network.protocol.packet.summarycard.SummaryCard import net.mamoe.mirai.utils.DeprecatedSinceMirai import net.mamoe.mirai.utils.MiraiLoggerWithSwitch +import kotlin.jvm.JvmName internal sealed class PacketFactory { /** diff --git a/mirai-core/src/commonMain/kotlin/network/protocol/packet/Tlv.kt b/mirai-core/src/commonMain/kotlin/network/protocol/packet/Tlv.kt index 85c7b2396..70e4d1a6b 100644 --- a/mirai-core/src/commonMain/kotlin/network/protocol/packet/Tlv.kt +++ b/mirai-core/src/commonMain/kotlin/network/protocol/packet/Tlv.kt @@ -20,6 +20,7 @@ import net.mamoe.mirai.internal.utils.NetworkType import net.mamoe.mirai.internal.utils.guidFlag import net.mamoe.mirai.internal.utils.io.* import net.mamoe.mirai.utils.* +import kotlin.jvm.JvmInline import kotlin.random.Random private val Char.isHumanReadable get() = this in '0'..'9' || this in 'a'..'z' || this in 'A'..'Z' || this in """ <>?,.";':/\][{}~!@#$%^&*()_+-=`""" || this in "\n\r" diff --git a/mirai-core/src/commonMain/kotlin/network/protocol/packet/chat/receive/MessageSvc.PbSendMsg.kt b/mirai-core/src/commonMain/kotlin/network/protocol/packet/chat/receive/MessageSvc.PbSendMsg.kt index aecaafc88..10557cc3a 100644 --- a/mirai-core/src/commonMain/kotlin/network/protocol/packet/chat/receive/MessageSvc.PbSendMsg.kt +++ b/mirai-core/src/commonMain/kotlin/network/protocol/packet/chat/receive/MessageSvc.PbSendMsg.kt @@ -42,7 +42,6 @@ import net.mamoe.mirai.internal.utils.io.serialization.readProtoBuf import net.mamoe.mirai.internal.utils.io.serialization.writeProtoBuf import net.mamoe.mirai.message.data.* import net.mamoe.mirai.utils.getRandomUnsignedInt -import java.util.concurrent.atomic.AtomicReference import kotlin.contracts.InvocationKind import kotlin.contracts.contract import kotlin.math.absoluteValue @@ -57,6 +56,7 @@ internal object MessageSvcPbSendMsg : OutgoingPacketFactory { + lateinit var value: T + } + internal inline fun buildOutgoingMessageCommon( client: QQAndroidClient, message: MessageChain, @@ -115,9 +119,9 @@ internal object MessageSvcPbSendMsg : OutgoingPacketFactory MsgSvc.PbSendMsgReq, - sequenceIds: AtomicReference, + sequenceIds: LateinitBox, sequenceIdsInitializer: (Int) -> IntArray, - randIds: AtomicReference, + randIds: LateinitBox, doFragmented: Boolean = true, postInit: () -> Unit, ): List { @@ -131,8 +135,9 @@ internal object MessageSvcPbSendMsg : OutgoingPacketFactory response.add( @@ -190,8 +195,8 @@ internal object MessageSvcPbSendMsg : OutgoingPacketFactory Unit, ): List { - val sequenceIds = AtomicReference() - val randIds = AtomicReference() + val sequenceIds = LateinitBox() + val randIds = LateinitBox() return buildOutgoingMessageCommon( client = client, message = message, @@ -221,11 +226,11 @@ internal object MessageSvcPbSendMsg : OutgoingPacketFactory() - val randIds = AtomicReference() + val sequenceIds = LateinitBox() + val randIds = LateinitBox() return buildOutgoingMessageCommon( client = client, message = message, @@ -286,11 +291,11 @@ internal object MessageSvcPbSendMsg : OutgoingPacketFactory Unit, ): List { - val sequenceIds = AtomicReference() - val randIds = AtomicReference() + val sequenceIds = LateinitBox() + val randIds = LateinitBox() return buildOutgoingMessageCommon( client = client, message = message, @@ -416,11 +421,11 @@ internal object MessageSvcPbSendMsg : OutgoingPacketFactory client.syncController.syncGroupMessageReceipt(id) } + randIds.value.forEach { id -> client.syncController.syncGroupMessageReceipt(id) } sourceCallback( OnlineMessageSourceToGroupImpl( targetGroup, - internalIds = randIds.get(), + internalIds = randIds.value, sender = client.bot, target = targetGroup, time = client.bot.clock.server.currentTimeSeconds().toInt(), diff --git a/mirai-core/src/commonMain/kotlin/network/protocol/packet/chat/receive/MessageSvc.PushForceOffline.kt b/mirai-core/src/commonMain/kotlin/network/protocol/packet/chat/receive/MessageSvc.PushForceOffline.kt index 93530ab20..0fae50485 100644 --- a/mirai-core/src/commonMain/kotlin/network/protocol/packet/chat/receive/MessageSvc.PushForceOffline.kt +++ b/mirai-core/src/commonMain/kotlin/network/protocol/packet/chat/receive/MessageSvc.PushForceOffline.kt @@ -13,7 +13,7 @@ import io.ktor.utils.io.core.* import net.mamoe.mirai.internal.QQAndroidBot import net.mamoe.mirai.internal.network.components.AccountSecretsManager import net.mamoe.mirai.internal.network.components.BotInitProcessor -import net.mamoe.mirai.internal.network.impl.netty.ForceOfflineException +import net.mamoe.mirai.internal.network.impl.ForceOfflineException import net.mamoe.mirai.internal.network.protocol.data.jce.RequestPushForceOffline import net.mamoe.mirai.internal.network.protocol.packet.OutgoingPacketFactory import net.mamoe.mirai.internal.utils.io.serialization.readUniPacket diff --git a/mirai-core/src/commonMain/kotlin/network/protocol/packet/login/StatSvc.kt b/mirai-core/src/commonMain/kotlin/network/protocol/packet/login/StatSvc.kt index 4a86a01b2..c0f942f9c 100644 --- a/mirai-core/src/commonMain/kotlin/network/protocol/packet/login/StatSvc.kt +++ b/mirai-core/src/commonMain/kotlin/network/protocol/packet/login/StatSvc.kt @@ -9,11 +9,11 @@ package net.mamoe.mirai.internal.network.protocol.packet.login +import io.ktor.utils.io.core.* import kotlinx.coroutines.CancellationException import kotlinx.coroutines.cancel import kotlinx.coroutines.delay import kotlinx.coroutines.sync.withLock -import io.ktor.utils.io.core.ByteReadPacket import kotlinx.serialization.protobuf.ProtoBuf import net.mamoe.mirai.Mirai import net.mamoe.mirai.contact.ClientKind @@ -34,7 +34,7 @@ import net.mamoe.mirai.internal.network.components.ServerList import net.mamoe.mirai.internal.network.getRandomByteArray import net.mamoe.mirai.internal.network.handler.logger import net.mamoe.mirai.internal.network.handler.selector.NetworkException -import net.mamoe.mirai.internal.network.impl.netty.HeartbeatFailedException +import net.mamoe.mirai.internal.network.impl.HeartbeatFailedException import net.mamoe.mirai.internal.network.protocol.data.jce.* import net.mamoe.mirai.internal.network.protocol.data.proto.Oidb0x769 import net.mamoe.mirai.internal.network.protocol.data.proto.StatSvcGetOnline @@ -43,7 +43,6 @@ import net.mamoe.mirai.internal.network.protocol.packet.* import net.mamoe.mirai.internal.utils.NetworkType import net.mamoe.mirai.internal.utils.io.serialization.* import net.mamoe.mirai.internal.utils.structureToString -import net.mamoe.mirai.internal.utils.toIpV4Long import net.mamoe.mirai.utils.* @Suppress("EnumEntryName", "unused") @@ -418,3 +417,10 @@ internal class StatSvc { } } } + +internal fun String.toIpV4Long(): Long { + if (isEmpty()) return 0 + val split = split('.') + if (split.size != 4) return 0 + return split.mapToByteArray { it.toByte() }.toInt().toLongUnsigned() +} diff --git a/mirai-core/src/commonMain/kotlin/network/protocol/packet/login/WtLogin.kt b/mirai-core/src/commonMain/kotlin/network/protocol/packet/login/WtLogin.kt index d718366e0..3d643fce3 100644 --- a/mirai-core/src/commonMain/kotlin/network/protocol/packet/login/WtLogin.kt +++ b/mirai-core/src/commonMain/kotlin/network/protocol/packet/login/WtLogin.kt @@ -388,24 +388,24 @@ internal class WtLogin { if (client.wLoginSigInfoInitialized) { client.wLoginSigInfo.apply { - superKey = tlvMap119.getOrDefault(0x16d, superKey) + superKey = tlvMap119.getOrElse(0x16d) { superKey } d2 = KeyWithExpiry( - tlvMap119.getOrDefault(0x143, d2.data), + tlvMap119.getOrElse(0x143) { d2.data }, creationTime, - creationTime + changeTokenTimeMap.getOrDefault(0x143, 1728000L) + creationTime + changeTokenTimeMap.getOrElse(0x143) { 1728000L } ) - d2Key = tlvMap119.getOrDefault(0x305, d2Key) - tgt = tlvMap119.getOrDefault(0x10a, tgt) - tgtKey = tlvMap119.getOrDefault(0x10d, tgtKey) - a2ExpiryTime = creationTime + changeTokenTimeMap.getOrDefault(0x10a, 2160000L) + d2Key = tlvMap119.getOrElse(0x305) { d2Key } + tgt = tlvMap119.getOrElse(0x10a) { tgt } + tgtKey = tlvMap119.getOrElse(0x10d) { tgtKey } + a2ExpiryTime = creationTime + changeTokenTimeMap.getOrElse(0x10a) { 2160000L } userStWebSig = KeyWithExpiry( - tlvMap119.getOrDefault(0x103, userStWebSig.data), + tlvMap119.getOrElse(0x103) { userStWebSig.data }, creationTime, - creationTime + changeTokenTimeMap.getOrDefault(0x103, 6000L) + creationTime + changeTokenTimeMap.getOrElse(0x103) { 6000L } ) - userStKey = tlvMap119.getOrDefault(0x10e, userStKey) + userStKey = tlvMap119.getOrElse(0x10e) { userStKey } userStSig = - KeyWithCreationTime((tlvMap119.getOrDefault(0x114, userStSig.data)), creationTime) + KeyWithCreationTime((tlvMap119.getOrElse(0x114) { userStSig.data }), creationTime) appPri = tlvMap119[0x11f]?.let { it.read { //change interval (int time) @@ -417,22 +417,23 @@ internal class WtLogin { sKey = KeyWithExpiry( tlvMap119.getOrEmpty(0x120), creationTime, - creationTime + changeTokenTimeMap.getOrDefault(0x120, 86400L) + creationTime + changeTokenTimeMap.getOrElse(0x120) { 86400L } ) wtSessionTicket = KeyWithCreationTime( - tlvMap119.getOrDefault( - 0x133, + tlvMap119.getOrElse( + 0x133 + ) { client.wLoginSigInfo.wtSessionTicket.data - ), creationTime + }, creationTime ) - wtSessionTicketKey = tlvMap119.getOrDefault(0x134, client.wLoginSigInfo.wtSessionTicketKey) - deviceToken = tlvMap119.getOrDefault(0x322, deviceToken) + wtSessionTicketKey = tlvMap119.getOrElse(0x134) { client.wLoginSigInfo.wtSessionTicketKey } + deviceToken = tlvMap119.getOrElse(0x322) { deviceToken } encryptedDownloadSession = tlvMap119[0x11d]?.let { client.analysisTlv11d(it) } ?: encryptedDownloadSession - encryptA1 = tlvMap119.getOrDefault(0x106, encryptA1) - noPicSig = tlvMap119.getOrDefault(0x16a, noPicSig) + encryptA1 = tlvMap119.getOrElse(0x106) { encryptA1 } + noPicSig = tlvMap119.getOrElse(0x16a) { noPicSig } psKeyMap.putAll(outPSKeyMap.orEmpty().toMutableMap()) pt4TokenMap.putAll(outPt4TokenMap.orEmpty().toMutableMap()) } @@ -464,10 +465,11 @@ internal class WtLogin { } } ?: 4294967295L, // defaults {}, from asyncContext._G - a2ExpiryTime = creationTime + changeTokenTimeMap.getOrDefault( - 0x10a, + a2ExpiryTime = creationTime + changeTokenTimeMap.getOrElse( + 0x10a + ) { 2160000L - ), // or from asyncContext._t403.get_body_data() + }, // or from asyncContext._t403.get_body_data() loginBitmap = 0, tgt = tlvMap119.getOrFail(0x10a), a2CreationTime = creationTime, @@ -477,23 +479,23 @@ internal class WtLogin { userStWebSig = KeyWithExpiry( tlvMap119.getOrEmpty(0x103), creationTime, - creationTime + changeTokenTimeMap.getOrDefault(0x103, 6000L) + creationTime + changeTokenTimeMap.getOrElse(0x103) { 6000L } ), userA5 = KeyWithCreationTime(tlvMap119.getOrEmpty(0x10b), creationTime), userA8 = KeyWithExpiry( tlvMap119.getOrEmpty(0x102), creationTime, - creationTime + changeTokenTimeMap.getOrDefault(0x102, 72000L) + creationTime + changeTokenTimeMap.getOrElse(0x102) { 72000L } ), lsKey = KeyWithExpiry( tlvMap119.getOrEmpty(0x11c), creationTime, - creationTime + changeTokenTimeMap.getOrDefault(0x11c, 1641600L) + creationTime + changeTokenTimeMap.getOrElse(0x11c) { 1641600L } ), sKey = KeyWithExpiry( tlvMap119.getOrEmpty(0x120), creationTime, - creationTime + changeTokenTimeMap.getOrDefault(0x120, 86400L) + creationTime + changeTokenTimeMap.getOrElse(0x120) { 86400L } ), userSig64 = KeyWithCreationTime(tlvMap119.getOrEmpty(0x121), creationTime), openId = openId.orEmpty(), @@ -501,19 +503,19 @@ internal class WtLogin { vKey = KeyWithExpiry( tlvMap119.getOrEmpty(0x136), creationTime, - creationTime + changeTokenTimeMap.getOrDefault(0x136, 1728000L) + creationTime + changeTokenTimeMap.getOrElse(0x136) { 1728000L } ), accessToken = KeyWithCreationTime(tlvMap119.getOrEmpty(0x136), creationTime), d2 = KeyWithExpiry( tlvMap119.getOrFail(0x143), creationTime, - creationTime + changeTokenTimeMap.getOrDefault(0x143, 1728000L) + creationTime + changeTokenTimeMap.getOrElse(0x143) { 1728000L } ), d2Key = tlvMap119.getOrEmpty(0x305), sid = KeyWithExpiry( tlvMap119.getOrEmpty(0x164), creationTime, - creationTime + changeTokenTimeMap.getOrDefault(0x164, 1728000L) + creationTime + changeTokenTimeMap.getOrElse(0x164) { 1728000L } ), aqSig = KeyWithCreationTime(tlvMap119.getOrEmpty(0x171), creationTime), psKeyMap = outPSKeyMap.orEmpty().toMutableMap(), diff --git a/mirai-core/src/commonMain/kotlin/network/protocol/packet/login/wtlogin/WtLogin15.kt b/mirai-core/src/commonMain/kotlin/network/protocol/packet/login/wtlogin/WtLogin15.kt index 878154ab3..de72d1347 100644 --- a/mirai-core/src/commonMain/kotlin/network/protocol/packet/login/wtlogin/WtLogin15.kt +++ b/mirai-core/src/commonMain/kotlin/network/protocol/packet/login/wtlogin/WtLogin15.kt @@ -14,8 +14,8 @@ import net.mamoe.mirai.internal.network.* import net.mamoe.mirai.internal.network.protocol.packet.* import net.mamoe.mirai.internal.network.protocol.packet.login.WtLogin import net.mamoe.mirai.internal.utils.io.writeShortLVByteArray -import java.util.* import kotlin.math.abs +import kotlin.random.Random internal object WtLogin15 : WtLoginExt { private const val subCommand = 15.toShort() @@ -132,9 +132,8 @@ internal object WtLogin15 : WtLoginExt { } @Suppress("FunctionName", "SpellCheckingInspection") -internal fun get_mpasswd(): String { +internal fun get_mpasswd(random: Random = Random): String { var var5: String - val random = Random() run label41@{ val var6 = ByteArray(16) random.nextBytes(var6) @@ -145,7 +144,7 @@ internal fun get_mpasswd(): String { if (var0 >= var6.size) { return var5 } - val var3: Boolean = Random().nextBoolean() + val var3: Boolean = random.nextBoolean() val var2: Int = abs(var6[var0] % 26) val var1: Byte = if (var3) { diff --git a/mirai-core/src/commonMain/kotlin/network/protocol/packet/login/wtlogin/WtLogin20.kt b/mirai-core/src/commonMain/kotlin/network/protocol/packet/login/wtlogin/WtLogin20.kt index 612a3b92e..bbdaa692c 100644 --- a/mirai-core/src/commonMain/kotlin/network/protocol/packet/login/wtlogin/WtLogin20.kt +++ b/mirai-core/src/commonMain/kotlin/network/protocol/packet/login/wtlogin/WtLogin20.kt @@ -9,6 +9,7 @@ package net.mamoe.mirai.internal.network.protocol.packet.login.wtlogin +import io.ktor.utils.io.core.* import net.mamoe.mirai.internal.network.QQAndroidClient import net.mamoe.mirai.internal.network.miscBitMap import net.mamoe.mirai.internal.network.protocol.packet.* diff --git a/mirai-core/src/commonMain/kotlin/network/protocol/packet/login/wtlogin/WtLogin9.kt b/mirai-core/src/commonMain/kotlin/network/protocol/packet/login/wtlogin/WtLogin9.kt index f399bb469..ac0bb8e4c 100644 --- a/mirai-core/src/commonMain/kotlin/network/protocol/packet/login/wtlogin/WtLogin9.kt +++ b/mirai-core/src/commonMain/kotlin/network/protocol/packet/login/wtlogin/WtLogin9.kt @@ -9,7 +9,7 @@ package net.mamoe.mirai.internal.network.protocol.packet.login.wtlogin -import io.ktor.utils.io.core.toByteArray +import io.ktor.utils.io.core.* import net.mamoe.mirai.internal.network.* import net.mamoe.mirai.internal.network.protocol.packet.* import net.mamoe.mirai.internal.network.protocol.packet.login.WtLogin diff --git a/mirai-core/src/commonMain/kotlin/pipeline/ProcessorPipeline.kt b/mirai-core/src/commonMain/kotlin/pipeline/ProcessorPipeline.kt index f9ef20f1d..7ba46c52f 100644 --- a/mirai-core/src/commonMain/kotlin/pipeline/ProcessorPipeline.kt +++ b/mirai-core/src/commonMain/kotlin/pipeline/ProcessorPipeline.kt @@ -9,15 +9,14 @@ package net.mamoe.mirai.internal.pipeline +import io.ktor.util.collections.* +import io.ktor.utils.io.core.* import net.mamoe.mirai.internal.message.contextualBugReportException import net.mamoe.mirai.internal.message.protocol.outgoing.OutgoingMessagePipelineContext import net.mamoe.mirai.internal.network.components.NoticeProcessor import net.mamoe.mirai.internal.utils.structureToStringAndDesensitizeIfAvailable import net.mamoe.mirai.utils.* -import java.io.Closeable -import java.util.* -import java.util.concurrent.ConcurrentLinkedDeque -import java.util.concurrent.ConcurrentLinkedQueue +import kotlin.jvm.JvmInline internal interface Processor, D> : PipelineConsumptionMarker { val origin: Any get() = this @@ -156,24 +155,24 @@ internal abstract class AbstractProcessorPipelineContext( override val attributes: TypeSafeMap, private val traceLogging: MiraiLogger, ) : ProcessorPipelineContext { - private val consumers: Stack = Stack() + private val consumers: ArrayDeque = ArrayDeque() override val isConsumed: Boolean get() = consumers.isNotEmpty() override fun PipelineConsumptionMarker.markAsConsumed(marker: Any) { traceLogging.info { "markAsConsumed: marker=$marker" } - consumers.push(marker) + consumers.addFirst(marker) } override fun PipelineConsumptionMarker.markNotConsumed(marker: Any) { - if (consumers.peek() === marker) { - consumers.pop() + if (consumers.firstOrNull() === marker) { + consumers.removeFirst() traceLogging.info { "markNotConsumed: Y, marker=$marker" } } else { traceLogging.info { "markNotConsumed: N, marker=$marker" } } } - override val collected: MutablePipelineResult = MutablePipelineResult(ConcurrentLinkedQueue()) + override val collected: MutablePipelineResult = MutablePipelineResult(ConcurrentLinkedDeque()) override fun collect(result: R) { collected.data.add(result) @@ -200,7 +199,7 @@ protected constructor( /** * Must be ordered */ - override val processors: ConcurrentLinkedDeque> = ConcurrentLinkedDeque() + override val processors: MutableDeque> = ConcurrentLinkedDeque() override fun registerProcessor(processor: P): ProcessorPipeline.DisposableRegistry { val box = ProcessorBox(processor) @@ -212,7 +211,7 @@ protected constructor( override fun registerBefore(processor: P): ProcessorPipeline.DisposableRegistry { val box = ProcessorBox(processor) - processors.addFirst(box) + processors.add(box) return ProcessorPipeline.DisposableRegistry { processors.remove(box) } diff --git a/mirai-core/src/commonMain/kotlin/utils/AtomicIntSeq.kt b/mirai-core/src/commonMain/kotlin/utils/AtomicIntSeq.kt index e29aff6b5..d2c8d9299 100644 --- a/mirai-core/src/commonMain/kotlin/utils/AtomicIntSeq.kt +++ b/mirai-core/src/commonMain/kotlin/utils/AtomicIntSeq.kt @@ -1,10 +1,10 @@ /* - * Copyright 2019-2021 Mamoe Technologies and contributors. + * Copyright 2019-2022 Mamoe Technologies and contributors. * - * 此源代码的使用受 GNU AFFERO GENERAL PUBLIC LICENSE version 3 许可证的约束, 可以在以下链接找到该许可证. - * Use of this source code is governed by the GNU AGPLv3 license that can be found through the following link. + * 此源代码的使用受 GNU AFFERO GENERAL PUBLIC LICENSE version 3 许可证的约束, 可以在以下链接找到该许可证. + * Use of this source code is governed by the GNU AGPLv3 license that can be found through the following link. * - * https://github.com/mamoe/mirai/blob/master/LICENSE + * https://github.com/mamoe/mirai/blob/dev/LICENSE */ package net.mamoe.mirai.internal.utils @@ -13,6 +13,7 @@ import kotlinx.atomicfu.atomic import kotlinx.atomicfu.update import net.mamoe.mirai.utils.getRandomUnsignedInt import net.mamoe.mirai.utils.toLongUnsigned +import kotlin.jvm.JvmStatic // We probably can reduce duplicates by using value classes, but atomicFU compiler might not be able to compile it. diff --git a/mirai-core/src/commonMain/kotlin/utils/BotConfigurationExt.kt b/mirai-core/src/commonMain/kotlin/utils/BotConfigurationExt.kt index 8e0dd9a20..0d08e112f 100644 --- a/mirai-core/src/commonMain/kotlin/utils/BotConfigurationExt.kt +++ b/mirai-core/src/commonMain/kotlin/utils/BotConfigurationExt.kt @@ -1,23 +1,32 @@ /* - * Copyright 2019-2021 Mamoe Technologies and contributors. + * Copyright 2019-2022 Mamoe Technologies and contributors. * - * 此源代码的使用受 GNU AFFERO GENERAL PUBLIC LICENSE version 3 许可证的约束, 可以在以下链接找到该许可证. - * Use of this source code is governed by the GNU AGPLv3 license that can be found through the following link. + * 此源代码的使用受 GNU AFFERO GENERAL PUBLIC LICENSE version 3 许可证的约束, 可以在以下链接找到该许可证. + * Use of this source code is governed by the GNU AGPLv3 license that can be found through the following link. * - * https://github.com/mamoe/mirai/blob/master/LICENSE + * https://github.com/mamoe/mirai/blob/dev/LICENSE */ +@file:JvmName("BotConfigurationExt_common") + package net.mamoe.mirai.internal.utils import net.mamoe.mirai.utils.BotConfiguration +import net.mamoe.mirai.utils.MiraiFile import net.mamoe.mirai.utils.resolveCreateFile import net.mamoe.mirai.utils.resolveMkdir -import java.io.File +import kotlin.jvm.JvmName -internal fun BotConfiguration.actualCacheDir(): File = workingDir.resolveMkdir(cacheDir) -internal fun BotConfiguration.contactCacheDir(): File = actualCacheDir().resolveMkdir("contacts") -internal fun BotConfiguration.friendCacheFile(): File = contactCacheDir().resolveCreateFile("friends.json") -internal fun BotConfiguration.groupCacheDir(): File = contactCacheDir().resolveMkdir("groups") -internal fun BotConfiguration.groupCacheFile(groupId: Long): File = groupCacheDir().resolveCreateFile("$groupId.json") -internal fun BotConfiguration.accountSecretsFile(): File = actualCacheDir().resolve("account.secrets") \ No newline at end of file +internal expect val BotConfiguration.workingDirPath: String + +internal expect val BotConfiguration.cacheDirPath: String + +internal fun BotConfiguration.actualCacheDir(): MiraiFile = MiraiFile.create(workingDirPath).resolveMkdir(cacheDirPath) +internal fun BotConfiguration.contactCacheDir(): MiraiFile = actualCacheDir().resolveMkdir("contacts") +internal fun BotConfiguration.friendCacheFile(): MiraiFile = contactCacheDir().resolveCreateFile("friends.json") +internal fun BotConfiguration.groupCacheDir(): MiraiFile = contactCacheDir().resolveMkdir("groups") +internal fun BotConfiguration.groupCacheFile(groupId: Long): MiraiFile = + groupCacheDir().resolveCreateFile("$groupId.json") + +internal fun BotConfiguration.accountSecretsFile(): MiraiFile = actualCacheDir().resolve("account.secrets") \ No newline at end of file diff --git a/mirai-core/src/commonMain/kotlin/utils/FragmentedMsgParsingCache.kt b/mirai-core/src/commonMain/kotlin/utils/FragmentedMsgParsingCache.kt index 5085819a3..b32a762d8 100644 --- a/mirai-core/src/commonMain/kotlin/utils/FragmentedMsgParsingCache.kt +++ b/mirai-core/src/commonMain/kotlin/utils/FragmentedMsgParsingCache.kt @@ -1,19 +1,19 @@ /* - * Copyright 2019-2021 Mamoe Technologies and contributors. + * Copyright 2019-2022 Mamoe Technologies and contributors. * - * 此源代码的使用受 GNU AFFERO GENERAL PUBLIC LICENSE version 3 许可证的约束, 可以在以下链接找到该许可证. - * Use of this source code is governed by the GNU AGPLv3 license that can be found through the following link. + * 此源代码的使用受 GNU AFFERO GENERAL PUBLIC LICENSE version 3 许可证的约束, 可以在以下链接找到该许可证. + * Use of this source code is governed by the GNU AGPLv3 license that can be found through the following link. * - * https://github.com/mamoe/mirai/blob/master/LICENSE + * https://github.com/mamoe/mirai/blob/dev/LICENSE */ package net.mamoe.mirai.internal.utils +import kotlinx.atomicfu.locks.reentrantLock import kotlinx.atomicfu.locks.withLock import net.mamoe.mirai.internal.network.protocol.data.proto.MsgComm import net.mamoe.mirai.internal.network.protocol.data.proto.MsgOnlinePush import net.mamoe.mirai.utils.currentTimeMillis -import java.util.concurrent.locks.ReentrantLock /** * fragmented message @@ -28,9 +28,9 @@ internal abstract class FragmentedMsgParsingCache { } private val deque = ArrayList>(16) - private val accessLock = ReentrantLock() + private val accessLock = reentrantLock() private fun clearInvalid() { - deque.removeIf { + deque.removeAll { currentTimeMillis() - it.createTime > 10000L } } @@ -50,7 +50,7 @@ internal abstract class FragmentedMsgParsingCache { } ?: PkgMsg(size, seq, mutableMapOf()).also { deque.add(it) } pkgMsg.data[index] = msg if (pkgMsg.data.size == pkgMsg.size) { - deque.removeIf { it.divSeq == seq } + deque.removeAll { it.divSeq == seq } return pkgMsg.data.entries.asSequence() .sortedBy { it.key } .map { it.value } diff --git a/mirai-core/src/commonMain/kotlin/utils/GuidSource.kt b/mirai-core/src/commonMain/kotlin/utils/GuidSource.kt index 9e2a2b6e8..a6309809b 100644 --- a/mirai-core/src/commonMain/kotlin/utils/GuidSource.kt +++ b/mirai-core/src/commonMain/kotlin/utils/GuidSource.kt @@ -1,14 +1,17 @@ /* - * Copyright 2019-2021 Mamoe Technologies and contributors. + * Copyright 2019-2022 Mamoe Technologies and contributors. * - * 此源代码的使用受 GNU AFFERO GENERAL PUBLIC LICENSE version 3 许可证的约束, 可以在以下链接找到该许可证. - * Use of this source code is governed by the GNU AGPLv3 license that can be found through the following link. + * 此源代码的使用受 GNU AFFERO GENERAL PUBLIC LICENSE version 3 许可证的约束, 可以在以下链接找到该许可证. + * Use of this source code is governed by the GNU AGPLv3 license that can be found through the following link. * - * https://github.com/mamoe/mirai/blob/master/LICENSE + * https://github.com/mamoe/mirai/blob/dev/LICENSE */ package net.mamoe.mirai.internal.utils +import kotlin.jvm.JvmInline +import kotlin.jvm.JvmStatic + /** * GUID 来源 * diff --git a/mirai-core/src/commonMain/kotlin/utils/MiraiProtocolInternal.kt b/mirai-core/src/commonMain/kotlin/utils/MiraiProtocolInternal.kt index fe6b7f2a3..561851806 100644 --- a/mirai-core/src/commonMain/kotlin/utils/MiraiProtocolInternal.kt +++ b/mirai-core/src/commonMain/kotlin/utils/MiraiProtocolInternal.kt @@ -1,16 +1,17 @@ /* - * Copyright 2019-2021 Mamoe Technologies and contributors. + * Copyright 2019-2022 Mamoe Technologies and contributors. * - * 此源代码的使用受 GNU AFFERO GENERAL PUBLIC LICENSE version 3 许可证的约束, 可以在以下链接找到该许可证. - * Use of this source code is governed by the GNU AGPLv3 license that can be found through the following link. + * 此源代码的使用受 GNU AFFERO GENERAL PUBLIC LICENSE version 3 许可证的约束, 可以在以下链接找到该许可证. + * Use of this source code is governed by the GNU AGPLv3 license that can be found through the following link. * - * https://github.com/mamoe/mirai/blob/master/LICENSE + * https://github.com/mamoe/mirai/blob/dev/LICENSE */ package net.mamoe.mirai.internal.utils import net.mamoe.mirai.utils.BotConfiguration.MiraiProtocol -import java.util.* +import net.mamoe.mirai.utils.EnumMap +import kotlin.jvm.JvmField internal class MiraiProtocolInternal( @JvmField internal val apkId: String, @@ -25,9 +26,7 @@ internal class MiraiProtocolInternal( @JvmField internal val ssoVersion: Int, ) { internal companion object { - internal val protocols = EnumMap( - MiraiProtocol::class.java - ) + internal val protocols = EnumMap(MiraiProtocol::class) operator fun get(protocol: MiraiProtocol): MiraiProtocolInternal = protocols[protocol] ?: error("Internal Error: Missing protocol $protocol") diff --git a/mirai-core/src/commonMain/kotlin/utils/NetworkType.kt b/mirai-core/src/commonMain/kotlin/utils/NetworkType.kt index 98c8fabde..16ab0a1c3 100644 --- a/mirai-core/src/commonMain/kotlin/utils/NetworkType.kt +++ b/mirai-core/src/commonMain/kotlin/utils/NetworkType.kt @@ -1,14 +1,16 @@ /* - * Copyright 2019-2021 Mamoe Technologies and contributors. + * Copyright 2019-2022 Mamoe Technologies and contributors. * - * 此源代码的使用受 GNU AFFERO GENERAL PUBLIC LICENSE version 3 许可证的约束, 可以在以下链接找到该许可证. - * Use of this source code is governed by the GNU AGPLv3 license that can be found through the following link. + * 此源代码的使用受 GNU AFFERO GENERAL PUBLIC LICENSE version 3 许可证的约束, 可以在以下链接找到该许可证. + * Use of this source code is governed by the GNU AGPLv3 license that can be found through the following link. * - * https://github.com/mamoe/mirai/blob/master/LICENSE + * https://github.com/mamoe/mirai/blob/dev/LICENSE */ package net.mamoe.mirai.internal.utils +import kotlin.jvm.JvmInline + /** * 连接类型 */ diff --git a/mirai-core/src/commonMain/kotlin/utils/PlatformDatagramChannel.kt b/mirai-core/src/commonMain/kotlin/utils/PlatformDatagramChannel.kt index b7c4ae64d..691c21213 100644 --- a/mirai-core/src/commonMain/kotlin/utils/PlatformDatagramChannel.kt +++ b/mirai-core/src/commonMain/kotlin/utils/PlatformDatagramChannel.kt @@ -10,41 +10,34 @@ package net.mamoe.mirai.internal.utils import io.ktor.utils.io.core.* -import io.ktor.utils.io.nio.readPacketAtMost -import io.ktor.utils.io.nio.writePacket -import kotlinx.coroutines.Dispatchers -import kotlinx.coroutines.withContext -import java.net.InetSocketAddress -import java.nio.channels.DatagramChannel -import java.nio.channels.ReadableByteChannel -import java.nio.channels.WritableByteChannel -/** - * 多平台适配的 DatagramChannel. - */ -internal class PlatformDatagramChannel(serverHost: String, serverPort: Short) : Closeable { - @PublishedApi - internal val channel: DatagramChannel = - DatagramChannel.open().connect(InetSocketAddress(serverHost, serverPort.toInt())) - val isOpen: Boolean get() = channel.isOpen - override fun close() = channel.close() - - suspend inline fun send(packet: ByteReadPacket): Boolean = withContext(Dispatchers.IO) { - try { - (channel as WritableByteChannel).writePacket(packet) - } catch (e: Throwable) { - throw SendPacketInternalException(e) - } - } - - suspend inline fun read(): ByteReadPacket = withContext(Dispatchers.IO) { - try { - (channel as ReadableByteChannel).readPacketAtMost(Long.MAX_VALUE) - } catch (e: Throwable) { - throw ReadPacketInternalException(e) - } - } -} +// +///** +// * 多平台适配的 DatagramChannel. +// */ +//internal class PlatformDatagramChannel(serverHost: String, serverPort: Short) : Closeable { +// @PublishedApi +// internal val channel: DatagramChannel = +// DatagramChannel.open().connect(InetSocketAddress(serverHost, serverPort.toInt())) +// val isOpen: Boolean get() = channel.isOpen +// override fun close() = channel.close() +// +// suspend inline fun send(packet: ByteReadPacket): Boolean = withContext(Dispatchers.IO) { +// try { +// (channel as WritableByteChannel).writePacket(packet) +// } catch (e: Throwable) { +// throw SendPacketInternalException(e) +// } +// } +// +// suspend inline fun read(): ByteReadPacket = withContext(Dispatchers.IO) { +// try { +// (channel as ReadableByteChannel).readPacketAtMost(Long.MAX_VALUE) +// } catch (e: Throwable) { +// throw ReadPacketInternalException(e) +// } +// } +//} /* diff --git a/mirai-core/src/commonMain/kotlin/utils/PlatformSocket.kt b/mirai-core/src/commonMain/kotlin/utils/PlatformSocket.kt index 2a33db2b5..32e8f5882 100644 --- a/mirai-core/src/commonMain/kotlin/utils/PlatformSocket.kt +++ b/mirai-core/src/commonMain/kotlin/utils/PlatformSocket.kt @@ -10,117 +10,56 @@ package net.mamoe.mirai.internal.utils import io.ktor.utils.io.core.* -import io.ktor.utils.io.streams.readPacketAtMost -import io.ktor.utils.io.streams.writePacket -import kotlinx.coroutines.Dispatchers -import kotlinx.coroutines.runInterruptible -import kotlinx.coroutines.suspendCancellableCoroutine +import io.ktor.utils.io.errors.* import net.mamoe.mirai.internal.network.highway.HighwayProtocolChannel -import net.mamoe.mirai.utils.withUse -import java.io.BufferedInputStream -import java.io.BufferedOutputStream -import java.io.IOException -import java.net.Socket -import java.util.concurrent.Executors -import kotlin.contracts.InvocationKind -import kotlin.contracts.contract /** * TCP Socket. */ -internal class PlatformSocket : Closeable, HighwayProtocolChannel { - private lateinit var socket: Socket - +internal expect class PlatformSocket : Closeable, HighwayProtocolChannel { val isOpen: Boolean - get() = - if (::socket.isInitialized) - socket.isConnected - else false - override fun close() { - if (::socket.isInitialized) { - socket.close() - } - thread.shutdownNow() - kotlin.runCatching { writeChannel.close() } - kotlin.runCatching { readChannel.close() } - } + override fun close() - @PublishedApi - internal lateinit var writeChannel: BufferedOutputStream - - @PublishedApi - internal lateinit var readChannel: BufferedInputStream - - suspend fun send(packet: ByteArray, offset: Int, length: Int) { - runInterruptible(Dispatchers.IO) { - writeChannel.write(packet, offset, length) - writeChannel.flush() - } - } + suspend fun send(packet: ByteArray, offset: Int, length: Int) /** * @throws SendPacketInternalException */ - override suspend fun send(packet: ByteReadPacket) { - runInterruptible(Dispatchers.IO) { - try { - writeChannel.writePacket(packet) - writeChannel.flush() - } catch (e: IOException) { - throw SendPacketInternalException(e) - } - } - } - - private val thread = Executors.newSingleThreadExecutor() + override suspend fun send(packet: ByteReadPacket) /** * @throws ReadPacketInternalException */ - override suspend fun read(): ByteReadPacket = suspendCancellableCoroutine { cont -> - val task = thread.submit { - kotlin.runCatching { - readChannel.readPacketAtMost(Long.MAX_VALUE) - }.let { - cont.resumeWith(it) - } - } - cont.invokeOnCancellation { - kotlin.runCatching { task.cancel(true) } - } - } - - suspend fun connect(serverHost: String, serverPort: Int) { - runInterruptible(Dispatchers.IO) { - socket = Socket(serverHost, serverPort) - readChannel = socket.getInputStream().buffered() - writeChannel = socket.getOutputStream().buffered() - } - } + override suspend fun read(): ByteReadPacket + suspend fun connect(serverHost: String, serverPort: Int) companion object { suspend fun connect( serverIp: String, serverPort: Int, - ): PlatformSocket { - val socket = PlatformSocket() - socket.connect(serverIp, serverPort) - return socket - } + ): PlatformSocket suspend inline fun withConnection( serverIp: String, serverPort: Int, block: PlatformSocket.() -> R, - ): R { - contract { callsInPlace(block, InvocationKind.EXACTLY_ONCE) } - return connect(serverIp, serverPort).withUse(block) - } + ): R } } -internal typealias SocketException = java.net.SocketException -internal typealias NoRouteToHostException = NoRouteToHostException -internal typealias UnknownHostException = UnknownHostException \ No newline at end of file +internal expect class SocketException : IOException { + constructor() + constructor(message: String) +} + +internal expect class NoRouteToHostException : IOException { + constructor() + constructor(message: String) +} + +internal expect class UnknownHostException : IOException { + constructor() + constructor(message: String) +} \ No newline at end of file diff --git a/mirai-core/src/commonMain/kotlin/utils/RemoteFileImpl.kt b/mirai-core/src/commonMain/kotlin/utils/RemoteFileImpl.kt index 01a5b0005..112e81e42 100644 --- a/mirai-core/src/commonMain/kotlin/utils/RemoteFileImpl.kt +++ b/mirai-core/src/commonMain/kotlin/utils/RemoteFileImpl.kt @@ -11,6 +11,7 @@ package net.mamoe.mirai.internal.utils +import io.ktor.utils.io.core.* import kotlinx.coroutines.flow.* import kotlinx.coroutines.runBlocking import net.mamoe.mirai.contact.Contact @@ -30,11 +31,9 @@ import net.mamoe.mirai.internal.utils.io.serialization.toByteArray import net.mamoe.mirai.message.MessageReceipt import net.mamoe.mirai.message.data.FileMessage import net.mamoe.mirai.utils.* -import net.mamoe.mirai.utils.ExternalResource.Companion.toExternalResource import net.mamoe.mirai.utils.RemoteFile.Companion.ROOT_PATH -import java.io.File -import java.util.* import kotlin.contracts.contract +import kotlin.jvm.Volatile private val fs = FileSystem @@ -99,18 +98,23 @@ internal class RemoteFileInfo( } } -internal fun RemoteFile.checkIsImpl(): RemoteFileImpl { +internal fun RemoteFile.checkIsImpl(): CommonRemoteFileImpl { contract { returns() implies (this@checkIsImpl is RemoteFileImpl) } return this as? RemoteFileImpl ?: error("RemoteFile must not be implemented manually.") } -internal class RemoteFileImpl( +internal expect class RemoteFileImpl( + contact: Group, + path: String, // absolute +) : CommonRemoteFileImpl { + constructor(contact: Group, parent: String, name: String) +} + +internal abstract class CommonRemoteFileImpl( override val contact: Group, override val path: String, // absolute ) : RemoteFile { - constructor(contact: Group, parent: String, name: String) : this(contact, fs.normalize(parent, name)) - override var id: String? = null override val name: String @@ -119,7 +123,7 @@ internal class RemoteFileImpl( private val bot get() = contact.bot.asQQAndroidBot() private val client get() = bot.client - override val parent: RemoteFileImpl? + override val parent: CommonRemoteFileImpl? get() { if (path == ROOT_PATH) return null val s = path.substringBeforeLast('/') @@ -133,10 +137,10 @@ internal class RemoteFileImpl( var nameMatching: Oidb0x6d8.GetFileListRspBody.Item? = null val idMatching = firstOrNull { - if (it.name == this@RemoteFileImpl.name) { + if (it.name == this@CommonRemoteFileImpl.name) { nameMatching = it } - it.id == this@RemoteFileImpl.id + it.id == this@CommonRemoteFileImpl.id } return idMatching ?: nameMatching @@ -582,45 +586,11 @@ internal class RemoteFileImpl( return upload(resource, null) } - // compiler bug - @Deprecated( - "Use uploadAndSend instead.", - replaceWith = ReplaceWith("this.uploadAndSend(file, callback)"), - level = DeprecationLevel.ERROR - ) - @Suppress("DEPRECATION_ERROR") - override suspend fun upload(file: File, callback: RemoteFile.ProgressionCallback?): FileMessage = - file.toExternalResource().use { upload(it, callback) } - - //compiler bug - @Deprecated( - "Use sendFile instead.", - replaceWith = ReplaceWith("this.uploadAndSend(file)"), - level = DeprecationLevel.ERROR - ) - @Suppress("DEPRECATION_ERROR") - override suspend fun upload(file: File): FileMessage { - // Dear compiler: - // - // Please generate invokeinterface. - // - // Yours Sincerely - // Him188 - return file.toExternalResource().use { upload(it) } - } - override suspend fun uploadAndSend(resource: ExternalResource): MessageReceipt { @Suppress("DEPRECATION") return contact.sendMessage(uploadInternal(resource, null) + MiraiInternalMessageFlag) } - // compiler bug - override suspend fun uploadAndSend(file: File): MessageReceipt = - file.toExternalResource().use { uploadAndSend(it) } - - // override suspend fun writeSession(resource: ExternalResource): FileUploadSession { - // } - override suspend fun getDownloadInfo(): RemoteFile.DownloadInfo? { val info = getFileFolderInfo() ?: return null if (!info.isFile) return null diff --git a/mirai-core/src/commonMain/kotlin/utils/SingleEntrantLock.kt b/mirai-core/src/commonMain/kotlin/utils/SingleEntrantLock.kt index 5d15614ae..6035572c9 100644 --- a/mirai-core/src/commonMain/kotlin/utils/SingleEntrantLock.kt +++ b/mirai-core/src/commonMain/kotlin/utils/SingleEntrantLock.kt @@ -9,18 +9,21 @@ package net.mamoe.mirai.internal.utils +import kotlinx.atomicfu.AtomicRef +import kotlinx.atomicfu.atomic +import kotlinx.atomicfu.locks.SynchronizedObject +import kotlinx.atomicfu.locks.synchronized -internal class SingleEntrantLock { - @Volatile - @PublishedApi - internal var locker: Any? = null + +internal class SingleEntrantLock : SynchronizedObject() { + private val locker: AtomicRef = atomic(null) inline fun withLock(locker: Any, crossinline block: () -> R): R? { return synchronized(this) { - if (this.locker === locker) return null - this.locker = locker + if (this.locker.value === locker) return@synchronized null + this.locker.value = locker block().also { - this.locker = null + this.locker.value = null } } } diff --git a/mirai-core/src/commonMain/kotlin/utils/crypto/ECDH.kt b/mirai-core/src/commonMain/kotlin/utils/crypto/ECDH.kt index abb57ccae..8c684b0e8 100644 --- a/mirai-core/src/commonMain/kotlin/utils/crypto/ECDH.kt +++ b/mirai-core/src/commonMain/kotlin/utils/crypto/ECDH.kt @@ -1,5 +1,5 @@ /* - * Copyright 2019-2021 Mamoe Technologies and contributors. + * Copyright 2019-2022 Mamoe Technologies and contributors. * * 此源代码的使用受 GNU AFFERO GENERAL PUBLIC LICENSE version 3 许可证的约束, 可以在以下链接找到该许可证. * Use of this source code is governed by the GNU AGPLv3 license that can be found through the following link. @@ -11,10 +11,7 @@ package net.mamoe.mirai.internal.utils.crypto import kotlinx.serialization.Serializable import kotlinx.serialization.Transient -import net.mamoe.mirai.utils.decodeBase64 import net.mamoe.mirai.utils.hexToBytes -import java.security.KeyFactory -import java.security.spec.X509EncodedKeySpec internal expect interface ECDHPrivateKey @@ -125,10 +122,8 @@ internal data class ECDHInitialPublicKey(val version: Int = 1, val keyStr: Strin internal val key: ECDHPublicKey = keyStr.adjustToPublicKey() } -internal val publicKeyForVerify by lazy { - KeyFactory.getInstance("RSA") - .generatePublic(X509EncodedKeySpec("MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAuJTW4abQJXeVdAODw1CamZH4QJZChyT08ribet1Gp0wpSabIgyKFZAOxeArcCbknKyBrRY3FFI9HgY1AyItH8DOUe6ajDEb6c+vrgjgeCiOiCVyum4lI5Fmp38iHKH14xap6xGaXcBccdOZNzGT82sPDM2Oc6QYSZpfs8EO7TYT7KSB2gaHz99RQ4A/Lel1Vw0krk+DescN6TgRCaXjSGn268jD7lOO23x5JS1mavsUJtOZpXkK9GqCGSTCTbCwZhI33CpwdQ2EHLhiP5RaXZCio6lksu+d8sKTWU1eEiEb3cQ7nuZXLYH7leeYFoPtbFV4RicIWp0/YG+RP7rLPCwIDAQAB".decodeBase64())) -} +internal expect val publicKeyForVerify: ECDHPublicKey + internal val defaultInitialPublicKey: ECDHInitialPublicKey by lazy { ECDHInitialPublicKey(keyStr = "04EBCA94D733E399B2DB96EACDD3F69A8BB0F74224E2B44E3357812211D2E62EFBC91BB553098E25E33A799ADC7F76FEB208DA7C6522CDB0719A305180CC54A82E") } private val signHead = "3059301306072a8648ce3d020106082a8648ce3d030107034200".hexToBytes() diff --git a/mirai-core/src/commonMain/kotlin/utils/crypto/TEA.kt b/mirai-core/src/commonMain/kotlin/utils/crypto/TEA.kt index 550c24c59..310e116a6 100644 --- a/mirai-core/src/commonMain/kotlin/utils/crypto/TEA.kt +++ b/mirai-core/src/commonMain/kotlin/utils/crypto/TEA.kt @@ -15,6 +15,7 @@ import net.mamoe.mirai.utils.toByteArray import net.mamoe.mirai.utils.toUHexString import kotlin.experimental.and import kotlin.experimental.xor +import kotlin.jvm.JvmStatic import kotlin.random.Random /** diff --git a/mirai-core/src/commonMain/kotlin/utils/flags.kt b/mirai-core/src/commonMain/kotlin/utils/flags.kt index 2c99fd53a..7ce3399de 100644 --- a/mirai-core/src/commonMain/kotlin/utils/flags.kt +++ b/mirai-core/src/commonMain/kotlin/utils/flags.kt @@ -1,14 +1,16 @@ /* - * Copyright 2019-2021 Mamoe Technologies and contributors. + * Copyright 2019-2022 Mamoe Technologies and contributors. * - * 此源代码的使用受 GNU AFFERO GENERAL PUBLIC LICENSE version 3 许可证的约束, 可以在以下链接找到该许可证. - * Use of this source code is governed by the GNU AGPLv3 license that can be found through the following link. + * 此源代码的使用受 GNU AFFERO GENERAL PUBLIC LICENSE version 3 许可证的约束, 可以在以下链接找到该许可证. + * Use of this source code is governed by the GNU AGPLv3 license that can be found through the following link. * - * https://github.com/mamoe/mirai/blob/master/LICENSE + * https://github.com/mamoe/mirai/blob/dev/LICENSE */ package net.mamoe.mirai.internal.utils +import kotlin.jvm.JvmInline + @JvmInline internal value class MacOrAndroidIdChangeFlag(val value: Long = 0) { fun macChanged(): MacOrAndroidIdChangeFlag = diff --git a/mirai-core/src/commonMain/kotlin/utils/io/output.kt b/mirai-core/src/commonMain/kotlin/utils/io/output.kt index 1ed92b1ec..a6a89eea1 100644 --- a/mirai-core/src/commonMain/kotlin/utils/io/output.kt +++ b/mirai-core/src/commonMain/kotlin/utils/io/output.kt @@ -14,12 +14,12 @@ package net.mamoe.mirai.internal.utils.io import io.ktor.utils.io.core.* -import io.ktor.utils.io.streams.* -import io.ktor.utils.io.streams.outputStream import net.mamoe.mirai.internal.utils.coerceAtMostOrFail import net.mamoe.mirai.internal.utils.crypto.TEA import net.mamoe.mirai.utils.ExternalResource import net.mamoe.mirai.utils.withUse +import kotlin.jvm.JvmMultifileClass +import kotlin.jvm.JvmName internal fun BytePacketBuilder.writeShortLVByteArrayLimitedLength(array: ByteArray, maxLength: Int) { if (array.size <= maxLength) { @@ -36,14 +36,10 @@ internal fun BytePacketBuilder.writeShortLVByteArrayLimitedLength(array: ByteArr internal fun BytePacketBuilder.writeResource( resource: ExternalResource, close: Boolean = false, -): Long = resource.inputStream().withUse { copyTo(outputStream()) }.also { +): Long = resource.input().withUse { copyTo(this@writeResource) }.also { if (close) resource.close() } -internal fun io.ktor.utils.io.core.BytePacketBuilder.writeResource( - resource: ExternalResource, -): Long = resource.inputStream().withUse { copyTo(outputStream()) } - internal inline fun BytePacketBuilder.writeShortLVByteArray(byteArray: ByteArray): Int { this.writeShort(byteArray.size.toShort()) this.writeFully(byteArray) diff --git a/mirai-core/src/commonMain/kotlin/utils/io/serialization/tars/Tars.kt b/mirai-core/src/commonMain/kotlin/utils/io/serialization/tars/Tars.kt index 313ea7c49..2f50f4ddd 100644 --- a/mirai-core/src/commonMain/kotlin/utils/io/serialization/tars/Tars.kt +++ b/mirai-core/src/commonMain/kotlin/utils/io/serialization/tars/Tars.kt @@ -21,6 +21,7 @@ import net.mamoe.mirai.internal.utils.io.serialization.tars.internal.TarsDecoder import net.mamoe.mirai.internal.utils.io.serialization.tars.internal.TarsInput import net.mamoe.mirai.internal.utils.io.serialization.tars.internal.TarsOld import net.mamoe.mirai.utils.read +import kotlin.jvm.JvmStatic /** * The main entry point to work with Tars serialization. diff --git a/mirai-core/src/commonMain/kotlin/utils/io/serialization/tars/internal/TarsDecoder.kt b/mirai-core/src/commonMain/kotlin/utils/io/serialization/tars/internal/TarsDecoder.kt index 457059cbd..8fa7cc540 100644 --- a/mirai-core/src/commonMain/kotlin/utils/io/serialization/tars/internal/TarsDecoder.kt +++ b/mirai-core/src/commonMain/kotlin/utils/io/serialization/tars/internal/TarsDecoder.kt @@ -11,6 +11,7 @@ package net.mamoe.mirai.internal.utils.io.serialization.tars.internal +import io.ktor.utils.io.core.* import kotlinx.serialization.ExperimentalSerializationApi import kotlinx.serialization.InternalSerializationApi import kotlinx.serialization.descriptors.* @@ -21,23 +22,22 @@ import kotlinx.serialization.modules.SerializersModule import net.mamoe.mirai.internal.utils.io.serialization.tars.Tars import net.mamoe.mirai.internal.utils.io.serialization.tars.TarsId import net.mamoe.mirai.utils.MiraiLogger -import java.io.PrintStream internal class DebugLogger( - val out: PrintStream? + val out: Output? ) { var structureHierarchy: Int = 0 fun println(message: Any?) { - out?.println(" ".repeat(structureHierarchy) + message) + out?.appendLine(" ".repeat(structureHierarchy) + message) } fun println() { - out?.println() + out?.appendLine() } inline fun println(lazyMessage: () -> String) { - out?.println(" ".repeat(structureHierarchy) + lazyMessage()) + out?.appendLine(" ".repeat(structureHierarchy) + lazyMessage()) } } diff --git a/mirai-core/src/commonMain/kotlin/utils/io/serialization/utils.kt b/mirai-core/src/commonMain/kotlin/utils/io/serialization/utils.kt index 6b5948136..5cbca9643 100644 --- a/mirai-core/src/commonMain/kotlin/utils/io/serialization/utils.kt +++ b/mirai-core/src/commonMain/kotlin/utils/io/serialization/utils.kt @@ -14,7 +14,6 @@ package net.mamoe.mirai.internal.utils.io.serialization import io.ktor.utils.io.core.* -import io.ktor.utils.io.streams.asInput import kotlinx.serialization.DeserializationStrategy import kotlinx.serialization.SerializationStrategy import kotlinx.serialization.descriptors.SerialDescriptor @@ -30,10 +29,11 @@ import net.mamoe.mirai.internal.utils.io.serialization.tars.internal.DebugLogger import net.mamoe.mirai.internal.utils.io.serialization.tars.internal.TarsDecoder import net.mamoe.mirai.internal.utils.printStructure import net.mamoe.mirai.utils.* -import java.io.ByteArrayOutputStream -import java.io.PrintStream import kotlin.contracts.InvocationKind import kotlin.contracts.contract +import kotlin.jvm.JvmInline +import kotlin.jvm.JvmMultifileClass +import kotlin.jvm.JvmName internal typealias KtProtoBuf = kotlinx.serialization.protobuf.ProtoBuf @@ -69,36 +69,36 @@ private fun ByteArray.doLoadAs( length: Int, ): T { try { - return this.inputStream(offset = offset, length = length).asInput().use { input -> + return this.toReadPacket(offset = offset, length = length).use { input -> Tars.UTF_8.load(deserializer, input) } } catch (originalException: Exception) { - val log = ByteArrayOutputStream() - try { - val value = PrintStream(log).use { stream -> - stream.println("\nData: ") - stream.println(this.toUHexString(offset = offset, length = length)) - stream.println("Trace:") + BytePacketBuilder().use { log -> + val build by lazy { log.build() } + try { + log.appendLine("\nData: ") + log.appendLine(this.toUHexString(offset = offset, length = length)) + log.appendLine("Trace:") - this.inputStream(offset = offset, length = length).asInput().use { input -> - Tars.UTF_8.load(deserializer, input, debugLogger = DebugLogger(stream)) + val value = this.toReadPacket(offset = offset, length = length).use { input -> + Tars.UTF_8.load(deserializer, input, debugLogger = DebugLogger(log)) } - } - return value.also { - TarsDecoder.logger.warning( - contextualBugReportException( - "解析 " + deserializer.descriptor.serialName, - "启用 debug 模式后解析正常: $value \n\n${log.toByteArray().decodeToString()}", - originalException + return value.also { + TarsDecoder.logger.warning( + contextualBugReportException( + "解析 " + deserializer.descriptor.serialName, + "启用 debug 模式后解析正常: $value \n\n${build.readText()}", + originalException + ) ) + } + } catch (secondFailure: Exception) { + throw contextualBugReportException( + "解析 " + deserializer.descriptor.serialName, + build.readText(), + ExceptionCollector.compressExceptions(originalException, secondFailure) ) } - } catch (secondFailure: Exception) { - throw contextualBugReportException( - "解析 " + deserializer.descriptor.serialName, - log.toByteArray().decodeToString(), - ExceptionCollector.compressExceptions(originalException, secondFailure) - ) } } } diff --git a/mirai-core/src/commonMain/kotlin/utils/type.kt b/mirai-core/src/commonMain/kotlin/utils/type.kt index 4a0402d68..f44b353f4 100644 --- a/mirai-core/src/commonMain/kotlin/utils/type.kt +++ b/mirai-core/src/commonMain/kotlin/utils/type.kt @@ -15,7 +15,6 @@ import net.mamoe.mirai.message.data.* import net.mamoe.mirai.utils.chineseLength import net.mamoe.mirai.utils.toInt import net.mamoe.mirai.utils.toLongUnsigned -import java.net.Inet4Address internal fun Int.toIpV4AddressString(): String { @@ -31,19 +30,6 @@ internal fun Int.toIpV4AddressString(): String { } } } - -internal fun String.toIpV4Long(): Long { - return if (isEmpty()) { - 0 - } else { - try { - Inet4Address.getByName(this).address.toInt().toLongUnsigned() - } catch (e: UnknownHostException) { - -2 - } - } -} - internal fun Iterable.estimateLength(target: ContactOrBot, upTo: Int): Int = sumUpTo(upTo) { it, up -> it.estimateLength(target, up) diff --git a/mirai-core/src/commonTest/kotlin/MockBot.kt b/mirai-core/src/commonTest/kotlin/MockBot.kt index b5125c3db..89ac2b965 100644 --- a/mirai-core/src/commonTest/kotlin/MockBot.kt +++ b/mirai-core/src/commonTest/kotlin/MockBot.kt @@ -9,6 +9,7 @@ @file:Suppress("unused") +@file:OptIn(TestOnly::class) package net.mamoe.mirai.internal @@ -17,6 +18,7 @@ import net.mamoe.mirai.internal.network.component.ConcurrentComponentStorage import net.mamoe.mirai.internal.network.component.setAll import net.mamoe.mirai.internal.network.handler.NetworkHandler import net.mamoe.mirai.utils.BotConfiguration +import net.mamoe.mirai.utils.TestOnly import kotlin.contracts.InvocationKind import kotlin.contracts.contract import kotlin.math.absoluteValue diff --git a/mirai-core/src/commonTest/kotlin/PlatformUtilsTest.kt b/mirai-core/src/commonTest/kotlin/PlatformUtilsTest.kt index b8e4a40c7..d719783f9 100644 --- a/mirai-core/src/commonTest/kotlin/PlatformUtilsTest.kt +++ b/mirai-core/src/commonTest/kotlin/PlatformUtilsTest.kt @@ -11,10 +11,10 @@ package net.mamoe.mirai.internal import io.ktor.utils.io.core.* import net.mamoe.mirai.internal.test.AbstractTest +import net.mamoe.mirai.utils.deflate import net.mamoe.mirai.utils.gzip +import net.mamoe.mirai.utils.inflate import net.mamoe.mirai.utils.ungzip -import net.mamoe.mirai.utils.unzip -import net.mamoe.mirai.utils.zip import kotlin.test.Test import kotlin.test.assertEquals @@ -22,7 +22,7 @@ internal class PlatformUtilsTest : AbstractTest() { @Test fun testZip() { - assertEquals("test", "test".toByteArray().zip().unzip().decodeToString()) + assertEquals("test", "test".toByteArray().deflate().inflate().decodeToString()) } @Test diff --git a/mirai-core/src/commonTest/kotlin/ScheduledJobTest.kt b/mirai-core/src/commonTest/kotlin/ScheduledJobTest.kt index cfdfc7551..6ee56b760 100644 --- a/mirai-core/src/commonTest/kotlin/ScheduledJobTest.kt +++ b/mirai-core/src/commonTest/kotlin/ScheduledJobTest.kt @@ -1,21 +1,21 @@ /* - * Copyright 2019-2021 Mamoe Technologies and contributors. + * Copyright 2019-2022 Mamoe Technologies and contributors. * - * 此源代码的使用受 GNU AFFERO GENERAL PUBLIC LICENSE version 3 许可证的约束, 可以在以下链接找到该许可证. - * Use of this source code is governed by the GNU AGPLv3 license that can be found through the following link. + * 此源代码的使用受 GNU AFFERO GENERAL PUBLIC LICENSE version 3 许可证的约束, 可以在以下链接找到该许可证. + * Use of this source code is governed by the GNU AGPLv3 license that can be found through the following link. * - * https://github.com/mamoe/mirai/blob/master/LICENSE + * https://github.com/mamoe/mirai/blob/dev/LICENSE */ package net.mamoe.mirai.internal +import kotlinx.atomicfu.atomic import kotlinx.coroutines.CoroutineExceptionHandler import kotlinx.coroutines.CoroutineScope import kotlinx.coroutines.delay import kotlinx.coroutines.runBlocking import net.mamoe.mirai.internal.test.AbstractTest import net.mamoe.mirai.internal.utils.ScheduledJob -import org.junit.jupiter.api.Test -import java.util.concurrent.atomic.AtomicInteger +import kotlin.test.Test import kotlin.test.assertEquals internal class ScheduledJobTest : AbstractTest() { @@ -25,12 +25,12 @@ internal class ScheduledJobTest : AbstractTest() { val scope = CoroutineScope(CoroutineExceptionHandler { _, throwable -> throwable.printStackTrace() }) - val invoked = AtomicInteger(0) + val invoked = atomic(0) val job = ScheduledJob(scope.coroutineContext, 1000) { invoked.incrementAndGet() } delay(100) - assertEquals(0, invoked.get()) + assertEquals(0, invoked.value) job.notice() job.notice() job.notice() diff --git a/mirai-core/src/commonTest/kotlin/contact/file/AbsoluteFolderTest.kt b/mirai-core/src/commonTest/kotlin/contact/file/AbsoluteFolderTest.kt index f28676b4e..86dd7b1a9 100644 --- a/mirai-core/src/commonTest/kotlin/contact/file/AbsoluteFolderTest.kt +++ b/mirai-core/src/commonTest/kotlin/contact/file/AbsoluteFolderTest.kt @@ -1,5 +1,5 @@ /* - * Copyright 2019-2021 Mamoe Technologies and contributors. + * Copyright 2019-2022 Mamoe Technologies and contributors. * * 此源代码的使用受 GNU AFFERO GENERAL PUBLIC LICENSE version 3 许可证的约束, 可以在以下链接找到该许可证. * Use of this source code is governed by the GNU AGPLv3 license that can be found through the following link. @@ -17,7 +17,8 @@ import net.mamoe.mirai.internal.network.notice.BotAware import net.mamoe.mirai.internal.network.protocol.data.proto.GroupFileCommon import net.mamoe.mirai.internal.notice.processors.GroupExtensions import net.mamoe.mirai.internal.test.AbstractTest -import org.junit.jupiter.api.Test +import net.mamoe.mirai.internal.test.runBlockingUnit +import kotlin.test.Test import kotlin.test.assertEquals internal class AbsoluteFolderTest : AbstractTest(), BotAware, GroupExtensions { @@ -26,7 +27,7 @@ internal class AbsoluteFolderTest : AbstractTest(), BotAware, GroupExtensions { private val root = group.files.root @Test - suspend fun `resolveFolderById always returns null if it is not root`() { + fun `resolveFolderById always returns null if it is not root`() = runBlockingUnit { val child = root.impl().createChildFolder( GroupFileCommon.FolderInfo( folderId = "/f-1", @@ -37,7 +38,7 @@ internal class AbsoluteFolderTest : AbstractTest(), BotAware, GroupExtensions { } @Test - suspend fun `resolveFolderById always returns root for slash`() { + fun `resolveFolderById always returns root for slash`() = runBlockingUnit { val child = root.impl().createChildFolder( GroupFileCommon.FolderInfo( folderId = "/f-1", diff --git a/mirai-core/src/commonTest/kotlin/event/CancelScopeTest.kt b/mirai-core/src/commonTest/kotlin/event/CancelScopeTest.kt index 51d5b8dd3..804aca8d8 100644 --- a/mirai-core/src/commonTest/kotlin/event/CancelScopeTest.kt +++ b/mirai-core/src/commonTest/kotlin/event/CancelScopeTest.kt @@ -11,7 +11,7 @@ package net.mamoe.mirai.internal.event import kotlinx.coroutines.* import net.mamoe.mirai.event.broadcast import net.mamoe.mirai.event.globalEventChannel -import org.junit.jupiter.api.Test +import kotlin.test.Test import kotlin.test.assertFalse diff --git a/mirai-core/src/commonTest/kotlin/event/EventChannelFlowTest.kt b/mirai-core/src/commonTest/kotlin/event/EventChannelFlowTest.kt index 22249405a..1e5451514 100644 --- a/mirai-core/src/commonTest/kotlin/event/EventChannelFlowTest.kt +++ b/mirai-core/src/commonTest/kotlin/event/EventChannelFlowTest.kt @@ -11,19 +11,19 @@ package net.mamoe.mirai.internal.event import kotlinx.coroutines.CoroutineStart import kotlinx.coroutines.async -import kotlinx.coroutines.coroutineScope import kotlinx.coroutines.flow.first import me.him188.kotlin.jvm.blocking.bridge.JvmBlockingBridge import net.mamoe.mirai.event.GlobalEventChannel import net.mamoe.mirai.event.broadcast -import org.junit.jupiter.api.Test +import net.mamoe.mirai.internal.test.runBlockingUnit +import kotlin.test.Test import kotlin.test.assertIs @JvmBlockingBridge internal class EventChannelFlowTest : AbstractEventTest() { @Test - suspend fun asFlow(): Unit = coroutineScope { + fun asFlow(): Unit = runBlockingUnit() { val channel = GlobalEventChannel val job = async(start = CoroutineStart.UNDISPATCHED) { channel.asFlow().first() diff --git a/mirai-core/src/commonTest/kotlin/event/EventChannelTest.kt b/mirai-core/src/commonTest/kotlin/event/EventChannelTest.kt index 8abe101e2..dc68efe2d 100644 --- a/mirai-core/src/commonTest/kotlin/event/EventChannelTest.kt +++ b/mirai-core/src/commonTest/kotlin/event/EventChannelTest.kt @@ -18,7 +18,6 @@ import net.mamoe.mirai.event.events.FriendEvent import net.mamoe.mirai.event.events.GroupEvent import net.mamoe.mirai.event.events.GroupMessageEvent import net.mamoe.mirai.event.events.MessageEvent -import org.junit.jupiter.api.Test import kotlin.coroutines.coroutineContext import kotlin.coroutines.resume import kotlin.coroutines.resumeWithException diff --git a/mirai-core/src/commonTest/kotlin/event/EventTests.kt b/mirai-core/src/commonTest/kotlin/event/EventTests.kt index 64fe92302..ac3b52610 100644 --- a/mirai-core/src/commonTest/kotlin/event/EventTests.kt +++ b/mirai-core/src/commonTest/kotlin/event/EventTests.kt @@ -9,12 +9,11 @@ package net.mamoe.mirai.internal.event +import kotlinx.atomicfu.atomic import kotlinx.coroutines.* import net.mamoe.mirai.event.* -import org.junit.jupiter.api.AfterEach -import java.util.concurrent.Executor -import java.util.concurrent.atomic.AtomicInteger import kotlin.coroutines.EmptyCoroutineContext +import kotlin.test.AfterTest import kotlin.test.Test import kotlin.test.assertTrue @@ -25,7 +24,7 @@ class TestEvent : AbstractEvent() { internal class EventTests : AbstractEventTest() { var scope = CoroutineScope(EmptyCoroutineContext) - @AfterEach + @AfterTest fun finallyReset() { resetEventListeners() } @@ -60,7 +59,7 @@ internal class EventTests : AbstractEventTest() { fun `test concurrent listening`() { resetEventListeners() var listeners = 0 - val counter = AtomicInteger(0) + val counter = atomic(0) for (p in EventPriority.values()) { repeat(2333) { listeners++ @@ -72,7 +71,7 @@ internal class EventTests : AbstractEventTest() { runBlocking { ParentEvent().broadcast() } - val called = counter.get() + val called = counter.value println("Registered $listeners listeners and $called called") if (listeners != called) { throw IllegalStateException("Registered $listeners listeners but only $called called") @@ -83,8 +82,8 @@ internal class EventTests : AbstractEventTest() { fun `test concurrent listening 3`() { resetEventListeners() runBlocking { - val called = AtomicInteger() - val registered = AtomicInteger() + val called = atomic(0) + val registered = atomic(0) coroutineScope { println("Step 0") for (priority in EventPriority.values()) { @@ -105,17 +104,17 @@ internal class EventTests : AbstractEventTest() { println("Step 2") ParentEvent().broadcast() println("Step 3") - check(called.get() == registered.get()) + check(called.value == registered.value) println("Done") - println("Called ${called.get()}, registered ${registered.get()}") + println("Called ${called.value}, registered ${registered.value}") } } @Test fun `test concurrent listening 2`() = runBlocking { resetEventListeners() - val registered = AtomicInteger() - val called = AtomicInteger() + val registered = atomic(0) + val called = atomic(0) val supervisor = CoroutineScope(SupervisorJob()) @@ -142,8 +141,8 @@ internal class EventTests : AbstractEventTest() { } } - val calledCount = called.get() - val shouldCalled = registered.get() * postCount + val calledCount = called.value + val shouldCalled = registered.value * postCount supervisor.cancel() println("Should call $shouldCalled times and $called called") @@ -191,7 +190,7 @@ internal class EventTests : AbstractEventTest() { private fun singleThreaded(step: StepUtil, invoke: suspend EventChannel.() -> Unit) { // runBlocking 会完全堵死, 没法退出 - val scope = CoroutineScope(Executor { it.run() }.asCoroutineDispatcher()) + val scope = CoroutineScope(borrowSingleThreadDispatcher()) val job = scope.launch { invoke(scope.globalEventChannel()) } diff --git a/mirai-core/src/commonTest/kotlin/event/NextEventTest.kt b/mirai-core/src/commonTest/kotlin/event/NextEventTest.kt index ec1214283..083801039 100644 --- a/mirai-core/src/commonTest/kotlin/event/NextEventTest.kt +++ b/mirai-core/src/commonTest/kotlin/event/NextEventTest.kt @@ -14,22 +14,17 @@ package net.mamoe.mirai.internal.event import kotlinx.coroutines.* import me.him188.kotlin.jvm.blocking.bridge.JvmBlockingBridge import net.mamoe.mirai.event.* -import org.junit.jupiter.api.AfterEach -import org.junit.jupiter.api.Test -import org.junit.jupiter.api.assertThrows -import java.util.concurrent.Executors -import kotlin.test.assertEquals -import kotlin.test.assertFalse -import kotlin.test.assertIs -import kotlin.test.assertTrue +import net.mamoe.mirai.internal.test.runBlockingUnit +import kotlin.test.* @JvmBlockingBridge internal class NextEventTest : AbstractEventTest() { - private val dispatcher = Executors.newFixedThreadPool(1).asCoroutineDispatcher() + private val dispatcher: CoroutineDispatcher = borrowSingleThreadDispatcher() - @AfterEach + @OptIn(ExperimentalCoroutinesApi::class) + @AfterTest fun stopDispatcher() { - dispatcher.close() + (dispatcher as CloseableCoroutineDispatcher).close() } @@ -46,7 +41,7 @@ internal class NextEventTest : AbstractEventTest() { /////////////////////////////////////////////////////////////////////////// @Test - suspend fun `nextEvent can receive`() { + fun `nextEvent can receive`() = runBlockingUnit { val channel = GlobalEventChannel withContext(dispatcher) { @@ -62,7 +57,7 @@ internal class NextEventTest : AbstractEventTest() { } @Test - suspend fun `nextEvent can filter type`() { + fun `nextEvent can filter type`() = runBlockingUnit { val channel = GlobalEventChannel withContext(dispatcher) { @@ -82,7 +77,7 @@ internal class NextEventTest : AbstractEventTest() { } @Test - suspend fun `nextEvent can filter by filter`() { + fun `nextEvent can filter by filter`() = runBlockingUnit { val channel = GlobalEventChannel withContext(dispatcher) { @@ -102,18 +97,18 @@ internal class NextEventTest : AbstractEventTest() { } @Test - suspend fun `nextEvent can timeout`() { + fun `nextEvent can timeout`() = runBlockingUnit { val channel = GlobalEventChannel withContext(dispatcher) { - assertThrows { + assertFailsWith { withTimeout(timeMillis = 1) { channel.nextEvent(EventPriority.MONITOR) } } } } @Test - suspend fun `nextEvent can cancel`() { + fun `nextEvent can cancel`() = runBlockingUnit { val channel = GlobalEventChannel withContext(dispatcher) { @@ -136,7 +131,7 @@ internal class NextEventTest : AbstractEventTest() { /////////////////////////////////////////////////////////////////////////// @Test - suspend fun `nextEventOrNull can receive`() { + fun `nextEventOrNull can receive`() = runBlockingUnit { withContext(dispatcher) { val deferred = async(start = CoroutineStart.UNDISPATCHED) { withTimeoutOrNull(5000) { globalEventChannel().nextEvent(EventPriority.MONITOR) } @@ -150,7 +145,7 @@ internal class NextEventTest : AbstractEventTest() { } @Test - suspend fun `nextEventOrNull can filter type`() { + fun `nextEventOrNull can filter type`() = runBlockingUnit { withContext(dispatcher) { val deferred = async(start = CoroutineStart.UNDISPATCHED) { withTimeoutOrNull(5000) { globalEventChannel().nextEvent(EventPriority.MONITOR) } @@ -168,7 +163,7 @@ internal class NextEventTest : AbstractEventTest() { } @Test - suspend fun `nextEventOrNull can filter by filter`() { + fun `nextEventOrNull can filter by filter`() = runBlockingUnit { withContext(dispatcher) { val deferred = async(start = CoroutineStart.UNDISPATCHED) { withTimeoutOrNull(5000) { globalEventChannel().nextEvent(EventPriority.MONITOR) { it.x == 2 } } @@ -186,7 +181,7 @@ internal class NextEventTest : AbstractEventTest() { } @Test - suspend fun `nextEventOrNull can timeout`() { + fun `nextEventOrNull can timeout`() = runBlockingUnit { withContext(dispatcher) { assertEquals(null, withTimeoutOrNull(timeMillis = 1) { globalEventChannel().nextEvent(EventPriority.MONITOR) }) diff --git a/mirai-core/src/commonTest/kotlin/event/StepUtil.kt b/mirai-core/src/commonTest/kotlin/event/StepUtil.kt index 1d23455f9..ceffe2950 100644 --- a/mirai-core/src/commonTest/kotlin/event/StepUtil.kt +++ b/mirai-core/src/commonTest/kotlin/event/StepUtil.kt @@ -10,7 +10,7 @@ package net.mamoe.mirai.internal.event import kotlinx.atomicfu.atomic -import java.util.concurrent.ConcurrentLinkedDeque +import net.mamoe.mirai.utils.ConcurrentLinkedDeque class StepUtil { val step = atomic(0) diff --git a/mirai-core/src/commonTest/kotlin/message/CleanupRubbishMessageElementsTest.kt b/mirai-core/src/commonTest/kotlin/message/CleanupRubbishMessageElementsTest.kt index 83e6947d8..2377b1085 100644 --- a/mirai-core/src/commonTest/kotlin/message/CleanupRubbishMessageElementsTest.kt +++ b/mirai-core/src/commonTest/kotlin/message/CleanupRubbishMessageElementsTest.kt @@ -16,11 +16,9 @@ import net.mamoe.mirai.internal.message.protocol.impl.PokeMessageProtocol.Compan import net.mamoe.mirai.internal.message.protocol.impl.RichMessageProtocol.Companion.UNSUPPORTED_MERGED_MESSAGE_PLAIN import net.mamoe.mirai.internal.message.source.OfflineMessageSourceImplData import net.mamoe.mirai.message.data.* -import org.junit.jupiter.api.Test -import org.junit.jupiter.api.TestInstance +import kotlin.test.Test import kotlin.test.assertEquals -@TestInstance(TestInstance.Lifecycle.PER_CLASS) internal class CleanupRubbishMessageElementsTest { //region private val replySource = OfflineMessageSourceImplData( diff --git a/mirai-core/src/commonTest/kotlin/message/ImageReadingTest.kt b/mirai-core/src/commonTest/kotlin/message/ImageReadingTest.kt index 66323f8bb..99ac63c0f 100644 --- a/mirai-core/src/commonTest/kotlin/message/ImageReadingTest.kt +++ b/mirai-core/src/commonTest/kotlin/message/ImageReadingTest.kt @@ -9,15 +9,16 @@ package net.mamoe.mirai.internal.message +import io.ktor.utils.io.errors.* import net.mamoe.mirai.internal.message.image.calculateImageInfo import net.mamoe.mirai.internal.test.AbstractTest import net.mamoe.mirai.message.data.ImageType import net.mamoe.mirai.utils.ExternalResource.Companion.toExternalResource +import net.mamoe.mirai.utils.MiraiFile import net.mamoe.mirai.utils.hexToBytes +import net.mamoe.mirai.utils.readBytes import net.mamoe.mirai.utils.withUse -import org.junit.jupiter.api.Test -import java.io.File -import java.io.IOException +import kotlin.test.Test import kotlin.test.assertEquals import kotlin.test.assertFailsWith @@ -66,9 +67,9 @@ internal class ImageReadingTest : AbstractTest() { "FF D8 FF E0 00 10 4A 46 49 46 00 01 01 01 00 78 00 78 00 00 FF D0 FF D1 FF D2 FF D3 FF D4 FF D5 FF D6 FF D7 FF E1 00 5A 45 78 69 66 00 00 4D 4D 00 2A 00 00 00 08 00 05 03 01 00 05 00 00 00 01 00 00 00 4A 03 03 00 01 00 00 00 01 00 00 00 00 51 10 00 01 00 00 00 01 01 00 00 00 51 11 00 04 00 00 00 01 00 00 12 74 51 12 00 04 00 00 00 01 00 00 12 74 00 00 00 00 00 01 86 A0 00 00 B1 8F FF DB 00 43 00 02 01 01 02 01 01 02 02 02 02 02 02 02 02 03 05 03 03 03 03 03 06 04 04 03 05 07 06 07 07 07 06 07 07 08 09 0B 09 08 08 0A 08 07 07 0A 0D 0A 0A 0B 0C 0C 0C 0C 07 09 0E 0F 0D 0C 0E 0B 0C 0C 0C FF DB 00 43 01 02 02 02 03 03 03 06 03 03 06 0C 08 07 08 0C 0C 0C 0C 0C 0C 0C 0C 0C 0C 0C 0C 0C 0C 0C 0C 0C 0C 0C 0C 0C 0C 0C 0C 0C 0C 0C 0C 0C 0C 0C 0C 0C 0C 0C 0C 0C 0C 0C 0C 0C 0C 0C 0C 0C 0C 0C 0C 0C 0C FF C2 00 11 08 01 90 01 E0 03 01 22 00 02 11 01 03 11 01 FF DA".testMatch( ImageType.JPG ) - println("Current path: "+File(".").absolutePath) + println("Current path: " + net.mamoe.mirai.utils.MiraiFile.create(".").absolutePath) //Issue 1610 - File("./src/commonTest/resources/image/jpeg-header-issue-1610.bin").readBytes().testRead( + MiraiFile.create("./src/commonTest/resources/image/jpeg-header-issue-1610.bin").readBytes().testRead( ImageType.JPG ) //Failed to find @@ -77,7 +78,7 @@ internal class ImageReadingTest : AbstractTest() { ImageType.JPG ) } - assertFailsWith(IOException::class) { + assertFailsWith(IllegalStateException::class) { "FF D8 FF E0 00 10 4A 46 49 46 00 01 01 01 00 78 00 78 00 00 FF E1 00 5A".testMatch( ImageType.JPG ) @@ -109,6 +110,7 @@ internal class ImageReadingTest : AbstractTest() { } } } + private fun String.testMatch(type: ImageType) { this.hexToBytes().toExternalResource().withUse { calculateImageInfo().run { diff --git a/mirai-core/src/commonTest/kotlin/message/code/TestMiraiCode.kt b/mirai-core/src/commonTest/kotlin/message/code/TestMiraiCode.kt index 16bd33f5d..403d00a63 100644 --- a/mirai-core/src/commonTest/kotlin/message/code/TestMiraiCode.kt +++ b/mirai-core/src/commonTest/kotlin/message/code/TestMiraiCode.kt @@ -1,10 +1,10 @@ /* - * Copyright 2019-2021 Mamoe Technologies and contributors. + * Copyright 2019-2022 Mamoe Technologies and contributors. * - * 此源代码的使用受 GNU AFFERO GENERAL PUBLIC LICENSE version 3 许可证的约束, 可以在以下链接找到该许可证. - * Use of this source code is governed by the GNU AGPLv3 license that can be found through the following link. + * 此源代码的使用受 GNU AFFERO GENERAL PUBLIC LICENSE version 3 许可证的约束, 可以在以下链接找到该许可证. + * Use of this source code is governed by the GNU AGPLv3 license that can be found through the following link. * - * https://github.com/mamoe/mirai/blob/master/LICENSE + * https://github.com/mamoe/mirai/blob/dev/LICENSE */ @file:Suppress("INVISIBLE_MEMBER", "INVISIBLE_REFERENCE") // MiraiCodeParser @@ -15,11 +15,11 @@ import net.mamoe.mirai.internal.test.AbstractTest import net.mamoe.mirai.message.code.MiraiCode.deserializeMiraiCode import net.mamoe.mirai.message.code.internal.MiraiCodeParser import net.mamoe.mirai.message.data.* -import org.junit.jupiter.api.Test +import kotlin.test.Test import kotlin.test.assertEquals import kotlin.test.assertNotNull -class TestMiraiCode : AbstractTest() { +internal class TestMiraiCode : AbstractTest() { @Test fun testDynamicMiraiCodeParser() { fun runTest(args: Int, code: String, parse: (args: Array) -> Unit) { diff --git a/mirai-core/src/commonTest/kotlin/message/protocol/MessageProtocolFacadeTest.kt b/mirai-core/src/commonTest/kotlin/message/protocol/MessageProtocolFacadeTest.kt index 999c38522..0aed8969b 100644 --- a/mirai-core/src/commonTest/kotlin/message/protocol/MessageProtocolFacadeTest.kt +++ b/mirai-core/src/commonTest/kotlin/message/protocol/MessageProtocolFacadeTest.kt @@ -10,7 +10,7 @@ package net.mamoe.mirai.internal.message.protocol import net.mamoe.mirai.internal.test.AbstractTest -import org.junit.jupiter.api.Test +import kotlin.test.Test import kotlin.test.assertEquals internal class MessageProtocolFacadeTest : AbstractTest() { diff --git a/mirai-core/src/commonTest/kotlin/message/protocol/impl/AbstractMessageProtocolTest.kt b/mirai-core/src/commonTest/kotlin/message/protocol/impl/AbstractMessageProtocolTest.kt index 6feea6143..3321a6c41 100644 --- a/mirai-core/src/commonTest/kotlin/message/protocol/impl/AbstractMessageProtocolTest.kt +++ b/mirai-core/src/commonTest/kotlin/message/protocol/impl/AbstractMessageProtocolTest.kt @@ -42,22 +42,16 @@ import net.mamoe.mirai.internal.network.protocol.packet.chat.receive.MessageSvcP import net.mamoe.mirai.internal.notice.processors.GroupExtensions import net.mamoe.mirai.internal.test.runBlockingUnit import net.mamoe.mirai.message.data.* -import net.mamoe.mirai.utils.Clock -import net.mamoe.mirai.utils.lateinitMutableProperty -import net.mamoe.mirai.utils.md5 -import net.mamoe.mirai.utils.toUHexString -import org.junit.jupiter.api.AfterEach -import org.junit.jupiter.api.BeforeEach +import net.mamoe.mirai.utils.* import kotlin.contracts.InvocationKind import kotlin.contracts.contract -import kotlin.test.Asserter -import kotlin.test.assertEquals -import kotlin.test.asserter +import kotlin.test.* +@OptIn(TestOnly::class) internal abstract class AbstractMessageProtocolTest : AbstractMockNetworkHandlerTest(), GroupExtensions { init { - System.setProperty("mirai.message.protocol.log.full", "true") - System.setProperty("mirai.message.outgoing.pipeline.log.full", "true") + setSystemProp("mirai.message.protocol.log.full", "true") + setSystemProp("mirai.message.outgoing.pipeline.log.full", "true") } override fun createAccount(): BotAccount = BotAccount(1230001L, "pwd") @@ -72,7 +66,7 @@ internal abstract class AbstractMessageProtocolTest : AbstractMockNetworkHandler private var decoderLoggerEnabled = false private var encoderLoggerEnabled = false - @BeforeEach + @BeforeTest fun beforeEach() { decoderLoggerEnabled = MessageDecoderPipelineImpl.defaultTraceLogging.isEnabled MessageDecoderPipelineImpl.defaultTraceLogging.enable() @@ -80,7 +74,7 @@ internal abstract class AbstractMessageProtocolTest : AbstractMockNetworkHandler MessageEncoderPipelineImpl.defaultTraceLogging.enable() } - @AfterEach + @AfterTest fun afterEach() { if (!decoderLoggerEnabled) { MessageDecoderPipelineImpl.defaultTraceLogging.disable() @@ -322,13 +316,11 @@ internal abstract class AbstractMessageProtocolTest : AbstractMockNetworkHandler val expectedChain = expected.toMessageChain() val actualChain = actual.toMessageChain() - val message = String.format( - """ - Expected: %s + val message = """ + Expected: $1 - Actual: %s - """.trimIndent(), expectedChain.render(), actualChain.render() - ) + Actual: $2 + """.trimIndent().replace("$1", expectedChain.render()).replace("$2", actualChain.render()) assertEquals(expectedChain.size, actualChain.size, message) asserter.assertEquals(message, expectedChain, actualChain) } @@ -337,14 +329,12 @@ internal abstract class AbstractMessageProtocolTest : AbstractMockNetworkHandler val expectedChain = expected.toMessageChain() val actualChain = actual.toMessageChain() - val message = String.format( - """ + val message = """ Facade: ${this.remark} - Expected: %s + Expected: $1 - Actual: %s - """.trimIndent(), expectedChain.render(), actualChain.render() - ) + Actual: $2 + """.trimIndent().replace("$1", expectedChain.render()).replace("$2", actualChain.render()) assertEquals(expectedChain.size, actualChain.size, message) asserter.assertEquals(message, expectedChain, actualChain) } diff --git a/mirai-core/src/commonTest/kotlin/message/protocol/impl/CustomMessageProtocolTest.kt b/mirai-core/src/commonTest/kotlin/message/protocol/impl/CustomMessageProtocolTest.kt index b09f0facc..ef17a50e2 100644 --- a/mirai-core/src/commonTest/kotlin/message/protocol/impl/CustomMessageProtocolTest.kt +++ b/mirai-core/src/commonTest/kotlin/message/protocol/impl/CustomMessageProtocolTest.kt @@ -18,20 +18,20 @@ import net.mamoe.mirai.internal.utils.io.serialization.toByteArray import net.mamoe.mirai.message.data.CustomMessage import net.mamoe.mirai.message.data.CustomMessageMetadata import net.mamoe.mirai.utils.hexToBytes -import org.junit.jupiter.api.BeforeEach -import org.junit.jupiter.api.Test +import kotlin.test.BeforeTest +import kotlin.test.Test internal class CustomMessageProtocolTest : AbstractMessageProtocolTest() { override val protocols: Array = arrayOf(CustomMessageProtocol(), TextProtocol()) - @BeforeEach + @BeforeTest fun `init group`() { defaultTarget = bot.addGroup(123, 1230003).apply { addMember(1230003, "user3", MemberPermission.OWNER) } } - @BeforeEach + @BeforeTest fun init() { MyCustomMessage(1) // register } diff --git a/mirai-core/src/commonTest/kotlin/message/protocol/impl/FaceProtocolTest.kt b/mirai-core/src/commonTest/kotlin/message/protocol/impl/FaceProtocolTest.kt index 14e43f1b2..385b20bda 100644 --- a/mirai-core/src/commonTest/kotlin/message/protocol/impl/FaceProtocolTest.kt +++ b/mirai-core/src/commonTest/kotlin/message/protocol/impl/FaceProtocolTest.kt @@ -15,7 +15,7 @@ import net.mamoe.mirai.message.data.Face import net.mamoe.mirai.message.data.MessageSourceKind import net.mamoe.mirai.message.data.messageChainOf import net.mamoe.mirai.utils.hexToBytes -import org.junit.jupiter.api.Test +import kotlin.test.Test internal class FaceProtocolTest : AbstractMessageProtocolTest() { override val protocols: Array = arrayOf(FaceProtocol()) diff --git a/mirai-core/src/commonTest/kotlin/message/protocol/impl/FileMessageProtocolTest.kt b/mirai-core/src/commonTest/kotlin/message/protocol/impl/FileMessageProtocolTest.kt index 9956e9a12..438dc8346 100644 --- a/mirai-core/src/commonTest/kotlin/message/protocol/impl/FileMessageProtocolTest.kt +++ b/mirai-core/src/commonTest/kotlin/message/protocol/impl/FileMessageProtocolTest.kt @@ -13,13 +13,13 @@ import net.mamoe.mirai.contact.MemberPermission import net.mamoe.mirai.internal.message.protocol.MessageProtocol import net.mamoe.mirai.message.data.FileMessage import net.mamoe.mirai.utils.hexToBytes -import org.junit.jupiter.api.BeforeEach -import org.junit.jupiter.api.Test +import kotlin.test.BeforeTest +import kotlin.test.Test internal class FileMessageProtocolTest : AbstractMessageProtocolTest() { override val protocols: Array = arrayOf(FileMessageProtocol(), TextProtocol()) - @BeforeEach + @BeforeTest fun `init group`() { defaultTarget = bot.addGroup(123, 1230003).apply { addMember(1230003, "user3", MemberPermission.OWNER) diff --git a/mirai-core/src/commonTest/kotlin/message/protocol/impl/FlashImageProtocolTest.kt b/mirai-core/src/commonTest/kotlin/message/protocol/impl/FlashImageProtocolTest.kt index 659cf27d4..20695daa0 100644 Binary files a/mirai-core/src/commonTest/kotlin/message/protocol/impl/FlashImageProtocolTest.kt and b/mirai-core/src/commonTest/kotlin/message/protocol/impl/FlashImageProtocolTest.kt differ diff --git a/mirai-core/src/commonTest/kotlin/message/protocol/impl/GeneralMessageSenderProtocolTest.kt b/mirai-core/src/commonTest/kotlin/message/protocol/impl/GeneralMessageSenderProtocolTest.kt index 50ada7e88..d3f9c6f74 100644 --- a/mirai-core/src/commonTest/kotlin/message/protocol/impl/GeneralMessageSenderProtocolTest.kt +++ b/mirai-core/src/commonTest/kotlin/message/protocol/impl/GeneralMessageSenderProtocolTest.kt @@ -7,6 +7,8 @@ * https://github.com/mamoe/mirai/blob/dev/LICENSE */ +@file:OptIn(TestOnly::class) + package net.mamoe.mirai.internal.message.protocol.impl import kotlinx.coroutines.Deferred @@ -24,8 +26,9 @@ import net.mamoe.mirai.message.data.MessageChain import net.mamoe.mirai.message.data.OnlineMessageSource import net.mamoe.mirai.message.data.PlainText import net.mamoe.mirai.message.data.messageChainOf +import net.mamoe.mirai.utils.TestOnly import net.mamoe.mirai.utils.castUp -import org.junit.jupiter.api.Test +import kotlin.test.Test import kotlin.test.assertFalse import kotlin.test.assertTrue diff --git a/mirai-core/src/commonTest/kotlin/message/protocol/impl/ImageProtocolTest.kt b/mirai-core/src/commonTest/kotlin/message/protocol/impl/ImageProtocolTest.kt index 7cdbc9ae1..9648d45ee 100644 --- a/mirai-core/src/commonTest/kotlin/message/protocol/impl/ImageProtocolTest.kt +++ b/mirai-core/src/commonTest/kotlin/message/protocol/impl/ImageProtocolTest.kt @@ -9,18 +9,19 @@ package net.mamoe.mirai.internal.message.protocol.impl +import io.ktor.utils.io.core.* import net.mamoe.mirai.contact.MemberPermission import net.mamoe.mirai.internal.message.protocol.MessageProtocol import net.mamoe.mirai.message.data.Image import net.mamoe.mirai.message.data.ImageType import net.mamoe.mirai.utils.hexToBytes -import org.junit.jupiter.api.BeforeEach -import org.junit.jupiter.api.Test +import kotlin.test.BeforeTest +import kotlin.test.Test internal class ImageProtocolTest : AbstractMessageProtocolTest() { override val protocols: Array = arrayOf(ImageProtocol()) - @BeforeEach + @BeforeTest fun `init group`() { defaultTarget = bot.addGroup(123, 1230003).apply { addMember(1230003, "user3", MemberPermission.OWNER) diff --git a/mirai-core/src/commonTest/kotlin/message/protocol/impl/MarketFaceProtocolTest.kt b/mirai-core/src/commonTest/kotlin/message/protocol/impl/MarketFaceProtocolTest.kt index f9ef3a4c0..2078cbac2 100644 --- a/mirai-core/src/commonTest/kotlin/message/protocol/impl/MarketFaceProtocolTest.kt +++ b/mirai-core/src/commonTest/kotlin/message/protocol/impl/MarketFaceProtocolTest.kt @@ -9,18 +9,19 @@ package net.mamoe.mirai.internal.message.protocol.impl +import io.ktor.utils.io.core.* import net.mamoe.mirai.contact.MemberPermission import net.mamoe.mirai.internal.message.data.MarketFaceImpl import net.mamoe.mirai.internal.message.protocol.MessageProtocol import net.mamoe.mirai.message.data.Dice import net.mamoe.mirai.utils.hexToBytes -import org.junit.jupiter.api.BeforeEach -import org.junit.jupiter.api.Test +import kotlin.test.BeforeTest +import kotlin.test.Test internal class MarketFaceProtocolTest : AbstractMessageProtocolTest() { override val protocols: Array = arrayOf(MarketFaceProtocol(), TextProtocol()) - @BeforeEach + @BeforeTest fun `init group`() { defaultTarget = bot.addGroup(123, 1230003).apply { addMember(1230003, "user3", MemberPermission.OWNER) diff --git a/mirai-core/src/commonTest/kotlin/message/protocol/impl/MusicShareProtocolTest.kt b/mirai-core/src/commonTest/kotlin/message/protocol/impl/MusicShareProtocolTest.kt index a08176f62..10654b6a0 100644 --- a/mirai-core/src/commonTest/kotlin/message/protocol/impl/MusicShareProtocolTest.kt +++ b/mirai-core/src/commonTest/kotlin/message/protocol/impl/MusicShareProtocolTest.kt @@ -23,15 +23,15 @@ import net.mamoe.mirai.message.data.MusicKind.NeteaseCloudMusic import net.mamoe.mirai.message.data.MusicShare import net.mamoe.mirai.utils.castUp import net.mamoe.mirai.utils.hexToBytes -import org.junit.jupiter.api.BeforeEach -import org.junit.jupiter.api.Test +import kotlin.test.BeforeTest +import kotlin.test.Test import kotlin.test.assertTrue internal class MusicShareProtocolTest : AbstractMessageProtocolTest() { override val protocols: Array = arrayOf(TextProtocol(), MusicShareProtocol(), RichMessageProtocol(), GeneralMessageSenderProtocol()) - @BeforeEach + @BeforeTest fun `init group`() { defaultTarget = bot.addGroup(123, 1230003).apply { addMember(1230003, "user3", MemberPermission.OWNER) diff --git a/mirai-core/src/commonTest/kotlin/message/protocol/impl/PokeMessageProtocolTest.kt b/mirai-core/src/commonTest/kotlin/message/protocol/impl/PokeMessageProtocolTest.kt index 14573dda7..311ac19c9 100644 --- a/mirai-core/src/commonTest/kotlin/message/protocol/impl/PokeMessageProtocolTest.kt +++ b/mirai-core/src/commonTest/kotlin/message/protocol/impl/PokeMessageProtocolTest.kt @@ -13,13 +13,13 @@ import net.mamoe.mirai.contact.MemberPermission import net.mamoe.mirai.internal.message.protocol.MessageProtocol import net.mamoe.mirai.message.data.PokeMessage import net.mamoe.mirai.utils.hexToBytes -import org.junit.jupiter.api.BeforeEach -import org.junit.jupiter.api.Test +import kotlin.test.BeforeTest +import kotlin.test.Test internal class PokeMessageProtocolTest : AbstractMessageProtocolTest() { override val protocols: Array = arrayOf(TextProtocol(), PokeMessageProtocol()) - @BeforeEach + @BeforeTest fun `init group`() { defaultTarget = bot.addGroup(123, 1230003).apply { addMember(1230003, "user3", MemberPermission.OWNER) diff --git a/mirai-core/src/commonTest/kotlin/message/protocol/impl/QuoteReplyProtocolTest.kt b/mirai-core/src/commonTest/kotlin/message/protocol/impl/QuoteReplyProtocolTest.kt index 091ec78c8..84f93acae 100644 --- a/mirai-core/src/commonTest/kotlin/message/protocol/impl/QuoteReplyProtocolTest.kt +++ b/mirai-core/src/commonTest/kotlin/message/protocol/impl/QuoteReplyProtocolTest.kt @@ -19,7 +19,7 @@ import net.mamoe.mirai.message.data.PlainText import net.mamoe.mirai.message.data.QuoteReply import net.mamoe.mirai.message.data.messageChainOf import net.mamoe.mirai.utils.hexToBytes -import org.junit.jupiter.api.Test +import kotlin.test.Test internal class QuoteReplyProtocolTest : AbstractMessageProtocolTest() { override val protocols: Array = arrayOf(QuoteReplyProtocol(), TextProtocol()) diff --git a/mirai-core/src/commonTest/kotlin/message/protocol/impl/RichMessageProtocolTest.kt b/mirai-core/src/commonTest/kotlin/message/protocol/impl/RichMessageProtocolTest.kt index cec1a5968..e4305f6d6 100644 --- a/mirai-core/src/commonTest/kotlin/message/protocol/impl/RichMessageProtocolTest.kt +++ b/mirai-core/src/commonTest/kotlin/message/protocol/impl/RichMessageProtocolTest.kt @@ -12,13 +12,13 @@ package net.mamoe.mirai.internal.message.protocol.impl import net.mamoe.mirai.contact.MemberPermission import net.mamoe.mirai.internal.message.protocol.MessageProtocol import net.mamoe.mirai.utils.hexToBytes -import org.junit.jupiter.api.BeforeEach -import org.junit.jupiter.api.Test +import kotlin.test.BeforeTest +import kotlin.test.Test internal class RichMessageProtocolTest : AbstractMessageProtocolTest() { override val protocols: Array = arrayOf(TextProtocol(), RichMessageProtocol()) - @BeforeEach + @BeforeTest fun `init group`() { defaultTarget = bot.addGroup(123, 1230003).apply { addMember(1230003, "user3", MemberPermission.OWNER) diff --git a/mirai-core/src/commonTest/kotlin/message/protocol/impl/TextProtocolTest.kt b/mirai-core/src/commonTest/kotlin/message/protocol/impl/TextProtocolTest.kt index 4f7681101..a1a9d47c3 100644 --- a/mirai-core/src/commonTest/kotlin/message/protocol/impl/TextProtocolTest.kt +++ b/mirai-core/src/commonTest/kotlin/message/protocol/impl/TextProtocolTest.kt @@ -15,13 +15,13 @@ import net.mamoe.mirai.message.data.At import net.mamoe.mirai.message.data.AtAll import net.mamoe.mirai.message.data.PlainText import net.mamoe.mirai.utils.hexToBytes -import org.junit.jupiter.api.BeforeEach -import org.junit.jupiter.api.Test +import kotlin.test.BeforeTest +import kotlin.test.Test internal class TextProtocolTest : AbstractMessageProtocolTest() { override val protocols: Array = arrayOf(TextProtocol()) - @BeforeEach + @BeforeTest fun `init group`() { defaultTarget = bot.addGroup(123, 1230003).apply { addMember(1230003, "user3", MemberPermission.OWNER) diff --git a/mirai-core/src/commonTest/kotlin/message/protocol/impl/VipFaceProtocolTest.kt b/mirai-core/src/commonTest/kotlin/message/protocol/impl/VipFaceProtocolTest.kt index d2f52a389..b11b89346 100644 --- a/mirai-core/src/commonTest/kotlin/message/protocol/impl/VipFaceProtocolTest.kt +++ b/mirai-core/src/commonTest/kotlin/message/protocol/impl/VipFaceProtocolTest.kt @@ -13,13 +13,13 @@ import net.mamoe.mirai.contact.MemberPermission import net.mamoe.mirai.internal.message.protocol.MessageProtocol import net.mamoe.mirai.message.data.VipFace import net.mamoe.mirai.utils.hexToBytes -import org.junit.jupiter.api.BeforeEach -import org.junit.jupiter.api.Test +import kotlin.test.BeforeTest +import kotlin.test.Test internal class VipFaceProtocolTest : AbstractMessageProtocolTest() { override val protocols: Array = arrayOf(VipFaceProtocol(), TextProtocol()) - @BeforeEach + @BeforeTest fun `init group`() { defaultTarget = bot.addGroup(123, 1230003).apply { addMember(1230003, "user3", MemberPermission.OWNER) diff --git a/mirai-core/src/commonTest/kotlin/network/AwaitStateTest.kt b/mirai-core/src/commonTest/kotlin/network/AwaitStateTest.kt index 27588e9e9..4ef5310f3 100644 --- a/mirai-core/src/commonTest/kotlin/network/AwaitStateTest.kt +++ b/mirai-core/src/commonTest/kotlin/network/AwaitStateTest.kt @@ -1,16 +1,15 @@ /* - * Copyright 2019-2021 Mamoe Technologies and contributors. + * Copyright 2019-2022 Mamoe Technologies and contributors. * - * 此源代码的使用受 GNU AFFERO GENERAL PUBLIC LICENSE version 3 许可证的约束, 可以在以下链接找到该许可证. - * Use of this source code is governed by the GNU AGPLv3 license that can be found through the following link. + * 此源代码的使用受 GNU AFFERO GENERAL PUBLIC LICENSE version 3 许可证的约束, 可以在以下链接找到该许可证. + * Use of this source code is governed by the GNU AGPLv3 license that can be found through the following link. * - * https://github.com/mamoe/mirai/blob/master/LICENSE + * https://github.com/mamoe/mirai/blob/dev/LICENSE */ package net.mamoe.mirai.internal.network import kotlinx.coroutines.* -import kotlinx.coroutines.flow.collect import kotlinx.coroutines.flow.consumeAsFlow import kotlinx.coroutines.selects.select import net.mamoe.mirai.internal.network.framework.AbstractMockNetworkHandlerTest @@ -19,14 +18,9 @@ import net.mamoe.mirai.internal.network.handler.NetworkHandler.State.* import net.mamoe.mirai.internal.network.handler.awaitState import net.mamoe.mirai.internal.network.handler.awaitStateChange import net.mamoe.mirai.internal.test.runBlockingUnit -import org.junit.jupiter.api.Test -import java.util.concurrent.ConcurrentLinkedQueue -import java.util.concurrent.Executors +import net.mamoe.mirai.utils.ConcurrentLinkedDeque import kotlin.coroutines.ContinuationInterceptor -import kotlin.test.assertEquals -import kotlin.test.assertNotNull -import kotlin.test.assertSame -import kotlin.test.assertTrue +import kotlin.test.* internal class AwaitStateTest : AbstractMockNetworkHandlerTest() { @@ -34,7 +28,7 @@ internal class AwaitStateTest : AbstractMockNetworkHandlerTest() { fun `test select onStateChanged`() = runBlockingUnit { createNetworkHandler().run { assertState(INITIALIZED) - val queue = ConcurrentLinkedQueue() + val queue = ConcurrentLinkedDeque() launch(start = CoroutineStart.UNDISPATCHED) { select { stateChannel.onReceive { queue.add(it) } } assertEquals(1, queue.size) @@ -47,12 +41,11 @@ internal class AwaitStateTest : AbstractMockNetworkHandlerTest() { } } - @OptIn(ExperimentalCoroutinesApi::class) @Test fun `test whileSelect onStateChanged on demand`() = runBlockingUnit(singleThreadDispatcher + Job()) { createNetworkHandler().run { assertState(INITIALIZED) - val queue = ConcurrentLinkedQueue() + val queue = ConcurrentLinkedDeque() val selector = launch( singleThreadDispatcher + CoroutineExceptionHandler { _, throwable -> if (throwable !is CancellationException) throwable.printStackTrace() @@ -78,14 +71,20 @@ internal class AwaitStateTest : AbstractMockNetworkHandlerTest() { } // single thread so we can use [yield] to transfer dispatch - private val singleThreadDispatcher = Executors.newSingleThreadExecutor().asCoroutineDispatcher() + private val singleThreadDispatcher: CoroutineDispatcher = borrowSingleThreadDispatcher() @OptIn(ExperimentalCoroutinesApi::class) + @AfterTest + fun after() { + (singleThreadDispatcher as CloseableCoroutineDispatcher).close() + } + + @Test fun `test whileSelect onStateChanged drop if not listening`() = runBlockingUnit(singleThreadDispatcher + Job()) { createNetworkHandler().run { assertState(INITIALIZED) - val queue = ConcurrentLinkedQueue() + val queue = ConcurrentLinkedDeque() assertNotNull(setState(CONNECTING)) assertNotNull(setState(LOADING)) diff --git a/mirai-core/src/commonTest/kotlin/network/ServerListTest.kt b/mirai-core/src/commonTest/kotlin/network/ServerListTest.kt index 57c6b6c4e..77ee60696 100644 --- a/mirai-core/src/commonTest/kotlin/network/ServerListTest.kt +++ b/mirai-core/src/commonTest/kotlin/network/ServerListTest.kt @@ -1,18 +1,21 @@ /* - * Copyright 2019-2021 Mamoe Technologies and contributors. + * Copyright 2019-2022 Mamoe Technologies and contributors. * - * 此源代码的使用受 GNU AFFERO GENERAL PUBLIC LICENSE version 3 许可证的约束, 可以在以下链接找到该许可证. - * Use of this source code is governed by the GNU AGPLv3 license that can be found through the following link. + * 此源代码的使用受 GNU AFFERO GENERAL PUBLIC LICENSE version 3 许可证的约束, 可以在以下链接找到该许可证. + * Use of this source code is governed by the GNU AGPLv3 license that can be found through the following link. * - * https://github.com/mamoe/mirai/blob/master/LICENSE + * https://github.com/mamoe/mirai/blob/dev/LICENSE */ +@file:OptIn(TestOnly::class) + package net.mamoe.mirai.internal.network import net.mamoe.mirai.internal.network.components.ServerAddress import net.mamoe.mirai.internal.network.components.ServerList import net.mamoe.mirai.internal.network.components.ServerListImpl import net.mamoe.mirai.internal.test.AbstractTest +import net.mamoe.mirai.utils.TestOnly import kotlin.test.* internal class ServerListTest : AbstractTest() { @@ -32,7 +35,7 @@ internal class ServerListTest : AbstractTest() { val instance = ServerListImpl() val old = instance.getLastPolledIP() assertNotNull(old) - assert(old.isEmpty()) + assertTrue { old.isEmpty() } assertNotNull(instance.pollCurrent()) val new = instance.getLastPolledIP() assertNotNull(new) diff --git a/mirai-core/src/commonTest/kotlin/network/component/AbstractMutableComponentStorageTest.kt b/mirai-core/src/commonTest/kotlin/network/component/AbstractMutableComponentStorageTest.kt index 4b829e94c..972262d58 100644 --- a/mirai-core/src/commonTest/kotlin/network/component/AbstractMutableComponentStorageTest.kt +++ b/mirai-core/src/commonTest/kotlin/network/component/AbstractMutableComponentStorageTest.kt @@ -1,16 +1,16 @@ /* - * Copyright 2019-2021 Mamoe Technologies and contributors. + * Copyright 2019-2022 Mamoe Technologies and contributors. * - * 此源代码的使用受 GNU AFFERO GENERAL PUBLIC LICENSE version 3 许可证的约束, 可以在以下链接找到该许可证. - * Use of this source code is governed by the GNU AGPLv3 license that can be found through the following link. + * 此源代码的使用受 GNU AFFERO GENERAL PUBLIC LICENSE version 3 许可证的约束, 可以在以下链接找到该许可证. + * Use of this source code is governed by the GNU AGPLv3 license that can be found through the following link. * - * https://github.com/mamoe/mirai/blob/master/LICENSE + * https://github.com/mamoe/mirai/blob/dev/LICENSE */ package net.mamoe.mirai.internal.network.component import net.mamoe.mirai.internal.test.AbstractTest -import org.junit.jupiter.api.Test +import kotlin.test.Test import kotlin.test.assertEquals internal abstract class AbstractMutableComponentStorageTest : AbstractTest() { diff --git a/mirai-core/src/commonTest/kotlin/network/component/BotInitProcessorTest.kt b/mirai-core/src/commonTest/kotlin/network/component/BotInitProcessorTest.kt index b4d4f91d1..c653b4f01 100644 --- a/mirai-core/src/commonTest/kotlin/network/component/BotInitProcessorTest.kt +++ b/mirai-core/src/commonTest/kotlin/network/component/BotInitProcessorTest.kt @@ -1,32 +1,35 @@ /* - * Copyright 2019-2021 Mamoe Technologies and contributors. + * Copyright 2019-2022 Mamoe Technologies and contributors. * - * 此源代码的使用受 GNU AFFERO GENERAL PUBLIC LICENSE version 3 许可证的约束, 可以在以下链接找到该许可证. - * Use of this source code is governed by the GNU AGPLv3 license that can be found through the following link. + * 此源代码的使用受 GNU AFFERO GENERAL PUBLIC LICENSE version 3 许可证的约束, 可以在以下链接找到该许可证. + * Use of this source code is governed by the GNU AGPLv3 license that can be found through the following link. * - * https://github.com/mamoe/mirai/blob/master/LICENSE + * https://github.com/mamoe/mirai/blob/dev/LICENSE */ +@file:OptIn(TestOnly::class) + package net.mamoe.mirai.internal.network.component import kotlinx.coroutines.CompletableDeferred import kotlinx.coroutines.isActive import net.mamoe.mirai.internal.contact.uin import net.mamoe.mirai.internal.network.components.BotInitProcessor -import net.mamoe.mirai.internal.network.framework.AbstractNettyNHTest -import net.mamoe.mirai.internal.network.framework.AbstractNettyNHTestWithSelector +import net.mamoe.mirai.internal.network.framework.AbstractCommonNHTest +import net.mamoe.mirai.internal.network.framework.AbstractCommonNHTestWithSelector import net.mamoe.mirai.internal.network.handler.NetworkHandler import net.mamoe.mirai.internal.network.protocol.data.jce.RequestPushForceOffline import net.mamoe.mirai.internal.network.protocol.packet.IncomingPacket import net.mamoe.mirai.internal.network.protocol.packet.chat.receive.MessageSvcPushForceOffline import net.mamoe.mirai.internal.test.runBlockingUnit -import org.junit.jupiter.api.Test +import net.mamoe.mirai.utils.TestOnly +import kotlin.test.Test import kotlin.test.assertEquals import kotlin.test.assertFalse import kotlin.test.assertTrue internal class BotInitProcessorTest { - class WithoutSelector : AbstractNettyNHTest() { + class WithoutSelector : AbstractCommonNHTest() { @Test fun `BotInitProcessor halted`() = runBlockingUnit { val p = setComponent(BotInitProcessor, object : BotInitProcessor { @@ -43,7 +46,7 @@ internal class BotInitProcessorTest { } }) assertTrue { network.isActive } - network.setStateLoading(channel) + network.setStateLoading(conn) assertEquals(1, p.ranTimes) assertEquals(0, p.haltedTimes) assertState(NetworkHandler.State.LOADING) @@ -61,7 +64,7 @@ internal class BotInitProcessorTest { } } - class WithSelector : AbstractNettyNHTestWithSelector() { + class WithSelector : AbstractCommonNHTestWithSelector() { @Test fun `BotInitProcessor halted`() = runBlockingUnit { bot.configuration.autoReconnectOnForceOffline = true @@ -79,7 +82,7 @@ internal class BotInitProcessorTest { } }) assertTrue { network.isActive } - network.setStateLoading(channel) + network.setStateLoading(conn) assertEquals(1, p.ranTimes) assertEquals(0, p.haltedTimes) assertState(NetworkHandler.State.LOADING) diff --git a/mirai-core/src/commonTest/kotlin/network/component/CombinedStorageTest.kt b/mirai-core/src/commonTest/kotlin/network/component/CombinedStorageTest.kt index a815f8492..8a37c9732 100644 --- a/mirai-core/src/commonTest/kotlin/network/component/CombinedStorageTest.kt +++ b/mirai-core/src/commonTest/kotlin/network/component/CombinedStorageTest.kt @@ -1,16 +1,16 @@ /* - * Copyright 2019-2021 Mamoe Technologies and contributors. + * Copyright 2019-2022 Mamoe Technologies and contributors. * - * 此源代码的使用受 GNU AFFERO GENERAL PUBLIC LICENSE version 3 许可证的约束, 可以在以下链接找到该许可证. - * Use of this source code is governed by the GNU AGPLv3 license that can be found through the following link. + * 此源代码的使用受 GNU AFFERO GENERAL PUBLIC LICENSE version 3 许可证的约束, 可以在以下链接找到该许可证. + * Use of this source code is governed by the GNU AGPLv3 license that can be found through the following link. * - * https://github.com/mamoe/mirai/blob/master/LICENSE + * https://github.com/mamoe/mirai/blob/dev/LICENSE */ package net.mamoe.mirai.internal.network.component import net.mamoe.mirai.internal.test.AbstractTest -import org.junit.jupiter.api.Test +import kotlin.test.Test import kotlin.test.assertEquals import kotlin.test.assertSame diff --git a/mirai-core/src/commonTest/kotlin/network/component/ComponentKeyTest.kt b/mirai-core/src/commonTest/kotlin/network/component/ComponentKeyTest.kt index a2bb8dac6..7e356ad5c 100644 --- a/mirai-core/src/commonTest/kotlin/network/component/ComponentKeyTest.kt +++ b/mirai-core/src/commonTest/kotlin/network/component/ComponentKeyTest.kt @@ -1,10 +1,10 @@ /* - * Copyright 2019-2021 Mamoe Technologies and contributors. + * Copyright 2019-2022 Mamoe Technologies and contributors. * - * 此源代码的使用受 GNU AFFERO GENERAL PUBLIC LICENSE version 3 许可证的约束, 可以在以下链接找到该许可证. - * Use of this source code is governed by the GNU AGPLv3 license that can be found through the following link. + * 此源代码的使用受 GNU AFFERO GENERAL PUBLIC LICENSE version 3 许可证的约束, 可以在以下链接找到该许可证. + * Use of this source code is governed by the GNU AGPLv3 license that can be found through the following link. * - * https://github.com/mamoe/mirai/blob/master/LICENSE + * https://github.com/mamoe/mirai/blob/dev/LICENSE */ package net.mamoe.mirai.internal.network.component @@ -12,7 +12,7 @@ package net.mamoe.mirai.internal.network.component import net.mamoe.mirai.internal.network.component.ComponentKey.Companion.componentName import net.mamoe.mirai.internal.network.component.ComponentKey.Companion.smartToString import net.mamoe.mirai.internal.test.AbstractTest -import org.junit.jupiter.api.Test +import kotlin.test.Test import kotlin.test.assertEquals private open class TestComponent { diff --git a/mirai-core/src/commonTest/kotlin/network/component/ConcurrentComponentStorageTest.kt b/mirai-core/src/commonTest/kotlin/network/component/ConcurrentComponentStorageTest.kt index 76a2db95b..681b1b9e4 100644 --- a/mirai-core/src/commonTest/kotlin/network/component/ConcurrentComponentStorageTest.kt +++ b/mirai-core/src/commonTest/kotlin/network/component/ConcurrentComponentStorageTest.kt @@ -1,15 +1,15 @@ /* - * Copyright 2019-2021 Mamoe Technologies and contributors. + * Copyright 2019-2022 Mamoe Technologies and contributors. * - * 此源代码的使用受 GNU AFFERO GENERAL PUBLIC LICENSE version 3 许可证的约束, 可以在以下链接找到该许可证. - * Use of this source code is governed by the GNU AGPLv3 license that can be found through the following link. + * 此源代码的使用受 GNU AFFERO GENERAL PUBLIC LICENSE version 3 许可证的约束, 可以在以下链接找到该许可证. + * Use of this source code is governed by the GNU AGPLv3 license that can be found through the following link. * - * https://github.com/mamoe/mirai/blob/master/LICENSE + * https://github.com/mamoe/mirai/blob/dev/LICENSE */ package net.mamoe.mirai.internal.network.component -import org.junit.jupiter.api.Test +import kotlin.test.Test import kotlin.test.assertEquals internal data class TestComponent2( diff --git a/mirai-core/src/commonTest/kotlin/network/component/EventDispatcherTest.kt b/mirai-core/src/commonTest/kotlin/network/component/EventDispatcherTest.kt index 38f28c1df..1be1e988e 100644 --- a/mirai-core/src/commonTest/kotlin/network/component/EventDispatcherTest.kt +++ b/mirai-core/src/commonTest/kotlin/network/component/EventDispatcherTest.kt @@ -1,10 +1,10 @@ /* - * Copyright 2019-2021 Mamoe Technologies and contributors. + * Copyright 2019-2022 Mamoe Technologies and contributors. * - * 此源代码的使用受 GNU AFFERO GENERAL PUBLIC LICENSE version 3 许可证的约束, 可以在以下链接找到该许可证. - * Use of this source code is governed by the GNU AGPLv3 license that can be found through the following link. + * 此源代码的使用受 GNU AFFERO GENERAL PUBLIC LICENSE version 3 许可证的约束, 可以在以下链接找到该许可证. + * Use of this source code is governed by the GNU AGPLv3 license that can be found through the following link. * - * https://github.com/mamoe/mirai/blob/master/LICENSE + * https://github.com/mamoe/mirai/blob/dev/LICENSE */ @file:OptIn(TestOnly::class) @@ -21,7 +21,7 @@ import net.mamoe.mirai.internal.test.runBlockingUnit import net.mamoe.mirai.utils.MiraiLogger import net.mamoe.mirai.utils.TestOnly import network.framework.components.TestEventDispatcherImpl -import org.junit.jupiter.api.Test +import kotlin.test.Test internal class EventDispatcherTest : AbstractTest() { private class Ev : AbstractEvent() diff --git a/mirai-core/src/commonTest/kotlin/network/framework/AbstractCommonNHTest.kt b/mirai-core/src/commonTest/kotlin/network/framework/AbstractCommonNHTest.kt new file mode 100644 index 000000000..9368c290b --- /dev/null +++ b/mirai-core/src/commonTest/kotlin/network/framework/AbstractCommonNHTest.kt @@ -0,0 +1,110 @@ +/* + * Copyright 2019-2022 Mamoe Technologies and contributors. + * + * 此源代码的使用受 GNU AFFERO GENERAL PUBLIC LICENSE version 3 许可证的约束, 可以在以下链接找到该许可证. + * Use of this source code is governed by the GNU AGPLv3 license that can be found through the following link. + * + * https://github.com/mamoe/mirai/blob/dev/LICENSE + */ + +package net.mamoe.mirai.internal.network.framework + +import kotlinx.coroutines.CompletableDeferred +import net.mamoe.mirai.internal.QQAndroidBot +import net.mamoe.mirai.internal.network.Packet +import net.mamoe.mirai.internal.network.handler.* +import net.mamoe.mirai.internal.network.protocol.packet.IncomingPacket +import net.mamoe.mirai.internal.network.protocol.packet.OutgoingPacket +import net.mamoe.mirai.utils.ExceptionCollector + +/** + * You may need to override [createConnection] + */ +internal abstract class TestCommonNetworkHandler( + override val bot: QQAndroidBot, + context: NetworkHandlerContext, + address: SocketAddress, +) : CommonNetworkHandler(context, address), ITestNetworkHandler { + override suspend fun createConnection(): PlatformConn { + return PlatformConn() + } + + override fun PlatformConn.writeAndFlushOrCloseAsync(packet: OutgoingPacket) { + for (packetReplier in packetRepliers) { + packetReplier.run { + object : PacketReplierContext { + override fun reply(incoming: IncomingPacket) { + collectReceived(incoming) + } + + override fun reply(incoming: Packet) { + reply(IncomingPacket(packet.commandName, packet.sequenceId, incoming)) + } + + override fun reply(incoming: Throwable) { + reply(IncomingPacket(packet.commandName, packet.sequenceId, incoming)) + } + }.onSend(packet) + } + } + } + + @Suppress("EXTENSION_SHADOWED_BY_MEMBER") + override fun PlatformConn.close() { + } + + override fun setStateClosed(exception: Throwable?): NetworkHandlerSupport.BaseStateImpl? { + return setState { StateClosed(exception) } + } + + override fun setStateConnecting(exception: Throwable?): NetworkHandlerSupport.BaseStateImpl? { + return setState { StateConnecting(ExceptionCollector(exception)) } + } + + override fun setStateOK(conn: PlatformConn, exception: Throwable?): NetworkHandlerSupport.BaseStateImpl? { + exception?.printStackTrace() + return setState { StateOK(conn, CompletableDeferred(Unit)) } + } + + override fun setStateLoading(conn: PlatformConn): NetworkHandlerSupport.BaseStateImpl? { + return setState { StateLoading(conn) } + } + + private val packetRepliers = mutableListOf() + + fun addPacketReplier(packetReplier: PacketReplier) { + packetRepliers.add(packetReplier) + } +} + +/** + * 应答器, 模拟服务器返回. + */ +internal fun interface PacketReplier { + fun PacketReplierContext.onSend(packet: OutgoingPacket) +} + +internal interface PacketReplierContext { + fun reply(incoming: IncomingPacket) + fun reply(incoming: Packet) + fun reply(incoming: Throwable) +} + +/** + * Without selector. When network is closed, it will not reconnect, so that you can check for its states. + * + * @see AbstractCommonNHTestWithSelector + */ +internal expect abstract class AbstractCommonNHTest() : + AbstractRealNetworkHandlerTest { + + val conn: PlatformConn + + override val network: TestCommonNetworkHandler + + override val factory: NetworkHandlerFactory + + protected fun removeOutgoingPacketEncoder() +} + +internal expect class PlatformConn() \ No newline at end of file diff --git a/mirai-core/src/commonTest/kotlin/network/framework/AbstractNettyNHTestWithSelector.kt b/mirai-core/src/commonTest/kotlin/network/framework/AbstractCommonNHTestWithSelector.kt similarity index 57% rename from mirai-core/src/commonTest/kotlin/network/framework/AbstractNettyNHTestWithSelector.kt rename to mirai-core/src/commonTest/kotlin/network/framework/AbstractCommonNHTestWithSelector.kt index 64adec3ef..6955af4c7 100644 --- a/mirai-core/src/commonTest/kotlin/network/framework/AbstractNettyNHTestWithSelector.kt +++ b/mirai-core/src/commonTest/kotlin/network/framework/AbstractCommonNHTestWithSelector.kt @@ -1,17 +1,16 @@ /* - * Copyright 2019-2021 Mamoe Technologies and contributors. + * Copyright 2019-2022 Mamoe Technologies and contributors. * - * 此源代码的使用受 GNU AFFERO GENERAL PUBLIC LICENSE version 3 许可证的约束, 可以在以下链接找到该许可证. - * Use of this source code is governed by the GNU AGPLv3 license that can be found through the following link. + * 此源代码的使用受 GNU AFFERO GENERAL PUBLIC LICENSE version 3 许可证的约束, 可以在以下链接找到该许可证. + * Use of this source code is governed by the GNU AGPLv3 license that can be found through the following link. * - * https://github.com/mamoe/mirai/blob/master/LICENSE + * https://github.com/mamoe/mirai/blob/dev/LICENSE */ @file:Suppress("MemberVisibilityCanBePrivate") package net.mamoe.mirai.internal.network.framework -import io.netty.channel.Channel import net.mamoe.mirai.internal.QQAndroidBot import net.mamoe.mirai.internal.network.components.BotOfflineEventMonitor import net.mamoe.mirai.internal.network.components.BotOfflineEventMonitorImpl @@ -20,37 +19,37 @@ import net.mamoe.mirai.internal.network.handler.NetworkHandlerSupport import net.mamoe.mirai.internal.network.handler.TestSelector import net.mamoe.mirai.internal.network.handler.selector.NetworkHandlerSelector import net.mamoe.mirai.internal.network.handler.selector.SelectorNetworkHandler -import net.mamoe.mirai.utils.cast /** * When network is closed, it will reconnect, so that you test for real environment, * but you cannot check for its states (it will never be CLOSED until some fatal error, just like in real). */ -internal abstract class AbstractNettyNHTestWithSelector : AbstractRealNetworkHandlerTest() { +internal abstract class AbstractCommonNHTestWithSelector : + AbstractRealNetworkHandlerTest() { init { overrideComponents[BotOfflineEventMonitor] = BotOfflineEventMonitorImpl() } - val channel = AbstractNettyNHTest.NettyNHTestChannel( - logger = lazy { bot.logger }, - ) + val conn = PlatformConn() - val selector = TestSelector { - object : TestNettyNH(bot, createContext(), createAddress()) { - override suspend fun createConnection(decodePipeline: PacketDecodePipeline): Channel = channel + val selector = TestSelector { + object : TestCommonNetworkHandler(bot, createContext(), createAddress()) { + // override suspend fun createConnection(decodePipeline: PacketDecodePipeline): PlatformConn = channel + override suspend fun createConnection(): PlatformConn { + return conn + } } } override val factory: NetworkHandlerFactory = NetworkHandlerFactory { _, _ -> TestSelectorNetworkHandler(selector, bot) } - override val network: TestSelectorNetworkHandler get() = bot.network.cast() } internal class TestSelectorNetworkHandler( - selector: NetworkHandlerSelector, override val bot: QQAndroidBot, -) : ITestNetworkHandler, - SelectorNetworkHandler(selector) { + selector: NetworkHandlerSelector, override val bot: QQAndroidBot, +) : ITestNetworkHandler, + SelectorNetworkHandler(selector) { fun currentInstance() = selector.getCurrentInstanceOrCreate() fun currentInstanceOrNull() = selector.getCurrentInstanceOrNull() @@ -63,12 +62,12 @@ internal class TestSelectorNetworkHandler( return selector.getCurrentInstanceOrCreate().setStateConnecting(exception) } - override fun setStateOK(channel: Channel, exception: Throwable?): NetworkHandlerSupport.BaseStateImpl? { - return selector.getCurrentInstanceOrCreate().setStateOK(channel, exception) + override fun setStateOK(conn: PlatformConn, exception: Throwable?): NetworkHandlerSupport.BaseStateImpl? { + return selector.getCurrentInstanceOrCreate().setStateOK(conn, exception) } - override fun setStateLoading(channel: Channel): NetworkHandlerSupport.BaseStateImpl? { - return selector.getCurrentInstanceOrCreate().setStateLoading(channel) + override fun setStateLoading(conn: PlatformConn): NetworkHandlerSupport.BaseStateImpl? { + return selector.getCurrentInstanceOrCreate().setStateLoading(conn) } } \ No newline at end of file diff --git a/mirai-core/src/commonTest/kotlin/network/framework/AbstractMockNetworkHandlerTest.kt b/mirai-core/src/commonTest/kotlin/network/framework/AbstractMockNetworkHandlerTest.kt index 62844d39f..06a287401 100644 --- a/mirai-core/src/commonTest/kotlin/network/framework/AbstractMockNetworkHandlerTest.kt +++ b/mirai-core/src/commonTest/kotlin/network/framework/AbstractMockNetworkHandlerTest.kt @@ -17,10 +17,7 @@ import net.mamoe.mirai.internal.BotAccount import net.mamoe.mirai.internal.MockBot import net.mamoe.mirai.internal.QQAndroidBot import net.mamoe.mirai.internal.network.component.ConcurrentComponentStorage -import net.mamoe.mirai.internal.network.components.EventDispatcher -import net.mamoe.mirai.internal.network.components.PacketLoggingStrategy -import net.mamoe.mirai.internal.network.components.PacketLoggingStrategyImpl -import net.mamoe.mirai.internal.network.components.SsoProcessor +import net.mamoe.mirai.internal.network.components.* import net.mamoe.mirai.internal.network.framework.components.TestImagePatcher import net.mamoe.mirai.internal.network.framework.components.TestSsoProcessor import net.mamoe.mirai.internal.network.handler.NetworkHandler @@ -31,15 +28,11 @@ import net.mamoe.mirai.internal.utils.ImagePatcher import net.mamoe.mirai.internal.utils.subLogger import net.mamoe.mirai.utils.MiraiLogger import network.framework.components.TestEventDispatcherImpl -import org.junit.jupiter.api.TestInstance import kotlin.math.absoluteValue import kotlin.random.Random import kotlin.test.assertEquals -/** - * Mock network, can only test implementation of the framework of [NetworkHandler]. - */ -@TestInstance(TestInstance.Lifecycle.PER_METHOD) + internal abstract class AbstractMockNetworkHandlerTest : AbstractNetworkHandlerTest() { protected open fun createNetworkHandlerContext() = TestNetworkHandlerContext(bot, logger, components) protected open fun createNetworkHandler() = TestNetworkHandler(bot, createNetworkHandlerContext()) @@ -73,9 +66,10 @@ internal abstract class AbstractMockNetworkHandlerTest : AbstractNetworkHandlerT ) set(ImagePatcher, TestImagePatcher()) set(PacketLoggingStrategy, PacketLoggingStrategyImpl(bot)) + set(AccountSecretsManager, MemoryAccountSecretsManager()) } fun NetworkHandler.assertState(state: NetworkHandler.State) { - assertEquals(state, state) + assertEquals(this.state, state) } } \ No newline at end of file diff --git a/mirai-core/src/commonTest/kotlin/network/framework/AbstractNettyNHTest.kt b/mirai-core/src/commonTest/kotlin/network/framework/AbstractNettyNHTest.kt deleted file mode 100644 index 0c69fd14f..000000000 --- a/mirai-core/src/commonTest/kotlin/network/framework/AbstractNettyNHTest.kt +++ /dev/null @@ -1,170 +0,0 @@ -/* - * Copyright 2019-2022 Mamoe Technologies and contributors. - * - * 此源代码的使用受 GNU AFFERO GENERAL PUBLIC LICENSE version 3 许可证的约束, 可以在以下链接找到该许可证. - * Use of this source code is governed by the GNU AGPLv3 license that can be found through the following link. - * - * https://github.com/mamoe/mirai/blob/dev/LICENSE - */ - -package net.mamoe.mirai.internal.network.framework - -import io.ktor.utils.io.core.* -import io.netty.channel.Channel -import io.netty.channel.embedded.EmbeddedChannel -import io.netty.util.ReferenceCountUtil -import kotlinx.coroutines.CompletableDeferred -import kotlinx.coroutines.CoroutineScope -import kotlinx.serialization.InternalSerializationApi -import kotlinx.serialization.KSerializer -import kotlinx.serialization.serializer -import net.mamoe.mirai.internal.AbstractBot -import net.mamoe.mirai.internal.QQAndroidBot -import net.mamoe.mirai.internal.network.components.BotOfflineEventMonitor -import net.mamoe.mirai.internal.network.components.RawIncomingPacket -import net.mamoe.mirai.internal.network.handler.NetworkHandlerContext -import net.mamoe.mirai.internal.network.handler.NetworkHandlerFactory -import net.mamoe.mirai.internal.network.handler.NetworkHandlerSupport -import net.mamoe.mirai.internal.network.impl.netty.NettyNetworkHandler -import net.mamoe.mirai.internal.network.protocol.packet.OutgoingPacket -import net.mamoe.mirai.internal.utils.io.ProtoBuf -import net.mamoe.mirai.internal.utils.io.serialization.writeProtoBuf -import net.mamoe.mirai.utils.ExceptionCollector -import net.mamoe.mirai.utils.MiraiLogger -import net.mamoe.mirai.utils.cast -import net.mamoe.mirai.utils.error -import java.net.SocketAddress - -/** - * You may need to override [createConnection] - */ -internal abstract class TestNettyNH( - override val bot: QQAndroidBot, - context: NetworkHandlerContext, - address: SocketAddress, -) : NettyNetworkHandler(context, address), ITestNetworkHandler { - - protected abstract suspend fun createConnection(decodePipeline: PacketDecodePipeline): Channel - final override suspend fun createConnection(): Channel { - return createConnection(createDummyDecodePipeline()) - } - - override fun setStateClosed(exception: Throwable?): NetworkHandlerSupport.BaseStateImpl? { - return setState { StateClosed(exception) } - } - - override fun setStateConnecting(exception: Throwable?): NetworkHandlerSupport.BaseStateImpl? { - return setState { StateConnecting(ExceptionCollector(exception)) } - } - - override fun setStateOK(channel: Channel, exception: Throwable?): NetworkHandlerSupport.BaseStateImpl? { - exception?.printStackTrace() - return setState { StateOK(channel, CompletableDeferred(Unit)) } - } - - override fun setStateLoading(channel: Channel): NetworkHandlerSupport.BaseStateImpl? { - return setState { StateLoading(channel) } - } - -} - -/** - * Without selector. When network is closed, it will not reconnect, so that you can check for its states. - * - * @see AbstractNettyNHTestWithSelector - */ -internal abstract class AbstractNettyNHTest : AbstractRealNetworkHandlerTest() { - - init { - overrideComponents[BotOfflineEventMonitor] = object : BotOfflineEventMonitor { - override fun attachJob(bot: AbstractBot, scope: CoroutineScope) { - } - } - } - - class NettyNHTestChannel( - val logger: Lazy, - var fakeServer: (NettyNHTestChannel.(msg: Any?) -> Unit)? = null, - ) : EmbeddedChannel() { - @OptIn(InternalSerializationApi::class) - fun listen(listener: (OutgoingPacket) -> Any?) { - fakeServer = { packet -> - if (packet is OutgoingPacket) { - val rsp0 = when (val rsp = listener(packet)) { - null -> null - is Unit -> null - is ByteArray -> { - RawIncomingPacket( - commandName = packet.commandName, - sequenceId = packet.sequenceId, - body = rsp - ) - } - is RawIncomingPacket -> rsp - is ProtoBuf -> { - RawIncomingPacket( - commandName = packet.commandName, - sequenceId = packet.sequenceId, - body = buildPacket { - writeProtoBuf( - rsp::class.serializer().cast>(), - rsp - ) - }.readBytes() - ) - } - else -> { - logger.value.error { "Failed to respond $rsp" } - null - } - } - if (rsp0 != null) { - pipeline().fireChannelRead(rsp0) - } - } - ReferenceCountUtil.release(packet) - } - } - - public /*internal*/ override fun doRegister() { - super.doRegister() // Set channel state to ACTIVE - // Drop old handlers - pipeline().let { p -> - while (p.first() != null) { - p.removeFirst() - } - } - } - - override fun handleInboundMessage(msg: Any?) { - ReferenceCountUtil.release(msg) // Not handled, Drop - } - - override fun handleOutboundMessage(msg: Any?) { - fakeServer?.invoke(this, msg) ?: ReferenceCountUtil.release(msg) - } - } - - val channel = NettyNHTestChannel( - logger = lazy { bot.logger }, - ) - - override val network: TestNettyNH get() = bot.network as TestNettyNH - - override val factory: NetworkHandlerFactory = - NetworkHandlerFactory { context, address -> - object : TestNettyNH(bot, context, address) { - override suspend fun createConnection(decodePipeline: PacketDecodePipeline): Channel = - channel.apply { - doRegister() // restart channel - setupChannelPipeline(pipeline(), decodePipeline) - } - } - } - - protected fun removeOutgoingPacketEncoder() { - kotlin.runCatching { - channel.pipeline().remove("outgoing-packet-encoder") - } - } -} diff --git a/mirai-core/src/commonTest/kotlin/network/framework/AbstractNetworkHandlerTest.kt b/mirai-core/src/commonTest/kotlin/network/framework/AbstractNetworkHandlerTest.kt index 1c1c1dadc..7f6a54160 100644 --- a/mirai-core/src/commonTest/kotlin/network/framework/AbstractNetworkHandlerTest.kt +++ b/mirai-core/src/commonTest/kotlin/network/framework/AbstractNetworkHandlerTest.kt @@ -1,18 +1,19 @@ /* - * Copyright 2019-2021 Mamoe Technologies and contributors. + * Copyright 2019-2022 Mamoe Technologies and contributors. * - * 此源代码的使用受 GNU AFFERO GENERAL PUBLIC LICENSE version 3 许可证的约束, 可以在以下链接找到该许可证. - * Use of this source code is governed by the GNU AGPLv3 license that can be found through the following link. + * 此源代码的使用受 GNU AFFERO GENERAL PUBLIC LICENSE version 3 许可证的约束, 可以在以下链接找到该许可证. + * Use of this source code is governed by the GNU AGPLv3 license that can be found through the following link. * - * https://github.com/mamoe/mirai/blob/master/LICENSE + * https://github.com/mamoe/mirai/blob/dev/LICENSE */ package net.mamoe.mirai.internal.network.framework import net.mamoe.mirai.internal.network.handler.selector.AbstractKeepAliveNetworkHandlerSelector import net.mamoe.mirai.internal.test.AbstractTest -import org.junit.jupiter.api.AfterEach -import org.junit.jupiter.api.BeforeEach +import net.mamoe.mirai.utils.setSystemProp +import kotlin.test.AfterTest +import kotlin.test.BeforeTest internal sealed class AbstractNetworkHandlerTest : AbstractTest() { /////////////////////////////////////////////////////////////////////////// @@ -20,15 +21,15 @@ internal sealed class AbstractNetworkHandlerTest : AbstractTest() { /////////////////////////////////////////////////////////////////////////// init { - System.setProperty("mirai.event.launch.undispatched", "true") // allow us to do some + setSystemProp("mirai.event.launch.undispatched", "true") // allow us to do some } - @BeforeEach + @BeforeTest fun be() { AbstractKeepAliveNetworkHandlerSelector.RECONNECT_DELAY = 0 } - @AfterEach + @AfterTest fun af() { AbstractKeepAliveNetworkHandlerSelector.RECONNECT_DELAY = 3000 } diff --git a/mirai-core/src/commonTest/kotlin/network/framework/AbstractRealNetworkHandlerTest.kt b/mirai-core/src/commonTest/kotlin/network/framework/AbstractRealNetworkHandlerTest.kt index c938200d8..f4828ea71 100644 --- a/mirai-core/src/commonTest/kotlin/network/framework/AbstractRealNetworkHandlerTest.kt +++ b/mirai-core/src/commonTest/kotlin/network/framework/AbstractRealNetworkHandlerTest.kt @@ -7,13 +7,18 @@ * https://github.com/mamoe/mirai/blob/dev/LICENSE */ +@file:OptIn(TestOnly::class) + package net.mamoe.mirai.internal.network.framework +import kotlinx.coroutines.CoroutineScope import kotlinx.coroutines.SupervisorJob -import net.mamoe.mirai.internal.BotAccount -import net.mamoe.mirai.internal.MockAccount -import net.mamoe.mirai.internal.MockConfiguration -import net.mamoe.mirai.internal.QQAndroidBot +import net.mamoe.mirai.internal.* +import net.mamoe.mirai.internal.contact.uin +import net.mamoe.mirai.internal.network.KeyWithCreationTime +import net.mamoe.mirai.internal.network.KeyWithExpiry +import net.mamoe.mirai.internal.network.WLoginSigInfo +import net.mamoe.mirai.internal.network.WLoginSimpleInfo import net.mamoe.mirai.internal.network.component.ComponentKey import net.mamoe.mirai.internal.network.component.ConcurrentComponentStorage import net.mamoe.mirai.internal.network.component.setAll @@ -23,33 +28,38 @@ import net.mamoe.mirai.internal.network.handler.NetworkHandler import net.mamoe.mirai.internal.network.handler.NetworkHandler.State import net.mamoe.mirai.internal.network.handler.NetworkHandlerContextImpl import net.mamoe.mirai.internal.network.handler.NetworkHandlerFactory +import net.mamoe.mirai.internal.network.handler.SocketAddress import net.mamoe.mirai.internal.network.protocol.data.jce.SvcRespRegister import net.mamoe.mirai.internal.network.protocol.packet.login.StatSvc import net.mamoe.mirai.internal.utils.subLogger -import net.mamoe.mirai.utils.MiraiLogger -import net.mamoe.mirai.utils.debug -import net.mamoe.mirai.utils.lateinitMutableProperty +import net.mamoe.mirai.utils.* import network.framework.components.TestEventDispatcherImpl -import org.junit.jupiter.api.AfterEach -import org.junit.jupiter.api.TestInstance -import java.net.InetSocketAddress -import java.util.concurrent.ConcurrentLinkedQueue +import kotlin.random.Random +import kotlin.test.AfterTest import kotlin.test.assertEquals /** * With real factory and components as in [QQAndroidBot.components]. * - * Extend [AbstractNettyNHTestWithSelector] or [AbstractNettyNHTest]. + * Extend [AbstractCommonNHTestWithSelector] or [AbstractCommonNHTest]. */ -@TestInstance(TestInstance.Lifecycle.PER_METHOD) -internal sealed class AbstractRealNetworkHandlerTest : AbstractNetworkHandlerTest() { +internal abstract class AbstractRealNetworkHandlerTest : AbstractNetworkHandlerTest() { abstract val factory: NetworkHandlerFactory - abstract val network: H + + /** + * This is shared for all [createBot] by default. `network === bot.network`, unless you change it. + */ + open val network: H by lateinitMutableProperty { + factory.create(createContext(), createAddress()) + } private var botInit = false - var bot: QQAndroidBot by lateinitMutableProperty { botInit = true; createBot() } + var bot: QQAndroidBot by lateinitMutableProperty { + botInit = true + createBot() + } - @AfterEach + @AfterTest fun afterEach() { if (botInit) bot.close() } @@ -57,10 +67,16 @@ internal sealed class AbstractRealNetworkHandlerTest : Abstr protected open fun createBot(account: BotAccount = MockAccount): QQAndroidBot { return object : QQAndroidBot(account, MockConfiguration.copy()) { override fun createBotLevelComponents(): ConcurrentComponentStorage = - super.createBotLevelComponents().apply { setAll(overrideComponents) } + super.createBotLevelComponents().apply { + setAll(overrideComponents) + get(AccountSecretsManager).getSecretsOrCreate( + account, + DeviceInfo.random(Random(1)) + ).wLoginSigInfo = createWLoginSigInfo(uin) + } override fun createNetworkHandler(): NetworkHandler = - this@AbstractRealNetworkHandlerTest.createHandler() + this@AbstractRealNetworkHandlerTest.network } } @@ -79,6 +95,7 @@ internal sealed class AbstractRealNetworkHandlerTest : Abstr /** * This overrides [QQAndroidBot.components] */ + @OptIn(TestOnly::class) val overrideComponents = ConcurrentComponentStorage().apply { set(SsoProcessorContext, SsoProcessorContextImpl(bot)) set(SsoProcessor, object : TestSsoProcessor(bot) { @@ -139,21 +156,26 @@ internal sealed class AbstractRealNetworkHandlerTest : Abstr bot.logger.subLogger("TestEventDispatcherImpl") ) ) + + set(BotOfflineEventMonitor, object : BotOfflineEventMonitor { + override fun attachJob(bot: AbstractBot, scope: CoroutineScope) { + } + }) // set(StateObserver, bot.run { stateObserverChain() }) } + fun setComponent(key: ComponentKey, instance: T): T { overrideComponents[key] = instance return instance } - open fun createHandler(): NetworkHandler = factory.create(createContext(), createAddress()) open fun createContext(): NetworkHandlerContextImpl = NetworkHandlerContextImpl(bot, networkLogger, bot.createNetworkLevelComponents()) //Use overrideComponents to avoid StackOverflowError when applying components - open fun createAddress(): InetSocketAddress = - overrideComponents[ServerList].pollAny().let { InetSocketAddress.createUnresolved(it.host, it.port) } + open fun createAddress(): SocketAddress = + overrideComponents[ServerList].pollAny().let { SocketAddress(it.host, it.port) } /////////////////////////////////////////////////////////////////////////// // Assertions @@ -183,3 +205,83 @@ internal fun AbstractRealNetworkHandlerTest<*>.setSsoProcessor(action: suspend S override suspend fun login(handler: NetworkHandler) = action(handler) } } + +private fun createWLoginSigInfo( + uin: Long, + creationTime: Long = currentTimeSeconds(), + random: Random = Random(1) +): WLoginSigInfo { + return WLoginSigInfo( + uin = uin, + encryptA1 = null, + noPicSig = null, + simpleInfo = WLoginSimpleInfo( + uin = uin, + imgType = EMPTY_BYTE_ARRAY, + imgFormat = EMPTY_BYTE_ARRAY, + imgUrl = EMPTY_BYTE_ARRAY, + mainDisplayName = EMPTY_BYTE_ARRAY + ), // defaults {}, from asyncContext._G + appPri = 4294967295L, // defaults {}, from asyncContext._G + a2ExpiryTime = creationTime + 2160000L, // or from asyncContext._t403.get_body_data() + loginBitmap = 0, + tgt = getRandomByteArray(16, random), + a2CreationTime = creationTime, + tgtKey = getRandomByteArray(16, random), // from asyncContext._login_bitmap + userStSig = KeyWithCreationTime(getRandomByteArray(16, random), creationTime), + userStKey = EMPTY_BYTE_ARRAY, + userStWebSig = KeyWithExpiry( + EMPTY_BYTE_ARRAY, + creationTime, + creationTime + 6000 + ), + userA5 = KeyWithCreationTime(getRandomByteArray(16, random), creationTime), + userA8 = KeyWithExpiry( + EMPTY_BYTE_ARRAY, + creationTime, + creationTime + 72000L + ), + lsKey = KeyWithExpiry( + EMPTY_BYTE_ARRAY, + creationTime, + creationTime + 1641600L + ), + sKey = KeyWithExpiry( + EMPTY_BYTE_ARRAY, + creationTime, + creationTime + 86400L + ), + userSig64 = KeyWithCreationTime(EMPTY_BYTE_ARRAY, creationTime), + openId = EMPTY_BYTE_ARRAY, + openKey = KeyWithCreationTime(EMPTY_BYTE_ARRAY, creationTime), + vKey = KeyWithExpiry( + EMPTY_BYTE_ARRAY, + creationTime, + creationTime + 1728000L + ), + accessToken = KeyWithCreationTime(EMPTY_BYTE_ARRAY, creationTime), + d2 = KeyWithExpiry( + getRandomByteArray(16, random), + creationTime, + creationTime + 1728000L + ), + d2Key = getRandomByteArray(16, random), + sid = KeyWithExpiry( + EMPTY_BYTE_ARRAY, + creationTime, + creationTime + 1728000L + ), + aqSig = KeyWithCreationTime(EMPTY_BYTE_ARRAY, creationTime), + psKeyMap = mutableMapOf(), + pt4TokenMap = mutableMapOf(), + superKey = EMPTY_BYTE_ARRAY, + payToken = EMPTY_BYTE_ARRAY, + pf = EMPTY_BYTE_ARRAY, + pfKey = EMPTY_BYTE_ARRAY, + da2 = EMPTY_BYTE_ARRAY, + wtSessionTicket = KeyWithCreationTime(EMPTY_BYTE_ARRAY, creationTime), + wtSessionTicketKey = EMPTY_BYTE_ARRAY, + deviceToken = EMPTY_BYTE_ARRAY, + encryptedDownloadSession = null + ) +} diff --git a/mirai-core/src/commonTest/kotlin/network/framework/AbstractRealTimeActionTestUnit.kt b/mirai-core/src/commonTest/kotlin/network/framework/AbstractRealTimeActionTestUnit.kt index 813b14eaa..5b6b14a25 100644 --- a/mirai-core/src/commonTest/kotlin/network/framework/AbstractRealTimeActionTestUnit.kt +++ b/mirai-core/src/commonTest/kotlin/network/framework/AbstractRealTimeActionTestUnit.kt @@ -1,5 +1,5 @@ /* - * Copyright 2019-2021 Mamoe Technologies and contributors. + * Copyright 2019-2022 Mamoe Technologies and contributors. * * 此源代码的使用受 GNU AFFERO GENERAL PUBLIC LICENSE version 3 许可证的约束, 可以在以下链接找到该许可证. * Use of this source code is governed by the GNU AGPLv3 license that can be found through the following link. @@ -9,6 +9,7 @@ package net.mamoe.mirai.internal.network.framework +import io.ktor.utils.io.core.* import net.mamoe.mirai.internal.network.KeyWithCreationTime import net.mamoe.mirai.internal.network.KeyWithExpiry import net.mamoe.mirai.internal.network.WLoginSigInfo @@ -16,10 +17,10 @@ import net.mamoe.mirai.internal.network.WLoginSimpleInfo import net.mamoe.mirai.internal.notice.processors.GroupExtensions import net.mamoe.mirai.utils.EMPTY_BYTE_ARRAY import net.mamoe.mirai.utils.hexToBytes -import org.junit.jupiter.api.BeforeEach +import kotlin.test.BeforeTest -internal abstract class AbstractRealTimeActionTestUnit : AbstractNettyNHTest(), GroupExtensions { - @BeforeEach +internal abstract class AbstractRealTimeActionTestUnit : AbstractCommonNHTest(), GroupExtensions { + @BeforeTest internal fun prepareEnv() { bot.client.wLoginSigInfoField = WLoginSigInfo( uin = bot.id, @@ -66,7 +67,7 @@ internal abstract class AbstractRealTimeActionTestUnit : AbstractNettyNHTest(), deviceToken = "Winserver datacenter 2077".toByteArray(), ) bot.client._bot = bot - network.setStateOK(channel) + network.setStateOK(conn) removeOutgoingPacketEncoder() } } diff --git a/mirai-core/src/commonTest/kotlin/network/framework/ITestNetworkHandler.kt b/mirai-core/src/commonTest/kotlin/network/framework/ITestNetworkHandler.kt index a782e50bf..b626e907c 100644 --- a/mirai-core/src/commonTest/kotlin/network/framework/ITestNetworkHandler.kt +++ b/mirai-core/src/commonTest/kotlin/network/framework/ITestNetworkHandler.kt @@ -1,29 +1,29 @@ /* - * Copyright 2019-2021 Mamoe Technologies and contributors. + * Copyright 2019-2022 Mamoe Technologies and contributors. * - * 此源代码的使用受 GNU AFFERO GENERAL PUBLIC LICENSE version 3 许可证的约束, 可以在以下链接找到该许可证. - * Use of this source code is governed by the GNU AGPLv3 license that can be found through the following link. + * 此源代码的使用受 GNU AFFERO GENERAL PUBLIC LICENSE version 3 许可证的约束, 可以在以下链接找到该许可证. + * Use of this source code is governed by the GNU AGPLv3 license that can be found through the following link. * - * https://github.com/mamoe/mirai/blob/master/LICENSE + * https://github.com/mamoe/mirai/blob/dev/LICENSE */ package net.mamoe.mirai.internal.network.framework -import io.netty.channel.Channel import net.mamoe.mirai.internal.QQAndroidBot import net.mamoe.mirai.internal.network.components.EventDispatcher import net.mamoe.mirai.internal.network.components.SsoProcessor import net.mamoe.mirai.internal.network.handler.NetworkHandler import net.mamoe.mirai.internal.network.handler.NetworkHandlerSupport -internal interface ITestNetworkHandler : NetworkHandler { +internal interface ITestNetworkHandler : NetworkHandler { val bot: QQAndroidBot + fun setStateClosed(exception: Throwable? = null): NetworkHandlerSupport.BaseStateImpl? fun setStateConnecting(exception: Throwable? = null): NetworkHandlerSupport.BaseStateImpl? - fun setStateOK(channel: Channel, exception: Throwable? = null): NetworkHandlerSupport.BaseStateImpl? - fun setStateLoading(channel: Channel): NetworkHandlerSupport.BaseStateImpl? + fun setStateOK(conn: Conn, exception: Throwable? = null): NetworkHandlerSupport.BaseStateImpl? + fun setStateLoading(conn: Conn): NetworkHandlerSupport.BaseStateImpl? } -internal val ITestNetworkHandler.eventDispatcher get() = bot.components[EventDispatcher] -internal val ITestNetworkHandler.ssoProcessor get() = bot.components[SsoProcessor] +internal val ITestNetworkHandler<*>.eventDispatcher get() = bot.components[EventDispatcher] +internal val ITestNetworkHandler<*>.ssoProcessor get() = bot.components[SsoProcessor] diff --git a/mirai-core/src/commonTest/kotlin/network/framework/SynchronizedStdoutLogger.kt b/mirai-core/src/commonTest/kotlin/network/framework/SynchronizedStdoutLogger.kt index db59bc78a..3b6110778 100644 --- a/mirai-core/src/commonTest/kotlin/network/framework/SynchronizedStdoutLogger.kt +++ b/mirai-core/src/commonTest/kotlin/network/framework/SynchronizedStdoutLogger.kt @@ -1,18 +1,18 @@ /* - * Copyright 2019-2021 Mamoe Technologies and contributors. + * Copyright 2019-2022 Mamoe Technologies and contributors. * - * 此源代码的使用受 GNU AFFERO GENERAL PUBLIC LICENSE version 3 许可证的约束, 可以在以下链接找到该许可证. - * Use of this source code is governed by the GNU AGPLv3 license that can be found through the following link. + * 此源代码的使用受 GNU AFFERO GENERAL PUBLIC LICENSE version 3 许可证的约束, 可以在以下链接找到该许可证. + * Use of this source code is governed by the GNU AGPLv3 license that can be found through the following link. * - * https://github.com/mamoe/mirai/blob/master/LICENSE + * https://github.com/mamoe/mirai/blob/dev/LICENSE */ package net.mamoe.mirai.internal.network.framework +import kotlinx.atomicfu.locks.reentrantLock import kotlinx.atomicfu.locks.withLock -import java.util.concurrent.locks.ReentrantLock -private val lock = ReentrantLock() +private val lock = reentrantLock() @Suppress("INVISIBLE_MEMBER", "INVISIBLE_REFERENCE", "CANNOT_OVERRIDE_INVISIBLE_MEMBER") internal class SynchronizedStdoutLogger(override val identity: String?) : net.mamoe.mirai.internal.utils.StdoutLogger( diff --git a/mirai-core/src/commonTest/kotlin/network/framework/TestNetworkHandler.kt b/mirai-core/src/commonTest/kotlin/network/framework/TestNetworkHandler.kt index 35b67c80a..b2517c6c5 100644 --- a/mirai-core/src/commonTest/kotlin/network/framework/TestNetworkHandler.kt +++ b/mirai-core/src/commonTest/kotlin/network/framework/TestNetworkHandler.kt @@ -1,15 +1,15 @@ /* - * Copyright 2019-2021 Mamoe Technologies and contributors. + * Copyright 2019-2022 Mamoe Technologies and contributors. * - * 此源代码的使用受 GNU AFFERO GENERAL PUBLIC LICENSE version 3 许可证的约束, 可以在以下链接找到该许可证. - * Use of this source code is governed by the GNU AGPLv3 license that can be found through the following link. + * 此源代码的使用受 GNU AFFERO GENERAL PUBLIC LICENSE version 3 许可证的约束, 可以在以下链接找到该许可证. + * Use of this source code is governed by the GNU AGPLv3 license that can be found through the following link. * - * https://github.com/mamoe/mirai/blob/master/LICENSE + * https://github.com/mamoe/mirai/blob/dev/LICENSE */ package net.mamoe.mirai.internal.network.framework -import io.netty.channel.Channel +import kotlinx.atomicfu.atomic import kotlinx.coroutines.CompletableDeferred import kotlinx.coroutines.sync.Mutex import kotlinx.coroutines.sync.withLock @@ -19,9 +19,8 @@ import net.mamoe.mirai.internal.network.handler.NetworkHandlerContext import net.mamoe.mirai.internal.network.handler.NetworkHandlerSupport import net.mamoe.mirai.internal.network.handler.logger import net.mamoe.mirai.internal.network.protocol.packet.OutgoingPacket +import net.mamoe.mirai.utils.ConcurrentLinkedQueue import net.mamoe.mirai.utils.TestOnly -import java.util.concurrent.ConcurrentLinkedQueue -import java.util.concurrent.atomic.AtomicInteger /** * States are manually set. @@ -29,13 +28,15 @@ import java.util.concurrent.atomic.AtomicInteger internal open class TestNetworkHandler( override val bot: QQAndroidBot, context: NetworkHandlerContext, -) : NetworkHandlerSupport(context), ITestNetworkHandler { +) : NetworkHandlerSupport(context), ITestNetworkHandler { + class Connection + @Suppress("EXPOSED_SUPER_CLASS") internal open inner class TestState( correspondingState: NetworkHandler.State ) : BaseStateImpl(correspondingState) { val resumeDeferred = CompletableDeferred() - val resumeCount = AtomicInteger(0) + val resumeCount = atomic(0) val onResume get() = resumeDeferred.onJoin private val mutex = Mutex() @@ -81,12 +82,12 @@ internal open class TestNetworkHandler( return setState(NetworkHandler.State.CONNECTING) } - override fun setStateOK(channel: Channel, exception: Throwable?): TestState? { + override fun setStateOK(conn: Connection, exception: Throwable?): TestState? { exception?.printStackTrace() return setState(NetworkHandler.State.OK) } - override fun setStateLoading(channel: Channel): TestState? { + override fun setStateLoading(conn: Connection): TestState? { return setState(NetworkHandler.State.LOADING) } } \ No newline at end of file diff --git a/mirai-core/src/commonTest/kotlin/network/framework/components/TestSsoProcessor.kt b/mirai-core/src/commonTest/kotlin/network/framework/components/TestSsoProcessor.kt index baac69da6..c473b148c 100644 --- a/mirai-core/src/commonTest/kotlin/network/framework/components/TestSsoProcessor.kt +++ b/mirai-core/src/commonTest/kotlin/network/framework/components/TestSsoProcessor.kt @@ -18,13 +18,22 @@ import net.mamoe.mirai.internal.network.handler.NetworkHandler import net.mamoe.mirai.internal.network.handler.logger import net.mamoe.mirai.internal.network.protocol.data.jce.SvcRespRegister import net.mamoe.mirai.internal.network.protocol.packet.login.StatSvc +import net.mamoe.mirai.utils.DeviceInfo import net.mamoe.mirai.utils.debug import net.mamoe.mirai.utils.lateinitMutableProperty +import kotlin.random.Random internal open class TestSsoProcessor(private val bot: QQAndroidBot) : SsoProcessor { val deviceInfo = bot.configuration.createDeviceInfo(bot) override var client: QQAndroidClient by lateinitMutableProperty { - QQAndroidClient(bot.account, device = deviceInfo, accountSecrets = AccountSecretsImpl(deviceInfo, bot.account)) + QQAndroidClient( + bot.account, + device = deviceInfo, + accountSecrets = bot.components[AccountSecretsManager].getSecretsOrCreate( + bot.account, + DeviceInfo.random(Random(1)) + ) + ) } override val ssoSession: SsoSession get() = bot.client override val firstLoginResult: AtomicRef = atomic(null) diff --git a/mirai-core/src/commonTest/kotlin/network/framework/sessionUtils.kt b/mirai-core/src/commonTest/kotlin/network/framework/sessionUtils.kt index 91c0d43ad..0709b9eeb 100644 --- a/mirai-core/src/commonTest/kotlin/network/framework/sessionUtils.kt +++ b/mirai-core/src/commonTest/kotlin/network/framework/sessionUtils.kt @@ -1,10 +1,10 @@ /* - * Copyright 2019-2021 Mamoe Technologies and contributors. + * Copyright 2019-2022 Mamoe Technologies and contributors. * - * 此源代码的使用受 GNU AFFERO GENERAL PUBLIC LICENSE version 3 许可证的约束, 可以在以下链接找到该许可证. - * Use of this source code is governed by the GNU AGPLv3 license that can be found through the following link. + * 此源代码的使用受 GNU AFFERO GENERAL PUBLIC LICENSE version 3 许可证的约束, 可以在以下链接找到该许可证. + * Use of this source code is governed by the GNU AGPLv3 license that can be found through the following link. * - * https://github.com/mamoe/mirai/blob/master/LICENSE + * https://github.com/mamoe/mirai/blob/dev/LICENSE */ package net.mamoe.mirai.internal.network.framework @@ -16,12 +16,12 @@ import net.mamoe.mirai.internal.network.WLoginSigInfo import net.mamoe.mirai.internal.network.components.AccountSecrets import net.mamoe.mirai.internal.network.components.AccountSecretsImpl import net.mamoe.mirai.internal.network.components.SsoSession -import net.mamoe.mirai.internal.utils.io.serialization.loadAs import net.mamoe.mirai.internal.utils.io.serialization.toByteArray import net.mamoe.mirai.utils.EMPTY_BYTE_ARRAY +import net.mamoe.mirai.utils.MiraiFile import net.mamoe.mirai.utils.debug -import net.mamoe.mirai.utils.withUse -import java.io.File +import net.mamoe.mirai.utils.writeBytes + internal class TestSsoSession( private val accountSecrets: AccountSecrets, @@ -32,13 +32,13 @@ internal class TestSsoSession( override val randomKey: ByteArray by accountSecrets::randomKey } -internal fun loadSession( - resourceName: String, -): AccountSecretsImpl { - val bytes = ClassLoader.getSystemResourceAsStream(resourceName)?.withUse { readBytes() } - ?: error("AccountSecrets resource '$resourceName' not found.") - return bytes.loadAs(AccountSecretsImpl.serializer()) -} +//internal fun loadSession( +// resourceName: String, +//): AccountSecretsImpl { +// val bytes = ClassLoader.getSystemResourceAsStream(resourceName)?.withUse { readBytes() } +// ?: error("AccountSecrets resource '$resourceName' not found.") +// return bytes.loadAs(AccountSecretsImpl.serializer()) +//} /** * Secure to share with others. Designed to save real data for tests. @@ -54,7 +54,7 @@ internal fun QQAndroidClient.dumpSessionSafe(): ByteArray { return secrets.toByteArray(AccountSecretsImpl.serializer()) } -internal fun QQAndroidBot.scheduleSafeSessionDump(outputFile: File) { +internal fun QQAndroidBot.scheduleSafeSessionDump(outputFile: MiraiFile) { this.eventChannel.subscribeAlways { outputFile.writeBytes(client.dumpSessionSafe()) bot.logger.debug { "Dumped safe session to " } diff --git a/mirai-core/src/commonTest/kotlin/network/handler/KeepAliveNetworkHandlerSelectorRealTest.kt b/mirai-core/src/commonTest/kotlin/network/handler/KeepAliveNetworkHandlerSelectorRealTest.kt index 05425ebf7..119bc2d48 100644 --- a/mirai-core/src/commonTest/kotlin/network/handler/KeepAliveNetworkHandlerSelectorRealTest.kt +++ b/mirai-core/src/commonTest/kotlin/network/handler/KeepAliveNetworkHandlerSelectorRealTest.kt @@ -11,33 +11,30 @@ package net.mamoe.mirai.internal.network.handler -import io.netty.channel.Channel import net.mamoe.mirai.internal.network.components.FirstLoginResult import net.mamoe.mirai.internal.network.components.SsoProcessor -import net.mamoe.mirai.internal.network.framework.AbstractNettyNHTest -import net.mamoe.mirai.internal.network.framework.TestNettyNH +import net.mamoe.mirai.internal.network.framework.AbstractCommonNHTest +import net.mamoe.mirai.internal.network.framework.PlatformConn +import net.mamoe.mirai.internal.network.framework.TestCommonNetworkHandler import net.mamoe.mirai.internal.network.handler.selector.MaxAttemptsReachedException import net.mamoe.mirai.internal.network.handler.selector.NetworkException import net.mamoe.mirai.internal.test.runBlockingUnit import net.mamoe.mirai.utils.TestOnly -import org.junit.jupiter.api.BeforeEach -import org.junit.jupiter.api.assertThrows -import kotlin.test.Test -import kotlin.test.assertEquals -import kotlin.test.assertIs +import kotlin.test.* -internal class KeepAliveNetworkHandlerSelectorRealTest : AbstractNettyNHTest() { +internal class KeepAliveNetworkHandlerSelectorRealTest : AbstractCommonNHTest() { - internal class FakeFailOnCreatingConnection : AbstractNettyNHTest() { + internal class FakeFailOnCreatingConnection : AbstractCommonNHTest() { private class MyException : Exception() private lateinit var throwException: () -> Nothing - override val factory: NetworkHandlerFactory = - NetworkHandlerFactory { context, address -> - object : TestNettyNH(bot, context, address) { - override suspend fun createConnection(decodePipeline: PacketDecodePipeline): Channel = + override val factory: NetworkHandlerFactory = + NetworkHandlerFactory { context, address -> + object : TestCommonNetworkHandler(bot, context, address) { + override suspend fun createConnection(): PlatformConn { throwException() + } } } @@ -48,12 +45,12 @@ internal class KeepAliveNetworkHandlerSelectorRealTest : AbstractNettyNHTest() { throw MyException() } - val selector = TestSelector(3) { createHandler() } - assertThrows { selector.awaitResumeInstance() } + val selector = TestSelector(3) { factory.create(createContext(), createAddress()) } + assertFailsWith { selector.awaitResumeInstance() } } // Since #1963, any error during first login will close the bot. So we assume first login succeed to do our test. - @BeforeEach + @BeforeTest private fun setFirstLoginPassed() { assertEquals(null, bot.components[SsoProcessor].firstLoginResult.value) bot.components[SsoProcessor].firstLoginResult.value = FirstLoginResult.PASSED @@ -66,8 +63,8 @@ internal class KeepAliveNetworkHandlerSelectorRealTest : AbstractNettyNHTest() { throw object : NetworkException(true) {} } - val selector = TestSelector(3) { createHandler() } - assertThrows { selector.awaitResumeInstance() }.let { + val selector = TestSelector(3) { factory.create(createContext(), createAddress()) } + assertFailsWith { selector.awaitResumeInstance() }.let { assertIs(it.cause) } } @@ -77,8 +74,8 @@ internal class KeepAliveNetworkHandlerSelectorRealTest : AbstractNettyNHTest() { throwException = { throw MyException() } - val selector = TestSelector(3) { createHandler() } - assertThrows { selector.awaitResumeInstance() }.let { + val selector = TestSelector(3) { factory.create(createContext(), createAddress()) } + assertFailsWith { selector.awaitResumeInstance() }.let { assertIs(it.cause) } } diff --git a/mirai-core/src/commonTest/kotlin/network/handler/KeepAliveNetworkHandlerSelectorTest.kt b/mirai-core/src/commonTest/kotlin/network/handler/KeepAliveNetworkHandlerSelectorTest.kt index 61d0462c8..63221b51a 100644 --- a/mirai-core/src/commonTest/kotlin/network/handler/KeepAliveNetworkHandlerSelectorTest.kt +++ b/mirai-core/src/commonTest/kotlin/network/handler/KeepAliveNetworkHandlerSelectorTest.kt @@ -1,23 +1,23 @@ /* - * Copyright 2019-2021 Mamoe Technologies and contributors. + * Copyright 2019-2022 Mamoe Technologies and contributors. * - * 此源代码的使用受 GNU AFFERO GENERAL PUBLIC LICENSE version 3 许可证的约束, 可以在以下链接找到该许可证. - * Use of this source code is governed by the GNU AGPLv3 license that can be found through the following link. + * 此源代码的使用受 GNU AFFERO GENERAL PUBLIC LICENSE version 3 许可证的约束, 可以在以下链接找到该许可证. + * Use of this source code is governed by the GNU AGPLv3 license that can be found through the following link. * - * https://github.com/mamoe/mirai/blob/master/LICENSE + * https://github.com/mamoe/mirai/blob/dev/LICENSE */ @file:OptIn(TestOnly::class) package net.mamoe.mirai.internal.network.handler +import kotlinx.atomicfu.atomic import net.mamoe.mirai.internal.network.framework.AbstractMockNetworkHandlerTest import net.mamoe.mirai.internal.network.handler.NetworkHandler.State import net.mamoe.mirai.internal.network.handler.selector.AbstractKeepAliveNetworkHandlerSelector import net.mamoe.mirai.internal.test.runBlockingUnit import net.mamoe.mirai.utils.MiraiLogger import net.mamoe.mirai.utils.TestOnly -import java.util.concurrent.atomic.AtomicInteger import kotlin.test.* import kotlin.time.Duration.Companion.seconds @@ -36,7 +36,7 @@ internal class TestSelector : this.createInstance0 = createInstance0 } - val createInstanceCount: AtomicInteger = AtomicInteger(0) + val createInstanceCount = atomic(0) override fun createInstance(): H { createInstanceCount.incrementAndGet() @@ -76,7 +76,7 @@ internal class KeepAliveNetworkHandlerSelectorTest : AbstractMockNetworkHandlerT assertSame(handler, selector.getCurrentInstanceOrNull()) handler.setState(State.CLOSED) runBlockingUnit(timeout = 3.seconds) { selector.awaitResumeInstance() } - assertEquals(1, selector.createInstanceCount.get()) + assertEquals(1, selector.createInstanceCount.value) } @Test @@ -87,6 +87,6 @@ internal class KeepAliveNetworkHandlerSelectorTest : AbstractMockNetworkHandlerT assertFailsWith { selector.awaitResumeInstance() } - assertEquals(3, selector.createInstanceCount.get()) + assertEquals(3, selector.createInstanceCount.value) } } \ No newline at end of file diff --git a/mirai-core/src/commonTest/kotlin/network/handler/SelectorRecoveryTest.kt b/mirai-core/src/commonTest/kotlin/network/handler/SelectorRecoveryTest.kt index 4358b9c1c..0deb61695 100644 --- a/mirai-core/src/commonTest/kotlin/network/handler/SelectorRecoveryTest.kt +++ b/mirai-core/src/commonTest/kotlin/network/handler/SelectorRecoveryTest.kt @@ -7,6 +7,8 @@ * https://github.com/mamoe/mirai/blob/dev/LICENSE */ +@file:OptIn(TestOnly::class) + package net.mamoe.mirai.internal.network.handler import kotlinx.coroutines.CoroutineScope @@ -14,29 +16,26 @@ import kotlinx.coroutines.Job import net.mamoe.mirai.internal.network.components.EventDispatcher import net.mamoe.mirai.internal.network.components.HeartbeatFailureHandler import net.mamoe.mirai.internal.network.components.HeartbeatScheduler -import net.mamoe.mirai.internal.network.framework.AbstractNettyNHTestWithSelector -import net.mamoe.mirai.internal.network.impl.netty.HeartbeatFailedException -import net.mamoe.mirai.internal.network.impl.netty.NettyChannelException +import net.mamoe.mirai.internal.network.framework.AbstractCommonNHTestWithSelector +import net.mamoe.mirai.internal.network.handler.selector.NetworkException import net.mamoe.mirai.internal.test.runBlockingUnit -import org.junit.jupiter.api.AfterEach -import org.junit.jupiter.api.BeforeEach -import org.junit.jupiter.api.Test -import org.junit.jupiter.api.TestInfo +import net.mamoe.mirai.utils.TestOnly +import kotlin.test.Test import kotlin.test.assertFails /** * Test whether the selector can recover the connection after first successful login. */ -internal class SelectorRecoveryTest : AbstractNettyNHTestWithSelector() { - @BeforeEach - fun beforeTest(info: TestInfo) { - println("=".repeat(30) + "BEGIN: ${info.displayName}" + "=".repeat(30)) - } - - @AfterEach - fun afterTest(info: TestInfo) { - println("=".repeat(31) + "END: ${info.displayName}" + "=".repeat(31)) - } +internal class SelectorRecoveryTest : AbstractCommonNHTestWithSelector() { +// @BeforeTest +// fun beforeTest(info: TestInfo) { +// println("=".repeat(30) + "BEGIN: ${info.displayName}" + "=".repeat(30)) +// } +// +// @AfterTest +// fun afterTest(info: TestInfo) { +// println("=".repeat(31) + "END: ${info.displayName}" + "=".repeat(31)) +// } @Test fun `stop on manual close`() = runBlockingUnit { @@ -47,12 +46,11 @@ internal class SelectorRecoveryTest : AbstractNettyNHTestWithSelector() { /** * Emulates system hibernation and network failure. - * @see HeartbeatFailedException */ @Test fun `can recover on heartbeat failure with NettyChannelException`() = runBlockingUnit { // We allow NetworkException to cause a reconnect. - testRecoverWhenHeartbeatFailWith { NettyChannelException("test IO ex") } + testRecoverWhenHeartbeatFailWith { NetworkException("test IO ex", true) } bot.components[EventDispatcher].joinBroadcast() // Wait our async connector to complete. diff --git a/mirai-core/src/commonTest/kotlin/network/handler/StateObserverTest.kt b/mirai-core/src/commonTest/kotlin/network/handler/StateObserverTest.kt index 80570f227..14ef7b772 100644 --- a/mirai-core/src/commonTest/kotlin/network/handler/StateObserverTest.kt +++ b/mirai-core/src/commonTest/kotlin/network/handler/StateObserverTest.kt @@ -1,10 +1,10 @@ /* - * Copyright 2019-2021 Mamoe Technologies and contributors. + * Copyright 2019-2022 Mamoe Technologies and contributors. * - * 此源代码的使用受 GNU AFFERO GENERAL PUBLIC LICENSE version 3 许可证的约束, 可以在以下链接找到该许可证. - * Use of this source code is governed by the GNU AGPLv3 license that can be found through the following link. + * 此源代码的使用受 GNU AFFERO GENERAL PUBLIC LICENSE version 3 许可证的约束, 可以在以下链接找到该许可证. + * Use of this source code is governed by the GNU AGPLv3 license that can be found through the following link. * - * https://github.com/mamoe/mirai/blob/master/LICENSE + * https://github.com/mamoe/mirai/blob/dev/LICENSE */ package net.mamoe.mirai.internal.network.handler @@ -16,12 +16,10 @@ import net.mamoe.mirai.internal.network.handler.state.CombinedStateObserver import net.mamoe.mirai.internal.network.handler.state.CombinedStateObserver.Companion.plus import net.mamoe.mirai.internal.network.handler.state.StateChangedObserver import net.mamoe.mirai.internal.network.handler.state.StateObserver -import org.junit.jupiter.api.Test -import org.junit.jupiter.api.TestInstance +import kotlin.test.Test import kotlin.test.assertEquals import kotlin.test.assertIs -@TestInstance(TestInstance.Lifecycle.PER_METHOD) internal class StateObserverTest : AbstractMockNetworkHandlerTest() { @Test fun `can trigger observer`() { diff --git a/mirai-core/src/commonTest/kotlin/network/impl/netty/AccountSecretsTest.kt b/mirai-core/src/commonTest/kotlin/network/impl/common/AccountSecretsTest.kt similarity index 85% rename from mirai-core/src/commonTest/kotlin/network/impl/netty/AccountSecretsTest.kt rename to mirai-core/src/commonTest/kotlin/network/impl/common/AccountSecretsTest.kt index 5fd0022a8..776f84fe6 100644 --- a/mirai-core/src/commonTest/kotlin/network/impl/netty/AccountSecretsTest.kt +++ b/mirai-core/src/commonTest/kotlin/network/impl/common/AccountSecretsTest.kt @@ -1,5 +1,5 @@ /* - * Copyright 2019-2021 Mamoe Technologies and contributors. + * Copyright 2019-2022 Mamoe Technologies and contributors. * * 此源代码的使用受 GNU AFFERO GENERAL PUBLIC LICENSE version 3 许可证的约束, 可以在以下链接找到该许可证. * Use of this source code is governed by the GNU AGPLv3 license that can be found through the following link. @@ -7,21 +7,22 @@ * https://github.com/mamoe/mirai/blob/dev/LICENSE */ -package net.mamoe.mirai.internal.network.impl.netty +package net.mamoe.mirai.internal.network.impl.common import net.mamoe.mirai.internal.network.components.AccountSecretsImpl import net.mamoe.mirai.internal.network.components.AccountSecretsManager import net.mamoe.mirai.internal.network.components.FileCacheAccountSecretsManager -import net.mamoe.mirai.internal.network.framework.AbstractNettyNHTest +import net.mamoe.mirai.internal.network.framework.AbstractCommonNHTest import net.mamoe.mirai.internal.network.handler.NetworkHandler import net.mamoe.mirai.internal.test.runBlockingUnit import net.mamoe.mirai.internal.utils.accountSecretsFile import net.mamoe.mirai.utils.DeviceInfo import net.mamoe.mirai.utils.getRandomByteArray -import org.junit.jupiter.api.Test +import net.mamoe.mirai.utils.writeBytes +import kotlin.test.Test import kotlin.test.assertEquals -internal class AccountSecretsTest : AbstractNettyNHTest() { +internal class AccountSecretsTest : AbstractCommonNHTest() { @Test fun `can login with no secrets`() = runBlockingUnit { val file = bot.configuration.accountSecretsFile() diff --git a/mirai-core/src/commonTest/kotlin/network/impl/netty/NettyBotLifecycleTest.kt b/mirai-core/src/commonTest/kotlin/network/impl/common/BotLifecycleTest.kt similarity index 81% rename from mirai-core/src/commonTest/kotlin/network/impl/netty/NettyBotLifecycleTest.kt rename to mirai-core/src/commonTest/kotlin/network/impl/common/BotLifecycleTest.kt index 341dc006e..d9ac1ceb9 100644 --- a/mirai-core/src/commonTest/kotlin/network/impl/netty/NettyBotLifecycleTest.kt +++ b/mirai-core/src/commonTest/kotlin/network/impl/common/BotLifecycleTest.kt @@ -1,34 +1,34 @@ /* - * Copyright 2019-2021 Mamoe Technologies and contributors. + * Copyright 2019-2022 Mamoe Technologies and contributors. * - * 此源代码的使用受 GNU AFFERO GENERAL PUBLIC LICENSE version 3 许可证的约束, 可以在以下链接找到该许可证. - * Use of this source code is governed by the GNU AGPLv3 license that can be found through the following link. + * 此源代码的使用受 GNU AFFERO GENERAL PUBLIC LICENSE version 3 许可证的约束, 可以在以下链接找到该许可证. + * Use of this source code is governed by the GNU AGPLv3 license that can be found through the following link. * - * https://github.com/mamoe/mirai/blob/master/LICENSE + * https://github.com/mamoe/mirai/blob/dev/LICENSE */ -package net.mamoe.mirai.internal.network.impl.netty +@file:OptIn(TestOnly::class) + +package net.mamoe.mirai.internal.network.impl.common import kotlinx.coroutines.CoroutineName import kotlinx.coroutines.SupervisorJob import kotlinx.coroutines.isActive +import kotlinx.coroutines.job import net.mamoe.mirai.internal.MockBot import net.mamoe.mirai.internal.network.components.EventDispatcher import net.mamoe.mirai.internal.network.components.SsoProcessor -import net.mamoe.mirai.internal.network.framework.AbstractNettyNHTest +import net.mamoe.mirai.internal.network.framework.AbstractCommonNHTest import net.mamoe.mirai.internal.network.framework.components.TestSsoProcessor import net.mamoe.mirai.internal.network.handler.NetworkHandler.State.* import net.mamoe.mirai.internal.network.protocol.packet.IncomingPacket import net.mamoe.mirai.internal.network.protocol.packet.login.StatSvc import net.mamoe.mirai.internal.test.runBlockingUnit import net.mamoe.mirai.supervisorJob -import org.junit.jupiter.api.Test -import kotlin.test.assertEquals -import kotlin.test.assertFalse -import kotlin.test.assertIs -import kotlin.test.assertTrue +import net.mamoe.mirai.utils.TestOnly +import kotlin.test.* -internal class NettyBotLifecycleTest : AbstractNettyNHTest() { +internal class BotLifecycleTest : AbstractCommonNHTest() { // not allowed anymore @@ -85,7 +85,7 @@ internal class NettyBotLifecycleTest : AbstractNettyNHTest() { conf { parentCoroutineContext = CoroutineName("Overrode") } - networkHandlerProvider { createHandler() } + networkHandlerProvider { factory.create(createContext(), createAddress()) } } assertEquals("Overrode", bot.coroutineContext[CoroutineName]!!.name) } @@ -97,7 +97,7 @@ internal class NettyBotLifecycleTest : AbstractNettyNHTest() { conf { parentCoroutineContext = parentJob } - networkHandlerProvider { createHandler() } + networkHandlerProvider { factory.create(createContext(), createAddress()) } } assertEquals(1, parentJob.children.count()) assertEquals(bot.supervisorJob, parentJob.children.first()) @@ -122,8 +122,9 @@ internal class NettyBotLifecycleTest : AbstractNettyNHTest() { data = StatSvc.SimpleGet.Response.Error(1, "test error"), ) ) - assertFalse { network.isActive } + network.coroutineContext.job.join() network.assertState(CLOSED) // we do not use selector in this test so it will be CLOSED. It will recover (reconnect) instead in real. + assertFalse { network.isActive } } diff --git a/mirai-core/src/commonTest/kotlin/network/impl/netty/NettyAddressChangedTest.kt b/mirai-core/src/commonTest/kotlin/network/impl/common/CommonNHAddressChangedTest.kt similarity index 56% rename from mirai-core/src/commonTest/kotlin/network/impl/netty/NettyAddressChangedTest.kt rename to mirai-core/src/commonTest/kotlin/network/impl/common/CommonNHAddressChangedTest.kt index 333830ea9..34967e99a 100644 --- a/mirai-core/src/commonTest/kotlin/network/impl/netty/NettyAddressChangedTest.kt +++ b/mirai-core/src/commonTest/kotlin/network/impl/common/CommonNHAddressChangedTest.kt @@ -1,22 +1,32 @@ -package net.mamoe.mirai.internal.network.impl.netty +/* + * Copyright 2019-2022 Mamoe Technologies and contributors. + * + * 此源代码的使用受 GNU AFFERO GENERAL PUBLIC LICENSE version 3 许可证的约束, 可以在以下链接找到该许可证. + * Use of this source code is governed by the GNU AGPLv3 license that can be found through the following link. + * + * https://github.com/mamoe/mirai/blob/dev/LICENSE + */ + +package net.mamoe.mirai.internal.network.impl.common import net.mamoe.mirai.internal.network.components.ServerList -import net.mamoe.mirai.internal.network.framework.AbstractNettyNHTest -import net.mamoe.mirai.internal.network.framework.TestNettyNH +import net.mamoe.mirai.internal.network.framework.AbstractCommonNHTest +import net.mamoe.mirai.internal.network.framework.TestCommonNetworkHandler import net.mamoe.mirai.internal.network.handler.NetworkHandler import net.mamoe.mirai.internal.test.runBlockingUnit -import org.junit.jupiter.api.Test +import kotlin.test.Test import kotlin.test.assertEquals import kotlin.test.assertNotEquals +import kotlin.test.assertTrue -internal class NettyAddressChangedTest : AbstractNettyNHTest() { +internal class CommonNHAddressChangedTest : AbstractCommonNHTest() { @Test fun `test login ip changes`() = runBlockingUnit { networkLogger.debug("before login, Assuming both ip is empty") val lastConnectedIpOld = bot.components[ServerList].lastConnectedIP val lastDisconnectedIpOld = bot.components[ServerList].lastDisconnectedIP - assert(lastConnectedIpOld.isEmpty()) { "Assuming lastConnectedIp is empty" } - assert(lastDisconnectedIpOld.isEmpty()) { "Assuming lastDisconnectedIp is empty" } + assertTrue(lastConnectedIpOld.isEmpty(), "Assuming lastConnectedIp is empty") + assertTrue(lastDisconnectedIpOld.isEmpty(), "Assuming lastDisconnectedIp is empty") networkLogger.debug("Do login, Assuming lastConnectedIp is NOT empty") bot.login() @@ -28,7 +38,7 @@ internal class NettyAddressChangedTest : AbstractNettyNHTest() { ) networkLogger.debug("Offline the bot, Assuming lastConnectedIp is equals lastDisconnectedIp") - (bot.network as TestNettyNH).setStateClosed() + (bot.network as TestCommonNetworkHandler).setStateClosed() assertState(NetworkHandler.State.CLOSED) assertEquals( bot.components[ServerList].lastConnectedIP, diff --git a/mirai-core/src/commonTest/kotlin/network/impl/netty/NettyBotNormalLoginTest.kt b/mirai-core/src/commonTest/kotlin/network/impl/common/CommonNHBotNormalLoginTest.kt similarity index 68% rename from mirai-core/src/commonTest/kotlin/network/impl/netty/NettyBotNormalLoginTest.kt rename to mirai-core/src/commonTest/kotlin/network/impl/common/CommonNHBotNormalLoginTest.kt index b6aa65035..9dc53ae7b 100644 --- a/mirai-core/src/commonTest/kotlin/network/impl/netty/NettyBotNormalLoginTest.kt +++ b/mirai-core/src/commonTest/kotlin/network/impl/common/CommonNHBotNormalLoginTest.kt @@ -7,50 +7,62 @@ * https://github.com/mamoe/mirai/blob/dev/LICENSE */ -package net.mamoe.mirai.internal.network.impl.netty +@file:OptIn(TestOnly::class) +package net.mamoe.mirai.internal.network.impl.common + +import io.ktor.utils.io.errors.* import kotlinx.coroutines.delay import kotlinx.coroutines.isActive +import net.mamoe.mirai.internal.BotAccount +import net.mamoe.mirai.internal.MockConfiguration +import net.mamoe.mirai.internal.QQAndroidBot +import net.mamoe.mirai.internal.network.component.ConcurrentComponentStorage +import net.mamoe.mirai.internal.network.component.setAll import net.mamoe.mirai.internal.network.components.BotOfflineEventMonitor import net.mamoe.mirai.internal.network.components.BotOfflineEventMonitorImpl import net.mamoe.mirai.internal.network.components.FirstLoginResult -import net.mamoe.mirai.internal.network.framework.AbstractNettyNHTest -import net.mamoe.mirai.internal.network.framework.TestNettyNH +import net.mamoe.mirai.internal.network.framework.AbstractCommonNHTest +import net.mamoe.mirai.internal.network.framework.TestCommonNetworkHandler import net.mamoe.mirai.internal.network.framework.setSsoProcessor import net.mamoe.mirai.internal.network.handler.NetworkHandler import net.mamoe.mirai.internal.network.handler.selector.KeepAliveNetworkHandlerSelector +import net.mamoe.mirai.internal.network.handler.selector.NetworkChannelException import net.mamoe.mirai.internal.network.handler.selector.SelectorNetworkHandler import net.mamoe.mirai.internal.network.handler.selectorLogger import net.mamoe.mirai.internal.network.protocol.packet.login.StatSvc import net.mamoe.mirai.internal.test.runBlockingUnit import net.mamoe.mirai.network.CustomLoginFailedException +import net.mamoe.mirai.utils.TestOnly import net.mamoe.mirai.utils.cast -import org.junit.jupiter.api.AfterEach -import org.junit.jupiter.api.Test -import java.io.IOException -import kotlin.test.assertEquals -import kotlin.test.assertFailsWith -import kotlin.test.assertFalse +import kotlin.test.* -internal class NettyBotNormalLoginTest : AbstractNettyNHTest() { +internal class CommonNHBotNormalLoginTest : AbstractCommonNHTest() { init { overrideComponents[BotOfflineEventMonitor] = BotOfflineEventMonitorImpl() } val selector = KeepAliveNetworkHandlerSelector(selectorLogger) { - super.factory.create(createContext(), createAddress()) + factory.create(createContext(), createAddress()) } - override val network: TestNettyNH - get() = bot.network.cast>().selector.getCurrentInstanceOrCreate().cast() + override val network: TestCommonNetworkHandler + get() = selector.getCurrentInstanceOrCreate().cast() - override fun createHandler(): NetworkHandler { - return SelectorNetworkHandler(selector) + override fun createBot(account: BotAccount): QQAndroidBot { + return object : QQAndroidBot(account, MockConfiguration.copy()) { + override fun createBotLevelComponents(): ConcurrentComponentStorage = + super.createBotLevelComponents().apply { setAll(overrideComponents) } + + override fun createNetworkHandler(): NetworkHandler = + SelectorNetworkHandler(selector) + } } + class CusLoginException(message: String?) : CustomLoginFailedException(true, message) - @AfterEach + @AfterTest fun `close bot`() = runBlockingUnit { bot.logger.info("[TEST UNIT] Releasing bot....") bot.closeAndJoin() @@ -74,8 +86,8 @@ internal class NettyBotNormalLoginTest : AbstractNettyNHTest() { // #1963 @Test fun `test first login failure with internally handled exceptions2`() = runBlockingUnit { - setSsoProcessor { throw NettyChannelException("test Connection reset by peer") } - assertFailsWith("test Connection reset by peer") { bot.login() } + setSsoProcessor { throw NetworkChannelException("test Connection reset by peer") } + assertFailsWith("test Connection reset by peer") { bot.login() } assertState(NetworkHandler.State.CLOSED) } diff --git a/mirai-core/src/commonTest/kotlin/network/impl/netty/NettyHandlerEventTest.kt b/mirai-core/src/commonTest/kotlin/network/impl/common/CommonNHEventTest.kt similarity index 92% rename from mirai-core/src/commonTest/kotlin/network/impl/netty/NettyHandlerEventTest.kt rename to mirai-core/src/commonTest/kotlin/network/impl/common/CommonNHEventTest.kt index bd3498b56..21af63f4c 100644 --- a/mirai-core/src/commonTest/kotlin/network/impl/netty/NettyHandlerEventTest.kt +++ b/mirai-core/src/commonTest/kotlin/network/impl/common/CommonNHEventTest.kt @@ -7,8 +7,11 @@ * https://github.com/mamoe/mirai/blob/dev/LICENSE */ -package net.mamoe.mirai.internal.network.impl.netty +@file:OptIn(TestOnly::class) +package net.mamoe.mirai.internal.network.impl.common + +import kotlinx.atomicfu.atomic import kotlinx.coroutines.CompletableDeferred import kotlinx.coroutines.isActive import net.mamoe.mirai.event.Event @@ -17,7 +20,7 @@ import net.mamoe.mirai.event.events.BotOnlineEvent import net.mamoe.mirai.event.events.BotReloginEvent import net.mamoe.mirai.internal.network.components.FirstLoginResult import net.mamoe.mirai.internal.network.components.SsoProcessor -import net.mamoe.mirai.internal.network.framework.AbstractNettyNHTest +import net.mamoe.mirai.internal.network.framework.AbstractCommonNHTest import net.mamoe.mirai.internal.network.framework.eventDispatcher import net.mamoe.mirai.internal.network.framework.setSsoProcessor import net.mamoe.mirai.internal.network.handler.NetworkHandler.State.* @@ -25,15 +28,13 @@ import net.mamoe.mirai.internal.test.assertEventBroadcasts import net.mamoe.mirai.internal.test.assertEventNotBroadcast import net.mamoe.mirai.internal.test.runBlockingUnit import net.mamoe.mirai.supervisorJob -import org.junit.jupiter.api.TestInstance -import java.util.concurrent.atomic.AtomicReference +import net.mamoe.mirai.utils.TestOnly import kotlin.test.Test import kotlin.test.assertEquals import kotlin.test.assertTrue -@TestInstance(TestInstance.Lifecycle.PER_METHOD) -internal class NettyHandlerEventTest : AbstractNettyNHTest() { +internal class CommonNHEventTest : AbstractCommonNHTest() { @Test fun `BotOnlineEvent after successful logon`() = runBlockingUnit { assertEventBroadcasts { @@ -94,24 +95,24 @@ internal class NettyHandlerEventTest : AbstractNettyNHTest() { @Test fun `from CONNECTING TO OK the second time`() = runBlockingUnit { - val ok = AtomicReference(CompletableDeferred()) + val ok = atomic(CompletableDeferred()) setSsoProcessor { - ok.get().join() + ok.value.join() } assertState(INITIALIZED) network.setStateConnecting() - ok.get().complete(Unit) + ok.value.complete(Unit) network.resumeConnection() assertState(OK) - ok.set(CompletableDeferred()) + ok.value = CompletableDeferred() network.setStateConnecting() eventDispatcher.joinBroadcast() println("Starting receiving events") assertEventBroadcasts(2) { - ok.get().complete(Unit) + ok.value.complete(Unit) network.resumeConnection() eventDispatcher.joinBroadcast() }.let { event -> diff --git a/mirai-core/src/commonTest/kotlin/network/impl/netty/NettyResumeConnectionTest.kt b/mirai-core/src/commonTest/kotlin/network/impl/common/ResumeConnectionTest.kt similarity index 89% rename from mirai-core/src/commonTest/kotlin/network/impl/netty/NettyResumeConnectionTest.kt rename to mirai-core/src/commonTest/kotlin/network/impl/common/ResumeConnectionTest.kt index 318bf2878..fdeeeec5e 100644 --- a/mirai-core/src/commonTest/kotlin/network/impl/netty/NettyResumeConnectionTest.kt +++ b/mirai-core/src/commonTest/kotlin/network/impl/common/ResumeConnectionTest.kt @@ -10,13 +10,13 @@ package net.mamoe.mirai.internal.network.impl.netty import io.ktor.utils.io.core.* -import net.mamoe.mirai.internal.network.framework.AbstractNettyNHTest +import net.mamoe.mirai.internal.network.framework.AbstractCommonNHTest import net.mamoe.mirai.internal.network.protocol.packet.OutgoingPacket import net.mamoe.mirai.internal.test.runBlockingUnit import kotlin.test.Test import kotlin.test.assertFailsWith -internal class NettyResumeConnectionTest : AbstractNettyNHTest() { +internal class ResumeConnectionTest : AbstractCommonNHTest() { private val packet = OutgoingPacket("", "", 1, ByteReadPacket.Empty) @@ -43,7 +43,7 @@ internal class NettyResumeConnectionTest : AbstractNettyNHTest() { @Test fun `resumeConnection switches a state that can send packet on LOADING`() = runBlockingUnit { - network.setStateLoading(channel) + network.setStateLoading(conn) network.resumeConnection() network.sendWithoutExpect(packet) } diff --git a/mirai-core/src/commonTest/kotlin/network/impl/netty/NettySendPacketTest.kt b/mirai-core/src/commonTest/kotlin/network/impl/common/SendPacketTest.kt similarity index 63% rename from mirai-core/src/commonTest/kotlin/network/impl/netty/NettySendPacketTest.kt rename to mirai-core/src/commonTest/kotlin/network/impl/common/SendPacketTest.kt index f09329780..954d9d90c 100644 --- a/mirai-core/src/commonTest/kotlin/network/impl/netty/NettySendPacketTest.kt +++ b/mirai-core/src/commonTest/kotlin/network/impl/common/SendPacketTest.kt @@ -7,44 +7,25 @@ * https://github.com/mamoe/mirai/blob/dev/LICENSE */ -package net.mamoe.mirai.internal.network.impl.netty +package net.mamoe.mirai.internal.network.impl.common import io.ktor.utils.io.core.* -import io.netty.channel.Channel import kotlinx.atomicfu.atomic import kotlinx.coroutines.CoroutineStart -import kotlinx.coroutines.asCoroutineDispatcher import kotlinx.coroutines.launch import kotlinx.coroutines.yield import net.mamoe.mirai.internal.network.Packet -import net.mamoe.mirai.internal.network.framework.AbstractNettyNHTest -import net.mamoe.mirai.internal.network.framework.TestNettyNH -import net.mamoe.mirai.internal.network.handler.NetworkHandlerContext -import net.mamoe.mirai.internal.network.handler.NetworkHandlerFactory +import net.mamoe.mirai.internal.network.framework.AbstractCommonNHTest import net.mamoe.mirai.internal.network.protocol.packet.IncomingPacket import net.mamoe.mirai.internal.network.protocol.packet.OutgoingPacket import net.mamoe.mirai.internal.test.runBlockingUnit -import org.junit.jupiter.api.Test -import java.net.SocketAddress -import java.util.concurrent.Executors +import kotlin.test.Test import kotlin.test.assertNotNull import kotlin.test.assertTrue -internal class NettySendPacketTest : AbstractNettyNHTest() { - override val factory: NetworkHandlerFactory = object : NetworkHandlerFactory { - override fun create(context: NetworkHandlerContext, address: SocketAddress): TestNettyNH { - return object : TestNettyNH(bot, context, address) { - override suspend fun createConnection(decodePipeline: PacketDecodePipeline): Channel = - channel.apply { - doRegister() // restart channel - setupChannelPipeline(pipeline(), decodePipeline) - } - } - } - } - +internal class SendPacketTest : AbstractCommonNHTest() { // single thread so we can use [yield] to transfer dispatch - private val singleThreadDispatcher = Executors.newSingleThreadExecutor().asCoroutineDispatcher() + private val singleThreadDispatcher = borrowSingleThreadDispatcher() @Test fun `sendPacketImpl suspends until a valid state`() = runBlockingUnit(singleThreadDispatcher) { @@ -55,7 +36,7 @@ internal class NettySendPacketTest : AbstractNettyNHTest() { assertNotNull(network.sendAndExpect(OutgoingPacket("name", "cmd", 1, ByteReadPacket.Empty))) assertTrue { expectStop.value } } - network.setStateOK(channel) // then we can send packet. + network.setStateOK(conn) // then we can send packet. yield() // yields the thread to run `sendAndExpect` // when we got thread here again, `sendAndExpect` is suspending for response [Packet]. @@ -66,7 +47,7 @@ internal class NettySendPacketTest : AbstractNettyNHTest() { @Test fun `sendPacketImpl does not suspend if state is valid`() = runBlockingUnit(singleThreadDispatcher) { - network.setStateOK(channel) // then we can send packet. + network.setStateOK(conn) // then we can send packet. val expectStop = atomic(false) val job = launch(singleThreadDispatcher, start = CoroutineStart.UNDISPATCHED) { diff --git a/mirai-core/src/commonTest/kotlin/network/impl/netty/SetStateTest.kt b/mirai-core/src/commonTest/kotlin/network/impl/common/SetStateTest.kt similarity index 73% rename from mirai-core/src/commonTest/kotlin/network/impl/netty/SetStateTest.kt rename to mirai-core/src/commonTest/kotlin/network/impl/common/SetStateTest.kt index f59a4c3cb..7c5215535 100644 --- a/mirai-core/src/commonTest/kotlin/network/impl/netty/SetStateTest.kt +++ b/mirai-core/src/commonTest/kotlin/network/impl/common/SetStateTest.kt @@ -1,30 +1,29 @@ /* - * Copyright 2019-2021 Mamoe Technologies and contributors. + * Copyright 2019-2022 Mamoe Technologies and contributors. * - * 此源代码的使用受 GNU AFFERO GENERAL PUBLIC LICENSE version 3 许可证的约束, 可以在以下链接找到该许可证. - * Use of this source code is governed by the GNU AGPLv3 license that can be found through the following link. + * 此源代码的使用受 GNU AFFERO GENERAL PUBLIC LICENSE version 3 许可证的约束, 可以在以下链接找到该许可证. + * Use of this source code is governed by the GNU AGPLv3 license that can be found through the following link. * - * https://github.com/mamoe/mirai/blob/master/LICENSE + * https://github.com/mamoe/mirai/blob/dev/LICENSE */ -package net.mamoe.mirai.internal.network.impl.netty +@file:OptIn(TestOnly::class) + +package net.mamoe.mirai.internal.network.impl.common import kotlinx.coroutines.CoroutineScope import net.mamoe.mirai.event.Event import net.mamoe.mirai.event.events.BotOfflineEvent import net.mamoe.mirai.internal.AbstractBot import net.mamoe.mirai.internal.network.components.BotOfflineEventMonitor -import net.mamoe.mirai.internal.network.framework.AbstractNettyNHTest +import net.mamoe.mirai.internal.network.framework.AbstractCommonNHTest import net.mamoe.mirai.internal.network.handler.NetworkHandler.State.* import net.mamoe.mirai.internal.test.assertEventBroadcasts import net.mamoe.mirai.internal.test.runBlockingUnit -import org.junit.jupiter.api.Test -import kotlin.test.assertEquals -import kotlin.test.assertIs -import kotlin.test.assertNotNull -import kotlin.test.assertNull +import net.mamoe.mirai.utils.TestOnly +import kotlin.test.* -internal class SetStateTest : AbstractNettyNHTest() { +internal class SetStateTest : AbstractCommonNHTest() { @Test fun `setState should ignore duplications INITIALIZED to CLOSED to CLOSED`() { assertState(INITIALIZED) @@ -36,7 +35,7 @@ internal class SetStateTest : AbstractNettyNHTest() { @Test fun `setState should ignore duplications OK to CLOSED to CLOSED`() { - assertNotNull(network.setStateOK(channel)) + assertNotNull(network.setStateOK(conn)) assertState(OK) assertNotNull(network.setStateClosed(IllegalStateException("1"))) assertState(CLOSED) @@ -50,7 +49,7 @@ internal class SetStateTest : AbstractNettyNHTest() { override fun attachJob(bot: AbstractBot, scope: CoroutineScope) { } } - assertNotNull(network.setStateOK(channel)) + assertNotNull(network.setStateOK(conn)) assertState(OK) assertEventBroadcasts { assertNotNull(network.setStateClosed(IllegalStateException("1"))) @@ -66,7 +65,7 @@ internal class SetStateTest : AbstractNettyNHTest() { @Test fun `Precondition - setState should ignore duplications 2 OK to CLOSED to CLOSED`() = runBlockingUnit { - assertNotNull(network.setStateOK(channel)) + assertNotNull(network.setStateOK(conn)) assertState(OK) assertEventBroadcasts { assertNotNull(network.setStateClosed(IllegalStateException("1"))) diff --git a/mirai-core/src/commonTest/kotlin/notice/processors/AbstractNoticeProcessorTest.kt b/mirai-core/src/commonTest/kotlin/notice/processors/AbstractNoticeProcessorTest.kt index 4e01582c1..592c10e2f 100644 --- a/mirai-core/src/commonTest/kotlin/notice/processors/AbstractNoticeProcessorTest.kt +++ b/mirai-core/src/commonTest/kotlin/notice/processors/AbstractNoticeProcessorTest.kt @@ -7,6 +7,8 @@ * https://github.com/mamoe/mirai/blob/dev/LICENSE */ +@file:OptIn(TestOnly::class) + package net.mamoe.mirai.internal.notice.processors import kotlinx.serialization.SerializationStrategy @@ -27,7 +29,7 @@ import net.mamoe.mirai.internal.contact.info.StrangerInfoImpl import net.mamoe.mirai.internal.network.Packet import net.mamoe.mirai.internal.network.components.* import net.mamoe.mirai.internal.network.components.NoticeProcessorPipeline.Companion.noticeProcessorPipeline -import net.mamoe.mirai.internal.network.framework.AbstractNettyNHTest +import net.mamoe.mirai.internal.network.framework.AbstractCommonNHTest import net.mamoe.mirai.internal.network.protocol.packet.IncomingPacket import net.mamoe.mirai.internal.utils.io.JceStruct import net.mamoe.mirai.internal.utils.io.ProtocolStruct @@ -38,9 +40,9 @@ import net.mamoe.mirai.utils.* /** * To add breakpoint, see [NoticeProcessorPipelineImpl.process] */ -internal abstract class AbstractNoticeProcessorTest : AbstractNettyNHTest(), GroupExtensions { +internal abstract class AbstractNoticeProcessorTest : AbstractCommonNHTest(), GroupExtensions { init { - System.setProperty("mirai.network.notice.pipeline.log.full", "true") + setSystemProp("mirai.network.notice.pipeline.log.full", "true") } protected class UseTestContext( @@ -56,7 +58,7 @@ internal abstract class AbstractNoticeProcessorTest : AbstractNettyNHTest(), Gro } protected suspend inline fun use( - attributes: TypeSafeMap = TypeSafeMap(), + attributes: TypeSafeMap = createTypeSafeMap(), pipeline: NoticeProcessorPipeline = bot.components.noticeProcessorPipeline, block: UseTestContext.() -> ProtocolStruct ): Collection { @@ -71,7 +73,7 @@ internal abstract class AbstractNoticeProcessorTest : AbstractNettyNHTest(), Gro } protected suspend inline fun use( - attributes: TypeSafeMap = TypeSafeMap(), + attributes: TypeSafeMap = createTypeSafeMap(), crossinline createContext: NoticeProcessorPipelineImpl.(attributes: TypeSafeMap) -> NoticeProcessorPipelineImpl.ContextImpl, block: UseTestContext.() -> ProtocolStruct ): Collection = @@ -84,7 +86,7 @@ internal abstract class AbstractNoticeProcessorTest : AbstractNettyNHTest(), Gro createContext(this, attributes) }, block) - fun setBot(id: Long): QQAndroidBot { + open fun setBot(id: Long): QQAndroidBot { bot = createBot(BotAccount(id, "a")) return bot } diff --git a/mirai-core/src/commonTest/kotlin/notice/processors/BotInvitedJoinTest.kt b/mirai-core/src/commonTest/kotlin/notice/processors/BotInvitedJoinTest.kt index 61ead2bde..77c4f6ff0 100644 --- a/mirai-core/src/commonTest/kotlin/notice/processors/BotInvitedJoinTest.kt +++ b/mirai-core/src/commonTest/kotlin/notice/processors/BotInvitedJoinTest.kt @@ -18,46 +18,48 @@ import net.mamoe.mirai.event.events.BotJoinGroupEvent import net.mamoe.mirai.internal.QQAndroidBot import net.mamoe.mirai.internal.contact.GroupImpl import net.mamoe.mirai.internal.network.components.NoticeProcessorPipelineImpl -import org.junit.jupiter.api.Test +import net.mamoe.mirai.internal.network.protocol.data.proto.Structmsg +import net.mamoe.mirai.internal.test.runBlockingUnit +import kotlin.test.Test import kotlin.test.assertEquals import kotlin.test.assertIs internal class BotInvitedJoinTest : AbstractNoticeProcessorTest() { @Test - suspend fun `invited join`() { + fun `invited join`() = runBlockingUnit { suspend fun runTest() = use { - net.mamoe.mirai.internal.network.protocol.data.proto.Structmsg.StructMsg( + Structmsg.StructMsg( version = 1, msgType = 2, msgSeq = 1630, msgTime = 1630, reqUin = 1230, - msg = net.mamoe.mirai.internal.network.protocol.data.proto.Structmsg.SystemMsg( + msg = Structmsg.SystemMsg( subType = 1, msgTitle = "邀请加群", msgDescribe = "邀请你加入 %group_name%", actions = mutableListOf( - net.mamoe.mirai.internal.network.protocol.data.proto.Structmsg.SystemMsgAction( + Structmsg.SystemMsgAction( name = "拒绝", result = "已拒绝", - actionInfo = net.mamoe.mirai.internal.network.protocol.data.proto.Structmsg.SystemMsgActionInfo( + actionInfo = Structmsg.SystemMsgActionInfo( type = 12, groupCode = 2230203, ), detailName = "拒绝", - ), net.mamoe.mirai.internal.network.protocol.data.proto.Structmsg.SystemMsgAction( + ), Structmsg.SystemMsgAction( name = "同意", result = "已同意", - actionInfo = net.mamoe.mirai.internal.network.protocol.data.proto.Structmsg.SystemMsgActionInfo( + actionInfo = Structmsg.SystemMsgActionInfo( type = 11, groupCode = 2230203, ), detailName = "同意", - ), net.mamoe.mirai.internal.network.protocol.data.proto.Structmsg.SystemMsgAction( + ), Structmsg.SystemMsgAction( name = "忽略", result = "已忽略", - actionInfo = net.mamoe.mirai.internal.network.protocol.data.proto.Structmsg.SystemMsgActionInfo( + actionInfo = Structmsg.SystemMsgActionInfo( type = 14, groupCode = 2230203, ), @@ -68,10 +70,10 @@ internal class BotInvitedJoinTest : AbstractNoticeProcessorTest() { actionUin = 1230001, groupMsgType = 2, groupInviterRole = 1, - groupInfo = net.mamoe.mirai.internal.network.protocol.data.proto.Structmsg.GroupInfo( + groupInfo = Structmsg.GroupInfo( appPrivilegeFlag = 67698880, ), - msgInviteExtinfo = net.mamoe.mirai.internal.network.protocol.data.proto.Structmsg.MsgInviteExt( + msgInviteExtinfo = Structmsg.MsgInviteExt( ), reqUinNick = "user3", groupName = "testtest", @@ -101,7 +103,7 @@ internal class BotInvitedJoinTest : AbstractNoticeProcessorTest() { } @Test - suspend fun `invited join, accepted`() { + fun `invited join accepted`() = runBlockingUnit { // https://github.com/mamoe/mirai/issues/1213 suspend fun runTest() = use( createContext = { attributes -> @@ -152,7 +154,7 @@ internal class BotInvitedJoinTest : AbstractNoticeProcessorTest() { @Test - suspend fun `invitation accepted`() { + fun `invitation accepted`() = runBlockingUnit { suspend fun runTest() = use(createContext = { attributes -> object : NoticeProcessorPipelineImpl.ContextImpl(attributes) { diff --git a/mirai-core/src/commonTest/kotlin/notice/processors/FriendNickChangeTest.kt b/mirai-core/src/commonTest/kotlin/notice/processors/FriendNickChangeTest.kt index 1dd32d7dc..443b9349d 100644 --- a/mirai-core/src/commonTest/kotlin/notice/processors/FriendNickChangeTest.kt +++ b/mirai-core/src/commonTest/kotlin/notice/processors/FriendNickChangeTest.kt @@ -1,5 +1,5 @@ /* - * Copyright 2019-2021 Mamoe Technologies and contributors. + * Copyright 2019-2022 Mamoe Technologies and contributors. * * 此源代码的使用受 GNU AFFERO GENERAL PUBLIC LICENSE version 3 许可证的约束, 可以在以下链接找到该许可证. * Use of this source code is governed by the GNU AGPLv3 license that can be found through the following link. @@ -15,22 +15,24 @@ import me.him188.kotlin.jvm.blocking.bridge.JvmBlockingBridge import net.mamoe.mirai.event.events.FriendNickChangedEvent import net.mamoe.mirai.internal.network.protocol.data.jce.MsgInfo import net.mamoe.mirai.internal.network.protocol.data.jce.MsgType0x210 +import net.mamoe.mirai.internal.network.protocol.data.jce.OnlinePushPack import net.mamoe.mirai.internal.network.protocol.data.jce.ShareData import net.mamoe.mirai.internal.network.protocol.data.proto.Submsgtype0x27.SubMsgType0x27 +import net.mamoe.mirai.internal.test.runBlockingUnit import net.mamoe.mirai.internal.utils.io.serialization.toByteArray -import org.junit.jupiter.api.Test +import kotlin.test.Test import kotlin.test.assertEquals import kotlin.test.assertIs internal class FriendNickChangeTest : AbstractNoticeProcessorTest() { @Test - suspend fun `nick changed`() { + fun `nick changed`() = runBlockingUnit { // FriendNickChangedEvent 内容异常 https://github.com/mamoe/mirai/issues/1356 suspend fun runTest() = use { - net.mamoe.mirai.internal.network.protocol.data.jce.OnlinePushPack.SvcReqPushMsg( + OnlinePushPack.SvcReqPushMsg( uin = 1230002, uMsgTime = 1633037660, vMsgInfos = mutableListOf( diff --git a/mirai-core/src/commonTest/kotlin/notice/processors/GroupRetrieveTest.kt b/mirai-core/src/commonTest/kotlin/notice/processors/GroupRetrieveTest.kt index 6479a089c..6e26889e3 100644 --- a/mirai-core/src/commonTest/kotlin/notice/processors/GroupRetrieveTest.kt +++ b/mirai-core/src/commonTest/kotlin/notice/processors/GroupRetrieveTest.kt @@ -1,5 +1,5 @@ /* - * Copyright 2019-2021 Mamoe Technologies and contributors. + * Copyright 2019-2022 Mamoe Technologies and contributors. * * 此源代码的使用受 GNU AFFERO GENERAL PUBLIC LICENSE version 3 许可证的约束, 可以在以下链接找到该许可证. * Use of this source code is governed by the GNU AGPLv3 license that can be found through the following link. @@ -14,14 +14,15 @@ import me.him188.kotlin.jvm.blocking.bridge.JvmBlockingBridge import net.mamoe.mirai.contact.MemberPermission import net.mamoe.mirai.event.events.BotGroupPermissionChangeEvent import net.mamoe.mirai.event.events.MemberPermissionChangeEvent -import org.junit.jupiter.api.Test +import net.mamoe.mirai.internal.test.runBlockingUnit +import kotlin.test.Test import kotlin.test.assertEquals import kotlin.test.assertIs internal class GroupRetrieveTest : AbstractNoticeProcessorTest() { @Test - suspend fun `other member retrieves group from another member when they are in the group`() { + fun `other member retrieves group from another member when they are in the group`() = runBlockingUnit { suspend fun runTest() = use { net.mamoe.mirai.internal.network.protocol.data.proto.OnlinePushTrans.PbMsgInfo( fromUin = 2230203, @@ -62,7 +63,7 @@ internal class GroupRetrieveTest : AbstractNoticeProcessorTest() { } @Test - suspend fun `other member retrieves group from bot when they are in the group`() { + fun `other member retrieves group from bot when they are in the group`() = runBlockingUnit { suspend fun runTest() = use { net.mamoe.mirai.internal.network.protocol.data.proto.OnlinePushTrans.PbMsgInfo( fromUin = 2230203, diff --git a/mirai-core/src/commonTest/kotlin/notice/processors/GroupTransferTest.kt b/mirai-core/src/commonTest/kotlin/notice/processors/GroupTransferTest.kt index dec19f7a3..3985fc3b1 100644 --- a/mirai-core/src/commonTest/kotlin/notice/processors/GroupTransferTest.kt +++ b/mirai-core/src/commonTest/kotlin/notice/processors/GroupTransferTest.kt @@ -1,5 +1,5 @@ /* - * Copyright 2019-2021 Mamoe Technologies and contributors. + * Copyright 2019-2022 Mamoe Technologies and contributors. * * 此源代码的使用受 GNU AFFERO GENERAL PUBLIC LICENSE version 3 许可证的约束, 可以在以下链接找到该许可证. * Use of this source code is governed by the GNU AGPLv3 license that can be found through the following link. @@ -14,7 +14,8 @@ import me.him188.kotlin.jvm.blocking.bridge.JvmBlockingBridge import net.mamoe.mirai.contact.MemberPermission import net.mamoe.mirai.event.events.BotGroupPermissionChangeEvent import net.mamoe.mirai.event.events.MemberPermissionChangeEvent -import org.junit.jupiter.api.Test +import net.mamoe.mirai.internal.test.runBlockingUnit +import kotlin.test.Test import kotlin.test.assertEquals import kotlin.test.assertIs @@ -22,7 +23,7 @@ import kotlin.test.assertIs internal class GroupTransferTest : AbstractNoticeProcessorTest() { @Test - suspend fun `owner transfers group to other member`() { + fun `owner transfers group to other member`() = runBlockingUnit { suspend fun runTest() = use { net.mamoe.mirai.internal.network.protocol.data.proto.OnlinePushTrans.PbMsgInfo( fromUin = 2230203, @@ -63,7 +64,7 @@ internal class GroupTransferTest : AbstractNoticeProcessorTest() { } @Test - suspend fun `owner transfers group to bot`() { + fun `owner transfers group to bot`() = runBlockingUnit { suspend fun runTest() = use { net.mamoe.mirai.internal.network.protocol.data.proto.OnlinePushTrans.PbMsgInfo( fromUin = 2230203, diff --git a/mirai-core/src/commonTest/kotlin/notice/processors/MemberAdminChangeTest.kt b/mirai-core/src/commonTest/kotlin/notice/processors/MemberAdminChangeTest.kt index 3e3d5a1e7..efedb9076 100644 --- a/mirai-core/src/commonTest/kotlin/notice/processors/MemberAdminChangeTest.kt +++ b/mirai-core/src/commonTest/kotlin/notice/processors/MemberAdminChangeTest.kt @@ -1,5 +1,5 @@ /* - * Copyright 2019-2021 Mamoe Technologies and contributors. + * Copyright 2019-2022 Mamoe Technologies and contributors. * * 此源代码的使用受 GNU AFFERO GENERAL PUBLIC LICENSE version 3 许可证的约束, 可以在以下链接找到该许可证. * Use of this source code is governed by the GNU AGPLv3 license that can be found through the following link. @@ -15,7 +15,9 @@ import me.him188.kotlin.jvm.blocking.bridge.JvmBlockingBridge import net.mamoe.mirai.contact.MemberPermission import net.mamoe.mirai.event.events.BotGroupPermissionChangeEvent import net.mamoe.mirai.event.events.MemberPermissionChangeEvent -import org.junit.jupiter.api.Test +import net.mamoe.mirai.internal.network.protocol.data.proto.OnlinePushTrans +import net.mamoe.mirai.internal.test.runBlockingUnit +import kotlin.test.Test import kotlin.test.assertEquals import kotlin.test.assertIs @@ -26,9 +28,9 @@ import kotlin.test.assertIs internal class MemberAdminChangeTest : AbstractNoticeProcessorTest() { @Test - suspend fun `bot member to admin`() { + fun `bot member to admin`() = runBlockingUnit { suspend fun runTest() = use { - net.mamoe.mirai.internal.network.protocol.data.proto.OnlinePushTrans.PbMsgInfo( + OnlinePushTrans.PbMsgInfo( fromUin = 2230203, toUin = 1230003, msgType = 44, @@ -74,7 +76,7 @@ internal class MemberAdminChangeTest : AbstractNoticeProcessorTest() { } @Test - suspend fun `bot admin to member`() { + fun `bot admin to member`() = runBlockingUnit { suspend fun runTest() = use { net.mamoe.mirai.internal.network.protocol.data.proto.OnlinePushTrans.PbMsgInfo( fromUin = 2230203, @@ -119,7 +121,7 @@ internal class MemberAdminChangeTest : AbstractNoticeProcessorTest() { } @Test - suspend fun `member member to admin`() { + fun `member member to admin`() = runBlockingUnit { suspend fun runTest() = use { net.mamoe.mirai.internal.network.protocol.data.proto.OnlinePushTrans.PbMsgInfo( fromUin = 2230203, @@ -168,7 +170,7 @@ internal class MemberAdminChangeTest : AbstractNoticeProcessorTest() { } @Test - suspend fun `member admin to member`() { + fun `member admin to member`() = runBlockingUnit { suspend fun runTest() = use { net.mamoe.mirai.internal.network.protocol.data.proto.OnlinePushTrans.PbMsgInfo( fromUin = 2230203, diff --git a/mirai-core/src/commonTest/kotlin/notice/processors/MemberJoinTest.kt b/mirai-core/src/commonTest/kotlin/notice/processors/MemberJoinTest.kt index a9dc858a2..2aef0604b 100644 --- a/mirai-core/src/commonTest/kotlin/notice/processors/MemberJoinTest.kt +++ b/mirai-core/src/commonTest/kotlin/notice/processors/MemberJoinTest.kt @@ -1,5 +1,5 @@ /* - * Copyright 2019-2021 Mamoe Technologies and contributors. + * Copyright 2019-2022 Mamoe Technologies and contributors. * * 此源代码的使用受 GNU AFFERO GENERAL PUBLIC LICENSE version 3 许可证的约束, 可以在以下链接找到该许可证. * Use of this source code is governed by the GNU AGPLv3 license that can be found through the following link. @@ -15,24 +15,22 @@ import me.him188.kotlin.jvm.blocking.bridge.JvmBlockingBridge import net.mamoe.mirai.contact.MemberPermission import net.mamoe.mirai.event.events.MemberJoinEvent import net.mamoe.mirai.event.events.MemberJoinRequestEvent -import org.junit.jupiter.api.Test -import kotlin.test.assertEquals -import kotlin.test.assertIs -import kotlin.test.assertNotNull -import kotlin.test.assertNull +import net.mamoe.mirai.internal.network.protocol.data.proto.Structmsg +import net.mamoe.mirai.internal.test.runBlockingUnit +import kotlin.test.* internal class MemberJoinTest : AbstractNoticeProcessorTest() { @Test - suspend fun `member actively request join`() { + fun `member actively request join`() = runBlockingUnit { suspend fun runTest() = use { - net.mamoe.mirai.internal.network.protocol.data.proto.Structmsg.StructMsg( + Structmsg.StructMsg( version = 1, msgType = 2, msgSeq = 16300, msgTime = 1630, reqUin = 1230001, - msg = net.mamoe.mirai.internal.network.protocol.data.proto.Structmsg.SystemMsg( + msg = Structmsg.SystemMsg( subType = 1, msgTitle = "加群申请", msgDescribe = "申请加入 %group_name%", @@ -40,26 +38,26 @@ internal class MemberJoinTest : AbstractNoticeProcessorTest() { srcId = 1, subSrcId = 5, actions = mutableListOf( - net.mamoe.mirai.internal.network.protocol.data.proto.Structmsg.SystemMsgAction( + Structmsg.SystemMsgAction( name = "拒绝", result = "已拒绝", - actionInfo = net.mamoe.mirai.internal.network.protocol.data.proto.Structmsg.SystemMsgActionInfo( + actionInfo = Structmsg.SystemMsgActionInfo( type = 12, groupCode = 2230203, ), detailName = "拒绝", - ), net.mamoe.mirai.internal.network.protocol.data.proto.Structmsg.SystemMsgAction( + ), Structmsg.SystemMsgAction( name = "同意", result = "已同意", - actionInfo = net.mamoe.mirai.internal.network.protocol.data.proto.Structmsg.SystemMsgActionInfo( + actionInfo = Structmsg.SystemMsgActionInfo( type = 11, groupCode = 2230203, ), detailName = "同意", - ), net.mamoe.mirai.internal.network.protocol.data.proto.Structmsg.SystemMsgAction( + ), Structmsg.SystemMsgAction( name = "忽略", result = "已忽略", - actionInfo = net.mamoe.mirai.internal.network.protocol.data.proto.Structmsg.SystemMsgActionInfo( + actionInfo = Structmsg.SystemMsgActionInfo( type = 14, groupCode = 2230203, ), @@ -68,7 +66,7 @@ internal class MemberJoinTest : AbstractNoticeProcessorTest() { ), groupCode = 2230203, groupMsgType = 1, - groupInfo = net.mamoe.mirai.internal.network.protocol.data.proto.Structmsg.GroupInfo( + groupInfo = Structmsg.GroupInfo( appPrivilegeFlag = 67698880, ), groupFlagext3 = 128, @@ -100,7 +98,7 @@ internal class MemberJoinTest : AbstractNoticeProcessorTest() { } @Test - suspend fun `member request accepted by other admin`() { + fun `member request accepted by other admin`() = runBlockingUnit { suspend fun runTest() = use { net.mamoe.mirai.internal.network.protocol.data.proto.MsgComm.Msg( msgHead = net.mamoe.mirai.internal.network.protocol.data.proto.MsgComm.MsgHead( @@ -147,7 +145,7 @@ internal class MemberJoinTest : AbstractNoticeProcessorTest() { } @Test - suspend fun `member request accepted by bot as admin`() { + fun `member request accepted by bot as admin`() = runBlockingUnit { suspend fun runTest() = use { net.mamoe.mirai.internal.network.protocol.data.proto.MsgComm.Msg( msgHead = net.mamoe.mirai.internal.network.protocol.data.proto.MsgComm.MsgHead( @@ -195,7 +193,7 @@ internal class MemberJoinTest : AbstractNoticeProcessorTest() { @Test - suspend fun `member joins directly when group allows anyone`() { + fun `member joins directly when group allows anyone`() = runBlockingUnit { suspend fun runTest() = use { net.mamoe.mirai.internal.network.protocol.data.proto.MsgComm.Msg( msgHead = net.mamoe.mirai.internal.network.protocol.data.proto.MsgComm.MsgHead( diff --git a/mirai-core/src/commonTest/kotlin/notice/processors/MemberQuitTest.kt b/mirai-core/src/commonTest/kotlin/notice/processors/MemberQuitTest.kt index 8359c959d..80560af31 100644 --- a/mirai-core/src/commonTest/kotlin/notice/processors/MemberQuitTest.kt +++ b/mirai-core/src/commonTest/kotlin/notice/processors/MemberQuitTest.kt @@ -1,5 +1,5 @@ /* - * Copyright 2019-2021 Mamoe Technologies and contributors. + * Copyright 2019-2022 Mamoe Technologies and contributors. * * 此源代码的使用受 GNU AFFERO GENERAL PUBLIC LICENSE version 3 许可证的约束, 可以在以下链接找到该许可证. * Use of this source code is governed by the GNU AGPLv3 license that can be found through the following link. @@ -14,16 +14,18 @@ package net.mamoe.mirai.internal.notice.processors import me.him188.kotlin.jvm.blocking.bridge.JvmBlockingBridge import net.mamoe.mirai.contact.MemberPermission import net.mamoe.mirai.event.events.MemberLeaveEvent -import org.junit.jupiter.api.Test +import net.mamoe.mirai.internal.network.protocol.data.proto.OnlinePushTrans +import net.mamoe.mirai.internal.test.runBlockingUnit +import kotlin.test.Test import kotlin.test.assertEquals import kotlin.test.assertIs internal class MemberQuitTest : AbstractNoticeProcessorTest() { @Test - suspend fun `member active quit`() { + fun `member active quit`() = runBlockingUnit { suspend fun runTest() = use { - net.mamoe.mirai.internal.network.protocol.data.proto.OnlinePushTrans.PbMsgInfo( + OnlinePushTrans.PbMsgInfo( fromUin = 2230203, toUin = 1230003, msgType = 34, @@ -33,7 +35,7 @@ internal class MemberQuitTest : AbstractNoticeProcessorTest() { realMsgTime = 1629, msgData = "00 22 07 BB 01 00 12 C4 B1 02 00 30 39 41 36 36 41 32 31 32 33 35 37 32 43 39 35 38 42 42 36 38 45 32 36 44 34 34 32 38 45 32 32 37 32 36 44 39 44 45 41 31 34 41 44 37 30 31 46 31".hexToBytes(), svrIp = 618, - extGroupKeyInfo = net.mamoe.mirai.internal.network.protocol.data.proto.OnlinePushTrans.ExtGroupKeyInfo( + extGroupKeyInfo = OnlinePushTrans.ExtGroupKeyInfo( curMaxSeq = 1626, curTime = 16298, ), @@ -58,7 +60,7 @@ internal class MemberQuitTest : AbstractNoticeProcessorTest() { } @Test - suspend fun `member kick`() { + fun `member kick`() = runBlockingUnit { suspend fun runTest() = use { net.mamoe.mirai.internal.network.protocol.data.proto.OnlinePushTrans.PbMsgInfo( fromUin = 2230203, diff --git a/mirai-core/src/commonTest/kotlin/notice/processors/MessageSyncTest.kt b/mirai-core/src/commonTest/kotlin/notice/processors/MessageSyncTest.kt index c05d53ea2..c283c04f0 100644 --- a/mirai-core/src/commonTest/kotlin/notice/processors/MessageSyncTest.kt +++ b/mirai-core/src/commonTest/kotlin/notice/processors/MessageSyncTest.kt @@ -9,13 +9,18 @@ package net.mamoe.mirai.internal.notice.processors +import io.ktor.utils.io.core.* import me.him188.kotlin.jvm.blocking.bridge.JvmBlockingBridge import net.mamoe.mirai.contact.MemberPermission import net.mamoe.mirai.event.events.FriendMessageSyncEvent import net.mamoe.mirai.event.events.GroupMessageSyncEvent import net.mamoe.mirai.internal.network.components.NoticePipelineContext.Companion.KEY_FROM_SYNC +import net.mamoe.mirai.internal.network.protocol.data.proto.ImMsgBody +import net.mamoe.mirai.internal.network.protocol.data.proto.MsgComm +import net.mamoe.mirai.internal.network.protocol.data.proto.MsgOnlinePush +import net.mamoe.mirai.internal.test.runBlockingUnit import net.mamoe.mirai.message.data.content -import org.junit.jupiter.api.Test +import kotlin.test.Test import kotlin.test.assertEquals import kotlin.test.assertIs @@ -23,18 +28,18 @@ import kotlin.test.assertIs internal class MessageSyncTest : AbstractNoticeProcessorTest() { @Test - suspend fun `can receive group sync from macOS client`() { + fun `can receive group sync from macOS client`() = runBlockingUnit { suspend fun runTest() = use { - net.mamoe.mirai.internal.network.protocol.data.proto.MsgOnlinePush.PbPushMsg( - msg = net.mamoe.mirai.internal.network.protocol.data.proto.MsgComm.Msg( - msgHead = net.mamoe.mirai.internal.network.protocol.data.proto.MsgComm.MsgHead( + MsgOnlinePush.PbPushMsg( + msg = MsgComm.Msg( + msgHead = MsgComm.MsgHead( fromUin = 1230002, toUin = 1230002, msgType = 82, msgSeq = 1772, msgTime = 1640029614, msgUid = 144115188088832082, - groupInfo = net.mamoe.mirai.internal.network.protocol.data.proto.MsgComm.GroupInfo( + groupInfo = MsgComm.GroupInfo( groupCode = 2230203, groupType = 1, groupInfoSeq = 657, @@ -47,9 +52,9 @@ internal class MessageSyncTest : AbstractNoticeProcessorTest() { fromInstid = 537067835, userActive = 1, ), - msgBody = net.mamoe.mirai.internal.network.protocol.data.proto.ImMsgBody.MsgBody( - richText = net.mamoe.mirai.internal.network.protocol.data.proto.ImMsgBody.RichText( - attr = net.mamoe.mirai.internal.network.protocol.data.proto.ImMsgBody.Attr( + msgBody = ImMsgBody.MsgBody( + richText = ImMsgBody.RichText( + attr = ImMsgBody.Attr( codePage = 0, time = 1640029614, random = 25984994, @@ -60,23 +65,23 @@ internal class MessageSyncTest : AbstractNoticeProcessorTest() { fontName = "Helvetica", ), elems = mutableListOf( - net.mamoe.mirai.internal.network.protocol.data.proto.ImMsgBody.Elem( - text = net.mamoe.mirai.internal.network.protocol.data.proto.ImMsgBody.Text( + ImMsgBody.Elem( + text = ImMsgBody.Text( str = "s", ), ), - net.mamoe.mirai.internal.network.protocol.data.proto.ImMsgBody.Elem( - elemFlags2 = net.mamoe.mirai.internal.network.protocol.data.proto.ImMsgBody.ElemFlags2( + ImMsgBody.Elem( + elemFlags2 = ImMsgBody.ElemFlags2( msgRptCnt = 1, ), ), - net.mamoe.mirai.internal.network.protocol.data.proto.ImMsgBody.Elem( - generalFlags = net.mamoe.mirai.internal.network.protocol.data.proto.ImMsgBody.GeneralFlags( + ImMsgBody.Elem( + generalFlags = ImMsgBody.GeneralFlags( pbReserve = "".hexToBytes(), ), ), - net.mamoe.mirai.internal.network.protocol.data.proto.ImMsgBody.Elem( - extraInfo = net.mamoe.mirai.internal.network.protocol.data.proto.ImMsgBody.ExtraInfo( + ImMsgBody.Elem( + extraInfo = ImMsgBody.ExtraInfo( nick = "user2", level = 1, groupMask = 1, @@ -112,7 +117,7 @@ internal class MessageSyncTest : AbstractNoticeProcessorTest() { @Test - suspend fun `can receive friend sync from macOS client`() { + fun `can receive friend sync from macOS client`() = runBlockingUnit { suspend fun runTest() = use { attributes[KEY_FROM_SYNC] = true diff --git a/mirai-core/src/commonTest/kotlin/notice/processors/MessageTest.kt b/mirai-core/src/commonTest/kotlin/notice/processors/MessageTest.kt index d6b7ffc16..a34307fb2 100644 --- a/mirai-core/src/commonTest/kotlin/notice/processors/MessageTest.kt +++ b/mirai-core/src/commonTest/kotlin/notice/processors/MessageTest.kt @@ -11,17 +11,19 @@ package net.mamoe.mirai.internal.notice.processors +import io.ktor.utils.io.core.* import me.him188.kotlin.jvm.blocking.bridge.JvmBlockingBridge import net.mamoe.mirai.contact.MemberPermission import net.mamoe.mirai.event.events.FriendMessageEvent import net.mamoe.mirai.event.events.GroupMessageEvent import net.mamoe.mirai.event.events.GroupTempMessageEvent import net.mamoe.mirai.internal.network.components.NoticePipelineContext.Companion.KEY_FROM_SYNC +import net.mamoe.mirai.internal.test.runBlockingUnit import net.mamoe.mirai.message.data.MessageSource import net.mamoe.mirai.message.data.OnlineMessageSource import net.mamoe.mirai.message.data.PlainText import net.mamoe.mirai.message.data.content -import org.junit.jupiter.api.Test +import kotlin.test.Test import kotlin.test.assertContentEquals import kotlin.test.assertEquals import kotlin.test.assertIs @@ -29,7 +31,7 @@ import kotlin.test.assertIs internal class MessageTest : AbstractNoticeProcessorTest() { @Test - suspend fun `group message test`() { + fun `group message test`() = runBlockingUnit { suspend fun runTest() = use { net.mamoe.mirai.internal.network.protocol.data.proto.MsgOnlinePush.PbPushMsg( msg = net.mamoe.mirai.internal.network.protocol.data.proto.MsgComm.Msg( @@ -132,7 +134,7 @@ internal class MessageTest : AbstractNoticeProcessorTest() { @Test - suspend fun `friend message test`() { + fun `friend message test`() = runBlockingUnit { suspend fun runTest() = use(KEY_FROM_SYNC to false) { net.mamoe.mirai.internal.network.protocol.data.proto.MsgComm.Msg( msgHead = net.mamoe.mirai.internal.network.protocol.data.proto.MsgComm.MsgHead( @@ -212,7 +214,7 @@ internal class MessageTest : AbstractNoticeProcessorTest() { } @Test - suspend fun `group temp message test`() { + fun `group temp message test`() = runBlockingUnit { suspend fun runTest() = use(KEY_FROM_SYNC to false) { net.mamoe.mirai.internal.network.protocol.data.proto.MsgComm.Msg( msgHead = net.mamoe.mirai.internal.network.protocol.data.proto.MsgComm.MsgHead( @@ -306,7 +308,7 @@ internal class MessageTest : AbstractNoticeProcessorTest() { // for #1410 @Test - suspend fun `group temp message test for issue 1410`() { + fun `group temp message test for issue 1410`() = runBlockingUnit { suspend fun runTest() = use(KEY_FROM_SYNC to false) { net.mamoe.mirai.internal.network.protocol.data.proto.MsgComm.Msg( msgHead = net.mamoe.mirai.internal.network.protocol.data.proto.MsgComm.MsgHead( diff --git a/mirai-core/src/commonTest/kotlin/notice/processors/MuteTest.kt b/mirai-core/src/commonTest/kotlin/notice/processors/MuteTest.kt index b8d480f53..2149bb35d 100644 --- a/mirai-core/src/commonTest/kotlin/notice/processors/MuteTest.kt +++ b/mirai-core/src/commonTest/kotlin/notice/processors/MuteTest.kt @@ -1,5 +1,5 @@ /* - * Copyright 2019-2021 Mamoe Technologies and contributors. + * Copyright 2019-2022 Mamoe Technologies and contributors. * * 此源代码的使用受 GNU AFFERO GENERAL PUBLIC LICENSE version 3 许可证的约束, 可以在以下链接找到该许可证. * Use of this source code is governed by the GNU AGPLv3 license that can be found through the following link. @@ -20,15 +20,16 @@ import net.mamoe.mirai.event.events.MemberUnmuteEvent import net.mamoe.mirai.internal.network.protocol.data.jce.MsgInfo import net.mamoe.mirai.internal.network.protocol.data.jce.OnlinePushPack import net.mamoe.mirai.internal.network.protocol.data.jce.ShareData +import net.mamoe.mirai.internal.test.runBlockingUnit import net.mamoe.mirai.utils.currentTimeSeconds -import org.junit.jupiter.api.Test +import kotlin.test.Test import kotlin.test.assertEquals import kotlin.test.assertIs internal class MuteTest : AbstractNoticeProcessorTest() { @Test - suspend fun `bot mute`() { + fun `bot mute`() = runBlockingUnit { suspend fun MuteTest.runTest() = use { OnlinePushPack.SvcReqPushMsg( uin = 1230001, @@ -79,7 +80,7 @@ internal class MuteTest : AbstractNoticeProcessorTest() { } @Test - suspend fun `bot unmute`() { + fun `bot unmute`() = runBlockingUnit { suspend fun MuteTest.runTest() = use { OnlinePushPack.SvcReqPushMsg( uin = 1230001, @@ -146,7 +147,7 @@ internal class MuteTest : AbstractNoticeProcessorTest() { } @Test - suspend fun `member mute`() { + fun `member mute`() = runBlockingUnit { suspend fun MuteTest.runTest() = use { OnlinePushPack.SvcReqPushMsg( uin = 1230001, @@ -198,7 +199,7 @@ internal class MuteTest : AbstractNoticeProcessorTest() { @Test - suspend fun `member unmute`() { + fun `member unmute`() = runBlockingUnit { suspend fun MuteTest.runTest() = use { OnlinePushPack.SvcReqPushMsg( uin = 1230001, diff --git a/mirai-core/src/commonTest/kotlin/notice/processors/RecallTest.kt b/mirai-core/src/commonTest/kotlin/notice/processors/RecallTest.kt index 958da8c43..b6648aa99 100644 --- a/mirai-core/src/commonTest/kotlin/notice/processors/RecallTest.kt +++ b/mirai-core/src/commonTest/kotlin/notice/processors/RecallTest.kt @@ -9,17 +9,22 @@ package net.mamoe.mirai.internal.notice.processors +import io.ktor.utils.io.core.* import net.mamoe.mirai.Bot import net.mamoe.mirai.Mirai import net.mamoe.mirai.contact.MemberPermission import net.mamoe.mirai.contact.PermissionDeniedException +import net.mamoe.mirai.internal.QQAndroidBot import net.mamoe.mirai.internal.message.source.OnlineMessageSourceFromGroupImpl import net.mamoe.mirai.internal.network.protocol.data.proto.ImMsgBody import net.mamoe.mirai.internal.network.protocol.data.proto.MsgComm +import net.mamoe.mirai.internal.network.protocol.packet.chat.PbMessageSvc +import net.mamoe.mirai.internal.test.runBlockingUnit import net.mamoe.mirai.message.data.OnlineMessageSource import net.mamoe.mirai.utils.hexToBytes -import org.junit.jupiter.api.Test -import org.junit.jupiter.api.assertThrows +import kotlin.test.Test +import kotlin.test.assertEquals +import kotlin.test.assertFailsWith internal class RecallTest : AbstractNoticeProcessorTest() { @@ -90,8 +95,18 @@ internal class RecallTest : AbstractNoticeProcessorTest() { ) ) + override fun setBot(id: Long): QQAndroidBot { + return super.setBot(id).also { bot -> + runBlockingUnit { bot.login() } + network.addPacketReplier { + assertEquals("PbMessageSvc.PbMsgWithDraw", it.commandName) + reply(PbMessageSvc.PbMsgWithDraw.Response.Success) + } + } + } + @Test - suspend fun `recall member message without permission`() { + fun `recall member message without permission`() = runBlockingUnit { val bot = setBot(2) val group = bot.addGroup(5, 3, MemberPermission.MEMBER).apply { // owner @@ -99,13 +114,13 @@ internal class RecallTest : AbstractNoticeProcessorTest() { // sender addMember(1, permission = MemberPermission.MEMBER) } - assertThrows { + assertFailsWith { Mirai.recallMessage(bot, source(bot, 1, group.id, group.botPermission)) } } @Test - suspend fun `recall member message`() { + fun `recall member message`() = runBlockingUnit { val bot = setBot(2) val group = bot.addGroup(5, 3, MemberPermission.ADMINISTRATOR).apply { // owner @@ -117,7 +132,7 @@ internal class RecallTest : AbstractNoticeProcessorTest() { } @Test - suspend fun `recall administrator message`() { + fun `recall administrator message`() = runBlockingUnit { val bot = setBot(2) val group = bot.addGroup(5, 3, MemberPermission.ADMINISTRATOR).apply { // owner @@ -125,13 +140,13 @@ internal class RecallTest : AbstractNoticeProcessorTest() { // sender addMember(1, permission = MemberPermission.ADMINISTRATOR) } - assertThrows { + assertFailsWith { Mirai.recallMessage(bot, source(bot, 1, group.id, group.botPermission)) } } - + @Test - suspend fun `recall administrator message as owner`() { + fun `recall administrator message as owner`() = runBlockingUnit { val bot = setBot(2) val group = bot.addGroup(5, 2, MemberPermission.OWNER).apply { // sender @@ -141,13 +156,13 @@ internal class RecallTest : AbstractNoticeProcessorTest() { } @Test - suspend fun `recall owner message`() { + fun `recall owner message`() = runBlockingUnit { val bot = setBot(2) val group = bot.addGroup(5, 1, MemberPermission.ADMINISTRATOR).apply { // sender addMember(1, permission = MemberPermission.OWNER) } - assertThrows { + assertFailsWith { Mirai.recallMessage(bot, source(bot, 1, group.id, group.botPermission)) } } diff --git a/mirai-core/src/commonTest/kotlin/test/events.kt b/mirai-core/src/commonTest/kotlin/test/events.kt index e73ed1024..a4e698d1d 100644 --- a/mirai-core/src/commonTest/kotlin/test/events.kt +++ b/mirai-core/src/commonTest/kotlin/test/events.kt @@ -12,8 +12,8 @@ package net.mamoe.mirai.internal.test import kotlinx.coroutines.ExperimentalCoroutinesApi import net.mamoe.mirai.event.Event import net.mamoe.mirai.event.GlobalEventChannel +import net.mamoe.mirai.utils.ConcurrentLinkedQueue import net.mamoe.mirai.utils.cast -import java.util.concurrent.ConcurrentLinkedQueue import kotlin.contracts.InvocationKind import kotlin.contracts.contract import kotlin.test.assertEquals diff --git a/mirai-core/src/commonTest/kotlin/test/initPlatform.common.kt b/mirai-core/src/commonTest/kotlin/test/initPlatform.common.kt index dc609a753..b44f6bd65 100644 --- a/mirai-core/src/commonTest/kotlin/test/initPlatform.common.kt +++ b/mirai-core/src/commonTest/kotlin/test/initPlatform.common.kt @@ -1,56 +1,44 @@ /* - * Copyright 2019-2021 Mamoe Technologies and contributors. + * Copyright 2019-2022 Mamoe Technologies and contributors. * - * 此源代码的使用受 GNU AFFERO GENERAL PUBLIC LICENSE version 3 许可证的约束, 可以在以下链接找到该许可证. - * Use of this source code is governed by the GNU AGPLv3 license that can be found through the following link. + * 此源代码的使用受 GNU AFFERO GENERAL PUBLIC LICENSE version 3 许可证的约束, 可以在以下链接找到该许可证. + * Use of this source code is governed by the GNU AGPLv3 license that can be found through the following link. * - * https://github.com/mamoe/mirai/blob/master/LICENSE + * https://github.com/mamoe/mirai/blob/dev/LICENSE */ package net.mamoe.mirai.internal.test -import net.mamoe.mirai.IMirai -import net.mamoe.mirai.internal.network.framework.SynchronizedStdoutLogger -import net.mamoe.mirai.utils.MiraiLogger -import org.junit.jupiter.api.AfterEach -import org.junit.jupiter.api.Test -import org.junit.jupiter.api.Timeout -import java.util.concurrent.TimeUnit +import kotlinx.coroutines.* +import kotlin.test.AfterTest +import kotlin.test.Test internal expect fun initPlatform() + +@Suppress("UnnecessaryOptInAnnotation") // on JVM +@OptIn(ExperimentalCoroutinesApi::class, DelicateCoroutinesApi::class) +internal abstract class CommonAbstractTest { + private val dispatchers = mutableListOf() + + fun borrowSingleThreadDispatcher(): CoroutineDispatcher { + return newSingleThreadContext(this::class.simpleName ?: "CommonAbstractTest") + } + + @AfterTest + fun closeAllDispatchers() { + for (dispatcher in dispatchers) { + dispatcher.close() + } + } +} + /** * All test classes should inherit from [AbstractTest] */ -@Timeout(value = 7, unit = TimeUnit.MINUTES) -abstract class AbstractTest { - init { - initPlatform() +internal expect abstract class AbstractTest() : CommonAbstractTest { - restoreLoggerFactory() - - System.setProperty("mirai.network.packet.logger", "true") - System.setProperty("mirai.network.state.observer.logging", "true") - System.setProperty("mirai.network.show.all.components", "true") - System.setProperty("mirai.network.show.components.creation.stacktrace", "true") - System.setProperty("mirai.network.handle.selector.logging", "true") - - } - - @AfterEach - protected fun restoreLoggerFactory() { - @Suppress("DEPRECATION_ERROR") - MiraiLogger.setDefaultLoggerCreator { - SynchronizedStdoutLogger(it) - } - } - - companion object { - init { - Exception() // create a exception to load relevant classes to estimate invocation time of test cases more accurately. - IMirai::class.simpleName // similarly, load classes. - } - } + companion object } internal expect class PlatformInitializationTest() : AbstractTest { diff --git a/mirai-core/src/commonTest/kotlin/testFramework/DebugProbes.kt b/mirai-core/src/commonTest/kotlin/testFramework/DebugProbes.kt new file mode 100644 index 000000000..929e14c04 --- /dev/null +++ b/mirai-core/src/commonTest/kotlin/testFramework/DebugProbes.kt @@ -0,0 +1,106 @@ +/* + * Copyright 2019-2022 Mamoe Technologies and contributors. + * + * 此源代码的使用受 GNU AFFERO GENERAL PUBLIC LICENSE version 3 许可证的约束, 可以在以下链接找到该许可证. + * Use of this source code is governed by the GNU AGPLv3 license that can be found through the following link. + * + * https://github.com/mamoe/mirai/blob/dev/LICENSE + */ + +package net.mamoe.mirai.internal.testFramework + +import kotlinx.coroutines.CoroutineScope +import kotlinx.coroutines.Job + +/** + * Mirror of kotlinx-coroutines-debug to be used in common sources. + */ +@Suppress("RedundantSetter", "RedundantGetter") +internal expect object DebugProbes { + + /** + * Whether coroutine creation stack traces should be sanitized. + * Sanitization removes all frames from `kotlinx.coroutines` package except + * the first one and the last one to simplify diagnostic. + */ + var sanitizeStackTraces: Boolean get set + + /** + * Whether coroutine creation stack traces should be captured. + * When enabled, for each created coroutine a stack trace of the current + * thread is captured and attached to the coroutine. + * This option can be useful during local debug sessions, but is recommended + * to be disabled in production environments to avoid stack trace dumping overhead. + */ + var enableCreationStackTraces: Boolean get set + + /** + * Determines whether debug probes were [installed][DebugProbes.install]. + */ + val isInstalled: Boolean get + + /** + * Installs a [DebugProbes] instead of no-op stdlib probes by redefining + * debug probes class using the same class loader as one loaded [DebugProbes] class. + */ + fun install() + + /** + * Uninstall debug probes. + */ + fun uninstall() + + /** + * Invokes given block of code with installed debug probes and uninstall probes in the end. + */ + inline fun withDebugProbes(block: () -> Unit) + + /** + * Returns string representation of the coroutines [job] hierarchy with additional debug information. + * Hierarchy is printed from the [job] as a root transitively to all children. + */ + fun jobToString(job: Job): String + + /** + * Returns string representation of all coroutines launched within the given [scope]. + * Throws [IllegalStateException] if the scope has no a job in it. + */ + fun scopeToString(scope: CoroutineScope): String + + /** + * Prints [job] hierarchy representation from [jobToString] to the given [out]. + */ + public fun printJob(job: Job): Unit + + /** + * Prints all coroutines launched within the given [scope]. + * Throws [IllegalStateException] if the scope has no a job in it. + */ + public fun printScope(scope: CoroutineScope): Unit + + /** + * Returns all existing coroutines info. + * The resulting collection represents a consistent snapshot of all existing coroutines at the moment of invocation. + */ +// public fun dumpCoroutinesInfo(): List + + /** + * Dumps all active coroutines into the given output stream, providing a consistent snapshot of all existing coroutines at the moment of invocation. + * The output of this method is similar to `jstack` or a full thread dump. It can be used as the replacement to + * "Dump threads" action. + * + * Example of the output: + * ``` + * Coroutines dump 2018/11/12 19:45:14 + * + * Coroutine "coroutine#42":StandaloneCoroutine{Active}@58fdd99, state: SUSPENDED + * at MyClass$awaitData.invokeSuspend(MyClass.kt:37) + * (Coroutine creation stacktrace) + * at MyClass.createIoRequest(MyClass.kt:142) + * at MyClass.fetchData(MyClass.kt:154) + * at MyClass.showData(MyClass.kt:31) + * ... + * ``` + */ + fun dumpCoroutines(): Unit +} diff --git a/mirai-core/src/commonTest/kotlin/utils/FileSystemTest.kt b/mirai-core/src/commonTest/kotlin/utils/FileSystemTest.kt index 5defec88a..8824fc0f1 100644 --- a/mirai-core/src/commonTest/kotlin/utils/FileSystemTest.kt +++ b/mirai-core/src/commonTest/kotlin/utils/FileSystemTest.kt @@ -1,17 +1,17 @@ /* - * Copyright 2019-2021 Mamoe Technologies and contributors. + * Copyright 2019-2022 Mamoe Technologies and contributors. * - * 此源代码的使用受 GNU AFFERO GENERAL PUBLIC LICENSE version 3 许可证的约束, 可以在以下链接找到该许可证. - * Use of this source code is governed by the GNU AGPLv3 license that can be found through the following link. + * 此源代码的使用受 GNU AFFERO GENERAL PUBLIC LICENSE version 3 许可证的约束, 可以在以下链接找到该许可证. + * Use of this source code is governed by the GNU AGPLv3 license that can be found through the following link. * - * https://github.com/mamoe/mirai/blob/master/LICENSE + * https://github.com/mamoe/mirai/blob/dev/LICENSE */ package net.mamoe.mirai.internal.utils import net.mamoe.mirai.internal.test.AbstractTest -import org.junit.jupiter.api.Test +import kotlin.test.Test import kotlin.test.assertEquals import kotlin.test.assertFailsWith diff --git a/mirai-core/src/commonTest/kotlin/utils/io/serialization/ReadJceStructTest.kt b/mirai-core/src/commonTest/kotlin/utils/io/serialization/ReadJceStructTest.kt index cf9d5458e..68f323ab8 100644 --- a/mirai-core/src/commonTest/kotlin/utils/io/serialization/ReadJceStructTest.kt +++ b/mirai-core/src/commonTest/kotlin/utils/io/serialization/ReadJceStructTest.kt @@ -14,7 +14,7 @@ import net.mamoe.mirai.internal.network.protocol.data.jce.RequestPacket import net.mamoe.mirai.internal.test.AbstractTest import net.mamoe.mirai.utils.hexToBytes import net.mamoe.mirai.utils.read -import org.junit.jupiter.api.Test +import kotlin.test.Test internal class ReadJceStructTest : AbstractTest() { diff --git a/mirai-core/src/commonTest/kotlin/utils/io/serialization/tars/internal/DebugLoggerTest.kt b/mirai-core/src/commonTest/kotlin/utils/io/serialization/tars/internal/DebugLoggerTest.kt index 6b91d44e0..5b9ab161f 100644 --- a/mirai-core/src/commonTest/kotlin/utils/io/serialization/tars/internal/DebugLoggerTest.kt +++ b/mirai-core/src/commonTest/kotlin/utils/io/serialization/tars/internal/DebugLoggerTest.kt @@ -9,6 +9,7 @@ package net.mamoe.mirai.internal.utils.io.serialization.tars.internal +import io.ktor.utils.io.core.* import kotlinx.serialization.Serializable import net.mamoe.mirai.internal.test.AbstractTest import net.mamoe.mirai.internal.utils.io.JceStruct @@ -18,13 +19,11 @@ import net.mamoe.mirai.internal.utils.io.serialization.tars.TarsId import net.mamoe.mirai.internal.utils.io.serialization.toByteArray import net.mamoe.mirai.utils.toReadPacket import net.mamoe.mirai.utils.toUHexString -import org.junit.jupiter.api.Test -import java.io.ByteArrayOutputStream -import java.io.PrintStream +import kotlin.test.Test import kotlin.test.assertEquals import kotlin.test.assertFails -class DebugLoggerTest : AbstractTest() { +internal class DebugLoggerTest : AbstractTest() { fun String.uniteLine(): String = replace("\r\n", "\n").replace("\r", "\n") @@ -36,8 +35,8 @@ class DebugLoggerTest : AbstractTest() { @Test fun `can log`() { - val out = ByteArrayOutputStream() - val logger = DebugLogger(PrintStream(out)) + val out = BytePacketBuilder() + val logger = DebugLogger(out) val original = Struct("string", 1) val bytes = original.toByteArray(Struct.serializer()) val value = bytes.toReadPacket().use { Tars.UTF_8.load(Struct.serializer(), it, logger) } @@ -51,7 +50,7 @@ class DebugLoggerTest : AbstractTest() { name=int decodeElementIndex: currentHead == null endStructure: net.mamoe.mirai.internal.utils.io.serialization.tars.internal.DebugLoggerTest.Struct, null, null - """.trimIndent(), out.toByteArray().decodeToString().trim().uniteLine() + """.trimIndent(), out.build().readBytes().decodeToString().trim().uniteLine() ) } diff --git a/mirai-core/src/jvmBaseMain/kotlin/BotAccount.kt b/mirai-core/src/jvmBaseMain/kotlin/BotAccount.kt new file mode 100644 index 000000000..204a34966 --- /dev/null +++ b/mirai-core/src/jvmBaseMain/kotlin/BotAccount.kt @@ -0,0 +1,63 @@ +/* + * Copyright 2019-2022 Mamoe Technologies and contributors. + * + * 此源代码的使用受 GNU AFFERO GENERAL PUBLIC LICENSE version 3 许可证的约束, 可以在以下链接找到该许可证. + * Use of this source code is governed by the GNU AGPLv3 license that can be found through the following link. + * + * https://github.com/mamoe/mirai/blob/dev/LICENSE + */ + +package net.mamoe.mirai.internal + +import net.mamoe.mirai.utils.* +import java.nio.ByteBuffer + +internal actual data class BotAccount( + internal actual val id: Long, + + val passwordMd5Buffer: ByteBuffer, // md5 + + actual val phoneNumber: String = "" +) { + init { + check(passwordMd5Buffer.remaining == 16) { + "Invalid passwordMd5: size must be 16 but got ${passwordMd5Buffer.remaining}. passwordMd5=${passwordMd5.toUHexString()}" + } + } + + actual constructor(id: Long, passwordMd5: ByteArray, phoneNumber: String) : this( + id, SecretsProtection.escape(passwordMd5), phoneNumber + ) + + actual constructor(id: Long, passwordPlainText: String, phoneNumber: String) : this( + id, + passwordPlainText.md5(), + phoneNumber + ) { + require(passwordPlainText.length <= 16) { "Password length must be at most 16." } + } + + actual val passwordMd5: ByteArray + get() { + return passwordMd5Buffer.duplicate().readBytes() + } + + actual override fun equals(other: Any?): Boolean { + if (this === other) return true + if (other == null || this::class != other::class) return false + + other as BotAccount + + if (id != other.id) return false + if (passwordMd5Buffer != other.passwordMd5Buffer) return false + + return true + } + + + actual override fun hashCode(): Int { + var result = id.hashCode() + result = 31 * result + passwordMd5Buffer.hashCode() + return result + } +} \ No newline at end of file diff --git a/mirai-core/src/jvmBaseMain/kotlin/contact/announcement/AnnouncementsImpl.kt b/mirai-core/src/jvmBaseMain/kotlin/contact/announcement/AnnouncementsImpl.kt new file mode 100644 index 000000000..faa2a4646 --- /dev/null +++ b/mirai-core/src/jvmBaseMain/kotlin/contact/announcement/AnnouncementsImpl.kt @@ -0,0 +1,37 @@ +/* + * Copyright 2019-2022 Mamoe Technologies and contributors. + * + * 此源代码的使用受 GNU AFFERO GENERAL PUBLIC LICENSE version 3 许可证的约束, 可以在以下链接找到该许可证. + * Use of this source code is governed by the GNU AGPLv3 license that can be found through the following link. + * + * https://github.com/mamoe/mirai/blob/dev/LICENSE + */ + +package net.mamoe.mirai.internal.contact.announcement + +import kotlinx.coroutines.runBlocking +import net.mamoe.mirai.contact.announcement.OnlineAnnouncement +import net.mamoe.mirai.internal.contact.GroupImpl +import net.mamoe.mirai.internal.contact.announcement.AnnouncementProtocol.toAnnouncement +import net.mamoe.mirai.utils.MiraiLogger +import net.mamoe.mirai.utils.stream +import java.util.stream.Stream + +internal actual class AnnouncementsImpl actual constructor( + group: GroupImpl, + logger: MiraiLogger, +) : CommonAnnouncementsImpl(group, logger) { + override fun asStream(): Stream { + return stream { + var i = 1 + while (true) { + val result = runBlocking { getGroupAnnouncementList(i++) } ?: break + + if (result.inst.isNullOrEmpty() && result.feeds.isNullOrEmpty()) break + + result.inst?.let { yieldAll(it) } + result.feeds?.let { yieldAll(it) } + } + }.map { it.toAnnouncement(group) } + } +} diff --git a/mirai-core/src/jvmBaseMain/kotlin/contact/file/AbsoluteFolderImpl.kt b/mirai-core/src/jvmBaseMain/kotlin/contact/file/AbsoluteFolderImpl.kt new file mode 100644 index 000000000..147b19c99 --- /dev/null +++ b/mirai-core/src/jvmBaseMain/kotlin/contact/file/AbsoluteFolderImpl.kt @@ -0,0 +1,103 @@ +/* + * Copyright 2019-2022 Mamoe Technologies and contributors. + * + * 此源代码的使用受 GNU AFFERO GENERAL PUBLIC LICENSE version 3 许可证的约束, 可以在以下链接找到该许可证. + * Use of this source code is governed by the GNU AGPLv3 license that can be found through the following link. + * + * https://github.com/mamoe/mirai/blob/dev/LICENSE + */ + +package net.mamoe.mirai.internal.contact.file + +import kotlinx.coroutines.runBlocking +import net.mamoe.mirai.contact.FileSupported +import net.mamoe.mirai.contact.file.AbsoluteFile +import net.mamoe.mirai.contact.file.AbsoluteFileFolder +import net.mamoe.mirai.contact.file.AbsoluteFolder +import net.mamoe.mirai.internal.network.protocol.data.proto.Oidb0x6d8 +import net.mamoe.mirai.internal.network.protocol.packet.chat.FileManagement +import net.mamoe.mirai.internal.network.protocol.packet.chat.toResult +import net.mamoe.mirai.internal.utils.FileSystem +import net.mamoe.mirai.utils.JavaFriendlyAPI +import java.util.stream.Stream +import kotlin.streams.asStream + +internal actual class AbsoluteFolderImpl actual constructor( + contact: FileSupported, parent: AbsoluteFolder?, id: String, name: String, + uploadTime: Long, uploaderId: Long, lastModifiedTime: Long, + contentsCount: Int, +) : CommonAbsoluteFolderImpl(contact, parent, id, name, uploadTime, uploaderId, lastModifiedTime, contentsCount) { + + @JavaFriendlyAPI + override suspend fun foldersStream(): Stream { + return getItemsSequence().filter { it.folderInfo != null }.map { it.resolve() as AbsoluteFolder }.asStream() + } + + @JavaFriendlyAPI + override suspend fun filesStream(): Stream { + return getItemsSequence().filter { it.fileInfo != null }.map { it.resolve() as AbsoluteFile }.asStream() + } + + @JavaFriendlyAPI + override suspend fun childrenStream(): Stream { + return getItemsSequence().mapNotNull { it.resolve() }.asStream() + } + + @JavaFriendlyAPI + private suspend fun getItemsSequence(): Sequence { + return sequence { + var index = 0 + while (true) { + val list = runBlocking { + bot.network.sendAndExpect( + FileManagement.GetFileList( + client, + groupCode = contact.id, + folderId = id, + startIndex = index + ) + ) + }.toResult("AbsoluteFolderImpl.getFilesFlow").getOrThrow() + index += list.itemList.size + + if (list.int32RetCode != 0) return@sequence + if (list.itemList.isEmpty()) return@sequence + + yieldAll(list.itemList) + } + } + } + + @OptIn(JavaFriendlyAPI::class) + override suspend fun resolveFilesStream(path: String): Stream { + if (path.isBlank()) throw IllegalArgumentException("path cannot be blank.") + if (!FileSystem.isLegal(path)) return Stream.empty() + + if (path[0] == '/') { + return root.resolveFilesStream(path.substring(1)) + } + + if (!path.contains('/')) { + return getItemsSequence() + .filter { it.fileInfo?.fileName == path } + .map { it.resolve() as AbsoluteFile } + .asStream() + } + + return resolveFolder(path.substringBefore('/'))?.resolveFilesStream(path.substringAfter('/')) ?: Stream.empty() + } + + @JavaFriendlyAPI + override suspend fun resolveAllStream(path: String): Stream { + if (path.isBlank()) throw IllegalArgumentException("path cannot be blank.") + if (!FileSystem.isLegal(path)) return Stream.empty() + if (path[0] == '/') { + return root.resolveAllStream(path.substring(1)) + } + if (!path.contains('/')) { + return getItemsSequence().mapNotNull { it.resolve() }.asStream() + } + + return resolveFolder(path.substringBefore('/'))?.resolveAllStream(path.substringAfter('/')) ?: Stream.empty() + } +} diff --git a/mirai-core/src/jvmBaseMain/kotlin/contact/roaming/RoamingMessagesImpl.kt b/mirai-core/src/jvmBaseMain/kotlin/contact/roaming/RoamingMessagesImpl.kt new file mode 100644 index 000000000..febac3384 --- /dev/null +++ b/mirai-core/src/jvmBaseMain/kotlin/contact/roaming/RoamingMessagesImpl.kt @@ -0,0 +1,51 @@ +/* + * Copyright 2019-2022 Mamoe Technologies and contributors. + * + * 此源代码的使用受 GNU AFFERO GENERAL PUBLIC LICENSE version 3 许可证的约束, 可以在以下链接找到该许可证. + * Use of this source code is governed by the GNU AGPLv3 license that can be found through the following link. + * + * https://github.com/mamoe/mirai/blob/dev/LICENSE + */ + +package net.mamoe.mirai.internal.contact.roaming + +import kotlinx.coroutines.runBlocking +import net.mamoe.mirai.contact.roaming.RoamingMessageFilter +import net.mamoe.mirai.internal.message.toMessageChainOnline +import net.mamoe.mirai.message.data.MessageChain +import net.mamoe.mirai.utils.JavaFriendlyAPI +import net.mamoe.mirai.utils.stream +import java.util.stream.Stream + +internal actual sealed class RoamingMessagesImpl : CommonRoamingMessagesImpl() { + @JavaFriendlyAPI + override suspend fun getMessagesStream( + timeStart: Long, + timeEnd: Long, + filter: RoamingMessageFilter?, + ): Stream { + return stream { + var lastMessageTime = timeEnd + var random = 0L + while (true) { + val resp = runBlocking { + requestRoamMsg(timeStart, lastMessageTime, random) + } + + val messages = resp.messages ?: break + if (filter == null || filter === RoamingMessageFilter.ANY) { + messages.forEach { yield(runBlocking { it.toMessageChainOnline(contact.bot) }) } + } else { + for (message in messages) { + if (filter.invoke(createRoamingMessage(message, messages))) { + yield(runBlocking { message.toMessageChainOnline(contact.bot) }) + } + } + } + + lastMessageTime = resp.lastMessageTime + random = resp.random + } + } + } +} \ No newline at end of file diff --git a/mirai-core/src/jvmBaseMain/kotlin/message/protocol/impl/TextProtocol.kt b/mirai-core/src/jvmBaseMain/kotlin/message/protocol/impl/TextProtocol.kt new file mode 100644 index 000000000..4fad9506c --- /dev/null +++ b/mirai-core/src/jvmBaseMain/kotlin/message/protocol/impl/TextProtocol.kt @@ -0,0 +1,15 @@ +/* + * Copyright 2019-2022 Mamoe Technologies and contributors. + * + * 此源代码的使用受 GNU AFFERO GENERAL PUBLIC LICENSE version 3 许可证的约束, 可以在以下链接找到该许可证. + * Use of this source code is governed by the GNU AGPLv3 license that can be found through the following link. + * + * https://github.com/mamoe/mirai/blob/dev/LICENSE + */ + +package net.mamoe.mirai.internal.message.protocol.impl + +internal actual fun getEmojiPatternResourceOrNull(): String? { + return TextProtocol::class.java.classLoader.getResourceAsStream("emoji-pattern.regex") + ?.use { it.readBytes().decodeToString() } +} \ No newline at end of file diff --git a/mirai-core/src/jvmBaseMain/kotlin/network/component/ComponentKey.kt b/mirai-core/src/jvmBaseMain/kotlin/network/component/ComponentKey.kt new file mode 100644 index 000000000..8ac970633 --- /dev/null +++ b/mirai-core/src/jvmBaseMain/kotlin/network/component/ComponentKey.kt @@ -0,0 +1,18 @@ +/* + * Copyright 2019-2022 Mamoe Technologies and contributors. + * + * 此源代码的使用受 GNU AFFERO GENERAL PUBLIC LICENSE version 3 许可证的约束, 可以在以下链接找到该许可证. + * Use of this source code is governed by the GNU AGPLv3 license that can be found through the following link. + * + * https://github.com/mamoe/mirai/blob/dev/LICENSE + */ + +package net.mamoe.mirai.internal.network.component + +import kotlin.reflect.KTypeProjection +import kotlin.reflect.full.allSupertypes + +internal actual fun ComponentKey<*>.getComponentTypeArgument(): KTypeProjection? { + val thisType = this::class.allSupertypes.find { it.classifier == ComponentKey::class } + return thisType?.arguments?.firstOrNull() +} \ No newline at end of file diff --git a/mirai-core/src/jvmBaseMain/kotlin/network/handler/NetworkHandlerFactory.kt b/mirai-core/src/jvmBaseMain/kotlin/network/handler/NetworkHandlerFactory.kt new file mode 100644 index 000000000..203517126 --- /dev/null +++ b/mirai-core/src/jvmBaseMain/kotlin/network/handler/NetworkHandlerFactory.kt @@ -0,0 +1,35 @@ +/* + * Copyright 2019-2022 Mamoe Technologies and contributors. + * + * 此源代码的使用受 GNU AFFERO GENERAL PUBLIC LICENSE version 3 许可证的约束, 可以在以下链接找到该许可证. + * Use of this source code is governed by the GNU AGPLv3 license that can be found through the following link. + * + * https://github.com/mamoe/mirai/blob/dev/LICENSE + */ + +package net.mamoe.mirai.internal.network.handler + +import net.mamoe.mirai.internal.network.handler.NetworkHandler.State +import net.mamoe.mirai.internal.network.impl.netty.NettyNetworkHandlerFactory +import java.net.InetAddress +import java.net.InetSocketAddress + +/** + * Factory for a specific [NetworkHandler] implementation. + */ +internal actual fun interface NetworkHandlerFactory { + actual fun create(context: NetworkHandlerContext, host: String, port: Int): H = + create(context, InetSocketAddress.createUnresolved(host, port)) + + fun create(context: NetworkHandlerContext, host: InetAddress, port: Int): H = + create(context, InetSocketAddress(host, port)) + + /** + * Create an instance of [H]. The returning [H] has [NetworkHandler.state] of [State.INITIALIZED] + */ + actual fun create(context: NetworkHandlerContext, address: SocketAddress): H + + actual companion object { + actual fun getPlatformDefault(): NetworkHandlerFactory<*> = NettyNetworkHandlerFactory + } +} diff --git a/mirai-core/src/jvmBaseMain/kotlin/network/handler/SocketAddress.kt b/mirai-core/src/jvmBaseMain/kotlin/network/handler/SocketAddress.kt new file mode 100644 index 000000000..ae732d448 --- /dev/null +++ b/mirai-core/src/jvmBaseMain/kotlin/network/handler/SocketAddress.kt @@ -0,0 +1,19 @@ +/* + * Copyright 2019-2022 Mamoe Technologies and contributors. + * + * 此源代码的使用受 GNU AFFERO GENERAL PUBLIC LICENSE version 3 许可证的约束, 可以在以下链接找到该许可证. + * Use of this source code is governed by the GNU AGPLv3 license that can be found through the following link. + * + * https://github.com/mamoe/mirai/blob/dev/LICENSE + */ + +package net.mamoe.mirai.internal.network.handler + +import java.net.InetSocketAddress + +@Suppress("ACTUAL_WITHOUT_EXPECT") // visibility +internal actual typealias SocketAddress = java.net.SocketAddress + +internal actual fun SocketAddress(host: String, port: Int): SocketAddress { + return InetSocketAddress.createUnresolved(host, port) +} \ No newline at end of file diff --git a/mirai-core/src/jvmBaseMain/kotlin/network/impl/netty/NettyChannelException.kt b/mirai-core/src/jvmBaseMain/kotlin/network/impl/netty/NettyChannelException.kt index 5f3820cdb..ca06e22e6 100644 --- a/mirai-core/src/jvmBaseMain/kotlin/network/impl/netty/NettyChannelException.kt +++ b/mirai-core/src/jvmBaseMain/kotlin/network/impl/netty/NettyChannelException.kt @@ -9,9 +9,9 @@ package net.mamoe.mirai.internal.network.impl.netty -import net.mamoe.mirai.internal.network.handler.selector.NetworkException +import net.mamoe.mirai.internal.network.handler.selector.NetworkChannelException internal data class NettyChannelException( override val message: String? = null, override val cause: Throwable? = null, -) : NetworkException(true) \ No newline at end of file +) : NetworkChannelException() \ No newline at end of file diff --git a/mirai-core/src/jvmBaseMain/kotlin/network/impl/netty/NettyNetworkHandler.kt b/mirai-core/src/jvmBaseMain/kotlin/network/impl/netty/NettyNetworkHandler.kt index 96431a04a..fe7c2d44b 100644 --- a/mirai-core/src/jvmBaseMain/kotlin/network/impl/netty/NettyNetworkHandler.kt +++ b/mirai-core/src/jvmBaseMain/kotlin/network/impl/netty/NettyNetworkHandler.kt @@ -17,34 +17,24 @@ import io.netty.channel.socket.SocketChannel import io.netty.channel.socket.nio.NioSocketChannel import io.netty.handler.codec.LengthFieldBasedFrameDecoder import io.netty.handler.codec.MessageToByteEncoder -import kotlinx.coroutines.* -import net.mamoe.mirai.internal.network.components.* +import kotlinx.coroutines.CompletableDeferred +import kotlinx.coroutines.asCoroutineDispatcher +import kotlinx.coroutines.job +import net.mamoe.mirai.internal.network.components.PacketCodec +import net.mamoe.mirai.internal.network.components.RawIncomingPacket +import net.mamoe.mirai.internal.network.components.SsoProcessor +import net.mamoe.mirai.internal.network.handler.CommonNetworkHandler import net.mamoe.mirai.internal.network.handler.NetworkHandler.State import net.mamoe.mirai.internal.network.handler.NetworkHandlerContext -import net.mamoe.mirai.internal.network.handler.NetworkHandlerSupport -import net.mamoe.mirai.internal.network.handler.selector.NetworkException -import net.mamoe.mirai.internal.network.handler.selector.NetworkHandlerSelector -import net.mamoe.mirai.internal.network.handler.state.StateObserver import net.mamoe.mirai.internal.network.protocol.packet.OutgoingPacket -import net.mamoe.mirai.utils.* -import java.io.EOFException +import net.mamoe.mirai.utils.debug import java.net.SocketAddress -import kotlin.coroutines.CoroutineContext import io.netty.channel.Channel as NettyChannel internal open class NettyNetworkHandler( context: NetworkHandlerContext, - private val address: SocketAddress, -) : NetworkHandlerSupport(context) { - final override tailrec suspend fun sendPacketImpl(packet: OutgoingPacket) { - val state = _state as NettyState - if (state.sendPacketImpl(packet)) return - - // now the state it not yet ready for sending packet ... - stateChannel.receive() // [SUSPENSION POINT] so we wait for next state ... - return sendPacketImpl(packet) // and try again. - } - + address: SocketAddress, +) : CommonNetworkHandler(context, address) { override fun toString(): String { return "NettyNetworkHandler(context=$context, address=$address)" } @@ -53,29 +43,6 @@ internal open class NettyNetworkHandler( /////////////////////////////////////////////////////////////////////////// // exception handling /////////////////////////////////////////////////////////////////////////// - protected open fun handleExceptionInDecoding(error: Throwable) { - fun passToExceptionHandler() { - // Typically, just log the exception - coroutineContext[CoroutineExceptionHandler]!!.handleException( - coroutineContext, - ExceptionInPacketCodecException(error.unwrap()) - ) - } - - if (error is PacketCodecException) { - if (error.targetException is EOFException) return - when (error.kind) { - PacketCodecException.Kind.SESSION_EXPIRED -> { - setState { StateClosed(error) } - return - } - PacketCodecException.Kind.PROTOCOL_UPDATED -> passToExceptionHandler() - PacketCodecException.Kind.OTHER -> passToExceptionHandler() - } - } - - passToExceptionHandler() - } protected open fun handlePipelineException(ctx: ChannelHandlerContext, error: Throwable) { setState { StateClosed(NettyChannelException(cause = error)) } @@ -131,7 +98,7 @@ internal open class NettyNetworkHandler( protected open fun createDummyDecodePipeline() = PacketDecodePipeline(this@NettyNetworkHandler.coroutineContext) // can be overridden for tests - protected open suspend fun createConnection(): NettyChannel { + override suspend fun createConnection(): NettyChannel { packetLogger.debug { "Connecting to $address" } val contextResult = CompletableDeferred() @@ -180,237 +147,14 @@ internal open class NettyNetworkHandler( return contextResult.await() } - protected inner class PacketDecodePipeline(parentContext: CoroutineContext) : - CoroutineScope by parentContext.childScope() { - private val packetCodec: PacketCodec by lazy { context[PacketCodec] } - - fun send(raw: RawIncomingPacket) { - launch { - val result = packetCodec.processBody(context.bot, raw) - if (result == null) { - collectUnknownPacket(raw) - } else collectReceived(result) - } - } + @Suppress("EXTENSION_SHADOWED_BY_MEMBER") + override fun io.netty.channel.Channel.close() { + this.close() } - - /////////////////////////////////////////////////////////////////////////// - // states - /////////////////////////////////////////////////////////////////////////// - - override fun close(cause: Throwable?) { - if (state == State.CLOSED) return // quick check if already closed - if (setState { StateClosed(cause) } == null) return // atomic check - super.close(cause) // cancel coroutine scope + override fun NettyChannel.writeAndFlushOrCloseAsync(packet: OutgoingPacket) { + writeAndFlush(packet) + .addListener(ChannelFutureListener.FIRE_EXCEPTION_ON_FAILURE) + .addListener(ChannelFutureListener.CLOSE_ON_FAILURE) } - - init { - coroutineContext.job.invokeOnCompletion { e -> - close(e?.unwrapCancellationException()) - } - } - - /** - * When state is initialized, it must be set to [_state]. (inside [setState]) - * - * For what jobs each state will do, it is not solely decided by the state itself. [StateObserver]s may also launch jobs into the scope. - * - * @see StateObserver - */ - protected abstract inner class NettyState( - correspondingState: State, - ) : BaseStateImpl(correspondingState) { - /** - * @return `true` if packet has been sent, `false` if state is not ready for send. - * @throws IllegalStateException if is [StateClosed]. - */ - abstract suspend fun sendPacketImpl(packet: OutgoingPacket): Boolean - } - - protected inner class StateInitialized : NettyState(State.INITIALIZED) { - override suspend fun sendPacketImpl(packet: OutgoingPacket): Boolean { - // error("Cannot send packet when connection is not set. (resumeConnection not called.)") - return false - } - - override suspend fun resumeConnection0() { - this.setState { StateConnecting(ExceptionCollector()) } - ?.resumeConnection() - ?: this@NettyNetworkHandler.resumeConnection() // concurrently closed by other thread. - } - - override fun toString(): String = "StateInitialized" - } - - /** - * 1. Connect to server. - * 2. Perform SSO login with [SsoProcessor] - * - * If failure, set state to [StateClosed] - * If success, set state to [StateOK] - */ - protected inner class StateConnecting( - /** - * Collected (suppressed) exceptions that have led this state. - * - * Dropped when state becomes [StateOK]. - */ - private val collectiveExceptions: ExceptionCollector, - ) : NettyState(State.CONNECTING) { - private lateinit var connection: Deferred - - @Suppress("JoinDeclarationAndAssignment") - private lateinit var connectResult: Deferred - - override fun startState() { - connection = async { - createConnection() - } - - connectResult = async { - connection.join() - context[SsoProcessor].login(this@NettyNetworkHandler) - } - connectResult.invokeOnCompletion { error -> - if (error == null) { - this@NettyNetworkHandler.launch { resumeConnection() } - } else { - // failed in SSO stage - context[SsoProcessor].firstLoginResult.compareAndSet(null, FirstLoginResult.OTHER_FAILURE) - - if (error is StateSwitchingException && error.new is StateConnecting) { - return@invokeOnCompletion // state already switched, so do not do it again. - } - setState { - // logon failure closes the network handler. - StateClosed(collectiveExceptions.collectGet(error)) - // The exception will be ignored unless all further attempts recovering connection have failed. - // This is to reduce useless logs for the user----there is nothing to worry about if we can recover the connection. - } - } - } - - } - - override fun getCause(): Throwable? = collectiveExceptions.getLast() - - override suspend fun sendPacketImpl(packet: OutgoingPacket): Boolean = runUnwrapCancellationException { - connection.await() // split line number - .writeAndFlushOrCloseAsync(packet) - return true - } - - override suspend fun resumeConnection0() = runUnwrapCancellationException { - connectResult.await() // propagates exceptions - val connection = connection.await() - this.setState { StateLoading(connection) } - ?.resumeConnection() - ?: this@NettyNetworkHandler.resumeConnection() // concurrently closed by other thread. - } - - override fun toString(): String = "StateConnecting" - } - - /** - * @see BotInitProcessor - * @see StateObserver - */ - protected inner class StateLoading( - private val connection: NettyChannel, - ) : NettyState(State.LOADING) { - - override fun startState() { - coroutineContext.job.invokeOnCompletion { - if (it != null) { - connection.close() - } - } - } - - override suspend fun sendPacketImpl(packet: OutgoingPacket): Boolean { - connection.writeAndFlushOrCloseAsync(packet) - return true - } - - private val configPush = this@NettyNetworkHandler.launch(CoroutineName("ConfigPush sync")) { - context[ConfigPushProcessor].syncConfigPush(this@NettyNetworkHandler) - } - - override suspend fun resumeConnection0(): Unit = runUnwrapCancellationException { - (coroutineContext.job as CompletableJob).run { - complete() - join() - } - joinCompleted(configPush) // throw exception - setState { StateOK(connection, configPush) } - } // noop - - override fun toString(): String = "StateLoading" - } - - protected inner class StateOK( - private val connection: NettyChannel, - private val configPush: Job, - ) : NettyState(State.OK) { - override fun startState() { - coroutineContext.job.invokeOnCompletion { err -> - if (err is StateSwitchingException) { - if (err.new.correspondingState == State.CLOSED) { - return@invokeOnCompletion - } - } - connection.close() - } - } - - private val heartbeatJobs = - context[HeartbeatScheduler].launchJobsIn(this@NettyNetworkHandler, this) { name, e -> - setState { StateClosed(HeartbeatFailedException(name, e)) } - } - - // we can also move them as observers if needed. - - private val keyRefresh = launch(CoroutineName("Key refresh")) { - context[KeyRefreshProcessor].keyRefreshLoop(this@NettyNetworkHandler) - } - - override suspend fun sendPacketImpl(packet: OutgoingPacket): Boolean { - connection.writeAndFlushOrCloseAsync(packet) - return true - } - - override suspend fun resumeConnection0(): Unit = runUnwrapCancellationException { - joinCompleted(coroutineContext.job) - for (job in heartbeatJobs) joinCompleted(job) - joinCompleted(configPush) - joinCompleted(keyRefresh) - } // noop - - override fun toString(): String = "StateOK" - } - - /** - * 这会永久关闭这个 [NettyNetworkHandler], 但通常 bot 会使用 [NetworkHandlerSelector], selector 会创建新的 [NettyNetworkHandler] 来恢复连接. - * - * 备注: selector 会恢复连接, 当且仅当 [exception] 类型是 [NetworkException] 且 [NetworkException.recoverable] 为 `true`. - */ - protected inner class StateClosed( - val exception: Throwable?, - ) : NettyState(State.CLOSED) { - - override fun afterUpdated() { - close(exception) - } - - override fun getCause(): Throwable? = exception - override suspend fun sendPacketImpl(packet: OutgoingPacket) = error("NetworkHandler is already closed.") - override suspend fun resumeConnection0() { - exception?.let { throw it } - } // noop - - override fun toString(): String = "StateClosed" - } - - override fun initialState(): BaseStateImpl = StateInitialized() } diff --git a/mirai-core/src/jvmBaseMain/kotlin/network/impl/netty/nettyUtils.kt b/mirai-core/src/jvmBaseMain/kotlin/network/impl/netty/nettyUtils.kt index 4e650d97a..82d0d0307 100644 --- a/mirai-core/src/jvmBaseMain/kotlin/network/impl/netty/nettyUtils.kt +++ b/mirai-core/src/jvmBaseMain/kotlin/network/impl/netty/nettyUtils.kt @@ -14,15 +14,7 @@ import io.ktor.utils.io.streams.* import io.netty.buffer.ByteBuf import io.netty.buffer.ByteBufInputStream import io.netty.channel.ChannelFuture -import io.netty.channel.ChannelFutureListener -import io.netty.channel.ChannelOutboundInvoker -import kotlinx.coroutines.CoroutineExceptionHandler -import kotlinx.coroutines.CoroutineName -import kotlinx.coroutines.Job import kotlinx.coroutines.suspendCancellableCoroutine -import net.mamoe.mirai.utils.MiraiLogger -import net.mamoe.mirai.utils.SimpleLogger -import net.mamoe.mirai.utils.SimpleLogger.LogPriority.ERROR import net.mamoe.mirai.utils.withUse @@ -48,27 +40,3 @@ internal fun ByteBuf.toReadPacket(): ByteReadPacket { ByteBufInputStream(buf).withUse { copyTo(outputStream()) } } } - - -internal fun MiraiLogger.asCoroutineExceptionHandler( - priority: SimpleLogger.LogPriority = ERROR, -): CoroutineExceptionHandler { - return CoroutineExceptionHandler { context, e -> - call( - priority, - context[CoroutineName]?.let { "Exception in coroutine '${it.name}'." } ?: "Exception in unnamed coroutine.", - e - ) - } -} - -internal fun ChannelOutboundInvoker.writeAndFlushOrCloseAsync(msg: Any?): ChannelFuture? { - return writeAndFlush(msg) - .addListener(ChannelFutureListener.FIRE_EXCEPTION_ON_FAILURE) - .addListener(ChannelFutureListener.CLOSE_ON_FAILURE) -} - - -internal suspend inline fun joinCompleted(job: Job) { - if (job.isCompleted) job.join() -} diff --git a/mirai-core/src/jvmBaseMain/kotlin/network/protocol/data/richstatus/RichStatus.kt b/mirai-core/src/jvmBaseMain/kotlin/network/protocol/data/richstatus/RichStatus.kt new file mode 100644 index 000000000..eb3e219e5 --- /dev/null +++ b/mirai-core/src/jvmBaseMain/kotlin/network/protocol/data/richstatus/RichStatus.kt @@ -0,0 +1,139 @@ +/* + * Copyright 2019-2022 Mamoe Technologies and contributors. + * + * 此源代码的使用受 GNU AFFERO GENERAL PUBLIC LICENSE version 3 许可证的约束, 可以在以下链接找到该许可证. + * Use of this source code is governed by the GNU AGPLv3 license that can be found through the following link. + * + * https://github.com/mamoe/mirai/blob/dev/LICENSE + */ + +package net.mamoe.mirai.internal.network.protocol.data.richstatus + +import net.mamoe.mirai.utils.pos +import net.mamoe.mirai.utils.toIntUnsigned +import java.nio.ByteBuffer +import java.nio.ByteOrder + +@Suppress("UsePropertyAccessSyntax") +internal actual fun parseRichStatusImpl(rawData: ByteArray?) : RichStatus { + val rsp = RichStatus() + + if (rawData == null || rawData.size <= 2) return rsp + + val byteBuffer = ByteBuffer.wrap(rawData).order(ByteOrder.BIG_ENDIAN) + + var lastPosition = 0 + var lastStringData: String? = null + + while (byteBuffer.remaining() >= 2) { + val dataType = byteBuffer.get().toIntUnsigned() + val dataLength = byteBuffer.get().toIntUnsigned() + + if (byteBuffer.remaining() < dataLength) break + + val dataStartPosition = lastPosition + 2 + + // Origin: dataType > 0 && dataType < 128 + if (dataType in 1..127) { + val dataContent = String(rawData, dataStartPosition, dataLength) + lastPosition = dataStartPosition + dataLength + byteBuffer.pos = lastPosition + + when (dataType) { + 1 -> rsp.actionText = dataContent + 2 -> rsp.dataText = dataContent + 4 -> { + if (lastStringData != null) { + rsp.addPlainText(lastStringData) + lastStringData = null + } + if (rsp.plainText != null) { + rsp.locationPosition = rsp.plainText!!.size + } else { + rsp.locationPosition = 0 + } + rsp.locationText = dataContent + } + else -> { + if (lastStringData == null) { + lastStringData = dataContent + } else { + lastStringData += dataContent + } + } + } + } else { + run theSwitch@{ + when (dataType) { + 129 -> { + if (byteBuffer.remaining() >= 8) { + rsp.actionId = byteBuffer.getInt() + rsp.dataId = byteBuffer.getInt() + } + } + 130 -> { + if (byteBuffer.remaining() >= 8) { + rsp.lontitude = byteBuffer.getInt() + rsp.latitude = byteBuffer.getInt() + } + } + 144 -> rsp.feedsId = String(rawData, dataStartPosition, dataLength) + 145 -> rsp.tplId = byteBuffer.getInt() + 146 -> rsp.tplType = byteBuffer.getInt() + 147 -> rsp.actId = byteBuffer.getInt() + 148 -> { + if (byteBuffer.remaining() >= 4) { + lastPosition = byteBuffer.getInt() + /* + if (var1 > 4) { + var19 = String(var0, var5+4, var1-4) + if (var19.isNotEmpty()) { + var9.topics.add(Pair(var2, var19)) + } + } + */ + } + } + 149 -> { + if (byteBuffer.remaining() >= 5) { + lastPosition = dataLength + while (true) { + if (lastPosition < 5) return@theSwitch + + byteBuffer.getInt() + byteBuffer.get().toIntUnsigned() + + // var9.topicsPos.add(new Pair(var6, var3)); + lastPosition -= 5 + } + } + } + 161 -> { + /* + val var11 = ByteArray(dataLength) + byteBuffer.get(var11) + */ + byteBuffer.pos += dataLength + // Parse richstatus_sticker$RichStatus_Sticker + } + 162 -> { + rsp.fontId = byteBuffer.getInt() + } + 163 -> { + rsp.fontType = byteBuffer.getInt() + } + + } + } + lastPosition = dataStartPosition + dataLength + byteBuffer.pos = lastPosition + } + } + + if (lastStringData != null) { + rsp.addPlainText(lastStringData) + } + + return rsp + +} \ No newline at end of file diff --git a/mirai-core/src/jvmBaseMain/kotlin/utils/BotConfigurationExt.kt b/mirai-core/src/jvmBaseMain/kotlin/utils/BotConfigurationExt.kt new file mode 100644 index 000000000..f102e6ca4 --- /dev/null +++ b/mirai-core/src/jvmBaseMain/kotlin/utils/BotConfigurationExt.kt @@ -0,0 +1,18 @@ +/* + * Copyright 2019-2022 Mamoe Technologies and contributors. + * + * 此源代码的使用受 GNU AFFERO GENERAL PUBLIC LICENSE version 3 许可证的约束, 可以在以下链接找到该许可证. + * Use of this source code is governed by the GNU AGPLv3 license that can be found through the following link. + * + * https://github.com/mamoe/mirai/blob/dev/LICENSE + */ + +package net.mamoe.mirai.internal.utils + +import net.mamoe.mirai.utils.BotConfiguration + +internal actual val BotConfiguration.workingDirPath: String + get() = workingDir.absolutePath + +internal actual val BotConfiguration.cacheDirPath: String + get() = cacheDir.absolutePath \ No newline at end of file diff --git a/mirai-core/src/jvmBaseMain/kotlin/utils/PlatformSocket.kt b/mirai-core/src/jvmBaseMain/kotlin/utils/PlatformSocket.kt new file mode 100644 index 000000000..1909a9364 --- /dev/null +++ b/mirai-core/src/jvmBaseMain/kotlin/utils/PlatformSocket.kt @@ -0,0 +1,124 @@ +/* + * Copyright 2019-2022 Mamoe Technologies and contributors. + * + * 此源代码的使用受 GNU AFFERO GENERAL PUBLIC LICENSE version 3 许可证的约束, 可以在以下链接找到该许可证. + * Use of this source code is governed by the GNU AGPLv3 license that can be found through the following link. + * + * https://github.com/mamoe/mirai/blob/dev/LICENSE + */ + +package net.mamoe.mirai.internal.utils + +import io.ktor.utils.io.core.* +import io.ktor.utils.io.streams.* +import kotlinx.coroutines.Dispatchers +import kotlinx.coroutines.runInterruptible +import kotlinx.coroutines.suspendCancellableCoroutine +import net.mamoe.mirai.internal.network.highway.HighwayProtocolChannel +import java.io.BufferedInputStream +import java.io.BufferedOutputStream +import java.io.IOException +import java.net.Socket +import java.util.concurrent.Executors +import kotlin.contracts.InvocationKind +import kotlin.contracts.contract + +internal actual class PlatformSocket : Closeable, HighwayProtocolChannel { + private lateinit var socket: Socket + + actual val isOpen: Boolean + get() = + if (::socket.isInitialized) + socket.isConnected + else false + + actual override fun close() { + if (::socket.isInitialized) { + socket.close() + } + thread.shutdownNow() + kotlin.runCatching { writeChannel.close() } + kotlin.runCatching { readChannel.close() } + } + + @PublishedApi + internal lateinit var writeChannel: BufferedOutputStream + + @PublishedApi + internal lateinit var readChannel: BufferedInputStream + + actual suspend fun send(packet: ByteArray, offset: Int, length: Int) { + runInterruptible(Dispatchers.IO) { + writeChannel.write(packet, offset, length) + writeChannel.flush() + } + } + + /** + * @throws SendPacketInternalException + */ + actual override suspend fun send(packet: ByteReadPacket) { + runInterruptible(Dispatchers.IO) { + try { + writeChannel.writePacket(packet) + writeChannel.flush() + } catch (e: IOException) { + throw SendPacketInternalException(e) + } + } + } + + private val thread = Executors.newSingleThreadExecutor() + + /** + * @throws ReadPacketInternalException + */ + actual override suspend fun read(): ByteReadPacket = suspendCancellableCoroutine { cont -> + val task = thread.submit { + kotlin.runCatching { + readChannel.readPacketAtMost(Long.MAX_VALUE) + }.let { + cont.resumeWith(it) + } + } + cont.invokeOnCancellation { + kotlin.runCatching { task.cancel(true) } + } + } + + actual suspend fun connect(serverHost: String, serverPort: Int) { + runInterruptible(Dispatchers.IO) { + socket = Socket(serverHost, serverPort) + readChannel = socket.getInputStream().buffered() + writeChannel = socket.getOutputStream().buffered() + } + } + + actual companion object { + actual suspend fun connect( + serverIp: String, + serverPort: Int, + ): PlatformSocket { + val socket = PlatformSocket() + socket.connect(serverIp, serverPort) + return socket + } + + actual suspend inline fun withConnection( + serverIp: String, + serverPort: Int, + block: PlatformSocket.() -> R, + ): R { + contract { callsInPlace(block, InvocationKind.EXACTLY_ONCE) } + return connect(serverIp, serverPort).use(block) + } + } +} + + +@Suppress("ACTUAL_WITHOUT_EXPECT") +internal actual typealias SocketException = java.net.SocketException +@Suppress("ACTUAL_WITHOUT_EXPECT") +internal actual typealias NoRouteToHostException = java.net.NoRouteToHostException +@Suppress("ACTUAL_WITHOUT_EXPECT") +internal actual typealias UnknownHostException = java.net.UnknownHostException diff --git a/mirai-core/src/jvmBaseMain/kotlin/utils/RemoteFileImpl.kt b/mirai-core/src/jvmBaseMain/kotlin/utils/RemoteFileImpl.kt new file mode 100644 index 000000000..80be35a52 --- /dev/null +++ b/mirai-core/src/jvmBaseMain/kotlin/utils/RemoteFileImpl.kt @@ -0,0 +1,62 @@ +/* + * Copyright 2019-2022 Mamoe Technologies and contributors. + * + * 此源代码的使用受 GNU AFFERO GENERAL PUBLIC LICENSE version 3 许可证的约束, 可以在以下链接找到该许可证. + * Use of this source code is governed by the GNU AGPLv3 license that can be found through the following link. + * + * https://github.com/mamoe/mirai/blob/dev/LICENSE + */ + +@file:Suppress("DEPRECATION") + +package net.mamoe.mirai.internal.utils + +import net.mamoe.mirai.contact.Contact +import net.mamoe.mirai.contact.Group +import net.mamoe.mirai.message.MessageReceipt +import net.mamoe.mirai.message.data.FileMessage +import net.mamoe.mirai.utils.ExternalResource.Companion.toExternalResource +import net.mamoe.mirai.utils.RemoteFile +import java.io.File + +internal actual class RemoteFileImpl actual constructor( + contact: Group, + path: String +) : CommonRemoteFileImpl(contact, path), RemoteFile { + actual constructor(contact: Group, parent: String, name: String) : this(contact, FileSystem.normalize(parent, name)) + + // compiler bug + @Deprecated( + "Use uploadAndSend instead.", + replaceWith = ReplaceWith("this.uploadAndSend(file, callback)"), + level = DeprecationLevel.ERROR + ) + @Suppress("DEPRECATION_ERROR") + override suspend fun upload(file: File, callback: RemoteFile.ProgressionCallback?): FileMessage = + file.toExternalResource().use { upload(it, callback) } + + //compiler bug + @Deprecated( + "Use sendFile instead.", + replaceWith = ReplaceWith("this.uploadAndSend(file)"), + level = DeprecationLevel.ERROR + ) + @Suppress("DEPRECATION_ERROR") + override suspend fun upload(file: File): FileMessage { + // Dear compiler: + // + // Please generate invokeinterface. + // + // Yours Sincerely + // Him188 + return file.toExternalResource().use { upload(it) } + } + + // compiler bug + override suspend fun uploadAndSend(file: File): MessageReceipt = + file.toExternalResource().use { uploadAndSend(it) } + + // override suspend fun writeSession(resource: ExternalResource): FileUploadSession { + // } + +} \ No newline at end of file diff --git a/mirai-core/src/jvmBaseMain/kotlin/utils/crypto/ECDH.kt b/mirai-core/src/jvmBaseMain/kotlin/utils/crypto/ECDH.kt new file mode 100644 index 000000000..3df223055 --- /dev/null +++ b/mirai-core/src/jvmBaseMain/kotlin/utils/crypto/ECDH.kt @@ -0,0 +1,43 @@ +/* + * Copyright 2019-2022 Mamoe Technologies and contributors. + * + * 此源代码的使用受 GNU AFFERO GENERAL PUBLIC LICENSE version 3 许可证的约束, 可以在以下链接找到该许可证. + * Use of this source code is governed by the GNU AGPLv3 license that can be found through the following link. + * + * https://github.com/mamoe/mirai/blob/dev/LICENSE + */ + +@file:JvmName("ECDHKt_jvmBase") + +package net.mamoe.mirai.internal.utils.crypto + +import net.mamoe.mirai.utils.decodeBase64 +import java.security.KeyFactory +import java.security.KeyPair +import java.security.PrivateKey +import java.security.PublicKey +import java.security.spec.X509EncodedKeySpec + + + +@Suppress("ACTUAL_WITHOUT_EXPECT") +internal actual typealias ECDHPrivateKey = PrivateKey +@Suppress("ACTUAL_WITHOUT_EXPECT") +internal actual typealias ECDHPublicKey = PublicKey + +internal actual class ECDHKeyPairImpl( + private val delegate: KeyPair, + initialPublicKey: ECDHPublicKey = defaultInitialPublicKey.key +) : ECDHKeyPair { + override val privateKey: ECDHPrivateKey get() = delegate.private + override val publicKey: ECDHPublicKey get() = delegate.public + override val maskedPublicKey: ByteArray by lazy { publicKey.encoded.copyOfRange(26, 91) } + override val maskedShareKey: ByteArray by lazy { ECDH.calculateShareKey(privateKey, initialPublicKey) } +} + + +internal actual val publicKeyForVerify: ECDHPublicKey by lazy { + KeyFactory.getInstance("RSA") + .generatePublic(X509EncodedKeySpec("MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAuJTW4abQJXeVdAODw1CamZH4QJZChyT08ribet1Gp0wpSabIgyKFZAOxeArcCbknKyBrRY3FFI9HgY1AyItH8DOUe6ajDEb6c+vrgjgeCiOiCVyum4lI5Fmp38iHKH14xap6xGaXcBccdOZNzGT82sPDM2Oc6QYSZpfs8EO7TYT7KSB2gaHz99RQ4A/Lel1Vw0krk+DescN6TgRCaXjSGn268jD7lOO23x5JS1mavsUJtOZpXkK9GqCGSTCTbCwZhI33CpwdQ2EHLhiP5RaXZCio6lksu+d8sKTWU1eEiEb3cQ7nuZXLYH7leeYFoPtbFV4RicIWp0/YG+RP7rLPCwIDAQAB".decodeBase64())) +} + diff --git a/mirai-core/src/commonTest/kotlin/event/EventChannelJavaTest.java b/mirai-core/src/jvmBaseTest/kotlin/event/EventChannelJavaTest.java similarity index 100% rename from mirai-core/src/commonTest/kotlin/event/EventChannelJavaTest.java rename to mirai-core/src/jvmBaseTest/kotlin/event/EventChannelJavaTest.java diff --git a/mirai-core/src/commonTest/kotlin/event/EventLaunchUndispatchedTest.kt b/mirai-core/src/jvmBaseTest/kotlin/event/EventLaunchUndispatchedTest.kt similarity index 92% rename from mirai-core/src/commonTest/kotlin/event/EventLaunchUndispatchedTest.kt rename to mirai-core/src/jvmBaseTest/kotlin/event/EventLaunchUndispatchedTest.kt index 73b533274..5cd3614d7 100644 --- a/mirai-core/src/commonTest/kotlin/event/EventLaunchUndispatchedTest.kt +++ b/mirai-core/src/jvmBaseTest/kotlin/event/EventLaunchUndispatchedTest.kt @@ -18,10 +18,11 @@ import net.mamoe.mirai.event.EventPriority import net.mamoe.mirai.event.broadcast import net.mamoe.mirai.event.globalEventChannel import net.mamoe.mirai.internal.network.components.EVENT_LAUNCH_UNDISPATCHED +import net.mamoe.mirai.internal.test.runBlockingUnit import org.junit.jupiter.api.AfterEach -import org.junit.jupiter.api.Test import java.util.concurrent.ConcurrentLinkedQueue import java.util.concurrent.Executors +import kotlin.test.Test import kotlin.test.assertEquals import kotlin.test.assertFails import kotlin.test.assertSame @@ -30,10 +31,10 @@ import kotlin.test.assertSame internal class EventLaunchUndispatchedTest : AbstractEventTest() { internal class TestEvent : AbstractEvent() - var originalValue = EVENT_LAUNCH_UNDISPATCHED + private var originalValue = EVENT_LAUNCH_UNDISPATCHED @Test - suspend fun `event runs undispatched`() { + fun `event runs undispatched`() = runBlockingUnit { originalValue = EVENT_LAUNCH_UNDISPATCHED EVENT_LAUNCH_UNDISPATCHED = true doTest() @@ -41,7 +42,7 @@ internal class EventLaunchUndispatchedTest : AbstractEventTest() { } @Test - suspend fun `event runs undispatched fail`() { + fun `event runs undispatched fail`() = runBlockingUnit { originalValue = EVENT_LAUNCH_UNDISPATCHED EVENT_LAUNCH_UNDISPATCHED = false assertFails { doTest() } diff --git a/mirai-core/src/commonTest/kotlin/event/JvmMethodEventsTest.kt b/mirai-core/src/jvmBaseTest/kotlin/event/JvmMethodEventsTest.kt similarity index 98% rename from mirai-core/src/commonTest/kotlin/event/JvmMethodEventsTest.kt rename to mirai-core/src/jvmBaseTest/kotlin/event/JvmMethodEventsTest.kt index 88c822591..8f8d73db5 100644 --- a/mirai-core/src/commonTest/kotlin/event/JvmMethodEventsTest.kt +++ b/mirai-core/src/jvmBaseTest/kotlin/event/JvmMethodEventsTest.kt @@ -16,16 +16,12 @@ import kotlinx.coroutines.isActive import kotlinx.coroutines.runBlocking import net.mamoe.mirai.event.* import org.jetbrains.annotations.NotNull -import org.junit.jupiter.api.Test import java.util.concurrent.atomic.AtomicInteger import kotlin.contracts.InvocationKind import kotlin.contracts.contract import kotlin.coroutines.CoroutineContext import kotlin.coroutines.EmptyCoroutineContext -import kotlin.test.assertEquals -import kotlin.test.assertFalse -import kotlin.test.assertTrue -import kotlin.test.fail +import kotlin.test.* internal class JvmMethodEventsTest : AbstractEventTest() { diff --git a/mirai-core/src/commonTest/kotlin/event/JvmMethodEventsTestJava.kt b/mirai-core/src/jvmBaseTest/kotlin/event/JvmMethodEventsTestJava.kt similarity index 97% rename from mirai-core/src/commonTest/kotlin/event/JvmMethodEventsTestJava.kt rename to mirai-core/src/jvmBaseTest/kotlin/event/JvmMethodEventsTestJava.kt index 9ff0c3be0..2c6012121 100644 --- a/mirai-core/src/commonTest/kotlin/event/JvmMethodEventsTestJava.kt +++ b/mirai-core/src/jvmBaseTest/kotlin/event/JvmMethodEventsTestJava.kt @@ -12,8 +12,8 @@ package net.mamoe.mirai.internal.event import kotlinx.coroutines.cancel import kotlinx.coroutines.runBlocking import net.mamoe.mirai.event.* -import org.junit.jupiter.api.Test import java.util.concurrent.atomic.AtomicInteger +import kotlin.test.Test import kotlin.test.assertEquals internal class JvmMethodEventsTestJava : AbstractEventTest() { diff --git a/mirai-core/src/commonTest/kotlin/event/SimpleListenerHostTestJava.kt b/mirai-core/src/jvmBaseTest/kotlin/event/SimpleListenerHostTestJava.kt similarity index 88% rename from mirai-core/src/commonTest/kotlin/event/SimpleListenerHostTestJava.kt rename to mirai-core/src/jvmBaseTest/kotlin/event/SimpleListenerHostTestJava.kt index f870ca125..8a9848a1d 100644 --- a/mirai-core/src/commonTest/kotlin/event/SimpleListenerHostTestJava.kt +++ b/mirai-core/src/jvmBaseTest/kotlin/event/SimpleListenerHostTestJava.kt @@ -9,13 +9,13 @@ package net.mamoe.mirai.internal.event +import kotlinx.atomicfu.atomic import kotlinx.coroutines.CoroutineScope import kotlinx.coroutines.runBlocking import net.mamoe.mirai.event.* import net.mamoe.mirai.utils.JavaFriendlyAPI -import org.junit.jupiter.api.Test -import java.util.concurrent.atomic.AtomicBoolean import kotlin.coroutines.EmptyCoroutineContext +import kotlin.test.Test @JavaFriendlyAPI @Suppress("INVISIBLE_MEMBER", "INVISIBLE_REFERENCE") @@ -23,7 +23,7 @@ import kotlin.coroutines.EmptyCoroutineContext internal class SimpleListenerHostTestJava : AbstractEventTest() { @Test fun testJavaSimpleListenerHostWork() { - val called = AtomicBoolean() + val called = atomic(false) val host: SimpleListenerHost = object : SimpleListenerHost() { @EventHandler @net.mamoe.mirai.utils.EventListenerLikeJava @@ -31,13 +31,13 @@ internal class SimpleListenerHostTestJava : AbstractEventTest() { event: AbstractEvent? ) { println(event) - called.set(true) + called.value = true } } val scope = CoroutineScope(EmptyCoroutineContext) scope.globalEventChannel().registerListenerHost(host) runBlocking { object : AbstractEvent() {}.broadcast() } - if (!called.get()) { + if (!called.value) { throw AssertionError("JavaTest: SimpleListenerHost Failed.") } } diff --git a/mirai-core/src/jvmBaseTest/kotlin/network/framework/AbstractCommonNHTest.kt b/mirai-core/src/jvmBaseTest/kotlin/network/framework/AbstractCommonNHTest.kt new file mode 100644 index 000000000..40a5bef12 --- /dev/null +++ b/mirai-core/src/jvmBaseTest/kotlin/network/framework/AbstractCommonNHTest.kt @@ -0,0 +1,60 @@ +/* + * Copyright 2019-2022 Mamoe Technologies and contributors. + * + * 此源代码的使用受 GNU AFFERO GENERAL PUBLIC LICENSE version 3 许可证的约束, 可以在以下链接找到该许可证. + * Use of this source code is governed by the GNU AGPLv3 license that can be found through the following link. + * + * https://github.com/mamoe/mirai/blob/dev/LICENSE + */ + +package net.mamoe.mirai.internal.network.framework + +import kotlinx.coroutines.ExecutorCoroutineDispatcher +import net.mamoe.mirai.internal.network.handler.NetworkHandlerFactory +import kotlin.test.AfterTest + +/** + * Without selector. When network is closed, it will not reconnect, so that you can check for its states. + * + * @see AbstractCommonNHTestWithSelector + */ +internal actual abstract class AbstractCommonNHTest actual constructor() : + AbstractRealNetworkHandlerTest() { + actual override val network: TestCommonNetworkHandler by lazy { + factory.create(createContext(), createAddress()) + } + + private val startedDispatchers = mutableListOf() + + @AfterTest + fun cleanupDispatchers() { + startedDispatchers.forEach { it.close() } + } + + actual override val factory: NetworkHandlerFactory = + NetworkHandlerFactory { context, address -> + object : TestCommonNetworkHandler(bot, context, address) { + override suspend fun createConnection(): PlatformConn { + return conn.apply { + doRegister() // restart channel +// setupChannelPipeline( +// pipeline(), PacketDecodePipeline( +// coroutineContext.plus( +// NioEventLoopGroup().asCoroutineDispatcher().also { startedDispatchers.add(it) }) +// ) +// ) + } + } + } + } + + protected actual fun removeOutgoingPacketEncoder() { + kotlin.runCatching { + conn.pipeline().remove("outgoing-packet-encoder") + } + } + + actual val conn: PlatformConn = NettyNHTestChannel() +} + +internal actual typealias PlatformConn = NettyNHTestChannel diff --git a/mirai-core/src/jvmBaseTest/kotlin/network/framework/AbstractNettyNHTest.kt b/mirai-core/src/jvmBaseTest/kotlin/network/framework/AbstractNettyNHTest.kt new file mode 100644 index 000000000..0d6f45b4f --- /dev/null +++ b/mirai-core/src/jvmBaseTest/kotlin/network/framework/AbstractNettyNHTest.kt @@ -0,0 +1,14 @@ +/* + * Copyright 2019-2022 Mamoe Technologies and contributors. + * + * 此源代码的使用受 GNU AFFERO GENERAL PUBLIC LICENSE version 3 许可证的约束, 可以在以下链接找到该许可证. + * Use of this source code is governed by the GNU AGPLv3 license that can be found through the following link. + * + * https://github.com/mamoe/mirai/blob/dev/LICENSE + */ + +package net.mamoe.mirai.internal.network.framework + +internal abstract class AbstractNettyNHTest : AbstractCommonNHTest() { + +} \ No newline at end of file diff --git a/mirai-core/src/jvmBaseTest/kotlin/network/framework/NettyNHTestChannel.kt b/mirai-core/src/jvmBaseTest/kotlin/network/framework/NettyNHTestChannel.kt new file mode 100644 index 000000000..6636c936f --- /dev/null +++ b/mirai-core/src/jvmBaseTest/kotlin/network/framework/NettyNHTestChannel.kt @@ -0,0 +1,94 @@ +/* + * Copyright 2019-2022 Mamoe Technologies and contributors. + * + * 此源代码的使用受 GNU AFFERO GENERAL PUBLIC LICENSE version 3 许可证的约束, 可以在以下链接找到该许可证. + * Use of this source code is governed by the GNU AGPLv3 license that can be found through the following link. + * + * https://github.com/mamoe/mirai/blob/dev/LICENSE + */ + +package net.mamoe.mirai.internal.network.framework + +import io.ktor.utils.io.core.* +import io.netty.channel.embedded.EmbeddedChannel +import io.netty.util.ReferenceCountUtil +import kotlinx.serialization.InternalSerializationApi +import kotlinx.serialization.KSerializer +import kotlinx.serialization.serializer +import net.mamoe.mirai.internal.network.components.RawIncomingPacket +import net.mamoe.mirai.internal.network.protocol.packet.OutgoingPacket +import net.mamoe.mirai.internal.utils.io.ProtoBuf +import net.mamoe.mirai.internal.utils.io.serialization.writeProtoBuf +import net.mamoe.mirai.utils.MiraiLogger +import net.mamoe.mirai.utils.cast +import net.mamoe.mirai.utils.error + +internal class NettyNHTestChannel( + var fakeServer: (NettyNHTestChannel.(msg: Any?) -> Unit)?, +) : EmbeddedChannel() { + constructor() : this(null) + + @OptIn(InternalSerializationApi::class) + fun listen(listener: (OutgoingPacket) -> Any?) { + fakeServer = { packet -> + if (packet is OutgoingPacket) { + val rsp0 = when (val rsp = listener(packet)) { + null -> null + is Unit -> null + is ByteArray -> { + RawIncomingPacket( + commandName = packet.commandName, + sequenceId = packet.sequenceId, + body = rsp + ) + } + is RawIncomingPacket -> rsp + is ProtoBuf -> { + RawIncomingPacket( + commandName = packet.commandName, + sequenceId = packet.sequenceId, + body = buildPacket { + writeProtoBuf( + rsp::class.serializer().cast>(), + rsp + ) + }.readBytes() + ) + } + else -> { + logger.error { "Failed to respond $rsp" } + null + } + } + if (rsp0 != null) { + pipeline().fireChannelRead(rsp0) + } + } + ReferenceCountUtil.release(packet) + } + } + + public /*internal*/ override fun doRegister() { + super.doRegister() // Set channel state to ACTIVE + // Drop old handlers + pipeline().let { p -> + while (p.first() != null) { + p.removeFirst() + } + } + } + + override fun handleInboundMessage(msg: Any?) { + ReferenceCountUtil.release(msg) // Not handled, Drop + } + + override fun handleOutboundMessage(msg: Any?) { + fakeServer?.invoke(this, msg) ?: ReferenceCountUtil.release(msg) + } + + companion object { + private val logger by lazy { + MiraiLogger.Factory.create(NettyNHTestChannel::class) + } + } +} \ No newline at end of file diff --git a/mirai-core/src/commonTest/kotlin/network/impl/netty/NettyUtilsTest.kt b/mirai-core/src/jvmBaseTest/kotlin/network/impl/netty/CommonNHUtilsTest.kt similarity index 79% rename from mirai-core/src/commonTest/kotlin/network/impl/netty/NettyUtilsTest.kt rename to mirai-core/src/jvmBaseTest/kotlin/network/impl/netty/CommonNHUtilsTest.kt index 3c9183603..f42fd9fa8 100644 --- a/mirai-core/src/commonTest/kotlin/network/impl/netty/NettyUtilsTest.kt +++ b/mirai-core/src/jvmBaseTest/kotlin/network/impl/netty/CommonNHUtilsTest.kt @@ -1,10 +1,10 @@ /* - * Copyright 2019-2021 Mamoe Technologies and contributors. + * Copyright 2019-2022 Mamoe Technologies and contributors. * - * 此源代码的使用受 GNU AFFERO GENERAL PUBLIC LICENSE version 3 许可证的约束, 可以在以下链接找到该许可证. - * Use of this source code is governed by the GNU AGPLv3 license that can be found through the following link. + * 此源代码的使用受 GNU AFFERO GENERAL PUBLIC LICENSE version 3 许可证的约束, 可以在以下链接找到该许可证. + * Use of this source code is governed by the GNU AGPLv3 license that can be found through the following link. * - * https://github.com/mamoe/mirai/blob/master/LICENSE + * https://github.com/mamoe/mirai/blob/dev/LICENSE */ package net.mamoe.mirai.internal.network.impl.netty @@ -16,7 +16,7 @@ import kotlinx.coroutines.launch import net.mamoe.mirai.internal.test.AbstractTest import net.mamoe.mirai.internal.test.runBlockingUnit import org.junit.jupiter.api.AfterAll -import org.junit.jupiter.api.Test +import kotlin.test.Test import kotlin.test.assertFailsWith import kotlin.test.assertTrue import kotlin.time.Duration.Companion.seconds @@ -24,7 +24,7 @@ import kotlin.time.Duration.Companion.seconds /** * @see awaitKt */ -internal class NettyUtilsTest : AbstractTest() { +internal class CommonNHUtilsTest : AbstractTest() { companion object { private val channel = EmbeddedChannel() diff --git a/mirai-core/src/jvmBaseTest/kotlin/package.kt b/mirai-core/src/jvmBaseTest/kotlin/package.kt new file mode 100644 index 000000000..7df5cebc4 --- /dev/null +++ b/mirai-core/src/jvmBaseTest/kotlin/package.kt @@ -0,0 +1,10 @@ +/* + * Copyright 2019-2022 Mamoe Technologies and contributors. + * + * 此源代码的使用受 GNU AFFERO GENERAL PUBLIC LICENSE version 3 许可证的约束, 可以在以下链接找到该许可证. + * Use of this source code is governed by the GNU AGPLv3 license that can be found through the following link. + * + * https://github.com/mamoe/mirai/blob/dev/LICENSE + */ + +package net.mamoe.mirai.internal \ No newline at end of file diff --git a/mirai-core/src/jvmBaseTest/kotlin/test/AbstractTest.kt b/mirai-core/src/jvmBaseTest/kotlin/test/AbstractTest.kt new file mode 100644 index 000000000..72f70c3c1 --- /dev/null +++ b/mirai-core/src/jvmBaseTest/kotlin/test/AbstractTest.kt @@ -0,0 +1,45 @@ +/* + * Copyright 2019-2022 Mamoe Technologies and contributors. + * + * 此源代码的使用受 GNU AFFERO GENERAL PUBLIC LICENSE version 3 许可证的约束, 可以在以下链接找到该许可证. + * Use of this source code is governed by the GNU AGPLv3 license that can be found through the following link. + * + * https://github.com/mamoe/mirai/blob/dev/LICENSE + */ + +package net.mamoe.mirai.internal.test + +import kotlinx.coroutines.ExperimentalCoroutinesApi +import kotlinx.coroutines.debug.DebugProbes +import net.mamoe.mirai.IMirai +import net.mamoe.mirai.internal.network.framework.SynchronizedStdoutLogger +import net.mamoe.mirai.utils.MiraiLogger +import net.mamoe.mirai.utils.setSystemProp +import org.junit.jupiter.api.Timeout +import java.util.concurrent.TimeUnit + +@Timeout(value = 7, unit = TimeUnit.MINUTES) +internal actual abstract class AbstractTest actual constructor() : CommonAbstractTest() { + @OptIn(ExperimentalCoroutinesApi::class) + actual companion object { + init { + initPlatform() + + DebugProbes.install() + + @Suppress("DEPRECATION_ERROR") + MiraiLogger.setDefaultLoggerCreator { + SynchronizedStdoutLogger(it) + } + + setSystemProp("mirai.network.packet.logger", "true") + setSystemProp("mirai.network.state.observer.logging", "true") + setSystemProp("mirai.network.show.all.components", "true") + setSystemProp("mirai.network.show.components.creation.stacktrace", "true") + setSystemProp("mirai.network.handle.selector.logging", "true") + + Exception() // create a exception to load relevant classes to estimate invocation time of test cases more accurately. + IMirai::class.simpleName // similarly, load classes. + } + } +} diff --git a/mirai-core/src/jvmBaseTest/kotlin/testFramework/DebugProbes.kt b/mirai-core/src/jvmBaseTest/kotlin/testFramework/DebugProbes.kt new file mode 100644 index 000000000..786ca43fd --- /dev/null +++ b/mirai-core/src/jvmBaseTest/kotlin/testFramework/DebugProbes.kt @@ -0,0 +1,16 @@ +/* + * Copyright 2019-2022 Mamoe Technologies and contributors. + * + * 此源代码的使用受 GNU AFFERO GENERAL PUBLIC LICENSE version 3 许可证的约束, 可以在以下链接找到该许可证. + * Use of this source code is governed by the GNU AGPLv3 license that can be found through the following link. + * + * https://github.com/mamoe/mirai/blob/dev/LICENSE + */ + +package net.mamoe.mirai.internal.testFramework + +import kotlinx.coroutines.ExperimentalCoroutinesApi + +@Suppress("ACTUAL_WITHOUT_EXPECT") +@OptIn(ExperimentalCoroutinesApi::class) +internal actual typealias DebugProbes = kotlinx.coroutines.debug.DebugProbes \ No newline at end of file diff --git a/mirai-core/src/commonTest/kotlin/testFramework/codegen/RemoveDefaultValuesVisitor.kt b/mirai-core/src/jvmBaseTest/kotlin/testFramework/codegen/RemoveDefaultValuesVisitor.kt similarity index 98% rename from mirai-core/src/commonTest/kotlin/testFramework/codegen/RemoveDefaultValuesVisitor.kt rename to mirai-core/src/jvmBaseTest/kotlin/testFramework/codegen/RemoveDefaultValuesVisitor.kt index 60f2e93b3..32080cea3 100644 --- a/mirai-core/src/commonTest/kotlin/testFramework/codegen/RemoveDefaultValuesVisitor.kt +++ b/mirai-core/src/jvmBaseTest/kotlin/testFramework/codegen/RemoveDefaultValuesVisitor.kt @@ -1,5 +1,5 @@ /* - * Copyright 2019-2021 Mamoe Technologies and contributors. + * Copyright 2019-2022 Mamoe Technologies and contributors. * * 此源代码的使用受 GNU AFFERO GENERAL PUBLIC LICENSE version 3 许可证的约束, 可以在以下链接找到该许可证. * Use of this source code is governed by the GNU AGPLv3 license that can be found through the following link. diff --git a/mirai-core/src/commonTest/kotlin/testFramework/codegen/ValueDescAnalyzer.kt b/mirai-core/src/jvmBaseTest/kotlin/testFramework/codegen/ValueDescAnalyzer.kt similarity index 96% rename from mirai-core/src/commonTest/kotlin/testFramework/codegen/ValueDescAnalyzer.kt rename to mirai-core/src/jvmBaseTest/kotlin/testFramework/codegen/ValueDescAnalyzer.kt index 2bea3b924..60f02adef 100644 --- a/mirai-core/src/commonTest/kotlin/testFramework/codegen/ValueDescAnalyzer.kt +++ b/mirai-core/src/jvmBaseTest/kotlin/testFramework/codegen/ValueDescAnalyzer.kt @@ -1,5 +1,5 @@ /* - * Copyright 2019-2021 Mamoe Technologies and contributors. + * Copyright 2019-2022 Mamoe Technologies and contributors. * * 此源代码的使用受 GNU AFFERO GENERAL PUBLIC LICENSE version 3 许可证的约束, 可以在以下链接找到该许可证. * Use of this source code is governed by the GNU AGPLv3 license that can be found through the following link. @@ -83,7 +83,6 @@ object ValueDescAnalyzer { } } -@OptIn(ExperimentalStdlibApi::class) inline fun ValueDescAnalyzer.analyze(value: T): ValueDesc { return analyze(value, typeOf()) } \ No newline at end of file diff --git a/mirai-core/src/commonTest/kotlin/testFramework/codegen/descriptors/ClassValueDesc.kt b/mirai-core/src/jvmBaseTest/kotlin/testFramework/codegen/descriptors/ClassValueDesc.kt similarity index 96% rename from mirai-core/src/commonTest/kotlin/testFramework/codegen/descriptors/ClassValueDesc.kt rename to mirai-core/src/jvmBaseTest/kotlin/testFramework/codegen/descriptors/ClassValueDesc.kt index a5693b90a..c9dcc8012 100644 --- a/mirai-core/src/commonTest/kotlin/testFramework/codegen/descriptors/ClassValueDesc.kt +++ b/mirai-core/src/jvmBaseTest/kotlin/testFramework/codegen/descriptors/ClassValueDesc.kt @@ -1,5 +1,5 @@ /* - * Copyright 2019-2021 Mamoe Technologies and contributors. + * Copyright 2019-2022 Mamoe Technologies and contributors. * * 此源代码的使用受 GNU AFFERO GENERAL PUBLIC LICENSE version 3 许可证的约束, 可以在以下链接找到该许可证. * Use of this source code is governed by the GNU AGPLv3 license that can be found through the following link. diff --git a/mirai-core/src/commonTest/kotlin/testFramework/codegen/descriptors/CollectionLikeValueDesc.kt b/mirai-core/src/jvmBaseTest/kotlin/testFramework/codegen/descriptors/CollectionLikeValueDesc.kt similarity index 97% rename from mirai-core/src/commonTest/kotlin/testFramework/codegen/descriptors/CollectionLikeValueDesc.kt rename to mirai-core/src/jvmBaseTest/kotlin/testFramework/codegen/descriptors/CollectionLikeValueDesc.kt index b0eb95a35..bc6b3f554 100644 --- a/mirai-core/src/commonTest/kotlin/testFramework/codegen/descriptors/CollectionLikeValueDesc.kt +++ b/mirai-core/src/jvmBaseTest/kotlin/testFramework/codegen/descriptors/CollectionLikeValueDesc.kt @@ -1,5 +1,5 @@ /* - * Copyright 2019-2021 Mamoe Technologies and contributors. + * Copyright 2019-2022 Mamoe Technologies and contributors. * * 此源代码的使用受 GNU AFFERO GENERAL PUBLIC LICENSE version 3 许可证的约束, 可以在以下链接找到该许可证. * Use of this source code is governed by the GNU AGPLv3 license that can be found through the following link. diff --git a/mirai-core/src/commonTest/kotlin/testFramework/codegen/descriptors/CollectionValueDesc.kt b/mirai-core/src/jvmBaseTest/kotlin/testFramework/codegen/descriptors/CollectionValueDesc.kt similarity index 96% rename from mirai-core/src/commonTest/kotlin/testFramework/codegen/descriptors/CollectionValueDesc.kt rename to mirai-core/src/jvmBaseTest/kotlin/testFramework/codegen/descriptors/CollectionValueDesc.kt index 3baae13ad..ad3a63c1b 100644 --- a/mirai-core/src/commonTest/kotlin/testFramework/codegen/descriptors/CollectionValueDesc.kt +++ b/mirai-core/src/jvmBaseTest/kotlin/testFramework/codegen/descriptors/CollectionValueDesc.kt @@ -1,5 +1,5 @@ /* - * Copyright 2019-2021 Mamoe Technologies and contributors. + * Copyright 2019-2022 Mamoe Technologies and contributors. * * 此源代码的使用受 GNU AFFERO GENERAL PUBLIC LICENSE version 3 许可证的约束, 可以在以下链接找到该许可证. * Use of this source code is governed by the GNU AGPLv3 license that can be found through the following link. diff --git a/mirai-core/src/commonTest/kotlin/testFramework/codegen/descriptors/MapValueDesc.kt b/mirai-core/src/jvmBaseTest/kotlin/testFramework/codegen/descriptors/MapValueDesc.kt similarity index 97% rename from mirai-core/src/commonTest/kotlin/testFramework/codegen/descriptors/MapValueDesc.kt rename to mirai-core/src/jvmBaseTest/kotlin/testFramework/codegen/descriptors/MapValueDesc.kt index e928b808b..83dd56a33 100644 --- a/mirai-core/src/commonTest/kotlin/testFramework/codegen/descriptors/MapValueDesc.kt +++ b/mirai-core/src/jvmBaseTest/kotlin/testFramework/codegen/descriptors/MapValueDesc.kt @@ -1,5 +1,5 @@ /* - * Copyright 2019-2021 Mamoe Technologies and contributors. + * Copyright 2019-2022 Mamoe Technologies and contributors. * * 此源代码的使用受 GNU AFFERO GENERAL PUBLIC LICENSE version 3 许可证的约束, 可以在以下链接找到该许可证. * Use of this source code is governed by the GNU AGPLv3 license that can be found through the following link. diff --git a/mirai-core/src/commonTest/kotlin/testFramework/codegen/descriptors/ObjectArrayValueDesc.kt b/mirai-core/src/jvmBaseTest/kotlin/testFramework/codegen/descriptors/ObjectArrayValueDesc.kt similarity index 96% rename from mirai-core/src/commonTest/kotlin/testFramework/codegen/descriptors/ObjectArrayValueDesc.kt rename to mirai-core/src/jvmBaseTest/kotlin/testFramework/codegen/descriptors/ObjectArrayValueDesc.kt index 9b93a7b34..560d503e0 100644 --- a/mirai-core/src/commonTest/kotlin/testFramework/codegen/descriptors/ObjectArrayValueDesc.kt +++ b/mirai-core/src/jvmBaseTest/kotlin/testFramework/codegen/descriptors/ObjectArrayValueDesc.kt @@ -1,5 +1,5 @@ /* - * Copyright 2019-2021 Mamoe Technologies and contributors. + * Copyright 2019-2022 Mamoe Technologies and contributors. * * 此源代码的使用受 GNU AFFERO GENERAL PUBLIC LICENSE version 3 许可证的约束, 可以在以下链接找到该许可证. * Use of this source code is governed by the GNU AGPLv3 license that can be found through the following link. diff --git a/mirai-core/src/commonTest/kotlin/testFramework/codegen/descriptors/PlainValueDesc.kt b/mirai-core/src/jvmBaseTest/kotlin/testFramework/codegen/descriptors/PlainValueDesc.kt similarity index 94% rename from mirai-core/src/commonTest/kotlin/testFramework/codegen/descriptors/PlainValueDesc.kt rename to mirai-core/src/jvmBaseTest/kotlin/testFramework/codegen/descriptors/PlainValueDesc.kt index b9578d7a8..2a52d2596 100644 --- a/mirai-core/src/commonTest/kotlin/testFramework/codegen/descriptors/PlainValueDesc.kt +++ b/mirai-core/src/jvmBaseTest/kotlin/testFramework/codegen/descriptors/PlainValueDesc.kt @@ -1,5 +1,5 @@ /* - * Copyright 2019-2021 Mamoe Technologies and contributors. + * Copyright 2019-2022 Mamoe Technologies and contributors. * * 此源代码的使用受 GNU AFFERO GENERAL PUBLIC LICENSE version 3 许可证的约束, 可以在以下链接找到该许可证. * Use of this source code is governed by the GNU AGPLv3 license that can be found through the following link. diff --git a/mirai-core/src/commonTest/kotlin/testFramework/codegen/descriptors/PrimitiveArrayValueDesc.kt b/mirai-core/src/jvmBaseTest/kotlin/testFramework/codegen/descriptors/PrimitiveArrayValueDesc.kt similarity index 97% rename from mirai-core/src/commonTest/kotlin/testFramework/codegen/descriptors/PrimitiveArrayValueDesc.kt rename to mirai-core/src/jvmBaseTest/kotlin/testFramework/codegen/descriptors/PrimitiveArrayValueDesc.kt index 90e3df809..b00b54b84 100644 --- a/mirai-core/src/commonTest/kotlin/testFramework/codegen/descriptors/PrimitiveArrayValueDesc.kt +++ b/mirai-core/src/jvmBaseTest/kotlin/testFramework/codegen/descriptors/PrimitiveArrayValueDesc.kt @@ -1,5 +1,5 @@ /* - * Copyright 2019-2021 Mamoe Technologies and contributors. + * Copyright 2019-2022 Mamoe Technologies and contributors. * * 此源代码的使用受 GNU AFFERO GENERAL PUBLIC LICENSE version 3 许可证的约束, 可以在以下链接找到该许可证. * Use of this source code is governed by the GNU AGPLv3 license that can be found through the following link. diff --git a/mirai-core/src/commonTest/kotlin/testFramework/codegen/descriptors/ValueDesc.kt b/mirai-core/src/jvmBaseTest/kotlin/testFramework/codegen/descriptors/ValueDesc.kt similarity index 96% rename from mirai-core/src/commonTest/kotlin/testFramework/codegen/descriptors/ValueDesc.kt rename to mirai-core/src/jvmBaseTest/kotlin/testFramework/codegen/descriptors/ValueDesc.kt index aa18185d3..d91370562 100644 --- a/mirai-core/src/commonTest/kotlin/testFramework/codegen/descriptors/ValueDesc.kt +++ b/mirai-core/src/jvmBaseTest/kotlin/testFramework/codegen/descriptors/ValueDesc.kt @@ -1,5 +1,5 @@ /* - * Copyright 2019-2021 Mamoe Technologies and contributors. + * Copyright 2019-2022 Mamoe Technologies and contributors. * * 此源代码的使用受 GNU AFFERO GENERAL PUBLIC LICENSE version 3 许可证的约束, 可以在以下链接找到该许可证. * Use of this source code is governed by the GNU AGPLv3 license that can be found through the following link. diff --git a/mirai-core/src/commonTest/kotlin/testFramework/codegen/test/IndenterTest.kt b/mirai-core/src/jvmBaseTest/kotlin/testFramework/codegen/test/IndenterTest.kt similarity index 88% rename from mirai-core/src/commonTest/kotlin/testFramework/codegen/test/IndenterTest.kt rename to mirai-core/src/jvmBaseTest/kotlin/testFramework/codegen/test/IndenterTest.kt index 824bbfb12..3eeb25f11 100644 --- a/mirai-core/src/commonTest/kotlin/testFramework/codegen/test/IndenterTest.kt +++ b/mirai-core/src/jvmBaseTest/kotlin/testFramework/codegen/test/IndenterTest.kt @@ -1,5 +1,5 @@ /* - * Copyright 2019-2021 Mamoe Technologies and contributors. + * Copyright 2019-2022 Mamoe Technologies and contributors. * * 此源代码的使用受 GNU AFFERO GENERAL PUBLIC LICENSE version 3 许可证的约束, 可以在以下链接找到该许可证. * Use of this source code is governed by the GNU AGPLv3 license that can be found through the following link. @@ -11,7 +11,7 @@ package net.mamoe.mirai.internal.testFramework.codegen.test import net.mamoe.mirai.internal.test.AbstractTest import net.mamoe.mirai.internal.testFramework.codegen.visitors.WordingIndenter -import org.junit.jupiter.api.Test +import kotlin.test.Test import kotlin.test.assertEquals internal class IndenterTest : AbstractTest() { diff --git a/mirai-core/src/commonTest/kotlin/testFramework/codegen/test/OptimizeByteArrayAsHexStringTransformerTest.kt b/mirai-core/src/jvmBaseTest/kotlin/testFramework/codegen/test/OptimizeByteArrayAsHexStringTransformerTest.kt similarity index 95% rename from mirai-core/src/commonTest/kotlin/testFramework/codegen/test/OptimizeByteArrayAsHexStringTransformerTest.kt rename to mirai-core/src/jvmBaseTest/kotlin/testFramework/codegen/test/OptimizeByteArrayAsHexStringTransformerTest.kt index 23b3cc76e..c8b2e112d 100644 --- a/mirai-core/src/commonTest/kotlin/testFramework/codegen/test/OptimizeByteArrayAsHexStringTransformerTest.kt +++ b/mirai-core/src/jvmBaseTest/kotlin/testFramework/codegen/test/OptimizeByteArrayAsHexStringTransformerTest.kt @@ -16,7 +16,7 @@ import net.mamoe.mirai.internal.testFramework.codegen.descriptors.transform import net.mamoe.mirai.internal.testFramework.codegen.visitors.OptimizeByteArrayAsHexStringTransformer import net.mamoe.mirai.internal.testFramework.codegen.visitors.ValueDescToStringRenderer import net.mamoe.mirai.internal.testFramework.codegen.visitors.renderToString -import org.junit.jupiter.api.Test +import kotlin.test.Test import kotlin.test.assertEquals internal class OptimizeByteArrayAsHexStringTransformerTest : AbstractTest() { @@ -43,7 +43,7 @@ internal class OptimizeByteArrayAsHexStringTransformerTest : AbstractTest() { fun `can optimize as hex`() { assertEquals( """ - "O".toByteArray() /* 4F 02 */ + "4F 02".hexToBytes() """.trimIndent(), analyzeTransformAndRender(byteArrayOf(0x4f, 0x02)) ) } diff --git a/mirai-core/src/commonTest/kotlin/testFramework/codegen/test/visitors/ValueDescAnalyzerTest.kt b/mirai-core/src/jvmBaseTest/kotlin/testFramework/codegen/test/visitors/ValueDescAnalyzerTest.kt similarity index 99% rename from mirai-core/src/commonTest/kotlin/testFramework/codegen/test/visitors/ValueDescAnalyzerTest.kt rename to mirai-core/src/jvmBaseTest/kotlin/testFramework/codegen/test/visitors/ValueDescAnalyzerTest.kt index 7b88fda1a..4fd6f6821 100644 --- a/mirai-core/src/commonTest/kotlin/testFramework/codegen/test/visitors/ValueDescAnalyzerTest.kt +++ b/mirai-core/src/jvmBaseTest/kotlin/testFramework/codegen/test/visitors/ValueDescAnalyzerTest.kt @@ -1,5 +1,5 @@ /* - * Copyright 2019-2021 Mamoe Technologies and contributors. + * Copyright 2019-2022 Mamoe Technologies and contributors. * * 此源代码的使用受 GNU AFFERO GENERAL PUBLIC LICENSE version 3 许可证的约束, 可以在以下链接找到该许可证. * Use of this source code is governed by the GNU AGPLv3 license that can be found through the following link. diff --git a/mirai-core/src/commonTest/kotlin/testFramework/codegen/test/visitors/ValueDescToStringRendererTest.kt b/mirai-core/src/jvmBaseTest/kotlin/testFramework/codegen/test/visitors/ValueDescToStringRendererTest.kt similarity index 87% rename from mirai-core/src/commonTest/kotlin/testFramework/codegen/test/visitors/ValueDescToStringRendererTest.kt rename to mirai-core/src/jvmBaseTest/kotlin/testFramework/codegen/test/visitors/ValueDescToStringRendererTest.kt index e97a28feb..4e8a17ecf 100644 --- a/mirai-core/src/commonTest/kotlin/testFramework/codegen/test/visitors/ValueDescToStringRendererTest.kt +++ b/mirai-core/src/jvmBaseTest/kotlin/testFramework/codegen/test/visitors/ValueDescToStringRendererTest.kt @@ -1,5 +1,5 @@ /* - * Copyright 2019-2021 Mamoe Technologies and contributors. + * Copyright 2019-2022 Mamoe Technologies and contributors. * * 此源代码的使用受 GNU AFFERO GENERAL PUBLIC LICENSE version 3 许可证的约束, 可以在以下链接找到该许可证. * Use of this source code is governed by the GNU AGPLv3 license that can be found through the following link. @@ -15,7 +15,7 @@ import net.mamoe.mirai.internal.testFramework.codegen.ValueDescAnalyzer import net.mamoe.mirai.internal.testFramework.codegen.analyze import net.mamoe.mirai.internal.testFramework.codegen.visitors.ValueDescToStringRenderer import net.mamoe.mirai.internal.testFramework.codegen.visitors.renderToString -import org.junit.jupiter.api.Test +import kotlin.test.Test import kotlin.test.assertEquals internal class ValueDescToStringRendererTest { @@ -126,14 +126,15 @@ internal class ValueDescToStringRendererTest { val self: MyClass?, ) + @Suppress("ClassName") + data class `MyClass$3`( + val str: String, + val int: Int, + val self: `MyClass$3`?, + ) + @Test fun `class value`() { - data class MyClass2( - val str: String, - val int: Int, - val self: MyClass2?, - ) - assertEquals( """ ${MyClass::class.qualifiedName}( @@ -144,10 +145,19 @@ internal class ValueDescToStringRendererTest { """.trimIndent(), ValueDescAnalyzer.analyze(MyClass("str", 1, null)).renderToString(renderer) ) + } + + @Test + fun `local class`() { + data class MyClass2( + val str: String, + val int: Int, + val self: MyClass2?, + ) assertEquals( """ - `${MyClass2::class.java.name}`( + ${MyClass2::class.simpleName}( str = "str", int = 1, self = null, @@ -157,6 +167,21 @@ internal class ValueDescToStringRendererTest { ) } + @Test + fun `class with special name`() { + assertEquals( + """ + `${`MyClass$3`::class.qualifiedName}`( + str = "str", + int = 1, + self = null, + ) + """.trimIndent(), + ValueDescAnalyzer.analyze(`MyClass$3`("str", 1, null)).renderToString(renderer) + ) + } + + @Test fun `class value nested`() { data class MyClass2( @@ -182,10 +207,10 @@ internal class ValueDescToStringRendererTest { assertEquals( """ - `${MyClass2::class.java.name}`( + ${MyClass2::class.simpleName}( str = "str", int = 1, - self = `${MyClass2::class.java.name}`( + self = ${MyClass2::class.simpleName}( str = "str", int = 1, self = null, diff --git a/mirai-core/src/commonTest/kotlin/testFramework/codegen/visitor/ValueDescTransformer.kt b/mirai-core/src/jvmBaseTest/kotlin/testFramework/codegen/visitor/ValueDescTransformer.kt similarity index 98% rename from mirai-core/src/commonTest/kotlin/testFramework/codegen/visitor/ValueDescTransformer.kt rename to mirai-core/src/jvmBaseTest/kotlin/testFramework/codegen/visitor/ValueDescTransformer.kt index 32d4707ed..cc2c13283 100644 --- a/mirai-core/src/commonTest/kotlin/testFramework/codegen/visitor/ValueDescTransformer.kt +++ b/mirai-core/src/jvmBaseTest/kotlin/testFramework/codegen/visitor/ValueDescTransformer.kt @@ -1,5 +1,5 @@ /* - * Copyright 2019-2021 Mamoe Technologies and contributors. + * Copyright 2019-2022 Mamoe Technologies and contributors. * * 此源代码的使用受 GNU AFFERO GENERAL PUBLIC LICENSE version 3 许可证的约束, 可以在以下链接找到该许可证. * Use of this source code is governed by the GNU AGPLv3 license that can be found through the following link. diff --git a/mirai-core/src/commonTest/kotlin/testFramework/codegen/visitor/ValueDescVisitor.kt b/mirai-core/src/jvmBaseTest/kotlin/testFramework/codegen/visitor/ValueDescVisitor.kt similarity index 95% rename from mirai-core/src/commonTest/kotlin/testFramework/codegen/visitor/ValueDescVisitor.kt rename to mirai-core/src/jvmBaseTest/kotlin/testFramework/codegen/visitor/ValueDescVisitor.kt index 655eb8978..6f1be4f8a 100644 --- a/mirai-core/src/commonTest/kotlin/testFramework/codegen/visitor/ValueDescVisitor.kt +++ b/mirai-core/src/jvmBaseTest/kotlin/testFramework/codegen/visitor/ValueDescVisitor.kt @@ -1,5 +1,5 @@ /* - * Copyright 2019-2021 Mamoe Technologies and contributors. + * Copyright 2019-2022 Mamoe Technologies and contributors. * * 此源代码的使用受 GNU AFFERO GENERAL PUBLIC LICENSE version 3 许可证的约束, 可以在以下链接找到该许可证. * Use of this source code is governed by the GNU AGPLv3 license that can be found through the following link. diff --git a/mirai-core/src/commonTest/kotlin/testFramework/codegen/visitor/ValueDescVisitorUnit.kt b/mirai-core/src/jvmBaseTest/kotlin/testFramework/codegen/visitor/ValueDescVisitorUnit.kt similarity index 90% rename from mirai-core/src/commonTest/kotlin/testFramework/codegen/visitor/ValueDescVisitorUnit.kt rename to mirai-core/src/jvmBaseTest/kotlin/testFramework/codegen/visitor/ValueDescVisitorUnit.kt index b7831ce73..7a9784149 100644 --- a/mirai-core/src/commonTest/kotlin/testFramework/codegen/visitor/ValueDescVisitorUnit.kt +++ b/mirai-core/src/jvmBaseTest/kotlin/testFramework/codegen/visitor/ValueDescVisitorUnit.kt @@ -1,5 +1,5 @@ /* - * Copyright 2019-2021 Mamoe Technologies and contributors. + * Copyright 2019-2022 Mamoe Technologies and contributors. * * 此源代码的使用受 GNU AFFERO GENERAL PUBLIC LICENSE version 3 许可证的约束, 可以在以下链接找到该许可证. * Use of this source code is governed by the GNU AGPLv3 license that can be found through the following link. diff --git a/mirai-core/src/commonTest/kotlin/testFramework/codegen/visitors/AnalyzeDefaultValuesMappingVisitor.kt b/mirai-core/src/jvmBaseTest/kotlin/testFramework/codegen/visitors/AnalyzeDefaultValuesMappingVisitor.kt similarity index 97% rename from mirai-core/src/commonTest/kotlin/testFramework/codegen/visitors/AnalyzeDefaultValuesMappingVisitor.kt rename to mirai-core/src/jvmBaseTest/kotlin/testFramework/codegen/visitors/AnalyzeDefaultValuesMappingVisitor.kt index 8e9b6740a..fdaef2f1b 100644 --- a/mirai-core/src/commonTest/kotlin/testFramework/codegen/visitors/AnalyzeDefaultValuesMappingVisitor.kt +++ b/mirai-core/src/jvmBaseTest/kotlin/testFramework/codegen/visitors/AnalyzeDefaultValuesMappingVisitor.kt @@ -1,5 +1,5 @@ /* - * Copyright 2019-2021 Mamoe Technologies and contributors. + * Copyright 2019-2022 Mamoe Technologies and contributors. * * 此源代码的使用受 GNU AFFERO GENERAL PUBLIC LICENSE version 3 许可证的约束, 可以在以下链接找到该许可证. * Use of this source code is governed by the GNU AGPLv3 license that can be found through the following link. diff --git a/mirai-core/src/commonTest/kotlin/testFramework/codegen/visitors/ClassFormatter.kt b/mirai-core/src/jvmBaseTest/kotlin/testFramework/codegen/visitors/ClassFormatter.kt similarity index 91% rename from mirai-core/src/commonTest/kotlin/testFramework/codegen/visitors/ClassFormatter.kt rename to mirai-core/src/jvmBaseTest/kotlin/testFramework/codegen/visitors/ClassFormatter.kt index 068258dfd..7d856d84b 100644 --- a/mirai-core/src/commonTest/kotlin/testFramework/codegen/visitors/ClassFormatter.kt +++ b/mirai-core/src/jvmBaseTest/kotlin/testFramework/codegen/visitors/ClassFormatter.kt @@ -1,5 +1,5 @@ /* - * Copyright 2019-2021 Mamoe Technologies and contributors. + * Copyright 2019-2022 Mamoe Technologies and contributors. * * 此源代码的使用受 GNU AFFERO GENERAL PUBLIC LICENSE version 3 许可证的约束, 可以在以下链接找到该许可证. * Use of this source code is governed by the GNU AGPLv3 license that can be found through the following link. @@ -19,7 +19,7 @@ data class ClassFormatterContext( open class ClassFormatter { open fun formatClassName(context: ClassFormatterContext): String { - val name = context.desc.type.qualifiedName ?: context.desc.type.java.name + val name = context.desc.type.qualifiedName ?: context.desc.type.simpleName ?: context.desc.type.toString() return wrapBacktickIfNecessary(name) } diff --git a/mirai-core/src/commonTest/kotlin/testFramework/codegen/visitors/Indenter.kt b/mirai-core/src/jvmBaseTest/kotlin/testFramework/codegen/visitors/Indenter.kt similarity index 93% rename from mirai-core/src/commonTest/kotlin/testFramework/codegen/visitors/Indenter.kt rename to mirai-core/src/jvmBaseTest/kotlin/testFramework/codegen/visitors/Indenter.kt index e99c8f963..e2087112a 100644 --- a/mirai-core/src/commonTest/kotlin/testFramework/codegen/visitors/Indenter.kt +++ b/mirai-core/src/jvmBaseTest/kotlin/testFramework/codegen/visitors/Indenter.kt @@ -1,5 +1,5 @@ /* - * Copyright 2019-2021 Mamoe Technologies and contributors. + * Copyright 2019-2022 Mamoe Technologies and contributors. * * 此源代码的使用受 GNU AFFERO GENERAL PUBLIC LICENSE version 3 许可证的约束, 可以在以下链接找到该许可证. * Use of this source code is governed by the GNU AGPLv3 license that can be found through the following link. diff --git a/mirai-core/src/commonTest/kotlin/testFramework/codegen/visitors/OptimizeByteArrayAsHexStringTransformer.kt b/mirai-core/src/jvmBaseTest/kotlin/testFramework/codegen/visitors/OptimizeByteArrayAsHexStringTransformer.kt similarity index 100% rename from mirai-core/src/commonTest/kotlin/testFramework/codegen/visitors/OptimizeByteArrayAsHexStringTransformer.kt rename to mirai-core/src/jvmBaseTest/kotlin/testFramework/codegen/visitors/OptimizeByteArrayAsHexStringTransformer.kt diff --git a/mirai-core/src/commonTest/kotlin/testFramework/codegen/visitors/ValueDescToStringRenderer.kt b/mirai-core/src/jvmBaseTest/kotlin/testFramework/codegen/visitors/ValueDescToStringRenderer.kt similarity index 95% rename from mirai-core/src/commonTest/kotlin/testFramework/codegen/visitors/ValueDescToStringRenderer.kt rename to mirai-core/src/jvmBaseTest/kotlin/testFramework/codegen/visitors/ValueDescToStringRenderer.kt index a8a9c128c..60ef54cb4 100644 --- a/mirai-core/src/commonTest/kotlin/testFramework/codegen/visitors/ValueDescToStringRenderer.kt +++ b/mirai-core/src/jvmBaseTest/kotlin/testFramework/codegen/visitors/ValueDescToStringRenderer.kt @@ -1,5 +1,5 @@ /* - * Copyright 2019-2021 Mamoe Technologies and contributors. + * Copyright 2019-2022 Mamoe Technologies and contributors. * * 此源代码的使用受 GNU AFFERO GENERAL PUBLIC LICENSE version 3 许可证的约束, 可以在以下链接找到该许可证. * Use of this source code is governed by the GNU AGPLv3 license that can be found through the following link. @@ -125,10 +125,10 @@ open class ValueDescToStringRenderer : ValueDescVisitor append(")") } - open fun renderClassName(desc: ClassValueDesc<*>): String { - val name = desc.type.qualifiedName ?: desc.type.java.name - return wrapBacktickIfNecessary(name) - } +// open fun renderClassName(desc: ClassValueDesc<*>): String { +// val name = desc.type.qualifiedName ?: desc.type.java.name +// return wrapBacktickIfNecessary(name) +// } companion object { protected fun wrapBacktickIfNecessary(name: String) = if (name.contains(' ') || name.contains('$')) { diff --git a/mirai-core/src/commonTest/kotlin/testFramework/desensitizer/Desensitizer.kt b/mirai-core/src/jvmBaseTest/kotlin/testFramework/desensitizer/Desensitizer.kt similarity index 95% rename from mirai-core/src/commonTest/kotlin/testFramework/desensitizer/Desensitizer.kt rename to mirai-core/src/jvmBaseTest/kotlin/testFramework/desensitizer/Desensitizer.kt index 6ac352d89..3f1d4b7ef 100644 --- a/mirai-core/src/commonTest/kotlin/testFramework/desensitizer/Desensitizer.kt +++ b/mirai-core/src/jvmBaseTest/kotlin/testFramework/desensitizer/Desensitizer.kt @@ -9,6 +9,7 @@ package net.mamoe.mirai.internal.testFramework.desensitizer +import io.ktor.utils.io.core.* import kotlinx.serialization.decodeFromString import net.mamoe.mirai.Mirai import net.mamoe.mirai.internal.testFramework.codegen.ValueDescAnalyzer @@ -94,7 +95,6 @@ internal class Desensitizer private constructor( .renderToString() } - @OptIn(ExperimentalStdlibApi::class) inline fun ValueDescAnalyzer.generateAndDesensitize( value: T, desensitizer: Desensitizer = instance, @@ -118,24 +118,25 @@ internal class Desensitizer private constructor( error("Replacement '$replacement' must not be longer than '$value'") } else -> { - map.putIfAbsent(value.toByteArray().toUHexString(), replacement.toByteArray().toUHexString()) + map.getOrPut(value.toByteArray().toUHexString()) { replacement.toByteArray().toUHexString() } } } } fun addExtraRulesForNumber(value: Long, replacement: Long) { - map.putIfAbsent(value.toString(), replacement.toString()) + map.getOrPut(value.toString()) { replacement.toString() } // 某些地方会 readLong, readInt, desensitizer visit 不到这些目标 - map.putIfAbsent(value.toByteArray().toUHexString(), replacement.toByteArray().toUHexString()) + map.getOrPut(value.toByteArray().toUHexString()) { replacement.toByteArray().toUHexString() } if (value in Int.MIN_VALUE.toLong()..UInt.MAX_VALUE.toLong() && replacement in Int.MIN_VALUE.toLong()..UInt.MAX_VALUE.toLong() ) { - map.putIfAbsent( - value.toInt().toByteArray().toUHexString(), + map.getOrPut( + value.toInt().toByteArray().toUHexString() + ) { replacement.toInt().toByteArray().toUHexString() - ) + } } // 不需要处理 proto, 所有 proto 都会被反序列化为结构类型由 desensitizer 处理 } diff --git a/mirai-core/src/commonTest/kotlin/testFramework/message/protocol/MessageDecodingRecorder.kt b/mirai-core/src/jvmBaseTest/kotlin/testFramework/message/protocol/MessageDecodingRecorder.kt similarity index 100% rename from mirai-core/src/commonTest/kotlin/testFramework/message/protocol/MessageDecodingRecorder.kt rename to mirai-core/src/jvmBaseTest/kotlin/testFramework/message/protocol/MessageDecodingRecorder.kt diff --git a/mirai-core/src/commonTest/kotlin/testFramework/notice/RecordingNoticeHandler.kt b/mirai-core/src/jvmBaseTest/kotlin/testFramework/notice/RecordingNoticeHandler.kt similarity index 96% rename from mirai-core/src/commonTest/kotlin/testFramework/notice/RecordingNoticeHandler.kt rename to mirai-core/src/jvmBaseTest/kotlin/testFramework/notice/RecordingNoticeHandler.kt index fde34f062..5bf769861 100644 --- a/mirai-core/src/commonTest/kotlin/testFramework/notice/RecordingNoticeHandler.kt +++ b/mirai-core/src/jvmBaseTest/kotlin/testFramework/notice/RecordingNoticeHandler.kt @@ -1,5 +1,5 @@ /* - * Copyright 2019-2021 Mamoe Technologies and contributors. + * Copyright 2019-2022 Mamoe Technologies and contributors. * * 此源代码的使用受 GNU AFFERO GENERAL PUBLIC LICENSE version 3 许可证的约束, 可以在以下链接找到该许可证. * Use of this source code is governed by the GNU AGPLv3 license that can be found through the following link. diff --git a/mirai-core/src/commonTest/kotlin/notice/test/RecordingNoticeProcessorTest.kt b/mirai-core/src/jvmBaseTest/kotlin/testFramework/test/RecordingNoticeProcessorTest.kt similarity index 96% rename from mirai-core/src/commonTest/kotlin/notice/test/RecordingNoticeProcessorTest.kt rename to mirai-core/src/jvmBaseTest/kotlin/testFramework/test/RecordingNoticeProcessorTest.kt index d8ad24a96..84a766433 100644 --- a/mirai-core/src/commonTest/kotlin/notice/test/RecordingNoticeProcessorTest.kt +++ b/mirai-core/src/jvmBaseTest/kotlin/testFramework/test/RecordingNoticeProcessorTest.kt @@ -1,5 +1,5 @@ /* - * Copyright 2019-2021 Mamoe Technologies and contributors. + * Copyright 2019-2022 Mamoe Technologies and contributors. * * 此源代码的使用受 GNU AFFERO GENERAL PUBLIC LICENSE version 3 许可证的约束, 可以在以下链接找到该许可证. * Use of this source code is governed by the GNU AGPLv3 license that can be found through the following link. @@ -7,13 +7,13 @@ * https://github.com/mamoe/mirai/blob/dev/LICENSE */ -package net.mamoe.mirai.internal.notice.test +package net.mamoe.mirai.internal.testFramework.test import kotlinx.serialization.Serializable import kotlinx.serialization.decodeFromString import kotlinx.serialization.protobuf.ProtoNumber -import net.mamoe.mirai.internal.testFramework.desensitizer.Desensitizer import net.mamoe.mirai.internal.test.AbstractTest +import net.mamoe.mirai.internal.testFramework.desensitizer.Desensitizer import net.mamoe.mirai.internal.utils.io.ProtocolStruct import net.mamoe.yamlkt.Yaml import net.mamoe.yamlkt.YamlBuilder diff --git a/mirai-core/src/commonTest/kotlin/utils/StructureToStringTransformerLegacy.kt b/mirai-core/src/jvmBaseTest/kotlin/utils/StructureToStringTransformerLegacy.kt similarity index 100% rename from mirai-core/src/commonTest/kotlin/utils/StructureToStringTransformerLegacy.kt rename to mirai-core/src/jvmBaseTest/kotlin/utils/StructureToStringTransformerLegacy.kt diff --git a/mirai-core/src/commonTest/kotlin/utils/StructureToStringTransformerNew.kt b/mirai-core/src/jvmBaseTest/kotlin/utils/StructureToStringTransformerNew.kt similarity index 100% rename from mirai-core/src/commonTest/kotlin/utils/StructureToStringTransformerNew.kt rename to mirai-core/src/jvmBaseTest/kotlin/utils/StructureToStringTransformerNew.kt diff --git a/mirai-core/src/jvmMain/kotlin/utils/crypto/ECDHJvmDesktop.kt b/mirai-core/src/jvmMain/kotlin/utils/crypto/ECDHJvmDesktop.kt index c855076d0..d2ee43d65 100644 --- a/mirai-core/src/jvmMain/kotlin/utils/crypto/ECDHJvmDesktop.kt +++ b/mirai-core/src/jvmMain/kotlin/utils/crypto/ECDHJvmDesktop.kt @@ -1,5 +1,5 @@ /* - * Copyright 2019-2021 Mamoe Technologies and contributors. + * Copyright 2019-2022 Mamoe Technologies and contributors. * * 此源代码的使用受 GNU AFFERO GENERAL PUBLIC LICENSE version 3 许可证的约束, 可以在以下链接找到该许可证. * Use of this source code is governed by the GNU AGPLv3 license that can be found through the following link. @@ -17,22 +17,6 @@ import java.security.spec.ECGenParameterSpec import java.security.spec.X509EncodedKeySpec import javax.crypto.KeyAgreement - -@Suppress("ACTUAL_WITHOUT_EXPECT") -internal actual typealias ECDHPrivateKey = PrivateKey -@Suppress("ACTUAL_WITHOUT_EXPECT") -internal actual typealias ECDHPublicKey = PublicKey - -internal actual class ECDHKeyPairImpl( - private val delegate: KeyPair, - initialPublicKey: ECDHPublicKey = defaultInitialPublicKey.key, -) : ECDHKeyPair { - override val privateKey: ECDHPrivateKey get() = delegate.private - override val publicKey: ECDHPublicKey get() = delegate.public - override val maskedShareKey: ByteArray by lazy { ECDH.calculateShareKey(privateKey, initialPublicKey) } - override val maskedPublicKey: ByteArray by lazy { publicKey.encoded.copyOfRange(26, 91) } -} - internal actual class ECDH actual constructor(actual val keyPair: ECDHKeyPair) { actual companion object { private const val curveName = "prime256v1" // p-256 diff --git a/mirai-core/src/jvmTest/kotlin/AbstractTestWithMiraiImpl.kt b/mirai-core/src/jvmTest/kotlin/AbstractTestWithMiraiImpl.kt index d1b1ded38..7667a7fe6 100644 --- a/mirai-core/src/jvmTest/kotlin/AbstractTestWithMiraiImpl.kt +++ b/mirai-core/src/jvmTest/kotlin/AbstractTestWithMiraiImpl.kt @@ -1,10 +1,10 @@ /* - * Copyright 2019-2021 Mamoe Technologies and contributors. + * Copyright 2019-2022 Mamoe Technologies and contributors. * - * 此源代码的使用受 GNU AFFERO GENERAL PUBLIC LICENSE version 3 许可证的约束, 可以在以下链接找到该许可证. - * Use of this source code is governed by the GNU AGPLv3 license that can be found through the following link. + * 此源代码的使用受 GNU AFFERO GENERAL PUBLIC LICENSE version 3 许可证的约束, 可以在以下链接找到该许可证. + * Use of this source code is governed by the GNU AGPLv3 license that can be found through the following link. * - * https://github.com/mamoe/mirai/blob/master/LICENSE + * https://github.com/mamoe/mirai/blob/dev/LICENSE */ @@ -12,12 +12,12 @@ package net.mamoe.mirai.internal import net.mamoe.mirai.Mirai import org.junit.jupiter.api.AfterEach -import org.junit.jupiter.api.BeforeEach +import kotlin.test.BeforeTest internal abstract class AbstractTestWithMiraiImpl : MiraiImpl() { private val originalImpl = Mirai - @BeforeEach + @BeforeTest fun setupMiraiImpl() { @Suppress("INVISIBLE_MEMBER", "INVISIBLE_REFERENCE") net.mamoe.mirai._MiraiInstance.set(this) diff --git a/mirai-core/src/jvmTest/kotlin/message/data/ForwardRefineTest.kt b/mirai-core/src/jvmTest/kotlin/message/data/ForwardRefineTest.kt index 8f10f5fdc..be91ccd8e 100644 --- a/mirai-core/src/jvmTest/kotlin/message/data/ForwardRefineTest.kt +++ b/mirai-core/src/jvmTest/kotlin/message/data/ForwardRefineTest.kt @@ -17,7 +17,7 @@ import net.mamoe.mirai.internal.message.SimpleRefineContext import net.mamoe.mirai.internal.test.runBlockingUnit import net.mamoe.mirai.internal.utils.structureToString import net.mamoe.mirai.message.data.* -import org.junit.jupiter.api.Test +import kotlin.test.Test import kotlin.test.assertEquals import kotlin.test.assertTrue diff --git a/mirai-core/src/jvmTest/kotlin/message/data/MessageReceiptTest.kt b/mirai-core/src/jvmTest/kotlin/message/data/MessageReceiptTest.kt index 5860639f0..b16485c5e 100644 --- a/mirai-core/src/jvmTest/kotlin/message/data/MessageReceiptTest.kt +++ b/mirai-core/src/jvmTest/kotlin/message/data/MessageReceiptTest.kt @@ -28,7 +28,7 @@ import net.mamoe.mirai.message.data.buildForwardMessage import net.mamoe.mirai.message.data.toMessageChain import net.mamoe.mirai.utils.Clock import net.mamoe.mirai.utils.currentTimeSeconds -import org.junit.jupiter.api.Test +import kotlin.test.Test import kotlin.test.assertEquals import kotlin.test.assertIs import kotlin.test.assertSame diff --git a/mirai-core/src/jvmTest/kotlin/message/data/MessageRefineTest.kt b/mirai-core/src/jvmTest/kotlin/message/data/MessageRefineTest.kt index e92e8eb1a..3fb9d1140 100644 --- a/mirai-core/src/jvmTest/kotlin/message/data/MessageRefineTest.kt +++ b/mirai-core/src/jvmTest/kotlin/message/data/MessageRefineTest.kt @@ -31,8 +31,8 @@ import net.mamoe.mirai.message.data.* import net.mamoe.mirai.message.data.MessageChain.Companion.serializeToJsonString import net.mamoe.mirai.utils.PlatformLogger import net.mamoe.mirai.utils.hexToBytes -import org.junit.jupiter.api.Test import kotlin.random.Random +import kotlin.test.Test import kotlin.test.assertEquals diff --git a/mirai-core/src/jvmTest/kotlin/message/data/MessageSerializationTest.kt b/mirai-core/src/jvmTest/kotlin/message/data/MessageSerializationTest.kt index ac57bbb7c..7fb2cae34 100644 --- a/mirai-core/src/jvmTest/kotlin/message/data/MessageSerializationTest.kt +++ b/mirai-core/src/jvmTest/kotlin/message/data/MessageSerializationTest.kt @@ -21,7 +21,7 @@ import net.mamoe.mirai.message.MessageSerializers import net.mamoe.mirai.message.data.* import net.mamoe.mirai.utils.cast import org.junit.jupiter.api.BeforeAll -import org.junit.jupiter.api.Test +import kotlin.test.Test import kotlin.test.assertEquals import kotlin.test.assertTrue diff --git a/mirai-core/src/jvmTest/kotlin/test/initPlatform.kt b/mirai-core/src/jvmTest/kotlin/test/initPlatform.kt index 47a7f1660..c1f9cd3eb 100644 --- a/mirai-core/src/jvmTest/kotlin/test/initPlatform.kt +++ b/mirai-core/src/jvmTest/kotlin/test/initPlatform.kt @@ -1,15 +1,15 @@ /* - * Copyright 2019-2021 Mamoe Technologies and contributors. + * Copyright 2019-2022 Mamoe Technologies and contributors. * - * 此源代码的使用受 GNU AFFERO GENERAL PUBLIC LICENSE version 3 许可证的约束, 可以在以下链接找到该许可证. - * Use of this source code is governed by the GNU AGPLv3 license that can be found through the following link. + * 此源代码的使用受 GNU AFFERO GENERAL PUBLIC LICENSE version 3 许可证的约束, 可以在以下链接找到该许可证. + * Use of this source code is governed by the GNU AGPLv3 license that can be found through the following link. * - * https://github.com/mamoe/mirai/blob/master/LICENSE + * https://github.com/mamoe/mirai/blob/dev/LICENSE */ package net.mamoe.mirai.internal.test -import org.junit.jupiter.api.Test +import kotlin.test.Test internal actual fun initPlatform() { // nothing to do diff --git a/mirai-core/src/nativeMain/kotlin/BotAccount.kt b/mirai-core/src/nativeMain/kotlin/BotAccount.kt new file mode 100644 index 000000000..3f4cd9791 --- /dev/null +++ b/mirai-core/src/nativeMain/kotlin/BotAccount.kt @@ -0,0 +1,41 @@ +/* + * Copyright 2019-2022 Mamoe Technologies and contributors. + * + * 此源代码的使用受 GNU AFFERO GENERAL PUBLIC LICENSE version 3 许可证的约束, 可以在以下链接找到该许可证. + * Use of this source code is governed by the GNU AGPLv3 license that can be found through the following link. + * + * https://github.com/mamoe/mirai/blob/dev/LICENSE + */ + +package net.mamoe.mirai.internal + +import net.mamoe.mirai.utils.isSameClass +import net.mamoe.mirai.utils.md5 + +internal actual class BotAccount actual constructor( + internal actual val id: Long, + actual val passwordMd5: ByteArray, + actual val phoneNumber: String, +) { + actual constructor(id: Long, passwordPlainText: String, phoneNumber: String) : this( + id, + passwordPlainText.md5(), + phoneNumber + ) + + actual override fun equals(other: Any?): Boolean { + if (this === other) return true + if (other !is BotAccount || !isSameClass(this, other)) return false + + if (id != other.id) return false + if (!passwordMd5.contentEquals(other.passwordMd5)) return false + + return true + } + + actual override fun hashCode(): Int { + var result = id.hashCode() + result = 31 * result + passwordMd5.hashCode() + return result + } +} \ No newline at end of file diff --git a/mirai-core/src/nativeMain/kotlin/contact/announcement/AnnouncementsImpl.kt b/mirai-core/src/nativeMain/kotlin/contact/announcement/AnnouncementsImpl.kt new file mode 100644 index 000000000..964c88f13 --- /dev/null +++ b/mirai-core/src/nativeMain/kotlin/contact/announcement/AnnouncementsImpl.kt @@ -0,0 +1,18 @@ +/* + * Copyright 2019-2022 Mamoe Technologies and contributors. + * + * 此源代码的使用受 GNU AFFERO GENERAL PUBLIC LICENSE version 3 许可证的约束, 可以在以下链接找到该许可证. + * Use of this source code is governed by the GNU AGPLv3 license that can be found through the following link. + * + * https://github.com/mamoe/mirai/blob/dev/LICENSE + */ + +package net.mamoe.mirai.internal.contact.announcement + +import net.mamoe.mirai.internal.contact.GroupImpl +import net.mamoe.mirai.utils.MiraiLogger + +internal actual class AnnouncementsImpl actual constructor( + group: GroupImpl, + logger: MiraiLogger +) : CommonAnnouncementsImpl(group, logger) \ No newline at end of file diff --git a/mirai-core/src/nativeMain/kotlin/contact/file/AbsoluteFolderImpl.kt b/mirai-core/src/nativeMain/kotlin/contact/file/AbsoluteFolderImpl.kt new file mode 100644 index 000000000..8088e2c0b --- /dev/null +++ b/mirai-core/src/nativeMain/kotlin/contact/file/AbsoluteFolderImpl.kt @@ -0,0 +1,24 @@ +/* + * Copyright 2019-2022 Mamoe Technologies and contributors. + * + * 此源代码的使用受 GNU AFFERO GENERAL PUBLIC LICENSE version 3 许可证的约束, 可以在以下链接找到该许可证. + * Use of this source code is governed by the GNU AGPLv3 license that can be found through the following link. + * + * https://github.com/mamoe/mirai/blob/dev/LICENSE + */ + +package net.mamoe.mirai.internal.contact.file + +import net.mamoe.mirai.contact.FileSupported +import net.mamoe.mirai.contact.file.AbsoluteFolder + +internal actual class AbsoluteFolderImpl actual constructor( + contact: FileSupported, + parent: AbsoluteFolder?, + id: String, + name: String, + uploadTime: Long, + uploaderId: Long, + lastModifiedTime: Long, + contentsCount: Int +) : CommonAbsoluteFolderImpl(contact, parent, id, name, uploadTime, uploaderId, lastModifiedTime, contentsCount) \ No newline at end of file diff --git a/mirai-core/src/nativeMain/kotlin/contact/roaming/RoamingMessagesImpl.kt b/mirai-core/src/nativeMain/kotlin/contact/roaming/RoamingMessagesImpl.kt new file mode 100644 index 000000000..7131b0f05 --- /dev/null +++ b/mirai-core/src/nativeMain/kotlin/contact/roaming/RoamingMessagesImpl.kt @@ -0,0 +1,13 @@ +/* + * Copyright 2019-2022 Mamoe Technologies and contributors. + * + * 此源代码的使用受 GNU AFFERO GENERAL PUBLIC LICENSE version 3 许可证的约束, 可以在以下链接找到该许可证. + * Use of this source code is governed by the GNU AGPLv3 license that can be found through the following link. + * + * https://github.com/mamoe/mirai/blob/dev/LICENSE + */ + +package net.mamoe.mirai.internal.contact.roaming + +internal actual sealed class RoamingMessagesImpl actual constructor() : + CommonRoamingMessagesImpl() \ No newline at end of file diff --git a/mirai-core/src/nativeMain/kotlin/message/protocol/impl/getEmojiPatternResource.kt b/mirai-core/src/nativeMain/kotlin/message/protocol/impl/getEmojiPatternResource.kt new file mode 100644 index 000000000..8c3766f13 --- /dev/null +++ b/mirai-core/src/nativeMain/kotlin/message/protocol/impl/getEmojiPatternResource.kt @@ -0,0 +1,14 @@ +/* + * Copyright 2019-2022 Mamoe Technologies and contributors. + * + * 此源代码的使用受 GNU AFFERO GENERAL PUBLIC LICENSE version 3 许可证的约束, 可以在以下链接找到该许可证. + * Use of this source code is governed by the GNU AGPLv3 license that can be found through the following link. + * + * https://github.com/mamoe/mirai/blob/dev/LICENSE + */ + +package net.mamoe.mirai.internal.message.protocol.impl + +internal actual fun getEmojiPatternResourceOrNull(): String? { + return """\x{1F3F4}\x{E0067}\x{E0062}(?:\x{E0077}\x{E006C}\x{E0073}|\x{E0073}\x{E0063}\x{E0074}|\x{E0065}\x{E006E}\x{E0067})\x{E007F}|(?:\x{1F9D1}\x{1F3FF}\x{200D}\x{2764}(?:\x{FE0F}\x{200D}(?:\x{1F48B}\x{200D})?|\x{200D}(?:\x{1F48B}\x{200D})?)\x{1F9D1}|\x{1F469}\x{1F3FF}\x{200D}\x{1F91D}\x{200D}[\x{1F468}\x{1F469}]|\x{1FAF1}\x{1F3FF}\x{200D}\x{1FAF2})[\x{1F3FB}-\x{1F3FE}]|(?:\x{1F9D1}\x{1F3FE}\x{200D}\x{2764}(?:\x{FE0F}\x{200D}(?:\x{1F48B}\x{200D})?|\x{200D}(?:\x{1F48B}\x{200D})?)\x{1F9D1}|\x{1F469}\x{1F3FE}\x{200D}\x{1F91D}\x{200D}[\x{1F468}\x{1F469}]|\x{1FAF1}\x{1F3FE}\x{200D}\x{1FAF2})[\x{1F3FB}-\x{1F3FD}\x{1F3FF}]|(?:\x{1F9D1}\x{1F3FD}\x{200D}\x{2764}(?:\x{FE0F}\x{200D}(?:\x{1F48B}\x{200D})?|\x{200D}(?:\x{1F48B}\x{200D})?)\x{1F9D1}|\x{1F469}\x{1F3FD}\x{200D}\x{1F91D}\x{200D}[\x{1F468}\x{1F469}]|\x{1FAF1}\x{1F3FD}\x{200D}\x{1FAF2})[\x{1F3FB}\x{1F3FC}\x{1F3FE}\x{1F3FF}]|(?:\x{1F9D1}\x{1F3FC}\x{200D}\x{2764}(?:\x{FE0F}\x{200D}(?:\x{1F48B}\x{200D})?|\x{200D}(?:\x{1F48B}\x{200D})?)\x{1F9D1}|\x{1F469}\x{1F3FC}\x{200D}\x{1F91D}\x{200D}[\x{1F468}\x{1F469}]|\x{1FAF1}\x{1F3FC}\x{200D}\x{1FAF2})[\x{1F3FB}\x{1F3FD}-\x{1F3FF}]|(?:\x{1F9D1}\x{1F3FB}\x{200D}\x{2764}(?:\x{FE0F}\x{200D}(?:\x{1F48B}\x{200D})?|\x{200D}(?:\x{1F48B}\x{200D})?)\x{1F9D1}|\x{1F469}\x{1F3FB}\x{200D}\x{1F91D}\x{200D}[\x{1F468}\x{1F469}]|\x{1FAF1}\x{1F3FB}\x{200D}\x{1FAF2})[\x{1F3FC}-\x{1F3FF}]|\x{1F468}(?:\x{1F3FB}(?:\x{200D}(?:\x{2764}(?:\x{FE0F}\x{200D}(?:\x{1F48B}\x{200D}\x{1F468}[\x{1F3FB}-\x{1F3FF}]|\x{1F468}[\x{1F3FB}-\x{1F3FF}])|\x{200D}(?:\x{1F48B}\x{200D}\x{1F468}[\x{1F3FB}-\x{1F3FF}]|\x{1F468}[\x{1F3FB}-\x{1F3FF}]))|\x{1F91D}\x{200D}\x{1F468}[\x{1F3FC}-\x{1F3FF}]|[\x{2695}\x{2696}\x{2708}]\x{FE0F}|[\x{2695}\x{2696}\x{2708}]|[\x{1F33E}\x{1F373}\x{1F37C}\x{1F393}\x{1F3A4}\x{1F3A8}\x{1F3EB}\x{1F3ED}\x{1F4BB}\x{1F4BC}\x{1F527}\x{1F52C}\x{1F680}\x{1F692}\x{1F9AF}-\x{1F9B3}\x{1F9BC}\x{1F9BD}]))?|[\x{1F3FC}-\x{1F3FF}]\x{200D}\x{2764}(?:\x{FE0F}\x{200D}(?:\x{1F48B}\x{200D}\x{1F468}[\x{1F3FB}-\x{1F3FF}]|\x{1F468}[\x{1F3FB}-\x{1F3FF}])|\x{200D}(?:\x{1F48B}\x{200D}\x{1F468}[\x{1F3FB}-\x{1F3FF}]|\x{1F468}[\x{1F3FB}-\x{1F3FF}]))|\x{200D}(?:\x{2764}(?:\x{FE0F}\x{200D}(?:\x{1F48B}\x{200D})?|\x{200D}(?:\x{1F48B}\x{200D})?)\x{1F468}|[\x{1F468}\x{1F469}]\x{200D}(?:\x{1F466}\x{200D}\x{1F466}|\x{1F467}\x{200D}[\x{1F466}\x{1F467}])|\x{1F466}\x{200D}\x{1F466}|\x{1F467}\x{200D}[\x{1F466}\x{1F467}]|[\x{1F33E}\x{1F373}\x{1F37C}\x{1F393}\x{1F3A4}\x{1F3A8}\x{1F3EB}\x{1F3ED}\x{1F4BB}\x{1F4BC}\x{1F527}\x{1F52C}\x{1F680}\x{1F692}\x{1F9AF}-\x{1F9B3}\x{1F9BC}\x{1F9BD}])|\x{1F3FF}\x{200D}(?:\x{1F91D}\x{200D}\x{1F468}[\x{1F3FB}-\x{1F3FE}]|[\x{1F33E}\x{1F373}\x{1F37C}\x{1F393}\x{1F3A4}\x{1F3A8}\x{1F3EB}\x{1F3ED}\x{1F4BB}\x{1F4BC}\x{1F527}\x{1F52C}\x{1F680}\x{1F692}\x{1F9AF}-\x{1F9B3}\x{1F9BC}\x{1F9BD}])|\x{1F3FE}\x{200D}(?:\x{1F91D}\x{200D}\x{1F468}[\x{1F3FB}-\x{1F3FD}\x{1F3FF}]|[\x{1F33E}\x{1F373}\x{1F37C}\x{1F393}\x{1F3A4}\x{1F3A8}\x{1F3EB}\x{1F3ED}\x{1F4BB}\x{1F4BC}\x{1F527}\x{1F52C}\x{1F680}\x{1F692}\x{1F9AF}-\x{1F9B3}\x{1F9BC}\x{1F9BD}])|\x{1F3FD}\x{200D}(?:\x{1F91D}\x{200D}\x{1F468}[\x{1F3FB}\x{1F3FC}\x{1F3FE}\x{1F3FF}]|[\x{1F33E}\x{1F373}\x{1F37C}\x{1F393}\x{1F3A4}\x{1F3A8}\x{1F3EB}\x{1F3ED}\x{1F4BB}\x{1F4BC}\x{1F527}\x{1F52C}\x{1F680}\x{1F692}\x{1F9AF}-\x{1F9B3}\x{1F9BC}\x{1F9BD}])|\x{1F3FC}\x{200D}(?:\x{1F91D}\x{200D}\x{1F468}[\x{1F3FB}\x{1F3FD}-\x{1F3FF}]|[\x{1F33E}\x{1F373}\x{1F37C}\x{1F393}\x{1F3A4}\x{1F3A8}\x{1F3EB}\x{1F3ED}\x{1F4BB}\x{1F4BC}\x{1F527}\x{1F52C}\x{1F680}\x{1F692}\x{1F9AF}-\x{1F9B3}\x{1F9BC}\x{1F9BD}])|(?:\x{1F3FF}\x{200D}[\x{2695}\x{2696}\x{2708}]|\x{1F3FE}\x{200D}[\x{2695}\x{2696}\x{2708}]|\x{1F3FD}\x{200D}[\x{2695}\x{2696}\x{2708}]|\x{1F3FC}\x{200D}[\x{2695}\x{2696}\x{2708}]|\x{200D}[\x{2695}\x{2696}\x{2708}])\x{FE0F}|\x{200D}(?:[\x{1F468}\x{1F469}]\x{200D}[\x{1F466}\x{1F467}]|[\x{1F466}\x{1F467}])|\x{1F3FF}\x{200D}[\x{2695}\x{2696}\x{2708}]|\x{1F3FE}\x{200D}[\x{2695}\x{2696}\x{2708}]|\x{1F3FD}\x{200D}[\x{2695}\x{2696}\x{2708}]|\x{1F3FC}\x{200D}[\x{2695}\x{2696}\x{2708}]|\x{1F3FF}|\x{1F3FE}|\x{1F3FD}|\x{1F3FC}|\x{200D}[\x{2695}\x{2696}\x{2708}])?|(?:\x{1F469}(?:\x{1F3FB}\x{200D}\x{2764}(?:\x{FE0F}\x{200D}(?:\x{1F48B}\x{200D}[\x{1F468}\x{1F469}]|[\x{1F468}\x{1F469}])|\x{200D}(?:\x{1F48B}\x{200D}[\x{1F468}\x{1F469}]|[\x{1F468}\x{1F469}]))|[\x{1F3FC}-\x{1F3FF}]\x{200D}\x{2764}(?:\x{FE0F}\x{200D}(?:\x{1F48B}\x{200D}[\x{1F468}\x{1F469}]|[\x{1F468}\x{1F469}])|\x{200D}(?:\x{1F48B}\x{200D}[\x{1F468}\x{1F469}]|[\x{1F468}\x{1F469}])))|\x{1F9D1}[\x{1F3FB}-\x{1F3FF}]\x{200D}\x{1F91D}\x{200D}\x{1F9D1})[\x{1F3FB}-\x{1F3FF}]|\x{1F469}\x{200D}\x{1F469}\x{200D}(?:\x{1F466}\x{200D}\x{1F466}|\x{1F467}\x{200D}[\x{1F466}\x{1F467}])|\x{1F469}(?:\x{200D}(?:\x{2764}(?:\x{FE0F}\x{200D}(?:\x{1F48B}\x{200D}[\x{1F468}\x{1F469}]|[\x{1F468}\x{1F469}])|\x{200D}(?:\x{1F48B}\x{200D}[\x{1F468}\x{1F469}]|[\x{1F468}\x{1F469}]))|[\x{1F33E}\x{1F373}\x{1F37C}\x{1F393}\x{1F3A4}\x{1F3A8}\x{1F3EB}\x{1F3ED}\x{1F4BB}\x{1F4BC}\x{1F527}\x{1F52C}\x{1F680}\x{1F692}\x{1F9AF}-\x{1F9B3}\x{1F9BC}\x{1F9BD}])|\x{1F3FF}\x{200D}[\x{1F33E}\x{1F373}\x{1F37C}\x{1F393}\x{1F3A4}\x{1F3A8}\x{1F3EB}\x{1F3ED}\x{1F4BB}\x{1F4BC}\x{1F527}\x{1F52C}\x{1F680}\x{1F692}\x{1F9AF}-\x{1F9B3}\x{1F9BC}\x{1F9BD}]|\x{1F3FE}\x{200D}[\x{1F33E}\x{1F373}\x{1F37C}\x{1F393}\x{1F3A4}\x{1F3A8}\x{1F3EB}\x{1F3ED}\x{1F4BB}\x{1F4BC}\x{1F527}\x{1F52C}\x{1F680}\x{1F692}\x{1F9AF}-\x{1F9B3}\x{1F9BC}\x{1F9BD}]|\x{1F3FD}\x{200D}[\x{1F33E}\x{1F373}\x{1F37C}\x{1F393}\x{1F3A4}\x{1F3A8}\x{1F3EB}\x{1F3ED}\x{1F4BB}\x{1F4BC}\x{1F527}\x{1F52C}\x{1F680}\x{1F692}\x{1F9AF}-\x{1F9B3}\x{1F9BC}\x{1F9BD}]|\x{1F3FC}\x{200D}[\x{1F33E}\x{1F373}\x{1F37C}\x{1F393}\x{1F3A4}\x{1F3A8}\x{1F3EB}\x{1F3ED}\x{1F4BB}\x{1F4BC}\x{1F527}\x{1F52C}\x{1F680}\x{1F692}\x{1F9AF}-\x{1F9B3}\x{1F9BC}\x{1F9BD}]|\x{1F3FB}\x{200D}[\x{1F33E}\x{1F373}\x{1F37C}\x{1F393}\x{1F3A4}\x{1F3A8}\x{1F3EB}\x{1F3ED}\x{1F4BB}\x{1F4BC}\x{1F527}\x{1F52C}\x{1F680}\x{1F692}\x{1F9AF}-\x{1F9B3}\x{1F9BC}\x{1F9BD}])|\x{1F9D1}(?:\x{200D}(?:\x{1F91D}\x{200D}\x{1F9D1}|[\x{1F33E}\x{1F373}\x{1F37C}\x{1F384}\x{1F393}\x{1F3A4}\x{1F3A8}\x{1F3EB}\x{1F3ED}\x{1F4BB}\x{1F4BC}\x{1F527}\x{1F52C}\x{1F680}\x{1F692}\x{1F9AF}-\x{1F9B3}\x{1F9BC}\x{1F9BD}])|\x{1F3FF}\x{200D}[\x{1F33E}\x{1F373}\x{1F37C}\x{1F384}\x{1F393}\x{1F3A4}\x{1F3A8}\x{1F3EB}\x{1F3ED}\x{1F4BB}\x{1F4BC}\x{1F527}\x{1F52C}\x{1F680}\x{1F692}\x{1F9AF}-\x{1F9B3}\x{1F9BC}\x{1F9BD}]|\x{1F3FE}\x{200D}[\x{1F33E}\x{1F373}\x{1F37C}\x{1F384}\x{1F393}\x{1F3A4}\x{1F3A8}\x{1F3EB}\x{1F3ED}\x{1F4BB}\x{1F4BC}\x{1F527}\x{1F52C}\x{1F680}\x{1F692}\x{1F9AF}-\x{1F9B3}\x{1F9BC}\x{1F9BD}]|\x{1F3FD}\x{200D}[\x{1F33E}\x{1F373}\x{1F37C}\x{1F384}\x{1F393}\x{1F3A4}\x{1F3A8}\x{1F3EB}\x{1F3ED}\x{1F4BB}\x{1F4BC}\x{1F527}\x{1F52C}\x{1F680}\x{1F692}\x{1F9AF}-\x{1F9B3}\x{1F9BC}\x{1F9BD}]|\x{1F3FC}\x{200D}[\x{1F33E}\x{1F373}\x{1F37C}\x{1F384}\x{1F393}\x{1F3A4}\x{1F3A8}\x{1F3EB}\x{1F3ED}\x{1F4BB}\x{1F4BC}\x{1F527}\x{1F52C}\x{1F680}\x{1F692}\x{1F9AF}-\x{1F9B3}\x{1F9BC}\x{1F9BD}]|\x{1F3FB}\x{200D}[\x{1F33E}\x{1F373}\x{1F37C}\x{1F384}\x{1F393}\x{1F3A4}\x{1F3A8}\x{1F3EB}\x{1F3ED}\x{1F4BB}\x{1F4BC}\x{1F527}\x{1F52C}\x{1F680}\x{1F692}\x{1F9AF}-\x{1F9B3}\x{1F9BC}\x{1F9BD}])|\x{1F469}\x{200D}\x{1F466}\x{200D}\x{1F466}|\x{1F469}\x{200D}\x{1F469}\x{200D}[\x{1F466}\x{1F467}]|\x{1F469}\x{200D}\x{1F467}\x{200D}[\x{1F466}\x{1F467}]|(?:\x{1F441}\x{FE0F}?\x{200D}\x{1F5E8}|\x{1F9D1}(?:\x{1F3FF}\x{200D}[\x{2695}\x{2696}\x{2708}]|\x{1F3FE}\x{200D}[\x{2695}\x{2696}\x{2708}]|\x{1F3FD}\x{200D}[\x{2695}\x{2696}\x{2708}]|\x{1F3FC}\x{200D}[\x{2695}\x{2696}\x{2708}]|\x{1F3FB}\x{200D}[\x{2695}\x{2696}\x{2708}]|\x{200D}[\x{2695}\x{2696}\x{2708}])|\x{1F469}(?:\x{1F3FF}\x{200D}[\x{2695}\x{2696}\x{2708}]|\x{1F3FE}\x{200D}[\x{2695}\x{2696}\x{2708}]|\x{1F3FD}\x{200D}[\x{2695}\x{2696}\x{2708}]|\x{1F3FC}\x{200D}[\x{2695}\x{2696}\x{2708}]|\x{1F3FB}\x{200D}[\x{2695}\x{2696}\x{2708}]|\x{200D}[\x{2695}\x{2696}\x{2708}])|\x{1F636}\x{200D}\x{1F32B}|\x{1F3F3}\x{FE0F}?\x{200D}\x{26A7}|\x{1F43B}\x{200D}\x{2744}|(?:[\x{1F3C3}\x{1F3C4}\x{1F3CA}\x{1F46E}\x{1F470}\x{1F471}\x{1F473}\x{1F477}\x{1F481}\x{1F482}\x{1F486}\x{1F487}\x{1F645}-\x{1F647}\x{1F64B}\x{1F64D}\x{1F64E}\x{1F6A3}\x{1F6B4}-\x{1F6B6}\x{1F926}\x{1F935}\x{1F937}-\x{1F939}\x{1F93D}\x{1F93E}\x{1F9B8}\x{1F9B9}\x{1F9CD}-\x{1F9CF}\x{1F9D4}\x{1F9D6}-\x{1F9DD}][\x{1F3FB}-\x{1F3FF}]|[\x{1F46F}\x{1F9DE}\x{1F9DF}])\x{200D}[\x{2640}\x{2642}]|[\x{26F9}\x{1F3CB}\x{1F3CC}\x{1F575}](?:[\x{FE0F}\x{1F3FB}-\x{1F3FF}]\x{200D}[\x{2640}\x{2642}]|\x{200D}[\x{2640}\x{2642}])|\x{1F3F4}\x{200D}\x{2620}|[\x{1F3C3}\x{1F3C4}\x{1F3CA}\x{1F46E}\x{1F470}\x{1F471}\x{1F473}\x{1F477}\x{1F481}\x{1F482}\x{1F486}\x{1F487}\x{1F645}-\x{1F647}\x{1F64B}\x{1F64D}\x{1F64E}\x{1F6A3}\x{1F6B4}-\x{1F6B6}\x{1F926}\x{1F935}\x{1F937}-\x{1F939}\x{1F93C}-\x{1F93E}\x{1F9B8}\x{1F9B9}\x{1F9CD}-\x{1F9CF}\x{1F9D4}\x{1F9D6}-\x{1F9DD}]\x{200D}[\x{2640}\x{2642}]|[\xA9\xAE\x{203C}\x{2049}\x{2122}\x{2139}\x{2194}-\x{2199}\x{21A9}\x{21AA}\x{231A}\x{231B}\x{2328}\x{23CF}\x{23ED}-\x{23EF}\x{23F1}\x{23F2}\x{23F8}-\x{23FA}\x{24C2}\x{25AA}\x{25AB}\x{25B6}\x{25C0}\x{25FB}\x{25FC}\x{25FE}\x{2600}-\x{2604}\x{260E}\x{2611}\x{2614}\x{2615}\x{2618}\x{2620}\x{2622}\x{2623}\x{2626}\x{262A}\x{262E}\x{262F}\x{2638}-\x{263A}\x{2640}\x{2642}\x{2648}-\x{2653}\x{265F}\x{2660}\x{2663}\x{2665}\x{2666}\x{2668}\x{267B}\x{267E}\x{267F}\x{2692}\x{2694}-\x{2697}\x{2699}\x{269B}\x{269C}\x{26A0}\x{26A7}\x{26AA}\x{26B0}\x{26B1}\x{26BD}\x{26BE}\x{26C4}\x{26C8}\x{26CF}\x{26D1}\x{26D3}\x{26E9}\x{26F0}-\x{26F5}\x{26F7}\x{26F8}\x{26FA}\x{2702}\x{2708}\x{2709}\x{270F}\x{2712}\x{2714}\x{2716}\x{271D}\x{2721}\x{2733}\x{2734}\x{2744}\x{2747}\x{2757}\x{2763}\x{27A1}\x{2934}\x{2935}\x{2B05}-\x{2B07}\x{2B1B}\x{2B1C}\x{2B55}\x{3030}\x{303D}\x{3297}\x{3299}\x{1F004}\x{1F170}\x{1F171}\x{1F17E}\x{1F17F}\x{1F202}\x{1F237}\x{1F321}\x{1F324}-\x{1F32C}\x{1F336}\x{1F37D}\x{1F396}\x{1F397}\x{1F399}-\x{1F39B}\x{1F39E}\x{1F39F}\x{1F3CD}\x{1F3CE}\x{1F3D4}-\x{1F3DF}\x{1F3F5}\x{1F3F7}\x{1F43F}\x{1F4FD}\x{1F549}\x{1F54A}\x{1F56F}\x{1F570}\x{1F573}\x{1F576}-\x{1F579}\x{1F587}\x{1F58A}-\x{1F58D}\x{1F5A5}\x{1F5A8}\x{1F5B1}\x{1F5B2}\x{1F5BC}\x{1F5C2}-\x{1F5C4}\x{1F5D1}-\x{1F5D3}\x{1F5DC}-\x{1F5DE}\x{1F5E1}\x{1F5E3}\x{1F5E8}\x{1F5EF}\x{1F5F3}\x{1F5FA}\x{1F6CB}\x{1F6CD}-\x{1F6CF}\x{1F6E0}-\x{1F6E5}\x{1F6E9}\x{1F6F0}\x{1F6F3}])\x{FE0F}|\x{1F441}\x{FE0F}?\x{200D}\x{1F5E8}|\x{1F9D1}(?:\x{1F3FF}\x{200D}[\x{2695}\x{2696}\x{2708}]|\x{1F3FE}\x{200D}[\x{2695}\x{2696}\x{2708}]|\x{1F3FD}\x{200D}[\x{2695}\x{2696}\x{2708}]|\x{1F3FC}\x{200D}[\x{2695}\x{2696}\x{2708}]|\x{1F3FB}\x{200D}[\x{2695}\x{2696}\x{2708}]|\x{200D}[\x{2695}\x{2696}\x{2708}])|\x{1F469}(?:\x{1F3FF}\x{200D}[\x{2695}\x{2696}\x{2708}]|\x{1F3FE}\x{200D}[\x{2695}\x{2696}\x{2708}]|\x{1F3FD}\x{200D}[\x{2695}\x{2696}\x{2708}]|\x{1F3FC}\x{200D}[\x{2695}\x{2696}\x{2708}]|\x{1F3FB}\x{200D}[\x{2695}\x{2696}\x{2708}]|\x{200D}[\x{2695}\x{2696}\x{2708}])|\x{1F3F3}\x{FE0F}?\x{200D}\x{1F308}|\x{1F469}\x{200D}\x{1F467}|\x{1F469}\x{200D}\x{1F466}|\x{1F636}\x{200D}\x{1F32B}|\x{1F3F3}\x{FE0F}?\x{200D}\x{26A7}|\x{1F635}\x{200D}\x{1F4AB}|\x{1F62E}\x{200D}\x{1F4A8}|\x{1F415}\x{200D}\x{1F9BA}|\x{1FAF1}(?:\x{1F3FF}|\x{1F3FE}|\x{1F3FD}|\x{1F3FC}|\x{1F3FB})?|\x{1F9D1}(?:\x{1F3FF}|\x{1F3FE}|\x{1F3FD}|\x{1F3FC}|\x{1F3FB})?|\x{1F469}(?:\x{1F3FF}|\x{1F3FE}|\x{1F3FD}|\x{1F3FC}|\x{1F3FB})?|\x{1F43B}\x{200D}\x{2744}|(?:[\x{1F3C3}\x{1F3C4}\x{1F3CA}\x{1F46E}\x{1F470}\x{1F471}\x{1F473}\x{1F477}\x{1F481}\x{1F482}\x{1F486}\x{1F487}\x{1F645}-\x{1F647}\x{1F64B}\x{1F64D}\x{1F64E}\x{1F6A3}\x{1F6B4}-\x{1F6B6}\x{1F926}\x{1F935}\x{1F937}-\x{1F939}\x{1F93D}\x{1F93E}\x{1F9B8}\x{1F9B9}\x{1F9CD}-\x{1F9CF}\x{1F9D4}\x{1F9D6}-\x{1F9DD}][\x{1F3FB}-\x{1F3FF}]|[\x{1F46F}\x{1F9DE}\x{1F9DF}])\x{200D}[\x{2640}\x{2642}]|[\x{26F9}\x{1F3CB}\x{1F3CC}\x{1F575}](?:[\x{FE0F}\x{1F3FB}-\x{1F3FF}]\x{200D}[\x{2640}\x{2642}]|\x{200D}[\x{2640}\x{2642}])|\x{1F3F4}\x{200D}\x{2620}|\x{1F1FD}\x{1F1F0}|\x{1F1F6}\x{1F1E6}|\x{1F1F4}\x{1F1F2}|\x{1F408}\x{200D}\x{2B1B}|\x{2764}(?:\x{FE0F}\x{200D}[\x{1F525}\x{1FA79}]|\x{200D}[\x{1F525}\x{1FA79}])|\x{1F441}\x{FE0F}?|\x{1F3F3}\x{FE0F}?|[\x{1F3C3}\x{1F3C4}\x{1F3CA}\x{1F46E}\x{1F470}\x{1F471}\x{1F473}\x{1F477}\x{1F481}\x{1F482}\x{1F486}\x{1F487}\x{1F645}-\x{1F647}\x{1F64B}\x{1F64D}\x{1F64E}\x{1F6A3}\x{1F6B4}-\x{1F6B6}\x{1F926}\x{1F935}\x{1F937}-\x{1F939}\x{1F93C}-\x{1F93E}\x{1F9B8}\x{1F9B9}\x{1F9CD}-\x{1F9CF}\x{1F9D4}\x{1F9D6}-\x{1F9DD}]\x{200D}[\x{2640}\x{2642}]|\x{1F1FF}[\x{1F1E6}\x{1F1F2}\x{1F1FC}]|\x{1F1FE}[\x{1F1EA}\x{1F1F9}]|\x{1F1FC}[\x{1F1EB}\x{1F1F8}]|\x{1F1FB}[\x{1F1E6}\x{1F1E8}\x{1F1EA}\x{1F1EC}\x{1F1EE}\x{1F1F3}\x{1F1FA}]|\x{1F1FA}[\x{1F1E6}\x{1F1EC}\x{1F1F2}\x{1F1F3}\x{1F1F8}\x{1F1FE}\x{1F1FF}]|\x{1F1F9}[\x{1F1E6}\x{1F1E8}\x{1F1E9}\x{1F1EB}-\x{1F1ED}\x{1F1EF}-\x{1F1F4}\x{1F1F7}\x{1F1F9}\x{1F1FB}\x{1F1FC}\x{1F1FF}]|\x{1F1F8}[\x{1F1E6}-\x{1F1EA}\x{1F1EC}-\x{1F1F4}\x{1F1F7}-\x{1F1F9}\x{1F1FB}\x{1F1FD}-\x{1F1FF}]|\x{1F1F7}[\x{1F1EA}\x{1F1F4}\x{1F1F8}\x{1F1FA}\x{1F1FC}]|\x{1F1F5}[\x{1F1E6}\x{1F1EA}-\x{1F1ED}\x{1F1F0}-\x{1F1F3}\x{1F1F7}-\x{1F1F9}\x{1F1FC}\x{1F1FE}]|\x{1F1F3}[\x{1F1E6}\x{1F1E8}\x{1F1EA}-\x{1F1EC}\x{1F1EE}\x{1F1F1}\x{1F1F4}\x{1F1F5}\x{1F1F7}\x{1F1FA}\x{1F1FF}]|\x{1F1F2}[\x{1F1E6}\x{1F1E8}-\x{1F1ED}\x{1F1F0}-\x{1F1FF}]|\x{1F1F1}[\x{1F1E6}-\x{1F1E8}\x{1F1EE}\x{1F1F0}\x{1F1F7}-\x{1F1FB}\x{1F1FE}]|\x{1F1F0}[\x{1F1EA}\x{1F1EC}-\x{1F1EE}\x{1F1F2}\x{1F1F3}\x{1F1F5}\x{1F1F7}\x{1F1FC}\x{1F1FE}\x{1F1FF}]|\x{1F1EF}[\x{1F1EA}\x{1F1F2}\x{1F1F4}\x{1F1F5}]|\x{1F1EE}[\x{1F1E8}-\x{1F1EA}\x{1F1F1}-\x{1F1F4}\x{1F1F6}-\x{1F1F9}]|\x{1F1ED}[\x{1F1F0}\x{1F1F2}\x{1F1F3}\x{1F1F7}\x{1F1F9}\x{1F1FA}]|\x{1F1EC}[\x{1F1E6}\x{1F1E7}\x{1F1E9}-\x{1F1EE}\x{1F1F1}-\x{1F1F3}\x{1F1F5}-\x{1F1FA}\x{1F1FC}\x{1F1FE}]|\x{1F1EB}[\x{1F1EE}-\x{1F1F0}\x{1F1F2}\x{1F1F4}\x{1F1F7}]|\x{1F1EA}[\x{1F1E6}\x{1F1E8}\x{1F1EA}\x{1F1EC}\x{1F1ED}\x{1F1F7}-\x{1F1FA}]|\x{1F1E9}[\x{1F1EA}\x{1F1EC}\x{1F1EF}\x{1F1F0}\x{1F1F2}\x{1F1F4}\x{1F1FF}]|\x{1F1E8}[\x{1F1E6}\x{1F1E8}\x{1F1E9}\x{1F1EB}-\x{1F1EE}\x{1F1F0}-\x{1F1F5}\x{1F1F7}\x{1F1FA}-\x{1F1FF}]|\x{1F1E7}[\x{1F1E6}\x{1F1E7}\x{1F1E9}-\x{1F1EF}\x{1F1F1}-\x{1F1F4}\x{1F1F6}-\x{1F1F9}\x{1F1FB}\x{1F1FC}\x{1F1FE}\x{1F1FF}]|\x{1F1E6}[\x{1F1E8}-\x{1F1EC}\x{1F1EE}\x{1F1F1}\x{1F1F2}\x{1F1F4}\x{1F1F6}-\x{1F1FA}\x{1F1FC}\x{1F1FD}\x{1F1FF}]|[#\*0-9]\x{FE0F}?\x{20E3}|\x{1F93C}[\x{1F3FB}-\x{1F3FF}]|\x{2764}\x{FE0F}?|[\x{1F3C3}\x{1F3C4}\x{1F3CA}\x{1F46E}\x{1F470}\x{1F471}\x{1F473}\x{1F477}\x{1F481}\x{1F482}\x{1F486}\x{1F487}\x{1F645}-\x{1F647}\x{1F64B}\x{1F64D}\x{1F64E}\x{1F6A3}\x{1F6B4}-\x{1F6B6}\x{1F926}\x{1F935}\x{1F937}-\x{1F939}\x{1F93D}\x{1F93E}\x{1F9B8}\x{1F9B9}\x{1F9CD}-\x{1F9CF}\x{1F9D4}\x{1F9D6}-\x{1F9DD}][\x{1F3FB}-\x{1F3FF}]|[\x{26F9}\x{1F3CB}\x{1F3CC}\x{1F575}][\x{FE0F}\x{1F3FB}-\x{1F3FF}]?|\x{1F3F4}|[\x{270A}\x{270B}\x{1F385}\x{1F3C2}\x{1F3C7}\x{1F442}\x{1F443}\x{1F446}-\x{1F450}\x{1F466}\x{1F467}\x{1F46B}-\x{1F46D}\x{1F472}\x{1F474}-\x{1F476}\x{1F478}\x{1F47C}\x{1F483}\x{1F485}\x{1F48F}\x{1F491}\x{1F4AA}\x{1F57A}\x{1F595}\x{1F596}\x{1F64C}\x{1F64F}\x{1F6C0}\x{1F6CC}\x{1F90C}\x{1F90F}\x{1F918}-\x{1F91F}\x{1F930}-\x{1F934}\x{1F936}\x{1F977}\x{1F9B5}\x{1F9B6}\x{1F9BB}\x{1F9D2}\x{1F9D3}\x{1F9D5}\x{1FAC3}-\x{1FAC5}\x{1FAF0}\x{1FAF2}-\x{1FAF6}][\x{1F3FB}-\x{1F3FF}]|[\x{261D}\x{270C}\x{270D}\x{1F574}\x{1F590}][\x{FE0F}\x{1F3FB}-\x{1F3FF}]|[\x{261D}\x{270A}-\x{270D}\x{1F385}\x{1F3C2}\x{1F3C7}\x{1F408}\x{1F415}\x{1F43B}\x{1F442}\x{1F443}\x{1F446}-\x{1F450}\x{1F466}\x{1F467}\x{1F46B}-\x{1F46D}\x{1F472}\x{1F474}-\x{1F476}\x{1F478}\x{1F47C}\x{1F483}\x{1F485}\x{1F48F}\x{1F491}\x{1F4AA}\x{1F574}\x{1F57A}\x{1F590}\x{1F595}\x{1F596}\x{1F62E}\x{1F635}\x{1F636}\x{1F64C}\x{1F64F}\x{1F6C0}\x{1F6CC}\x{1F90C}\x{1F90F}\x{1F918}-\x{1F91F}\x{1F930}-\x{1F934}\x{1F936}\x{1F93C}\x{1F977}\x{1F9B5}\x{1F9B6}\x{1F9BB}\x{1F9D2}\x{1F9D3}\x{1F9D5}\x{1FAC3}-\x{1FAC5}\x{1FAF0}\x{1FAF2}-\x{1FAF6}]|[\x{1F3C3}\x{1F3C4}\x{1F3CA}\x{1F46E}\x{1F470}\x{1F471}\x{1F473}\x{1F477}\x{1F481}\x{1F482}\x{1F486}\x{1F487}\x{1F645}-\x{1F647}\x{1F64B}\x{1F64D}\x{1F64E}\x{1F6A3}\x{1F6B4}-\x{1F6B6}\x{1F926}\x{1F935}\x{1F937}-\x{1F939}\x{1F93D}\x{1F93E}\x{1F9B8}\x{1F9B9}\x{1F9CD}-\x{1F9CF}\x{1F9D4}\x{1F9D6}-\x{1F9DD}]|[\x{1F46F}\x{1F9DE}\x{1F9DF}]|[\xA9\xAE\x{203C}\x{2049}\x{2122}\x{2139}\x{2194}-\x{2199}\x{21A9}\x{21AA}\x{231A}\x{231B}\x{2328}\x{23CF}\x{23ED}-\x{23EF}\x{23F1}\x{23F2}\x{23F8}-\x{23FA}\x{24C2}\x{25AA}\x{25AB}\x{25B6}\x{25C0}\x{25FB}\x{25FC}\x{25FE}\x{2600}-\x{2604}\x{260E}\x{2611}\x{2614}\x{2615}\x{2618}\x{2620}\x{2622}\x{2623}\x{2626}\x{262A}\x{262E}\x{262F}\x{2638}-\x{263A}\x{2640}\x{2642}\x{2648}-\x{2653}\x{265F}\x{2660}\x{2663}\x{2665}\x{2666}\x{2668}\x{267B}\x{267E}\x{267F}\x{2692}\x{2694}-\x{2697}\x{2699}\x{269B}\x{269C}\x{26A0}\x{26A7}\x{26AA}\x{26B0}\x{26B1}\x{26BD}\x{26BE}\x{26C4}\x{26C8}\x{26CF}\x{26D1}\x{26D3}\x{26E9}\x{26F0}-\x{26F5}\x{26F7}\x{26F8}\x{26FA}\x{2702}\x{2708}\x{2709}\x{270F}\x{2712}\x{2714}\x{2716}\x{271D}\x{2721}\x{2733}\x{2734}\x{2744}\x{2747}\x{2757}\x{2763}\x{27A1}\x{2934}\x{2935}\x{2B05}-\x{2B07}\x{2B1B}\x{2B1C}\x{2B55}\x{3030}\x{303D}\x{3297}\x{3299}\x{1F004}\x{1F170}\x{1F171}\x{1F17E}\x{1F17F}\x{1F202}\x{1F237}\x{1F321}\x{1F324}-\x{1F32C}\x{1F336}\x{1F37D}\x{1F396}\x{1F397}\x{1F399}-\x{1F39B}\x{1F39E}\x{1F39F}\x{1F3CD}\x{1F3CE}\x{1F3D4}-\x{1F3DF}\x{1F3F5}\x{1F3F7}\x{1F43F}\x{1F4FD}\x{1F549}\x{1F54A}\x{1F56F}\x{1F570}\x{1F573}\x{1F576}-\x{1F579}\x{1F587}\x{1F58A}-\x{1F58D}\x{1F5A5}\x{1F5A8}\x{1F5B1}\x{1F5B2}\x{1F5BC}\x{1F5C2}-\x{1F5C4}\x{1F5D1}-\x{1F5D3}\x{1F5DC}-\x{1F5DE}\x{1F5E1}\x{1F5E3}\x{1F5E8}\x{1F5EF}\x{1F5F3}\x{1F5FA}\x{1F6CB}\x{1F6CD}-\x{1F6CF}\x{1F6E0}-\x{1F6E5}\x{1F6E9}\x{1F6F0}\x{1F6F3}]|[\x{23E9}-\x{23EC}\x{23F0}\x{23F3}\x{25FD}\x{2693}\x{26A1}\x{26AB}\x{26C5}\x{26CE}\x{26D4}\x{26EA}\x{26FD}\x{2705}\x{2728}\x{274C}\x{274E}\x{2753}-\x{2755}\x{2795}-\x{2797}\x{27B0}\x{27BF}\x{2B50}\x{1F0CF}\x{1F18E}\x{1F191}-\x{1F19A}\x{1F201}\x{1F21A}\x{1F22F}\x{1F232}-\x{1F236}\x{1F238}-\x{1F23A}\x{1F250}\x{1F251}\x{1F300}-\x{1F320}\x{1F32D}-\x{1F335}\x{1F337}-\x{1F37C}\x{1F37E}-\x{1F384}\x{1F386}-\x{1F393}\x{1F3A0}-\x{1F3C1}\x{1F3C5}\x{1F3C6}\x{1F3C8}\x{1F3C9}\x{1F3CF}-\x{1F3D3}\x{1F3E0}-\x{1F3F0}\x{1F3F8}-\x{1F407}\x{1F409}-\x{1F414}\x{1F416}-\x{1F43A}\x{1F43C}-\x{1F43E}\x{1F440}\x{1F444}\x{1F445}\x{1F451}-\x{1F465}\x{1F46A}\x{1F479}-\x{1F47B}\x{1F47D}-\x{1F480}\x{1F484}\x{1F488}-\x{1F48E}\x{1F490}\x{1F492}-\x{1F4A9}\x{1F4AB}-\x{1F4FC}\x{1F4FF}-\x{1F53D}\x{1F54B}-\x{1F54E}\x{1F550}-\x{1F567}\x{1F5A4}\x{1F5FB}-\x{1F62D}\x{1F62F}-\x{1F634}\x{1F637}-\x{1F644}\x{1F648}-\x{1F64A}\x{1F680}-\x{1F6A2}\x{1F6A4}-\x{1F6B3}\x{1F6B7}-\x{1F6BF}\x{1F6C1}-\x{1F6C5}\x{1F6D0}-\x{1F6D2}\x{1F6D5}-\x{1F6D7}\x{1F6DD}-\x{1F6DF}\x{1F6EB}\x{1F6EC}\x{1F6F4}-\x{1F6FC}\x{1F7E0}-\x{1F7EB}\x{1F7F0}\x{1F90D}\x{1F90E}\x{1F910}-\x{1F917}\x{1F920}-\x{1F925}\x{1F927}-\x{1F92F}\x{1F93A}\x{1F93F}-\x{1F945}\x{1F947}-\x{1F976}\x{1F978}-\x{1F9B4}\x{1F9B7}\x{1F9BA}\x{1F9BC}-\x{1F9CC}\x{1F9D0}\x{1F9E0}-\x{1F9FF}\x{1FA70}-\x{1FA74}\x{1FA78}-\x{1FA7C}\x{1FA80}-\x{1FA86}\x{1FA90}-\x{1FAAC}\x{1FAB0}-\x{1FABA}\x{1FAC0}-\x{1FAC2}\x{1FAD0}-\x{1FAD9}\x{1FAE0}-\x{1FAE7}]""" +} \ No newline at end of file diff --git a/mirai-core/src/nativeMain/kotlin/network/component/getComponentTypeArgument.kt b/mirai-core/src/nativeMain/kotlin/network/component/getComponentTypeArgument.kt new file mode 100644 index 000000000..82435478a --- /dev/null +++ b/mirai-core/src/nativeMain/kotlin/network/component/getComponentTypeArgument.kt @@ -0,0 +1,14 @@ +/* + * Copyright 2019-2022 Mamoe Technologies and contributors. + * + * 此源代码的使用受 GNU AFFERO GENERAL PUBLIC LICENSE version 3 许可证的约束, 可以在以下链接找到该许可证. + * Use of this source code is governed by the GNU AGPLv3 license that can be found through the following link. + * + * https://github.com/mamoe/mirai/blob/dev/LICENSE + */ + +package net.mamoe.mirai.internal.network.component + +import kotlin.reflect.KTypeProjection + +internal actual fun ComponentKey<*>.getComponentTypeArgument(): KTypeProjection? = null \ No newline at end of file diff --git a/mirai-core/src/nativeMain/kotlin/network/handler/SocketAddress.kt b/mirai-core/src/nativeMain/kotlin/network/handler/SocketAddress.kt new file mode 100644 index 000000000..44fb18548 --- /dev/null +++ b/mirai-core/src/nativeMain/kotlin/network/handler/SocketAddress.kt @@ -0,0 +1,49 @@ +/* + * Copyright 2019-2022 Mamoe Technologies and contributors. + * + * 此源代码的使用受 GNU AFFERO GENERAL PUBLIC LICENSE version 3 许可证的约束, 可以在以下链接找到该许可证. + * Use of this source code is governed by the GNU AGPLv3 license that can be found through the following link. + * + * https://github.com/mamoe/mirai/blob/dev/LICENSE + */ + +package net.mamoe.mirai.internal.network.handler + +import net.mamoe.mirai.internal.network.handler.NetworkHandler.State + +internal actual abstract class SocketAddress( + actual val host: String, + actual val port: Int, + @Suppress("UNUSED_PARAMETER") constructorMarker: Unit?, // avoid ambiguity with function SocketAddress +) + +internal class SocketAddressImpl(host: String, port: Int) : SocketAddress(host, port, null) + +internal actual fun SocketAddress(host: String, port: Int): SocketAddress { + return SocketAddressImpl(host, port) +} + +/** + * Factory for a specific [NetworkHandler] implementation. + */ +internal actual fun interface NetworkHandlerFactory { + actual fun create( + context: NetworkHandlerContext, + host: String, + port: Int + ): H = create(context, SocketAddressImpl(host, port)) + + /** + * Create an instance of [H]. The returning [H] has [NetworkHandler.state] of [State.INITIALIZED] + */ + actual fun create( + context: NetworkHandlerContext, + address: SocketAddress + ): H + + actual companion object { + actual fun getPlatformDefault(): NetworkHandlerFactory<*> { + TODO("Not yet implemented") + } + } +} \ No newline at end of file diff --git a/mirai-core/src/nativeMain/kotlin/network/protocol/data/richstatus/parseRichStatusImpl.kt b/mirai-core/src/nativeMain/kotlin/network/protocol/data/richstatus/parseRichStatusImpl.kt new file mode 100644 index 000000000..632bbcc95 --- /dev/null +++ b/mirai-core/src/nativeMain/kotlin/network/protocol/data/richstatus/parseRichStatusImpl.kt @@ -0,0 +1,14 @@ +/* + * Copyright 2019-2022 Mamoe Technologies and contributors. + * + * 此源代码的使用受 GNU AFFERO GENERAL PUBLIC LICENSE version 3 许可证的约束, 可以在以下链接找到该许可证. + * Use of this source code is governed by the GNU AGPLv3 license that can be found through the following link. + * + * https://github.com/mamoe/mirai/blob/dev/LICENSE + */ + +package net.mamoe.mirai.internal.network.protocol.data.richstatus + +internal actual fun parseRichStatusImpl(rawData: ByteArray?): RichStatus { + return RichStatus() +} \ No newline at end of file diff --git a/mirai-core/src/nativeMain/kotlin/package.kt b/mirai-core/src/nativeMain/kotlin/package.kt new file mode 100644 index 000000000..7df5cebc4 --- /dev/null +++ b/mirai-core/src/nativeMain/kotlin/package.kt @@ -0,0 +1,10 @@ +/* + * Copyright 2019-2022 Mamoe Technologies and contributors. + * + * 此源代码的使用受 GNU AFFERO GENERAL PUBLIC LICENSE version 3 许可证的约束, 可以在以下链接找到该许可证. + * Use of this source code is governed by the GNU AGPLv3 license that can be found through the following link. + * + * https://github.com/mamoe/mirai/blob/dev/LICENSE + */ + +package net.mamoe.mirai.internal \ No newline at end of file diff --git a/mirai-core/src/nativeMain/kotlin/utils/MiraiCoreServices.kt b/mirai-core/src/nativeMain/kotlin/utils/MiraiCoreServices.kt new file mode 100644 index 000000000..350c98d13 --- /dev/null +++ b/mirai-core/src/nativeMain/kotlin/utils/MiraiCoreServices.kt @@ -0,0 +1,115 @@ +/* + * Copyright 2019-2022 Mamoe Technologies and contributors. + * + * 此源代码的使用受 GNU AFFERO GENERAL PUBLIC LICENSE version 3 许可证的约束, 可以在以下链接找到该许可证. + * Use of this source code is governed by the GNU AGPLv3 license that can be found through the following link. + * + * https://github.com/mamoe/mirai/blob/dev/LICENSE + */ + +package net.mamoe.mirai.internal.utils + +import net.mamoe.mirai.internal.event.InternalEventMechanism +import net.mamoe.mirai.utils.Services + +internal object MiraiCoreServices { + + @OptIn(InternalEventMechanism::class) + fun registerAll() { + Services.register( + "net.mamoe.mirai.event.InternalGlobalEventChannelProvider", + "net.mamoe.mirai.internal.event.GlobalEventChannelProviderImpl" + ) { net.mamoe.mirai.internal.event.GlobalEventChannelProviderImpl() } + + Services.register( + "net.mamoe.mirai.IMirai", + "net.mamoe.mirai.IMirai" + ) { net.mamoe.mirai.internal.MiraiImpl() } + + val msgProtocol = "net.mamoe.mirai.internal.message.protocol.MessageProtocol" + + Services.register( + msgProtocol, + "net.mamoe.mirai.internal.message.protocol.impl.CustomMessageProtocol" + ) { net.mamoe.mirai.internal.message.protocol.impl.CustomMessageProtocol() } + Services.register( + msgProtocol, + "net.mamoe.mirai.internal.message.protocol.impl.FaceProtocol" + ) { net.mamoe.mirai.internal.message.protocol.impl.FaceProtocol() } + Services.register( + msgProtocol, + "net.mamoe.mirai.internal.message.protocol.impl.FileMessageProtocol" + ) { net.mamoe.mirai.internal.message.protocol.impl.FileMessageProtocol() } + Services.register( + msgProtocol, + "net.mamoe.mirai.internal.message.protocol.impl.FlashImageProtocol" + ) { net.mamoe.mirai.internal.message.protocol.impl.FlashImageProtocol() } + Services.register( + msgProtocol, + "net.mamoe.mirai.internal.message.protocol.impl.IgnoredMessagesProtocol" + ) { net.mamoe.mirai.internal.message.protocol.impl.IgnoredMessagesProtocol() } + Services.register( + msgProtocol, + "net.mamoe.mirai.internal.message.protocol.impl.ImageProtocol" + ) { net.mamoe.mirai.internal.message.protocol.impl.ImageProtocol() } + Services.register( + msgProtocol, + "net.mamoe.mirai.internal.message.protocol.impl.MarketFaceProtocol" + ) { net.mamoe.mirai.internal.message.protocol.impl.MarketFaceProtocol() } + Services.register( + msgProtocol, + "net.mamoe.mirai.internal.message.protocol.impl.MusicShareProtocol" + ) { net.mamoe.mirai.internal.message.protocol.impl.MusicShareProtocol() } + Services.register( + msgProtocol, + "net.mamoe.mirai.internal.message.protocol.impl.PokeMessageProtocol" + ) { net.mamoe.mirai.internal.message.protocol.impl.PokeMessageProtocol() } + Services.register( + msgProtocol, + "net.mamoe.mirai.internal.message.protocol.impl.PttMessageProtocol" + ) { net.mamoe.mirai.internal.message.protocol.impl.PttMessageProtocol() } + Services.register( + msgProtocol, + "net.mamoe.mirai.internal.message.protocol.impl.QuoteReplyProtocol" + ) { net.mamoe.mirai.internal.message.protocol.impl.QuoteReplyProtocol() } + Services.register( + msgProtocol, + "net.mamoe.mirai.internal.message.protocol.impl.RichMessageProtocol" + ) { net.mamoe.mirai.internal.message.protocol.impl.RichMessageProtocol() } + Services.register( + msgProtocol, + "net.mamoe.mirai.internal.message.protocol.impl.TextProtocol" + ) { net.mamoe.mirai.internal.message.protocol.impl.TextProtocol() } + Services.register( + msgProtocol, + "net.mamoe.mirai.internal.message.protocol.impl.VipFaceProtocol" + ) { net.mamoe.mirai.internal.message.protocol.impl.VipFaceProtocol() } + Services.register( + msgProtocol, + "net.mamoe.mirai.internal.message.protocol.impl.ForwardMessageProtocol" + ) { net.mamoe.mirai.internal.message.protocol.impl.ForwardMessageProtocol() } + Services.register( + msgProtocol, + "net.mamoe.mirai.internal.message.protocol.impl.LongMessageProtocol" + ) { net.mamoe.mirai.internal.message.protocol.impl.LongMessageProtocol() } + Services.register( + msgProtocol, + "net.mamoe.mirai.internal.message.protocol.impl.UnsupportedMessageProtocol" + ) { net.mamoe.mirai.internal.message.protocol.impl.UnsupportedMessageProtocol() } + Services.register( + msgProtocol, + "net.mamoe.mirai.internal.message.protocol.impl.GeneralMessageSenderProtocol" + ) { net.mamoe.mirai.internal.message.protocol.impl.GeneralMessageSenderProtocol() } + + + Services.register( + "net.mamoe.mirai.message.data.InternalImageProtocol", + "net.mamoe.mirai.internal.message.image.InternalImageProtocolImpl" + ) { net.mamoe.mirai.internal.message.image.InternalImageProtocolImpl() } + + Services.register( + "net.mamoe.mirai.message.data.OfflineAudio.Factory", + "net.mamoe.mirai.internal.message.data.OfflineAudioFactoryImpl" + ) { net.mamoe.mirai.internal.message.data.OfflineAudioFactoryImpl() } + } +} \ No newline at end of file diff --git a/mirai-core/src/nativeMain/kotlin/utils/PlatformSocket.kt b/mirai-core/src/nativeMain/kotlin/utils/PlatformSocket.kt new file mode 100644 index 000000000..e60e08d15 --- /dev/null +++ b/mirai-core/src/nativeMain/kotlin/utils/PlatformSocket.kt @@ -0,0 +1,79 @@ +/* + * Copyright 2019-2022 Mamoe Technologies and contributors. + * + * 此源代码的使用受 GNU AFFERO GENERAL PUBLIC LICENSE version 3 许可证的约束, 可以在以下链接找到该许可证. + * Use of this source code is governed by the GNU AGPLv3 license that can be found through the following link. + * + * https://github.com/mamoe/mirai/blob/dev/LICENSE + */ + +package net.mamoe.mirai.internal.utils + +import io.ktor.utils.io.core.* +import io.ktor.utils.io.errors.* +import net.mamoe.mirai.internal.network.highway.HighwayProtocolChannel + +/** + * TCP Socket. + */ +internal actual class PlatformSocket : Closeable, HighwayProtocolChannel { + actual val isOpen: Boolean + get() = TODO("Not yet implemented") + + actual override fun close() { + } + + actual suspend fun send(packet: ByteArray, offset: Int, length: Int) { + } + + /** + * @throws SendPacketInternalException + */ + actual override suspend fun send(packet: ByteReadPacket) { + } + + /** + * @throws ReadPacketInternalException + */ + actual override suspend fun read(): ByteReadPacket { + TODO("Not yet implemented") + } + + actual suspend fun connect(serverHost: String, serverPort: Int) { + } + + actual companion object { + actual suspend fun connect( + serverIp: String, + serverPort: Int + ): PlatformSocket { + TODO("Not yet implemented") + } + + actual suspend inline fun withConnection( + serverIp: String, + serverPort: Int, + block: PlatformSocket.() -> R + ): R { + TODO("Not yet implemented") + } + + } + +} + +internal actual class SocketException : IOException { + actual constructor() : super("", null) + + actual constructor(message: String) : super(message) +} + +internal actual class NoRouteToHostException : IOException { + actual constructor() : super("") + actual constructor(message: String) : super(message) +} + +internal actual class UnknownHostException : IOException { + actual constructor() : super("") + actual constructor(message: String) : super(message) +} \ No newline at end of file diff --git a/mirai-core/src/nativeMain/kotlin/utils/RemoteFileImpl.kt b/mirai-core/src/nativeMain/kotlin/utils/RemoteFileImpl.kt new file mode 100644 index 000000000..a598140e8 --- /dev/null +++ b/mirai-core/src/nativeMain/kotlin/utils/RemoteFileImpl.kt @@ -0,0 +1,18 @@ +/* + * Copyright 2019-2022 Mamoe Technologies and contributors. + * + * 此源代码的使用受 GNU AFFERO GENERAL PUBLIC LICENSE version 3 许可证的约束, 可以在以下链接找到该许可证. + * Use of this source code is governed by the GNU AGPLv3 license that can be found through the following link. + * + * https://github.com/mamoe/mirai/blob/dev/LICENSE + */ + +package net.mamoe.mirai.internal.utils + +import net.mamoe.mirai.contact.Group + +internal actual class RemoteFileImpl actual constructor(contact: Group, path: String) : + CommonRemoteFileImpl(contact, path) { + + actual constructor(contact: Group, parent: String, name: String) : this(contact, FileSystem.normalize(parent, name)) +} \ No newline at end of file diff --git a/mirai-core/src/nativeMain/kotlin/utils/crypto/ECDHPrivateKey.kt b/mirai-core/src/nativeMain/kotlin/utils/crypto/ECDHPrivateKey.kt new file mode 100644 index 000000000..36785f9d9 --- /dev/null +++ b/mirai-core/src/nativeMain/kotlin/utils/crypto/ECDHPrivateKey.kt @@ -0,0 +1,86 @@ +/* + * Copyright 2019-2022 Mamoe Technologies and contributors. + * + * 此源代码的使用受 GNU AFFERO GENERAL PUBLIC LICENSE version 3 许可证的约束, 可以在以下链接找到该许可证. + * Use of this source code is governed by the GNU AGPLv3 license that can be found through the following link. + * + * https://github.com/mamoe/mirai/blob/dev/LICENSE + */ + +package net.mamoe.mirai.internal.utils.crypto + +internal actual interface ECDHPrivateKey +internal actual interface ECDHPublicKey { + actual fun getEncoded(): ByteArray +} + +internal actual class ECDHKeyPairImpl( + override val privateKey: ECDHPrivateKey, + override val publicKey: ECDHPublicKey, + override val maskedShareKey: ByteArray, + override val maskedPublicKey: ByteArray +) : ECDHKeyPair + +/** + * 椭圆曲线密码, ECDH 加密 + */ +internal actual class ECDH actual constructor(keyPair: ECDHKeyPair) { + actual val keyPair: ECDHKeyPair + get() = TODO("Not yet implemented") + + /** + * 由 [keyPair] 的私匙和 [peerPublicKey] 计算 shareKey + */ + actual fun calculateShareKeyByPeerPublicKey(peerPublicKey: ECDHPublicKey): ByteArray { + TODO("Not yet implemented") + } + + actual companion object { + actual val isECDHAvailable: Boolean + get() = TODO("Not yet implemented") + + /** + * 由完整的 publicKey ByteArray 得到 [ECDHPublicKey] + */ + actual fun constructPublicKey(key: ByteArray): ECDHPublicKey { + TODO("Not yet implemented") + } + + /** + * 由完整的 rsaKey 校验 publicKey + */ + actual fun verifyPublicKey( + version: Int, + publicKey: String, + publicKeySign: String + ): Boolean { + TODO("Not yet implemented") + } + + /** + * 生成随机密匙对 + */ + actual fun generateKeyPair(initialPublicKey: ECDHPublicKey): ECDHKeyPair { + TODO("Not yet implemented") + } + + /** + * 由一对密匙计算 shareKey + */ + actual fun calculateShareKey( + privateKey: ECDHPrivateKey, + publicKey: ECDHPublicKey + ): ByteArray { + TODO("Not yet implemented") + } + + } + + actual override fun toString(): String { + TODO("Not yet implemented") + } + +} + +internal actual val publicKeyForVerify: ECDHPublicKey + get() = TODO("Not yet implemented") diff --git a/mirai-core/src/nativeMain/kotlin/utils/workingDirPath.kt b/mirai-core/src/nativeMain/kotlin/utils/workingDirPath.kt new file mode 100644 index 000000000..9ee23ec19 --- /dev/null +++ b/mirai-core/src/nativeMain/kotlin/utils/workingDirPath.kt @@ -0,0 +1,17 @@ +/* + * Copyright 2019-2022 Mamoe Technologies and contributors. + * + * 此源代码的使用受 GNU AFFERO GENERAL PUBLIC LICENSE version 3 许可证的约束, 可以在以下链接找到该许可证. + * Use of this source code is governed by the GNU AGPLv3 license that can be found through the following link. + * + * https://github.com/mamoe/mirai/blob/dev/LICENSE + */ + +package net.mamoe.mirai.internal.utils + +import net.mamoe.mirai.utils.BotConfiguration + +internal actual val BotConfiguration.workingDirPath: String + get() = this.workingDir +internal actual val BotConfiguration.cacheDirPath: String + get() = this.cacheDir \ No newline at end of file diff --git a/mirai-core/src/nativeTest/kotlin/network/framework/AbstractCommonNHTest.kt b/mirai-core/src/nativeTest/kotlin/network/framework/AbstractCommonNHTest.kt new file mode 100644 index 000000000..67a3095c7 --- /dev/null +++ b/mirai-core/src/nativeTest/kotlin/network/framework/AbstractCommonNHTest.kt @@ -0,0 +1,35 @@ +/* + * Copyright 2019-2022 Mamoe Technologies and contributors. + * + * 此源代码的使用受 GNU AFFERO GENERAL PUBLIC LICENSE version 3 许可证的约束, 可以在以下链接找到该许可证. + * Use of this source code is governed by the GNU AGPLv3 license that can be found through the following link. + * + * https://github.com/mamoe/mirai/blob/dev/LICENSE + */ + +package net.mamoe.mirai.internal.network.framework + +import net.mamoe.mirai.internal.network.handler.NetworkHandlerFactory + +/** + * Without selector. When network is closed, it will not reconnect, so that you can check for its states. + * + * @see AbstractCommonNHTestWithSelector + */ +internal actual abstract class AbstractCommonNHTest actual constructor() : + AbstractRealNetworkHandlerTest() { + + actual override val network: TestCommonNetworkHandler + get() = TODO("Not yet implemented") + actual override val factory: NetworkHandlerFactory + get() = TODO("Not yet implemented") + + protected actual fun removeOutgoingPacketEncoder() { + } + + actual val conn: PlatformConn + get() = TODO("Not yet implemented") + +} + +internal actual class PlatformConn \ No newline at end of file diff --git a/mirai-core/src/nativeTest/kotlin/package.kt b/mirai-core/src/nativeTest/kotlin/package.kt new file mode 100644 index 000000000..7df5cebc4 --- /dev/null +++ b/mirai-core/src/nativeTest/kotlin/package.kt @@ -0,0 +1,10 @@ +/* + * Copyright 2019-2022 Mamoe Technologies and contributors. + * + * 此源代码的使用受 GNU AFFERO GENERAL PUBLIC LICENSE version 3 许可证的约束, 可以在以下链接找到该许可证. + * Use of this source code is governed by the GNU AGPLv3 license that can be found through the following link. + * + * https://github.com/mamoe/mirai/blob/dev/LICENSE + */ + +package net.mamoe.mirai.internal \ No newline at end of file diff --git a/mirai-core/src/nativeTest/kotlin/test/PlatformInitializationTest.kt b/mirai-core/src/nativeTest/kotlin/test/PlatformInitializationTest.kt new file mode 100644 index 000000000..8f967cd69 --- /dev/null +++ b/mirai-core/src/nativeTest/kotlin/test/PlatformInitializationTest.kt @@ -0,0 +1,46 @@ +/* + * Copyright 2019-2022 Mamoe Technologies and contributors. + * + * 此源代码的使用受 GNU AFFERO GENERAL PUBLIC LICENSE version 3 许可证的约束, 可以在以下链接找到该许可证. + * Use of this source code is governed by the GNU AGPLv3 license that can be found through the following link. + * + * https://github.com/mamoe/mirai/blob/dev/LICENSE + */ + +package net.mamoe.mirai.internal.test + +import net.mamoe.mirai.IMirai +import net.mamoe.mirai.utils.setSystemProp +import kotlin.test.Test + + +internal actual fun initPlatform() { +} + +internal actual class PlatformInitializationTest actual constructor() : AbstractTest() { + @Test + actual fun test() { + } +} + +/** + * All test classes should inherit from [AbstractTest] + */ +internal actual abstract class AbstractTest actual constructor() : CommonAbstractTest() { + + actual companion object { + init { + initPlatform() + + setSystemProp("mirai.network.packet.logger", "true") + setSystemProp("mirai.network.state.observer.logging", "true") + setSystemProp("mirai.network.show.all.components", "true") + setSystemProp("mirai.network.show.components.creation.stacktrace", "true") + setSystemProp("mirai.network.handle.selector.logging", "true") + + Exception() // create a exception to load relevant classes to estimate invocation time of test cases more accurately. + IMirai::class.simpleName // similarly, load classes. + } + } + +} \ No newline at end of file diff --git a/mirai-core/src/nativeTest/kotlin/testFramework/DebugProbes.kt b/mirai-core/src/nativeTest/kotlin/testFramework/DebugProbes.kt new file mode 100644 index 000000000..5bed4554c --- /dev/null +++ b/mirai-core/src/nativeTest/kotlin/testFramework/DebugProbes.kt @@ -0,0 +1,128 @@ +/* + * Copyright 2019-2022 Mamoe Technologies and contributors. + * + * 此源代码的使用受 GNU AFFERO GENERAL PUBLIC LICENSE version 3 许可证的约束, 可以在以下链接找到该许可证. + * Use of this source code is governed by the GNU AGPLv3 license that can be found through the following link. + * + * https://github.com/mamoe/mirai/blob/dev/LICENSE + */ + +package net.mamoe.mirai.internal.testFramework + +import kotlinx.coroutines.CoroutineScope +import kotlinx.coroutines.Job + +internal actual object DebugProbes { + + /** + * Prints [job] hierarchy representation from [jobToString] to the given [out]. + */ +// public fun printJob(job: Job, out: PrintStream = System.out): Unit + + /** + * Prints all coroutines launched within the given [scope]. + * Throws [IllegalStateException] if the scope has no a job in it. + */ +// public fun printScope(scope: CoroutineScope, out: PrintStream = System.out): Unit + + /** + * Returns all existing coroutines info. + * The resulting collection represents a consistent snapshot of all existing coroutines at the moment of invocation. + */ +// public fun dumpCoroutinesInfo(): List + /** + * Whether coroutine creation stack traces should be sanitized. + * Sanitization removes all frames from `kotlinx.coroutines` package except + * the first one and the last one to simplify diagnostic. + */ + actual var sanitizeStackTraces: Boolean + get() = false + set(value) {} + + /** + * Whether coroutine creation stack traces should be captured. + * When enabled, for each created coroutine a stack trace of the current + * thread is captured and attached to the coroutine. + * This option can be useful during local debug sessions, but is recommended + * to be disabled in production environments to avoid stack trace dumping overhead. + */ + actual var enableCreationStackTraces: Boolean + get() = false + set(value) {} + + /** + * Determines whether debug probes were [installed][DebugProbes.install]. + */ + actual val isInstalled: Boolean + get() = false + + /** + * Installs a [DebugProbes] instead of no-op stdlib probes by redefining + * debug probes class using the same class loader as one loaded [DebugProbes] class. + */ + actual fun install() { + } + + /** + * Uninstall debug probes. + */ + actual fun uninstall() { + } + + /** + * Invokes given block of code with installed debug probes and uninstall probes in the end. + */ + actual inline fun withDebugProbes(block: () -> Unit) { + } + + /** + * Returns string representation of the coroutines [job] hierarchy with additional debug information. + * Hierarchy is printed from the [job] as a root transitively to all children. + */ + actual fun jobToString(job: Job): String { + return "" + } + + /** + * Returns string representation of all coroutines launched within the given [scope]. + * Throws [IllegalStateException] if the scope has no a job in it. + */ + actual fun scopeToString(scope: CoroutineScope): String { + return "" + } + + /** + * Dumps all active coroutines into the given output stream, providing a consistent snapshot of all existing coroutines at the moment of invocation. + * The output of this method is similar to `jstack` or a full thread dump. It can be used as the replacement to + * "Dump threads" action. + * + * Example of the output: + * ``` + * Coroutines dump 2018/11/12 19:45:14 + * + * Coroutine "coroutine#42":StandaloneCoroutine{Active}@58fdd99, state: SUSPENDED + * at MyClass$awaitData.invokeSuspend(MyClass.kt:37) + * (Coroutine creation stacktrace) + * at MyClass.createIoRequest(MyClass.kt:142) + * at MyClass.fetchData(MyClass.kt:154) + * at MyClass.showData(MyClass.kt:31) + * ... + * ``` + */ + actual fun dumpCoroutines() { + } + + /** + * Prints [job] hierarchy representation from [jobToString] to the given [out]. + */ + actual fun printJob(job: Job) { + } + + /** + * Prints all coroutines launched within the given [scope]. + * Throws [IllegalStateException] if the scope has no a job in it. + */ + actual fun printScope(scope: CoroutineScope) { + } + +} \ No newline at end of file