Просмотр исходного кода

Simplify platform structure: merge jvmMain into commonMain

Him188 5 лет назад
Родитель
Сommit
eafca6d4ed
36 измененных файлов с 293 добавлено и 420 удалено
  1. 6 17
      mirai-core-api/build.gradle.kts
  2. 0 0
      mirai-core-api/src/commonMain/kotlin/event/JvmMethodListeners.kt
  3. 2 3
      mirai-core-api/src/commonMain/kotlin/event/events/friend.kt
  4. 3 3
      mirai-core-api/src/commonMain/kotlin/event/events/group.kt
  5. 13 8
      mirai-core-api/src/commonMain/kotlin/event/internal/InternalEventListeners.kt
  6. 6 4
      mirai-core-api/src/commonMain/kotlin/event/internal/MiraiAtomicBoolean.kt
  7. 14 1
      mirai-core-api/src/commonMain/kotlin/utils/DeviceInfo.kt
  8. 2 2
      mirai-core-api/src/commonMain/kotlin/utils/FileLogger.kt
  9. 0 0
      mirai-core-api/src/commonMain/kotlin/utils/HyperLinkLabel.kt
  10. 0 0
      mirai-core-api/src/commonMain/kotlin/utils/LoggerAdapters.kt
  11. 202 3
      mirai-core-api/src/commonMain/kotlin/utils/LoginSolver.kt
  12. 7 4
      mirai-core-api/src/commonMain/kotlin/utils/MiraiLogger.kt
  13. 1 4
      mirai-core-api/src/commonMain/kotlin/utils/OverFileSizeMaxException.kt
  14. 5 1
      mirai-core-api/src/commonMain/kotlin/utils/SwingSolver.kt
  15. 2 5
      mirai-core-api/src/commonMain/kotlin/utils/TimeUtils.kt
  16. 0 0
      mirai-core-api/src/commonMain/kotlin/utils/WindowHelperJvm.kt
  17. 0 0
      mirai-core-api/src/commonMain/kotlin/utils/internal/DeferredReusableInput.jvm.kt
  18. 0 14
      mirai-core-api/src/commonMain/kotlin/utils/internal/asReusableInput.common.kt
  19. 3 1
      mirai-core-api/src/commonMain/kotlin/utils/internal/asReusableInput.kt
  20. 0 0
      mirai-core-api/src/commonMain/kotlin/utils/internal/logging/JdkLogger.kt
  21. 0 0
      mirai-core-api/src/commonMain/kotlin/utils/internal/logging/Log4jLogger.kt
  22. 0 0
      mirai-core-api/src/commonMain/kotlin/utils/internal/logging/Slf4jLogger.kt
  23. 0 0
      mirai-core-api/src/commonMain/kotlin/utils/internal/md5.jvm.kt
  24. 0 0
      mirai-core-api/src/commonMain/kotlin/utils/internal/runBlocking.kt
  25. 0 32
      mirai-core-api/src/jvmMain/kotlin/event/internal/GlobalEventListeners.kt
  26. 0 25
      mirai-core-api/src/jvmMain/kotlin/utils/DeviceInfoUtils.kt
  27. 0 227
      mirai-core-api/src/jvmMain/kotlin/utils/LoginSolver.jvm.kt
  28. 0 15
      mirai-core-api/src/jvmMain/kotlin/utils/OverFileSizeMaxException.kt
  29. 2 3
      mirai-core-api/src/jvmMain/kotlin/utils/PlatformLogger.jvm.kt
  30. 0 20
      mirai-core-api/src/jvmMain/kotlin/utils/PlatformUtilsJvm.kt
  31. 9 10
      mirai-core/src/commonMain/kotlin/MiraiImpl.kt
  32. 5 5
      mirai-core/src/commonMain/kotlin/message/incomingSourceImpl.kt
  33. 3 3
      mirai-core/src/commonMain/kotlin/message/offlineSourceImpl.kt
  34. 4 4
      mirai-core/src/commonMain/kotlin/message/outgoingSourceImpl.kt
  35. 3 3
      mirai-core/src/commonMain/kotlin/network/protocol/packet/Tlv.kt
  36. 1 3
      mirai-core/src/commonMain/kotlin/utils/AtomicResizeCacheList.kt

+ 6 - 17
mirai-core-api/build.gradle.kts

@@ -75,8 +75,8 @@ kotlin {
                 api1(`kotlinx-serialization-core`)
                 api1(`kotlinx-serialization-json`)
                 implementation1(`kotlinx-serialization-protobuf`)
-                api1(`kotlinx-io`)
-                api1(`kotlinx-coroutines-io`)
+                api1(`kotlinx-io-jvm`)
+                api1(`kotlinx-coroutines-io-jvm`)
                 api(`kotlinx-coroutines-core`)
 
                 implementation1(`kotlinx-atomicfu`)
@@ -84,32 +84,21 @@ kotlin {
                 api1(`ktor-client-cio`)
                 api1(`ktor-client-core`)
                 api1(`ktor-network`)
+
+                compileOnly(`log4j-api`)
+                compileOnly(slf4j)
             }
         }
 
         if (isAndroidSDKAvailable) {
             androidMain {
                 dependencies {
-                    api(kotlin("reflect"))
-
-                    api1(`kotlinx-io-jvm`)
-                    api1(`kotlinx-coroutines-io-jvm`)
-
                     api1(`ktor-client-android`)
                 }
             }
         }
 
-        val jvmMain by getting {
-            dependencies {
-                api(kotlin("reflect"))
-                compileOnly(`log4j-api`)
-                compileOnly(slf4j)
-
-                api1(`kotlinx-io-jvm`)
-                api1(`kotlinx-coroutines-io-jvm`)
-            }
-        }
+        val jvmMain by getting
 
         val jvmTest by getting {
             dependencies {

+ 0 - 0
mirai-core-api/src/jvmMain/kotlin/event/JvmMethodListeners.kt → mirai-core-api/src/commonMain/kotlin/event/JvmMethodListeners.kt


+ 2 - 3
mirai-core-api/src/commonMain/kotlin/event/events/friend.kt

@@ -20,13 +20,12 @@ import net.mamoe.mirai.contact.Friend
 import net.mamoe.mirai.contact.Group
 import net.mamoe.mirai.contact.User
 import net.mamoe.mirai.event.AbstractEvent
-import net.mamoe.mirai.event.internal.MiraiAtomicBoolean
 import net.mamoe.mirai.internal.network.Packet
 import net.mamoe.mirai.message.action.Nudge
 import net.mamoe.mirai.utils.MiraiExperimentalApi
 import net.mamoe.mirai.utils.SinceMirai
 import net.mamoe.mirai.utils.internal.runBlocking
-import kotlin.jvm.*
+import java.util.concurrent.atomic.AtomicBoolean
 
 
 /**
@@ -90,7 +89,7 @@ public data class NewFriendRequestEvent internal constructor(
     public val fromNick: String
 ) : BotEvent, Packet, AbstractEvent() {
     @JvmField
-    internal val responded: MiraiAtomicBoolean = MiraiAtomicBoolean(false)
+    internal val responded: AtomicBoolean = AtomicBoolean(false)
 
     /**
      * @return 申请人来自的群. 当申请人来自其他途径申请时为 `null`

+ 3 - 3
mirai-core-api/src/commonMain/kotlin/event/events/group.kt

@@ -22,12 +22,12 @@ import net.mamoe.mirai.contact.Member
 import net.mamoe.mirai.contact.MemberPermission
 import net.mamoe.mirai.event.AbstractEvent
 import net.mamoe.mirai.event.BroadcastControllable
-import net.mamoe.mirai.event.internal.MiraiAtomicBoolean
 import net.mamoe.mirai.internal.network.Packet
 import net.mamoe.mirai.message.action.Nudge
 import net.mamoe.mirai.utils.MiraiExperimentalApi
 import net.mamoe.mirai.utils.SinceMirai
 import net.mamoe.mirai.utils.internal.runBlocking
+import java.util.concurrent.atomic.AtomicBoolean
 import kotlin.internal.LowPriorityInOverloadResolution
 import kotlin.jvm.*
 
@@ -339,7 +339,7 @@ public data class BotInvitedJoinGroupRequestEvent internal constructor(
     public val invitor: Friend get() = this.bot.getFriend(invitorId)
 
     @JvmField
-    internal val responded: MiraiAtomicBoolean = MiraiAtomicBoolean(false)
+    internal val responded: AtomicBoolean = AtomicBoolean(false)
 
     @JvmSynthetic
     public suspend fun accept(): Unit = Mirai.acceptInvitedJoinGroupRequest(this)
@@ -387,7 +387,7 @@ public data class MemberJoinRequestEvent internal constructor(
 
     @JvmField
     @PublishedApi
-    internal val responded: MiraiAtomicBoolean = MiraiAtomicBoolean(false)
+    internal val responded: AtomicBoolean = AtomicBoolean(false)
 
     @JvmSynthetic
     public suspend fun accept(): Unit = Mirai.acceptMemberJoinRequest(this)

+ 13 - 8
mirai-core-api/src/commonMain/kotlin/event/internal/InternalEventListeners.kt

@@ -17,9 +17,9 @@ import net.mamoe.mirai.event.events.BotEvent
 import net.mamoe.mirai.utils.LockFreeLinkedList
 import net.mamoe.mirai.utils.MiraiLogger
 import net.mamoe.mirai.utils.PlannedRemoval
+import java.util.*
 import kotlin.coroutines.CoroutineContext
 import kotlin.coroutines.coroutineContext
-import kotlin.jvm.JvmField
 import kotlin.reflect.KClass
 
 
@@ -112,16 +112,21 @@ internal class ListenerRegistry(
     val type: KClass<out Event>
 )
 
-internal expect object GlobalEventListeners {
-    operator fun get(priority: Listener.EventPriority): LockFreeLinkedList<ListenerRegistry>
-}
 
-@PublishedApi
-internal expect class MiraiAtomicBoolean(initial: Boolean) {
+internal object GlobalEventListeners {
+    private val ALL_LEVEL_REGISTRIES: Map<Listener.EventPriority, LockFreeLinkedList<ListenerRegistry>>
 
-    fun compareAndSet(expect: Boolean, update: Boolean): Boolean
+    init {
+        val map =
+            EnumMap<Listener.EventPriority, LockFreeLinkedList<ListenerRegistry>>(Listener.EventPriority::class.java)
+        Listener.EventPriority.values().forEach {
+            map[it] = LockFreeLinkedList()
+        }
+        this.ALL_LEVEL_REGISTRIES = map
+    }
 
-    var value: Boolean
+    operator fun get(priority: Listener.EventPriority): LockFreeLinkedList<ListenerRegistry> =
+        ALL_LEVEL_REGISTRIES[priority]!!
 }
 
 

+ 6 - 4
mirai-core-api/src/jvmMain/kotlin/event/internal/MiraiAtomicBoolean.kt → mirai-core-api/src/commonMain/kotlin/event/internal/MiraiAtomicBoolean.kt

@@ -11,16 +11,18 @@ package net.mamoe.mirai.event.internal
 
 import java.util.concurrent.atomic.AtomicBoolean
 
-internal actual class MiraiAtomicBoolean actual constructor(initial: Boolean) {
+
+@PublishedApi
+internal class MiraiAtomicBoolean constructor(initial: Boolean) {
     private val delegate: AtomicBoolean = AtomicBoolean(initial)
 
-    actual fun compareAndSet(expect: Boolean, update: Boolean): Boolean {
+    fun compareAndSet(expect: Boolean, update: Boolean): Boolean {
         return delegate.compareAndSet(expect, update)
     }
 
-    actual var value: Boolean
+    var value: Boolean
         get() = delegate.get()
         set(value) {
             delegate.set(value)
         }
-}
+}

+ 14 - 1
mirai-core-api/src/commonMain/kotlin/utils/DeviceInfo.kt

@@ -11,13 +11,26 @@ package net.mamoe.mirai.utils
 
 import kotlinx.io.core.toByteArray
 import kotlinx.serialization.Serializable
+import kotlinx.serialization.json.Json
 import kotlinx.serialization.protobuf.ProtoBuf
 import kotlinx.serialization.protobuf.ProtoNumber
 import net.mamoe.mirai.utils.internal.getRandomByteArray
 import net.mamoe.mirai.utils.internal.getRandomIntString
 import net.mamoe.mirai.utils.internal.getRandomString
 import net.mamoe.mirai.utils.internal.md5
-import kotlin.jvm.JvmStatic
+import java.io.File
+
+/**
+ * 加载一个设备信息. 若文件不存在或为空则随机并创建一个设备信息保存.
+ */
+public fun File.loadAsDeviceInfo(json: Json): DeviceInfo {
+    if (!this.exists() || this.length() == 0L) {
+        return DeviceInfo.random().also {
+            this.writeText(json.encodeToString(DeviceInfo.serializer(), it))
+        }
+    }
+    return json.decodeFromString(DeviceInfo.serializer(), this.readText())
+}
 
 @Serializable
 public class DeviceInfo(

+ 2 - 2
mirai-core-api/src/jvmMain/kotlin/utils/FileLogger.kt → mirai-core-api/src/commonMain/kotlin/utils/FileLogger.kt

@@ -25,7 +25,7 @@ public class SingleFileLogger @JvmOverloads constructor(
     identity: String,
     file: File = File("$identity-$currentDate.log")
 ) :
-    PlatformLogger(identity, { file.appendText(it + "\n") }, false) {
+    PlatformLogger(identity, { file.appendText(it + "\n") }) {
 
     init {
         file.createNewFile()
@@ -55,7 +55,7 @@ public class DirectoryLogger @JvmOverloads constructor(
     }
 
     private fun checkOutdated() {
-        val current = currentTimeMillis
+        val current = currentTimeMillis()
         directory.walk().filter(File::isFile).filter { current - it.lastModified() > retain }.forEach {
             it.delete()
         }

+ 0 - 0
mirai-core-api/src/jvmMain/kotlin/utils/HyperLinkLabel.kt → mirai-core-api/src/commonMain/kotlin/utils/HyperLinkLabel.kt


+ 0 - 0
mirai-core-api/src/jvmMain/kotlin/utils/LoggerAdapters.kt → mirai-core-api/src/commonMain/kotlin/utils/LoggerAdapters.kt


+ 202 - 3
mirai-core-api/src/commonMain/kotlin/utils/LoginSolver.kt

@@ -9,13 +9,29 @@
 
 package net.mamoe.mirai.utils
 
+import kotlinx.coroutines.CoroutineName
+import kotlinx.coroutines.Dispatchers
+import kotlinx.coroutines.GlobalScope
+import kotlinx.coroutines.io.*
+import kotlinx.coroutines.io.jvm.nio.copyTo
+import kotlinx.coroutines.sync.Mutex
+import kotlinx.coroutines.sync.withLock
+import kotlinx.coroutines.withContext
 import net.mamoe.mirai.Bot
 import net.mamoe.mirai.network.LoginFailedException
+import net.mamoe.mirai.network.NoStandardInputForCaptchaException
+import utils.SwingSolver
+import java.awt.Image
+import java.awt.image.BufferedImage
+import java.io.File
+import java.io.RandomAccessFile
+import javax.imageio.ImageIO
+import kotlin.coroutines.CoroutineContext
 
 /**
  * 验证码, 设备锁解决器
  */
-public expect abstract class LoginSolver {
+public abstract class LoginSolver {
     /**
      * 处理图片验证码.
      * 返回 null 以表示无法处理验证码, 将会刷新验证码或重试登录.
@@ -46,6 +62,189 @@ public expect abstract class LoginSolver {
     public abstract suspend fun onSolveUnsafeDeviceLoginVerify(bot: Bot, url: String): String?
 
     public companion object {
-        public val Default: LoginSolver
+        public val Default: LoginSolver =
+            DefaultLoginSolver({ readLine() ?: throw NoStandardInputForCaptchaException(null) })
     }
-}
+}
+
+
+/**
+ * 自动选择 [SwingSolver] 或 [StandardCharImageLoginSolver]
+ */
+@MiraiExperimentalApi
+public class DefaultLoginSolver(
+    public val input: suspend () -> String,
+    public val overrideLogger: MiraiLogger? = null
+) : LoginSolver() {
+    private val delegate: LoginSolver
+
+    init {
+        if (WindowHelperJvm.isDesktopSupported) {
+            delegate = SwingSolver
+        } else {
+            delegate = StandardCharImageLoginSolver(input, overrideLogger)
+        }
+    }
+
+    override suspend fun onSolvePicCaptcha(bot: Bot, data: ByteArray): String? {
+        return delegate.onSolvePicCaptcha(bot, data)
+    }
+
+    override suspend fun onSolveSliderCaptcha(bot: Bot, url: String): String? {
+        return delegate.onSolveSliderCaptcha(bot, url)
+    }
+
+    override suspend fun onSolveUnsafeDeviceLoginVerify(bot: Bot, url: String): String? {
+        return delegate.onSolveUnsafeDeviceLoginVerify(bot, url)
+    }
+}
+
+/**
+ * 使用字符图片展示验证码, 使用 [input] 获取输入, 使用 [overrideLogger] 输出
+ */
+@MiraiExperimentalApi
+public class StandardCharImageLoginSolver(
+    input: suspend () -> String,
+    /**
+     * 为 `null` 时使用 [Bot.logger]
+     */
+    private val overrideLogger: MiraiLogger? = null
+) : LoginSolver() {
+    private val input: suspend () -> String = suspend {
+        withContext(Dispatchers.IO) { input() }
+    }
+
+    override suspend fun onSolvePicCaptcha(bot: Bot, data: ByteArray): String? = loginSolverLock.withLock {
+        val logger = overrideLogger ?: bot.logger
+        val tempFile: File = createTempFile(suffix = ".png").apply { deleteOnExit() }
+        withContext(Dispatchers.IO) {
+            tempFile.createNewFile()
+            logger.info("需要图片验证码登录, 验证码为 4 字母")
+            try {
+                tempFile.writeChannel().apply { writeFully(data); close() }
+                logger.info("将会显示字符图片. 若看不清字符图片, 请查看文件 ${tempFile.absolutePath}")
+            } catch (e: Exception) {
+                logger.info("无法写出验证码文件(${e.message}), 请尝试查看以上字符图片")
+            }
+
+            tempFile.inputStream().use {
+                try {
+                    val img = ImageIO.read(it)
+                    if (img == null) {
+                        logger.info("无法创建字符图片. 请查看文件")
+                    } else {
+                        logger.info("\n" + img.createCharImg())
+                    }
+                } catch (throwable: Throwable) {
+                    logger.info("创建字符图片时出错($throwable)。请查看文件")
+                }
+            }
+        }
+        logger.info("请输入 4 位字母验证码. 若要更换验证码, 请直接回车")
+        return input().takeUnless { it.isEmpty() || it.length != 4 }.also {
+            logger.info("正在提交[$it]中...")
+        }
+    }
+
+    override suspend fun onSolveSliderCaptcha(bot: Bot, url: String): String = loginSolverLock.withLock {
+        val logger = overrideLogger ?: bot.logger
+        logger.info("需要滑动验证码")
+        logger.info("请在任意浏览器中打开以下链接并完成验证码. ")
+        logger.info("完成后请输入任意字符 ")
+        logger.info(url)
+        return input().also {
+            logger.info("正在提交中...")
+        }
+    }
+
+    override suspend fun onSolveUnsafeDeviceLoginVerify(bot: Bot, url: String): String? = loginSolverLock.withLock {
+        val logger = overrideLogger ?: bot.logger
+        logger.info("需要进行账户安全认证")
+        logger.info("该账户有[设备锁]/[不常用登录地点]/[不常用设备登录]的问题")
+        logger.info("完成以下账号认证即可成功登录|理论本认证在mirai每个账户中最多出现1次")
+        logger.info("请将该链接在QQ浏览器中打开并完成认证, 成功后输入任意字符")
+        logger.info("这步操作将在后续的版本中优化")
+        logger.info(url)
+        return input().also {
+            logger.info("正在提交中...")
+        }
+    }
+}
+
+///////////////////////////////
+//////////////// internal
+///////////////////////////////
+
+internal fun BotConfiguration.getFileBasedDeviceInfoSupplier(filename: String): (Context) -> DeviceInfo {
+    return {
+        File(filename).loadAsDeviceInfo(json)
+    }
+}
+
+// Copied from Ktor CIO
+private fun File.writeChannel(
+    coroutineContext: CoroutineContext = Dispatchers.IO
+): ByteWriteChannel = GlobalScope.reader(CoroutineName("file-writer") + coroutineContext, autoFlush = true) {
+    @Suppress("BlockingMethodInNonBlockingContext")
+    RandomAccessFile(this@writeChannel, "rw").use { file ->
+        val copied = channel.copyTo(file.channel)
+        file.setLength(copied) // truncate tail that could remain from the previously written data
+    }
+}.channel
+
+private val loginSolverLock = Mutex()
+
+/**
+ * @author NaturalHG
+ */
+private fun BufferedImage.createCharImg(outputWidth: Int = 100, ignoreRate: Double = 0.95): String {
+    val newHeight = (this.height * (outputWidth.toDouble() / this.width)).toInt()
+    val tmp = this.getScaledInstance(outputWidth, newHeight, Image.SCALE_SMOOTH)
+    val image = BufferedImage(outputWidth, newHeight, BufferedImage.TYPE_INT_ARGB)
+    val g2d = image.createGraphics()
+    g2d.drawImage(tmp, 0, 0, null)
+    fun gray(rgb: Int): Int {
+        val r = rgb and 0xff0000 shr 16
+        val g = rgb and 0x00ff00 shr 8
+        val b = rgb and 0x0000ff
+        return (r * 30 + g * 59 + b * 11 + 50) / 100
+    }
+
+    fun grayCompare(g1: Int, g2: Int): Boolean =
+        kotlin.math.min(g1, g2).toDouble() / kotlin.math.max(g1, g2) >= ignoreRate
+
+    val background = gray(image.getRGB(0, 0))
+
+    return buildString(capacity = height) {
+
+        val lines = mutableListOf<StringBuilder>()
+
+        var minXPos = outputWidth
+        var maxXPos = 0
+
+        for (y in 0 until image.height) {
+            val builderLine = StringBuilder()
+            for (x in 0 until image.width) {
+                val gray = gray(image.getRGB(x, y))
+                if (grayCompare(gray, background)) {
+                    builderLine.append(" ")
+                } else {
+                    builderLine.append("#")
+                    if (x < minXPos) {
+                        minXPos = x
+                    }
+                    if (x > maxXPos) {
+                        maxXPos = x
+                    }
+                }
+            }
+            if (builderLine.toString().isBlank()) {
+                continue
+            }
+            lines.add(builderLine)
+        }
+        for (line in lines) {
+            append(line.substring(minXPos, maxXPos)).append("\n")
+        }
+    }
+}

+ 7 - 4
mirai-core-api/src/commonMain/kotlin/utils/MiraiLogger.kt

@@ -14,9 +14,6 @@
 package net.mamoe.mirai.utils
 
 import net.mamoe.mirai.Bot
-import kotlin.jvm.JvmMultifileClass
-import kotlin.jvm.JvmName
-import kotlin.jvm.JvmOverloads
 
 
 /**
@@ -229,7 +226,13 @@ public inline fun MiraiLogger.error(lazyMessage: () -> String?, e: Throwable?) {
  *
  * @see DefaultLogger
  */
-public expect open class PlatformLogger @JvmOverloads constructor(identity: String? = "Mirai") : MiraiLoggerPlatformBase
+public expect open class PlatformLogger constructor(
+    identity: String? = "Mirai",
+    output: (String) -> Unit, // TODO: 2020/11/30 review logs, currently it's just for compile
+) : MiraiLoggerPlatformBase {
+    @JvmOverloads
+    public constructor(identity: String? = "Mirai")
+}
 
 
 /**

+ 1 - 4
mirai-core-api/src/commonMain/kotlin/utils/OverFileSizeMaxException.kt

@@ -12,10 +12,7 @@
 
 package net.mamoe.mirai.utils
 
-import kotlin.jvm.JvmMultifileClass
-import kotlin.jvm.JvmName
-
 /**
  * 图片文件过大
  */ // 不要删除多平台结构, 这是 kotlin 的 bug
-public expect class OverFileSizeMaxException() : IllegalStateException
+public class OverFileSizeMaxException : IllegalStateException()

+ 5 - 1
mirai-core-api/src/jvmMain/kotlin/utils/LoginSolver.swing.jvm.kt → mirai-core-api/src/commonMain/kotlin/utils/SwingSolver.kt

@@ -7,9 +7,13 @@
  *  https://github.com/mamoe/mirai/blob/master/LICENSE
  */
 
-package net.mamoe.mirai.utils
+package utils
 
 import net.mamoe.mirai.Bot
+import net.mamoe.mirai.utils.HyperLinkLabel
+import net.mamoe.mirai.utils.LoginSolver
+import net.mamoe.mirai.utils.MiraiExperimentalApi
+import net.mamoe.mirai.utils.openWindow
 import java.awt.Desktop
 import java.net.URI
 import javax.imageio.ImageIO

+ 2 - 5
mirai-core-api/src/commonMain/kotlin/utils/TimeUtils.kt

@@ -12,9 +12,6 @@
 
 package net.mamoe.mirai.utils
 
-import kotlin.jvm.JvmMultifileClass
-import kotlin.jvm.JvmName
-import kotlin.jvm.JvmSynthetic
 import kotlin.math.floor
 import kotlin.time.Duration
 import kotlin.time.DurationUnit
@@ -23,11 +20,11 @@ import kotlin.time.ExperimentalTime
 /**
  * 时间戳
  */
-public expect val currentTimeMillis: Long
+public fun currentTimeMillis(): Long = System.currentTimeMillis()
 
 @get:JvmSynthetic
 public inline val currentTimeSeconds: Long
-    get() = currentTimeMillis / 1000
+    get() = currentTimeMillis() / 1000
 
 
 // 临时使用, 待 Kotlin Duration 稳定后使用 Duration.

+ 0 - 0
mirai-core-api/src/jvmMain/kotlin/utils/WindowHelperJvm.kt → mirai-core-api/src/commonMain/kotlin/utils/WindowHelperJvm.kt


+ 0 - 0
mirai-core-api/src/jvmMain/kotlin/utils/internal/DeferredReusableInput.jvm.kt → mirai-core-api/src/commonMain/kotlin/utils/internal/DeferredReusableInput.jvm.kt


+ 0 - 14
mirai-core-api/src/commonMain/kotlin/utils/internal/asReusableInput.common.kt

@@ -1,14 +0,0 @@
-/*
- * Copyright 2019-2020 Mamoe Technologies and contributors.
- *
- *  此源代码的使用受 GNU AFFERO GENERAL 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
- */
-
-package net.mamoe.mirai.utils.internal
-
-internal expect fun ByteArray.asReusableInput(): ReusableInput
-
-internal fun asReusableInput0(input: ByteArray): ReusableInput = input.asReusableInput()

+ 3 - 1
mirai-core-api/src/jvmMain/kotlin/utils/internal/asReusableInput.jvm.kt → mirai-core-api/src/commonMain/kotlin/utils/internal/asReusableInput.kt

@@ -20,9 +20,11 @@ import java.io.ByteArrayInputStream
 import java.io.File
 import java.io.InputStream
 
+internal fun asReusableInput0(input: ByteArray): ReusableInput = input.asReusableInput()
+
 internal const val DEFAULT_REUSABLE_INPUT_BUFFER_SIZE = 8192
 
-internal actual fun ByteArray.asReusableInput(): ReusableInput {
+internal fun ByteArray.asReusableInput(): ReusableInput {
     return object : ReusableInput {
         override val md5: ByteArray = md5()
         override val size: Long get() = [email protected]()

+ 0 - 0
mirai-core-api/src/jvmMain/kotlin/utils/internal/logging/JdkLogger.kt → mirai-core-api/src/commonMain/kotlin/utils/internal/logging/JdkLogger.kt


+ 0 - 0
mirai-core-api/src/jvmMain/kotlin/utils/internal/logging/Log4jLogger.kt → mirai-core-api/src/commonMain/kotlin/utils/internal/logging/Log4jLogger.kt


+ 0 - 0
mirai-core-api/src/jvmMain/kotlin/utils/internal/logging/Slf4jLogger.kt → mirai-core-api/src/commonMain/kotlin/utils/internal/logging/Slf4jLogger.kt


+ 0 - 0
mirai-core-api/src/jvmMain/kotlin/utils/internal/md5.jvm.kt → mirai-core-api/src/commonMain/kotlin/utils/internal/md5.jvm.kt


+ 0 - 0
mirai-core-api/src/jvmMain/kotlin/utils/internal/runBlocking.kt → mirai-core-api/src/commonMain/kotlin/utils/internal/runBlocking.kt


+ 0 - 32
mirai-core-api/src/jvmMain/kotlin/event/internal/GlobalEventListeners.kt

@@ -1,32 +0,0 @@
-/*
- * Copyright 2019-2020 Mamoe Technologies and contributors.
- *
- *  此源代码的使用受 GNU AFFERO GENERAL 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
- */
-
-package net.mamoe.mirai.event.internal
-
-import net.mamoe.mirai.event.Listener
-import net.mamoe.mirai.utils.LockFreeLinkedList
-import java.util.*
-
-
-internal actual object GlobalEventListeners {
-    private val ALL_LEVEL_REGISTRIES: Map<Listener.EventPriority, LockFreeLinkedList<ListenerRegistry>>
-
-    init {
-        val map =
-            EnumMap<Listener.EventPriority, LockFreeLinkedList<ListenerRegistry>>(Listener.EventPriority::class.java)
-        Listener.EventPriority.values().forEach {
-            map[it] = LockFreeLinkedList()
-        }
-        this.ALL_LEVEL_REGISTRIES = map
-    }
-
-    actual operator fun get(priority: Listener.EventPriority): LockFreeLinkedList<ListenerRegistry> =
-        ALL_LEVEL_REGISTRIES[priority]!!
-
-}

+ 0 - 25
mirai-core-api/src/jvmMain/kotlin/utils/DeviceInfoUtils.kt

@@ -1,25 +0,0 @@
-/*
- * Copyright 2019-2020 Mamoe Technologies and contributors.
- *
- *  此源代码的使用受 GNU AFFERO GENERAL 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
- */
-
-package net.mamoe.mirai.utils
-
-import kotlinx.serialization.json.Json
-import java.io.File
-
-/**
- * 加载一个设备信息. 若文件不存在或为空则随机并创建一个设备信息保存.
- */
-public fun File.loadAsDeviceInfo(json: Json): DeviceInfo {
-    if (!this.exists() || this.length() == 0L) {
-        return DeviceInfo.random().also {
-            this.writeText(json.encodeToString(DeviceInfo.serializer(), it))
-        }
-    }
-    return json.decodeFromString(DeviceInfo.serializer(), this.readText())
-}

+ 0 - 227
mirai-core-api/src/jvmMain/kotlin/utils/LoginSolver.jvm.kt

@@ -1,227 +0,0 @@
-/*
- * Copyright 2019-2020 Mamoe Technologies and contributors.
- *
- *  此源代码的使用受 GNU AFFERO GENERAL 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
- */
-
-package net.mamoe.mirai.utils
-
-import kotlinx.coroutines.CoroutineName
-import kotlinx.coroutines.Dispatchers
-import kotlinx.coroutines.GlobalScope
-import kotlinx.coroutines.io.ByteWriteChannel
-import kotlinx.coroutines.io.close
-import kotlinx.coroutines.io.jvm.nio.copyTo
-import kotlinx.coroutines.io.reader
-import kotlinx.coroutines.io.writeFully
-import kotlinx.coroutines.sync.Mutex
-import kotlinx.coroutines.sync.withLock
-import kotlinx.coroutines.withContext
-import kotlinx.io.core.use
-import net.mamoe.mirai.Bot
-import net.mamoe.mirai.network.NoStandardInputForCaptchaException
-import java.awt.Image
-import java.awt.image.BufferedImage
-import java.io.File
-import java.io.RandomAccessFile
-import javax.imageio.ImageIO
-import kotlin.coroutines.CoroutineContext
-
-/**
- * 自动选择 [SwingSolver] 或 [StandardCharImageLoginSolver]
- */
-@MiraiExperimentalApi
-public class DefaultLoginSolver(
-    public val input: suspend () -> String,
-    public val overrideLogger: MiraiLogger? = null
-) : LoginSolver() {
-    private val delegate: LoginSolver
-
-    init {
-        if (WindowHelperJvm.isDesktopSupported) {
-            delegate = SwingSolver
-        } else {
-            delegate = StandardCharImageLoginSolver(input, overrideLogger)
-        }
-    }
-
-    override suspend fun onSolvePicCaptcha(bot: Bot, data: ByteArray): String? {
-        return delegate.onSolvePicCaptcha(bot, data)
-    }
-
-    override suspend fun onSolveSliderCaptcha(bot: Bot, url: String): String? {
-        return delegate.onSolveSliderCaptcha(bot, url)
-    }
-
-    override suspend fun onSolveUnsafeDeviceLoginVerify(bot: Bot, url: String): String? {
-        return delegate.onSolveUnsafeDeviceLoginVerify(bot, url)
-    }
-}
-
-/**
- * 使用字符图片展示验证码, 使用 [input] 获取输入, 使用 [overrideLogger] 输出
- */
-@MiraiExperimentalApi
-public class StandardCharImageLoginSolver(
-    input: suspend () -> String,
-    /**
-     * 为 `null` 时使用 [Bot.logger]
-     */
-    private val overrideLogger: MiraiLogger? = null
-) : LoginSolver() {
-    private val input: suspend () -> String = suspend {
-        withContext(Dispatchers.IO) { input() }
-    }
-
-    override suspend fun onSolvePicCaptcha(bot: Bot, data: ByteArray): String? = loginSolverLock.withLock {
-        val logger = overrideLogger ?: bot.logger
-        val tempFile: File = createTempFile(suffix = ".png").apply { deleteOnExit() }
-        withContext(Dispatchers.IO) {
-            tempFile.createNewFile()
-            logger.info("需要图片验证码登录, 验证码为 4 字母")
-            try {
-                tempFile.writeChannel().apply { writeFully(data); close() }
-                logger.info("将会显示字符图片. 若看不清字符图片, 请查看文件 ${tempFile.absolutePath}")
-            } catch (e: Exception) {
-                logger.info("无法写出验证码文件(${e.message}), 请尝试查看以上字符图片")
-            }
-
-            tempFile.inputStream().use {
-                try {
-                    val img = ImageIO.read(it)
-                    if (img == null) {
-                        logger.info("无法创建字符图片. 请查看文件")
-                    } else {
-                        logger.info("\n" + img.createCharImg())
-                    }
-                } catch (throwable: Throwable) {
-                    logger.info("创建字符图片时出错($throwable)。请查看文件")
-                }
-            }
-        }
-        logger.info("请输入 4 位字母验证码. 若要更换验证码, 请直接回车")
-        return input().takeUnless { it.isEmpty() || it.length != 4 }.also {
-            logger.info("正在提交[$it]中...")
-        }
-    }
-
-    override suspend fun onSolveSliderCaptcha(bot: Bot, url: String): String? = loginSolverLock.withLock {
-        val logger = overrideLogger ?: bot.logger
-        logger.info("需要滑动验证码")
-        logger.info("请在任意浏览器中打开以下链接并完成验证码. ")
-        logger.info("完成后请输入任意字符 ")
-        logger.info(url)
-        return input().also {
-            logger.info("正在提交中...")
-        }
-    }
-
-    override suspend fun onSolveUnsafeDeviceLoginVerify(bot: Bot, url: String): String? = loginSolverLock.withLock {
-        val logger = overrideLogger ?: bot.logger
-        logger.info("需要进行账户安全认证")
-        logger.info("该账户有[设备锁]/[不常用登录地点]/[不常用设备登录]的问题")
-        logger.info("完成以下账号认证即可成功登录|理论本认证在mirai每个账户中最多出现1次")
-        logger.info("请将该链接在QQ浏览器中打开并完成认证, 成功后输入任意字符")
-        logger.info("这步操作将在后续的版本中优化")
-        logger.info(url)
-        return input().also {
-            logger.info("正在提交中...")
-        }
-    }
-}
-
-/**
- * 验证码, 设备锁解决器
- */
-public actual abstract class LoginSolver {
-    public actual abstract suspend fun onSolvePicCaptcha(bot: Bot, data: ByteArray): String?
-    public actual abstract suspend fun onSolveSliderCaptcha(bot: Bot, url: String): String?
-    public actual abstract suspend fun onSolveUnsafeDeviceLoginVerify(bot: Bot, url: String): String?
-
-    public actual companion object {
-        public actual val Default: LoginSolver =
-
-            DefaultLoginSolver({ readLine() ?: throw NoStandardInputForCaptchaException(null) })
-    }
-}
-
-///////////////////////////////
-//////////////// internal
-///////////////////////////////
-
-internal fun BotConfiguration.getFileBasedDeviceInfoSupplier(filename: String): ((Context) -> DeviceInfo)? {
-    return {
-        File(filename).loadAsDeviceInfo(json)
-    }
-}
-
-// Copied from Ktor CIO
-private fun File.writeChannel(
-    coroutineContext: CoroutineContext = Dispatchers.IO
-): ByteWriteChannel = GlobalScope.reader(CoroutineName("file-writer") + coroutineContext, autoFlush = true) {
-    @Suppress("BlockingMethodInNonBlockingContext")
-    RandomAccessFile(this@writeChannel, "rw").use { file ->
-        val copied = channel.copyTo(file.channel)
-        file.setLength(copied) // truncate tail that could remain from the previously written data
-    }
-}.channel
-
-private val loginSolverLock = Mutex()
-
-/**
- * @author NaturalHG
- */
-private fun BufferedImage.createCharImg(outputWidth: Int = 100, ignoreRate: Double = 0.95): String {
-    val newHeight = (this.height * (outputWidth.toDouble() / this.width)).toInt()
-    val tmp = this.getScaledInstance(outputWidth, newHeight, Image.SCALE_SMOOTH)
-    val image = BufferedImage(outputWidth, newHeight, BufferedImage.TYPE_INT_ARGB)
-    val g2d = image.createGraphics()
-    g2d.drawImage(tmp, 0, 0, null)
-    fun gray(rgb: Int): Int {
-        val r = rgb and 0xff0000 shr 16
-        val g = rgb and 0x00ff00 shr 8
-        val b = rgb and 0x0000ff
-        return (r * 30 + g * 59 + b * 11 + 50) / 100
-    }
-
-    fun grayCompare(g1: Int, g2: Int): Boolean =
-        kotlin.math.min(g1, g2).toDouble() / kotlin.math.max(g1, g2) >= ignoreRate
-
-    val background = gray(image.getRGB(0, 0))
-
-    return buildString(capacity = height) {
-
-        val lines = mutableListOf<StringBuilder>()
-
-        var minXPos = outputWidth
-        var maxXPos = 0
-
-        for (y in 0 until image.height) {
-            val builderLine = StringBuilder()
-            for (x in 0 until image.width) {
-                val gray = gray(image.getRGB(x, y))
-                if (grayCompare(gray, background)) {
-                    builderLine.append(" ")
-                } else {
-                    builderLine.append("#")
-                    if (x < minXPos) {
-                        minXPos = x
-                    }
-                    if (x > maxXPos) {
-                        maxXPos = x
-                    }
-                }
-            }
-            if (builderLine.toString().isBlank()) {
-                continue
-            }
-            lines.add(builderLine)
-        }
-        for (line in lines) {
-            append(line.substring(minXPos, maxXPos)).append("\n")
-        }
-    }
-}

+ 0 - 15
mirai-core-api/src/jvmMain/kotlin/utils/OverFileSizeMaxException.kt

@@ -1,15 +0,0 @@
-/*
- * Copyright 2019-2020 Mamoe Technologies and contributors.
- *
- *  此源代码的使用受 GNU AFFERO GENERAL 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
- */
-
-package net.mamoe.mirai.utils
-
-/**
- * 图片文件过大
- */
-public actual class OverFileSizeMaxException : IllegalStateException()

+ 2 - 3
mirai-core-api/src/jvmMain/kotlin/utils/PlatformLogger.jvm.kt

@@ -13,8 +13,6 @@
 
 package net.mamoe.mirai.utils
 
-import java.io.ByteArrayOutputStream
-import java.io.PrintStream
 import java.text.DateFormat
 import java.text.SimpleDateFormat
 import java.util.*
@@ -44,7 +42,7 @@ import java.util.*
  * @see SingleFileLogger 使用单一文件记录日志
  * @see DirectoryLogger 在一个目录中按日期存放文件记录日志, 自动清理过期日志
  */
-public actual open class PlatformLogger @JvmOverloads constructor(
+public actual open class PlatformLogger constructor(
     public override val identity: String? = "Mirai",
     /**
      * 日志输出. 不会自动添加换行
@@ -53,6 +51,7 @@ public actual open class PlatformLogger @JvmOverloads constructor(
     public val isColored: Boolean = true
 ) : MiraiLoggerPlatformBase() {
     public actual constructor(identity: String?) : this(identity, ::println)
+    public actual constructor(identity: String?, output: (String) -> Unit) : this(identity, output, true)
 
     /**
      * 输出一条日志. [message] 末尾可能不带换行符.

+ 0 - 20
mirai-core-api/src/jvmMain/kotlin/utils/PlatformUtilsJvm.kt

@@ -1,20 +0,0 @@
-/*
- * Copyright 2019-2020 Mamoe Technologies and contributors.
- *
- *  此源代码的使用受 GNU AFFERO GENERAL 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
- */
-
-@file:JvmMultifileClass
-@file:JvmName("Utils")
-@file:Suppress("EXPERIMENTAL_API_USAGE", "NOTHING_TO_INLINE")
-
-package net.mamoe.mirai.utils
-
-/**
- * 时间戳
- */
-public actual val currentTimeMillis: Long
-    get() = System.currentTimeMillis()

+ 9 - 10
mirai-core/src/commonMain/kotlin/MiraiImpl.kt

@@ -44,7 +44,7 @@ import net.mamoe.mirai.message.data.*
 import net.mamoe.mirai.utils.BotConfiguration
 import net.mamoe.mirai.utils.MiraiExperimentalApi
 import net.mamoe.mirai.utils.currentTimeSeconds
-import kotlin.jvm.JvmSynthetic
+import java.util.concurrent.atomic.AtomicBoolean
 import kotlin.math.absoluteValue
 import kotlin.random.Random
 
@@ -63,7 +63,7 @@ internal open class MiraiImpl : IMirai, LowLevelApiAccessor {
     @OptIn(LowLevelApi::class)
     override suspend fun acceptNewFriendRequest(event: NewFriendRequestEvent) {
         @Suppress("INVISIBLE_REFERENCE", "INVISIBLE_MEMBER")
-        check(event.responded.compareAndSet(expect = false, update = true)) {
+        check(event.responded.compareAndSet(false, true)) {
             "the request $this has already been responded"
         }
 
@@ -83,7 +83,7 @@ internal open class MiraiImpl : IMirai, LowLevelApiAccessor {
 
     override suspend fun rejectNewFriendRequest(event: NewFriendRequestEvent, blackList: Boolean) {
         @Suppress("INVISIBLE_REFERENCE", "INVISIBLE_MEMBER")
-        check(event.responded.compareAndSet(expect = false, update = true)) {
+        check(event.responded.compareAndSet(false, true)) {
             "the request $event has already been responded"
         }
 
@@ -105,7 +105,7 @@ internal open class MiraiImpl : IMirai, LowLevelApiAccessor {
         @Suppress("DuplicatedCode")
         checkGroupPermission(event.bot, event.group) { event::class.simpleName ?: "<anonymous class>" }
         @Suppress("INVISIBLE_REFERENCE", "INVISIBLE_MEMBER")
-        check(event.responded.compareAndSet(expect = false, update = true)) {
+        check(event.responded.compareAndSet(false, true)) {
             "the request $this has already been responded"
         }
 
@@ -128,7 +128,7 @@ internal open class MiraiImpl : IMirai, LowLevelApiAccessor {
     override suspend fun rejectMemberJoinRequest(event: MemberJoinRequestEvent, blackList: Boolean, message: String) {
         checkGroupPermission(event.bot, event.group) { event::class.simpleName ?: "<anonymous class>" }
         @Suppress("INVISIBLE_REFERENCE", "INVISIBLE_MEMBER")
-        check(event.responded.compareAndSet(expect = false, update = true)) {
+        check(event.responded.compareAndSet(false, true)) {
             "the request $this has already been responded"
         }
 
@@ -164,7 +164,7 @@ internal open class MiraiImpl : IMirai, LowLevelApiAccessor {
     override suspend fun ignoreMemberJoinRequest(event: MemberJoinRequestEvent, blackList: Boolean) {
         checkGroupPermission(event.bot, event.group) { event::class.simpleName ?: "<anonymous class>" }
         @Suppress("INVISIBLE_REFERENCE", "INVISIBLE_MEMBER")
-        check(event.responded.compareAndSet(expect = false, update = true)) {
+        check(event.responded.compareAndSet(false, true)) {
             "the request $this has already been responded"
         }
 
@@ -188,7 +188,7 @@ internal open class MiraiImpl : IMirai, LowLevelApiAccessor {
 
     private suspend fun solveInvitedJoinGroupRequest(event: BotInvitedJoinGroupRequestEvent, accept: Boolean) {
         @Suppress("INVISIBLE_REFERENCE", "INVISIBLE_MEMBER")
-        check(event.responded.compareAndSet(expect = false, update = true)) {
+        check(event.responded.compareAndSet(false, true)) {
             "the request $this has already been responded"
         }
 
@@ -260,7 +260,7 @@ internal open class MiraiImpl : IMirai, LowLevelApiAccessor {
         source.ensureSequenceIdAvailable()
 
         @Suppress("BooleanLiteralArgument", "INVISIBLE_REFERENCE", "INVISIBLE_MEMBER") // false positive
-        check(!source.isRecalledOrPlanned.value && source.isRecalledOrPlanned.compareAndSet(false, true)) {
+        check(!source.isRecalledOrPlanned.get() && source.isRecalledOrPlanned.compareAndSet(false, true)) {
             "$source had already been recalled."
         }
 
@@ -830,8 +830,7 @@ internal open class MiraiImpl : IMirai, LowLevelApiAccessor {
             override val internalId: Int = internalId
 
             @Suppress("INVISIBLE_REFERENCE", "INVISIBLE_MEMBER")
-            override var isRecalledOrPlanned: net.mamoe.mirai.event.internal.MiraiAtomicBoolean =
-                net.mamoe.mirai.event.internal.MiraiAtomicBoolean(false)
+            override var isRecalledOrPlanned: AtomicBoolean = AtomicBoolean(false)
 
             override fun toJceData(): ImMsgBody.SourceMsg {
                 return ImMsgBody.SourceMsg(

+ 5 - 5
mirai-core/src/commonMain/kotlin/message/incomingSourceImpl.kt

@@ -14,7 +14,6 @@ package net.mamoe.mirai.internal.message
 import net.mamoe.mirai.Bot
 import net.mamoe.mirai.contact.Friend
 import net.mamoe.mirai.contact.Member
-import net.mamoe.mirai.event.internal.MiraiAtomicBoolean
 import net.mamoe.mirai.internal.contact.GroupImpl
 import net.mamoe.mirai.internal.network.protocol.data.proto.ImMsgBody
 import net.mamoe.mirai.internal.network.protocol.data.proto.MsgComm
@@ -27,6 +26,7 @@ import net.mamoe.mirai.message.data.Message
 import net.mamoe.mirai.message.data.MessageChain
 import net.mamoe.mirai.message.data.MessageSource
 import net.mamoe.mirai.message.data.OnlineMessageSource
+import java.util.concurrent.atomic.AtomicBoolean
 
 internal interface MessageSourceInternal {
     val sequenceId: Int
@@ -35,7 +35,7 @@ internal interface MessageSourceInternal {
     @Deprecated("don't use this internally. Use sequenceId or random instead.", level = DeprecationLevel.ERROR)
     val id: Int
 
-    val isRecalledOrPlanned: MiraiAtomicBoolean
+    val isRecalledOrPlanned: AtomicBoolean
 
     fun toJceData(): ImMsgBody.SourceMsg
 }
@@ -67,7 +67,7 @@ internal class MessageSourceFromFriendImpl(
     val msg: MsgComm.Msg
 ) : OnlineMessageSource.Incoming.FromFriend(), MessageSourceInternal {
     override val sequenceId: Int get() = msg.msgHead.msgSeq
-    override var isRecalledOrPlanned: MiraiAtomicBoolean = MiraiAtomicBoolean(false)
+    override var isRecalledOrPlanned: AtomicBoolean = AtomicBoolean(false)
     override val id: Int get() = sequenceId// msg.msgBody.richText.attr!!.random
     override val internalId: Int get() = msg.msgBody.richText.attr!!.random
     override val time: Int get() = msg.msgHead.msgTime
@@ -121,7 +121,7 @@ internal class MessageSourceFromTempImpl(
 ) : OnlineMessageSource.Incoming.FromTemp(), MessageSourceInternal {
     override val sequenceId: Int get() = msg.msgHead.msgSeq
     override val internalId: Int get() = msg.msgBody.richText.attr!!.random
-    override var isRecalledOrPlanned: MiraiAtomicBoolean = MiraiAtomicBoolean(false)
+    override var isRecalledOrPlanned: AtomicBoolean = AtomicBoolean(false)
     override val id: Int get() = sequenceId//
     override val time: Int get() = msg.msgHead.msgTime
     override val originalMessage: MessageChain by lazy { msg.toMessageChain(bot, 0, false) }
@@ -135,7 +135,7 @@ internal data class MessageSourceFromGroupImpl(
     override val bot: Bot,
     private val msg: MsgComm.Msg
 ) : OnlineMessageSource.Incoming.FromGroup(), MessageSourceInternal {
-    override var isRecalledOrPlanned: MiraiAtomicBoolean = MiraiAtomicBoolean(false)
+    override var isRecalledOrPlanned: AtomicBoolean = AtomicBoolean(false)
     override val sequenceId: Int get() = msg.msgHead.msgSeq
     override val internalId: Int get() = msg.msgBody.richText.attr!!.random
     override val id: Int get() = sequenceId

+ 3 - 3
mirai-core/src/commonMain/kotlin/message/offlineSourceImpl.kt

@@ -12,7 +12,6 @@
 package net.mamoe.mirai.internal.message
 
 import net.mamoe.mirai.Bot
-import net.mamoe.mirai.event.internal.MiraiAtomicBoolean
 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.SourceMsg
@@ -20,6 +19,7 @@ import net.mamoe.mirai.internal.network.protocol.packet.EMPTY_BYTE_ARRAY
 import net.mamoe.mirai.internal.utils.io.serialization.loadAs
 import net.mamoe.mirai.message.data.MessageChain
 import net.mamoe.mirai.message.data.OfflineMessageSource
+import java.util.concurrent.atomic.AtomicBoolean
 
 
 internal class OfflineMessageSourceImplByMsg(
@@ -47,7 +47,7 @@ internal class OfflineMessageSourceImplByMsg(
     override val sequenceId: Int
         get() = delegate.msgHead.msgSeq
 
-    override var isRecalledOrPlanned: MiraiAtomicBoolean = MiraiAtomicBoolean(false)
+    override var isRecalledOrPlanned: AtomicBoolean = AtomicBoolean(false)
 
     override fun toJceData(): ImMsgBody.SourceMsg {
         return ImMsgBody.SourceMsg(
@@ -72,7 +72,7 @@ internal class OfflineMessageSourceImplBySourceMsg(
 ) : OfflineMessageSource(), MessageSourceInternal {
     override val kind: Kind get() = if (delegate.srcMsg == null) Kind.GROUP else Kind.FRIEND
 
-    override var isRecalledOrPlanned: MiraiAtomicBoolean = MiraiAtomicBoolean(false)
+    override var isRecalledOrPlanned: AtomicBoolean = AtomicBoolean(false)
     override val sequenceId: Int
         get() = delegate.origSeqs.firstOrNull() ?: error("cannot find sequenceId")
     override val internalId: Int

+ 4 - 4
mirai-core/src/commonMain/kotlin/message/outgoingSourceImpl.kt

@@ -19,7 +19,6 @@ import net.mamoe.mirai.contact.Friend
 import net.mamoe.mirai.contact.Group
 import net.mamoe.mirai.contact.Member
 import net.mamoe.mirai.event.asyncFromEventOrNull
-import net.mamoe.mirai.event.internal.MiraiAtomicBoolean
 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.SourceMsg
@@ -28,6 +27,7 @@ import net.mamoe.mirai.internal.utils.io.serialization.toByteArray
 import net.mamoe.mirai.message.data.MessageChain
 import net.mamoe.mirai.message.data.MessageSource
 import net.mamoe.mirai.message.data.OnlineMessageSource
+import java.util.concurrent.atomic.AtomicBoolean
 
 
 private fun <T> T.toJceDataImpl(): ImMsgBody.SourceMsg
@@ -81,7 +81,7 @@ internal class MessageSourceToFriendImpl(
         get() = sender
     override val id: Int
         get() = sequenceId
-    override var isRecalledOrPlanned: MiraiAtomicBoolean = MiraiAtomicBoolean(false)
+    override var isRecalledOrPlanned: AtomicBoolean = AtomicBoolean(false)
     private val jceData by lazy { toJceDataImpl() }
     override fun toJceData(): ImMsgBody.SourceMsg = jceData
 }
@@ -98,7 +98,7 @@ internal class MessageSourceToTempImpl(
         get() = sender
     override val id: Int
         get() = sequenceId
-    override var isRecalledOrPlanned: MiraiAtomicBoolean = MiraiAtomicBoolean(false)
+    override var isRecalledOrPlanned: AtomicBoolean = AtomicBoolean(false)
     private val jceData by lazy { toJceDataImpl() }
     override fun toJceData(): ImMsgBody.SourceMsg = jceData
 }
@@ -115,7 +115,7 @@ internal class MessageSourceToGroupImpl(
         get() = sequenceId
     override val bot: Bot
         get() = sender
-    override var isRecalledOrPlanned: MiraiAtomicBoolean = MiraiAtomicBoolean(false)
+    override var isRecalledOrPlanned: AtomicBoolean = AtomicBoolean(false)
 
     private val sequenceIdDeferred: Deferred<Int?> =
         coroutineScope.asyncFromEventOrNull<SendGroupMessageReceipt, Int>(

+ 3 - 3
mirai-core/src/commonMain/kotlin/network/protocol/packet/Tlv.kt

@@ -35,7 +35,7 @@ internal fun BytePacketBuilder.t1(uin: Long, ip: ByteArray) {
         writeShort(1) // _ip_ver
         writeInt(Random.nextInt())
         writeInt(uin.toInt())
-        writeInt(currentTimeMillis.toInt())
+        writeInt(currentTimeMillis().toInt())
         writeFully(ip)
         writeShort(0)
     } shouldEqualsTo 20
@@ -113,7 +113,7 @@ internal fun BytePacketBuilder.t106(
                 writeLong(uin)
             }
 
-            writeInt(currentTimeMillis.toInt())
+            writeInt(currentTimeMillis().toInt())
             writeFully(ByteArray(4)) // ip // no need to write actual ip
             writeByte(isSavePassword.toByte())
             writeFully(passwordMd5)
@@ -555,7 +555,7 @@ internal fun BytePacketBuilder.t400(
             writeFully(dpwd)
             writeInt(appId.toInt())
             writeInt(subAppId.toInt())
-            writeLong(currentTimeMillis)
+            writeLong(currentTimeMillis())
             writeFully(randomSeed)
         }
     }

+ 1 - 3
mirai-core/src/commonMain/kotlin/utils/AtomicResizeCacheList.kt

@@ -14,8 +14,6 @@ import kotlinx.atomicfu.atomic
 import kotlinx.atomicfu.locks.reentrantLock
 import kotlinx.atomicfu.locks.withLock
 import net.mamoe.mirai.utils.currentTimeMillis
-import kotlin.jvm.JvmField
-import kotlin.jvm.Volatile
 
 
 /**
@@ -43,7 +41,7 @@ internal class AtomicResizeCacheList<E>(private val retention: Long) {
      * No concurrency guaranteed on same [element].
      */
     private fun add(element: E) {
-        val currentTime = currentTimeMillis
+        val currentTime = currentTimeMillis()
         findAvailable@ while (true) {
             for (cache in list) {
                 val instant = cache.time.value