Слияние ветки dev: актуальный код бота с inline-клавиатурами и PostgreSQL
This commit is contained in:
Vendored
BIN
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
+222
-71
@@ -1,11 +1,21 @@
|
||||
import re
|
||||
|
||||
from aiogram import Router, F
|
||||
from aiogram.types import Message
|
||||
from aiogram.fsm.context import FSMContext
|
||||
from aiogram.filters import StateFilter
|
||||
|
||||
from aiogram.types import Message, CallbackQuery
|
||||
from app.keyboards.services import get_services_keyboard
|
||||
from app.keyboards.masters import get_masters_keyboard
|
||||
from app.states.booking import BookingState
|
||||
from app.services.booking_service import create_booking
|
||||
from app.services.booking_service import (
|
||||
create_booking,
|
||||
update_booking,
|
||||
get_busy_times
|
||||
)
|
||||
from app.keyboards.menu import menu, cancel_menu
|
||||
from app.keyboards.calendar import get_dates_keyboard
|
||||
from app.keyboards.time_slots import get_time_keyboard
|
||||
from app.keyboards.confirm_booking import get_confirm_booking_keyboard
|
||||
|
||||
router = Router()
|
||||
|
||||
@@ -28,95 +38,236 @@ async def start_booking(message: Message, state: FSMContext):
|
||||
await state.set_state(BookingState.service)
|
||||
|
||||
await message.answer(
|
||||
"Выберите услугу:\n"
|
||||
"1. Массаж\n"
|
||||
"2. SPA\n"
|
||||
"3. Косметология",
|
||||
reply_markup=cancel_menu
|
||||
"Выберите услугу:",
|
||||
reply_markup=get_services_keyboard()
|
||||
)
|
||||
|
||||
|
||||
# выбор услуги
|
||||
@router.message(StateFilter(BookingState.service))
|
||||
async def choose_service(message: Message, state: FSMContext):
|
||||
@router.callback_query(
|
||||
StateFilter(BookingState.service),
|
||||
F.data.startswith("service_")
|
||||
)
|
||||
async def choose_service(
|
||||
callback: CallbackQuery,
|
||||
state: FSMContext
|
||||
):
|
||||
services = {
|
||||
"service_massage": "Массаж",
|
||||
"service_spa": "SPA",
|
||||
"service_cosmetology": "Косметология"
|
||||
}
|
||||
|
||||
forbidden = [
|
||||
"📅 Записаться",
|
||||
"👤 Профиль",
|
||||
"📖 Мои записи",
|
||||
"ℹ️ О компании",
|
||||
"📞 Контакты"
|
||||
]
|
||||
service_name = services[callback.data]
|
||||
|
||||
if message.text in forbidden:
|
||||
await message.answer(
|
||||
"⚠️ Сначала завершите запись или напишите 'отмена'"
|
||||
)
|
||||
return
|
||||
|
||||
await state.update_data(service=message.text)
|
||||
|
||||
await state.set_state(BookingState.date)
|
||||
|
||||
await message.answer(
|
||||
"Введите дату (например 20.06)"
|
||||
await state.update_data(
|
||||
service=service_name
|
||||
)
|
||||
|
||||
await state.set_state(
|
||||
BookingState.master
|
||||
)
|
||||
await callback.message.edit_text(
|
||||
"👤 Выберите специалиста:",
|
||||
reply_markup=get_masters_keyboard()
|
||||
)
|
||||
|
||||
await callback.answer()
|
||||
|
||||
#выбор мастера
|
||||
@router.callback_query(
|
||||
StateFilter(BookingState.master),
|
||||
F.data.startswith("master_")
|
||||
)
|
||||
async def choose_master(
|
||||
callback: CallbackQuery,
|
||||
state: FSMContext
|
||||
):
|
||||
|
||||
masters = {
|
||||
"master_anna": "Анна",
|
||||
"master_maria": "Мария",
|
||||
"master_alex": "Алексей"
|
||||
}
|
||||
|
||||
master_name = masters[callback.data]
|
||||
|
||||
await state.update_data(
|
||||
master=master_name
|
||||
)
|
||||
|
||||
await state.set_state(
|
||||
BookingState.date
|
||||
)
|
||||
|
||||
await callback.message.edit_text(
|
||||
"📅 Выберите дату:",
|
||||
reply_markup=get_dates_keyboard()
|
||||
)
|
||||
|
||||
await callback.answer()
|
||||
|
||||
|
||||
# выбор даты
|
||||
@router.message(StateFilter(BookingState.date))
|
||||
async def choose_date(message: Message, state: FSMContext):
|
||||
@router.callback_query(
|
||||
StateFilter(BookingState.date),
|
||||
F.data.startswith("date_")
|
||||
)
|
||||
async def choose_date(
|
||||
callback: CallbackQuery,
|
||||
state: FSMContext
|
||||
):
|
||||
selected_date = callback.data.replace(
|
||||
"date_",
|
||||
""
|
||||
)
|
||||
|
||||
forbidden = [
|
||||
"📅 Записаться",
|
||||
"👤 Профиль",
|
||||
"📖 Мои записи",
|
||||
"ℹ️ О компании",
|
||||
"📞 Контакты"
|
||||
]
|
||||
|
||||
if message.text in forbidden:
|
||||
await message.answer(
|
||||
"⚠️ Сначала завершите запись или напишите 'отмена'"
|
||||
)
|
||||
return
|
||||
|
||||
await state.update_data(date=message.text)
|
||||
await state.update_data(
|
||||
date=selected_date
|
||||
)
|
||||
|
||||
data = await state.get_data()
|
||||
|
||||
await state.set_state(BookingState.confirm)
|
||||
|
||||
await message.answer(
|
||||
f"Услуга: {data['service']}\n"
|
||||
f"Дата: {data['date']}\n\n"
|
||||
f"Напишите 'да' для подтверждения"
|
||||
busy_times = get_busy_times(
|
||||
master=data["master"],
|
||||
date=selected_date
|
||||
)
|
||||
|
||||
await state.set_state(
|
||||
BookingState.time
|
||||
)
|
||||
|
||||
await callback.message.edit_text(
|
||||
"🕒 Выберите время:",
|
||||
reply_markup=get_time_keyboard(
|
||||
busy_times
|
||||
)
|
||||
)
|
||||
|
||||
await callback.answer()
|
||||
|
||||
|
||||
@router.callback_query(
|
||||
StateFilter(BookingState.time),
|
||||
F.data.startswith("time_")
|
||||
)
|
||||
async def choose_time(
|
||||
callback: CallbackQuery,
|
||||
state: FSMContext
|
||||
):
|
||||
selected_time = callback.data.replace(
|
||||
"time_",
|
||||
""
|
||||
)
|
||||
|
||||
await state.update_data(
|
||||
time=selected_time
|
||||
)
|
||||
|
||||
data = await state.get_data()
|
||||
|
||||
await state.set_state(
|
||||
BookingState.confirm
|
||||
)
|
||||
|
||||
await callback.message.edit_text(
|
||||
f"📋 Проверьте запись:\n\n"
|
||||
f"💆 Услуга: {data['service']}\n"
|
||||
f"📅 Дата: {data['date']}\n"
|
||||
f"👤 Мастер:{data['master']}\n"
|
||||
f"🕒 Время: {selected_time}",
|
||||
reply_markup=get_confirm_booking_keyboard()
|
||||
)
|
||||
|
||||
await callback.answer()
|
||||
|
||||
|
||||
# подтверждение
|
||||
@router.message(StateFilter(BookingState.confirm))
|
||||
async def confirm_booking(message: Message, state: FSMContext):
|
||||
|
||||
if message.text.lower() != "да":
|
||||
await state.clear()
|
||||
|
||||
await message.answer(
|
||||
"❌ Запись отменена",
|
||||
reply_markup=menu
|
||||
)
|
||||
|
||||
@router.callback_query(
|
||||
StateFilter(BookingState.confirm),
|
||||
F.data == "confirm_booking"
|
||||
)
|
||||
async def confirm_booking_callback(
|
||||
callback: CallbackQuery,
|
||||
state: FSMContext
|
||||
):
|
||||
data = await state.get_data()
|
||||
|
||||
create_booking(
|
||||
user_id=message.from_user.id,
|
||||
service=data["service"],
|
||||
date=data["date"]
|
||||
editing_booking_id = data.get(
|
||||
"editing_booking_id"
|
||||
)
|
||||
|
||||
if editing_booking_id:
|
||||
|
||||
update_booking(
|
||||
booking_id=editing_booking_id,
|
||||
service=data["service"],
|
||||
master=data["master"],
|
||||
date=data["date"],
|
||||
time=data["time"]
|
||||
)
|
||||
|
||||
result_text = "✅ Запись успешно обновлена"
|
||||
|
||||
else:
|
||||
|
||||
create_booking(
|
||||
user_id=callback.from_user.id,
|
||||
service=data["service"],
|
||||
master=data["master"],
|
||||
date=data["date"],
|
||||
time=data["time"]
|
||||
)
|
||||
|
||||
result_text = "✅ Запись успешно создана"
|
||||
|
||||
await state.clear()
|
||||
|
||||
await message.answer(
|
||||
"✅ Вы успешно записаны!",
|
||||
reply_markup=menu
|
||||
await callback.message.edit_text(
|
||||
result_text
|
||||
)
|
||||
await callback.answer()
|
||||
|
||||
|
||||
@router.callback_query(
|
||||
StateFilter(BookingState.confirm),
|
||||
F.data == "cancel_booking"
|
||||
)
|
||||
async def cancel_booking_callback(
|
||||
callback: CallbackQuery,
|
||||
state: FSMContext
|
||||
):
|
||||
await state.clear()
|
||||
|
||||
await callback.message.edit_text(
|
||||
"❌ Запись отменена"
|
||||
)
|
||||
|
||||
await callback.answer()
|
||||
|
||||
|
||||
@router.callback_query(
|
||||
StateFilter(BookingState.confirm),
|
||||
F.data == "edit_booking"
|
||||
)
|
||||
async def edit_booking_callback(
|
||||
callback: CallbackQuery,
|
||||
state: FSMContext
|
||||
):
|
||||
old_data = await state.get_data()
|
||||
|
||||
await state.clear()
|
||||
|
||||
await state.update_data(
|
||||
service=old_data.get("service")
|
||||
)
|
||||
|
||||
await state.set_state(
|
||||
BookingState.service
|
||||
)
|
||||
|
||||
from app.keyboards.services import get_services_keyboard
|
||||
|
||||
await callback.message.edit_text(
|
||||
"Выберите услугу:",
|
||||
reply_markup=get_services_keyboard()
|
||||
)
|
||||
+87
-16
@@ -1,28 +1,99 @@
|
||||
from aiogram import Router, F
|
||||
from aiogram.types import Message
|
||||
from aiogram.types import (
|
||||
Message,
|
||||
CallbackQuery
|
||||
)
|
||||
|
||||
from aiogram.fsm.context import FSMContext
|
||||
|
||||
from app.db.memory import bookings
|
||||
from app.states.booking import BookingState
|
||||
from app.keyboards.services import get_services_keyboard
|
||||
|
||||
from app.services.booking_service import (
|
||||
get_user_bookings,
|
||||
cancel_booking,
|
||||
)
|
||||
|
||||
from app.keyboards.my_booking import (
|
||||
get_booking_actions
|
||||
)
|
||||
|
||||
router = Router()
|
||||
|
||||
@router.message(F.text == "📖 Мои записи")
|
||||
async def my_bookings(message: Message, state: FSMContext):
|
||||
await state.clear()
|
||||
|
||||
user_id = message.from_user.id
|
||||
@router.message(
|
||||
F.text == "📖 Мои записи"
|
||||
)
|
||||
async def my_bookings(
|
||||
message: Message
|
||||
):
|
||||
bookings = get_user_bookings(
|
||||
message.from_user.id
|
||||
)
|
||||
|
||||
user_bookings = [
|
||||
b for b in bookings if b["user_id"] == user_id
|
||||
]
|
||||
|
||||
if not user_bookings:
|
||||
await message.answer("У вас нет записей")
|
||||
if not bookings:
|
||||
await message.answer(
|
||||
"У вас нет активных записей"
|
||||
)
|
||||
return
|
||||
|
||||
text = "📖 Ваши записи:\n\n"
|
||||
for booking in bookings:
|
||||
await message.answer(
|
||||
f"💆 {booking['service']}\n"
|
||||
f"👤 {booking['master']}\n"
|
||||
f"📅 {booking['date']}\n"
|
||||
f"🕒 {booking['time']}",
|
||||
reply_markup=get_booking_actions(
|
||||
booking["id"]
|
||||
)
|
||||
)
|
||||
|
||||
for b in user_bookings:
|
||||
text += f"💆 {b['service']} — 📅 {b['date']}\n"
|
||||
|
||||
await message.answer(text)
|
||||
@router.callback_query(
|
||||
F.data.startswith(
|
||||
"cancel_booking_"
|
||||
)
|
||||
)
|
||||
async def cancel_booking_handler(
|
||||
callback: CallbackQuery
|
||||
):
|
||||
booking_id = int(
|
||||
callback.data.split("_")[-1]
|
||||
)
|
||||
|
||||
cancel_booking(
|
||||
booking_id
|
||||
)
|
||||
|
||||
await callback.message.edit_text(
|
||||
"❌ Запись отменена"
|
||||
)
|
||||
|
||||
await callback.answer()
|
||||
|
||||
@router.callback_query(
|
||||
F.data.startswith("edit_booking_")
|
||||
)
|
||||
async def edit_booking_handler(
|
||||
callback: CallbackQuery,
|
||||
state: FSMContext
|
||||
):
|
||||
|
||||
booking_id = int(
|
||||
callback.data.split("_")[-1]
|
||||
)
|
||||
|
||||
await state.update_data(
|
||||
editing_booking_id=booking_id
|
||||
)
|
||||
|
||||
await state.set_state(
|
||||
BookingState.service
|
||||
)
|
||||
|
||||
await callback.message.edit_text(
|
||||
"✏️ Выберите новую услугу:",
|
||||
reply_markup=get_services_keyboard()
|
||||
)
|
||||
|
||||
await callback.answer()
|
||||
Binary file not shown.
@@ -0,0 +1,24 @@
|
||||
from aiogram.types import InlineKeyboardMarkup, InlineKeyboardButton
|
||||
from datetime import datetime, timedelta
|
||||
|
||||
|
||||
def get_dates_keyboard():
|
||||
buttons = []
|
||||
|
||||
today = datetime.now()
|
||||
|
||||
for i in range(7):
|
||||
day = today + timedelta(days=i)
|
||||
|
||||
buttons.append(
|
||||
[
|
||||
InlineKeyboardButton(
|
||||
text=day.strftime("%d.%m"),
|
||||
callback_data=f"date_{day.strftime('%d.%m')}"
|
||||
)
|
||||
]
|
||||
)
|
||||
|
||||
return InlineKeyboardMarkup(
|
||||
inline_keyboard=buttons
|
||||
)
|
||||
@@ -0,0 +1,28 @@
|
||||
from aiogram.types import InlineKeyboardMarkup
|
||||
from aiogram.types import InlineKeyboardButton
|
||||
|
||||
|
||||
def get_confirm_booking_keyboard():
|
||||
|
||||
return InlineKeyboardMarkup(
|
||||
inline_keyboard=[
|
||||
[
|
||||
InlineKeyboardButton(
|
||||
text="✅ Подтвердить",
|
||||
callback_data="confirm_booking"
|
||||
)
|
||||
],
|
||||
[
|
||||
InlineKeyboardButton(
|
||||
text="✏️ Изменить",
|
||||
callback_data="edit_booking"
|
||||
)
|
||||
],
|
||||
[
|
||||
InlineKeyboardButton(
|
||||
text="❌ Отменить",
|
||||
callback_data="cancel_booking"
|
||||
)
|
||||
]
|
||||
]
|
||||
)
|
||||
@@ -0,0 +1,28 @@
|
||||
from aiogram.types import InlineKeyboardMarkup
|
||||
from aiogram.types import InlineKeyboardButton
|
||||
|
||||
|
||||
def get_masters_keyboard():
|
||||
|
||||
return InlineKeyboardMarkup(
|
||||
inline_keyboard=[
|
||||
[
|
||||
InlineKeyboardButton(
|
||||
text="👩 Анна",
|
||||
callback_data="master_anna"
|
||||
)
|
||||
],
|
||||
[
|
||||
InlineKeyboardButton(
|
||||
text="👩 Мария",
|
||||
callback_data="master_maria"
|
||||
)
|
||||
],
|
||||
[
|
||||
InlineKeyboardButton(
|
||||
text="👨 Алексей",
|
||||
callback_data="master_alex"
|
||||
)
|
||||
]
|
||||
]
|
||||
)
|
||||
@@ -0,0 +1,25 @@
|
||||
from aiogram.types import (
|
||||
InlineKeyboardMarkup,
|
||||
InlineKeyboardButton
|
||||
)
|
||||
|
||||
|
||||
def get_booking_actions(
|
||||
booking_id: int
|
||||
):
|
||||
return InlineKeyboardMarkup(
|
||||
inline_keyboard=[
|
||||
[
|
||||
InlineKeyboardButton(
|
||||
text="✏️ Изменить",
|
||||
callback_data=f"edit_booking_{booking_id}"
|
||||
)
|
||||
],
|
||||
[
|
||||
InlineKeyboardButton(
|
||||
text="❌ Отменить",
|
||||
callback_data=f"cancel_booking_{booking_id}"
|
||||
)
|
||||
]
|
||||
]
|
||||
)
|
||||
@@ -0,0 +1,28 @@
|
||||
from aiogram.types import InlineKeyboardMarkup
|
||||
from aiogram.types import InlineKeyboardButton
|
||||
|
||||
|
||||
def get_services_keyboard():
|
||||
|
||||
return InlineKeyboardMarkup(
|
||||
inline_keyboard=[
|
||||
[
|
||||
InlineKeyboardButton(
|
||||
text="💆 Массаж",
|
||||
callback_data="service_massage"
|
||||
)
|
||||
],
|
||||
[
|
||||
InlineKeyboardButton(
|
||||
text="🌿 SPA",
|
||||
callback_data="service_spa"
|
||||
)
|
||||
],
|
||||
[
|
||||
InlineKeyboardButton(
|
||||
text="✨ Косметология",
|
||||
callback_data="service_cosmetology"
|
||||
)
|
||||
]
|
||||
]
|
||||
)
|
||||
@@ -0,0 +1,41 @@
|
||||
from aiogram.types import InlineKeyboardMarkup
|
||||
from aiogram.types import InlineKeyboardButton
|
||||
|
||||
|
||||
def get_time_keyboard(
|
||||
busy_times=None
|
||||
):
|
||||
|
||||
if busy_times is None:
|
||||
busy_times = []
|
||||
|
||||
times = [
|
||||
"10:00",
|
||||
"11:00",
|
||||
"12:00",
|
||||
"13:00",
|
||||
"14:00",
|
||||
"15:00",
|
||||
"16:00",
|
||||
"17:00"
|
||||
]
|
||||
|
||||
buttons = []
|
||||
|
||||
for time in times:
|
||||
|
||||
if time in busy_times:
|
||||
continue
|
||||
|
||||
buttons.append(
|
||||
[
|
||||
InlineKeyboardButton(
|
||||
text=time,
|
||||
callback_data=f"time_{time}"
|
||||
)
|
||||
]
|
||||
)
|
||||
|
||||
return InlineKeyboardMarkup(
|
||||
inline_keyboard=buttons
|
||||
)
|
||||
+5
-1
@@ -10,7 +10,11 @@ from app.handlers.profile import router as profile_router
|
||||
from app.handlers.mybookings import router as mybooking_router
|
||||
from app.handlers.about import router as about_router
|
||||
from app.handlers.contacts import router as contacts_router
|
||||
|
||||
from app.services.booking_service import (
|
||||
create_booking,
|
||||
update_booking,
|
||||
get_busy_times
|
||||
)
|
||||
|
||||
async def main():
|
||||
bot = Bot(token=BOT_TOKEN)
|
||||
|
||||
Binary file not shown.
@@ -1,12 +1,92 @@
|
||||
from app.db.memory import bookings
|
||||
|
||||
def create_booking(user_id: int, service: str, date: str):
|
||||
|
||||
def create_booking(
|
||||
user_id: int,
|
||||
service: str,
|
||||
date: str,
|
||||
master: str,
|
||||
time: str
|
||||
):
|
||||
booking = {
|
||||
"id": len(bookings) + 1,
|
||||
"user_id": user_id,
|
||||
"service": service,
|
||||
"date": date,
|
||||
"master": master,
|
||||
"time": time,
|
||||
"status": "active"
|
||||
}
|
||||
|
||||
bookings.append(booking)
|
||||
|
||||
return booking
|
||||
|
||||
|
||||
def get_busy_times(
|
||||
master: str,
|
||||
date: str
|
||||
):
|
||||
busy_times = []
|
||||
|
||||
for booking in bookings:
|
||||
|
||||
if (
|
||||
booking["master"] == master
|
||||
and booking["date"] == date
|
||||
and booking["status"] == "active"
|
||||
):
|
||||
busy_times.append(
|
||||
booking["time"]
|
||||
)
|
||||
|
||||
return busy_times
|
||||
|
||||
|
||||
def get_user_bookings(
|
||||
user_id: int
|
||||
):
|
||||
result = []
|
||||
|
||||
for booking in bookings:
|
||||
|
||||
if (
|
||||
booking["user_id"] == user_id
|
||||
and booking["status"] == "active"
|
||||
):
|
||||
result.append(booking)
|
||||
|
||||
return result
|
||||
|
||||
def cancel_booking(
|
||||
booking_id: int
|
||||
):
|
||||
|
||||
for booking in bookings:
|
||||
|
||||
if booking["id"] == booking_id:
|
||||
booking["status"] = "cancelled"
|
||||
return True
|
||||
|
||||
return False
|
||||
|
||||
def update_booking(
|
||||
booking_id: int,
|
||||
service: str,
|
||||
master: str,
|
||||
date: str,
|
||||
time: str
|
||||
):
|
||||
|
||||
for booking in bookings:
|
||||
|
||||
if booking["id"] == booking_id:
|
||||
|
||||
booking["service"] = service
|
||||
booking["master"] = master
|
||||
booking["date"] = date
|
||||
booking["time"] = time
|
||||
|
||||
return booking
|
||||
|
||||
return None
|
||||
|
||||
Binary file not shown.
@@ -2,5 +2,7 @@ from aiogram.fsm.state import StatesGroup, State
|
||||
|
||||
class BookingState(StatesGroup):
|
||||
service = State()
|
||||
master = State()
|
||||
date = State()
|
||||
time = State()
|
||||
confirm = State()
|
||||
Reference in New Issue
Block a user