Чат-бот Telegram с помощью python-telegram-bot | Сообщения между клиентами и операторами испорчены
У нас есть 3 оператора, и я хочу, чтобы они были заняты, если они вошли в чат с кем-то. Когда чат с клиентом закончится, оператор будет свободен для других клиентов и сможет общаться. Я случайно удалил часть кода, где оператор после входа в чат становится занятым (is_available=False), а после завершения чата становится доступным (is_available=True).
Стек: Django, python-telegram-bot
models.py:
from django.db import models
class CustomUser(models.Model):
tg_id = models.IntegerField()
tg_first_name = models.CharField(max_length=500, blank=True, null=True)
tg_username = models.CharField(max_length=500, blank=True, null=True)
name = models.CharField(max_length=500, blank=True, null=True)
choosen_lang = models.CharField(max_length=50, blank=True, null=True)
phone_number = models.CharField(max_length=50, blank=True, null=True)
status = models.CharField(max_length=500, blank=True, null=True)
is_operator = models.BooleanField(default=False, blank=True, null=True)
is_supervisor = models.BooleanField(default=False, blank=True, null=True)
is_available = models.BooleanField(default=True, blank=True, null=True)
def __str__(self):
return self.tg_first_name
class Chat(models.Model):
is_closed = models.BooleanField(default=False)
client = models.ForeignKey(CustomUser, on_delete=models.CASCADE, related_name='client_of_chat', blank=True, null=True)
operator = models.ForeignKey(CustomUser, on_delete=models.CASCADE, related_name='operator_of_chat', blank=True, null=True)
created = models.DateTimeField(auto_now_add=True)
class Appeal(models.Model):
custom_user = models.ForeignKey(CustomUser, on_delete=models.CASCADE, blank=True, null=True)
body = models.TextField(blank=True, null=True)
def __str__(self):
return self.body
buttons.py
from main.models import *
lang_btn = [
["O'zbek"],
["Русский"],
]
main_page_btn_uz = [
["Savol"],
["Shikoyat"],
]
main_page_btn_ru = [
["Вопрос"],
["Жалоба"],
]
chat_btn_uz = [
['Onlayn opertor'],
]
chat_btn_ru = [
['Онлайн-оператор'],
]
main_btn_operator = [
["Открытие чаты"],
["Закрытие чаты"],
]
close_chat_btn_ru = [
['✅Завершить чат'],
]
close_chat_btn_uz = [
['✅Chatni yopish'],
]
bot.py
Я тестировал с одним оператором и одним клиентом все работает нормально, но когда я добавляю еще одного оператора и больше клиентов сообщения между ними путаются... Что я делаю не так, пожалуйста, помогите мне?!)
Это действительно сводится к глобальным переменным и полному отсутствию безопасности параллелизма.
В PTB есть встроенное решение для хранения подобных переменных. Я бы, вероятно, использовал bot_data
для хранения в этом случае, но вам следует прочесть всю статью и самостоятельно выбрать лучший вариант.
Чтобы управлять доступностью операторов и обеспечить правильную маршрутизацию сообщений между клиентами и операторами, необходимо правильно обрабатывать флаг is_available
. Также необходимо убедиться, что каждый чат правильно сопоставлен с соответствующим оператором и клиентом.
Я внес несколько изменений в код ur 2 для управления статусом is_available операторов:
- Обновите функцию
move_to_get_reply
, чтобы помечать оператора как занятого, когда он начинает чат .
async def move_to_get_reply(update: Update, context: ContextTypes.DEFAULT_TYPE) -> int:
print('move_to_get_reply()')
global ch_id
query = update.callback_query
await query.answer()
if ch_id is not None:
created_chat = Chat.objects.get(id=ch_id)
operator = CustomUser.objects.get(tg_id=query.from_user.id)
created_chat.operator = operator
created_chat.save()
# Mark operator as busy
operator.is_available = False
operator.save()
await context.bot.send_message(created_chat.operator.tg_id, 'Чат открыт, напишите ...', reply_markup=markup_close_chat_ru)
if created_chat.client.choosen_lang == "O'zbek":
await context.bot.send_message(created_chat.client.tg_id, 'Assalomu alaykum, nima yordam bera olaman?', reply_markup=markup_close_chat_uz)
else:
await context.bot.send_message(created_chat.client.tg_id, 'Здравствуйте, чем могу помочь?', reply_markup=markup_close_chat_ru)
return PHASE_OPERATOR_CHAT
else:
await context.bot.send_message(created_chat.operator.tg_id, 'Клиент уже завершил чат.', reply_markup=markup_main_operator)
return PHASE_MAIN_OPERATOR
- Обновление функции
done_operator
оператора 2 mark как доступной, когда чат заканчивается
async def done_operator(update: Update, context: ContextTypes.DEFAULT_TYPE) -> int:
print('done_operator()')
global ch_id
await update.message.reply_text("Вы завершили чат.", reply_markup=markup_main_operator)
try:
chat = Chat.objects.get(id=ch_id)
except Chat.DoesNotExist:
print('Chat not found')
return PHASE_MAIN_OPERATOR
chat.is_closed = True
chat.save()
client_tg_id = chat.client.tg_id
await context.bot.send_message(client_tg_id, 'Оператор завершил чат', reply_markup=markup_main_ru)
# Mark operator as available
operator = chat.operator
operator.is_available = True
operator.save()
# Reset the ch_id after chat is done
ch_id = None
return PHASE_MAIN_OPERATOR
- Обновление функции
done_client
аналогичным образом для обработки завершения чата клиентом
async def done_client(update: Update, context: ContextTypes.DEFAULT_TYPE) -> int:
print('done_client()')
global ch_id
await update.message.reply_text("Вы завершили чат.", reply_markup=markup_main_ru)
try:
chat = Chat.objects.get(id=ch_id)
except Chat.DoesNotExist:
await update.message.reply_text("Chat not found.")
print('Chat not found')
return PHASE_MAIN_PAGE_RU
chat.is_closed = True
chat.save()
if chat.operator:
await context.bot.send_message(chat.operator.tg_id, 'Клиент завершил чат', reply_markup=markup_main_operator)
# Mark operator as available
operator = chat.operator
operator.is_available = True
operator.save()
# Reset the ch_id after chat is done
ch_id = None
return PHASE_MAIN_PAGE_RU
- Убедитесь, что функция
lets_chat
назначает чату доступного оператора и правильно обрабатывает несколько операторов
async def lets_chat(update: Update, context: ContextTypes.DEFAULT_TYPE) -> int:
print('lets_chat()')
global ch_id
created_chat = Chat.objects.create(
is_closed=False,
client=CustomUser.objects.get(tg_id=update.message.from_user.id),
)
ch_id = created_chat.id
keyboard = [
[
InlineKeyboardButton("Присоединиться к чату", callback_data=ch_id),
],
]
reply_markup = InlineKeyboardMarkup(keyboard)
available_operator = CustomUser.objects.filter(is_operator=True, is_available=True).first()
notification_message = (
f"<u>Появился новый чат!</u>\n"
f'\n'
f'<b>Имя пользователя:</b> <i>{update.message.from_user.first_name}</i>'
f'\n'
f'<b>Обрашения:</b> <i>{update.message.text}</i>'
)
if available_operator:
await context.bot.send_message(available_operator.tg_id, notification_message, reply_markup=reply_markup, parse_mode='HTML')
if created_chat.client.choosen_lang == "O'zbek":
await update.message.reply_text("Iltimos, kutib turing! Biz sizni mavjud operator bilan bog'layabmiz.", reply_markup=markup_close_chat_uz)
else:
await update.message.reply_text("Пожалуйста, ожидайте! Мы соединяем Вас со свободным оператором.", reply_markup=markup_close_chat_ru)
return PHASE_CLIENT_CHAT
Я надеюсь, что эти изменения обеспечат, чтобы оператор отмечался как занятой (is_available=False
), когда он начинает чат, и как доступный (is_available=True
), когда чат заканчивается. Это должно решить проблему путаницы сообщений между разными клиентами и операторами.