mirror of
https://github.com/InsanusMokrassar/TelegramBotAPI-examples.git
synced 2026-07-31 06:06:23 +00:00
Add Bot API 10.2 Bot Subscriptions support example
New BotSubscriptionsBot demonstrating the 36.0.0 subscription-updates API: - onBotSubscriptionUpdated trigger reading user / invoicePayload / state - exhaustive handling of the typed BotSubscriptionUpdated.State sealed type (Active / Canceled / Failed data objects + Unknown value-class fallback) - botSubscriptionUpdatedUpdatesFlow raw update flow - waitBotSubscriptionUpdated expectation Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
12
BotSubscriptionsBot/README.md
Normal file
12
BotSubscriptionsBot/README.md
Normal file
@@ -0,0 +1,12 @@
|
||||
# BotSubscriptionsBot
|
||||
|
||||
Demonstrates Bot Subscriptions (subscription updates) support introduced in Telegram Bot API 10.2.
|
||||
|
||||
The bot logs and reacts to `subscription` updates — when a user's recurring Telegram Stars subscription to
|
||||
the bot becomes active, is canceled, or fails — handling the typed `BotSubscriptionUpdated.State`.
|
||||
|
||||
## Launch
|
||||
|
||||
```bash
|
||||
../gradlew :BotSubscriptionsBot:run --args="BOT_TOKEN"
|
||||
```
|
||||
21
BotSubscriptionsBot/build.gradle
Normal file
21
BotSubscriptionsBot/build.gradle
Normal file
@@ -0,0 +1,21 @@
|
||||
buildscript {
|
||||
repositories {
|
||||
mavenCentral()
|
||||
}
|
||||
|
||||
dependencies {
|
||||
classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:$kotlin_version"
|
||||
}
|
||||
}
|
||||
|
||||
apply plugin: 'kotlin'
|
||||
apply plugin: 'application'
|
||||
|
||||
mainClassName="BotSubscriptionsBotKt"
|
||||
|
||||
|
||||
dependencies {
|
||||
implementation "org.jetbrains.kotlin:kotlin-stdlib:$kotlin_version"
|
||||
|
||||
implementation "dev.inmo:tgbotapi:$telegram_bot_api_version"
|
||||
}
|
||||
93
BotSubscriptionsBot/src/main/kotlin/BotSubscriptionsBot.kt
Normal file
93
BotSubscriptionsBot/src/main/kotlin/BotSubscriptionsBot.kt
Normal file
@@ -0,0 +1,93 @@
|
||||
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
|
||||
|
||||
/**
|
||||
* This bot demonstrates Bot Subscriptions (subscription updates) support introduced in Telegram Bot API 10.2.
|
||||
*
|
||||
* When a user starts, renews, cancels or fails to pay a subscription to the bot (a recurring Telegram Stars
|
||||
* payment), the bot receives a `subscription` update carrying a [BotSubscriptionUpdated].
|
||||
*
|
||||
* Key concepts demonstrated:
|
||||
* - [onBotSubscriptionUpdated] — trigger whose handler receives a [BotSubscriptionUpdated] (`user`,
|
||||
* `invoicePayload`, `state`)
|
||||
* - [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]
|
||||
*/
|
||||
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()
|
||||
}
|
||||
@@ -85,3 +85,5 @@ include ":JoinRequestQueriesBot"
|
||||
include ":EphemeralMessagesBot"
|
||||
|
||||
include ":CommunitiesBot"
|
||||
|
||||
include ":BotSubscriptionsBot"
|
||||
|
||||
Reference in New Issue
Block a user