Redesign Contacts: Use interfaces and hide internal implementations

This commit is contained in:
Him188 2019-11-24 17:29:05 +08:00
parent 1a56235cb0
commit b797ef3cc1
14 changed files with 327 additions and 210 deletions

View File

@ -7,6 +7,8 @@ import kotlinx.coroutines.sync.Mutex
import kotlinx.coroutines.sync.withLock import kotlinx.coroutines.sync.withLock
import net.mamoe.mirai.Bot.ContactSystem import net.mamoe.mirai.Bot.ContactSystem
import net.mamoe.mirai.contact.* 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.BotNetworkHandler
import net.mamoe.mirai.network.protocol.tim.TIMBotNetworkHandler import net.mamoe.mirai.network.protocol.tim.TIMBotNetworkHandler
import net.mamoe.mirai.network.protocol.tim.packet.login.LoginResult 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 * @see Bot.contacts
*/ */
inner class ContactSystem internal constructor() { inner class ContactSystem internal constructor() {
inline val bot: Bot get() = this@Bot val bot: Bot get() = this@Bot
private val _groups = ContactList<Group>() @Suppress("PropertyName")
private lateinit var groupsUpdater: Job internal val _groups = MutableContactList<Group>()
val groups = ContactList<Group>() internal lateinit var groupsUpdater: Job
private val groupsLock = Mutex() private val groupsLock = Mutex()
private val _qqs = ContactList<QQ>() //todo 实现群列表和好友列表获取 val groups: ContactList<Group> = ContactList(_groups)
private lateinit var qqUpdaterJob: Job
val qqs: ContactList<QQ> = _qqs @Suppress("PropertyName")
internal val _qqs = MutableContactList<QQ>() //todo 实现群列表和好友列表获取
internal lateinit var qqUpdaterJob: Job
private val qqsLock = Mutex() private val qqsLock = Mutex()
val qqs: ContactList<QQ> = ContactList(_qqs)
/** /**
* 获取缓存的 QQ 对象. 若没有对应的缓存, 则会创建一个. * 获取缓存的 QQ 对象. 若没有对应的缓存, 则会创建一个.
* *
* : 这个方法是线程安全的 * : 这个方法是线程安全的
*/ */
suspend fun getQQ(id: UInt): QQ = suspend fun getQQ(id: UInt): QQ =
if (qqs.containsKey(id)) qqs[id]!! if (_qqs.containsKey(id)) _qqs[id]!!
else qqsLock.withLock { 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 { suspend fun getGroup(id: GroupId): Group = id.value.let {
if (groups.containsKey(it)) groups[it]!! if (_groups.containsKey(it)) _groups[it]!!
else groupsLock.withLock { 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()) 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() { suspend fun close() {
network.close() network.close()
this.coroutineContext.cancelChildren() this.coroutineContext.cancelChildren()
contacts.groups.clear() contacts._groups.clear()
contacts.qqs.clear() contacts._qqs.clear()
} }
companion object { companion object {

View File

@ -38,14 +38,12 @@ suspend inline fun Bot.getGroup(internalId: GroupInternalId): Group = this.conta
/** /**
* 取得机器人的群成员列表 * 取得机器人的群成员列表
*/ */
inline val Bot.groups: ContactList<Group> inline val Bot.groups: ContactList<Group> get() = this.contacts.groups
get() = this.contacts.groups
/** /**
* 取得机器人的好友列表 * 取得机器人的好友列表
*/ */
inline val Bot.qqs: ContactList<QQ> inline val Bot.qqs: ContactList<QQ> get() = this.contacts.qqs
get() = this.contacts.qqs
/** /**
* [BotSession] 作为接收器 (receiver) 并调用 [block], 返回 [block] 的返回值. * [BotSession] 作为接收器 (receiver) 并调用 [block], 返回 [block] 的返回值.

View File

@ -1,48 +1,40 @@
@file:Suppress("EXPERIMENTAL_API_USAGE", "unused", "MemberVisibilityCanBePrivate") @file:Suppress("EXPERIMENTAL_API_USAGE")
package net.mamoe.mirai.contact 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.Bot
import net.mamoe.mirai.message.Message import net.mamoe.mirai.message.Message
import net.mamoe.mirai.message.MessageChain import net.mamoe.mirai.message.MessageChain
import net.mamoe.mirai.message.singleChain import net.mamoe.mirai.message.singleChain
import net.mamoe.mirai.network.BotSession 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 net.mamoe.mirai.withSession
import kotlin.contracts.ExperimentalContracts import kotlin.contracts.ExperimentalContracts
import kotlin.contracts.InvocationKind import kotlin.contracts.InvocationKind
import kotlin.contracts.contract import kotlin.contracts.contract
class ContactList<C : Contact> : MutableMap<UInt, C> by mutableMapOf()
/** /**
* 联系人. 虽然叫做联系人, 但他的子类有 [QQ] [][Group]. * 联系人. 虽然叫做联系人, 但他的子类有 [QQ] [][Group].
* *
* @param bot 这个联系人所属 [Bot]
* @param id 可以是 QQ 号码或者群号码 [GroupId].
*
* @author Him188moe * @author Him188moe
*/ */
sealed class Contact(val bot: Bot, val id: UInt) { interface Contact {
/**
* 这个联系人所属 [Bot]
*/
val bot: Bot
/**
* 可以是 QQ 号码或者群号码 [GroupId].
*/
val id: UInt
/** /**
* 向这个对象发送消息. * 向这个对象发送消息.
* *
* 速度太快会被服务器屏蔽(无响应). 在测试中不延迟地发送 6 条消息就会被屏蔽之后的数据包 1 秒左右. * 速度太快会被服务器屏蔽(无响应). 在测试中不延迟地发送 6 条消息就会被屏蔽之后的数据包 1 秒左右.
*/ */
abstract suspend fun sendMessage(message: MessageChain) suspend fun sendMessage(message: MessageChain)
//这两个方法应写为扩展函数, 但为方便 import 还是写在这里 //这两个方法应写为扩展函数, 但为方便 import 还是写在这里
@ -51,62 +43,6 @@ sealed class Contact(val bot: Bot, val id: UInt) {
suspend fun sendMessage(message: Message) = sendMessage(message.singleChain()) 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] 作为接收器 (receiver) 并调用 [block], 返回 [block] 的返回值.
* 这个方法将能帮助使用在 [BotSession] 中定义的一些扩展方法, [BotSession.sendAndExpectAsync] * 这个方法将能帮助使用在 [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) { class ContactList<C : Contact> internal constructor(private val delegate: MutableContactList<C>) : Map<UInt, C> by delegate
private var _profile: Profile? = null
private val _initialProfile by bot.network.SuspendLazy { updateProfile() }
/** /**
* 用户资料. * 可修改联系人列表. 只会在内部使用.
*/ */
val profile: Deferred<Profile> internal class MutableContactList<C : Contact> : MutableMap<UInt, C> by mutableMapOf()
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 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;
}

View File

@ -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)

View File

@ -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;
}

View File

@ -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
}

View File

@ -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;
}

View File

@ -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})"
}

View File

@ -170,8 +170,6 @@ inline class ImageId0x06(override inline val value: String) : ImageId {
/** /**
* 一般是群的图片的 id. * 一般是群的图片的 id.
*
* @param md5 用于下载图片时提交
*/ */
class ImageId0x03 constructor(override inline val value: String, inline val uniqueId: UInt, inline val height: Int, inline val width: Int) : ImageId { 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)" override fun toString(): String = "ImageId(value=$value, uniqueId=${uniqueId}, height=$height, width=$width)"

View File

@ -4,8 +4,8 @@ package net.mamoe.mirai.network.protocol.tim.packet.action
import com.soywiz.klock.Date import com.soywiz.klock.Date
import kotlinx.io.core.* import kotlinx.io.core.*
import net.mamoe.mirai.contact.Gender import net.mamoe.mirai.contact.data.Gender
import net.mamoe.mirai.contact.Profile import net.mamoe.mirai.contact.data.Profile
import net.mamoe.mirai.network.BotNetworkHandler import net.mamoe.mirai.network.BotNetworkHandler
import net.mamoe.mirai.network.protocol.tim.packet.* import net.mamoe.mirai.network.protocol.tim.packet.*
import net.mamoe.mirai.utils.io.* import net.mamoe.mirai.utils.io.*

View File

@ -64,7 +64,6 @@ object EventPacketFactory : PacketFactory<Packet, SessionKey>(SessionKey) {
} }
} }
operator fun invoke( operator fun invoke(
id: PacketId, id: PacketId,
sequenceId: UShort, sequenceId: UShort,

View File

@ -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()
}
}

View File

@ -3,7 +3,7 @@
package net.mamoe.mirai.network.protocol.tim.packet.login package net.mamoe.mirai.network.protocol.tim.packet.login
import kotlinx.io.core.* 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.BotNetworkHandler
import net.mamoe.mirai.network.protocol.tim.TIMProtocol import net.mamoe.mirai.network.protocol.tim.TIMProtocol
import net.mamoe.mirai.network.protocol.tim.packet.* import net.mamoe.mirai.network.protocol.tim.packet.*

View File

@ -61,6 +61,7 @@ class ExternalImage(
suspend fun ExternalImage.sendTo(contact: Contact) = when (contact) { suspend fun ExternalImage.sendTo(contact: Contact) = when (contact) {
is Group -> contact.uploadImage(this).sendTo(contact) is Group -> contact.uploadImage(this).sendTo(contact)
is QQ -> 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) { suspend fun ExternalImage.upload(contact: Contact): Image = when (contact) {
is Group -> contact.uploadImage(this).image() is Group -> contact.uploadImage(this).image()
is QQ -> contact.uploadImage(this).image() is QQ -> contact.uploadImage(this).image()
else -> assertUnreachable()
} }
/** /**