Commit 3748963a authored by Him188's avatar Him188

Support `MessageReceipt.quoteReply` for group

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