Why it exists
Every Telegram bot starts as a when over the incoming message and ends as callbacks with a nullable awaitingCity field in a map somewhere. Not laziness — the framework hands you an event and no place to put the fact that this user is halfway through something.
state + input → newState + effects
What that buys
A transition is a function: current state and input in, next state and a list of effects out. It performs none of them. From that one split everything else follows —
- a transition is testable with no bot, no network and no mocks;
- a conversation replays from its inputs, because nothing hid in a closure;
- persisting a wizard is persisting a value, which is what makes it survive a restart.
Two transports, same shape
repositories { maven("https://reposilite.kotlin.website/snapshots") }
dependencies {
implementation("ru.workinprogress.telek:core:<VERSION>")
// pick one:
implementation("ru.workinprogress.telek:telegram:<VERSION>") // kotlin-telegram-bot
// implementation("ru.workinprogress.telek:ktg:<VERSION>") // ktgbotapi
}
Swapping transports is only possible because the core never touches the network. If transitions did their own sends, the transport would be baked into every one of them.
Native, because the core is pure
core, ktg, router, router-ktg, persistence and testing publish for JVM, linuxX64 and linuxArm64, so a bot can ship as a single native binary. telegram and router-telegram stay JVM-only, because kotlin-telegram-bot is — the honest shape of the dependency rather than a limitation to work around.