First full realization. JSON is main configuration now, .env was deleted

devel
dl 2025-05-16 16:30:29 +05:00
parent 6256351aad
commit ca47adf559
11 changed files with 153 additions and 51 deletions

View File

@ -21,7 +21,7 @@ AUTH_API - уникальный набор символов, для взаимо
- [x] Написать базовый функционал - [x] Написать базовый функционал
~~- [ ] Создать соотношение типа "id_пользователя: досутп_к_замку:"~~ ~~- [ ] Создать соотношение типа "id_пользователя: досутп_к_замку:"~~
- [x] Сделать проверку пользователей по их номеру телефона ... - [x] Сделать проверку пользователей по их номеру телефона ...
- [ ] Создать и парсить json с информацией о пользователях и их номерах - [x] Удалить модуль getenv
- [ ] Проверять, является ли пользователь администратором, если является - [x] Создать и парсить json с информацией о пользователях и их номерах
выводить дополнительную кнопку, предлагающую добавить номер в БД ~~- [ ] Проверять, является ли пользователь администратором, если является, выводить дополнительную кнопку, предлагающую добавить номер в БД~~
- [ ] Сделать логирование о том, что кто-то открыл дверь в конкретное время - [ ] Сделать логирование о том, что кто-то открыл дверь в конкретное время

29
auth.py
View File

@ -1,6 +1,8 @@
ALLOWED_PHONE_NUMBERS = [ from config import config
"+79000959392", import re
]
ALLOWED_PHONE_NUMBERS = list(config.get("users", {}).keys())
AUTHORIZED_USERS = {} AUTHORIZED_USERS = {}
@ -9,13 +11,26 @@ def check_user_auth(phone: str) -> bool:
return phone in ALLOWED_PHONE_NUMBERS return phone in ALLOWED_PHONE_NUMBERS
def normalize_phone(phone: str) -> str:
phone = phone.strip()
phone = re.sub(r"[^\d+]", "", phone)
if not phone.startswith("+"):
phone = "+" + phone
return phone
def authorize_user(user_id: int, phone: str) -> bool: def authorize_user(user_id: int, phone: str) -> bool:
if check_user_auth(phone): normalized_phone = normalize_phone(phone)
AUTHORIZED_USERS[user_id] = phone if normalized_phone in ALLOWED_PHONE_NUMBERS:
print(f"{user_id} авторизован с номером: {phone}") AUTHORIZED_USERS[user_id] = normalized_phone
# if check_user_auth(phone):
# AUTHORIZED_USERS[user_id] = phone
print(f"{user_id} авторизован с номером: {normalized_phone}")
return True return True
else: else:
print(f"Пользователь {user_id} пытался авторизоваться с номером {phone}") print(
f"Пользователь {user_id} пытался авторизоваться с номером {normalized_phone}"
)
return False return False

23
bot_config.json 100644
View File

@ -0,0 +1,23 @@
{
"bot_token": "",
"locks": {
"Room418": {
"ip": "10.9.1.26",
"auth_api": "73B15D12"
},
"Floor4": {
"ip": "10.9.1.27",
"auth_api": "F901C40A"
}
},
"users": {
"+79000959392": {
"access_card": "000D001195DD",
"lock_id": ["Room418", "Floor4"]
},
"+79221716513": {
"access_card": "",
"lock_id": ["Room418", "Floor4"]
}
}
}

View File

@ -1,7 +0,0 @@
from aiogram.utils.keyboard import ReplyKeyboardBuilder
def FBI_open_up():
kb = ReplyKeyboardBuilder()
kb.button(text = "Сизам вскройся")
return kb.as_markup(resize_keyboard = True)

11
config.py 100644
View File

@ -0,0 +1,11 @@
import json
CONFIG_PATH = "bot_config.json"
def load_config(path: str = CONFIG_PATH) -> dict:
with open(path, "r", encoding="utf-8") as f:
return json.load(f)
config = load_config()

View File

@ -1,8 +1,9 @@
from aiogram import Dispatcher, F from aiogram import Dispatcher, F
from aiogram.types import Message from aiogram.types import Message
from keyboard import get_keyboard from keyboard import get_locks_keyboard, get_contact_keyboard
from auth import authorize_user from auth import authorize_user, normalize_phone
from config import config
def register_contact_handler(dp: Dispatcher): def register_contact_handler(dp: Dispatcher):
@ -14,12 +15,22 @@ def register_contact_handler(dp: Dispatcher):
await msg.answer("Ошибка: номер телефона не получен") await msg.answer("Ошибка: номер телефона не получен")
return return
phone = msg.contact.phone_number phone = normalize_phone(msg.contact.phone_number)
print(f"Номер {phone} получен от пользователя {user_id}")
if not authorize_user(user_id, phone): if not authorize_user(user_id, phone):
await msg.answer("Доступ запрещен, номер не авторизирован") await msg.answer("Доступ запрещен, номер не идентифицирован")
return return
user_conf = config.get("users", {}).get(phone)
print(f"***user_conf для {phone}: {user_conf}***")
if not user_conf:
await msg.answer("Пользователь не опознан")
return
allowed_locks = user_conf.get("lock_id", [])
print(f"***allowed_locks = {allowed_locks}***")
reply_markup = get_locks_keyboard(allowed_locks)
await msg.answer( await msg.answer(
"Номер подтвержден", reply_markup=get_keyboard(authorized=True) "Номер подтвержден. Выберите дверь для открытия", reply_markup=reply_markup
) )

View File

@ -1,30 +1,66 @@
import asyncio import asyncio
import requests import requests
from os import getenv
from aiogram import Dispatcher, F from aiogram import Dispatcher
from aiogram.types import Message from aiogram.types import Message
from auth import is_user_auth
from auth import AUTHORIZED_USERS, is_user_auth
from config import config
def register_open_door_handler(dp: Dispatcher): def register_open_door_handler(dp: Dispatcher):
@dp.message(F.text == "Открыть дверь") @dp.message()
async def open_door_handler(msg: Message): async def open_door_handler(msg: Message):
print(
f"DEBUG: Получено сообщение от пользователя {msg.from_user.id}: '{msg.text}'"
)
user_id = msg.from_user.id user_id = msg.from_user.id
if not is_user_auth(user_id): if not is_user_auth(user_id):
await msg.answer( await msg.answer(
"Доступ запрщен. Необходимо предоставить свой номер телефона." "Доступ запрщен. Необходимо предоставить свой номер телефона."
) )
else:
print("OK")
phone = AUTHORIZED_USERS.get(user_id)
print(AUTHORIZED_USERS)
print(f"***user_id={user_id},phone={phone}***")
user_conf = config.get("users", {}).get(phone)
print(f"***phone={phone}, user_conf={user_conf}***")
# allowed_locks = user_conf.get("locks", [])
# print(f"***allowed_locks={allowed_locks}***")
# if msg.text not in allowed_locks:
# print("**********************")
# print(f"***{allowed_locks}***")
# print(f"***{msg.text}***")
# print("**********************")
# return
lock_conf = config.get("locks", {}).get(msg.text)
if not lock_conf:
await msg.answer("Информации по замку не найдено")
return return
url = f"http://{getenv('LOCK_IP')}/cgi-bin/ext" url = f"http://{lock_conf['ip']}/cgi-bin/ext"
auth = ("ext", f"{getenv('AUTH_API')}") auth_info = ("ext", lock_conf["auth_api"])
payload = f"CARD={getenv('CARD_ID')}&DIR=0" payload = f"CARD={user_conf['access_card']}&DIR=0"
headers = {"Content-Type": "application/x-www-form-urlencoded"} headers = {"Content-Type": "application/x-www-form-urlencoded"}
try: try:
print(
f"***DEBUG: Отправляю запрос к {url} c payload: {payload} и auth: {auth_info}"
)
response = await asyncio.to_thread( response = await asyncio.to_thread(
requests.post, url, auth=auth, data=payload, headers=headers, timeout=5 requests.post,
url,
auth=auth_info,
data=payload,
headers=headers,
timeout=5,
)
print(
f"DEBUG: URL: {url}, status: {response.status_code}, response: {response.text}"
) )
if response.status_code == 200: if response.status_code == 200:
await msg.answer("Открыто") await msg.answer("Открыто")

View File

@ -2,8 +2,9 @@ from aiogram import Dispatcher, F
from aiogram.types import Message from aiogram.types import Message
from aiogram.filters import CommandStart from aiogram.filters import CommandStart
from keyboard import get_keyboard from keyboard import get_contact_keyboard, get_locks_keyboard
from auth import is_user_auth from auth import is_user_auth, AUTHORIZED_USERS
from config import config
def register_start_handler(dp: Dispatcher): def register_start_handler(dp: Dispatcher):
@ -11,11 +12,25 @@ def register_start_handler(dp: Dispatcher):
async def command_start_handler(msg: Message): async def command_start_handler(msg: Message):
user_id = msg.from_user.id user_id = msg.from_user.id
if is_user_auth(user_id): if is_user_auth(user_id):
phone = AUTHORIZED_USERS.get(user_id)
if not phone:
await msg.answer("Номер не найден")
return
user_conf = config.get("user", {}).get(phone)
if not user_conf:
await msg.answer("Пользователь не найден в конфигурации")
return
allowed_locks = user_conf.get("locks_id", [])
reply_markup = get_locks_keyboard(allowed_locks)
await msg.answer( await msg.answer(
"Авторизация прошла успешно", reply_markup=get_keyboard(authorized=True) "Авторизация прошла успешно",
reply_markup=reply_markup,
) )
else: else:
reply_markup = get_contact_keyboard()
await msg.answer( await msg.answer(
"Для пользования ботом, предоставьте номер телефона", "Для пользования ботом, предоставьте номер телефона",
reply_markup=get_keyboard(authorized=False), reply_markup=reply_markup,
) )

View File

@ -1,11 +1,16 @@
from aiogram.utils.keyboard import ReplyKeyboardBuilder, KeyboardButton from aiogram.utils.keyboard import ReplyKeyboardBuilder
from aiogram.types import KeyboardButton
def get_keyboard(authorized: bool): def get_locks_keyboard(allowed_locks: list):
print(f"DEBUG: allowed_locks = {allowed_locks}")
kb = ReplyKeyboardBuilder() kb = ReplyKeyboardBuilder()
if authorized: for lock in allowed_locks:
kb.button(text="Открыть дверь") kb.button(text=lock)
else:
kb.add(KeyboardButton(text="Поделиться контактом", request_contact=True))
return kb.as_markup(resize_keyboard=True) return kb.as_markup(resize_keyboard=True)
def get_contact_keyboard():
kb = ReplyKeyboardBuilder()
kb.add(KeyboardButton(text="Поделиться контактом", request_contact=True))
return kb.as_markup(resize_keyboard=True)

12
main.py
View File

@ -1,29 +1,23 @@
import asyncio import asyncio
import logging import logging
import sys import sys
from os import getenv
from aiogram import Bot, Dispatcher from aiogram import Bot, Dispatcher
from aiogram.enums import ParseMode from aiogram.enums import ParseMode
from aiogram.client.default import DefaultBotProperties from aiogram.client.default import DefaultBotProperties
from handlers import register_all_handlers from handlers import register_all_handlers
from init_config import check_env_file, create_env_file, load_env from config import config
if not check_env_file(): BOT_TOKEN = config["bot_token"]
create_env_file()
load_env()
TOKEN = getenv("TOKEN")
dp = Dispatcher() dp = Dispatcher()
register_all_handlers(dp) register_all_handlers(dp)
async def main() -> None: async def main() -> None:
bot = Bot(token=TOKEN, default=DefaultBotProperties(parse_mode=ParseMode.HTML)) bot = Bot(token=BOT_TOKEN, default=DefaultBotProperties(parse_mode=ParseMode.HTML))
await dp.start_polling(bot) await dp.start_polling(bot)

View File

@ -14,7 +14,6 @@ multidict==6.4.3
propcache==0.3.1 propcache==0.3.1
pydantic==2.11.3 pydantic==2.11.3
pydantic_core==2.33.1 pydantic_core==2.33.1
python-dotenv==1.1.0
requests==2.32.3 requests==2.32.3
typing-inspection==0.4.0 typing-inspection==0.4.0
typing_extensions==4.13.2 typing_extensions==4.13.2