1
0
mirror of https://github.com/mamoe/mirai.git synced 2025-04-25 21:23:55 +08:00

Simplify Bot structure

This commit is contained in:
Him188 2020-04-09 16:14:48 +08:00
parent f28e50e0af
commit ec35eeba1c
8 changed files with 43 additions and 536 deletions
mirai-core-qqandroid/src/commonMain/kotlin/net/mamoe/mirai/qqandroid/contact
mirai-core/src
androidMain/kotlin/net/mamoe/mirai
commonMain/kotlin/net.mamoe.mirai
jvmMain/kotlin/net/mamoe/mirai

View File

@ -7,7 +7,7 @@
* https://github.com/mamoe/mirai/blob/master/LICENSE
*/
@file:Suppress("INAPPLICABLE_JVM_NAME")
@file:Suppress("INAPPLICABLE_JVM_NAME", "DEPRECATION_ERROR")
@file:OptIn(MiraiInternalAPI::class, LowLevelAPI::class)
package net.mamoe.mirai.qqandroid.contact

View File

@ -8,7 +8,7 @@
*/
@file:OptIn(MiraiInternalAPI::class, LowLevelAPI::class)
@file:Suppress("EXPERIMENTAL_API_USAGE")
@file:Suppress("EXPERIMENTAL_API_USAGE", "DEPRECATION_ERROR")
package net.mamoe.mirai.qqandroid.contact

View File

@ -28,6 +28,7 @@ import net.mamoe.mirai.qqandroid.network.protocol.packet.chat.receive.MessageSvc
import net.mamoe.mirai.utils.MiraiExperimentalAPI
import net.mamoe.mirai.utils.MiraiInternalAPI
@OptIn(MiraiInternalAPI::class)
internal suspend fun QQ.sendMessageImpl(message: Message): MessageReceipt<QQ> {
val event = MessageSendEvent.FriendMessageSendEvent(this, message.asMessageChain()).broadcast()
if (event.isCancelled) {

View File

@ -1,247 +0,0 @@
@file:Suppress("unused")
package net.mamoe.mirai
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.io.ByteReadChannel
import net.mamoe.mirai.contact.*
import net.mamoe.mirai.data.AddFriendResult
import net.mamoe.mirai.event.events.MemberJoinRequestEvent
import net.mamoe.mirai.event.events.NewFriendRequestEvent
import net.mamoe.mirai.message.MessageReceipt
import net.mamoe.mirai.message.data.Image
import net.mamoe.mirai.message.data.MessageChain
import net.mamoe.mirai.message.data.MessageSource
import net.mamoe.mirai.network.BotNetworkHandler
import net.mamoe.mirai.network.LoginFailedException
import net.mamoe.mirai.utils.*
/**
* 机器人对象. 一个机器人实例登录一个 QQ 账号.
* Mirai 为多账号设计, 可同时维护多个机器人.
*
* : Bot 为全协程实现, 没有其他任务时若不使用 [join], 主线程将会退出.
*
* @see Contact 联系人
* @see kotlinx.coroutines.isActive 判断 [Bot] 是否正常运行中. (在线, 且没有被 [close])
*/
@Suppress("INAPPLICABLE_JVM_NAME")
@OptIn(
MiraiInternalAPI::class, LowLevelAPI::class, MiraiExperimentalAPI::class, JavaFriendlyAPI::class
)
actual abstract class Bot actual constructor() : CoroutineScope, LowLevelBotAPIAccessor, BotJavaFriendlyAPI() {
actual companion object {
/**
* 复制一份此时的 [Bot] 实例列表.
*/
@JvmStatic
actual val instances: List<WeakRef<Bot>>
get() = BotImpl.instances.toList()
/**
* 遍历每一个 [Bot] 实例
*/
actual inline fun forEachInstance(block: (Bot) -> Unit) = BotImpl.forEachInstance(block)
/**
* 获取一个 [Bot] 实例, 找不到则 [NoSuchElementException]
*/
@JvmStatic
actual fun getInstance(qq: Long): Bot = BotImpl.getInstance(qq = qq)
}
/**
* [Bot] 运行的 [Context].
*
* JVM 的默认实现为 `class ContextImpl : Context`
* Android 实现为 [android.content.Context]
*/
actual abstract val context: Context
@PlannedRemoval("1.0.0")
@Deprecated("use id instead", replaceWith = ReplaceWith("id"))
actual abstract val uin: Long
/**
* QQ 号码. 实际类型为 uint
*/
@SinceMirai("0.32.0")
actual abstract val id: Long
/**
* 昵称
*/
actual abstract val nick: String
/**
* 日志记录器
*/
actual abstract val logger: MiraiLogger
// region contacts
actual abstract val selfQQ: QQ
/**
* 机器人的好友列表. 它将与服务器同步更新
*/
actual abstract val friends: ContactList<QQ>
/**
* 获取一个好友对象. 若没有这个好友, 则会抛出异常 [NoSuchElementException]
*/
actual fun getFriend(id: Long): QQ {
if (id == this.id) return selfQQ
return friends.delegate.getOrNull(id)
?: throw NoSuchElementException("No such friend $id for bot ${this.id}")
}
/**
* 机器人加入的群列表.
*/
actual abstract val groups: ContactList<Group>
/**
* 获取一个机器人加入的群.
*
* @throws NoSuchElementException 当不存在这个群时
*/
actual fun getGroup(id: Long): Group {
return groups.delegate.getOrNull(id)
?: throw NoSuchElementException("No such group $id for bot ${this.id}")
}
// endregion
// region network
/**
* 网络模块
*/
actual abstract val network: BotNetworkHandler
/**
* 挂起协程直到 [Bot] 下线.
*/
@JvmSynthetic
actual suspend inline fun join() = network.join()
/**
* 登录, 或重新登录.
* 这个函数总是关闭一切现有网路任务, 然后重新登录并重新缓存好友列表和群列表.
*
* 一般情况下不需要重新登录. Mirai 能够自动处理掉线情况.
*
* 最终调用 [net.mamoe.mirai.network.BotNetworkHandler.relogin]
*
* @throws LoginFailedException
*/
@JvmSynthetic
actual abstract suspend fun login()
// endregion
// region actions
/**
* 撤回这条消息. 可撤回自己 2 分钟内发出的消息, 和任意时间的群成员的消息.
*
* [Bot] 撤回自己的消息不需要权限.
* [Bot] 撤回群员的消息需要管理员权限.
*
* @param source 消息源. 可从 [MessageReceipt.source] 获得, 或从消息事件中的 [MessageChain] 获得.
*
* @throws PermissionDeniedException [Bot] 无权限操作时
*
* @see Bot.recall (扩展函数) 接受参数 [MessageChain]
* @see _lowLevelRecallFriendMessage 低级 API
* @see _lowLevelRecallGroupMessage 低级 API
*/
@JvmSynthetic
actual abstract suspend fun recall(source: MessageSource)
/**
* 获取图片下载链接
*/
@JvmSynthetic
actual abstract suspend fun queryImageUrl(image: Image): String
/**
* 获取图片下载链接并开始下载.
*
* @see ByteReadChannel.copyAndClose
* @see ByteReadChannel.copyTo
*/
@JvmSynthetic
actual abstract suspend fun openChannel(image: Image): ByteReadChannel
/**
* 添加一个好友
*
* @param message 若需要验证请求时的验证消息.
* @param remark 好友备注
*/
@JvmSynthetic
@MiraiExperimentalAPI("未支持")
actual abstract suspend fun addFriend(id: Long, message: String?, remark: String?): AddFriendResult
// endregion
/**
* 关闭这个 [Bot], 立即取消 [Bot] [kotlinx.coroutines.SupervisorJob].
* 之后 [kotlinx.coroutines.isActive] 将会返回 `false`.
*
* **注意:** 不可重新登录. 必须重新实例化一个 [Bot].
*
* @param cause 原因. null 时视为正常关闭, null 时视为异常关闭
*
* @see closeAndJoin 取消并 [Bot.join], 以确保 [Bot] 相关的活动被完全关闭
*/
actual abstract fun close(cause: Throwable?)
@OptIn(LowLevelAPI::class, MiraiExperimentalAPI::class)
actual final override fun toString(): String = "Bot($id)"
/**
* 通过好友验证
*
* @param event 好友验证的事件对象
*/
@JvmSynthetic
actual abstract suspend fun acceptNewFriendRequest(event: NewFriendRequestEvent)
/**
* 拒绝好友验证
*
* @param event 好友验证的事件对象
* @param blackList 拒绝后是否拉入黑名单
*/
@JvmSynthetic
actual abstract suspend fun rejectNewFriendRequest(event: NewFriendRequestEvent, blackList: Boolean)
/**
* 通过加群验证需管理员权限
*
* @param event 加群验证的事件对象
*/
@JvmSynthetic
actual abstract suspend fun acceptMemberJoinRequest(event: MemberJoinRequestEvent)
/**
* 拒绝加群验证需管理员权限
*
* @param event 加群验证的事件对象
* @param blackList 拒绝后是否拉入黑名单
*/
@JvmSynthetic
actual abstract suspend fun rejectMemberJoinRequest(event: MemberJoinRequestEvent, blackList: Boolean)
/**
* 忽略加群验证需管理员权限
*
* @param event 加群验证的事件对象
* @param blackList 忽略后是否拉入黑名单
*/
@JvmSynthetic
actual abstract suspend fun ignoreMemberJoinRequest(event: MemberJoinRequestEvent, blackList: Boolean)
}

View File

@ -48,24 +48,25 @@ suspend inline fun <B : Bot> B.alsoLogin(): B = also { login() }
*/
@Suppress("INAPPLICABLE_JVM_NAME")
@OptIn(MiraiInternalAPI::class, LowLevelAPI::class)
expect abstract class Bot() : CoroutineScope, LowLevelBotAPIAccessor {
abstract class Bot : CoroutineScope, LowLevelBotAPIAccessor, BotJavaFriendlyAPI() {
companion object {
/**
* 复制一份此时的 [Bot] 实例列表.
*/
@JvmStatic
val instances: List<WeakRef<Bot>>
get() = BotImpl.instances.toList()
/**
* 遍历每一个 [Bot] 实例
*/
inline fun forEachInstance(block: (Bot) -> Unit)
inline fun forEachInstance(block: (Bot) -> Unit) = BotImpl.forEachInstance(block)
/**
* 获取一个 [Bot] 实例, 找不到则 [NoSuchElementException]
*/
@JvmStatic
fun getInstance(qq: Long): Bot
fun getInstance(qq: Long): Bot = BotImpl.getInstance(qq = qq)
}
/**
@ -105,52 +106,39 @@ expect abstract class Bot() : CoroutineScope, LowLevelBotAPIAccessor {
abstract val selfQQ: QQ
/**
* 机器人的好友列表. 它将与服务器同步更新
* 机器人的好友列表. 与服务器同步更新
*/
abstract val friends: ContactList<QQ>
/**
* 获取一个好友对象. 若没有这个好友, 则会抛出异常 [NoSuchElementException]
* 获取一个好友对象.
* @throws [NoSuchElementException] 当不存在这个好友时抛出
*/
fun getFriend(id: Long): QQ
fun getFriend(id: Long): QQ = friends.firstOrNull { it.id == id } ?: throw NoSuchElementException("group $id")
/**
* 机器人加入的群列表.
* 机器人加入的群列表. 与服务器同步更新
*/
abstract val groups: ContactList<Group>
/**
* 获取一个机器人加入的群.
*
* @throws NoSuchElementException 当不存在这个群时
* @throws NoSuchElementException 当不存在这个群时抛出
*/
fun getGroup(id: Long): Group
fun getGroup(id: Long): Group = groups.firstOrNull { it.id == id } ?: throw NoSuchElementException("group $id")
// endregion
// region network
/**
* 网络模块
*/
abstract val network: BotNetworkHandler
/**
* 挂起协程直到 [Bot] 下线.
*/
@JvmSynthetic
suspend inline fun join()
/**
* 登录, 或重新登录.
* 这个函数总是关闭一切现有网路任务, 然后重新登录并重新缓存好友列表和群列表.
* 这个函数总是关闭一切现有网路任务 (但不会关闭其他任务), 然后重新登录并重新缓存好友列表和群列表.
*
* 一般情况下不需要重新登录. Mirai 能够自动处理掉线情况.
*
* 最终调用 [net.mamoe.mirai.network.BotNetworkHandler.relogin]
*
* @throws LoginFailedException
* @see alsoLogin
* @throws LoginFailedException 正常登录失败时抛出
* @see alsoLogin `.apply { login() }` 捷径
*/
@JvmSynthetic
abstract suspend fun login()
@ -264,9 +252,26 @@ expect abstract class Bot() : CoroutineScope, LowLevelBotAPIAccessor {
abstract fun close(cause: Throwable? = null)
@OptIn(LowLevelAPI::class, MiraiExperimentalAPI::class)
final override fun toString(): String
final override fun toString(): String = "Bot($id)"
/**
* 网络模块.
* 此为内部 API: 它可能在任意时刻被改动.
*/
@MiraiInternalAPI
abstract val network: BotNetworkHandler
@PlannedRemoval("1.0.0")
@Deprecated("for binary compatibility until 1.0.0", level = DeprecationLevel.HIDDEN)
suspend inline fun Bot.join() = this.coroutineContext[Job]!!.join()
}
/**
* 挂起协程直到 [Bot] 下线.
*/
@JvmSynthetic
suspend inline fun Bot.join() = this.coroutineContext[Job]!!.join()
/**
* 撤回这条消息.
*

View File

@ -16,14 +16,17 @@ import kotlin.jvm.JvmName
/**
* 只读联系人列表, lock-free 实现
* 只读联系人列表, 无锁链表实现
*
* @see ContactList.asSequence
*/
@OptIn(MiraiInternalAPI::class)
@Suppress("unused")
class ContactList<C : Contact>(@MiraiInternalAPI val delegate: LockFreeLinkedList<C>) : Iterable<C> {
operator fun get(id: Long): C = delegate.asSequence().first { it.id == id }
class ContactList<C : Contact>(@MiraiInternalAPI("Implementation may change in future release") val delegate: LockFreeLinkedList<C>) :
Iterable<C> {
operator fun get(id: Long): C =
delegate.asSequence().firstOrNull { it.id == id } ?: throw NoSuchElementException("Contact id $id")
fun getOrNull(id: Long): C? = delegate.getOrNull(id)
val size: Int get() = delegate.size

View File

@ -356,7 +356,7 @@ data class GroupAllowMemberInviteEvent(
data class MemberJoinEvent(override val member: Member) : GroupMemberEvent, BotPassiveEvent, Packet
/**
* 成员离开群的事件
* 成员离开群的事件. 在事件广播前成员就已经从 [Group.members] 中删除
*/
sealed class MemberLeaveEvent : GroupMemberEvent {
/**
@ -365,7 +365,7 @@ sealed class MemberLeaveEvent : GroupMemberEvent {
data class Kick(
override val member: Member,
/**
* 操作人. null 则是机器人操作
* 操作人. null 则是机器人操作.
*/
override val operator: Member?
) : MemberLeaveEvent(), Packet, GroupOperableEvent {

View File

@ -1,255 +0,0 @@
@file:Suppress("unused")
package net.mamoe.mirai
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.io.ByteReadChannel
import net.mamoe.mirai.contact.*
import net.mamoe.mirai.data.AddFriendResult
import net.mamoe.mirai.event.events.NewFriendRequestEvent
import net.mamoe.mirai.event.events.MemberJoinRequestEvent
import net.mamoe.mirai.message.MessageReceipt
import net.mamoe.mirai.message.data.*
import net.mamoe.mirai.network.BotNetworkHandler
import net.mamoe.mirai.network.LoginFailedException
import net.mamoe.mirai.utils.*
/**
* 机器人对象. 一个机器人实例登录一个 QQ 账号.
* Mirai 为多账号设计, 可同时维护多个机器人.
*
* : Bot 为全协程实现, 没有其他任务时若不使用 [join], 主线程将会退出.
*
* @see Contact 联系人
* @see kotlinx.coroutines.isActive 判断 [Bot] 是否正常运行中. (在线, 且没有被 [close])
*/
@Suppress("INAPPLICABLE_JVM_NAME")
@OptIn(
MiraiInternalAPI::class, LowLevelAPI::class, MiraiExperimentalAPI::class, JavaFriendlyAPI::class
)
actual abstract class Bot actual constructor() : CoroutineScope, LowLevelBotAPIAccessor, BotJavaFriendlyAPI() {
actual companion object {
/**
* 复制一份此时的 [Bot] 实例列表.
*/
@JvmStatic
actual val instances: List<WeakRef<Bot>>
get() = BotImpl.instances.toList()
/**
* 遍历每一个 [Bot] 实例
*/
@JvmName("forEachInstanceKotlin")
@JvmSynthetic
actual inline fun forEachInstance(block: (Bot) -> Unit) = BotImpl.forEachInstance(block)
/**
* 遍历每一个 [Bot] 实例
*/
@JavaFriendlyAPI
@JvmName("forEachInstance")
@Suppress("FunctionName")
fun __forEachInstanceForJava__(block: (Bot) -> Unit) = forEachInstance(block)
/**
* 获取一个 [Bot] 实例, 找不到则 [NoSuchElementException]
*/
@JvmStatic
actual fun getInstance(qq: Long): Bot = BotImpl.getInstance(qq = qq)
}
/**
* [Bot] 运行的 [Context].
*
* JVM 的默认实现为 [net.mamoe.mirai.utils.Context]
* Android 实现为 `android.content.Context`
*/
actual abstract val context: Context
@PlannedRemoval("1.0.0")
@Deprecated("use id instead", replaceWith = ReplaceWith("id"))
actual abstract val uin: Long
/**
* QQ 号码. 实际类型为 uint
*/
@SinceMirai("0.32.0")
actual abstract val id: Long
/**
* 昵称
*/
actual abstract val nick: String
/**
* 日志记录器
*/
actual abstract val logger: MiraiLogger
// region contacts
actual abstract val selfQQ: QQ
/**
* 机器人的好友列表. 它将与服务器同步更新
*/
actual abstract val friends: ContactList<QQ>
/**
* 获取一个好友对象. 若没有这个好友, 则会抛出异常 [NoSuchElementException]
*/
actual fun getFriend(id: Long): QQ {
if (id == this.id) return selfQQ
return friends.delegate.getOrNull(id)
?: throw NoSuchElementException("No such friend $id for bot ${this.id}")
}
/**
* 机器人加入的群列表.
*/
actual abstract val groups: ContactList<Group>
/**
* 获取一个机器人加入的群.
*
* @throws NoSuchElementException 当不存在这个群时
*/
actual fun getGroup(id: Long): Group {
return groups.delegate.getOrNull(id)
?: throw NoSuchElementException("No such group $id for bot ${this.id}")
}
// endregion
// region network
/**
* 网络模块
*/
actual abstract val network: BotNetworkHandler
/**
* 挂起协程直到 [Bot] 下线.
*/
@JvmSynthetic
actual suspend inline fun join() = network.join()
/**
* 登录, 或重新登录.
* 这个函数总是关闭一切现有网路任务, 然后重新登录并重新缓存好友列表和群列表.
*
* 一般情况下不需要重新登录. Mirai 能够自动处理掉线情况.
*
* 最终调用 [net.mamoe.mirai.network.BotNetworkHandler.relogin]
*
* @throws LoginFailedException
*/
@JvmSynthetic
actual abstract suspend fun login()
// endregion
// region actions
/**
* 撤回这条消息. 可撤回自己 2 分钟内发出的消息, 和任意时间的群成员的消息.
*
* [Bot] 撤回自己的消息不需要权限.
* [Bot] 撤回群员的消息需要管理员权限.
*
* @param source 消息源. 可从 [MessageReceipt.source] 获得, 或从消息事件中的 [MessageChain] 获得.
*
* @throws PermissionDeniedException [Bot] 无权限操作时
*
* @see Bot.recall (扩展函数) 接受参数 [MessageChain]
* @see _lowLevelRecallFriendMessage 低级 API
* @see _lowLevelRecallGroupMessage 低级 API
*/
@JvmSynthetic
actual abstract suspend fun recall(source: MessageSource)
/**
* 获取图片下载链接
*/
@JvmSynthetic
actual abstract suspend fun queryImageUrl(image: Image): String
/**
* 获取图片下载链接并开始下载.
*
* @see ByteReadChannel.copyAndClose
* @see ByteReadChannel.copyTo
*/
@JvmSynthetic
actual abstract suspend fun openChannel(image: Image): ByteReadChannel
/**
* 添加一个好友
*
* @param message 若需要验证请求时的验证消息.
* @param remark 好友备注
*/
@JvmSynthetic
@MiraiExperimentalAPI("未支持")
actual abstract suspend fun addFriend(id: Long, message: String?, remark: String?): AddFriendResult
// endregion
/**
* 关闭这个 [Bot], 立即取消 [Bot] [kotlinx.coroutines.SupervisorJob].
* 之后 [kotlinx.coroutines.isActive] 将会返回 `false`.
*
* **注意:** 不可重新登录. 必须重新实例化一个 [Bot].
*
* @param cause 原因. null 时视为正常关闭, null 时视为异常关闭
*
* @see closeAndJoin 取消并 [Bot.join], 以确保 [Bot] 相关的活动被完全关闭
*/
actual abstract fun close(cause: Throwable?)
@OptIn(LowLevelAPI::class, MiraiExperimentalAPI::class)
actual final override fun toString(): String = "Bot($id)"
/**
* 通过好友验证
*
* @param event 好友验证的事件对象
*/
@JvmSynthetic
actual abstract suspend fun acceptNewFriendRequest(event: NewFriendRequestEvent)
/**
* 拒绝好友验证
*
* @param event 好友验证的事件对象
* @param blackList 拒绝后是否拉入黑名单
*/
@JvmSynthetic
actual abstract suspend fun rejectNewFriendRequest(event: NewFriendRequestEvent, blackList: Boolean)
/**
* 通过加群验证需管理员权限
*
* @param event 加群验证的事件对象
*/
@JvmSynthetic
actual abstract suspend fun acceptMemberJoinRequest(event: MemberJoinRequestEvent)
/**
* 拒绝加群验证需管理员权限
*
* @param event 加群验证的事件对象
* @param blackList 拒绝后是否拉入黑名单
*/
@JvmSynthetic
actual abstract suspend fun rejectMemberJoinRequest(event: MemberJoinRequestEvent, blackList: Boolean)
/**
* 忽略加群验证需管理员权限
*
* @param event 加群验证的事件对象
* @param blackList 忽略后是否拉入黑名单
*/
@JvmSynthetic
actual abstract suspend fun ignoreMemberJoinRequest(event: MemberJoinRequestEvent, blackList: Boolean)
}