Skip to content

Commit

Permalink
7.7.1 (#321)
Browse files Browse the repository at this point in the history
* Added max handling retries to spring starter configuration.
* Fixed wrong name of `UpdateType.EDIT_MESSAGE` to `EDITED_MESSAGE` (thanks @KillWolfVlad) #319.
* Fixed a bug when using a provider other than logback.
  • Loading branch information
vendelieu authored Dec 31, 2024
1 parent 8b1a18d commit 7f4daba
Show file tree
Hide file tree
Showing 17 changed files with 142 additions and 23 deletions.
6 changes: 6 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,11 @@
# Telegram-bot (KtGram) Changelog

## 7.7.1

* Added max handling retries to spring starter configuration.
* Fixed wrong name of `UpdateType.EDIT_MESSAGE` to `EDITED_MESSAGE` (thanks @KillWolfVlad) #319.
* Fixed a bug when using a provider other than logback.

## 7.7.0

* Added `ktorJvmEngine` parameter to plugin with option to choose ktor engine.
Expand Down
2 changes: 1 addition & 1 deletion docs/build.gradle.kts
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ plugins {
}

dependencies {
rootProject.subprojects.filter { it.name !in listOf("helper", "ksp", "ktgram-gradle-plugin", "docs") }.forEach {
rootProject.subprojects.filter { it.name !in listOf("helper", "ksp", "docs") }.forEach {
dokka(project(":" + it.name))
}
}
3 changes: 3 additions & 0 deletions gradle/libs.versions.toml
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@
ktor = "3.0.3"
redis = "0.1.7"
logback = "1.5.15"
slf4j = "2.0.16"

datetime = "0.6.0"
serialization = "1.7.3"
Expand Down Expand Up @@ -43,6 +44,8 @@ ktor-server-core = { module = "io.ktor:ktor-server-core", version.ref = "ktor" }
ktor-server-netty = { module = "io.ktor:ktor-server-netty", version.ref = "ktor" }

logback = { module = "ch.qos.logback:logback-classic", version.ref = "logback" }
slf4j-api = { module = "org.slf4j:slf4j-api", version.ref = "slf4j" }


kotlin-serialization = { module = "org.jetbrains.kotlinx:kotlinx-serialization-json", version.ref = "serialization" }
kotlin-datetime = { module = "org.jetbrains.kotlinx:kotlinx-datetime", version.ref = "datetime" }
Expand Down
10 changes: 9 additions & 1 deletion ktgram-gradle-plugin/build.gradle.kts
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ plugins {
`java-gradle-plugin`
alias(libs.plugins.ktlinter)
alias(libs.plugins.gradle.publish)
dokka
}

gradlePlugin {
Expand All @@ -26,8 +27,15 @@ tasks.processResources {
val ktorVersion = libs.versions.ktor.get()
inputs.property("ktor", ktorVersion)

val logbackVersion = libs.versions.logback.get()
inputs.property("logback", logbackVersion)

filesMatching("ktgram.properties") {
expand("ktgramVer" to projectVersion, "ktorVer" to ktorVersion)
expand(
"ktgramVer" to projectVersion,
"ktorVer" to ktorVersion,
"logbackVer" to logbackVersion,
)
}
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,15 @@ import org.gradle.kotlin.dsl.listProperty
import org.gradle.kotlin.dsl.property
import javax.inject.Inject

/**
* Extension for [KtGramPlugin] that can be used to configure the KtGram DSL.
*
* @property packages list of packages to scan for KtGram annotations.
* @property addSnapshotRepo set to true to add the snapshot repository to the repositories list.
* @property forceVersion set to a version string to force library specific version.
* @property autoCleanClassData set to false to prevent the KSP processor from cleaning the class data automatically.
* @property ktorJvmEngine the Ktor JVM engine to use, defaults to [KtorJvmEngine.JAVA].
*/
abstract class KtGramExt
@Inject
constructor(
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -9,19 +9,28 @@ import org.gradle.kotlin.dsl.dependencies
import org.gradle.kotlin.dsl.get
import org.jetbrains.kotlin.gradle.dsl.KotlinMultiplatformExtension
import org.jetbrains.kotlin.gradle.dsl.KotlinProjectExtension
import org.jetbrains.kotlin.gradle.dsl.kotlinExtension
import org.jetbrains.kotlin.gradle.plugin.KotlinPlatformType
import org.jetbrains.kotlin.gradle.plugin.mpp.pm20.util.targets
import org.jetbrains.kotlin.gradle.utils.loadPropertyFromResources

abstract class KtGramPlugin : Plugin<Project> {
private val log = Logging.getLogger(KtGramPlugin::class.java)
private val libVer = loadPropertyFromResources("ktgram.properties", "ktgram.version")
private val ktorVer = loadPropertyFromResources("ktgram.properties", "ktgram.ktor")
private val logbackVer = loadPropertyFromResources("ktgram.properties", "ktgram.logback")

final override fun apply(project: Project) {
val pluginExtension = project.extensions.create("ktGram", KtGramExt::class.java)
val kspPluginPresent = project.plugins.hasPlugin("com.google.devtools.ksp")
var kspProcessorApplied = false
val isMultiplatform = project.plugins.hasPlugin("org.jetbrains.kotlin.multiplatform")

@Suppress("DEPRECATION")
val isJvm = project.kotlinExtension.targets.any {
it.platformType == KotlinPlatformType.jvm || it.platformType == KotlinPlatformType.androidJvm
}

project.configurations.configureEach {
if (name.startsWith("ksp")) dependencies.whenObjectAdded {
if (group == "eu.vendeli" && name == "ksp") kspProcessorApplied = true
Expand Down Expand Up @@ -52,12 +61,11 @@ abstract class KtGramPlugin : Plugin<Project> {
}
}

val ktorEngine = pluginExtension.ktorJvmEngine.getOrElse(KtorJvmEngine.JAVA)
if (ktorEngine != KtorJvmEngine.NONE && ktorEngine != KtorJvmEngine.JAVA) {
configurations.configureEach {
dependencies.removeIf { it.group == "io.ktor" && it.name == "ktor-client-java-jvm" }
}
dependencies.add("implementation", "io.ktor:ktor-client-${ktorEngine.artifact}-jvm:$ktorVer")
if (isJvm) {
val ktorEngine = pluginExtension.ktorJvmEngine.getOrElse(KtorJvmEngine.JAVA)
handleKtorEngine(ktorEngine)

handleLoggingProvider()
}

project.extensions.configure<KspExtension> {
Expand All @@ -77,6 +85,66 @@ abstract class KtGramPlugin : Plugin<Project> {
}
}

private fun Project.handleKtorEngine(engine: KtorJvmEngine) {
val isNone = engine == KtorJvmEngine.NONE
val isNotJava = engine != KtorJvmEngine.JAVA

if (isNone || isNotJava) configurations.configureEach {
log.debug("Removing ktor-client-java-jvm from $name configuration")
dependencies.removeIf { it.group == "io.ktor" && it.name == "ktor-client-java-jvm" }
}
if (!isNone && isNotJava) {
log.debug("Adding ktor-client-${engine.artifact}-jvm to $name configuration")
dependencies.add(
"implementation",
"io.ktor:ktor-client-${engine.artifact}-jvm:$ktorVer",
)
}
}

private fun Project.handleLoggingProvider() {
log.debug("Checking for logging providers")
var isProviderPresent = false
val knownProviders = setOf(
"ch.qos.logback" to "logback-classic",
"org.slf4j" to "slf4j-simple",
"org.slf4j" to "log4j-over-slf4j",
"org.slf4j" to "slf4j-log4j12",
"org.slf4j" to "slf4j-jdk14",
"org.slf4j" to "jul-to-slf4j",
"org.slf4j" to "slf4j-reload4j",
"org.slf4j" to "jcl-over-slf4j",
"org.slf4j" to "slf4j-jcl",
"org.slf4j" to "slf4j-jdk-platform-logging",
"org.apache.logging.log4j" to "log4j-slf4j-impl",
"org.apache.logging.log4j" to "log4j-slf4j2-impl",
"org.apache.logging.log4j" to "log4j-slf4j18-impl",
"org.tinylog" to "slf4j-tinylog",
"org.logevents" to "logevents",
"com.hkupty.penna" to "penna-core",
"io.jstach.rainbowgum" to "rainbowgum-core",
)

configurations.configureEach {
dependencies
.any {
it.group to it.name in knownProviders
}.takeIf { it }
?.let {
log.debug("Found logging provider in $name configuration")
isProviderPresent = true
}
}

if (!isProviderPresent) {
log.debug("Adding logback-classic to $name configuration")
dependencies.add(
"implementation",
"ch.qos.logback:logback-classic:$logbackVer",
)
}
}

private fun Project.applyDependencies(depVersion: String, isMultiplatform: Boolean, kspProcessorApplied: Boolean) {
if (isMultiplatform) extensions.configure<KotlinMultiplatformExtension> {
targets.forEach { target ->
Expand Down
3 changes: 2 additions & 1 deletion ktgram-gradle-plugin/src/main/resources/ktgram.properties
Original file line number Diff line number Diff line change
@@ -1,2 +1,3 @@
ktgram.version=${ktgramVer}
ktgram.ktor=${ktorVer}
ktgram.ktor=${ktorVer}
ktgram.logback=${logbackVer}
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ package eu.vendeli.spring.starter
import eu.vendeli.tgbot.TelegramBot
import eu.vendeli.tgbot.types.internal.UpdateType
import eu.vendeli.tgbot.utils.BotConfigurator
import kotlinx.coroutines.delay

/**
* Base configuration for a Telegram bot.
Expand Down Expand Up @@ -31,6 +32,9 @@ abstract class BotConfiguration {
/**
* Called when an exception is thrown by a handler.
* Handler is automatically restarts by default after this hook is called.
* Default behavior is to delay for 3 seconds.
*/
open suspend fun onHandlerException(exception: Throwable) {}
open suspend fun onHandlerException(exception: Throwable) {
delay(3000L)
}
}
Original file line number Diff line number Diff line change
@@ -1,7 +1,9 @@
package eu.vendeli.spring.starter

import eu.vendeli.tgbot.TelegramBot
import eu.vendeli.tgbot.utils.fqName
import io.ktor.client.HttpClient
import io.ktor.util.logging.KtorSimpleLogger
import kotlinx.coroutines.DelicateCoroutinesApi
import kotlinx.coroutines.GlobalScope
import kotlinx.coroutines.launch
Expand All @@ -18,6 +20,8 @@ open class TelegramAutoConfiguration(
protected val config: TgConfigProperties,
private val springClassManager: SpringClassManager,
) {
private val logger = KtorSimpleLogger(this::class.fqName)

@Autowired(required = false)
private var cfg: List<BotConfiguration>? = null

Expand All @@ -39,17 +43,30 @@ open class TelegramAutoConfiguration(
botCfg?.onInit(botInstance)
launch { botInstance.handleUpdatesCatching(botCfg) }
}
} else {
logger.warn("Bot ${bot.identifier} is not started automatically, make sure you handle this manually")
}

return@map botInstance
}

private suspend fun TelegramBot.handleUpdatesCatching(
botConfiguration: BotConfiguration? = null,
): Unit = try {
handleUpdates(botConfiguration?.allowedUpdates)
} catch (e: Throwable) {
botConfiguration?.onHandlerException(e)
handleUpdatesCatching(botConfiguration)
currentTry: Int = 1,
) {
try {
if (currentTry >= config.maxHandlingRetries) {
logger.error("Max handling retries reached")
return
}

if (currentTry > 1) logger.warn("Retrying to handle updates, attempt $currentTry")

handleUpdates(botConfiguration?.allowedUpdates)
} catch (e: Throwable) {
logger.error("An error occurred while handling updates", e)
botConfiguration?.onHandlerException(e)
handleUpdatesCatching(botConfiguration, currentTry + 1)
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ import org.springframework.boot.context.properties.bind.ConstructorBinding
*
* @property autoStartPolling Start long polling automatically after the bot is created.
* @property shareHttpClient Share the same HTTP client instance between all bots.
* @property maxHandlingRetries The maximum number of retries for handling updates.
* @property bot The list of bot configurations.
*/
@ConfigurationProperties(prefix = "ktgram")
Expand All @@ -16,6 +17,7 @@ data class TgConfigProperties
constructor(
val autoStartPolling: Boolean = true,
val shareHttpClient: Boolean = false,
val maxHandlingRetries: Int = 5,
val bot: List<BotProperties>,
) {
/**
Expand Down
2 changes: 1 addition & 1 deletion telegram-bot/api/telegram-bot.api
Original file line number Diff line number Diff line change
Expand Up @@ -8036,7 +8036,7 @@ public final class eu/vendeli/tgbot/types/internal/UpdateType : java/lang/Enum {
public static final field DELETED_BUSINESS_MESSAGES Leu/vendeli/tgbot/types/internal/UpdateType;
public static final field EDITED_BUSINESS_MESSAGE Leu/vendeli/tgbot/types/internal/UpdateType;
public static final field EDITED_CHANNEL_POST Leu/vendeli/tgbot/types/internal/UpdateType;
public static final field EDIT_MESSAGE Leu/vendeli/tgbot/types/internal/UpdateType;
public static final field EDITED_MESSAGE Leu/vendeli/tgbot/types/internal/UpdateType;
public static final field INLINE_QUERY Leu/vendeli/tgbot/types/internal/UpdateType;
public static final field MESSAGE Leu/vendeli/tgbot/types/internal/UpdateType;
public static final field MESSAGE_REACTION Leu/vendeli/tgbot/types/internal/UpdateType;
Expand Down
2 changes: 1 addition & 1 deletion telegram-bot/api/telegram-bot.klib.api
Original file line number Diff line number Diff line change
Expand Up @@ -376,7 +376,7 @@ final enum class eu.vendeli.tgbot.types.internal/UpdateType : kotlin/Enum<eu.ven
enum entry DELETED_BUSINESS_MESSAGES // eu.vendeli.tgbot.types.internal/UpdateType.DELETED_BUSINESS_MESSAGES|null[0]
enum entry EDITED_BUSINESS_MESSAGE // eu.vendeli.tgbot.types.internal/UpdateType.EDITED_BUSINESS_MESSAGE|null[0]
enum entry EDITED_CHANNEL_POST // eu.vendeli.tgbot.types.internal/UpdateType.EDITED_CHANNEL_POST|null[0]
enum entry EDIT_MESSAGE // eu.vendeli.tgbot.types.internal/UpdateType.EDIT_MESSAGE|null[0]
enum entry EDITED_MESSAGE // eu.vendeli.tgbot.types.internal/UpdateType.EDITED_MESSAGE|null[0]
enum entry INLINE_QUERY // eu.vendeli.tgbot.types.internal/UpdateType.INLINE_QUERY|null[0]
enum entry MESSAGE // eu.vendeli.tgbot.types.internal/UpdateType.MESSAGE|null[0]
enum entry MESSAGE_REACTION // eu.vendeli.tgbot.types.internal/UpdateType.MESSAGE_REACTION|null[0]
Expand Down
3 changes: 2 additions & 1 deletion telegram-bot/build.gradle.kts
Original file line number Diff line number Diff line change
Expand Up @@ -28,8 +28,9 @@ configuredKotlin {
implementation(libs.mockk)
}
jvmMain.dependencies {
implementation(libs.logback)
implementation(libs.ktor.client.java)
implementation(libs.slf4j.api)
compileOnly(libs.logback)
}
jsMain.dependencies {
implementation(libs.ktor.client.js)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -63,7 +63,7 @@ data class EditedMessageUpdate(
override val updateId: Int,
override val origin: Update,
val editedMessage: Message,
) : ProcessedUpdate(updateId, origin, UpdateType.EDIT_MESSAGE),
) : ProcessedUpdate(updateId, origin, UpdateType.EDITED_MESSAGE),
UserReference {
override val user = editedMessage.from!!
override val text = editedMessage.text.orEmpty()
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -8,8 +8,8 @@ enum class UpdateType {
@SerialName("message")
MESSAGE,

@SerialName("edit_message")
EDIT_MESSAGE,
@SerialName("edited_message")
EDITED_MESSAGE,

@SerialName("channel_post")
CHANNEL_POST,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -39,7 +39,7 @@ public fun FunctionalHandlingDsl.onMessage(block: suspend ActivityCtx<MessageUpd
* Action that is performed on the presence of [eu.vendeli.tgbot.types.Update.editedMessage] in the [eu.vendeli.tgbot.types.Update].
*/
public fun FunctionalHandlingDsl.onEditedMessage(block: suspend ActivityCtx<EditedMessageUpdate>.() -> Unit) {
functionalActivities.onUpdateActivities[UpdateType.EDIT_MESSAGE] = block.cast()
functionalActivities.onUpdateActivities[UpdateType.EDITED_MESSAGE] = block.cast()
}

/**
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -7,5 +7,5 @@ import io.ktor.util.logging.Logger

@Suppress("NOTHING_TO_INLINE")
internal actual inline fun getLogger(lvl: LogLvl, tag: String): Logger = KtorSimpleLogger(tag).apply {
safeCast<ch.qos.logback.classic.Logger>()?.level = Level.valueOf(lvl.name)
runCatching { safeCast<ch.qos.logback.classic.Logger>()?.level = Level.valueOf(lvl.name) }
}

0 comments on commit 7f4daba

Please sign in to comment.