I'm trying to implement a recognition (like authentication) method for each user who talks to my Telegram bot.
In the older version of python-telegram-bot, I was capable of accomplishing it really well with CustomContext.
It used to be something like this.
def get_user(update):
user_id = update.effective_user.id
try:
user_telegram = UserTelegram.objects.get(chat_id=user_id)
except UserTelegram.DoesNotExist:
first_name = update.effective_user.first_name
last_name = update.effective_user.last_name
user_telegram = UserTelegram.objects.create(chat_id=user_id, first_name=first_name, last_name=last_name)
if not user_telegram.active:
text="Not allowed."
update.message.reply_text(text=text)
return user_telegram
class CustomContext(CallbackContext[ExtBot, dict, dict, dict]):
@classmethod
def from_update(
cls,
update: object,
application: "Application",
) -> "CustomContext":
context = super().from_update(update, application)
context.user_telegram = get_user(update)
return context
My issue now is the async controls from python-telegram-bot 20.4. My get_user function is supposed to work in an async mode. But I can't find a way to make the request work.
I did the following:
from asgiref.sync import async_to_sync, sync_to_async
@sync_to_async
def get_user(update):
user_id = update.effective_user.id
try:
user_telegram = UserTelegram.objects.get(chat_id=user_id)
...
But this would need the "from_update" to be async.
@classmethod
async def from_update(
cls,
update: object,
application: "Application",
) -> "CustomContext":
context = super().from_update(update, application)
context.user_telegram = await get_user(update)
...
This gives me the following error:
File "/usr/local/lib/python3.10/site-packages/telegram/ext/_application.py", line 1156, in process_update
await context.refresh_data()
AttributeError: 'coroutine' object has no attribute 'refresh_data'
If I delete the async await from the "from_update" then the request is completed, but the DB query is not concluded - it returns the coroutine.
How can I manage my user status like this?
I'm aware we have decorators that could help me to make auth mandatory for some commands. But I was looking for something default that also delivers me the user object from the db that will be passed as a context to the commands.
I'm using Django==4.2.3 therefore I'm not using the running tasks. DB is Postgres. python-telegram-bot==20.4
By the way: A different approach is also welcome.
The context
object is created for each incoming update in the same process as checking which handlers should actually handle the update. If that process does requests to APIs (which can take some time, may fail, etc), that could significantly impact the way concurrency is handled and delay the overall processing of updates. Hence, from_update
is not designed to be a coroutine function.
Now, to run some I/O-based logic (e.g. requsets to the Bot API) on every update and make data available for all following handlers, I recommend using a TypeHandler
in a low group
. Please also have a look at this wiki page which describes this concept in detail.
Disclaimer: I'm currently the maintainer of python-telegram-bot
.