mirror of
https://github.com/mamoe/mirai.git
synced 2024-12-27 09:00:15 +08:00
[core] Proposal implementation of RoamingSupported
for Group
This commit is contained in:
parent
3c580eead4
commit
2d0b4d470a
@ -18,6 +18,7 @@ import net.mamoe.mirai.Bot
|
|||||||
import net.mamoe.mirai.contact.active.GroupActive
|
import net.mamoe.mirai.contact.active.GroupActive
|
||||||
import net.mamoe.mirai.contact.announcement.Announcements
|
import net.mamoe.mirai.contact.announcement.Announcements
|
||||||
import net.mamoe.mirai.contact.file.RemoteFiles
|
import net.mamoe.mirai.contact.file.RemoteFiles
|
||||||
|
import net.mamoe.mirai.contact.roaming.RoamingSupported
|
||||||
import net.mamoe.mirai.event.events.*
|
import net.mamoe.mirai.event.events.*
|
||||||
import net.mamoe.mirai.message.MessageReceipt
|
import net.mamoe.mirai.message.MessageReceipt
|
||||||
import net.mamoe.mirai.message.data.*
|
import net.mamoe.mirai.message.data.*
|
||||||
@ -60,7 +61,7 @@ import kotlin.jvm.JvmSynthetic
|
|||||||
* 使用 [Group.files] 获取群文件管理器后操作. 详见 [RemoteFiles].
|
* 使用 [Group.files] 获取群文件管理器后操作. 详见 [RemoteFiles].
|
||||||
*/
|
*/
|
||||||
@NotStableForInheritance
|
@NotStableForInheritance
|
||||||
public interface Group : Contact, CoroutineScope, FileSupported, AudioSupported {
|
public interface Group : Contact, CoroutineScope, FileSupported, AudioSupported, RoamingSupported {
|
||||||
/**
|
/**
|
||||||
* 群名称.
|
* 群名称.
|
||||||
*
|
*
|
||||||
|
@ -17,6 +17,7 @@ import net.mamoe.mirai.contact.*
|
|||||||
import net.mamoe.mirai.contact.announcement.OfflineAnnouncement
|
import net.mamoe.mirai.contact.announcement.OfflineAnnouncement
|
||||||
import net.mamoe.mirai.contact.announcement.buildAnnouncementParameters
|
import net.mamoe.mirai.contact.announcement.buildAnnouncementParameters
|
||||||
import net.mamoe.mirai.contact.file.RemoteFiles
|
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.GroupHonorType
|
||||||
import net.mamoe.mirai.data.MemberInfo
|
import net.mamoe.mirai.data.MemberInfo
|
||||||
import net.mamoe.mirai.event.broadcast
|
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.MockNormalMember
|
||||||
import net.mamoe.mirai.mock.contact.active.MockGroupActive
|
import net.mamoe.mirai.mock.contact.active.MockGroupActive
|
||||||
import net.mamoe.mirai.mock.internal.contact.active.MockGroupActiveImpl
|
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.OnlineMsgSrcToGroup
|
||||||
import net.mamoe.mirai.mock.internal.msgsrc.newMsgSrc
|
import net.mamoe.mirai.mock.internal.msgsrc.newMsgSrc
|
||||||
import net.mamoe.mirai.mock.utils.broadcastBlocking
|
import net.mamoe.mirai.mock.utils.broadcastBlocking
|
||||||
@ -353,4 +355,6 @@ internal class MockGroupImpl(
|
|||||||
override fun toString(): String {
|
override fun toString(): String {
|
||||||
return "Group($id)"
|
return "Group($id)"
|
||||||
}
|
}
|
||||||
|
|
||||||
|
override val roamingMessages: RoamingMessages = MockRoamingMessages(this)
|
||||||
}
|
}
|
@ -12,11 +12,8 @@ package net.mamoe.mirai.mock.test.mock
|
|||||||
import kotlinx.coroutines.flow.toList
|
import kotlinx.coroutines.flow.toList
|
||||||
import net.mamoe.mirai.contact.MemberPermission
|
import net.mamoe.mirai.contact.MemberPermission
|
||||||
import net.mamoe.mirai.event.events.*
|
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.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.MockActions.mockFireRecalled
|
||||||
import net.mamoe.mirai.mock.test.MockBotTestBase
|
import net.mamoe.mirai.mock.test.MockBotTestBase
|
||||||
import net.mamoe.mirai.mock.utils.broadcastMockEvents
|
import net.mamoe.mirai.mock.utils.broadcastMockEvents
|
||||||
@ -148,6 +145,24 @@ internal class MessagingTest: MockBotTestBase() {
|
|||||||
assertEquals(messageChainOf(PlainText("Test2!")), messages[1])
|
assertEquals(messageChainOf(PlainText("Test2!")), messages[1])
|
||||||
assertEquals(messageChainOf(PlainText("Pong!")), messages[2])
|
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
|
@Test
|
||||||
|
@ -20,6 +20,7 @@ import net.mamoe.mirai.contact.*
|
|||||||
import net.mamoe.mirai.contact.active.GroupActive
|
import net.mamoe.mirai.contact.active.GroupActive
|
||||||
import net.mamoe.mirai.contact.announcement.Announcements
|
import net.mamoe.mirai.contact.announcement.Announcements
|
||||||
import net.mamoe.mirai.contact.file.RemoteFiles
|
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.GroupHonorType
|
||||||
import net.mamoe.mirai.data.GroupInfo
|
import net.mamoe.mirai.data.GroupInfo
|
||||||
import net.mamoe.mirai.data.MemberInfo
|
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.announcement.AnnouncementsImpl
|
||||||
import net.mamoe.mirai.internal.contact.file.RemoteFilesImpl
|
import net.mamoe.mirai.internal.contact.file.RemoteFilesImpl
|
||||||
import net.mamoe.mirai.internal.contact.info.MemberInfoImpl
|
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.contextualBugReportException
|
||||||
import net.mamoe.mirai.internal.message.data.OfflineAudioImpl
|
import net.mamoe.mirai.internal.message.data.OfflineAudioImpl
|
||||||
import net.mamoe.mirai.internal.message.image.OfflineGroupImage
|
import net.mamoe.mirai.internal.message.image.OfflineGroupImage
|
||||||
@ -389,6 +391,8 @@ internal abstract class CommonGroupImpl constructor(
|
|||||||
return result.success
|
return result.success
|
||||||
}
|
}
|
||||||
|
|
||||||
|
override val roamingMessages: RoamingMessages by lazy { RoamingMessagesImplGroup(this) }
|
||||||
|
|
||||||
override fun toString(): String = "Group($id)"
|
override fun toString(): String = "Group($id)"
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -20,10 +20,14 @@ import net.mamoe.mirai.contact.roaming.RoamingMessage
|
|||||||
import net.mamoe.mirai.contact.roaming.RoamingMessageFilter
|
import net.mamoe.mirai.contact.roaming.RoamingMessageFilter
|
||||||
import net.mamoe.mirai.contact.roaming.RoamingMessages
|
import net.mamoe.mirai.contact.roaming.RoamingMessages
|
||||||
import net.mamoe.mirai.internal.contact.AbstractContact
|
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.contact.FriendImpl
|
||||||
import net.mamoe.mirai.internal.message.toMessageChainOnline
|
import net.mamoe.mirai.internal.message.toMessageChainOnline
|
||||||
import net.mamoe.mirai.internal.network.protocol.data.proto.MsgComm
|
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.network.protocol.packet.chat.receive.MessageSvcPbGetRoamMsgReq
|
||||||
|
import net.mamoe.mirai.internal.utils.indexFirstBE
|
||||||
import net.mamoe.mirai.message.data.MessageChain
|
import net.mamoe.mirai.message.data.MessageChain
|
||||||
import net.mamoe.mirai.utils.check
|
import net.mamoe.mirai.utils.check
|
||||||
import net.mamoe.mirai.utils.mapToIntArray
|
import net.mamoe.mirai.utils.mapToIntArray
|
||||||
@ -106,4 +110,71 @@ internal class RoamingMessagesImplFriend(
|
|||||||
)
|
)
|
||||||
).value.check()
|
).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())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
28
mirai-core/src/commonMain/kotlin/utils/collection.kt
Normal file
28
mirai-core/src/commonMain/kotlin/utils/collection.kt
Normal 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
|
||||||
|
}
|
Loading…
Reference in New Issue
Block a user