mirror of
https://github.com/InsanusMokrassar/TelegramBotAPI-examples.git
synced 2026-09-02 14:19:05 +00:00
101 lines
5.2 KiB
Kotlin
101 lines
5.2 KiB
Kotlin
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.runCatchingLogging
|
|
import dev.inmo.micro_utils.coroutines.subscribeSafelyWithoutExceptions
|
|
import dev.inmo.tgbotapi.extensions.api.bot.getMe
|
|
import dev.inmo.tgbotapi.extensions.api.send.reply
|
|
import dev.inmo.tgbotapi.extensions.api.send.send
|
|
import dev.inmo.tgbotapi.extensions.behaviour_builder.expectations.waitBotSubscriptionUpdated
|
|
import dev.inmo.tgbotapi.extensions.behaviour_builder.telegramBotWithBehaviourAndLongPolling
|
|
import dev.inmo.tgbotapi.extensions.behaviour_builder.triggers_handling.onBotSubscriptionUpdated
|
|
import dev.inmo.tgbotapi.extensions.behaviour_builder.triggers_handling.onCommand
|
|
import dev.inmo.tgbotapi.types.payments.BotSubscriptionUpdated
|
|
import kotlinx.coroutines.CoroutineScope
|
|
import kotlinx.coroutines.Dispatchers
|
|
import kotlinx.coroutines.flow.first
|
|
|
|
/**
|
|
* Runs a long-polling demonstration of bot payment-subscription updates introduced in Telegram Bot API 10.2.
|
|
*
|
|
* Telegram sends a `subscription` update carrying a [BotSubscriptionUpdated] when a user cancels a recurring
|
|
* payment subscription to the bot, re-enables a canceled subscription, or a subscription payment fails. This
|
|
* example consumes those updates; it does not create recurring invoices.
|
|
*
|
|
* Key concepts demonstrated:
|
|
* - [onBotSubscriptionUpdated] — trigger whose handler receives a [BotSubscriptionUpdated] (`user`,
|
|
* `invoicePayload`, `state`) and makes a best-effort status notification to the subscriber
|
|
* - [BotSubscriptionUpdated.State] — the typed sealed state: [BotSubscriptionUpdated.State.Active],
|
|
* [BotSubscriptionUpdated.State.Canceled], [BotSubscriptionUpdated.State.Failed] (data objects) and the
|
|
* [BotSubscriptionUpdated.State.Unknown] value-class fallback for any future state
|
|
* - `botSubscriptionUpdatedUpdatesFlow` — the raw update flow of
|
|
* [dev.inmo.tgbotapi.types.update.BotSubscriptionUpdatedUpdate] (available directly because a
|
|
* BehaviourContext is a `FlowsUpdatesFilter`); each emission's payload is its `data`
|
|
* - [waitBotSubscriptionUpdated] — expectation returning a flow of [BotSubscriptionUpdated]; the
|
|
* `/wait_subscription` handler takes its next value without a timeout and replies in the command's chat
|
|
*
|
|
* The first command-line argument is always treated as the bot token. Later arguments equal to `debug` and
|
|
* `testServer` enable console diagnostic logging and Telegram's Bot API test environment, respectively.
|
|
*
|
|
* @param args bot token followed by optional, case-sensitive `debug` and `testServer` flags
|
|
*/
|
|
suspend fun main(vararg args: String) {
|
|
val botToken = args.first()
|
|
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 me = getMe()
|
|
println("Bot info: $me")
|
|
|
|
// subscription update: react to the typed BotSubscriptionUpdated.State
|
|
onBotSubscriptionUpdated { update ->
|
|
val user = update.user
|
|
val payload = update.invoicePayload
|
|
val stateText = when (val state = update.state) {
|
|
BotSubscriptionUpdated.State.Active -> "active ✅"
|
|
BotSubscriptionUpdated.State.Canceled -> "canceled ❌"
|
|
BotSubscriptionUpdated.State.Failed -> "payment failed ⚠️"
|
|
// Unknown is a value class carrying the raw state name — future-proof fallback
|
|
is BotSubscriptionUpdated.State.Unknown -> "unknown (${state.name})"
|
|
}
|
|
println("Subscription update from ${user.id}: payload=$payload, state=${update.state.name}")
|
|
|
|
// notify the subscriber (only works if they have an open chat with the bot)
|
|
runCatchingLogging {
|
|
send(user.id, "Your subscription (payload: $payload) is now: $stateText")
|
|
}
|
|
}
|
|
|
|
// Raw flow variant of the same updates. BehaviourContext : FlowsUpdatesFilter, so the flow is
|
|
// available directly; each emission is a BotSubscriptionUpdatedUpdate whose payload is `.data`.
|
|
botSubscriptionUpdatedUpdatesFlow.subscribeSafelyWithoutExceptions(this) { update ->
|
|
println("[flow] update ${update.updateId}: user=${update.data.user.id}, state=${update.data.state.name}")
|
|
}
|
|
|
|
// waitBotSubscriptionUpdated expectation: suspend until the next subscription update
|
|
onCommand("wait_subscription") {
|
|
reply(it, "Waiting for the next subscription update...")
|
|
val update = waitBotSubscriptionUpdated().first()
|
|
reply(it, "Subscription update: state=${update.state.name}, payload=${update.invoicePayload}")
|
|
}
|
|
|
|
allUpdatesFlow.subscribeSafelyWithoutExceptions(this) {
|
|
println(it)
|
|
}
|
|
}.second.join()
|
|
}
|