pengrad / java-telegram-bot-api

Telegram Bot API for Java
https://core.telegram.org/bots
Apache License 2.0
1.81k stars 375 forks source link

pls support local bot api server #332

Closed huangzulin closed 1 year ago

huangzulin commented 1 year ago

ref: https://core.telegram.org/bots/api#using-a-local-bot-api-server

pengrad commented 1 year ago

Please use Builder with custom api url TelegramBot bot = new TelegramBot.Builder("BOT_TOKEN").apiUrl("local-api-url").build();

laal28 commented 6 days ago

Hi! I'm trying to run bot with webhooks on local api server, but getting 401 Unauthorized. Here is code listing.


interface TelegramBotService {
    fun handleUpdate(update: Update?, token: String): Boolean
}

@ConfigurationProperties(prefix = "com.test.telegrambot", ignoreUnknownFields = false)
data class TelegramBotProperties(
    val token: String,
    val url: String?,
    val telegramApi: TelegramApiProperties = TelegramApiProperties(),
    val debug: Boolean = false,
) {
    data class TelegramApiProperties(
        val telegramApiUrl: String = "https://api.telegram.org/",
        val useTestServer: Boolean = false,
    )
}

@Configuration
@EnableConfigurationProperties(TelegramBotProperties::class)
class TelegramBotConfig(
    private val telegramBotProperties: TelegramBotProperties
) {
    private val log: Logger = LoggerFactory.getLogger(this.javaClass)

    @Bean
    @Order(0)
    fun telegramBot(): TelegramBot {
        return TelegramBot.Builder(telegramBotProperties.token)
            .apiUrl(telegramBotProperties.telegramApi.telegramApiUrl)
            .useTestServer(telegramBotProperties.telegramApi.useTestServer)
            .let {
                if (telegramBotProperties.debug)
                    return@let it.debug()
                else return@let it
            }
            .build()
    }

    @Bean
    @Order(10)
    fun deleteWebhook(telegramBot: TelegramBot): TelegramBot {
        val deleteWebhook: DeleteWebhook = DeleteWebhook()
        val deleteWebhookResponse: BaseResponse = telegramBot.execute(deleteWebhook)

        if (!deleteWebhookResponse.isOk)
            throw Error("Error during webhook deletion! ${deleteWebhookResponse.errorCode()} ${deleteWebhookResponse.description()}")

        return telegramBot
    }

    @Bean
    @Order(20)
    fun configureTelegramBotWebhook(telegramBot: TelegramBot): TelegramBot {
        if (telegramBotProperties.url != null) {
            deleteWebhook(telegramBot)

            log.debug("Configuring webhook for URL: ${telegramBotProperties.url}")

            val request = SetWebhook().url(telegramBotProperties.url + "/api/telegram/webhook")
            val response: BaseResponse = telegramBot.execute(request)

            if (!response.isOk)
                throw Error("Error during webhook configuration! ${response.errorCode()}\n${response.description()}")
        } else {
            log.warn("No webhook url was configured. Continuing with long polling. Use this only in development")
        }

        return telegramBot
    }
}

@Configuration
class SecurityConfig {
    @Bean
    fun configureTelegramWebhookEndpoint(http: HttpSecurity): SecurityFilterChain {
        return http
            .csrf { obj: CsrfConfigurer<HttpSecurity> -> obj.disable() }
            .cors { cors: CorsConfigurer<HttpSecurity?> ->
                cors.configurationSource { request: HttpServletRequest? ->
                    val corsConfiguration = CorsConfiguration()
                    corsConfiguration.setAllowedOriginPatterns(listOf("*"))
                    corsConfiguration.allowedMethods = listOf("POST")
                    corsConfiguration.allowedHeaders = listOf("*")
                    corsConfiguration.allowCredentials = true
                    corsConfiguration.maxAge = 1800L
                    corsConfiguration
                }
            }
            .securityMatcher("/api/telegram/webhook")
            .authorizeHttpRequests { it.anyRequest().permitAll() }
            .build()
    }
}

@Component
class TelegramBotServiceImpl(
    private val telegramBot: TelegramBot
) : TelegramBotService{
    private val log: Logger = LoggerFactory.getLogger(this.javaClass)

    override fun handleUpdate(update: Update?, token: String): Boolean {
        log.trace("Received new update: $update")
        log.debug("Token: $token")

        if (update!!.message() == null) {
            log.trace("No message in update. Skipping.")
            return false
        }
        val message: Message = update.message()
        if (message.entities().isEmpty()) {
            if (message.text().startsWith("/start")) {
                log.trace("This is a \"/start\" command. Sending response")
                telegramBot.execute(SendMessage(message.from().id(), "Hello!"))
            }
        }
        return true
    }
}

@RestController
@RequestMapping("/api")
class TelegramBotWebhookController(
    private val telegramBotService: TelegramBotService
) {
    private val log: Logger = LoggerFactory.getLogger(this.javaClass)

    @PostMapping("/telegram/webhook")
    fun telegramWebhook(@RequestHeader("X-Telegram-Bot-Api-Secret-Token") token: String, @RequestBody update: Update?): Boolean {
        log.trace("Received update with webhook: $update")
        return telegramBotService.handleUpdate(update, token)
    }
}
laal28 commented 6 days ago

Looks like, local server requires name to authorize.

DURATION    inf 5sec    1min    1hour
uptime  4864.064249
bot_count   2
active_bot_count    2
rss 38984KB
vm  47276KB
rss_peak    38984KB
vm_peak 47500KB
total_cpu   0.805375%   0.505051%   0.637172%   0.805375%
user_cpu    0.352065%   0.202020%   0.300553%   0.352065%
system_cpu  0.453310%   0.303030%   0.336619%   0.453310%
buffer_memory   79360B
active_webhook_connections  0
active_requests 0
active_network_queries  0
request_count   0.031038    0.000000    0.242055    0.031044
request_bytes   7.992706    0.000000    60.513746   7.994349
request_file_count  0.000000    0.000000    0.000000    0.000000
request_files_bytes 0.000000    0.000000    0.000000    0.000000
request_max_bytes   0   0   0   0
response_count  0.031038    0.000000    0.242055    0.031044
response_count_ok   0.000411    0.000000    0.000000    0.000411
response_count_error    0.030627    0.000000    0.242055    0.030633
response_bytes  1.795044    0.000000    14.039189   1.795413
update_count    0.001028    0.000000    0.000000    0.001028

id  6844726460
uptime  287.254129
token   ****************
username    <failed to authorize>
head_update_id  0
request_count/sec   0.447522    0.000000    0.242055    0.447522
update_count/sec    0.000000    0.000000    0.000000    0.000000

id  6844726460
uptime  2433.012044
token   ****************
username    test_bot
head_update_id  944369801
tail_update_id  944369806
pending_update_count    5
request_count/sec   0.000822    0.000000    0.000000    0.000822
update_count/sec    0.002054    0.000000    0.000000    0.002054
laal28 commented 6 days ago

I've found out, that "useTestServer" option adds "/test" to the end of base url, that's why i was getting 401. After removing this option - everything started working as intended