Support friend group
close #1243 todo:
- [x] friendgroup设计
- for review 确定一些细节设计
protocol:
- [x] 重命名分组
- [x] 获取全部分组数据
- [x] 新建/删除分组
- [x] 移动friend到指定分组
mirai 已经有群叫 Group 了,应该会考虑换一个叫法
mirai 已经有群叫 Group 了,应该会考虑换一个叫法
可以叫 category ?
我目前先暂时用FriendGroup这个名字写下去,后面定了我再改
GroupGroup(
对于“组”这个概念感觉只有叫 collection 或者 set 比较合适了,但是这两个又同时是比较通用的集合概念
对于“组”这个概念感觉只有叫 collection 或者 set 比较合适了,但是这两个又同时是比较通用的集合概念
classification
对于“组”这个概念感觉只有叫 collection 或者 set 比较合适了,但是这两个又同时是比较通用的集合概念
用cluster吧
domain?
感觉...要不还是就叫 FriendGroup 吧
已过测试
private suspend fun refreshFriendGroupList(bot: QQAndroidBot): MutableList<FriendGroup> {
val friendGroupInfos = mutableListOf<FriendGroup>()
var count = 0
var total: Short
while (true) {
val data = bot.network.sendAndExpect(
FriendList.GetFriendGroupList(bot.client, 0, 0, count, 150)
)
total = data.totoalGroupCount
for (jceInfo in data.groupList) {
friendGroupInfos.add(
FriendGroupImpl(
bot, FriendGroupInfo(
jceInfo.groupId.toInt(), jceInfo.groupname, jceInfo.friendCount, jceInfo.onlineFriendCount
)
)
)
}
count += data.groupList.size
println("Loading friendGroup list: ${count}/${total}")
if (count >= total) break
}
println("Successfully loaded friendGroup list: $count in total")
return friendGroupInfos
}
suspend fun main() {
val bot = BotFactory.newBot(0L, "") .alsoLogin()
assertEquals("我的好友", bot.friendGroups.first().name)
// create
val newGroup = bot.friendGroups.create("111")
// refresh from server
refreshFriendGroupList(bot as QQAndroidBot).forEach {
println(it.name + " for " + it.id)
}
val newGroupInList = refreshFriendGroupList(bot).firstOrNull { it.name == "111" }
assertTrue { newGroupInList != null && newGroupInList.id == newGroup.id }
// moveIn
val friend = bot.getFriend(1)!!
assertTrue {
bot.friendGroups[friend.friendGroup!!.id]!!.friends.contains(friend)
}
val old = bot.friendGroups.friendGroups[0]
old.moveIn(friend)
val target = bot.friendGroups.friendGroups[2]
assertFalse {
target.friends.contains(friend)
}
target.moveIn(friend)
assertTrue {
target.friends.contains(friend) && bot.friendGroups[friend.friendGroup!!.id]!!.friends.contains(friend.id) && !old.friends.contains(
friend
)
}
assertEquals(friend.queryProfile().friendGroupId, friend.friendGroup!!.id)
newGroup.moveIn(friend)
// rename
newGroup.renameTo("222")
assertTrue { refreshFriendGroupList(bot).first { it.name == "222" }.id == newGroup.id }
assertTrue { bot.friendGroups.first { it.name == "222" } == newGroup }
// del
newGroup.delete()
assertTrue {
(newGroup.friends.first() as FriendImpl).info.friendGroupId == newGroup.friends.first()
.queryProfile().friendGroupId
}
// refresh from server
bot.friendGroups.friendGroups = refreshFriendGroupList(bot)
assertTrue { bot.friendGroups.firstOrNull { it == newGroup } == null }
println("-- test end --")
bot.join()
}
已经 rebase 到 dev 测试代码:
/*
* 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.local
//import net.mamoe.mirai.internal.network.protocol.data.jce.MovGroupMemReq
import kotlinx.serialization.DeserializationStrategy
import net.mamoe.mirai.BotFactory
import net.mamoe.mirai.Mirai
import net.mamoe.mirai.alsoLogin
import net.mamoe.mirai.data.FriendGroup
import net.mamoe.mirai.internal.QQAndroidBot
import net.mamoe.mirai.internal.contact.FriendGroupImpl
import net.mamoe.mirai.internal.contact.FriendGroupsImpl
import net.mamoe.mirai.internal.contact.FriendImpl
import net.mamoe.mirai.internal.contact.info
import net.mamoe.mirai.internal.contact.info.FriendGroupInfo
import net.mamoe.mirai.internal.network.protocol.data.jce.GetFriendListReq
import net.mamoe.mirai.internal.network.protocol.data.jce.GetFriendListResp
import net.mamoe.mirai.internal.network.protocol.data.jce.MsgType0x210
import net.mamoe.mirai.internal.network.protocol.data.jce.OnlinePushPack
import net.mamoe.mirai.internal.network.protocol.data.proto.Submsgtype0x27
import net.mamoe.mirai.internal.network.protocol.packet.list.FriendList
import net.mamoe.mirai.internal.utils.io.serialization.loadAs
import net.mamoe.mirai.internal.utils.io.serialization.tars.Tars
import net.mamoe.mirai.internal.utils.io.serialization.toByteArray
import net.mamoe.mirai.internal.utils.structureToString
import net.mamoe.mirai.utils.BotConfiguration
import net.mamoe.mirai.utils.info
import net.mamoe.mirai.utils.toByteArray
import net.mamoe.mirai.utils.verbose
import kotlin.test.assertEquals
import kotlin.test.assertFalse
import kotlin.test.assertNotEquals
import kotlin.test.assertTrue
fun String.decodeHex(): ByteArray {
check(length % 2 == 0) { "Must have an even length" }
return chunked(2).map { it.toInt(16).toByte() }.toByteArray()
}
private fun Long.toByteArray2(): ByteArray {
val arr = this.toByteArray()
val index = arr.indexOfFirst { it.toInt() != 0 }
return arr.sliceArray(index until arr.size)
}
fun ByteArray.toHex(): String = joinToString(separator = " ") { eachByte -> "%02x".format(eachByte) }
fun <T> ByteArray.decode(deserializer: DeserializationStrategy<T>) = Tars.UTF_8.decodeFromByteArray(deserializer, this)
private suspend fun refreshFriendGroupList(bot: QQAndroidBot): List<FriendGroup> {
val friendGroupInfos = mutableListOf<FriendGroup>()
var count = 0
var total: Short
while (true) {
val data = bot.network.sendAndExpect(
FriendList.GetFriendGroupList(bot.client, 0, 0, count, 150)
)
total = data.totoalGroupCount
for (jceInfo in data.groupList) {
friendGroupInfos.add(
FriendGroupImpl(
bot, FriendGroupInfo(
jceInfo.groupId.toInt(), jceInfo.groupname
)
)
)
}
count += data.groupList.size
println("Loading friendGroup list: ${count}/${total}")
if (count >= total) break
}
println("Successfully loaded friendGroup list: $count in total")
return friendGroupInfos
}
suspend fun main() {
val bot = BotFactory.newBot(id, "pwd") {
fileBasedDeviceInfo("device.json")
this.protocol = BotConfiguration.MiraiProtocol.MACOS
}.alsoLogin()
assertEquals("我的好友", bot.friendGroups.first().name)
// create
val newGroup = bot.friendGroups.create("111")
val newGroup2 = bot.friendGroups.create("111")
assertTrue { refreshFriendGroupList(bot as QQAndroidBot).count { it.name == "111" } == 2 && newGroup.id != newGroup2.id }
newGroup2.delete()
// refresh from server
refreshFriendGroupList(bot as QQAndroidBot).forEach {
println(it.name + " for " + it.id)
}
val newGroupInList = refreshFriendGroupList(bot).firstOrNull { it.name == "111" }
assertTrue { newGroupInList != null && newGroupInList.id == newGroup.id }
// moveIn
val friend = bot.getFriend(1930893235)!!
assertTrue {
bot.friendGroups[friend.friendGroup.id]!!.friends.contains(friend)
}
val old = bot.friendGroups.friendGroups.first()
old.moveIn(friend)
val target = bot.friendGroups.friendGroups.last()
assertFalse {
target.friends.contains(friend)
}
target.moveIn(friend)
assertTrue {
target.friends.contains(friend) && bot.friendGroups[friend.friendGroup.id]!!.friends.contains(friend) && !old.friends.contains(
friend
)
}
assertEquals(friend.queryProfile().friendGroupId, friend.friendGroup.id)
newGroup.moveIn(friend)
assertTrue { newGroup.friends.contains(friend) && friend.queryProfile().friendGroupId == newGroup.id }
// rename
newGroup.renameTo("222")
assertTrue { refreshFriendGroupList(bot).first { it.name == "222" }.id == newGroup.id }
assertTrue { bot.friendGroups.first { it.name == "222" } == newGroup }
// del
newGroup.delete()
assertTrue { refreshFriendGroupList(bot).first { it.id == 0 }.friends.contains(friend) }
// refresh from server
bot.friendGroups.friendGroups.clear()
bot.friendGroups.friendGroups.addAll(refreshFriendGroupList(bot))
assertTrue { bot.friendGroups.firstOrNull { it == newGroup } == null }
println("-- test end --")
bot.join()
}