mirror of
https://github.com/mamoe/mirai.git
synced 2025-01-09 09:50:16 +08:00
Make Profile data class
This commit is contained in:
parent
cc2343e220
commit
c655c0fe33
@ -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)
|
||||
}
|
||||
|
||||
|
@ -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<C : Contact> : MutableMap<UInt, C> 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 <R> 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 <R> 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<Profile> 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<Profile>
|
||||
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<RequestProfileDetailsResponse, Profile> { it.profile }
|
||||
.await().let {
|
||||
@Suppress("UNCHECKED_CAST")
|
||||
if ((::profile as SuspendLazy<Profile>).isInitialized()) {
|
||||
profile.await().apply { copyFrom(it) }
|
||||
} else it
|
||||
}
|
||||
_profile = RequestProfileDetailsPacket(bot.qqAccount, id, sessionKey)
|
||||
.sendAndExpect<RequestProfileDetailsResponse, Profile> { 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?
|
||||
)
|
||||
|
||||
/**
|
||||
* 性别
|
||||
|
@ -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()
|
||||
}
|
@ -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
|
||||
|
@ -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 <reified P : Packet, R> OutgoingPacket.sendAndExpect(checkSequence: Boolean = true, crossinline block: (P) -> R): R =
|
||||
sendAndExpectAsync<P, R>(checkSequence) { block(it) }.await()
|
||||
|
||||
suspend inline fun <reified P : Packet> OutgoingPacket.sendAndExpect(checkSequence: Boolean = true): P =
|
||||
sendAndExpectAsync<P, P>(checkSequence) { it }.await()
|
||||
|
||||
|
@ -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<TIMBotNetworkHandler.BotSocketAdapter>, 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()
|
||||
|
||||
|
@ -29,7 +29,7 @@ class TemporaryPacketHandler<P : Packet, R>(
|
||||
private val fromSession: BotSession,
|
||||
private val checkSequence: Boolean,
|
||||
/**
|
||||
* 调用者的 [CoroutineContext]
|
||||
* 调用者的 [CoroutineContext]. 包处理过程将会在这个 context 下运行
|
||||
*/
|
||||
private val callerContext: CoroutineContext
|
||||
) {
|
||||
|
@ -61,23 +61,23 @@ object RequestProfileDetailsPacket : SessionPacketFactory<RequestProfileDetailsR
|
||||
discardExact(6)
|
||||
val map = readTLVMap(tagSize = 2, expectingEOF = true)
|
||||
val profile = Profile(
|
||||
_qq = qq,
|
||||
_nickname = map[0x4E22u]?.stringOfWitch() ?: "",//error("Cannot determine nickname")
|
||||
_zipCode = map[0x4E25u]?.stringOfWitch(),
|
||||
_phone = map[0x4E27u]?.stringOfWitch(),
|
||||
_gender = when (map[0x4E29u]?.let { it[0] }?.toUInt()) {
|
||||
qq = qq,
|
||||
nickname = map[0x4E22u]?.stringOfWitch() ?: "",//error("Cannot determine nickname")
|
||||
zipCode = map[0x4E25u]?.stringOfWitch(),
|
||||
phone = map[0x4E27u]?.stringOfWitch(),
|
||||
gender = when (map[0x4E29u]?.let { it[0] }?.toUInt()) {
|
||||
null -> 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()
|
||||
|
||||
|
@ -52,7 +52,7 @@ object SendFriendMessagePacket : SessionPacketFactory<SendFriendMessagePacket.Re
|
||||
writeHex(TIMProtocol.messageConstNewest)
|
||||
writeZero(2)
|
||||
|
||||
writePacket(message.toPacket(false))
|
||||
writePacket(message.toPacket())
|
||||
|
||||
/*
|
||||
//Plain text
|
||||
|
@ -35,7 +35,7 @@ object SendGroupMessagePacket : SessionPacketFactory<SendGroupMessagePacket.Resp
|
||||
writeHex(TIMProtocol.messageConst1)
|
||||
writeZero(2)
|
||||
|
||||
writePacket(message.toPacket(true))
|
||||
writePacket(message.toPacket())
|
||||
}
|
||||
/*it.writeByte(0x01)
|
||||
it.writeShort(bytes.size + 3)
|
||||
|
@ -27,7 +27,7 @@ fun <R> CoroutineScope.SuspendLazy(initializer: suspend () -> R): Lazy<Deferred<
|
||||
* @sample QQ.profile
|
||||
*/
|
||||
@PublishedApi
|
||||
internal class SuspendLazy<R>(scope: CoroutineScope, val initializer: suspend () -> R) : Lazy<Deferred<R>> {
|
||||
internal class SuspendLazy<R>(scope: CoroutineScope, initializer: suspend () -> R) : Lazy<Deferred<R>> {
|
||||
private val valueUpdater: Deferred<R> by lazy { scope.async { initializer() } }
|
||||
|
||||
@Suppress("EXPERIMENTAL_API_USAGE")
|
||||
|
@ -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
|
Loading…
Reference in New Issue
Block a user