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.
- 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.
- 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}")
)
]
]
),
)
- 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()
- 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.