This commit is contained in:
2025-04-09 00:52:07 +03:00
parent d67ae7edb0
commit 774aeaa564
9 changed files with 173 additions and 102 deletions

View File

@@ -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" }
}
}

View File

@@ -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" }
}
}
}
}
}
}

View File

@@ -1,4 +1,4 @@
package com.pischule.memestv
package com.pischule.memestv.bot
import org.springframework.boot.context.properties.ConfigurationProperties

View File

@@ -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" }
}
}

View File

@@ -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<List<PhotoSize>>.shouldForwardMessage(): Boolean {
return message.chat.id != botProps.destinationChatId
}
private suspend fun MediaHandlerEnvironment<List<PhotoSize>>.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<List<PhotoSize>>.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" } }
}
}

View File

@@ -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" } }
}
}

View File

@@ -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

View File

@@ -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

View File

@@ -1,4 +1,4 @@
package com.pischule.memestv
package com.pischule.memestv.s3
import org.springframework.boot.context.properties.ConfigurationProperties