From 6256351aad40e8dc6e33c1842c53f3dc3239911e Mon Sep 17 00:00:00 2001 From: dl Date: Wed, 14 May 2025 17:55:26 +0500 Subject: [PATCH] Phone number check was added. Project structure was updated --- README.md | 29 ++++++++++++---------- auth.py | 23 ++++++++++++++++++ handlers/__init__.py | 9 +++++++ handlers/contact_handler.py | 25 +++++++++++++++++++ handlers/doors_handler.py | 36 ++++++++++++++++++++++++++++ handlers/start_handler.py | 21 ++++++++++++++++ keyboard.py | 11 +++++++++ main.py | 48 +++++-------------------------------- 8 files changed, 148 insertions(+), 54 deletions(-) create mode 100644 auth.py create mode 100644 handlers/__init__.py create mode 100644 handlers/contact_handler.py create mode 100644 handlers/doors_handler.py create mode 100644 handlers/start_handler.py create mode 100755 keyboard.py diff --git a/README.md b/README.md index cdde1de..535126c 100644 --- a/README.md +++ b/README.md @@ -1,22 +1,27 @@ # Usage -Создать файл .env в корневой дирректории проекта, объявить и присвоить значения переменным: +Создать файл .env в корневой дирректории проекта, объявить и присвоить значения переменным: -TOKEN= -LOCK_IP= -CARD_ID= -AUTH_API= +TOKEN= +LOCK_IP= +CARD_ID= +AUTH_API= -, где +, где -TOKEN - токен телеграм для взаимодействия с ботом -LOCK_IP - ip адресс замка -CARD_ID - уникальный номер ключ-карты -AUTH_API - уникальный набор символов, для взаимодействия с API замка +TOKEN - токен телеграм для взаимодействия с ботом +LOCK_IP - ip адресс замка +CARD_ID - уникальный номер ключ-карты +AUTH_API - уникальный набор символов, для взаимодействия с API замка -Дополнительные подробности можно найти в instruction_http_api_v5.pdf и исходном коде программы (см. main.py) +Дополнительные подробности можно найти в instruction_http_api_v5.pdf и исходном коде программы (см. main.py) ## TODO - [x] Написать базовый функционал -- [ ] Создать соотношение типа "id_пользователя: досутп_к_замку:" +~~- [ ] Создать соотношение типа "id_пользователя: досутп_к_замку:"~~ +- [x] Сделать проверку пользователей по их номеру телефона ... +- [ ] Создать и парсить json с информацией о пользователях и их номерах +- [ ] Проверять, является ли пользователь администратором, если является + выводить дополнительную кнопку, предлагающую добавить номер в БД +- [ ] Сделать логирование о том, что кто-то открыл дверь в конкретное время diff --git a/auth.py b/auth.py new file mode 100644 index 0000000..1102056 --- /dev/null +++ b/auth.py @@ -0,0 +1,23 @@ +ALLOWED_PHONE_NUMBERS = [ + "+79000959392", +] + +AUTHORIZED_USERS = {} + + +def check_user_auth(phone: str) -> bool: + return phone in ALLOWED_PHONE_NUMBERS + + +def authorize_user(user_id: int, phone: str) -> bool: + if check_user_auth(phone): + AUTHORIZED_USERS[user_id] = phone + print(f"{user_id} авторизован с номером: {phone}") + return True + else: + print(f"Пользователь {user_id} пытался авторизоваться с номером {phone}") + return False + + +def is_user_auth(user_id: int) -> bool: + return user_id in AUTHORIZED_USERS diff --git a/handlers/__init__.py b/handlers/__init__.py new file mode 100644 index 0000000..f4f37c2 --- /dev/null +++ b/handlers/__init__.py @@ -0,0 +1,9 @@ +from .start_handler import register_start_handler +from .contact_handler import register_contact_handler +from .doors_handler import register_open_door_handler + + +def register_all_handlers(dp): + register_start_handler(dp) + register_contact_handler(dp) + register_open_door_handler(dp) diff --git a/handlers/contact_handler.py b/handlers/contact_handler.py new file mode 100644 index 0000000..2c6bd35 --- /dev/null +++ b/handlers/contact_handler.py @@ -0,0 +1,25 @@ +from aiogram import Dispatcher, F +from aiogram.types import Message + +from keyboard import get_keyboard +from auth import authorize_user + + +def register_contact_handler(dp: Dispatcher): + @dp.message(F.contact) + async def contact_handler(msg: Message): + user_id = msg.from_user.id + + if msg.contact is None: + await msg.answer("Ошибка: номер телефона не получен") + return + + phone = msg.contact.phone_number + print(f"Номер {phone} получен от пользователя {user_id}") + + if not authorize_user(user_id, phone): + await msg.answer("Доступ запрещен, номер не авторизирован") + return + await msg.answer( + "Номер подтвержден", reply_markup=get_keyboard(authorized=True) + ) diff --git a/handlers/doors_handler.py b/handlers/doors_handler.py new file mode 100644 index 0000000..e137e9e --- /dev/null +++ b/handlers/doors_handler.py @@ -0,0 +1,36 @@ +import asyncio +import requests +from os import getenv + +from aiogram import Dispatcher, F +from aiogram.types import Message +from auth import is_user_auth + + +def register_open_door_handler(dp: Dispatcher): + @dp.message(F.text == "Открыть дверь") + async def open_door_handler(msg: Message): + user_id = msg.from_user.id + if not is_user_auth(user_id): + await msg.answer( + "Доступ запрщен. Необходимо предоставить свой номер телефона." + ) + return + + url = f"http://{getenv('LOCK_IP')}/cgi-bin/ext" + auth = ("ext", f"{getenv('AUTH_API')}") + payload = f"CARD={getenv('CARD_ID')}&DIR=0" + headers = {"Content-Type": "application/x-www-form-urlencoded"} + + try: + response = await asyncio.to_thread( + requests.post, url, auth=auth, data=payload, headers=headers, timeout=5 + ) + if response.status_code == 200: + await msg.answer("Открыто") + else: + await msg.answer( + f"Ошибка при открытии замка. Код ошибки: {response.status_code}" + ) + except Exception as e: + await msg.answer(f"Исключение: {str(e)}") diff --git a/handlers/start_handler.py b/handlers/start_handler.py new file mode 100644 index 0000000..de48615 --- /dev/null +++ b/handlers/start_handler.py @@ -0,0 +1,21 @@ +from aiogram import Dispatcher, F +from aiogram.types import Message +from aiogram.filters import CommandStart + +from keyboard import get_keyboard +from auth import is_user_auth + + +def register_start_handler(dp: Dispatcher): + @dp.message(CommandStart()) + async def command_start_handler(msg: Message): + user_id = msg.from_user.id + if is_user_auth(user_id): + await msg.answer( + "Авторизация прошла успешно", reply_markup=get_keyboard(authorized=True) + ) + else: + await msg.answer( + "Для пользования ботом, предоставьте номер телефона", + reply_markup=get_keyboard(authorized=False), + ) diff --git a/keyboard.py b/keyboard.py new file mode 100755 index 0000000..c114510 --- /dev/null +++ b/keyboard.py @@ -0,0 +1,11 @@ +from aiogram.utils.keyboard import ReplyKeyboardBuilder, KeyboardButton + + +def get_keyboard(authorized: bool): + kb = ReplyKeyboardBuilder() + if authorized: + kb.button(text="Открыть дверь") + else: + kb.add(KeyboardButton(text="Поделиться контактом", request_contact=True)) + return kb.as_markup(resize_keyboard=True) + diff --git a/main.py b/main.py index 9ae7679..281902c 100755 --- a/main.py +++ b/main.py @@ -1,18 +1,13 @@ import asyncio import logging import sys -import requests - -from aiogram.types import Message -from aiogram.filters import CommandStart -from aiogram import Bot, Dispatcher -from aiogram.client.default import DefaultBotProperties -from aiogram.enums import ParseMode -from aiogram import F - from os import getenv -from buttons import FBI_open_up +from aiogram import Bot, Dispatcher +from aiogram.enums import ParseMode +from aiogram.client.default import DefaultBotProperties + +from handlers import register_all_handlers from init_config import check_env_file, create_env_file, load_env @@ -24,42 +19,11 @@ load_env() TOKEN = getenv("TOKEN") dp = Dispatcher() - -@dp.message(CommandStart()) -async def command_start_handler(msg: Message): - await msg.answer("msg happens", reply_markup=FBI_open_up()) - - -@dp.message(F.text == "Сизам вскройся") -async def handle_open_door(msg: Message): - user_id = msg.from_user.id - # print(msg.__dict__) - print(user_id) - - url = f"http://{getenv('LOCK_IP')}/cgi-bin/ext" - auth = ("ext", f"{getenv('AUTH_API')}") - payload = f"CARD={getenv('CARD_ID')}&DIR=0" - headers = {"Content-Type": "application/x-www-form-urlencoded"} - - try: - response = await asyncio.to_thread( - requests.post, url, auth=auth, data=payload, headers=headers, timeout=5 - ) - if response.status_code == 200: - await msg.answer("Открыто") - else: - await msg.answer( - f"Ошибка при открытии замка. Код ошибки: {response.status_code}" - ) - except Exception as e: - await msg.answer(f"Исключение: {str(e)}") +register_all_handlers(dp) async def main() -> None: - # Initialize Bot instance with default bot properties which will be passed to all API calls bot = Bot(token=TOKEN, default=DefaultBotProperties(parse_mode=ParseMode.HTML)) - - # And the run events dispatching await dp.start_polling(bot)