Rewrite Group sendMessageImpl, improve current Highway impls, for music share #889 #682 and further friend message highway #194, #577

This commit is contained in:
Him188 2021-01-23 00:09:35 +08:00
parent 66ae897f20
commit d60006376c
12 changed files with 360 additions and 294 deletions

View File

@ -265,17 +265,4 @@ public class XmlMessageBuilder(
this.builder.append("<picture cover='$coverUrl'/>")
}
}
}
// internal runtime value, not serializable
internal data class LongMessage internal constructor(override val content: String, val resId: String) :
AbstractServiceMessage() {
override val serviceId: Int get() = 35
companion object Key : AbstractPolymorphicMessageKey<ServiceMessage, LongMessage>(ServiceMessage, { it.safeCast() })
}
// internal runtime value, not serializable
internal data class ForwardMessageInternal(override val content: String) : AbstractServiceMessage() {
override val serviceId: Int get() = 35
}

View File

@ -24,7 +24,7 @@ import net.mamoe.mirai.event.broadcast
import net.mamoe.mirai.event.events.*
import net.mamoe.mirai.internal.contact.*
import net.mamoe.mirai.internal.message.*
import net.mamoe.mirai.internal.network.highway.HighwayHelper
import net.mamoe.mirai.internal.network.highway.Highway
import net.mamoe.mirai.internal.network.protocol.data.jce.SvcDevLoginInfo
import net.mamoe.mirai.internal.network.protocol.data.proto.LongMsg
import net.mamoe.mirai.internal.network.protocol.packet.chat.*
@ -32,7 +32,6 @@ import net.mamoe.mirai.internal.network.protocol.packet.chat.voice.PttStore
import net.mamoe.mirai.internal.network.protocol.packet.list.FriendList
import net.mamoe.mirai.internal.network.protocol.packet.login.StatSvc
import net.mamoe.mirai.internal.utils.io.serialization.toByteArray
import net.mamoe.mirai.message.MessageReceipt
import net.mamoe.mirai.message.MessageSerializers
import net.mamoe.mirai.message.action.Nudge
import net.mamoe.mirai.message.data.*
@ -690,102 +689,76 @@ internal open class MiraiImpl : IMirai, LowLevelApiAccessor {
return jsonText?.let { json.decodeFromString(GroupHonorListData.serializer(), it) }
}
@JvmSynthetic
@LowLevelApi
@MiraiExperimentalApi
@Suppress("INVISIBLE_REFERENCE", "INVISIBLE_MEMBER")
internal suspend fun lowLevelSendGroupLongOrForwardMessage(
internal suspend fun uploadGroupMessageHighway(
bot: Bot,
groupCode: Long,
message: Collection<ForwardMessage.INode>,
isLong: Boolean,
forwardMessage: ForwardMessage?
): MessageReceipt<Group> = with(bot.asQQAndroidBot()) {
): String = with(bot.asQQAndroidBot()) {
message.forEach {
it.messageChain.ensureSequenceIdAvailable()
}
val group = getGroupOrFail(groupCode)
val time = currentTimeSeconds()
val sequenceId = client.atomicNextMessageSequenceId()
network.run {
val data = message.calculateValidationDataForGroup(
sequenceId = sequenceId,
random = Random.nextInt().absoluteValue,
group
)
val data = message.calculateValidationDataForGroup(
sequenceId = sequenceId,
random = Random.nextInt().absoluteValue,
group
)
val response =
MultiMsg.ApplyUp.createForGroupLongMessage(
buType = if (isLong) 1 else 2,
client = bot.client,
messageData = data,
dstUin = Mirai.calculateGroupUinByGroupCode(groupCode)
).sendAndExpect<MultiMsg.ApplyUp.Response>()
val response = network.run {
MultiMsg.ApplyUp.createForGroup(
buType = if (isLong) 1 else 2,
client = bot.client,
messageData = data,
dstUin = Mirai.calculateGroupUinByGroupCode(groupCode)
).sendAndExpect<MultiMsg.ApplyUp.Response>()
}
val resId: String
when (response) {
is MultiMsg.ApplyUp.Response.MessageTooLarge ->
error(
"Internal error: message is too large, but this should be handled before sending. "
)
is MultiMsg.ApplyUp.Response.RequireUpload -> {
resId = response.proto.msgResid
val body = LongMsg.ReqBody(
subcmd = 1,
platformType = 9,
termType = 5,
msgUpReq = listOf(
LongMsg.MsgUpReq(
msgType = 3, // group
dstUin = Mirai.calculateGroupUinByGroupCode(groupCode),
msgId = 0,
msgUkey = response.proto.msgUkey,
needCache = 0,
storeType = 2,
msgContent = data.data
)
)
).toByteArray(LongMsg.ReqBody.serializer())
HighwayHelper.uploadImageToServers(
bot,
response.proto.uint32UpIp.zip(response.proto.uint32UpPort),
response.proto.msgSig,
body.toExternalResource(null),
"group long message",
27
)
}
}
if (isLong) {
group.sendMessage(
RichMessage.longMessage(
brief = message.joinToString(limit = 27) { it.messageChain.contentToString() },
resId = resId,
timeSeconds = time
)
val resId: String
when (response) {
is MultiMsg.ApplyUp.Response.MessageTooLarge ->
error(
"Internal error: message is too large, but this should be handled before sending. "
)
} else {
checkNotNull(forwardMessage) { "Internal error: forwardMessage is null when sending forward" }
group.sendMessage(
RichMessage.forwardMessage(
resId = resId,
timeSeconds = time,
// preview = message.take(5).joinToString {
// """
// <title size="26" color="#777777" maxLines="2" lineSpace="12">${it.message.asMessageChain().joinToString(limit = 10)}</title>
// """.trimIndent()
// },
forwardMessage = forwardMessage,
is MultiMsg.ApplyUp.Response.RequireUpload -> {
resId = response.proto.msgResid
val body = LongMsg.ReqBody(
subcmd = 1,
platformType = 9,
termType = 5,
msgUpReq = listOf(
LongMsg.MsgUpReq(
msgType = 3, // group
dstUin = Mirai.calculateGroupUinByGroupCode(groupCode),
msgId = 0,
msgUkey = response.proto.msgUkey,
needCache = 0,
storeType = 2,
msgContent = data.data
)
)
).toByteArray(LongMsg.ReqBody.serializer())
Highway.uploadResource(
bot,
response.proto.uint32UpIp.zip(response.proto.uint32UpPort),
response.proto.msgSig,
body.toExternalResource(null),
when (isLong) {
true -> "group long message"
false -> "group forward message"
},
27
)
}
}
return resId
}

View File

@ -131,7 +131,7 @@ internal class QQAndroidBot constructor(
internal val EMPTY_BYTE_ARRAY = ByteArray(0)
internal fun RichMessage.Key.longMessage(brief: String, resId: String, timeSeconds: Long): RichMessage {
internal fun RichMessage.Key.longMessage(brief: String, resId: String, timeSeconds: Long): LongMessageInternal {
val limited: String = if (brief.length > 30) {
brief.take(30) + ""
} else {
@ -154,7 +154,7 @@ internal fun RichMessage.Key.longMessage(brief: String, resId: String, timeSecon
</msg>
""".trimIndent()
return LongMessage(template, resId)
return LongMessageInternal(template, resId)
}

View File

@ -18,15 +18,11 @@ import net.mamoe.mirai.data.GroupInfo
import net.mamoe.mirai.data.MemberInfo
import net.mamoe.mirai.event.broadcast
import net.mamoe.mirai.event.events.*
import net.mamoe.mirai.internal.MiraiImpl
import net.mamoe.mirai.internal.QQAndroidBot
import net.mamoe.mirai.internal.message.*
import net.mamoe.mirai.internal.message.firstIsInstanceOrNull
import net.mamoe.mirai.internal.network.QQAndroidBotNetworkHandler
import net.mamoe.mirai.internal.network.highway.HighwayHelper
import net.mamoe.mirai.internal.network.highway.Highway
import net.mamoe.mirai.internal.network.protocol.packet.chat.image.ImgStore
import net.mamoe.mirai.internal.network.protocol.packet.chat.receive.MessageSvcPbSendMsg
import net.mamoe.mirai.internal.network.protocol.packet.chat.receive.createToGroup
import net.mamoe.mirai.internal.network.protocol.packet.chat.voice.PttStore
import net.mamoe.mirai.internal.network.protocol.packet.chat.voice.voiceCodec
import net.mamoe.mirai.internal.network.protocol.packet.list.ProfileService
@ -118,142 +114,13 @@ internal class GroupImpl(
require(!message.isContentEmpty()) { "message is empty" }
check(!isBotMuted) { throw BotIsBeingMutedException(this) }
return sendMessageImpl(message, false).also {
logMessageSent(message)
val chain = broadcastGroupMessagePreSendEvent(message)
return sendMessageImpl(message, chain, false).also {
logMessageSent(chain)
}
}
private suspend fun sendMessageImpl(message: Message, isForward: Boolean): MessageReceipt<Group> {
if (message is MessageChain) {
if (message.anyIsInstance<ForwardMessage>()) {
return sendMessageImpl(message.singleOrNull() ?: error("ForwardMessage must be standalone"), true)
}
}
val forward = message as? ForwardMessage ?: message.castOrNull<MessageChain>()?.findIsInstance<ForwardMessage>()
if (forward != null) {
check(forward.nodeList.size < 200) {
throw MessageTooLargeException(
this, forward, forward,
"ForwardMessage allows up to 200 nodes, but found ${forward.nodeList.size}"
)
}
return MiraiImpl.lowLevelSendGroupLongOrForwardMessage(bot, this.id, forward.nodeList, false, forward)
}
val isLongOrForward = message is LongMessage || message is ForwardMessageInternal
val msg: MessageChain = if (!isLongOrForward) {
val chain = kotlin.runCatching {
GroupMessagePreSendEvent(this, message).broadcast()
}.onSuccess {
check(!it.isCancelled) {
throw EventCancelledException("cancelled by GroupMessagePreSendEvent")
}
}.getOrElse {
throw EventCancelledException("exception thrown when broadcasting GroupMessagePreSendEvent", it)
}.message.toMessageChain()
@Suppress("VARIABLE_WITH_REDUNDANT_INITIALIZER")
var length = 0
@Suppress("VARIABLE_WITH_REDUNDANT_INITIALIZER") // stupid compiler
var imageCnt = 0
chain.verityLength(message, this, lengthCallback = {
length = it
}, imageCntCallback = {
imageCnt = it
})
if (length > 702 || imageCnt > 1) { // 阈值为700左右限制到3的倍数
return MiraiImpl.lowLevelSendGroupLongOrForwardMessage(
bot,
this.id,
listOf(
ForwardMessage.Node(
senderId = bot.id,
time = currentTimeSeconds().toInt(),
messageChain = chain,
senderName = bot.nick
)
),
true, null
)
}
chain
} else message.toMessageChain()
msg.firstIsInstanceOrNull<QuoteReply>()?.source?.ensureSequenceIdAvailable()
msg.filterIsInstance<FriendImage>().forEach { image ->
bot.network.run {
ImgStore.GroupPicUp(
bot.client,
uin = bot.id,
groupCode = id,
md5 = image.md5,
size = if (image is OnlineFriendImageImpl) image.delegate.fileLen else 0
).sendAndExpect<ImgStore.GroupPicUp.Response>()
}
}
val result = bot.network.runCatching sendMsg@{
val source: OnlineMessageSourceToGroupImpl
MessageSvcPbSendMsg.createToGroup(
bot.client,
this@GroupImpl,
msg,
isForward
) {
source = it
}.sendAndExpect<MessageSvcPbSendMsg.Response>().let {
if (!isLongOrForward && it is MessageSvcPbSendMsg.Response.MessageTooLarge) {
return@sendMsg MiraiImpl.lowLevelSendGroupLongOrForwardMessage(
bot,
this@GroupImpl.id,
listOf(
ForwardMessage.Node(
senderId = bot.id,
time = currentTimeSeconds().toInt(),
messageChain = msg,
senderName = bot.nick
)
),
true, null
)
}
check(it is MessageSvcPbSendMsg.Response.SUCCESS) {
"Send group message failed: $it"
}
}
try {
source.ensureSequenceIdAvailable()
} catch (e: Exception) {
bot.network.logger.warning {
"Timeout awaiting sequenceId for group message(${
message.contentToString()
.take(10)
}). Some features may not work properly"
}
bot.network.logger.warning(e)
}
MessageReceipt(source, this@GroupImpl)
}
result.fold(
onSuccess = {
GroupMessagePostSendEvent(this, msg, null, it)
},
onFailure = {
GroupMessagePostSendEvent(this, msg, it, null)
}
).broadcast()
return result.getOrThrow()
}
@OptIn(ExperimentalTime::class)
override suspend fun uploadImage(resource: ExternalResource): Image {
if (BeforeImageUploadEvent(this, resource).broadcast().isCancelled) {
@ -280,7 +147,7 @@ internal class GroupImpl(
.also { ImageUploadEvent.Succeed(this@GroupImpl, resource, it).broadcast() }
}
is ImgStore.GroupPicUp.Response.RequireUpload -> {
HighwayHelper.uploadImageToServers(
Highway.uploadResource(
bot,
response.uploadIpList.zip(response.uploadPortList),
response.uKey,
@ -304,7 +171,7 @@ internal class GroupImpl(
val response: PttStore.GroupPttUp.Response.RequireUpload =
PttStore.GroupPttUp(bot.client, bot.id, id, resource).sendAndExpect()
HighwayHelper.uploadPttToServers(
Highway.uploadPttToServers(
bot,
response.uploadIpList.zip(response.uploadPortList),
resource,

View File

@ -0,0 +1,194 @@
/*
* Copyright 2020 Mamoe Technologies and contributors.
*
* 此源代码的使用受 GNU AFFERO GENERAL PUBLIC LICENSE version 3 许可证的约束, 可以在以下链接找到该许可证.
* Use of this source code is governed by the GNU AGPLv3 license that can be found through the following link.
*
* https://github.com/mamoe/mirai/blob/master/LICENSE
*/
package net.mamoe.mirai.internal.contact
import net.mamoe.mirai.contact.Contact
import net.mamoe.mirai.contact.Group
import net.mamoe.mirai.contact.MessageTooLargeException
import net.mamoe.mirai.event.broadcast
import net.mamoe.mirai.event.events.EventCancelledException
import net.mamoe.mirai.event.events.GroupMessagePostSendEvent
import net.mamoe.mirai.event.events.GroupMessagePreSendEvent
import net.mamoe.mirai.internal.MiraiImpl
import net.mamoe.mirai.internal.forwardMessage
import net.mamoe.mirai.internal.longMessage
import net.mamoe.mirai.internal.message.*
import net.mamoe.mirai.internal.network.protocol.packet.chat.image.ImgStore
import net.mamoe.mirai.internal.network.protocol.packet.chat.receive.MessageSvcPbSendMsg
import net.mamoe.mirai.internal.network.protocol.packet.chat.receive.createToGroup
import net.mamoe.mirai.message.MessageReceipt
import net.mamoe.mirai.message.data.*
import net.mamoe.mirai.utils.currentTimeSeconds
/**
* Might be recalled with [transformedMessage] `is` [LongMessageInternal] if length estimation failed ([sendMessagePacket])
*/
internal suspend fun GroupImpl.sendMessageImpl(
originalMessage: Message,
transformedMessage: Message,
forceAsLongMessage: Boolean,
): MessageReceipt<Group> {
val chain = transformedMessage
.transformSpecialMessages(this)
.convertToLongMessageIfNeeded(originalMessage, forceAsLongMessage, this)
chain.findIsInstance<QuoteReply>()?.source?.ensureSequenceIdAvailable()
chain.asSequence().filterIsInstance<FriendImage>().forEach { image ->
updateFriendImageForGroupMessage(image)
}
return sendMessagePacket(
originalMessage,
chain,
allowResendAsLongMessage = transformedMessage.takeSingleContent<LongMessageInternal>() == null
)
}
/**
* Called only in 'public' apis.
*/
internal suspend fun GroupImpl.broadcastGroupMessagePreSendEvent(message: Message): MessageChain {
return kotlin.runCatching {
GroupMessagePreSendEvent(this, message).broadcast()
}.onSuccess {
check(!it.isCancelled) {
throw EventCancelledException("cancelled by GroupMessagePreSendEvent")
}
}.getOrElse {
throw EventCancelledException("exception thrown when broadcasting GroupMessagePreSendEvent", it)
}.message.toMessageChain()
}
/**
* - [ForwardMessage] -> [ForwardMessageInternal] (by uploading through highway)
* - ... any others for future
*/
private suspend fun Message.transformSpecialMessages(contact: Contact): MessageChain {
return takeSingleContent<ForwardMessage>()?.let { forward ->
check(forward.nodeList.size <= 200) {
throw MessageTooLargeException(
contact, forward, forward,
"ForwardMessage allows up to 200 nodes, but found ${forward.nodeList.size}"
)
}
val resId = MiraiImpl.uploadGroupMessageHighway(contact.bot, contact.id, forward.nodeList, false)
RichMessage.forwardMessage(
resId = resId,
timeSeconds = currentTimeSeconds(),
forwardMessage = forward,
)
}?.toMessageChain() ?: toMessageChain()
}
/**
* Final process
*/
private suspend fun GroupImpl.sendMessagePacket(
originalMessage: Message,
finalMessage: MessageChain,
allowResendAsLongMessage: Boolean,
): MessageReceipt<Group> {
val group = this
val result = bot.network.runCatching sendMsg@{
val source: OnlineMessageSourceToGroupImpl
MessageSvcPbSendMsg.createToGroup(bot.client, group, finalMessage) {
source = it
}.sendAndExpect<MessageSvcPbSendMsg.Response>().let { resp ->
if (resp is MessageSvcPbSendMsg.Response.MessageTooLarge) {
if (allowResendAsLongMessage) {
return@sendMsg sendMessageImpl(originalMessage, finalMessage, true)
} else throw MessageTooLargeException(
group,
originalMessage,
finalMessage,
"Message '${finalMessage.content.take(10)}' is too large."
)
}
check(resp is MessageSvcPbSendMsg.Response.SUCCESS) {
"Send group message failed: $resp"
}
}
try {
source.ensureSequenceIdAvailable()
} catch (e: Exception) {
bot.network.logger.warning(
"Timeout awaiting sequenceId for group message(${finalMessage.content.take(10)}). Some features may not work properly",
e
)
}
MessageReceipt(source, group)
}
GroupMessagePostSendEvent(this, finalMessage, result.exceptionOrNull(), result.getOrNull()).broadcast()
return result.getOrThrow()
}
private suspend fun GroupImpl.uploadGroupLongMessageHighway(
chain: MessageChain
) = MiraiImpl.uploadGroupMessageHighway(
bot, this.id,
listOf(
ForwardMessage.Node(
senderId = bot.id,
time = currentTimeSeconds().toInt(),
messageChain = chain,
senderName = bot.nick
)
),
true
)
private suspend fun MessageChain.convertToLongMessageIfNeeded(
originalMessage: Message,
forceAsLongMessage: Boolean,
groupImpl: GroupImpl,
): MessageChain {
if (forceAsLongMessage || this.shouldSendAsLongMessage(originalMessage, groupImpl)) {
val resId = groupImpl.uploadGroupLongMessageHighway(this)
return this + RichMessage.longMessage(
brief = takeContent(27),
resId = resId,
timeSeconds = currentTimeSeconds()
) // LongMessageInternal replaces all contents and preserves metadata
}
return this
}
/**
* Ensures server holds the cache
*/
private suspend fun GroupImpl.updateFriendImageForGroupMessage(image: FriendImage) {
bot.network.run {
ImgStore.GroupPicUp(
bot.client,
uin = bot.id,
groupCode = id,
md5 = image.md5,
size = if (image is OnlineFriendImageImpl) image.delegate.fileLen else 0
).sendAndExpect<ImgStore.GroupPicUp.Response>()
}
}
private fun MessageChain.shouldSendAsLongMessage(originalMessage: Message, target: Contact): Boolean {
val length = verityLength(originalMessage, target)
return length > 700 || countImages() > 1
}

View File

@ -16,6 +16,7 @@ import net.mamoe.mirai.contact.*
import net.mamoe.mirai.event.broadcast
import net.mamoe.mirai.event.events.*
import net.mamoe.mirai.internal.asQQAndroidBot
import net.mamoe.mirai.internal.message.LongMessageInternal
import net.mamoe.mirai.internal.message.OnlineMessageSourceToFriendImpl
import net.mamoe.mirai.internal.message.OnlineMessageSourceToStrangerImpl
import net.mamoe.mirai.internal.message.ensureSequenceIdAvailable
@ -26,6 +27,7 @@ import net.mamoe.mirai.internal.utils.estimateLength
import net.mamoe.mirai.message.*
import net.mamoe.mirai.message.data.*
import net.mamoe.mirai.utils.cast
import net.mamoe.mirai.utils.castOrNull
import net.mamoe.mirai.utils.verbose
import kotlin.contracts.InvocationKind
import kotlin.contracts.contract
@ -52,7 +54,7 @@ internal suspend fun <T : User> Friend.sendMessageImpl(
}.getOrElse {
throw EventCancelledException("exception thrown when broadcasting FriendMessagePreSendEvent", it)
}.message.toMessageChain()
chain.verityLength(message, this, {}, {})
chain.verityLength(message, this)
chain.firstIsInstanceOrNull<QuoteReply>()?.source?.ensureSequenceIdAvailable()
@ -104,7 +106,7 @@ internal suspend fun <T : User> Stranger.sendMessageImpl(
}.getOrElse {
throw EventCancelledException("exception thrown when broadcasting StrangerMessagePreSendEvent", it)
}.message.toMessageChain()
chain.verityLength(message, this, {}, {})
chain.verityLength(message, this)
chain.firstIsInstanceOrNull<QuoteReply>()?.source?.ensureSequenceIdAvailable()
@ -138,32 +140,27 @@ internal suspend fun <T : User> Stranger.sendMessageImpl(
}
internal fun Contact.logMessageSent(message: Message) {
if (message !is LongMessage) {
if (message !is LongMessageInternal) {
bot.logger.verbose("$this <- $message".replaceMagicCodes())
}
}
internal inline fun MessageChain.verityLength(
message: Message, target: Contact,
lengthCallback: (Int) -> Unit,
imageCntCallback: (Int) -> Unit
) {
contract {
callsInPlace(lengthCallback, InvocationKind.EXACTLY_ONCE)
callsInPlace(imageCntCallback, InvocationKind.EXACTLY_ONCE)
}
internal fun MessageChain.countImages(): Int = this.count { it is Image }
internal fun MessageChain.verityLength(
originalMessage: Message, target: Contact,
): Int {
val chain = this
val length = estimateLength(target, 15001)
lengthCallback(length)
if (length > 15000 || count { it is Image }.apply { imageCntCallback(this) } > 50) {
if (length > 15000 || countImages() > 50) {
throw MessageTooLargeException(
target, message, this,
target, originalMessage, this,
"message(${
chain.joinToString("", limit = 10)
}) is too large. Allow up to 50 images or 5000 chars"
)
}
return length
}
@Suppress("RemoveRedundantQualifierName") // compiler bug
@ -214,3 +211,11 @@ internal fun String.applyCharMapping() = buildString(capacity = this.length) {
internal fun String.replaceMagicCodes(): String = this
.applyCharMapping()
internal fun Message.takeContent(length: Int): String =
this.toMessageChain().joinToString("", limit = length) { it.content }
internal inline fun <reified T : MessageContent> Message.takeSingleContent(): T? {
return this as? T ?: this.castOrNull<MessageChain>()?.findIsInstance()
}

View File

@ -0,0 +1,32 @@
/*
* 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 net.mamoe.mirai.message.data.AbstractPolymorphicMessageKey
import net.mamoe.mirai.message.data.AbstractServiceMessage
import net.mamoe.mirai.message.data.ServiceMessage
import net.mamoe.mirai.utils.safeCast
// internal runtime value, not serializable
internal data class LongMessageInternal internal constructor(override val content: String, val resId: String) :
AbstractServiceMessage() {
override val serviceId: Int get() = 35
companion object Key :
AbstractPolymorphicMessageKey<ServiceMessage, LongMessageInternal>(ServiceMessage, { it.safeCast() })
}
// internal runtime value, not serializable
internal data class ForwardMessageInternal(override val content: String) : AbstractServiceMessage() {
override val serviceId: Int get() = 35
companion object Key :
AbstractPolymorphicMessageKey<ServiceMessage, ForwardMessageInternal>(ServiceMessage, { it.safeCast() })
}

View File

@ -72,7 +72,7 @@ internal fun MessageChain.toRichTextElems(
)
transformOneMessage(UNSUPPORTED_MERGED_MESSAGE_PLAIN)
}
is LongMessage -> {
is LongMessageInternal -> {
check(longTextResId == null) { "There must be no more than one LongMessage element in the message chain" }
elements.add(
ImMsgBody.Elem(
@ -375,7 +375,7 @@ private fun MessageChain.cleanupRubbishMessageElements(): MessageChain {
return buildMessageChain(initialSize = this.count()) {
this@cleanupRubbishMessageElements.forEach { element ->
@Suppress("INVISIBLE_MEMBER", "INVISIBLE_REFERENCE")
if (last is LongMessage && element is PlainText) {
if (last is LongMessageInternal && element is PlainText) {
if (element == UNSUPPORTED_MERGED_MESSAGE_PLAIN) {
previousLast = last
last = element
@ -535,7 +535,7 @@ internal fun List<ImMsgBody.Elem>.joinToMessageChain(
1 -> @Suppress("DEPRECATION_ERROR")
list.add(SimpleServiceMessage(1, content))
/**
* [LongMessage], [ForwardMessage]
* [LongMessageInternal], [ForwardMessage]
*/
35 -> {
val resId = this.firstIsInstanceOrNull<ImMsgBody.GeneralFlags>()?.longTextResid

View File

@ -0,0 +1,39 @@
/*
* 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.network.highway
import kotlinx.io.core.Closeable
import net.mamoe.mirai.utils.runBIO
import net.mamoe.mirai.utils.withUse
import java.io.InputStream
internal class ChunkedFlowSession<T>(
private val input: InputStream,
private val buffer: ByteArray,
private val mapper: (buffer: ByteArray, size: Int, offset: Long) -> T
) : Closeable {
override fun close() {
input.close()
}
private var offset = 0L
@Suppress("BlockingMethodInNonBlockingContext")
internal suspend inline fun useAll(crossinline block: suspend (T) -> Unit) = withUse {
runBIO {
while (true) {
val size = input.read(buffer)
if (size == -1) return@runBIO
block(mapper(buffer, size, offset))
offset += size
}
}
}
}

View File

@ -15,7 +15,6 @@ import io.ktor.http.*
import io.ktor.http.content.*
import io.ktor.utils.io.*
import io.ktor.utils.io.jvm.javaio.*
import kotlinx.coroutines.InternalCoroutinesApi
import kotlinx.coroutines.delay
import kotlinx.coroutines.isActive
import kotlinx.coroutines.withTimeoutOrNull
@ -34,7 +33,6 @@ import net.mamoe.mirai.internal.utils.toIpV4AddressString
import net.mamoe.mirai.utils.*
import java.io.InputStream
import kotlin.math.roundToInt
import kotlin.time.ExperimentalTime
import kotlin.time.measureTime
@ -83,10 +81,9 @@ internal suspend fun HttpClient.postImage(
} == HttpStatusCode.OK
internal object HighwayHelper {
@Suppress("INVISIBLE_REFERENCE", "INVISIBLE_MEMBER")
@OptIn(ExperimentalTime::class)
suspend fun uploadImageToServers(
internal object Highway {
suspend fun uploadResource(
bot: QQAndroidBot,
servers: List<Pair<Int, Int>>,
uKey: ByteArray,
@ -104,7 +101,7 @@ internal object HighwayHelper {
}
val time = measureTime {
uploadImage(
uploadResourceImpl(
client = bot.client,
serverIp = ip,
serverPort = port,
@ -120,9 +117,7 @@ internal object HighwayHelper {
}
}
@Suppress("INVISIBLE_REFERENCE", "INVISIBLE_MEMBER")
@OptIn(InternalCoroutinesApi::class)
internal suspend fun uploadImage(
private suspend fun uploadResourceImpl(
client: QQAndroidClient,
serverIp: String,
serverPort: Int,
@ -213,30 +208,6 @@ internal object HighwayHelper {
}
}
internal class ChunkedFlowSession<T>(
private val input: InputStream,
private val buffer: ByteArray,
private val mapper: (buffer: ByteArray, size: Int, offset: Long) -> T
) : Closeable {
override fun close() {
input.close()
}
private var offset = 0L
@Suppress("BlockingMethodInNonBlockingContext")
internal suspend inline fun useAll(crossinline block: suspend (T) -> Unit) = withUse {
runBIO {
while (true) {
val size = input.read(buffer)
if (size == -1) return@runBIO
block(mapper(buffer, size, offset))
offset += size
}
}
}
}
internal fun createImageDataPacketSequence(
// RequestDataTrans

View File

@ -102,7 +102,7 @@ internal class MultiMsg {
if (PacketLogger.isEnabled) {
return _miraiContentToString()
}
return "MultiMsg.ApplyUp.Response.RequireUpload(proto=$proto)"
return "MultiMsg.ApplyUp.Response.RequireUpload"
}
}
@ -110,7 +110,7 @@ internal class MultiMsg {
}
// captured from group
fun createForGroupLongMessage(
fun createForGroup(
buType: Int,
client: QQAndroidClient,
messageData: MessageValidationData,

View File

@ -295,7 +295,6 @@ internal object MessageSvcPbSendMsg : OutgoingPacketFactory<MessageSvcPbSendMsg.
client: QQAndroidClient,
targetGroup: Group,
message: MessageChain,
isForward: Boolean,
source: OnlineMessageSourceToGroupImpl
): OutgoingPacket = buildOutgoingUniPacket(client) {
///writeFully("0A 08 0A 06 08 89 FC A6 8C 0B 12 06 08 01 10 00 18 00 1A 1F 0A 1D 12 08 0A 06 0A 04 F0 9F 92 A9 12 11 AA 02 0E 88 01 00 9A 01 08 78 00 F8 01 00 C8 02 00 20 9B 7A 28 F4 CA 9B B8 03 32 34 08 92 C2 C4 F1 05 10 92 C2 C4 F1 05 18 E6 ED B9 C3 02 20 89 FE BE A4 06 28 89 84 F9 A2 06 48 DE 8C EA E5 0E 58 D9 BD BB A0 09 60 1D 68 92 C2 C4 F1 05 70 00 40 01".hexToBytes())
@ -333,9 +332,10 @@ internal object MessageSvcPbSendMsg : OutgoingPacketFactory<MessageSvcPbSendMsg.
msgRand = source.internalIds.single(),
syncCookie = EMPTY_BYTE_ARRAY,
msgVia = 1,
msgCtrl = if (isForward) MsgCtrl.MsgCtrl(
msgFlag = 4
) else null
msgCtrl =
if (message[ForwardMessageInternal] != null)
MsgCtrl.MsgCtrl(msgFlag = 4)
else null
)
)
}
@ -429,7 +429,6 @@ internal inline fun MessageSvcPbSendMsg.createToGroup(
client: QQAndroidClient,
group: Group,
message: MessageChain,
isForward: Boolean,
crossinline sourceCallback: (OnlineMessageSourceToGroupImpl) -> Unit
): OutgoingPacket {
contract {
@ -457,7 +456,6 @@ internal inline fun MessageSvcPbSendMsg.createToGroup(
client,
group,
message,
isForward,
source
)
}