mirror of
https://github.com/mamoe/mirai.git
synced 2025-01-26 08:00:11 +08:00
Implement long message for private session and unify message send as SendMessageHandler
, fix send failure with quoted image fix #892
This commit is contained in:
parent
32362f02c3
commit
74c4369931
@ -695,9 +695,9 @@ internal open class MiraiImpl : IMirai, LowLevelApiAccessor {
|
|||||||
return jsonText?.let { json.decodeFromString(GroupHonorListData.serializer(), it) }
|
return jsonText?.let { json.decodeFromString(GroupHonorListData.serializer(), it) }
|
||||||
}
|
}
|
||||||
|
|
||||||
internal suspend fun uploadGroupMessageHighway(
|
internal suspend fun uploadMessageHighway(
|
||||||
bot: Bot,
|
bot: Bot,
|
||||||
groupCode: Long,
|
sendMessageHandler: SendMessageHandler<*>,
|
||||||
message: Collection<ForwardMessage.INode>,
|
message: Collection<ForwardMessage.INode>,
|
||||||
isLong: Boolean,
|
isLong: Boolean,
|
||||||
): String = with(bot.asQQAndroidBot()) {
|
): String = with(bot.asQQAndroidBot()) {
|
||||||
@ -705,14 +705,12 @@ internal open class MiraiImpl : IMirai, LowLevelApiAccessor {
|
|||||||
it.messageChain.ensureSequenceIdAvailable()
|
it.messageChain.ensureSequenceIdAvailable()
|
||||||
}
|
}
|
||||||
|
|
||||||
val group = getGroupOrFail(groupCode)
|
|
||||||
|
|
||||||
val sequenceId = client.atomicNextMessageSequenceId()
|
val sequenceId = client.atomicNextMessageSequenceId()
|
||||||
|
|
||||||
val data = message.calculateValidationDataForGroup(
|
val data = message.calculateValidationData(
|
||||||
sequenceId = sequenceId,
|
sequenceId = sequenceId,
|
||||||
random = Random.nextInt().absoluteValue,
|
random = Random.nextInt().absoluteValue,
|
||||||
group
|
sendMessageHandler
|
||||||
)
|
)
|
||||||
|
|
||||||
val response = network.run {
|
val response = network.run {
|
||||||
@ -720,7 +718,7 @@ internal open class MiraiImpl : IMirai, LowLevelApiAccessor {
|
|||||||
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 = sendMessageHandler.targetUin
|
||||||
).sendAndExpect<MultiMsg.ApplyUp.Response>()
|
).sendAndExpect<MultiMsg.ApplyUp.Response>()
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -740,7 +738,7 @@ internal open class MiraiImpl : IMirai, LowLevelApiAccessor {
|
|||||||
msgUpReq = listOf(
|
msgUpReq = listOf(
|
||||||
LongMsg.MsgUpReq(
|
LongMsg.MsgUpReq(
|
||||||
msgType = 3, // group
|
msgType = 3, // group
|
||||||
dstUin = Mirai.calculateGroupUinByGroupCode(groupCode),
|
dstUin = sendMessageHandler.targetUin,
|
||||||
msgId = 0,
|
msgId = 0,
|
||||||
msgUkey = response.proto.msgUkey,
|
msgUkey = response.proto.msgUkey,
|
||||||
needCache = 0,
|
needCache = 0,
|
||||||
@ -755,8 +753,8 @@ internal open class MiraiImpl : IMirai, LowLevelApiAccessor {
|
|||||||
bot = bot,
|
bot = bot,
|
||||||
resource = resource,
|
resource = resource,
|
||||||
kind = when (isLong) {
|
kind = when (isLong) {
|
||||||
true -> ResourceKind.GROUP_LONG_MESSAGE
|
true -> ResourceKind.LONG_MESSAGE
|
||||||
false -> ResourceKind.GROUP_FORWARD_MESSAGE
|
false -> ResourceKind.FORWARD_MESSAGE
|
||||||
},
|
},
|
||||||
commandId = 27,
|
commandId = 27,
|
||||||
initialTicket = response.proto.msgSig
|
initialTicket = response.proto.msgSig
|
||||||
|
@ -17,9 +17,7 @@ import net.mamoe.mirai.contact.Stranger
|
|||||||
import net.mamoe.mirai.contact.User
|
import net.mamoe.mirai.contact.User
|
||||||
import net.mamoe.mirai.data.UserInfo
|
import net.mamoe.mirai.data.UserInfo
|
||||||
import net.mamoe.mirai.event.broadcast
|
import net.mamoe.mirai.event.broadcast
|
||||||
import net.mamoe.mirai.event.events.BeforeImageUploadEvent
|
import net.mamoe.mirai.event.events.*
|
||||||
import net.mamoe.mirai.event.events.EventCancelledException
|
|
||||||
import net.mamoe.mirai.event.events.ImageUploadEvent
|
|
||||||
import net.mamoe.mirai.internal.message.OfflineFriendImage
|
import net.mamoe.mirai.internal.message.OfflineFriendImage
|
||||||
import net.mamoe.mirai.internal.message.getImageType
|
import net.mamoe.mirai.internal.message.getImageType
|
||||||
import net.mamoe.mirai.internal.network.highway.ChannelKind
|
import net.mamoe.mirai.internal.network.highway.ChannelKind
|
||||||
@ -29,7 +27,11 @@ import net.mamoe.mirai.internal.network.highway.postImage
|
|||||||
import net.mamoe.mirai.internal.network.highway.tryServers
|
import net.mamoe.mirai.internal.network.highway.tryServers
|
||||||
import net.mamoe.mirai.internal.network.protocol.data.proto.Cmd0x352
|
import net.mamoe.mirai.internal.network.protocol.data.proto.Cmd0x352
|
||||||
import net.mamoe.mirai.internal.network.protocol.packet.chat.image.LongConn
|
import net.mamoe.mirai.internal.network.protocol.packet.chat.image.LongConn
|
||||||
|
import net.mamoe.mirai.message.MessageReceipt
|
||||||
import net.mamoe.mirai.message.data.Image
|
import net.mamoe.mirai.message.data.Image
|
||||||
|
import net.mamoe.mirai.message.data.Message
|
||||||
|
import net.mamoe.mirai.message.data.MessageChain
|
||||||
|
import net.mamoe.mirai.message.data.isContentEmpty
|
||||||
import net.mamoe.mirai.utils.*
|
import net.mamoe.mirai.utils.*
|
||||||
import kotlin.coroutines.CoroutineContext
|
import kotlin.coroutines.CoroutineContext
|
||||||
|
|
||||||
@ -136,3 +138,24 @@ internal abstract class AbstractUser(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Suppress("DuplicatedCode")
|
||||||
|
internal suspend fun <C : User> SendMessageHandler<out C>.sendMessageImpl(
|
||||||
|
message: Message,
|
||||||
|
preSendEventConstructor: (C, Message) -> MessagePreSendEvent,
|
||||||
|
postSendEventConstructor: (C, MessageChain, Throwable?, MessageReceipt<C>?) -> MessagePostSendEvent<C>,
|
||||||
|
): MessageReceipt<C> {
|
||||||
|
require(!message.isContentEmpty()) { "message is empty" }
|
||||||
|
|
||||||
|
val chain = contact.broadcastMessagePreSendEvent(message, preSendEventConstructor)
|
||||||
|
|
||||||
|
val result = this
|
||||||
|
.runCatching { sendMessage(message, chain, SendMessageStep.FIRST) }
|
||||||
|
|
||||||
|
// logMessageSent(result.getOrNull()?.source?.plus(chain) ?: chain) // log with source
|
||||||
|
contact.logMessageSent(chain)
|
||||||
|
|
||||||
|
postSendEventConstructor(contact, chain, result.exceptionOrNull(), result.getOrNull()).broadcast()
|
||||||
|
|
||||||
|
return result.getOrThrow()
|
||||||
|
}
|
@ -24,12 +24,13 @@ import net.mamoe.mirai.LowLevelApi
|
|||||||
import net.mamoe.mirai.contact.Friend
|
import net.mamoe.mirai.contact.Friend
|
||||||
import net.mamoe.mirai.data.FriendInfo
|
import net.mamoe.mirai.data.FriendInfo
|
||||||
import net.mamoe.mirai.data.FriendInfoImpl
|
import net.mamoe.mirai.data.FriendInfoImpl
|
||||||
|
import net.mamoe.mirai.event.events.FriendMessagePostSendEvent
|
||||||
|
import net.mamoe.mirai.event.events.FriendMessagePreSendEvent
|
||||||
import net.mamoe.mirai.internal.QQAndroidBot
|
import net.mamoe.mirai.internal.QQAndroidBot
|
||||||
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.utils.C2CPkgMsgParsingCache
|
import net.mamoe.mirai.internal.utils.C2CPkgMsgParsingCache
|
||||||
import net.mamoe.mirai.message.MessageReceipt
|
import net.mamoe.mirai.message.MessageReceipt
|
||||||
import net.mamoe.mirai.message.data.Message
|
import net.mamoe.mirai.message.data.*
|
||||||
import net.mamoe.mirai.message.data.isContentEmpty
|
|
||||||
import kotlin.contracts.ExperimentalContracts
|
import kotlin.contracts.ExperimentalContracts
|
||||||
import kotlin.contracts.contract
|
import kotlin.contracts.contract
|
||||||
import kotlin.coroutines.CoroutineContext
|
import kotlin.coroutines.CoroutineContext
|
||||||
@ -80,16 +81,12 @@ internal class FriendImpl(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
private val handler: FriendSendMessageHandler by lazy { FriendSendMessageHandler(this) }
|
||||||
|
|
||||||
@Suppress("DuplicatedCode")
|
@Suppress("DuplicatedCode")
|
||||||
override suspend fun sendMessage(message: Message): MessageReceipt<Friend> {
|
override suspend fun sendMessage(message: Message): MessageReceipt<Friend> {
|
||||||
require(!message.isContentEmpty()) { "message is empty" }
|
return handler.sendMessageImpl(message, ::FriendMessagePreSendEvent, ::FriendMessagePostSendEvent)
|
||||||
return sendMessageImpl(
|
|
||||||
message,
|
|
||||||
friendReceiptConstructor = { MessageReceipt(it, this) },
|
|
||||||
tReceiptConstructor = { MessageReceipt(it, this) }
|
|
||||||
).also {
|
|
||||||
logMessageSent(message)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun toString(): String = "Friend($id)"
|
override fun toString(): String = "Friend($id)"
|
||||||
|
@ -120,9 +120,10 @@ 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) }
|
||||||
|
|
||||||
val chain = broadcastGroupMessagePreSendEvent(message)
|
val chain = broadcastMessagePreSendEvent(message, ::GroupMessagePreSendEvent)
|
||||||
|
|
||||||
val result = sendMessageImpl(message, chain, GroupMessageSendingStep.FIRST)
|
val result = GroupSendMessageHandler(this)
|
||||||
|
.runCatching { sendMessage(message, chain, SendMessageStep.FIRST) }
|
||||||
|
|
||||||
// logMessageSent(result.getOrNull()?.source?.plus(chain) ?: chain) // log with source
|
// logMessageSent(result.getOrNull()?.source?.plus(chain) ?: chain) // log with source
|
||||||
logMessageSent(chain)
|
logMessageSent(chain)
|
||||||
|
@ -12,61 +12,22 @@
|
|||||||
package net.mamoe.mirai.internal.contact
|
package net.mamoe.mirai.internal.contact
|
||||||
|
|
||||||
import net.mamoe.mirai.contact.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.broadcast
|
||||||
import net.mamoe.mirai.event.events.EventCancelledException
|
import net.mamoe.mirai.event.events.EventCancelledException
|
||||||
import net.mamoe.mirai.event.events.GroupMessagePreSendEvent
|
import net.mamoe.mirai.event.events.MessagePreSendEvent
|
||||||
import net.mamoe.mirai.event.nextEventOrNull
|
import net.mamoe.mirai.message.data.Message
|
||||||
import net.mamoe.mirai.internal.MiraiImpl
|
import net.mamoe.mirai.message.data.MessageChain
|
||||||
import net.mamoe.mirai.internal.forwardMessage
|
import net.mamoe.mirai.message.data.toMessageChain
|
||||||
import net.mamoe.mirai.internal.longMessage
|
|
||||||
import net.mamoe.mirai.internal.message.*
|
|
||||||
import net.mamoe.mirai.internal.network.Packet
|
|
||||||
import net.mamoe.mirai.internal.network.protocol.packet.chat.MusicSharePacket
|
|
||||||
import net.mamoe.mirai.internal.network.protocol.packet.chat.SendMessageMultiProtocol
|
|
||||||
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.OnlinePushPbPushGroupMsg
|
|
||||||
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,
|
|
||||||
step: GroupMessageSendingStep,
|
|
||||||
): Result<MessageReceipt<Group>> { // Result<MessageReceipt<Group>>
|
|
||||||
val chain = transformedMessage
|
|
||||||
.transformSpecialMessages(this)
|
|
||||||
.convertToLongMessageIfNeeded(step, this)
|
|
||||||
|
|
||||||
chain.findIsInstance<QuoteReply>()?.source?.ensureSequenceIdAvailable()
|
|
||||||
|
|
||||||
chain.asSequence().filterIsInstance<FriendImage>().forEach { image ->
|
|
||||||
updateFriendImageForGroupMessage(image)
|
|
||||||
}
|
|
||||||
|
|
||||||
return kotlin.runCatching {
|
|
||||||
sendMessagePacket(
|
|
||||||
originalMessage,
|
|
||||||
transformedMessage,
|
|
||||||
chain,
|
|
||||||
step
|
|
||||||
)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Called only in 'public' apis.
|
* Called only in 'public' apis.
|
||||||
*/
|
*/
|
||||||
internal suspend fun GroupImpl.broadcastGroupMessagePreSendEvent(message: Message): MessageChain {
|
internal suspend fun <C : Contact> C.broadcastMessagePreSendEvent(
|
||||||
|
message: Message,
|
||||||
|
eventConstructor: (C, Message) -> MessagePreSendEvent
|
||||||
|
): MessageChain {
|
||||||
return kotlin.runCatching {
|
return kotlin.runCatching {
|
||||||
GroupMessagePreSendEvent(this, message).broadcast()
|
eventConstructor(this, message).broadcast()
|
||||||
}.onSuccess {
|
}.onSuccess {
|
||||||
check(!it.isCancelled) {
|
check(!it.isCancelled) {
|
||||||
throw EventCancelledException("cancelled by GroupMessagePreSendEvent")
|
throw EventCancelledException("cancelled by GroupMessagePreSendEvent")
|
||||||
@ -77,178 +38,6 @@ internal suspend fun GroupImpl.broadcastGroupMessagePreSendEvent(message: Messag
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
/**
|
internal enum class SendMessageStep {
|
||||||
* - [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()
|
|
||||||
}
|
|
||||||
|
|
||||||
internal enum class GroupMessageSendingStep {
|
|
||||||
FIRST, LONG_MESSAGE, FRAGMENTED
|
FIRST, LONG_MESSAGE, FRAGMENTED
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* Final process
|
|
||||||
*/
|
|
||||||
private suspend fun GroupImpl.sendMessagePacket(
|
|
||||||
originalMessage: Message,
|
|
||||||
transformedMessage: Message,
|
|
||||||
finalMessage: MessageChain,
|
|
||||||
step: GroupMessageSendingStep,
|
|
||||||
): MessageReceipt<Group> {
|
|
||||||
|
|
||||||
val group = this
|
|
||||||
|
|
||||||
var source: OnlineMessageSourceToGroupImpl? = null
|
|
||||||
|
|
||||||
bot.network.run {
|
|
||||||
SendMessageMultiProtocol.createToGroup(
|
|
||||||
bot.client, group, finalMessage,
|
|
||||||
step == GroupMessageSendingStep.FRAGMENTED
|
|
||||||
) { source = it }.forEach { packet ->
|
|
||||||
|
|
||||||
when (val resp = packet.sendAndExpect<Packet>()) {
|
|
||||||
is MessageSvcPbSendMsg.Response -> {
|
|
||||||
if (resp is MessageSvcPbSendMsg.Response.MessageTooLarge) {
|
|
||||||
return when (step) {
|
|
||||||
GroupMessageSendingStep.FIRST -> {
|
|
||||||
sendMessageImpl(
|
|
||||||
originalMessage,
|
|
||||||
transformedMessage,
|
|
||||||
GroupMessageSendingStep.LONG_MESSAGE
|
|
||||||
)
|
|
||||||
}
|
|
||||||
GroupMessageSendingStep.LONG_MESSAGE -> {
|
|
||||||
sendMessageImpl(
|
|
||||||
originalMessage,
|
|
||||||
transformedMessage,
|
|
||||||
GroupMessageSendingStep.FRAGMENTED
|
|
||||||
)
|
|
||||||
|
|
||||||
}
|
|
||||||
else -> {
|
|
||||||
throw MessageTooLargeException(
|
|
||||||
group,
|
|
||||||
originalMessage,
|
|
||||||
finalMessage,
|
|
||||||
"Message '${finalMessage.content.take(10)}' is too large."
|
|
||||||
)
|
|
||||||
}
|
|
||||||
}.getOrThrow()
|
|
||||||
}
|
|
||||||
check(resp is MessageSvcPbSendMsg.Response.SUCCESS) {
|
|
||||||
"Send group message failed: $resp"
|
|
||||||
}
|
|
||||||
}
|
|
||||||
is MusicSharePacket.Response -> {
|
|
||||||
resp.pkg.checkSuccess("send group music share")
|
|
||||||
|
|
||||||
val receipt: OnlinePushPbPushGroupMsg.SendGroupMessageReceipt =
|
|
||||||
nextEventOrNull(3000) { it.fromAppId == 3116 }
|
|
||||||
?: OnlinePushPbPushGroupMsg.SendGroupMessageReceipt.EMPTY
|
|
||||||
|
|
||||||
source = OnlineMessageSourceToGroupImpl(
|
|
||||||
group,
|
|
||||||
internalIds = intArrayOf(receipt.messageRandom),
|
|
||||||
providedSequenceIds = intArrayOf(receipt.sequenceId),
|
|
||||||
sender = bot,
|
|
||||||
target = group,
|
|
||||||
time = currentTimeSeconds().toInt(),
|
|
||||||
originalMessage = finalMessage
|
|
||||||
)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
check(source != null) {
|
|
||||||
"Internal error: source is not initialized"
|
|
||||||
}
|
|
||||||
|
|
||||||
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
|
|
||||||
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
return MessageReceipt(source!!, group)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
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(
|
|
||||||
step: GroupMessageSendingStep,
|
|
||||||
groupImpl: GroupImpl,
|
|
||||||
): MessageChain {
|
|
||||||
suspend fun sendLongImpl(): MessageChain {
|
|
||||||
val resId = groupImpl.uploadGroupLongMessageHighway(this)
|
|
||||||
return this + RichMessage.longMessage(
|
|
||||||
brief = takeContent(27),
|
|
||||||
resId = resId,
|
|
||||||
timeSeconds = currentTimeSeconds()
|
|
||||||
) // LongMessageInternal replaces all contents and preserves metadata
|
|
||||||
}
|
|
||||||
return when (step) {
|
|
||||||
GroupMessageSendingStep.FIRST -> {
|
|
||||||
// 只需要在第一次发送的时候验证长度
|
|
||||||
// 后续重试直接跳过
|
|
||||||
if (contains(ForceAsLongMessage)) {
|
|
||||||
sendLongImpl()
|
|
||||||
}
|
|
||||||
verityLength(this, groupImpl)
|
|
||||||
this
|
|
||||||
}
|
|
||||||
GroupMessageSendingStep.LONG_MESSAGE -> {
|
|
||||||
sendLongImpl()
|
|
||||||
}
|
|
||||||
GroupMessageSendingStep.FRAGMENTED -> 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>()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
@ -20,13 +20,10 @@ 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.message.OnlineMessageSourceToTempImpl
|
import net.mamoe.mirai.internal.message.OnlineMessageSourceToTempImpl
|
||||||
import net.mamoe.mirai.internal.message.ensureSequenceIdAvailable
|
|
||||||
import net.mamoe.mirai.internal.message.firstIsInstanceOrNull
|
|
||||||
import net.mamoe.mirai.internal.network.protocol.packet.chat.TroopManagement
|
import net.mamoe.mirai.internal.network.protocol.packet.chat.TroopManagement
|
||||||
import net.mamoe.mirai.internal.network.protocol.packet.chat.receive.MessageSvcPbSendMsg
|
|
||||||
import net.mamoe.mirai.internal.network.protocol.packet.chat.receive.createToTemp
|
|
||||||
import net.mamoe.mirai.message.MessageReceipt
|
import net.mamoe.mirai.message.MessageReceipt
|
||||||
import net.mamoe.mirai.message.data.*
|
import net.mamoe.mirai.message.data.*
|
||||||
|
import net.mamoe.mirai.utils.cast
|
||||||
import net.mamoe.mirai.utils.currentTimeSeconds
|
import net.mamoe.mirai.utils.currentTimeSeconds
|
||||||
import kotlin.contracts.ExperimentalContracts
|
import kotlin.contracts.ExperimentalContracts
|
||||||
import kotlin.contracts.contract
|
import kotlin.contracts.contract
|
||||||
@ -48,65 +45,23 @@ internal class NormalMemberImpl constructor(
|
|||||||
|
|
||||||
override fun toString(): String = "NormalMember($id)"
|
override fun toString(): String = "NormalMember($id)"
|
||||||
|
|
||||||
@Suppress("UNCHECKED_CAST")
|
private val handler by lazy { GroupTempSendMessageHandler(this) }
|
||||||
@JvmSynthetic
|
|
||||||
|
@Suppress("DuplicatedCode")
|
||||||
override suspend fun sendMessage(message: Message): MessageReceipt<NormalMember> {
|
override suspend fun sendMessage(message: Message): MessageReceipt<NormalMember> {
|
||||||
require(!message.isContentEmpty()) { "message is empty" }
|
return asFriendOrNull()?.sendMessage(message)?.convert()
|
||||||
|
?: asStrangerOrNull()?.sendMessage(message)?.convert()
|
||||||
val asFriend = this.asFriendOrNull()
|
?: handler.sendMessageImpl<NormalMember>(
|
||||||
val asStranger = this.asStrangerOrNull()
|
message = message,
|
||||||
|
preSendEventConstructor = ::GroupTempMessagePreSendEvent,
|
||||||
return (asFriend?.sendMessageImpl(
|
postSendEventConstructor = ::GroupTempMessagePostSendEvent.cast()
|
||||||
message,
|
)
|
||||||
friendReceiptConstructor = { MessageReceipt(it, asFriend) },
|
|
||||||
tReceiptConstructor = { MessageReceipt(it, this) }
|
|
||||||
) ?: asStranger?.sendMessageImpl(
|
|
||||||
message,
|
|
||||||
strangerReceiptConstructor = { MessageReceipt(it, asStranger) },
|
|
||||||
tReceiptConstructor = { MessageReceipt(it, this) }
|
|
||||||
) ?: sendMessageImpl(message)).also { logMessageSent(message) }
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private suspend fun sendMessageImpl(message: Message): MessageReceipt<NormalMember> {
|
private fun MessageReceipt<User>.convert(): MessageReceipt<NormalMemberImpl> {
|
||||||
val chain = kotlin.runCatching {
|
return MessageReceipt(OnlineMessageSourceToTempImpl(source, this@NormalMemberImpl), this@NormalMemberImpl)
|
||||||
GroupTempMessagePreSendEvent(this, message).broadcast()
|
|
||||||
}.onSuccess {
|
|
||||||
check(!it.isCancelled) {
|
|
||||||
throw EventCancelledException("cancelled by GroupTempMessagePreSendEvent")
|
|
||||||
}
|
|
||||||
}.getOrElse {
|
|
||||||
throw EventCancelledException("exception thrown when broadcasting GroupTempMessagePreSendEvent", it)
|
|
||||||
}.message.toMessageChain()
|
|
||||||
|
|
||||||
chain.firstIsInstanceOrNull<QuoteReply>()?.source?.ensureSequenceIdAvailable()
|
|
||||||
|
|
||||||
val result = bot.network.runCatching {
|
|
||||||
val source: OnlineMessageSourceToTempImpl
|
|
||||||
MessageSvcPbSendMsg.createToTemp(
|
|
||||||
bot.client,
|
|
||||||
this@NormalMemberImpl,
|
|
||||||
chain
|
|
||||||
) {
|
|
||||||
source = it
|
|
||||||
}.sendAndExpect<MessageSvcPbSendMsg.Response>().let {
|
|
||||||
check(it is MessageSvcPbSendMsg.Response.SUCCESS) {
|
|
||||||
"Send temp message failed: $it"
|
|
||||||
}
|
|
||||||
}
|
|
||||||
MessageReceipt(source, this@NormalMemberImpl)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
result.fold(
|
|
||||||
onSuccess = {
|
|
||||||
GroupTempMessagePostSendEvent(this, chain, null, it)
|
|
||||||
},
|
|
||||||
onFailure = {
|
|
||||||
GroupTempMessagePostSendEvent(this, chain, it, null)
|
|
||||||
}
|
|
||||||
).broadcast()
|
|
||||||
|
|
||||||
return result.getOrThrow()
|
|
||||||
}
|
|
||||||
|
|
||||||
@Suppress("PropertyName")
|
@Suppress("PropertyName")
|
||||||
internal var _nameCard: String = memberInfo.nameCard
|
internal var _nameCard: String = memberInfo.nameCard
|
||||||
|
357
mirai-core/src/commonMain/kotlin/contact/SendMessageHandler.kt
Normal file
357
mirai-core/src/commonMain/kotlin/contact/SendMessageHandler.kt
Normal file
@ -0,0 +1,357 @@
|
|||||||
|
/*
|
||||||
|
* 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.*
|
||||||
|
import net.mamoe.mirai.event.nextEventOrNull
|
||||||
|
import net.mamoe.mirai.internal.MiraiImpl
|
||||||
|
import net.mamoe.mirai.internal.asQQAndroidBot
|
||||||
|
import net.mamoe.mirai.internal.forwardMessage
|
||||||
|
import net.mamoe.mirai.internal.longMessage
|
||||||
|
import net.mamoe.mirai.internal.message.*
|
||||||
|
import net.mamoe.mirai.internal.network.Packet
|
||||||
|
import net.mamoe.mirai.internal.network.QQAndroidClient
|
||||||
|
import net.mamoe.mirai.internal.network.protocol.data.proto.MsgComm
|
||||||
|
import net.mamoe.mirai.internal.network.protocol.packet.OutgoingPacket
|
||||||
|
import net.mamoe.mirai.internal.network.protocol.packet.chat.MusicSharePacket
|
||||||
|
import net.mamoe.mirai.internal.network.protocol.packet.chat.image.ImgStore
|
||||||
|
import net.mamoe.mirai.internal.network.protocol.packet.chat.receive.*
|
||||||
|
import net.mamoe.mirai.internal.network.protocol.packet.chat.receive.createToFriend
|
||||||
|
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.castOrNull
|
||||||
|
import net.mamoe.mirai.utils.currentTimeSeconds
|
||||||
|
import java.lang.UnsupportedOperationException
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 通用处理器
|
||||||
|
*/
|
||||||
|
internal abstract class SendMessageHandler<C : Contact> {
|
||||||
|
abstract val contact: C
|
||||||
|
abstract val senderName: String
|
||||||
|
|
||||||
|
val messageSourceKind: MessageSourceKind
|
||||||
|
get() {
|
||||||
|
return when (contact) {
|
||||||
|
is Group -> MessageSourceKind.GROUP
|
||||||
|
is Friend -> MessageSourceKind.FRIEND
|
||||||
|
is Member -> MessageSourceKind.TEMP
|
||||||
|
is Stranger -> MessageSourceKind.STRANGER
|
||||||
|
else -> error("Unsupported contact: $contact")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
val bot get() = contact.bot.asQQAndroidBot()
|
||||||
|
|
||||||
|
val targetUserUin: Long? get() = contact.castOrNull<User>()?.uin
|
||||||
|
val targetGroupUin: Long? get() = contact.castOrNull<Group>()?.uin
|
||||||
|
val targetGroupCode: Long? get() = contact.castOrNull<Group>()?.groupCode
|
||||||
|
|
||||||
|
val targetOtherClientBotUin: Long? get() = contact.castOrNull<OtherClient>()?.bot?.id
|
||||||
|
|
||||||
|
val targetUin: Long get() = targetGroupUin ?: targetOtherClientBotUin ?: contact.id
|
||||||
|
|
||||||
|
val groupInfo: MsgComm.GroupInfo?
|
||||||
|
get() = if (isToGroup) MsgComm.GroupInfo(
|
||||||
|
groupCode = targetGroupCode!!,
|
||||||
|
groupCard = senderName // Cinnamon
|
||||||
|
) else null
|
||||||
|
|
||||||
|
val isToGroup: Boolean get() = contact is Group
|
||||||
|
|
||||||
|
suspend fun MessageChain.convertToLongMessageIfNeeded(
|
||||||
|
step: SendMessageStep,
|
||||||
|
): MessageChain {
|
||||||
|
suspend fun sendLongImpl(): MessageChain {
|
||||||
|
val resId = uploadLongMessageHighway(this)
|
||||||
|
return this + RichMessage.longMessage(
|
||||||
|
brief = takeContent(27),
|
||||||
|
resId = resId,
|
||||||
|
timeSeconds = currentTimeSeconds()
|
||||||
|
) // LongMessageInternal replaces all contents and preserves metadata
|
||||||
|
}
|
||||||
|
return when (step) {
|
||||||
|
SendMessageStep.FIRST -> {
|
||||||
|
// 只需要在第一次发送的时候验证长度
|
||||||
|
// 后续重试直接跳过
|
||||||
|
if (contains(ForceAsLongMessage)) {
|
||||||
|
sendLongImpl()
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!contains(IgnoreLengthCheck)) {
|
||||||
|
verityLength(this, contact)
|
||||||
|
}
|
||||||
|
|
||||||
|
this
|
||||||
|
}
|
||||||
|
SendMessageStep.LONG_MESSAGE -> {
|
||||||
|
if (contains(DontAsLongMessage)) this // fragmented
|
||||||
|
else sendLongImpl()
|
||||||
|
}
|
||||||
|
SendMessageStep.FRAGMENTED -> this
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Final process
|
||||||
|
*/
|
||||||
|
suspend fun sendMessagePacket(
|
||||||
|
originalMessage: Message,
|
||||||
|
transformedMessage: Message,
|
||||||
|
finalMessage: MessageChain,
|
||||||
|
step: SendMessageStep,
|
||||||
|
): MessageReceipt<C> {
|
||||||
|
|
||||||
|
val group = contact
|
||||||
|
|
||||||
|
var source: OnlineMessageSource.Outgoing? = null
|
||||||
|
|
||||||
|
bot.network.run {
|
||||||
|
sendMessageMultiProtocol(
|
||||||
|
bot.client, finalMessage,
|
||||||
|
fragmented = step == SendMessageStep.FRAGMENTED
|
||||||
|
) { source = it }.forEach { packet ->
|
||||||
|
|
||||||
|
when (val resp = packet.sendAndExpect<Packet>()) {
|
||||||
|
is MessageSvcPbSendMsg.Response -> {
|
||||||
|
if (resp is MessageSvcPbSendMsg.Response.MessageTooLarge) {
|
||||||
|
return when (step) {
|
||||||
|
SendMessageStep.FIRST -> {
|
||||||
|
sendMessage(originalMessage, transformedMessage, SendMessageStep.LONG_MESSAGE)
|
||||||
|
}
|
||||||
|
SendMessageStep.LONG_MESSAGE -> {
|
||||||
|
sendMessage(originalMessage, transformedMessage, SendMessageStep.FRAGMENTED)
|
||||||
|
|
||||||
|
}
|
||||||
|
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"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
is MusicSharePacket.Response -> {
|
||||||
|
resp.pkg.checkSuccess("send group music share")
|
||||||
|
|
||||||
|
source = constructSourceFromMusicShareResponse(finalMessage, resp)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
check(source != null) {
|
||||||
|
"Internal error: source is not initialized"
|
||||||
|
}
|
||||||
|
|
||||||
|
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
|
||||||
|
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
return MessageReceipt(source!!, contact)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fun sendMessageMultiProtocol(
|
||||||
|
client: QQAndroidClient,
|
||||||
|
message: MessageChain,
|
||||||
|
fragmented: Boolean,
|
||||||
|
sourceCallback: (OnlineMessageSource.Outgoing) -> Unit
|
||||||
|
): List<OutgoingPacket> {
|
||||||
|
message.takeSingleContent<MusicShare>()?.let { musicShare ->
|
||||||
|
return listOf(
|
||||||
|
MusicSharePacket(
|
||||||
|
client, musicShare, contact.id,
|
||||||
|
targetKind = if (isToGroup) MessageSourceKind.GROUP else MessageSourceKind.FRIEND // always FRIEND
|
||||||
|
)
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
return messageSvcSendMessage(client, contact, message, fragmented, sourceCallback)
|
||||||
|
}
|
||||||
|
|
||||||
|
abstract val messageSvcSendMessage: (
|
||||||
|
client: QQAndroidClient,
|
||||||
|
contact: C,
|
||||||
|
message: MessageChain,
|
||||||
|
fragmented: Boolean,
|
||||||
|
sourceCallback: (OnlineMessageSource.Outgoing) -> Unit,
|
||||||
|
) -> List<OutgoingPacket>
|
||||||
|
|
||||||
|
abstract suspend fun constructSourceFromMusicShareResponse(
|
||||||
|
finalMessage: MessageChain,
|
||||||
|
response: MusicSharePacket.Response
|
||||||
|
): OnlineMessageSource.Outgoing
|
||||||
|
|
||||||
|
open suspend fun uploadLongMessageHighway(
|
||||||
|
chain: MessageChain
|
||||||
|
): String = with(contact) {
|
||||||
|
return MiraiImpl.uploadMessageHighway(
|
||||||
|
bot, this@SendMessageHandler,
|
||||||
|
listOf(
|
||||||
|
ForwardMessage.Node(
|
||||||
|
senderId = bot.id,
|
||||||
|
time = currentTimeSeconds().toInt(),
|
||||||
|
messageChain = chain,
|
||||||
|
senderName = bot.nick
|
||||||
|
)
|
||||||
|
),
|
||||||
|
true
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
open suspend fun postTransformActions(chain: MessageChain) {
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* - [ForwardMessage] -> [ForwardMessageInternal] (by uploading through highway)
|
||||||
|
* - ... any others for future
|
||||||
|
*/
|
||||||
|
internal suspend fun <C : Contact> SendMessageHandler<C>.transformSpecialMessages(message: Message): MessageChain {
|
||||||
|
return message.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.uploadMessageHighway(
|
||||||
|
bot = contact.bot,
|
||||||
|
sendMessageHandler = this,
|
||||||
|
message = forward.nodeList,
|
||||||
|
isLong = false,
|
||||||
|
)
|
||||||
|
RichMessage.forwardMessage(
|
||||||
|
resId = resId,
|
||||||
|
timeSeconds = currentTimeSeconds(),
|
||||||
|
forwardMessage = forward,
|
||||||
|
)
|
||||||
|
}?.toMessageChain() ?: message.toMessageChain()
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Might be recalled with [transformedMessage] `is` [LongMessageInternal] if length estimation failed ([sendMessagePacket])
|
||||||
|
*/
|
||||||
|
internal suspend fun <C : Contact> SendMessageHandler<C>.sendMessage(
|
||||||
|
originalMessage: Message,
|
||||||
|
transformedMessage: Message,
|
||||||
|
step: SendMessageStep,
|
||||||
|
): MessageReceipt<C> { // Result cannot be in interface.
|
||||||
|
val chain = transformSpecialMessages(transformedMessage)
|
||||||
|
.convertToLongMessageIfNeeded(step)
|
||||||
|
|
||||||
|
chain.findIsInstance<QuoteReply>()?.source?.ensureSequenceIdAvailable()
|
||||||
|
|
||||||
|
postTransformActions(chain)
|
||||||
|
|
||||||
|
return sendMessagePacket(originalMessage, transformedMessage, chain, step)
|
||||||
|
}
|
||||||
|
|
||||||
|
internal sealed class UserSendMessageHandler<C : AbstractUser>(
|
||||||
|
override val contact: C,
|
||||||
|
) : SendMessageHandler<C>() {
|
||||||
|
override val senderName: String get() = bot.nick
|
||||||
|
|
||||||
|
override suspend fun constructSourceFromMusicShareResponse(
|
||||||
|
finalMessage: MessageChain,
|
||||||
|
response: MusicSharePacket.Response
|
||||||
|
): OnlineMessageSource.Outgoing {
|
||||||
|
throw UnsupportedOperationException("Sending MusicShare to user is not yet supported")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
internal class FriendSendMessageHandler(
|
||||||
|
contact: FriendImpl,
|
||||||
|
) : UserSendMessageHandler<FriendImpl>(contact) {
|
||||||
|
override val messageSvcSendMessage: (client: QQAndroidClient, contact: FriendImpl, message: MessageChain, fragmented: Boolean, sourceCallback: (OnlineMessageSource.Outgoing) -> Unit) -> List<OutgoingPacket> =
|
||||||
|
MessageSvcPbSendMsg::createToFriend
|
||||||
|
}
|
||||||
|
|
||||||
|
internal class StrangerSendMessageHandler(
|
||||||
|
contact: StrangerImpl,
|
||||||
|
) : UserSendMessageHandler<StrangerImpl>(contact) {
|
||||||
|
override val messageSvcSendMessage: (client: QQAndroidClient, contact: StrangerImpl, message: MessageChain, fragmented: Boolean, sourceCallback: (OnlineMessageSource.Outgoing) -> Unit) -> List<OutgoingPacket> =
|
||||||
|
MessageSvcPbSendMsg::createToStranger
|
||||||
|
}
|
||||||
|
|
||||||
|
internal class GroupTempSendMessageHandler(
|
||||||
|
contact: NormalMemberImpl,
|
||||||
|
) : UserSendMessageHandler<NormalMemberImpl>(contact) {
|
||||||
|
override val messageSvcSendMessage: (client: QQAndroidClient, contact: NormalMemberImpl, message: MessageChain, fragmented: Boolean, sourceCallback: (OnlineMessageSource.Outgoing) -> Unit) -> List<OutgoingPacket> =
|
||||||
|
MessageSvcPbSendMsg::createToTemp
|
||||||
|
}
|
||||||
|
|
||||||
|
internal class GroupSendMessageHandler(
|
||||||
|
override val contact: GroupImpl,
|
||||||
|
) : SendMessageHandler<GroupImpl>() {
|
||||||
|
override val messageSvcSendMessage: (client: QQAndroidClient, contact: GroupImpl, message: MessageChain, fragmented: Boolean, sourceCallback: (OnlineMessageSource.Outgoing) -> Unit) -> List<OutgoingPacket> =
|
||||||
|
MessageSvcPbSendMsg::createToGroup
|
||||||
|
override val senderName: String
|
||||||
|
get() = contact.botAsMember.nameCardOrNick
|
||||||
|
|
||||||
|
override suspend fun postTransformActions(chain: MessageChain) {
|
||||||
|
chain.asSequence().filterIsInstance<FriendImage>().forEach { image ->
|
||||||
|
contact.updateFriendImageForGroupMessage(image)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
override suspend fun constructSourceFromMusicShareResponse(
|
||||||
|
finalMessage: MessageChain,
|
||||||
|
response: MusicSharePacket.Response
|
||||||
|
): OnlineMessageSource.Outgoing {
|
||||||
|
|
||||||
|
val receipt: OnlinePushPbPushGroupMsg.SendGroupMessageReceipt =
|
||||||
|
nextEventOrNull(3000) { it.fromAppId == 3116 }
|
||||||
|
?: OnlinePushPbPushGroupMsg.SendGroupMessageReceipt.EMPTY
|
||||||
|
|
||||||
|
return OnlineMessageSourceToGroupImpl(
|
||||||
|
contact,
|
||||||
|
internalIds = intArrayOf(receipt.messageRandom),
|
||||||
|
providedSequenceIds = intArrayOf(receipt.sequenceId),
|
||||||
|
sender = bot,
|
||||||
|
target = contact,
|
||||||
|
time = currentTimeSeconds().toInt(),
|
||||||
|
originalMessage = finalMessage
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
companion object {
|
||||||
|
/**
|
||||||
|
* 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>()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -20,14 +20,17 @@ package net.mamoe.mirai.internal.contact
|
|||||||
import kotlinx.atomicfu.AtomicInt
|
import kotlinx.atomicfu.AtomicInt
|
||||||
import kotlinx.atomicfu.atomic
|
import kotlinx.atomicfu.atomic
|
||||||
import net.mamoe.mirai.LowLevelApi
|
import net.mamoe.mirai.LowLevelApi
|
||||||
import net.mamoe.mirai.contact.Stranger
|
import net.mamoe.mirai.contact.*
|
||||||
import net.mamoe.mirai.data.FriendInfoImpl
|
import net.mamoe.mirai.data.FriendInfoImpl
|
||||||
import net.mamoe.mirai.data.StrangerInfo
|
import net.mamoe.mirai.data.StrangerInfo
|
||||||
|
import net.mamoe.mirai.event.events.StrangerMessagePostSendEvent
|
||||||
|
import net.mamoe.mirai.event.events.StrangerMessagePreSendEvent
|
||||||
import net.mamoe.mirai.internal.QQAndroidBot
|
import net.mamoe.mirai.internal.QQAndroidBot
|
||||||
|
import net.mamoe.mirai.internal.message.OnlineMessageSourceToStrangerImpl
|
||||||
import net.mamoe.mirai.internal.network.protocol.packet.list.StrangerList
|
import net.mamoe.mirai.internal.network.protocol.packet.list.StrangerList
|
||||||
import net.mamoe.mirai.message.MessageReceipt
|
import net.mamoe.mirai.message.MessageReceipt
|
||||||
import net.mamoe.mirai.message.data.Message
|
import net.mamoe.mirai.message.data.Message
|
||||||
import net.mamoe.mirai.message.data.isContentEmpty
|
import net.mamoe.mirai.utils.cast
|
||||||
import kotlin.contracts.ExperimentalContracts
|
import kotlin.contracts.ExperimentalContracts
|
||||||
import kotlin.contracts.contract
|
import kotlin.contracts.contract
|
||||||
import kotlin.coroutines.CoroutineContext
|
import kotlin.coroutines.CoroutineContext
|
||||||
@ -75,16 +78,20 @@ internal class StrangerImpl(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private val handler by lazy { StrangerSendMessageHandler(this) }
|
||||||
|
|
||||||
@Suppress("DuplicatedCode")
|
@Suppress("DuplicatedCode")
|
||||||
override suspend fun sendMessage(message: Message): MessageReceipt<Stranger> {
|
override suspend fun sendMessage(message: Message): MessageReceipt<Stranger> {
|
||||||
require(!message.isContentEmpty()) { "message is empty" }
|
return asFriendOrNull()?.sendMessage(message)?.convert()
|
||||||
return sendMessageImpl(
|
?: handler.sendMessageImpl<Stranger>(
|
||||||
message,
|
message = message,
|
||||||
strangerReceiptConstructor = { MessageReceipt(it, this) },
|
preSendEventConstructor = ::StrangerMessagePreSendEvent,
|
||||||
tReceiptConstructor = { MessageReceipt(it, this) }
|
postSendEventConstructor = ::StrangerMessagePostSendEvent.cast()
|
||||||
).also {
|
)
|
||||||
logMessageSent(message)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private fun MessageReceipt<User>.convert(): MessageReceipt<StrangerImpl> {
|
||||||
|
return MessageReceipt(OnlineMessageSourceToStrangerImpl(source, this@StrangerImpl), this@StrangerImpl)
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun toString(): String = "Stranger($id)"
|
override fun toString(): String = "Stranger($id)"
|
||||||
|
@ -58,12 +58,14 @@ internal suspend fun <T : User> Friend.sendMessageImpl(
|
|||||||
|
|
||||||
chain.firstIsInstanceOrNull<QuoteReply>()?.source?.ensureSequenceIdAvailable()
|
chain.firstIsInstanceOrNull<QuoteReply>()?.source?.ensureSequenceIdAvailable()
|
||||||
|
|
||||||
|
|
||||||
lateinit var source: OnlineMessageSourceToFriendImpl
|
lateinit var source: OnlineMessageSourceToFriendImpl
|
||||||
val result = bot.network.runCatching {
|
val result = bot.network.runCatching {
|
||||||
MessageSvcPbSendMsg.createToFriend(
|
MessageSvcPbSendMsg.createToFriend(
|
||||||
bot.client,
|
bot.client,
|
||||||
this@sendMessageImpl,
|
this@sendMessageImpl,
|
||||||
chain
|
chain,
|
||||||
|
false
|
||||||
) {
|
) {
|
||||||
source = it
|
source = it
|
||||||
}.forEach { packet ->
|
}.forEach { packet ->
|
||||||
@ -116,11 +118,15 @@ internal suspend fun <T : User> Stranger.sendMessageImpl(
|
|||||||
bot.client,
|
bot.client,
|
||||||
this@sendMessageImpl,
|
this@sendMessageImpl,
|
||||||
chain,
|
chain,
|
||||||
|
false,
|
||||||
) {
|
) {
|
||||||
source = it
|
source = it
|
||||||
}.sendAndExpect<MessageSvcPbSendMsg.Response>().let {
|
}.forEach { pk ->
|
||||||
check(it is MessageSvcPbSendMsg.Response.SUCCESS) {
|
pk.sendAndExpect<net.mamoe.mirai.internal.network.protocol.packet.chat.receive.MessageSvcPbSendMsg.Response>()
|
||||||
"Send stranger message failed: $it"
|
.let {
|
||||||
|
kotlin.check(it is net.mamoe.mirai.internal.network.protocol.packet.chat.receive.MessageSvcPbSendMsg.Response.SUCCESS) {
|
||||||
|
"Send temp message failed: $it"
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
strangerReceiptConstructor(source)
|
strangerReceiptConstructor(source)
|
||||||
|
@ -7,6 +7,8 @@
|
|||||||
* https://github.com/mamoe/mirai/blob/master/LICENSE
|
* https://github.com/mamoe/mirai/blob/master/LICENSE
|
||||||
*/
|
*/
|
||||||
|
|
||||||
|
@file:Suppress("unused")
|
||||||
|
|
||||||
package net.mamoe.mirai.internal.message
|
package net.mamoe.mirai.internal.message
|
||||||
|
|
||||||
import net.mamoe.mirai.message.data.*
|
import net.mamoe.mirai.message.data.*
|
||||||
@ -19,7 +21,24 @@ internal object ForceAsLongMessage : MessageMetadata, ConstrainSingle, InternalF
|
|||||||
AbstractMessageKey<ForceAsLongMessage>({ it.safeCast() }) {
|
AbstractMessageKey<ForceAsLongMessage>({ it.safeCast() }) {
|
||||||
override val key: MessageKey<ForceAsLongMessage> get() = this
|
override val key: MessageKey<ForceAsLongMessage> get() = this
|
||||||
|
|
||||||
override fun toString(): String = "ForceLongMessage"
|
override fun toString(): String = ""
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 强制不发 long
|
||||||
|
*/
|
||||||
|
internal object DontAsLongMessage : MessageMetadata, ConstrainSingle, InternalFlagOnlyMessage,
|
||||||
|
AbstractMessageKey<DontAsLongMessage>({ it.safeCast() }) {
|
||||||
|
override val key: MessageKey<DontAsLongMessage> get() = this
|
||||||
|
|
||||||
|
override fun toString(): String = ""
|
||||||
|
}
|
||||||
|
|
||||||
|
internal object IgnoreLengthCheck : MessageMetadata, ConstrainSingle, InternalFlagOnlyMessage,
|
||||||
|
AbstractMessageKey<IgnoreLengthCheck>({ it.safeCast() }) {
|
||||||
|
override val key: MessageKey<IgnoreLengthCheck> get() = this
|
||||||
|
|
||||||
|
override fun toString(): String = ""
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -33,7 +33,7 @@ import java.util.concurrent.atomic.AtomicBoolean
|
|||||||
|
|
||||||
internal interface MessageSourceInternal {
|
internal interface MessageSourceInternal {
|
||||||
@Transient
|
@Transient
|
||||||
val sequenceIds: IntArray
|
val sequenceIds: IntArray // ids
|
||||||
|
|
||||||
@Transient
|
@Transient
|
||||||
val internalIds: IntArray // randomId
|
val internalIds: IntArray // randomId
|
||||||
|
@ -103,6 +103,12 @@ internal class OnlineMessageSourceToStrangerImpl(
|
|||||||
override val sender: Bot,
|
override val sender: Bot,
|
||||||
override val target: Stranger
|
override val target: Stranger
|
||||||
) : OnlineMessageSource.Outgoing.ToStranger(), MessageSourceInternal {
|
) : OnlineMessageSource.Outgoing.ToStranger(), MessageSourceInternal {
|
||||||
|
|
||||||
|
constructor(
|
||||||
|
delegate: OnlineMessageSource.Outgoing,
|
||||||
|
target: Stranger
|
||||||
|
) : this(delegate.ids, delegate.internalIds, delegate.time, delegate.originalMessage, delegate.sender, target)
|
||||||
|
|
||||||
object Serializer : MessageSourceSerializerImpl("OnlineMessageSourceToStranger")
|
object Serializer : MessageSourceSerializerImpl("OnlineMessageSourceToStranger")
|
||||||
|
|
||||||
override val bot: Bot
|
override val bot: Bot
|
||||||
@ -123,6 +129,11 @@ internal class OnlineMessageSourceToTempImpl(
|
|||||||
override val sender: Bot,
|
override val sender: Bot,
|
||||||
override val target: Member
|
override val target: Member
|
||||||
) : OnlineMessageSource.Outgoing.ToTemp(), MessageSourceInternal {
|
) : OnlineMessageSource.Outgoing.ToTemp(), MessageSourceInternal {
|
||||||
|
constructor(
|
||||||
|
delegate: OnlineMessageSource.Outgoing,
|
||||||
|
target: Member
|
||||||
|
) : this(delegate.ids, delegate.internalIds, delegate.time, delegate.originalMessage, delegate.sender, target)
|
||||||
|
|
||||||
object Serializer : MessageSourceSerializerImpl("OnlineMessageSourceToTemp")
|
object Serializer : MessageSourceSerializerImpl("OnlineMessageSourceToTemp")
|
||||||
|
|
||||||
override val bot: Bot
|
override val bot: Bot
|
||||||
|
@ -89,8 +89,8 @@ internal enum class ResourceKind(
|
|||||||
PRIVATE_VOICE("private voice"),
|
PRIVATE_VOICE("private voice"),
|
||||||
GROUP_VOICE("group voice"),
|
GROUP_VOICE("group voice"),
|
||||||
|
|
||||||
GROUP_LONG_MESSAGE("group long message"),
|
LONG_MESSAGE("long message"),
|
||||||
GROUP_FORWARD_MESSAGE("group forward message"),
|
FORWARD_MESSAGE("forward message"),
|
||||||
;
|
;
|
||||||
|
|
||||||
override fun toString(): String = display
|
override fun toString(): String = display
|
||||||
|
@ -12,9 +12,8 @@
|
|||||||
package net.mamoe.mirai.internal.network.protocol.packet.chat
|
package net.mamoe.mirai.internal.network.protocol.packet.chat
|
||||||
|
|
||||||
import kotlinx.io.core.ByteReadPacket
|
import kotlinx.io.core.ByteReadPacket
|
||||||
import net.mamoe.mirai.contact.Group
|
|
||||||
import net.mamoe.mirai.internal.QQAndroidBot
|
import net.mamoe.mirai.internal.QQAndroidBot
|
||||||
import net.mamoe.mirai.internal.contact.groupCode
|
import net.mamoe.mirai.internal.contact.SendMessageHandler
|
||||||
import net.mamoe.mirai.internal.message.toRichTextElems
|
import net.mamoe.mirai.internal.message.toRichTextElems
|
||||||
import net.mamoe.mirai.internal.network.Packet
|
import net.mamoe.mirai.internal.network.Packet
|
||||||
import net.mamoe.mirai.internal.network.QQAndroidClient
|
import net.mamoe.mirai.internal.network.QQAndroidClient
|
||||||
@ -45,15 +44,16 @@ internal class MessageValidationData(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
internal fun Collection<ForwardMessage.INode>.calculateValidationDataForGroup(
|
internal fun Collection<ForwardMessage.INode>.calculateValidationData(
|
||||||
sequenceId: Int,
|
sequenceId: Int,
|
||||||
random: Int,
|
random: Int,
|
||||||
targetGroup: Group,
|
handler: SendMessageHandler<*>,
|
||||||
): MessageValidationData {
|
): MessageValidationData {
|
||||||
val msgList = map { chain ->
|
val msgList = map { chain ->
|
||||||
MsgComm.Msg(
|
MsgComm.Msg(
|
||||||
msgHead = MsgComm.MsgHead(
|
msgHead = MsgComm.MsgHead(
|
||||||
fromUin = chain.senderId,
|
fromUin = chain.senderId,
|
||||||
|
toUin = handler.targetUserUin ?: 0,
|
||||||
msgSeq = sequenceId,
|
msgSeq = sequenceId,
|
||||||
msgTime = chain.time,
|
msgTime = chain.time,
|
||||||
msgUid = 0x01000000000000000L or random.toLongUnsigned(),
|
msgUid = 0x01000000000000000L or random.toLongUnsigned(),
|
||||||
@ -62,16 +62,13 @@ internal fun Collection<ForwardMessage.INode>.calculateValidationDataForGroup(
|
|||||||
msgId = 1
|
msgId = 1
|
||||||
),
|
),
|
||||||
msgType = 82, // troop
|
msgType = 82, // troop
|
||||||
groupInfo = MsgComm.GroupInfo(
|
groupInfo = handler.groupInfo,
|
||||||
groupCode = targetGroup.groupCode,
|
|
||||||
groupCard = chain.senderName // Cinnamon
|
|
||||||
),
|
|
||||||
isSrcMsg = false
|
isSrcMsg = false
|
||||||
),
|
),
|
||||||
msgBody = ImMsgBody.MsgBody(
|
msgBody = ImMsgBody.MsgBody(
|
||||||
richText = ImMsgBody.RichText(
|
richText = ImMsgBody.RichText(
|
||||||
elems = chain.messageChain.toMessageChain()
|
elems = chain.messageChain.toMessageChain()
|
||||||
.toRichTextElems(targetGroup, withGeneralFlags = false).toMutableList()
|
.toRichTextElems(handler.contact, withGeneralFlags = false).toMutableList()
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
|
@ -9,33 +9,3 @@
|
|||||||
|
|
||||||
package net.mamoe.mirai.internal.network.protocol.packet.chat
|
package net.mamoe.mirai.internal.network.protocol.packet.chat
|
||||||
|
|
||||||
import net.mamoe.mirai.contact.Group
|
|
||||||
import net.mamoe.mirai.internal.contact.takeSingleContent
|
|
||||||
import net.mamoe.mirai.internal.message.OnlineMessageSourceToGroupImpl
|
|
||||||
import net.mamoe.mirai.internal.network.QQAndroidClient
|
|
||||||
import net.mamoe.mirai.internal.network.protocol.packet.OutgoingPacket
|
|
||||||
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.data.MessageChain
|
|
||||||
import net.mamoe.mirai.message.data.MessageSourceKind
|
|
||||||
import net.mamoe.mirai.message.data.MusicShare
|
|
||||||
import kotlin.contracts.InvocationKind
|
|
||||||
import kotlin.contracts.contract
|
|
||||||
|
|
||||||
internal object SendMessageMultiProtocol {
|
|
||||||
inline fun createToGroup(
|
|
||||||
client: QQAndroidClient,
|
|
||||||
group: Group,
|
|
||||||
message: MessageChain,
|
|
||||||
fragmented: Boolean,
|
|
||||||
crossinline sourceCallback: (OnlineMessageSourceToGroupImpl) -> Unit
|
|
||||||
): List<OutgoingPacket> {
|
|
||||||
contract { callsInPlace(sourceCallback, InvocationKind.AT_MOST_ONCE) }
|
|
||||||
|
|
||||||
message.takeSingleContent<MusicShare>()?.let { musicShare ->
|
|
||||||
return listOf(MusicSharePacket(client, musicShare, group.id, targetKind = MessageSourceKind.GROUP))
|
|
||||||
}
|
|
||||||
|
|
||||||
return MessageSvcPbSendMsg.createToGroup(client, group, message, fragmented, sourceCallback)
|
|
||||||
}
|
|
||||||
}
|
|
@ -146,29 +146,55 @@ internal object MessageSvcPbSendMsg : OutgoingPacketFactory<MessageSvcPbSendMsg.
|
|||||||
* 发送陌生人消息
|
* 发送陌生人消息
|
||||||
*/
|
*/
|
||||||
@Suppress("FunctionName")
|
@Suppress("FunctionName")
|
||||||
internal fun createToStrangerImpl(
|
internal inline fun createToStrangerImpl(
|
||||||
client: QQAndroidClient,
|
client: QQAndroidClient,
|
||||||
target: Stranger,
|
target: Stranger,
|
||||||
message: MessageChain,
|
message: MessageChain,
|
||||||
source: OnlineMessageSourceToStrangerImpl
|
fragmented: Boolean,
|
||||||
): OutgoingPacket = buildOutgoingUniPacket(client) {
|
source: (OnlineMessageSourceToStrangerImpl) -> Unit
|
||||||
///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())
|
): List<OutgoingPacket> {
|
||||||
|
|
||||||
///return@buildOutgoingUniPacket
|
val sequenceIds = AtomicReference<IntArray>()
|
||||||
writeProtoBuf(
|
val randIds = AtomicReference<IntArray>()
|
||||||
MsgSvc.PbSendMsgReq.serializer(), MsgSvc.PbSendMsgReq(
|
return buildOutgoingMessageCommon(
|
||||||
routingHead = MsgSvc.RoutingHead(c2c = MsgSvc.C2C(toUin = target.uin)),
|
client = client,
|
||||||
contentHead = MsgComm.ContentHead(pkgNum = 1),
|
message = message,
|
||||||
msgBody = ImMsgBody.MsgBody(
|
fragmentTranslator = {
|
||||||
|
ImMsgBody.MsgBody(
|
||||||
richText = ImMsgBody.RichText(
|
richText = ImMsgBody.RichText(
|
||||||
elems = message.toRichTextElems(messageTarget = target, withGeneralFlags = true)
|
elems = it.toRichTextElems(messageTarget = target, withGeneralFlags = true)
|
||||||
)
|
)
|
||||||
),
|
)
|
||||||
msgSeq = source.sequenceIds.single(),
|
},
|
||||||
msgRand = source.internalIds.single(),
|
pbSendMsgReq = { msgBody, msgSeq, msgRand, contentHead ->
|
||||||
|
MsgSvc.PbSendMsgReq(
|
||||||
|
routingHead = MsgSvc.RoutingHead(c2c = MsgSvc.C2C(toUin = target.uin)),
|
||||||
|
contentHead = contentHead,
|
||||||
|
msgBody = msgBody,
|
||||||
|
msgSeq = msgSeq,
|
||||||
|
msgRand = msgRand,
|
||||||
syncCookie = client.syncingController.syncCookie ?: byteArrayOf()
|
syncCookie = client.syncingController.syncCookie ?: byteArrayOf()
|
||||||
// msgVia = 1
|
// msgVia = 1
|
||||||
)
|
)
|
||||||
|
},
|
||||||
|
sequenceIds = sequenceIds,
|
||||||
|
randIds = randIds,
|
||||||
|
sequenceIdsInitializer = { size ->
|
||||||
|
IntArray(size) { client.nextFriendSeq() }
|
||||||
|
},
|
||||||
|
postInit = {
|
||||||
|
source(
|
||||||
|
OnlineMessageSourceToStrangerImpl(
|
||||||
|
internalIds = randIds.get(),
|
||||||
|
sender = client.bot,
|
||||||
|
target = target,
|
||||||
|
time = currentTimeSeconds().toInt(),
|
||||||
|
sequenceIds = sequenceIds.get(),
|
||||||
|
originalMessage = message
|
||||||
|
)
|
||||||
|
)
|
||||||
|
},
|
||||||
|
doFragmented = fragmented
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -180,6 +206,7 @@ internal object MessageSvcPbSendMsg : OutgoingPacketFactory<MessageSvcPbSendMsg.
|
|||||||
client: QQAndroidClient,
|
client: QQAndroidClient,
|
||||||
targetFriend: Friend,
|
targetFriend: Friend,
|
||||||
message: MessageChain,
|
message: MessageChain,
|
||||||
|
fragmented: Boolean,
|
||||||
crossinline sourceCallback: (OnlineMessageSourceToFriendImpl) -> Unit
|
crossinline sourceCallback: (OnlineMessageSourceToFriendImpl) -> Unit
|
||||||
): List<OutgoingPacket> {
|
): List<OutgoingPacket> {
|
||||||
contract {
|
contract {
|
||||||
@ -225,7 +252,8 @@ internal object MessageSvcPbSendMsg : OutgoingPacketFactory<MessageSvcPbSendMsg.
|
|||||||
originalMessage = message
|
originalMessage = message
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
}
|
},
|
||||||
|
doFragmented = fragmented
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
/*= buildOutgoingUniPacket(client) {
|
/*= buildOutgoingUniPacket(client) {
|
||||||
@ -269,6 +297,7 @@ internal object MessageSvcPbSendMsg : OutgoingPacketFactory<MessageSvcPbSendMsg.
|
|||||||
client: QQAndroidClient,
|
client: QQAndroidClient,
|
||||||
targetMember: Member,
|
targetMember: Member,
|
||||||
message: MessageChain,
|
message: MessageChain,
|
||||||
|
fragmented: Boolean,
|
||||||
source: OnlineMessageSourceToTempImpl
|
source: OnlineMessageSourceToTempImpl
|
||||||
): OutgoingPacket = buildOutgoingUniPacket(client) {
|
): OutgoingPacket = buildOutgoingUniPacket(client) {
|
||||||
writeProtoBuf(
|
writeProtoBuf(
|
||||||
@ -439,8 +468,9 @@ internal inline fun MessageSvcPbSendMsg.createToTemp(
|
|||||||
client: QQAndroidClient,
|
client: QQAndroidClient,
|
||||||
member: Member,
|
member: Member,
|
||||||
message: MessageChain,
|
message: MessageChain,
|
||||||
|
fragmented: Boolean,
|
||||||
crossinline sourceCallback: (OnlineMessageSourceToTempImpl) -> Unit
|
crossinline sourceCallback: (OnlineMessageSourceToTempImpl) -> Unit
|
||||||
): OutgoingPacket {
|
): List<OutgoingPacket> {
|
||||||
contract {
|
contract {
|
||||||
callsInPlace(sourceCallback, InvocationKind.EXACTLY_ONCE)
|
callsInPlace(sourceCallback, InvocationKind.EXACTLY_ONCE)
|
||||||
}
|
}
|
||||||
@ -457,33 +487,27 @@ internal inline fun MessageSvcPbSendMsg.createToTemp(
|
|||||||
client,
|
client,
|
||||||
member,
|
member,
|
||||||
message,
|
message,
|
||||||
|
fragmented,
|
||||||
source
|
source
|
||||||
)
|
).let { listOf(it) }
|
||||||
}
|
}
|
||||||
|
|
||||||
internal inline fun MessageSvcPbSendMsg.createToStranger(
|
internal inline fun MessageSvcPbSendMsg.createToStranger(
|
||||||
client: QQAndroidClient,
|
client: QQAndroidClient,
|
||||||
stranger: Stranger,
|
stranger: Stranger,
|
||||||
message: MessageChain,
|
message: MessageChain,
|
||||||
|
fragmented: Boolean,
|
||||||
crossinline sourceCallback: (OnlineMessageSourceToStrangerImpl) -> Unit
|
crossinline sourceCallback: (OnlineMessageSourceToStrangerImpl) -> Unit
|
||||||
): OutgoingPacket {
|
): List<OutgoingPacket> {
|
||||||
contract {
|
contract {
|
||||||
callsInPlace(sourceCallback, InvocationKind.EXACTLY_ONCE)
|
callsInPlace(sourceCallback, InvocationKind.EXACTLY_ONCE)
|
||||||
}
|
}
|
||||||
val source = OnlineMessageSourceToStrangerImpl(
|
|
||||||
internalIds = intArrayOf(Random.nextInt().absoluteValue),
|
|
||||||
sender = client.bot,
|
|
||||||
target = stranger,
|
|
||||||
time = currentTimeSeconds().toInt(),
|
|
||||||
sequenceIds = intArrayOf(client.atomicNextMessageSequenceId()),
|
|
||||||
originalMessage = message
|
|
||||||
)
|
|
||||||
sourceCallback(source)
|
|
||||||
return createToStrangerImpl(
|
return createToStrangerImpl(
|
||||||
client,
|
client,
|
||||||
stranger,
|
stranger,
|
||||||
message,
|
message,
|
||||||
source
|
fragmented,
|
||||||
|
sourceCallback
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -491,6 +515,7 @@ internal inline fun MessageSvcPbSendMsg.createToFriend(
|
|||||||
client: QQAndroidClient,
|
client: QQAndroidClient,
|
||||||
qq: Friend,
|
qq: Friend,
|
||||||
message: MessageChain,
|
message: MessageChain,
|
||||||
|
fragmented: Boolean,
|
||||||
crossinline sourceCallback: (OnlineMessageSourceToFriendImpl) -> Unit
|
crossinline sourceCallback: (OnlineMessageSourceToFriendImpl) -> Unit
|
||||||
): List<OutgoingPacket> {
|
): List<OutgoingPacket> {
|
||||||
contract {
|
contract {
|
||||||
@ -500,6 +525,7 @@ internal inline fun MessageSvcPbSendMsg.createToFriend(
|
|||||||
client,
|
client,
|
||||||
qq,
|
qq,
|
||||||
message,
|
message,
|
||||||
|
fragmented,
|
||||||
sourceCallback
|
sourceCallback
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
Loading…
Reference in New Issue
Block a user