From c655c0fe336f7007bdf70b28ca0d51ee1dfff8a4 Mon Sep 17 00:00:00 2001 From: Him188 Date: Wed, 13 Nov 2019 20:28:48 +0800 Subject: [PATCH] Make Profile data class --- .../commonMain/kotlin/net.mamoe.mirai/Bot.kt | 4 +- .../kotlin/net.mamoe.mirai/contact/Contact.kt | 115 ++++++------------ .../net.mamoe.mirai/message/MessageType.kt | 12 +- .../message/internal/MessageDataInternal.kt | 6 +- .../net.mamoe.mirai/network/BotSession.kt | 6 +- .../protocol/tim/TIMBotNetworkHandler.kt | 4 +- .../tim/handler/TemporaryPacketHandler.kt | 2 +- .../protocol/tim/packet/action/Profile.kt | 22 ++-- .../packet/action/SendFriendMessagePacket.kt | 2 +- .../packet/action/SendGroupMessagePacket.kt | 2 +- .../net.mamoe.mirai/utils/SuspendLazy.kt | 2 +- .../utils/internal/NumberUtils.kt | 29 +++-- 12 files changed, 91 insertions(+), 115 deletions(-) diff --git a/mirai-core/src/commonMain/kotlin/net.mamoe.mirai/Bot.kt b/mirai-core/src/commonMain/kotlin/net.mamoe.mirai/Bot.kt index 5d9285b94..d69d2e7a0 100644 --- a/mirai-core/src/commonMain/kotlin/net.mamoe.mirai/Bot.kt +++ b/mirai-core/src/commonMain/kotlin/net.mamoe.mirai/Bot.kt @@ -65,7 +65,7 @@ class Bot(val account: BotAccount, val logger: MiraiLogger) : CoroutineScope { val contacts = ContactSystem() - var network: BotNetworkHandler<*> = TIMBotNetworkHandler(this) + var network: BotNetworkHandler<*> = TIMBotNetworkHandler(this.coroutineContext, this) init { launch { @@ -90,7 +90,7 @@ class Bot(val account: BotAccount, val logger: MiraiLogger) : CoroutineScope { } catch (e: Exception) { logger.error(e) } - network = TIMBotNetworkHandler(this) + network = TIMBotNetworkHandler(this.coroutineContext, this) return network.login(configuration) } diff --git a/mirai-core/src/commonMain/kotlin/net.mamoe.mirai/contact/Contact.kt b/mirai-core/src/commonMain/kotlin/net.mamoe.mirai/contact/Contact.kt index 2ad4baacb..24e5379ea 100644 --- a/mirai-core/src/commonMain/kotlin/net.mamoe.mirai/contact/Contact.kt +++ b/mirai-core/src/commonMain/kotlin/net.mamoe.mirai/contact/Contact.kt @@ -3,6 +3,7 @@ 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 @@ -17,6 +18,7 @@ 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 @@ -33,7 +35,9 @@ class ContactList : MutableMap by mutableMapOf() sealed class Contact(val bot: Bot, val id: UInt) { /** - * 向这个对象发送消息. 速度太快会被服务器拒绝(无响应) + * 向这个对象发送消息. + * + * 速度太快会被服务器拒绝(无响应). 在测试中不延迟地发送 6 条消息就会被屏蔽之后的数据包 1 秒左右. */ abstract suspend fun sendMessage(message: MessageChain) @@ -65,7 +69,7 @@ fun UInt.groupId(): GroupId = GroupId(this) * * 注: 在 Java 中常用 [Long] 来表示 [UInt] */ -fun Long.groupId(): GroupId = GroupId(this.coerceAtLeastOrFail(0).toUInt()) +fun @receiver:PositiveNumbers Long.groupId(): GroupId = GroupId(this.coerceAtLeastOrFail(0).toUInt()) /** * 一些群 API 使用的 ID. 在使用时会特别注明 @@ -95,9 +99,7 @@ class Group internal constructor(bot: Bot, val groupId: GroupId) : Contact(bot, bot.sendPacket(SendGroupMessagePacket(bot.qqAccount, internalId, bot.sessionKey, message)) } - override fun toString(): String { - return "Group(${this.id})" - } + override fun toString(): String = "Group(${this.id})" companion object } @@ -111,6 +113,9 @@ inline fun Contact.withSession(block: BotSession.() -> R): R = bot.withSessi /** * 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. @@ -118,12 +123,17 @@ inline fun Contact.withSession(block: BotSession.() -> R): R = bot.withSessi * @author Him188moe */ open class QQ internal constructor(bot: Bot, id: UInt) : Contact(bot, id) { - // TODO: 2019/11/8 should be suspend val if kotlin supports - val profile: Deferred by bot.network.SuspendLazy { updateProfile() } + private var _profile: Profile? = null + private val _initialProfile by bot.network.SuspendLazy { updateProfile() } - override suspend fun sendMessage(message: MessageChain) { + /** + * 用户资料. + */ + val profile: Deferred + get() = if (_profile == null) _initialProfile else CompletableDeferred(_profile!!) + + override suspend fun sendMessage(message: MessageChain) = bot.sendPacket(SendFriendMessagePacket(bot.qqAccount, id, bot.sessionKey, message)) - } /** * 更新个人资料. @@ -137,19 +147,13 @@ open class QQ internal constructor(bot: Bot, id: UInt) : Contact(bot, id) { * ``` */ suspend fun updateProfile(): Profile = bot.withSession { - RequestProfileDetailsPacket(bot.qqAccount, id, sessionKey) - .sendAndExpectAsync { it.profile } - .await().let { - @Suppress("UNCHECKED_CAST") - if ((::profile as SuspendLazy).isInitialized()) { - profile.await().apply { copyFrom(it) } - } else it - } + _profile = RequestProfileDetailsPacket(bot.qqAccount, id, sessionKey) + .sendAndExpect { it.profile } + + return _profile!! } - override fun toString(): String { - return "QQ(${this.id})" - } + override fun toString(): String = "QQ(${this.id})" } @@ -161,9 +165,7 @@ class Member internal constructor(bot: Bot, id: UInt, val group: Group) : QQ(bot TODO("Group member implementation") } - override fun toString(): String { - return "Member(${this.id})" - } + override fun toString(): String = "Member(${this.id})" } /** @@ -187,61 +189,20 @@ enum class MemberPermission { /** * 个人资料 */ -// FIXME: 2019/11/8 should be `data class Profile` @Suppress("PropertyName") -class Profile( - internal var _qq: UInt, - internal var _nickname: String, - internal var _zipCode: String?, - internal var _phone: String?, - internal var _gender: Gender, - internal var _birthday: Date?, - internal var _personalStatus: String?, - internal var _school: String?, - internal var _homepage: String?, - internal var _email: String?, - internal var _company: String? -) { - - val qq: UInt get() = _qq - val nickname: String get() = _nickname - val zipCode: String? get() = _zipCode - val phone: String? get() = _phone - val gender: Gender get() = _gender - /** - * 个性签名 - */ - val personalStatus: String? get() = _personalStatus - val school: String? get() = _school - val company: String? get() = _company - - /** - * 主页 - */ - val homepage: String? get() = _homepage - val email: String? get() = _email - val birthday: Date? get() = _birthday - - override fun toString(): String = "Profile(" + - "qq=$qq, nickname=$nickname, zipCode=$zipCode, phone=$phone, " + - "gender=$gender, birthday=$birthday, personalStatus=$personalStatus, school=$school, " + - "homepage=$homepage, email=$email, company=$company" + - ")" -} - -fun Profile.copyFrom(another: Profile) { - this._qq = another.qq - this._nickname = another.nickname - this._zipCode = another.zipCode - this._phone = another.phone - this._gender = another.gender - this._birthday = another.birthday - this._personalStatus = another.personalStatus - this._school = another.school - this._homepage = another.homepage - this._email = another.email - this._company = another.company -} +data class Profile( + val qq: UInt, + val nickname: String, + val zipCode: String?, + val phone: String?, + val gender: Gender, + val birthday: Date?, + val personalStatus: String?, + val school: String?, + val homepage: String?, + val email: String?, + val company: String? +) /** * 性别 diff --git a/mirai-core/src/commonMain/kotlin/net.mamoe.mirai/message/MessageType.kt b/mirai-core/src/commonMain/kotlin/net.mamoe.mirai/message/MessageType.kt index 94f09f9fa..7e0edd76e 100644 --- a/mirai-core/src/commonMain/kotlin/net.mamoe.mirai/message/MessageType.kt +++ b/mirai-core/src/commonMain/kotlin/net.mamoe.mirai/message/MessageType.kt @@ -8,10 +8,16 @@ enum class MessageType(val value: UByte) { PLAIN_TEXT(0x01u), AT(0x06u), FACE(0x02u), - GROUP_IMAGE(0x03u), - FRIEND_IMAGE(0x06u), + /** + * [ImageId.value] 长度为 42 的图片 + */ + IMAGE_42(0x03u), + /** + * [ImageId.value] 长度为 37 的图片 + */ + IMAGE_37(0x06u), ; - val intValue: Int = this.value.toInt() + inline val intValue: Int get() = this.value.toInt() } \ No newline at end of file diff --git a/mirai-core/src/commonMain/kotlin/net.mamoe.mirai/message/internal/MessageDataInternal.kt b/mirai-core/src/commonMain/kotlin/net.mamoe.mirai/message/internal/MessageDataInternal.kt index c08636a74..ad455e8a0 100644 --- a/mirai-core/src/commonMain/kotlin/net.mamoe.mirai/message/internal/MessageDataInternal.kt +++ b/mirai-core/src/commonMain/kotlin/net.mamoe.mirai/message/internal/MessageDataInternal.kt @@ -145,7 +145,7 @@ fun ByteReadPacket.readMessageChain(): MessageChain { return chain } -fun MessageChain.toPacket(forGroup: Boolean): ByteReadPacket = buildPacket { +fun MessageChain.toPacket(): ByteReadPacket = buildPacket { this@toPacket.forEach { message -> writePacket(with(message) { when (this) { @@ -170,7 +170,7 @@ fun MessageChain.toPacket(forGroup: Boolean): ByteReadPacket = buildPacket { when (id.value.length) { // "{F61593B5-5B98-1798-3F47-2A91D32ED2FC}.jpg" 42 -> { - writeUByte(MessageType.GROUP_IMAGE.value) + writeUByte(MessageType.IMAGE_42.value) //00 00 03 00 CB 02 00 2A 7B 46 36 31 35 39 33 42 35 2D 35 42 39 38 2D 31 37 39 38 2D 33 46 34 37 2D 32 41 39 31 44 33 32 45 44 32 46 43 7D 2E 6A 70 67 // 04 00 04 87 E5 09 3B 05 00 04 D2 C4 C0 B7 06 00 04 00 00 00 50 07 00 01 43 08 00 00 09 00 01 01 0B 00 00 14 00 04 00 00 00 00 15 00 04 00 00 01 ED 16 00 04 00 00 02 17 18 00 04 00 00 EB 34 FF 00 5C 15 36 20 39 32 6B 41 31 43 38 37 65 35 30 39 33 62 64 32 63 34 63 30 62 37 20 20 20 20 20 20 35 30 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 @@ -191,7 +191,7 @@ fun MessageChain.toPacket(forGroup: Boolean): ByteReadPacket = buildPacket { // "/01ee6426-5ff1-4cf0-8278-e8634d2909ef" 37 -> { - writeUByte(MessageType.FRIEND_IMAGE.value) + writeUByte(MessageType.IMAGE_37.value) // 00 00 06 00 F3 02 // 00 1B 24 5B 56 54 4A 38 60 4C 5A 4E 46 7D 53 39 4F 52 36 25 45 60 42 55 53 2E 6A 70 67 diff --git a/mirai-core/src/commonMain/kotlin/net.mamoe.mirai/network/BotSession.kt b/mirai-core/src/commonMain/kotlin/net.mamoe.mirai/network/BotSession.kt index 7a4d5b63d..0527cee9e 100644 --- a/mirai-core/src/commonMain/kotlin/net.mamoe.mirai/network/BotSession.kt +++ b/mirai-core/src/commonMain/kotlin/net.mamoe.mirai/network/BotSession.kt @@ -25,7 +25,6 @@ import net.mamoe.mirai.utils.getGTK import net.mamoe.mirai.utils.internal.PositiveNumbers import net.mamoe.mirai.utils.internal.coerceAtLeastOrFail import kotlin.coroutines.coroutineContext -import kotlin.jvm.JvmField /** * 构造 [BotSession] 的捷径 @@ -60,7 +59,6 @@ class BotSession( */ val sKey: String get() = _sKey - @JvmField @Suppress("PropertyName") internal var _sKey: String = "" set(value) { @@ -73,7 +71,6 @@ class BotSession( */ val gtk: Int get() = _gtk - @JvmField private var _gtk: Int = 0 /** @@ -139,6 +136,9 @@ class BotSession( * ``` * @sample Bot.addFriend 添加好友 */ + suspend inline fun OutgoingPacket.sendAndExpect(checkSequence: Boolean = true, crossinline block: (P) -> R): R = + sendAndExpectAsync(checkSequence) { block(it) }.await() + suspend inline fun OutgoingPacket.sendAndExpect(checkSequence: Boolean = true): P = sendAndExpectAsync(checkSequence) { it }.await() diff --git a/mirai-core/src/commonMain/kotlin/net.mamoe.mirai/network/protocol/tim/TIMBotNetworkHandler.kt b/mirai-core/src/commonMain/kotlin/net.mamoe.mirai/network/protocol/tim/TIMBotNetworkHandler.kt index 128041bbb..0d0756dba 100644 --- a/mirai-core/src/commonMain/kotlin/net.mamoe.mirai/network/protocol/tim/TIMBotNetworkHandler.kt +++ b/mirai-core/src/commonMain/kotlin/net.mamoe.mirai/network/protocol/tim/TIMBotNetworkHandler.kt @@ -38,11 +38,11 @@ internal expect val NetworkDispatcher: CoroutineDispatcher * * @see BotNetworkHandler */ -internal class TIMBotNetworkHandler internal constructor(override inline val bot: Bot) : +internal class TIMBotNetworkHandler internal constructor(coroutineContext: CoroutineContext, override inline val bot: Bot) : BotNetworkHandler, PacketHandlerList() { override val coroutineContext: CoroutineContext = - NetworkDispatcher + CoroutineExceptionHandler { _, e -> + coroutineContext + NetworkDispatcher + CoroutineExceptionHandler { _, e -> bot.logger.error("An exception was thrown in a coroutine under TIMBotNetworkHandler", e) } + SupervisorJob() diff --git a/mirai-core/src/commonMain/kotlin/net.mamoe.mirai/network/protocol/tim/handler/TemporaryPacketHandler.kt b/mirai-core/src/commonMain/kotlin/net.mamoe.mirai/network/protocol/tim/handler/TemporaryPacketHandler.kt index 3caa60b04..021e0f7c8 100644 --- a/mirai-core/src/commonMain/kotlin/net.mamoe.mirai/network/protocol/tim/handler/TemporaryPacketHandler.kt +++ b/mirai-core/src/commonMain/kotlin/net.mamoe.mirai/network/protocol/tim/handler/TemporaryPacketHandler.kt @@ -29,7 +29,7 @@ class TemporaryPacketHandler

( private val fromSession: BotSession, private val checkSequence: Boolean, /** - * 调用者的 [CoroutineContext] + * 调用者的 [CoroutineContext]. 包处理过程将会在这个 context 下运行 */ private val callerContext: CoroutineContext ) { diff --git a/mirai-core/src/commonMain/kotlin/net.mamoe.mirai/network/protocol/tim/packet/action/Profile.kt b/mirai-core/src/commonMain/kotlin/net.mamoe.mirai/network/protocol/tim/packet/action/Profile.kt index 66ffc55db..eae77f2e9 100644 --- a/mirai-core/src/commonMain/kotlin/net.mamoe.mirai/network/protocol/tim/packet/action/Profile.kt +++ b/mirai-core/src/commonMain/kotlin/net.mamoe.mirai/network/protocol/tim/packet/action/Profile.kt @@ -61,23 +61,23 @@ object RequestProfileDetailsPacket : SessionPacketFactory Gender.SECRET //error("Cannot determine gender, entry 0x4E29u not found") 0x02u -> Gender.FEMALE 0x01u -> Gender.MALE else -> Gender.SECRET // 猜的 //else -> error("Cannot determine gender, bad value of 0x4E29u: ${map[0x4729u]!![0].toUHexString()}") }, - _birthday = map[0x4E3Fu]?.let { Date(it.toUInt().toInt()) }, - _personalStatus = map[0x4E33u]?.stringOfWitch(), - _homepage = map[0x4E2Du]?.stringOfWitch(), - _company = map[0x5DC8u]?.stringOfWitch(), - _school = map[0x4E35u]?.stringOfWitch(), - _email = map[0x4E2Bu]?.stringOfWitch() + birthday = map[0x4E3Fu]?.let { Date(it.toUInt().toInt()) }, + personalStatus = map[0x4E33u]?.stringOfWitch(), + homepage = map[0x4E2Du]?.stringOfWitch(), + company = map[0x5DC8u]?.stringOfWitch(), + school = map[0x4E35u]?.stringOfWitch(), + email = map[0x4E2Bu]?.stringOfWitch() ) map.clear() diff --git a/mirai-core/src/commonMain/kotlin/net.mamoe.mirai/network/protocol/tim/packet/action/SendFriendMessagePacket.kt b/mirai-core/src/commonMain/kotlin/net.mamoe.mirai/network/protocol/tim/packet/action/SendFriendMessagePacket.kt index fbd51deb1..aa8606e3e 100644 --- a/mirai-core/src/commonMain/kotlin/net.mamoe.mirai/network/protocol/tim/packet/action/SendFriendMessagePacket.kt +++ b/mirai-core/src/commonMain/kotlin/net.mamoe.mirai/network/protocol/tim/packet/action/SendFriendMessagePacket.kt @@ -52,7 +52,7 @@ object SendFriendMessagePacket : SessionPacketFactory CoroutineScope.SuspendLazy(initializer: suspend () -> R): Lazy(scope: CoroutineScope, val initializer: suspend () -> R) : Lazy> { +internal class SuspendLazy(scope: CoroutineScope, initializer: suspend () -> R) : Lazy> { private val valueUpdater: Deferred by lazy { scope.async { initializer() } } @Suppress("EXPERIMENTAL_API_USAGE") diff --git a/mirai-core/src/commonMain/kotlin/net.mamoe.mirai/utils/internal/NumberUtils.kt b/mirai-core/src/commonMain/kotlin/net.mamoe.mirai/utils/internal/NumberUtils.kt index 204be1787..a949439e6 100644 --- a/mirai-core/src/commonMain/kotlin/net.mamoe.mirai/utils/internal/NumberUtils.kt +++ b/mirai-core/src/commonMain/kotlin/net.mamoe.mirai/utils/internal/NumberUtils.kt @@ -1,30 +1,39 @@ package net.mamoe.mirai.utils.internal +/** + * 要求 [this] 最小为 [min]. + */ @PublishedApi -internal fun Int.coerceAtLeastOrFail(value: Int): Int { - require(this > value) +internal fun Int.coerceAtLeastOrFail(min: Int): Int { + require(this >= min) return this } +/** + * 要求 [this] 最小为 [min]. + */ @PublishedApi -internal fun Long.coerceAtLeastOrFail(value: Long): Long { - require(this > value) +internal fun Long.coerceAtLeastOrFail(min: Long): Long { + require(this >= min) return this } +/** + * 要求 [this] 最大为 [max]. + */ @PublishedApi -internal fun Int.coerceAtMostOrFail(maximumValue: Int): Int = - if (this > maximumValue) error("value is greater than its expected maximum value $maximumValue") +internal fun Int.coerceAtMostOrFail(max: Int): Int = + if (this >= max) error("value is greater than its expected maximum value $max") else this @PublishedApi -internal fun Long.coerceAtMostOrFail(maximumValue: Long): Long = - if (this > maximumValue) error("value is greater than its expected maximum value $maximumValue") +internal fun Long.coerceAtMostOrFail(max: Long): Long = + if (this >= max) error("value is greater than its expected maximum value $max") else this /** - * 表示这个参数必须为正数 + * 表示这个参数必须为正数. 仅用于警示 */ @Retention(AnnotationRetention.SOURCE) -@Target(AnnotationTarget.VALUE_PARAMETER) +@Target(AnnotationTarget.VALUE_PARAMETER, AnnotationTarget.TYPE) internal annotation class PositiveNumbers \ No newline at end of file