Commit 8bab1285 authored by Him188's avatar Him188

Platform adjustment

parent 00959ab2
package net.mamoe.mirai.utils
/**
* 直接抛出异常. 需自行处理验证码
*/
actual var DefaultCaptchaSolver: CaptchaSolver = {
error("No CaptchaSolver found. BotConfiguration.captchaSolver should be assigned manually")
}
\ No newline at end of file
package net.mamoe.mirai.utils
import kotlinx.io.core.IoBuffer
internal actual suspend fun solveCaptcha(captchaBuffer: IoBuffer): String? {
TODO("Unsupported yet")
}
\ No newline at end of file
...@@ -10,7 +10,7 @@ import net.mamoe.mirai.contact.* ...@@ -10,7 +10,7 @@ import net.mamoe.mirai.contact.*
import net.mamoe.mirai.network.BotNetworkHandler import net.mamoe.mirai.network.BotNetworkHandler
import net.mamoe.mirai.network.protocol.tim.TIMBotNetworkHandler import net.mamoe.mirai.network.protocol.tim.TIMBotNetworkHandler
import net.mamoe.mirai.network.protocol.tim.packet.login.LoginResult import net.mamoe.mirai.network.protocol.tim.packet.login.LoginResult
import net.mamoe.mirai.utils.BotNetworkConfiguration import net.mamoe.mirai.utils.BotConfiguration
import net.mamoe.mirai.utils.DefaultLogger import net.mamoe.mirai.utils.DefaultLogger
import net.mamoe.mirai.utils.MiraiLogger import net.mamoe.mirai.utils.MiraiLogger
import net.mamoe.mirai.utils.internal.coerceAtLeastOrFail import net.mamoe.mirai.utils.internal.coerceAtLeastOrFail
...@@ -71,7 +71,7 @@ class Bot(val account: BotAccount, val logger: MiraiLogger) { ...@@ -71,7 +71,7 @@ class Bot(val account: BotAccount, val logger: MiraiLogger) {
*/ */
@JvmOverloads @JvmOverloads
suspend fun reinitializeNetworkHandler( suspend fun reinitializeNetworkHandler(
configuration: BotNetworkConfiguration, configuration: BotConfiguration,
cause: Throwable? = null cause: Throwable? = null
): LoginResult { ): LoginResult {
logger.info("Initializing BotNetworkHandler") logger.info("Initializing BotNetworkHandler")
......
...@@ -8,7 +8,7 @@ import net.mamoe.mirai.network.BotSession ...@@ -8,7 +8,7 @@ import net.mamoe.mirai.network.BotSession
import net.mamoe.mirai.network.protocol.tim.packet.OutgoingPacket import net.mamoe.mirai.network.protocol.tim.packet.OutgoingPacket
import net.mamoe.mirai.network.protocol.tim.packet.login.LoginResult import net.mamoe.mirai.network.protocol.tim.packet.login.LoginResult
import net.mamoe.mirai.network.session import net.mamoe.mirai.network.session
import net.mamoe.mirai.utils.BotNetworkConfiguration import net.mamoe.mirai.utils.BotConfiguration
import net.mamoe.mirai.utils.internal.PositiveNumbers import net.mamoe.mirai.utils.internal.PositiveNumbers
import net.mamoe.mirai.utils.internal.coerceAtLeastOrFail import net.mamoe.mirai.utils.internal.coerceAtLeastOrFail
...@@ -52,12 +52,12 @@ suspend inline fun Bot.sendPacket(packet: OutgoingPacket) = this.network.sendPac ...@@ -52,12 +52,12 @@ suspend inline fun Bot.sendPacket(packet: OutgoingPacket) = this.network.sendPac
/** /**
* 使用在默认配置基础上修改的配置进行登录 * 使用在默认配置基础上修改的配置进行登录
*/ */
suspend inline fun Bot.login(noinline configuration: BotNetworkConfiguration.() -> Unit): LoginResult = this.network.login(BotNetworkConfiguration().apply(configuration)) suspend inline fun Bot.login(noinline configuration: BotConfiguration.() -> Unit): LoginResult = this.network.login(BotConfiguration().apply(configuration))
/** /**
* 使用默认的配置 ([BotNetworkConfiguration.Default]) 登录 * 使用默认的配置 ([BotConfiguration.Default]) 登录
*/ */
suspend inline fun Bot.login(): LoginResult = this.network.login(BotNetworkConfiguration.Default) suspend inline fun Bot.login(): LoginResult = this.network.login(BotConfiguration.Default)
/** /**
* 取得机器人的 QQ 号 * 取得机器人的 QQ 号
......
...@@ -13,7 +13,7 @@ import net.mamoe.mirai.network.protocol.tim.packet.OutgoingPacket ...@@ -13,7 +13,7 @@ import net.mamoe.mirai.network.protocol.tim.packet.OutgoingPacket
import net.mamoe.mirai.network.protocol.tim.packet.Packet import net.mamoe.mirai.network.protocol.tim.packet.Packet
import net.mamoe.mirai.network.protocol.tim.packet.login.LoginResult import net.mamoe.mirai.network.protocol.tim.packet.login.LoginResult
import net.mamoe.mirai.network.protocol.tim.packet.login.RequestSKeyPacket import net.mamoe.mirai.network.protocol.tim.packet.login.RequestSKeyPacket
import net.mamoe.mirai.utils.BotNetworkConfiguration import net.mamoe.mirai.utils.BotConfiguration
import net.mamoe.mirai.utils.io.PlatformDatagramChannel import net.mamoe.mirai.utils.io.PlatformDatagramChannel
/** /**
...@@ -62,7 +62,7 @@ interface BotNetworkHandler<Socket : DataPacketSocketAdapter> : CoroutineScope { ...@@ -62,7 +62,7 @@ interface BotNetworkHandler<Socket : DataPacketSocketAdapter> : CoroutineScope {
* 依次尝试登录到可用的服务器. 在任一服务器登录完成后返回登录结果 * 依次尝试登录到可用的服务器. 在任一服务器登录完成后返回登录结果
* 本函数将挂起直到登录成功. * 本函数将挂起直到登录成功.
*/ */
suspend fun login(configuration: BotNetworkConfiguration): LoginResult suspend fun login(configuration: BotConfiguration): LoginResult
/** /**
* 添加一个临时包处理器, 并发送相应的包 * 添加一个临时包处理器, 并发送相应的包
......
...@@ -22,10 +22,9 @@ import net.mamoe.mirai.network.protocol.tim.packet.* ...@@ -22,10 +22,9 @@ import net.mamoe.mirai.network.protocol.tim.packet.*
import net.mamoe.mirai.network.protocol.tim.packet.login.* import net.mamoe.mirai.network.protocol.tim.packet.login.*
import net.mamoe.mirai.network.session import net.mamoe.mirai.network.session
import net.mamoe.mirai.qqAccount import net.mamoe.mirai.qqAccount
import net.mamoe.mirai.utils.BotNetworkConfiguration import net.mamoe.mirai.utils.BotConfiguration
import net.mamoe.mirai.utils.OnlineStatus import net.mamoe.mirai.utils.OnlineStatus
import net.mamoe.mirai.utils.io.* import net.mamoe.mirai.utils.io.*
import net.mamoe.mirai.utils.solveCaptcha
import kotlin.coroutines.CoroutineContext import kotlin.coroutines.CoroutineContext
import kotlin.properties.Delegates import kotlin.properties.Delegates
...@@ -66,7 +65,7 @@ internal class TIMBotNetworkHandler internal constructor(override val bot: Bot) ...@@ -66,7 +65,7 @@ internal class TIMBotNetworkHandler internal constructor(override val bot: Bot)
temporaryPacketHandler.send(this[ActionPacketHandler].session) temporaryPacketHandler.send(this[ActionPacketHandler].session)
} }
override suspend fun login(configuration: BotNetworkConfiguration): LoginResult = override suspend fun login(configuration: BotConfiguration): LoginResult =
withContext(this.coroutineContext) { withContext(this.coroutineContext) {
TIMProtocol.SERVER_IP.forEach { ip -> TIMProtocol.SERVER_IP.forEach { ip ->
bot.logger.info("Connecting server $ip") bot.logger.info("Connecting server $ip")
...@@ -124,7 +123,7 @@ internal class TIMBotNetworkHandler internal constructor(override val bot: Bot) ...@@ -124,7 +123,7 @@ internal class TIMBotNetworkHandler internal constructor(override val bot: Bot)
override suspend fun sendPacket(packet: OutgoingPacket) = socket.sendPacket(packet) override suspend fun sendPacket(packet: OutgoingPacket) = socket.sendPacket(packet)
internal inner class BotSocketAdapter(override val serverIp: String, val configuration: BotNetworkConfiguration) : internal inner class BotSocketAdapter(override val serverIp: String, val configuration: BotConfiguration) :
DataPacketSocketAdapter { DataPacketSocketAdapter {
override val channel: PlatformDatagramChannel = PlatformDatagramChannel(serverIp, 8000) override val channel: PlatformDatagramChannel = PlatformDatagramChannel(serverIp, 8000)
...@@ -290,7 +289,7 @@ internal class TIMBotNetworkHandler internal constructor(override val bot: Bot) ...@@ -290,7 +289,7 @@ internal class TIMBotNetworkHandler internal constructor(override val bot: Bot)
/** /**
* 处理登录过程 * 处理登录过程
*/ */
inner class LoginHandler(private val configuration: BotNetworkConfiguration) { inner class LoginHandler(private val configuration: BotConfiguration) {
private lateinit var token00BA: ByteArray private lateinit var token00BA: ByteArray
private lateinit var token0825: ByteArray//56 private lateinit var token0825: ByteArray//56
private var loginTime: Int = 0 private var loginTime: Int = 0
...@@ -329,7 +328,7 @@ internal class TIMBotNetworkHandler internal constructor(override val bot: Bot) ...@@ -329,7 +328,7 @@ internal class TIMBotNetworkHandler internal constructor(override val bot: Bot)
else -> { else -> {
error("No decrypter found") error("No decrypter found")
} }
} as? D ?: error("Internal error: could not cast decrypter found for factory to class Decrypter") } as? D ?: error("Internal error: could not cast decrypter which is found for factory to class Decrypter")
suspend fun onPacketReceived(packet: Any) {//complex function, but it doesn't matter suspend fun onPacketReceived(packet: Any) {//complex function, but it doesn't matter
when (packet) { when (packet) {
...@@ -412,10 +411,10 @@ internal class TIMBotNetworkHandler internal constructor(override val bot: Bot) ...@@ -412,10 +411,10 @@ internal class TIMBotNetworkHandler internal constructor(override val bot: Bot)
this.token00BA = packet.token00BA this.token00BA = packet.token00BA
if (packet.transmissionCompleted) { if (packet.transmissionCompleted) {
val code = solveCaptcha(captchaCache!!) val code = configuration.captchaSolver(bot, captchaCache!!)
this.captchaCache = null this.captchaCache = null
if (code == null) { if (code == null || code.length != 4) {
this.captchaSectionId = 1//意味着正在刷新验证码 this.captchaSectionId = 1//意味着正在刷新验证码
socket.sendPacket(CaptchaPacket.Refresh(bot.qqAccount, token0825)) socket.sendPacket(CaptchaPacket.Refresh(bot.qqAccount, token0825))
} else { } else {
......
...@@ -2,14 +2,29 @@ package net.mamoe.mirai.utils ...@@ -2,14 +2,29 @@ package net.mamoe.mirai.utils
import com.soywiz.klock.TimeSpan import com.soywiz.klock.TimeSpan
import com.soywiz.klock.seconds import com.soywiz.klock.seconds
import kotlinx.io.core.IoBuffer
import net.mamoe.mirai.Bot
import net.mamoe.mirai.network.protocol.tim.packet.login.TouchPacket.TouchResponse
import kotlin.jvm.JvmField import kotlin.jvm.JvmField
/**
* 验证码处理器. 需阻塞直到处理完成验证码.
*
* 返回
*/
typealias CaptchaSolver = suspend Bot.(IoBuffer) -> String?
/**
* 在各平台实现的默认的验证码处理器.
*/
expect var DefaultCaptchaSolver: CaptchaSolver
/** /**
* 网络和连接配置 * 网络和连接配置
*/ */
class BotNetworkConfiguration { class BotConfiguration {
/** /**
* 等待 [TouchRespnose] 的时间 * 等待 [TouchResponse] 的时间
*/ */
var touchTimeout: TimeSpan = 2.seconds var touchTimeout: TimeSpan = 2.seconds
/** /**
...@@ -28,11 +43,16 @@ class BotNetworkConfiguration { ...@@ -28,11 +43,16 @@ class BotNetworkConfiguration {
*/ */
var heartbeatTimeout: TimeSpan = 2.seconds var heartbeatTimeout: TimeSpan = 2.seconds
/**
* 验证码处理器
*/
var captchaSolver: CaptchaSolver = DefaultCaptchaSolver
companion object { companion object {
/** /**
* 默认的配置实例 * 默认的配置实例
*/ */
@JvmField @JvmField
val Default = BotNetworkConfiguration() val Default = BotConfiguration()
} }
} }
\ No newline at end of file
package net.mamoe.mirai.utils
import kotlinx.io.core.IoBuffer
/**
* 让用户处理验证码
*
* @return 用户输入得到的验证码. 非 null 时一定 `length==4`.
*/
internal expect suspend fun solveCaptcha(captchaBuffer: IoBuffer): String?
package net.mamoe.mirai.task
import java.util.concurrent.Callable
import java.util.concurrent.Future
import java.util.concurrent.atomic.AtomicInteger
import java.util.function.Consumer
import java.util.function.Predicate
/**
* @author NaturalHG
*/
/*
class MiraiTaskManager private constructor() {
private val pool: MiraiThreadPool
init {
this.pool = MiraiThreadPool()
}
/**
* 基础Future处理
*/
fun execute(runnable: Runnable) {
this.execute(runnable, MiraiTaskExceptionHandler.printing())
}
fun execute(runnable: Runnable, handler: MiraiTaskExceptionHandler) {
this.pool.execute {
try {
runnable.run()
} catch (e: Exception) {
handler.onHandle(e)
}
}
}
fun <D> submit(callable: Callable<D>): Future<D> {
return this.submit(callable, MiraiTaskExceptionHandler.printing())
}
fun <D> submit(callable: Callable<D>, handler: MiraiTaskExceptionHandler): Future<D> {
return this.pool.submit<D> {
try {
callable.call()
} catch (e: Throwable) {
handler.onHandle(e)
null
}
}
}
/**
* 异步任务
*/
fun <D> ansycTask(callable: Callable<D>, callback: Consumer<D>) {
this.ansycTask(callable, callback, MiraiTaskExceptionHandler.printing())
}
fun <D> ansycTask(callable: Callable<D>, callback: Consumer<D>, handler: MiraiTaskExceptionHandler) {
this.pool.execute {
try {
callback.accept(callable.call())
} catch (e: Throwable) {
handler.onHandle(e)
}
}
}
/**
* 定时任务
*/
fun repeatingTask(runnable: Runnable, intervalMillis: Long) {
this.repeatingTask(runnable, intervalMillis, MiraiTaskExceptionHandler.printing())
}
fun repeatingTask(runnable: Runnable, intervalMillis: Long, handler: MiraiTaskExceptionHandler) {
this.repeatingTask<Runnable>(runnable, intervalMillis, { a -> true }, handler)
}
fun repeatingTask(runnable: Runnable, intervalMillis: Long, times: Int) {
this.repeatingTask(runnable, intervalMillis, times, MiraiTaskExceptionHandler.printing())
}
fun repeatingTask(runnable: Runnable, intervalMillis: Long, times: Int, handler: MiraiTaskExceptionHandler) {
val integer = AtomicInteger(times - 1)
this.repeatingTask<Runnable>(
runnable, intervalMillis, { a -> integer.getAndDecrement() > 0 }, handler
)
}
fun <D : Runnable> repeatingTask(runnable: D, intervalMillis: Long, shouldContinue: Predicate<D>, handler: MiraiTaskExceptionHandler) {
Thread {
do {
this.pool.execute {
try {
runnable.run()
} catch (e: Exception) {
handler.onHandle(e)
}
}
try {
Thread.sleep(intervalMillis)
} catch (e: InterruptedException) {
e.printStackTrace()
}
} while (shouldContinue.test(runnable))
}.start()
}
fun deletingTask(runnable: Runnable, intervalMillis: Long) {
Thread {
try {
Thread.sleep(intervalMillis)
} catch (e: InterruptedException) {
e.printStackTrace()
}
this.pool.execute(runnable)
}.start()
}
companion object {
val instance = MiraiTaskManager()
}
}
*/
\ No newline at end of file
package net.mamoe.mirai.task
import net.mamoe.mirai.Mirai
import java.io.Closeable
import java.util.concurrent.ScheduledThreadPoolExecutor
/**
* @author NaturalHG
*/
class MiraiThreadPool internal constructor()/*super(0,
Integer.MAX_VALUE,
60L,
TimeUnit.SECONDS,
new SynchronousQueue<>()
);*/ : ScheduledThreadPoolExecutor(10), Closeable {
override fun close() {
this.shutdown()
if (!this.isShutdown) {
this.shutdownNow()
}
}
companion object {
val instance = MiraiThreadPool()
@JvmStatic
fun main(args: Array<String>) {
println(Mirai.VERSION)
}
}
}
...@@ -6,29 +6,26 @@ import kotlinx.coroutines.Dispatchers ...@@ -6,29 +6,26 @@ import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.sync.Mutex import kotlinx.coroutines.sync.Mutex
import kotlinx.coroutines.sync.withLock import kotlinx.coroutines.sync.withLock
import kotlinx.coroutines.withContext import kotlinx.coroutines.withContext
import kotlinx.io.core.IoBuffer
import java.awt.Image import java.awt.Image
import java.awt.image.BufferedImage import java.awt.image.BufferedImage
import java.io.File import java.io.File
import javax.imageio.ImageIO import javax.imageio.ImageIO
import kotlin.math.max
import kotlin.math.min
/** /**
* 让用户处理验证码 * 平台默认的验证码识别器.
* *
* @return 用户输入得到的验证码 * 可被修改, 除覆盖配置外全局生效.
*/ */
@KtorExperimentalAPI @KtorExperimentalAPI
internal actual suspend fun solveCaptcha(captchaBuffer: IoBuffer): String? = captchaLock.withLock { actual var DefaultCaptchaSolver: CaptchaSolver = {
captchaLock.withLock {
val tempFile: File = createTempFile(suffix = ".png").apply { deleteOnExit() } val tempFile: File = createTempFile(suffix = ".png").apply { deleteOnExit() }
withContext(Dispatchers.IO) { withContext(Dispatchers.IO) {
tempFile.createNewFile() tempFile.createNewFile()
@Suppress("EXPERIMENTAL_API_USAGE") @Suppress("EXPERIMENTAL_API_USAGE")
MiraiLogger.info("需要验证码登录, 验证码为 4 字母") MiraiLogger.info("需要验证码登录, 验证码为 4 字母")
try { try {
tempFile.writeChannel().writeFully(captchaBuffer) tempFile.writeChannel().writeFully(it)
MiraiLogger.info("若看不清字符图片, 请查看 ${tempFile.absolutePath}") MiraiLogger.info("若看不清字符图片, 请查看 ${tempFile.absolutePath}")
} catch (e: Exception) { } catch (e: Exception) {
MiraiLogger.info("无法写出验证码文件(${e.message}), 请尝试查看以上字符图片") MiraiLogger.info("无法写出验证码文件(${e.message}), 请尝试查看以上字符图片")
...@@ -38,10 +35,11 @@ internal actual suspend fun solveCaptcha(captchaBuffer: IoBuffer): String? = cap ...@@ -38,10 +35,11 @@ internal actual suspend fun solveCaptcha(captchaBuffer: IoBuffer): String? = cap
} }
MiraiLogger.info("若要更换验证码, 请直接回车") MiraiLogger.info("若要更换验证码, 请直接回车")
readLine()?.takeUnless { it.isEmpty() || it.length != 4 } readLine()?.takeUnless { it.isEmpty() || it.length != 4 }
}
} }
private val captchaLock = Mutex()
private val captchaLock = Mutex()
/** /**
* @author NaturalHG * @author NaturalHG
...@@ -60,7 +58,7 @@ internal fun BufferedImage.createCharImg(outputWidth: Int = 100, ignoreRate: Dou ...@@ -60,7 +58,7 @@ internal fun BufferedImage.createCharImg(outputWidth: Int = 100, ignoreRate: Dou
return (r * 30 + g * 59 + b * 11 + 50) / 100 return (r * 30 + g * 59 + b * 11 + 50) / 100
} }
fun grayCompare(g1: Int, g2: Int): Boolean = min(g1, g2).toDouble() / max(g1, g2) >= ignoreRate 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)) val background = gray(image.getRGB(0, 0))
......
...@@ -41,7 +41,14 @@ class MainActivity : AppCompatActivity() { ...@@ -41,7 +41,14 @@ class MainActivity : AppCompatActivity() {
} }
} }
val bot = Bot(qq, password).apply { login().requireSuccess() } val bot = Bot(qq, password).apply {
login {
captchaSolver = {
"ABCD"
}
}.requireSuccess()
}
bot.subscribeFriendMessages { bot.subscribeFriendMessages {
"Hello" reply "Hello Mirai!" "Hello" reply "Hello Mirai!"
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment