diff --git a/mirai-core-qqandroid/src/commonMain/kotlin/net/mamoe/mirai/qqandroid/ContactImpl.kt b/mirai-core-qqandroid/src/commonMain/kotlin/net/mamoe/mirai/qqandroid/ContactImpl.kt index 39433be13..d747fd454 100644 --- a/mirai-core-qqandroid/src/commonMain/kotlin/net/mamoe/mirai/qqandroid/ContactImpl.kt +++ b/mirai-core-qqandroid/src/commonMain/kotlin/net/mamoe/mirai/qqandroid/ContactImpl.kt @@ -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.utils.* import kotlin.coroutines.CoroutineContext +import kotlin.coroutines.EmptyCoroutineContext internal abstract class ContactImpl : Contact @@ -74,13 +75,12 @@ internal class MemberImpl( @UseExperimental(MiraiInternalAPI::class) 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 ) : ContactImpl(), Group { override lateinit var owner: Member - override lateinit var name: String - override lateinit var announcement: String - override lateinit var members: ContactList - override val internalId: GroupInternalId = GroupId(id).toInternalId() override fun getMember(id: Long): Member = diff --git a/mirai-core-qqandroid/src/commonMain/kotlin/net/mamoe/mirai/qqandroid/QQAndroidBot.kt b/mirai-core-qqandroid/src/commonMain/kotlin/net/mamoe/mirai/qqandroid/QQAndroidBot.kt index 861b8d66d..f51768af4 100644 --- a/mirai-core-qqandroid/src/commonMain/kotlin/net/mamoe/mirai/qqandroid/QQAndroidBot.kt +++ b/mirai-core-qqandroid/src/commonMain/kotlin/net/mamoe/mirai/qqandroid/QQAndroidBot.kt @@ -43,17 +43,17 @@ internal abstract class QQAndroidBotBase constructor( override val groups: ContactList = ContactList(LockFreeLinkedList()) 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 { - internalId.toId().value.let { id -> - return groups.delegate.filteringGetOrAdd({ it.id == id }, { GroupImpl(this as QQAndroidBot, coroutineContext, id) }) + with(internalId.toId().value) { + return groups.delegate.getOrNull(this) ?: throw NoSuchElementException("Can not found group $this") } } 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 { diff --git a/mirai-core-qqandroid/src/commonMain/kotlin/net/mamoe/mirai/qqandroid/network/QQAndroidBotNetworkHandler.kt b/mirai-core-qqandroid/src/commonMain/kotlin/net/mamoe/mirai/qqandroid/network/QQAndroidBotNetworkHandler.kt index f50153147..c09abb61f 100644 --- a/mirai-core-qqandroid/src/commonMain/kotlin/net/mamoe/mirai/qqandroid/network/QQAndroidBotNetworkHandler.kt +++ b/mirai-core-qqandroid/src/commonMain/kotlin/net/mamoe/mirai/qqandroid/network/QQAndroidBotNetworkHandler.kt @@ -9,11 +9,15 @@ import kotlinx.io.core.ByteReadPacket import kotlinx.io.core.Input import kotlinx.io.core.buildPacket 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.Packet import net.mamoe.mirai.event.* import net.mamoe.mirai.network.BotNetworkHandler import net.mamoe.mirai.qqandroid.GroupImpl +import net.mamoe.mirai.qqandroid.MemberImpl import net.mamoe.mirai.qqandroid.QQAndroidBot import net.mamoe.mirai.qqandroid.QQImpl import net.mamoe.mirai.qqandroid.event.ForceOfflineEvent @@ -106,8 +110,6 @@ internal class QQAndroidBotNetworkHandler(bot: QQAndroidBot) : BotNetworkHandler } override suspend fun init() { - // delay(5000) - this@QQAndroidBotNetworkHandler.subscribeAlways { if (this@QQAndroidBotNetworkHandler.bot == this.bot) { close() @@ -149,28 +151,65 @@ internal class QQAndroidBotNetworkHandler(bot: QQAndroidBot) : BotNetworkHandler } try { - bot.logger.info("开始加载群组列表") + bot.logger.info("开始加载群组列表与群成员列表") val troopData = FriendList.GetTroopListSimplify( bot.client ).sendAndExpect(timeoutMillis = 1000) + println("获取到群数量" + troopData.groups.size) + val toGet: MutableMap> = mutableMapOf() troopData.groups.forEach { - bot.groups.delegate.addLast(GroupImpl(bot, EmptyCoroutineContext, it.groupUin)) + val contactList = ContactList(LockFreeLinkedList()) + 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) } - bot.logger.info("群组列表加载完成, 共 ${troopData.groups.size}个") + 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}个") } catch (e: Exception) { bot.logger.info("加载组信息失败|一般这是由于加载过于频繁导致/将以热加载方式加载群列表") } } - suspend fun getTroopMemberList(groupUni: Long) { - bot.logger.info("开始群[$groupUni]成员") - val data = FriendList.GetTroopMemberList( - bot.client, - groupUni, - 0 - ).sendAndExpect(timeoutMillis = 1000) - println(data.contentToString()) + suspend fun getTroopMemberList(group: GroupImpl, list: ContactList): ContactList { + bot.logger.info("开始获取群[${group.id}]成员列表") + var size = 0 + var nextUin = 0L + while (true) { + val data = FriendList.GetTroopMemberList( + bot.client, + group.id, + nextUin + ).sendAndExpect(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 } /** diff --git a/mirai-core-qqandroid/src/commonMain/kotlin/net/mamoe/mirai/qqandroid/network/protocol/packet/PacketFactory.kt b/mirai-core-qqandroid/src/commonMain/kotlin/net/mamoe/mirai/qqandroid/network/protocol/packet/PacketFactory.kt index 22fa8b51a..4f80d1114 100644 --- a/mirai-core-qqandroid/src/commonMain/kotlin/net/mamoe/mirai/qqandroid/network/protocol/packet/PacketFactory.kt +++ b/mirai-core-qqandroid/src/commonMain/kotlin/net/mamoe/mirai/qqandroid/network/protocol/packet/PacketFactory.kt @@ -116,7 +116,8 @@ internal object KnownPacketFactories { MessageSvc.PushForceOffline, MessageSvc.PbSendMsg, FriendList.GetFriendGroupList, - FriendList.GetTroopListSimplify + FriendList.GetTroopListSimplify, + FriendList.GetTroopMemberList ) object IncomingFactories : List> by mutableListOf( diff --git a/mirai-core-qqandroid/src/commonMain/kotlin/net/mamoe/mirai/qqandroid/network/protocol/packet/list/FriendListPacket.kt b/mirai-core-qqandroid/src/commonMain/kotlin/net/mamoe/mirai/qqandroid/network/protocol/packet/list/FriendListPacket.kt index 5b6893725..019f6374e 100644 --- a/mirai-core-qqandroid/src/commonMain/kotlin/net/mamoe/mirai/qqandroid/network/protocol/packet/list/FriendListPacket.kt +++ b/mirai-core-qqandroid/src/commonMain/kotlin/net/mamoe/mirai/qqandroid/network/protocol/packet/list/FriendListPacket.kt @@ -27,10 +27,11 @@ internal class FriendList { internal object GetTroopMemberList : OutgoingPacketFactory("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()) } return Response( - res.vecTroopMember + res.vecTroopMember, + res.nextUin ) } @@ -52,10 +53,11 @@ internal class FriendList { GetTroopMemberListReq.serializer(), GetTroopMemberListReq( uin = client.uin, - groupCode = GroupId(targetGroupId).toInternalId().value, + groupCode = targetGroupId, groupUin = targetGroupId, nextUin = nextUin, - reqType = 0 + reqType = 0, + version = 2 ) ) ) @@ -64,7 +66,8 @@ internal class FriendList { } class Response( - val members: List + val members: List, + val nextUin: Long ) : Packet { override fun toString(): String = "Friendlist.GetTroopMemberList.Response" } @@ -73,7 +76,6 @@ internal class FriendList { internal object GetTroopListSimplify : OutgoingPacketFactory("friendlist.GetTroopListReqV2") { - override suspend fun ByteReadPacket.decode(bot: QQAndroidBot): Response { val res = this.decodeUniPacket(GetTroopListRespV2.serializer()) return Response(res.vecTroopList.orEmpty()) diff --git a/mirai-core/src/commonMain/kotlin/net.mamoe.mirai/contact/Group.kt b/mirai-core/src/commonMain/kotlin/net.mamoe.mirai/contact/Group.kt index 55775a094..693466638 100644 --- a/mirai-core/src/commonMain/kotlin/net.mamoe.mirai/contact/Group.kt +++ b/mirai-core/src/commonMain/kotlin/net.mamoe.mirai/contact/Group.kt @@ -10,10 +10,11 @@ import net.mamoe.mirai.utils.coerceAtLeastOrFail /** * 群. 在 QQ Android 中叫做 "Troop" * - * Group ID 与 Group Number 并不是同一个值. - * - Group Number([Group.id]) 是通常使用的群号码.(在 QQ 客户端中可见) - * - Group ID([Group.internalId]) 是与调用 API 时使用的 id.(在 QQ 客户端中不可见) - * @author Him188moe + * Group UIN 与 Group Code 并不是同一个值. + * Group Code是在客户端显示的code + * Group Uin是QQ内部的群ID + * 在网络调用层 Code与Uin会被混用 + * 但在开发层 你应该只关注Group Code */ interface Group : Contact, CoroutineScope { /** @@ -75,30 +76,4 @@ interface Group : Contact, CoroutineScope { * * @see GroupInternalId.toId 由 [GroupInternalId] 转换为 [GroupId] * @see GroupId.toInternalId 由 [GroupId] 转换为 [GroupInternalId] - */ -inline class GroupId(inline val value: Long) - -/** - * 将 [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 + */ \ No newline at end of file