diff --git a/binary-compatibility-validator/api/binary-compatibility-validator.api b/binary-compatibility-validator/api/binary-compatibility-validator.api index 91edf6844..aa4beb76c 100644 --- a/binary-compatibility-validator/api/binary-compatibility-validator.api +++ b/binary-compatibility-validator/api/binary-compatibility-validator.api @@ -3475,6 +3475,41 @@ public final class net/mamoe/mirai/message/data/CustomMessageMetadata$Companion public final fun serializer ()Lkotlinx/serialization/KSerializer; } +public final class net/mamoe/mirai/message/data/Dice : net/mamoe/mirai/message/code/CodableMessage, net/mamoe/mirai/message/data/MarketFace { + public static final field Key Lnet/mamoe/mirai/message/data/Dice$Key; + public static final field SERIAL_NAME Ljava/lang/String; + public fun (I)V + public synthetic fun (IILkotlinx/serialization/internal/SerializationConstructorMarker;)V + public fun appendMiraiCodeTo (Ljava/lang/StringBuilder;)V + public final fun component1 ()I + public final fun copy (I)Lnet/mamoe/mirai/message/data/Dice; + public static synthetic fun copy$default (Lnet/mamoe/mirai/message/data/Dice;IILjava/lang/Object;)Lnet/mamoe/mirai/message/data/Dice; + public fun equals (Ljava/lang/Object;)Z + public fun getId ()I + public fun getName ()Ljava/lang/String; + public final fun getValue ()I + public fun hashCode ()I + public static final fun random ()Lnet/mamoe/mirai/message/data/Dice; + public fun toString ()Ljava/lang/String; + public static final fun write$Self (Lnet/mamoe/mirai/message/data/Dice;Lkotlinx/serialization/encoding/CompositeEncoder;Lkotlinx/serialization/descriptors/SerialDescriptor;)V +} + +public final class net/mamoe/mirai/message/data/Dice$$serializer : kotlinx/serialization/internal/GeneratedSerializer { + public static final field INSTANCE Lnet/mamoe/mirai/message/data/Dice$$serializer; + public fun childSerializers ()[Lkotlinx/serialization/KSerializer; + public synthetic fun deserialize (Lkotlinx/serialization/encoding/Decoder;)Ljava/lang/Object; + public fun deserialize (Lkotlinx/serialization/encoding/Decoder;)Lnet/mamoe/mirai/message/data/Dice; + public fun getDescriptor ()Lkotlinx/serialization/descriptors/SerialDescriptor; + public synthetic fun serialize (Lkotlinx/serialization/encoding/Encoder;Ljava/lang/Object;)V + public fun serialize (Lkotlinx/serialization/encoding/Encoder;Lnet/mamoe/mirai/message/data/Dice;)V + public fun typeParametersSerializers ()[Lkotlinx/serialization/KSerializer; +} + +public final class net/mamoe/mirai/message/data/Dice$Key : net/mamoe/mirai/message/data/AbstractPolymorphicMessageKey { + public final fun random ()Lnet/mamoe/mirai/message/data/Dice; + public final fun serializer ()Lkotlinx/serialization/KSerializer; +} + public final class net/mamoe/mirai/message/data/EmptyMessageChain : java/util/List, kotlin/jvm/internal/markers/KMappedMarker, net/mamoe/mirai/message/data/MessageChain { public static final field INSTANCE Lnet/mamoe/mirai/message/data/EmptyMessageChain; public synthetic fun add (ILjava/lang/Object;)V diff --git a/docs/Messages.md b/docs/Messages.md index f31caf988..0ec299070 100644 --- a/docs/Messages.md +++ b/docs/Messages.md @@ -76,6 +76,7 @@ Mirai 支持富文本消息。 [`FlashImage`]: ../mirai-core-api/src/commonMain/kotlin/message/data/FlashImage.kt [`MarketFace`]: ../mirai-core-api/src/commonMain/kotlin/message/data/MarketFace.kt [`MusicShare`]: ../mirai-core-api/src/commonMain/kotlin/message/data/MusicShare.kt +[`Dice`]: ../mirai-core-api/src/commonMain/kotlin/message/data/Dice.kt [`MessageSource`]: ../mirai-core-api/src/commonMain/kotlin/message/data/MessageSource.kt [`QuoteReply`]: ../mirai-core-api/src/commonMain/kotlin/message/data/QuoteReply.kt @@ -102,7 +103,8 @@ Mirai 支持富文本消息。 | [`MarketFace`] | 商城表情 | `[表情对应的中文名]` | 2.0 | | [`ForwardMessage`] | 合并转发 | `[转发消息]` | 2.0 *(1)* | | [`SimpleServiceMessage`] | (不稳定)服务消息 | `$content` | 2.0 | -| [`MusicShare`] | 音乐分享 | `[分享]曲名` | 2.1 | +| [`MusicShare`] | 音乐分享 | `[分享]曲名` | 2.1 | +| [`Dice`] | 骰子 | `[骰子:$value]` | 2.5 | @@ -357,6 +359,7 @@ at.serializeToMiraiCode() // 结果为 `[mirai:at:123]` | [`VipFace`] | `[mirai:vipface:${kind.id},${kind.name},$count]` | | [`LightApp`] | `[mirai:app:$content]` | | [`SimpleServiceMessage`] | `[mirai:service:$serviceId,$content]` | +| [`Dice`] | `[mirai:dice:$value]` | ### 由 mirai 码字符串取得 `MessageChain` 实例 diff --git a/mirai-core-api/src/commonMain/kotlin/internal/message/MessageSerializersImpl.kt b/mirai-core-api/src/commonMain/kotlin/internal/message/MessageSerializersImpl.kt index 67c15a509..feed5c709 100644 --- a/mirai-core-api/src/commonMain/kotlin/internal/message/MessageSerializersImpl.kt +++ b/mirai-core-api/src/commonMain/kotlin/internal/message/MessageSerializersImpl.kt @@ -141,6 +141,8 @@ private val builtInSerializersModule by lazy { subclass(FlashImage::class, FlashImage.serializer()) subclass(MusicShare::class, MusicShare.serializer()) + + subclass(Dice::class, Dice.serializer()) } 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 6d87eb554..39f226255 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 @@ -124,7 +124,10 @@ private object MiraiCodeParsers : Map by mapOf( }, "app" to MiraiCodeParser(Regex("""(.*)""")) { (content) -> LightApp(content.decodeMiraiCode()) - } + }, + "dice" to MiraiCodeParser(Regex("""([1-6])""")) { (value) -> + Dice(value.toInt()) + }, ) private class MiraiCodeParser( diff --git a/mirai-core-api/src/commonMain/kotlin/message/data/Dice.kt b/mirai-core-api/src/commonMain/kotlin/message/data/Dice.kt new file mode 100644 index 000000000..8265fa2eb --- /dev/null +++ b/mirai-core-api/src/commonMain/kotlin/message/data/Dice.kt @@ -0,0 +1,67 @@ +/* + * Copyright 2019-2021 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("NOTHING_TO_INLINE") +@file:JvmMultifileClass +@file:JvmName("MessageUtils") + +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.utils.MiraiExperimentalApi +import net.mamoe.mirai.utils.safeCast +import org.jetbrains.annotations.Range +import kotlin.random.Random +import kotlin.random.nextInt + +/** + * 骰子. + * + * @since 2.5 + */ +@Serializable +@SerialName(Dice.SERIAL_NAME) +public data class Dice( + /** + * 骰子的点数. 范围为 1..6 + */ + public val value: @Range(from = 1, to = 6) Int +) : MarketFace, CodableMessage { + init { + require(value in 1..6) { "Dice.value must be in 1 and 6 inclusive." } + } + + @MiraiExperimentalApi + override val name: String + get() = "[骰子:$value]" + + @MiraiExperimentalApi + override val id: Int + get() = 11464 + + @MiraiExperimentalApi + override fun appendMiraiCodeTo(builder: StringBuilder) { + builder.append("[mirai:dice:").append(value).append(']') + } + + override fun toString(): String = "[mirai:dice:$value]" + + public companion object Key : + AbstractPolymorphicMessageKey(MarketFace, { it.safeCast() }) { + public const val SERIAL_NAME: String = "Dice" + + /** + * 创建随机点数的 [骰子][Dice] + */ + @JvmStatic + public fun random(): Dice = Dice(Random.nextInt(1..6)) + } +} diff --git a/mirai-core-api/src/commonMain/kotlin/message/data/MarketFace.kt b/mirai-core-api/src/commonMain/kotlin/message/data/MarketFace.kt index 4a4151743..74d59ee34 100644 --- a/mirai-core-api/src/commonMain/kotlin/message/data/MarketFace.kt +++ b/mirai-core-api/src/commonMain/kotlin/message/data/MarketFace.kt @@ -15,7 +15,9 @@ import net.mamoe.mirai.utils.safeCast /** * 商城表情 * - * 目前不支持直接发送,可保存接收到的来自官方客户端的商城表情然后转发. + * 除 [Dice] 可以发送外, 目前不支持直接发送,可保存接收到的来自官方客户端的商城表情然后转发. + * + * @see Dice */ public interface MarketFace : HummerMessage { /** 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 4a332ce9d..6b0dcc4b3 100644 --- a/mirai-core-api/src/commonTest/kotlin/message/code/TestMiraiCode.kt +++ b/mirai-core-api/src/commonTest/kotlin/message/code/TestMiraiCode.kt @@ -43,5 +43,8 @@ class TestMiraiCode { +SimpleServiceMessage(1, "[HiHi!!!\\]") +PlainText(" XE") }, "[mirai:service:1,\\[HiHi!!!\\\\\\]] XE".deserializeMiraiCode()) + assertEquals(buildMessageChain { + +Dice(1) + }, "[mirai:dice:1]".deserializeMiraiCode()) } } \ No newline at end of file diff --git a/mirai-core/src/commonMain/kotlin/contact/SendMessageHandler.kt b/mirai-core/src/commonMain/kotlin/contact/SendMessageHandler.kt index 9fb5a732f..bd682a32f 100644 --- a/mirai-core/src/commonMain/kotlin/contact/SendMessageHandler.kt +++ b/mirai-core/src/commonMain/kotlin/contact/SendMessageHandler.kt @@ -240,7 +240,9 @@ internal abstract class SendMessageHandler { * - ... any others for future */ internal suspend fun SendMessageHandler.transformSpecialMessages(message: Message): MessageChain { - return message.takeSingleContent()?.let { forward -> + suspend fun processForwardMessage( + forward: ForwardMessage + ): ForwardMessageInternal { if (!(message is MessageChain && message.contains(IgnoreLengthCheck))) { check(forward.nodeList.size <= 200) { throw MessageTooLargeException( @@ -256,12 +258,20 @@ internal suspend fun SendMessageHandler.transformSpecialMessage message = forward.nodeList, isLong = false, ) - RichMessage.forwardMessage( + return RichMessage.forwardMessage( resId = resId, timeSeconds = currentTimeSeconds(), forwardMessage = forward, ) - }?.toMessageChain() ?: message.toMessageChain() + } + + fun processDice(dice: Dice): MarketFaceImpl { + return MarketFaceImpl(dice.toJceStruct()) + } + + return message.takeSingleContent()?.let { processForwardMessage(it) }?.toMessageChain() + ?: message.takeSingleContent()?.let { processDice(it) }?.toMessageChain() + ?: message.toMessageChain() } /** diff --git a/mirai-core/src/commonMain/kotlin/message/MarketFaceImpl.kt b/mirai-core/src/commonMain/kotlin/message/MarketFaceImpl.kt new file mode 100644 index 000000000..0f08e39e4 --- /dev/null +++ b/mirai-core/src/commonMain/kotlin/message/MarketFaceImpl.kt @@ -0,0 +1,93 @@ +/* + * Copyright 2019-2021 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.internal.message + +import kotlinx.serialization.SerialName +import kotlinx.serialization.Serializable +import kotlinx.serialization.Transient +import net.mamoe.mirai.contact.Contact +import net.mamoe.mirai.internal.network.protocol.data.proto.ImMsgBody +import net.mamoe.mirai.message.data.Dice +import net.mamoe.mirai.message.data.MarketFace +import net.mamoe.mirai.message.data.Message +import net.mamoe.mirai.message.data.MessageChain + +@SerialName(MarketFace.SERIAL_NAME) +@Serializable +internal data class MarketFaceImpl internal constructor( + internal val delegate: ImMsgBody.MarketFace, +) : MarketFace { + + override val name: String get() = delegate.faceName.decodeToString() + + @Transient + override val id: Int = delegate.tabId + + override fun toString() = "[mirai:marketface:$id,$name]" +} + +/** + * For refinement + */ +internal class MarketFaceInternal( + @JvmField private val delegate: ImMsgBody.MarketFace, +) : MarketFace, RefinableMessage { + override val name: String get() = delegate.faceName.decodeToString() + override val id: Int get() = delegate.tabId + + override suspend fun refine(contact: Contact, context: MessageChain): Message { + delegate.toDiceOrNull()?.let { return it } // TODO: 2021/2/12 add dice origin, maybe rename RichMessageOrigin + return MarketFaceImpl(delegate) + } + + override fun toString(): String = "[mirai:marketface:$id,$name]" +} + +// From https://github.com/mamoe/mirai/issues/1012 +internal fun Dice.toJceStruct(): ImMsgBody.MarketFace { + return ImMsgBody.MarketFace( + faceName = byteArrayOf(91, -23, -86, -80, -27, -83, -112, 93), + itemType = 6, + faceInfo = 1, + faceId = byteArrayOf( + 72, 35, -45, -83, -79, 93, + -16, -128, 20, -50, 93, 103, + -106, -73, 110, -31 + ), + tabId = 11464, + subType = 3, + key = byteArrayOf(52, 48, 57, 101, 50, 97, 54, 57, 98, 49, 54, 57, 49, 56, 102, 57), + mediaType = 0, + imageWidth = 200, + imageHeight = 200, + mobileParam = byteArrayOf( + 114, 115, 99, 84, 121, 112, 101, + 63, 49, 59, 118, 97, 108, 117, + 101, 61, + (47 + value).toByte() + ), + pbReserve = byteArrayOf( + 10, 6, 8, -56, 1, 16, -56, 1, 64, + 1, 88, 0, 98, 9, 35, 48, 48, 48, + 48, 48, 48, 48, 48, 106, 9, 35, + 48, 48, 48, 48, 48, 48, 48, 48 + ) + ) +} + +private fun ImMsgBody.MarketFace.toDiceOrNull(): Dice? { + if (this.tabId != 11464) return null + val value = mobileParam.lastOrNull()?.toInt()?.and(0xff)?.minus(47) ?: 0 + if (value in 1..6) { + return Dice(value) + } + return null +} \ No newline at end of file diff --git a/mirai-core/src/commonMain/kotlin/message/ReceiveMessageHandler.kt b/mirai-core/src/commonMain/kotlin/message/ReceiveMessageHandler.kt index 2a22cb2a2..5b60f2157 100644 --- a/mirai-core/src/commonMain/kotlin/message/ReceiveMessageHandler.kt +++ b/mirai-core/src/commonMain/kotlin/message/ReceiveMessageHandler.kt @@ -140,7 +140,7 @@ private object ReceiveMessageTransformer { element.customFace != null -> decodeCustomFace(element.customFace, builder) element.face != null -> builder.add(Face(element.face.index)) element.text != null -> decodeText(element.text, builder) - element.marketFace != null -> builder.add(MarketFaceImpl(element.marketFace)) + element.marketFace != null -> builder.add(MarketFaceInternal(element.marketFace)) element.lightApp != null -> decodeLightApp(element.lightApp, builder) element.customElem != null -> decodeCustomElem(element.customElem, builder) element.commonElem != null -> decodeCommonElem(element.commonElem, builder) diff --git a/mirai-core/src/commonMain/kotlin/message/faceImpl.kt b/mirai-core/src/commonMain/kotlin/message/faceImpl.kt index 50386fdf3..c29bf5507 100644 --- a/mirai-core/src/commonMain/kotlin/message/faceImpl.kt +++ b/mirai-core/src/commonMain/kotlin/message/faceImpl.kt @@ -10,14 +10,10 @@ package net.mamoe.mirai.internal.message import kotlinx.io.core.toByteArray -import kotlinx.serialization.SerialName -import kotlinx.serialization.Serializable -import kotlinx.serialization.Transient import net.mamoe.mirai.internal.network.protocol.data.proto.HummerCommelem import net.mamoe.mirai.internal.network.protocol.data.proto.ImMsgBody import net.mamoe.mirai.internal.utils.io.serialization.toByteArray import net.mamoe.mirai.message.data.Face -import net.mamoe.mirai.message.data.MarketFace import net.mamoe.mirai.utils.hexToBytes import net.mamoe.mirai.utils.toByteArray @@ -42,18 +38,4 @@ internal fun Face.toCommData(): ImMsgBody.CommonElem { businessType = 1 ) -} - -@SerialName(MarketFace.SERIAL_NAME) -@Serializable -internal data class MarketFaceImpl internal constructor( - internal val delegate: ImMsgBody.MarketFace, -) : MarketFace { - - override val name: String get() = delegate.faceName.decodeToString() - - @Transient - override val id: Int = delegate.tabId - - override fun toString() = "[mirai:marketface:$id,$name]" } \ No newline at end of file diff --git a/mirai-core/src/commonMain/kotlin/network/QQAndroidClient.kt b/mirai-core/src/commonMain/kotlin/network/QQAndroidClient.kt index b7473f040..ac5db3141 100644 --- a/mirai-core/src/commonMain/kotlin/network/QQAndroidClient.kt +++ b/mirai-core/src/commonMain/kotlin/network/QQAndroidClient.kt @@ -14,7 +14,6 @@ package net.mamoe.mirai.internal.network import kotlinx.atomicfu.AtomicBoolean import kotlinx.atomicfu.AtomicInt import kotlinx.atomicfu.atomic -import kotlinx.coroutines.CompletableDeferred import kotlinx.io.core.BytePacketBuilder import kotlinx.io.core.String import kotlinx.io.core.toByteArray @@ -46,7 +45,7 @@ internal fun getRandomByteArray(length: Int): ByteArray = ByteArray(length) { Ra // [114.221.148.179:14000, 113.96.13.125:8080, 14.22.3.51:8080, 42.81.172.207:443, 114.221.144.89:80, 125.94.60.148:14000, 42.81.192.226:443, 114.221.148.233:8080, msfwifi.3g.qq.com:8080, 42.81.172.22:80] internal val DefaultServerList: MutableSet> = - "114.221.148.179:14000, 113.96.13.125:8080, 14.22.3.51:8080, 42.81.172.207:443, 114.221.144.89:80, 125.94.60.148:14000, 42.81.192.226:443, 114.221.148.233:8080, msfwifi.3g.qq.com:8080, 42.81.172.22:80" + "msfwifi.3g.qq.com:8080, 14.215.138.110:8080, 113.96.12.224:8080, 157.255.13.77:14000, 120.232.18.27:443, 183.3.235.162:14000, 163.177.89.195:443, 183.232.94.44:80, 203.205.255.224:8080, 203.205.255.221:8080" .split(", ") .map { val host = it.substringBefore(':') diff --git a/mirai-core/src/commonMain/kotlin/network/handler/QQAndroidBotNetworkHandler.kt b/mirai-core/src/commonMain/kotlin/network/handler/QQAndroidBotNetworkHandler.kt index f3ed11327..bc4fb5a2c 100644 --- a/mirai-core/src/commonMain/kotlin/network/handler/QQAndroidBotNetworkHandler.kt +++ b/mirai-core/src/commonMain/kotlin/network/handler/QQAndroidBotNetworkHandler.kt @@ -368,14 +368,14 @@ internal class QQAndroidBotNetworkHandler(coroutineContext: CoroutineContext, bo logger.warning { "Missing ConfigPushSvc.PushReq. Switching server..." } bot.launch { BotOfflineEvent.RequireReconnect(bot).broadcast() } } else { - logger.warning { "Missing ConfigPushSvc.PushReq. Using latest response. File uploading may be affected." } + logger.warning { "Missing ConfigPushSvc.PushReq. Using the latest response. File uploading may be affected." } } } - is ConfigPushSvc.PushReq.PushReqResponse.Success -> { - logger.info { "ConfigPushSvc.PushReq: Success." } + is ConfigPushSvc.PushReq.PushReqResponse.ConfigPush -> { + logger.info { "ConfigPushSvc.PushReq: Config updated." } } - is ConfigPushSvc.PushReq.PushReqResponse.ChangeServer -> { - logger.info { "ConfigPushSvc.PushReq: Require reconnect" } + is ConfigPushSvc.PushReq.PushReqResponse.ServerListPush -> { + logger.info { "ConfigPushSvc.PushReq: Server updated." } // handled in ConfigPushSvc return@launch } diff --git a/mirai-core/src/commonMain/kotlin/network/protocol/data/jce/ConfigPush.kt b/mirai-core/src/commonMain/kotlin/network/protocol/data/jce/ConfigPush.kt index 3eddddcf2..45e7d4293 100644 --- a/mirai-core/src/commonMain/kotlin/network/protocol/data/jce/ConfigPush.kt +++ b/mirai-core/src/commonMain/kotlin/network/protocol/data/jce/ConfigPush.kt @@ -147,6 +147,55 @@ internal class PushReq( @TarsId(3) @JvmField val seq: Long ) : JceStruct, Packet +@Serializable +internal data class ServerListPush( + @TarsId(1) val mobileSSOServerList: List, + @TarsId(3) val wifiSSOServerList: List, + @TarsId(4) val reconnectNeeded: Int = 0, + //@JvmField @TarsId(5) val skipped:Byte? = 0, + //@JvmField @TarsId(6) val skipped:Byte? = 0, + //@JvmField @TarsId(7) val skipped:Int? = 1, + @TarsId(8) val mobileHttpServerList: List, + @TarsId(9) val wifiHttpServerList: List, + @TarsId(10) val quicServerList: List, + @TarsId(11) val ssoServerListIpv6: List, + @TarsId(12) val httpServerListIpv6: List, + @TarsId(13) val quicServerListIpv6: List, + /** + * wifi下&1==1则启用 + * 移动数据(mobile)下&2==2则启用 + */ + @TarsId(14) val ipv6ConfigVal: Byte? = 0, + //@JvmField @TarsId(15) val netTestDelay:Int? = 0, + @TarsId(16) val configDesc: String? = "" +) : JceStruct { + + @Serializable + data class ServerInfo( + @TarsId(1) val host: String, + @TarsId(2) val port: Int, + //@JvmField @TarsId(3) val skipped: Byte = 0, + //@JvmField @TarsId(4) val skipped: Byte = 0, + /** + * 2,3->http + * 0,1->socket + */ + //@JvmField @TarsId(5) val protocolType: Byte? = 0, + //@JvmField @TarsId(6) val skipped: Int? = 8, + //@JvmField @TarsId(7) val skipped: Byte? = 0, + @TarsId(8) val location: String = "", + /** + * cm->China mobile 中国移动 + * uni->China unicom 中国联通 + * others->其他 + */ + @TarsId(9) val ispName: String = "" + ) : JceStruct { + override fun toString(): String { + return "$host:$port" + } + } +} @Serializable internal class PushResp( @TarsId(1) @JvmField val type: Int, diff --git a/mirai-core/src/commonMain/kotlin/network/protocol/packet/login/ConfigPushSvc.kt b/mirai-core/src/commonMain/kotlin/network/protocol/packet/login/ConfigPushSvc.kt index 070803cb9..52cdd4c80 100644 --- a/mirai-core/src/commonMain/kotlin/network/protocol/packet/login/ConfigPushSvc.kt +++ b/mirai-core/src/commonMain/kotlin/network/protocol/packet/login/ConfigPushSvc.kt @@ -12,7 +12,6 @@ package net.mamoe.mirai.internal.network.protocol.packet.login import kotlinx.coroutines.delay import kotlinx.coroutines.launch import kotlinx.io.core.ByteReadPacket -import kotlinx.serialization.Serializable import net.mamoe.mirai.event.AbstractEvent import net.mamoe.mirai.event.Event import net.mamoe.mirai.event.broadcast @@ -24,15 +23,15 @@ import net.mamoe.mirai.internal.network.Packet import net.mamoe.mirai.internal.network.protocol.data.jce.FileStoragePushFSSvcList import net.mamoe.mirai.internal.network.protocol.data.jce.PushResp import net.mamoe.mirai.internal.network.protocol.data.jce.RequestPacket +import net.mamoe.mirai.internal.network.protocol.data.jce.ServerListPush import net.mamoe.mirai.internal.network.protocol.data.proto.Subcmd0x501 import net.mamoe.mirai.internal.network.protocol.packet.IncomingPacketFactory import net.mamoe.mirai.internal.network.protocol.packet.OutgoingPacket import net.mamoe.mirai.internal.network.protocol.packet.buildResponseUniPacket -import net.mamoe.mirai.internal.utils.io.JceStruct +import net.mamoe.mirai.internal.utils.NetworkType import net.mamoe.mirai.internal.utils.io.serialization.jceRequestSBuffer import net.mamoe.mirai.internal.utils.io.serialization.loadAs import net.mamoe.mirai.internal.utils.io.serialization.readUniPacket -import net.mamoe.mirai.internal.utils.io.serialization.tars.TarsId import net.mamoe.mirai.internal.utils.io.serialization.writeJceStruct import net.mamoe.mirai.utils.info import net.mamoe.mirai.utils.toUHexString @@ -46,66 +45,46 @@ internal class ConfigPushSvc { ) { override val canBeCached: Boolean get() = false - sealed class PushReqResponse : Packet, Event, AbstractEvent(), Packet.NoEventLog { - class Success( - val struct: PushReqJceStruct - ) : PushReqResponse() { + sealed class PushReqResponse(val struct: PushReqJceStruct) : Packet, Event, AbstractEvent(), Packet.NoEventLog { + class Unknown(struct: PushReqJceStruct) : PushReqResponse(struct) { override fun toString(): String { - return "ConfigPushSvc.PushReq.PushReqResponse.Success" + return "ConfigPushSvc.PushReq.PushReqResponse.Unknown" } } - @Serializable - data class ChangeServer( - @TarsId(1) val serverList: List, - // @TarsId(3) val serverList2: List, - // @TarsId(8) val serverList3: List, - ) : JceStruct, PushReqResponse() { + class LogAction(struct: PushReqJceStruct) : PushReqResponse(struct) { override fun toString(): String { - return "ConfigPushSvc.PushReq.PushReqResponse.ChangeServer" - } - - @Serializable - data class ServerInfo( - /* - skipping String1 - skipping Short - skipping Byte - skipping Zero - skipping Zero - skipping Byte - skipping Byte - skipping String1 - skipping String1 - */ - @TarsId(1) val host: String, - @TarsId(2) val port: Int, - @TarsId(8) val location: String - ) : JceStruct { - override fun toString(): String { - return "$host:$port" - } + return "ConfigPushSvc.PushReq.PushReqResponse.LogAction" } } + + class ServerListPush(struct: PushReqJceStruct) : PushReqResponse(struct) { + override fun toString(): String { + return "ConfigPushSvc.PushReq.PushReqResponse.ServerListPush" + } + } + + class ConfigPush(struct: PushReqJceStruct) : PushReqResponse(struct) { + override fun toString(): String { + return "ConfigPushSvc.PushReq.PushReqResponse.ConfigPush" + } + } + + } override suspend fun ByteReadPacket.decode(bot: QQAndroidBot, sequenceId: Int): PushReqResponse { val pushReq = readUniPacket(PushReqJceStruct.serializer(), "PushReq") return when (pushReq.type) { - 1 -> kotlin.runCatching { - pushReq.jcebuf.loadAs(PushReqResponse.ChangeServer.serializer()) - }.getOrElse { - throw contextualBugReportException( - "ConfigPush.ReqPush type=1", - forDebug = pushReq.jcebuf.toUHexString(), - ) - } - else -> PushReqResponse.Success(pushReq) + 1 -> PushReqResponse.ServerListPush(pushReq) + 2 -> PushReqResponse.ConfigPush(pushReq) + 3 -> PushReqResponse.LogAction(pushReq) + else -> PushReqResponse.Unknown(pushReq) } } override suspend fun QQAndroidBot.handle(packet: PushReqResponse, sequenceId: Int): OutgoingPacket? { - fun handleSuccess(packet: PushReqResponse.Success) { + fun handleConfigPush(packet: PushReqResponse.ConfigPush) { val pushReq = packet.struct // FS server @@ -148,64 +127,79 @@ internal class ConfigPushSvc { ) } - fun handleRequireReconnect(resp: PushReqResponse.ChangeServer) { - bot.logger.info { "Server requires reconnect." } - bot.logger.info { "Server list: ${resp.serverList.joinToString()}." } + fun handleServerListPush(resp: PushReqResponse.ServerListPush) { + bot.network.logger.info { "Server list updated." } + val serverListPush = kotlin.runCatching { + resp.struct.jcebuf.loadAs(ServerListPush.serializer()) + }.getOrElse { + throw contextualBugReportException( + "ConfigPush.ReqPush type=1", + forDebug = resp.struct.jcebuf.toUHexString(), + ) + } + val pushServerList = if (client.networkType == NetworkType.WIFI) { + serverListPush.wifiSSOServerList + } else { + serverListPush.mobileSSOServerList + } - if (resp.serverList.isNotEmpty()) { + bot.logger.info { "Server list: ${pushServerList.joinToString()}." } + + if (pushServerList.isNotEmpty()) { bot.serverList.clear() - resp.serverList.shuffled().forEach { + pushServerList.shuffled().forEach { bot.serverList.add(it.host to it.port) } } bot.bdhSyncer.saveToCache() bot.bdhSyncer.saveServerListToCache() - - bot.launch { - delay(1000) - BotOfflineEvent.RequireReconnect(bot).broadcast() + if (serverListPush.reconnectNeeded == 1) { + bot.logger.info { "Server request to change server." } + bot.launch { + delay(1000) + BotOfflineEvent.RequireReconnect(bot).broadcast() + } } } when (packet) { - is PushReqResponse.Success -> { - handleSuccess(packet) - if (!client.wLoginSigInfoInitialized) return null // concurrently doing reconnection - return buildResponseUniPacket( - client, - sequenceId = sequenceId, - key = client.wLoginSigInfo.d2Key - ) { - writeJceStruct( - RequestPacket.serializer(), - RequestPacket( - requestId = 0, - version = 3, - servantName = "QQService.ConfigPushSvc.MainServant", - funcName = "PushResp", - sBuffer = jceRequestSBuffer( - "PushResp", - PushResp.serializer(), - PushResp( - type = packet.struct.type, - seq = packet.struct.seq, - jcebuf = if (packet.struct.type == 3) packet.struct.jcebuf else null - ) - ) + is PushReqResponse.ConfigPush -> { + handleConfigPush(packet) + } + is PushReqResponse.ServerListPush -> { + handleServerListPush(packet) + } + is PushReqResponse.LogAction, is PushReqResponse.Unknown -> { + //ignore + } + } + //Always send resp + if (!client.wLoginSigInfoInitialized) return null // concurrently doing reconnection + return buildResponseUniPacket( + client, + sequenceId = sequenceId, + key = client.wLoginSigInfo.d2Key + ) { + writeJceStruct( + RequestPacket.serializer(), + RequestPacket( + requestId = client.nextRequestPacketRequestId(), + version = 3, + servantName = "QQService.ConfigPushSvc.MainServant", + funcName = "PushResp", + sBuffer = jceRequestSBuffer( + "PushResp", + PushResp.serializer(), + PushResp( + type = packet.struct.type, + seq = packet.struct.seq, + jcebuf = if (packet.struct.type == 3) packet.struct.jcebuf else null ) ) - // writePacket(this.build().debugPrintThis()) - } - } - is PushReqResponse.ChangeServer -> { - handleRequireReconnect(packet) - return null - } - else -> { - // handled in QQABot - return null - } + ) + ) + // writePacket(this.build().debugPrintThis()) } } } -} \ No newline at end of file +} diff --git a/mirai-core/src/jvmTest/kotlin/message/data/MessageSerializationTest.kt b/mirai-core/src/jvmTest/kotlin/message/data/MessageSerializationTest.kt index 7e36e82c0..a044de77b 100644 --- a/mirai-core/src/jvmTest/kotlin/message/data/MessageSerializationTest.kt +++ b/mirai-core/src/jvmTest/kotlin/message/data/MessageSerializationTest.kt @@ -109,7 +109,8 @@ internal class MessageSerializationTest { image.toForwardMessage(1L, "test"), MusicShare(MusicKind.NeteaseCloudMusic, "123", "123", "123", "123", "123", "123"), RichMessageOrigin(SimpleServiceMessage(1, "content"), "resource id", RichMessageKind.LONG), - ShowImageFlag + ShowImageFlag, + Dice(1), ) companion object {