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)}")