Files
Touchh/bot/handlers.py

436 lines
19 KiB
Python
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

from telegram import InlineKeyboardButton, InlineKeyboardMarkup, Update
from telegram.ext import ContextTypes
from users.models import User
from hotels.models import Hotel, UserHotel
from users.models import NotificationSettings
from asgiref.sync import sync_to_async
import smtplib
from hotels.models import PMSIntegrationLog
import requests
from email.mime.text import MIMEText
from django.core.mail import send_mail
from datetime import datetime
# --- Вспомогательные функции ---
async def get_user_from_chat_id(chat_id):
"""Получение пользователя из базы по chat_id."""
return await sync_to_async(User.objects.filter(chat_id=chat_id).first)()
async def get_hotel_by_id(hotel_id):
"""Получение отеля по ID."""
return await sync_to_async(Hotel.objects.filter(id=hotel_id).first)()
# --- Обработчики команд ---
async def start(update: Update, context: ContextTypes.DEFAULT_TYPE):
"""Обработчик команды /start с проверкой chat_id"""
user_id = update.message.from_user.id
print(f"Пользователь {user_id} вызвал команду /start")
user = await get_user_from_chat_id(user_id)
if user:
keyboard = [
[InlineKeyboardButton("📊 Статистика", callback_data="stats")],
[InlineKeyboardButton("🏨 Управление отелями", callback_data="manage_hotels")],
[InlineKeyboardButton("👤 Пользователи", callback_data="manage_users")],
[InlineKeyboardButton("⚙️ Настройки", callback_data="settings")],
]
reply_markup = InlineKeyboardMarkup(keyboard)
await update.message.reply_text("Выберите действие:", reply_markup=reply_markup)
else:
print(f"Пользователь {user_id} не зарегистрирован.")
await update.message.reply_text("Вы не зарегистрированы в системе. Обратитесь к администратору.")
async def manage_hotels(update: Update, context: ContextTypes.DEFAULT_TYPE):
"""Вывод списка отелей, связанных с пользователем"""
query = update.callback_query
await query.answer()
user_id = query.from_user.id
print(f"Пользователь {user_id} выбрал 'Управление отелями'")
user = await get_user_from_chat_id(user_id)
if not user:
print(f"Пользователь {user_id} не зарегистрирован.")
await query.edit_message_text("Вы не зарегистрированы в системе.")
return
user_hotels = await sync_to_async(list)(
UserHotel.objects.filter(user=user).select_related("hotel")
)
if not user_hotels:
print(f"У пользователя {user_id} нет связанных отелей.")
await query.edit_message_text("У вас нет связанных отелей.")
return
keyboard = [
[InlineKeyboardButton(f"🏨 {hotel.hotel.name}", callback_data=f"hotel_{hotel.hotel.id}")]
for hotel in user_hotels
]
reply_markup = InlineKeyboardMarkup(keyboard)
await query.edit_message_text("Выберите отель:", reply_markup=reply_markup)
# --- Обработчики кнопок ---
async def handle_button_click(update: Update, context: ContextTypes.DEFAULT_TYPE):
"""Обработчик всех нажатий кнопок"""
query = update.callback_query
print(f"Обработчик кнопок: Получен callback_data = {query.data}")
if query.data == "manage_hotels":
await manage_hotels(update, context)
elif query.data.startswith("hotel_"):
await hotel_actions(update, context)
elif query.data.startswith("delete_hotel_"):
await delete_hotel(update, context)
elif query.data.startswith("check_pms_"):
await check_pms(update, context)
elif query.data.startswith("setup_rooms_"):
await setup_rooms(update, context)
elif query.data == "settings":
await settings_menu(update, context)
elif query.data == "toggle_telegram":
await toggle_telegram(update, context)
elif query.data == "toggle_email":
await toggle_email(update, context)
elif query.data == "set_notification_time":
await set_notification_time(update, context)
elif query.data == "current_settings":
await show_current_settings(update, context)
else:
print(f"Неизвестный callback_data: {query.data}")
await query.edit_message_text("Команда не распознана.")
async def hotel_actions(update: Update, context: ContextTypes.DEFAULT_TYPE):
"""Обработчик действий для выбранного отеля"""
query = update.callback_query
await query.answer()
hotel_id = int(query.data.split("_")[1])
print(f"Пользователь {query.from_user.id} выбрал отель с ID {hotel_id}")
hotel = await get_hotel_by_id(hotel_id)
if not hotel:
print(f"Отель с ID {hotel_id} не найден.")
await query.edit_message_text("Отель не найден.")
return
keyboard = [
[InlineKeyboardButton("🗑️ Удалить отель", callback_data=f"delete_hotel_{hotel_id}")],
[InlineKeyboardButton("🔗 Проверить интеграцию с PMS", callback_data=f"check_pms_{hotel_id}")],
[InlineKeyboardButton("🛏️ Настроить номера", callback_data=f"setup_rooms_{hotel_id}")],
]
reply_markup = InlineKeyboardMarkup(keyboard)
await query.edit_message_text(f"Управление отелем: {hotel.name}", reply_markup=reply_markup)
async def delete_hotel(update: Update, context: ContextTypes.DEFAULT_TYPE):
"""Удаление отеля"""
query = update.callback_query
await query.answer()
hotel_id = int(query.data.split("_")[2])
print(f"Пользователь {query.from_user.id} выбрал удаление отеля с ID {hotel_id}")
hotel = await get_hotel_by_id(hotel_id)
if hotel:
hotel_name = hotel.name
await sync_to_async(hotel.delete)()
print(f"Отель {hotel_name} удалён.")
await query.edit_message_text(f"Отель {hotel_name} успешно удалён.")
else:
print(f"Отель с ID {hotel_id} не найден.")
await query.edit_message_text("Отель не найден.")
async def check_pms(update: Update, context: ContextTypes.DEFAULT_TYPE):
"""Проверить интеграцию с PMS"""
query = update.callback_query
await query.answer()
hotel_id = int(query.data.split("_")[2])
print(f"Пользователь {query.from_user.id} проверяет интеграцию PMS для отеля с ID {hotel_id}")
# Асинхронно получаем отель
hotel = await sync_to_async(Hotel.objects.select_related('api', 'pms').get)(id=hotel_id)
if not hotel:
print(f"Отель с ID {hotel_id} не найден.")
await query.edit_message_text("Отель не найден.")
return
# Асинхронно извлекаем связанные данные
api_name = hotel.api.name if hotel.api else "Не настроен"
pms_name = hotel.pms.name if hotel.pms else "Не указана"
# Формируем сообщение
status_message = (
f"Отель: {hotel.name}\n"
f"PMS система: {pms_name}\n"
f"API: {api_name}"
)
await query.edit_message_text(status_message)
async def setup_rooms(update: Update, context: ContextTypes.DEFAULT_TYPE):
"""Настроить номера отеля"""
query = update.callback_query
await query.answer()
hotel_id = int(query.data.split("_")[2])
print(f"Пользователь {query.from_user.id} настраивает номера для отеля с ID {hotel_id}")
hotel = await get_hotel_by_id(hotel_id)
if not hotel:
print(f"Отель с ID {hotel_id} не найден.")
await query.edit_message_text("Отель не найден.")
return
await query.edit_message_text(f"Настройка номеров для отеля: {hotel.name}")
async def settings_menu(update: Update, context: ContextTypes.DEFAULT_TYPE):
"""Меню настроек уведомлений."""
query = update.callback_query
await query.answer()
user_id = query.from_user.id
user = await get_user_from_chat_id(user_id)
if not user:
await query.edit_message_text("Вы не зарегистрированы.")
return
settings, _ = await sync_to_async(NotificationSettings.objects.get_or_create)(user=user)
telegram_status = "" if settings.telegram_enabled else ""
email_status = "" if settings.email_enabled else ""
keyboard = [
[InlineKeyboardButton(f"{telegram_status} Уведомления в Telegram", callback_data="toggle_telegram")],
[InlineKeyboardButton(f"{email_status} Уведомления по Email", callback_data="toggle_email")],
[InlineKeyboardButton("🕒 Настроить время уведомлений", callback_data="set_notification_time")],
[InlineKeyboardButton("📋 Показать текущие настройки", callback_data="current_settings")],
]
reply_markup = InlineKeyboardMarkup(keyboard)
await query.edit_message_text("Настройки уведомлений:", reply_markup=reply_markup)
async def toggle_telegram(update: Update, context: ContextTypes.DEFAULT_TYPE):
"""Переключение состояния Telegram-уведомлений."""
query = update.callback_query
await query.answer()
user_id = query.from_user.id
user = await get_user_from_chat_id(user_id)
if not user:
await query.edit_message_text("Вы не зарегистрированы.")
return
settings, _ = await sync_to_async(NotificationSettings.objects.get_or_create)(user=user)
settings.telegram_enabled = not settings.telegram_enabled
await sync_to_async(settings.save)()
print(f"Пользователь {user_id} переключил Telegram-уведомления: {settings.telegram_enabled}")
await settings_menu(update, context)
async def toggle_email(update: Update, context: ContextTypes.DEFAULT_TYPE):
"""Переключение состояния Email-уведомлений."""
query = update.callback_query
await query.answer()
user_id = query.from_user.id
user = await get_user_from_chat_id(user_id)
if not user:
await query.edit_message_text("Вы не зарегистрированы.")
return
settings, _ = await sync_to_async(NotificationSettings.objects.get_or_create)(user=user)
settings.email_enabled = not settings.email_enabled
await sync_to_async(settings.save)()
print(f"Пользователь {user_id} переключил Email-уведомления: {settings.email_enabled}")
await settings_menu(update, context)
async def set_notification_time(update: Update, context: ContextTypes.DEFAULT_TYPE):
"""Настройка времени уведомлений."""
query = update.callback_query
await query.answer()
user_id = query.from_user.id
user = await get_user_from_chat_id(user_id)
if not user:
await query.edit_message_text("Вы не зарегистрированы.")
return
await query.edit_message_text("Введите новое время для уведомлений в формате HH:MM (например, 08:30):")
context.user_data["set_time"] = True
print(f"Пользователь {user_id} настраивает время уведомлений.")
async def handle_notification_time(update: Update, context: ContextTypes.DEFAULT_TYPE):
"""Обработка ввода времени уведомлений."""
if context.user_data.get("set_time"):
user_id = update.message.from_user.id
new_time = update.message.text
try:
hour, minute = map(int, new_time.split(":"))
user = await get_user_from_chat_id(user_id)
if user:
settings, _ = await sync_to_async(NotificationSettings.objects.get_or_create)(user=user)
settings.notification_time = f"{hour:02}:{minute:02}"
await sync_to_async(settings.save)()
print(f"Пользователь {user_id} установил новое время уведомлений: {new_time}")
await update.message.reply_text(f"Время уведомлений обновлено на {new_time}.")
except ValueError:
print(f"Пользователь {user_id} ввёл некорректное время: {new_time}")
await update.message.reply_text("Неверный формат. Введите время в формате HH:MM.")
finally:
context.user_data["set_time"] = False
async def settings_menu(update: Update, context: ContextTypes.DEFAULT_TYPE):
"""Меню настроек уведомлений."""
query = update.callback_query
await query.answer()
user_id = query.from_user.id
user = await get_user_from_chat_id(user_id)
if not user:
await query.edit_message_text("Вы не зарегистрированы.")
return
settings, _ = await sync_to_async(NotificationSettings.objects.get_or_create)(user=user)
telegram_status = "" if settings.telegram_enabled else ""
email_status = "" if settings.email_enabled else ""
keyboard = [
[InlineKeyboardButton(f"{telegram_status} Уведомления в Telegram", callback_data="toggle_telegram")],
[InlineKeyboardButton(f"{email_status} Уведомления по Email", callback_data="toggle_email")],
[InlineKeyboardButton("🕒 Настроить время уведомлений", callback_data="set_notification_time")],
]
reply_markup = InlineKeyboardMarkup(keyboard)
await query.edit_message_text("Настройки уведомлений:", reply_markup=reply_markup)
async def send_telegram_notification(user, message):
"""Отправка уведомления через Telegram."""
# bot = Bot(token="ВАШ_ТОКЕН")
if user.chat_id:
try:
await bot.send_message(chat_id=user.chat_id, text=message)
print(f"Telegram-уведомление отправлено пользователю {user.chat_id}: {message}")
except Exception as e:
print(f"Ошибка отправки Telegram-уведомления пользователю {user.chat_id}: {e}")
def send_email_notification(user, message):
"""Отправка уведомления через Email."""
if user.email:
try:
send_mail(
subject="Уведомление от системы",
message=message,
from_email="noreply@yourdomain.com",
recipient_list=[user.email],
fail_silently=False,
)
print(f"Email-уведомление отправлено на {user.email}: {message}")
except Exception as e:
print(f"Ошибка отправки Email-уведомления пользователю {user.email}: {e}")
async def schedule_notifications():
"""Планировщик уведомлений."""
print("Запуск планировщика уведомлений...")
now = datetime.now().strftime("%H:%M")
users = await sync_to_async(list)(User.objects.all())
for user in users:
settings, _ = await sync_to_async(NotificationSettings.objects.get_or_create)(user=user)
if settings.notification_time == now:
message = "Это ваше уведомление от системы."
if settings.telegram_enabled:
await send_telegram_notification(user, message)
if settings.email_enabled:
send_email_notification(user, message)
async def show_current_settings(update: Update, context: ContextTypes.DEFAULT_TYPE):
"""Отображение текущих настроек уведомлений."""
query = update.callback_query
await query.answer()
user_id = query.from_user.id
user = await get_user_from_chat_id(user_id)
if not user:
await query.edit_message_text("Вы не зарегистрированы.")
return
settings, _ = await sync_to_async(NotificationSettings.objects.get_or_create)(user=user)
telegram_status = "✅ Включены" if settings.telegram_enabled else "❌ Выключены"
email_status = "✅ Включены" if settings.email_enabled else "❌ Выключены"
notification_time = settings.notification_time or "Не установлено"
message = (
f"📋 Ваши настройки уведомлений:\n"
f"🔔 Telegram: {telegram_status}\n"
f"📧 Email: {email_status}\n"
f"🕒 Время: {notification_time}"
)
await query.edit_message_text(message)
async def check_pms_integration(update: Update, context: ContextTypes.DEFAULT_TYPE):
query = update.callback_query
await query.answer()
hotel_id = int(query.data.split("_")[2])
hotel = await sync_to_async(Hotel.objects.get)(id=hotel_id)
pms_settings = hotel.pms.parser_settings # Настройки из связанной PMSConfiguration
try:
# Выполняем запрос к PMS
response = requests.post(
url=pms_settings["url"],
headers={
"Authorization": f"Bearer {hotel.api.api_key}",
"Content-Type": "application/json",
},
json={
"from": "2024-01-01T00:00:00Z",
"until": "2024-01-10T00:00:00Z",
"pagination": {"from": 0, "count": 10},
},
)
# Проверяем результат
if response.status_code == 200:
await sync_to_async(PMSIntegrationLog.objects.create)(
hotel=hotel,
status="success",
message="Интеграция успешно проверена.",
)
await query.edit_message_text(f"Интеграция с PMS для отеля '{hotel.name}' успешна.")
else:
await sync_to_async(PMSIntegrationLog.objects.create)(
hotel=hotel,
status="error",
message=f"Ошибка: {response.status_code}",
)
await query.edit_message_text(f"Ошибка интеграции с PMS для отеля '{hotel.name}': {response.status_code}")
except Exception as e:
await sync_to_async(PMSIntegrationLog.objects.create)(
hotel=hotel,
status="error",
message=str(e),
)
await query.edit_message_text(f"Произошла ошибка: {str(e)}")