'Nodejs Telegram Bot: sendChatAction while waiting for network call

I am writing a Telegram bot using the Telegraf framework in Nodejs, and would like to display the 'bot is typing...' banner while the bot is performing a networking call.

The issue that I have is that my function isn't executed like how it is supposed to be, ie the 'typing' banner appears and disappears after 5 seconds (as per the docs), but the invoice never gets sent until at least 30 seconds later. Assessing the logs also shows that the console logs were executed after the function ends.

Here is my implementation:

module.exports = (bot) => bot.command('buy', (ctx) => {
    let catalogueId = //some regex 
    return Promise.all([
        ctx.replyWithChatAction('typing'),
        sendInvoice(catalogueId, ctx)
    ]) 
})

function sendInvoice(catalogueId, ctx) {
    console.log('1')
    return helper.getItem(catalogueId)
    .then((item) => {
        console.log('2')
        return createInvoice(item, ctx)
        .then((invoice) => {
            console.log('3')
            return ctx.replyWithInvoice(invoice)
        })
    })
}

The log statements as seen in my Firebase Cloud Functions are like so: enter image description here

As you can see, the console logs are printed after the function has ended, and are at almost 15 seconds apart. Other attempts:

//Attempt 2, same result as above
module.exports = (bot) => bot.command('buy', (ctx) => {
    let catalogueId = //some regex 
    return ctx.replyWithChatAction('typing')
    .then(() => sendInvoice(catalogueId, ctx))
})

//Attempt 3, log statements are printed before function ends, but the 'typing' only shows at the end. Need it to show when the networking calls starts
module.exports = (bot) => bot.command('buy', (ctx) => {
    let catalogueId = //some regex 
    return sendInvoice(catalogueId, ctx))
})

function sendInvoice(catalogueId, ctx) {
    console.log('1')
    return helper.getItem(catalogueId)
    .then((item) => {
        console.log('2')
        return createInvoice(item, ctx)
        .then((invoice) => {
            console.log('3')
            return ctx.replyWithChatAction('typing')
            .then(() => ctx.replyWithInvoice(invoice))
        })
    })
}

My bot is currently deployed on Firebase Cloud Function.



Solution 1:[1]

I had a similar problem with a bot running on AWS Lambda. My bot would send the typing chat action correctly to the chat, then all of a sudden the lambda function would quit and none of my other code would be processed.

I'm not an expert but I think that as soon as the handler function you provide to Firebase Cloud Function (or Lambda) returns something to the client that initiated your request, Firebase considers the request complete and shuts down your function invocation. The default webhook callback that Telegraf provides sends a response to the client when your bot replies with a chat action or a message. Consequently, if you export that default webhook reply as your handler (as I did) then your handler will return a response as soon as your bot replies with anything, including a chat action, and the rest of your code may not be processed.

I solved my problem by replacing this:

export const handler = makeHandler(bot.webhookCallback("/"))

with this:

export const handler = async (event, context, callback) => {
  const tmp = JSON.parse(event.body) // get data passed to us
  await bot.handleUpdate(tmp) // make Telegraf process that data
  return callback(null, {
    // return something for webhook, so it doesn't try to send same stuff again
    statusCode: 200,
    body: "",
  })
}

(copied from this excellent github comment)

As you can see, instead of returning whenever the bot's webhook callback returns something, I'm now waiting until the update is completely handled and only returning afterward. Thus I can reply with a chat action, wait for my code to do some processing, reply with a message, wrap things up, and only then will my function invocation stop processing.

Sources

This article follows the attribution requirements of Stack Overflow and is licensed under CC BY-SA 3.0.

Source: Stack Overflow

Solution Source
Solution 1