Search code examples
pythontelegram-botpython-telegram-bot

Cannot access user_data when using job queue


I have a function that I need to be able to both execute by sending a command and execute automatically using a job queue. And in that function I need to have access to user_data to store data that is specific to each user. Now the problem is that when I try to execute it via job queue, user_data is None and thus unavailable. How can I fix that?

Here's how I'm currently doing this, simplified:

import datetime
from settings import TEST_TOKEN, BACKUP_USER
from telegram.ext import Updater, CommandHandler, CallbackContext
from pytz import timezone


def job_daily(context: CallbackContext):
    job(BACKUP_USER, context)


def job_command(update, context):
    job(update.message.chat_id, context)


def job(chat_id, context):
    print(context.user_data)


def main():
    updater = Updater(TEST_TOKEN, use_context=True)
    dispatcher = updater.dispatcher
    job_queue = updater.job_queue

    # To run it automatically
    tehran = timezone("Asia/Tehran")
    due = datetime.time(15, 3, tzinfo=tehran)
    job_queue.run_daily(job_daily, due)

    # To run it via command
    dispatcher.add_handler(CommandHandler("job", job_command))

    updater.start_polling()
    updater.idle()


if __name__ == "__main__":
    main()

Now when I send the command /job and thus executing job_command, the job function prints {} which means that I can access user_data. But when the job_daily function is executed, the job function prints None meaning that I don't have access to user_data. Same goes for chat_data.


Solution

  • In a callback function of python-telegram-bot, context.user_data and context.chat_data depend on the update. More precisely, PTB takes update.effective_user/chat.id and provides the corresponding user/chat_data. Jobs callbacks are not triggered by an update (but by a time based trigger), so there's no reasonable way to provide context.user_data.

    What you can do, when scheduling the job from within a handler callback, where user_data is available, is to pass it as context argument to the job:

    context.job_queue.run_*(..., context=context.user_data)
    

    Then within the job callback, you can retrieve it as user_data = context.job.context

    In your case, you schedule the job in main, which is not a handler callback and hence you don't have context.user_data (not even context). If you have a specific user id for which you'd like to pass the user_data, you can get that user_data as user_data = updater.dispatcher.user_data[user_id], which is the same object as context.user_data (for updates from this particular user).


    Disclaimer: I'm currently the maintainer of python-telegram-bot.