Skip to content
Projects
Groups
Snippets
Help
Loading...
Help
Support
Keyboard shortcuts
?
Submit feedback
Sign in / Register
Toggle navigation
M
Mirai
Project overview
Project overview
Details
Activity
Releases
Repository
Repository
Files
Commits
Branches
Tags
Contributors
Graph
Compare
Locked Files
Issues
0
Issues
0
List
Boards
Labels
Service Desk
Milestones
Merge Requests
0
Merge Requests
0
CI / CD
CI / CD
Pipelines
Jobs
Schedules
Security & Compliance
Security & Compliance
Dependency List
License Compliance
Packages
Packages
List
Container Registry
Analytics
Analytics
CI / CD
Code Review
Insights
Issues
Repository
Value Stream
Wiki
Wiki
Snippets
Snippets
Members
Members
Collapse sidebar
Close sidebar
Activity
Graph
Create a new issue
Jobs
Commits
Issue Boards
Open sidebar
MyCard
Mirai
Commits
22175eaa
Commit
22175eaa
authored
Dec 23, 2019
by
Him188
Browse files
Options
Browse Files
Download
Email Patches
Plain Diff
Rewrite events
parent
e56ef7ba
Changes
8
Hide whitespace changes
Inline
Side-by-side
Showing
8 changed files
with
269 additions
and
449 deletions
+269
-449
mirai-core/src/commonMain/kotlin/net.mamoe.mirai/event/MessageSubscribers.kt
...onMain/kotlin/net.mamoe.mirai/event/MessageSubscribers.kt
+60
-69
mirai-core/src/commonMain/kotlin/net.mamoe.mirai/event/Subscribable.kt
...c/commonMain/kotlin/net.mamoe.mirai/event/Subscribable.kt
+3
-30
mirai-core/src/commonMain/kotlin/net.mamoe.mirai/event/Subscribers.kt
...rc/commonMain/kotlin/net.mamoe.mirai/event/Subscribers.kt
+107
-121
mirai-core/src/commonMain/kotlin/net.mamoe.mirai/event/SubscribersWithBot.kt
...onMain/kotlin/net.mamoe.mirai/event/SubscribersWithBot.kt
+0
-103
mirai-core/src/commonMain/kotlin/net.mamoe.mirai/event/internal/InternalEventListeners.kt
.../net.mamoe.mirai/event/internal/InternalEventListeners.kt
+33
-92
mirai-core/src/jvmTest/kotlin/net/mamoe/mirai/event/EventTests.kt
...re/src/jvmTest/kotlin/net/mamoe/mirai/event/EventTests.kt
+44
-0
mirai-demos/mirai-demo-1/src/main/java/demo/subscribe/SubscribeSamples.kt
...i-demo-1/src/main/java/demo/subscribe/SubscribeSamples.kt
+13
-28
mirai-demos/mirai-demo-gentleman/src/main/kotlin/demo/gentleman/Main.kt
...rai-demo-gentleman/src/main/kotlin/demo/gentleman/Main.kt
+9
-6
No files found.
mirai-core/src/commonMain/kotlin/net.mamoe.mirai/event/MessageSubscribers.kt
View file @
22175eaa
...
...
@@ -2,6 +2,7 @@
package
net.mamoe.mirai.event
import
kotlinx.coroutines.CoroutineScope
import
net.mamoe.mirai.Bot
import
net.mamoe.mirai.contact.isAdministrator
import
net.mamoe.mirai.contact.isOperator
...
...
@@ -13,20 +14,19 @@ import net.mamoe.mirai.message.data.Message
import
kotlin.contracts.ExperimentalContracts
import
kotlin.contracts.InvocationKind
import
kotlin.contracts.contract
import
kotlin.jvm.JvmName
/**
* 订阅来自所有 [Bot] 的所有联系人的消息事件. 联系人可以是任意群或任意好友或临时会话.
*/
@UseExperimental
(
ExperimentalContracts
::
class
)
@MessageDsl
suspend
inline
fun
subscribeMessages
(
crossinline
listeners
:
suspend
MessageSubscribersBuilder
<
MessagePacket
<
*
,
*
>>.()
->
Unit
)
{
inline
fun
CoroutineScope
.
subscribeMessages
(
crossinline
listeners
:
MessageSubscribersBuilder
<
MessagePacket
<
*
,
*
>>.()
->
Unit
)
{
contract
{
callsInPlace
(
listeners
,
InvocationKind
.
EXACTLY_ONCE
)
}
MessageSubscribersBuilder
<
MessagePacket
<
*
,
*
>>
{
listener
->
subscribeAlways
<
MessagePacket
<
*
,
*
>>
{
listener
(
it
)
subscribeAlways
{
listener
(
it
,
this
.
message
.
toString
()
)
}
}.
apply
{
listeners
()
}
}
...
...
@@ -36,13 +36,13 @@ suspend inline fun subscribeMessages(crossinline listeners: suspend MessageSubsc
*/
@UseExperimental
(
ExperimentalContracts
::
class
)
@MessageDsl
suspend
inline
fun
subscribeGroupMessages
(
crossinline
listeners
:
suspend
MessageSubscribersBuilder
<
GroupMessage
>.()
->
Unit
)
{
inline
fun
CoroutineScope
.
subscribeGroupMessages
(
crossinline
listeners
:
MessageSubscribersBuilder
<
GroupMessage
>.()
->
Unit
)
{
contract
{
callsInPlace
(
listeners
,
InvocationKind
.
EXACTLY_ONCE
)
}
MessageSubscribersBuilder
<
GroupMessage
>
{
listener
->
subscribeAlways
<
GroupMessage
>
{
listener
(
it
)
subscribeAlways
{
listener
(
it
,
this
.
message
.
toString
()
)
}
}.
apply
{
listeners
()
}
}
...
...
@@ -52,13 +52,13 @@ suspend inline fun subscribeGroupMessages(crossinline listeners: suspend Message
*/
@UseExperimental
(
ExperimentalContracts
::
class
)
@MessageDsl
suspend
inline
fun
subscribeFriendMessages
(
crossinline
listeners
:
suspend
MessageSubscribersBuilder
<
FriendMessage
>.()
->
Unit
)
{
inline
fun
CoroutineScope
.
subscribeFriendMessages
(
crossinline
listeners
:
MessageSubscribersBuilder
<
FriendMessage
>.()
->
Unit
)
{
contract
{
callsInPlace
(
listeners
,
InvocationKind
.
EXACTLY_ONCE
)
}
MessageSubscribersBuilder
<
FriendMessage
>
{
listener
->
subscribeAlways
<
FriendMessage
>
{
listener
(
it
)
subscribeAlways
{
listener
(
it
,
this
.
message
.
toString
()
)
}
}.
apply
{
listeners
()
}
}
...
...
@@ -68,13 +68,13 @@ suspend inline fun subscribeFriendMessages(crossinline listeners: suspend Messag
*/
@UseExperimental
(
ExperimentalContracts
::
class
)
@MessageDsl
suspend
inline
fun
Bot
.
subscribeMessages
(
crossinline
listeners
:
suspend
MessageSubscribersBuilder
<
MessagePacket
<
*
,
*
>>.()
->
Unit
)
{
inline
fun
Bot
.
subscribeMessages
(
crossinline
listeners
:
MessageSubscribersBuilder
<
MessagePacket
<
*
,
*
>>.()
->
Unit
)
{
contract
{
callsInPlace
(
listeners
,
InvocationKind
.
EXACTLY_ONCE
)
}
MessageSubscribersBuilder
<
MessagePacket
<
*
,
*
>>
{
listener
->
this
.
subscribeAlways
<
MessagePacket
<
*
,
*
>>
{
listener
(
it
)
this
.
subscribeAlways
{
listener
(
it
,
this
.
message
.
toString
()
)
}
}.
apply
{
listeners
()
}
}
...
...
@@ -84,13 +84,13 @@ suspend inline fun Bot.subscribeMessages(crossinline listeners: suspend MessageS
*/
@UseExperimental
(
ExperimentalContracts
::
class
)
@MessageDsl
suspend
inline
fun
Bot
.
subscribeGroupMessages
(
crossinline
listeners
:
suspend
MessageSubscribersBuilder
<
GroupMessage
>.()
->
Unit
)
{
inline
fun
Bot
.
subscribeGroupMessages
(
crossinline
listeners
:
MessageSubscribersBuilder
<
GroupMessage
>.()
->
Unit
)
{
contract
{
callsInPlace
(
listeners
,
InvocationKind
.
EXACTLY_ONCE
)
}
MessageSubscribersBuilder
<
GroupMessage
>
{
listener
->
this
.
subscribeAlways
<
GroupMessage
>
{
listener
(
it
)
this
.
subscribeAlways
{
listener
(
it
,
this
.
message
.
toString
()
)
}
}.
apply
{
listeners
()
}
}
...
...
@@ -100,45 +100,36 @@ suspend inline fun Bot.subscribeGroupMessages(crossinline listeners: suspend Mes
*/
@UseExperimental
(
ExperimentalContracts
::
class
)
@MessageDsl
suspend
inline
fun
Bot
.
subscribeFriendMessages
(
crossinline
listeners
:
suspend
MessageSubscribersBuilder
<
FriendMessage
>.()
->
Unit
)
{
inline
fun
Bot
.
subscribeFriendMessages
(
crossinline
listeners
:
MessageSubscribersBuilder
<
FriendMessage
>.()
->
Unit
)
{
contract
{
callsInPlace
(
listeners
,
InvocationKind
.
EXACTLY_ONCE
)
}
MessageSubscribersBuilder
<
FriendMessage
>
{
listener
->
this
.
subscribeAlways
<
FriendMessage
>
{
listener
(
it
)
this
.
subscribeAlways
{
it
.
listener
(
it
.
message
.
toString
()
)
}
}.
apply
{
listeners
()
}
}
private
typealias
AnyReplier
<
T
>
=
@MessageDsl
suspend
T
.(
String
)
->
Any
?
private
suspend
inline
operator
fun
<
T
:
MessagePacket
<
*
,
*
>>
(@
MessageDsl
suspend
T
.(
String
)
->
Unit
).
invoke
(
t
:
T
)
=
this
.
invoke
(
t
,
t
.
message
.
stringValue
)
@JvmName
(
"invoke1"
)
//Avoid Platform declaration clash
private
suspend
inline
operator
fun
<
T
:
MessagePacket
<
*
,
*
>>
AnyReplier
<
T
>.
invoke
(
t
:
T
):
Any
?
=
this
.
invoke
(
t
,
t
.
message
.
stringValue
)
/**
* 消息订阅构造器
*
* @see subscribeFriendMessages
* @sample demo.subscribe.messageDSL
*/
// TODO: 2019/1
1/29 应定义为 inline, 但这会导致一个 JVM run-time VerifyError. 等待 kotlin 修复 bug
// TODO: 2019/1
2/23 应定义为 inline, 但这会导致一个 JVM run-time VerifyError. 等待 kotlin 修复 bug (Kotlin 1.3.61)
@Suppress
(
"unused"
)
@MessageDsl
class
MessageSubscribersBuilder
<
T
:
MessagePacket
<
*
,
*
>>(
inline
val
subscriber
:
suspend
(
@MessageDsl
suspend
T
.(
String
)
->
Unit
)
->
Unit
val
subscriber
:
(
@MessageDsl
suspend
T
.(
String
)
->
Unit
)
->
Listener
<
T
>
)
{
/**
* 无任何触发条件.
*/
@MessageDsl
suspend
inline
fun
always
(
noinline
onEvent
:
@MessageDsl
suspend
T
.(
String
)
->
Unit
)
{
content
({
true
},
onEvent
)
}
// TODO: 2019/12/4 这些 onEvent 都应该为 cross-inline, 而这会导致一个 CompilationException
inline
fun
always
(
crossinline
onEvent
:
@MessageDsl
suspend
T
.(
String
)
->
Unit
):
Listener
<
T
>
{
return
content
({
true
},
onEvent
)
}
/**
* 如果消息内容 `==` [equals], 就执行 [onEvent]
...
...
@@ -146,97 +137,97 @@ class MessageSubscribersBuilder<T : MessagePacket<*, *>>(
* @param ignoreCase `true` 则不区分大小写
*/
@MessageDsl
suspend
inline
fun
case
(
inline
fun
case
(
equals
:
String
,
trim
:
Boolean
=
true
,
ignoreCase
:
Boolean
=
false
,
no
inline
onEvent
:
@MessageDsl
suspend
T
.(
String
)
->
Unit
)
{
cross
inline
onEvent
:
@MessageDsl
suspend
T
.(
String
)
->
Unit
)
:
Listener
<
T
>
{
val
toCheck
=
if
(
trim
)
equals
.
trim
()
else
equals
content
({
toCheck
.
equals
(
if
(
trim
)
it
.
trim
()
else
it
,
ignoreCase
=
ignoreCase
)
},
onEvent
)
return
content
({
toCheck
.
equals
(
if
(
trim
)
it
.
trim
()
else
it
,
ignoreCase
=
ignoreCase
)
},
onEvent
)
}
/**
* 如果消息内容包含 [sub], 就执行 [onEvent]
*/
@MessageDsl
suspend
inline
fun
contains
(
sub
:
String
,
noinline
onEvent
:
@MessageDsl
suspend
T
.(
String
)
->
Unit
)
=
content
({
sub
in
it
},
onEvent
)
inline
fun
contains
(
sub
:
String
,
crossinline
onEvent
:
@MessageDsl
suspend
T
.(
String
)
->
Unit
):
Listener
<
T
>
=
content
({
sub
in
it
},
onEvent
)
/**
* 如果消息的前缀是 [prefix], 就执行 [onEvent]
*/
@MessageDsl
suspend
inline
fun
startsWith
(
inline
fun
startsWith
(
prefix
:
String
,
removePrefix
:
Boolean
=
true
,
no
inline
onEvent
:
@MessageDsl
suspend
T
.(
String
)
->
Unit
)
=
cross
inline
onEvent
:
@MessageDsl
suspend
T
.(
String
)
->
Unit
)
:
Listener
<
T
>
=
content
({
it
.
startsWith
(
prefix
)
})
{
if
(
removePrefix
)
this
.
onEvent
(
this
.
message
.
stringValue
.
substringAfter
(
prefix
))
else
onEvent
(
this
)
else
onEvent
(
this
,
this
.
message
.
toString
()
)
}
/**
* 如果消息的结尾是 [suffix], 就执行 [onEvent]
*/
@MessageDsl
suspend
inline
fun
endsWith
(
suffix
:
String
,
noinline
onEvent
:
@MessageDsl
suspend
T
.(
String
)
->
Unit
)
=
inline
fun
endsWith
(
suffix
:
String
,
crossinline
onEvent
:
@MessageDsl
suspend
T
.(
String
)
->
Unit
):
Listener
<
T
>
=
content
({
it
.
endsWith
(
suffix
)
},
onEvent
)
/**
* 如果是这个人发的消息, 就执行 [onEvent]. 消息可以是好友消息也可以是群消息
*/
@MessageDsl
suspend
inline
fun
sentBy
(
qqId
:
Long
,
noinline
onEvent
:
@MessageDsl
suspend
T
.(
String
)
->
Unit
)
=
inline
fun
sentBy
(
qqId
:
Long
,
crossinline
onEvent
:
@MessageDsl
suspend
T
.(
String
)
->
Unit
):
Listener
<
T
>
=
content
({
sender
.
id
==
qqId
},
onEvent
)
/**
* 如果是管理员或群主发的消息, 就执行 [onEvent]
*/
@MessageDsl
suspend
inline
fun
sentByOperator
(
noinline
onEvent
:
@MessageDsl
suspend
T
.(
String
)
->
Unit
)
=
inline
fun
sentByOperator
(
crossinline
onEvent
:
@MessageDsl
suspend
T
.(
String
)
->
Unit
):
Listener
<
T
>
=
content
({
this
is
GroupMessage
&&
sender
.
permission
.
isOperator
()
},
onEvent
)
/**
* 如果是管理员发的消息, 就执行 [onEvent]
*/
@MessageDsl
suspend
inline
fun
sentByAdministrator
(
noinline
onEvent
:
@MessageDsl
suspend
T
.(
String
)
->
Unit
)
=
inline
fun
sentByAdministrator
(
crossinline
onEvent
:
@MessageDsl
suspend
T
.(
String
)
->
Unit
):
Listener
<
T
>
=
content
({
this
is
GroupMessage
&&
sender
.
permission
.
isAdministrator
()
},
onEvent
)
/**
* 如果是群主发的消息, 就执行 [onEvent]
*/
@MessageDsl
suspend
inline
fun
sentByOwner
(
noinline
onEvent
:
@MessageDsl
suspend
T
.(
String
)
->
Unit
)
=
inline
fun
sentByOwner
(
crossinline
onEvent
:
@MessageDsl
suspend
T
.(
String
)
->
Unit
):
Listener
<
T
>
=
content
({
this
is
GroupMessage
&&
sender
.
permission
.
isOwner
()
},
onEvent
)
/**
* 如果是来自这个群的消息, 就执行 [onEvent]
*/
@MessageDsl
suspend
inline
fun
sentFrom
(
id
:
Long
,
noinline
onEvent
:
@MessageDsl
suspend
T
.(
String
)
->
Unit
)
=
inline
fun
sentFrom
(
id
:
Long
,
crossinline
onEvent
:
@MessageDsl
suspend
T
.(
String
)
->
Unit
):
Listener
<
T
>
=
content
({
if
(
this
is
GroupMessage
)
group
.
id
==
id
else
false
},
onEvent
)
/**
* 如果消息内容包含 [M] 类型的 [Message], 就执行 [onEvent]
*/
@MessageDsl
suspend
inline
fun
<
reified
M
:
Message
>
has
(
noinline
onEvent
:
@MessageDsl
suspend
T
.(
String
)
->
Unit
)
=
subscriber
{
if
(
message
.
any
{
it
::
class
==
M
::
class
}) on
Event
(
this
)
}
inline
fun
<
reified
M
:
Message
>
has
(
crossinline
onEvent
:
@MessageDsl
suspend
T
.(
String
)
->
Unit
):
Listener
<
T
>
=
subscriber
{
if
(
message
.
any
{
it
::
class
==
M
::
class
}) on
Event
(
this
,
this
.
message
.
toString
()
)
}
/**
* 如果 [filter] 返回 `true` 就执行 `onEvent`
*/
@MessageDsl
suspend
inline
fun
content
(
noinline
filter
:
T
.(
String
)
->
Boolean
,
noinline
onEvent
:
@MessageDsl
suspend
T
.(
String
)
->
Unit
)
=
subscriber
{
if
(
this
.
filter
(
message
.
stringValue
))
onEvent
(
this
)
}
inline
fun
content
(
crossinline
filter
:
T
.(
String
)
->
Boolean
,
crossinline
onEvent
:
@MessageDsl
suspend
T
.(
String
)
->
Unit
):
Listener
<
T
>
=
subscriber
{
if
(
this
.
filter
(
message
.
toString
()))
onEvent
(
this
,
this
.
message
.
toString
()
)
}
/**
* 如果消息内容可由正则表达式匹配([Regex.matchEntire]), 就执行 `onEvent`
*/
@MessageDsl
suspend
inline
fun
matching
(
regex
:
Regex
,
no
inline
onEvent
:
@MessageDsl
suspend
T
.(
String
)
->
Unit
)
{
inline
fun
matching
(
regex
:
Regex
,
cross
inline
onEvent
:
@MessageDsl
suspend
T
.(
String
)
->
Unit
)
{
content
({
regex
.
matchEntire
(
it
)
!=
null
},
onEvent
)
}
...
...
@@ -244,7 +235,7 @@ class MessageSubscribersBuilder<T : MessagePacket<*, *>>(
* 如果消息内容可由正则表达式查找([Regex.find]), 就执行 `onEvent`
*/
@MessageDsl
suspend
inline
fun
finding
(
regex
:
Regex
,
no
inline
onEvent
:
@MessageDsl
suspend
T
.(
String
)
->
Unit
)
{
inline
fun
finding
(
regex
:
Regex
,
cross
inline
onEvent
:
@MessageDsl
suspend
T
.(
String
)
->
Unit
)
{
content
({
regex
.
find
(
it
)
!=
null
},
onEvent
)
}
...
...
@@ -252,7 +243,7 @@ class MessageSubscribersBuilder<T : MessagePacket<*, *>>(
* 若消息内容包含 [this] 则回复 [reply]
*/
@MessageDsl
suspend
inline
infix
fun
String
.
containsReply
(
reply
:
String
)
=
infix
fun
String
.
containsReply
(
reply
:
String
)
=
content
({
this
@
containsReply
in
it
})
{
this
@
content
.
reply
(
reply
)
}
/**
...
...
@@ -263,7 +254,7 @@ class MessageSubscribersBuilder<T : MessagePacket<*, *>>(
* @param replier 若返回 [Message] 则直接发送; 若返回 [Unit] 则不回复; 其他情况则 [Any.toString] 后回复
*/
@MessageDsl
suspend
inline
infix
fun
String
.
containsReply
(
noinline
replier
:
AnyReplier
<
T
>
)
=
inline
infix
fun
String
.
containsReply
(
crossinline
replier
:
@MessageDsl
suspend
T
.(
String
)
->
Any
?
)
=
content
({
this
@
containsReply
in
it
})
{
@Suppress
(
"DSL_SCOPE_VIOLATION_WARNING"
)
// false negative warning
executeAndReply
(
replier
)
...
...
@@ -277,7 +268,7 @@ class MessageSubscribersBuilder<T : MessagePacket<*, *>>(
* @param replier 若返回 [Message] 则直接发送; 若返回 [Unit] 则不回复; 其他情况则 [Any.toString] 后回复
*/
@MessageDsl
suspend
inline
infix
fun
Regex
.
matchingReply
(
noinline
replier
:
AnyReplier
<
T
>
)
{
inline
infix
fun
Regex
.
matchingReply
(
crossinline
replier
:
@MessageDsl
suspend
T
.(
String
)
->
Any
?
)
{
content
({
this
@
matchingReply
.
matchEntire
(
it
)
!=
null
})
{
@Suppress
(
"DSL_SCOPE_VIOLATION_WARNING"
)
// false negative warning
executeAndReply
(
replier
)
...
...
@@ -292,7 +283,7 @@ class MessageSubscribersBuilder<T : MessagePacket<*, *>>(
* @param replier 若返回 [Message] 则直接发送; 若返回 [Unit] 则不回复; 其他情况则 [Any.toString] 后回复
*/
@MessageDsl
suspend
inline
infix
fun
Regex
.
findingReply
(
noinline
replier
:
AnyReplier
<
T
>
)
{
inline
infix
fun
Regex
.
findingReply
(
crossinline
replier
:
@MessageDsl
suspend
T
.(
String
)
->
Any
?
)
{
content
({
this
@
findingReply
.
find
(
it
)
!=
null
})
{
@Suppress
(
"DSL_SCOPE_VIOLATION_WARNING"
)
// false negative warning
executeAndReply
(
replier
)
...
...
@@ -313,7 +304,7 @@ class MessageSubscribersBuilder<T : MessagePacket<*, *>>(
* @param replier 若返回 [Message] 则直接发送; 若返回 [Unit] 则不回复; 其他类型则 [Any.toString] 后回复
*/
@MessageDsl
suspend
inline
infix
fun
String
.
startsWithReply
(
noinline
replier
:
AnyReplier
<
T
>
)
{
inline
infix
fun
String
.
startsWithReply
(
crossinline
replier
:
@MessageDsl
suspend
T
.(
String
)
->
Any
?
)
{
val
toCheck
=
this
.
trimStart
()
content
({
it
.
trimStart
().
startsWith
(
toCheck
)
})
{
@Suppress
(
"DSL_SCOPE_VIOLATION_WARNING"
)
// false negative warning
...
...
@@ -337,7 +328,7 @@ class MessageSubscribersBuilder<T : MessagePacket<*, *>>(
* @param replier 若返回 [Message] 则直接发送; 若返回 [Unit] 则不回复; 其他情况则 [Any.toString] 后回复
*/
@MessageDsl
suspend
inline
infix
fun
String
.
endswithReply
(
noinline
replier
:
AnyReplier
<
T
>
)
{
inline
infix
fun
String
.
endswithReply
(
crossinline
replier
:
@MessageDsl
suspend
T
.(
String
)
->
Any
?
)
{
val
toCheck
=
this
.
trimEnd
()
content
({
it
.
endsWith
(
this
@
endswithReply
)
})
{
@Suppress
(
"DSL_SCOPE_VIOLATION_WARNING"
)
// false negative warning
...
...
@@ -348,20 +339,20 @@ class MessageSubscribersBuilder<T : MessagePacket<*, *>>(
}
@MessageDsl
suspend
inline
infix
fun
String
.
reply
(
reply
:
String
)
=
case
(
this
)
{
infix
fun
String
.
reply
(
reply
:
String
)
=
case
(
this
)
{
this
@
case
.
reply
(
reply
)
}
@MessageDsl
suspend
inline
infix
fun
String
.
reply
(
noinline
replier
:
AnyReplier
<
T
>
)
=
case
(
this
)
{
inline
infix
fun
String
.
reply
(
crossinline
replier
:
@MessageDsl
suspend
T
.(
String
)
->
Any
?
)
=
case
(
this
)
{
@Suppress
(
"DSL_SCOPE_VIOLATION_WARNING"
)
// false negative warning
executeAndReply
(
replier
)
}
@PublishedApi
@Suppress
(
"
NOTHING_TO_INLINE"
)
internal
suspend
inline
fun
T
.
executeAndReply
(
noinline
replier
:
AnyReplier
<
T
>
)
{
when
(
val
message
=
replier
(
this
))
{
@Suppress
(
"
REDUNDANT_INLINE_SUSPEND_FUNCTION_TYPE"
)
// false positive
internal
suspend
inline
fun
T
.
executeAndReply
(
replier
:
@MessageDsl
suspend
T
.(
String
)
->
Any
?
)
{
when
(
val
message
=
replier
(
this
,
this
.
message
.
toString
()
))
{
is
Message
->
this
.
reply
(
message
)
is
Unit
->
{
...
...
@@ -371,10 +362,10 @@ class MessageSubscribersBuilder<T : MessagePacket<*, *>>(
}
/* 易产生迷惑感
suspend inline fun replyCase(equals: String, trim: Boolean = true, noinline
replier: MessageReplier<T>) = case(equals, trim) { reply(replier(this)) }
suspend inline fun replyContains(value: String, noinline
replier: MessageReplier<T>) = content({ value in it }) { replier(this) }
suspend inline fun replyStartsWith(value: String, noinline
replier: MessageReplier<T>) = content({ it.startsWith(value) }) { replier(this) }
suspend inline fun replyEndsWith(value: String, noinline
replier: MessageReplier<T>) = content({ it.endsWith(value) }) { replier(this) }
fun replyCase(equals: String, trim: Boolean = true,
replier: MessageReplier<T>) = case(equals, trim) { reply(replier(this)) }
fun replyContains(value: String,
replier: MessageReplier<T>) = content({ value in it }) { replier(this) }
fun replyStartsWith(value: String,
replier: MessageReplier<T>) = content({ it.startsWith(value) }) { replier(this) }
fun replyEndsWith(value: String,
replier: MessageReplier<T>) = content({ it.endsWith(value) }) { replier(this) }
*/
}
...
...
mirai-core/src/commonMain/kotlin/net.mamoe.mirai/event/Subscribable.kt
View file @
22175eaa
...
...
@@ -3,9 +3,6 @@
package
net.mamoe.mirai.event
import
net.mamoe.mirai.event.internal.broadcastInternal
import
net.mamoe.mirai.utils.DefaultLogger
import
net.mamoe.mirai.utils.MiraiLogger
import
net.mamoe.mirai.utils.withSwitch
/**
* 可被监听的.
...
...
@@ -13,9 +10,7 @@ import net.mamoe.mirai.utils.withSwitch
* 可以是任何 class 或 object.
*
* @see subscribeAlways
* @see subscribeOnce
* @see subscribeWhile
* @see subscribeAll
*
* @see subscribeMessages
*/
...
...
@@ -47,19 +42,6 @@ abstract class Event : Subscribable {
fun
cancel
()
{
cancelled
=
true
}
init
{
if
(
EventDebuggingFlag
)
{
EventDebugLogger
.
debug
(
this
::
class
.
simpleName
+
" created"
)
}
}
}
internal
object
EventDebugLogger
:
MiraiLogger
by
DefaultLogger
(
"Event"
).
withSwitch
(
EventDebuggingFlag
)
private
val
EventDebuggingFlag
:
Boolean
by
lazy
{
// avoid 'Condition is always true'
false
}
/**
...
...
@@ -74,19 +56,10 @@ interface Cancellable : Subscribable {
/**
* 广播一个事件的唯一途径.
*/
@Suppress
(
"UNCHECKED_CAST"
)
suspend
fun
<
E
:
Subscribable
>
E
.
broadcast
():
E
{
if
(
EventDebuggingFlag
)
{
EventDebugLogger
.
debug
(
this
::
class
.
simpleName
+
" pre broadcast"
)
}
try
{
@Suppress
(
"EXPERIMENTAL_API_USAGE"
)
return
this
@
broadcast
.
broadcastInternal
()
}
finally
{
if
(
EventDebuggingFlag
)
{
EventDebugLogger
.
debug
(
this
::
class
.
simpleName
+
" after broadcast"
)
}
}
@Suppress
(
"EXPERIMENTAL_API_USAGE"
)
this
@
broadcast
.
broadcastInternal
()
// inline, no extra cost
return
this
}
/**
...
...
mirai-core/src/commonMain/kotlin/net.mamoe.mirai/event/Subscribers.kt
View file @
22175eaa
@
file
:
Suppress
(
"unused"
)
package
net.mamoe.mirai.event
import
kotlinx.coroutines.CompletableJob
import
kotlinx.coroutines.CoroutineScope
import
kotlinx.coroutines.GlobalScope
import
net.mamoe.mirai.Bot
import
net.mamoe.mirai.event.internal.Handler
import
net.mamoe.mirai.event.internal.Listener
import
net.mamoe.mirai.event.internal.subscribeInternal
import
kotlin.jvm.JvmStatic
import
kotlin.reflect.KClass
/*
* 该文件为所有的订阅事件的方法.
...
...
@@ -14,125 +13,112 @@ import kotlin.reflect.KClass
/**
* 订阅者的状态
*/
// Not using enum for Android
inline
class
ListeningStatus
(
inline
val
listening
:
Boolean
)
{
companion
object
{
/**
* 表示继续监听
*/
@JvmStatic
val
LISTENING
=
ListeningStatus
(
true
)
/**
* 表示已停止
*/
@JvmStatic
val
STOPPED
=
ListeningStatus
(
false
)
}
*/
enum
class
ListeningStatus
{
/**
* 表示继续监听
*/
LISTENING
,
/**
* 表示已停止
*/
STOPPED
}
/**
* 事件监听器.
* 由 [subscribe] 等方法返回.
*/
interface
Listener
<
in
E
:
Subscribable
>
:
CompletableJob
{
suspend
fun
onEvent
(
event
:
E
):
ListeningStatus
}
// region 顶层方法
// region 顶层方法
创建当前 coroutineContext 下的子 Job
/**
* 订阅所有 [E] 及其子类事件.
* 在指定的 [CoroutineScope] 下订阅所有 [E] 及其子类事件.
* 每当 [事件广播][Subscribable.broadcast] 时, [handler] 都会被执行.
*
* 当 [handler] 返回 [ListeningStatus.STOPPED] 时停止监听.
* 或 [Listener] complete 时结束.
*
*
* **注意**: 这个函数返回 [Listener], 它是一个 [CompletableJob]. 如果不手动 [CompletableJob.complete], 它将会阻止当前 [CoroutineScope] 结束.
* 例如:
* ```kotlin
* runBlocking { // this: CoroutineScope
* subscribe<Subscribable> { /* 一些处理 */
}
// 返回 Listener, 即 CompletableJob
*
}
*
foo
()
*
```
*
`runBlocking`
不会结束
,
也就是下一行
`foo
()
`
不会被执行
.
直到监听时创建的
`Listener`
被停止
.
*
* 将以当前协程的 job 为父 job 启动监听, 因此, 当当前协程运行结束后, 监听也会结束.
* [handler] 将会有当前协程上下文执行, 即会被调用 [subscribe] 时的协程调度器执行
*
*
要创建一个全局都存在的监听
,
即守护协程
,
请在
[
GlobalScope
]
下调用本函数
:
*
``
`kotlin
*
GlobalScope
.
subscribe
<
Subscribable
>
{
/* 一些处理 */
}
*
```
*
*
*
要创建一个仅在机器人在线时的监听
,
请在
[
Bot
]
下调用本函数
(
因为
[
Bot
]
也实现
[
CoroutineScope
]):
*
``
`kotlin
*
bot
.
subscribe
<
Subscribe
>
{
/* 一些处理 */
}
*
```
*/
suspend
inline
fun
<
reified
E
:
Subscribable
>
subscribe
(
noinline
handler
:
suspend
E
.(
E
)
->
ListeningStatus
):
Listener
<
E
>
=
E
::
class
.
subscribe
(
handler
)
suspend
inline
fun
<
reified
E
:
Subscribable
>
subscribeAlways
(
noinline
listener
:
suspend
E
.(
E
)
->
Unit
):
Listener
<
E
>
=
E
::
class
.
subscribeAlways
(
listener
)
suspend
inline
fun
<
reified
E
:
Subscribable
>
subscribeOnce
(
noinline
listener
:
suspend
E
.(
E
)
->
Unit
):
Listener
<
E
>
=
E
::
class
.
subscribeOnce
(
listener
)
suspend
inline
fun
<
reified
E
:
Subscribable
,
T
>
subscribeUntil
(
valueIfStop
:
T
,
noinline
listener
:
suspend
E
.(
E
)
->
T
):
Listener
<
E
>
=
E
::
class
.
subscribeUntil
(
valueIfStop
,
listener
)
suspend
inline
fun
<
reified
E
:
Subscribable
>
subscribeUntilFalse
(
noinline
listener
:
suspend
E
.(
E
)
->
Boolean
):
Listener
<
E
>
=
E
::
class
.
subscribeUntilFalse
(
listener
)
suspend
inline
fun
<
reified
E
:
Subscribable
>
subscribeUntilTrue
(
noinline
listener
:
suspend
E
.(
E
)
->
Boolean
):
Listener
<
E
>
=
E
::
class
.
subscribeUntilTrue
(
listener
)
suspend
inline
fun
<
reified
E
:
Subscribable
>
subscribeUntilNull
(
noinline
listener
:
suspend
E
.(
E
)
->
Any
?):
Listener
<
E
>
=
E
::
class
.
subscribeUntilNull
(
listener
)
suspend
inline
fun
<
reified
E
:
Subscribable
,
T
>
subscribeWhile
(
valueIfContinue
:
T
,
noinline
listener
:
suspend
E
.(
E
)
->
T
):
Listener
<
E
>
=
E
::
class
.
subscribeWhile
(
valueIfContinue
,
listener
)
suspend
inline
fun
<
reified
E
:
Subscribable
>
subscribeWhileFalse
(
noinline
listener
:
suspend
E
.(
E
)
->
Boolean
):
Listener
<
E
>
=
E
::
class
.
subscribeWhileFalse
(
listener
)
suspend
inline
fun
<
reified
E
:
Subscribable
>
subscribeWhileTrue
(
noinline
listener
:
suspend
E
.(
E
)
->
Boolean
):
Listener
<
E
>
=
E
::
class
.
subscribeWhileTrue
(
listener
)
suspend
inline
fun
<
reified
E
:
Subscribable
>
subscribeWhileNull
(
noinline
listener
:
suspend
E
.(
E
)
->
Any
?):
Listener
<
E
>
=
E
::
class
.
subscribeWhileNull
(
listener
)
// endregion
// region KClass 的扩展方法 (不推荐)
@PublishedApi
internal
suspend
fun
<
E
:
Subscribable
>
KClass
<
E
>.
subscribe
(
handler
:
suspend
E
.(
E
)
->
ListeningStatus
)
=
this
.
subscribeInternal
(
Handler
{
it
.
handler
(
it
)
})
inline
fun
<
reified
E
:
Subscribable
>
CoroutineScope
.
subscribe
(
crossinline
handler
:
suspend
E
.(
E
)
->
ListeningStatus
):
Listener
<
E
>
=
E
::
class
.
subscribeInternal
(
Handler
{
it
.
handler
(
it
)
})
@PublishedApi
internal
suspend
fun
<
E
:
Subscribable
>
KClass
<
E
>.
subscribeAlways
(
listener
:
suspend
E
.(
E
)
->
Unit
)
=
this
.
subscribeInternal
(
Handler
{
it
.
listener
(
it
);
ListeningStatus
.
LISTENING
})
@PublishedApi
internal
suspend
fun
<
E
:
Subscribable
>
KClass
<
E
>.
subscribeOnce
(
listener
:
suspend
E
.(
E
)
->
Unit
)
=
this
.
subscribeInternal
(
Handler
{
it
.
listener
(
it
);
ListeningStatus
.
STOPPED
})
@PublishedApi
internal
suspend
fun
<
E
:
Subscribable
,
T
>
KClass
<
E
>.
subscribeUntil
(
valueIfStop
:
T
,
listener
:
suspend
E
.(
E
)
->
T
)
=
subscribeInternal
(
Handler
{
if
(
it
.
listener
(
it
)
==
valueIfStop
)
ListeningStatus
.
STOPPED
else
ListeningStatus
.
LISTENING
})
@PublishedApi
internal
suspend
inline
fun
<
E
:
Subscribable
>
KClass
<
E
>.
subscribeUntilFalse
(
noinline
listener
:
suspend
E
.(
E
)
->
Boolean
)
=
subscribeUntil
(
false
,
listener
)
@PublishedApi
internal
suspend
inline
fun
<
E
:
Subscribable
>
KClass
<
E
>.
subscribeUntilTrue
(
noinline
listener
:
suspend
E
.(
E
)
->
Boolean
)
=
subscribeUntil
(
true
,
listener
)
@PublishedApi
internal
suspend
inline
fun
<
E
:
Subscribable
>
KClass
<
E
>.
subscribeUntilNull
(
noinline
listener
:
suspend
E
.(
E
)
->
Any
?)
=
subscribeUntil
(
null
,
listener
)
@PublishedApi
internal
suspend
fun
<
E
:
Subscribable
,
T
>
KClass
<
E
>.
subscribeWhile
(
valueIfContinue
:
T
,
listener
:
suspend
E
.(
E
)
->
T
)
=
subscribeInternal
(
Handler
{
if
(
it
.
listener
(
it
)
!==
valueIfContinue
)
ListeningStatus
.
STOPPED
else
ListeningStatus
.
LISTENING
})
@PublishedApi
internal
suspend
inline
fun
<
E
:
Subscribable
>
KClass
<
E
>.
subscribeWhileFalse
(
noinline
listener
:
suspend
E
.(
E
)
->
Boolean
)
=
subscribeWhile
(
false
,
listener
)
@PublishedApi
internal
suspend
inline
fun
<
E
:
Subscribable
>
KClass
<
E
>.
subscribeWhileTrue
(
noinline
listener
:
suspend
E
.(
E
)
->
Boolean
)
=
subscribeWhile
(
true
,
listener
)
@PublishedApi
internal
suspend
inline
fun
<
E
:
Subscribable
>
KClass
<
E
>.
subscribeWhileNull
(
noinline
listener
:
suspend
E
.(
E
)
->
Any
?)
=
subscribeWhile
(
null
,
listener
)
// endregion
/**
* 在指定的 [CoroutineScope] 下订阅所有 [E] 及其子类事件.
* 每当 [事件广播][Subscribable.broadcast] 时, [listener] 都会被执行.
*
* 仅当 [Listener] complete 时结束.
*
* @see subscribe 获取更多说明
*/
inline
fun
<
reified
E
:
Subscribable
>
CoroutineScope
.
subscribeAlways
(
crossinline
listener
:
suspend
E
.(
E
)
->
Unit
):
Listener
<
E
>
=
E
::
class
.
subscribeInternal
(
Handler
{
it
.
listener
(
it
);
ListeningStatus
.
LISTENING
})
// region ListenerBuilder DSL
/**
* 在指定的 [CoroutineScope] 下订阅所有 [E] 及其子类事件.
* 仅在第一次 [事件广播][Subscribable.broadcast] 时, [listener] 会被执行.
*
* 在这之前, 可通过 [Listener.complete] 来停止监听.
*
* @see subscribe 获取更多说明
*/
inline
fun
<
reified
E
:
Subscribable
>
CoroutineScope
.
subscribeOnce
(
crossinline
listener
:
suspend
E
.(
E
)
->
Unit
):
Listener
<
E
>
=
E
::
class
.
subscribeInternal
(
Handler
{
it
.
listener
(
it
);
ListeningStatus
.
STOPPED
})
/**
* 监听一个事件. 可同时进行多种方式的监听
* @see ListenerBuilder
* 在指定的 [CoroutineScope] 下订阅所有 [E] 及其子类事件.
* 每当 [事件广播][Subscribable.broadcast] 时, [listener] 都会被执行, 直到 [listener] 的返回值 [equals] 于 [valueIfStop]
*
* 可在任意时刻通过 [Listener.complete] 来停止监听.
*
* @see subscribe 获取更多说明
*/
@ListenersBuilderDsl
@PublishedApi
internal
suspend
fun
<
E
:
Subscribable
>
KClass
<
E
>.
subscribeAll
(
listeners
:
suspend
ListenerBuilder
<
E
>.()
->
Unit
)
{
listeners
(
ListenerBuilder
{
this
.
subscribeInternal
(
it
)
})
}
inline
fun
<
reified
E
:
Subscribable
,
T
>
CoroutineScope
.
subscribeUntil
(
valueIfStop
:
T
,
crossinline
listener
:
suspend
E
.(
E
)
->
T
):
Listener
<
E
>
=
E
::
class
.
subscribeInternal
(
Handler
{
if
(
it
.
listener
(
it
)
==
valueIfStop
)
ListeningStatus
.
STOPPED
else
ListeningStatus
.
LISTENING
})
/**
* 监听一个事件. 可同时进行多种方式的监听
* @see ListenerBuilder
* 在指定的 [CoroutineScope] 下订阅所有 [E] 及其子类事件.
* 每当 [事件广播][Subscribable.broadcast] 时, [listener] 都会被执行,
* 如果 [listener] 的返回值 [equals] 于 [valueIfContinue], 则继续监听, 否则停止
*
* 可在任意时刻通过 [Listener.complete] 来停止监听.
*
* @see subscribe 获取更多说明
*/
@ListenersBuilderDsl
suspend
inline
fun
<
reified
E
:
Subscribable
>
subscribeAll
(
noinline
listeners
:
suspend
ListenerBuilder
<
E
>.()
->
Unit
)
=
E
::
class
.
subscribeAll
(
listeners
)
inline
fun
<
reified
E
:
Subscribable
,
T
>
CoroutineScope
.
subscribeWhile
(
valueIfContinue
:
T
,
crossinline
listener
:
suspend
E
.(
E
)
->
T
):
Listener
<
E
>
=
E
::
class
.
subscribeInternal
(
Handler
{
if
(
it
.
listener
(
it
)
!==
valueIfContinue
)
ListeningStatus
.
STOPPED
else
ListeningStatus
.
LISTENING
})
// endregion
// region ListenerBuilder DSL
/*
/**
* 监听构建器. 可同时进行多种方式的监听
*
...
...
@@ -152,34 +138,34 @@ suspend inline fun <reified E : Subscribable> subscribeAll(noinline listeners: s
@ListenersBuilderDsl
@Suppress("MemberVisibilityCanBePrivate", "unused")
inline class ListenerBuilder<out E : Subscribable>(
@PublishedApi
internal
inline
val
handlerConsumer
:
suspend
(
Listener
<
E
>)
->
Unit
@PublishedApi internal inline val handlerConsumer:
CoroutineCoroutineScope.
(Listener<E>) -> Unit
) {
suspend
inline
fun
handler
(
noinline
listener
:
suspend
E
.(
E
)
->
ListeningStatus
)
{
fun CoroutineCoroutineScope.handler(
listener: suspend E.(E) -> ListeningStatus) {
handlerConsumer(Handler { it.listener(it) })
}
suspend
inline
fun
always
(
noinline
listener
:
suspend
E
.(
E
)
->
Unit
)
=
handler
{
listener
(
it
);
ListeningStatus
.
LISTENING
}
fun CoroutineCoroutineScope.always(
listener: suspend E.(E) -> Unit) = handler { listener(it); ListeningStatus.LISTENING }
suspend
inline
fun
<
T
>
until
(
until
:
T
,
noinline
listener
:
suspend
E
.(
E
)
->
T
)
=
fun <T> CoroutineCoroutineScope.until(until: T,
listener: suspend E.(E) -> T) =
handler { if (listener(it) == until) ListeningStatus.STOPPED else ListeningStatus.LISTENING }
suspend
inline
fun
untilFalse
(
noinline
listener
:
suspend
E
.(
E
)
->
Boolean
)
=
until
(
false
,
listener
)
suspend
inline
fun
untilTrue
(
noinline
listener
:
suspend
E
.(
E
)
->
Boolean
)
=
until
(
true
,
listener
)
suspend
inline
fun
untilNull
(
noinline
listener
:
suspend
E
.(
E
)
->
Any
?)
=
until
(
null
,
listener
)
fun CoroutineCoroutineScope.untilFalse(
listener: suspend E.(E) -> Boolean) = until(false, listener)
fun CoroutineCoroutineScope.untilTrue(
listener: suspend E.(E) -> Boolean) = until(true, listener)
fun CoroutineCoroutineScope.untilNull(
listener: suspend E.(E) -> Any?) = until(null, listener)
suspend
inline
fun
<
T
>
`while`
(
until
:
T
,
noinline
listener
:
suspend
E
.(
E
)
->
T
)
=
fun <T> CoroutineCoroutineScope.`while`(until: T,
listener: suspend E.(E) -> T) =
handler { if (listener(it) !== until) ListeningStatus.STOPPED else ListeningStatus.LISTENING }
suspend
inline
fun
whileFalse
(
noinline
listener
:
suspend
E
.(
E
)
->
Boolean
)
=
`while`
(
false
,
listener
)
suspend
inline
fun
whileTrue
(
noinline
listener
:
suspend
E
.(
E
)
->
Boolean
)
=
`while`
(
true
,
listener
)
suspend
inline
fun
whileNull
(
noinline
listener
:
suspend
E
.(
E
)
->
Any
?)
=
`while`
(
null
,
listener
)
fun CoroutineCoroutineScope.whileFalse(
listener: suspend E.(E) -> Boolean) = `while`(false, listener)
fun CoroutineCoroutineScope.whileTrue(
listener: suspend E.(E) -> Boolean) = `while`(true, listener)
fun CoroutineCoroutineScope.whileNull(
listener: suspend E.(E) -> Any?) = `while`(null, listener)
suspend
inline
fun
once
(
noinline
listener
:
suspend
E
.(
E
)
->
Unit
)
=
handler
{
listener
(
it
);
ListeningStatus
.
STOPPED
}
fun CoroutineCoroutineScope.once(
listener: suspend E.(E) -> Unit) = handler { listener(it); ListeningStatus.STOPPED }
}
@DslMarker
annotation class ListenersBuilderDsl
*/
// endregion
\ No newline at end of file
mirai-core/src/commonMain/kotlin/net.mamoe.mirai/event/SubscribersWithBot.kt
deleted
100644 → 0
View file @
e56ef7ba
@
file
:
Suppress
(
"unused"
)
package
net.mamoe.mirai.event
import
net.mamoe.mirai.Bot
import
net.mamoe.mirai.event.internal.HandlerWithSession
import
net.mamoe.mirai.event.internal.Listener
import
net.mamoe.mirai.event.internal.subscribeInternal
import
kotlin.reflect.KClass
/**
* 该文件为所有的含 Bot 的事件的订阅方法
*
* 与不含 bot 的相比, 在监听时将会有 `this: Bot`
* 在 demo 中找到实例可很快了解区别.
*/
// region 顶层方法
suspend
inline
fun
<
reified
E
:
Subscribable
>
Bot
.
subscribe
(
noinline
handler
:
suspend
Bot
.(
E
)
->
ListeningStatus
):
Listener
<
E
>
=
E
::
class
.
subscribe
(
this
,
handler
)
suspend
inline
fun
<
reified
E
:
Subscribable
>
Bot
.
subscribeAlways
(
noinline
listener
:
suspend
Bot
.(
E
)
->
Unit
):
Listener
<
E
>
=
E
::
class
.
subscribeAlways
(
this
,
listener
)
suspend
inline
fun
<
reified
E
:
Subscribable
>
Bot
.
subscribeOnce
(
noinline
listener
:
suspend
Bot
.(
E
)
->
Unit
):
Listener
<
E
>
=
E
::
class
.
subscribeOnce
(
this
,
listener
)
suspend
inline
fun
<
reified
E
:
Subscribable
,
T
>
Bot
.
subscribeUntil
(
valueIfStop
:
T
,
noinline
listener
:
suspend
Bot
.(
E
)
->
T
):
Listener
<
E
>
=
E
::
class
.
subscribeUntil
(
this
,
valueIfStop
,
listener
)
suspend
inline
fun
<
reified
E
:
Subscribable
>
Bot
.
subscribeUntilFalse
(
noinline
listener
:
suspend
Bot
.(
E
)
->
Boolean
):
Listener
<
E
>
=
E
::
class
.
subscribeUntilFalse
(
this
,
listener
)
suspend
inline
fun
<
reified
E
:
Subscribable
>
Bot
.
subscribeUntilTrue
(
noinline
listener
:
suspend
Bot
.(
E
)
->
Boolean
):
Listener
<
E
>
=
E
::
class
.
subscribeUntilTrue
(
this
,
listener
)
suspend
inline
fun
<
reified
E
:
Subscribable
>
Bot
.
subscribeUntilNull
(
noinline
listener
:
suspend
Bot
.(
E
)
->
Any
?):
Listener
<
E
>
=
E
::
class
.
subscribeUntilNull
(
this
,
listener
)
suspend
inline
fun
<
reified
E
:
Subscribable
,
T
>
Bot
.
subscribeWhile
(
valueIfContinue
:
T
,
noinline
listener
:
suspend
Bot
.(
E
)
->
T
):
Listener
<
E
>
=
E
::
class
.
subscribeWhile
(
this
,
valueIfContinue
,
listener
)
suspend
inline
fun
<
reified
E
:
Subscribable
>
Bot
.
subscribeWhileFalse
(
noinline
listener
:
suspend
Bot
.(
E
)
->
Boolean
):
Listener
<
E
>
=
E
::
class
.
subscribeWhileFalse
(
this
,
listener
)
suspend
inline
fun
<
reified
E
:
Subscribable
>
Bot
.
subscribeWhileTrue
(
noinline
listener
:
suspend
Bot
.(
E
)
->
Boolean
):
Listener
<
E
>
=
E
::
class
.
subscribeWhileTrue
(
this
,
listener
)
suspend
inline
fun
<
reified
E
:
Subscribable
>
Bot
.
subscribeWhileNull
(
noinline
listener
:
suspend
Bot
.(
E
)
->
Any
?):
Listener
<
E
>
=
E
::
class
.
subscribeWhileNull
(
this
,
listener
)
// endregion
// region KClass 的扩展方法 (仅内部使用)
@PublishedApi
internal
suspend
fun
<
E
:
Subscribable
>
KClass
<
E
>.
subscribe
(
bot
:
Bot
,
handler
:
suspend
Bot
.(
E
)
->
ListeningStatus
)
=
this
.
subscribeInternal
(
HandlerWithSession
(
bot
,
handler
))
@PublishedApi
internal
suspend
fun
<
E
:
Subscribable
>
KClass
<
E
>.
subscribeAlways
(
bot
:
Bot
,
listener
:
suspend
Bot
.(
E
)
->
Unit
)
=
this
.
subscribeInternal
(
HandlerWithSession
(
bot
)
{
listener
(
it
);
ListeningStatus
.
LISTENING
})
@PublishedApi
internal
suspend
fun
<
E
:
Subscribable
>
KClass
<
E
>.
subscribeOnce
(
bot
:
Bot
,
listener
:
suspend
Bot
.(
E
)
->
Unit
)
=
this
.
subscribeInternal
(
HandlerWithSession
(
bot
)
{
listener
(
it
);
ListeningStatus
.
STOPPED
})
@PublishedApi
internal
suspend
fun
<
E
:
Subscribable
,
T
>
KClass
<
E
>.
subscribeUntil
(
bot
:
Bot
,
valueIfStop
:
T
,
listener
:
suspend
Bot
.(
E
)
->
T
)
=
subscribeInternal
(
HandlerWithSession
(
bot
)
{
if
(
listener
(
it
)
===
valueIfStop
)
ListeningStatus
.
STOPPED
else
ListeningStatus
.
LISTENING
})
@PublishedApi
internal
suspend
inline
fun
<
E
:
Subscribable
>
KClass
<
E
>.
subscribeUntilFalse
(
bot
:
Bot
,
noinline
listener
:
suspend
Bot
.(
E
)
->
Boolean
)
=
subscribeUntil
(
bot
,
false
,
listener
)
@PublishedApi
internal
suspend
inline
fun
<
E
:
Subscribable
>
KClass
<
E
>.
subscribeUntilTrue
(
bot
:
Bot
,
noinline
listener
:
suspend
Bot
.(
E
)
->
Boolean
)
=
subscribeUntil
(
bot
,
true
,
listener
)
@PublishedApi
internal
suspend
inline
fun
<
E
:
Subscribable
>
KClass
<
E
>.
subscribeUntilNull
(
bot
:
Bot
,
noinline
listener
:
suspend
Bot
.(
E
)
->
Any
?)
=
subscribeUntil
(
bot
,
null
,
listener
)
@PublishedApi
internal
suspend
fun
<
E
:
Subscribable
,
T
>
KClass
<
E
>.
subscribeWhile
(
bot
:
Bot
,
valueIfContinue
:
T
,
listener
:
suspend
Bot
.(
E
)
->
T
)
=
subscribeInternal
(
HandlerWithSession
(
bot
)
{
if
(
listener
(
it
)
!==
valueIfContinue
)
ListeningStatus
.
STOPPED
else
ListeningStatus
.
LISTENING
})
@PublishedApi
internal
suspend
inline
fun
<
E
:
Subscribable
>
KClass
<
E
>.
subscribeWhileFalse
(
bot
:
Bot
,
noinline
listener
:
suspend
Bot
.(
E
)
->
Boolean
)
=
subscribeWhile
(
bot
,
false
,
listener
)
@PublishedApi
internal
suspend
inline
fun
<
E
:
Subscribable
>
KClass
<
E
>.
subscribeWhileTrue
(
bot
:
Bot
,
noinline
listener
:
suspend
Bot
.(
E
)
->
Boolean
)
=
subscribeWhile
(
bot
,
true
,
listener
)
@PublishedApi
internal
suspend
inline
fun
<
E
:
Subscribable
>
KClass
<
E
>.
subscribeWhileNull
(
bot
:
Bot
,
noinline
listener
:
suspend
Bot
.(
E
)
->
Any
?)
=
subscribeWhile
(
bot
,
null
,
listener
)
// endregion
\ No newline at end of file
mirai-core/src/commonMain/kotlin/net.mamoe.mirai/event/internal/InternalEventListeners.kt
View file @
22175eaa
package
net.mamoe.mirai.event.internal
import
kotlinx.coroutines.CompletableJob
import
kotlinx.coroutines.CoroutineScope
import
kotlinx.coroutines.Job
import
kotlinx.coroutines.sync.Mutex
import
kotlinx.coroutines.sync.withLock
import
kotlinx.coroutines.withContext
import
net.mamoe.mirai.
Bot
import
net.mamoe.mirai.
event.Listener
import
net.mamoe.mirai.event.ListeningStatus
import
net.mamoe.mirai.event.Subscribable
import
net.mamoe.mirai.event.events.BotEvent
import
net.mamoe.mirai.utils.LockFreeLinkedList
import
net.mamoe.mirai.utils.io.logStacktrace
import
net.mamoe.mirai.utils.unsafeWeakRef
import
kotlin.coroutines.CoroutineContext
import
kotlin.coroutines.coroutineContext
import
kotlin.jvm.JvmField
import
kotlin.reflect.KClass
import
kotlin.reflect.KFunction
/**
* 设置为 `true` 以关闭事件.
...
...
@@ -24,15 +19,8 @@ import kotlin.reflect.KFunction
*/
var
EventDisabled
=
false
/**
* 监听和广播实现.
* 它会首先检查这个事件是否正在被广播
* - 如果是, 则将新的监听者放入缓存中. 在当前广播结束后转移到主列表 (通过一个协程完成)
* - 如果不是, 则直接将新的监听者放入主列表
*
* @author Him188moe
*/
// inline to avoid a Continuation creation
internal
suspend
inline
fun
<
L
:
Listener
<
E
>,
E
:
Subscribable
>
KClass
<
E
>.
subscribeInternal
(
listener
:
L
):
L
{
@PublishedApi
internal
fun
<
L
:
Listener
<
E
>,
E
:
Subscribable
>
KClass
<
out
E
>.
subscribeInternal
(
listener
:
L
):
L
{
this
.
listeners
().
addLast
(
listener
)
return
listener
}
...
...
@@ -40,18 +28,15 @@ internal suspend inline fun <L : Listener<E>, E : Subscribable> KClass<E>.subscr
/**
* 事件监听器.
*
* 它实现 [CompletableJob] 接口,
* 可通过 [CompletableJob.complete] 来正常结束监听, 或 [CompletableJob.completeExceptionally] 来异常地结束监听.
*
* @author Him188moe
*/
sealed
class
Listener
<
in
E
:
Subscribable
>
:
CompletableJob
{
abstract
suspend
fun
onEvent
(
event
:
E
):
ListeningStatus
internal
sealed
class
ListenerImpl
<
in
E
:
Subscribable
>
:
Listener
<
E
>
{
abstract
override
suspend
fun
onEvent
(
event
:
E
):
ListeningStatus
}
@PublishedApi
@Suppress
(
"FunctionName"
)
internal
suspend
inline
fun
<
E
:
Subscribable
>
Handler
(
noinline
handler
:
suspend
(
E
)
->
ListeningStatus
):
Handler
<
E
>
{
internal
fun
<
E
:
Subscribable
>
CoroutineScope
.
Handler
(
handler
:
suspend
(
E
)
->
ListeningStatus
):
Handler
<
E
>
{
return
Handler
(
coroutineContext
[
Job
],
coroutineContext
,
handler
)
}
...
...
@@ -60,14 +45,15 @@ internal suspend inline fun <E : Subscribable> Handler(noinline handler: suspend
*/
@PublishedApi
internal
class
Handler
<
in
E
:
Subscribable
>
@PublishedApi
internal
constructor
(
parentJob
:
Job
?,
private
val
c
ontext
:
CoroutineContext
,
@JvmField
val
handler
:
suspend
(
E
)
->
ListeningStatus
)
:
Listener
<
E
>(),
CompletableJob
by
Job
(
parentJob
)
{
@PublishedApi
internal
constructor
(
parentJob
:
Job
?,
private
val
subscriberC
ontext
:
CoroutineContext
,
@JvmField
val
handler
:
suspend
(
E
)
->
ListeningStatus
)
:
Listener
Impl
<
E
>(),
CompletableJob
by
Job
(
parentJob
)
{
override
suspend
fun
onEvent
(
event
:
E
):
ListeningStatus
{
if
(
isCompleted
||
isCancelled
)
return
ListeningStatus
.
STOPPED
if
(!
isActive
)
return
ListeningStatus
.
LISTENING
return
try
{
withContext
(
context
)
{
handler
.
invoke
(
event
)
}.
also
{
if
(
it
==
ListeningStatus
.
STOPPED
)
this
.
complete
()
}
// Inherit context.
withContext
(
subscriberContext
)
{
handler
.
invoke
(
event
)
}.
also
{
if
(
it
==
ListeningStatus
.
STOPPED
)
this
.
complete
()
}
}
catch
(
e
:
Throwable
)
{
e
.
logStacktrace
()
//this.completeExceptionally(e)
...
...
@@ -76,96 +62,51 @@ internal class Handler<in E : Subscribable>
}
}
@PublishedApi
@Suppress
(
"FunctionName"
)
internal
suspend
inline
fun
<
E
:
Subscribable
>
HandlerWithSession
(
bot
:
Bot
,
noinline
handler
:
suspend
Bot
.(
E
)
->
ListeningStatus
):
HandlerWithSession
<
E
>
{
return
HandlerWithSession
(
bot
,
coroutineContext
[
Job
],
coroutineContext
,
handler
)
}
/**
* 带有 bot 筛选的监听器.
* 所有的非 [BotEvent] 的事件都不会被处理
* 所有的 [BotEvent.bot] `!==` `bot` 的事件都不会被处理
*/
@PublishedApi
internal
class
HandlerWithSession
<
E
:
Subscribable
>
@PublishedApi
internal
constructor
(
bot
:
Bot
,
parentJob
:
Job
?,
private
val
context
:
CoroutineContext
,
@JvmField
val
handler
:
suspend
Bot
.(
E
)
->
ListeningStatus
)
:
Listener
<
E
>(),
CompletableJob
by
Job
(
parentJob
)
{
val
bot
:
Bot
by
bot
.
unsafeWeakRef
()
override
suspend
fun
onEvent
(
event
:
E
):
ListeningStatus
{
if
(
isCompleted
||
isCancelled
)
return
ListeningStatus
.
STOPPED
if
(!
isActive
)
return
ListeningStatus
.
LISTENING
if
(
event
!
is
BotEvent
||
event
.
bot
!==
bot
)
return
ListeningStatus
.
LISTENING
return
try
{
withContext
(
context
)
{
bot
.
handler
(
event
)
}.
also
{
if
(
it
==
ListeningStatus
.
STOPPED
)
complete
()
}
}
catch
(
e
:
Throwable
)
{
e
.
logStacktrace
()
//completeExceptionally(e)
complete
()
ListeningStatus
.
STOPPED
}
}
}
/**
* 这个事件类的监听器 list
*/
internal
suspend
fun
<
E
:
Subscribable
>
KClass
<
out
E
>.
listeners
():
EventListeners
<
E
>
=
EventListenerManger
.
get
(
this
)
internal
class
EventListeners
<
E
:
Subscribable
>
:
LockFreeLinkedList
<
Listener
<
E
>>()
{
init
{
this
::
class
.
members
.
filterIsInstance
<
KFunction
<
*
>>().
forEach
{
if
(
it
.
name
==
"add"
)
{
it
.
isExternal
}
}
}
}
internal
fun
<
E
:
Subscribable
>
KClass
<
out
E
>.
listeners
():
EventListeners
<
E
>
=
EventListenerManger
.
get
(
this
)
internal
class
EventListeners
<
E
:
Subscribable
>
:
LockFreeLinkedList
<
Listener
<
E
>>()
/**
* 管理每个事件 class 的 [EventListeners].
* [EventListeners] 是 lazy 的: 它们只会在被需要的时候才创建和存储.
*/
internal
object
EventListenerManger
{
private
val
registries
:
MutableMap
<
KClass
<
out
Subscribable
>,
EventListeners
<
*
>>
=
mutableMapOf
()
private
val
registriesMutex
=
Mutex
()
private
data class
Registry
<
E
:
Subscribable
>(
val
clazz
:
KClass
<
E
>,
val
listeners
:
EventListeners
<
E
>)
@Suppress
(
"UNCHECKED_CAST"
)
internal
suspend
fun
<
E
:
Subscribable
>
get
(
clazz
:
KClass
<
out
E
>):
EventListeners
<
E
>
=
if
(
registries
.
containsKey
(
clazz
))
registries
[
clazz
]
as
EventListeners
<
E
>
else
registriesMutex
.
withLock
{
EventListeners
<
E
>().
let
{
registries
[
clazz
]
=
it
return
it
}
}
private
val
registries
=
LockFreeLinkedList
<
Registry
<
*
>>()
@Suppress
(
"UNCHECKED_CAST"
)
internal
fun
<
E
:
Subscribable
>
get
(
clazz
:
KClass
<
out
E
>):
EventListeners
<
E
>
{
return
registries
.
filteringGetOrAdd
({
it
.
clazz
==
clazz
})
{
Registry
(
clazz
,
EventListeners
()
)
}.
listeners
as
EventListeners
<
E
>
}
}
// inline: NO extra Continuation
internal
suspend
inline
fun
<
E
:
Subscribable
>
E
.
broadcastInternal
():
E
{
if
(
EventDisabled
)
return
this
internal
suspend
inline
fun
Subscribable
.
broadcastInternal
()
{
if
(
EventDisabled
)
return
callAndRemoveIfRequired
(
this
::
class
.
listeners
())
this
::
class
.
supertypes
.
forEach
{
superType
->
if
(
Subscribable
::
class
.
isInstance
(
superType
))
{
// the super type is a child of Subscribable, then we can cast.
val
superListeners
=
@Suppress
(
"UNCHECKED_CAST"
)
callAndRemoveIfRequired
((
superType
.
classifier
as
KClass
<
out
Subscribable
>).
listeners
())
}
(
superType
.
classifier
as
?
KClass
<
out
Subscribable
>)
?.
listeners
()
?:
return
// return if super type is not Subscribable
callAndRemoveIfRequired
(
superListeners
)
}
return
this
return
}
private
suspend
inline
fun
<
E
:
Subscribable
>
E
.
callAndRemoveIfRequired
(
listeners
:
EventListeners
<
E
>)
{
// atomic foreach
listeners
.
forEach
{
if
(
it
.
onEvent
(
this
)
==
ListeningStatus
.
STOPPED
)
{
listeners
.
remove
(
it
)
// atomic remove
...
...
mirai-core/src/jvmTest/kotlin/net/mamoe/mirai/event/EventTests.kt
0 → 100644
View file @
22175eaa
package
net.mamoe.mirai.event
import
kotlinx.coroutines.runBlocking
import
net.mamoe.mirai.test.shouldBeEqualTo
import
kotlin.system.exitProcess
import
kotlin.test.Test
class
TestEvent
:
Subscribable
{
var
triggered
=
false
}
class
EventTests
{
@Test
fun
testSubscribeInplace
()
{
runBlocking
{
val
subscriber
=
subscribeAlways
<
TestEvent
>
{
triggered
=
true
println
(
"Triggered"
)
}
TestEvent
().
broadcast
().
triggered
shouldBeEqualTo
true
subscriber
.
complete
()
println
(
"finished"
)
}
}
@Test
fun
testSubscribeGlobalScope
()
{
runBlocking
{
TestEvent
().
broadcast
().
triggered
shouldBeEqualTo
true
println
(
"finished"
)
}
}
companion
object
{
@JvmStatic
fun
main
(
args
:
Array
<
String
>)
{
EventTests
().
testSubscribeGlobalScope
()
exitProcess
(
0
)
}
}
}
\ No newline at end of file
mirai-demos/mirai-demo-1/src/main/java/demo/subscribe/SubscribeSamples.kt
View file @
22175eaa
...
...
@@ -51,17 +51,6 @@ suspend fun main() {
bot
.
messageDSL
()
directlySubscribe
(
bot
)
//DSL 监听
subscribeAll
<
FriendMessage
>
{
always
{
//获取第一个纯文本消息
val
firstText
=
it
.
message
.
firstOrNull
<
PlainText
>()
}
}
demo2
()
bot
.
network
.
awaitDisconnection
()
//等到直到断开连接
}
...
...
@@ -199,9 +188,22 @@ suspend fun Bot.messageDSL() {
*/
@Suppress
(
"UNUSED_VARIABLE"
)
suspend
fun
directlySubscribe
(
bot
:
Bot
)
{
// 在当前协程作用域 (CoroutineScope) 下创建一个子 Job, 监听一个事件.
//
// 手动处理消息
// 使用 Bot 的扩展方法监听, 将在处理事件时得到一个 this: Bot.
// 这样可以调用 Bot 内的一些扩展方法如 UInt.qq():QQ
//
// 这个函数返回 Listener, Listener 是一个 CompletableJob. 如果不手动 close 它, 它会一直阻止当前 CoroutineScope 结束.
// 例如:
// ```kotlin
// runBlocking {// this: CoroutineScope
// bot.subscribeAlways<FriendMessage> {
// }
// }
// ```
// 则这个 `runBlocking` 永远不会结束, 因为 `subscribeAlways` 在 `runBlocking` 的 `CoroutineScope` 下创建了一个 Job.
// 正确的用法为:
bot
.
subscribeAlways
<
FriendMessage
>
{
// this: Bot
// it: FriendMessageEvent
...
...
@@ -245,21 +247,4 @@ suspend fun directlySubscribe(bot: Bot) {
it
.
message
eq
"发图片2"
->
it
.
reply
(
PlainText
(
"test"
)
+
Image
(
ImageId
(
"{7AA4B3AA-8C3C-0F45-2D9B-7F302A0ACEAA}.jpg"
)))
}
}
}
/**
* 实现功能:
* 对机器人说 "记笔记", 机器人记录之后的所有消息.
* 对机器人说 "停止", 机器人停止
*/
suspend
fun
demo2
()
{
subscribeAlways
<
FriendMessage
>
{
event
->
if
(
event
.
message
eq
"记笔记"
)
{
subscribeUntilFalse
<
FriendMessage
>
{
it
.
reply
(
"你发送了 ${it.message}"
)
it
.
message
eq
"停止"
}
}
}
}
\ No newline at end of file
mirai-demos/mirai-demo-gentleman/src/main/kotlin/demo/gentleman/Main.kt
View file @
22175eaa
...
...
@@ -37,7 +37,7 @@ private fun readTestAccount(): BotAccount? {
val
lines
=
file
.
readLines
()
return
try
{
BotAccount
(
lines
[
0
].
toLong
(),
lines
[
1
])
}
catch
(
e
:
Exception
)
{
}
catch
(
e
:
Throwable
)
{
null
}
}
...
...
@@ -54,16 +54,15 @@ suspend fun main() {
/**
* 监听所有事件
*/
subscribeAlways
<
Subscribable
>
{
//bot.logger.verbose("收到了一个事件: ${it::class.simpleName}")
GlobalScope
.
subscribeAlways
<
Subscribable
>
{
bot
.
logger
.
verbose
(
"收到了一个事件: $this"
)
}
subscribeAlways
<
ReceiveFriendAddRequestEvent
>
{
GlobalScope
.
subscribeAlways
<
ReceiveFriendAddRequestEvent
>
{
it
.
approve
()
}
bot
.
subscribeGroupMessages
{
GlobalScope
.
subscribeGroupMessages
{
"群资料"
reply
{
group
.
updateGroupInfo
().
toString
().
reply
()
}
...
...
@@ -85,7 +84,11 @@ suspend fun main() {
}
bot
.
subscribeMessages
{
always
{
}
case
(
"at me"
)
{
At
(
sender
).
reply
()
}
// 等同于 "at me" reply { At(sender) }
"你好"
reply
"你好!"
...
...
Write
Preview
Markdown
is supported
0%
Try again
or
attach a new file
Attach a file
Cancel
You are about to add
0
people
to the discussion. Proceed with caution.
Finish editing this message first!
Cancel
Please
register
or
sign in
to comment