From 4555962049fae72883266166f357fa39e3628746 Mon Sep 17 00:00:00 2001 From: Noire Date: Sun, 14 Feb 2021 07:41:57 -0600 Subject: [PATCH 01/12] Fix document of BotConfiguration.botLoggerSupplier (#1021) --- .../src/commonMain/kotlin/utils/BotConfiguration.kt | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/mirai-core-api/src/commonMain/kotlin/utils/BotConfiguration.kt b/mirai-core-api/src/commonMain/kotlin/utils/BotConfiguration.kt index bc5f4e673..90ceea238 100644 --- a/mirai-core-api/src/commonMain/kotlin/utils/BotConfiguration.kt +++ b/mirai-core-api/src/commonMain/kotlin/utils/BotConfiguration.kt @@ -277,8 +277,8 @@ public open class BotConfiguration { // open for Java * * - 默认打印到标准输出, 通过 [MiraiLogger.create] * - 忽略所有日志: [noBotLog] - * - 重定向到一个目录: `networkLoggerSupplier = { DirectoryLogger("Net ${it.id}") }` - * - 重定向到一个文件: `networkLoggerSupplier = { SingleFileLogger("Net ${it.id}") }` + * - 重定向到一个目录: `botLoggerSupplier = { DirectoryLogger("Bot ${it.id}") }` + * - 重定向到一个文件: `botLoggerSupplier = { SingleFileLogger("Bot ${it.id}") }` * * @see MiraiLogger */ From 6e2ae6b3af76f6dd940852e25b18c81c27329ebe Mon Sep 17 00:00:00 2001 From: Karlatemp Date: Mon, 15 Feb 2021 10:42:37 +0800 Subject: [PATCH 02/12] Update caller-finder version --- build.gradle.kts | 1 - buildSrc/src/main/kotlin/Versions.kt | 2 +- 2 files changed, 1 insertion(+), 2 deletions(-) diff --git a/build.gradle.kts b/build.gradle.kts index 0cae31c90..e7a1cc7d5 100644 --- a/build.gradle.kts +++ b/build.gradle.kts @@ -105,7 +105,6 @@ allprojects { maven(url = "https://kotlin.bintray.com/kotlinx") google() mavenCentral() - maven(url = "https://dl.bintray.com/karlatemp/misc") } afterEvaluate { diff --git a/buildSrc/src/main/kotlin/Versions.kt b/buildSrc/src/main/kotlin/Versions.kt index 29c8ff0ae..24d1727c8 100644 --- a/buildSrc/src/main/kotlin/Versions.kt +++ b/buildSrc/src/main/kotlin/Versions.kt @@ -103,4 +103,4 @@ const val yamlkt = "net.mamoe.yamlkt:yamlkt:${Versions.yamlkt}" const val `jetbrains-annotations` = "org.jetbrains:annotations:19.0.0" -const val `caller-finder` = "io.github.karlatemp:caller:1.0.1" +const val `caller-finder` = "io.github.karlatemp:caller:1.1.1" From e7e8d8ca6a4936682a41b96e201d2b838a527c9e Mon Sep 17 00:00:00 2001 From: Karlatemp Date: Mon, 15 Feb 2021 11:38:42 +0800 Subject: [PATCH 03/12] Fix MessageChain.cleanupRubbishMessageElements() --- .../kotlin/message/ReceiveMessageHandler.kt | 36 ++++++++++++++++--- 1 file changed, 31 insertions(+), 5 deletions(-) diff --git a/mirai-core/src/commonMain/kotlin/message/ReceiveMessageHandler.kt b/mirai-core/src/commonMain/kotlin/message/ReceiveMessageHandler.kt index 5b60f2157..e82f7d333 100644 --- a/mirai-core/src/commonMain/kotlin/message/ReceiveMessageHandler.kt +++ b/mirai-core/src/commonMain/kotlin/message/ReceiveMessageHandler.kt @@ -156,6 +156,33 @@ private object ReceiveMessageTransformer { } } + fun MessageChainBuilder.compressContinuousPlainText() { + var index = 0 + val builder = StringBuilder() + while (index + 1 < size) { + val elm0 = get(index) + val elm1 = get(index + 1) + if (elm0 is PlainText && elm1 is PlainText) { + builder.setLength(0) + var end = -1 + for (i in index until size) { + val elm = get(i) + if (elm is PlainText) { + end = i + builder.append(elm.content) + } else break + } + set(index, PlainText(builder.toString())) + // do delete + val index1 = index + 1 + repeat(end - index) { + removeAt(index1) + } + } + index++ + } + } + fun MessageChain.cleanupRubbishMessageElements(): MessageChain { var previousLast: SingleMessage? = null var last: SingleMessage? = null @@ -215,15 +242,14 @@ private object ReceiveMessageTransformer { } } - if (element is PlainText) { // 处理分片消息 - append(element.content) - } else { - add(element) - } + append(element) previousLast = last last = element } + + // 处理分片信息 + compressContinuousPlainText() } } From addee38c708c7834c6d033de399c61480b8d2c53 Mon Sep 17 00:00:00 2001 From: Karlatemp Date: Mon, 15 Feb 2021 13:43:03 +0800 Subject: [PATCH 04/12] Don't add new friend when reject new friend request --- mirai-core/src/commonMain/kotlin/MiraiImpl.kt | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/mirai-core/src/commonMain/kotlin/MiraiImpl.kt b/mirai-core/src/commonMain/kotlin/MiraiImpl.kt index 6babe0880..7b25879a0 100644 --- a/mirai-core/src/commonMain/kotlin/MiraiImpl.kt +++ b/mirai-core/src/commonMain/kotlin/MiraiImpl.kt @@ -20,7 +20,6 @@ import kotlinx.coroutines.withContext import kotlinx.io.core.discardExact import kotlinx.io.core.readBytes import kotlinx.serialization.json.* -import net.mamoe.kjbb.JvmBlockingBridge import net.mamoe.mirai.* import net.mamoe.mirai.contact.* import net.mamoe.mirai.data.* @@ -794,6 +793,9 @@ internal open class MiraiImpl : IMirai, LowLevelApiAccessor { accept = accept, blackList = blackList ).sendWithoutExpect() + + if (!accept) return@apply + @Suppress("INVISIBLE_MEMBER", "INVISIBLE_REFERENCE") bot.friends.delegate.add(newFriend(bot, FriendInfoImpl(fromId, fromNick, ""))) } From 2bfc20048e1b3835d5c09cb172336d5505b4e62e Mon Sep 17 00:00:00 2001 From: Karlatemp Date: Tue, 16 Feb 2021 20:56:26 +0800 Subject: [PATCH 05/12] [MessageSubscribersBuilder] Ignore `null` execution result. close #1011 --- .../src/commonMain/kotlin/event/MessageSubscribersBuilder.kt | 2 ++ 1 file changed, 2 insertions(+) diff --git a/mirai-core-api/src/commonMain/kotlin/event/MessageSubscribersBuilder.kt b/mirai-core-api/src/commonMain/kotlin/event/MessageSubscribersBuilder.kt index 6a20d70bb..b4e941716 100644 --- a/mirai-core-api/src/commonMain/kotlin/event/MessageSubscribersBuilder.kt +++ b/mirai-core-api/src/commonMain/kotlin/event/MessageSubscribersBuilder.kt @@ -475,6 +475,7 @@ public open class MessageSubscribersBuilder Any?): RR { when (val message = replier(m, m.message.contentToString())) { is Message -> m.subject.sendMessage(message) + null, is Unit -> Unit else -> m.subject.sendMessage(message.toString()) } @@ -485,6 +486,7 @@ public open class MessageSubscribersBuilder Any?): RR { when (val message = replier(m, m.message.contentToString())) { is Message -> m.subject.sendMessage(m.message.quote() + message) + null, is Unit -> Unit else -> m.subject.sendMessage(m.message.quote() + message.toString()) } From 3ba2510f10b756ae18abfc49e2beda2b9685dd8e Mon Sep 17 00:00:00 2001 From: Karlatemp Date: Tue, 16 Feb 2021 21:07:43 +0800 Subject: [PATCH 06/12] Update permission flags; close #1013 --- .../packet/chat/receive/OnlinePush.PbPushGroupMsg.kt | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/mirai-core/src/commonMain/kotlin/network/protocol/packet/chat/receive/OnlinePush.PbPushGroupMsg.kt b/mirai-core/src/commonMain/kotlin/network/protocol/packet/chat/receive/OnlinePush.PbPushGroupMsg.kt index 4b351520b..444bedc17 100644 --- a/mirai-core/src/commonMain/kotlin/network/protocol/packet/chat/receive/OnlinePush.PbPushGroupMsg.kt +++ b/mirai-core/src/commonMain/kotlin/network/protocol/packet/chat/receive/OnlinePush.PbPushGroupMsg.kt @@ -156,7 +156,11 @@ internal object OnlinePushPbPushGroupMsg : IncomingPacketFactory("Onlin ) = when { flags and 16 != 0 -> MemberPermission.ADMINISTRATOR flags and 8 != 0 -> MemberPermission.OWNER - flags == 0 || flags == 1 -> MemberPermission.MEMBER + (when (flags) { + 1, 0, 64, + -> true + else -> false + }) -> MemberPermission.MEMBER else -> { bot.logger.warning { "判断群 ${sender.group.id} 的群员 ${sender.id} 的权限失败: ${flags._miraiContentToString()}. 请完整截图或复制此日志并确认其真实权限后发送给 mirai 维护者以帮助解决问题." } sender.permission From 2eaa13a7c21815f96eae61f8559691105ea0cb48 Mon Sep 17 00:00:00 2001 From: Karlatemp Date: Thu, 18 Feb 2021 01:15:48 +0800 Subject: [PATCH 07/12] Release 2.5.0-dev-2 --- buildSrc/src/main/kotlin/Versions.kt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/buildSrc/src/main/kotlin/Versions.kt b/buildSrc/src/main/kotlin/Versions.kt index 24d1727c8..a9882c7de 100644 --- a/buildSrc/src/main/kotlin/Versions.kt +++ b/buildSrc/src/main/kotlin/Versions.kt @@ -12,7 +12,7 @@ import org.gradle.api.attributes.Attribute object Versions { - const val project = "2.5.0-dev-1" + const val project = "2.5.0-dev-2" const val core = project const val console = project From ce06ce85f0b78e774e9183025a1f0eaa1b6b7a0b Mon Sep 17 00:00:00 2001 From: Karlatemp Date: Fri, 19 Feb 2021 23:09:26 +0800 Subject: [PATCH 08/12] Improve RspSystemMsgNew handling; #1033, #996, #959, #610 --- .../kotlin/network/QQAndroidClient.kt | 2 + .../protocol/packet/chat/NewContact.kt | 76 +++++++++++++------ 2 files changed, 54 insertions(+), 24 deletions(-) diff --git a/mirai-core/src/commonMain/kotlin/network/QQAndroidClient.kt b/mirai-core/src/commonMain/kotlin/network/QQAndroidClient.kt index ac5db3141..d76162f18 100644 --- a/mirai-core/src/commonMain/kotlin/network/QQAndroidClient.kt +++ b/mirai-core/src/commonMain/kotlin/network/QQAndroidClient.kt @@ -165,6 +165,8 @@ internal open class QQAndroidClient( class MessageSvcSyncData { val firstNotify: AtomicBoolean = atomic(true) + var latestMsgNewGroupTime: Long = currentTimeSeconds() + var latestMsgNewFriendTime: Long = currentTimeSeconds() @Volatile var syncCookie: ByteArray? = null diff --git a/mirai-core/src/commonMain/kotlin/network/protocol/packet/chat/NewContact.kt b/mirai-core/src/commonMain/kotlin/network/protocol/packet/chat/NewContact.kt index 127090cd3..707e03174 100644 --- a/mirai-core/src/commonMain/kotlin/network/protocol/packet/chat/NewContact.kt +++ b/mirai-core/src/commonMain/kotlin/network/protocol/packet/chat/NewContact.kt @@ -19,6 +19,7 @@ import net.mamoe.mirai.event.events.MemberJoinRequestEvent import net.mamoe.mirai.event.events.NewFriendRequestEvent import net.mamoe.mirai.internal.QQAndroidBot import net.mamoe.mirai.internal.message.contextualBugReportException +import net.mamoe.mirai.internal.network.MultiPacketByIterable import net.mamoe.mirai.internal.network.Packet import net.mamoe.mirai.internal.network.QQAndroidClient import net.mamoe.mirai.internal.network.protocol.data.proto.Structmsg @@ -27,11 +28,12 @@ import net.mamoe.mirai.internal.network.protocol.packet.buildOutgoingUniPacket import net.mamoe.mirai.internal.utils._miraiContentToString import net.mamoe.mirai.internal.utils.io.serialization.loadAs import net.mamoe.mirai.internal.utils.io.serialization.writeProtoBuf +import net.mamoe.mirai.utils.currentTimeSeconds internal class NewContact { internal object SystemMsgNewFriend : - OutgoingPacketFactory("ProfileService.Pb.ReqSystemMsgNew.Friend") { + OutgoingPacketFactory("ProfileService.Pb.ReqSystemMsgNew.Friend") { operator fun invoke(client: QQAndroidClient) = buildOutgoingUniPacket(client) { writeProtoBuf( @@ -55,18 +57,29 @@ internal class NewContact { } - override suspend fun ByteReadPacket.decode(bot: QQAndroidBot): NewFriendRequestEvent? { + override suspend fun ByteReadPacket.decode(bot: QQAndroidBot): Packet? { readBytes().loadAs(Structmsg.RspSystemMsgNew.serializer()).run { - val struct = friendmsgs.firstOrNull()// 会有重复且无法过滤, 不要用 map - return struct?.msg?.run { - NewFriendRequestEvent( - bot, - struct.msgSeq, - msgAdditional, - struct.reqUin, - groupCode, - reqUinNick - ) + return friendmsgs.filter { + it.msgTime > bot.client.syncingController.latestMsgNewFriendTime + }.mapNotNull { struct -> + struct.msg?.run { + NewFriendRequestEvent( + bot, + struct.msgSeq, + msgAdditional, + struct.reqUin, + groupCode, + reqUinNick + ) + } + }.let { packets -> + when { + packets.isEmpty() -> null + packets.size == 1 -> packets[0] + else -> MultiPacketByIterable(packets) + } + }.also { + bot.client.syncingController.latestMsgNewFriendTime = currentTimeSeconds() } } } @@ -143,18 +156,8 @@ internal class NewContact { override suspend fun ByteReadPacket.decode(bot: QQAndroidBot): Packet? { - return readBytes().loadAs(Structmsg.RspSystemMsgNew.serializer()).run { - val struct = groupmsgs.firstOrNull() ?: return null // 会有重复且无法过滤, 不要用 map - - if (!bot.client.syncingController.systemMsgNewGroupCacheList.addCache( - QQAndroidClient.MessageSvcSyncData.SystemMsgNewGroupSyncId(struct.msgSeq, struct.msgTime) - ) - ) { // duplicate - return null - } - - struct.msg?.run { - //this.soutv("SystemMsg") + fun handleStruct(struct: Structmsg.StructMsg): Packet? { + return struct.msg?.run { when (subType) { 1 -> { // 处理被邀请入群 或 处理成员入群申请 when (groupMsgType) { @@ -232,6 +235,31 @@ internal class NewContact { } } } + + return readBytes().loadAs(Structmsg.RspSystemMsgNew.serializer()).run { + groupmsgs.filter { + it.msgTime > bot.client.syncingController.latestMsgNewGroupTime + }.mapNotNull { struct -> + if (!bot.client.syncingController.systemMsgNewGroupCacheList.addCache( + QQAndroidClient.MessageSvcSyncData.SystemMsgNewGroupSyncId( + struct.msgSeq, + struct.msgTime + ) + ) + ) { // duplicate + return@mapNotNull null + } + handleStruct(struct) + }.let { packets -> + when { + packets.isEmpty() -> null + packets.size == 1 -> packets[0] + else -> MultiPacketByIterable(packets) + } + } + }.also { + bot.client.syncingController.latestMsgNewGroupTime = currentTimeSeconds() + } } internal object Action : OutgoingPacketFactory("ProfileService.Pb.ReqSystemMsgAction.Group") { From f12f78a55fa8725fdd03d3687823c8aa8838fccc Mon Sep 17 00:00:00 2001 From: Karlatemp Date: Sat, 20 Feb 2021 20:50:44 +0800 Subject: [PATCH 09/12] Query self profile when missing nickname; Fix #1023 --- mirai-core/src/commonMain/kotlin/AbstractBot.kt | 11 +++++------ 1 file changed, 5 insertions(+), 6 deletions(-) diff --git a/mirai-core/src/commonMain/kotlin/AbstractBot.kt b/mirai-core/src/commonMain/kotlin/AbstractBot.kt index 5bc3f2f02..1ec3b7475 100644 --- a/mirai-core/src/commonMain/kotlin/AbstractBot.kt +++ b/mirai-core/src/commonMain/kotlin/AbstractBot.kt @@ -292,13 +292,12 @@ internal abstract class AbstractBot constructor( // https://github.com/mamoe/mirai/issues/1019 kotlin.runCatching { - nick + bot.nick }.onFailure { - throw contextualBugReportException( - context = "Bot login", - forDebug = it.toString(), - e = it, - ) + bot.asQQAndroidBot().nick = MiraiImpl.queryProfile(bot, bot.id).nickname + if (bot.nick.isBlank()) { + logger.warning { "Unable to fetch nickname of bot." } + } } logger.info { "Login successful" } From a5c28758e93bc0845c283f30da9ccb8764c78361 Mon Sep 17 00:00:00 2001 From: Karlatemp Date: Sun, 21 Feb 2021 14:28:51 +0800 Subject: [PATCH 10/12] Improve RspSystemMsgNew filtering --- .../kotlin/network/QQAndroidClient.kt | 5 ++-- .../protocol/packet/chat/NewContact.kt | 27 ++++++++++++++----- 2 files changed, 23 insertions(+), 9 deletions(-) diff --git a/mirai-core/src/commonMain/kotlin/network/QQAndroidClient.kt b/mirai-core/src/commonMain/kotlin/network/QQAndroidClient.kt index d76162f18..67dcf2639 100644 --- a/mirai-core/src/commonMain/kotlin/network/QQAndroidClient.kt +++ b/mirai-core/src/commonMain/kotlin/network/QQAndroidClient.kt @@ -182,12 +182,13 @@ internal open class QQAndroidClient( val pbGetMessageCacheList = SyncingCacheList() - internal data class SystemMsgNewGroupSyncId( + internal data class SystemMsgNewSyncId( val sequence: Long, val time: Long ) - val systemMsgNewGroupCacheList = SyncingCacheList(10) + val systemMsgNewGroupCacheList = SyncingCacheList(10) + val systemMsgNewFriendCacheList = SyncingCacheList(10) internal data class PbPushTransMsgSyncId( diff --git a/mirai-core/src/commonMain/kotlin/network/protocol/packet/chat/NewContact.kt b/mirai-core/src/commonMain/kotlin/network/protocol/packet/chat/NewContact.kt index 707e03174..edd09df57 100644 --- a/mirai-core/src/commonMain/kotlin/network/protocol/packet/chat/NewContact.kt +++ b/mirai-core/src/commonMain/kotlin/network/protocol/packet/chat/NewContact.kt @@ -28,7 +28,7 @@ import net.mamoe.mirai.internal.network.protocol.packet.buildOutgoingUniPacket import net.mamoe.mirai.internal.utils._miraiContentToString import net.mamoe.mirai.internal.utils.io.serialization.loadAs import net.mamoe.mirai.internal.utils.io.serialization.writeProtoBuf -import net.mamoe.mirai.utils.currentTimeSeconds +import kotlin.math.max internal class NewContact { @@ -60,8 +60,17 @@ internal class NewContact { override suspend fun ByteReadPacket.decode(bot: QQAndroidBot): Packet? { readBytes().loadAs(Structmsg.RspSystemMsgNew.serializer()).run { return friendmsgs.filter { - it.msgTime > bot.client.syncingController.latestMsgNewFriendTime + it.msgTime >= bot.client.syncingController.latestMsgNewFriendTime }.mapNotNull { struct -> + if (!bot.client.syncingController.systemMsgNewFriendCacheList.addCache( + QQAndroidClient.MessageSvcSyncData.SystemMsgNewSyncId( + struct.msgSeq, + struct.msgTime + ) + ) + ) { // duplicate + return@mapNotNull null + } struct.msg?.run { NewFriendRequestEvent( bot, @@ -79,7 +88,9 @@ internal class NewContact { else -> MultiPacketByIterable(packets) } }.also { - bot.client.syncingController.latestMsgNewFriendTime = currentTimeSeconds() + bot.client.syncingController.run { + latestMsgNewFriendTime = max(latestMsgNewFriendTime, friendmsgs.maxOfOrNull { it.msgTime } ?: 0) + } } } } @@ -238,10 +249,10 @@ internal class NewContact { return readBytes().loadAs(Structmsg.RspSystemMsgNew.serializer()).run { groupmsgs.filter { - it.msgTime > bot.client.syncingController.latestMsgNewGroupTime + it.msgTime >= bot.client.syncingController.latestMsgNewGroupTime }.mapNotNull { struct -> if (!bot.client.syncingController.systemMsgNewGroupCacheList.addCache( - QQAndroidClient.MessageSvcSyncData.SystemMsgNewGroupSyncId( + QQAndroidClient.MessageSvcSyncData.SystemMsgNewSyncId( struct.msgSeq, struct.msgTime ) @@ -256,9 +267,11 @@ internal class NewContact { packets.size == 1 -> packets[0] else -> MultiPacketByIterable(packets) } + }.also { + bot.client.syncingController.run { + latestMsgNewGroupTime = max(latestMsgNewGroupTime, groupmsgs.maxOfOrNull { it.msgTime } ?: 0) + } } - }.also { - bot.client.syncingController.latestMsgNewGroupTime = currentTimeSeconds() } } From 69cceaf6952be4d0f2bdee856438d64455221140 Mon Sep 17 00:00:00 2001 From: Him188 Date: Mon, 22 Feb 2021 13:25:58 +0800 Subject: [PATCH 11/12] Update Bots.md --- docs/Bots.md | 55 +++++++++++++++++++++++++++++++++++++++++++++++++--- 1 file changed, 52 insertions(+), 3 deletions(-) diff --git a/docs/Bots.md b/docs/Bots.md index b9d975450..b9aaee885 100644 --- a/docs/Bots.md +++ b/docs/Bots.md @@ -68,6 +68,25 @@ workingDir = File("C:/mirai") setWorkingDir(File("C:/mirai")) ``` +#### 修改缓存目录 + +缓存目录会相对于 `workingDir` 解析。如 `File("cache")` 将会解析为 `workingDir` 内的 `cache` 目录。而 `File("C:/cache")` 将会解析为绝对的 `C:/cache` 目录。 + +默认为 `File("cache")` + +要修改缓存目录(自 mirai 2.4.0): +``` +// Kotlin +cacheDir = File("cache") // 最终为 workingDir 目录中的 cache 目录 +cacheDir = File("C:/cache") // 最终为 C:/cache + +// Java +setCacheDir(File("cache")) // 最终为 workingDir 目录中的 cache 目录 +setCacheDir(File("C:/cache")) // 最终为 C:/cache +``` + +目前缓存目录会存储列表缓存、登录服务器、资源会话秘钥等。这些数据的存储方式有可能变化,请不要修改缓存目录中的文件。 + #### 设备信息 Bot 默认使用全随机的设备信息。**在更换账号地点时候使用随机设备信息可能会导致无法登录**,当然,**成功登录时使用的设备信息也可以保存后在新的设备使用**。 @@ -102,7 +121,6 @@ protocol = BotConfiguration.MiraiProtocol.ANDROID_PAD // Java setProtocol(MiraiProtocol.ANDROID_PAD) ``` - #### 重定向日志 Bot 有两个日志类别,`Bot` 或 `Net`。`Bot` 为通常日志,如收到事件。`Net` 为网络日志,包含收到和发出的每一个包和网络层解析时遇到的错误。 @@ -149,6 +167,34 @@ setLoginSolver(new YourLoginSolver()) > 要获取更多有关 `LoginSolver` 的信息,查看 [LoginSolver.kt](../mirai-core-api/src/commonMain/kotlin/utils/LoginSolver.kt#L32) +#### 启用列表缓存 +Mirai 在启动时会拉取全部好友列表和群成员列表。当账号拥有过多群时登录可能缓慢,开启列表缓存会大幅加速登录过程。 + +Mirai 自动根据事件更新列表,并在每次登录时与服务器校验缓存有效性,**但有时候可能发生意外情况导致列表没有同步。如果出现找不到群员或好友等不同步情况,请关闭缓存并[提交 Bug](https://github.com/mamoe/mirai/issues/new?assignees=&labels=question&template=bug.md)** + +要开启列表缓存(自 mirai 2.4.0): +``` +// 开启所有列表缓存 +enableContactCache() +``` + +也可以只开启部分缓存: +``` +// Kotlin +contactListCache { + friendListCacheEnabled = true // 开启好友列表缓存 + groupMemberListCacheEnabled = true // 开启群成员列表缓存 + + saveIntervalMillis = 60_000 // 可选设置有更新时的保存时间间隔, 默认 60 秒 +} + +// Java +contactListCache.setFriendListCacheEnabled(true) // 开启好友列表缓存 +contactListCache.setGroupMemberListCacheEnabled(true) // 开启群成员列表缓存 +contactListCache.setSaveIntervalMillis(60000) // 可选设置有更新时的保存时间间隔, 默认 60 秒 +``` + + ### 获取当前所有 `Bot` 实例 在登录后 `Bot` 实例会被自动记录。可在 `Bot.instances` 获取到当前**在线**的所有 `Bot` 列表。 @@ -168,11 +214,14 @@ setLoginSolver(new YourLoginSolver()) ### 常见登录失败原因 +[#993]: https://github.com/mamoe/mirai/discussions/993 + | 错误信息 | 可能的原因 | 可能的解决方案 | |:--------------|:---------------|:----------------------| -| 当前版本过低 | 密码错误 | 检查密码 | -| 当前上网环境异常 | 设备锁 | 开启或关闭设备锁后重试登录 | +| 当前版本过低 | 密码错误 | 检查密码或修改密码到 16 位以内 | +| 当前上网环境异常 | 设备锁 | 开启或关闭设备锁 (登录保护) | | 禁止登录 | 需要处理滑块验证码 | [project-mirai/mirai-login-solver-selenium] | +| 密码错误 | 密码错误或过长 | 手机协议最大支持 16 位密码 ([#993]). 在官方 PC 客户端登录后修改密码 | 若以上方案无法解决问题,请尝试 [切换登录协议](#切换登录协议) 和 **[处理滑动验证码](#处理滑动验证码)**。 From 9bb3ae20ef7f53a50910e006d314501e7ce84c97 Mon Sep 17 00:00:00 2001 From: Karlatemp Date: Tue, 23 Feb 2021 23:00:32 +0800 Subject: [PATCH 12/12] MiraiCode of MusicShare (#1044) * Improve MiraiCode parsing * MiraiCode of MusicShare --- .../api/binary-compatibility-validator.api | 3 +- .../kotlin/message/code/internal/impl.kt | 80 ++++++++++++++++--- .../kotlin/message/data/MusicShare.kt | 17 +++- .../kotlin/message/code/TestMiraiCode.kt | 27 +++++++ 4 files changed, 114 insertions(+), 13 deletions(-) diff --git a/binary-compatibility-validator/api/binary-compatibility-validator.api b/binary-compatibility-validator/api/binary-compatibility-validator.api index aa4beb76c..b4fa01302 100644 --- a/binary-compatibility-validator/api/binary-compatibility-validator.api +++ b/binary-compatibility-validator/api/binary-compatibility-validator.api @@ -4706,12 +4706,13 @@ public final class net/mamoe/mirai/message/data/MusicKind : java/lang/Enum { public static fun values ()[Lnet/mamoe/mirai/message/data/MusicKind; } -public final class net/mamoe/mirai/message/data/MusicShare : net/mamoe/mirai/message/data/ConstrainSingle, net/mamoe/mirai/message/data/MessageContent { +public final class net/mamoe/mirai/message/data/MusicShare : net/mamoe/mirai/message/code/CodableMessage, net/mamoe/mirai/message/data/ConstrainSingle, net/mamoe/mirai/message/data/MessageContent { public static final field Key Lnet/mamoe/mirai/message/data/MusicShare$Key; public static final field SERIAL_NAME Ljava/lang/String; public synthetic fun (ILnet/mamoe/mirai/message/data/MusicKind;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Lkotlinx/serialization/internal/SerializationConstructorMarker;)V public fun (Lnet/mamoe/mirai/message/data/MusicKind;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;)V public fun (Lnet/mamoe/mirai/message/data/MusicKind;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;)V + public fun appendMiraiCodeTo (Ljava/lang/StringBuilder;)V public final fun component1 ()Lnet/mamoe/mirai/message/data/MusicKind; public final fun component2 ()Ljava/lang/String; public final fun component3 ()Ljava/lang/String; diff --git a/mirai-core-api/src/commonMain/kotlin/message/code/internal/impl.kt b/mirai-core-api/src/commonMain/kotlin/message/code/internal/impl.kt index 39f226255..cfe2a0abe 100644 --- a/mirai-core-api/src/commonMain/kotlin/message/code/internal/impl.kt +++ b/mirai-core-api/src/commonMain/kotlin/message/code/internal/impl.kt @@ -25,13 +25,7 @@ internal fun String.parseMiraiCodeImpl(contact: Contact?): MessageChain = buildM add(PlainText(origin.decodeMiraiCode())) return@forEachMiraiCode } - parser.argsRegex.matchEntire(args) - ?.destructured - ?.let { - parser.runCatching { - contact.mapper(it) - }.getOrNull() - } + parser.parse(contact, args) ?.let(::add) ?: add(PlainText(origin.decodeMiraiCode())) } @@ -128,12 +122,76 @@ private object MiraiCodeParsers : Map by mapOf( "dice" to MiraiCodeParser(Regex("""([1-6])""")) { (value) -> Dice(value.toInt()) }, + "musicshare" to MiraiCodeParser.DynamicParser(7) { args -> + val (kind, title, summary, jumpUrl, pictureUrl) = args + val musicUrl = args[5] + val brief = args[6] + + MusicShare(MusicKind.valueOf(kind), title, summary, jumpUrl, pictureUrl, musicUrl, brief) + }, ) -private class MiraiCodeParser( - val argsRegex: Regex, - val mapper: Contact?.(MatchResult.Destructured) -> Message? -) + +// Visitable for test +internal sealed class MiraiCodeParser { + abstract fun parse(contact: Contact?, args: String): Message? + class RegexParser( + private val argsRegex: Regex, + private val mapper: Contact?.(MatchResult.Destructured) -> Message? + ) : MiraiCodeParser() { + override fun parse(contact: Contact?, args: String): Message? = + argsRegex.matchEntire(args) + ?.destructured + ?.let { + runCatching { + contact.mapper(it) + }.getOrNull() + } + } + + class DynamicParser( + private val minArgs: Int, + private val maxArgs: Int = minArgs, + private val parser: (Contact?.(args: Array) -> Message?), + ) : MiraiCodeParser() { + override fun parse(contact: Contact?, args: String): Message? { + val ranges = mutableListOf() + if (args.isNotEmpty()) { + var begin = 0 + var pos = 0 + val len = args.length + while (pos < len) { + when (args[pos]) { + '\\' -> pos += 2 + ',' -> { + ranges.add(begin..pos) + pos++ + begin = pos + } + else -> pos++ + } + } + ranges.add(begin..len) + } + if (ranges.size < minArgs) return null + if (ranges.size > maxArgs) return null + @Suppress("RemoveExplicitTypeArguments") + val args0 = Array(ranges.size) { index -> + val range = ranges[index] + args.substring(range.first, range.last).decodeMiraiCode() + } + runCatching { + return parser(contact, args0) + } + return null + } + } +} + +private fun MiraiCodeParser( + argsRegex: Regex, + mapper: Contact?.(MatchResult.Destructured) -> Message? +): MiraiCodeParser = MiraiCodeParser.RegexParser(argsRegex, mapper) internal fun StringBuilder.appendStringAsMiraiCode(value: String): StringBuilder = apply { value.forEach { char -> diff --git a/mirai-core-api/src/commonMain/kotlin/message/data/MusicShare.kt b/mirai-core-api/src/commonMain/kotlin/message/data/MusicShare.kt index fe63cc520..e80a125ae 100644 --- a/mirai-core-api/src/commonMain/kotlin/message/data/MusicShare.kt +++ b/mirai-core-api/src/commonMain/kotlin/message/data/MusicShare.kt @@ -13,6 +13,8 @@ package net.mamoe.mirai.message.data import kotlinx.serialization.SerialName import kotlinx.serialization.Serializable +import net.mamoe.mirai.message.code.CodableMessage +import net.mamoe.mirai.message.code.internal.appendStringAsMiraiCode import net.mamoe.mirai.utils.MiraiExperimentalApi import net.mamoe.mirai.utils.MiraiInternalApi import net.mamoe.mirai.utils.safeCast @@ -53,7 +55,7 @@ public data class MusicShare( * 在消息列表显示 */ public val brief: String, -) : MessageContent, ConstrainSingle { +) : MessageContent, ConstrainSingle, CodableMessage { public constructor( /** @@ -88,6 +90,19 @@ public data class MusicShare( override fun contentToString(): String = brief.takeIf { it.isNotBlank() } ?: "[分享]$title" // empty content is not accepted by `sendMessage` + override fun appendMiraiCodeTo(builder: StringBuilder) { + builder.append("[mirai:musicshare:") + .append(kind.name) + .append(',').appendStringAsMiraiCode(title) + .append(',').appendStringAsMiraiCode(summary) + .append(',').appendStringAsMiraiCode(jumpUrl) + .append(',').appendStringAsMiraiCode(pictureUrl) + .append(',').appendStringAsMiraiCode(musicUrl) + .append(',').appendStringAsMiraiCode(brief) + .append(']') + } + + // MusicShare(type=NeteaseCloudMusic, title='ファッション', summary='rinahamu/Yunomi', brief='', url='http://music.163.com/song/1338728297/?userid=324076307', pictureUrl='http://p2.music.126.net/y19E5SadGUmSR8SZxkrNtw==/109951163785855539.jpg', musicUrl='http://music.163.com/song/media/outer/url?id=1338728297&userid=324076307') /** diff --git a/mirai-core-api/src/commonTest/kotlin/message/code/TestMiraiCode.kt b/mirai-core-api/src/commonTest/kotlin/message/code/TestMiraiCode.kt index 6b0dcc4b3..a62b48daa 100644 --- a/mirai-core-api/src/commonTest/kotlin/message/code/TestMiraiCode.kt +++ b/mirai-core-api/src/commonTest/kotlin/message/code/TestMiraiCode.kt @@ -10,11 +10,27 @@ package net.mamoe.mirai.message.code import net.mamoe.mirai.message.code.MiraiCode.deserializeMiraiCode +import net.mamoe.mirai.message.code.internal.MiraiCodeParser import net.mamoe.mirai.message.data.* import org.junit.jupiter.api.Test import kotlin.test.assertEquals +import kotlin.test.assertNotNull class TestMiraiCode { + @Test + fun testDynamicMiraiCodeParser() { + fun runTest(args: Int, code: String, parse: (args: Array) -> Unit) { + val response = MiraiCodeParser.DynamicParser(args) { args0 -> parse(args0); AtAll }.parse(null, code) + assertNotNull(response, "Parser not invoked") + } + runTest(3, "test,\\,test,\\,\\,test") { (arg1, arg2, arg3) -> + assertEquals("test", arg1) + assertEquals(",test", arg2) + assertEquals(",,test", arg3) + } + runTest(2, ",") {} + } + @Test fun testCodes() { assertEquals(AtAll.toMessageChain(), "[mirai:atall]".deserializeMiraiCode()) @@ -46,5 +62,16 @@ class TestMiraiCode { assertEquals(buildMessageChain { +Dice(1) }, "[mirai:dice:1]".deserializeMiraiCode()) + + val musicShare = MusicShare( + kind = MusicKind.NeteaseCloudMusic, + title = "ファッション", + summary = "rinahamu/Yunomi", + jumpUrl = "http://music.163.com/song/1338728297/?userid=324076307", + pictureUrl = "http://p2.music.126.net/y19E5SadGUmSR8SZxkrNtw==/109951163785855539.jpg", + musicUrl = "http://music.163.com/song/media/outer/url?id=1338728297&userid=324076307", + brief = "", + ) + assertEquals(musicShare.toMessageChain(), musicShare.serializeToMiraiCode().deserializeMiraiCode()) } } \ No newline at end of file