Please wait. This can take some minutes ...
Many resources are needed to download a project. Please understand that we have to compensate our server costs. Thank you in advance.
Project price only 1 $
You can buy this project and download/modify it how often you want.
commonMain.KeyboardBuilder.kt Maven / Gradle / Ivy
package dev.inmo.tgbotapi.keyboards.lib
import dev.inmo.micro_utils.common.Either
import dev.inmo.micro_utils.common.mapOnFirst
import dev.inmo.micro_utils.common.mapOnSecond
import dev.inmo.micro_utils.coroutines.launchSafelyWithoutExceptions
import dev.inmo.tgbotapi.extensions.behaviour_builder.BehaviourContext
import dev.inmo.tgbotapi.extensions.behaviour_builder.CustomBehaviourContextAndTypeReceiver
import dev.inmo.tgbotapi.extensions.behaviour_builder.expectations.waitBaseInlineQuery
import dev.inmo.tgbotapi.extensions.behaviour_builder.expectations.waitInlineMessageIdDataCallbackQuery
import dev.inmo.tgbotapi.extensions.behaviour_builder.expectations.waitMessageDataCallbackQuery
import dev.inmo.tgbotapi.extensions.behaviour_builder.expectations.waitPreCheckoutQueries
import dev.inmo.tgbotapi.extensions.behaviour_builder.triggers_handling.onBaseInlineQuery
import dev.inmo.tgbotapi.extensions.behaviour_builder.triggers_handling.onDataCallbackQuery
import dev.inmo.tgbotapi.extensions.behaviour_builder.triggers_handling.onPreCheckoutQuery
import dev.inmo.tgbotapi.extensions.behaviour_builder.utils.SimpleFilter
import dev.inmo.tgbotapi.extensions.utils.extensions.sameMessage
import dev.inmo.tgbotapi.requests.edit.reply_markup.EditChatMessageReplyMarkup
import dev.inmo.tgbotapi.requests.edit.reply_markup.EditInlineMessageReplyMarkup
import dev.inmo.tgbotapi.types.*
import dev.inmo.tgbotapi.types.InlineQueries.query.BaseInlineQuery
import dev.inmo.tgbotapi.types.InlineQueries.query.InlineQuery
import dev.inmo.tgbotapi.types.buttons.InlineKeyboardButtons.*
import dev.inmo.tgbotapi.types.payments.PreCheckoutQuery
import dev.inmo.tgbotapi.types.queries.callback.DataCallbackQuery
import dev.inmo.tgbotapi.types.queries.callback.InaccessibleMessageDataCallbackQuery
import dev.inmo.tgbotapi.types.queries.callback.InlineMessageIdDataCallbackQuery
import dev.inmo.tgbotapi.types.queries.callback.MessageDataCallbackQuery
import dev.inmo.tgbotapi.types.webapps.WebAppInfo
import dev.inmo.tgbotapi.utils.MatrixBuilder
import kotlinx.coroutines.flow.*
class KeyboardBuilder : MatrixBuilder>() {
sealed interface Button {
class Data (
val id: String,
val reaction: Reaction,
val callbacksRegex: Regex = Regex(id),
val textBuilder: suspend BC.() -> String
) : Button {
/**
* Reaction onto [DataCallbackQuery] event
*/
sealed interface Reaction {
/**
* When [DataCallbackQuery] event happen, will build new [KeyboardMenu] as well as react onto event
*
* @param keyboardMenuBuilder Will receive null [DataCallbackQuery] on setup of triggers stage
*/
class Keyboard(
val keyboardMenuBuilder: suspend BC.(DataCallbackQuery?) -> KeyboardMenu?
) : Reaction
/**
* Simple action as reaction onto [DataCallbackQuery] event
*/
class Action(
val callback: suspend BC.(DataCallbackQuery) -> Unit
) : Reaction
companion object
}
override suspend fun buildButton(context: BC): InlineKeyboardButton = CallbackDataInlineKeyboardButton(
text = context.textBuilder(),
callbackData = id
)
override suspend fun includeTriggers(context: BC) {
with(context) {
when (reaction) {
is Reaction.Action -> onDataCallbackQuery(callbacksRegex) {
reaction.callback(this, it)
}
is Reaction.Keyboard -> {
reaction.keyboardMenuBuilder.invoke(this, null) ?.setupTriggers(this)
onDataCallbackQuery(callbacksRegex) {
val keyboard = reaction.keyboardMenuBuilder(this, it) ?.buildButtons(this)
when (it) {
is InlineMessageIdDataCallbackQuery -> execute(
EditInlineMessageReplyMarkup(
it.inlineMessageId,
keyboard
)
)
is MessageDataCallbackQuery -> execute(
EditChatMessageReplyMarkup(
it.message.chat.id,
it.message.messageId,
keyboard
)
)
is InaccessibleMessageDataCallbackQuery -> return@onDataCallbackQuery
}
}
}
}
}
}
override suspend fun performWaiters(
context: BC,
messageInfo: Either, InlineMessageId>
): Flow?> {
val filteredFlow = with(context) {
messageInfo.mapOnFirst { (chatId, messageId) ->
waitMessageDataCallbackQuery().filter {
it.message.sameMessage(chatId, messageId) && callbacksRegex.matches(it.data)
}
} ?: messageInfo.mapOnSecond { messageId ->
waitInlineMessageIdDataCallbackQuery().filter {
it.inlineMessageId == messageId && callbacksRegex.matches(it.data)
}
} ?: emptyFlow()
}
val resultFlow: Flow?> = when (reaction) {
is Reaction.Action -> with(context) {
launchSafelyWithoutExceptions {
filteredFlow.collect {
reaction.callback(context, it)
}
}
emptyFlow()
}
is Reaction.Keyboard -> filteredFlow.map {
reaction.keyboardMenuBuilder(context, it)
}
}
return resultFlow
}
}
class Game (
val textBuilder: suspend BC.() -> String
) : Button {
override suspend fun buildButton(context: BC): InlineKeyboardButton = CallbackGameInlineKeyboardButton(
textBuilder(context)
)
override suspend fun includeTriggers(context: BC) {}
}
class LoginUrl (
val textBuilder: suspend BC.() -> String,
val loginUrlBuilder: suspend BC.() -> LoginURL
) : Button {
override suspend fun buildButton(context: BC): InlineKeyboardButton = LoginURLInlineKeyboardButton(
textBuilder(context),
loginUrlBuilder(context)
)
override suspend fun includeTriggers(context: BC) {}
}
class Pay (
val textBuilder: suspend BC.() -> String,
val initialFilter: SimpleFilter? = null,
val onPreCheckoutQueryCallback: CustomBehaviourContextAndTypeReceiver? = null
) : Button {
override suspend fun buildButton(context: BC): InlineKeyboardButton = PayInlineKeyboardButton(
textBuilder(context),
)
override suspend fun includeTriggers(context: BC) {
with(context) {
onPreCheckoutQueryCallback ?.let { onPreCheckout ->
onPreCheckoutQuery(initialFilter) {
onPreCheckout(it)
}
}
}
}
override suspend fun performWaiters(
context: BC,
messageInfo: Either, InlineMessageId>
): Flow?> {
with (context) {
onPreCheckoutQueryCallback ?.let { onPreCheckoutQueryCallback ->
launchSafelyWithoutExceptions {
waitPreCheckoutQueries().collect {
onPreCheckoutQueryCallback(context, it)
}
}
}
}
return super.performWaiters(context, messageInfo)
}
}
class SwitchInlineQueryChosenChat (
val textBuilder: suspend BC.() -> String,
val parametersBuilder: suspend BC.() -> dev.inmo.tgbotapi.types.buttons.InlineKeyboardButtons.SwitchInlineQueryChosenChat,
val initialFilter: SimpleFilter? = null,
val onBaseInlineQueryCallback: CustomBehaviourContextAndTypeReceiver? = null
) : Button {
override suspend fun buildButton(context: BC): InlineKeyboardButton = SwitchInlineQueryChosenChatInlineKeyboardButton(
textBuilder(context),
parametersBuilder(context),
)
override suspend fun includeTriggers(context: BC) {
with(context) {
onBaseInlineQueryCallback ?.let { onBaseInlineQueryCallback ->
onBaseInlineQuery(initialFilter) {
onBaseInlineQueryCallback(it)
}
}
}
}
override suspend fun performWaiters(
context: BC,
messageInfo: Either, InlineMessageId>
): Flow?> {
with (context) {
onBaseInlineQueryCallback ?.let { onBaseInlineQueryCallback ->
launchSafelyWithoutExceptions {
waitBaseInlineQuery().collect {
onBaseInlineQueryCallback(context, it)
}
}
}
}
return super.performWaiters(context, messageInfo)
}
}
class SwitchInlineQueryCurrentChat (
val textBuilder: suspend BC.() -> String,
val initialInlineQueryBuilder: suspend BC.() -> String,
val initialFilter: SimpleFilter? = null,
val onBaseInlineQueryCallback: CustomBehaviourContextAndTypeReceiver? = null
) : Button {
override suspend fun buildButton(context: BC): InlineKeyboardButton = SwitchInlineQueryCurrentChatInlineKeyboardButton(
textBuilder(context),
initialInlineQueryBuilder(context),
)
override suspend fun includeTriggers(context: BC) {
with(context) {
onBaseInlineQueryCallback ?.let { onBaseInlineQueryCallback ->
onBaseInlineQuery(initialFilter) {
onBaseInlineQueryCallback(it)
}
}
}
}
override suspend fun performWaiters(
context: BC,
messageInfo: Either, InlineMessageId>
): Flow?> {
with (context) {
onBaseInlineQueryCallback ?.let { onBaseInlineQueryCallback ->
launchSafelyWithoutExceptions {
waitBaseInlineQuery().collect {
onBaseInlineQueryCallback(context, it)
}
}
}
}
return super.performWaiters(context, messageInfo)
}
}
class SwitchInlineQuery (
val textBuilder: suspend BC.() -> String,
val initialInlineQueryBuilder: suspend BC.() -> String,
val initialFilter: SimpleFilter? = null,
val onBaseInlineQueryCallback: CustomBehaviourContextAndTypeReceiver? = null
) : Button {
override suspend fun buildButton(context: BC): InlineKeyboardButton = SwitchInlineQueryInlineKeyboardButton(
textBuilder(context),
initialInlineQueryBuilder(context),
)
override suspend fun includeTriggers(context: BC) {
with(context) {
onBaseInlineQueryCallback ?.let { onBaseInlineQueryCallback ->
onBaseInlineQuery(initialFilter) {
onBaseInlineQueryCallback(it)
}
}
}
}
override suspend fun performWaiters(
context: BC,
messageInfo: Either, InlineMessageId>
): Flow?> {
with (context) {
onBaseInlineQueryCallback ?.let { onBaseInlineQueryCallback ->
launchSafelyWithoutExceptions {
waitBaseInlineQuery().collect {
onBaseInlineQueryCallback(context, it)
}
}
}
}
return super.performWaiters(context, messageInfo)
}
}
class URL (
val textBuilder: suspend BC.() -> String,
val urlBuilder: suspend BC.() -> String
) : Button {
override suspend fun buildButton(context: BC): InlineKeyboardButton = URLInlineKeyboardButton(
textBuilder(context),
urlBuilder(context),
)
override suspend fun includeTriggers(context: BC) {}
}
class WebApp (
val textBuilder: suspend BC.() -> String,
val webAppInfoBuilder: suspend BC.() -> WebAppInfo
) : Button {
override suspend fun buildButton(context: BC): InlineKeyboardButton = WebAppInlineKeyboardButton(
textBuilder(context),
webAppInfoBuilder(context),
)
override suspend fun includeTriggers(context: BC) {}
}
suspend fun buildButton(context: BC): InlineKeyboardButton
suspend fun includeTriggers(context: BC)
suspend fun performWaiters(context: BC, messageInfo: Either, InlineMessageId>): Flow?> = emptyFlow()
}
fun buildFreezed(): KeyboardMenu {
val freezedMatrix = matrix.map { it.toList() }
return KeyboardMenu(
freezedMatrix
)
}
fun buildLazy(): KeyboardMenu {
return KeyboardMenu {
matrix
}
}
}