io.github.dehuckakpyt.telegrambot.resolver.ChainResolver.kt Maven / Gradle / Ivy
package io.github.dehuckakpyt.telegrambot.resolver
import io.github.dehuckakpyt.telegrambot.container.CallbackContainer
import io.github.dehuckakpyt.telegrambot.container.GeneralContainer
import io.github.dehuckakpyt.telegrambot.container.message.CommandContainer
import io.github.dehuckakpyt.telegrambot.container.message.MessageContainer
import io.github.dehuckakpyt.telegrambot.converter.CallbackSerializer
import io.github.dehuckakpyt.telegrambot.converter.ContentConverter
import io.github.dehuckakpyt.telegrambot.converter.toContentOrNull
import io.github.dehuckakpyt.telegrambot.exception.handler.chain.ChainExceptionHandler
import io.github.dehuckakpyt.telegrambot.ext.container.chatId
import io.github.dehuckakpyt.telegrambot.source.chain.ChainSource
import kotlin.collections.set
import kotlin.reflect.KClass
/**
* Created on 12.11.2023.
*
* Resolver for chain actions.
*
* Here you can add and retrieve actions for commands, steps and callbacks.
*
* @author Denis Matytsin
*/
internal class ChainResolver(
private val callbackSerializer: CallbackSerializer,
private val chainSource: ChainSource,
private val chainExceptionHandler: ChainExceptionHandler,
private val contentConverter: ContentConverter,
) {
private val actionByCommand: MutableMap Unit> = hashMapOf()
private val actionByStep: MutableMap, suspend MessageContainer.() -> Unit>> = hashMapOf()
private val actionByCallback: MutableMap Unit> = hashMapOf()
/**
* Add command handler.
*
* @param command name of the command, started with the '/' (for example, '/start', '/help')
* @param next name of the next step (for example, 'get_name', 'get_phone')
* @param action lambda, which will be invoked
*/
fun addCommand(command: String, next: String? = null, action: suspend CommandContainer.() -> Unit) {
actionByCommand[command] = wrapAction(next, action)
}
/**
* Add step handler.
*
* @param step name of the step (for example, 'get_name', 'get_phone')
* @param type class of the MessageArgument (for example, TEXT, DOCUMENT) (see MessageType)
* @param next name of the next step (for example, 'get_name', 'get_phone')
* @param action lambda, which will be invoked
*
* @see io.github.dehuckakpyt.telegrambot.container.message.MessageType
*/
@Suppress("UNCHECKED_CAST")
fun addStep(
step: String,
type: KClass,
next: String? = null,
action: suspend T.() -> Unit,
) {
val actionByType = actionByStep.getOrPut(step) { hashMapOf() }
actionByType[type] = wrapAction(next, action as suspend MessageContainer.() -> Unit)
}
/**
* Add callback handler.
*
* @param callback callback name (sets in ButtonFactory.callbackButton())
* @param next name of the next step (for example, 'get_name', 'get_phone')
* @param action lambda, which will be invoked
*
* @see io.github.dehuckakpyt.telegrambot.factory.button.ButtonFactory
*/
fun addCallback(callback: String, next: String? = null, action: suspend CallbackContainer.() -> Unit) {
callbackSerializer.validateCallbackName(callback)
actionByCallback[callback] = wrapAction(next, action)
}
/**
* Get command handler.
*
* @param command name of the command, started with the '/' (for example, '/start', '/help')
*/
fun getCommand(command: String): suspend CommandContainer.() -> Unit {
return actionByCommand[command] ?: chainExceptionHandler.whenCommandNotFound(command)
}
/**
* Get step handler.
*
* @param step name of the step (for example, 'get_name', 'get_phone')
* @param type class of the MessageArgument (for example, TEXT, DOCUMENT) (see MessageType)
*
* @see io.github.dehuckakpyt.telegrambot.container.message.MessageType
*/
fun getStep(step: String, type: KClass): (suspend MessageContainer.() -> Unit)? {
val actionByMessageType = step.let(actionByStep::get) ?: return null
return actionByMessageType[type] ?: chainExceptionHandler.whenUnexpectedMessageType()
}
/**
* Get callback handler.
*
* @param callback callback name (sets in ButtonFactory.callbackButton())
*
* @see io.github.dehuckakpyt.telegrambot.factory.button.ButtonFactory
*/
fun getCallback(callback: String): (suspend CallbackContainer.() -> Unit)? {
return actionByCallback[callback]
}
internal val allowedUpdates: Set
get() = buildSet {
if (actionByCommand.isNotEmpty() || actionByStep.isNotEmpty()) add("message")
if (actionByCallback.isNotEmpty()) add("callback_query")
}
/**
* Wrap chain action.
*
* The lambda needs to be wrapped with additional actions to make the dynamic chains work.
*
* @param next name of the next step (for example, 'get_name', 'get_phone')
* @param action action needed to wrap
*
* @return lambda with applied dynamic step
*/
private fun wrapAction(next: String?, action: suspend T.() -> Unit): suspend T.() -> Unit = {
// First, set name ot the next step from static param.
this.nextStep = next
// Invoke action, which can change next step.
this.action()
// Save name ot the next step and transferred object.
this.saveNextStepInChain()
}
/**
* Save state of the current chain.
*
* Will be saved name of the next step and transferred object.
*/
private suspend fun GeneralContainer.saveNextStepInChain() {
chainSource.save(chatId, from.id, nextStep, nextStepInstance.toContent())
}
/**
* Convert transferred object to string.
*
* @return stringified object (defaults json from JsonContentConverter)
*
* @see io.github.dehuckakpyt.telegrambot.converter.JsonContentConverter
*/
private fun Any?.toContent(): String? = contentConverter.toContentOrNull(this)
}
© 2015 - 2025 Weber Informatics LLC | Privacy Policy