DEHuckaKpyT / telegram-bot

Kotlin Telegram Bot Library for creating scalable and expandable applications with helpful features.
https://dehuckakpyt.github.io/telegram-bot/starter-topic.html
Apache License 2.0
24 stars 3 forks source link

Telegram Bot Error in Docker Container #4

Closed PureBasics closed 2 months ago

PureBasics commented 2 months ago

The Telegram Bot works perfectly in my Spring application locally. However, in a Docker container, I get the following error: [atcher-worker-1] i.g.d.t.r.LongPollingUpdateReceiver : Internal error. Receiving updates will be resumed after 5000 milliseconds.

I know this isn't directly related to this Wrapper itself, but I'm hoping someone might have a solution.

Does the Telegram bot require any specific port to be open?

I've been stuck on this problem for 2 days and don't know how to proceed.

Thank you!

DEHuckaKpyT commented 2 months ago

I've been running bots with docker compose.

telegram-bot.Dockerfile

## Clone service
FROM alpine/git:latest as clone

WORKDIR /clone
RUN git clone https://<token>@github.com/DEHuckaKpyT/some-telegram-bot.git

## Build service
FROM gradle:8.7-jdk17 AS build

WORKDIR /build/some-telegram-bot
COPY --from=clone /clone/some-telegram-bot /build/some-telegram-bot
RUN ["chmod", "+x", "gradlew"]
RUN gradle bootJar

## Run service
FROM openjdk:17-oracle

COPY --from=build /build/some-telegram-bot/build/libs/some-telegram-bot-0.0.1-SNAPSHOT.jar /app.jar

ENTRYPOINT ["java","-jar","/app.jar"]

docker-compose.yaml

version: "3"
services:

  database:
    image: postgres:16
    environment:
      POSTGRES_DB: some_telegram_bot
      POSTGRES_PASSWORD: 11
      POSTGRES_USER: postgres
    logging:
      driver: "none"
    restart: no
    volumes:
      - postgres-data:/var/lib/postgresql/data
    ports:
      - "9200:5432"

  telegram-bot:
    image: telegram-bot
    build:
      dockerfile: ./telegram-bot.Dockerfile
    command: >
      --DATASOURCE_URL=jdbc:postgresql://database:5432/some_telegram_bot 
      --TELEGRAM_BOT_TOKEN=<token> 
      --TELEGRAM_BOT_USERNAME=some_bot 
    depends_on:
      - database
    restart: no
    ports:
      - "9201:8080"

volumes:
  postgres-data:

docker compose build --no-cache && docker compose up -d

DEHuckaKpyT commented 2 months ago

@PureBasics, or can you write exception message? It should be in the log after Internal error. Receiving updates will be resumed after 5000 milliseconds.

PureBasics commented 2 months ago

@PureBasics, or can you write exception message? It should be in the log after Internal error. Receiving updates will be resumed after 5000 milliseconds.

Unfortunately, the log ends with this message:

`2024-07-13 12:44:49 2024-07-13T10:44:49.798Z  INFO 1 --- [           main] o.s.b.w.embedded.tomcat.TomcatWebServer  : Tomcat started on port 8080 (http) with context path ''
2024-07-13 12:44:49 2024-07-13T10:44:49.819Z  INFO 1 --- [           main] c.DriveFy.DriveFy.DriveFyApplicationKt   : Started DriveFyApplicationKt in 6.211 seconds (process running for 6.727)
2024-07-13 12:44:50 Response Code: 200
2024-07-13 12:44:50 2024-07-13T10:44:50.805Z  INFO 1 --- [atcher-worker-1] i.g.d.t.r.LongPollingUpdateReceiver      : Started update receiver
2024-07-13 12:44:50 2024-07-13T10:44:50.812Z  INFO 1 --- [           main] mBotInitializationConfig$$SpringCGLIB$$0 : Starting telegram-bot 'Test'..
2024-07-13 12:44:50 2024-07-13T10:44:50.812Z  INFO 1 --- [atcher-worker-2] i.g.d.t.r.LongPollingUpdateReceiver      : Started update receiver
2024-07-13 12:44:50 2024-07-13T10:44:50.812Z  INFO 1 --- [           main] mBotInitializationConfig$$SpringCGLIB$$0 : Telegram-bot 'Test' started.
2024-07-13 12:44:50 2024-07-13T10:44:50.857Z ERROR 1 --- [atcher-worker-1] i.g.d.t.r.LongPollingUpdateReceiver      : Internal error. Receiving updates will be resumed after 5000 milliseconds.
2024-07-13 12:44:50 
2024-07-13 12:44:50 kotlinx.coroutines.JobCancellationException: Parent job is Completed
2024-07-13 12:44:50`
PureBasics commented 2 months ago

My very simple Dockerfile:

FROM openjdk:17-jdk-alpine

WORKDIR /app

EXPOSE 8080

COPY build/libs/*.jar app.jar

ENTRYPOINT ["java", "-jar", "app.jar"]
DEHuckaKpyT commented 2 months ago

Hmm. It works for me with this Dockerfile.

Maybe more details are needed. Like spring version, bot modules, bot version, kotlin version

DEHuckaKpyT commented 2 months ago

2024-07-13 12:44:50 2024-07-13T10:44:50.805Z INFO 1 --- [atcher-worker-1] i.g.d.t.r.LongPollingUpdateReceiver : Started update receiver 2024-07-13 12:44:50 2024-07-13T10:44:50.812Z INFO 1 --- [ main] mBotInitializationConfig$$SpringCGLIB$$0 : Starting telegram-bot 'Test'.. 2024-07-13 12:44:50 2024-07-13T10:44:50.812Z INFO 1 --- [atcher-worker-2] i.g.d.t.r.LongPollingUpdateReceiver : Started update receiver 2024-07-13 12:44:50 2024-07-13T10:44:50.812Z INFO 1 --- [ main] mBotInitializationConfig$$SpringCGLIB$$0 : Telegram-bot 'Test' started.

I see a strange thing. For some reason you have LongPollingUpdateReceiver running twice (Started update receiver). This should not be..

PureBasics commented 2 months ago

build.gradle.kts:

import org.jetbrains.kotlin.gradle.tasks.KotlinCompile

plugins {
    id("org.springframework.boot") version "3.2.5"
    id("io.spring.dependency-management") version "1.1.4"
    kotlin("jvm") version "1.9.23"
    kotlin("plugin.spring") version "1.9.23"
    id("org.jetbrains.kotlin.plugin.jpa") version "2.0.0"
}

group = "com.DriveFy"
version = "0.0.1-SNAPSHOT"

java {
    sourceCompatibility = JavaVersion.VERSION_17
}

repositories {
    mavenCentral()
}

dependencies {
    implementation("org.springframework.boot:spring-boot-starter-security")
    implementation("org.springframework.boot:spring-boot-starter-web")
    implementation("org.springframework.boot:spring-boot-starter-validation:3.3.1")
    implementation("com.fasterxml.jackson.module:jackson-module-kotlin")
    implementation("org.springframework.boot:spring-boot-starter-data-jpa")
    implementation("org.springframework.hateoas:spring-hateoas:2.3.0")
    implementation("org.jetbrains.kotlin:kotlin-reflect")
    implementation("io.jsonwebtoken:jjwt-api:0.11.5")
    implementation("org.mariadb.jdbc:mariadb-java-client:2.2.0")
    implementation("mysql:mysql-connector-java:5.1.13")
    implementation("io.github.dehuckakpyt.telegrambot:telegram-bot-core:0.9.7")
    implementation("io.github.dehuckakpyt.telegrambot:telegram-bot-spring:0.9.7")
    implementation("com.opencsv:opencsv:3.7")
    runtimeOnly("io.jsonwebtoken:jjwt-impl:0.11.5")
    runtimeOnly("io.jsonwebtoken:jjwt-jackson:0.11.5")
    testImplementation("org.springframework.boot:spring-boot-starter-test")
    testImplementation("org.springframework.security:spring-security-test")
    testRuntimeOnly("org.junit.platform:junit-platform-launcher")
}

tasks.withType<KotlinCompile> {
    kotlinOptions {
        freeCompilerArgs += "-Xjsr305=strict"
        jvmTarget = "17"
    }
}

tasks.withType<Test> {
    useJUnitPlatform()
}

My Bot implementation:


@Service
class TelegramBotService(@Value("\${telegram-bot.token}") private val botToken: String, @Value("\${telegram-bot.username}") private val botUsername: String, @Lazy val companyService: CompanyService) {
    lateinit var bot: TelegramBot
    var started = false

    fun start() {
        val config = TelegramBotConfig().apply {
            token = botToken
            username = botUsername
            receiving {
                handling {
                    startCommand()
                    helpCommand()
                }
            }
        }

        val context = TelegramBotFactory.createTelegramBotContext(config)
        bot = context.telegramBot
        val updateReceiver = context.updateReceiver

        updateReceiver.start()
        readlnOrNull()
        updateReceiver.stop()
    }

    fun BotHandling.startCommand() {
        command("/start") {
            sendMessage("start command works")
        }
    }

    fun BotHandling.helpCommand() {
        command("/help") {
            sendMessage("help command works")
        }
    }

    fun sendMessage(chatId: String, message: String){
        runBlocking {
            bot.sendMessage(chatId, message)
        }
    }
}
PureBasics commented 2 months ago

2024-07-13 12:44:50 2024-07-13T10:44:50.805Z INFO 1 --- [atcher-worker-1] i.g.d.t.r.LongPollingUpdateReceiver : Started update receiver 2024-07-13 12:44:50 2024-07-13T10:44:50.812Z INFO 1 --- [ main] mBotInitializationConfig$$SpringCGLIB$$0 : Starting telegram-bot 'Test'.. 2024-07-13 12:44:50 2024-07-13T10:44:50.812Z INFO 1 --- [atcher-worker-2] i.g.d.t.r.LongPollingUpdateReceiver : Started update receiver 2024-07-13 12:44:50 2024-07-13T10:44:50.812Z INFO 1 --- [ main] mBotInitializationConfig$$SpringCGLIB$$0 : Telegram-bot 'Test' started.

I see a strange thing. For some reason you have LongPollingUpdateReceiver running twice (Started update receiver). This should not be..

Yes, I noticed that too. It only happens in the Dockerfile, not when I start the Spring app directly.

DEHuckaKpyT commented 2 months ago

fun start() {

this function invokes once? Maybe you are creating the same bot twice?

PureBasics commented 2 months ago

I only call it here. This should only run once at startup

@Component
class StartupRunner(
    val telegramBotService: TelegramBotService
) : ApplicationRunner {

    override fun run(args: ApplicationArguments) {
        telegramBotService.start()
    }
}
PureBasics commented 2 months ago

I've added a log statement to that:

@Component
class StartupRunner(
    val telegramBotService: TelegramBotService
) : ApplicationRunner {

    override fun run(args: ApplicationArguments) {
        println("START TELEGRAM BOT")
        telegramBotService.start()
    }
}

Only runs once

2024-07-13 13:36:17 START TELEGRAM BOT
2024-07-13 13:36:18 2024-07-13T11:36:18.735Z  INFO 1 --- [atcher-worker-2] i.g.d.t.r.LongPollingUpdateReceiver      : Started update receiver
2024-07-13 13:36:18 2024-07-13T11:36:18.742Z  INFO 1 --- [           main] mBotInitializationConfig$$SpringCGLIB$$0 : Starting telegram-bot 'Test'..
2024-07-13 13:36:18 2024-07-13T11:36:18.742Z  INFO 1 --- [atcher-worker-1] i.g.d.t.r.LongPollingUpdateReceiver      : Started update receiver
2024-07-13 13:36:18 2024-07-13T11:36:18.742Z  INFO 1 --- [           main] mBotInitializationConfig$$SpringCGLIB$$0 : Telegram-bot 'Test' started.
2024-07-13 13:36:18 2024-07-13T11:36:18.793Z ERROR 1 --- [atcher-worker-2] i.g.d.t.r.LongPollingUpdateReceiver      : Internal error. Receiving updates will be resumed after 5000 milliseconds.
2024-07-13 13:36:18 
2024-07-13 13:36:18 kotlinx.coroutines.JobCancellationException: Parent job is Completed
2024-07-13 13:36:18
DEHuckaKpyT commented 2 months ago

If it's no secret. And why don't you want to declare the bot via an annotation in the config? It will be started with application. https://dehuckakpyt.github.io/telegram-bot/get-started.html

@EnableTelegramBot
@Configuration
class BotConfig

And you can inject TelegramBot wherever you like bot: TelegramBot

Youc can configure it also like this:

@EnableTelegramBot
@Configuration
class BotConfig {

    @Bean
    fun telegramBotConfig(): TelegramBotConfig = TelegramBotConfig().apply {
        receiving {
            exceptionHandler = { WSExceptionHandler(telegramBot, receiving.messageTemplate, templating.templater) }
        }
    }
}

It still might not solve your problem, but it will definitely make the code cleaner.

DEHuckaKpyT commented 2 months ago

I think I know what the problem is. The console is not available in the docker. you can't write something like readlnOrNull().

You could try removing those two lines to check.

readlnOrNull()
updateReceiver.stop()
PureBasics commented 2 months ago

The issue was that the bot started twice. I had included the @EnableTelegramBot annotation and also called my own start method.

I realize now that I should have read the documentation more carefully.

Thank you so much for your help! Without it, I would've spent hours searching for the problem.

DEHuckaKpyT commented 2 months ago

I'll try to improve the description in the documentation a bit. I don't promise that it will be soon. But I want to)

DEHuckaKpyT commented 2 months ago

And one other observation. I suggest using a database to store states inside the bot. I saw in the dependencies that you use a database. You could just add a dependency for that io.github.dehuckakpyt.telegrambot:telegram-bot-source-jpa:0.9.7a

https://dehuckakpyt.github.io/telegram-bot/spring-jpa.html

*At least it should work just by adding the dependency. Otherwise dependency is useless)