JungDev / django-telegrambot

Simple app for Telegram bot in Django
BSD 3-Clause "New" or "Revised" License
237 stars 88 forks source link

How to make bot conversations persistent #30

Closed alisher-matkurbanov closed 4 years ago

alisher-matkurbanov commented 5 years ago

I want to my bot save its conversation handler states to Pickle and then deserialize on start. Without Django-Telegrambot extension I solve this problem that way:

def main():
    dp = Dispatcher(bot, update_queue)

    load_data(dp, main_conv_handler)
    threading.Thread(target=save_data, args=(dp, main_conv_handler)).start()
    delete_webhook()
    start_webhook()
    add_handlers(dp)
    ....

But using Django-Telegrambot this way doesnt work. After every python manage.py ... operation console locks, I think because of Thread...

So what is the way to make bot persistent using this library?

alistvt commented 5 years ago

you can create a user model in your app and save your users data there.

alisher-matkurbanov commented 5 years ago

you can create a user model in your app and save your users data there.

Well I've created models for my data, no problems with this. But I dont have access to ConversationHandler states inside my app. Its up to python-telegram-bot library, dont it? Do you undestand what I want?

alistvt commented 5 years ago

actually I don't use the conversationhandler and I save my users states in database and handle them somewhere else...

alisher-matkurbanov commented 5 years ago

actually I don't use the conversationhandler and I save my users states in database and handle them somewhere else...

Oh, I got. But I find ConversationHandler very useful state machine. The way I described above works well. With Django and manage.py it still works but have some issues when using manage.py for migrations. It just locks the console 🤷🏻‍♂️ I'm new to Django and I dont know how to solve this lock issue 😢

lukruh commented 4 years ago

You could set the state of your model every time your ConversationHandler is returning a new State. Let me know if this is still an Issue than I'll give an example.

alisher-matkurbanov commented 4 years ago

@lukruh I still using bad solution. Could you give an example, please?

lukruh commented 4 years ago

Sorry missed the notification, but if its still relevant: If your bots code is roughly based on the conversation example and your goal is to safe the conversation state to your user model you could do it like this:

from telegram import ...
from app import UserModel  # import your model which has an IntegerField "state"

GENDER, PHOTO, LOCATION, BIO = range(4)

def start(update, context):
    reply_keyboard = [['Boy', 'Girl', 'Other']]

    update.message.reply_text(
       # ... skipped some text
        'Are you a boy or a girl?',
        reply_markup=ReplyKeyboardMarkup(reply_keyboard, one_time_keyboard=True))

    # conversation state will change to  "GENDER" at the end of this function, write it to model
    user = UserModel.objects.get(name='right user')
    user.state = GENDER  # set state in your model to "GENDER" (its an integer)
    user.save()
    return GENDER  # set conversation to GENDER

you can get the user based on the update (telegram id) and if you have more than one conversation between user and bot you should probably store the state with a conversation instead of a user (depends on what you want).

alisher-matkurbanov commented 4 years ago

@lukruh Oh, I got what you mean. I was looking for a more short way to do it (state management in one place, not in every callback), cause with this solution I need to fix all my callbacks to add state management. Also this way doesn't allow use ptb state machine persistence. But anyway thanks for answer.

sasha00123 commented 4 years ago

Hello, @alisher-matkurbanov ! I don't know if the question is still relevant for you, but first, you could try to consult this page.

As far as I understand adding persistence with this approach is not possible without modification of the library since arguments for Updater constructor call cannot be changed. But you can always fork it and implement the required functionality.

Also, as lukruh mentioned, you could do it using the Django database - either implementing a generic Persistence class with its models or storing everything besides the User model, but I don't see any consistent method to store it in User model due to the way a key for storage is generated in python-telegram-bot, because it will break per_chat/per_user/per_message logic.

sasha00123 commented 4 years ago

Maybe not the best option to do, but I have hardcoded PicklePersistence to my fork. Maybe one day I will make a custom Persistence class there, but not today.