Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add way to intercept events and insert custom context #667

Merged
merged 15 commits into from
Aug 18, 2022
Merged
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Prev Previous commit
Next Next commit
Add gatewayEventInterceptor to KordBuilder
lukellmann committed Aug 9, 2022
commit f14418b790c22b57edf8b24b2d3119750492e34f
7 changes: 1 addition & 6 deletions core/src/main/kotlin/Kord.kt
Original file line number Diff line number Diff line change
@@ -20,7 +20,6 @@ import dev.kord.core.event.Event
import dev.kord.core.exception.EntityNotFoundException
import dev.kord.core.exception.KordInitializationException
import dev.kord.core.gateway.MasterGateway
import dev.kord.core.gateway.handler.DefaultGatewayEventInterceptor
import dev.kord.core.gateway.handler.GatewayEventInterceptor
import dev.kord.core.gateway.start
import dev.kord.core.supplier.*
@@ -56,13 +55,9 @@ public class Kord(
public val selfId: Snowflake,
private val eventFlow: MutableSharedFlow<Event>,
dispatcher: CoroutineDispatcher,
interceptorBuilder: () -> GatewayEventInterceptor = {
DefaultGatewayEventInterceptor(cache)
}
private val interceptor: GatewayEventInterceptor,
) : CoroutineScope {

private val interceptor = interceptorBuilder.invoke()

/**
* Global commands made by the bot under this Kord instance.
*
26 changes: 18 additions & 8 deletions core/src/main/kotlin/builder/kord/KordBuilder.kt
Original file line number Diff line number Diff line change
@@ -13,6 +13,8 @@ import dev.kord.core.cache.registerKordData
import dev.kord.core.event.Event
import dev.kord.core.exception.KordInitializationException
import dev.kord.core.gateway.DefaultMasterGateway
import dev.kord.core.gateway.handler.DefaultGatewayEventInterceptor
import dev.kord.core.gateway.handler.GatewayEventInterceptor
import dev.kord.core.supplier.EntitySupplyStrategy
import dev.kord.gateway.DefaultGateway
import dev.kord.gateway.Gateway
@@ -116,6 +118,14 @@ public class KordBuilder(public val token: String) {

public var applicationId: Snowflake? = null

/**
* The [GatewayEventInterceptor] used for converting [gateway events][dev.kord.gateway.Event] to
* [core events][dev.kord.core.event.Event].
*
* [DefaultGatewayEventInterceptor] will be used when not set.
*/
public var gatewayEventInterceptor: GatewayEventInterceptor? = null

/**
* Configures the shards this client will connect to, by default `0 until recommended`.
* This can be used to break up to client into multiple processes.
@@ -274,14 +284,14 @@ public class KordBuilder(public val token: String) {
}

return Kord(
resources,
cache,
gateway,
rest,
self,
eventFlow,
defaultDispatcher
resources = resources,
cache = cache,
gateway = gateway,
rest = rest,
selfId = self,
eventFlow = eventFlow,
dispatcher = defaultDispatcher,
interceptor = gatewayEventInterceptor ?: DefaultGatewayEventInterceptor(),
)
}

}
17 changes: 9 additions & 8 deletions core/src/main/kotlin/builder/kord/RestOnlyBuilder.kt
Original file line number Diff line number Diff line change
@@ -5,6 +5,7 @@ import dev.kord.common.entity.Snowflake
import dev.kord.core.ClientResources
import dev.kord.core.Kord
import dev.kord.core.gateway.DefaultMasterGateway
import dev.kord.core.gateway.handler.GatewayEventInterceptor
import dev.kord.core.supplier.EntitySupplyStrategy
import dev.kord.gateway.Gateway
import dev.kord.gateway.builder.Shards
@@ -65,14 +66,14 @@ public abstract class RestOnlyBuilder {
val rest = RestClient(handlerBuilder(resources))

return Kord(
resources,
@OptIn(ExperimentalCoroutinesApi::class)
DataCache.none(),
DefaultMasterGateway(mapOf(0 to Gateway.none())),
rest,
selfId,
MutableSharedFlow(),
defaultDispatcher,
resources = resources,
cache = @OptIn(ExperimentalCoroutinesApi::class) DataCache.none(),
gateway = DefaultMasterGateway(mapOf(0 to Gateway.none())),
rest = rest,
selfId = selfId,
eventFlow = MutableSharedFlow(),
dispatcher = defaultDispatcher,
interceptor = GatewayEventInterceptor.none(),
)
}
}
Original file line number Diff line number Diff line change
@@ -1,13 +1,10 @@
package dev.kord.core.gateway.handler

import dev.kord.cache.api.DataCache
import dev.kord.core.Kord
import dev.kord.core.event.Event as CoreEvent
import dev.kord.gateway.Event as GatewayEvent

public abstract class BaseGatewayEventHandler(
protected val cache: DataCache
) {
public abstract class BaseGatewayEventHandler {

public abstract suspend fun handle(event: GatewayEvent, shard: Int, kord: Kord): CoreEvent?

17 changes: 7 additions & 10 deletions core/src/main/kotlin/gateway/handler/ChannelEventHandler.kt
Original file line number Diff line number Diff line change
@@ -1,6 +1,5 @@
package dev.kord.core.gateway.handler

import dev.kord.cache.api.DataCache
import dev.kord.cache.api.put
import dev.kord.cache.api.query
import dev.kord.cache.api.remove
@@ -16,9 +15,7 @@ import dev.kord.core.event.channel.data.TypingStartEventData
import dev.kord.gateway.*
import dev.kord.core.event.Event as CoreEvent

internal class ChannelEventHandler(
cache: DataCache
) : BaseGatewayEventHandler(cache) {
internal class ChannelEventHandler : BaseGatewayEventHandler() {

override suspend fun handle(event: Event, shard: Int, kord: Kord): CoreEvent? = when (event) {
is ChannelCreate -> handle(event, shard, kord)
@@ -31,7 +28,7 @@ internal class ChannelEventHandler(

private suspend fun handle(event: ChannelCreate, shard: Int, kord: Kord): ChannelCreateEvent? {
val data = ChannelData.from(event.channel)
cache.put(data)
kord.cache.put(data)

val coreEvent = when (val channel = Channel.from(data, kord)) {
is NewsChannel -> NewsChannelCreateEvent(channel, shard)
@@ -51,8 +48,8 @@ internal class ChannelEventHandler(

private suspend fun handle(event: ChannelUpdate, shard: Int, kord: Kord): ChannelUpdateEvent? {
val data = ChannelData.from(event.channel)
val oldData = cache.query<ChannelData> { idEq(ChannelData::id, data.id) }.singleOrNull()
cache.put(data)
val oldData = kord.cache.query<ChannelData> { idEq(ChannelData::id, data.id) }.singleOrNull()
kord.cache.put(data)
val old = oldData?.let { Channel.from(it, kord) }
val coreEvent = when (val channel = Channel.from(data, kord)) {
is NewsChannel -> NewsChannelUpdateEvent(channel, old as? NewsChannel, shard)
@@ -71,7 +68,7 @@ internal class ChannelEventHandler(
}

private suspend fun handle(event: ChannelDelete, shard: Int, kord: Kord): ChannelDeleteEvent? {
cache.remove<ChannelData> { idEq(ChannelData::id, event.channel.id) }
kord.cache.remove<ChannelData> { idEq(ChannelData::id, event.channel.id) }
val data = ChannelData.from(event.channel)

val coreEvent = when (val channel = Channel.from(data, kord)) {
@@ -93,7 +90,7 @@ internal class ChannelEventHandler(
with(event.pins) {
val coreEvent = ChannelPinsUpdateEvent(ChannelPinsUpdateEventData.from(this), kord, shard)

cache.query<ChannelData> { idEq(ChannelData::id, channelId) }.update {
kord.cache.query<ChannelData> { idEq(ChannelData::id, channelId) }.update {
it.copy(lastPinTimestamp = lastPinTimestamp)
}

@@ -102,7 +99,7 @@ internal class ChannelEventHandler(

private suspend fun handle(event: TypingStart, shard: Int, kord: Kord): TypingStartEvent = with(event.data) {
member.value?.let {
cache.put(MemberData.from(userId = it.user.value!!.id, guildId = guildId.value!!, it))
kord.cache.put(MemberData.from(userId = it.user.value!!.id, guildId = guildId.value!!, it))
}

TypingStartEvent(TypingStartEventData.from(this), kord, shard)
Original file line number Diff line number Diff line change
@@ -1,6 +1,5 @@
package dev.kord.core.gateway.handler

import dev.kord.cache.api.DataCache
import dev.kord.core.Kord
import dev.kord.core.gateway.ShardEvent
import io.ktor.util.logging.*
@@ -9,18 +8,19 @@ import dev.kord.core.event.Event as CoreEvent

private val logger = KotlinLogging.logger { }

public class DefaultGatewayEventInterceptor(cache: DataCache) : GatewayEventInterceptor {
/** Default implementation of [GatewayEventInterceptor] that also updates [cache][Kord.cache]. */
public class DefaultGatewayEventInterceptor : GatewayEventInterceptor {

private val listeners = listOf(
MessageEventHandler(cache),
ChannelEventHandler(cache),
ThreadEventHandler(cache),
GuildEventHandler(cache),
LifeCycleEventHandler(cache),
UserEventHandler(cache),
VoiceEventHandler(cache),
WebhookEventHandler(cache),
InteractionEventHandler(cache)
MessageEventHandler(),
ChannelEventHandler(),
ThreadEventHandler(),
GuildEventHandler(),
LifeCycleEventHandler(),
UserEventHandler(),
VoiceEventHandler(),
WebhookEventHandler(),
InteractionEventHandler()
)

override suspend fun handle(event: ShardEvent, kord: Kord): CoreEvent? {
27 changes: 24 additions & 3 deletions core/src/main/kotlin/gateway/handler/GatewayEventInterceptor.kt
Original file line number Diff line number Diff line change
@@ -2,12 +2,33 @@ package dev.kord.core.gateway.handler

import dev.kord.core.Kord
import dev.kord.core.gateway.ShardEvent
import kotlinx.coroutines.Job
import kotlinx.coroutines.channels.Channel
import kotlinx.coroutines.flow.*
import dev.kord.core.event.Event as CoreEvent

/**
* Instances of this type are used to convert [gateway events][dev.kord.gateway.Event] to
* [core events][dev.kord.core.event.Event].
*/
public interface GatewayEventInterceptor {

/**
* Converts a [gateway event][dev.kord.gateway.Event] (in the form of a [ShardEvent]) to a
* [core event][dev.kord.core.event.Event].
*
* This might also have side effects like updating the [cache][Kord.cache].
*/
public suspend fun handle(event: ShardEvent, kord: Kord): CoreEvent?


public companion object {
private object None : GatewayEventInterceptor {
override suspend fun handle(event: ShardEvent, kord: Kord) = null
}

/**
* Returns a [GatewayEventInterceptor] with no-op behavior.
*
* [handle] will always return `null`.
*/
public fun none(): GatewayEventInterceptor = None
}
}
Loading