Skip to content

Commit

Permalink
update storage and context
Browse files Browse the repository at this point in the history
  • Loading branch information
Данила Беляков authored and Данила Беляков committed Nov 17, 2024
1 parent d86f387 commit ec8eab0
Show file tree
Hide file tree
Showing 45 changed files with 662 additions and 277 deletions.
2 changes: 2 additions & 0 deletions alice-ktx/build.gradle.kts
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,8 @@ dependencies {

implementation("io.ktor:ktor-serialization-kotlinx-json-jvm")

implementation("io.lettuce:lettuce-core:6.5.0.RELEASE")

testImplementation("junit:junit:4.13.2")
testImplementation("org.jetbrains.kotlinx:kotlinx-coroutines-test:1.9.0")
}
Expand Down
2 changes: 1 addition & 1 deletion alice-ktx/src/main/kotlin/com/github/alice/ktx/Dispatch.kt
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ import com.github.alice.ktx.middleware.Middleware
import com.github.alice.ktx.middleware.MiddlewareType
import com.github.alice.ktx.models.FSMStrategy
import com.github.alice.ktx.models.request.MessageRequest
import com.github.alice.ktx.state.FSMContext
import com.github.alice.ktx.context.FSMContext

/**
* Расширение для `Skill.Builder`, позволяющее настроить `Dispatcher`.
Expand Down
35 changes: 21 additions & 14 deletions alice-ktx/src/main/kotlin/com/github/alice/ktx/Skill.kt
Original file line number Diff line number Diff line change
Expand Up @@ -3,12 +3,16 @@ package com.github.alice.ktx
import com.github.alice.ktx.api.dialog.DialogApi
import com.github.alice.ktx.middleware.MiddlewareType
import com.github.alice.ktx.models.FSMStrategy
import com.github.alice.ktx.models.Request
import com.github.alice.ktx.models.request
import com.github.alice.ktx.models.request.MessageRequest
import com.github.alice.ktx.models.response.MessageResponse
import com.github.alice.ktx.models.toEventRequest
import com.github.alice.ktx.server.WebServer
import com.github.alice.ktx.server.WebServerResponseListener
import com.github.alice.ktx.state.FSMContext
import com.github.alice.ktx.state.impl.BaseFSMContext
import com.github.alice.ktx.context.FSMContext
import com.github.alice.ktx.context.impl.BaseFSMContext
import com.github.alice.ktx.storage.Storage
import com.github.alice.ktx.storage.apiStorage.EnableApiStorage
import com.github.alice.ktx.storage.impl.memoryStorage
import kotlinx.serialization.json.Json
Expand Down Expand Up @@ -42,13 +46,13 @@ class Skill internal constructor(
var json: Json = Json { ignoreUnknownKeys = true }
var dialogApi: DialogApi? = null
lateinit var webServer: WebServer
var fsmStrategy: FSMStrategy = FSMStrategy.USER
var defaultFSMStrategy: FSMStrategy = FSMStrategy.USER
internal var dispatcherConfiguration: Dispatcher.() -> Unit = { }

var storage = memoryStorage { }
var storage: Storage = memoryStorage()

var fsmContext: (message: MessageRequest) -> FSMContext = { message ->
BaseFSMContext(storage, id, fsmStrategy, message)
BaseFSMContext(storage, defaultFSMStrategy, message, id)
}

internal fun build(body: Builder.() -> Unit): Skill {
Expand All @@ -57,7 +61,7 @@ class Skill internal constructor(
return Skill(
webServer = webServer,
dispatcher = Dispatcher(
fsmStrategy = fsmStrategy,
fsmStrategy = defaultFSMStrategy,
dialogApi = dialogApi,
fsmContext = fsmContext,
enableApiStorage = storage.javaClass.isAnnotationPresent(EnableApiStorage::class.java)
Expand All @@ -81,19 +85,23 @@ class Skill internal constructor(
*/
private fun webServerResponseCallback(): WebServerResponseListener = object : WebServerResponseListener {
override suspend fun messageHandle(model: MessageRequest): MessageResponse? {
runMiddlewares(model, MiddlewareType.OUTER)?.let { return it }
val request = dispatcher.request(model)
val eventRequest = request.toEventRequest()

runMiddlewares(request, MiddlewareType.OUTER)?.let { return it }
dispatcher.commandHandlers.forEach { handler ->
if (handler.event(model)) {
runMiddlewares(model, MiddlewareType.INNER)?.let { return it }
return handler.handle(model)
if (handler.event(eventRequest)) {
runMiddlewares(request, MiddlewareType.INNER)?.let { return it }
return handler.handle(request)
}
}
return null
}

override suspend fun responseFailure(model: MessageRequest, ex: Exception): MessageResponse? {
val request = dispatcher.request(model)
dispatcher.networkErrorHandlers.forEach { errorHandler ->
errorHandler.responseFailure(model, ex)?.let { response ->
errorHandler.responseFailure(request, ex)?.let { response ->
return response
}
}
Expand All @@ -103,13 +111,12 @@ class Skill internal constructor(
/**
* Выполняет мидлвари указанного типа.
*
* @param model Модель запроса сообщения.
* @param type Тип мидлвари, который следует выполнить.
* @return `MessageResponse?` — ответ от мидлвари, или `null`, если обработка не завершена.
*/
private suspend fun runMiddlewares(model: MessageRequest, type: MiddlewareType): MessageResponse? {
private suspend fun runMiddlewares(request: Request, type: MiddlewareType): MessageResponse? {
dispatcher.middlewares[type]?.forEach { middleware ->
middleware.invoke(model)?.let { response ->
middleware.invoke(request)?.let { response ->
return response
}
}
Expand Down
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
package com.github.alice.ktx.state
package com.github.alice.ktx.context

/**
* Интерфейс для доступа к информации состояния конкретного пользователя.
Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
package com.github.alice.ktx.state
package com.github.alice.ktx.context

import com.github.alice.ktx.models.FSMStrategy
import kotlin.reflect.KClass

interface MutableFSMContext : ReadOnlyFSMContext {
Expand All @@ -9,40 +10,60 @@ interface MutableFSMContext : ReadOnlyFSMContext {
* */
suspend fun setState(key: String)

suspend fun setState(key: String, strategy: FSMStrategy)

/**
* Записать данные (перезапись) с произвольным типом.
* */
suspend fun <V: Any> setTypedData(vararg pairs: Pair<String, V>, clazz: KClass<V>)
suspend fun <V : Any> setTypedData(clazz: KClass<V>, vararg pairs: Pair<String, V>)

suspend fun <V : Any> setTypedData(clazz: KClass<V>, strategy: FSMStrategy, vararg pairs: Pair<String, V>)

/**
* Записать данные (перезапись).
* */
suspend fun setData(vararg pairs: Pair<String, String>)

suspend fun setData(strategy: FSMStrategy, vararg pairs: Pair<String, String>)

/**
* Обновление данных в хранилище по ключу с произвольным типом.
* @return Обновленные данные.
* */
suspend fun <V: Any> updateTypedData(vararg pairs: Pair<String, V>, clazz: KClass<V>): Map<String, String>
suspend fun <V : Any> updateTypedData(clazz: KClass<V>, vararg pairs: Pair<String, V>): Map<String, String>

suspend fun <V : Any> updateTypedData(
clazz: KClass<V>,
strategy: FSMStrategy,
vararg pairs: Pair<String, V>
): Map<String, String>

/**
* Обновление данные в хранилище по ключу.
* @return Обновленные данные.
* */
suspend fun updateData(vararg pairs: Pair<String, String>): Map<String, String>

suspend fun updateData(strategy: FSMStrategy, vararg pairs: Pair<String, String>): Map<String, String>

/**
* Удалить данные по ключу.
* */
suspend fun removeData(key: String): String?

suspend fun removeData(key: String, strategy: FSMStrategy): String?

/**
* Удалить данные по ключу с произвольным типом.
* */
suspend fun <V: Any> removeTypedData(key: String, clazz: KClass<V>): V?
suspend fun <V : Any> removeTypedData(key: String, clazz: KClass<V>): V?

suspend fun <V : Any> removeTypedData(key: String, clazz: KClass<V>, strategy: FSMStrategy): V?

/**
* Очистить состояние и данные.
* */
suspend fun clear()

suspend fun clear(strategy: FSMStrategy)
}
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
package com.github.alice.ktx.state
package com.github.alice.ktx.context

import com.github.alice.ktx.models.FSMStrategy
import kotlin.reflect.KClass

interface ReadOnlyFSMContext {
Expand All @@ -9,18 +10,26 @@ interface ReadOnlyFSMContext {
* */
suspend fun getState(): String?

suspend fun getState(strategy: FSMStrategy): String?

/**
* Получить все данные.
* */
suspend fun getData(): Map<String, String>

suspend fun getData(strategy: FSMStrategy): Map<String, String>

/**
* Получить данные по ключу с произвольным типом.
* */
suspend fun <V: Any> getTypedData(key: String, clazz: KClass<V>): V?
suspend fun <V : Any> getTypedData(key: String, clazz: KClass<V>): V?

suspend fun <V : Any> getTypedData(key: String, clazz: KClass<V>, strategy: FSMStrategy): V?

/**
* Получить данные по ключу.
* */
suspend fun getData(key: String): String?

suspend fun getData(key: String, strategy: FSMStrategy): String?
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,144 @@
package com.github.alice.ktx.context.impl

import com.github.alice.ktx.models.FSMStrategy
import com.github.alice.ktx.models.request.MessageRequest
import com.github.alice.ktx.context.FSMContext
import com.github.alice.ktx.storage.Storage
import com.github.alice.ktx.storage.apiStorage.ApiStorageDetails
import com.github.alice.ktx.storage.models.StorageKey
import kotlin.reflect.KClass

/**
* Класс `BaseFSMContext` предоставляет реализацию интерфейса `FSMContext` для управления состоянием и данными пользователя.
*
* @param message Сообщение запроса, содержащего начальное состояние и данные пользователя.
* @param defaultStrategy Стратегия конечного автомата состояний (FSM), используемая для управления состояниями.
*/
class BaseFSMContext(
private val storage: Storage,
private val defaultStrategy: FSMStrategy,
private val message: MessageRequest,

skillId: String?
) : FSMContext {

private val storageKey = StorageKey.Builder().build(skillId, message, defaultStrategy)

override suspend fun init() {
(storage as? ApiStorageDetails)?.apply {
FSMStrategy.entries.forEach { strategy ->
message.state?.getCurrentStateAndData(strategy)?.let { stateAndData ->
setCurrentState(storageKey.copy(strategy = strategy), stateAndData.first)
setCurrentData(storageKey.copy(strategy = strategy), stateAndData.second)
}
}
}
}

override suspend fun setState(key: String) {
storage.setState(storageKey, key)
}

override suspend fun setState(key: String, strategy: FSMStrategy) {
storage.setState(storageKey.copy(strategy = strategy), key)
}

override suspend fun <V : Any> setTypedData(clazz: KClass<V>, vararg pairs: Pair<String, V>) {
storage.setTypedData(storageKey, clazz, *pairs)
}

override suspend fun <V : Any> setTypedData(
clazz: KClass<V>,
strategy: FSMStrategy,
vararg pairs: Pair<String, V>
) {
storage.setTypedData(storageKey.copy(strategy = strategy), clazz, *pairs)
}

override suspend fun setData(vararg pairs: Pair<String, String>) {
storage.setData(storageKey, *pairs)
}

override suspend fun setData(strategy: FSMStrategy, vararg pairs: Pair<String, String>) {
storage.setData(storageKey.copy(strategy = strategy), *pairs)
}

override suspend fun <V : Any> updateTypedData(
clazz: KClass<V>,
vararg pairs: Pair<String, V>
): Map<String, String> {
return storage.updateTypedData(storageKey, clazz, *pairs)
}

override suspend fun <V : Any> updateTypedData(
clazz: KClass<V>,
strategy: FSMStrategy,
vararg pairs: Pair<String, V>
): Map<String, String> {
return storage.updateTypedData(storageKey.copy(strategy = strategy), clazz, *pairs)
}

override suspend fun updateData(vararg pairs: Pair<String, String>): Map<String, String> {
return storage.updateData(storageKey, *pairs)
}

override suspend fun updateData(strategy: FSMStrategy, vararg pairs: Pair<String, String>): Map<String, String> {
return storage.updateData(storageKey.copy(strategy = strategy), *pairs)
}

override suspend fun removeData(key: String): String? {
return storage.removeData(storageKey, key)
}

override suspend fun removeData(key: String, strategy: FSMStrategy): String? {
return storage.removeData(storageKey.copy(strategy = strategy), key)
}

override suspend fun <V : Any> removeTypedData(key: String, clazz: KClass<V>): V? {
return storage.removeTypedData(storageKey, key, clazz)
}

override suspend fun <V : Any> removeTypedData(key: String, clazz: KClass<V>, strategy: FSMStrategy): V? {
return storage.removeTypedData(storageKey.copy(strategy = strategy), key, clazz)
}

override suspend fun clear() {
storage.clear(storageKey)
}

override suspend fun clear(strategy: FSMStrategy) {
storage.clear(storageKey.copy(strategy = strategy))
}

override suspend fun getState(): String? {
return storage.getState(storageKey)
}

override suspend fun getState(strategy: FSMStrategy): String? {
return storage.getState(storageKey.copy(strategy = strategy))
}

override suspend fun getData(): Map<String, String> {
return storage.getData(storageKey)
}

override suspend fun getData(strategy: FSMStrategy): Map<String, String> {
return storage.getData(storageKey.copy(strategy = strategy))
}

override suspend fun getData(key: String): String? {
return storage.getData(storageKey, key)
}

override suspend fun getData(key: String, strategy: FSMStrategy): String? {
return storage.getData(storageKey.copy(strategy = strategy), key)
}

override suspend fun <V : Any> getTypedData(key: String, clazz: KClass<V>): V? {
return storage.getTypedData(storageKey, key, clazz)
}

override suspend fun <V : Any> getTypedData(key: String, clazz: KClass<V>, strategy: FSMStrategy): V? {
return storage.getTypedData(storageKey.copy(strategy = strategy), key, clazz)
}
}
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
package com.github.alice.ktx.handlers

import com.github.alice.ktx.models.request.MessageRequest
import com.github.alice.ktx.models.EventRequest
import com.github.alice.ktx.models.Request
import com.github.alice.ktx.models.response.MessageResponse

/**
Expand All @@ -16,13 +17,13 @@ interface Handler {
* @param message Сообщение, которое проверяется на соответствие условиям обработчика.
* @return `true`, если обработчик должен сработать для данного сообщения; `false` в противном случае.
*/
suspend fun event(message: MessageRequest): Boolean
suspend fun event(request: EventRequest): Boolean

/**
* Выполняет обработку запроса сообщения и возвращает ответ.
*
* @param request Запрос сообщения, который будет обработан.
* @return Ответ на запрос в виде `MessageResponse`.
*/
suspend fun handle(request: MessageRequest): MessageResponse
suspend fun handle(request: Request): MessageResponse
}
Loading

0 comments on commit ec8eab0

Please sign in to comment.