Handlers can now wait for a specific update before further progress
### Handlers can now wait for successor events
This release contains utilities to wait for updates within a handler. This allows handlers to be stateful.
Until now, state needed to be global, and thus needed disambiguation and explicit synchronization:
```kotlin
val counterMessages = mutableMapOf<Message.Id, Message>()
val counters = mutableMapOf<Message.Id, Int>()
val lock = Mutex()
command("/counter") { msg ->
bot.sendMessage(
msg.chat.id,
"0",
replyMarkup = InlineKeyboardMarkup(
listOf(listOf(InlineKeyboardButton("+", callbackData = "plus")))
)
)
lock.withLock {
counterMessages[msg.id] = msg
counters[msg.id] = 0
}
// Clean up the resources in the future to avoid infinite memory growth
GlobalScope.launch {
delay(24.hours)
lock.withLock {
counterMessages.remove(msg.id)
counters.remove(msg.id)
}
}
}
callbackQuery {
val initialMessageId = (it.message as Message).id
val (initialMessage, counter) = lock.withLock {
val initialMessage = counterMessages[initialMessageId] ?: return@callbackQuery
var counter = counters[initialMessageId] ?: return@callbackQuery
counter++
counters[initialMessageId] = counter
initialMessage to counter
}
bot.editMessageText(
chat = initialMessage.chat.id,
messageId = initialMessageId,
text = "$counter"
)
}
```
In this version, route handlers gain the ability to wait until specific events themselves, which allows them to use local state:
```kotlin
command("/counter") { msg ->
var counter = 0
msg.reply(
text = "0",
replyMarkup = InlineKeyboardMarkup(
InlineKeyboardButton("+", callbackData = "plus"),
)
)
selectUntilStopped {
timeout(24.hours) {
stop()
}
msg.callbackQuery("plus") {
counter++
msg.edit(
text = "$counter",
replyMarkup = InlineKeyboardMarkup(
InlineKeyboardButton("+", callbackData = "plus"),
)
)
}
}
}
```
### Entities
- Added a convenience constructor for a single-line `InlineKeyboardMarkup` (ca16161d, !47)
### SDK
- Added `Message.reply()` (f95ac860, !47)
- Added `Message.edit()` (002b0cb6, !47)
- Added `Message.awaitReply()` (fd0b362f, !47)
- Added `awaitUpdate()` (e76a8dfc, !47)
- Added `selectFirst {}` to wait for one of multiple updates (da160116, !47)
- Added `selectUntilStopped {}` to wait in a loop for one of multiple events (89d1f204, !47)
- All handlers are now executed concurrently (d02c76f3, !47)
- Added the bot as a receiver in all handlers (!45)
- Marked `sendMessage` and `editMessageText` as ignorable return values (#4, !44)
- Created `BotRouter.HandlerContext` to inject information into handlers (!47)