Introduce information descriptors for contacts

This commit is contained in:
Him188 2020-02-12 20:03:19 +08:00
parent b0d274dd1a
commit 51ee123a1e
6 changed files with 228 additions and 38 deletions

View File

@ -11,9 +11,7 @@ package net.mamoe.mirai.qqandroid
import kotlinx.coroutines.launch
import net.mamoe.mirai.contact.*
import net.mamoe.mirai.data.FriendNameRemark
import net.mamoe.mirai.data.PreviousNameList
import net.mamoe.mirai.data.Profile
import net.mamoe.mirai.data.*
import net.mamoe.mirai.event.broadcast
import net.mamoe.mirai.event.events.*
import net.mamoe.mirai.event.events.MessageSendEvent.FriendMessageSendEvent
@ -24,6 +22,7 @@ import net.mamoe.mirai.message.data.MessageChain
import net.mamoe.mirai.message.data.NotOnlineImageFromFile
import net.mamoe.mirai.qqandroid.network.highway.HighwayHelper
import net.mamoe.mirai.qqandroid.network.highway.postImage
import net.mamoe.mirai.qqandroid.network.protocol.data.jce.StTroopMemberInfo
import net.mamoe.mirai.qqandroid.network.protocol.data.proto.Cmd0x352
import net.mamoe.mirai.qqandroid.network.protocol.packet.chat.TroopManagement
import net.mamoe.mirai.qqandroid.network.protocol.packet.chat.image.ImgStore
@ -33,6 +32,7 @@ import net.mamoe.mirai.qqandroid.utils.toIpV4AddressString
import net.mamoe.mirai.utils.*
import net.mamoe.mirai.utils.io.toUHexString
import kotlin.coroutines.CoroutineContext
import net.mamoe.mirai.qqandroid.network.protocol.data.jce.FriendInfo as JceFriendInfo
internal abstract class ContactImpl : Contact {
override fun hashCode(): Int {
@ -49,10 +49,22 @@ internal abstract class ContactImpl : Contact {
}
}
internal class QQImpl(bot: QQAndroidBot, override val coroutineContext: CoroutineContext, override val id: Long) : ContactImpl(), QQ {
override val bot: QQAndroidBot by bot.unsafeWeakRef()
internal inline class FriendInfoImpl(
private val jceFriendInfo: JceFriendInfo
) : FriendInfo {
override val nick: String get() = jceFriendInfo.nick ?: ""
override val uin: Long get() = jceFriendInfo.friendUin
}
override lateinit var nick: String
internal class QQImpl(
bot: QQAndroidBot,
override val coroutineContext: CoroutineContext,
override val id: Long,
private val friendInfo: FriendInfo
) : ContactImpl(), QQ {
override val bot: QQAndroidBot by bot.unsafeWeakRef()
override val nick: String
get() = friendInfo.nick
override suspend fun sendMessage(message: MessageChain) {
val event = FriendMessageSendEvent(this, message).broadcast()
@ -135,14 +147,17 @@ internal class QQImpl(bot: QQAndroidBot, override val coroutineContext: Coroutin
image.input.close()
}
@MiraiExperimentalAPI
override suspend fun queryProfile(): Profile {
TODO("not implemented")
}
@MiraiExperimentalAPI
override suspend fun queryPreviousNameList(): PreviousNameList {
TODO("not implemented")
}
@MiraiExperimentalAPI
override suspend fun queryRemark(): FriendNameRemark {
TODO("not implemented")
}
@ -156,24 +171,27 @@ internal class QQImpl(bot: QQAndroidBot, override val coroutineContext: Coroutin
}
@Suppress("MemberVisibilityCanBePrivate")
internal class MemberImpl(
qq: QQImpl,
var _groupCard: String,
var _specialTitle: String,
group: GroupImpl,
override val coroutineContext: CoroutineContext,
override var permission: MemberPermission
memberInfo: MemberInfo
) : ContactImpl(), Member, QQ by qq {
override val group: GroupImpl by group.unsafeWeakRef()
val qq: QQImpl by qq.unsafeWeakRef()
override var permission: MemberPermission = memberInfo.permission
internal var _nameCard: String = memberInfo.nameCard
internal var _specialTitle: String = memberInfo.specialTitle
override var nameCard: String
get() = _groupCard
get() = _nameCard
set(newValue) {
group.checkBotPermissionOperator()
if (_groupCard != newValue) {
val oldValue = _groupCard
_groupCard = newValue
if (_nameCard != newValue) {
val oldValue = _nameCard
_nameCard = newValue
launch {
bot.network.run {
TroopManagement.EditGroupNametag(
@ -223,7 +241,8 @@ internal class MemberImpl(
).sendAndExpect<TroopManagement.Mute.Response>()
}
MemberMuteEvent(this@MemberImpl, durationSeconds, null).broadcast()
@Suppress("RemoveRedundantQualifierName") // or unresolved reference
net.mamoe.mirai.event.events.MemberMuteEvent(this@MemberImpl, durationSeconds, null).broadcast()
return true
}
@ -241,7 +260,8 @@ internal class MemberImpl(
).sendAndExpect<TroopManagement.Mute.Response>()
}
MemberUnmuteEvent(this@MemberImpl, null).broadcast()
@Suppress("RemoveRedundantQualifierName") // or unresolved reference
net.mamoe.mirai.event.events.MemberUnmuteEvent(this@MemberImpl, null).broadcast()
return true
}
@ -269,25 +289,60 @@ internal class MemberImpl(
override fun hashCode(): Int = super.hashCode()
}
internal class MemberInfoImpl(
private val jceInfo: StTroopMemberInfo,
private val groupOwnerId: Long
) : MemberInfo {
override val uin: Long get() = jceInfo.memberUin
override val nameCard: String get() = jceInfo.sName ?: ""
override val nick: String get() = jceInfo.nick
override val permission: MemberPermission
get() = when {
jceInfo.memberUin == groupOwnerId -> MemberPermission.OWNER
jceInfo.dwFlag == 1L -> MemberPermission.ADMINISTRATOR
else -> MemberPermission.MEMBER
}
override val specialTitle: String get() = jceInfo.sSpecialTitle ?: ""
}
/**
* 对GroupImpl
* 中name/announcement的更改会直接向服务器异步汇报
*/
@Suppress("PropertyName")
@UseExperimental(MiraiInternalAPI::class)
internal class GroupImpl(
bot: QQAndroidBot, override val coroutineContext: CoroutineContext,
override val id: Long,
val uin: Long,
var _name: String,
var _announcement: String,
var _allowMemberInvite: Boolean,
var _confessTalk: Boolean,
var _muteAll: Boolean,
var _autoApprove: Boolean,
var _anonymousChat: Boolean,
override val members: ContactList<Member>
groupInfo: GroupInfo,
members: Sequence<MemberInfo>
) : ContactImpl(), Group {
override val bot: QQAndroidBot by bot.unsafeWeakRef()
val uin: Long = groupInfo.uin
override lateinit var owner: Member
@UseExperimental(MiraiExperimentalAPI::class)
override lateinit var botPermission: MemberPermission
override val members: ContactList<Member> = ContactList(members.asSequence().mapNotNull {
if (it.uin == bot.uin) {
botPermission = it.permission
null
} else Member(it).also { member ->
if (member.permission == MemberPermission.OWNER) {
owner = member
}
}
}.toLockFreeLinkedList())
internal var _name: String = groupInfo.name
internal var _announcement: String = groupInfo.memo
internal var _allowMemberInvite: Boolean = groupInfo.allowMemberInvite
internal var _confessTalk: Boolean = groupInfo.confessTalk
internal var _muteAll: Boolean = groupInfo.muteAll
internal var _autoApprove: Boolean = groupInfo.autoApprove
internal var _anonymousChat: Boolean = groupInfo.allowAnonymousChat
override var name: String
get() = _name
@ -304,7 +359,7 @@ internal class GroupImpl(
newName = newValue
).sendWithoutExpect()
}
GroupNameChangeEvent(oldValue, newValue, this@GroupImpl, null).broadcast()
GroupNameChangeEvent(oldValue, newValue, this@GroupImpl, true).broadcast()
}
}
}
@ -377,7 +432,7 @@ internal class GroupImpl(
switch = newValue
).sendWithoutExpect()
}
GroupAllowConfessTalkEvent(oldValue, newValue, this@GroupImpl, null).broadcast()
GroupAllowConfessTalkEvent(oldValue, newValue, this@GroupImpl, true).broadcast()
}
}
}
@ -403,16 +458,21 @@ internal class GroupImpl(
}
}
override lateinit var owner: Member
@UseExperimental(MiraiExperimentalAPI::class)
override var botPermission: MemberPermission = MemberPermission.MEMBER
override suspend fun quit(): Boolean {
check(botPermission != MemberPermission.OWNER) { "An owner cannot quit from a owning group" }
TODO("not implemented")
}
@UseExperimental(MiraiExperimentalAPI::class)
override fun Member(memberInfo: MemberInfo): Member {
return MemberImpl(
bot.QQ(memberInfo) as QQImpl,
this,
this.coroutineContext,
memberInfo
)
}
override operator fun get(id: Long): Member {
return members.delegate.filteringGetOrNull { it.id == id } ?: throw NoSuchElementException("member $id not found in group $uin")
@ -426,8 +486,6 @@ internal class GroupImpl(
return members.delegate.filteringGetOrNull { it.id == id }
}
override val bot: QQAndroidBot by bot.unsafeWeakRef()
override suspend fun sendMessage(message: MessageChain) {
val event = GroupMessageSendEvent(this, message).broadcast()
if (event.isCancelled) {

View File

@ -19,13 +19,13 @@ import kotlinx.io.core.IoBuffer
import kotlinx.io.core.use
import net.mamoe.mirai.contact.*
import net.mamoe.mirai.data.AddFriendResult
import net.mamoe.mirai.data.FriendInfo
import net.mamoe.mirai.data.GroupInfo
import net.mamoe.mirai.data.MemberInfo
import net.mamoe.mirai.message.data.Image
import net.mamoe.mirai.network.BotNetworkHandler
import net.mamoe.mirai.utils.MiraiInternalAPI
import net.mamoe.mirai.utils.MiraiLogger
import net.mamoe.mirai.utils.WeakRef
import net.mamoe.mirai.utils.*
import net.mamoe.mirai.utils.io.transferTo
import net.mamoe.mirai.utils.toList
/**
* 机器人对象. 一个机器人实例登录一个 QQ 账号.
@ -65,6 +65,13 @@ abstract class Bot : CoroutineScope {
*/
abstract val uin: Long
/**
* 昵称
*/
@MiraiExperimentalAPI("还未支持")
val nick: String
get() = TODO("bot 昵称获取")
/**
* 日志记录器
*/
@ -116,7 +123,7 @@ abstract class Bot : CoroutineScope {
* [Bot] 无法管理这个对象, 但这个对象会以 [Bot] [Job] 作为父 Job.
* 因此, [Bot] 被关闭后, 这个对象也会被关闭.
*/
abstract fun QQ(id: Long): QQ
abstract fun QQ(friendInfo: FriendInfo): QQ
/**
* 机器人加入的群列表.
@ -131,6 +138,25 @@ abstract class Bot : CoroutineScope {
?: throw NoSuchElementException("No such group $id for bot ${this.uin}")
}
/**
* 获取群列表. 返回值前 32 bits uin, 32 bits groupCode
*/
abstract suspend fun queryGroupList(): Sequence<Long>
/**
* 查询群资料. 获得的仅为当前时刻的资料.
* 请优先使用 [getGroup] 然后查看群资料.
*/
abstract suspend fun queryGroupInfo(id: Long): GroupInfo
/**
* 查询群成员列表.
* 请优先使用 [getGroup], [Group.members] 查看群成员.
*
* 这个函数很慢. 请不要频繁使用.
*/
abstract suspend fun queryGroupMemberList(groupUin: Long, groupCode: Long, ownerId: Long): Sequence<MemberInfo>
// TODO 目前还不能构造群对象. 这将在以后支持
// endregion

View File

@ -12,8 +12,10 @@
package net.mamoe.mirai.contact
import kotlinx.coroutines.CoroutineScope
import net.mamoe.mirai.data.MemberInfo
import net.mamoe.mirai.event.events.*
import net.mamoe.mirai.utils.MiraiExperimentalAPI
import kotlin.jvm.JvmName
/**
* . QQ Android 中叫做 "Troop"
@ -117,6 +119,14 @@ interface Group : Contact, CoroutineScope {
*/
suspend fun quit(): Boolean
/**
* 构造一个 [Member].
* 非特殊情况请不要使用这个函数. 优先使用 [get].
*/
@MiraiExperimentalAPI("dangerous")
@Suppress("INAPPLICABLE_JVM_NAME")
@JvmName("newMember")
fun Member(memberInfo: MemberInfo): Member
companion object {

View File

@ -0,0 +1,16 @@
/*
* Copyright 2020 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/master/LICENSE
*/
package net.mamoe.mirai.data
interface FriendInfo {
val uin: Long
val nick: String
}

View File

@ -0,0 +1,60 @@
package net.mamoe.mirai.data
import net.mamoe.mirai.Bot
/**
* 群资料.
*
* 通过 [Bot.queryGroupInfo] 得到
*/
interface GroupInfo {
/**
* Uin
*/
val uin: Long
/**
* 群号码
*/ // 由 uin 计算得到
val groupCode: Long
/**
* 名称
*/
val name: String
/**
* 群主
*/
val owner: Long
/**
* 入群公告
*/
val memo: String
/**
* 允许群员邀请其他人加入群
*/
val allowMemberInvite: Boolean
/**
* 允许匿名聊天
*/
val allowAnonymousChat: Boolean
/**
* 自动审批加群请求
*/
val autoApprove: Boolean
/**
* 坦白说开启状态
*/
val confessTalk: Boolean
/**
* 全员禁言
*/
val muteAll: Boolean
}

View File

@ -0,0 +1,20 @@
/*
* Copyright 2020 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/master/LICENSE
*/
package net.mamoe.mirai.data
import net.mamoe.mirai.contact.MemberPermission
interface MemberInfo : FriendInfo {
val nameCard: String
val permission: MemberPermission
val specialTitle: String
}