Commit 146615e6 authored by Him188's avatar Him188

Change Bot to interface, implement Bot as BotImpl, fix #20

parent f3cbb6e4
...@@ -20,7 +20,6 @@ import net.mamoe.mirai.Bot ...@@ -20,7 +20,6 @@ import net.mamoe.mirai.Bot
import net.mamoe.mirai.addFriend import net.mamoe.mirai.addFriend
import net.mamoe.mirai.contact.sendMessage import net.mamoe.mirai.contact.sendMessage
import net.mamoe.mirai.getGroup import net.mamoe.mirai.getGroup
import net.mamoe.mirai.getQQ
import net.mamoe.mirai.utils.io.hexToBytes import net.mamoe.mirai.utils.io.hexToBytes
import net.mamoe.mirai.utils.io.hexToUBytes import net.mamoe.mirai.utils.io.hexToUBytes
...@@ -81,6 +80,7 @@ suspend inline fun ApplicationCall.ok() = this.respond(HttpStatusCode.OK, "OK") ...@@ -81,6 +80,7 @@ suspend inline fun ApplicationCall.ok() = this.respond(HttpStatusCode.OK, "OK")
/** /**
* 错误请求. 抛出这个异常后将会返回错误给一个请求 * 错误请求. 抛出这个异常后将会返回错误给一个请求
*/ */
@Suppress("unused")
open class IllegalAccessException : Exception { open class IllegalAccessException : Exception {
override val message: String get() = super.message!! override val message: String get() = super.message!!
......
@file:Suppress("EXPERIMENTAL_API_USAGE", "unused") @file:Suppress("EXPERIMENTAL_API_USAGE", "unused", "FunctionName", "NOTHING_TO_INLINE")
package net.mamoe.mirai package net.mamoe.mirai
import kotlinx.coroutines.* import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.sync.Mutex import kotlinx.coroutines.Deferred
import kotlinx.coroutines.sync.withLock import kotlinx.coroutines.Job
import net.mamoe.mirai.Bot.ContactSystem
import net.mamoe.mirai.contact.* import net.mamoe.mirai.contact.*
import net.mamoe.mirai.contact.internal.Group
import net.mamoe.mirai.contact.internal.QQ
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.packet.KnownPacketId
import net.mamoe.mirai.network.protocol.tim.packet.action.GroupNotFound
import net.mamoe.mirai.network.protocol.tim.packet.action.GroupPacket
import net.mamoe.mirai.network.protocol.tim.packet.action.RawGroupInfo
import net.mamoe.mirai.network.protocol.tim.packet.login.LoginResult import net.mamoe.mirai.network.protocol.tim.packet.login.LoginResult
import net.mamoe.mirai.network.protocol.tim.packet.login.isSuccess import net.mamoe.mirai.utils.BotConfiguration
import net.mamoe.mirai.network.qqAccount import net.mamoe.mirai.utils.GroupNotFoundException
import net.mamoe.mirai.utils.* import net.mamoe.mirai.utils.MiraiLogger
import net.mamoe.mirai.utils.internal.PositiveNumbers
import net.mamoe.mirai.utils.internal.coerceAtLeastOrFail
import net.mamoe.mirai.utils.io.inline
import net.mamoe.mirai.utils.io.logStacktrace
import kotlin.coroutines.CoroutineContext import kotlin.coroutines.CoroutineContext
import kotlin.coroutines.coroutineContext import kotlin.coroutines.coroutineContext
import kotlin.jvm.JvmOverloads
import kotlin.jvm.JvmSynthetic import kotlin.jvm.JvmSynthetic
data class BotAccount(
val id: UInt,
val password: String
) {
constructor(id: Long, password: String) : this(id.toUInt(), password)
}
// These pseudo 'constructors' are suspend, therefore they should not be inside the object
@Suppress("FunctionName")
suspend inline fun Bot(account: BotAccount, logger: MiraiLogger): Bot = Bot(account, logger, coroutineContext)
@Suppress("FunctionName")
suspend inline fun Bot(account: BotAccount): Bot = Bot(account, coroutineContext)
@JvmSynthetic
@Suppress("FunctionName")
suspend inline fun Bot(qq: UInt, password: String): Bot = Bot(BotAccount(qq, password), coroutineContext)
@Suppress("FunctionName")
suspend inline fun Bot(qq: Long, password: String): Bot = Bot(BotAccount(qq.toUInt(), password), coroutineContext)
/** /**
* Mirai 的机器人. 一个机器人实例登录一个 QQ 账号. * Mirai 的机器人. 一个机器人实例登录一个 QQ 账号.
* Mirai 为多账号设计, 可同时维护多个机器人. * Mirai 为多账号设计, 可同时维护多个机器人.
* *
* [Bot] 由 3 个模块组成.
* [联系人管理][ContactSystem]: 可通过 [Bot.contacts] 访问
* [网络处理器][TIMBotNetworkHandler]: 可通过 [Bot.network] 访问
* [机器人账号信息][BotAccount]: 可通过 [Bot.qqAccount] 访问
*
* 若需要得到机器人的 QQ 账号, 请访问 [Bot.qqAccount]
* 若需要得到服务器上所有机器人列表, 请访问 [Bot.instances]
*
* 在 BotHelper.kt 中有一些访问的捷径. 如 [Bot.getGroup]
*
*
*
* Bot that is the base of the whole program.
* It consists of
* a [ContactSystem], which manage contacts such as [QQ] and [Group];
* a [TIMBotNetworkHandler], which manages the connection to the server;
* a [BotAccount], which stores the account information(e.g. qq number the bot)
*
* To of all the QQ contacts, access [Bot.qqAccount]
* To of all the Robot instance, access [Bot.instances]
*
*
* @author Him188moe
* @author NaturalHG
* @see Contact * @see Contact
*/ */
class Bot(val account: BotAccount, val logger: MiraiLogger, context: CoroutineContext) : CoroutineScope { interface Bot : CoroutineScope {
private val supervisorJob = SupervisorJob(context[Job]) companion object {
override val coroutineContext: CoroutineContext = suspend inline operator fun invoke(account: BotAccount, logger: MiraiLogger): Bot = BotImpl(account, logger, coroutineContext)
context + supervisorJob + CoroutineExceptionHandler { _, e -> e.logStacktrace("An exception was thrown under a coroutine of Bot") } suspend inline operator fun invoke(account: BotAccount): Bot = BotImpl(account, context = coroutineContext)
@JvmSynthetic
suspend inline operator fun invoke(qq: UInt, password: String): Bot = BotImpl(BotAccount(qq, password), context = coroutineContext)
constructor(qq: Long, password: String, context: CoroutineContext) : this(BotAccount(qq, password), context) suspend inline operator fun invoke(qq: Long, password: String): Bot = BotImpl(BotAccount(qq.toUInt(), password), context = coroutineContext)
constructor(qq: UInt, password: String, context: CoroutineContext) : this(BotAccount(qq, password), context) operator fun invoke(qq: Long, password: String, context: CoroutineContext): Bot = BotImpl(BotAccount(qq, password), context = context)
constructor(account: BotAccount, context: CoroutineContext) : this(account, DefaultLogger("Bot(" + account.id + ")"), context) @JvmSynthetic
operator fun invoke(qq: UInt, password: String, context: CoroutineContext): Bot = BotImpl(BotAccount(qq, password), context = context)
val contacts = ContactSystem() operator fun invoke(account: BotAccount, context: CoroutineContext): Bot = BotImpl(account, context = context)
val network: BotNetworkHandler<*> get() = _network
private lateinit var _network: BotNetworkHandler<*> inline fun forEachInstance(block: (Bot) -> Unit) = BotImpl.forEachInstance(block)
init { fun instanceWhose(qq: UInt): Bot = BotImpl.instanceWhose(qq = qq)
launch {
addInstance(this@Bot)
}
} }
override fun toString(): String = "Bot(${account.id})" /**
* 账号信息
*/
val account: BotAccount
/**
* 日志记录器
*/
val logger: MiraiLogger
override val coroutineContext: CoroutineContext
/**
* 网络模块
*/
val network: BotNetworkHandler<*>
/** /**
* [关闭][BotNetworkHandler.close]网络处理器, 取消所有运行在 [BotNetworkHandler] 下的协程. * [关闭][BotNetworkHandler.close]网络处理器, 取消所有运行在 [BotNetworkHandler] 下的协程.
* 然后重新启动并尝试登录 * 然后重新启动并尝试登录
*/ */
@JvmOverloads // shouldn't be suspend!! This function MUST NOT inherit the context from the caller because the caller(NetworkHandler) is going to close
fun tryReinitializeNetworkHandler( fun tryReinitializeNetworkHandler(
configuration: BotConfiguration, configuration: BotConfiguration,
cause: Throwable? = null cause: Throwable? = null
): Job = launch { ): Job
repeat(configuration.reconnectionRetryTimes) {
if (reinitializeNetworkHandlerAsync(configuration, cause).await().isSuccess()) {
logger.info("Reconnected successfully")
return@launch
} else {
delay(configuration.reconnectPeriodMillis)
}
}
}
/** /**
* [关闭][BotNetworkHandler.close]网络处理器, 取消所有运行在 [BotNetworkHandler] 下的协程. * [关闭][BotNetworkHandler.close]网络处理器, 取消所有运行在 [BotNetworkHandler] 下的协程.
* 然后重新启动并尝试登录 * 然后重新启动并尝试登录
*/ */
@JvmOverloads
suspend fun reinitializeNetworkHandler( suspend fun reinitializeNetworkHandler(
configuration: BotConfiguration, configuration: BotConfiguration,
cause: Throwable? = null cause: Throwable? = null
): LoginResult { ): LoginResult
logger.info("BotAccount: ${qqAccount.toLong()}")
logger.info("Initializing BotNetworkHandler")
try {
if (::_network.isInitialized) {
_network.close(cause)
}
} catch (e: Exception) {
logger.error("Cannot close network handler", e)
}
_network = TIMBotNetworkHandler(this@Bot.coroutineContext + configuration, this@Bot)
return _network.login()
}
/** /**
* [关闭][BotNetworkHandler.close]网络处理器, 取消所有运行在 [BotNetworkHandler] 下的协程. * [关闭][BotNetworkHandler.close]网络处理器, 取消所有运行在 [BotNetworkHandler] 下的协程.
* 然后重新启动并尝试登录 * 然后重新启动并尝试登录
*/ */
@JvmOverloads
fun reinitializeNetworkHandlerAsync( fun reinitializeNetworkHandlerAsync(
configuration: BotConfiguration, configuration: BotConfiguration,
cause: Throwable? = null cause: Throwable? = null
): Deferred<LoginResult> = async { reinitializeNetworkHandler(configuration, cause) } ): Deferred<LoginResult>
/** /**
* Bot 联系人管理. * 与这个机器人相关的 QQ 列表. 机器人与 QQ 不一定是好友
*
* @see Bot.contacts
*/ */
inner class ContactSystem internal constructor() { val qqs: ContactList<QQ>
val bot: Bot get() = this@Bot
@UseExperimental(MiraiInternalAPI::class)
val groups: ContactList<Group> = ContactList(MutableContactList())
@UseExperimental(MiraiInternalAPI::class)
val qqs: ContactList<QQ> = ContactList(MutableContactList())
/**
* 线程安全地获取缓存的 QQ 对象. 若没有对应的缓存, 则会创建一个.
*/
@UseExperimental(MiraiInternalAPI::class)
@JvmSynthetic
fun getQQ(id: UInt): QQ = qqs.delegate.getOrAdd(id) { QQ(bot, id, coroutineContext) }
/**
* 线程安全地获取缓存的 QQ 对象. 若没有对应的缓存, 则会创建一个.
*/
// NO INLINE!! to help Java
@UseExperimental(MiraiInternalAPI::class)
fun getQQ(@PositiveNumbers id: Long): QQ = getQQ(id.coerceAtLeastOrFail(0).toUInt())
/**
* 线程安全地获取缓存的群对象. 若没有对应的缓存, 则会创建一个.
*/
suspend fun getGroup(internalId: GroupInternalId): Group = getGroup(internalId.toId())
/** /**
* 线程安全地获取缓存的群对象. 若没有对应的缓存, 则会创建一个. * 获取缓存的 QQ 对象. 若没有对应的缓存, 则会线程安全地创建一个.
*/ */
@UseExperimental(MiraiInternalAPI::class, ExperimentalUnsignedTypes::class) fun getQQ(id: UInt): QQ
suspend fun getGroup(id: GroupId): Group = groups.delegate.getOrNull(id.value) ?: inline {
val info: RawGroupInfo = try {
when (val response =
bot.withSession { GroupPacket.QueryGroupInfo(qqAccount, id.toInternalId(), sessionKey).sendAndExpect<GroupPacket.InfoResponse>() }) {
is RawGroupInfo -> response
is GroupNotFound -> throw GroupNotFoundException("id=${id.value.toLong()}")
else -> assertUnreachable()
}
} catch (e: Exception) {
throw IllegalStateException("Cannot obtain group info for id ${id.value.toLong()}", e)
}
return groups.delegate.getOrAdd(id.value) { Group(bot, id, info, coroutineContext) }
}
/**
* 线程安全地获取缓存的群对象. 若没有对应的缓存, 则会创建一个.
*/
// NO INLINE!! to help Java
@UseExperimental(MiraiInternalAPI::class)
suspend fun getGroup(@PositiveNumbers id: Long): Group = id.coerceAtLeastOrFail(0).toUInt().let {
groups.delegate.getOrNull(it) ?: inline {
val info: RawGroupInfo = try {
bot.withSession { GroupPacket.QueryGroupInfo(qqAccount, GroupId(it).toInternalId(), sessionKey).sendAndExpect() }
} catch (e: Exception) {
e.logStacktrace()
error("Cannot obtain group info for id ${it.toLong()}")
}
return groups.delegate.getOrAdd(it) { Group(bot, GroupId(it), info, coroutineContext) }
}
}
}
@UseExperimental(MiraiInternalAPI::class) /**
fun close() { * 获取缓存的 QQ 对象. 若没有对应的缓存, 则会线程安全地创建一个.
_network.close() */
this.coroutineContext.cancelChildren() fun getQQ(id: Long): QQ
contacts.groups.delegate.clear()
contacts.qqs.delegate.clear()
}
companion object { /**
@Suppress("ObjectPropertyName") * 与这个机器人相关的群列表. 机器人不一定是群成员.
private val _instances: MutableList<Bot> = mutableListOf() */
private val instanceLock: Mutex = Mutex() val groups: ContactList<Group>
private val instances: List<Bot> get() = _instances /**
* 获取缓存的群对象. 若没有对应的缓存, 则会线程安全地创建一个.
* 若 [id] 无效, 将会抛出 [GroupNotFoundException]
*/
suspend fun getGroup(id: GroupId): Group
suspend fun instanceWhose(qq: UInt) = instanceLock.withLock { /**
instances.first { it.qqAccount == qq } * 获取缓存的群对象. 若没有对应的缓存, 则会线程安全地创建一个.
} * 若 [internalId] 无效, 将会抛出 [GroupNotFoundException]
*/
suspend fun getGroup(internalId: GroupInternalId): Group
internal suspend fun addInstance(bot: Bot) = instanceLock.withLock { /**
_instances += bot * 获取缓存的群对象. 若没有对应的缓存, 则会线程安全地创建一个.
} * 若 [id] 无效, 将会抛出 [GroupNotFoundException]
*/
suspend fun getGroup(id: Long): Group
init { fun close()
KnownPacketId.values() // load packet ids
}
}
} }
\ No newline at end of file
@file:Suppress("EXPERIMENTAL_API_USAGE")
package net.mamoe.mirai
data class BotAccount(
val id: UInt,
val password: String
) {
constructor(id: Long, password: String) : this(id.toUInt(), password)
}
\ No newline at end of file
...@@ -68,15 +68,15 @@ Mirai 22:04:48 : Packet received: UnknownEventPacket(id=00 D6, identity=(2092749 ...@@ -68,15 +68,15 @@ Mirai 22:04:48 : Packet received: UnknownEventPacket(id=00 D6, identity=(2092749
* @param remark 好友备注 * @param remark 好友备注
*/ */
@UseExperimental(ExperimentalContracts::class) @UseExperimental(ExperimentalContracts::class)
suspend fun Bot.ContactSystem.addFriend(id: UInt, message: String? = null, remark: String? = null): AddFriendResult = bot.withSession { suspend fun Bot.addFriend(id: UInt, message: String? = null, remark: String? = null): AddFriendResult = withSession {
return when (CanAddFriendPacket(bot.qqAccount, id, bot.sessionKey).sendAndExpect<CanAddFriendResponse>()) { return when (CanAddFriendPacket(qqAccount, id, sessionKey).sendAndExpect<CanAddFriendResponse>()) {
is CanAddFriendResponse.AlreadyAdded -> AddFriendResult.ALREADY_ADDED is CanAddFriendResponse.AlreadyAdded -> AddFriendResult.ALREADY_ADDED
is CanAddFriendResponse.Rejected -> AddFriendResult.REJECTED is CanAddFriendResponse.Rejected -> AddFriendResult.REJECTED
is CanAddFriendResponse.ReadyToAdd, is CanAddFriendResponse.ReadyToAdd,
is CanAddFriendResponse.RequireVerification -> { is CanAddFriendResponse.RequireVerification -> {
val key = RequestFriendAdditionKeyPacket(bot.qqAccount, id, sessionKey).sendAndExpect<RequestFriendAdditionKeyPacket.Response>().key val key = RequestFriendAdditionKeyPacket(qqAccount, id, sessionKey).sendAndExpect<RequestFriendAdditionKeyPacket.Response>().key
AddFriendPacket.RequestAdd(bot.qqAccount, id, sessionKey, message, remark, key).sendAndExpect<AddFriendPacket.Response>() AddFriendPacket.RequestAdd(qqAccount, id, sessionKey, message, remark, key).sendAndExpect<AddFriendPacket.Response>()
AddFriendResult.WAITING_FOR_APPROVE AddFriendResult.WAITING_FOR_APPROVE
} //这个做的是需要验证消息的情况, 不确定 ReadyToAdd 的是啥 } //这个做的是需要验证消息的情况, 不确定 ReadyToAdd 的是啥
...@@ -87,7 +87,7 @@ suspend fun Bot.ContactSystem.addFriend(id: UInt, message: String? = null, remar ...@@ -87,7 +87,7 @@ suspend fun Bot.ContactSystem.addFriend(id: UInt, message: String? = null, remar
/*is CanAddFriendResponse.ReadyToAdd -> { /*is CanAddFriendResponse.ReadyToAdd -> {
// TODO: 2019/11/11 这不需要验证信息的情况 // TODO: 2019/11/11 这不需要验证信息的情况
//AddFriendPacket(bot.qqAccount, id, bot.sessionKey, ).sendAndExpectAsync<AddFriendPacket.Response>().await() //AddFriendPacket(qqAccount, id, sessionKey, ).sendAndExpectAsync<AddFriendPacket.Response>().await()
TODO() TODO()
}*/ }*/
} }
......
...@@ -26,25 +26,8 @@ import kotlin.jvm.JvmOverloads ...@@ -26,25 +26,8 @@ import kotlin.jvm.JvmOverloads
*/ */
//Contacts //Contacts
inline fun Bot.getQQ(@PositiveNumbers number: Long): QQ = this.contacts.getQQ(number) suspend inline fun Bot.getGroup(id: UInt): Group = this.getGroup(GroupId(id))
inline fun Bot.getQQ(number: UInt): QQ = this.contacts.getQQ(number)
suspend inline fun Bot.getGroup(id: UInt): Group = this.contacts.getGroup(GroupId(id))
suspend inline fun Bot.getGroup(@PositiveNumbers id: Long): Group =
this.contacts.getGroup(GroupId(id.coerceAtLeastOrFail(0).toUInt()))
suspend inline fun Bot.getGroup(id: GroupId): Group = this.contacts.getGroup(id)
suspend inline fun Bot.getGroup(internalId: GroupInternalId): Group = this.contacts.getGroup(internalId)
/**
* 取得群列表
*/
inline val Bot.groups: ContactList<Group> get() = this.contacts.groups
/**
* 取得好友列表
*/
inline val Bot.qqs: ContactList<QQ> get() = this.contacts.qqs
/** /**
* 以 [BotSession] 作为接收器 (receiver) 并调用 [block], 返回 [block] 的返回值. * 以 [BotSession] 作为接收器 (receiver) 并调用 [block], 返回 [block] 的返回值.
...@@ -107,14 +90,6 @@ suspend inline fun Bot.alsoLogin(message: String): Bot { ...@@ -107,14 +90,6 @@ suspend inline fun Bot.alsoLogin(message: String): Bot {
} }
} }
/**
* 添加好友
*/
@UseExperimental(ExperimentalContracts::class)
@JvmOverloads
suspend inline fun Bot.addFriend(id: UInt, message: String? = null, remark: String? = null): AddFriendResult =
contacts.addFriend(id, message, remark)
/** /**
* 取得机器人的 QQ 号 * 取得机器人的 QQ 号
*/ */
......
@file:Suppress("EXPERIMENTAL_API_USAGE")
package net.mamoe.mirai
import kotlinx.coroutines.*
import net.mamoe.mirai.contact.*
import net.mamoe.mirai.contact.internal.Group
import net.mamoe.mirai.contact.internal.QQ
import net.mamoe.mirai.network.BotNetworkHandler
import net.mamoe.mirai.network.protocol.tim.TIMBotNetworkHandler
import net.mamoe.mirai.network.protocol.tim.packet.KnownPacketId
import net.mamoe.mirai.network.protocol.tim.packet.action.GroupNotFound
import net.mamoe.mirai.network.protocol.tim.packet.action.GroupPacket
import net.mamoe.mirai.network.protocol.tim.packet.action.RawGroupInfo
import net.mamoe.mirai.network.protocol.tim.packet.login.LoginResult
import net.mamoe.mirai.network.protocol.tim.packet.login.isSuccess
import net.mamoe.mirai.network.qqAccount
import net.mamoe.mirai.utils.*
import net.mamoe.mirai.utils.internal.PositiveNumbers
import net.mamoe.mirai.utils.internal.coerceAtLeastOrFail
import net.mamoe.mirai.utils.io.inline
import net.mamoe.mirai.utils.io.logStacktrace
import kotlin.coroutines.CoroutineContext
import kotlin.jvm.JvmSynthetic
@PublishedApi
internal class BotImpl @PublishedApi internal constructor(
override val account: BotAccount,
override val logger: MiraiLogger = DefaultLogger("Bot(" + account.id + ")"),
context: CoroutineContext
) : Bot, CoroutineScope {
private val supervisorJob = SupervisorJob(context[Job])
override val coroutineContext: CoroutineContext =
context + supervisorJob + CoroutineExceptionHandler { _, e -> e.logStacktrace("An exception was thrown under a coroutine of Bot") }
init {
launch {
instances.addLast(this@BotImpl)
}
}
companion object {
init {
KnownPacketId.values() /* load id classes */
}
@PublishedApi
internal val instances: LockFreeLinkedList<Bot> = LockFreeLinkedList()
inline fun forEachInstance(block: (Bot) -> Unit) = instances.forEach(block)
fun instanceWhose(qq: UInt): Bot {
instances.forEach {
if (it.qqAccount == qq) {
return it
}
}
throw NoSuchElementException()
}
}
override fun toString(): String = "Bot(${account.id})"
// region network
override val network: BotNetworkHandler<*> get() = _network
private lateinit var _network: BotNetworkHandler<*>
override fun tryReinitializeNetworkHandler(// shouldn't be suspend!! This function MUST NOT inherit the context from the caller because the caller(NetworkHandler) is going to close
configuration: BotConfiguration,
cause: Throwable?
): Job = launch {
repeat(configuration.reconnectionRetryTimes) {
if (reinitializeNetworkHandlerAsync(configuration, cause).await().isSuccess()) {
logger.info("Reconnected successfully")
return@launch
} else {
delay(configuration.reconnectPeriodMillis)
}
}
}
override suspend fun reinitializeNetworkHandler(
configuration: BotConfiguration,
cause: Throwable?
): LoginResult {
logger.info("BotAccount: ${qqAccount.toLong()}")
logger.info("Initializing BotNetworkHandler")
try {
if (::_network.isInitialized) {
_network.close(cause)
}
} catch (e: Exception) {
logger.error("Cannot close network handler", e)
}
_network = TIMBotNetworkHandler(this.coroutineContext + configuration, this)
return _network.login()
}
override fun reinitializeNetworkHandlerAsync(
configuration: BotConfiguration,
cause: Throwable?
): Deferred<LoginResult> = async { reinitializeNetworkHandler(configuration, cause) }
// endregion
// region contacts
@UseExperimental(MiraiInternalAPI::class)
override val groups: ContactList<Group> = ContactList(MutableContactList())
@UseExperimental(MiraiInternalAPI::class)
override val qqs: ContactList<QQ> = ContactList(MutableContactList())
/**
* 线程安全地获取缓存的 QQ 对象. 若没有对应的缓存, 则会创建一个.
*/
@UseExperimental(MiraiInternalAPI::class)
@JvmSynthetic
override fun getQQ(id: UInt): QQ = qqs.delegate.getOrAdd(id) { QQ(this, id, coroutineContext) }
// NO INLINE!! to help Java
@UseExperimental(MiraiInternalAPI::class)
override fun getQQ(@PositiveNumbers id: Long): QQ = getQQ(id.coerceAtLeastOrFail(0).toUInt())
override suspend fun getGroup(internalId: GroupInternalId): Group = getGroup(internalId.toId())
@UseExperimental(MiraiInternalAPI::class, ExperimentalUnsignedTypes::class)
override suspend fun getGroup(id: GroupId): Group = groups.delegate.getOrNull(id.value) ?: inline {
val info: RawGroupInfo = try {
when (val response =
withSession { GroupPacket.QueryGroupInfo(qqAccount, id.toInternalId(), sessionKey).sendAndExpect<GroupPacket.InfoResponse>() }) {
is RawGroupInfo -> response
is GroupNotFound -> throw GroupNotFoundException("id=${id.value.toLong()}")
else -> assertUnreachable()
}
} catch (e: Exception) {
throw IllegalStateException("Cannot obtain group info for id ${id.value.toLong()}", e)
}
return groups.delegate.getOrAdd(id.value) { Group(this, id, info, coroutineContext) }
}
// NO INLINE!! to help Java
@UseExperimental(MiraiInternalAPI::class)
override suspend fun getGroup(@PositiveNumbers id: Long): Group = id.coerceAtLeastOrFail(0).toUInt().let {
groups.delegate.getOrNull(it) ?: inline {
val info: RawGroupInfo = try {
withSession { GroupPacket.QueryGroupInfo(qqAccount, GroupId(it).toInternalId(), sessionKey).sendAndExpect() }
} catch (e: Exception) {
e.logStacktrace()
error("Cannot obtain group info for id ${it.toLong()}")
}
return groups.delegate.getOrAdd(it) { Group(this, GroupId(it), info, coroutineContext) }
}
}
// endregion
@UseExperimental(MiraiInternalAPI::class)
override fun close() {
_network.close()
this.supervisorJob.complete()
groups.delegate.clear()
qqs.delegate.clear()
}
}
...@@ -85,7 +85,7 @@ internal data class GroupImpl internal constructor(override val bot: Bot, val gr ...@@ -85,7 +85,7 @@ internal data class GroupImpl internal constructor(override val bot: Bot, val gr
} }
@Suppress("FunctionName", "NOTHING_TO_INLINE") @Suppress("FunctionName", "NOTHING_TO_INLINE")
inline fun CoroutineScope.QQ(bot: Bot, id: UInt, coroutineContext: CoroutineContext): QQ = QQImpl(bot, id, coroutineContext).apply { launch { startUpdater() } } internal inline fun CoroutineScope.QQ(bot: Bot, id: UInt, coroutineContext: CoroutineContext): QQ = QQImpl(bot, id, coroutineContext).apply { launch { startUpdater() } }
@PublishedApi @PublishedApi
internal data class QQImpl @PublishedApi internal constructor(override val bot: Bot, override val id: UInt, override val coroutineContext: CoroutineContext) : internal data class QQImpl @PublishedApi internal constructor(override val bot: Bot, override val id: UInt, override val coroutineContext: CoroutineContext) :
...@@ -115,7 +115,7 @@ internal data class QQImpl @PublishedApi internal constructor(override val bot: ...@@ -115,7 +115,7 @@ internal data class QQImpl @PublishedApi internal constructor(override val bot:
} }
@Suppress("FunctionName", "NOTHING_TO_INLINE") @Suppress("FunctionName", "NOTHING_TO_INLINE")
inline fun Group.Member(delegate: QQ, permission: MemberPermission, coroutineContext: CoroutineContext): Member = internal inline fun Group.Member(delegate: QQ, permission: MemberPermission, coroutineContext: CoroutineContext): Member =
MemberImpl(delegate, this, permission, coroutineContext).apply { launch { startUpdater() } } MemberImpl(delegate, this, permission, coroutineContext).apply { launch { startUpdater() } }
/** /**
......
...@@ -7,7 +7,6 @@ import kotlinx.io.core.discardExact ...@@ -7,7 +7,6 @@ import kotlinx.io.core.discardExact
import kotlinx.io.core.readUByte import kotlinx.io.core.readUByte
import kotlinx.io.core.readUInt import kotlinx.io.core.readUInt
import net.mamoe.mirai.contact.QQ import net.mamoe.mirai.contact.QQ
import net.mamoe.mirai.getQQ
import net.mamoe.mirai.network.BotNetworkHandler import net.mamoe.mirai.network.BotNetworkHandler
import net.mamoe.mirai.network.protocol.tim.packet.KnownPacketId import net.mamoe.mirai.network.protocol.tim.packet.KnownPacketId
import net.mamoe.mirai.network.protocol.tim.packet.PacketId import net.mamoe.mirai.network.protocol.tim.packet.PacketId
......
...@@ -13,7 +13,6 @@ import net.mamoe.mirai.contact.internal.MemberImpl ...@@ -13,7 +13,6 @@ import net.mamoe.mirai.contact.internal.MemberImpl
import net.mamoe.mirai.event.Subscribable import net.mamoe.mirai.event.Subscribable
import net.mamoe.mirai.event.broadcast import net.mamoe.mirai.event.broadcast
import net.mamoe.mirai.getGroup import net.mamoe.mirai.getGroup
import net.mamoe.mirai.getQQ
import net.mamoe.mirai.network.BotNetworkHandler import net.mamoe.mirai.network.BotNetworkHandler
import net.mamoe.mirai.utils.MiraiInternalAPI import net.mamoe.mirai.utils.MiraiInternalAPI
import net.mamoe.mirai.utils.io.discardExact import net.mamoe.mirai.utils.io.discardExact
......
...@@ -76,8 +76,7 @@ sealed class UnmuteEvent : EventOfMute() { ...@@ -76,8 +76,7 @@ sealed class UnmuteEvent : EventOfMute() {
// endregion // endregion
@Suppress("ClassName") internal object Unknown0x02DCPacketFlag0x0EMaybeMutePacket : EventOfMute() {
internal object `Unknown0x02DCPacket_falg=0x0E_MaybeMutePacket` : EventOfMute() {
override val operator: Member get() = error("Getting a field from Unknown0x02DCPacket_MaybeMutePacket") override val operator: Member get() = error("Getting a field from Unknown0x02DCPacket_MaybeMutePacket")
override val group: Group get() = error("Getting a field from Unknown0x02DCPacket_MaybeMutePacket") override val group: Group get() = error("Getting a field from Unknown0x02DCPacket_MaybeMutePacket")
override fun toString(): String = "`Unknown0x02DCPacket_falg=0x0E_MaybeMutePacket`" override fun toString(): String = "`Unknown0x02DCPacket_falg=0x0E_MaybeMutePacket`"
...@@ -124,7 +123,7 @@ internal object MemberMuteEventPacketParserAndHandler : KnownEventParserAndHandl ...@@ -124,7 +123,7 @@ internal object MemberMuteEventPacketParserAndHandler : KnownEventParserAndHandl
0x0Eu -> { 0x0Eu -> {
//00 00 00 0E 00 08 00 02 00 01 00 //00 00 00 0E 00 08 00 02 00 01 00
// 0A 00 04 01 00 00 00 35 DB 60 A2 11 00 3E 08 07 20 A2 C1 ED AE 03 5A 34 08 A2 FF 8C F0 03 1A 19 08 F4 0E 10 FE 8C D3 EF 05 18 84 A1 F8 F9 06 20 00 28 00 30 A2 FF 8C F0 03 2A 0D 08 00 12 09 08 F4 0E 10 00 18 01 20 00 30 00 38 00 // 0A 00 04 01 00 00 00 35 DB 60 A2 11 00 3E 08 07 20 A2 C1 ED AE 03 5A 34 08 A2 FF 8C F0 03 1A 19 08 F4 0E 10 FE 8C D3 EF 05 18 84 A1 F8 F9 06 20 00 28 00 30 A2 FF 8C F0 03 2A 0D 08 00 12 09 08 F4 0E 10 00 18 01 20 00 30 00 38 00
`Unknown0x02DCPacket_falg=0x0E_MaybeMutePacket` Unknown0x02DCPacketFlag0x0EMaybeMutePacket
} }
0x11u -> { 0x11u -> {
......
...@@ -11,7 +11,6 @@ import net.mamoe.mirai.contact.* ...@@ -11,7 +11,6 @@ import net.mamoe.mirai.contact.*
import net.mamoe.mirai.event.BroadcastControllable import net.mamoe.mirai.event.BroadcastControllable
import net.mamoe.mirai.event.events.BotEvent import net.mamoe.mirai.event.events.BotEvent
import net.mamoe.mirai.getGroup import net.mamoe.mirai.getGroup
import net.mamoe.mirai.getQQ
import net.mamoe.mirai.message.* import net.mamoe.mirai.message.*
import net.mamoe.mirai.message.internal.readMessageChain import net.mamoe.mirai.message.internal.readMessageChain
import net.mamoe.mirai.network.protocol.tim.packet.PacketVersion import net.mamoe.mirai.network.protocol.tim.packet.PacketVersion
......
...@@ -2,6 +2,8 @@ ...@@ -2,6 +2,8 @@
package net.mamoe.mirai.utils package net.mamoe.mirai.utils
import net.mamoe.mirai.contact.Group
/** /**
* 在获取 [Group] 对象等操作时可能出现的异常 * 在获取 [Group] 对象等操作时可能出现的异常
*/ */
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment