ebeneditos / telegram.bot

Develop a Telegram Bot with R
https://ebeneditos.github.io/telegram.bot/
GNU General Public License v3.0
108 stars 24 forks source link

Async execution #34

Closed hmeleiro closed 7 months ago

hmeleiro commented 1 year ago

I'm wondering if there is a way to write a bot that could handle simultaneous user petitions like in the python version of this library. Or maybe implement from outside with coro package? Would this be possible?

I have a basic understanding of concurrency and async functions, so I would not know where to start, but I'm going to give it a try with coro package and if it is possible write some kind of minimal example.

hmeleiro commented 1 year ago

I was not able to implement coro::async() function to telegram.bot handlers. But I managed to implement the async execution using Telegram API directly. Here is a simple example code:

library(httr)
library(jsonlite)
library(coro)

APIKEY <- ""
getUpdates <- function(){
  url <- sprintf("https://api.telegram.org/bot%s/getupdates", APIKEY)
  res <- GET(url)
  if(res$status_code == 200) {
    body <- content(res)

    return(body$result)

  } else {
    return(NULL)
  }
}

sendMessage <- async(
  function(update) {
    chat_id <- update$message$chat$id
    msg <- as.numeric(update$message$text)

    url <- sprintf("https://api.telegram.org/bot%s/sendMessage?chat_id=%s&text=Sleeping_%s_seconds", APIKEY, chat_id, msg)
    GET(url)
    await(async_sleep(msg))

    url <- sprintf("https://api.telegram.org/bot%s/sendMessage?chat_id=%s&text=Done_%s", APIKEY, chat_id, msg)
    GET(url)
  }
)

# The bot waits for the seconds you tell it to wait and sends you a message back when it has stopped waiting.
# Send two Telegram messages to the bot, first a high number (like 5) and then a lower one (1).
# Then run this code below
updates <- getUpdates()
for (update in updates) {
  sendMessage(update)
}
RKonstantinR commented 1 year ago

I've been experimenting with the future package.

I have configured parallel processing of commands, but for some reason it only works once.

In my example, if you run a slow and fast function, the bot will process them in parallel. But when they are called again, the updates do not reach the bot.

How can this be fixed?

library(telegram.bot)
library(promises)
library(future)

future::plan(multisession, workers = 2)

bot_token <- ""

updater <- Updater(token = bot_token )

start <- function(bot, update) {

    RKM <- ReplyKeyboardMarkup(
        keyboard = list(
            list(
                KeyboardButton("Button"))
        ),
        resize_keyboard = TRUE,
        one_time_keyboard = FALSE
    )

    bot$sendMessage(update$message$chat_id,
                    text = "Hello",
                    reply_markup = RKM)
    }

start_hendler <- CommandHandler('start', start)

updater$dispatcher$add_handler(start_hendler)

MessageFilters$button <- BaseFilter(function(message) {
    message$text == "Button"
})

ikm_button <- function(bot, update) {

    IKM <- InlineKeyboardMarkup(
        inline_keyboard = list(
            list(
                InlineKeyboardButton(text = 'Slow', callback_data = 'slow')),
            list(
                InlineKeyboardButton(text = 'Fast', callback_data = 'fast'))
        )
    )

    bot$sendMessage(chat_id = update$message$chat_id, 
                    text = "Select function", 
                    reply_markup = IKM)
}

ikm_button_hendler <- MessageHandler(ikm_button, filters = MessageFilters$button)

updater$dispatcher$add_handler(ikm_button_hendler)

flow_fun <- function(bot, update) { 

    promises::future_promise({

        bot$answerCallbackQuery(callback_query_id = update$callback_query$id)

        bot$sendMessage(chat_id = update$from_chat_id(),
                        text = "start slow calc...")
        Sys.sleep(5)

        bot$sendMessage(chat_id = update$from_chat_id(),
                        text = "slow calc complete...")
    })

}

flow_fun_handler <- CallbackQueryHandler(flow_fun, pattern = "slow")

updater$dispatcher$add_handler(flow_fun_handler)

fast_fun <- function(bot, update) { 

    promises::future_promise({

        bot$answerCallbackQuery(callback_query_id = update$callback_query$id)

        bot$sendMessage(chat_id = update$from_chat_id(),
                        text = "start fast calc....")

        Sys.sleep(1)

        bot$sendMessage(chat_id = update$from_chat_id(),
                        text = "fast calc complete...")
    })
}

fast_fun_handler <- CallbackQueryHandler(fast_fun, pattern = "fast")

updater$dispatcher$add_handler(fast_fun_handler)

updater$start_polling(verbose = TRUE, clean = TRUE)
ebeneditos commented 7 months ago

Hi, thanks for the comment, right now this fuctionality is not developed in-package.