mirror of
https://github.com/mamoe/mirai.git
synced 2025-04-24 20:43:33 +08:00
Support MessageReceipt.quoteReply
for group
This commit is contained in:
parent
f57d0242ad
commit
3748963a03
mirai-core-qqandroid/src/commonMain/kotlin/net/mamoe/mirai/qqandroid
mirai-core/src/commonMain/kotlin/net.mamoe.mirai
@ -20,6 +20,7 @@ import net.mamoe.mirai.event.events.MessageSendEvent.FriendMessageSendEvent
|
||||
import net.mamoe.mirai.event.events.MessageSendEvent.GroupMessageSendEvent
|
||||
import net.mamoe.mirai.message.MessageReceipt
|
||||
import net.mamoe.mirai.message.data.*
|
||||
import net.mamoe.mirai.qqandroid.message.MessageSourceFromSendGroup
|
||||
import net.mamoe.mirai.qqandroid.network.highway.HighwayHelper
|
||||
import net.mamoe.mirai.qqandroid.network.highway.postImage
|
||||
import net.mamoe.mirai.qqandroid.network.protocol.data.jce.StTroopMemberInfo
|
||||
@ -79,10 +80,12 @@ internal class QQImpl(
|
||||
bot.client,
|
||||
id,
|
||||
event.message
|
||||
) { source = it }.sendAndExpect<MessageSvc.PbSendMsg.Response>() is MessageSvc.PbSendMsg.Response.SUCCESS
|
||||
) {
|
||||
source = it
|
||||
}.sendAndExpect<MessageSvc.PbSendMsg.Response>() is MessageSvc.PbSendMsg.Response.SUCCESS
|
||||
) { "send message failed" }
|
||||
}
|
||||
return MessageReceipt(source, this)
|
||||
return MessageReceipt(source, this, null)
|
||||
}
|
||||
|
||||
override suspend fun uploadImage(image: ExternalImage): Image = try {
|
||||
@ -117,7 +120,14 @@ internal class QQImpl(
|
||||
ImageUploadEvent.Succeed(this@QQImpl, image, it).broadcast()
|
||||
}
|
||||
is LongConn.OffPicUp.Response.RequireUpload -> {
|
||||
Http.postImage("0x6ff0070", bot.uin, null, imageInput = image.input, inputSize = image.inputSize, uKeyHex = response.uKey.toUHexString(""))
|
||||
Http.postImage(
|
||||
"0x6ff0070",
|
||||
bot.uin,
|
||||
null,
|
||||
imageInput = image.input,
|
||||
inputSize = image.inputSize,
|
||||
uKeyHex = response.uKey.toUHexString("")
|
||||
)
|
||||
//HighwayHelper.uploadImage(
|
||||
// client = bot.client,
|
||||
// serverIp = response.serverIp[0].toIpV4AddressString(),
|
||||
@ -527,7 +537,8 @@ internal class GroupImpl(
|
||||
|
||||
|
||||
override operator fun get(id: Long): Member {
|
||||
return members.delegate.filteringGetOrNull { it.id == id } ?: throw NoSuchElementException("member $id not found in group $uin")
|
||||
return members.delegate.filteringGetOrNull { it.id == id }
|
||||
?: throw NoSuchElementException("member $id not found in group $uin")
|
||||
}
|
||||
|
||||
override fun contains(id: Long): Boolean {
|
||||
@ -544,21 +555,22 @@ internal class GroupImpl(
|
||||
if (event.isCancelled) {
|
||||
throw EventCancelledException("cancelled by FriendMessageSendEvent")
|
||||
}
|
||||
lateinit var source: MessageSvc.PbSendMsg.MessageSourceFromSendGroup
|
||||
lateinit var source: MessageSourceFromSendGroup
|
||||
bot.network.run {
|
||||
val response: MessageSvc.PbSendMsg.Response = MessageSvc.PbSendMsg.ToGroup(
|
||||
bot.client,
|
||||
id,
|
||||
event.message
|
||||
) { source = it }.sendAndExpect()
|
||||
) {
|
||||
source = it
|
||||
source.startWaitingSequenceId(this)
|
||||
}.sendAndExpect()
|
||||
check(
|
||||
response is MessageSvc.PbSendMsg.Response.SUCCESS
|
||||
) { "send message failed: $response" }
|
||||
}
|
||||
|
||||
source.startWaitingSequenceId(this)
|
||||
|
||||
return MessageReceipt(source, this)
|
||||
return MessageReceipt(source, this, botAsMember)
|
||||
}
|
||||
|
||||
override suspend fun uploadImage(image: ExternalImage): Image = try {
|
||||
|
@ -14,6 +14,7 @@ import io.ktor.client.statement.HttpResponse
|
||||
import io.ktor.utils.io.ByteReadChannel
|
||||
import net.mamoe.mirai.BotAccount
|
||||
import net.mamoe.mirai.BotImpl
|
||||
import net.mamoe.mirai.LowLevelAPI
|
||||
import net.mamoe.mirai.contact.*
|
||||
import net.mamoe.mirai.data.AddFriendResult
|
||||
import net.mamoe.mirai.data.FriendInfo
|
||||
@ -84,21 +85,28 @@ internal abstract class QQAndroidBotBase constructor(
|
||||
return groups.delegate.getOrNull(uin)
|
||||
}
|
||||
|
||||
override suspend fun queryGroupList(): Sequence<Long> {
|
||||
@UseExperimental(LowLevelAPI::class)
|
||||
override suspend fun _lowLevelQueryGroupList(): Sequence<Long> {
|
||||
return network.run {
|
||||
FriendList.GetTroopListSimplify(bot.client)
|
||||
.sendAndExpect<FriendList.GetTroopListSimplify.Response>(retry = 2)
|
||||
}.groups.asSequence().map { it.groupUin.shl(32) and it.groupCode }
|
||||
}
|
||||
|
||||
override suspend fun queryGroupInfo(groupCode: Long): GroupInfo = network.run {
|
||||
@UseExperimental(LowLevelAPI::class)
|
||||
override suspend fun _lowLevelQueryGroupInfo(groupCode: Long): GroupInfo = network.run {
|
||||
TroopManagement.GetGroupInfo(
|
||||
client = bot.client,
|
||||
groupCode = groupCode
|
||||
).sendAndExpect<GroupInfoImpl>(retry = 2)
|
||||
}
|
||||
|
||||
override suspend fun queryGroupMemberList(groupUin: Long, groupCode: Long, ownerId: Long): Sequence<MemberInfo> =
|
||||
@UseExperimental(LowLevelAPI::class)
|
||||
override suspend fun _lowLevelQueryGroupMemberList(
|
||||
groupUin: Long,
|
||||
groupCode: Long,
|
||||
ownerId: Long
|
||||
): Sequence<MemberInfo> =
|
||||
network.run {
|
||||
var nextUin = 0L
|
||||
var sequence = sequenceOf<MemberInfoImpl>()
|
||||
@ -125,7 +133,7 @@ internal abstract class QQAndroidBotBase constructor(
|
||||
}
|
||||
|
||||
override suspend fun recall(source: MessageSource) {
|
||||
if (source.qqId != uin && source.groupId != 0L) {
|
||||
if (source.senderId != uin && source.groupId != 0L) {
|
||||
getGroup(source.groupId).checkBotPermissionOperator()
|
||||
}
|
||||
|
||||
@ -136,7 +144,7 @@ internal abstract class QQAndroidBotBase constructor(
|
||||
if (source.groupId == 0L) {
|
||||
PbMessageSvc.PbMsgWithDraw.Friend(
|
||||
bot.client,
|
||||
source.qqId,
|
||||
source.senderId,
|
||||
source.sequenceId,
|
||||
source.messageRandom,
|
||||
source.time
|
||||
@ -144,7 +152,7 @@ internal abstract class QQAndroidBotBase constructor(
|
||||
} else {
|
||||
MessageRecallEvent.GroupRecall(
|
||||
bot,
|
||||
source.qqId,
|
||||
source.senderId,
|
||||
source.id,
|
||||
source.time.toInt(),
|
||||
null,
|
||||
@ -162,6 +170,7 @@ internal abstract class QQAndroidBotBase constructor(
|
||||
}
|
||||
}
|
||||
|
||||
@UseExperimental(LowLevelAPI::class)
|
||||
override suspend fun _lowLevelRecallFriendMessage(friendId: Long, messageId: Long, time: Long) {
|
||||
network.run {
|
||||
val response: PbMessageSvc.PbMsgWithDraw.Response =
|
||||
@ -172,6 +181,7 @@ internal abstract class QQAndroidBotBase constructor(
|
||||
}
|
||||
}
|
||||
|
||||
@UseExperimental(LowLevelAPI::class)
|
||||
override suspend fun _lowLevelRecallGroupMessage(groupId: Long, messageId: Long) {
|
||||
network.run {
|
||||
val response: PbMessageSvc.PbMsgWithDraw.Response =
|
||||
|
@ -1,138 +0,0 @@
|
||||
/*
|
||||
* 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.message
|
||||
|
||||
import net.mamoe.mirai.contact.Group
|
||||
import net.mamoe.mirai.message.data.MessageSource
|
||||
import net.mamoe.mirai.message.data.messageRandom
|
||||
import net.mamoe.mirai.qqandroid.io.serialization.loadAs
|
||||
import net.mamoe.mirai.qqandroid.io.serialization.toByteArray
|
||||
import net.mamoe.mirai.qqandroid.network.protocol.data.proto.ImMsgBody
|
||||
import net.mamoe.mirai.qqandroid.network.protocol.data.proto.MsgComm
|
||||
import net.mamoe.mirai.qqandroid.network.protocol.data.proto.SourceMsg
|
||||
|
||||
internal inline class MessageSourceFromServer(
|
||||
val delegate: ImMsgBody.SourceMsg
|
||||
) : MessageSource {
|
||||
override val time: Long get() = delegate.time.toLong() and 0xFFFFFFFF
|
||||
|
||||
override val id: Long
|
||||
get() = (delegate.origSeqs?.firstOrNull() ?: error("cannot find sequenceId from ImMsgBody.SourceMsg")).toLong().shl(32) or
|
||||
(delegate.pbReserve.loadAs(SourceMsg.ResvAttr.serializer()).origUids!!.toInt()).toLong().and(0xFFFFFFFF)
|
||||
|
||||
|
||||
override suspend fun ensureSequenceIdAvailable() {
|
||||
// nothing to do
|
||||
}
|
||||
|
||||
// override val sourceMessage: MessageChain get() = delegate.toMessageChain()
|
||||
override val qqId: Long get() = delegate.senderUin
|
||||
override val groupId: Long get() = Group.calculateGroupCodeByGroupUin(delegate.toUin)
|
||||
|
||||
override fun toString(): String = ""
|
||||
}
|
||||
|
||||
internal inline class MessageSourceFromMsg(
|
||||
val delegate: MsgComm.Msg
|
||||
) : MessageSource {
|
||||
override val time: Long get() = delegate.msgHead.msgTime.toLong() and 0xFFFFFFFF
|
||||
override val id: Long
|
||||
get() = delegate.msgHead.msgSeq.toLong().shl(32) or
|
||||
delegate.msgBody.richText.attr!!.random.toLong().and(0xFFFFFFFF)
|
||||
|
||||
override suspend fun ensureSequenceIdAvailable() {
|
||||
// nothing to do
|
||||
}
|
||||
|
||||
// override val sourceMessage: MessageChain get() = delegate.toMessageChain()
|
||||
override val qqId: Long get() = delegate.msgHead.fromUin
|
||||
override val groupId: Long get() = delegate.msgHead.groupInfo?.groupCode ?: 0
|
||||
|
||||
fun toJceData(): ImMsgBody.SourceMsg {
|
||||
return if (groupId == 0L) {
|
||||
toJceDataImplForFriend()
|
||||
} else toJceDataImplForGroup()
|
||||
}
|
||||
|
||||
private fun toJceDataImplForFriend(): ImMsgBody.SourceMsg {
|
||||
return ImMsgBody.SourceMsg(
|
||||
origSeqs = listOf(delegate.msgHead.msgSeq),
|
||||
senderUin = delegate.msgHead.fromUin,
|
||||
toUin = delegate.msgHead.toUin,
|
||||
flag = 1,
|
||||
elems = delegate.msgBody.richText.elems,
|
||||
type = 0,
|
||||
time = delegate.msgHead.msgTime,
|
||||
pbReserve = SourceMsg.ResvAttr(
|
||||
origUids = messageRandom.toLong() and 0xffFFffFF
|
||||
).toByteArray(SourceMsg.ResvAttr.serializer()),
|
||||
srcMsg = MsgComm.Msg(
|
||||
msgHead = MsgComm.MsgHead(
|
||||
fromUin = delegate.msgHead.fromUin, // qq
|
||||
toUin = delegate.msgHead.toUin, // group
|
||||
msgType = delegate.msgHead.msgType, // 82?
|
||||
c2cCmd = delegate.msgHead.c2cCmd,
|
||||
msgSeq = delegate.msgHead.msgSeq,
|
||||
msgTime = delegate.msgHead.msgTime,
|
||||
msgUid = messageRandom.toLong() and 0xffFFffFF, // ok
|
||||
// groupInfo = MsgComm.GroupInfo(groupCode = delegate.msgHead.groupInfo.groupCode),
|
||||
isSrcMsg = true
|
||||
),
|
||||
msgBody = ImMsgBody.MsgBody(
|
||||
richText = ImMsgBody.RichText(
|
||||
elems = delegate.msgBody.richText.elems.also {
|
||||
if (it.last().elemFlags2 == null) it.add(ImMsgBody.Elem(elemFlags2 = ImMsgBody.ElemFlags2()))
|
||||
}
|
||||
)
|
||||
)
|
||||
).toByteArray(MsgComm.Msg.serializer())
|
||||
)
|
||||
}
|
||||
|
||||
private fun toJceDataImplForGroup(): ImMsgBody.SourceMsg {
|
||||
|
||||
val groupUin = Group.calculateGroupUinByGroupCode(groupId)
|
||||
|
||||
return ImMsgBody.SourceMsg(
|
||||
origSeqs = listOf(delegate.msgHead.msgSeq),
|
||||
senderUin = delegate.msgHead.fromUin,
|
||||
toUin = groupUin,
|
||||
flag = 1,
|
||||
elems = delegate.msgBody.richText.elems,
|
||||
type = 0,
|
||||
time = delegate.msgHead.msgTime,
|
||||
pbReserve = SourceMsg.ResvAttr(
|
||||
origUids = messageRandom.toLong() and 0xffFFffFF
|
||||
).toByteArray(SourceMsg.ResvAttr.serializer()),
|
||||
srcMsg = MsgComm.Msg(
|
||||
msgHead = MsgComm.MsgHead(
|
||||
fromUin = delegate.msgHead.fromUin, // qq
|
||||
toUin = groupUin, // group
|
||||
msgType = delegate.msgHead.msgType, // 82?
|
||||
c2cCmd = delegate.msgHead.c2cCmd,
|
||||
msgSeq = delegate.msgHead.msgSeq,
|
||||
msgTime = delegate.msgHead.msgTime,
|
||||
msgUid = messageRandom.toLong() and 0xffFFffFF, // ok
|
||||
groupInfo = MsgComm.GroupInfo(groupCode = groupId),
|
||||
isSrcMsg = true
|
||||
),
|
||||
msgBody = ImMsgBody.MsgBody(
|
||||
richText = ImMsgBody.RichText(
|
||||
elems = delegate.msgBody.richText.elems.also {
|
||||
if (it.last().elemFlags2 == null) it.add(ImMsgBody.Elem(elemFlags2 = ImMsgBody.ElemFlags2()))
|
||||
}
|
||||
)
|
||||
)
|
||||
).toByteArray(MsgComm.Msg.serializer())
|
||||
)
|
||||
}
|
||||
|
||||
override fun toString(): String = ""
|
||||
}
|
@ -0,0 +1,295 @@
|
||||
/*
|
||||
* 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.message
|
||||
|
||||
import kotlinx.coroutines.CoroutineScope
|
||||
import kotlinx.coroutines.Deferred
|
||||
import kotlinx.coroutines.ExperimentalCoroutinesApi
|
||||
import net.mamoe.mirai.contact.Group
|
||||
import net.mamoe.mirai.event.subscribingGetAsync
|
||||
import net.mamoe.mirai.message.data.MessageChain
|
||||
import net.mamoe.mirai.message.data.MessageSource
|
||||
import net.mamoe.mirai.message.data.messageRandom
|
||||
import net.mamoe.mirai.message.data.sequenceId
|
||||
import net.mamoe.mirai.qqandroid.io.serialization.loadAs
|
||||
import net.mamoe.mirai.qqandroid.io.serialization.toByteArray
|
||||
import net.mamoe.mirai.qqandroid.network.protocol.data.proto.ImMsgBody
|
||||
import net.mamoe.mirai.qqandroid.network.protocol.data.proto.MsgComm
|
||||
import net.mamoe.mirai.qqandroid.network.protocol.data.proto.SourceMsg
|
||||
import net.mamoe.mirai.qqandroid.network.protocol.packet.chat.receive.OnlinePush
|
||||
import net.mamoe.mirai.utils.MiraiExperimentalAPI
|
||||
|
||||
internal inline class MessageSourceFromServer(
|
||||
val delegate: ImMsgBody.SourceMsg
|
||||
) : MessageSource {
|
||||
override val time: Long get() = delegate.time.toLong() and 0xFFFFFFFF
|
||||
|
||||
override val id: Long
|
||||
get() = (delegate.origSeqs?.firstOrNull()
|
||||
?: error("cannot find sequenceId from ImMsgBody.SourceMsg")).toLong().shl(32) or
|
||||
(delegate.pbReserve.loadAs(SourceMsg.ResvAttr.serializer()).origUids!!.toInt()).toLong().and(0xFFFFFFFF)
|
||||
|
||||
override val toUin: Long get() = delegate.toUin
|
||||
|
||||
override suspend fun ensureSequenceIdAvailable() {
|
||||
// nothing to do
|
||||
}
|
||||
|
||||
// override val sourceMessage: MessageChain get() = delegate.toMessageChain()
|
||||
override val senderId: Long get() = delegate.senderUin
|
||||
override val groupId: Long get() = Group.calculateGroupCodeByGroupUin(delegate.toUin)
|
||||
|
||||
override fun toString(): String = ""
|
||||
}
|
||||
|
||||
internal inline class MessageSourceFromMsg(
|
||||
val delegate: MsgComm.Msg
|
||||
) : MessageSource {
|
||||
override val time: Long get() = delegate.msgHead.msgTime.toLong() and 0xFFFFFFFF
|
||||
override val id: Long
|
||||
get() = delegate.msgHead.msgSeq.toLong().shl(32) or
|
||||
delegate.msgBody.richText.attr!!.random.toLong().and(0xFFFFFFFF)
|
||||
|
||||
override suspend fun ensureSequenceIdAvailable() {
|
||||
// nothing to do
|
||||
}
|
||||
|
||||
override val toUin: Long get() = delegate.msgHead.toUin
|
||||
override val senderId: Long get() = delegate.msgHead.fromUin
|
||||
override val groupId: Long get() = delegate.msgHead.groupInfo?.groupCode ?: 0
|
||||
|
||||
fun toJceData(): ImMsgBody.SourceMsg {
|
||||
return if (groupId == 0L) {
|
||||
toJceDataImplForFriend()
|
||||
} else toJceDataImplForGroup()
|
||||
}
|
||||
|
||||
private fun toJceDataImplForFriend(): ImMsgBody.SourceMsg {
|
||||
return ImMsgBody.SourceMsg(
|
||||
origSeqs = listOf(delegate.msgHead.msgSeq),
|
||||
senderUin = delegate.msgHead.fromUin,
|
||||
toUin = delegate.msgHead.toUin,
|
||||
flag = 1,
|
||||
elems = delegate.msgBody.richText.elems,
|
||||
type = 0,
|
||||
time = delegate.msgHead.msgTime,
|
||||
pbReserve = SourceMsg.ResvAttr(
|
||||
origUids = messageRandom.toLong() and 0xffFFffFF
|
||||
).toByteArray(SourceMsg.ResvAttr.serializer()),
|
||||
srcMsg = MsgComm.Msg(
|
||||
msgHead = MsgComm.MsgHead(
|
||||
fromUin = delegate.msgHead.fromUin, // qq
|
||||
toUin = delegate.msgHead.toUin, // group
|
||||
msgType = delegate.msgHead.msgType, // 82?
|
||||
c2cCmd = delegate.msgHead.c2cCmd,
|
||||
msgSeq = delegate.msgHead.msgSeq,
|
||||
msgTime = delegate.msgHead.msgTime,
|
||||
msgUid = messageRandom.toLong() and 0xffFFffFF, // ok
|
||||
// groupInfo = MsgComm.GroupInfo(groupCode = delegate.msgHead.groupInfo.groupCode),
|
||||
isSrcMsg = true
|
||||
),
|
||||
msgBody = ImMsgBody.MsgBody(
|
||||
richText = ImMsgBody.RichText(
|
||||
elems = delegate.msgBody.richText.elems.also {
|
||||
if (it.last().elemFlags2 == null) it.add(ImMsgBody.Elem(elemFlags2 = ImMsgBody.ElemFlags2()))
|
||||
}
|
||||
)
|
||||
)
|
||||
).toByteArray(MsgComm.Msg.serializer())
|
||||
)
|
||||
}
|
||||
|
||||
private fun toJceDataImplForGroup(): ImMsgBody.SourceMsg {
|
||||
|
||||
val groupUin = Group.calculateGroupUinByGroupCode(groupId)
|
||||
|
||||
return ImMsgBody.SourceMsg(
|
||||
origSeqs = listOf(delegate.msgHead.msgSeq),
|
||||
senderUin = delegate.msgHead.fromUin,
|
||||
toUin = groupUin,
|
||||
flag = 1,
|
||||
elems = delegate.msgBody.richText.elems,
|
||||
type = 0,
|
||||
time = delegate.msgHead.msgTime,
|
||||
pbReserve = SourceMsg.ResvAttr(
|
||||
origUids = messageRandom.toLong() and 0xffFFffFF
|
||||
).toByteArray(SourceMsg.ResvAttr.serializer()),
|
||||
srcMsg = MsgComm.Msg(
|
||||
msgHead = MsgComm.MsgHead(
|
||||
fromUin = delegate.msgHead.fromUin, // qq
|
||||
toUin = groupUin, // group
|
||||
msgType = delegate.msgHead.msgType, // 82?
|
||||
c2cCmd = delegate.msgHead.c2cCmd,
|
||||
msgSeq = delegate.msgHead.msgSeq,
|
||||
msgTime = delegate.msgHead.msgTime,
|
||||
msgUid = messageRandom.toLong() and 0xffFFffFF, // ok
|
||||
groupInfo = MsgComm.GroupInfo(groupCode = groupId),
|
||||
isSrcMsg = true
|
||||
),
|
||||
msgBody = ImMsgBody.MsgBody(
|
||||
richText = ImMsgBody.RichText(
|
||||
elems = delegate.msgBody.richText.elems.also {
|
||||
if (it.last().elemFlags2 == null) it.add(ImMsgBody.Elem(elemFlags2 = ImMsgBody.ElemFlags2()))
|
||||
}
|
||||
)
|
||||
)
|
||||
).toByteArray(MsgComm.Msg.serializer())
|
||||
)
|
||||
}
|
||||
|
||||
override fun toString(): String = ""
|
||||
}
|
||||
|
||||
internal abstract class MessageSourceFromSend : MessageSource {
|
||||
|
||||
abstract val sourceMessage: MessageChain
|
||||
|
||||
fun toJceData(): ImMsgBody.SourceMsg {
|
||||
return if (groupId == 0L) {
|
||||
toJceDataImplForFriend()
|
||||
} else toJceDataImplForGroup()
|
||||
}
|
||||
|
||||
private val elems by lazy {
|
||||
sourceMessage.toRichTextElems(groupId != 0L)
|
||||
}
|
||||
|
||||
private fun toJceDataImplForFriend(): ImMsgBody.SourceMsg {
|
||||
val messageUid: Long = 262144L.shl(32) or messageRandom.toLong().and(0xffFFffFF)
|
||||
return ImMsgBody.SourceMsg(
|
||||
origSeqs = listOf(sequenceId),
|
||||
senderUin = senderId,
|
||||
toUin = toUin,
|
||||
flag = 1,
|
||||
elems = elems,
|
||||
type = 0,
|
||||
time = time.toInt(),
|
||||
pbReserve = SourceMsg.ResvAttr(
|
||||
origUids = messageUid
|
||||
).toByteArray(SourceMsg.ResvAttr.serializer()),
|
||||
srcMsg = MsgComm.Msg(
|
||||
msgHead = MsgComm.MsgHead(
|
||||
fromUin = senderId, // qq
|
||||
toUin = toUin, // group
|
||||
msgType = 9, // 82?
|
||||
c2cCmd = 11,
|
||||
msgSeq = sequenceId,
|
||||
msgTime = time.toInt(),
|
||||
msgUid = messageUid, // ok
|
||||
// groupInfo = MsgComm.GroupInfo(groupCode = delegate.msgHead.groupInfo.groupCode),
|
||||
isSrcMsg = true
|
||||
),
|
||||
msgBody = ImMsgBody.MsgBody(
|
||||
richText = ImMsgBody.RichText(
|
||||
elems = elems.toMutableList().also {
|
||||
if (it.last().elemFlags2 == null) it.add(ImMsgBody.Elem(elemFlags2 = ImMsgBody.ElemFlags2()))
|
||||
}
|
||||
)
|
||||
)
|
||||
).toByteArray(MsgComm.Msg.serializer())
|
||||
)
|
||||
}
|
||||
|
||||
private fun toJceDataImplForGroup(): ImMsgBody.SourceMsg {
|
||||
return ImMsgBody.SourceMsg(
|
||||
origSeqs = listOf(sequenceId),
|
||||
senderUin = senderId,
|
||||
toUin = toUin,
|
||||
flag = 1,
|
||||
elems = elems,
|
||||
type = 0,
|
||||
time = time.toInt(),
|
||||
pbReserve = SourceMsg.ResvAttr(
|
||||
origUids = messageRandom.toLong() and 0xffFFffFF
|
||||
).toByteArray(SourceMsg.ResvAttr.serializer()),
|
||||
srcMsg = MsgComm.Msg(
|
||||
msgHead = MsgComm.MsgHead(
|
||||
fromUin = senderId, // qq
|
||||
toUin = toUin, // group
|
||||
msgType = 82, // 82?
|
||||
c2cCmd = 1,
|
||||
msgSeq = sequenceId,
|
||||
msgTime = time.toInt(),
|
||||
msgUid = messageRandom.toLong() and 0xffFFffFF, // ok
|
||||
groupInfo = MsgComm.GroupInfo(groupCode = groupId),
|
||||
isSrcMsg = true
|
||||
),
|
||||
msgBody = ImMsgBody.MsgBody(
|
||||
richText = ImMsgBody.RichText(
|
||||
elems = elems.toMutableList().also {
|
||||
if (it.last().elemFlags2 == null) it.add(ImMsgBody.Elem(elemFlags2 = ImMsgBody.ElemFlags2()))
|
||||
}
|
||||
)
|
||||
)
|
||||
).toByteArray(MsgComm.Msg.serializer())
|
||||
)
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
internal class MessageSourceFromSendFriend(
|
||||
val messageRandom: Int,
|
||||
override val time: Long,
|
||||
override val senderId: Long,
|
||||
override val toUin: Long,
|
||||
override val groupId: Long,
|
||||
val sequenceId: Int,
|
||||
override val sourceMessage: MessageChain
|
||||
) : MessageSourceFromSend() {
|
||||
@UseExperimental(ExperimentalCoroutinesApi::class)
|
||||
override val id: Long
|
||||
get() = sequenceId.toLong().shl(32) or
|
||||
messageRandom.toLong().and(0xFFFFFFFF)
|
||||
|
||||
override suspend fun ensureSequenceIdAvailable() {
|
||||
// nothing to do
|
||||
}
|
||||
|
||||
override fun toString(): String {
|
||||
return ""
|
||||
}
|
||||
}
|
||||
|
||||
internal class MessageSourceFromSendGroup(
|
||||
val messageRandom: Int,
|
||||
override val time: Long,
|
||||
override val senderId: Long,
|
||||
override val toUin: Long,
|
||||
override val groupId: Long,
|
||||
override val sourceMessage: MessageChain
|
||||
) : MessageSourceFromSend() {
|
||||
private lateinit var sequenceIdDeferred: Deferred<Int>
|
||||
|
||||
@UseExperimental(ExperimentalCoroutinesApi::class)
|
||||
override val id: Long
|
||||
get() = sequenceIdDeferred.getCompleted().toLong().shl(32) or
|
||||
messageRandom.toLong().and(0xFFFFFFFF)
|
||||
|
||||
@UseExperimental(MiraiExperimentalAPI::class)
|
||||
internal fun startWaitingSequenceId(coroutineScope: CoroutineScope) {
|
||||
sequenceIdDeferred =
|
||||
coroutineScope.subscribingGetAsync<OnlinePush.PbPushGroupMsg.SendGroupMessageReceipt, Int>(
|
||||
timeoutMillis = 3000
|
||||
) {
|
||||
if (it.messageRandom == this@MessageSourceFromSendGroup.messageRandom) {
|
||||
it.sequenceId
|
||||
} else null
|
||||
}
|
||||
}
|
||||
|
||||
override suspend fun ensureSequenceIdAvailable() {
|
||||
sequenceIdDeferred.join()
|
||||
}
|
||||
|
||||
override fun toString(): String {
|
||||
return ""
|
||||
}
|
||||
}
|
@ -226,6 +226,7 @@ internal fun MessageChain.toRichTextElems(forGroup: Boolean): MutableList<ImMsgB
|
||||
when (val source = this[QuoteReply].source) {
|
||||
is MessageSourceFromServer -> elements.add(ImMsgBody.Elem(srcMsg = source.delegate))
|
||||
is MessageSourceFromMsg -> elements.add(ImMsgBody.Elem(srcMsg = source.toJceData()))
|
||||
is MessageSourceFromSend -> elements.add(ImMsgBody.Elem(srcMsg = source.toJceData()))
|
||||
else -> error("unsupported MessageSource implementation: ${source::class.simpleName}")
|
||||
}
|
||||
}
|
||||
@ -243,6 +244,9 @@ internal fun MessageChain.toRichTextElems(forGroup: Boolean): MutableList<ImMsgB
|
||||
is Face -> elements.add(ImMsgBody.Elem(face = it.toJceData()))
|
||||
is QuoteReplyToSend -> {
|
||||
if (forGroup) {
|
||||
check(it is QuoteReplyToSend.ToGroup) {
|
||||
"sending a quote to group suing QuoteReplyToSend.ToFriend"
|
||||
}
|
||||
if (it.sender is Member) {
|
||||
transformOneMessage(it.createAt())
|
||||
}
|
||||
|
@ -245,7 +245,7 @@ internal class QQAndroidBotNetworkHandler(bot: QQAndroidBot) : BotNetworkHandler
|
||||
bot = bot,
|
||||
coroutineContext = bot.coroutineContext,
|
||||
id = troopNum.groupCode,
|
||||
groupInfo = bot.queryGroupInfo(troopNum.groupCode).apply {
|
||||
groupInfo = bot._lowLevelQueryGroupInfo(troopNum.groupCode).apply {
|
||||
this as GroupInfoImpl
|
||||
|
||||
if (this.delegate.groupName == null) {
|
||||
@ -262,7 +262,11 @@ internal class QQAndroidBotNetworkHandler(bot: QQAndroidBot) : BotNetworkHandler
|
||||
|
||||
this.delegate.groupCode = troopNum.groupCode
|
||||
},
|
||||
members = bot.queryGroupMemberList(troopNum.groupUin, troopNum.groupCode, troopNum.dwGroupOwnerUin)
|
||||
members = bot._lowLevelQueryGroupMemberList(
|
||||
troopNum.groupUin,
|
||||
troopNum.groupCode,
|
||||
troopNum.dwGroupOwnerUin
|
||||
)
|
||||
))
|
||||
)
|
||||
}?.let {
|
||||
|
@ -78,6 +78,7 @@ internal class PbMessageSvc {
|
||||
messageRandom: Int, // 921878719
|
||||
time: Long
|
||||
): OutgoingPacket = buildOutgoingUniPacket(client) {
|
||||
val messageUid: Long = 262144L.shl(32) or messageRandom.toLong().and(0xffFFffFF)
|
||||
writeProtoBuf(
|
||||
MsgSvc.PbMsgWithDrawReq.serializer(),
|
||||
MsgSvc.PbMsgWithDrawReq(
|
||||
@ -89,7 +90,7 @@ internal class PbMessageSvc {
|
||||
fromUin = client.bot.uin,
|
||||
toUin = toUin,
|
||||
msgSeq = messageSequenceId,
|
||||
msgUid = messageRandom.toLong() and 0xffffffff,
|
||||
msgUid = messageUid,
|
||||
msgTime = time and 0xffffffff,
|
||||
routingHead = MsgSvc.RoutingHead(
|
||||
c2c = MsgSvc.C2C(
|
||||
|
@ -10,9 +10,6 @@
|
||||
package net.mamoe.mirai.qqandroid.network.protocol.packet.chat.receive
|
||||
|
||||
import io.ktor.utils.io.core.ByteReadPacket
|
||||
import kotlinx.coroutines.CoroutineScope
|
||||
import kotlinx.coroutines.Deferred
|
||||
import kotlinx.coroutines.ExperimentalCoroutinesApi
|
||||
import net.mamoe.mirai.contact.Group
|
||||
import net.mamoe.mirai.contact.MemberPermission
|
||||
import net.mamoe.mirai.data.MemberInfo
|
||||
@ -21,16 +18,16 @@ import net.mamoe.mirai.data.Packet
|
||||
import net.mamoe.mirai.event.events.BotJoinGroupEvent
|
||||
import net.mamoe.mirai.event.events.BotOfflineEvent
|
||||
import net.mamoe.mirai.event.events.MemberJoinEvent
|
||||
import net.mamoe.mirai.event.subscribingGetAsync
|
||||
import net.mamoe.mirai.message.FriendMessage
|
||||
import net.mamoe.mirai.message.data.MessageChain
|
||||
import net.mamoe.mirai.message.data.MessageSource
|
||||
import net.mamoe.mirai.qqandroid.GroupImpl
|
||||
import net.mamoe.mirai.qqandroid.QQAndroidBot
|
||||
import net.mamoe.mirai.qqandroid.io.serialization.decodeUniPacket
|
||||
import net.mamoe.mirai.qqandroid.io.serialization.readProtoBuf
|
||||
import net.mamoe.mirai.qqandroid.io.serialization.toByteArray
|
||||
import net.mamoe.mirai.qqandroid.io.serialization.writeProtoBuf
|
||||
import net.mamoe.mirai.qqandroid.message.MessageSourceFromSendFriend
|
||||
import net.mamoe.mirai.qqandroid.message.MessageSourceFromSendGroup
|
||||
import net.mamoe.mirai.qqandroid.message.toMessageChain
|
||||
import net.mamoe.mirai.qqandroid.message.toRichTextElems
|
||||
import net.mamoe.mirai.qqandroid.network.QQAndroidClient
|
||||
@ -45,6 +42,7 @@ import net.mamoe.mirai.qqandroid.network.protocol.packet.chat.GroupInfoImpl
|
||||
import net.mamoe.mirai.qqandroid.network.protocol.packet.list.FriendList
|
||||
import net.mamoe.mirai.utils.MiraiExperimentalAPI
|
||||
import net.mamoe.mirai.utils.MiraiInternalAPI
|
||||
import net.mamoe.mirai.utils._miraiContentToString
|
||||
import net.mamoe.mirai.utils.currentTimeSeconds
|
||||
import kotlin.math.absoluteValue
|
||||
import kotlin.random.Random
|
||||
@ -97,7 +95,7 @@ internal class MessageSvc {
|
||||
syncFlag = syncFlag,
|
||||
// serverBuf = from.serverBuf ?: EMPTY_BYTE_ARRAY,
|
||||
syncCookie = client.c2cMessageSync.syncCookie
|
||||
?: SyncCookie(time = msgTime + client.timeDifference).toByteArray(SyncCookie.serializer())//.also { client.c2cMessageSync.syncCookie = it },
|
||||
?: SyncCookie(time = msgTime).toByteArray(SyncCookie.serializer())//.also { client.c2cMessageSync.syncCookie = it },
|
||||
// syncFlag = client.c2cMessageSync.syncFlag,
|
||||
//msgCtrlBuf = client.c2cMessageSync.msgCtrlBuf,
|
||||
//pubaccountCookie = client.c2cMessageSync.pubAccountCookie
|
||||
@ -165,7 +163,7 @@ internal class MessageSvc {
|
||||
bot = bot,
|
||||
coroutineContext = bot.coroutineContext,
|
||||
id = Group.calculateGroupCodeByGroupUin(msg.msgHead.fromUin),
|
||||
groupInfo = bot.queryGroupInfo(troopNum.groupCode).apply {
|
||||
groupInfo = bot._lowLevelQueryGroupInfo(troopNum.groupCode).apply {
|
||||
|
||||
this as GroupInfoImpl
|
||||
|
||||
@ -183,7 +181,11 @@ internal class MessageSvc {
|
||||
|
||||
this.delegate.groupCode = troopNum.groupCode
|
||||
},
|
||||
members = bot.queryGroupMemberList(troopNum.groupUin, troopNum.groupCode, troopNum.dwGroupOwnerUin)
|
||||
members = bot._lowLevelQueryGroupMemberList(
|
||||
troopNum.groupUin,
|
||||
troopNum.groupCode,
|
||||
troopNum.dwGroupOwnerUin
|
||||
)
|
||||
)
|
||||
bot.groups.delegate.addLast(newGroup)
|
||||
return@mapNotNull BotJoinGroupEvent(newGroup)
|
||||
@ -198,12 +200,15 @@ internal class MessageSvc {
|
||||
override val specialTitle: String get() = ""
|
||||
override val muteTimestamp: Int get() = 0
|
||||
override val uin: Long get() = msg.msgHead.authUin
|
||||
override val nick: String get() = msg.msgHead.authNick.takeIf { it.isNotEmpty() } ?: msg.msgHead.fromNick
|
||||
override val nick: String
|
||||
get() = msg.msgHead.authNick.takeIf { it.isNotEmpty() }
|
||||
?: msg.msgHead.fromNick
|
||||
}).also { group.members.delegate.addLast(it) })
|
||||
}
|
||||
}
|
||||
}
|
||||
166 -> {
|
||||
println(msg._miraiContentToString())
|
||||
return@mapNotNull when {
|
||||
msg.msgHead.fromUin == bot.uin -> null
|
||||
!bot.firstLoginSucceed -> null
|
||||
@ -227,8 +232,13 @@ internal class MessageSvc {
|
||||
|
||||
override suspend fun QQAndroidBot.handle(packet: Response) {
|
||||
when (packet.syncFlagFromServer) {
|
||||
MsgSvc.SyncFlag.STOP,
|
||||
MsgSvc.SyncFlag.START -> return
|
||||
MsgSvc.SyncFlag.STOP -> return
|
||||
MsgSvc.SyncFlag.START -> {
|
||||
network.run {
|
||||
PbGetMsg(client, MsgSvc.SyncFlag.CONTINUE, currentTimeSeconds).sendWithoutExpect()
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
MsgSvc.SyncFlag.CONTINUE -> {
|
||||
network.run {
|
||||
@ -266,62 +276,6 @@ internal class MessageSvc {
|
||||
}
|
||||
}
|
||||
|
||||
internal class MessageSourceFromSendFriend(
|
||||
val messageRandom: Int,
|
||||
override val time: Long,
|
||||
override val qqId: Long,
|
||||
override val groupId: Long,
|
||||
val sequenceId: Int
|
||||
) : MessageSource {
|
||||
@UseExperimental(ExperimentalCoroutinesApi::class)
|
||||
override val id: Long
|
||||
get() = sequenceId.toLong().shl(32) or
|
||||
messageRandom.toLong().and(0xFFFFFFFF)
|
||||
|
||||
override suspend fun ensureSequenceIdAvailable() {
|
||||
// nothing to do
|
||||
}
|
||||
|
||||
override fun toString(): String {
|
||||
return ""
|
||||
}
|
||||
}
|
||||
|
||||
internal class MessageSourceFromSendGroup(
|
||||
val messageRandom: Int,
|
||||
override val time: Long,
|
||||
override val qqId: Long,
|
||||
override val groupId: Long// ,
|
||||
// override val sourceMessage: MessageChain
|
||||
) : MessageSource {
|
||||
private lateinit var sequenceIdDeferred: Deferred<Int>
|
||||
|
||||
@UseExperimental(ExperimentalCoroutinesApi::class)
|
||||
override val id: Long
|
||||
get() = sequenceIdDeferred.getCompleted().toLong().shl(32) or
|
||||
messageRandom.toLong().and(0xFFFFFFFF)
|
||||
|
||||
@UseExperimental(MiraiExperimentalAPI::class)
|
||||
fun startWaitingSequenceId(coroutineScope: CoroutineScope) {
|
||||
sequenceIdDeferred =
|
||||
coroutineScope.subscribingGetAsync<OnlinePush.PbPushGroupMsg.SendGroupMessageReceipt, Int>(
|
||||
timeoutMillis = 3000
|
||||
) {
|
||||
if (it.messageRandom == this@MessageSourceFromSendGroup.messageRandom) {
|
||||
it.sequenceId
|
||||
} else null
|
||||
}
|
||||
}
|
||||
|
||||
override suspend fun ensureSequenceIdAvailable() {
|
||||
sequenceIdDeferred.join()
|
||||
}
|
||||
|
||||
override fun toString(): String {
|
||||
return ""
|
||||
}
|
||||
}
|
||||
|
||||
inline fun ToFriend(
|
||||
client: QQAndroidClient,
|
||||
toUin: Long,
|
||||
@ -330,10 +284,12 @@ internal class MessageSvc {
|
||||
): OutgoingPacket {
|
||||
val source = MessageSourceFromSendFriend(
|
||||
messageRandom = Random.nextInt().absoluteValue,
|
||||
qqId = toUin,
|
||||
senderId = client.uin,
|
||||
toUin = toUin,
|
||||
time = currentTimeSeconds + client.timeDifference,
|
||||
groupId = 0,
|
||||
sequenceId = client.atomicNextMessageSequenceId()
|
||||
sequenceId = client.atomicNextMessageSequenceId(),
|
||||
sourceMessage = message
|
||||
)
|
||||
sourceCallback(source)
|
||||
return ToFriend(client, toUin, message, source)
|
||||
@ -379,9 +335,11 @@ internal class MessageSvc {
|
||||
|
||||
val source = MessageSourceFromSendGroup(
|
||||
messageRandom = Random.nextInt().absoluteValue,
|
||||
qqId = client.uin,
|
||||
senderId = client.uin,
|
||||
toUin = Group.calculateGroupUinByGroupCode(groupCode),
|
||||
time = currentTimeSeconds + client.timeDifference,
|
||||
groupId = groupCode//,
|
||||
groupId = groupCode,
|
||||
sourceMessage = message//,
|
||||
// sourceMessage = message
|
||||
)
|
||||
sourceCallback(source)
|
||||
|
@ -19,8 +19,6 @@ import kotlinx.coroutines.launch
|
||||
import net.mamoe.mirai.contact.*
|
||||
import net.mamoe.mirai.data.AddFriendResult
|
||||
import net.mamoe.mirai.data.FriendInfo
|
||||
import net.mamoe.mirai.data.GroupInfo
|
||||
import net.mamoe.mirai.data.MemberInfo
|
||||
import net.mamoe.mirai.message.MessageReceipt
|
||||
import net.mamoe.mirai.message.data.Image
|
||||
import net.mamoe.mirai.message.data.MessageChain
|
||||
@ -41,7 +39,7 @@ import kotlin.jvm.JvmStatic
|
||||
* @see Contact 联系人
|
||||
* @see kotlinx.coroutines.isActive 判断 [Bot] 是否正常运行中. (在线, 且没有被 [close])
|
||||
*/
|
||||
@UseExperimental(MiraiInternalAPI::class)
|
||||
@UseExperimental(MiraiInternalAPI::class, LowLevelAPI::class)
|
||||
abstract class Bot : CoroutineScope, LowLevelBotAPIAccessor {
|
||||
companion object {
|
||||
/**
|
||||
@ -151,27 +149,6 @@ abstract class Bot : CoroutineScope, LowLevelBotAPIAccessor {
|
||||
?: throw NoSuchElementException("No such group $id for bot ${this.uin}")
|
||||
}
|
||||
|
||||
/**
|
||||
* 向服务器查询群列表. 返回值前 32 bits 为 uin, 后 32 bits 为 groupCode
|
||||
*/
|
||||
abstract suspend fun queryGroupList(): Sequence<Long>
|
||||
|
||||
/**
|
||||
* 向服务器查询群资料. 获得的仅为当前时刻的资料.
|
||||
* 请优先使用 [getGroup] 然后查看群资料.
|
||||
*/
|
||||
abstract suspend fun queryGroupInfo(groupCode: Long): GroupInfo
|
||||
|
||||
/**
|
||||
* 向服务器查询群成员列表.
|
||||
* 请优先使用 [getGroup], [Group.members] 查看群成员.
|
||||
*
|
||||
* 这个函数很慢. 请不要频繁使用.
|
||||
*
|
||||
* @see Group.calculateGroupUinByGroupCode 使用 groupCode 计算 groupUin
|
||||
*/
|
||||
abstract suspend fun queryGroupMemberList(groupUin: Long, groupCode: Long, ownerId: Long): Sequence<MemberInfo>
|
||||
|
||||
// endregion
|
||||
|
||||
// region network
|
||||
@ -263,6 +240,7 @@ abstract class Bot : CoroutineScope, LowLevelBotAPIAccessor {
|
||||
*/
|
||||
abstract fun close(cause: Throwable? = null)
|
||||
|
||||
@UseExperimental(LowLevelAPI::class)
|
||||
final override fun toString(): String = "Bot(${uin})"
|
||||
}
|
||||
|
||||
|
@ -5,7 +5,7 @@ import net.mamoe.mirai.Bot
|
||||
/**
|
||||
* 群资料.
|
||||
*
|
||||
* 通过 [Bot.queryGroupInfo] 得到
|
||||
* 通过 [Bot._lowLevelQueryGroupInfo] 得到
|
||||
*/
|
||||
interface GroupInfo {
|
||||
/**
|
||||
|
@ -9,6 +9,9 @@
|
||||
|
||||
package net.mamoe.mirai
|
||||
|
||||
import net.mamoe.mirai.contact.Group
|
||||
import net.mamoe.mirai.data.GroupInfo
|
||||
import net.mamoe.mirai.data.MemberInfo
|
||||
import net.mamoe.mirai.message.data.MessageSource
|
||||
import net.mamoe.mirai.utils.MiraiExperimentalAPI
|
||||
|
||||
@ -18,6 +21,7 @@ import net.mamoe.mirai.utils.MiraiExperimentalAPI
|
||||
* 使用低级的 API 无法带来任何安全和便捷保障.
|
||||
* 仅在某些使用结构化 API 可能影响性能的情况下使用这些低级 API.
|
||||
*/
|
||||
@Experimental
|
||||
@Retention(AnnotationRetention.BINARY)
|
||||
@Target(AnnotationTarget.CLASS, AnnotationTarget.TYPE, AnnotationTarget.FUNCTION, AnnotationTarget.PROPERTY)
|
||||
annotation class LowLevelAPI
|
||||
@ -29,6 +33,31 @@ annotation class LowLevelAPI
|
||||
@Suppress("FunctionName", "unused")
|
||||
@LowLevelAPI
|
||||
interface LowLevelBotAPIAccessor {
|
||||
|
||||
/**
|
||||
* 向服务器查询群列表. 返回值前 32 bits 为 uin, 后 32 bits 为 groupCode
|
||||
*/
|
||||
@LowLevelAPI
|
||||
suspend fun _lowLevelQueryGroupList(): Sequence<Long>
|
||||
|
||||
/**
|
||||
* 向服务器查询群资料. 获得的仅为当前时刻的资料.
|
||||
* 请优先使用 [Bot.getGroup] 然后查看群资料.
|
||||
*/
|
||||
@LowLevelAPI
|
||||
suspend fun _lowLevelQueryGroupInfo(groupCode: Long): GroupInfo
|
||||
|
||||
/**
|
||||
* 向服务器查询群成员列表.
|
||||
* 请优先使用 [Bot.getGroup], [Group.members] 查看群成员.
|
||||
*
|
||||
* 这个函数很慢. 请不要频繁使用.
|
||||
*
|
||||
* @see Group.calculateGroupUinByGroupCode 使用 groupCode 计算 groupUin
|
||||
*/
|
||||
@LowLevelAPI
|
||||
suspend fun _lowLevelQueryGroupMemberList(groupUin: Long, groupCode: Long, ownerId: Long): Sequence<MemberInfo>
|
||||
|
||||
/**
|
||||
* 撤回一条由机器人发送给好友的消息
|
||||
* @param messageId [MessageSource.id]
|
||||
|
@ -12,13 +12,10 @@ package net.mamoe.mirai.message
|
||||
import kotlinx.atomicfu.atomic
|
||||
import kotlinx.coroutines.Job
|
||||
import net.mamoe.mirai.Bot
|
||||
import net.mamoe.mirai.contact.Contact
|
||||
import net.mamoe.mirai.contact.Group
|
||||
import net.mamoe.mirai.contact.QQ
|
||||
import net.mamoe.mirai.contact.sendMessage
|
||||
import net.mamoe.mirai.LowLevelAPI
|
||||
import net.mamoe.mirai.contact.*
|
||||
import net.mamoe.mirai.message.data.*
|
||||
import net.mamoe.mirai.recallIn
|
||||
import net.mamoe.mirai.utils.MiraiExperimentalAPI
|
||||
import net.mamoe.mirai.utils.getValue
|
||||
import net.mamoe.mirai.utils.unsafeWeakRef
|
||||
|
||||
@ -36,7 +33,8 @@ import net.mamoe.mirai.utils.unsafeWeakRef
|
||||
*/
|
||||
open class MessageReceipt<C : Contact>(
|
||||
val source: MessageSource,
|
||||
target: C
|
||||
target: C,
|
||||
private val botAsMember: Member?
|
||||
) {
|
||||
init {
|
||||
require(target is Group || target is QQ) { "target must be either Group or QQ" }
|
||||
@ -47,6 +45,11 @@ open class MessageReceipt<C : Contact>(
|
||||
*/
|
||||
val target: C by target.unsafeWeakRef()
|
||||
|
||||
/**
|
||||
* 是否为发送给群的消息的回执
|
||||
*/
|
||||
val isToGroup: Boolean = botAsMember != null
|
||||
|
||||
private val _isRecalled = atomic(false)
|
||||
|
||||
/**
|
||||
@ -55,7 +58,6 @@ open class MessageReceipt<C : Contact>(
|
||||
* @see Bot.recall
|
||||
* @throws IllegalStateException 当此消息已经被撤回或正计划撤回时
|
||||
*/
|
||||
@UseExperimental(MiraiExperimentalAPI::class)
|
||||
suspend fun recall() {
|
||||
@Suppress("BooleanLiteralArgument")
|
||||
if (_isRecalled.compareAndSet(false, true)) {
|
||||
@ -75,10 +77,8 @@ open class MessageReceipt<C : Contact>(
|
||||
* 在一段时间后撤回这条消息.. [recall] 或 [recallIn] 只能被调用一次.
|
||||
*
|
||||
* @param millis 延迟时间, 单位为毫秒
|
||||
*
|
||||
* @throws IllegalStateException 当此消息已经被撤回或正计划撤回时
|
||||
*/
|
||||
@UseExperimental(MiraiExperimentalAPI::class)
|
||||
fun recallIn(millis: Long): Job {
|
||||
@Suppress("BooleanLiteralArgument")
|
||||
if (_isRecalled.compareAndSet(false, true)) {
|
||||
@ -91,27 +91,32 @@ open class MessageReceipt<C : Contact>(
|
||||
}
|
||||
|
||||
/**
|
||||
* 引用这条消息.
|
||||
* [确保 sequenceId可用][MessageSource.ensureSequenceIdAvailable] 然后引用这条消息.
|
||||
* @see MessageChain.quote 引用一条消息
|
||||
*/
|
||||
open suspend fun quote(): QuoteReplyToSend {
|
||||
this.source.ensureSequenceIdAvailable()
|
||||
@UseExperimental(LowLevelAPI::class)
|
||||
return _unsafeQuote()
|
||||
}
|
||||
|
||||
/**
|
||||
* 引用这条消息, 但不会 [确保 sequenceId可用][MessageSource.ensureSequenceIdAvailable].
|
||||
* 在 sequenceId 可用前就发送这条消息则会导致一个异常.
|
||||
* 当且仅当用于存储而不用于发送时使用这个方法.
|
||||
*
|
||||
* @see MessageChain.quote 引用一条消息
|
||||
*
|
||||
* @throws IllegalStateException 当此消息不是群消息时
|
||||
*/
|
||||
@MiraiExperimentalAPI("unstable")
|
||||
open fun quote(): QuoteReplyToSend {
|
||||
val target = target
|
||||
check(target is Group) { "quote is only available for GroupMessage" }
|
||||
return this.source.quote(target.botAsMember)
|
||||
@LowLevelAPI
|
||||
@Suppress("FunctionName")
|
||||
fun _unsafeQuote(): QuoteReplyToSend {
|
||||
return this.source.quote(botAsMember as? QQ)
|
||||
}
|
||||
|
||||
/**
|
||||
* 引用这条消息并回复.
|
||||
*
|
||||
* @see MessageChain.quote 引用一条消息
|
||||
*
|
||||
* @throws IllegalStateException 当此消息不是群消息时
|
||||
*/
|
||||
@MiraiExperimentalAPI("unstable")
|
||||
suspend fun quoteReply(message: MessageChain) {
|
||||
target.sendMessage(this.quote() + message)
|
||||
}
|
||||
@ -138,12 +143,10 @@ inline val MessageReceipt<*>.sourceSequenceId: Int get() = this.source.sequenceI
|
||||
*/
|
||||
inline val MessageReceipt<*>.sourceTime: Long get() = this.source.time
|
||||
|
||||
@MiraiExperimentalAPI("unstable")
|
||||
suspend inline fun MessageReceipt<out Contact>.quoteReply(message: Message) {
|
||||
return this.quoteReply(message.toChain())
|
||||
}
|
||||
|
||||
@MiraiExperimentalAPI("unstable")
|
||||
suspend inline fun MessageReceipt<out Contact>.quoteReply(message: String) {
|
||||
return this.quoteReply(message.toMessage().toChain())
|
||||
}
|
||||
|
@ -13,7 +13,7 @@
|
||||
package net.mamoe.mirai.message.data
|
||||
|
||||
import net.mamoe.mirai.Bot
|
||||
import net.mamoe.mirai.contact.QQ
|
||||
import net.mamoe.mirai.contact.Group
|
||||
import kotlin.jvm.JvmMultifileClass
|
||||
import kotlin.jvm.JvmName
|
||||
|
||||
@ -50,19 +50,17 @@ interface MessageSource : Message, MessageMetadata {
|
||||
val time: Long
|
||||
|
||||
/**
|
||||
* 与这个消息相关的 [QQ] 的 [QQ.id]
|
||||
*
|
||||
* 群消息时为发送人的 id (可能为 bot 自己). 好友消息时为消息发送目标好友的 id (不可能为 bot 自己)
|
||||
* 发送人. 可以为机器人自己
|
||||
*/
|
||||
val qqId: Long
|
||||
|
||||
@Suppress("unused")
|
||||
@Deprecated("使用 qqId. 此 API 将在不久后删除", level = DeprecationLevel.ERROR, replaceWith = ReplaceWith("this.qqId"))
|
||||
val senderId: Long
|
||||
get() = qqId
|
||||
|
||||
/**
|
||||
* 群号码, 为 0 时则来自好友消息
|
||||
* 消息发送对象, 可以为一个群的 `uin` (非 `id`)或一个好友, 或机器人自己
|
||||
*/
|
||||
val toUin: Long
|
||||
|
||||
/**
|
||||
* 当群消息时为群 id, [Group.id], 好友消息时为 0
|
||||
*/
|
||||
val groupId: Long
|
||||
|
||||
@ -72,6 +70,8 @@ interface MessageSource : Message, MessageMetadata {
|
||||
override fun toString(): String
|
||||
}
|
||||
|
||||
interface GroupMessageSource : MessageSource
|
||||
|
||||
/**
|
||||
* 序列号. 若是机器人发出去的消息, 请先 [确保 sequenceId 可用][MessageSource.ensureSequenceIdAvailable]
|
||||
* @see MessageSource.id
|
||||
|
@ -40,16 +40,21 @@ open class QuoteReply
|
||||
* 总是使用 [quote] 来构造实例.
|
||||
*/
|
||||
@UseExperimental(MiraiInternalAPI::class)
|
||||
class QuoteReplyToSend
|
||||
@MiraiInternalAPI constructor(source: MessageSource, val sender: QQ) : QuoteReply(source) {
|
||||
fun createAt(): At = At(sender as Member)
|
||||
sealed class QuoteReplyToSend
|
||||
@MiraiInternalAPI constructor(source: MessageSource) : QuoteReply(source) {
|
||||
class ToGroup(source: MessageSource, val sender: QQ) : QuoteReplyToSend(source) {
|
||||
fun createAt(): At = At(sender as Member)
|
||||
}
|
||||
|
||||
class ToFriend(source: MessageSource) : QuoteReplyToSend(source)
|
||||
}
|
||||
|
||||
/**
|
||||
* 引用这条消息.
|
||||
* @see sender 消息发送人.
|
||||
*/
|
||||
@UseExperimental(MiraiInternalAPI::class)
|
||||
fun MessageChain.quote(sender: QQ): QuoteReplyToSend {
|
||||
fun MessageChain.quote(sender: QQ?): QuoteReplyToSend {
|
||||
this.firstOrNull<MessageSource>()?.let {
|
||||
return it.quote(sender)
|
||||
}
|
||||
@ -58,11 +63,12 @@ fun MessageChain.quote(sender: QQ): QuoteReplyToSend {
|
||||
|
||||
/**
|
||||
* 引用这条消息.
|
||||
* @see from 消息来源. 若是好友发送
|
||||
*/
|
||||
@UseExperimental(MiraiInternalAPI::class)
|
||||
fun MessageSource.quote(sender: QQ): QuoteReplyToSend {
|
||||
if (this.groupId != 0L) {
|
||||
check(sender is Member) { "sender must be Member to quote a GroupMessage" }
|
||||
}
|
||||
return QuoteReplyToSend(this, sender)
|
||||
fun MessageSource.quote(from: QQ?): QuoteReplyToSend {
|
||||
return if (this.groupId != 0L) {
|
||||
check(from is Member) { "sender must be Member to quote a GroupMessage" }
|
||||
QuoteReplyToSend.ToGroup(this, from)
|
||||
} else QuoteReplyToSend.ToFriend(this)
|
||||
}
|
Loading…
Reference in New Issue
Block a user