1
0
mirror of https://github.com/mamoe/mirai.git synced 2025-04-25 21:23:55 +08:00

Platform adjustment

This commit is contained in:
Him188 2019-11-07 15:06:38 +08:00
parent 00959ab291
commit 8bab12854b
17 changed files with 75 additions and 230 deletions
mirai-core/src
mirai-demos/mirai-demo-android/src/main/kotlin/net/mamoe/mirai/demo

View File

@ -0,0 +1,8 @@
package net.mamoe.mirai.utils
/**
* 直接抛出异常. 需自行处理验证码
*/
actual var DefaultCaptchaSolver: CaptchaSolver = {
error("No CaptchaSolver found. BotConfiguration.captchaSolver should be assigned manually")
}

View File

@ -1,7 +0,0 @@
package net.mamoe.mirai.utils
import kotlinx.io.core.IoBuffer
internal actual suspend fun solveCaptcha(captchaBuffer: IoBuffer): String? {
TODO("Unsupported yet")
}

View File

@ -10,7 +10,7 @@ import net.mamoe.mirai.contact.*
import net.mamoe.mirai.network.BotNetworkHandler
import net.mamoe.mirai.network.protocol.tim.TIMBotNetworkHandler
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.MiraiLogger
import net.mamoe.mirai.utils.internal.coerceAtLeastOrFail
@ -71,7 +71,7 @@ class Bot(val account: BotAccount, val logger: MiraiLogger) {
*/
@JvmOverloads
suspend fun reinitializeNetworkHandler(
configuration: BotNetworkConfiguration,
configuration: BotConfiguration,
cause: Throwable? = null
): LoginResult {
logger.info("Initializing BotNetworkHandler")

View File

@ -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.login.LoginResult
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.coerceAtLeastOrFail
@ -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

View File

@ -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.login.LoginResult
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
/**
@ -62,7 +62,7 @@ interface BotNetworkHandler<Socket : DataPacketSocketAdapter> : CoroutineScope {
* 依次尝试登录到可用的服务器. 在任一服务器登录完成后返回登录结果
* 本函数将挂起直到登录成功.
*/
suspend fun login(configuration: BotNetworkConfiguration): LoginResult
suspend fun login(configuration: BotConfiguration): LoginResult
/**
* 添加一个临时包处理器, 并发送相应的包

View File

@ -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.session
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.io.*
import net.mamoe.mirai.utils.solveCaptcha
import kotlin.coroutines.CoroutineContext
import kotlin.properties.Delegates
@ -66,7 +65,7 @@ internal class TIMBotNetworkHandler internal constructor(override val bot: Bot)
temporaryPacketHandler.send(this[ActionPacketHandler].session)
}
override suspend fun login(configuration: BotNetworkConfiguration): LoginResult =
override suspend fun login(configuration: BotConfiguration): LoginResult =
withContext(this.coroutineContext) {
TIMProtocol.SERVER_IP.forEach { ip ->
bot.logger.info("Connecting server $ip")
@ -124,7 +123,7 @@ internal class TIMBotNetworkHandler internal constructor(override val bot: Bot)
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 {
override val channel: PlatformDatagramChannel = PlatformDatagramChannel(serverIp, 8000)
@ -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 token0825: ByteArray//56
private var loginTime: Int = 0
@ -329,7 +328,7 @@ internal class TIMBotNetworkHandler internal constructor(override val bot: Bot)
else -> {
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
when (packet) {
@ -412,10 +411,10 @@ internal class TIMBotNetworkHandler internal constructor(override val bot: Bot)
this.token00BA = packet.token00BA
if (packet.transmissionCompleted) {
val code = solveCaptcha(captchaCache!!)
val code = configuration.captchaSolver(bot, captchaCache!!)
this.captchaCache = null
if (code == null) {
if (code == null || code.length != 4) {
this.captchaSectionId = 1//意味着正在刷新验证码
socket.sendPacket(CaptchaPacket.Refresh(bot.qqAccount, token0825))
} else {

View File

@ -2,14 +2,29 @@ package net.mamoe.mirai.utils
import com.soywiz.klock.TimeSpan
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
/**
* 验证码处理器. 需阻塞直到处理完成验证码.
*
* 返回
*/
typealias CaptchaSolver = suspend Bot.(IoBuffer) -> String?
/**
* 在各平台实现的默认的验证码处理器.
*/
expect var DefaultCaptchaSolver: CaptchaSolver
/**
* 网络和连接配置
*/
class BotNetworkConfiguration {
class BotConfiguration {
/**
* 等待 [TouchRespnose] 的时间
* 等待 [TouchResponse] 的时间
*/
var touchTimeout: TimeSpan = 2.seconds
/**
@ -28,11 +43,16 @@ class BotNetworkConfiguration {
*/
var heartbeatTimeout: TimeSpan = 2.seconds
/**
* 验证码处理器
*/
var captchaSolver: CaptchaSolver = DefaultCaptchaSolver
companion object {
/**
* 默认的配置实例
*/
@JvmField
val Default = BotNetworkConfiguration()
val Default = BotConfiguration()
}
}

View File

@ -1,10 +0,0 @@
package net.mamoe.mirai.utils
import kotlinx.io.core.IoBuffer
/**
* 让用户处理验证码
*
* @return 用户输入得到的验证码. null 时一定 `length==4`.
*/
internal expect suspend fun solveCaptcha(captchaBuffer: IoBuffer): String?

View File

@ -1,135 +0,0 @@
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()
}
}
*/

View File

@ -1,35 +0,0 @@
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)
}
}
}

View File

@ -6,42 +6,40 @@ import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.sync.Mutex
import kotlinx.coroutines.sync.withLock
import kotlinx.coroutines.withContext
import kotlinx.io.core.IoBuffer
import java.awt.Image
import java.awt.image.BufferedImage
import java.io.File
import javax.imageio.ImageIO
import kotlin.math.max
import kotlin.math.min
/**
* 让用户处理验证码
* 平台默认的验证码识别器.
*
* @return 用户输入得到的验证码
* 可被修改, 除覆盖配置外全局生效.
*/
@KtorExperimentalAPI
internal actual suspend fun solveCaptcha(captchaBuffer: IoBuffer): String? = captchaLock.withLock {
val tempFile: File = createTempFile(suffix = ".png").apply { deleteOnExit() }
withContext(Dispatchers.IO) {
tempFile.createNewFile()
@Suppress("EXPERIMENTAL_API_USAGE")
MiraiLogger.info("需要验证码登录, 验证码为 4 字母")
try {
tempFile.writeChannel().writeFully(captchaBuffer)
MiraiLogger.info("若看不清字符图片, 请查看 ${tempFile.absolutePath}")
} catch (e: Exception) {
MiraiLogger.info("无法写出验证码文件(${e.message}), 请尝试查看以上字符图片")
}
actual var DefaultCaptchaSolver: CaptchaSolver = {
captchaLock.withLock {
val tempFile: File = createTempFile(suffix = ".png").apply { deleteOnExit() }
withContext(Dispatchers.IO) {
tempFile.createNewFile()
@Suppress("EXPERIMENTAL_API_USAGE")
MiraiLogger.info("需要验证码登录, 验证码为 4 字母")
try {
tempFile.writeChannel().writeFully(it)
MiraiLogger.info("若看不清字符图片, 请查看 ${tempFile.absolutePath}")
} catch (e: Exception) {
MiraiLogger.info("无法写出验证码文件(${e.message}), 请尝试查看以上字符图片")
}
MiraiLogger.info(ImageIO.read(tempFile.inputStream()).createCharImg())
MiraiLogger.info(ImageIO.read(tempFile.inputStream()).createCharImg())
}
MiraiLogger.info("若要更换验证码, 请直接回车")
readLine()?.takeUnless { it.isEmpty() || it.length != 4 }
}
MiraiLogger.info("若要更换验证码, 请直接回车")
readLine()?.takeUnless { it.isEmpty() || it.length != 4 }
}
private val captchaLock = Mutex()
private val captchaLock = Mutex()
/**
* @author NaturalHG
@ -60,7 +58,7 @@ internal fun BufferedImage.createCharImg(outputWidth: Int = 100, ignoreRate: Dou
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))

View File

@ -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 {
"Hello" reply "Hello Mirai!"