mirror of
https://github.com/mamoe/mirai.git
synced 2025-04-17 09:09:23 +08:00
Simplify platform structure: merge jvmMain into commonMain
This commit is contained in:
parent
d0d73d5285
commit
eafca6d4ed
mirai-core-api
build.gradle.kts
src
commonMain/kotlin
event
utils
jvmMain/kotlin
mirai-core/src/commonMain/kotlin
@ -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 {
|
||||
|
@ -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`
|
||||
|
@ -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)
|
||||
|
@ -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]!!
|
||||
}
|
||||
|
||||
|
||||
|
@ -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)
|
||||
}
|
||||
}
|
||||
}
|
@ -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(
|
||||
|
@ -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()
|
||||
}
|
@ -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")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -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")
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
|
@ -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()
|
@ -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
|
@ -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.
|
||||
|
@ -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()
|
@ -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() = this@asReusableInput.size.toLongUnsigned()
|
@ -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]!!
|
||||
|
||||
}
|
@ -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())
|
||||
}
|
@ -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")
|
||||
}
|
||||
}
|
||||
}
|
@ -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()
|
@ -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] 末尾可能不带换行符.
|
||||
|
@ -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()
|
@ -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(
|
||||
|
@ -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
|
||||
|
@ -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
|
||||
|
@ -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>(
|
||||
|
@ -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)
|
||||
}
|
||||
}
|
||||
|
@ -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
|
||||
|
Loading…
Reference in New Issue
Block a user