Commit 35e9138c authored by ryoii's avatar ryoii

Merge remote-tracking branch 'origin'

parents 2b60130b 8ea7027f
......@@ -2,11 +2,34 @@
开发版本. 频繁更新, 不保证高稳定性
## `0.15.0` 2020/2/14
### mirai-core
- 新增事件: `BotReloginEvent``BotOfflineEvent.Dropped`
- `AtAll` 现在实现 `Message.Key`
- 新增 `BotConfiguration` DSL, 支持自动将设备信息存储在文件系统等
- 新增 `MessageSource.quote(Member)`
- 更好的网络层连接逻辑
- 密码错误后不再重试登录
- 掉线后尝试快速重连, 失败则普通重连 (#47)
- 有原因的登录失败时将抛出特定异常: `LoginFailedException`
- 默认心跳时间调整为 60s
### mirai-core-qqandroid
- 解决一些验证码无法识别的问题
- 忽略一些不需要处理的事件(机器人主动操作触发的事件)
## `0.14.0` 2020/2/13
### mirai-core
- **支持 at 全体成员: `AtAll`**
### mirai-core-qqandroid
- **支持 `AtAll` 的发送和解析**
- **修复某些情况下禁言处理异常**
......
# style guide
kotlin.code.style=official
# config
mirai_version=0.14.0
mirai_version=0.15.0
kotlin.incremental.multiplatform=true
kotlin.parallel.tasks.in.project=true
# kotlin
......
......@@ -67,11 +67,8 @@ internal class QQAndroidBotNetworkHandler(bot: QQAndroidBot) : BotNetworkHandler
private val packetReceiveLock: Mutex = Mutex()
private fun startPacketReceiverJobOrGet(): Job {
val job = _packetReceiverJob
if (job != null && job.isActive && channel.isOpen) {
return job
}
private fun startPacketReceiverJobOrKill(cancelCause: CancellationException? = null): Job {
_packetReceiverJob?.cancel(cancelCause)
return this.launch(CoroutineName("Incoming Packet Receiver")) {
while (channel.isOpen) {
......@@ -103,8 +100,10 @@ internal class QQAndroidBotNetworkHandler(bot: QQAndroidBot) : BotNetworkHandler
}
channel = PlatformSocket()
// TODO: 2020/2/14 连接多个服务器
channel.connect("113.96.13.208", 8080)
startPacketReceiverJobOrGet()
withTimeoutOrNull(3000) {
channel.connect("113.96.13.208", 8080)
} ?: error("timeout connecting server")
startPacketReceiverJobOrKill(CancellationException("reconnect"))
// logger.info("Trying login")
var response: WtLogin.Login.LoginPacketResponse = WtLogin.Login.SubCommand9(bot.client).sendAndExpect()
......@@ -532,5 +531,5 @@ internal class QQAndroidBotNetworkHandler(bot: QQAndroidBot) : BotNetworkHandler
super.close(cause)
}
override suspend fun awaitDisconnection() = _packetReceiverJob?.join() ?: Unit
override suspend fun awaitDisconnection() = supervisor.join()
}
\ No newline at end of file
......@@ -35,6 +35,7 @@ import net.mamoe.mirai.qqandroid.network.protocol.packet.IncomingPacketFactory
import net.mamoe.mirai.qqandroid.network.protocol.packet.OutgoingPacket
import net.mamoe.mirai.qqandroid.network.protocol.packet.buildResponseUniPacket
import net.mamoe.mirai.utils.MiraiInternalAPI
import net.mamoe.mirai.utils.debug
import net.mamoe.mirai.utils.io.discardExact
import net.mamoe.mirai.utils.io.read
import net.mamoe.mirai.utils.io.readString
......@@ -250,7 +251,7 @@ internal class OnlinePush {
)
}
else -> {
bot.network.logger.debug{"Unknown server messages $message"}
bot.network.logger.debug { "Unknown server messages $message" }
return NoPacket
}
}
......
......@@ -124,7 +124,7 @@ fun ByteReadPacket.decodeMultiClientToServerPackets() {
}
fun Map<Int, ByteArray>.printTLVMap(name: String = "", keyLength: Int = 2) =
debugPrintln("TLVMap $name= " + this.mapValues { (_, value) -> value.toUHexString() }.mapKeys {
DebugLogger.debug("TLVMap $name= " + this.mapValues { (_, value) -> value.toUHexString() }.mapKeys {
when (keyLength) {
1 -> it.key.toUByte().contentToString()
2 -> it.key.toUShort().contentToString()
......
/*
* Copyright 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.io.core.IoBuffer
import net.mamoe.mirai.Bot
import net.mamoe.mirai.network.BotNetworkHandler
import kotlin.coroutines.CoroutineContext
import kotlin.coroutines.EmptyCoroutineContext
/**
* 在各平台实现的默认的验证码处理器.
*/
actual var defaultLoginSolver: LoginSolver = object : LoginSolver() {
override suspend fun onSolvePicCaptcha(bot: Bot, data: IoBuffer): String? {
error("should be implemented manually by you")
}
override suspend fun onSolveSliderCaptcha(bot: Bot, url: String): String? {
error("should be implemented manually by you")
}
override suspend fun onSolveUnsafeDeviceLoginVerify(bot: Bot, url: String): String? {
error("should be implemented manually by you")
}
}
@Suppress("ClassName", "PropertyName")
actual open class BotConfiguration actual constructor() {
/**
* 日志记录器
*/
actual var botLoggerSupplier: ((Bot) -> MiraiLogger) = { DefaultLogger("Bot(${it.uin})") }
/**
* 网络层日志构造器
*/
actual var networkLoggerSupplier: ((BotNetworkHandler) -> MiraiLogger) = { DefaultLogger("Network(${it.bot.uin})") }
/**
* 设备信息覆盖. 默认使用随机的设备信息.
*/
actual var deviceInfo: ((Context) -> DeviceInfo)? = null
/**
* 父 [CoroutineContext]
*/
actual var parentCoroutineContext: CoroutineContext = EmptyCoroutineContext
/**
* 心跳周期. 过长会导致被服务器断开连接.
*/
actual var heartbeatPeriodMillis: Long = 60.secondsToMillis
/**
* 每次心跳时等待结果的时间.
* 一旦心跳超时, 整个网络服务将会重启 (将消耗约 5s). 除正在进行的任务 (如图片上传) 会被中断外, 事件和插件均不受影响.
*/
actual var heartbeatTimeoutMillis: Long = 2.secondsToMillis
/**
* 心跳失败后的第一次重连前的等待时间.
*/
actual var firstReconnectDelayMillis: Long = 5.secondsToMillis
/**
* 重连失败后, 继续尝试的每次等待时间
*/
actual var reconnectPeriodMillis: Long = 60.secondsToMillis
/**
* 最多尝试多少次重连
*/
actual var reconnectionRetryTimes: Int = 3
/**
* 验证码处理器
*/
actual var loginSolver: LoginSolver = defaultLoginSolver
actual companion object {
/**
* 默认的配置实例
*/
@JvmStatic
actual val Default = BotConfiguration()
}
actual operator fun _NoNetworkLog.unaryPlus() {
networkLoggerSupplier = supplier
}
/**
* 不记录网络层的 log.
* 网络层 log 包含包接收, 包发送, 和一些调试用的记录.
*/
@BotConfigurationDsl
actual val NoNetworkLog: _NoNetworkLog
get() = _NoNetworkLog
@BotConfigurationDsl
actual object _NoNetworkLog {
internal val supplier = { _: BotNetworkHandler -> SilentLogger }
}
}
/**
* 使用文件系统存储设备信息.
*/
@BotConfigurationDsl
inline class FileBasedDeviceInfo @BotConfigurationDsl constructor(val filepath: String) {
/**
* 使用 "device.json" 存储设备信息
*/
@BotConfigurationDsl
companion object ByDeviceDotJson
}
\ No newline at end of file
......@@ -16,40 +16,40 @@ import android.util.Log
* 不应该直接构造这个类的实例. 需使用 [DefaultLogger]
*/
actual open class PlatformLogger actual constructor(override val identity: String?) : MiraiLoggerPlatformBase() {
override fun verbose0(any: Any?) {
Log.v(identity, any?.toString() ?: "")
override fun verbose0(message: String?) {
Log.v(identity, message ?: "")
}
override fun verbose0(message: String?, e: Throwable?) {
Log.v(identity, message ?: "", e)
}
override fun debug0(any: Any?) {
Log.d(identity, any?.toString() ?: "")
override fun debug0(message: String?) {
Log.d(identity, message ?: "")
}
override fun debug0(message: String?, e: Throwable?) {
Log.d(identity, message ?: "", e)
}
override fun info0(any: Any?) {
Log.i(identity, any?.toString() ?: "")
override fun info0(message: String?) {
Log.i(identity, message ?: "")
}
override fun info0(message: String?, e: Throwable?) {
Log.i(identity, message ?: "", e)
}
override fun warning0(any: Any?) {
Log.w(identity, any?.toString() ?: "")
override fun warning0(message: String?) {
Log.w(identity, message ?: "")
}
override fun warning0(message: String?, e: Throwable?) {
Log.w(identity, message ?: "", e)
}
override fun error0(any: Any?) {
Log.e(identity, any?.toString() ?: "")
override fun error0(message: String?) {
Log.e(identity, message ?: "")
}
override fun error0(message: String?, e: Throwable?) {
......
......@@ -14,13 +14,40 @@ import android.net.wifi.WifiManager
import android.os.Build
import android.telephony.TelephonyManager
import kotlinx.io.core.toByteArray
import kotlinx.serialization.Serializable
import kotlinx.serialization.Transient
import kotlinx.serialization.UnstableDefault
import kotlinx.serialization.json.Json
import java.io.File
/**
* 加载一个设备信息. 若文件不存在或为空则随机并创建一个设备信息保存.
*/
@UseExperimental(UnstableDefault::class)
fun File.loadAsDeviceInfo(context: Context): DeviceInfo {
if (!this.exists() || this.length() == 0L) {
return SystemDeviceInfo(context).also {
this.writeText(Json.plain.stringify(SystemDeviceInfo.serializer(), it))
}
}
return Json.nonstrict.parse(DeviceInfoData.serializer(), this.readText()).also {
it.context = context
}
}
/**
* 部分引用指向 [Build].
* 部分需要权限, 若无权限则会使用默认值.
*/
actual open class SystemDeviceInfo actual constructor(context: Context) : DeviceInfo(context) {
@Serializable
actual open class SystemDeviceInfo actual constructor() : DeviceInfo() {
actual constructor(context: Context) : this() {
this.context = context
}
@Transient
final override lateinit var context: Context
override val display: ByteArray get() = Build.DISPLAY.toByteArray()
override val product: ByteArray get() = Build.PRODUCT.toByteArray()
override val device: ByteArray get() = Build.DEVICE.toByteArray()
......@@ -88,7 +115,8 @@ actual open class SystemDeviceInfo actual constructor(context: Context) : Device
override val androidId: ByteArray get() = Build.ID.toByteArray()
override val apn: ByteArray get() = "wifi".toByteArray()
object Version : DeviceInfo.Version {
@Serializable
actual object Version : DeviceInfo.Version {
override val incremental: ByteArray get() = Build.VERSION.INCREMENTAL.toByteArray()
override val release: ByteArray get() = Build.VERSION.RELEASE.toByteArray()
override val codename: ByteArray get() = Build.VERSION.CODENAME.toByteArray()
......
/*
* Copyright 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.io.core.IoBuffer
import net.mamoe.mirai.Bot
/**
* 在各平台实现的默认的验证码处理器.
*/
actual var defaultLoginSolver: LoginSolver = object : LoginSolver() {
override suspend fun onSolvePicCaptcha(bot: Bot, data: IoBuffer): String? {
error("should be implemented manually by you")
}
override suspend fun onSolveSliderCaptcha(bot: Bot, url: String): String? {
error("should be implemented manually by you")
}
override suspend fun onSolveUnsafeDeviceLoginVerify(bot: Bot, url: String): String? {
error("should be implemented manually by you")
}
}
\ No newline at end of file
......@@ -14,6 +14,8 @@ package net.mamoe.mirai.message.data
* 消息源只用于 [QuoteReply]
*
* `mirai-core-qqandroid`: `net.mamoe.mirai.qqandroid.message.MessageSourceFromMsg`
*
* @see MessageSource.quote 引用这条消息, 创建 [MessageChain]
*/
interface MessageSource : Message {
companion object : Message.Key<MessageSource>
......
......@@ -33,4 +33,13 @@ fun MessageChain.quote(sender: Member): MessageChain {
return QuoteReply(it) + sender.at() + " " // required
}
error("cannot find MessageSource")
}
/**
* 引用这条消息.
* 返回 `[QuoteReply] + [At] + [PlainText]`(必要的结构)
*/
fun MessageSource.quote(sender: Member): MessageChain {
@UseExperimental(MiraiInternalAPI::class)
return QuoteReply(this) + sender.at() + " " // required
}
\ No newline at end of file
......@@ -13,7 +13,6 @@ import kotlinx.io.core.IoBuffer
import net.mamoe.mirai.Bot
import net.mamoe.mirai.network.BotNetworkHandler
import kotlin.coroutines.CoroutineContext
import kotlin.coroutines.EmptyCoroutineContext
import kotlin.jvm.JvmStatic
/**
......@@ -33,58 +32,74 @@ abstract class LoginSolver {
expect var defaultLoginSolver: LoginSolver
/**
* 网络和连接配置
* [Bot] 配置
*/
class BotConfiguration {
@Suppress("PropertyName")
expect open class BotConfiguration() {
/**
* 日志记录器
*/
var botLoggerSupplier: ((Bot) -> MiraiLogger) = { DefaultLogger("Bot(${it.uin})") }
var botLoggerSupplier: ((Bot) -> MiraiLogger)
/**
* 网络层日志构造器
*/
var networkLoggerSupplier: ((BotNetworkHandler) -> MiraiLogger) = { DefaultLogger("Network(${it.bot.uin})") }
var networkLoggerSupplier: ((BotNetworkHandler) -> MiraiLogger)
/**
* 设备信息覆盖
* 设备信息覆盖. 默认使用随机的设备信息.
*/
var deviceInfo: ((Context) -> DeviceInfo)? = null
var deviceInfo: ((Context) -> DeviceInfo)?
/**
* 父 [CoroutineContext]
*/
var parentCoroutineContext: CoroutineContext = EmptyCoroutineContext
var parentCoroutineContext: CoroutineContext
/**
* 心跳周期. 过长会导致被服务器断开连接.
*/
var heartbeatPeriodMillis: Long = 60.secondsToMillis
var heartbeatPeriodMillis: Long
/**
* 每次心跳时等待结果的时间.
* 一旦心跳超时, 整个网络服务将会重启 (将消耗约 5s). 除正在进行的任务 (如图片上传) 会被中断外, 事件和插件均不受影响.
*/
var heartbeatTimeoutMillis: Long = 2.secondsToMillis
var heartbeatTimeoutMillis: Long
/**
* 心跳失败后的第一次重连前的等待时间.
*/
var firstReconnectDelayMillis: Long = 5.secondsToMillis
var firstReconnectDelayMillis: Long
/**
* 重连失败后, 继续尝试的每次等待时间
*/
var reconnectPeriodMillis: Long = 60.secondsToMillis
var reconnectPeriodMillis: Long
/**
* 最多尝试多少次重连
*/
var reconnectionRetryTimes: Int = 3
var reconnectionRetryTimes: Int
/**
* 验证码处理器
*/
var loginSolver: LoginSolver = defaultLoginSolver
var loginSolver: LoginSolver
companion object {
/**
* 默认的配置实例
*/
@JvmStatic
val Default = BotConfiguration()
val Default: BotConfiguration
}
}
\ No newline at end of file
operator fun _NoNetworkLog.unaryPlus()
/**
* 不记录网络层的 log.
* 网络层 log 包含包接收, 包发送, 和一些调试用的记录.
*/
@BotConfigurationDsl
val NoNetworkLog: _NoNetworkLog
@Suppress("ClassName")
object _NoNetworkLog
}
@DslMarker
annotation class BotConfigurationDsl
\ No newline at end of file
......@@ -11,16 +11,15 @@ package net.mamoe.mirai.utils
import kotlinx.serialization.SerialId
import kotlinx.serialization.Serializable
import kotlinx.serialization.Transient
import kotlinx.serialization.protobuf.ProtoBuf
import net.mamoe.mirai.utils.cryptor.contentToString
/**
* 设备信息. 可通过继承 [SystemDeviceInfo] 来在默认的基础上修改
*/
abstract class DeviceInfo internal constructor(
context: Context
) {
val context: Context by context.unsafeWeakRef()
abstract class DeviceInfo {
@Transient
abstract val context: Context
abstract val display: ByteArray
abstract val product: ByteArray
......@@ -95,6 +94,45 @@ abstract class DeviceInfo internal constructor(
}
}
@Serializable
class DeviceInfoData(
override val display: ByteArray,
override val product: ByteArray,
override val device: ByteArray,
override val board: ByteArray,
override val brand: ByteArray,
override val model: ByteArray,
override val bootloader: ByteArray,
override val fingerprint: ByteArray,
override val bootId: ByteArray,
override val procVersion: ByteArray,
override val baseBand: ByteArray,
override val version: VersionData,
override val simInfo: ByteArray,
override val osType: ByteArray,
override val macAddress: ByteArray,
override val wifiBSSID: ByteArray?,
override val wifiSSID: ByteArray?,
override val imsiMd5: ByteArray,
override val imei: String,
override val apn: ByteArray
) : DeviceInfo() {
@Transient
override lateinit var context: Context
@UseExperimental(ExperimentalUnsignedTypes::class)
override val ipAddress: ByteArray
get() = localIpAddress().split(".").map { it.toUByte().toByte() }.takeIf { it.size == 4 }?.toByteArray() ?: byteArrayOf()
override val androidId: ByteArray get() = display
@Serializable
class VersionData(
override val incremental: ByteArray = SystemDeviceInfo.Version.incremental,
override val release: ByteArray = SystemDeviceInfo.Version.release,
override val codename: ByteArray = SystemDeviceInfo.Version.codename,
override val sdk: Int = SystemDeviceInfo.Version.sdk
) : Version
}
/**
* Defaults "%4;7t>;28<fc.5*6".toByteArray()
*/
......
......@@ -9,13 +9,15 @@
package net.mamoe.mirai.utils
import net.mamoe.mirai.utils.Context
import net.mamoe.mirai.utils.DeviceInfo
/**
* 通过本机信息来获取设备信息.
*
* Android: 获取手机信息, 与 QQ 官方相同.
* JVM: 部分为常量, 部分为随机
*/
open expect class SystemDeviceInfo(context: Context) : DeviceInfo
\ No newline at end of file
expect open class SystemDeviceInfo : DeviceInfo {
constructor()
constructor(context: Context)
object Version : DeviceInfo.Version
}
\ No newline at end of file
......@@ -15,11 +15,13 @@ package net.mamoe.mirai.utils.io
import kotlinx.io.core.*
import kotlinx.io.pool.useInstance
import net.mamoe.mirai.utils.*
import net.mamoe.mirai.utils.DefaultLogger
import net.mamoe.mirai.utils.MiraiDebugAPI
import net.mamoe.mirai.utils.MiraiLoggerWithSwitch
import net.mamoe.mirai.utils.withSwitch
import kotlin.contracts.ExperimentalContracts
import kotlin.contracts.InvocationKind
import kotlin.contracts.contract
import kotlin.js.JsName
import kotlin.jvm.JvmMultifileClass
import kotlin.jvm.JvmName
......@@ -30,9 +32,6 @@ val DebugLogger : MiraiLoggerWithSwitch = DefaultLogger("Packet Debug").withSwit
@MiraiDebugAPI("Unstable")
inline fun Throwable.logStacktrace(message: String? = null) = DebugLogger.error(message, this)
@MiraiDebugAPI("Low efficiency.")
inline fun debugPrintln(any: Any?) = DebugLogger.debug(any)
@MiraiDebugAPI("Low efficiency.")
inline fun String.debugPrintThis(name: String): String {
DebugLogger.debug("$name=$this")
......
......@@ -22,12 +22,14 @@ import kotlinx.coroutines.withContext
import kotlinx.io.core.IoBuffer
import kotlinx.io.core.use
import net.mamoe.mirai.Bot
import net.mamoe.mirai.network.BotNetworkHandler
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
import kotlin.coroutines.EmptyCoroutineContext
/**
* 平台默认的验证码识别器.
......@@ -157,3 +159,98 @@ private fun BufferedImage.createCharImg(outputWidth: Int = 100, ignoreRate: Doub
}
}
@Suppress("ClassName", "PropertyName")
actual open class BotConfiguration actual constructor() {
/**
* 日志记录器
*/
actual var botLoggerSupplier: ((Bot) -> MiraiLogger) = { DefaultLogger("Bot(${it.uin})") }
/**
* 网络层日志构造器
*/
actual var networkLoggerSupplier: ((BotNetworkHandler) -> MiraiLogger) = { DefaultLogger("Network(${it.bot.uin})") }
/**
* 设备信息覆盖. 默认使用随机的设备信息.
*/
actual var deviceInfo: ((Context) -> DeviceInfo)? = null
/**
* 父 [CoroutineContext]
*/
actual var parentCoroutineContext: CoroutineContext = EmptyCoroutineContext
/**
* 心跳周期. 过长会导致被服务器断开连接.
*/
actual var heartbeatPeriodMillis: Long = 60.secondsToMillis
/**
* 每次心跳时等待结果的时间.
* 一旦心跳超时, 整个网络服务将会重启 (将消耗约 5s). 除正在进行的任务 (如图片上传) 会被中断外, 事件和插件均不受影响.
*/
actual var heartbeatTimeoutMillis: Long = 2.secondsToMillis
/**
* 心跳失败后的第一次重连前的等待时间.
*/
actual var firstReconnectDelayMillis: Long = 5.secondsToMillis
/**
* 重连失败后, 继续尝试的每次等待时间
*/
actual var reconnectPeriodMillis: Long = 60.secondsToMillis
/**
* 最多尝试多少次重连
*/
actual var reconnectionRetryTimes: Int = 3
/**
* 验证码处理器
*/
actual var loginSolver: LoginSolver = defaultLoginSolver
actual companion object {
/**
* 默认的配置实例
*/
@JvmStatic
actual val Default = BotConfiguration()
}
@Suppress("NOTHING_TO_INLINE")
@BotConfigurationDsl
inline operator fun FileBasedDeviceInfo.unaryPlus() {
deviceInfo = { File(filepath).loadAsDeviceInfo(it) }
}
@Suppress("NOTHING_TO_INLINE")
@BotConfigurationDsl
inline operator fun FileBasedDeviceInfo.ByDeviceDotJson.unaryPlus() {
deviceInfo = { File("device.json").loadAsDeviceInfo(it) }
}
actual operator fun _NoNetworkLog.unaryPlus() {
networkLoggerSupplier = supplier
}
/**
* 不记录网络层的 log.
* 网络层 log 包含包接收, 包发送, 和一些调试用的记录.
*/
@BotConfigurationDsl
actual val NoNetworkLog: _NoNetworkLog
get() = _NoNetworkLog
@BotConfigurationDsl
actual object _NoNetworkLog {
internal val supplier = { _: BotNetworkHandler -> SilentLogger }
}
}
/**
* 使用文件系统存储设备信息.
*/
@BotConfigurationDsl
inline class FileBasedDeviceInfo @BotConfigurationDsl constructor(val filepath: String) {
/**
* 使用 "device.json" 存储设备信息
*/
@BotConfigurationDsl
companion object ByDeviceDotJson
}
\ No newline at end of file
......@@ -18,37 +18,37 @@ import java.util.*
actual open class PlatformLogger @JvmOverloads internal actual constructor(
override val identity: String?
) : MiraiLoggerPlatformBase() {
override fun verbose0(any: Any?) = println(any, LoggerTextFormat.RESET)
override fun verbose0(message: String?) = println(message, LoggerTextFormat.RESET)
override fun verbose0(message: String?, e: Throwable?) {
if (message != null) verbose(message.toString())
e?.printStackTrace()
}
override fun info0(any: Any?) = println(any, LoggerTextFormat.LIGHT_GREEN)
override fun info0(message: String?) = println(message, LoggerTextFormat.LIGHT_GREEN)
override fun info0(message: String?, e: Throwable?) {
if (message != null) info(message.toString())
e?.printStackTrace()
}
override fun warning0(any: Any?) = println(any, LoggerTextFormat.LIGHT_RED)
override fun warning0(message: String?) = println(message, LoggerTextFormat.LIGHT_RED)
override fun warning0(message: String?, e: Throwable?) {
if (message != null) warning(message.toString())
e?.printStackTrace()
}
override fun error0(any: Any?) = println(any, LoggerTextFormat.RED)
override fun error0(message: String?) = println(message, LoggerTextFormat.RED)
override fun error0(message: String?, e: Throwable?) {
if (message != null) error(message.toString())
e?.printStackTrace()
}
override fun debug0(any: Any?) = println(any, LoggerTextFormat.LIGHT_CYAN)
override fun debug0(message: String?) = println(message, LoggerTextFormat.LIGHT_CYAN)
override fun debug0(message: String?, e: Throwable?) {
if (message != null) debug(message.toString())
e?.printStackTrace()
}
private fun println(value: Any?, color: LoggerTextFormat) {
private fun println(value: String?, color: LoggerTextFormat) {
val time = SimpleDateFormat("HH:mm:ss", Locale.SIMPLIFIED_CHINESE).format(Date())
if (identity == null) {
......@@ -62,6 +62,7 @@ actual open class PlatformLogger @JvmOverloads internal actual constructor(
/**
* @author NaturalHG
*/
@Suppress("unused")
internal enum class LoggerTextFormat(private val format: String) {
RESET("\u001b[0m"),
......
......@@ -10,38 +10,68 @@
package net.mamoe.mirai.utils
import kotlinx.io.core.toByteArray
import kotlinx.serialization.Serializable
import kotlinx.serialization.Transient
import kotlinx.serialization.UnstableDefault
import kotlinx.serialization.json.Json
import net.mamoe.mirai.utils.io.getRandomByteArray
import net.mamoe.mirai.utils.io.getRandomString
import java.io.File
/**
* 加载一个设备信息. 若文件不存在或为空则随机并创建一个设备信息保存.
*/
@UseExperimental(UnstableDefault::class)
fun File.loadAsDeviceInfo(context: Context = ContextImpl()): DeviceInfo {
if (!this.exists() || this.length() == 0L) {
return SystemDeviceInfo(context).also {
this.writeText(Json.plain.stringify(SystemDeviceInfo.serializer(), it))
}
}
return Json.nonstrict.parse(DeviceInfoData.serializer(), this.readText()).also {
it.context = context
}
}
@Serializable
@UseExperimental(ExperimentalUnsignedTypes::class)
actual open class SystemDeviceInfo actual constructor(context: Context) : DeviceInfo(context) {
override val display: ByteArray get() = "MIRAI.200122.001".toByteArray()
override val product: ByteArray get() = "mirai".toByteArray()
override val device: ByteArray get() = "mirai".toByteArray()
override val board: ByteArray get() = "mirai".toByteArray()
override val brand: ByteArray get() = "mamoe".toByteArray()
override val model: ByteArray get() = "mirai".toByteArray()
override val bootloader: ByteArray get() = "unknown".toByteArray()
override val fingerprint: ByteArray get() = "mamoe/mirai/mirai:10/MIRAI.200122.001/5891938:user/release-keys".toByteArray()
actual open class SystemDeviceInfo actual constructor() : DeviceInfo() {
actual constructor(context: Context) : this() {
this.context = context
}
@Transient
final override lateinit var context: Context
override val display: ByteArray = "MIRAI.200122.001".toByteArray()
override val product: ByteArray = "mirai".toByteArray()
override val device: ByteArray = "mirai".toByteArray()
override val board: ByteArray = "mirai".toByteArray()
override val brand: ByteArray = "mamoe".toByteArray()
override val model: ByteArray = "mirai".toByteArray()
override val bootloader: ByteArray = "unknown".toByteArray()
override val fingerprint: ByteArray = "mamoe/mirai/mirai:10/MIRAI.200122.001/${getRandomString(7, '0'..'9')}:user/release-keys".toByteArray()
override val bootId: ByteArray = ExternalImage.generateUUID(md5(getRandomByteArray(16))).toByteArray()
override val procVersion: ByteArray get() = "Linux version 3.0.31-g6fb96c9 (android-build@xxx.xxx.xxx.xxx.com)".toByteArray()
override val baseBand: ByteArray get() = byteArrayOf()
override val version: DeviceInfo.Version get() = Version
override val simInfo: ByteArray get() = "T-Mobile".toByteArray()
override val osType: ByteArray get() = "android".toByteArray()
override val macAddress: ByteArray get() = "02:00:00:00:00:00".toByteArray()
override val wifiBSSID: ByteArray? get() = "02:00:00:00:00:00".toByteArray()
override val wifiSSID: ByteArray? get() = "<unknown ssid>".toByteArray()
override val imsiMd5: ByteArray get() = md5(getRandomByteArray(16))
override val imei: String get() = getRandomString(15, '0'..'9')
override val procVersion: ByteArray = "Linux version 3.0.31-${getRandomString(8)} (android-build@xxx.xxx.xxx.xxx.com)".toByteArray()
override val baseBand: ByteArray = byteArrayOf()
override val version: Version = Version
override val simInfo: ByteArray = "T-Mobile".toByteArray()
override val osType: ByteArray = "android".toByteArray()
override val macAddress: ByteArray = "02:00:00:00:00:00".toByteArray()
override val wifiBSSID: ByteArray? = "02:00:00:00:00:00".toByteArray()
override val wifiSSID: ByteArray? = "<unknown ssid>".toByteArray()
override val imsiMd5: ByteArray = md5(getRandomByteArray(16))
override val imei: String = getRandomString(15, '0'..'9')
override val ipAddress: ByteArray get() = localIpAddress().split(".").map { it.toUByte().toByte() }.takeIf { it.size == 4 }?.toByteArray() ?: byteArrayOf()
override val androidId: ByteArray get() = display
override val apn: ByteArray get() = "wifi".toByteArray()
override val apn: ByteArray = "wifi".toByteArray()
object Version : DeviceInfo.Version {
override val incremental: ByteArray get() = "5891938".toByteArray()
override val release: ByteArray get() = "10".toByteArray()
override val codename: ByteArray get() = "REL".toByteArray()
override val sdk: Int get() = 29
@Serializable
actual object Version : DeviceInfo.Version {
override val incremental: ByteArray = "5891938".toByteArray()
override val release: ByteArray = "10".toByteArray()
override val codename: ByteArray = "REL".toByteArray()
override val sdk: Int = 29
}
}
\ No newline at end of file
......@@ -27,6 +27,7 @@ import net.mamoe.mirai.message.data.firstOrNull
import net.mamoe.mirai.message.sendAsImageTo
import net.mamoe.mirai.qqandroid.Bot
import net.mamoe.mirai.qqandroid.QQAndroid
import net.mamoe.mirai.utils.FileBasedDeviceInfo
import java.io.File
private fun readTestAccount(): BotAccount? {
......@@ -51,7 +52,7 @@ suspend fun main() {
"123456"
) {
// 覆盖默认的配置
+FileBasedDeviceInfo // 使用 "device.json" 保存设备信息
// networkLoggerSupplier = { SilentLogger } // 禁用网络层输出
}.alsoLogin()
......
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