Reduce multiplatform functions

This commit is contained in:
Him188 2019-10-31 22:19:26 +08:00
parent 71d5dda297
commit 048fe647f0
24 changed files with 345 additions and 473 deletions

View File

@ -9,7 +9,7 @@ buildscript {
}
dependencies {
classpath 'com.android.tools.build:gradle:3.4.2'
classpath 'com.android.tools.build:gradle:3.5.1'
classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:$kotlin_version"
classpath "org.jetbrains.kotlinx:atomicfu-gradle-plugin:$atomicfu_version"
}
@ -25,4 +25,4 @@ allprojects {
google()
maven { url "https://mirrors.huaweicloud.com/repository/maven/" }
}
}
}

View File

@ -5,9 +5,6 @@ package net.mamoe.mirai;
* @author NaturalHG
*/
public final class MiraiMain {
private static MiraiServer server;
public static void main(String[] args) {
Runtime.getRuntime().addShutdownHook(new Thread(() -> server.shutdown()));
}
}

View File

@ -1,186 +0,0 @@
@file:Suppress("EXPERIMENTAL_API_USAGE")
package net.mamoe.mirai
import kotlinx.coroutines.runBlocking
import net.mamoe.mirai.network.protocol.tim.packet.login.LoginResult
import net.mamoe.mirai.utils.LoggerTextFormat
import net.mamoe.mirai.utils.MiraiLogger
import net.mamoe.mirai.utils.config.MiraiConfig
import net.mamoe.mirai.utils.setting.MiraiSettings
import java.io.File
import java.io.IOException
import java.util.concurrent.ExecutionException
/**
* Mirai 服务器.
* 管理一些基础的事务
*
* @author NaturalHG
*/
object MiraiServer {
var isUnix: Boolean = false
private set
var parentFolder: File = File(System.getProperty("user.dir"))
var logger: MiraiLogger
internal set
internal lateinit var settings: MiraiSettings
internal lateinit var qqs: MiraiConfig
private var enabled: Boolean = false
init {
this.isUnix = !System.getProperties().getProperty("os.name").toUpperCase().contains("WINDOWS")
this.logger = MiraiLogger
logger.logInfo("About to run Mirai (" + Mirai.VERSION + ") under " + if (isUnix) "unix" else "windows")
logger.logInfo("Loading data under " + LoggerTextFormat.GREEN + this.parentFolder)
val setting = File(this.parentFolder, "/Mirai.ini")
logger.logInfo("Selecting setting from " + LoggerTextFormat.GREEN + setting)
/*
if (!setting.exists()) {
this.initSetting(setting);
} else {
this.settings = new MiraiSettings(setting);
}
File qqs = new File(this.parentFolder + "/QQ.yml");
getLogger().logInfo("Reading QQ accounts from " + LoggerTextFormat.GREEN + qqs);
if (!qqs.exists()) {
this.initQQConfig(qqs);
} else {
this.qqs = new MiraiConfig(qqs);
}
if (this.qqs.isEmpty()) {
this.initQQConfig(qqs);
}*/
/*
MiraiSettingMapSection qqs = this.setting.getMapSection("qq");
qqs.forEach((a,p) -> {
this.getLogger().logInfo("Finding available ports between " + "1-65536");
try {
int port = MiraiNetwork.getAvailablePort();
this.getLogger().logInfo("Listening on port " + port);
} catch (IOException e) {
e.printStackTrace();
}
});
*/
this.reload()
}
fun shutdown() {
if (this.enabled) {
logger.logInfo("About to shutdown Mirai")
logger.logInfo("Data have been saved")
}
}
private fun initSetting(setting: File) {
logger.logInfo("Thanks for using Mirai")
logger.logInfo("initializing Settings")
try {
if (setting.createNewFile()) {
logger.logInfo("Mirai Config Created")
}
} catch (e: IOException) {
e.printStackTrace()
}
this.settings = MiraiSettings(setting)
val network = this.settings.getMapSection("network")
network["enable_proxy"] = "not supporting yet"
val proxy = this.settings.getListSection("proxy")
proxy.add("1.2.3.4:95")
proxy.add("1.2.3.4:100")
val worker = this.settings.getMapSection("worker")
worker["core_task_pool_worker_amount"] = 5
val plugin = this.settings.getMapSection("plugin")
plugin["logDebug"] = false
this.settings.save()
logger.logInfo("initialized; changing can be made in setting file: $setting")
}
private fun initQQConfig(qqConfig: File) {
this.qqs = MiraiConfig(qqConfig)
MiraiLogger.logInfo("QQ account initialized; changing can be made in Config file: $qqConfig")
logger.logInfo("QQ 账户管理初始化完毕")
}
private fun reload() {
this.enabled = true
MiraiLogger.logInfo(LoggerTextFormat.GREEN.toString() + "Server enabled; Welcome to Mirai")
MiraiLogger.logInfo("Mirai Version=" + Mirai.VERSION)
MiraiLogger.logInfo("Initializing [Bot]s")
try {
availableBot
} catch (e: ExecutionException) {
e.printStackTrace()
} catch (e: InterruptedException) {
e.printStackTrace()
}
/*
this.qqs.keySet().stream().map(key -> this.qqs.getSection(key)).forEach(section -> {
getLogger().logInfo("Initializing [Bot] " + section.getString("account"));
try {
Bot bot = new Bot(section);
var state = bot.network.login$mirai_core().of();
//bot.network.login$mirai_core().whenComplete((state, e) -> {
if (state == LoginState.REQUIRE_UPLOAD) {
Bot.instances.add(bot);
getLogger().logGreen(" Login Succeed");
} else {
getLogger().logError(" Login Failed with logError " + state);
bot.close();
}
// }).of();
} catch (Throwable e) {
e.printStackTrace();
getLogger().logError("Could not load QQ bots config!");
System.exit(1);
}
});*/
}
//todo only for test now
private var qqList = "1683921395----bb22222\n"
private val availableBot: Bot
@Throws(ExecutionException::class, InterruptedException::class)
get() {
for (it in qqList.split("\n").dropLastWhile { it.isEmpty() }.toTypedArray()) {
val strings = it.split("----").dropLastWhile { it.isEmpty() }.toTypedArray()
val bot = Bot(BotAccount(strings[0].toUInt(), strings[1]), MiraiLogger)
if (runBlocking { bot.login() } === LoginResult.SUCCESS) {
bot.logger.logGreen("Login succeed")
return bot
}
}
throw RuntimeException()
}
}

View File

@ -14,7 +14,6 @@ import net.mamoe.mirai.utils.BotNetworkConfiguration
import net.mamoe.mirai.utils.DefaultLogger
import net.mamoe.mirai.utils.MiraiLogger
import net.mamoe.mirai.utils.internal.coerceAtLeastOrFail
import net.mamoe.mirai.utils.log
import kotlin.jvm.JvmOverloads
data class BotAccount(
@ -75,11 +74,11 @@ class Bot(val account: BotAccount, val logger: MiraiLogger) {
configuration: BotNetworkConfiguration,
cause: Throwable? = null
): LoginResult {
logger.logPurple("Reinitializing BotNetworkHandler")
logger.warning("Reinitializing BotNetworkHandler")
try {
network.close(cause)
} catch (e: Exception) {
e.log()
logger.error(e)
}
network = TIMBotNetworkHandler(this)
return network.login(configuration)

View File

@ -10,7 +10,6 @@ import net.mamoe.mirai.event.internal.broadcastInternal
import net.mamoe.mirai.network.BotNetworkHandler
import net.mamoe.mirai.utils.DefaultLogger
import net.mamoe.mirai.utils.MiraiLogger
import net.mamoe.mirai.utils.log
import kotlin.coroutines.CoroutineContext
import kotlin.coroutines.EmptyCoroutineContext
import kotlin.jvm.JvmOverloads
@ -44,7 +43,7 @@ abstract class Event {
init {
if (EventDebuggingFlag) {
EventLogger.logDebug(this::class.simpleName + " created")
EventLogger.debug(this::class.simpleName + " created")
}
}
}
@ -66,20 +65,20 @@ interface Cancellable {
/**
* 广播一个事件的唯一途径.
* [context] 不包含 [CoroutineExceptionHandler], 将会使用默认的异常捕获, [log]
* [context] 不包含 [CoroutineExceptionHandler], 将会使用默认的异常捕获, [error]
* 也就是说, 这个方法不会抛出异常, 只会把异常交由 [context] 捕获
*/
@Suppress("UNCHECKED_CAST")
@JvmOverloads
suspend fun <E : Event> E.broadcast(context: CoroutineContext = EmptyCoroutineContext): E {
if (EventDebuggingFlag) {
EventLogger.logDebug(this::class.simpleName + " pre broadcast")
EventLogger.debug(this::class.simpleName + " pre broadcast")
}
try {
return withContext(EventScope.coroutineContext + context) { this@broadcast.broadcastInternal() }
} finally {
if (EventDebuggingFlag) {
EventLogger.logDebug(this::class.simpleName + " after broadcast")
EventLogger.debug(this::class.simpleName + " after broadcast")
}
}
}

View File

@ -13,18 +13,34 @@ import net.mamoe.mirai.utils.ExternalImage
* 采用这样的消息模式是因为 QQ 的消息多元化, 一条消息中可包含 [纯文本][PlainText], [图片][Image] .
*
* ** Kotlin 使用 [Message]**
* 这与使用 [String] 的使用非常类似.
* 这与使用 [String] 的使用非常类似.
*
* 比较 [Message] [String] (使用 infix [Message.eq]):
* 比较 [Message] [String] (使用 infix [Message.eq]):
* `if(event eq "你好") qq.sendMessage(event)`
*
* 连接 [Message] [Message], [String], (使用 operator [Message.plus]):
* ```
* 连接 [Message] [Message], [String], (使用 operator [Message.plus]):
* ```kotlin
* event = PlainText("Hello ")
* qq.sendMessage(event + "world")
* ```
*
* 但注意: 不能 `String + Message`. 只能 `Message + String`
* `Message1 + Message2 + Message3`, 类似 [String] 的连接:
*
* +----------+ plus +----------+ plus +----------+
* | Message1 | <------ | Message2 | <------ | Message3 |
* +----------+ +----------+ +----------+
* | | |
* +--------------------+--------------------+
* |
* | 构成
* |
* V
* +--------------+
* | MessageChain |
* +--------------+
*
*
* 但注意: 不能 `String + Message`. 只能 `Message + String`
*
* @see PlainText 纯文本
* @see Image 图片
@ -49,7 +65,7 @@ interface Message {
* [MessageChain] , 每个 [Message] 类型都拥有一个`伴生对象`(companion object) 来持有一个 Key
* [MessageChain.get] 时将会使用到这个 Key 进行判断类型.
*
* @param M 指代持有的消息类型
* @param M 指代持有这个 Key 的消息类型
*/
interface Key<M : Message>

View File

@ -33,7 +33,7 @@ internal fun IoBuffer.parseLongText0x19(): PlainText {
internal fun IoBuffer.parseMessageImage0x06(): Image {
discardExact(1)
//MiraiLogger.logDebug(this.toUHexString())
//MiraiLogger.debug(this.toUHexString())
val filenameLength = readShort()
discardExact(filenameLength.toInt())

View File

@ -26,7 +26,6 @@ import net.mamoe.mirai.qqAccount
import net.mamoe.mirai.utils.BotNetworkConfiguration
import net.mamoe.mirai.utils.OnlineStatus
import net.mamoe.mirai.utils.io.*
import net.mamoe.mirai.utils.log
import net.mamoe.mirai.utils.solveCaptcha
import kotlin.coroutines.CoroutineContext
@ -39,7 +38,7 @@ internal class TIMBotNetworkHandler internal constructor(private val bot: Bot) :
override val coroutineContext: CoroutineContext =
Dispatchers.Default + CoroutineExceptionHandler { _, e ->
bot.logger.log(e)
bot.logger.error(e)
} + SupervisorJob()
@ -61,7 +60,7 @@ internal class TIMBotNetworkHandler internal constructor(private val bot: Bot) :
override suspend fun login(configuration: BotNetworkConfiguration): LoginResult = withContext(this.coroutineContext) {
TIMProtocol.SERVER_IP.forEach { ip ->
bot.logger.logPurple("Connecting server $ip")
bot.logger.warning("Connecting server $ip")
socket = BotSocketAdapter(ip, configuration)
loginResult = CompletableDeferred()
@ -69,7 +68,7 @@ internal class TIMBotNetworkHandler internal constructor(private val bot: Bot) :
socket.resendTouch().takeIf { it != LoginResult.TIMEOUT }?.let { return@withContext it }
println()
bot.logger.logPurple("Timeout. Retrying next server")
bot.logger.warning("Timeout. Retrying next server")
socket.close()
}
@ -86,7 +85,7 @@ internal class TIMBotNetworkHandler internal constructor(private val bot: Bot) :
add(EventPacketHandler(session).asNode(EventPacketHandler))
add(ActionPacketHandler(session).asNode(ActionPacketHandler))
bot.logger.logPurple("Successfully logged in")
bot.logger.warning("Successfully logged in")
}
private lateinit var sessionKey: ByteArray
@ -134,15 +133,14 @@ internal class TIMBotNetworkHandler internal constructor(private val bot: Bot) :
close()
return
} catch (e: ReadPacketInternalException) {
bot.logger.logError("Socket channel read failed: ${e.message}")
bot.logger.error("Socket channel read failed: ${e.message}")
continue
} catch (e: Throwable) {
bot.logger.logError("Caught unexpected exceptions")
bot.logger.logError(e)
bot.logger.error("Caught unexpected exceptions", e)
continue
} finally {
if (!buffer.canRead() || buffer.readRemaining == 0) {//size==0
//bot.logger.logDebug("processReceive: Buffer cannot be read")
//bot.logger.debug("processReceive: Buffer cannot be read")
buffer.release(IoBuffer.Pool)
continue
}// sometimes exceptions are thrown without this `if` clause
@ -156,7 +154,7 @@ internal class TIMBotNetworkHandler internal constructor(private val bot: Bot) :
try {
distributePacket(it.parseServerPacket(buffer.readRemaining))
} catch (e: Exception) {
bot.logger.logError(e)
bot.logger.error(e)
}
}
}
@ -197,17 +195,17 @@ internal class TIMBotNetworkHandler internal constructor(private val bot: Bot) :
try {
packet.decode()
} catch (e: Exception) {
e.log()
bot.logger.logDebug("Packet=$packet")
bot.logger.logDebug("Packet size=" + packet.input.readBytes().size)
bot.logger.logDebug("Packet data=" + packet.input.readBytes().toUHexString())
bot.logger.error("When distributePacket", e)
bot.logger.debug("Packet=$packet")
bot.logger.debug("Packet size=" + packet.input.readBytes().size)
bot.logger.debug("Packet data=" + packet.input.readBytes().toUHexString())
packet.close()
throw e
}
packet.use {
packet::class.simpleName?.takeIf { !it.endsWith("Encrypted") && !it.endsWith("Raw") }?.let {
bot.logger.logCyan("Packet received: $packet")
bot.logger.verbose("Packet received: $packet")
}
// Remove first to release the lock
@ -284,7 +282,7 @@ internal class TIMBotNetworkHandler internal constructor(private val bot: Bot) :
val shouldBeSent = buffer.readRemaining
check(channel.send(buffer) == shouldBeSent) { "Buffer is not entirely sent. Required sent length=$shouldBeSent, but after channel.send, buffer remains ${buffer.readBytes().toUHexString()}" }//JVM: withContext(IO)
} catch (e: SendPacketInternalException) {
bot.logger.logError("Caught SendPacketInternalException: ${e.cause?.message}")
bot.logger.error("Caught SendPacketInternalException: ${e.cause?.message}")
bot.reinitializeNetworkHandler(configuration, e)
return@withContext
} finally {
@ -292,7 +290,7 @@ internal class TIMBotNetworkHandler internal constructor(private val bot: Bot) :
}
}
bot.logger.logGreen("Packet sent: $packet")
bot.logger.info("Packet sent: $packet")
PacketSentEvent(bot, packet).broadcast()
@ -340,7 +338,7 @@ internal class TIMBotNetworkHandler internal constructor(private val bot: Bot) :
if (packet.serverIP != null) {//redirection
socket.close()
socket = BotSocketAdapter(packet.serverIP!!, socket.configuration)
bot.logger.logPurple("Redirecting to ${packet.serverIP}")
bot.logger.warning("Redirecting to ${packet.serverIP}")
loginResult.complete(socket.resendTouch())
} else {//password submission
this.loginIP = packet.loginIP
@ -406,7 +404,7 @@ internal class TIMBotNetworkHandler internal constructor(private val bot: Bot) :
is CaptchaTransmissionResponsePacket -> {
//packet is ServerCaptchaWrongPacket
if (this.captchaSectionId == 0) {
bot.logger.logPurple("验证码错误, 请重新输入")
bot.logger.warning("验证码错误, 请重新输入")
this.captchaSectionId = 1
this.captchaCache = null
}
@ -478,7 +476,7 @@ internal class TIMBotNetworkHandler internal constructor(private val bot: Bot) :
is SessionKeyResponsePacket -> {
sessionKey = packet.sessionKey
bot.logger.logPurple("sessionKey = ${sessionKey.toUHexString()}")
bot.logger.warning("sessionKey = ${sessionKey.toUHexString()}")
heartbeatJob = launch {
while (socket.isOpen) {
@ -492,7 +490,7 @@ internal class TIMBotNetworkHandler internal constructor(private val bot: Bot) :
sessionKey
).sendAndExpect<HeartbeatPacket.Response>().join()
} == null) {
bot.logger.logPurple("Heartbeat timed out")
bot.logger.warning("Heartbeat timed out")
bot.reinitializeNetworkHandler(configuration, HeartbeatTimeoutException())
return@launch
}

View File

@ -2,9 +2,6 @@
package net.mamoe.mirai.network.protocol.tim
import net.mamoe.mirai.utils.solveIpAddress
object TIMProtocol {
val SERVER_IP: List<String> = {
//add("183.60.56.29")
@ -17,7 +14,7 @@ object TIMProtocol {
"sz8.tencent.com",
"sz9.tencent.com",
"sz2.tencent.com"
).forEach { list.add(solveIpAddress(it)) }
).forEach { list.add(it) }
list.toList()
}()//不使用lazy, 在初始化时就加载.

View File

@ -14,7 +14,6 @@ import net.mamoe.mirai.network.protocol.tim.packet.ServerPacket
import net.mamoe.mirai.network.protocol.tim.packet.event.ServerEventPacket
import net.mamoe.mirai.network.protocol.tim.packet.login.RequestSKeyPacket
import net.mamoe.mirai.network.qqAccount
import net.mamoe.mirai.utils.log
/**
* 动作: 获取好友列表, 点赞, 踢人等.
@ -50,7 +49,7 @@ class ActionPacketHandler(session: BotSession) : PacketHandler(session) {
try {
requestSKey()
} catch (e: Throwable) {
e.log()
bot.logger.error(e)
}
}
}

View File

@ -16,7 +16,7 @@ class UnknownServerPacket(
) : ServerPacket(input) {
override fun decode() {
val raw = this.input.readBytes()
MiraiLogger.logDebug("UnknownServerPacket data: " + raw.toUHexString())
MiraiLogger.debug("UnknownServerPacket data: " + raw.toUHexString())
}
class Encrypted(

View File

@ -50,7 +50,7 @@ abstract class ServerEventPacket(input: ByteReadPacket, val eventIdentity: Event
)
discardExact(2)
val type = readUShort()
//DebugLogger.logPurple("unknown2Byte+byte = ${unknown2Byte.toUHexString()} ${type.toUHexString()}")
//DebugLogger.warning("unknown2Byte+byte = ${unknown2Byte.toUHexString()} ${type.toUHexString()}")
return when (type.toUInt()) {
0x00C4u -> {
discardExact(13)
@ -90,7 +90,7 @@ abstract class ServerEventPacket(input: ByteReadPacket, val eventIdentity: Event
//"02 10", "00 12" -> ServerUnknownEventPacket(input, eventIdentity)
else -> {//0x00 79u, 可能是正在输入的包
MiraiLogger.logDebug("UnknownEvent type = ${type.toByteArray().toUHexString()}")
MiraiLogger.debug("UnknownEvent type = ${type.toByteArray().toUHexString()}")
UnknownServerEventPacket(input, eventIdentity)
}
}.applyId(id).applySequence(sequenceId)
@ -126,7 +126,7 @@ abstract class ServerEventPacket(input: ByteReadPacket, val eventIdentity: Event
class IgnoredServerEventPacket(val eventId: ByteArray, private val showData: Boolean = false, input: ByteReadPacket, eventIdentity: EventPacketIdentity) : ServerEventPacket(input, eventIdentity) {
override fun decode() {
if (showData) {
MiraiLogger.logDebug("IgnoredServerEventPacket data: " + this.input.readBytes().toUHexString())
MiraiLogger.debug("IgnoredServerEventPacket data: " + this.input.readBytes().toUHexString())
} else {
this.input.discard()
}
@ -138,6 +138,6 @@ class IgnoredServerEventPacket(val eventId: ByteArray, private val showData: Boo
*/
class UnknownServerEventPacket(input: ByteReadPacket, eventIdentity: EventPacketIdentity) : ServerEventPacket(input, eventIdentity) {
override fun decode() {
MiraiLogger.logDebug("UnknownServerEventPacket data: " + this.input.readBytes().toUHexString())
MiraiLogger.debug("UnknownServerEventPacket data: " + this.input.readBytes().toUHexString())
}
}

View File

@ -40,8 +40,8 @@ class RequestSKeyPacket(
discardExact(4)
//debugDiscardExact(2)
sKey = this.readString(10)//16??
DebugLogger.logPurple("SKey=$sKey")
DebugLogger.logPurple("SKey 包后面${this.readRemainingBytes().toUHexString()}")
DebugLogger.warning("SKey=$sKey")
DebugLogger.warning("SKey 包后面${this.readRemainingBytes().toUHexString()}")
}
}
}

View File

@ -3,6 +3,23 @@ package net.mamoe.mirai.utils
import net.mamoe.mirai.Bot
import kotlin.jvm.JvmOverloads
/**
* 用于创建默认的日志记录器. 在一些需要使用日志的 Mirai 的组件, [Bot], 都会通过这个函数构造日志记录器
* 可直接修改这个变量的值来重定向日志输出.
*/
var DefaultLogger: (identity: String?) -> MiraiLogger = { PlatformLogger() }
/**
* 当前平台的默认的日志记录器.
* _JVM 控制台_ 端的实现为 [println]
* _Android_ 端的实现为 [android.util.Log]
*
* 不应该直接构造这个类的实例. 请使用 [DefaultLogger]
*/
expect open class PlatformLogger @JvmOverloads internal constructor(identity: String? = null) : MiraiLoggerPlatformBase
/**
* 日志记录器. 所有的输出均依赖于它.
* 不同的对象可能拥有只属于自己的 logger. 通过 [identity] 来区分.
@ -28,38 +45,67 @@ interface MiraiLogger {
* val bot = Bot( ... )
* bot.follower = MyOwnLogger()
*
* bot.logInfo("Hi")
* bot.info("Hi")
* ```
* 在这个例子中的 `MyOwnLogger` 将可以记录到 "Hi".
*/
var follower: MiraiLogger?
fun logInfo(any: Any?) = log(any)
/**
* 记录一个 `verbose` 级别的日志.
*/
fun verbose(any: Any?)
fun log(e: Throwable)
fun verbose(e: Throwable?) = verbose(null, e)
fun verbose(message: String?, e: Throwable?)
fun log(any: Any?)
/**
* 记录一个 `debug` 级别的日志.
*/
fun debug(any: Any?)
fun logError(any: Any?)
fun debug(e: Throwable?) = debug(null, e)
fun debug(message: String?, e: Throwable?)
fun logError(e: Throwable) = log(e)
fun logDebug(any: Any?)
/**
* 记录一个 `info` 级别的日志.
*/
fun info(any: Any?)
fun logCyan(any: Any?)
fun info(e: Throwable?) = info(null, e)
fun info(message: String?, e: Throwable?)
fun logPurple(any: Any?)
fun logGreen(any: Any?)
/**
* 记录一个 `warning` 级别的日志.
*/
fun warning(any: Any?)
fun warning(e: Throwable?) = warning(null, e)
fun warning(message: String?, e: Throwable?)
/**
* 记录一个 `error` 级别的日志.
*/
fun error(e: Any?)
fun error(e: Throwable?) = error(null, e)
fun error(message: String?, e: Throwable?)
fun logBlue(any: Any?)
/**
* 添加一个 [follower], 返回 [follower]
* 它只会把 `this` 的属性 [MiraiLogger.follower] 修改为这个函数的参数 [follower], 然后返回这个参数.
* [MiraiLogger.follower] 已经有值, 则会替换掉这个值.
*
* +------+ +----------+ +----------+ +----------+
* | base | <-- | follower | <-- | follower | <-- | follower |
* +------+ +----------+ +----------+ +----------+
*
* @see follower
* @return [follower]
*/
operator fun plus(follower: MiraiLogger): MiraiLogger
@ -72,6 +118,29 @@ interface MiraiLogger {
operator fun plusAssign(follower: MiraiLogger)
}
/**
* 不做任何事情的 logger, keep silent.
*/
@Suppress("unused")
object Silent : PlatformLogger() {
override val identity: String? = null
override fun error0(any: Any?) {
}
override fun debug0(any: Any?) {
}
override fun warning0(any: Any?) {
}
override fun verbose0(any: Any?) {
}
override fun info0(any: Any?) {
}
}
/**
* 平台基类.
* 实现了 [follower] 的调用传递.
@ -80,58 +149,66 @@ interface MiraiLogger {
abstract class MiraiLoggerPlatformBase : MiraiLogger {
final override var follower: MiraiLogger? = null
final override fun logInfo(any: Any?) = log(any)
final override fun log(e: Throwable) {
log0(e)
follower?.log(e)
final override fun verbose(any: Any?) {
follower?.verbose(any)
verbose0(any)
}
final override fun log(any: Any?) {
log0(any)
follower?.log(any)
final override fun verbose(message: String?, e: Throwable?) {
follower?.verbose(message, e)
verbose0(message, e)
}
final override fun logError(any: Any?) {
logError0(any)
follower?.logError(any)
final override fun debug(any: Any?) {
follower?.debug(any)
debug0(any)
}
override fun logError(e: Throwable) = log(e)
final override fun logDebug(any: Any?) {
logDebug0(any)
follower?.logDebug(any)
final override fun debug(message: String?, e: Throwable?) {
follower?.debug(message, e)
debug0(message, e)
}
final override fun logCyan(any: Any?) {
logCyan0(any)
follower?.logCyan(any)
final override fun info(any: Any?) {
follower?.info(any)
info0(any)
}
final override fun logPurple(any: Any?) {
logPurple0(any)
follower?.logPurple(any)
final override fun info(message: String?, e: Throwable?) {
follower?.info(message, e)
info0(message, e)
}
final override fun logGreen(any: Any?) {
logGreen0(any)
follower?.logGreen(any)
final override fun warning(any: Any?) {
follower?.warning(any)
warning0(any)
}
final override fun logBlue(any: Any?) {
logBlue0(any)
follower?.logBlue(any)
final override fun warning(message: String?, e: Throwable?) {
follower?.warning(message, e)
warning0(message, e)
}
protected abstract fun log0(e: Throwable)
protected abstract fun log0(any: Any?)
protected abstract fun logError0(any: Any?)
protected abstract fun logDebug0(any: Any?)
protected abstract fun logCyan0(any: Any?)
protected abstract fun logPurple0(any: Any?)
protected abstract fun logGreen0(any: Any?)
protected abstract fun logBlue0(any: Any?)
final override fun error(e: Any?) {
follower?.error(e)
error0(e)
}
final override fun error(message: String?, e: Throwable?) {
follower?.error(message, e)
error0(message, e)
}
protected abstract fun verbose0(any: Any?)
protected abstract fun verbose0(message: String?, e: Throwable?)
protected abstract fun debug0(any: Any?)
protected abstract fun debug0(message: String?, e: Throwable?)
protected abstract fun info0(any: Any?)
protected abstract fun info0(message: String?, e: Throwable?)
protected abstract fun warning0(any: Any?)
protected abstract fun warning0(message: String?, e: Throwable?)
protected abstract fun error0(any: Any?)
protected abstract fun error0(message: String?, e: Throwable?)
override fun plus(follower: MiraiLogger): MiraiLogger {
this.follower = follower
@ -142,53 +219,3 @@ abstract class MiraiLoggerPlatformBase : MiraiLogger {
if (this.follower == null) this.follower = follower
else this.follower!! += follower
}
/**
* 用于创建默认的日志记录器. 在一些需要使用日志的 Mirai 的组件, [Bot], 都会通过这个函数构造日志记录器
*/
var DefaultLogger: (identity: String?) -> MiraiLogger = { PlatformLogger() }
/**
* 当前平台的默认的日志记录器.
* _JVM 控制台_ 端的实现为 [println]
*
* 不应该直接构造这个类的实例. 需使用 [DefaultLogger]
*/
expect open class PlatformLogger @JvmOverloads internal constructor(identity: String? = null) : MiraiLoggerPlatformBase
/**
* 不作任何事情的 logger
*/
@Suppress("unused")
object NoLogger : PlatformLogger() {
override val identity: String? = null
override fun log0(e: Throwable) {
}
override fun log0(any: Any?) {
}
override fun logError0(any: Any?) {
}
override fun logDebug0(any: Any?) {
}
override fun logCyan0(any: Any?) {
}
override fun logPurple0(any: Any?) {
}
override fun logGreen0(any: Any?) {
}
override fun logBlue0(any: Any?) {
}
}
/**
* 在顶层日志记录这个异常
*/
fun Throwable.log() = MiraiLogger.log(this)

View File

@ -0,0 +1,2 @@
package net.mamoe.mirai.utils

View File

@ -30,11 +30,6 @@ expect fun crc32(key: ByteArray): Int
*/
expect fun md5(byteArray: ByteArray): ByteArray
/**
* Hostname 解析 IP 地址
*/
expect fun solveIpAddress(hostname: String): String // TODO: 2019/10/28 是否有必要?
/**
* Localhost 解析
*/

View File

@ -7,29 +7,29 @@ import net.mamoe.mirai.utils.MiraiLogger
internal object DebugLogger : MiraiLogger by DefaultLogger("Packet Debug")
internal fun debugPrintln(any: Any?) = DebugLogger.logPurple(any)
internal fun debugPrintln(any: Any?) = DebugLogger.warning(any)
internal fun ByteArray.debugPrint(name: String): ByteArray {
DebugLogger.logPurple(name + "=" + this.toUHexString())
DebugLogger.warning(name + "=" + this.toUHexString())
return this
}
@Deprecated("Low efficiency, only for debug purpose", ReplaceWith("this"))
internal fun IoBuffer.debugPrint(name: String): IoBuffer {
val readBytes = this.readBytes()
DebugLogger.logPurple(name + "=" + readBytes.toUHexString())
DebugLogger.warning(name + "=" + readBytes.toUHexString())
return readBytes.toIoBuffer()
}
@Deprecated("Low efficiency, only for debug purpose", ReplaceWith("discardExact(n)"))
internal fun Input.debugDiscardExact(n: Number, name: String = "") {
DebugLogger.logPurple("Discarded($n) $name=" + this.readBytes(n.toInt()).toUHexString())
DebugLogger.warning("Discarded($n) $name=" + this.readBytes(n.toInt()).toUHexString())
}
@Deprecated("Low efficiency, only for debug purpose", ReplaceWith("this"))
internal fun ByteReadPacket.debugPrint(name: String = ""): ByteReadPacket {
val bytes = this.readBytes()
DebugLogger.logPurple("ByteReadPacket $name=" + bytes.toUHexString())
DebugLogger.warning("ByteReadPacket $name=" + bytes.toUHexString())
return bytes.toReadPacket()
}
@ -74,10 +74,4 @@ internal fun ByteArray.printColorizedHex(name: String = "", ignoreUntilFirstCons
println(toUHexString().printColorize(ignoreUntilFirstConst))
}
println()
}
/**
* TODO 这两个方法不应该 MPP
*/
expect fun printCompareHex(hex1s: String, hex2s: String): String
expect fun String.printColorize(ignoreUntilFirstConst: Boolean = false): String
}

View File

@ -1,11 +1,79 @@
@file:Suppress("ObjectPropertyName", "MayBeConstant", "NonAsciiCharacters", "SpellCheckingInspection")
@file:Suppress("ObjectPropertyName", "MayBeConstant", "NonAsciiCharacters", "SpellCheckingInspection", "unused")
package net.mamoe.mirai.utils.io
import kotlinx.io.core.toByteArray
import net.mamoe.mirai.network.protocol.tim.TIMProtocol
import java.lang.reflect.Field
import java.util.*
import kotlin.math.max
import kotlin.reflect.KProperty
/**
* 匹配已知 hex 常量并格式化后打印到控制台.
*
* 低效率, 仅调试使用.
*/
internal fun String.printColorize(ignoreUntilFirstConst: Boolean): String = with(HexComparator) { colorize(ignoreUntilFirstConst) }
/**
* 比较两个 hex 并格式化后打印到控制台.
*
* 低效率, 仅调试使用.
*/
internal fun printCompareHex(hex1s: String, hex2s: String): String = with(HexComparator) { compare(hex1s, hex2s) }
data class NamedHexElement(
val name: String,
val value: String
)
/**
* 初始化用于匹配的 Hex 列表
*/
private fun LinkedHashSet<NamedHexElement>.initConstFileds() {
listOf(
TestConsts,
TIMProtocol,
PacketIds
).forEach { obj ->
obj::class.members.filterIsInstance<KProperty<*>>().forEach { property ->
add(NamedHexElement(property.name, property.getter.call().toString()))
}
}
}
private object TestConsts {
val NIU_BI = "牛逼".toByteArray().toUHexString()
val _1994701021 = 1994701021.toUHexString(" ")
val _1040400290 = 1040400290.toUHexString(" ")
val _580266363 = 580266363.toUHexString(" ")
val _1040400290_ = "3E 03 3F A2"
val _1994701021_ = "76 E4 B8 DD"
val _jiahua_ = "B1 89 BE 09"
val _Him188moe_ = "Him188moe".toByteArray().toUHexString()
val 发图片2 = "发图片2".toByteArray().toUHexString()
val 发图片群 = "发图片群".toByteArray().toUHexString()
val 发图片 = "发图片".toByteArray().toUHexString()
val = "".toByteArray().toUHexString()
val 你好 = "你好".toByteArray().toUHexString()
val MESSAGE_TAIL_10404 =
"0E 00 07 01 00 04 00 00 00 09 19 00 18 01 00 15 AA 02 12 9A 01 0F 80 01 01 C8 01 00 F0 01 00 F8 01 00 90 02 00"
.replace(" ", " ")
val FONT_10404 = "E5 BE AE E8 BD AF E9 9B 85 E9 BB 91"
val varint580266363 = "FB D2 D8 94 02"
val varint1040400290 = "A2 FF 8C F0 03"
var varint1994701021 = "DD F1 92 B7 07"
}
@Suppress("SpellCheckingInspection")
private object PacketIds {
val heartbeat = "00 58"
val friendmsgsend = "00 CD"
val friendmsgevent = "00 CE"
}
/**
* Hex 比较器, 并着色已知常量
@ -16,7 +84,7 @@ import kotlin.math.max
* @author NaturalHG
* @author Him188moe
*/
internal object HexComparator {
private object HexComparator {
private val RED = "\u001b[31m"
private val GREEN = "\u001b[33m"
@ -24,49 +92,8 @@ internal object HexComparator {
private val BLUE = "\u001b[34m"
@Suppress("unused")
class ConstMatcher constructor(hex: String) {
private val matches = LinkedList<Match>()
object TestConsts {
val NIU_BI = "牛逼".toByteArray().toUHexString()
val _1994701021 = 1994701021.toUHexString(" ")
val _1040400290 = 1040400290.toUHexString(" ")
val _580266363 = 580266363.toUHexString(" ")
val _1040400290_ = "3E 03 3F A2"
val _1994701021_ = "76 E4 B8 DD"
val _jiahua_ = "B1 89 BE 09"
val _Him188moe_ = "Him188moe".toByteArray().toUHexString()
val 发图片2 = "发图片2".toByteArray().toUHexString()
val 发图片群 = "发图片群".toByteArray().toUHexString()
val 发图片 = "发图片".toByteArray().toUHexString()
val = "".toByteArray().toUHexString()
val 你好 = "你好".toByteArray().toUHexString()
val MESSAGE_TAIL_10404 = "0E 00 07 01 00 04 00 00 00 09 19 00 18 01 00 15 AA 02 12 9A 01 0F 80 01 01 C8 01 00 F0 01 00 F8 01 00 90 02 00"
.replace(" ", " ")
val FONT_10404 = "E5 BE AE E8 BD AF E9 9B 85 E9 BB 91"
val varint580266363 = "FB D2 D8 94 02"
val varint1040400290 = "A2 FF 8C F0 03"
var varint1994701021 = "DD F1 92 B7 07"
}
@Suppress("SpellCheckingInspection")
object PacketIds {
val heartbeat = "00 58"
val friendmsgsend = "00 CD"
val friendmsgevent = "00 CE"
}
init {
CONST_FIELDS.forEach { field ->
for (match in match(hex, field)) {
matches.add(Match(match, field.name))
}
}
}
private class ConstMatcher constructor(hex: String) {
private val matches = linkedSetOf<Match>()
fun getMatchedConstName(hexNumber: Int): String? {
for (match in this.matches) {
@ -79,41 +106,36 @@ internal object HexComparator {
private class Match internal constructor(val range: IntRange, val constName: String)
companion object {
private val CONST_FIELDS: List<Field> = listOf(
TestConsts::class.java,
TIMProtocol::class.java,
PacketIds::class.java
).map { it.declaredFields }.flatMap { fields ->
fields.map { field ->
field.trySetAccessible()
field
init {
TIMProtocol::class.members.filterIsInstance<KProperty<*>>().forEach {
for (match in match(hex, it.getter.call().toString())) {
matches.add(Match(match, it.getter.call().toString()))
}
}
}
private fun match(hex: String, field: Field): List<IntRange> {
companion object {
val CONST_FIELDS: Set<NamedHexElement> = linkedSetOf<NamedHexElement>().apply { initConstFileds() }
}
private fun match(hex: String, field: String): Set<IntRange> {
val constValue: String
try {
constValue = (field.get(null) as String).trim { it <= ' ' }
constValue = field.trim { it <= ' ' }
if (constValue.length / 3 <= 3) {//Minimum numbers of const hex bytes
return LinkedList()
return linkedSetOf()
}
} catch (e: IllegalAccessException) {
throw RuntimeException(e)
} catch (ignored: ClassCastException) {
return LinkedList()
return linkedSetOf()
}
return object : LinkedList<IntRange>() {
init {
var index = -1
index = hex.indexOf(constValue, index + 1)
while (index != -1) {
add(IntRange(index / 3, (index + constValue.length) / 3))
return mutableSetOf<IntRange>().apply {
var index = -1
index = hex.indexOf(constValue, index + 1)
while (index != -1) {
add(IntRange(index / 3, (index + constValue.length) / 3))
index = hex.indexOf(constValue, index + 1)
}
index = hex.indexOf(constValue, index + 1)
}
}
}
@ -222,12 +244,12 @@ internal object HexComparator {
}
return builder.append(" ").append(dif).append(" 个不同").append("\n")
.append(numberLine).append("\n")
.append(hex1ConstName).append("\n")
.append(hex1b).append("\n")
.append(hex2b).append("\n")
.append(hex2ConstName).append("\n")
.toString()
.append(numberLine).append("\n")
.append(hex1ConstName).append("\n")
.append(hex1b).append("\n")
.append(hex2b).append("\n")
.append(hex2ConstName).append("\n")
.toString()
}
@ -267,15 +289,15 @@ internal object HexComparator {
}
return builder.append("\n")
.append(numberLine).append("\n")
.append(if (firstConst == null) hex1ConstName else {
with(hex1ConstName) {
val index = indexOf(firstConst)
if (index == -1) toString() else " " + substring(index, length)
}
}).append("\n")
.append(hex1b).append("\n")
.toString()
.append(numberLine).append("\n")
.append(if (firstConst == null) hex1ConstName else {
with(hex1ConstName) {
val index = indexOf(firstConst)
if (index == -1) toString() else " " + substring(index, length)
}
}).append("\n")
.append(hex1b).append("\n")
.toString()
}
@ -288,7 +310,4 @@ internal object HexComparator {
} else number.toString()
}
}
actual fun String.printColorize(ignoreUntilFirstConst: Boolean): String = with(HexComparator) { colorize(ignoreUntilFirstConst) }
actual fun printCompareHex(hex1s: String, hex2s: String): String = with(HexComparator) { compare(hex1s, hex2s) }
}

View File

@ -47,7 +47,7 @@ fun ByteReadPacket.parseServerPacket(size: Int): ServerPacket {
135 -> {//包数据错误. 目前怀疑是 tlv0006
this.readRemainingBytes().cutTail(1).decryptBy(TIMProtocol.shareKey).read {
discardExact(51)
MiraiLogger.logError("Internal logError: " + readLVString())//抱歉,请重新输入密码。
MiraiLogger.error("Internal error: " + readLVString())//抱歉,请重新输入密码。
}
LoginResult.INTERNAL_ERROR
@ -63,8 +63,8 @@ fun ByteReadPacket.parseServerPacket(size: Int): ServerPacket {
else -> LoginResult.UNKNOWN
/*
//unknown
63 -> throw IllegalArgumentException(bytes.size.toString() + " (Unknown logError)")
351 -> throw IllegalArgumentException(bytes.size.toString() + " (Unknown logError)")
63 -> throw IllegalArgumentException(bytes.size.toString() + " (Unknown error)")
351 -> throw IllegalArgumentException(bytes.size.toString() + " (Unknown error)")
else -> throw IllegalArgumentException(bytes.size.toString())*/
}, this).applySequence(sequenceId)

View File

@ -22,17 +22,17 @@ import kotlin.math.min
internal actual suspend fun solveCaptcha(captchaBuffer: IoBuffer): String? = captchaLock.withLock {
val captcha = captchaBuffer.readBytes()
withContext(Dispatchers.IO) {
MiraiLogger.logCyan(ImageIO.read(captcha.inputStream()).createCharImg())
MiraiLogger.verbose(ImageIO.read(captcha.inputStream()).createCharImg())
}
MiraiLogger.logCyan("需要验证码登录, 验证码为 4 字母")
MiraiLogger.verbose("需要验证码登录, 验证码为 4 字母")
try {
File(System.getProperty("user.dir") + "/temp/Captcha.png")
.let { withContext(Dispatchers.IO) { it.createNewFile(); it.writeBytes(captcha) } }
MiraiLogger.logCyan("若看不清字符图片, 请查看 Mirai 目录下 /temp/Captcha.png")
MiraiLogger.verbose("若看不清字符图片, 请查看 Mirai 目录下 /temp/Captcha.png")
} catch (e: Exception) {
MiraiLogger.logCyan("无法写出验证码文件(${e.message}), 请尝试查看以上字符图片")
MiraiLogger.verbose("无法写出验证码文件(${e.message}), 请尝试查看以上字符图片")
}
MiraiLogger.logCyan("若要更换验证码, 请直接回车")
MiraiLogger.verbose("若要更换验证码, 请直接回车")
readLine()?.takeUnless { it.isEmpty() || it.length != 4 }
}

View File

@ -1,5 +1,7 @@
package net.mamoe.mirai.utils
import java.io.ByteArrayOutputStream
import java.io.PrintStream
import java.text.SimpleDateFormat
import java.util.*
@ -11,21 +13,41 @@ actual typealias PlatformLogger = Console
open class Console @JvmOverloads internal constructor(
override val identity: String? = null
) : MiraiLoggerPlatformBase() {
override fun logGreen0(any: Any?) = println(any.toString(), LoggerTextFormat.GREEN)
override fun logPurple0(any: Any?) = println(any.toString(), LoggerTextFormat.LIGHT_PURPLE)
override fun logBlue0(any: Any?) = println(any.toString(), LoggerTextFormat.BLUE)
override fun logCyan0(any: Any?) = println(any.toString(), LoggerTextFormat.LIGHT_CYAN)
override fun logError0(any: Any?) = println(any.toString(), LoggerTextFormat.RED)
override fun log0(e: Throwable) = e.printStackTrace()
override fun log0(any: Any?) = println(any.toString(), LoggerTextFormat.WHITE)
override fun logDebug0(any: Any?) {
if (DEBUGGING) {
println(any.toString(), LoggerTextFormat.YELLOW)
}
override fun verbose0(any: Any?) = println(any.toString(), LoggerTextFormat.LIGHT_GRAY)
override fun verbose0(message: String?, e: Throwable?) {
verbose(message.toString())
e?.printStackTrace()
}
override fun info0(any: Any?) = println(any.toString(), LoggerTextFormat.GREEN)
override fun info0(message: String?, e: Throwable?) {
info(message.toString())
e?.printStackTrace()
}
override fun warning0(any: Any?) = println(any.toString(), LoggerTextFormat.YELLOW)
override fun warning0(message: String?, e: Throwable?) {
warning(message.toString())
e?.printStackTrace()
}
override fun error0(any: Any?) = println(any.toString(), LoggerTextFormat.RED)
override fun error0(message: String?, e: Throwable?) {
error(message.toString())
e?.printStackTrace()
}
override fun debug0(any: Any?) {
println(any.toString(), LoggerTextFormat.CYAN)
}
override fun debug0(message: String?, e: Throwable?) {
debug(message.toString())
e?.printStackTrace()
}
private fun println(value: String?, color: LoggerTextFormat) {
val time = SimpleDateFormat("HH:mm:ss").format(Date())
val time = SimpleDateFormat("HH:mm:ss", Locale.SIMPLIFIED_CHINESE).format(Date())
if (identity == null) {
println("$color$time : $value")
@ -35,8 +57,6 @@ open class Console @JvmOverloads internal constructor(
}
}
private val DEBUGGING: Boolean by lazy {
//todo 添加环境变量检测
//avoid inspections
true
}
@Suppress("unused")
val Throwable.stacktraceString: String
get() = ByteArrayOutputStream().also { printStackTrace(PrintStream(it)) }.toString()

View File

@ -19,10 +19,6 @@ import java.util.zip.CRC32
actual val deviceName: String = InetAddress.getLocalHost().hostName
/*
* TODO: we may use libraries that provide these functions
*/
actual fun crc32(key: ByteArray): Int = CRC32().let { it.update(key); it.value.toInt() }
actual fun md5(byteArray: ByteArray): ByteArray = MessageDigest.getInstance("MD5").digest(byteArray)
@ -55,7 +51,7 @@ fun DataInput.md5(): ByteArray {
return digest.digest()
}
actual fun solveIpAddress(hostname: String): String = InetAddress.getByName(hostname).hostAddress
//actual fun solveIpAddress(hostname: String): String = InetAddress.getByName(hostname).hostAddress
actual fun localIpAddress(): String = InetAddress.getLocalHost().hostAddress

View File