Commit 90185490 authored by Him188's avatar Him188

Implementations rearrangement

parent 2b0165d8
...@@ -21,12 +21,10 @@ package net.mamoe.mirai.message ...@@ -21,12 +21,10 @@ package net.mamoe.mirai.message
import net.mamoe.mirai.Bot import net.mamoe.mirai.Bot
import net.mamoe.mirai.contact.* import net.mamoe.mirai.contact.*
import net.mamoe.mirai.event.AbstractEvent
import net.mamoe.mirai.event.events.BotEvent import net.mamoe.mirai.event.events.BotEvent
import net.mamoe.mirai.event.subscribe
import net.mamoe.mirai.message.data.* import net.mamoe.mirai.message.data.*
import net.mamoe.mirai.qqandroid.network.Packet
import net.mamoe.mirai.utils.ExternalImage import net.mamoe.mirai.utils.ExternalImage
import net.mamoe.mirai.utils.PlannedRemoval
import net.mamoe.mirai.utils.sendTo import net.mamoe.mirai.utils.sendTo
import net.mamoe.mirai.utils.upload import net.mamoe.mirai.utils.upload
import kotlin.jvm.JvmMultifileClass import kotlin.jvm.JvmMultifileClass
...@@ -46,7 +44,7 @@ import kotlin.jvm.JvmSynthetic ...@@ -46,7 +44,7 @@ import kotlin.jvm.JvmSynthetic
* @see isContextIdenticalWith 判断语境是否相同 * @see isContextIdenticalWith 判断语境是否相同
*/ */
@Suppress("DEPRECATION_ERROR") @Suppress("DEPRECATION_ERROR")
public abstract class MessageEvent : @PlannedRemoval("1.2.0") ContactMessage(), public abstract class MessageEvent : ContactMessage(),
BotEvent, MessageEventExtensions<User, Contact> { BotEvent, MessageEventExtensions<User, Contact> {
/** /**
...@@ -167,112 +165,3 @@ internal expect interface MessageEventPlatformExtensions<out TSender : User, out ...@@ -167,112 +165,3 @@ internal expect interface MessageEventPlatformExtensions<out TSender : User, out
val message: MessageChain val message: MessageChain
val bot: Bot val bot: Bot
} }
/**
* 已废弃, 请使用 [MessageEvent]
*/
@PlannedRemoval("1.2.0")
@Deprecated(
message = "use MessageEvent",
replaceWith = ReplaceWith("MessageEvent", "net.mamoe.mirai.message.MessageEvent"),
level = DeprecationLevel.HIDDEN
)
public abstract class MessagePacketBase<out TSender : User, out TSubject : Contact> : Packet, BotEvent,
AbstractEvent() {
abstract override val bot: Bot
public abstract val sender: User
public abstract val subject: Contact
public abstract val message: MessageChain
public abstract val time: Int
public abstract val source: OnlineMessageSource.Incoming
public abstract val senderName: String
}
@PlannedRemoval("1.2.0")
@Deprecated(
message = "Ambiguous name. Use MessageEvent instead",
replaceWith = ReplaceWith("MessageEvent", "net.mamoe.mirai.message.MessageEvent"),
level = DeprecationLevel.HIDDEN
)
@Suppress("DEPRECATION_ERROR")
public abstract class MessagePacket : MessagePacketBase<User, Contact>(),
BotEvent, MessageEventExtensions<User, Contact> {
abstract override val bot: Bot
abstract override val sender: User
abstract override val subject: Contact
abstract override val message: MessageChain
abstract override val time: Int
abstract override val source: OnlineMessageSource.Incoming
abstract override val senderName: String
}
@PlannedRemoval("1.2.0")
@Deprecated(
message = "Ambiguous name. Use MessageEvent instead",
replaceWith = ReplaceWith("MessageEvent", "net.mamoe.mirai.message.MessageEvent"),
level = DeprecationLevel.HIDDEN
)
@Suppress("DEPRECATION_ERROR")
public abstract class ContactMessage : MessagePacket(),
BotEvent, MessageEventExtensions<User, Contact> {
abstract override val bot: Bot
abstract override val sender: User
abstract override val subject: Contact
abstract override val message: MessageChain
abstract override val time: Int
abstract override val source: OnlineMessageSource.Incoming
abstract override val senderName: String
}
@PlannedRemoval("1.2.0")
@Deprecated(
message = "Ambiguous name. Use FriendMessageEvent instead",
replaceWith = ReplaceWith("FriendMessageEvent", "net.mamoe.mirai.message.FriendMessageEvent"),
level = DeprecationLevel.HIDDEN
)
@Suppress("DEPRECATION_ERROR")
public abstract class FriendMessage : MessageEvent() {
abstract override val bot: Bot
abstract override val sender: Friend
abstract override val subject: Friend
abstract override val message: MessageChain
abstract override val time: Int
abstract override val source: OnlineMessageSource.Incoming.FromFriend
abstract override val senderName: String
}
@PlannedRemoval("1.2.0")
@Deprecated(
message = "Ambiguous name. Use GroupMessageEvent instead",
replaceWith = ReplaceWith("GroupMessageEvent", "net.mamoe.mirai.message.GroupMessageEvent"),
level = DeprecationLevel.HIDDEN
)
@Suppress("DEPRECATION_ERROR")
public abstract class GroupMessage : MessageEvent() {
public abstract val group: Group
abstract override val bot: Bot
abstract override val sender: Member
abstract override val subject: Group
abstract override val message: MessageChain
abstract override val time: Int
abstract override val source: OnlineMessageSource.Incoming.FromGroup
abstract override val senderName: String
}
@PlannedRemoval("1.2.0")
@Deprecated(
message = "Ambiguous name. Use TempMessageEvent instead",
replaceWith = ReplaceWith("TempMessageEvent", "net.mamoe.mirai.message.TempMessageEvent"),
level = DeprecationLevel.HIDDEN
)
public abstract class TempMessage : MessageEvent() {
abstract override val bot: Bot
abstract override val sender: Member
abstract override val subject: Member
abstract override val message: MessageChain
abstract override val time: Int
abstract override val source: OnlineMessageSource.Incoming.FromTemp
public abstract val group: Group
abstract override val senderName: String
}
\ No newline at end of file
/*
* Copyright 2020 Mamoe Technologies and contributors.
*
* 此源代码的使用受 GNU AFFERO GENERAL PUBLIC LICENSE version 3 许可证的约束, 可以在以下链接找到该许可证.
* Use of this source code is governed by the GNU AGPLv3 license that can be found through the following link.
*
* https://github.com/mamoe/mirai/blob/master/LICENSE
*/
@file:Suppress(
"EXPERIMENTAL_UNSIGNED_LITERALS",
"EXPERIMENTAL_API_USAGE",
"unused",
"DECLARATION_CANT_BE_INLINED", "UNCHECKED_CAST", "NOTHING_TO_INLINE"
)
@file:JvmMultifileClass
@file:JvmName("MessageEventKt")
package net.mamoe.mirai.message
import net.mamoe.mirai.Bot
import net.mamoe.mirai.contact.*
import net.mamoe.mirai.event.AbstractEvent
import net.mamoe.mirai.event.events.BotEvent
import net.mamoe.mirai.message.data.MessageChain
import net.mamoe.mirai.message.data.OnlineMessageSource
import net.mamoe.mirai.qqandroid.network.Packet
import kotlin.jvm.JvmMultifileClass
import kotlin.jvm.JvmName
/**
* 已废弃, 请使用 [MessageEvent]
*/
@Deprecated(
message = "use MessageEvent",
replaceWith = ReplaceWith("MessageEvent", "net.mamoe.mirai.message.MessageEvent"),
level = DeprecationLevel.HIDDEN
)
public abstract class MessagePacketBase<out TSender : User, out TSubject : Contact> : Packet, BotEvent,
AbstractEvent() {
abstract override val bot: Bot
public abstract val sender: User
public abstract val subject: Contact
public abstract val message: MessageChain
public abstract val time: Int
public abstract val source: OnlineMessageSource.Incoming
public abstract val senderName: String
}
@Deprecated(
message = "Ambiguous name. Use MessageEvent instead",
replaceWith = ReplaceWith("MessageEvent", "net.mamoe.mirai.message.MessageEvent"),
level = DeprecationLevel.HIDDEN
)
@Suppress("DEPRECATION_ERROR")
public abstract class MessagePacket : MessagePacketBase<User, Contact>(),
BotEvent, MessageEventExtensions<User, Contact> {
abstract override val bot: Bot
abstract override val sender: User
abstract override val subject: Contact
abstract override val message: MessageChain
abstract override val time: Int
abstract override val source: OnlineMessageSource.Incoming
abstract override val senderName: String
}
@Deprecated(
message = "Ambiguous name. Use MessageEvent instead",
replaceWith = ReplaceWith("MessageEvent", "net.mamoe.mirai.message.MessageEvent"),
level = DeprecationLevel.HIDDEN
)
@Suppress("DEPRECATION_ERROR")
public abstract class ContactMessage : MessagePacket(),
BotEvent, MessageEventExtensions<User, Contact> {
abstract override val bot: Bot
abstract override val sender: User
abstract override val subject: Contact
abstract override val message: MessageChain
abstract override val time: Int
abstract override val source: OnlineMessageSource.Incoming
abstract override val senderName: String
}
@Deprecated(
message = "Ambiguous name. Use FriendMessageEvent instead",
replaceWith = ReplaceWith("FriendMessageEvent", "net.mamoe.mirai.message.FriendMessageEvent"),
level = DeprecationLevel.HIDDEN
)
@Suppress("DEPRECATION_ERROR")
public abstract class FriendMessage : MessageEvent() {
abstract override val bot: Bot
abstract override val sender: Friend
abstract override val subject: Friend
abstract override val message: MessageChain
abstract override val time: Int
abstract override val source: OnlineMessageSource.Incoming.FromFriend
abstract override val senderName: String
}
@Deprecated(
message = "Ambiguous name. Use GroupMessageEvent instead",
replaceWith = ReplaceWith("GroupMessageEvent", "net.mamoe.mirai.message.GroupMessageEvent"),
level = DeprecationLevel.HIDDEN
)
@Suppress("DEPRECATION_ERROR")
public abstract class GroupMessage : MessageEvent() {
public abstract val group: Group
abstract override val bot: Bot
abstract override val sender: Member
abstract override val subject: Group
abstract override val message: MessageChain
abstract override val time: Int
abstract override val source: OnlineMessageSource.Incoming.FromGroup
abstract override val senderName: String
}
@Deprecated(
message = "Ambiguous name. Use TempMessageEvent instead",
replaceWith = ReplaceWith("TempMessageEvent", "net.mamoe.mirai.message.TempMessageEvent"),
level = DeprecationLevel.HIDDEN
)
public abstract class TempMessage : MessageEvent() {
abstract override val bot: Bot
abstract override val sender: Member
abstract override val subject: Member
abstract override val message: MessageChain
abstract override val time: Int
abstract override val source: OnlineMessageSource.Incoming.FromTemp
public abstract val group: Group
abstract override val senderName: String
}
\ No newline at end of file
...@@ -14,15 +14,9 @@ ...@@ -14,15 +14,9 @@
package net.mamoe.mirai.event package net.mamoe.mirai.event
import kotlinx.coroutines.* import kotlinx.coroutines.*
import java.lang.reflect.Method import net.mamoe.mirai.event.internal.registerEvent
import kotlin.coroutines.CoroutineContext import kotlin.coroutines.CoroutineContext
import kotlin.coroutines.EmptyCoroutineContext import kotlin.coroutines.EmptyCoroutineContext
import kotlin.reflect.KClass
import kotlin.reflect.full.IllegalCallableAccessException
import kotlin.reflect.full.callSuspend
import kotlin.reflect.full.isSubclassOf
import kotlin.reflect.jvm.isAccessible
import kotlin.reflect.jvm.kotlinFunction
/** /**
* 标注一个函数为事件监听器. * 标注一个函数为事件监听器.
...@@ -227,154 +221,13 @@ public fun <T> T.registerEvents(coroutineContext: CoroutineContext = EmptyCorout ...@@ -227,154 +221,13 @@ public fun <T> T.registerEvents(coroutineContext: CoroutineContext = EmptyCorout
* @see EventHandler 获取更多信息 * @see EventHandler 获取更多信息
*/ */
@JvmOverloads @JvmOverloads
public fun CoroutineScope.registerEvents(host: ListenerHost, coroutineContext: CoroutineContext = EmptyCoroutineContext) { public fun CoroutineScope.registerEvents(
host: ListenerHost,
coroutineContext: CoroutineContext = EmptyCoroutineContext
) {
for (method in host.javaClass.declaredMethods) { for (method in host.javaClass.declaredMethods) {
method.getAnnotation(EventHandler::class.java)?.let { method.getAnnotation(EventHandler::class.java)?.let {
method.registerEvent(host, this, it, coroutineContext) method.registerEvent(host, this, it, coroutineContext)
} }
} }
} }
\ No newline at end of file
@Suppress("UNCHECKED_CAST")
private fun Method.registerEvent(
owner: Any,
scope: CoroutineScope,
annotation: EventHandler,
coroutineContext: CoroutineContext
): Listener<Event> {
this.isAccessible = true
val kotlinFunction = kotlin.runCatching { this.kotlinFunction }.getOrNull()
return if (kotlinFunction != null) {
// kotlin functions
val param = kotlinFunction.parameters
when (param.size) {
3 -> { // ownerClass, receiver, event
check(param[1].type == param[2].type) { "Illegal kotlin function ${kotlinFunction.name}. Receiver and param must have same type" }
check((param[1].type.classifier as? KClass<*>)?.isSubclassOf(Event::class) == true) {
"Illegal kotlin function ${kotlinFunction.name}. First param or receiver must be subclass of Event, but found ${param[1].type.classifier}"
}
}
2 -> { // ownerClass, event
check((param[1].type.classifier as? KClass<*>)?.isSubclassOf(Event::class) == true) {
"Illegal kotlin function ${kotlinFunction.name}. First param or receiver must be subclass of Event, but found ${param[1].type.classifier}"
}
}
else -> error("function ${kotlinFunction.name} must have one Event param")
}
lateinit var listener: Listener<*>
kotlin.runCatching {
kotlinFunction.isAccessible = true
}
suspend fun callFunction(event: Event): Any? {
try {
return when (param.size) {
3 -> {
if (kotlinFunction.isSuspend) {
kotlinFunction.callSuspend(owner, event, event)
} else withContext(Dispatchers.IO) { // for safety
kotlinFunction.call(owner, event, event)
}
}
2 -> {
if (kotlinFunction.isSuspend) {
kotlinFunction.callSuspend(owner, event)
} else withContext(Dispatchers.IO) { // for safety
kotlinFunction.call(owner, event)
}
}
else -> error("stub")
}
} catch (e: IllegalCallableAccessException) {
listener.completeExceptionally(e)
return ListeningStatus.STOPPED
}
}
require(!kotlinFunction.returnType.isMarkedNullable) {
"Kotlin event handlers cannot have nullable return type."
}
require(kotlinFunction.parameters.any { it.type.isMarkedNullable }) {
"Kotlin event handlers cannot have nullable parameter type."
}
when (kotlinFunction.returnType.classifier) {
Unit::class, Nothing::class -> {
scope.subscribeAlways(
param[1].type.classifier as KClass<out Event>,
priority = annotation.priority,
concurrency = annotation.concurrency,
coroutineContext = coroutineContext
) {
if (annotation.ignoreCancelled) {
if ((this as? CancellableEvent)?.isCancelled != true) {
callFunction(this)
}
} else callFunction(this)
}.also { listener = it }
}
ListeningStatus::class -> {
scope.subscribe(
param[1].type.classifier as KClass<out Event>,
priority = annotation.priority,
concurrency = annotation.concurrency,
coroutineContext = coroutineContext
) {
if (annotation.ignoreCancelled) {
if ((this as? CancellableEvent)?.isCancelled != true) {
callFunction(this) as ListeningStatus
} else ListeningStatus.LISTENING
} else callFunction(this) as ListeningStatus
}.also { listener = it }
}
else -> error("Illegal method return type. Required Void, Nothing or ListeningStatus, found ${kotlinFunction.returnType.classifier}")
}
} else {
// java methods
val paramType = this.parameters[0].type
check(this.parameterCount == 1 && Event::class.java.isAssignableFrom(paramType)) {
"Illegal method parameter. Required one exact Event subclass. found $paramType"
}
when (this.returnType) {
Void::class.java, Void.TYPE, Nothing::class.java -> {
scope.subscribeAlways(
paramType.kotlin as KClass<out Event>,
priority = annotation.priority,
concurrency = annotation.concurrency,
coroutineContext = coroutineContext
) {
if (annotation.ignoreCancelled) {
if ((this as? CancellableEvent)?.isCancelled != true) {
withContext(Dispatchers.IO) {
this@registerEvent.invoke(owner, this)
}
}
} else withContext(Dispatchers.IO) {
this@registerEvent.invoke(owner, this)
}
}
}
ListeningStatus::class.java -> {
scope.subscribe(
paramType.kotlin as KClass<out Event>,
priority = annotation.priority,
concurrency = annotation.concurrency,
coroutineContext = coroutineContext
) {
if (annotation.ignoreCancelled) {
if ((this as? CancellableEvent)?.isCancelled != true) {
withContext(Dispatchers.IO) {
this@registerEvent.invoke(owner, this) as ListeningStatus
}
} else ListeningStatus.LISTENING
} else withContext(Dispatchers.IO) {
this@registerEvent.invoke(owner, this) as ListeningStatus
}
}
}
else -> error("Illegal method return type. Required Void or ListeningStatus, but found ${this.returnType.canonicalName}")
}
}
}
\ No newline at end of file
...@@ -14,12 +14,18 @@ package net.mamoe.mirai.event.internal ...@@ -14,12 +14,18 @@ package net.mamoe.mirai.event.internal
import kotlinx.coroutines.CoroutineScope import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.withContext import kotlinx.coroutines.withContext
import net.mamoe.mirai.event.Event import net.mamoe.mirai.event.*
import net.mamoe.mirai.event.Listener import java.lang.reflect.Method
import net.mamoe.mirai.event.ListeningStatus
import java.util.function.Consumer import java.util.function.Consumer
import java.util.function.Function import java.util.function.Function
import kotlin.coroutines.CoroutineContext
import kotlin.coroutines.EmptyCoroutineContext import kotlin.coroutines.EmptyCoroutineContext
import kotlin.reflect.KClass
import kotlin.reflect.full.IllegalCallableAccessException
import kotlin.reflect.full.callSuspend
import kotlin.reflect.full.isSubclassOf
import kotlin.reflect.jvm.isAccessible
import kotlin.reflect.jvm.kotlinFunction
@Suppress("FunctionName") @Suppress("FunctionName")
internal fun <E : Event> Class<E>._subscribeEventForJaptOnly( internal fun <E : Event> Class<E>._subscribeEventForJaptOnly(
...@@ -41,3 +47,148 @@ internal fun <E : Event> Class<E>._subscribeEventForJaptOnly(scope: CoroutineSco ...@@ -41,3 +47,148 @@ internal fun <E : Event> Class<E>._subscribeEventForJaptOnly(scope: CoroutineSco
Listener.ConcurrencyKind.LOCKED Listener.ConcurrencyKind.LOCKED
) { withContext(Dispatchers.IO) { onEvent.accept(it) }; ListeningStatus.LISTENING; }) ) { withContext(Dispatchers.IO) { onEvent.accept(it) }; ListeningStatus.LISTENING; })
} }
@Suppress("UNCHECKED_CAST")
internal fun Method.registerEvent(
owner: Any,
scope: CoroutineScope,
annotation: EventHandler,
coroutineContext: CoroutineContext
): Listener<Event> {
this.isAccessible = true
val kotlinFunction = kotlin.runCatching { this.kotlinFunction }.getOrNull()
return if (kotlinFunction != null) {
// kotlin functions
val param = kotlinFunction.parameters
when (param.size) {
3 -> { // ownerClass, receiver, event
check(param[1].type == param[2].type) { "Illegal kotlin function ${kotlinFunction.name}. Receiver and param must have same type" }
check((param[1].type.classifier as? KClass<*>)?.isSubclassOf(Event::class) == true) {
"Illegal kotlin function ${kotlinFunction.name}. First param or receiver must be subclass of Event, but found ${param[1].type.classifier}"
}
}
2 -> { // ownerClass, event
check((param[1].type.classifier as? KClass<*>)?.isSubclassOf(Event::class) == true) {
"Illegal kotlin function ${kotlinFunction.name}. First param or receiver must be subclass of Event, but found ${param[1].type.classifier}"
}
}
else -> error("function ${kotlinFunction.name} must have one Event param")
}
lateinit var listener: Listener<*>
kotlin.runCatching {
kotlinFunction.isAccessible = true
}
suspend fun callFunction(event: Event): Any? {
try {
return when (param.size) {
3 -> {
if (kotlinFunction.isSuspend) {
kotlinFunction.callSuspend(owner, event, event)
} else withContext(Dispatchers.IO) { // for safety
kotlinFunction.call(owner, event, event)
}
}
2 -> {
if (kotlinFunction.isSuspend) {
kotlinFunction.callSuspend(owner, event)
} else withContext(Dispatchers.IO) { // for safety
kotlinFunction.call(owner, event)
}
}
else -> error("stub")
}
} catch (e: IllegalCallableAccessException) {
listener.completeExceptionally(e)
return ListeningStatus.STOPPED
}
}
require(!kotlinFunction.returnType.isMarkedNullable) {
"Kotlin event handlers cannot have nullable return type."
}
require(kotlinFunction.parameters.any { it.type.isMarkedNullable }) {
"Kotlin event handlers cannot have nullable parameter type."
}
when (kotlinFunction.returnType.classifier) {
Unit::class, Nothing::class -> {
scope.subscribeAlways(
param[1].type.classifier as KClass<out Event>,
priority = annotation.priority,
concurrency = annotation.concurrency,
coroutineContext = coroutineContext
) {
if (annotation.ignoreCancelled) {
if ((this as? CancellableEvent)?.isCancelled != true) {
callFunction(this)
}
} else callFunction(this)
}.also { listener = it }
}
ListeningStatus::class -> {
scope.subscribe(
param[1].type.classifier as KClass<out Event>,
priority = annotation.priority,
concurrency = annotation.concurrency,
coroutineContext = coroutineContext
) {
if (annotation.ignoreCancelled) {
if ((this as? CancellableEvent)?.isCancelled != true) {
callFunction(this) as ListeningStatus
} else ListeningStatus.LISTENING
} else callFunction(this) as ListeningStatus
}.also { listener = it }
}
else -> error("Illegal method return type. Required Void, Nothing or ListeningStatus, found ${kotlinFunction.returnType.classifier}")
}
} else {
// java methods
val paramType = this.parameters[0].type
check(this.parameterCount == 1 && Event::class.java.isAssignableFrom(paramType)) {
"Illegal method parameter. Required one exact Event subclass. found $paramType"
}
when (this.returnType) {
Void::class.java, Void.TYPE, Nothing::class.java -> {
scope.subscribeAlways(
paramType.kotlin as KClass<out Event>,
priority = annotation.priority,
concurrency = annotation.concurrency,
coroutineContext = coroutineContext
) {
if (annotation.ignoreCancelled) {
if ((this as? CancellableEvent)?.isCancelled != true) {
withContext(Dispatchers.IO) {
this@registerEvent.invoke(owner, this)
}
}
} else withContext(Dispatchers.IO) {
this@registerEvent.invoke(owner, this)
}
}
}
ListeningStatus::class.java -> {
scope.subscribe(
paramType.kotlin as KClass<out Event>,
priority = annotation.priority,
concurrency = annotation.concurrency,
coroutineContext = coroutineContext
) {
if (annotation.ignoreCancelled) {
if ((this as? CancellableEvent)?.isCancelled != true) {
withContext(Dispatchers.IO) {
this@registerEvent.invoke(owner, this) as ListeningStatus
}
} else ListeningStatus.LISTENING
} else withContext(Dispatchers.IO) {
this@registerEvent.invoke(owner, this) as ListeningStatus
}
}
}
else -> error("Illegal method return type. Required Void or ListeningStatus, but found ${this.returnType.canonicalName}")
}
}
}
\ No newline at end of file
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