Commit 3f523e6f authored by Him188's avatar Him188

Long message

parent e495b91d
package net.mamoe.mirai.qqandroid.utils.cryptor
internal actual fun arraycopy(
src: ByteArray,
srcPos: Int,
dest: ByteArray,
destPos: Int,
length: Int
) = System.arraycopy(src, srcPos, dest, destPos, length)
\ No newline at end of file
...@@ -33,11 +33,13 @@ import net.mamoe.mirai.message.data.* ...@@ -33,11 +33,13 @@ import net.mamoe.mirai.message.data.*
import net.mamoe.mirai.qqandroid.contact.MemberInfoImpl import net.mamoe.mirai.qqandroid.contact.MemberInfoImpl
import net.mamoe.mirai.qqandroid.contact.QQImpl import net.mamoe.mirai.qqandroid.contact.QQImpl
import net.mamoe.mirai.qqandroid.contact.checkIsGroupImpl import net.mamoe.mirai.qqandroid.contact.checkIsGroupImpl
import net.mamoe.mirai.qqandroid.message.MessageSourceFromSendFriend
import net.mamoe.mirai.qqandroid.message.OnlineFriendImageImpl import net.mamoe.mirai.qqandroid.message.OnlineFriendImageImpl
import net.mamoe.mirai.qqandroid.message.OnlineGroupImageImpl import net.mamoe.mirai.qqandroid.message.OnlineGroupImageImpl
import net.mamoe.mirai.qqandroid.network.QQAndroidBotNetworkHandler import net.mamoe.mirai.qqandroid.network.QQAndroidBotNetworkHandler
import net.mamoe.mirai.qqandroid.network.QQAndroidClient import net.mamoe.mirai.qqandroid.network.QQAndroidClient
import net.mamoe.mirai.qqandroid.network.protocol.packet.chat.GroupInfoImpl import net.mamoe.mirai.qqandroid.network.protocol.packet.chat.GroupInfoImpl
import net.mamoe.mirai.qqandroid.network.protocol.packet.chat.MultiMsg
import net.mamoe.mirai.qqandroid.network.protocol.packet.chat.PbMessageSvc import net.mamoe.mirai.qqandroid.network.protocol.packet.chat.PbMessageSvc
import net.mamoe.mirai.qqandroid.network.protocol.packet.chat.TroopManagement import net.mamoe.mirai.qqandroid.network.protocol.packet.chat.TroopManagement
import net.mamoe.mirai.qqandroid.network.protocol.packet.list.FriendList import net.mamoe.mirai.qqandroid.network.protocol.packet.list.FriendList
...@@ -45,6 +47,8 @@ import net.mamoe.mirai.utils.* ...@@ -45,6 +47,8 @@ import net.mamoe.mirai.utils.*
import net.mamoe.mirai.utils.io.encodeToString import net.mamoe.mirai.utils.io.encodeToString
import kotlin.collections.asSequence import kotlin.collections.asSequence
import kotlin.coroutines.CoroutineContext import kotlin.coroutines.CoroutineContext
import kotlin.math.absoluteValue
import kotlin.random.Random
@OptIn(MiraiInternalAPI::class) @OptIn(MiraiInternalAPI::class)
internal expect class QQAndroidBot constructor( internal expect class QQAndroidBot constructor(
...@@ -360,6 +364,34 @@ internal abstract class QQAndroidBotBase constructor( ...@@ -360,6 +364,34 @@ internal abstract class QQAndroidBotBase constructor(
return json.parse(GroupActiveData.serializer(), rep) return json.parse(GroupActiveData.serializer(), rep)
} }
@LowLevelAPI
@MiraiExperimentalAPI
override suspend fun _lowLevelSendLongMessage(groupCode: Long, message: Message) {
val source = MessageSourceFromSendFriend(
messageRandom = Random.nextInt().absoluteValue,
senderId = client.uin,
toUin = Group.calculateGroupUinByGroupCode(groupCode),
time = currentTimeSeconds,
groupId = groupCode,
originalMessage = message.asMessageChain(),
sequenceId = 0
// sourceMessage = message
)
// TODO: 2020/3/26 util 方法来添加单例元素
val toSend = buildMessageChain {
source.originalMessage.filter { it !is MessageSource }.forEach {
add(it)
}
add(source)
}
network.run {
val response = MultiMsg.ApplyUp.createForLongMessage(this@QQAndroidBotBase.client, toSend, groupCode)
.sendAndExpect<MultiMsg.ApplyUp.Response>()
println(response._miraiContentToString())
}
}
override suspend fun queryImageUrl(image: Image): String = when (image) { override suspend fun queryImageUrl(image: Image): String = when (image) {
is OnlineFriendImageImpl -> image.originUrl is OnlineFriendImageImpl -> image.originUrl
is OnlineGroupImageImpl -> image.originUrl is OnlineGroupImageImpl -> image.originUrl
......
...@@ -254,7 +254,7 @@ internal class MessageSourceFromSendGroup( ...@@ -254,7 +254,7 @@ internal class MessageSourceFromSendGroup(
override val groupId: Long, override val groupId: Long,
override val originalMessage: MessageChain override val originalMessage: MessageChain
) : MessageSourceFromSend() { ) : MessageSourceFromSend() {
private lateinit var sequenceIdDeferred: Deferred<Int> internal lateinit var sequenceIdDeferred: Deferred<Int>
@OptIn(ExperimentalCoroutinesApi::class) @OptIn(ExperimentalCoroutinesApi::class)
override val id: Long override val id: Long
......
...@@ -6,6 +6,7 @@ ...@@ -6,6 +6,7 @@
* *
* https://github.com/mamoe/mirai/blob/master/LICENSE * https://github.com/mamoe/mirai/blob/master/LICENSE
*/ */
@file: OptIn(MiraiExperimentalAPI::class, MiraiInternalAPI::class, LowLevelAPI::class, ExperimentalUnsignedTypes::class)
package net.mamoe.mirai.qqandroid.message package net.mamoe.mirai.qqandroid.message
...@@ -218,6 +219,8 @@ private val atAllData = ImMsgBody.Elem( ...@@ -218,6 +219,8 @@ private val atAllData = ImMsgBody.Elem(
) )
) )
private val UNSUPPORTED_MERGED_MESSAGE_PLAIN = PlainText("你的QQ暂不支持查看[转发多条消息],请期待后续版本。")
@OptIn(MiraiInternalAPI::class, MiraiExperimentalAPI::class) @OptIn(MiraiInternalAPI::class, MiraiExperimentalAPI::class)
internal fun MessageChain.toRichTextElems(forGroup: Boolean): MutableList<ImMsgBody.Elem> { internal fun MessageChain.toRichTextElems(forGroup: Boolean): MutableList<ImMsgBody.Elem> {
val elements = mutableListOf<ImMsgBody.Elem>() val elements = mutableListOf<ImMsgBody.Elem>()
...@@ -233,31 +236,49 @@ internal fun MessageChain.toRichTextElems(forGroup: Boolean): MutableList<ImMsgB ...@@ -233,31 +236,49 @@ internal fun MessageChain.toRichTextElems(forGroup: Boolean): MutableList<ImMsgB
fun transformOneMessage(it: Message) { fun transformOneMessage(it: Message) {
if (it is RichMessage) {
val content = MiraiPlatformUtils.zip(it.content.toByteArray())
when (it) {
is LightApp -> elements.add(
ImMsgBody.Elem(
lightApp = ImMsgBody.LightAppElem(
data = byteArrayOf(1) + content
)
)
)
is MergedForwardedMessage -> {
elements.add(
ImMsgBody.Elem(
richMsg = ImMsgBody.RichMsg(
serviceId = 35,
template1 = byteArrayOf(1) + content
)
)
)
transformOneMessage(UNSUPPORTED_MERGED_MESSAGE_PLAIN) // required
}
else -> elements.add(
ImMsgBody.Elem(
richMsg = ImMsgBody.RichMsg(
serviceId = when (it) {
is XmlMessage -> 60
is JsonMessage -> 1
is MergedForwardedMessage -> 35
else -> error("unsupported RichMessage: ${it::class.simpleName}")
},
template1 = byteArrayOf(1) + content
)
)
)
}
}
when (it) { when (it) {
is PlainText -> elements.add(ImMsgBody.Elem(text = ImMsgBody.Text(str = it.stringValue))) is PlainText -> elements.add(ImMsgBody.Elem(text = ImMsgBody.Text(str = it.stringValue)))
is At -> { is At -> {
elements.add(ImMsgBody.Elem(text = it.toJceData())) elements.add(ImMsgBody.Elem(text = it.toJceData()))
elements.add(ImMsgBody.Elem(text = ImMsgBody.Text(str = " "))) elements.add(ImMsgBody.Elem(text = ImMsgBody.Text(str = " ")))
} }
is LightApp -> elements.add(
ImMsgBody.Elem(
lightApp = ImMsgBody.LightAppElem(
data = byteArrayOf(1) + MiraiPlatformUtils.zip(it.content.toByteArray())
)
)
)
is RichMessage -> elements.add(
ImMsgBody.Elem(
richMsg = ImMsgBody.RichMsg(
serviceId = when (it) {
is XmlMessage -> 60
is JsonMessage -> 1
else -> error("unsupported RichMessage")
},
template1 = byteArrayOf(1) + MiraiPlatformUtils.zip(it.content.toByteArray())
)
)
)
is OfflineGroupImage -> elements.add(ImMsgBody.Elem(customFace = it.toJceData())) is OfflineGroupImage -> elements.add(ImMsgBody.Elem(customFace = it.toJceData()))
is OnlineGroupImageImpl -> elements.add(ImMsgBody.Elem(customFace = it.delegate)) is OnlineGroupImageImpl -> elements.add(ImMsgBody.Elem(customFace = it.delegate))
is OnlineFriendImageImpl -> elements.add(ImMsgBody.Elem(notOnlineImage = it.delegate)) is OnlineFriendImageImpl -> elements.add(ImMsgBody.Elem(notOnlineImage = it.delegate))
...@@ -267,16 +288,17 @@ internal fun MessageChain.toRichTextElems(forGroup: Boolean): MutableList<ImMsgB ...@@ -267,16 +288,17 @@ internal fun MessageChain.toRichTextElems(forGroup: Boolean): MutableList<ImMsgB
is QuoteReplyToSend -> { is QuoteReplyToSend -> {
if (forGroup) { if (forGroup) {
check(it is QuoteReplyToSend.ToGroup) { check(it is QuoteReplyToSend.ToGroup) {
"sending a quote to group using QuoteReplyToSend.ToFriend" "sending a quote to group using QuoteReplyToSend.ToFriend is prohibited"
} }
if (it.sender is Member) { if (it.sender is Member) {
transformOneMessage(it.createAt()) transformOneMessage(it.createAt())
} }
transformOneMessage(" ".toMessage()) transformOneMessage(PlainText(" "))
} }
} }
is QuoteReply, is QuoteReply,
is MessageSource -> { is MessageSource,
-> {
} }
else -> error("unsupported message type: ${it::class.simpleName}") else -> error("unsupported message type: ${it::class.simpleName}")
...@@ -358,7 +380,7 @@ internal fun MsgComm.Msg.toMessageChain(): MessageChain { ...@@ -358,7 +380,7 @@ internal fun MsgComm.Msg.toMessageChain(): MessageChain {
return buildMessageChain(elements.size + 1) { return buildMessageChain(elements.size + 1) {
+MessageSourceFromMsg(delegate = this@toMessageChain) +MessageSourceFromMsg(delegate = this@toMessageChain)
elements.joinToMessageChain(this) elements.joinToMessageChain(this)
}.removeAtIfHasQuoteReply() }.cleanupRubbishMessageElements()
} }
// These two functions are not identical, dont combine. // These two functions are not identical, dont combine.
...@@ -369,11 +391,31 @@ internal fun ImMsgBody.SourceMsg.toMessageChain(): MessageChain { ...@@ -369,11 +391,31 @@ internal fun ImMsgBody.SourceMsg.toMessageChain(): MessageChain {
return buildMessageChain(elements.size + 1) { return buildMessageChain(elements.size + 1) {
+MessageSourceFromServer(delegate = this@toMessageChain) +MessageSourceFromServer(delegate = this@toMessageChain)
elements.joinToMessageChain(this) elements.joinToMessageChain(this)
}.removeAtIfHasQuoteReply() }.cleanupRubbishMessageElements()
}
private fun MessageChain.cleanupRubbishMessageElements(): MessageChain {
var last: SingleMessage? = null
return buildMessageChain(initialSize = this.count()) {
this@cleanupRubbishMessageElements.forEach { element ->
if (last == null) {
last = element
return@forEach
} else {
if (last is MergedForwardedMessage && element is PlainText) {
if (element == UNSUPPORTED_MERGED_MESSAGE_PLAIN) {
last = element
return@forEach
}
}
}
add(element)
last = element
}
}
} }
private fun MessageChain.removeAtIfHasQuoteReply(): MessageChain =
this
/* /*
if (this.any<QuoteReply>()) { if (this.any<QuoteReply>()) {
var removed = false var removed = false
...@@ -387,9 +429,6 @@ private fun MessageChain.removeAtIfHasQuoteReply(): MessageChain = ...@@ -387,9 +429,6 @@ private fun MessageChain.removeAtIfHasQuoteReply(): MessageChain =
}.asMessageChain() }.asMessageChain()
} else this*/ } else this*/
@OptIn(
MiraiInternalAPI::class, ExperimentalUnsignedTypes::class, MiraiDebugAPI::class, LowLevelAPI::class
)
internal fun List<ImMsgBody.Elem>.joinToMessageChain(message: MessageChainBuilder) { internal fun List<ImMsgBody.Elem>.joinToMessageChain(message: MessageChainBuilder) {
this.forEach { this.forEach {
when { when {
...@@ -425,6 +464,7 @@ internal fun List<ImMsgBody.Elem>.joinToMessageChain(message: MessageChainBuilde ...@@ -425,6 +464,7 @@ internal fun List<ImMsgBody.Elem>.joinToMessageChain(message: MessageChainBuilde
when (it.richMsg.serviceId) { when (it.richMsg.serviceId) {
1 -> message.add(JsonMessage(content)) 1 -> message.add(JsonMessage(content))
60 -> message.add(XmlMessage(content)) 60 -> message.add(XmlMessage(content))
35 -> message.add(MergedForwardedMessage(content))
else -> { else -> {
@Suppress("DEPRECATION") @Suppress("DEPRECATION")
MiraiLogger.debug { MiraiLogger.debug {
......
...@@ -101,7 +101,7 @@ internal open class QQAndroidClient( ...@@ -101,7 +101,7 @@ internal open class QQAndroidClient(
var openAppId: Long = 715019303L var openAppId: Long = 715019303L
val apkVersionName: ByteArray get() = "8.2.7".toByteArray() val apkVersionName: ByteArray get() = "8.2.7".toByteArray()
val buildVer: String get() = "8.2.7.4410" val buildVer: String get() = "8.2.7.4410" // 8.2.0.1296
private val messageSequenceId: AtomicInt = atomic(22911) private val messageSequenceId: AtomicInt = atomic(22911)
internal fun atomicNextMessageSequenceId(): Int = messageSequenceId.getAndAdd(2) internal fun atomicNextMessageSequenceId(): Int = messageSequenceId.getAndAdd(2)
......
package net.mamoe.mirai.qqandroid.network.protocol.data.proto
import kotlinx.serialization.Serializable
import kotlinx.serialization.protobuf.ProtoId
import net.mamoe.mirai.qqandroid.io.ProtoBuf
import net.mamoe.mirai.qqandroid.network.protocol.packet.EMPTY_BYTE_ARRAY
internal class LongMsg : ProtoBuf {
@Serializable
class MsgDeleteReq(
@ProtoId(1) val msgResid: ByteArray = EMPTY_BYTE_ARRAY,
@ProtoId(2) val msgType: Int = 0
) : ProtoBuf
@Serializable
class MsgDeleteRsp(
@ProtoId(1) val result: Int = 0,
@ProtoId(2) val msgResid: ByteArray = EMPTY_BYTE_ARRAY
) : ProtoBuf
@Serializable
class MsgDownReq(
@ProtoId(1) val srcUin: Int = 0,
@ProtoId(2) val msgResid: ByteArray = EMPTY_BYTE_ARRAY,
@ProtoId(3) val msgType: Int = 0,
@ProtoId(4) val needCache: Int = 0
) : ProtoBuf
@Serializable
class MsgDownRsp(
@ProtoId(1) val result: Int = 0,
@ProtoId(2) val msgResid: ByteArray = EMPTY_BYTE_ARRAY,
@ProtoId(3) val msgContent: ByteArray = EMPTY_BYTE_ARRAY
) : ProtoBuf
@Serializable
class MsgUpReq(
@ProtoId(1) val msgType: Int = 0,
@ProtoId(2) val dstUin: Long = 0L,
@ProtoId(3) val msgId: Int = 0,
@ProtoId(4) val msgContent: ByteArray = EMPTY_BYTE_ARRAY,
@ProtoId(5) val storeType: Int = 0,
@ProtoId(6) val msgUkey: ByteArray = EMPTY_BYTE_ARRAY,
@ProtoId(7) val needCache: Int = 0
) : ProtoBuf
@Serializable
class MsgUpRsp(
@ProtoId(1) val result: Int = 0,
@ProtoId(2) val msgId: Int = 0,
@ProtoId(3) val msgResid: ByteArray = EMPTY_BYTE_ARRAY
) : ProtoBuf
@Serializable
class ReqBody(
@ProtoId(1) val subcmd: Int = 0,
@ProtoId(2) val termType: Int = 0,
@ProtoId(3) val platformType: Int = 0,
@ProtoId(4) val msgUpReq: List<LongMsg.MsgUpReq>? = null,
@ProtoId(5) val msgDownReq: List<LongMsg.MsgDownReq>? = null,
@ProtoId(6) val msgDelReq: List<LongMsg.MsgDeleteReq>? = null,
@ProtoId(10) val agentType: Int = 0
) : ProtoBuf
@Serializable
class RspBody(
@ProtoId(1) val subcmd: Int = 0,
@ProtoId(2) val msgUpRsp: List<LongMsg.MsgUpRsp>? = null,
@ProtoId(3) val msgDownRsp: List<LongMsg.MsgDownRsp>? = null,
@ProtoId(4) val msgDelRsp: List<LongMsg.MsgDeleteRsp>? = null
) : ProtoBuf
}
\ No newline at end of file
package net.mamoe.mirai.qqandroid.network.protocol.data.proto
import kotlinx.serialization.Serializable
import kotlinx.serialization.protobuf.ProtoId
import net.mamoe.mirai.qqandroid.io.ProtoBuf
import net.mamoe.mirai.qqandroid.network.protocol.packet.EMPTY_BYTE_ARRAY
internal class MsgTransmit : ProtoBuf {
@Serializable
class PbMultiMsgItem(
@ProtoId(1) val fileName: String = "",
@ProtoId(2) val buffer: ByteArray = EMPTY_BYTE_ARRAY
) : ProtoBuf
@Serializable
class PbMultiMsgNew(
@ProtoId(1) val msg: List<MsgComm.Msg>? = null
) : ProtoBuf
@Serializable
class PbMultiMsgTransmit(
@ProtoId(1) val msg: List<MsgComm.Msg>? = null,
@ProtoId(2) val pbItemList: List<MsgTransmit.PbMultiMsgItem>? = null
) : ProtoBuf
}
\ No newline at end of file
package net.mamoe.mirai.qqandroid.network.protocol.data.proto
import kotlinx.serialization.Serializable
import kotlinx.serialization.protobuf.ProtoId
import net.mamoe.mirai.qqandroid.io.ProtoBuf
import net.mamoe.mirai.qqandroid.network.protocol.packet.EMPTY_BYTE_ARRAY
@Serializable
internal class MultiMsg : ProtoBuf {
@Serializable
class ExternMsg(
@ProtoId(1) val channelType: Int = 0
) : ProtoBuf
@Serializable
class MultiMsgApplyDownReq(
@ProtoId(1) val msgResid: ByteArray = EMPTY_BYTE_ARRAY,
@ProtoId(2) val msgType: Int = 0,
@ProtoId(3) val srcUin: Long = 0L
) : ProtoBuf
@Serializable
class MultiMsgApplyDownRsp(
@ProtoId(1) val result: Int = 0,
@ProtoId(2) val thumbDownPara: ByteArray = EMPTY_BYTE_ARRAY,
@ProtoId(3) val msgKey: ByteArray = EMPTY_BYTE_ARRAY,
@ProtoId(4) val uint32DownIp: List<Int>? = null,
@ProtoId(5) val uint32DownPort: List<Int>? = null,
@ProtoId(6) val msgResid: ByteArray = EMPTY_BYTE_ARRAY,
@ProtoId(7) val msgExternInfo: MultiMsg.ExternMsg? = null,
@ProtoId(8) val bytesDownIpV6: List<ByteArray>? = null,
@ProtoId(9) val uint32DownV6Port: List<Int>? = null
) : ProtoBuf
@Serializable
class MultiMsgApplyUpReq(
@ProtoId(1) val dstUin: Long = 0L,
@ProtoId(2) val msgSize: Long = 0L,
@ProtoId(3) val msgMd5: ByteArray = EMPTY_BYTE_ARRAY,
@ProtoId(4) val msgType: Int = 0,
@ProtoId(5) val applyId: Int = 0
) : ProtoBuf
@Serializable
class MultiMsgApplyUpRsp(
@ProtoId(1) val result: Int = 0,
@ProtoId(2) val msgResid: ByteArray = EMPTY_BYTE_ARRAY,
@ProtoId(3) val msgUkey: ByteArray = EMPTY_BYTE_ARRAY,
@ProtoId(4) val uint32UpIp: List<Int>? = null,
@ProtoId(5) val uint32UpPort: List<Int>? = null,
@ProtoId(6) val blockSize: Long = 0L,
@ProtoId(7) val upOffset: Long = 0L,
@ProtoId(8) val applyId: Int = 0,
@ProtoId(9) val msgKey: ByteArray = EMPTY_BYTE_ARRAY,
@ProtoId(10) val msgSig: ByteArray = EMPTY_BYTE_ARRAY,
@ProtoId(11) val msgExternInfo: MultiMsg.ExternMsg? = null,
@ProtoId(12) val bytesUpIpV6: List<ByteArray>? = null,
@ProtoId(13) val uint32UpV6Port: List<Int>? = null
) : ProtoBuf
@Serializable
class ReqBody(
@ProtoId(1) val subcmd: Int = 0,
@ProtoId(2) val termType: Int = 0,
@ProtoId(3) val platformType: Int = 0,
@ProtoId(4) val netType: Int = 0,
@ProtoId(5) val buildVer: String = "",
@ProtoId(6) val multimsgApplyupReq: List<MultiMsg.MultiMsgApplyUpReq>? = null,
@ProtoId(7) val multimsgApplydownReq: List<MultiMsg.MultiMsgApplyDownReq>? = null,
@ProtoId(8) val buType: Int = 0,
@ProtoId(9) val reqChannelType: Int = 0
) : ProtoBuf
@Serializable
class RspBody(
@ProtoId(1) val subcmd: Int = 0,
@ProtoId(2) val multimsgApplyupRsp: List<MultiMsg.MultiMsgApplyUpRsp>? = null,
@ProtoId(3) val multimsgApplydownRsp: List<MultiMsg.MultiMsgApplyDownRsp>? = null
) : ProtoBuf
}
...@@ -11,9 +11,10 @@ package net.mamoe.mirai.qqandroid.network.protocol.packet ...@@ -11,9 +11,10 @@ package net.mamoe.mirai.qqandroid.network.protocol.packet
import kotlinx.io.core.* import kotlinx.io.core.*
import kotlinx.io.pool.useInstance import kotlinx.io.pool.useInstance
import net.mamoe.mirai.qqandroid.network.Packet
import net.mamoe.mirai.event.Event import net.mamoe.mirai.event.Event
import net.mamoe.mirai.qqandroid.QQAndroidBot import net.mamoe.mirai.qqandroid.QQAndroidBot
import net.mamoe.mirai.qqandroid.network.Packet
import net.mamoe.mirai.qqandroid.network.protocol.packet.chat.MultiMsg
import net.mamoe.mirai.qqandroid.network.protocol.packet.chat.PbMessageSvc import net.mamoe.mirai.qqandroid.network.protocol.packet.chat.PbMessageSvc
import net.mamoe.mirai.qqandroid.network.protocol.packet.chat.TroopManagement import net.mamoe.mirai.qqandroid.network.protocol.packet.chat.TroopManagement
import net.mamoe.mirai.qqandroid.network.protocol.packet.chat.image.ImgStore import net.mamoe.mirai.qqandroid.network.protocol.packet.chat.image.ImgStore
...@@ -144,7 +145,8 @@ internal object KnownPacketFactories { ...@@ -144,7 +145,8 @@ internal object KnownPacketFactories {
TroopManagement.EditGroupNametag, TroopManagement.EditGroupNametag,
TroopManagement.Kick, TroopManagement.Kick,
Heartbeat.Alive, Heartbeat.Alive,
PbMessageSvc.PbMsgWithDraw PbMessageSvc.PbMsgWithDraw,
MultiMsg.ApplyUp
) )
object IncomingFactories : List<IncomingPacketFactory<*>> by mutableListOf( object IncomingFactories : List<IncomingPacketFactory<*>> by mutableListOf(
......
/*
* 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.qqandroid.utils.cryptor
import net.mamoe.mirai.utils.io.toUHexString
internal object MultiMsgCryptor {
private val impl = class_1457()
fun decrypt(data: ByteArray, offset: Int, length: Int, key: ByteArray): ByteArray {
return this.impl.method_67425(data, offset, length, key) ?: error("MultiMsgCryptor decypt failed: key=${key.toUHexString()}, data=${data.drop(offset).take(length).toByteArray().toUHexString()}")
}
fun decrypt(data: ByteArray, key: ByteArray): ByteArray {
return this.impl.method_67426(data, key) ?: error("MultiMsgCryptor decrypt failed: key=${key.toUHexString()}, data=${data.toUHexString()}")
}
fun enableResultRandom(enabled: Boolean) {
this.impl.method_67424(enabled)
}
fun encrypt(data: ByteArray, key: ByteArray): ByteArray {
return this.impl.method_67427(data, key)
}
}
\ No newline at end of file
package net.mamoe.mirai.qqandroid.utils.cryptor
internal actual fun arraycopy(
src: ByteArray,
srcPos: Int,
dest: ByteArray,
destPos: Int,
length: Int
) = System.arraycopy(src, srcPos, dest, destPos, length)
\ No newline at end of file
...@@ -13,6 +13,7 @@ import kotlinx.coroutines.Job ...@@ -13,6 +13,7 @@ import kotlinx.coroutines.Job
import net.mamoe.mirai.contact.Group import net.mamoe.mirai.contact.Group
import net.mamoe.mirai.contact.QQ import net.mamoe.mirai.contact.QQ
import net.mamoe.mirai.data.* import net.mamoe.mirai.data.*
import net.mamoe.mirai.message.data.Message
import net.mamoe.mirai.message.data.MessageSource import net.mamoe.mirai.message.data.MessageSource
import net.mamoe.mirai.utils.MiraiExperimentalAPI import net.mamoe.mirai.utils.MiraiExperimentalAPI
import net.mamoe.mirai.utils.MiraiInternalAPI import net.mamoe.mirai.utils.MiraiInternalAPI
...@@ -139,6 +140,14 @@ interface LowLevelBotAPIAccessor { ...@@ -139,6 +140,14 @@ interface LowLevelBotAPIAccessor {
@LowLevelAPI @LowLevelAPI
@MiraiExperimentalAPI @MiraiExperimentalAPI
suspend fun _lowLevelGetGroupActiveData(groupId: Long): GroupActiveData suspend fun _lowLevelGetGroupActiveData(groupId: Long): GroupActiveData
/**
* 发送长消息
*/
@SinceMirai("0.31.0")
@LowLevelAPI
@MiraiExperimentalAPI
suspend fun _lowLevelSendLongMessage(groupCode: Long, message: Message)
} }
/** /**
......
...@@ -15,7 +15,6 @@ package net.mamoe.mirai.message.data ...@@ -15,7 +15,6 @@ package net.mamoe.mirai.message.data
import kotlin.jvm.JvmMultifileClass import kotlin.jvm.JvmMultifileClass
import kotlin.jvm.JvmName import kotlin.jvm.JvmName
import kotlin.jvm.JvmStatic
import kotlin.jvm.JvmSynthetic import kotlin.jvm.JvmSynthetic
/** /**
...@@ -28,26 +27,13 @@ class PlainText(val stringValue: String) : ...@@ -28,26 +27,13 @@ class PlainText(val stringValue: String) :
Comparable<String> by stringValue, Comparable<String> by stringValue,
CharSequence by stringValue { CharSequence by stringValue {
@Suppress("unused")
constructor(charSequence: CharSequence) : this(charSequence.toString()) constructor(charSequence: CharSequence) : this(charSequence.toString())
override operator fun contains(sub: String): Boolean = sub in stringValue override operator fun contains(sub: String): Boolean = sub in stringValue
override fun toString(): String = stringValue override fun toString(): String = stringValue
companion object Key : Message.Key<PlainText> { companion object Key : Message.Key<PlainText>
@JvmStatic
val Empty = PlainText("")
@JvmStatic
val Null = PlainText("null")
inline fun of(value: String): PlainText {
return PlainText(value)
}
inline fun of(value: CharSequence): PlainText {
return PlainText(value)
}
}
} }
/** /**
......
...@@ -33,6 +33,40 @@ interface RichMessage : MessageContent { ...@@ -33,6 +33,40 @@ interface RichMessage : MessageContent {
@SinceMirai("0.30.0") @SinceMirai("0.30.0")
companion object Templates : Message.Key<RichMessage> { companion object Templates : Message.Key<RichMessage> {
/**
* 合并转发.
*/
@MiraiExperimentalAPI
fun mergedForward(): Nothing {
TODO()
}
/**
* 长消息.
*
* @param brief 消息内容纯文本, 显示在图片的前面
*/
@MiraiExperimentalAPI
fun longMessage(brief: String, resId: String, time: Long): XmlMessage {
val template = """
<?xml version='1.0' encoding='UTF-8' standalone='yes' ?>
<msg serviceID="35" templateID="1" action="viewMultiMsg"
brief="$brief"
m_resid="$resId"
m_fileName="$time" sourceMsgId="0" url=""
flag="3" adverSign="0" multiMsgFlag="1">
<item layout="1">
<title>$brief…</title>
<hr hidden="false" style="0"/>
<summary>点击查看完整消息</summary>
</item>
<source name="聊天记录" icon="" action="" appid="-1"/>
</msg>
"""
return XmlMessage(template)
}
@MiraiExperimentalAPI @MiraiExperimentalAPI
@SinceMirai("0.30.0") @SinceMirai("0.30.0")
fun share(url: String, title: String? = null, content: String? = null, coverUrl: String? = null): XmlMessage = fun share(url: String, title: String? = null, content: String? = null, coverUrl: String? = null): XmlMessage =
...@@ -107,6 +141,19 @@ class XmlMessage constructor(override val content: String) : RichMessage { ...@@ -107,6 +141,19 @@ class XmlMessage constructor(override val content: String) : RichMessage {
override fun toString(): String = content override fun toString(): String = content
} }
/**
* 合并转发消息
*/
@SinceMirai("0.31.0")
@MiraiExperimentalAPI
class MergedForwardedMessage(override val content: String) : RichMessage {
companion object Key : Message.Key<XmlMessage>
// serviceId = 35
override fun toString(): String = content
}
/** /**
* 构造一条 XML 消息 * 构造一条 XML 消息
*/ */
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment