mirror of
https://github.com/mamoe/mirai.git
synced 2025-01-25 07:30:14 +08:00
Merge remote-tracking branch 'mirai/dev' into dev
This commit is contained in:
commit
9d20d28148
@ -3475,6 +3475,41 @@ public final class net/mamoe/mirai/message/data/CustomMessageMetadata$Companion
|
|||||||
public final fun serializer ()Lkotlinx/serialization/KSerializer;
|
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 <init> (I)V
|
||||||
|
public synthetic fun <init> (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 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 static final field INSTANCE Lnet/mamoe/mirai/message/data/EmptyMessageChain;
|
||||||
public synthetic fun add (ILjava/lang/Object;)V
|
public synthetic fun add (ILjava/lang/Object;)V
|
||||||
|
@ -76,6 +76,7 @@ Mirai 支持富文本消息。
|
|||||||
[`FlashImage`]: ../mirai-core-api/src/commonMain/kotlin/message/data/FlashImage.kt
|
[`FlashImage`]: ../mirai-core-api/src/commonMain/kotlin/message/data/FlashImage.kt
|
||||||
[`MarketFace`]: ../mirai-core-api/src/commonMain/kotlin/message/data/MarketFace.kt
|
[`MarketFace`]: ../mirai-core-api/src/commonMain/kotlin/message/data/MarketFace.kt
|
||||||
[`MusicShare`]: ../mirai-core-api/src/commonMain/kotlin/message/data/MusicShare.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
|
[`MessageSource`]: ../mirai-core-api/src/commonMain/kotlin/message/data/MessageSource.kt
|
||||||
[`QuoteReply`]: ../mirai-core-api/src/commonMain/kotlin/message/data/QuoteReply.kt
|
[`QuoteReply`]: ../mirai-core-api/src/commonMain/kotlin/message/data/QuoteReply.kt
|
||||||
@ -102,7 +103,8 @@ Mirai 支持富文本消息。
|
|||||||
| [`MarketFace`] | 商城表情 | `[表情对应的中文名]` | 2.0 |
|
| [`MarketFace`] | 商城表情 | `[表情对应的中文名]` | 2.0 |
|
||||||
| [`ForwardMessage`] | 合并转发 | `[转发消息]` | 2.0 *<sup>(1)</sup>* |
|
| [`ForwardMessage`] | 合并转发 | `[转发消息]` | 2.0 *<sup>(1)</sup>* |
|
||||||
| [`SimpleServiceMessage`] | (不稳定)服务消息 | `$content` | 2.0 |
|
| [`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]` |
|
| [`VipFace`] | `[mirai:vipface:${kind.id},${kind.name},$count]` |
|
||||||
| [`LightApp`] | `[mirai:app:$content]` |
|
| [`LightApp`] | `[mirai:app:$content]` |
|
||||||
| [`SimpleServiceMessage`] | `[mirai:service:$serviceId,$content]` |
|
| [`SimpleServiceMessage`] | `[mirai:service:$serviceId,$content]` |
|
||||||
|
| [`Dice`] | `[mirai:dice:$value]` |
|
||||||
|
|
||||||
### 由 mirai 码字符串取得 `MessageChain` 实例
|
### 由 mirai 码字符串取得 `MessageChain` 实例
|
||||||
|
|
||||||
|
@ -141,6 +141,8 @@ private val builtInSerializersModule by lazy {
|
|||||||
subclass(FlashImage::class, FlashImage.serializer())
|
subclass(FlashImage::class, FlashImage.serializer())
|
||||||
|
|
||||||
subclass(MusicShare::class, MusicShare.serializer())
|
subclass(MusicShare::class, MusicShare.serializer())
|
||||||
|
|
||||||
|
subclass(Dice::class, Dice.serializer())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@ -124,7 +124,10 @@ private object MiraiCodeParsers : Map<String, MiraiCodeParser> by mapOf(
|
|||||||
},
|
},
|
||||||
"app" to MiraiCodeParser(Regex("""(.*)""")) { (content) ->
|
"app" to MiraiCodeParser(Regex("""(.*)""")) { (content) ->
|
||||||
LightApp(content.decodeMiraiCode())
|
LightApp(content.decodeMiraiCode())
|
||||||
}
|
},
|
||||||
|
"dice" to MiraiCodeParser(Regex("""([1-6])""")) { (value) ->
|
||||||
|
Dice(value.toInt())
|
||||||
|
},
|
||||||
)
|
)
|
||||||
|
|
||||||
private class MiraiCodeParser(
|
private class MiraiCodeParser(
|
||||||
|
67
mirai-core-api/src/commonMain/kotlin/message/data/Dice.kt
Normal file
67
mirai-core-api/src/commonMain/kotlin/message/data/Dice.kt
Normal file
@ -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, Dice>(MarketFace, { it.safeCast() }) {
|
||||||
|
public const val SERIAL_NAME: String = "Dice"
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 创建随机点数的 [骰子][Dice]
|
||||||
|
*/
|
||||||
|
@JvmStatic
|
||||||
|
public fun random(): Dice = Dice(Random.nextInt(1..6))
|
||||||
|
}
|
||||||
|
}
|
@ -15,7 +15,9 @@ import net.mamoe.mirai.utils.safeCast
|
|||||||
/**
|
/**
|
||||||
* 商城表情
|
* 商城表情
|
||||||
*
|
*
|
||||||
* 目前不支持直接发送,可保存接收到的来自官方客户端的商城表情然后转发.
|
* 除 [Dice] 可以发送外, 目前不支持直接发送,可保存接收到的来自官方客户端的商城表情然后转发.
|
||||||
|
*
|
||||||
|
* @see Dice
|
||||||
*/
|
*/
|
||||||
public interface MarketFace : HummerMessage {
|
public interface MarketFace : HummerMessage {
|
||||||
/**
|
/**
|
||||||
|
@ -43,5 +43,8 @@ class TestMiraiCode {
|
|||||||
+SimpleServiceMessage(1, "[HiHi!!!\\]")
|
+SimpleServiceMessage(1, "[HiHi!!!\\]")
|
||||||
+PlainText(" XE")
|
+PlainText(" XE")
|
||||||
}, "[mirai:service:1,\\[HiHi!!!\\\\\\]] XE".deserializeMiraiCode())
|
}, "[mirai:service:1,\\[HiHi!!!\\\\\\]] XE".deserializeMiraiCode())
|
||||||
|
assertEquals(buildMessageChain {
|
||||||
|
+Dice(1)
|
||||||
|
}, "[mirai:dice:1]".deserializeMiraiCode())
|
||||||
}
|
}
|
||||||
}
|
}
|
@ -240,7 +240,9 @@ internal abstract class SendMessageHandler<C : Contact> {
|
|||||||
* - ... any others for future
|
* - ... any others for future
|
||||||
*/
|
*/
|
||||||
internal suspend fun <C : Contact> SendMessageHandler<C>.transformSpecialMessages(message: Message): MessageChain {
|
internal suspend fun <C : Contact> SendMessageHandler<C>.transformSpecialMessages(message: Message): MessageChain {
|
||||||
return message.takeSingleContent<ForwardMessage>()?.let { forward ->
|
suspend fun processForwardMessage(
|
||||||
|
forward: ForwardMessage
|
||||||
|
): ForwardMessageInternal {
|
||||||
if (!(message is MessageChain && message.contains(IgnoreLengthCheck))) {
|
if (!(message is MessageChain && message.contains(IgnoreLengthCheck))) {
|
||||||
check(forward.nodeList.size <= 200) {
|
check(forward.nodeList.size <= 200) {
|
||||||
throw MessageTooLargeException(
|
throw MessageTooLargeException(
|
||||||
@ -256,12 +258,20 @@ internal suspend fun <C : Contact> SendMessageHandler<C>.transformSpecialMessage
|
|||||||
message = forward.nodeList,
|
message = forward.nodeList,
|
||||||
isLong = false,
|
isLong = false,
|
||||||
)
|
)
|
||||||
RichMessage.forwardMessage(
|
return RichMessage.forwardMessage(
|
||||||
resId = resId,
|
resId = resId,
|
||||||
timeSeconds = currentTimeSeconds(),
|
timeSeconds = currentTimeSeconds(),
|
||||||
forwardMessage = forward,
|
forwardMessage = forward,
|
||||||
)
|
)
|
||||||
}?.toMessageChain() ?: message.toMessageChain()
|
}
|
||||||
|
|
||||||
|
fun processDice(dice: Dice): MarketFaceImpl {
|
||||||
|
return MarketFaceImpl(dice.toJceStruct())
|
||||||
|
}
|
||||||
|
|
||||||
|
return message.takeSingleContent<ForwardMessage>()?.let { processForwardMessage(it) }?.toMessageChain()
|
||||||
|
?: message.takeSingleContent<Dice>()?.let { processDice(it) }?.toMessageChain()
|
||||||
|
?: message.toMessageChain()
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
93
mirai-core/src/commonMain/kotlin/message/MarketFaceImpl.kt
Normal file
93
mirai-core/src/commonMain/kotlin/message/MarketFaceImpl.kt
Normal file
@ -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
|
||||||
|
}
|
@ -140,7 +140,7 @@ private object ReceiveMessageTransformer {
|
|||||||
element.customFace != null -> decodeCustomFace(element.customFace, builder)
|
element.customFace != null -> decodeCustomFace(element.customFace, builder)
|
||||||
element.face != null -> builder.add(Face(element.face.index))
|
element.face != null -> builder.add(Face(element.face.index))
|
||||||
element.text != null -> decodeText(element.text, builder)
|
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.lightApp != null -> decodeLightApp(element.lightApp, builder)
|
||||||
element.customElem != null -> decodeCustomElem(element.customElem, builder)
|
element.customElem != null -> decodeCustomElem(element.customElem, builder)
|
||||||
element.commonElem != null -> decodeCommonElem(element.commonElem, builder)
|
element.commonElem != null -> decodeCommonElem(element.commonElem, builder)
|
||||||
|
@ -10,14 +10,10 @@
|
|||||||
package net.mamoe.mirai.internal.message
|
package net.mamoe.mirai.internal.message
|
||||||
|
|
||||||
import kotlinx.io.core.toByteArray
|
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.HummerCommelem
|
||||||
import net.mamoe.mirai.internal.network.protocol.data.proto.ImMsgBody
|
import net.mamoe.mirai.internal.network.protocol.data.proto.ImMsgBody
|
||||||
import net.mamoe.mirai.internal.utils.io.serialization.toByteArray
|
import net.mamoe.mirai.internal.utils.io.serialization.toByteArray
|
||||||
import net.mamoe.mirai.message.data.Face
|
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.hexToBytes
|
||||||
import net.mamoe.mirai.utils.toByteArray
|
import net.mamoe.mirai.utils.toByteArray
|
||||||
|
|
||||||
@ -43,17 +39,3 @@ internal fun Face.toCommData(): ImMsgBody.CommonElem {
|
|||||||
)
|
)
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@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]"
|
|
||||||
}
|
|
@ -14,7 +14,6 @@ package net.mamoe.mirai.internal.network
|
|||||||
import kotlinx.atomicfu.AtomicBoolean
|
import kotlinx.atomicfu.AtomicBoolean
|
||||||
import kotlinx.atomicfu.AtomicInt
|
import kotlinx.atomicfu.AtomicInt
|
||||||
import kotlinx.atomicfu.atomic
|
import kotlinx.atomicfu.atomic
|
||||||
import kotlinx.coroutines.CompletableDeferred
|
|
||||||
import kotlinx.io.core.BytePacketBuilder
|
import kotlinx.io.core.BytePacketBuilder
|
||||||
import kotlinx.io.core.String
|
import kotlinx.io.core.String
|
||||||
import kotlinx.io.core.toByteArray
|
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]
|
// [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<Pair<String, Int>> =
|
internal val DefaultServerList: MutableSet<Pair<String, Int>> =
|
||||||
"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(", ")
|
.split(", ")
|
||||||
.map {
|
.map {
|
||||||
val host = it.substringBefore(':')
|
val host = it.substringBefore(':')
|
||||||
|
@ -368,14 +368,14 @@ internal class QQAndroidBotNetworkHandler(coroutineContext: CoroutineContext, bo
|
|||||||
logger.warning { "Missing ConfigPushSvc.PushReq. Switching server..." }
|
logger.warning { "Missing ConfigPushSvc.PushReq. Switching server..." }
|
||||||
bot.launch { BotOfflineEvent.RequireReconnect(bot).broadcast() }
|
bot.launch { BotOfflineEvent.RequireReconnect(bot).broadcast() }
|
||||||
} else {
|
} 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 -> {
|
is ConfigPushSvc.PushReq.PushReqResponse.ConfigPush -> {
|
||||||
logger.info { "ConfigPushSvc.PushReq: Success." }
|
logger.info { "ConfigPushSvc.PushReq: Config updated." }
|
||||||
}
|
}
|
||||||
is ConfigPushSvc.PushReq.PushReqResponse.ChangeServer -> {
|
is ConfigPushSvc.PushReq.PushReqResponse.ServerListPush -> {
|
||||||
logger.info { "ConfigPushSvc.PushReq: Require reconnect" }
|
logger.info { "ConfigPushSvc.PushReq: Server updated." }
|
||||||
// handled in ConfigPushSvc
|
// handled in ConfigPushSvc
|
||||||
return@launch
|
return@launch
|
||||||
}
|
}
|
||||||
|
@ -147,6 +147,55 @@ internal class PushReq(
|
|||||||
@TarsId(3) @JvmField val seq: Long
|
@TarsId(3) @JvmField val seq: Long
|
||||||
) : JceStruct, Packet
|
) : JceStruct, Packet
|
||||||
|
|
||||||
|
@Serializable
|
||||||
|
internal data class ServerListPush(
|
||||||
|
@TarsId(1) val mobileSSOServerList: List<ServerInfo>,
|
||||||
|
@TarsId(3) val wifiSSOServerList: List<ServerInfo>,
|
||||||
|
@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<ServerInfo>,
|
||||||
|
@TarsId(9) val wifiHttpServerList: List<ServerInfo>,
|
||||||
|
@TarsId(10) val quicServerList: List<ServerInfo>,
|
||||||
|
@TarsId(11) val ssoServerListIpv6: List<ServerInfo>,
|
||||||
|
@TarsId(12) val httpServerListIpv6: List<ServerInfo>,
|
||||||
|
@TarsId(13) val quicServerListIpv6: List<ServerInfo>,
|
||||||
|
/**
|
||||||
|
* 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
|
@Serializable
|
||||||
internal class PushResp(
|
internal class PushResp(
|
||||||
@TarsId(1) @JvmField val type: Int,
|
@TarsId(1) @JvmField val type: Int,
|
||||||
|
@ -12,7 +12,6 @@ package net.mamoe.mirai.internal.network.protocol.packet.login
|
|||||||
import kotlinx.coroutines.delay
|
import kotlinx.coroutines.delay
|
||||||
import kotlinx.coroutines.launch
|
import kotlinx.coroutines.launch
|
||||||
import kotlinx.io.core.ByteReadPacket
|
import kotlinx.io.core.ByteReadPacket
|
||||||
import kotlinx.serialization.Serializable
|
|
||||||
import net.mamoe.mirai.event.AbstractEvent
|
import net.mamoe.mirai.event.AbstractEvent
|
||||||
import net.mamoe.mirai.event.Event
|
import net.mamoe.mirai.event.Event
|
||||||
import net.mamoe.mirai.event.broadcast
|
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.FileStoragePushFSSvcList
|
||||||
import net.mamoe.mirai.internal.network.protocol.data.jce.PushResp
|
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.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.data.proto.Subcmd0x501
|
||||||
import net.mamoe.mirai.internal.network.protocol.packet.IncomingPacketFactory
|
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.OutgoingPacket
|
||||||
import net.mamoe.mirai.internal.network.protocol.packet.buildResponseUniPacket
|
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.jceRequestSBuffer
|
||||||
import net.mamoe.mirai.internal.utils.io.serialization.loadAs
|
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.readUniPacket
|
||||||
import net.mamoe.mirai.internal.utils.io.serialization.tars.TarsId
|
|
||||||
import net.mamoe.mirai.internal.utils.io.serialization.writeJceStruct
|
import net.mamoe.mirai.internal.utils.io.serialization.writeJceStruct
|
||||||
import net.mamoe.mirai.utils.info
|
import net.mamoe.mirai.utils.info
|
||||||
import net.mamoe.mirai.utils.toUHexString
|
import net.mamoe.mirai.utils.toUHexString
|
||||||
@ -46,66 +45,46 @@ internal class ConfigPushSvc {
|
|||||||
) {
|
) {
|
||||||
override val canBeCached: Boolean get() = false
|
override val canBeCached: Boolean get() = false
|
||||||
|
|
||||||
sealed class PushReqResponse : Packet, Event, AbstractEvent(), Packet.NoEventLog {
|
sealed class PushReqResponse(val struct: PushReqJceStruct) : Packet, Event, AbstractEvent(), Packet.NoEventLog {
|
||||||
class Success(
|
class Unknown(struct: PushReqJceStruct) : PushReqResponse(struct) {
|
||||||
val struct: PushReqJceStruct
|
|
||||||
) : PushReqResponse() {
|
|
||||||
override fun toString(): String {
|
override fun toString(): String {
|
||||||
return "ConfigPushSvc.PushReq.PushReqResponse.Success"
|
return "ConfigPushSvc.PushReq.PushReqResponse.Unknown"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@Serializable
|
class LogAction(struct: PushReqJceStruct) : PushReqResponse(struct) {
|
||||||
data class ChangeServer(
|
|
||||||
@TarsId(1) val serverList: List<ServerInfo>,
|
|
||||||
// @TarsId(3) val serverList2: List<ServerInfo>,
|
|
||||||
// @TarsId(8) val serverList3: List<ServerInfo>,
|
|
||||||
) : JceStruct, PushReqResponse() {
|
|
||||||
override fun toString(): String {
|
override fun toString(): String {
|
||||||
return "ConfigPushSvc.PushReq.PushReqResponse.ChangeServer"
|
return "ConfigPushSvc.PushReq.PushReqResponse.LogAction"
|
||||||
}
|
|
||||||
|
|
||||||
@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"
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
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 {
|
override suspend fun ByteReadPacket.decode(bot: QQAndroidBot, sequenceId: Int): PushReqResponse {
|
||||||
val pushReq = readUniPacket(PushReqJceStruct.serializer(), "PushReq")
|
val pushReq = readUniPacket(PushReqJceStruct.serializer(), "PushReq")
|
||||||
return when (pushReq.type) {
|
return when (pushReq.type) {
|
||||||
1 -> kotlin.runCatching {
|
1 -> PushReqResponse.ServerListPush(pushReq)
|
||||||
pushReq.jcebuf.loadAs(PushReqResponse.ChangeServer.serializer())
|
2 -> PushReqResponse.ConfigPush(pushReq)
|
||||||
}.getOrElse {
|
3 -> PushReqResponse.LogAction(pushReq)
|
||||||
throw contextualBugReportException(
|
else -> PushReqResponse.Unknown(pushReq)
|
||||||
"ConfigPush.ReqPush type=1",
|
|
||||||
forDebug = pushReq.jcebuf.toUHexString(),
|
|
||||||
)
|
|
||||||
}
|
|
||||||
else -> PushReqResponse.Success(pushReq)
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
override suspend fun QQAndroidBot.handle(packet: PushReqResponse, sequenceId: Int): OutgoingPacket? {
|
override suspend fun QQAndroidBot.handle(packet: PushReqResponse, sequenceId: Int): OutgoingPacket? {
|
||||||
fun handleSuccess(packet: PushReqResponse.Success) {
|
fun handleConfigPush(packet: PushReqResponse.ConfigPush) {
|
||||||
val pushReq = packet.struct
|
val pushReq = packet.struct
|
||||||
|
|
||||||
// FS server
|
// FS server
|
||||||
@ -148,63 +127,78 @@ internal class ConfigPushSvc {
|
|||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
fun handleRequireReconnect(resp: PushReqResponse.ChangeServer) {
|
fun handleServerListPush(resp: PushReqResponse.ServerListPush) {
|
||||||
bot.logger.info { "Server requires reconnect." }
|
bot.network.logger.info { "Server list updated." }
|
||||||
bot.logger.info { "Server list: ${resp.serverList.joinToString()}." }
|
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()
|
bot.serverList.clear()
|
||||||
resp.serverList.shuffled().forEach {
|
pushServerList.shuffled().forEach {
|
||||||
bot.serverList.add(it.host to it.port)
|
bot.serverList.add(it.host to it.port)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
bot.bdhSyncer.saveToCache()
|
bot.bdhSyncer.saveToCache()
|
||||||
bot.bdhSyncer.saveServerListToCache()
|
bot.bdhSyncer.saveServerListToCache()
|
||||||
|
if (serverListPush.reconnectNeeded == 1) {
|
||||||
bot.launch {
|
bot.logger.info { "Server request to change server." }
|
||||||
delay(1000)
|
bot.launch {
|
||||||
BotOfflineEvent.RequireReconnect(bot).broadcast()
|
delay(1000)
|
||||||
|
BotOfflineEvent.RequireReconnect(bot).broadcast()
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
when (packet) {
|
when (packet) {
|
||||||
is PushReqResponse.Success -> {
|
is PushReqResponse.ConfigPush -> {
|
||||||
handleSuccess(packet)
|
handleConfigPush(packet)
|
||||||
if (!client.wLoginSigInfoInitialized) return null // concurrently doing reconnection
|
}
|
||||||
return buildResponseUniPacket(
|
is PushReqResponse.ServerListPush -> {
|
||||||
client,
|
handleServerListPush(packet)
|
||||||
sequenceId = sequenceId,
|
}
|
||||||
key = client.wLoginSigInfo.d2Key
|
is PushReqResponse.LogAction, is PushReqResponse.Unknown -> {
|
||||||
) {
|
//ignore
|
||||||
writeJceStruct(
|
}
|
||||||
RequestPacket.serializer(),
|
}
|
||||||
RequestPacket(
|
//Always send resp
|
||||||
requestId = 0,
|
if (!client.wLoginSigInfoInitialized) return null // concurrently doing reconnection
|
||||||
version = 3,
|
return buildResponseUniPacket(
|
||||||
servantName = "QQService.ConfigPushSvc.MainServant",
|
client,
|
||||||
funcName = "PushResp",
|
sequenceId = sequenceId,
|
||||||
sBuffer = jceRequestSBuffer(
|
key = client.wLoginSigInfo.d2Key
|
||||||
"PushResp",
|
) {
|
||||||
PushResp.serializer(),
|
writeJceStruct(
|
||||||
PushResp(
|
RequestPacket.serializer(),
|
||||||
type = packet.struct.type,
|
RequestPacket(
|
||||||
seq = packet.struct.seq,
|
requestId = client.nextRequestPacketRequestId(),
|
||||||
jcebuf = if (packet.struct.type == 3) packet.struct.jcebuf else null
|
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())
|
)
|
||||||
}
|
)
|
||||||
}
|
// writePacket(this.build().debugPrintThis())
|
||||||
is PushReqResponse.ChangeServer -> {
|
|
||||||
handleRequireReconnect(packet)
|
|
||||||
return null
|
|
||||||
}
|
|
||||||
else -> {
|
|
||||||
// handled in QQABot
|
|
||||||
return null
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -109,7 +109,8 @@ internal class MessageSerializationTest {
|
|||||||
image.toForwardMessage(1L, "test"),
|
image.toForwardMessage(1L, "test"),
|
||||||
MusicShare(MusicKind.NeteaseCloudMusic, "123", "123", "123", "123", "123", "123"),
|
MusicShare(MusicKind.NeteaseCloudMusic, "123", "123", "123", "123", "123", "123"),
|
||||||
RichMessageOrigin(SimpleServiceMessage(1, "content"), "resource id", RichMessageKind.LONG),
|
RichMessageOrigin(SimpleServiceMessage(1, "content"), "resource id", RichMessageKind.LONG),
|
||||||
ShowImageFlag
|
ShowImageFlag,
|
||||||
|
Dice(1),
|
||||||
)
|
)
|
||||||
|
|
||||||
companion object {
|
companion object {
|
||||||
|
Loading…
Reference in New Issue
Block a user