mirror of
https://github.com/mamoe/mirai.git
synced 2025-01-09 09:50:16 +08:00
Redesign Contacts: Use interfaces and hide internal implementations
This commit is contained in:
parent
1a56235cb0
commit
b797ef3cc1
@ -7,6 +7,8 @@ import kotlinx.coroutines.sync.Mutex
|
||||
import kotlinx.coroutines.sync.withLock
|
||||
import net.mamoe.mirai.Bot.ContactSystem
|
||||
import net.mamoe.mirai.contact.*
|
||||
import net.mamoe.mirai.contact.internal.GroupImpl
|
||||
import net.mamoe.mirai.contact.internal.QQImpl
|
||||
import net.mamoe.mirai.network.BotNetworkHandler
|
||||
import net.mamoe.mirai.network.protocol.tim.TIMBotNetworkHandler
|
||||
import net.mamoe.mirai.network.protocol.tim.packet.login.LoginResult
|
||||
@ -95,27 +97,31 @@ class Bot(val account: BotAccount, val logger: MiraiLogger) : CoroutineScope {
|
||||
* @see Bot.contacts
|
||||
*/
|
||||
inner class ContactSystem internal constructor() {
|
||||
inline val bot: Bot get() = this@Bot
|
||||
val bot: Bot get() = this@Bot
|
||||
|
||||
private val _groups = ContactList<Group>()
|
||||
private lateinit var groupsUpdater: Job
|
||||
val groups = ContactList<Group>()
|
||||
@Suppress("PropertyName")
|
||||
internal val _groups = MutableContactList<Group>()
|
||||
internal lateinit var groupsUpdater: Job
|
||||
private val groupsLock = Mutex()
|
||||
|
||||
private val _qqs = ContactList<QQ>() //todo 实现群列表和好友列表获取
|
||||
private lateinit var qqUpdaterJob: Job
|
||||
val qqs: ContactList<QQ> = _qqs
|
||||
val groups: ContactList<Group> = ContactList(_groups)
|
||||
|
||||
@Suppress("PropertyName")
|
||||
internal val _qqs = MutableContactList<QQ>() //todo 实现群列表和好友列表获取
|
||||
internal lateinit var qqUpdaterJob: Job
|
||||
private val qqsLock = Mutex()
|
||||
|
||||
val qqs: ContactList<QQ> = ContactList(_qqs)
|
||||
|
||||
/**
|
||||
* 获取缓存的 QQ 对象. 若没有对应的缓存, 则会创建一个.
|
||||
*
|
||||
* 注: 这个方法是线程安全的
|
||||
*/
|
||||
suspend fun getQQ(id: UInt): QQ =
|
||||
if (qqs.containsKey(id)) qqs[id]!!
|
||||
if (_qqs.containsKey(id)) _qqs[id]!!
|
||||
else qqsLock.withLock {
|
||||
qqs.getOrPut(id) { QQ(bot, id) }
|
||||
_qqs.getOrPut(id) { QQImpl(bot, id) }
|
||||
}
|
||||
|
||||
/**
|
||||
@ -131,12 +137,11 @@ class Bot(val account: BotAccount, val logger: MiraiLogger) : CoroutineScope {
|
||||
* 注: 这个方法是线程安全的
|
||||
*/
|
||||
suspend fun getGroup(id: GroupId): Group = id.value.let {
|
||||
if (groups.containsKey(it)) groups[it]!!
|
||||
if (_groups.containsKey(it)) _groups[it]!!
|
||||
else groupsLock.withLock {
|
||||
groups.getOrPut(it) { Group(bot, id) }
|
||||
_groups.getOrPut(it) { GroupImpl(bot, id) }
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
suspend inline fun Int.qq(): QQ = getQQ(this.coerceAtLeastOrFail(0).toUInt())
|
||||
@ -152,8 +157,8 @@ class Bot(val account: BotAccount, val logger: MiraiLogger) : CoroutineScope {
|
||||
suspend fun close() {
|
||||
network.close()
|
||||
this.coroutineContext.cancelChildren()
|
||||
contacts.groups.clear()
|
||||
contacts.qqs.clear()
|
||||
contacts._groups.clear()
|
||||
contacts._qqs.clear()
|
||||
}
|
||||
|
||||
companion object {
|
||||
|
@ -38,14 +38,12 @@ suspend inline fun Bot.getGroup(internalId: GroupInternalId): Group = this.conta
|
||||
/**
|
||||
* 取得机器人的群成员列表
|
||||
*/
|
||||
inline val Bot.groups: ContactList<Group>
|
||||
get() = this.contacts.groups
|
||||
inline val Bot.groups: ContactList<Group> get() = this.contacts.groups
|
||||
|
||||
/**
|
||||
* 取得机器人的好友列表
|
||||
*/
|
||||
inline val Bot.qqs: ContactList<QQ>
|
||||
get() = this.contacts.qqs
|
||||
inline val Bot.qqs: ContactList<QQ> get() = this.contacts.qqs
|
||||
|
||||
/**
|
||||
* 以 [BotSession] 作为接收器 (receiver) 并调用 [block], 返回 [block] 的返回值.
|
||||
|
@ -1,48 +1,40 @@
|
||||
@file:Suppress("EXPERIMENTAL_API_USAGE", "unused", "MemberVisibilityCanBePrivate")
|
||||
@file:Suppress("EXPERIMENTAL_API_USAGE")
|
||||
|
||||
package net.mamoe.mirai.contact
|
||||
|
||||
import com.soywiz.klock.Date
|
||||
import kotlinx.coroutines.CompletableDeferred
|
||||
import kotlinx.coroutines.Deferred
|
||||
import net.mamoe.mirai.Bot
|
||||
import net.mamoe.mirai.message.Message
|
||||
import net.mamoe.mirai.message.MessageChain
|
||||
import net.mamoe.mirai.message.singleChain
|
||||
import net.mamoe.mirai.network.BotSession
|
||||
import net.mamoe.mirai.network.protocol.tim.packet.action.RequestProfileDetailsPacket
|
||||
import net.mamoe.mirai.network.protocol.tim.packet.action.RequestProfileDetailsResponse
|
||||
import net.mamoe.mirai.network.protocol.tim.packet.action.SendFriendMessagePacket
|
||||
import net.mamoe.mirai.network.protocol.tim.packet.action.SendGroupMessagePacket
|
||||
import net.mamoe.mirai.network.sessionKey
|
||||
import net.mamoe.mirai.qqAccount
|
||||
import net.mamoe.mirai.sendPacket
|
||||
import net.mamoe.mirai.utils.SuspendLazy
|
||||
import net.mamoe.mirai.utils.internal.PositiveNumbers
|
||||
import net.mamoe.mirai.utils.internal.coerceAtLeastOrFail
|
||||
import net.mamoe.mirai.withSession
|
||||
import kotlin.contracts.ExperimentalContracts
|
||||
import kotlin.contracts.InvocationKind
|
||||
import kotlin.contracts.contract
|
||||
|
||||
class ContactList<C : Contact> : MutableMap<UInt, C> by mutableMapOf()
|
||||
|
||||
/**
|
||||
* 联系人. 虽然叫做联系人, 但他的子类有 [QQ] 和 [群][Group].
|
||||
*
|
||||
* @param bot 这个联系人所属 [Bot]
|
||||
* @param id 可以是 QQ 号码或者群号码 [GroupId].
|
||||
*
|
||||
* @author Him188moe
|
||||
*/
|
||||
sealed class Contact(val bot: Bot, val id: UInt) {
|
||||
interface Contact {
|
||||
/**
|
||||
* 这个联系人所属 [Bot]
|
||||
*/
|
||||
val bot: Bot
|
||||
|
||||
/**
|
||||
* 可以是 QQ 号码或者群号码 [GroupId].
|
||||
*/
|
||||
val id: UInt
|
||||
|
||||
/**
|
||||
* 向这个对象发送消息.
|
||||
*
|
||||
* 速度太快会被服务器屏蔽(无响应). 在测试中不延迟地发送 6 条消息就会被屏蔽之后的数据包 1 秒左右.
|
||||
*/
|
||||
abstract suspend fun sendMessage(message: MessageChain)
|
||||
suspend fun sendMessage(message: MessageChain)
|
||||
|
||||
|
||||
//这两个方法应写为扩展函数, 但为方便 import 还是写在这里
|
||||
@ -51,62 +43,6 @@ sealed class Contact(val bot: Bot, val id: UInt) {
|
||||
suspend fun sendMessage(message: Message) = sendMessage(message.singleChain())
|
||||
}
|
||||
|
||||
/**
|
||||
* 一般的用户可见的 ID.
|
||||
* 在 TIM/QQ 客户端中所看到的的号码均是这个 ID.
|
||||
*
|
||||
* 注: 在引用群 ID 时, 应使用 [GroupId] 或 [GroupInternalId] 类型, 而不是 [UInt]
|
||||
*
|
||||
* @see GroupInternalId.toId 由 [GroupInternalId] 转换为 [GroupId]
|
||||
* @see GroupId.toInternalId 由 [GroupId] 转换为 [GroupInternalId]
|
||||
*/
|
||||
inline class GroupId(inline val value: UInt)
|
||||
|
||||
/**
|
||||
* 将 [this] 转为 [GroupId].
|
||||
*/
|
||||
fun UInt.groupId(): GroupId = GroupId(this)
|
||||
|
||||
/**
|
||||
* 将无符号整数格式的 [Long] 转为 [GroupId].
|
||||
*
|
||||
* 注: 在 Java 中常用 [Long] 来表示 [UInt]
|
||||
*/
|
||||
fun @receiver:PositiveNumbers Long.groupId(): GroupId = GroupId(this.coerceAtLeastOrFail(0).toUInt())
|
||||
|
||||
/**
|
||||
* 一些群 API 使用的 ID. 在使用时会特别注明
|
||||
*
|
||||
* 注: 在引用群 ID 时, 应使用 [GroupId] 或 [GroupInternalId] 类型, 而不是 [UInt]
|
||||
*
|
||||
* @see GroupInternalId.toId 由 [GroupInternalId] 转换为 [GroupId]
|
||||
* @see GroupId.toInternalId 由 [GroupId] 转换为 [GroupInternalId]
|
||||
*/
|
||||
inline class GroupInternalId(inline val value: UInt)
|
||||
|
||||
/**
|
||||
* 群.
|
||||
*
|
||||
* Group ID 与 Group Number 并不是同一个值.
|
||||
* - Group Number([Group.id]) 是通常使用的群号码.(在 QQ 客户端中可见)
|
||||
* - Group ID([Group.internalId]) 是与调用 API 时使用的 id.(在 QQ 客户端中不可见)
|
||||
* @author Him188moe
|
||||
*/
|
||||
@Suppress("MemberVisibilityCanBePrivate", "CanBeParameter")
|
||||
class Group internal constructor(bot: Bot, val groupId: GroupId) : Contact(bot, groupId.value) {
|
||||
val internalId = GroupId(id).toInternalId()
|
||||
val members: ContactList<Member>
|
||||
get() = TODO("Implementing group members is less important")
|
||||
|
||||
override suspend fun sendMessage(message: MessageChain) {
|
||||
bot.sendPacket(SendGroupMessagePacket(bot.qqAccount, internalId, bot.sessionKey, message))
|
||||
}
|
||||
|
||||
override fun toString(): String = "Group(${this.id})"
|
||||
|
||||
companion object
|
||||
}
|
||||
|
||||
/**
|
||||
* 以 [BotSession] 作为接收器 (receiver) 并调用 [block], 返回 [block] 的返回值.
|
||||
* 这个方法将能帮助使用在 [BotSession] 中定义的一些扩展方法, 如 [BotSession.sendAndExpectAsync]
|
||||
@ -120,117 +56,11 @@ inline fun <R> Contact.withSession(block: BotSession.() -> R): R {
|
||||
}
|
||||
|
||||
/**
|
||||
* QQ 对象.
|
||||
* 注意: 一个 [QQ] 实例并不是独立的, 它属于一个 [Bot].
|
||||
* 它不能被直接构造. 任何时候都应从 [Bot.qq], [Bot.ContactSystem.getQQ], [BotSession.qq] 或事件中获取.
|
||||
*
|
||||
* 对于同一个 [Bot] 任何一个人的 [QQ] 实例都是单一的.
|
||||
*
|
||||
* A QQ instance helps you to receive event from or sendPacket event to.
|
||||
* Notice that, one QQ instance belong to one [Bot], that is, QQ instances from different [Bot] are NOT the same.
|
||||
*
|
||||
* @author Him188moe
|
||||
* 只读联系人列表
|
||||
*/
|
||||
open class QQ internal constructor(bot: Bot, id: UInt) : Contact(bot, id) {
|
||||
private var _profile: Profile? = null
|
||||
private val _initialProfile by bot.network.SuspendLazy { updateProfile() }
|
||||
|
||||
/**
|
||||
* 用户资料.
|
||||
*/
|
||||
val profile: Deferred<Profile>
|
||||
get() = if (_profile == null) _initialProfile else CompletableDeferred(_profile!!)
|
||||
|
||||
override suspend fun sendMessage(message: MessageChain) =
|
||||
bot.sendPacket(SendFriendMessagePacket(bot.qqAccount, id, bot.sessionKey, message))
|
||||
|
||||
/**
|
||||
* 更新个人资料.
|
||||
* 将会同步更新 property [profile]
|
||||
*/
|
||||
suspend fun updateProfile(): Profile = bot.withSession {
|
||||
_profile = RequestProfileDetailsPacket(bot.qqAccount, id, sessionKey)
|
||||
.sendAndExpect<RequestProfileDetailsResponse, Profile> { it.profile }
|
||||
|
||||
return _profile!!
|
||||
}
|
||||
|
||||
override fun toString(): String = "QQ(${this.id})"
|
||||
}
|
||||
|
||||
class ContactList<C : Contact> internal constructor(private val delegate: MutableContactList<C>) : Map<UInt, C> by delegate
|
||||
|
||||
/**
|
||||
* 群成员
|
||||
* 可修改联系人列表. 只会在内部使用.
|
||||
*/
|
||||
class Member internal constructor(bot: Bot, id: UInt, val group: Group) : QQ(bot, id) {
|
||||
init {
|
||||
TODO("Group member implementation")
|
||||
}
|
||||
|
||||
override fun toString(): String = "Member(${this.id})"
|
||||
}
|
||||
|
||||
/**
|
||||
* 群成员的权限
|
||||
*/
|
||||
enum class MemberPermission {
|
||||
/**
|
||||
* 群主
|
||||
*/
|
||||
OWNER,
|
||||
/**
|
||||
* 管理员
|
||||
*/
|
||||
OPERATOR,
|
||||
/**
|
||||
* 一般群成员
|
||||
*/
|
||||
MEMBER;
|
||||
}
|
||||
|
||||
/**
|
||||
* 个人资料
|
||||
*/
|
||||
@Suppress("PropertyName")
|
||||
data class Profile(
|
||||
val qq: UInt,
|
||||
val nickname: String,
|
||||
val englishName: String?,
|
||||
val chineseName: String?,
|
||||
val qAge: Int?, // q 龄
|
||||
val zipCode: String?,
|
||||
val phone: String?,
|
||||
val gender: Gender,
|
||||
val birthday: Date?,
|
||||
val personalStatement: String?,// 个人说明
|
||||
val school: String?,
|
||||
val homepage: String?,
|
||||
val email: String?,
|
||||
val company: String?
|
||||
) {
|
||||
|
||||
override fun toString(): String = "Profile(qq=$qq, " +
|
||||
"nickname=$nickname, " +
|
||||
"gender=$gender, " +
|
||||
(englishName?.let { "englishName=$englishName, " } ?: "") +
|
||||
(chineseName?.let { "chineseName=$chineseName, " } ?: "") +
|
||||
(qAge?.toString()?.let { "qAge=$qAge, " } ?: "") +
|
||||
(zipCode?.let { "zipCode=$zipCode, " } ?: "") +
|
||||
(phone?.let { "phone=$phone, " } ?: "") +
|
||||
(birthday?.toString()?.let { "birthday=$birthday, " } ?: "") +
|
||||
(personalStatement?.let { "personalStatement=$personalStatement, " } ?: "") +
|
||||
(school?.let { "school=$school, " } ?: "") +
|
||||
(homepage?.let { "homepage=$homepage, " } ?: "") +
|
||||
(email?.let { "email=$email, " } ?: "") +
|
||||
(company?.let { "company=$company," } ?: "") +
|
||||
")"// 最终会是 ", )", 但这并不影响什么.
|
||||
}
|
||||
|
||||
/**
|
||||
* 性别
|
||||
*/
|
||||
enum class Gender {
|
||||
SECRET,
|
||||
MALE,
|
||||
FEMALE;
|
||||
}
|
||||
internal class MutableContactList<C : Contact> : MutableMap<UInt, C> by mutableMapOf()
|
@ -0,0 +1,57 @@
|
||||
@file:Suppress("EXPERIMENTAL_API_USAGE")
|
||||
|
||||
package net.mamoe.mirai.contact
|
||||
|
||||
import net.mamoe.mirai.utils.internal.PositiveNumbers
|
||||
import net.mamoe.mirai.utils.internal.coerceAtLeastOrFail
|
||||
|
||||
|
||||
/**
|
||||
* 群.
|
||||
*
|
||||
* Group ID 与 Group Number 并不是同一个值.
|
||||
* - Group Number([Group.id]) 是通常使用的群号码.(在 QQ 客户端中可见)
|
||||
* - Group ID([Group.internalId]) 是与调用 API 时使用的 id.(在 QQ 客户端中不可见)
|
||||
* @author Him188moe
|
||||
*/
|
||||
interface Group : Contact {
|
||||
val internalId: GroupInternalId
|
||||
|
||||
val member: ContactList<Member>
|
||||
|
||||
suspend fun getMember(id: UInt): Member
|
||||
}
|
||||
|
||||
/**
|
||||
* 一般的用户可见的 ID.
|
||||
* 在 TIM/QQ 客户端中所看到的的号码均是这个 ID.
|
||||
*
|
||||
* 注: 在引用群 ID 时, 应使用 [GroupId] 或 [GroupInternalId] 类型, 而不是 [UInt]
|
||||
*
|
||||
* @see GroupInternalId.toId 由 [GroupInternalId] 转换为 [GroupId]
|
||||
* @see GroupId.toInternalId 由 [GroupId] 转换为 [GroupInternalId]
|
||||
*/
|
||||
inline class GroupId(inline val value: UInt)
|
||||
|
||||
/**
|
||||
* 将 [this] 转为 [GroupId].
|
||||
*/
|
||||
fun UInt.groupId(): GroupId = GroupId(this)
|
||||
|
||||
/**
|
||||
* 将无符号整数格式的 [Long] 转为 [GroupId].
|
||||
*
|
||||
* 注: 在 Java 中常用 [Long] 来表示 [UInt]
|
||||
*/
|
||||
fun @receiver:PositiveNumbers Long.groupId(): GroupId =
|
||||
GroupId(this.coerceAtLeastOrFail(0).toUInt())
|
||||
|
||||
/**
|
||||
* 一些群 API 使用的 ID. 在使用时会特别注明
|
||||
*
|
||||
* 注: 在引用群 ID 时, 应使用 [GroupId] 或 [GroupInternalId] 类型, 而不是 [UInt]
|
||||
*
|
||||
* @see GroupInternalId.toId 由 [GroupInternalId] 转换为 [GroupId]
|
||||
* @see GroupId.toInternalId 由 [GroupId] 转换为 [GroupInternalId]
|
||||
*/
|
||||
inline class GroupInternalId(inline val value: UInt)
|
@ -0,0 +1,31 @@
|
||||
package net.mamoe.mirai.contact
|
||||
|
||||
/**
|
||||
* 群成员.
|
||||
*
|
||||
* 使用 [QQ.equals]. 因此同 ID 的群成员和 QQ 是 `==` 的
|
||||
*/
|
||||
interface Member : QQ, Contact {
|
||||
/**
|
||||
* 所在的群
|
||||
*/
|
||||
val group: Group
|
||||
}
|
||||
|
||||
/**
|
||||
* 群成员的权限
|
||||
*/
|
||||
enum class MemberPermission {
|
||||
/**
|
||||
* 群主
|
||||
*/
|
||||
OWNER,
|
||||
/**
|
||||
* 管理员
|
||||
*/
|
||||
OPERATOR,
|
||||
/**
|
||||
* 一般群成员
|
||||
*/
|
||||
MEMBER;
|
||||
}
|
@ -0,0 +1,33 @@
|
||||
@file:Suppress("EXPERIMENTAL_API_USAGE")
|
||||
|
||||
package net.mamoe.mirai.contact
|
||||
|
||||
import kotlinx.coroutines.Deferred
|
||||
import net.mamoe.mirai.Bot
|
||||
import net.mamoe.mirai.contact.data.Profile
|
||||
import net.mamoe.mirai.network.BotSession
|
||||
|
||||
/**
|
||||
* QQ 对象.
|
||||
* 注意: 一个 [QQ] 实例并不是独立的, 它属于一个 [Bot].
|
||||
* 它不能被直接构造. 任何时候都应从 [Bot.qq], [Bot.ContactSystem.getQQ], [BotSession.qq] 或事件中获取.
|
||||
*
|
||||
* 对于同一个 [Bot] 任何一个人的 [QQ] 实例都是单一的.
|
||||
*
|
||||
* A QQ instance helps you to receive event from or sendPacket event to.
|
||||
* Notice that, one QQ instance belong to one [Bot], that is, QQ instances from different [Bot] are NOT the same.
|
||||
*
|
||||
* @author Him188moe
|
||||
*/
|
||||
interface QQ : Contact {
|
||||
/**
|
||||
* 用户资料.
|
||||
*/
|
||||
val profile: Deferred<Profile>
|
||||
|
||||
/**
|
||||
* 更新个人资料.
|
||||
* 将会同步更新 property [profile]
|
||||
*/
|
||||
suspend fun updateProfile(): Profile
|
||||
}
|
@ -0,0 +1,52 @@
|
||||
@file:Suppress("EXPERIMENTAL_API_USAGE")
|
||||
|
||||
package net.mamoe.mirai.contact.data
|
||||
|
||||
import com.soywiz.klock.Date
|
||||
|
||||
/**
|
||||
* 个人资料
|
||||
*/
|
||||
@Suppress("PropertyName")
|
||||
data class Profile(
|
||||
val qq: UInt,
|
||||
val nickname: String,
|
||||
val englishName: String?,
|
||||
val chineseName: String?,
|
||||
val qAge: Int?, // q 龄
|
||||
val zipCode: String?,
|
||||
val phone: String?,
|
||||
val gender: Gender,
|
||||
val birthday: Date?,
|
||||
val personalStatement: String?,// 个人说明
|
||||
val school: String?,
|
||||
val homepage: String?,
|
||||
val email: String?,
|
||||
val company: String?
|
||||
) {
|
||||
|
||||
override fun toString(): String = "Profile(qq=$qq, " +
|
||||
"nickname=$nickname, " +
|
||||
"gender=$gender, " +
|
||||
(englishName?.let { "englishName=$englishName, " } ?: "") +
|
||||
(chineseName?.let { "chineseName=$chineseName, " } ?: "") +
|
||||
(qAge?.toString()?.let { "qAge=$qAge, " } ?: "") +
|
||||
(zipCode?.let { "zipCode=$zipCode, " } ?: "") +
|
||||
(phone?.let { "phone=$phone, " } ?: "") +
|
||||
(birthday?.toString()?.let { "birthday=$birthday, " } ?: "") +
|
||||
(personalStatement?.let { "personalStatement=$personalStatement, " } ?: "") +
|
||||
(school?.let { "school=$school, " } ?: "") +
|
||||
(homepage?.let { "homepage=$homepage, " } ?: "") +
|
||||
(email?.let { "email=$email, " } ?: "") +
|
||||
(company?.let { "company=$company," } ?: "") +
|
||||
")"// 最终会是 ", )", 但这并不影响什么.
|
||||
}
|
||||
|
||||
/**
|
||||
* 性别
|
||||
*/
|
||||
enum class Gender {
|
||||
SECRET,
|
||||
MALE,
|
||||
FEMALE;
|
||||
}
|
@ -0,0 +1,87 @@
|
||||
@file:Suppress("EXPERIMENTAL_API_USAGE", "unused", "MemberVisibilityCanBePrivate")
|
||||
|
||||
package net.mamoe.mirai.contact.internal
|
||||
|
||||
import kotlinx.coroutines.CompletableDeferred
|
||||
import kotlinx.coroutines.Deferred
|
||||
import kotlinx.coroutines.sync.Mutex
|
||||
import kotlinx.coroutines.sync.withLock
|
||||
import net.mamoe.mirai.*
|
||||
import net.mamoe.mirai.contact.*
|
||||
import net.mamoe.mirai.contact.data.Profile
|
||||
import net.mamoe.mirai.message.Message
|
||||
import net.mamoe.mirai.message.MessageChain
|
||||
import net.mamoe.mirai.message.singleChain
|
||||
import net.mamoe.mirai.network.protocol.tim.packet.action.RequestProfileDetailsPacket
|
||||
import net.mamoe.mirai.network.protocol.tim.packet.action.RequestProfileDetailsResponse
|
||||
import net.mamoe.mirai.network.protocol.tim.packet.action.SendFriendMessagePacket
|
||||
import net.mamoe.mirai.network.protocol.tim.packet.action.SendGroupMessagePacket
|
||||
import net.mamoe.mirai.network.sessionKey
|
||||
import net.mamoe.mirai.utils.SuspendLazy
|
||||
|
||||
internal sealed class ContactImpl : Contact {
|
||||
abstract override suspend fun sendMessage(message: MessageChain)
|
||||
|
||||
//这两个方法应写为扩展函数, 但为方便 import 还是写在这里
|
||||
override suspend fun sendMessage(plain: String) = sendMessage(plain.singleChain())
|
||||
|
||||
override suspend fun sendMessage(message: Message) = sendMessage(message.singleChain())
|
||||
}
|
||||
|
||||
@Suppress("MemberVisibilityCanBePrivate", "CanBeParameter")
|
||||
internal class GroupImpl internal constructor(override val bot: Bot, val groupId: GroupId) : ContactImpl(), Group {
|
||||
override val id: UInt get() = groupId.value
|
||||
override val internalId = GroupId(id).toInternalId()
|
||||
|
||||
private val _members: MutableContactList<Member> = MutableContactList()
|
||||
override val member: ContactList<Member> = ContactList(_members)
|
||||
private val membersLock: Mutex = Mutex()
|
||||
|
||||
override suspend fun getMember(id: UInt): Member =
|
||||
if (_members.containsKey(id)) _members[id]!!
|
||||
else membersLock.withLock {
|
||||
_members.getOrPut(id) { MemberImpl(bot, bot.getQQ(id), this) }
|
||||
}
|
||||
|
||||
override suspend fun sendMessage(message: MessageChain) {
|
||||
bot.sendPacket(SendGroupMessagePacket(bot.qqAccount, internalId, bot.sessionKey, message))
|
||||
}
|
||||
|
||||
override fun toString(): String = "Group(${this.id})"
|
||||
}
|
||||
|
||||
internal class QQImpl internal constructor(override val bot: Bot, override val id: UInt) : ContactImpl(), QQ {
|
||||
private var _profile: Profile? = null
|
||||
private val _initialProfile by bot.network.SuspendLazy { updateProfile() }
|
||||
|
||||
override val profile: Deferred<Profile> get() = if (_profile == null) _initialProfile else CompletableDeferred(_profile!!)
|
||||
|
||||
override suspend fun sendMessage(message: MessageChain) =
|
||||
bot.sendPacket(SendFriendMessagePacket(bot.qqAccount, id, bot.sessionKey, message))
|
||||
|
||||
/**
|
||||
* 更新个人资料.
|
||||
* 将会同步更新 property [profile]
|
||||
*/
|
||||
override suspend fun updateProfile(): Profile = bot.withSession {
|
||||
_profile = RequestProfileDetailsPacket(bot.qqAccount, id, sessionKey)
|
||||
.sendAndExpect<RequestProfileDetailsResponse, Profile> { it.profile }
|
||||
|
||||
return _profile!!
|
||||
}
|
||||
|
||||
override fun toString(): String = "QQ(${this.id})"
|
||||
}
|
||||
|
||||
/**
|
||||
* 群成员
|
||||
*/
|
||||
internal class MemberImpl(override val bot: Bot, private val delegate: QQ, override val group: Group) : Member, ContactImpl() {
|
||||
override val profile: Deferred<Profile> get() = delegate.profile
|
||||
override val id: UInt get() = delegate.id
|
||||
|
||||
override suspend fun updateProfile(): Profile = delegate.updateProfile()
|
||||
override suspend fun sendMessage(message: MessageChain) = delegate.sendMessage(message)
|
||||
|
||||
override fun toString(): String = "Member(${this.id})"
|
||||
}
|
@ -170,8 +170,6 @@ inline class ImageId0x06(override inline val value: String) : ImageId {
|
||||
|
||||
/**
|
||||
* 一般是群的图片的 id.
|
||||
*
|
||||
* @param md5 用于下载图片时提交
|
||||
*/
|
||||
class ImageId0x03 constructor(override inline val value: String, inline val uniqueId: UInt, inline val height: Int, inline val width: Int) : ImageId {
|
||||
override fun toString(): String = "ImageId(value=$value, uniqueId=${uniqueId}, height=$height, width=$width)"
|
||||
|
@ -4,8 +4,8 @@ package net.mamoe.mirai.network.protocol.tim.packet.action
|
||||
|
||||
import com.soywiz.klock.Date
|
||||
import kotlinx.io.core.*
|
||||
import net.mamoe.mirai.contact.Gender
|
||||
import net.mamoe.mirai.contact.Profile
|
||||
import net.mamoe.mirai.contact.data.Gender
|
||||
import net.mamoe.mirai.contact.data.Profile
|
||||
import net.mamoe.mirai.network.BotNetworkHandler
|
||||
import net.mamoe.mirai.network.protocol.tim.packet.*
|
||||
import net.mamoe.mirai.utils.io.*
|
||||
|
@ -64,7 +64,6 @@ object EventPacketFactory : PacketFactory<Packet, SessionKey>(SessionKey) {
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
operator fun invoke(
|
||||
id: PacketId,
|
||||
sequenceId: UShort,
|
||||
|
@ -0,0 +1,25 @@
|
||||
@file:Suppress("EXPERIMENTAL_UNSIGNED_LITERALS")
|
||||
|
||||
package net.mamoe.mirai.network.protocol.tim.packet.event
|
||||
|
||||
import kotlinx.io.core.ByteReadPacket
|
||||
import net.mamoe.mirai.Bot
|
||||
import net.mamoe.mirai.contact.Member
|
||||
import net.mamoe.mirai.network.protocol.tim.packet.PacketVersion
|
||||
|
||||
|
||||
/**
|
||||
* 成员被踢出
|
||||
*/
|
||||
data class MemberKickEvent(
|
||||
val member: Member
|
||||
) : EventPacket
|
||||
|
||||
@PacketVersion(date = "2019.11.20", timVersion = "2.3.2 (21173)")
|
||||
object MemberKickEventPacketFactory : KnownEventParserAndHandler<MemberKickEvent>(0x0022u) {
|
||||
override suspend fun ByteReadPacket.parse(bot: Bot, identity: EventPacketIdentity): MemberKickEvent {
|
||||
|
||||
TODO()
|
||||
// return MemberKickEvent()
|
||||
}
|
||||
}
|
@ -3,7 +3,7 @@
|
||||
package net.mamoe.mirai.network.protocol.tim.packet.login
|
||||
|
||||
import kotlinx.io.core.*
|
||||
import net.mamoe.mirai.contact.Gender
|
||||
import net.mamoe.mirai.contact.data.Gender
|
||||
import net.mamoe.mirai.network.BotNetworkHandler
|
||||
import net.mamoe.mirai.network.protocol.tim.TIMProtocol
|
||||
import net.mamoe.mirai.network.protocol.tim.packet.*
|
||||
|
@ -61,6 +61,7 @@ class ExternalImage(
|
||||
suspend fun ExternalImage.sendTo(contact: Contact) = when (contact) {
|
||||
is Group -> contact.uploadImage(this).sendTo(contact)
|
||||
is QQ -> contact.uploadImage(this).sendTo(contact)
|
||||
else -> assertUnreachable()
|
||||
}
|
||||
|
||||
/**
|
||||
@ -72,6 +73,7 @@ suspend fun ExternalImage.sendTo(contact: Contact) = when (contact) {
|
||||
suspend fun ExternalImage.upload(contact: Contact): Image = when (contact) {
|
||||
is Group -> contact.uploadImage(this).image()
|
||||
is QQ -> contact.uploadImage(this).image()
|
||||
else -> assertUnreachable()
|
||||
}
|
||||
|
||||
/**
|
||||
|
Loading…
Reference in New Issue
Block a user