From 774aeaa56497eff0df1df9b5439d12605e1d0d5e Mon Sep 17 00:00:00 2001 From: Maksim Pischulenok Date: Wed, 9 Apr 2025 00:52:07 +0300 Subject: [PATCH] Refactor --- .../kotlin/com/pischule/memestv/BotService.kt | 98 ------------------- .../pischule/memestv/bot/BotConfiguration.kt | 47 +++++++++ .../pischule/memestv/{ => bot}/BotProps.kt | 2 +- .../com/pischule/memestv/bot/BotService.kt | 25 +++++ .../bot/handler/PhotoHandlerService.kt | 49 ++++++++++ .../bot/handler/ThisCommandHandlerService.kt | 48 +++++++++ .../memestv/{ => s3}/FileUploaderService.kt | 2 +- .../com/pischule/memestv/{ => s3}/S3Config.kt | 2 +- .../com/pischule/memestv/{ => s3}/S3Props.kt | 2 +- 9 files changed, 173 insertions(+), 102 deletions(-) delete mode 100644 src/main/kotlin/com/pischule/memestv/BotService.kt create mode 100644 src/main/kotlin/com/pischule/memestv/bot/BotConfiguration.kt rename src/main/kotlin/com/pischule/memestv/{ => bot}/BotProps.kt (84%) create mode 100644 src/main/kotlin/com/pischule/memestv/bot/BotService.kt create mode 100644 src/main/kotlin/com/pischule/memestv/bot/handler/PhotoHandlerService.kt create mode 100644 src/main/kotlin/com/pischule/memestv/bot/handler/ThisCommandHandlerService.kt rename src/main/kotlin/com/pischule/memestv/{ => s3}/FileUploaderService.kt (94%) rename src/main/kotlin/com/pischule/memestv/{ => s3}/S3Config.kt (96%) rename src/main/kotlin/com/pischule/memestv/{ => s3}/S3Props.kt (86%) diff --git a/src/main/kotlin/com/pischule/memestv/BotService.kt b/src/main/kotlin/com/pischule/memestv/BotService.kt deleted file mode 100644 index 49a2859..0000000 --- a/src/main/kotlin/com/pischule/memestv/BotService.kt +++ /dev/null @@ -1,98 +0,0 @@ -package com.pischule.memestv - -import com.github.kotlintelegrambot.Bot -import com.github.kotlintelegrambot.bot -import com.github.kotlintelegrambot.dispatch -import com.github.kotlintelegrambot.dispatcher.message -import com.github.kotlintelegrambot.dispatcher.photos -import com.github.kotlintelegrambot.entities.ChatId -import com.github.kotlintelegrambot.entities.reaction.ReactionType -import io.github.oshai.kotlinlogging.KotlinLogging -import jakarta.annotation.PostConstruct -import jakarta.annotation.PreDestroy -import org.springframework.boot.context.properties.EnableConfigurationProperties -import org.springframework.context.annotation.Profile -import org.springframework.stereotype.Service - -val log = KotlinLogging.logger {} - -@Profile("!test") -@EnableConfigurationProperties(BotProps::class) -@Service -class BotService( - private val botProps: BotProps, - private val fileUploaderService: FileUploaderService, -) { - private lateinit var bot: Bot - - @PostConstruct - fun start() { - bot = bot { - token = botProps.token - dispatch { - message { - try { - val chatId = message.chat.id - val replyToPhotos = - message.replyToMessage?.photo?.takeIf { it.isNotEmpty() } - if ( - chatId == botProps.destinationChatId && - message.text?.lowercase() == "this" && - replyToPhotos != null - ) { - val maxResPhoto = replyToPhotos.last().fileId - val fileBytes = bot.downloadFileBytes(maxResPhoto) - fileBytes?.let { - log.info { "Downloaded a file $maxResPhoto from telegram" } - fileUploaderService.uploadFile(it) - log.info { "Uploaded a file $maxResPhoto to s3" } - bot.setMessageReaction( - chatId = ChatId.fromId(message.chat.id), - messageId = message.messageId, - reaction = listOf(ReactionType.Emoji("👍")), - ) - .onError { error -> - log.warn { "Failed to react to message: $error" } - } - } - } - } catch (e: Error) { - log.error(e) { "Error while handling message" } - } - } - photos { - val message = this.message - - if (message.chat.id != botProps.destinationChatId) { - bot.forwardMessage( - chatId = ChatId.fromId(botProps.destinationChatId), - fromChatId = ChatId.fromId(message.chat.id), - messageId = message.messageId, - ) - .fold( - { log.info { "Forwarded pictures message: $it" } }, - { log.error { "Failed to forward message: $it" } }, - ) - } - - bot.setMessageReaction( - chatId = ChatId.fromId(message.chat.id), - messageId = message.messageId, - reaction = listOf(ReactionType.Emoji("👀")), - ) - .onError { error -> log.warn { "Failed to react to message: $error" } } - } - } - } - - Thread { bot.startPolling() }.start() - - log.info { "Initialized bot" } - } - - @PreDestroy - fun stop() { - bot.stopPolling() - log.info { "Stopped bot" } - } -} diff --git a/src/main/kotlin/com/pischule/memestv/bot/BotConfiguration.kt b/src/main/kotlin/com/pischule/memestv/bot/BotConfiguration.kt new file mode 100644 index 0000000..116e99e --- /dev/null +++ b/src/main/kotlin/com/pischule/memestv/bot/BotConfiguration.kt @@ -0,0 +1,47 @@ +package com.pischule.memestv.bot + +import com.github.kotlintelegrambot.Bot +import com.github.kotlintelegrambot.bot +import com.github.kotlintelegrambot.dispatch +import com.github.kotlintelegrambot.dispatcher.message +import com.github.kotlintelegrambot.dispatcher.photos +import com.pischule.memestv.bot.handler.PhotoHandlerService +import com.pischule.memestv.bot.handler.ThisCommandHandlerService +import io.github.oshai.kotlinlogging.KotlinLogging +import org.springframework.boot.context.properties.EnableConfigurationProperties +import org.springframework.context.annotation.Bean +import org.springframework.context.annotation.Configuration + +private val log = KotlinLogging.logger {} + +@EnableConfigurationProperties(BotProps::class) +@Configuration +class BotConfiguration( + private val botProps: BotProps, + private val thisCommandHandlerService: ThisCommandHandlerService, + private val photoHandlerService: PhotoHandlerService, +) { + + @Bean + fun telegramBot(): Bot { + return bot { + token = botProps.token + dispatch { + message { + try { + thisCommandHandlerService.create().invoke(this) + } catch (e: Error) { + log.error(e) { "Error while handling message" } + } + } + photos { + try { + photoHandlerService.create().invoke(this) + } catch (e: Error) { + log.error(e) { "Error while handling photo" } + } + } + } + } + } +} diff --git a/src/main/kotlin/com/pischule/memestv/BotProps.kt b/src/main/kotlin/com/pischule/memestv/bot/BotProps.kt similarity index 84% rename from src/main/kotlin/com/pischule/memestv/BotProps.kt rename to src/main/kotlin/com/pischule/memestv/bot/BotProps.kt index 61243de..bbdd11c 100644 --- a/src/main/kotlin/com/pischule/memestv/BotProps.kt +++ b/src/main/kotlin/com/pischule/memestv/bot/BotProps.kt @@ -1,4 +1,4 @@ -package com.pischule.memestv +package com.pischule.memestv.bot import org.springframework.boot.context.properties.ConfigurationProperties diff --git a/src/main/kotlin/com/pischule/memestv/bot/BotService.kt b/src/main/kotlin/com/pischule/memestv/bot/BotService.kt new file mode 100644 index 0000000..ad1aebf --- /dev/null +++ b/src/main/kotlin/com/pischule/memestv/bot/BotService.kt @@ -0,0 +1,25 @@ +package com.pischule.memestv.bot + +import com.github.kotlintelegrambot.Bot +import io.github.oshai.kotlinlogging.KotlinLogging +import jakarta.annotation.PostConstruct +import jakarta.annotation.PreDestroy +import org.springframework.stereotype.Service + +private val log = KotlinLogging.logger {} + +@Service +class BotService(private val bot: Bot) { + + @PostConstruct + fun start() { + Thread { bot.startPolling() }.start() + log.info { "Initialized bot" } + } + + @PreDestroy + fun stop() { + bot.stopPolling() + log.info { "Stopped bot" } + } +} diff --git a/src/main/kotlin/com/pischule/memestv/bot/handler/PhotoHandlerService.kt b/src/main/kotlin/com/pischule/memestv/bot/handler/PhotoHandlerService.kt new file mode 100644 index 0000000..2b6667e --- /dev/null +++ b/src/main/kotlin/com/pischule/memestv/bot/handler/PhotoHandlerService.kt @@ -0,0 +1,49 @@ +package com.pischule.memestv.bot.handler + +import com.github.kotlintelegrambot.dispatcher.handlers.HandlePhotos +import com.github.kotlintelegrambot.dispatcher.handlers.media.MediaHandlerEnvironment +import com.github.kotlintelegrambot.entities.ChatId +import com.github.kotlintelegrambot.entities.files.PhotoSize +import com.github.kotlintelegrambot.entities.reaction.ReactionType +import com.pischule.memestv.bot.BotProps +import io.github.oshai.kotlinlogging.KotlinLogging +import org.springframework.stereotype.Service + +private val log = KotlinLogging.logger {} + +@Service +class PhotoHandlerService(private val botProps: BotProps) { + + fun create(): HandlePhotos = { + if (shouldForwardMessage()) { + forwardPhotoMessage() + } + + reactToMessage("👀") + } + + private fun MediaHandlerEnvironment>.shouldForwardMessage(): Boolean { + return message.chat.id != botProps.destinationChatId + } + + private suspend fun MediaHandlerEnvironment>.forwardPhotoMessage() { + bot.forwardMessage( + chatId = ChatId.fromId(botProps.destinationChatId), + fromChatId = ChatId.fromId(message.chat.id), + messageId = message.messageId, + ) + .fold( + { log.info { "Forwarded picture message: $it" } }, + { log.error { "Failed to forward message: $it" } }, + ) + } + + private suspend fun MediaHandlerEnvironment>.reactToMessage(emoji: String) { + bot.setMessageReaction( + chatId = ChatId.fromId(message.chat.id), + messageId = message.messageId, + reaction = listOf(ReactionType.Emoji(emoji)), + ) + .onError { error -> log.warn { "Failed to react to message: $error" } } + } +} diff --git a/src/main/kotlin/com/pischule/memestv/bot/handler/ThisCommandHandlerService.kt b/src/main/kotlin/com/pischule/memestv/bot/handler/ThisCommandHandlerService.kt new file mode 100644 index 0000000..d64146f --- /dev/null +++ b/src/main/kotlin/com/pischule/memestv/bot/handler/ThisCommandHandlerService.kt @@ -0,0 +1,48 @@ +package com.pischule.memestv.bot.handler + +import com.github.kotlintelegrambot.dispatcher.handlers.HandleMessage +import com.github.kotlintelegrambot.dispatcher.handlers.MessageHandlerEnvironment +import com.github.kotlintelegrambot.entities.ChatId +import com.github.kotlintelegrambot.entities.reaction.ReactionType +import com.pischule.memestv.bot.BotProps +import com.pischule.memestv.s3.FileUploaderService +import io.github.oshai.kotlinlogging.KotlinLogging +import org.springframework.stereotype.Component + +private val log = KotlinLogging.logger {} + +@Component +class ThisCommandHandlerService( + private val botProps: BotProps, + private val fileUploaderService: FileUploaderService, +) { + fun create(): HandleMessage = HandleMessage@{ + if (!shouldHandleMessage()) return@HandleMessage + + val maxResPhotoId = message.replyToMessage!!.photo!!.last().fileId + val fileBytes = bot.downloadFileBytes(maxResPhotoId) ?: return@HandleMessage + + log.info { "Downloaded a file $maxResPhotoId from Telegram" } + + fileUploaderService.uploadFile(fileBytes) + log.info { "Uploaded a file $maxResPhotoId to S3" } + + reactToMessage("👍") + } + + private fun MessageHandlerEnvironment.shouldHandleMessage(): Boolean { + val isFromTargetChat = message.chat.id == botProps.destinationChatId + val isThisCommand = message.text?.lowercase() == "this" + val hasPhotoReply = message.replyToMessage?.photo?.isNotEmpty() == true + return isFromTargetChat && isThisCommand && hasPhotoReply + } + + private suspend fun MessageHandlerEnvironment.reactToMessage(emoji: String) { + bot.setMessageReaction( + chatId = ChatId.fromId(message.chat.id), + messageId = message.messageId, + reaction = listOf(ReactionType.Emoji(emoji)), + ) + .onError { error -> log.warn { "Failed to react to message: $error" } } + } +} diff --git a/src/main/kotlin/com/pischule/memestv/FileUploaderService.kt b/src/main/kotlin/com/pischule/memestv/s3/FileUploaderService.kt similarity index 94% rename from src/main/kotlin/com/pischule/memestv/FileUploaderService.kt rename to src/main/kotlin/com/pischule/memestv/s3/FileUploaderService.kt index 622fb3d..6768714 100644 --- a/src/main/kotlin/com/pischule/memestv/FileUploaderService.kt +++ b/src/main/kotlin/com/pischule/memestv/s3/FileUploaderService.kt @@ -1,4 +1,4 @@ -package com.pischule.memestv +package com.pischule.memestv.s3 import aws.sdk.kotlin.services.s3.S3Client import aws.sdk.kotlin.services.s3.model.PutObjectRequest diff --git a/src/main/kotlin/com/pischule/memestv/S3Config.kt b/src/main/kotlin/com/pischule/memestv/s3/S3Config.kt similarity index 96% rename from src/main/kotlin/com/pischule/memestv/S3Config.kt rename to src/main/kotlin/com/pischule/memestv/s3/S3Config.kt index 7f7c08e..161f051 100644 --- a/src/main/kotlin/com/pischule/memestv/S3Config.kt +++ b/src/main/kotlin/com/pischule/memestv/s3/S3Config.kt @@ -1,4 +1,4 @@ -package com.pischule.memestv +package com.pischule.memestv.s3 import aws.sdk.kotlin.runtime.auth.credentials.StaticCredentialsProvider import aws.sdk.kotlin.services.s3.S3Client diff --git a/src/main/kotlin/com/pischule/memestv/S3Props.kt b/src/main/kotlin/com/pischule/memestv/s3/S3Props.kt similarity index 86% rename from src/main/kotlin/com/pischule/memestv/S3Props.kt rename to src/main/kotlin/com/pischule/memestv/s3/S3Props.kt index 13344d8..f202d8b 100644 --- a/src/main/kotlin/com/pischule/memestv/S3Props.kt +++ b/src/main/kotlin/com/pischule/memestv/s3/S3Props.kt @@ -1,4 +1,4 @@ -package com.pischule.memestv +package com.pischule.memestv.s3 import org.springframework.boot.context.properties.ConfigurationProperties