Vildan 2 vuotta sitten
commit
88fc762db5

+ 35 - 0
Readme.md

@@ -0,0 +1,35 @@
+## Readme
+
+Модули сервиса
+- Telegram бот для отправки на бронирование, одобрение и отлклонение заявок
+- Календарь Moodle
+- База данных мероприятий и пользователей
+- микросервис сборки мероприятия после одобрения
+- микросервис отправки мероприятия в календарь
+
+
+## TODO list
+- [x] База данных
+- [ ] Календарь в Moodle 
+<<<<<<< HEAD
+- [x] Телеграм бот
+- [x] Разграничение доступа для администраторов и пользователей
+- [x] Клавиатура для пользователей
+- [x] Клавиатура для заявок регистрации
+- [x] Клавиатура для регистрации
+- [ ] Клавиатура для управления мероприятиями
+- [x] Клавиатура для управления пользователями
+- [ ] Меню для бота
+- [ ] Выгрузка мероприятий в Moodle
+- [ ] Проверка на занятость кабинета
+- [ ] Оповещение о предстоящих мероприятиях в чате коллектива
+- [ ] Просмотр всех событий в боте
+=======
+- [ ] Телеграм бот
+- [ ] Разграничение доступа для администраторов и пользователей
+- [ ] Короткие команды для броонирования
+- [ ] Меню для бота
+- [ ] Выгрузка мероприятий в Moodle
+- [ ] Проверка на занятость кабинета
+- [ ] Оповещение о предстоящих мероприятиях в чате коллектива
+>>>>>>> 5c6b10b... Add readme.md

+ 15 - 0
config.py

@@ -0,0 +1,15 @@
+import os
+from dotenv import load_dotenv
+
+
+load_dotenv()
+env_path = '.env'
+load_dotenv(dotenv_path=env_path)
+
+BOT_TOKEN = os.getenv('BOT_TOKEN')
+DB_HOST = os.getenv('DB_HOST')
+DB_NAME = os.getenv('DB_NAME')
+DB_USER = os.getenv('DB_USER')
+DB_PASS = os.getenv('DB_PASS')
+
+

+ 74 - 0
database.py

@@ -0,0 +1,74 @@
+from mysql.connector import connect
+import config as conf
+
+
+def sql_check_user(db_query):
+    conn = connect(host=conf.db_host,
+                   user=conf.db_user,
+                   password=conf.db_password,
+                   database=conf.db_name)
+
+    cursor = conn.cursor()
+    cursor.execute(db_query)
+    result_set = cursor.fetchall()
+    if len(result_set) == 0:
+        return False
+    elif len(result_set) > 0:
+        return True
+
+
+def sql_simple_check(db_query, field):
+    conn = connect(host=conf.db_host,
+                   user=conf.db_user,
+                   password=conf.db_password,
+                   database=conf.db_name)
+
+    cursor = conn.cursor(dictionary=True)
+    cursor.execute(db_query)
+    result_set = cursor.fetchone()
+    if result_set[field] == '0':
+        return False
+    else:
+        return True
+
+
+def sql_parse_users(db_query):
+    conn = connect(host=conf.db_host,
+                   user=conf.db_user,
+                   password=conf.db_password,
+                   database=conf.db_name)
+    cursor = conn.cursor(dictionary=True)
+    cursor.execute(db_query)
+    result_set = cursor.fetchall()
+    # print(len(result_set))
+    users_list = []
+    if len(result_set) == 0:
+        return False
+    elif len(result_set) > 0:
+        for row in result_set:
+            users_data = {"ID": row['id'],
+                          "ФИО": row['name'],
+                          "Номер телефона": row['phone']}
+            users_list.append(users_data)
+        return users_list
+
+
+def sql_query_send(db_query):
+    conn = connect(host=conf.db_host,
+                   user=conf.db_user,
+                   password=conf.db_password,
+                   database=conf.db_name)
+
+    cursor = conn.cursor()
+    cursor.execute(db_query)
+    conn.commit()
+    conn.close()
+
+
+# sql_query_users(f"select name,phone from user_table where approved = '0'")
+# print(sql_simple_check(f"select approved from user_table where tg_id='338836490'"))
+# print(sql_parse_users(f"select id, name,phone from user_table where approved = '0'"))
+# print(sql_simple_check(f"select admin from user_table where tg_id = '338836490'", "admin"))
+# print(not not sql_query_single_get(f"select tg_id from user_table where tg_id = '11'"))
+# sql_query(f"INSERT INTO user_table (tg_id,name,phone) VALUES ('123123','ФФ ыв ывыв','89539299323')")
+# select name,phone from user_table where approved = '0'

+ 8 - 0
dispatcher.py

@@ -0,0 +1,8 @@
+from aiogram import Bot
+from aiogram.dispatcher import Dispatcher
+from config import BOT_TOKEN
+from aiogram.contrib.fsm_storage.memory import MemoryStorage
+
+
+bot = Bot(token=BOT_TOKEN, parse_mode="HTML",disable_web_page_preview=True)
+dp = Dispatcher(bot, storage=MemoryStorage())

+ 47 - 0
draft.py

@@ -0,0 +1,47 @@
+# @dp.callback_query_handler(text_contains='menu_1')
+# async def menu(call: types.CallbackQuery):
+#     await bot.answer_callback_query(call.id)
+#     await bot.edit_message_reply_markup(call.message.chat.id, call.message.message_id, reply_markup=make_keyboard()
+#
+#
+# @dp.callback_query_handler(text='menu_4')
+# async def xyi(call: types.CallbackQuery):
+#     await bot.answer_callback_query(call.id)
+#     await bot.send_message(call.from_user.id, text="пошел нахуй со своим расписанием")
+
+
+# Класс состояний для бронирования
+# from aiogram.dispatcher.filters.state import StatesGroup, State
+# class BookingState(StatesGroup):
+#     owner = State()
+#     time = State()
+#     description = State()
+#     group = State()
+#     persons = State()
+
+
+
+
+# async def user_manage(message: types.Message):
+#     if not sql_check_user(f"select tg_id from user_table where tg_id ={message.from_user.id}") or \
+#             not sql_simple_check(f"select approved from user_table where tg_id={message.from_user.id}", "approved"):
+#         await message.delete()
+#         await message.answer("Команды станут доступны после регистрации", reply_markup=register_kb)
+#     elif not sql_simple_check(f'select admin from user_table where tg_id = {message.from_user.id}', "admin"):
+#         await message.answer("Доступ только для администраторов", reply_markup=main_kb)
+#     else:
+#         await message.answer(f"Управление пользователями\n\n"
+#                              f"Здесь вы можете управлять заявками на регистрацию\n\n")
+#         if not sql_check_user(f"select name,phone from user_table where approved = '0'"):
+#             await message.answer("Нет заявок на регистрацию")
+#         else:
+#             data = sql_parse_users(f"select id,name,phone from user_table where approved = '0'")
+#             await message.answer(f"Заявки на регистрацию")
+#             await message.answer(" ".join(sql_parse_users(f"select id,name,phone from user_table where approved = '0'")))
+#             await message.answer(''.join(data[:1]), reply_markup=kb_user_manage())
+
+
+# @dp.callback_query_handler()
+# async def select_date(call: types.CallbackQuery):
+#     await bot.answer_callback_query(call.id)
+#     await bot.send_message(call.message.chat.id, "Вы выбрали " + call.data)

+ 61 - 0
functions.py

@@ -0,0 +1,61 @@
+import phonenumbers
+import re
+import datetime
+
+
+def validate_phone(number):
+    number = number.replace('-', '')
+    number = number.replace(' ', '')
+
+    if len(number) == 10:
+        number = "+7" + number
+    elif len(number) == 11 and number[0] == '8':
+        number = "+7" + number[1:]
+
+    try:
+        parse_phone = phonenumbers.parse(number)
+        if phonenumbers.is_possible_number(parse_phone):
+            return True
+        else:
+            return False
+    except:
+        return False
+
+
+def reject_cmd(text):
+    if "/" in text:
+        return True
+    else:
+        return False
+
+
+def reject_latin(text):
+    if re.search(r'[a-zA-Z0-9]', text):
+        return True
+    else:
+        return False
+
+
+def validate_fio(text):
+    if len(text.split(' ')) < 3:
+        return True
+    else:
+        return False
+
+
+def validate_time():
+    pass
+
+
+def beauty_reg_request(data):
+
+    result = (f"ФИО: {data['ФИО']}\n"
+              f"Контакт: {data['Номер телефона']}")
+
+    return result
+
+
+def make_date():
+    today = datetime.datetime.now()
+    return datetime.datetime.strftime(today, '%d.%m.%Y')
+

+ 0 - 0
handlers.py


+ 6 - 0
handlers/__init__.py

@@ -0,0 +1,6 @@
+import handlers.admin.admin_menu
+import handlers.admin.manage_users
+import handlers.admin.manage_events
+import handlers.registration
+import handlers.comands
+import handlers.events

+ 3 - 0
handlers/admin/__init__.py

@@ -0,0 +1,3 @@
+import handlers.admin.admin_menu
+import handlers.admin.manage_users
+import handlers.admin.manage_events

+ 32 - 0
handlers/admin/admin_menu.py

@@ -0,0 +1,32 @@
+from aiogram import types,Dispatcher
+from database import sql_simple_check, sql_check_user
+from keyboards import register_kb, main_kb, admin_keyboard
+
+
+async def enter_admin_menu(message: types.message):
+    if not sql_check_user(f"select tg_id from user_table where tg_id ={message.from_user.id}") or \
+            not sql_simple_check(f"select approved from user_table where tg_id={message.from_user.id}", "approved"):
+        await message.delete()
+        await message.answer("Команды станут доступны после регистрации", reply_markup=register_kb)
+    elif not sql_simple_check(f'select admin from user_table where tg_id = {message.from_user.id}', "admin"):
+        await message.answer("Доступ только для администраторов")
+    else:
+        await message.delete()
+        await message.answer(f"Панель управления лакеем\n\n"
+                             f"Здесь вы можете управлять заявками", reply_markup=admin_keyboard)
+
+
+async def exit_admin_menu(message: types.message):
+    if not sql_check_user(f"select tg_id from user_table where tg_id ={message.from_user.id}") or \
+            not sql_simple_check(f"select approved from user_table where tg_id={message.from_user.id}", "approved"):
+        await message.delete()
+        await message.answer("Команды станут доступны после регистрации", reply_markup=register_kb)
+    elif not sql_simple_check(f'select admin from user_table where tg_id = {message.from_user.id}', "admin"):
+        await message.answer("Доступ только для администраторов", reply_markup=main_kb)
+    else:
+        await message.answer("Выход", reply_markup=main_kb)
+
+
+def register_admin_menu(dp: Dispatcher):
+    dp.register_message_handler(enter_admin_menu, text="👮 Управление")
+    dp.register_message_handler(exit_admin_menu, text="Выйти")

+ 1 - 0
handlers/admin/manage_events.py

@@ -0,0 +1 @@
+# TODO: Добавить управоение мероприятиями

+ 109 - 0
handlers/admin/manage_users.py

@@ -0,0 +1,109 @@
+from database import sql_check_user,sql_parse_users,sql_query_send
+from aiogram import types, Dispatcher
+from functions import beauty_reg_request
+from keyboards import user_manage_kb
+
+
+async def list_users(message: types.Message):
+    if not sql_check_user(f"select name,phone from user_table where approved = '0'"):
+        await message.answer('Заявки на регистрацию отсутствуют')
+    else:
+        data = sql_parse_users(f"select id,name,phone from user_table where approved = '0'")
+        await message.answer(beauty_reg_request(data[0]),
+                             reply_markup=user_manage_kb(f"accept:{data[0]['ID']}",
+                                                         f"deny:{data[0]['ID']}",
+                                                         f"next:0",
+                                                         f"prev:0",
+                                                         f"1/{len(data)}"))
+
+
+async def next_user_page(call: types.CallbackQuery):
+    data = sql_parse_users(f"select id,name,phone from user_table where approved = '0'")
+    index = int(call.data.split(":")[1]) + 1
+
+    if index == len(data):
+        pass
+    elif not data:
+        await call.message.answer('Заявки на регистрацию отсутствуют')
+    else:
+        print(f"next: {index}")
+        user_id = data[index]['ID']
+        await call.message.edit_text(beauty_reg_request(data[index]),
+                                     reply_markup=user_manage_kb(f"accept:{user_id}",
+                                                                 f"deny:{user_id}",
+                                                                 f"next:{index}",
+                                                                 f"prev:{index}",
+                                                                 f"{index + 1}/{len(data)}"))
+
+
+async def prev_user_page(call: types.CallbackQuery):
+    data = sql_parse_users(f"select id,name,phone from user_table where approved = '0'")
+    index = int(call.data.split(":")[1])-1
+    print(f"prev_index{index}")
+    if not data:
+        await call.message.answer('Заявки на регистрацию отсутствуют')
+    elif index < 0:
+        pass
+    else:
+        user_id = data[index]['ID']
+        await call.message.edit_text(beauty_reg_request(data[index]),
+                                     reply_markup=user_manage_kb(f"accept:{user_id}",
+                                                                 f"deny:{user_id}",
+                                                                 f"next:{index}",
+                                                                 f"prev:{index}",
+                                                                 f"{index+1}/{len(data)}"))
+
+
+async def accept_user(call: types.CallbackQuery):
+    data = sql_parse_users(f"select id,name,phone from user_table where approved = '0'")
+    index = int(call.message.reply_markup.inline_keyboard[1][1].text.split("/")[0])-1
+    print(index)
+    #
+
+    if len(data) == 1:
+        user_id = data[index]['ID']
+        sql_query_send(f"UPDATE booking.user_table SET approved='1' WHERE id={user_id}")
+        await call.message.delete()
+        await call.message.answer('Заявки на регистрацию отсутствуют')
+    elif index == 0:
+        user_id = data[index]['ID']
+        sql_query_send(f"UPDATE booking.user_table SET approved='1' WHERE id={user_id}")
+        await call.message.edit_text(beauty_reg_request(data[index+1]),
+                                     reply_markup=user_manage_kb(f"accept:{user_id}",
+                                                                 f"deny:{user_id}",
+                                                                 f"next:{index + 1}",
+                                                                 f"prev:{index + 1}",
+                                                                 f"{index+1}/{len(data) - 1}"))
+    elif index == len(data)-1:
+        user_id = data[index]['ID']
+        sql_query_send(f"UPDATE booking.user_table SET approved='1' WHERE id={user_id}")
+        await call.message.edit_text(beauty_reg_request(data[index-1]),
+                                     reply_markup=user_manage_kb(f"accept:{user_id}",
+                                                                 f"deny:{user_id}",
+                                                                 f"next:{index - 1}",
+                                                                 f"prev:{index - 1}",
+                                                                 f"{index}/{len(data) - 1}"))
+    else:
+        user_id = data[index]['ID']
+        sql_query_send(f"UPDATE booking.user_table SET approved='1' WHERE id={user_id}")
+        await call.message.edit_text(beauty_reg_request(data[index-1]),
+                                     reply_markup=user_manage_kb(f"accept:{user_id}",
+                                                                 f"deny:{user_id}",
+                                                                 f"next:{index}",
+                                                                 f"prev:{index - 1}",
+                                                                 f"{index}/{len(data) - 1}"))
+
+
+async def events_manage(message: types.message):
+    await message.answer(f"Управление мероприятиями\n"
+                         f"Здесь вы можете управлять заявками мероприятий\n\n")
+
+
+# Регистрация команд
+def admin_handlers(dp: Dispatcher):
+    dp.register_message_handler(list_users, text='👤 Управление пользователями')
+    dp.register_message_handler(events_manage, text='🎫 Управление мероприятиями')
+
+    dp.register_callback_query_handler(next_user_page, text_startswith='next')
+    dp.register_callback_query_handler(prev_user_page, text_startswith='prev')
+    dp.register_callback_query_handler(accept_user, text_startswith='accept')

+ 1 - 0
handlers/admin/notifications.py

@@ -0,0 +1 @@
+# TODO: Рассылка для администраторов

+ 35 - 0
handlers/comands.py

@@ -0,0 +1,35 @@
+from aiogram import types, Dispatcher
+from dispatcher import bot
+from keyboards import main_kb, register_kb, make_calendar, admin_keyboard, check_register_kb
+from database import sql_check_user, sql_simple_check
+
+
+# @dp.message_handler(commands=['start'])
+async def start_cmd(message: types.Message):
+    await message.delete()
+    if not sql_check_user(f"select tg_id from user_table where tg_id ={message.from_user.id}"):
+        await message.answer(f"🤖Вас приветствует лакей ТТИТ🤖\n\n"
+                             "Для доступа к функциям нужно пройти простую регистрацию\n",
+                             reply_markup=register_kb)
+    elif not sql_simple_check(f"select approved from user_table where tg_id={message.from_user.id}", "approved"):
+        await message.answer(f"Ваша заявка находится на рассмотрернии", reply_markup=check_register_kb)
+    else:
+        await message.answer(f"🤖Вас приветствует лакей ТТИТ🤖\n"
+                             "\n"
+                             "Я помогу Вам запланировать мероприятие в 405 аудитории.\n\n"
+                             "Меня еще разрабатыают по этому умею немного.\n\n"
+                             "Вот мои функции:\n"
+                             "Запланировать мероприятие\n"
+                             "Мои события\n"
+                             "Все события(В разработке)\n\n"
+                             "Если есть пожелания или замечания\n"
+                             "Можете обратиться к создателю @FeldwebelWillman",
+                             reply_markup=main_kb)
+
+
+# @dp.message_handler()
+
+
+def main_register(dp: Dispatcher):
+    dp.register_message_handler(start_cmd, commands=['start', 'help'])
+    # dp.register_callback_query_handler(select_date)

+ 41 - 0
handlers/events.py

@@ -0,0 +1,41 @@
+from aiogram import types, Dispatcher
+from database import sql_check_user,sql_simple_check
+from keyboards import register_kb,make_calendar
+from functions import make_date
+
+
+async def make_event(message: types.message):
+    if not sql_check_user(f"select tg_id from user_table where tg_id ={message.from_user.id}") or \
+            not sql_simple_check(f"select approved from user_table where tg_id={message.from_user.id}", "approved"):
+        await message.delete()
+        await message.answer("Команды станут доступны после регистрации", reply_markup=register_kb)
+    else:
+        if message.text == "🎯 Запланировать мероприятие":
+
+            await message.delete()
+            await message.answer(f"Выберете дату чтобы увидеть список мероприятий\n\n"
+                                 f"Так же календарь мероприятий можно посмотреть в "
+                                 f"<a href=moodle.tomtit.tomsk.ru>Moodle</a>\n\n"
+                                 f"Сегодняшняя дата <b>{make_date()}</b>", reply_markup=make_calendar())
+            msg = await message.answer("asd")
+            print(msg)
+
+
+async def select_date(call: types.CallbackQuery):
+    await call.message.answer(call.data)
+
+
+async def my_events(message: types.Message):
+    pass
+
+
+async def all_events(message: types.Message):
+    pass
+
+
+def events_register(dp: Dispatcher):
+    dp.register_message_handler(make_event, text="🎯 Запланировать мероприятие")
+    dp.register_message_handler(my_events, text="🗒 ️Мои события")
+    dp.register_message_handler(all_events, text="📅 Все события")
+
+    dp.register_callback_query_handler(select_date, text_startswith='date_')

+ 88 - 0
handlers/registration.py

@@ -0,0 +1,88 @@
+from aiogram import types, Dispatcher
+# from dispatcher import bot
+from states import RegisterStates
+from functions import validate_fio, validate_phone, reject_latin, reject_cmd
+from aiogram.dispatcher.storage import FSMContext
+from keyboards import reset_register_kb, register_kb, main_kb, check_register_kb
+from database import sql_check_user, sql_query_send
+
+
+async def registration(message: types.Message):
+    await message.delete()
+    if sql_check_user(f'select tg_id from user_table where tg_id = {message.from_user.id} and approved = 0'):
+        await message.answer("Ваша заявка рассматривается", reply_markup=check_register_kb)
+    elif sql_check_user(f'select tg_id from user_table where tg_id = {message.from_user.id} and approved = 1'):
+        msg = await message.answer("Вы зарегистрированны", reply_markup=main_kb)
+        await msg.delete()
+    else:
+        await message.answer(f"Дkя регистрации необходимо указать\n"
+                             f"Номер телефона\n"
+                             f"Фамилия Имя Отчество")
+
+        await message.answer(f"Введите номер телефона\n"
+                             f"Возможные форматы:\n\n"
+                             f"<b>+79995554433</b>\n"
+                             f"<b>9997771122</b>\n"
+                             f"<b>89995554433</b>\n"
+                             f"<b>8-999-888-11-22</b>\n"
+                             f"<b>+7-999-888-11-22</b>", reply_markup=reset_register_kb)
+
+        await RegisterStates.phone.set()
+
+
+async def get_number(message: types.Message, state: FSMContext):
+    if validate_phone(message.text):
+        await state.update_data(number=message.text)
+        await message.answer(f"Теперь укажите ФИО\n"
+                             f"Например: Иванов Иван Иванович", reply_markup=reset_register_kb)
+        await RegisterStates.FIO.set()
+    else:
+        await message.delete()
+        await message.answer(f"Указан некорректный номер телефона", reply_markup=reset_register_kb)
+
+
+async def get_fio(message: types.Message, state: FSMContext):
+    if reject_cmd(message.text):
+        await message.delete()
+        await message.answer("Нельзя использовать команды", reply_markup=reset_register_kb)
+    elif reject_latin(message.text):
+        await message.delete()
+        await message.answer("Нельзя использовать латиницу и символы", reply_markup=reset_register_kb)
+    elif validate_fio(message.text):
+        await message.answer("Необходимо указать полное ФИО", reply_markup=reset_register_kb)
+    else:
+        await state.update_data(FIO=message.text)
+        await state.update_data(id=message.from_user.id)
+        reg_data = await state.get_data()
+        await message.answer(f"Спасибо за регистрацию\n"
+                             f"Вы сможете воспользоваться функциями после одобрения\n", reply_markup=check_register_kb)
+
+        sql_query_send(f"INSERT INTO user_table"
+                       f"(tg_id,name,phone) VALUES "
+                       f"({reg_data['id']},"
+                       f"'{reg_data['FIO']}'"
+                       f",{reg_data['number']})")
+        await state.finish()
+
+
+async def reset_register(message: types.Message, state: FSMContext):
+    await state.finish()
+    await message.answer("Регистрация отменена", reply_markup=register_kb)
+
+
+async def check_reg_status(message: types.Message):
+    await message.delete()
+    if sql_check_user(f'select tg_id from user_table where tg_id = {message.from_user.id} and approved = 0'):
+        await message.answer("Ваша заявка рассматривается", reply_markup=check_register_kb)
+    elif sql_check_user(f'select tg_id from user_table where tg_id = {message.from_user.id} and approved = 1'):
+        await message.answer("Вы зарегистрированны", reply_markup=main_kb)
+
+
+def register_handlers(dp: Dispatcher):
+    # хендлеры регистрации
+    dp.register_message_handler(registration, text="Зарегистрироваться")
+    dp.register_message_handler(check_reg_status, text="Проверить статус заявки")
+    dp.register_message_handler(reset_register, text='Отменить регистрацию', state=[RegisterStates.phone,
+                                                                                    RegisterStates.FIO])
+    dp.register_message_handler(get_number, state=RegisterStates.phone)
+    dp.register_message_handler(get_fio, state=RegisterStates.FIO)

+ 69 - 0
keyboards.py

@@ -0,0 +1,69 @@
+from aiogram.types import InlineKeyboardMarkup, InlineKeyboardButton, ReplyKeyboardMarkup, KeyboardButton
+import calendar
+import datetime
+
+
+# Основная
+button_bind = KeyboardButton('🎯 Запланировать мероприятие')
+button_my = KeyboardButton('🗒 ️Мои события')
+button_all = KeyboardButton('📅 Все события')
+button_config = KeyboardButton("👮 Управление")
+main_kb = ReplyKeyboardMarkup(resize_keyboard=True)
+
+main_kb.add(button_bind, button_config)
+main_kb.add(button_my, button_all)
+
+# регистрация
+register_kb = ReplyKeyboardMarkup(resize_keyboard=True)
+button_register = KeyboardButton('Зарегистрироваться')
+register_kb.add(button_register)
+
+# отмена регистрации
+reset_register_kb = ReplyKeyboardMarkup(resize_keyboard=True)
+res_button = KeyboardButton("Отменить регистрацию")
+reset_register_kb.add(res_button)
+
+# Проверка статуса регистрации
+check_register_kb = ReplyKeyboardMarkup(resize_keyboard=True)
+check_button = KeyboardButton("Проверить статус заявки")
+check_register_kb.add(check_button)
+
+
+# Для администраторов
+admin_keyboard = ReplyKeyboardMarkup(resize_keyboard=True)
+waiting_users = KeyboardButton("👤 Управление пользователями")
+waiting_events = KeyboardButton("🎫 Управление мероприятиями")
+back = KeyboardButton("Выйти")
+admin_keyboard.add(waiting_events, waiting_users)
+admin_keyboard.add(back)
+
+
+# Клавиатура для заявок
+def user_manage_kb(b_accept, b_deny, b_next, b_prev, b_count):
+    keyboard = InlineKeyboardMarkup()
+    accept_button = InlineKeyboardButton(text="Одобрить", callback_data=b_accept)
+    deny_button = InlineKeyboardButton(text="Отклонить", callback_data=b_deny)
+    count_button = InlineKeyboardButton(text=b_count, callback_data="NULL")  # f"{index}/{len(data)}"
+    next_button = InlineKeyboardButton(text="Далее", callback_data=b_next)   # f"next:0"
+    prev_button = InlineKeyboardButton(text="Назад", callback_data=b_prev)
+
+    keyboard.add(accept_button, deny_button)
+    keyboard.add(prev_button, count_button, next_button)
+    return keyboard
+
+
+# Генератор календаря
+def make_calendar():
+    current_date = datetime.date.today()
+    days_in_month = calendar.monthrange(current_date.year, current_date.month)[1]
+    month = datetime.datetime.today().month
+    keyboard = InlineKeyboardMarkup(row_width=7)
+    button_today = InlineKeyboardButton(text="Сегодня", callback_data="today")
+    button_tomorrow = InlineKeyboardButton(text="Завтра", callback_data="tomorrow")
+    button_after_tomorrow = InlineKeyboardButton(text="Послезавтра",callback_data="after_tomorrow")
+
+    for i in range(1, days_in_month+1, 1):
+        date_i = InlineKeyboardButton(text=str(i), callback_data=f"date_{str(i)}.{str(month)}")
+        keyboard.insert(date_i)
+    keyboard.add(button_today, button_tomorrow, button_after_tomorrow)
+    return keyboard

+ 13 - 0
main.py

@@ -0,0 +1,13 @@
+from aiogram.utils import executor
+from dispatcher import dp
+import handlers.admin.admin_menu
+
+
+if __name__ == '__main__':
+    handlers.registration.register_handlers(dp)
+    handlers.comands.main_register(dp)
+    handlers.events.events_register(dp)
+    handlers.admin.admin_menu.register_admin_menu(dp)
+    handlers.admin.manage_users.admin_handlers(dp)
+    executor.start_polling(dp, skip_updates=True)
+

+ 20 - 0
requirements.txt

@@ -0,0 +1,20 @@
+dotenv
+aiogram==2.22.2
+aiohttp==3.8.3
+aiosignal==1.2.0
+async-timeout==4.0.2
+asynctest==0.13.0
+attrs==22.1.0
+Babel==2.9.1
+certifi==2022.9.24
+charset-normalizer==2.1.1
+frozenlist==1.3.1
+idna==3.4
+multidict==6.0.2
+mysql-connector-python==8.0.30
+phonenumbers==8.12.56
+protobuf==3.20.1
+python-dotenv==0.21.0
+pytz==2022.4
+typing-extensions==4.3.0
+yarl==1.8.1

+ 14 - 0
states.py

@@ -0,0 +1,14 @@
+from aiogram.dispatcher.filters.state import StatesGroup, State
+
+
+class RegisterStates(StatesGroup):
+    FIO = State()
+    phone = State()
+
+
+class BookingState(StatesGroup):
+    owner = State()
+    time = State()
+    description = State()
+    group = State()
+    persons = State()