mirror of
https://github.com/mamoe/mirai.git
synced 2025-02-12 18:50:20 +08:00
Rewrite Group sendMessageImpl, improve current Highway impls, for music share #889 #682 and further friend message highway #194, #577
This commit is contained in:
parent
66ae897f20
commit
d60006376c
@ -265,17 +265,4 @@ public class XmlMessageBuilder(
|
|||||||
this.builder.append("<picture cover='$coverUrl'/>")
|
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
|
|
||||||
}
|
}
|
@ -24,7 +24,7 @@ import net.mamoe.mirai.event.broadcast
|
|||||||
import net.mamoe.mirai.event.events.*
|
import net.mamoe.mirai.event.events.*
|
||||||
import net.mamoe.mirai.internal.contact.*
|
import net.mamoe.mirai.internal.contact.*
|
||||||
import net.mamoe.mirai.internal.message.*
|
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.jce.SvcDevLoginInfo
|
||||||
import net.mamoe.mirai.internal.network.protocol.data.proto.LongMsg
|
import net.mamoe.mirai.internal.network.protocol.data.proto.LongMsg
|
||||||
import net.mamoe.mirai.internal.network.protocol.packet.chat.*
|
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.list.FriendList
|
||||||
import net.mamoe.mirai.internal.network.protocol.packet.login.StatSvc
|
import net.mamoe.mirai.internal.network.protocol.packet.login.StatSvc
|
||||||
import net.mamoe.mirai.internal.utils.io.serialization.toByteArray
|
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.MessageSerializers
|
||||||
import net.mamoe.mirai.message.action.Nudge
|
import net.mamoe.mirai.message.action.Nudge
|
||||||
import net.mamoe.mirai.message.data.*
|
import net.mamoe.mirai.message.data.*
|
||||||
@ -690,102 +689,76 @@ internal open class MiraiImpl : IMirai, LowLevelApiAccessor {
|
|||||||
return jsonText?.let { json.decodeFromString(GroupHonorListData.serializer(), it) }
|
return jsonText?.let { json.decodeFromString(GroupHonorListData.serializer(), it) }
|
||||||
}
|
}
|
||||||
|
|
||||||
@JvmSynthetic
|
internal suspend fun uploadGroupMessageHighway(
|
||||||
@LowLevelApi
|
|
||||||
@MiraiExperimentalApi
|
|
||||||
@Suppress("INVISIBLE_REFERENCE", "INVISIBLE_MEMBER")
|
|
||||||
internal suspend fun lowLevelSendGroupLongOrForwardMessage(
|
|
||||||
bot: Bot,
|
bot: Bot,
|
||||||
groupCode: Long,
|
groupCode: Long,
|
||||||
message: Collection<ForwardMessage.INode>,
|
message: Collection<ForwardMessage.INode>,
|
||||||
isLong: Boolean,
|
isLong: Boolean,
|
||||||
forwardMessage: ForwardMessage?
|
): String = with(bot.asQQAndroidBot()) {
|
||||||
): MessageReceipt<Group> = with(bot.asQQAndroidBot()) {
|
|
||||||
message.forEach {
|
message.forEach {
|
||||||
it.messageChain.ensureSequenceIdAvailable()
|
it.messageChain.ensureSequenceIdAvailable()
|
||||||
}
|
}
|
||||||
|
|
||||||
val group = getGroupOrFail(groupCode)
|
val group = getGroupOrFail(groupCode)
|
||||||
|
|
||||||
val time = currentTimeSeconds()
|
|
||||||
val sequenceId = client.atomicNextMessageSequenceId()
|
val sequenceId = client.atomicNextMessageSequenceId()
|
||||||
|
|
||||||
network.run {
|
val data = message.calculateValidationDataForGroup(
|
||||||
val data = message.calculateValidationDataForGroup(
|
sequenceId = sequenceId,
|
||||||
sequenceId = sequenceId,
|
random = Random.nextInt().absoluteValue,
|
||||||
random = Random.nextInt().absoluteValue,
|
group
|
||||||
group
|
)
|
||||||
)
|
|
||||||
|
|
||||||
val response =
|
val response = network.run {
|
||||||
MultiMsg.ApplyUp.createForGroupLongMessage(
|
MultiMsg.ApplyUp.createForGroup(
|
||||||
buType = if (isLong) 1 else 2,
|
buType = if (isLong) 1 else 2,
|
||||||
client = bot.client,
|
client = bot.client,
|
||||||
messageData = data,
|
messageData = data,
|
||||||
dstUin = Mirai.calculateGroupUinByGroupCode(groupCode)
|
dstUin = Mirai.calculateGroupUinByGroupCode(groupCode)
|
||||||
).sendAndExpect<MultiMsg.ApplyUp.Response>()
|
).sendAndExpect<MultiMsg.ApplyUp.Response>()
|
||||||
|
}
|
||||||
|
|
||||||
val resId: String
|
val resId: String
|
||||||
when (response) {
|
when (response) {
|
||||||
is MultiMsg.ApplyUp.Response.MessageTooLarge ->
|
is MultiMsg.ApplyUp.Response.MessageTooLarge ->
|
||||||
error(
|
error(
|
||||||
"Internal error: message is too large, but this should be handled before sending. "
|
"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
|
|
||||||
)
|
|
||||||
)
|
)
|
||||||
} else {
|
is MultiMsg.ApplyUp.Response.RequireUpload -> {
|
||||||
checkNotNull(forwardMessage) { "Internal error: forwardMessage is null when sending forward" }
|
resId = response.proto.msgResid
|
||||||
group.sendMessage(
|
|
||||||
RichMessage.forwardMessage(
|
val body = LongMsg.ReqBody(
|
||||||
resId = resId,
|
subcmd = 1,
|
||||||
timeSeconds = time,
|
platformType = 9,
|
||||||
// preview = message.take(5).joinToString {
|
termType = 5,
|
||||||
// """
|
msgUpReq = listOf(
|
||||||
// <title size="26" color="#777777" maxLines="2" lineSpace="12">${it.message.asMessageChain().joinToString(limit = 10)}</title>
|
LongMsg.MsgUpReq(
|
||||||
// """.trimIndent()
|
msgType = 3, // group
|
||||||
// },
|
dstUin = Mirai.calculateGroupUinByGroupCode(groupCode),
|
||||||
forwardMessage = forwardMessage,
|
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
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@ -131,7 +131,7 @@ internal class QQAndroidBot constructor(
|
|||||||
|
|
||||||
internal val EMPTY_BYTE_ARRAY = ByteArray(0)
|
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) {
|
val limited: String = if (brief.length > 30) {
|
||||||
brief.take(30) + "…"
|
brief.take(30) + "…"
|
||||||
} else {
|
} else {
|
||||||
@ -154,7 +154,7 @@ internal fun RichMessage.Key.longMessage(brief: String, resId: String, timeSecon
|
|||||||
</msg>
|
</msg>
|
||||||
""".trimIndent()
|
""".trimIndent()
|
||||||
|
|
||||||
return LongMessage(template, resId)
|
return LongMessageInternal(template, resId)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@ -18,15 +18,11 @@ import net.mamoe.mirai.data.GroupInfo
|
|||||||
import net.mamoe.mirai.data.MemberInfo
|
import net.mamoe.mirai.data.MemberInfo
|
||||||
import net.mamoe.mirai.event.broadcast
|
import net.mamoe.mirai.event.broadcast
|
||||||
import net.mamoe.mirai.event.events.*
|
import net.mamoe.mirai.event.events.*
|
||||||
import net.mamoe.mirai.internal.MiraiImpl
|
|
||||||
import net.mamoe.mirai.internal.QQAndroidBot
|
import net.mamoe.mirai.internal.QQAndroidBot
|
||||||
import net.mamoe.mirai.internal.message.*
|
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.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.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.PttStore
|
||||||
import net.mamoe.mirai.internal.network.protocol.packet.chat.voice.voiceCodec
|
import net.mamoe.mirai.internal.network.protocol.packet.chat.voice.voiceCodec
|
||||||
import net.mamoe.mirai.internal.network.protocol.packet.list.ProfileService
|
import net.mamoe.mirai.internal.network.protocol.packet.list.ProfileService
|
||||||
@ -118,142 +114,13 @@ internal class GroupImpl(
|
|||||||
require(!message.isContentEmpty()) { "message is empty" }
|
require(!message.isContentEmpty()) { "message is empty" }
|
||||||
check(!isBotMuted) { throw BotIsBeingMutedException(this) }
|
check(!isBotMuted) { throw BotIsBeingMutedException(this) }
|
||||||
|
|
||||||
return sendMessageImpl(message, false).also {
|
val chain = broadcastGroupMessagePreSendEvent(message)
|
||||||
logMessageSent(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)
|
@OptIn(ExperimentalTime::class)
|
||||||
override suspend fun uploadImage(resource: ExternalResource): Image {
|
override suspend fun uploadImage(resource: ExternalResource): Image {
|
||||||
if (BeforeImageUploadEvent(this, resource).broadcast().isCancelled) {
|
if (BeforeImageUploadEvent(this, resource).broadcast().isCancelled) {
|
||||||
@ -280,7 +147,7 @@ internal class GroupImpl(
|
|||||||
.also { ImageUploadEvent.Succeed(this@GroupImpl, resource, it).broadcast() }
|
.also { ImageUploadEvent.Succeed(this@GroupImpl, resource, it).broadcast() }
|
||||||
}
|
}
|
||||||
is ImgStore.GroupPicUp.Response.RequireUpload -> {
|
is ImgStore.GroupPicUp.Response.RequireUpload -> {
|
||||||
HighwayHelper.uploadImageToServers(
|
Highway.uploadResource(
|
||||||
bot,
|
bot,
|
||||||
response.uploadIpList.zip(response.uploadPortList),
|
response.uploadIpList.zip(response.uploadPortList),
|
||||||
response.uKey,
|
response.uKey,
|
||||||
@ -304,7 +171,7 @@ internal class GroupImpl(
|
|||||||
val response: PttStore.GroupPttUp.Response.RequireUpload =
|
val response: PttStore.GroupPttUp.Response.RequireUpload =
|
||||||
PttStore.GroupPttUp(bot.client, bot.id, id, resource).sendAndExpect()
|
PttStore.GroupPttUp(bot.client, bot.id, id, resource).sendAndExpect()
|
||||||
|
|
||||||
HighwayHelper.uploadPttToServers(
|
Highway.uploadPttToServers(
|
||||||
bot,
|
bot,
|
||||||
response.uploadIpList.zip(response.uploadPortList),
|
response.uploadIpList.zip(response.uploadPortList),
|
||||||
resource,
|
resource,
|
||||||
|
194
mirai-core/src/commonMain/kotlin/contact/GroupSendMessageImpl.kt
Normal file
194
mirai-core/src/commonMain/kotlin/contact/GroupSendMessageImpl.kt
Normal 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
|
||||||
|
}
|
@ -16,6 +16,7 @@ import net.mamoe.mirai.contact.*
|
|||||||
import net.mamoe.mirai.event.broadcast
|
import net.mamoe.mirai.event.broadcast
|
||||||
import net.mamoe.mirai.event.events.*
|
import net.mamoe.mirai.event.events.*
|
||||||
import net.mamoe.mirai.internal.asQQAndroidBot
|
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.OnlineMessageSourceToFriendImpl
|
||||||
import net.mamoe.mirai.internal.message.OnlineMessageSourceToStrangerImpl
|
import net.mamoe.mirai.internal.message.OnlineMessageSourceToStrangerImpl
|
||||||
import net.mamoe.mirai.internal.message.ensureSequenceIdAvailable
|
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.*
|
||||||
import net.mamoe.mirai.message.data.*
|
import net.mamoe.mirai.message.data.*
|
||||||
import net.mamoe.mirai.utils.cast
|
import net.mamoe.mirai.utils.cast
|
||||||
|
import net.mamoe.mirai.utils.castOrNull
|
||||||
import net.mamoe.mirai.utils.verbose
|
import net.mamoe.mirai.utils.verbose
|
||||||
import kotlin.contracts.InvocationKind
|
import kotlin.contracts.InvocationKind
|
||||||
import kotlin.contracts.contract
|
import kotlin.contracts.contract
|
||||||
@ -52,7 +54,7 @@ internal suspend fun <T : User> Friend.sendMessageImpl(
|
|||||||
}.getOrElse {
|
}.getOrElse {
|
||||||
throw EventCancelledException("exception thrown when broadcasting FriendMessagePreSendEvent", it)
|
throw EventCancelledException("exception thrown when broadcasting FriendMessagePreSendEvent", it)
|
||||||
}.message.toMessageChain()
|
}.message.toMessageChain()
|
||||||
chain.verityLength(message, this, {}, {})
|
chain.verityLength(message, this)
|
||||||
|
|
||||||
chain.firstIsInstanceOrNull<QuoteReply>()?.source?.ensureSequenceIdAvailable()
|
chain.firstIsInstanceOrNull<QuoteReply>()?.source?.ensureSequenceIdAvailable()
|
||||||
|
|
||||||
@ -104,7 +106,7 @@ internal suspend fun <T : User> Stranger.sendMessageImpl(
|
|||||||
}.getOrElse {
|
}.getOrElse {
|
||||||
throw EventCancelledException("exception thrown when broadcasting StrangerMessagePreSendEvent", it)
|
throw EventCancelledException("exception thrown when broadcasting StrangerMessagePreSendEvent", it)
|
||||||
}.message.toMessageChain()
|
}.message.toMessageChain()
|
||||||
chain.verityLength(message, this, {}, {})
|
chain.verityLength(message, this)
|
||||||
|
|
||||||
chain.firstIsInstanceOrNull<QuoteReply>()?.source?.ensureSequenceIdAvailable()
|
chain.firstIsInstanceOrNull<QuoteReply>()?.source?.ensureSequenceIdAvailable()
|
||||||
|
|
||||||
@ -138,32 +140,27 @@ internal suspend fun <T : User> Stranger.sendMessageImpl(
|
|||||||
}
|
}
|
||||||
|
|
||||||
internal fun Contact.logMessageSent(message: Message) {
|
internal fun Contact.logMessageSent(message: Message) {
|
||||||
if (message !is LongMessage) {
|
if (message !is LongMessageInternal) {
|
||||||
bot.logger.verbose("$this <- $message".replaceMagicCodes())
|
bot.logger.verbose("$this <- $message".replaceMagicCodes())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
internal inline fun MessageChain.verityLength(
|
internal fun MessageChain.countImages(): Int = this.count { it is Image }
|
||||||
message: Message, target: Contact,
|
|
||||||
lengthCallback: (Int) -> Unit,
|
|
||||||
imageCntCallback: (Int) -> Unit
|
|
||||||
) {
|
|
||||||
contract {
|
|
||||||
callsInPlace(lengthCallback, InvocationKind.EXACTLY_ONCE)
|
|
||||||
callsInPlace(imageCntCallback, InvocationKind.EXACTLY_ONCE)
|
|
||||||
}
|
|
||||||
|
|
||||||
|
internal fun MessageChain.verityLength(
|
||||||
|
originalMessage: Message, target: Contact,
|
||||||
|
): Int {
|
||||||
val chain = this
|
val chain = this
|
||||||
val length = estimateLength(target, 15001)
|
val length = estimateLength(target, 15001)
|
||||||
lengthCallback(length)
|
if (length > 15000 || countImages() > 50) {
|
||||||
if (length > 15000 || count { it is Image }.apply { imageCntCallback(this) } > 50) {
|
|
||||||
throw MessageTooLargeException(
|
throw MessageTooLargeException(
|
||||||
target, message, this,
|
target, originalMessage, this,
|
||||||
"message(${
|
"message(${
|
||||||
chain.joinToString("", limit = 10)
|
chain.joinToString("", limit = 10)
|
||||||
}) is too large. Allow up to 50 images or 5000 chars"
|
}) is too large. Allow up to 50 images or 5000 chars"
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
return length
|
||||||
}
|
}
|
||||||
|
|
||||||
@Suppress("RemoveRedundantQualifierName") // compiler bug
|
@Suppress("RemoveRedundantQualifierName") // compiler bug
|
||||||
@ -214,3 +211,11 @@ internal fun String.applyCharMapping() = buildString(capacity = this.length) {
|
|||||||
|
|
||||||
internal fun String.replaceMagicCodes(): String = this
|
internal fun String.replaceMagicCodes(): String = this
|
||||||
.applyCharMapping()
|
.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()
|
||||||
|
}
|
||||||
|
@ -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() })
|
||||||
|
}
|
@ -72,7 +72,7 @@ internal fun MessageChain.toRichTextElems(
|
|||||||
)
|
)
|
||||||
transformOneMessage(UNSUPPORTED_MERGED_MESSAGE_PLAIN)
|
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" }
|
check(longTextResId == null) { "There must be no more than one LongMessage element in the message chain" }
|
||||||
elements.add(
|
elements.add(
|
||||||
ImMsgBody.Elem(
|
ImMsgBody.Elem(
|
||||||
@ -375,7 +375,7 @@ private fun MessageChain.cleanupRubbishMessageElements(): MessageChain {
|
|||||||
return buildMessageChain(initialSize = this.count()) {
|
return buildMessageChain(initialSize = this.count()) {
|
||||||
this@cleanupRubbishMessageElements.forEach { element ->
|
this@cleanupRubbishMessageElements.forEach { element ->
|
||||||
@Suppress("INVISIBLE_MEMBER", "INVISIBLE_REFERENCE")
|
@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) {
|
if (element == UNSUPPORTED_MERGED_MESSAGE_PLAIN) {
|
||||||
previousLast = last
|
previousLast = last
|
||||||
last = element
|
last = element
|
||||||
@ -535,7 +535,7 @@ internal fun List<ImMsgBody.Elem>.joinToMessageChain(
|
|||||||
1 -> @Suppress("DEPRECATION_ERROR")
|
1 -> @Suppress("DEPRECATION_ERROR")
|
||||||
list.add(SimpleServiceMessage(1, content))
|
list.add(SimpleServiceMessage(1, content))
|
||||||
/**
|
/**
|
||||||
* [LongMessage], [ForwardMessage]
|
* [LongMessageInternal], [ForwardMessage]
|
||||||
*/
|
*/
|
||||||
35 -> {
|
35 -> {
|
||||||
val resId = this.firstIsInstanceOrNull<ImMsgBody.GeneralFlags>()?.longTextResid
|
val resId = this.firstIsInstanceOrNull<ImMsgBody.GeneralFlags>()?.longTextResid
|
||||||
|
@ -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
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -15,7 +15,6 @@ import io.ktor.http.*
|
|||||||
import io.ktor.http.content.*
|
import io.ktor.http.content.*
|
||||||
import io.ktor.utils.io.*
|
import io.ktor.utils.io.*
|
||||||
import io.ktor.utils.io.jvm.javaio.*
|
import io.ktor.utils.io.jvm.javaio.*
|
||||||
import kotlinx.coroutines.InternalCoroutinesApi
|
|
||||||
import kotlinx.coroutines.delay
|
import kotlinx.coroutines.delay
|
||||||
import kotlinx.coroutines.isActive
|
import kotlinx.coroutines.isActive
|
||||||
import kotlinx.coroutines.withTimeoutOrNull
|
import kotlinx.coroutines.withTimeoutOrNull
|
||||||
@ -34,7 +33,6 @@ import net.mamoe.mirai.internal.utils.toIpV4AddressString
|
|||||||
import net.mamoe.mirai.utils.*
|
import net.mamoe.mirai.utils.*
|
||||||
import java.io.InputStream
|
import java.io.InputStream
|
||||||
import kotlin.math.roundToInt
|
import kotlin.math.roundToInt
|
||||||
import kotlin.time.ExperimentalTime
|
|
||||||
import kotlin.time.measureTime
|
import kotlin.time.measureTime
|
||||||
|
|
||||||
|
|
||||||
@ -83,10 +81,9 @@ internal suspend fun HttpClient.postImage(
|
|||||||
} == HttpStatusCode.OK
|
} == HttpStatusCode.OK
|
||||||
|
|
||||||
|
|
||||||
internal object HighwayHelper {
|
internal object Highway {
|
||||||
@Suppress("INVISIBLE_REFERENCE", "INVISIBLE_MEMBER")
|
|
||||||
@OptIn(ExperimentalTime::class)
|
suspend fun uploadResource(
|
||||||
suspend fun uploadImageToServers(
|
|
||||||
bot: QQAndroidBot,
|
bot: QQAndroidBot,
|
||||||
servers: List<Pair<Int, Int>>,
|
servers: List<Pair<Int, Int>>,
|
||||||
uKey: ByteArray,
|
uKey: ByteArray,
|
||||||
@ -104,7 +101,7 @@ internal object HighwayHelper {
|
|||||||
}
|
}
|
||||||
|
|
||||||
val time = measureTime {
|
val time = measureTime {
|
||||||
uploadImage(
|
uploadResourceImpl(
|
||||||
client = bot.client,
|
client = bot.client,
|
||||||
serverIp = ip,
|
serverIp = ip,
|
||||||
serverPort = port,
|
serverPort = port,
|
||||||
@ -120,9 +117,7 @@ internal object HighwayHelper {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@Suppress("INVISIBLE_REFERENCE", "INVISIBLE_MEMBER")
|
private suspend fun uploadResourceImpl(
|
||||||
@OptIn(InternalCoroutinesApi::class)
|
|
||||||
internal suspend fun uploadImage(
|
|
||||||
client: QQAndroidClient,
|
client: QQAndroidClient,
|
||||||
serverIp: String,
|
serverIp: String,
|
||||||
serverPort: Int,
|
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(
|
internal fun createImageDataPacketSequence(
|
||||||
// RequestDataTrans
|
// RequestDataTrans
|
@ -102,7 +102,7 @@ internal class MultiMsg {
|
|||||||
if (PacketLogger.isEnabled) {
|
if (PacketLogger.isEnabled) {
|
||||||
return _miraiContentToString()
|
return _miraiContentToString()
|
||||||
}
|
}
|
||||||
return "MultiMsg.ApplyUp.Response.RequireUpload(proto=$proto)"
|
return "MultiMsg.ApplyUp.Response.RequireUpload"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -110,7 +110,7 @@ internal class MultiMsg {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// captured from group
|
// captured from group
|
||||||
fun createForGroupLongMessage(
|
fun createForGroup(
|
||||||
buType: Int,
|
buType: Int,
|
||||||
client: QQAndroidClient,
|
client: QQAndroidClient,
|
||||||
messageData: MessageValidationData,
|
messageData: MessageValidationData,
|
||||||
|
@ -295,7 +295,6 @@ internal object MessageSvcPbSendMsg : OutgoingPacketFactory<MessageSvcPbSendMsg.
|
|||||||
client: QQAndroidClient,
|
client: QQAndroidClient,
|
||||||
targetGroup: Group,
|
targetGroup: Group,
|
||||||
message: MessageChain,
|
message: MessageChain,
|
||||||
isForward: Boolean,
|
|
||||||
source: OnlineMessageSourceToGroupImpl
|
source: OnlineMessageSourceToGroupImpl
|
||||||
): OutgoingPacket = buildOutgoingUniPacket(client) {
|
): 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())
|
///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(),
|
msgRand = source.internalIds.single(),
|
||||||
syncCookie = EMPTY_BYTE_ARRAY,
|
syncCookie = EMPTY_BYTE_ARRAY,
|
||||||
msgVia = 1,
|
msgVia = 1,
|
||||||
msgCtrl = if (isForward) MsgCtrl.MsgCtrl(
|
msgCtrl =
|
||||||
msgFlag = 4
|
if (message[ForwardMessageInternal] != null)
|
||||||
) else null
|
MsgCtrl.MsgCtrl(msgFlag = 4)
|
||||||
|
else null
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
@ -429,7 +429,6 @@ internal inline fun MessageSvcPbSendMsg.createToGroup(
|
|||||||
client: QQAndroidClient,
|
client: QQAndroidClient,
|
||||||
group: Group,
|
group: Group,
|
||||||
message: MessageChain,
|
message: MessageChain,
|
||||||
isForward: Boolean,
|
|
||||||
crossinline sourceCallback: (OnlineMessageSourceToGroupImpl) -> Unit
|
crossinline sourceCallback: (OnlineMessageSourceToGroupImpl) -> Unit
|
||||||
): OutgoingPacket {
|
): OutgoingPacket {
|
||||||
contract {
|
contract {
|
||||||
@ -457,7 +456,6 @@ internal inline fun MessageSvcPbSendMsg.createToGroup(
|
|||||||
client,
|
client,
|
||||||
group,
|
group,
|
||||||
message,
|
message,
|
||||||
isForward,
|
|
||||||
source
|
source
|
||||||
)
|
)
|
||||||
}
|
}
|
Loading…
Reference in New Issue
Block a user