How to know telegram web app opened from an inline button, was closed?
Asked Answered
F

2

6

I successfully open a web app using an in-line button from my Telegram bot. When the "work" is complete in the web app, it persists the data to a DB via an API call and then closes itself (the web app) using Telegram.WebApp.close(). Upon the closing of the Web app my bot then needs to automatically refresh some information shown in the private chat from where the web app was launched.

Question: How can my bot know that the web app has been closed? Or how can I send a "notification" from the web app to the bot to let it know that it should refresh the data.

Supposedly I should be using answerWebAppQuery but search as I may, I cannot find any examples of how to use this. The closest example I could find is "In our case, we need to send data via answerWebAppQuery, without any servers, using the bot API: https://api.telegram.org/${yourBotToken}/answerWebAppQuery, options. You will need query_id from initData to send data using this approach.". I tried it and it just gives a 404 error.

I would appreciate any help or pointers to where I can find the info I need.

Footrope answered 5/9, 2022 at 23:7 Comment(2)
i am looking for the same thing, some kind of onClose event for the web app so I can keep the user engaged with the registration flow.Rogue
Any update on this? Did you figure it out in the end?Antiscorbutic
B
0

The answerWebAppQuery is ment to "set the result of an interaction with a Web App", you won't be able to use that to detect closing the webApp.


Looking at the Telegram WebApp change-log, Bot API 6.2 implements the popupClose event:

August 12, 2022
Bot API 6.2
  • Added the field isClosingConfirmationEnabled and the methods enableClosingConfirmation, disableClosingConfirmation, showPopup, showAlert, showConfirm to the class WebApp.
  • Added the field is_premium to the class WebAppUser.
  • Added the event popupClosed.

The Events available for Web Apps shows the following:

popupClosed
Bot API 6.2+

Occurrs when the opened popup is closed. eventHandler receives an object with the single field button_id – the value of the field id of the pressed button. If no buttons were pressed, the field button_id will be null.

So you should be able to capture the close of the WebApp, and then trigger the bot to refresh the data

Brace answered 10/7, 2023 at 14:37 Comment(0)
S
0

Here's how I worked around it using a MySQL database, as such a basic thing seems impossible using only the Telegram bot API. My app already requires a database so this does not cause significant overhead.

  1. When the user starts the interaction, I log the hit in a table in the database which has a user_id and a hit_id property, where hit_id is set to automatically increment so that I can get it using cursor.lastrowid:
query = "INSERT INTO HITS (user_id) VALUES (%s)"
cursor.execute(query, (user_id))
conn.commit()
hit_id = (cursor.lastrowid) 

As far as race conditions are concerned, to the best of my knowledge cursor.lastrowid should be thread-safe and specific to the cursor used to execute the insert operation. So, even if another session adds a hit concurrently, cursor.lastrowid should return the correct ID for the respective thread or process.

  1. When I show the inline button, I (a) pass the hit ID to the web app and (b) save the message that it returns in a variable:
btn_message = await update.message.reply_text(
            "example text",
            reply_markup=InlineKeyboardMarkup(
                [
                    [
                        InlineKeyboardButton(
                            "example button",
                            web_app=WebAppInfo(url=f"https://mywebsite.com/app?hit_id={hit_id}")
                        )
                    ]
                ]
            ),
        )
  1. I save the chat id and message in the database:
query = "UPDATE HITS SET message_id = %s, chat_id = %s WHERE user_id = %s AND hit_id = %s"
cursor.execute(query, (btn_message.message_id, btn_message.chat_id, user_id, hit_id))
conn.commit()
  1. On the web app side, I have some JS code that gets executed just before the interaction finishes, then closes the web app; before closing the web app, it POSTs a request to the following route:
@app.route("/end_interaction", methods=["POST"])
async def end_interaction():
    hit_id = request.json["hit_id"]
    user_id = request.json["user_id"]
    query = (
        "SELECT chat_id, message_id FROM HITS WHERE user_id = %s AND hit_id = %s"
    )
    cursor.execute(query, (user_id, hit_id))
    chat_id, message_id = cursor.fetchone()
    bot = Bot(TELEGRAM_BOT_TOKEN)
    await bot.edit_message_text(chat_id=chat_id, message_id=message_id, text="Done")
    await bot.edit_message_reply_markup(
        chat_id=chat_id, message_id=message_id, reply_markup=None
    )

@0stone0 's answer is not entirely relevant as popupClosed is for extra pop-ups over the web app, and also it's on the web app side, so it wouldn't be possible to use it anyway as the web app is closed.

Sinusoid answered 12/7, 2023 at 9:0 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.