From f175d07c0ab6f7da74723155f219acc53cd2c588 Mon Sep 17 00:00:00 2001 From: Him188 Date: Tue, 16 Jun 2020 15:09:32 +0800 Subject: [PATCH] Introduce multi-platform BotConfiguration structure; Add `redirectNetworkLogToDirectory` functions, fix #371 --- ...guration.kt => BotConfiguration.common.kt} | 91 ++++------ .../net/mamoe/mirai/utils/BotConfiguration.kt | 165 ++++++++++++++++++ .../net/mamoe/mirai/utils/LoginSolver.jvm.kt | 2 +- 3 files changed, 196 insertions(+), 62 deletions(-) rename mirai-core/src/commonMain/kotlin/net.mamoe.mirai/utils/{BotConfiguration.kt => BotConfiguration.common.kt} (79%) create mode 100644 mirai-core/src/jvmMain/kotlin/net/mamoe/mirai/utils/BotConfiguration.kt diff --git a/mirai-core/src/commonMain/kotlin/net.mamoe.mirai/utils/BotConfiguration.kt b/mirai-core/src/commonMain/kotlin/net.mamoe.mirai/utils/BotConfiguration.common.kt similarity index 79% rename from mirai-core/src/commonMain/kotlin/net.mamoe.mirai/utils/BotConfiguration.kt rename to mirai-core/src/commonMain/kotlin/net.mamoe.mirai/utils/BotConfiguration.common.kt index 0c8410707..1569dc3d4 100644 --- a/mirai-core/src/commonMain/kotlin/net.mamoe.mirai/utils/BotConfiguration.kt +++ b/mirai-core/src/commonMain/kotlin/net.mamoe.mirai/utils/BotConfiguration.common.kt @@ -6,7 +6,7 @@ * * https://github.com/mamoe/mirai/blob/master/LICENSE */ -@file:Suppress("unused", "DEPRECATION_ERROR") +@file:Suppress("unused", "DEPRECATION_ERROR", "EXPOSED_SUPER_CLASS") package net.mamoe.mirai.utils @@ -17,7 +17,6 @@ import kotlin.coroutines.CoroutineContext import kotlin.coroutines.EmptyCoroutineContext import kotlin.coroutines.coroutineContext import kotlin.jvm.JvmField -import kotlin.jvm.JvmOverloads import kotlin.jvm.JvmStatic import kotlin.jvm.JvmSynthetic @@ -35,8 +34,34 @@ import kotlin.jvm.JvmSynthetic * } * ``` */ +expect open class BotConfiguration() : BotConfigurationBase { + /** + * 设备信息覆盖. 在没有手动指定时将会通过日志警告, 并使用随机设备信息. + * @see randomDeviceInfo 使用随机设备信息 + */ + var deviceInfo: ((Context) -> DeviceInfo)? + + /** + * 使用随机设备信息. + * + * @see deviceInfo + */ + @ConfigurationDsl + fun randomDeviceInfo() + + companion object { + /** 默认的配置实例. 可以进行修改 */ + @JvmStatic + val Default: BotConfiguration + } + + fun copy(): BotConfiguration +} + +@MiraiInternalAPI @Suppress("PropertyName") -open class BotConfiguration { +@SinceMirai("1.1.0") +internal open class BotConfigurationBase internal constructor() { /** * 日志记录器 * @@ -61,13 +86,6 @@ open class BotConfiguration { */ var networkLoggerSupplier: ((Bot) -> MiraiLogger) = { DefaultLogger("Net ${it.id}") } - /** - * 设备信息覆盖. 在没有手动指定时将会通过日志警告, 并使用随机设备信息. - * @see fileBasedDeviceInfo 使用指定文件存储设备信息 - * @see randomDeviceInfo 使用随机设备信息 - */ - var deviceInfo: ((Context) -> DeviceInfo)? = deviceInfoStub - /** 父 [CoroutineContext]. [Bot] 创建后会使用 [SupervisorJob] 覆盖其 [Job], 但会将这个 [Job] 作为父 [Job] */ var parentCoroutineContext: CoroutineContext = EmptyCoroutineContext @@ -121,12 +139,6 @@ open class BotConfiguration { ANDROID_PAD(537062409) } - companion object { - /** 默认的配置实例. 可以进行修改 */ - @JvmStatic - val Default = BotConfiguration() - } - /** * 不显示网络日志. 不推荐. * @see networkLoggerSupplier 更多日志处理方式 @@ -145,29 +157,6 @@ open class BotConfiguration { botLoggerSupplier = { _ -> SilentLogger } } - /** - * 使用文件存储设备信息. - * - * 此函数只在 JVM 和 Android 有效. 在其他平台将会抛出异常. - * @param filepath 文件路径. 可相对于程序运行路径 (`user.dir`), 也可以是绝对路径. - * @see deviceInfo - */ - @JvmOverloads - @ConfigurationDsl - fun fileBasedDeviceInfo(filepath: String = "device.json") { - deviceInfo = getFileBasedDeviceInfoSupplier(filepath) - } - - /** - * 使用随机设备信息. - * - * @see deviceInfo - */ - @ConfigurationDsl - fun randomDeviceInfo() { - deviceInfo = null - } - /** * 使用当前协程的 [coroutineContext] 作为 [parentCoroutineContext]. * @@ -230,32 +219,12 @@ open class BotConfiguration { @Target(AnnotationTarget.FUNCTION) @DslMarker annotation class ConfigurationDsl - - fun copy(): BotConfiguration { - return BotConfiguration().also { new -> - new.botLoggerSupplier = botLoggerSupplier - new.networkLoggerSupplier = networkLoggerSupplier - new.deviceInfo = deviceInfo - new.parentCoroutineContext = parentCoroutineContext - new.heartbeatPeriodMillis = heartbeatPeriodMillis - new.heartbeatTimeoutMillis = heartbeatTimeoutMillis - new.firstReconnectDelayMillis = firstReconnectDelayMillis - new.reconnectPeriodMillis = reconnectPeriodMillis - new.reconnectionRetryTimes = reconnectionRetryTimes - new.loginSolver = loginSolver - new.protocol = protocol - new.fileCacheStrategy = fileCacheStrategy - } - } } -private val deviceInfoStub: (Context) -> DeviceInfo = { +internal val deviceInfoStub: (Context) -> DeviceInfo = { @Suppress("DEPRECATION") MiraiLogger.warning("未指定设备信息, 已使用随机设备信息. 请查看 BotConfiguration.deviceInfo 以获取更多信息.") @Suppress("DEPRECATION") MiraiLogger.warning("Device info isn't specified. Please refer to BotConfiguration.deviceInfo for more information") SystemDeviceInfo() -} - -@OptIn(ExperimentalMultiplatform::class) -internal expect fun getFileBasedDeviceInfoSupplier(filename: String): ((Context) -> DeviceInfo)? \ No newline at end of file +} \ No newline at end of file diff --git a/mirai-core/src/jvmMain/kotlin/net/mamoe/mirai/utils/BotConfiguration.kt b/mirai-core/src/jvmMain/kotlin/net/mamoe/mirai/utils/BotConfiguration.kt new file mode 100644 index 000000000..a692209a3 --- /dev/null +++ b/mirai-core/src/jvmMain/kotlin/net/mamoe/mirai/utils/BotConfiguration.kt @@ -0,0 +1,165 @@ +/* + * 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 + */ +@file:Suppress("unused", "DEPRECATION_ERROR", "EXPOSED_SUPER_CLASS") + +package net.mamoe.mirai.utils + +import net.mamoe.mirai.Bot +import java.io.File + +/** + * [Bot] 配置. + * + * Kotlin 使用方法: + * ``` + * val bot = Bot(...) { + * // 在这里配置 Bot + * + * bogLoggerSupplier = { bot -> ... } + * fileBasedDeviceInfo() + * inheritCoroutineContext() // 使用 `coroutineScope` 的 Job 作为父 Job + * } + * ``` + * + * Java 使用方法: + * ```java + * Bot bot = BotFactory.newBot(..., new BotConfiguration() {{ + * setBogLoggerSupplier((Bot bot) -> { ... }) + * fileBasedDeviceInfo() + * ... + * }}) + * ``` + */ +@Suppress("PropertyName") +actual open class BotConfiguration : BotConfigurationBase() { // open for Java + /** + * 设备信息覆盖. 在没有手动指定时将会通过日志警告, 并使用随机设备信息. + * @see fileBasedDeviceInfo 使用指定文件存储设备信息 + * @see randomDeviceInfo 使用随机设备信息 + */ + actual var deviceInfo: ((Context) -> DeviceInfo)? = deviceInfoStub + + /** + * 使用随机设备信息. + * + * @see deviceInfo + */ + @ConfigurationDsl + actual fun randomDeviceInfo() { + deviceInfo = null + } + + /** + * 重定向 [网络日志][networkLoggerSupplier] 到指定目录. 若目录不存在将会自动创建 ([File.mkdirs]) + * @see DirectoryLogger + * @see redirectNetworkLogToDirectory + */ + @JvmOverloads + @ConfigurationDsl + @SinceMirai("1.1.0") + fun redirectNetworkLogToDirectory( + dir: File = File("logs"), + retain: Long = 1.weeksToMillis, + identity: (bot: Bot) -> String = { "Net ${it.id}" } + ) { + require(!dir.isFile) { "dir must not be a file" } + dir.mkdirs() + networkLoggerSupplier = { DirectoryLogger(identity(it), dir, retain) } + } + + /** + * 重定向 [网络日志][networkLoggerSupplier] 到指定文件. + * 日志将会逐行追加到此文件. 若文件不存在将会自动创建 ([File.createNewFile]) + * @see SingleFileLogger + * @see redirectNetworkLogToDirectory + */ + @JvmOverloads + @SinceMirai("1.1.0") + @ConfigurationDsl + fun redirectNetworkLogToFile( + file: File = File("mirai.log"), + identity: (bot: Bot) -> String = { "Net ${it.id}" } + ) { + require(!file.isFile) { "file must not be a dir" } + file.createNewFile() + networkLoggerSupplier = { SingleFileLogger(identity(it), file) } + } + + /** + * 重定向 [Bot 日志][botLoggerSupplier] 到指定目录. 若目录不存在将会自动创建 ([File.mkdirs]) + * @see DirectoryLogger + * @see redirectBotLogToFile + */ + @JvmOverloads + @ConfigurationDsl + @SinceMirai("1.1.0") + fun redirectBotLogToDirectory( + dir: File, + retain: Long = 1.weeksToMillis, + identity: (bot: Bot) -> String = { "Net ${it.id}" } + ) { + require(!dir.isFile) { "dir must not be a file" } + dir.mkdirs() + botLoggerSupplier = { DirectoryLogger(identity(it), dir, retain) } + } + + /** + * 重定向 [Bot 日志][botLoggerSupplier] 到指定文件. + * 日志将会逐行追加到此文件. 若文件不存在将会自动创建 ([File.createNewFile]) + * @see SingleFileLogger + * @see redirectBotLogToDirectory + */ + @JvmOverloads + @ConfigurationDsl + @SinceMirai("1.1.0") + fun redirectBotLogToFile( + file: File, + identity: (bot: Bot) -> String = { "Net ${it.id}" } + ) { + require(!file.isFile) { "file must not be a dir" } + file.createNewFile() + botLoggerSupplier = { SingleFileLogger(identity(it), file) } + } + + actual companion object { + /** 默认的配置实例. 可以进行修改 */ + @JvmStatic + actual val Default = BotConfiguration() + } + + /** + * 使用文件存储设备信息. + * + * 此函数只在 JVM 和 Android 有效. 在其他平台将会抛出异常. + * @param filepath 文件路径. 可相对于程序运行路径 (`user.dir`), 也可以是绝对路径. + * @see deviceInfo + */ + @JvmOverloads + @ConfigurationDsl + fun fileBasedDeviceInfo(filepath: String = "device.json") { + deviceInfo = getFileBasedDeviceInfoSupplier(filepath) + } + + actual fun copy(): BotConfiguration { + return BotConfiguration().also { new -> + new.botLoggerSupplier = botLoggerSupplier + new.networkLoggerSupplier = networkLoggerSupplier + new.deviceInfo = deviceInfo + new.parentCoroutineContext = parentCoroutineContext + new.heartbeatPeriodMillis = heartbeatPeriodMillis + new.heartbeatTimeoutMillis = heartbeatTimeoutMillis + new.firstReconnectDelayMillis = firstReconnectDelayMillis + new.reconnectPeriodMillis = reconnectPeriodMillis + new.reconnectionRetryTimes = reconnectionRetryTimes + new.loginSolver = loginSolver + new.protocol = protocol + new.fileCacheStrategy = fileCacheStrategy + } + } +} diff --git a/mirai-core/src/jvmMain/kotlin/net/mamoe/mirai/utils/LoginSolver.jvm.kt b/mirai-core/src/jvmMain/kotlin/net/mamoe/mirai/utils/LoginSolver.jvm.kt index 0acfdf267..2874eba5b 100644 --- a/mirai-core/src/jvmMain/kotlin/net/mamoe/mirai/utils/LoginSolver.jvm.kt +++ b/mirai-core/src/jvmMain/kotlin/net/mamoe/mirai/utils/LoginSolver.jvm.kt @@ -154,7 +154,7 @@ actual abstract class LoginSolver { //////////////// internal /////////////////////////////// -internal actual fun getFileBasedDeviceInfoSupplier(filename: String): ((Context) -> DeviceInfo)? { +internal fun getFileBasedDeviceInfoSupplier(filename: String): ((Context) -> DeviceInfo)? { return { File(filename).loadAsDeviceInfo(it) }