import dev.inmo.kslog.common.KSLog import dev.inmo.kslog.common.LogLevel import dev.inmo.kslog.common.defaultMessageFormatter import dev.inmo.kslog.common.setDefaultKSLog import dev.inmo.micro_utils.coroutines.subscribeSafelyWithoutExceptions import dev.inmo.tgbotapi.extensions.api.answers.payments.answerPreCheckoutQueryOk import dev.inmo.tgbotapi.extensions.api.edit.edit import dev.inmo.tgbotapi.extensions.api.files.downloadFile import dev.inmo.tgbotapi.extensions.api.files.downloadFileToTemp import dev.inmo.tgbotapi.extensions.api.get.getStarTransactions import dev.inmo.tgbotapi.extensions.api.send.reply import dev.inmo.tgbotapi.extensions.api.send.send import dev.inmo.tgbotapi.extensions.behaviour_builder.telegramBotWithBehaviourAndLongPolling import dev.inmo.tgbotapi.extensions.behaviour_builder.triggers_handling.* import dev.inmo.tgbotapi.extensions.utils.extensions.sameChat import dev.inmo.tgbotapi.extensions.utils.types.buttons.dataButton import dev.inmo.tgbotapi.extensions.utils.types.buttons.flatInlineKeyboard import dev.inmo.tgbotapi.extensions.utils.types.buttons.inlineKeyboard import dev.inmo.tgbotapi.extensions.utils.types.buttons.payButton import dev.inmo.tgbotapi.extensions.utils.withContentOrNull import dev.inmo.tgbotapi.requests.abstracts.asMultipartFile import dev.inmo.tgbotapi.types.RawChatId import dev.inmo.tgbotapi.types.UserId import dev.inmo.tgbotapi.types.buttons.InlineKeyboardMarkup import dev.inmo.tgbotapi.types.files.* import dev.inmo.tgbotapi.types.media.TelegramPaidMediaPhoto import dev.inmo.tgbotapi.types.media.TelegramPaidMediaVideo import dev.inmo.tgbotapi.types.media.toTelegramPaidMediaPhoto import dev.inmo.tgbotapi.types.media.toTelegramPaidMediaVideo import dev.inmo.tgbotapi.types.message.content.TextContent import dev.inmo.tgbotapi.types.message.textsources.TextSourcesList import dev.inmo.tgbotapi.types.message.textsources.textSourcesOrElse import dev.inmo.tgbotapi.types.payments.LabeledPrice import dev.inmo.tgbotapi.types.payments.stars.StarTransaction import dev.inmo.tgbotapi.utils.bold import dev.inmo.tgbotapi.utils.buildEntities import dev.inmo.tgbotapi.utils.regular import dev.inmo.tgbotapi.utils.row import kotlinx.coroutines.CoroutineScope import kotlinx.coroutines.Dispatchers suspend fun main(vararg args: String) { val botToken = args.first() val adminUserId = args.getOrNull(1) ?.toLongOrNull() ?.let(::RawChatId) ?.let(::UserId) ?: error("Pass user-admin for full access to the bot") val isDebug = args.any { it == "debug" } val isTestServer = args.any { it == "testServer" } if (isDebug) { setDefaultKSLog( KSLog { level: LogLevel, tag: String?, message: Any, throwable: Throwable? -> println(defaultMessageFormatter(level, tag, message, throwable)) } ) } telegramBotWithBehaviourAndLongPolling(botToken, CoroutineScope(Dispatchers.IO), testServer = isTestServer) { val payload = "sample payload" command("start") { reply( it, price = LabeledPrice("1", 1L), title = "Sample", description = "Sample description", payload = payload, replyMarkup = flatInlineKeyboard { payButton("Pay") }, ) } onPreCheckoutQuery(initialFilter = { it.invoicePayload == payload }) { answerPreCheckoutQueryOk(it) } val transactionsDataPrefix = "getStarTransactions" fun buildTransactionsData(offset: Int, limit: Int = 10) = "$transactionsDataPrefix $offset $limit" fun parseTransactionsData(data: String): Pair = data.split(" ").drop(1).let { it.first().toInt() to it.last().toInt() } suspend fun buildStarTransactionsPage(offset: Int, limit: Int = 10): Pair { val transactions = getStarTransactions(offset, limit) return buildEntities { transactions.transactions.forEach { regular("Transaction Id: ") + bold(it.id.string) + "\n" regular("Date: ") + bold(it.date.asDate.toStringDefault()) + "\n" regular("Amount: ") + bold(it.amount.toString()) + "\n" when (it) { is StarTransaction.Incoming -> { regular("Type: ") + bold("incoming") + "\n" regular("Partner: ") + bold(it.partner.type) + "\n" } is StarTransaction.Outgoing -> { regular("Type: ") + bold("outgoing") + "\n" regular("Partner: ") + bold(it.partner.type) + "\n" } is StarTransaction.Unknown -> { regular("Type: ") + bold("unknown") + "\n" regular("Partner: ") + bold(it.partner.type) + "\n" } } } } to inlineKeyboard { row { val prevOffset = (offset - limit).coerceAtLeast(0) if (prevOffset < offset) { dataButton("<", buildTransactionsData(prevOffset, limit)) } val nextOffset = (offset + limit) dataButton(">", buildTransactionsData(nextOffset, limit)) } } } onCommand("transactions", initialFilter = { it.sameChat(adminUserId) }) { val (text, keyboard) = buildStarTransactionsPage(0) reply(it, text, replyMarkup = keyboard) } onMessageDataCallbackQuery(Regex("$transactionsDataPrefix \\d+ \\d+")) { val (offset, limit) = parseTransactionsData(it.data) val (text, keyboard) = buildStarTransactionsPage(offset, limit) edit( it.message.withContentOrNull() ?: return@onMessageDataCallbackQuery, text, replyMarkup = keyboard, ) } onVisualGalleryMessages { send( it.chat, 1, it.content.group.mapNotNull { val file = downloadFileToTemp(it.content.media) when (it.content.media) { is VideoFile -> { TelegramPaidMediaVideo( file.asMultipartFile() ) } is PhotoSize -> { TelegramPaidMediaPhoto( file.asMultipartFile() ) } else -> null } }, it.content.textSources, showCaptionAboveMedia = true ) } onPhoto { send( it.chat, 1, listOf(it.content.media.toTelegramPaidMediaPhoto()) ) } onVideo { send( it.chat, 1, listOf(it.content.media.toTelegramPaidMediaVideo()) ) } onPaidMediaInfoContent { println(it) } onRefundedPayment { reply( it, "Received your refund: ${it.chatEvent.payment}" ) } allUpdatesFlow.subscribeSafelyWithoutExceptions(this) { println(it) } }.second.join() }