支持获取消息记录 (漫游消息) (#1520)

* Support roaming messages for friend

* Remove `RoamingMessage.getMessage`

* Remove `RoamingAuthenticator`

* Mark `@JavaFriendlyAPI`

* apiDump

Co-authored-by: Karlatemp <karlatemp@vip.qq.com>
This commit is contained in:
Him188 2021-09-18 19:37:13 +08:00 committed by GitHub
parent 3b7e85f3c5
commit ef301cc6e5
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
11 changed files with 579 additions and 5 deletions

View File

@ -331,7 +331,7 @@ public abstract interface class net/mamoe/mirai/contact/FileSupported : net/mamo
public abstract fun getFilesRoot ()Lnet/mamoe/mirai/utils/RemoteFile;
}
public abstract interface class net/mamoe/mirai/contact/Friend : kotlinx/coroutines/CoroutineScope, net/mamoe/mirai/contact/AudioSupported, net/mamoe/mirai/contact/User {
public abstract interface class net/mamoe/mirai/contact/Friend : kotlinx/coroutines/CoroutineScope, net/mamoe/mirai/contact/AudioSupported, net/mamoe/mirai/contact/User, net/mamoe/mirai/contact/roaming/RoamingSupported {
public fun delete ()V
public abstract fun delete (Lkotlin/coroutines/Continuation;)Ljava/lang/Object;
public fun nudge ()Lnet/mamoe/mirai/message/action/FriendNudge;
@ -748,6 +748,57 @@ public final class net/mamoe/mirai/contact/announcement/OnlineAnnouncementKt {
public static final fun getBot (Lnet/mamoe/mirai/contact/announcement/OnlineAnnouncement;)Lnet/mamoe/mirai/Bot;
}
public abstract interface class net/mamoe/mirai/contact/roaming/RoamingMessage {
public fun getBot ()Lnet/mamoe/mirai/Bot;
public abstract fun getContact ()Lnet/mamoe/mirai/contact/Contact;
public abstract fun getIds ()[I
public abstract fun getInternalIds ()[I
public abstract fun getSender ()J
public abstract fun getTarget ()J
public abstract fun getTime ()J
}
public abstract interface class net/mamoe/mirai/contact/roaming/RoamingMessageFilter {
public static final field ANY Lnet/mamoe/mirai/contact/roaming/RoamingMessageFilter;
public static final field Companion Lnet/mamoe/mirai/contact/roaming/RoamingMessageFilter$Companion;
public static final field RECEIVED Lnet/mamoe/mirai/contact/roaming/RoamingMessageFilter;
public static final field SENT Lnet/mamoe/mirai/contact/roaming/RoamingMessageFilter;
public fun and (Lnet/mamoe/mirai/contact/roaming/RoamingMessageFilter;)Lnet/mamoe/mirai/contact/roaming/RoamingMessageFilter;
public abstract fun invoke (Lnet/mamoe/mirai/contact/roaming/RoamingMessage;)Z
public fun not ()Lnet/mamoe/mirai/contact/roaming/RoamingMessageFilter;
public fun or (Lnet/mamoe/mirai/contact/roaming/RoamingMessageFilter;)Lnet/mamoe/mirai/contact/roaming/RoamingMessageFilter;
}
public final class net/mamoe/mirai/contact/roaming/RoamingMessageFilter$Companion {
}
public abstract interface class net/mamoe/mirai/contact/roaming/RoamingMessages {
public fun getAllMessages (Lnet/mamoe/mirai/contact/roaming/RoamingMessageFilter;)Lkotlinx/coroutines/flow/Flow;
public fun getAllMessages (Lnet/mamoe/mirai/contact/roaming/RoamingMessageFilter;Lkotlin/coroutines/Continuation;)Ljava/lang/Object;
public static synthetic fun getAllMessages$default (Lnet/mamoe/mirai/contact/roaming/RoamingMessages;Lnet/mamoe/mirai/contact/roaming/RoamingMessageFilter;ILjava/lang/Object;)Lkotlinx/coroutines/flow/Flow;
public static synthetic fun getAllMessages$default (Lnet/mamoe/mirai/contact/roaming/RoamingMessages;Lnet/mamoe/mirai/contact/roaming/RoamingMessageFilter;Lkotlin/coroutines/Continuation;ILjava/lang/Object;)Ljava/lang/Object;
public fun getAllMessagesStream ()Ljava/util/stream/Stream;
public fun getAllMessagesStream (Lkotlin/coroutines/Continuation;)Ljava/lang/Object;
public fun getAllMessagesStream (Lnet/mamoe/mirai/contact/roaming/RoamingMessageFilter;)Ljava/util/stream/Stream;
public fun getAllMessagesStream (Lnet/mamoe/mirai/contact/roaming/RoamingMessageFilter;Lkotlin/coroutines/Continuation;)Ljava/lang/Object;
public static synthetic fun getAllMessagesStream$default (Lnet/mamoe/mirai/contact/roaming/RoamingMessages;Lnet/mamoe/mirai/contact/roaming/RoamingMessageFilter;ILjava/lang/Object;)Ljava/util/stream/Stream;
public static synthetic fun getAllMessagesStream$default (Lnet/mamoe/mirai/contact/roaming/RoamingMessages;Lnet/mamoe/mirai/contact/roaming/RoamingMessageFilter;Lkotlin/coroutines/Continuation;ILjava/lang/Object;)Ljava/lang/Object;
public fun getMessagesIn (JJLnet/mamoe/mirai/contact/roaming/RoamingMessageFilter;)Lkotlinx/coroutines/flow/Flow;
public abstract fun getMessagesIn (JJLnet/mamoe/mirai/contact/roaming/RoamingMessageFilter;Lkotlin/coroutines/Continuation;)Ljava/lang/Object;
public static synthetic fun getMessagesIn$default (Lnet/mamoe/mirai/contact/roaming/RoamingMessages;JJLnet/mamoe/mirai/contact/roaming/RoamingMessageFilter;ILjava/lang/Object;)Lkotlinx/coroutines/flow/Flow;
public static synthetic fun getMessagesIn$default (Lnet/mamoe/mirai/contact/roaming/RoamingMessages;JJLnet/mamoe/mirai/contact/roaming/RoamingMessageFilter;Lkotlin/coroutines/Continuation;ILjava/lang/Object;)Ljava/lang/Object;
public fun getMessagesStream (JJ)Ljava/util/stream/Stream;
public fun getMessagesStream (JJLkotlin/coroutines/Continuation;)Ljava/lang/Object;
public fun getMessagesStream (JJLnet/mamoe/mirai/contact/roaming/RoamingMessageFilter;)Ljava/util/stream/Stream;
public abstract fun getMessagesStream (JJLnet/mamoe/mirai/contact/roaming/RoamingMessageFilter;Lkotlin/coroutines/Continuation;)Ljava/lang/Object;
public static synthetic fun getMessagesStream$default (Lnet/mamoe/mirai/contact/roaming/RoamingMessages;JJLnet/mamoe/mirai/contact/roaming/RoamingMessageFilter;ILjava/lang/Object;)Ljava/util/stream/Stream;
public static synthetic fun getMessagesStream$default (Lnet/mamoe/mirai/contact/roaming/RoamingMessages;JJLnet/mamoe/mirai/contact/roaming/RoamingMessageFilter;Lkotlin/coroutines/Continuation;ILjava/lang/Object;)Ljava/lang/Object;
}
public abstract interface class net/mamoe/mirai/contact/roaming/RoamingSupported : net/mamoe/mirai/contact/Contact {
public abstract fun getRoamingMessages ()Lnet/mamoe/mirai/contact/roaming/RoamingMessages;
}
public abstract interface class net/mamoe/mirai/data/FriendInfo : net/mamoe/mirai/data/UserInfo {
public abstract fun getNick ()Ljava/lang/String;
public abstract fun getRemark ()Ljava/lang/String;

View File

@ -331,7 +331,7 @@ public abstract interface class net/mamoe/mirai/contact/FileSupported : net/mamo
public abstract fun getFilesRoot ()Lnet/mamoe/mirai/utils/RemoteFile;
}
public abstract interface class net/mamoe/mirai/contact/Friend : kotlinx/coroutines/CoroutineScope, net/mamoe/mirai/contact/AudioSupported, net/mamoe/mirai/contact/User {
public abstract interface class net/mamoe/mirai/contact/Friend : kotlinx/coroutines/CoroutineScope, net/mamoe/mirai/contact/AudioSupported, net/mamoe/mirai/contact/User, net/mamoe/mirai/contact/roaming/RoamingSupported {
public fun delete ()V
public abstract fun delete (Lkotlin/coroutines/Continuation;)Ljava/lang/Object;
public fun nudge ()Lnet/mamoe/mirai/message/action/FriendNudge;
@ -748,6 +748,57 @@ public final class net/mamoe/mirai/contact/announcement/OnlineAnnouncementKt {
public static final fun getBot (Lnet/mamoe/mirai/contact/announcement/OnlineAnnouncement;)Lnet/mamoe/mirai/Bot;
}
public abstract interface class net/mamoe/mirai/contact/roaming/RoamingMessage {
public fun getBot ()Lnet/mamoe/mirai/Bot;
public abstract fun getContact ()Lnet/mamoe/mirai/contact/Contact;
public abstract fun getIds ()[I
public abstract fun getInternalIds ()[I
public abstract fun getSender ()J
public abstract fun getTarget ()J
public abstract fun getTime ()J
}
public abstract interface class net/mamoe/mirai/contact/roaming/RoamingMessageFilter {
public static final field ANY Lnet/mamoe/mirai/contact/roaming/RoamingMessageFilter;
public static final field Companion Lnet/mamoe/mirai/contact/roaming/RoamingMessageFilter$Companion;
public static final field RECEIVED Lnet/mamoe/mirai/contact/roaming/RoamingMessageFilter;
public static final field SENT Lnet/mamoe/mirai/contact/roaming/RoamingMessageFilter;
public fun and (Lnet/mamoe/mirai/contact/roaming/RoamingMessageFilter;)Lnet/mamoe/mirai/contact/roaming/RoamingMessageFilter;
public abstract fun invoke (Lnet/mamoe/mirai/contact/roaming/RoamingMessage;)Z
public fun not ()Lnet/mamoe/mirai/contact/roaming/RoamingMessageFilter;
public fun or (Lnet/mamoe/mirai/contact/roaming/RoamingMessageFilter;)Lnet/mamoe/mirai/contact/roaming/RoamingMessageFilter;
}
public final class net/mamoe/mirai/contact/roaming/RoamingMessageFilter$Companion {
}
public abstract interface class net/mamoe/mirai/contact/roaming/RoamingMessages {
public fun getAllMessages (Lnet/mamoe/mirai/contact/roaming/RoamingMessageFilter;)Lkotlinx/coroutines/flow/Flow;
public fun getAllMessages (Lnet/mamoe/mirai/contact/roaming/RoamingMessageFilter;Lkotlin/coroutines/Continuation;)Ljava/lang/Object;
public static synthetic fun getAllMessages$default (Lnet/mamoe/mirai/contact/roaming/RoamingMessages;Lnet/mamoe/mirai/contact/roaming/RoamingMessageFilter;ILjava/lang/Object;)Lkotlinx/coroutines/flow/Flow;
public static synthetic fun getAllMessages$default (Lnet/mamoe/mirai/contact/roaming/RoamingMessages;Lnet/mamoe/mirai/contact/roaming/RoamingMessageFilter;Lkotlin/coroutines/Continuation;ILjava/lang/Object;)Ljava/lang/Object;
public fun getAllMessagesStream ()Ljava/util/stream/Stream;
public fun getAllMessagesStream (Lkotlin/coroutines/Continuation;)Ljava/lang/Object;
public fun getAllMessagesStream (Lnet/mamoe/mirai/contact/roaming/RoamingMessageFilter;)Ljava/util/stream/Stream;
public fun getAllMessagesStream (Lnet/mamoe/mirai/contact/roaming/RoamingMessageFilter;Lkotlin/coroutines/Continuation;)Ljava/lang/Object;
public static synthetic fun getAllMessagesStream$default (Lnet/mamoe/mirai/contact/roaming/RoamingMessages;Lnet/mamoe/mirai/contact/roaming/RoamingMessageFilter;ILjava/lang/Object;)Ljava/util/stream/Stream;
public static synthetic fun getAllMessagesStream$default (Lnet/mamoe/mirai/contact/roaming/RoamingMessages;Lnet/mamoe/mirai/contact/roaming/RoamingMessageFilter;Lkotlin/coroutines/Continuation;ILjava/lang/Object;)Ljava/lang/Object;
public fun getMessagesIn (JJLnet/mamoe/mirai/contact/roaming/RoamingMessageFilter;)Lkotlinx/coroutines/flow/Flow;
public abstract fun getMessagesIn (JJLnet/mamoe/mirai/contact/roaming/RoamingMessageFilter;Lkotlin/coroutines/Continuation;)Ljava/lang/Object;
public static synthetic fun getMessagesIn$default (Lnet/mamoe/mirai/contact/roaming/RoamingMessages;JJLnet/mamoe/mirai/contact/roaming/RoamingMessageFilter;ILjava/lang/Object;)Lkotlinx/coroutines/flow/Flow;
public static synthetic fun getMessagesIn$default (Lnet/mamoe/mirai/contact/roaming/RoamingMessages;JJLnet/mamoe/mirai/contact/roaming/RoamingMessageFilter;Lkotlin/coroutines/Continuation;ILjava/lang/Object;)Ljava/lang/Object;
public fun getMessagesStream (JJ)Ljava/util/stream/Stream;
public fun getMessagesStream (JJLkotlin/coroutines/Continuation;)Ljava/lang/Object;
public fun getMessagesStream (JJLnet/mamoe/mirai/contact/roaming/RoamingMessageFilter;)Ljava/util/stream/Stream;
public abstract fun getMessagesStream (JJLnet/mamoe/mirai/contact/roaming/RoamingMessageFilter;Lkotlin/coroutines/Continuation;)Ljava/lang/Object;
public static synthetic fun getMessagesStream$default (Lnet/mamoe/mirai/contact/roaming/RoamingMessages;JJLnet/mamoe/mirai/contact/roaming/RoamingMessageFilter;ILjava/lang/Object;)Ljava/util/stream/Stream;
public static synthetic fun getMessagesStream$default (Lnet/mamoe/mirai/contact/roaming/RoamingMessages;JJLnet/mamoe/mirai/contact/roaming/RoamingMessageFilter;Lkotlin/coroutines/Continuation;ILjava/lang/Object;)Ljava/lang/Object;
}
public abstract interface class net/mamoe/mirai/contact/roaming/RoamingSupported : net/mamoe/mirai/contact/Contact {
public abstract fun getRoamingMessages ()Lnet/mamoe/mirai/contact/roaming/RoamingMessages;
}
public abstract interface class net/mamoe/mirai/data/FriendInfo : net/mamoe/mirai/data/UserInfo {
public abstract fun getNick ()Ljava/lang/String;
public abstract fun getRemark ()Ljava/lang/String;

View File

@ -15,6 +15,7 @@ package net.mamoe.mirai.contact
import kotlinx.coroutines.CoroutineScope
import net.mamoe.kjbb.JvmBlockingBridge
import net.mamoe.mirai.Bot
import net.mamoe.mirai.contact.roaming.RoamingSupported
import net.mamoe.mirai.event.events.*
import net.mamoe.mirai.message.MessageReceipt
import net.mamoe.mirai.message.action.FriendNudge
@ -34,7 +35,7 @@ import net.mamoe.mirai.utils.NotStableForInheritance
* @see FriendMessageEvent
*/
@NotStableForInheritance
public interface Friend : User, CoroutineScope, AudioSupported {
public interface Friend : User, CoroutineScope, AudioSupported, RoamingSupported {
/**
* 向这个对象发送消息.
*

View File

@ -0,0 +1,90 @@
/*
* Copyright 2019-2021 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.contact.roaming
import net.mamoe.mirai.Bot
import net.mamoe.mirai.contact.Contact
import net.mamoe.mirai.message.data.MessageSource
/**
* @since 2.8
*/
public fun interface RoamingMessageFilter {
public operator fun invoke(roamingMessage: RoamingMessage): Boolean
public infix fun and(other: RoamingMessageFilter): RoamingMessageFilter {
return RoamingMessageFilter { this.invoke(it) && other.invoke(it) }
}
public infix fun or(other: RoamingMessageFilter): RoamingMessageFilter {
return RoamingMessageFilter { this.invoke(it) || other.invoke(it) }
}
public fun not(): RoamingMessageFilter {
return RoamingMessageFilter { !this.invoke(it) }
}
public companion object {
/**
* 筛选任何消息 (相当于不筛选)
*/
@JvmField
public val ANY: RoamingMessageFilter = RoamingMessageFilter { true }
/**
* 筛选 bot 接收的消息
*/
@JvmField
public val RECEIVED: RoamingMessageFilter = RoamingMessageFilter { it.sender != it.bot.id }
/**
* 筛选 bot 发送的消息
*/
@JvmField
public val SENT: RoamingMessageFilter = RoamingMessageFilter { it.sender == it.bot.id }
}
}
/**
* 还未解析的漫游消息.
*
* @since 2.8
*/
public interface RoamingMessage {
public val contact: Contact
public val bot: Bot get() = contact.bot
/**
* 发送人 id
*/
public val sender: Long
/**
* 收信人或群的 id
*/
public val target: Long
/**
* 时间戳, 单位为秒, 服务器时间.
*/
public val time: Long
/**
* @see MessageSource.ids
*/
public val ids: IntArray
/**
* @see MessageSource.internalIds
*/
public val internalIds: IntArray
}

View File

@ -0,0 +1,101 @@
/*
* Copyright 2019-2021 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
*/
@file:JvmBlockingBridge
package net.mamoe.mirai.contact.roaming
import kotlinx.coroutines.flow.Flow
import net.mamoe.kjbb.JvmBlockingBridge
import net.mamoe.mirai.message.data.MessageChain
import net.mamoe.mirai.message.data.MessageSource
import net.mamoe.mirai.utils.JavaFriendlyAPI
import java.util.stream.Stream
/**
* 漫游消息记录管理器.
*
* @since 2.8
* @see RoamingSupported
*/
public interface RoamingMessages {
///////////////////////////////////////////////////////////////////////////
// Get list
///////////////////////////////////////////////////////////////////////////
/**
* 查询指定时间段内的漫游消息记录.
*
* 返回查询到的漫游消息记录, 顺序为由新到旧. 这些 [MessageChain] 与从事件中收到的消息链相似, 属于在线消息.
* 可从 [MessageChain] 获取 [MessageSource] 来确定发送人等相关信息, 也可以进行引用回复或撤回.
*
* 性能提示: 请在 [filter] 执行筛选, [filter] 返回 `false` 则不会解析消息链, 这对本函数的处理速度有决定性影响.
*
* @param timeStart 起始时间, UTC+8 时间戳, 单位为秒. 可以为 `0`, 即表示从可以获取的最早的消息起. 负数将会被看是 `0`.
* @param timeEnd 结束时间, UTC+8 时间戳, 单位为秒. 可以为 [Long.MAX_VALUE], 即表示到可以获取的最晚的消息为止. 低于 [timeStart] 的值将会被看作是 [timeStart] 的值.
* @param filter 过滤器.
*/
public suspend fun getMessagesIn(
timeStart: Long,
timeEnd: Long,
filter: RoamingMessageFilter? = null
): Flow<MessageChain>
/**
* 查询所有漫游消息记录.
*
* 返回查询到的漫游消息记录, 顺序为由新到旧. 这些 [MessageChain] 与从事件中收到的消息链相似, 属于在线消息.
* 可从 [MessageChain] 获取 [MessageSource] 来确定发送人等相关信息, 也可以进行引用回复或撤回.
*
* 性能提示: 请在 [filter] 执行筛选, [filter] 返回 `false` 则不会解析消息链, 这对本函数的处理速度有决定性影响.
*
* @param filter 过滤器.
*/
public suspend fun getAllMessages(
filter: RoamingMessageFilter? = null
): Flow<MessageChain> = getMessagesIn(0, Long.MAX_VALUE, filter)
/**
* 查询指定时间段内的漫游消息记录.
*
* 返回查询到的漫游消息记录, 顺序为由新到旧. 这些 [MessageChain] 与从事件中收到的消息链相似, 属于在线消息.
* 可从 [MessageChain] 获取 [MessageSource] 来确定发送人等相关信息, 也可以进行引用回复或撤回.
*
* 性能提示: 请在 [filter] 执行筛选, [filter] 返回 `false` 则不会解析消息链, 这对本函数的处理速度有决定性影响.
*
* @param timeStart 起始时间, UTC+8 时间戳, 单位为秒. 可以为 `0`, 即表示从可以获取的最早的消息起. 负数将会被看是 `0`.
* @param timeEnd 结束时间, UTC+8 时间戳, 单位为秒. 可以为 [Long.MAX_VALUE], 即表示到可以获取的最晚的消息为止. 低于 [timeStart] 的值将会被看作是 [timeStart] 的值.
* @param filter 过滤器.
*/
@Suppress("OVERLOADS_INTERFACE")
@JvmOverloads
@JavaFriendlyAPI
public suspend fun getMessagesStream(
timeStart: Long,
timeEnd: Long,
filter: RoamingMessageFilter? = null
): Stream<MessageChain>
/**
* 查询所有漫游消息记录.
*
* 返回查询到的漫游消息记录, 顺序为由新到旧. 这些 [MessageChain] 与从事件中收到的消息链相似, 属于在线消息.
* 可从 [MessageChain] 获取 [MessageSource] 来确定发送人等相关信息, 也可以进行引用回复或撤回.
*
* 性能提示: 请在 [filter] 执行筛选, [filter] 返回 `false` 则不会解析消息链, 这对本函数的处理速度有决定性影响.
*
* @param filter 过滤器.
*/
@Suppress("OVERLOADS_INTERFACE")
@JvmOverloads
@JavaFriendlyAPI
public suspend fun getAllMessagesStream(
filter: RoamingMessageFilter? = null
): Stream<MessageChain> = getMessagesStream(0, Long.MAX_VALUE, filter)
}

View File

@ -0,0 +1,26 @@
/*
* Copyright 2019-2021 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
*/
@file:JvmBlockingBridge
package net.mamoe.mirai.contact.roaming
import net.mamoe.kjbb.JvmBlockingBridge
import net.mamoe.mirai.contact.Contact
/**
* 支持查询漫游消息记录的 [Contact].
* @since 2.8
*/
public interface RoamingSupported : Contact {
/**
* 获取漫游消息记录管理器.
*/
public val roamingMessages: RoamingMessages
}

View File

@ -17,10 +17,12 @@ package net.mamoe.mirai.internal.contact
import net.mamoe.mirai.LowLevelApi
import net.mamoe.mirai.Mirai
import net.mamoe.mirai.contact.Friend
import net.mamoe.mirai.contact.roaming.RoamingMessages
import net.mamoe.mirai.event.events.FriendMessagePostSendEvent
import net.mamoe.mirai.event.events.FriendMessagePreSendEvent
import net.mamoe.mirai.internal.QQAndroidBot
import net.mamoe.mirai.internal.contact.info.FriendInfoImpl
import net.mamoe.mirai.internal.contact.roaming.RoamingMessagesImplFriend
import net.mamoe.mirai.internal.message.OfflineAudioImpl
import net.mamoe.mirai.internal.network.highway.*
import net.mamoe.mirai.internal.network.protocol.data.proto.Cmd0x346
@ -61,8 +63,8 @@ internal class FriendImpl(
override val info: FriendInfoImpl,
) : Friend, AbstractUser(bot, parentCoroutineContext, info) {
override suspend fun delete() {
check(bot.friends[this.id] != null) {
"Friend ${this.id} had already been deleted"
check(bot.friends[id] != null) {
"Friend $id had already been deleted"
}
bot.network.run {
FriendList.DelFriend.invoke(bot.client, this@FriendImpl).sendAndExpect().also {
@ -145,4 +147,6 @@ internal class FriendImpl(
return audio!!
}
override val roamingMessages: RoamingMessages by lazy { RoamingMessagesImplFriend(this) }
}

View File

@ -0,0 +1,137 @@
/*
* Copyright 2019-2021 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
*/
@file:Suppress("INVISIBLE_REFERENCE", "INVISIBLE_MEMBER")
package net.mamoe.mirai.internal.contact.roaming
import kotlinx.coroutines.currentCoroutineContext
import kotlinx.coroutines.flow.Flow
import kotlinx.coroutines.flow.flow
import kotlinx.coroutines.isActive
import kotlinx.coroutines.runBlocking
import net.mamoe.mirai.contact.Contact
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.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.receive.MessageSvcPbGetRoamMsgReq
import net.mamoe.mirai.internal.network.protocol.packet.sendAndExpect
import net.mamoe.mirai.message.data.MessageChain
import net.mamoe.mirai.utils.*
import java.util.stream.Stream
internal sealed class RoamingMessagesImpl : RoamingMessages {
abstract val contact: AbstractContact
override suspend fun getMessagesIn(
timeStart: Long,
timeEnd: Long,
filter: RoamingMessageFilter?
): Flow<MessageChain> {
return flow {
var lastMessageTime = timeEnd.coerceAtLeast(timeStart).coerceAtLeast(1)
var random = 0L
while (currentCoroutineContext().isActive) {
val resp = requestRoamMsg(timeStart, lastMessageTime, random)
val messages = resp.messages ?: break
if (filter == null || filter === RoamingMessageFilter.ANY) {
// fast path
messages.forEach { emit(it.toMessageChainOnline(contact.bot)) }
} else {
for (message in messages) {
if (filter.invoke(createRoamingMessage(message, messages))) {
emit(message.toMessageChainOnline(contact.bot))
}
}
}
lastMessageTime = resp.lastMessageTime
random = resp.random
}
}
}
private fun createRoamingMessage(
message: MsgComm.Msg,
messages: List<MsgComm.Msg>
) = object : RoamingMessage {
override val contact: Contact get() = this@RoamingMessagesImpl.contact
override val sender: Long get() = message.msgHead.fromUin
override val target: Long
get() = message.msgHead.groupInfo?.groupCode ?: message.msgHead.toUin
override val time: Long get() = message.msgHead.msgTime.toLongUnsigned()
override val ids: IntArray by lazy { messages.mapToIntArray { it.msgHead.msgSeq } }
override val internalIds: IntArray by lazy {
messages.mapToIntArray { it.msgBody.richText.attr?.random ?: 0 } // other client 消息的这个是0
}
}
@JavaFriendlyAPI
override suspend fun getMessagesStream(
timeStart: Long,
timeEnd: Long,
filter: RoamingMessageFilter?,
): Stream<MessageChain> {
return stream {
var lastMessageTime = timeEnd
var random = 0L
while (true) {
val resp = runBlocking {
requestRoamMsg(timeStart, lastMessageTime, random)
}
val messages = resp.messages ?: break
if (filter == null || filter === RoamingMessageFilter.ANY) {
messages.forEach { yield(runBlocking { it.toMessageChainOnline(contact.bot) }) }
} else {
for (message in messages) {
if (filter.invoke(createRoamingMessage(message, messages))) {
yield(runBlocking { message.toMessageChainOnline(contact.bot) })
}
}
}
lastMessageTime = resp.lastMessageTime
random = resp.random
}
}
}
abstract suspend fun requestRoamMsg(
timeStart: Long,
lastMessageTime: Long,
random: Long
): MessageSvcPbGetRoamMsgReq.Response
}
internal class RoamingMessagesImplFriend(
override val contact: FriendImpl
) : RoamingMessagesImpl() {
override suspend fun requestRoamMsg(
timeStart: Long,
lastMessageTime: Long,
random: Long
): MessageSvcPbGetRoamMsgReq.Response {
return MessageSvcPbGetRoamMsgReq.createForFriend(
client = contact.bot.client,
uin = contact.id,
timeStart = timeStart,
lastMsgTime = lastMessageTime,
random = random,
maxCount = 1000,
sig = byteArrayOf(),
pwd = byteArrayOf()
).sendAndExpect(contact.bot).value.check()
}
}

View File

@ -50,6 +50,27 @@ internal suspend fun List<MsgComm.Msg>.toMessageChainOnline(
return toMessageChain(bot, groupIdOrZero, true, messageSourceKind).refineDeep(bot, refineContext)
}
internal suspend fun MsgComm.Msg.toMessageChainOnline(
bot: Bot,
refineContext: RefineContext = EmptyRefineContext,
): MessageChain {
fun getSourceKind(c2cCmd: Int): MessageSourceKind {
return when (c2cCmd) {
11 -> MessageSourceKind.FRIEND // bot 给其他人发消息
4 -> MessageSourceKind.FRIEND // bot 给自己作为好友发消息 (非 other client)
1 -> MessageSourceKind.GROUP
else -> error("Could not get source kind from c2cCmd: $c2cCmd")
}
}
val kind = getSourceKind(msgHead.c2cCmd)
val groupId = when (kind) {
MessageSourceKind.GROUP -> msgHead.groupInfo?.groupCode ?: 0
else -> 0
}
return listOf(this).toMessageChainOnline(bot, groupId, kind, refineContext)
}
//internal fun List<MsgComm.Msg>.toMessageChainOffline(
// bot: Bot,
// groupIdOrZero: Long,

View File

@ -135,6 +135,7 @@ internal object KnownPacketFactories {
MessageSvcPushForceOffline,
MessageSvcPbSendMsg,
MessageSvcPbDeleteMsg,
MessageSvcPbGetRoamMsgReq,
FriendList.GetFriendGroupList,
FriendList.DelFriend,
FriendList.GetTroopListSimplify,

View File

@ -0,0 +1,91 @@
/*
* Copyright 2019-2021 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.network.protocol.packet.chat.receive
import kotlinx.io.core.ByteReadPacket
import net.mamoe.mirai.internal.QQAndroidBot
import net.mamoe.mirai.internal.network.Packet
import net.mamoe.mirai.internal.network.QQAndroidClient
import net.mamoe.mirai.internal.network.protocol.data.proto.MsgComm
import net.mamoe.mirai.internal.network.protocol.data.proto.MsgSvc
import net.mamoe.mirai.internal.network.protocol.packet.OutgoingPacketFactory
import net.mamoe.mirai.internal.network.protocol.packet.buildOutgoingUniPacket
import net.mamoe.mirai.internal.utils.io.serialization.readProtoBuf
import net.mamoe.mirai.internal.utils.io.serialization.writeProtoBuf
import net.mamoe.mirai.utils.EMPTY_BYTE_ARRAY
import net.mamoe.mirai.utils.Either
import net.mamoe.mirai.utils.Either.Companion.mapRight
import net.mamoe.mirai.utils.FailureResponse
import net.mamoe.mirai.utils.checked
internal class CheckedResponse<T>(
val value: Either<FailureResponse, T>
) : Packet
internal object MessageSvcPbGetRoamMsgReq :
OutgoingPacketFactory<CheckedResponse<MessageSvcPbGetRoamMsgReq.Response>>("MessageSvc.PbGetRoamMsg") {
class Response(
val messages: List<MsgComm.Msg>?,
val lastMessageTime: Long,
val random: Long,
val sig: ByteArray, // 似乎没被用到, 服务器每次返回不同
) {
}
fun createForFriend(
client: QQAndroidClient,
uin: Long,
timeStart: Long,
lastMsgTime: Long, // 上次 resp 中的, 否则为期待的 time end
random: Long = 0,
maxCount: Int = 1000,
sig: ByteArray = EMPTY_BYTE_ARRAY, // 客户端每次请求相同
pwd: ByteArray = EMPTY_BYTE_ARRAY,
) = buildOutgoingUniPacket(client) {
writeProtoBuf(
MsgSvc.PbGetRoamMsgReq.serializer(), MsgSvc.PbGetRoamMsgReq(
peerUin = uin,
beginMsgtime = timeStart,
lastMsgtime = lastMsgTime,
checkPwd = 1, // always
readCnt = maxCount,
subcmd = 1,
reqType = 1,
sig = sig,
pwd = pwd,
random = random,
)
)
}
override suspend fun ByteReadPacket.decode(bot: QQAndroidBot): CheckedResponse<Response> {
val resp = readProtoBuf(MsgSvc.PbGetRoamMsgResp.serializer())
if (resp.result == 1) return CheckedResponse(
Either.right(
Response(
null,
resp.lastMsgtime,
resp.random,
resp.sig,
)
)
) // finished
return CheckedResponse(resp.checked().mapRight {
Response(
messages = resp.msg.asReversed(),
lastMessageTime = resp.lastMsgtime,
random = resp.random,
sig = resp.sig,
)
})
}
}