bot refactor

This commit is contained in:
2024-12-07 17:41:27 +09:00
parent 72cb7a4ef7
commit 626f378303
25 changed files with 1065 additions and 657 deletions

View File

107
bot/operations/hotels.py Normal file
View File

@@ -0,0 +1,107 @@
from telegram import InlineKeyboardButton, InlineKeyboardMarkup, Update
from asgiref.sync import sync_to_async
from hotels.models import Hotel, UserHotel
from users.models import User
async def manage_hotels(update: Update, context):
"""Отображение списка отелей, связанных с пользователем."""
query = update.callback_query
await query.answer()
user_id = query.from_user.id
user = await sync_to_async(User.objects.filter(chat_id=user_id).first)()
if not user:
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:
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 hotel_actions(update: Update, context):
"""Обработчик действий для выбранного отеля."""
query = update.callback_query
await query.answer()
hotel_id = int(query.data.split("_")[1])
hotel = await sync_to_async(Hotel.objects.filter(id=hotel_id).first)()
if not hotel:
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}")],
[InlineKeyboardButton("🏠 Главная", callback_data="main_menu")],
[InlineKeyboardButton("🔙 Назад", callback_data="back")],
]
reply_markup = InlineKeyboardMarkup(keyboard)
await query.edit_message_text(f"Управление отелем: {hotel.name}", reply_markup=reply_markup)
async def delete_hotel(update: Update, context):
"""Удаление отеля."""
query = update.callback_query
await query.answer()
hotel_id = int(query.data.split("_")[2])
hotel = await sync_to_async(Hotel.objects.filter(id=hotel_id).first)()
if hotel:
hotel_name = hotel.name
await sync_to_async(hotel.delete)()
await query.edit_message_text(f"Отель {hotel_name} успешно удалён.")
else:
await query.edit_message_text("Отель не найден.")
async def check_pms(update: Update, context):
"""Проверить интеграцию с PMS."""
query = update.callback_query
await query.answer()
hotel_id = int(query.data.split("_")[2])
hotel = await sync_to_async(Hotel.objects.select_related('api', 'pms').get)(id=hotel_id)
if not hotel:
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}\nPMS система: {pms_name}\nAPI: {api_name}"
await query.edit_message_text(status_message)
async def setup_rooms(update: Update, context):
"""Настроить номера отеля."""
query = update.callback_query
await query.answer()
hotel_id = int(query.data.split("_")[2])
hotel = await sync_to_async(Hotel.objects.filter(id=hotel_id).first)()
if not hotel:
await query.edit_message_text("Отель не найден.")
return
await query.edit_message_text(f"Настройка номеров для отеля: {hotel.name}")
async def get_users_for_hotel(hotel_id):
"""Получение пользователей, зарегистрированных в отеле с правами управления через бота."""
users = await sync_to_async(list)(
User.objects.filter(user_hotels__hotel_id=hotel_id, user_hotels__role__in=["admin", "manager"]).distinct()
)
return users

View File

@@ -0,0 +1,74 @@
from telegram import Bot
from django.core.mail import send_mail
from datetime import datetime
from asgiref.sync import sync_to_async
from users.models import User, NotificationSettings
async def send_telegram_notification(user, message):
"""Отправка уведомления через Telegram."""
if user.chat_id:
try:
bot = Bot(token="ВАШ_ТОКЕН")
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 handle_notification_time(update, context):
"""Обработка ввода времени уведомлений."""
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 sync_to_async(User.objects.filter(chat_id=user_id).first)()
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

108
bot/operations/settings.py Normal file
View File

@@ -0,0 +1,108 @@
from telegram import InlineKeyboardButton, InlineKeyboardMarkup, Update
from telegram.ext import ContextTypes
from asgiref.sync import sync_to_async
from users.models import User, NotificationSettings
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 sync_to_async(User.objects.filter(chat_id=user_id).first)()
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")],
[InlineKeyboardButton("🏠 Главная", callback_data="main_menu")],
[InlineKeyboardButton("🔙 Назад", callback_data="back")],
]
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 sync_to_async(User.objects.filter(chat_id=user_id).first)()
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 sync_to_async(User.objects.filter(chat_id=user_id).first)()
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 show_current_settings(update: Update, context: ContextTypes.DEFAULT_TYPE):
"""Отображение текущих настроек уведомлений."""
query = update.callback_query
await query.answer()
user_id = query.from_user.id
user = await sync_to_async(User.objects.filter(chat_id=user_id).first)()
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 set_notification_time(update: Update, context: ContextTypes.DEFAULT_TYPE):
"""Настройка времени уведомлений."""
query = update.callback_query
await query.answer()
user_id = query.from_user.id
user = await sync_to_async(User.objects.filter(chat_id=user_id).first)()
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} настраивает время уведомлений.")

View File

@@ -0,0 +1,81 @@
from datetime import datetime, timedelta
from telegram import InlineKeyboardButton, InlineKeyboardMarkup, Update
from telegram.ext import ContextTypes
from asgiref.sync import sync_to_async
from hotels.models import Reservation, Hotel
from users.models import User
from bot.utils.pdf_report import generate_pdf_report
from bot.utils.database import get_hotels_for_user
async def statistics(update: Update, context: ContextTypes.DEFAULT_TYPE):
"""Вывод списка отелей для статистики."""
query = update.callback_query
user_id = query.from_user.id
await query.answer()
# Получаем пользователя
user = await sync_to_async(User.objects.filter(chat_id=user_id).first)()
if not user:
await query.edit_message_text("Вы не зарегистрированы в системе.")
return
# Получаем отели, связанные с пользователем
hotels = await get_hotels_for_user(user)
if not hotels:
await query.edit_message_text("У вас нет доступных отелей для статистики.")
return
# Формируем кнопки для выбора отеля
keyboard = [[InlineKeyboardButton(hotel.name, callback_data=f"stats_hotel_{hotel.id}")] for hotel in hotels]
reply_markup = InlineKeyboardMarkup(keyboard)
await query.edit_message_text("Выберите отель:", reply_markup=reply_markup)
async def stats_select_period(update: Update, context):
"""Выбор периода времени для статистики."""
query = update.callback_query
await query.answer()
hotel_id = int(query.data.split("_")[2])
context.user_data["selected_hotel"] = hotel_id
keyboard = [
[InlineKeyboardButton("Неделя", callback_data="stats_period_week")],
[InlineKeyboardButton("Месяц", callback_data="stats_period_month")],
[InlineKeyboardButton("Все время", callback_data="stats_period_all")],
[InlineKeyboardButton("🏠 Главная", callback_data="main_menu")],
[InlineKeyboardButton("🔙 Назад", callback_data="back")],
]
reply_markup = InlineKeyboardMarkup(keyboard)
await query.edit_message_text("Выберите период времени:", reply_markup=reply_markup)
async def generate_statistics(update: Update, context):
"""Генерация и отправка статистики."""
query = update.callback_query
await query.answer()
hotel_id = context.user_data["selected_hotel"]
period = query.data.split("_")[2]
now = datetime.now()
if period == "week":
start_date = now - timedelta(days=7)
end_date = now
elif period == "month":
start_date = now - timedelta(days=30)
end_date = now
else:
start_date = None
end_date = None
reservations = await sync_to_async(list)(
Reservation.objects.filter(hotel_id=hotel_id).prefetch_related('guests')
)
hotel = await sync_to_async(Hotel.objects.get)(id=hotel_id)
file_path = generate_pdf_report(hotel.name, reservations, start_date, end_date)
with open(file_path, "rb") as file:
await query.message.reply_document(document=file, filename=file_path)

163
bot/operations/users.py Normal file
View File

@@ -0,0 +1,163 @@
from asgiref.sync import sync_to_async
from telegram import Update
from telegram.ext import ContextTypes
from telegram.ext import CallbackContext
from telegram import InlineKeyboardButton, InlineKeyboardMarkup
from telegram import KeyboardButton, ReplyKeyboardMarkup
from hotels.models import Hotel, UserHotel
from users.models import User
async def edit_user(update: Update, context: ContextTypes.DEFAULT_TYPE):
"""Изменение имени пользователя."""
query = update.callback_query
await query.answer()
user_id = int(query.data.split("_")[2])
context.user_data["edit_user_id"] = user_id
await query.edit_message_text("Введите новое имя пользователя:")
async def delete_user(update: Update, context: ContextTypes.DEFAULT_TYPE):
"""Удаление пользователя."""
query = update.callback_query
await query.answer()
user_id = int(query.data.split("_")[2])
user = await sync_to_async(User.objects.get)(id=user_id)
await sync_to_async(user.delete)()
await query.edit_message_text("Пользователь успешно удален.")
async def get_users_for_hotel(hotel_id):
"""
Получение пользователей, зарегистрированных в отеле с правами управления через бота.
"""
users = await sync_to_async(list)(
User.objects.filter(user_hotels__hotel_id=hotel_id, user_hotels__role__in=["admin", "manager"]).distinct()
)
return users
async def show_users(update: Update, context: ContextTypes.DEFAULT_TYPE):
"""Показать пользователей, зарегистрированных в отеле."""
query = update.callback_query
await query.answer()
# Если callback_data не содержит ID отеля, отображаем список отелей
if not query.data.startswith("manage_users_hotel_"):
user_id = query.from_user.id
hotels = await get_hotels_for_user(user_id)
if not hotels:
await query.edit_message_text("У вас нет доступных отелей.")
return
keyboard = [
[InlineKeyboardButton(hotel.name, callback_data=f"manage_users_hotel_{hotel.id}")]
for hotel in hotels
]
keyboard.append([InlineKeyboardButton("🏠 Главная", callback_data="main_menu")])
reply_markup = InlineKeyboardMarkup(keyboard)
await query.edit_message_text("Выберите отель:", reply_markup=reply_markup)
return
# Обработка пользователей отеля
hotel_id = int(query.data.split("_")[-1])
users = await sync_to_async(list)(
User.objects.filter(user_hotel__hotel_id=hotel_id)
)
if not users:
await query.edit_message_text("В этом отеле нет пользователей.")
return
keyboard = [
[InlineKeyboardButton(f"{user.username}", callback_data=f"edit_user_{user.id}")]
for user in users
]
keyboard.append([
InlineKeyboardButton("🏠 Главная", callback_data="main_menu"),
InlineKeyboardButton("🔙 Назад", callback_data="manage_users"),
[InlineKeyboardButton("🏠 Главная", callback_data="main_menu")],
[InlineKeyboardButton("🔙 Назад", callback_data="back")],
])
reply_markup = InlineKeyboardMarkup(keyboard)
await query.edit_message_text("Выберите пользователя:", reply_markup=reply_markup)
async def get_hotels_for_user(user):
"""Получение отелей, связанных с пользователем."""
return await sync_to_async(list)(
Hotel.objects.filter(hotel_users__user=user).distinct()
)
async def show_user_hotels(update: Update, context: ContextTypes.DEFAULT_TYPE):
"""Показ списка отелей пользователя."""
query = update.callback_query
await query.answer()
user_id = query.from_user.id
user_hotels = await get_hotels_for_user(user_id)
if not user_hotels:
await query.edit_message_text("У вас нет связанных отелей.")
return
keyboard = [
[InlineKeyboardButton(hotel.name, callback_data=f"users_hotel_{hotel.id}")]
for hotel in user_hotels
]
keyboard.append([InlineKeyboardButton("🔙 Назад", callback_data="main_menu")])
reply_markup = InlineKeyboardMarkup(keyboard)
await query.edit_message_text("Выберите отель:", reply_markup=reply_markup)
async def show_users_in_hotel(update: Update, context: ContextTypes.DEFAULT_TYPE):
"""Показ пользователей в выбранном отеле."""
query = update.callback_query
await query.answer()
hotel_id = int(query.data.split("_")[2])
users = await get_users_for_hotel(hotel_id)
if not users:
await query.edit_message_text("В этом отеле нет пользователей.")
return
keyboard = [
[InlineKeyboardButton(f"{user.first_name} {user.last_name}", callback_data=f"user_action_{user.id}")]
for user in users
]
keyboard.append([
InlineKeyboardButton("🏠 Главная", callback_data="main_menu"),
InlineKeyboardButton("🔙 Назад", callback_data="manage_users"),
])
reply_markup = InlineKeyboardMarkup(keyboard)
await query.edit_message_text("Пользователи отеля:", reply_markup=reply_markup)
async def user_action_menu(update: Update, context: ContextTypes.DEFAULT_TYPE):
"""Меню действий для пользователя."""
query = update.callback_query
await query.answer()
user_id = int(query.data.split("_")[2])
user = await sync_to_async(User.objects.get)(id=user_id)
keyboard = [
[InlineKeyboardButton("✏️ Изменить имя", callback_data=f"edit_user_{user_id}")],
[InlineKeyboardButton("🗑️ Удалить", callback_data=f"delete_user_{user_id}")],
[
InlineKeyboardButton("🏠 Главная", callback_data="main_menu"),
InlineKeyboardButton("🔙 Назад", callback_data="users_hotel"),
],
]
reply_markup = InlineKeyboardMarkup(keyboard)
await query.edit_message_text(
f"Пользователь: {user.first_name} {user.last_name}\nВыберите действие:",
reply_markup=reply_markup,
)