Use groupCode as primary key

This commit is contained in:
Him188 2020-02-03 02:45:40 +08:00
parent d9e5805255
commit 8c69eefcde
16 changed files with 70 additions and 111 deletions

View File

@ -77,7 +77,7 @@ internal class MemberImpl(
internal class GroupImpl( internal class GroupImpl(
bot: QQAndroidBot, override val coroutineContext: CoroutineContext, bot: QQAndroidBot, override val coroutineContext: CoroutineContext,
override val id: Long, override val id: Long,
override val groupCode: Long, val uin: Long,
override var name: String, override var name: String,
override var announcement: String, override var announcement: String,
override var members: ContactList<Member> override var members: ContactList<Member>

View File

@ -51,13 +51,13 @@ internal abstract class QQAndroidBotBase constructor(
override val groups: ContactList<Group> = ContactList(LockFreeLinkedList()) override val groups: ContactList<Group> = ContactList(LockFreeLinkedList())
override fun getGroupByID(id: Long): Group { fun getGroupByUin(uin: Long): Group {
return groups.delegate.getOrNull(id) ?: throw NoSuchElementException("Can not found group with ID=${id}") return groups.delegate.filteringGetOrNull { (it as GroupImpl).uin == uin } ?: throw NoSuchElementException("Can not found group with ID=${uin}")
} }
override fun getGroupByGroupCode(groupCode: Long): Group { override fun getGroup(id: Long): Group {
return groups.delegate.filteringGetOrNull { it.groupCode == groupCode } return groups.delegate.getOrNull(id)
?: throw NoSuchElementException("Can not found group with GroupCode=${groupCode}") ?: throw NoSuchElementException("Can not found group with GroupCode=${id}")
} }
override suspend fun addFriend(id: Long, message: String?, remark: String?): AddFriendResult { override suspend fun addFriend(id: Long, message: String?, remark: String?): AddFriendResult {

View File

@ -31,7 +31,6 @@ import net.mamoe.mirai.qqandroid.network.protocol.packet.login.StatSvc
import net.mamoe.mirai.utils.* import net.mamoe.mirai.utils.*
import net.mamoe.mirai.utils.io.* import net.mamoe.mirai.utils.io.*
import kotlin.coroutines.CoroutineContext import kotlin.coroutines.CoroutineContext
import kotlin.coroutines.EmptyCoroutineContext
import kotlin.jvm.Volatile import kotlin.jvm.Volatile
@Suppress("MemberVisibilityCanBePrivate") @Suppress("MemberVisibilityCanBePrivate")
@ -104,12 +103,10 @@ internal class QQAndroidBotNetworkHandler(bot: QQAndroidBot) : BotNetworkHandler
} }
// println("d2key=${bot.client.wLoginSigInfo.d2Key.toUHexString()}") // println("d2key=${bot.client.wLoginSigInfo.d2Key.toUHexString()}")
StatSvc.Register(bot.client).sendAndExpect<StatSvc.Register.Response>(6000) StatSvc.Register(bot.client).sendAndExpect<StatSvc.Register.Response>(6000) // it's slow
} }
override suspend fun init() { override suspend fun init() {
MessageSvc.PbGetMsg(bot.client, MsgSvc.SyncFlag.START, currentTimeSeconds).sendWithoutExpect()
this@QQAndroidBotNetworkHandler.subscribeAlways<ForceOfflineEvent> { this@QQAndroidBotNetworkHandler.subscribeAlways<ForceOfflineEvent> {
if (this@QQAndroidBotNetworkHandler.bot == this.bot) { if (this@QQAndroidBotNetworkHandler.bot == this.bot) {
close() close()
@ -159,26 +156,26 @@ internal class QQAndroidBotNetworkHandler(bot: QQAndroidBot) : BotNetworkHandler
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) // println("获取到群数量" + troopData.groups.size)
val toGet: MutableMap<GroupImpl, ContactList<Member>> = mutableMapOf() val toGet: MutableMap<GroupImpl, ContactList<Member>> = mutableMapOf()
troopData.groups.forEach { troopData.groups.forEach {
val contactList = ContactList(LockFreeLinkedList<Member>()) val contactList = ContactList(LockFreeLinkedList<Member>())
val group = val group =
GroupImpl( GroupImpl(
bot, bot = bot,
this.coroutineContext, coroutineContext = this.coroutineContext,
it.groupUin, id = it.groupCode,
it.groupCode, uin = it.groupUin,
it.groupName, name = it.groupName,
it.groupMemo, announcement = it.groupMemo,
contactList members = contactList
) )
group.owner = group.owner =
MemberImpl( MemberImpl(
bot.QQ(it.dwGroupOwnerUin) as QQImpl, qq = bot.QQ(it.dwGroupOwnerUin) as QQImpl,
group, group = group,
group.coroutineContext, coroutineContext = group.coroutineContext,
MemberPermission.OWNER permission = MemberPermission.OWNER
) )
toGet[group] = contactList toGet[group] = contactList
bot.groups.delegate.addLast(group) bot.groups.delegate.addLast(group)
@ -186,10 +183,10 @@ internal class QQAndroidBotNetworkHandler(bot: QQAndroidBot) : BotNetworkHandler
toGet.forEach { toGet.forEach {
try { try {
getTroopMemberList(it.key, it.value, it.key.owner.id) getTroopMemberList(it.key, it.value, it.key.owner.id)
groupInfo[it.key.groupCode] = it.value.size groupInfo[it.key.uin] = it.value.size
} catch (e: Exception) { } catch (e: Exception) {
groupInfo[it.key.groupCode] = -1 groupInfo[it.key.uin] = -1
bot.logger.info("${it.key.groupCode}的列表拉取失败, 将采用动态加入") bot.logger.info("${it.key.uin}的列表拉取失败, 将采用动态加入")
} }
//delay(200) //delay(200)
} }
@ -221,36 +218,32 @@ internal class QQAndroidBotNetworkHandler(bot: QQAndroidBot) : BotNetworkHandler
} }
} }
bot.logger.info("====================Mirai Bot List初始化完毕====================") bot.logger.info("====================Mirai Bot List初始化完毕====================")
MessageSvc.PbGetMsg(bot.client, MsgSvc.SyncFlag.START, currentTimeSeconds).sendWithoutExpect()
} }
suspend fun getTroopMemberList(group: GroupImpl, list: ContactList<Member>, owner: Long): ContactList<Member> { suspend fun getTroopMemberList(group: GroupImpl, list: ContactList<Member>, owner: Long): ContactList<Member> {
bot.logger.info("开始获取群[${group.groupCode}]成员列表") bot.logger.info("开始获取群[${group.uin}]成员列表")
var size = 0 var size = 0
var nextUin = 0L var nextUin = 0L
while (true) { while (true) {
val data = FriendList.GetTroopMemberList( val data = FriendList.GetTroopMemberList(
bot.client, client = bot.client,
group.id, targetGroupUin = group.uin,
group.groupCode, targetGroupCode = group.id,
nextUin nextUin = nextUin
).sendAndExpect<FriendList.GetTroopMemberList.Response>(timeoutMillis = 3000) ).sendAndExpect<FriendList.GetTroopMemberList.Response>(timeoutMillis = 3000)
data.members.forEach { data.members.forEach {
if (it.memberUin != bot.uin) { if (it.memberUin != bot.uin) {
list.delegate.addLast( list.delegate.addLast(
MemberImpl( MemberImpl(
bot.QQ(it.memberUin) as QQImpl, qq = bot.QQ(it.memberUin) as QQImpl,
group, group = group,
EmptyCoroutineContext, coroutineContext = group.coroutineContext,
when { permission = when {
it.memberUin == owner -> { it.memberUin == owner -> MemberPermission.OWNER
MemberPermission.OWNER it.dwFlag == 1L -> MemberPermission.ADMINISTRATOR
} else -> MemberPermission.MEMBER
it.dwFlag == 1L -> {
MemberPermission.ADMINISTRATOR
}
else -> {
MemberPermission.MEMBER
}
} }
) )
) )
@ -261,9 +254,9 @@ internal class QQAndroidBotNetworkHandler(bot: QQAndroidBot) : BotNetworkHandler
if (nextUin == 0L) { if (nextUin == 0L) {
break break
} }
println("已获取群[${group.groupCode}]成员列表前" + size + "个成员") //println("已获取群[${group.uin}]成员列表前" + size + "个成员")
} }
println("群[${group.groupCode}]成员全部获取完成, 共${list.size}个成员") //println("群[${group.uin}]成员全部获取完成, 共${list.size}个成员")
return list return list
} }

View File

@ -214,7 +214,7 @@ internal class MessageSvc {
*/ */
fun ToGroup( fun ToGroup(
client: QQAndroidClient, client: QQAndroidClient,
groupId: Long, groupCode: Long,
message: MessageChain message: MessageChain
): OutgoingPacket = buildOutgoingUniPacket(client) { ): OutgoingPacket = buildOutgoingUniPacket(client) {
@ -224,7 +224,7 @@ internal class MessageSvc {
///return@buildOutgoingUniPacket ///return@buildOutgoingUniPacket
writeProtoBuf( writeProtoBuf(
MsgSvc.PbSendMsgReq.serializer(), MsgSvc.PbSendMsgReq( MsgSvc.PbSendMsgReq.serializer(), MsgSvc.PbSendMsgReq(
routingHead = MsgSvc.RoutingHead(grp = MsgSvc.Grp(groupCode = groupId)), // TODO: 2020/1/30 确认这里是 id 还是 internalId routingHead = MsgSvc.RoutingHead(grp = MsgSvc.Grp(groupCode = groupCode)), // TODO: 2020/1/30 确认这里是 id 还是 internalId
contentHead = MsgComm.ContentHead(pkgNum = 1, divSeq = seq), contentHead = MsgComm.ContentHead(pkgNum = 1, divSeq = seq),
msgBody = ImMsgBody.MsgBody( msgBody = ImMsgBody.MsgBody(
richText = ImMsgBody.RichText( richText = ImMsgBody.RichText(

View File

@ -38,7 +38,7 @@ internal class OnlinePush {
return GroupMessageOrNull(null) return GroupMessageOrNull(null)
} }
val group = bot.getGroupByGroupCode(pbPushMsg.msg.msgHead.groupInfo!!.groupCode) val group = bot.getGroup(pbPushMsg.msg.msgHead.groupInfo!!.groupCode)
val flags = extraInfo?.flags ?: 0 val flags = extraInfo?.flags ?: 0
return GroupMessageOrNull( return GroupMessageOrNull(

View File

@ -14,11 +14,7 @@ import net.mamoe.mirai.qqandroid.network.protocol.packet.EMPTY_BYTE_ARRAY
import net.mamoe.mirai.qqandroid.network.protocol.packet.OutgoingPacket import net.mamoe.mirai.qqandroid.network.protocol.packet.OutgoingPacket
import net.mamoe.mirai.qqandroid.network.protocol.packet.OutgoingPacketFactory import net.mamoe.mirai.qqandroid.network.protocol.packet.OutgoingPacketFactory
import net.mamoe.mirai.qqandroid.network.protocol.packet.buildOutgoingUniPacket import net.mamoe.mirai.qqandroid.network.protocol.packet.buildOutgoingUniPacket
import net.mamoe.mirai.qqandroid.network.protocol.packet.list.FriendList.GetFriendGroupList.decode
import net.mamoe.mirai.utils.io.debugIfFail import net.mamoe.mirai.utils.io.debugIfFail
import net.mamoe.mirai.utils.io.debugPrintThis
import net.mamoe.mirai.utils.io.debugPrintln
import net.mamoe.mirai.utils.io.discardExact
internal class FriendList { internal class FriendList {
@ -35,7 +31,7 @@ internal class FriendList {
operator fun invoke( operator fun invoke(
client: QQAndroidClient, client: QQAndroidClient,
targetGroupId: Long, targetGroupUin: Long,
targetGroupCode: Long, targetGroupCode: Long,
nextUin: Long = 0 nextUin: Long = 0
): OutgoingPacket { ): OutgoingPacket {
@ -53,7 +49,7 @@ internal class FriendList {
GetTroopMemberListReq( GetTroopMemberListReq(
uin = client.uin, uin = client.uin,
groupCode = targetGroupCode, groupCode = targetGroupCode,
groupUin = targetGroupId, groupUin = targetGroupUin,
nextUin = nextUin, nextUin = nextUin,
reqType = 0, reqType = 0,
version = 2 version = 2

View File

@ -21,7 +21,7 @@ import net.mamoe.mirai.utils.WeakRef
import net.mamoe.mirai.utils.io.transferTo import net.mamoe.mirai.utils.io.transferTo
/** /**
* Mirai 机器人. 一个机器人实例登录一个 QQ 账号. * 机器人对象. 一个机器人实例登录一个 QQ 账号.
* Mirai 为多账号设计, 可同时维护多个机器人. * Mirai 为多账号设计, 可同时维护多个机器人.
* *
* @see Contact * @see Contact
@ -78,12 +78,7 @@ abstract class Bot : CoroutineScope {
/** /**
* 获取一个机器人加入的群. 若没有这个群, 则会抛出异常 [NoSuchElementException] * 获取一个机器人加入的群. 若没有这个群, 则会抛出异常 [NoSuchElementException]
*/ */
abstract fun getGroupByID(id: Long): Group abstract fun getGroup(id: Long): Group
/**
* 获取一个机器人加入的群. 若没有这个群, 则会抛出异常 [NoSuchElementException]
*/
abstract fun getGroupByGroupCode(groupCode: Long): Group
// 目前还不能构造群对象. 这将在以后支持 // 目前还不能构造群对象. 这将在以后支持
@ -131,8 +126,10 @@ abstract class Bot : CoroutineScope {
/** /**
* 关闭这个 [Bot], 停止一切相关活动. 不可重新登录. * 关闭这个 [Bot], 停止一切相关活动. 不可重新登录.
*
* @param cause 原因. null 时视为正常关闭, null 时视为异常关闭
*/ */
abstract fun dispose(throwable: Throwable?) abstract fun close(cause: Throwable? = null)
// region extensions // region extensions

View File

@ -133,15 +133,15 @@ abstract class BotImpl<N : BotNetworkHandler> constructor(
// endregion // endregion
@UseExperimental(MiraiInternalAPI::class) @UseExperimental(MiraiInternalAPI::class)
override fun dispose(throwable: Throwable?) { override fun close(cause: Throwable?) {
if (throwable == null) { if (cause == null) {
network.close() network.close()
this.botJob.complete() this.botJob.complete()
groups.delegate.clear() groups.delegate.clear()
qqs.delegate.clear() qqs.delegate.clear()
} else { } else {
network.close(throwable) network.close(cause)
this.botJob.completeExceptionally(throwable) this.botJob.completeExceptionally(cause)
groups.delegate.clear() groups.delegate.clear()
qqs.delegate.clear() qqs.delegate.clear()
} }

View File

@ -25,7 +25,7 @@ interface Contact : CoroutineScope {
val bot: Bot // weak ref val bot: Bot // weak ref
/** /**
* 可以是 QQ 号码或者群号码 [GroupId]. * 可以是 QQ 号码或者群号码.
*/ */
val id: Long val id: Long

View File

@ -7,32 +7,25 @@ import kotlinx.coroutines.CoroutineScope
/** /**
* . QQ Android 中叫做 "Troop" * . QQ Android 中叫做 "Troop"
*
* Group UIN Group Code 并不是同一个值.
* Group Code是在客户端显示的code
* Group Uin是QQ内部的群ID[在Mirai中则为 id]
* 但是有的时候 两个是相同的value
* 在网络调用层 Code与Uin会被混用
* 但在开发层 你应该只关注Group Code
*/ */
interface Group : Contact, CoroutineScope { interface Group : Contact, CoroutineScope {
/**
* 同为 groupCode, 用户看到的群号码.
*/
override val id: Long
val groupCode: Long
/** /**
* 群主 (同步事件更新) * 群主 (同步事件更新)
* 进行 [updateGroupInfo] 时将会更新这个值.
*/ */
val owner: Member val owner: Member
/** /**
* 群名称 (同步事件更新) * 群名称 (同步事件更新)
* 进行 [updateGroupInfo] 时将会更新这个值.
*/ */
val name: String val name: String
/** /**
* 入群公告, 没有时为空字符串. (同步事件更新) * 入群公告, 没有时为空字符串. (同步事件更新)
* 进行 [updateGroupInfo] 时将会更新这个值.
*/ */
val announcement: String val announcement: String
@ -45,7 +38,7 @@ interface Group : Contact, CoroutineScope {
/** /**
* 获取群成员实例. 若此 ID 的成员不存在, 则会抛出 [kotlin.NoSuchElementException] * 获取群成员实例. 若此 id 的成员不存在, 则会抛出 [kotlin.NoSuchElementException]
*/ */
operator fun get(id: Long): Member operator fun get(id: Long): Member

View File

@ -63,6 +63,8 @@ abstract class BotNetworkHandler : CoroutineScope {
/** /**
* 关闭网络接口, 停止所有有关协程和任务 * 关闭网络接口, 停止所有有关协程和任务
*
* @param cause 关闭的原因. null 时视为正常关闭, null 时视为异常关闭.
*/ */
open fun close(cause: Throwable? = null) { open fun close(cause: Throwable? = null) {
if (supervisor.isActive) { if (supervisor.isActive) {

View File

@ -1,14 +0,0 @@
package net.mamoe.mirai.contact
import net.mamoe.mirai.test.shouldBeEqualTo
import org.junit.Test
@UseExperimental(ExperimentalUnsignedTypes::class)
internal class GroupIdConversionsKtTest {
@Test
fun checkToInternalId() {
GroupId(221056495).toInternalId().value shouldBeEqualTo 4111056495
// 61 056495
//4111 056495
}
}

View File

@ -217,7 +217,9 @@ suspend fun directlySubscribe(bot: Bot) {
"复读" in message -> sender.sendMessage(message) "复读" in message -> sender.sendMessage(message)
"发群消息" in message -> 580266363.group().sendMessage(message.toString().substringAfter("发群消息")) "发群消息" in message -> {
bot.getGroup(580266363).sendMessage(message.toString().substringAfter("发群消息"))
}
} }
} }
} }

View File

@ -50,10 +50,6 @@ suspend fun main() {
// 订阅来自这个 bot 的群消息事件 // 订阅来自这个 bot 的群消息事件
bot.subscribeGroupMessages { bot.subscribeGroupMessages {
"群资料" reply {
group.updateGroupInfo().toString().reply()
}
startsWith("mute") { startsWith("mute") {
val at: At by message val at: At by message
at.member().mute(30) at.member().mute(30)

View File

@ -5,7 +5,6 @@ import net.mamoe.mirai.BotAccount;
import net.mamoe.mirai.data.AddFriendResult; import net.mamoe.mirai.data.AddFriendResult;
import net.mamoe.mirai.message.data.Image; import net.mamoe.mirai.message.data.Image;
import net.mamoe.mirai.network.BotNetworkHandler; import net.mamoe.mirai.network.BotNetworkHandler;
import net.mamoe.mirai.utils.GroupNotFoundException;
import net.mamoe.mirai.utils.MiraiInternalAPI; import net.mamoe.mirai.utils.MiraiInternalAPI;
import net.mamoe.mirai.utils.MiraiLogger; import net.mamoe.mirai.utils.MiraiLogger;
import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.NotNull;
@ -15,6 +14,9 @@ import java.util.List;
@SuppressWarnings("unused") @SuppressWarnings("unused")
public interface BlockingBot { public interface BlockingBot {
// TODO: 2020/2/3 需要更新
/** /**
* 账号信息 * 账号信息
*/ */
@ -55,17 +57,11 @@ public interface BlockingBot {
/** /**
* 获取缓存的群对象. 若没有对应的缓存, 则会线程安全地创建一个. * 获取缓存的群对象. 若没有对应的缓存, 则会线程安全地创建一个.
* {@code id} 无效, 将会抛出 {@link GroupNotFoundException} * {@code id} 无效, 将会抛出 {@link java.util.NoSuchElementException}
*/ */
@NotNull @NotNull
BlockingGroup getGroup(long id); BlockingGroup getGroup(long id);
/**
* 获取缓存的群对象. 若没有对应的缓存, 则会线程安全地创建一个.
* {@code internalId} 无效, 将会抛出 {@link GroupNotFoundException}
*/
@NotNull
BlockingGroup getGroupByInternalId(long internalId);
// endregion // endregion

View File

@ -4,7 +4,6 @@ import kotlinx.coroutines.runBlocking
import kotlinx.io.core.ByteReadPacket import kotlinx.io.core.ByteReadPacket
import net.mamoe.mirai.Bot import net.mamoe.mirai.Bot
import net.mamoe.mirai.BotAccount import net.mamoe.mirai.BotAccount
import net.mamoe.mirai.contact.GroupInternalId
import net.mamoe.mirai.data.AddFriendResult import net.mamoe.mirai.data.AddFriendResult
import net.mamoe.mirai.japt.BlockingBot import net.mamoe.mirai.japt.BlockingBot
import net.mamoe.mirai.japt.BlockingGroup import net.mamoe.mirai.japt.BlockingGroup
@ -29,12 +28,11 @@ internal class BlockingBotImpl(private val bot: Bot) : BlockingBot {
override fun getGroups(): List<BlockingGroup> = bot.groups.delegate.toList().map { it.blocking() } override fun getGroups(): List<BlockingGroup> = bot.groups.delegate.toList().map { it.blocking() }
override fun getGroup(id: Long): BlockingGroup = runBlocking { bot.getGroup(id).blocking() } override fun getGroup(id: Long): BlockingGroup = runBlocking { bot.getGroup(id).blocking() }
override fun getGroupByInternalId(internalId: Long): BlockingGroup = runBlocking { bot.getGroup(GroupInternalId(internalId)).blocking() }
override fun getNetwork(): BotNetworkHandler = bot.network override fun getNetwork(): BotNetworkHandler = bot.network
override fun login() = runBlocking { bot.login() } override fun login() = runBlocking { bot.login() }
override fun downloadAsByteArray(image: Image): ByteArray = bot.run { runBlocking { image.downloadAsByteArray() } } override fun downloadAsByteArray(image: Image): ByteArray = bot.run { runBlocking { image.downloadAsByteArray() } }
override fun download(image: Image): ByteReadPacket = bot.run { runBlocking { image.download() } } override fun download(image: Image): ByteReadPacket = bot.run { runBlocking { image.download() } }
override fun addFriend(id: Long, message: String?, remark: String?): AddFriendResult = runBlocking { bot.addFriend(id, message, remark) } override fun addFriend(id: Long, message: String?, remark: String?): AddFriendResult = runBlocking { bot.addFriend(id, message, remark) }
override fun approveFriendAddRequest(id: Long, remark: String?) = runBlocking { bot.approveFriendAddRequest(id, remark) } override fun approveFriendAddRequest(id: Long, remark: String?) = runBlocking { bot.approveFriendAddRequest(id, remark) }
override fun dispose(throwable: Throwable?) = bot.dispose(throwable) override fun dispose(throwable: Throwable?) = bot.close(throwable)
} }