Skip to content

Commit 0298573

Browse files
committed
注解过滤器工厂
Signed-off-by: ForteScarlet <[email protected]>
1 parent d74152c commit 0298573

File tree

6 files changed

+206
-237
lines changed

6 files changed

+206
-237
lines changed

apis/simbot-api/src/main/kotlin/love/forte/simbot/event/EventFilter.kt

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,7 @@ import love.forte.simbot.Api4J
2121
import love.forte.simbot.BlockingFilter
2222
import love.forte.simbot.Filter
2323
import love.forte.simbot.PriorityConstant
24+
import love.forte.simbot.utils.runWithInterruptible
2425

2526
/**
2627
* 事件过滤器。
@@ -34,6 +35,8 @@ import love.forte.simbot.PriorityConstant
3435
*
3536
* 过滤器存在 [优先级][priority], 默认情况下的优先级为 [PriorityConstant.NORMAL].
3637
*
38+
* 对于不支持挂起函数的实现者,可参考 [BlockingEventFilter]。
39+
*
3740
* @author ForteScarlet
3841
*/
3942
public interface EventFilter : Filter<EventListenerProcessingContext> {
@@ -73,6 +76,6 @@ public interface BlockingEventFilter : EventFilter, BlockingFilter<EventListener
7376
*/
7477
@JvmSynthetic
7578
@Suppress("PARAMETER_NAME_CHANGED_ON_OVERRIDE")
76-
override suspend fun test(context: EventListenerProcessingContext): Boolean = testBlocking()
79+
override suspend fun test(context: EventListenerProcessingContext): Boolean = runWithInterruptible { testBlocking() }
7780

7881
}

boots/simboot-api/src/test/kotlin/MatcherTest.kt

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,7 @@
1616
*/
1717

1818
import love.forte.simboot.filter.RegexMatcherValue
19-
import org.junit.Test
19+
import kotlin.test.Test
2020

2121

2222
/**

boots/simboot-core-annotation/src/main/kotlin/love/forte/simboot/annotation/AnnotationEventFilterFactory.kt

Lines changed: 28 additions & 58 deletions
Original file line numberDiff line numberDiff line change
@@ -1,16 +1,42 @@
11
package love.forte.simboot.annotation
22

33
import love.forte.simbot.Api4J
4+
import love.forte.simbot.MutableAttributeMap
45
import love.forte.simbot.event.*
5-
import javax.inject.Singleton
66

77
/**
8+
* 应用于 [@Filter][love.forte.simboot.annotation.Filter] 注解上的
9+
* [Filter.by][love.forte.simboot.annotation.Filter.by] 参数,用于
10+
* 通过参数构建一个当前 `Filter` 对应的过滤器实例。
811
*
12+
* ```kotlin
13+
* @Filter(by = FooAnnotationEventFilterFactory::class)
14+
* suspend fun Event.onEvent() { ... }
15+
* ```
16+
*
17+
* @author ForteScarlet
918
*/
1019
public interface AnnotationEventFilterFactory {
11-
// TODO
20+
21+
22+
/**
23+
* 通过提供的监听函数和过滤器注解参数来解析并得到一个 [EventFilter] 实例。
24+
*
25+
* 如果需要跳过本次解析,可以直接返回一个 null。
26+
*
27+
* @see Filter.by
28+
*
29+
*/
30+
public fun resolveFilter(
31+
listener: EventListener,
32+
listenerAttributes: MutableAttributeMap,
33+
filter: Filter,
34+
filters: Filters,
35+
): EventFilter?
36+
1237
}
1338

39+
1440
/**
1541
* 应用于 [love.forte.simboot.annotation.Filter] 注解上用来直接处理对应注解的函数。
1642
*
@@ -23,72 +49,16 @@ public interface AnnotationEventFilterFactory {
2349
@Suppress("KDocUnresolvedReference")
2450
public interface AnnotationEventFilter : EventFilter {
2551

26-
27-
/**
28-
* 在此实例被获取/构建之后、应用之前被调用, 用于通过监听函数实例和对应的过滤器注解参数来对当前过滤器进行初始化。
29-
*
30-
* ## 使用
31-
* [AnnotationEventFilter] 应用于 [love.forte.simboot.annotation.Filter.by] 中,具体使用描述参考其文档注释。
32-
* ```kotlin
33-
* @Filter(by = FooAnnotationEventFilter::class)
34-
* suspend fun Event.onEvent() { ... }
35-
* ```
36-
*
37-
* ## 实例获取
38-
*
39-
* 如果当前过滤器存在于依赖管理之下并且作为唯一实例使用,则初始化函数 [init] 可能会执行多次;
40-
* 如果当前过滤器为 `object` 类型,[init] 同样可能会执行多次。
41-
* ```kotlin
42-
* object FooAnnotationEventFilter : AnnotationEventFilter {
43-
* // ...
44-
* }
45-
* class BarAnnotationEventFilter : AnnotationEventFilter {
46-
* // ...
47-
* }
48-
* ```
49-
*
50-
* 如果当前过滤器不存在于任何依赖管理中并且为普通的类,则会为每一个监听函数构建一个唯一的实例,
51-
* 此时 [init] 将会只执行一次。
52-
*
53-
*
54-
*
55-
* 假如 [AnnotationEventFilter] 的实现不是一个 `object` 类型,但是希望对所有的监听函数来讲
56-
* 其实例唯一,那么在使用的此类类型上标记 [Singleton]。标记了 [Singleton] 的普通实现,
57-
* 会在构造一次实例后进行缓存,保证所有监听函数对应的实例唯一。
58-
* 此时 [init] 将可能执行多次。
59-
* ```kotlin
60-
* @javax.inject.Singleton
61-
* class FooAnnotationEventFilter : AnnotationEventFilter {
62-
* // ...
63-
* }
64-
* ```
65-
*
66-
* 除了交由依赖容器所管理的类型之外,所有要使用的实现类型必须保证存在一个公开无参的构造函数用于进行实例化。
67-
*
68-
* @return 此监听函数初始化后的整合类型
69-
*
70-
* @see Filter.by
71-
* @see InitType
72-
*
73-
*/
7452
public fun init(listener: EventListener, filter: Filter, filters: Filters)
7553

7654

77-
/**
78-
* [AnnotationEventFilter] 初始化后, 用于代表当前过滤器针对于当前监听函数的状态类型。
79-
*
80-
* @see AnnotationEventFilter.init
81-
*/
8255
@Deprecated("Unused")
8356
public enum class InitType {
8457
INDEPENDENT,
8558
UNITED
8659
}
8760

8861

89-
/**
90-
* 过滤器的检测函数。通过 [EventProcessingContext] 来验证是否需要处理当前事件。
91-
*/
9262
override suspend fun test(context: EventListenerProcessingContext): Boolean
9363

9464
}

boots/simboot-core-annotation/src/main/kotlin/love/forte/simboot/annotation/Filter.kt

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -126,17 +126,17 @@ public annotation class Filter(
126126

127127

128128
/**
129-
* 指定一个对当前 [Filter] 的处理过滤器。当 [by] 指定了任意一个不直接等同于 [AnnotationEventFilter]
129+
* 指定一个对当前 [Filter] 的处理过滤器。当 [by] 指定了任意一个不直接等同于 [AnnotationEventFilterFactory]
130130
* 的类型时,此注解的上述其他参数将不再继续被解析,而是直接交由指定目标进行处理。
131131
*
132132
*
133133
* ```kotlin
134-
* @Filter(by = FooAnnotationEventFilter::class)
134+
* @Filter(by = FooAnnotationEventFilterFactory::class)
135135
* suspend fun Event.onEvent() { ... }
136136
* ```
137137
*
138138
*/
139-
val by: KClass<out AnnotationEventFilter> = AnnotationEventFilter::class,
139+
val by: KClass<out AnnotationEventFilterFactory> = AnnotationEventFilterFactory::class,
140140

141141
)
142142

Lines changed: 134 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,134 @@
1+
package love.forte.simboot.core.filter
2+
3+
import love.forte.simboot.annotation.AnnotationEventFilterFactory
4+
import love.forte.simboot.annotation.Filter
5+
import love.forte.simboot.annotation.Filters
6+
import love.forte.simboot.filter.EmptyKeyword
7+
import love.forte.simboot.filter.MatchType
8+
import love.forte.simbot.MutableAttributeMap
9+
import love.forte.simbot.event.*
10+
import love.forte.simbot.literal
11+
import love.forte.simbot.message.At
12+
import java.util.concurrent.CopyOnWriteArrayList
13+
14+
public object CoreAnnotationEventFilterFactory : AnnotationEventFilterFactory {
15+
override fun resolveFilter(
16+
listener: EventListener,
17+
listenerAttributes: MutableAttributeMap,
18+
filter: Filter,
19+
filters: Filters,
20+
): EventFilter? {
21+
val value = filter.value
22+
val target = filter.target.box()
23+
24+
if (value.isEmpty() && target == null) {
25+
return null
26+
}
27+
// 当前的普通过滤器
28+
val currentFilter = AnnotationFilter(
29+
target, value, filter.ifNullPass, filter.matchType
30+
) { it.textContent } // selector
31+
32+
// put keyword.
33+
listenerAttributes.computeIfAbsent(KeywordsAttribute) { CopyOnWriteArrayList() }.add(currentFilter.keyword)
34+
35+
return currentFilter
36+
}
37+
}
38+
39+
40+
private class AnnotationFilter(
41+
target: FilterTarget?,
42+
val value: String,
43+
val ifNullPass: Boolean,
44+
val matchType: MatchType,
45+
val contentSelector: (EventListenerProcessingContext) -> String?,
46+
) : EventFilter {
47+
val keyword = if (value.isEmpty()) EmptyKeyword else KeywordImpl(value)
48+
val targetMatch: suspend (Event) -> Boolean = target?.toMatcher() ?: { true }
49+
50+
override suspend fun test(context: EventListenerProcessingContext): Boolean {
51+
val event = context.event
52+
53+
// target
54+
if (!targetMatch(event)) {
55+
return false
56+
}
57+
58+
val textContent = contentSelector(context) //.textContent
59+
60+
// match
61+
// 存在匹配词, 尝试匹配
62+
if (keyword !== EmptyKeyword) {
63+
if (textContent != null) {
64+
if (!matchType.match(textContent, keyword)) {
65+
return false
66+
}
67+
} else return ifNullPass
68+
}
69+
// 匹配关键词本身没有, 直接放行.
70+
71+
72+
// maybe other match
73+
74+
return true
75+
}
76+
}
77+
78+
79+
private fun FilterTarget.toMatcher(): suspend (Event) -> Boolean {
80+
return M@{ event ->
81+
if (components.isNotEmpty()) {
82+
if (event.component.id.literal !in components) {
83+
return@M false
84+
}
85+
}
86+
87+
if (bots.isNotEmpty()) {
88+
if (event.bot.id.literal !in bots) {
89+
return@M false
90+
}
91+
}
92+
93+
if (authors.isNotEmpty()) {
94+
if (event is ChatRoomMessageEvent) {
95+
if (event.author().id.literal !in authors) {
96+
return@M false
97+
}
98+
}
99+
if (event is ContactMessageEvent) {
100+
if (event.source().id.literal !in authors) {
101+
return@M false
102+
}
103+
}
104+
}
105+
106+
if (groups.isNotEmpty() && event is GroupEvent) {
107+
if (event.group().id.literal !in groups) {
108+
return@M false
109+
}
110+
}
111+
112+
if (channels.isNotEmpty() && event is ChannelEvent) {
113+
if (event.channel().id.literal !in channels) {
114+
return@M false
115+
}
116+
}
117+
118+
if (guilds.isNotEmpty() && event is GuildEvent) {
119+
if (event.guild().id.literal !in guilds) {
120+
return@M false
121+
}
122+
}
123+
124+
// atBot
125+
if (atBot && event is ChatRoomMessageEvent) {
126+
if (event.messageContent.messages.none { it is At && event.bot.isMe(it.target) }) {
127+
return@M false
128+
}
129+
}
130+
131+
true
132+
}
133+
134+
}

0 commit comments

Comments
 (0)