[core] Proposal implementation of RoamingSupported for Group

This commit is contained in:
StageGuard 2022-11-08 09:33:42 +08:00 committed by Him188
parent 3c580eead4
commit 2d0b4d470a
6 changed files with 128 additions and 5 deletions

View File

@ -18,6 +18,7 @@ import net.mamoe.mirai.Bot
import net.mamoe.mirai.contact.active.GroupActive
import net.mamoe.mirai.contact.announcement.Announcements
import net.mamoe.mirai.contact.file.RemoteFiles
import net.mamoe.mirai.contact.roaming.RoamingSupported
import net.mamoe.mirai.event.events.*
import net.mamoe.mirai.message.MessageReceipt
import net.mamoe.mirai.message.data.*
@ -60,7 +61,7 @@ import kotlin.jvm.JvmSynthetic
* 使用 [Group.files] 获取群文件管理器后操作. 详见 [RemoteFiles].
*/
@NotStableForInheritance
public interface Group : Contact, CoroutineScope, FileSupported, AudioSupported {
public interface Group : Contact, CoroutineScope, FileSupported, AudioSupported, RoamingSupported {
/**
* 群名称.
*

View File

@ -17,6 +17,7 @@ import net.mamoe.mirai.contact.*
import net.mamoe.mirai.contact.announcement.OfflineAnnouncement
import net.mamoe.mirai.contact.announcement.buildAnnouncementParameters
import net.mamoe.mirai.contact.file.RemoteFiles
import net.mamoe.mirai.contact.roaming.RoamingMessages
import net.mamoe.mirai.data.GroupHonorType
import net.mamoe.mirai.data.MemberInfo
import net.mamoe.mirai.event.broadcast
@ -31,6 +32,7 @@ import net.mamoe.mirai.mock.contact.MockGroupControlPane
import net.mamoe.mirai.mock.contact.MockNormalMember
import net.mamoe.mirai.mock.contact.active.MockGroupActive
import net.mamoe.mirai.mock.internal.contact.active.MockGroupActiveImpl
import net.mamoe.mirai.mock.internal.contact.roaming.MockRoamingMessages
import net.mamoe.mirai.mock.internal.msgsrc.OnlineMsgSrcToGroup
import net.mamoe.mirai.mock.internal.msgsrc.newMsgSrc
import net.mamoe.mirai.mock.utils.broadcastBlocking
@ -353,4 +355,6 @@ internal class MockGroupImpl(
override fun toString(): String {
return "Group($id)"
}
override val roamingMessages: RoamingMessages = MockRoamingMessages(this)
}

View File

@ -12,11 +12,8 @@ package net.mamoe.mirai.mock.test.mock
import kotlinx.coroutines.flow.toList
import net.mamoe.mirai.contact.MemberPermission
import net.mamoe.mirai.event.events.*
import net.mamoe.mirai.message.data.*
import net.mamoe.mirai.message.data.MessageSource.Key.recall
import net.mamoe.mirai.message.data.OnlineMessageSource
import net.mamoe.mirai.message.data.PlainText
import net.mamoe.mirai.message.data.messageChainOf
import net.mamoe.mirai.message.data.source
import net.mamoe.mirai.mock.MockActions.mockFireRecalled
import net.mamoe.mirai.mock.test.MockBotTestBase
import net.mamoe.mirai.mock.utils.broadcastMockEvents
@ -148,6 +145,24 @@ internal class MessagingTest: MockBotTestBase() {
assertEquals(messageChainOf(PlainText("Test2!")), messages[1])
assertEquals(messageChainOf(PlainText("Pong!")), messages[2])
}
val mockGroup = bot.addGroup(2, "2")
val mockGroupMember1 = mockGroup.addMember(123, "123")
val mockGroupMember2 = mockGroup.addMember(124, "124")
val mockGroupMember3 = mockGroup.addMember(125, "125")
broadcastMockEvents {
mockGroupMember1 says { append("msg1") }
mockGroupMember2 says { append("msg2") }
mockGroupMember3 says { append("msg3") }
}
with(mockGroup.roamingMessages.getAllMessages().toList()) {
assertEquals(3, size)
assertEquals(messageChainOf(PlainText("msg1")), get(0))
assertEquals(messageChainOf(PlainText("msg2")), get(1))
assertEquals(messageChainOf(PlainText("msg3")), get(2))
}
}
@Test

View File

@ -20,6 +20,7 @@ import net.mamoe.mirai.contact.*
import net.mamoe.mirai.contact.active.GroupActive
import net.mamoe.mirai.contact.announcement.Announcements
import net.mamoe.mirai.contact.file.RemoteFiles
import net.mamoe.mirai.contact.roaming.RoamingMessages
import net.mamoe.mirai.data.GroupHonorType
import net.mamoe.mirai.data.GroupInfo
import net.mamoe.mirai.data.MemberInfo
@ -30,6 +31,7 @@ import net.mamoe.mirai.internal.contact.active.GroupActiveImpl
import net.mamoe.mirai.internal.contact.announcement.AnnouncementsImpl
import net.mamoe.mirai.internal.contact.file.RemoteFilesImpl
import net.mamoe.mirai.internal.contact.info.MemberInfoImpl
import net.mamoe.mirai.internal.contact.roaming.RoamingMessagesImplGroup
import net.mamoe.mirai.internal.message.contextualBugReportException
import net.mamoe.mirai.internal.message.data.OfflineAudioImpl
import net.mamoe.mirai.internal.message.image.OfflineGroupImage
@ -389,6 +391,8 @@ internal abstract class CommonGroupImpl constructor(
return result.success
}
override val roamingMessages: RoamingMessages by lazy { RoamingMessagesImplGroup(this) }
override fun toString(): String = "Group($id)"
}

View File

@ -20,10 +20,14 @@ import net.mamoe.mirai.contact.roaming.RoamingMessage
import net.mamoe.mirai.contact.roaming.RoamingMessageFilter
import net.mamoe.mirai.contact.roaming.RoamingMessages
import net.mamoe.mirai.internal.contact.AbstractContact
import net.mamoe.mirai.internal.contact.CommonGroupImpl
import net.mamoe.mirai.internal.contact.FriendImpl
import net.mamoe.mirai.internal.message.toMessageChainOnline
import net.mamoe.mirai.internal.network.protocol.data.proto.MsgComm
import net.mamoe.mirai.internal.network.protocol.packet.chat.TroopManagement
import net.mamoe.mirai.internal.network.protocol.packet.chat.receive.MessageSvcPbGetGroupMsg
import net.mamoe.mirai.internal.network.protocol.packet.chat.receive.MessageSvcPbGetRoamMsgReq
import net.mamoe.mirai.internal.utils.indexFirstBE
import net.mamoe.mirai.message.data.MessageChain
import net.mamoe.mirai.utils.check
import net.mamoe.mirai.utils.mapToIntArray
@ -106,4 +110,71 @@ internal class RoamingMessagesImplFriend(
)
).value.check()
}
}
internal class RoamingMessagesImplGroup(
override val contact: CommonGroupImpl
) : RoamingMessagesImpl() {
override suspend fun requestRoamMsg(
timeStart: Long,
lastMessageTime: Long,
random: Long // unused field
): MessageSvcPbGetRoamMsgReq.Response {
val lastMsgSeq = contact.bot.network.sendAndExpect(
TroopManagement.GetGroupLastMsgSeq(
client = contact.bot.client,
groupUin = contact.uin
)
)
return when(lastMsgSeq) {
is TroopManagement.GetGroupLastMsgSeq.Response.Success -> {
val results = mutableListOf<MsgComm.Msg>()
var currentSeq = lastMsgSeq.seq
while (true) {
if (currentSeq <= 0) break
val resp = contact.bot.network.sendAndExpect(
MessageSvcPbGetGroupMsg(
client = contact.bot.client,
groupUin = contact.uin,
messageSequence = currentSeq,
20 // maximum 20
)
)
if (resp is MessageSvcPbGetGroupMsg.Failed) break
if ((resp as MessageSvcPbGetGroupMsg.Success).msgElem.isEmpty()) break
// the message may be sorted increasing by message time,
// if so, additional sortBy will not take cost.
val msgElems = resp.msgElem.sortedBy { it.msgHead.msgTime }
results.addAll(0, msgElems)
val firstMsgElem = msgElems.first()
if (firstMsgElem.msgHead.msgTime < timeStart) {
break
} else {
currentSeq = (firstMsgElem.msgHead.msgSeq - 1).toLong()
}
}
// use binary search to find the first message that message time is lager than lastMessageTime
var right = results.indexFirstBE(lastMessageTime) { it.msgHead.msgTime.toLong() }
// check messages with same time
if (results[right].msgHead.msgTime.toLong() == lastMessageTime) {
do { right ++ } while (right <= results.size - 1 && results[right].msgHead.msgTime <= lastMessageTime)
}
// loops at most 20 times, just traverse
val left = results.indexOfFirst { it.msgHead.msgTime >= timeStart }
MessageSvcPbGetRoamMsgReq.Response(
if (left == right) null else results.subList(left, right),
if (left == right) -1L else results[right - 1].msgHead.msgTime.toLong(), -1L, byteArrayOf()
)
}
is TroopManagement.GetGroupLastMsgSeq.Response.Failed -> {
MessageSvcPbGetRoamMsgReq.Response(null, -1L, -1L, byteArrayOf())
}
}
}
}

View File

@ -0,0 +1,28 @@
/*
* Copyright 2019-2022 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/dev/LICENSE
*/
package net.mamoe.mirai.internal.utils
internal fun <T, M : Comparable<M>> List<T>.indexFirstBE(
value: M,
mapping: (T) -> M
): Int {
var (left, right) = 0 to size - 1
var index = -1
while (left <= right) {
val middle = left + (right - left) / 2
if (mapping(get(middle)) >= value) {
index = middle
right = middle - 1
} else {
left = middle + 1
}
}
return index
}