Commit bfdcee1a authored by jiahua.liu's avatar jiahua.liu

List complete

parent bc860a2d
...@@ -10,6 +10,7 @@ import net.mamoe.mirai.message.data.MessageChain ...@@ -10,6 +10,7 @@ import net.mamoe.mirai.message.data.MessageChain
import net.mamoe.mirai.qqandroid.network.protocol.packet.chat.receive.MessageSvc import net.mamoe.mirai.qqandroid.network.protocol.packet.chat.receive.MessageSvc
import net.mamoe.mirai.utils.* import net.mamoe.mirai.utils.*
import kotlin.coroutines.CoroutineContext import kotlin.coroutines.CoroutineContext
import kotlin.coroutines.EmptyCoroutineContext
internal abstract class ContactImpl : Contact internal abstract class ContactImpl : Contact
...@@ -74,13 +75,12 @@ internal class MemberImpl( ...@@ -74,13 +75,12 @@ internal class MemberImpl(
@UseExperimental(MiraiInternalAPI::class) @UseExperimental(MiraiInternalAPI::class)
internal class GroupImpl( internal class GroupImpl(
bot: QQAndroidBot, override val coroutineContext: CoroutineContext, override val id: Long bot: QQAndroidBot, override val coroutineContext: CoroutineContext, override val id: Long,
override var name: String,
override var announcement: String,
override var members: ContactList<Member>
) : ContactImpl(), Group { ) : ContactImpl(), Group {
override lateinit var owner: Member override lateinit var owner: Member
override lateinit var name: String
override lateinit var announcement: String
override lateinit var members: ContactList<Member>
override val internalId: GroupInternalId = GroupId(id).toInternalId() override val internalId: GroupInternalId = GroupId(id).toInternalId()
override fun getMember(id: Long): Member = override fun getMember(id: Long): Member =
......
...@@ -43,17 +43,17 @@ internal abstract class QQAndroidBotBase constructor( ...@@ -43,17 +43,17 @@ internal abstract class QQAndroidBotBase constructor(
override val groups: ContactList<Group> = ContactList(LockFreeLinkedList()) override val groups: ContactList<Group> = ContactList(LockFreeLinkedList())
override suspend fun getGroup(id: GroupId): Group { override suspend fun getGroup(id: GroupId): Group {
return groups.delegate.filteringGetOrAdd({ it.id == id.value }, { GroupImpl(this as QQAndroidBot, coroutineContext, id.value) }) return groups.delegate.getOrNull(id.value) ?: throw NoSuchElementException("Can not found group ${id.value}")
} }
override suspend fun getGroup(internalId: GroupInternalId): Group { override suspend fun getGroup(internalId: GroupInternalId): Group {
internalId.toId().value.let { id -> with(internalId.toId().value) {
return groups.delegate.filteringGetOrAdd({ it.id == id }, { GroupImpl(this as QQAndroidBot, coroutineContext, id) }) return groups.delegate.getOrNull(this) ?: throw NoSuchElementException("Can not found group $this")
} }
} }
override suspend fun getGroup(id: Long): Group { override suspend fun getGroup(id: Long): Group {
return groups.delegate.filteringGetOrAdd({ it.id == id }, { GroupImpl(this as QQAndroidBot, coroutineContext, id) }) return groups.delegate.getOrNull(id) ?: throw NoSuchElementException("Can not found group $id")
} }
override suspend fun Image.getLink(): ImageLink { override suspend fun Image.getLink(): ImageLink {
......
...@@ -9,11 +9,15 @@ import kotlinx.io.core.ByteReadPacket ...@@ -9,11 +9,15 @@ import kotlinx.io.core.ByteReadPacket
import kotlinx.io.core.Input import kotlinx.io.core.Input
import kotlinx.io.core.buildPacket import kotlinx.io.core.buildPacket
import kotlinx.io.core.use import kotlinx.io.core.use
import net.mamoe.mirai.contact.ContactList
import net.mamoe.mirai.contact.Group
import net.mamoe.mirai.contact.Member
import net.mamoe.mirai.data.MultiPacket import net.mamoe.mirai.data.MultiPacket
import net.mamoe.mirai.data.Packet import net.mamoe.mirai.data.Packet
import net.mamoe.mirai.event.* import net.mamoe.mirai.event.*
import net.mamoe.mirai.network.BotNetworkHandler import net.mamoe.mirai.network.BotNetworkHandler
import net.mamoe.mirai.qqandroid.GroupImpl import net.mamoe.mirai.qqandroid.GroupImpl
import net.mamoe.mirai.qqandroid.MemberImpl
import net.mamoe.mirai.qqandroid.QQAndroidBot import net.mamoe.mirai.qqandroid.QQAndroidBot
import net.mamoe.mirai.qqandroid.QQImpl import net.mamoe.mirai.qqandroid.QQImpl
import net.mamoe.mirai.qqandroid.event.ForceOfflineEvent import net.mamoe.mirai.qqandroid.event.ForceOfflineEvent
...@@ -106,8 +110,6 @@ internal class QQAndroidBotNetworkHandler(bot: QQAndroidBot) : BotNetworkHandler ...@@ -106,8 +110,6 @@ internal class QQAndroidBotNetworkHandler(bot: QQAndroidBot) : BotNetworkHandler
} }
override suspend fun init() { override suspend fun init() {
// delay(5000)
this@QQAndroidBotNetworkHandler.subscribeAlways<ForceOfflineEvent> { this@QQAndroidBotNetworkHandler.subscribeAlways<ForceOfflineEvent> {
if (this@QQAndroidBotNetworkHandler.bot == this.bot) { if (this@QQAndroidBotNetworkHandler.bot == this.bot) {
close() close()
...@@ -149,28 +151,65 @@ internal class QQAndroidBotNetworkHandler(bot: QQAndroidBot) : BotNetworkHandler ...@@ -149,28 +151,65 @@ internal class QQAndroidBotNetworkHandler(bot: QQAndroidBot) : BotNetworkHandler
} }
try { try {
bot.logger.info("开始加载群组列表") bot.logger.info("开始加载群组列表与群成员列表")
val troopData = FriendList.GetTroopListSimplify( val troopData = FriendList.GetTroopListSimplify(
bot.client bot.client
).sendAndExpect<FriendList.GetTroopListSimplify.Response>(timeoutMillis = 1000) ).sendAndExpect<FriendList.GetTroopListSimplify.Response>(timeoutMillis = 1000)
println("获取到群数量" + troopData.groups.size)
val toGet: MutableMap<GroupImpl, ContactList<Member>> = mutableMapOf()
troopData.groups.forEach { troopData.groups.forEach {
bot.groups.delegate.addLast(GroupImpl(bot, EmptyCoroutineContext, it.groupUin)) val contactList = ContactList(LockFreeLinkedList<Member>())
val group =
GroupImpl(bot, EmptyCoroutineContext, it.groupUin, it.groupName!!, it.groupMemo!!, contactList)
group.owner =
MemberImpl(QQImpl(bot, EmptyCoroutineContext, it.dwGroupOwnerUin!!), group, EmptyCoroutineContext)
toGet[group] = contactList
bot.groups.delegate.addLast(group)
println(it.groupUin.toString() + " - " + it.groupCode)
}
toGet.forEach {
try {
getTroopMemberList(it.key, it.value)
} catch (e: Exception) {
bot.logger.info("群${it.key.id}的列表拉取失败, 将采用动态加入")
}
delay(200)
} }
bot.logger.info("群组列表加载完成, 共 ${troopData.groups.size}个") bot.logger.info("群组列表与群成员加载完成, 共 ${troopData.groups.size}个")
} catch (e: Exception) { } catch (e: Exception) {
bot.logger.info("加载组信息失败|一般这是由于加载过于频繁导致/将以热加载方式加载群列表") bot.logger.info("加载组信息失败|一般这是由于加载过于频繁导致/将以热加载方式加载群列表")
} }
} }
suspend fun getTroopMemberList(groupUni: Long) { suspend fun getTroopMemberList(group: GroupImpl, list: ContactList<Member>): ContactList<Member> {
bot.logger.info("开始群[$groupUni]成员") bot.logger.info("开始获取群[${group.id}]成员列表")
val data = FriendList.GetTroopMemberList( var size = 0
bot.client, var nextUin = 0L
groupUni, while (true) {
0 val data = FriendList.GetTroopMemberList(
).sendAndExpect<FriendList.GetFriendGroupList.Response>(timeoutMillis = 1000) bot.client,
println(data.contentToString()) group.id,
nextUin
).sendAndExpect<FriendList.GetTroopMemberList.Response>(timeoutMillis = 3000)
data.members.forEach {
list.delegate.addLast(
MemberImpl(
QQImpl(bot, EmptyCoroutineContext, it.memberUin),
group,
EmptyCoroutineContext
)
)
}
size += data.members.size
nextUin = data.nextUin
if (nextUin == 0L) {
break
}
println("已获取群[${group.id}]成员列表前" + size + "个成员")
}
println("群[${group.id}]成员全部获取完成, 共${list.size}个成员")
return list
} }
/** /**
......
...@@ -116,7 +116,8 @@ internal object KnownPacketFactories { ...@@ -116,7 +116,8 @@ internal object KnownPacketFactories {
MessageSvc.PushForceOffline, MessageSvc.PushForceOffline,
MessageSvc.PbSendMsg, MessageSvc.PbSendMsg,
FriendList.GetFriendGroupList, FriendList.GetFriendGroupList,
FriendList.GetTroopListSimplify FriendList.GetTroopListSimplify,
FriendList.GetTroopMemberList
) )
object IncomingFactories : List<IncomingPacketFactory<*>> by mutableListOf( object IncomingFactories : List<IncomingPacketFactory<*>> by mutableListOf(
......
...@@ -27,10 +27,11 @@ internal class FriendList { ...@@ -27,10 +27,11 @@ internal class FriendList {
internal object GetTroopMemberList : internal object GetTroopMemberList :
OutgoingPacketFactory<GetTroopMemberList.Response>("friendlist.GetTroopMemberListReq") { OutgoingPacketFactory<GetTroopMemberList.Response>("friendlist.GetTroopMemberListReq") {
override suspend fun ByteReadPacket.decode(bot: QQAndroidBot): GetTroopMemberList.Response { override suspend fun ByteReadPacket.decode(bot: QQAndroidBot): Response {
val res = this.debugIfFail { this.decodeUniPacket(GetTroopMemberListResp.serializer()) } val res = this.debugIfFail { this.decodeUniPacket(GetTroopMemberListResp.serializer()) }
return Response( return Response(
res.vecTroopMember res.vecTroopMember,
res.nextUin
) )
} }
...@@ -52,10 +53,11 @@ internal class FriendList { ...@@ -52,10 +53,11 @@ internal class FriendList {
GetTroopMemberListReq.serializer(), GetTroopMemberListReq.serializer(),
GetTroopMemberListReq( GetTroopMemberListReq(
uin = client.uin, uin = client.uin,
groupCode = GroupId(targetGroupId).toInternalId().value, groupCode = targetGroupId,
groupUin = targetGroupId, groupUin = targetGroupId,
nextUin = nextUin, nextUin = nextUin,
reqType = 0 reqType = 0,
version = 2
) )
) )
) )
...@@ -64,7 +66,8 @@ internal class FriendList { ...@@ -64,7 +66,8 @@ internal class FriendList {
} }
class Response( class Response(
val members: List<stTroopMemberInfo> val members: List<stTroopMemberInfo>,
val nextUin: Long
) : Packet { ) : Packet {
override fun toString(): String = "Friendlist.GetTroopMemberList.Response" override fun toString(): String = "Friendlist.GetTroopMemberList.Response"
} }
...@@ -73,7 +76,6 @@ internal class FriendList { ...@@ -73,7 +76,6 @@ internal class FriendList {
internal object GetTroopListSimplify : internal object GetTroopListSimplify :
OutgoingPacketFactory<GetTroopListSimplify.Response>("friendlist.GetTroopListReqV2") { OutgoingPacketFactory<GetTroopListSimplify.Response>("friendlist.GetTroopListReqV2") {
override suspend fun ByteReadPacket.decode(bot: QQAndroidBot): Response { override suspend fun ByteReadPacket.decode(bot: QQAndroidBot): Response {
val res = this.decodeUniPacket(GetTroopListRespV2.serializer()) val res = this.decodeUniPacket(GetTroopListRespV2.serializer())
return Response(res.vecTroopList.orEmpty()) return Response(res.vecTroopList.orEmpty())
......
...@@ -10,10 +10,11 @@ import net.mamoe.mirai.utils.coerceAtLeastOrFail ...@@ -10,10 +10,11 @@ import net.mamoe.mirai.utils.coerceAtLeastOrFail
/** /**
* 群. 在 QQ Android 中叫做 "Troop" * 群. 在 QQ Android 中叫做 "Troop"
* *
* Group ID 与 Group Number 并不是同一个值. * Group UIN 与 Group Code 并不是同一个值.
* - Group Number([Group.id]) 是通常使用的群号码.(在 QQ 客户端中可见) * Group Code是在客户端显示的code
* - Group ID([Group.internalId]) 是与调用 API 时使用的 id.(在 QQ 客户端中不可见) * Group Uin是QQ内部的群ID
* @author Him188moe * 在网络调用层 Code与Uin会被混用
* 但在开发层 你应该只关注Group Code
*/ */
interface Group : Contact, CoroutineScope { interface Group : Contact, CoroutineScope {
/** /**
...@@ -75,30 +76,4 @@ interface Group : Contact, CoroutineScope { ...@@ -75,30 +76,4 @@ interface Group : Contact, CoroutineScope {
* *
* @see GroupInternalId.toId 由 [GroupInternalId] 转换为 [GroupId] * @see GroupInternalId.toId 由 [GroupInternalId] 转换为 [GroupId]
* @see GroupId.toInternalId 由 [GroupId] 转换为 [GroupInternalId] * @see GroupId.toInternalId 由 [GroupId] 转换为 [GroupInternalId]
*/ */
inline class GroupId(inline val value: Long) \ No newline at end of file
/**
* 将 [this] 转为 [GroupInternalId].
*/
fun Long.groupInternalId(): GroupInternalId = GroupInternalId(this)
/**
* 将无符号整数格式的 [Long] 转为 [GroupId].
*
* 注: 在 Java 中常用 [Long] 来表示 [UInt].
*
* 注: 在 Kotlin/Java, 有符号的数据类型的二进制最高位为符号标志.
* 如一个 byte, `1000 0000` 最高位为 1, 则为负数.
*/
fun Long.groupId(): GroupId = GroupId(this.coerceAtLeastOrFail(0))
/**
* 一些群 API 使用的 ID. 在使用时会特别注明
*
* 注: 在引用群 ID 时, 应使用 [GroupId] 或 [GroupInternalId] 类型, 而不是 [UInt]
*
* @see GroupInternalId.toId 由 [GroupInternalId] 转换为 [GroupId]
* @see GroupId.toInternalId 由 [GroupId] 转换为 [GroupInternalId]
*/
inline class GroupInternalId(inline val value: Long)
\ 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